User prompt
change the rpg sprite
User prompt
the rpg does not work when bought from shop
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'shoot')' in or related to this line: 'player.shoot(x, y);' Line Number: 1820
User prompt
change the rpg sprite and when it is shut make it have a explosion that damages everything on the screen besides the player and make the fire pistol not a starter thing and can only be optained by level ups or shops ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
make it so when a enemy is hit by the fire pistol upgrade it has a fire effect showing its working and make the shop keeper text boxes far bigger because i can bearly read what im buying and what they do ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'currentGun')' in or related to this line: 'if (isRightClickHeld && player.currentGun === 'rifle') {' Line Number: 1733
User prompt
add a cash system where every enemy you kill is one dollar and put this to the left of the hotbar and every 4 waves theres a shopkeeper and on your screen it shows different things you can buy and one thing he will always sell is the upgrade fire pistol and once you buy it from him you cant buy it again and it will get replaced by somthing random like more hp more damage or some different weapons he will also somtimes sell a RPG that has one shot and when is shot makes a huge explosion that does 40 damage but only 30 on bosses and the rpg cost 30 coins ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
make the slowdown button be on the right side and mimic the position of the parry button just on the right side like the opisite
User prompt
make the slowdown button mirror the parry button just for looks
User prompt
make the xp counter go above the hotbar and make it a bar and make each enemy give 50 xp the boss 200 xp and the blue enemys 75 xp and above the xp bar put in noticable letters XP and then make the parry button far bigger and same thing with the slowdown button
User prompt
add a button called slowdown on the right of the screen when the button is clicked it goes on cooldown for 50 seconds but the cooldown gets cut by 5 seconds per enemy killed and when the button is pressed it slows all enemys by 70% and make it so after wave 5 a perentage amount of normal enemys will turn into blue enemys and this scales over every wave ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
@upit/tween.v1 ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make a special level up that has a low chance of being a choice called fire pistol where the pistol can deal one damage over time and it stacks but it has a low chance of spawning ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
make the level up systm different make it so the 3 choices can vairy from one of these +5 max hp +2 damage +Range +1 Piercing
User prompt
make it so when the sniper is out of ammo it deletes itself from the hotbar
User prompt
make the sniper have a 40% chance to drop from normal enemys and 100% from blue enemys
User prompt
add a sniper to the game that piercies through enemys only 3 at a time tough and does 20 damage and the sniper only has 5 shots before it brakes and make the shotgun do more damage it feels to weak
User prompt
add a blue enemy that appers on wave five and will apper on every wave after that this blue enemy has 5 hp unlike the 10 hp normal enemy and he has a shotgun so if he gets close to the player he deals a lot of damage
User prompt
make the player starting hp 10
User prompt
make a bossfight happen every ten rooms the boss is big and purple and cannot be killed by the hammer and can not be stunned by the sword if the boss is parried it takes five damage the the boss has a big healthbar at the top of the screen and has 50 health and gets 50 more health every 10 rooms
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var BlueEnemy = Container.expand(function (roomLevel) { var self = Container.call(this); var enemyGraphics = self.attachAsset('blueEnemy', { anchorX: 0.5, anchorY: 0.5 }); // Set blue enemy health to 5 HP self.health = 5; self.speed = Math.min(1.5 + roomLevel * 0.2, 3.5); self.shootTimer = 0; self.shootCooldown = Math.max(90 - roomLevel * 3, 50); self.active = true; self.lastPlayerX = 0; self.lastPlayerY = 0; self.stunned = false; self.stunTimer = 0; self.isBlueEnemy = true; self.update = function () { if (!self.active || !player || isLevelingUp) return; // Handle stunning if (self.stunned) { self.stunTimer--; if (self.stunTimer <= 0) { self.stunned = false; } return; // Don't move or shoot while stunned } // Track last position for intersection detection self.lastPlayerX = player.x; self.lastPlayerY = player.y; // Move aggressively towards player for close range shotgun attack var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var optimalDistance = 120; // Very close for shotgun effectiveness var minDistance = 80; // Get very close // Apply slowdown effect if active var currentSpeed = self.speed; if (slowdownActive) { currentSpeed = self.speed * slowdownEffect; } if (distance > optimalDistance) { // Move closer aggressively self.x += dx / distance * currentSpeed; self.y += dy / distance * currentSpeed; } else if (distance < minDistance) { // Too close - back away slightly self.x -= dx / distance * currentSpeed * 0.5; self.y -= dx / distance * currentSpeed * 0.5; } // Shooting logic - shoots when close to player self.shootTimer++; if (self.shootTimer >= self.shootCooldown && distance < 200) { self.shoot(); self.shootTimer = 0; } }; self.shoot = function () { // Shotgun spread attack - fires 3 bullets in a spread var baseAngle = Math.atan2(player.y - self.y, player.x - self.x); for (var i = -1; i <= 1; i++) { var bullet = new Bullet(false); bullet.direction = baseAngle + i * 0.4; // Wide spread bullet.x = self.x; bullet.y = self.y; bullet.startX = self.x; bullet.startY = self.y; bullet.speed = 12; bullet.damage = 3; // Increased damage per pellet for stronger shotgun enemyBullets.push(bullet); game.addChild(bullet); } }; self.stun = function (duration) { self.stunned = true; self.stunTimer = duration; // Visual feedback for stunned enemy LK.effects.flashObject(self, 0xfff700, duration * 16.67); }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFFFFFF, 200); if (self.health <= 0) { self.active = false; LK.setScore(LK.getScore() + 15); // Higher score for blue enemies gainXP(75); // More XP for blue enemies gainCash(1); // Gain $1 per enemy killed LK.getSound('enemyHit').play(); // Reduce slowdown cooldown by 5 seconds (300 ticks) if (slowdownCooldown > 0) { slowdownCooldown = Math.max(0, slowdownCooldown - 300); } // 100% chance to drop sniper weapon var sniperPickup = new Pickup('gun', self.x, self.y); sniperPickup.weapon = 'sniper'; // Force it to be sniper pickups.push(sniperPickup); game.addChild(sniperPickup); // 25% chance to drop weapon pickup (higher than normal) if (Math.random() < 0.25) { spawnPickup(self.x, self.y); } // 15% chance to drop shotgun ammo if (Math.random() < 0.15) { var ammoPickup = new ShotgunAmmo(self.x, self.y); shotgunAmmoPickups.push(ammoPickup); game.addChild(ammoPickup); } } }; return self; }); var Boss = Container.expand(function (roomLevel) { var self = Container.call(this); // Create boss graphics - big and purple var bossGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 3, tint: 0x8e44ad // Purple color }); // Boss stats scale with room level self.maxHealth = 50 + Math.floor((roomLevel - 10) / 10) * 50; self.health = self.maxHealth; self.speed = Math.min(0.8 + roomLevel * 0.1, 2); self.shootTimer = 0; self.shootCooldown = Math.max(90 - roomLevel * 2, 45); self.active = true; self.isBoss = true; self.update = function () { if (!self.active || !player || isLevelingUp) return; // Move towards player but maintain distance var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var optimalDistance = 300; var minDistance = 200; // Apply slowdown effect if active var currentSpeed = self.speed; if (slowdownActive) { currentSpeed = self.speed * slowdownEffect; } if (distance < minDistance) { self.x -= dx / distance * currentSpeed; self.y -= dy / distance * currentSpeed; } else if (distance > optimalDistance + 150) { self.x += dx / distance * currentSpeed; self.y += dy / distance * currentSpeed; } // Shooting logic - bosses shoot more frequently self.shootTimer++; if (self.shootTimer >= self.shootCooldown && distance < 600) { self.shoot(); self.shootTimer = 0; } }; self.shoot = function () { // Bosses shoot 3 bullets in a spread var baseAngle = Math.atan2(player.y - self.y, player.x - self.x); for (var i = -1; i <= 1; i++) { var bullet = new Bullet(false); bullet.direction = baseAngle + i * 0.3; bullet.x = self.x; bullet.y = self.y; bullet.startX = self.x; bullet.startY = self.y; bullet.speed = 10; enemyBullets.push(bullet); game.addChild(bullet); } }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFFFFFF, 200); updateBossHealthBar(); if (self.health <= 0) { self.active = false; LK.setScore(LK.getScore() + 100); gainXP(200); gainCash(1); // Gain $1 per enemy killed LK.getSound('enemyHit').play(); // Hide boss health bar if (bossHealthBarBg) { bossHealthBarBg.alpha = 0; bossHealthBar.alpha = 0; } } }; return self; }); var Bullet = Container.expand(function (isPlayer, gunType) { var self = Container.call(this); self.isPlayer = isPlayer || false; self.gunType = gunType || 'pistol'; var bulletGraphics = self.attachAsset(self.isPlayer ? 'bullet' : 'enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); // Different bullet properties based on gun type if (self.isPlayer) { switch (self.gunType) { case 'pistol': self.speed = 12; self.damage = 5 + damageBonus; self.maxRange = 800 * rangeBonus; // Large range break; case 'rifle': self.speed = 18; self.damage = 2 + damageBonus; self.maxRange = 1000 * rangeBonus; // Large range break; case 'shotgun': self.speed = 10; self.damage = 15 + damageBonus; // Increased from 10 to 15 self.maxRange = 300; // Short range break; case 'sniper': self.speed = 25; self.damage = 20 + damageBonus; self.maxRange = 1200 * rangeBonus; // Very long range self.pierceCount = 0; // Track how many enemies pierced self.maxPierce = 3 + piercingBonus; // Can pierce through 3 + bonus enemies break; case 'rpg': self.speed = 8; self.damage = 40; // High damage self.maxRange = 600; self.isExplosive = true; break; } } else { self.speed = 8; self.damage = 1; self.maxRange = 400; } self.direction = 0; self.active = true; self.startX = 0; self.startY = 0; self.distanceTraveled = 0; self.update = function () { if (!self.active) return; self.x += Math.cos(self.direction) * self.speed; self.y += Math.sin(self.direction) * self.speed; // Track distance traveled var dx = self.x - self.startX; var dy = self.y - self.startY; self.distanceTraveled = Math.sqrt(dx * dx + dy * dy); // Check range limit if (self.maxRange && self.distanceTraveled > self.maxRange) { self.active = false; } // Check bounds if (self.x < -100 || self.x > 2148 || self.y < -100 || self.y > 2832) { self.active = false; } }; return self; }); var Enemy = Container.expand(function (roomLevel) { var self = Container.call(this); var enemyGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); // Set enemy health to 10 HP self.health = 10; self.speed = Math.min(1 + roomLevel * 0.2, 3); self.shootTimer = 0; self.shootCooldown = Math.max(120 - roomLevel * 5, 60); self.active = true; self.lastPlayerX = 0; self.lastPlayerY = 0; self.stunned = false; self.stunTimer = 0; self.update = function () { if (!self.active || !player || isLevelingUp) return; // Handle stunning if (self.stunned) { self.stunTimer--; if (self.stunTimer <= 0) { self.stunned = false; } return; // Don't move or shoot while stunned } // Track last position for intersection detection self.lastPlayerX = player.x; self.lastPlayerY = player.y; // Move to maintain optimal shooting distance var dx = player.x - self.x; var dy = player.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var optimalDistance = 250; // Preferred shooting distance var minDistance = 150; // Minimum distance to maintain // Apply slowdown effect if active var currentSpeed = self.speed; if (slowdownActive) { currentSpeed = self.speed * slowdownEffect; } if (distance < minDistance) { // Too close - back away from player self.x -= dx / distance * currentSpeed; self.y -= dy / distance * currentSpeed; } else if (distance > optimalDistance + 100) { // Too far - move closer but stop at optimal distance self.x += dx / distance * currentSpeed; self.y += dy / distance * currentSpeed; } // If between minDistance and optimalDistance+100, stay in place // Shooting logic self.shootTimer++; if (self.shootTimer >= self.shootCooldown && distance < 500) { self.shoot(); self.shootTimer = 0; } }; self.shoot = function () { var bullet = new Bullet(false); var angle = Math.atan2(player.y - self.y, player.x - self.x); bullet.direction = angle; bullet.x = self.x; bullet.y = self.y; bullet.startX = self.x; bullet.startY = self.y; enemyBullets.push(bullet); game.addChild(bullet); }; self.stun = function (duration) { self.stunned = true; self.stunTimer = duration; // Visual feedback for stunned enemy LK.effects.flashObject(self, 0xfff700, duration * 16.67); }; self.takeDamage = function (damage) { self.health -= damage; LK.effects.flashObject(self, 0xFFFFFF, 200); if (self.health <= 0) { self.active = false; LK.setScore(LK.getScore() + 10); gainXP(50); // Gain 50 XP per enemy killed gainCash(1); // Gain $1 per enemy killed LK.getSound('enemyHit').play(); // Reduce slowdown cooldown by 5 seconds (300 ticks) if (slowdownCooldown > 0) { slowdownCooldown = Math.max(0, slowdownCooldown - 300); } // 40% chance to drop sniper weapon if (Math.random() < 0.4) { var sniperPickup = new Pickup('gun', self.x, self.y); sniperPickup.weapon = 'sniper'; // Force it to be sniper pickups.push(sniperPickup); game.addChild(sniperPickup); } // 20% chance to drop weapon pickup if (Math.random() < 0.2) { spawnPickup(self.x, self.y); } // 10% chance to drop shotgun ammo if (Math.random() < 0.1) { var ammoPickup = new ShotgunAmmo(self.x, self.y); shotgunAmmoPickups.push(ammoPickup); game.addChild(ammoPickup); } } }; return self; }); var Pickup = Container.expand(function (type, x, y) { var self = Container.call(this); self.type = type; // 'gun' or 'melee' self.x = x; self.y = y; self.active = true; self.bobOffset = Math.random() * Math.PI * 2; self.startY = y; var pickupGraphics = self.attachAsset(type === 'gun' ? 'gunPickup' : 'meleePickup', { anchorX: 0.5, anchorY: 0.5 }); // Determine what weapon this pickup contains if (type === 'gun') { var guns = ['pistol', 'rifle', 'shotgun', 'sniper']; self.weapon = guns[Math.floor(Math.random() * guns.length)]; } else { var melees = ['sword', 'hammer']; self.weapon = melees[Math.floor(Math.random() * melees.length)]; } self.update = function () { if (!self.active) return; // Bobbing animation self.y = self.startY + Math.sin(LK.ticks * 0.1 + self.bobOffset) * 5; // Check pickup collision with player if (self.intersects(player)) { self.active = false; if (self.type === 'gun') { if (addWeaponToHotbar(self.weapon, 'gun')) { // Successfully added to hotbar LK.getSound('pickup').play(); LK.setScore(LK.getScore() + 5); } } else { if (addWeaponToHotbar(self.weapon, 'melee')) { // Successfully added to hotbar LK.getSound('pickup').play(); LK.setScore(LK.getScore() + 5); } } self.destroy(); } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); // Gun display var gunGraphics = self.attachAsset('pistol', { anchorX: 0, anchorY: 0.5, x: 20, y: 0 }); // Melee weapon display var meleeGraphics = self.attachAsset('sword', { anchorX: 0.5, anchorY: 1, x: -15, y: -10 }); self.health = 10; self.maxHealth = 10; self.speed = 5; self.shootCooldown = 0; self.parryCooldown = 0; self.parryWindow = 0; self.invincibilityFrames = 0; // Weapon systems self.currentGun = 'pistol'; self.currentMelee = 'sword'; // Ammo system self.shotgunAmmo = 5; self.sniperAmmo = 5; self.rpgAmmo = 0; // Gun properties self.gunStats = { pistol: { cooldown: 12, spread: 0 }, rifle: { cooldown: 8, spread: 0.1 }, shotgun: { cooldown: 30, spread: 0.3, pellets: 3 }, sniper: { cooldown: 60, spread: 0, ammo: 5 }, rpg: { cooldown: 120, spread: 0, ammo: 1 } }; // Melee properties self.meleeStats = { sword: { parryWindow: 8, cooldown: 25, effect: 'reflect', color: 0x1abc9c }, hammer: { parryWindow: 15, cooldown: 45, effect: 'shockwave', color: 0xe74c3c } }; self.shoot = function (targetX, targetY) { if (self.shootCooldown > 0) return; // Check ammo for shotgun and sniper if (self.currentGun === 'shotgun' && self.shotgunAmmo <= 0) return; if (self.currentGun === 'sniper' && self.sniperAmmo <= 0) { // Remove sniper from hotbar when out of ammo removeWeaponFromHotbar('sniper'); return; } if (self.currentGun === 'rpg' && self.rpgAmmo <= 0) { // Remove RPG from hotbar when out of ammo removeWeaponFromHotbar('rpg'); return; } var gunStat = self.gunStats[self.currentGun]; var baseAngle = Math.atan2(targetY - self.y, targetX - self.x); if (self.currentGun === 'shotgun') { // Shotgun fires multiple pellets and consumes ammo self.shotgunAmmo--; for (var i = 0; i < gunStat.pellets; i++) { var bullet = new Bullet(true, self.currentGun); bullet.direction = baseAngle + (Math.random() - 0.5) * gunStat.spread; bullet.x = self.x; bullet.y = self.y; bullet.startX = self.x; bullet.startY = self.y; playerBullets.push(bullet); game.addChild(bullet); } } else if (self.currentGun === 'sniper') { // Sniper fires single high-damage piercing bullet and consumes ammo self.sniperAmmo--; var bullet = new Bullet(true, self.currentGun); bullet.direction = baseAngle + (Math.random() - 0.5) * gunStat.spread; bullet.x = self.x; bullet.y = self.y; bullet.startX = self.x; bullet.startY = self.y; playerBullets.push(bullet); game.addChild(bullet); } else if (self.currentGun === 'rpg') { // RPG fires explosive rocket and consumes ammo self.rpgAmmo--; var bullet = new Bullet(true, self.currentGun); bullet.direction = baseAngle + (Math.random() - 0.5) * gunStat.spread; bullet.x = self.x; bullet.y = self.y; bullet.startX = self.x; bullet.startY = self.y; playerBullets.push(bullet); game.addChild(bullet); } else { var bullet = new Bullet(true, self.currentGun); bullet.direction = baseAngle + (Math.random() - 0.5) * gunStat.spread; bullet.x = self.x; bullet.y = self.y; bullet.startX = self.x; bullet.startY = self.y; playerBullets.push(bullet); game.addChild(bullet); } LK.getSound('shoot').play(); self.shootCooldown = gunStat.cooldown; }; self.parry = function () { if (self.parryCooldown > 0) return; var meleeStat = self.meleeStats[self.currentMelee]; self.parryWindow = 180; // 3 seconds at 60fps self.parryCooldown = meleeStat.cooldown; // Visual feedback var parryFX = game.attachAsset('parryEffect', { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y, alpha: 0.8, tint: meleeStat.color }); tween(parryFX, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 3000, // 3 seconds onFinish: function onFinish() { parryFX.destroy(); } }); LK.getSound('parry').play(); }; self.switchGun = function (newGun) { if (newGun && self.gunStats[newGun]) { self.currentGun = newGun; gunGraphics.destroy(); gunGraphics = self.attachAsset(newGun, { anchorX: 0, anchorY: 0.5, x: 20, y: 0 }); } }; self.switchMelee = function (newMelee) { if (newMelee && self.meleeStats[newMelee]) { self.currentMelee = newMelee; meleeGraphics.destroy(); meleeGraphics = self.attachAsset(newMelee, { anchorX: 0.5, anchorY: 1, x: -15, y: -10 }); } }; self.takeDamage = function () { self.health--; LK.effects.flashObject(self, 0xFF0000, 500); LK.getSound('playerHit').play(); if (self.health <= 0) { LK.showGameOver(); } }; self.heal = function (amount) { self.health = Math.min(self.health + amount, self.maxHealth); }; self.update = function () { if (self.shootCooldown > 0) self.shootCooldown--; if (self.parryCooldown > 0) self.parryCooldown--; if (self.invincibilityFrames > 0) self.invincibilityFrames--; if (self.parryWindow > 0) { self.parryWindow--; // Check if parry window just expired without being used if (self.parryWindow === 0) { // Parry was missed, add 2 second cooldown self.parryCooldown = 120; // 2 seconds at 60fps } } }; return self; }); var RPG = Container.expand(function (x, y) { var self = Container.call(this); self.x = x; self.y = y; self.active = true; self.bobOffset = Math.random() * Math.PI * 2; self.startY = y; var rpgGraphics = self.attachAsset('rifle', { anchorX: 0.5, anchorY: 0.5, tint: 0xe74c3c, scaleX: 1.5, scaleY: 1.5 }); self.update = function () { if (!self.active) return; // Bobbing animation self.y = self.startY + Math.sin(LK.ticks * 0.1 + self.bobOffset) * 5; // Check pickup collision with player if (self.intersects(player)) { self.active = false; if (addWeaponToHotbar('rpg', 'gun')) { LK.getSound('pickup').play(); LK.setScore(LK.getScore() + 5); } self.destroy(); } }; return self; }); var Room = Container.expand(function (level) { var self = Container.call(this); self.level = level || 1; self.enemies = []; self.pickups = []; self.cleared = false; self.door = null; self.generateRoom = function () { // Create border walls var wallSize = 60; var roomWidth = 2048; var roomHeight = 2732; var wallsX = Math.floor(roomWidth / wallSize); var wallsY = Math.floor(roomHeight / wallSize); // Top and bottom walls for (var i = 0; i < wallsX; i++) { var topWall = self.attachAsset('wall', { x: i * wallSize, y: 0 }); var bottomWall = self.attachAsset('wall', { x: i * wallSize, y: roomHeight - wallSize }); } // Left and right walls for (var j = 1; j < wallsY - 1; j++) { var leftWall = self.attachAsset('wall', { x: 0, y: j * wallSize }); var rightWall = self.attachAsset('wall', { x: roomWidth - wallSize, y: j * wallSize }); } // Check if this is a boss room (every 10 rooms) if (self.level % 10 === 0) { // Boss fight var boss = new Boss(self.level); boss.x = roomWidth / 2; boss.y = roomHeight / 3; self.enemies.push(boss); self.addChild(boss); currentBoss = boss; // Show boss health bar bossHealthBarBg.alpha = 1; bossHealthBar.alpha = 1; bossNameText.alpha = 1; updateBossHealthBar(); } else { // Generate regular enemies var enemyCount = Math.min(2 + self.level, 8); var blueEnemyCount = 0; // Blue enemies appear starting from wave 5 if (self.level >= 5) { blueEnemyCount = Math.min(1 + Math.floor((self.level - 5) / 3), 3); // 1-3 blue enemies enemyCount = Math.max(1, enemyCount - blueEnemyCount); // Reduce regular enemies to maintain balance } // After wave 5, convert percentage of normal enemies to blue enemies var blueConversionRate = 0; if (self.level > 5) { // Start at 10% after wave 5, increase by 5% each wave, cap at 50% blueConversionRate = Math.min(0.1 + (self.level - 5) * 0.05, 0.5); } // Spawn regular enemies for (var k = 0; k < enemyCount; k++) { // Check if this enemy should be converted to blue if (self.level > 5 && Math.random() < blueConversionRate) { var convertedBlueEnemy = new BlueEnemy(self.level); convertedBlueEnemy.x = 200 + Math.random() * (roomWidth - 400); convertedBlueEnemy.y = 200 + Math.random() * (roomHeight - 400); self.enemies.push(convertedBlueEnemy); self.addChild(convertedBlueEnemy); } else { var enemy = new Enemy(self.level); enemy.x = 200 + Math.random() * (roomWidth - 400); enemy.y = 200 + Math.random() * (roomHeight - 400); self.enemies.push(enemy); self.addChild(enemy); } } // Spawn blue enemies starting from wave 5 for (var b = 0; b < blueEnemyCount; b++) { var blueEnemy = new BlueEnemy(self.level); blueEnemy.x = 200 + Math.random() * (roomWidth - 400); blueEnemy.y = 200 + Math.random() * (roomHeight - 400); self.enemies.push(blueEnemy); self.addChild(blueEnemy); } } // Create exit door (initially locked) self.door = self.attachAsset('door', { anchorX: 0.5, anchorY: 0.5, x: roomWidth / 2, y: wallSize / 2, tint: 0xc0392b }); // Spawn shotgun pickup in room spawnShotgunPickup(); }; self.checkCleared = function () { var activeEnemies = 0; for (var i = 0; i < self.enemies.length; i++) { if (self.enemies[i].active) { activeEnemies++; } } if (activeEnemies === 0 && !self.cleared) { self.cleared = true; // Unlock door if (self.door) { tween(self.door, { tint: 0x27ae60 }, { duration: 500 }); } } return self.cleared; }; return self; }); var ShotgunAmmo = Container.expand(function (x, y) { var self = Container.call(this); self.x = x; self.y = y; self.active = true; self.bobOffset = Math.random() * Math.PI * 2; self.startY = y; var ammoGraphics = self.attachAsset('shotgunAmmo', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { if (!self.active) return; // Bobbing animation self.y = self.startY + Math.sin(LK.ticks * 0.1 + self.bobOffset) * 5; // Check pickup collision with player if (self.intersects(player)) { self.active = false; player.shotgunAmmo += 3; // Give 3 ammo directly LK.getSound('pickup').play(); LK.setScore(LK.getScore() + 2); updateUI(); // Update ammo display self.destroy(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a1a }); /**** * Game Code ****/ var player; var currentRoom; var playerBullets = []; var enemyBullets = []; var pickups = []; var shotgunAmmoPickups = []; var roomLevel = 1; var dragStartX = 0; var dragStartY = 0; var isDragging = false; var draggedWeapon = null; var draggedSlot = null; var trashIcon = null; var selectedWeapon = null; var selectedSlot = null; // XP System var playerXP = 0; var playerLevel = 1; var xpToNextLevel = 100; var isLevelingUp = false; var levelUpChoices = null; var damageBonus = 0; var rangeBonus = 1; var piercingBonus = 0; // Cash System var playerCash = storage.playerCash || 0; var isShopOpen = false; var shopChoices = null; var hasUpgradePistol = storage.hasUpgradePistol || false; // UI Elements var scoreText = new Text2('Score: 0', { size: 40, fill: '#ffffff' }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); // Health bar instead of text var healthBarBg = LK.gui.topRight.attachAsset('wall', { width: 150, height: 20, anchorX: 1, anchorY: 0, tint: 0x7f8c8d }); var healthBar = LK.gui.topRight.attachAsset('wall', { width: 150, height: 20, anchorX: 1, anchorY: 0, tint: 0xe74c3c }); var roomText = new Text2('Room: 1', { size: 30, fill: '#3498db' }); roomText.anchor.set(0, 0); roomText.x = 120; LK.gui.topLeft.addChild(roomText); var gunText = new Text2('Gun: Pistol', { size: 28, fill: '#f39c12' }); gunText.anchor.set(0, 1); LK.gui.bottomLeft.addChild(gunText); var meleeText = new Text2('Melee: Sword', { size: 28, fill: '#9b59b6' }); meleeText.anchor.set(1, 1); LK.gui.bottomRight.addChild(meleeText); // Hotbar system var hotbar = new Container(); hotbar.x = 1024; // Center of screen hotbar.y = 2600; // Bottom of screen game.addChild(hotbar); // Cash counter to the left of hotbar var cashText = new Text2('$0', { size: 40, fill: '#2ecc71' }); cashText.anchor.set(1, 0.5); cashText.x = -300; // Position to the left of hotbar cashText.y = -80; hotbar.addChild(cashText); // Ammo counter to the left of hotbar var ammoText = new Text2('Shotgun: 5', { size: 32, fill: '#f39c12' }); ammoText.anchor.set(1, 0.5); ammoText.x = -300; // Position to the left of hotbar ammoText.y = 0; hotbar.addChild(ammoText); // Sniper ammo counter var sniperAmmoText = new Text2('Sniper: 5', { size: 32, fill: '#8b4513' }); sniperAmmoText.anchor.set(1, 0.5); sniperAmmoText.x = -300; sniperAmmoText.y = -40; hotbar.addChild(sniperAmmoText); // XP label above hotbar var xpLabel = new Text2('XP', { size: 48, fill: '#ffffff' }); xpLabel.anchor.set(0.5, 1); xpLabel.x = 0; xpLabel.y = -150; hotbar.addChild(xpLabel); // XP bar background above hotbar var xpBarBg = hotbar.attachAsset('wall', { width: 400, height: 30, anchorX: 0.5, anchorY: 1, x: 0, y: -100, tint: 0x7f8c8d }); // XP bar fill above hotbar var xpBar = hotbar.attachAsset('wall', { width: 400, height: 30, anchorX: 0.5, anchorY: 1, x: 0, y: -100, tint: 0xe67e22 }); // Level display next to XP bar var levelText = new Text2('Level 1', { size: 36, fill: '#ffffff' }); levelText.anchor.set(0, 0.5); levelText.x = 220; levelText.y = -85; hotbar.addChild(levelText); // Boss health bar (initially hidden) var bossHealthBarBg = LK.gui.top.attachAsset('wall', { width: 800, height: 30, anchorX: 0.5, anchorY: 0, y: 60, tint: 0x7f8c8d, alpha: 0 }); var bossHealthBar = LK.gui.top.attachAsset('wall', { width: 800, height: 30, anchorX: 0.5, anchorY: 0, y: 60, tint: 0x8e44ad, alpha: 0 }); var bossNameText = new Text2('BOSS', { size: 36, fill: '#8e44ad' }); bossNameText.anchor.set(0.5, 1); bossNameText.y = 55; bossNameText.alpha = 0; LK.gui.top.addChild(bossNameText); var hotbarSlots = []; var slotWidth = 80; var slotHeight = 80; var slotSpacing = 100; // Create 5 hotbar slots for (var h = 0; h < 5; h++) { var slot = hotbar.attachAsset('wall', { width: slotWidth, height: slotHeight, anchorX: 0.5, anchorY: 0.5, x: (h - 2) * slotSpacing, y: 0, tint: 0x2c3e50 }); slot.slotIndex = h; slot.weapon = null; slot.weaponType = null; // 'gun' or 'melee' // Visual weapon display slot.weaponGraphics = null; slot.down = function (x, y, obj) { if (this.weapon) { // Select weapon and light up trash selectedWeapon = this.weapon; selectedSlot = this; // Highlight the selected slot tween(this, { tint: 0x3498db }, { duration: 100 }); // Light up trash icon tween(trashIcon, { tint: 0xff6b6b }, { duration: 100 }); } }; slot.up = function (x, y, obj) { // Switch to this weapon if it's selected but not the same as current selection if (selectedWeapon && selectedSlot === this) { if (this.weaponType === 'gun') { player.switchGun(this.weapon); } else if (this.weaponType === 'melee') { player.switchMelee(this.weapon); } updateUI(); } }; hotbarSlots.push(slot); } // Create trash icon next to hotbar trashIcon = hotbar.attachAsset('trashIcon', { width: 60, height: 60, anchorX: 0.5, anchorY: 0.5, x: 350, // Position to the right of hotbar y: 0, tint: 0xc0392b }); // Add click handler to trash icon trashIcon.down = function (x, y, obj) { if (selectedWeapon && selectedSlot) { // Delete weapon from selected slot selectedSlot.weapon = null; selectedSlot.weaponType = null; if (selectedSlot.weaponGraphics) { selectedSlot.weaponGraphics.destroy(); selectedSlot.weaponGraphics = null; } // Reset slot color tween(selectedSlot, { tint: 0x2c3e50 }, { duration: 100 }); // Flash trash icon to show deletion tween(trashIcon, { tint: 0xff0000 }, { duration: 200, onFinish: function onFinish() { tween(trashIcon, { tint: 0xc0392b }, { duration: 200 }); } }); // Clear selection selectedWeapon = null; selectedSlot = null; } }; // Add visual indicator for trash (X symbol) var trashX1 = hotbar.attachAsset('wall', { width: 30, height: 4, anchorX: 0.5, anchorY: 0.5, x: 350, y: 0, rotation: Math.PI / 4, tint: 0xffffff }); var trashX2 = hotbar.attachAsset('wall', { width: 30, height: 4, anchorX: 0.5, anchorY: 0.5, x: 350, y: 0, rotation: -Math.PI / 4, tint: 0xffffff }); // Create parry button var parryButton = game.attachAsset('parryButton', { anchorX: 0.5, anchorY: 0.5, x: 200, y: 2500, scaleX: 2, scaleY: 2 }); // Create slowdown button var slowdownButton = game.attachAsset('parryButton', { anchorX: 0.5, anchorY: 0.5, x: 1848, // Mirror parry button position on right side y: 2500, // Same Y position as parry button tint: 0x3498db, scaleX: 2, scaleY: 2 }); // Slowdown system variables var slowdownCooldown = 0; var slowdownActive = false; var slowdownDuration = 0; var maxSlowdownCooldown = 3000; // 50 seconds at 60fps var slowdownEffect = 0.3; // 70% slow (30% of original speed) // Add parry button text var parryButtonText = new Text2('PARRY', { size: 48, fill: '#ffffff' }); parryButtonText.anchor.set(0.5, 0.5); parryButtonText.x = 200; parryButtonText.y = 2500; game.addChild(parryButtonText); // Add slowdown button text var slowdownButtonText = new Text2('SLOW', { size: 48, fill: '#ffffff' }); slowdownButtonText.anchor.set(0.5, 0.5); slowdownButtonText.x = 1848; slowdownButtonText.y = 2500; game.addChild(slowdownButtonText); // Add slowdown cooldown text var slowdownCooldownText = new Text2('', { size: 24, fill: '#ffffff' }); slowdownCooldownText.anchor.set(0.5, 0.5); slowdownCooldownText.x = 1848; slowdownCooldownText.y = 2400; game.addChild(slowdownCooldownText); // Add parry button click handler parryButton.down = function (x, y, obj) { player.parry(); // Visual feedback on button press tween(parryButton, { scaleX: 0.9, scaleY: 0.9, tint: 0x16a085 }, { duration: 100, onFinish: function onFinish() { tween(parryButton, { scaleX: 1, scaleY: 1, tint: 0x1abc9c }, { duration: 100 }); } }); }; // Add slowdown button click handler slowdownButton.down = function (x, y, obj) { if (slowdownCooldown <= 0 && !slowdownActive) { // Activate slowdown slowdownActive = true; slowdownDuration = 600; // 10 seconds at 60fps slowdownCooldown = maxSlowdownCooldown; // Visual feedback on button press tween(slowdownButton, { scaleX: 0.9, scaleY: 0.9, tint: 0x2980b9 }, { duration: 100, onFinish: function onFinish() { tween(slowdownButton, { scaleX: 1, scaleY: 1, tint: 0xe74c3c // Red when on cooldown }, { duration: 100 }); } }); } }; // Set initial weapons in hotbar hotbarSlots[0].weapon = 'pistol'; hotbarSlots[0].weaponType = 'gun'; hotbarSlots[0].weaponGraphics = hotbarSlots[0].attachAsset('pistol', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); hotbarSlots[1].weapon = 'sword'; hotbarSlots[1].weaponType = 'melee'; hotbarSlots[1].weaponGraphics = hotbarSlots[1].attachAsset('sword', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); hotbarSlots[2].weapon = 'rifle'; hotbarSlots[2].weaponType = 'gun'; hotbarSlots[2].weaponGraphics = hotbarSlots[2].attachAsset('rifle', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); function updateUI() { scoreText.setText('Score: ' + LK.getScore()); // Update health bar var healthPercent = player.health / player.maxHealth; healthBar.width = 150 * healthPercent; roomText.setText('Room: ' + roomLevel); gunText.setText('Gun: ' + player.currentGun.charAt(0).toUpperCase() + player.currentGun.slice(1)); meleeText.setText('Melee: ' + player.currentMelee.charAt(0).toUpperCase() + player.currentMelee.slice(1)); // Update ammo counter ammoText.setText('Shotgun: ' + player.shotgunAmmo); // Update sniper ammo counter sniperAmmoText.setText('Sniper: ' + player.sniperAmmo); // Update XP bar display var xpPercent = playerXP / xpToNextLevel; xpBar.width = 400 * xpPercent; levelText.setText('Level ' + playerLevel); // Update cash display cashText.setText('$' + playerCash); } function updateBossHealthBar() { if (currentBoss && currentBoss.active) { var healthPercent = currentBoss.health / currentBoss.maxHealth; bossHealthBar.width = 800 * healthPercent; } } function gainXP(amount) { playerXP += amount; if (playerXP >= xpToNextLevel && !isLevelingUp) { levelUp(); } } function gainCash(amount) { playerCash += amount; storage.playerCash = playerCash; } function levelUp() { isLevelingUp = true; playerLevel++; playerXP -= xpToNextLevel; xpToNextLevel = Math.floor(xpToNextLevel * 1.5); // Create level up choices overlay showLevelUpChoices(); } function showLevelUpChoices() { // Create overlay background var overlay = game.attachAsset('wall', { width: 2048, height: 2732, anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 0.8, tint: 0x000000 }); // Randomly select 3 different upgrade options var availableUpgrades = [{ id: 'hp', text: '+5 Max HP', color: 0x27ae60 }, { id: 'damage', text: '+2 Damage', color: 0xe74c3c }, { id: 'range', text: '+Range', color: 0x3498db }, { id: 'piercing', text: '+1 Piercing', color: 0xf39c12 }]; // Shuffle and pick 3 random upgrades var selectedUpgrades = []; var tempUpgrades = availableUpgrades.slice(); // Create copy for (var i = 0; i < 3; i++) { var randomIndex = Math.floor(Math.random() * tempUpgrades.length); selectedUpgrades.push(tempUpgrades[randomIndex]); tempUpgrades.splice(randomIndex, 1); } // Create choice buttons var choiceY = 1366; // Center Y var choiceSpacing = 400; var choices = []; var choiceTexts = []; for (var j = 0; j < 3; j++) { var upgrade = selectedUpgrades[j]; var xPos = 1024 + (j - 1) * choiceSpacing; var choice = game.attachAsset('wall', { width: 300, height: 100, anchorX: 0.5, anchorY: 0.5, x: xPos, y: choiceY, tint: upgrade.color }); var choiceText = new Text2(upgrade.text, { size: 36, fill: '#ffffff' }); choiceText.anchor.set(0.5, 0.5); choiceText.x = xPos; choiceText.y = choiceY; game.addChild(choiceText); // Store upgrade id on the choice button choice.upgradeId = upgrade.id; choice.down = function () { chooseLevelUpOption(this.upgradeId); }; choices.push(choice); choiceTexts.push(choiceText); } // Store references for cleanup levelUpChoices = { overlay: overlay, choices: choices, choiceTexts: choiceTexts }; } function chooseLevelUpOption(choice) { if (!isLevelingUp || !levelUpChoices) return; // Apply the chosen upgrade switch (choice) { case 'damage': damageBonus += 2; break; case 'hp': player.maxHealth += 5; player.health += 5; break; case 'range': rangeBonus += 0.2; break; case 'piercing': piercingBonus++; break; } // Clean up level up UI levelUpChoices.overlay.destroy(); for (var i = 0; i < levelUpChoices.choices.length; i++) { levelUpChoices.choices[i].destroy(); levelUpChoices.choiceTexts[i].destroy(); } levelUpChoices = null; isLevelingUp = false; updateUI(); } function spawnPickup(x, y) { var pickupType = Math.random() < 0.6 ? 'gun' : 'melee'; var pickup = new Pickup(pickupType, x, y); pickups.push(pickup); game.addChild(pickup); } function addWeaponToHotbar(weapon, weaponType) { // Find first empty slot for (var i = 0; i < hotbarSlots.length; i++) { var slot = hotbarSlots[i]; if (!slot.weapon) { slot.weapon = weapon; slot.weaponType = weaponType; // Remove old graphics if any if (slot.weaponGraphics) { slot.weaponGraphics.destroy(); } // Add new weapon graphics slot.weaponGraphics = slot.attachAsset(weapon, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); return true; } } return false; // No empty slots } function removeWeaponFromHotbar(weapon) { // Find and remove the weapon from hotbar for (var i = 0; i < hotbarSlots.length; i++) { var slot = hotbarSlots[i]; if (slot.weapon === weapon) { // If this was the current weapon, switch to pistol if (player.currentGun === weapon) { player.switchGun('pistol'); } // Clear the slot slot.weapon = null; slot.weaponType = null; if (slot.weaponGraphics) { slot.weaponGraphics.destroy(); slot.weaponGraphics = null; } // Reset slot color slot.tint = 0x2c3e50; // Clear selection if this weapon was selected if (selectedWeapon === weapon && selectedSlot === slot) { selectedWeapon = null; selectedSlot = null; } updateUI(); return true; } } return false; } // Spawn shotgun pickup in each room function spawnShotgunPickup() { var shotgunPickup = new Pickup('gun', 300 + Math.random() * 1400, 400 + Math.random() * 1800); shotgunPickup.weapon = 'shotgun'; // Force it to be shotgun pickups.push(shotgunPickup); game.addChild(shotgunPickup); } function generateNewRoom() { // Check if shop should appear (every 4 waves) if (roomLevel > 0 && roomLevel % 4 === 0 && !isShopOpen) { showShop(); return; } // Clear current room if (currentRoom) { currentRoom.destroy(); } // Clear boss reference and hide boss health bar currentBoss = null; if (bossHealthBarBg) { bossHealthBarBg.alpha = 0; bossHealthBar.alpha = 0; bossNameText.alpha = 0; } // Clear bullets and pickups for (var i = 0; i < playerBullets.length; i++) { playerBullets[i].destroy(); } for (var j = 0; j < enemyBullets.length; j++) { enemyBullets[j].destroy(); } for (var k = 0; k < pickups.length; k++) { pickups[k].destroy(); } for (var l = 0; l < shotgunAmmoPickups.length; l++) { shotgunAmmoPickups[l].destroy(); } playerBullets = []; enemyBullets = []; pickups = []; shotgunAmmoPickups = []; // Create new room roomLevel++; currentRoom = new Room(roomLevel); currentRoom.generateRoom(); game.addChild(currentRoom); // Reset player position player.x = 1024; player.y = 2400; game.addChild(player); // Regenerate HP when entering new room player.heal(1); // Heal 1 HP when entering a new room updateUI(); } // Game state management var gameStarted = false; var weaponSelectionUI = null; var currentBoss = null; // Show weapon selection screen function showWeaponSelection() { // Create overlay background var overlay = game.attachAsset('wall', { width: 2048, height: 2732, anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 0.9, tint: 0x2c3e50 }); // Title text var titleText = new Text2('Choose Your Weapon', { size: 60, fill: '#ffffff' }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 800; game.addChild(titleText); // Create weapon choice buttons var choiceY = 1366; // Center Y var choiceSpacing = 400; // Hammer choice var hammerChoice = game.attachAsset('wall', { width: 300, height: 200, anchorX: 0.5, anchorY: 0.5, x: 1024 - choiceSpacing, y: choiceY, tint: 0x8e44ad }); var hammerIcon = game.attachAsset('hammer', { anchorX: 0.5, anchorY: 0.5, x: 1024 - choiceSpacing, y: choiceY - 20, scaleX: 3, scaleY: 3 }); var hammerText = new Text2('HAMMER', { size: 32, fill: '#ffffff' }); hammerText.anchor.set(0.5, 0.5); hammerText.x = 1024 - choiceSpacing; hammerText.y = choiceY + 50; game.addChild(hammerText); var hammerDesc = new Text2('Kills enemies\nwhen parrying', { size: 24, fill: '#ecf0f1' }); hammerDesc.anchor.set(0.5, 0.5); hammerDesc.x = 1024 - choiceSpacing; hammerDesc.y = choiceY + 80; game.addChild(hammerDesc); // Sword choice var swordChoice = game.attachAsset('wall', { width: 300, height: 200, anchorX: 0.5, anchorY: 0.5, x: 1024 + choiceSpacing, y: choiceY, tint: 0xe67e22 }); var swordIcon = game.attachAsset('sword', { anchorX: 0.5, anchorY: 0.5, x: 1024 + choiceSpacing, y: choiceY - 20, scaleX: 3, scaleY: 3 }); var swordText = new Text2('SWORD', { size: 32, fill: '#ffffff' }); swordText.anchor.set(0.5, 0.5); swordText.x = 1024 + choiceSpacing; swordText.y = choiceY + 50; game.addChild(swordText); var swordDesc = new Text2('Gives invincibility\nframes when parrying', { size: 24, fill: '#ecf0f1' }); swordDesc.anchor.set(0.5, 0.5); swordDesc.x = 1024 + choiceSpacing; swordDesc.y = choiceY + 80; game.addChild(swordDesc); // Store references for cleanup weaponSelectionUI = { overlay: overlay, titleText: titleText, hammerChoice: hammerChoice, hammerIcon: hammerIcon, hammerText: hammerText, hammerDesc: hammerDesc, swordChoice: swordChoice, swordIcon: swordIcon, swordText: swordText, swordDesc: swordDesc }; // Add click handlers hammerChoice.down = function () { chooseWeapon('hammer'); }; swordChoice.down = function () { chooseWeapon('sword'); }; } // Handle weapon selection function chooseWeapon(weaponType) { if (!weaponSelectionUI) return; // Clean up weapon selection UI weaponSelectionUI.overlay.destroy(); weaponSelectionUI.titleText.destroy(); weaponSelectionUI.hammerChoice.destroy(); weaponSelectionUI.hammerIcon.destroy(); weaponSelectionUI.hammerText.destroy(); weaponSelectionUI.hammerDesc.destroy(); weaponSelectionUI.swordChoice.destroy(); weaponSelectionUI.swordIcon.destroy(); weaponSelectionUI.swordText.destroy(); weaponSelectionUI.swordDesc.destroy(); weaponSelectionUI = null; // Start the game with chosen weapon startGameWithWeapon(weaponType); } // Initialize game with chosen weapon function startGameWithWeapon(startingWeapon) { gameStarted = true; // Initialize game currentRoom = new Room(roomLevel); currentRoom.generateRoom(); game.addChild(currentRoom); player = new Player(); player.x = 1024; player.y = 2400; game.addChild(player); // Set the chosen starting weapon player.switchMelee(startingWeapon); // Update hotbar to reflect chosen weapon hotbarSlots[1].weapon = startingWeapon; hotbarSlots[1].weaponType = 'melee'; if (hotbarSlots[1].weaponGraphics) { hotbarSlots[1].weaponGraphics.destroy(); } hotbarSlots[1].weaponGraphics = hotbarSlots[1].attachAsset(startingWeapon, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); updateUI(); } // Show weapon selection at start showWeaponSelection(); // Touch controls game.down = function (x, y, obj) { isDragging = true; dragStartX = x; dragStartY = y; }; game.move = function (x, y, obj) { // Update aim position for rifle full auto if (isRightClickHeld && player.currentGun === 'rifle') { dragStartX = x; dragStartY = y; } }; game.up = function (x, y, obj) { if (isDragging && !isRightClickHeld) { var dx = x - dragStartX; var dy = y - dragStartY; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 30) { // Check if this is a potential parry (tap in bottom area of screen or near parry button) var parryButtonDistance = Math.sqrt((x - 200) * (x - 200) + (y - 2500) * (y - 2500)); if (y > 2200 || parryButtonDistance < 120) { // Tap in bottom area or near parry button - parry player.parry(); } else { // Tap elsewhere - shoot player.shoot(x, y); } } } isDragging = false; isRightClickHeld = false; rightClickDetected = false; }; // Double click variables for movement system var lastClickTime = 0; var doubleClickThreshold = 300; // milliseconds var rightClickDetected = false; var isRightClickHeld = false; var fullAutoTimer = 0; var fullAutoInterval = 8; // Fire every 8 ticks for rifle full auto // Function to handle double click movement function handleDoubleClick(x, y) { // Move player to clicked position with smooth movement var targetX = Math.max(80, Math.min(1968, x)); var targetY = Math.max(80, Math.min(2652, y)); // Use tween for smooth movement tween(player, { x: targetX, y: targetY }, { duration: 500 }); } // Override the existing down handler to detect double clicks and right clicks game.down = function (x, y, obj) { var currentTime = Date.now(); // Check for right click (simulate with touch hold detection) // We'll detect right click as a hold that lasts longer than normal tap LK.setTimeout(function () { if (isDragging && !rightClickDetected) { // This is a right click hold isRightClickHeld = true; rightClickDetected = true; fullAutoTimer = 0; } }, 200); // 200ms to detect hold if (currentTime - lastClickTime < doubleClickThreshold) { // Double click detected handleDoubleClick(x, y); lastClickTime = 0; // Reset to prevent triple click } else { // Single click - start drag isDragging = true; dragStartX = x; dragStartY = y; lastClickTime = currentTime; } }; // Main game loop game.update = function () { if (!gameStarted || !player || !currentRoom || isLevelingUp || isShopOpen) return; // Update player player.update(); // Update player bullets for (var i = playerBullets.length - 1; i >= 0; i--) { var bullet = playerBullets[i]; bullet.update(); if (!bullet.active) { bullet.destroy(); playerBullets.splice(i, 1); continue; } // Check collision with enemies for (var j = 0; j < currentRoom.enemies.length; j++) { var enemy = currentRoom.enemies[j]; if (enemy.active && bullet.intersects(enemy)) { // Handle RPG explosion if (bullet.gunType === 'rpg' && bullet.isExplosive) { // Explosion damages all enemies in range for (var exp = 0; exp < currentRoom.enemies.length; exp++) { var expEnemy = currentRoom.enemies[exp]; if (expEnemy.active) { var expDx = expEnemy.x - bullet.x; var expDy = expEnemy.y - bullet.y; var expDistance = Math.sqrt(expDx * expDx + expDy * expDy); if (expDistance < 200) { // Explosion radius var explosionDamage = expEnemy.isBoss ? 30 : 40; expEnemy.takeDamage(explosionDamage); } } } // Visual explosion effect LK.effects.flashScreen(0xff6b35, 500); bullet.active = false; bullet.destroy(); playerBullets.splice(i, 1); break; } else { enemy.takeDamage(bullet.damage); // Handle sniper piercing if (bullet.gunType === 'sniper' && bullet.pierceCount < bullet.maxPierce) { bullet.pierceCount++; // Continue through enemy, don't destroy bullet } else { bullet.active = false; bullet.destroy(); playerBullets.splice(i, 1); } break; } } } } // Update enemy bullets for (var k = enemyBullets.length - 1; k >= 0; k--) { var enemyBullet = enemyBullets[k]; enemyBullet.update(); if (!enemyBullet.active) { enemyBullet.destroy(); enemyBullets.splice(k, 1); continue; } // Check collision with player if (enemyBullet.intersects(player)) { if (player.parryWindow > 0) { // Successful parry - find the enemy that shot this bullet and stun them var bulletShooter = null; var shortestDistance = Infinity; for (var bulletCheck = 0; bulletCheck < currentRoom.enemies.length; bulletCheck++) { var checkEnemy = currentRoom.enemies[bulletCheck]; if (checkEnemy.active) { var checkDx = checkEnemy.x - enemyBullet.x; var checkDy = checkEnemy.y - enemyBullet.y; var checkDistance = Math.sqrt(checkDx * checkDx + checkDy * checkDy); if (checkDistance < shortestDistance) { shortestDistance = checkDistance; bulletShooter = checkEnemy; } } } // Stun the enemy that shot the bullet if (bulletShooter) { if (bulletShooter.isBoss) { // Boss takes 5 damage when parried, cannot be stunned or killed by parry bulletShooter.takeDamage(5); } else if (player.currentMelee === 'hammer') { // Hammer kills the enemy instead of stunning bulletShooter.takeDamage(bulletShooter.health); } else if (player.currentMelee === 'sword') { // Sword stuns enemy for 5 seconds (works on both regular and blue enemies) bulletShooter.stun(300); // Stun for 5 seconds (300 ticks at 60fps) } else { // Other weapons stun the enemy for 2 seconds bulletShooter.stun(120); // Stun for 2 seconds (120 ticks at 60fps) } } var meleeStat = player.meleeStats[player.currentMelee]; // Give sword users 5 seconds of invincibility frames if (player.currentMelee === 'sword') { player.invincibilityFrames = 300; // 5 seconds at 60fps // Visual feedback for invincibility tween(player, { alpha: 0.5 }, { duration: 5000, onFinish: function onFinish() { player.alpha = 1; } }); } if (meleeStat.effect === 'reflect') { // Reflect bullet back var reflectedBullet = new Bullet(true, 'pistol'); reflectedBullet.x = enemyBullet.x; reflectedBullet.y = enemyBullet.y; reflectedBullet.startX = enemyBullet.x; reflectedBullet.startY = enemyBullet.y; reflectedBullet.direction = enemyBullet.direction + Math.PI; reflectedBullet.speed = 15; playerBullets.push(reflectedBullet); game.addChild(reflectedBullet); } else if (meleeStat.effect === 'shockwave') { // Damage nearby enemies for (var l = 0; l < currentRoom.enemies.length; l++) { var nearbyEnemy = currentRoom.enemies[l]; var dx = nearbyEnemy.x - player.x; var dy = nearbyEnemy.y - player.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 150 && nearbyEnemy.active) { if (nearbyEnemy.isBoss) { // Boss takes 5 damage from shockwave, cannot be killed nearbyEnemy.takeDamage(5); } else if (player.currentMelee === 'hammer') { // Hammer kills nearby enemies (works on both regular and blue enemies) nearbyEnemy.takeDamage(nearbyEnemy.health); } else { // Other weapons damage nearby enemies nearbyEnemy.takeDamage(2); } } } } LK.effects.flashObject(player, meleeStat.color, 300); LK.setScore(LK.getScore() + 3); } else { // Player takes damage (unless they have invincibility frames) if (!player.invincibilityFrames || player.invincibilityFrames <= 0) { player.takeDamage(); } } enemyBullet.active = false; enemyBullet.destroy(); enemyBullets.splice(k, 1); } } // Update enemies for (var m = 0; m < currentRoom.enemies.length; m++) { var enemy = currentRoom.enemies[m]; if (enemy.active) { enemy.update(); } } // Update pickups for (var n = pickups.length - 1; n >= 0; n--) { var pickup = pickups[n]; pickup.update(); if (!pickup.active) { pickups.splice(n, 1); } } // Update shotgun ammo pickups for (var o = shotgunAmmoPickups.length - 1; o >= 0; o--) { var ammoPickup = shotgunAmmoPickups[o]; ammoPickup.update(); if (!ammoPickup.active) { shotgunAmmoPickups.splice(o, 1); } } // Check room completion if (currentRoom.checkCleared()) { // Check if player reaches door if (currentRoom.door && player.intersects(currentRoom.door)) { generateNewRoom(); } } // Reset trash icon when no weapon is selected if (!selectedWeapon && trashIcon.tint !== 0xc0392b) { tween(trashIcon, { tint: 0xc0392b }, { duration: 100 }); } // Handle aimed shooting for rifle while held if (isRightClickHeld && player.currentGun === 'rifle') { fullAutoTimer++; if (fullAutoTimer >= fullAutoInterval) { // Use current mouse/touch position for aiming var currentX = dragStartX; // This gets updated in move handler var currentY = dragStartY; // This gets updated in move handler player.shoot(currentX, currentY); fullAutoTimer = 0; } } // Update slowdown system if (slowdownCooldown > 0) { slowdownCooldown--; } if (slowdownDuration > 0) { slowdownDuration--; if (slowdownDuration <= 0) { slowdownActive = false; // Reset button color when slowdown ends tween(slowdownButton, { tint: 0x3498db }, { duration: 200 }); } } // Update slowdown button display if (slowdownCooldown > 0) { var secondsLeft = Math.ceil(slowdownCooldown / 60); slowdownCooldownText.setText(secondsLeft + 's'); slowdownButton.tint = 0xe74c3c; // Red when on cooldown } else { slowdownCooldownText.setText(''); if (!slowdownActive) { slowdownButton.tint = 0x3498db; // Blue when ready } } // Update UI periodically if (LK.ticks % 30 === 0) { updateUI(); } }; function showShop() { isShopOpen = true; // Create overlay background var overlay = game.attachAsset('wall', { width: 2048, height: 2732, anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 0.9, tint: 0x2c3e50 }); // Title text var titleText = new Text2('SHOPKEEPER', { size: 60, fill: '#f39c12' }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 600; game.addChild(titleText); // Available shop items var shopItems = []; // Always include upgrade pistol if not owned if (!hasUpgradePistol) { shopItems.push({ id: 'upgradePistol', name: 'Upgraded Pistol', price: 15, description: 'Better damage\nand fire rate' }); } // Add random items var randomItems = [{ id: 'health', name: '+5 Max HP', price: 10, description: 'Increase health' }, { id: 'damage', name: '+3 Damage', price: 12, description: 'More damage' }, { id: 'rpg', name: 'RPG Launcher', price: 30, description: '40 damage explosion\n30 on bosses' }]; // Add 2-3 random items var itemsToAdd = Math.min(3 - shopItems.length, randomItems.length); for (var i = 0; i < itemsToAdd; i++) { if (randomItems.length > 0) { var randomIndex = Math.floor(Math.random() * randomItems.length); shopItems.push(randomItems[randomIndex]); randomItems.splice(randomIndex, 1); } } // Create shop item buttons var choiceY = 1366; var choiceSpacing = 300; var choices = []; var choiceTexts = []; var choiceDescTexts = []; for (var j = 0; j < shopItems.length; j++) { var item = shopItems[j]; var xPos = 1024 + (j - Math.floor(shopItems.length / 2)) * choiceSpacing; var choice = game.attachAsset('wall', { width: 250, height: 150, anchorX: 0.5, anchorY: 0.5, x: xPos, y: choiceY, tint: playerCash >= item.price ? 0x27ae60 : 0xe74c3c }); var nameText = new Text2(item.name, { size: 24, fill: '#ffffff' }); nameText.anchor.set(0.5, 0.5); nameText.x = xPos; nameText.y = choiceY - 30; game.addChild(nameText); var priceText = new Text2('$' + item.price, { size: 32, fill: '#f39c12' }); priceText.anchor.set(0.5, 0.5); priceText.x = xPos; priceText.y = choiceY + 20; game.addChild(priceText); var descText = new Text2(item.description, { size: 18, fill: '#ecf0f1' }); descText.anchor.set(0.5, 0.5); descText.x = xPos; descText.y = choiceY + 50; game.addChild(descText); choice.shopItem = item; choice.down = function () { buyItem(this.shopItem); }; choices.push(choice); choiceTexts.push(nameText); choiceTexts.push(priceText); choiceDescTexts.push(descText); } // Continue button var continueButton = game.attachAsset('wall', { width: 200, height: 80, anchorX: 0.5, anchorY: 0.5, x: 1024, y: 2000, tint: 0x3498db }); var continueText = new Text2('CONTINUE', { size: 36, fill: '#ffffff' }); continueText.anchor.set(0.5, 0.5); continueText.x = 1024; continueText.y = 2000; game.addChild(continueText); continueButton.down = function () { closeShop(); }; // Store references for cleanup shopChoices = { overlay: overlay, titleText: titleText, choices: choices, choiceTexts: choiceTexts, choiceDescTexts: choiceDescTexts, continueButton: continueButton, continueText: continueText }; } function buyItem(item) { if (playerCash < item.price) return; playerCash -= item.price; storage.playerCash = playerCash; switch (item.id) { case 'upgradePistol': hasUpgradePistol = true; storage.hasUpgradePistol = true; // Upgrade pistol stats player.gunStats.pistol.cooldown = 8; player.gunStats.pistol.damage = 8; break; case 'health': player.maxHealth += 5; player.health += 5; break; case 'damage': damageBonus += 3; break; case 'rpg': player.rpgAmmo = 1; if (!addWeaponToHotbar('rpg', 'gun')) { // If hotbar full, give ammo anyway player.rpgAmmo = 1; } break; } updateUI(); closeShop(); } function closeShop() { if (!shopChoices) return; // Clean up shop UI shopChoices.overlay.destroy(); shopChoices.titleText.destroy(); shopChoices.continueButton.destroy(); shopChoices.continueText.destroy(); for (var i = 0; i < shopChoices.choices.length; i++) { shopChoices.choices[i].destroy(); } for (var j = 0; j < shopChoices.choiceTexts.length; j++) { shopChoices.choiceTexts[j].destroy(); } for (var k = 0; k < shopChoices.choiceDescTexts.length; k++) { shopChoices.choiceDescTexts[k].destroy(); } shopChoices = null; isShopOpen = false; // Continue to next room continueToNextRoom(); } function continueToNextRoom() { // Clear current room if (currentRoom) { currentRoom.destroy(); } // Clear boss reference and hide boss health bar currentBoss = null; if (bossHealthBarBg) { bossHealthBarBg.alpha = 0; bossHealthBar.alpha = 0; bossNameText.alpha = 0; } // Clear bullets and pickups for (var i = 0; i < playerBullets.length; i++) { playerBullets[i].destroy(); } for (var j = 0; j < enemyBullets.length; j++) { enemyBullets[j].destroy(); } for (var k = 0; k < pickups.length; k++) { pickups[k].destroy(); } for (var l = 0; l < shotgunAmmoPickups.length; l++) { shotgunAmmoPickups[l].destroy(); } playerBullets = []; enemyBullets = []; pickups = []; shotgunAmmoPickups = []; // Create new room roomLevel++; currentRoom = new Room(roomLevel); currentRoom.generateRoom(); game.addChild(currentRoom); // Reset player position player.x = 1024; player.y = 2400; game.addChild(player); // Regenerate HP when entering new room player.heal(1); updateUI(); }
===================================================================
--- original.js
+++ change.js
@@ -1,8 +1,9 @@
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
+var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
@@ -91,8 +92,9 @@
if (self.health <= 0) {
self.active = false;
LK.setScore(LK.getScore() + 15); // Higher score for blue enemies
gainXP(75); // More XP for blue enemies
+ gainCash(1); // Gain $1 per enemy killed
LK.getSound('enemyHit').play();
// Reduce slowdown cooldown by 5 seconds (300 ticks)
if (slowdownCooldown > 0) {
slowdownCooldown = Math.max(0, slowdownCooldown - 300);
@@ -183,8 +185,9 @@
if (self.health <= 0) {
self.active = false;
LK.setScore(LK.getScore() + 100);
gainXP(200);
+ gainCash(1); // Gain $1 per enemy killed
LK.getSound('enemyHit').play();
// Hide boss health bar
if (bossHealthBarBg) {
bossHealthBarBg.alpha = 0;
@@ -226,8 +229,14 @@
self.maxRange = 1200 * rangeBonus; // Very long range
self.pierceCount = 0; // Track how many enemies pierced
self.maxPierce = 3 + piercingBonus; // Can pierce through 3 + bonus enemies
break;
+ case 'rpg':
+ self.speed = 8;
+ self.damage = 40; // High damage
+ self.maxRange = 600;
+ self.isExplosive = true;
+ break;
}
} else {
self.speed = 8;
self.damage = 1;
@@ -337,8 +346,9 @@
if (self.health <= 0) {
self.active = false;
LK.setScore(LK.getScore() + 10);
gainXP(50); // Gain 50 XP per enemy killed
+ gainCash(1); // Gain $1 per enemy killed
LK.getSound('enemyHit').play();
// Reduce slowdown cooldown by 5 seconds (300 ticks)
if (slowdownCooldown > 0) {
slowdownCooldown = Math.max(0, slowdownCooldown - 300);
@@ -441,8 +451,9 @@
self.currentMelee = 'sword';
// Ammo system
self.shotgunAmmo = 5;
self.sniperAmmo = 5;
+ self.rpgAmmo = 0;
// Gun properties
self.gunStats = {
pistol: {
cooldown: 12,
@@ -460,8 +471,13 @@
sniper: {
cooldown: 60,
spread: 0,
ammo: 5
+ },
+ rpg: {
+ cooldown: 120,
+ spread: 0,
+ ammo: 1
}
};
// Melee properties
self.meleeStats = {
@@ -486,8 +502,13 @@
// Remove sniper from hotbar when out of ammo
removeWeaponFromHotbar('sniper');
return;
}
+ if (self.currentGun === 'rpg' && self.rpgAmmo <= 0) {
+ // Remove RPG from hotbar when out of ammo
+ removeWeaponFromHotbar('rpg');
+ return;
+ }
var gunStat = self.gunStats[self.currentGun];
var baseAngle = Math.atan2(targetY - self.y, targetX - self.x);
if (self.currentGun === 'shotgun') {
// Shotgun fires multiple pellets and consumes ammo
@@ -512,8 +533,19 @@
bullet.startX = self.x;
bullet.startY = self.y;
playerBullets.push(bullet);
game.addChild(bullet);
+ } else if (self.currentGun === 'rpg') {
+ // RPG fires explosive rocket and consumes ammo
+ self.rpgAmmo--;
+ var bullet = new Bullet(true, self.currentGun);
+ bullet.direction = baseAngle + (Math.random() - 0.5) * gunStat.spread;
+ bullet.x = self.x;
+ bullet.y = self.y;
+ bullet.startX = self.x;
+ bullet.startY = self.y;
+ playerBullets.push(bullet);
+ game.addChild(bullet);
} else {
var bullet = new Bullet(true, self.currentGun);
bullet.direction = baseAngle + (Math.random() - 0.5) * gunStat.spread;
bullet.x = self.x;
@@ -602,8 +634,38 @@
}
};
return self;
});
+var RPG = Container.expand(function (x, y) {
+ var self = Container.call(this);
+ self.x = x;
+ self.y = y;
+ self.active = true;
+ self.bobOffset = Math.random() * Math.PI * 2;
+ self.startY = y;
+ var rpgGraphics = self.attachAsset('rifle', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ tint: 0xe74c3c,
+ scaleX: 1.5,
+ scaleY: 1.5
+ });
+ self.update = function () {
+ if (!self.active) return;
+ // Bobbing animation
+ self.y = self.startY + Math.sin(LK.ticks * 0.1 + self.bobOffset) * 5;
+ // Check pickup collision with player
+ if (self.intersects(player)) {
+ self.active = false;
+ if (addWeaponToHotbar('rpg', 'gun')) {
+ LK.getSound('pickup').play();
+ LK.setScore(LK.getScore() + 5);
+ }
+ self.destroy();
+ }
+ };
+ return self;
+});
var Room = Container.expand(function (level) {
var self = Container.call(this);
self.level = level || 1;
self.enemies = [];
@@ -788,8 +850,13 @@
var levelUpChoices = null;
var damageBonus = 0;
var rangeBonus = 1;
var piercingBonus = 0;
+// Cash System
+var playerCash = storage.playerCash || 0;
+var isShopOpen = false;
+var shopChoices = null;
+var hasUpgradePistol = storage.hasUpgradePistol || false;
// UI Elements
var scoreText = new Text2('Score: 0', {
size: 40,
fill: '#ffffff'
@@ -834,8 +901,17 @@
var hotbar = new Container();
hotbar.x = 1024; // Center of screen
hotbar.y = 2600; // Bottom of screen
game.addChild(hotbar);
+// Cash counter to the left of hotbar
+var cashText = new Text2('$0', {
+ size: 40,
+ fill: '#2ecc71'
+});
+cashText.anchor.set(1, 0.5);
+cashText.x = -300; // Position to the left of hotbar
+cashText.y = -80;
+hotbar.addChild(cashText);
// Ammo counter to the left of hotbar
var ammoText = new Text2('Shotgun: 5', {
size: 32,
fill: '#f39c12'
@@ -1177,8 +1253,10 @@
// Update XP bar display
var xpPercent = playerXP / xpToNextLevel;
xpBar.width = 400 * xpPercent;
levelText.setText('Level ' + playerLevel);
+ // Update cash display
+ cashText.setText('$' + playerCash);
}
function updateBossHealthBar() {
if (currentBoss && currentBoss.active) {
var healthPercent = currentBoss.health / currentBoss.maxHealth;
@@ -1190,8 +1268,12 @@
if (playerXP >= xpToNextLevel && !isLevelingUp) {
levelUp();
}
}
+function gainCash(amount) {
+ playerCash += amount;
+ storage.playerCash = playerCash;
+}
function levelUp() {
isLevelingUp = true;
playerLevel++;
playerXP -= xpToNextLevel;
@@ -1370,8 +1452,13 @@
pickups.push(shotgunPickup);
game.addChild(shotgunPickup);
}
function generateNewRoom() {
+ // Check if shop should appear (every 4 waves)
+ if (roomLevel > 0 && roomLevel % 4 === 0 && !isShopOpen) {
+ showShop();
+ return;
+ }
// Clear current room
if (currentRoom) {
currentRoom.destroy();
}
@@ -1657,9 +1744,9 @@
}
};
// Main game loop
game.update = function () {
- if (!gameStarted || !player || !currentRoom || isLevelingUp) return;
+ if (!gameStarted || !player || !currentRoom || isLevelingUp || isShopOpen) return;
// Update player
player.update();
// Update player bullets
for (var i = playerBullets.length - 1; i >= 0; i--) {
@@ -1673,19 +1760,43 @@
// Check collision with enemies
for (var j = 0; j < currentRoom.enemies.length; j++) {
var enemy = currentRoom.enemies[j];
if (enemy.active && bullet.intersects(enemy)) {
- enemy.takeDamage(bullet.damage);
- // Handle sniper piercing
- if (bullet.gunType === 'sniper' && bullet.pierceCount < bullet.maxPierce) {
- bullet.pierceCount++;
- // Continue through enemy, don't destroy bullet
- } else {
+ // Handle RPG explosion
+ if (bullet.gunType === 'rpg' && bullet.isExplosive) {
+ // Explosion damages all enemies in range
+ for (var exp = 0; exp < currentRoom.enemies.length; exp++) {
+ var expEnemy = currentRoom.enemies[exp];
+ if (expEnemy.active) {
+ var expDx = expEnemy.x - bullet.x;
+ var expDy = expEnemy.y - bullet.y;
+ var expDistance = Math.sqrt(expDx * expDx + expDy * expDy);
+ if (expDistance < 200) {
+ // Explosion radius
+ var explosionDamage = expEnemy.isBoss ? 30 : 40;
+ expEnemy.takeDamage(explosionDamage);
+ }
+ }
+ }
+ // Visual explosion effect
+ LK.effects.flashScreen(0xff6b35, 500);
bullet.active = false;
bullet.destroy();
playerBullets.splice(i, 1);
+ break;
+ } else {
+ enemy.takeDamage(bullet.damage);
+ // Handle sniper piercing
+ if (bullet.gunType === 'sniper' && bullet.pierceCount < bullet.maxPierce) {
+ bullet.pierceCount++;
+ // Continue through enemy, don't destroy bullet
+ } else {
+ bullet.active = false;
+ bullet.destroy();
+ playerBullets.splice(i, 1);
+ }
+ break;
}
- break;
}
}
}
// Update enemy bullets
@@ -1869,5 +1980,240 @@
// Update UI periodically
if (LK.ticks % 30 === 0) {
updateUI();
}
-};
\ No newline at end of file
+};
+function showShop() {
+ isShopOpen = true;
+ // Create overlay background
+ var overlay = game.attachAsset('wall', {
+ width: 2048,
+ height: 2732,
+ anchorX: 0,
+ anchorY: 0,
+ x: 0,
+ y: 0,
+ alpha: 0.9,
+ tint: 0x2c3e50
+ });
+ // Title text
+ var titleText = new Text2('SHOPKEEPER', {
+ size: 60,
+ fill: '#f39c12'
+ });
+ titleText.anchor.set(0.5, 0.5);
+ titleText.x = 1024;
+ titleText.y = 600;
+ game.addChild(titleText);
+ // Available shop items
+ var shopItems = [];
+ // Always include upgrade pistol if not owned
+ if (!hasUpgradePistol) {
+ shopItems.push({
+ id: 'upgradePistol',
+ name: 'Upgraded Pistol',
+ price: 15,
+ description: 'Better damage\nand fire rate'
+ });
+ }
+ // Add random items
+ var randomItems = [{
+ id: 'health',
+ name: '+5 Max HP',
+ price: 10,
+ description: 'Increase health'
+ }, {
+ id: 'damage',
+ name: '+3 Damage',
+ price: 12,
+ description: 'More damage'
+ }, {
+ id: 'rpg',
+ name: 'RPG Launcher',
+ price: 30,
+ description: '40 damage explosion\n30 on bosses'
+ }];
+ // Add 2-3 random items
+ var itemsToAdd = Math.min(3 - shopItems.length, randomItems.length);
+ for (var i = 0; i < itemsToAdd; i++) {
+ if (randomItems.length > 0) {
+ var randomIndex = Math.floor(Math.random() * randomItems.length);
+ shopItems.push(randomItems[randomIndex]);
+ randomItems.splice(randomIndex, 1);
+ }
+ }
+ // Create shop item buttons
+ var choiceY = 1366;
+ var choiceSpacing = 300;
+ var choices = [];
+ var choiceTexts = [];
+ var choiceDescTexts = [];
+ for (var j = 0; j < shopItems.length; j++) {
+ var item = shopItems[j];
+ var xPos = 1024 + (j - Math.floor(shopItems.length / 2)) * choiceSpacing;
+ var choice = game.attachAsset('wall', {
+ width: 250,
+ height: 150,
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: xPos,
+ y: choiceY,
+ tint: playerCash >= item.price ? 0x27ae60 : 0xe74c3c
+ });
+ var nameText = new Text2(item.name, {
+ size: 24,
+ fill: '#ffffff'
+ });
+ nameText.anchor.set(0.5, 0.5);
+ nameText.x = xPos;
+ nameText.y = choiceY - 30;
+ game.addChild(nameText);
+ var priceText = new Text2('$' + item.price, {
+ size: 32,
+ fill: '#f39c12'
+ });
+ priceText.anchor.set(0.5, 0.5);
+ priceText.x = xPos;
+ priceText.y = choiceY + 20;
+ game.addChild(priceText);
+ var descText = new Text2(item.description, {
+ size: 18,
+ fill: '#ecf0f1'
+ });
+ descText.anchor.set(0.5, 0.5);
+ descText.x = xPos;
+ descText.y = choiceY + 50;
+ game.addChild(descText);
+ choice.shopItem = item;
+ choice.down = function () {
+ buyItem(this.shopItem);
+ };
+ choices.push(choice);
+ choiceTexts.push(nameText);
+ choiceTexts.push(priceText);
+ choiceDescTexts.push(descText);
+ }
+ // Continue button
+ var continueButton = game.attachAsset('wall', {
+ width: 200,
+ height: 80,
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 1024,
+ y: 2000,
+ tint: 0x3498db
+ });
+ var continueText = new Text2('CONTINUE', {
+ size: 36,
+ fill: '#ffffff'
+ });
+ continueText.anchor.set(0.5, 0.5);
+ continueText.x = 1024;
+ continueText.y = 2000;
+ game.addChild(continueText);
+ continueButton.down = function () {
+ closeShop();
+ };
+ // Store references for cleanup
+ shopChoices = {
+ overlay: overlay,
+ titleText: titleText,
+ choices: choices,
+ choiceTexts: choiceTexts,
+ choiceDescTexts: choiceDescTexts,
+ continueButton: continueButton,
+ continueText: continueText
+ };
+}
+function buyItem(item) {
+ if (playerCash < item.price) return;
+ playerCash -= item.price;
+ storage.playerCash = playerCash;
+ switch (item.id) {
+ case 'upgradePistol':
+ hasUpgradePistol = true;
+ storage.hasUpgradePistol = true;
+ // Upgrade pistol stats
+ player.gunStats.pistol.cooldown = 8;
+ player.gunStats.pistol.damage = 8;
+ break;
+ case 'health':
+ player.maxHealth += 5;
+ player.health += 5;
+ break;
+ case 'damage':
+ damageBonus += 3;
+ break;
+ case 'rpg':
+ player.rpgAmmo = 1;
+ if (!addWeaponToHotbar('rpg', 'gun')) {
+ // If hotbar full, give ammo anyway
+ player.rpgAmmo = 1;
+ }
+ break;
+ }
+ updateUI();
+ closeShop();
+}
+function closeShop() {
+ if (!shopChoices) return;
+ // Clean up shop UI
+ shopChoices.overlay.destroy();
+ shopChoices.titleText.destroy();
+ shopChoices.continueButton.destroy();
+ shopChoices.continueText.destroy();
+ for (var i = 0; i < shopChoices.choices.length; i++) {
+ shopChoices.choices[i].destroy();
+ }
+ for (var j = 0; j < shopChoices.choiceTexts.length; j++) {
+ shopChoices.choiceTexts[j].destroy();
+ }
+ for (var k = 0; k < shopChoices.choiceDescTexts.length; k++) {
+ shopChoices.choiceDescTexts[k].destroy();
+ }
+ shopChoices = null;
+ isShopOpen = false;
+ // Continue to next room
+ continueToNextRoom();
+}
+function continueToNextRoom() {
+ // Clear current room
+ if (currentRoom) {
+ currentRoom.destroy();
+ }
+ // Clear boss reference and hide boss health bar
+ currentBoss = null;
+ if (bossHealthBarBg) {
+ bossHealthBarBg.alpha = 0;
+ bossHealthBar.alpha = 0;
+ bossNameText.alpha = 0;
+ }
+ // Clear bullets and pickups
+ for (var i = 0; i < playerBullets.length; i++) {
+ playerBullets[i].destroy();
+ }
+ for (var j = 0; j < enemyBullets.length; j++) {
+ enemyBullets[j].destroy();
+ }
+ for (var k = 0; k < pickups.length; k++) {
+ pickups[k].destroy();
+ }
+ for (var l = 0; l < shotgunAmmoPickups.length; l++) {
+ shotgunAmmoPickups[l].destroy();
+ }
+ playerBullets = [];
+ enemyBullets = [];
+ pickups = [];
+ shotgunAmmoPickups = [];
+ // Create new room
+ roomLevel++;
+ currentRoom = new Room(roomLevel);
+ currentRoom.generateRoom();
+ game.addChild(currentRoom);
+ // Reset player position
+ player.x = 1024;
+ player.y = 2400;
+ game.addChild(player);
+ // Regenerate HP when entering new room
+ player.heal(1);
+ updateUI();
+}
\ No newline at end of file