diff --git a/assets/ClassCoder.ttf b/assets/ClassCoder.ttf new file mode 100644 index 0000000..ae59210 Binary files /dev/null and b/assets/ClassCoder.ttf differ diff --git a/assets/Vector.ttf b/assets/Vector.ttf new file mode 100644 index 0000000..299a7cd Binary files /dev/null and b/assets/Vector.ttf differ diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..c005d01 Binary files /dev/null and b/assets/logo.png differ diff --git a/assets/menu-alien.png b/assets/menu-alien.png new file mode 100644 index 0000000..0b12aaf Binary files /dev/null and b/assets/menu-alien.png differ diff --git a/assets/menu-hunter.png b/assets/menu-hunter.png new file mode 100644 index 0000000..d0f09ce Binary files /dev/null and b/assets/menu-hunter.png differ diff --git a/assets/menu-ship.png b/assets/menu-ship.png new file mode 100644 index 0000000..5ef5d46 Binary files /dev/null and b/assets/menu-ship.png differ diff --git a/assets/music/NeonVelocity.mp3 b/assets/music/NeonVelocity.mp3 new file mode 100644 index 0000000..1ef8148 Binary files /dev/null and b/assets/music/NeonVelocity.mp3 differ diff --git a/assets/neon-city-bg.png b/assets/neon-city-bg.png index f6520bb..445ed12 100644 Binary files a/assets/neon-city-bg.png and b/assets/neon-city-bg.png differ diff --git a/assets/neon-city-bg2 (2).png b/assets/neon-city-bg2 (2).png new file mode 100644 index 0000000..f6520bb Binary files /dev/null and b/assets/neon-city-bg2 (2).png differ diff --git a/assets/neon-city-dark-clouds.png b/assets/neon-city-dark-clouds.png new file mode 100644 index 0000000..7d397dc Binary files /dev/null and b/assets/neon-city-dark-clouds.png differ diff --git a/assets/neon-city-dark-clouds2.png b/assets/neon-city-dark-clouds2.png new file mode 100644 index 0000000..fb70d0c Binary files /dev/null and b/assets/neon-city-dark-clouds2.png differ diff --git a/assets/player-sprite - Copy.png b/assets/player-sprite - Copy.png new file mode 100644 index 0000000..ba67f40 Binary files /dev/null and b/assets/player-sprite - Copy.png differ diff --git a/assets/reticle.png b/assets/reticle.png index 4ebd4fd..ff6ca07 100644 Binary files a/assets/reticle.png and b/assets/reticle.png differ diff --git a/assets/space-age.otf b/assets/space-age.otf new file mode 100644 index 0000000..6b22db0 Binary files /dev/null and b/assets/space-age.otf differ diff --git a/index.html b/index.html index ed06c84..87f592d 100644 --- a/index.html +++ b/index.html @@ -30,12 +30,6 @@ - + \ No newline at end of file diff --git a/raw/class-coder.zip b/raw/class-coder.zip new file mode 100644 index 0000000..3ad5ec2 Binary files /dev/null and b/raw/class-coder.zip differ diff --git a/raw/saucer.zip b/raw/saucer.zip new file mode 100644 index 0000000..9dc5b7a Binary files /dev/null and b/raw/saucer.zip differ diff --git a/raw/space-age.zip b/raw/space-age.zip new file mode 100644 index 0000000..6a7edb2 Binary files /dev/null and b/raw/space-age.zip differ diff --git a/raw/vector.zip b/raw/vector.zip new file mode 100644 index 0000000..65ef17c Binary files /dev/null and b/raw/vector.zip differ diff --git a/src/config.js b/src/config.js index f734491..e798427 100644 --- a/src/config.js +++ b/src/config.js @@ -1,3 +1,6 @@ +import { MenuScene } from './scenes/MenuScene.js'; +import { GameScene } from './scenes/GameScene.js'; + export const config = { type: Phaser.AUTO, width: 600, @@ -10,13 +13,14 @@ export const config = { debug: false } }, - scene: { - preload: function() {}, - create: function() {}, - update: function() {} - }, + scene: [ + MenuScene, + GameScene, + ], scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH } -}; \ No newline at end of file +}; + +const game = new Phaser.Game(config); \ No newline at end of file diff --git a/src/scenes/GameScene.js b/src/scenes/GameScene.js index fcf1c6e..e26dcb6 100644 --- a/src/scenes/GameScene.js +++ b/src/scenes/GameScene.js @@ -33,8 +33,9 @@ export class GameScene extends Phaser.Scene { // Load background images this.load.image('bg-layer1', 'assets/neon-city-bg.png'); - //this.load.image('bg-layer2', 'assets/bg-layer2.png'); - //this.load.image('bg-layer3', 'assets/bg-layer3.png'); + this.load.image('bg-layer2', 'assets/neon-city-dark-clouds2.png'); + this.load.image('bg-layer3', 'assets/neon-city-dark-clouds.png'); + this.load.image('logo', 'assets/logo.png'); // Load Sprites this.load.spritesheet('player-sprite', 'assets/player-sprite.png', { @@ -54,6 +55,9 @@ export class GameScene extends Phaser.Scene { 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'); + + // Load Fonts + this.load.font('Space', 'assets/space-age.otf'); // Create simple placeholder graphics for now this.createPlaceholderGraphics(); @@ -102,13 +106,13 @@ export class GameScene extends Phaser.Scene { createBackgrounds() { // Create multiple background layers with different speeds this.bgLayer1 = this.add.image(280, 30, 'bg-layer1').setOrigin(0.5); - //this.bgLayer2 = this.add.image(400, 300, 'bg-layer2').setOrigin(0.5); - //this.bgLayer3 = this.add.image(400, 300, 'bg-layer3').setOrigin(0.5); + this.bgLayer2 = this.add.image(280, 30, 'bg-layer2').setOrigin(0.5); + this.bgLayer3 = this.add.image(280, 30, 'bg-layer3').setOrigin(0.5); // Set different speeds for parallax effect (smaller = slower) this.bgSpeed1 = 0.2; - //this.bgSpeed2 = 0.5; - //this.bgSpeed3 = 0.8; + this.bgSpeed2 = 0.5; + this.bgSpeed3 = 0.8; } update(time, delta) { @@ -141,19 +145,19 @@ export class GameScene extends Phaser.Scene { updateBackgrounds() { // Move background layers at different speeds this.bgLayer1.y += this.bgSpeed1; - //this.bgLayer2.x -= this.bgSpeed2; - //this.bgLayer3.x -= this.bgSpeed3; + this.bgLayer2.y += this.bgSpeed2; + this.bgLayer3.y += this.bgSpeed3; // Reset positions to create continuous scrolling effect if (this.bgLayer1.y > 736) { - this.bgLayer1.y = 30; // Width of game + layer width + this.bgLayer1.y = 0; + } + if (this.bgLayer2.y > 1536) { + this.bgLayer2.y = 0; + } + if (this.bgLayer3.y > 1536) { + this.bgLayer3.y = 0; } - // if (this.bgLayer2.x < -400) { - // this.bgLayer2.x = 1200; - // } - // if (this.bgLayer3.x < -400) { - // this.bgLayer3.x = 1200; - // } } setupMouseInput() { @@ -229,17 +233,18 @@ export class GameScene extends Phaser.Scene { this.scoreText = this.add.text(16, 16, 'Score: 0', { fontSize: '28px', fill: '#ffffff', - fontFamily: 'Arial' + fontFamily: 'Space, Arial' }); // Create game title - this.titleText = this.add.text(300, 50, 'Zenith Vector', { - fontSize: '40px', - fill: '#00ffcc', - fontFamily: 'Arial', - align: 'center' - }); - this.titleText.setOrigin(0.5); + // this.titleText = this.add.text(300, 50, 'Zenith Vector', { + // fontSize: '40px', + // fill: '#00ffcc', + // fontFamily: 'Arial', + // align: 'center' + // }); + // this.titleText.setOrigin(0.5); + this.titleText = this.add.image(300, 50, 'logo').setScale(.1).setOrigin(0.5); } shoot(pointer) { @@ -365,14 +370,24 @@ export class GameScene extends Phaser.Scene { const gameOverText = this.add.text(300, 400, 'GAME OVER', { fontSize: '54px', fill: '#ff3366', - fontFamily: 'Arial' + fontFamily: 'Space, Arial' }); gameOverText.setOrigin(0.5); - // Restart game after delay - // this.time.delayedCall(2000, () => { - // this.scene.restart(); - // }); + // Return to menu after delay + this.time.delayedCall(5000, () => { + // Fade camera out before returning to menu + this.tweens.add({ + targets: this.cameras.main, + alpha: 0, // Fade to transparent + duration: 1000, // 1 second fade + ease: 'Linear', + onComplete: () => { + this.bgMusic.stop(); + this.scene.start('MenuScene'); + } + }); + }); } startWave(waveNumber) { @@ -381,9 +396,9 @@ export class GameScene extends Phaser.Scene { // Add wave indicator text const waveText = this.add.text(300, 150, `WAVE ${waveNumber + 1}`, { - fontSize: '48px', + fontSize: '48px', fill: '#ffff00', - fontFamily: 'Arial' + fontFamily: 'Space, Arial' }); waveText.setOrigin(0.5); diff --git a/src/scenes/MenuScene.js b/src/scenes/MenuScene.js new file mode 100644 index 0000000..e219b30 --- /dev/null +++ b/src/scenes/MenuScene.js @@ -0,0 +1,299 @@ +export class MenuScene extends Phaser.Scene { + constructor() { + super('MenuScene'); + } + + preload() { + this.load.image('menu-logo','assets/logo.png'); + this.load.image('menu-ship','assets/menu-ship.png'); + this.load.image('menu-hunter','assets/menu-hunter.png'); + this.load.image('menu-alien','assets/menu-alien.png'); + this.load.image('reticle', 'assets/reticle.png'); + this.load.audio('menu-bgMusic', 'assets/music/NeonVelocity.mp3'); + + // Load custom font + this.load.font('Space', 'assets/space-age.otf'); + this.load.font('coder', 'assets/ClassCoder.ttf'); + } + + create() { + // Create menu background and title + this.add.rectangle(300, 400, 600, 800, 0x111111); + + // Create starfield effect + this.createStarfield(); + + const logo = this.add.image(300, 250, 'menu-logo').setOrigin(0.5).setScale(0.5).setAlpha(0.7); + const menuHunter = this.add.image(450, 700, 'menu-hunter').setScale(0.6).setOrigin(0.5).setAlpha(0); + const menuShip = this.add.image(900, -300, 'menu-ship').setOrigin(0.5).setScale(.6); + const menuAlien = this.add.image(150, 700, 'menu-alien').setScale(0.6).setOrigin(0.5).setAlpha(0); + + this.tweens.add({ + targets: menuShip, + x: 500, + y: 100, + duration: 2000, + ease: 'Cubic', + delay: 3000 + }); + + this.tweens.add({ + targets: logo, + alpha: 1, + scale: 1, + duration: 5000, + ease: 'Bounce', + }); + + this.tweens.add({ + targets: [menuHunter, menuAlien], + alpha: 1, + duration: 3000, + delay: 4000, + ease: 'Bounce' + }) + + // Controls display with typing animation + const controlsText = 'Controls: A and D to go Left and Right. Mouse to Aim and Fire'; + this.controlsText = this.add.text(300, 500, '', { + fontFamily: 'Coder, Arial', + fontSize: '16px', + fill: '#00ff00' + }).setOrigin(0.5); + + // Typing animation + this.time.delayedCall(1000, () => { + let i = 0; + const typingTimer = this.time.addEvent({ + delay: 50, // Characters per second + callback: () => { + if (i < controlsText.length) { + this.controlsText.text += controlsText.charAt(i); + i++; + } else { + typingTimer.remove(); + } + }, + callbackScope: this, + loop: true + }); + }); + + // Menu button + this.startButton = this.add.text(300, 550, ' START GAME ', { + fontFamily: 'Space, Arial', + fontSize: '48px', + fill: '#fff' + }).setOrigin(0.5); + + // Add color cycling effect for synthwave colors + const synthwaveColors = ['#ffffff', '#ff00ff', '#00ffff', '#ffff00', '#ff00cc']; + let colorIndex = 0; + + this.time.addEvent({ + delay: 1500, + callback: () => { + colorIndex = (colorIndex + 1) % synthwaveColors.length; + this.startButton.setFill(synthwaveColors[colorIndex]); + }, + loop: true + }); + + this.tweens.add({ + targets: this.startButton, + scale: 1.2, + duration: 1500, + repeat: -1, + ease: 'Sine', + yoyo: true, + }); + + this.startButton.setInteractive(); + this.startButton.on('pointerdown', () => { + // Start the actual game scene + this.bgMusic.stop(); + this.scene.start('GameScene'); + }); + + // Play background music + this.bgMusic = this.sound.add('menu-bgMusic', { loop: true }); + this.bgMusic.play(); + + // Setup mouse input for shooting and reticle + this.setupMouseInput(); + + // Add scanline effect (narrow and moving) + this.scanlines = this.add.graphics({ x: 0, y: 0 }); + this.scanlines.fillStyle(0x00ff00, 0.15); // Green scanlines with moderate opacity + // Create narrow horizontal lines that move vertically + for (let i = 0; i < 20; i++) { + const y = (i * 40) % 800; + this.scanlines.fillRect(0, y, 600, 2); // Narrow lines (2px height) + } + + // Add VHS artifacts + this.vhsArtifacts = this.add.graphics({ x: 0, y: 0 }); + this.vhsArtifacts.fillStyle(0xff00ff, 0.1); // Magenta artifacts + this.vhsArtifacts.fillRect(0, 0, 600, 800); + + // Add VHS artifacts 2 + this.vhsArtifacts2 = this.add.graphics({ x: 0, y: 0 }); + this.vhsArtifacts2.fillStyle(0xff00ff, 0.1); // Magenta artifacts + this.vhsArtifacts2.fillRect(0, 0, 600, 800); + + this.tweens.add({ + targets: this.vhsArtifacts2, + alpha: 0.1, + duration: 10000, + yoyo: true, + repeat: -1 + }); + + // Start artifact effects + this.artifactTimer = this.time.addEvent({ + delay: 200, + callback: this.updateVHSArtifacts, + callbackScope: this, + loop: true + }); + } + + createStarfield() { + // Create a graphics object for the starfield + this.starfield = this.add.graphics({ x: 0, y: 0 }); + + // Star configuration + this.stars = []; + const starCount = 200; + + // Initialize stars with random positions and z-depths + for (let i = 0; i < starCount; i++) { + const star = { + x: Math.random() * 600, + y: Math.random() * 800, + z: Math.random() * 10 + 1, // Depth from 1 to 11 + size: Math.random() * 2 + 1, + speed: Math.random() * 0.5 + 0.2 // Significantly reduced speed + }; + + // Set color based on depth (closer stars are brighter) + const brightness = Math.max(0.5, 1 - star.z / 15); // Closer stars brighter + const grayValue = Math.floor(150 + brightness * 105); + star.color = (grayValue << 16) | (grayValue << 8) | grayValue; + + this.stars.push(star); + } + + // Change direction timer - increased to make it more subtle + this.directionChangeTimer = this.time.addEvent({ + delay: 5000, // Increase delay to 5 seconds + callback: this.changeStarDirection, + callbackScope: this, + loop: true + }); + } + + update() { + // Update star positions + if (this.starfield && this.stars) { + this.starfield.clear(); + + for (let i = 0; i < this.stars.length; i++) { + const star = this.stars[i]; + + // Move stars toward camera (decrease z) - greatly reduced speed + star.z -= star.speed * 0.02; + + // Reset stars that move too close to camera + if (star.z <= 0) { + star.z = Math.random() * 10 + 1; // Reset with new depth + star.x = Math.random() * 600; // Random x position + star.y = Math.random() * 800; // Random y position + + // Recalculate color based on new depth + const brightness = Math.max(0.5, 1 - star.z / 15); + const grayValue = Math.floor(150 + brightness * 105); + star.color = (grayValue << 16) | (grayValue << 8) | grayValue; + } + + // Apply perspective projection to create parallax effect + const scale = 20 / (star.z + 20); // Scale based on depth + const screenX = star.x * scale; + const screenY = star.y * scale; + + // Draw the star with its color and scaled size + this.starfield.fillStyle(star.color); + this.starfield.fillCircle(screenX, screenY, star.size * scale); + } + } + + // Update scanline position + if (this.scanlines) { + this.scanlines.y = (this.scanlines.y + 0.5) % 800; + } + } + + updateVHSArtifacts() { + // Randomly update VHS artifacts + if (this.vhsArtifacts && Math.random() > 0.7) { + // Clear previous artifacts but keep some for flickering effect + this.vhsArtifacts.clear(); + + // Add random color shifts and distortions + for (let i = 0; i < 5; i++) { + const x = Math.random() * 600; + const y = Math.random() * 800; + const width = Math.random() * 10 + 1; + const height = Math.random() * 20 + 5; + + // Random color (neon colors for synthwave) + const colors = [0xff00ff, 0x00ffff, 0xffff00]; + const color = Phaser.Display.Color.GetColor32( + ...colors[Math.floor(Math.random() * colors.length)].toString(16).split('').map(c => parseInt(c, 16) * 17) + ); + + // Use higher opacity for flickering effect (instead of very low opacity) + this.vhsArtifacts.fillStyle(color, Math.random() * 0.3 + 0.1); // Increased from 0.05 to 0.3-0.1 range + this.vhsArtifacts.fillRect(x, y, width, height); + } + } + } + + changeStarDirection() { + // Change direction of all stars (they'll move toward camera) + for (let i = 0; i < this.stars.length; i++) { + const star = this.stars[i]; + + // Randomly adjust speed and depth - much slower changes + star.speed = Math.random() * 1 + 0.5; // Reduced speed range + star.z += (Math.random() * 0.5 - 0.25); // Very subtle z position change + + // Keep stars within reasonable bounds + if (star.z < 0.5) { + star.z = 0.5; + } else if (star.z > 15) { + star.z = 15; + } + + // Recalculate color based on new depth and speed + const brightness = Math.max(0.5, 1 - star.z / 15); + const grayValue = Math.floor(150 + brightness * 105); + star.color = (grayValue << 16) | (grayValue << 8) | grayValue; + } + } + + setupMouseInput() { + // Create mouse reticle as sprite (replacing the circle) + this.reticle = this.add.sprite(0, 0, 'reticle'); + this.reticle.setOrigin(0.5); + this.reticle.preFX.addGlow(0x00ffcc, .7); + + // Update reticle position with mouse + this.input.on('pointermove', (pointer) => { + if (this.gameOver) return; + + this.reticle.x = pointer.x; + this.reticle.y = pointer.y; + }); + } +} \ No newline at end of file