User prompt
Let's give the area for controlling. It should be separated from gameplay area
User prompt
Please fix the bug: 'ReferenceError: timerTxt is not defined' in or related to this line: 'timerTxt.setText(t.toFixed(1));' Line Number: 286
User prompt
No need any timer and level will be ended when the target finished. The target for level 1 - 10, level 2 - 15 and the others will be increased 5 more than previous level's target. Let's power ups: fire rate/ fire style (Double triple like that) and shield. Let's turn off mouse control and adjust control button and place lane for them. For background change it for jungle
User prompt
Let's add the controller left and right. And change hero figure to Tank and enemy to soldier
User prompt
game ends when kill 10 enemy
User prompt
howcan u add move
Code edit (1 edits merged)
Please save this source code
User prompt
Pistol Road: Run & Survive
Initial prompt
i want to creat rpg game there are enemies try to stop me to reach end i have pistol to shoot them mai character has 3 lives if they reach me one of them delete enemy drop fast 2 sec length of road 20 sec
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // Enemy class var Enemy = Container.expand(function () { var self = Container.call(this); // Randomly choose between enemy_1 and enemy_2 var enemyAsset = Math.random() < 0.5 ? 'enemy' : 'Enemy_2'; var enemySprite = self.attachAsset(enemyAsset, { anchorX: 0.5, anchorY: 0.5 }); self.width = enemySprite.width; self.height = enemySprite.height; self.speed = 5; // Decreased speed for level 1 self.alive = true; self.lane = 0; // Track which lane this enemy is in (0-4) self.update = function () { var effectiveSpeed = self.speed; if (enemySpeedUpgrade) { effectiveSpeed *= 0.7; // 30% slower } self.y += effectiveSpeed * gameSpeed; if (self.y > GAMEPLAY_AREA_BOTTOM) { self.alpha = 0; } else { self.alpha = 1; } }; return self; }); // FireRateBoost drop var FireRateBoost = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('speedBoost', { anchorX: 0.5, anchorY: 0.5, color: 0x00bfff // blue }); self.width = sprite.width; self.height = sprite.height; self.speed = 10; self.type = 'firerate'; self.update = function () { self.y += self.speed * gameSpeed; }; return self; }); // FireStyleBoost drop var FireStyleBoost = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('speedBoost', { anchorX: 0.5, anchorY: 0.5, color: 0xff8800 // orange }); self.width = sprite.width; self.height = sprite.height; self.speed = 10; self.type = 'firestyle'; self.update = function () { self.y += self.speed * gameSpeed; }; return self; }); // Hero class var Hero = Container.expand(function () { var self = Container.call(this); var heroSprite = self.attachAsset('hero', { anchorX: 0.5, anchorY: 0.5 }); self.width = heroSprite.width; self.height = heroSprite.height; self.shootCooldown = 0; // frames until next allowed shot // Shoot method self.shoot = function () { if (self.shootCooldown > 0 || isReloading || bulletCount <= 0) return; // Calculate bullets to fire based on fire style var bulletsToFire = fireStyle; if (bulletCount < bulletsToFire) { startReload(); return; } // Fire style: 1=single, 2=double, 3=triple if (fireStyle === 1) { var bullet = new HeroBullet(); bullet.x = self.x; bullet.y = self.y - self.height / 2 - bullet.height / 2; heroBullets.push(bullet); game.addChild(bullet); } else if (fireStyle === 2) { for (var i = -1; i <= 1; i += 2) { var bullet = new HeroBullet(); bullet.x = self.x + i * 40; bullet.y = self.y - self.height / 2 - bullet.height / 2; heroBullets.push(bullet); game.addChild(bullet); } } else if (fireStyle === 3) { for (var i = -1; i <= 1; i++) { var bullet = new HeroBullet(); bullet.x = self.x + i * 40; bullet.y = self.y - self.height / 2 - bullet.height / 2; heroBullets.push(bullet); game.addChild(bullet); } } // Consume bullets bulletCount -= bulletsToFire; updateBulletDisplay(); // Remove bullet display elements based on bullets fired for (var k = 0; k < bulletsToFire; k++) { if (bulletTxt.children.length > 0) { var bulletElement = bulletTxt.children[bulletTxt.children.length - 1]; bulletTxt.removeChild(bulletElement); } } // Check if we need to reload if (bulletCount <= 0) { startReload(); } if (fireRateActive) { self.shootCooldown = 4; // much faster } else { self.shootCooldown = 20; // slower normal fire rate } LK.getSound('shoot').play(); }; // Called every tick self.update = function () { if (self.shootCooldown > 0) self.shootCooldown--; }; return self; }); // Hero bullet class var HeroBullet = Container.expand(function () { var self = Container.call(this); var bulletSprite = self.attachAsset('heroBullet', { anchorX: 0.5, anchorY: 0.5 }); // Apply bullet size upgrade if (bulletSizeUpgrade) { bulletSprite.scaleX = 1.5; bulletSprite.scaleY = 1.5; } self.width = bulletSprite.width; self.height = bulletSprite.height; self.speed = -36; // Upwards self.update = function () { self.y += self.speed; }; return self; }); // Market window class var MarketWindow = Container.expand(function () { var self = Container.call(this); // Semi-transparent background - larger market window var bg = LK.getAsset('centerCircle', { anchorX: 0.5, anchorY: 0.5, width: 1800, height: 1400, color: 0x000000 }); bg.alpha = 0.8; self.addChild(bg); // Market title var title = new Text2('MARKET', { size: 80, fill: 0xffffff }); title.anchor.set(0.5, 0.5); title.x = 0; title.y = -400; self.addChild(title); // Bullet size upgrade button - larger for better touch var bulletUpgradeBtn = LK.getAsset('marketButton', { anchorX: 0.5, anchorY: 0.5, color: 0x00ff00, scaleX: 2.0, scaleY: 1.5 }); bulletUpgradeBtn.x = 0; bulletUpgradeBtn.y = -200; self.addChild(bulletUpgradeBtn); var bulletUpgradeText = new Text2('BIGGER BULLETS\n50 Coins', { size: 40, fill: 0xffffff }); bulletUpgradeText.anchor.set(0.5, 0.5); bulletUpgradeText.x = 0; bulletUpgradeText.y = -200; self.addChild(bulletUpgradeText); // Enemy speed slower upgrade button - larger for better touch var speedUpgradeBtn = LK.getAsset('marketButton', { anchorX: 0.5, anchorY: 0.5, color: 0xff6600, scaleX: 2.0, scaleY: 1.5 }); speedUpgradeBtn.x = 0; speedUpgradeBtn.y = 0; self.addChild(speedUpgradeBtn); var speedUpgradeText = new Text2('SLOWER ENEMIES\n75 Coins', { size: 40, fill: 0xffffff }); speedUpgradeText.anchor.set(0.5, 0.5); speedUpgradeText.x = 0; speedUpgradeText.y = 0; self.addChild(speedUpgradeText); // Back button - larger for better touch var backBtn = LK.getAsset('marketButton', { anchorX: 0.5, anchorY: 0.5, color: 0xff0000, scaleX: 2.0, scaleY: 1.5 }); backBtn.x = 0; backBtn.y = 300; self.addChild(backBtn); var backText = new Text2('BACK', { size: 50, fill: 0xffffff }); backText.anchor.set(0.5, 0.5); backText.x = 0; backText.y = 300; self.addChild(backText); // Store button references for hit detection self.bulletUpgradeBtn = bulletUpgradeBtn; self.speedUpgradeBtn = speedUpgradeBtn; self.backBtn = backBtn; return self; }); // ShieldBoost drop var ShieldBoost = Container.expand(function () { var self = Container.call(this); var sprite = self.attachAsset('speedBoost', { anchorX: 0.5, anchorY: 0.5, color: 0x00ffea // cyan }); self.width = sprite.width; self.height = sprite.height; self.speed = 10; self.type = 'shield'; self.update = function () { self.y += self.speed * gameSpeed; }; return self; }); // Speed boost drop class var SpeedBoost = Container.expand(function () { var self = Container.call(this); var boostSprite = self.attachAsset('speedBoost', { anchorX: 0.5, anchorY: 0.5 }); self.width = boostSprite.width; self.height = boostSprite.height; self.speed = 10; self.type = 'speed'; // default type self.update = function () { self.y += self.speed * gameSpeed; }; return self; }); // Warning asset that appears on idle player var WarningAsset = Container.expand(function () { var self = Container.call(this); var warningSprite = self.attachAsset('laneholding', { anchorX: 0.5, anchorY: 0.5 }); self.width = warningSprite.width; self.height = warningSprite.height; self.lifeTimer = 2000; // 2 seconds self.update = function () { self.lifeTimer -= 1000 / 60; // Decrease by frame time if (self.lifeTimer <= 0) { // Check if damage cooldown is active if (damageCooldown <= 0) { // Damage player lives--; updateLivesDisplay(); LK.getSound('hit').play(); LK.effects.flashObject(hero, 0xff0000, 400); // Start damage cooldown damageCooldown = damageCooldownDuration; // Check game over if (lives <= 0) { finished = true; LK.showGameOver(); return; } } // Remove warning asset regardless of whether damage was dealt if (warningAsset) { warningAsset.destroy(); warningAsset = null; } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x111111 }); /**** * Game Code ****/ // --- Global variables --- // Hero (player) // Hero bullet // Enemy // Speed boost drop // Road (background) // Sound effects // Music // soldier: gray box // tank: green box // jungle green var hero; var heroBullets = []; var enemies = []; var boosts = []; var lives = 3; var gameTime = 0; // ms var gameDuration = 20000; // 20 seconds var gameSpeed = 1; // Multiplier, affected by boosts var boostActive = false; var boostTimer = 0; // Power-up states var fireRateActive = false; var fireRateTimer = 0; var fireStyle = 1; // 1: single, 2: double, 3: triple var fireStyleTimer = 0; var shieldActive = false; var shieldTimer = 0; var lastEnemySpawnTick = 0; var enemySpawnInterval = 48; // frames (0.8s at 60fps) var score = 0; var finished = false; var level = 1; var killTarget = 10; // Level 1: 10, Level 2: 15, Level 3: 20, etc. // Lane idle detection var lastLane = 2; // Track last lane position var laneIdleTimer = 0; // Timer for staying in same lane var idleThreshold = 3000; // 3 seconds in milliseconds var warningAsset = null; // Asset that appears on player var damageCooldown = 0; // Cooldown timer after taking damage var damageCooldownDuration = 5000; // 5 seconds cooldown after damage // Lane holding kill tracking var laneHoldingTimer = 0; // Timer for consecutive kills in same lane var laneHoldingThreshold = 3000; // 3 seconds in milliseconds var lastKillLane = -1; // Track lane of last kill (-1 means no previous kill) // Coin system var coins = storage.coins || 0; // Market system var gamePaused = false; var marketWindowVisible = false; var marketWindow = null; var marketInactivityTimer = 0; var marketInactivityTimeout = 5000; // 5 seconds in milliseconds // Upgrade states var bulletSizeUpgrade = false; var enemySpeedUpgrade = false; // Bullet system var bulletCount = 5; // Current bullets available var maxBullets = 5; // Maximum bullets in magazine var isReloading = false; var reloadTimer = 0; var reloadDuration = 2000; // 2 seconds in milliseconds function updateKillTarget() { if (level === 1) { killTarget = 10; } else if (level === 2) { killTarget = 15; } else { killTarget = 15 + (level - 2) * 5; } } updateKillTarget(); // --- Define gameplay and control areas --- var CONTROL_AREA_HEIGHT = 400; // px reserved for controller at bottom var GAMEPLAY_AREA_TOP = 0; var GAMEPLAY_AREA_BOTTOM = 2732 - CONTROL_AREA_HEIGHT; // --- Define 5 lanes --- var LANE_COUNT = 5; var ROAD_WIDTH = 900; var LANE_WIDTH = ROAD_WIDTH / LANE_COUNT; // 180px per lane var ROAD_LEFT = 2048 / 2 - ROAD_WIDTH / 2; var currentLane = 2; // Start hero in middle lane (0-4) // Get lane center X position function getLaneX(laneIndex) { return ROAD_LEFT + (laneIndex + 0.5) * LANE_WIDTH; } // Get lane index from X position function getLaneFromX(x) { var relativeX = x - ROAD_LEFT; var laneIndex = Math.floor(relativeX / LANE_WIDTH); return Math.max(0, Math.min(LANE_COUNT - 1, laneIndex)); } // --- Controller area background (army green) --- var controllerBg = LK.getAsset('centerCircle', { anchorX: 0, anchorY: 0, width: 2048, height: CONTROL_AREA_HEIGHT, color: 0x4B5320, // army green x: 0, y: 2732 - CONTROL_AREA_HEIGHT }); game.addChild(controllerBg); // --- Road background (restricted to end at controller line) --- var road = LK.getAsset('road', { anchorX: 0.5, anchorY: 0, width: 900, height: 2732 - CONTROL_AREA_HEIGHT // End at controller line }); road.x = 2048 / 2; road.y = 0; game.addChild(road); // --- Hero --- hero = new Hero(); hero.x = getLaneX(currentLane); hero.y = GAMEPLAY_AREA_BOTTOM - 150; game.addChild(hero); // --- GUI: Lives (Heart Icons) --- var heartIcons = []; for (var i = 0; i < 3; i++) { var heart = LK.getAsset('heart', { anchorX: 0.5, anchorY: 0.5 }); heart.x = 150 + i * 100; // Space hearts 100px apart heart.y = 60; heartIcons.push(heart); LK.gui.top.addChild(heart); } // --- GUI: Level/Target --- var levelTxt = new Text2('Level 1', { size: 90, fill: 0xFFFFFF }); levelTxt.anchor.set(0, 0); LK.gui.topLeft.addChild(levelTxt); var targetTxt = new Text2('Target: 10', { size: 90, fill: 0xFFFFFF }); targetTxt.anchor.set(0.5, 0); LK.gui.bottom.addChild(targetTxt); function updateLevelDisplay() { levelTxt.setText('Level ' + level); targetTxt.setText('Target: ' + killTarget); } // --- GUI: Score --- var scoreTxt = new Text2('0', { size: 90, fill: 0xFFE066 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // --- GUI: Coins --- var coinContainer = new Container(); var coinTxt = new Text2(coins, { size: 50, fill: 0x000000 }); coinTxt.anchor.set(0.5, 0.5); var coinIcon = LK.getAsset('coin', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); coinIcon.x = 0; coinIcon.y = 0; coinTxt.x = 0; coinTxt.y = 0; coinContainer.addChild(coinIcon); coinContainer.addChild(coinTxt); coinContainer.x = -100; coinContainer.y = 120; LK.gui.topRight.addChild(coinContainer); // --- GUI: Bullet Count and Reloading --- var bulletTxt = LK.getAsset('bulletCount', { anchorX: 1.0, anchorY: 0 }); LK.gui.topRight.addChild(bulletTxt); var reloadingTxt = LK.getAsset('reloadingText', { anchorX: 0.5, anchorY: 0.5 }); reloadingTxt.alpha = 0; LK.gui.center.addChild(reloadingTxt); // --- Music --- LK.playMusic('bgmusic'); // --- Helper: Update GUI --- function updateLivesDisplay() { for (var i = 0; i < heartIcons.length; i++) { heartIcons[i].alpha = i < lives ? 1 : 0.2; // Show full hearts for remaining lives, dim for lost lives } } function updateTimerDisplay() { // Timer GUI removed, nothing to update } function updateScoreDisplay() { scoreTxt.setText(score); } function updateBulletDisplay() { // Clear existing bullet display elements while (bulletTxt.children.length > 0) { bulletTxt.removeChild(bulletTxt.children[0]); } // Add bullet elements based on current bullet count for (var i = 0; i < bulletCount; i++) { var bulletElement = LK.getAsset('heroBullet', { anchorX: 0.5, anchorY: 0.5 }); bulletElement.x = -30 - i * 25; // Position bullets horizontally bulletElement.y = 0; bulletTxt.addChild(bulletElement); } } function updateCoinDisplay() { coinTxt.setText(coins); storage.coins = coins; // Save to persistent storage } function startReload() { if (!isReloading && bulletCount < maxBullets) { isReloading = true; reloadTimer = reloadDuration; reloadingTxt.alpha = 1; tween(reloadingTxt, { alpha: 0 }, { duration: reloadDuration, onFinish: function onFinish() { bulletCount = maxBullets; isReloading = false; updateBulletDisplay(); } }); } } // --- Touch controls: Move hero, shoot --- var dragHero = false; var leftArrowBtn, rightArrowBtn, fireBtn; var leftBtnPressed = false; var rightBtnPressed = false; var fireBtnPressed = false; var controllerBtnSize = 200; // Size for arrow buttons var fireBtnSize = 250; // Size for fire button // Move hero to specific lane function moveHeroToLane(newLane) { currentLane = Math.max(0, Math.min(LANE_COUNT - 1, newLane)); hero.x = getLaneX(currentLane); } // Controller buttons (left arrow, right arrow, fire) leftArrowBtn = LK.getAsset('leftArrow', { anchorX: 0.5, anchorY: 0.5 }); rightArrowBtn = LK.getAsset('rightArrow', { anchorX: 0.5, anchorY: 0.5 }); fireBtn = LK.getAsset('fireButton', { anchorX: 0.5, anchorY: 0.5 }); leftArrowBtn.alpha = 1.0; rightArrowBtn.alpha = 1.0; fireBtn.alpha = 1.0; // Place buttons side-by-side in the control area with better left-to-right spacing leftArrowBtn.x = 300; leftArrowBtn.y = 2732 - CONTROL_AREA_HEIGHT / 2; rightArrowBtn.x = 600; rightArrowBtn.y = 2732 - CONTROL_AREA_HEIGHT / 2; fireBtn.x = 1600; fireBtn.y = 2732 - CONTROL_AREA_HEIGHT / 2; // Make sure controllerBg is below buttons (already added above) // Add controller buttons to game layer to make them visible game.addChild(leftArrowBtn); game.addChild(rightArrowBtn); game.addChild(fireBtn); // Market button var marketBtn = LK.getAsset('marketButton', { anchorX: 0.5, anchorY: 0.5 }); marketBtn.x = 1000; marketBtn.y = 2732 - CONTROL_AREA_HEIGHT / 2; marketBtn.alpha = 0.8; game.addChild(marketBtn); // Market text label var marketLabel = new Text2('MARKET', { size: 30, fill: 0xffffff }); marketLabel.anchor.set(0.5, 0.5); marketLabel.x = marketBtn.x; marketLabel.y = marketBtn.y; game.addChild(marketLabel); // Make buttons more visible with higher alpha leftArrowBtn.alpha = 0.8; rightArrowBtn.alpha = 0.8; fireBtn.alpha = 0.8; // Helper: check if point is inside button function isInsideBtn(btn, x, y) { // Account for button scaling and provide generous touch areas var baseWidth = btn.width || 150; var baseHeight = btn.height || 80; var scaleX = btn.scaleX || 1; var scaleY = btn.scaleY || 1; var touchWidth = baseWidth * scaleX * 1.2; // 20% extra touch area var touchHeight = baseHeight * scaleY * 1.2; // 20% extra touch area return x >= btn.x - touchWidth / 2 && x <= btn.x + touchWidth / 2 && y >= btn.y - touchHeight / 2 && y <= btn.y + touchHeight / 2; } function showMarket() { if (!marketWindowVisible) { gamePaused = true; marketWindowVisible = true; marketInactivityTimer = 0; // Reset timer when market opens // Create and show market window marketWindow = new MarketWindow(); marketWindow.x = 2048 / 2; marketWindow.y = 2732 / 2; game.addChild(marketWindow); } } function hideMarket() { if (marketWindowVisible) { gamePaused = false; marketWindowVisible = false; // Remove market window if (marketWindow) { marketWindow.destroy(); marketWindow = null; } } } game.down = function (x, y, obj) { // If touch is on left arrow, set pressed if (isInsideBtn(leftArrowBtn, x, y)) { leftBtnPressed = true; leftArrowBtn.alpha = 0.9; return; } // If touch is on right arrow, set pressed if (isInsideBtn(rightArrowBtn, x, y)) { rightBtnPressed = true; rightArrowBtn.alpha = 0.9; return; } // If touch is on fire button, set pressed and shoot if (isInsideBtn(fireBtn, x, y)) { fireBtnPressed = true; fireBtn.alpha = 0.9; hero.shoot(); return; } // If touch is on market button, show market if (isInsideBtn(marketBtn, x, y)) { marketBtn.alpha = 0.9; showMarket(); return; } // Handle market window interactions if (marketWindowVisible && marketWindow) { // Reset inactivity timer on any interaction with market window marketInactivityTimer = 0; // Convert to market window local coordinates var localPos = marketWindow.toLocal({ x: x, y: y }); // Check bullet upgrade button - use global coordinates var bulletBtnGlobalPos = marketWindow.toGlobal(marketWindow.bulletUpgradeBtn.position); if (isInsideBtn({ x: bulletBtnGlobalPos.x, y: bulletBtnGlobalPos.y, width: marketWindow.bulletUpgradeBtn.width, height: marketWindow.bulletUpgradeBtn.height }, x, y)) { if (coins >= 50 && !bulletSizeUpgrade) { coins -= 50; bulletSizeUpgrade = true; updateCoinDisplay(); LK.effects.flashScreen(0x00ff00, 300); } return; } // Check enemy speed upgrade button - use global coordinates var speedBtnGlobalPos = marketWindow.toGlobal(marketWindow.speedUpgradeBtn.position); if (isInsideBtn({ x: speedBtnGlobalPos.x, y: speedBtnGlobalPos.y, width: marketWindow.speedUpgradeBtn.width, height: marketWindow.speedUpgradeBtn.height }, x, y)) { if (coins >= 75 && !enemySpeedUpgrade) { coins -= 75; enemySpeedUpgrade = true; updateCoinDisplay(); LK.effects.flashScreen(0x00ff00, 300); } return; } // Check back button - use global coordinates var backBtnGlobalPos = marketWindow.toGlobal(marketWindow.backBtn.position); if (isInsideBtn({ x: backBtnGlobalPos.x, y: backBtnGlobalPos.y, width: marketWindow.backBtn.width, height: marketWindow.backBtn.height }, x, y)) { hideMarket(); return; } } }; game.move = function (x, y, obj) { // Drag control disabled // If finger moves off controller, release if (!isInsideBtn(leftArrowBtn, x, y)) { leftBtnPressed = false; leftArrowBtn.alpha = 0.8; } if (!isInsideBtn(rightArrowBtn, x, y)) { rightBtnPressed = false; rightArrowBtn.alpha = 0.8; } if (!isInsideBtn(fireBtn, x, y)) { fireBtnPressed = false; fireBtn.alpha = 0.8; } if (!isInsideBtn(marketBtn, x, y)) { marketBtn.alpha = 0.8; } }; game.up = function (x, y, obj) { leftBtnPressed = false; rightBtnPressed = false; fireBtnPressed = false; leftArrowBtn.alpha = 0.8; rightArrowBtn.alpha = 0.8; fireBtn.alpha = 0.8; marketBtn.alpha = 0.8; }; // --- Enemy spawn logic --- function spawnEnemy() { // Enemies spawn randomly in one of the 5 lanes var enemy = new Enemy(); var enemyLane = Math.floor(Math.random() * LANE_COUNT); enemy.x = getLaneX(enemyLane); enemy.lane = enemyLane; // Set the enemy's lane // Spawn at the top of the gameplay area, not above the road enemy.y = GAMEPLAY_AREA_TOP - enemy.height / 2 - 10; // Randomize speed a bit enemy.speed = 6 + Math.random() * 3; enemies.push(enemy); game.addChild(enemy); } // --- Boost logic --- function spawnBoost(x, y) { // Randomly choose power-up type var r = Math.random(); var boost; if (r < 0.25) { boost = new SpeedBoost(); } else if (r < 0.5) { boost = new FireRateBoost(); } else if (r < 0.75) { boost = new FireStyleBoost(); } else { boost = new ShieldBoost(); } boost.x = x; boost.y = y; boosts.push(boost); game.addChild(boost); } // --- Game update --- game.update = function () { // Handle market inactivity timer if (marketWindowVisible) { marketInactivityTimer += 1000 / 60; // Increase by frame time if (marketInactivityTimer >= marketInactivityTimeout) { // Auto-close market after 5 seconds of inactivity tween(marketWindow, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { hideMarket(); } }); marketInactivityTimer = 0; // Reset timer to prevent multiple triggers } return; // Don't continue with game updates when paused } if (finished) return; // --- Controller movement --- if (leftBtnPressed) { moveHeroToLane(currentLane - 1); leftBtnPressed = false; // Single lane move per press leftArrowBtn.alpha = 0.8; } if (rightBtnPressed) { moveHeroToLane(currentLane + 1); rightBtnPressed = false; // Single lane move per press rightArrowBtn.alpha = 0.8; } // --- Update damage cooldown --- if (damageCooldown > 0) { damageCooldown -= 1000 / 60; // Decrease by frame time } // --- Lane idle detection --- if (currentLane === lastLane) { // Only increase idle timer if not in damage cooldown if (damageCooldown <= 0) { laneIdleTimer += 1000 / 60; // Increase by frame time (milliseconds) } // If player has been idle for 3 seconds and no warning exists and not in cooldown if (laneIdleTimer >= idleThreshold && !warningAsset && damageCooldown <= 0) { warningAsset = new WarningAsset(); warningAsset.x = hero.x; warningAsset.y = hero.y - 100; // Position above hero game.addChild(warningAsset); } } else { // Player moved to different lane, reset timer laneIdleTimer = 0; lastLane = currentLane; // Remove warning if it exists if (warningAsset) { warningAsset.destroy(); warningAsset = null; } } // --- Lane holding kill timer --- if (lastKillLane === currentLane && lastKillLane !== -1) { // Player is holding in same lane where they made their last kill laneHoldingTimer += 1000 / 60; // Increase by frame time (milliseconds) // If player has been holding for 3 seconds, reset timer and start count again if (laneHoldingTimer >= laneHoldingThreshold) { laneHoldingTimer = 0; // Reset timer to 0 and start count again } } else if (currentLane !== lastKillLane && lastKillLane !== -1) { // Player moved away from kill lane, reset holding timer laneHoldingTimer = 0; } // --- Update warning asset --- if (warningAsset) { warningAsset.update(); // Keep warning positioned above hero warningAsset.x = hero.x; warningAsset.y = hero.y - 100; } // --- Timer --- // (Timer removed, no time-based win/lose condition) updateTimerDisplay(); // --- Win condition --- // (Handled by kill target below) // --- Boost logic --- if (boostActive) { boostTimer -= 1000 / 60; if (boostTimer <= 0) { boostActive = false; gameSpeed = 1; } } if (fireRateActive) { fireRateTimer -= 1000 / 60; if (fireRateTimer <= 0) { fireRateActive = false; } } if (fireStyle > 1) { fireStyleTimer -= 1000 / 60; if (fireStyleTimer <= 0) { fireStyle = 1; } } if (shieldActive) { shieldTimer -= 1000 / 60; if (shieldTimer <= 0) { shieldActive = false; } } // --- Enemy spawn --- if (LK.ticks - lastEnemySpawnTick >= enemySpawnInterval) { spawnEnemy(); lastEnemySpawnTick = LK.ticks; } // --- Update hero --- hero.update(); // --- Update hero bullets --- for (var i = heroBullets.length - 1; i >= 0; i--) { var b = heroBullets[i]; b.update(); // Remove if off screen if (b.y < -b.height) { b.destroy(); heroBullets.splice(i, 1); } } // --- Update enemies --- for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; e.update(); // Remove if off screen if (e.y > 2732 + e.height) { e.destroy(); enemies.splice(i, 1); continue; } // Check collision with hero - only if enemy is in same lane if (e.alive && e.lane === currentLane && e.intersects(hero)) { if (shieldActive) { // Ignore hit, just destroy enemy LK.effects.flashObject(hero, 0x00ffea, 200); e.alive = false; e.destroy(); enemies.splice(i, 1); continue; } // Lose a life, destroy enemy lives--; updateLivesDisplay(); LK.getSound('hit').play(); LK.effects.flashObject(hero, 0xff0000, 400); e.alive = false; e.destroy(); enemies.splice(i, 1); // Game over? if (lives <= 0) { finished = true; LK.showGameOver(); return; } continue; } // Check collision with hero bullets for (var j = heroBullets.length - 1; j >= 0; j--) { var b = heroBullets[j]; if (e.alive && b.intersects(e)) { // Enemy down e.alive = false; e.destroy(); enemies.splice(i, 1); b.destroy(); heroBullets.splice(j, 1); score++; coins++; // Award 1 coin per enemy kill updateScoreDisplay(); updateCoinDisplay(); LK.getSound('enemyDown').play(); // Lane holding kill detection if (lastKillLane === currentLane) { // Player killed enemy in same lane as previous kill, reset timer laneHoldingTimer = 0; } else { // Player switched lanes between kills, start new count laneHoldingTimer = 0; lastKillLane = currentLane; } // Win if enough enemies killed for this level if (score >= killTarget) { // Prepare for next level level++; updateKillTarget(); updateLevelDisplay(); // Reset score for next level score = 0; updateScoreDisplay(); // Restore full health when level ends lives = 3; updateLivesDisplay(); // Increase enemy spawn rate for higher difficulty enemySpawnInterval = Math.max(20, enemySpawnInterval - 3); // Flash screen to indicate level complete LK.effects.flashScreen(0x00ff00, 500); } // Chance to drop boost (30%) if (Math.random() < 0.3) { spawnBoost(e.x, e.y); } break; } } } // --- Update boosts --- for (var i = boosts.length - 1; i >= 0; i--) { var boost = boosts[i]; boost.update(); // Remove if off screen if (boost.y > 2732 + boost.height) { boost.destroy(); boosts.splice(i, 1); continue; } // Check collision with hero if (boost.intersects(hero)) { if (boost.type === 'speed') { boostActive = true; boostTimer = 2000; // 2 seconds gameSpeed = 2.2; LK.getSound('boost').play(); LK.effects.flashObject(hero, 0x44e07b, 400); } else if (boost.type === 'firerate') { fireRateActive = true; fireRateTimer = 4000; // 4 seconds LK.effects.flashObject(hero, 0x00bfff, 400); } else if (boost.type === 'firestyle') { fireStyle = Math.min(3, fireStyle + 1); // double, then triple fireStyleTimer = 4000; // 4 seconds LK.effects.flashObject(hero, 0xff8800, 400); } else if (boost.type === 'shield') { shieldActive = true; shieldTimer = 4000; // 4 seconds LK.effects.flashObject(hero, 0x00ffea, 400); } boost.destroy(); boosts.splice(i, 1); } } }; // --- Initial GUI update --- updateLivesDisplay(); updateScoreDisplay(); updateLevelDisplay(); updateBulletDisplay(); updateCoinDisplay();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Enemy class
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Randomly choose between enemy_1 and enemy_2
var enemyAsset = Math.random() < 0.5 ? 'enemy' : 'Enemy_2';
var enemySprite = self.attachAsset(enemyAsset, {
anchorX: 0.5,
anchorY: 0.5
});
self.width = enemySprite.width;
self.height = enemySprite.height;
self.speed = 5; // Decreased speed for level 1
self.alive = true;
self.lane = 0; // Track which lane this enemy is in (0-4)
self.update = function () {
var effectiveSpeed = self.speed;
if (enemySpeedUpgrade) {
effectiveSpeed *= 0.7; // 30% slower
}
self.y += effectiveSpeed * gameSpeed;
if (self.y > GAMEPLAY_AREA_BOTTOM) {
self.alpha = 0;
} else {
self.alpha = 1;
}
};
return self;
});
// FireRateBoost drop
var FireRateBoost = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('speedBoost', {
anchorX: 0.5,
anchorY: 0.5,
color: 0x00bfff // blue
});
self.width = sprite.width;
self.height = sprite.height;
self.speed = 10;
self.type = 'firerate';
self.update = function () {
self.y += self.speed * gameSpeed;
};
return self;
});
// FireStyleBoost drop
var FireStyleBoost = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('speedBoost', {
anchorX: 0.5,
anchorY: 0.5,
color: 0xff8800 // orange
});
self.width = sprite.width;
self.height = sprite.height;
self.speed = 10;
self.type = 'firestyle';
self.update = function () {
self.y += self.speed * gameSpeed;
};
return self;
});
// Hero class
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroSprite = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = heroSprite.width;
self.height = heroSprite.height;
self.shootCooldown = 0; // frames until next allowed shot
// Shoot method
self.shoot = function () {
if (self.shootCooldown > 0 || isReloading || bulletCount <= 0) return;
// Calculate bullets to fire based on fire style
var bulletsToFire = fireStyle;
if (bulletCount < bulletsToFire) {
startReload();
return;
}
// Fire style: 1=single, 2=double, 3=triple
if (fireStyle === 1) {
var bullet = new HeroBullet();
bullet.x = self.x;
bullet.y = self.y - self.height / 2 - bullet.height / 2;
heroBullets.push(bullet);
game.addChild(bullet);
} else if (fireStyle === 2) {
for (var i = -1; i <= 1; i += 2) {
var bullet = new HeroBullet();
bullet.x = self.x + i * 40;
bullet.y = self.y - self.height / 2 - bullet.height / 2;
heroBullets.push(bullet);
game.addChild(bullet);
}
} else if (fireStyle === 3) {
for (var i = -1; i <= 1; i++) {
var bullet = new HeroBullet();
bullet.x = self.x + i * 40;
bullet.y = self.y - self.height / 2 - bullet.height / 2;
heroBullets.push(bullet);
game.addChild(bullet);
}
}
// Consume bullets
bulletCount -= bulletsToFire;
updateBulletDisplay();
// Remove bullet display elements based on bullets fired
for (var k = 0; k < bulletsToFire; k++) {
if (bulletTxt.children.length > 0) {
var bulletElement = bulletTxt.children[bulletTxt.children.length - 1];
bulletTxt.removeChild(bulletElement);
}
}
// Check if we need to reload
if (bulletCount <= 0) {
startReload();
}
if (fireRateActive) {
self.shootCooldown = 4; // much faster
} else {
self.shootCooldown = 20; // slower normal fire rate
}
LK.getSound('shoot').play();
};
// Called every tick
self.update = function () {
if (self.shootCooldown > 0) self.shootCooldown--;
};
return self;
});
// Hero bullet class
var HeroBullet = Container.expand(function () {
var self = Container.call(this);
var bulletSprite = self.attachAsset('heroBullet', {
anchorX: 0.5,
anchorY: 0.5
});
// Apply bullet size upgrade
if (bulletSizeUpgrade) {
bulletSprite.scaleX = 1.5;
bulletSprite.scaleY = 1.5;
}
self.width = bulletSprite.width;
self.height = bulletSprite.height;
self.speed = -36; // Upwards
self.update = function () {
self.y += self.speed;
};
return self;
});
// Market window class
var MarketWindow = Container.expand(function () {
var self = Container.call(this);
// Semi-transparent background - larger market window
var bg = LK.getAsset('centerCircle', {
anchorX: 0.5,
anchorY: 0.5,
width: 1800,
height: 1400,
color: 0x000000
});
bg.alpha = 0.8;
self.addChild(bg);
// Market title
var title = new Text2('MARKET', {
size: 80,
fill: 0xffffff
});
title.anchor.set(0.5, 0.5);
title.x = 0;
title.y = -400;
self.addChild(title);
// Bullet size upgrade button - larger for better touch
var bulletUpgradeBtn = LK.getAsset('marketButton', {
anchorX: 0.5,
anchorY: 0.5,
color: 0x00ff00,
scaleX: 2.0,
scaleY: 1.5
});
bulletUpgradeBtn.x = 0;
bulletUpgradeBtn.y = -200;
self.addChild(bulletUpgradeBtn);
var bulletUpgradeText = new Text2('BIGGER BULLETS\n50 Coins', {
size: 40,
fill: 0xffffff
});
bulletUpgradeText.anchor.set(0.5, 0.5);
bulletUpgradeText.x = 0;
bulletUpgradeText.y = -200;
self.addChild(bulletUpgradeText);
// Enemy speed slower upgrade button - larger for better touch
var speedUpgradeBtn = LK.getAsset('marketButton', {
anchorX: 0.5,
anchorY: 0.5,
color: 0xff6600,
scaleX: 2.0,
scaleY: 1.5
});
speedUpgradeBtn.x = 0;
speedUpgradeBtn.y = 0;
self.addChild(speedUpgradeBtn);
var speedUpgradeText = new Text2('SLOWER ENEMIES\n75 Coins', {
size: 40,
fill: 0xffffff
});
speedUpgradeText.anchor.set(0.5, 0.5);
speedUpgradeText.x = 0;
speedUpgradeText.y = 0;
self.addChild(speedUpgradeText);
// Back button - larger for better touch
var backBtn = LK.getAsset('marketButton', {
anchorX: 0.5,
anchorY: 0.5,
color: 0xff0000,
scaleX: 2.0,
scaleY: 1.5
});
backBtn.x = 0;
backBtn.y = 300;
self.addChild(backBtn);
var backText = new Text2('BACK', {
size: 50,
fill: 0xffffff
});
backText.anchor.set(0.5, 0.5);
backText.x = 0;
backText.y = 300;
self.addChild(backText);
// Store button references for hit detection
self.bulletUpgradeBtn = bulletUpgradeBtn;
self.speedUpgradeBtn = speedUpgradeBtn;
self.backBtn = backBtn;
return self;
});
// ShieldBoost drop
var ShieldBoost = Container.expand(function () {
var self = Container.call(this);
var sprite = self.attachAsset('speedBoost', {
anchorX: 0.5,
anchorY: 0.5,
color: 0x00ffea // cyan
});
self.width = sprite.width;
self.height = sprite.height;
self.speed = 10;
self.type = 'shield';
self.update = function () {
self.y += self.speed * gameSpeed;
};
return self;
});
// Speed boost drop class
var SpeedBoost = Container.expand(function () {
var self = Container.call(this);
var boostSprite = self.attachAsset('speedBoost', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = boostSprite.width;
self.height = boostSprite.height;
self.speed = 10;
self.type = 'speed'; // default type
self.update = function () {
self.y += self.speed * gameSpeed;
};
return self;
});
// Warning asset that appears on idle player
var WarningAsset = Container.expand(function () {
var self = Container.call(this);
var warningSprite = self.attachAsset('laneholding', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = warningSprite.width;
self.height = warningSprite.height;
self.lifeTimer = 2000; // 2 seconds
self.update = function () {
self.lifeTimer -= 1000 / 60; // Decrease by frame time
if (self.lifeTimer <= 0) {
// Check if damage cooldown is active
if (damageCooldown <= 0) {
// Damage player
lives--;
updateLivesDisplay();
LK.getSound('hit').play();
LK.effects.flashObject(hero, 0xff0000, 400);
// Start damage cooldown
damageCooldown = damageCooldownDuration;
// Check game over
if (lives <= 0) {
finished = true;
LK.showGameOver();
return;
}
}
// Remove warning asset regardless of whether damage was dealt
if (warningAsset) {
warningAsset.destroy();
warningAsset = null;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111111
});
/****
* Game Code
****/
// --- Global variables ---
// Hero (player)
// Hero bullet
// Enemy
// Speed boost drop
// Road (background)
// Sound effects
// Music
// soldier: gray box
// tank: green box
// jungle green
var hero;
var heroBullets = [];
var enemies = [];
var boosts = [];
var lives = 3;
var gameTime = 0; // ms
var gameDuration = 20000; // 20 seconds
var gameSpeed = 1; // Multiplier, affected by boosts
var boostActive = false;
var boostTimer = 0;
// Power-up states
var fireRateActive = false;
var fireRateTimer = 0;
var fireStyle = 1; // 1: single, 2: double, 3: triple
var fireStyleTimer = 0;
var shieldActive = false;
var shieldTimer = 0;
var lastEnemySpawnTick = 0;
var enemySpawnInterval = 48; // frames (0.8s at 60fps)
var score = 0;
var finished = false;
var level = 1;
var killTarget = 10; // Level 1: 10, Level 2: 15, Level 3: 20, etc.
// Lane idle detection
var lastLane = 2; // Track last lane position
var laneIdleTimer = 0; // Timer for staying in same lane
var idleThreshold = 3000; // 3 seconds in milliseconds
var warningAsset = null; // Asset that appears on player
var damageCooldown = 0; // Cooldown timer after taking damage
var damageCooldownDuration = 5000; // 5 seconds cooldown after damage
// Lane holding kill tracking
var laneHoldingTimer = 0; // Timer for consecutive kills in same lane
var laneHoldingThreshold = 3000; // 3 seconds in milliseconds
var lastKillLane = -1; // Track lane of last kill (-1 means no previous kill)
// Coin system
var coins = storage.coins || 0;
// Market system
var gamePaused = false;
var marketWindowVisible = false;
var marketWindow = null;
var marketInactivityTimer = 0;
var marketInactivityTimeout = 5000; // 5 seconds in milliseconds
// Upgrade states
var bulletSizeUpgrade = false;
var enemySpeedUpgrade = false;
// Bullet system
var bulletCount = 5; // Current bullets available
var maxBullets = 5; // Maximum bullets in magazine
var isReloading = false;
var reloadTimer = 0;
var reloadDuration = 2000; // 2 seconds in milliseconds
function updateKillTarget() {
if (level === 1) {
killTarget = 10;
} else if (level === 2) {
killTarget = 15;
} else {
killTarget = 15 + (level - 2) * 5;
}
}
updateKillTarget();
// --- Define gameplay and control areas ---
var CONTROL_AREA_HEIGHT = 400; // px reserved for controller at bottom
var GAMEPLAY_AREA_TOP = 0;
var GAMEPLAY_AREA_BOTTOM = 2732 - CONTROL_AREA_HEIGHT;
// --- Define 5 lanes ---
var LANE_COUNT = 5;
var ROAD_WIDTH = 900;
var LANE_WIDTH = ROAD_WIDTH / LANE_COUNT; // 180px per lane
var ROAD_LEFT = 2048 / 2 - ROAD_WIDTH / 2;
var currentLane = 2; // Start hero in middle lane (0-4)
// Get lane center X position
function getLaneX(laneIndex) {
return ROAD_LEFT + (laneIndex + 0.5) * LANE_WIDTH;
}
// Get lane index from X position
function getLaneFromX(x) {
var relativeX = x - ROAD_LEFT;
var laneIndex = Math.floor(relativeX / LANE_WIDTH);
return Math.max(0, Math.min(LANE_COUNT - 1, laneIndex));
}
// --- Controller area background (army green) ---
var controllerBg = LK.getAsset('centerCircle', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: CONTROL_AREA_HEIGHT,
color: 0x4B5320,
// army green
x: 0,
y: 2732 - CONTROL_AREA_HEIGHT
});
game.addChild(controllerBg);
// --- Road background (restricted to end at controller line) ---
var road = LK.getAsset('road', {
anchorX: 0.5,
anchorY: 0,
width: 900,
height: 2732 - CONTROL_AREA_HEIGHT // End at controller line
});
road.x = 2048 / 2;
road.y = 0;
game.addChild(road);
// --- Hero ---
hero = new Hero();
hero.x = getLaneX(currentLane);
hero.y = GAMEPLAY_AREA_BOTTOM - 150;
game.addChild(hero);
// --- GUI: Lives (Heart Icons) ---
var heartIcons = [];
for (var i = 0; i < 3; i++) {
var heart = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
heart.x = 150 + i * 100; // Space hearts 100px apart
heart.y = 60;
heartIcons.push(heart);
LK.gui.top.addChild(heart);
}
// --- GUI: Level/Target ---
var levelTxt = new Text2('Level 1', {
size: 90,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0);
LK.gui.topLeft.addChild(levelTxt);
var targetTxt = new Text2('Target: 10', {
size: 90,
fill: 0xFFFFFF
});
targetTxt.anchor.set(0.5, 0);
LK.gui.bottom.addChild(targetTxt);
function updateLevelDisplay() {
levelTxt.setText('Level ' + level);
targetTxt.setText('Target: ' + killTarget);
}
// --- GUI: Score ---
var scoreTxt = new Text2('0', {
size: 90,
fill: 0xFFE066
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
// --- GUI: Coins ---
var coinContainer = new Container();
var coinTxt = new Text2(coins, {
size: 50,
fill: 0x000000
});
coinTxt.anchor.set(0.5, 0.5);
var coinIcon = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
coinIcon.x = 0;
coinIcon.y = 0;
coinTxt.x = 0;
coinTxt.y = 0;
coinContainer.addChild(coinIcon);
coinContainer.addChild(coinTxt);
coinContainer.x = -100;
coinContainer.y = 120;
LK.gui.topRight.addChild(coinContainer);
// --- GUI: Bullet Count and Reloading ---
var bulletTxt = LK.getAsset('bulletCount', {
anchorX: 1.0,
anchorY: 0
});
LK.gui.topRight.addChild(bulletTxt);
var reloadingTxt = LK.getAsset('reloadingText', {
anchorX: 0.5,
anchorY: 0.5
});
reloadingTxt.alpha = 0;
LK.gui.center.addChild(reloadingTxt);
// --- Music ---
LK.playMusic('bgmusic');
// --- Helper: Update GUI ---
function updateLivesDisplay() {
for (var i = 0; i < heartIcons.length; i++) {
heartIcons[i].alpha = i < lives ? 1 : 0.2; // Show full hearts for remaining lives, dim for lost lives
}
}
function updateTimerDisplay() {
// Timer GUI removed, nothing to update
}
function updateScoreDisplay() {
scoreTxt.setText(score);
}
function updateBulletDisplay() {
// Clear existing bullet display elements
while (bulletTxt.children.length > 0) {
bulletTxt.removeChild(bulletTxt.children[0]);
}
// Add bullet elements based on current bullet count
for (var i = 0; i < bulletCount; i++) {
var bulletElement = LK.getAsset('heroBullet', {
anchorX: 0.5,
anchorY: 0.5
});
bulletElement.x = -30 - i * 25; // Position bullets horizontally
bulletElement.y = 0;
bulletTxt.addChild(bulletElement);
}
}
function updateCoinDisplay() {
coinTxt.setText(coins);
storage.coins = coins; // Save to persistent storage
}
function startReload() {
if (!isReloading && bulletCount < maxBullets) {
isReloading = true;
reloadTimer = reloadDuration;
reloadingTxt.alpha = 1;
tween(reloadingTxt, {
alpha: 0
}, {
duration: reloadDuration,
onFinish: function onFinish() {
bulletCount = maxBullets;
isReloading = false;
updateBulletDisplay();
}
});
}
}
// --- Touch controls: Move hero, shoot ---
var dragHero = false;
var leftArrowBtn, rightArrowBtn, fireBtn;
var leftBtnPressed = false;
var rightBtnPressed = false;
var fireBtnPressed = false;
var controllerBtnSize = 200; // Size for arrow buttons
var fireBtnSize = 250; // Size for fire button
// Move hero to specific lane
function moveHeroToLane(newLane) {
currentLane = Math.max(0, Math.min(LANE_COUNT - 1, newLane));
hero.x = getLaneX(currentLane);
}
// Controller buttons (left arrow, right arrow, fire)
leftArrowBtn = LK.getAsset('leftArrow', {
anchorX: 0.5,
anchorY: 0.5
});
rightArrowBtn = LK.getAsset('rightArrow', {
anchorX: 0.5,
anchorY: 0.5
});
fireBtn = LK.getAsset('fireButton', {
anchorX: 0.5,
anchorY: 0.5
});
leftArrowBtn.alpha = 1.0;
rightArrowBtn.alpha = 1.0;
fireBtn.alpha = 1.0;
// Place buttons side-by-side in the control area with better left-to-right spacing
leftArrowBtn.x = 300;
leftArrowBtn.y = 2732 - CONTROL_AREA_HEIGHT / 2;
rightArrowBtn.x = 600;
rightArrowBtn.y = 2732 - CONTROL_AREA_HEIGHT / 2;
fireBtn.x = 1600;
fireBtn.y = 2732 - CONTROL_AREA_HEIGHT / 2;
// Make sure controllerBg is below buttons (already added above)
// Add controller buttons to game layer to make them visible
game.addChild(leftArrowBtn);
game.addChild(rightArrowBtn);
game.addChild(fireBtn);
// Market button
var marketBtn = LK.getAsset('marketButton', {
anchorX: 0.5,
anchorY: 0.5
});
marketBtn.x = 1000;
marketBtn.y = 2732 - CONTROL_AREA_HEIGHT / 2;
marketBtn.alpha = 0.8;
game.addChild(marketBtn);
// Market text label
var marketLabel = new Text2('MARKET', {
size: 30,
fill: 0xffffff
});
marketLabel.anchor.set(0.5, 0.5);
marketLabel.x = marketBtn.x;
marketLabel.y = marketBtn.y;
game.addChild(marketLabel);
// Make buttons more visible with higher alpha
leftArrowBtn.alpha = 0.8;
rightArrowBtn.alpha = 0.8;
fireBtn.alpha = 0.8;
// Helper: check if point is inside button
function isInsideBtn(btn, x, y) {
// Account for button scaling and provide generous touch areas
var baseWidth = btn.width || 150;
var baseHeight = btn.height || 80;
var scaleX = btn.scaleX || 1;
var scaleY = btn.scaleY || 1;
var touchWidth = baseWidth * scaleX * 1.2; // 20% extra touch area
var touchHeight = baseHeight * scaleY * 1.2; // 20% extra touch area
return x >= btn.x - touchWidth / 2 && x <= btn.x + touchWidth / 2 && y >= btn.y - touchHeight / 2 && y <= btn.y + touchHeight / 2;
}
function showMarket() {
if (!marketWindowVisible) {
gamePaused = true;
marketWindowVisible = true;
marketInactivityTimer = 0; // Reset timer when market opens
// Create and show market window
marketWindow = new MarketWindow();
marketWindow.x = 2048 / 2;
marketWindow.y = 2732 / 2;
game.addChild(marketWindow);
}
}
function hideMarket() {
if (marketWindowVisible) {
gamePaused = false;
marketWindowVisible = false;
// Remove market window
if (marketWindow) {
marketWindow.destroy();
marketWindow = null;
}
}
}
game.down = function (x, y, obj) {
// If touch is on left arrow, set pressed
if (isInsideBtn(leftArrowBtn, x, y)) {
leftBtnPressed = true;
leftArrowBtn.alpha = 0.9;
return;
}
// If touch is on right arrow, set pressed
if (isInsideBtn(rightArrowBtn, x, y)) {
rightBtnPressed = true;
rightArrowBtn.alpha = 0.9;
return;
}
// If touch is on fire button, set pressed and shoot
if (isInsideBtn(fireBtn, x, y)) {
fireBtnPressed = true;
fireBtn.alpha = 0.9;
hero.shoot();
return;
}
// If touch is on market button, show market
if (isInsideBtn(marketBtn, x, y)) {
marketBtn.alpha = 0.9;
showMarket();
return;
}
// Handle market window interactions
if (marketWindowVisible && marketWindow) {
// Reset inactivity timer on any interaction with market window
marketInactivityTimer = 0;
// Convert to market window local coordinates
var localPos = marketWindow.toLocal({
x: x,
y: y
});
// Check bullet upgrade button - use global coordinates
var bulletBtnGlobalPos = marketWindow.toGlobal(marketWindow.bulletUpgradeBtn.position);
if (isInsideBtn({
x: bulletBtnGlobalPos.x,
y: bulletBtnGlobalPos.y,
width: marketWindow.bulletUpgradeBtn.width,
height: marketWindow.bulletUpgradeBtn.height
}, x, y)) {
if (coins >= 50 && !bulletSizeUpgrade) {
coins -= 50;
bulletSizeUpgrade = true;
updateCoinDisplay();
LK.effects.flashScreen(0x00ff00, 300);
}
return;
}
// Check enemy speed upgrade button - use global coordinates
var speedBtnGlobalPos = marketWindow.toGlobal(marketWindow.speedUpgradeBtn.position);
if (isInsideBtn({
x: speedBtnGlobalPos.x,
y: speedBtnGlobalPos.y,
width: marketWindow.speedUpgradeBtn.width,
height: marketWindow.speedUpgradeBtn.height
}, x, y)) {
if (coins >= 75 && !enemySpeedUpgrade) {
coins -= 75;
enemySpeedUpgrade = true;
updateCoinDisplay();
LK.effects.flashScreen(0x00ff00, 300);
}
return;
}
// Check back button - use global coordinates
var backBtnGlobalPos = marketWindow.toGlobal(marketWindow.backBtn.position);
if (isInsideBtn({
x: backBtnGlobalPos.x,
y: backBtnGlobalPos.y,
width: marketWindow.backBtn.width,
height: marketWindow.backBtn.height
}, x, y)) {
hideMarket();
return;
}
}
};
game.move = function (x, y, obj) {
// Drag control disabled
// If finger moves off controller, release
if (!isInsideBtn(leftArrowBtn, x, y)) {
leftBtnPressed = false;
leftArrowBtn.alpha = 0.8;
}
if (!isInsideBtn(rightArrowBtn, x, y)) {
rightBtnPressed = false;
rightArrowBtn.alpha = 0.8;
}
if (!isInsideBtn(fireBtn, x, y)) {
fireBtnPressed = false;
fireBtn.alpha = 0.8;
}
if (!isInsideBtn(marketBtn, x, y)) {
marketBtn.alpha = 0.8;
}
};
game.up = function (x, y, obj) {
leftBtnPressed = false;
rightBtnPressed = false;
fireBtnPressed = false;
leftArrowBtn.alpha = 0.8;
rightArrowBtn.alpha = 0.8;
fireBtn.alpha = 0.8;
marketBtn.alpha = 0.8;
};
// --- Enemy spawn logic ---
function spawnEnemy() {
// Enemies spawn randomly in one of the 5 lanes
var enemy = new Enemy();
var enemyLane = Math.floor(Math.random() * LANE_COUNT);
enemy.x = getLaneX(enemyLane);
enemy.lane = enemyLane; // Set the enemy's lane
// Spawn at the top of the gameplay area, not above the road
enemy.y = GAMEPLAY_AREA_TOP - enemy.height / 2 - 10;
// Randomize speed a bit
enemy.speed = 6 + Math.random() * 3;
enemies.push(enemy);
game.addChild(enemy);
}
// --- Boost logic ---
function spawnBoost(x, y) {
// Randomly choose power-up type
var r = Math.random();
var boost;
if (r < 0.25) {
boost = new SpeedBoost();
} else if (r < 0.5) {
boost = new FireRateBoost();
} else if (r < 0.75) {
boost = new FireStyleBoost();
} else {
boost = new ShieldBoost();
}
boost.x = x;
boost.y = y;
boosts.push(boost);
game.addChild(boost);
}
// --- Game update ---
game.update = function () {
// Handle market inactivity timer
if (marketWindowVisible) {
marketInactivityTimer += 1000 / 60; // Increase by frame time
if (marketInactivityTimer >= marketInactivityTimeout) {
// Auto-close market after 5 seconds of inactivity
tween(marketWindow, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
hideMarket();
}
});
marketInactivityTimer = 0; // Reset timer to prevent multiple triggers
}
return; // Don't continue with game updates when paused
}
if (finished) return;
// --- Controller movement ---
if (leftBtnPressed) {
moveHeroToLane(currentLane - 1);
leftBtnPressed = false; // Single lane move per press
leftArrowBtn.alpha = 0.8;
}
if (rightBtnPressed) {
moveHeroToLane(currentLane + 1);
rightBtnPressed = false; // Single lane move per press
rightArrowBtn.alpha = 0.8;
}
// --- Update damage cooldown ---
if (damageCooldown > 0) {
damageCooldown -= 1000 / 60; // Decrease by frame time
}
// --- Lane idle detection ---
if (currentLane === lastLane) {
// Only increase idle timer if not in damage cooldown
if (damageCooldown <= 0) {
laneIdleTimer += 1000 / 60; // Increase by frame time (milliseconds)
}
// If player has been idle for 3 seconds and no warning exists and not in cooldown
if (laneIdleTimer >= idleThreshold && !warningAsset && damageCooldown <= 0) {
warningAsset = new WarningAsset();
warningAsset.x = hero.x;
warningAsset.y = hero.y - 100; // Position above hero
game.addChild(warningAsset);
}
} else {
// Player moved to different lane, reset timer
laneIdleTimer = 0;
lastLane = currentLane;
// Remove warning if it exists
if (warningAsset) {
warningAsset.destroy();
warningAsset = null;
}
}
// --- Lane holding kill timer ---
if (lastKillLane === currentLane && lastKillLane !== -1) {
// Player is holding in same lane where they made their last kill
laneHoldingTimer += 1000 / 60; // Increase by frame time (milliseconds)
// If player has been holding for 3 seconds, reset timer and start count again
if (laneHoldingTimer >= laneHoldingThreshold) {
laneHoldingTimer = 0; // Reset timer to 0 and start count again
}
} else if (currentLane !== lastKillLane && lastKillLane !== -1) {
// Player moved away from kill lane, reset holding timer
laneHoldingTimer = 0;
}
// --- Update warning asset ---
if (warningAsset) {
warningAsset.update();
// Keep warning positioned above hero
warningAsset.x = hero.x;
warningAsset.y = hero.y - 100;
}
// --- Timer ---
// (Timer removed, no time-based win/lose condition)
updateTimerDisplay();
// --- Win condition ---
// (Handled by kill target below)
// --- Boost logic ---
if (boostActive) {
boostTimer -= 1000 / 60;
if (boostTimer <= 0) {
boostActive = false;
gameSpeed = 1;
}
}
if (fireRateActive) {
fireRateTimer -= 1000 / 60;
if (fireRateTimer <= 0) {
fireRateActive = false;
}
}
if (fireStyle > 1) {
fireStyleTimer -= 1000 / 60;
if (fireStyleTimer <= 0) {
fireStyle = 1;
}
}
if (shieldActive) {
shieldTimer -= 1000 / 60;
if (shieldTimer <= 0) {
shieldActive = false;
}
}
// --- Enemy spawn ---
if (LK.ticks - lastEnemySpawnTick >= enemySpawnInterval) {
spawnEnemy();
lastEnemySpawnTick = LK.ticks;
}
// --- Update hero ---
hero.update();
// --- Update hero bullets ---
for (var i = heroBullets.length - 1; i >= 0; i--) {
var b = heroBullets[i];
b.update();
// Remove if off screen
if (b.y < -b.height) {
b.destroy();
heroBullets.splice(i, 1);
}
}
// --- Update enemies ---
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
e.update();
// Remove if off screen
if (e.y > 2732 + e.height) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Check collision with hero - only if enemy is in same lane
if (e.alive && e.lane === currentLane && e.intersects(hero)) {
if (shieldActive) {
// Ignore hit, just destroy enemy
LK.effects.flashObject(hero, 0x00ffea, 200);
e.alive = false;
e.destroy();
enemies.splice(i, 1);
continue;
}
// Lose a life, destroy enemy
lives--;
updateLivesDisplay();
LK.getSound('hit').play();
LK.effects.flashObject(hero, 0xff0000, 400);
e.alive = false;
e.destroy();
enemies.splice(i, 1);
// Game over?
if (lives <= 0) {
finished = true;
LK.showGameOver();
return;
}
continue;
}
// Check collision with hero bullets
for (var j = heroBullets.length - 1; j >= 0; j--) {
var b = heroBullets[j];
if (e.alive && b.intersects(e)) {
// Enemy down
e.alive = false;
e.destroy();
enemies.splice(i, 1);
b.destroy();
heroBullets.splice(j, 1);
score++;
coins++; // Award 1 coin per enemy kill
updateScoreDisplay();
updateCoinDisplay();
LK.getSound('enemyDown').play();
// Lane holding kill detection
if (lastKillLane === currentLane) {
// Player killed enemy in same lane as previous kill, reset timer
laneHoldingTimer = 0;
} else {
// Player switched lanes between kills, start new count
laneHoldingTimer = 0;
lastKillLane = currentLane;
}
// Win if enough enemies killed for this level
if (score >= killTarget) {
// Prepare for next level
level++;
updateKillTarget();
updateLevelDisplay();
// Reset score for next level
score = 0;
updateScoreDisplay();
// Restore full health when level ends
lives = 3;
updateLivesDisplay();
// Increase enemy spawn rate for higher difficulty
enemySpawnInterval = Math.max(20, enemySpawnInterval - 3);
// Flash screen to indicate level complete
LK.effects.flashScreen(0x00ff00, 500);
}
// Chance to drop boost (30%)
if (Math.random() < 0.3) {
spawnBoost(e.x, e.y);
}
break;
}
}
}
// --- Update boosts ---
for (var i = boosts.length - 1; i >= 0; i--) {
var boost = boosts[i];
boost.update();
// Remove if off screen
if (boost.y > 2732 + boost.height) {
boost.destroy();
boosts.splice(i, 1);
continue;
}
// Check collision with hero
if (boost.intersects(hero)) {
if (boost.type === 'speed') {
boostActive = true;
boostTimer = 2000; // 2 seconds
gameSpeed = 2.2;
LK.getSound('boost').play();
LK.effects.flashObject(hero, 0x44e07b, 400);
} else if (boost.type === 'firerate') {
fireRateActive = true;
fireRateTimer = 4000; // 4 seconds
LK.effects.flashObject(hero, 0x00bfff, 400);
} else if (boost.type === 'firestyle') {
fireStyle = Math.min(3, fireStyle + 1); // double, then triple
fireStyleTimer = 4000; // 4 seconds
LK.effects.flashObject(hero, 0xff8800, 400);
} else if (boost.type === 'shield') {
shieldActive = true;
shieldTimer = 4000; // 4 seconds
LK.effects.flashObject(hero, 0x00ffea, 400);
}
boost.destroy();
boosts.splice(i, 1);
}
}
};
// --- Initial GUI update ---
updateLivesDisplay();
updateScoreDisplay();
updateLevelDisplay();
updateBulletDisplay();
updateCoinDisplay();
Let's remove background and resize it bigger
Make it view from sky and change color of rifle to black and brown
Change towards to right
change it mecha-style heart for hero lives. In-Game asset. 2d. High contrast. No shadows. mechaart
fire button for tank game controller. Fire button in mecha style. In-Game asset. 2d. High contrast. No shadows
green line with army style. In-Game asset. 2d. High contrast. No shadows
make shorter horizontal wing
make it vertical
remove dollar emblem from it
exclamantation. In-Game asset. 2d. High contrast. No shadows
black market which sells weapon. In-Game asset. 2d. High contrast. No shadows
blur brown