User prompt
### **Prompt:** *"Design and implement a complete **main menu system** and **game reset logic** for a JavaScript game (using Canvas/WebGL + DOM or libraries like Phaser/Pixi.js). Follow these specifications: ### **1. Main Menu UI:** - **Title**: "Villain Defense" in a bold **comic-style font** (e.g., `Bangers`, `Luckiest Guy`, or custom TTF/woff2). - *Effect*: Optional outline/shadow for readability. - **Play Button**: - Text: "JUGAR" (Spanish). - Visual: Comic-style button with hover/click animations (e.g., scale + color change). - *Trigger*: Starts a **new game session** (resets all progress). ### **2. Reset Progression on Play**: - **Mechanics to Reset**: - Player level → `1`. - Experience → `0`. - Gems/currency → `0`. - Skill tree → Unlocked defaults. - **Storage**: Ensure no persistence (e.g., clear `localStorage` or game-state variables). ### **3. Custom Assets Creation**: - **Requested Assets** (provide SVG/PNG descriptions or generate procedurally): - **Menu Background**: Thematic (e.g., city under attack, cartoon villains). - **Game Background**: Scrollable battlefield (e.g., comic-style streets). - **UI Elements**: - Experience/level bar (comic-themed with progress fill). - Gem counter (icon + comic-style border). - Buttons (rounded, bold borders, vibrant colors). ### **Technical Implementation:** - **If DOM/CSS**: Use flexbox/grid for responsive layout. Example: ```css #play-button { font-family: "Bangers", cursive; background-color: #FF0000; } ``` - **If Canvas/Phaser**: - Load assets via `this.load.image()` (Phaser) or `new Image()` (Canvas). - Reset logic: `this.player.resetStats()` on game start. ### **Optional Extras**: - Add a "Settings" button (sound toggle). - Animated title (e.g., wobble effect). Prioritize mobile/desktop responsiveness (16:9). Provide all code snippets and asset specs."* ↪💡 Consider importing and using the following plugins: @upit/tween.v1, @upit/storage.v1
User prompt
### **Prompt:** *"Analyze and fix this JavaScript game UI issue (likely using Canvas/WebGL with DOM overlay or a library like Phaser/Pixi.js): **Problem:** When skill choices appear during level-up, their description text is hidden *behind* the selection buttons, making it unreadable. **Expected Behavior:** - Skill descriptions should appear **below** their respective buttons. - Text must remain clearly visible with proper contrast (no overlaps). **Technical Debugging Steps Requested:** 1. **UI Hierarchy Analysis:** - If using DOM/CSS: Check `z-index`, `position`, and parent-container `overflow` properties. - If using Canvas/WebGL: Verify render order of UI elements (e.g., `ctx.fillText()` called after buttons are drawn). 2. **Positioning Fix:** - Provide code to reposition descriptions dynamically (e.g., `buttonElement.getBoundingClientRect().bottom + 10px` for DOM). - For game engines: Adjust skill description coordinates (e.g., `skillDesc.y = skillButton.y + skillButton.height` in Phaser). 3. **Responsive Layout:** - Ensure descriptions adapt to screen size (e.g., `window.resize` event listeners). **Code Examples Needed:** - A snippet to *detect overlaps* (e.g., `elementFromPoint()` for DOM). - Logic to *reposition descriptions* without breaking existing event listeners. **Assumptions:** - 16:9 aspect ratio (prioritize 1920x1080). - UI is either DOM-based (HTML/CSS) or rendered via Canvas/WebGL."* ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
### **Prompt:** *"Analyze and fix these JavaScript game issues (likely using Canvas/WebGL or a library like Phaser/Pixi.js): 1. **Character/Enemies Too Small on Screen**: - **Problem**: Player and enemies appear disproportionately small compared to the game environment. - **Expected**: Proper scaling relative to screen size and camera view. - **Debug Steps**: - Check if `ctx.scale()` (Canvas 2D) or sprite `scale.set()` (Phaser/Pixi) is misconfigured. - Ensure camera/viewport settings match the game's resolution (e.g., `camera.zoom` in Phaser). - Dynamically adjust scaling based on `window.innerWidth/innerHeight`. 2. **Dark Overlay Obstructing Upgrade Menu**: - **Problem**: When leveling up, a dark UI overlay blocks visibility of skill upgrades. - **Expected**: Transparent or properly layered UI elements with readable text. - **Debug Steps**: - Verify `zIndex`/rendering order of DOM elements (if HTML/CSS-based UI). - Adjust `globalAlpha` (Canvas) or `setAlpha()` (Phaser) for the overlay. - Check for conflicting event listeners that might prevent UI updates. **Request**: - Provide JavaScript code snippets to fix scaling (e.g., dynamic resizing logic). - Explain how to modify the overlay’s opacity/rendering (e.g., CSS `rgba()` or Canvas `globalAlpha`). - Assume a 16:9 aspect ratio (1920x1080 baseline) but ensure responsiveness."* ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
haz que cada vez que subas de nivel y aparezcan las elecciones de mejoras, se ponga un cuadro donde muestre cuál mejoras puedes elegir ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Arregla Bugs visual al elegir las ventajas por experiencia de nivel ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Arregla: Compilation error[L488]: Uncaught TypeError: Cannot read properties of undefined (reading 'apply')
User prompt
Arregla Bugs
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'apply')' in or related to this line: 'upgradeTypes[upgradeKey].apply();' Line Number: 488
User prompt
Añade una barra de experiencia, que cada vez que el jugador suba de nivel, aparezcan diferentes mejoras para elegir, cómo aumentar la velocidad de disparo, el rango de disparo y más ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Haz que las gemas aparezcan automáticamente en el suelo. Haz que los enemigos aparezcan más rápido al iniciar el juego. Arregla Bugs del juego con las gemas. Y Haz que el jugador disparé automáticamente a los enemigos cuando entran a un rango corto de distancia y que los disparos tengan tiempo de espera de 1,8 segundos ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Endless Horde Survivor
Initial prompt
Quiero un juego de supervivencia con oleadas de enemigos estilo 'Vampire Survivors' con estas características:* 1. **Personaje principal**: - Movimiento con teclas WASD o joystick. - Ataque automático (sin botones, el personaje dispara o golpea en dirección a los enemigos más cercanos). 2. **Sistema de oleadas**: - Enemigos aparecen en grupos cada 30 segundos, aumentando en cantidad y dificultad con el tiempo. - Tipos de enemigos: - *Básicos*: Caminan hacia el jugador lentamente. - *Rápidos*: Velocidad alta pero poca vida. - *Tanques*: Mucha vida pero lentos. 3. **Progresión y habilidades**: - El jugador gana experiencia al matar enemigos, sube de nivel y puede elegir mejoras (ej: +daño, +velocidad, nuevos ataques). - Habilidades especiales (ej: área de efecto, proyectiles giratorios, invocaciones). 4. **Recolección de objetos**: - Monedas o gemas que caen de enemigos para comprar mejoras entre partidas. 5. **Estilo visual**: - Gráficos pixel art 2D (personaje y enemigos con sprites simples). - Efectos de partículas para ataques y explosiones. 6. **Sonido**: - Música retro y SFX para ataques/muertes de enemigos. *Usa JavaScript con los plugins de Upit como @upit/tween.v1 para animaciones y @upit/storage.v1 para guardar el progreso. Genera el código base con clases para el jugador, enemigos y sistema de oleadas."*
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 20;
self.targetX = 0;
self.targetY = 0;
self.velocityX = 0;
self.velocityY = 0;
// Velocity will be calculated when bullet is created
self.update = function () {
self.x += self.velocityX;
self.y += self.velocityY;
// Check if off screen
if (self.x < -50 || self.x > 2098 || self.y < -50 || self.y > 2782) {
self.markForDestroy = true;
}
// Check collision with enemies
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
if (getDistance(self, enemy) < 30) {
enemy.takeDamage(self.damage);
self.markForDestroy = true;
break;
}
}
};
return self;
});
var Enemy = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'basic';
var assetName = self.type + 'Enemy';
var enemyGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Set stats based on type
if (self.type === 'basic') {
self.health = 30;
self.speed = 1.5;
self.damage = 10;
self.experienceValue = 2;
} else if (self.type === 'fast') {
self.health = 15;
self.speed = 3.5;
self.damage = 8;
self.experienceValue = 3;
} else if (self.type === 'tank') {
self.health = 80;
self.speed = 0.8;
self.damage = 20;
self.experienceValue = 5;
}
self.maxHealth = self.health;
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xFFFFFF, 100);
if (self.health <= 0) {
self.die();
}
};
self.die = function () {
// Drop experience orb
var expOrb = new ExperienceOrb();
expOrb.x = self.x;
expOrb.y = self.y;
expOrb.value = self.experienceValue;
experienceOrbs.push(expOrb);
game.addChild(expOrb);
// Chance to drop gem
if (Math.random() < 0.3) {
var gem = new Gem();
gem.x = self.x;
gem.y = self.y;
gems.push(gem);
game.addChild(gem);
}
LK.getSound('enemyHit').play();
LK.setScore(LK.getScore() + self.experienceValue * 10);
};
self.update = function () {
// Move towards player
var dx = player.x - self.x;
var dy = player.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
}
// Check collision with player
if (getDistance(self, player) < 50) {
player.takeDamage(self.damage);
}
};
return self;
});
var ExperienceOrb = Container.expand(function () {
var self = Container.call(this);
var orbGraphics = self.attachAsset('experienceOrb', {
anchorX: 0.5,
anchorY: 0.5
});
self.value = 1;
self.collectRadius = 100;
self.update = function () {
// Move towards player if close enough
var distance = getDistance(self, player);
if (distance < self.collectRadius) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var moveSpeed = 6;
if (distance > 0) {
self.x += dx / distance * moveSpeed;
self.y += dy / distance * moveSpeed;
}
// Collect if very close
if (distance < 25) {
player.gainExperience(self.value);
self.markForDestroy = true;
}
}
};
return self;
});
var Gem = Container.expand(function () {
var self = Container.call(this);
var gemGraphics = self.attachAsset('gem', {
anchorX: 0.5,
anchorY: 0.5
});
self.value = 1;
self.update = function () {
// Simple floating animation
self.y += Math.sin(LK.ticks * 0.1) * 0.5;
// Collect if player touches
if (getDistance(self, player) < 40) {
gemsCollected++;
LK.getSound('gemCollect').play();
// Add collection animation
tween(self, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
self.markForDestroy = true;
}
});
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
var playerGraphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.speed = 4;
self.damage = 20;
self.attackSpeed = 1.0;
self.lastAttack = 0;
self.level = 1;
self.experience = 0;
self.experienceToNext = 10;
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xFF0000, 200);
if (self.health <= 0) {
LK.showGameOver();
}
};
self.gainExperience = function (amount) {
self.experience += amount;
if (self.experience >= self.experienceToNext) {
self.levelUp();
}
};
self.levelUp = function () {
self.level++;
self.experience = 0;
self.experienceToNext = Math.floor(self.experienceToNext * 1.2);
LK.getSound('levelUp').play();
showUpgradeChoice();
};
self.update = function () {
// Auto-attack logic with configurable cooldown
var cooldown = self.attackCooldown || 108; // Default 1.8 seconds
var range = self.attackRange || 200; // Default range
if (LK.ticks - self.lastAttack > cooldown) {
var nearestEnemy = findNearestEnemy();
if (nearestEnemy && getDistance(self, nearestEnemy) < range) {
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.targetX = nearestEnemy.x;
bullet.targetY = nearestEnemy.y;
bullet.damage = self.damage;
// Calculate velocity towards target
var dx = bullet.targetX - bullet.x;
var dy = bullet.targetY - bullet.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 0) {
bullet.velocityX = dx / distance * bullet.speed;
bullet.velocityY = dy / distance * bullet.speed;
}
bullets.push(bullet);
game.addChild(bullet);
LK.getSound('shoot').play();
self.lastAttack = LK.ticks;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2E2E2E
});
/****
* Game Code
****/
var player;
var enemies = [];
var bullets = [];
var experienceOrbs = [];
var gems = [];
var waveNumber = 1;
var enemiesPerWave = 5;
var lastWaveTime = 0;
var gemsCollected = storage.gemsCollected || 0;
var upgradePanelVisible = false;
var upgradePanel = null;
var upgradeOptions = [];
// Upgrade definitions
var upgradeTypes = {
fireRate: {
name: "Fire Rate +",
description: "Reduce attack cooldown",
apply: function apply() {
player.attackCooldown = Math.max(30, (player.attackCooldown || 108) - 18);
}
},
damage: {
name: "Damage +",
description: "Increase bullet damage",
apply: function apply() {
player.damage += 8;
}
},
range: {
name: "Range +",
description: "Increase attack range",
apply: function apply() {
player.attackRange = (player.attackRange || 200) + 50;
}
},
speed: {
name: "Speed +",
description: "Move faster",
apply: function apply() {
player.speed += 0.8;
}
},
health: {
name: "Max Health +",
description: "Increase maximum health",
apply: function apply() {
player.maxHealth += 25;
player.health += 25;
}
}
};
// UI Elements
var healthBar = new Text2('Health: 100/100', {
size: 60,
fill: 0xFF4444
});
healthBar.anchor.set(0, 0);
LK.gui.topLeft.addChild(healthBar);
healthBar.x = 120; // Offset from menu icon
var levelText = new Text2('Level: 1', {
size: 60,
fill: 0xFFFF44
});
levelText.anchor.set(0.5, 0);
LK.gui.top.addChild(levelText);
var waveText = new Text2('Wave: 1', {
size: 60,
fill: 0x44FFFF
});
waveText.anchor.set(1, 0);
LK.gui.topRight.addChild(waveText);
var gemsText = new Text2('Gems: ' + gemsCollected, {
size: 50,
fill: 0x00BCD4
});
gemsText.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(gemsText);
// Experience bar
var expBarBg = LK.getAsset('experienceOrb', {
width: 400,
height: 20,
anchorX: 0.5,
anchorY: 1
});
expBarBg.tint = 0x333333;
LK.gui.bottom.addChild(expBarBg);
expBarBg.y = -20;
var expBarFill = LK.getAsset('experienceOrb', {
width: 1,
height: 18,
anchorX: 0,
anchorY: 1
});
expBarFill.tint = 0x8bc34a;
expBarBg.addChild(expBarFill);
expBarFill.x = -200;
expBarFill.y = -1;
var expText = new Text2('XP: 0/10', {
size: 40,
fill: 0xFFFFFF
});
expText.anchor.set(0.5, 1);
LK.gui.bottom.addChild(expText);
expText.y = -50;
// Initialize player
player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
// Utility functions
function getDistance(obj1, obj2) {
var dx = obj1.x - obj2.x;
var dy = obj1.y - obj2.y;
return Math.sqrt(dx * dx + dy * dy);
}
function findNearestEnemy() {
var nearest = null;
var minDistance = Infinity;
for (var i = 0; i < enemies.length; i++) {
var distance = getDistance(player, enemies[i]);
if (distance < minDistance) {
minDistance = distance;
nearest = enemies[i];
}
}
return nearest;
}
function spawnEnemy() {
var type = 'basic';
var rand = Math.random();
if (rand < 0.6) {
type = 'basic';
} else if (rand < 0.8) {
type = 'fast';
} else {
type = 'tank';
}
var enemy = new Enemy(type);
// Spawn from random edge
var side = Math.floor(Math.random() * 4);
if (side === 0) {
// Top
enemy.x = Math.random() * 2048;
enemy.y = -50;
} else if (side === 1) {
// Right
enemy.x = 2098;
enemy.y = Math.random() * 2732;
} else if (side === 2) {
// Bottom
enemy.x = Math.random() * 2048;
enemy.y = 2782;
} else {
// Left
enemy.x = -50;
enemy.y = Math.random() * 2732;
}
enemies.push(enemy);
game.addChild(enemy);
}
function showUpgradeChoice() {
if (upgradePanelVisible) return;
upgradePanelVisible = true;
// Create upgrade panel background
upgradePanel = LK.getAsset('experienceOrb', {
width: 800,
height: 600,
anchorX: 0.5,
anchorY: 0.5
});
upgradePanel.tint = 0x000000;
upgradePanel.alpha = 0.8;
LK.gui.center.addChild(upgradePanel);
// Create title
var title = new Text2('LEVEL UP!', {
size: 80,
fill: 0xFFD700
});
title.anchor.set(0.5, 0.5);
upgradePanel.addChild(title);
title.y = -200;
// Select 3 random upgrades
var availableUpgrades = Object.keys(upgradeTypes);
var selectedUpgrades = [];
for (var i = 0; i < 3; i++) {
var randomIndex = Math.floor(Math.random() * availableUpgrades.length);
selectedUpgrades.push(availableUpgrades[randomIndex]);
availableUpgrades.splice(randomIndex, 1);
}
// Create upgrade buttons
upgradeOptions = [];
for (var i = 0; i < selectedUpgrades.length; i++) {
var upgradeKey = selectedUpgrades[i];
var upgrade = upgradeTypes[upgradeKey];
var button = LK.getAsset('experienceOrb', {
width: 200,
height: 120,
anchorX: 0.5,
anchorY: 0.5
});
button.tint = 0x4CAF50;
button.upgradeKey = upgradeKey;
upgradePanel.addChild(button);
button.x = (i - 1) * 250;
button.y = 50;
var buttonText = new Text2(upgrade.name, {
size: 40,
fill: 0xFFFFFF
});
buttonText.anchor.set(0.5, 0.3);
button.addChild(buttonText);
var descText = new Text2(upgrade.description, {
size: 25,
fill: 0xCCCCCC
});
descText.anchor.set(0.5, 0.8);
button.addChild(descText);
// Add button functionality
button.down = function (x, y, obj) {
selectUpgrade(obj.upgradeKey);
};
upgradeOptions.push(button);
}
}
function selectUpgrade(upgradeKey) {
if (!upgradePanelVisible) return;
// Apply the upgrade
upgradeTypes[upgradeKey].apply();
// Remove upgrade panel
if (upgradePanel) {
upgradePanel.destroy();
upgradePanel = null;
}
upgradeOptions = [];
upgradePanelVisible = false;
}
// Touch controls
var dragStarted = false;
game.down = function (x, y, obj) {
dragStarted = true;
player.x = x;
player.y = y;
};
game.move = function (x, y, obj) {
if (dragStarted) {
player.x = x;
player.y = y;
}
};
game.up = function (x, y, obj) {
dragStarted = false;
};
// Add gem spawn timer
var lastGemSpawn = 0;
// Main game loop
game.update = function () {
// Spawn gems automatically every 5 seconds
if (LK.ticks - lastGemSpawn > 300) {
// 5 seconds at 60fps
var gem = new Gem();
// Random position on screen
gem.x = Math.random() * 1800 + 124; // Keep away from edges
gem.y = Math.random() * 2400 + 166;
gems.push(gem);
game.addChild(gem);
lastGemSpawn = LK.ticks;
}
// Spawn waves - faster at start
var waveInterval = Math.max(600, 1800 - waveNumber * 60); // Start at 10s, max 30s
if (LK.ticks - lastWaveTime > waveInterval) {
waveNumber++;
enemiesPerWave += 2;
lastWaveTime = LK.ticks;
// Spawn enemies for this wave
for (var i = 0; i < enemiesPerWave; i++) {
LK.setTimeout(function () {
spawnEnemy();
}, i * 200); // Stagger spawns
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
if (enemy.health <= 0) {
enemy.die();
enemy.destroy();
enemies.splice(i, 1);
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (bullet.markForDestroy) {
bullet.destroy();
bullets.splice(i, 1);
}
}
// Update experience orbs
for (var i = experienceOrbs.length - 1; i >= 0; i--) {
var orb = experienceOrbs[i];
if (orb.markForDestroy) {
orb.destroy();
experienceOrbs.splice(i, 1);
}
}
// Update gems
for (var i = gems.length - 1; i >= 0; i--) {
var gem = gems[i];
if (gem.markForDestroy) {
gem.destroy();
gems.splice(i, 1);
}
}
// Update UI
healthBar.setText('Health: ' + player.health + '/' + player.maxHealth);
levelText.setText('Level: ' + player.level);
waveText.setText('Wave: ' + waveNumber);
gemsText.setText('Gems: ' + gemsCollected);
// Update experience bar
var expPercentage = player.experience / player.experienceToNext;
expBarFill.width = 398 * expPercentage;
expText.setText('XP: ' + player.experience + '/' + player.experienceToNext);
// Save gems to storage
storage.gemsCollected = gemsCollected;
// Keep player within bounds
player.x = Math.max(40, Math.min(2008, player.x));
player.y = Math.max(40, Math.min(2692, player.y));
}; ===================================================================
--- original.js
+++ change.js
@@ -199,14 +199,14 @@
LK.getSound('levelUp').play();
showUpgradeChoice();
};
self.update = function () {
- // Auto-attack logic with 1.8 second cooldown
- if (LK.ticks - self.lastAttack > 108) {
- // 1.8 seconds at 60fps
+ // Auto-attack logic with configurable cooldown
+ var cooldown = self.attackCooldown || 108; // Default 1.8 seconds
+ var range = self.attackRange || 200; // Default range
+ if (LK.ticks - self.lastAttack > cooldown) {
var nearestEnemy = findNearestEnemy();
- if (nearestEnemy && getDistance(self, nearestEnemy) < 200) {
- // Short range
+ if (nearestEnemy && getDistance(self, nearestEnemy) < range) {
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
bullet.targetX = nearestEnemy.x;
@@ -249,8 +249,49 @@
var enemiesPerWave = 5;
var lastWaveTime = 0;
var gemsCollected = storage.gemsCollected || 0;
var upgradePanelVisible = false;
+var upgradePanel = null;
+var upgradeOptions = [];
+// Upgrade definitions
+var upgradeTypes = {
+ fireRate: {
+ name: "Fire Rate +",
+ description: "Reduce attack cooldown",
+ apply: function apply() {
+ player.attackCooldown = Math.max(30, (player.attackCooldown || 108) - 18);
+ }
+ },
+ damage: {
+ name: "Damage +",
+ description: "Increase bullet damage",
+ apply: function apply() {
+ player.damage += 8;
+ }
+ },
+ range: {
+ name: "Range +",
+ description: "Increase attack range",
+ apply: function apply() {
+ player.attackRange = (player.attackRange || 200) + 50;
+ }
+ },
+ speed: {
+ name: "Speed +",
+ description: "Move faster",
+ apply: function apply() {
+ player.speed += 0.8;
+ }
+ },
+ health: {
+ name: "Max Health +",
+ description: "Increase maximum health",
+ apply: function apply() {
+ player.maxHealth += 25;
+ player.health += 25;
+ }
+ }
+};
// UI Elements
var healthBar = new Text2('Health: 100/100', {
size: 60,
fill: 0xFF4444
@@ -275,8 +316,35 @@
fill: 0x00BCD4
});
gemsText.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(gemsText);
+// Experience bar
+var expBarBg = LK.getAsset('experienceOrb', {
+ width: 400,
+ height: 20,
+ anchorX: 0.5,
+ anchorY: 1
+});
+expBarBg.tint = 0x333333;
+LK.gui.bottom.addChild(expBarBg);
+expBarBg.y = -20;
+var expBarFill = LK.getAsset('experienceOrb', {
+ width: 1,
+ height: 18,
+ anchorX: 0,
+ anchorY: 1
+});
+expBarFill.tint = 0x8bc34a;
+expBarBg.addChild(expBarFill);
+expBarFill.x = -200;
+expBarFill.y = -1;
+var expText = new Text2('XP: 0/10', {
+ size: 40,
+ fill: 0xFFFFFF
+});
+expText.anchor.set(0.5, 1);
+LK.gui.bottom.addChild(expText);
+expText.y = -50;
// Initialize player
player = game.addChild(new Player());
player.x = 1024;
player.y = 1366;
@@ -331,20 +399,81 @@
enemies.push(enemy);
game.addChild(enemy);
}
function showUpgradeChoice() {
+ if (upgradePanelVisible) return;
upgradePanelVisible = true;
- // This is a simplified upgrade system - in a full implementation
- // you would show upgrade options and pause the game
- // For now, just give a random upgrade
- var upgradeType = Math.floor(Math.random() * 3);
- if (upgradeType === 0) {
- player.damage += 5;
- } else if (upgradeType === 1) {
- player.speed += 0.5;
- } else {
- player.attackSpeed += 0.2;
+ // Create upgrade panel background
+ upgradePanel = LK.getAsset('experienceOrb', {
+ width: 800,
+ height: 600,
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ upgradePanel.tint = 0x000000;
+ upgradePanel.alpha = 0.8;
+ LK.gui.center.addChild(upgradePanel);
+ // Create title
+ var title = new Text2('LEVEL UP!', {
+ size: 80,
+ fill: 0xFFD700
+ });
+ title.anchor.set(0.5, 0.5);
+ upgradePanel.addChild(title);
+ title.y = -200;
+ // Select 3 random upgrades
+ var availableUpgrades = Object.keys(upgradeTypes);
+ var selectedUpgrades = [];
+ for (var i = 0; i < 3; i++) {
+ var randomIndex = Math.floor(Math.random() * availableUpgrades.length);
+ selectedUpgrades.push(availableUpgrades[randomIndex]);
+ availableUpgrades.splice(randomIndex, 1);
}
+ // Create upgrade buttons
+ upgradeOptions = [];
+ for (var i = 0; i < selectedUpgrades.length; i++) {
+ var upgradeKey = selectedUpgrades[i];
+ var upgrade = upgradeTypes[upgradeKey];
+ var button = LK.getAsset('experienceOrb', {
+ width: 200,
+ height: 120,
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ button.tint = 0x4CAF50;
+ button.upgradeKey = upgradeKey;
+ upgradePanel.addChild(button);
+ button.x = (i - 1) * 250;
+ button.y = 50;
+ var buttonText = new Text2(upgrade.name, {
+ size: 40,
+ fill: 0xFFFFFF
+ });
+ buttonText.anchor.set(0.5, 0.3);
+ button.addChild(buttonText);
+ var descText = new Text2(upgrade.description, {
+ size: 25,
+ fill: 0xCCCCCC
+ });
+ descText.anchor.set(0.5, 0.8);
+ button.addChild(descText);
+ // Add button functionality
+ button.down = function (x, y, obj) {
+ selectUpgrade(obj.upgradeKey);
+ };
+ upgradeOptions.push(button);
+ }
+}
+function selectUpgrade(upgradeKey) {
+ if (!upgradePanelVisible) return;
+ // Apply the upgrade
+ upgradeTypes[upgradeKey].apply();
+ // Remove upgrade panel
+ if (upgradePanel) {
+ upgradePanel.destroy();
+ upgradePanel = null;
+ }
+ upgradeOptions = [];
upgradePanelVisible = false;
}
// Touch controls
var dragStarted = false;
@@ -427,8 +556,12 @@
healthBar.setText('Health: ' + player.health + '/' + player.maxHealth);
levelText.setText('Level: ' + player.level);
waveText.setText('Wave: ' + waveNumber);
gemsText.setText('Gems: ' + gemsCollected);
+ // Update experience bar
+ var expPercentage = player.experience / player.experienceToNext;
+ expBarFill.width = 398 * expPercentage;
+ expText.setText('XP: ' + player.experience + '/' + player.experienceToNext);
// Save gems to storage
storage.gemsCollected = gemsCollected;
// Keep player within bounds
player.x = Math.max(40, Math.min(2008, player.x));