Code edit (21 edits merged)
Please save this source code
User prompt
in decreaseHealth if (self.health <= 0), add play of explosion sound 2 times with 200ms interval
Code edit (1 edits merged)
Please save this source code
Code edit (8 edits merged)
Please save this source code
User prompt
create a separate variable to prepare the mission time in minutes and seconds to use in finalStatsText
Code edit (4 edits merged)
Please save this source code
User prompt
in initScoreState instanciate a text2 in finalStatsText. with the text " Our Hero Mission Time: ... Sectors Cleared: ... Enemies Beated: ... Lasers Stopped: ... Team Kills: ... Shots Fired: ... Precision : ... " filled with stats
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
log also Lasers stopped
User prompt
log all the stats in initScoreState
Code edit (1 edits merged)
Please save this source code
User prompt
update the new variables properly
User prompt
ok. add the new variables
Code edit (3 edits merged)
Please save this source code
User prompt
play teamkill sound on team kill
User prompt
Show a big centered Message "TEAM KILL!" during 2 seconds on team kill
Code edit (8 edits merged)
Please save this source code
User prompt
add a violet flash on team kill
Code edit (1 edits merged)
Please save this source code
User prompt
update `star.rotation = !star.slow && starsSpeed == starsSpeedFast ? Math.PI * 0.75 * Math.sign(directionX) * Math.sign(directionY) : 0;` to take into account the star position to caculate the angle
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (16 edits merged)
Please save this source code
/**** * Classes ****/ /***********************************************************************************/ /********************************** BULLET CLASS ***********************************/ /***********************************************************************************/ var Bullet = Container.expand(function (isLeft, isTop) { var self = Container.call(this); self.isTop = isTop; var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5, width: 5, height: 250, tint: 0x00FF00, alpha: 0.9 }); self.distanceToCenter = 1024; self.isLeft = isLeft; self.speed = 10; self.update = function () { self.x += self.isLeft ? self.speed : -self.speed; self.y += self.isTop ? self.speed * 0.6 : -self.speed * 0.6; // Calculate distance to center and adjust width accordingly var deltaX = 1024 - self.x; // Left: 1024 => 0- / Right: -1024 => 0+ self.distanceToCenter = Math.abs(deltaX); var shrinkRatio = 0.1 + Math.pow(self.distanceToCenter, 1.2) * 5 / Math.pow(1024, 1.2); self.width = Math.max(0.5, 6 * Math.pow(shrinkRatio, 1.1)); self.height = Math.max(10, 250 * Math.pow(shrinkRatio, 1.1)); self.speed = Math.max(40, Math.min(100, 100 * Math.exp(self.distanceToCenter / 1024 - 1))); var touched = self.checkForCollision(); if (!touched && currentFriend) { touched = self.checkForFriendCollision(); } // Check if passed center if (!touched && (self.isLeft && deltaX <= 0 || !self.isLeft && deltaX >= 0)) { self.destroy(); } }; self.checkForCollision = function () { if (!currentFighter) { return; } var touched = (currentFighter.x < 1024 && self.isLeft || currentFighter.x >= 1024 && !self.isLeft) && self.intersects(currentFighter) && self.distanceToCenter <= 75; if (!touched) { return self.checkForEnmeyBulletCollision(); } if (isDebug) { debugText.setText("Distance to Center: " + self.distanceToCenter.toFixed(0)); } // Handle collision: destroy bullet and enemy, update score, etc. self.destroy(); currentFighter.explode(); // Update score updateScore(pointsKillFoe * (currentFighter.flyMode == 3 ? 0.8 : currentFighter.flyMode == 2 ? 0.9 : 1)); return true; }; self.checkForEnmeyBulletCollision = function () { if (!currentEnemyBullet || currentEnemyBullet.isDestroyed || !self.isLeft) { return false; } log("Check Enemy Bullet collid", currentEnemyBullet.x, currentEnemyBullet.y); var touched = self.intersects(currentEnemyBullet) || self.intersects(currentEnemyBulletPair); if (!touched) { return false; } currentEnemyBullet.explode(); currentEnemyBulletPair.explode(); successfulHits++; // Increment successful hits log("checkForEnmeyBulletCollision successfulHits++ => ", successfulHits); lasersStopped++; // Increment lasers stopped // Update score updateScore(pointsKillEnemyBullet); self.destroy(); return true; }; self.checkForFriendCollision = function () { if (!currentFriend) { return; } var touched = (currentFriend.x < 1024 && self.isLeft || currentFriend.x >= 1024 && !self.isLeft) && self.intersects(currentFriend) && self.distanceToCenter <= 75; if (!touched) { return false; } // Handle collision: destroy bullet and enemy, update score, etc. self.destroy(); currentFriend.explode(); currentTeamKill++; // Update score updateScore(pointsKillFriend * currentTeamKill); // Add violet flash effect on team kill LK.effects.flashScreen(0x8A2BE2, 1000); // Flash screen violet for 1 second // Play teamkill sound LK.getSound('teamKill').play(); // Show 'TEAM KILL!' message var teamKillMessage = new Text2('TEAM KILL!', { size: 180, fill: "#FF0000", font: "Arial", weight: 1000 }); teamKillMessage.anchor.set(0.5, 0.5); teamKillMessage.x = 1024; teamKillMessage.y = 683; game.addChild(teamKillMessage); LK.setTimeout(function () { game.removeChild(teamKillMessage); teamKillMessage.destroy(); }, 2000); return true; }; }); /***********************************************************************************/ /********************************** COCKPIT CLASS ***********************************/ /***********************************************************************************/ var CockpitDisplay = Container.expand(function () { var self = Container.call(this); self.cockpitButtonStand = self.attachAsset('fireButtonStand', { anchorX: 0.5, anchorY: 0.5, alpha: 0.9, y: 2732 + 300 }); self.cockpitButtonBase = self.attachAsset('fireButtonBase', { anchorX: 0.5, anchorY: 0.5, alpha: 1, y: 2732 }); self.cockpitButton = self.attachAsset('fireButton', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 2732 - 570 }); self.cockpitButton.originalScaleX = self.cockpitButton.scale.x; self.cockpitButton.originalScaleY = self.cockpitButton.scale.y; self.cockpitButton.isAnimating = false; }); /***********************************************************************************/ /********************************** ENEMY BULLET CLASS *****************************/ /***********************************************************************************/ var EnemyBullet = Container.expand(function (isLeft) { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5, width: 2, height: 1, tint: 0xFF0000, alpha: 0.9 }); self.speed = 8; self.isLeft = isLeft; self.isDestroyed = false; self.rotation = (isLeft ? -1 : 1) * Math.PI * 0.9; self.update = function () { if (self.isDestroyed) { return; } self.speed += self.speed * 0.05; // Increase speed as it gets farther self.y += self.speed; self.x += (self.isLeft ? -1 : 1) * self.speed * 0.25; self.width = Math.min(15, self.width + 0.5); self.height = Math.min(512, self.height + 4); // Destroy bullet if it goes off screen if (self.y > 2732 || self.x < 0 || self.x > 2048) { currentEnemyBullet = null; currentEnemyBulletPair = null; self.destroy(); var rand = Math.random(); log("Rand struck ", rand, " / ", currentLevel * 0.5); if (self.isLeft && rand < currentLevel * 0.5) { spaceship.struck(); } } }; self.explode = function (callback) { if (self.isDestroyed) { return; } self.isDestroyed = true; LK.effects.flashScreen(0xFFFFFF, 1240); // Flash screen red for 1 second log("Enemy Bullet Killed !"); LK.getSound('explosion').play(); self.destroy(); }; }); /***********************************************************************************/ /********************************** FIGHTER CLASS **********************************/ /***********************************************************************************/ var Fighter = Container.expand(function (index, model, isFriend) { var self = Container.call(this); self.baseSize = 512; var outOffset = 512; var centerX = 1024; var centerY = 1366; var friendShotThreshold = 0.25; self.isFriend = isFriend; self.assetName = "fighter1"; model = model || 1; if (self.isFriend) { model = Math.max(1, Math.min(nbFriendModels, model)); self.assetName = "friend" + model; ; } else { model = Math.max(1, Math.min(nbFigherModels, model)); // 1 -> nbFigherModels self.assetName = 'fighter' + model; } self.fighterGraphics = self.attachAsset(self.assetName, { anchorX: 0.5, anchorY: 0.5, width: self.baseSize, height: self.baseSize }); var passingSound = LK.getSound('fighterPassing'); self.index = index; self.isActive = false; self.hasShot = false; self.hasPassed = false; self.speedX = 4; self.speedY = 4; self.speedZ = 1; self.dirX = 0; self.dirY = 0; self.sizeRatio = 1; self.finalSpeedX = 0; self.finalSpeedY = 0; self.finalSpeedZ = 0; self.isDestroyed = false; self.speedYModifier = 1; self.flyMode = 1; self.previousFlyMode = 1; self.update = function () { if (!self.isActive) { return; } // Update size on z distance self.sizeRatio = initialFighterZ / (Math.abs(self.z) * 20 + 0.1); self.sizeRatio = Math.max(0.1, Math.min(2, self.sizeRatio)); self.width = self.baseSize * self.sizeRatio; self.height = self.baseSize * self.sizeRatio; //log("iniZ=" + initialFighterZ, "z=" + self.z.toFixed(0), "sizeRatio=" + self.sizeRatio, " baseSize" + self.baseSize, " => w=h=" + self.width); // Update speed on z distance if (self.sizeRatio <= 1) { self.speedZ = self.speedZ + 0.015; } else { self.speedZ = self.speedZ * self.sizeRatio * 0.90; } // Fix speed when destroyed if (self.isDestroyed) { // Stop increase speed when destroyed if (!self.finalSpeedY) { self.finalSpeedX = self.speedX * 0.5; self.finalSpeedY = self.speedY * 0.5; self.finalSpeedZ = self.speedZ * 0.5; } self.speedX = self.finalSpeedX; self.speedY = self.finalSpeedY; self.speedZ = self.finalSpeedZ; } switch (self.flyMode) { case 2: self.updateFlyMode2(); break; case 3: self.updateFlyMode3(); break; default: self.updateFlyMode1(); } // Update position self.x += self.dirX * self.speedX * currentlevelSpeed; self.y += self.dirY * self.speedY * currentlevelSpeed; self.z -= self.speedZ * currentlevelSpeed; // Passing sound if (!self.isDestroyed && !self.hasPassed && self.z < 380) { self.hasPassed = true; lastFighterPassTime = Date.now(); passingSound.play(); } // Remove when out of screen if (!self.isDestroyed && self.checkIsOut()) { log(self.index + ") Fighter passed behind. cleaning currentFighter...Friend=" + self.isFriend); self.reset(); if (self.isFriend) { currentFriend = null; } else { currentFighter = null; } } }; self.checkIsOut = function () { if (self.flyMode == 3) { return self.speedX < 0 && self.x <= -outOffset || self.speedX >= 0 && self.x > 2048 + outOffset; } return self.z <= 0 || self.speedX <= 0 && self.x < -outOffset || self.speedY <= 0 && self.y < -outOffset || self.speedX >= 0 && self.x > 2048 + outOffset || self.speedY >= 0 && self.y > 2732 + outOffset; }; self.updateFlyMode1 = function () { self.dirY = Math.sign(self.y); var length = Math.sqrt(self.dirX * self.dirX + self.dirY * self.dirY); if (length === 0) { length = 1; } self.dirX /= length; self.dirY /= length; if (self.z > 1024) { self.dirX += current3DSpaceAngles.deltaH * 10; self.dirY += current3DSpaceAngles.deltaV * self.speedYModifier; } else { self.y += self.dirY * (self.sizeRatio > 0.3 ? 50 * Math.pow(self.sizeRatio, 2) : 0.5); if (!self.isDestroyed && !self.hasShot) { self.hasShot = true; self.shoot(); } } var distanceFromCenter = self.x - 1024; // Calculate the rotation based on the distance from the center self.rotation = distanceFromCenter / 1024 * Math.PI * 0.25; }; self.updateFlyMode2 = function () { var deltaX = centerX - self.x; var deltaY = centerY - self.y; var rand = Math.random(); var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (self.speedX > 0 && deltaX > 42 || self.speedX < 0 && deltaX < -42) { self.dirX = 6 * Math.abs(deltaX) / distance; self.dirY = 6 * Math.abs(deltaY) / distance; } else { self.dirX = 6; self.dirY = 6; } if (self.isFriend && !self.isDestroyed && currentFighter && !currentFighter.isDestroyed && !self.hasShot && rand >= friendShotThreshold && Math.abs(deltaX) < rand * 480) { log("FriendShot..."); self.hasShot = true; self.shoot(); } // Accelerate y move when close self.x += self.dirX * (self.sizeRatio > 0.3 ? 10 * Math.pow(self.sizeRatio, 2) : 0.5) * Math.sign(self.speedX); self.y += self.dirY * (self.sizeRatio > 0.3 ? 10 * Math.pow(self.sizeRatio, 2) : 0.5) * Math.sign(self.speedY); }; self.updateFlyMode3 = function () { self.dirX = 10; self.dirY = 1.5; var rand = Math.random(); var deltaX = centerX - self.x; if (self.isFriend && !self.isDestroyed && currentFighter && !currentFighter.isDestroyed && !self.hasShot && rand >= friendShotThreshold && Math.abs(deltaX) < Math.random() * 480) { log("FriendShot..."); self.hasShot = true; self.shoot(); } // Accelerate y move when close self.x += self.dirX * (self.sizeRatio > 0.3 ? 10 * Math.pow(self.sizeRatio, 2) : 0.5) * Math.sign(self.speedX); self.y += self.dirY * (self.sizeRatio > 0.3 ? 10 * Math.pow(self.sizeRatio, 2) : 0.5) * Math.sign(self.speedY); }; self.reset = function () { var rand = Math.random(); self.visible = false; self.isActive = false; self.hasShot = false; self.hasPassed = false; self.speedX = 1; self.speedY = 1; self.speedZ = 1; self.sizeRatio = 1; self.rotation = 0; self.width = self.baseSize * self.sizeRatio; self.height = self.baseSize * self.sizeRatio; //log(self.index + ") Reseting width to :" + (self.baseSize * self.sizeRatio).toFixed(0), " => " + self.width); self.finalSpeedY = 0; self.finalSpeedZ = 0; self.speedYModifier = Math.random() * 3; self.isDestroyed = false; self.flyMode = Math.ceil(Math.random() * 3); if (!self.isFriend) { if (self.flyMode == self.previousFlyMode) { //self.flyMode = (self.flyMode - 1 + Math.floor(Math.random() * 2) + 1) % 3 + 1; self.flyMode = (self.flyMode + 1) % 3 + 1; } self.previousFlyMode = self.flyMode; if (Date.now() - currentLevelStartTime > 100000 + currentLevel * 1000 * 20) { // Force attak if lasting more than 3min+ self.flyMode = 1; } } log(self.index + ") FlyMode=" + self.flyMode, " time=", Math.floor((Date.now() - currentLevelStartTime) / 180000)); switch (self.flyMode) { case 2: self.initFlyMode2(rand); break; case 3: self.initFlyMode3(rand); break; default: self.initFlyMode1(rand); } if (self.isFriend) { self.setPursuitPosition(); } }; self.setPursuitPosition = function () { if (!currentFighter || currentFighter.flyMode < 2) { return; } log("setPursuitPosition..."); var rand = Math.random(); self.speedYModifier = currentFighter.speedYModifier; self.isDestroyed = false; self.flyMode = currentFighter.flyMode; var followOffset = 512 + 256 * rand; switch (self.flyMode) { case 2: self.speedZ = 1.2; self.x = currentFighter.x > 1024 ? currentFighter.x + followOffset : currentFighter.x - followOffset; self.z = currentFighter.z; self.y = currentFighter.y; self.speedX = currentFighter.speedX; self.speedY = currentFighter.speedY; self.rotation = currentFighter.rotation; break; case 3: self.speedZ = 0.8; self.x = currentFighter.x > 1024 ? currentFighter.x + followOffset : currentFighter.x - followOffset; self.z = currentFighter.z; self.y = currentFighter.y; self.speedX = currentFighter.speedX; self.speedY = currentFighter.speedY; self.rotation = currentFighter.rotation; break; default: log("INVALID FLY MODE FOR FRIEND : ", self.flyMode); } }; self.initFlyMode1 = function (rand) { self.x = rand > 0.5 ? -512 : 2048 + 512; self.z = initialFighterZ; self.y = 512 - 512 * rand; }; self.initFlyMode2 = function (rand) { self.speedZ = 1.2; self.x = rand > 0.5 ? 1700 + 42 * rand : 42 * rand + 460; self.z = initialFighterZ - 768 - 256 * rand; self.y = 512 - 512 * rand; self.speedX = rand > 0.5 ? -1 : 1; self.rotation = Math.PI * 0.25 * (rand <= 0.5 ? -1 : 1); }; self.initFlyMode3 = function (rand) { self.speedZ = 0.8; self.x = rand > 0.5 ? 2048 + 42 : -42; self.z = 512 - 128 * rand; self.y = centerY - 64 + 64 * (rand > 0.5 ? 1.5 : -3); self.speedX = rand > 0.5 ? -1 : 1; self.speedY = rand > 0.5 ? -0.8 : 1; self.rotation = Math.PI * 0.5 * (rand <= 0.5 ? -1 : 1); }; self.activate = function () { self.visible = true; self.isActive = true; }; self.shoot = function () { if (self.isFriend) { var bullet = new FriendBullet(self.rotation); if (self.flyMode == 2) { bullet.x = self.x - 32 * Math.sign(self.speedX); // + 100 * self.speedX; //* Math.sign(self.speedX) bullet.y = self.y + 32; // + 100 * self.speedY; bullet.speedX = self.speedX * 3; bullet.speedY = self.speedY * 3; } if (self.flyMode == 3) { bullet.x = self.x + 256 * self.speedX; bullet.y = self.y - 128 * (self.speedX < 0 ? 1 : 0); bullet.speedX = self.speedX * 3; bullet.speedY = 0; } middleLayer.addChildAt(bullet, 2); LK.getSound('laserShot2').play(); LK.setTimeout(function () { var bullet2 = new FriendBullet(self.rotation); if (self.flyMode == 2) { bullet2.x = self.x + 32 * Math.sign(self.speedX); // + 100 * self.speedX; bullet2.y = self.y - 32; // + 100 * self.speedY; bullet2.speedX = self.speedX * 3; bullet2.speedY = self.speedY * 3; } if (self.flyMode == 3) { bullet2.x = self.x + 256 * self.speedX; bullet2.y = self.y + 64 * (self.speedX > 0 ? 1 : 0); bullet2.speedX = self.speedX * 3; bullet2.speedY = 0; } middleLayer.addChildAt(bullet2, 2); LK.getSound('laserShot2').play(); }, 128 + Math.random() * 128 * (self.flyMode == 3 ? 0 : 1)); } else { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y; currentEnemyBullet = bullet; middleLayer.addChildAt(bullet, 2); var bullet2 = new EnemyBullet(true); bullet2.x = self.x; bullet2.y = self.y; currentEnemyBulletPair = bullet2; middleLayer.addChildAt(bullet2, 2); LK.getSound('laserShot2').play(); LK.setTimeout(function () { LK.getSound('laserShot2').play(); }, 128); } }; self.explode = function (callback) { if (self.isDestroyed) { return; } self.isDestroyed = true; passingSound.stop(); log(self.index + ") Killed"); if (!self.isFriend) { successfulHits++; // Increment successful hits log(self.index + ") explode successfulHits++ => ", successfulHits); enemiesStopped++; // Increment enemies stopped } var explosion = self.attachAsset('explosion1', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, width: 256, height: 256, rotation: Math.PI * 2 * Math.random() }); var explosion2 = self.attachAsset('explosion2', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0, width: 128, height: 128, rotation: Math.PI * 2 * Math.random() }); // Play explosion sound LK.getSound('explosion').play(); var growInterval = LK.setInterval(function () { explosion.width += 50; explosion.height += 50; if (explosion.width >= self.fighterGraphics.width) { if (explosion.width >= 512) { self.fighterGraphics.visible = false; } if (explosion.width >= 1024) { explosion2.width += 50; explosion2.height += 50; explosion.alpha -= 0.05; } if (explosion2.width >= 2048) { explosion2.alpha -= 0.05; } if (explosion2.width >= 3000 * 2) { LK.clearInterval(growInterval); explosion.destroy(); explosion2.destroy(); if (self.isFriend) { // TODO TEAM KILL ALARM currentFriend = null; log("FRIEND KILL!!!"); } else { lastFighterKillTime = Date.now(); currentNbEnnemies--; if (!currentNbEnnemies) { log("All enemies cleared => Next level"); cleanPlayingState(); } else { log(currentNbEnnemies + " Enemy to go"); currentFighter = null; } } } } }, 16); // 60 FPS }; // Reset the fighter's properties self.reset(); }); /***********************************************************************************/ /********************************** FRIEND BULLET CLASS *****************************/ /***********************************************************************************/ var FriendBullet = Container.expand(function (rotation) { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5, width: 4, height: 16, tint: 0x7DF9FF, alpha: 0.9 }); self.speedX = 0; self.speedY = 0; self.isDestroyed = false; self.rotation = rotation; self.update = function () { if (self.isDestroyed) { return; } self.speedX += self.speedX * 0.15; // Increase speed as it gets farther self.speedY += self.speedY * 0.15; // Increase speed as it gets farther self.x += self.speedX; self.y += self.speedY; self.height = Math.min(512, self.height + 4); // Destroy bullet if it goes off screen if (self.x < 0 || self.x > 2048 || self.y < 0 || self.y > 2732) { self.destroy(); } }; }); /***********************************************************************************/ /********************************** SPACE OBJECT CLASS *****************************/ /***********************************************************************************/ var SpaceObject = Container.expand(function (index) { var self = Container.call(this); index = Math.min(nbSpaceObjects - 1, Math.max(0, index || 0)); var spaceObjectGraphics = self.attachAsset('spaceObject' + index, { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0.33; self.mvtRatioX = 1; self.mvtRatioY = 1; self.dirX = 1; self.dirY = 1; self.update = function () { self.x += (current3DSpaceAngles.deltaH || self.dirX * self.mvtRatioX) * self.speed; self.y += (current3DSpaceAngles.deltaV || self.dirY * self.mvtRatioY) * self.speed; //background.x += current3DSpaceAngles.deltaH; //background.y += current3DSpaceAngles.deltaV; }; self.initDirection = function () { self.dirX = -Math.sign(self.x); self.dirY = -Math.sign(self.y); self.mvtRatioX = 0.1 + Math.random(); self.mvtRatioY = 0.1 + Math.random(); }; self.initDirection(); }); /***********************************************************************************/ /******************************** SPACESHIP CLASS **********************************/ /***********************************************************************************/ var Spaceship = Container.expand(function () { var self = Container.call(this); var canonLeft = self.attachAsset('canon', { anchorX: 0.5, anchorY: 0.5, x: -1100, y: 650, rotation: -Math.PI * 0.01 }); var canonRight = self.attachAsset('canon', { anchorX: 0.5, anchorY: 0.5, scaleX: -1, x: 1100, y: 650, rotation: Math.PI * 0.01 }); self.centralHud = self.attachAsset('aimingCentralHud', { anchorX: 0.5, anchorY: 0.5, alpha: 0.6, y: -100, tint: 0x00FF00 }); self.pointerHud = self.attachAsset('pointerHud', { anchorX: 0.5, anchorY: 4, alpha: 0.5, x: 0, y: -100, tint: 0xFF0000, rotation: Math.PI * 0.5 }); var spaceshipGraphics = self.attachAsset('spaceship', { anchorX: 0.5, anchorY: 0.5 }); var spaceshipDeco1 = self.attachAsset('shipDeco1', { anchorX: 0.5, anchorY: 0.5, x: -955, y: 1185, alpha: 0.8, rotation: -0.35, tint: 0xFFFFFF }); self.energyBar = new Container(); self.energyBar.alpha = 0.75; self.energyBarY = 480; self.energyBarFrame = self.energyBar.attachAsset('energyBarFrame', { anchorX: 0.5, anchorY: 0.5, width: 500, height: 30, x: 10, y: self.energyBarY }); self.energyBarFill = self.energyBar.attachAsset('energyBar', { anchorX: 0, anchorY: 0.5, width: 465, height: 15, x: -215, y: self.energyBarY }); self.energyBarMask = self.energyBar.attachAsset('energyBarMask', { anchorX: 0, anchorY: 0.5, width: 460, height: 15, x: 247, y: self.energyBarY }); self.addChild(self.energyBar); self.damageScreen = new Container(); self.damageScreenBack = self.damageScreen.attachAsset('boardBlackScreen', { anchorX: 0.5, anchorY: 0.5, width: 215, height: 290, x: -350, y: 1020 }); self.damageScreenSchema = self.damageScreen.attachAsset('spaceshipSchema', { anchorX: 0.5, anchorY: 0.5, width: 200, height: 200, scaleX: 0.95, scaleY: 0.95, x: -350, y: 1020, alpha: 0.8, tint: 0x00ff67, visible: true }); self.damageScreenSchema1 = self.damageScreen.attachAsset('spaceshipSchema1', { anchorX: 0.5, anchorY: 0.5, width: 100, height: 100, x: -400, y: 970, alpha: 0, tint: 0xFFFFFF, damage: 0 // 0 : White / 1 : Orange / 2 : Red }); self.damageScreenSchema2 = self.damageScreen.attachAsset('spaceshipSchema2', { anchorX: 0.5, anchorY: 0.5, width: 100, height: 100, x: -300, y: 970, alpha: 0, tint: 0xFFFFFF, damage: 0 }); self.damageScreenSchema3 = self.damageScreen.attachAsset('spaceshipSchema3', { anchorX: 0.5, anchorY: 0.5, width: 100, height: 100, x: -400, y: 1070, alpha: 0, tint: 0xFFFFFF, damage: 0 }); self.damageScreenSchema4 = self.damageScreen.attachAsset('spaceshipSchema4', { anchorX: 0.5, anchorY: 0.5, width: 100, height: 100, x: -300, y: 1070, alpha: 0, tint: 0xFFFFFF, damage: 0 }); self.addChild(self.damageScreen); self.radarScreen = new Container(); self.radarScreenBack = self.radarScreen.attachAsset('boardBlackScreen', { anchorX: 0.5, anchorY: 0.5, width: 235, height: 310, x: 352, y: 1020 }); self.radarScreenHud = self.radarScreen.attachAsset('radarHud', { anchorX: 0.5, anchorY: 0.5, width: 170, height: 170, x: 360, y: 1040, alpha: 0.8 }); self.radarBarHud = self.radarScreen.attachAsset('radarBar', { anchorX: 0.5, anchorY: 0, width: 4, height: 80, x: 360, y: 1050, alpha: 0.8 }); self.radarScreenFoeHudUpdated = false; self.radarScreenFoeHud = self.radarScreen.attachAsset('fighter', { anchorX: 0.5, anchorY: 0.5, width: 150, height: 150, x: 360, y: 1040, alpha: 0.8, visible: true }); self.radarScreenLevel = new Text2('SCANNING', { size: 40, fill: "#FFFFFF", font: "Arial", weight: 1000, visible: false }); self.radarScreenLevel.x = 250; self.radarScreenLevel.y = 880; self.radarScreenLevel.alpha = 0.8; self.radarScreen.addChild(self.radarScreenLevel); self.addChild(self.radarScreen); self.topBoard = new Container(); self.topBoardScreen = self.topBoard.attachAsset('topScreen', { anchorX: 0.5, anchorY: 0.5, width: 640, height: 200, x: 0, y: -1265, alpha: 0.6 }); self.addChild(self.topBoard); self.coords = { x: 0, y: 0, z: 0 }; self.speed = 0.01; self.energy = 100; self.health = 10; //100; TEMP DEBUG self.isShooting = false; self.currentSchemaIndex = Math.ceil(Math.random() * 4); self.warningSound = LK.getSound('warning'); self.update = function () { if (gameState == GAME_STATE.SCORE) { return; } // Blink energyBarFill when health is under 33 if (self.health < 33) { var currentTime = Date.now(); self.energyBarFill.alpha = 0.5 + 0.5 * Math.sin(currentTime / ((self.health || 1) * 10)); } else { self.energyBarFill.alpha = 1; } // Update space coordinates spaceship.coords.x += Math.sin(current3DSpaceAngles.horizontalAngle) * spaceship.speed; spaceship.coords.y += Math.cos(current3DSpaceAngles.verticalAngle) * spaceship.speed; spaceship.coords.z += Math.sin(current3DSpaceAngles.verticalAngle) * Math.cos(current3DSpaceAngles.horizontalAngle) * spaceship.speed; self.updateCentralHud(); self.updatePointerHud(); self.updateFoeHud(); self.animateDeco(); self.updateSchema(); self.radarBarHud.rotation += 0.01; }; self.updateSchema = function () { var currentTime = Date.now(); if (self.damageScreenSchema1.damage) { self.damageScreenSchema1.alpha = 0.5 + Math.sin(currentTime * self.damageScreenSchema1.damage / 200) * 0.5; } if (self.damageScreenSchema2.damage) { self.damageScreenSchema2.alpha = 0.5 + Math.sin(currentTime * self.damageScreenSchema2.damage / 200) * 0.5; } if (self.damageScreenSchema3.damage) { self.damageScreenSchema3.alpha = 0.5 + Math.sin(currentTime * self.damageScreenSchema3.damage / 200) * 0.5; } if (self.damageScreenSchema4.damage) { self.damageScreenSchema4.alpha = 0.5 + Math.sin(currentTime * self.damageScreenSchema4.damage / 200) * 0.5; } }; self.updateCentralHud = function () { var targetLocked = false; if (currentFighter && !currentFighter.isDestroyed) { targetLocked = self.centralHud.intersects(currentFighter); self.radarScreenFoeHud.rotation = currentFighter.rotation; if (self.radarScreenHud.visible) { self.radarScreenFoeHud.visible = true; self.radarScreenHud.visible = false; self.radarBarHud.visible = false; self.radarScreenLevel.tint = 0xFF0000; self.radarScreenLevel.setText("INCOMING"); } } else { if (!self.radarScreenHud.visible) { self.radarScreenHud.visible = true; self.radarBarHud.visible = true; self.radarScreenFoeHud.visible = false; self.radarScreenLevel.tint = 0x00FF00; self.radarScreenLevel.setText("SCANNING"); } } self.centralHud.tint = targetLocked ? 0x00FF00 : 0xFFFFFF; }; self.updatePointerHud = function () { //directionX += current3DSpaceAngles.deltaH * 4; //.horizontalAngle; //directionY += current3DSpaceAngles.deltaV * 4; //.verticalAngle; var deltaX = current3DSpaceAngles.deltaH; var deltaY = current3DSpaceAngles.deltaV; var currentTime = Date.now(); if (currentFighter && !currentFighter.isDestroyed) { self.pointerHud.tint = 0xFF0000; deltaX = currentFighter.x - self.x; deltaY = currentFighter.y - self.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (Math.abs(deltaX) > 256) { self.pointerHud.y += Math.sin(currentTime / 100) * 1; // Sinusoidal movement self.pointerHud.alpha = 0.3 + Math.sin(currentTime / 100) * 0.7; // Sinusoidal movement if (currentTime - lastSearchBeepTime > targetFoundBeepDelay) { lastSearchBeepTime = Date.now(); LK.getSound('targetFoundBeep').play(); } } else { self.pointerHud.alpha = 0.5; self.pointerHud.y = -100; } var targetRotation = Math.atan2(deltaY, deltaX) + Math.PI * 0.5; var currentRotation = self.pointerHud.rotation; var rotationDifference = targetRotation - currentRotation; // Normalize the rotation difference to the range [-PI, PI] if (rotationDifference > Math.PI) { rotationDifference -= 2 * Math.PI; } else if (rotationDifference < -Math.PI) { rotationDifference += 2 * Math.PI; } // Apply a fraction of the rotation difference to make the rotation progressive self.pointerHud.rotation += rotationDifference * 0.1; } else { self.radarScreenFoeHudUpdated = false; self.pointerHud.tint = 0xFFFFFF; self.pointerHud.alpha = 0.5; var targetRotation = deltaY || deltaX ? Math.atan2(deltaY, deltaX) + Math.PI * 1.5 : 0; if ((deltaY || deltaX) && currentTime - Math.max(lastSearchBeepTime, lastFighterKillTime) > searchBeepDelay) { lastSearchBeepTime = Date.now(); LK.getSound('detectionBeep1').play(); } var currentRotation = self.pointerHud.rotation; var rotationDifference = targetRotation - currentRotation; // Normalize the rotation difference to the range [-PI, PI] if (rotationDifference > Math.PI) { rotationDifference -= 2 * Math.PI; } else if (rotationDifference < -Math.PI) { rotationDifference += 2 * Math.PI; } // Apply a fraction of the rotation difference to make the rotation progressive self.pointerHud.rotation += rotationDifference * 0.1; } if (isDebug) { debugText.setText("Deltas: " + deltaX.toFixed(0) + "," + deltaY.toFixed(0) + " => " + (self.pointerHud.rotation * 180 / Math.PI).toFixed(0)); } }; self.updateFoeHud = function () { if (currentFighter && !currentFighter.isDestroyed && !self.radarScreenFoeHudUpdated) { log("updateFoeHud => ", currentFighter.assetName); self.radarScreenFoeHud.destroy(); self.radarScreenFoeHud = self.radarScreen.attachAsset(currentFighter.assetName, { anchorX: 0.5, anchorY: 0.5, width: 150, height: 150, x: 360, y: 1050, alpha: 0.8, visible: true }); self.radarScreenFoeHudUpdated = true; } }; self.shoot = function () { if (self.isShooting || gameState != GAME_STATE.PLAYING) { return; } self.isShooting = true; totalShotsFired++; // Increment total shots fired // Recoil effect for left canon var originalLeftX = canonLeft.x; var originalLeftY = canonLeft.y; var recoilDistance = 50; var recoilSpeed = 15; var energySpeed = 10; var recoilLeftInterval = LK.setInterval(function () { canonLeft.x -= recoilSpeed; canonLeft.y += recoilSpeed; self.energy -= energySpeed; canonLeft.tint = 0xccFFcc; if (canonLeft.x <= originalLeftX - recoilDistance) { LK.clearInterval(recoilLeftInterval); var returnLeftInterval = LK.setInterval(function () { canonLeft.x += recoilSpeed; canonLeft.y -= recoilSpeed; self.energy += energySpeed; if (canonLeft.x >= originalLeftX) { canonLeft.x = originalLeftX; canonLeft.y = originalLeftY; self.energy = 100; canonLeft.tint = 0xFFFFFF; LK.clearInterval(returnLeftInterval); self.isShooting = false; } }, 16); } }, 16); // Recoil effect for right canon var originalRightX = canonRight.x; var originalRightY = canonRight.y; var recoilRightInterval = LK.setInterval(function () { canonRight.x += recoilSpeed; canonRight.y += recoilSpeed; canonRight.tint = 0xccFFcc; if (canonRight.x >= originalRightX + recoilDistance) { LK.clearInterval(recoilRightInterval); var returnRightInterval = LK.setInterval(function () { canonRight.x -= recoilSpeed; canonRight.y -= recoilSpeed; if (canonRight.x <= originalRightX) { canonRight.x = originalRightX; canonRight.y = originalRightY; canonRight.tint = 0xFFFFFF; LK.clearInterval(returnRightInterval); } }, 16); } }, 16); // Play laserShot sound LK.getSound('laserShot').play(); LK.setTimeout(function () { LK.getSound('laserShot').play(); }, 128); // Spawn bullets from side canons var leftBullet = new Bullet(true); leftBullet.x = 0; // Adjust for left canon position leftBullet.y = self.y + 512; // Start the bullet just above the spaceship leftBullet.rotation = Math.PI * 0.30; if (middleLayer.children.length > 1) { middleLayer.addChildAt(leftBullet, 1); } else { middleLayer.addChild(leftBullet); } var rightBullet = new Bullet(false); rightBullet.x = game.width; // Adjust for right canon position rightBullet.y = self.y + 512; // Start the bullet just above the spaceship rightBullet.rotation = -Math.PI * 0.30; if (middleLayer.children.length > 1) { middleLayer.addChildAt(rightBullet, 1); } else { middleLayer.addChild(rightBullet); } if (!currentFighter) { updateScore(pointsFireWithoutEnmey); } }; self.struck = function () { log("struck!"); LK.getSound('damage').play(); LK.setTimeout(function () { LK.getSound('damage').play(); }, 128); LK.effects.flashScreen(0xff0000, 1000); // Flash screen red for 1 second // Shake animation var originalX = self.x; var originalY = self.y; var shakeIntensity = 30; var shakeDuration = 600; // in milliseconds var shakeInterval = 16; // roughly 60 FPS var elapsedTime = 0; var shakeAnimation = LK.setInterval(function () { if (elapsedTime >= shakeDuration) { LK.clearInterval(shakeAnimation); self.x = originalX; self.y = originalY; return; } self.x = originalX + (Math.random() - 0.5) * shakeIntensity; self.y = originalY + (Math.random() - 0.5) * shakeIntensity; elapsedTime += shakeInterval; }, shakeInterval); self.decreaseHealth(); }; self.decreaseHealth = function () { // Handle health self.health -= minDamage + Math.random() * (currentLevel - 1); self.energyBarMask.scale.x = -(100 - self.health) / 100; log("New health =", self.health); if (self.health <= 0) { self.warningSound.stop(); LK.getSound('explosion').play(); LK.setTimeout(function () { LK.getSound('explosion').play(); }, 200); cleanPlayingState(); return; } else { self.energyBarFill.alpha = 1; } if (self.health < 33) { LK.setTimeout(function () { self.warningSound.play(); if (self.health > 0 && self.health < 15) { LK.setTimeout(function () { if (gameState == GAME_STATE.SCORE) { return; } self.warningSound.play(); }, 2000); } }, 256); } if (self.health < 66) { self.updateSchemaLevels(); } }; self.updateSchemaLevels = function () { var currentSchema; log("updateSchemaLevels i=", self.currentSchemaIndex); switch (self.currentSchemaIndex) { case 2: currentSchema = self.damageScreenSchema2; break; case 3: currentSchema = self.damageScreenSchema3; break; case 4: currentSchema = self.damageScreenSchema4; break; default: currentSchema = self.damageScreenSchema1; break; } currentSchema.damage = Math.min(2, currentSchema.damage + 1); currentSchema.tint = currentSchema.damage == 1 ? 0xff6700 : 0xFF0000; self.currentSchemaIndex = 1 + self.currentSchemaIndex % 4; var totalDamage = self.damageScreenSchema1.damage + self.damageScreenSchema2.damage + self.damageScreenSchema3.damage + self.damageScreenSchema4.damage; if (totalDamage > 4) { self.damageScreenSchema.tint = 0xff6700; } }; self.animateDeco = function () { var currentTime = Date.now(); spaceshipDeco1.alpha = Math.max(0, Math.min(1, 0.75 + 0.25 * Math.sin(currentTime / 500))); }; self.init = function () { self.energyBarMask.scale.x = -(100 - self.health) / 100; self.radarScreenHud.visible = true; self.radarScreenFoeHud.visible = false; self.radarScreenLevel.tint = 0x00FF00; self.radarScreenLevel.setText("SCANNING"); }; self.init(); }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 // Initialize game with a black background }); /**** * Game Code ****/ function animateCockpitButton() { if (cockpitDisplay.cockpitButton.isAnimating) { return; } cockpitDisplay.cockpitButton.isAnimating = true; var moveDown = true; var originalY = cockpitDisplay.cockpitButton.y; var originalBaseY = cockpitDisplay.cockpitButtonBase.y; var animationInterval = LK.setInterval(function () { if (moveDown) { cockpitDisplay.cockpitButton.y += 15; cockpitDisplay.cockpitButtonBase.y += 2; if (cockpitDisplay.cockpitButton.y >= originalY + 20) { moveDown = false; } } else { cockpitDisplay.cockpitButton.y -= 15; cockpitDisplay.cockpitButtonBase.y -= 2; if (cockpitDisplay.cockpitButton.y <= originalY) { cockpitDisplay.cockpitButton.y = originalY; cockpitDisplay.cockpitButtonBase.y = originalBaseY; cockpitDisplay.cockpitButton.isAnimating = false; LK.clearInterval(animationInterval); } } }, 16); // 60 FPS } /* # Space Strike ### Game Design and Planning 1. **Game Objectives**: Defeating all enemy fighters. 2. **Game Levels**: Random map with an increasing number of ennemy figthers per level. 3. **Game Assets**: 4. **Game Mechanics**: 1. **Player Interaction**: 1st person view. Tap to shoot. 4. **Game Loop and Events**: Objects updates occur in the game loop. Input events are handled in input functions. 5. **Scoring and Progression**: Player's score is updated when killing ennemies. When no more ennemies game switches to next level. 6. **Game Over and Reset**: The game is over when player's lives reaches zero. */ /****************************************************************************************** */ /************************************** GLOBAL VARIABLES ********************************** */ /****************************************************************************************** */ // Enumeration for game states var GAME_STATE = { INIT: 'INIT', MENU: 'MENU', HELP: 'HELP', STARTING: 'STARTING', NEW_ROUND: 'NEW_ROUND', PLAYING: 'PLAYING', SCORE: 'SCORE' }; var gameState = GAME_STATE.INIT; var currentLevel = 0; var currentlevelSpeed = 1; var currentNbEnnemies = 0; var currentLevelStartTime = 0; var nbFigherModels = 5; var nbFriendModels = 2; var minDamage = 5; var nbStars = 50; var starsSize = 10; var starsSizeBase = 10; var starsSizeFast = 40; var starsSpeed = 10; var starsSpeedBase = 10; var starsSpeedFast = 40; var stars = []; var starsFixed = []; var current3DSpaceAngles = { horizontalAngle: 0, verticalAngle: 0, deltaH: 0, deltaV: 0 }; var backgroundLayer = new Container(); var middleLayer = new Container(); var foregroundLayer = new Container(); var background; var currentSpaceObject; var spaceObjectOffset = 16; var lastSpaceObjectChangeTime = 0; var nbSpaceObjects = 7; var initialFighterZ = 2048; var spaceship; var cockpitDisplay; var reachedTargetAngle = false; var enemies = {}; // Ennemy fighters var lastFighterKillTime = 0; var lastFighterPassTime = 0; var nextFigtherSpawnDelay = 128; var fighterPool = []; var rotationSpeed = 0.25; var isChangingDirection = false; var currentFighter = null; var currentEnemyBullet = null; var currentEnemyBulletPair = null; var currentFriend = null; var lastAngleChangeTime = Date.now(); var lastSearchBeepTime = lastAngleChangeTime; var searchBeepDelay = 1024; var targetFoundBeepDelay = 360; var nextAngleChangeDelay = 128; var currentIndex = 0; var currentScore = 0; var currentTeamKill = 0; var previousScore = 0; var pointsKillFoe = 1000; var pointsKillEnemyBullet = 500; var pointsKillFriend = -5000; var pointsHit = -100; var pointsFireWithoutEnmey = -10; var finalFlash1 = false; var finalFlash2 = false; var missionEndTime; // Stores the timestamp when the mission ends var missionStartTime; // Stores the timestamp when the mission starts var successfulHits = 0; // Tracks the number of successful hits on enemies var totalShotsFired = 0; // Tracks the total number of shots fired by the player var enemiesStopped = 0; // Tracks the number of enemies stopped var lasersStopped = 0; // Tracks the number of lasers stopped // UI var title; var titleOne; var scoreText; var startText; var startButton; var bgMusic; var missionText; var announceText; var announceAsset; var animateMissionTextEnded = false; var startingStateTime = 0; var startingStateDelay = 1500; var startButtonClicked = 0; var finalEarth; var whiteRose; var finalStatsText; var scoreStateTime = 0; var scoreStateDelay = 3000; // Debug var isDebug = false; var fpsText; var lastTick; var frameCount; var debugText; /****************************************************************************************** */ /*********************************** UTILITY FUNCTIONS ************************************ */ /****************************************************************************************** */ function log() { if (isDebug) { var _console; (_console = console).log.apply(_console, arguments); } } function getNextIndex() { currentIndex++; return currentIndex; } function animateMissionText(missionText, missionLabel) { var currentIndex = 0; var interval = LK.setInterval(function () { if (currentIndex < missionLabel.length) { missionText.setText(missionText.getContent().text + missionLabel[currentIndex]); currentIndex++; } else { LK.clearInterval(interval); LK.setTimeout(function () { animateMissionTextEnded = true; }, 900); } }, 120); // Adjust the speed of the animation by changing the interval time } function getSectorName(nb) { var letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; var randomLetters = letters.charAt(Math.floor(Math.random() * letters.length)) + letters.charAt(Math.floor(Math.random() * letters.length)); return randomLetters + nb; } /****************************************************************************************** */ /************************************** INPUT HANDLERS ************************************ */ /****************************************************************************************** */ game.on('down', function (x, y, obj) { switch (gameState) { case GAME_STATE.MENU: gameMenuDown(x, y, obj); break; case GAME_STATE.STARTING: gameStartingDown(x, y, obj); break; case GAME_STATE.PLAYING: gamePlayingDown(x, y, obj); break; case GAME_STATE.SCORE: gameScoreDown(x, y, obj); break; } }); function gameMenuDown(x, y, obj) { log("gameMenuDown..."); if (startButtonClicked >= 2) { return; } startButtonClicked++; log("Ok start click..." + startButtonClicked); if (startButtonClicked == 1) { startButton.visible = true; startText.visible = true; var fadeInInterval = LK.setInterval(function () { startButton.alpha += 0.05; startText.alpha += 0.05; if (startButton.alpha >= 1) { LK.clearInterval(fadeInInterval); startButton.alpha = 1; startText.alpha = 1; } }, 16); // 60 FPS if (bgMusic) { // Start music after 1st user interaction (mobile) bgMusic.play(); } } else { cleanMenuState(); } } function gameStartingDown(x, y, obj) { log("gameStartingDown ..."); //cleanStartingState(); //initPlayingState(); } function gamePlayingDown(x, y, obj) { log("gamePlayingDown ..."); spaceship.shoot(); animateCockpitButton(); } function gameScoreDown(x, y, obj) { log("gameScoreDown ..."); if (Date.now() - scoreStateTime > scoreStateDelay) { cleanScoreState(); } } /****************************************************************************************** */ /************************************* GAME FUNCTIONS **************************************** */ /****************************************************************************************** */ function initStars() { for (var i = 0; i < nbStars; i++) { var star = LK.getAsset('star', { anchorX: 0.5, anchorY: 0.5, alpha: 0.9, x: Math.random() * 2048, y: Math.random() * 2732 }); stars.push(star); middleLayer.addChild(star); } // Farther stars for (var i = 0; i < nbStars * 2; i++) { var star = LK.getAsset('star', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 + Math.random() * 0.5, x: Math.random() * 2048, y: Math.random() * 2732, slow: true }); stars.push(star); backgroundLayer.addChildAt(star, 0); } // Fixed stars for (var i = 0; i < nbStars * 4; i++) { var star = LK.getAsset('star', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 + Math.random() * 0.5, x: Math.random() * 2048, y: Math.random() * 2732, fixed: true }); starsFixed.push(star); backgroundLayer.addChildAt(star, 0); //game.addChildAt(star, game.children.length); } } function starFieldAnimation() { stars.forEach(function (star) { // Calculate direction vector from center to star var directionX = star.x - 1024; // 1024 is half the width of the screen var directionY = star.y - 1366; // 1366 is half the height of the screen // Normalize direction var length = Math.sqrt(directionX * directionX + directionY * directionY); if (length === 0) { // Prevent division by zero length = 1; } directionX /= length; directionY /= length; // Add offset based on current3DSpaceAngles directionX += current3DSpaceAngles.deltaH * 4 * (star.slow ? 0.25 : 1); //.horizontalAngle; directionY += current3DSpaceAngles.deltaV * 4 * (star.slow ? 0.25 : 1); //.verticalAngle; // Move star away from center // Increase star size as it moves away to simulate faster movement var sizeIncrease = Math.min(Math.abs(directionX * 2), Math.abs(directionY * 2)); star.width = Math.min(20, star.width + sizeIncrease * 0.05 * (star.slow ? 0 : 1)); // Limit width to 20 star.height = star.width * (!star.slow && starsSpeed == starsSpeedFast ? 8 : 1); var angle = Math.atan2(directionY, directionX) + Math.PI * 0.5; star.rotation = starsSpeed == starsSpeedFast ? angle : 0; star.x += directionX * starsSpeed * (star.slow ? starsSpeed == starsSpeedFast ? 0.5 : 0.075 : 1); // Increase speed to 10 for faster star movement star.y += directionY * starsSpeed * (star.slow ? starsSpeed == starsSpeedFast ? 0.5 : 0.075 : 1); if (star.alpha < 1) { star.alpha = Math.min(1, star.alpha + 0.01); } // Reset star position if it moves off screen if (star.x < 0 || star.x > 2048 || star.y < 0 || star.y > 2732) { star.x = Math.random() * 2048; star.y = Math.random() * 2732; // Reset height to initial value star.width = Math.random() * starsSize; star.height = star.width; star.alpha = Math.random() * (star.slow ? Math.random() * 0.5 : 1); } }); // Move nebulas images here .x-=0.1 } function mapZtoScale(z) { if (z <= 0) { return 2; // Handle non-positive inputs (optional) } // Adjusted base function (linear decay) var base = -z / 512 + 4; // Adjust coefficients for better fit // Smoothing term (optional, remove for strictly increasing function) var smoothTerm = Math.exp(-Math.pow(z - 2048, 2) / 10000); // Combine base and smoothing term with an adjustable weight var weight = 0.8; // Adjust weight between 0 (no smoothing) and 1 (full smoothing) return weight * base + (1 - weight) * smoothTerm; } function loopBgMusic() { if (bgMusic && Date.now() - bgMusic.lastPlayTime > 10000) { bgMusic.lastPlayTime = Date.now(); bgMusic.play(); } } function handleGlobalMovement() { if (currentFighter) { if (Date.now() - lastAngleChangeTime < 128) { return; } // Calculate the difference in position between the spaceship and the currentFighter var deltaX = currentFighter.x - 1024; var deltaY = currentFighter.y - 1366; // Normalize the deltas to get the direction var length = Math.sqrt(deltaX * deltaX + deltaY * deltaY); //log("Aiming at fighter ", currentFighter.x.toFixed(0), currentFighter.y.toFixed(0), "=> deltas=", deltaX.toFixed(0), deltaY.toFixed(0), " / l=" + length.toFixed(0)); if (length !== 0 && Math.abs(length) > 8 && currentFighter.z > 512) { current3DSpaceAngles.deltaH = -deltaX / length; current3DSpaceAngles.deltaV = -deltaY / length; //lastAngleChangeTime = Date.now(); } else { reachedTargetAngle = true; current3DSpaceAngles.deltaH = 0; current3DSpaceAngles.deltaV = 0; } } else { // Space ride if (Date.now() - lastAngleChangeTime < nextAngleChangeDelay) { return; } reachedTargetAngle = false; var newDeltaH = 0; var newDeltaV = 0; isChangingDirection = false; if (current3DSpaceAngles.deltaH == 0 & current3DSpaceAngles.deltaH == 0) { var rH = Math.random(); var rV = Math.random(); var dirH = rH < 0.33 ? 1 : rH > 0.66 ? -1 : 0; var dirV = rV < 0.33 ? 1 : rV > 0.66 ? -1 : 0; newDeltaH = rH * 0.25 * dirH; newDeltaV = rV * 0.4 * dirV; isChangingDirection = true; } current3DSpaceAngles.deltaH = newDeltaH; current3DSpaceAngles.deltaV = newDeltaV; lastAngleChangeTime = Date.now(); nextAngleChangeDelay = 512 + 512 * Math.random(); } } function getFighter() { var rand = Math.random(); var model = 1; switch (currentLevel) { case 1: case 2: model = 1; break; case 3: model = 2; break; case 4: // Intro friend 1 case 5: model = rand > 0.5 ? 1 : 2; break; case 6: model = 3; break; case 7: case 8: // Intro friend 2 model = Math.ceil(rand * 3); break; case 9: model = 4; break; case 10: model = Math.ceil(rand * 4); break; case 11: model = 5; break; default: model = Math.ceil(rand * nbFigherModels); } return new Fighter(getNextIndex(), model); // No pooling for now } function getFriend() { var rand = Math.random(); var model = 1; switch (currentLevel) { case 1: case 2: case 3: case 4: case 5: case 6: case 7: model = 1; break; case 8: model = 2; break; default: model = Math.ceil(rand * nbFriendModels); break; } return new Fighter(getNextIndex(), model, true); } function handleFightersLifeCycle() { if (isChangingDirection) { return; } if (currentFighter || currentFriend) { return; } if (Date.now() - Math.max(lastFighterPassTime, lastFighterKillTime) < nextFigtherSpawnDelay) { return; } log("Setting currentFighter..."); log("enemies:", enemies); var keys = Object.keys(enemies); // Select random inactive enemy for (var i = 0; i < keys.length; i++) { if (!enemies[keys[i]].isDestroyed && !enemies[keys[i]].isActive) { log("selecting " + i + "/" + keys.length); currentFighter = enemies[keys[i]]; break; } } if (currentFighter) { currentFighter.reset(); currentFighter.activate(); middleLayer.addChild(currentFighter); if ((currentLevel == 4 || currentLevel == 8 || currentLevel > 4 && Math.random() > 0.33) && currentFighter.flyMode > 1) { currentFriend = getFriend(); currentFriend.reset(); currentFriend.activate(); middleLayer.addChild(currentFriend); } } log("=> currentFighter #" + (currentFighter ? currentFighter.index : "null"), currentFighter); log("=> currentFriend #" + (currentFriend ? currentFriend.index : "null"), currentFriend); } function handleSpaceObjectsLifeCycle() { if (!currentSpaceObject) { currentSpaceObject = backgroundLayer.addChild(new SpaceObject(Math.floor(Math.random() * nbSpaceObjects))); currentSpaceObject.x = 2048 * Math.random(); currentSpaceObject.y = 2732 * Math.random(); currentSpaceObject.dirX = -Math.sign(currentSpaceObject.x); currentSpaceObject.dirY = -Math.sign(currentSpaceObject.y); currentSpaceObject.alpha = 0.9; } else { // Check if currentSpaceObject is completely out of the screen var xMin = -currentSpaceObject.width / 2 - spaceObjectOffset; var xMax = 2048 + currentSpaceObject.width / 2 + spaceObjectOffset; var yMin = -currentSpaceObject.height / 2 - spaceObjectOffset; var yMax = 2732 + currentSpaceObject.height / 2 + spaceObjectOffset; if (Date.now() - lastSpaceObjectChangeTime > 1000 && (currentSpaceObject.x < xMin || currentSpaceObject.x > xMax || currentSpaceObject.y < yMin || currentSpaceObject.y > yMax)) { lastSpaceObjectChangeTime = Date.now(); var oldX = currentSpaceObject.x; var oldY = currentSpaceObject.y; // Destroy the current space object currentSpaceObject.destroy(); // Create a new space object with another index currentSpaceObject = backgroundLayer.addChild(new SpaceObject(Math.floor(Math.random() * nbSpaceObjects))); currentSpaceObject.alpha = 0.9; xMin = -currentSpaceObject.width / 2 - spaceObjectOffset; xMax = 2048 + currentSpaceObject.width / 2 + spaceObjectOffset; yMin = -currentSpaceObject.height / 2 - spaceObjectOffset; yMax = 2732 + currentSpaceObject.height / 2 + spaceObjectOffset; var nextX = Math.max(xMin, Math.min(2048 - oldX, xMax)); var nextY = Math.max(yMin, Math.min(2732 - oldY, yMax)); log("old:", oldX.toFixed(0), oldY.toFixed(0), "new=", nextX.toFixed(0), nextY.toFixed(0)); currentSpaceObject.alpha = 0.9; currentSpaceObject.x = nextX; currentSpaceObject.y = nextY; currentSpaceObject.initDirection(); } } } function updateScore(points) { log("updateScore:", points); // Add points to currentScore with level multiplier currentScore += points + points * Math.max(0, currentLevel - 2) * 0.25; // Clamp score between 0 and 999999 currentScore = Math.max(0, Math.min(999999, currentScore)); // Save currentScore in LK LK.setScore(currentScore); // Animate the scoreText from previousScore to currentScore var animationDuration = 1000; // Duration of the animation in milliseconds var startTime = Date.now(); var startScore = previousScore; var endScore = currentScore; function animateScore() { var currentTime = Date.now(); var elapsedTime = currentTime - startTime; var progress = Math.min(1, elapsedTime / animationDuration); var animatedScore = Math.floor(startScore + (endScore - startScore) * progress); //log("Animating score: progress =", progress, "animatedScore =", animatedScore); // Update scoreText scoreText.setText(animatedScore.toString().padStart(6, '0')); if (progress < 1) { LK.setTimeout(animateScore, 16); // Approximately 60 FPS } else { previousScore = currentScore; } } animateScore(); } /****************************************************************************************** */ /************************************* GAME STATES **************************************** */ /****************************************************************************************** */ function gameInitialize() { log("Game initialize..."); //backgroundLayer = new Container(); backgroundLayer.alpha = 1; //middleLayer = new Container(); //foregroundLayer = new Container(); game.addChild(backgroundLayer); game.addChild(middleLayer); game.addChild(foregroundLayer); background = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 1 }); //backgroundLayer.width = 2048; //backgroundLayer.heigth = 2732; //backgroundLayer.addChild(background); //game.addChildAt(background, 0); bgMusic = LK.getSound('bgMusic'); bgMusic.lastPlayTime = 0; handleSpaceObjectsLifeCycle(); initStars(); spaceship = foregroundLayer.addChild(new Spaceship()); //spaceship = game.addChild(new Spaceship()); //enemies[0].z = 1024; // TEMP DEEBUG spaceship.x = 2048 / 2; // Center spaceship horizontally spaceship.y = 2732 - spaceship.height / 2; // Position spaceship near the bottom of the screen spaceship.visible = false; cockpitDisplay = foregroundLayer.addChild(new CockpitDisplay()); //cockpitDisplay = game.addChild(new CockpitDisplay()); cockpitDisplay.x = 2048 / 2; // Center cockpit display horizontally cockpitDisplay.visible = false; scoreText = new Text2('000000', { size: 75, fill: "#ffffff", font: "Arial", weight: 1000 }); scoreText.tint = 0x00FF00; scoreText.alpha = 0.6; scoreText.visible = false; scoreText.anchor.set(0.5, 0); // Anchor to the bottom-right LK.gui.top.addChild(scoreText); if (isDebug) { var debugMarker = LK.getAsset('debugMarker', { anchorX: 0.5, anchorY: 0.5, x: 2048 * 0.5, y: 2732 }); game.addChild(debugMarker); fpsText = new Text2('FPS: 0', { size: 50, fill: "#ffffff" }); // Position FPS text at the bottom-right corner fpsText.anchor.set(1, 1); // Anchor to the bottom-right LK.gui.bottomRight.addChild(fpsText); // Update FPS display every second lastTick = Date.now(); frameCount = 0; // Debug text to display cube information debugText = new Text2('Debug Info', { size: 50, fill: "#ffffff" }); debugText.anchor.set(0.5, 1); // Anchor to the bottom-right LK.gui.bottom.addChild(debugText); } initMenuState(); } // GAME MENU function initMenuState() { log("initMenuState..."); gameState = GAME_STATE.MENU; log("bgMusic volume", bgMusic.volume); title = LK.getAsset('title', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 - 780 }); titleOne = LK.getAsset('titleOne', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 - 240 }); startText = LK.getAsset('startText', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 + 640, alpha: 0, visible: false }); game.addChild(title); startButton = LK.getAsset('startButton', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 + 640, alpha: 0, visible: false }); game.addChild(title); game.addChild(titleOne); game.addChild(startText); game.addChild(startButton); //cleanMenuState(); // TEMP DEBUG !!!! } function handleMenuLoop() { // Menu animations here starFieldAnimation(); // Call the animation function within the game tick loopBgMusic(); } function cleanMenuState() { log("cleanMenuState..."); // Animate start button press var originalScaleX = startButton.scale.x; var originalScaleY = startButton.scale.y; LK.getSound('startSound').play(); var animationInterval = LK.setInterval(function () { startButton.scale.x *= 0.99; startButton.scale.y *= 0.99; startText.scale.x *= 0.99; startText.scale.y *= 0.99; if (startButton.scale.x <= originalScaleX * 0.9) { LK.clearInterval(animationInterval); // Big scale up and slow fade animation var fadeInterval = LK.setInterval(function () { startButton.scale.x *= 1.05; startButton.scale.y *= 1.05; startText.scale.x *= 1.05; startText.scale.y *= 1.05; startButton.alpha -= 0.01; startText.alpha -= 0.01; title.alpha -= 0.02; titleOne.alpha -= 0.02; if (startButton.alpha <= 0) { LK.clearInterval(fadeInterval); // Remove start button and text startButton.destroy(); startText.destroy(); title.destroy(); titleOne.destroy(); initStartingState(); } }, 16); // 60 FPS } }, 16); // 60 FPS missionStartTime = Date.now(); // Initialize mission start time } // STARTING function initStartingState() { log("initStartingState..."); spaceship.visible = true; scoreText.visible = true; cockpitDisplay.visible = true; animateMissionTextEnded = false; gameState = GAME_STATE.STARTING; // Round preparation logic here. currentLevel++; log("Level:" + currentLevel); currentNbEnnemies = currentLevel; currentLevelStartTime = Date.now(); currentlevelSpeed = Math.min(3, 1 + currentLevel / 10); for (var i = 0; i < currentNbEnnemies; i++) { // Initialize first enemy fighter var newFighter = getFighter(); enemies[newFighter.index] = newFighter; } currentFighter = null; currentFriend = null; // Show Mission objectives var missionLabel = 'SECTOR #' + getSectorName(currentLevel); missionText = new Text2('', { size: 175, fill: "#00FF00", font: "Arial", weight: 900 }); missionText.alpha = 0.6; missionText.x = 340; missionText.y = 256; game.addChild(missionText); animateMissionText(missionText, missionLabel); // TEMPORARY. //cleanStartingState(); //initPlayingState(); log("initStartingState...announceText=", announceText); if (currentLevel > 1) { current3DSpaceAngles.deltaH = 0; current3DSpaceAngles.deltaV = 0; starsSize = starsSizeFast; starsSpeed = starsSpeedFast; LK.getSound('acceleration').play(); } } function handleStartingLoop() { // Round Starting animations here loopBgMusic(); starFieldAnimation(); if (animateMissionTextEnded && !announceText) { startingStateTime = Date.now(); var announceAssetName; var announceTextLabel; switch (currentLevel) { case 1: announceAssetName = "fighter1"; announceTextLabel = "FOE:"; break; case 3: announceAssetName = "fighter2"; announceTextLabel = "FOE:"; break; case 4: announceAssetName = "friend1"; announceTextLabel = "ALLY:"; break; case 6: announceAssetName = "fighter3"; announceTextLabel = "FOE:"; break; case 8: announceAssetName = "friend2"; announceTextLabel = "ALLY:"; break; case 9: announceAssetName = "fighter4"; announceTextLabel = "FOE:"; break; case 11: announceAssetName = "fighter5"; announceTextLabel = "FOE:"; break; default: announceAssetName = ""; announceTextLabel = ""; break; } if (announceTextLabel) { log("announceTextLabel: " + announceTextLabel, announceAssetName); announceText = new Text2(announceTextLabel, { size: 180, fill: "#FFFFFF", font: "Arial", weight: 1000 }); announceText.alpha = 0.6; announceText.x = 540; announceText.y = 600; announceText.tint = announceTextLabel == "FOE:" ? 0xFF0000 : 0x1FC7FF; game.addChild(announceText); announceAsset = LK.getAsset(announceAssetName, { anchorX: 0.5, anchorY: 0.5, width: 200, height: 200 }); announceAsset.x = 1250; announceAsset.y = 720; game.addChild(announceAsset); log("Defined new announce:...", announceAsset, announceText); } else { log("No new announce..."); cleanStartingState(); initPlayingState(); } } if (announceAsset) { var currentTime = Date.now(); announceAsset.alpha = 0.75 + 0.25 * Math.sin(currentTime / 100); if (currentTime - startingStateTime > startingStateDelay) { log("Start anim end..."); cleanStartingState(); initPlayingState(); } } } function cleanStartingState() { log("cleanStartingState...", announceAsset, announceText); if (announceAsset) { game.removeChild(announceAsset); announceAsset.destroy(); announceAsset = null; } if (announceText) { game.removeChild(announceText); announceText.destroy(); announceText = null; } if (missionText) { game.removeChild(missionText); missionText.destroy(); missionText = null; } } // PLAYING function initPlayingState() { log("initPlayingState..."); starsSpeed = starsSpeedBase; starsSize = starsSizeBase; stars.forEach(function (star) { star.width = starsSizeBase; star.height = starsSizeBase; }); lastFighterKillTime = Date.now(); // Prevent direct enemy spawn gameState = GAME_STATE.PLAYING; } function handlePlayingLoop() { loopBgMusic(); handleGlobalMovement(); starFieldAnimation(); handleFightersLifeCycle(); handleSpaceObjectsLifeCycle(); // Update angles at a lower rate using modulo on ticks if (LK.ticks % 15 == 0) { current3DSpaceAngles.horizontalAngle += current3DSpaceAngles.deltaH; current3DSpaceAngles.verticalAngle += current3DSpaceAngles.deltaV; } // Rotate cockpitDisplay based on current3DSpaceAngles.horizontalAngle if (cockpitDisplay.cockpitBase) { cockpitDisplay.cockpitBase.rotation += -current3DSpaceAngles.deltaH - cockpitDisplay.cockpitBase.rotation >= 0.02 ? 0.1 : 0; cockpitDisplay.cockpitBase.rotation += -current3DSpaceAngles.deltaH - cockpitDisplay.cockpitBase.rotation <= -0.02 ? -0.1 : 0; cockpitDisplay.cockpitBase.rotation = Math.min(Math.PI * 0.3, Math.max(-Math.PI * 0.3, cockpitDisplay.cockpitBase.rotation)); cockpitDisplay.cockpitBase.scale.y = 1.5 + current3DSpaceAngles.deltaV * 0.5; // Illustrate vertical rotation by scaling the wheel } if (isDebug) { //debugText.setText("lives: " + player.lives); // FPS var now = Date.now(); frameCount++; if (now - lastTick >= 1000) { // Update every second fpsText.setText('FPS: ' + frameCount); frameCount = 0; lastTick = now; } } } function cleanPlayingState() { log("cleanPlayingState..."); // TODO Remove elements enemies = {}; animateMissionTextEnded = false; if (spaceship.health > 0) { initStartingState(); } else { spaceship.visible = false; scoreText.visible = false; cockpitDisplay.visible = false; current3DSpaceAngles.deltaH = 0; current3DSpaceAngles.deltaV = 0; if (currentSpaceObject) { currentSpaceObject.visible = false; backgroundLayer.removeChild(currentSpaceObject); currentSpaceObject.destroy(); currentSpaceObject = null; } initScoreState(); } } // SCORE function initScoreState() { if (gameState == GAME_STATE.SCORE) { return; } log("initScoreState..."); gameState = GAME_STATE.SCORE; missionEndTime = Date.now(); // Set mission end time if (spaceship.warningSound) { spaceship.warningSound.stop(); } scoreStateTime = Date.now(); finalEarth = LK.getAsset('earth', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 + 2048 / 2, width: 2048, height: 2048, alpha: 1, visible: true }); middleLayer.addChild(finalEarth); whiteRose = LK.getAsset('whiteRose', { anchorX: 0.5, anchorY: 0.5, x: 2304, y: 1366 + 512 - 1024 * Math.random(), width: 512, height: 512, alpha: 1, visible: true }); foregroundLayer.addChild(whiteRose); starsSpeed = 2.5; // Calculate precision percentage var precision = totalShotsFired > 0 ? successfulHits / totalShotsFired * 100 : 0; // Calculate mission time in minutes and seconds var missionDuration = missionEndTime - missionStartTime; var minutes = Math.floor(missionDuration / 60000); var seconds = Math.floor(missionDuration % 60000 / 1000); var missionTimeFormatted = minutes + "min " + seconds + "s"; // Log all the stats log("Mission Time: " + ((missionEndTime - missionStartTime) / 1000).toFixed(2) + " seconds"); log("Sectors Cleared: " + (currentLevel - 1)); log("Enemies Beated: " + enemiesStopped); log("Lasers Stopped: " + lasersStopped); log("Team Kills: " + currentTeamKill); log("Shots Fired: " + totalShotsFired); log("Precision %: " + precision.toFixed(0) + "%"); // Create the final stats text finalStatsText = new Text2("OUR HERO\n\nMission Time: ".concat(missionTimeFormatted, "\nSectors Cleared: ").concat(currentLevel - 1, "\nEnemies Beated: ").concat(enemiesStopped, "\nLasers Stopped: ").concat(lasersStopped, "\nTeam Kills: ").concat(currentTeamKill, "\nShots Fired: ").concat(totalShotsFired, "\nPrecision: ").concat(precision.toFixed(0), "%"), { size: 90, fill: "#FFFFFF", font: "Arial", weight: 1000, align: "center" }); finalStatsText.anchor.set(0.5, 0.5); finalStatsText.x = 2048 / 2; finalStatsText.y = 512; middleLayer.addChild(finalStatsText); // TEMPORARY : Avoid multiple clicks //cleanScoreState(); } function handleScoreLoop() { if (!finalFlash1) { finalFlash1 = true; LK.effects.flashScreen(0xFFFFFF, 4000); } /* if (finalFlash1 && !finalFlash2 && Date.now() - scoreStateTime > 1500) { finalFlash2 = true; LK.effects.flashScreen(0xFFFFFF, 2000); } */ // Score display logic here starFieldAnimation(); loopBgMusic(); if (finalEarth.width > 2 && finalEarth.y > -256) { finalEarth.width *= 0.9995; finalEarth.height *= 0.9995; finalEarth.y -= 1; } else { starsSpeed = Math.min(20, starsSpeed + 0.05); } if (whiteRose.width > 1 && whiteRose.x > -1024) { whiteRose.width *= 0.9995; whiteRose.height *= 0.9995; whiteRose.x -= 0.6; whiteRose.rotation += 0.002; } } function cleanScoreState() { log("cleanScoreState..."); LK.showGameOver(); } /***********************************************************************************/ /******************************** MAIN GAME LOOP ***********************************/ /***********************************************************************************/ game.update = function () { switch (gameState) { case GAME_STATE.MENU: handleMenuLoop(); break; case GAME_STATE.STARTING: handleStartingLoop(); break; case GAME_STATE.PLAYING: handlePlayingLoop(); break; case GAME_STATE.SCORE: handleScoreLoop(); break; } }; gameInitialize(); // Initialize the game
===================================================================
--- original.js
+++ change.js
@@ -853,9 +853,9 @@
z: 0
};
self.speed = 0.01;
self.energy = 100;
- self.health = 100;
+ self.health = 10; //100; TEMP DEBUG
self.isShooting = false;
self.currentSchemaIndex = Math.ceil(Math.random() * 4);
self.warningSound = LK.getSound('warning');
self.update = function () {
starfield.
remove
elongated futuristic laser canon gun green. top view
explosion from top. zenith view
white triangle.
black background ethereal blue gas.
black background ethereal centered galaxy.
black background ethereal centered galaxy.
black background ethereal centered planet.
close up of a giant red star. black background
planet with rings. black background. full, with margin.
metalic oval border with bevel. Black. Electronic style. empty inside. no background
futuristic space fighter.. full front view
Space scene with full earth (europe and africa side). High definition
elegant white rose in a long transparent futuristic glass tube.
laserShot
Sound effect
bgMusic
Sound effect
explosion
Sound effect
laserShot2
Sound effect
detectionBeep1
Sound effect
fighterPassing
Sound effect
targetFoundBeep
Sound effect
damage
Sound effect
warning
Sound effect
startSound
Sound effect
acceleration
Sound effect
teamKill
Sound effect
finalExplosion
Sound effect