User prompt
This is not working: function checkSelectionState() { // Check if any userTurnBackground elements exist (indicating messages are on screen) var noUserTurnMessages = true; for (var i = 0; i < game.children.length; i++) { if (game.children[i].texture && game.children[i].texture.baseTexture && game.children[i].texture.baseTexture.imageUrl && game.children[i].texture.baseTexture.imageUrl.includes('userTurnBackground')) { noUserTurnMessages = false; break; } } // Check for coin flip elements (indicating coin is flipping) var noCoinFlip = true; for (var i = 0; i < game.children.length; i++) { if (game.children[i].children && game.children[i].children.length > 0) { for (var j = 0; j < game.children[i].children.length; j++) { var child = game.children[i].children[j]; if (child.texture && child.texture.baseTexture && child.texture.baseTexture.imageUrl && (child.texture.baseTexture.imageUrl.includes('coin') || child.texture.baseTexture.imageUrl.includes('coinHeads') || child.texture.baseTexture.imageUrl.includes('coinTails'))) { noCoinFlip = false; break; } } if (!noCoinFlip) { break; } } } return playerGoesFirst && !isWheelSpinning && !showingIntro && clicksDisabled === false && noUserTurnMessages && noCoinFlip && gameState !== 'battle'; } To track if there is a message, please create a global variable showingMessage (true|false). True when it shows the message, false onFinish. And just check that global var. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please redo gameState = selection. gameState = selection only if: - No coin flips are happening (after onFinish) - No intro screen (after oNFinish) - No battle screen (after onFinish) - No userTurNBackground message is shown (after onFinish) - If it's the turn to select of the user. If gameState = selection, allow hovering and clicking element buttons. If all other cases, disable them. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
gameState = 'selection' is only when the following conditionsa re met: 1. It's the user turn, not the AI's 2. There are no userTurnMessages on screen 3. It's not spinning the wheel 4. It's not flipping a coint 5. It's not battle screen. 6. The coin has stated it's the user turn
User prompt
And now, again for the 10x time, once you hover one element, another gets hovered instead 🔄 Fix hover effect coordinate mapping to prevent hovering on wrong elements
User prompt
And now, again for the 10x time, once you hover one element, another gets hovered instead
User prompt
Ok now you have broken disabling the element buttons. Please fix. These are the conditions: - ALl buttons are disabled by default (hover and click) - You only enable them if it's the user turn AND the popup of the "Your turn" as alrady finished showing.
User prompt
Ok now you introduced a bug: when the hovering is enabled, if I hover on one element it starts hovering effects on an element which is several positions after. Please analyse why and fix.
User prompt
Ok now you introduced a bug: when the hovering is enabled, if I hover on one element it starts hovering effects on an element which is several positions after. Please analyse why and fix.
User prompt
Ok now you introduced a bug: when the hovering is enabled, if I hover on one element it starts hovering effects on an element which is several positions after. Please analyse why and fix.
User prompt
Ok now you introduced a bug: when the hovering is enabled, if I hover on one element it starts hovering effects on another element. It seems you are remembering the position of the wheel and then the wheel spins and you don't refresh that or something like that
User prompt
No. It's not working at all. I can click any element, element button, anything! I can select elements! while there are messages with userTurnBackground still showing, on top!!! And even without user turn! Fix ALL OF THAT! You can only click, hover, etc. element/elementButtons exclusively during User turn after the Your turn messages has hidden. Disable them including hover in all the other cases!
User prompt
No. It's not working at all. I can click any element, element button, anything! I can select elements! while there are messages with userTurnBackground still showing, on top!!! And even without user turn! Fix ALL OF THAT! You can't only click, hover, etc. element/elementButtons exclusively during User turn after the Your turn messages has hidden. Disable them including hover in all the other cases!
User prompt
I still can click elemnts when any message with userTurnBackground is shown. Please disable clicking!
User prompt
Disable clicking the elements unless the YOUR TURN message has appeared and finished disappeared
Code edit (3 edits merged)
Please save this source code
User prompt
Divide Flipping coin to determine turn order... into 2 messges. "Flipping coin..." (-250 px left to the coin) and "...to determine turn order" (300px right the coin)) ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
The flipping of the coin now is hidden behind the "Flipping the coin ... " message, probably because it's instantiated first and gets a index in the container array smaller. Make sure the coin flip always stays on top of everything else, instantiate it last.
User prompt
The flipping of the coin how is hidden behind the "Flipping the coin ... " message, probably because it's instantiated first and gets a index in the container array smaller. Make sure the coin flip always stays on top of everything else, instantiate it last.
Code edit (1 edits merged)
Please save this source code
User prompt
Message "Heads you go first" or "Tails - AI goes first" should be split in two. First, 300 px LEFT TO THE COIN, "Head" or "Tails". Second, 300px RIGHT to the coin, the rest of the message (You go first or AI goes first). ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Ok all the messages with the userTurnBackground - they should be centered horizontally and vertically on the screen, all of them same position. Also, they should last 1 second and dissapear. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Ok all the messages with the userTurnBackground - they should be centered horizontally and vertically on the screen, all of them same position. Also, they should last 1 second and dissapear. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
All the messages "Flipping coin ... " , "Ai goes first", "Wheel refreshed", ... "Defensive shield", etc. should be shown exactly as userTurnBackground and userTurnBackgroundMessage ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
All the messages "Flipping coin ... " , "Ai goes first", "Wheel refreshed", ... "Defensive shield", etc. should be shown exactly as userTurnBackground and userTurnBackgroundMessage ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BattleElement = Container.expand(function (elementType, isPlayer, comboElements) { var self = Container.call(this); self.elementType = elementType; self.isPlayer = isPlayer; var background = self.attachAsset('elementButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); var icon = self.attachAsset(elementType, { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); var label = new Text2(elementType.charAt(0).toUpperCase() + elementType.slice(1), { size: 32, fill: 0xffffff, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3 }); label.anchor.set(0.5, 0.5); label.y = 220; self.addChild(label); // Add second element label if this is a combo self.addSecondElementLabel = function (secondElementType) { var secondLabel = new Text2(secondElementType.charAt(0).toUpperCase() + secondElementType.slice(1), { size: 20, fill: 0xffffff, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 2 }); secondLabel.anchor.set(0.5, 0.5); secondLabel.y = -180; secondLabel.x = isPlayer ? 130 : -80; self.addChild(secondLabel); }; self.playWinAnimation = function () { var effect = self.attachAsset('winEffect', { anchorX: 0.5, anchorY: 0.5, alpha: 0.7 }); tween(self, { scaleX: 1.3, scaleY: 1.3 }, { duration: 500 }); tween(effect, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 800, onFinish: function onFinish() { self.removeChild(effect); } }); }; self.playLoseAnimation = function () { var effect = self.attachAsset('loseEffect', { anchorX: 0.5, anchorY: 0.5, alpha: 0.7 }); tween(self, { scaleX: 0.7, scaleY: 0.7, alpha: 0.5 }, { duration: 500 }); tween(effect, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 800, onFinish: function onFinish() { self.removeChild(effect); } }); }; return self; }); var ElementButton = Container.expand(function (elementType, index) { var self = Container.call(this); self.elementType = elementType; self.index = index; self.isSelected = false; self.isDisabled = false; var button = self.attachAsset('elementButton', { anchorX: 0.5, anchorY: 0.5 }); var icon = self.attachAsset(elementType, { anchorX: 0.5, anchorY: 0.5 }); var label = new Text2(elementType.charAt(0).toUpperCase() + elementType.slice(1), { size: 42, fill: 0xffffff, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 2 }); label.anchor.set(0.5, 0.5); self.addChild(label); // Store reference to the label for easy access self.textLabel = label; // Position text toward wheel center self.positionTextTowardCenter = function (centerX, centerY) { var angle = Math.atan2(self.y - centerY, self.x - centerX); var offsetX = Math.cos(angle) * textOffsetTowardCenter * 1.3; var offsetY = Math.sin(angle) * textOffsetTowardCenter * 1.3; label.x = -offsetX; label.y = -offsetY; }; self.setSelected = function (selected) { self.isSelected = selected; if (selected) { button.tint = 0xffd700; tween(self, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200 }); } else { button.tint = 0xffffff; tween(self, { scaleX: 1.0, scaleY: 1.0 }, { duration: 200 }); } }; self.setDisabled = function (disabled) { self.isDisabled = disabled; if (disabled) { button.alpha = 0.5; icon.alpha = 0.5; label.alpha = 0.5; } else { button.alpha = 1.0; icon.alpha = 1.0; label.alpha = 1.0; } }; self.down = function (x, y, obj) { // Disable clicking during intro, when clicks are explicitly disabled, or when any message is showing if (showingIntro || clicksDisabled || gameState !== 'selection') { return; } // Only allow clicks when not disabled and during selection state if (gameState === 'selection' && !self.isDisabled && !clicksDisabled) { LK.getSound('select').play(); selectElement(self.elementType); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xffffff }); /**** * Game Code ****/ // Game state - start with intro var _defineProperty5; function _typeof5(o) { "@babel/helpers - typeof"; return _typeof5 = "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; }, _typeof5(o); } function _defineProperty7(e, r, t) { return (r = _toPropertyKey5(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey5(t) { var i = _toPrimitive5(t, "string"); return "symbol" == _typeof5(i) ? i : i + ""; } function _toPrimitive5(t, r) { if ("object" != _typeof5(t) || !t) { return t; } var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof5(i)) { return i; } throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } var showingIntro = true; var introContainer = null; var clicksDisabled = true; // Flag to disable clicks - starts disabled and only enabled after YOUR TURN message // Function to clear all hover effects when clicks are disabled function clearHoverEffects() { if (hoveredButton && !hoveredButton.isSelected && !hoveredButton.isDisabled) { // Stop any existing scale tweens tween.stop(hoveredButton, { scaleX: true, scaleY: true }); // Scale back to normal tween(hoveredButton, { scaleX: 1.0, scaleY: 1.0 }, { duration: 150 }); } hoveredButton = null; } // Create intro screen function createIntroScreen() { // Create purple background covering entire screen var introBg = LK.getAsset('elementIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 50, scaleY: 80 }); introBg.tint = 0x8B008B; // Purple color introBg.x = 0; introBg.y = 0; LK.gui.center.addChild(introBg); // Create intro container for all intro elements introContainer = new Container(); LK.gui.center.addChild(introContainer); // Create title text var titleText = new Text2('COMBO-NATION:\nAn alchemic experiment', { size: 80, fill: 0xFFFFFF, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 4, align: 'center' }); titleText.anchor.set(0.5, 0.5); titleText.x = 0; titleText.y = -400; introContainer.addChild(titleText); // Create playerHero on the left var introPlayerHero = LK.getAsset('playerHero', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); introPlayerHero.x = -300; introPlayerHero.y = 0; introContainer.addChild(introPlayerHero); // Create aiHero on the right, facing left var introAiHero = LK.getAsset('aiHero', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); introAiHero.x = 300; introAiHero.y = 0; introContainer.addChild(introAiHero); // Create bottom instruction text var instructText = new Text2('Tap the screen to start', { size: 48, fill: 0xFFFFFF, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3, align: 'center' }); instructText.anchor.set(0.5, 0.5); instructText.x = 0; instructText.y = 400; introContainer.addChild(instructText); // Add tween animations to heroes // Player hero gentle floating animation function animatePlayerHero() { tween(introPlayerHero, { y: introPlayerHero.y + 30, rotation: 0.1 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(introPlayerHero, { y: introPlayerHero.y - 30, rotation: -0.1 }, { duration: 2000, easing: tween.easeInOut, onFinish: animatePlayerHero }); } }); } // AI hero gentle floating animation (opposite phase) function animateAiHero() { tween(introAiHero, { y: introAiHero.y - 30, rotation: -0.1 }, { duration: 2000, easing: tween.easeInOut, onFinish: function onFinish() { tween(introAiHero, { y: introAiHero.y + 30, rotation: 0.1 }, { duration: 2000, easing: tween.easeInOut, onFinish: animateAiHero }); } }); } // Start animations animatePlayerHero(); animateAiHero(); // Store background reference for cleanup introContainer.introBg = introBg; } // Function to hide intro screen and start game function hideIntroScreen() { if (!showingIntro) { return; } showingIntro = false; // Fade out intro screen tween(introContainer, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { // Clean up intro elements if (introContainer.introBg) { introContainer.introBg.destroy(); } introContainer.destroy(); introContainer = null; // Re-enable clicks after a delay to prevent accidental clicks LK.setTimeout(function () { clicksDisabled = false; }, 2000); // 2000ms delay // Start the actual game startActualGame(); } }); } // Function to start the actual game after intro function startActualGame() { // Show all the game UI elements that were hidden during intro for (var i = 0; i < elementButtons.length; i++) { tween(elementButtons[i], { alpha: 1 }, { duration: 500 }); } if (wheelContainer) { tween(wheelContainer, { alpha: 1 }, { duration: 500 }); } tween(titleText, { alpha: 1 }, { duration: 500 }); tween(instructionText, { alpha: 1 }, { duration: 500 }); tween(comboDisplay, { alpha: 1 }, { duration: 500 }); tween(aiComboDisplay, { alpha: 1 }, { duration: 500 }); // Start game with coin flip after UI is shown LK.setTimeout(function () { startGameWithCoinFlip(); }, 600); } // Add background image - will be updated based on weather function _typeof4(o) { "@babel/helpers - typeof"; return _typeof4 = "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; }, _typeof4(o); } function _defineProperty6(e, r, t) { return (r = _toPropertyKey4(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey4(t) { var i = _toPrimitive4(t, "string"); return "symbol" == _typeof4(i) ? i : i + ""; } function _toPrimitive4(t, r) { if ("object" != _typeof4(t) || !t) { return t; } var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof4(i)) { return i; } throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } var _defineProperty3; function _typeof3(o) { "@babel/helpers - typeof"; return _typeof3 = "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; }, _typeof3(o); } function _defineProperty4(e, r, t) { return (r = _toPropertyKey3(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey3(t) { var i = _toPrimitive3(t, "string"); return "symbol" == _typeof3(i) ? i : i + ""; } function _toPrimitive3(t, r) { if ("object" != _typeof3(t) || !t) { return t; } var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof3(i)) { return i; } throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _typeof2(o) { "@babel/helpers - typeof"; return _typeof2 = "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; }, _typeof2(o); } function _defineProperty2(e, r, t) { return (r = _toPropertyKey2(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey2(t) { var i = _toPrimitive2(t, "string"); return "symbol" == _typeof2(i) ? i : i + ""; } function _toPrimitive2(t, r) { if ("object" != _typeof2(t) || !t) { return t; } var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof2(i)) { return i; } throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } 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 backgroundImage = null; var weatherEffectsContainer = null; // Container for weather effect particles // Function to create rain effect function createRainEffect() { var _loop = function _loop() { rainDrop = LK.getAsset('rain', { anchorX: 0.5, anchorY: 0, scaleX: 0.3, scaleY: 4, alpha: 0.3 }); rainDrop.tint = 0x87CEEB; // Light blue color rainDrop.x = Math.random() * 2200 - 100; // Spread across screen width // Start rain drops at random heights across the full screen for immediate coverage rainDrop.y = Math.random() * 3100 - 300; // Random Y from -300 to 2800 (full falling range) rainDrop.rotation = Math.PI / 12; // Slight angle for realistic rain weatherEffectsContainer.addChild(rainDrop); // Animate rain falling function animateRainDrop(drop) { var fallSpeed = 8 + Math.random() * 4; // Varying speeds var duration = (2800 - drop.y) / fallSpeed * 16; tween(drop, { y: 2800 // Fall below screen }, { duration: duration, // Speed-based duration easing: tween.linear, onFinish: function onFinish() { // Immediately reset to top without delay to ensure continuous falling drop.y = -100 - Math.random() * 200; // Start from above screen drop.x = Math.random() * 2200 - 100; // New random X position animateRainDrop(drop); // Continue animation immediately } }); } animateRainDrop(rainDrop); }, rainDrop; // Create multiple rain drops with staggered start positions for continuous effect for (var i = 0; i < 120; i++) { _loop(); } } // Function to create storm effect (rain + lightning flashes) function createStormEffect() { // Create rain first createRainEffect(); // Add lightning flashes function createLightningFlash() { var flash = LK.getAsset('elementIcon', { anchorX: 0.5, anchorY: 0.5, scaleX: 25, scaleY: 25, alpha: 0 }); flash.tint = 0xFFFFFF; // White flash flash.x = 1024; flash.y = 1366; // Create new weather effects container if it doesn't exist if (!weatherEffectsContainer) { weatherEffectsContainer = new Container(); game.addChild(weatherEffectsContainer); // Move weather effects right after background (index 1, since background is at index 0) game.setChildIndex(weatherEffectsContainer, 1); } weatherEffectsContainer.addChild(flash); tween(flash, { alpha: 0.3 }, { duration: 100, onFinish: function onFinish() { tween(flash, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { flash.destroy(); // Schedule next flash randomly LK.setTimeout(createLightningFlash, 3000 + Math.random() * 5000); } }); } }); } // Start lightning flashes LK.setTimeout(createLightningFlash, 2000 + Math.random() * 3000); } // Function to create wind effect function createWindEffect() { var _loop2 = function _loop2() { windParticle = LK.getAsset('wind', { anchorX: 0.5, anchorY: 0.5, //scaleX: 0.3 + Math.random() * 0.4, //scaleY: 0.3 + Math.random() * 0.4, alpha: 0.1 }); windParticle.tint = 0xE6E6FA; // Light lavender color windParticle.x = Math.random() * -300 - 100; // Start left of screen windParticle.y = Math.random() * 2732; weatherEffectsContainer.addChild(windParticle); // Animate wind movement function animateWindParticle(particle) { var windSpeed = 6 + Math.random() * 8; var verticalDrift = (Math.random() - 0.5) * 200; // Random vertical movement tween(particle, { x: 2200, // Move across screen y: particle.y + verticalDrift //rotation: Math.random() * Math.PI * 2 }, { duration: (2200 - particle.x) / windSpeed * 16, easing: tween.linear, onFinish: function onFinish() { // Reset to left side and continue particle.x = Math.random() * -300 - 100; particle.y = Math.random() * 2732; animateWindParticle(particle); } }); } animateWindParticle(windParticle); }, windParticle; // Create moving particles to show wind for (var i = 0; i < 40; i++) { _loop2(); } } // Function to create drought effect (heat shimmer/radiation) function createDroughtEffect() { var _loop3 = function _loop3() { heatParticle = LK.getAsset('radiation', { anchorX: 0.5, anchorY: 0, alpha: 0.4 }); heatParticle.x = Math.random() * 2200 - 100; // Spread across screen width // Start heat particles at random heights across the full screen for immediate coverage heatParticle.y = Math.random() * 3100 + 300; // Random Y from 300 to 3400 (full rising range) //heatParticle.rotation = -Math.PI / 12; // Slight angle opposite to rain for realistic heat rise weatherEffectsContainer.addChild(heatParticle); // Animate heat rising function animateHeatParticle(particle) { var riseSpeed = 8 + Math.random() * 4; // Varying speeds var duration = (particle.y + 300) / riseSpeed * 16; tween(particle, { y: -300 // Rise above screen }, { duration: duration, // Speed-based duration easing: tween.linear, onFinish: function onFinish() { // Immediately reset to bottom without delay to ensure continuous rising particle.y = 2900 + Math.random() * 200; // Start from below screen particle.x = Math.random() * 2200 - 100; // New random X position animateHeatParticle(particle); // Continue animation immediately } }); } animateHeatParticle(heatParticle); }, heatParticle; // Create multiple heat particles with staggered start positions for continuous effect for (var i = 0; i < 120; i++) { _loop3(); } } // Function to update background based on current weather function updateBackgroundForWeather() { // Remove existing background if it exists if (backgroundImage) { backgroundImage.destroy(); backgroundImage = null; } // Determine which background asset to use var backgroundAsset = 'background'; // default if (currentWeather === 'drought') { backgroundAsset = 'drought'; } else if (currentWeather === 'rainy') { backgroundAsset = 'rainy'; } else if (currentWeather === 'storm') { backgroundAsset = 'stormy'; } else if (currentWeather === 'windy') { backgroundAsset = 'windy'; } // Create new background with weather-appropriate asset backgroundImage = game.attachAsset(backgroundAsset, { anchorX: 0.5, anchorY: 0.5, scaleX: 1.137, scaleY: 1.013 }); backgroundImage.x = 1024; backgroundImage.y = 1366; // Move background to back game.setChildIndex(backgroundImage, 0); // Clear existing weather effects if (weatherEffectsContainer) { weatherEffectsContainer.destroy(); } weatherEffectsContainer = null; // Create new weather effects container weatherEffectsContainer = new Container(); weatherEffectsContainer.x = 0; weatherEffectsContainer.y = 0; game.addChild(weatherEffectsContainer); // Move weather effects right after background (index 1, since background is at index 0) game.setChildIndex(weatherEffectsContainer, 1); // Create weather-specific effects if (currentWeather === 'rainy') { createRainEffect(); } else if (currentWeather === 'storm') { createStormEffect(); } else if (currentWeather === 'windy') { createWindEffect(); } else if (currentWeather === 'drought') { createDroughtEffect(); } } // Initialize with default background updateBackgroundForWeather(); // Game elements and their relationships - wheel shows 16, but total pool is larger var allElements = ['rock', 'paper', 'scissors', 'water', 'fire', 'air', 'lightning', 'tree', 'gun', 'sponge', 'smoke', 'gas', 'metal', 'ice', 'sand', 'oil', 'ash', 'fungus', 'sun', 'moon', 'salt', 'plastic', 'leather', 'cloth', 'glass']; var elements = ['rock', 'paper', 'scissors', 'water', 'fire', 'air', 'lightning', 'tree', 'gun', 'sponge', 'smoke', 'gas', 'metal', 'ice', 'sand', 'oil']; // Wheel elements stay 16 // Element defeat relationships (each element defeats multiple others) var defeats = { // Rock 'rock': ['scissors', 'fire', 'ice', 'sponge', 'sand', 'glass', 'ash'], // Paper 'paper': ['rock', 'water', 'gas', 'gun', 'lightning', 'plastic', 'cloth'], // Scissors 'scissors': ['paper', 'sponge', 'gas', 'tree', 'air', 'cloth', 'leather'], // Water 'water': ['fire', 'rock', 'metal', 'sponge', 'sand', 'ash', 'salt'], // Fire 'fire': ['paper', 'scissors', 'tree', 'gas', 'ice', 'plastic', 'cloth'], // Air 'air': ['fire', 'gas', 'paper', 'sand', 'smoke', 'ash', 'fungus'], // Lightning 'lightning': ['water', 'air', 'sponge', 'metal', 'smoke', 'tree', 'glass'], // Tree 'tree': ['water', 'air', 'paper', 'sand', 'gun', 'fungus', 'leather'], // Gun 'gun': ['rock', 'scissors', 'air', 'fire', 'metal', 'plastic', 'glass'], // Sponge 'sponge': ['paper', 'water', 'fire', 'metal', 'gun', 'oil', 'salt'], // Smoke 'smoke': ['gun', 'tree', 'sponge', 'ice', 'oil', 'air', 'moon'], // Gas 'gas': ['water', 'lightning', 'smoke', 'oil', 'metal', 'plastic', 'air'], // Metal 'metal': ['scissors', 'air', 'rock', 'ice', 'smoke', 'lightning', 'gun'], // Ice 'ice': ['air', 'tree', 'scissors', 'water', 'lightning', 'fire', 'moon'], // Sand 'sand': ['fire', 'ice', 'lightning', 'sponge', 'smoke', 'glass', 'metal'], // Oil 'oil': ['sponge', 'gun', 'sand', 'tree', 'paper', 'plastic', 'water'], // New elements 'ash': ['tree', 'paper', 'cloth', 'air', 'fungus', 'leather', 'oil'], 'fungus': ['tree', 'paper', 'leather', 'cloth', 'oil', 'sponge', 'water'], 'sun': ['ice', 'moon', 'water', 'metal', 'plastic', 'ash', 'glass'], 'moon': ['sun', 'fire', 'lightning', 'ash', 'sand', 'rock', 'metal'], 'salt': ['ice', 'metal', 'fungus', 'tree', 'leather', 'sponge', 'water'], 'plastic': ['tree', 'paper', 'cloth', 'leather', 'metal', 'rock', 'glass'], 'leather': ['paper', 'cloth', 'sponge', 'water', 'ice', 'metal', 'tree'], 'cloth': ['water', 'oil', 'ash', 'sand', 'sponge', 'air', 'lightning'], 'glass': ['ice', 'paper', 'cloth', 'air', 'sponge', 'oil', 'water'] }; // Action keywords for how elements defeat others var actionKeywords = { 'rock': { 'scissors': 'crushes', 'fire': 'hardens against', 'ice': 'shatters', 'sponge': 'compacts', 'sand': 'forms' }, 'paper': { 'rock': 'wraps', 'water': 'absorbs', 'gas': 'captures', 'gun': 'covers', 'lightning': 'insulates against' }, 'scissors': { 'paper': 'cuts', 'sponge': 'snips', 'gas': 'ventilates', 'tree': 'trims', 'air': 'slices through' }, 'water': { 'fire': 'extinguishes', 'rock': 'erodes', 'metal': 'rusts', 'sponge': 'saturates', 'sand': 'washes away' }, 'fire': { 'paper': 'burns', 'scissors': 'melts', 'tree': 'incinerates', 'gas': 'ignites', 'ice': 'melts' }, 'air': { 'fire': 'feeds', 'gas': 'disperses', 'paper': 'carries', 'sand': 'blows', 'smoke': 'clears' }, 'lightning': { 'water': 'electrifies', 'air': 'ionizes', 'sponge': 'shocks', 'metal': 'conducts', 'smoke': 'disperses' }, 'tree': { 'water': 'absorbs', 'air': 'filters', 'paper': 'grows from', 'sand': 'roots in', 'gun': 'blocks' }, 'gun': { 'rock': 'shatters', 'scissors': 'shoots through', 'air': 'pushes', 'fire': 'ignites', 'metal': 'pierces' }, 'sponge': { 'paper': 'soaks', 'water': 'absorbs', 'fire': 'smothers', 'metal': 'corrodes', 'gun': 'dampens' }, 'smoke': { 'gun': 'jams', 'tree': 'obscures', 'sponge': 'clogs', 'ice': 'condenses', 'oil': 'mixes with' }, 'gas': { 'water': 'dissolves in', 'lightning': 'combusts with', 'smoke': 'displaces', 'oil': 'absorbs', 'metal': 'corrodes' }, 'metal': { 'scissors': 'dulls', 'air': 'oxidizes', 'rock': 'resists', 'ice': 'chills', 'smoke': 'tarnishes' }, 'ice': { 'air': 'freezes', 'tree': 'breaks', 'scissors': 'dulls', 'water': 'freezes', 'lightning': 'grounds' }, 'sand': { 'fire': 'smothers', 'ice': 'melts', 'lightning': 'vitrifies', 'sponge': 'absorbs', 'smoke': 'mixes with' }, 'oil': { 'sponge': 'saturates', 'gun': 'lubricates', 'sand': 'coats', 'tree': 'poisons', 'paper': 'stains', 'plastic': 'dissolves', 'water': 'floats on' }, // New elements 'ash': { 'tree': 'suffocates', 'paper': 'dirties', 'cloth': 'stains', 'air': 'pollutes', 'fungus': 'preserves against', 'leather': 'dries out', 'oil': 'absorbs' }, 'fungus': { 'tree': 'decomposes', 'paper': 'rots', 'leather': 'molds', 'cloth': 'spoils', 'oil': 'breaks down', 'sponge': 'infects', 'water': 'contaminates' }, 'sun': { 'ice': 'melts', 'moon': 'outshines', 'water': 'evaporates', 'metal': 'heats', 'plastic': 'warps', 'ash': 'disperses', 'glass': 'focuses through' }, 'moon': { 'sun': 'eclipses', 'fire': 'dims', 'lightning': 'attracts', 'ash': 'settles', 'sand': 'pulls', 'rock': 'moves', 'metal': 'magnetizes' }, 'salt': { 'ice': 'melts', 'metal': 'corrodes', 'fungus': 'preserves against', 'tree': 'dehydrates', 'leather': 'cures', 'sponge': 'dries', 'water': 'purifies' }, 'plastic': { 'tree': 'replaces', 'paper': 'substitutes', 'cloth': 'outperforms', 'leather': 'mimics', 'metal': 'insulates', 'rock': 'shapes around', 'glass': 'replaces' }, 'leather': { 'paper': 'outlasts', 'cloth': 'protects better than', 'sponge': 'repels', 'water': 'resists', 'ice': 'insulates against', 'metal': 'cushions', 'tree': 'preserves' }, 'cloth': { 'water': 'absorbs', 'oil': 'soaks up', 'ash': 'filters', 'sand': 'catches', 'sponge': 'wraps', 'air': 'blocks', 'lightning': 'insulates against' }, 'glass': { 'ice': 'stays solid against', 'paper': 'cuts', 'cloth': 'tears', 'air': 'seals against', 'sponge': 'repels', 'oil': 'contains', 'water': 'holds' } }; // Game state variables var gameState = 'selection'; // 'selection', 'spinning', 'battle', 'result' var dragNode = null; // Track dragging state var elementButtons = []; var playerElement = null; var aiElement = null; var playerBattleElement = null; var aiBattleElement = null; var vsText = null; var battleResultText = null; // Global battle result text var playerLife = 100; var aiLife = 100; var difficultyLevel = 1; var battleTimer = 0; var hoveredButton = null; // Track which button is currently hovered var victoryArrows = []; // Array to store victory arrows var usedPlayerElements = []; // Track elements used by player var usedAIElements = []; // Track elements used by AI var wheelContainer = null; // Container for wheel elements var isWheelSpinning = false; // Track if wheel is currently spinning var wheelRotation = 0; // Current wheel rotation // Combo system variables var playerCombo = []; // Array to store player's selected combo elements var aiCombo = []; // Array to store AI's combo elements var isComboMode = false; // Track if player is building a combo var comboDisplay = null; // UI element to show current combo var aiComboSpins = 0; // Track AI combo wheel spins // Defensive token system variables var playerDefenseTokens = 0; // Track player's defensive tokens var aiDefenseTokens = 0; // Track AI's defensive tokens var playerDefenseTokenDisplay = null; // UI element to show player tokens var aiDefenseTokenDisplay = null; // UI element to show AI tokens // Weather system variables var currentWeather = null; var weatherRoundsLeft = 0; var noWeatherRounds = 0; // Track how many rounds no weather has been active var weatherTypes = ['rainy', 'drought', 'windy', 'storm']; var weatherEffects = { 'rainy': ['water', 'ice', 'sponge'], // Water-based elements get bonus 'drought': ['fire', 'sand', 'sun'], // Fire-based elements get bonus 'windy': ['air', 'gas', 'smoke'], // Air-based elements get bonus 'storm': ['lightning', 'metal', 'gun'] // Electric/conductive elements get bonus }; // Hidden element system variables var hiddenElements = []; // Array to track which elements are currently hidden // Weather penalties - elements that receive negative effects during specific weather var weatherPenalties = { 'rainy': ['fire', 'sand', 'sun'], // Fire-based elements get penalty during rain 'drought': ['water', 'ice', 'sponge'], // Water-based elements get penalty during drought 'windy': ['rock', 'metal', 'gun'], // Heavy elements get penalty in wind 'storm': ['water', 'tree', 'sponge'] // Conductive/tall elements get penalty in storm }; // Combo names for all possible combinations var comboNames = _defineProperty2(_defineProperty2(_defineProperty2(_defineProperty2(_defineProperty2(_defineProperty2(_defineProperty2(_defineProperty2(_defineProperty2(_defineProperty2((_defineProperty3 = _defineProperty6((_defineProperty5 = { 'air+fire': 'Blazing Wind', 'air+gas': 'Toxic Cloud', 'air+gun': 'Wind Shot', 'air+ice': 'Frozen Gale', 'air+lightning': 'Thunder Storm', 'air+metal': 'Steel Breeze', 'air+oil': 'Fuel Vapor', 'air+paper': 'Paper Plane', 'air+rock': 'Flying Stone', 'air+sand': 'Sand Storm', 'air+scissors': 'Wind Blade', 'air+smoke': 'Smog Cloud', 'air+sponge': 'Airy Cushion', 'air+tree': 'Rustling Leaves', 'air+water': 'Mist', 'fire+gas': 'Explosive Blast', 'fire+gun': 'Flaming Shot', 'fire+ice': 'Steam Burst', 'fire+lightning': 'Plasma Storm', 'fire+metal': 'Molten Steel', 'fire+oil': 'Oil Fire', 'fire+paper': 'Burning Page', 'fire+rock': 'Lava Rock', 'fire+sand': 'Glass Forge', 'fire+scissors': 'Heated Blade', 'fire+smoke': 'Inferno Smoke', 'fire+sponge': 'Burning Sponge', 'fire+tree': 'Forest Fire', 'fire+water': 'Steam Power', 'gas+gun': 'Gas Shot', 'gas+ice': 'Frozen Gas', 'gas+lightning': 'Electric Gas', 'gas+metal': 'Metal Corrosion', 'gas+oil': 'Fuel Mix', 'gas+paper': 'Gas Wrapper', 'gas+rock': 'Gas Bubble', 'gas+sand': 'Gas Pocket', 'gas+scissors': 'Gas Cutter', 'gas+smoke': 'Dense Fog', 'gas+sponge': 'Gas Absorber', 'gas+tree': 'Tree Poison', 'gas+water': 'Carbonation', 'gun+ice': 'Ice Bullet', 'gun+lightning': 'Electric Gun', 'gun+metal': 'Steel Shot', 'gun+oil': 'Oiled Gun', 'gun+paper': 'Paper Target', 'gun+rock': 'Rock Bullet', 'gun+sand': 'Sand Blast', 'gun+scissors': 'Sharp Shooter', 'gun+smoke': 'Smoke Screen', 'gun+sponge': 'Soft Target', 'gun+tree': 'Wood Shot', 'gun+water': 'Water Gun', 'ice+lightning': 'Frozen Lightning', 'ice+metal': 'Frozen Steel', 'ice+oil': 'Frozen Oil', 'ice+paper': 'Ice Sheet', 'ice+rock': 'Frozen Rock', 'ice+sand': 'Frozen Sand', 'ice+scissors': 'Ice Blade', 'ice+smoke': 'Frozen Smoke', 'ice+sponge': 'Ice Block', 'ice+tree': 'Frozen Tree', 'ice+water': 'Frozen Flood', 'lightning+metal': 'Electric Conductor', 'lightning+oil': 'Electric Oil', 'lightning+paper': 'Electric Paper', 'lightning+rock': 'Thunder Stone', 'lightning+sand': 'Lightning Glass', 'lightning+scissors': 'Electric Blade', 'lightning+smoke': 'Electric Smoke', 'lightning+sponge': 'Shock Absorber', 'lightning+tree': 'Struck Tree', 'lightning+water': 'Electrified Storm', 'metal+oil': 'Oiled Metal', 'metal+paper': 'Foil', 'metal+rock': 'Crushing Force', 'metal+sand': 'Metal Sand', 'metal+scissors': 'Steel Scissors', 'metal+smoke': 'Metal Smoke', 'metal+sponge': 'Steel Wool', 'metal+tree': 'Metal Tree', 'metal+water': 'Rust Formation', 'oil+paper': 'Oil Stain', 'oil+rock': 'Oil Rock', 'oil+sand': 'Oil Sand', 'oil+scissors': 'Oiled Blade', 'oil+smoke': 'Oil Smoke', 'oil+sponge': 'Oil Absorber', 'oil+tree': 'Oil Tree', 'oil+water': 'Oil Spill', 'paper+rock': 'Rock Wrapper', 'paper+sand': 'Sand Paper', 'paper+scissors': 'Paper Cut', 'paper+smoke': 'Smoke Paper', 'paper+sponge': 'Paper Towel', 'paper+tree': 'Paper Wood', 'paper+water': 'Wet Paper', 'rock+sand': 'Sand Stone', 'rock+scissors': 'Rock Crusher', 'rock+smoke': 'Smoky Rock', 'rock+sponge': 'Rock Sponge', 'rock+tree': 'Rock Garden', 'rock+water': 'Rock Pool', 'sand+scissors': 'Sand Blade', 'sand+smoke': 'Dust Cloud', 'sand+sponge': 'Sand Filter', 'sand+tree': 'Desert Tree', 'sand+water': 'Mud', 'sand+air': "Sand storm", 'sand+sun': 'Drought', 'scissors+smoke': 'Smoky Blade', 'scissors+sponge': 'Soft Cut', 'scissors+tree': 'Tree Cutter', 'scissors+water': 'Wet Scissors', 'smoke+sponge': 'Smoke Filter', 'smoke+tree': 'Smoky Tree', 'smoke+water': 'Steam Cloud', 'sponge+tree': 'Tree Sponge', 'sponge+water': 'Water Sponge', 'tree+water': 'Living Tree', // New element combinations with existing elements 'ash+air': 'Ash Cloud', 'ash+fire': 'Cinder Storm', 'ash+gas': 'Toxic Ash', 'ash+gun': 'Ash Shot', 'ash+ice': 'Frozen Ash', 'ash+lightning': 'Electric Ash', 'ash+metal': 'Metal Dust', 'ash+oil': 'Ash Oil', 'ash+paper': 'Ash Paper', 'ash+rock': 'Dusty Rock', 'ash+sand': 'Ash Sand', 'ash+scissors': 'Dusty Blade', 'ash+smoke': 'Ash Smoke', 'ash+sponge': 'Ash Filter', 'ash+tree': 'Dead Tree', 'ash+water': 'Ash Slurry', 'cloth+air': 'Flying Fabric', 'cloth+fire': 'Burning Cloth', 'cloth+gas': 'Gas Mask', 'cloth+gun': 'Cloth Wrap', 'cloth+ice': 'Frozen Fabric', 'cloth+lightning': 'Static Cloth', 'cloth+metal': 'Armored Cloth', 'cloth+oil': 'Oiled Fabric', 'cloth+paper': 'Paper Cloth', 'cloth+rock': 'Rock Wrap', 'cloth+sand': 'Sandy Cloth', 'cloth+scissors': 'Cut Fabric', 'cloth+smoke': 'Smoky Cloth', 'cloth+sponge': 'Cloth Sponge', 'cloth+tree': 'Natural Fiber', 'cloth+water': 'Wet Cloth', 'fungus+air': 'Spore Cloud', 'fungus+fire': 'Burning Fungus', 'fungus+gas': 'Toxic Spores', 'fungus+gun': 'Bio Weapon', 'fungus+ice': 'Frozen Fungus', 'fungus+lightning': 'Electric Fungus', 'fungus+metal': 'Metal Decay', 'fungus+oil': 'Oil Fungus', 'fungus+paper': 'Moldy Paper', 'fungus+rock': 'Stone Fungus', 'fungus+sand': 'Sandy Fungus', 'fungus+scissors': 'Moldy Blade', 'fungus+smoke': 'Fungal Smoke', 'fungus+sponge': 'Fungal Sponge', 'fungus+tree': 'Tree Fungus', 'fungus+water': 'Fungal Water', 'glass+air': 'Glass Bubble', 'glass+fire': 'Molten Glass', 'glass+gas': 'Glass Chamber', 'glass+gun': 'Glass Bullet', 'glass+ice': 'Ice Glass', 'glass+lightning': 'Electric Glass', 'glass+metal': 'Glass Metal', 'glass+oil': 'Glass Oil', 'glass+paper': 'Glass Paper', 'glass+rock': 'Glass Rock', 'glass+sand': 'Glass Sand', 'glass+scissors': 'Glass Cutter', 'glass+smoke': 'Smoky Glass', 'glass+sponge': 'Glass Sponge', 'glass+tree': 'Glass Tree', 'glass+water': 'Glass Water', 'leather+air': 'Wind Leather', 'fire+leather': 'Burned Leather', 'leather+fire': 'Burned Leather', 'leather+gas': 'Gas Mask', 'leather+gun': 'Leather Holster', 'leather+ice': 'Frozen Hide', 'leather+lightning': 'Electric Hide', 'leather+metal': 'Studded Leather', 'leather+oil': 'Oiled Hide', 'leather+paper': 'Leather Paper', 'leather+rock': 'Rock Hide', 'leather+sand': 'Sandy Hide', 'leather+scissors': 'Cut Leather', 'leather+smoke': 'Smoky Hide', 'leather+sponge': 'Soft Leather', 'leather+tree': 'Bark Leather', 'leather+water': 'Wet Leather', 'moon+air': 'Night Wind', 'moon+fire': 'Moon Fire', 'moon+gas': 'Lunar Gas', 'moon+gun': 'Moon Shot', 'moon+ice': 'Lunar Ice', 'moon+lightning': 'Lunar Storm', 'lightning+moon': 'Lunar Storm', 'moon+metal': 'Lunar Metal', 'moon+oil': 'Moon Oil', 'moon+paper': 'Moon Paper', 'moon+rock': 'Moon Rock', 'moon+sand': 'Lunar Sand', 'moon+scissors': 'Lunar Blade', 'moon+smoke': 'Moon Smoke', 'moon+sponge': 'Moon Sponge', 'moon+tree': 'Moon Tree', 'moon+water': 'Lunar Tide', 'plastic+air': 'Plastic Bag', 'plastic+fire': 'Melted Plastic', 'plastic+gas': 'Plastic Gas', 'plastic+gun': 'Plastic Gun', 'plastic+ice': 'Frozen Plastic', 'plastic+lightning': 'Electric Plastic', 'plastic+metal': 'Plastic Metal', 'plastic+oil': 'Plastic Oil', 'plastic+paper': 'Plastic Paper', 'plastic+rock': 'Plastic Rock', 'plastic+sand': 'Plastic Sand', 'plastic+scissors': 'Plastic Cutter', 'plastic+smoke': 'Plastic Smoke', 'plastic+sponge': 'Plastic Sponge', 'plastic+tree': 'Plastic Tree', 'plastic+water': 'Plastic Water', 'salt+air': 'Salty Air', 'salt+fire': 'Salt Fire', 'salt+gas': 'Salt Gas', 'salt+gun': 'Salt Shot', 'salt+ice': 'Salt Ice', 'salt+lightning': 'Electric Salt', 'smoke+sun': 'Solar Haze' }, _defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty5, "salt+ice", 'Frozen Brine'), 'salt+metal', 'Salt Metal'), 'salt+oil', 'Salt Oil'), 'salt+paper', 'Salt Paper'), 'salt+rock', 'Salt Rock'), 'salt+sand', 'Salt Sand'), 'salt+scissors', 'Salt Blade'), 'salt+smoke', 'Salt Smoke'), 'salt+sponge', 'Salt Sponge'), 'salt+tree', 'Salt Tree'), _defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty5, 'salt+water', 'Salt Water'), 'sun+air', 'Solar Wind'), 'sun+fire', 'Solar Fire'), 'sun+gas', 'Solar Gas'), 'sun+gun', 'Solar Ray'), 'sun+ice', 'Solar Ice'), 'sun+lightning', 'Solar Lightning'), 'sun+metal', 'Solar Metal'), 'sun+oil', 'Solar Oil'), 'sun+paper', 'Solar Paper'), _defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty5, 'sun+rock', 'Solar Rock'), 'sun+sand', 'Solar Sand'), 'sun+scissors', 'Solar Blade'), 'sun+smoke', 'Solar Smoke'), 'sun+sponge', 'Solar Sponge'), 'sun+tree', 'Solar Tree'), 'sun+water', 'Solar Water'), 'ash+cloth', 'Dirty Fabric'), 'ash+fungus', 'Decay Spores'), 'ash+glass', 'Ash Glass'), _defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty5, 'ash+leather', 'Dusty Hide'), 'ash+moon', 'Lunar Dust'), 'ash+plastic', 'Ash Plastic'), 'ash+salt', 'Alkaline Mix'), 'ash+sun', 'Solar Ash'), 'cloth+fungus', 'Moldy Cloth'), 'cloth+glass', 'Fiber Glass'), 'cloth+leather', 'Textile Blend'), 'cloth+moon', 'Night Fabric'), 'cloth+plastic', 'Synthetic Cloth'), _defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty5, 'cloth+salt', 'Preserved Fabric'), 'cloth+sun', 'Sun Dried Cloth'), 'fungus+glass', 'Bio Glass'), 'fungus+leather', 'Moldy Hide'), 'fungus+moon', 'Night Spores'), 'fungus+plastic', 'Bio Plastic'), 'fungus+salt', 'Preserved Fungus'), 'fungus+sun', 'Solar Fungus'), 'glass+leather', 'Glass Armor'), 'glass+moon', 'Lunar Glass'), _defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty5, 'glass+plastic', 'Composite Glass'), 'glass+salt', 'Salt Crystal'), 'glass+sun', 'Solar Glass'), 'ice+leather', 'Frozen Leather'), 'leather+moon', 'Night Hide'), 'leather+plastic', 'Synthetic Leather'), 'leather+salt', 'Cured Hide'), 'leather+sun', 'Sun Leather'), 'moon+plastic', 'Lunar Plastic'), 'moon+salt', 'Tidal Salt'), _defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty7(_defineProperty5, 'moon+sun', 'Eclipse'), 'plastic+salt', 'Polymer Salt'), 'plastic+sun', 'Solar Plastic'), 'salt+sun', 'Evaporated Brine'), 'rock+sun', 'Solar Stone')), "salt+lightning", 'Electric Salt'), _defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty3, "moon+gas", 'Lunar Atmosphere'), "moon+gun", 'Lunar Shot'), "moon+metal", 'Lunar Steel'), "moon+oil", 'Tidal Oil'), "moon+paper", 'Lunar Script'), "moon+plastic", 'Lunar Polymer'), "moon+sponge", 'Tidal Sponge'), "moon+tree", 'Lunar Grove'), 'moon+ash', 'Lunar Ash'), 'moon+cloth', 'Starlight Fabric'), _defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty3, 'moon+fungus', 'Lunar Spores'), 'moon+glass', 'Moonbeam Glass'), 'moon+leather', 'Lunar Hide'), "moon+salt", 'Tidal Salt'), 'gas+moon', 'Lunar Atmosphere'), 'gun+moon', 'Lunar Shot'), 'metal+moon', 'Lunar Steel'), 'oil+moon', 'Tidal Oil'), 'paper+moon', 'Lunar Script'), 'plastic+moon', 'Lunar Polymer'), _defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty3, 'sponge+moon', 'Tidal Sponge'), 'tree+moon', 'Lunar Grove'), "ash+moon", 'Lunar Ash'), "cloth+moon", 'Starlight Fabric'), "fungus+moon", 'Lunar Spores'), "glass+moon", 'Moonbeam Glass'), "leather+moon", 'Lunar Hide'), 'salt+moon', 'Tidal Salt'), "sun+gas", 'Solar Flare'), "sun+gun", 'Solar Cannon'), _defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty3, "sun+metal", 'Solar Steel'), "sun+oil", 'Solar Fuel'), "sun+paper", 'Solar Paper'), 'sun+plastic', 'Solar Polymer'), "sun+sponge", 'Solar Sponge'), "sun+tree", 'Solar Grove'), 'sun+ash', 'Solar Ash'), 'sun+cloth', 'Solar Fabric'), 'sun+fungus', 'Solar Spores'), 'sun+glass', 'Solar Glass'), _defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty3, 'sun+leather', 'Solar Hide'), 'sun+salt', 'Solar Salt'), 'gas+sun', 'Solar Flare'), 'gun+sun', 'Solar Cannon'), 'metal+sun', 'Solar Steel'), 'oil+sun', 'Solar Fuel'), 'paper+sun', 'Solar Paper'), "plastic+sun", 'Solar Polymer'), 'sponge+sun', 'Solar Sponge'), 'tree+sun', 'Solar Grove'), _defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty4(_defineProperty3, "ash+sun", 'Solar Ash'), "cloth+sun", 'Solar Fabric'), "fungus+sun", 'Solar Spores'), "glass+sun", 'Solar Glass'), "leather+sun", 'Solar Hide'), "salt+sun", 'Solar Salt')), "sun+lightning", 'Solar Storm'), 'metal+salt', 'Corrosive Alloy'), 'metal+ash', 'Steel Dust'), 'metal+cloth', 'Chain Mail'), 'metal+fungus', 'Rusted Metal'), 'metal+glass', 'Reinforced Glass'), 'metal+leather', 'Armored Hide'), 'metal+moon', 'Lunar Steel'), 'metal+plastic', 'Composite Metal'), 'metal+sun', 'Solar Steel'); // Protective combo definitions - combos that provide defensive tokens var protectiveCombos = { 'metal+rock': true, // Armored Defense 'leather+cloth': true, // Padded Protection 'ice+water': true, // Frozen Shield 'tree+sponge': true, // Natural Buffer 'glass+plastic': true, // Composite Shield 'salt+sand': true, // Crystalline Barrier 'ash+smoke': true, // Obscuring Veil 'moon+sun': true, // Cosmic Balance 'metal+ice': true, // Reinforced Barrier 'rock+sand': true, // Stone Wall 'oil+plastic': true, // Waterproof Shell 'fungus+leather': true, // Bio Armor 'gas+smoke': true, // Toxic Screen 'paper+cloth': true, // Layered Guard 'air+gas': true, // Pressure Shield 'lightning+metal': true, // Electric Fence 'tree+paper': true, // Fiber Defense 'sponge+water': true, // Absorption Shield 'metal+leather': true // Reinforced Armor }; // Function to check if a combo is protective function isProtectiveCombo(combo) { if (combo.length !== 2) { return false; } var key = getComboKey(combo); return protectiveCombos[key] || false; } // Combo system definitions var comboEffects = { // Critical failure combos - elements that defeat each other 'water+fire': { name: 'Steam Explosion', defeatsExtra: [], multiplier: 0 }, 'fire+water': { name: 'Steam Explosion', defeatsExtra: [], multiplier: 0 }, 'rock+paper': { name: 'Torn Rock', defeatsExtra: [], multiplier: 0 }, 'paper+rock': { name: 'Torn Rock', defeatsExtra: [], multiplier: 0 }, 'scissors+paper': { name: 'Dulled Scissors', defeatsExtra: [], multiplier: 0 }, 'paper+scissors': { name: 'Dulled Scissors', defeatsExtra: [], multiplier: 0 }, 'rock+scissors': { name: 'Broken Blades', defeatsExtra: [], multiplier: 0 }, 'scissors+rock': { name: 'Broken Blades', defeatsExtra: [], multiplier: 0 }, 'water+rock': { name: 'Eroded Stone', defeatsExtra: [], multiplier: 0 }, 'rock+water': { name: 'Eroded Stone', defeatsExtra: [], multiplier: 0 }, 'fire+rock': { name: 'Cracked Stone', defeatsExtra: [], multiplier: 0 }, 'rock+fire': { name: 'Cracked Stone', defeatsExtra: [], multiplier: 0 }, 'fire+paper': { name: 'Burnt Paper', defeatsExtra: [], multiplier: 0 }, 'paper+fire': { name: 'Burnt Paper', defeatsExtra: [], multiplier: 0 }, 'fire+scissors': { name: 'Molten Scissors', defeatsExtra: [], multiplier: 0 }, 'scissors+fire': { name: 'Molten Scissors', defeatsExtra: [], multiplier: 0 }, 'fire+tree': { name: 'Forest Fire', defeatsExtra: [], multiplier: 0 }, 'tree+fire': { name: 'Forest Fire', defeatsExtra: [], multiplier: 0 }, 'fire+gas': { name: 'Gas Explosion', defeatsExtra: [], multiplier: 0 }, 'gas+fire': { name: 'Gas Explosion', defeatsExtra: [], multiplier: 0 }, 'fire+ice': { name: 'Steam Burst', defeatsExtra: [], multiplier: 0 }, 'ice+fire': { name: 'Steam Burst', defeatsExtra: [], multiplier: 0 }, 'plastic+gun': { name: 'Plastic Gun', defeatsExtra: [], multiplier: 0 }, 'gun+plastic': { name: 'Plastic Gun', defeatsExtra: [], multiplier: 0 }, // Working combo combinations with enhanced effects 'lightning+water': { name: 'Electrified Storm', defeatsExtra: ['metal', 'gun', 'scissors'], // Additional defeats beyond normal rules multiplier: 1.5 }, 'fire+air': { name: 'Blazing Wind', defeatsExtra: ['tree', 'paper', 'oil'], multiplier: 2.0 }, 'water+ice': { name: 'Frozen Flood', defeatsExtra: ['fire', 'rock', 'sand'], multiplier: 1.5 }, 'rock+metal': { name: 'Crushing Force', defeatsExtra: ['gun', 'scissors', 'lightning'], multiplier: 1.8 }, 'lightning+metal': { name: 'Electric Conductor', defeatsExtra: ['water', 'ice', 'gun'], multiplier: 1.7 }, 'air+metal': { name: 'Steel Storm', defeatsExtra: ['rock', 'paper', 'tree'], multiplier: 1.6 }, 'ice+metal': { name: 'Frozen Steel', defeatsExtra: ['fire', 'lightning', 'air'], multiplier: 1.5 }, 'gas+oil': { name: 'Fuel Mixture', defeatsExtra: ['water', 'sponge', 'ice'], multiplier: 2.2 }, 'tree+water': { name: 'Living Forest', defeatsExtra: ['fire', 'sand', 'oil'], multiplier: 1.4 }, 'smoke+gas': { name: 'Toxic Cloud', defeatsExtra: ['air', 'tree', 'sponge'], multiplier: 1.9 } }; // Function to hide random elements function hideRandomElements() { // Clear previous hidden elements hiddenElements = []; // Get available element indices (not disabled) var availableIndices = []; for (var i = 0; i < elementButtons.length; i++) { if (!elementButtons[i].isDisabled) { availableIndices.push(i); } } // Randomly select 2 elements to hide (if we have enough available) var numToHide = Math.min(2, availableIndices.length); for (var h = 0; h < numToHide; h++) { var randomIndex = Math.floor(Math.random() * availableIndices.length); var elementIndex = availableIndices[randomIndex]; hiddenElements.push(elementIndex); // Remove from available list so we don't select it again availableIndices.splice(randomIndex, 1); } // Apply hidden state to selected elements for (var i = 0; i < hiddenElements.length; i++) { var buttonIndex = hiddenElements[i]; var button = elementButtons[buttonIndex]; // Remove all existing children (background, icon) while (button.children.length > 1) { // Keep the label (last child) button.removeChild(button.children[0]); } // Add only the secret element - no icon, no background var secretElement = button.attachAsset('secretElement', { anchorX: 0.5, anchorY: 0.5 }); // Move secret element to back so label stays on top button.setChildIndex(secretElement, 0); // Keep the existing text label showing the element name if (button.textLabel) { //button.textLabel.setText(button.elementType.charAt(0).toUpperCase() + button.elementType.slice(1)); button.textLabel.setText("????"); button.hiddenTextLabel = button.elementType.charAt(0).toUpperCase() + button.elementType.slice(1); } } } // Function to reveal an element when selected function revealElement(elementIndex) { var hiddenIndex = hiddenElements.indexOf(elementIndex); if (hiddenIndex === -1) { return; // Element wasn't hidden } // Remove from hidden list hiddenElements.splice(hiddenIndex, 1); var button = elementButtons[elementIndex]; console.log(button); // Restore the complete button appearance // Remove secret element (should be first child) if (button.children.length > 0 && button.children[0]) { button.removeChild(button.children[0]); } // Add normal element background var normalBackground = button.attachAsset('elementButton', { anchorX: 0.5, anchorY: 0.5 }); // Add element icon var icon = button.attachAsset(button.elementType, { anchorX: 0.5, anchorY: 0.5 }); // The existing label is already positioned correctly, just reveal the text if (button.textLabel) { console.log(button.textLabel); console.log(button.hiddenTextLabel); // Set the proper text for the revealed element button.textLabel.setText(); var label = new Text2(button.hiddenTextLabel.slice(0, 1).toUpperCase() + button.hiddenTextLabel.slice(1), { size: 42, fill: 0xffffff, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 2 }); label.anchor.set(0.5, 0.5); button.addChild(label); // Store reference to the label for easy access button.textLabel = label; button.textLabel.x = button.originalLabelX; button.textLabel.y = button.originalLabelY; //button.positionTextTowardCenter(); } // Apply weather effects if needed applyWeatherBoostEffects(); // Add reveal effect var revealText = new Text2('REVEALED!', { size: 32, fill: 0xFFD700, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 2 }); revealText.anchor.set(0.5, 0.5); revealText.x = wheelContainer.x + button.x; revealText.y = wheelContainer.y + button.y - 100; revealText.alpha = 0; game.addChild(revealText); // Animate reveal text tween(revealText, { alpha: 1, y: revealText.y - 50 }, { duration: 800, onFinish: function onFinish() { tween(revealText, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { revealText.destroy(); } }); } }); } // Function to get the element at the top position function getTopElement() { // At the start, rock (index 0) is at the top (-PI/2) // Calculate angle per element var anglePerElement = Math.PI * 2 / elements.length; // Normalize rotation to 0-2PI range var normalizedRotation = (wheelContainer.rotation % (Math.PI * 2) + Math.PI * 2) % (Math.PI * 2); // Calculate how many element positions we've rotated from the starting position var elementSteps = Math.round(normalizedRotation / anglePerElement); // Find which element is now at the ROCK position (top position) // Since wheel rotates clockwise, we subtract the steps from the starting rock index var topElementIndex = (0 - elementSteps + elements.length * 10) % elements.length; var topElement = elements[topElementIndex]; return topElement; } // Function to start new weather function startNewWeather() { var newWeather = weatherTypes[Math.floor(Math.random() * weatherTypes.length)]; currentWeather = newWeather; weatherRoundsLeft = 1 + Math.floor(Math.random() * 3); // 3-5 rounds noWeatherRounds = 0; // Reset no weather counter when weather starts updateWeatherDisplay(); LK.stopMusic(); // Play music based on weather type, then return to background music if (currentWeather === 'windy') { LK.playMusic('air'); } else if (currentWeather === 'drought') { LK.playMusic('draught'); } else if (currentWeather === 'rainy') { LK.playMusic('rain'); } else if (currentWeather === 'storm') { LK.playMusic('storm'); // Resume background music after weather music } else { LK.playMusic('background'); } } // Function to update defensive token displays function updateDefenseTokenDisplays() { // Clear existing tokens for (var i = playerDefenseTokenDisplay.children.length - 1; i >= 0; i--) { playerDefenseTokenDisplay.removeChild(playerDefenseTokenDisplay.children[i]); } for (var i = aiDefenseTokenDisplay.children.length - 1; i >= 0; i--) { aiDefenseTokenDisplay.removeChild(aiDefenseTokenDisplay.children[i]); } // Add player tokens for (var i = 0; i < playerDefenseTokens; i++) { var token = LK.getAsset('defenseToken', { anchorX: 0, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4 }); token.x = i * 40; // Space tokens out playerDefenseTokenDisplay.addChild(token); } // Add AI tokens for (var i = 0; i < aiDefenseTokens; i++) { var token = LK.getAsset('defenseToken', { anchorX: 1, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4 }); token.x = i * -40; // Space tokens out aiDefenseTokenDisplay.addChild(token); } } // Function to update weather display // Function to update weather display function updateWeatherDisplay() { var text = ""; if (currentWeather && weatherRoundsLeft > 0) { var weatherName = currentWeather.charAt(0).toUpperCase() + currentWeather.slice(1); var boostedElements = weatherEffects[currentWeather].map(function (element) { return element.charAt(0).toUpperCase() + element.slice(1); }).join(', '); var penalizedElements = weatherPenalties[currentWeather].map(function (element) { return element.charAt(0).toUpperCase() + element.slice(1); }).join(', '); text = weatherName + ' Season (' + weatherRoundsLeft + ' rounds)\n' + boostedElements + ' boosted\n' + penalizedElements + ' penalized'; } else { // No weather conditions - show default message with remaining rounds var remainingRounds = 2 - noWeatherRounds; text = "No weather conditions (" + remainingRounds + " rounds)"; // Play background music when no weather is active LK.playMusic('background'); } if (weatherText) { weatherText.destroy(); } weatherText = new Text2(text, { size: 28, fill: getWeatherColor(currentWeather), font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3, align: 'center' }); LK.gui.center.addChild(weatherText); weatherText.anchor.set(0.5, 0); LK.gui.top.addChild(weatherText); weatherText.x = 20; weatherText.y = 150; // Position under Wins text // Update background to match current weather updateBackgroundForWeather(); // Clean up weather effects if no weather is active if (!currentWeather || weatherRoundsLeft <= 0) { if (weatherEffectsContainer) { // Fade out effects before destroying tween(weatherEffectsContainer, { alpha: 0 }, { duration: 1000, onFinish: function onFinish() { if (weatherEffectsContainer) { weatherEffectsContainer.destroy(); } weatherEffectsContainer = null; } }); } } // Apply visual boost effects to weather-enhanced elements applyWeatherBoostEffects(); } // Function to get weather color function getWeatherColor(weather) { switch (weather) { case 'rainy': return 0x8ba8ff; // Royal blue case 'drought': return 0xfca586; // Orange red case 'windy': return 0x87CEEB; // Sky blue case 'storm': return 0xb895ff; // Medium purple default: return 0xFFFFFF; } } // Function to check if element gets weather bonus function hasWeatherBonus(element) { if (!currentWeather || weatherRoundsLeft <= 0) { return false; } return weatherEffects[currentWeather].indexOf(element) !== -1; } // Function to check if element gets weather penalty function hasWeatherPenalty(element) { if (!currentWeather || weatherRoundsLeft <= 0) { return false; } return weatherPenalties[currentWeather].indexOf(element) !== -1; } // Function to apply boost visual effects to weather-enhanced elements function applyWeatherBoostEffects() { for (var i = 0; i < elementButtons.length; i++) { var button = elementButtons[i]; var element = button.elementType; // Skip weather effects for hidden elements - they should only show secretElement if (hiddenElements.indexOf(i) !== -1) { continue; } if (hasWeatherBonus(element) && !button.isDisabled) { // Replace elementButton with boostedElementButton for visual enhancement var background = button.children[0]; // The button background is the first child if (background) { // Remove old background button.removeChild(background); // Add boosted background with weather-specific variant var boostedAssetName = 'boostedElementButton'; // default if (currentWeather === 'storm') { boostedAssetName = 'boostElementButtonStormy'; } else if (currentWeather === 'rainy') { boostedAssetName = 'boostedElementButtonRain'; } else if (currentWeather === 'drought') { boostedAssetName = 'boostedElementButtonDraught'; } else if (currentWeather === 'windy') { boostedAssetName = 'boostedElementButtonWind'; } var boostedBackground = button.attachAsset(boostedAssetName, { anchorX: 0.5, anchorY: 0.5 }); // Move to back so icon and label stay on top button.setChildIndex(boostedBackground, 0); } // Reset tint for boosted elements button.tint = 0xffffff; // Boosted elements use boostedElementButton asset without scaling animation } else if (hasWeatherPenalty(element) && !button.isDisabled) { // Penalized elements use unboostedElementButton asset var background = button.children[0]; // The button background is the first child if (background) { // Remove old background button.removeChild(background); // Add unboosted background for penalized elements var penalizedBackground = button.attachAsset('unboostedElementButton', { anchorX: 0.5, anchorY: 0.5 }); // Move to back so icon and label stay on top button.setChildIndex(penalizedBackground, 0); } // Reset tint for penalized elements (no red tinting) button.tint = 0xffffff; // Stop any existing scale tweens for penalized elements tween.stop(button, { scaleX: true, scaleY: true }); // Reset scale to normal without animation for penalized elements if (!button.isSelected && !button.isDisabled) { button.scaleX = 1.0; button.scaleY = 1.0; } } else { // Replace boostedElementButton back with regular elementButton for neutral elements var background = button.children[0]; // The button background is the first child if (background) { // Check if it's currently boosted and needs to be replaced button.removeChild(background); // Add regular background var regularBackground = button.attachAsset('elementButton', { anchorX: 0.5, anchorY: 0.5 }); // Move to back so icon and label stay on top button.setChildIndex(regularBackground, 0); } button.tint = 0xffffff; // Stop any existing scale tweens for non-boosted elements tween.stop(button, { scaleX: true, scaleY: true }); // Reset scale to normal without animation for non-boosted elements if (!button.isSelected && !button.isDisabled) { button.scaleX = 1.0; button.scaleY = 1.0; } } } } // Combo helper functions function getComboKey(combo) { if (combo.length !== 2) { return null; } var sorted = combo.slice().sort(); return sorted[0] + '+' + sorted[1]; } function getComboName(combo) { if (combo.length !== 2) { return null; } var key = getComboKey(combo); return comboNames[key] || null; } function getComboEffect(combo) { var key = getComboKey(combo); return comboEffects[key] || null; } function updateComboDisplay() { // Clear existing combo elements for (var i = comboDisplay.children.length - 1; i >= 0; i--) { comboDisplay.removeChild(comboDisplay.children[i]); } comboDisplay.setText(''); if (playerCombo.length === 0) { // No combo } else if (playerCombo.length === 1) { comboDisplay.setText(playerCombo[0].charAt(0).toUpperCase() + playerCombo[0].slice(1)); var icon1 = LK.getAsset(playerCombo[0], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.9, scaleY: 0.9 }); icon1.x = -180; icon1.y = 130; comboDisplay.addChild(icon1); } else if (playerCombo.length === 2) { var effect = getComboEffect(playerCombo); var comboName = getComboName(playerCombo); if (effect) { comboDisplay.setText(effect.name); comboDisplay.tint = 0xFFFFFF; // White for special combo } else if (comboName) { comboDisplay.setText(comboName); comboDisplay.tint = 0xFFFFFF; // White for normal combo } else { comboDisplay.setText('Unknown Combo'); comboDisplay.tint = 0xFFFFFF; // White for normal combo } var icon1 = LK.getAsset(playerCombo[0], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.9, scaleY: 0.9 }); icon1.x = -180; icon1.y = effect ? 130 : 130; comboDisplay.addChild(icon1); var icon2 = LK.getAsset(playerCombo[1], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.9, scaleY: 0.9 }); icon2.x = 180; icon2.y = effect ? 130 : 130; comboDisplay.addChild(icon2); } } function updateAIComboDisplay() { // Clear existing combo elements for (var i = aiComboDisplay.children.length - 1; i >= 0; i--) { aiComboDisplay.removeChild(aiComboDisplay.children[i]); } aiComboDisplay.setText(''); if (aiCombo.length === 0) { // No combo } else if (aiCombo.length === 1) { aiComboDisplay.setText(aiCombo[0].charAt(0).toUpperCase() + aiCombo[0].slice(1)); var icon1 = LK.getAsset(aiCombo[0], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.9, scaleY: 0.9 }); icon1.x = -180; icon1.y = 130; aiComboDisplay.addChild(icon1); var plusText = new Text2('', { size: 60, fill: 0xFFFFFF, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 6 }); plusText.x = 0; plusText.y = 130; plusText.anchor.set(0.5, 0.5); aiComboDisplay.addChild(plusText); } else if (aiCombo.length === 2) { var effect = getComboEffect(aiCombo); var comboName = getComboName(aiCombo); if (effect) { aiComboDisplay.setText(effect.name); aiComboDisplay.tint = 0xFFFFFF; // White for special combo } else if (comboName) { aiComboDisplay.setText(comboName); aiComboDisplay.tint = 0xFFFFFF; // White for normal combo } else { aiComboDisplay.setText('Unknown Combo'); aiComboDisplay.tint = 0xFFFFFF; // White for normal combo } var icon1 = LK.getAsset(aiCombo[0], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.9, scaleY: 0.9 }); icon1.x = -180; icon1.y = effect ? 130 : 130; aiComboDisplay.addChild(icon1); var plusText = new Text2('', { size: 60, fill: 0xFFFFFF, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 6 }); plusText.x = 0; plusText.y = effect ? 130 : 130; plusText.anchor.set(0.5, 0.5); aiComboDisplay.addChild(plusText); var icon2 = LK.getAsset(aiCombo[1], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.9, scaleY: 0.9 }); icon2.x = 180; icon2.y = effect ? 130 : 130; aiComboDisplay.addChild(icon2); } } // Text positioning parameter - distance to move text toward wheel center var textOffsetTowardCenter = 160; // Adjustable parameter for text positioning // UI elements var titleText = new Text2('COMBO-NATION', { size: 58, fill: 0xFFFFFF, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 4 }); titleText.anchor.set(0.5, 0); LK.gui.top.addChild(titleText); titleText.y = 50; titleText.x = 25; // Add rainbow color animation to title text var rainbowColors = [0xFF0000, 0xFF8800, 0xFFFF00, 0x00FF00, 0x0088FF, 0x0000FF, 0x8800FF]; var currentColorIndex = 0; function animateRainbowTitle() { var nextColorIndex = (currentColorIndex + 1) % rainbowColors.length; tween(titleText, { tint: rainbowColors[nextColorIndex] }, { duration: 8000, onFinish: function onFinish() { currentColorIndex = nextColorIndex; animateRainbowTitle(); } }); } // Start rainbow animation animateRainbowTitle(); var instructionText = new Text2('Choose your element', { size: 32, fill: 0xFFFFFF, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3 }); instructionText.anchor.set(0.5, 0.5); game.addChild(instructionText); instructionText.x = 1024; instructionText.y = 410; // Player health bar at bottom var playerHealthBarBg = LK.getAsset('healthBarBg', { anchorX: 0, anchorY: 1, scaleX: 14, scaleY: 0.5 }); playerHealthBarBg.tint = 0x333333; LK.gui.bottom.addChild(playerHealthBarBg); playerHealthBarBg.x = -700; playerHealthBarBg.y = -60; var playerHealthBar = LK.getAsset('healthBar', { anchorX: 0, anchorY: 1, scaleX: 14, scaleY: 0.5 }); playerHealthBar.tint = 0x77b5f8; LK.gui.bottom.addChild(playerHealthBar); playerHealthBar.x = -700; playerHealthBar.y = -60; var playerHealthText = new Text2('PLAYER: 100/100', { size: 28, fill: 0xFFFFFF, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3 }); playerHealthText.anchor.set(0.5, 0.5); LK.gui.bottom.addChild(playerHealthText); playerHealthText.x = 0; playerHealthText.y = -90; // AI health bar at bottom var aiHealthBarBg = LK.getAsset('healthBarBg', { anchorX: 0, anchorY: 1, scaleX: 14, scaleY: 0.5 }); aiHealthBarBg.tint = 0x333333; LK.gui.bottom.addChild(aiHealthBarBg); aiHealthBarBg.x = -700; aiHealthBarBg.y = -10; var aiHealthBar = LK.getAsset('healthBar', { anchorX: 0, anchorY: 1, scaleX: 14, scaleY: 0.5 }); aiHealthBar.tint = 0xd992e9; LK.gui.bottom.addChild(aiHealthBar); aiHealthBar.x = -700; aiHealthBar.y = -10; var aiHealthText = new Text2('AI: 100/100', { size: 28, fill: 0xFFFFFF, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3 }); aiHealthText.anchor.set(0.5, 0.5); LK.gui.bottom.addChild(aiHealthText); aiHealthText.x = 0; aiHealthText.y = -35; // Player defensive tokens display // Player defensive tokens display playerDefenseTokenDisplay = new Container(); // Use a container for multiple tokens playerDefenseTokenDisplay.x = -700; playerDefenseTokenDisplay.y = -120; LK.gui.bottom.addChild(playerDefenseTokenDisplay); // Player hero image on top of Defense text var playerHeroImage = LK.getAsset('playerHero', { anchorX: 0.5, anchorY: 0.5, alpha: 1 }); LK.gui.bottom.addChild(playerHeroImage); playerHeroImage.x = -600; playerHeroImage.y = -260; // AI defensive tokens display // AI defensive tokens display aiDefenseTokenDisplay = new Container(); // Use a container for multiple tokens aiDefenseTokenDisplay.x = 700; aiDefenseTokenDisplay.y = -120; LK.gui.bottom.addChild(aiDefenseTokenDisplay); // AI hero image on top of AI Defense text var aiHeroImage = LK.getAsset('aiHero', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5 }); LK.gui.bottom.addChild(aiHeroImage); aiHeroImage.x = 610; aiHeroImage.y = -260; var weatherText = new Text2('', { size: 28, fill: 0x87CEEB, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3 }); weatherText.anchor.set(1, 0); LK.gui.topRight.addChild(weatherText); weatherText.x = -20; weatherText.y = 120; // Position under Wins text // Combo display in center of wheel comboDisplay = new Text2('', { size: 72, fill: 0xFFD700, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xFFFFFF, strokeThickness: 6 }); comboDisplay.anchor.set(0.5, 0.5); game.addChild(comboDisplay); comboDisplay.x = 1024; comboDisplay.y = 1000; // AI combo display widget var aiComboDisplay = new Text2('', { size: 72, fill: 0xFFD700, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xFFFFFF, strokeThickness: 6 }); aiComboDisplay.anchor.set(0.5, 0.5); game.addChild(aiComboDisplay); aiComboDisplay.x = 1024; aiComboDisplay.y = 1400; // 400 pixels down from player combo display at y: 1000 // Create element selection grid function createElementButtons() { // Randomly select 16 elements from the full pool for this game var shuffledElements = allElements.slice(); for (var i = shuffledElements.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = shuffledElements[i]; shuffledElements[i] = shuffledElements[j]; shuffledElements[j] = temp; } elements = shuffledElements.slice(0, 16); // Take first 16 elements var centerX = 1024; var centerY = 1400; var radius = 800; // Create container for victory arrows (behind wheel elements) var arrowsContainer = new Container(); arrowsContainer.x = centerX; arrowsContainer.y = centerY; game.addChild(arrowsContainer); // Create wheel container for spinning animation wheelContainer = new Container(); wheelContainer.x = centerX; wheelContainer.y = centerY; game.addChild(wheelContainer); for (var i = 0; i < elements.length; i++) { var angle = i / elements.length * Math.PI * 2 - Math.PI / 2; var x = Math.cos(angle) * radius; var y = Math.sin(angle) * radius; var button = wheelContainer.addChild(new ElementButton(elements[i], i)); button.x = x; button.y = y; button.positionTextTowardCenter(0, 0); // Use wheel center as reference // Store original label position var label = button.children[button.children.length - 1]; if (label && label.setText) { button.originalLabelX = label.x; button.originalLabelY = label.y; } elementButtons.push(button); } // Hide 2 random elements after creating all buttons hideRandomElements(); } // Global variables for confirmation buttons var confirmationContainer = null; var useAsComboButton = null; var useAsSingleButton = null; // Create confirmation buttons for combo/single choice function createConfirmationButtons(selectedElement) { // Clean up existing confirmation buttons if (confirmationContainer) { confirmationContainer.destroy(); confirmationContainer = null; } // Create container for confirmation popup centered in the wheel confirmationContainer = new Container(); confirmationContainer.x = 1024; // Center horizontally confirmationContainer.y = 1000; // Center at wheel position game.addChild(confirmationContainer); // Create white background popup var popupBackground = confirmationContainer.attachAsset('popup', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 2.0, alpha: 0 }); confirmationContainer.addChild(popupBackground); // Create "Use as Single" button useAsSingleButton = confirmationContainer.attachAsset('button', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1 }); useAsSingleButton.x = 0; useAsSingleButton.y = 275; useAsSingleButton.tint = 0xFFD700; // Gold tint var singleText = new Text2('USE 1 ELEMENT', { size: 25, fill: 0x000000, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 1 }); singleText.anchor.set(0.5, 0.5); singleText.x = 0; singleText.y = 275; confirmationContainer.addChild(singleText); // Title text removed - combo display will be in center instead // Add click handlers useAsSingleButton.down = function (x, y, obj) { LK.getSound('select').play(); handleComboChoice(selectedElement, false); }; // Animate buttons in confirmationContainer.alpha = 0; tween(confirmationContainer, { alpha: 1 }, { duration: 300 }); } // Handle combo/single choice function handleComboChoice(selectedElement, isCombo) { // Clean up confirmation buttons if (confirmationContainer) { tween(confirmationContainer, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { confirmationContainer.destroy(); confirmationContainer = null; } }); } if (isCombo) { // Player wants to make a combo - wait for second element selection instructionText.setText('Choose second element for combo'); // Keep the first element in combo array, wait for second selection } else { // Player wants to use single element playerElement = selectedElement; playerCombo = [selectedElement]; // Keep as single element combo updateComboDisplay(); // Check if AI went first - if so, go directly to battle instead of spinning wheel again if (!playerGoesFirst && (aiElement || aiCombo.length > 0)) { // AI already selected, go to battle LK.setTimeout(function () { startBattle(); }, 1000); } else { // Reset aiElement and combo when starting new selection aiElement = null; aiCombo = []; aiComboSpins = 0; // Update button states for (var i = 0; i < elementButtons.length; i++) { elementButtons[i].setSelected(false); } // Start wheel spinning animation startWheelSpin(); } } } // Select element function function selectElement(elementType) { if (gameState !== 'selection') { return; } // Set playerHero alpha to 1 when user is picking tween(playerHeroImage, { alpha: 1 }, { duration: 200 }); // Check if element has already been used by player if (usedPlayerElements.indexOf(elementType) !== -1) { return; // Don't allow selection of already used elements } // Immediately disable the selected element so AI can't use it for (var i = 0; i < elementButtons.length; i++) { if (elementButtons[i].elementType === elementType) { elementButtons[i].setDisabled(true); // Reveal element if it was hidden revealElement(i); break; } } // Check if element is already in current player combo if (playerCombo.indexOf(elementType) !== -1) { return; // Don't allow selecting same element twice in combo } // Hide victory arrows when user selects an element for (var i = 0; i < victoryArrows.length; i++) { tween(victoryArrows[i], { alpha: 0 }, { duration: 300 }); } // Hide triangle marker if it exists if (window.triangleMarker) { window.triangleMarker.alpha = 0; } // Play sound for specific elements if (allElements.indexOf(elementType) !== -1) { LK.getSound(elementType).play(); } if (elementType === 'air') { LK.getSound('airy').play(); } // Play sounds for new elements when clicked if (elementType === 'plastic') { LK.getSound('plastic').play(); } if (elementType === 'leather') { LK.getSound('leather').play(); } if (elementType === 'sun') { LK.getSound('sun').play(); } if (elementType === 'glass') { LK.getSound('glass').play(); } if (elementType === 'cloth') { LK.getSound('cloth').play(); } if (elementType === 'ash') { LK.getSound('ash').play(); } if (elementType === 'fungus') { LK.getSound('fungus').play(); } if (elementType === 'moon') { LK.getSound('moon').play(); } if (elementType === 'salt') { LK.getSound('salt').play(); } // Handle combo selection if (playerCombo.length === 0) { // First element selection playerCombo.push(elementType); updateComboDisplay(); // Update button states to show first selection for (var i = 0; i < elementButtons.length; i++) { elementButtons[i].setSelected(elementButtons[i].elementType === elementType); } // Create confirmation buttons createConfirmationButtons(elementType); } else if (playerCombo.length === 1) { // Second element selection (only reached if player chose combo option) playerCombo.push(elementType); playerElement = null; // Use combo instead of single element updateComboDisplay(); // Check if this combo is protective and show creation message if (isProtectiveCombo(playerCombo)) { var comboKey = getComboKey(playerCombo); var protectiveComboName = ''; if (comboKey === 'metal+rock') { protectiveComboName = 'Armored Defense'; } else if (comboKey === 'cloth+leather') { protectiveComboName = 'Padded Protection'; } else if (comboKey === 'ice+water') { protectiveComboName = 'Frozen Shield'; } else if (comboKey === 'sponge+tree') { protectiveComboName = 'Natural Buffer'; } else if (comboKey === 'glass+plastic') { protectiveComboName = 'Composite Shield'; } else if (comboKey === 'salt+sand') { protectiveComboName = 'Crystalline Barrier'; } else if (comboKey === 'ash+smoke') { protectiveComboName = 'Obscuring Veil'; } else if (comboKey === 'moon+sun') { protectiveComboName = 'Cosmic Balance'; } else if (comboKey === 'ice+metal') { protectiveComboName = 'Reinforced Barrier'; } else if (comboKey === 'rock+sand') { protectiveComboName = 'Stone Wall'; } else if (comboKey === 'oil+plastic') { protectiveComboName = 'Waterproof Shell'; } else if (comboKey === 'fungus+leather') { protectiveComboName = 'Bio Armor'; } else if (comboKey === 'gas+smoke') { protectiveComboName = 'Toxic Screen'; } else if (comboKey === 'cloth+paper') { protectiveComboName = 'Layered Guard'; } else if (comboKey === 'air+gas') { protectiveComboName = 'Pressure Shield'; } else if (comboKey === 'lightning+metal') { protectiveComboName = 'Electric Fence'; } else if (comboKey === 'paper+tree') { protectiveComboName = 'Fiber Defense'; } else if (comboKey === 'sponge+water') { protectiveComboName = 'Absorption Shield'; } else if (comboKey === 'metal+leather') { protectiveComboName = 'Reinforced Armor'; } // Disable clicks when protective combo message shows clicksDisabled = true; clearHoverEffects(); // Clear any active hover effects // Create white background for protective combo message var protectiveBackground = LK.getAsset('userTurnBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 2 }); protectiveBackground.tint = 0xFFFFFF; protectiveBackground.alpha = 1; protectiveBackground.x = 1024; protectiveBackground.y = 1366; game.addChild(protectiveBackground); // Show protective combo creation message var protectiveMessage = new Text2(protectiveComboName + ' created', { size: 48, fill: 0xffffff, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3, align: 'center' }); protectiveMessage.anchor.set(0.5, 0.5); protectiveMessage.x = 1024; protectiveMessage.y = 1366; protectiveMessage.alpha = 0; game.addChild(protectiveMessage); // Animate message appearance tween(protectiveBackground, { alpha: 1 }, { duration: 500 }); tween(protectiveMessage, { alpha: 1 }, { duration: 500, onFinish: function onFinish() { LK.setTimeout(function () { tween(protectiveBackground, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { protectiveBackground.destroy(); } }); tween(protectiveMessage, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { protectiveMessage.destroy(); } }); }, 1000); } }); // Animate adding token to player defense display playerDefenseTokens++; updateDefenseTokenDisplays(); // Create floating +1 animation near player defense display var tokenAnimation = new Text2('+1', { size: 36, fill: 0x00FF00, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 2 }); tokenAnimation.anchor.set(0.5, 0.5); LK.gui.bottom.addChild(tokenAnimation); tokenAnimation.x = -600; tokenAnimation.y = -120; tokenAnimation.alpha = 0; tween(tokenAnimation, { alpha: 1, y: -150, scaleX: 1.5, scaleY: 1.5 }, { duration: 800, onFinish: function onFinish() { tween(tokenAnimation, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { tokenAnimation.destroy(); } }); } }); } // Clean up confirmation buttons when second element is selected if (confirmationContainer) { tween(confirmationContainer, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { confirmationContainer.destroy(); confirmationContainer = null; } }); } // Check if AI went first - if so, go directly to battle instead of spinning wheel again if (!playerGoesFirst && (aiElement || aiCombo.length > 0)) { // AI already selected, go to battle LK.setTimeout(function () { startBattle(); }, 1000); } else { // Reset aiElement and combo when starting new selection aiElement = null; aiCombo = []; aiComboSpins = 0; // Update button states for (var i = 0; i < elementButtons.length; i++) { elementButtons[i].setSelected(false); } // Start wheel spinning animation startWheelSpin(); } } } // Start wheel spinning animation function startWheelSpin() { gameState = 'spinning'; isWheelSpinning = true; // Play spinning sound when AI spins the wheel LK.getSound('spinning').play(); // Set aiHero alpha to 1 when AI is spinning tween(aiHeroImage, { alpha: 1 }, { duration: 200 }); // Set playerHero alpha to 0.5 when not user's turn tween(playerHeroImage, { alpha: 0.5 }, { duration: 200 }); // Hide triangle immediately when wheel starts spinning if (window.triangleMarker) { window.triangleMarker.alpha = 0; } // Determine if AI should use combo var shouldUseCombo = Math.random() < 0.4; // 40% chance for AI to use combo if (shouldUseCombo && aiCombo.length === 0) { instructionText.setText('AI is choosing combo...'); aiComboSpins = 1; // AI will spin twice for combo } else if (aiComboSpins === 1) { instructionText.setText('AI choosing second element...'); } else { instructionText.setText('AI is choosing...'); } // Only show triangle during AI spinning, never during user selection if (gameState === 'spinning') { // Add triangle pointing down behind the "AI is choosing" text var triangle = LK.getAsset('marker', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); triangle.tint = 0xFFFFFF; triangle.rotation = Math.PI / 2; // Rotate 90 degrees to point down triangle.x = 1024; // Center horizontally triangle.y = 475; // Position below the instruction text triangle.alpha = 1; // Show triangle immediately when AI is spinning game.addChild(triangle); // Store triangle reference globally for cleanup window.triangleMarker = triangle; } // Hide victory arrows during spinning for (var i = 0; i < victoryArrows.length; i++) { tween(victoryArrows[i], { alpha: 0 }, { duration: 300 }); } // Hide element labels during spinning for (var i = 0; i < elementButtons.length; i++) { var button = elementButtons[i]; var label = button.children[button.children.length - 1]; // Label is the last child if (label && label.setText) { label.alpha = 0; // Immediately hide labels } } // Get available elements for AI (not disabled and not already in current AI combo) var availableElements = []; for (var i = 0; i < elementButtons.length; i++) { var elementType = elementButtons[i].elementType; if (!elementButtons[i].isDisabled && aiCombo.indexOf(elementType) === -1) { availableElements.push(elementType); } } // Calculate how many spins we need to ensure we land on an available element var spinAmount = 3 + Math.random() * 3; // 3-6 full rotations var baseRotation = spinAmount * Math.PI * 2; // Choose a random available element to land on var targetElement = availableElements[Math.floor(Math.random() * availableElements.length)]; var targetIndex = elements.indexOf(targetElement); // Calculate angle per element var anglePerElement = Math.PI * 2 / elements.length; // Calculate the rotation needed to put target element at top // Rock (index 0) starts at -PI/2, so target element should be at -PI/2 var targetAngleFromStart = targetIndex * anglePerElement; var rotationToTarget = -targetAngleFromStart; // Negative because wheel rotates clockwise // Final rotation combines base spins with precise positioning var finalRotation = baseRotation + rotationToTarget; // Ensure we stop at exact element positions by rounding to nearest element angle var exactElementPosition = Math.round(finalRotation / anglePerElement) * anglePerElement; finalRotation = exactElementPosition; // Reset wheel rotation for animation wheelContainer.rotation = 0; // Function to check if wheel stopped on disabled element and continue spinning function checkAndContinueSpinning() { // Get the current top element var currentTopElement = getTopElement(); // Find the button for this element var topButton = null; for (var i = 0; i < elementButtons.length; i++) { if (elementButtons[i].elementType === currentTopElement) { topButton = elementButtons[i]; break; } } // If the top element is disabled, continue spinning to next available element if (topButton && topButton.isDisabled) { // Find the next non-disabled element var nextAvailableIndex = -1; var currentIndex = elements.indexOf(currentTopElement); for (var step = 1; step < elements.length; step++) { var checkIndex = (currentIndex + step) % elements.length; var checkElement = elements[checkIndex]; var checkButton = null; for (var i = 0; i < elementButtons.length; i++) { if (elementButtons[i].elementType === checkElement) { checkButton = elementButtons[i]; break; } } if (checkButton && !checkButton.isDisabled) { nextAvailableIndex = checkIndex; break; } } if (nextAvailableIndex !== -1) { // Calculate additional rotation needed to reach next available element var additionalSteps = (nextAvailableIndex - currentIndex + elements.length) % elements.length; var additionalRotation = additionalSteps * anglePerElement; // Continue spinning to the next available element tween(wheelContainer, { rotation: wheelContainer.rotation - additionalRotation }, { duration: 1000, easing: tween.easeOut, onFinish: function onFinish() { // Check again in case we need to continue further checkAndContinueSpinning(); } }); return; } } // If we reach here, the wheel has stopped on a valid element // Reveal element if it was hidden for (var i = 0; i < elementButtons.length; i++) { if (elementButtons[i].elementType === currentTopElement) { revealElement(i); break; } } // Play sound for specific elements when AI selects them if (allElements.indexOf(currentTopElement) !== -1) { LK.getSound(currentTopElement).play(); } // Play sounds for new elements when AI selects them if (currentTopElement === 'plastic') { LK.getSound('plastic').play(); } if (currentTopElement === 'leather') { LK.getSound('leather').play(); } if (currentTopElement === 'sun') { LK.getSound('sun').play(); } if (currentTopElement === 'glass') { LK.getSound('glass').play(); } if (currentTopElement === 'cloth') { LK.getSound('cloth').play(); } if (currentTopElement === 'ash') { LK.getSound('ash').play(); } if (currentTopElement === 'fungus') { LK.getSound('fungus').play(); } if (currentTopElement === 'moon') { LK.getSound('moon').play(); } if (currentTopElement === 'salt') { LK.getSound('salt').play(); } if (aiComboSpins === 1) { // AI is building a combo - this is first element // Check if element is already in AI combo (should not happen, but safety check) if (aiCombo.indexOf(currentTopElement) === -1) { aiCombo.push(currentTopElement); } aiComboSpins = 2; updateAIComboDisplay(); // Start spinning again for second element LK.setTimeout(function () { startWheelSpin(); }, 1000); } else if (aiComboSpins === 2) { // AI is completing combo - this is second element // Check if element is already in AI combo to prevent duplicates if (aiCombo.indexOf(currentTopElement) === -1) { aiCombo.push(currentTopElement); aiComboSpins = 0; isWheelSpinning = false; updateAIComboDisplay(); // Check if this is AI's turn first - if so, let user choose next if (!playerGoesFirst && playerCombo.length === 0) { // AI went first, now let user choose LK.setTimeout(function () { // Check if this is AI's turn first - if so, let user choose next // Show "Choose 1 or 2 elements" message for 3 seconds // Create white background var userTurnBackground = LK.getAsset('userTurnBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 2 }); userTurnBackground.tint = 0xFFFFFF; userTurnBackground.alpha = 1; userTurnBackground.x = 1024; userTurnBackground.y = 1366; game.addChild(userTurnBackground); var userTurnMessage = new Text2('Your turn!\nChoose 1 or 2 elements', { size: 96, fill: 0xffffff, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3, align: 'center' }); userTurnMessage.anchor.set(0.5, 0.5); userTurnMessage.x = 1024; userTurnMessage.y = 1366; userTurnMessage.alpha = 0; game.addChild(userTurnMessage); // Animate message in tween(userTurnBackground, { alpha: 1 }, { duration: 500 }); tween(userTurnMessage, { alpha: 1 }, { duration: 500, onFinish: function onFinish() { // After 3 seconds, fade out and show normal instruction LK.setTimeout(function () { tween(userTurnBackground, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { userTurnBackground.destroy(); } }); tween(userTurnMessage, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { userTurnMessage.destroy(); instructionText.setText('Choose your element'); // Enable clicks after YOUR TURN message disappears clicksDisabled = false; } }); }, 3000); } }); tween(playerHeroImage, { alpha: 1 }, { duration: 200 }); tween(aiHeroImage, { alpha: 0.5 }, { duration: 200 }); gameState = 'selection'; }, 2000); } else { // Start battle after wheel stops LK.setTimeout(function () { startBattle(); }, 2000); // Wait 2 seconds before VS screen } } else { // AI selected same element twice, spin again for different element LK.setTimeout(function () { startWheelSpin(); }, 1000); } } else { // AI using single element isWheelSpinning = false; aiElement = currentTopElement; aiCombo = [currentTopElement]; // Ensure AI combo shows single element updateAIComboDisplay(); // Check if this is AI's turn first - if so, let user choose next if (!playerGoesFirst && playerCombo.length === 0) { // AI went first, now let user choose LK.setTimeout(function () { // Switch to user's turn // Show "Choose 1 or 2 elements" message for 3 seconds // Create white background var userTurnBackground = LK.getAsset('userTurnBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 2 }); userTurnBackground.tint = 0xFFFFFF; userTurnBackground.alpha = 1; userTurnBackground.x = 1024; userTurnBackground.y = 1366; game.addChild(userTurnBackground); var userTurnMessage = new Text2('Your turn!\nChoose 1 or 2 elements', { size: 96, fill: 0xffffff, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3, align: 'center' }); userTurnMessage.anchor.set(0.5, 0.5); userTurnMessage.x = 1024; userTurnMessage.y = 1366; userTurnMessage.alpha = 0; game.addChild(userTurnMessage); // Animate message in tween(userTurnBackground, { alpha: 1 }, { duration: 500 }); tween(userTurnMessage, { alpha: 1 }, { duration: 500, onFinish: function onFinish() { // After 3 seconds, fade out and show normal instruction LK.setTimeout(function () { tween(userTurnBackground, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { userTurnBackground.destroy(); } }); tween(userTurnMessage, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { userTurnMessage.destroy(); instructionText.setText('Choose your element'); // Enable clicks after YOUR TURN message disappears clicksDisabled = false; } }); }, 3000); } }); tween(playerHeroImage, { alpha: 1 }, { duration: 200 }); tween(aiHeroImage, { alpha: 0.5 }, { duration: 200 }); gameState = 'selection'; }, 2000); } else { // Start battle after wheel stops LK.setTimeout(function () { startBattle(); }, 2000); // Wait 2 seconds before VS screen } } } // Animate wheel spinning tween(wheelContainer, { rotation: finalRotation }, { duration: 2000 + Math.random() * 1000, // 2-3 seconds easing: tween.easeOut, onFinish: function onFinish() { // Check if we need to continue spinning checkAndContinueSpinning(); } }); } // AI element selection with intelligent combo avoidance function getAIElement() { // Get available elements from the full pool (not used by AI yet) var availableElements = []; for (var i = 0; i < allElements.length; i++) { if (usedAIElements.indexOf(allElements[i]) === -1) { availableElements.push(allElements[i]); } } // If no available elements, use all elements (shouldn't happen in normal gameplay) if (availableElements.length === 0) { availableElements = allElements.slice(); } // Get user's elements for counter strategy var userElements = []; if (playerElement) { userElements.push(playerElement); } if (playerCombo.length > 0) { for (var i = 0; i < playerCombo.length; i++) { if (userElements.indexOf(playerCombo[i]) === -1) { userElements.push(playerCombo[i]); } } } // Get boosted elements that are available var boostedAvailableElements = []; for (var i = 0; i < availableElements.length; i++) { if (hasWeatherBonus(availableElements[i])) { boostedAvailableElements.push(availableElements[i]); } } // AI intelligently avoids critical combos (registered combo effects) var safeElements = []; var criticalElements = []; for (var i = 0; i < availableElements.length; i++) { var element = availableElements[i]; var wouldCreateCriticalCombo = false; // Check if this element would create a critical combo with existing AI combo elements if (aiCombo.length > 0) { var testCombo = aiCombo.slice(); testCombo.push(element); if (getComboEffect(testCombo)) { wouldCreateCriticalCombo = true; } } if (wouldCreateCriticalCombo) { criticalElements.push(element); } else { safeElements.push(element); } } // AI prefers safe elements (80% chance to avoid critical combos) var elementsToChooseFrom = safeElements.length > 0 && Math.random() < 0.8 ? safeElements : availableElements; // Smart AI strategy: prioritize boosted elements and counter user // 1. First priority: Use boosted elements if available if (boostedAvailableElements.length > 0) { // Filter boosted elements to only safe ones var safeBoostedElements = []; for (var i = 0; i < boostedAvailableElements.length; i++) { if (elementsToChooseFrom.indexOf(boostedAvailableElements[i]) !== -1) { safeBoostedElements.push(boostedAvailableElements[i]); } } if (safeBoostedElements.length > 0) { // 50% chance to counter user with boosted elements if (Math.random() < 0.5 && userElements.length > 0) { var boostedCounters = []; for (var i = 0; i < safeBoostedElements.length; i++) { for (var j = 0; j < userElements.length; j++) { if (defeats[safeBoostedElements[i]] && defeats[safeBoostedElements[i]].indexOf(userElements[j]) !== -1) { boostedCounters.push(safeBoostedElements[i]); break; } } } if (boostedCounters.length > 0) { return boostedCounters[Math.floor(Math.random() * boostedCounters.length)]; } } // Use any available boosted element return safeBoostedElements[Math.floor(Math.random() * safeBoostedElements.length)]; } } // 2. Second priority: Counter user elements (50% chance) if (Math.random() < 0.5 && userElements.length > 0) { var counters = []; for (var i = 0; i < elementsToChooseFrom.length; i++) { for (var j = 0; j < userElements.length; j++) { if (defeats[elementsToChooseFrom[i]] && defeats[elementsToChooseFrom[i]].indexOf(userElements[j]) !== -1) { counters.push(elementsToChooseFrom[i]); break; } } } if (counters.length > 0) { return counters[Math.floor(Math.random() * counters.length)]; } } // 3. Fallback: Random selection from safe elements return elementsToChooseFrom[Math.floor(Math.random() * elementsToChooseFrom.length)]; } // Function to create individual attacking elements for animation function createAttackingElement(elementType, startX, startY, targetX, targetY, isPlayer, onComplete, effectType) { var attackElement = new Container(); attackElement.x = startX; attackElement.y = startY; game.addChild(attackElement); var icon = attackElement.attachAsset(elementType, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.8, scaleY: 0.8 }); // Add glow effect var glow = attackElement.attachAsset(elementType, { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2, alpha: 0.3 }); // Animate to target tween(attackElement, { x: targetX, y: targetY }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { // Impact effect - use effectType parameter to determine which effect to show var effectAsset = effectType === 'lose' ? 'loseEffect' : 'winEffect'; var impact = LK.getAsset(effectAsset, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); impact.x = targetX; impact.y = targetY; game.addChild(impact); tween(impact, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 500, onFinish: function onFinish() { impact.destroy(); } }); attackElement.destroy(); if (onComplete) { onComplete(); } } }); } // Function to animate battle results with element interactions function animateBattleResults(result, playerCriticalFailure, aiCriticalFailure) { var playerElements = playerElement ? [playerElement] : playerCombo; var aiElements = aiElement ? [aiElement] : aiCombo; var animationsCompleted = 0; var totalAnimations = 0; // Calculate total animations needed if (playerCriticalFailure && aiCriticalFailure) { totalAnimations = playerElements.length + aiElements.length; } else if (playerCriticalFailure) { totalAnimations = playerElements.length; } else if (aiCriticalFailure) { totalAnimations = aiElements.length; } else { totalAnimations = playerElements.length * aiElements.length; } function onAnimationComplete() { animationsCompleted++; if (animationsCompleted >= totalAnimations) { // All animations complete, show result after delay LK.setTimeout(function () { // Apply appropriate effects to battle elements based on critical failures if (playerCriticalFailure && aiCriticalFailure) { playerBattleElement.playLoseAnimation(); aiBattleElement.playLoseAnimation(); } else if (playerCriticalFailure) { playerBattleElement.playLoseAnimation(); } else if (aiCriticalFailure) { aiBattleElement.playLoseAnimation(); } showBattleResult(); }, 1000); } } // Delay before starting animations LK.setTimeout(function () { if (playerCriticalFailure && aiCriticalFailure) { // Both have critical failures - show animations for both sides for (var i = 0; i < playerElements.length; i++) { var startX = playerBattleElement.x + (i === 0 ? -50 : 50); var startY = playerBattleElement.y; var targetX = playerBattleElement.x + (i === 0 ? 50 : -50); var targetY = playerBattleElement.y; createAttackingElement(playerElements[i], startX, startY, targetX, targetY, true, onAnimationComplete, 'lose'); } for (var i = 0; i < aiElements.length; i++) { var startX = aiBattleElement.x + (i === 0 ? -50 : 50); var startY = aiBattleElement.y; var targetX = aiBattleElement.x + (i === 0 ? 50 : -50); var targetY = aiBattleElement.y; createAttackingElement(aiElements[i], startX, startY, targetX, targetY, false, onAnimationComplete, 'lose'); } } else if (playerCriticalFailure) { // Player elements attack each other for (var i = 0; i < playerElements.length; i++) { var startX = playerBattleElement.x + (i === 0 ? -50 : 50); var startY = playerBattleElement.y; var targetX = playerBattleElement.x + (i === 0 ? 50 : -50); var targetY = playerBattleElement.y; createAttackingElement(playerElements[i], startX, startY, targetX, targetY, true, onAnimationComplete, 'lose'); } } else if (aiCriticalFailure) { // AI elements attack each other for (var i = 0; i < aiElements.length; i++) { var startX = aiBattleElement.x + (i === 0 ? -50 : 50); var startY = aiBattleElement.y; var targetX = aiBattleElement.x + (i === 0 ? 50 : -50); var targetY = aiBattleElement.y; createAttackingElement(aiElements[i], startX, startY, targetX, targetY, false, onAnimationComplete, 'lose'); } } else { // Normal battle - elements attack opposing elements for (var i = 0; i < playerElements.length; i++) { for (var j = 0; j < aiElements.length; j++) { var startX = playerBattleElement.x + (playerElements.length > 1 ? i === 0 ? -50 : 50 : 0); var startY = playerBattleElement.y; var targetX = aiBattleElement.x + (aiElements.length > 1 ? j === 0 ? -50 : 50 : 0); var targetY = aiBattleElement.y; createAttackingElement(playerElements[i], startX, startY, targetX, targetY, true, onAnimationComplete, 'win'); } } // AI elements attack back if not a clear player victory if (result !== 'player') { for (var i = 0; i < aiElements.length; i++) { for (var j = 0; j < playerElements.length; j++) { var startX = aiBattleElement.x + (aiElements.length > 1 ? i === 0 ? -50 : 50 : 0); var startY = aiBattleElement.y; var targetX = playerBattleElement.x + (playerElements.length > 1 ? j === 0 ? -50 : 50 : 0); var targetY = playerBattleElement.y; totalAnimations++; // Increase total since we're adding more animations createAttackingElement(aiElements[i], startX, startY, targetX, targetY, false, onAnimationComplete, 'win'); } } } } }, 1500); // Start animations 1.5 seconds after VS text appears } // Start battle phase function startBattle() { gameState = 'battle'; // aiElement is already set by wheel spin, only get it if not set if (!aiElement && aiCombo.length === 0) { aiElement = getAIElement(); } // Check for protective combos and award defensive tokens for AI only (player already handled in selection) var aiComboIsProtective = isProtectiveCombo(aiCombo); if (aiComboIsProtective) { var comboKey = getComboKey(aiCombo); var protectiveComboName = ''; if (comboKey === 'metal+rock') { protectiveComboName = 'Armored Defense'; } else if (comboKey === 'cloth+leather') { protectiveComboName = 'Padded Protection'; } else if (comboKey === 'ice+water') { protectiveComboName = 'Frozen Shield'; } else if (comboKey === 'sponge+tree') { protectiveComboName = 'Natural Buffer'; } else if (comboKey === 'glass+plastic') { protectiveComboName = 'Composite Shield'; } else if (comboKey === 'salt+sand') { protectiveComboName = 'Crystalline Barrier'; } else if (comboKey === 'ash+smoke') { protectiveComboName = 'Obscuring Veil'; } else if (comboKey === 'moon+sun') { protectiveComboName = 'Cosmic Balance'; } else if (comboKey === 'ice+metal') { protectiveComboName = 'Reinforced Barrier'; } else if (comboKey === 'rock+sand') { protectiveComboName = 'Stone Wall'; } else if (comboKey === 'oil+plastic') { protectiveComboName = 'Waterproof Shell'; } else if (comboKey === 'fungus+leather') { protectiveComboName = 'Bio Armor'; } else if (comboKey === 'gas+smoke') { protectiveComboName = 'Toxic Screen'; } else if (comboKey === 'cloth+paper') { protectiveComboName = 'Layered Guard'; } else if (comboKey === 'air+gas') { protectiveComboName = 'Pressure Shield'; } else if (comboKey === 'lightning+metal') { protectiveComboName = 'Electric Fence'; } else if (comboKey === 'paper+tree') { protectiveComboName = 'Fiber Defense'; } else if (comboKey === 'sponge+water') { protectiveComboName = 'Absorption Shield'; } else if (comboKey === 'metal+leather') { protectiveComboName = 'Reinforced Armor'; } aiDefenseTokens++; updateDefenseTokenDisplays(); // Disable clicks when AI defensive combo notification shows clicksDisabled = true; // Create white background for AI defensive combo notification var aiDefenseBackground = LK.getAsset('userTurnBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 2 }); aiDefenseBackground.tint = 0xFFFFFF; aiDefenseBackground.alpha = 1; aiDefenseBackground.x = 1024; aiDefenseBackground.y = 1366; game.addChild(aiDefenseBackground); // Show notification for AI defensive combo var aiDefenseNotification = new Text2('AI Created ' + protectiveComboName + '!\n+1 AI Defense Token', { size: 36, fill: 0xffffff, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3, align: 'center' }); aiDefenseNotification.anchor.set(0.5, 0.5); aiDefenseNotification.x = 1024; aiDefenseNotification.y = 1366; aiDefenseNotification.alpha = 0; game.addChild(aiDefenseNotification); tween(aiDefenseBackground, { alpha: 1 }, { duration: 500 }); tween(aiDefenseNotification, { alpha: 1 }, { duration: 500, onFinish: function onFinish() { LK.setTimeout(function () { tween(aiDefenseBackground, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { aiDefenseBackground.destroy(); } }); tween(aiDefenseNotification, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { aiDefenseNotification.destroy(); } }); }, 1000); } }); // Create floating +1 animation near AI defense display var aiTokenAnimation = new Text2('+1', { size: 36, fill: 0xFF6666, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 2 }); aiTokenAnimation.anchor.set(0.5, 0.5); LK.gui.bottom.addChild(aiTokenAnimation); aiTokenAnimation.x = 600; aiTokenAnimation.y = -120; aiTokenAnimation.alpha = 0; tween(aiTokenAnimation, { alpha: 1, y: -150, scaleX: 1.5, scaleY: 1.5 }, { duration: 800, onFinish: function onFinish() { tween(aiTokenAnimation, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { aiTokenAnimation.destroy(); } }); } }); } // Track used elements if (playerElement && usedPlayerElements.indexOf(playerElement) === -1) { usedPlayerElements.push(playerElement); } if (playerCombo.length > 0) { for (var i = 0; i < playerCombo.length; i++) { if (usedPlayerElements.indexOf(playerCombo[i]) === -1) { usedPlayerElements.push(playerCombo[i]); } } } if (aiElement && usedAIElements.indexOf(aiElement) === -1) { usedAIElements.push(aiElement); } if (aiCombo.length > 0) { for (var i = 0; i < aiCombo.length; i++) { if (usedAIElements.indexOf(aiCombo[i]) === -1) { usedAIElements.push(aiCombo[i]); } } } // Reset hover state hoveredButton = null; // Hide victory arrows during battle for (var i = 0; i < victoryArrows.length; i++) { tween(victoryArrows[i], { alpha: 0 }, { duration: 300 }); } // Hide combo displays during battle if (comboDisplay) { tween(comboDisplay, { alpha: 0 }, { duration: 300 }); } // Hide AI combo display during battle if (aiComboDisplay) { tween(aiComboDisplay, { alpha: 0 }, { duration: 300 }); } // Hide triangle if it exists if (window.triangleMarker) { window.triangleMarker.alpha = 0; } // Hide selection elements and disable them for (var i = 0; i < elementButtons.length; i++) { elementButtons[i].setDisabled(true); tween(elementButtons[i], { alpha: 0 }, { duration: 300 }); } instructionText.setText('BATTLE!'); // Create battle elements var playerDisplayElement = playerElement || playerCombo[0] || 'unknown'; var aiDisplayElement = aiElement || aiCombo[0] || 'unknown'; playerBattleElement = game.addChild(new BattleElement(playerDisplayElement, true)); playerBattleElement.x = 400; playerBattleElement.y = 1100; playerBattleElement.alpha = 0; // Add second element icon and label for player combo if (playerCombo.length === 2) { var playerSecondIcon = LK.getAsset(playerCombo[1], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6 }); playerSecondIcon.x = 100; playerSecondIcon.y = -80; playerBattleElement.addChild(playerSecondIcon); playerBattleElement.addSecondElementLabel(playerCombo[1]); } aiBattleElement = game.addChild(new BattleElement(aiDisplayElement, false)); aiBattleElement.x = 1648; aiBattleElement.y = 1100; aiBattleElement.alpha = 0; // Add second element icon and label for AI combo if (aiCombo.length === 2) { var aiSecondIcon = LK.getAsset(aiCombo[1], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.6, scaleY: 0.6 }); aiSecondIcon.x = -80; aiSecondIcon.y = -100; aiBattleElement.addChild(aiSecondIcon); aiBattleElement.addSecondElementLabel(aiCombo[1]); } // Animate battle elements in tween(playerBattleElement, { alpha: 1, x: 512 }, { duration: 500 }); tween(aiBattleElement, { alpha: 1, x: 1536 }, { duration: 500 }); // Create VS battle text with combo/element names var playerDisplayText = ''; var aiDisplayText = ''; // Get player display name if (playerCombo.length === 2) { var playerComboName = getComboName(playerCombo); var playerComboEffect = getComboEffect(playerCombo); if (playerComboEffect) { playerDisplayText = playerComboEffect.name; } else if (playerComboName) { playerDisplayText = playerComboName; } else { playerDisplayText = playerCombo.join(' + ').toUpperCase(); } } else if (playerElement) { playerDisplayText = playerElement.charAt(0).toUpperCase() + playerElement.slice(1); } else { playerDisplayText = 'PLAYER'; } // Get AI display name if (aiCombo.length === 2) { var aiComboName = getComboName(aiCombo); var aiComboEffect = getComboEffect(aiCombo); if (aiComboEffect) { aiDisplayText = aiComboEffect.name; } else if (aiComboName) { aiDisplayText = aiComboName; } else { aiDisplayText = aiCombo.join(' + ').toUpperCase(); } } else if (aiElement) { aiDisplayText = aiElement.charAt(0).toUpperCase() + aiElement.slice(1); } else { aiDisplayText = 'AI'; } // Add battle instruction text vsText = new Text2(playerDisplayText + '\nVS\n' + aiDisplayText, { size: 80, fill: 0xFF6B35, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 8, align: 'center' }); vsText.anchor.set(0.5, 0.5); vsText.x = 1024; vsText.y = 1100; vsText.alpha = 0; game.addChild(vsText); // Create battle result text (separate from BATTLE! text) battleResultText = new Text2('', { size: 60, fill: 0xFFFFFF, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 6, align: "center" }); battleResultText.anchor.set(0.5, 0.5); battleResultText.x = 1024; battleResultText.y = 1500; battleResultText.alpha = 0; game.addChild(battleResultText); tween(vsText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 1500, onFinish: function onFinish() { LK.getSound('battle').play(); // Calculate battle result and critical failures for animation var result = checkBattleResult(); var playerComboEffect = playerCombo.length === 2 ? getComboEffect(playerCombo) : null; var aiComboEffect = aiCombo.length === 2 ? getComboEffect(aiCombo) : null; var playerIsFailure = false; var aiIsFailure = false; // Check for critical failures if (playerCombo.length === 2) { if (defeats[playerCombo[0]] && defeats[playerCombo[0]].indexOf(playerCombo[1]) !== -1 || defeats[playerCombo[1]] && defeats[playerCombo[1]].indexOf(playerCombo[0]) !== -1) { playerIsFailure = true; } if (playerComboEffect && playerComboEffect.multiplier === 0) { playerIsFailure = true; } } if (aiCombo.length === 2) { if (defeats[aiCombo[0]] && defeats[aiCombo[0]].indexOf(aiCombo[1]) !== -1 || defeats[aiCombo[1]] && defeats[aiCombo[1]].indexOf(aiCombo[0]) !== -1) { aiIsFailure = true; } if (aiComboEffect && aiComboEffect.multiplier === 0) { aiIsFailure = true; } } // Start battle animations animateBattleResults(result, playerIsFailure, aiIsFailure); } }); } // Get action keyword for how one element defeats another function getActionKeyword(winner, loser) { if (actionKeywords[winner] && actionKeywords[winner][loser]) { return actionKeywords[winner][loser]; } return 'defeats'; } // Check battle result function checkBattleResult() { // Determine what elements are actually battling var playerBattleElements = playerElement ? [playerElement] : playerCombo; var aiBattleElements = aiElement ? [aiElement] : aiCombo; // Check for combo effects var playerComboEffect = playerCombo.length === 2 ? getComboEffect(playerCombo) : null; var aiComboEffect = aiCombo.length === 2 ? getComboEffect(aiCombo) : null; // Check for critical failures (opposing elements that defeat each other) var playerCriticalFailure = false; var aiCriticalFailure = false; if (playerCombo.length === 2) { // Check if player combo elements defeat each other (critical failure) if (defeats[playerCombo[0]] && defeats[playerCombo[0]].indexOf(playerCombo[1]) !== -1 || defeats[playerCombo[1]] && defeats[playerCombo[1]].indexOf(playerCombo[0]) !== -1) { playerCriticalFailure = true; } // Also check if combo effect has multiplier 0 (registered critical failure) if (playerComboEffect && playerComboEffect.multiplier === 0) { playerCriticalFailure = true; } } if (aiCombo.length === 2) { // Check if AI combo elements defeat each other (critical failure) if (defeats[aiCombo[0]] && defeats[aiCombo[0]].indexOf(aiCombo[1]) !== -1 || defeats[aiCombo[1]] && defeats[aiCombo[1]].indexOf(aiCombo[0]) !== -1) { aiCriticalFailure = true; } // Also check if combo effect has multiplier 0 (registered critical failure) if (aiComboEffect && aiComboEffect.multiplier === 0) { aiCriticalFailure = true; } } // Critical failures override all other battle logic if (playerCriticalFailure && !aiCriticalFailure) { return 'ai'; // Player loses due to critical failure } else if (aiCriticalFailure && !playerCriticalFailure) { return 'player'; // AI loses due to critical failure } else if (playerCriticalFailure && aiCriticalFailure) { return 'tie'; // Both have critical failures } // Calculate wins using combo logic var playerWins = false; var aiWins = false; // Player vs AI battle logic for (var i = 0; i < playerBattleElements.length && !playerWins; i++) { for (var j = 0; j < aiBattleElements.length && !playerWins; j++) { if (defeats[playerBattleElements[i]] && defeats[playerBattleElements[i]].indexOf(aiBattleElements[j]) !== -1) { playerWins = true; } // Check combo extra defeats if (playerComboEffect && playerComboEffect.defeatsExtra.indexOf(aiBattleElements[j]) !== -1) { playerWins = true; } } } for (var i = 0; i < aiBattleElements.length && !aiWins; i++) { for (var j = 0; j < playerBattleElements.length && !aiWins; j++) { if (defeats[aiBattleElements[i]] && defeats[aiBattleElements[i]].indexOf(playerBattleElements[j]) !== -1) { aiWins = true; } // Check combo extra defeats if (aiComboEffect && aiComboEffect.defeatsExtra.indexOf(playerBattleElements[j]) !== -1) { aiWins = true; } } } // Apply weather effects var playerHasBonus = false; var aiHasBonus = false; var playerHasPenalty = false; var aiHasPenalty = false; for (var i = 0; i < playerBattleElements.length; i++) { if (hasWeatherBonus(playerBattleElements[i])) { playerHasBonus = true; break; } if (hasWeatherPenalty(playerBattleElements[i])) { playerHasPenalty = true; break; } } for (var i = 0; i < aiBattleElements.length; i++) { if (hasWeatherBonus(aiBattleElements[i])) { aiHasBonus = true; break; } if (hasWeatherPenalty(aiBattleElements[i])) { aiHasPenalty = true; break; } } // Weather effects can override normal defeat relationships if ((playerHasBonus || aiHasPenalty) && !(aiHasBonus || playerHasPenalty)) { // Player gets weather advantage (bonus or opponent penalty) if (!playerWins && !aiWins) { // In a tie, weather advantage wins return 'player'; } // If AI would normally win, weather advantage creates a tie instead if (aiWins && !playerWins) { return 'tie'; } } else if ((aiHasBonus || playerHasPenalty) && !(playerHasBonus || aiHasPenalty)) { // AI gets weather advantage (bonus or opponent penalty) if (!playerWins && !aiWins) { // In a tie, weather advantage wins return 'ai'; } // If player would normally win, weather advantage creates a tie instead if (playerWins && !aiWins) { return 'tie'; } } // Combo win condition: A combo wins if either of its elements defeats both of the opponent's elements. var playerComboWinsAll = false; if (playerBattleElements.length === 2 && aiBattleElements.length === 2) { if (defeats[playerBattleElements[0]] && defeats[playerBattleElements[0]].indexOf(aiBattleElements[0]) !== -1 && defeats[playerBattleElements[0]].indexOf(aiBattleElements[1]) !== -1) { playerComboWinsAll = true; } else if (defeats[playerBattleElements[1]] && defeats[playerBattleElements[1]].indexOf(aiBattleElements[0]) !== -1 && defeats[playerBattleElements[1]].indexOf(aiBattleElements[1]) !== -1) { playerComboWinsAll = true; } } var aiComboWinsAll = false; if (playerBattleElements.length === 2 && aiBattleElements.length === 2) { if (defeats[aiBattleElements[0]] && defeats[aiBattleElements[0]].indexOf(playerBattleElements[0]) !== -1 && defeats[aiBattleElements[0]].indexOf(playerBattleElements[1]) !== -1) { aiComboWinsAll = true; } else if (defeats[aiBattleElements[1]] && defeats[aiBattleElements[1]].indexOf(playerBattleElements[0]) !== -1 && defeats[aiBattleElements[1]].indexOf(playerBattleElements[1]) !== -1) { aiComboWinsAll = true; } } // Apply weather effects var playerHasBonus = false; var aiHasBonus = false; for (var i = 0; i < playerBattleElements.length; i++) { if (hasWeatherBonus(playerBattleElements[i])) { playerHasBonus = true; break; } } for (var i = 0; i < aiBattleElements.length; i++) { if (hasWeatherBonus(aiBattleElements[i])) { aiHasBonus = true; break; } } // Weather bonus can override normal defeat relationships if (playerHasBonus && !aiHasBonus) { // Player gets weather advantage if (!playerWins && !aiWins) { // In a tie, weather bonus wins return 'player'; } // If AI would normally win, weather bonus creates a tie instead if (aiWins && !playerWins) { return 'tie'; } } else if (aiHasBonus && !playerHasBonus) { // AI gets weather advantage if (!playerWins && !aiWins) { // In a tie, weather bonus wins return 'ai'; } // If player would normally win, weather bonus creates a tie instead if (playerWins && !aiWins) { return 'tie'; } } // Normal battle logic if no weather advantage or both have advantage if (playerComboWinsAll && !aiComboWinsAll) { return 'player'; } else if (aiComboWinsAll && !playerComboWinsAll) { return 'ai'; } else if (playerWins && !aiWins) { return 'player'; } else if (aiWins && !playerWins) { return 'ai'; } else { return 'tie'; } } // Show battle result with life system and critical mechanics function showBattleResult() { gameState = 'result'; var result = checkBattleResult(); // Determine if combos are critical (registered) or failures (unregistered) var playerComboEffect = playerCombo.length === 2 ? getComboEffect(playerCombo) : null; var aiComboEffect = aiCombo.length === 2 ? getComboEffect(aiCombo) : null; var playerIsCritical = playerCombo.length === 2 && playerComboEffect !== null && playerComboEffect.multiplier > 1; var playerIsFailure = false; var aiIsCritical = aiCombo.length === 2 && aiComboEffect !== null && aiComboEffect.multiplier > 1; var aiIsFailure = false; // Check for critical failures (opposing elements that defeat each other) if (playerCombo.length === 2) { // Check if player combo elements defeat each other (critical failure) if (defeats[playerCombo[0]] && defeats[playerCombo[0]].indexOf(playerCombo[1]) !== -1 || defeats[playerCombo[1]] && defeats[playerCombo[1]].indexOf(playerCombo[0]) !== -1) { playerIsFailure = true; } // Also check if combo effect has multiplier 0 (registered critical failure) if (playerComboEffect && playerComboEffect.multiplier === 0) { playerIsFailure = true; } } if (aiCombo.length === 2) { // Check if AI combo elements defeat each other (critical failure) if (defeats[aiCombo[0]] && defeats[aiCombo[0]].indexOf(aiCombo[1]) !== -1 || defeats[aiCombo[1]] && defeats[aiCombo[1]].indexOf(aiCombo[0]) !== -1) { aiIsFailure = true; } // Also check if combo effect has multiplier 0 (registered critical failure) if (aiComboEffect && aiComboEffect.multiplier === 0) { aiIsFailure = true; } } var baseDamage = 10; var playerDamage = baseDamage; var aiDamage = baseDamage; // Apply critical effects if (playerIsCritical) { // Critical success - double damage to opponent } else if (playerIsFailure) { // Critical failure - damage to self (applied in result section) } if (aiIsCritical) { // Critical success - double damage to opponent } else if (aiIsFailure) { // Critical failure - damage to self (applied in result section) } if (result === 'player') { LK.getSound('win').play(); playerBattleElement.playWinAnimation(); aiBattleElement.playLoseAnimation(); // Player wins - damage to AI var damage = playerIsCritical ? baseDamage * 2 : baseDamage; // Apply AI defensive tokens to reduce damage if (aiDefenseTokens > 0) { var damageReduction = Math.min(aiDefenseTokens * 5, damage); damage = Math.max(0, damage - damageReduction); aiDefenseTokens--; updateDefenseTokenDisplays(); // Create floating -1 animation for AI defense token removal var aiTokenRemovalAnimation = new Text2('-1', { size: 36, fill: 0xFF6666, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 2 }); aiTokenRemovalAnimation.anchor.set(0.5, 0.5); LK.gui.bottom.addChild(aiTokenRemovalAnimation); aiTokenRemovalAnimation.x = 600; aiTokenRemovalAnimation.y = -120; aiTokenRemovalAnimation.alpha = 0; tween(aiTokenRemovalAnimation, { alpha: 1, y: -90, scaleX: 1.5, scaleY: 1.5 }, { duration: 800, onFinish: function onFinish() { tween(aiTokenRemovalAnimation, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { aiTokenRemovalAnimation.destroy(); } }); } }); } aiLife -= damage; if (aiLife < 0) { aiLife = 0; } // Create result text with combo names var playerDesc = ''; if (playerElement) { playerDesc = playerElement.charAt(0).toUpperCase() + playerElement.slice(1); } else if (playerCombo.length === 2) { var playerComboName = getComboName(playerCombo); playerDesc = playerComboName || playerCombo.map(function (element) { return element.charAt(0).toUpperCase() + element.slice(1); }).join(' + '); } var aiDesc = ''; if (aiElement) { aiDesc = aiElement.charAt(0).toUpperCase() + aiElement.slice(1); } else if (aiCombo.length === 2) { var aiComboName = getComboName(aiCombo); aiDesc = aiComboName || aiCombo.map(function (element) { return element.charAt(0).toUpperCase() + element.slice(1); }).join(' + '); } var actionWord = 'defeats'; if (playerElement && aiElement) { actionWord = getActionKeyword(playerElement, aiElement); } var resultText = playerDesc + ' ' + actionWord + ' ' + aiDesc + '!\nYOU WIN! -' + damage + ' AI Life'; if (playerIsCritical) { resultText += '\nCRITICAL SUCCESS!'; } // Add explanation for why player won var explanation = ''; if (playerCombo.length === 2 && aiCombo.length === 2) { // Combo vs combo explanation var playerComboEffect = getComboEffect(playerCombo); if (playerComboEffect && playerComboEffect.defeatsExtra.length > 0) { explanation = '\n' + playerComboEffect.name + ' combo defeats extra elements!'; } else { // Find which specific elements won var winningElements = []; for (var pi = 0; pi < playerCombo.length; pi++) { for (var ai = 0; ai < aiCombo.length; ai++) { if (defeats[playerCombo[pi]] && defeats[playerCombo[pi]].indexOf(aiCombo[ai]) !== -1) { var actionWord = getActionKeyword(playerCombo[pi], aiCombo[ai]); winningElements.push(playerCombo[pi].charAt(0).toUpperCase() + playerCombo[pi].slice(1) + ' ' + actionWord + ' ' + aiCombo[ai].charAt(0).toUpperCase() + aiCombo[ai].slice(1)); } } } if (winningElements.length > 0) { explanation = '\n' + winningElements.join(',\n') + '!'; } else { explanation = '\nPlayer combo overwhelms AI combo through strategic element advantage!'; } } } else if (playerCombo.length === 2 && aiElement) { // Combo vs single explanation var winningElements = []; for (var i = 0; i < playerCombo.length; i++) { if (defeats[playerCombo[i]] && defeats[playerCombo[i]].indexOf(aiElement) !== -1) { var actionWord = getActionKeyword(playerCombo[i], aiElement); winningElements.push(playerCombo[i].charAt(0).toUpperCase() + playerCombo[i].slice(1) + ' ' + actionWord + ' ' + aiElement.charAt(0).toUpperCase() + aiElement.slice(1)); } } if (winningElements.length > 0) { explanation = '\n' + winningElements.join(',\n') + '!'; } else { explanation = '\nPlayer combo gains strategic advantage over AI single element!'; } } else if (playerElement && aiCombo.length === 2) { // Single vs combo explanation var winningElements = []; for (var i = 0; i < aiCombo.length; i++) { if (defeats[playerElement] && defeats[playerElement].indexOf(aiCombo[i]) !== -1) { var actionWord = getActionKeyword(playerElement, aiCombo[i]); winningElements.push(playerElement.charAt(0).toUpperCase() + playerElement.slice(1) + ' ' + actionWord + ' ' + aiCombo[i].charAt(0).toUpperCase() + aiCombo[i].slice(1)); } } if (winningElements.length > 0) { explanation = '\n' + winningElements.join(',\n') + '!'; } else { explanation = '\nPlayer element exploits critical weakness in AI combo structure!'; } } else if (playerElement && aiElement) { // Single vs single explanation var actionDesc = getActionKeyword(playerElement, aiElement); explanation = '\n' + (playerElement.charAt(0).toUpperCase() + playerElement.slice(1)) + ' ' + actionDesc + ' ' + (aiElement.charAt(0).toUpperCase() + aiElement.slice(1)) + ' by nature!'; } // Check for weather bonus explanation var playerHasBonus = false; for (var i = 0; i < (playerElement ? [playerElement] : playerCombo).length; i++) { if (hasWeatherBonus((playerElement ? [playerElement] : playerCombo)[i])) { playerHasBonus = true; break; } } if (playerHasBonus) { explanation += '\nWeather boost gives you the advantage!'; } resultText += explanation; battleResultText.setText(resultText); battleResultText.tint = 0x00ff00; battleResultText.alpha = 1; tween(battleResultText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 500 }); // Shake/blink animation for SUCCESS // Check for AI death if (playerLife > 0 && aiLife <= 0) { LK.showYouWin(); return; } } else if (result === 'ai') { LK.getSound('lose').play(); playerBattleElement.playLoseAnimation(); aiBattleElement.playWinAnimation(); // AI wins - damage to player var damage = aiIsCritical ? baseDamage * 2 : baseDamage; // Apply player defensive tokens to reduce damage if (playerDefenseTokens > 0) { var damageReduction = Math.min(playerDefenseTokens * 5, damage); damage = Math.max(0, damage - damageReduction); playerDefenseTokens--; updateDefenseTokenDisplays(); // Create floating -1 animation for player defense token removal var playerTokenRemovalAnimation = new Text2('-1', { size: 36, fill: 0x08d0f8, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 2 }); playerTokenRemovalAnimation.anchor.set(0.5, 0.5); LK.gui.bottom.addChild(playerTokenRemovalAnimation); playerTokenRemovalAnimation.x = -600; playerTokenRemovalAnimation.y = -120; playerTokenRemovalAnimation.alpha = 0; tween(playerTokenRemovalAnimation, { alpha: 1, y: -90, scaleX: 1.5, scaleY: 1.5 }, { duration: 800, onFinish: function onFinish() { tween(playerTokenRemovalAnimation, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { playerTokenRemovalAnimation.destroy(); } }); } }); } playerLife -= damage; if (playerLife < 0) { playerLife = 0; } var aiDesc = ''; if (aiElement) { aiDesc = aiElement.charAt(0).toUpperCase() + aiElement.slice(1); } else if (aiCombo.length === 2) { var aiComboName = getComboName(aiCombo); aiDesc = aiComboName || aiCombo.map(function (element) { return element.charAt(0).toUpperCase() + element.slice(1); }).join(' + '); } var playerDesc = ''; if (playerElement) { playerDesc = playerElement.charAt(0).toUpperCase() + playerElement.slice(1); } else if (playerCombo.length === 2) { var playerComboName = getComboName(playerCombo); playerDesc = playerComboName || playerCombo.map(function (element) { return element.charAt(0).toUpperCase() + element.slice(1); }).join(' + '); } var actionWord = 'defeats'; if (aiElement && playerElement) { actionWord = getActionKeyword(aiElement, playerElement); } var resultText = aiDesc + ' ' + actionWord + ' ' + playerDesc + '!\nYOU LOSE! -' + damage + ' Life'; if (aiIsCritical) { resultText += '\nAI CRITICAL SUCCESS!'; } // Add explanation for why AI won var explanation = ''; if (aiCombo.length === 2 && playerCombo.length === 2) { // Combo vs combo explanation var aiComboEffect = getComboEffect(aiCombo); if (aiComboEffect && aiComboEffect.defeatsExtra.length > 0) { explanation = '\n' + aiComboEffect.name + ' combo defeats extra elements!'; } else { // Find which specific elements won var winningElements = []; for (var ai = 0; ai < aiCombo.length; ai++) { for (var pi = 0; pi < playerCombo.length; pi++) { if (defeats[aiCombo[ai]] && defeats[aiCombo[ai]].indexOf(playerCombo[pi]) !== -1) { var actionWord = getActionKeyword(aiCombo[ai], playerCombo[pi]); winningElements.push(aiCombo[ai].charAt(0).toUpperCase() + aiCombo[ai].slice(1) + ' ' + actionWord + ' ' + playerCombo[pi].charAt(0).toUpperCase() + playerCombo[pi].slice(1)); } } } if (winningElements.length > 0) { explanation = '\n' + winningElements.join(',\n') + '!'; } else { explanation = '\nAI combo achieves strategic dominance over player combo\n through superior element synergy!'; } } } else if (aiCombo.length === 2 && playerElement) { // Combo vs single explanation var winningElements = []; for (var i = 0; i < aiCombo.length; i++) { if (defeats[aiCombo[i]] && defeats[aiCombo[i]].indexOf(playerElement) !== -1) { var actionWord = getActionKeyword(aiCombo[i], playerElement); winningElements.push(aiCombo[i].charAt(0).toUpperCase() + aiCombo[i].slice(1) + ' ' + actionWord + ' ' + playerElement.charAt(0).toUpperCase() + playerElement.slice(1)); } } if (winningElements.length > 0) { explanation = '\n' + winningElements.join(',\n') + '!'; } else { explanation = '\nAI combo achieves overwhelming tactical advantage\n against player single element!'; } } else if (aiElement && playerCombo.length === 2) { // Single vs combo explanation var winningElements = []; for (var i = 0; i < playerCombo.length; i++) { if (defeats[aiElement] && defeats[aiElement].indexOf(playerCombo[i]) !== -1) { var actionWord = getActionKeyword(aiElement, playerCombo[i]); winningElements.push(aiElement.charAt(0).toUpperCase() + aiElement.slice(1) + ' ' + actionWord + ' ' + playerCombo[i].charAt(0).toUpperCase() + playerCombo[i].slice(1)); } } if (winningElements.length > 0) { explanation = '\n' + winningElements.join(',\n') + '!'; } else { explanation = '\nAI element exploits critical structural weakness in player combo!'; } } else if (aiElement && playerElement) { // Single vs single explanation var actionDesc = getActionKeyword(aiElement, playerElement); explanation = '\n' + (aiElement.charAt(0).toUpperCase() + aiElement.slice(1)) + ' ' + actionDesc + ' ' + (playerElement.charAt(0).toUpperCase() + playerElement.slice(1)) + ' by nature!'; } // Check for weather bonus explanation var aiHasBonus = false; for (var i = 0; i < (aiElement ? [aiElement] : aiCombo).length; i++) { if (hasWeatherBonus((aiElement ? [aiElement] : aiCombo)[i])) { aiHasBonus = true; break; } } if (aiHasBonus) { explanation += '\nWeather boost gives AI the advantage!'; } resultText += explanation; battleResultText.setText(resultText); battleResultText.tint = 0xff0000; battleResultText.alpha = 1; tween(battleResultText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 500 }); // Shake/blink animation for FAILURE // Check for player death if (playerLife <= 0 && aiLife > 0) { LK.showGameOver(); return; } } else { // Handle critical failures in ties if (playerIsFailure && !aiIsFailure) { // Player critical failure - damage to player var damage = baseDamage; // Apply player defensive tokens to reduce damage if (playerDefenseTokens > 0) { var damageReduction = Math.min(playerDefenseTokens * 5, damage); damage = Math.max(0, damage - damageReduction); playerDefenseTokens--; updateDefenseTokenDisplays(); // Create floating -1 animation for player defense token removal var playerTokenRemovalAnimation = new Text2('-1', { size: 36, fill: 0x08d0f8, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 2 }); playerTokenRemovalAnimation.anchor.set(0.5, 0.5); LK.gui.bottom.addChild(playerTokenRemovalAnimation); playerTokenRemovalAnimation.x = -600; playerTokenRemovalAnimation.y = -120; playerTokenRemovalAnimation.alpha = 0; tween(playerTokenRemovalAnimation, { alpha: 1, y: -90, scaleX: 1.5, scaleY: 1.5 }, { duration: 800, onFinish: function onFinish() { tween(playerTokenRemovalAnimation, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { playerTokenRemovalAnimation.destroy(); } }); } }); } playerLife -= damage; if (playerLife < 0) { playerLife = 0; } var explanation = '\nUSER selected elements of opposite effects!'; if (playerCombo.length === 2) { var elem1 = playerCombo[0].charAt(0).toUpperCase() + playerCombo[0].slice(1); var elem2 = playerCombo[1].charAt(0).toUpperCase() + playerCombo[1].slice(1); explanation = '\nUSER selected elements of opposite effects!\n' + elem1 + ' and ' + elem2 + ' work against each other!'; } battleResultText.setText('TIE!\nCRITICAL FAILURE! -' + baseDamage + ' Life' + explanation); battleResultText.tint = 0xff0000; battleResultText.alpha = 1; tween(battleResultText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 500 }); // Shake/blink animation for CRITICAL FAILURE if (playerLife <= 0 && aiLife > 0) { LK.showGameOver(); return; } } else if (aiIsFailure && !playerIsFailure) { // AI critical failure - damage to AI var damage = baseDamage; // Apply AI defensive tokens to reduce damage if (aiDefenseTokens > 0) { var damageReduction = Math.min(aiDefenseTokens * 5, damage); damage = Math.max(0, damage - damageReduction); aiDefenseTokens--; updateDefenseTokenDisplays(); // Create floating -1 animation for AI defense token removal var aiTokenRemovalAnimation = new Text2('-1', { size: 36, fill: 0xFF6666, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 2 }); aiTokenRemovalAnimation.anchor.set(0.5, 0.5); LK.gui.bottom.addChild(aiTokenRemovalAnimation); aiTokenRemovalAnimation.x = 600; aiTokenRemovalAnimation.y = -120; aiTokenRemovalAnimation.alpha = 0; tween(aiTokenRemovalAnimation, { alpha: 1, y: -90, scaleX: 1.5, scaleY: 1.5 }, { duration: 800, onFinish: function onFinish() { tween(aiTokenRemovalAnimation, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { aiTokenRemovalAnimation.destroy(); } }); } }); } aiLife -= damage; if (aiLife < 0) { aiLife = 0; } var explanation = '\nAI selected elements of opposite effects!'; if (aiCombo.length === 2) { var elem1 = aiCombo[0].charAt(0).toUpperCase() + aiCombo[0].slice(1); var elem2 = aiCombo[1].charAt(0).toUpperCase() + aiCombo[1].slice(1); explanation = '\nAI selected elements of opposite effects!\n' + elem1 + ' and ' + elem2 + ' work against each other!'; } battleResultText.setText('TIE!\nAI CRITICAL FAILURE! -' + baseDamage + ' AI Life' + explanation); battleResultText.tint = 0x00ff00; battleResultText.alpha = 1; tween(battleResultText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 500 }); // Shake/blink animation for CRITICAL FAILURE if (aiLife <= 0) { LK.showYouWin(); return; } } else if (playerIsFailure && aiIsFailure) { // Both critical failures var playerDamage = baseDamage; var aiDamage = baseDamage; // Apply player defensive tokens to reduce damage if (playerDefenseTokens > 0) { var playerDamageReduction = Math.min(playerDefenseTokens * 5, playerDamage); playerDamage = Math.max(0, playerDamage - playerDamageReduction); playerDefenseTokens--; // Create floating -1 animation for player defense token removal var playerTokenRemovalAnimation = new Text2('-1', { size: 36, fill: 0x08d0f8, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 2 }); playerTokenRemovalAnimation.anchor.set(0.5, 0.5); LK.gui.bottom.addChild(playerTokenRemovalAnimation); playerTokenRemovalAnimation.x = -600; playerTokenRemovalAnimation.y = -120; playerTokenRemovalAnimation.alpha = 0; tween(playerTokenRemovalAnimation, { alpha: 1, y: -90, scaleX: 1.5, scaleY: 1.5 }, { duration: 800, onFinish: function onFinish() { tween(playerTokenRemovalAnimation, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { playerTokenRemovalAnimation.destroy(); } }); } }); } // Apply AI defensive tokens to reduce damage if (aiDefenseTokens > 0) { var aiDamageReduction = Math.min(aiDefenseTokens * 5, aiDamage); aiDamage = Math.max(0, aiDamage - aiDamageReduction); aiDefenseTokens--; // Create floating -1 animation for AI defense token removal var aiTokenRemovalAnimation = new Text2('-1', { size: 36, fill: 0xFF6666, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 2 }); aiTokenRemovalAnimation.anchor.set(0.5, 0.5); LK.gui.bottom.addChild(aiTokenRemovalAnimation); aiTokenRemovalAnimation.x = 600; aiTokenRemovalAnimation.y = -120; aiTokenRemovalAnimation.alpha = 0; tween(aiTokenRemovalAnimation, { alpha: 1, y: -90, scaleX: 1.5, scaleY: 1.5 }, { duration: 800, onFinish: function onFinish() { tween(aiTokenRemovalAnimation, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { aiTokenRemovalAnimation.destroy(); } }); } }); } updateDefenseTokenDisplays(); playerLife -= playerDamage; aiLife -= aiDamage; if (playerLife < 0) { playerLife = 0; } if (aiLife < 0) { aiLife = 0; } var explanation = '\nBoth USER and AI selected elements of opposite effects!'; battleResultText.setText('TIE!\nBOTH CRITICAL FAILURES!' + explanation); battleResultText.tint = 0xffff00; battleResultText.alpha = 1; tween(battleResultText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 500 }); // Shake/blink animation for BOTH CRITICAL FAILURES if (playerLife <= 0 && aiLife <= 0) { LK.showGameOver(); // Player dies first in simultaneous death return; } else if (playerLife <= 0 && aiLife > 0) { LK.showGameOver(); return; } else if (aiLife <= 0 && playerLife > 0) { LK.showYouWin(); return; } } else { var explanation = ''; if (playerElement && aiElement) { explanation = '\nNeither element has advantage over the other!'; } else if (playerCombo.length === 2 && aiCombo.length === 2) { explanation = '\nBoth combos are equally matched!'; } else { explanation = '\nForces are perfectly balanced!'; } // Check for weather effects on ties var playerHasBonus = false; var aiHasBonus = false; for (var i = 0; i < (playerElement ? [playerElement] : playerCombo).length; i++) { if (hasWeatherBonus((playerElement ? [playerElement] : playerCombo)[i])) { playerHasBonus = true; break; } } for (var i = 0; i < (aiElement ? [aiElement] : aiCombo).length; i++) { if (hasWeatherBonus((aiElement ? [aiElement] : aiCombo)[i])) { aiHasBonus = true; break; } } if (playerHasBonus && aiHasBonus) { explanation += '\nBoth sides have weather advantage!'; } battleResultText.setText('TIE!' + explanation); battleResultText.tint = 0xffff00; battleResultText.alpha = 1; tween(battleResultText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 500 }); // Shake/blink animation for TIE } } // Update health bars var playerHealthPercent = playerLife / 100; var aiHealthPercent = aiLife / 100; playerHealthBar.scaleX = 14 * playerHealthPercent; aiHealthBar.scaleX = 14 * aiHealthPercent; playerHealthText.setText('PLAYER: ' + playerLife + '/100'); aiHealthText.setText('AI: ' + aiLife + '/100'); // Update weather system if (weatherRoundsLeft > 0) { weatherRoundsLeft--; updateWeatherDisplay(); if (weatherRoundsLeft === 0) { currentWeather = null; noWeatherRounds = 1; // Start counting no weather rounds updateWeatherDisplay(); // Update display when weather ends } } else { // Increment no weather counter noWeatherRounds++; // Force weather after 2 turns of no weather, otherwise 30% chance if (noWeatherRounds >= 2) { startNewWeather(); } else if (Math.random() < 0.3) { startNewWeather(); } } // Check if all elements are used up var allElementsUsed = true; for (var i = 0; i < elementButtons.length; i++) { if (!elementButtons[i].isDisabled) { window.alert("ALL"); allElementsUsed = false; break; } } // Check if all elements are used up var allElementsUsed = true; for (var i = 0; i < elementButtons.length; i++) { if (!elementButtons[i].isDisabled) { allElementsUsed = false; break; } } // Game over if player health is zero and AI health is greater than zero if (playerLife <= 0 && aiLife > 0) { LK.showGameOver(); return; } // Return to selection after delay LK.setTimeout(function () { resetForNextRound(); }, 5000); } // Create victory arrow function function createVictoryArrow(losingElement, winningElement) { // Find the actual button elements var losingButton = null; var winningButton = null; for (var i = 0; i < elementButtons.length; i++) { if (elementButtons[i].elementType === losingElement) { losingButton = elementButtons[i]; } if (elementButtons[i].elementType === winningElement) { winningButton = elementButtons[i]; } } if (!losingButton || !winningButton) { return; } // Get positions relative to wheel container center var startX = wheelContainer.x + losingButton.x; var startY = wheelContainer.y + losingButton.y; var endX = wheelContainer.x + winningButton.x; var endY = wheelContainer.y + winningButton.y; // Control point at the center of the wheel var controlX = wheelContainer.x; var controlY = wheelContainer.y; // Create container for the arrow var arrowContainer = new Container(); game.addChild(arrowContainer); // Number of segments for smooth bezier curve var segments = 50; var prevX = startX; var prevY = startY; // Create bezier curve using line segments for (var i = 1; i <= segments; i++) { var t = i / segments; // Quadratic bezier formula: (1-t)²P₀ + 2(1-t)tP₁ + t²P₂ var currentX = (1 - t) * (1 - t) * startX + 2 * (1 - t) * t * controlX + t * t * endX; var currentY = (1 - t) * (1 - t) * startY + 2 * (1 - t) * t * controlY + t * t * endY; // Calculate segment direction and length var dx = currentX - prevX; var dy = currentY - prevY; var distance = Math.sqrt(dx * dx + dy * dy); var angle = Math.atan2(dy, dx); // Create line segment var lineSegment = LK.getAsset('line', { anchorX: 0, anchorY: 0.5 }); arrowContainer.addChild(lineSegment); // Position and scale the segment lineSegment.x = prevX; lineSegment.y = prevY; lineSegment.rotation = angle; lineSegment.scaleX = distance / 20; // Scale to match distance (line asset is 20px wide) lineSegment.scaleY = 0.4; // Make line thinner // Color gradient: red at start, green at end var colorRatio = t; var red = Math.floor(255 * (1 - colorRatio) + 203 * colorRatio); var green = Math.floor(151 * (1 - colorRatio) + 244 * colorRatio); var blue = Math.floor(151 * (1 - colorRatio) + 131 * colorRatio); lineSegment.tint = red << 16 | green << 8 | blue; // Update previous position prevX = currentX; prevY = currentY; } // Store arrow for cleanup victoryArrows.push(arrowContainer); // Animate arrow appearance arrowContainer.alpha = 0; tween(arrowContainer, { alpha: 0.8 }, { duration: 800 }); } // Variables to track turn order var playerGoesFirst = true; // Will be determined by coin flip // Function to perform coin flip animation function performCoinFlip(onComplete) { // Create coin flip container var coinContainer = new Container(); coinContainer.x = 1024; coinContainer.y = 1366; game.addChild(coinContainer); // Disable clicks when coin flip background appears clicksDisabled = true; // Create white background first var flipBackground = LK.getAsset('userTurnBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 2 }); flipBackground.tint = 0xFFFFFF; flipBackground.alpha = 1; flipBackground.x = 0; flipBackground.y = 0; coinContainer.addChild(flipBackground); // Add coin flip text - split into two parts var flipText = new Text2('Flipping coin...', { size: 48, fill: 0xffffff, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3, align: 'center' }); flipText.anchor.set(0.5, 0.5); flipText.x = -325; flipText.y = 0; coinContainer.addChild(flipText); // Add second part of message on the right var flipTextRight = new Text2('...to determine turn order', { size: 48, fill: 0xffffff, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3, align: 'center' }); flipTextRight.anchor.set(0.5, 0.5); flipTextRight.x = 450; flipTextRight.y = 0; coinContainer.addChild(flipTextRight); // Create the coin last so it appears on top var coin = coinContainer.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2 }); // Determine flip result var flipResult = Math.random() < 0.5; // true = heads (player first), false = tails (AI first) playerGoesFirst = flipResult; // Animate coin spinning var spins = 8 + Math.random() * 4; // 8-12 spins tween(coin, { rotation: spins * Math.PI * 2, scaleY: 0.2 }, { duration: 2000, easing: tween.easeOut, onFinish: function onFinish() { // Remove spinning coin coin.destroy(); // Show result coin var resultAsset = flipResult ? 'coinHeads' : 'coinTails'; var resultCoin = coinContainer.attachAsset(resultAsset, { anchorX: 0.5, anchorY: 0.5, scaleX: 2, scaleY: 2, alpha: 0 }); // Update background and text flipBackground.tint = 0xFFFFFF; flipBackground.alpha = 1; flipBackground.x = 0; flipBackground.y = 0; // Update text - split into two parts var headsOrTails = flipResult ? 'HEADS' : 'TAILS'; var turnMessage = flipResult ? 'You go first!' : 'AI goes first!'; // Update original text to show heads/tails 300px left of coin flipText.setText(headsOrTails); flipText.fill = 0xffffff; flipText.stroke = 0x000000; flipText.strokeThickness = 3; flipText.tint = flipResult ? 0x00FF00 : 0xFF6666; flipText.x = -250; flipText.y = 0; flipTextRight.setText(turnMessage); flipTextRight.fill = 0xffffff; flipTextRight.stroke = 0x000000; flipTextRight.strokeThickness = 3; flipTextRight.tint = flipResult ? 0x00FF00 : 0xFF6666; flipTextRight.x = 300; flipTextRight.y = 0; // Animate result appearance tween(resultCoin, { alpha: 1, scaleX: 2.5, scaleY: 2.5 }, { duration: 800, onFinish: function onFinish() { // Hold result for a moment then fade out LK.setTimeout(function () { tween(coinContainer, { alpha: 0 }, { duration: 1000, onFinish: function onFinish() { coinContainer.destroy(); if (onComplete) { onComplete(); } } }); }, 1000); } }); } }); } // Reset for next round function resetForNextRound() { // Disable clicks at start of round reset clicksDisabled = true; // Perform coin flip first performCoinFlip(function () { gameState = 'selection'; // Set hero alphas based on coin flip result if (playerGoesFirst) { // Player goes first // Show "Choose 1 or 2 elements" message for 3 seconds // Create white background var userTurnBackground = LK.getAsset('userTurnBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 2 }); userTurnBackground.tint = 0xFFFFFF; userTurnBackground.alpha = 1; userTurnBackground.x = 1024; userTurnBackground.y = 1366; game.addChild(userTurnBackground); var userTurnMessage = new Text2('Your turn!\nChoose 1 or 2 elements', { size: 96, fill: 0xffffff, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3, align: 'center' }); userTurnMessage.anchor.set(0.5, 0.5); userTurnMessage.x = 1024; userTurnMessage.y = 1366; userTurnMessage.alpha = 0; game.addChild(userTurnMessage); // Animate message in tween(userTurnBackground, { alpha: 1 }, { duration: 500 }); tween(userTurnMessage, { alpha: 1 }, { duration: 500, onFinish: function onFinish() { // After 3 seconds, fade out and show normal instruction LK.setTimeout(function () { tween(userTurnBackground, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { userTurnBackground.destroy(); } }); tween(userTurnMessage, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { userTurnMessage.destroy(); instructionText.setText('Choose your element'); // Enable clicks after YOUR TURN message disappears clicksDisabled = false; } }); }, 3000); } }); tween(playerHeroImage, { alpha: 1 }, { duration: 200 }); tween(aiHeroImage, { alpha: 0.5 }, { duration: 200 }); } else { // AI goes first // Keep clicks disabled when AI goes first clicksDisabled = true; tween(playerHeroImage, { alpha: 0.5 }, { duration: 200 }); tween(aiHeroImage, { alpha: 1 }, { duration: 200 }); instructionText.setText('AI is choosing...'); // Start AI turn immediately LK.setTimeout(function () { startWheelSpin(); }, 1000); } }); // Stop any ongoing wheel spinning animation if (wheelContainer) { tween.stop(wheelContainer, { rotation: true }); } // Reset spinning state isWheelSpinning = false; // Create victory arrow if we have a battle result and both elements are valid var battleResult = checkBattleResult(); if (playerCombo.length < 1) { playerCombo = [playerElement]; } if (aiCombo.length < 1) { aiCombo = [aiElement]; } console.log(playerCombo); console.log(aiCombo); console.log(battleResult); for (var i = 0; i < playerCombo.length; i++) { for (var j = 0; j < aiCombo.length; j++) { if (playerCombo[i] && aiCombo[j] && battleResult !== 'tie' && playerCombo[i] !== aiCombo[j]) { if (battleResult === 'player') { createVictoryArrow(aiCombo[j], playerCombo[i]); } else if (battleResult === 'ai') { createVictoryArrow(playerCombo[i], aiCombo[j]); } } } } // Show victory arrows again for (var i = 0; i < victoryArrows.length; i++) { tween(victoryArrows[i], { alpha: 1 }, { duration: 300 }); } // Reset combo state for next round playerCombo = []; aiCombo = []; aiComboSpins = 0; updateComboDisplay(); updateAIComboDisplay(); // Don't reset aiElement to null immediately - we need it for victory arrows // aiElement will be reset when a new round actually starts // Reset wheel rotation and element rotations if (wheelContainer) { wheelContainer.rotation = 0; } // Reset all element button rotations to keep them upright for (var i = 0; i < elementButtons.length; i++) { elementButtons[i].rotation = 0; } // Clean up battle elements if (playerBattleElement) { playerBattleElement.destroy(); playerBattleElement = null; } if (aiBattleElement) { aiBattleElement.destroy(); aiBattleElement = null; } if (vsText) { vsText.destroy(); vsText = null; } if (battleResultText) { tween.stop(battleResultText); // Stop any ongoing tween animation battleResultText.destroy(); battleResultText = null; } // Reset instruction text instructionText.setText('Choose first element for combo'); instructionText.tint = 0xffffff; // Restore original font size instructionText.size = 32; // Show triangle again after battle screen finishes if (window.triangleMarker) { window.triangleMarker.alpha = 1; } // Clean up confirmation buttons if they exist if (confirmationContainer) { confirmationContainer.destroy(); confirmationContainer = null; } // Reset confirmation button references useAsComboButton = null; useAsSingleButton = null; // Show combo displays again after battle if (comboDisplay) { tween(comboDisplay, { alpha: 1 }, { duration: 300 }); } // Show AI combo display again after battle if (aiComboDisplay) { tween(aiComboDisplay, { alpha: 1 }, { duration: 300 }); } // Count remaining non-used elements before updating button states var remainingElements = 0; for (var i = 0; i < elementButtons.length; i++) { var elementType = elementButtons[i].elementType; var isUsed = usedPlayerElements.indexOf(elementType) !== -1 || usedAIElements.indexOf(elementType) !== -1; if (!isUsed) { remainingElements++; } } // Check if we need to refresh the wheel var shouldRefresh = remainingElements < 5; if (shouldRefresh) { // Clear used element arrays to re-enable all buttons usedPlayerElements = []; usedAIElements = []; // Hide 2 random elements again after refresh hideRandomElements(); // Disable clicks when wheel refresh message shows clicksDisabled = true; // Create white background for refresh message var refreshBackground = LK.getAsset('userTurnBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 2 }); refreshBackground.tint = 0xFFFFFF; refreshBackground.alpha = 1; refreshBackground.x = 1024; refreshBackground.y = 1366; game.addChild(refreshBackground); // Show refresh message var refreshMessage = new Text2('Wheel refreshed!', { size: 48, fill: 0xffffff, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3, align: 'center' }); refreshMessage.anchor.set(0.5, 0.5); refreshMessage.x = 1024; refreshMessage.y = 1366; refreshMessage.alpha = 0; game.addChild(refreshMessage); // Animate message appearance tween(refreshBackground, { alpha: 1 }, { duration: 500 }); tween(refreshMessage, { alpha: 1 }, { duration: 500, onFinish: function onFinish() { LK.setTimeout(function () { tween(refreshBackground, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { refreshBackground.destroy(); } }); tween(refreshMessage, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { refreshMessage.destroy(); } }); }, 1000); } }); } // Show selection elements and re-enable only unused ones for (var i = 0; i < elementButtons.length; i++) { elementButtons[i].setSelected(false); var elementType = elementButtons[i].elementType; var isUsed = usedPlayerElements.indexOf(elementType) !== -1 || usedAIElements.indexOf(elementType) !== -1; elementButtons[i].setDisabled(isUsed); tween(elementButtons[i], { alpha: 1 }, { duration: 300 }); // Show element labels again after returning from battle var button = elementButtons[i]; var label = button.children[button.children.length - 1]; // Label is the last child if (label && label.setText) { tween(label, { alpha: 1 }, { duration: 300 }); } } // Apply weather boost effects to visible elements applyWeatherBoostEffects(); // Reset battle timer battleTimer = 0; } // Game move handler for hover effects game.move = function (x, y, obj) { // Disable hover effects when not in selection state, clicks are disabled, or during intro if (gameState !== 'selection' || clicksDisabled || showingIntro) { return; } // Triangle should never be visible during user turn - it's only for AI spinning if (window.triangleMarker && gameState === 'selection') { window.triangleMarker.alpha = 0; } var newHoveredButton = null; // Check which button is under the cursor for (var i = 0; i < elementButtons.length; i++) { var button = elementButtons[i]; // Convert game coordinates to wheel container space // Account for wheel container position (1024, 1400) var localX = x - wheelContainer.x; var localY = y - wheelContainer.y; // Calculate distance from cursor to button center var dx = localX - button.x; var dy = localY - button.y; var distance = Math.sqrt(dx * dx + dy * dy); // Use button's actual width/height to determine hit area (button is anchored at center) var hitRadius = 150; // Approximate radius for the button hit area if (distance < hitRadius) { newHoveredButton = button; break; } } // Handle hover state changes if (hoveredButton !== newHoveredButton) { // Handle leave event for previously hovered button if (hoveredButton && !hoveredButton.isSelected && !hoveredButton.isDisabled) { // Stop any existing scale tweens tween.stop(hoveredButton, { scaleX: true, scaleY: true }); // Scale back to normal tween(hoveredButton, { scaleX: 1.0, scaleY: 1.0 }, { duration: 150 }); } // Handle hover event for new button if (newHoveredButton && !newHoveredButton.isSelected && !newHoveredButton.isDisabled) { // Stop any existing scale tweens tween.stop(newHoveredButton, { scaleX: true, scaleY: true }); // Scale up on hover tween(newHoveredButton, { scaleX: 1.1, scaleY: 1.1 }, { duration: 150 }); } hoveredButton = newHoveredButton; } }; // Initialize intro screen first createIntroScreen(); // Add click handler for intro screen game.down = function (x, y, obj) { if (showingIntro) { hideIntroScreen(); return; } // Original game click handler code will be added here after intro if (gameState === 'selection' && dragNode) { dragNode = character; handleMove(x, y, obj); } }; // Initialize game elements (but don't show them yet during intro) function initializeGameElements() { createElementButtons(); // Hide all game elements initially during intro if (showingIntro) { for (var i = 0; i < elementButtons.length; i++) { elementButtons[i].alpha = 0; } if (wheelContainer) { wheelContainer.alpha = 0; } titleText.alpha = 0; instructionText.alpha = 0; comboDisplay.alpha = 0; aiComboDisplay.alpha = 0; } } // Call initialization initializeGameElements(); // Perform initial coin flip after intro (will be called when game actually starts) function startGameWithCoinFlip() { performCoinFlip(function () { gameState = 'selection'; if (playerGoesFirst) { // Player goes first // Show "Choose 1 or 2 elements" message for 3 seconds // Create white background var userTurnBackground = LK.getAsset('userTurnBackground', { anchorX: 0.5, anchorY: 0.5, scaleX: 3, scaleY: 2 }); userTurnBackground.tint = 0xFFFFFF; userTurnBackground.alpha = 1; userTurnBackground.x = 1024; userTurnBackground.y = 1366; game.addChild(userTurnBackground); var userTurnMessage = new Text2('Your turn!\nChoose 1 or 2 elements', { size: 96, fill: 0xffffff, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xffffff, strokeThickness: 3, align: 'center' }); userTurnMessage.anchor.set(0.5, 0.5); userTurnMessage.x = 1024; userTurnMessage.y = 1366; userTurnMessage.alpha = 0; game.addChild(userTurnMessage); // Animate message in tween(userTurnBackground, { alpha: 1 }, { duration: 500 }); tween(userTurnMessage, { alpha: 1 }, { duration: 500, onFinish: function onFinish() { // After 3 seconds, fade out and show normal instruction LK.setTimeout(function () { tween(userTurnBackground, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { userTurnBackground.destroy(); } }); tween(userTurnMessage, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { userTurnMessage.destroy(); instructionText.setText('Choose your element'); // Enable clicks after YOUR TURN message disappears clicksDisabled = false; } }); }, 3000); } }); tween(playerHeroImage, { alpha: 1 }, { duration: 200 }); tween(aiHeroImage, { alpha: 0.5 }, { duration: 200 }); } else { // AI goes first // Keep clicks disabled when AI goes first clicksDisabled = true; instructionText.setText('AI is choosing...'); tween(playerHeroImage, { alpha: 0.5 }, { duration: 200 }); tween(aiHeroImage, { alpha: 1 }, { duration: 200 }); // Start AI turn LK.setTimeout(function () { startWheelSpin(); }, 1000); } }); } // Start with a random weather 50% of the time if (Math.random() < 0.5) { startNewWeather(); } else { // Start a new weather if none is active startNewWeather(); } // Game update loop game.update = function () { // Keep elements upright during wheel spinning if (isWheelSpinning && wheelContainer) { for (var i = 0; i < elementButtons.length; i++) { // Counter-rotate each element button to keep it upright elementButtons[i].rotation = -wheelContainer.rotation; // Restore original label position var button = elementButtons[i]; var label = button.children[button.children.length - 1]; if (label && label.setText && button.originalLabelX !== undefined) { label.x = button.originalLabelX; label.y = button.originalLabelY; } } } // Battle timing is now handled by animation system if (gameState === 'battle' && playerBattleElement && aiBattleElement) { // Battle animations handle timing automatically } };
===================================================================
--- original.js
+++ change.js
@@ -160,13 +160,14 @@
label.alpha = 1.0;
}
};
self.down = function (x, y, obj) {
- if (showingIntro || clicksDisabled) {
- // Disable clicking during intro or when clicks are explicitly disabled
+ // Disable clicking during intro, when clicks are explicitly disabled, or when any message is showing
+ if (showingIntro || clicksDisabled || gameState !== 'selection') {
return;
}
- if (gameState === 'selection' && !self.isDisabled) {
+ // Only allow clicks when not disabled and during selection state
+ if (gameState === 'selection' && !self.isDisabled && !clicksDisabled) {
LK.getSound('select').play();
selectElement(self.elementType);
}
};
@@ -221,8 +222,26 @@
}
var showingIntro = true;
var introContainer = null;
var clicksDisabled = true; // Flag to disable clicks - starts disabled and only enabled after YOUR TURN message
+// Function to clear all hover effects when clicks are disabled
+function clearHoverEffects() {
+ if (hoveredButton && !hoveredButton.isSelected && !hoveredButton.isDisabled) {
+ // Stop any existing scale tweens
+ tween.stop(hoveredButton, {
+ scaleX: true,
+ scaleY: true
+ });
+ // Scale back to normal
+ tween(hoveredButton, {
+ scaleX: 1.0,
+ scaleY: 1.0
+ }, {
+ duration: 150
+ });
+ }
+ hoveredButton = null;
+}
// Create intro screen
function createIntroScreen() {
// Create purple background covering entire screen
var introBg = LK.getAsset('elementIcon', {
@@ -2508,8 +2527,9 @@
protectiveComboName = 'Reinforced Armor';
}
// Disable clicks when protective combo message shows
clicksDisabled = true;
+ clearHoverEffects(); // Clear any active hover effects
// Create white background for protective combo message
var protectiveBackground = LK.getAsset('userTurnBackground', {
anchorX: 0.5,
anchorY: 0.5,
@@ -5035,9 +5055,10 @@
battleTimer = 0;
}
// Game move handler for hover effects
game.move = function (x, y, obj) {
- if (gameState !== 'selection') {
+ // Disable hover effects when not in selection state, clicks are disabled, or during intro
+ if (gameState !== 'selection' || clicksDisabled || showingIntro) {
return;
}
// Triangle should never be visible during user turn - it's only for AI spinning
if (window.triangleMarker && gameState === 'selection') {
A hand with its fingers symbolizing gas
Make the background darker (blue)
Make the bacgkground color darker(blue)
a triangle pointing down. In-Game asset. 2d. High contrast. No shadows
This place after a draught
This place in very windy conditions
This place but it's raining
This place but there is an electric storm
a popup frame, retro pixel style, rectangular. In-Game asset. 2d. High contrast. No shadows
A version of this frame that symbolizes that this button has boosted / improved power
a minimalistic pixel button, no text on it, just the buton. White.. In-Game asset. 2d. High contrast. No shadows
A win effect that will be over a round button and that will triger for some seconds after a win happens, scaling in x and y as an effect.. In-Game asset. 2d. High contrast. No shadows
Lose
The frame should be ice related too
The frame should be earth related too
The frame should be water related too
The frame should be wind too
A hand with its fingers symbolizing ash
A hand with its fingers symbolizing sun
A hand with its fingers symbolizing moon
A hand with a piece of leather
A hand with a piece of plastic
A hand with a piece of opaque plastic
A hand with its fingers symbolizing salt
Can you make this picture slightly lighter
Can you make this image lighter please? But not a lot lighter, just a little
A frame that symbolizes that the element inside is penalized, unboosted, has a minus to its effect. It should be shown in the frame, not inside! With some VFX effect
One drop of rain. In-Game asset. 2d. High contrast. No shadows
Yellow / Orange colour
wind. In-Game asset. 2d. High contrast. No shadows
A pixel style magician from Magicka who has an arc around him of elements to be casted (fire, water, ice, rock, etc). In-Game asset. 2d. High contrast. No shadows
Tint the wizard to blue
Make the black border of the start twice thicker
A '?' (question mark) symbol inside
A white shield. In-Game asset. 2d. High contrast. No shadows
background
Music
air
Music
storm
Music
rain
Music
draught
Music
rock
Sound effect
paper
Sound effect
scissors
Sound effect
fire
Sound effect
metal
Sound effect
water
Sound effect
airy
Sound effect
lightning
Sound effect
tree
Sound effect
gun
Sound effect
sponge
Sound effect
smoke
Sound effect
gas
Sound effect
ice
Sound effect
sand
Sound effect
oil
Sound effect
plastic
Sound effect
leather
Sound effect
sun
Sound effect
glass
Sound effect
cloth
Sound effect
ash
Sound effect
fungus
Sound effect
moon
Sound effect
salt
Sound effect
select
Sound effect
spinning
Sound effect
win
Sound effect