User prompt
Unifica los assets biblioteca, biblioteca_1, biblioteca_2 y biblioteca_3 en una biblioteca a la que entrar desda la plaza al pulsar el asset biblioteca y para salir de este y volver a la plaza volver a tocar el asset biblioteca
User prompt
Haz que la puerta de la librería coincida con espacio de la puerta de la izquierda del asset plaza dentro del modo aventura
User prompt
Elimina el rectángulo azul que está encima del teclado qué creaste en el menú de selección de personaje
User prompt
Haz que el teclado qué creaste sea visible
User prompt
Crea un teclado funcional para escribir el nombre en el menú de selección de personaje
User prompt
VAMOS A VER PEDAZO DE SUBNORMAL DE AVA DE TU PUTA MADRE QUIERO QUE SI EN EL JODIDO ASSET TECLADO TOCO LA PUTA Q DE ESCRIBA UNA PUTA Q, SI ESCRIBO LA L LA L Y ASÍ CON CADA JODIDA LETRA Y NÚMERO NO ES TAN PUTAMENTE DIFÍCIL PEDAZO SUBNORMAL RETRASADA
User prompt
Haz que las letras del asset teclado coincidían con las del teclado QWERTY
User prompt
En la selección de personaje siitúa más abajo el asset teclado y agrándalo
User prompt
Quiero que en la selección de personaje el asset teclado sea funcional y se ponga debajo de los botones borrar y continuar, pero sin tocarlos
User prompt
Quiero que la plaza tenga mejor resolución, para ello haz que se oculten zonas en función de la visión general del personaje y que por ende, si el personaje avanza a un sitio se vea este y deje de verse el más lejano al personaje
User prompt
Haz que el shuriken de hielo use el asset shuriken de hielo y que troncazo use el asset troncazo
User prompt
Crea una nueva carta del elemento planta llamada Troncazo que quite 4 de vida y tenga un 10% de noquear al enemigo, es decir que el enemigo pierda su turno
User prompt
Haz que las enredaderas usen el asset enredaderas. Crea una carta del elemento hielo llamada Shuriken de hielo, la cuál al usarla lanzas un shuriken de hielo el cuál quite 2 de vida al rival y en tu próximo turno vaya al fondo de tu mazo de cartas quitando 2 puntos de vida al rival en el proceso, agrégale el maná que consideres, su tutorial y créale un asset
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of undefined (reading 'toLowerCase')' in or related to this line: 'var soundId = element.toLowerCase() + soundNumber;' Line Number: 978
User prompt
Por tutorial para carta es un combate de 4 o 5 turnos donde veas la interacciones de estas en un combate
User prompt
Quiero que dentro de los libros elementales, al ado de cada carta integres el botón ▶️ para ir a un tutorial donde te enseñen a usar dicha carta y sus efectos
User prompt
Crea la carta enredaderas, del elemento planta, está misma, quita 2 de vida, y tiene un 10% de envenenar al enemigo, lo cuál supone estar 3 o 4 números (este número sale al azar, es decir, no sigue un patrón) envenenado, es decir pierdes 1 vida por cada turno que estés envenenado
User prompt
Quita la arena de batalla de la plaza
User prompt
Ajusta el asset plaza_general para que se pueda pasear por la misma moviendo al personaje y que se vea todo bien, la idea es poder caminar por esta controlando al personaje que camine por esta, así que mete también físicas realistas como no poder atravesar columnas y cosas del estilo
User prompt
Mete en la plaza SOLO el asset plaza
User prompt
Elimina TODOS los elementos de la plaza
User prompt
¿Cómo se puede quitar el rectángulo negro que está expandiéndose y disminuyéndose en la plaza?
User prompt
Elimina el recuadro negro que se ve en la plaza
User prompt
Quiero que el edificio de la izquierda sea la entrada de la biblioteca, así que cambia la puerta por la puerta del asset biblioteca
User prompt
Elimina el color negro que hay en la plaza al momento de jugar
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highestLevel: 0, completedCourses: 0, currentCourse: 0, currentBattleInCourse: 0, playerMaxHealth: 10, hasPlayedAdventure: false, playerGender: "undefined", playerName: "undefined", selectedMagoAsset: "Mago", selectedMagaAsset: "Maga" }); /**** * Classes ****/ var Card = Container.expand(function (element, damage, name, effect) { var self = Container.call(this); self.element = element; self.damage = damage; self.name = name || element; self.effect = effect || 'normal'; self.isUsed = false; // Card border background (slightly larger than content) var cardBorder = self.attachAsset('cardBase', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.05, scaleY: 1.05, tint: 0x444444 }); // Card main background var cardBg = self.attachAsset('cardBase', { anchorX: 0.5, anchorY: 0.5 }); self.addChild(cardBg); // Use specific card name image if available, otherwise fallback to element graphic var cardImageId = getCardImageId(self.name); // TOP HALF: Card design image (scaled to fit top half of card) var cardDesign = self.attachAsset(cardImageId, { anchorX: 0.5, anchorY: 0.5, y: -105, // Position in top half scaleX: 0.93, scaleY: cardImageId === 'agua_oxigenada' ? 0.32 : 0.5 }); // Get element color var elementColor = getElementColor(element); // BOTTOM HALF: Element color background (covers bottom half of card) var elementColorBg = LK.getAsset('cardBase', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.93, scaleY: 0.5, tint: elementColor }); elementColorBg.y = 105; // Position in bottom half self.addChild(elementColorBg); // Get mana cost for this card self.manaCost = getCardManaCost(name, effect); // Create damage/healing text in the format "x/+y", "x", or "+y" var healAmount = getHealAmount(name, effect); var damageDisplayText = ''; // Determine display format based on damage and healing if (damage > 0 && healAmount > 0) { // Both damage and healing: "x/+y" damageDisplayText = damage.toString() + '/+' + healAmount.toString(); } else if (damage > 0 && healAmount === 0) { // Only damage: "x" damageDisplayText = damage.toString(); } else if (damage === 0 && healAmount > 0) { // Only healing: "+y" damageDisplayText = '+' + healAmount.toString(); } else { // Neither damage nor healing (shouldn't happen, but fallback) damageDisplayText = '0'; } // Damage/healing text on the element color background var damageText = new Text2(damageDisplayText, { size: 48, fill: 0xFFFFFF, font: "'Arial Black', Arial, sans-serif" }); damageText.anchor.set(0.5, 0.5); damageText.y = 85; // Position slightly higher to make room for mana cost // Add dark stroke for better contrast damageText.stroke = 0x000000; damageText.strokeThickness = 3; self.addChild(damageText); // Mana cost text on the element color background var manaText = new Text2('⚡' + self.manaCost.toString(), { size: 36, fill: 0xFFD700, font: "'Arial Black', Arial, sans-serif" }); manaText.anchor.set(0.5, 0.5); manaText.y = 125; // Position below damage text manaText.stroke = 0x000000; manaText.strokeThickness = 3; self.addChild(manaText); self.down = function (x, y, obj) { // Handle card selection phase if (cardSelectionPhase && !playerSelectedCard) { if (playerMana >= self.manaCost) { // Select this card for the turn playerSelectedCard = self; // Highlight selected card tween(self, { tint: 0x00FF00 }, { duration: 300 }); // Check if both players have selected cards checkCardSelections(); } else { // Flash card red to indicate insufficient mana tween(self, { tint: 0xff0000 }, { duration: 200, onFinish: function onFinish() { tween(self, { tint: 0xffffff }, { duration: 200 }); } }); } return; } if (!self.isUsed && gameState === 'playerTurn') { if (playerMana >= self.manaCost) { playCard(self); } else { // Flash card red to indicate insufficient mana tween(self, { tint: 0xff0000 }, { duration: 200, onFinish: function onFinish() { tween(self, { tint: 0xffffff }, { duration: 200 }); } }); } } }; return self; }); var Enemy = Container.expand(function (name, health, difficulty, battleIndex) { var self = Container.call(this); self.name = name; self.maxHealth = health; self.health = health; self.difficulty = difficulty; self.battleIndex = battleIndex; // Store battle index for wizard assignment self.isFrozen = false; self.hasExtraAttack = false; self.iceBlocked = false; self.fogFailureChance = 0; // 0 = no fog, 10 = 10% failure chance self.priorityBlocked = false; // Flag to indicate if enemy's priority is blocked self.mana = 10; self.maxMana = 10; self.deck = createEnemyDeck(difficulty); self.hand = []; self.dialogBox = null; // Dialogue box container self.dialogText = null; // Dialogue text element // Use wizard sprite based on battle index for adventure mode var wizardSpriteId = 'academy'; // Default fallback if (gameMode === 'adventure' && typeof battleIndex !== 'undefined') { // Sequential mapping: battleIndex 0-17 maps to wizard1-wizard18 // Course 1 (battles 0-2): wizard1, wizard2, wizard3 // Course 2 (battles 3-5): wizard4, wizard5, wizard6 // Course 3 (battles 6-8): wizard7, wizard8, wizard9 // Course 4 (battles 9-11): wizard10, wizard11, wizard12 // Course 5 (battles 12-14): wizard13, wizard14, wizard15 // Course 6 (battles 15-17): wizard16, wizard17, wizard18 var wizardNumber = battleIndex + 1; // Direct 1:1 mapping wizardNumber = Math.max(1, Math.min(wizardNumber, 18)); // Ensure range 1-18 wizardSpriteId = 'wizard' + wizardNumber; } var enemyGraphic = self.attachAsset(wizardSpriteId, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6 }); var nameText = new Text2(name, { size: 50, fill: 0xFFFFFF }); nameText.anchor.set(0.5, 0.5); nameText.y = -200; self.addChild(nameText); var healthText = new Text2('HP: ' + health, { size: 40, fill: 0xFFFFFF }); healthText.anchor.set(0.5, 0.5); healthText.y = 200; self.addChild(healthText); self.healthText = healthText; self.drawHand = function () { // Clear existing mazo de selección visuals if (self.handCards) { for (var j = 0; j < self.handCards.length; j++) { if (self.handCards[j].parent) { self.handCards[j].parent.removeChild(self.handCards[j]); } } } self.handCards = []; self.hand = []; // Enemy's mazo de selección (3 cards to choose from) var handCardNames = []; // Track card names to prevent duplicates in mazo de selección for (var i = 0; i < 3; i++) { if (self.deck.length > 0) { var attempts = 0; var foundValidCard = false; while (attempts < 50 && !foundValidCard) { // Limit attempts to prevent infinite loop var randomIndex = Math.floor(Math.random() * self.deck.length); var cardData = self.deck[randomIndex]; // Check if this card name is already in current mazo de selección var isDuplicate = false; for (var j = 0; j < handCardNames.length; j++) { if (handCardNames[j] === (cardData.name || cardData.element)) { isDuplicate = true; break; } } // Use card if it's not a duplicate in current mazo de selección if (!isDuplicate) { self.hand.push(cardData); // Add to enemy's mazo de selección // Create visual card for enemy with same design as player cards var enemyCard = new Card(cardData.element, cardData.damage, cardData.name || cardData.element, cardData.effect || 'normal'); enemyCard.x = -400 + i * 200; enemyCard.y = -400; enemyCard.scaleX = 0.6; enemyCard.scaleY = 0.6; self.addChild(enemyCard); self.handCards.push(enemyCard); handCardNames.push(cardData.name || cardData.element); foundValidCard = true; } attempts++; } } } }; self.takeDamage = function (damage, element) { // Check for enemy magma shields if (self.magmaShields && self.magmaShields > 0 && damage > 0) { if (element !== 'Water') { // Normal attacks are reduced by shield count var damageReduction = self.magmaShields; var reducedDamage = Math.max(0, damage - damageReduction); self.magmaShields -= 1; // Always lose 1 shield per attack damage = reducedDamage; } else { // Water attacks consume 2 shields and are not reduced var shieldsToRemove = Math.min(2, self.magmaShields); self.magmaShields -= shieldsToRemove; // Water damage is NOT reduced } } self.health -= damage; if (self.health < 0) self.health = 0; self.healthText.setText('HP: ' + self.health); tween(self, { tint: 0xff0000 }, { duration: 200, onFinish: function onFinish() { tween(self, { tint: 0xffffff }, { duration: 200 }); } }); }; self.removeCardFromHand = function (cardIndex) { if (cardIndex >= 0 && cardIndex < self.hand.length) { // Remove from hand array self.hand.splice(cardIndex, 1); // Remove visual card if (self.handCards && self.handCards[cardIndex]) { self.removeChild(self.handCards[cardIndex]); self.handCards.splice(cardIndex, 1); } } }; self.showDialogue = function (message, duration) { // Remove existing dialogue if any if (self.dialogBox) { self.removeChild(self.dialogBox); self.dialogBox = null; self.dialogText = null; } // Create dialogue box container self.dialogBox = new Container(); // Add cuadro_dialogo as background var dialogBg = self.dialogBox.attachAsset('Cuadro_dialogo', { anchorX: 0.5, anchorY: 0.5, scaleX: 6.0, scaleY: 4.0 }); // Create dialogue text self.dialogText = new Text2(message, { size: 35, fill: 0x000000, font: "'Arial Black', Arial, sans-serif" }); self.dialogText.anchor.set(0.5, 0.5); self.dialogText.x = 0; self.dialogText.y = 0; self.dialogBox.addChild(self.dialogText); // Position dialogue box above enemy self.dialogBox.x = 0; self.dialogBox.y = -300; self.addChild(self.dialogBox); // Auto-remove dialogue after duration (default 3 seconds) var dialogDuration = duration || 3000; LK.setTimeout(function () { if (self.dialogBox) { self.removeChild(self.dialogBox); self.dialogBox = null; self.dialogText = null; } }, dialogDuration); }; self.selectCard = function () { // Enemy selects a card for the turn if (self.hand.length > 0) { // Check if enemy can afford any card var affordableCards = []; for (var i = 0; i < self.hand.length; i++) { var cardCost = getCardManaCost(self.hand[i].name || self.hand[i].element, self.hand[i].effect || 'normal'); if (self.mana >= cardCost) { affordableCards.push({ card: self.hand[i], index: i, cost: cardCost }); } } // If no affordable cards, pass turn if (affordableCards.length === 0) { return null; } var selectedCard = affordableCards[Math.floor(Math.random() * affordableCards.length)]; return selectedCard; } return null; }; self.playTurn = function () { // Show dialogue when enemy plays turn var dialogues = ["¡Es mi turno!", "¡Prepárate!", "¡Vamos a ver qué puedes hacer!", "¡Mi magia es superior!", "¡No podrás conmigo!", "¡Atacaré ahora!", "¡Tu derrota está cerca!", "¡Usaré mi mejor hechizo!"]; var randomDialogue = dialogues[Math.floor(Math.random() * dialogues.length)]; self.showDialogue(randomDialogue, 2000); if (self.isFrozen) { self.isFrozen = false; endEnemyTurn(); return; } // Check if priority is blocked by Esquirla Helada if (self.priorityBlocked) { // Priority blocked - enemy loses turn self.priorityBlocked = false; // Reset flag endEnemyTurn(); return; } // Check for fog failure if (self.fogFailureChance > 0 && Math.random() < self.fogFailureChance / 100) { // Enemy fails turn due to fog endEnemyTurn(); return; } if (self.hand.length > 0) { // Check if enemy can afford any card var affordableCards = []; for (var i = 0; i < self.hand.length; i++) { var cardCost = getCardManaCost(self.hand[i].name || self.hand[i].element, self.hand[i].effect || 'normal'); if (self.mana >= cardCost) { affordableCards.push({ card: self.hand[i], index: i, cost: cardCost }); } } // If no affordable cards, pass turn to restore mana if (affordableCards.length === 0) { self.mana = self.maxMana; endEnemyTurn(); return; } var selectedCard = affordableCards[Math.floor(Math.random() * affordableCards.length)]; var cardIndex = selectedCard.index; var card = selectedCard.card; // If ice is blocked, find a non-ice card if (self.iceBlocked && card.element === 'Ice') { var nonIceCards = []; for (var i = 0; i < self.hand.length; i++) { if (self.hand[i].element !== 'Ice') { nonIceCards.push({ card: self.hand[i], index: i }); } } if (nonIceCards.length > 0) { var randomNonIce = nonIceCards[Math.floor(Math.random() * nonIceCards.length)]; card = randomNonIce.card; cardIndex = randomNonIce.index; } } // Reset ice block after turn self.iceBlocked = false; enemyPlayCard(card, cardIndex); // Check for extra attack from hombre de nieve effect if (self.hasExtraAttack && self.hand.length > 0) { self.hasExtraAttack = false; // Play another card with damage 1 LK.setTimeout(function () { if (self.hand.length > 0) { var extraCardIndex = Math.floor(Math.random() * self.hand.length); var extraCard = { element: 'Ice', damage: 1 }; enemyPlayCard(extraCard, extraCardIndex); } }, 500); } } else { endEnemyTurn(); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xffffff }); /**** * Game Code ****/ // Game state variables // Fire card images // Water card images // Air card images // Plant card images // Rock card images // Ice card images var playerUsedCards = []; // Track cards used in current turn var enemyUsedCards = []; // Track enemy cards used in current turn var playerUnavailableCards = []; // Track cards that can't be drawn yet (with turn counters) var playerShield = null; // Player's shield (Muralla Rocosa) var playerShieldBar = null; // Shield health bar var playerShieldText = null; // Shield health text var playerMagmaShields = 0; // Player's magma shields count (0-3) // Fire card images // Water card images // Air card images // Plant card images // Rock card images // Ice card images // Adventure mode wizard sprites with different colored hats // Green hat // Lime green hat // Lawn green hat // Green yellow hat // Yellow hat // Gold hat // Orange hat // Dark orange hat // Coral hat // Tomato hat // Orange red hat // Red hat // Crimson hat // Fire brick hat // Dark red hat // Maroon hat // Golden hat (penultimate) // Astral purple hat (final) function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } var gameState = 'menu'; // 'menu', 'modeSelect', 'battle', 'victory', 'defeat', 'tutorial' var gameMode = 'adventure'; // 'adventure', 'roguelike' var roguelikeLevel = storage.highestLevel || 0; var currentCourse = storage.currentCourse || 0; var currentBattleInCourse = storage.currentBattleInCourse || 0; var completedCourses = storage.completedCourses || 0; var isFirstTimeAdventure = !storage.hasPlayedAdventure || !storage.playerName || storage.playerName === "undefined"; // Track if this is first time playing adventure mode or no name set var tutorialStep = 0; // Track current tutorial step var tutorialWizard = null; // Tutorial wizard character var tutorialEnemy = null; // Special tutorial enemy var tutorialTextBox = null; // Tutorial text display var tutorialContainer = null; // Container for tutorial UI var tutorialHighlight = null; // Highlight element for tutorial var isTutorialActive = false; // Track if tutorial is currently running // Card selection phase variables var playerSelectedCard = null; // Card selected by player for the turn var enemySelectedCard = null; // Card selected by enemy for the turn var cardSelectionPhase = false; // Track if we're in card selection phase var selectionInstructions = null; // Text instructions for card selection // Helper function to map card names to image IDs function getCardImageId(cardName) { switch (cardName) { // Fire cards case 'Llamarada': return 'llamarada'; case 'Fénix': return 'fenix_invocation'; case 'Brasas': return 'brasas'; case 'Llama': return 'llama'; case 'Mechero': return 'mechero'; case 'Escudos de Magma': return 'llamarada'; // Use fire graphics for magma shields // Water cards case 'Gota de agua': return 'gota_de_agua'; case 'Luna roja': return 'luna_roja'; case 'Agua oxigenada': return 'agua_oxigenada'; case 'Marea': return 'marea'; case 'Pirata': return 'pirata_invocation'; // Air cards case 'Vendaval': return 'vendaval'; case 'Brisa': return 'brisa'; case 'Huracán': return 'huracan'; case 'Niebla': return 'niebla'; case 'Tormenta': return 'tormenta'; case 'Oxígeno': return 'Oxigeno'; // Plant cards case 'Arboleda': return 'arboleda'; case 'Serafín': return 'serafin_invocation'; case 'Rama': return 'rama'; case 'Vida extra': return 'vida_extra'; case 'Aullidos del bosque': return 'aullidos_del_bosque'; // Rock cards case 'Pedrada': return 'pedrada'; case 'Tormenta de arena': return 'tormenta_de_arena'; case 'Sarcófago': return 'sarcofago_invocation'; case 'Muralla Rocosa': return 'muralla_rocosa'; case 'Terremoto': return 'terremoto'; // Ice cards case 'Nevada': return 'nevada'; case 'Ventisca': return 'ventisca'; case 'Bola de nieve': return 'bola_de_nieve'; case 'Hombre de nieve': return 'hombre_de_nieve_invocation'; case 'Esquirla Helada': return 'esquirla_helada'; default: return getElementCardId(cardName); } } // Helper function to get fallback element card ID function getElementCardId(element) { switch (element) { case 'Fire': return 'fireCard'; case 'Water': return 'waterCard'; case 'Air': return 'airCard'; case 'Plant': return 'plantCard'; case 'Rock': return 'rockCard'; case 'Ice': return 'iceCard'; default: return 'cardBase'; } } // Helper function to get element colors function getElementColor(element) { switch (element) { case 'Fire': return 0xff4500; case 'Water': return 0x4169e1; case 'Air': return 0x87ceeb; case 'Plant': return 0x228b22; case 'Rock': return 0x696969; case 'Ice': return 0xb0e0e6; default: return 0xf5f5dc; } } // Helper function to get healing amount for cards function getHealAmount(cardName, effect) { switch (cardName) { case 'Fénix': return 2; case 'Agua oxigenada': return 3; case 'Serafín': return 2; case 'Sarcófago': return 2; case 'Vida extra': return 1; // Max health increase case 'Oxígeno': return 2; default: return 0; } } // Helper function to get mana cost for cards based on their versatility function getCardManaCost(cardName, effect) { switch (cardName) { // High cost cards (8-10 mana) - very versatile/powerful case 'Muralla Rocosa': // Shield with 4 health return 7; case 'Terremoto': // Replaces all cards for both players return 10; case 'Huracán': // Replaces enemy cards return 8; case 'Niebla': // Damage + persistent failure chance return 4; case 'Tormenta': // Damage + chance to skip enemy turn return 6; case 'Vida extra': // Increases max health permanently return 9; case 'Aullidos del bosque': // Damage + skip enemy turn return 8; case 'Ventisca': // Damage + freeze chance return 7; // Medium-high cost cards (6-7 mana) - good versatility case 'Fénix': // Damage + heal return 7; case 'Llamarada': // High damage + burn card return 6; case 'Pirata': // Damage + steal card return 6; case 'Sarcófago': // Damage + heal return 6; case 'Hombre de nieve': // Damage + extra attack chance return 6; case 'Esquirla Helada': // Priority damage (1 damage with priority) return 3; // Medium cost cards (4-5 mana) - moderate versatility case 'Mechero': // Damage + prevent ice return 4; case 'Escudos de Magma': // Creates 3 magma shields that reduce damage return 6; case 'Nevada': // Damage + freeze return 5; case 'Bola de nieve': // Damage + freeze return 4; case 'Agua oxigenada': // Pure healing return 5; case 'Serafín': // Pure healing return 4; case 'Oxígeno': // Air healing card return 3; // Low-medium cost cards (2-3 mana) - basic with small effects case 'Brasas': // Damage + burn return 3; case 'Llama': // Damage + burn return 3; case 'Luna roja': // Pure damage (2) return 3; case 'Vendaval': // Pure damage (2) return 3; case 'Arboleda': // Pure damage (2) return 3; case 'Pedrada': // Pure damage (2) return 3; case 'Tormenta de arena': // Pure damage (2) return 3; case 'Marea': // Pure damage (2) return 3; // Low cost cards (1-2 mana) - basic effects case 'Gota de agua': // Pure damage (1) return 1; case 'Brisa': // Pure damage (1) return 2; case 'Rama': // Pure damage (1) return 1; // Default costs for generic elements default: if (effect === 'heal' || effect === 'damageAndHeal') { return 5; } else if (effect === 'replaceCards' || effect === 'replaceAllCards') { return 8; } else if (effect === 'skipEnemyTurn' || effect === 'steal') { return 6; } else if (effect === 'chooseBurn' || effect === 'preventIce') { return 4; } else { // Basic damage cards return 2; } } } // Helper function to play element-specific sounds function playElementSound(element) { var soundNumber = Math.floor(Math.random() * 5) + 1; var soundId = element.toLowerCase() + soundNumber; LK.getSound(soundId).play(); } var currentBattle = 0; var playerHealth = 10; var playerMaxHealth = 10; var playerMana = 10; var playerMaxMana = 10; var playerDeck = []; var playerHand = []; var currentEnemy = null; var enemies = []; var turnCounter = 0; var playerStartsFirst = true; // Track who starts the current turn // UI elements var menuContainer = new Container(); var battleContainer = new Container(); var playerHealthBar = null; var playerHealthText = null; var playerManaBar = null; var playerManaText = null; var passButton = null; var turnText = null; var battleText = null; // Initialize player deck function initializeDeck() { var elements = ['Fire', 'Water', 'Air', 'Plant', 'Rock', 'Ice']; playerDeck = []; for (var i = 0; i < elements.length; i++) { if (elements[i] === 'Fire') { // Add specific fire cards with names and effects var fireCards = [{ element: 'Fire', damage: 3, name: 'Llamarada', effect: 'chooseBurn' }, { element: 'Fire', damage: 2, name: 'Fénix', effect: 'heal' }, { element: 'Fire', damage: 2, name: 'Brasas', effect: 'normal' }, { element: 'Fire', damage: 2, name: 'Llama', effect: 'normal' }, { element: 'Fire', damage: 1, name: 'Mechero', effect: 'preventIce' }, { element: 'Fire', damage: 0, name: 'Escudos de Magma', effect: 'magmaShields' }]; for (var k = 0; k < fireCards.length; k++) { playerDeck.push(fireCards[k]); } } else if (elements[i] === 'Air') { // Add specific air cards with names and effects var airCards = [{ element: 'Air', damage: 2, name: 'Vendaval', effect: 'normal' }, { element: 'Air', damage: 1, name: 'Brisa', effect: 'normal' }, { element: 'Air', damage: 2, name: 'Huracán', effect: 'replaceCards' }, { element: 'Air', damage: 3, name: 'Tormenta', effect: 'stormSkip' }, { element: 'Air', damage: 1, name: 'Niebla', effect: 'fogFailure' }, { element: 'Air', damage: 0, name: 'Oxígeno', effect: 'heal' }]; for (var k = 0; k < airCards.length; k++) { playerDeck.push(airCards[k]); } // Air deck now has 5 cards total (including Niebla) } else if (elements[i] === 'Water') { // Add specific water cards with names and effects var waterCards = [{ element: 'Water', damage: 1, name: 'Gota de agua', effect: 'normal' }, { element: 'Water', damage: 2, name: 'Luna roja', effect: 'normal' }, { element: 'Water', damage: 0, name: 'Agua oxigenada', effect: 'heal' }, { element: 'Water', damage: 2, name: 'Marea', effect: 'normal' }, { element: 'Water', damage: 1, name: 'Pirata', effect: 'steal' }]; for (var k = 0; k < waterCards.length; k++) { playerDeck.push(waterCards[k]); } } else if (elements[i] === 'Rock') { // Add specific rock/earth cards with names and effects var rockCards = [{ element: 'Rock', damage: 2, name: 'Pedrada', effect: 'normal' }, { element: 'Rock', damage: 2, name: 'Tormenta de arena', effect: 'normal' }, { element: 'Rock', damage: 2, name: 'Sarcófago', effect: 'damageAndHeal' }, { element: 'Rock', damage: 0, name: 'Muralla Rocosa', effect: 'shield' }, { element: 'Rock', damage: 2, name: 'Terremoto', effect: 'replaceAllCards' }]; for (var k = 0; k < rockCards.length; k++) { playerDeck.push(rockCards[k]); } } else if (elements[i] === 'Plant') { // Add specific plant cards with names and effects var plantCards = [{ element: 'Plant', damage: 2, name: 'Arboleda', effect: 'normal' }, { element: 'Plant', damage: 0, name: 'Serafín', effect: 'heal' }, { element: 'Plant', damage: 1, name: 'Rama', effect: 'normal' }, { element: 'Plant', damage: 0, name: 'Vida extra', effect: 'increaseMaxHealth' }, { element: 'Plant', damage: 2, name: 'Aullidos del bosque', effect: 'skipEnemyTurn' }]; for (var k = 0; k < plantCards.length; k++) { playerDeck.push(plantCards[k]); } } else if (elements[i] === 'Ice') { // Add specific ice cards with names and effects var iceCards = [{ element: 'Ice', damage: 2, name: 'Nevada', effect: 'normal' }, { element: 'Ice', damage: 2, name: 'Ventisca', effect: 'blizzard' }, { element: 'Ice', damage: 1, name: 'Bola de nieve', effect: 'normal' }, { element: 'Ice', damage: 2, name: 'Hombre de nieve', effect: 'snowmanAttack' }, { element: 'Ice', damage: 1, name: 'Esquirla Helada', effect: 'priority' }]; for (var k = 0; k < iceCards.length; k++) { playerDeck.push(iceCards[k]); } } else { for (var j = 0; j < 5; j++) { var damage = 1; playerDeck.push({ element: elements[i], damage: damage }); } } } } function createEnemyDeck(difficulty) { var deck = []; var elements = ['Fire', 'Water', 'Air', 'Plant', 'Rock', 'Ice']; var cardCount = 15 + difficulty * 5; for (var i = 0; i < cardCount; i++) { var element = elements[Math.floor(Math.random() * elements.length)]; var damage = 1 + Math.floor(Math.random() * (difficulty + 1)); deck.push({ element: element, damage: damage }); } return deck; } function initializeEnemies() { if (gameMode === 'adventure') { // Adventure mode: 6 courses with 3 battles each enemies = []; var courseNames = ['Apprentice', 'Novice', 'Scholar', 'Expert', 'Master', 'Grandmaster']; var battleTitles = ['Guard', 'Defender', 'Champion']; var battleIndex = 0; for (var course = 0; course < 6; course++) { for (var battle = 0; battle < 3; battle++) { var health = 5 + course * 3 + battle * 2; // Increasing health per course and battle var name = courseNames[course] + ' ' + battleTitles[battle]; enemies.push(new Enemy(name, health, course, battleIndex)); battleIndex++; } } } else { // Roguelike mode (original) enemies = [new Enemy('Tutorial Apprentice', 5, 0), new Enemy('Novice Mage', 8, 1), new Enemy('Advanced Scholar', 12, 2), new Enemy('Master Wizard', 15, 3)]; } } function createMenu() { menuContainer.removeChildren(); var bg = LK.getAsset('background', { anchorX: 0, anchorY: 0 }); menuContainer.addChild(bg); // Magical school background var magicSchool = LK.getAsset('magicSchool', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 900 }); menuContainer.addChild(magicSchool); // Add magical towers and buildings var leftTower = LK.getAsset('academy', { anchorX: 0.5, anchorY: 1, x: 400, y: 1200, scaleX: 0.8, scaleY: 1.2 }); menuContainer.addChild(leftTower); var rightTower = LK.getAsset('academy', { anchorX: 0.5, anchorY: 1, x: 1648, y: 1200, scaleX: 0.8, scaleY: 1.2 }); menuContainer.addChild(rightTower); var mainBuilding = LK.getAsset('academy', { anchorX: 0.5, anchorY: 1, x: 1024, y: 1300, scaleX: 1.5, scaleY: 1.0 }); menuContainer.addChild(mainBuilding); var titleText = new Text2('MAGICAL ACADEMY', { size: 120, fill: 0xFFD700, font: "'Arial Black', Arial, sans-serif", stroke: 0x4B0082, strokeThickness: 25 }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 200; menuContainer.addChild(titleText); // Add magical glow effect with pulsing 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() { // Create infinite loop by calling the animation again 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 }); } }); } }); } }); // Add magical color shifting effect var _colorCycle = function colorCycle() { tween(titleText, { tint: 0xFF69B4 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { tween(titleText, { tint: 0x00FFFF }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { tween(titleText, { tint: 0xFFD700 }, { duration: 1500, easing: tween.easeInOut, onFinish: _colorCycle }); } }); } }); }; _colorCycle(); var adventureButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1450 }); menuContainer.addChild(adventureButton); var adventureText = new Text2('Adventure Mode', { size: 50, fill: 0xFFFFFF }); adventureText.anchor.set(0.5, 0.5); adventureText.x = 1024; adventureText.y = 1450; menuContainer.addChild(adventureText); adventureButton.down = function () { LK.playMusic('Academia', { fade: { start: 1, end: 0, duration: 500 } }); LK.setTimeout(function () { LK.playMusic('Academia3', { fade: { start: 0, end: 1, duration: 500 } }); }, 500); gameMode = 'adventure'; // Check if this is first time playing adventure mode or no player data if (isFirstTimeAdventure) { // Show player setup interface for first-time players showPlayerSetup(); } else { // Start in the plaza with selected character startPlaza(); } }; var roguelikeButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1650 }); menuContainer.addChild(roguelikeButton); var roguelikeText = new Text2('Roguelike Mode', { size: 50, fill: 0xFFFFFF }); roguelikeText.anchor.set(0.5, 0.5); roguelikeText.x = 1024; roguelikeText.y = 1650; menuContainer.addChild(roguelikeText); roguelikeButton.down = function () { LK.playMusic('Academia', { fade: { start: 1, end: 0, duration: 500 } }); LK.setTimeout(function () { LK.playMusic('Roguelite', { fade: { start: 0, end: 1, duration: 500 } }); }, 500); gameMode = 'roguelike'; startBattle(); }; if (roguelikeLevel > 0) { var recordText = new Text2('Roguelike Record: Level ' + roguelikeLevel, { size: 40, fill: 0xFFD700 }); recordText.anchor.set(0.5, 0.5); recordText.x = 1024; recordText.y = 1850; menuContainer.addChild(recordText); } if (completedCourses > 0) { var adventureText = new Text2('Adventure Progress: ' + completedCourses + '/6 Courses Completed', { size: 40, fill: 0x00FF00 }); adventureText.anchor.set(0.5, 0.5); adventureText.x = 1024; adventureText.y = 1900; menuContainer.addChild(adventureText); } // Fire Book Button var fireBookButton = LK.getAsset('fireCard', { anchorX: 0.5, anchorY: 0.5, x: 200, y: 2100 }); menuContainer.addChild(fireBookButton); var fireBookText = new Text2('Fire\nBook', { size: 40, fill: 0x000000, font: "'Arial Black', Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3 }); fireBookText.anchor.set(0.5, 0.5); fireBookText.x = 200; fireBookText.y = 2100; menuContainer.addChild(fireBookText); fireBookButton.down = function () { LK.playMusic('Academia', { fade: { start: 1, end: 0, duration: 500 } }); showElementBook('Fire'); }; // Water Book Button var waterBookButton = LK.getAsset('waterCard', { anchorX: 0.5, anchorY: 0.5, x: 400, y: 2100 }); menuContainer.addChild(waterBookButton); var waterBookText = new Text2('Water\nBook', { size: 40, fill: 0x000000, font: "'Arial Black', Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3 }); waterBookText.anchor.set(0.5, 0.5); waterBookText.x = 400; waterBookText.y = 2100; menuContainer.addChild(waterBookText); waterBookButton.down = function () { LK.playMusic('Academia', { fade: { start: 1, end: 0, duration: 500 } }); showElementBook('Water'); }; // Air Book Button var airBookButton = LK.getAsset('airCard', { anchorX: 0.5, anchorY: 0.5, x: 600, y: 2100 }); menuContainer.addChild(airBookButton); var airBookText = new Text2('Air\nBook', { size: 40, fill: 0x000000, font: "'Arial Black', Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3 }); airBookText.anchor.set(0.5, 0.5); airBookText.x = 600; airBookText.y = 2100; menuContainer.addChild(airBookText); airBookButton.down = function () { LK.playMusic('Academia', { fade: { start: 1, end: 0, duration: 500 } }); showElementBook('Air'); }; // Plant Book Button var plantBookButton = LK.getAsset('plantCard', { anchorX: 0.5, anchorY: 0.5, x: 800, y: 2100 }); menuContainer.addChild(plantBookButton); var plantBookText = new Text2('Plant\nBook', { size: 40, fill: 0x000000, font: "'Arial Black', Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3 }); plantBookText.anchor.set(0.5, 0.5); plantBookText.x = 800; plantBookText.y = 2100; menuContainer.addChild(plantBookText); plantBookButton.down = function () { LK.playMusic('Academia', { fade: { start: 1, end: 0, duration: 500 } }); showElementBook('Plant'); }; // Rock Book Button var rockBookButton = LK.getAsset('rockCard', { anchorX: 0.5, anchorY: 0.5, x: 1000, y: 2100 }); menuContainer.addChild(rockBookButton); var rockBookText = new Text2('Rock\nBook', { size: 40, fill: 0x000000, font: "'Arial Black', Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3 }); rockBookText.anchor.set(0.5, 0.5); rockBookText.x = 1000; rockBookText.y = 2100; menuContainer.addChild(rockBookText); rockBookButton.down = function () { LK.playMusic('Academia', { fade: { start: 1, end: 0, duration: 500 } }); showElementBook('Rock'); }; // Ice Book Button var iceBookButton = LK.getAsset('iceCard', { anchorX: 0.5, anchorY: 0.5, x: 1200, y: 2100 }); menuContainer.addChild(iceBookButton); var iceBookText = new Text2('Ice\nBook', { size: 40, fill: 0x000000, font: "'Arial Black', Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3 }); iceBookText.anchor.set(0.5, 0.5); iceBookText.x = 1200; iceBookText.y = 2100; menuContainer.addChild(iceBookText); iceBookButton.down = function () { LK.playMusic('Academia', { fade: { start: 1, end: 0, duration: 500 } }); showElementBook('Ice'); }; // Invocation Cards Book Button var invocationBookButton = LK.getAsset('fenix_invocation_card', { anchorX: 0.5, anchorY: 0.5, x: 1400, y: 2100 }); menuContainer.addChild(invocationBookButton); var invocationBookText = new Text2('Invocation\nBook', { size: 35, fill: 0x000000, font: "'Arial Black', Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3 }); invocationBookText.anchor.set(0.5, 0.5); invocationBookText.x = 1400; invocationBookText.y = 2100; menuContainer.addChild(invocationBookText); invocationBookButton.down = function () { LK.playMusic('Academia', { fade: { start: 1, end: 0, duration: 500 } }); showElementBook('Invocation'); }; // Reset Character Selection Button (positioned in bottom right corner) var resetCharacterMenuButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1700, y: 2300, scaleX: 0.8, scaleY: 0.6 }); menuContainer.addChild(resetCharacterMenuButton); var resetCharacterMenuText = new Text2('RESET\nCHARACTER', { size: 30, fill: 0xFFFFFF }); resetCharacterMenuText.anchor.set(0.5, 0.5); resetCharacterMenuText.x = 1700; resetCharacterMenuText.y = 2300; menuContainer.addChild(resetCharacterMenuText); resetCharacterMenuButton.down = function () { // Reset all character selection data storage.playerGender = "undefined"; storage.playerName = "undefined"; storage.selectedMagoAsset = "Mago"; storage.selectedMagaAsset = "Maga"; storage.hasPlayedAdventure = false; storage.completedCourses = 0; storage.currentCourse = 0; storage.currentBattleInCourse = 0; storage.playerMaxHealth = 10; // Reset local variables completedCourses = 0; currentBattle = 0; isFirstTimeAdventure = true; // Show player setup interface showPlayerSetup(); }; game.addChild(menuContainer); } function createBattle() { battleContainer.removeChildren(); // Background var bg; if (gameMode === 'roguelike') { bg = LK.getAsset('Mazmorra', { anchorX: 0, anchorY: 0, scaleX: 1, scaleY: 1 }); } else if (gameMode === 'adventure' && currentBattle >= 0 && currentBattle <= 2) { // Use Clases1 for first three adventure levels bg = LK.getAsset('Clases1', { anchorX: 0, anchorY: 0, scaleX: 1, scaleY: 1 }); } else if (gameMode === 'adventure' && currentBattle >= 3 && currentBattle <= 5) { // Use Clases2 for course 2 (battles 3-5) with native resolution bg = LK.getAsset('Clases2', { anchorX: 0, anchorY: 0, scaleX: 1, scaleY: 1 }); } else { bg = LK.getAsset('background', { anchorX: 0, anchorY: 0 }); } battleContainer.addChild(bg); // Player health playerHealthBar = LK.getAsset('healthBar', { anchorX: 0, anchorY: 0, x: 100, y: 100 }); battleContainer.addChild(playerHealthBar); playerHealthText = new Text2('HP: ' + playerHealth + '/' + playerMaxHealth, { size: 30, fill: 0xFFFFFF }); playerHealthText.anchor.set(0, 0.5); playerHealthText.x = 420; playerHealthText.y = 120; battleContainer.addChild(playerHealthText); // Player mana bar playerManaBar = LK.getAsset('healthBar', { anchorX: 0, anchorY: 0, x: 100, y: 200, tint: 0x0066FF }); battleContainer.addChild(playerManaBar); playerManaText = new Text2('⚡: ' + playerMana + '/' + playerMaxMana, { size: 30, fill: 0xFFFFFF }); playerManaText.anchor.set(0, 0.5); playerManaText.x = 420; playerManaText.y = 220; battleContainer.addChild(playerManaText); // Player shield bar (initially hidden) playerShieldBar = LK.getAsset('healthBar', { anchorX: 0, anchorY: 0, x: 100, y: 300, tint: 0x696969, alpha: 0 }); battleContainer.addChild(playerShieldBar); playerShieldText = new Text2('🛡: 0/0', { size: 30, fill: 0xFFFFFF }); playerShieldText.anchor.set(0, 0.5); playerShieldText.x = 420; playerShieldText.y = 320; playerShieldText.alpha = 0; battleContainer.addChild(playerShieldText); // Pass turn button passButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1750, y: 350, scaleX: 0.6, scaleY: 0.6 }); battleContainer.addChild(passButton); var passText = new Text2('Pass\n(+10 ⚡)', { size: 25, fill: 0xFFFFFF }); passText.anchor.set(0.5, 0.5); passText.x = 1750; passText.y = 350; battleContainer.addChild(passText); passButton.down = function () { if (gameState === 'playerTurn') { // Restore full mana playerMana = playerMaxMana; updatePlayerMana(); // Handle tutorial progression for pass turn if (isTutorialActive && tutorialStep === 8) { tutorialStep = 9; showTutorialMessage("¡Perfecto! Tu maná se restauró completamente. Ahora el final...", function () { showTutorialMessage("Una última cosa: no siempre vas a atacar primero, así que ten cuidado.", function () { endTutorial(); }); }); return; } endPlayerTurn(); } }; // Exit button to return to menu var exitButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1750, y: 150, scaleX: 0.6, scaleY: 0.6 }); battleContainer.addChild(exitButton); var exitText = new Text2('Exit', { size: 30, fill: 0xFFFFFF }); exitText.anchor.set(0.5, 0.5); exitText.x = 1750; exitText.y = 150; battleContainer.addChild(exitText); exitButton.down = function () { gameState = 'menu'; game.removeChild(battleContainer); createMenu(); LK.playMusic('Academia', { fade: { start: 0, end: 1, duration: 500 } }); }; // Reset button for adventure mode if (gameMode === 'adventure') { var resetButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1750, y: 250, scaleX: 0.6, scaleY: 0.6 }); battleContainer.addChild(resetButton); var resetText = new Text2('Reset', { size: 25, fill: 0xFFFFFF }); resetText.anchor.set(0.5, 0.5); resetText.x = 1750; resetText.y = 250; battleContainer.addChild(resetText); resetButton.down = function () { // Reset adventure progress completedCourses = 0; currentBattle = 0; storage.completedCourses = 0; storage.currentCourse = 0; storage.currentBattleInCourse = 0; // Reset player health playerHealth = 10; playerMaxHealth = 10; // Restart the first battle gameState = 'menu'; game.removeChild(battleContainer); createMenu(); LK.playMusic('Academia', { fade: { start: 0, end: 1, duration: 500 } }); }; } // Course and battle indicator for adventure mode if (gameMode === 'adventure') { var courseNumber = Math.floor(currentBattle / 3) + 1; var battleInCourse = currentBattle % 3 + 1; var courseText = new Text2('Course ' + courseNumber + ' - Battle ' + battleInCourse, { size: 35, fill: 0xFFD700 }); courseText.anchor.set(0.5, 0.5); courseText.x = 1024; courseText.y = 150; battleContainer.addChild(courseText); } // Turn indicator turnText = new Text2('Your Turn - Turn 1', { size: 40, fill: 0xFFFFFF }); turnText.anchor.set(0.5, 0.5); turnText.x = 1024; turnText.y = 200; battleContainer.addChild(turnText); // Card selection instructions (initially hidden) selectionInstructions = new Text2('', { size: 35, fill: 0xFFD700, stroke: 0x000000, strokeThickness: 2 }); selectionInstructions.anchor.set(0.5, 0.5); selectionInstructions.x = 1024; selectionInstructions.y = 250; selectionInstructions.alpha = 0; battleContainer.addChild(selectionInstructions); // Battle text (removed course/battle display for adventure mode) if (gameMode === 'roguelike') { battleText = new Text2('Battle ' + (currentBattle + 1), { size: 60, fill: 0xFFFFFF }); battleText.anchor.set(0.5, 0.5); battleText.x = 1024; battleText.y = 300; battleContainer.addChild(battleText); } // Player character display (bottom of screen) - show only selected backsprite var playerCharacterAsset = 'wizard1'; // Default fallback if (storage.playerGender === 'mago') { // Use the selected mago asset directly playerCharacterAsset = storage.selectedMagoAsset || 'Mago'; } else if (storage.playerGender === 'maga') { // Use the selected maga asset directly playerCharacterAsset = storage.selectedMagaAsset || 'Maga'; } var playerCharacter = LK.getAsset(playerCharacterAsset, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); playerCharacter.x = 1024; playerCharacter.y = 2200; // Position at bottom of screen battleContainer.addChild(playerCharacter); // Enemy currentEnemy = enemies[currentBattle]; currentEnemy.x = 1024; currentEnemy.y = 800; battleContainer.addChild(currentEnemy); // Add deck pile (face down) var deckPile = LK.getAsset('deckPile', { anchorX: 0.5, anchorY: 0.5, x: 1750, y: 1800 }); battleContainer.addChild(deckPile); var deckText = new Text2('Deck', { size: 30, fill: 0xFFFFFF }); deckText.anchor.set(0.5, 0.5); deckText.x = 1750; deckText.y = 1950; battleContainer.addChild(deckText); game.addChild(battleContainer); updatePlayerMana(); drawPlayerHand(); currentEnemy.drawHand(); } function drawPlayerHand() { // Remove existing selection deck (mazo de selección) for (var i = battleContainer.children.length - 1; i >= 0; i--) { var child = battleContainer.children[i]; if (child instanceof Card) { battleContainer.removeChild(child); } } playerHand = []; // playerHand represents the current mazo de selección (3 cards to choose from) var handCardNames = []; // Track card names to prevent duplicates in selection deck for (var i = 0; i < 3; i++) { if (playerDeck.length > 0) { var attempts = 0; var foundValidCard = false; while (attempts < 50 && !foundValidCard) { // Limit attempts to prevent infinite loop var randomIndex = Math.floor(Math.random() * playerDeck.length); var cardData = playerDeck[randomIndex]; // Check if this card name is already in current mazo de selección var isDuplicate = false; for (var j = 0; j < handCardNames.length; j++) { if (handCardNames[j] === cardData.name) { isDuplicate = true; break; } } // Use card if it's not a duplicate in current mazo de selección if (!isDuplicate) { var card = new Card(cardData.element, cardData.damage, cardData.name, cardData.effect); card.x = 400 + i * 350; card.y = 1800; playerHand.push(card); // Add to current mazo de selección battleContainer.addChild(card); handCardNames.push(cardData.name); foundValidCard = true; } attempts++; } } } } function playCard(card) { if (gameState !== 'playerTurn') return; if (playerMana < card.manaCost) return; card.isUsed = true; // Consume mana playerMana -= card.manaCost; updatePlayerMana(); LK.getSound('cardPlay').play(); playElementSound(card.element); // Apply card effects var damage = card.damage; var element = card.element; // Check for priority effect if (card.effect === 'priority') { // Priority card - block enemy's next turn priority currentEnemy.priorityBlocked = true; } // Fire element invocation animation if (element === 'Fire') { // Create the invoked asset that will fly to the enemy var invokedAsset = LK.getAsset(getCardImageId(card.name), { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8, alpha: 0.9 }); // Start at card position invokedAsset.x = card.x; invokedAsset.y = card.y; battleContainer.addChild(invokedAsset); // Animate the asset flying to the enemy tween(invokedAsset, { x: currentEnemy.x, y: currentEnemy.y, scaleX: 1.2, scaleY: 1.2, rotation: Math.PI * 2 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { // Impact effect - flash the asset and make it bigger momentarily tween(invokedAsset, { scaleX: 2.0, scaleY: 2.0, alpha: 0.3, tint: 0xFFFFFF }, { duration: 200, onFinish: function onFinish() { // Remove the invoked asset after impact battleContainer.removeChild(invokedAsset); // Apply damage and effects after animation applyFireCardEffects(card, damage); } }); } }); } else { // Non-fire cards apply effects immediately applyCardEffects(card, damage, element); } } // Helper function to apply fire card effects after animation function applyFireCardEffects(card, damage) { switch (card.effect) { case 'chooseBurn': // Llamarada - deal 3 damage and let player choose card to burn if (currentEnemy.hand.length > 0) { var burnIndex = Math.floor(Math.random() * currentEnemy.hand.length); currentEnemy.removeCardFromHand(burnIndex); } break; case 'heal': // Fénix - deal 2 damage and heal 2 HP playerHealth = Math.min(playerHealth + 2, playerMaxHealth); updatePlayerHealth(); break; case 'preventIce': // Mechero - deal 1 damage and prevent enemy from using ice cards next turn currentEnemy.iceBlocked = true; break; case 'magmaShields': // Escudos de Magma - create 3 magma shields playerMagmaShields = 3; break; default: // Brasas and Llama - deal 2 damage normally if (currentEnemy.hand.length > 0) { var burnIndex = Math.floor(Math.random() * currentEnemy.hand.length); currentEnemy.removeCardFromHand(burnIndex); } break; } // Apply damage and continue with battle logic currentEnemy.takeDamage(damage, 'Fire'); LK.getSound('damage').play(); finishCardPlay(card); } // Helper function to apply non-fire card effects immediately function applyCardEffects(card, damage, element) { // Check if this is an entity card that needs invocation animation var entityCards = ['Fénix', 'Serafín', 'Aullidos del bosque', 'Pirata', 'Sarcófago', 'Hombre de nieve', 'Bola de nieve', 'Pedrada', 'Momia']; if (entityCards.indexOf(card.name) !== -1) { invokeEntityCard(card, damage, element); return; } switch (element) { case 'Water': // Handle special water card effects if (card.effect === 'heal') { // Agua oxigenada - heal 3 HP (no damage) playerHealth = Math.min(playerHealth + 3, playerMaxHealth); updatePlayerHealth(); damage = 0; // No damage dealt } else if (card.effect === 'steal') { // Pirata - let player choose which card to steal if (currentEnemy.hand.length > 0 && playerHand.length < 3) { showCardStealInterface(); return; // Don't continue with normal card play flow yet } } break; case 'Plant': // Handle special plant card effects if (card.effect === 'increaseMaxHealth') { // Vida extra - increase max health by 1 playerMaxHealth += 1; updatePlayerHealth(); damage = 0; // No damage dealt } // Standard damage for arboleda and rama break; case 'Air': // Handle special air card effects if (card.effect === 'replaceCards') { // Huracán - deal 2 damage and replace enemy's 3 cards with random ones from their deck currentEnemy.drawHand(); } else if (card.effect === 'stormSkip') { // Tormenta - deal 3 damage and 10% chance enemy can't attack next turn if (Math.random() < 0.1) { currentEnemy.isFrozen = true; } } else if (card.effect === 'fogFailure') { // Niebla - deal 1 damage and set persistent 10% failure chance if (currentEnemy.fogFailureChance === 0) { currentEnemy.fogFailureChance = 10; // Set 10% failure chance for rest of battle } } else { // Vendaval and Brisa - standard damage without replacing cards } break; case 'Rock': // Handle special rock card effects if (card.effect === 'shield') { // Muralla Rocosa - create shield with 4 health if (!playerShield) { playerShield = { health: 4, maxHealth: 4 }; updateShieldDisplay(); damage = 0; // No damage dealt } } else if (card.effect === 'replaceAllCards') { // Terremoto - deal 2 damage to enemy and both players get new cards currentEnemy.drawHand(); drawPlayerHand(); } // Standard damage for all rock cards break; case 'Ice': // Handle special ice card effects if (card.effect === 'blizzard') { // Ventisca - deal 2 damage and 10% chance enemy can't attack next turn if (Math.random() < 0.1) { currentEnemy.isFrozen = true; } } else { // Nevada and Bola de nieve - freeze enemy for next turn + damage currentEnemy.isFrozen = true; } break; } // Apply damage and finish card play currentEnemy.takeDamage(damage, element); LK.getSound('damage').play(); finishCardPlay(card); } // Helper function to invoke entity cards with special animations function invokeEntityCard(card, damage, element) { var centerX = 1024; var centerY = 1366; switch (card.name) { case 'Fénix': // Phoenix rises from ashes in center and attacks with fire var ashes = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.2, tint: 0x404040, alpha: 0.8 }); ashes.x = centerX; ashes.y = centerY; battleContainer.addChild(ashes); var phoenix = LK.getAsset('Fenix', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1, scaleY: 0.1, alpha: 0 }); phoenix.x = centerX; phoenix.y = centerY; battleContainer.addChild(phoenix); // Phoenix emerges from ashes tween(phoenix, { scaleX: 1.2, scaleY: 1.2, alpha: 1 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { // Phoenix breathes fire at enemy tween(phoenix, { rotation: -0.3, scaleX: 1.4, scaleY: 1.4 }, { duration: 300, onFinish: function onFinish() { // Fire effect towards enemy var fireBreath = LK.getAsset('llamarada', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, alpha: 0.9 }); fireBreath.x = centerX; fireBreath.y = centerY; battleContainer.addChild(fireBreath); tween(fireBreath, { x: currentEnemy.x, y: currentEnemy.y, scaleX: 1.5, scaleY: 1.5, rotation: Math.PI }, { duration: 500, onFinish: function onFinish() { // White healing aura for player var healingAura = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0, tint: 0xFFFFFF, alpha: 0.3 }); healingAura.x = 1024; healingAura.y = 1800; battleContainer.addChild(healingAura); tween(healingAura, { scaleX: 2.0, scaleY: 2.0, alpha: 0 }, { duration: 800, onFinish: function onFinish() { battleContainer.removeChild(healingAura); } }); // Apply effects currentEnemy.takeDamage(damage, 'Fire'); playerHealth = Math.min(playerHealth + 2, playerMaxHealth); updatePlayerHealth(); LK.getSound('damage').play(); // Clean up battleContainer.removeChild(fireBreath); battleContainer.removeChild(phoenix); battleContainer.removeChild(ashes); finishCardPlay(card); } }); } }); } }); break; case 'Serafín': // Seraph appears facing player and heals with green aura var seraph = LK.getAsset('serafin', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1, scaleY: 0.1, alpha: 0 }); seraph.x = centerX; seraph.y = centerY; battleContainer.addChild(seraph); tween(seraph, { scaleX: 1.3, scaleY: 1.3, alpha: 1 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { // Green healing aura towards player var greenAura = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0, tint: 0x00FF00, alpha: 0.6 }); greenAura.x = 1024; greenAura.y = 1800; battleContainer.addChild(greenAura); tween(greenAura, { scaleX: 2.5, scaleY: 2.5, alpha: 0 }, { duration: 1000, onFinish: function onFinish() { battleContainer.removeChild(greenAura); } }); // Apply healing playerHealth = Math.min(playerHealth + 2, playerMaxHealth); updatePlayerHealth(); LK.getSound('Curar').play(); // Clean up LK.setTimeout(function () { battleContainer.removeChild(seraph); finishCardPlay(card); }, 800); } }); break; case 'Aullidos del bosque': // Environment becomes forest with wolf howls and two wolves attack var forestBg = LK.getAsset('arboleda', { anchorX: 0.5, anchorY: 0.5, scaleX: 7.0, scaleY: 4.0, alpha: 0 }); forestBg.x = centerX; forestBg.y = centerY; battleContainer.addChild(forestBg); tween(forestBg, { alpha: 0.7 }, { duration: 500, onFinish: function onFinish() { // Two wolves appear and attack enemy (using Lobos asset without background) var wolf1 = LK.getAsset('Lobos', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8, alpha: 0 }); wolf1.x = centerX - 200; wolf1.y = centerY + 100; battleContainer.addChild(wolf1); var wolf2 = LK.getAsset('Lobos', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8, alpha: 0 }); wolf2.x = centerX + 200; wolf2.y = centerY + 100; battleContainer.addChild(wolf2); // Wolves appear and leap at enemy tween(wolf1, { alpha: 1 }, { duration: 300 }); tween(wolf2, { alpha: 1 }, { duration: 300 }); LK.setTimeout(function () { tween(wolf1, { x: currentEnemy.x - 100, y: currentEnemy.y, scaleX: 1.2, scaleY: 1.2 }, { duration: 600 }); tween(wolf2, { x: currentEnemy.x + 100, y: currentEnemy.y, scaleX: -1.2, scaleY: 1.2 }, { duration: 600, onFinish: function onFinish() { // Apply effects currentEnemy.takeDamage(damage); currentEnemy.isFrozen = true; LK.getSound('Aullidos').play(); LK.getSound('Aullidos').play(null, 1.5); // Clean up LK.setTimeout(function () { tween(forestBg, { alpha: 0 }, { duration: 500 }); battleContainer.removeChild(wolf1); battleContainer.removeChild(wolf2); LK.setTimeout(function () { battleContainer.removeChild(forestBg); finishCardPlay(card); }, 500); }, 800); } }); }, 500); } }); break; case 'Pedrada': // Rock throw appears in center and flies to enemy (reusing snowball animation) var rock = LK.getAsset('pedrada', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); rock.x = centerX; rock.y = centerY; battleContainer.addChild(rock); tween(rock, { x: currentEnemy.x, y: currentEnemy.y, scaleX: 1.2, scaleY: 1.2, rotation: Math.PI * 3 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { // Apply effects currentEnemy.takeDamage(damage, 'Rock'); LK.getSound('damage').play(); // Impact effect tween(rock, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 200, onFinish: function onFinish() { battleContainer.removeChild(rock); finishCardPlay(card); } }); } }); break; case 'Pirata': // Pirate appears and strikes enemy with hook, then offers card steal choice var pirate = LK.getAsset('Pirata', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1, scaleY: 0.1, alpha: 0 }); pirate.x = centerX; pirate.y = centerY; battleContainer.addChild(pirate); tween(pirate, { scaleX: 1.4, scaleY: 1.4, alpha: 1 }, { duration: 500, easing: tween.bounceOut, onFinish: function onFinish() { // Pirate swings hook at enemy tween(pirate, { x: currentEnemy.x - 150, y: currentEnemy.y, rotation: -0.5 }, { duration: 400, onFinish: function onFinish() { // Apply effects currentEnemy.takeDamage(damage); LK.getSound('damage').play(); // Card steal effect - show interface for player to choose if (currentEnemy.hand.length > 0 && playerHand.length < 3) { LK.setTimeout(function () { showCardStealInterface(); }, 800); // Delay to let pirate animation finish } // Clean up LK.setTimeout(function () { battleContainer.removeChild(pirate); finishCardPlay(card); }, 600); } }); } }); break; case 'Sarcófago': // Create sand pile (Arena asset) in center of battle var sandPile = LK.getAsset('Arena', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2, alpha: 0.8 }); sandPile.x = centerX; sandPile.y = centerY + 150; battleContainer.addChild(sandPile); // Sarcophagus starts buried under sand var sarcophagus = LK.getAsset('sarcofago', { anchorX: 0.5, anchorY: 1.0, scaleX: 0.1, scaleY: 0.1, alpha: 0 }); sarcophagus.x = centerX; sarcophagus.y = centerY + 150; battleContainer.addChild(sarcophagus); // Sarcophagus emerges from sand pile tween(sarcophagus, { scaleX: 1.2, scaleY: 1.2, alpha: 1, y: centerY - 50 }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { // Sarcophagus opens with dramatic effect tween(sarcophagus, { scaleX: 1.8, scaleY: 1.1 }, { duration: 500, easing: tween.bounceOut, onFinish: function onFinish() { // Mummy emerges from the sarcophagus and appears on top of Arena var mummy = LK.getAsset('Momia', { anchorX: 0.5, anchorY: 1.0, scaleX: 0.1, scaleY: 0.1, alpha: 0 }); mummy.x = centerX; mummy.y = centerY + 100; // Position on top of Arena asset battleContainer.addChild(mummy); // Mummy appears with growing animation tween(mummy, { scaleX: 1.5, scaleY: 1.5, alpha: 1 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { // Mummy attacks enemy with right hand (slight rotation and movement) tween(mummy, { rotation: -0.3, x: centerX + 50 }, { duration: 300, onFinish: function onFinish() { // Apply damage from mummy's right hand attack currentEnemy.takeDamage(damage); LK.getSound('damage').play(); // Mummy extends left hand with healing bandages toward player tween(mummy, { rotation: 0.3, x: centerX - 50 }, { duration: 400, onFinish: function onFinish() { // Healing bandages fly from mummy's left hand to player var healingBandages = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 1.5, tint: 0xF5DEB3, alpha: 0.9 }); healingBandages.x = centerX - 50; healingBandages.y = centerY - 50; battleContainer.addChild(healingBandages); // Animate bandages wrapping around player tween(healingBandages, { x: 1024, y: 1800, scaleX: 1.5, scaleY: 2.5, rotation: Math.PI * 2 }, { duration: 800, easing: tween.easeOut, onFinish: function onFinish() { // Create green healing aura around player var greenAura = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.0, scaleY: 1.0, tint: 0x00FF00, alpha: 0.7 }); greenAura.x = 1024; greenAura.y = 1800; battleContainer.addChild(greenAura); // Green aura pulsing effect tween(greenAura, { scaleX: 3.0, scaleY: 3.0, alpha: 0 }, { duration: 1200, easing: tween.easeOut, onFinish: function onFinish() { battleContainer.removeChild(greenAura); } }); // Apply healing playerHealth = Math.min(playerHealth + 2, playerMaxHealth); updatePlayerHealth(); battleContainer.removeChild(healingBandages); } }); // Clean up after complete animation LK.setTimeout(function () { battleContainer.removeChild(mummy); battleContainer.removeChild(sarcophagus); battleContainer.removeChild(sandPile); finishCardPlay(card); }, 1500); } }); } }); } }); } }); } }); break; case 'Momia': // Mummy appears on top of Arena asset in battle center var arenaBase = LK.getAsset('Arena', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2, alpha: 0.8 }); arenaBase.x = centerX; arenaBase.y = centerY; battleContainer.addChild(arenaBase); var mummy = LK.getAsset('Momia', { anchorX: 0.5, anchorY: 1.0, scaleX: 0.1, scaleY: 0.1, alpha: 0 }); mummy.x = centerX; mummy.y = centerY; battleContainer.addChild(mummy); // Mummy appears with growing animation on Arena tween(mummy, { scaleX: 1.5, scaleY: 1.5, alpha: 1 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { // Apply effects currentEnemy.takeDamage(damage); LK.getSound('damage').play(); // Clean up LK.setTimeout(function () { battleContainer.removeChild(mummy); battleContainer.removeChild(arenaBase); finishCardPlay(card); }, 800); } }); break; case 'Hombre de nieve': // Snowman appears and throws snowball at enemy var snowman = LK.getAsset('hombre_de_nieve', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.1, scaleY: 0.1, alpha: 0 }); snowman.x = centerX; snowman.y = centerY; battleContainer.addChild(snowman); tween(snowman, { scaleX: 1.3, scaleY: 1.3, alpha: 1 }, { duration: 500, easing: tween.bounceOut, onFinish: function onFinish() { // Snowman throws snowball var snowball = LK.getAsset('bola_de_nieve', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6 }); snowball.x = centerX; snowball.y = centerY; battleContainer.addChild(snowball); tween(snowball, { x: currentEnemy.x, y: currentEnemy.y, scaleX: 1.0, scaleY: 1.0, rotation: Math.PI * 3 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { // Apply effects currentEnemy.takeDamage(damage); currentEnemy.hasExtraAttack = true; LK.getSound('Bola_de_nieve').play(); // Impact effect tween(snowball, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 200, onFinish: function onFinish() { battleContainer.removeChild(snowball); } }); // Clean up LK.setTimeout(function () { battleContainer.removeChild(snowman); finishCardPlay(card); }, 800); } }); } }); break; case 'Bola de nieve': // Snowball appears in center and flies to enemy (reusing snowman animation) var snowball = LK.getAsset('bola_de_nieve', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); snowball.x = centerX; snowball.y = centerY; battleContainer.addChild(snowball); tween(snowball, { x: currentEnemy.x, y: currentEnemy.y, scaleX: 1.2, scaleY: 1.2, rotation: Math.PI * 3 }, { duration: 600, easing: tween.easeOut, onFinish: function onFinish() { // Apply effects currentEnemy.takeDamage(damage); currentEnemy.isFrozen = true; LK.getSound('Bola_de_nieve').play(); // Impact effect tween(snowball, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 200, onFinish: function onFinish() { battleContainer.removeChild(snowball); finishCardPlay(card); } }); } }); break; } } // Helper function to show card steal interface when Pirata is used function showCardStealInterface() { if (currentEnemy.hand.length === 0) return; // Create steal interface container var stealContainer = new Container(); // Semi-transparent background var stealBg = LK.getAsset('background', { anchorX: 0, anchorY: 0, alpha: 0.8 }); stealContainer.addChild(stealBg); // Title text var stealTitleText = new Text2('Choose a card to steal:', { size: 60, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 3 }); stealTitleText.anchor.set(0.5, 0.5); stealTitleText.x = 1024; stealTitleText.y = 600; stealContainer.addChild(stealTitleText); // Display enemy cards in center for selection var stealCards = []; for (var i = 0; i < currentEnemy.hand.length; i++) { var enemyCardData = currentEnemy.hand[i]; var stealCard = new Card(enemyCardData.element, enemyCardData.damage, enemyCardData.name || enemyCardData.element, enemyCardData.effect || 'normal'); stealCard.x = 1024 + (i - 1) * 350; // Center cards around screen center stealCard.y = 1200; stealCard.cardIndex = i; // Store original index for removal // Override down handler for stealing stealCard.down = function (x, y, obj) { var selectedIndex = obj.cardIndex; var stolenCardData = currentEnemy.hand[selectedIndex]; // Remove card from enemy hand currentEnemy.removeCardFromHand(selectedIndex); // Add stolen card to player hand var stolenCard = new Card(stolenCardData.element, stolenCardData.damage, stolenCardData.name || stolenCardData.element, stolenCardData.effect || 'normal'); stolenCard.x = 400 + playerHand.length * 350; stolenCard.y = 1800; playerHand.push(stolenCard); battleContainer.addChild(stolenCard); // Remove steal interface game.removeChild(stealContainer); // Continue with normal card play flow currentEnemy.takeDamage(1); // Pirata deals 1 damage LK.getSound('damage').play(); // Check if enemy is defeated if (currentEnemy.health === 0) { LK.getSound('victory').play(); currentBattle++; // Handle victory logic... if (gameMode === 'roguelike') { playerHealth = Math.min(playerHealth + 1, 10); if (currentBattle > roguelikeLevel) { roguelikeLevel = currentBattle; storage.highestLevel = roguelikeLevel; } } else { var courseNumber = Math.floor((currentBattle - 1) / 3); var battleInCourse = (currentBattle - 1) % 3 + 1; playerHealth = playerMaxHealth; if (battleInCourse === 3) { completedCourses = courseNumber + 1; storage.completedCourses = completedCourses; showCourseCompletionAnimation(courseNumber + 1); } } updatePlayerHealth(); if (currentBattle >= enemies.length) { LK.showYouWin(); } else { LK.setTimeout(function () { createBattle(); gameState = 'playerTurn'; turnText.setText('Your Turn - Turn 1'); }, 2000); } return; } // Continue to next turn endPlayerTurn(); }; stealContainer.addChild(stealCard); stealCards.push(stealCard); } game.addChild(stealContainer); } // Helper function for enemy pirata card steal interface function showEnemyCardStealInterface() { if (playerHand.length === 0) return; // Create steal interface container var enemyStealContainer = new Container(); // Semi-transparent background var enemyStealBg = LK.getAsset('background', { anchorX: 0, anchorY: 0, alpha: 0.8 }); enemyStealContainer.addChild(enemyStealBg); // Title text var enemyStealTitleText = new Text2('Enemy is choosing a card to steal...', { size: 60, fill: 0xFF0000, stroke: 0x000000, strokeThickness: 3 }); enemyStealTitleText.anchor.set(0.5, 0.5); enemyStealTitleText.x = 1024; enemyStealTitleText.y = 600; enemyStealContainer.addChild(enemyStealTitleText); // Display player cards in center (non-interactive) var displayCards = []; for (var i = 0; i < playerHand.length; i++) { var playerCardData = playerHand[i]; var displayCard = new Card(playerCardData.element, playerCardData.damage, playerCardData.name, playerCardData.effect); displayCard.x = 1024 + (i - 1) * 350; displayCard.y = 1200; displayCard.isUsed = true; // Make non-interactive enemyStealContainer.addChild(displayCard); displayCards.push(displayCard); } game.addChild(enemyStealContainer); // After 2 seconds, enemy makes selection and removes interface LK.setTimeout(function () { // Enemy steals a random card var stealIndex = Math.floor(Math.random() * playerHand.length); var stolenCard = playerHand[stealIndex]; var stolenCardData = { element: stolenCard.element, damage: stolenCard.damage, name: stolenCard.name, effect: stolenCard.effect }; // Remove from player hand playerHand.splice(stealIndex, 1); battleContainer.removeChild(stolenCard); // Add to enemy hand currentEnemy.hand.push(stolenCardData); var enemyCard = new Card(stolenCardData.element, stolenCardData.damage, stolenCardData.name, stolenCardData.effect); enemyCard.x = -400 + currentEnemy.hand.length * 200 - 200; enemyCard.y = -400; enemyCard.scaleX = 0.6; enemyCard.scaleY = 0.6; currentEnemy.addChild(enemyCard); if (!currentEnemy.handCards) currentEnemy.handCards = []; currentEnemy.handCards.push(enemyCard); // Remove steal interface game.removeChild(enemyStealContainer); }, 2000); } // Helper function to finish card play logic function finishCardPlay(card) { // Remove card from hand var cardIndex = playerHand.indexOf(card); if (cardIndex !== -1) { playerHand.splice(cardIndex, 1); } battleContainer.removeChild(card); // Add used card to tracking array with turn counter playerUsedCards.push({ element: card.element, damage: card.damage, name: card.name, effect: card.effect }); // Handle tutorial progression if (isTutorialActive && tutorialStep === 3) { // After first card play, enemy attacks tutorialStep = 4; showTutorialMessage("¡La carta atacó! Ahora el mago rival atacará.", function () { gameState = 'enemyTurn'; turnText.setText('Tutorial - Turno del Enemigo'); LK.setTimeout(function () { // Enemy plays a card that costs mana to demonstrate mana restoration tutorialEnemy.mana = 0; // Set enemy mana to 0 currentEnemy.playTurn(); }, 1000); }); return; } // Check if enemy is defeated (must reach exactly 0 health) if (currentEnemy.health === 0) { LK.getSound('victory').play(); currentBattle++; // Handle victory based on game mode if (gameMode === 'roguelike') { // Add 1 health for winning a battle playerHealth = Math.min(playerHealth + 1, 10); // Update roguelike progress if (currentBattle > roguelikeLevel) { roguelikeLevel = currentBattle; storage.highestLevel = roguelikeLevel; } } else { // Adventure mode - handle course progression var courseNumber = Math.floor((currentBattle - 1) / 3); var battleInCourse = (currentBattle - 1) % 3 + 1; // Always restore full health after winning any battle in adventure mode playerHealth = playerMaxHealth; // If this was the third battle of a course, mark course as completed but don't change max health if (battleInCourse === 3) { completedCourses = courseNumber + 1; storage.completedCourses = completedCourses; // Course completion animation showCourseCompletionAnimation(courseNumber + 1); } } // Restore full mana after battle victory playerMana = playerMaxMana; updatePlayerMana(); // Update player health display after victory updatePlayerHealth(); if (currentBattle >= enemies.length) { // All battles won LK.showYouWin(); } else { // Next battle LK.setTimeout(function () { createBattle(); gameState = 'playerTurn'; turnText.setText('Your Turn - Turn 1'); }, 2000); } return; } endPlayerTurn(); } function endPlayerTurn() { // Clear player used cards for this turn playerUsedCards = []; // Check if this completes a turn cycle if (playerStartsFirst) { // Player started first, now enemy gets their turn gameState = 'enemyTurn'; turnText.setText('Enemy Turn - Turn ' + turnCounter); LK.setTimeout(function () { currentEnemy.playTurn(); }, 1000); } else { // Enemy started first and player just finished, complete turn ends here startNewCompleteTurn(); } } function enemyPlayCard(cardData, cardIndex) { // Consume enemy mana var manaCost = getCardManaCost(cardData.name || cardData.element, cardData.effect || 'normal'); currentEnemy.mana -= manaCost; playElementSound(cardData.element); var damage = cardData.damage; var element = cardData.element; // Track used card before removing enemyUsedCards.push({ element: cardData.element, damage: cardData.damage, name: cardData.name || cardData.element, effect: cardData.effect || 'normal' }); // Remove the played card from enemy hand currentEnemy.removeCardFromHand(cardIndex); // Check for snowman effect - hombre de nieve deals 2 damage and sets up future 1 damage if (cardData.name === 'Hombre de nieve') { // Deal 2 damage now playerHealth -= 2; // Set up 10% chance for 1 damage next turn currentEnemy.snowmanPendingDamage = true; } else { // Check if there's pending snowman damage from previous turn if (currentEnemy.snowmanPendingDamage) { if (Math.random() < 0.1) { // 10% chance to deal 1 additional damage playerHealth -= 1; } // Reset the pending damage flag currentEnemy.snowmanPendingDamage = false; } switch (element) { case 'Fire': // Fire burns one random player card + damage if (playerHand.length > 0) { var burnIndex = Math.floor(Math.random() * playerHand.length); var cardToBurn = playerHand[burnIndex]; playerHand.splice(burnIndex, 1); battleContainer.removeChild(cardToBurn); } damage += 2; break; case 'Water': // Handle water card effects if (cardData.effect === 'steal') { // Pirata - enemy shows card steal interface if (playerHand.length > 0 && currentEnemy.hand.length < 3) { showEnemyCardStealInterface(); return; // Don't continue with normal enemy turn flow yet } } break; case 'Air': drawPlayerHand(); break; case 'Ice': // Player frozen (skip next turn - handled in game flow) break; } // Apply damage through shields first if (damage > 0) { // Check for magma shields first if (playerMagmaShields > 0 && element !== 'Water') { // Magma shields reduce damage by shield count, but water attacks consume 2 shields var damageReduction = playerMagmaShields; var reducedDamage = Math.max(0, damage - damageReduction); playerMagmaShields -= 1; // Always lose 1 shield per attack damage = reducedDamage; } else if (playerMagmaShields > 0 && element === 'Water') { // Water attacks consume 2 magma shields and are not reduced var shieldsToRemove = Math.min(2, playerMagmaShields); playerMagmaShields -= shieldsToRemove; // Water damage is NOT reduced by magma shields } // Then check for regular shields if (playerShield && playerShield.health > 0 && damage > 0) { var shieldAbsorbed = Math.min(damage, playerShield.health); playerShield.health -= shieldAbsorbed; damage -= shieldAbsorbed; updateShieldDisplay(); if (damage > 0) { playerHealth -= damage; } } else if (damage > 0) { playerHealth -= damage; } } } if (playerHealth < 0) playerHealth = 0; updatePlayerHealth(); LK.getSound('damage').play(); if (playerHealth === 0) { // In roguelike mode, track the level where player died if (gameMode === 'roguelike' && currentBattle > roguelikeLevel) { roguelikeLevel = currentBattle; storage.highestLevel = roguelikeLevel; } // Reset mana to full when battle ends playerMana = playerMaxMana; updatePlayerMana(); LK.showGameOver(); return; } endEnemyTurn(); } function endEnemyTurn() { // Clear enemy used cards for this turn enemyUsedCards = []; // Handle tutorial progression if (isTutorialActive && tutorialStep === 4) { // After enemy attack, show mana restoration tutorialStep = 5; showTutorialMessage("Como puedes ver, tu barra de maná se restauró. Fíjate bien.", function () { highlightManaBar(); LK.setTimeout(function () { tutorialStep = 6; showTutorialMessage("Tienes una carta de daño (Rama), otra que sana (Agua oxigenada) y otra que protege (Muralla rocosa).", function () { highlightCardTypes(); LK.setTimeout(function () { tutorialStep = 7; showTutorialMessage("El mago usará un hechizo que te dejará sin maná. ¡Observa!", function () { // Enemy casts spell to drain player mana playerMana = 0; updatePlayerMana(); LK.setTimeout(function () { tutorialStep = 8; showTutorialMessage("Si te quedas sin maná, puedes pasar el turno para recuperarlo todo.", function () { gameState = 'playerTurn'; turnText.setText('Tutorial - Prueba Pasar Turno'); }); }, 1000); }); }, 2000); }); }, 2000); }); return; } // Check if this is the end of a complete turn (both players have played) // If enemy started first, complete turn ends here // If player started first, we need to give player their turn if (!playerStartsFirst) { // Enemy started first, now it's player's turn to complete the turn gameState = 'playerTurn'; turnText.setText('Your Turn - Turn ' + turnCounter); } else { // Player started first and enemy just finished, complete turn ends here // Complete turn cycle - both player and enemy have played, now start new complete turn startNewCompleteTurn(); } } function startNewCompleteTurn() { turnCounter++; // Restore mana after complete turn cycle (except first turn) if (turnCounter > 1) { // Restore 3-5 mana for player, but never exceed max var manaToRestore = 3 + Math.floor(Math.random() * 3); // Random between 3-5 playerMana = Math.min(playerMana + manaToRestore, playerMaxMana); // Restore 3-5 mana for enemy, but never exceed max var enemyManaToRestore = 3 + Math.floor(Math.random() * 3); // Random between 3-5 currentEnemy.mana = Math.min(currentEnemy.mana + enemyManaToRestore, currentEnemy.maxMana); updatePlayerMana(); } // Both players get 3 new cards (maintaining unused cards from previous mazo de selección) drawNewHandForCompleteTurn('player'); drawNewHandForCompleteTurn('enemy'); // Start card selection phase startCardSelectionPhase(); } function clearAllHands() { // Clear player hand for (var i = playerHand.length - 1; i >= 0; i--) { var card = playerHand[i]; battleContainer.removeChild(card); } playerHand = []; // Clear enemy hand if (currentEnemy.handCards) { for (var i = 0; i < currentEnemy.handCards.length; i++) { if (currentEnemy.handCards[i].parent) { currentEnemy.handCards[i].parent.removeChild(currentEnemy.handCards[i]); } } } currentEnemy.handCards = []; currentEnemy.hand = []; } function drawNewHandForCompleteTurn(playerType) { if (playerType === 'player') { // Player maintains unused cards from previous mazo de selección in the new mazo de selección var playerHandNames = []; // Track card names to prevent duplicates in new mazo de selección // Identify unused cards from current mazo de selección (those not in playerUsedCards) var cardsToKeep = []; for (var i = 0; i < playerHand.length; i++) { var wasUsed = false; for (var j = 0; j < playerUsedCards.length; j++) { if (playerUsedCards[j].name === playerHand[i].name) { wasUsed = true; break; } } // Keep unused cards from previous mazo de selección for new mazo de selección if (!wasUsed) { cardsToKeep.push(playerHand[i]); playerHandNames.push(playerHand[i].name); } } // Clear current hand visuals and re-add kept cards for (var i = playerHand.length - 1; i >= 0; i--) { battleContainer.removeChild(playerHand[i]); } playerHand = []; // Re-add kept cards to hand with updated positions for (var i = 0; i < cardsToKeep.length; i++) { var keptCard = cardsToKeep[i]; keptCard.x = 400 + i * 350; keptCard.y = 1800; keptCard.isUsed = false; // Reset used state playerHand.push(keptCard); battleContainer.addChild(keptCard); } // Add new cards to complete hand to 3 cards total var cardsNeeded = 3 - cardsToKeep.length; for (var i = 0; i < cardsNeeded; i++) { if (playerDeck.length > 0) { var attempts = 0; var foundValidCard = false; while (attempts < 50 && !foundValidCard) { var randomIndex = Math.floor(Math.random() * playerDeck.length); var cardData = playerDeck[randomIndex]; // Check if this card name is already in current hand or was used this turn var isDuplicate = false; // Check against current hand for (var j = 0; j < playerHandNames.length; j++) { if (playerHandNames[j] === cardData.name) { isDuplicate = true; break; } } // Check against used cards this turn if (!isDuplicate) { for (var j = 0; j < playerUsedCards.length; j++) { if (playerUsedCards[j].name === cardData.name) { isDuplicate = true; break; } } } // Use card if it's not a duplicate if (!isDuplicate) { var card = new Card(cardData.element, cardData.damage, cardData.name, cardData.effect); card.x = 400 + (cardsToKeep.length + i) * 350; card.y = 1800; playerHand.push(card); battleContainer.addChild(card); playerHandNames.push(cardData.name); foundValidCard = true; } attempts++; } } } } else { // Enemy maintains unused cards from previous mazo de selección in the new mazo de selección var enemyHandNames = []; // Track card names to prevent duplicates in new mazo de selección // Identify unused cards from current mazo de selección (those not in enemyUsedCards) var cardsToKeep = []; for (var i = 0; i < currentEnemy.hand.length; i++) { var wasUsed = false; var cardName = currentEnemy.hand[i].name || currentEnemy.hand[i].element; for (var j = 0; j < enemyUsedCards.length; j++) { if (enemyUsedCards[j].name === cardName) { wasUsed = true; break; } } // Keep unused cards from previous mazo de selección for new mazo de selección if (!wasUsed) { cardsToKeep.push(currentEnemy.hand[i]); enemyHandNames.push(cardName); } } // Clear current enemy hand visuals if (currentEnemy.handCards) { for (var i = 0; i < currentEnemy.handCards.length; i++) { if (currentEnemy.handCards[i].parent) { currentEnemy.handCards[i].parent.removeChild(currentEnemy.handCards[i]); } } } currentEnemy.handCards = []; currentEnemy.hand = []; // Re-add kept cards to enemy hand for (var i = 0; i < cardsToKeep.length; i++) { currentEnemy.hand.push(cardsToKeep[i]); // Create visual card for kept enemy card var enemyCard = new Card(cardsToKeep[i].element, cardsToKeep[i].damage, cardsToKeep[i].name || cardsToKeep[i].element, cardsToKeep[i].effect || 'normal'); enemyCard.x = -400 + i * 200; enemyCard.y = -400; enemyCard.scaleX = 0.6; enemyCard.scaleY = 0.6; currentEnemy.addChild(enemyCard); if (!currentEnemy.handCards) currentEnemy.handCards = []; currentEnemy.handCards.push(enemyCard); } // Add new cards to complete hand to 3 cards total var cardsNeeded = 3 - cardsToKeep.length; for (var i = 0; i < cardsNeeded; i++) { if (currentEnemy.deck.length > 0) { var attempts = 0; var foundValidCard = false; while (attempts < 50 && !foundValidCard) { var randomIndex = Math.floor(Math.random() * currentEnemy.deck.length); var cardData = currentEnemy.deck[randomIndex]; var cardName = cardData.name || cardData.element; // Check if this card name is already in current hand or was used this turn var isDuplicate = false; // Check against current hand for (var j = 0; j < enemyHandNames.length; j++) { if (enemyHandNames[j] === cardName) { isDuplicate = true; break; } } // Check against used cards this turn if (!isDuplicate) { for (var j = 0; j < enemyUsedCards.length; j++) { if (enemyUsedCards[j].name === cardName) { isDuplicate = true; break; } } } // Use card if it's not a duplicate if (!isDuplicate) { currentEnemy.hand.push(cardData); // Create visual card for enemy var enemyCard = new Card(cardData.element, cardData.damage, cardData.name || cardData.element, cardData.effect || 'normal'); enemyCard.x = -400 + (cardsToKeep.length + i) * 200; enemyCard.y = -400; enemyCard.scaleX = 0.6; enemyCard.scaleY = 0.6; currentEnemy.addChild(enemyCard); if (!currentEnemy.handCards) currentEnemy.handCards = []; currentEnemy.handCards.push(enemyCard); enemyHandNames.push(cardName); foundValidCard = true; } attempts++; } } } } } function updatePlayerHealth() { var healthPercentage = playerHealth / playerMaxHealth; playerHealthBar.scaleX = healthPercentage; playerHealthText.setText('HP: ' + playerHealth + '/' + playerMaxHealth); } function updatePlayerMana() { var manaPercentage = playerMana / playerMaxMana; playerManaBar.scaleX = manaPercentage; playerManaText.setText('⚡: ' + playerMana + '/' + playerMaxMana); } function updateShieldDisplay() { if (playerShield && playerShield.health > 0) { var shieldPercentage = playerShield.health / playerShield.maxHealth; playerShieldBar.scaleX = shieldPercentage; playerShieldBar.alpha = 1; playerShieldText.setText('🛡: ' + playerShield.health + '/' + playerShield.maxHealth); playerShieldText.alpha = 1; } else { playerShieldBar.alpha = 0; playerShieldText.alpha = 0; playerShield = null; } } function startBattle() { gameState = 'battle'; if (gameMode === 'adventure') { // Start from first uncompleted battle currentBattle = completedCourses * 3; // Set health based on last completed course's final wizard if (completedCourses > 0) { // Get the last wizard from the previous completed course var lastCompletedCourse = completedCourses - 1; var lastWizardIndex = lastCompletedCourse * 3 + 2; // Third wizard of the course (index 2) if (lastWizardIndex < enemies.length) { var lastWizard = enemies[lastWizardIndex]; playerHealth = lastWizard.maxHealth; playerMaxHealth = lastWizard.maxHealth; // Store the max health for future reference storage.playerMaxHealth = playerMaxHealth; } else { playerHealth = 10; playerMaxHealth = 10; } } else { // First course - start with default health playerHealth = 10; playerMaxHealth = 10; } } else { // Roguelike mode currentBattle = 0; playerHealth = 10; playerMaxHealth = 10; } turnCounter = 0; playerMana = playerMaxMana; // Randomly determine who starts first playerStartsFirst = Math.random() < 0.5; game.removeChild(menuContainer); createBattle(); // Ensure both players have 3 cards in their mazo de selección (selection deck) at start drawPlayerHand(); currentEnemy.drawHand(); // Start with card selection phase for turn 1 turnCounter = 1; startCardSelectionPhase(); } function showElementBook(element) { // Create book container var bookContainer = new Container(); // Background var bookBg = LK.getAsset('background', { anchorX: 0, anchorY: 0 }); bookContainer.addChild(bookBg); // Title var titleText = new Text2(element + ' Element Cards', { size: 60, fill: 0xFFFFFF }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 300; bookContainer.addChild(titleText); // Create scrollable content container var scrollContainer = new Container(); var currentScrollY = 0; var maxScrollY = 0; // Get element cards based on element type var elementCards = []; switch (element) { case 'Fire': elementCards = [{ name: 'Llamarada', damage: 3, effect: 'Inflige 3 de daño y quema una carta enemiga' }, { name: 'Fénix', damage: 2, effect: 'Inflige 2 de daño y cura 2 puntos de vida' }, { name: 'Brasas', damage: 2, effect: 'Inflige 2 de daño y quema una carta enemiga' }, { name: 'Llama', damage: 2, effect: 'Inflige 2 de daño y quema una carta enemiga' }, { name: 'Mechero', damage: 1, effect: 'Inflige 1 de daño y evita que el enemigo use cartas de hielo' }, { name: 'Escudos de Magma', damage: 0, effect: 'Crea 3 escudos que reducen daño según su cantidad. Los ataques de agua no se reducen y gastan 2 escudos.' }]; break; case 'Water': elementCards = [{ name: 'Gota de agua', damage: 1, effect: 'Inflige 1 de daño' }, { name: 'Luna roja', damage: 2, effect: 'Inflige 2 de daño' }, { name: 'Agua oxigenada', damage: 0, effect: 'Cura 3 puntos de vida' }, { name: 'Marea', damage: 2, effect: 'Inflige 2 de daño' }, { name: 'Pirata', damage: 1, effect: 'Inflige 1 de daño y roba una carta enemiga' }]; break; case 'Air': elementCards = [{ name: 'Vendaval', damage: 2, effect: 'Inflige 2 de daño' }, { name: 'Brisa', damage: 1, effect: 'Inflige 1 de daño' }, { name: 'Huracán', damage: 2, effect: 'Inflige 2 de daño y reemplaza las cartas enemigas' }, { name: 'Tormenta', damage: 3, effect: 'Inflige 3 de daño, 10% de probabilidad de saltar turno enemigo' }, { name: 'Niebla', damage: 1, effect: 'Inflige 1 de daño, 10% de fallo permanente en turnos enemigos' }, { name: 'Oxígeno', damage: 0, effect: 'Cura 2 puntos de vida' }]; break; case 'Plant': elementCards = [{ name: 'Arboleda', damage: 2, effect: 'Inflige 2 de daño' }, { name: 'Serafín', damage: 0, effect: 'Cura 2 puntos de vida' }, { name: 'Rama', damage: 1, effect: 'Inflige 1 de daño' }, { name: 'Vida extra', damage: 0, effect: 'Aumenta la vida máxima en 1' }, { name: 'Aullidos del bosque', damage: 2, effect: 'Inflige 2 de daño, el enemigo pierde su turno' }]; break; case 'Rock': elementCards = [{ name: 'Pedrada', damage: 2, effect: 'Inflige 2 de daño' }, { name: 'Tormenta de arena', damage: 2, effect: 'Inflige 2 de daño' }, { name: 'Sarcófago', damage: 2, effect: 'Inflige 2 de daño y cura 2 puntos de vida' }, { name: 'Muralla Rocosa', damage: 0, effect: 'Crea escudo con 4 puntos de vida que absorbe daño' }, { name: 'Terremoto', damage: 2, effect: 'Inflige 2 de daño, ambos jugadores obtienen cartas nuevas' }]; break; case 'Ice': elementCards = [{ name: 'Nevada', damage: 2, effect: 'Inflige 2 de daño y congela al enemigo' }, { name: 'Ventisca', damage: 2, effect: 'Inflige 2 de daño, 10% de probabilidad de congelar' }, { name: 'Bola de nieve', damage: 1, effect: 'Inflige 1 de daño y congela al enemigo' }, { name: 'Hombre de nieve', damage: 2, effect: 'Inflige 2 de daño, puede atacar de nuevo por 1 de daño' }, { name: 'Esquirla Helada', damage: 1, effect: 'Inflige 1 de daño con prioridad, el enemigo pierde su próximo turno' }]; break; case 'Invocation': elementCards = [{ name: 'Fénix', damage: 2, effect: 'Inflige 2 de daño y cura 2 puntos de vida' }, { name: 'Pirata', damage: 1, effect: 'Inflige 1 de daño y roba una carta enemiga' }, { name: 'Serafín', damage: 0, effect: 'Cura 2 puntos de vida' }, { name: 'Sarcófago', damage: 2, effect: 'Inflige 2 de daño y cura 2 puntos de vida' }, { name: 'Hombre de nieve', damage: 2, effect: 'Inflige 2 de daño, puede atacar de nuevo por 1 de daño' }]; break; } // Helper function to word wrap text function wrapText(text, maxWidth) { var words = text.split(' '); var lines = []; var currentLine = ''; for (var i = 0; i < words.length; i++) { var testLine = currentLine + (currentLine ? ' ' : '') + words[i]; if (testLine.length * 22 <= maxWidth) { // Approximate character width currentLine = testLine; } else { if (currentLine) { lines.push(currentLine); } currentLine = words[i]; } } if (currentLine) { lines.push(currentLine); } return lines; } // Display cards in scroll container for (var i = 0; i < elementCards.length; i++) { var cardInfo = elementCards[i]; var yPos = 100 + i * 380; // Increased spacing for wrapped text // Card name var nameText = new Text2(cardInfo.name, { size: 55, fill: 0xffffff, stroke: 0x000000, strokeThickness: 2 }); nameText.anchor.set(0, 0.5); nameText.x = 100; nameText.y = yPos; scrollContainer.addChild(nameText); // Damage var damageText = new Text2('Daño: ' + cardInfo.damage, { size: 45, fill: 0xffffff, stroke: 0x000000, strokeThickness: 2 }); damageText.anchor.set(0, 0.5); damageText.x = 100; damageText.y = yPos + 50; scrollContainer.addChild(damageText); // Mana cost var manaCost = getCardManaCost(cardInfo.name, cardInfo.effect); var manaText = new Text2('Maná: ⚡' + manaCost, { size: 45, fill: 0xFFD700, stroke: 0x000000, strokeThickness: 2 }); manaText.anchor.set(0, 0.5); manaText.x = 100; manaText.y = yPos + 100; scrollContainer.addChild(manaText); // Effect with word wrapping var effectLines = wrapText('Efecto: ' + cardInfo.effect, 1800); for (var j = 0; j < effectLines.length; j++) { var effectText = new Text2(effectLines[j], { size: 40, fill: 0xffffff, stroke: 0x000000, strokeThickness: 2 }); effectText.anchor.set(0, 0.5); effectText.x = 100; effectText.y = yPos + 150 + j * 45; scrollContainer.addChild(effectText); } maxScrollY = Math.max(maxScrollY, yPos + 200); } // Position scroll container to start below title with more spacing scrollContainer.y = 450; // Start well below title area to prevent overlap bookContainer.addChild(scrollContainer); // Scroll controls var scrollUpButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1850, y: 1200, scaleX: 0.5, scaleY: 0.4 }); bookContainer.addChild(scrollUpButton); var scrollUpText = new Text2('▲', { size: 60, fill: 0xFFFFFF }); scrollUpText.anchor.set(0.5, 0.5); scrollUpText.x = 1850; scrollUpText.y = 1200; bookContainer.addChild(scrollUpText); var scrollDownButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1850, y: 2200, scaleX: 0.5, scaleY: 0.4 }); bookContainer.addChild(scrollDownButton); var scrollDownText = new Text2('▼', { size: 60, fill: 0xFFFFFF }); scrollDownText.anchor.set(0.5, 0.5); scrollDownText.x = 1850; scrollDownText.y = 2200; bookContainer.addChild(scrollDownText); // Scroll functionality scrollUpButton.down = function () { var maxScrollUp = -50; // Prevent scrolling too far up to keep content below title if (currentScrollY < maxScrollUp) { currentScrollY += 200; if (currentScrollY > maxScrollUp) currentScrollY = maxScrollUp; scrollContainer.y = 450 + currentScrollY; // Base position + scroll offset } }; scrollDownButton.down = function () { var minScrollY = -(maxScrollY - 1500); // Visible area height if (currentScrollY > minScrollY) { currentScrollY -= 200; if (currentScrollY < minScrollY) currentScrollY = minScrollY; scrollContainer.y = 450 + currentScrollY; // Base position + scroll offset } }; // Back button var backButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 2600 }); bookContainer.addChild(backButton); var backText = new Text2('Back to Menu', { size: 40, fill: 0xFFFFFF }); backText.anchor.set(0.5, 0.5); backText.x = 1024; backText.y = 2600; bookContainer.addChild(backText); backButton.down = function () { game.removeChild(bookContainer); LK.playMusic('Academia', { fade: { start: 0, end: 1, duration: 500 } }); }; game.addChild(bookContainer); } function showCourseCompletionAnimation(courseNumber) { // Create animation container var animContainer = new Container(); // Semi-transparent background var animBg = LK.getAsset('background', { anchorX: 0, anchorY: 0, alpha: 0.8 }); animContainer.addChild(animBg); // Course completed text var completionText = new Text2('COURSE ' + courseNumber + ' COMPLETED!', { size: 80, fill: 0xFFD700, font: "'Arial Black', Arial, sans-serif", stroke: 0x4B0082, strokeThickness: 4 }); completionText.anchor.set(0.5, 0.5); completionText.x = 1024; completionText.y = 1366; completionText.alpha = 0; completionText.scaleX = 0.1; completionText.scaleY = 0.1; animContainer.addChild(completionText); // Star effects var stars = []; for (var i = 0; i < 5; i++) { var star = LK.getAsset('academy', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.2, scaleY: 0.2, tint: 0xFFD700 }); star.x = 1024 + (i - 2) * 200; star.y = 1000; star.alpha = 0; star.rotation = 0; animContainer.addChild(star); stars.push(star); } game.addChild(animContainer); // Animate text appearance tween(completionText, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 800, easing: tween.elasticOut }); // Animate stars for (var i = 0; i < stars.length; i++) { (function (star, delay) { LK.setTimeout(function () { tween(star, { alpha: 1, rotation: Math.PI * 2 }, { duration: 600, easing: tween.easeOut }); }, delay); })(stars[i], i * 150); } // Pulsing effect LK.setTimeout(function () { tween(completionText, { scaleX: 1.2, scaleY: 1.2 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(completionText, { scaleX: 1, scaleY: 1 }, { duration: 500, easing: tween.easeInOut }); } }); }, 1000); // Remove animation after 3 seconds LK.setTimeout(function () { tween(animContainer, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { game.removeChild(animContainer); // Check if all courses completed for graduation if (completedCourses >= 6) { showGraduationAnimation(); } } }); }, 3000); } function startTutorialBattle() { isTutorialActive = true; gameState = 'tutorial'; turnCounter = 0; playerHealth = 10; playerMaxHealth = 10; playerMana = 10; playerMaxMana = 10; playerStartsFirst = true; // Player always starts first in tutorial // Create special tutorial enemy (wizard18 - different from regular enemies) tutorialEnemy = new Enemy('Maestro del Tutorial', 8, 0, 17); // Use wizard18 currentEnemy = tutorialEnemy; // Initialize special tutorial deck for player with specific cards playerDeck = [{ element: 'Plant', damage: 1, name: 'Rama', effect: 'normal' }, { element: 'Water', damage: 0, name: 'Agua oxigenada', effect: 'heal' }, { element: 'Rock', damage: 0, name: 'Muralla Rocosa', effect: 'shield' }]; game.removeChild(menuContainer); createBattle(); startTutorial(); } function createTutorialWizard() { // Create tutorial wizard (friendly mentor) tutorialWizard = LK.getAsset('wizard1', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); tutorialWizard.x = 300; tutorialWizard.y = 800; battleContainer.addChild(tutorialWizard); // Create text box for tutorial messages var textBoxBg = LK.getAsset('Cuadro_dialogo', { anchorX: 0.5, anchorY: 0.5, scaleX: 6.0, scaleY: 4.0 }); textBoxBg.x = 1024; textBoxBg.y = 400; tutorialTextBox = new Text2('', { size: 28, fill: 0x000000, stroke: 0x000000, strokeThickness: 3 }); tutorialTextBox.anchor.set(0.5, 0.5); tutorialTextBox.x = 1024; tutorialTextBox.y = 400; tutorialContainer = new Container(); tutorialContainer.addChild(textBoxBg); tutorialContainer.addChild(tutorialTextBox); battleContainer.addChild(tutorialContainer); } function showTutorialMessage(message, callback) { if (tutorialTextBox) { // Split message into lines to fit within dialogue box width var words = message.split(' '); var lines = []; var currentLine = ''; var maxWordsPerLine = 8; // Adjust based on dialogue box width var wordsInCurrentLine = 0; for (var i = 0; i < words.length; i++) { if (wordsInCurrentLine >= maxWordsPerLine) { lines.push(currentLine.trim()); currentLine = words[i] + ' '; wordsInCurrentLine = 1; } else { currentLine += words[i] + ' '; wordsInCurrentLine++; } } if (currentLine.trim().length > 0) { lines.push(currentLine.trim()); } tutorialTextBox.setText(lines.join('\n')); // Add black border to text tutorialTextBox.stroke = 0x000000; tutorialTextBox.strokeThickness = 3; // Adjust dialogue box scale based on number of lines var lineCount = lines.length; var scaleX = Math.max(6.0, lineCount * 0.8 + 4.0); // Minimum 6.0, grows with content var scaleY = Math.max(3.0, lineCount * 0.6 + 2.0); // Minimum 3.0, grows with content // Find and update the dialogue box scale if (tutorialContainer && tutorialContainer.children[0]) { tutorialContainer.children[0].scaleX = scaleX; tutorialContainer.children[0].scaleY = scaleY; } // Make tutorial container clickable to continue tutorialContainer.down = function () { if (callback) callback(); }; } } function startTutorial() { gameState = 'tutorial'; tutorialStep = 1; createTutorialWizard(); // Step 1: Welcome and show mazo de selección showTutorialMessage("¡Bienvenido! Estos son tu mazo de selección de 3 cartas. Observa cómo se iluminan.", function () { highlightPlayerHand(); LK.setTimeout(function () { tutorialStep = 2; // Step 2: Explain mana costs showTutorialMessage("Cada carta tiene un coste de maná. Fíjate en el símbolo ⚡ de cada carta.", function () { highlightManaCosts(); LK.setTimeout(function () { tutorialStep = 3; // Step 3: Player selects and plays first card showTutorialMessage("Ahora selecciona una carta para atacar. Te recomiendo usar 'Rama'.", function () { gameState = 'playerTurn'; turnText.setText('Tutorial - Elige una carta'); removeHighlight(); }); }, 2000); }); }, 2000); }); } function highlightPlayerHand() { // Create highlight effect for player hand for (var i = 0; i < playerHand.length; i++) { (function (card) { tween(card, { tint: 0xFFFF00 }, { duration: 500, easing: tween.easeInOut, onFinish: function onFinish() { tween(card, { tint: 0xFFFFFF }, { duration: 500, easing: tween.easeInOut }); } }); })(playerHand[i]); } } function highlightManaCosts() { // Visual emphasis on mana costs (cards will flash blue to highlight mana symbols) for (var i = 0; i < playerHand.length; i++) { (function (card) { tween(card, { tint: 0x00FFFF }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(card, { tint: 0xFFFFFF }, { duration: 800, easing: tween.easeInOut }); } }); })(playerHand[i]); } } function highlightManaBar() { // Highlight player mana bar tween(playerManaBar, { tint: 0xFFFF00 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(playerManaBar, { tint: 0x0066FF }, { duration: 1000, easing: tween.easeInOut }); } }); } function highlightCardTypes() { // Highlight each card type with different colors for (var i = 0; i < playerHand.length; i++) { var card = playerHand[i]; var highlightColor = 0xFFFFFF; if (card.name === 'Rama') highlightColor = 0xFF0000; // Red for damage else if (card.name === 'Agua oxigenada') highlightColor = 0x00FF00; // Green for heal else if (card.name === 'Muralla Rocosa') highlightColor = 0x0000FF; // Blue for shield (function (cardToHighlight, color) { tween(cardToHighlight, { tint: color }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { tween(cardToHighlight, { tint: 0xFFFFFF }, { duration: 1500, easing: tween.easeInOut }); } }); })(card, highlightColor); } } function removeHighlight() { // Remove any highlights if (tutorialHighlight) { battleContainer.removeChild(tutorialHighlight); tutorialHighlight = null; } } function endTutorial() { // Mark tutorial as completed if (isFirstTimeAdventure || !storage.hasPlayedAdventure) { storage.hasPlayedAdventure = true; isFirstTimeAdventure = false; } isTutorialActive = false; // Remove tutorial UI if (tutorialContainer) { battleContainer.removeChild(tutorialContainer); tutorialContainer = null; } if (tutorialWizard) { battleContainer.removeChild(tutorialWizard); tutorialWizard = null; } removeHighlight(); // Defeat tutorial enemy to end tutorial tutorialEnemy.health = 0; tutorialEnemy.healthText.setText('HP: 0'); // Show victory and return to menu LK.setTimeout(function () { gameState = 'menu'; game.removeChild(battleContainer); createMenu(); LK.playMusic('Academia', { fade: { start: 0, end: 1, duration: 500 } }); }, 1000); } function showPlayerSetup() { // Create setup container var setupContainer = new Container(); // Background var setupBg = LK.getAsset('background', { anchorX: 0, anchorY: 0 }); setupContainer.addChild(setupBg); // Title var titleText = new Text2('¡Bienvenido a la Academia Mágica!', { size: 80, fill: 0xFFD700, font: "'Arial Black', Arial, sans-serif", stroke: 0x4B0082, strokeThickness: 4 }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 400; setupContainer.addChild(titleText); // Gender selection text var genderText = new Text2('Eres mago o maga:', { size: 60, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 3 }); genderText.anchor.set(0.5, 0.5); genderText.x = 1024; genderText.y = 800; setupContainer.addChild(genderText); // Mago (wizard) option - three wizard assets var magoImages = []; var magoAssets = ['Mago', 'Mago1', 'Mago2']; // Three Mago images with spacing - positioned between gender text and name text for (var i = 0; i < magoAssets.length; i++) { var magoImage = LK.getAsset(magoAssets[i], { anchorX: 0.5, anchorY: 0.5, x: 300 + i * 150, y: 1100, scaleX: 2.0, scaleY: 2.0 }); magoImages.push(magoImage); setupContainer.addChild(magoImage); } // Play buttons below mago assets var magoPlayButtons = []; for (var i = 0; i < magoAssets.length; i++) { var playButton = new Text2('▶️', { size: 60, fill: 0xFFFFFF }); playButton.anchor.set(0.5, 0.5); playButton.x = 300 + i * 150; playButton.y = 1250; magoPlayButtons.push(playButton); setupContainer.addChild(playButton); } // Make mago play buttons clickable for (var i = 0; i < magoPlayButtons.length; i++) { (function (buttonIndex, assetName, magoImage) { magoPlayButtons[buttonIndex].down = function (x, y, obj) { selectedGender = 'mago'; storage.playerGender = selectedGender; // Store which specific mago asset was selected storage.selectedMagoAsset = assetName; // Reset all mago images to normal and stop any existing animations for (var j = 0; j < magoImages.length; j++) { tween.stop(magoImages[j], { tint: true }); magoImages[j].tint = 0xFFFFFF; } // Reset all maga images and stop any glowing animations for (var j = 0; j < magaImages.length; j++) { tween.stop(magaImages[j], { tint: true }); magaImages[j].tint = 0xFFFFFF; } // Reset all play buttons for (var j = 0; j < magoPlayButtons.length; j++) { tween.stop(magoPlayButtons[j], { tint: true }); magoPlayButtons[j].tint = 0xFFFFFF; } for (var j = 0; j < magaPlayButtons.length; j++) { tween.stop(magaPlayButtons[j], { tint: true }); magaPlayButtons[j].tint = 0xFFFFFF; } // Create yellow border glow effect for the mago asset var borderGlow = LK.getAsset(assetName, { anchorX: 0.5, anchorY: 0.5, x: magoImage.x, y: magoImage.y, scaleX: 2.1, scaleY: 2.1, tint: 0xFFFF00, alpha: 0.7 }); setupContainer.addChild(borderGlow); // Position border behind the asset setupContainer.setChildIndex(borderGlow, setupContainer.getChildIndex(magoImage) - 1); function createMagoAssetGlow() { tween(borderGlow, { alpha: 1, scaleX: 2.2, scaleY: 2.2 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { tween(borderGlow, { alpha: 0.7, scaleX: 2.1, scaleY: 2.1 }, { duration: 600, easing: tween.easeInOut, onFinish: createMagoAssetGlow }); } }); } createMagoAssetGlow(); updateContinueButton(); }; })(i, magoAssets[i], magoImages[i]); } // Make mago assets clickable for (var i = 0; i < magoImages.length; i++) { (function (imageIndex, assetName) { magoImages[imageIndex].down = function (x, y, obj) { selectedGender = 'mago'; storage.playerGender = selectedGender; // Store which specific mago asset was selected storage.selectedMagoAsset = assetName; // Reset all mago images to normal and stop any existing animations for (var j = 0; j < magoImages.length; j++) { tween.stop(magoImages[j], { tint: true }); magoImages[j].tint = 0xFFFFFF; } // Reset all maga images and stop any glowing animations for (var j = 0; j < magaImages.length; j++) { tween.stop(magaImages[j], { tint: true }); magaImages[j].tint = 0xFFFFFF; } // Reset all play buttons for (var j = 0; j < magoPlayButtons.length; j++) { tween.stop(magoPlayButtons[j], { tint: true }); magoPlayButtons[j].tint = 0xFFFFFF; } for (var j = 0; j < magaPlayButtons.length; j++) { tween.stop(magaPlayButtons[j], { tint: true }); magaPlayButtons[j].tint = 0xFFFFFF; } // Create bright glowing effect for the selected mago asset function createMagoGlow() { tween(obj, { tint: 0xFFFF00 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { tween(obj, { tint: 0xFFFFFF }, { duration: 600, easing: tween.easeInOut, onFinish: createMagoGlow }); } }); } createMagoGlow(); updateContinueButton(); }; })(i, magoAssets[i]); } // Remove mago selection buttons - assets are now directly clickable // Maga (witch) option - three witch assets var magaImages = []; var magaAssets = ['Maga', 'Maga1', 'Maga2']; // Three Maga images with spacing - positioned between gender text and name text for (var i = 0; i < magaAssets.length; i++) { var magaImage = LK.getAsset(magaAssets[i], { anchorX: 0.5, anchorY: 0.5, x: 1200 + i * 150, y: 1100, scaleX: 2.0, scaleY: 2.0 }); magaImages.push(magaImage); setupContainer.addChild(magaImage); } // Play buttons below maga assets var magaPlayButtons = []; for (var i = 0; i < magaAssets.length; i++) { var playButton = new Text2('▶️', { size: 60, fill: 0xFFFFFF }); playButton.anchor.set(0.5, 0.5); playButton.x = 1200 + i * 150; playButton.y = 1250; magaPlayButtons.push(playButton); setupContainer.addChild(playButton); } // Make maga play buttons clickable for (var i = 0; i < magaPlayButtons.length; i++) { (function (buttonIndex, assetName, magaImage) { magaPlayButtons[buttonIndex].down = function (x, y, obj) { selectedGender = 'maga'; storage.playerGender = selectedGender; // Store which specific maga asset was selected storage.selectedMagaAsset = assetName; // Reset all mago images to normal and stop any existing animations for (var j = 0; j < magoImages.length; j++) { tween.stop(magoImages[j], { tint: true }); magoImages[j].tint = 0xFFFFFF; } // Reset all maga images to normal and stop any existing animations for (var j = 0; j < magaImages.length; j++) { tween.stop(magaImages[j], { tint: true }); magaImages[j].tint = 0xFFFFFF; } // Reset all play buttons for (var j = 0; j < magoPlayButtons.length; j++) { tween.stop(magoPlayButtons[j], { tint: true }); magoPlayButtons[j].tint = 0xFFFFFF; } for (var j = 0; j < magaPlayButtons.length; j++) { tween.stop(magaPlayButtons[j], { tint: true }); magaPlayButtons[j].tint = 0xFFFFFF; } // Create yellow border glow effect for the maga asset var borderGlow = LK.getAsset(assetName, { anchorX: 0.5, anchorY: 0.5, x: magaImage.x, y: magaImage.y, scaleX: 2.1, scaleY: 2.1, tint: 0xFFFF00, alpha: 0.7 }); setupContainer.addChild(borderGlow); // Position border behind the asset setupContainer.setChildIndex(borderGlow, setupContainer.getChildIndex(magaImage) - 1); function createMagaAssetGlow() { tween(borderGlow, { alpha: 1, scaleX: 2.2, scaleY: 2.2 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { tween(borderGlow, { alpha: 0.7, scaleX: 2.1, scaleY: 2.1 }, { duration: 600, easing: tween.easeInOut, onFinish: createMagaAssetGlow }); } }); } createMagaAssetGlow(); updateContinueButton(); }; })(i, magaAssets[i], magaImages[i]); } // Make maga assets clickable for (var i = 0; i < magaImages.length; i++) { (function (imageIndex, assetName) { magaImages[imageIndex].down = function (x, y, obj) { selectedGender = 'maga'; storage.playerGender = selectedGender; // Store which specific maga asset was selected storage.selectedMagaAsset = assetName; // Reset all mago images to normal and stop any existing animations for (var j = 0; j < magoImages.length; j++) { tween.stop(magoImages[j], { tint: true }); magoImages[j].tint = 0xFFFFFF; } // Reset all maga images to normal and stop any existing animations for (var j = 0; j < magaImages.length; j++) { tween.stop(magaImages[j], { tint: true }); magaImages[j].tint = 0xFFFFFF; } // Reset all play buttons for (var j = 0; j < magoPlayButtons.length; j++) { tween.stop(magoPlayButtons[j], { tint: true }); magoPlayButtons[j].tint = 0xFFFFFF; } for (var j = 0; j < magaPlayButtons.length; j++) { tween.stop(magaPlayButtons[j], { tint: true }); magaPlayButtons[j].tint = 0xFFFFFF; } // Create bright glowing effect for the selected maga asset function createMagaGlow() { tween(obj, { tint: 0xFF69B4 }, { duration: 600, easing: tween.easeInOut, onFinish: function onFinish() { tween(obj, { tint: 0xFFFFFF }, { duration: 600, easing: tween.easeInOut, onFinish: createMagaGlow }); } }); } createMagaGlow(); updateContinueButton(); }; })(i, magaAssets[i]); } // Remove maga selection buttons - assets are now directly clickable // Name input text var nameText = new Text2('¿Cuál es tu nombre?', { size: 60, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 3 }); nameText.anchor.set(0.5, 0.5); nameText.x = 1024; nameText.y = 1400; setupContainer.addChild(nameText); // Name input field (simulated with text) var nameInputBg = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1550, scaleX: 1.5, scaleY: 0.8 }); setupContainer.addChild(nameInputBg); var nameInputText = new Text2('Toca aquí para escribir tu nombre', { size: 40, fill: 0x888888 }); nameInputText.anchor.set(0.5, 0.5); nameInputText.x = 1024; nameInputText.y = 1550; setupContainer.addChild(nameInputText); // Continue button (initially hidden) var continueButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1750, alpha: 0.5 }); setupContainer.addChild(continueButton); var continueText = new Text2('Continuar', { size: 50, fill: 0xFFFFFF }); continueText.anchor.set(0.5, 0.5); continueText.x = 1024; continueText.y = 1750; continueText.alpha = 0.5; setupContainer.addChild(continueText); // State tracking var selectedGender = null; var playerName = ''; function updateContinueButton() { if (selectedGender && playerName.length > 0) { continueButton.alpha = 1; continueText.alpha = 1; } else { continueButton.alpha = 0.5; continueText.alpha = 0.5; } } // Mago button handlers removed - assets are now directly clickable // Maga button handlers removed - assets are now directly clickable // Name input handler (simple text input simulation) nameInputBg.down = function () { showNameInputDialog(); }; function showNameInputDialog() { // Create a text input interface var inputContainer = new Container(); // Semi-transparent background var inputBg = LK.getAsset('background', { anchorX: 0, anchorY: 0, alpha: 0.8 }); inputContainer.addChild(inputBg); // Input dialog box var dialogBox = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 2.5, scaleY: 2.0 }); inputContainer.addChild(dialogBox); // Title text var inputTitle = new Text2('Escribe tu nombre:', { size: 50, fill: 0xFFFFFF }); inputTitle.anchor.set(0.5, 0.5); inputTitle.x = 1024; inputTitle.y = 1200; inputContainer.addChild(inputTitle); // Current name display var currentNameText = new Text2(playerName || '', { size: 60, fill: 0xFFD700, stroke: 0x000000, strokeThickness: 3 }); currentNameText.anchor.set(0.5, 0.5); currentNameText.x = 1024; currentNameText.y = 1300; inputContainer.addChild(currentNameText); // Keyboard using Teclado asset var keyboard = LK.getAsset('Teclado', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1500, scaleX: 8.0, scaleY: 6.0 }); inputContainer.addChild(keyboard); // Make the entire keyboard clickable for functionality keyboard.down = function (x, y, obj) { // Convert local click coordinates to determine which key was pressed var localPos = keyboard.toLocal({ x: x, y: y }); var keyboardWidth = 100 * keyboard.scaleX; // Original width * scale var keyboardHeight = 100 * keyboard.scaleY; // Original height * scale // Normalize coordinates to 0-1 range var normalizedX = (localPos.x + keyboardWidth / 2) / keyboardWidth; var normalizedY = (localPos.y + keyboardHeight / 2) / keyboardHeight; // Define keyboard layout based on typical QWERTY layout var keyRows = ['QWERTYUIOP', 'ASDFGHJKL', 'ZXCVBNM']; // Determine which row was clicked (3 rows of keys) var rowIndex = Math.floor(normalizedY * 3); if (rowIndex >= 0 && rowIndex < keyRows.length) { var row = keyRows[rowIndex]; var keyIndex = Math.floor(normalizedX * row.length); if (keyIndex >= 0 && keyIndex < row.length) { var letter = row[keyIndex]; if (playerName.length < 15) { playerName += letter; currentNameText.setText(playerName); } } } }; // Letter buttons grid (invisible but functional over the keyboard) var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; var letterButtons = []; var buttonsPerRow = 9; var rows = Math.ceil(letters.length / buttonsPerRow); for (var i = 0; i < letters.length; i++) { var row = Math.floor(i / buttonsPerRow); var col = i % buttonsPerRow; var startX = 1024 - buttonsPerRow * 60 / 2; var letterButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: startX + col * 60, y: 1400 + row * 60, scaleX: 0.4, scaleY: 0.4, alpha: 0 }); (function (letter) { letterButton.down = function () { if (playerName.length < 15) { // Limit name length playerName += letter; currentNameText.setText(playerName); } }; })(letters[i]); inputContainer.addChild(letterButton); letterButtons.push(letterButton); } // Backspace button var backspaceButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 800, y: 1600, scaleX: 0.8, scaleY: 0.6 }); inputContainer.addChild(backspaceButton); var backspaceText = new Text2('BORRAR', { size: 35, fill: 0xFFFFFF }); backspaceText.anchor.set(0.5, 0.5); backspaceText.x = 800; backspaceText.y = 1600; inputContainer.addChild(backspaceText); backspaceButton.down = function () { if (playerName.length > 0) { playerName = playerName.slice(0, -1); currentNameText.setText(playerName); } }; // Confirm button var confirmButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1248, y: 1600, scaleX: 0.8, scaleY: 0.6 }); inputContainer.addChild(confirmButton); var confirmText = new Text2('CONFIRMAR', { size: 30, fill: 0xFFFFFF }); confirmText.anchor.set(0.5, 0.5); confirmText.x = 1248; confirmText.y = 1600; inputContainer.addChild(confirmText); confirmButton.down = function () { if (playerName.length > 0) { storage.playerName = playerName; nameInputText.setText(playerName); nameInputText.fill = 0xFFFFFF; updateContinueButton(); game.removeChild(inputContainer); } }; game.addChild(inputContainer); } // Reset character selection button (positioned in bottom right corner) var resetCharacterButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1700, y: 2500, scaleX: 0.8, scaleY: 0.6 }); setupContainer.addChild(resetCharacterButton); var resetCharacterText = new Text2('RESET CHARACTER', { size: 25, fill: 0xFFFFFF }); resetCharacterText.anchor.set(0.5, 0.5); resetCharacterText.x = 1700; resetCharacterText.y = 2500; setupContainer.addChild(resetCharacterText); resetCharacterButton.down = function () { // Reset all character selection data storage.playerGender = "undefined"; storage.playerName = "undefined"; storage.selectedMagoAsset = "Mago"; storage.selectedMagaAsset = "Maga"; storage.hasPlayedAdventure = false; storage.completedCourses = 0; storage.currentCourse = 0; storage.currentBattleInCourse = 0; storage.playerMaxHealth = 10; // Reset local variables selectedGender = null; playerName = ''; completedCourses = 0; currentBattle = 0; isFirstTimeAdventure = true; // Reset UI state nameInputText.setText('Toca aquí para escribir tu nombre'); nameInputText.fill = 0x888888; // Reset all selection button states - removed since buttons no longer exist // Reset all images and stop glowing animations for (var j = 0; j < magoImages.length; j++) { tween.stop(magoImages[j], { tint: true }); magoImages[j].tint = 0xFFFFFF; } for (var j = 0; j < magaImages.length; j++) { tween.stop(magaImages[j], { tint: true }); magaImages[j].tint = 0xFFFFFF; } // Remove any existing border glow effects for (var j = setupContainer.children.length - 1; j >= 0; j--) { var child = setupContainer.children[j]; if (child.tint === 0xFFFF00 && child.alpha <= 1) { setupContainer.removeChild(child); } } // Reset all play buttons and stop glowing animations for (var j = 0; j < magoPlayButtons.length; j++) { tween.stop(magoPlayButtons[j], { tint: true }); magoPlayButtons[j].tint = 0xFFFFFF; } for (var j = 0; j < magaPlayButtons.length; j++) { tween.stop(magaPlayButtons[j], { tint: true }); magaPlayButtons[j].tint = 0xFFFFFF; } updateContinueButton(); }; // Continue button handler continueButton.down = function () { if (selectedGender && playerName.length > 0) { // Save player data to storage storage.playerGender = selectedGender; storage.playerName = playerName; storage.hasPlayedAdventure = true; isFirstTimeAdventure = false; game.removeChild(setupContainer); startTutorialBattle(); } }; game.addChild(setupContainer); } function startCardSelectionPhase() { cardSelectionPhase = true; playerSelectedCard = null; enemySelectedCard = null; gameState = 'cardSelection'; // Reset card highlights for (var i = 0; i < playerHand.length; i++) { playerHand[i].tint = 0xFFFFFF; } // Show selection instructions - player selects first selectionInstructions.setText('Selecciona una carta para tu turno - Turno ' + turnCounter); selectionInstructions.alpha = 1; turnText.setText('Selección de Cartas - Turno ' + turnCounter); // Enemy automatically selects a card (hidden from player) var enemySelection = currentEnemy.selectCard(); if (enemySelection) { enemySelectedCard = enemySelection; } } function checkCardSelections() { if (playerSelectedCard && enemySelectedCard) { // Player has selected their card, now determine turn order and execute executeSelectedCards(); } } function executeSelectedCards() { cardSelectionPhase = false; selectionInstructions.alpha = 0; // Check for priority effects var playerHasPriority = playerSelectedCard.effect === 'priority'; var enemyHasPriority = enemySelectedCard.card.effect === 'priority'; var playerGoesFirst = false; if (playerHasPriority && !enemyHasPriority) { // Only player has priority playerGoesFirst = true; } else if (!playerHasPriority && enemyHasPriority) { // Only enemy has priority playerGoesFirst = false; } else if (playerHasPriority && enemyHasPriority) { // Both have priority - random decision playerGoesFirst = Math.random() < 0.5; } else { // Neither has priority - random decision playerGoesFirst = Math.random() < 0.5; } // Execute the selected cards in order if (playerGoesFirst) { gameState = 'playerTurn'; turnText.setText('Your Turn - Turn ' + turnCounter); // Check if selected card is still available if (playerHand.indexOf(playerSelectedCard) !== -1) { // Card is available, play it immediately playCard(playerSelectedCard); } else { // Selected card is no longer available, player must choose new card and attack immediately handleCardUnavailable(); } } else { gameState = 'enemyTurn'; turnText.setText('Enemy Turn - Turn ' + turnCounter); // Execute enemy's selected card first LK.setTimeout(function () { enemyPlayCard(enemySelectedCard.card, enemySelectedCard.index); }, 1000); } } function showGraduationAnimation() { // Create graduation container var gradContainer = new Container(); // Background var gradBg = LK.getAsset('background', { anchorX: 0, anchorY: 0, tint: 0x000050 }); gradContainer.addChild(gradBg); // Graduation text var gradText = new Text2('GRADUATION!', { size: 120, fill: 0xFFD700, font: "'Arial Black', Arial, sans-serif", stroke: 0xFFFFFF, strokeThickness: 6 }); gradText.anchor.set(0.5, 0.5); gradText.x = 1024; gradText.y = 1000; gradText.alpha = 0; gradText.scaleX = 0.1; gradText.scaleY = 0.1; gradContainer.addChild(gradText); // Congratulations text var congratsText = new Text2('¡Congratulations, Master Wizard!', { size: 60, fill: 0xFFFFFF, font: "'Arial Black', Arial, sans-serif" }); congratsText.anchor.set(0.5, 0.5); congratsText.x = 1024; congratsText.y = 1200; congratsText.alpha = 0; gradContainer.addChild(congratsText); // Academy building as graduation cap var academy = LK.getAsset('academy', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2, tint: 0xFFD700 }); academy.x = 1024; academy.y = 600; academy.alpha = 0; academy.rotation = -Math.PI / 4; gradContainer.addChild(academy); // Fireworks effects var fireworks = []; for (var i = 0; i < 8; i++) { var firework = LK.getAsset('academy', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3, tint: [0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFFD700, 0xFF4500][i] }); var angle = i / 8 * Math.PI * 2; firework.x = 1024 + Math.cos(angle) * 400; firework.y = 1366 + Math.sin(angle) * 400; firework.alpha = 0; firework.scaleX = 0.1; firework.scaleY = 0.1; gradContainer.addChild(firework); fireworks.push(firework); } game.addChild(gradContainer); // Animate academy tween(academy, { alpha: 1, rotation: 0 }, { duration: 1000, easing: tween.elasticOut }); // Animate main text LK.setTimeout(function () { tween(gradText, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 1200, easing: tween.bounceOut }); }, 500); // Animate congratulations text LK.setTimeout(function () { tween(congratsText, { alpha: 1 }, { duration: 800, easing: tween.easeOut }); }, 1500); // Animate fireworks LK.setTimeout(function () { for (var i = 0; i < fireworks.length; i++) { (function (firework, delay) { LK.setTimeout(function () { tween(firework, { alpha: 1, scaleX: 0.5, scaleY: 0.5, rotation: Math.PI * 4 }, { duration: 1000, easing: tween.easeOut }); }, delay); })(fireworks[i], i * 100); } }, 2000); // Continuous celebration effects function celebrationPulse() { tween(gradText, { scaleX: 1.1, scaleY: 1.1, tint: 0xFF69B4 }, { duration: 800, easing: tween.easeInOut, onFinish: function onFinish() { tween(gradText, { scaleX: 1, scaleY: 1, tint: 0xFFD700 }, { duration: 800, easing: tween.easeInOut, onFinish: celebrationPulse }); } }); } LK.setTimeout(celebrationPulse, 3000); // Remove animation after 8 seconds LK.setTimeout(function () { tween(gradContainer, { alpha: 0 }, { duration: 1000, onFinish: function onFinish() { game.removeChild(gradContainer); } }); }, 8000); } // Initialize game initializeDeck(); initializeEnemies(); createMenu(); // Play menu music with fade in LK.playMusic('Academia', { fade: { start: 0, end: 1, duration: 1000 } }); // Track last health state for music transitions var lastHealthState = 'normal'; // 'normal' or 'low' game.update = function () { // Game loop handled by turn-based system // Check for health-based music transitions during battle if (gameState === 'battle' || gameState === 'playerTurn' || gameState === 'enemyTurn') { var currentHealthState = playerHealth < 5 ? 'low' : 'normal'; // Only change music if health state changed if (currentHealthState !== lastHealthState) { if (currentHealthState === 'low') { // Switch to tense music when health drops below 5 LK.playMusic('Poca_vida', { fade: { start: 0, end: 1, duration: 500 } }); } else { // Switch back to normal battle music when health is 5 or above var normalMusic = 'Academia3'; // Default battle music if (gameMode === 'roguelike') { normalMusic = 'Roguelite'; } LK.playMusic(normalMusic, { fade: { start: 0, end: 1, duration: 500 } }); } lastHealthState = currentHealthState; } } }; function handleCardUnavailable() { // Show message that selected card is unavailable selectionInstructions.setText('Tu carta seleccionada ya no está disponible. Elige una nueva carta.'); selectionInstructions.alpha = 1; turnText.setText('Elige Nueva Carta - Turno ' + turnCounter); // Reset card selection state but stay in player turn cardSelectionPhase = true; playerSelectedCard = null; // Reset card highlights for (var i = 0; i < playerHand.length; i++) { playerHand[i].tint = 0xFFFFFF; } } function startPlaza() { gameState = 'plaza'; game.removeChild(menuContainer); createPlaza(); } function createPlaza() { var plazaContainer = new Container(); // Enhanced plaza background with more detail var plazaBg = LK.getAsset('Plaza_general', { anchorX: 0, anchorY: 0, scaleX: 20.48, scaleY: 27.32 }); plazaContainer.addChild(plazaBg); // Add decorative elements to make plaza feel more alive var fountain = LK.getAsset('academy', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1000, scaleX: 1.5, scaleY: 1.5, tint: 0x87CEEB }); plazaContainer.addChild(fountain); // Animated fountain effect function animateFountain() { tween(fountain, { scaleX: 1.6, scaleY: 1.6, alpha: 0.8 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(fountain, { scaleX: 1.5, scaleY: 1.5, alpha: 1 }, { duration: 2000, easing: tween.easeInOut, onFinish: animateFountain }); } }); } animateFountain(); // Zone portals - positioned around the plaza var zonePortals = []; // Battle Academy Portal (North) var battlePortal = LK.getAsset('academy', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 600, scaleX: 1.2, scaleY: 1.2, tint: 0xFF4500 }); plazaContainer.addChild(battlePortal); zonePortals.push({ portal: battlePortal, name: 'Academia de Batalla', type: 'battle' }); // Library Portal (East) var libraryPortal = LK.getAsset('Biblioteca', { anchorX: 0.5, anchorY: 0.5, x: 1600, y: 1200, scaleX: 8.0, scaleY: 8.0, tint: 0x8A2BE2 }); plazaContainer.addChild(libraryPortal); zonePortals.push({ portal: libraryPortal, name: 'Gran Biblioteca', type: 'library' }); // Arena Portal (West) var arenaPortal = LK.getAsset('Arena', { anchorX: 0.5, anchorY: 0.5, x: 450, y: 1200, scaleX: 0.8, scaleY: 0.8, tint: 0xFFD700 }); plazaContainer.addChild(arenaPortal); zonePortals.push({ portal: arenaPortal, name: 'Arena de Duelos', type: 'arena' }); // Dungeon Portal (South) var dungeonPortal = LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1800, scaleX: 0.8, scaleY: 0.8, tint: 0x2F4F4F }); plazaContainer.addChild(dungeonPortal); zonePortals.push({ portal: dungeonPortal, name: 'Mazmorras Profundas', type: 'dungeon' }); // Animate all portals with pulsing glow for (var i = 0; i < zonePortals.length; i++) { (function (portalData, delay) { function animatePortal() { tween(portalData.portal, { scaleX: portalData.portal.scaleX * 1.1, scaleY: portalData.portal.scaleY * 1.1, alpha: 0.7 }, { duration: 1500, easing: tween.easeInOut, onFinish: function onFinish() { tween(portalData.portal, { scaleX: portalData.portal.scaleX / 1.1, scaleY: portalData.portal.scaleY / 1.1, alpha: 1 }, { duration: 1500, easing: tween.easeInOut, onFinish: animatePortal }); } }); } LK.setTimeout(animatePortal, delay); })(zonePortals[i], i * 500); } // Determine which character sprite to use based on selection var selectedCharacterAsset = 'Mago'; // Default fallback if (storage.playerGender === 'mago') { selectedCharacterAsset = storage.selectedMagoAsset || 'Mago'; } else if (storage.playerGender === 'maga') { selectedCharacterAsset = storage.selectedMagaAsset || 'Maga'; } // Create player character in plaza with enhanced movement var playerCharacter = LK.getAsset(selectedCharacterAsset, { anchorX: 0.5, anchorY: 0.5, scaleX: 3.0, scaleY: 3.0 }); playerCharacter.x = 1024; playerCharacter.y = 1366; playerCharacter.lastX = playerCharacter.x; playerCharacter.lastY = playerCharacter.y; plazaContainer.addChild(playerCharacter); // Character movement variables with improved physics var characterSpeed = 12; var isDragging = false; var targetX = playerCharacter.x; var targetY = playerCharacter.y; var movementSmoothing = 0.15; // Enhanced plaza title with character greeting var plazaTitle = new Text2('Plaza Central - ¡Bienvenido ' + (storage.playerName || 'Aventurero') + '!', { size: 50, fill: 0xFFD700, stroke: 0x000000, strokeThickness: 3 }); plazaTitle.anchor.set(0.5, 0.5); plazaTitle.x = 1024; plazaTitle.y = 120; plazaContainer.addChild(plazaTitle); // Zone information display var zoneInfoText = new Text2('Explora los portales para acceder a diferentes zonas', { size: 35, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 2 }); zoneInfoText.anchor.set(0.5, 0.5); zoneInfoText.x = 1024; zoneInfoText.y = 2500; plazaContainer.addChild(zoneInfoText); // Current zone indicator (shows when near portals) var currentZoneIndicator = new Text2('', { size: 45, fill: 0x00FF00, stroke: 0x000000, strokeThickness: 3 }); currentZoneIndicator.anchor.set(0.5, 0.5); currentZoneIndicator.x = 1024; currentZoneIndicator.y = 250; currentZoneIndicator.alpha = 0; plazaContainer.addChild(currentZoneIndicator); // Battle access button (repositioned) var battleButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1850, y: 400, scaleX: 0.8, scaleY: 0.6 }); plazaContainer.addChild(battleButton); var battleText = new Text2('BATALLA', { size: 35, fill: 0xFFFFFF }); battleText.anchor.set(0.5, 0.5); battleText.x = 1850; battleText.y = 400; plazaContainer.addChild(battleText); battleButton.down = function () { currentBattle = completedCourses * 3; game.removeChild(plazaContainer); startBattle(); }; // Back to menu button (repositioned) var menuButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1850, y: 300, scaleX: 0.8, scaleY: 0.6 }); plazaContainer.addChild(menuButton); var menuText = new Text2('MENÚ', { size: 35, fill: 0xFFFFFF }); menuText.anchor.set(0.5, 0.5); menuText.x = 1850; menuText.y = 300; plazaContainer.addChild(menuText); menuButton.down = function () { gameState = 'menu'; game.removeChild(plazaContainer); createMenu(); LK.playMusic('Academia', { fade: { start: 0, end: 1, duration: 500 } }); }; // Enhanced movement controls with portal interaction plazaContainer.move = function (x, y, obj) { if (isDragging) { targetX = x; targetY = y; } }; plazaContainer.down = function (x, y, obj) { isDragging = true; targetX = x; targetY = y; }; plazaContainer.up = function (x, y, obj) { isDragging = false; }; // Enhanced character movement with portal detection function updateCharacterMovement() { // Store last position for direction detection playerCharacter.lastX = playerCharacter.x; playerCharacter.lastY = playerCharacter.y; // Smooth movement towards target var deltaX = targetX - playerCharacter.x; var deltaY = targetY - playerCharacter.y; var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY); if (distance > 5) { var moveX = deltaX * movementSmoothing; var moveY = deltaY * movementSmoothing; // Add movement speed limit var moveDistance = Math.sqrt(moveX * moveX + moveY * moveY); if (moveDistance > characterSpeed) { moveX = moveX / moveDistance * characterSpeed; moveY = moveY / moveDistance * characterSpeed; } playerCharacter.x += moveX; playerCharacter.y += moveY; // Character facing direction based on movement if (Math.abs(moveX) > 1) { if (moveX > 0) { playerCharacter.scaleX = Math.abs(playerCharacter.scaleX); } else { playerCharacter.scaleX = -Math.abs(playerCharacter.scaleX); } } } // Keep character within enhanced plaza bounds playerCharacter.x = Math.max(150, Math.min(1898, playerCharacter.x)); playerCharacter.y = Math.max(350, Math.min(2550, playerCharacter.y)); // Check proximity to portals and show zone information var nearPortal = null; var minDistance = Number.MAX_VALUE; for (var i = 0; i < zonePortals.length; i++) { var portal = zonePortals[i]; var portalDistance = Math.sqrt(Math.pow(playerCharacter.x - portal.portal.x, 2) + Math.pow(playerCharacter.y - portal.portal.y, 2)); if (portalDistance < 200 && portalDistance < minDistance) { minDistance = portalDistance; nearPortal = portal; } } // Update zone indicator based on proximity if (nearPortal) { var proximityText = 'Cerca de: ' + nearPortal.name; if (minDistance < 100) { proximityText += ' - ¡Toca para entrar!'; // Make portal glow brighter when very close tween(nearPortal.portal, { tint: 0xFFFFFF, scaleX: nearPortal.portal.scaleX * 1.2, scaleY: nearPortal.portal.scaleY * 1.2 }, { duration: 200 }); } else { // Reset portal appearance when not very close tween(nearPortal.portal, { tint: nearPortal.portal.tint, scaleX: nearPortal.portal.scaleX / 1.2, scaleY: nearPortal.portal.scaleY / 1.2 }, { duration: 200 }); } currentZoneIndicator.setText(proximityText); currentZoneIndicator.alpha = 1; } else { currentZoneIndicator.alpha = 0; } } // Portal interaction system function checkPortalInteraction() { for (var i = 0; i < zonePortals.length; i++) { var portal = zonePortals[i]; var distance = Math.sqrt(Math.pow(playerCharacter.x - portal.portal.x, 2) + Math.pow(playerCharacter.y - portal.portal.y, 2)); if (distance < 100) { // Create interaction for different portal types switch (portal.type) { case 'battle': // Flash portal and start battle tween(portal.portal, { tint: 0xFFFFFF, scaleX: portal.portal.scaleX * 1.5, scaleY: portal.portal.scaleY * 1.5 }, { duration: 300, onFinish: function onFinish() { currentBattle = completedCourses * 3; game.removeChild(plazaContainer); startBattle(); } }); break; case 'library': showZoneMessage('La Gran Biblioteca está en construcción...', 'Pronto podrás estudiar hechizos avanzados aquí'); break; case 'arena': showZoneMessage('La Arena de Duelos se abrirá pronto...', 'Aquí podrás enfrentarte a otros magos'); break; case 'dungeon': showZoneMessage('Las Mazmorras Profundas esperan...', 'Aventuras épicas te aguardan en el futuro'); break; } return true; } } return false; } // Add click detection for portals plazaContainer.down = function (x, y, obj) { // Check if click is near any portal var clickedPortal = false; for (var i = 0; i < zonePortals.length; i++) { var portal = zonePortals[i]; var distance = Math.sqrt(Math.pow(x - portal.portal.x, 2) + Math.pow(y - portal.portal.y, 2)); if (distance < 150) { // Move character towards portal and interact targetX = portal.portal.x; targetY = portal.portal.y; clickedPortal = true; break; } } if (!clickedPortal) { isDragging = true; targetX = x; targetY = y; } }; // Enhanced movement update loop with portal checking var movementInterval = LK.setInterval(function () { updateCharacterMovement(); // Check for portal interaction when character stops moving var deltaX = Math.abs(targetX - playerCharacter.x); var deltaY = Math.abs(targetY - playerCharacter.y); if (deltaX < 10 && deltaY < 10) { checkPortalInteraction(); } }, 16); // ~60fps game.addChild(plazaContainer); } // Helper function to show zone messages function showZoneMessage(title, description) { var messageContainer = new Container(); // Semi-transparent background var messageBg = LK.getAsset('background', { anchorX: 0, anchorY: 0, alpha: 0.9 }); messageContainer.addChild(messageBg); // Message box var messageBox = LK.getAsset('Cuadro_dialogo', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1366, scaleX: 8.0, scaleY: 5.0 }); messageContainer.addChild(messageBox); // Title text var titleText = new Text2(title, { size: 50, fill: 0x000000, stroke: 0xFFFFFF, strokeThickness: 2 }); titleText.anchor.set(0.5, 0.5); titleText.x = 1024; titleText.y = 1250; messageContainer.addChild(titleText); // Description text var descText = new Text2(description, { size: 40, fill: 0x000000 }); descText.anchor.set(0.5, 0.5); descText.x = 1024; descText.y = 1350; messageContainer.addChild(descText); // Close button var closeButton = LK.getAsset('button', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 1500, scaleX: 0.8, scaleY: 0.6 }); messageContainer.addChild(closeButton); var closeText = new Text2('CERRAR', { size: 35, fill: 0xFFFFFF }); closeText.anchor.set(0.5, 0.5); closeText.x = 1024; closeText.y = 1500; messageContainer.addChild(closeText); closeButton.down = function () { game.removeChild(messageContainer); }; // Auto-close after 5 seconds LK.setTimeout(function () { if (messageContainer.parent) { game.removeChild(messageContainer); } }, 5000); game.addChild(messageContainer); }
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highestLevel: 0,
completedCourses: 0,
currentCourse: 0,
currentBattleInCourse: 0,
playerMaxHealth: 10,
hasPlayedAdventure: false,
playerGender: "undefined",
playerName: "undefined",
selectedMagoAsset: "Mago",
selectedMagaAsset: "Maga"
});
/****
* Classes
****/
var Card = Container.expand(function (element, damage, name, effect) {
var self = Container.call(this);
self.element = element;
self.damage = damage;
self.name = name || element;
self.effect = effect || 'normal';
self.isUsed = false;
// Card border background (slightly larger than content)
var cardBorder = self.attachAsset('cardBase', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.05,
scaleY: 1.05,
tint: 0x444444
});
// Card main background
var cardBg = self.attachAsset('cardBase', {
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(cardBg);
// Use specific card name image if available, otherwise fallback to element graphic
var cardImageId = getCardImageId(self.name);
// TOP HALF: Card design image (scaled to fit top half of card)
var cardDesign = self.attachAsset(cardImageId, {
anchorX: 0.5,
anchorY: 0.5,
y: -105,
// Position in top half
scaleX: 0.93,
scaleY: cardImageId === 'agua_oxigenada' ? 0.32 : 0.5
});
// Get element color
var elementColor = getElementColor(element);
// BOTTOM HALF: Element color background (covers bottom half of card)
var elementColorBg = LK.getAsset('cardBase', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.93,
scaleY: 0.5,
tint: elementColor
});
elementColorBg.y = 105; // Position in bottom half
self.addChild(elementColorBg);
// Get mana cost for this card
self.manaCost = getCardManaCost(name, effect);
// Create damage/healing text in the format "x/+y", "x", or "+y"
var healAmount = getHealAmount(name, effect);
var damageDisplayText = '';
// Determine display format based on damage and healing
if (damage > 0 && healAmount > 0) {
// Both damage and healing: "x/+y"
damageDisplayText = damage.toString() + '/+' + healAmount.toString();
} else if (damage > 0 && healAmount === 0) {
// Only damage: "x"
damageDisplayText = damage.toString();
} else if (damage === 0 && healAmount > 0) {
// Only healing: "+y"
damageDisplayText = '+' + healAmount.toString();
} else {
// Neither damage nor healing (shouldn't happen, but fallback)
damageDisplayText = '0';
}
// Damage/healing text on the element color background
var damageText = new Text2(damageDisplayText, {
size: 48,
fill: 0xFFFFFF,
font: "'Arial Black', Arial, sans-serif"
});
damageText.anchor.set(0.5, 0.5);
damageText.y = 85; // Position slightly higher to make room for mana cost
// Add dark stroke for better contrast
damageText.stroke = 0x000000;
damageText.strokeThickness = 3;
self.addChild(damageText);
// Mana cost text on the element color background
var manaText = new Text2('⚡' + self.manaCost.toString(), {
size: 36,
fill: 0xFFD700,
font: "'Arial Black', Arial, sans-serif"
});
manaText.anchor.set(0.5, 0.5);
manaText.y = 125; // Position below damage text
manaText.stroke = 0x000000;
manaText.strokeThickness = 3;
self.addChild(manaText);
self.down = function (x, y, obj) {
// Handle card selection phase
if (cardSelectionPhase && !playerSelectedCard) {
if (playerMana >= self.manaCost) {
// Select this card for the turn
playerSelectedCard = self;
// Highlight selected card
tween(self, {
tint: 0x00FF00
}, {
duration: 300
});
// Check if both players have selected cards
checkCardSelections();
} else {
// Flash card red to indicate insufficient mana
tween(self, {
tint: 0xff0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 200
});
}
});
}
return;
}
if (!self.isUsed && gameState === 'playerTurn') {
if (playerMana >= self.manaCost) {
playCard(self);
} else {
// Flash card red to indicate insufficient mana
tween(self, {
tint: 0xff0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 200
});
}
});
}
}
};
return self;
});
var Enemy = Container.expand(function (name, health, difficulty, battleIndex) {
var self = Container.call(this);
self.name = name;
self.maxHealth = health;
self.health = health;
self.difficulty = difficulty;
self.battleIndex = battleIndex; // Store battle index for wizard assignment
self.isFrozen = false;
self.hasExtraAttack = false;
self.iceBlocked = false;
self.fogFailureChance = 0; // 0 = no fog, 10 = 10% failure chance
self.priorityBlocked = false; // Flag to indicate if enemy's priority is blocked
self.mana = 10;
self.maxMana = 10;
self.deck = createEnemyDeck(difficulty);
self.hand = [];
self.dialogBox = null; // Dialogue box container
self.dialogText = null; // Dialogue text element
// Use wizard sprite based on battle index for adventure mode
var wizardSpriteId = 'academy'; // Default fallback
if (gameMode === 'adventure' && typeof battleIndex !== 'undefined') {
// Sequential mapping: battleIndex 0-17 maps to wizard1-wizard18
// Course 1 (battles 0-2): wizard1, wizard2, wizard3
// Course 2 (battles 3-5): wizard4, wizard5, wizard6
// Course 3 (battles 6-8): wizard7, wizard8, wizard9
// Course 4 (battles 9-11): wizard10, wizard11, wizard12
// Course 5 (battles 12-14): wizard13, wizard14, wizard15
// Course 6 (battles 15-17): wizard16, wizard17, wizard18
var wizardNumber = battleIndex + 1; // Direct 1:1 mapping
wizardNumber = Math.max(1, Math.min(wizardNumber, 18)); // Ensure range 1-18
wizardSpriteId = 'wizard' + wizardNumber;
}
var enemyGraphic = self.attachAsset(wizardSpriteId, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
var nameText = new Text2(name, {
size: 50,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0.5);
nameText.y = -200;
self.addChild(nameText);
var healthText = new Text2('HP: ' + health, {
size: 40,
fill: 0xFFFFFF
});
healthText.anchor.set(0.5, 0.5);
healthText.y = 200;
self.addChild(healthText);
self.healthText = healthText;
self.drawHand = function () {
// Clear existing mazo de selección visuals
if (self.handCards) {
for (var j = 0; j < self.handCards.length; j++) {
if (self.handCards[j].parent) {
self.handCards[j].parent.removeChild(self.handCards[j]);
}
}
}
self.handCards = [];
self.hand = []; // Enemy's mazo de selección (3 cards to choose from)
var handCardNames = []; // Track card names to prevent duplicates in mazo de selección
for (var i = 0; i < 3; i++) {
if (self.deck.length > 0) {
var attempts = 0;
var foundValidCard = false;
while (attempts < 50 && !foundValidCard) {
// Limit attempts to prevent infinite loop
var randomIndex = Math.floor(Math.random() * self.deck.length);
var cardData = self.deck[randomIndex];
// Check if this card name is already in current mazo de selección
var isDuplicate = false;
for (var j = 0; j < handCardNames.length; j++) {
if (handCardNames[j] === (cardData.name || cardData.element)) {
isDuplicate = true;
break;
}
}
// Use card if it's not a duplicate in current mazo de selección
if (!isDuplicate) {
self.hand.push(cardData); // Add to enemy's mazo de selección
// Create visual card for enemy with same design as player cards
var enemyCard = new Card(cardData.element, cardData.damage, cardData.name || cardData.element, cardData.effect || 'normal');
enemyCard.x = -400 + i * 200;
enemyCard.y = -400;
enemyCard.scaleX = 0.6;
enemyCard.scaleY = 0.6;
self.addChild(enemyCard);
self.handCards.push(enemyCard);
handCardNames.push(cardData.name || cardData.element);
foundValidCard = true;
}
attempts++;
}
}
}
};
self.takeDamage = function (damage, element) {
// Check for enemy magma shields
if (self.magmaShields && self.magmaShields > 0 && damage > 0) {
if (element !== 'Water') {
// Normal attacks are reduced by shield count
var damageReduction = self.magmaShields;
var reducedDamage = Math.max(0, damage - damageReduction);
self.magmaShields -= 1; // Always lose 1 shield per attack
damage = reducedDamage;
} else {
// Water attacks consume 2 shields and are not reduced
var shieldsToRemove = Math.min(2, self.magmaShields);
self.magmaShields -= shieldsToRemove;
// Water damage is NOT reduced
}
}
self.health -= damage;
if (self.health < 0) self.health = 0;
self.healthText.setText('HP: ' + self.health);
tween(self, {
tint: 0xff0000
}, {
duration: 200,
onFinish: function onFinish() {
tween(self, {
tint: 0xffffff
}, {
duration: 200
});
}
});
};
self.removeCardFromHand = function (cardIndex) {
if (cardIndex >= 0 && cardIndex < self.hand.length) {
// Remove from hand array
self.hand.splice(cardIndex, 1);
// Remove visual card
if (self.handCards && self.handCards[cardIndex]) {
self.removeChild(self.handCards[cardIndex]);
self.handCards.splice(cardIndex, 1);
}
}
};
self.showDialogue = function (message, duration) {
// Remove existing dialogue if any
if (self.dialogBox) {
self.removeChild(self.dialogBox);
self.dialogBox = null;
self.dialogText = null;
}
// Create dialogue box container
self.dialogBox = new Container();
// Add cuadro_dialogo as background
var dialogBg = self.dialogBox.attachAsset('Cuadro_dialogo', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 6.0,
scaleY: 4.0
});
// Create dialogue text
self.dialogText = new Text2(message, {
size: 35,
fill: 0x000000,
font: "'Arial Black', Arial, sans-serif"
});
self.dialogText.anchor.set(0.5, 0.5);
self.dialogText.x = 0;
self.dialogText.y = 0;
self.dialogBox.addChild(self.dialogText);
// Position dialogue box above enemy
self.dialogBox.x = 0;
self.dialogBox.y = -300;
self.addChild(self.dialogBox);
// Auto-remove dialogue after duration (default 3 seconds)
var dialogDuration = duration || 3000;
LK.setTimeout(function () {
if (self.dialogBox) {
self.removeChild(self.dialogBox);
self.dialogBox = null;
self.dialogText = null;
}
}, dialogDuration);
};
self.selectCard = function () {
// Enemy selects a card for the turn
if (self.hand.length > 0) {
// Check if enemy can afford any card
var affordableCards = [];
for (var i = 0; i < self.hand.length; i++) {
var cardCost = getCardManaCost(self.hand[i].name || self.hand[i].element, self.hand[i].effect || 'normal');
if (self.mana >= cardCost) {
affordableCards.push({
card: self.hand[i],
index: i,
cost: cardCost
});
}
}
// If no affordable cards, pass turn
if (affordableCards.length === 0) {
return null;
}
var selectedCard = affordableCards[Math.floor(Math.random() * affordableCards.length)];
return selectedCard;
}
return null;
};
self.playTurn = function () {
// Show dialogue when enemy plays turn
var dialogues = ["¡Es mi turno!", "¡Prepárate!", "¡Vamos a ver qué puedes hacer!", "¡Mi magia es superior!", "¡No podrás conmigo!", "¡Atacaré ahora!", "¡Tu derrota está cerca!", "¡Usaré mi mejor hechizo!"];
var randomDialogue = dialogues[Math.floor(Math.random() * dialogues.length)];
self.showDialogue(randomDialogue, 2000);
if (self.isFrozen) {
self.isFrozen = false;
endEnemyTurn();
return;
}
// Check if priority is blocked by Esquirla Helada
if (self.priorityBlocked) {
// Priority blocked - enemy loses turn
self.priorityBlocked = false; // Reset flag
endEnemyTurn();
return;
}
// Check for fog failure
if (self.fogFailureChance > 0 && Math.random() < self.fogFailureChance / 100) {
// Enemy fails turn due to fog
endEnemyTurn();
return;
}
if (self.hand.length > 0) {
// Check if enemy can afford any card
var affordableCards = [];
for (var i = 0; i < self.hand.length; i++) {
var cardCost = getCardManaCost(self.hand[i].name || self.hand[i].element, self.hand[i].effect || 'normal');
if (self.mana >= cardCost) {
affordableCards.push({
card: self.hand[i],
index: i,
cost: cardCost
});
}
}
// If no affordable cards, pass turn to restore mana
if (affordableCards.length === 0) {
self.mana = self.maxMana;
endEnemyTurn();
return;
}
var selectedCard = affordableCards[Math.floor(Math.random() * affordableCards.length)];
var cardIndex = selectedCard.index;
var card = selectedCard.card;
// If ice is blocked, find a non-ice card
if (self.iceBlocked && card.element === 'Ice') {
var nonIceCards = [];
for (var i = 0; i < self.hand.length; i++) {
if (self.hand[i].element !== 'Ice') {
nonIceCards.push({
card: self.hand[i],
index: i
});
}
}
if (nonIceCards.length > 0) {
var randomNonIce = nonIceCards[Math.floor(Math.random() * nonIceCards.length)];
card = randomNonIce.card;
cardIndex = randomNonIce.index;
}
}
// Reset ice block after turn
self.iceBlocked = false;
enemyPlayCard(card, cardIndex);
// Check for extra attack from hombre de nieve effect
if (self.hasExtraAttack && self.hand.length > 0) {
self.hasExtraAttack = false;
// Play another card with damage 1
LK.setTimeout(function () {
if (self.hand.length > 0) {
var extraCardIndex = Math.floor(Math.random() * self.hand.length);
var extraCard = {
element: 'Ice',
damage: 1
};
enemyPlayCard(extraCard, extraCardIndex);
}
}, 500);
}
} else {
endEnemyTurn();
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xffffff
});
/****
* Game Code
****/
// Game state variables
// Fire card images
// Water card images
// Air card images
// Plant card images
// Rock card images
// Ice card images
var playerUsedCards = []; // Track cards used in current turn
var enemyUsedCards = []; // Track enemy cards used in current turn
var playerUnavailableCards = []; // Track cards that can't be drawn yet (with turn counters)
var playerShield = null; // Player's shield (Muralla Rocosa)
var playerShieldBar = null; // Shield health bar
var playerShieldText = null; // Shield health text
var playerMagmaShields = 0; // Player's magma shields count (0-3)
// Fire card images
// Water card images
// Air card images
// Plant card images
// Rock card images
// Ice card images
// Adventure mode wizard sprites with different colored hats
// Green hat
// Lime green hat
// Lawn green hat
// Green yellow hat
// Yellow hat
// Gold hat
// Orange hat
// Dark orange hat
// Coral hat
// Tomato hat
// Orange red hat
// Red hat
// Crimson hat
// Fire brick hat
// Dark red hat
// Maroon hat
// Golden hat (penultimate)
// Astral purple hat (final)
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
function _defineProperty(e, r, t) {
return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: !0,
configurable: !0,
writable: !0
}) : e[r] = t, e;
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == _typeof(i) ? i : i + "";
}
function _toPrimitive(t, r) {
if ("object" != _typeof(t) || !t) return t;
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != _typeof(i)) return i;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
var gameState = 'menu'; // 'menu', 'modeSelect', 'battle', 'victory', 'defeat', 'tutorial'
var gameMode = 'adventure'; // 'adventure', 'roguelike'
var roguelikeLevel = storage.highestLevel || 0;
var currentCourse = storage.currentCourse || 0;
var currentBattleInCourse = storage.currentBattleInCourse || 0;
var completedCourses = storage.completedCourses || 0;
var isFirstTimeAdventure = !storage.hasPlayedAdventure || !storage.playerName || storage.playerName === "undefined"; // Track if this is first time playing adventure mode or no name set
var tutorialStep = 0; // Track current tutorial step
var tutorialWizard = null; // Tutorial wizard character
var tutorialEnemy = null; // Special tutorial enemy
var tutorialTextBox = null; // Tutorial text display
var tutorialContainer = null; // Container for tutorial UI
var tutorialHighlight = null; // Highlight element for tutorial
var isTutorialActive = false; // Track if tutorial is currently running
// Card selection phase variables
var playerSelectedCard = null; // Card selected by player for the turn
var enemySelectedCard = null; // Card selected by enemy for the turn
var cardSelectionPhase = false; // Track if we're in card selection phase
var selectionInstructions = null; // Text instructions for card selection
// Helper function to map card names to image IDs
function getCardImageId(cardName) {
switch (cardName) {
// Fire cards
case 'Llamarada':
return 'llamarada';
case 'Fénix':
return 'fenix_invocation';
case 'Brasas':
return 'brasas';
case 'Llama':
return 'llama';
case 'Mechero':
return 'mechero';
case 'Escudos de Magma':
return 'llamarada';
// Use fire graphics for magma shields
// Water cards
case 'Gota de agua':
return 'gota_de_agua';
case 'Luna roja':
return 'luna_roja';
case 'Agua oxigenada':
return 'agua_oxigenada';
case 'Marea':
return 'marea';
case 'Pirata':
return 'pirata_invocation';
// Air cards
case 'Vendaval':
return 'vendaval';
case 'Brisa':
return 'brisa';
case 'Huracán':
return 'huracan';
case 'Niebla':
return 'niebla';
case 'Tormenta':
return 'tormenta';
case 'Oxígeno':
return 'Oxigeno';
// Plant cards
case 'Arboleda':
return 'arboleda';
case 'Serafín':
return 'serafin_invocation';
case 'Rama':
return 'rama';
case 'Vida extra':
return 'vida_extra';
case 'Aullidos del bosque':
return 'aullidos_del_bosque';
// Rock cards
case 'Pedrada':
return 'pedrada';
case 'Tormenta de arena':
return 'tormenta_de_arena';
case 'Sarcófago':
return 'sarcofago_invocation';
case 'Muralla Rocosa':
return 'muralla_rocosa';
case 'Terremoto':
return 'terremoto';
// Ice cards
case 'Nevada':
return 'nevada';
case 'Ventisca':
return 'ventisca';
case 'Bola de nieve':
return 'bola_de_nieve';
case 'Hombre de nieve':
return 'hombre_de_nieve_invocation';
case 'Esquirla Helada':
return 'esquirla_helada';
default:
return getElementCardId(cardName);
}
}
// Helper function to get fallback element card ID
function getElementCardId(element) {
switch (element) {
case 'Fire':
return 'fireCard';
case 'Water':
return 'waterCard';
case 'Air':
return 'airCard';
case 'Plant':
return 'plantCard';
case 'Rock':
return 'rockCard';
case 'Ice':
return 'iceCard';
default:
return 'cardBase';
}
}
// Helper function to get element colors
function getElementColor(element) {
switch (element) {
case 'Fire':
return 0xff4500;
case 'Water':
return 0x4169e1;
case 'Air':
return 0x87ceeb;
case 'Plant':
return 0x228b22;
case 'Rock':
return 0x696969;
case 'Ice':
return 0xb0e0e6;
default:
return 0xf5f5dc;
}
}
// Helper function to get healing amount for cards
function getHealAmount(cardName, effect) {
switch (cardName) {
case 'Fénix':
return 2;
case 'Agua oxigenada':
return 3;
case 'Serafín':
return 2;
case 'Sarcófago':
return 2;
case 'Vida extra':
return 1;
// Max health increase
case 'Oxígeno':
return 2;
default:
return 0;
}
}
// Helper function to get mana cost for cards based on their versatility
function getCardManaCost(cardName, effect) {
switch (cardName) {
// High cost cards (8-10 mana) - very versatile/powerful
case 'Muralla Rocosa':
// Shield with 4 health
return 7;
case 'Terremoto':
// Replaces all cards for both players
return 10;
case 'Huracán':
// Replaces enemy cards
return 8;
case 'Niebla':
// Damage + persistent failure chance
return 4;
case 'Tormenta':
// Damage + chance to skip enemy turn
return 6;
case 'Vida extra':
// Increases max health permanently
return 9;
case 'Aullidos del bosque':
// Damage + skip enemy turn
return 8;
case 'Ventisca':
// Damage + freeze chance
return 7;
// Medium-high cost cards (6-7 mana) - good versatility
case 'Fénix':
// Damage + heal
return 7;
case 'Llamarada':
// High damage + burn card
return 6;
case 'Pirata':
// Damage + steal card
return 6;
case 'Sarcófago':
// Damage + heal
return 6;
case 'Hombre de nieve':
// Damage + extra attack chance
return 6;
case 'Esquirla Helada':
// Priority damage (1 damage with priority)
return 3;
// Medium cost cards (4-5 mana) - moderate versatility
case 'Mechero':
// Damage + prevent ice
return 4;
case 'Escudos de Magma':
// Creates 3 magma shields that reduce damage
return 6;
case 'Nevada':
// Damage + freeze
return 5;
case 'Bola de nieve':
// Damage + freeze
return 4;
case 'Agua oxigenada':
// Pure healing
return 5;
case 'Serafín':
// Pure healing
return 4;
case 'Oxígeno':
// Air healing card
return 3;
// Low-medium cost cards (2-3 mana) - basic with small effects
case 'Brasas':
// Damage + burn
return 3;
case 'Llama':
// Damage + burn
return 3;
case 'Luna roja':
// Pure damage (2)
return 3;
case 'Vendaval':
// Pure damage (2)
return 3;
case 'Arboleda':
// Pure damage (2)
return 3;
case 'Pedrada':
// Pure damage (2)
return 3;
case 'Tormenta de arena':
// Pure damage (2)
return 3;
case 'Marea':
// Pure damage (2)
return 3;
// Low cost cards (1-2 mana) - basic effects
case 'Gota de agua':
// Pure damage (1)
return 1;
case 'Brisa':
// Pure damage (1)
return 2;
case 'Rama':
// Pure damage (1)
return 1;
// Default costs for generic elements
default:
if (effect === 'heal' || effect === 'damageAndHeal') {
return 5;
} else if (effect === 'replaceCards' || effect === 'replaceAllCards') {
return 8;
} else if (effect === 'skipEnemyTurn' || effect === 'steal') {
return 6;
} else if (effect === 'chooseBurn' || effect === 'preventIce') {
return 4;
} else {
// Basic damage cards
return 2;
}
}
}
// Helper function to play element-specific sounds
function playElementSound(element) {
var soundNumber = Math.floor(Math.random() * 5) + 1;
var soundId = element.toLowerCase() + soundNumber;
LK.getSound(soundId).play();
}
var currentBattle = 0;
var playerHealth = 10;
var playerMaxHealth = 10;
var playerMana = 10;
var playerMaxMana = 10;
var playerDeck = [];
var playerHand = [];
var currentEnemy = null;
var enemies = [];
var turnCounter = 0;
var playerStartsFirst = true; // Track who starts the current turn
// UI elements
var menuContainer = new Container();
var battleContainer = new Container();
var playerHealthBar = null;
var playerHealthText = null;
var playerManaBar = null;
var playerManaText = null;
var passButton = null;
var turnText = null;
var battleText = null;
// Initialize player deck
function initializeDeck() {
var elements = ['Fire', 'Water', 'Air', 'Plant', 'Rock', 'Ice'];
playerDeck = [];
for (var i = 0; i < elements.length; i++) {
if (elements[i] === 'Fire') {
// Add specific fire cards with names and effects
var fireCards = [{
element: 'Fire',
damage: 3,
name: 'Llamarada',
effect: 'chooseBurn'
}, {
element: 'Fire',
damage: 2,
name: 'Fénix',
effect: 'heal'
}, {
element: 'Fire',
damage: 2,
name: 'Brasas',
effect: 'normal'
}, {
element: 'Fire',
damage: 2,
name: 'Llama',
effect: 'normal'
}, {
element: 'Fire',
damage: 1,
name: 'Mechero',
effect: 'preventIce'
}, {
element: 'Fire',
damage: 0,
name: 'Escudos de Magma',
effect: 'magmaShields'
}];
for (var k = 0; k < fireCards.length; k++) {
playerDeck.push(fireCards[k]);
}
} else if (elements[i] === 'Air') {
// Add specific air cards with names and effects
var airCards = [{
element: 'Air',
damage: 2,
name: 'Vendaval',
effect: 'normal'
}, {
element: 'Air',
damage: 1,
name: 'Brisa',
effect: 'normal'
}, {
element: 'Air',
damage: 2,
name: 'Huracán',
effect: 'replaceCards'
}, {
element: 'Air',
damage: 3,
name: 'Tormenta',
effect: 'stormSkip'
}, {
element: 'Air',
damage: 1,
name: 'Niebla',
effect: 'fogFailure'
}, {
element: 'Air',
damage: 0,
name: 'Oxígeno',
effect: 'heal'
}];
for (var k = 0; k < airCards.length; k++) {
playerDeck.push(airCards[k]);
}
// Air deck now has 5 cards total (including Niebla)
} else if (elements[i] === 'Water') {
// Add specific water cards with names and effects
var waterCards = [{
element: 'Water',
damage: 1,
name: 'Gota de agua',
effect: 'normal'
}, {
element: 'Water',
damage: 2,
name: 'Luna roja',
effect: 'normal'
}, {
element: 'Water',
damage: 0,
name: 'Agua oxigenada',
effect: 'heal'
}, {
element: 'Water',
damage: 2,
name: 'Marea',
effect: 'normal'
}, {
element: 'Water',
damage: 1,
name: 'Pirata',
effect: 'steal'
}];
for (var k = 0; k < waterCards.length; k++) {
playerDeck.push(waterCards[k]);
}
} else if (elements[i] === 'Rock') {
// Add specific rock/earth cards with names and effects
var rockCards = [{
element: 'Rock',
damage: 2,
name: 'Pedrada',
effect: 'normal'
}, {
element: 'Rock',
damage: 2,
name: 'Tormenta de arena',
effect: 'normal'
}, {
element: 'Rock',
damage: 2,
name: 'Sarcófago',
effect: 'damageAndHeal'
}, {
element: 'Rock',
damage: 0,
name: 'Muralla Rocosa',
effect: 'shield'
}, {
element: 'Rock',
damage: 2,
name: 'Terremoto',
effect: 'replaceAllCards'
}];
for (var k = 0; k < rockCards.length; k++) {
playerDeck.push(rockCards[k]);
}
} else if (elements[i] === 'Plant') {
// Add specific plant cards with names and effects
var plantCards = [{
element: 'Plant',
damage: 2,
name: 'Arboleda',
effect: 'normal'
}, {
element: 'Plant',
damage: 0,
name: 'Serafín',
effect: 'heal'
}, {
element: 'Plant',
damage: 1,
name: 'Rama',
effect: 'normal'
}, {
element: 'Plant',
damage: 0,
name: 'Vida extra',
effect: 'increaseMaxHealth'
}, {
element: 'Plant',
damage: 2,
name: 'Aullidos del bosque',
effect: 'skipEnemyTurn'
}];
for (var k = 0; k < plantCards.length; k++) {
playerDeck.push(plantCards[k]);
}
} else if (elements[i] === 'Ice') {
// Add specific ice cards with names and effects
var iceCards = [{
element: 'Ice',
damage: 2,
name: 'Nevada',
effect: 'normal'
}, {
element: 'Ice',
damage: 2,
name: 'Ventisca',
effect: 'blizzard'
}, {
element: 'Ice',
damage: 1,
name: 'Bola de nieve',
effect: 'normal'
}, {
element: 'Ice',
damage: 2,
name: 'Hombre de nieve',
effect: 'snowmanAttack'
}, {
element: 'Ice',
damage: 1,
name: 'Esquirla Helada',
effect: 'priority'
}];
for (var k = 0; k < iceCards.length; k++) {
playerDeck.push(iceCards[k]);
}
} else {
for (var j = 0; j < 5; j++) {
var damage = 1;
playerDeck.push({
element: elements[i],
damage: damage
});
}
}
}
}
function createEnemyDeck(difficulty) {
var deck = [];
var elements = ['Fire', 'Water', 'Air', 'Plant', 'Rock', 'Ice'];
var cardCount = 15 + difficulty * 5;
for (var i = 0; i < cardCount; i++) {
var element = elements[Math.floor(Math.random() * elements.length)];
var damage = 1 + Math.floor(Math.random() * (difficulty + 1));
deck.push({
element: element,
damage: damage
});
}
return deck;
}
function initializeEnemies() {
if (gameMode === 'adventure') {
// Adventure mode: 6 courses with 3 battles each
enemies = [];
var courseNames = ['Apprentice', 'Novice', 'Scholar', 'Expert', 'Master', 'Grandmaster'];
var battleTitles = ['Guard', 'Defender', 'Champion'];
var battleIndex = 0;
for (var course = 0; course < 6; course++) {
for (var battle = 0; battle < 3; battle++) {
var health = 5 + course * 3 + battle * 2; // Increasing health per course and battle
var name = courseNames[course] + ' ' + battleTitles[battle];
enemies.push(new Enemy(name, health, course, battleIndex));
battleIndex++;
}
}
} else {
// Roguelike mode (original)
enemies = [new Enemy('Tutorial Apprentice', 5, 0), new Enemy('Novice Mage', 8, 1), new Enemy('Advanced Scholar', 12, 2), new Enemy('Master Wizard', 15, 3)];
}
}
function createMenu() {
menuContainer.removeChildren();
var bg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0
});
menuContainer.addChild(bg);
// Magical school background
var magicSchool = LK.getAsset('magicSchool', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 900
});
menuContainer.addChild(magicSchool);
// Add magical towers and buildings
var leftTower = LK.getAsset('academy', {
anchorX: 0.5,
anchorY: 1,
x: 400,
y: 1200,
scaleX: 0.8,
scaleY: 1.2
});
menuContainer.addChild(leftTower);
var rightTower = LK.getAsset('academy', {
anchorX: 0.5,
anchorY: 1,
x: 1648,
y: 1200,
scaleX: 0.8,
scaleY: 1.2
});
menuContainer.addChild(rightTower);
var mainBuilding = LK.getAsset('academy', {
anchorX: 0.5,
anchorY: 1,
x: 1024,
y: 1300,
scaleX: 1.5,
scaleY: 1.0
});
menuContainer.addChild(mainBuilding);
var titleText = new Text2('MAGICAL ACADEMY', {
size: 120,
fill: 0xFFD700,
font: "'Arial Black', Arial, sans-serif",
stroke: 0x4B0082,
strokeThickness: 25
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 200;
menuContainer.addChild(titleText);
// Add magical glow effect with pulsing 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() {
// Create infinite loop by calling the animation again
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
});
}
});
}
});
}
});
// Add magical color shifting effect
var _colorCycle = function colorCycle() {
tween(titleText, {
tint: 0xFF69B4
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleText, {
tint: 0x00FFFF
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(titleText, {
tint: 0xFFD700
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: _colorCycle
});
}
});
}
});
};
_colorCycle();
var adventureButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1450
});
menuContainer.addChild(adventureButton);
var adventureText = new Text2('Adventure Mode', {
size: 50,
fill: 0xFFFFFF
});
adventureText.anchor.set(0.5, 0.5);
adventureText.x = 1024;
adventureText.y = 1450;
menuContainer.addChild(adventureText);
adventureButton.down = function () {
LK.playMusic('Academia', {
fade: {
start: 1,
end: 0,
duration: 500
}
});
LK.setTimeout(function () {
LK.playMusic('Academia3', {
fade: {
start: 0,
end: 1,
duration: 500
}
});
}, 500);
gameMode = 'adventure';
// Check if this is first time playing adventure mode or no player data
if (isFirstTimeAdventure) {
// Show player setup interface for first-time players
showPlayerSetup();
} else {
// Start in the plaza with selected character
startPlaza();
}
};
var roguelikeButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1650
});
menuContainer.addChild(roguelikeButton);
var roguelikeText = new Text2('Roguelike Mode', {
size: 50,
fill: 0xFFFFFF
});
roguelikeText.anchor.set(0.5, 0.5);
roguelikeText.x = 1024;
roguelikeText.y = 1650;
menuContainer.addChild(roguelikeText);
roguelikeButton.down = function () {
LK.playMusic('Academia', {
fade: {
start: 1,
end: 0,
duration: 500
}
});
LK.setTimeout(function () {
LK.playMusic('Roguelite', {
fade: {
start: 0,
end: 1,
duration: 500
}
});
}, 500);
gameMode = 'roguelike';
startBattle();
};
if (roguelikeLevel > 0) {
var recordText = new Text2('Roguelike Record: Level ' + roguelikeLevel, {
size: 40,
fill: 0xFFD700
});
recordText.anchor.set(0.5, 0.5);
recordText.x = 1024;
recordText.y = 1850;
menuContainer.addChild(recordText);
}
if (completedCourses > 0) {
var adventureText = new Text2('Adventure Progress: ' + completedCourses + '/6 Courses Completed', {
size: 40,
fill: 0x00FF00
});
adventureText.anchor.set(0.5, 0.5);
adventureText.x = 1024;
adventureText.y = 1900;
menuContainer.addChild(adventureText);
}
// Fire Book Button
var fireBookButton = LK.getAsset('fireCard', {
anchorX: 0.5,
anchorY: 0.5,
x: 200,
y: 2100
});
menuContainer.addChild(fireBookButton);
var fireBookText = new Text2('Fire\nBook', {
size: 40,
fill: 0x000000,
font: "'Arial Black', Arial, sans-serif",
stroke: 0xffffff,
strokeThickness: 3
});
fireBookText.anchor.set(0.5, 0.5);
fireBookText.x = 200;
fireBookText.y = 2100;
menuContainer.addChild(fireBookText);
fireBookButton.down = function () {
LK.playMusic('Academia', {
fade: {
start: 1,
end: 0,
duration: 500
}
});
showElementBook('Fire');
};
// Water Book Button
var waterBookButton = LK.getAsset('waterCard', {
anchorX: 0.5,
anchorY: 0.5,
x: 400,
y: 2100
});
menuContainer.addChild(waterBookButton);
var waterBookText = new Text2('Water\nBook', {
size: 40,
fill: 0x000000,
font: "'Arial Black', Arial, sans-serif",
stroke: 0xffffff,
strokeThickness: 3
});
waterBookText.anchor.set(0.5, 0.5);
waterBookText.x = 400;
waterBookText.y = 2100;
menuContainer.addChild(waterBookText);
waterBookButton.down = function () {
LK.playMusic('Academia', {
fade: {
start: 1,
end: 0,
duration: 500
}
});
showElementBook('Water');
};
// Air Book Button
var airBookButton = LK.getAsset('airCard', {
anchorX: 0.5,
anchorY: 0.5,
x: 600,
y: 2100
});
menuContainer.addChild(airBookButton);
var airBookText = new Text2('Air\nBook', {
size: 40,
fill: 0x000000,
font: "'Arial Black', Arial, sans-serif",
stroke: 0xffffff,
strokeThickness: 3
});
airBookText.anchor.set(0.5, 0.5);
airBookText.x = 600;
airBookText.y = 2100;
menuContainer.addChild(airBookText);
airBookButton.down = function () {
LK.playMusic('Academia', {
fade: {
start: 1,
end: 0,
duration: 500
}
});
showElementBook('Air');
};
// Plant Book Button
var plantBookButton = LK.getAsset('plantCard', {
anchorX: 0.5,
anchorY: 0.5,
x: 800,
y: 2100
});
menuContainer.addChild(plantBookButton);
var plantBookText = new Text2('Plant\nBook', {
size: 40,
fill: 0x000000,
font: "'Arial Black', Arial, sans-serif",
stroke: 0xffffff,
strokeThickness: 3
});
plantBookText.anchor.set(0.5, 0.5);
plantBookText.x = 800;
plantBookText.y = 2100;
menuContainer.addChild(plantBookText);
plantBookButton.down = function () {
LK.playMusic('Academia', {
fade: {
start: 1,
end: 0,
duration: 500
}
});
showElementBook('Plant');
};
// Rock Book Button
var rockBookButton = LK.getAsset('rockCard', {
anchorX: 0.5,
anchorY: 0.5,
x: 1000,
y: 2100
});
menuContainer.addChild(rockBookButton);
var rockBookText = new Text2('Rock\nBook', {
size: 40,
fill: 0x000000,
font: "'Arial Black', Arial, sans-serif",
stroke: 0xffffff,
strokeThickness: 3
});
rockBookText.anchor.set(0.5, 0.5);
rockBookText.x = 1000;
rockBookText.y = 2100;
menuContainer.addChild(rockBookText);
rockBookButton.down = function () {
LK.playMusic('Academia', {
fade: {
start: 1,
end: 0,
duration: 500
}
});
showElementBook('Rock');
};
// Ice Book Button
var iceBookButton = LK.getAsset('iceCard', {
anchorX: 0.5,
anchorY: 0.5,
x: 1200,
y: 2100
});
menuContainer.addChild(iceBookButton);
var iceBookText = new Text2('Ice\nBook', {
size: 40,
fill: 0x000000,
font: "'Arial Black', Arial, sans-serif",
stroke: 0xffffff,
strokeThickness: 3
});
iceBookText.anchor.set(0.5, 0.5);
iceBookText.x = 1200;
iceBookText.y = 2100;
menuContainer.addChild(iceBookText);
iceBookButton.down = function () {
LK.playMusic('Academia', {
fade: {
start: 1,
end: 0,
duration: 500
}
});
showElementBook('Ice');
};
// Invocation Cards Book Button
var invocationBookButton = LK.getAsset('fenix_invocation_card', {
anchorX: 0.5,
anchorY: 0.5,
x: 1400,
y: 2100
});
menuContainer.addChild(invocationBookButton);
var invocationBookText = new Text2('Invocation\nBook', {
size: 35,
fill: 0x000000,
font: "'Arial Black', Arial, sans-serif",
stroke: 0xffffff,
strokeThickness: 3
});
invocationBookText.anchor.set(0.5, 0.5);
invocationBookText.x = 1400;
invocationBookText.y = 2100;
menuContainer.addChild(invocationBookText);
invocationBookButton.down = function () {
LK.playMusic('Academia', {
fade: {
start: 1,
end: 0,
duration: 500
}
});
showElementBook('Invocation');
};
// Reset Character Selection Button (positioned in bottom right corner)
var resetCharacterMenuButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1700,
y: 2300,
scaleX: 0.8,
scaleY: 0.6
});
menuContainer.addChild(resetCharacterMenuButton);
var resetCharacterMenuText = new Text2('RESET\nCHARACTER', {
size: 30,
fill: 0xFFFFFF
});
resetCharacterMenuText.anchor.set(0.5, 0.5);
resetCharacterMenuText.x = 1700;
resetCharacterMenuText.y = 2300;
menuContainer.addChild(resetCharacterMenuText);
resetCharacterMenuButton.down = function () {
// Reset all character selection data
storage.playerGender = "undefined";
storage.playerName = "undefined";
storage.selectedMagoAsset = "Mago";
storage.selectedMagaAsset = "Maga";
storage.hasPlayedAdventure = false;
storage.completedCourses = 0;
storage.currentCourse = 0;
storage.currentBattleInCourse = 0;
storage.playerMaxHealth = 10;
// Reset local variables
completedCourses = 0;
currentBattle = 0;
isFirstTimeAdventure = true;
// Show player setup interface
showPlayerSetup();
};
game.addChild(menuContainer);
}
function createBattle() {
battleContainer.removeChildren();
// Background
var bg;
if (gameMode === 'roguelike') {
bg = LK.getAsset('Mazmorra', {
anchorX: 0,
anchorY: 0,
scaleX: 1,
scaleY: 1
});
} else if (gameMode === 'adventure' && currentBattle >= 0 && currentBattle <= 2) {
// Use Clases1 for first three adventure levels
bg = LK.getAsset('Clases1', {
anchorX: 0,
anchorY: 0,
scaleX: 1,
scaleY: 1
});
} else if (gameMode === 'adventure' && currentBattle >= 3 && currentBattle <= 5) {
// Use Clases2 for course 2 (battles 3-5) with native resolution
bg = LK.getAsset('Clases2', {
anchorX: 0,
anchorY: 0,
scaleX: 1,
scaleY: 1
});
} else {
bg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0
});
}
battleContainer.addChild(bg);
// Player health
playerHealthBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0,
x: 100,
y: 100
});
battleContainer.addChild(playerHealthBar);
playerHealthText = new Text2('HP: ' + playerHealth + '/' + playerMaxHealth, {
size: 30,
fill: 0xFFFFFF
});
playerHealthText.anchor.set(0, 0.5);
playerHealthText.x = 420;
playerHealthText.y = 120;
battleContainer.addChild(playerHealthText);
// Player mana bar
playerManaBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0,
x: 100,
y: 200,
tint: 0x0066FF
});
battleContainer.addChild(playerManaBar);
playerManaText = new Text2('⚡: ' + playerMana + '/' + playerMaxMana, {
size: 30,
fill: 0xFFFFFF
});
playerManaText.anchor.set(0, 0.5);
playerManaText.x = 420;
playerManaText.y = 220;
battleContainer.addChild(playerManaText);
// Player shield bar (initially hidden)
playerShieldBar = LK.getAsset('healthBar', {
anchorX: 0,
anchorY: 0,
x: 100,
y: 300,
tint: 0x696969,
alpha: 0
});
battleContainer.addChild(playerShieldBar);
playerShieldText = new Text2('🛡: 0/0', {
size: 30,
fill: 0xFFFFFF
});
playerShieldText.anchor.set(0, 0.5);
playerShieldText.x = 420;
playerShieldText.y = 320;
playerShieldText.alpha = 0;
battleContainer.addChild(playerShieldText);
// Pass turn button
passButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1750,
y: 350,
scaleX: 0.6,
scaleY: 0.6
});
battleContainer.addChild(passButton);
var passText = new Text2('Pass\n(+10 ⚡)', {
size: 25,
fill: 0xFFFFFF
});
passText.anchor.set(0.5, 0.5);
passText.x = 1750;
passText.y = 350;
battleContainer.addChild(passText);
passButton.down = function () {
if (gameState === 'playerTurn') {
// Restore full mana
playerMana = playerMaxMana;
updatePlayerMana();
// Handle tutorial progression for pass turn
if (isTutorialActive && tutorialStep === 8) {
tutorialStep = 9;
showTutorialMessage("¡Perfecto! Tu maná se restauró completamente. Ahora el final...", function () {
showTutorialMessage("Una última cosa: no siempre vas a atacar primero, así que ten cuidado.", function () {
endTutorial();
});
});
return;
}
endPlayerTurn();
}
};
// Exit button to return to menu
var exitButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1750,
y: 150,
scaleX: 0.6,
scaleY: 0.6
});
battleContainer.addChild(exitButton);
var exitText = new Text2('Exit', {
size: 30,
fill: 0xFFFFFF
});
exitText.anchor.set(0.5, 0.5);
exitText.x = 1750;
exitText.y = 150;
battleContainer.addChild(exitText);
exitButton.down = function () {
gameState = 'menu';
game.removeChild(battleContainer);
createMenu();
LK.playMusic('Academia', {
fade: {
start: 0,
end: 1,
duration: 500
}
});
};
// Reset button for adventure mode
if (gameMode === 'adventure') {
var resetButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1750,
y: 250,
scaleX: 0.6,
scaleY: 0.6
});
battleContainer.addChild(resetButton);
var resetText = new Text2('Reset', {
size: 25,
fill: 0xFFFFFF
});
resetText.anchor.set(0.5, 0.5);
resetText.x = 1750;
resetText.y = 250;
battleContainer.addChild(resetText);
resetButton.down = function () {
// Reset adventure progress
completedCourses = 0;
currentBattle = 0;
storage.completedCourses = 0;
storage.currentCourse = 0;
storage.currentBattleInCourse = 0;
// Reset player health
playerHealth = 10;
playerMaxHealth = 10;
// Restart the first battle
gameState = 'menu';
game.removeChild(battleContainer);
createMenu();
LK.playMusic('Academia', {
fade: {
start: 0,
end: 1,
duration: 500
}
});
};
}
// Course and battle indicator for adventure mode
if (gameMode === 'adventure') {
var courseNumber = Math.floor(currentBattle / 3) + 1;
var battleInCourse = currentBattle % 3 + 1;
var courseText = new Text2('Course ' + courseNumber + ' - Battle ' + battleInCourse, {
size: 35,
fill: 0xFFD700
});
courseText.anchor.set(0.5, 0.5);
courseText.x = 1024;
courseText.y = 150;
battleContainer.addChild(courseText);
}
// Turn indicator
turnText = new Text2('Your Turn - Turn 1', {
size: 40,
fill: 0xFFFFFF
});
turnText.anchor.set(0.5, 0.5);
turnText.x = 1024;
turnText.y = 200;
battleContainer.addChild(turnText);
// Card selection instructions (initially hidden)
selectionInstructions = new Text2('', {
size: 35,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 2
});
selectionInstructions.anchor.set(0.5, 0.5);
selectionInstructions.x = 1024;
selectionInstructions.y = 250;
selectionInstructions.alpha = 0;
battleContainer.addChild(selectionInstructions);
// Battle text (removed course/battle display for adventure mode)
if (gameMode === 'roguelike') {
battleText = new Text2('Battle ' + (currentBattle + 1), {
size: 60,
fill: 0xFFFFFF
});
battleText.anchor.set(0.5, 0.5);
battleText.x = 1024;
battleText.y = 300;
battleContainer.addChild(battleText);
}
// Player character display (bottom of screen) - show only selected backsprite
var playerCharacterAsset = 'wizard1'; // Default fallback
if (storage.playerGender === 'mago') {
// Use the selected mago asset directly
playerCharacterAsset = storage.selectedMagoAsset || 'Mago';
} else if (storage.playerGender === 'maga') {
// Use the selected maga asset directly
playerCharacterAsset = storage.selectedMagaAsset || 'Maga';
}
var playerCharacter = LK.getAsset(playerCharacterAsset, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
playerCharacter.x = 1024;
playerCharacter.y = 2200; // Position at bottom of screen
battleContainer.addChild(playerCharacter);
// Enemy
currentEnemy = enemies[currentBattle];
currentEnemy.x = 1024;
currentEnemy.y = 800;
battleContainer.addChild(currentEnemy);
// Add deck pile (face down)
var deckPile = LK.getAsset('deckPile', {
anchorX: 0.5,
anchorY: 0.5,
x: 1750,
y: 1800
});
battleContainer.addChild(deckPile);
var deckText = new Text2('Deck', {
size: 30,
fill: 0xFFFFFF
});
deckText.anchor.set(0.5, 0.5);
deckText.x = 1750;
deckText.y = 1950;
battleContainer.addChild(deckText);
game.addChild(battleContainer);
updatePlayerMana();
drawPlayerHand();
currentEnemy.drawHand();
}
function drawPlayerHand() {
// Remove existing selection deck (mazo de selección)
for (var i = battleContainer.children.length - 1; i >= 0; i--) {
var child = battleContainer.children[i];
if (child instanceof Card) {
battleContainer.removeChild(child);
}
}
playerHand = []; // playerHand represents the current mazo de selección (3 cards to choose from)
var handCardNames = []; // Track card names to prevent duplicates in selection deck
for (var i = 0; i < 3; i++) {
if (playerDeck.length > 0) {
var attempts = 0;
var foundValidCard = false;
while (attempts < 50 && !foundValidCard) {
// Limit attempts to prevent infinite loop
var randomIndex = Math.floor(Math.random() * playerDeck.length);
var cardData = playerDeck[randomIndex];
// Check if this card name is already in current mazo de selección
var isDuplicate = false;
for (var j = 0; j < handCardNames.length; j++) {
if (handCardNames[j] === cardData.name) {
isDuplicate = true;
break;
}
}
// Use card if it's not a duplicate in current mazo de selección
if (!isDuplicate) {
var card = new Card(cardData.element, cardData.damage, cardData.name, cardData.effect);
card.x = 400 + i * 350;
card.y = 1800;
playerHand.push(card); // Add to current mazo de selección
battleContainer.addChild(card);
handCardNames.push(cardData.name);
foundValidCard = true;
}
attempts++;
}
}
}
}
function playCard(card) {
if (gameState !== 'playerTurn') return;
if (playerMana < card.manaCost) return;
card.isUsed = true;
// Consume mana
playerMana -= card.manaCost;
updatePlayerMana();
LK.getSound('cardPlay').play();
playElementSound(card.element);
// Apply card effects
var damage = card.damage;
var element = card.element;
// Check for priority effect
if (card.effect === 'priority') {
// Priority card - block enemy's next turn priority
currentEnemy.priorityBlocked = true;
}
// Fire element invocation animation
if (element === 'Fire') {
// Create the invoked asset that will fly to the enemy
var invokedAsset = LK.getAsset(getCardImageId(card.name), {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.9
});
// Start at card position
invokedAsset.x = card.x;
invokedAsset.y = card.y;
battleContainer.addChild(invokedAsset);
// Animate the asset flying to the enemy
tween(invokedAsset, {
x: currentEnemy.x,
y: currentEnemy.y,
scaleX: 1.2,
scaleY: 1.2,
rotation: Math.PI * 2
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Impact effect - flash the asset and make it bigger momentarily
tween(invokedAsset, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0.3,
tint: 0xFFFFFF
}, {
duration: 200,
onFinish: function onFinish() {
// Remove the invoked asset after impact
battleContainer.removeChild(invokedAsset);
// Apply damage and effects after animation
applyFireCardEffects(card, damage);
}
});
}
});
} else {
// Non-fire cards apply effects immediately
applyCardEffects(card, damage, element);
}
}
// Helper function to apply fire card effects after animation
function applyFireCardEffects(card, damage) {
switch (card.effect) {
case 'chooseBurn':
// Llamarada - deal 3 damage and let player choose card to burn
if (currentEnemy.hand.length > 0) {
var burnIndex = Math.floor(Math.random() * currentEnemy.hand.length);
currentEnemy.removeCardFromHand(burnIndex);
}
break;
case 'heal':
// Fénix - deal 2 damage and heal 2 HP
playerHealth = Math.min(playerHealth + 2, playerMaxHealth);
updatePlayerHealth();
break;
case 'preventIce':
// Mechero - deal 1 damage and prevent enemy from using ice cards next turn
currentEnemy.iceBlocked = true;
break;
case 'magmaShields':
// Escudos de Magma - create 3 magma shields
playerMagmaShields = 3;
break;
default:
// Brasas and Llama - deal 2 damage normally
if (currentEnemy.hand.length > 0) {
var burnIndex = Math.floor(Math.random() * currentEnemy.hand.length);
currentEnemy.removeCardFromHand(burnIndex);
}
break;
}
// Apply damage and continue with battle logic
currentEnemy.takeDamage(damage, 'Fire');
LK.getSound('damage').play();
finishCardPlay(card);
}
// Helper function to apply non-fire card effects immediately
function applyCardEffects(card, damage, element) {
// Check if this is an entity card that needs invocation animation
var entityCards = ['Fénix', 'Serafín', 'Aullidos del bosque', 'Pirata', 'Sarcófago', 'Hombre de nieve', 'Bola de nieve', 'Pedrada', 'Momia'];
if (entityCards.indexOf(card.name) !== -1) {
invokeEntityCard(card, damage, element);
return;
}
switch (element) {
case 'Water':
// Handle special water card effects
if (card.effect === 'heal') {
// Agua oxigenada - heal 3 HP (no damage)
playerHealth = Math.min(playerHealth + 3, playerMaxHealth);
updatePlayerHealth();
damage = 0; // No damage dealt
} else if (card.effect === 'steal') {
// Pirata - let player choose which card to steal
if (currentEnemy.hand.length > 0 && playerHand.length < 3) {
showCardStealInterface();
return; // Don't continue with normal card play flow yet
}
}
break;
case 'Plant':
// Handle special plant card effects
if (card.effect === 'increaseMaxHealth') {
// Vida extra - increase max health by 1
playerMaxHealth += 1;
updatePlayerHealth();
damage = 0; // No damage dealt
}
// Standard damage for arboleda and rama
break;
case 'Air':
// Handle special air card effects
if (card.effect === 'replaceCards') {
// Huracán - deal 2 damage and replace enemy's 3 cards with random ones from their deck
currentEnemy.drawHand();
} else if (card.effect === 'stormSkip') {
// Tormenta - deal 3 damage and 10% chance enemy can't attack next turn
if (Math.random() < 0.1) {
currentEnemy.isFrozen = true;
}
} else if (card.effect === 'fogFailure') {
// Niebla - deal 1 damage and set persistent 10% failure chance
if (currentEnemy.fogFailureChance === 0) {
currentEnemy.fogFailureChance = 10; // Set 10% failure chance for rest of battle
}
} else {
// Vendaval and Brisa - standard damage without replacing cards
}
break;
case 'Rock':
// Handle special rock card effects
if (card.effect === 'shield') {
// Muralla Rocosa - create shield with 4 health
if (!playerShield) {
playerShield = {
health: 4,
maxHealth: 4
};
updateShieldDisplay();
damage = 0; // No damage dealt
}
} else if (card.effect === 'replaceAllCards') {
// Terremoto - deal 2 damage to enemy and both players get new cards
currentEnemy.drawHand();
drawPlayerHand();
}
// Standard damage for all rock cards
break;
case 'Ice':
// Handle special ice card effects
if (card.effect === 'blizzard') {
// Ventisca - deal 2 damage and 10% chance enemy can't attack next turn
if (Math.random() < 0.1) {
currentEnemy.isFrozen = true;
}
} else {
// Nevada and Bola de nieve - freeze enemy for next turn + damage
currentEnemy.isFrozen = true;
}
break;
}
// Apply damage and finish card play
currentEnemy.takeDamage(damage, element);
LK.getSound('damage').play();
finishCardPlay(card);
}
// Helper function to invoke entity cards with special animations
function invokeEntityCard(card, damage, element) {
var centerX = 1024;
var centerY = 1366;
switch (card.name) {
case 'Fénix':
// Phoenix rises from ashes in center and attacks with fire
var ashes = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.2,
tint: 0x404040,
alpha: 0.8
});
ashes.x = centerX;
ashes.y = centerY;
battleContainer.addChild(ashes);
var phoenix = LK.getAsset('Fenix', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
phoenix.x = centerX;
phoenix.y = centerY;
battleContainer.addChild(phoenix);
// Phoenix emerges from ashes
tween(phoenix, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 1
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Phoenix breathes fire at enemy
tween(phoenix, {
rotation: -0.3,
scaleX: 1.4,
scaleY: 1.4
}, {
duration: 300,
onFinish: function onFinish() {
// Fire effect towards enemy
var fireBreath = LK.getAsset('llamarada', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
alpha: 0.9
});
fireBreath.x = centerX;
fireBreath.y = centerY;
battleContainer.addChild(fireBreath);
tween(fireBreath, {
x: currentEnemy.x,
y: currentEnemy.y,
scaleX: 1.5,
scaleY: 1.5,
rotation: Math.PI
}, {
duration: 500,
onFinish: function onFinish() {
// White healing aura for player
var healingAura = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0,
tint: 0xFFFFFF,
alpha: 0.3
});
healingAura.x = 1024;
healingAura.y = 1800;
battleContainer.addChild(healingAura);
tween(healingAura, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
battleContainer.removeChild(healingAura);
}
});
// Apply effects
currentEnemy.takeDamage(damage, 'Fire');
playerHealth = Math.min(playerHealth + 2, playerMaxHealth);
updatePlayerHealth();
LK.getSound('damage').play();
// Clean up
battleContainer.removeChild(fireBreath);
battleContainer.removeChild(phoenix);
battleContainer.removeChild(ashes);
finishCardPlay(card);
}
});
}
});
}
});
break;
case 'Serafín':
// Seraph appears facing player and heals with green aura
var seraph = LK.getAsset('serafin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
seraph.x = centerX;
seraph.y = centerY;
battleContainer.addChild(seraph);
tween(seraph, {
scaleX: 1.3,
scaleY: 1.3,
alpha: 1
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
// Green healing aura towards player
var greenAura = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0,
tint: 0x00FF00,
alpha: 0.6
});
greenAura.x = 1024;
greenAura.y = 1800;
battleContainer.addChild(greenAura);
tween(greenAura, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
battleContainer.removeChild(greenAura);
}
});
// Apply healing
playerHealth = Math.min(playerHealth + 2, playerMaxHealth);
updatePlayerHealth();
LK.getSound('Curar').play();
// Clean up
LK.setTimeout(function () {
battleContainer.removeChild(seraph);
finishCardPlay(card);
}, 800);
}
});
break;
case 'Aullidos del bosque':
// Environment becomes forest with wolf howls and two wolves attack
var forestBg = LK.getAsset('arboleda', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 7.0,
scaleY: 4.0,
alpha: 0
});
forestBg.x = centerX;
forestBg.y = centerY;
battleContainer.addChild(forestBg);
tween(forestBg, {
alpha: 0.7
}, {
duration: 500,
onFinish: function onFinish() {
// Two wolves appear and attack enemy (using Lobos asset without background)
var wolf1 = LK.getAsset('Lobos', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0
});
wolf1.x = centerX - 200;
wolf1.y = centerY + 100;
battleContainer.addChild(wolf1);
var wolf2 = LK.getAsset('Lobos', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0
});
wolf2.x = centerX + 200;
wolf2.y = centerY + 100;
battleContainer.addChild(wolf2);
// Wolves appear and leap at enemy
tween(wolf1, {
alpha: 1
}, {
duration: 300
});
tween(wolf2, {
alpha: 1
}, {
duration: 300
});
LK.setTimeout(function () {
tween(wolf1, {
x: currentEnemy.x - 100,
y: currentEnemy.y,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 600
});
tween(wolf2, {
x: currentEnemy.x + 100,
y: currentEnemy.y,
scaleX: -1.2,
scaleY: 1.2
}, {
duration: 600,
onFinish: function onFinish() {
// Apply effects
currentEnemy.takeDamage(damage);
currentEnemy.isFrozen = true;
LK.getSound('Aullidos').play();
LK.getSound('Aullidos').play(null, 1.5);
// Clean up
LK.setTimeout(function () {
tween(forestBg, {
alpha: 0
}, {
duration: 500
});
battleContainer.removeChild(wolf1);
battleContainer.removeChild(wolf2);
LK.setTimeout(function () {
battleContainer.removeChild(forestBg);
finishCardPlay(card);
}, 500);
}, 800);
}
});
}, 500);
}
});
break;
case 'Pedrada':
// Rock throw appears in center and flies to enemy (reusing snowball animation)
var rock = LK.getAsset('pedrada', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
rock.x = centerX;
rock.y = centerY;
battleContainer.addChild(rock);
tween(rock, {
x: currentEnemy.x,
y: currentEnemy.y,
scaleX: 1.2,
scaleY: 1.2,
rotation: Math.PI * 3
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
// Apply effects
currentEnemy.takeDamage(damage, 'Rock');
LK.getSound('damage').play();
// Impact effect
tween(rock, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
battleContainer.removeChild(rock);
finishCardPlay(card);
}
});
}
});
break;
case 'Pirata':
// Pirate appears and strikes enemy with hook, then offers card steal choice
var pirate = LK.getAsset('Pirata', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
pirate.x = centerX;
pirate.y = centerY;
battleContainer.addChild(pirate);
tween(pirate, {
scaleX: 1.4,
scaleY: 1.4,
alpha: 1
}, {
duration: 500,
easing: tween.bounceOut,
onFinish: function onFinish() {
// Pirate swings hook at enemy
tween(pirate, {
x: currentEnemy.x - 150,
y: currentEnemy.y,
rotation: -0.5
}, {
duration: 400,
onFinish: function onFinish() {
// Apply effects
currentEnemy.takeDamage(damage);
LK.getSound('damage').play();
// Card steal effect - show interface for player to choose
if (currentEnemy.hand.length > 0 && playerHand.length < 3) {
LK.setTimeout(function () {
showCardStealInterface();
}, 800); // Delay to let pirate animation finish
}
// Clean up
LK.setTimeout(function () {
battleContainer.removeChild(pirate);
finishCardPlay(card);
}, 600);
}
});
}
});
break;
case 'Sarcófago':
// Create sand pile (Arena asset) in center of battle
var sandPile = LK.getAsset('Arena', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.8
});
sandPile.x = centerX;
sandPile.y = centerY + 150;
battleContainer.addChild(sandPile);
// Sarcophagus starts buried under sand
var sarcophagus = LK.getAsset('sarcofago', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
sarcophagus.x = centerX;
sarcophagus.y = centerY + 150;
battleContainer.addChild(sarcophagus);
// Sarcophagus emerges from sand pile
tween(sarcophagus, {
scaleX: 1.2,
scaleY: 1.2,
alpha: 1,
y: centerY - 50
}, {
duration: 1000,
easing: tween.easeOut,
onFinish: function onFinish() {
// Sarcophagus opens with dramatic effect
tween(sarcophagus, {
scaleX: 1.8,
scaleY: 1.1
}, {
duration: 500,
easing: tween.bounceOut,
onFinish: function onFinish() {
// Mummy emerges from the sarcophagus and appears on top of Arena
var mummy = LK.getAsset('Momia', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
mummy.x = centerX;
mummy.y = centerY + 100; // Position on top of Arena asset
battleContainer.addChild(mummy);
// Mummy appears with growing animation
tween(mummy, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
// Mummy attacks enemy with right hand (slight rotation and movement)
tween(mummy, {
rotation: -0.3,
x: centerX + 50
}, {
duration: 300,
onFinish: function onFinish() {
// Apply damage from mummy's right hand attack
currentEnemy.takeDamage(damage);
LK.getSound('damage').play();
// Mummy extends left hand with healing bandages toward player
tween(mummy, {
rotation: 0.3,
x: centerX - 50
}, {
duration: 400,
onFinish: function onFinish() {
// Healing bandages fly from mummy's left hand to player
var healingBandages = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 1.5,
tint: 0xF5DEB3,
alpha: 0.9
});
healingBandages.x = centerX - 50;
healingBandages.y = centerY - 50;
battleContainer.addChild(healingBandages);
// Animate bandages wrapping around player
tween(healingBandages, {
x: 1024,
y: 1800,
scaleX: 1.5,
scaleY: 2.5,
rotation: Math.PI * 2
}, {
duration: 800,
easing: tween.easeOut,
onFinish: function onFinish() {
// Create green healing aura around player
var greenAura = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.0,
scaleY: 1.0,
tint: 0x00FF00,
alpha: 0.7
});
greenAura.x = 1024;
greenAura.y = 1800;
battleContainer.addChild(greenAura);
// Green aura pulsing effect
tween(greenAura, {
scaleX: 3.0,
scaleY: 3.0,
alpha: 0
}, {
duration: 1200,
easing: tween.easeOut,
onFinish: function onFinish() {
battleContainer.removeChild(greenAura);
}
});
// Apply healing
playerHealth = Math.min(playerHealth + 2, playerMaxHealth);
updatePlayerHealth();
battleContainer.removeChild(healingBandages);
}
});
// Clean up after complete animation
LK.setTimeout(function () {
battleContainer.removeChild(mummy);
battleContainer.removeChild(sarcophagus);
battleContainer.removeChild(sandPile);
finishCardPlay(card);
}, 1500);
}
});
}
});
}
});
}
});
}
});
break;
case 'Momia':
// Mummy appears on top of Arena asset in battle center
var arenaBase = LK.getAsset('Arena', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.8
});
arenaBase.x = centerX;
arenaBase.y = centerY;
battleContainer.addChild(arenaBase);
var mummy = LK.getAsset('Momia', {
anchorX: 0.5,
anchorY: 1.0,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
mummy.x = centerX;
mummy.y = centerY;
battleContainer.addChild(mummy);
// Mummy appears with growing animation on Arena
tween(mummy, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
// Apply effects
currentEnemy.takeDamage(damage);
LK.getSound('damage').play();
// Clean up
LK.setTimeout(function () {
battleContainer.removeChild(mummy);
battleContainer.removeChild(arenaBase);
finishCardPlay(card);
}, 800);
}
});
break;
case 'Hombre de nieve':
// Snowman appears and throws snowball at enemy
var snowman = LK.getAsset('hombre_de_nieve', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.1,
scaleY: 0.1,
alpha: 0
});
snowman.x = centerX;
snowman.y = centerY;
battleContainer.addChild(snowman);
tween(snowman, {
scaleX: 1.3,
scaleY: 1.3,
alpha: 1
}, {
duration: 500,
easing: tween.bounceOut,
onFinish: function onFinish() {
// Snowman throws snowball
var snowball = LK.getAsset('bola_de_nieve', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.6,
scaleY: 0.6
});
snowball.x = centerX;
snowball.y = centerY;
battleContainer.addChild(snowball);
tween(snowball, {
x: currentEnemy.x,
y: currentEnemy.y,
scaleX: 1.0,
scaleY: 1.0,
rotation: Math.PI * 3
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
// Apply effects
currentEnemy.takeDamage(damage);
currentEnemy.hasExtraAttack = true;
LK.getSound('Bola_de_nieve').play();
// Impact effect
tween(snowball, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
battleContainer.removeChild(snowball);
}
});
// Clean up
LK.setTimeout(function () {
battleContainer.removeChild(snowman);
finishCardPlay(card);
}, 800);
}
});
}
});
break;
case 'Bola de nieve':
// Snowball appears in center and flies to enemy (reusing snowman animation)
var snowball = LK.getAsset('bola_de_nieve', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
snowball.x = centerX;
snowball.y = centerY;
battleContainer.addChild(snowball);
tween(snowball, {
x: currentEnemy.x,
y: currentEnemy.y,
scaleX: 1.2,
scaleY: 1.2,
rotation: Math.PI * 3
}, {
duration: 600,
easing: tween.easeOut,
onFinish: function onFinish() {
// Apply effects
currentEnemy.takeDamage(damage);
currentEnemy.isFrozen = true;
LK.getSound('Bola_de_nieve').play();
// Impact effect
tween(snowball, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
battleContainer.removeChild(snowball);
finishCardPlay(card);
}
});
}
});
break;
}
}
// Helper function to show card steal interface when Pirata is used
function showCardStealInterface() {
if (currentEnemy.hand.length === 0) return;
// Create steal interface container
var stealContainer = new Container();
// Semi-transparent background
var stealBg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
alpha: 0.8
});
stealContainer.addChild(stealBg);
// Title text
var stealTitleText = new Text2('Choose a card to steal:', {
size: 60,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3
});
stealTitleText.anchor.set(0.5, 0.5);
stealTitleText.x = 1024;
stealTitleText.y = 600;
stealContainer.addChild(stealTitleText);
// Display enemy cards in center for selection
var stealCards = [];
for (var i = 0; i < currentEnemy.hand.length; i++) {
var enemyCardData = currentEnemy.hand[i];
var stealCard = new Card(enemyCardData.element, enemyCardData.damage, enemyCardData.name || enemyCardData.element, enemyCardData.effect || 'normal');
stealCard.x = 1024 + (i - 1) * 350; // Center cards around screen center
stealCard.y = 1200;
stealCard.cardIndex = i; // Store original index for removal
// Override down handler for stealing
stealCard.down = function (x, y, obj) {
var selectedIndex = obj.cardIndex;
var stolenCardData = currentEnemy.hand[selectedIndex];
// Remove card from enemy hand
currentEnemy.removeCardFromHand(selectedIndex);
// Add stolen card to player hand
var stolenCard = new Card(stolenCardData.element, stolenCardData.damage, stolenCardData.name || stolenCardData.element, stolenCardData.effect || 'normal');
stolenCard.x = 400 + playerHand.length * 350;
stolenCard.y = 1800;
playerHand.push(stolenCard);
battleContainer.addChild(stolenCard);
// Remove steal interface
game.removeChild(stealContainer);
// Continue with normal card play flow
currentEnemy.takeDamage(1); // Pirata deals 1 damage
LK.getSound('damage').play();
// Check if enemy is defeated
if (currentEnemy.health === 0) {
LK.getSound('victory').play();
currentBattle++;
// Handle victory logic...
if (gameMode === 'roguelike') {
playerHealth = Math.min(playerHealth + 1, 10);
if (currentBattle > roguelikeLevel) {
roguelikeLevel = currentBattle;
storage.highestLevel = roguelikeLevel;
}
} else {
var courseNumber = Math.floor((currentBattle - 1) / 3);
var battleInCourse = (currentBattle - 1) % 3 + 1;
playerHealth = playerMaxHealth;
if (battleInCourse === 3) {
completedCourses = courseNumber + 1;
storage.completedCourses = completedCourses;
showCourseCompletionAnimation(courseNumber + 1);
}
}
updatePlayerHealth();
if (currentBattle >= enemies.length) {
LK.showYouWin();
} else {
LK.setTimeout(function () {
createBattle();
gameState = 'playerTurn';
turnText.setText('Your Turn - Turn 1');
}, 2000);
}
return;
}
// Continue to next turn
endPlayerTurn();
};
stealContainer.addChild(stealCard);
stealCards.push(stealCard);
}
game.addChild(stealContainer);
}
// Helper function for enemy pirata card steal interface
function showEnemyCardStealInterface() {
if (playerHand.length === 0) return;
// Create steal interface container
var enemyStealContainer = new Container();
// Semi-transparent background
var enemyStealBg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
alpha: 0.8
});
enemyStealContainer.addChild(enemyStealBg);
// Title text
var enemyStealTitleText = new Text2('Enemy is choosing a card to steal...', {
size: 60,
fill: 0xFF0000,
stroke: 0x000000,
strokeThickness: 3
});
enemyStealTitleText.anchor.set(0.5, 0.5);
enemyStealTitleText.x = 1024;
enemyStealTitleText.y = 600;
enemyStealContainer.addChild(enemyStealTitleText);
// Display player cards in center (non-interactive)
var displayCards = [];
for (var i = 0; i < playerHand.length; i++) {
var playerCardData = playerHand[i];
var displayCard = new Card(playerCardData.element, playerCardData.damage, playerCardData.name, playerCardData.effect);
displayCard.x = 1024 + (i - 1) * 350;
displayCard.y = 1200;
displayCard.isUsed = true; // Make non-interactive
enemyStealContainer.addChild(displayCard);
displayCards.push(displayCard);
}
game.addChild(enemyStealContainer);
// After 2 seconds, enemy makes selection and removes interface
LK.setTimeout(function () {
// Enemy steals a random card
var stealIndex = Math.floor(Math.random() * playerHand.length);
var stolenCard = playerHand[stealIndex];
var stolenCardData = {
element: stolenCard.element,
damage: stolenCard.damage,
name: stolenCard.name,
effect: stolenCard.effect
};
// Remove from player hand
playerHand.splice(stealIndex, 1);
battleContainer.removeChild(stolenCard);
// Add to enemy hand
currentEnemy.hand.push(stolenCardData);
var enemyCard = new Card(stolenCardData.element, stolenCardData.damage, stolenCardData.name, stolenCardData.effect);
enemyCard.x = -400 + currentEnemy.hand.length * 200 - 200;
enemyCard.y = -400;
enemyCard.scaleX = 0.6;
enemyCard.scaleY = 0.6;
currentEnemy.addChild(enemyCard);
if (!currentEnemy.handCards) currentEnemy.handCards = [];
currentEnemy.handCards.push(enemyCard);
// Remove steal interface
game.removeChild(enemyStealContainer);
}, 2000);
}
// Helper function to finish card play logic
function finishCardPlay(card) {
// Remove card from hand
var cardIndex = playerHand.indexOf(card);
if (cardIndex !== -1) {
playerHand.splice(cardIndex, 1);
}
battleContainer.removeChild(card);
// Add used card to tracking array with turn counter
playerUsedCards.push({
element: card.element,
damage: card.damage,
name: card.name,
effect: card.effect
});
// Handle tutorial progression
if (isTutorialActive && tutorialStep === 3) {
// After first card play, enemy attacks
tutorialStep = 4;
showTutorialMessage("¡La carta atacó! Ahora el mago rival atacará.", function () {
gameState = 'enemyTurn';
turnText.setText('Tutorial - Turno del Enemigo');
LK.setTimeout(function () {
// Enemy plays a card that costs mana to demonstrate mana restoration
tutorialEnemy.mana = 0; // Set enemy mana to 0
currentEnemy.playTurn();
}, 1000);
});
return;
}
// Check if enemy is defeated (must reach exactly 0 health)
if (currentEnemy.health === 0) {
LK.getSound('victory').play();
currentBattle++;
// Handle victory based on game mode
if (gameMode === 'roguelike') {
// Add 1 health for winning a battle
playerHealth = Math.min(playerHealth + 1, 10);
// Update roguelike progress
if (currentBattle > roguelikeLevel) {
roguelikeLevel = currentBattle;
storage.highestLevel = roguelikeLevel;
}
} else {
// Adventure mode - handle course progression
var courseNumber = Math.floor((currentBattle - 1) / 3);
var battleInCourse = (currentBattle - 1) % 3 + 1;
// Always restore full health after winning any battle in adventure mode
playerHealth = playerMaxHealth;
// If this was the third battle of a course, mark course as completed but don't change max health
if (battleInCourse === 3) {
completedCourses = courseNumber + 1;
storage.completedCourses = completedCourses;
// Course completion animation
showCourseCompletionAnimation(courseNumber + 1);
}
}
// Restore full mana after battle victory
playerMana = playerMaxMana;
updatePlayerMana();
// Update player health display after victory
updatePlayerHealth();
if (currentBattle >= enemies.length) {
// All battles won
LK.showYouWin();
} else {
// Next battle
LK.setTimeout(function () {
createBattle();
gameState = 'playerTurn';
turnText.setText('Your Turn - Turn 1');
}, 2000);
}
return;
}
endPlayerTurn();
}
function endPlayerTurn() {
// Clear player used cards for this turn
playerUsedCards = [];
// Check if this completes a turn cycle
if (playerStartsFirst) {
// Player started first, now enemy gets their turn
gameState = 'enemyTurn';
turnText.setText('Enemy Turn - Turn ' + turnCounter);
LK.setTimeout(function () {
currentEnemy.playTurn();
}, 1000);
} else {
// Enemy started first and player just finished, complete turn ends here
startNewCompleteTurn();
}
}
function enemyPlayCard(cardData, cardIndex) {
// Consume enemy mana
var manaCost = getCardManaCost(cardData.name || cardData.element, cardData.effect || 'normal');
currentEnemy.mana -= manaCost;
playElementSound(cardData.element);
var damage = cardData.damage;
var element = cardData.element;
// Track used card before removing
enemyUsedCards.push({
element: cardData.element,
damage: cardData.damage,
name: cardData.name || cardData.element,
effect: cardData.effect || 'normal'
});
// Remove the played card from enemy hand
currentEnemy.removeCardFromHand(cardIndex);
// Check for snowman effect - hombre de nieve deals 2 damage and sets up future 1 damage
if (cardData.name === 'Hombre de nieve') {
// Deal 2 damage now
playerHealth -= 2;
// Set up 10% chance for 1 damage next turn
currentEnemy.snowmanPendingDamage = true;
} else {
// Check if there's pending snowman damage from previous turn
if (currentEnemy.snowmanPendingDamage) {
if (Math.random() < 0.1) {
// 10% chance to deal 1 additional damage
playerHealth -= 1;
}
// Reset the pending damage flag
currentEnemy.snowmanPendingDamage = false;
}
switch (element) {
case 'Fire':
// Fire burns one random player card + damage
if (playerHand.length > 0) {
var burnIndex = Math.floor(Math.random() * playerHand.length);
var cardToBurn = playerHand[burnIndex];
playerHand.splice(burnIndex, 1);
battleContainer.removeChild(cardToBurn);
}
damage += 2;
break;
case 'Water':
// Handle water card effects
if (cardData.effect === 'steal') {
// Pirata - enemy shows card steal interface
if (playerHand.length > 0 && currentEnemy.hand.length < 3) {
showEnemyCardStealInterface();
return; // Don't continue with normal enemy turn flow yet
}
}
break;
case 'Air':
drawPlayerHand();
break;
case 'Ice':
// Player frozen (skip next turn - handled in game flow)
break;
}
// Apply damage through shields first
if (damage > 0) {
// Check for magma shields first
if (playerMagmaShields > 0 && element !== 'Water') {
// Magma shields reduce damage by shield count, but water attacks consume 2 shields
var damageReduction = playerMagmaShields;
var reducedDamage = Math.max(0, damage - damageReduction);
playerMagmaShields -= 1; // Always lose 1 shield per attack
damage = reducedDamage;
} else if (playerMagmaShields > 0 && element === 'Water') {
// Water attacks consume 2 magma shields and are not reduced
var shieldsToRemove = Math.min(2, playerMagmaShields);
playerMagmaShields -= shieldsToRemove;
// Water damage is NOT reduced by magma shields
}
// Then check for regular shields
if (playerShield && playerShield.health > 0 && damage > 0) {
var shieldAbsorbed = Math.min(damage, playerShield.health);
playerShield.health -= shieldAbsorbed;
damage -= shieldAbsorbed;
updateShieldDisplay();
if (damage > 0) {
playerHealth -= damage;
}
} else if (damage > 0) {
playerHealth -= damage;
}
}
}
if (playerHealth < 0) playerHealth = 0;
updatePlayerHealth();
LK.getSound('damage').play();
if (playerHealth === 0) {
// In roguelike mode, track the level where player died
if (gameMode === 'roguelike' && currentBattle > roguelikeLevel) {
roguelikeLevel = currentBattle;
storage.highestLevel = roguelikeLevel;
}
// Reset mana to full when battle ends
playerMana = playerMaxMana;
updatePlayerMana();
LK.showGameOver();
return;
}
endEnemyTurn();
}
function endEnemyTurn() {
// Clear enemy used cards for this turn
enemyUsedCards = [];
// Handle tutorial progression
if (isTutorialActive && tutorialStep === 4) {
// After enemy attack, show mana restoration
tutorialStep = 5;
showTutorialMessage("Como puedes ver, tu barra de maná se restauró. Fíjate bien.", function () {
highlightManaBar();
LK.setTimeout(function () {
tutorialStep = 6;
showTutorialMessage("Tienes una carta de daño (Rama), otra que sana (Agua oxigenada) y otra que protege (Muralla rocosa).", function () {
highlightCardTypes();
LK.setTimeout(function () {
tutorialStep = 7;
showTutorialMessage("El mago usará un hechizo que te dejará sin maná. ¡Observa!", function () {
// Enemy casts spell to drain player mana
playerMana = 0;
updatePlayerMana();
LK.setTimeout(function () {
tutorialStep = 8;
showTutorialMessage("Si te quedas sin maná, puedes pasar el turno para recuperarlo todo.", function () {
gameState = 'playerTurn';
turnText.setText('Tutorial - Prueba Pasar Turno');
});
}, 1000);
});
}, 2000);
});
}, 2000);
});
return;
}
// Check if this is the end of a complete turn (both players have played)
// If enemy started first, complete turn ends here
// If player started first, we need to give player their turn
if (!playerStartsFirst) {
// Enemy started first, now it's player's turn to complete the turn
gameState = 'playerTurn';
turnText.setText('Your Turn - Turn ' + turnCounter);
} else {
// Player started first and enemy just finished, complete turn ends here
// Complete turn cycle - both player and enemy have played, now start new complete turn
startNewCompleteTurn();
}
}
function startNewCompleteTurn() {
turnCounter++;
// Restore mana after complete turn cycle (except first turn)
if (turnCounter > 1) {
// Restore 3-5 mana for player, but never exceed max
var manaToRestore = 3 + Math.floor(Math.random() * 3); // Random between 3-5
playerMana = Math.min(playerMana + manaToRestore, playerMaxMana);
// Restore 3-5 mana for enemy, but never exceed max
var enemyManaToRestore = 3 + Math.floor(Math.random() * 3); // Random between 3-5
currentEnemy.mana = Math.min(currentEnemy.mana + enemyManaToRestore, currentEnemy.maxMana);
updatePlayerMana();
}
// Both players get 3 new cards (maintaining unused cards from previous mazo de selección)
drawNewHandForCompleteTurn('player');
drawNewHandForCompleteTurn('enemy');
// Start card selection phase
startCardSelectionPhase();
}
function clearAllHands() {
// Clear player hand
for (var i = playerHand.length - 1; i >= 0; i--) {
var card = playerHand[i];
battleContainer.removeChild(card);
}
playerHand = [];
// Clear enemy hand
if (currentEnemy.handCards) {
for (var i = 0; i < currentEnemy.handCards.length; i++) {
if (currentEnemy.handCards[i].parent) {
currentEnemy.handCards[i].parent.removeChild(currentEnemy.handCards[i]);
}
}
}
currentEnemy.handCards = [];
currentEnemy.hand = [];
}
function drawNewHandForCompleteTurn(playerType) {
if (playerType === 'player') {
// Player maintains unused cards from previous mazo de selección in the new mazo de selección
var playerHandNames = []; // Track card names to prevent duplicates in new mazo de selección
// Identify unused cards from current mazo de selección (those not in playerUsedCards)
var cardsToKeep = [];
for (var i = 0; i < playerHand.length; i++) {
var wasUsed = false;
for (var j = 0; j < playerUsedCards.length; j++) {
if (playerUsedCards[j].name === playerHand[i].name) {
wasUsed = true;
break;
}
}
// Keep unused cards from previous mazo de selección for new mazo de selección
if (!wasUsed) {
cardsToKeep.push(playerHand[i]);
playerHandNames.push(playerHand[i].name);
}
}
// Clear current hand visuals and re-add kept cards
for (var i = playerHand.length - 1; i >= 0; i--) {
battleContainer.removeChild(playerHand[i]);
}
playerHand = [];
// Re-add kept cards to hand with updated positions
for (var i = 0; i < cardsToKeep.length; i++) {
var keptCard = cardsToKeep[i];
keptCard.x = 400 + i * 350;
keptCard.y = 1800;
keptCard.isUsed = false; // Reset used state
playerHand.push(keptCard);
battleContainer.addChild(keptCard);
}
// Add new cards to complete hand to 3 cards total
var cardsNeeded = 3 - cardsToKeep.length;
for (var i = 0; i < cardsNeeded; i++) {
if (playerDeck.length > 0) {
var attempts = 0;
var foundValidCard = false;
while (attempts < 50 && !foundValidCard) {
var randomIndex = Math.floor(Math.random() * playerDeck.length);
var cardData = playerDeck[randomIndex];
// Check if this card name is already in current hand or was used this turn
var isDuplicate = false;
// Check against current hand
for (var j = 0; j < playerHandNames.length; j++) {
if (playerHandNames[j] === cardData.name) {
isDuplicate = true;
break;
}
}
// Check against used cards this turn
if (!isDuplicate) {
for (var j = 0; j < playerUsedCards.length; j++) {
if (playerUsedCards[j].name === cardData.name) {
isDuplicate = true;
break;
}
}
}
// Use card if it's not a duplicate
if (!isDuplicate) {
var card = new Card(cardData.element, cardData.damage, cardData.name, cardData.effect);
card.x = 400 + (cardsToKeep.length + i) * 350;
card.y = 1800;
playerHand.push(card);
battleContainer.addChild(card);
playerHandNames.push(cardData.name);
foundValidCard = true;
}
attempts++;
}
}
}
} else {
// Enemy maintains unused cards from previous mazo de selección in the new mazo de selección
var enemyHandNames = []; // Track card names to prevent duplicates in new mazo de selección
// Identify unused cards from current mazo de selección (those not in enemyUsedCards)
var cardsToKeep = [];
for (var i = 0; i < currentEnemy.hand.length; i++) {
var wasUsed = false;
var cardName = currentEnemy.hand[i].name || currentEnemy.hand[i].element;
for (var j = 0; j < enemyUsedCards.length; j++) {
if (enemyUsedCards[j].name === cardName) {
wasUsed = true;
break;
}
}
// Keep unused cards from previous mazo de selección for new mazo de selección
if (!wasUsed) {
cardsToKeep.push(currentEnemy.hand[i]);
enemyHandNames.push(cardName);
}
}
// Clear current enemy hand visuals
if (currentEnemy.handCards) {
for (var i = 0; i < currentEnemy.handCards.length; i++) {
if (currentEnemy.handCards[i].parent) {
currentEnemy.handCards[i].parent.removeChild(currentEnemy.handCards[i]);
}
}
}
currentEnemy.handCards = [];
currentEnemy.hand = [];
// Re-add kept cards to enemy hand
for (var i = 0; i < cardsToKeep.length; i++) {
currentEnemy.hand.push(cardsToKeep[i]);
// Create visual card for kept enemy card
var enemyCard = new Card(cardsToKeep[i].element, cardsToKeep[i].damage, cardsToKeep[i].name || cardsToKeep[i].element, cardsToKeep[i].effect || 'normal');
enemyCard.x = -400 + i * 200;
enemyCard.y = -400;
enemyCard.scaleX = 0.6;
enemyCard.scaleY = 0.6;
currentEnemy.addChild(enemyCard);
if (!currentEnemy.handCards) currentEnemy.handCards = [];
currentEnemy.handCards.push(enemyCard);
}
// Add new cards to complete hand to 3 cards total
var cardsNeeded = 3 - cardsToKeep.length;
for (var i = 0; i < cardsNeeded; i++) {
if (currentEnemy.deck.length > 0) {
var attempts = 0;
var foundValidCard = false;
while (attempts < 50 && !foundValidCard) {
var randomIndex = Math.floor(Math.random() * currentEnemy.deck.length);
var cardData = currentEnemy.deck[randomIndex];
var cardName = cardData.name || cardData.element;
// Check if this card name is already in current hand or was used this turn
var isDuplicate = false;
// Check against current hand
for (var j = 0; j < enemyHandNames.length; j++) {
if (enemyHandNames[j] === cardName) {
isDuplicate = true;
break;
}
}
// Check against used cards this turn
if (!isDuplicate) {
for (var j = 0; j < enemyUsedCards.length; j++) {
if (enemyUsedCards[j].name === cardName) {
isDuplicate = true;
break;
}
}
}
// Use card if it's not a duplicate
if (!isDuplicate) {
currentEnemy.hand.push(cardData);
// Create visual card for enemy
var enemyCard = new Card(cardData.element, cardData.damage, cardData.name || cardData.element, cardData.effect || 'normal');
enemyCard.x = -400 + (cardsToKeep.length + i) * 200;
enemyCard.y = -400;
enemyCard.scaleX = 0.6;
enemyCard.scaleY = 0.6;
currentEnemy.addChild(enemyCard);
if (!currentEnemy.handCards) currentEnemy.handCards = [];
currentEnemy.handCards.push(enemyCard);
enemyHandNames.push(cardName);
foundValidCard = true;
}
attempts++;
}
}
}
}
}
function updatePlayerHealth() {
var healthPercentage = playerHealth / playerMaxHealth;
playerHealthBar.scaleX = healthPercentage;
playerHealthText.setText('HP: ' + playerHealth + '/' + playerMaxHealth);
}
function updatePlayerMana() {
var manaPercentage = playerMana / playerMaxMana;
playerManaBar.scaleX = manaPercentage;
playerManaText.setText('⚡: ' + playerMana + '/' + playerMaxMana);
}
function updateShieldDisplay() {
if (playerShield && playerShield.health > 0) {
var shieldPercentage = playerShield.health / playerShield.maxHealth;
playerShieldBar.scaleX = shieldPercentage;
playerShieldBar.alpha = 1;
playerShieldText.setText('🛡: ' + playerShield.health + '/' + playerShield.maxHealth);
playerShieldText.alpha = 1;
} else {
playerShieldBar.alpha = 0;
playerShieldText.alpha = 0;
playerShield = null;
}
}
function startBattle() {
gameState = 'battle';
if (gameMode === 'adventure') {
// Start from first uncompleted battle
currentBattle = completedCourses * 3;
// Set health based on last completed course's final wizard
if (completedCourses > 0) {
// Get the last wizard from the previous completed course
var lastCompletedCourse = completedCourses - 1;
var lastWizardIndex = lastCompletedCourse * 3 + 2; // Third wizard of the course (index 2)
if (lastWizardIndex < enemies.length) {
var lastWizard = enemies[lastWizardIndex];
playerHealth = lastWizard.maxHealth;
playerMaxHealth = lastWizard.maxHealth;
// Store the max health for future reference
storage.playerMaxHealth = playerMaxHealth;
} else {
playerHealth = 10;
playerMaxHealth = 10;
}
} else {
// First course - start with default health
playerHealth = 10;
playerMaxHealth = 10;
}
} else {
// Roguelike mode
currentBattle = 0;
playerHealth = 10;
playerMaxHealth = 10;
}
turnCounter = 0;
playerMana = playerMaxMana;
// Randomly determine who starts first
playerStartsFirst = Math.random() < 0.5;
game.removeChild(menuContainer);
createBattle();
// Ensure both players have 3 cards in their mazo de selección (selection deck) at start
drawPlayerHand();
currentEnemy.drawHand();
// Start with card selection phase for turn 1
turnCounter = 1;
startCardSelectionPhase();
}
function showElementBook(element) {
// Create book container
var bookContainer = new Container();
// Background
var bookBg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0
});
bookContainer.addChild(bookBg);
// Title
var titleText = new Text2(element + ' Element Cards', {
size: 60,
fill: 0xFFFFFF
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 300;
bookContainer.addChild(titleText);
// Create scrollable content container
var scrollContainer = new Container();
var currentScrollY = 0;
var maxScrollY = 0;
// Get element cards based on element type
var elementCards = [];
switch (element) {
case 'Fire':
elementCards = [{
name: 'Llamarada',
damage: 3,
effect: 'Inflige 3 de daño y quema una carta enemiga'
}, {
name: 'Fénix',
damage: 2,
effect: 'Inflige 2 de daño y cura 2 puntos de vida'
}, {
name: 'Brasas',
damage: 2,
effect: 'Inflige 2 de daño y quema una carta enemiga'
}, {
name: 'Llama',
damage: 2,
effect: 'Inflige 2 de daño y quema una carta enemiga'
}, {
name: 'Mechero',
damage: 1,
effect: 'Inflige 1 de daño y evita que el enemigo use cartas de hielo'
}, {
name: 'Escudos de Magma',
damage: 0,
effect: 'Crea 3 escudos que reducen daño según su cantidad. Los ataques de agua no se reducen y gastan 2 escudos.'
}];
break;
case 'Water':
elementCards = [{
name: 'Gota de agua',
damage: 1,
effect: 'Inflige 1 de daño'
}, {
name: 'Luna roja',
damage: 2,
effect: 'Inflige 2 de daño'
}, {
name: 'Agua oxigenada',
damage: 0,
effect: 'Cura 3 puntos de vida'
}, {
name: 'Marea',
damage: 2,
effect: 'Inflige 2 de daño'
}, {
name: 'Pirata',
damage: 1,
effect: 'Inflige 1 de daño y roba una carta enemiga'
}];
break;
case 'Air':
elementCards = [{
name: 'Vendaval',
damage: 2,
effect: 'Inflige 2 de daño'
}, {
name: 'Brisa',
damage: 1,
effect: 'Inflige 1 de daño'
}, {
name: 'Huracán',
damage: 2,
effect: 'Inflige 2 de daño y reemplaza las cartas enemigas'
}, {
name: 'Tormenta',
damage: 3,
effect: 'Inflige 3 de daño, 10% de probabilidad de saltar turno enemigo'
}, {
name: 'Niebla',
damage: 1,
effect: 'Inflige 1 de daño, 10% de fallo permanente en turnos enemigos'
}, {
name: 'Oxígeno',
damage: 0,
effect: 'Cura 2 puntos de vida'
}];
break;
case 'Plant':
elementCards = [{
name: 'Arboleda',
damage: 2,
effect: 'Inflige 2 de daño'
}, {
name: 'Serafín',
damage: 0,
effect: 'Cura 2 puntos de vida'
}, {
name: 'Rama',
damage: 1,
effect: 'Inflige 1 de daño'
}, {
name: 'Vida extra',
damage: 0,
effect: 'Aumenta la vida máxima en 1'
}, {
name: 'Aullidos del bosque',
damage: 2,
effect: 'Inflige 2 de daño, el enemigo pierde su turno'
}];
break;
case 'Rock':
elementCards = [{
name: 'Pedrada',
damage: 2,
effect: 'Inflige 2 de daño'
}, {
name: 'Tormenta de arena',
damage: 2,
effect: 'Inflige 2 de daño'
}, {
name: 'Sarcófago',
damage: 2,
effect: 'Inflige 2 de daño y cura 2 puntos de vida'
}, {
name: 'Muralla Rocosa',
damage: 0,
effect: 'Crea escudo con 4 puntos de vida que absorbe daño'
}, {
name: 'Terremoto',
damage: 2,
effect: 'Inflige 2 de daño, ambos jugadores obtienen cartas nuevas'
}];
break;
case 'Ice':
elementCards = [{
name: 'Nevada',
damage: 2,
effect: 'Inflige 2 de daño y congela al enemigo'
}, {
name: 'Ventisca',
damage: 2,
effect: 'Inflige 2 de daño, 10% de probabilidad de congelar'
}, {
name: 'Bola de nieve',
damage: 1,
effect: 'Inflige 1 de daño y congela al enemigo'
}, {
name: 'Hombre de nieve',
damage: 2,
effect: 'Inflige 2 de daño, puede atacar de nuevo por 1 de daño'
}, {
name: 'Esquirla Helada',
damage: 1,
effect: 'Inflige 1 de daño con prioridad, el enemigo pierde su próximo turno'
}];
break;
case 'Invocation':
elementCards = [{
name: 'Fénix',
damage: 2,
effect: 'Inflige 2 de daño y cura 2 puntos de vida'
}, {
name: 'Pirata',
damage: 1,
effect: 'Inflige 1 de daño y roba una carta enemiga'
}, {
name: 'Serafín',
damage: 0,
effect: 'Cura 2 puntos de vida'
}, {
name: 'Sarcófago',
damage: 2,
effect: 'Inflige 2 de daño y cura 2 puntos de vida'
}, {
name: 'Hombre de nieve',
damage: 2,
effect: 'Inflige 2 de daño, puede atacar de nuevo por 1 de daño'
}];
break;
}
// Helper function to word wrap text
function wrapText(text, maxWidth) {
var words = text.split(' ');
var lines = [];
var currentLine = '';
for (var i = 0; i < words.length; i++) {
var testLine = currentLine + (currentLine ? ' ' : '') + words[i];
if (testLine.length * 22 <= maxWidth) {
// Approximate character width
currentLine = testLine;
} else {
if (currentLine) {
lines.push(currentLine);
}
currentLine = words[i];
}
}
if (currentLine) {
lines.push(currentLine);
}
return lines;
}
// Display cards in scroll container
for (var i = 0; i < elementCards.length; i++) {
var cardInfo = elementCards[i];
var yPos = 100 + i * 380; // Increased spacing for wrapped text
// Card name
var nameText = new Text2(cardInfo.name, {
size: 55,
fill: 0xffffff,
stroke: 0x000000,
strokeThickness: 2
});
nameText.anchor.set(0, 0.5);
nameText.x = 100;
nameText.y = yPos;
scrollContainer.addChild(nameText);
// Damage
var damageText = new Text2('Daño: ' + cardInfo.damage, {
size: 45,
fill: 0xffffff,
stroke: 0x000000,
strokeThickness: 2
});
damageText.anchor.set(0, 0.5);
damageText.x = 100;
damageText.y = yPos + 50;
scrollContainer.addChild(damageText);
// Mana cost
var manaCost = getCardManaCost(cardInfo.name, cardInfo.effect);
var manaText = new Text2('Maná: ⚡' + manaCost, {
size: 45,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 2
});
manaText.anchor.set(0, 0.5);
manaText.x = 100;
manaText.y = yPos + 100;
scrollContainer.addChild(manaText);
// Effect with word wrapping
var effectLines = wrapText('Efecto: ' + cardInfo.effect, 1800);
for (var j = 0; j < effectLines.length; j++) {
var effectText = new Text2(effectLines[j], {
size: 40,
fill: 0xffffff,
stroke: 0x000000,
strokeThickness: 2
});
effectText.anchor.set(0, 0.5);
effectText.x = 100;
effectText.y = yPos + 150 + j * 45;
scrollContainer.addChild(effectText);
}
maxScrollY = Math.max(maxScrollY, yPos + 200);
}
// Position scroll container to start below title with more spacing
scrollContainer.y = 450; // Start well below title area to prevent overlap
bookContainer.addChild(scrollContainer);
// Scroll controls
var scrollUpButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1850,
y: 1200,
scaleX: 0.5,
scaleY: 0.4
});
bookContainer.addChild(scrollUpButton);
var scrollUpText = new Text2('▲', {
size: 60,
fill: 0xFFFFFF
});
scrollUpText.anchor.set(0.5, 0.5);
scrollUpText.x = 1850;
scrollUpText.y = 1200;
bookContainer.addChild(scrollUpText);
var scrollDownButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1850,
y: 2200,
scaleX: 0.5,
scaleY: 0.4
});
bookContainer.addChild(scrollDownButton);
var scrollDownText = new Text2('▼', {
size: 60,
fill: 0xFFFFFF
});
scrollDownText.anchor.set(0.5, 0.5);
scrollDownText.x = 1850;
scrollDownText.y = 2200;
bookContainer.addChild(scrollDownText);
// Scroll functionality
scrollUpButton.down = function () {
var maxScrollUp = -50; // Prevent scrolling too far up to keep content below title
if (currentScrollY < maxScrollUp) {
currentScrollY += 200;
if (currentScrollY > maxScrollUp) currentScrollY = maxScrollUp;
scrollContainer.y = 450 + currentScrollY; // Base position + scroll offset
}
};
scrollDownButton.down = function () {
var minScrollY = -(maxScrollY - 1500); // Visible area height
if (currentScrollY > minScrollY) {
currentScrollY -= 200;
if (currentScrollY < minScrollY) currentScrollY = minScrollY;
scrollContainer.y = 450 + currentScrollY; // Base position + scroll offset
}
};
// Back button
var backButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 2600
});
bookContainer.addChild(backButton);
var backText = new Text2('Back to Menu', {
size: 40,
fill: 0xFFFFFF
});
backText.anchor.set(0.5, 0.5);
backText.x = 1024;
backText.y = 2600;
bookContainer.addChild(backText);
backButton.down = function () {
game.removeChild(bookContainer);
LK.playMusic('Academia', {
fade: {
start: 0,
end: 1,
duration: 500
}
});
};
game.addChild(bookContainer);
}
function showCourseCompletionAnimation(courseNumber) {
// Create animation container
var animContainer = new Container();
// Semi-transparent background
var animBg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
alpha: 0.8
});
animContainer.addChild(animBg);
// Course completed text
var completionText = new Text2('COURSE ' + courseNumber + ' COMPLETED!', {
size: 80,
fill: 0xFFD700,
font: "'Arial Black', Arial, sans-serif",
stroke: 0x4B0082,
strokeThickness: 4
});
completionText.anchor.set(0.5, 0.5);
completionText.x = 1024;
completionText.y = 1366;
completionText.alpha = 0;
completionText.scaleX = 0.1;
completionText.scaleY = 0.1;
animContainer.addChild(completionText);
// Star effects
var stars = [];
for (var i = 0; i < 5; i++) {
var star = LK.getAsset('academy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.2,
scaleY: 0.2,
tint: 0xFFD700
});
star.x = 1024 + (i - 2) * 200;
star.y = 1000;
star.alpha = 0;
star.rotation = 0;
animContainer.addChild(star);
stars.push(star);
}
game.addChild(animContainer);
// Animate text appearance
tween(completionText, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 800,
easing: tween.elasticOut
});
// Animate stars
for (var i = 0; i < stars.length; i++) {
(function (star, delay) {
LK.setTimeout(function () {
tween(star, {
alpha: 1,
rotation: Math.PI * 2
}, {
duration: 600,
easing: tween.easeOut
});
}, delay);
})(stars[i], i * 150);
}
// Pulsing effect
LK.setTimeout(function () {
tween(completionText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(completionText, {
scaleX: 1,
scaleY: 1
}, {
duration: 500,
easing: tween.easeInOut
});
}
});
}, 1000);
// Remove animation after 3 seconds
LK.setTimeout(function () {
tween(animContainer, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
game.removeChild(animContainer);
// Check if all courses completed for graduation
if (completedCourses >= 6) {
showGraduationAnimation();
}
}
});
}, 3000);
}
function startTutorialBattle() {
isTutorialActive = true;
gameState = 'tutorial';
turnCounter = 0;
playerHealth = 10;
playerMaxHealth = 10;
playerMana = 10;
playerMaxMana = 10;
playerStartsFirst = true; // Player always starts first in tutorial
// Create special tutorial enemy (wizard18 - different from regular enemies)
tutorialEnemy = new Enemy('Maestro del Tutorial', 8, 0, 17); // Use wizard18
currentEnemy = tutorialEnemy;
// Initialize special tutorial deck for player with specific cards
playerDeck = [{
element: 'Plant',
damage: 1,
name: 'Rama',
effect: 'normal'
}, {
element: 'Water',
damage: 0,
name: 'Agua oxigenada',
effect: 'heal'
}, {
element: 'Rock',
damage: 0,
name: 'Muralla Rocosa',
effect: 'shield'
}];
game.removeChild(menuContainer);
createBattle();
startTutorial();
}
function createTutorialWizard() {
// Create tutorial wizard (friendly mentor)
tutorialWizard = LK.getAsset('wizard1', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.8,
scaleY: 0.8
});
tutorialWizard.x = 300;
tutorialWizard.y = 800;
battleContainer.addChild(tutorialWizard);
// Create text box for tutorial messages
var textBoxBg = LK.getAsset('Cuadro_dialogo', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 6.0,
scaleY: 4.0
});
textBoxBg.x = 1024;
textBoxBg.y = 400;
tutorialTextBox = new Text2('', {
size: 28,
fill: 0x000000,
stroke: 0x000000,
strokeThickness: 3
});
tutorialTextBox.anchor.set(0.5, 0.5);
tutorialTextBox.x = 1024;
tutorialTextBox.y = 400;
tutorialContainer = new Container();
tutorialContainer.addChild(textBoxBg);
tutorialContainer.addChild(tutorialTextBox);
battleContainer.addChild(tutorialContainer);
}
function showTutorialMessage(message, callback) {
if (tutorialTextBox) {
// Split message into lines to fit within dialogue box width
var words = message.split(' ');
var lines = [];
var currentLine = '';
var maxWordsPerLine = 8; // Adjust based on dialogue box width
var wordsInCurrentLine = 0;
for (var i = 0; i < words.length; i++) {
if (wordsInCurrentLine >= maxWordsPerLine) {
lines.push(currentLine.trim());
currentLine = words[i] + ' ';
wordsInCurrentLine = 1;
} else {
currentLine += words[i] + ' ';
wordsInCurrentLine++;
}
}
if (currentLine.trim().length > 0) {
lines.push(currentLine.trim());
}
tutorialTextBox.setText(lines.join('\n'));
// Add black border to text
tutorialTextBox.stroke = 0x000000;
tutorialTextBox.strokeThickness = 3;
// Adjust dialogue box scale based on number of lines
var lineCount = lines.length;
var scaleX = Math.max(6.0, lineCount * 0.8 + 4.0); // Minimum 6.0, grows with content
var scaleY = Math.max(3.0, lineCount * 0.6 + 2.0); // Minimum 3.0, grows with content
// Find and update the dialogue box scale
if (tutorialContainer && tutorialContainer.children[0]) {
tutorialContainer.children[0].scaleX = scaleX;
tutorialContainer.children[0].scaleY = scaleY;
}
// Make tutorial container clickable to continue
tutorialContainer.down = function () {
if (callback) callback();
};
}
}
function startTutorial() {
gameState = 'tutorial';
tutorialStep = 1;
createTutorialWizard();
// Step 1: Welcome and show mazo de selección
showTutorialMessage("¡Bienvenido! Estos son tu mazo de selección de 3 cartas. Observa cómo se iluminan.", function () {
highlightPlayerHand();
LK.setTimeout(function () {
tutorialStep = 2;
// Step 2: Explain mana costs
showTutorialMessage("Cada carta tiene un coste de maná. Fíjate en el símbolo ⚡ de cada carta.", function () {
highlightManaCosts();
LK.setTimeout(function () {
tutorialStep = 3;
// Step 3: Player selects and plays first card
showTutorialMessage("Ahora selecciona una carta para atacar. Te recomiendo usar 'Rama'.", function () {
gameState = 'playerTurn';
turnText.setText('Tutorial - Elige una carta');
removeHighlight();
});
}, 2000);
});
}, 2000);
});
}
function highlightPlayerHand() {
// Create highlight effect for player hand
for (var i = 0; i < playerHand.length; i++) {
(function (card) {
tween(card, {
tint: 0xFFFF00
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(card, {
tint: 0xFFFFFF
}, {
duration: 500,
easing: tween.easeInOut
});
}
});
})(playerHand[i]);
}
}
function highlightManaCosts() {
// Visual emphasis on mana costs (cards will flash blue to highlight mana symbols)
for (var i = 0; i < playerHand.length; i++) {
(function (card) {
tween(card, {
tint: 0x00FFFF
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(card, {
tint: 0xFFFFFF
}, {
duration: 800,
easing: tween.easeInOut
});
}
});
})(playerHand[i]);
}
}
function highlightManaBar() {
// Highlight player mana bar
tween(playerManaBar, {
tint: 0xFFFF00
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(playerManaBar, {
tint: 0x0066FF
}, {
duration: 1000,
easing: tween.easeInOut
});
}
});
}
function highlightCardTypes() {
// Highlight each card type with different colors
for (var i = 0; i < playerHand.length; i++) {
var card = playerHand[i];
var highlightColor = 0xFFFFFF;
if (card.name === 'Rama') highlightColor = 0xFF0000; // Red for damage
else if (card.name === 'Agua oxigenada') highlightColor = 0x00FF00; // Green for heal
else if (card.name === 'Muralla Rocosa') highlightColor = 0x0000FF; // Blue for shield
(function (cardToHighlight, color) {
tween(cardToHighlight, {
tint: color
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(cardToHighlight, {
tint: 0xFFFFFF
}, {
duration: 1500,
easing: tween.easeInOut
});
}
});
})(card, highlightColor);
}
}
function removeHighlight() {
// Remove any highlights
if (tutorialHighlight) {
battleContainer.removeChild(tutorialHighlight);
tutorialHighlight = null;
}
}
function endTutorial() {
// Mark tutorial as completed
if (isFirstTimeAdventure || !storage.hasPlayedAdventure) {
storage.hasPlayedAdventure = true;
isFirstTimeAdventure = false;
}
isTutorialActive = false;
// Remove tutorial UI
if (tutorialContainer) {
battleContainer.removeChild(tutorialContainer);
tutorialContainer = null;
}
if (tutorialWizard) {
battleContainer.removeChild(tutorialWizard);
tutorialWizard = null;
}
removeHighlight();
// Defeat tutorial enemy to end tutorial
tutorialEnemy.health = 0;
tutorialEnemy.healthText.setText('HP: 0');
// Show victory and return to menu
LK.setTimeout(function () {
gameState = 'menu';
game.removeChild(battleContainer);
createMenu();
LK.playMusic('Academia', {
fade: {
start: 0,
end: 1,
duration: 500
}
});
}, 1000);
}
function showPlayerSetup() {
// Create setup container
var setupContainer = new Container();
// Background
var setupBg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0
});
setupContainer.addChild(setupBg);
// Title
var titleText = new Text2('¡Bienvenido a la Academia Mágica!', {
size: 80,
fill: 0xFFD700,
font: "'Arial Black', Arial, sans-serif",
stroke: 0x4B0082,
strokeThickness: 4
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 400;
setupContainer.addChild(titleText);
// Gender selection text
var genderText = new Text2('Eres mago o maga:', {
size: 60,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3
});
genderText.anchor.set(0.5, 0.5);
genderText.x = 1024;
genderText.y = 800;
setupContainer.addChild(genderText);
// Mago (wizard) option - three wizard assets
var magoImages = [];
var magoAssets = ['Mago', 'Mago1', 'Mago2'];
// Three Mago images with spacing - positioned between gender text and name text
for (var i = 0; i < magoAssets.length; i++) {
var magoImage = LK.getAsset(magoAssets[i], {
anchorX: 0.5,
anchorY: 0.5,
x: 300 + i * 150,
y: 1100,
scaleX: 2.0,
scaleY: 2.0
});
magoImages.push(magoImage);
setupContainer.addChild(magoImage);
}
// Play buttons below mago assets
var magoPlayButtons = [];
for (var i = 0; i < magoAssets.length; i++) {
var playButton = new Text2('▶️', {
size: 60,
fill: 0xFFFFFF
});
playButton.anchor.set(0.5, 0.5);
playButton.x = 300 + i * 150;
playButton.y = 1250;
magoPlayButtons.push(playButton);
setupContainer.addChild(playButton);
}
// Make mago play buttons clickable
for (var i = 0; i < magoPlayButtons.length; i++) {
(function (buttonIndex, assetName, magoImage) {
magoPlayButtons[buttonIndex].down = function (x, y, obj) {
selectedGender = 'mago';
storage.playerGender = selectedGender;
// Store which specific mago asset was selected
storage.selectedMagoAsset = assetName;
// Reset all mago images to normal and stop any existing animations
for (var j = 0; j < magoImages.length; j++) {
tween.stop(magoImages[j], {
tint: true
});
magoImages[j].tint = 0xFFFFFF;
}
// Reset all maga images and stop any glowing animations
for (var j = 0; j < magaImages.length; j++) {
tween.stop(magaImages[j], {
tint: true
});
magaImages[j].tint = 0xFFFFFF;
}
// Reset all play buttons
for (var j = 0; j < magoPlayButtons.length; j++) {
tween.stop(magoPlayButtons[j], {
tint: true
});
magoPlayButtons[j].tint = 0xFFFFFF;
}
for (var j = 0; j < magaPlayButtons.length; j++) {
tween.stop(magaPlayButtons[j], {
tint: true
});
magaPlayButtons[j].tint = 0xFFFFFF;
}
// Create yellow border glow effect for the mago asset
var borderGlow = LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5,
x: magoImage.x,
y: magoImage.y,
scaleX: 2.1,
scaleY: 2.1,
tint: 0xFFFF00,
alpha: 0.7
});
setupContainer.addChild(borderGlow);
// Position border behind the asset
setupContainer.setChildIndex(borderGlow, setupContainer.getChildIndex(magoImage) - 1);
function createMagoAssetGlow() {
tween(borderGlow, {
alpha: 1,
scaleX: 2.2,
scaleY: 2.2
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(borderGlow, {
alpha: 0.7,
scaleX: 2.1,
scaleY: 2.1
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: createMagoAssetGlow
});
}
});
}
createMagoAssetGlow();
updateContinueButton();
};
})(i, magoAssets[i], magoImages[i]);
}
// Make mago assets clickable
for (var i = 0; i < magoImages.length; i++) {
(function (imageIndex, assetName) {
magoImages[imageIndex].down = function (x, y, obj) {
selectedGender = 'mago';
storage.playerGender = selectedGender;
// Store which specific mago asset was selected
storage.selectedMagoAsset = assetName;
// Reset all mago images to normal and stop any existing animations
for (var j = 0; j < magoImages.length; j++) {
tween.stop(magoImages[j], {
tint: true
});
magoImages[j].tint = 0xFFFFFF;
}
// Reset all maga images and stop any glowing animations
for (var j = 0; j < magaImages.length; j++) {
tween.stop(magaImages[j], {
tint: true
});
magaImages[j].tint = 0xFFFFFF;
}
// Reset all play buttons
for (var j = 0; j < magoPlayButtons.length; j++) {
tween.stop(magoPlayButtons[j], {
tint: true
});
magoPlayButtons[j].tint = 0xFFFFFF;
}
for (var j = 0; j < magaPlayButtons.length; j++) {
tween.stop(magaPlayButtons[j], {
tint: true
});
magaPlayButtons[j].tint = 0xFFFFFF;
}
// Create bright glowing effect for the selected mago asset
function createMagoGlow() {
tween(obj, {
tint: 0xFFFF00
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(obj, {
tint: 0xFFFFFF
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: createMagoGlow
});
}
});
}
createMagoGlow();
updateContinueButton();
};
})(i, magoAssets[i]);
}
// Remove mago selection buttons - assets are now directly clickable
// Maga (witch) option - three witch assets
var magaImages = [];
var magaAssets = ['Maga', 'Maga1', 'Maga2'];
// Three Maga images with spacing - positioned between gender text and name text
for (var i = 0; i < magaAssets.length; i++) {
var magaImage = LK.getAsset(magaAssets[i], {
anchorX: 0.5,
anchorY: 0.5,
x: 1200 + i * 150,
y: 1100,
scaleX: 2.0,
scaleY: 2.0
});
magaImages.push(magaImage);
setupContainer.addChild(magaImage);
}
// Play buttons below maga assets
var magaPlayButtons = [];
for (var i = 0; i < magaAssets.length; i++) {
var playButton = new Text2('▶️', {
size: 60,
fill: 0xFFFFFF
});
playButton.anchor.set(0.5, 0.5);
playButton.x = 1200 + i * 150;
playButton.y = 1250;
magaPlayButtons.push(playButton);
setupContainer.addChild(playButton);
}
// Make maga play buttons clickable
for (var i = 0; i < magaPlayButtons.length; i++) {
(function (buttonIndex, assetName, magaImage) {
magaPlayButtons[buttonIndex].down = function (x, y, obj) {
selectedGender = 'maga';
storage.playerGender = selectedGender;
// Store which specific maga asset was selected
storage.selectedMagaAsset = assetName;
// Reset all mago images to normal and stop any existing animations
for (var j = 0; j < magoImages.length; j++) {
tween.stop(magoImages[j], {
tint: true
});
magoImages[j].tint = 0xFFFFFF;
}
// Reset all maga images to normal and stop any existing animations
for (var j = 0; j < magaImages.length; j++) {
tween.stop(magaImages[j], {
tint: true
});
magaImages[j].tint = 0xFFFFFF;
}
// Reset all play buttons
for (var j = 0; j < magoPlayButtons.length; j++) {
tween.stop(magoPlayButtons[j], {
tint: true
});
magoPlayButtons[j].tint = 0xFFFFFF;
}
for (var j = 0; j < magaPlayButtons.length; j++) {
tween.stop(magaPlayButtons[j], {
tint: true
});
magaPlayButtons[j].tint = 0xFFFFFF;
}
// Create yellow border glow effect for the maga asset
var borderGlow = LK.getAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5,
x: magaImage.x,
y: magaImage.y,
scaleX: 2.1,
scaleY: 2.1,
tint: 0xFFFF00,
alpha: 0.7
});
setupContainer.addChild(borderGlow);
// Position border behind the asset
setupContainer.setChildIndex(borderGlow, setupContainer.getChildIndex(magaImage) - 1);
function createMagaAssetGlow() {
tween(borderGlow, {
alpha: 1,
scaleX: 2.2,
scaleY: 2.2
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(borderGlow, {
alpha: 0.7,
scaleX: 2.1,
scaleY: 2.1
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: createMagaAssetGlow
});
}
});
}
createMagaAssetGlow();
updateContinueButton();
};
})(i, magaAssets[i], magaImages[i]);
}
// Make maga assets clickable
for (var i = 0; i < magaImages.length; i++) {
(function (imageIndex, assetName) {
magaImages[imageIndex].down = function (x, y, obj) {
selectedGender = 'maga';
storage.playerGender = selectedGender;
// Store which specific maga asset was selected
storage.selectedMagaAsset = assetName;
// Reset all mago images to normal and stop any existing animations
for (var j = 0; j < magoImages.length; j++) {
tween.stop(magoImages[j], {
tint: true
});
magoImages[j].tint = 0xFFFFFF;
}
// Reset all maga images to normal and stop any existing animations
for (var j = 0; j < magaImages.length; j++) {
tween.stop(magaImages[j], {
tint: true
});
magaImages[j].tint = 0xFFFFFF;
}
// Reset all play buttons
for (var j = 0; j < magoPlayButtons.length; j++) {
tween.stop(magoPlayButtons[j], {
tint: true
});
magoPlayButtons[j].tint = 0xFFFFFF;
}
for (var j = 0; j < magaPlayButtons.length; j++) {
tween.stop(magaPlayButtons[j], {
tint: true
});
magaPlayButtons[j].tint = 0xFFFFFF;
}
// Create bright glowing effect for the selected maga asset
function createMagaGlow() {
tween(obj, {
tint: 0xFF69B4
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(obj, {
tint: 0xFFFFFF
}, {
duration: 600,
easing: tween.easeInOut,
onFinish: createMagaGlow
});
}
});
}
createMagaGlow();
updateContinueButton();
};
})(i, magaAssets[i]);
}
// Remove maga selection buttons - assets are now directly clickable
// Name input text
var nameText = new Text2('¿Cuál es tu nombre?', {
size: 60,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 3
});
nameText.anchor.set(0.5, 0.5);
nameText.x = 1024;
nameText.y = 1400;
setupContainer.addChild(nameText);
// Name input field (simulated with text)
var nameInputBg = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1550,
scaleX: 1.5,
scaleY: 0.8
});
setupContainer.addChild(nameInputBg);
var nameInputText = new Text2('Toca aquí para escribir tu nombre', {
size: 40,
fill: 0x888888
});
nameInputText.anchor.set(0.5, 0.5);
nameInputText.x = 1024;
nameInputText.y = 1550;
setupContainer.addChild(nameInputText);
// Continue button (initially hidden)
var continueButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1750,
alpha: 0.5
});
setupContainer.addChild(continueButton);
var continueText = new Text2('Continuar', {
size: 50,
fill: 0xFFFFFF
});
continueText.anchor.set(0.5, 0.5);
continueText.x = 1024;
continueText.y = 1750;
continueText.alpha = 0.5;
setupContainer.addChild(continueText);
// State tracking
var selectedGender = null;
var playerName = '';
function updateContinueButton() {
if (selectedGender && playerName.length > 0) {
continueButton.alpha = 1;
continueText.alpha = 1;
} else {
continueButton.alpha = 0.5;
continueText.alpha = 0.5;
}
}
// Mago button handlers removed - assets are now directly clickable
// Maga button handlers removed - assets are now directly clickable
// Name input handler (simple text input simulation)
nameInputBg.down = function () {
showNameInputDialog();
};
function showNameInputDialog() {
// Create a text input interface
var inputContainer = new Container();
// Semi-transparent background
var inputBg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
alpha: 0.8
});
inputContainer.addChild(inputBg);
// Input dialog box
var dialogBox = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 2.5,
scaleY: 2.0
});
inputContainer.addChild(dialogBox);
// Title text
var inputTitle = new Text2('Escribe tu nombre:', {
size: 50,
fill: 0xFFFFFF
});
inputTitle.anchor.set(0.5, 0.5);
inputTitle.x = 1024;
inputTitle.y = 1200;
inputContainer.addChild(inputTitle);
// Current name display
var currentNameText = new Text2(playerName || '', {
size: 60,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 3
});
currentNameText.anchor.set(0.5, 0.5);
currentNameText.x = 1024;
currentNameText.y = 1300;
inputContainer.addChild(currentNameText);
// Keyboard using Teclado asset
var keyboard = LK.getAsset('Teclado', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1500,
scaleX: 8.0,
scaleY: 6.0
});
inputContainer.addChild(keyboard);
// Make the entire keyboard clickable for functionality
keyboard.down = function (x, y, obj) {
// Convert local click coordinates to determine which key was pressed
var localPos = keyboard.toLocal({
x: x,
y: y
});
var keyboardWidth = 100 * keyboard.scaleX; // Original width * scale
var keyboardHeight = 100 * keyboard.scaleY; // Original height * scale
// Normalize coordinates to 0-1 range
var normalizedX = (localPos.x + keyboardWidth / 2) / keyboardWidth;
var normalizedY = (localPos.y + keyboardHeight / 2) / keyboardHeight;
// Define keyboard layout based on typical QWERTY layout
var keyRows = ['QWERTYUIOP', 'ASDFGHJKL', 'ZXCVBNM'];
// Determine which row was clicked (3 rows of keys)
var rowIndex = Math.floor(normalizedY * 3);
if (rowIndex >= 0 && rowIndex < keyRows.length) {
var row = keyRows[rowIndex];
var keyIndex = Math.floor(normalizedX * row.length);
if (keyIndex >= 0 && keyIndex < row.length) {
var letter = row[keyIndex];
if (playerName.length < 15) {
playerName += letter;
currentNameText.setText(playerName);
}
}
}
};
// Letter buttons grid (invisible but functional over the keyboard)
var letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var letterButtons = [];
var buttonsPerRow = 9;
var rows = Math.ceil(letters.length / buttonsPerRow);
for (var i = 0; i < letters.length; i++) {
var row = Math.floor(i / buttonsPerRow);
var col = i % buttonsPerRow;
var startX = 1024 - buttonsPerRow * 60 / 2;
var letterButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: startX + col * 60,
y: 1400 + row * 60,
scaleX: 0.4,
scaleY: 0.4,
alpha: 0
});
(function (letter) {
letterButton.down = function () {
if (playerName.length < 15) {
// Limit name length
playerName += letter;
currentNameText.setText(playerName);
}
};
})(letters[i]);
inputContainer.addChild(letterButton);
letterButtons.push(letterButton);
}
// Backspace button
var backspaceButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 800,
y: 1600,
scaleX: 0.8,
scaleY: 0.6
});
inputContainer.addChild(backspaceButton);
var backspaceText = new Text2('BORRAR', {
size: 35,
fill: 0xFFFFFF
});
backspaceText.anchor.set(0.5, 0.5);
backspaceText.x = 800;
backspaceText.y = 1600;
inputContainer.addChild(backspaceText);
backspaceButton.down = function () {
if (playerName.length > 0) {
playerName = playerName.slice(0, -1);
currentNameText.setText(playerName);
}
};
// Confirm button
var confirmButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1248,
y: 1600,
scaleX: 0.8,
scaleY: 0.6
});
inputContainer.addChild(confirmButton);
var confirmText = new Text2('CONFIRMAR', {
size: 30,
fill: 0xFFFFFF
});
confirmText.anchor.set(0.5, 0.5);
confirmText.x = 1248;
confirmText.y = 1600;
inputContainer.addChild(confirmText);
confirmButton.down = function () {
if (playerName.length > 0) {
storage.playerName = playerName;
nameInputText.setText(playerName);
nameInputText.fill = 0xFFFFFF;
updateContinueButton();
game.removeChild(inputContainer);
}
};
game.addChild(inputContainer);
}
// Reset character selection button (positioned in bottom right corner)
var resetCharacterButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1700,
y: 2500,
scaleX: 0.8,
scaleY: 0.6
});
setupContainer.addChild(resetCharacterButton);
var resetCharacterText = new Text2('RESET CHARACTER', {
size: 25,
fill: 0xFFFFFF
});
resetCharacterText.anchor.set(0.5, 0.5);
resetCharacterText.x = 1700;
resetCharacterText.y = 2500;
setupContainer.addChild(resetCharacterText);
resetCharacterButton.down = function () {
// Reset all character selection data
storage.playerGender = "undefined";
storage.playerName = "undefined";
storage.selectedMagoAsset = "Mago";
storage.selectedMagaAsset = "Maga";
storage.hasPlayedAdventure = false;
storage.completedCourses = 0;
storage.currentCourse = 0;
storage.currentBattleInCourse = 0;
storage.playerMaxHealth = 10;
// Reset local variables
selectedGender = null;
playerName = '';
completedCourses = 0;
currentBattle = 0;
isFirstTimeAdventure = true;
// Reset UI state
nameInputText.setText('Toca aquí para escribir tu nombre');
nameInputText.fill = 0x888888;
// Reset all selection button states - removed since buttons no longer exist
// Reset all images and stop glowing animations
for (var j = 0; j < magoImages.length; j++) {
tween.stop(magoImages[j], {
tint: true
});
magoImages[j].tint = 0xFFFFFF;
}
for (var j = 0; j < magaImages.length; j++) {
tween.stop(magaImages[j], {
tint: true
});
magaImages[j].tint = 0xFFFFFF;
}
// Remove any existing border glow effects
for (var j = setupContainer.children.length - 1; j >= 0; j--) {
var child = setupContainer.children[j];
if (child.tint === 0xFFFF00 && child.alpha <= 1) {
setupContainer.removeChild(child);
}
}
// Reset all play buttons and stop glowing animations
for (var j = 0; j < magoPlayButtons.length; j++) {
tween.stop(magoPlayButtons[j], {
tint: true
});
magoPlayButtons[j].tint = 0xFFFFFF;
}
for (var j = 0; j < magaPlayButtons.length; j++) {
tween.stop(magaPlayButtons[j], {
tint: true
});
magaPlayButtons[j].tint = 0xFFFFFF;
}
updateContinueButton();
};
// Continue button handler
continueButton.down = function () {
if (selectedGender && playerName.length > 0) {
// Save player data to storage
storage.playerGender = selectedGender;
storage.playerName = playerName;
storage.hasPlayedAdventure = true;
isFirstTimeAdventure = false;
game.removeChild(setupContainer);
startTutorialBattle();
}
};
game.addChild(setupContainer);
}
function startCardSelectionPhase() {
cardSelectionPhase = true;
playerSelectedCard = null;
enemySelectedCard = null;
gameState = 'cardSelection';
// Reset card highlights
for (var i = 0; i < playerHand.length; i++) {
playerHand[i].tint = 0xFFFFFF;
}
// Show selection instructions - player selects first
selectionInstructions.setText('Selecciona una carta para tu turno - Turno ' + turnCounter);
selectionInstructions.alpha = 1;
turnText.setText('Selección de Cartas - Turno ' + turnCounter);
// Enemy automatically selects a card (hidden from player)
var enemySelection = currentEnemy.selectCard();
if (enemySelection) {
enemySelectedCard = enemySelection;
}
}
function checkCardSelections() {
if (playerSelectedCard && enemySelectedCard) {
// Player has selected their card, now determine turn order and execute
executeSelectedCards();
}
}
function executeSelectedCards() {
cardSelectionPhase = false;
selectionInstructions.alpha = 0;
// Check for priority effects
var playerHasPriority = playerSelectedCard.effect === 'priority';
var enemyHasPriority = enemySelectedCard.card.effect === 'priority';
var playerGoesFirst = false;
if (playerHasPriority && !enemyHasPriority) {
// Only player has priority
playerGoesFirst = true;
} else if (!playerHasPriority && enemyHasPriority) {
// Only enemy has priority
playerGoesFirst = false;
} else if (playerHasPriority && enemyHasPriority) {
// Both have priority - random decision
playerGoesFirst = Math.random() < 0.5;
} else {
// Neither has priority - random decision
playerGoesFirst = Math.random() < 0.5;
}
// Execute the selected cards in order
if (playerGoesFirst) {
gameState = 'playerTurn';
turnText.setText('Your Turn - Turn ' + turnCounter);
// Check if selected card is still available
if (playerHand.indexOf(playerSelectedCard) !== -1) {
// Card is available, play it immediately
playCard(playerSelectedCard);
} else {
// Selected card is no longer available, player must choose new card and attack immediately
handleCardUnavailable();
}
} else {
gameState = 'enemyTurn';
turnText.setText('Enemy Turn - Turn ' + turnCounter);
// Execute enemy's selected card first
LK.setTimeout(function () {
enemyPlayCard(enemySelectedCard.card, enemySelectedCard.index);
}, 1000);
}
}
function showGraduationAnimation() {
// Create graduation container
var gradContainer = new Container();
// Background
var gradBg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
tint: 0x000050
});
gradContainer.addChild(gradBg);
// Graduation text
var gradText = new Text2('GRADUATION!', {
size: 120,
fill: 0xFFD700,
font: "'Arial Black', Arial, sans-serif",
stroke: 0xFFFFFF,
strokeThickness: 6
});
gradText.anchor.set(0.5, 0.5);
gradText.x = 1024;
gradText.y = 1000;
gradText.alpha = 0;
gradText.scaleX = 0.1;
gradText.scaleY = 0.1;
gradContainer.addChild(gradText);
// Congratulations text
var congratsText = new Text2('¡Congratulations, Master Wizard!', {
size: 60,
fill: 0xFFFFFF,
font: "'Arial Black', Arial, sans-serif"
});
congratsText.anchor.set(0.5, 0.5);
congratsText.x = 1024;
congratsText.y = 1200;
congratsText.alpha = 0;
gradContainer.addChild(congratsText);
// Academy building as graduation cap
var academy = LK.getAsset('academy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2,
tint: 0xFFD700
});
academy.x = 1024;
academy.y = 600;
academy.alpha = 0;
academy.rotation = -Math.PI / 4;
gradContainer.addChild(academy);
// Fireworks effects
var fireworks = [];
for (var i = 0; i < 8; i++) {
var firework = LK.getAsset('academy', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3,
tint: [0xFF0000, 0x00FF00, 0x0000FF, 0xFFFF00, 0xFF00FF, 0x00FFFF, 0xFFD700, 0xFF4500][i]
});
var angle = i / 8 * Math.PI * 2;
firework.x = 1024 + Math.cos(angle) * 400;
firework.y = 1366 + Math.sin(angle) * 400;
firework.alpha = 0;
firework.scaleX = 0.1;
firework.scaleY = 0.1;
gradContainer.addChild(firework);
fireworks.push(firework);
}
game.addChild(gradContainer);
// Animate academy
tween(academy, {
alpha: 1,
rotation: 0
}, {
duration: 1000,
easing: tween.elasticOut
});
// Animate main text
LK.setTimeout(function () {
tween(gradText, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 1200,
easing: tween.bounceOut
});
}, 500);
// Animate congratulations text
LK.setTimeout(function () {
tween(congratsText, {
alpha: 1
}, {
duration: 800,
easing: tween.easeOut
});
}, 1500);
// Animate fireworks
LK.setTimeout(function () {
for (var i = 0; i < fireworks.length; i++) {
(function (firework, delay) {
LK.setTimeout(function () {
tween(firework, {
alpha: 1,
scaleX: 0.5,
scaleY: 0.5,
rotation: Math.PI * 4
}, {
duration: 1000,
easing: tween.easeOut
});
}, delay);
})(fireworks[i], i * 100);
}
}, 2000);
// Continuous celebration effects
function celebrationPulse() {
tween(gradText, {
scaleX: 1.1,
scaleY: 1.1,
tint: 0xFF69B4
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(gradText, {
scaleX: 1,
scaleY: 1,
tint: 0xFFD700
}, {
duration: 800,
easing: tween.easeInOut,
onFinish: celebrationPulse
});
}
});
}
LK.setTimeout(celebrationPulse, 3000);
// Remove animation after 8 seconds
LK.setTimeout(function () {
tween(gradContainer, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
game.removeChild(gradContainer);
}
});
}, 8000);
}
// Initialize game
initializeDeck();
initializeEnemies();
createMenu();
// Play menu music with fade in
LK.playMusic('Academia', {
fade: {
start: 0,
end: 1,
duration: 1000
}
});
// Track last health state for music transitions
var lastHealthState = 'normal'; // 'normal' or 'low'
game.update = function () {
// Game loop handled by turn-based system
// Check for health-based music transitions during battle
if (gameState === 'battle' || gameState === 'playerTurn' || gameState === 'enemyTurn') {
var currentHealthState = playerHealth < 5 ? 'low' : 'normal';
// Only change music if health state changed
if (currentHealthState !== lastHealthState) {
if (currentHealthState === 'low') {
// Switch to tense music when health drops below 5
LK.playMusic('Poca_vida', {
fade: {
start: 0,
end: 1,
duration: 500
}
});
} else {
// Switch back to normal battle music when health is 5 or above
var normalMusic = 'Academia3'; // Default battle music
if (gameMode === 'roguelike') {
normalMusic = 'Roguelite';
}
LK.playMusic(normalMusic, {
fade: {
start: 0,
end: 1,
duration: 500
}
});
}
lastHealthState = currentHealthState;
}
}
};
function handleCardUnavailable() {
// Show message that selected card is unavailable
selectionInstructions.setText('Tu carta seleccionada ya no está disponible. Elige una nueva carta.');
selectionInstructions.alpha = 1;
turnText.setText('Elige Nueva Carta - Turno ' + turnCounter);
// Reset card selection state but stay in player turn
cardSelectionPhase = true;
playerSelectedCard = null;
// Reset card highlights
for (var i = 0; i < playerHand.length; i++) {
playerHand[i].tint = 0xFFFFFF;
}
}
function startPlaza() {
gameState = 'plaza';
game.removeChild(menuContainer);
createPlaza();
}
function createPlaza() {
var plazaContainer = new Container();
// Enhanced plaza background with more detail
var plazaBg = LK.getAsset('Plaza_general', {
anchorX: 0,
anchorY: 0,
scaleX: 20.48,
scaleY: 27.32
});
plazaContainer.addChild(plazaBg);
// Add decorative elements to make plaza feel more alive
var fountain = LK.getAsset('academy', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1000,
scaleX: 1.5,
scaleY: 1.5,
tint: 0x87CEEB
});
plazaContainer.addChild(fountain);
// Animated fountain effect
function animateFountain() {
tween(fountain, {
scaleX: 1.6,
scaleY: 1.6,
alpha: 0.8
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(fountain, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 1
}, {
duration: 2000,
easing: tween.easeInOut,
onFinish: animateFountain
});
}
});
}
animateFountain();
// Zone portals - positioned around the plaza
var zonePortals = [];
// Battle Academy Portal (North)
var battlePortal = LK.getAsset('academy', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 600,
scaleX: 1.2,
scaleY: 1.2,
tint: 0xFF4500
});
plazaContainer.addChild(battlePortal);
zonePortals.push({
portal: battlePortal,
name: 'Academia de Batalla',
type: 'battle'
});
// Library Portal (East)
var libraryPortal = LK.getAsset('Biblioteca', {
anchorX: 0.5,
anchorY: 0.5,
x: 1600,
y: 1200,
scaleX: 8.0,
scaleY: 8.0,
tint: 0x8A2BE2
});
plazaContainer.addChild(libraryPortal);
zonePortals.push({
portal: libraryPortal,
name: 'Gran Biblioteca',
type: 'library'
});
// Arena Portal (West)
var arenaPortal = LK.getAsset('Arena', {
anchorX: 0.5,
anchorY: 0.5,
x: 450,
y: 1200,
scaleX: 0.8,
scaleY: 0.8,
tint: 0xFFD700
});
plazaContainer.addChild(arenaPortal);
zonePortals.push({
portal: arenaPortal,
name: 'Arena de Duelos',
type: 'arena'
});
// Dungeon Portal (South)
var dungeonPortal = LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1800,
scaleX: 0.8,
scaleY: 0.8,
tint: 0x2F4F4F
});
plazaContainer.addChild(dungeonPortal);
zonePortals.push({
portal: dungeonPortal,
name: 'Mazmorras Profundas',
type: 'dungeon'
});
// Animate all portals with pulsing glow
for (var i = 0; i < zonePortals.length; i++) {
(function (portalData, delay) {
function animatePortal() {
tween(portalData.portal, {
scaleX: portalData.portal.scaleX * 1.1,
scaleY: portalData.portal.scaleY * 1.1,
alpha: 0.7
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(portalData.portal, {
scaleX: portalData.portal.scaleX / 1.1,
scaleY: portalData.portal.scaleY / 1.1,
alpha: 1
}, {
duration: 1500,
easing: tween.easeInOut,
onFinish: animatePortal
});
}
});
}
LK.setTimeout(animatePortal, delay);
})(zonePortals[i], i * 500);
}
// Determine which character sprite to use based on selection
var selectedCharacterAsset = 'Mago'; // Default fallback
if (storage.playerGender === 'mago') {
selectedCharacterAsset = storage.selectedMagoAsset || 'Mago';
} else if (storage.playerGender === 'maga') {
selectedCharacterAsset = storage.selectedMagaAsset || 'Maga';
}
// Create player character in plaza with enhanced movement
var playerCharacter = LK.getAsset(selectedCharacterAsset, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3.0,
scaleY: 3.0
});
playerCharacter.x = 1024;
playerCharacter.y = 1366;
playerCharacter.lastX = playerCharacter.x;
playerCharacter.lastY = playerCharacter.y;
plazaContainer.addChild(playerCharacter);
// Character movement variables with improved physics
var characterSpeed = 12;
var isDragging = false;
var targetX = playerCharacter.x;
var targetY = playerCharacter.y;
var movementSmoothing = 0.15;
// Enhanced plaza title with character greeting
var plazaTitle = new Text2('Plaza Central - ¡Bienvenido ' + (storage.playerName || 'Aventurero') + '!', {
size: 50,
fill: 0xFFD700,
stroke: 0x000000,
strokeThickness: 3
});
plazaTitle.anchor.set(0.5, 0.5);
plazaTitle.x = 1024;
plazaTitle.y = 120;
plazaContainer.addChild(plazaTitle);
// Zone information display
var zoneInfoText = new Text2('Explora los portales para acceder a diferentes zonas', {
size: 35,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 2
});
zoneInfoText.anchor.set(0.5, 0.5);
zoneInfoText.x = 1024;
zoneInfoText.y = 2500;
plazaContainer.addChild(zoneInfoText);
// Current zone indicator (shows when near portals)
var currentZoneIndicator = new Text2('', {
size: 45,
fill: 0x00FF00,
stroke: 0x000000,
strokeThickness: 3
});
currentZoneIndicator.anchor.set(0.5, 0.5);
currentZoneIndicator.x = 1024;
currentZoneIndicator.y = 250;
currentZoneIndicator.alpha = 0;
plazaContainer.addChild(currentZoneIndicator);
// Battle access button (repositioned)
var battleButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1850,
y: 400,
scaleX: 0.8,
scaleY: 0.6
});
plazaContainer.addChild(battleButton);
var battleText = new Text2('BATALLA', {
size: 35,
fill: 0xFFFFFF
});
battleText.anchor.set(0.5, 0.5);
battleText.x = 1850;
battleText.y = 400;
plazaContainer.addChild(battleText);
battleButton.down = function () {
currentBattle = completedCourses * 3;
game.removeChild(plazaContainer);
startBattle();
};
// Back to menu button (repositioned)
var menuButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1850,
y: 300,
scaleX: 0.8,
scaleY: 0.6
});
plazaContainer.addChild(menuButton);
var menuText = new Text2('MENÚ', {
size: 35,
fill: 0xFFFFFF
});
menuText.anchor.set(0.5, 0.5);
menuText.x = 1850;
menuText.y = 300;
plazaContainer.addChild(menuText);
menuButton.down = function () {
gameState = 'menu';
game.removeChild(plazaContainer);
createMenu();
LK.playMusic('Academia', {
fade: {
start: 0,
end: 1,
duration: 500
}
});
};
// Enhanced movement controls with portal interaction
plazaContainer.move = function (x, y, obj) {
if (isDragging) {
targetX = x;
targetY = y;
}
};
plazaContainer.down = function (x, y, obj) {
isDragging = true;
targetX = x;
targetY = y;
};
plazaContainer.up = function (x, y, obj) {
isDragging = false;
};
// Enhanced character movement with portal detection
function updateCharacterMovement() {
// Store last position for direction detection
playerCharacter.lastX = playerCharacter.x;
playerCharacter.lastY = playerCharacter.y;
// Smooth movement towards target
var deltaX = targetX - playerCharacter.x;
var deltaY = targetY - playerCharacter.y;
var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance > 5) {
var moveX = deltaX * movementSmoothing;
var moveY = deltaY * movementSmoothing;
// Add movement speed limit
var moveDistance = Math.sqrt(moveX * moveX + moveY * moveY);
if (moveDistance > characterSpeed) {
moveX = moveX / moveDistance * characterSpeed;
moveY = moveY / moveDistance * characterSpeed;
}
playerCharacter.x += moveX;
playerCharacter.y += moveY;
// Character facing direction based on movement
if (Math.abs(moveX) > 1) {
if (moveX > 0) {
playerCharacter.scaleX = Math.abs(playerCharacter.scaleX);
} else {
playerCharacter.scaleX = -Math.abs(playerCharacter.scaleX);
}
}
}
// Keep character within enhanced plaza bounds
playerCharacter.x = Math.max(150, Math.min(1898, playerCharacter.x));
playerCharacter.y = Math.max(350, Math.min(2550, playerCharacter.y));
// Check proximity to portals and show zone information
var nearPortal = null;
var minDistance = Number.MAX_VALUE;
for (var i = 0; i < zonePortals.length; i++) {
var portal = zonePortals[i];
var portalDistance = Math.sqrt(Math.pow(playerCharacter.x - portal.portal.x, 2) + Math.pow(playerCharacter.y - portal.portal.y, 2));
if (portalDistance < 200 && portalDistance < minDistance) {
minDistance = portalDistance;
nearPortal = portal;
}
}
// Update zone indicator based on proximity
if (nearPortal) {
var proximityText = 'Cerca de: ' + nearPortal.name;
if (minDistance < 100) {
proximityText += ' - ¡Toca para entrar!';
// Make portal glow brighter when very close
tween(nearPortal.portal, {
tint: 0xFFFFFF,
scaleX: nearPortal.portal.scaleX * 1.2,
scaleY: nearPortal.portal.scaleY * 1.2
}, {
duration: 200
});
} else {
// Reset portal appearance when not very close
tween(nearPortal.portal, {
tint: nearPortal.portal.tint,
scaleX: nearPortal.portal.scaleX / 1.2,
scaleY: nearPortal.portal.scaleY / 1.2
}, {
duration: 200
});
}
currentZoneIndicator.setText(proximityText);
currentZoneIndicator.alpha = 1;
} else {
currentZoneIndicator.alpha = 0;
}
}
// Portal interaction system
function checkPortalInteraction() {
for (var i = 0; i < zonePortals.length; i++) {
var portal = zonePortals[i];
var distance = Math.sqrt(Math.pow(playerCharacter.x - portal.portal.x, 2) + Math.pow(playerCharacter.y - portal.portal.y, 2));
if (distance < 100) {
// Create interaction for different portal types
switch (portal.type) {
case 'battle':
// Flash portal and start battle
tween(portal.portal, {
tint: 0xFFFFFF,
scaleX: portal.portal.scaleX * 1.5,
scaleY: portal.portal.scaleY * 1.5
}, {
duration: 300,
onFinish: function onFinish() {
currentBattle = completedCourses * 3;
game.removeChild(plazaContainer);
startBattle();
}
});
break;
case 'library':
showZoneMessage('La Gran Biblioteca está en construcción...', 'Pronto podrás estudiar hechizos avanzados aquí');
break;
case 'arena':
showZoneMessage('La Arena de Duelos se abrirá pronto...', 'Aquí podrás enfrentarte a otros magos');
break;
case 'dungeon':
showZoneMessage('Las Mazmorras Profundas esperan...', 'Aventuras épicas te aguardan en el futuro');
break;
}
return true;
}
}
return false;
}
// Add click detection for portals
plazaContainer.down = function (x, y, obj) {
// Check if click is near any portal
var clickedPortal = false;
for (var i = 0; i < zonePortals.length; i++) {
var portal = zonePortals[i];
var distance = Math.sqrt(Math.pow(x - portal.portal.x, 2) + Math.pow(y - portal.portal.y, 2));
if (distance < 150) {
// Move character towards portal and interact
targetX = portal.portal.x;
targetY = portal.portal.y;
clickedPortal = true;
break;
}
}
if (!clickedPortal) {
isDragging = true;
targetX = x;
targetY = y;
}
};
// Enhanced movement update loop with portal checking
var movementInterval = LK.setInterval(function () {
updateCharacterMovement();
// Check for portal interaction when character stops moving
var deltaX = Math.abs(targetX - playerCharacter.x);
var deltaY = Math.abs(targetY - playerCharacter.y);
if (deltaX < 10 && deltaY < 10) {
checkPortalInteraction();
}
}, 16); // ~60fps
game.addChild(plazaContainer);
}
// Helper function to show zone messages
function showZoneMessage(title, description) {
var messageContainer = new Container();
// Semi-transparent background
var messageBg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
alpha: 0.9
});
messageContainer.addChild(messageBg);
// Message box
var messageBox = LK.getAsset('Cuadro_dialogo', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1366,
scaleX: 8.0,
scaleY: 5.0
});
messageContainer.addChild(messageBox);
// Title text
var titleText = new Text2(title, {
size: 50,
fill: 0x000000,
stroke: 0xFFFFFF,
strokeThickness: 2
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 1024;
titleText.y = 1250;
messageContainer.addChild(titleText);
// Description text
var descText = new Text2(description, {
size: 40,
fill: 0x000000
});
descText.anchor.set(0.5, 0.5);
descText.x = 1024;
descText.y = 1350;
messageContainer.addChild(descText);
// Close button
var closeButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1500,
scaleX: 0.8,
scaleY: 0.6
});
messageContainer.addChild(closeButton);
var closeText = new Text2('CERRAR', {
size: 35,
fill: 0xFFFFFF
});
closeText.anchor.set(0.5, 0.5);
closeText.x = 1024;
closeText.y = 1500;
messageContainer.addChild(closeText);
closeButton.down = function () {
game.removeChild(messageContainer);
};
// Auto-close after 5 seconds
LK.setTimeout(function () {
if (messageContainer.parent) {
game.removeChild(messageContainer);
}
}, 5000);
game.addChild(messageContainer);
}
Un bote de agua oxigenada. In-Game asset. 2d. High contrast. No shadows
Pila de cartas mágicas bocabajo. In-Game asset. 2d. High contrast. No shadows
Barra de vida. In-Game asset. 2d. High contrast. No shadows
Botón de videojuegos sin nada escrito. In-Game asset. 2d. High contrast. No shadows
Hombre de nieve con pipa y manos de ramas y tres botones negros verticalmente algo realista. In-Game asset. 2d. High contrast. No shadows
Llama fuego, algo realista. In-Game asset. 2d. High contrast. No shadows
Luna roja sobre cielo nocturno, sin bordes y algo realista, que se aprecie el fondo. In-Game asset. 2d. High contrast. No shadows
Terremoto, algo realista. In-Game asset. 2d. High contrast. No shadows
Howgarts, algo realista. In-Game asset. 2d. High contrast. No shadows
Noche estrellada, algo realista. In-Game asset. 2d. High contrast. No shadows
Marea, algo realista, sin nombre. In-Game asset. 2d. High contrast. No shadows
Mechero encendido, algo realista. In-Game asset. 2d. High contrast. No shadows
Nevada, evento atmosférico, algo realista. In-Game asset. 2d. High contrast. No shadows
Arboleda, con fondo, cielo nocturno, algo realista. In-Game asset. 2d. High contrast. No shadows
Aullidos del bosque, algo realista. In-Game asset. 2d. High contrast. No shadows
Bola de nieve, algo realista. In-Game asset. 2d. High contrast. No shadows
Brasas con llama, algo realista. In-Game asset. 2d. High contrast. No shadows
Fénix envuelto en llamas mirando al frente, algo realista, pero no poner un pájaro rojo cualquiera. In-Game asset. 2d. High contrast. No shadows
Pedrada, algo realista. In-Game asset. 2d. High contrast. No shadows
Pirata con sombrero pirata y garfio, en barco pirata, navegando sobre el mar en una noche estrellada, algo realista, pero fuera de lo caricaturesco. In-Game asset. 2d. High contrast. No shadows
Rama, algo realista. In-Game asset. 2d. High contrast. No shadows
Sarcófago, algo realista. In-Game asset. 2d. High contrast. No shadows
Tormenta de arena, algo realista. In-Game asset. 2d. High contrast. No shadows
Vendaval, algo realista. In-Game asset. 2d. High contrast. No shadows
Ventisca, algo realista. In-Game asset. 2d. High contrast. No shadows
Vida extra, algo realista. In-Game asset. 2d. High contrast. No shadows
Mago astral. In-Game asset. 2d. High contrast. No shadows
Mago principiante con sombrero verde y marrón. In-Game asset. 2d. High contrast. No shadows
Libro en la mano libre y cambiar la esfera de su varita por una estrella
Cambiar la esfera por una estrella y usar el diseño de varita de, cuarto asset
Mago con sombrero naranja y marrón sin barba con varita con estrella, de cuerpo entero. In-Game asset. 2d. High contrast. No shadows
Light wizard con cuerpo entero. In-Game asset. 2d. High contrast. No shadows
Darkness wizard, con cuerpo entero. In-Game asset. 2d. High contrast. No shadows
Quitar fondo blanco
Mago con sombrero naranja y marrón sin barba con varita con estrella, de cuerpo entero. In-Game asset. 2d. High contrast. No shadows
Mago con sombrero naranja y marrón sin barba con varita con estrella, de cuerpo entero. In-Game asset. 2d. High contrast. No shadows
Mago con sombrero rojo y marrón sin barba con varita con estrella, de cuerpo entero. In-Game asset. 2d. High contrast. No shadows
Mago con sombrero morado y marrón sin barba con varita con estrella, de cuerpo entero. In-Game asset. 2d. High contrast. No shadows
Mago con sombrero morado oscuro y marrón sin barba con varita con estrella, de cuerpo entero. In-Game asset. 2d. High contrast. No shadows
Momia realista en 2D. In-Game asset. 2d. High contrast. No shadows
Montículo de arena realista 2D. In-Game asset. 2d. High contrast. No shadows
Nubes moradas de las que cae un rayo amarillo más realista en 2D. In-Game asset. 2d. High contrast. No shadows
Muralla hecha de piedras y rocas realista en 2D. In-Game asset. 2d. High contrast. No shadows
Niebla muy realista en 2D. In-Game asset. 2d. High contrast. No shadows
Esquirla morada de hielo muy realista en 2D. In-Game asset. 2d. High contrast. No shadows
Mazmorra de ladrillos agrietados con lianas y escaleras que suben muy realista en 2D. In-Game asset. 2d. High contrast. No shadows
Oxigeno muy realista en 2D. In-Game asset. 2d. High contrast. No shadows
Clase mágica simple muy realista en 2D. In-Game asset. 2d. High contrast. No shadows
Clase estudiantil mágica algo más detallada muy realista en 2D. In-Game asset. 2d. High contrast. No shadows