Skip to content

Commit 3908c1c

Browse files
various additions
1 parent 9e6113c commit 3908c1c

File tree

8 files changed

+537
-123
lines changed

8 files changed

+537
-123
lines changed

audio/xeno-war.mp3

24.5 KB
Binary file not shown.

index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<html>
33
<head>
44
<title>Space Shooter</title>
5+
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
56
<style>
67
* { margin: 0; padding: 0; }
78
body {
@@ -24,6 +25,7 @@
2425
.dg.main {
2526
margin-right: 0 !important;
2627
}
28+
* { font-family: 'Press Start 2P', cursive; }
2729
</style>
2830
<!-- Load dat.GUI with CORS enabled -->
2931
<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.9/dat.gui.min.js" crossorigin="anonymous"></script>

js/PatternFormation.js

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { patterns } from './patterns/formationPatterns.js';
33
import SplineCurve from './math/SplineCurve.js';
44
import BezierPath from './math/BezierPath.js';
55
import AlienLaser from './LaserParticle.js';
6+
import ExplosionEffect from './effects/ExplosionEffect.js';
67

78
class PatternFormation {
89
constructor(ctx, options = {}) {
@@ -17,7 +18,7 @@ class PatternFormation {
1718
patternType: 'infinity',
1819
loopDuration: 10,
1920
alienCount: 5,
20-
showPath: true,
21+
showPath: false, // Changed from true to false
2122
pathPoints: 100, // number of points to draw on path
2223
formationRadius: 150, // New separate radius for formation
2324
pulseIntensity: 0, // Add pulse intensity control
@@ -38,6 +39,9 @@ class PatternFormation {
3839
this.velocity = { x: 0, y: 0 };
3940

4041
this.calculateFormationParameters();
42+
43+
// Add tracking of original positions
44+
this.alienSlots = []; // Keep track of original formation slots
4145
this.createFormation();
4246

4347
this.patternNames = Object.keys(patterns);
@@ -62,6 +66,14 @@ class PatternFormation {
6266

6367
// Delay GUI setup to ensure dat.GUI is loaded
6468
setTimeout(() => this.setupGUI(), 100);
69+
70+
this.difficulty = options.difficulty || 1;
71+
this.shootInterval = Math.max(0.3, 1.0 - (this.difficulty * 0.1)); // Shoot faster with higher difficulty
72+
this.config.speed = Math.min(2.0, 0.3 + (this.difficulty * 0.1)); // Move faster with higher difficulty
73+
this.initialAlienCount = this.config.alienCount; // Store initial count
74+
this.pointsBase = 100; // Base points per alien
75+
76+
this.explosionEffect = new ExplosionEffect(ctx);
6577
}
6678

6779
setupGUI() {
@@ -147,23 +159,32 @@ class PatternFormation {
147159
}
148160

149161
createFormation() {
150-
this.aliens = []; // Clear existing aliens
162+
this.aliens = [];
163+
this.alienSlots = []; // Reset slots
151164
const count = Math.floor(this.config.alienCount);
152165

166+
// First, create all possible slots
167+
const angleStep = (Math.PI * 2) / count;
168+
for (let i = 0; i < count; i++) {
169+
this.alienSlots.push({
170+
index: i,
171+
angle: i * angleStep,
172+
occupied: true
173+
});
174+
}
175+
176+
// Then create aliens and assign them to slots
153177
for (let i = 0; i < count; i++) {
154178
const alien = new Alien(this.ctx, {
155179
virtualWidth: this.virtualWidth,
156180
virtualHeight: this.virtualHeight,
157-
width: 100, // Explicit size
181+
width: 100,
158182
height: 100
159183
});
160-
// Start aliens off-screen at the top
161-
alien.x = this.virtualWidth / 2;
162-
alien.y = -100;
184+
alien.slotIndex = i; // Remember which slot this alien belongs to
163185
this.aliens.push(alien);
164186
}
165187

166-
// Recalculate formation parameters for new count
167188
this.calculateFormationParameters();
168189
}
169190

@@ -234,13 +255,12 @@ class PatternFormation {
234255
(this.config.pulseIntensity * 5);
235256
const currentRadius = this.config.formationRadius + pulseAmount;
236257

237-
// Position aliens in formation with pulsing radius
238-
const angleStep = (Math.PI * 2) / this.aliens.length;
239-
this.aliens.forEach((alien, index) => {
240-
const formationAngle = index * angleStep;
258+
// Position aliens in formation based on their slots
259+
this.aliens.forEach(alien => {
260+
const slot = this.alienSlots[alien.slotIndex];
261+
const formationAngle = slot.angle;
241262
const rotationOffset = Math.atan2(this.velocity.y, this.velocity.x);
242263

243-
// Use pulsing radius
244264
const targetX = pos.x + Math.cos(formationAngle + rotationOffset) * currentRadius;
245265
const targetY = pos.y + Math.sin(formationAngle + rotationOffset) * currentRadius;
246266

@@ -271,6 +291,8 @@ class PatternFormation {
271291
// Update lasers
272292
this.lasers = this.lasers.filter(laser => laser.life > 0);
273293
this.lasers.forEach(laser => laser.update(delta));
294+
295+
this.explosionEffect.update(delta);
274296
}
275297

276298
shoot() {
@@ -325,6 +347,8 @@ class PatternFormation {
325347

326348
// Draw lasers
327349
this.lasers.forEach(laser => laser.draw(this.ctx));
350+
351+
this.explosionEffect.draw();
328352
}
329353

330354
drawPath() {
@@ -336,7 +360,27 @@ class PatternFormation {
336360
checkCollision(x, y) {
337361
for (let alien of this.aliens) {
338362
if (alien.collidesWith(x, y)) {
363+
// Create explosion at alien's center
364+
this.explosionEffect.createExplosion(
365+
alien.x + alien.width/2,
366+
alien.y + alien.height/2
367+
);
368+
369+
// Mark the slot as unoccupied but don't remove it
370+
this.alienSlots[alien.slotIndex].occupied = false;
371+
372+
// Remove only this alien
339373
this.aliens = this.aliens.filter(a => a !== alien);
374+
375+
// Calculate points with multiplier
376+
const pointsMultiplier = this.difficulty * (1 + (this.initialAlienCount - this.aliens.length) * 0.1);
377+
const points = Math.floor(this.pointsBase * pointsMultiplier);
378+
379+
// Add points to game score using window.game
380+
if (window.game) {
381+
console.log('Alien destroyed, adding points:', points); // Debug log
382+
window.game.addPoints(points);
383+
}
340384
return true;
341385
}
342386
}

js/effects/ExplosionEffect.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
class ExplosionParticle {
2+
constructor(x, y) {
3+
this.x = x;
4+
this.y = y;
5+
const angle = Math.random() * Math.PI * 2;
6+
const speed = Math.random() * 150 + 50; // Reduced speed range
7+
this.vx = Math.cos(angle) * speed;
8+
this.vy = Math.sin(angle) * speed;
9+
this.life = Math.random() * 0.4 + 0.3; // Shorter life: 0.3 to 0.7 seconds
10+
this.maxLife = this.life;
11+
this.radius = Math.random() * 8 + 4; // Smaller particles: 4-12px
12+
13+
// Brighter neon green
14+
this.hue = Math.random() * 15 + 90; // Tighter hue range
15+
this.saturation = 100;
16+
this.brightness = Math.random() * 10 + 90; // Even brighter: 90-100%
17+
18+
// Tighter glow
19+
this.pulseSpeed = Math.random() * 10 + 8;
20+
this.glowSize = Math.random() * 1.5 + 1.5; // 1.5-3x size glow
21+
}
22+
23+
update(delta) {
24+
this.x += this.vx * delta;
25+
this.y += this.vy * delta;
26+
this.life -= delta;
27+
this.vy += 100 * delta; // Less gravity
28+
this.radius *= 0.95; // Faster shrink
29+
}
30+
31+
draw(ctx) {
32+
const alpha = (this.life / this.maxLife) * 2.0; // Even brighter
33+
const pulse = 1 + Math.sin(this.life * this.pulseSpeed) * 0.3;
34+
35+
ctx.save();
36+
ctx.globalCompositeOperation = 'screen';
37+
38+
// Outer glow
39+
ctx.globalAlpha = alpha * 0.5;
40+
ctx.filter = 'blur(8px)';
41+
ctx.fillStyle = `hsla(${this.hue}, 100%, 70%, ${alpha})`;
42+
ctx.beginPath();
43+
ctx.arc(this.x, this.y, this.radius * this.glowSize * pulse, 0, Math.PI * 2);
44+
ctx.fill();
45+
46+
// Middle glow
47+
ctx.globalAlpha = alpha * 0.7;
48+
ctx.filter = 'blur(4px)';
49+
ctx.fillStyle = `hsla(${this.hue}, 100%, 80%, ${alpha})`;
50+
ctx.beginPath();
51+
ctx.arc(this.x, this.y, this.radius * 1.5 * pulse, 0, Math.PI * 2);
52+
ctx.fill();
53+
54+
// Core
55+
ctx.globalAlpha = alpha;
56+
ctx.filter = 'none';
57+
ctx.fillStyle = `hsla(${this.hue}, 100%, 100%, ${alpha})`;
58+
ctx.beginPath();
59+
ctx.arc(this.x, this.y, this.radius * pulse, 0, Math.PI * 2);
60+
ctx.fill();
61+
62+
ctx.restore();
63+
}
64+
}
65+
66+
class ExplosionEffect {
67+
constructor(ctx) {
68+
this.ctx = ctx;
69+
this.explosions = [];
70+
}
71+
72+
createExplosion(x, y) {
73+
const particles = [];
74+
// Fewer particles
75+
for (let i = 0; i < 15; i++) {
76+
particles.push(new ExplosionParticle(x, y));
77+
}
78+
// Just 3 highlight particles
79+
for (let i = 0; i < 3; i++) {
80+
const p = new ExplosionParticle(x, y);
81+
p.radius *= 1.5; // Not as huge
82+
p.life *= 1.2;
83+
particles.push(p);
84+
}
85+
this.explosions.push(particles);
86+
}
87+
88+
update(delta) {
89+
this.explosions.forEach(particles => {
90+
particles.forEach(p => p.update(delta));
91+
});
92+
// Remove finished explosions
93+
this.explosions = this.explosions.filter(particles =>
94+
particles.some(p => p.life > 0)
95+
);
96+
}
97+
98+
draw() {
99+
this.explosions.forEach(particles => {
100+
particles.forEach(p => p.draw(this.ctx));
101+
});
102+
}
103+
}
104+
105+
export default ExplosionEffect;

0 commit comments

Comments
 (0)