User prompt
add view leaderboard a leaderboard and name creation ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
different warriors with different stats and different ship types neither of those have a change for what anything looks like there is a warrior selection battle and ship type selection menu ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
gun type selection menu back button
User prompt
tutorial gun types not seen but effects speed and damage and finally enemies can have varied health numbers and guns ↪💡 Consider importing and using the following plugins: @upit/storage.v1, @upit/tween.v1
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'toGlobal')' in or related to this line: 'var clickPos = self.toLocal(obj.parent.toGlobal(obj.position));' Line Number: 182
User prompt
levels, home screen , and story mode ↪💡 Consider importing and using the following plugins: @upit/storage.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Space Defender
Initial prompt
shooting game
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { currentLevel: 1, unlockedLevels: 1, gameMode: "story", storyProgress: 0 }); /**** * Classes ****/ var Boss = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('boss', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 1; self.hp = 10; self.shootTimer = 0; self.shootInterval = 30; self.moveDirection = 1; self.update = function () { self.y += self.speed; self.x += self.moveDirection * 2; if (self.x <= 100 || self.x >= 1948) { self.moveDirection *= -1; } self.shootTimer++; if (self.shootTimer >= self.shootInterval) { self.shootTimer = 0; var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y + 60; enemyBullets.push(bullet); game.addChild(bullet); } }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 3; // Varied health based on current level and randomization var baseHp = Math.floor(storage.currentLevel / 2) + 1; self.hp = baseHp + (Math.random() < 0.3 ? Math.floor(Math.random() * 2) + 1 : 0); self.maxHp = self.hp; self.shootTimer = 0; self.shootInterval = 120; // Visual indicator for health - tint red for high HP enemies if (self.hp > 2) { graphics.tint = 0xFF6666; } else if (self.hp > 1) { graphics.tint = 0xFFAAAA; } self.update = function () { self.y += self.speed; self.shootTimer++; if (self.shootTimer >= self.shootInterval && Math.random() < 0.01) { self.shootTimer = 0; var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y + 30; enemyBullets.push(bullet); game.addChild(bullet); } // Update tint based on current health if (self.hp < self.maxHp) { var healthRatio = self.hp / self.maxHp; if (healthRatio < 0.3) { graphics.tint = 0xFF0000; } else if (healthRatio < 0.7) { graphics.tint = 0xFF6666; } } }; return self; }); var EnemyBullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.update = function () { self.y += self.speed; }; return self; }); var LevelSelectScreen = Container.expand(function () { var self = Container.call(this); var titleText = new Text2('SELECT LEVEL', { size: 100, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 300; self.addChild(titleText); var levelButtons = []; for (var i = 1; i <= 10; i++) { var button = new Text2(i.toString(), { size: 60, fill: i <= storage.unlockedLevels ? 0xFFFFFF : 0x666666 }); button.anchor.set(0.5, 0.5); button.x = 400 + (i - 1) % 5 * 300; button.y = 600 + Math.floor((i - 1) / 5) * 200; button.levelNumber = i; self.addChild(button); levelButtons.push(button); } var backButton = new Text2('BACK', { size: 80, fill: 0xFF0000 }); backButton.anchor.set(0.5, 0.5); backButton.x = 1024; backButton.y = 1400; self.addChild(backButton); self.down = function (x, y, obj) { var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : { x: x, y: y }; if (clickPos.y > 1350 && clickPos.y < 1450) { // Back button clicked showMenu(); return; } // Check level buttons for (var i = 0; i < levelButtons.length; i++) { var btn = levelButtons[i]; if (Math.abs(clickPos.x - btn.x) < 100 && Math.abs(clickPos.y - btn.y) < 50) { if (btn.levelNumber <= storage.unlockedLevels) { storage.gameMode = 'level'; storage.currentLevel = btn.levelNumber; startGame(); } break; } } }; return self; }); var MenuScreen = Container.expand(function () { var self = Container.call(this); var titleText = new Text2('SPACE DEFENDER', { size: 120, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 400; self.addChild(titleText); var storyButton = new Text2('STORY MODE', { size: 80, fill: 0x00FF00 }); storyButton.anchor.set(0.5, 0.5); storyButton.x = 1024; storyButton.y = 800; self.addChild(storyButton); var levelsButton = new Text2('LEVEL SELECT', { size: 80, fill: 0x0080FF }); levelsButton.anchor.set(0.5, 0.5); levelsButton.x = 1024; levelsButton.y = 1000; self.addChild(levelsButton); var endlessButton = new Text2('ENDLESS MODE', { size: 80, fill: 0xFF8000 }); endlessButton.anchor.set(0.5, 0.5); endlessButton.x = 1024; endlessButton.y = 1200; self.addChild(endlessButton); var tutorialButton = new Text2('TUTORIAL', { size: 80, fill: 0xFFFF00 }); tutorialButton.anchor.set(0.5, 0.5); tutorialButton.x = 1024; tutorialButton.y = 1400; self.addChild(tutorialButton); self.down = function (x, y, obj) { var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : { x: x, y: y }; if (clickPos.y > 750 && clickPos.y < 850) { // Story mode clicked storage.gameMode = 'story'; storage.currentLevel = storage.storyProgress + 1; startGame(); } else if (clickPos.y > 950 && clickPos.y < 1050) { // Level select clicked showLevelSelect(); } else if (clickPos.y > 1150 && clickPos.y < 1250) { // Endless mode clicked storage.gameMode = 'endless'; storage.currentLevel = 1; startGame(); } else if (clickPos.y > 1350 && clickPos.y < 1450) { // Tutorial clicked showTutorial(); } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.hp = 3; self.shootTimer = 0; self.shootInterval = 15; self.powerUpTimer = 0; self.rapidFire = false; self.gunChangeTimer = 0; self.update = function () { self.shootTimer++; self.gunChangeTimer++; // Auto-cycle gun types every 5 seconds for tutorial effect if (self.gunChangeTimer >= 300) { self.gunChangeTimer = 0; var gunTypeKeys = Object.keys(gunTypes); var currentIndex = gunTypeKeys.indexOf(currentGunType); currentGunType = gunTypeKeys[(currentIndex + 1) % gunTypeKeys.length]; } var currentGun = gunTypes[currentGunType]; var currentShootInterval = self.rapidFire ? 5 : currentGun.interval; if (self.shootTimer >= currentShootInterval) { self.shootTimer = 0; var bullet = new PlayerBullet(); bullet.x = self.x; bullet.y = self.y - 40; bullet.damage = currentGun.damage; bullet.speed = -12 * currentGun.speedMultiplier; playerBullets.push(bullet); game.addChild(bullet); LK.getSound('shoot').play(); } if (self.rapidFire) { self.powerUpTimer--; if (self.powerUpTimer <= 0) { self.rapidFire = false; } } }; return self; }); var PlayerBullet = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('playerBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = -12; self.damage = 1; self.update = function () { self.y += self.speed; }; return self; }); var PowerUp = Container.expand(function () { var self = Container.call(this); var graphics = self.attachAsset('powerUp', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 4; self.type = 'rapidFire'; // rapidFire, shield self.update = function () { self.y += self.speed; self.rotation += 0.1; }; return self; }); var StoryPanel = Container.expand(function (levelNumber) { var self = Container.call(this); var stories = ["The alien invasion has begun!\nDefend Earth's last outpost.", "Enemy forces are multiplying.\nPrepare for heavier resistance.", "Advanced alien fighters detected.\nUpgrade your weapons!", "The mothership approaches.\nThis is our final stand!", "Victory! Earth is safe.\nBut stay vigilant, commander."]; var storyText = new Text2(stories[Math.min(levelNumber - 1, stories.length - 1)] || "Mission briefing loading...", { size: 70, fill: 0xFFFFFF }); storyText.anchor.set(0.5, 0.5); storyText.x = 1024; storyText.y = 800; self.addChild(storyText); var continueText = new Text2('TAP TO CONTINUE', { size: 60, fill: 0x00FF00 }); continueText.anchor.set(0.5, 0.5); continueText.x = 1024; continueText.y = 1200; self.addChild(continueText); self.down = function (x, y, obj) { self.destroy(); initializeGameplay(); }; return self; }); var TutorialScreen = Container.expand(function () { var self = Container.call(this); var tutorialSteps = ["Welcome to Space Defender!\nTap and drag to move your ship.", "Your ship fires automatically.\nDestroy enemies to score points!", "Collect blue power-ups for rapid fire.\nAvoid enemy bullets!", "Different gun types affect your damage:\nBasic, Rapid, and Heavy modes.", "Enemies have different health levels.\nSome take multiple hits to destroy!", "Ready to begin your mission?\nTap to start the game!"]; var currentStep = 0; var stepText = new Text2(tutorialSteps[0], { size: 80, fill: 0xFFFFFF }); stepText.anchor.set(0.5, 0.5); stepText.x = 1024; stepText.y = 800; self.addChild(stepText); var nextText = new Text2('TAP TO CONTINUE', { size: 60, fill: 0x00FF00 }); nextText.anchor.set(0.5, 0.5); nextText.x = 1024; nextText.y = 1200; self.addChild(nextText); var skipText = new Text2('SKIP TUTORIAL', { size: 50, fill: 0xFF0000 }); skipText.anchor.set(0.5, 0.5); skipText.x = 1024; skipText.y = 1400; self.addChild(skipText); self.down = function (x, y, obj) { var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : { x: x, y: y }; // Skip tutorial if (clickPos.y > 1350 && clickPos.y < 1450) { self.destroy(); initializeGameplay(); return; } // Next step currentStep++; if (currentStep >= tutorialSteps.length) { self.destroy(); initializeGameplay(); } else { stepText.setText(tutorialSteps[currentStep]); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000033 }); /**** * Game Code ****/ // Game state variables var gameState = 'menu'; // menu, levelSelect, story, playing, tutorial var menuScreen = null; var levelSelectScreen = null; var storyPanel = null; var tutorialScreen = null; // Gun types system var gunTypes = { basic: { damage: 1, speedMultiplier: 1.0, interval: 15 }, rapid: { damage: 1, speedMultiplier: 1.2, interval: 8 }, heavy: { damage: 3, speedMultiplier: 0.8, interval: 25 } }; var currentGunType = 'basic'; // Game variables var player; var playerBullets = []; var enemies = []; var enemyBullets = []; var powerUps = []; var bosses = []; var enemySpawnTimer = 0; var enemySpawnInterval = 60; var powerUpSpawnTimer = 0; var bossSpawnScore = 500; var nextBossScore = bossSpawnScore; var dragNode = null; var waveLevel = 1; var levelTargetScore = 0; var levelCompleted = false; // UI Elements var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0, 0); scoreTxt.x = 120; scoreTxt.y = 50; var hpTxt = new Text2('HP: 3', { size: 60, fill: 0xFF0000 }); hpTxt.anchor.set(1, 0); hpTxt.x = -50; hpTxt.y = 50; var levelTxt = new Text2('Level: 1', { size: 60, fill: 0x00FF00 }); levelTxt.anchor.set(0.5, 0); levelTxt.y = 50; function showMenu() { gameState = 'menu'; clearGame(); menuScreen = new MenuScreen(); game.addChild(menuScreen); } function showLevelSelect() { gameState = 'levelSelect'; if (menuScreen) { menuScreen.destroy(); menuScreen = null; } levelSelectScreen = new LevelSelectScreen(); game.addChild(levelSelectScreen); } function showTutorial() { gameState = 'tutorial'; if (menuScreen) { menuScreen.destroy(); menuScreen = null; } storage.gameMode = 'tutorial'; storage.currentLevel = 1; tutorialScreen = new TutorialScreen(); game.addChild(tutorialScreen); } function startGame() { gameState = storage.gameMode === 'story' ? 'story' : 'playing'; if (levelSelectScreen) { levelSelectScreen.destroy(); levelSelectScreen = null; } if (menuScreen) { menuScreen.destroy(); menuScreen = null; } if (storage.gameMode === 'story' && storage.currentLevel <= 5) { storyPanel = new StoryPanel(storage.currentLevel); game.addChild(storyPanel); } else { initializeGameplay(); } } function initializeGameplay() { gameState = 'playing'; // Clear any story panel if (storyPanel) { storyPanel.destroy(); storyPanel = null; } // Reset game variables clearGame(); LK.setScore(0); // Set level parameters setupLevel(storage.currentLevel); // Initialize player player = new Player(); player.x = 1024; player.y = 2400; game.addChild(player); // Add UI elements LK.gui.topLeft.addChild(scoreTxt); LK.gui.topRight.addChild(hpTxt); LK.gui.top.addChild(levelTxt); levelTxt.setText('Level: ' + storage.currentLevel); } function setupLevel(level) { // Set level-specific parameters if (storage.gameMode === 'endless') { levelTargetScore = 0; // No target for endless mode enemySpawnInterval = 60; } else { levelTargetScore = level * 200; // Score needed to complete level enemySpawnInterval = Math.max(20, 80 - level * 5); bossSpawnScore = levelTargetScore * 0.8; nextBossScore = bossSpawnScore; } levelCompleted = false; } function clearGame() { // Clear all game objects for (var i = 0; i < enemies.length; i++) { enemies[i].destroy(); } for (var i = 0; i < enemyBullets.length; i++) { enemyBullets[i].destroy(); } for (var i = 0; i < playerBullets.length; i++) { playerBullets[i].destroy(); } for (var i = 0; i < powerUps.length; i++) { powerUps[i].destroy(); } for (var i = 0; i < bosses.length; i++) { bosses[i].destroy(); } if (player) { player.destroy(); player = null; } // Clear arrays enemies = []; enemyBullets = []; playerBullets = []; powerUps = []; bosses = []; // Remove UI elements if (scoreTxt.parent) scoreTxt.parent.removeChild(scoreTxt); if (hpTxt.parent) hpTxt.parent.removeChild(hpTxt); if (levelTxt.parent) levelTxt.parent.removeChild(levelTxt); } function checkLevelComplete() { if (storage.gameMode !== 'endless' && !levelCompleted && LK.getScore() >= levelTargetScore) { levelCompleted = true; // Update progress if (storage.gameMode === 'story') { storage.storyProgress = Math.max(storage.storyProgress, storage.currentLevel); } storage.unlockedLevels = Math.max(storage.unlockedLevels, storage.currentLevel + 1); // Show level complete message var completeText = new Text2('LEVEL COMPLETE!', { size: 100, fill: 0x00FF00 }); completeText.anchor.set(0.5, 0.5); completeText.x = 1024; completeText.y = 1366; game.addChild(completeText); // Auto-advance after 2 seconds LK.setTimeout(function () { if (storage.gameMode === 'story' && storage.currentLevel < 5) { storage.currentLevel++; startGame(); } else { showMenu(); } }, 2000); } } // Initialize menu on start showMenu(); // Event handlers function handleMove(x, y, obj) { if (dragNode) { dragNode.x = Math.max(40, Math.min(2008, x)); dragNode.y = Math.max(100, Math.min(2632, y)); } } game.move = handleMove; game.down = function (x, y, obj) { dragNode = player; handleMove(x, y, obj); }; game.up = function (x, y, obj) { dragNode = null; }; // Main game loop game.update = function () { if (gameState !== 'playing') return; // Update score display scoreTxt.setText('Score: ' + LK.getScore()); hpTxt.setText('HP: ' + player.hp); // Check level completion checkLevelComplete(); // Spawn enemies enemySpawnTimer++; var currentSpawnInterval = Math.max(20, enemySpawnInterval - Math.floor(LK.getScore() / 100) * 5); if (enemySpawnTimer >= currentSpawnInterval) { enemySpawnTimer = 0; var enemy = new Enemy(); enemy.x = Math.random() * 1800 + 124; enemy.y = -50; enemy.speed = 2 + Math.floor(LK.getScore() / 200) + (storage.currentLevel - 1); enemies.push(enemy); game.addChild(enemy); } // Spawn power-ups occasionally powerUpSpawnTimer++; if (powerUpSpawnTimer >= 900 && Math.random() < 0.02) { powerUpSpawnTimer = 0; var powerUp = new PowerUp(); powerUp.x = Math.random() * 1800 + 124; powerUp.y = -50; powerUps.push(powerUp); game.addChild(powerUp); } // Spawn boss at score milestones if (LK.getScore() >= nextBossScore && bosses.length === 0) { var boss = new Boss(); boss.x = 1024; boss.y = -100; bosses.push(boss); game.addChild(boss); nextBossScore += bossSpawnScore; } // Handle player bullets for (var i = playerBullets.length - 1; i >= 0; i--) { var bullet = playerBullets[i]; if (bullet.lastY === undefined) bullet.lastY = bullet.y; // Remove bullets that go off screen if (bullet.lastY >= -50 && bullet.y < -50) { bullet.destroy(); playerBullets.splice(i, 1); continue; } // Check collision with enemies var hitEnemy = false; for (var j = enemies.length - 1; j >= 0; j--) { if (bullet.intersects(enemies[j])) { enemies[j].hp -= bullet.damage || 1; // Visual feedback for damage tween(enemies[j], { tint: 0xFFFFFF }, { duration: 100, onFinish: function onFinish() { if (enemies[j] && enemies[j].hp > 2) { enemies[j].children[0].tint = 0xFF6666; } else if (enemies[j] && enemies[j].hp > 1) { enemies[j].children[0].tint = 0xFFAAAA; } } }); if (enemies[j].hp <= 0) { LK.setScore(LK.getScore() + 10 * enemies[j].maxHp); LK.getSound('enemyHit').play(); enemies[j].destroy(); enemies.splice(j, 1); } bullet.destroy(); playerBullets.splice(i, 1); hitEnemy = true; break; } } if (hitEnemy) continue; // Check collision with bosses for (var k = bosses.length - 1; k >= 0; k--) { if (bullet.intersects(bosses[k])) { bosses[k].hp -= bullet.damage || 1; // Visual feedback for boss damage tween(bosses[k], { tint: 0xFFFFFF }, { duration: 150 }); if (bosses[k].hp <= 0) { LK.setScore(LK.getScore() + 100); LK.getSound('enemyHit').play(); bosses[k].destroy(); bosses.splice(k, 1); } bullet.destroy(); playerBullets.splice(i, 1); break; } } bullet.lastY = bullet.y; } // Handle enemy bullets for (var i = enemyBullets.length - 1; i >= 0; i--) { var bullet = enemyBullets[i]; if (bullet.lastY === undefined) bullet.lastY = bullet.y; // Remove bullets that go off screen if (bullet.lastY <= 2782 && bullet.y > 2782) { bullet.destroy(); enemyBullets.splice(i, 1); continue; } // Check collision with player if (bullet.intersects(player)) { player.hp--; LK.getSound('playerHit').play(); LK.effects.flashObject(player, 0xff0000, 500); if (player.hp <= 0) { LK.setTimeout(function () { showMenu(); }, 2000); LK.showGameOver(); return; } bullet.destroy(); enemyBullets.splice(i, 1); } bullet.lastY = bullet.y; } // Handle enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; if (enemy.lastY === undefined) enemy.lastY = enemy.y; // Remove enemies that go off screen if (enemy.lastY <= 2782 && enemy.y > 2782) { enemy.destroy(); enemies.splice(i, 1); continue; } // Check collision with player if (enemy.intersects(player)) { player.hp--; LK.getSound('playerHit').play(); LK.effects.flashScreen(0xff0000, 1000); if (player.hp <= 0) { LK.setTimeout(function () { showMenu(); }, 2000); LK.showGameOver(); return; } enemy.destroy(); enemies.splice(i, 1); } enemy.lastY = enemy.y; } // Handle bosses for (var i = bosses.length - 1; i >= 0; i--) { var boss = bosses[i]; if (boss.lastY === undefined) boss.lastY = boss.y; // Remove bosses that go off screen if (boss.lastY <= 2782 && boss.y > 2782) { boss.destroy(); bosses.splice(i, 1); continue; } // Check collision with player if (boss.intersects(player)) { player.hp--; LK.getSound('playerHit').play(); LK.effects.flashScreen(0xff0000, 1000); if (player.hp <= 0) { LK.setTimeout(function () { showMenu(); }, 2000); LK.showGameOver(); return; } } boss.lastY = boss.y; } // Handle power-ups for (var i = powerUps.length - 1; i >= 0; i--) { var powerUp = powerUps[i]; if (powerUp.lastY === undefined) powerUp.lastY = powerUp.y; // Remove power-ups that go off screen if (powerUp.lastY <= 2782 && powerUp.y > 2782) { powerUp.destroy(); powerUps.splice(i, 1); continue; } // Check collision with player if (powerUp.intersects(player)) { LK.getSound('powerUpSound').play(); if (powerUp.type === 'rapidFire') { player.rapidFire = true; player.powerUpTimer = 600; // 10 seconds at 60fps } powerUp.destroy(); powerUps.splice(i, 1); } powerUp.lastY = powerUp.y; } };
===================================================================
--- original.js
+++ change.js
@@ -47,11 +47,20 @@
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
- self.hp = 1;
+ // Varied health based on current level and randomization
+ var baseHp = Math.floor(storage.currentLevel / 2) + 1;
+ self.hp = baseHp + (Math.random() < 0.3 ? Math.floor(Math.random() * 2) + 1 : 0);
+ self.maxHp = self.hp;
self.shootTimer = 0;
self.shootInterval = 120;
+ // Visual indicator for health - tint red for high HP enemies
+ if (self.hp > 2) {
+ graphics.tint = 0xFF6666;
+ } else if (self.hp > 1) {
+ graphics.tint = 0xFFAAAA;
+ }
self.update = function () {
self.y += self.speed;
self.shootTimer++;
if (self.shootTimer >= self.shootInterval && Math.random() < 0.01) {
@@ -61,8 +70,17 @@
bullet.y = self.y + 30;
enemyBullets.push(bullet);
game.addChild(bullet);
}
+ // Update tint based on current health
+ if (self.hp < self.maxHp) {
+ var healthRatio = self.hp / self.maxHp;
+ if (healthRatio < 0.3) {
+ graphics.tint = 0xFF0000;
+ } else if (healthRatio < 0.7) {
+ graphics.tint = 0xFF6666;
+ }
+ }
};
return self;
});
var EnemyBullet = Container.expand(function () {
@@ -166,8 +184,16 @@
endlessButton.anchor.set(0.5, 0.5);
endlessButton.x = 1024;
endlessButton.y = 1200;
self.addChild(endlessButton);
+ var tutorialButton = new Text2('TUTORIAL', {
+ size: 80,
+ fill: 0xFFFF00
+ });
+ tutorialButton.anchor.set(0.5, 0.5);
+ tutorialButton.x = 1024;
+ tutorialButton.y = 1400;
+ self.addChild(tutorialButton);
self.down = function (x, y, obj) {
var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : {
x: x,
y: y
@@ -184,8 +210,11 @@
// Endless mode clicked
storage.gameMode = 'endless';
storage.currentLevel = 1;
startGame();
+ } else if (clickPos.y > 1350 && clickPos.y < 1450) {
+ // Tutorial clicked
+ showTutorial();
}
};
return self;
});
@@ -199,16 +228,28 @@
self.shootTimer = 0;
self.shootInterval = 15;
self.powerUpTimer = 0;
self.rapidFire = false;
+ self.gunChangeTimer = 0;
self.update = function () {
self.shootTimer++;
- var currentShootInterval = self.rapidFire ? 5 : self.shootInterval;
+ self.gunChangeTimer++;
+ // Auto-cycle gun types every 5 seconds for tutorial effect
+ if (self.gunChangeTimer >= 300) {
+ self.gunChangeTimer = 0;
+ var gunTypeKeys = Object.keys(gunTypes);
+ var currentIndex = gunTypeKeys.indexOf(currentGunType);
+ currentGunType = gunTypeKeys[(currentIndex + 1) % gunTypeKeys.length];
+ }
+ var currentGun = gunTypes[currentGunType];
+ var currentShootInterval = self.rapidFire ? 5 : currentGun.interval;
if (self.shootTimer >= currentShootInterval) {
self.shootTimer = 0;
var bullet = new PlayerBullet();
bullet.x = self.x;
bullet.y = self.y - 40;
+ bullet.damage = currentGun.damage;
+ bullet.speed = -12 * currentGun.speedMultiplier;
playerBullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
}
@@ -227,8 +268,9 @@
anchorX: 0.5,
anchorY: 0.5
});
self.speed = -12;
+ self.damage = 1;
self.update = function () {
self.y += self.speed;
};
return self;
@@ -271,8 +313,58 @@
initializeGameplay();
};
return self;
});
+var TutorialScreen = Container.expand(function () {
+ var self = Container.call(this);
+ var tutorialSteps = ["Welcome to Space Defender!\nTap and drag to move your ship.", "Your ship fires automatically.\nDestroy enemies to score points!", "Collect blue power-ups for rapid fire.\nAvoid enemy bullets!", "Different gun types affect your damage:\nBasic, Rapid, and Heavy modes.", "Enemies have different health levels.\nSome take multiple hits to destroy!", "Ready to begin your mission?\nTap to start the game!"];
+ var currentStep = 0;
+ var stepText = new Text2(tutorialSteps[0], {
+ size: 80,
+ fill: 0xFFFFFF
+ });
+ stepText.anchor.set(0.5, 0.5);
+ stepText.x = 1024;
+ stepText.y = 800;
+ self.addChild(stepText);
+ var nextText = new Text2('TAP TO CONTINUE', {
+ size: 60,
+ fill: 0x00FF00
+ });
+ nextText.anchor.set(0.5, 0.5);
+ nextText.x = 1024;
+ nextText.y = 1200;
+ self.addChild(nextText);
+ var skipText = new Text2('SKIP TUTORIAL', {
+ size: 50,
+ fill: 0xFF0000
+ });
+ skipText.anchor.set(0.5, 0.5);
+ skipText.x = 1024;
+ skipText.y = 1400;
+ self.addChild(skipText);
+ self.down = function (x, y, obj) {
+ var clickPos = obj.parent ? self.toLocal(obj.parent.toGlobal(obj.position)) : {
+ x: x,
+ y: y
+ };
+ // Skip tutorial
+ if (clickPos.y > 1350 && clickPos.y < 1450) {
+ self.destroy();
+ initializeGameplay();
+ return;
+ }
+ // Next step
+ currentStep++;
+ if (currentStep >= tutorialSteps.length) {
+ self.destroy();
+ initializeGameplay();
+ } else {
+ stepText.setText(tutorialSteps[currentStep]);
+ }
+ };
+ return self;
+});
/****
* Initialize Game
****/
@@ -283,12 +375,32 @@
/****
* Game Code
****/
// Game state variables
-var gameState = 'menu'; // menu, levelSelect, story, playing
+var gameState = 'menu'; // menu, levelSelect, story, playing, tutorial
var menuScreen = null;
var levelSelectScreen = null;
var storyPanel = null;
+var tutorialScreen = null;
+// Gun types system
+var gunTypes = {
+ basic: {
+ damage: 1,
+ speedMultiplier: 1.0,
+ interval: 15
+ },
+ rapid: {
+ damage: 1,
+ speedMultiplier: 1.2,
+ interval: 8
+ },
+ heavy: {
+ damage: 3,
+ speedMultiplier: 0.8,
+ interval: 25
+ }
+};
+var currentGunType = 'basic';
// Game variables
var player;
var playerBullets = [];
var enemies = [];
@@ -339,8 +451,19 @@
}
levelSelectScreen = new LevelSelectScreen();
game.addChild(levelSelectScreen);
}
+function showTutorial() {
+ gameState = 'tutorial';
+ if (menuScreen) {
+ menuScreen.destroy();
+ menuScreen = null;
+ }
+ storage.gameMode = 'tutorial';
+ storage.currentLevel = 1;
+ tutorialScreen = new TutorialScreen();
+ game.addChild(tutorialScreen);
+}
function startGame() {
gameState = storage.gameMode === 'story' ? 'story' : 'playing';
if (levelSelectScreen) {
levelSelectScreen.destroy();
@@ -522,11 +645,24 @@
// Check collision with enemies
var hitEnemy = false;
for (var j = enemies.length - 1; j >= 0; j--) {
if (bullet.intersects(enemies[j])) {
- enemies[j].hp--;
+ enemies[j].hp -= bullet.damage || 1;
+ // Visual feedback for damage
+ tween(enemies[j], {
+ tint: 0xFFFFFF
+ }, {
+ duration: 100,
+ onFinish: function onFinish() {
+ if (enemies[j] && enemies[j].hp > 2) {
+ enemies[j].children[0].tint = 0xFF6666;
+ } else if (enemies[j] && enemies[j].hp > 1) {
+ enemies[j].children[0].tint = 0xFFAAAA;
+ }
+ }
+ });
if (enemies[j].hp <= 0) {
- LK.setScore(LK.getScore() + 10);
+ LK.setScore(LK.getScore() + 10 * enemies[j].maxHp);
LK.getSound('enemyHit').play();
enemies[j].destroy();
enemies.splice(j, 1);
}
@@ -539,9 +675,15 @@
if (hitEnemy) continue;
// Check collision with bosses
for (var k = bosses.length - 1; k >= 0; k--) {
if (bullet.intersects(bosses[k])) {
- bosses[k].hp--;
+ bosses[k].hp -= bullet.damage || 1;
+ // Visual feedback for boss damage
+ tween(bosses[k], {
+ tint: 0xFFFFFF
+ }, {
+ duration: 150
+ });
if (bosses[k].hp <= 0) {
LK.setScore(LK.getScore() + 100);
LK.getSound('enemyHit').play();
bosses[k].destroy();