User prompt
also play tap when a cell is touched, or when any other eleement is touched, like chest or the close button of the chest reward, or the shop or the next level buttons
User prompt
play victory when player defeats monster
User prompt
play monster1 sound when monster attacks
User prompt
when battle screen is over, go back to title music
User prompt
play encounter music when a player fights a monster in the battle screen
User prompt
play tap sound when inventory or stats are open or closed
User prompt
play slash sound when player touches on start game
User prompt
play gametitle music when the game title starts, and keep on playing it.
User prompt
Keep using gametitle background music when the game is on, but not when battles are up. will create a new music for batttles
User prompt
play gametitle music when title screen is up
User prompt
play gametitle when game title is up βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
game title asset should have a small up and down animation βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
play slash when player attacks
User prompt
replace ok by close on popups that have ok
User prompt
Inventory and stats buttons and text are not centered in the bottom of the screen. Inventory is closer to the left side. Cann you make them better ccenteredD?
User prompt
Move level in gui a little lower
User prompt
Text of what is found in the chest should be white
User prompt
Numbers of cells should be white
User prompt
Text of you found should be black
User prompt
Inventpry and stats shold have buttons bacgkround
User prompt
Start game should have a button bacgkround and an animation βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Remove overlay in main menu. Just leave the game title and a start game button, and use the current background
User prompt
Add a background image to the game
User prompt
Move game title more up
User prompt
Move title higher on the main screen
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // BattleScreen: Handles the turn-based combat var BattleScreen = Container.expand(function () { var self = Container.call(this); self.visible = false; // Initially hidden self.alpha = 0; // Set alpha to 0 for fade-in animation // Background overlay self.overlay = self.attachAsset('emptyTileCover', { width: 2048, height: 2732, alpha: 0.8 }); // Monster display self.monsterDisplay = self.attachAsset('monsterTileMonster', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 300, // Position near top-right corner within the battle area y: 2732 * 0.2, // Position near top-right corner within the battle area, moved higher scaleX: 4, // Increased scale scaleY: 4 // Increased scale }); // Monster stats text self.monsterStatText = new Text2('', { size: 60, fill: "#fff", font: "Impact" // Using a pixel-style font }); self.monsterStatText.anchor.set(0.5, 0); // Anchor to top-center self.monsterStatText.x = self.monsterDisplay.x; self.monsterStatText.y = self.monsterDisplay.y + self.monsterDisplay.height * self.monsterDisplay.scaleY / 2 + 60; // Positioned closer below monster image self.addChild(self.monsterStatText); // Player stats text (in battle) self.playerBattleStatText = new Text2('', { size: 60, fill: "#fff", font: "Impact" // Using a pixel-style font }); self.playerDisplay = self.attachAsset('character', { anchorX: 0.5, anchorY: 0.5, x: 300, // Position near bottom-left corner within the battle area y: 2732 * 0.8 - 350, // Position near bottom-left corner within the battle area, moved higher scaleX: 4, // Increased scale scaleY: 4 // Increased scale }); self.playerBattleStatText.anchor.set(0.5, 0); // Anchor to top-center // Set stat text position to where hero will appear self.playerBattleStatText.x = self.playerDisplay.x; // Relative to player display X self.playerBattleStatText.y = self.playerDisplay.y + self.playerDisplay.height * self.playerDisplay.scaleY / 2 + 60; // Positioned closer below player, accounting for new scale self.addChild(self.playerBattleStatText); // Action button (Attack) self.attackBtn = new Text2('ATTACK', { size: 80, fill: "#fff", font: "Impact" // Using a pixel-style font }); self.attackBtn.anchor.set(0.5, 0.5); // Action button (Items) - Initial declaration for width calculation // The actual self.itemsBtn will be (re)declared later. This corresponds to original first itemsBtn {J} var tempItemsBtn = new Text2('ITEMS', { size: 80, fill: "#fff", font: "Impact" // Using a pixel-style font }); // Action button (Run) - Initial declaration for width calculation // The actual self.runBtn will be (re)declared later. This corresponds to original first runBtn {D} var tempRunBtn = new Text2('RUN', { size: 80, fill: "#fff", font: "Impact" // Using a pixel-style font }); // Calculate total width using the three distinct buttons' initial declarations var totalButtonWidth = self.attackBtn.width + tempItemsBtn.width + tempRunBtn.width + 120; // Widths + 2*60px spacing var startX = (2048 - totalButtonWidth) / 2; // --- Position buttons in order: Attack, Items, Run --- var commonButtonY = 2732 - 300; // Common Y position for all buttons // 1. ATTACK Button (leftmost) // self.attackBtn is already defined and its anchor is set. self.attackBtn.x = startX + self.attackBtn.width / 2; self.attackBtn.y = commonButtonY; self.addChild(self.attackBtn); self.attackBtn.down = function () { if (!self.visible) return; self.playerTurn(); }; // 2. ITEMS Button (center) // Action button (Items)//{Z} // This (re)declaration corresponds to original second itemsBtn {Z} self.itemsBtn = new Text2('ITEMS', { size: 80, fill: "#fff", font: "Impact" // Using a pixel-style font }); self.itemsBtn.anchor.set(0.5, 0.5); self.itemsBtn.x = startX + self.attackBtn.width + 60 + self.itemsBtn.width / 2; self.itemsBtn.y = commonButtonY; self.addChild(self.itemsBtn); self.itemsBtn.down = function () { if (!self.visible || player.inventory.potionCount <= 0) return; // Only allow if visible and player has potions self.showBattleItemsMenu(); }; // 3. RUN Button (rightmost) // Action button (Run)//{R} // This (re)declaration corresponds to original second runBtn {R} self.runBtn = new Text2('RUN', { size: 80, fill: "#fff", font: "Impact" // Using a pixel-style font }); self.runBtn.anchor.set(0.5, 0.5); self.runBtn.x = startX + self.attackBtn.width + 60 + self.itemsBtn.width + 60 + self.runBtn.width / 2; self.runBtn.y = commonButtonY; self.addChild(self.runBtn); self.runBtn.down = function () { if (!self.visible) return; self.tryToRun(); }; self.currentMonster = null; // Reference to the monster tile being fought // Battle Items Menu self.battleItemsMenu = new Container(); self.battleItemsMenu.visible = false; self.battleItemsMenuOverlay = self.battleItemsMenu.attachAsset('emptyTileCover', { width: 800, height: 500, alpha: 0.9, anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); self.battleItemsMenu.titleText = new Text2('Use Item', { size: 70, fill: 0x7DD3FC, // Light Blue font: "Impact" // Using a pixel-style font }); self.battleItemsMenu.titleText.anchor.set(0.5, 0.5); self.battleItemsMenu.titleText.x = 2048 / 2; self.battleItemsMenu.titleText.y = 2732 / 2 - 150; self.battleItemsMenu.addChild(self.battleItemsMenu.titleText); self.battleItemsMenu.potionCountText = new Text2('', { size: 60, fill: 0xA3E635, // Green font: "Impact" // Using a pixel-style font }); self.battleItemsMenu.potionCountText.anchor.set(0.5, 0.5); self.battleItemsMenu.potionCountText.x = 2048 / 2; self.battleItemsMenu.potionCountText.y = 2732 / 2 - 50; self.battleItemsMenu.addChild(self.battleItemsMenu.potionCountText); self.battleItemsMenu.usePotionBtn = new Text2('Use Life Potion', { size: 60, fill: 0xA3E635, // Green font: "Impact" // Using a pixel-style font }); self.battleItemsMenu.usePotionBtn.anchor.set(0.5, 0.5); self.battleItemsMenu.usePotionBtn.x = 2048 / 2; self.battleItemsMenu.usePotionBtn.y = 2732 / 2 + 50; self.battleItemsMenu.addChild(self.battleItemsMenu.usePotionBtn); self.battleItemsMenu.usePotionBtn.down = function () { if (player.inventory.potionCount > 0 && player.hp < player.maxHp) { player.inventory.potionCount--; player.hp += 20; // Heal amount if (player.hp > player.maxHp) player.hp = player.maxHp; self.updateBattleStats(); // Update battle stats display self.hideBattleItemsMenu(); self.showConfirmationText('Used a Life Potion!', 0xA3E635); // Show confirmation message playerInventoryScreen.updateInventoryDisplay(); // Update inventory screen if open showGameGUIWithTransition(); } }; self.battleItemsMenu.closeBtn = new Text2('Close', { size: 60, fill: "#fff", font: "Impact" // Using a pixel-style font }); self.battleItemsMenu.closeBtn.anchor.set(0.5, 0.5); self.battleItemsMenu.closeBtn.x = 2048 / 2; self.battleItemsMenu.closeBtn.y = 2732 / 2 + 150; self.battleItemsMenu.addChild(self.battleItemsMenu.closeBtn); self.battleItemsMenu.closeBtn.down = function () { self.hideBattleItemsMenu(); }; self.addChild(self.battleItemsMenu); self._confirmationText = null; // Text for temporary messages (e.g., item used) self.showConfirmationText = function (message, color) { if (self._confirmationText && typeof self._confirmationText.destroy === 'function') { self._confirmationText.destroy(); } self._confirmationText = new Text2(message, { size: 60, fill: color, font: "Impact" // Using a pixel-style font }); self._confirmationText.anchor.set(0.5, 0.5); self._confirmationText.x = 2048 / 2; self._confirmationText.y = 2732 / 2 + 300; // Position below action buttons self.addChild(self._confirmationText); tween(self._confirmationText, { alpha: 0 }, { duration: 1000, delay: 800, onFinish: function onFinish() { self._confirmationText.destroy(); self._confirmationText = null; } }); }; // Start battle self.startBattle = function (monsterTile) { self.currentMonster = monsterTile; self.visible = true; self.alpha = 0; game.addChild(self); // Add battle screen to game updateGUI(); // Ensure main GUI elements are hidden // Hide hero/monster and attack button for intro animation self.monsterDisplay.visible = false; self.playerDisplay.visible = false; self.monsterStatText.visible = false; self.playerBattleStatText.visible = false; self.attackBtn.visible = false; self.runBtn.visible = false; // Initially hidden self.itemsBtn.visible = false; // Hide items button // Fade in battle screen tween(self, { alpha: 1 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Show "You encountered a monster!" message var encounterText = new Text2('You encountered a monster!', { size: 110, fill: 0xFFE066, font: "Impact" // Using a pixel-style font }); encounterText.anchor.set(0.5, 0.5); encounterText.x = 2048 / 2; encounterText.y = 2732 / 2 - 200; self.addChild(encounterText); tween(encounterText, { alpha: 0 }, { duration: 900, delay: 900, easing: tween.easeOut, onFinish: function onFinish() { encounterText.destroy(); // Prepare hero/monster offscreen for slide-in // Save original positions var heroTargetX = 300; var heroTargetY = 2732 * 0.8 - 350; // Adjusted Y position var monsterTargetX = 2048 - 300; var monsterTargetY = 2732 * 0.2; // Adjusted Y position self.playerDisplay.x = -self.playerDisplay.width; // Offscreen left self.playerDisplay.y = heroTargetY; self.monsterDisplay.x = 2048 + self.monsterDisplay.width; // Offscreen right self.monsterDisplay.y = monsterTargetY; self.playerDisplay.visible = true; self.monsterDisplay.visible = true; self.monsterStatText.visible = false; self.playerBattleStatText.visible = false; // Slide in hero and monster simultaneously var slideInCount = 0; function afterSlideIn() { slideInCount++; if (slideInCount === 2) { // Show stats and decide who attacks first var firstAttacker = "monster"; // Always monster attacks first self.monsterStatText.visible = true; self.playerBattleStatText.visible = true; self.updateBattleStats(); // Randomize who attacks first: 0 = player, 1 = monster var firstAttacker = Math.random() < 0.5 ? "player" : "monster"; var firstText = firstAttacker === "monster" ? "Monster strikes first!" : "You start!"; var firstTextColor = firstAttacker === "monster" ? 0xff0000 : 0xFFE066; var whoFirstText = new Text2(firstText, { size: 110, fill: firstTextColor, font: "Impact" // Using a pixel-style font }); whoFirstText.anchor.set(0.5, 0.5); whoFirstText.x = 2048 / 2; whoFirstText.y = 2732 / 2; self.addChild(whoFirstText); tween(whoFirstText, { alpha: 0 }, { duration: 900, delay: 900, easing: tween.easeOut, onFinish: function onFinish() { whoFirstText.destroy(); // Show attack button and start the correct turn after a short delay if (firstAttacker === "monster") { // Hide player actions until after monster strikes self.attackBtn.visible = false; self.runBtn.visible = false; self.itemsBtn.visible = false; LK.setTimeout(self.monsterTurn, 500); } else { self.attackBtn.visible = true; self.runBtn.visible = true; // Show run button after intro self.itemsBtn.visible = true; // Show items button after intro } // If player starts, do nothing: player must press attackBtn } }); } } tween(self.playerDisplay, { x: heroTargetX }, { duration: 600, easing: tween.cubicOut, onFinish: afterSlideIn }); tween(self.monsterDisplay, { x: monsterTargetX }, { duration: 600, easing: tween.cubicOut, onFinish: afterSlideIn }); } }); } }); }; // End battle self.endBattle = function (win) { self.visible = false; self.alpha = 0; // Reset alpha self.attackBtn.visible = false; self.runBtn.visible = false; // Hide run button self.itemsBtn.visible = false; // Hide items button self.hideBattleItemsMenu(); // Hide item menu if (self._confirmationText) { // Hide any confirmation text self._confirmationText.destroy(); self._confirmationText = null; } if (win) { // expGained and player.gainExp(expGained) are now handled in playerTurn's onFinish monster death animation self.currentMonster.defeat(); monstersLeft--; showGameGUIWithTransition(); if (monstersLeft <= 0) { // If not last level, advance to next level if (currentLevel < LEVELS.length - 1) { // Show level end screen levelEndScreen.startLevelEndScreen(); } else { LK.showYouWin(); gameOver = true; } } else {} // Player lost, game over is handled in handleTileDown } self.currentMonster = null; self.parent.removeChild(self); // Remove battle screen from game }; // Update battle stats display self.updateBattleStats = function () { if (self.currentMonster) { self.monsterStatText.setText('HP:' + self.currentMonster.hp + '/' + self.currentMonster.maxHp + ' DMG:' + self.currentMonster.damage); self.playerBattleStatText.setText('HP:' + player.hp + '/' + player.maxHp + ' DMG:' + player.damage); } // Grey out and disable ITEMS button if player has no potions if (player.inventory.potionCount <= 0) { self.itemsBtn.alpha = 0.5; self.itemsBtn.interactive = false; } else { self.itemsBtn.alpha = 1; self.itemsBtn.interactive = true; } }; // Player's turn self.playerTurn = function () { if (!self.currentMonster) return; // Animate player attack: slide playerDisplay forward diagonally, then back, then apply damage var originalX = self.playerDisplay.x; var originalY = self.playerDisplay.y; var attackX = self.monsterDisplay.x - self.monsterDisplay.width * self.monsterDisplay.scaleX / 2 - 60; // Stop a bit before monster var attackY = self.monsterDisplay.y + self.monsterDisplay.height * self.monsterDisplay.scaleY / 2 - 60; // Move up towards monster self.attackBtn.visible = false; // Hide attack button during animation self.runBtn.visible = false; // Hide run button during animation self.itemsBtn.visible = false; // Hide items button during animation // Show "Player Attacks!" text var playerAttackText = new Text2('Player Attacks!', { size: 90, fill: 0xffe066, font: "Impact" // Using a pixel-style font }); playerAttackText.anchor.set(0.5, 0.5); playerAttackText.x = 2048 / 2; playerAttackText.y = self.monsterStatText.y - 100; self.addChild(playerAttackText); function doAttack() { // Damage and flash self.currentMonster.hp -= player.damage; LK.effects.flashObject(self.monsterDisplay, 0xffe066, 300); // Show animated -X text above monster in battle if (self.currentMonster && self.monsterDisplay) { var monsterDmgText = new Text2('-' + player.damage, { size: 100, fill: 0xff0000, font: "Impact" // Using a pixel-style font }); monsterDmgText.anchor.set(0.5, 1); // Position above monster sprite monsterDmgText.x = self.monsterDisplay.x; monsterDmgText.y = self.monsterDisplay.y - self.monsterDisplay.height / 2 - 20; self.addChild(monsterDmgText); // Animate: pop up and fade out tween(monsterDmgText, { y: monsterDmgText.y - 80, alpha: 0 }, { duration: 700, easing: tween.cubicOut, onFinish: function onFinish() { monsterDmgText.destroy(); } }); } self.updateBattleStats(); // Fade out attack text tween(playerAttackText, { alpha: 0 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { playerAttackText.destroy(); } }); // If monster dies, animate monster death if (self.currentMonster.hp <= 0) { // Monster death animation: fade out and scale down tween(self.monsterDisplay, { alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 600, easing: tween.cubicIn, onFinish: function onFinish() { var expGained = self.currentMonster.exp; var lootMessages = []; // Award EXP player.gainExp(expGained); // Coin Drop var coinsDropped = Math.floor(self.currentMonster.exp * (2 + Math.random() * 4)); // 2-5 coins per EXP point if (coinsDropped > 0) { player.gold += coinsDropped; lootMessages.push(coinsDropped + ' Coins'); } // Gear Drop (Rare) var gearDropChance = 0.1; // 10% chance for now, we can adjust this later! if (Math.random() < gearDropChance) { if (chestScreen && chestScreen._rewardTypes && chestScreen._rewardTypes.length > 2 && chestScreen._lootQualities && chestScreen._lootQualities.length > 0) { var potentialGearRewards = chestScreen._rewardTypes.slice(2); // Exclude potion and coins if (potentialGearRewards.length > 0) { var gearReward = potentialGearRewards[Math.floor(Math.random() * potentialGearRewards.length)]; var gearQuality = chestScreen._lootQualities[Math.floor(Math.random() * chestScreen._lootQualities.length)]; player.equipGear(gearReward.type, gearQuality); // This also calls player.updateEquippedStats() var gearLabel = gearQuality.label + ' ' + gearReward.label; lootMessages.push(gearLabel); // playerStatsScreen and playerInventoryScreen will be updated via updateGUI() when battle ends } } } // Construct Victory Message var victoryMessage = 'Victory!\nGained ' + expGained + ' EXP!'; if (lootMessages.length > 0) { victoryMessage += '\n\nLoot:\n' + lootMessages.join('\n'); } // Show "Victory!" message before closing battle screen var victoryText = new Text2(victoryMessage, { size: 110, fill: 0xA3E635, align: 'center', // Ensure multi-line text is centered font: "Impact" // Using a pixel-style font }); victoryText.anchor.set(0.5, 0.5); victoryText.x = 2048 / 2; victoryText.y = 2732 / 2; self.addChild(victoryText); victoryText.alpha = 0; // Fade in, hold, then fade out tween(victoryText, { alpha: 1 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { tween(victoryText, { alpha: 0 }, { duration: 500, delay: 1000, // Increased delay to read loot easing: tween.easeIn, onFinish: function onFinish() { victoryText.destroy(); self.endBattle(true); // Player wins // Reset monsterDisplay for next battle self.monsterDisplay.alpha = 1; self.monsterDisplay.scaleX = 4; // Adjusted to new scale self.monsterDisplay.scaleY = 4; // Adjusted to new scale } }); } }); } }); } else { // Monster survives, monster's turn after a delay LK.setTimeout(self.monsterTurn, 500); self.attackBtn.visible = true; self.runBtn.visible = true; // Show run button again self.itemsBtn.visible = true; // Show items button again } } // Animate player forward diagonally, then back, then do attack tween(self.playerDisplay, { x: attackX, y: attackY }, { duration: 180, easing: tween.cubicOut, onFinish: function onFinish() { tween(self.playerDisplay, { x: originalX, y: originalY }, { duration: 180, easing: tween.cubicIn, onFinish: doAttack }); } }); }; // Monster's turn self.monsterTurn = function () { if (!self.currentMonster) return; // Animate monster attack: slide monsterDisplay forward diagonally, then back, then apply damage var originalX = self.monsterDisplay.x; var originalY = self.monsterDisplay.y; var attackX = self.playerDisplay.x + self.playerDisplay.width * self.playerDisplay.scaleX / 2 + 60; // Stop a bit before player var attackY = self.playerDisplay.y - self.playerDisplay.height * self.playerDisplay.scaleY / 2 + 60; // Move down towards player self.attackBtn.visible = false; // Hide attack button during animation self.runBtn.visible = false; // Hide run button during animation self.itemsBtn.visible = false; // Hide items button during animation // Show "Monster Attacks!" text var monsterAttackText = new Text2('Monster Attacks!', { size: 90, fill: 0xff0000, font: "Impact" // Using a pixel-style font }); monsterAttackText.anchor.set(0.5, 0.5); monsterAttackText.x = 2048 / 2; monsterAttackText.y = self.playerBattleStatText.y + 100; self.addChild(monsterAttackText); function doMonsterAttack() { player.takeDamage(self.currentMonster.damage); // Flash monster and player LK.effects.flashObject(self.monsterDisplay, 0xff0000, 300); // Flash monster indicating it attacked LK.effects.flashObject(self.playerDisplay, 0xff0000, 300); // Flash player sprite LK.effects.flashObject(self.playerBattleStatText, 0xff0000, 300); // Flash player stats indicating damage // Show animated -X text above player in battle (damage taken) or show "Blocked!" text if (player && self.playerDisplay) { var damageTaken = Math.max(0, self.currentMonster.damage - player.defense); var playerDmgText; var textColor; if (damageTaken > 0) { playerDmgText = new Text2('-' + damageTaken, { size: 100, fill: 0xff0000, // Red for damage font: "Impact" // Using a pixel-style font }); textColor = 0xff0000; } else { playerDmgText = new Text2('Blocked!', { size: 90, fill: 0x7DD3FC, // Light blue for blocked font: "Impact" // Using a pixel-style font }); textColor = 0x7DD3FC; } playerDmgText.anchor.set(0.5, 1); // Position above player sprite playerDmgText.x = self.playerDisplay.x; playerDmgText.y = self.playerDisplay.y - self.playerDisplay.height / 2 - 20; self.addChild(playerDmgText); // Animate: pop up and fade out tween(playerDmgText, { y: playerDmgText.y - 80, alpha: 0 }, { duration: 700, easing: tween.cubicOut, onFinish: function onFinish() { playerDmgText.destroy(); } }); } // Fade out attack text tween(monsterAttackText, { alpha: 0 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { monsterAttackText.destroy(); // Show attack button again if player is still alive and battle is ongoing if (player.hp > 0 && self.currentMonster && !gameOver) { self.attackBtn.visible = true; self.runBtn.visible = true; // Show run button again self.itemsBtn.visible = true; // Show items button again } } }); self.updateBattleStats(); if (player.hp <= 0) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); gameOver = true; revealAllMonsters(); self.endBattle(false); // Player loses } } // Animate monster forward diagonally, then back, then do attack tween(self.monsterDisplay, { x: attackX, y: attackY }, { duration: 180, easing: tween.cubicOut, onFinish: function onFinish() { tween(self.monsterDisplay, { x: originalX, y: originalY }, { duration: 180, easing: tween.cubicIn, onFinish: doMonsterAttack }); } }); }; self.tryToRun = function () { if (!self.currentMonster) return; self.attackBtn.visible = false; // Hide attack and run buttons self.runBtn.visible = false; self.itemsBtn.visible = false; // Hide items button self.itemsBtn.visible = false; // Hide items button var runSuccess = true; // Calculate chance to fail run based on monster level vs player level if (self.currentMonster.exp > player.level) { // Use monster EXP as a rough proxy for level/difficulty var failChance = Math.min(0.8, (self.currentMonster.exp - player.level) * 0.1); // 10% fail chance per monster level difference, max 80% if (Math.random() < failChance) { runSuccess = false; } } var runMessageText; var runMessageColor; if (runSuccess) { runMessageText = new Text2('You successfully ran away!', { size: 90, fill: 0xA3E635, // Green font: "Impact" // Using a pixel-style font }); } else { runMessageText = new Text2('Failed to run!', { size: 90, fill: 0xFF0000, // Red font: "Impact" // Using a pixel-style font }); } runMessageText.anchor.set(0.5, 0.5); runMessageText.x = 2048 / 2; runMessageText.y = 2732 / 2; self.addChild(runMessageText); tween(runMessageText, { alpha: 0 }, { duration: 800, delay: 600, onFinish: function onFinish() { runMessageText.destroy(); if (runSuccess) { // Reset monster HP and mark as not defeated self.currentMonster.hp = self.currentMonster.maxHp; self.currentMonster.defeated = false; // Important: Reset defeated state // Make sure monster remains visible but cover is restored self.currentMonster.cover.alpha = 1; // Restore cover to original state self.currentMonster.monster.alpha = 1; // Ensure monster image remains visible self.currentMonster.revealed = false; // Set revealed to false // End battle and return to grid self.endBattle(false); // Pass false, as it's not a win. This sets battleScreen.visible to false. showGameGUIWithTransition(); } else { // Running failed, monster attacks LK.setTimeout(self.monsterTurn, 300); // Monster attacks immediately after message } } }); }; // Show the battle items menu self.showBattleItemsMenu = function () { self.battleItemsMenu.visible = true; self.attackBtn.visible = false; // Hide action buttons self.runBtn.visible = false; self.itemsBtn.visible = false; // Update potion count display self.battleItemsMenu.potionCountText.setText('Potions: ' + player.inventory.potionCount); // Disable use potion button if player is full HP or no potions if (player.inventory.potionCount <= 0 || player.hp >= player.maxHp) { self.battleItemsMenu.usePotionBtn.alpha = 0.5; self.battleItemsMenu.usePotionBtn.interactive = false; } else { self.battleItemsMenu.usePotionBtn.alpha = 1; self.battleItemsMenu.usePotionBtn.interactive = true; } // Also update ITEMS button state in battle screen if (player.inventory.potionCount <= 0) { self.itemsBtn.alpha = 0.5; self.itemsBtn.interactive = false; } else { self.itemsBtn.alpha = 1; self.itemsBtn.interactive = true; } }; // Hide the battle items menu self.hideBattleItemsMenu = function () { self.battleItemsMenu.visible = false; // Show action buttons again if player is alive and battle is ongoing if (player.hp > 0 && self.currentMonster && !gameOver) { self.attackBtn.visible = true; self.runBtn.visible = true; self.itemsBtn.visible = true; // Update ITEMS button state if (player.inventory.potionCount <= 0) { self.itemsBtn.alpha = 0.5; self.itemsBtn.interactive = false; } else { self.itemsBtn.alpha = 1; self.itemsBtn.interactive = true; } } }; return self; }); // ChestScreen: Handles the chest found popup var ChestScreen = Container.expand(function () { var self = Container.call(this); self.visible = false; self.alpha = 0; // Background overlay self.overlay = self.attachAsset('emptyTileCover', { width: 2048, height: 2732, alpha: 0.8 }); // Chest display (center) - use chest image asset self.chestDisplay = self.attachAsset('chest', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 - 100, scaleX: 4, // Increased scale scaleY: 4 // Increased scale }); // "You found a chest!" text self.chestText = new Text2('You found a chest!', { size: 120, // Increased size fill: 0xFFD700, font: "Impact" // Using a pixel-style font }); self.chestText.anchor.set(0.5, 0.5); self.chestText.x = 2048 / 2; self.chestText.y = 2732 * 0.25; // Adjusted Y position to be higher self.addChild(self.chestText); // Player display (bottom, like battle screen) self.playerDisplay = self.attachAsset('character', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 * 0.8, scaleX: 4, scaleY: 4 }); // --- Double-tap to open chest logic --- self._lastTapTime = 0; self._tapCount = 0; self._rewardPopup = null; // Chest reward types self._rewardTypes = [{ type: "potion", label: "Life Potion", color: 0xA3E635 }, { type: "coins", label: "Coins", color: 0xFFD700 }, { type: "shield", label: "Shield", color: 0x7DD3FC }, { type: "sword", label: "Sword", color: 0xF87171 }, { type: "helmet", label: "Helmet", color: 0xFACC15 }, { type: "armour", label: "Armour", color: 0xA78BFA }]; // Loot qualities self._allLootQualities = [{ label: "Wooden", modifier: 1, color: 0x8B4513, // SaddleBrown minLevel: 1 }, { label: "Bronze", modifier: 2, color: 0xCD7F32, // Bronze minLevel: 2 }, { label: "Silver", modifier: 3, color: 0xC0C0C0, // Silver minLevel: 3 }]; // This will be set on chest open, based on player.level self._lootQualities = self._allLootQualities; // Helper to show reward popup self._showRewardPopup = function (reward) { if (self._rewardPopup) { self._rewardPopup.destroy(); self._rewardPopup = null; } var popup = new Container(); // Overlay var overlay = LK.getAsset('emptyTileBg', { width: 1200, height: 600, alpha: 0.95, anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); popup.addChild(overlay); // Reward text var rewardText = new Text2('You found: ' + reward.label + '!', { size: 90, fill: reward.color, font: "Impact" // Using a pixel-style font }); rewardText.anchor.set(0.5, 0.5); rewardText.x = 2048 / 2; rewardText.y = 2732 / 2 - 50; popup.addChild(rewardText); // "OK" button var okBtn = new Text2('OK', { size: 70, fill: "#fff", font: "Impact" // Using a pixel-style font }); okBtn.anchor.set(0.5, 0.5); okBtn.x = 2048 / 2; okBtn.y = 2732 / 2 + 80; okBtn.down = function () { popup.destroy(); self._rewardPopup = null; self.endChest(); }; popup.addChild(okBtn); self._rewardPopup = popup; game.addChild(popup); }; // Double-tap handler for chest self.chestDisplay.down = function (x, y, obj) { var now = Date.now(); if (now - self._lastTapTime < 400) { self._tapCount++; } else { self._tapCount = 1; } self._lastTapTime = now; if (self._tapCount === 2 && self.visible && !self._rewardPopup) { // Open chest if (self.currentChest && !self.currentChest.opened) { self.currentChest.open(); // Pick a random reward var reward = self._rewardTypes[Math.floor(Math.random() * self._rewardTypes.length)]; var quality = null; if (reward.type !== "potion" && reward.type !== "coins") { // For gear, pick a random quality from those unlocked by player level var unlockedQualities = []; for (var i = 0; i < self._allLootQualities.length; i++) { if (player.level >= self._allLootQualities[i].minLevel) { unlockedQualities.push(self._allLootQualities[i]); } } // Fallback: always at least wooden if (unlockedQualities.length === 0) unlockedQualities.push(self._allLootQualities[0]); quality = unlockedQualities[Math.floor(Math.random() * unlockedQualities.length)]; } // Apply reward effect (for now, just heal for potion, add coins, or do nothing for gear) if (reward.type === "potion") { player.inventory.potionCount++; // Add potion to inventory self._showRewardPopup(reward); playerInventoryScreen.updateInventoryDisplay(); // Update inventory screen if open } else if (reward.type === "coins") { player.gold += 10 + Math.floor(Math.random() * 10); // Add 10-20 coins self._showRewardPopup(reward); playerInventoryScreen.updateInventoryDisplay(); // Update inventory screen if open } else if (quality) { // Add gear to player inventory with quality modifier player.equipGear(reward.type, quality); var lootLabel = quality.label + ' ' + reward.label; self._showRewardPopup({ label: lootLabel, color: quality.color }); playerStatsScreen.updateStatsDisplay(); // Update stats screen if open playerInventoryScreen.updateInventoryDisplay(); // Update inventory screen if open } } self._tapCount = 0; } }; // Start chest screen self.startChest = function (chestTile) { self.currentChest = chestTile; // Set available loot qualities for this chest based on player level var unlockedQualities = []; for (var i = 0; i < self._allLootQualities.length; i++) { if (player.level >= self._allLootQualities[i].minLevel) { unlockedQualities.push(self._allLootQualities[i]); } } // Fallback: always at least wooden if (unlockedQualities.length === 0) unlockedQualities.push(self._allLootQualities[0]); self._lootQualities = unlockedQualities; self.visible = true; self.alpha = 0; game.addChild(self); updateGUI(); // Hide main GUI // Fade in tween(self, { alpha: 1 }, { duration: 300, easing: tween.easeOut }); }; // End chest screen self.endChest = function () { self.visible = false; self.alpha = 0; self.currentChest = null; if (self._rewardPopup) { self._rewardPopup.destroy(); self._rewardPopup = null; } if (self.parent) self.parent.removeChild(self); showGameGUIWithTransition(); }; return self; }); // ChestTile: Represents a chest hidden under a tile var ChestTile = Container.expand(function () { var self = Container.call(this); self.revealed = false; self.opened = false; self.outline = self.attachAsset('tileOutline', { // Attach black outline anchorX: 0.5, anchorY: 0.5 }); self.cover = self.attachAsset('emptyTileCover', { anchorX: 0.5, anchorY: 0.5 }); // Use a chest image asset self.chest = self.attachAsset('chest', { anchorX: 0.5, anchorY: 0.5, alpha: 0 // Hidden until revealed }); // Reveal the chest self.reveal = function () { if (self.revealed) return; self.revealed = true; self.cover.alpha = 0.2; self.chest.alpha = 1; }; // Mark as opened self.open = function () { self.opened = true; self.cover.alpha = 0.1; // Grey out cover self.chest.alpha = 0.5; // Grey out chest image // Optionally animate chest opening here }; return self; }); // EmptyTile: Represents a safe tile (no monster) var EmptyTile = Container.expand(function () { var self = Container.call(this); self.revealed = false; self.adjacentMonsterDamage = 0; // Change to store damage sum self.outline = self.attachAsset('tileOutline', { // Attach black outline anchorX: 0.5, anchorY: 0.5 }); self.cover = self.attachAsset('emptyTileCover', { anchorX: 0.5, anchorY: 0.5 }); self.bg = self.attachAsset('emptyTileBg', { anchorX: 0.5, anchorY: 0.5, alpha: 0 // Hidden until revealed }); self.adjText = new Text2('', { size: 48, fill: "#fff", font: "Impact" // Using a pixel-style font }); self.adjText.anchor.set(0.5, 0.5); self.adjText.alpha = 0; self.addChild(self.adjText); self.reveal = function () { if (self.revealed) return; self.revealed = true; self.cover.alpha = 0.2; self.bg.alpha = 1; if (self.adjacentMonsterDamage > 0) { // Check damage sum self.adjText.setText(self.adjacentMonsterDamage + ''); // Display damage sum self.adjText.alpha = 1; } }; return self; }); // LevelEndScreen: Appears after clearing a level, offering Next Level and Shop options var LevelEndScreen = Container.expand(function () { var self = Container.call(this); self.visible = false; self.alpha = 0; // Background overlay self.overlay = self.attachAsset('emptyTileCover', { width: 2048, height: 2732, alpha: 0.9 }); // Main panel background self.panel = self.attachAsset('emptyTileBg', { width: 1200, height: 1000, anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); // Title self.titleText = new Text2('Level Complete!', { size: 110, fill: 0xA3E635, font: "Impact" // Using a pixel-style font }); self.titleText.anchor.set(0.5, 0.5); self.titleText.x = 2048 / 2; self.titleText.y = self.panel.y - self.panel.height / 2 + 120; self.addChild(self.titleText); // Next Level Button self.nextLevelBtn = new Text2('NEXT LEVEL', { size: 80, fill: "#fff", font: "Impact" // Using a pixel-style font }); self.nextLevelBtn.anchor.set(0.5, 0.5); self.nextLevelBtn.x = 2048 / 2; self.nextLevelBtn.y = self.panel.y + 100; self.nextLevelBtn.down = function () { if (!self.visible) return; self.endLevelEndScreen(); currentLevel++; MONSTER_COUNT = LEVELS[currentLevel].monsters; generateBoard(); updateGUI(); levelText.setText('LVL: ' + (currentLevel + 1)); gameOver = false; }; self.addChild(self.nextLevelBtn); // Shop Button self.shopBtn = new Text2('SHOP', { size: 80, fill: "#fff", font: "Impact" // Using a pixel-style font }); self.shopBtn.anchor.set(0.5, 0.5); self.shopBtn.x = 2048 / 2; self.shopBtn.y = self.panel.y + 300; self.shopBtn.down = function () { if (!self.visible) return; self.endLevelEndScreen(); shopScreen.startShopScreen(); }; self.addChild(self.shopBtn); // Start the level end screen self.startLevelEndScreen = function () { self.visible = true; self.alpha = 0; game.addChild(self); updateGUI(); // Hide main GUI // Fade in tween(self, { alpha: 1 }, { duration: 300, easing: tween.easeOut }); }; // End the level end screen self.endLevelEndScreen = function () { self.visible = false; self.alpha = 0; if (self.parent) self.parent.removeChild(self); showGameGUIWithTransition(); }; return self; }); var MainScreen = Container.expand(function () { var self = Container.call(this); self.visible = false; // Initially hidden self.alpha = 0; // Set alpha to 0 for fade-in animation // Background overlay // self.overlay = self.attachAsset('emptyTileCover', { // width: 2048, // height: 2732, // alpha: 0.9 // }); self.overlay = new Container(); // Replace the overlay with an empty container // Main panel background // self.panel = self.attachAsset('emptyTileBg', { // width: 1200, // height: 1000, // anchorX: 0.5, // anchorY: 0.5, // x: 2048 / 2, // y: 2732 / 2 // }); self.panel = new Container(); // Replace the panel with an empty container // Title // Title - now using image asset self.titleImage = self.attachAsset('gameTitle', { // Using the new gameTitle image asset anchorX: 0.5, // Anchor to center anchorY: 0.5, // Anchor to center x: 2048 / 2, // Center horizontally y: 2732 * 0.3 // Position the game title image higher on the screen }); // Remove the old text title // self.titleText.destroy(); // Assuming the original text is added somewhere before this block // Re-add the title image to ensure it's a child of MainScreen. self.addChild(self.titleImage); // Add the title image to the screen container // Update the reference if needed // self.titleText = self.titleImage; // If any other code references self.titleText, this line can replace it // Start Game Button with background and animation self.startGameBtnBg = self.attachAsset('emptyTileBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 * 0.7, width: 500, height: 140, alpha: 0.85 }); self.startGameBtn = new Text2('START GAME', { size: 80, fill: "#fff", font: "Impact" }); self.startGameBtn.anchor.set(0.5, 0.5); self.startGameBtn.x = 2048 / 2; self.startGameBtn.y = 2732 * 0.7; // Position start button lower // Animate the button background (pulse) tween(self.startGameBtnBg, { scaleX: 1.1, scaleY: 1.1 }, { duration: 700, easing: tween.easeInOut, onFinish: function onFinish() { tween(self.startGameBtnBg, { scaleX: 1, scaleY: 1 }, { duration: 700, easing: tween.easeInOut, onFinish: function pulseAgain() { // Loop the pulse tween(self.startGameBtnBg, { scaleX: 1.1, scaleY: 1.1 }, { duration: 700, easing: tween.easeInOut, onFinish: function onFinish() { tween(self.startGameBtnBg, { scaleX: 1, scaleY: 1 }, { duration: 700, easing: tween.easeInOut, onFinish: pulseAgain }); } }); } }); } }); // Animate the button text (gentle scale pulse) tween(self.startGameBtn, { scaleX: 1.08, scaleY: 1.08 }, { duration: 700, easing: tween.easeInOut, onFinish: function onFinish() { tween(self.startGameBtn, { scaleX: 1, scaleY: 1 }, { duration: 700, easing: tween.easeInOut, onFinish: function pulseTextAgain() { tween(self.startGameBtn, { scaleX: 1.08, scaleY: 1.08 }, { duration: 700, easing: tween.easeInOut, onFinish: function onFinish() { tween(self.startGameBtn, { scaleX: 1, scaleY: 1 }, { duration: 700, easing: tween.easeInOut, onFinish: pulseTextAgain }); } }); } }); } }); self.startGameBtn.down = function () { if (!self.visible) return; self.endMainScreen(); // Start the game logic here (e.g., generateBoard) startGame(); // Call a new function to start the game }; self.addChild(self.startGameBtnBg); self.addChild(self.startGameBtn); // (Reset Progress Button removed) // Start the main screen self.startMainScreen = function () { self.visible = true; self.alpha = 0; game.addChild(self); // Fade in tween(self, { alpha: 1 }, { duration: 300, easing: tween.easeOut }); }; // End the main screen self.endMainScreen = function () { self.visible = false; self.alpha = 0; if (self.parent) self.parent.removeChild(self); }; return self; }); // MonsterTile: Represents a monster hidden under a tile var MonsterTile = Container.expand(function () { var self = Container.call(this); // Monster stats self.hp = 1; self.maxHp = 1; self.damage = 1; self.exp = 1; self.revealed = false; self.defeated = false; // Visuals self.outline = self.attachAsset('tileOutline', { // Attach black outline anchorX: 0.5, anchorY: 0.5 }); self.cover = self.attachAsset('monsterTileCover', { anchorX: 0.5, anchorY: 0.5 }); self.monster = self.attachAsset('monsterTileMonster', { anchorX: 0.5, anchorY: 0.5, alpha: 0 // Hidden until revealed }); // Show monster stats as text (hidden until revealed) self.statText = new Text2('', { size: 48, fill: "#fff", font: "Impact" // Using a pixel-style font }); self.statText.anchor.set(0.5, 0.5); self.statText.alpha = 0; self.addChild(self.statText); // Reveal the monster self.reveal = function () { if (self.revealed) return; self.revealed = true; self.cover.alpha = 0.2; self.monster.alpha = 1; self.statText.setText(''); self.statText.alpha = 0; }; // Mark as defeated self.defeat = function () { self.defeated = true; self.monster.alpha = 0.3; self.cover.alpha = 0.1; self.statText.setText(''); self.statText.alpha = 0; // Update adjacent empty tiles' numbers since this monster is no longer generating damage if (typeof grid !== "undefined") { // Find this monster's position in the grid for (var row = 0; row < grid.length; row++) { for (var col = 0; col < grid[row].length; col++) { if (grid[row][col] === self) { var adj = getAdjacent(row, col); for (var i = 0; i < adj.length; i++) { var nr = adj[i][0], nc = adj[i][1]; var t = grid[nr][nc]; if (t instanceof EmptyTile) { // Recalculate adjacentMonsterDamage for this empty tile var adj2 = getAdjacent(nr, nc); var damageSum = 0; for (var j = 0; j < adj2.length; j++) { var ar = adj2[j][0], ac = adj2[j][1]; var adjTile = grid[ar][ac]; if (adjTile instanceof MonsterTile && !adjTile.defeated) { damageSum += adjTile.damage; } } t.adjacentMonsterDamage = damageSum; // If revealed, update the displayed number if (t.revealed) { if (damageSum > 0) { t.adjText.setText(damageSum + ''); t.adjText.alpha = 1; } else { t.adjText.setText(''); t.adjText.alpha = 0; } } } } // Only need to update once for the found monster row = grid.length; // break outer loop break; } } } } }; return self; }); // Add event listener to MonsterTile for clicking // Player: Holds player stats and methods var Player = Container.expand(function () { var self = Container.call(this); // Player stats self.level = 1; self.exp = 0; self.expToLevel = 3; // Start with 3 exp needed for level up self.maxHp = 10; self.hp = self.maxHp; self.damage = 1; // Start with 1 damage self.defense = 0; // Initial defense is 0 self.skillPoints = 0; // Points to spend on upgrades self.gold = 0; // Player's currency self.inventory = { // Player inventory potionCount: 0, sword: null, // Equipped items shield: null, helmet: null, armour: null }; self.baseDamage = 1; // Base damage before gear self.baseDefense = 0; // Base defense before gear // Gain experience self.gainExp = function (amount) { self.exp += amount; while (self.exp >= self.expToLevel) { self.exp -= self.expToLevel; self.levelUp(); } }; // Level up player self.levelUp = function () { self.level++; self.skillPoints++; self.expToLevel = Math.ceil(self.expToLevel * 1.2); // Increase EXP needed for next level, ensuring it consistently grows // Increase base stats on level up // self.maxHp += 5; // Example stat increase // self.damage += 1; // Example stat increase // self.hp = self.maxHp; // Do not heal to full HP on level up self.updateEquippedStats(); // Update stats after base increase }; // Take damage self.takeDamage = function (amount) { var damageTaken = Math.max(0, amount - self.defense); // Damage reduced by defense self.hp -= damageTaken; if (self.hp < 0) self.hp = 0; }; // Spend skill points self.spendSkillPoint = function (stat) { if (self.skillPoints <= 0) return; self.skillPoints--; if (stat === "hp") { self.maxHp += 10; // More HP per point self.hp = self.maxHp; // Heal to full } else if (stat === "damage") { self.baseDamage += 2; // Increase base damage } else if (stat === "defense") { self.baseDefense += 1; // Increase base defense } self.updateEquippedStats(); // Update stats after spending points }; // Equip gear self.equipGear = function (type, quality) { self.inventory[type] = { type: type, label: quality.label, modifier: quality.modifier, color: quality.color }; self.updateEquippedStats(); // Update stats after equipping }; // Update stats based on equipped gear self.updateEquippedStats = function () { self.damage = self.baseDamage; self.defense = self.baseDefense; if (self.inventory.sword) self.damage += self.inventory.sword.modifier; if (self.inventory.shield) self.defense += self.inventory.shield.modifier; if (self.inventory.helmet) self.defense += Math.floor(self.inventory.helmet.modifier / 2); // Helmet gives half defense if (self.inventory.armour) self.defense += self.inventory.armour.modifier; // Armour gives full defense // Ensure current HP doesn't exceed new max HP after stat changes if (self.hp > self.maxHp) self.hp = self.maxHp; }; return self; }); // PlayerInventoryScreen: Displays player's inventory and allows using potions var PlayerInventoryScreen = Container.expand(function () { var self = Container.call(this); self.visible = false; // Initially hidden self.alpha = 0; // Set alpha to 0 for fade-in animation // Background overlay self.overlay = self.attachAsset('emptyTileCover', { width: 2048, height: 2732, alpha: 0.9 }); // Main panel background self.panel = self.attachAsset('emptyTileBg', { width: 1200, height: 1800, // Increased height for more items anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); // Title self.titleText = new Text2('INVENTORY', { size: 110, fill: 0x7DD3FC, // Light Blue font: "Impact" // Using a pixel-style font }); self.titleText.anchor.set(0.5, 0.5); self.titleText.x = 2048 / 2; self.titleText.y = self.panel.y - self.panel.height / 2 + 120; self.addChild(self.titleText); // Gold display self.goldText = new Text2('', { size: 60, fill: 0xFFD700, // Gold font: "Impact" // Using a pixel-style font }); self.goldText.anchor.set(0, 0.5); self.goldText.x = self.panel.x - self.panel.width / 2 + 80; self.goldText.y = self.panel.y - self.panel.height / 2 + 220; self.addChild(self.goldText); // Inventory items display self.inventoryItemsText = new Text2('', { size: 60, fill: "#fff", font: "Impact" // Using a pixel-style font }); self.goldText.anchor.set(0, 0.5); self.goldText.x = self.panel.x - self.panel.width / 2 + 80; self.goldText.y = self.panel.y - self.panel.height / 2 + 220; self.addChild(self.goldText); // Inventory items display // Use separate text elements for better layout control // Potion display and button self.potionTitle = new Text2('Consumables:', { size: 70, fill: 0x7DD3FC, // Light Blue font: "Impact" // Using a pixel-style font }); self.potionTitle.anchor.set(0, 0.5); self.potionTitle.x = self.panel.x - self.panel.width / 2 + 80; self.potionTitle.y = self.goldText.y + 100; // Position below gold self.addChild(self.potionTitle); self.potionCountText = new Text2('', { size: 60, fill: 0xA3E635, // Green font: "Impact" // Using a pixel-style font }); self.potionCountText.anchor.set(0, 0.5); self.potionCountText.x = self.potionTitle.x + 50; // Indented below title self.potionCountText.y = self.potionTitle.y + 70; self.addChild(self.potionCountText); self.usePotionBtn = new Text2('USE POTION', { size: 70, fill: 0xA3E635, // Green font: "Impact" // Using a pixel-style font }); self.usePotionBtn.anchor.set(0.5, 0.5); self.usePotionBtn.x = 2048 / 2; self.usePotionBtn.y = self.potionCountText.y + 80; // Position below potion count self.usePotionBtn.visible = false; // Initially hidden self.usePotionBtn.down = function () { if (!self.visible || player.inventory.potionCount <= 0 || player.hp >= player.maxHp) return; player.inventory.potionCount--; player.hp += 20; // Heal amount if (player.hp > player.maxHp) player.hp = player.maxHp; self.updateInventoryDisplay(); playerStatsScreen.updateStatsDisplay(); // Update stats screen if open updateGUI(); // Update main GUI HP display }; self.addChild(self.usePotionBtn); // Gear items display self.gearTitle = new Text2('Equipped Gear:', { size: 70, fill: 0xFFE066, // Gold font: "Impact" // Using a pixel-style font }); self.gearTitle.anchor.set(0, 0.5); self.gearTitle.x = self.panel.x - self.panel.width / 2 + 80; self.gearTitle.y = self.usePotionBtn.y + 120; // Position below potion button self.addChild(self.gearTitle); self.gearItemsText = new Text2('', { size: 60, fill: "#fff", font: "Impact" // Using a pixel-style font }); self.gearItemsText.anchor.set(0, 0); // Anchor to top-left self.gearItemsText.x = self.gearTitle.x + 50; // Indented below title self.gearItemsText.y = self.gearTitle.y + 70; self.addChild(self.gearItemsText); // Close button self.closeBtn = new Text2('CLOSE', { size: 70, fill: "#fff", font: "Impact" // Using a pixel-style font }); self.closeBtn.anchor.set(0.5, 0.5); self.closeBtn.x = 2048 / 2; self.closeBtn.y = self.panel.y + self.panel.height / 2 - 80; self.closeBtn.down = function () { if (!self.visible) return; self.endInventoryScreen(); }; self.addChild(self.closeBtn); // Update the displayed inventory self.updateInventoryDisplay = function () { // Animate gold text if value changed if (typeof self._lastGold !== "undefined" && self._lastGold !== player.gold) { tween(self.goldText, { scaleX: 1.25, scaleY: 1.25 }, { duration: 120, easing: tween.cubicOut, onFinish: function onFinish() { tween(self.goldText, { scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.cubicIn }); } }); } self.goldText.setText('Coins: ' + player.gold); // Animate potion count if changed if (typeof self._lastPotionCount !== "undefined" && self._lastPotionCount !== player.inventory.potionCount) { tween(self.potionCountText, { // Animate potionCountText instead of inventoryItemsText scaleX: 1.25, scaleY: 1.25 }, { duration: 120, easing: tween.cubicOut, onFinish: function onFinish() { tween(self.potionCountText, { // Animate potionCountText scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.cubicIn }); } }); } self.potionCountText.setText('Life Potions: ' + player.inventory.potionCount); var gearText = ''; var gearTypes = ['sword', 'shield', 'helmet', 'armour']; for (var i = 0; i < gearTypes.length; i++) { var type = gearTypes[i]; var item = player.inventory[type]; if (item) { var attributeText = ''; if (type === 'sword') { attributeText = '(+' + item.modifier + ' DMG)'; } else if (type === 'shield' || type === 'helmet' || type === 'armour') { // Calculate defense bonus based on type (matching player.updateEquippedStats) var defenseBonus = 0; if (type === 'shield') defenseBonus = item.modifier; if (type === 'helmet') defenseBonus = Math.floor(item.modifier / 2); if (type === 'armour') defenseBonus = item.modifier; attributeText = '(+' + defenseBonus + ' DEF)'; } gearText += item.label + ' ' + type.charAt(0).toUpperCase() + type.slice(1) + ' ' + attributeText + '\n'; } else { gearText += type.charAt(0).toUpperCase() + type.slice(1) + ': -\n'; } } self.gearItemsText.setText(gearText); // Update gearItemsText // Store last values for next update self._lastGold = player.gold; self._lastPotionCount = player.inventory.potionCount; // Show/hide use potion button if (player.inventory.potionCount > 0 && player.hp < player.maxHp) { self.usePotionBtn.visible = true; self.usePotionBtn.alpha = 1; self.usePotionBtn.interactive = true; } else { self.usePotionBtn.visible = true; self.usePotionBtn.alpha = 0.5; self.usePotionBtn.interactive = false; } }; // Start the inventory screen self.startInventoryScreen = function () { self.visible = true; self.alpha = 0; game.addChild(self); updateGUI(); // Hide main GUI self.updateInventoryDisplay(); // Update inventory when opening // Fade in tween(self, { alpha: 1 }, { duration: 300, easing: tween.easeOut }); }; // End the inventory screen self.endInventoryScreen = function () { self.visible = false; self.alpha = 0; if (self.parent) self.parent.removeChild(self); showGameGUIWithTransition(); }; return self; }); // PlayerStatsScreen: Displays and allows spending skill points var PlayerStatsScreen = Container.expand(function () { var self = Container.call(this); self.visible = false; self.alpha = 0; // Background overlay self.overlay = self.attachAsset('emptyTileCover', { width: 2048, height: 2732, alpha: 0.9 }); // Main panel background self.panel = self.attachAsset('emptyTileBg', { width: 1200, height: 1600, // Adjusted height anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); // Title self.titleText = new Text2('Player Stats', { size: 110, fill: 0xFFE066, font: "Impact" // Using a pixel-style font }); self.titleText.anchor.set(0.5, 0.5); self.titleText.x = 2048 / 2; self.titleText.y = self.panel.y - self.panel.height / 2 + 120; self.addChild(self.titleText); // Stat texts self.hpStatText = new Text2('', { size: 60, fill: 0xA3E635, font: "Impact" // Using a pixel-style font }); self.hpStatText.anchor.set(0, 0.5); self.hpStatText.x = self.panel.x - self.panel.width / 2 + 80; self.hpStatText.y = self.panel.y - 350; // Adjusted Y position self.addChild(self.hpStatText); self.dmgStatText = new Text2('', { size: 60, fill: 0xF87171, font: "Impact" // Using a pixel-style font }); self.dmgStatText.anchor.set(0, 0.5); self.dmgStatText.x = self.panel.x - self.panel.width / 2 + 80; self.dmgStatText.y = self.panel.y - 200; // Adjusted Y position self.addChild(self.dmgStatText); self.defStatText = new Text2('', { size: 60, fill: 0x7DD3FC, font: "Impact" // Using a pixel-style font }); self.defStatText.anchor.set(0, 0.5); self.defStatText.x = self.panel.x - self.panel.width / 2 + 80; self.defStatText.y = self.panel.y - 50; // Adjusted Y position self.addChild(self.defStatText); self.skillPointsText = new Text2('', { size: 60, fill: 0xFFE066, font: "Impact" // Using a pixel-style font }); self.skillPointsText.anchor.set(0.5, 0.5); self.skillPointsText.x = 2048 / 2; self.skillPointsText.y = self.panel.y + 200; // Adjusted Y position self.addChild(self.skillPointsText); // Upgrade buttons self.upgradeBtns = []; var upgradeOptions = [{ label: "+HP & Heal", stat: "hp", color: 0xA3E635 }, { label: "+DMG", stat: "damage", color: 0xF87171 }, { label: "+DEF", stat: "defense", color: 0x7DD3FC }]; for (var i = 0; i < upgradeOptions.length; i++) { var option = upgradeOptions[i]; var btn = new Text2(option.label, { size: 60, fill: option.color, font: "Impact" // Using a pixel-style font }); btn.anchor.set(0.5, 0.5); btn.x = self.panel.x + self.panel.width / 2 - 150; btn.y = self.panel.y - 350 + i * 150; // Adjusted Y position to match new stat positions btn.optionStat = option.stat; // Store the stat this button upgrades btn.visible = false; // Initially hidden btn.down = function () { if (!self.visible || player.skillPoints <= 0) return; player.spendSkillPoint(this.optionStat); self.updateStatsDisplay(); updateGUI(); // Update main GUI as well // Hide buttons if no skill points left if (player.skillPoints <= 0) { self.hideUpgradeButtons(); } }; self.addChild(btn); self.upgradeBtns.push(btn); } // Close button self.closeBtn = new Text2('CLOSE', { size: 70, fill: "#fff", font: "Impact" // Using a pixel-style font }); self.closeBtn.anchor.set(0.5, 0.5); self.closeBtn.x = 2048 / 2; self.closeBtn.y = self.panel.y + self.panel.height / 2 - 80; self.closeBtn.down = function () { if (!self.visible) return; self.endStatsScreen(); }; self.addChild(self.closeBtn); // Update the displayed stats self.updateStatsDisplay = function () { // Animate stat text if value changed if (typeof self._lastHp !== "undefined" && (self._lastHp !== player.hp || self._lastMaxHp !== player.maxHp)) { tween(self.hpStatText, { scaleX: 1.25, scaleY: 1.25 }, { duration: 120, easing: tween.cubicOut, onFinish: function onFinish() { tween(self.hpStatText, { scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.cubicIn }); } }); } if (typeof self._lastDmg !== "undefined" && self._lastDmg !== player.damage) { tween(self.dmgStatText, { scaleX: 1.25, scaleY: 1.25 }, { duration: 120, easing: tween.cubicOut, onFinish: function onFinish() { tween(self.dmgStatText, { scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.cubicIn }); } }); } if (typeof self._lastDef !== "undefined" && self._lastDef !== player.defense) { tween(self.defStatText, { scaleX: 1.25, scaleY: 1.25 }, { duration: 120, easing: tween.cubicOut, onFinish: function onFinish() { tween(self.defStatText, { scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.cubicIn }); } }); } if (typeof self._lastSkillPoints !== "undefined" && self._lastSkillPoints !== player.skillPoints) { tween(self.skillPointsText, { scaleX: 1.25, scaleY: 1.25 }, { duration: 120, easing: tween.cubicOut, onFinish: function onFinish() { tween(self.skillPointsText, { scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.cubicIn }); } }); } self.hpStatText.setText('HP: ' + player.hp + '/' + player.maxHp); self.dmgStatText.setText('DMG: ' + player.damage); self.defStatText.setText('DEF: ' + player.defense); self.skillPointsText.setText('Skill Points: ' + player.skillPoints); // Add player level display at the top left of the panel if (!self.levelText) { self.levelText = new Text2('', { size: 60, fill: 0xA3E635, font: "Impact" // Using a pixel-style font }); self.levelText.anchor.set(0, 0.5); self.levelText.x = self.panel.x - self.panel.width / 2 + 80; self.levelText.y = self.panel.y - self.panel.height / 2 + 230; // Adjusted Y position below the title self.addChild(self.levelText); } self.levelText.setText('LEVEL: ' + player.level); // Store last values for next update self._lastHp = player.hp; self._lastMaxHp = player.maxHp; self._lastDmg = player.damage; self._lastDef = player.defense; self._lastSkillPoints = player.skillPoints; if (player.skillPoints > 0) { self.showUpgradeButtons(); } else { self.hideUpgradeButtons(); } }; // Show upgrade buttons self.showUpgradeButtons = function () { for (var i = 0; i < self.upgradeBtns.length; i++) { self.upgradeBtns[i].visible = true; } }; // Hide upgrade buttons self.hideUpgradeButtons = function () { for (var i = 0; i < self.upgradeBtns.length; i++) { self.upgradeBtns[i].visible = false; } }; // Start the stats screen self.startStatsScreen = function () { self.visible = true; self.alpha = 0; game.addChild(self); updateGUI(); // Hide main GUI self.updateStatsDisplay(); // Update stats when opening // Fade in tween(self, { alpha: 1 }, { duration: 300, easing: tween.easeOut }); }; // End the stats screen self.endStatsScreen = function () { self.visible = false; self.alpha = 0; if (self.parent) self.parent.removeChild(self); showGameGUIWithTransition(); }; return self; }); // ShopScreen: Allows buying and selling items var ShopScreen = Container.expand(function () { var self = Container.call(this); self.visible = false; self.alpha = 0; // Background overlay self.overlay = self.attachAsset('emptyTileCover', { width: 2048, height: 2732, alpha: 0.9 }); // Main panel background self.panel = self.attachAsset('emptyTileBg', { width: 1400, height: 2000, anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); // Title self.titleText = new Text2('Shop', { size: 110, fill: 0xADD8E6, // LightBlue font: "Impact" // Using a pixel-style font }); self.titleText.anchor.set(0.5, 0.5); self.titleText.x = 2048 / 2; self.titleText.y = self.panel.y - self.panel.height / 2 + 120; self.addChild(self.titleText); // Coins display self.coinsText = new Text2('Coins: ' + player.gold, { size: 60, fill: 0xFFD700, font: "Impact" // Using a pixel-style font }); self.coinsText.anchor.set(0, 0.5); self.coinsText.x = self.panel.x - self.panel.width / 2 + 80; self.coinsText.y = self.panel.y - self.panel.height / 2 + 220; self.addChild(self.coinsText); // Items for Sale (simplified for now: only potions) self.itemsForSale = [{ type: "potion", label: "Life Potion", color: 0xA3E635, cost: 10 }]; self.saleButtons = []; var startY = self.coinsText.y + 100; for (var i = 0; i < self.itemsForSale.length; i++) { var item = self.itemsForSale[i]; var buyBtn = new Text2('BUY ' + item.label + ' (' + item.cost + ' Coins)', { size: 60, fill: item.color, font: "Impact" // Using a pixel-style font }); buyBtn.anchor.set(0, 0.5); buyBtn.x = self.panel.x - self.panel.width / 2 + 80; buyBtn.y = startY + i * 100; buyBtn.item = item; buyBtn.down = function () { if (!self.visible) return; var itemToBuy = this.item; if (player.gold >= itemToBuy.cost) { player.gold -= itemToBuy.cost; if (itemToBuy.type === "potion") { player.inventory.potionCount++; } self.updateShopDisplay(); playerInventoryScreen.updateInventoryDisplay(); // Update inventory screen if open // Show confirmation self.showConfirmationText('Bought ' + itemToBuy.label + '!', itemToBuy.color); } else { // Show insufficient funds message self.showConfirmationText('Not enough coins!', 0xFF0000); } }; self.addChild(buyBtn); self.saleButtons.push(buyBtn); } // Items to Sell (simplified for now: gear and potions) self.sellButtons = []; var sellTitle = new Text2('Sell Items:', { size: 70, fill: 0xFFE066, // Yellow font: "Impact" // Using a pixel-style font }); sellTitle.anchor.set(0, 0.5); sellTitle.x = self.panel.x - self.panel.width / 2 + 80; sellTitle.y = startY + self.itemsForSale.length * 100 + 80; self.addChild(sellTitle); self.updateSellButtons = function () { // Clear existing sell buttons for (var i = 0; i < self.sellButtons.length; i++) { self.sellButtons[i].destroy(); } self.sellButtons = []; var currentSellY = sellTitle.y + 80; // Sell gear var gearTypes = ['sword', 'shield', 'helmet', 'armour']; for (var i = 0; i < gearTypes.length; i++) { var gearType = gearTypes[i]; var item = player.inventory[gearType]; if (item) { var sellPrice = item.modifier * 5; // Example sell price var sellBtn = new Text2('SELL ' + item.label + ' ' + gearType.charAt(0).toUpperCase() + gearType.slice(1) + ' (' + sellPrice + ' Coins)', { size: 50, fill: item.color, font: "Impact" // Using a pixel-style font }); sellBtn.anchor.set(0, 0.5); sellBtn.x = self.panel.x - self.panel.width / 2 + 80; sellBtn.y = currentSellY; sellBtn.itemType = gearType; sellBtn.sellPrice = sellPrice; sellBtn.down = function () { if (!self.visible) return; var type = this.itemType; var price = this.sellPrice; player.gold += price; player.inventory[type] = null; // Remove item from inventory player.updateEquippedStats(); // Update player stats self.updateShopDisplay(); playerInventoryScreen.updateInventoryDisplay(); // Update inventory screen if open playerStatsScreen.updateStatsDisplay(); // Update stats screen if open self.updateSellButtons(); // Refresh sell buttons self.showConfirmationText('Sold ' + type.charAt(0).toUpperCase() + type.slice(1) + ' for ' + price + ' coins!', 0xFFD700); }; self.addChild(sellBtn); self.sellButtons.push(sellBtn); currentSellY += 60; } } // Sell potions if (player.inventory.potionCount > 0) { var potionSellPrice = 10; // Example sell price for potion var sellPotionBtn = new Text2('SELL Life Potion (' + potionSellPrice + ' Coins)', { size: 50, fill: 0xA3E635, font: "Impact" // Using a pixel-style font }); sellPotionBtn.anchor.set(0, 0.5); sellPotionBtn.x = self.panel.x - self.panel.width / 2 + 80; sellPotionBtn.y = currentSellY; sellPotionBtn.sellPrice = potionSellPrice; sellPotionBtn.down = function () { if (!self.visible || player.inventory.potionCount <= 0) return; var price = this.sellPrice; player.gold += price; player.inventory.potionCount--; // Remove potion self.updateShopDisplay(); playerInventoryScreen.updateInventoryDisplay(); // Update inventory screen if open self.updateSellButtons(); // Refresh sell buttons self.showConfirmationText('Sold a Life Potion for ' + price + ' coins!', 0xFFD700); }; self.addChild(sellPotionBtn); self.sellButtons.push(sellPotionBtn); currentSellY += 60; } }; // Confirmation text display self._confirmationText = null; self.showConfirmationText = function (message, color) { if (self._confirmationText && typeof self._confirmationText.destroy === 'function') { self._confirmationText.destroy(); } self._confirmationText = new Text2(message, { size: 60, fill: color, font: "Impact" // Using a pixel-style font }); self._confirmationText.anchor.set(0.5, 0.5); self._confirmationText.x = 2048 / 2; self._confirmationText.y = self.panel.y + self.panel.height / 2 - 150; self.addChild(self._confirmationText); tween(self._confirmationText, { alpha: 0 }, { duration: 1000, delay: 800, onFinish: function onFinish() { if (self._confirmationText && typeof self._confirmationText.destroy === 'function') { self._confirmationText.destroy(); } self._confirmationText = null; } }); }; // Close button self.closeBtn = new Text2('CLOSE', { size: 70, fill: "#fff", font: "Impact" // Using a pixel-style font }); self.closeBtn.anchor.set(0.5, 0.5); self.closeBtn.x = 2048 / 2; self.closeBtn.y = self.panel.y + self.panel.height / 2 - 80; self.closeBtn.down = function () { if (!self.visible) return; self.endShopScreen(); }; self.addChild(self.closeBtn); // Update the displayed shop info self.updateShopDisplay = function () { // Animate coins text if value changed if (typeof self._lastGold !== "undefined" && self._lastGold !== player.gold) { tween(self.coinsText, { scaleX: 1.25, scaleY: 1.25 }, { duration: 120, easing: tween.cubicOut, onFinish: function onFinish() { tween(self.coinsText, { scaleX: 1, scaleY: 1 }, { duration: 120, easing: tween.cubicIn }); } }); } self.coinsText.setText('Coins: ' + player.gold); self._lastGold = player.gold; self.updateSellButtons(); // Refresh sell buttons }; // Start the shop screen self.startShopScreen = function () { self.visible = true; self.alpha = 0; game.addChild(self); self.updateShopDisplay(); // Update shop info when opening // Fade in tween(self, { alpha: 1 }, { duration: 300, easing: tween.easeOut }); }; // End the shop screen self.endShopScreen = function () { self.visible = false; self.alpha = 0; if (self._confirmationText && typeof self._confirmationText.destroy === 'function') { self._confirmationText.destroy(); self._confirmationText = null; } if (self.parent) self.parent.removeChild(self); if (monstersLeft <= 0 && currentLevel < LEVELS.length - 1) { levelEndScreen.startLevelEndScreen(); } else { showGameGUIWithTransition(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x22223a }); /**** * Game Code ****/ // New shape for black outline // --- Game Constants --- // Add event listener to MonsterTile for clicking // New asset for game title image MonsterTile.prototype.down = function (x, y, obj) { if (gameOver) return; // Prevent interaction if any overlay screen is visible if (mainScreen.visible || battleScreen.visible || battleScreen.battleItemsMenu.visible || chestScreen.visible || playerStatsScreen.visible || playerInventoryScreen.visible || levelEndScreen.visible || shopScreen.visible) return; // If the monster is defeated, do nothing if (this.defeated) return; // If the monster is not defeated (either initially or after running away), a click should initiate a battle. // Ensure it's revealed before starting the battle. // The reveal() method has an internal check (if (self.revealed) return;) // so it's safe to call even if already revealed. this.reveal(); battleScreen.startBattle(this); }; function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } } function _arrayWithHoles(r) { if (Array.isArray(r)) return r; } var GRID_COLS = 8; var GRID_ROWS = 8; var TILE_SIZE = 180; var GRID_OFFSET_X = Math.floor((2048 - GRID_COLS * TILE_SIZE) / 2); var GRID_OFFSET_Y = Math.floor(2732 * 0.2 + (2732 * 0.6 - GRID_ROWS * TILE_SIZE) / 2); // Center the grid vertically within the middle 60% // --- Level System --- var LEVELS = [{ monsters: 3, monsterStats: { minHp: 1, maxHp: 2, minDmg: 1, maxDmg: 1, minExp: 1, maxExp: 1 } }, { monsters: 5, monsterStats: { minHp: 3, maxHp: 5, minDmg: 2, maxDmg: 3, minExp: 2, maxExp: 3 } }, { monsters: 7, monsterStats: { minHp: 5, maxHp: 8, minDmg: 3, maxDmg: 5, minExp: 3, maxExp: 4 } }]; var currentLevel = 0; var MONSTER_COUNT = LEVELS[currentLevel].monsters; // --- Asset Initialization --- // --- Game State --- var player = new Player(); player.damage = 1; player.baseDamage = 1; player.exp = 0; var grid = []; // 2D array [row][col] var tileObjs = []; // Flat array of all tile objects for easy iteration var monstersLeft = MONSTER_COUNT; var revealedTiles = 0; var totalSafeTiles = GRID_COLS * GRID_ROWS - MONSTER_COUNT; var gameOver = false; var mainScreen = new MainScreen(); // Initialize main screen var background = game.addChild(LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 })); var battleScreen = new BattleScreen(); var chestScreen = new ChestScreen(); var playerStatsScreen = new PlayerStatsScreen(); // Initialize stats screen var playerInventoryScreen = new PlayerInventoryScreen(); // Initialize inventory screen var levelEndScreen = new LevelEndScreen(); // Initialize level end screen var shopScreen = new ShopScreen(); // Initialize shop screen // --- GUI Elements --- var hpText = new Text2('', { size: 60, fill: 0xFF6666, font: "Impact" // Using a pixel-style font }); hpText.anchor.set(0.5, 0); // Anchor to top-center LK.gui.top.addChild(hpText); var expText = new Text2('', { size: 60, fill: 0xFFE066, font: "Impact" // Using a pixel-style font }); expText.anchor.set(0.5, 0); // Anchor to top-center LK.gui.top.addChild(expText); var levelText = new Text2('', { size: 60, fill: 0xA3E635, font: "Impact" // Using a pixel-style font }); levelText.anchor.set(0, 0.5); // Anchor to left-middle LK.gui.top.addChild(levelText); // var monstersLeftText = new Text2('', { // size: 40, // fill: "#fff" // }); // monstersLeftText.anchor.set(0, 0.5); // Anchor to left-middle // LK.gui.top.addChild(monstersLeftText); var statsBtn = new Text2('STATS', { size: 70, fill: "#fff", font: "Impact" // Using a pixel-style font }); statsBtn.anchor.set(1, 1); // Anchor to right-bottom statsBtn.x = -100; // Position to the left of the right edge (adjusted for safe area) statsBtn.y = -40; // Position near the bottom edge statsBtn.visible = false; // Set initial visibility to false statsBtn.down = function () { if (gameOver || battleScreen.visible || chestScreen.visible || playerStatsScreen.visible || playerInventoryScreen.visible) return; // Don't open if another screen is active playerStatsScreen.startStatsScreen(); }; LK.gui.bottom.addChild(statsBtn); var inventoryBtn = new Text2('INVENTORY', { size: 70, fill: "#fff", font: "Impact" // Using a pixel-style font }); inventoryBtn.anchor.set(1, 1); // Anchor to right-bottom inventoryBtn.x = -100 - statsBtn.width - 40; // Position to the left of the right edge (adjusted for safe area and other button) inventoryBtn.y = -40; // Position near the bottom edge inventoryBtn.visible = false; // Set initial visibility to false inventoryBtn.down = function () { if (gameOver || battleScreen.visible || chestScreen.visible || playerStatsScreen.visible || playerInventoryScreen.visible) return; // Don't open if another screen is active playerInventoryScreen.startInventoryScreen(); }; LK.gui.bottom.addChild(inventoryBtn); var levelUpBtn = new Text2('LEVEL UP', { size: 70, fill: "#fff", font: "Impact" // Using a pixel-style font }); levelUpBtn.anchor.set(0.5, 0.5); levelUpBtn.alpha = 0.7; levelUpBtn.visible = false; levelUpBtn.down = function () { if (!this.visible) return; // Old level up choices removed }; LK.gui.top.addChild(levelUpBtn); var managedGameGuiElements = [hpText, expText, levelText, statsBtn, inventoryBtn]; // Old level up choice buttons and logic removed // --- Helper Functions --- // Helper function to show main GUI elements with a fade-in transition function showGameGUIWithTransition() { updateGUI(); // Set visibility and content first for (var i = 0; i < managedGameGuiElements.length; i++) { var el = managedGameGuiElements[i]; // Tween alpha to 1. statsBtn's specific animation in game.update will take over if active. tween(el, { alpha: 1 }, { duration: 300 }); } } function updateGUI() { var showMainGui = !mainScreen.visible && !battleScreen.visible && !(chestScreen && chestScreen.visible) && !(playerStatsScreen && playerStatsScreen.visible) && !(playerInventoryScreen && playerInventoryScreen.visible) && !(levelEndScreen && levelEndScreen.visible) && !(shopScreen && shopScreen.visible); for (var i = 0; i < managedGameGuiElements.length; i++) { var el = managedGameGuiElements[i]; if (showMainGui) { el.visible = true; // Alpha is primarily handled by transitions or default to 1 after transition. // Specific alpha animations (like statsBtn) are handled in game.update. } else { el.visible = false; el.alpha = 0; // Ensure they are fully transparent when hidden } } // Ensure levelUpBtn is also hidden if main GUI is hidden or if it's simply not used levelUpBtn.visible = false; // Specific content updates if GUI is shown if (showMainGui) { hpText.setText('HP: ' + player.hp + '/' + player.maxHp); hpText.x = 450; // Position HP more to the right hpText.y = 50; // Adjusted vertical position var canLevelUp = player.exp >= player.expToLevel; var plusStr = canLevelUp ? " [color=#FFE066]+[/color]" : ""; expText.setText('EXP: ' + player.exp + '/' + player.expToLevel + plusStr); expText.x = 450; // Position EXP more to the right expText.y = 120; // Adjusted vertical position below HP var lvlPlusStr = canLevelUp ? " [color=#FFE066]+[/color]" : ""; levelText.setText('LEVEL: ' + player.level + lvlPlusStr); levelText.x = 0; // Position LVL in the center of the top gui levelText.y = 30; // Position near the top edge levelText.anchor.set(0.5, 0.5); // monstersLeftText logic remains commented out as in original // monstersLeftText.setText('Monsters: ' + monstersLeft); // monstersLeftText.x = levelText.x + levelText.width + 40; // monstersLeftText.y = 60; // Position and style statsBtn and inventoryBtn // Their visibility is already handled by the loop above. var totalBtnWidth = statsBtn.width + inventoryBtn.width + 40; var centerX = 0; // LK.gui.bottom center is x=0, these are added to LK.gui.bottom statsBtn.x = centerX + totalBtnWidth / 2 - statsBtn.width / 2; statsBtn.y = -40; // Position near the bottom edge inventoryBtn.x = centerX - totalBtnWidth / 2 + inventoryBtn.width / 2; inventoryBtn.y = -40; // Position near the bottom edge if (player.skillPoints > 0) { statsBtn.setText('STATS +'); if (statsBtn.style) statsBtn.style.fill = 0xFFE066; // Yellow color if (typeof statsBtn.tint !== "undefined") statsBtn.tint = 0xFFE066; // Set yellow tint // Alpha for statsBtn (pulsing animation) is handled in game.update } else { statsBtn.setText('STATS'); if (statsBtn.style) statsBtn.style.fill = "#fff"; // Default white color if (typeof statsBtn.tint !== "undefined") statsBtn.tint = 0xFFFFFF; // Reset to white tint // Alpha for statsBtn (solid) is handled in game.update or by transition } } else { // If GUI is hidden, and battle screen is the one visible, update its stats if (battleScreen.visible) { battleScreen.updateBattleStats(); } } } // Returns true if (row, col) is inside grid function inBounds(row, col) { return row >= 0 && row < GRID_ROWS && col >= 0 && col < GRID_COLS; } // Get all adjacent tile positions function getAdjacent(row, col) { var adj = []; for (var dr = -1; dr <= 1; dr++) { for (var dc = -1; dc <= 1; dc++) { if (dr === 0 && dc === 0) continue; var nr = row + dr, nc = col + dc; if (inBounds(nr, nc)) adj.push([nr, nc]); } } return adj; } // Reveal empty tiles recursively (flood fill) function revealEmptyTiles(row, col) { var tile = grid[row][col]; if (tile.revealed) return; tile.reveal(); revealedTiles++; if (tile.adjacentMonsterDamage === 0) { var adj = getAdjacent(row, col); for (var i = 0; i < adj.length; i++) { var _adj$i = _slicedToArray(adj[i], 2), nr = _adj$i[0], nc = _adj$i[1]; var t = grid[nr][nc]; if (t instanceof EmptyTile && !t.revealed) { revealEmptyTiles(nr, nc); } } } } // Reveal all monsters (on game over) function revealAllMonsters() { for (var i = 0; i < tileObjs.length; i++) { var t = tileObjs[i]; if (t instanceof MonsterTile && !t.revealed) { t.reveal(); } } } // --- Board Generation --- function generateBoard() { // Clear previous for (var i = 0; i < tileObjs.length; i++) { tileObjs[i].destroy(); } grid = []; tileObjs = []; monstersLeft = LEVELS[currentLevel].monsters; revealedTiles = 0; totalSafeTiles = GRID_COLS * GRID_ROWS - LEVELS[currentLevel].monsters; gameOver = false; // Place monsters var monsterPositions = []; while (monsterPositions.length < LEVELS[currentLevel].monsters) { var r = Math.floor(Math.random() * GRID_ROWS); var c = Math.floor(Math.random() * GRID_COLS); // Prevent monsters from being placed at (0,0) where the chest is always placed if (r === 0 && c === 0) continue; var found = false; for (var i = 0; i < monsterPositions.length; i++) { if (monsterPositions[i][0] === r && monsterPositions[i][1] === c) { found = true; break; } } if (!found) monsterPositions.push([r, c]); } // --- Randomize chest position --- var chestRow = -1, chestCol = -1; while (true) { var tryRow = Math.floor(Math.random() * GRID_ROWS); var tryCol = Math.floor(Math.random() * GRID_COLS); // Chest cannot be placed on a monster var isMonsterCell = false; for (var i = 0; i < monsterPositions.length; i++) { if (monsterPositions[i][0] === tryRow && monsterPositions[i][1] === tryCol) { isMonsterCell = true; break; } } if (!isMonsterCell) { chestRow = tryRow; chestCol = tryCol; break; } } // Build grid var stats = LEVELS[currentLevel].monsterStats; for (var row = 0; row < GRID_ROWS; row++) { grid[row] = []; for (var col = 0; col < GRID_COLS; col++) { var isMonster = false; for (var i = 0; i < monsterPositions.length; i++) { if (monsterPositions[i][0] === row && monsterPositions[i][1] === col) { isMonster = true; break; } } var tile; // Place chest at randomized position if (row === chestRow && col === chestCol) { tile = new ChestTile(); } else if (isMonster) { tile = new MonsterTile(); // Level-based monster stats tile.maxHp = tile.hp = stats.minHp + Math.floor(Math.random() * (stats.maxHp - stats.minHp + 1)); tile.damage = stats.minDmg + Math.floor(Math.random() * (stats.maxDmg - stats.minDmg + 1)); tile.exp = stats.minExp + Math.floor(Math.random() * (stats.maxExp - stats.minExp + 1)); } else { tile = new EmptyTile(); } tile.x = GRID_OFFSET_X + col * TILE_SIZE + TILE_SIZE / 2; tile.y = GRID_OFFSET_Y + row * TILE_SIZE + TILE_SIZE / 2; game.addChild(tile); grid[row][col] = tile; tileObjs.push(tile); } } // Set adjacent monster counts for empty tiles for (var row = 0; row < GRID_ROWS; row++) { for (var col = 0; col < GRID_COLS; col++) { var tile = grid[row][col]; if (tile instanceof EmptyTile) { var adj = getAdjacent(row, col); var damageSum = 0; // Variable to store the sum of damage for (var i = 0; i < adj.length; i++) { var _adj$i2 = _slicedToArray(adj[i], 2), nr = _adj$i2[0], nc = _adj$i2[1]; if (grid[nr][nc] instanceof MonsterTile) { damageSum += grid[nr][nc].damage; // Add monster's damage to the sum } } tile.adjacentMonsterDamage = damageSum; // Store the damage sum } } } } // --- Game Logic --- function handleTileDown(x, y, obj) { if (gameOver) return; if (battleScreen.visible || battleScreen.battleItemsMenu.visible || levelEndScreen.visible || shopScreen.visible || chestScreen.visible || playerStatsScreen.visible || playerInventoryScreen.visible) return; // Don't allow tile interaction if battle screen, battle item menu, level end, shop, chest, stats, or inventory screen is up // Find which tile was pressed for (var i = 0; i < tileObjs.length; i++) { var tile = tileObjs[i]; if (tile.revealed) continue; if (tile.cover && tile.cover.alpha > 0.1) { // Check if (x, y) is inside tile var dx = x - tile.x; var dy = y - tile.y; if (Math.abs(dx) < TILE_SIZE / 2 && Math.abs(dy) < TILE_SIZE / 2) { // Reveal tile if (tile instanceof EmptyTile) { revealEmptyTiles(Math.floor((tile.y - GRID_OFFSET_Y) / TILE_SIZE), Math.floor((tile.x - GRID_OFFSET_X) / TILE_SIZE)); updateGUI(); // Win check // (Removed: revealing all safe tiles no longer triggers win. Level is won only when all monsters are defeated.) } else if (tile instanceof ChestTile) { // Reveal chest and show chest screen tile.reveal(); chestScreen.startChest(tile); } else if (tile instanceof MonsterTile) { // Reveal monster and start battle tile.reveal(); // Reveal the monster visually // Start the battle sequence battleScreen.startBattle(tile); } break; } } } } // --- Level Up UI --- // Old level up UI functions removed // --- Event Handlers --- game.down = function (x, y, obj) { if (mainScreen.visible) return; // Prevent interaction if main screen is visible // The click handling for levelUpBtn and levelUpChoiceBtns // has been moved to their own 'down' event handlers. // Check for tile interactions handleTileDown(x, y, obj); }; game.update = function () { if (mainScreen.visible || battleScreen.visible || battleScreen.battleItemsMenu.visible || chestScreen && chestScreen.visible || playerStatsScreen.visible || playerInventoryScreen.visible || levelEndScreen.visible || shopScreen.visible) return; // Don't update game elements when main, battle, battle item menu, stats, level end, shop, chest, stats, or inventory screen is up // Animate level up button (removed) // if (levelUpBtn.visible) { // levelUpBtn.x = 0; // Centered horizontally relative to LK.gui.top // levelUpBtn.y = 120; // levelUpBtn.alpha = 0.8 + 0.2 * Math.sin(LK.ticks / 20); // } // Animate level up choices (removed) // for (var i = 0; i < levelUpChoiceBtns.length; i++) { // var btn = levelUpChoiceBtns[i]; // if (btn.visible) { // btn.x = (i - 1) * 400; // x relative to LK.gui.bottom // btn.y = -2732 * 0.15; // Position choices above the bottom boundary of the battle area // btn.alpha = 0.9 + 0.1 * Math.sin(LK.ticks / 15 + i); // } // } // Animate stats button if skill points are available if (statsBtn.visible && player.skillPoints > 0) { statsBtn.alpha = 0.8 + 0.2 * Math.sin(LK.ticks / 20); } else if (statsBtn.visible) { statsBtn.alpha = 1; // Solid if no points } if (mainScreen.visible || battleScreen.visible || chestScreen && chestScreen.visible || playerStatsScreen.visible || playerInventoryScreen.visible || levelEndScreen.visible || shopScreen.visible) { for (var i = 0; i < tileObjs.length; i++) { tileObjs[i].visible = false; } background.visible = false; // Hide background } else { for (var i = 0; i < tileObjs.length; i++) { tileObjs[i].visible = true; } background.visible = true; // Show background } }; // --- Start Game --- function startGame() { generateBoard(); showGameGUIWithTransition(); // This will call updateGUI which handles levelText } // Show main screen at start mainScreen.startMainScreen();
===================================================================
--- original.js
+++ change.js
@@ -1058,9 +1058,9 @@
alpha: 0 // Hidden until revealed
});
self.adjText = new Text2('', {
size: 48,
- fill: "#000",
+ fill: "#fff",
font: "Impact" // Using a pixel-style font
});
self.adjText.anchor.set(0.5, 0.5);
self.adjText.alpha = 0;
@@ -2276,12 +2276,12 @@
/****
* Game Code
****/
-// New asset for game title image
-// Add event listener to MonsterTile for clicking
-// --- Game Constants ---
// New shape for black outline
+// --- Game Constants ---
+// Add event listener to MonsterTile for clicking
+// New asset for game title image
MonsterTile.prototype.down = function (x, y, obj) {
if (gameOver) return;
// Prevent interaction if any overlay screen is visible
if (mainScreen.visible || battleScreen.visible || battleScreen.battleItemsMenu.visible || chestScreen.visible || playerStatsScreen.visible || playerInventoryScreen.visible || levelEndScreen.visible || shopScreen.visible) return;