------------------------------------------------------------
commit a3e0f244d92b914fc760a2ed7d08b3a217ae4a45
Author: Breck Yunits <breck7@gmail.com> Date: Tue Nov 26 19:00:40 2024 -0800 diff --git a/wavewar.css b/wavewar.css index eac14ac..ee744dc 100644 --- a/wavewar.css +++ b/wavewar.css @@ -22,8 +22,11 @@ canvas { } #summaryLink { position: absolute; - top: 3px; - left: 3px; + top: 20px; + left: 20px; + padding: 10px 20px; text-decoration: none; - color: rgba(255, 255, 255, 0.5); + color: #4CAF50; + font-size: 24px; + font-family: monospace; } ------------------------------------------------------------
commit 63a5eb53ef7391aca7bdc7a9b55dc935badede56
Author: Breck Yunits <breck7@gmail.com> Date: Tue Nov 26 18:55:16 2024 -0800 diff --git a/WaveWar.js b/WaveWar.js index 8d3caca..249dcb7 100644 --- a/WaveWar.js +++ b/WaveWar.js @@ -193,10 +193,7 @@ class Globe { // Wave and weapon settings this.activeWaves = [] - this.empCooldown = false - this.soundCooldown = false - this.empCooldownDuration = 1000 - this.soundCooldownDuration = 1000 + this.cooldownDuration = 1000 // Drone settings this.drones = [] @@ -303,19 +300,6 @@ class Globe { // Add styles for the cooldown animation const style = document.createElement("style") style.textContent = ` - @keyframes cooldown { - from { - background: linear-gradient(to top, - var(--button-color) 0%, - transparent 0%); - } - to { - background: linear-gradient(to top, - var(--button-color) 100%, - transparent 100%); - } - } - .control-button { width: 40px; height: 40px; @@ -326,7 +310,7 @@ class Globe { font-weight: bold; color: white; position: relative; - transition: transform 0.2s ease; + transition: transform 0.5s ease; } .control-button:not(.cooldown):hover { @@ -335,8 +319,7 @@ class Globe { .control-button.cooldown { cursor: not-allowed; - opacity: 0.7; - animation: cooldown 1s linear; + background-color: transparent !important; } ` document.head.appendChild(style) @@ -412,7 +395,7 @@ class Globe { setTimeout(() => { this[cooldownProperty] = false button.classList.remove("cooldown") - }, this.soundCooldownDuration) + }, this.cooldownDuration) } } triggerEMP() { @@ -490,25 +473,8 @@ class Globe { maxScale, } - // Start animation loop for this wave - const animateWave = () => { - const elapsed = Date.now() - wave.startTime - const progress = elapsed / wave.duration - - if (progress >= 1) { - this.scene.remove(wave.mesh) - wave.geometry.dispose() - wave.material.dispose() - this.activeWaves = this.activeWaves.filter((w) => w !== wave) - } else { - const scale = 1 + (wave.maxScale - 1) * progress - wave.mesh.scale.set(scale, scale, scale) - wave.material.uniforms.time.value = progress - requestAnimationFrame(animateWave) - } - } - - requestAnimationFrame(animateWave) + // Add to active waves and return + this.activeWaves.push(wave) return wave } @@ -829,6 +795,24 @@ class Globe { const delta = (currentTime - (this.lastFrameTime || currentTime)) / 1000 this.lastFrameTime = currentTime + // Update all waves in the main animation loop + this.activeWaves = this.activeWaves.filter((wave) => { + const elapsed = currentTime - wave.startTime + const progress = elapsed / wave.duration + + if (progress >= 1) { + this.scene.remove(wave.mesh) + wave.geometry.dispose() + wave.material.dispose() + return false + } + + const scale = 1 + (wave.maxScale - 1) * progress + wave.mesh.scale.set(scale, scale, scale) + wave.material.uniforms.time.value = progress + return true + }) + // Spawn new drones if ( currentTime - this.lastDroneSpawn > this.droneSpawnInterval && ------------------------------------------------------------
commit 4269d448cbd546b3dd55e0736f5d0ca67617266c
Author: Breck Yunits <breck7@gmail.com> Date: Tue Nov 26 18:42:49 2024 -0800 diff --git a/WaveWar.js b/WaveWar.js index 945c3b0..8d3caca 100644 --- a/WaveWar.js +++ b/WaveWar.js @@ -155,54 +155,117 @@ class Drone { class Globe { constructor() { + // Keep only the essential properties that shouldn't be reset this.renderer = null this.scene = null this.camera = null + this.animationId = null + this.gameOverScreen = null + this.empButton = null + this.soundButton = null + + // Basic initialization + this.createScoreDisplay() + this.isDragging = false + this.previousMousePosition = { x: 0, y: 0 } + this.shouldRotate = true + } + + setupGame() { + // Game state this.earth = null this.spikes = [] - this.animationId = null this.stars = null - this.earthHealth = 1.0 // 1.0 = full health, 0 = dead + this.earthHealth = 1.0 this.gameOver = false this.score = 0 - this.createScoreDisplay() - - // Camera control properties - this.isDragging = false - this.previousMousePosition = { - x: 0, - y: 0, - } - // Camera orbit properties + // Camera settings this.cameraDistance = 1 this.cameraRotation = { - x: Math.PI * 0.1, // slightly above equator - y: Math.PI * 0.6, // west of prime meridian + x: Math.PI * 0.1, + y: Math.PI * 0.6, } - - // Zoom properties this.minZoom = 0.1 this.maxZoom = 10 this.currentZoom = 0.7 this.zoomSpeed = 0.02 - // Replace separate wave arrays with unified array + // Wave and weapon settings this.activeWaves = [] this.empCooldown = false this.soundCooldown = false this.empCooldownDuration = 1000 this.soundCooldownDuration = 1000 - // Store button references - this.empButton = null - this.soundButton = null - + // Drone settings this.drones = [] this.droneSpeedMultiplier = 0 this.lastDroneSpawn = 0 - this.droneSpawnInterval = 1000 // Spawn a new drone every 2 seconds - this.maxDrones = 200 // Maximum number of drones allowed + this.droneSpawnInterval = 1000 + this.maxDrones = 200 + + // Initialize scene, camera, renderer + this.scene = new THREE.Scene() + const height = window.innerHeight - 100 + this.camera = new THREE.PerspectiveCamera( + 75, + window.innerWidth / height, + 0.1, + 1000, + ) + this.updateCameraPosition() + + // Set up renderer + this.renderer = new THREE.WebGLRenderer({ antialias: true }) + this.renderer.setSize(window.innerWidth, height) + this.renderer.setClearColor(0x000000) + document.body.prepend(this.renderer.domElement) + + // Create game elements + this.createStarField() + this.createEarth() + this.setupLighting() + + // Set up controls + this.setupMouseControls() + this.setupZoomControls() + + // Reset score display + document.getElementById("score-display").textContent = "Drones Destroyed: 0" + + // Start animation + this.animate() + } + + createEarth() { + const geometry = new THREE.SphereGeometry(0.5, 32, 32) + const texture = new THREE.TextureLoader().load("earth_atmos_2048.jpg") + const material = new THREE.MeshPhongMaterial({ + map: texture, + specular: 0x333333, + shininess: 5, + emissive: 0xff0000, + emissiveIntensity: 0, + }) + + this.earth = new THREE.Mesh(geometry, material) + this.scene.add(this.earth) + } + + setupLighting() { + const ambientLight = new THREE.AmbientLight(0xfffffff) + this.scene.add(ambientLight) + + const directionalLight = new THREE.DirectionalLight(0xffffff, 0.4) + directionalLight.position.set(5, 3, 5) + this.scene.add(directionalLight) + } + + createGlobe() { + this.removeGlobe() + this.setupGame() + return this } createScoreDisplay() { @@ -498,66 +561,6 @@ class Globe { this.scene.add(this.stars) } - createGlobe() { - // Ensure any previous globe and animation is removed - this.removeGlobe() - - // Initialize scene, camera, renderer, etc. - this.scene = new THREE.Scene() - const height = window.innerHeight - 100 - this.camera = new THREE.PerspectiveCamera( - 75, - window.innerWidth / height, - 0.1, - 1000, - ) - - // Set initial camera position - this.cameraDistance = 1 - this.updateCameraPosition() - - // Set up renderer with alpha and better quality - this.renderer = new THREE.WebGLRenderer({ antialias: true }) - this.renderer.setSize(window.innerWidth, height) - this.renderer.setClearColor(0x000000) // Set background to black - document.body.prepend(this.renderer.domElement) - - // Create starry background - this.createStarField() - - // Create Earth with better lighting - const geometry = new THREE.SphereGeometry(0.5, 32, 32) - const texture = new THREE.TextureLoader().load("earth_atmos_2048.jpg") - const material = new THREE.MeshPhongMaterial({ - map: texture, - specular: 0x333333, - shininess: 5, - emissive: 0xff0000, // Red glow for damage - emissiveIntensity: 0, // Start with no damage glow - }) - - this.earth = new THREE.Mesh(geometry, material) - this.scene.add(this.earth) - - // Add ambient light - const ambientLight = new THREE.AmbientLight(0xfffffff) - this.scene.add(ambientLight) - - // Add directional light - const directionalLight = new THREE.DirectionalLight(0xffffff, 0.4) - directionalLight.position.set(5, 3, 5) - this.scene.add(directionalLight) - - // Add mouse and zoom controls - this.setupMouseControls() - this.setupZoomControls() - - // Start the animation loop - this.animate() - - return this - } - damageEarth() { if (this.gameOver) return @@ -609,25 +612,97 @@ class Globe { // Hide Earth this.earth.visible = false - // Create game over text - const gameOverDiv = document.createElement("div") - gameOverDiv.style.position = "fixed" - gameOverDiv.style.top = "50%" - gameOverDiv.style.left = "50%" - gameOverDiv.style.transform = "translate(-50%, -50%)" - gameOverDiv.style.color = "#ff0000" - gameOverDiv.style.fontSize = "64px" - gameOverDiv.style.fontFamily = "Arial, sans-serif" - gameOverDiv.style.fontWeight = "bold" - gameOverDiv.style.textShadow = "0 0 10px #ff0000" - gameOverDiv.style.zIndex = "1000" - gameOverDiv.innerHTML = `GAME OVER<br>Score: ${this.score}` - document.body.appendChild(gameOverDiv) + // Create game over screen + this.createGameOverScreen() // Start explosion animation this.animateExplosion() } + createGameOverScreen() { + // Create container for game over screen + const gameOverScreen = document.createElement("div") + gameOverScreen.style.position = "fixed" + gameOverScreen.style.top = "50%" + gameOverScreen.style.left = "50%" + gameOverScreen.style.transform = "translate(-50%, -50%)" + gameOverScreen.style.textAlign = "center" + gameOverScreen.style.color = "#ff0000" + gameOverScreen.style.fontFamily = "Arial, sans-serif" + gameOverScreen.style.zIndex = "1000" + + // Game Over text + const gameOverText = document.createElement("div") + gameOverText.textContent = "GAME OVER" + gameOverText.style.fontSize = "64px" + gameOverText.style.fontWeight = "bold" + gameOverText.style.textShadow = "0 0 10px #ff0000" + gameOverText.style.marginBottom = "20px" + + // Score text + const scoreText = document.createElement("div") + scoreText.textContent = `Score: ${this.score}` + scoreText.style.fontSize = "32px" + scoreText.style.marginBottom = "40px" + + // Play Again button + const playAgainButton = document.createElement("button") + playAgainButton.textContent = "Play Again" + playAgainButton.style.padding = "15px 30px" + playAgainButton.style.fontSize = "24px" + playAgainButton.style.backgroundColor = "#4CAF50" + playAgainButton.style.color = "white" + playAgainButton.style.border = "none" + playAgainButton.style.borderRadius = "5px" + playAgainButton.style.cursor = "pointer" + playAgainButton.style.transition = "background-color 0.3s" + + // Hover effect + playAgainButton.addEventListener("mouseover", () => { + playAgainButton.style.backgroundColor = "#45a049" + }) + playAgainButton.addEventListener("mouseout", () => { + playAgainButton.style.backgroundColor = "#4CAF50" + }) + + // Click event + playAgainButton.addEventListener("click", () => this.resetGame()) + + // Add elements to game over screen + gameOverScreen.appendChild(gameOverText) + gameOverScreen.appendChild(scoreText) + gameOverScreen.appendChild(playAgainButton) + + // Store reference and add to document + this.gameOverScreen = gameOverScreen + document.body.appendChild(gameOverScreen) + } + + resetGame() { + // Remove existing elements + this.removeGlobe() + + if (this.gameOverScreen) { + this.gameOverScreen.remove() + this.gameOverScreen = null + } + + // Reset buttons + if (this.empButton) { + this.empButton.classList.remove("cooldown") + this.empButton.style.cursor = "pointer" + this.empButton.style.opacity = "1" + } + if (this.soundButton) { + this.soundButton.classList.remove("cooldown") + this.soundButton.style.cursor = "pointer" + this.soundButton.style.opacity = "1" + } + + // Reinitialize game + this.setupGame() + } + animateExplosion() { if (!this.explosionParticles) return ------------------------------------------------------------
commit d1078dd99c18d41409de28df705aa9aa6f224328
Author: Breck Yunits <breck7@gmail.com> Date: Tue Nov 26 18:32:39 2024 -0800 diff --git a/WaveWar.js b/WaveWar.js index 07eb0b3..945c3b0 100644 --- a/WaveWar.js +++ b/WaveWar.js @@ -1,7 +1,7 @@ class Drone { constructor(globe) { this.globe = globe - this.speed = 0.01 + Math.random() * 0.2 + this.speed = 0.01 + Math.random() * 0.2 + globe.droneSpeedMultiplier this.mesh = this.createDroneMesh() this.position = this.getRandomPosition() this.target = this.getRandomPosition() @@ -80,21 +80,10 @@ class Drone { this.checkWaveCollisions() } + // Update the Drone class's checkWaveCollisions method checkWaveCollisions() { - // Check EMP waves - this.globe.activeEMPWaves.forEach((wave) => { - const waveRadius = wave.mesh.scale.x * 0.5 - const distanceFromCenter = this.position.length() - if ( - Math.abs(distanceFromCenter - waveRadius) < 0.05 && - !this.isExploding - ) { - this.startExplosion() - } - }) - - // Check Sound waves - this.globe.activeSoundWaves.forEach((wave) => { + // Check all active waves + this.globe.activeWaves.forEach((wave) => { const waveRadius = wave.mesh.scale.x * 0.5 const distanceFromCenter = this.position.length() if ( @@ -198,16 +187,19 @@ class Globe { this.currentZoom = 0.7 this.zoomSpeed = 0.02 - this.activeEMPWaves = [] + // Replace separate wave arrays with unified array + this.activeWaves = [] this.empCooldown = false - this.empCooldownDuration = 20 // 300ms cooldown - - // Add sound wave properties - this.activeSoundWaves = [] this.soundCooldown = false - this.soundCooldownDuration = 20 // 300ms cooldown like EMP + this.empCooldownDuration = 1000 + this.soundCooldownDuration = 1000 + + // Store button references + this.empButton = null + this.soundButton = null this.drones = [] + this.droneSpeedMultiplier = 0 this.lastDroneSpawn = 0 this.droneSpawnInterval = 1000 // Spawn a new drone every 2 seconds this.maxDrones = 200 // Maximum number of drones allowed @@ -245,17 +237,46 @@ class Globe { controlPanel.style.alignItems = "center" controlPanel.style.gap = "10px" - // Controller shape background - const controllerShape = document.createElement("div") - controllerShape.style.position = "absolute" - controllerShape.style.top = "0" - controllerShape.style.left = "0" - controllerShape.style.right = "0" - controllerShape.style.bottom = "0" - controllerShape.style.borderRadius = "15px" - controllerShape.style.border = "2px solid rgba(255, 255, 255, 0.2)" - controllerShape.style.boxShadow = "0 0 10px rgba(0, 0, 0, 0.5)" - controlPanel.appendChild(controllerShape) + // Add styles for the cooldown animation + const style = document.createElement("style") + style.textContent = ` + @keyframes cooldown { + from { + background: linear-gradient(to top, + var(--button-color) 0%, + transparent 0%); + } + to { + background: linear-gradient(to top, + var(--button-color) 100%, + transparent 100%); + } + } + + .control-button { + width: 40px; + height: 40px; + border: none; + border-radius: 50%; + cursor: pointer; + font-size: 16px; + font-weight: bold; + color: white; + position: relative; + transition: transform 0.2s ease; + } + + .control-button:not(.cooldown):hover { + transform: scale(1.1); + } + + .control-button.cooldown { + cursor: not-allowed; + opacity: 0.7; + animation: cooldown 1s linear; + } + ` + document.head.appendChild(style) const buttonContainer = document.createElement("div") buttonContainer.style.display = "flex" @@ -266,36 +287,22 @@ class Globe { const createButton = (text, color, action) => { const button = document.createElement("button") button.textContent = text - button.style.width = "40px" - button.style.height = "40px" + button.className = "control-button" + button.style.setProperty("--button-color", color) button.style.backgroundColor = color - button.style.color = "white" - button.style.border = "none" - button.style.borderRadius = "50%" - button.style.cursor = "pointer" - button.style.fontSize = "16px" - button.style.fontWeight = "bold" - button.style.boxShadow = "0 0 5px rgba(0, 0, 0, 0.3)" - button.style.transition = "all 0.2s ease" - - button.addEventListener("mouseenter", () => { - if (!this[action.toLowerCase() + "Cooldown"]) { - button.style.transform = "scale(1.1)" - button.style.boxShadow = "0 0 10px " + color - } - }) - button.addEventListener("mouseleave", () => { - button.style.transform = "scale(1)" - button.style.boxShadow = "0 0 5px rgba(0, 0, 0, 0.3)" - }) button.addEventListener("click", () => this.handleAction(action)) return button } + // Create buttons with their base colors const empButton = createButton("E", "#4CAF50", "EMP") const soundButton = createButton("S", "#2196F3", "SOUND") + // Store button references + this.empButton = empButton + this.soundButton = soundButton + buttonContainer.appendChild(empButton) buttonContainer.appendChild(soundButton) controlPanel.appendChild(buttonContainer) @@ -326,6 +333,8 @@ class Globe { handleAction(action) { const cooldownProperty = action.toLowerCase() + "Cooldown" + const button = action === "EMP" ? this.empButton : this.soundButton + if (!this[cooldownProperty]) { if (action === "EMP") { this.triggerEMP() @@ -333,169 +342,111 @@ class Globe { this.triggerSound() } - // Handle cooldown + // Handle cooldown and animation this[cooldownProperty] = true + button.classList.add("cooldown") + setTimeout(() => { this[cooldownProperty] = false + button.classList.remove("cooldown") }, this.soundCooldownDuration) } } - - triggerSound() { - const soundGeometry = new THREE.SphereGeometry(0.5, 32, 32) - const soundMaterial = new THREE.ShaderMaterial({ - transparent: true, - uniforms: { - time: { value: 0 }, - color: { value: new THREE.Color(0x2196f3) }, - maxRadius: { value: 5.0 }, - }, - vertexShader: ` - varying vec3 vNormal; - void main() { - vNormal = normalize(normalMatrix * normal); - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - } - `, - fragmentShader: ` - uniform float time; - uniform vec3 color; - uniform float maxRadius; - varying vec3 vNormal; - - void main() { - float intensity = 1.0 - (time * time); - float edge = 0.5; - float rim = smoothstep(0.5 - edge, 0.5 + edge, dot(vNormal, vec3(0.0, 0.0, 1.0))); - float wave = sin(time * 20.0) * 0.5 + 0.5; - gl_FragColor = vec4(color, intensity * (1.0 - rim) * wave * 0.7); - } - `, + triggerEMP() { + const wave = this.triggerWave({ + type: "emp", + color: 0x00ff00, + duration: 2000, + maxScale: 10, + pulseEffect: false, }) - - const soundWave = new THREE.Mesh(soundGeometry, soundMaterial) - this.scene.add(soundWave) - - const wave = { - mesh: soundWave, - geometry: soundGeometry, - material: soundMaterial, - startTime: Date.now(), - duration: 4000, // Increased from 2000 to 4000 for slower wave - maxScale: 8, - } - - this.activeSoundWaves.push(wave) - - if (this.activeSoundWaves.length === 1) { - this.animateSoundWaves() - } + this.activeWaves.push(wave) } - animateSoundWaves() { - for (let i = this.activeSoundWaves.length - 1; i >= 0; i--) { - const wave = this.activeSoundWaves[i] - const elapsed = Date.now() - wave.startTime - const progress = elapsed / wave.duration - - if (progress >= 1) { - this.scene.remove(wave.mesh) - wave.geometry.dispose() - wave.material.dispose() - this.activeSoundWaves.splice(i, 1) - } else { - // Slower expansion rate - const scale = 1 + (wave.maxScale - 1) * (progress * 0.5) // Added 0.5 multiplier to slow expansion - wave.mesh.scale.set(scale, scale, scale) - wave.material.uniforms.time.value = progress - } - } - - if (this.activeSoundWaves.length > 0) { - requestAnimationFrame(() => this.animateSoundWaves()) - } + triggerSound() { + const wave = this.triggerWave({ + type: "sound", + color: 0x2196f3, + duration: 4000, + maxScale: 8, + pulseEffect: true, + }) + this.activeWaves.push(wave) } - triggerEMP() { - // Create a sphere geometry for the EMP wave - const empGeometry = new THREE.SphereGeometry(0.5, 32, 32) - const empMaterial = new THREE.ShaderMaterial({ + triggerWave(config) { + const { + type, + color = 0x00ff00, + duration = 2000, + maxScale = 10, + pulseEffect = false, + } = config + + const waveGeometry = new THREE.SphereGeometry(0.5, 32, 32) + const waveMaterial = new THREE.ShaderMaterial({ transparent: true, uniforms: { time: { value: 0 }, - color: { value: new THREE.Color(0x00ff00) }, + color: { value: new THREE.Color(color) }, maxRadius: { value: 5.0 }, }, vertexShader: ` - varying vec3 vNormal; - void main() { - vNormal = normalize(normalMatrix * normal); - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); - } - `, + varying vec3 vNormal; + void main() { + vNormal = normalize(normalMatrix * normal); + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); + } + `, fragmentShader: ` - uniform float time; - uniform vec3 color; - uniform float maxRadius; - varying vec3 vNormal; - - void main() { - float intensity = 1.0 - (time * time); // Inverse square law decay - float edge = 0.2; - float rim = smoothstep(0.5 - edge, 0.5 + edge, dot(vNormal, vec3(0.0, 0.0, 1.0))); - gl_FragColor = vec4(color, intensity * (1.0 - rim) * 0.5); - } - `, + uniform float time; + uniform vec3 color; + uniform float maxRadius; + varying vec3 vNormal; + + void main() { + float intensity = 1.0 - (time * time); + float edge = 0.5; + float rim = smoothstep(0.5 - edge, 0.5 + edge, dot(vNormal, vec3(0.0, 0.0, 1.0))); + ${pulseEffect ? "float wave = sin(time * 20.0) * 0.5 + 0.5;" : "float wave = 1.0;"} + gl_FragColor = vec4(color, intensity * (1.0 - rim) * wave * 0.7); + } + `, }) - const empWave = new THREE.Mesh(empGeometry, empMaterial) - this.scene.add(empWave) + const waveMesh = new THREE.Mesh(waveGeometry, waveMaterial) + this.scene.add(waveMesh) - // Create wave object to track this specific wave const wave = { - mesh: empWave, - geometry: empGeometry, - material: empMaterial, + type, + mesh: waveMesh, + geometry: waveGeometry, + material: waveMaterial, startTime: Date.now(), - duration: 2000, // 2 seconds - maxScale: 10, + duration, + maxScale, } - // Add to active waves array - this.activeEMPWaves.push(wave) - - // Modify the animate method to handle multiple waves - if (this.activeEMPWaves.length === 1) { - // Only start if this is the first wave - this.animateEMPWaves() - } - } - - animateEMPWaves() { - // Process all active waves - for (let i = this.activeEMPWaves.length - 1; i >= 0; i--) { - const wave = this.activeEMPWaves[i] + // Start animation loop for this wave + const animateWave = () => { const elapsed = Date.now() - wave.startTime const progress = elapsed / wave.duration if (progress >= 1) { - // Remove completed wave this.scene.remove(wave.mesh) wave.geometry.dispose() wave.material.dispose() - this.activeEMPWaves.splice(i, 1) + this.activeWaves = this.activeWaves.filter((w) => w !== wave) } else { - // Update wave const scale = 1 + (wave.maxScale - 1) * progress wave.mesh.scale.set(scale, scale, scale) wave.material.uniforms.time.value = progress + requestAnimationFrame(animateWave) } } - // Continue animation if there are still active waves - if (this.activeEMPWaves.length > 0) { - requestAnimationFrame(() => this.animateEMPWaves()) - } + requestAnimationFrame(animateWave) + return wave } updateCameraPosition() { @@ -812,6 +763,7 @@ class Globe { this.drones.push(new Drone(this)) this.lastDroneSpawn = currentTime this.droneSpawnInterval = Math.max(10, this.droneSpawnInterval * 0.95) + this.droneSpeedMultiplier += 0.001 } // Check for drone collisions with Earth ------------------------------------------------------------
commit 00aea62201f0fa3833968abf8e09acedf70cfd03
Author: Breck Yunits <breck7@gmail.com> Date: Tue Nov 26 18:08:16 2024 -0800 diff --git a/WaveWar.js b/WaveWar.js index e005cf8..07eb0b3 100644 --- a/WaveWar.js +++ b/WaveWar.js @@ -56,8 +56,8 @@ class Drone { ) } - update(delta) { - if (this.globe.gameOver) return; + update(delta) { + if (this.globe.gameOver) return if (this.isExploding) { this.updateExplosion(delta) return @@ -110,8 +110,9 @@ class Drone { this.isExploding = true this.explosionStartTime = Date.now() // Increment score when drone is destroyed - this.globe.score++; - document.getElementById('score-display').textContent = `Drones Destroyed: ${this.globe.score}`; + this.globe.score++ + document.getElementById("score-display").textContent = + `Drones Destroyed: ${this.globe.score}` // Create explosion particles const particleCount = 20 @@ -138,28 +139,28 @@ class Drone { this.globe.scene.add(particles) this.explosionParticles = particles } -// Modify the Drone class's updateExplosion method + // Modify the Drone class's updateExplosion method updateExplosion(delta) { - const elapsed = Date.now() - this.explosionStartTime; - const progress = elapsed / this.explosionDuration; + const elapsed = Date.now() - this.explosionStartTime + const progress = elapsed / this.explosionDuration if (progress >= 1) { // Remove drone and particles - this.globe.scene.remove(this.mesh); - this.globe.scene.remove(this.explosionParticles); - this.globe.drones = this.globe.drones.filter((d) => d !== this); - - return; + this.globe.scene.remove(this.mesh) + this.globe.scene.remove(this.explosionParticles) + this.globe.drones = this.globe.drones.filter((d) => d !== this) + + return } // Update particle positions and scale this.explosionParticles.children.forEach((particle) => { - particle.position.add(particle.velocity); - particle.scale.multiplyScalar(0.95); - }); + particle.position.add(particle.velocity) + particle.scale.multiplyScalar(0.95) + }) // Fade out original drone - this.mesh.scale.multiplyScalar(0.9); + this.mesh.scale.multiplyScalar(0.9) } } @@ -172,10 +173,10 @@ class Globe { this.spikes = [] this.animationId = null this.stars = null -this.earthHealth = 1.0; // 1.0 = full health, 0 = dead - this.gameOver = false; - this.score = 0; - this.createScoreDisplay(); + this.earthHealth = 1.0 // 1.0 = full health, 0 = dead + this.gameOver = false + this.score = 0 + this.createScoreDisplay() // Camera control properties this.isDragging = false @@ -194,7 +195,7 @@ this.earthHealth = 1.0; // 1.0 = full health, 0 = dead // Zoom properties this.minZoom = 0.1 this.maxZoom = 10 - this.currentZoom = .7 + this.currentZoom = 0.7 this.zoomSpeed = 0.02 this.activeEMPWaves = [] @@ -212,26 +213,24 @@ this.earthHealth = 1.0; // 1.0 = full health, 0 = dead this.maxDrones = 200 // Maximum number of drones allowed } - - createScoreDisplay() { - const scoreDisplay = document.createElement("div"); - scoreDisplay.style.position = "fixed"; - scoreDisplay.style.top = "20px"; - scoreDisplay.style.right = "20px"; - scoreDisplay.style.padding = "10px 20px"; - scoreDisplay.style.backgroundColor = "rgba(0, 0, 0, 0.8)"; - scoreDisplay.style.color = "#4CAF50"; - scoreDisplay.style.borderRadius = "10px"; - scoreDisplay.style.fontSize = "24px"; - scoreDisplay.style.fontFamily = "monospace"; - scoreDisplay.style.zIndex = "1000"; - scoreDisplay.style.border = "1px solid rgba(76, 175, 80, 0.3)"; - scoreDisplay.id = "score-display"; - scoreDisplay.textContent = "Drones Destroyed: 0"; - document.body.appendChild(scoreDisplay); + createScoreDisplay() { + const scoreDisplay = document.createElement("div") + scoreDisplay.style.position = "fixed" + scoreDisplay.style.top = "20px" + scoreDisplay.style.right = "20px" + scoreDisplay.style.padding = "10px 20px" + scoreDisplay.style.backgroundColor = "rgba(0, 0, 0, 0.8)" + scoreDisplay.style.color = "#4CAF50" + scoreDisplay.style.borderRadius = "10px" + scoreDisplay.style.fontSize = "24px" + scoreDisplay.style.fontFamily = "monospace" + scoreDisplay.style.zIndex = "1000" + scoreDisplay.style.border = "1px solid rgba(76, 175, 80, 0.3)" + scoreDisplay.id = "score-display" + scoreDisplay.textContent = "Drones Destroyed: 0" + document.body.appendChild(scoreDisplay) } - createControlPanel() { const controlPanel = document.createElement("div") controlPanel.style.position = "fixed" @@ -315,12 +314,11 @@ this.earthHealth = 1.0; // 1.0 = full health, 0 = dead document.addEventListener("keydown", (e) => { if (e.key.toLowerCase() === "e") { this.handleAction("EMP") - }else if (e.key.toLowerCase() === " ") { + } else if (e.key.toLowerCase() === " ") { this.handleAction("EMP") } else if (e.key.toLowerCase() === "s") { this.handleAction("SOUND") - } - else if (e.key.toLowerCase() === "f") { + } else if (e.key.toLowerCase() === "f") { this.toggleFullScreen() } }) @@ -343,7 +341,7 @@ this.earthHealth = 1.0; // 1.0 = full health, 0 = dead } } - triggerSound() { + triggerSound() { const soundGeometry = new THREE.SphereGeometry(0.5, 32, 32) const soundMaterial = new THREE.ShaderMaterial({ transparent: true, @@ -576,20 +574,17 @@ this.earthHealth = 1.0; // 1.0 = full health, 0 = dead // Create starry background this.createStarField() - - // Create Earth with better lighting const geometry = new THREE.SphereGeometry(0.5, 32, 32) -const texture = new THREE.TextureLoader().load("earth_atmos_2048.jpg") + const texture = new THREE.TextureLoader().load("earth_atmos_2048.jpg") const material = new THREE.MeshPhongMaterial({ map: texture, specular: 0x333333, shininess: 5, - emissive: 0xff0000, // Red glow for damage - emissiveIntensity: 0 // Start with no damage glow + emissive: 0xff0000, // Red glow for damage + emissiveIntensity: 0, // Start with no damage glow }) - this.earth = new THREE.Mesh(geometry, material) this.scene.add(this.earth) @@ -613,85 +608,84 @@ const texture = new THREE.TextureLoader().load("earth_atmos_2048.jpg") } damageEarth() { - if (this.gameOver) return; - - this.earthHealth = Math.max(0, this.earthHealth - 0.1); // Reduce health by 10% - this.earth.material.emissiveIntensity = 1 - this.earthHealth; // Increase red glow + if (this.gameOver) return + + this.earthHealth = Math.max(0, this.earthHealth - 0.1) // Reduce health by 10% + this.earth.material.emissiveIntensity = 1 - this.earthHealth // Increase red glow if (this.earthHealth <= 0) { - this.triggerGameOver(); + this.triggerGameOver() } } - triggerGameOver() { - this.gameOver = true; - + this.gameOver = true + // Create explosion particles - const particleCount = 1000; - const particles = new THREE.Group(); - + const particleCount = 1000 + const particles = new THREE.Group() + for (let i = 0; i < particleCount; i++) { - const geometry = new THREE.BoxGeometry(0.02, 0.02, 0.02); + const geometry = new THREE.BoxGeometry(0.02, 0.02, 0.02) const material = new THREE.MeshPhongMaterial({ color: Math.random() > 0.5 ? 0xff4444 : 0xff7700, emissive: 0x441111, - }); - const particle = new THREE.Mesh(geometry, material); - + }) + const particle = new THREE.Mesh(geometry, material) + // Position particles on earth's surface - const theta = Math.random() * Math.PI * 2; - const phi = Math.random() * Math.PI; - const radius = 0.5; - - particle.position.x = radius * Math.sin(phi) * Math.cos(theta); - particle.position.y = radius * Math.sin(phi) * Math.sin(theta); - particle.position.z = radius * Math.cos(phi); - + const theta = Math.random() * Math.PI * 2 + const phi = Math.random() * Math.PI + const radius = 0.5 + + particle.position.x = radius * Math.sin(phi) * Math.cos(theta) + particle.position.y = radius * Math.sin(phi) * Math.sin(theta) + particle.position.z = radius * Math.cos(phi) + // Add velocity for explosion const velocity = new THREE.Vector3() .copy(particle.position) .normalize() - .multiplyScalar(0.03); - particle.velocity = velocity; - - particles.add(particle); + .multiplyScalar(0.03) + particle.velocity = velocity + + particles.add(particle) } - - this.scene.add(particles); - this.explosionParticles = particles; + + this.scene.add(particles) + this.explosionParticles = particles // Hide Earth - this.earth.visible = false; + this.earth.visible = false // Create game over text - const gameOverDiv = document.createElement('div'); - gameOverDiv.style.position = 'fixed'; - gameOverDiv.style.top = '50%'; - gameOverDiv.style.left = '50%'; - gameOverDiv.style.transform = 'translate(-50%, -50%)'; - gameOverDiv.style.color = '#ff0000'; - gameOverDiv.style.fontSize = '64px'; - gameOverDiv.style.fontFamily = 'Arial, sans-serif'; - gameOverDiv.style.fontWeight = 'bold'; - gameOverDiv.style.textShadow = '0 0 10px #ff0000'; - gameOverDiv.style.zIndex = '1000'; - gameOverDiv.innerHTML = `GAME OVER<br>Score: ${this.score}`; - document.body.appendChild(gameOverDiv); + const gameOverDiv = document.createElement("div") + gameOverDiv.style.position = "fixed" + gameOverDiv.style.top = "50%" + gameOverDiv.style.left = "50%" + gameOverDiv.style.transform = "translate(-50%, -50%)" + gameOverDiv.style.color = "#ff0000" + gameOverDiv.style.fontSize = "64px" + gameOverDiv.style.fontFamily = "Arial, sans-serif" + gameOverDiv.style.fontWeight = "bold" + gameOverDiv.style.textShadow = "0 0 10px #ff0000" + gameOverDiv.style.zIndex = "1000" + gameOverDiv.innerHTML = `GAME OVER<br>Score: ${this.score}` + document.body.appendChild(gameOverDiv) // Start explosion animation - this.animateExplosion(); + this.animateExplosion() } animateExplosion() { - if (!this.explosionParticles) return; - - this.explosionParticles.children.forEach(particle => { - particle.position.add(particle.velocity); - particle.scale.multiplyScalar(0.98); - }); - - requestAnimationFrame(() => this.animateExplosion()); + if (!this.explosionParticles) return + + this.explosionParticles.children.forEach((particle) => { + particle.position.add(particle.velocity) + particle.scale.multiplyScalar(0.98) + }) + + requestAnimationFrame(() => this.animateExplosion()) } setupMouseControls() { @@ -812,24 +806,25 @@ const texture = new THREE.TextureLoader().load("earth_atmos_2048.jpg") // Spawn new drones if ( currentTime - this.lastDroneSpawn > this.droneSpawnInterval && - this.drones.length < this.maxDrones && !this.gameOver + this.drones.length < this.maxDrones && + !this.gameOver ) { this.drones.push(new Drone(this)) this.lastDroneSpawn = currentTime - this.droneSpawnInterval = Math.max(10, this.droneSpawnInterval * .95) + this.droneSpawnInterval = Math.max(10, this.droneSpawnInterval * 0.95) } - // Check for drone collisions with Earth this.drones.forEach((drone) => { if (!drone.isExploding) { - const distanceFromCenter = drone.position.length(); - if (distanceFromCenter <= 0.52) { // Slightly larger than Earth's radius (0.5) - this.damageEarth(); - drone.startExplosion(); + const distanceFromCenter = drone.position.length() + if (distanceFromCenter <= 0.52) { + // Slightly larger than Earth's radius (0.5) + this.damageEarth() + drone.startExplosion() } } - }); + }) // Update drones this.drones.forEach((drone) => drone.update(delta)) @@ -923,19 +918,18 @@ const texture = new THREE.TextureLoader().load("earth_atmos_2048.jpg") } toggleFullScreen() { -if (!document.fullscreenElement) { - document.body.requestFullscreen().catch((err) => { - console.log( - `Error trying to go fullscreen: ${err.message} (${err.name})`, - ) - }) - } else { - document.exitFullscreen() - } + if (!document.fullscreenElement) { + document.body.requestFullscreen().catch((err) => { + console.log( + `Error trying to go fullscreen: ${err.message} (${err.name})`, + ) + }) + } else { + document.exitFullscreen() + } } shouldRotate = true - } window.globe = new Globe().createGlobe().listenToResize() ------------------------------------------------------------
commit b1f85e919787f7a33936fcb520e4ae584966d83e
Author: Breck Yunits <breck7@gmail.com> Date: Sun Nov 24 15:32:32 2024 -0800 diff --git a/WaveWar.js b/WaveWar.js index 7fcfea8..e005cf8 100644 --- a/WaveWar.js +++ b/WaveWar.js @@ -209,7 +209,7 @@ this.earthHealth = 1.0; // 1.0 = full health, 0 = dead this.drones = [] this.lastDroneSpawn = 0 this.droneSpawnInterval = 1000 // Spawn a new drone every 2 seconds - this.maxDrones = 500 // Maximum number of drones allowed + this.maxDrones = 200 // Maximum number of drones allowed } ------------------------------------------------------------
commit 237586d9e6db57e61b1509e9d5d8953059347686
Author: Breck Yunits <breck7@gmail.com> Date: Sun Nov 24 15:31:17 2024 -0800 diff --git a/WaveWar.js b/WaveWar.js index ac8a82e..7fcfea8 100644 --- a/WaveWar.js +++ b/WaveWar.js @@ -209,7 +209,7 @@ this.earthHealth = 1.0; // 1.0 = full health, 0 = dead this.drones = [] this.lastDroneSpawn = 0 this.droneSpawnInterval = 1000 // Spawn a new drone every 2 seconds - this.maxDrones = 1000 // Maximum number of drones allowed + this.maxDrones = 500 // Maximum number of drones allowed } @@ -816,7 +816,7 @@ const texture = new THREE.TextureLoader().load("earth_atmos_2048.jpg") ) { this.drones.push(new Drone(this)) this.lastDroneSpawn = currentTime - this.droneSpawnInterval = Math.max(10, this.droneSpawnInterval * .98) + this.droneSpawnInterval = Math.max(10, this.droneSpawnInterval * .95) } ------------------------------------------------------------
commit 2d2763e5abe34808bc18e04c807353f66d9a6995
Author: Breck Yunits <breck7@gmail.com> Date: Sun Nov 24 15:29:29 2024 -0800 diff --git a/WaveWar.js b/WaveWar.js index 76e816d..ac8a82e 100644 --- a/WaveWar.js +++ b/WaveWar.js @@ -816,7 +816,7 @@ const texture = new THREE.TextureLoader().load("earth_atmos_2048.jpg") ) { this.drones.push(new Drone(this)) this.lastDroneSpawn = currentTime - this.droneSpawnInterval = Math.max(10, this.droneSpawnInterval * .99) + this.droneSpawnInterval = Math.max(10, this.droneSpawnInterval * .98) } ------------------------------------------------------------
commit b1ab71f7d0992f861c10338464fc12cead1484a2
Author: Breck Yunits <breck7@gmail.com> Date: Sun Nov 24 15:27:38 2024 -0800 diff --git a/WaveWar.js b/WaveWar.js index acd763a..76e816d 100644 --- a/WaveWar.js +++ b/WaveWar.js @@ -812,7 +812,7 @@ const texture = new THREE.TextureLoader().load("earth_atmos_2048.jpg") // Spawn new drones if ( currentTime - this.lastDroneSpawn > this.droneSpawnInterval && - this.drones.length < this.maxDrones + this.drones.length < this.maxDrones && !this.gameOver ) { this.drones.push(new Drone(this)) this.lastDroneSpawn = currentTime ------------------------------------------------------------
commit 4d0d9575443368de665d7e88821cb692634a3c8e
Author: Breck Yunits <breck7@gmail.com> Date: Sun Nov 24 15:26:29 2024 -0800 diff --git a/WaveWar.js b/WaveWar.js index b8dc4f3..acd763a 100644 --- a/WaveWar.js +++ b/WaveWar.js @@ -1,7 +1,7 @@ class Drone { constructor(globe) { this.globe = globe - this.speed = 0.01 + Math.random() * 0.02 + this.speed = 0.01 + Math.random() * 0.2 this.mesh = this.createDroneMesh() this.position = this.getRandomPosition() this.target = this.getRandomPosition() @@ -56,7 +56,8 @@ class Drone { ) } - update(delta) { + update(delta) { + if (this.globe.gameOver) return; if (this.isExploding) { this.updateExplosion(delta) return @@ -171,7 +172,8 @@ class Globe { this.spikes = [] this.animationId = null this.stars = null - +this.earthHealth = 1.0; // 1.0 = full health, 0 = dead + this.gameOver = false; this.score = 0; this.createScoreDisplay(); @@ -574,17 +576,20 @@ class Globe { // Create starry background this.createStarField() + + // Create Earth with better lighting const geometry = new THREE.SphereGeometry(0.5, 32, 32) - const texture = new THREE.TextureLoader().load("earth_atmos_2048.jpg") - - // Use PhongMaterial for better lighting +const texture = new THREE.TextureLoader().load("earth_atmos_2048.jpg") const material = new THREE.MeshPhongMaterial({ map: texture, specular: 0x333333, shininess: 5, + emissive: 0xff0000, // Red glow for damage + emissiveIntensity: 0 // Start with no damage glow }) + this.earth = new THREE.Mesh(geometry, material) this.scene.add(this.earth) @@ -607,6 +612,88 @@ class Globe { return this } + damageEarth() { + if (this.gameOver) return; + + this.earthHealth = Math.max(0, this.earthHealth - 0.1); // Reduce health by 10% + this.earth.material.emissiveIntensity = 1 - this.earthHealth; // Increase red glow + + if (this.earthHealth <= 0) { + this.triggerGameOver(); + } + } + + + triggerGameOver() { + this.gameOver = true; + + // Create explosion particles + const particleCount = 1000; + const particles = new THREE.Group(); + + for (let i = 0; i < particleCount; i++) { + const geometry = new THREE.BoxGeometry(0.02, 0.02, 0.02); + const material = new THREE.MeshPhongMaterial({ + color: Math.random() > 0.5 ? 0xff4444 : 0xff7700, + emissive: 0x441111, + }); + const particle = new THREE.Mesh(geometry, material); + + // Position particles on earth's surface + const theta = Math.random() * Math.PI * 2; + const phi = Math.random() * Math.PI; + const radius = 0.5; + + particle.position.x = radius * Math.sin(phi) * Math.cos(theta); + particle.position.y = radius * Math.sin(phi) * Math.sin(theta); + particle.position.z = radius * Math.cos(phi); + + // Add velocity for explosion + const velocity = new THREE.Vector3() + .copy(particle.position) + .normalize() + .multiplyScalar(0.03); + particle.velocity = velocity; + + particles.add(particle); + } + + this.scene.add(particles); + this.explosionParticles = particles; + + // Hide Earth + this.earth.visible = false; + + // Create game over text + const gameOverDiv = document.createElement('div'); + gameOverDiv.style.position = 'fixed'; + gameOverDiv.style.top = '50%'; + gameOverDiv.style.left = '50%'; + gameOverDiv.style.transform = 'translate(-50%, -50%)'; + gameOverDiv.style.color = '#ff0000'; + gameOverDiv.style.fontSize = '64px'; + gameOverDiv.style.fontFamily = 'Arial, sans-serif'; + gameOverDiv.style.fontWeight = 'bold'; + gameOverDiv.style.textShadow = '0 0 10px #ff0000'; + gameOverDiv.style.zIndex = '1000'; + gameOverDiv.innerHTML = `GAME OVER<br>Score: ${this.score}`; + document.body.appendChild(gameOverDiv); + + // Start explosion animation + this.animateExplosion(); + } + + animateExplosion() { + if (!this.explosionParticles) return; + + this.explosionParticles.children.forEach(particle => { + particle.position.add(particle.velocity); + particle.scale.multiplyScalar(0.98); + }); + + requestAnimationFrame(() => this.animateExplosion()); + } + setupMouseControls() { const canvas = this.renderer.domElement @@ -732,6 +819,18 @@ class Globe { this.droneSpawnInterval = Math.max(10, this.droneSpawnInterval * .99) } + + // Check for drone collisions with Earth + this.drones.forEach((drone) => { + if (!drone.isExploding) { + const distanceFromCenter = drone.position.length(); + if (distanceFromCenter <= 0.52) { // Slightly larger than Earth's radius (0.5) + this.damageEarth(); + drone.startExplosion(); + } + } + }); + // Update drones this.drones.forEach((drone) => drone.update(delta))