diff --git a/assets/enemy-sprite.png b/assets/enemy-sprite.png new file mode 100644 index 0000000..cdca7ac Binary files /dev/null and b/assets/enemy-sprite.png differ diff --git a/assets/player-sprite.png b/assets/player-sprite.png index b05cc4e..ba67f40 100644 Binary files a/assets/player-sprite.png and b/assets/player-sprite.png differ diff --git a/assets/sounds/enemy-shoot.mp3 b/assets/sounds/enemy-shoot.mp3 new file mode 100644 index 0000000..e651f1e Binary files /dev/null and b/assets/sounds/enemy-shoot.mp3 differ diff --git a/assets/sounds/next-wave.mp3 b/assets/sounds/next-wave.mp3 new file mode 100644 index 0000000..d860246 Binary files /dev/null and b/assets/sounds/next-wave.mp3 differ diff --git a/assets/sounds/player-death.mp3 b/assets/sounds/player-death.mp3 new file mode 100644 index 0000000..872830c Binary files /dev/null and b/assets/sounds/player-death.mp3 differ diff --git a/index.html b/index.html index ee44f0f..ed06c84 100644 --- a/index.html +++ b/index.html @@ -17,6 +17,8 @@ } #game-container { position: relative; + } + #game-container canvas { cursor: none; } diff --git a/raw/enemies.psd b/raw/enemies.psd new file mode 100644 index 0000000..4f71e68 Binary files /dev/null and b/raw/enemies.psd differ diff --git a/raw/player.psd b/raw/player.psd index babc5a9..e8bc81d 100644 Binary files a/raw/player.psd and b/raw/player.psd differ diff --git a/src/scenes/GameScene.js b/src/scenes/GameScene.js index 52788b2..17b68a7 100644 --- a/src/scenes/GameScene.js +++ b/src/scenes/GameScene.js @@ -8,6 +8,9 @@ export class GameScene extends Phaser.Scene { this.player = null; this.enemies = null; this.bullets = null; + + // Enemy bullets group + this.enemyBullets = null; // Game state this.score = 0; @@ -15,6 +18,10 @@ export class GameScene extends Phaser.Scene { // Mouse reticle this.reticle = null; + + // Wave system + this.waveTimer = 0; + this.currentWave = 0; } preload() { @@ -34,11 +41,19 @@ export class GameScene extends Phaser.Scene { frameWidth: 48, frameHeight: 48 }); + this.load.spritesheet('enemy-sprite', 'assets/enemy-sprite.png', { + frameWidth: 48, + frameHeight: 48 + }); // Load background music and sounds this.load.audio('bgm', 'assets/music/NeonPulse.mp3'); this.load.audio('main-gun', 'assets/sounds/main-gun.mp3'); this.load.audio('enemy-kill', 'assets/sounds/enemy-kill.mp3'); + this.load.audio('enemy-shoot', 'assets/sounds/enemy-shoot.mp3'); + this.load.audio('player-death', 'assets/sounds/player-death.mp3'); + this.load.audio('enemy-shoot', 'assets/sounds/enemy-shoot.mp3'); + this.load.audio('next-wave', 'assets/sounds/next-wave.mp3'); // Create simple placeholder graphics for now this.createPlaceholderGraphics(); @@ -57,11 +72,13 @@ export class GameScene extends Phaser.Scene { // Create groups for bullets and enemies this.bullets = this.physics.add.group(); + this.enemyBullets = this.physics.add.group(); this.enemies = this.physics.add.group(); // Setup collision detection this.physics.add.overlap(this.bullets, this.enemies, this.hitEnemy, null, this); this.physics.add.overlap(this.player.sprite, this.enemies, this.hitPlayer, null, this); + this.physics.add.overlap(this.player.sprite, this.enemyBullets, this.hitPlayer, null, this); // Start enemy spawning this.time.addEvent({ @@ -94,18 +111,31 @@ export class GameScene extends Phaser.Scene { //this.bgSpeed3 = 0.8; } - update() { + update(time, delta) { if (this.gameOver) return; // Update background positions for parallax effect this.updateBackgrounds(); + + // Update wave timer + this.waveTimer += delta; + // Check for wave changes every 30 seconds (30,000 milliseconds) + if (this.waveTimer >= 5000 && this.currentWave === 0) { + this.startWave(1); + } else if (this.waveTimer >= 10000 && this.currentWave === 1) { + this.startWave(2); + } + // Get A/D keys for player movement const aKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A); const dKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D); // Update player with A/D controls this.player.update(aKey, dKey); + + // Check if enemies are off-screen and destroy them + this.checkEnemiesOffScreen(); } updateBackgrounds() { @@ -115,7 +145,6 @@ export class GameScene extends Phaser.Scene { //this.bgLayer3.x -= this.bgSpeed3; // Reset positions to create continuous scrolling effect - console.log(this.bgLayer1.y); if (this.bgLayer1.y > 736) { this.bgLayer1.y = 30; // Width of game + layer width } @@ -157,18 +186,6 @@ export class GameScene extends Phaser.Scene { canvas.width = 32; canvas.height = 32; - // // Player ship (triangle) - // const ctx = canvas.getContext('2d'); - // ctx.fillStyle = '#00ffcc'; - // ctx.beginPath(); - // ctx.moveTo(16, 5); - // ctx.lineTo(5, 30); - // ctx.lineTo(27, 30); - // ctx.closePath(); - // ctx.fill(); - - // this.textures.addCanvas('player', canvas); - // Enemy (square) const enemyCanvas = document.createElement('canvas'); enemyCanvas.width = 24; @@ -192,6 +209,19 @@ export class GameScene extends Phaser.Scene { bCtx.fill(); this.textures.addCanvas('bullet', bulletCanvas); + + // Enemy Bullet (small circle) + const enemyBulletCanvas = document.createElement('canvas'); + enemyBulletCanvas.width = 8; + enemyBulletCanvas.height = 8; + + const ebCtx = enemyBulletCanvas.getContext('2d'); + ebCtx.fillStyle = '#00ffcc'; + ebCtx.beginPath(); + ebCtx.arc(4, 4, 3, 0, Math.PI * 2); + ebCtx.fill(); + + this.textures.addCanvas('enemyBullet', enemyBulletCanvas); } createUI() { @@ -238,15 +268,53 @@ export class GameScene extends Phaser.Scene { spawnEnemy() { // Spawn enemies at random x position within game width (800px) - const x = Phaser.Math.Between(20, 780); - const enemy = this.enemies.create(x, 0, 'enemy'); + const x = Phaser.Math.Between(20, 580); + + // Calculate texture range with minimum of 0 + const minTexture = Math.max(0, this.currentWave - 2); + const maxTexture = this.currentWave; + + // Ensure we don't get negative values + const texture = Phaser.Math.Between(minTexture, maxTexture); + + const enemy = this.enemies.create(x, 0, 'enemy-sprite', texture); // Randomize enemy speed const speed = Phaser.Math.Between(50, 150); enemy.setVelocityY(speed); // Add some rotation for visual effect - enemy.setRotation(Phaser.Math.FloatBetween(-0.2, 0.2)); + //enemy.setRotation(Phaser.Math.FloatBetween(-0.2, 0.2)); + + // Apply wave-specific properties + this.applyWaveProperties(enemy, texture); + } + + applyWaveProperties(enemy, texture) { + // Set the current wave number on the enemy + enemy.waveNumber = this.currentWave; + + // Wave 1: Basic enemies (no shooting) + if (this.currentWave === 0) { + enemy.canShoot = false; + if (enemy.shootTimer) { + enemy.shootTimer.remove(); + enemy.shootTimer = null; + } + } + // Wave 2: Enemies that can shoot + else if (this.currentWave >= 1 && texture > 0) { + enemy.canShoot = true; + // Only set up shooting timer if it doesn't exist yet + if (!enemy.shootTimer) { + enemy.shootTimer = this.time.addEvent({ + delay: Phaser.Math.Between(1000, 3000), + callback: () => this.enemyShoot(enemy, texture), + callbackScope: this, + loop: true + }); + } + } } hitEnemy(bullet, enemy) { @@ -259,8 +327,37 @@ export class GameScene extends Phaser.Scene { } hitPlayer(player, enemy) { + // Create explosion at player's position + const explosion = this.add.sprite(this.player.sprite.x, this.player.sprite.y, 'player-sprite', 3); + const explosion2 = this.add.sprite(this.player.sprite.x, this.player.sprite.y, 'player-sprite', 3); + + // Add rotation and scaling animation + this.tweens.add({ + targets: explosion, + scale: 3, // Scale up to 3x + rotation: Math.PI * 2, // Full rotation + alpha: 0.5, // Fade out slightly + duration: 1000, + ease: 'Power2', + onComplete: () => { + explosion.destroy(); + } + }); + this.tweens.add({ + targets: explosion2, + scale: 3, // Scale up to 3x + rotation: -Math.PI * 2, // Full rotation + alpha: 0.5, // Fade out slightly + duration: 1000, + ease: 'Power2', + onComplete: () => { + explosion2.destroy(); + } + }); + player.destroy(); enemy.destroy(); + this.sound.play('player-death'); this.gameOver = true; @@ -273,8 +370,81 @@ export class GameScene extends Phaser.Scene { gameOverText.setOrigin(0.5); // Restart game after delay + // this.time.delayedCall(2000, () => { + // this.scene.restart(); + // }); + } + + startWave(waveNumber) { + this.currentWave = waveNumber; + this.sound.play('next-wave'); + + // Add wave indicator text + const waveText = this.add.text(300, 150, `WAVE ${waveNumber + 1}`, { + fontSize: '48px', + fill: '#ffff00', + fontFamily: 'Arial' + }); + waveText.setOrigin(0.5); + + // Add scaling animation + this.tweens.add({ + targets: waveText, + scale: 2, // Grow to double size + duration: 1000, + ease: 'Elastic.out', // Elastic easing for bouncy effect + yoyo: true, // Scale back and forth + repeat: 1 // Do the animation twice (total of 3 states) + }); + + // Remove text after delay this.time.delayedCall(2000, () => { - this.scene.restart(); + waveText.destroy(); + }); + + // Apply wave properties to all existing enemies + this.enemies.children.iterate((enemy) => { + this.applyWaveProperties(enemy); + }); + } + + enemyShoot(enemy, texture) { + // Only shoot if enemy can shoot and isn't destroyed + if (!enemy.canShoot || !enemy.active) return; + + console.log(texture); + // Check if this is wave 3 and enemy has texture 2 (special shooting) + if (this.currentWave >= 2 && enemy.waveNumber >= 2 && texture >= 2) { + // Shoot toward player + const angle = Phaser.Math.Angle.Between( + enemy.x, + enemy.y, + this.player.sprite.x, + this.player.sprite.y + ); + + const bullet = this.enemyBullets.create(enemy.x, enemy.y, 'enemyBullet'); + const speed = 200; + bullet.setVelocityX(Math.cos(angle) * speed); + bullet.setVelocityY(Math.sin(angle) * speed); + bullet.setScale(1.5); + } else { + // Regular straight-down shooting + const bullet = this.enemyBullets.create(enemy.x, enemy.y, 'enemyBullet'); + bullet.setVelocityY(300); // Move straight down at 200px/s + bullet.setScale(1.5); + } + + // Sound effect for enemy shooting + this.sound.play('enemy-shoot'); + } + + checkEnemiesOffScreen() { + // Destroy enemies that have gone below the screen + this.enemies.children.iterate((enemy) => { + if (enemy && enemy.y > 800) { // Adjust 600 to your game height as needed + enemy.destroy(); + } }); } } \ No newline at end of file