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) { // If not last level, advance to next level if (currentLevel < LEVELS.length - 1) { // Animated level transition screen var transitionContainer = new Container(); transitionContainer.alpha = 0; // Overlay var overlay = LK.getAsset('emptyTileCover', { width: 2048, height: 2732, alpha: 0.92, anchorX: 0, anchorY: 0 }); transitionContainer.addChild(overlay); // "Level Complete!" text var completeText = new Text2('Level Complete!', { size: 120, fill: 0xA3E635 }); completeText.anchor.set(0.5, 0.5); completeText.x = 2048 / 2; completeText.y = 2732 / 2 - 200; transitionContainer.addChild(completeText); // "Entering new dungeon!" text var enteringText = new Text2('Entering new dungeon...', { size: 90, fill: 0xFFE066 }); enteringText.anchor.set(0.5, 0.5); enteringText.x = 2048 / 2; enteringText.y = 2732 / 2 + 100; enteringText.alpha = 0; transitionContainer.addChild(enteringText); // "Level X" text var nextLevelText = new Text2('Level ' + (currentLevel + 2) + '!', { size: 100, fill: 0xA3E635 }); nextLevelText.anchor.set(0.5, 0.5); nextLevelText.x = 2048 / 2; nextLevelText.y = 2732 / 2 + 300; nextLevelText.alpha = 0; transitionContainer.addChild(nextLevelText); game.addChild(transitionContainer); // Fade in overlay and "Level Complete!" tween(transitionContainer, { alpha: 1 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { // Fade in "Entering new dungeon..." tween(enteringText, { alpha: 1 }, { duration: 400, delay: 200, easing: tween.easeIn, onFinish: function onFinish() { // Fade in "Level X" tween(nextLevelText, { alpha: 1 }, { duration: 400, delay: 200, easing: tween.easeIn, onFinish: function onFinish() { // Hold, then fade out all tween(transitionContainer, { alpha: 0 }, { duration: 600, delay: 900, easing: tween.easeIn, onFinish: function onFinish() { transitionContainer.destroy(); // Advance to next level currentLevel++; MONSTER_COUNT = LEVELS[currentLevel].monsters; generateBoard(); updateGUI(); levelText.setText('LVL: ' + (currentLevel + 1)); } }); } }); } }); } }); gameOver = false; } 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); } }; // 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() { // Show "Victory!" message before closing battle screen var victoryText = new Text2('Victory!', { size: 120, fill: 0xA3E635 }); 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: 700, 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 = 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(); // 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.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; }); // 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: 2, scaleY: 2 }); // "You found a chest!" text self.chestText = new Text2('You found a chest!', { size: 90, fill: 0xFFD700 }); self.chestText.anchor.set(0.5, 0.5); self.chestText.x = 2048 / 2; self.chestText.y = 2732 / 2 - 350; 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: 2, scaleY: 2 }); // --- 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._lootQualities = [{ label: "Wooden", modifier: 1, color: 0x8B4513 // SaddleBrown }, { label: "Bronze", modifier: 2, color: 0xCD7F32 // Bronze }, { label: "Silver", modifier: 3, color: 0xC0C0C0 // Silver }]; // 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('emptyTileCover', { width: 900, height: 400, 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: 80, fill: reward.color }); rewardText.anchor.set(0.5, 0.5); rewardText.x = 2048 / 2; rewardText.y = 2732 / 2 - 40; popup.addChild(rewardText); // "OK" button var okBtn = new Text2('OK', { size: 70, fill: "#fff", font: "Impact" }); 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 quality = self._lootQualities[Math.floor(Math.random() * self._lootQualities.length)]; } // Apply reward effect (for now, just heal for potion, add coins, or do nothing for gear) if (reward.type === "potion") { player.heal(3 + Math.floor(Math.random() * 3)); // Heal 3-5 self._showRewardPopup(reward); } else if (reward.type === "coins") { // TODO: Add coins to player inventory self._showRewardPopup(reward); } else if (quality) { // TODO: Add gear to player inventory with quality modifier var lootLabel = quality.label + ' ' + reward.label; self._showRewardPopup({ label: lootLabel, color: quality.color }); } } self._tapCount = 0; } }; // Start chest screen self.startChest = function (chestTile) { self.currentChest = chestTile; self.visible = true; self.alpha = 0; game.addChild(self); // 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); }; 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.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; // 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.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.defense = 1; // Add defense stat self.exp = 0; self.level = 1; self.expToLevel = 3; self.skillPoints = 0; // Add skill points // Level up: gain skill point self.levelUp = function () { if (self.exp < self.expToLevel) return false; self.exp -= self.expToLevel; self.level += 1; self.expToLevel = Math.floor(self.expToLevel * 1.5); self.skillPoints += 1; // Gain a skill point on level up return true; }; // Spend skill point on a stat self.spendSkillPoint = function (stat) { if (self.skillPoints <= 0) return false; if (stat === 'hp') { self.maxHp += 3; self.hp = self.maxHp; // Heal to max on HP upgrade } else if (stat === 'damage') { self.damage += 1; } else if (stat === 'defense') { self.defense += 1; } else { return false; // Invalid stat } self.skillPoints -= 1; 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; // Check for level up after gaining exp while (self.exp >= self.expToLevel) { self.levelUp(); } }; // Heal self.heal = function (amount) { self.hp += amount; if (self.hp > self.maxHp) self.hp = self.maxHp; }; 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, anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); // Tabs container self.tabsContainer = new Container(); self.tabsContainer.x = self.panel.x - self.panel.width / 2; self.tabsContainer.y = self.panel.y - self.panel.height / 2 + 50; // Position tabs above panel content self.addChild(self.tabsContainer); // Stats tab button self.statsTabBtn = new Text2('Stats', { size: 80, fill: 0xFFE066 // Active color }); self.statsTabBtn.anchor.set(0, 0.5); self.statsTabBtn.x = 50; self.statsTabBtn.down = function () { if (!self.visible) return; self.showStatsTab(); }; self.tabsContainer.addChild(self.statsTabBtn); // Inventory tab button self.inventoryTabBtn = new Text2('Inventory', { size: 80, fill: "#fff" // Inactive color }); self.inventoryTabBtn.anchor.set(0, 0.5); self.inventoryTabBtn.x = 300; // Position next to stats tab self.inventoryTabBtn.down = function () { if (!self.visible) return; self.showInventoryTab(); }; self.tabsContainer.addChild(self.inventoryTabBtn); // Stats content container self.statsContent = new Container(); self.statsContent.x = self.panel.x; self.statsContent.y = self.panel.y; self.addChild(self.statsContent); // Inventory content container self.inventoryContent = new Container(); self.inventoryContent.x = self.panel.x; self.inventoryContent.y = self.panel.y; self.addChild(self.inventoryContent); // Title self.titleText = new Text2('Player Stats', { size: 100, fill: 0xFFE066 }); self.titleText.anchor.set(0.5, 0.5); self.titleText.x = 0; self.titleText.y = -self.panel.height / 2 + 120; self.statsContent.addChild(self.titleText); // Stat texts self.hpStatText = new Text2('', { size: 60, fill: 0xA3E635 }); self.hpStatText.anchor.set(0, 0.5); self.hpStatText.x = -self.panel.width / 2 + 80; self.hpStatText.y = -200; self.statsContent.addChild(self.hpStatText); self.dmgStatText = new Text2('', { size: 60, fill: 0xF87171 }); self.dmgStatText.anchor.set(0, 0.5); self.dmgStatText.x = -self.panel.width / 2 + 80; self.dmgStatText.y = 0; self.statsContent.addChild(self.dmgStatText); self.defStatText = new Text2('', { size: 60, fill: 0x7DD3FC }); self.defStatText.anchor.set(0, 0.5); self.defStatText.x = -self.panel.width / 2 + 80; self.defStatText.y = 200; self.statsContent.addChild(self.defStatText); self.skillPointsText = new Text2('', { size: 60, fill: 0xFFE066 }); self.skillPointsText.anchor.set(0.5, 0.5); self.skillPointsText.x = 0; self.skillPointsText.y = self.panel.height / 2 - 200; self.statsContent.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" }); btn.anchor.set(0.5, 0.5); btn.x = self.panel.width / 2 - 150; btn.y = -200 + i * 200; 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.statsContent.addChild(btn); self.upgradeBtns.push(btn); } // Close button self.closeBtn = new Text2('CLOSE', { size: 70, fill: "#fff", font: "Impact" }); 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 () { 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); 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; } }; // Show Stats tab self.showStatsTab = function () { self.statsContent.visible = true; self.inventoryContent.visible = false; self.statsTabBtn.textStyle.fill = 0xFFE066; self.inventoryTabBtn.textStyle.fill = "#fff"; self.titleText.setText('Player Stats'); self.updateStatsDisplay(); // Ensure stats are updated }; // Show Inventory tab self.showInventoryTab = function () { self.statsContent.visible = false; self.inventoryContent.visible = true; self.statsTabBtn.textStyle.fill = "#fff"; self.inventoryTabBtn.textStyle.fill = 0xFFE066; self.titleText.setText('Inventory'); // TODO: Implement inventory display }; // Start the stats screen self.startStatsScreen = function () { self.visible = true; self.alpha = 0; game.addChild(self); self.showStatsTab(); // Show stats tab by default // 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); updateGUI(); // Ensure main GUI is updated when closing }; 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% // --- 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(); 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(); var chestScreen = new ChestScreen(); var playerStatsScreen = new PlayerStatsScreen(); // Initialize stats screen // --- 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 statsBtn = new Text2('STATS', { size: 70, fill: "#fff", font: "Impact" }); statsBtn.anchor.set(1, 0.5); // Anchor to right-middle statsBtn.x = -100; // Position to the left of the right edge (adjusted for safe area) statsBtn.y = 60; // Position near the top edge statsBtn.down = function () { if (gameOver || battleScreen.visible || chestScreen.visible || playerStatsScreen.visible) return; // Don't open if another screen is active playerStatsScreen.startStatsScreen(); }; LK.gui.top.addChild(statsBtn); 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; // Old level up choices removed }; LK.gui.top.addChild(levelUpBtn); // Old level up choice buttons and logic removed // --- Helper Functions --- function updateGUI() { if (battleScreen.visible || playerStatsScreen.visible) { hpText.visible = false; dmgText.visible = false; expText.visible = false; levelText.visible = false; monstersLeftText.visible = false; levelUpBtn.visible = false; statsBtn.visible = false; // Hide stats button // levelUpChoiceBtns removed // 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; statsBtn.visible = true; // Show stats button } 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 // Hide level up button (replaced by stats screen) levelUpBtn.visible = false; // Show stats button and optionally indicate available skill points statsBtn.visible = true; if (player.skillPoints > 0) { statsBtn.alpha = 0.8 + 0.2 * Math.sin(LK.ticks / 20); // Animate if points available } else { statsBtn.alpha = 1; } } // 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 = 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); 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 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; // Always place a chest at (0,0) for dev if (row === 0 && col === 0) { 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 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 // (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) { // 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 || playerStatsScreen.visible) return; // Don't update game elements when battle or stats 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 (battleScreen.visible || chestScreen && chestScreen.visible || playerStatsScreen.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(); levelText.setText('LVL: ' + (currentLevel + 1)); updateGUI();
===================================================================
--- original.js
+++ change.js
@@ -1051,19 +1051,19 @@
// Show Stats tab
self.showStatsTab = function () {
self.statsContent.visible = true;
self.inventoryContent.visible = false;
- self.statsTabBtn.style.fill = 0xFFE066;
- self.inventoryTabBtn.style.fill = "#fff";
+ self.statsTabBtn.textStyle.fill = 0xFFE066;
+ self.inventoryTabBtn.textStyle.fill = "#fff";
self.titleText.setText('Player Stats');
self.updateStatsDisplay(); // Ensure stats are updated
};
// Show Inventory tab
self.showInventoryTab = function () {
self.statsContent.visible = false;
self.inventoryContent.visible = true;
- self.statsTabBtn.style.fill = "#fff";
- self.inventoryTabBtn.style.fill = 0xFFE066;
+ self.statsTabBtn.textStyle.fill = "#fff";
+ self.inventoryTabBtn.textStyle.fill = 0xFFE066;
self.titleText.setText('Inventory');
// TODO: Implement inventory display
};
// Start the stats screen