User prompt
When a sword or defensive gear is added,show it in the player stats and also increase either dmg or defense depending on the geat the player has.
User prompt
No loot dropped is appearign when the battle starts also, something is wrong.
User prompt
No loot dropped is being displayed even after battle ends, can you try fix it.
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'filter')' in or related to this line: 'var lootTypes = battleScreen._rewardTypes.filter(function (type) {' Line Number: 857
User prompt
monters can also drop loot. ifmonster drops loot, it should be announced on battle end screen
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'self.statsTabBtn.textStyle.fill = 0xFFE066;' Line Number: 1095
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'self.statsTabBtn.style.fill = 0xFFE066;' Line Number: 1095
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'self.statsTabBtn.textStyle.fill = 0xFFE066;' Line Number: 1095
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'self.statsTabBtn.style.fill = 0xFFE066;' Line Number: 1095
User prompt
stats should have two tabs. one with the actaulplayer stats and the other with inventory including itemsand coins.
User prompt
When a monster is killed they can drop coins, or loot, like swords, shiedls, armor or helmets. the lower the level of enemy the worst the quality of loot. Initially, lets just create some loot. Wood, bronze an silver for each item, adding +1 +2 +3 to def or attack depending on what they are for.
User prompt
Lets add more rpg elements to othe game. So lets add a button where the player can open up the player stats. Player stats will be DEF, ATK, and HP. Will start with HP=10 and DEF and ATK(DMG) =1. Once the player levels up, they can distribute skill points to either of this categories, and heal their life. this will replace the previous level up options.
User prompt
on chest screen, initially just double click on chest to open it. we will then add more keys, or lockpicks. Chests can have life potions, coins, or gear like shields, swords, helmets and armour.
User prompt
first exp upgrade shoudlbe at 3 exp points
User prompt
Make sure lvl is updated in gui when level changes
User prompt
Add transition and animated screen from one level to another. Something like Level complete! and Entering new dungeon! or however feelsbetter. βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Level is complete when all monsters are defeated, even if a cell is not uncoveredor chest not opened
User prompt
first level shoudl be easier
User prompt
add 2 more levels that are more difficult and have also new more difficult enemies.
User prompt
add a a chest image
User prompt
only have 3 monsters in level 1
User prompt
add chests in the grid. Just for development purposes always add a chest on the top left cell, will remove it later. when a chest is found, open a screen similar to battle screen, but instead will say, you found a chest, and then add the player on the bottom like in the battle screen and a chest in the center. we will then add a minigame to try open the game. βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
After monster attacks, I do not see the attack option on the player
User prompt
In the battle screen, when enemy is destroyed, do not just close screen,but also add a message saying Victroy! or something and then close.
User prompt
Player shoudl start with 1 dmg
/**** * 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 + 200, // Position near top-right corner within the battle area scaleX: 2, scaleY: 2 }); // Monster stats text self.monsterStatText = new Text2('', { size: 60, fill: "#fff" }); 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 / 2 + 60; // Move further below monster image self.addChild(self.monsterStatText); // Player stats text (in battle) self.playerBattleStatText = new Text2('', { size: 60, fill: "#fff" }); 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 - 200, // Position near bottom-left corner within the battle area scaleX: 2, scaleY: 2 }); self.playerBattleStatText.anchor.set(0.5, 0); // Anchor to top-center // Set stat text position to where hero will appear self.playerBattleStatText.x = 300; self.playerBattleStatText.y = 2732 * 0.8 - 200 + 100 + 60; // 100 is half hero height at scale 2 self.addChild(self.playerBattleStatText); // Action button (Attack) self.attackBtn = new Text2('ATTACK', { size: 80, fill: "#fff", font: "Impact" }); self.attackBtn.anchor.set(0.5, 0.5); self.attackBtn.x = 2048 / 2; self.attackBtn.y = 2732 - 300; // Position near bottom self.addChild(self.attackBtn); self.attackBtn.down = function () { if (!self.visible) return; self.playerTurn(); }; self.currentMonster = null; // Reference to the monster tile being fought // Start battle self.startBattle = function (monsterTile) { self.currentMonster = monsterTile; self.visible = true; self.alpha = 0; game.addChild(self); // Add battle screen to game // 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; // 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: 90, fill: 0xFFE066 }); 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 - 200; var monsterTargetX = 2048 - 300; var monsterTargetY = 2732 * 0.2 + 200; 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 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: 90, fill: firstTextColor }); 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 self.attackBtn.visible = true; if (firstAttacker === "monster") { LK.setTimeout(self.monsterTurn, 500); } // 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; if (win) { self.currentMonster.defeat(); monstersLeft--; player.gainExp(self.currentMonster.exp); updateGUI(); if (monstersLeft <= 0) { 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); } }; // Player's turn self.playerTurn = function () { if (!self.currentMonster) return; // Animate player attack: slide playerDisplay forward, then back, then apply damage var originalX = self.playerDisplay.x; var attackX = originalX + 220; self.attackBtn.visible = false; // Hide attack button during animation // Show "Player Attacks!" text var playerAttackText = new Text2('Player Attacks!', { size: 80, fill: 0xffe066 }); 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: 90, fill: 0xff0000 }); 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() { self.endBattle(true); // Player wins // Reset monsterDisplay for next battle self.monsterDisplay.alpha = 1; self.monsterDisplay.scaleX = 2; self.monsterDisplay.scaleY = 2; } }); } else { // Monster survives, monster's turn after a delay LK.setTimeout(self.monsterTurn, 500); self.attackBtn.visible = true; } } // Animate player forward, then back, then do attack tween(self.playerDisplay, { x: attackX }, { duration: 180, easing: tween.cubicOut, onFinish: function onFinish() { tween(self.playerDisplay, { x: originalX }, { duration: 180, easing: tween.cubicIn, onFinish: doAttack }); } }); }; // Monster's turn self.monsterTurn = function () { if (!self.currentMonster) return; // Animate monster attack: slide monsterDisplay forward, then back, then apply damage var originalX = self.monsterDisplay.x; var attackX = originalX - 220; // Slide left toward player self.attackBtn.visible = false; // Hide attack button during animation // Show "Monster Attacks!" text var monsterAttackText = new Text2('Monster Attacks!', { size: 80, fill: 0xff0000 }); 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 // Fade out attack text tween(monsterAttackText, { alpha: 0 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { monsterAttackText.destroy(); } }); self.updateBattleStats(); if (player.hp <= 0) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); gameOver = true; revealAllMonsters(); self.endBattle(false); // Player loses } } // Animate monster forward, then back, then do attack tween(self.monsterDisplay, { x: attackX }, { duration: 180, easing: tween.cubicOut, onFinish: function onFinish() { tween(self.monsterDisplay, { x: originalX }, { duration: 180, easing: tween.cubicIn, onFinish: doMonsterAttack }); } }); }; return self; }); // EmptyTile: Represents a safe tile (no monster) var EmptyTile = Container.expand(function () { var self = Container.call(this); self.revealed = false; self.adjacentMonsters = 0; 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" }); 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.adjacentMonsters > 0) { self.adjText.setText(self.adjacentMonsters + ''); self.adjText.alpha = 1; } }; 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.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" }); 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; }; return self; }); // Player: Holds player stats and methods var Player = Container.expand(function () { var self = Container.call(this); self.maxHp = 10; self.hp = 10; self.damage = 1; self.exp = 0; self.level = 1; self.expToLevel = 5; // Level up: spend exp to increase stats or heal self.levelUp = function (choice) { if (self.exp < self.expToLevel) return false; self.exp -= self.expToLevel; self.level += 1; self.expToLevel = Math.floor(self.expToLevel * 1.5); if (choice === 'hp') { self.maxHp += 3; self.hp = self.maxHp; } else if (choice === 'damage') { self.damage += 1; } else if (choice === 'heal') { self.hp = self.maxHp; } return true; }; // Take damage self.takeDamage = function (amount) { self.hp -= amount; if (self.hp < 0) self.hp = 0; // Show animated -X text above player in battle if (battleScreen && battleScreen.visible && battleScreen.playerDisplay) { var dmgText = new Text2('-' + amount, { size: 90, fill: 0xff0000 }); dmgText.anchor.set(0.5, 1); // Position above player sprite dmgText.x = battleScreen.playerDisplay.x; dmgText.y = battleScreen.playerDisplay.y - battleScreen.playerDisplay.height / 2 - 20; battleScreen.addChild(dmgText); // Animate: pop up and fade out tween(dmgText, { y: dmgText.y - 80, alpha: 0 }, { duration: 700, easing: tween.cubicOut, onFinish: function onFinish() { dmgText.destroy(); } }); } }; // Gain exp self.gainExp = function (amount) { self.exp += amount; }; // Heal self.heal = function (amount) { self.hp += amount; if (self.hp > self.maxHp) self.hp = self.maxHp; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x22223a }); /**** * Game Code ****/ // --- Game Constants --- 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% var MONSTER_COUNT = 12; // --- Asset Initialization --- // --- Game State --- var player = new Player(); 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 battleScreen = new BattleScreen(); // --- GUI Elements --- var hpText = new Text2('', { size: 40, fill: 0xFF6666 }); hpText.anchor.set(0.5, 1); // Anchor to bottom-center LK.gui.bottom.addChild(hpText); var dmgText = new Text2('', { size: 40, fill: "#fff" }); dmgText.anchor.set(0.5, 1); // Anchor to bottom-center LK.gui.bottom.addChild(dmgText); var expText = new Text2('', { size: 40, fill: 0xFFE066 }); expText.anchor.set(0.5, 1); // Anchor to bottom-center LK.gui.bottom.addChild(expText); var levelText = new Text2('', { size: 40, fill: 0xA3E635 }); 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 levelUpBtn = new Text2('LEVEL UP', { size: 70, fill: "#fff", font: "Impact" }); levelUpBtn.anchor.set(0.5, 0.5); levelUpBtn.alpha = 0.7; levelUpBtn.visible = false; levelUpBtn.down = function () { if (!this.visible) return; showLevelUpChoices(); }; LK.gui.top.addChild(levelUpBtn); var levelUpChoiceBtns = []; var levelUpChoices = [{ label: "Max HP +3 & Heal", value: "hp" }, { label: "Damage +1", value: "damage" }, { label: "Heal to Max", value: "heal" }]; for (var i = 0; i < levelUpChoices.length; i++) { var btn = new Text2(levelUpChoices[i].label, { size: 60, fill: "#fff", font: "Impact" }); btn.anchor.set(0.5, 0.5); btn.visible = false; btn.alpha = 0.9; // Capture the current choice for this button's event handler var currentChoice = levelUpChoices[i]; btn.down = function () { if (!this.visible) return; player.levelUp(currentChoice.value); hideLevelUpChoices(); updateGUI(); // If player still has enough EXP, show the level up button again if (!gameOver && player.exp >= player.expToLevel) { levelUpBtn.visible = true; levelUpBtn.alpha = 1; } }; LK.gui.bottom.addChild(btn); levelUpChoiceBtns.push(btn); } // --- Helper Functions --- function updateGUI() { if (battleScreen.visible) { hpText.visible = false; dmgText.visible = false; expText.visible = false; levelText.visible = false; monstersLeftText.visible = false; levelUpBtn.visible = false; for (var i = 0; i < levelUpChoiceBtns.length; i++) { levelUpChoiceBtns[i].visible = false; } battleScreen.updateBattleStats(); // Update battle screen stats when visible return; } else { hpText.visible = true; dmgText.visible = true; expText.visible = true; levelText.visible = true; monstersLeftText.visible = true; } hpText.setText('HP: ' + player.hp + '/' + player.maxHp); hpText.x = -200; // Position to the left of center hpText.y = -40; // Position near the bottom edge dmgText.setText('DMG: ' + player.damage); dmgText.x = 0; // Center horizontally dmgText.y = -40; // Position near the bottom edge expText.setText('EXP: ' + player.exp + '/' + player.expToLevel); expText.x = 200; // Position to the right of center expText.y = -40; // Position near the bottom edge // Position levelText and monstersLeftText side-by-side at the top-center, accounting for 100x100 top-left area levelText.setText('LVL: ' + player.level); levelText.x = -100; // Position to the left of center (adjusted for safe area) levelText.y = 60; // Position near the top edge monstersLeftText.setText('Monsters: ' + monstersLeft); monstersLeftText.x = 100; // Position to the right of center monstersLeftText.y = 60; // Position near the top edge // Show level up button if enough exp if (!gameOver && player.exp >= player.expToLevel && !levelUpChoiceBtns.some(function (btn) { return btn.visible; })) { levelUpBtn.visible = true; levelUpBtn.alpha = 1; } else { levelUpBtn.visible = false; } } // 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.adjacentMonsters === 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 = MONSTER_COUNT; revealedTiles = 0; totalSafeTiles = GRID_COLS * GRID_ROWS - MONSTER_COUNT; gameOver = false; // Place monsters var monsterPositions = []; while (monsterPositions.length < MONSTER_COUNT) { var r = Math.floor(Math.random() * GRID_ROWS); var c = Math.floor(Math.random() * GRID_COLS); var key = r + ',' + c; 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]); } // Build grid 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; if (isMonster) { tile = new MonsterTile(); // Randomize monster stats a bit tile.maxHp = tile.hp = 1 + Math.floor(Math.random() * (1 + player.level)); tile.damage = 1 + Math.floor(Math.random() * (1 + Math.floor(player.level / 2))); tile.exp = 1 + Math.floor(Math.random() * 2); } 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 count = 0; 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) count++; } tile.adjacentMonsters = count; } } } } // --- Game Logic --- function handleTileDown(x, y, obj) { if (gameOver) return; if (battleScreen.visible) return; // Don't allow tile interaction if battle 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 if (revealedTiles >= totalSafeTiles) { LK.showYouWin(); gameOver = true; revealAllMonsters(); } } 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 --- function showLevelUpChoices() { levelUpBtn.visible = false; for (var i = 0; i < levelUpChoiceBtns.length; i++) { var btn = levelUpChoiceBtns[i]; btn.visible = true; btn.x = (i - 1) * 400; // x relative to LK.gui.bottom (which is screen center horizontally) btn.y = -2732 * 0.15; // Position choices above the bottom boundary of the battle area btn.alpha = 1; } } function hideLevelUpChoices() { for (var i = 0; i < levelUpChoiceBtns.length; i++) { levelUpChoiceBtns[i].visible = false; } } // --- Event Handlers --- game.down = function (x, y, obj) { // 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 (battleScreen.visible) return; // Don't update game elements when battle screen is up // Animate level up button 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 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); } } if (battleScreen.visible) { for (var i = 0; i < tileObjs.length; i++) { tileObjs[i].visible = false; } } else { for (var i = 0; i < tileObjs.length; i++) { tileObjs[i].visible = true; } } }; // --- Start Game --- generateBoard(); updateGUI();
/****
* 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 + 200,
// Position near top-right corner within the battle area
scaleX: 2,
scaleY: 2
});
// Monster stats text
self.monsterStatText = new Text2('', {
size: 60,
fill: "#fff"
});
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 / 2 + 60; // Move further below monster image
self.addChild(self.monsterStatText);
// Player stats text (in battle)
self.playerBattleStatText = new Text2('', {
size: 60,
fill: "#fff"
});
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 - 200,
// Position near bottom-left corner within the battle area
scaleX: 2,
scaleY: 2
});
self.playerBattleStatText.anchor.set(0.5, 0); // Anchor to top-center
// Set stat text position to where hero will appear
self.playerBattleStatText.x = 300;
self.playerBattleStatText.y = 2732 * 0.8 - 200 + 100 + 60; // 100 is half hero height at scale 2
self.addChild(self.playerBattleStatText);
// Action button (Attack)
self.attackBtn = new Text2('ATTACK', {
size: 80,
fill: "#fff",
font: "Impact"
});
self.attackBtn.anchor.set(0.5, 0.5);
self.attackBtn.x = 2048 / 2;
self.attackBtn.y = 2732 - 300; // Position near bottom
self.addChild(self.attackBtn);
self.attackBtn.down = function () {
if (!self.visible) return;
self.playerTurn();
};
self.currentMonster = null; // Reference to the monster tile being fought
// Start battle
self.startBattle = function (monsterTile) {
self.currentMonster = monsterTile;
self.visible = true;
self.alpha = 0;
game.addChild(self); // Add battle screen to game
// 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;
// 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: 90,
fill: 0xFFE066
});
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 - 200;
var monsterTargetX = 2048 - 300;
var monsterTargetY = 2732 * 0.2 + 200;
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
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: 90,
fill: firstTextColor
});
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
self.attackBtn.visible = true;
if (firstAttacker === "monster") {
LK.setTimeout(self.monsterTurn, 500);
}
// 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;
if (win) {
self.currentMonster.defeat();
monstersLeft--;
player.gainExp(self.currentMonster.exp);
updateGUI();
if (monstersLeft <= 0) {
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);
}
};
// Player's turn
self.playerTurn = function () {
if (!self.currentMonster) return;
// Animate player attack: slide playerDisplay forward, then back, then apply damage
var originalX = self.playerDisplay.x;
var attackX = originalX + 220;
self.attackBtn.visible = false; // Hide attack button during animation
// Show "Player Attacks!" text
var playerAttackText = new Text2('Player Attacks!', {
size: 80,
fill: 0xffe066
});
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: 90,
fill: 0xff0000
});
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() {
self.endBattle(true); // Player wins
// Reset monsterDisplay for next battle
self.monsterDisplay.alpha = 1;
self.monsterDisplay.scaleX = 2;
self.monsterDisplay.scaleY = 2;
}
});
} else {
// Monster survives, monster's turn after a delay
LK.setTimeout(self.monsterTurn, 500);
self.attackBtn.visible = true;
}
}
// Animate player forward, then back, then do attack
tween(self.playerDisplay, {
x: attackX
}, {
duration: 180,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(self.playerDisplay, {
x: originalX
}, {
duration: 180,
easing: tween.cubicIn,
onFinish: doAttack
});
}
});
};
// Monster's turn
self.monsterTurn = function () {
if (!self.currentMonster) return;
// Animate monster attack: slide monsterDisplay forward, then back, then apply damage
var originalX = self.monsterDisplay.x;
var attackX = originalX - 220; // Slide left toward player
self.attackBtn.visible = false; // Hide attack button during animation
// Show "Monster Attacks!" text
var monsterAttackText = new Text2('Monster Attacks!', {
size: 80,
fill: 0xff0000
});
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
// Fade out attack text
tween(monsterAttackText, {
alpha: 0
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
monsterAttackText.destroy();
}
});
self.updateBattleStats();
if (player.hp <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
gameOver = true;
revealAllMonsters();
self.endBattle(false); // Player loses
}
}
// Animate monster forward, then back, then do attack
tween(self.monsterDisplay, {
x: attackX
}, {
duration: 180,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(self.monsterDisplay, {
x: originalX
}, {
duration: 180,
easing: tween.cubicIn,
onFinish: doMonsterAttack
});
}
});
};
return self;
});
// EmptyTile: Represents a safe tile (no monster)
var EmptyTile = Container.expand(function () {
var self = Container.call(this);
self.revealed = false;
self.adjacentMonsters = 0;
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"
});
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.adjacentMonsters > 0) {
self.adjText.setText(self.adjacentMonsters + '');
self.adjText.alpha = 1;
}
};
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.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"
});
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;
};
return self;
});
// Player: Holds player stats and methods
var Player = Container.expand(function () {
var self = Container.call(this);
self.maxHp = 10;
self.hp = 10;
self.damage = 1;
self.exp = 0;
self.level = 1;
self.expToLevel = 5;
// Level up: spend exp to increase stats or heal
self.levelUp = function (choice) {
if (self.exp < self.expToLevel) return false;
self.exp -= self.expToLevel;
self.level += 1;
self.expToLevel = Math.floor(self.expToLevel * 1.5);
if (choice === 'hp') {
self.maxHp += 3;
self.hp = self.maxHp;
} else if (choice === 'damage') {
self.damage += 1;
} else if (choice === 'heal') {
self.hp = self.maxHp;
}
return true;
};
// Take damage
self.takeDamage = function (amount) {
self.hp -= amount;
if (self.hp < 0) self.hp = 0;
// Show animated -X text above player in battle
if (battleScreen && battleScreen.visible && battleScreen.playerDisplay) {
var dmgText = new Text2('-' + amount, {
size: 90,
fill: 0xff0000
});
dmgText.anchor.set(0.5, 1);
// Position above player sprite
dmgText.x = battleScreen.playerDisplay.x;
dmgText.y = battleScreen.playerDisplay.y - battleScreen.playerDisplay.height / 2 - 20;
battleScreen.addChild(dmgText);
// Animate: pop up and fade out
tween(dmgText, {
y: dmgText.y - 80,
alpha: 0
}, {
duration: 700,
easing: tween.cubicOut,
onFinish: function onFinish() {
dmgText.destroy();
}
});
}
};
// Gain exp
self.gainExp = function (amount) {
self.exp += amount;
};
// Heal
self.heal = function (amount) {
self.hp += amount;
if (self.hp > self.maxHp) self.hp = self.maxHp;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x22223a
});
/****
* Game Code
****/
// --- Game Constants ---
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%
var MONSTER_COUNT = 12;
// --- Asset Initialization ---
// --- Game State ---
var player = new Player();
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 battleScreen = new BattleScreen();
// --- GUI Elements ---
var hpText = new Text2('', {
size: 40,
fill: 0xFF6666
});
hpText.anchor.set(0.5, 1); // Anchor to bottom-center
LK.gui.bottom.addChild(hpText);
var dmgText = new Text2('', {
size: 40,
fill: "#fff"
});
dmgText.anchor.set(0.5, 1); // Anchor to bottom-center
LK.gui.bottom.addChild(dmgText);
var expText = new Text2('', {
size: 40,
fill: 0xFFE066
});
expText.anchor.set(0.5, 1); // Anchor to bottom-center
LK.gui.bottom.addChild(expText);
var levelText = new Text2('', {
size: 40,
fill: 0xA3E635
});
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 levelUpBtn = new Text2('LEVEL UP', {
size: 70,
fill: "#fff",
font: "Impact"
});
levelUpBtn.anchor.set(0.5, 0.5);
levelUpBtn.alpha = 0.7;
levelUpBtn.visible = false;
levelUpBtn.down = function () {
if (!this.visible) return;
showLevelUpChoices();
};
LK.gui.top.addChild(levelUpBtn);
var levelUpChoiceBtns = [];
var levelUpChoices = [{
label: "Max HP +3 & Heal",
value: "hp"
}, {
label: "Damage +1",
value: "damage"
}, {
label: "Heal to Max",
value: "heal"
}];
for (var i = 0; i < levelUpChoices.length; i++) {
var btn = new Text2(levelUpChoices[i].label, {
size: 60,
fill: "#fff",
font: "Impact"
});
btn.anchor.set(0.5, 0.5);
btn.visible = false;
btn.alpha = 0.9;
// Capture the current choice for this button's event handler
var currentChoice = levelUpChoices[i];
btn.down = function () {
if (!this.visible) return;
player.levelUp(currentChoice.value);
hideLevelUpChoices();
updateGUI();
// If player still has enough EXP, show the level up button again
if (!gameOver && player.exp >= player.expToLevel) {
levelUpBtn.visible = true;
levelUpBtn.alpha = 1;
}
};
LK.gui.bottom.addChild(btn);
levelUpChoiceBtns.push(btn);
}
// --- Helper Functions ---
function updateGUI() {
if (battleScreen.visible) {
hpText.visible = false;
dmgText.visible = false;
expText.visible = false;
levelText.visible = false;
monstersLeftText.visible = false;
levelUpBtn.visible = false;
for (var i = 0; i < levelUpChoiceBtns.length; i++) {
levelUpChoiceBtns[i].visible = false;
}
battleScreen.updateBattleStats(); // Update battle screen stats when visible
return;
} else {
hpText.visible = true;
dmgText.visible = true;
expText.visible = true;
levelText.visible = true;
monstersLeftText.visible = true;
}
hpText.setText('HP: ' + player.hp + '/' + player.maxHp);
hpText.x = -200; // Position to the left of center
hpText.y = -40; // Position near the bottom edge
dmgText.setText('DMG: ' + player.damage);
dmgText.x = 0; // Center horizontally
dmgText.y = -40; // Position near the bottom edge
expText.setText('EXP: ' + player.exp + '/' + player.expToLevel);
expText.x = 200; // Position to the right of center
expText.y = -40; // Position near the bottom edge
// Position levelText and monstersLeftText side-by-side at the top-center, accounting for 100x100 top-left area
levelText.setText('LVL: ' + player.level);
levelText.x = -100; // Position to the left of center (adjusted for safe area)
levelText.y = 60; // Position near the top edge
monstersLeftText.setText('Monsters: ' + monstersLeft);
monstersLeftText.x = 100; // Position to the right of center
monstersLeftText.y = 60; // Position near the top edge
// Show level up button if enough exp
if (!gameOver && player.exp >= player.expToLevel && !levelUpChoiceBtns.some(function (btn) {
return btn.visible;
})) {
levelUpBtn.visible = true;
levelUpBtn.alpha = 1;
} else {
levelUpBtn.visible = false;
}
}
// 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.adjacentMonsters === 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 = MONSTER_COUNT;
revealedTiles = 0;
totalSafeTiles = GRID_COLS * GRID_ROWS - MONSTER_COUNT;
gameOver = false;
// Place monsters
var monsterPositions = [];
while (monsterPositions.length < MONSTER_COUNT) {
var r = Math.floor(Math.random() * GRID_ROWS);
var c = Math.floor(Math.random() * GRID_COLS);
var key = r + ',' + c;
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]);
}
// Build grid
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;
if (isMonster) {
tile = new MonsterTile();
// Randomize monster stats a bit
tile.maxHp = tile.hp = 1 + Math.floor(Math.random() * (1 + player.level));
tile.damage = 1 + Math.floor(Math.random() * (1 + Math.floor(player.level / 2)));
tile.exp = 1 + Math.floor(Math.random() * 2);
} 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 count = 0;
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) count++;
}
tile.adjacentMonsters = count;
}
}
}
}
// --- Game Logic ---
function handleTileDown(x, y, obj) {
if (gameOver) return;
if (battleScreen.visible) return; // Don't allow tile interaction if battle 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
if (revealedTiles >= totalSafeTiles) {
LK.showYouWin();
gameOver = true;
revealAllMonsters();
}
} 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 ---
function showLevelUpChoices() {
levelUpBtn.visible = false;
for (var i = 0; i < levelUpChoiceBtns.length; i++) {
var btn = levelUpChoiceBtns[i];
btn.visible = true;
btn.x = (i - 1) * 400; // x relative to LK.gui.bottom (which is screen center horizontally)
btn.y = -2732 * 0.15; // Position choices above the bottom boundary of the battle area
btn.alpha = 1;
}
}
function hideLevelUpChoices() {
for (var i = 0; i < levelUpChoiceBtns.length; i++) {
levelUpChoiceBtns[i].visible = false;
}
}
// --- Event Handlers ---
game.down = function (x, y, obj) {
// 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 (battleScreen.visible) return; // Don't update game elements when battle screen is up
// Animate level up button
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
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);
}
}
if (battleScreen.visible) {
for (var i = 0; i < tileObjs.length; i++) {
tileObjs[i].visible = false;
}
} else {
for (var i = 0; i < tileObjs.length; i++) {
tileObjs[i].visible = true;
}
}
};
// --- Start Game ---
generateBoard();
updateGUI();