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;
// Loot text (initially hidden)
self.lootText = new Text2('', {
size: 60,
fill: "#fff"
});
self.lootText.anchor.set(0.5, 0.5);
self.lootText.x = 2048 / 2;
self.lootText.y = 2732 / 2 + 50; // Position below victory text
self.lootText.visible = false;
self.addChild(self.lootText);
// 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 (self.lootText) {
// Ensure lootText exists before accessing
self.lootText.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;
if (self.lootText) {
// Ensure lootText exists before destroying
self.lootText.destroy();
self.lootText = 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 - 100;
self.addChild(victoryText);
// Add text for loot dropped, if any
var lootLabel = self.currentMonster.droppedLoot ? 'Dropped: ' + self.currentMonster.droppedLoot.label : 'No loot dropped.';
var lootText = new Text2(lootLabel, {
size: 60,
fill: self.currentMonster.droppedLoot ? self.currentMonster.droppedLoot.color : "#fff"
});
lootText.anchor.set(0.5, 0.5);
lootText.x = 2048 / 2;
lootText.y = 2732 / 2 + 50;
self.addChild(lootText);
victoryText.alpha = 0;
// Show loot text after victory text appears
if (self.currentMonster.droppedLoot) {
self.lootText.setText('Dropped: ' + self.currentMonster.droppedLoot.label);
self.lootText.fill = self.currentMonster.droppedLoot.color;
self.lootText.visible = true;
} else {
self.lootText.setText('No loot dropped.');
self.lootText.fill = "#fff";
self.lootText.visible = true;
}
// 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.droppedLoot = null; // Initialize dropped loot property
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;
// Determine if monster drops loot and what kind
if (Math.random() < 0.3) {
// 30% chance to drop loot
// Ensure battleScreen exists before accessing its properties
var lootQualities = battleScreen ? battleScreen._lootQualities : []; // Access loot qualities from battle screen
// Ensure _rewardTypes exists before filtering
var lootTypes = battleScreen && battleScreen._rewardTypes ? battleScreen._rewardTypes.filter(function (type) {
return type.type !== "potion" && type.type !== "coins";
}) : []; // Only gear types
if (lootTypes.length > 0 && lootQualities.length > 0) {
var randomType = lootTypes[Math.floor(Math.random() * lootTypes.length)];
var randomQuality = lootQualities[Math.floor(Math.random() * lootQualities.length)];
self.droppedLoot = {
label: randomQuality.label + ' ' + randomType.label,
color: randomQuality.color,
type: randomType.type,
quality: randomQuality.modifier
};
}
} else {
self.droppedLoot = null; // No loot dropped
}
};
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;
});
// Player: Holds player stats and methods
// 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
});
// Title
self.titleText = new Text2('Player Stats', {
size: 100,
fill: 0xFFE066
});
self.titleText.anchor.set(0.5, 0.5);
self.titleText.x = 2048 / 2;
self.titleText.y = self.panel.y - self.panel.height / 2 + 120;
self.addChild(self.titleText);
// Stat texts
self.hpStatText = new Text2('', {
size: 60,
fill: 0xA3E635
});
self.hpStatText.anchor.set(0, 0.5);
self.hpStatText.x = self.panel.x - self.panel.width / 2 + 80;
self.hpStatText.y = self.panel.y - 200;
self.addChild(self.hpStatText);
self.dmgStatText = new Text2('', {
size: 60,
fill: 0xF87171
});
self.dmgStatText.anchor.set(0, 0.5);
self.dmgStatText.x = self.panel.x - self.panel.width / 2 + 80;
self.dmgStatText.y = self.panel.y;
self.addChild(self.dmgStatText);
self.defStatText = new Text2('', {
size: 60,
fill: 0x7DD3FC
});
self.defStatText.anchor.set(0, 0.5);
self.defStatText.x = self.panel.x - self.panel.width / 2 + 80;
self.defStatText.y = self.panel.y + 200;
self.addChild(self.defStatText);
self.skillPointsText = new Text2('', {
size: 60,
fill: 0xFFE066
});
self.skillPointsText.anchor.set(0.5, 0.5);
self.skillPointsText.x = 2048 / 2;
self.skillPointsText.y = self.panel.y + self.panel.height / 2 - 200;
self.addChild(self.skillPointsText);
// Upgrade buttons
self.upgradeBtns = [];
var upgradeOptions = [{
label: "+HP & Heal",
stat: "hp",
color: 0xA3E635
}, {
label: "+DMG",
stat: "damage",
color: 0xF87171
}, {
label: "+DEF",
stat: "defense",
color: 0x7DD3FC
}];
for (var i = 0; i < upgradeOptions.length; i++) {
var option = upgradeOptions[i];
var btn = new Text2(option.label, {
size: 60,
fill: option.color,
font: "Impact"
});
btn.anchor.set(0.5, 0.5);
btn.x = self.panel.x + self.panel.width / 2 - 150;
btn.y = self.panel.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.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;
}
};
// Start the stats screen
self.startStatsScreen = function () {
self.visible = true;
self.alpha = 0;
game.addChild(self);
self.updateStatsDisplay(); // Update stats when opening
// Fade in
tween(self, {
alpha: 1
}, {
duration: 300,
easing: tween.easeOut
});
};
// End the stats screen
self.endStatsScreen = function () {
self.visible = false;
self.alpha = 0;
if (self.parent) self.parent.removeChild(self);
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));
tile.droppedLoot = null; // Initialize dropped loot property
} 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
@@ -789,8 +789,9 @@
self.hp = 1;
self.maxHp = 1;
self.damage = 1;
self.exp = 1;
+ self.droppedLoot = null; // Initialize dropped loot property
self.revealed = false;
self.defeated = false;
// Visuals
self.cover = self.attachAsset('monsterTileCover', {