Code edit (2 edits merged)
Please save this source code
User prompt
Redo createDroughtEffect - it should be the same as createRainEffect but only the other way around - from bottom to up and using 'rain' instead of 'radiation' ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
createDroughEffect: fix it, the effects should be all over the screen from the very beginning and continuosly created to be all over the screen ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
createRainEffect: please at start make random rain drops all over the screen dropping from different places ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Fix rain: it should be constantly falling without intervals where no rain is falling. Also, make it more transparent and render it on the background - for that, you need to render it just after the background of the screen and before anything else ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (3 edits merged)
Please save this source code
User prompt
Fix radiation effect that is showin only in the bottom and dying, it should show all over the screen ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (3 edits merged)
Please save this source code
User prompt
Ok for each season I want to add a full screen mostly transparent effect symbolizing the season weather. If it's rain or storm, then rain. If it's wind, wind. If it's draught, some kind of radiation. It should be in the background playing continuosly until the next season ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (2 edits merged)
Please save this source code
User prompt
COMBO-NATION should be rainbow-coloured ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
When a defensive token is removed shown an animation ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Rename "Metal wrapper" to "Foil"
User prompt
plastic + gun is a combo named "Plastic gun" and is a critical failure.
User prompt
After coming back from the battle screen to the wheel screen, and recalculate the number of disabled buttons because of being used, do also another check: check if the number of remaining non-used elements is smaller than 5. If so, Reenable the used buttons again and show a message "Wheel refreshed!"
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
When less than 5 elements are available, reenable them all again.
User prompt
Create 10 more protective combos
User prompt
For the protectiveCombos, can you show the name of the protectiveCombo (Armored Defense, Frozen Shield, etc) + "created" in the wheel screen when the combo is selected. And add an animation adding 1 token to the score of the player or the user. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Detect if the combo of the user or the AI is a protective item. If so, add a defensive "token" to the owner. Each defensive token will be consumed to reduce the damage in defeats by -5. Show those tokens in both screens. Also notify when a defensive item has been created and for whom. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Detect if the combo of the user or the AI is a protective item. If so, add a defensive "token" to the owner. Each defensive token will be consumed to reduce the damage in defeats by -5. Show those tokens in both screens. Also notify when a defensive item has been created and for whom.
User prompt
XXX combo elements dominate XXXX elements. Replace that and explain in detail which element dominated what.
/**** * 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: 0x000000, 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: 0x000000, 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: 0x000000, strokeThickness: 2 }); label.anchor.set(0.5, 0.5); self.addChild(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) { if (gameState === 'selection' && !self.isDisabled) { LK.getSound('select').play(); selectElement(self.elementType); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xffffff }); /**** * Game Code ****/ // Add background image - will be updated based on weather 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; 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.3 }); 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, scaleX: 0.3, scaleY: 4, alpha: 0.3 }); heatParticle.tint = 0xFFA500; // Orange color for heat 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 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 }; // 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 = { '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', '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': 'Moon Lightning', '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': 'Salt Lightning', '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', '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', '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', // New element combinations between new elements 'ash+cloth': 'Dirty Fabric', 'ash+fungus': 'Decay Spores', 'ash+glass': 'Ash Glass', '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', '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', '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', 'moon+sun': 'Eclipse', 'plastic+salt': 'Polymer Salt', 'plastic+sun': 'Solar Plastic', 'salt+sun': 'Evaporated Brine', 'rock+sun': 'Solar Stone' }, _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 }; // 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 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(); // Play music based on weather type, then return to background music if (currentWeather === 'windy') { LK.playMusic('air'); // Resume background music after weather music LK.setTimeout(function () { LK.playMusic('background'); }, 3000); // Resume after 3 seconds } else if (currentWeather === 'drought') { LK.playMusic('draught'); // Resume background music after weather music LK.setTimeout(function () { LK.playMusic('background'); }, 3000); // Resume after 3 seconds } else if (currentWeather === 'rainy') { LK.playMusic('rain'); // Resume background music after weather music LK.setTimeout(function () { LK.playMusic('background'); }, 3000); // Resume after 3 seconds } else if (currentWeather === 'storm') { LK.playMusic('storm'); // Resume background music after weather music LK.setTimeout(function () { LK.playMusic('background'); }, 3000); // Resume after 3 seconds } } // Function to update defensive token displays function updateDefenseTokenDisplays() { playerDefenseTokenDisplay.setText('Defense: ' + playerDefenseTokens); aiDefenseTokenDisplay.setText('AI Defense: ' + aiDefenseTokens); } // 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() { 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; 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: 0x000000, 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: 0x000000, 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: 0x000000, 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: 0x000000, strokeThickness: 3 }); instructionText.anchor.set(0.5, 0.5); game.addChild(instructionText); instructionText.x = 1024; instructionText.y = 425; // 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: 0x000000, 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: 0x000000, strokeThickness: 3 }); aiHealthText.anchor.set(0.5, 0.5); LK.gui.bottom.addChild(aiHealthText); aiHealthText.x = 0; aiHealthText.y = -35; // Player defensive tokens display playerDefenseTokenDisplay = new Text2('Defense: 0', { size: 24, fill: 0x08d0f8, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, strokeThickness: 2 }); playerDefenseTokenDisplay.anchor.set(0, 0.5); LK.gui.bottom.addChild(playerDefenseTokenDisplay); playerDefenseTokenDisplay.x = -700; playerDefenseTokenDisplay.y = -120; // AI defensive tokens display aiDefenseTokenDisplay = new Text2('AI Defense: 0', { size: 24, fill: 0xfda4d4, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, strokeThickness: 2 }); aiDefenseTokenDisplay.anchor.set(1, 0.5); LK.gui.bottom.addChild(aiDefenseTokenDisplay); aiDefenseTokenDisplay.x = 700; aiDefenseTokenDisplay.y = -120; var weatherText = new Text2('', { size: 28, fill: 0x87CEEB, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, 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); } } // 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: 0x000000, 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(); // 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; } // Check if element has already been used by player if (usedPlayerElements.indexOf(elementType) !== -1) { return; // Don't allow selection of already used elements } // 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'; } // Show protective combo creation message var protectiveMessage = new Text2(protectiveComboName + ' created', { size: 48, fill: 0x00FF00, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, strokeThickness: 3, align: 'center' }); protectiveMessage.anchor.set(0.5, 0.5); protectiveMessage.x = 1024; protectiveMessage.y = 800; protectiveMessage.alpha = 0; game.addChild(protectiveMessage); // Animate message appearance tween(protectiveMessage, { alpha: 1, y: 750 }, { duration: 1000, onFinish: function onFinish() { tween(protectiveMessage, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { protectiveMessage.destroy(); } }); } }); // 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: 0x000000, 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; } }); } // 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; // 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 // 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(); // 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(); // 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'; } aiDefenseTokens++; updateDefenseTokenDisplays(); // Show notification for AI defensive combo var aiDefenseNotification = new Text2('AI Created ' + protectiveComboName + '!\n+1 AI Defense Token', { size: 36, fill: 0xFF6666, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, strokeThickness: 3, align: 'center' }); aiDefenseNotification.anchor.set(0.5, 0.5); aiDefenseNotification.x = 1536; aiDefenseNotification.y = 800; aiDefenseNotification.alpha = 0; game.addChild(aiDefenseNotification); tween(aiDefenseNotification, { alpha: 1, y: 750 }, { duration: 1000, onFinish: function onFinish() { tween(aiDefenseNotification, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { aiDefenseNotification.destroy(); } }); } }); // Create floating +1 animation near AI defense display var aiTokenAnimation = new Text2('+1', { size: 36, fill: 0xFF6666, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, 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: 0x000000, 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(', ') + '!'; } 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(', ') + '!'; } 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(', ') + '!'; } 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: 0x000000, 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(', ') + '!'; } else { explanation = '\nAI combo achieves strategic dominance over player combo 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(', ') + '!'; } else { explanation = '\nAI combo achieves overwhelming tactical advantage 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(', ') + '!'; } 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: 0x000000, 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: 0x000000, 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: 0x000000, 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: 0x000000, 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 element indices var losingIndex = elements.indexOf(losingElement); var winningIndex = elements.indexOf(winningElement); if (losingIndex === -1 || winningIndex === -1) { return; } // Calculate initial static positions (same as in createElementButtons) var centerX = 1024; var centerY = 1400; var radius = 800; // Calculate losing element position var losingAngle = losingIndex / elements.length * Math.PI * 2 - Math.PI / 2; var losingX = centerX + Math.cos(losingAngle) * radius; var losingY = centerY + Math.sin(losingAngle) * radius; // Calculate winning element position var winningAngle = winningIndex / elements.length * Math.PI * 2 - Math.PI / 2; var winningX = centerX + Math.cos(winningAngle) * radius; var winningY = centerY + Math.sin(winningAngle) * radius; // Create bezier curve points using static positions // Adjust for arrows container offset var startX = losingX - centerX; var startY = losingY - centerY; var endX = winningX - centerX; var endY = winningY - centerY; var controlX = 0; // Center relative to arrows container var controlY = 0; // Create container for the arrow var arrowContainer = new Container(); // Find the arrows container (first child of game that's a Container) var arrowsContainer = null; for (var i = 0; i < game.children.length; i++) { if (game.children[i].constructor === Container && game.children[i] !== wheelContainer) { arrowsContainer = game.children[i]; break; } } if (arrowsContainer) { arrowsContainer.addChild(arrowContainer); } else { game.addChild(arrowContainer); } // Number of segments for the bezier curve var segments = 80; var halfSegments = segments / 2; // Create line segments for bezier curve for (var i = 0; i < segments; i++) { var t1 = i / segments; var t2 = (i + 1) / segments; // Calculate bezier points var x1 = (1 - t1) * (1 - t1) * startX + 2 * (1 - t1) * t1 * controlX + t1 * t1 * endX; var y1 = (1 - t1) * (1 - t1) * startY + 2 * (1 - t1) * t1 * controlY + t1 * t1 * endY; var x2 = (1 - t2) * (1 - t2) * startX + 2 * (1 - t2) * t2 * controlX + t2 * t2 * endX; var y2 = (1 - t2) * (1 - t2) * startY + 2 * (1 - t2) * t2 * controlY + t2 * t2 * endY; // Calculate angle and distance for this segment var dx = x2 - x1; var dy = y2 - y1; 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 rotate the segment lineSegment.x = x1; lineSegment.y = y1; lineSegment.rotation = angle; //lineSegment.width = distance; //lineSegment.height = 8; // Line thickness // Color the segment - first half red, second half green if (i < halfSegments) { lineSegment.tint = 0xff9797; // Red for first half } else { lineSegment.tint = 0xcbf483; // Green for second half } } // Store arrow for cleanup victoryArrows.push(arrowContainer); // Animate arrow appearance arrowContainer.alpha = 0; tween(arrowContainer, { alpha: 1 }, { duration: 500 }); } // Reset for next round function resetForNextRound() { gameState = 'selection'; // 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 = []; // Show refresh message var refreshMessage = new Text2('Wheel refreshed!', { size: 48, fill: 0x00FF00, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, strokeThickness: 3, align: 'center' }); refreshMessage.anchor.set(0.5, 0.5); refreshMessage.x = 1024; refreshMessage.y = 800; refreshMessage.alpha = 0; game.addChild(refreshMessage); // Animate message appearance tween(refreshMessage, { alpha: 1, y: 750 }, { duration: 1000, onFinish: function onFinish() { tween(refreshMessage, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { refreshMessage.destroy(); } }); } }); } // 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) { if (gameState !== 'selection') { 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 game createElementButtons(); // Play background music immediately when game starts LK.playMusic('background'); // 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
@@ -407,9 +407,9 @@
}
// Function to create drought effect (heat shimmer/radiation)
function createDroughtEffect() {
var _loop3 = function _loop3() {
- heatParticle = LK.getAsset('rain', {
+ heatParticle = LK.getAsset('radiation', {
anchorX: 0.5,
anchorY: 0,
scaleX: 0.3,
scaleY: 4,
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