User prompt
when every card play by player play the sound effect
User prompt
assign boss asset to the last enemy (boss)
User prompt
there is no boss asset in assets make one
User prompt
make boss a different enemy
User prompt
sometimes when i end turn cards disappear make fresh card deck when end turn now there is no card left
User prompt
make end turn text black
User prompt
make end turn button background as asset
User prompt
make end turn button visual to end turn text
User prompt
defend cards should be dragged to the player not the enemy.
User prompt
make card titles white
User prompt
allign card texts under the card
User prompt
make player bigger
User prompt
make enemies as the same height as player horizontally same level
User prompt
No. It shouldn't be the defend asset. Add background asset so i can change it
User prompt
add background asset. It is the same with defend asset now.
User prompt
Player health should be under the character
User prompt
Add background to the maps and also to the map. Boss should be seperate background. And also can you explain cards (card texts) under cards. and make it white text
User prompt
still. There is no information about players' stamina. Add text for player stamina above player or above cards. And also enemy says it would attack with 6 but it only attacks 1. Also there is no Block text under player
User prompt
Please fix the bug: 'Uncaught TypeError: guiStaminaTxt.setText is not a function' in or related to this line: 'guiStaminaTxt.setText('Stamina: ' + playerStamina + ' / ' + playerMaxStamina);' Line Number: 664
User prompt
still there is no information about players' stamina. And also end turn button should be little more above. Now it blends with cards.
User prompt
add stamina gui to see stamina. when stamina finishes player shouldn't be able to play any cards and add a button above the cards to end turn. When player clicks to end turn now its enemies turn
User prompt
Please fix the bug: 'Uncaught ReferenceError: enemyData is not defined' in or related to this line: 'enemyNode.setIntent(enemyData.intent);' Line Number: 551
User prompt
enemy attacks are not taking the players health down. And we can add more visiual to the game player can be on the left as a sprite. And background will be a cave. And the enemy can be on the right. (sometimes there are multiple enemies and player has to drag card to choose enemy to attack). Also there should be a stamina and every card has a stamina cost so the player sometimes can't play all cards in hand. Every cave the player can choose stamina upgrade or a new card
User prompt
Please fix the bug: 'TypeError: setTimeout is not a function' in or related to this line: 'setTimeout(enemyTurn, 500);' Line Number: 681
Code edit (1 edits merged)
Please save this source code
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Card class
var Card = Container.expand(function () {
var self = Container.call(this);
// Card data: {type, name, desc, damage, block, special}
self.cardData = null;
// Card background
var cardBg = null;
// Card text
var nameTxt = null;
var descTxt = null;
// Set card data and visuals
self.setCard = function (cardData) {
self.cardData = cardData;
if (cardBg) cardBg.destroy();
if (nameTxt) nameTxt.destroy();
if (descTxt) descTxt.destroy();
if (self.staminaTxt) {
self.staminaTxt.destroy();
self.staminaTxt = null;
}
var assetId = 'card';
if (cardData.type === 'attack') assetId = 'cardAttack';else if (cardData.type === 'defend') assetId = 'cardDefend';else if (cardData.type === 'special') assetId = 'cardSpecial';
cardBg = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
nameTxt = new Text2(cardData.name, {
size: 48,
fill: '#222222'
});
nameTxt.anchor.set(0.5, 0);
nameTxt.x = 0;
nameTxt.y = -180;
self.addChild(nameTxt);
descTxt = new Text2(cardData.desc, {
size: 32,
fill: '#333333'
});
descTxt.anchor.set(0.5, 0);
descTxt.x = 0;
descTxt.y = -100;
self.addChild(descTxt);
// Show stamina cost
var cost = cardData.stamina || 1;
self.staminaTxt = new Text2(cost + '', {
size: 48,
fill: '#ffaa00'
});
self.staminaTxt.anchor.set(0.5, 0.5);
self.staminaTxt.x = 0;
self.staminaTxt.y = 160;
self.addChild(self.staminaTxt);
};
// Highlight for selection
self.setSelected = function (selected) {
if (cardBg) {
cardBg.alpha = selected ? 1 : 0.85;
cardBg.scaleX = cardBg.scaleY = selected ? 1.08 : 1;
}
};
// For drag/press
self.down = function (x, y, obj) {
if (self.onCardDown) self.onCardDown(self, x, y, obj);
};
return self;
});
// Enemy class
var Enemy = Container.expand(function () {
var self = Container.call(this);
self.maxHp = 1;
self.hp = 1;
self.intent = null; // {type, value}
self.enemyData = null;
var enemyShape = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
var hpTxt = new Text2('', {
size: 48,
fill: '#ffffff'
});
hpTxt.anchor.set(0.5, 0.5);
hpTxt.x = 0;
hpTxt.y = 0;
self.addChild(hpTxt);
var intentTxt = new Text2('', {
size: 36,
fill: '#ffcc00'
});
intentTxt.anchor.set(0.5, 0);
intentTxt.x = 0;
intentTxt.y = 120;
self.addChild(intentTxt);
self.setEnemy = function (enemyData) {
self.enemyData = enemyData;
self.maxHp = enemyData.hp;
self.hp = enemyData.hp;
self.updateHp();
self.setIntent(enemyData.intent);
};
self.setIntent = function (intent) {
self.intent = intent;
if (intent && intent.type === 'attack') {
intentTxt.setText('Attack\n' + intent.value);
} else if (intent && intent.type === 'block') {
intentTxt.setText('Block\n' + intent.value);
} else {
intentTxt.setText('');
}
};
self.updateHp = function () {
hpTxt.setText(self.hp + ' / ' + self.maxHp);
};
return self;
});
// MapNode class
var MapNode = Container.expand(function () {
var self = Container.call(this);
self.nodeType = 'normal'; // 'normal', 'boss'
self.nodeIndex = 0;
self.isCurrent = false;
self.isVisited = false;
var nodeShape = null;
var nodeTxt = null;
self.setNode = function (type, isCurrent, isBoss) {
self.nodeType = type;
self.isCurrent = isCurrent;
if (nodeShape) nodeShape.destroy();
if (nodeTxt) nodeTxt.destroy();
var assetId = isBoss ? 'mapNodeBoss' : isCurrent ? 'mapNodeCurrent' : 'mapNode';
nodeShape = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
nodeTxt = new Text2(isBoss ? 'B' : '', {
size: 64,
fill: '#222222'
});
nodeTxt.anchor.set(0.5, 0.5);
nodeTxt.x = 0;
nodeTxt.y = 0;
self.addChild(nodeTxt);
};
// For tap
self.down = function (x, y, obj) {
if (self.onNodeDown) self.onNodeDown(self, x, y, obj);
};
return self;
});
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
self.maxHp = 30;
self.hp = 30;
self.block = 0;
var playerShape = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
var hpTxt = new Text2('', {
size: 48,
fill: '#222222'
});
hpTxt.anchor.set(0.5, 0.5);
hpTxt.x = 0;
hpTxt.y = 0;
self.addChild(hpTxt);
var blockTxt = new Text2('', {
size: 36,
fill: '#55aaff'
});
blockTxt.anchor.set(0.5, 0);
blockTxt.x = 0;
blockTxt.y = 120;
self.addChild(blockTxt);
self.updateStats = function () {
hpTxt.setText(self.hp + ' / ' + self.maxHp);
blockTxt.setText(self.block > 0 ? 'Block: ' + self.block : '');
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181818
});
/****
* Game Code
****/
// --- Card Data ---
// Card shapes (rectangles for cards)
var CARD_LIBRARY = [{
type: 'attack',
name: 'Strike',
desc: 'Deal 6 damage.',
damage: 6,
stamina: 1
}, {
type: 'defend',
name: 'Defend',
desc: 'Gain 5 block.',
block: 5,
stamina: 1
}, {
type: 'attack',
name: 'Slash',
desc: 'Deal 8 damage.',
damage: 8,
stamina: 2
}, {
type: 'defend',
name: 'Guard',
desc: 'Gain 7 block.',
block: 7,
stamina: 2
}, {
type: 'special',
name: 'Heal',
desc: 'Heal 4 HP.',
special: 'heal',
value: 4,
stamina: 2
}, {
type: 'special',
name: 'Double Strike',
desc: 'Deal 4 damage twice.',
special: 'double',
value: 4,
stamina: 2
}, {
type: 'special',
name: 'Bash',
desc: 'Deal 10 damage. Next attack +3.',
special: 'bash',
value: 10,
stamina: 2
}];
// --- Enemy Data ---
var ENEMY_LIBRARY = [{
name: 'Goblin',
hp: 18,
intent: {
type: 'attack',
value: 5
}
}, {
name: 'Slime',
hp: 22,
intent: {
type: 'block',
value: 6
}
}, {
name: 'Orc',
hp: 28,
intent: {
type: 'attack',
value: 8
}
}, {
name: 'Boss',
hp: 40,
intent: {
type: 'attack',
value: 12
}
}];
// --- Game State ---
var player = null;
var enemy = null;
var hand = [];
var deck = [];
var discard = [];
var drawPile = [];
var selectedCard = null;
var cardNodes = [];
var enemyNode = null;
var playerNode = null;
var mapNodes = [];
var mapPaths = [];
var currentMapIndex = 0;
var mapData = [];
var inBattle = false;
var battleResult = null; // null, 'win', 'lose'
var rewardCards = [];
var rewardCardNodes = [];
var rewardTxt = null;
var dragCard = null;
var dragStart = null;
var dragValid = false;
var guiHpTxt = null;
var guiEnemyHpTxt = null;
var guiFloorTxt = null;
var guiBlockTxt = null;
var guiEnemyIntentTxt = null;
var guiMapGroup = null;
var guiHandGroup = null;
var guiBattleGroup = null;
var guiRewardGroup = null;
var guiStaminaTxt = null;
var floorNum = 1;
var maxFloors = 8;
// Stamina system
var playerStamina = 3;
var playerMaxStamina = 3;
// --- Utility Functions ---
function shuffleArray(arr) {
for (var i = arr.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}
function cloneCard(card) {
var c = {};
for (var k in card) c[k] = card[k];
return c;
}
function getRandomCard() {
var idx = Math.floor(Math.random() * CARD_LIBRARY.length);
return cloneCard(CARD_LIBRARY[idx]);
}
function getRandomEnemy(isBoss) {
if (isBoss) return ENEMY_LIBRARY[3];
var idx = Math.floor(Math.random() * 3);
return ENEMY_LIBRARY[idx];
}
// --- Map Generation ---
function generateMap() {
// Simple linear with 2-branch at each node, boss at end
mapData = [];
for (var i = 0; i < maxFloors - 1; ++i) {
var node = {
type: 'normal',
isBoss: false,
index: i
};
mapData.push(node);
}
mapData.push({
type: 'boss',
isBoss: true,
index: maxFloors - 1
});
}
// --- Map Rendering ---
function renderMap() {
if (!guiMapGroup) {
guiMapGroup = new Container();
game.addChild(guiMapGroup);
}
// Clear old
for (var i = 0; i < mapNodes.length; ++i) mapNodes[i].destroy();
mapNodes = [];
for (var i = 0; i < mapPaths.length; ++i) mapPaths[i].destroy();
mapPaths = [];
var startY = 350;
var gapY = 220;
var centerX = 2048 / 2;
for (var i = 0; i < mapData.length; ++i) {
var node = new MapNode();
var isCurrent = i === currentMapIndex;
node.setNode(mapData[i].type, isCurrent, mapData[i].isBoss);
node.x = centerX;
node.y = startY + i * gapY;
node.nodeIndex = i;
node.onNodeDown = onMapNodeDown;
guiMapGroup.addChild(node);
mapNodes.push(node);
// Path to next
if (i < mapData.length - 1) {
var path = LK.getAsset('mapPath', {
anchorX: 0.5,
anchorY: 0,
x: centerX,
y: node.y + 60,
scaleY: 1.2
});
guiMapGroup.addChild(path);
mapPaths.push(path);
}
}
}
// --- Map Node Click ---
function onMapNodeDown(node, x, y, obj) {
if (inBattle) return;
if (node.nodeIndex === currentMapIndex) {
// Enter battle
startBattle(node.nodeIndex);
}
}
// --- Start Battle ---
function startBattle(mapIdx) {
inBattle = true;
// Remove map
if (guiMapGroup) guiMapGroup.visible = false;
// Setup cave background
if (!game.caveBg) {
game.caveBg = LK.getAsset('cardDefend', {
// Use cardDefend as a placeholder cave bg
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
scaleY: 8,
x: 2048 / 2,
y: 2732 / 2
});
game.addChildAt(game.caveBg, 0);
} else {
game.caveBg.visible = true;
}
// Setup player sprite on left
if (!playerNode) {
playerNode = new Player();
playerNode.x = 400;
playerNode.y = 1700;
game.addChild(playerNode);
} else {
playerNode.x = 400;
playerNode.y = 1700;
playerNode.visible = true;
}
playerNode.updateStats();
// Setup enemy/enemies on right
if (enemyNode) enemyNode.destroy();
enemyNode = new Enemy();
enemyNode.setEnemy(getRandomEnemy(mapData[mapIdx].isBoss));
enemyNode.x = 1648;
enemyNode.y = 700;
game.addChild(enemyNode);
// For future: support multiple enemies
// (for now, just one enemyNode, but can be extended to an array of Enemy nodes)
// Setup deck/hand
if (deck.length === 0) {
// Starting deck: 5x Strike, 5x Defend
deck = [];
for (var i = 0; i < 5; ++i) deck.push(cloneCard(CARD_LIBRARY[0]));
for (var i = 0; i < 5; ++i) deck.push(cloneCard(CARD_LIBRARY[1]));
}
drawPile = [];
for (var i = 0; i < deck.length; ++i) drawPile.push(cloneCard(deck[i]));
shuffleArray(drawPile);
discard = [];
hand = [];
drawHand();
// Show battle group
if (!guiBattleGroup) {
guiBattleGroup = new Container();
game.addChild(guiBattleGroup);
}
guiBattleGroup.visible = true;
// Hide reward group if present
if (guiRewardGroup) guiRewardGroup.visible = false;
// Floor text
if (!guiFloorTxt) {
guiFloorTxt = new Text2('', {
size: 64,
fill: '#ffffff'
});
guiFloorTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(guiFloorTxt);
}
guiFloorTxt.setText('Floor ' + (mapIdx + 1));
// Reset player stats
playerNode.block = 0;
playerNode.updateStats();
// Reset stamina
playerMaxStamina = 3 + Math.floor(currentMapIndex / 2); // Optionally scale with progress
playerStamina = playerMaxStamina;
// Show stamina UI as a bar and text above cards, visually distinct
if (!guiStaminaTxt) {
// Stamina bar background
guiStaminaTxt = new Container();
guiStaminaTxt.bg = LK.getAsset('card', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.2,
scaleY: 0.25,
x: 0,
y: 0
});
guiStaminaTxt.bg.alpha = 0.18;
guiStaminaTxt.addChild(guiStaminaTxt.bg);
// Stamina bar fill
guiStaminaTxt.bar = LK.getAsset('cardAttack', {
anchorX: 0,
anchorY: 0.5,
scaleX: 2.1,
scaleY: 0.18,
x: -160,
y: 0
});
guiStaminaTxt.bar.alpha = 0.45;
guiStaminaTxt.addChild(guiStaminaTxt.bar);
// Stamina text
guiStaminaTxt.text = new Text2('', {
size: 54,
fill: '#ffaa00'
});
guiStaminaTxt.text.anchor.set(0.5, 0.5);
guiStaminaTxt.text.x = 0;
guiStaminaTxt.text.y = 0;
guiStaminaTxt.addChild(guiStaminaTxt.text);
guiStaminaTxt.x = 2048 / 2;
guiStaminaTxt.y = 1550;
LK.gui.top.addChild(guiStaminaTxt);
}
// Update stamina bar and text
var staminaRatio = playerMaxStamina > 0 ? playerStamina / playerMaxStamina : 0;
if (guiStaminaTxt.bar) {
guiStaminaTxt.bar.scaleX = 2.1 * staminaRatio;
}
if (guiStaminaTxt.text) {
guiStaminaTxt.text.setText('Stamina: ' + playerStamina + ' / ' + playerMaxStamina);
}
// Enemy intent
enemyNode.setIntent(enemyNode.enemyData.intent);
// Render hand
renderHand();
}
// --- Draw Hand ---
function drawHand() {
hand = [];
for (var i = 0; i < 5; ++i) {
if (drawPile.length === 0) {
// Reshuffle discard
if (discard.length === 0) break;
for (var j = 0; j < discard.length; ++j) drawPile.push(cloneCard(discard[j]));
shuffleArray(drawPile);
discard = [];
}
if (drawPile.length > 0) {
hand.push(drawPile.pop());
}
}
}
// --- Render Hand ---
function renderHand() {
if (!guiHandGroup) {
guiHandGroup = new Container();
game.addChild(guiHandGroup);
}
// Remove old
for (var i = 0; i < cardNodes.length; ++i) cardNodes[i].destroy();
cardNodes = [];
// Remove old end turn button if present
if (guiHandGroup.endTurnBtn) {
guiHandGroup.endTurnBtn.destroy();
guiHandGroup.endTurnBtn = null;
}
var handY = 2400;
var handX0 = 2048 / 2 - 2 * 360;
for (var i = 0; i < hand.length; ++i) {
var cardNode = new Card();
cardNode.setCard(hand[i]);
cardNode.x = handX0 + i * 360;
cardNode.y = handY;
cardNode.onCardDown = onCardDown;
guiHandGroup.addChild(cardNode);
cardNodes.push(cardNode);
// Disable card interaction if stamina is 0
if (playerStamina <= 0) {
cardNode.interactive = false;
cardNode.alpha = 0.5;
} else {
cardNode.interactive = true;
cardNode.alpha = 1;
}
}
// Add End Turn button above cards
if (!guiHandGroup.endTurnBtn) {
var btn = new Text2('End Turn', {
size: 64,
fill: playerStamina > 0 ? '#cccccc' : '#888888'
});
btn.anchor.set(0.5, 0.5);
btn.x = 2048 / 2;
btn.y = handY - 320; // Move button higher above the cards for better separation
btn.interactive = true;
btn.buttonMode = true;
btn.alpha = playerStamina > 0 ? 1 : 0.7;
btn.down = function (x, y, obj) {
if (!inBattle || battleResult) return;
// Only allow end turn if player has stamina left or hand is not empty
if (playerStamina > 0 || hand.length > 0) {
playerStamina = 0;
if (guiStaminaTxt && guiStaminaTxt.text) guiStaminaTxt.text.setText('Stamina: ' + playerStamina + ' / ' + playerMaxStamina);
// Disable cards
for (var i = 0; i < cardNodes.length; ++i) {
cardNodes[i].interactive = false;
cardNodes[i].alpha = 0.5;
}
// End turn, let enemy act after short delay
LK.setTimeout(enemyTurn, 400);
}
};
guiHandGroup.endTurnBtn = btn;
guiHandGroup.addChild(btn);
}
// Update button color/alpha if stamina is 0
if (guiHandGroup.endTurnBtn) {
guiHandGroup.endTurnBtn.fill = playerStamina > 0 ? '#cccccc' : '#888888';
guiHandGroup.endTurnBtn.alpha = playerStamina > 0 ? 1 : 0.7;
}
// Update stamina bar and text after hand render
if (guiStaminaTxt && guiStaminaTxt.bar && guiStaminaTxt.text) {
var staminaRatio = playerMaxStamina > 0 ? playerStamina / playerMaxStamina : 0;
guiStaminaTxt.bar.scaleX = 2.1 * staminaRatio;
guiStaminaTxt.text.setText('Stamina: ' + playerStamina + ' / ' + playerMaxStamina);
}
}
// --- Card Down (Select/Drag) ---
function onCardDown(cardNode, x, y, obj) {
if (!inBattle) return;
if (battleResult) return;
if (playerStamina <= 0) return; // Prevent card play if no stamina
dragCard = cardNode;
dragStart = {
x: cardNode.x,
y: cardNode.y
};
dragValid = false;
// Highlight
for (var i = 0; i < cardNodes.length; ++i) cardNodes[i].setSelected(cardNodes[i] === cardNode);
}
// --- Game Move (Drag Card) ---
game.move = function (x, y, obj) {
if (dragCard && inBattle && !battleResult) {
// Move card with finger
dragCard.x = x;
dragCard.y = y;
// If dragged over enemy, highlight
var enemyRect = {
x: enemyNode.x - 110,
y: enemyNode.y - 110,
w: 220,
h: 220
};
if (x > enemyRect.x && x < enemyRect.x + enemyRect.w && y > enemyRect.y && y < enemyRect.y + enemyRect.h) {
dragValid = true;
enemyNode.alpha = 0.7;
} else {
dragValid = false;
enemyNode.alpha = 1;
}
}
};
// --- Game Up (Release Card) ---
game.up = function (x, y, obj) {
if (dragCard && inBattle && !battleResult) {
// If valid, play card
if (dragValid) {
playCard(dragCard);
} else {
// Return to hand
tween(dragCard, {
x: dragStart.x,
y: dragStart.y
}, {
duration: 180,
easing: tween.easeOut
});
for (var i = 0; i < cardNodes.length; ++i) cardNodes[i].setSelected(false);
}
dragCard = null;
dragStart = null;
dragValid = false;
enemyNode.alpha = 1;
}
};
// --- Play Card ---
function playCard(cardNode) {
var idx = cardNodes.indexOf(cardNode);
if (idx === -1) return;
var card = hand[idx];
var cost = card.stamina || 1;
if (playerStamina < cost) {
// Not enough stamina, shake card and return
tween(cardNode, {
x: cardNode.x - 30
}, {
duration: 80,
yoyo: true,
repeat: 1,
onFinish: function onFinish() {
tween(cardNode, {
x: dragStart ? dragStart.x : cardNode.x
}, {
duration: 80
});
}
});
for (var i = 0; i < cardNodes.length; ++i) cardNodes[i].setSelected(false);
return;
}
playerStamina -= cost;
// Update stamina GUI bar and text after card play
if (guiStaminaTxt && guiStaminaTxt.bar && guiStaminaTxt.text) {
var staminaRatio = playerMaxStamina > 0 ? playerStamina / playerMaxStamina : 0;
guiStaminaTxt.bar.scaleX = 2.1 * staminaRatio;
guiStaminaTxt.text.setText('Stamina: ' + playerStamina + ' / ' + playerMaxStamina);
}
// Animate card to enemy
tween(cardNode, {
x: enemyNode.x,
y: enemyNode.y
}, {
duration: 180,
easing: tween.easeIn,
onFinish: function onFinish() {
// Apply effect
if (card.type === 'attack') {
var dmg = card.damage;
// Bash effect: next attack +3
if (playerNode.nextAttackBonus) {
dmg += playerNode.nextAttackBonus;
playerNode.nextAttackBonus = 0;
}
enemyNode.hp -= dmg;
if (enemyNode.hp < 0) enemyNode.hp = 0;
enemyNode.updateHp();
LK.effects.flashObject(enemyNode, 0xff0000, 300);
} else if (card.type === 'defend') {
playerNode.block += card.block;
LK.effects.flashObject(playerNode, 0x55aaff, 300);
} else if (card.type === 'special') {
if (card.special === 'heal') {
playerNode.hp += card.value;
if (playerNode.hp > playerNode.maxHp) playerNode.hp = playerNode.maxHp;
LK.effects.flashObject(playerNode, 0x55ff99, 300);
} else if (card.special === 'double') {
var dmg = card.value;
if (playerNode.nextAttackBonus) {
dmg += playerNode.nextAttackBonus;
playerNode.nextAttackBonus = 0;
}
enemyNode.hp -= dmg;
enemyNode.hp -= dmg;
if (enemyNode.hp < 0) enemyNode.hp = 0;
enemyNode.updateHp();
LK.effects.flashObject(enemyNode, 0xff0000, 300);
} else if (card.special === 'bash') {
enemyNode.hp -= card.value;
if (enemyNode.hp < 0) enemyNode.hp = 0;
enemyNode.updateHp();
playerNode.nextAttackBonus = 3;
LK.effects.flashObject(enemyNode, 0xff0000, 300);
}
}
playerNode.updateStats();
// Discard card
discard.push(card);
hand.splice(idx, 1);
cardNode.destroy();
cardNodes.splice(idx, 1);
// Check win
if (enemyNode.hp <= 0) {
battleResult = 'win';
LK.setTimeout(showBattleWin, 600);
return;
}
// Enemy turn if no cards left or after play
if (hand.length === 0) {
LK.setTimeout(enemyTurn, 500);
} else {
// If stamina is 0, disable cards and prompt end turn
if (playerStamina <= 0) {
renderHand();
} else {
renderHand();
}
}
}
});
}
// --- Enemy Turn ---
function enemyTurn() {
// Enemy intent
var intent = enemyNode.intent;
if (intent && intent.type === 'attack') {
var dmg = intent.value;
var block = playerNode.block;
var taken = dmg;
if (block > 0) {
if (block >= dmg) {
playerNode.block -= dmg;
taken = 0;
} else {
taken = dmg - block;
playerNode.block = 0;
}
}
playerNode.hp -= taken;
if (playerNode.hp < 0) playerNode.hp = 0;
LK.effects.flashObject(playerNode, 0xff0000, 400);
} else if (intent && intent.type === 'block') {
enemyNode.hp += intent.value;
if (enemyNode.hp > enemyNode.maxHp) enemyNode.hp = enemyNode.maxHp;
LK.effects.flashObject(enemyNode, 0x55aaff, 400);
}
playerNode.updateStats();
enemyNode.updateHp();
// Check lose
if (playerNode.hp <= 0) {
battleResult = 'lose';
LK.setTimeout(showBattleLose, 600);
return;
}
// Next turn: draw new hand, reset block
playerNode.block = 0;
playerNode.updateStats();
playerStamina = playerMaxStamina;
// Update stamina GUI bar and text after enemy turn
if (guiStaminaTxt && guiStaminaTxt.bar && guiStaminaTxt.text) {
var staminaRatio = playerMaxStamina > 0 ? playerStamina / playerMaxStamina : 0;
guiStaminaTxt.bar.scaleX = 2.1 * staminaRatio;
guiStaminaTxt.text.setText('Stamina: ' + playerStamina + ' / ' + playerMaxStamina);
}
drawHand();
renderHand();
// New enemy intent
var enemyData = enemyNode.enemyData;
// Randomize intent
if (Math.random() < 0.5) {
enemyNode.setIntent({
type: 'attack',
value: enemyData.intent.value
});
} else {
enemyNode.setIntent({
type: 'block',
value: Math.floor(enemyData.intent.value * 0.8)
});
}
}
// --- Show Battle Win ---
function showBattleWin() {
// Remove enemy
if (enemyNode) enemyNode.destroy();
enemyNode = null;
// Hide end turn button and disable cards
if (guiHandGroup && guiHandGroup.endTurnBtn) {
guiHandGroup.endTurnBtn.visible = false;
}
for (var i = 0; i < cardNodes.length; ++i) {
cardNodes[i].interactive = false;
cardNodes[i].alpha = 0.5;
}
// Show reward
showReward();
}
// --- Show Battle Lose ---
function showBattleLose() {
LK.effects.flashScreen(0xff0000, 1000);
// Hide end turn button and disable cards
if (guiHandGroup && guiHandGroup.endTurnBtn) {
guiHandGroup.endTurnBtn.visible = false;
}
for (var i = 0; i < cardNodes.length; ++i) {
cardNodes[i].interactive = false;
cardNodes[i].alpha = 0.5;
}
LK.showGameOver();
}
// --- Show Reward (Choose 1 of 3 cards) ---
function showReward() {
if (!guiRewardGroup) {
guiRewardGroup = new Container();
game.addChild(guiRewardGroup);
}
guiRewardGroup.visible = true;
// Remove old
for (var i = 0; i < rewardCardNodes.length; ++i) rewardCardNodes[i].destroy();
rewardCardNodes = [];
if (rewardTxt) rewardTxt.destroy();
rewardTxt = new Text2('Choose a card to add to your deck\nor +1 Max Stamina', {
size: 64,
fill: '#fff'
});
rewardTxt.anchor.set(0.5, 0);
rewardTxt.x = 2048 / 2;
rewardTxt.y = 600;
guiRewardGroup.addChild(rewardTxt);
rewardCards = [];
for (var i = 0; i < 3; ++i) rewardCards.push(getRandomCard());
var rx0 = 2048 / 2 - 360;
for (var i = 0; i < 3; ++i) {
var cardNode = new Card();
cardNode.setCard(rewardCards[i]);
cardNode.x = rx0 + i * 360;
cardNode.y = 1100;
cardNode.onCardDown = onRewardCardDown;
guiRewardGroup.addChild(cardNode);
rewardCardNodes.push(cardNode);
}
// Add stamina upgrade button
if (!guiRewardGroup.staminaBtn) {
guiRewardGroup.staminaBtn = new Text2('+1 Max Stamina', {
size: 56,
fill: '#ffaa00'
});
guiRewardGroup.staminaBtn.anchor.set(0.5, 0.5);
guiRewardGroup.staminaBtn.x = 2048 / 2;
guiRewardGroup.staminaBtn.y = 1500;
guiRewardGroup.staminaBtn.interactive = true;
guiRewardGroup.staminaBtn.buttonMode = true;
guiRewardGroup.staminaBtn.down = function (x, y, obj) {
playerMaxStamina += 1;
guiRewardGroup.visible = false;
battleResult = null;
inBattle = false;
currentMapIndex += 1;
if (currentMapIndex >= mapData.length) {
LK.showYouWin();
return;
}
guiMapGroup.visible = true;
renderMap();
};
guiRewardGroup.addChild(guiRewardGroup.staminaBtn);
} else {
guiRewardGroup.staminaBtn.visible = true;
}
}
// --- Reward Card Down (Pick Card) ---
function onRewardCardDown(cardNode, x, y, obj) {
var idx = rewardCardNodes.indexOf(cardNode);
if (idx === -1) return;
// Add to deck
deck.push(cloneCard(rewardCards[idx]));
// Next map node
guiRewardGroup.visible = false;
if (game.caveBg) game.caveBg.visible = false;
if (playerNode) playerNode.visible = false;
if (enemyNode) enemyNode.visible = false;
battleResult = null;
inBattle = false;
currentMapIndex += 1;
if (currentMapIndex >= mapData.length) {
// Win game
LK.showYouWin();
return;
}
// Show map again
guiMapGroup.visible = true;
renderMap();
}
// --- Game Update ---
game.update = function () {
// No per-frame logic needed for now
};
// --- Game Start ---
function startGame() {
// Reset state
player = null;
enemy = null;
hand = [];
deck = [];
discard = [];
drawPile = [];
selectedCard = null;
cardNodes = [];
enemyNode = null;
playerNode = null;
mapNodes = [];
mapPaths = [];
currentMapIndex = 0;
inBattle = false;
battleResult = null;
rewardCards = [];
rewardCardNodes = [];
floorNum = 1;
// Generate map
generateMap();
renderMap();
}
startGame(); ===================================================================
--- original.js
+++ change.js
@@ -595,9 +595,9 @@
if (!inBattle || battleResult) return;
// Only allow end turn if player has stamina left or hand is not empty
if (playerStamina > 0 || hand.length > 0) {
playerStamina = 0;
- if (guiStaminaTxt) guiStaminaTxt.setText('Stamina: ' + playerStamina + ' / ' + playerMaxStamina);
+ if (guiStaminaTxt && guiStaminaTxt.text) guiStaminaTxt.text.setText('Stamina: ' + playerStamina + ' / ' + playerMaxStamina);
// Disable cards
for (var i = 0; i < cardNodes.length; ++i) {
cardNodes[i].interactive = false;
cardNodes[i].alpha = 0.5;
2d stylized dungeon enemy. In-Game asset. 2d. High contrast. No shadows
boss logo skull head. In-Game asset. 2d. High contrast. No shadows
dungeon card game, Strike card. In-Game asset. 2d. High contrast. No shadows
dungeon card game defend card . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
dungeon card game special card with fire logo . No background. Transparent background. Blank background. No shadows. 2d. In-Game asset. flat
2d card game player hero asset. In-Game asset. 2d. High contrast. No shadows
dungeon simple 2d side view dungeon crawler game background. In-Game asset. 2d. High contrast. No shadows. background
golden button horizontal. In-Game asset. 2d. High contrast. No shadows
2d dungeon crawler boss. In-Game asset. 2d. High contrast. No shadows