User prompt
2x vuruş atınca kritik efekti gelsin ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Mage sınıfı özelliğini değiştirip yerine öfkelenme getir %50 şans ile 2x hasar ihtimali olsun
User prompt
Sinerji bonusu +1 can olmasın onun yerine her tur +1 can yenileme olsun
User prompt
Sınıfların üstüne bastığında ne özellik vereceğini söylesin
User prompt
Sol tarafta hangi sinerjileri açık olduğu sınıflarla göstersin
User prompt
Oyuna sınıf sinerjisi ekle
User prompt
Sınıfları logolarıyla deste bölümüne ekle
User prompt
Geri yazısını sil
User prompt
Kolay orta zor yazısını tuşun altına köy
User prompt
Kolay orta zor arasında boşluk oluştur
User prompt
Kolay orta zor geri ana menü tuşlarına asset ekle
User prompt
Kapata asset ekle
User prompt
Destedeki kapat yazısını sil
User prompt
Play yazısını sil ve play tuşunu biraz daha yukarı al
User prompt
Deste tuşuna asset ekle
User prompt
Ana menüdeki kart savaşı ismini sil yerine Arsenic yaz
User prompt
Kartların üstünde karakterlerin ismini sil
User prompt
Bütün sınıflara asset getir yazı olmasın
User prompt
Earth golem ve lightning Bolt : tank sınıfı sınıflar kartların sağ üstünde gözüksün
User prompt
Madalyanın üstüne basınca hangi modu kaç kere kazandığını göstersin ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Oyuna madalya sistemi getir ana menüde oynanın üstünde gözüksün madalyanın amacı oyunu kaç kere bitirdiğini göstermektir ↪💡 Consider importing and using the following plugins: @upit/storage.v1
User prompt
Fire ball 10 hasar versin
User prompt
Ana menüye basıp tekrar oyuna girince karakterler birbirine giriyor onu düzelt
User prompt
Zor moda basıyorum ama zor mod olmuyor yine rakibin canı 50 oluyor
User prompt
Hangi zorluğu seçersen ona göre oynansın rastgele rakip zorluğu olmasın
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Card = Container.expand(function (cardData) { var self = Container.call(this); // Card properties self.cardData = cardData || { name: "Basic Card", cost: 1, attack: 2, health: 2, description: "A basic creature card", passive: null // Passive ability type - all passives removed }; // Damage tracking to prevent duplicate triggers self.damageDealtThisTurn = 0; self.hasTriggeredLifeSteal = false; // Ensure cardData has valid numeric values if (typeof self.cardData.attack !== 'number' || isNaN(self.cardData.attack)) { self.cardData.attack = 2; } if (typeof self.cardData.health !== 'number' || isNaN(self.cardData.health)) { self.cardData.health = 2; } if (typeof self.cardData.cost !== 'number' || isNaN(self.cardData.cost)) { self.cardData.cost = 1; } self.maxHealth = self.cardData.health; self.currentHealth = self.cardData.health; self.isPlayable = false; self.isOnBattlefield = false; self.hasAttacked = false; // Create card graphics based on card type var cardAssetName = 'cardBack'; // default var symbolAssetName = null; // Determine assets based on card name switch (self.cardData.name) { case "Fire Imp": cardAssetName = 'fireImpBg'; symbolAssetName = 'fireImpSymbol'; break; case "Water Spirit": cardAssetName = 'waterSpiritBg'; symbolAssetName = 'waterSpiritSymbol'; break; case "Earth Golem": cardAssetName = 'earthGolemBg'; symbolAssetName = 'earthGolemSymbol'; break; case "Air Wisp": cardAssetName = 'airWispBg'; symbolAssetName = 'airWispSymbol'; break; case "Lightning Bolt": cardAssetName = 'lightningBoltBg'; symbolAssetName = 'lightningBoltSymbol'; break; case "Lucifer": cardAssetName = 'walterSpiritBg'; symbolAssetName = 'walterSpiritSymbol'; break; case "Shadow Drake": cardAssetName = 'shadowDrakeBg'; symbolAssetName = 'shadowDrakeSymbol'; break; case "Michael Demiurgos": cardAssetName = 'michaelDemiurgosBg'; symbolAssetName = 'michaelDemiurgosSymbol'; break; } var cardBg = self.attachAsset(cardAssetName, { anchorX: 0.5, anchorY: 0.5 }); // Add symbol if available if (symbolAssetName) { var cardSymbol = self.attachAsset(symbolAssetName, { anchorX: 0.5, anchorY: 0.5 }); cardSymbol.x = 0; cardSymbol.y = -20; // Position symbol in upper middle area } // Card text elements var nameText = new Text2(self.cardData.name, { size: 24, fill: 0x2C3E50 }); nameText.anchor.set(0.5, 0); nameText.x = 0; nameText.y = -100; self.addChild(nameText); var costText = new Text2(self.cardData.cost.toString(), { size: 32, fill: 0x9B59B6 }); costText.anchor.set(0.5, 0.5); costText.x = -70; costText.y = -100; self.addChild(costText); var attackText = new Text2(self.cardData.attack.toString(), { size: 28, fill: 0xE74C3C }); attackText.anchor.set(0.5, 0.5); attackText.x = -50; attackText.y = 90; self.addChild(attackText); // Store reference to attack text for potential updates self.attackText = attackText; var healthText = new Text2(self.currentHealth.toString(), { size: 28, fill: 0x27AE60 }); healthText.anchor.set(0.5, 0.5); healthText.x = 50; healthText.y = 90; self.addChild(healthText); // Store reference to health text self.healthText = healthText; self.updateHealthDisplay = function () { // Ensure we display a valid number, not NaN var displayHealth = isNaN(self.currentHealth) ? 0 : Math.max(0, self.currentHealth); // Ensure currentHealth is always a valid number if (isNaN(self.currentHealth)) { self.currentHealth = 0; } if (isNaN(self.maxHealth)) { self.maxHealth = self.cardData.health || 1; } // Update health text display if (self.healthText) { self.healthText.setText(displayHealth.toString()); } else if (healthText) { healthText.setText(displayHealth.toString()); } // Update attack text if it exists and attack value changed if (self.attackText && self.cardData.attack !== undefined) { self.attackText.setText(self.cardData.attack.toString()); } // Debug log for Lightning Bolt if (self.cardData.name === "Lightning Bolt") { console.log("Lightning Bolt health - current:", self.currentHealth, "max:", self.maxHealth, "display:", displayHealth); } if (self.currentHealth <= 0) { cardBg.alpha = 0.5; } }; self.takeDamage = function (damage, attacker) { // Ensure damage is a valid number but don't reduce it to 0 unnecessarily if (typeof damage !== 'number' || isNaN(damage)) { damage = 0; } if (damage < 0) { damage = 0; } // No passive damage reduction - all passives removed var finalDamage = Math.max(0, damage); self.currentHealth -= finalDamage; // Ensure currentHealth is always a valid number if (isNaN(self.currentHealth)) { self.currentHealth = 0; } self.updateHealthDisplay(); // If card dies, remove it immediately if (self.currentHealth <= 0) { // Check if card is on battlefield before trying to find player var ownerPlayer = null; if (self.isOnBattlefield) { // Safely check which player owns this card for (var i = 0; i < humanPlayer.battlefield.length; i++) { if (humanPlayer.battlefield[i] === self) { ownerPlayer = humanPlayer; break; } } if (!ownerPlayer) { for (var i = 0; i < aiPlayer.battlefield.length; i++) { if (aiPlayer.battlefield[i] === self) { ownerPlayer = aiPlayer; break; } } } } // Trigger death passive before removing if (ownerPlayer) { self.triggerPassive("death", { player: ownerPlayer }); } // Remove from battlefield arrays and add new card to deck var humanIndex = humanPlayer.battlefield.indexOf(self); if (humanIndex >= 0) { humanPlayer.battlefield.splice(humanIndex, 1); // Create a new card with the same data instead of resetting this one var newCard = new Card(self.cardData); newCard.validateStats(); // Ensure stats are correct // Add new card to human player's deck humanPlayer.deck.push(newCard); } var aiIndex = aiPlayer.battlefield.indexOf(self); if (aiIndex >= 0) { aiPlayer.battlefield.splice(aiIndex, 1); // Create a new card with the same data instead of resetting this one var newCard = new Card(self.cardData); newCard.validateStats(); // Ensure stats are correct // Add new card to AI player's deck aiPlayer.deck.push(newCard); } // Clear lane assignment self.laneIndex = undefined; self.isOnBattlefield = false; // Death animation and removal from game animateCardDeath(self); LK.setTimeout(function () { if (game.children.includes(self)) { game.removeChild(self); } // Update battlefield layout after card removal arrangeBattlefield(); }, 600); // Match the death animation duration } // Lucifer passive: heal 2 health when dealing damage to other cards if (attacker && attacker.cardData.name === "Lucifer" && self.isOnBattlefield) { // Heal Lucifer by 2 health (not exceeding max health) var healAmount = 2; attacker.currentHealth = Math.min(attacker.maxHealth, attacker.currentHealth + healAmount); attacker.updateHealthDisplay(); // Create healing visual effect var healText = new Text2("+" + healAmount.toString(), { size: 40, fill: 0x00ff00 }); healText.anchor.set(0.5, 0.5); healText.x = attacker.x + (Math.random() - 0.5) * 60; healText.y = attacker.y - 50; healText.alpha = 1.0; game.addChild(healText); // Animate healing number floating up and fading tween(healText, { y: healText.y - 80, alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(healText); } }); // Brief green flash on Lucifer LK.effects.flashObject(attacker, 0x00ff00, 300); } // Enhanced damage animation // Screen shake for significant damage if (damage >= 3) { LK.effects.flashScreen(0xff4444, 200); } // Damage number popup animation var damageText = new Text2("-" + damage.toString(), { size: 40, fill: 0xff0000 }); damageText.anchor.set(0.5, 0.5); damageText.x = self.x + (Math.random() - 0.5) * 60; damageText.y = self.y - 50; damageText.alpha = 1.0; game.addChild(damageText); // Animate damage number floating up and fading tween(damageText, { y: damageText.y - 80, alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(damageText); } }); // Card damage animation - recoil and flash tween(self, { x: self.x + (Math.random() - 0.5) * 30, y: self.y + (Math.random() - 0.5) * 20, scaleX: 0.9, scaleY: 0.9, tint: 0xff4444 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { x: self.x, y: self.y, scaleX: 1.0, scaleY: 1.0, tint: 0xFFFFFF }, { duration: 300, easing: tween.easeOut }); } }); // Flash red when taking damage LK.effects.flashObject(self, 0xff0000, 500); }; self.canAttack = function () { return self.isOnBattlefield && !self.hasAttacked && self.currentHealth > 0; }; self.attack = function (target) { if (self.canAttack() && target) { // Check if both cards are in the same lane if (self.laneIndex !== undefined && target.laneIndex !== undefined && self.laneIndex !== target.laneIndex) { return; // Cannot attack cards in different lanes } // Store original position var originalX = self.x; var originalY = self.y; // Calculate target position (move towards target) var targetX = target.x; var targetY = target.y; // Animate attack: move toward target, then back tween(self, { x: targetX, y: targetY }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Collision animation - both cards shake and flash on impact var collisionDuration = 150; // Shake the attacking card tween(self, { x: targetX + 20 }, { duration: collisionDuration / 3, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { x: targetX - 20 }, { duration: collisionDuration / 3, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { x: targetX }, { duration: collisionDuration / 3, easing: tween.easeInOut }); } }); } }); // Shake the target card tween(target, { x: target.x - 15 }, { duration: collisionDuration / 3, easing: tween.easeInOut, onFinish: function onFinish() { tween(target, { x: target.x + 15 }, { duration: collisionDuration / 3, easing: tween.easeInOut, onFinish: function onFinish() { tween(target, { x: target.x }, { duration: collisionDuration / 3, easing: tween.easeInOut }); } }); } }); // Flash both cards white for collision effect tween(self, { tint: 0xFFFFFF }, { duration: collisionDuration, easing: tween.easeInOut, onFinish: function onFinish() { tween(self, { tint: 0xFFFFFF }, { duration: collisionDuration, easing: tween.easeInOut }); } }); tween(target, { tint: 0xFFFFFF }, { duration: collisionDuration, easing: tween.easeInOut, onFinish: function onFinish() { tween(target, { tint: 0xFFFFFF }, { duration: collisionDuration, easing: tween.easeInOut }); } }); // Use exactly the attack value shown on the card - no modifications var totalDamage = self.cardData.attack; // Ensure we're using a clean integer value if (typeof totalDamage !== 'number' || isNaN(totalDamage)) { totalDamage = self.cardData.attack; } // Debug logging for attacks console.log("Card attacking:", self.cardData.name, "(" + totalDamage + " damage) ->", target.cardData.name, "(" + target.currentHealth + " health)"); // Deal exact damage as written on card target.takeDamage(totalDamage, self); // After collision, animate return to original position LK.setTimeout(function () { tween(self, { x: originalX, y: originalY }, { duration: 300, easing: tween.easeIn }); }, collisionDuration); } }); self.hasAttacked = true; cardBg.alpha = 0.7; LK.getSound('attack').play(); } }; self.resetForNewTurn = function () { self.hasAttacked = false; self.damageDealtThisTurn = 0; self.hasTriggeredLifeSteal = false; if (self.currentHealth > 0) { cardBg.alpha = 1.0; } }; // Passive ability system - all passives removed self.triggerPassive = function (trigger, context) { return 0; }; self.hasPassive = function () { return false; // All passives removed }; // Method to validate and fix card stats self.validateStats = function () { // Ensure all stats are valid numbers if (typeof self.cardData.attack !== 'number' || isNaN(self.cardData.attack)) { self.cardData.attack = 1; } if (typeof self.cardData.health !== 'number' || isNaN(self.cardData.health)) { self.cardData.health = 1; } if (typeof self.cardData.cost !== 'number' || isNaN(self.cardData.cost)) { self.cardData.cost = 1; } if (typeof self.currentHealth !== 'number' || isNaN(self.currentHealth)) { self.currentHealth = self.cardData.health; } if (typeof self.maxHealth !== 'number' || isNaN(self.maxHealth)) { self.maxHealth = self.cardData.health; } // Ensure current health doesn't exceed max health if (self.currentHealth > self.maxHealth) { self.currentHealth = self.maxHealth; } // Update display after validation self.updateHealthDisplay(); }; return self; }); var DeckView = Container.expand(function () { var self = Container.call(this); // Dark overlay background var overlay = self.attachAsset('menuOverlay', { anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 0.9 }); // Title var titleText = new Text2("KART VE BÜYÜ KOLEKSİYONU", { size: 80, fill: 0xf39c12 }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 200; self.addChild(titleText); // Cards section title var cardsTitle = new Text2("KARTLAR", { size: 60, fill: 0x3498db }); cardsTitle.anchor.set(0.5, 0.5); cardsTitle.x = 1024; cardsTitle.y = 350; self.addChild(cardsTitle); // Spells section title var spellsTitle = new Text2("BÜYÜLER", { size: 60, fill: 0x9b59b6 }); spellsTitle.anchor.set(0.5, 0.5); spellsTitle.x = 1024; spellsTitle.y = 1800; self.addChild(spellsTitle); // Close button var closeBtn = self.attachAsset('playButton', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 2400, scaleX: 0.8, scaleY: 0.8 }); var closeBtnText = new Text2("KAPAT", { size: 48, fill: 0xffffff }); closeBtnText.anchor.set(0.5, 0.5); closeBtnText.x = 1024; closeBtnText.y = 2400; self.addChild(closeBtnText); // Create sample cards for display var cardTypes = [{ name: "Fire Imp", cost: 1, attack: 3, health: 12, description: "A small fire creature" }, { name: "Water Spirit", cost: 2, attack: 3, health: 13, description: "A defensive water creature" }, { name: "Earth Golem", cost: 6, attack: 3, health: 22, description: "A powerful earth creature" }, { name: "Air Wisp", cost: 1, attack: 2, health: 13, description: "A quick air creature" }, { name: "Lightning Bolt", cost: 3, attack: 3, health: 14, description: "A shocking creature" }, { name: "Lucifer", cost: 4, attack: 3, health: 15, description: "A mystical water spirit with high endurance" }, { name: "Shadow Drake", cost: 3, attack: 4, health: 14, description: "A powerful shadow dragon" }, { name: "Michael Demiurgos", cost: 4, attack: 5, health: 14, description: "An archangel with divine power" }]; // Display cards in a grid (4 cards per row) self.displayCards = []; for (var i = 0; i < cardTypes.length; i++) { var displayCard = new Card(cardTypes[i]); var row = Math.floor(i / 4); var col = i % 4; var startX = 1024 - 3 * 100; // Center 4 cards displayCard.x = startX + col * 200; displayCard.y = 500 + row * 300; displayCard.scaleX = 0.6; displayCard.scaleY = 0.6; self.addChild(displayCard); self.displayCards.push(displayCard); } // Create sample spells for display var spellTypes = [{ name: "Fire Ball", cost: 2, damage: 4, target: "enemy", description: "Deals 4 damage to target" }, { name: "Şifa", cost: 2, damage: -3, target: "ally", description: "Heals target for 3 health" }]; // Display spells self.displaySpells = []; for (var i = 0; i < spellTypes.length; i++) { var displaySpell = new Spell(spellTypes[i]); displaySpell.x = 1024 - 100 + i * 200; displaySpell.y = 1950; displaySpell.scaleX = 0.8; displaySpell.scaleY = 0.8; self.addChild(displaySpell); self.displaySpells.push(displaySpell); } self.closeButtonHover = false; self.down = function (x, y, obj) { // If showing zoom, hide it first if (self.isShowingZoom) { self.hideCardZoom(); return; } // Check if close button was clicked if (x >= 824 && x <= 1224 && y >= 2360 && y <= 2440) { // Button press effect tween(closeBtn, { scaleX: 0.75, scaleY: 0.75, tint: 0x2980b9 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(closeBtn, { scaleX: 0.8, scaleY: 0.8, tint: 0x3498db }, { duration: 100, easing: tween.easeOut }); } }); // Close deck view after animation LK.setTimeout(function () { self.closeDeckView(); }, 200); return; } // Check if any card was clicked for zoom preview for (var i = 0; i < self.displayCards.length; i++) { var card = self.displayCards[i]; var cardBounds = { left: card.x - 54, // card.scaleX is 0.6, so bounds are 90*0.6 = 54 right: card.x + 54, top: card.y - 75, // card.scaleY is 0.6, so bounds are 125*0.6 = 75 bottom: card.y + 75 }; if (x >= cardBounds.left && x <= cardBounds.right && y >= cardBounds.top && y <= cardBounds.bottom) { self.showCardZoom(card); return; } } // Check if any spell was clicked for zoom preview for (var i = 0; i < self.displaySpells.length; i++) { var spell = self.displaySpells[i]; var spellBounds = { left: spell.x - 48, // spell.scaleX is 0.8, so bounds are 60*0.8 = 48 right: spell.x + 48, top: spell.y - 48, // spell.scaleY is 0.8, so bounds are 60*0.8 = 48 bottom: spell.y + 48 }; if (x >= spellBounds.left && x <= spellBounds.right && y >= spellBounds.top && y <= spellBounds.bottom) { self.showSpellZoom(spell); return; } } }; self.move = function (x, y, obj) { // Check if mouse is over close button var isOverCloseButton = x >= 824 && x <= 1224 && y >= 2360 && y <= 2440; if (isOverCloseButton && !self.closeButtonHover) { self.closeButtonHover = true; tween(closeBtn, { scaleX: 0.9, scaleY: 0.9, tint: 0x5dade2 }, { duration: 200, easing: tween.easeOut }); } else if (!isOverCloseButton && self.closeButtonHover) { self.closeButtonHover = false; tween(closeBtn, { scaleX: 0.8, scaleY: 0.8, tint: 0x3498db }, { duration: 200, easing: tween.easeOut }); } }; self.closeDeckView = function () { // Fade out deck view tween(self, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 500, easing: tween.easeIn, onFinish: function onFinish() { // Remove deck view and return to menu game.removeChild(self); } }); }; // Entrance animation self.alpha = 0; self.scaleX = 0.8; self.scaleY = 0.8; tween(self, { alpha: 1.0, scaleX: 1.0, scaleY: 1.0 }, { duration: 600, easing: tween.easeOut }); // Card zoom preview variables for deck view self.zoomCard = null; self.zoomBg = null; self.zoomTexts = []; self.isShowingZoom = false; self.showCardZoom = function (card) { if (self.isShowingZoom) { self.hideCardZoom(); } // Create dark overlay self.zoomBg = self.attachAsset('menuOverlay', { anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 0.95 }); // Create zoomed card self.zoomCard = new Card(card.cardData); self.zoomCard.x = 1024; self.zoomCard.y = 1200; self.zoomCard.scaleX = 3.0; self.zoomCard.scaleY = 3.0; self.addChild(self.zoomCard); // Card name var nameText = new Text2(card.cardData.name, { size: 80, fill: 0xf39c12 }); nameText.anchor.set(0.5, 0.5); nameText.x = 1024; nameText.y = 800; self.addChild(nameText); self.zoomTexts.push(nameText); // Card stats var statsText = new Text2("Mana: " + card.cardData.cost + " | Saldırı: " + card.cardData.attack + " | Can: " + card.cardData.health, { size: 48, fill: 0x3498db }); statsText.anchor.set(0.5, 0.5); statsText.x = 1024; statsText.y = 900; self.addChild(statsText); self.zoomTexts.push(statsText); // Card description var descText = new Text2(card.cardData.description, { size: 40, fill: 0xecf0f1 }); descText.anchor.set(0.5, 0.5); descText.x = 1024; descText.y = 1600; self.addChild(descText); self.zoomTexts.push(descText); // Passive abilities (currently no passives, but show placeholder) var passiveText = new Text2("Pasif Yetenek: Yok", { size: 36, fill: 0x95a5a6 }); passiveText.anchor.set(0.5, 0.5); passiveText.x = 1024; passiveText.y = 1700; self.addChild(passiveText); self.zoomTexts.push(passiveText); // Close instruction var closeText = new Text2("Kapatmak için herhangi bir yere tıklayın", { size: 32, fill: 0x7f8c8d }); closeText.anchor.set(0.5, 0.5); closeText.x = 1024; closeText.y = 2000; self.addChild(closeText); self.zoomTexts.push(closeText); // Animate zoom in self.zoomCard.alpha = 0; self.zoomCard.scaleX = 1.0; self.zoomCard.scaleY = 1.0; tween(self.zoomCard, { alpha: 1.0, scaleX: 3.0, scaleY: 3.0 }, { duration: 400, easing: tween.easeOut }); for (var i = 0; i < self.zoomTexts.length; i++) { self.zoomTexts[i].alpha = 0; tween(self.zoomTexts[i], { alpha: 1.0 }, { duration: 400, easing: tween.easeOut }); } self.isShowingZoom = true; }; self.showSpellZoom = function (spell) { if (self.isShowingZoom) { self.hideCardZoom(); } // Create dark overlay self.zoomBg = self.attachAsset('menuOverlay', { anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 0.95 }); // Create zoomed spell self.zoomCard = new Spell(spell.spellData); self.zoomCard.x = 1024; self.zoomCard.y = 1200; self.zoomCard.scaleX = 4.0; self.zoomCard.scaleY = 4.0; self.addChild(self.zoomCard); // Spell name var nameText = new Text2(spell.spellData.name, { size: 80, fill: 0x9b59b6 }); nameText.anchor.set(0.5, 0.5); nameText.x = 1024; nameText.y = 800; self.addChild(nameText); self.zoomTexts.push(nameText); // Spell stats var effectText = spell.spellData.damage >= 0 ? "Hasar: " + spell.spellData.damage : "İyileştirme: " + Math.abs(spell.spellData.damage); var statsText = new Text2("Mana: " + spell.spellData.cost + " | " + effectText + " | Hedef: " + (spell.spellData.target === "enemy" ? "Düşman" : "Müttefik"), { size: 48, fill: 0x3498db }); statsText.anchor.set(0.5, 0.5); statsText.x = 1024; statsText.y = 900; self.addChild(statsText); self.zoomTexts.push(statsText); // Spell description var descText = new Text2(spell.spellData.description, { size: 40, fill: 0xecf0f1 }); descText.anchor.set(0.5, 0.5); descText.x = 1024; descText.y = 1600; self.addChild(descText); self.zoomTexts.push(descText); // Close instruction var closeText = new Text2("Kapatmak için herhangi bir yere tıklayın", { size: 32, fill: 0x7f8c8d }); closeText.anchor.set(0.5, 0.5); closeText.x = 1024; closeText.y = 2000; self.addChild(closeText); self.zoomTexts.push(closeText); // Animate zoom in self.zoomCard.alpha = 0; self.zoomCard.scaleX = 1.0; self.zoomCard.scaleY = 1.0; tween(self.zoomCard, { alpha: 1.0, scaleX: 4.0, scaleY: 4.0 }, { duration: 400, easing: tween.easeOut }); for (var i = 0; i < self.zoomTexts.length; i++) { self.zoomTexts[i].alpha = 0; tween(self.zoomTexts[i], { alpha: 1.0 }, { duration: 400, easing: tween.easeOut }); } self.isShowingZoom = true; }; self.hideCardZoom = function () { if (!self.isShowingZoom) return; // Animate zoom out if (self.zoomCard) { tween(self.zoomCard, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { self.removeChild(self.zoomCard); self.zoomCard = null; } }); } // Animate text fade out for (var i = 0; i < self.zoomTexts.length; i++) { var text = self.zoomTexts[i]; tween(text, { alpha: 0 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { self.removeChild(text); } }); } // Remove background if (self.zoomBg) { tween(self.zoomBg, { alpha: 0 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { self.removeChild(self.zoomBg); self.zoomBg = null; } }); } self.zoomTexts = []; self.isShowingZoom = false; }; return self; }); var DifficultySelection = Container.expand(function () { var self = Container.call(this); // Dark overlay background var overlay = self.attachAsset('menuOverlay', { anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 0.9 }); // Title var titleText = new Text2("ZORLUK SEÇİN", { size: 100, fill: 0xf39c12 }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 600; self.addChild(titleText); // Easy button var easyBtn = self.attachAsset('playButton', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1000, tint: 0x27ae60 }); var easyBtnText = new Text2("KOLAY", { size: 56, fill: 0xffffff }); easyBtnText.anchor.set(0.5, 0.5); easyBtnText.x = 1024; easyBtnText.y = 1000; self.addChild(easyBtnText); // Medium button var mediumBtn = self.attachAsset('playButton', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1200, tint: 0xf39c12 }); var mediumBtnText = new Text2("ORTA", { size: 56, fill: 0xffffff }); mediumBtnText.anchor.set(0.5, 0.5); mediumBtnText.x = 1024; mediumBtnText.y = 1200; self.addChild(mediumBtnText); // Hard button var hardBtn = self.attachAsset('playButton', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1400, tint: 0xe74c3c }); var hardBtnText = new Text2("ZOR", { size: 56, fill: 0xffffff }); hardBtnText.anchor.set(0.5, 0.5); hardBtnText.x = 1024; hardBtnText.y = 1400; self.addChild(hardBtnText); // Back button var backBtn = self.attachAsset('playButton', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1700, scaleX: 0.8, scaleY: 0.8, tint: 0x95a5a6 }); var backBtnText = new Text2("GERİ", { size: 48, fill: 0xffffff }); backBtnText.anchor.set(0.5, 0.5); backBtnText.x = 1024; backBtnText.y = 1700; self.addChild(backBtnText); // Button hover tracking self.easyButtonHover = false; self.mediumButtonHover = false; self.hardButtonHover = false; self.backButtonHover = false; self.down = function (x, y, obj) { // Check if easy button was clicked if (x >= 824 && x <= 1224 && y >= 940 && y <= 1060) { // Button press effect tween(easyBtn, { scaleX: 0.95, scaleY: 0.95, tint: 0x2ecc71 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(easyBtn, { scaleX: 1.0, scaleY: 1.0, tint: 0x27ae60 }, { duration: 100, easing: tween.easeOut }); } }); // Start game with easy difficulty LK.setTimeout(function () { self.startGameWithDifficulty("easy"); }, 200); } // Check if medium button was clicked else if (x >= 824 && x <= 1224 && y >= 1140 && y <= 1260) { // Button press effect tween(mediumBtn, { scaleX: 0.95, scaleY: 0.95, tint: 0xe67e22 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(mediumBtn, { scaleX: 1.0, scaleY: 1.0, tint: 0xf39c12 }, { duration: 100, easing: tween.easeOut }); } }); // Start game with medium difficulty LK.setTimeout(function () { self.startGameWithDifficulty("medium"); }, 200); } // Check if hard button was clicked else if (x >= 824 && x <= 1224 && y >= 1340 && y <= 1460) { // Button press effect tween(hardBtn, { scaleX: 0.95, scaleY: 0.95, tint: 0xc0392b }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(hardBtn, { scaleX: 1.0, scaleY: 1.0, tint: 0xe74c3c }, { duration: 100, easing: tween.easeOut }); } }); // Start game with hard difficulty LK.setTimeout(function () { self.startGameWithDifficulty("hard"); }, 200); } // Check if back button was clicked else if (x >= 904 && x <= 1144 && y >= 1660 && y <= 1740) { // Button press effect tween(backBtn, { scaleX: 0.75, scaleY: 0.75, tint: 0x7f8c8d }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(backBtn, { scaleX: 0.8, scaleY: 0.8, tint: 0x95a5a6 }, { duration: 100, easing: tween.easeOut }); } }); // Go back to main menu LK.setTimeout(function () { self.goBackToMenu(); }, 200); } }; self.move = function (x, y, obj) { // Check if mouse is over easy button var isOverEasyButton = x >= 824 && x <= 1224 && y >= 940 && y <= 1060; if (isOverEasyButton && !self.easyButtonHover) { self.easyButtonHover = true; tween(easyBtn, { scaleX: 1.1, scaleY: 1.1, tint: 0x2ecc71 }, { duration: 200, easing: tween.easeOut }); } else if (!isOverEasyButton && self.easyButtonHover) { self.easyButtonHover = false; tween(easyBtn, { scaleX: 1.0, scaleY: 1.0, tint: 0x27ae60 }, { duration: 200, easing: tween.easeOut }); } // Check if mouse is over medium button var isOverMediumButton = x >= 824 && x <= 1224 && y >= 1140 && y <= 1260; if (isOverMediumButton && !self.mediumButtonHover) { self.mediumButtonHover = true; tween(mediumBtn, { scaleX: 1.1, scaleY: 1.1, tint: 0xe67e22 }, { duration: 200, easing: tween.easeOut }); } else if (!isOverMediumButton && self.mediumButtonHover) { self.mediumButtonHover = false; tween(mediumBtn, { scaleX: 1.0, scaleY: 1.0, tint: 0xf39c12 }, { duration: 200, easing: tween.easeOut }); } // Check if mouse is over hard button var isOverHardButton = x >= 824 && x <= 1224 && y >= 1340 && y <= 1460; if (isOverHardButton && !self.hardButtonHover) { self.hardButtonHover = true; tween(hardBtn, { scaleX: 1.1, scaleY: 1.1, tint: 0xc0392b }, { duration: 200, easing: tween.easeOut }); } else if (!isOverHardButton && self.hardButtonHover) { self.hardButtonHover = false; tween(hardBtn, { scaleX: 1.0, scaleY: 1.0, tint: 0xe74c3c }, { duration: 200, easing: tween.easeOut }); } // Check if mouse is over back button var isOverBackButton = x >= 904 && x <= 1144 && y >= 1660 && y <= 1740; if (isOverBackButton && !self.backButtonHover) { self.backButtonHover = true; tween(backBtn, { scaleX: 0.9, scaleY: 0.9, tint: 0xbdc3c7 }, { duration: 200, easing: tween.easeOut }); } else if (!isOverBackButton && self.backButtonHover) { self.backButtonHover = false; tween(backBtn, { scaleX: 0.8, scaleY: 0.8, tint: 0x95a5a6 }, { duration: 200, easing: tween.easeOut }); } }; self.startGameWithDifficulty = function (difficulty) { // Store difficulty for later use gameDifficulty = difficulty; // Fade out difficulty selection tween(self, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 500, easing: tween.easeIn, onFinish: function onFinish() { // Remove difficulty selection and start game game.removeChild(self); gameState = "playing"; initializeGameplay(); } }); }; self.goBackToMenu = function () { // Fade out difficulty selection tween(self, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 500, easing: tween.easeIn, onFinish: function onFinish() { // Remove difficulty selection and return to menu game.removeChild(self); showMainMenu(); } }); }; // Entrance animation self.alpha = 0; self.scaleX = 0.8; self.scaleY = 0.8; tween(self, { alpha: 1.0, scaleX: 1.0, scaleY: 1.0 }, { duration: 600, easing: tween.easeOut }); return self; }); var MainMenu = Container.expand(function () { var self = Container.call(this); // Menu background var menuBg = self.attachAsset('menuBackground', { anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 0.8 }); // Dark overlay for better text visibility var overlay = self.attachAsset('menuOverlay', { anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 0.6 }); // Game title var titleText = new Text2("KART SAVAŞI", { size: 120, fill: 0xf39c12 }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 800; self.addChild(titleText); // Subtitle var subtitleText = new Text2("Rakibini Yenmek İçin Kartlarını Kullan", { size: 48, fill: 0xecf0f1 }); subtitleText.anchor.set(0.5, 0.5); subtitleText.x = 1024; subtitleText.y = 950; self.addChild(subtitleText); // Play button var playBtn = self.attachAsset('playButton', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1350 }); var playBtnText = new Text2("OYNA", { size: 56, fill: 0xffffff }); playBtnText.anchor.set(0.5, 0.5); playBtnText.x = 1024; playBtnText.y = 1350; self.addChild(playBtnText); // Deck button var deckBtn = self.attachAsset('playButton', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1500 }); var deckBtnText = new Text2("DESTE", { size: 56, fill: 0xffffff }); deckBtnText.anchor.set(0.5, 0.5); deckBtnText.x = 1024; deckBtnText.y = 1500; self.addChild(deckBtnText); // Instructions var instructionText = new Text2("• Kartları sürükleyerek oynat\n• Büyüleri rakip kartlara hedefle\n• Rakibin canını 0'a düşür", { size: 36, fill: 0xbdc3c7 }); instructionText.anchor.set(0.5, 0.5); instructionText.x = 1024; instructionText.y = 1750; self.addChild(instructionText); // Early Access text var earlyAccessText = new Text2("Early Access", { size: 36, fill: 0xe74c3c }); earlyAccessText.anchor.set(0.5, 0.5); earlyAccessText.x = 1024; earlyAccessText.y = 2150; self.addChild(earlyAccessText); // Waldo credit text var waldoText = new Text2("Waldo tarafından yapıldı", { size: 32, fill: 0x3498db }); waldoText.anchor.set(0.5, 0.5); waldoText.x = 1024; waldoText.y = 2220; self.addChild(waldoText); // Credits var creditsText = new Text2("FRVR Tarafından Yapıldı", { size: 28, fill: 0x95a5a6 }); creditsText.anchor.set(0.5, 0.5); creditsText.x = 1024; creditsText.y = 2280; self.addChild(creditsText); // Button hover effect self.playButtonHover = false; self.deckButtonHover = false; self.down = function (x, y, obj) { // Check if play button was clicked if (x >= 824 && x <= 1224 && y >= 1290 && y <= 1410) { // Button press effect tween(playBtn, { scaleX: 0.95, scaleY: 0.95, tint: 0x2980b9 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(playBtn, { scaleX: 1.0, scaleY: 1.0, tint: 0x3498db }, { duration: 100, easing: tween.easeOut }); } }); // Start game after animation LK.setTimeout(function () { self.startGame(); }, 200); } // Check if deck button was clicked else if (x >= 824 && x <= 1224 && y >= 1440 && y <= 1560) { // Button press effect tween(deckBtn, { scaleX: 0.95, scaleY: 0.95, tint: 0x2980b9 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(deckBtn, { scaleX: 1.0, scaleY: 1.0, tint: 0x3498db }, { duration: 100, easing: tween.easeOut }); } }); // Deck functionality can be added here later // Show deck view after animation LK.setTimeout(function () { self.showDeckView(); }, 200); } }; self.move = function (x, y, obj) { var isOverPlayButton = x >= 824 && x <= 1224 && y >= 1290 && y <= 1410; if (isOverPlayButton && !self.playButtonHover) { self.playButtonHover = true; tween(playBtn, { scaleX: 1.1, scaleY: 1.1, tint: 0x5dade2 }, { duration: 200, easing: tween.easeOut }); } else if (!isOverPlayButton && self.playButtonHover) { self.playButtonHover = false; tween(playBtn, { scaleX: 1.0, scaleY: 1.0, tint: 0x3498db }, { duration: 200, easing: tween.easeOut }); } // Check if mouse is over deck button var isOverDeckButton = x >= 824 && x <= 1224 && y >= 1440 && y <= 1560; if (isOverDeckButton && !self.deckButtonHover) { self.deckButtonHover = true; tween(deckBtn, { scaleX: 1.1, scaleY: 1.1, tint: 0x5dade2 }, { duration: 200, easing: tween.easeOut }); } else if (!isOverDeckButton && self.deckButtonHover) { self.deckButtonHover = false; tween(deckBtn, { scaleX: 1.0, scaleY: 1.0, tint: 0x3498db }, { duration: 200, easing: tween.easeOut }); } }; self.startGame = function () { // Fade out menu tween(self, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 500, easing: tween.easeIn, onFinish: function onFinish() { // Remove menu and show difficulty selection game.removeChild(self); self.showDifficultySelection(); } }); }; // Entrance animation self.alpha = 0; self.scaleX = 0.8; self.scaleY = 0.8; tween(self, { alpha: 1.0, scaleX: 1.0, scaleY: 1.0 }, { duration: 800, easing: tween.easeOut }); // Title pulse animation tween(titleText, { scaleX: 1.1, scaleY: 1.1 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(titleText, { scaleX: 1.0, scaleY: 1.0 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { // Restart pulse animation self.titlePulse(); } }); } }); self.showDeckView = function () { // Create and show deck view var deckView = new DeckView(); game.addChild(deckView); }; self.showDifficultySelection = function () { // Create and show difficulty selection var difficultySelection = new DifficultySelection(); game.addChild(difficultySelection); }; self.titlePulse = function () { tween(titleText, { scaleX: 1.1, scaleY: 1.1 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(titleText, { scaleX: 1.0, scaleY: 1.0 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { self.titlePulse(); } }); } }); }; return self; }); var Player = Container.expand(function (isHuman) { var self = Container.call(this); self.isHuman = isHuman || false; // Set health based on difficulty and player type if (gameDifficulty === "easy") { self.health = 50; // Both players have 50 health on easy } else if (gameDifficulty === "medium") { self.health = isHuman ? 50 : 80; // Player: 50, AI: 80 on medium } else if (gameDifficulty === "hard") { self.health = isHuman ? 50 : 100; // Player: 50, AI: 100 on hard } else { self.health = 50; // Default fallback } self.maxMana = 10; self.currentMana = 3; self.hand = []; self.battlefield = []; self.deck = []; self.spells = []; // Initialize with Fire Ball spell var fireBallSpell = new Spell({ name: "Fire Ball", cost: 2, damage: 4, target: "enemy", description: "Deals 4 damage to target" }); self.spells.push(fireBallSpell); // Add heal spell var healSpell = new Spell({ name: "Şifa", cost: 2, damage: -3, // Negative damage for healing target: "ally", description: "Heals target for 3 health" }); self.spells.push(healSpell); // AI player also gets spells if not human if (!isHuman) { var aiFireBallSpell = new Spell({ name: "Fire Ball", cost: 2, damage: 4, target: "enemy", description: "Deals 4 damage to target" }); self.spells.push(aiFireBallSpell); } // Initialize deck with basic cards var cardTypes = [{ name: "Fire Imp", cost: 1, attack: 3, health: 12, description: "A small fire creature" }, { name: "Water Spirit", cost: 2, attack: 3, health: 13, description: "A defensive water creature" }, { name: "Earth Golem", cost: 6, attack: 3, health: 22, description: "A powerful earth creature" }, { name: "Air Wisp", cost: 1, attack: 2, health: 13, description: "A quick air creature" }, { name: "Lightning Bolt", cost: 3, attack: 3, health: 14, description: "A shocking creature" }, { name: "Lucifer", cost: 4, attack: 3, health: 15, description: "A mystical water spirit with high endurance" }, { name: "Shadow Drake", cost: 3, attack: 4, health: 14, description: "A powerful shadow dragon" }, { name: "Michael Demiurgos", cost: 4, attack: 5, health: 14, description: "An archangel with divine power" }]; // Add exactly one of each card type to the deck for (var i = 0; i < cardTypes.length; i++) { var newCard = new Card(cardTypes[i]); newCard.validateStats(); // Ensure stats are correct self.deck.push(newCard); } // Shuffle the deck to randomize card order for (var i = self.deck.length - 1; i > 0; i--) { var randomIndex = Math.floor(Math.random() * (i + 1)); var temp = self.deck[i]; self.deck[i] = self.deck[randomIndex]; self.deck[randomIndex] = temp; } self.drawCard = function () { if (self.deck.length > 0 && self.hand.length < 7) { var card = self.deck.pop(); self.hand.push(card); LK.getSound('cardDraw').play(); return card; } return null; }; self.canPlayCard = function (card) { return card && card.cardData.cost <= self.currentMana; }; self.playCard = function (card) { var handIndex = self.hand.indexOf(card); if (handIndex >= 0 && self.canPlayCard(card) && self.battlefield.length < 3) { self.hand.splice(handIndex, 1); self.battlefield.push(card); self.currentMana -= card.cardData.cost; card.isOnBattlefield = true; LK.getSound('cardPlay').play(); return true; } return false; }; self.takeDamage = function (damage) { self.health -= damage; if (self.health < 0) self.health = 0; // Tower damage animation animateTowerDamage(!self.isHuman); // Damage number popup for tower var damageText = new Text2("-" + damage.toString(), { size: 60, fill: 0xff0000 }); damageText.anchor.set(0.5, 0.5); damageText.x = 1024 + (Math.random() - 0.5) * 100; damageText.y = self.isHuman ? 1700 : 400; damageText.alpha = 1.0; game.addChild(damageText); // Animate damage number tween(damageText, { y: damageText.y - 120, alpha: 0, scaleX: 2.0, scaleY: 2.0 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(damageText); } }); }; self.canCastSpell = function (spell) { return spell && spell.spellData.cost <= self.currentMana; }; self.castSpell = function (spell, target) { if (self.canCastSpell(spell)) { self.currentMana -= spell.spellData.cost; return spell.castSpell(target); } return false; }; self.startTurn = function () { // Base mana gain: 1 per turn var manaGain = 1; // Check if this is every 3rd turn (turnCounter is global) if (turnCounter % 3 === 0) { manaGain += 2; // Add 2 extra mana every 3 turns } self.currentMana = Math.min(self.maxMana, self.currentMana + manaGain); if (self.maxMana < 10) self.maxMana++; // Reset battlefield cards and trigger turn start passives for (var i = 0; i < self.battlefield.length; i++) { self.battlefield[i].resetForNewTurn(); self.battlefield[i].triggerPassive("turn_start", { player: self }); } // Draw a card self.drawCard(); }; return self; }); var Spell = Container.expand(function (spellData) { var self = Container.call(this); // Spell properties self.spellData = spellData || { name: "Fire Ball", cost: 2, damage: 4, target: "enemy", // "enemy", "ally", "self" description: "Deals 4 damage to target" }; self.isUsable = false; // Create spell graphics var spellBg = self.attachAsset('spellSlot', { anchorX: 0.5, anchorY: 0.5 }); // Choose appropriate spell icon based on spell name var spellAssetName = 'fireballSpell'; // default if (self.spellData.name === "Şifa") { spellAssetName = 'healSpell'; } var spellIcon = self.attachAsset(spellAssetName, { anchorX: 0.5, anchorY: 0.5 }); // Spell text elements var nameText = new Text2(self.spellData.name, { size: 18, fill: 0xFFFFFF }); nameText.anchor.set(0.5, 0); nameText.x = 0; nameText.y = -50; self.addChild(nameText); var costText = new Text2(self.spellData.cost.toString(), { size: 24, fill: 0x9B59B6 }); costText.anchor.set(0.5, 0.5); costText.x = -40; costText.y = -40; self.addChild(costText); var damageText = new Text2(self.spellData.damage.toString(), { size: 20, fill: 0xE74C3C }); damageText.anchor.set(0.5, 0.5); damageText.x = 0; damageText.y = 35; self.addChild(damageText); self.castSpell = function (target) { if (!self.isUsable) return false; // Play appropriate sound based on spell type if (self.spellData.name === "Şifa") { LK.getSound('healSound').play(); } else { LK.getSound('spellCast').play(); } // Animate spell casting var originalScale = self.scaleX; tween(self, { scaleX: 1.3, scaleY: 1.3, tint: 0xffff00 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: originalScale, scaleY: originalScale, tint: 0xFFFFFF }, { duration: 200, easing: tween.easeIn }); } }); // Create spell projectile animation var projectileAsset = self.spellData.name === "Şifa" ? 'healSpell' : 'fireballSpell'; var projectile = game.addChild(LK.getAsset(projectileAsset, { anchorX: 0.5, anchorY: 0.5, x: self.x, y: self.y })); var targetX = target ? target.x : 1024 + (Math.random() - 0.5) * 200; var targetY = target ? target.y : aiPlayer.battlefield.length > 0 ? 650 : 300; // Animate projectile to target tween(projectile, { x: targetX, y: targetY, scaleX: 1.5, scaleY: 1.5, rotation: Math.PI * 2 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { // Impact effect LK.effects.flashScreen(0xff4444, 300); // Handle both damage and healing spells if (target && target.takeDamage && target.isOnBattlefield) { if (self.spellData.damage < 0) { // Healing spell var healAmount = Math.abs(self.spellData.damage); target.currentHealth = Math.min(target.maxHealth, target.currentHealth + healAmount); target.updateHealthDisplay(); // Create healing visual effect var healText = new Text2("+" + healAmount.toString(), { size: 40, fill: 0x00ff00 }); healText.anchor.set(0.5, 0.5); healText.x = target.x + (Math.random() - 0.5) * 60; healText.y = target.y - 50; healText.alpha = 1.0; game.addChild(healText); // Animate healing number floating up and fading tween(healText, { y: healText.y - 80, alpha: 0, scaleX: 1.5, scaleY: 1.5 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(healText); } }); // Brief green flash on target LK.effects.flashObject(target, 0x00ff00, 300); } else { // Damage spell target.takeDamage(self.spellData.damage); } } // Remove projectile game.removeChild(projectile); } }); return true; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2c3e50 }); /**** * Game Code ****/ // Generic card back // Walter Spirit card assets // Lightning Bolt card assets // Air Wisp card assets // Earth Golem card assets // Water Spirit card assets // Fire Imp card assets // Game state variables var gameState = "menu"; // "menu", "playing", "gameOver" var gameDifficulty = "easy"; // "easy", "medium", "hard" - will be set by difficulty selection var currentPlayer = 0; // 0 = human, 1 = AI var gamePhase = "playing"; // "playing", "gameOver" var mainMenu = null; var selectedCard = null; var draggedCard = null; var turnCounter = 0; // Track total turns taken var combatPhase = false; // Track if we're in combat phase // Create players var humanPlayer = new Player(true); var aiPlayer = new Player(false); var players = [humanPlayer, aiPlayer]; // Create game areas var opponentAreaBg = game.addChild(LK.getAsset('opponentArea', { anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 0.3 })); var battlefieldBg = game.addChild(LK.getAsset('battlefield', { anchorX: 0, anchorY: 0, x: 0, y: 400, alpha: 0.8 })); var playerAreaBg = game.addChild(LK.getAsset('playerArea', { anchorX: 0, anchorY: 0, x: 0, y: 1800, alpha: 0.3 })); // Create visible lane graphics var lanePositions = [600, 1024, 1448]; // Left, Center, Right lanes - centered var lanes = []; // Create lanes with borders for visual clarity for (var i = 0; i < 3; i++) { // Lane border (darker background) var laneBorder = game.addChild(LK.getAsset('laneBorder', { anchorX: 0.5, anchorY: 0.5, x: lanePositions[i], y: 985, alpha: 0.8 })); // Lane background (lighter) var lane = game.addChild(LK.getAsset('lane', { anchorX: 0.5, anchorY: 0.5, x: lanePositions[i], y: 985, alpha: 0.7 })); lanes.push({ border: laneBorder, background: lane }); // Add lane number text var laneText = new Text2("Lane " + (i + 1), { size: 24, fill: 0xECF0F1 }); laneText.anchor.set(0.5, 0.5); laneText.x = lanePositions[i]; laneText.y = 1255; laneText.alpha = 0.6; game.addChild(laneText); } // UI Elements var playerHealthText = new Text2("Health: 50", { size: 48, fill: 0xECF0F1 }); playerHealthText.anchor.set(0, 0.5); playerHealthText.x = 50; playerHealthText.y = 1950; game.addChild(playerHealthText); var opponentHealthText = new Text2("Enemy: 50", { size: 48, fill: 0xECF0F1 }); opponentHealthText.anchor.set(0, 0.5); opponentHealthText.x = 50; opponentHealthText.y = 150; game.addChild(opponentHealthText); var manaText = new Text2("Mana: 3/3", { size: 36, fill: 0x9B59B6 }); manaText.anchor.set(0, 0.5); manaText.x = 50; manaText.y = 2000; game.addChild(manaText); var aiManaText = new Text2("Enemy Mana: 3/3", { size: 36, fill: 0x9B59B6 }); aiManaText.anchor.set(0, 0.5); aiManaText.x = 50; aiManaText.y = 100; game.addChild(aiManaText); var turnText = new Text2("Your Turn", { size: 42, fill: 0xF39C12 }); turnText.anchor.set(0.5, 0.5); turnText.x = 1024; turnText.y = 100; game.addChild(turnText); // End turn button var endTurnBtn = game.addChild(LK.getAsset('endTurnButton', { anchorX: 0.5, anchorY: 0.5, x: 1800, y: 1950 })); var endTurnText = new Text2("End Turn", { size: 28, fill: 0x2C3E50 }); endTurnText.anchor.set(0.5, 0.5); endTurnText.x = 1800; endTurnText.y = 1950; game.addChild(endTurnText); // Main menu button var mainMenuBtn = game.addChild(LK.getAsset('playButton', { anchorX: 0.5, anchorY: 0.5, x: 1600, y: 150, scaleX: 0.6, scaleY: 0.6 })); var mainMenuBtnText = new Text2("ANA MENÜ", { size: 20, fill: 0xffffff }); mainMenuBtnText.anchor.set(0.5, 0.5); mainMenuBtnText.x = 1600; mainMenuBtnText.y = 150; game.addChild(mainMenuBtnText); function arrangeSpells() { // Arrange human player spells var spells = humanPlayer.spells; for (var i = 0; i < spells.length; i++) { var spell = spells[i]; if (!game.children.includes(spell)) { game.addChild(spell); } spell.x = 150; spell.y = 2150 + i * 140; spell.isUsable = humanPlayer.canCastSpell(spell) && currentPlayer === 0 && !combatPhase; // Visual feedback for usable spells if (spell.isUsable) { spell.alpha = 1.0; } else { spell.alpha = 0.6; } } // AI spells are hidden from UI - no display for AI spells } function updateUI() { playerHealthText.setText("Health: " + humanPlayer.health); opponentHealthText.setText("Enemy: " + aiPlayer.health); manaText.setText("Mana: " + humanPlayer.currentMana + "/" + humanPlayer.maxMana); aiManaText.setText("Enemy Mana: " + aiPlayer.currentMana + "/" + aiPlayer.maxMana); if (combatPhase) { turnText.setText("Combat Phase"); turnText.fill = "#e67e22"; } else if (currentPlayer === 0) { turnText.setText("Your Turn"); turnText.fill = "#f39c12"; } else { turnText.setText("Enemy Turn"); turnText.fill = "#e74c3c"; } } function arrangeHand() { var handCards = humanPlayer.hand; var startX = 1024 - handCards.length * 100; for (var i = 0; i < handCards.length; i++) { var card = handCards[i]; if (!game.children.includes(card)) { game.addChild(card); } card.x = startX + i * 200; card.y = 2300; card.isPlayable = humanPlayer.canPlayCard(card); // Visual feedback for playable cards if (card.isPlayable && currentPlayer === 0) { card.alpha = 1.0; } else { card.alpha = 0.6; } } arrangeSpells(); } function arrangeBattlefield() { // Define 3 lane positions var lanePositions = [600, 1024, 1448]; // Left, Center, Right lanes - centered // Player battlefield - maintain lane assignments var playerCards = humanPlayer.battlefield; for (var i = 0; i < playerCards.length; i++) { var card = playerCards[i]; if (!game.children.includes(card)) { game.addChild(card); } // Use the card's assigned lane position instead of array index if (card.laneIndex !== undefined) { card.x = lanePositions[card.laneIndex]; card.y = 1120; } } // AI battlefield - maintain lane assignments var aiCards = aiPlayer.battlefield; for (var i = 0; i < aiCards.length; i++) { var card = aiCards[i]; if (!game.children.includes(card)) { game.addChild(card); } // Use the card's assigned lane position instead of array index if (card.laneIndex !== undefined) { card.x = lanePositions[card.laneIndex]; card.y = 650; } } } function resolveCombat() { // Process lanes sequentially - lane 1, then lane 2, then lane 3 processLaneCombat(0, function () { // Lane 1 complete, process lane 2 processLaneCombat(1, function () { // Lane 2 complete, process lane 3 processLaneCombat(2, function () { // All lanes complete, update battlefield LK.setTimeout(function () { arrangeBattlefield(); updateUI(); }, 500); }); }); }); } function processLaneCombat(laneIndex, onComplete) { var humanCard = null; var aiCard = null; // Find cards in this lane for (var i = 0; i < humanPlayer.battlefield.length; i++) { if (humanPlayer.battlefield[i].laneIndex === laneIndex && humanPlayer.battlefield[i].currentHealth > 0) { humanCard = humanPlayer.battlefield[i]; break; } } for (var i = 0; i < aiPlayer.battlefield.length; i++) { if (aiPlayer.battlefield[i].laneIndex === laneIndex && aiPlayer.battlefield[i].currentHealth > 0) { aiCard = aiPlayer.battlefield[i]; break; } } // Determine combat outcome for this lane if (humanCard && humanCard.currentHealth > 0 && aiCard && aiCard.currentHealth > 0) { // Cards fight each other - use exact attack values as written on cards var humanDamage = humanCard.cardData.attack; var aiDamage = aiCard.cardData.attack; // Ensure we're using the exact attack values animateCardVsCard(humanCard, aiCard, humanDamage, aiDamage, 0); // Wait for combat animation to complete before continuing LK.setTimeout(onComplete, 1200); } else if (humanCard && humanCard.currentHealth > 0 && !aiCard) { // Human card attacks AI tower - use card's actual attack value animateCardToTower(humanCard, "ai", 0, 0); // Wait for tower attack animation to complete before continuing LK.setTimeout(onComplete, 1000); } else if (aiCard && aiCard.currentHealth > 0 && !humanCard) { // AI card attacks human tower - use card's actual attack value animateCardToTower(aiCard, "human", 0, 0); // Wait for tower attack animation to complete before continuing LK.setTimeout(onComplete, 1000); } else { // No combat in this lane, proceed immediately LK.setTimeout(onComplete, 100); } } function animateCardVsCard(card1, card2, damage1, damage2, delay) { LK.setTimeout(function () { // Store original positions var originalX1 = card1.x; var originalY1 = card1.y; var originalX2 = card2.x; var originalY2 = card2.y; // Calculate midpoint for collision var midX = (card1.x + card2.x) / 2; var midY = (card1.y + card2.y) / 2; // Phase 1: Both cards move toward each other tween(card1, { x: midX, y: midY - 20, scaleX: 1.1, scaleY: 1.1 }, { duration: 300, easing: tween.easeOut }); tween(card2, { x: midX, y: midY + 20, scaleX: 1.1, scaleY: 1.1 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { // Phase 2: Collision effect - shake and flash LK.getSound('attack').play(); // Shake both cards tween(card1, { x: midX + 15, rotation: 0.1 }, { duration: 60, easing: tween.easeInOut, onFinish: function onFinish() { tween(card1, { x: midX - 15, rotation: -0.1 }, { duration: 60, easing: tween.easeInOut, onFinish: function onFinish() { tween(card1, { x: midX, rotation: 0 }, { duration: 60, easing: tween.easeInOut }); } }); } }); tween(card2, { x: midX - 15, rotation: -0.1 }, { duration: 60, easing: tween.easeInOut, onFinish: function onFinish() { tween(card2, { x: midX + 15, rotation: 0.1 }, { duration: 60, easing: tween.easeInOut, onFinish: function onFinish() { tween(card2, { x: midX, rotation: 0 }, { duration: 60, easing: tween.easeInOut }); } }); } }); // Flash white for collision tween(card1, { tint: 0xFFFFFF }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(card1, { tint: 0xFFFFFF }, { duration: 150, easing: tween.easeInOut }); } }); tween(card2, { tint: 0xFFFFFF }, { duration: 150, easing: tween.easeInOut, onFinish: function onFinish() { tween(card2, { tint: 0xFFFFFF }, { duration: 150, easing: tween.easeInOut }); } }); // Apply damage after collision - use exact damage values passed to function LK.setTimeout(function () { // card1 takes damage2 (from card2), card2 takes damage1 (from card1) card1.takeDamage(damage2, card2); card2.takeDamage(damage1, card1); }, 180); // Phase 3: Return to original positions LK.setTimeout(function () { tween(card1, { x: originalX1, y: originalY1, scaleX: 1.0, scaleY: 1.0 }, { duration: 400, easing: tween.easeIn }); tween(card2, { x: originalX2, y: originalY2, scaleX: 1.0, scaleY: 1.0 }, { duration: 400, easing: tween.easeIn }); }, 300); } }); }, delay); } function animateCardToTower(card, target, damage, delay) { LK.setTimeout(function () { // Store original position var originalX = card.x; var originalY = card.y; // Determine tower position var towerX = 1024; // Center of screen var towerY = target === "ai" ? 100 : 1800; // AI or human area var targetX = towerX + (Math.random() - 0.5) * 200; // Add some randomness var targetY = towerY + (Math.random() - 0.5) * 100; // Phase 1: Card charges toward tower tween(card, { x: targetX, y: targetY, scaleX: 1.2, scaleY: 1.2, rotation: Math.PI * 0.1 }, { duration: 400, easing: tween.easeOut, onFinish: function onFinish() { // Phase 2: Impact effect LK.getSound('attack').play(); // Use card's actual attack value instead of passed damage parameter var actualDamage = card.cardData.attack; // Flash the tower area if (target === "ai") { LK.effects.flashObject(opponentAreaBg, 0xff0000, 400); LK.effects.flashScreen(0xff0000, 200); aiPlayer.takeDamage(actualDamage); } else { LK.effects.flashObject(playerAreaBg, 0xff0000, 400); LK.effects.flashScreen(0xff0000, 200); humanPlayer.takeDamage(actualDamage); } // Shake the card on impact tween(card, { x: targetX + 20, rotation: Math.PI * 0.15 }, { duration: 80, easing: tween.easeInOut, onFinish: function onFinish() { tween(card, { x: targetX - 20, rotation: Math.PI * 0.05 }, { duration: 80, easing: tween.easeInOut, onFinish: function onFinish() { tween(card, { x: targetX, rotation: Math.PI * 0.1 }, { duration: 80, easing: tween.easeInOut }); } }); } }); // Phase 3: Return to original position LK.setTimeout(function () { tween(card, { x: originalX, y: originalY, scaleX: 1.0, scaleY: 1.0, rotation: 0 }, { duration: 500, easing: tween.easeIn }); }, 300); } }); }, delay); } function animateCardDeath(card) { // Death animation - fade out while spinning and shrinking tween(card, { alpha: 0, scaleX: 0.2, scaleY: 0.2, rotation: Math.PI * 2, y: card.y - 100 }, { duration: 600, easing: tween.easeIn }); } function animateTowerDamage(isAI) { // Tower damage animation - screen shake and flash var targetArea = isAI ? opponentAreaBg : playerAreaBg; var healthText = isAI ? opponentHealthText : playerHealthText; // Flash the area red LK.effects.flashObject(targetArea, 0xff0000, 500); // Screen shake effect LK.effects.flashScreen(0xff0000, 300); // Animate health text tween(healthText, { scaleX: 1.3, scaleY: 1.3, tint: 0xff0000 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(healthText, { scaleX: 1.0, scaleY: 1.0, tint: 0xFFFFFF }, { duration: 300, easing: tween.easeIn }); } }); } function checkGameOver() { if (humanPlayer.health <= 0) { gamePhase = "gameOver"; LK.showGameOver(); // Reset game after showing game over LK.setTimeout(function () { resetGame(); }, 3000); return true; } else if (aiPlayer.health <= 0) { gamePhase = "gameOver"; LK.showYouWin(); // Reset game after showing you win LK.setTimeout(function () { resetGame(); }, 3000); return true; } return false; } function resetGame() { // Reset game state gameState = "menu"; gamePhase = "playing"; currentPlayer = 0; turnCounter = 0; combatPhase = false; // Reset players with difficulty-based health if (gameDifficulty === "easy") { humanPlayer.health = 50; aiPlayer.health = 50; } else if (gameDifficulty === "medium") { humanPlayer.health = 50; aiPlayer.health = 80; } else if (gameDifficulty === "hard") { humanPlayer.health = 50; aiPlayer.health = 100; } else { humanPlayer.health = 50; aiPlayer.health = 50; } humanPlayer.currentMana = 3; humanPlayer.maxMana = 3; humanPlayer.hand = []; humanPlayer.battlefield = []; humanPlayer.deck = []; aiPlayer.currentMana = 3; aiPlayer.maxMana = 3; aiPlayer.hand = []; aiPlayer.battlefield = []; aiPlayer.deck = []; // Reinitialize decks var cardTypes = [{ name: "Fire Imp", cost: 1, attack: 3, health: 12, description: "A small fire creature" }, { name: "Water Spirit", cost: 2, attack: 3, health: 13, description: "A defensive water creature" }, { name: "Earth Golem", cost: 6, attack: 3, health: 22, description: "A powerful earth creature" }, { name: "Air Wisp", cost: 1, attack: 2, health: 13, description: "A quick air creature" }, { name: "Lightning Bolt", cost: 3, attack: 3, health: 14, description: "A shocking creature" }, { name: "Lucifer", cost: 4, attack: 3, health: 15, description: "A mystical water spirit with high endurance" }, { name: "Shadow Drake", cost: 3, attack: 4, health: 14, description: "A powerful shadow dragon" }, { name: "Michael Demiurgos", cost: 4, attack: 5, health: 14, description: "An archangel with divine power" }]; // Recreate decks for both players for (var p = 0; p < 2; p++) { var player = p === 0 ? humanPlayer : aiPlayer; for (var i = 0; i < cardTypes.length; i++) { var newCard = new Card(cardTypes[i]); newCard.validateStats(); player.deck.push(newCard); } // Shuffle deck for (var i = player.deck.length - 1; i > 0; i--) { var randomIndex = Math.floor(Math.random() * (i + 1)); var temp = player.deck[i]; player.deck[i] = player.deck[randomIndex]; player.deck[randomIndex] = temp; } } // Clear any remaining display elements selectedCard = null; draggedCard = null; // Show main menu showMainMenu(); } function endTurn() { // Trigger end turn passives for current player var currentPlayerObj = players[currentPlayer]; for (var i = 0; i < currentPlayerObj.battlefield.length; i++) { currentPlayerObj.battlefield[i].triggerPassive("end_turn", { player: currentPlayerObj }); } // Switch to next player turnCounter++; currentPlayer = 1 - currentPlayer; // Check if both players have completed their turns (every 2 turns) if (turnCounter % 2 === 0) { // Combat happens only after both players finish their turns combatPhase = true; turnText.setText("Combat Phase"); turnText.fill = "#e67e22"; // Resolve combat after both players complete their turns LK.setTimeout(function () { resolveCombat(); combatPhase = false; // Continue with next player's turn after combat players[currentPlayer].startTurn(); if (currentPlayer === 1) { // AI turn LK.setTimeout(function () { performAITurn(); }, 1000); } if (!checkGameOver()) { updateUI(); arrangeHand(); arrangeBattlefield(); } }, 1000); } else { // Just switch player without combat players[currentPlayer].startTurn(); if (currentPlayer === 1) { // AI turn LK.setTimeout(function () { performAITurn(); }, 1000); } if (!checkGameOver()) { updateUI(); arrangeHand(); arrangeBattlefield(); } } } function performAITurn() { // AI behavior based on difficulty level var shouldCastSpell = false; var shouldPlayCard = false; var spellTarget = null; var cardToPlay = null; var laneChoice = -1; // AI can cast spells first if they have enough mana var usableSpells = aiPlayer.spells.filter(function (spell) { return aiPlayer.canCastSpell(spell); }); if (usableSpells.length > 0) { if (gameDifficulty === "easy") { // Easy: 30% chance to cast spell, random targeting if (Math.random() < 0.3) { var randomSpell = usableSpells[Math.floor(Math.random() * usableSpells.length)]; var target = null; // Determine target based on spell type if (randomSpell.spellData.target === "enemy" && humanPlayer.battlefield.length > 0) { // Target random enemy card for damage spells target = humanPlayer.battlefield[Math.floor(Math.random() * humanPlayer.battlefield.length)]; } else if (randomSpell.spellData.target === "ally" && aiPlayer.battlefield.length > 0) { // Target random ally card for heal spells target = aiPlayer.battlefield[Math.floor(Math.random() * aiPlayer.battlefield.length)]; } // Only cast if target is a valid battlefield card if (target && target.isOnBattlefield && aiPlayer.castSpell(randomSpell, target)) { updateUI(); arrangeHand(); arrangeBattlefield(); } } } else if (gameDifficulty === "medium") { // Medium: 60% chance to cast spell, smart targeting if (Math.random() < 0.6) { var bestSpell = null; var bestTarget = null; var bestValue = -1; for (var s = 0; s < usableSpells.length; s++) { var spell = usableSpells[s]; if (spell.spellData.target === "enemy" && humanPlayer.battlefield.length > 0) { // Target weakest enemy card that can be killed for (var t = 0; t < humanPlayer.battlefield.length; t++) { var enemyCard = humanPlayer.battlefield[t]; var value = 0; if (enemyCard.currentHealth <= spell.spellData.damage) { value = 100; // Can kill the card } else { value = spell.spellData.damage; // Just damage value } if (value > bestValue) { bestValue = value; bestSpell = spell; bestTarget = enemyCard; } } } else if (spell.spellData.target === "ally" && aiPlayer.battlefield.length > 0) { // Heal most damaged ally for (var t = 0; t < aiPlayer.battlefield.length; t++) { var allyCard = aiPlayer.battlefield[t]; var missingHealth = allyCard.maxHealth - allyCard.currentHealth; if (missingHealth > 0 && missingHealth > bestValue) { bestValue = missingHealth; bestSpell = spell; bestTarget = allyCard; } } } } if (bestSpell && bestTarget && bestTarget.isOnBattlefield && aiPlayer.castSpell(bestSpell, bestTarget)) { updateUI(); arrangeHand(); arrangeBattlefield(); } } } else if (gameDifficulty === "hard") { // Hard: 80% chance to cast spell, optimal targeting if (Math.random() < 0.8) { var bestSpell = null; var bestTarget = null; var bestValue = -1; for (var s = 0; s < usableSpells.length; s++) { var spell = usableSpells[s]; if (spell.spellData.target === "enemy" && humanPlayer.battlefield.length > 0) { // Prioritize killing high-value targets for (var t = 0; t < humanPlayer.battlefield.length; t++) { var enemyCard = humanPlayer.battlefield[t]; var value = 0; if (enemyCard.currentHealth <= spell.spellData.damage) { // Can kill - priority based on card cost and attack value = 150 + enemyCard.cardData.cost * 10 + enemyCard.cardData.attack * 5; } else { // Just damage - lower priority value = spell.spellData.damage * 2; } if (value > bestValue) { bestValue = value; bestSpell = spell; bestTarget = enemyCard; } } } else if (spell.spellData.target === "ally" && aiPlayer.battlefield.length > 0) { // Heal most valuable damaged ally for (var t = 0; t < aiPlayer.battlefield.length; t++) { var allyCard = aiPlayer.battlefield[t]; var missingHealth = allyCard.maxHealth - allyCard.currentHealth; if (missingHealth > 0) { var value = missingHealth + allyCard.cardData.cost * 5; if (value > bestValue) { bestValue = value; bestSpell = spell; bestTarget = allyCard; } } } } } if (bestSpell && bestTarget && bestTarget.isOnBattlefield && aiPlayer.castSpell(bestSpell, bestTarget)) { updateUI(); arrangeHand(); arrangeBattlefield(); } } } } // Card playing logic based on difficulty var playableCards = aiPlayer.hand.filter(function (card) { return aiPlayer.canPlayCard(card); }); if (playableCards.length > 0 && aiPlayer.battlefield.length < 3) { var cardToPlay = null; if (gameDifficulty === "easy") { // Easy: Play random card cardToPlay = playableCards[Math.floor(Math.random() * playableCards.length)]; } else if (gameDifficulty === "medium") { // Medium: Prefer higher attack cards var bestCard = null; var bestValue = -1; for (var c = 0; c < playableCards.length; c++) { var card = playableCards[c]; var value = card.cardData.attack * 10 + card.cardData.health * 2; if (value > bestValue) { bestValue = value; bestCard = card; } } cardToPlay = bestCard; } else if (gameDifficulty === "hard") { // Hard: Strategic card selection based on cost-effectiveness and board state var bestCard = null; var bestValue = -1; for (var c = 0; c < playableCards.length; c++) { var card = playableCards[c]; // Calculate value based on attack/cost ratio, health, and board position var costEffectiveness = card.cardData.cost > 0 ? (card.cardData.attack + card.cardData.health / 2) / card.cardData.cost : card.cardData.attack + card.cardData.health; var value = costEffectiveness * 10; // Bonus for high attack cards when enemy has low health cards for (var e = 0; e < humanPlayer.battlefield.length; e++) { if (humanPlayer.battlefield[e].currentHealth <= card.cardData.attack) { value += 30; // Can kill enemy card } } if (value > bestValue) { bestValue = value; bestCard = card; } } cardToPlay = bestCard; } if (cardToPlay) { // Find available lane for AI var availableLanes = [0, 1, 2]; for (var i = 0; i < aiPlayer.battlefield.length; i++) { var occupiedLane = aiPlayer.battlefield[i].laneIndex; if (occupiedLane !== undefined) { var laneIdx = availableLanes.indexOf(occupiedLane); if (laneIdx >= 0) availableLanes.splice(laneIdx, 1); } } if (availableLanes.length > 0 && aiPlayer.playCard(cardToPlay)) { var selectedLane = -1; if (gameDifficulty === "easy") { // Easy: Random lane selection // Filter out lanes where Lucifer would face another Lucifer var validLanes = availableLanes.slice(); // Copy array if (cardToPlay.cardData.name === "Lucifer") { validLanes = availableLanes.filter(function (lane) { // Check if human has Lucifer in this lane for (var k = 0; k < humanPlayer.battlefield.length; k++) { if (humanPlayer.battlefield[k].laneIndex === lane && humanPlayer.battlefield[k].cardData.name === "Lucifer") { return false; // This lane is blocked for Lucifer } } return true; // Lane is safe for Lucifer }); } // Use valid lanes, fallback to available lanes if no valid lanes for Lucifer var lanesToUse = validLanes.length > 0 ? validLanes : availableLanes; selectedLane = lanesToUse[Math.floor(Math.random() * lanesToUse.length)]; } else if (gameDifficulty === "medium") { // Medium: Prefer center lane, avoid Lucifer conflicts var preferredLanes = availableLanes.slice(); if (preferredLanes.includes(1)) { selectedLane = 1; // Center lane preferred } else { selectedLane = preferredLanes[0]; } // Check Lucifer conflict if (cardToPlay.cardData.name === "Lucifer") { for (var k = 0; k < humanPlayer.battlefield.length; k++) { if (humanPlayer.battlefield[k].laneIndex === selectedLane && humanPlayer.battlefield[k].cardData.name === "Lucifer") { // Find alternative lane var altLanes = preferredLanes.filter(function (lane) { return lane !== selectedLane; }); if (altLanes.length > 0) { selectedLane = altLanes[0]; } break; } } } } else if (gameDifficulty === "hard") { // Hard: Strategic lane placement var bestLane = -1; var bestLaneValue = -1; for (var l = 0; l < availableLanes.length; l++) { var lane = availableLanes[l]; var laneValue = 0; // Check if this lane has an enemy card we can beat for (var e = 0; e < humanPlayer.battlefield.length; e++) { if (humanPlayer.battlefield[e].laneIndex === lane) { if (cardToPlay.cardData.attack >= humanPlayer.battlefield[e].currentHealth) { laneValue += 50; // Can kill enemy } else if (cardToPlay.cardData.health > humanPlayer.battlefield[e].cardData.attack) { laneValue += 20; // Can survive enemy attack } break; } } // If no enemy in lane, prefer center lane for board control if (laneValue === 0 && lane === 1) { laneValue = 10; } // Avoid Lucifer conflicts if (cardToPlay.cardData.name === "Lucifer") { for (var k = 0; k < humanPlayer.battlefield.length; k++) { if (humanPlayer.battlefield[k].laneIndex === lane && humanPlayer.battlefield[k].cardData.name === "Lucifer") { laneValue = -100; // Heavily penalize Lucifer conflicts break; } } } if (laneValue > bestLaneValue) { bestLaneValue = laneValue; bestLane = lane; } } selectedLane = bestLane >= 0 ? bestLane : availableLanes[0]; } cardToPlay.laneIndex = selectedLane; cardToPlay.targetLane = selectedLane; // Immediately update display after AI plays card updateUI(); arrangeHand(); arrangeBattlefield(); } } } // Combat is now handled automatically at end of turn // AI just plays cards, combat resolution happens automatically // End AI turn with different delays based on difficulty var turnDelay = 1500; if (gameDifficulty === "easy") { turnDelay = 2000; // Slower for easy difficulty } else if (gameDifficulty === "hard") { turnDelay = 1000; // Faster for hard difficulty } LK.setTimeout(function () { if (!checkGameOver()) { endTurn(); } }, turnDelay); } // Card zoom preview variables var zoomPreviewCard = null; var zoomPreviewBg = null; var zoomPreviewTimeout = null; var isShowingZoom = false; // Double-tap tracking variables var lastTappedCard = null; var lastTapTime = 0; var doubleTapThreshold = 500; // milliseconds // Button hover tracking var mainMenuButtonHover = false; // Card info display variables var cardInfoDisplay = null; var cardInfoBg = null; var isShowingCardInfo = false; function createZoomPreview(card) { if (isShowingZoom) return; // Create dark background overlay zoomPreviewBg = game.addChild(LK.getAsset('battlefield', { anchorX: 0, anchorY: 0, x: 0, y: 0, alpha: 0.8, tint: 0x000000 })); zoomPreviewBg.width = 2048; zoomPreviewBg.height = 2732; // Create zoomed card preview zoomPreviewCard = new Card(card.cardData); zoomPreviewCard.x = 1024; zoomPreviewCard.y = 1000; zoomPreviewCard.scaleX = 2.5; zoomPreviewCard.scaleY = 2.5; zoomPreviewCard.alpha = 0; game.addChild(zoomPreviewCard); // Add card name text var nameText = new Text2(card.cardData.name, { size: 60, fill: 0xFFFFFF }); nameText.anchor.set(0.5, 0.5); nameText.x = 1024; nameText.y = 700; nameText.alpha = 0; game.addChild(nameText); zoomPreviewCard.nameDisplay = nameText; // Add passive description if card has passive if (card.hasPassive()) { var passiveText = new Text2("Passive: " + getPassiveDescription(card.cardData.passive), { size: 36, fill: 0xF39C12 }); passiveText.anchor.set(0.5, 0.5); passiveText.x = 1024; passiveText.y = 1400; passiveText.alpha = 0; game.addChild(passiveText); zoomPreviewCard.passiveDisplay = passiveText; } // Add description text var descText = new Text2(card.cardData.description, { size: 32, fill: 0xECF0F1 }); descText.anchor.set(0.5, 0.5); descText.x = 1024; descText.y = card.hasPassive() ? 1500 : 1400; descText.alpha = 0; game.addChild(descText); zoomPreviewCard.descDisplay = descText; // Animate zoom in tween(zoomPreviewBg, { alpha: 0.8 }, { duration: 300, easing: tween.easeOut }); tween(zoomPreviewCard, { alpha: 1.0, scaleX: 2.5, scaleY: 2.5 }, { duration: 400, easing: tween.easeOut }); tween(nameText, { alpha: 1.0 }, { duration: 400, easing: tween.easeOut }); tween(descText, { alpha: 1.0 }, { duration: 400, easing: tween.easeOut }); if (zoomPreviewCard.passiveDisplay) { tween(zoomPreviewCard.passiveDisplay, { alpha: 1.0 }, { duration: 400, easing: tween.easeOut }); } isShowingZoom = true; } function destroyZoomPreview() { if (!isShowingZoom) return; if (zoomPreviewCard) { if (zoomPreviewCard.nameDisplay) { game.removeChild(zoomPreviewCard.nameDisplay); } if (zoomPreviewCard.passiveDisplay) { game.removeChild(zoomPreviewCard.passiveDisplay); } if (zoomPreviewCard.descDisplay) { game.removeChild(zoomPreviewCard.descDisplay); } game.removeChild(zoomPreviewCard); zoomPreviewCard = null; } if (zoomPreviewBg) { game.removeChild(zoomPreviewBg); zoomPreviewBg = null; } isShowingZoom = false; } function getPassiveDescription(passiveType) { return "No passive ability"; } function createCardInfoDisplay(card) { if (isShowingCardInfo) { destroyCardInfoDisplay(); } // Create semi-transparent background at bottom cardInfoBg = game.addChild(LK.getAsset('playerArea', { anchorX: 0, anchorY: 0, x: 0, y: 2200, alpha: 0.8, tint: 0x2c3e50 })); // Create card name text var nameText = new Text2(card.cardData.name, { size: 48, fill: 0xFFFFFF }); nameText.anchor.set(0.5, 0.5); nameText.x = 1024; nameText.y = 2320; game.addChild(nameText); // Create passive text if card has passive var passiveText = null; if (card.hasPassive()) { var passiveDesc = getPassiveDescription(card.cardData.passive); passiveText = new Text2("Pasif: " + passiveDesc, { size: 36, fill: 0xF39C12 }); passiveText.anchor.set(0.5, 0.5); passiveText.x = 1024; passiveText.y = 2380; game.addChild(passiveText); } else { passiveText = new Text2("Pasif: Yok", { size: 36, fill: 0x95A5A6 }); passiveText.anchor.set(0.5, 0.5); passiveText.x = 1024; passiveText.y = 2380; game.addChild(passiveText); } // Store references for cleanup cardInfoDisplay = { background: cardInfoBg, nameText: nameText, passiveText: passiveText }; isShowingCardInfo = true; // Auto-hide after 3 seconds LK.setTimeout(function () { destroyCardInfoDisplay(); }, 3000); } function destroyCardInfoDisplay() { if (!isShowingCardInfo || !cardInfoDisplay) return; if (cardInfoDisplay.background) { game.removeChild(cardInfoDisplay.background); } if (cardInfoDisplay.nameText) { game.removeChild(cardInfoDisplay.nameText); } if (cardInfoDisplay.passiveText) { game.removeChild(cardInfoDisplay.passiveText); } cardInfoDisplay = null; cardInfoBg = null; isShowingCardInfo = false; } // Event handlers game.down = function (x, y, obj) { if (gamePhase !== "playing" || currentPlayer !== 0 || combatPhase) return; // Close zoom preview if showing if (isShowingZoom) { destroyZoomPreview(); return; } // Close card info display if showing if (isShowingCardInfo) { destroyCardInfoDisplay(); } // Check if end turn button was clicked if (x >= 1700 && x <= 1900 && y >= 1910 && y <= 1990) { endTurn(); return; } // Check if main menu button was clicked if (x >= 1480 && x <= 1720 && y >= 110 && y <= 190) { // Button press effect tween(mainMenuBtn, { scaleX: 0.55, scaleY: 0.55, tint: 0x2980b9 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(mainMenuBtn, { scaleX: 0.6, scaleY: 0.6, tint: 0x3498db }, { duration: 100, easing: tween.easeOut }); } }); // Return to main menu and reset game LK.setTimeout(function () { resetGame(); }, 200); return; } // Check if a spell was clicked for (var i = 0; i < humanPlayer.spells.length; i++) { var spell = humanPlayer.spells[i]; var spellBounds = { left: spell.x - 60, right: spell.x + 60, top: spell.y - 60, bottom: spell.y + 60 }; if (x >= spellBounds.left && x <= spellBounds.right && y >= spellBounds.top && y <= spellBounds.bottom) { if (spell.isUsable) { // Start dragging spell instead of auto-casting draggedCard = spell; selectedCard = spell; } return; } } // Check if a hand card was clicked for (var i = 0; i < humanPlayer.hand.length; i++) { var card = humanPlayer.hand[i]; var cardBounds = { left: card.x - 90, right: card.x + 90, top: card.y - 125, bottom: card.y + 125 }; if (x >= cardBounds.left && x <= cardBounds.right && y >= cardBounds.top && y <= cardBounds.bottom) { // Check for double-tap var currentTime = Date.now(); if (lastTappedCard === card && currentTime - lastTapTime < doubleTapThreshold) { // Double-tap detected - show card info createCardInfoDisplay(card); lastTappedCard = null; lastTapTime = 0; return; } // Record this tap lastTappedCard = card; lastTapTime = currentTime; if (card.isPlayable) { selectedCard = card; draggedCard = card; // Start long press timer for zoom preview if (zoomPreviewTimeout) { LK.clearTimeout(zoomPreviewTimeout); } zoomPreviewTimeout = LK.setTimeout(function () { if (selectedCard === card) { createZoomPreview(card); draggedCard = null; // Cancel drag when showing zoom } }, 800); // 800ms long press } return; } } // Check battlefield cards for zoom preview var allBattlefieldCards = humanPlayer.battlefield.concat(aiPlayer.battlefield); for (var i = 0; i < allBattlefieldCards.length; i++) { var card = allBattlefieldCards[i]; var cardBounds = { left: card.x - 90, right: card.x + 90, top: card.y - 125, bottom: card.y + 125 }; if (x >= cardBounds.left && x <= cardBounds.right && y >= cardBounds.top && y <= cardBounds.bottom) { // Check for double-tap on battlefield cards var currentTime = Date.now(); if (lastTappedCard === card && currentTime - lastTapTime < doubleTapThreshold) { // Double-tap detected - show card info createCardInfoDisplay(card); lastTappedCard = null; lastTapTime = 0; return; } // Record this tap lastTappedCard = card; lastTapTime = currentTime; // Start long press timer for battlefield card zoom if (zoomPreviewTimeout) { LK.clearTimeout(zoomPreviewTimeout); } zoomPreviewTimeout = LK.setTimeout(function () { createZoomPreview(card); }, 800); return; } } // Battlefield cards can no longer be manually selected for attacking // Combat is now automatic at end of turn }; game.move = function (x, y, obj) { // Cancel zoom preview if user starts moving if (zoomPreviewTimeout) { LK.clearTimeout(zoomPreviewTimeout); zoomPreviewTimeout = null; } // Check if mouse is over main menu button var isOverMainMenuButton = x >= 1480 && x <= 1720 && y >= 110 && y <= 190; if (isOverMainMenuButton && !mainMenuButtonHover) { mainMenuButtonHover = true; tween(mainMenuBtn, { scaleX: 0.7, scaleY: 0.7, tint: 0x5dade2 }, { duration: 200, easing: tween.easeOut }); } else if (!isOverMainMenuButton && mainMenuButtonHover) { mainMenuButtonHover = false; tween(mainMenuBtn, { scaleX: 0.6, scaleY: 0.6, tint: 0x3498db }, { duration: 200, easing: tween.easeOut }); } if (draggedCard) { draggedCard.x = x; draggedCard.y = y; // If dragging a spell, highlight potential targets if (draggedCard.castSpell) { // Reset all card alphas first for (var i = 0; i < aiPlayer.battlefield.length; i++) { aiPlayer.battlefield[i].alpha = 1.0; } for (var i = 0; i < humanPlayer.battlefield.length; i++) { humanPlayer.battlefield[i].alpha = 1.0; } // Determine which cards to highlight based on spell target type var targetCards = []; if (draggedCard.spellData.target === "enemy") { targetCards = aiPlayer.battlefield; } else if (draggedCard.spellData.target === "ally") { targetCards = humanPlayer.battlefield; } // Highlight cards that are close to the spell for (var i = 0; i < targetCards.length; i++) { var card = targetCards[i]; var distance = Math.sqrt((card.x - x) * (card.x - x) + (card.y - y) * (card.y - y)); if (distance < 150) { card.alpha = 0.7; } } } } }; game.up = function (x, y, obj) { // Clear zoom preview timeout if (zoomPreviewTimeout) { LK.clearTimeout(zoomPreviewTimeout); zoomPreviewTimeout = null; } // Handle spell targeting if (draggedCard && draggedCard.castSpell) { var target = null; var targetCards = []; // Determine valid targets based on spell type if (draggedCard.spellData.target === "enemy") { targetCards = aiPlayer.battlefield; } else if (draggedCard.spellData.target === "ally") { targetCards = humanPlayer.battlefield; } // Check if spell was dropped on a valid target card for (var i = 0; i < targetCards.length; i++) { var card = targetCards[i]; var cardBounds = { left: card.x - 90, right: card.x + 90, top: card.y - 125, bottom: card.y + 125 }; if (x >= cardBounds.left && x <= cardBounds.right && y >= cardBounds.top && y <= cardBounds.bottom) { target = card; break; } } // Cast spell if valid target found if (target && target.isOnBattlefield && humanPlayer.castSpell(draggedCard, target)) { updateUI(); arrangeHand(); } // Reset all card alphas after spell targeting for (var i = 0; i < aiPlayer.battlefield.length; i++) { aiPlayer.battlefield[i].alpha = 1.0; } for (var i = 0; i < humanPlayer.battlefield.length; i++) { humanPlayer.battlefield[i].alpha = 1.0; } // Return spell to original position arrangeSpells(); draggedCard = null; selectedCard = null; return; } if (draggedCard && y < 1350 && y > 700) { // Determine which lane the card was dropped in var targetLane = -1; if (x >= 380 && x < 812) targetLane = 0; // Left lane else if (x >= 812 && x < 1236) targetLane = 1; // Center lane else if (x >= 1236 && x < 1668) targetLane = 2; // Right lane // Check if lane is available and card can be played if (targetLane >= 0 && humanPlayer.battlefield.length < 3) { // Check if target lane is already occupied var laneOccupied = false; for (var i = 0; i < humanPlayer.battlefield.length; i++) { if (humanPlayer.battlefield[i].laneIndex === targetLane) { laneOccupied = true; break; } } // Check if placing Lucifer would face another Lucifer in same lane var luciferBlocked = false; if (draggedCard.cardData.name === "Lucifer") { // Check if AI has Lucifer in the target lane for (var j = 0; j < aiPlayer.battlefield.length; j++) { if (aiPlayer.battlefield[j].laneIndex === targetLane && aiPlayer.battlefield[j].cardData.name === "Lucifer") { luciferBlocked = true; break; } } } if (!laneOccupied && !luciferBlocked && humanPlayer.playCard(draggedCard)) { draggedCard.laneIndex = targetLane; draggedCard.targetLane = targetLane; draggedCard = null; selectedCard = null; updateUI(); arrangeHand(); arrangeBattlefield(); } } } selectedCard = null; if (draggedCard) { arrangeHand(); draggedCard = null; } // Dead cards are now automatically removed in takeDamage function updateUI(); arrangeBattlefield(); checkGameOver(); }; // Mana regeneration timer var manaRegenTimer = 0; var manaRegenInterval = 500; // Regenerate mana every 500ms game.update = function () { if (gamePhase === "playing") { // Handle mana regeneration during active turn if (currentPlayer === 0 && !combatPhase) { // Only for human player during their turn manaRegenTimer += LK.deltaTime; if (manaRegenTimer >= manaRegenInterval) { if (humanPlayer.currentMana < humanPlayer.maxMana) { humanPlayer.currentMana = Math.min(humanPlayer.maxMana, humanPlayer.currentMana + 2); // Mana regeneration visual effect var manaOrbEffect = game.addChild(LK.getAsset('manaOrb', { anchorX: 0.5, anchorY: 0.5, x: 50, y: 2000, alpha: 0.8 })); tween(manaOrbEffect, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { game.removeChild(manaOrbEffect); } }); } manaRegenTimer = 0; } } updateUI(); } }; function initializeGameplay() { // Initialize starting hands for (var i = 0; i < 4; i++) { humanPlayer.drawCard(); aiPlayer.drawCard(); } // Play background music LK.playMusic('Darksouls'); // Initial setup updateUI(); arrangeHand(); arrangeBattlefield(); arrangeSpells(); } function showMainMenu() { if (mainMenu) { game.removeChild(mainMenu); } mainMenu = new MainMenu(); game.addChild(mainMenu); } // Initialize with main menu if (gameState === "menu") { showMainMenu(); }
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var Card = Container.expand(function (cardData) {
var self = Container.call(this);
// Card properties
self.cardData = cardData || {
name: "Basic Card",
cost: 1,
attack: 2,
health: 2,
description: "A basic creature card",
passive: null // Passive ability type - all passives removed
};
// Damage tracking to prevent duplicate triggers
self.damageDealtThisTurn = 0;
self.hasTriggeredLifeSteal = false;
// Ensure cardData has valid numeric values
if (typeof self.cardData.attack !== 'number' || isNaN(self.cardData.attack)) {
self.cardData.attack = 2;
}
if (typeof self.cardData.health !== 'number' || isNaN(self.cardData.health)) {
self.cardData.health = 2;
}
if (typeof self.cardData.cost !== 'number' || isNaN(self.cardData.cost)) {
self.cardData.cost = 1;
}
self.maxHealth = self.cardData.health;
self.currentHealth = self.cardData.health;
self.isPlayable = false;
self.isOnBattlefield = false;
self.hasAttacked = false;
// Create card graphics based on card type
var cardAssetName = 'cardBack'; // default
var symbolAssetName = null;
// Determine assets based on card name
switch (self.cardData.name) {
case "Fire Imp":
cardAssetName = 'fireImpBg';
symbolAssetName = 'fireImpSymbol';
break;
case "Water Spirit":
cardAssetName = 'waterSpiritBg';
symbolAssetName = 'waterSpiritSymbol';
break;
case "Earth Golem":
cardAssetName = 'earthGolemBg';
symbolAssetName = 'earthGolemSymbol';
break;
case "Air Wisp":
cardAssetName = 'airWispBg';
symbolAssetName = 'airWispSymbol';
break;
case "Lightning Bolt":
cardAssetName = 'lightningBoltBg';
symbolAssetName = 'lightningBoltSymbol';
break;
case "Lucifer":
cardAssetName = 'walterSpiritBg';
symbolAssetName = 'walterSpiritSymbol';
break;
case "Shadow Drake":
cardAssetName = 'shadowDrakeBg';
symbolAssetName = 'shadowDrakeSymbol';
break;
case "Michael Demiurgos":
cardAssetName = 'michaelDemiurgosBg';
symbolAssetName = 'michaelDemiurgosSymbol';
break;
}
var cardBg = self.attachAsset(cardAssetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Add symbol if available
if (symbolAssetName) {
var cardSymbol = self.attachAsset(symbolAssetName, {
anchorX: 0.5,
anchorY: 0.5
});
cardSymbol.x = 0;
cardSymbol.y = -20; // Position symbol in upper middle area
}
// Card text elements
var nameText = new Text2(self.cardData.name, {
size: 24,
fill: 0x2C3E50
});
nameText.anchor.set(0.5, 0);
nameText.x = 0;
nameText.y = -100;
self.addChild(nameText);
var costText = new Text2(self.cardData.cost.toString(), {
size: 32,
fill: 0x9B59B6
});
costText.anchor.set(0.5, 0.5);
costText.x = -70;
costText.y = -100;
self.addChild(costText);
var attackText = new Text2(self.cardData.attack.toString(), {
size: 28,
fill: 0xE74C3C
});
attackText.anchor.set(0.5, 0.5);
attackText.x = -50;
attackText.y = 90;
self.addChild(attackText);
// Store reference to attack text for potential updates
self.attackText = attackText;
var healthText = new Text2(self.currentHealth.toString(), {
size: 28,
fill: 0x27AE60
});
healthText.anchor.set(0.5, 0.5);
healthText.x = 50;
healthText.y = 90;
self.addChild(healthText);
// Store reference to health text
self.healthText = healthText;
self.updateHealthDisplay = function () {
// Ensure we display a valid number, not NaN
var displayHealth = isNaN(self.currentHealth) ? 0 : Math.max(0, self.currentHealth);
// Ensure currentHealth is always a valid number
if (isNaN(self.currentHealth)) {
self.currentHealth = 0;
}
if (isNaN(self.maxHealth)) {
self.maxHealth = self.cardData.health || 1;
}
// Update health text display
if (self.healthText) {
self.healthText.setText(displayHealth.toString());
} else if (healthText) {
healthText.setText(displayHealth.toString());
}
// Update attack text if it exists and attack value changed
if (self.attackText && self.cardData.attack !== undefined) {
self.attackText.setText(self.cardData.attack.toString());
}
// Debug log for Lightning Bolt
if (self.cardData.name === "Lightning Bolt") {
console.log("Lightning Bolt health - current:", self.currentHealth, "max:", self.maxHealth, "display:", displayHealth);
}
if (self.currentHealth <= 0) {
cardBg.alpha = 0.5;
}
};
self.takeDamage = function (damage, attacker) {
// Ensure damage is a valid number but don't reduce it to 0 unnecessarily
if (typeof damage !== 'number' || isNaN(damage)) {
damage = 0;
}
if (damage < 0) {
damage = 0;
}
// No passive damage reduction - all passives removed
var finalDamage = Math.max(0, damage);
self.currentHealth -= finalDamage;
// Ensure currentHealth is always a valid number
if (isNaN(self.currentHealth)) {
self.currentHealth = 0;
}
self.updateHealthDisplay();
// If card dies, remove it immediately
if (self.currentHealth <= 0) {
// Check if card is on battlefield before trying to find player
var ownerPlayer = null;
if (self.isOnBattlefield) {
// Safely check which player owns this card
for (var i = 0; i < humanPlayer.battlefield.length; i++) {
if (humanPlayer.battlefield[i] === self) {
ownerPlayer = humanPlayer;
break;
}
}
if (!ownerPlayer) {
for (var i = 0; i < aiPlayer.battlefield.length; i++) {
if (aiPlayer.battlefield[i] === self) {
ownerPlayer = aiPlayer;
break;
}
}
}
}
// Trigger death passive before removing
if (ownerPlayer) {
self.triggerPassive("death", {
player: ownerPlayer
});
}
// Remove from battlefield arrays and add new card to deck
var humanIndex = humanPlayer.battlefield.indexOf(self);
if (humanIndex >= 0) {
humanPlayer.battlefield.splice(humanIndex, 1);
// Create a new card with the same data instead of resetting this one
var newCard = new Card(self.cardData);
newCard.validateStats(); // Ensure stats are correct
// Add new card to human player's deck
humanPlayer.deck.push(newCard);
}
var aiIndex = aiPlayer.battlefield.indexOf(self);
if (aiIndex >= 0) {
aiPlayer.battlefield.splice(aiIndex, 1);
// Create a new card with the same data instead of resetting this one
var newCard = new Card(self.cardData);
newCard.validateStats(); // Ensure stats are correct
// Add new card to AI player's deck
aiPlayer.deck.push(newCard);
}
// Clear lane assignment
self.laneIndex = undefined;
self.isOnBattlefield = false;
// Death animation and removal from game
animateCardDeath(self);
LK.setTimeout(function () {
if (game.children.includes(self)) {
game.removeChild(self);
}
// Update battlefield layout after card removal
arrangeBattlefield();
}, 600); // Match the death animation duration
}
// Lucifer passive: heal 2 health when dealing damage to other cards
if (attacker && attacker.cardData.name === "Lucifer" && self.isOnBattlefield) {
// Heal Lucifer by 2 health (not exceeding max health)
var healAmount = 2;
attacker.currentHealth = Math.min(attacker.maxHealth, attacker.currentHealth + healAmount);
attacker.updateHealthDisplay();
// Create healing visual effect
var healText = new Text2("+" + healAmount.toString(), {
size: 40,
fill: 0x00ff00
});
healText.anchor.set(0.5, 0.5);
healText.x = attacker.x + (Math.random() - 0.5) * 60;
healText.y = attacker.y - 50;
healText.alpha = 1.0;
game.addChild(healText);
// Animate healing number floating up and fading
tween(healText, {
y: healText.y - 80,
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(healText);
}
});
// Brief green flash on Lucifer
LK.effects.flashObject(attacker, 0x00ff00, 300);
}
// Enhanced damage animation
// Screen shake for significant damage
if (damage >= 3) {
LK.effects.flashScreen(0xff4444, 200);
}
// Damage number popup animation
var damageText = new Text2("-" + damage.toString(), {
size: 40,
fill: 0xff0000
});
damageText.anchor.set(0.5, 0.5);
damageText.x = self.x + (Math.random() - 0.5) * 60;
damageText.y = self.y - 50;
damageText.alpha = 1.0;
game.addChild(damageText);
// Animate damage number floating up and fading
tween(damageText, {
y: damageText.y - 80,
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(damageText);
}
});
// Card damage animation - recoil and flash
tween(self, {
x: self.x + (Math.random() - 0.5) * 30,
y: self.y + (Math.random() - 0.5) * 20,
scaleX: 0.9,
scaleY: 0.9,
tint: 0xff4444
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
x: self.x,
y: self.y,
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeOut
});
}
});
// Flash red when taking damage
LK.effects.flashObject(self, 0xff0000, 500);
};
self.canAttack = function () {
return self.isOnBattlefield && !self.hasAttacked && self.currentHealth > 0;
};
self.attack = function (target) {
if (self.canAttack() && target) {
// Check if both cards are in the same lane
if (self.laneIndex !== undefined && target.laneIndex !== undefined && self.laneIndex !== target.laneIndex) {
return; // Cannot attack cards in different lanes
}
// Store original position
var originalX = self.x;
var originalY = self.y;
// Calculate target position (move towards target)
var targetX = target.x;
var targetY = target.y;
// Animate attack: move toward target, then back
tween(self, {
x: targetX,
y: targetY
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Collision animation - both cards shake and flash on impact
var collisionDuration = 150;
// Shake the attacking card
tween(self, {
x: targetX + 20
}, {
duration: collisionDuration / 3,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
x: targetX - 20
}, {
duration: collisionDuration / 3,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
x: targetX
}, {
duration: collisionDuration / 3,
easing: tween.easeInOut
});
}
});
}
});
// Shake the target card
tween(target, {
x: target.x - 15
}, {
duration: collisionDuration / 3,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(target, {
x: target.x + 15
}, {
duration: collisionDuration / 3,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(target, {
x: target.x
}, {
duration: collisionDuration / 3,
easing: tween.easeInOut
});
}
});
}
});
// Flash both cards white for collision effect
tween(self, {
tint: 0xFFFFFF
}, {
duration: collisionDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(self, {
tint: 0xFFFFFF
}, {
duration: collisionDuration,
easing: tween.easeInOut
});
}
});
tween(target, {
tint: 0xFFFFFF
}, {
duration: collisionDuration,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(target, {
tint: 0xFFFFFF
}, {
duration: collisionDuration,
easing: tween.easeInOut
});
}
});
// Use exactly the attack value shown on the card - no modifications
var totalDamage = self.cardData.attack;
// Ensure we're using a clean integer value
if (typeof totalDamage !== 'number' || isNaN(totalDamage)) {
totalDamage = self.cardData.attack;
}
// Debug logging for attacks
console.log("Card attacking:", self.cardData.name, "(" + totalDamage + " damage) ->", target.cardData.name, "(" + target.currentHealth + " health)");
// Deal exact damage as written on card
target.takeDamage(totalDamage, self);
// After collision, animate return to original position
LK.setTimeout(function () {
tween(self, {
x: originalX,
y: originalY
}, {
duration: 300,
easing: tween.easeIn
});
}, collisionDuration);
}
});
self.hasAttacked = true;
cardBg.alpha = 0.7;
LK.getSound('attack').play();
}
};
self.resetForNewTurn = function () {
self.hasAttacked = false;
self.damageDealtThisTurn = 0;
self.hasTriggeredLifeSteal = false;
if (self.currentHealth > 0) {
cardBg.alpha = 1.0;
}
};
// Passive ability system - all passives removed
self.triggerPassive = function (trigger, context) {
return 0;
};
self.hasPassive = function () {
return false; // All passives removed
};
// Method to validate and fix card stats
self.validateStats = function () {
// Ensure all stats are valid numbers
if (typeof self.cardData.attack !== 'number' || isNaN(self.cardData.attack)) {
self.cardData.attack = 1;
}
if (typeof self.cardData.health !== 'number' || isNaN(self.cardData.health)) {
self.cardData.health = 1;
}
if (typeof self.cardData.cost !== 'number' || isNaN(self.cardData.cost)) {
self.cardData.cost = 1;
}
if (typeof self.currentHealth !== 'number' || isNaN(self.currentHealth)) {
self.currentHealth = self.cardData.health;
}
if (typeof self.maxHealth !== 'number' || isNaN(self.maxHealth)) {
self.maxHealth = self.cardData.health;
}
// Ensure current health doesn't exceed max health
if (self.currentHealth > self.maxHealth) {
self.currentHealth = self.maxHealth;
}
// Update display after validation
self.updateHealthDisplay();
};
return self;
});
var DeckView = Container.expand(function () {
var self = Container.call(this);
// Dark overlay background
var overlay = self.attachAsset('menuOverlay', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 0.9
});
// Title
var titleText = new Text2("KART VE BÜYÜ KOLEKSİYONU", {
size: 80,
fill: 0xf39c12
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 200;
self.addChild(titleText);
// Cards section title
var cardsTitle = new Text2("KARTLAR", {
size: 60,
fill: 0x3498db
});
cardsTitle.anchor.set(0.5, 0.5);
cardsTitle.x = 1024;
cardsTitle.y = 350;
self.addChild(cardsTitle);
// Spells section title
var spellsTitle = new Text2("BÜYÜLER", {
size: 60,
fill: 0x9b59b6
});
spellsTitle.anchor.set(0.5, 0.5);
spellsTitle.x = 1024;
spellsTitle.y = 1800;
self.addChild(spellsTitle);
// Close button
var closeBtn = self.attachAsset('playButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2400,
scaleX: 0.8,
scaleY: 0.8
});
var closeBtnText = new Text2("KAPAT", {
size: 48,
fill: 0xffffff
});
closeBtnText.anchor.set(0.5, 0.5);
closeBtnText.x = 1024;
closeBtnText.y = 2400;
self.addChild(closeBtnText);
// Create sample cards for display
var cardTypes = [{
name: "Fire Imp",
cost: 1,
attack: 3,
health: 12,
description: "A small fire creature"
}, {
name: "Water Spirit",
cost: 2,
attack: 3,
health: 13,
description: "A defensive water creature"
}, {
name: "Earth Golem",
cost: 6,
attack: 3,
health: 22,
description: "A powerful earth creature"
}, {
name: "Air Wisp",
cost: 1,
attack: 2,
health: 13,
description: "A quick air creature"
}, {
name: "Lightning Bolt",
cost: 3,
attack: 3,
health: 14,
description: "A shocking creature"
}, {
name: "Lucifer",
cost: 4,
attack: 3,
health: 15,
description: "A mystical water spirit with high endurance"
}, {
name: "Shadow Drake",
cost: 3,
attack: 4,
health: 14,
description: "A powerful shadow dragon"
}, {
name: "Michael Demiurgos",
cost: 4,
attack: 5,
health: 14,
description: "An archangel with divine power"
}];
// Display cards in a grid (4 cards per row)
self.displayCards = [];
for (var i = 0; i < cardTypes.length; i++) {
var displayCard = new Card(cardTypes[i]);
var row = Math.floor(i / 4);
var col = i % 4;
var startX = 1024 - 3 * 100; // Center 4 cards
displayCard.x = startX + col * 200;
displayCard.y = 500 + row * 300;
displayCard.scaleX = 0.6;
displayCard.scaleY = 0.6;
self.addChild(displayCard);
self.displayCards.push(displayCard);
}
// Create sample spells for display
var spellTypes = [{
name: "Fire Ball",
cost: 2,
damage: 4,
target: "enemy",
description: "Deals 4 damage to target"
}, {
name: "Şifa",
cost: 2,
damage: -3,
target: "ally",
description: "Heals target for 3 health"
}];
// Display spells
self.displaySpells = [];
for (var i = 0; i < spellTypes.length; i++) {
var displaySpell = new Spell(spellTypes[i]);
displaySpell.x = 1024 - 100 + i * 200;
displaySpell.y = 1950;
displaySpell.scaleX = 0.8;
displaySpell.scaleY = 0.8;
self.addChild(displaySpell);
self.displaySpells.push(displaySpell);
}
self.closeButtonHover = false;
self.down = function (x, y, obj) {
// If showing zoom, hide it first
if (self.isShowingZoom) {
self.hideCardZoom();
return;
}
// Check if close button was clicked
if (x >= 824 && x <= 1224 && y >= 2360 && y <= 2440) {
// Button press effect
tween(closeBtn, {
scaleX: 0.75,
scaleY: 0.75,
tint: 0x2980b9
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(closeBtn, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x3498db
}, {
duration: 100,
easing: tween.easeOut
});
}
});
// Close deck view after animation
LK.setTimeout(function () {
self.closeDeckView();
}, 200);
return;
}
// Check if any card was clicked for zoom preview
for (var i = 0; i < self.displayCards.length; i++) {
var card = self.displayCards[i];
var cardBounds = {
left: card.x - 54,
// card.scaleX is 0.6, so bounds are 90*0.6 = 54
right: card.x + 54,
top: card.y - 75,
// card.scaleY is 0.6, so bounds are 125*0.6 = 75
bottom: card.y + 75
};
if (x >= cardBounds.left && x <= cardBounds.right && y >= cardBounds.top && y <= cardBounds.bottom) {
self.showCardZoom(card);
return;
}
}
// Check if any spell was clicked for zoom preview
for (var i = 0; i < self.displaySpells.length; i++) {
var spell = self.displaySpells[i];
var spellBounds = {
left: spell.x - 48,
// spell.scaleX is 0.8, so bounds are 60*0.8 = 48
right: spell.x + 48,
top: spell.y - 48,
// spell.scaleY is 0.8, so bounds are 60*0.8 = 48
bottom: spell.y + 48
};
if (x >= spellBounds.left && x <= spellBounds.right && y >= spellBounds.top && y <= spellBounds.bottom) {
self.showSpellZoom(spell);
return;
}
}
};
self.move = function (x, y, obj) {
// Check if mouse is over close button
var isOverCloseButton = x >= 824 && x <= 1224 && y >= 2360 && y <= 2440;
if (isOverCloseButton && !self.closeButtonHover) {
self.closeButtonHover = true;
tween(closeBtn, {
scaleX: 0.9,
scaleY: 0.9,
tint: 0x5dade2
}, {
duration: 200,
easing: tween.easeOut
});
} else if (!isOverCloseButton && self.closeButtonHover) {
self.closeButtonHover = false;
tween(closeBtn, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x3498db
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.closeDeckView = function () {
// Fade out deck view
tween(self, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
// Remove deck view and return to menu
game.removeChild(self);
}
});
};
// Entrance animation
self.alpha = 0;
self.scaleX = 0.8;
self.scaleY = 0.8;
tween(self, {
alpha: 1.0,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 600,
easing: tween.easeOut
});
// Card zoom preview variables for deck view
self.zoomCard = null;
self.zoomBg = null;
self.zoomTexts = [];
self.isShowingZoom = false;
self.showCardZoom = function (card) {
if (self.isShowingZoom) {
self.hideCardZoom();
}
// Create dark overlay
self.zoomBg = self.attachAsset('menuOverlay', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 0.95
});
// Create zoomed card
self.zoomCard = new Card(card.cardData);
self.zoomCard.x = 1024;
self.zoomCard.y = 1200;
self.zoomCard.scaleX = 3.0;
self.zoomCard.scaleY = 3.0;
self.addChild(self.zoomCard);
// Card name
var nameText = new Text2(card.cardData.name, {
size: 80,
fill: 0xf39c12
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 1024;
nameText.y = 800;
self.addChild(nameText);
self.zoomTexts.push(nameText);
// Card stats
var statsText = new Text2("Mana: " + card.cardData.cost + " | Saldırı: " + card.cardData.attack + " | Can: " + card.cardData.health, {
size: 48,
fill: 0x3498db
});
statsText.anchor.set(0.5, 0.5);
statsText.x = 1024;
statsText.y = 900;
self.addChild(statsText);
self.zoomTexts.push(statsText);
// Card description
var descText = new Text2(card.cardData.description, {
size: 40,
fill: 0xecf0f1
});
descText.anchor.set(0.5, 0.5);
descText.x = 1024;
descText.y = 1600;
self.addChild(descText);
self.zoomTexts.push(descText);
// Passive abilities (currently no passives, but show placeholder)
var passiveText = new Text2("Pasif Yetenek: Yok", {
size: 36,
fill: 0x95a5a6
});
passiveText.anchor.set(0.5, 0.5);
passiveText.x = 1024;
passiveText.y = 1700;
self.addChild(passiveText);
self.zoomTexts.push(passiveText);
// Close instruction
var closeText = new Text2("Kapatmak için herhangi bir yere tıklayın", {
size: 32,
fill: 0x7f8c8d
});
closeText.anchor.set(0.5, 0.5);
closeText.x = 1024;
closeText.y = 2000;
self.addChild(closeText);
self.zoomTexts.push(closeText);
// Animate zoom in
self.zoomCard.alpha = 0;
self.zoomCard.scaleX = 1.0;
self.zoomCard.scaleY = 1.0;
tween(self.zoomCard, {
alpha: 1.0,
scaleX: 3.0,
scaleY: 3.0
}, {
duration: 400,
easing: tween.easeOut
});
for (var i = 0; i < self.zoomTexts.length; i++) {
self.zoomTexts[i].alpha = 0;
tween(self.zoomTexts[i], {
alpha: 1.0
}, {
duration: 400,
easing: tween.easeOut
});
}
self.isShowingZoom = true;
};
self.showSpellZoom = function (spell) {
if (self.isShowingZoom) {
self.hideCardZoom();
}
// Create dark overlay
self.zoomBg = self.attachAsset('menuOverlay', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 0.95
});
// Create zoomed spell
self.zoomCard = new Spell(spell.spellData);
self.zoomCard.x = 1024;
self.zoomCard.y = 1200;
self.zoomCard.scaleX = 4.0;
self.zoomCard.scaleY = 4.0;
self.addChild(self.zoomCard);
// Spell name
var nameText = new Text2(spell.spellData.name, {
size: 80,
fill: 0x9b59b6
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 1024;
nameText.y = 800;
self.addChild(nameText);
self.zoomTexts.push(nameText);
// Spell stats
var effectText = spell.spellData.damage >= 0 ? "Hasar: " + spell.spellData.damage : "İyileştirme: " + Math.abs(spell.spellData.damage);
var statsText = new Text2("Mana: " + spell.spellData.cost + " | " + effectText + " | Hedef: " + (spell.spellData.target === "enemy" ? "Düşman" : "Müttefik"), {
size: 48,
fill: 0x3498db
});
statsText.anchor.set(0.5, 0.5);
statsText.x = 1024;
statsText.y = 900;
self.addChild(statsText);
self.zoomTexts.push(statsText);
// Spell description
var descText = new Text2(spell.spellData.description, {
size: 40,
fill: 0xecf0f1
});
descText.anchor.set(0.5, 0.5);
descText.x = 1024;
descText.y = 1600;
self.addChild(descText);
self.zoomTexts.push(descText);
// Close instruction
var closeText = new Text2("Kapatmak için herhangi bir yere tıklayın", {
size: 32,
fill: 0x7f8c8d
});
closeText.anchor.set(0.5, 0.5);
closeText.x = 1024;
closeText.y = 2000;
self.addChild(closeText);
self.zoomTexts.push(closeText);
// Animate zoom in
self.zoomCard.alpha = 0;
self.zoomCard.scaleX = 1.0;
self.zoomCard.scaleY = 1.0;
tween(self.zoomCard, {
alpha: 1.0,
scaleX: 4.0,
scaleY: 4.0
}, {
duration: 400,
easing: tween.easeOut
});
for (var i = 0; i < self.zoomTexts.length; i++) {
self.zoomTexts[i].alpha = 0;
tween(self.zoomTexts[i], {
alpha: 1.0
}, {
duration: 400,
easing: tween.easeOut
});
}
self.isShowingZoom = true;
};
self.hideCardZoom = function () {
if (!self.isShowingZoom) return;
// Animate zoom out
if (self.zoomCard) {
tween(self.zoomCard, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
self.removeChild(self.zoomCard);
self.zoomCard = null;
}
});
}
// Animate text fade out
for (var i = 0; i < self.zoomTexts.length; i++) {
var text = self.zoomTexts[i];
tween(text, {
alpha: 0
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
self.removeChild(text);
}
});
}
// Remove background
if (self.zoomBg) {
tween(self.zoomBg, {
alpha: 0
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
self.removeChild(self.zoomBg);
self.zoomBg = null;
}
});
}
self.zoomTexts = [];
self.isShowingZoom = false;
};
return self;
});
var DifficultySelection = Container.expand(function () {
var self = Container.call(this);
// Dark overlay background
var overlay = self.attachAsset('menuOverlay', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 0.9
});
// Title
var titleText = new Text2("ZORLUK SEÇİN", {
size: 100,
fill: 0xf39c12
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 600;
self.addChild(titleText);
// Easy button
var easyBtn = self.attachAsset('playButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1000,
tint: 0x27ae60
});
var easyBtnText = new Text2("KOLAY", {
size: 56,
fill: 0xffffff
});
easyBtnText.anchor.set(0.5, 0.5);
easyBtnText.x = 1024;
easyBtnText.y = 1000;
self.addChild(easyBtnText);
// Medium button
var mediumBtn = self.attachAsset('playButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1200,
tint: 0xf39c12
});
var mediumBtnText = new Text2("ORTA", {
size: 56,
fill: 0xffffff
});
mediumBtnText.anchor.set(0.5, 0.5);
mediumBtnText.x = 1024;
mediumBtnText.y = 1200;
self.addChild(mediumBtnText);
// Hard button
var hardBtn = self.attachAsset('playButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1400,
tint: 0xe74c3c
});
var hardBtnText = new Text2("ZOR", {
size: 56,
fill: 0xffffff
});
hardBtnText.anchor.set(0.5, 0.5);
hardBtnText.x = 1024;
hardBtnText.y = 1400;
self.addChild(hardBtnText);
// Back button
var backBtn = self.attachAsset('playButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1700,
scaleX: 0.8,
scaleY: 0.8,
tint: 0x95a5a6
});
var backBtnText = new Text2("GERİ", {
size: 48,
fill: 0xffffff
});
backBtnText.anchor.set(0.5, 0.5);
backBtnText.x = 1024;
backBtnText.y = 1700;
self.addChild(backBtnText);
// Button hover tracking
self.easyButtonHover = false;
self.mediumButtonHover = false;
self.hardButtonHover = false;
self.backButtonHover = false;
self.down = function (x, y, obj) {
// Check if easy button was clicked
if (x >= 824 && x <= 1224 && y >= 940 && y <= 1060) {
// Button press effect
tween(easyBtn, {
scaleX: 0.95,
scaleY: 0.95,
tint: 0x2ecc71
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(easyBtn, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0x27ae60
}, {
duration: 100,
easing: tween.easeOut
});
}
});
// Start game with easy difficulty
LK.setTimeout(function () {
self.startGameWithDifficulty("easy");
}, 200);
}
// Check if medium button was clicked
else if (x >= 824 && x <= 1224 && y >= 1140 && y <= 1260) {
// Button press effect
tween(mediumBtn, {
scaleX: 0.95,
scaleY: 0.95,
tint: 0xe67e22
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(mediumBtn, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xf39c12
}, {
duration: 100,
easing: tween.easeOut
});
}
});
// Start game with medium difficulty
LK.setTimeout(function () {
self.startGameWithDifficulty("medium");
}, 200);
}
// Check if hard button was clicked
else if (x >= 824 && x <= 1224 && y >= 1340 && y <= 1460) {
// Button press effect
tween(hardBtn, {
scaleX: 0.95,
scaleY: 0.95,
tint: 0xc0392b
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(hardBtn, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xe74c3c
}, {
duration: 100,
easing: tween.easeOut
});
}
});
// Start game with hard difficulty
LK.setTimeout(function () {
self.startGameWithDifficulty("hard");
}, 200);
}
// Check if back button was clicked
else if (x >= 904 && x <= 1144 && y >= 1660 && y <= 1740) {
// Button press effect
tween(backBtn, {
scaleX: 0.75,
scaleY: 0.75,
tint: 0x7f8c8d
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(backBtn, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x95a5a6
}, {
duration: 100,
easing: tween.easeOut
});
}
});
// Go back to main menu
LK.setTimeout(function () {
self.goBackToMenu();
}, 200);
}
};
self.move = function (x, y, obj) {
// Check if mouse is over easy button
var isOverEasyButton = x >= 824 && x <= 1224 && y >= 940 && y <= 1060;
if (isOverEasyButton && !self.easyButtonHover) {
self.easyButtonHover = true;
tween(easyBtn, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0x2ecc71
}, {
duration: 200,
easing: tween.easeOut
});
} else if (!isOverEasyButton && self.easyButtonHover) {
self.easyButtonHover = false;
tween(easyBtn, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0x27ae60
}, {
duration: 200,
easing: tween.easeOut
});
}
// Check if mouse is over medium button
var isOverMediumButton = x >= 824 && x <= 1224 && y >= 1140 && y <= 1260;
if (isOverMediumButton && !self.mediumButtonHover) {
self.mediumButtonHover = true;
tween(mediumBtn, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0xe67e22
}, {
duration: 200,
easing: tween.easeOut
});
} else if (!isOverMediumButton && self.mediumButtonHover) {
self.mediumButtonHover = false;
tween(mediumBtn, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xf39c12
}, {
duration: 200,
easing: tween.easeOut
});
}
// Check if mouse is over hard button
var isOverHardButton = x >= 824 && x <= 1224 && y >= 1340 && y <= 1460;
if (isOverHardButton && !self.hardButtonHover) {
self.hardButtonHover = true;
tween(hardBtn, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0xc0392b
}, {
duration: 200,
easing: tween.easeOut
});
} else if (!isOverHardButton && self.hardButtonHover) {
self.hardButtonHover = false;
tween(hardBtn, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xe74c3c
}, {
duration: 200,
easing: tween.easeOut
});
}
// Check if mouse is over back button
var isOverBackButton = x >= 904 && x <= 1144 && y >= 1660 && y <= 1740;
if (isOverBackButton && !self.backButtonHover) {
self.backButtonHover = true;
tween(backBtn, {
scaleX: 0.9,
scaleY: 0.9,
tint: 0xbdc3c7
}, {
duration: 200,
easing: tween.easeOut
});
} else if (!isOverBackButton && self.backButtonHover) {
self.backButtonHover = false;
tween(backBtn, {
scaleX: 0.8,
scaleY: 0.8,
tint: 0x95a5a6
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.startGameWithDifficulty = function (difficulty) {
// Store difficulty for later use
gameDifficulty = difficulty;
// Fade out difficulty selection
tween(self, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
// Remove difficulty selection and start game
game.removeChild(self);
gameState = "playing";
initializeGameplay();
}
});
};
self.goBackToMenu = function () {
// Fade out difficulty selection
tween(self, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
// Remove difficulty selection and return to menu
game.removeChild(self);
showMainMenu();
}
});
};
// Entrance animation
self.alpha = 0;
self.scaleX = 0.8;
self.scaleY = 0.8;
tween(self, {
alpha: 1.0,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 600,
easing: tween.easeOut
});
return self;
});
var MainMenu = Container.expand(function () {
var self = Container.call(this);
// Menu background
var menuBg = self.attachAsset('menuBackground', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 0.8
});
// Dark overlay for better text visibility
var overlay = self.attachAsset('menuOverlay', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 0.6
});
// Game title
var titleText = new Text2("KART SAVAŞI", {
size: 120,
fill: 0xf39c12
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 800;
self.addChild(titleText);
// Subtitle
var subtitleText = new Text2("Rakibini Yenmek İçin Kartlarını Kullan", {
size: 48,
fill: 0xecf0f1
});
subtitleText.anchor.set(0.5, 0.5);
subtitleText.x = 1024;
subtitleText.y = 950;
self.addChild(subtitleText);
// Play button
var playBtn = self.attachAsset('playButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1350
});
var playBtnText = new Text2("OYNA", {
size: 56,
fill: 0xffffff
});
playBtnText.anchor.set(0.5, 0.5);
playBtnText.x = 1024;
playBtnText.y = 1350;
self.addChild(playBtnText);
// Deck button
var deckBtn = self.attachAsset('playButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1500
});
var deckBtnText = new Text2("DESTE", {
size: 56,
fill: 0xffffff
});
deckBtnText.anchor.set(0.5, 0.5);
deckBtnText.x = 1024;
deckBtnText.y = 1500;
self.addChild(deckBtnText);
// Instructions
var instructionText = new Text2("• Kartları sürükleyerek oynat\n• Büyüleri rakip kartlara hedefle\n• Rakibin canını 0'a düşür", {
size: 36,
fill: 0xbdc3c7
});
instructionText.anchor.set(0.5, 0.5);
instructionText.x = 1024;
instructionText.y = 1750;
self.addChild(instructionText);
// Early Access text
var earlyAccessText = new Text2("Early Access", {
size: 36,
fill: 0xe74c3c
});
earlyAccessText.anchor.set(0.5, 0.5);
earlyAccessText.x = 1024;
earlyAccessText.y = 2150;
self.addChild(earlyAccessText);
// Waldo credit text
var waldoText = new Text2("Waldo tarafından yapıldı", {
size: 32,
fill: 0x3498db
});
waldoText.anchor.set(0.5, 0.5);
waldoText.x = 1024;
waldoText.y = 2220;
self.addChild(waldoText);
// Credits
var creditsText = new Text2("FRVR Tarafından Yapıldı", {
size: 28,
fill: 0x95a5a6
});
creditsText.anchor.set(0.5, 0.5);
creditsText.x = 1024;
creditsText.y = 2280;
self.addChild(creditsText);
// Button hover effect
self.playButtonHover = false;
self.deckButtonHover = false;
self.down = function (x, y, obj) {
// Check if play button was clicked
if (x >= 824 && x <= 1224 && y >= 1290 && y <= 1410) {
// Button press effect
tween(playBtn, {
scaleX: 0.95,
scaleY: 0.95,
tint: 0x2980b9
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(playBtn, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0x3498db
}, {
duration: 100,
easing: tween.easeOut
});
}
});
// Start game after animation
LK.setTimeout(function () {
self.startGame();
}, 200);
}
// Check if deck button was clicked
else if (x >= 824 && x <= 1224 && y >= 1440 && y <= 1560) {
// Button press effect
tween(deckBtn, {
scaleX: 0.95,
scaleY: 0.95,
tint: 0x2980b9
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(deckBtn, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0x3498db
}, {
duration: 100,
easing: tween.easeOut
});
}
});
// Deck functionality can be added here later
// Show deck view after animation
LK.setTimeout(function () {
self.showDeckView();
}, 200);
}
};
self.move = function (x, y, obj) {
var isOverPlayButton = x >= 824 && x <= 1224 && y >= 1290 && y <= 1410;
if (isOverPlayButton && !self.playButtonHover) {
self.playButtonHover = true;
tween(playBtn, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0x5dade2
}, {
duration: 200,
easing: tween.easeOut
});
} else if (!isOverPlayButton && self.playButtonHover) {
self.playButtonHover = false;
tween(playBtn, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0x3498db
}, {
duration: 200,
easing: tween.easeOut
});
}
// Check if mouse is over deck button
var isOverDeckButton = x >= 824 && x <= 1224 && y >= 1440 && y <= 1560;
if (isOverDeckButton && !self.deckButtonHover) {
self.deckButtonHover = true;
tween(deckBtn, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0x5dade2
}, {
duration: 200,
easing: tween.easeOut
});
} else if (!isOverDeckButton && self.deckButtonHover) {
self.deckButtonHover = false;
tween(deckBtn, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0x3498db
}, {
duration: 200,
easing: tween.easeOut
});
}
};
self.startGame = function () {
// Fade out menu
tween(self, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 500,
easing: tween.easeIn,
onFinish: function onFinish() {
// Remove menu and show difficulty selection
game.removeChild(self);
self.showDifficultySelection();
}
});
};
// Entrance animation
self.alpha = 0;
self.scaleX = 0.8;
self.scaleY = 0.8;
tween(self, {
alpha: 1.0,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 800,
easing: tween.easeOut
});
// Title pulse animation
tween(titleText, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Restart pulse animation
self.titlePulse();
}
});
}
});
self.showDeckView = function () {
// Create and show deck view
var deckView = new DeckView();
game.addChild(deckView);
};
self.showDifficultySelection = function () {
// Create and show difficulty selection
var difficultySelection = new DifficultySelection();
game.addChild(difficultySelection);
};
self.titlePulse = function () {
tween(titleText, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
self.titlePulse();
}
});
}
});
};
return self;
});
var Player = Container.expand(function (isHuman) {
var self = Container.call(this);
self.isHuman = isHuman || false;
// Set health based on difficulty and player type
if (gameDifficulty === "easy") {
self.health = 50; // Both players have 50 health on easy
} else if (gameDifficulty === "medium") {
self.health = isHuman ? 50 : 80; // Player: 50, AI: 80 on medium
} else if (gameDifficulty === "hard") {
self.health = isHuman ? 50 : 100; // Player: 50, AI: 100 on hard
} else {
self.health = 50; // Default fallback
}
self.maxMana = 10;
self.currentMana = 3;
self.hand = [];
self.battlefield = [];
self.deck = [];
self.spells = [];
// Initialize with Fire Ball spell
var fireBallSpell = new Spell({
name: "Fire Ball",
cost: 2,
damage: 4,
target: "enemy",
description: "Deals 4 damage to target"
});
self.spells.push(fireBallSpell);
// Add heal spell
var healSpell = new Spell({
name: "Şifa",
cost: 2,
damage: -3,
// Negative damage for healing
target: "ally",
description: "Heals target for 3 health"
});
self.spells.push(healSpell);
// AI player also gets spells if not human
if (!isHuman) {
var aiFireBallSpell = new Spell({
name: "Fire Ball",
cost: 2,
damage: 4,
target: "enemy",
description: "Deals 4 damage to target"
});
self.spells.push(aiFireBallSpell);
}
// Initialize deck with basic cards
var cardTypes = [{
name: "Fire Imp",
cost: 1,
attack: 3,
health: 12,
description: "A small fire creature"
}, {
name: "Water Spirit",
cost: 2,
attack: 3,
health: 13,
description: "A defensive water creature"
}, {
name: "Earth Golem",
cost: 6,
attack: 3,
health: 22,
description: "A powerful earth creature"
}, {
name: "Air Wisp",
cost: 1,
attack: 2,
health: 13,
description: "A quick air creature"
}, {
name: "Lightning Bolt",
cost: 3,
attack: 3,
health: 14,
description: "A shocking creature"
}, {
name: "Lucifer",
cost: 4,
attack: 3,
health: 15,
description: "A mystical water spirit with high endurance"
}, {
name: "Shadow Drake",
cost: 3,
attack: 4,
health: 14,
description: "A powerful shadow dragon"
}, {
name: "Michael Demiurgos",
cost: 4,
attack: 5,
health: 14,
description: "An archangel with divine power"
}];
// Add exactly one of each card type to the deck
for (var i = 0; i < cardTypes.length; i++) {
var newCard = new Card(cardTypes[i]);
newCard.validateStats(); // Ensure stats are correct
self.deck.push(newCard);
}
// Shuffle the deck to randomize card order
for (var i = self.deck.length - 1; i > 0; i--) {
var randomIndex = Math.floor(Math.random() * (i + 1));
var temp = self.deck[i];
self.deck[i] = self.deck[randomIndex];
self.deck[randomIndex] = temp;
}
self.drawCard = function () {
if (self.deck.length > 0 && self.hand.length < 7) {
var card = self.deck.pop();
self.hand.push(card);
LK.getSound('cardDraw').play();
return card;
}
return null;
};
self.canPlayCard = function (card) {
return card && card.cardData.cost <= self.currentMana;
};
self.playCard = function (card) {
var handIndex = self.hand.indexOf(card);
if (handIndex >= 0 && self.canPlayCard(card) && self.battlefield.length < 3) {
self.hand.splice(handIndex, 1);
self.battlefield.push(card);
self.currentMana -= card.cardData.cost;
card.isOnBattlefield = true;
LK.getSound('cardPlay').play();
return true;
}
return false;
};
self.takeDamage = function (damage) {
self.health -= damage;
if (self.health < 0) self.health = 0;
// Tower damage animation
animateTowerDamage(!self.isHuman);
// Damage number popup for tower
var damageText = new Text2("-" + damage.toString(), {
size: 60,
fill: 0xff0000
});
damageText.anchor.set(0.5, 0.5);
damageText.x = 1024 + (Math.random() - 0.5) * 100;
damageText.y = self.isHuman ? 1700 : 400;
damageText.alpha = 1.0;
game.addChild(damageText);
// Animate damage number
tween(damageText, {
y: damageText.y - 120,
alpha: 0,
scaleX: 2.0,
scaleY: 2.0
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(damageText);
}
});
};
self.canCastSpell = function (spell) {
return spell && spell.spellData.cost <= self.currentMana;
};
self.castSpell = function (spell, target) {
if (self.canCastSpell(spell)) {
self.currentMana -= spell.spellData.cost;
return spell.castSpell(target);
}
return false;
};
self.startTurn = function () {
// Base mana gain: 1 per turn
var manaGain = 1;
// Check if this is every 3rd turn (turnCounter is global)
if (turnCounter % 3 === 0) {
manaGain += 2; // Add 2 extra mana every 3 turns
}
self.currentMana = Math.min(self.maxMana, self.currentMana + manaGain);
if (self.maxMana < 10) self.maxMana++;
// Reset battlefield cards and trigger turn start passives
for (var i = 0; i < self.battlefield.length; i++) {
self.battlefield[i].resetForNewTurn();
self.battlefield[i].triggerPassive("turn_start", {
player: self
});
}
// Draw a card
self.drawCard();
};
return self;
});
var Spell = Container.expand(function (spellData) {
var self = Container.call(this);
// Spell properties
self.spellData = spellData || {
name: "Fire Ball",
cost: 2,
damage: 4,
target: "enemy",
// "enemy", "ally", "self"
description: "Deals 4 damage to target"
};
self.isUsable = false;
// Create spell graphics
var spellBg = self.attachAsset('spellSlot', {
anchorX: 0.5,
anchorY: 0.5
});
// Choose appropriate spell icon based on spell name
var spellAssetName = 'fireballSpell'; // default
if (self.spellData.name === "Şifa") {
spellAssetName = 'healSpell';
}
var spellIcon = self.attachAsset(spellAssetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Spell text elements
var nameText = new Text2(self.spellData.name, {
size: 18,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0);
nameText.x = 0;
nameText.y = -50;
self.addChild(nameText);
var costText = new Text2(self.spellData.cost.toString(), {
size: 24,
fill: 0x9B59B6
});
costText.anchor.set(0.5, 0.5);
costText.x = -40;
costText.y = -40;
self.addChild(costText);
var damageText = new Text2(self.spellData.damage.toString(), {
size: 20,
fill: 0xE74C3C
});
damageText.anchor.set(0.5, 0.5);
damageText.x = 0;
damageText.y = 35;
self.addChild(damageText);
self.castSpell = function (target) {
if (!self.isUsable) return false;
// Play appropriate sound based on spell type
if (self.spellData.name === "Şifa") {
LK.getSound('healSound').play();
} else {
LK.getSound('spellCast').play();
}
// Animate spell casting
var originalScale = self.scaleX;
tween(self, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xffff00
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(self, {
scaleX: originalScale,
scaleY: originalScale,
tint: 0xFFFFFF
}, {
duration: 200,
easing: tween.easeIn
});
}
});
// Create spell projectile animation
var projectileAsset = self.spellData.name === "Şifa" ? 'healSpell' : 'fireballSpell';
var projectile = game.addChild(LK.getAsset(projectileAsset, {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
}));
var targetX = target ? target.x : 1024 + (Math.random() - 0.5) * 200;
var targetY = target ? target.y : aiPlayer.battlefield.length > 0 ? 650 : 300;
// Animate projectile to target
tween(projectile, {
x: targetX,
y: targetY,
scaleX: 1.5,
scaleY: 1.5,
rotation: Math.PI * 2
}, {
duration: 500,
easing: tween.easeOut,
onFinish: function onFinish() {
// Impact effect
LK.effects.flashScreen(0xff4444, 300);
// Handle both damage and healing spells
if (target && target.takeDamage && target.isOnBattlefield) {
if (self.spellData.damage < 0) {
// Healing spell
var healAmount = Math.abs(self.spellData.damage);
target.currentHealth = Math.min(target.maxHealth, target.currentHealth + healAmount);
target.updateHealthDisplay();
// Create healing visual effect
var healText = new Text2("+" + healAmount.toString(), {
size: 40,
fill: 0x00ff00
});
healText.anchor.set(0.5, 0.5);
healText.x = target.x + (Math.random() - 0.5) * 60;
healText.y = target.y - 50;
healText.alpha = 1.0;
game.addChild(healText);
// Animate healing number floating up and fading
tween(healText, {
y: healText.y - 80,
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(healText);
}
});
// Brief green flash on target
LK.effects.flashObject(target, 0x00ff00, 300);
} else {
// Damage spell
target.takeDamage(self.spellData.damage);
}
}
// Remove projectile
game.removeChild(projectile);
}
});
return true;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2c3e50
});
/****
* Game Code
****/
// Generic card back
// Walter Spirit card assets
// Lightning Bolt card assets
// Air Wisp card assets
// Earth Golem card assets
// Water Spirit card assets
// Fire Imp card assets
// Game state variables
var gameState = "menu"; // "menu", "playing", "gameOver"
var gameDifficulty = "easy"; // "easy", "medium", "hard" - will be set by difficulty selection
var currentPlayer = 0; // 0 = human, 1 = AI
var gamePhase = "playing"; // "playing", "gameOver"
var mainMenu = null;
var selectedCard = null;
var draggedCard = null;
var turnCounter = 0; // Track total turns taken
var combatPhase = false; // Track if we're in combat phase
// Create players
var humanPlayer = new Player(true);
var aiPlayer = new Player(false);
var players = [humanPlayer, aiPlayer];
// Create game areas
var opponentAreaBg = game.addChild(LK.getAsset('opponentArea', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 0.3
}));
var battlefieldBg = game.addChild(LK.getAsset('battlefield', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 400,
alpha: 0.8
}));
var playerAreaBg = game.addChild(LK.getAsset('playerArea', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 1800,
alpha: 0.3
}));
// Create visible lane graphics
var lanePositions = [600, 1024, 1448]; // Left, Center, Right lanes - centered
var lanes = [];
// Create lanes with borders for visual clarity
for (var i = 0; i < 3; i++) {
// Lane border (darker background)
var laneBorder = game.addChild(LK.getAsset('laneBorder', {
anchorX: 0.5,
anchorY: 0.5,
x: lanePositions[i],
y: 985,
alpha: 0.8
}));
// Lane background (lighter)
var lane = game.addChild(LK.getAsset('lane', {
anchorX: 0.5,
anchorY: 0.5,
x: lanePositions[i],
y: 985,
alpha: 0.7
}));
lanes.push({
border: laneBorder,
background: lane
});
// Add lane number text
var laneText = new Text2("Lane " + (i + 1), {
size: 24,
fill: 0xECF0F1
});
laneText.anchor.set(0.5, 0.5);
laneText.x = lanePositions[i];
laneText.y = 1255;
laneText.alpha = 0.6;
game.addChild(laneText);
}
// UI Elements
var playerHealthText = new Text2("Health: 50", {
size: 48,
fill: 0xECF0F1
});
playerHealthText.anchor.set(0, 0.5);
playerHealthText.x = 50;
playerHealthText.y = 1950;
game.addChild(playerHealthText);
var opponentHealthText = new Text2("Enemy: 50", {
size: 48,
fill: 0xECF0F1
});
opponentHealthText.anchor.set(0, 0.5);
opponentHealthText.x = 50;
opponentHealthText.y = 150;
game.addChild(opponentHealthText);
var manaText = new Text2("Mana: 3/3", {
size: 36,
fill: 0x9B59B6
});
manaText.anchor.set(0, 0.5);
manaText.x = 50;
manaText.y = 2000;
game.addChild(manaText);
var aiManaText = new Text2("Enemy Mana: 3/3", {
size: 36,
fill: 0x9B59B6
});
aiManaText.anchor.set(0, 0.5);
aiManaText.x = 50;
aiManaText.y = 100;
game.addChild(aiManaText);
var turnText = new Text2("Your Turn", {
size: 42,
fill: 0xF39C12
});
turnText.anchor.set(0.5, 0.5);
turnText.x = 1024;
turnText.y = 100;
game.addChild(turnText);
// End turn button
var endTurnBtn = game.addChild(LK.getAsset('endTurnButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1800,
y: 1950
}));
var endTurnText = new Text2("End Turn", {
size: 28,
fill: 0x2C3E50
});
endTurnText.anchor.set(0.5, 0.5);
endTurnText.x = 1800;
endTurnText.y = 1950;
game.addChild(endTurnText);
// Main menu button
var mainMenuBtn = game.addChild(LK.getAsset('playButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 1600,
y: 150,
scaleX: 0.6,
scaleY: 0.6
}));
var mainMenuBtnText = new Text2("ANA MENÜ", {
size: 20,
fill: 0xffffff
});
mainMenuBtnText.anchor.set(0.5, 0.5);
mainMenuBtnText.x = 1600;
mainMenuBtnText.y = 150;
game.addChild(mainMenuBtnText);
function arrangeSpells() {
// Arrange human player spells
var spells = humanPlayer.spells;
for (var i = 0; i < spells.length; i++) {
var spell = spells[i];
if (!game.children.includes(spell)) {
game.addChild(spell);
}
spell.x = 150;
spell.y = 2150 + i * 140;
spell.isUsable = humanPlayer.canCastSpell(spell) && currentPlayer === 0 && !combatPhase;
// Visual feedback for usable spells
if (spell.isUsable) {
spell.alpha = 1.0;
} else {
spell.alpha = 0.6;
}
}
// AI spells are hidden from UI - no display for AI spells
}
function updateUI() {
playerHealthText.setText("Health: " + humanPlayer.health);
opponentHealthText.setText("Enemy: " + aiPlayer.health);
manaText.setText("Mana: " + humanPlayer.currentMana + "/" + humanPlayer.maxMana);
aiManaText.setText("Enemy Mana: " + aiPlayer.currentMana + "/" + aiPlayer.maxMana);
if (combatPhase) {
turnText.setText("Combat Phase");
turnText.fill = "#e67e22";
} else if (currentPlayer === 0) {
turnText.setText("Your Turn");
turnText.fill = "#f39c12";
} else {
turnText.setText("Enemy Turn");
turnText.fill = "#e74c3c";
}
}
function arrangeHand() {
var handCards = humanPlayer.hand;
var startX = 1024 - handCards.length * 100;
for (var i = 0; i < handCards.length; i++) {
var card = handCards[i];
if (!game.children.includes(card)) {
game.addChild(card);
}
card.x = startX + i * 200;
card.y = 2300;
card.isPlayable = humanPlayer.canPlayCard(card);
// Visual feedback for playable cards
if (card.isPlayable && currentPlayer === 0) {
card.alpha = 1.0;
} else {
card.alpha = 0.6;
}
}
arrangeSpells();
}
function arrangeBattlefield() {
// Define 3 lane positions
var lanePositions = [600, 1024, 1448]; // Left, Center, Right lanes - centered
// Player battlefield - maintain lane assignments
var playerCards = humanPlayer.battlefield;
for (var i = 0; i < playerCards.length; i++) {
var card = playerCards[i];
if (!game.children.includes(card)) {
game.addChild(card);
}
// Use the card's assigned lane position instead of array index
if (card.laneIndex !== undefined) {
card.x = lanePositions[card.laneIndex];
card.y = 1120;
}
}
// AI battlefield - maintain lane assignments
var aiCards = aiPlayer.battlefield;
for (var i = 0; i < aiCards.length; i++) {
var card = aiCards[i];
if (!game.children.includes(card)) {
game.addChild(card);
}
// Use the card's assigned lane position instead of array index
if (card.laneIndex !== undefined) {
card.x = lanePositions[card.laneIndex];
card.y = 650;
}
}
}
function resolveCombat() {
// Process lanes sequentially - lane 1, then lane 2, then lane 3
processLaneCombat(0, function () {
// Lane 1 complete, process lane 2
processLaneCombat(1, function () {
// Lane 2 complete, process lane 3
processLaneCombat(2, function () {
// All lanes complete, update battlefield
LK.setTimeout(function () {
arrangeBattlefield();
updateUI();
}, 500);
});
});
});
}
function processLaneCombat(laneIndex, onComplete) {
var humanCard = null;
var aiCard = null;
// Find cards in this lane
for (var i = 0; i < humanPlayer.battlefield.length; i++) {
if (humanPlayer.battlefield[i].laneIndex === laneIndex && humanPlayer.battlefield[i].currentHealth > 0) {
humanCard = humanPlayer.battlefield[i];
break;
}
}
for (var i = 0; i < aiPlayer.battlefield.length; i++) {
if (aiPlayer.battlefield[i].laneIndex === laneIndex && aiPlayer.battlefield[i].currentHealth > 0) {
aiCard = aiPlayer.battlefield[i];
break;
}
}
// Determine combat outcome for this lane
if (humanCard && humanCard.currentHealth > 0 && aiCard && aiCard.currentHealth > 0) {
// Cards fight each other - use exact attack values as written on cards
var humanDamage = humanCard.cardData.attack;
var aiDamage = aiCard.cardData.attack;
// Ensure we're using the exact attack values
animateCardVsCard(humanCard, aiCard, humanDamage, aiDamage, 0);
// Wait for combat animation to complete before continuing
LK.setTimeout(onComplete, 1200);
} else if (humanCard && humanCard.currentHealth > 0 && !aiCard) {
// Human card attacks AI tower - use card's actual attack value
animateCardToTower(humanCard, "ai", 0, 0);
// Wait for tower attack animation to complete before continuing
LK.setTimeout(onComplete, 1000);
} else if (aiCard && aiCard.currentHealth > 0 && !humanCard) {
// AI card attacks human tower - use card's actual attack value
animateCardToTower(aiCard, "human", 0, 0);
// Wait for tower attack animation to complete before continuing
LK.setTimeout(onComplete, 1000);
} else {
// No combat in this lane, proceed immediately
LK.setTimeout(onComplete, 100);
}
}
function animateCardVsCard(card1, card2, damage1, damage2, delay) {
LK.setTimeout(function () {
// Store original positions
var originalX1 = card1.x;
var originalY1 = card1.y;
var originalX2 = card2.x;
var originalY2 = card2.y;
// Calculate midpoint for collision
var midX = (card1.x + card2.x) / 2;
var midY = (card1.y + card2.y) / 2;
// Phase 1: Both cards move toward each other
tween(card1, {
x: midX,
y: midY - 20,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 300,
easing: tween.easeOut
});
tween(card2, {
x: midX,
y: midY + 20,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
// Phase 2: Collision effect - shake and flash
LK.getSound('attack').play();
// Shake both cards
tween(card1, {
x: midX + 15,
rotation: 0.1
}, {
duration: 60,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(card1, {
x: midX - 15,
rotation: -0.1
}, {
duration: 60,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(card1, {
x: midX,
rotation: 0
}, {
duration: 60,
easing: tween.easeInOut
});
}
});
}
});
tween(card2, {
x: midX - 15,
rotation: -0.1
}, {
duration: 60,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(card2, {
x: midX + 15,
rotation: 0.1
}, {
duration: 60,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(card2, {
x: midX,
rotation: 0
}, {
duration: 60,
easing: tween.easeInOut
});
}
});
}
});
// Flash white for collision
tween(card1, {
tint: 0xFFFFFF
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(card1, {
tint: 0xFFFFFF
}, {
duration: 150,
easing: tween.easeInOut
});
}
});
tween(card2, {
tint: 0xFFFFFF
}, {
duration: 150,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(card2, {
tint: 0xFFFFFF
}, {
duration: 150,
easing: tween.easeInOut
});
}
});
// Apply damage after collision - use exact damage values passed to function
LK.setTimeout(function () {
// card1 takes damage2 (from card2), card2 takes damage1 (from card1)
card1.takeDamage(damage2, card2);
card2.takeDamage(damage1, card1);
}, 180);
// Phase 3: Return to original positions
LK.setTimeout(function () {
tween(card1, {
x: originalX1,
y: originalY1,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 400,
easing: tween.easeIn
});
tween(card2, {
x: originalX2,
y: originalY2,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 400,
easing: tween.easeIn
});
}, 300);
}
});
}, delay);
}
function animateCardToTower(card, target, damage, delay) {
LK.setTimeout(function () {
// Store original position
var originalX = card.x;
var originalY = card.y;
// Determine tower position
var towerX = 1024; // Center of screen
var towerY = target === "ai" ? 100 : 1800; // AI or human area
var targetX = towerX + (Math.random() - 0.5) * 200; // Add some randomness
var targetY = towerY + (Math.random() - 0.5) * 100;
// Phase 1: Card charges toward tower
tween(card, {
x: targetX,
y: targetY,
scaleX: 1.2,
scaleY: 1.2,
rotation: Math.PI * 0.1
}, {
duration: 400,
easing: tween.easeOut,
onFinish: function onFinish() {
// Phase 2: Impact effect
LK.getSound('attack').play();
// Use card's actual attack value instead of passed damage parameter
var actualDamage = card.cardData.attack;
// Flash the tower area
if (target === "ai") {
LK.effects.flashObject(opponentAreaBg, 0xff0000, 400);
LK.effects.flashScreen(0xff0000, 200);
aiPlayer.takeDamage(actualDamage);
} else {
LK.effects.flashObject(playerAreaBg, 0xff0000, 400);
LK.effects.flashScreen(0xff0000, 200);
humanPlayer.takeDamage(actualDamage);
}
// Shake the card on impact
tween(card, {
x: targetX + 20,
rotation: Math.PI * 0.15
}, {
duration: 80,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(card, {
x: targetX - 20,
rotation: Math.PI * 0.05
}, {
duration: 80,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(card, {
x: targetX,
rotation: Math.PI * 0.1
}, {
duration: 80,
easing: tween.easeInOut
});
}
});
}
});
// Phase 3: Return to original position
LK.setTimeout(function () {
tween(card, {
x: originalX,
y: originalY,
scaleX: 1.0,
scaleY: 1.0,
rotation: 0
}, {
duration: 500,
easing: tween.easeIn
});
}, 300);
}
});
}, delay);
}
function animateCardDeath(card) {
// Death animation - fade out while spinning and shrinking
tween(card, {
alpha: 0,
scaleX: 0.2,
scaleY: 0.2,
rotation: Math.PI * 2,
y: card.y - 100
}, {
duration: 600,
easing: tween.easeIn
});
}
function animateTowerDamage(isAI) {
// Tower damage animation - screen shake and flash
var targetArea = isAI ? opponentAreaBg : playerAreaBg;
var healthText = isAI ? opponentHealthText : playerHealthText;
// Flash the area red
LK.effects.flashObject(targetArea, 0xff0000, 500);
// Screen shake effect
LK.effects.flashScreen(0xff0000, 300);
// Animate health text
tween(healthText, {
scaleX: 1.3,
scaleY: 1.3,
tint: 0xff0000
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(healthText, {
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF
}, {
duration: 300,
easing: tween.easeIn
});
}
});
}
function checkGameOver() {
if (humanPlayer.health <= 0) {
gamePhase = "gameOver";
LK.showGameOver();
// Reset game after showing game over
LK.setTimeout(function () {
resetGame();
}, 3000);
return true;
} else if (aiPlayer.health <= 0) {
gamePhase = "gameOver";
LK.showYouWin();
// Reset game after showing you win
LK.setTimeout(function () {
resetGame();
}, 3000);
return true;
}
return false;
}
function resetGame() {
// Reset game state
gameState = "menu";
gamePhase = "playing";
currentPlayer = 0;
turnCounter = 0;
combatPhase = false;
// Reset players with difficulty-based health
if (gameDifficulty === "easy") {
humanPlayer.health = 50;
aiPlayer.health = 50;
} else if (gameDifficulty === "medium") {
humanPlayer.health = 50;
aiPlayer.health = 80;
} else if (gameDifficulty === "hard") {
humanPlayer.health = 50;
aiPlayer.health = 100;
} else {
humanPlayer.health = 50;
aiPlayer.health = 50;
}
humanPlayer.currentMana = 3;
humanPlayer.maxMana = 3;
humanPlayer.hand = [];
humanPlayer.battlefield = [];
humanPlayer.deck = [];
aiPlayer.currentMana = 3;
aiPlayer.maxMana = 3;
aiPlayer.hand = [];
aiPlayer.battlefield = [];
aiPlayer.deck = [];
// Reinitialize decks
var cardTypes = [{
name: "Fire Imp",
cost: 1,
attack: 3,
health: 12,
description: "A small fire creature"
}, {
name: "Water Spirit",
cost: 2,
attack: 3,
health: 13,
description: "A defensive water creature"
}, {
name: "Earth Golem",
cost: 6,
attack: 3,
health: 22,
description: "A powerful earth creature"
}, {
name: "Air Wisp",
cost: 1,
attack: 2,
health: 13,
description: "A quick air creature"
}, {
name: "Lightning Bolt",
cost: 3,
attack: 3,
health: 14,
description: "A shocking creature"
}, {
name: "Lucifer",
cost: 4,
attack: 3,
health: 15,
description: "A mystical water spirit with high endurance"
}, {
name: "Shadow Drake",
cost: 3,
attack: 4,
health: 14,
description: "A powerful shadow dragon"
}, {
name: "Michael Demiurgos",
cost: 4,
attack: 5,
health: 14,
description: "An archangel with divine power"
}];
// Recreate decks for both players
for (var p = 0; p < 2; p++) {
var player = p === 0 ? humanPlayer : aiPlayer;
for (var i = 0; i < cardTypes.length; i++) {
var newCard = new Card(cardTypes[i]);
newCard.validateStats();
player.deck.push(newCard);
}
// Shuffle deck
for (var i = player.deck.length - 1; i > 0; i--) {
var randomIndex = Math.floor(Math.random() * (i + 1));
var temp = player.deck[i];
player.deck[i] = player.deck[randomIndex];
player.deck[randomIndex] = temp;
}
}
// Clear any remaining display elements
selectedCard = null;
draggedCard = null;
// Show main menu
showMainMenu();
}
function endTurn() {
// Trigger end turn passives for current player
var currentPlayerObj = players[currentPlayer];
for (var i = 0; i < currentPlayerObj.battlefield.length; i++) {
currentPlayerObj.battlefield[i].triggerPassive("end_turn", {
player: currentPlayerObj
});
}
// Switch to next player
turnCounter++;
currentPlayer = 1 - currentPlayer;
// Check if both players have completed their turns (every 2 turns)
if (turnCounter % 2 === 0) {
// Combat happens only after both players finish their turns
combatPhase = true;
turnText.setText("Combat Phase");
turnText.fill = "#e67e22";
// Resolve combat after both players complete their turns
LK.setTimeout(function () {
resolveCombat();
combatPhase = false;
// Continue with next player's turn after combat
players[currentPlayer].startTurn();
if (currentPlayer === 1) {
// AI turn
LK.setTimeout(function () {
performAITurn();
}, 1000);
}
if (!checkGameOver()) {
updateUI();
arrangeHand();
arrangeBattlefield();
}
}, 1000);
} else {
// Just switch player without combat
players[currentPlayer].startTurn();
if (currentPlayer === 1) {
// AI turn
LK.setTimeout(function () {
performAITurn();
}, 1000);
}
if (!checkGameOver()) {
updateUI();
arrangeHand();
arrangeBattlefield();
}
}
}
function performAITurn() {
// AI behavior based on difficulty level
var shouldCastSpell = false;
var shouldPlayCard = false;
var spellTarget = null;
var cardToPlay = null;
var laneChoice = -1;
// AI can cast spells first if they have enough mana
var usableSpells = aiPlayer.spells.filter(function (spell) {
return aiPlayer.canCastSpell(spell);
});
if (usableSpells.length > 0) {
if (gameDifficulty === "easy") {
// Easy: 30% chance to cast spell, random targeting
if (Math.random() < 0.3) {
var randomSpell = usableSpells[Math.floor(Math.random() * usableSpells.length)];
var target = null;
// Determine target based on spell type
if (randomSpell.spellData.target === "enemy" && humanPlayer.battlefield.length > 0) {
// Target random enemy card for damage spells
target = humanPlayer.battlefield[Math.floor(Math.random() * humanPlayer.battlefield.length)];
} else if (randomSpell.spellData.target === "ally" && aiPlayer.battlefield.length > 0) {
// Target random ally card for heal spells
target = aiPlayer.battlefield[Math.floor(Math.random() * aiPlayer.battlefield.length)];
}
// Only cast if target is a valid battlefield card
if (target && target.isOnBattlefield && aiPlayer.castSpell(randomSpell, target)) {
updateUI();
arrangeHand();
arrangeBattlefield();
}
}
} else if (gameDifficulty === "medium") {
// Medium: 60% chance to cast spell, smart targeting
if (Math.random() < 0.6) {
var bestSpell = null;
var bestTarget = null;
var bestValue = -1;
for (var s = 0; s < usableSpells.length; s++) {
var spell = usableSpells[s];
if (spell.spellData.target === "enemy" && humanPlayer.battlefield.length > 0) {
// Target weakest enemy card that can be killed
for (var t = 0; t < humanPlayer.battlefield.length; t++) {
var enemyCard = humanPlayer.battlefield[t];
var value = 0;
if (enemyCard.currentHealth <= spell.spellData.damage) {
value = 100; // Can kill the card
} else {
value = spell.spellData.damage; // Just damage value
}
if (value > bestValue) {
bestValue = value;
bestSpell = spell;
bestTarget = enemyCard;
}
}
} else if (spell.spellData.target === "ally" && aiPlayer.battlefield.length > 0) {
// Heal most damaged ally
for (var t = 0; t < aiPlayer.battlefield.length; t++) {
var allyCard = aiPlayer.battlefield[t];
var missingHealth = allyCard.maxHealth - allyCard.currentHealth;
if (missingHealth > 0 && missingHealth > bestValue) {
bestValue = missingHealth;
bestSpell = spell;
bestTarget = allyCard;
}
}
}
}
if (bestSpell && bestTarget && bestTarget.isOnBattlefield && aiPlayer.castSpell(bestSpell, bestTarget)) {
updateUI();
arrangeHand();
arrangeBattlefield();
}
}
} else if (gameDifficulty === "hard") {
// Hard: 80% chance to cast spell, optimal targeting
if (Math.random() < 0.8) {
var bestSpell = null;
var bestTarget = null;
var bestValue = -1;
for (var s = 0; s < usableSpells.length; s++) {
var spell = usableSpells[s];
if (spell.spellData.target === "enemy" && humanPlayer.battlefield.length > 0) {
// Prioritize killing high-value targets
for (var t = 0; t < humanPlayer.battlefield.length; t++) {
var enemyCard = humanPlayer.battlefield[t];
var value = 0;
if (enemyCard.currentHealth <= spell.spellData.damage) {
// Can kill - priority based on card cost and attack
value = 150 + enemyCard.cardData.cost * 10 + enemyCard.cardData.attack * 5;
} else {
// Just damage - lower priority
value = spell.spellData.damage * 2;
}
if (value > bestValue) {
bestValue = value;
bestSpell = spell;
bestTarget = enemyCard;
}
}
} else if (spell.spellData.target === "ally" && aiPlayer.battlefield.length > 0) {
// Heal most valuable damaged ally
for (var t = 0; t < aiPlayer.battlefield.length; t++) {
var allyCard = aiPlayer.battlefield[t];
var missingHealth = allyCard.maxHealth - allyCard.currentHealth;
if (missingHealth > 0) {
var value = missingHealth + allyCard.cardData.cost * 5;
if (value > bestValue) {
bestValue = value;
bestSpell = spell;
bestTarget = allyCard;
}
}
}
}
}
if (bestSpell && bestTarget && bestTarget.isOnBattlefield && aiPlayer.castSpell(bestSpell, bestTarget)) {
updateUI();
arrangeHand();
arrangeBattlefield();
}
}
}
}
// Card playing logic based on difficulty
var playableCards = aiPlayer.hand.filter(function (card) {
return aiPlayer.canPlayCard(card);
});
if (playableCards.length > 0 && aiPlayer.battlefield.length < 3) {
var cardToPlay = null;
if (gameDifficulty === "easy") {
// Easy: Play random card
cardToPlay = playableCards[Math.floor(Math.random() * playableCards.length)];
} else if (gameDifficulty === "medium") {
// Medium: Prefer higher attack cards
var bestCard = null;
var bestValue = -1;
for (var c = 0; c < playableCards.length; c++) {
var card = playableCards[c];
var value = card.cardData.attack * 10 + card.cardData.health * 2;
if (value > bestValue) {
bestValue = value;
bestCard = card;
}
}
cardToPlay = bestCard;
} else if (gameDifficulty === "hard") {
// Hard: Strategic card selection based on cost-effectiveness and board state
var bestCard = null;
var bestValue = -1;
for (var c = 0; c < playableCards.length; c++) {
var card = playableCards[c];
// Calculate value based on attack/cost ratio, health, and board position
var costEffectiveness = card.cardData.cost > 0 ? (card.cardData.attack + card.cardData.health / 2) / card.cardData.cost : card.cardData.attack + card.cardData.health;
var value = costEffectiveness * 10;
// Bonus for high attack cards when enemy has low health cards
for (var e = 0; e < humanPlayer.battlefield.length; e++) {
if (humanPlayer.battlefield[e].currentHealth <= card.cardData.attack) {
value += 30; // Can kill enemy card
}
}
if (value > bestValue) {
bestValue = value;
bestCard = card;
}
}
cardToPlay = bestCard;
}
if (cardToPlay) {
// Find available lane for AI
var availableLanes = [0, 1, 2];
for (var i = 0; i < aiPlayer.battlefield.length; i++) {
var occupiedLane = aiPlayer.battlefield[i].laneIndex;
if (occupiedLane !== undefined) {
var laneIdx = availableLanes.indexOf(occupiedLane);
if (laneIdx >= 0) availableLanes.splice(laneIdx, 1);
}
}
if (availableLanes.length > 0 && aiPlayer.playCard(cardToPlay)) {
var selectedLane = -1;
if (gameDifficulty === "easy") {
// Easy: Random lane selection
// Filter out lanes where Lucifer would face another Lucifer
var validLanes = availableLanes.slice(); // Copy array
if (cardToPlay.cardData.name === "Lucifer") {
validLanes = availableLanes.filter(function (lane) {
// Check if human has Lucifer in this lane
for (var k = 0; k < humanPlayer.battlefield.length; k++) {
if (humanPlayer.battlefield[k].laneIndex === lane && humanPlayer.battlefield[k].cardData.name === "Lucifer") {
return false; // This lane is blocked for Lucifer
}
}
return true; // Lane is safe for Lucifer
});
}
// Use valid lanes, fallback to available lanes if no valid lanes for Lucifer
var lanesToUse = validLanes.length > 0 ? validLanes : availableLanes;
selectedLane = lanesToUse[Math.floor(Math.random() * lanesToUse.length)];
} else if (gameDifficulty === "medium") {
// Medium: Prefer center lane, avoid Lucifer conflicts
var preferredLanes = availableLanes.slice();
if (preferredLanes.includes(1)) {
selectedLane = 1; // Center lane preferred
} else {
selectedLane = preferredLanes[0];
}
// Check Lucifer conflict
if (cardToPlay.cardData.name === "Lucifer") {
for (var k = 0; k < humanPlayer.battlefield.length; k++) {
if (humanPlayer.battlefield[k].laneIndex === selectedLane && humanPlayer.battlefield[k].cardData.name === "Lucifer") {
// Find alternative lane
var altLanes = preferredLanes.filter(function (lane) {
return lane !== selectedLane;
});
if (altLanes.length > 0) {
selectedLane = altLanes[0];
}
break;
}
}
}
} else if (gameDifficulty === "hard") {
// Hard: Strategic lane placement
var bestLane = -1;
var bestLaneValue = -1;
for (var l = 0; l < availableLanes.length; l++) {
var lane = availableLanes[l];
var laneValue = 0;
// Check if this lane has an enemy card we can beat
for (var e = 0; e < humanPlayer.battlefield.length; e++) {
if (humanPlayer.battlefield[e].laneIndex === lane) {
if (cardToPlay.cardData.attack >= humanPlayer.battlefield[e].currentHealth) {
laneValue += 50; // Can kill enemy
} else if (cardToPlay.cardData.health > humanPlayer.battlefield[e].cardData.attack) {
laneValue += 20; // Can survive enemy attack
}
break;
}
}
// If no enemy in lane, prefer center lane for board control
if (laneValue === 0 && lane === 1) {
laneValue = 10;
}
// Avoid Lucifer conflicts
if (cardToPlay.cardData.name === "Lucifer") {
for (var k = 0; k < humanPlayer.battlefield.length; k++) {
if (humanPlayer.battlefield[k].laneIndex === lane && humanPlayer.battlefield[k].cardData.name === "Lucifer") {
laneValue = -100; // Heavily penalize Lucifer conflicts
break;
}
}
}
if (laneValue > bestLaneValue) {
bestLaneValue = laneValue;
bestLane = lane;
}
}
selectedLane = bestLane >= 0 ? bestLane : availableLanes[0];
}
cardToPlay.laneIndex = selectedLane;
cardToPlay.targetLane = selectedLane;
// Immediately update display after AI plays card
updateUI();
arrangeHand();
arrangeBattlefield();
}
}
}
// Combat is now handled automatically at end of turn
// AI just plays cards, combat resolution happens automatically
// End AI turn with different delays based on difficulty
var turnDelay = 1500;
if (gameDifficulty === "easy") {
turnDelay = 2000; // Slower for easy difficulty
} else if (gameDifficulty === "hard") {
turnDelay = 1000; // Faster for hard difficulty
}
LK.setTimeout(function () {
if (!checkGameOver()) {
endTurn();
}
}, turnDelay);
}
// Card zoom preview variables
var zoomPreviewCard = null;
var zoomPreviewBg = null;
var zoomPreviewTimeout = null;
var isShowingZoom = false;
// Double-tap tracking variables
var lastTappedCard = null;
var lastTapTime = 0;
var doubleTapThreshold = 500; // milliseconds
// Button hover tracking
var mainMenuButtonHover = false;
// Card info display variables
var cardInfoDisplay = null;
var cardInfoBg = null;
var isShowingCardInfo = false;
function createZoomPreview(card) {
if (isShowingZoom) return;
// Create dark background overlay
zoomPreviewBg = game.addChild(LK.getAsset('battlefield', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
alpha: 0.8,
tint: 0x000000
}));
zoomPreviewBg.width = 2048;
zoomPreviewBg.height = 2732;
// Create zoomed card preview
zoomPreviewCard = new Card(card.cardData);
zoomPreviewCard.x = 1024;
zoomPreviewCard.y = 1000;
zoomPreviewCard.scaleX = 2.5;
zoomPreviewCard.scaleY = 2.5;
zoomPreviewCard.alpha = 0;
game.addChild(zoomPreviewCard);
// Add card name text
var nameText = new Text2(card.cardData.name, {
size: 60,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 1024;
nameText.y = 700;
nameText.alpha = 0;
game.addChild(nameText);
zoomPreviewCard.nameDisplay = nameText;
// Add passive description if card has passive
if (card.hasPassive()) {
var passiveText = new Text2("Passive: " + getPassiveDescription(card.cardData.passive), {
size: 36,
fill: 0xF39C12
});
passiveText.anchor.set(0.5, 0.5);
passiveText.x = 1024;
passiveText.y = 1400;
passiveText.alpha = 0;
game.addChild(passiveText);
zoomPreviewCard.passiveDisplay = passiveText;
}
// Add description text
var descText = new Text2(card.cardData.description, {
size: 32,
fill: 0xECF0F1
});
descText.anchor.set(0.5, 0.5);
descText.x = 1024;
descText.y = card.hasPassive() ? 1500 : 1400;
descText.alpha = 0;
game.addChild(descText);
zoomPreviewCard.descDisplay = descText;
// Animate zoom in
tween(zoomPreviewBg, {
alpha: 0.8
}, {
duration: 300,
easing: tween.easeOut
});
tween(zoomPreviewCard, {
alpha: 1.0,
scaleX: 2.5,
scaleY: 2.5
}, {
duration: 400,
easing: tween.easeOut
});
tween(nameText, {
alpha: 1.0
}, {
duration: 400,
easing: tween.easeOut
});
tween(descText, {
alpha: 1.0
}, {
duration: 400,
easing: tween.easeOut
});
if (zoomPreviewCard.passiveDisplay) {
tween(zoomPreviewCard.passiveDisplay, {
alpha: 1.0
}, {
duration: 400,
easing: tween.easeOut
});
}
isShowingZoom = true;
}
function destroyZoomPreview() {
if (!isShowingZoom) return;
if (zoomPreviewCard) {
if (zoomPreviewCard.nameDisplay) {
game.removeChild(zoomPreviewCard.nameDisplay);
}
if (zoomPreviewCard.passiveDisplay) {
game.removeChild(zoomPreviewCard.passiveDisplay);
}
if (zoomPreviewCard.descDisplay) {
game.removeChild(zoomPreviewCard.descDisplay);
}
game.removeChild(zoomPreviewCard);
zoomPreviewCard = null;
}
if (zoomPreviewBg) {
game.removeChild(zoomPreviewBg);
zoomPreviewBg = null;
}
isShowingZoom = false;
}
function getPassiveDescription(passiveType) {
return "No passive ability";
}
function createCardInfoDisplay(card) {
if (isShowingCardInfo) {
destroyCardInfoDisplay();
}
// Create semi-transparent background at bottom
cardInfoBg = game.addChild(LK.getAsset('playerArea', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 2200,
alpha: 0.8,
tint: 0x2c3e50
}));
// Create card name text
var nameText = new Text2(card.cardData.name, {
size: 48,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 1024;
nameText.y = 2320;
game.addChild(nameText);
// Create passive text if card has passive
var passiveText = null;
if (card.hasPassive()) {
var passiveDesc = getPassiveDescription(card.cardData.passive);
passiveText = new Text2("Pasif: " + passiveDesc, {
size: 36,
fill: 0xF39C12
});
passiveText.anchor.set(0.5, 0.5);
passiveText.x = 1024;
passiveText.y = 2380;
game.addChild(passiveText);
} else {
passiveText = new Text2("Pasif: Yok", {
size: 36,
fill: 0x95A5A6
});
passiveText.anchor.set(0.5, 0.5);
passiveText.x = 1024;
passiveText.y = 2380;
game.addChild(passiveText);
}
// Store references for cleanup
cardInfoDisplay = {
background: cardInfoBg,
nameText: nameText,
passiveText: passiveText
};
isShowingCardInfo = true;
// Auto-hide after 3 seconds
LK.setTimeout(function () {
destroyCardInfoDisplay();
}, 3000);
}
function destroyCardInfoDisplay() {
if (!isShowingCardInfo || !cardInfoDisplay) return;
if (cardInfoDisplay.background) {
game.removeChild(cardInfoDisplay.background);
}
if (cardInfoDisplay.nameText) {
game.removeChild(cardInfoDisplay.nameText);
}
if (cardInfoDisplay.passiveText) {
game.removeChild(cardInfoDisplay.passiveText);
}
cardInfoDisplay = null;
cardInfoBg = null;
isShowingCardInfo = false;
}
// Event handlers
game.down = function (x, y, obj) {
if (gamePhase !== "playing" || currentPlayer !== 0 || combatPhase) return;
// Close zoom preview if showing
if (isShowingZoom) {
destroyZoomPreview();
return;
}
// Close card info display if showing
if (isShowingCardInfo) {
destroyCardInfoDisplay();
}
// Check if end turn button was clicked
if (x >= 1700 && x <= 1900 && y >= 1910 && y <= 1990) {
endTurn();
return;
}
// Check if main menu button was clicked
if (x >= 1480 && x <= 1720 && y >= 110 && y <= 190) {
// Button press effect
tween(mainMenuBtn, {
scaleX: 0.55,
scaleY: 0.55,
tint: 0x2980b9
}, {
duration: 100,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(mainMenuBtn, {
scaleX: 0.6,
scaleY: 0.6,
tint: 0x3498db
}, {
duration: 100,
easing: tween.easeOut
});
}
});
// Return to main menu and reset game
LK.setTimeout(function () {
resetGame();
}, 200);
return;
}
// Check if a spell was clicked
for (var i = 0; i < humanPlayer.spells.length; i++) {
var spell = humanPlayer.spells[i];
var spellBounds = {
left: spell.x - 60,
right: spell.x + 60,
top: spell.y - 60,
bottom: spell.y + 60
};
if (x >= spellBounds.left && x <= spellBounds.right && y >= spellBounds.top && y <= spellBounds.bottom) {
if (spell.isUsable) {
// Start dragging spell instead of auto-casting
draggedCard = spell;
selectedCard = spell;
}
return;
}
}
// Check if a hand card was clicked
for (var i = 0; i < humanPlayer.hand.length; i++) {
var card = humanPlayer.hand[i];
var cardBounds = {
left: card.x - 90,
right: card.x + 90,
top: card.y - 125,
bottom: card.y + 125
};
if (x >= cardBounds.left && x <= cardBounds.right && y >= cardBounds.top && y <= cardBounds.bottom) {
// Check for double-tap
var currentTime = Date.now();
if (lastTappedCard === card && currentTime - lastTapTime < doubleTapThreshold) {
// Double-tap detected - show card info
createCardInfoDisplay(card);
lastTappedCard = null;
lastTapTime = 0;
return;
}
// Record this tap
lastTappedCard = card;
lastTapTime = currentTime;
if (card.isPlayable) {
selectedCard = card;
draggedCard = card;
// Start long press timer for zoom preview
if (zoomPreviewTimeout) {
LK.clearTimeout(zoomPreviewTimeout);
}
zoomPreviewTimeout = LK.setTimeout(function () {
if (selectedCard === card) {
createZoomPreview(card);
draggedCard = null; // Cancel drag when showing zoom
}
}, 800); // 800ms long press
}
return;
}
}
// Check battlefield cards for zoom preview
var allBattlefieldCards = humanPlayer.battlefield.concat(aiPlayer.battlefield);
for (var i = 0; i < allBattlefieldCards.length; i++) {
var card = allBattlefieldCards[i];
var cardBounds = {
left: card.x - 90,
right: card.x + 90,
top: card.y - 125,
bottom: card.y + 125
};
if (x >= cardBounds.left && x <= cardBounds.right && y >= cardBounds.top && y <= cardBounds.bottom) {
// Check for double-tap on battlefield cards
var currentTime = Date.now();
if (lastTappedCard === card && currentTime - lastTapTime < doubleTapThreshold) {
// Double-tap detected - show card info
createCardInfoDisplay(card);
lastTappedCard = null;
lastTapTime = 0;
return;
}
// Record this tap
lastTappedCard = card;
lastTapTime = currentTime;
// Start long press timer for battlefield card zoom
if (zoomPreviewTimeout) {
LK.clearTimeout(zoomPreviewTimeout);
}
zoomPreviewTimeout = LK.setTimeout(function () {
createZoomPreview(card);
}, 800);
return;
}
}
// Battlefield cards can no longer be manually selected for attacking
// Combat is now automatic at end of turn
};
game.move = function (x, y, obj) {
// Cancel zoom preview if user starts moving
if (zoomPreviewTimeout) {
LK.clearTimeout(zoomPreviewTimeout);
zoomPreviewTimeout = null;
}
// Check if mouse is over main menu button
var isOverMainMenuButton = x >= 1480 && x <= 1720 && y >= 110 && y <= 190;
if (isOverMainMenuButton && !mainMenuButtonHover) {
mainMenuButtonHover = true;
tween(mainMenuBtn, {
scaleX: 0.7,
scaleY: 0.7,
tint: 0x5dade2
}, {
duration: 200,
easing: tween.easeOut
});
} else if (!isOverMainMenuButton && mainMenuButtonHover) {
mainMenuButtonHover = false;
tween(mainMenuBtn, {
scaleX: 0.6,
scaleY: 0.6,
tint: 0x3498db
}, {
duration: 200,
easing: tween.easeOut
});
}
if (draggedCard) {
draggedCard.x = x;
draggedCard.y = y;
// If dragging a spell, highlight potential targets
if (draggedCard.castSpell) {
// Reset all card alphas first
for (var i = 0; i < aiPlayer.battlefield.length; i++) {
aiPlayer.battlefield[i].alpha = 1.0;
}
for (var i = 0; i < humanPlayer.battlefield.length; i++) {
humanPlayer.battlefield[i].alpha = 1.0;
}
// Determine which cards to highlight based on spell target type
var targetCards = [];
if (draggedCard.spellData.target === "enemy") {
targetCards = aiPlayer.battlefield;
} else if (draggedCard.spellData.target === "ally") {
targetCards = humanPlayer.battlefield;
}
// Highlight cards that are close to the spell
for (var i = 0; i < targetCards.length; i++) {
var card = targetCards[i];
var distance = Math.sqrt((card.x - x) * (card.x - x) + (card.y - y) * (card.y - y));
if (distance < 150) {
card.alpha = 0.7;
}
}
}
}
};
game.up = function (x, y, obj) {
// Clear zoom preview timeout
if (zoomPreviewTimeout) {
LK.clearTimeout(zoomPreviewTimeout);
zoomPreviewTimeout = null;
}
// Handle spell targeting
if (draggedCard && draggedCard.castSpell) {
var target = null;
var targetCards = [];
// Determine valid targets based on spell type
if (draggedCard.spellData.target === "enemy") {
targetCards = aiPlayer.battlefield;
} else if (draggedCard.spellData.target === "ally") {
targetCards = humanPlayer.battlefield;
}
// Check if spell was dropped on a valid target card
for (var i = 0; i < targetCards.length; i++) {
var card = targetCards[i];
var cardBounds = {
left: card.x - 90,
right: card.x + 90,
top: card.y - 125,
bottom: card.y + 125
};
if (x >= cardBounds.left && x <= cardBounds.right && y >= cardBounds.top && y <= cardBounds.bottom) {
target = card;
break;
}
}
// Cast spell if valid target found
if (target && target.isOnBattlefield && humanPlayer.castSpell(draggedCard, target)) {
updateUI();
arrangeHand();
}
// Reset all card alphas after spell targeting
for (var i = 0; i < aiPlayer.battlefield.length; i++) {
aiPlayer.battlefield[i].alpha = 1.0;
}
for (var i = 0; i < humanPlayer.battlefield.length; i++) {
humanPlayer.battlefield[i].alpha = 1.0;
}
// Return spell to original position
arrangeSpells();
draggedCard = null;
selectedCard = null;
return;
}
if (draggedCard && y < 1350 && y > 700) {
// Determine which lane the card was dropped in
var targetLane = -1;
if (x >= 380 && x < 812) targetLane = 0; // Left lane
else if (x >= 812 && x < 1236) targetLane = 1; // Center lane
else if (x >= 1236 && x < 1668) targetLane = 2; // Right lane
// Check if lane is available and card can be played
if (targetLane >= 0 && humanPlayer.battlefield.length < 3) {
// Check if target lane is already occupied
var laneOccupied = false;
for (var i = 0; i < humanPlayer.battlefield.length; i++) {
if (humanPlayer.battlefield[i].laneIndex === targetLane) {
laneOccupied = true;
break;
}
}
// Check if placing Lucifer would face another Lucifer in same lane
var luciferBlocked = false;
if (draggedCard.cardData.name === "Lucifer") {
// Check if AI has Lucifer in the target lane
for (var j = 0; j < aiPlayer.battlefield.length; j++) {
if (aiPlayer.battlefield[j].laneIndex === targetLane && aiPlayer.battlefield[j].cardData.name === "Lucifer") {
luciferBlocked = true;
break;
}
}
}
if (!laneOccupied && !luciferBlocked && humanPlayer.playCard(draggedCard)) {
draggedCard.laneIndex = targetLane;
draggedCard.targetLane = targetLane;
draggedCard = null;
selectedCard = null;
updateUI();
arrangeHand();
arrangeBattlefield();
}
}
}
selectedCard = null;
if (draggedCard) {
arrangeHand();
draggedCard = null;
}
// Dead cards are now automatically removed in takeDamage function
updateUI();
arrangeBattlefield();
checkGameOver();
};
// Mana regeneration timer
var manaRegenTimer = 0;
var manaRegenInterval = 500; // Regenerate mana every 500ms
game.update = function () {
if (gamePhase === "playing") {
// Handle mana regeneration during active turn
if (currentPlayer === 0 && !combatPhase) {
// Only for human player during their turn
manaRegenTimer += LK.deltaTime;
if (manaRegenTimer >= manaRegenInterval) {
if (humanPlayer.currentMana < humanPlayer.maxMana) {
humanPlayer.currentMana = Math.min(humanPlayer.maxMana, humanPlayer.currentMana + 2);
// Mana regeneration visual effect
var manaOrbEffect = game.addChild(LK.getAsset('manaOrb', {
anchorX: 0.5,
anchorY: 0.5,
x: 50,
y: 2000,
alpha: 0.8
}));
tween(manaOrbEffect, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
game.removeChild(manaOrbEffect);
}
});
}
manaRegenTimer = 0;
}
}
updateUI();
}
};
function initializeGameplay() {
// Initialize starting hands
for (var i = 0; i < 4; i++) {
humanPlayer.drawCard();
aiPlayer.drawCard();
}
// Play background music
LK.playMusic('Darksouls');
// Initial setup
updateUI();
arrangeHand();
arrangeBattlefield();
arrangeSpells();
}
function showMainMenu() {
if (mainMenu) {
game.removeChild(mainMenu);
}
mainMenu = new MainMenu();
game.addChild(mainMenu);
}
// Initialize with main menu
if (gameState === "menu") {
showMainMenu();
}
End turn button fark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Kart alanı dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Ateş ruhu karakteri dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Lightning spirit character Dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Kocaman kayadan oluşan golem kırmızı parıldayan gözlere sahip dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Air wisp character dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Arka planı doldur sadece aynısını yap
Koridor yukarıdan bakış dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Ateş 🔥 dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Ulan yatay tarsfa doğru geniş yap yüksekliğe doğru küçük yap
Shadow drake dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Michael demiurgos dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
İnfinity Minion character dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Fireball dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Magic stand dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Play button Dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Deck yaz ama düzgün bir arka planla
Büyü asası logosu Dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Suikastçı bıçağı logosu Dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Kalkan logosu Dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Warrior logo Dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Sadece freeze büyüsü yap insan olmasın Dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Sadece play yerine Wiki yaz
Spell icon Dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
İpuçları icon Dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Synergy icon Dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Game Rules dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Frost Wolf Man Dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Water spirit dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Phoenix woman Dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Void Stalker Dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows
Crystal guardian Dark souls style 2d pixel art. In-Game asset. 2d. High contrast. No shadows