Code edit (7 edits merged)
Please save this source code
User prompt
Please fix the bug: 'ReferenceError: battleResultText is not defined' in or related to this line: 'battleResultText.setText(resultText);' Line Number: 1617
User prompt
Show battle result can't reuse the BATTLE! label to show the result (tie, win, defeat, etc)! Create a new one, similar place but it's a new label
User prompt
user or AI can't never select twice the same element for a combo.
User prompt
Crate the very same widget as YOUR COMBO with information about User combo, but 400 pixels down and for AI COMBO
User prompt
The bezier lines are not shown anymore! Please fix them. Is probably the ai element that is null.
Code edit (3 edits merged)
Please save this source code
User prompt
Move the text of the second element in the battle screen 400 pixels up
User prompt
In battle, when you show the elements, there is only text on the first element, if it's a combo and there is a second element, it does not have text. Please fix, I need texts on both.
Code edit (1 edits merged)
Please save this source code
User prompt
Make sure the marker triangle is always HIDDEN in user turn! force it always to be alpha: 0 !
User prompt
Remove the AI ELEMENT: on the bottom right part of the screen
User prompt
Hide the triangle marker (alpha: 0?) when the user is selecting. Show it when the AI is spinning. Hide it in VS screen.
Code edit (3 edits merged)
Please save this source code
User prompt
The results of the battle should be written in the same label text 2d as BATTLE
User prompt
Don't use instruction text form TIE, Critical Failure, Win, Critical success, etc. Create a new one only for the VS / battle screen. after the icons. should be super big.
Code edit (9 edits merged)
Please save this source code
User prompt
Remvoe any combo information from Battle (VS) screen
User prompt
Remove the combo popup when the second element is shown. Reset after a new round starts.
Code edit (1 edits merged)
Please save this source code
User prompt
Make Your Combo and the icon1, icon2, etc. 3 times bigger. Make Use as Single button half the size.
Code edit (4 edits merged)
Please save this source code
User prompt
Ok the "Your combo" with the icon1, icon2, etc. Move that to the center of the wheel to replace the "Click on another element for a combo or click on Use as Single".
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BattleElement = Container.expand(function (elementType, isPlayer) { 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.toUpperCase(), { 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); 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.toUpperCase(), { size: 24, 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 backgroundImage = null; // 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); } // Initialize with default background updateBackgroundForWeather(); // Game elements and their relationships var elements = ['rock', 'paper', 'scissors', 'water', 'fire', 'air', 'lightning', 'tree', 'gun', 'sponge', 'smoke', 'gas', 'metal', 'ice', 'sand', 'oil']; // Element defeat relationships (each element defeats 8 others) var defeats = { // Rock: Heavy and solid, crushes fragile things, breaks weapons, absorbs energy 'rock': ['scissors', 'sponge', 'gas', 'metal', 'ice', 'gun', 'tree', 'sand'], // Paper: Wraps and covers, conducts electricity, absorbs liquids, blocks air flow 'paper': ['rock', 'metal', 'water', 'gas', 'sand', 'ice', 'air', 'smoke'], // Scissors: Sharp cutting tool, cuts through soft materials and gases 'scissors': ['paper', 'sponge', 'air', 'tree', 'gas', 'oil', 'ice', 'smoke'], // Water: Extinguishes fire, erodes materials, conducts electricity, flows through spaces 'water': ['fire', 'rock', 'scissors', 'gas', 'oil', 'sand', 'tree', 'metal'], // Fire: Burns combustible materials, melts solids, heats and expands gases 'fire': ['paper', 'scissors', 'sponge', 'ice', 'tree', 'sand', 'oil', 'smoke'], // Air: Feeds fire, disperses gases, creates pressure, moves objects 'air': ['fire', 'gas', 'smoke', 'metal', 'rock', 'oil', 'tree', 'gun'], // Lightning: Electrical discharge, conducts through metals and water, ionizes gases 'lightning': ['water', 'metal', 'gun', 'air', 'paper', 'sponge', 'smoke', 'sand'], // Tree: Absorbs water and nutrients, grows through materials, provides shelter 'tree': ['water', 'paper', 'sponge', 'smoke', 'gun', 'sand', 'ice', 'gas'], // Gun: Projectile weapon, breaks through barriers, fires through gases 'gun': ['scissors', 'rock', 'tree', 'metal', 'gas', 'ice', 'oil', 'paper'], // Sponge: Absorbs liquids and gases, flexible material, filters particles 'sponge': ['water', 'metal', 'air', 'paper', 'gas', 'rock', 'sand', 'smoke'], // Smoke: Obscures vision, suffocates, corrodes metals, covers surfaces 'smoke': ['ice', 'gun', 'rock', 'paper', 'air', 'metal', 'water', 'oil'], // Gas: Expands and fills spaces, displaces air, creates pressure, volatile 'gas': ['paper', 'ice', 'gun', 'smoke', 'air', 'scissors', 'sponge', 'sand'], // Metal: Strong and durable, conducts electricity, resists cutting, heavy 'metal': ['scissors', 'tree', 'ice', 'fire', 'gun', 'oil', 'rock', 'sand'], // Ice: Freezes liquids, slippery surface, preserves, expands when frozen 'ice': ['air', 'water', 'paper', 'sponge', 'fire', 'smoke', 'gun', 'lightning'], // Sand: Abrasive particles, absorbs liquids, extinguishes fire, buries objects 'sand': ['scissors', 'ice', 'gun', 'fire', 'air', 'paper', 'rock', 'lightning'], // Oil: Slippery liquid, flammable, coats surfaces, lubricates mechanisms 'oil': ['rock', 'scissors', 'gun', 'metal', 'water', 'gas', 'lightning', 'fire'] }; // Action keywords for how elements defeat others var actionKeywords = { 'rock': { 'scissors': 'crushes', 'sponge': 'crushes', 'gas': 'disperses', 'metal': 'breaks', 'ice': 'shatters', 'gun': 'blocks', 'tree': 'crushes', 'sand': 'compacts' }, 'paper': { 'rock': 'wraps', 'metal': 'covers', 'water': 'absorbs', 'gas': 'captures', 'sand': 'covers', 'ice': 'insulates', 'air': 'blocks', 'smoke': 'filters' }, 'scissors': { 'paper': 'cuts', 'sponge': 'cuts', 'air': 'cuts', 'tree': 'cuts', 'gas': 'cuts', 'oil': 'cuts', 'ice': 'cuts', 'smoke': 'cuts' }, 'water': { 'fire': 'extinguishes', 'rock': 'erodes', 'scissors': 'rusts', 'gas': 'dissolves', 'oil': 'displaces', 'sand': 'washes', 'tree': 'nourishes', 'metal': 'rusts' }, 'fire': { 'paper': 'burns', 'scissors': 'melts', 'sponge': 'burns', 'ice': 'melts', 'tree': 'burns', 'sand': 'heats', 'oil': 'ignites', 'smoke': 'burns' }, 'air': { 'fire': 'feeds', 'gas': 'disperses', 'smoke': 'clears', 'metal': 'oxidizes', 'rock': 'erodes', 'oil': 'evaporates', 'tree': 'sways', 'gun': 'deflects' }, 'lightning': { 'water': 'electrifies', 'metal': 'conducts', 'gun': 'shorts', 'air': 'ionizes', 'paper': 'ignites', 'sponge': 'shocks', 'smoke': 'disperses', 'sand': 'vitrifies' }, 'tree': { 'water': 'absorbs', 'paper': 'grows over', 'sponge': 'outgrows', 'smoke': 'filters', 'gun': 'blocks', 'sand': 'roots in', 'ice': 'breaks', 'gas': 'absorbs' }, 'gun': { 'scissors': 'shoots', 'rock': 'shatters', 'tree': 'shoots through', 'metal': 'pierces', 'gas': 'ignites', 'ice': 'shatters', 'oil': 'ignites', 'paper': 'pierces' }, 'sponge': { 'water': 'absorbs', 'metal': 'corrodes', 'air': 'filters', 'paper': 'soaks', 'gas': 'absorbs', 'rock': 'erodes', 'sand': 'absorbs', 'smoke': 'filters' }, 'smoke': { 'ice': 'melts', 'gun': 'jams', 'rock': 'corrodes', 'paper': 'stains', 'air': 'pollutes', 'metal': 'corrodes', 'water': 'pollutes', 'oil': 'mixes with' }, 'gas': { 'paper': 'dissolves', 'ice': 'sublimates', 'gun': 'corrodes', 'smoke': 'displaces', 'air': 'displaces', 'scissors': 'corrodes', 'sponge': 'fills', 'sand': 'displaces' }, 'metal': { 'scissors': 'dulls', 'tree': 'cuts', 'ice': 'breaks', 'fire': 'conducts', 'gun': 'reinforces', 'oil': 'repels', 'rock': 'crushes', 'sand': 'compacts' }, 'ice': { 'air': 'freezes', 'water': 'freezes', 'paper': 'freezes', 'sponge': 'freezes', 'fire': 'extinguishes', 'smoke': 'condenses', 'gun': 'jams', 'lightning': 'grounds' }, 'sand': { 'scissors': 'dulls', 'ice': 'melts', 'gun': 'jams', 'fire': 'smothers', 'air': 'fills', 'paper': 'abrades', 'rock': 'erodes', 'lightning': 'grounds' }, 'oil': { 'rock': 'lubricates', 'scissors': 'lubricates', 'gun': 'lubricates', 'metal': 'lubricates', 'water': 'floats on', 'gas': 'dissolves', 'lightning': 'insulates', 'fire': 'feeds' } }; // 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 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 // Weather system variables var currentWeather = null; var weatherRoundsLeft = 0; var weatherTypes = ['rainy', 'drought', 'windy', 'storm']; var weatherEffects = { 'rainy': ['water', 'ice'], // Water-based elements get bonus 'drought': ['fire', 'sand'], // Fire-based elements get bonus 'windy': ['air', 'gas'], // Air-based elements get bonus 'storm': ['lightning', 'metal'] // Electric/conductive elements get bonus }; // Combo system definitions var comboEffects = { // Special 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: 'Inferno', 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 }, 'gas+fire': { name: 'Explosive Blast', defeatsExtra: ['rock', 'metal', 'ice'], multiplier: 2.0 }, 'lightning+metal': { name: 'Electric Conductor', defeatsExtra: ['water', 'ice', 'gun'], multiplier: 1.7 } }; // 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 updateWeatherDisplay(); } // 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 effectElements = weatherEffects[currentWeather].join(', ').toUpperCase(); text = weatherName + ' Season (' + weatherRoundsLeft + ' rounds)\n' + effectElements + ' elements boosted'; } 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(); // Apply visual boost effects to weather-enhanced elements applyWeatherBoostEffects(); } // Function to get weather color function getWeatherColor(weather) { switch (weather) { case 'rainy': return 0x4169E1; // Royal blue case 'drought': return 0xFF4500; // Orange red case 'windy': return 0x87CEEB; // Sky blue case 'storm': return 0x9370DB; // 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 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)) { // Replace elementButton with boostedElementButton for visual enhancement var background = button.children[0]; // The button background is the first child console.log(background); if (background) { // Remove old background button.removeChild(background); // Add boosted background var boostedBackground = button.attachAsset('boostedElementButton', { anchorX: 0.5, anchorY: 0.5 }); // Move to back so icon and label stay on top button.setChildIndex(boostedBackground, 0); } // Boosted elements use boostedElementButton asset without scaling animation } else { // Replace boostedElementButton back with regular elementButton for non-boosted elements var background = button.children[0]; // The button background is the first child if (background && background.texture) { // Check if it's currently boosted and needs to be replaced var isCurrentlyBoosted = background.texture.baseTexture && background.texture.baseTexture.imageUrl && background.texture.baseTexture.imageUrl.includes('boostedElementButton'); if (isCurrentlyBoosted) { // Remove boosted background 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 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('Your Combo:'); var icon1 = LK.getAsset(playerCombo[0], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); icon1.x = 60; icon1.y = 40; comboDisplay.addChild(icon1); var plusText = new Text2(' + ?', { size: 20, fill: 0xFFFFFF, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, strokeThickness: 2 }); plusText.x = 140; plusText.y = 40; plusText.anchor.set(0, 0.5); comboDisplay.addChild(plusText); } else if (playerCombo.length === 2) { var effect = getComboEffect(playerCombo); if (effect) { comboDisplay.setText('Your Combo:\n' + effect.name); comboDisplay.tint = 0x00FF00; // Green for special combo } else { comboDisplay.setText('Your Combo'); comboDisplay.tint = 0xFFD700; // Gold for normal combo } var icon1 = LK.getAsset(playerCombo[0], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); icon1.x = 40; icon1.y = effect ? 40 : 40; comboDisplay.addChild(icon1); var plusText = new Text2(' + ', { size: 20, fill: 0xFFFFFF, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, strokeThickness: 2 }); plusText.x = 90; plusText.y = effect ? 40 : 40; plusText.anchor.set(0.5, 0.5); comboDisplay.addChild(plusText); var icon2 = LK.getAsset(playerCombo[1], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); icon2.x = 140; icon2.y = effect ? 40 : 40; comboDisplay.addChild(icon2); } } function updateAIComboDisplay() { // Clear existing AI combo elements for (var i = aiComboDisplay.children.length - 1; i >= 0; i--) { aiComboDisplay.removeChild(aiComboDisplay.children[i]); } aiComboDisplay.setText(''); if (aiCombo.length === 0 && !aiElement) { // No combo or element yet } else if (aiCombo.length === 1) { aiComboDisplay.setText('AI Combo:'); var icon1 = LK.getAsset(aiCombo[0], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); icon1.x = -80; icon1.y = -50; aiComboDisplay.addChild(icon1); var plusText = new Text2(' + ?', { size: 20, fill: 0xFFFFFF, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, strokeThickness: 2 }); plusText.x = -120; plusText.y = -50; plusText.anchor.set(1, 0.5); aiComboDisplay.addChild(plusText); } else if (aiCombo.length === 2) { var effect = getComboEffect(aiCombo); if (effect) { aiComboDisplay.setText('AI Combo:\n' + effect.name); aiComboDisplay.tint = 0xFF0000; // Red for AI special combo } else { aiComboDisplay.setText('AI Combo:'); aiComboDisplay.tint = 0xFF6B35; // Orange for AI normal combo } var icon1 = LK.getAsset(aiCombo[0], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); icon1.x = -120; icon1.y = effect ? -80 : -50; aiComboDisplay.addChild(icon1); var plusText = new Text2(' + ', { size: 20, fill: 0xFFFFFF, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, strokeThickness: 2 }); plusText.x = -90; plusText.y = effect ? -80 : -50; plusText.anchor.set(0.5, 0.5); aiComboDisplay.addChild(plusText); var icon2 = LK.getAsset(aiCombo[1], { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); icon2.x = -60; icon2.y = effect ? -80 : -50; aiComboDisplay.addChild(icon2); } else if (aiElement) { aiComboDisplay.setText('AI Element:'); var icon = LK.getAsset(aiElement, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.3, scaleY: 0.3 }); icon.x = -80; icon.y = -50; aiComboDisplay.addChild(icon); } } // Text positioning parameter - distance to move text toward wheel center var textOffsetTowardCenter = 160; // Adjustable parameter for text positioning // UI elements var titleText = new Text2('ULTIMATE ELEMENTS SHOWDOWN', { size: 48, 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; var instructionText = new Text2('Choose your element', { size: 32, fill: 0xCCCCCC, 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 = 400; var playerLifeText = new Text2('Player Life: 100', { size: 36, fill: 0x00FF00, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, strokeThickness: 3 }); playerLifeText.anchor.set(1, 0); LK.gui.topRight.addChild(playerLifeText); playerLifeText.x = -20; playerLifeText.y = 20; var aiLifeText = new Text2('AI Life: 100', { size: 32, fill: 0xFF0000, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, strokeThickness: 3 }); aiLifeText.anchor.set(1, 0); LK.gui.topRight.addChild(aiLifeText); aiLifeText.x = -20; aiLifeText.y = 70; 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 bottom left comboDisplay = new Text2('', { size: 24, fill: 0xFFD700, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, strokeThickness: 2 }); comboDisplay.anchor.set(0, 1); LK.gui.bottomLeft.addChild(comboDisplay); comboDisplay.x = 20; comboDisplay.y = -120; // AI combo display in bottom right var aiComboDisplay = new Text2('', { size: 24, fill: 0xFF6B35, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, strokeThickness: 2 }); aiComboDisplay.anchor.set(1, 1); LK.gui.bottomRight.addChild(aiComboDisplay); aiComboDisplay.x = -20; aiComboDisplay.y = -20; // Create element selection grid function createElementButtons() { 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 = 1400; // 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: 0.8, scaleY: 0.8 }); useAsSingleButton.x = 200; useAsSingleButton.y = 50; useAsSingleButton.tint = 0xFFD700; // Gold tint var singleText = new Text2('USE AS\nSINGLE', { size: 20, fill: 0xffffff, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, strokeThickness: 2 }); singleText.anchor.set(0.5, 0.5); singleText.x = 200; singleText.y = 50; confirmationContainer.addChild(singleText); // Add title text to the popup var titleText = new Text2('Click on another element for a combo\nor click on Use as Single', { size: 24, fill: 0x000000, font: "Trebuchet MS, Arial, sans-serif", stroke: 0xFFFFFF, strokeThickness: 2 }); titleText.anchor.set(0.5, 0.5); titleText.x = 0; titleText.y = -80; confirmationContainer.addChild(titleText); // Add click handlers useAsComboButton.down = function (x, y, obj) { LK.getSound('select').play(); handleComboChoice(selectedElement, true); }; 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 } // Handle combo selection if (playerCombo.length === 0) { // First element selection playerCombo.push(elementType); updateComboDisplay(); instructionText.setText('Use ' + elementType.toUpperCase() + ' as single element or add second for combo?'); // 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(); // 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; // 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...'); } // 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 = 0; game.addChild(triangle); // Animate triangle in tween(triangle, { alpha: 1 }, { duration: 300 }); // Store triangle reference for cleanup instructionText.triangle = 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) var availableElements = []; for (var i = 0; i < elementButtons.length; i++) { if (!elementButtons[i].isDisabled) { availableElements.push(elementButtons[i].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 if (aiComboSpins === 1) { // AI is building a combo - this is first element 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 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 using single element isWheelSpinning = false; aiElement = currentTopElement; 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 (not used by AI yet) var availableElements = []; for (var i = 0; i < elements.length; i++) { if (usedAIElements.indexOf(elements[i]) === -1) { availableElements.push(elements[i]); } } // If no available elements, use all elements (shouldn't happen in normal gameplay) if (availableElements.length === 0) { availableElements = elements.slice(); } // 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; // Basic AI that becomes more strategic with difficulty if (difficultyLevel === 1) { // Random selection from safe elements return elementsToChooseFrom[Math.floor(Math.random() * elementsToChooseFrom.length)]; } else if (difficultyLevel === 2) { // 30% chance to counter player's last choice if (Math.random() < 0.3 && playerElement) { var counters = []; for (var i = 0; i < elementsToChooseFrom.length; i++) { if (defeats[elementsToChooseFrom[i]] && defeats[elementsToChooseFrom[i]].indexOf(playerElement) !== -1) { counters.push(elementsToChooseFrom[i]); } } if (counters.length > 0) { return counters[Math.floor(Math.random() * counters.length)]; } } return elementsToChooseFrom[Math.floor(Math.random() * elementsToChooseFrom.length)]; } else { // 50% chance to counter player's choice if (Math.random() < 0.5 && playerElement) { var counters = []; for (var i = 0; i < elementsToChooseFrom.length; i++) { if (defeats[elementsToChooseFrom[i]] && defeats[elementsToChooseFrom[i]].indexOf(playerElement) !== -1) { counters.push(elementsToChooseFrom[i]); } } if (counters.length > 0) { return counters[Math.floor(Math.random() * counters.length)]; } } return elementsToChooseFrom[Math.floor(Math.random() * elementsToChooseFrom.length)]; } } // 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(); } // 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 triangle if it exists if (instructionText.triangle) { tween(instructionText.triangle, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { instructionText.triangle.destroy(); instructionText.triangle = null; } }); } // 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 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 = 80; playerSecondIcon.y = -100; playerBattleElement.addChild(playerSecondIcon); } aiBattleElement = game.addChild(new BattleElement(aiDisplayElement, false)); aiBattleElement.x = 1648; aiBattleElement.y = 1100; aiBattleElement.alpha = 0; // Add second element icon 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); } // Animate battle elements in tween(playerBattleElement, { alpha: 1, x: 512 }, { duration: 500 }); tween(aiBattleElement, { alpha: 1, x: 1536 }, { duration: 500 }); // Add VS text vsText = new Text2('VS', { size: 72, fill: 0xFF6B35, font: "Trebuchet MS, Arial, sans-serif", stroke: 0x000000, strokeThickness: 5 }); vsText.anchor.set(0.5, 0.5); vsText.x = 1024; vsText.y = 1100; vsText.alpha = 0; game.addChild(vsText); tween(vsText, { alpha: 1, scaleX: 1.5, scaleY: 1.5 }, { duration: 300, onFinish: function onFinish() { LK.getSound('battle').play(); // Start battle timer battleTimer = 0; } }); } // 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; // 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; 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 (playerWins) { return 'player'; } else if (aiWins) { 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; var playerIsFailure = playerCombo.length === 2 && playerComboEffect === null; var aiIsCritical = aiCombo.length === 2 && aiComboEffect !== null; var aiIsFailure = aiCombo.length === 2 && aiComboEffect === null; var baseDamage = 10; var playerDamage = baseDamage; var aiDamage = baseDamage; // Apply critical effects if (playerIsCritical) { playerDamage *= 2; // Critical success - double damage to opponent } else if (playerIsFailure) { // Critical failure - double damage to self (will be applied if player loses) playerDamage *= 2; } if (aiIsCritical) { aiDamage *= 2; // Critical success - double damage to opponent } else if (aiIsFailure) { // Critical failure - double damage to self (will be applied if AI loses) aiDamage *= 2; } if (result === 'player') { LK.getSound('win').play(); playerBattleElement.playWinAnimation(); aiBattleElement.playLoseAnimation(); // Player wins - damage to AI var damage = playerIsCritical ? playerDamage : baseDamage; aiLife -= damage; if (aiLife < 0) { aiLife = 0; } // Create result text var playerDesc = playerElement ? playerElement.toUpperCase() : playerComboEffect ? playerComboEffect.name : playerCombo.join(' + ').toUpperCase(); var aiDesc = aiElement ? aiElement.toUpperCase() : aiCombo.length === 2 ? aiCombo.join(' + ').toUpperCase() : 'AI COMBO'; 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!'; } instructionText.setText(resultText); instructionText.tint = 0x00ff00; // Check for AI death if (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 ? aiDamage : baseDamage; playerLife -= damage; if (playerLife < 0) { playerLife = 0; } var aiDesc = aiElement ? aiElement.toUpperCase() : aiCombo.length === 2 ? aiCombo.join(' + ').toUpperCase() : 'AI'; var playerDesc = playerElement ? playerElement.toUpperCase() : playerCombo.length === 2 ? playerCombo.join(' + ').toUpperCase() : 'PLAYER'; 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!'; } instructionText.setText(resultText); instructionText.tint = 0xff0000; // Check for player death if (playerLife <= 0) { LK.showGameOver(); return; } } else { // Handle critical failures in ties if (playerIsFailure && !aiIsFailure) { // Player critical failure - damage to player playerLife -= playerDamage; if (playerLife < 0) { playerLife = 0; } instructionText.setText('TIE!\nCRITICAL FAILURE! -' + playerDamage + ' Life'); instructionText.tint = 0xff0000; if (playerLife <= 0) { LK.showGameOver(); return; } } else if (aiIsFailure && !playerIsFailure) { // AI critical failure - damage to AI aiLife -= aiDamage; if (aiLife < 0) { aiLife = 0; } instructionText.setText('TIE!\nAI CRITICAL FAILURE! -' + aiDamage + ' AI Life'); instructionText.tint = 0x00ff00; if (aiLife <= 0) { LK.showYouWin(); return; } } else if (playerIsFailure && aiIsFailure) { // Both critical failures playerLife -= playerDamage; aiLife -= aiDamage; if (playerLife < 0) { playerLife = 0; } if (aiLife < 0) { aiLife = 0; } instructionText.setText('TIE!\nBOTH CRITICAL FAILURES!'); instructionText.tint = 0xffff00; if (playerLife <= 0 && aiLife <= 0) { LK.showGameOver(); // Player dies first in simultaneous death return; } else if (playerLife <= 0) { LK.showGameOver(); return; } else if (aiLife <= 0) { LK.showYouWin(); return; } } else { instructionText.setText('TIE!'); instructionText.tint = 0xffff00; } } // Make text bigger for result display instructionText.size = 180; // Update UI playerLifeText.setText('Player Life: ' + playerLife); aiLifeText.setText('AI Life: ' + aiLife); // Update weather system if (weatherRoundsLeft > 0) { weatherRoundsLeft--; updateWeatherDisplay(); if (weatherRoundsLeft === 0) { currentWeather = null; updateWeatherDisplay(); // Update display when weather ends } } else { // 30% chance to start new weather when no weather is active if (Math.random() < 0.3) { startNewWeather(); } } // Return to selection after delay LK.setTimeout(function () { resetForNextRound(); }, 2000); } // 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 = 0xff0000; // Red for first half } else { lineSegment.tint = 0x00ff00; // 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; // 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; } // Reset instruction text instructionText.setText('Choose first element for combo'); instructionText.tint = 0xcccccc; // Restore original font size instructionText.size = 32; // Clean up triangle if it exists if (instructionText.triangle) { instructionText.triangle.destroy(); instructionText.triangle = null; } // Clean up confirmation buttons if they exist if (confirmationContainer) { confirmationContainer.destroy(); confirmationContainer = null; } // Create victory arrow if we have a battle result and both elements are valid var battleResult = checkBattleResult(); if (playerElement && aiElement && battleResult !== 'tie') { if (battleResult === 'player') { createVictoryArrow(aiElement, playerElement); } else if (battleResult === 'ai') { createVictoryArrow(playerElement, aiElement); } } // Show victory arrows again for (var i = 0; i < victoryArrows.length; i++) { tween(victoryArrows[i], { alpha: 1 }, { duration: 300 }); } // 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; } 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(); // 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; } } } if (gameState === 'battle' && playerBattleElement && aiBattleElement) { battleTimer++; // Show result after 3 seconds if (battleTimer >= 180) { // 3 seconds at 60 FPS showBattleResult(); } } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BattleElement = Container.expand(function (elementType, isPlayer) {
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.toUpperCase(), {
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);
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.toUpperCase(), {
size: 24,
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 backgroundImage = null;
// 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);
}
// Initialize with default background
updateBackgroundForWeather();
// Game elements and their relationships
var elements = ['rock', 'paper', 'scissors', 'water', 'fire', 'air', 'lightning', 'tree', 'gun', 'sponge', 'smoke', 'gas', 'metal', 'ice', 'sand', 'oil'];
// Element defeat relationships (each element defeats 8 others)
var defeats = {
// Rock: Heavy and solid, crushes fragile things, breaks weapons, absorbs energy
'rock': ['scissors', 'sponge', 'gas', 'metal', 'ice', 'gun', 'tree', 'sand'],
// Paper: Wraps and covers, conducts electricity, absorbs liquids, blocks air flow
'paper': ['rock', 'metal', 'water', 'gas', 'sand', 'ice', 'air', 'smoke'],
// Scissors: Sharp cutting tool, cuts through soft materials and gases
'scissors': ['paper', 'sponge', 'air', 'tree', 'gas', 'oil', 'ice', 'smoke'],
// Water: Extinguishes fire, erodes materials, conducts electricity, flows through spaces
'water': ['fire', 'rock', 'scissors', 'gas', 'oil', 'sand', 'tree', 'metal'],
// Fire: Burns combustible materials, melts solids, heats and expands gases
'fire': ['paper', 'scissors', 'sponge', 'ice', 'tree', 'sand', 'oil', 'smoke'],
// Air: Feeds fire, disperses gases, creates pressure, moves objects
'air': ['fire', 'gas', 'smoke', 'metal', 'rock', 'oil', 'tree', 'gun'],
// Lightning: Electrical discharge, conducts through metals and water, ionizes gases
'lightning': ['water', 'metal', 'gun', 'air', 'paper', 'sponge', 'smoke', 'sand'],
// Tree: Absorbs water and nutrients, grows through materials, provides shelter
'tree': ['water', 'paper', 'sponge', 'smoke', 'gun', 'sand', 'ice', 'gas'],
// Gun: Projectile weapon, breaks through barriers, fires through gases
'gun': ['scissors', 'rock', 'tree', 'metal', 'gas', 'ice', 'oil', 'paper'],
// Sponge: Absorbs liquids and gases, flexible material, filters particles
'sponge': ['water', 'metal', 'air', 'paper', 'gas', 'rock', 'sand', 'smoke'],
// Smoke: Obscures vision, suffocates, corrodes metals, covers surfaces
'smoke': ['ice', 'gun', 'rock', 'paper', 'air', 'metal', 'water', 'oil'],
// Gas: Expands and fills spaces, displaces air, creates pressure, volatile
'gas': ['paper', 'ice', 'gun', 'smoke', 'air', 'scissors', 'sponge', 'sand'],
// Metal: Strong and durable, conducts electricity, resists cutting, heavy
'metal': ['scissors', 'tree', 'ice', 'fire', 'gun', 'oil', 'rock', 'sand'],
// Ice: Freezes liquids, slippery surface, preserves, expands when frozen
'ice': ['air', 'water', 'paper', 'sponge', 'fire', 'smoke', 'gun', 'lightning'],
// Sand: Abrasive particles, absorbs liquids, extinguishes fire, buries objects
'sand': ['scissors', 'ice', 'gun', 'fire', 'air', 'paper', 'rock', 'lightning'],
// Oil: Slippery liquid, flammable, coats surfaces, lubricates mechanisms
'oil': ['rock', 'scissors', 'gun', 'metal', 'water', 'gas', 'lightning', 'fire']
};
// Action keywords for how elements defeat others
var actionKeywords = {
'rock': {
'scissors': 'crushes',
'sponge': 'crushes',
'gas': 'disperses',
'metal': 'breaks',
'ice': 'shatters',
'gun': 'blocks',
'tree': 'crushes',
'sand': 'compacts'
},
'paper': {
'rock': 'wraps',
'metal': 'covers',
'water': 'absorbs',
'gas': 'captures',
'sand': 'covers',
'ice': 'insulates',
'air': 'blocks',
'smoke': 'filters'
},
'scissors': {
'paper': 'cuts',
'sponge': 'cuts',
'air': 'cuts',
'tree': 'cuts',
'gas': 'cuts',
'oil': 'cuts',
'ice': 'cuts',
'smoke': 'cuts'
},
'water': {
'fire': 'extinguishes',
'rock': 'erodes',
'scissors': 'rusts',
'gas': 'dissolves',
'oil': 'displaces',
'sand': 'washes',
'tree': 'nourishes',
'metal': 'rusts'
},
'fire': {
'paper': 'burns',
'scissors': 'melts',
'sponge': 'burns',
'ice': 'melts',
'tree': 'burns',
'sand': 'heats',
'oil': 'ignites',
'smoke': 'burns'
},
'air': {
'fire': 'feeds',
'gas': 'disperses',
'smoke': 'clears',
'metal': 'oxidizes',
'rock': 'erodes',
'oil': 'evaporates',
'tree': 'sways',
'gun': 'deflects'
},
'lightning': {
'water': 'electrifies',
'metal': 'conducts',
'gun': 'shorts',
'air': 'ionizes',
'paper': 'ignites',
'sponge': 'shocks',
'smoke': 'disperses',
'sand': 'vitrifies'
},
'tree': {
'water': 'absorbs',
'paper': 'grows over',
'sponge': 'outgrows',
'smoke': 'filters',
'gun': 'blocks',
'sand': 'roots in',
'ice': 'breaks',
'gas': 'absorbs'
},
'gun': {
'scissors': 'shoots',
'rock': 'shatters',
'tree': 'shoots through',
'metal': 'pierces',
'gas': 'ignites',
'ice': 'shatters',
'oil': 'ignites',
'paper': 'pierces'
},
'sponge': {
'water': 'absorbs',
'metal': 'corrodes',
'air': 'filters',
'paper': 'soaks',
'gas': 'absorbs',
'rock': 'erodes',
'sand': 'absorbs',
'smoke': 'filters'
},
'smoke': {
'ice': 'melts',
'gun': 'jams',
'rock': 'corrodes',
'paper': 'stains',
'air': 'pollutes',
'metal': 'corrodes',
'water': 'pollutes',
'oil': 'mixes with'
},
'gas': {
'paper': 'dissolves',
'ice': 'sublimates',
'gun': 'corrodes',
'smoke': 'displaces',
'air': 'displaces',
'scissors': 'corrodes',
'sponge': 'fills',
'sand': 'displaces'
},
'metal': {
'scissors': 'dulls',
'tree': 'cuts',
'ice': 'breaks',
'fire': 'conducts',
'gun': 'reinforces',
'oil': 'repels',
'rock': 'crushes',
'sand': 'compacts'
},
'ice': {
'air': 'freezes',
'water': 'freezes',
'paper': 'freezes',
'sponge': 'freezes',
'fire': 'extinguishes',
'smoke': 'condenses',
'gun': 'jams',
'lightning': 'grounds'
},
'sand': {
'scissors': 'dulls',
'ice': 'melts',
'gun': 'jams',
'fire': 'smothers',
'air': 'fills',
'paper': 'abrades',
'rock': 'erodes',
'lightning': 'grounds'
},
'oil': {
'rock': 'lubricates',
'scissors': 'lubricates',
'gun': 'lubricates',
'metal': 'lubricates',
'water': 'floats on',
'gas': 'dissolves',
'lightning': 'insulates',
'fire': 'feeds'
}
};
// 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 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
// Weather system variables
var currentWeather = null;
var weatherRoundsLeft = 0;
var weatherTypes = ['rainy', 'drought', 'windy', 'storm'];
var weatherEffects = {
'rainy': ['water', 'ice'],
// Water-based elements get bonus
'drought': ['fire', 'sand'],
// Fire-based elements get bonus
'windy': ['air', 'gas'],
// Air-based elements get bonus
'storm': ['lightning', 'metal'] // Electric/conductive elements get bonus
};
// Combo system definitions
var comboEffects = {
// Special 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: 'Inferno',
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
},
'gas+fire': {
name: 'Explosive Blast',
defeatsExtra: ['rock', 'metal', 'ice'],
multiplier: 2.0
},
'lightning+metal': {
name: 'Electric Conductor',
defeatsExtra: ['water', 'ice', 'gun'],
multiplier: 1.7
}
};
// 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
updateWeatherDisplay();
}
// 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 effectElements = weatherEffects[currentWeather].join(', ').toUpperCase();
text = weatherName + ' Season (' + weatherRoundsLeft + ' rounds)\n' + effectElements + ' elements boosted';
}
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();
// Apply visual boost effects to weather-enhanced elements
applyWeatherBoostEffects();
}
// Function to get weather color
function getWeatherColor(weather) {
switch (weather) {
case 'rainy':
return 0x4169E1;
// Royal blue
case 'drought':
return 0xFF4500;
// Orange red
case 'windy':
return 0x87CEEB;
// Sky blue
case 'storm':
return 0x9370DB;
// 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 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)) {
// Replace elementButton with boostedElementButton for visual enhancement
var background = button.children[0]; // The button background is the first child
console.log(background);
if (background) {
// Remove old background
button.removeChild(background);
// Add boosted background
var boostedBackground = button.attachAsset('boostedElementButton', {
anchorX: 0.5,
anchorY: 0.5
});
// Move to back so icon and label stay on top
button.setChildIndex(boostedBackground, 0);
}
// Boosted elements use boostedElementButton asset without scaling animation
} else {
// Replace boostedElementButton back with regular elementButton for non-boosted elements
var background = button.children[0]; // The button background is the first child
if (background && background.texture) {
// Check if it's currently boosted and needs to be replaced
var isCurrentlyBoosted = background.texture.baseTexture && background.texture.baseTexture.imageUrl && background.texture.baseTexture.imageUrl.includes('boostedElementButton');
if (isCurrentlyBoosted) {
// Remove boosted background
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 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('Your Combo:');
var icon1 = LK.getAsset(playerCombo[0], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
icon1.x = 60;
icon1.y = 40;
comboDisplay.addChild(icon1);
var plusText = new Text2(' + ?', {
size: 20,
fill: 0xFFFFFF,
font: "Trebuchet MS, Arial, sans-serif",
stroke: 0x000000,
strokeThickness: 2
});
plusText.x = 140;
plusText.y = 40;
plusText.anchor.set(0, 0.5);
comboDisplay.addChild(plusText);
} else if (playerCombo.length === 2) {
var effect = getComboEffect(playerCombo);
if (effect) {
comboDisplay.setText('Your Combo:\n' + effect.name);
comboDisplay.tint = 0x00FF00; // Green for special combo
} else {
comboDisplay.setText('Your Combo');
comboDisplay.tint = 0xFFD700; // Gold for normal combo
}
var icon1 = LK.getAsset(playerCombo[0], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
icon1.x = 40;
icon1.y = effect ? 40 : 40;
comboDisplay.addChild(icon1);
var plusText = new Text2(' + ', {
size: 20,
fill: 0xFFFFFF,
font: "Trebuchet MS, Arial, sans-serif",
stroke: 0x000000,
strokeThickness: 2
});
plusText.x = 90;
plusText.y = effect ? 40 : 40;
plusText.anchor.set(0.5, 0.5);
comboDisplay.addChild(plusText);
var icon2 = LK.getAsset(playerCombo[1], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
icon2.x = 140;
icon2.y = effect ? 40 : 40;
comboDisplay.addChild(icon2);
}
}
function updateAIComboDisplay() {
// Clear existing AI combo elements
for (var i = aiComboDisplay.children.length - 1; i >= 0; i--) {
aiComboDisplay.removeChild(aiComboDisplay.children[i]);
}
aiComboDisplay.setText('');
if (aiCombo.length === 0 && !aiElement) {
// No combo or element yet
} else if (aiCombo.length === 1) {
aiComboDisplay.setText('AI Combo:');
var icon1 = LK.getAsset(aiCombo[0], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
icon1.x = -80;
icon1.y = -50;
aiComboDisplay.addChild(icon1);
var plusText = new Text2(' + ?', {
size: 20,
fill: 0xFFFFFF,
font: "Trebuchet MS, Arial, sans-serif",
stroke: 0x000000,
strokeThickness: 2
});
plusText.x = -120;
plusText.y = -50;
plusText.anchor.set(1, 0.5);
aiComboDisplay.addChild(plusText);
} else if (aiCombo.length === 2) {
var effect = getComboEffect(aiCombo);
if (effect) {
aiComboDisplay.setText('AI Combo:\n' + effect.name);
aiComboDisplay.tint = 0xFF0000; // Red for AI special combo
} else {
aiComboDisplay.setText('AI Combo:');
aiComboDisplay.tint = 0xFF6B35; // Orange for AI normal combo
}
var icon1 = LK.getAsset(aiCombo[0], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
icon1.x = -120;
icon1.y = effect ? -80 : -50;
aiComboDisplay.addChild(icon1);
var plusText = new Text2(' + ', {
size: 20,
fill: 0xFFFFFF,
font: "Trebuchet MS, Arial, sans-serif",
stroke: 0x000000,
strokeThickness: 2
});
plusText.x = -90;
plusText.y = effect ? -80 : -50;
plusText.anchor.set(0.5, 0.5);
aiComboDisplay.addChild(plusText);
var icon2 = LK.getAsset(aiCombo[1], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
icon2.x = -60;
icon2.y = effect ? -80 : -50;
aiComboDisplay.addChild(icon2);
} else if (aiElement) {
aiComboDisplay.setText('AI Element:');
var icon = LK.getAsset(aiElement, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.3,
scaleY: 0.3
});
icon.x = -80;
icon.y = -50;
aiComboDisplay.addChild(icon);
}
}
// Text positioning parameter - distance to move text toward wheel center
var textOffsetTowardCenter = 160; // Adjustable parameter for text positioning
// UI elements
var titleText = new Text2('ULTIMATE ELEMENTS SHOWDOWN', {
size: 48,
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;
var instructionText = new Text2('Choose your element', {
size: 32,
fill: 0xCCCCCC,
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 = 400;
var playerLifeText = new Text2('Player Life: 100', {
size: 36,
fill: 0x00FF00,
font: "Trebuchet MS, Arial, sans-serif",
stroke: 0x000000,
strokeThickness: 3
});
playerLifeText.anchor.set(1, 0);
LK.gui.topRight.addChild(playerLifeText);
playerLifeText.x = -20;
playerLifeText.y = 20;
var aiLifeText = new Text2('AI Life: 100', {
size: 32,
fill: 0xFF0000,
font: "Trebuchet MS, Arial, sans-serif",
stroke: 0x000000,
strokeThickness: 3
});
aiLifeText.anchor.set(1, 0);
LK.gui.topRight.addChild(aiLifeText);
aiLifeText.x = -20;
aiLifeText.y = 70;
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 bottom left
comboDisplay = new Text2('', {
size: 24,
fill: 0xFFD700,
font: "Trebuchet MS, Arial, sans-serif",
stroke: 0x000000,
strokeThickness: 2
});
comboDisplay.anchor.set(0, 1);
LK.gui.bottomLeft.addChild(comboDisplay);
comboDisplay.x = 20;
comboDisplay.y = -120;
// AI combo display in bottom right
var aiComboDisplay = new Text2('', {
size: 24,
fill: 0xFF6B35,
font: "Trebuchet MS, Arial, sans-serif",
stroke: 0x000000,
strokeThickness: 2
});
aiComboDisplay.anchor.set(1, 1);
LK.gui.bottomRight.addChild(aiComboDisplay);
aiComboDisplay.x = -20;
aiComboDisplay.y = -20;
// Create element selection grid
function createElementButtons() {
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 = 1400; // 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: 0.8,
scaleY: 0.8
});
useAsSingleButton.x = 200;
useAsSingleButton.y = 50;
useAsSingleButton.tint = 0xFFD700; // Gold tint
var singleText = new Text2('USE AS\nSINGLE', {
size: 20,
fill: 0xffffff,
font: "Trebuchet MS, Arial, sans-serif",
stroke: 0x000000,
strokeThickness: 2
});
singleText.anchor.set(0.5, 0.5);
singleText.x = 200;
singleText.y = 50;
confirmationContainer.addChild(singleText);
// Add title text to the popup
var titleText = new Text2('Click on another element for a combo\nor click on Use as Single', {
size: 24,
fill: 0x000000,
font: "Trebuchet MS, Arial, sans-serif",
stroke: 0xFFFFFF,
strokeThickness: 2
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 0;
titleText.y = -80;
confirmationContainer.addChild(titleText);
// Add click handlers
useAsComboButton.down = function (x, y, obj) {
LK.getSound('select').play();
handleComboChoice(selectedElement, true);
};
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
}
// Handle combo selection
if (playerCombo.length === 0) {
// First element selection
playerCombo.push(elementType);
updateComboDisplay();
instructionText.setText('Use ' + elementType.toUpperCase() + ' as single element or add second for combo?');
// 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();
// 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;
// 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...');
}
// 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 = 0;
game.addChild(triangle);
// Animate triangle in
tween(triangle, {
alpha: 1
}, {
duration: 300
});
// Store triangle reference for cleanup
instructionText.triangle = 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)
var availableElements = [];
for (var i = 0; i < elementButtons.length; i++) {
if (!elementButtons[i].isDisabled) {
availableElements.push(elementButtons[i].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
if (aiComboSpins === 1) {
// AI is building a combo - this is first element
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
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 using single element
isWheelSpinning = false;
aiElement = currentTopElement;
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 (not used by AI yet)
var availableElements = [];
for (var i = 0; i < elements.length; i++) {
if (usedAIElements.indexOf(elements[i]) === -1) {
availableElements.push(elements[i]);
}
}
// If no available elements, use all elements (shouldn't happen in normal gameplay)
if (availableElements.length === 0) {
availableElements = elements.slice();
}
// 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;
// Basic AI that becomes more strategic with difficulty
if (difficultyLevel === 1) {
// Random selection from safe elements
return elementsToChooseFrom[Math.floor(Math.random() * elementsToChooseFrom.length)];
} else if (difficultyLevel === 2) {
// 30% chance to counter player's last choice
if (Math.random() < 0.3 && playerElement) {
var counters = [];
for (var i = 0; i < elementsToChooseFrom.length; i++) {
if (defeats[elementsToChooseFrom[i]] && defeats[elementsToChooseFrom[i]].indexOf(playerElement) !== -1) {
counters.push(elementsToChooseFrom[i]);
}
}
if (counters.length > 0) {
return counters[Math.floor(Math.random() * counters.length)];
}
}
return elementsToChooseFrom[Math.floor(Math.random() * elementsToChooseFrom.length)];
} else {
// 50% chance to counter player's choice
if (Math.random() < 0.5 && playerElement) {
var counters = [];
for (var i = 0; i < elementsToChooseFrom.length; i++) {
if (defeats[elementsToChooseFrom[i]] && defeats[elementsToChooseFrom[i]].indexOf(playerElement) !== -1) {
counters.push(elementsToChooseFrom[i]);
}
}
if (counters.length > 0) {
return counters[Math.floor(Math.random() * counters.length)];
}
}
return elementsToChooseFrom[Math.floor(Math.random() * elementsToChooseFrom.length)];
}
}
// 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();
}
// 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 triangle if it exists
if (instructionText.triangle) {
tween(instructionText.triangle, {
alpha: 0
}, {
duration: 300,
onFinish: function onFinish() {
instructionText.triangle.destroy();
instructionText.triangle = null;
}
});
}
// 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 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 = 80;
playerSecondIcon.y = -100;
playerBattleElement.addChild(playerSecondIcon);
}
aiBattleElement = game.addChild(new BattleElement(aiDisplayElement, false));
aiBattleElement.x = 1648;
aiBattleElement.y = 1100;
aiBattleElement.alpha = 0;
// Add second element icon 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);
}
// Animate battle elements in
tween(playerBattleElement, {
alpha: 1,
x: 512
}, {
duration: 500
});
tween(aiBattleElement, {
alpha: 1,
x: 1536
}, {
duration: 500
});
// Add VS text
vsText = new Text2('VS', {
size: 72,
fill: 0xFF6B35,
font: "Trebuchet MS, Arial, sans-serif",
stroke: 0x000000,
strokeThickness: 5
});
vsText.anchor.set(0.5, 0.5);
vsText.x = 1024;
vsText.y = 1100;
vsText.alpha = 0;
game.addChild(vsText);
tween(vsText, {
alpha: 1,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 300,
onFinish: function onFinish() {
LK.getSound('battle').play();
// Start battle timer
battleTimer = 0;
}
});
}
// 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;
// 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;
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 (playerWins) {
return 'player';
} else if (aiWins) {
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;
var playerIsFailure = playerCombo.length === 2 && playerComboEffect === null;
var aiIsCritical = aiCombo.length === 2 && aiComboEffect !== null;
var aiIsFailure = aiCombo.length === 2 && aiComboEffect === null;
var baseDamage = 10;
var playerDamage = baseDamage;
var aiDamage = baseDamage;
// Apply critical effects
if (playerIsCritical) {
playerDamage *= 2; // Critical success - double damage to opponent
} else if (playerIsFailure) {
// Critical failure - double damage to self (will be applied if player loses)
playerDamage *= 2;
}
if (aiIsCritical) {
aiDamage *= 2; // Critical success - double damage to opponent
} else if (aiIsFailure) {
// Critical failure - double damage to self (will be applied if AI loses)
aiDamage *= 2;
}
if (result === 'player') {
LK.getSound('win').play();
playerBattleElement.playWinAnimation();
aiBattleElement.playLoseAnimation();
// Player wins - damage to AI
var damage = playerIsCritical ? playerDamage : baseDamage;
aiLife -= damage;
if (aiLife < 0) {
aiLife = 0;
}
// Create result text
var playerDesc = playerElement ? playerElement.toUpperCase() : playerComboEffect ? playerComboEffect.name : playerCombo.join(' + ').toUpperCase();
var aiDesc = aiElement ? aiElement.toUpperCase() : aiCombo.length === 2 ? aiCombo.join(' + ').toUpperCase() : 'AI COMBO';
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!';
}
instructionText.setText(resultText);
instructionText.tint = 0x00ff00;
// Check for AI death
if (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 ? aiDamage : baseDamage;
playerLife -= damage;
if (playerLife < 0) {
playerLife = 0;
}
var aiDesc = aiElement ? aiElement.toUpperCase() : aiCombo.length === 2 ? aiCombo.join(' + ').toUpperCase() : 'AI';
var playerDesc = playerElement ? playerElement.toUpperCase() : playerCombo.length === 2 ? playerCombo.join(' + ').toUpperCase() : 'PLAYER';
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!';
}
instructionText.setText(resultText);
instructionText.tint = 0xff0000;
// Check for player death
if (playerLife <= 0) {
LK.showGameOver();
return;
}
} else {
// Handle critical failures in ties
if (playerIsFailure && !aiIsFailure) {
// Player critical failure - damage to player
playerLife -= playerDamage;
if (playerLife < 0) {
playerLife = 0;
}
instructionText.setText('TIE!\nCRITICAL FAILURE! -' + playerDamage + ' Life');
instructionText.tint = 0xff0000;
if (playerLife <= 0) {
LK.showGameOver();
return;
}
} else if (aiIsFailure && !playerIsFailure) {
// AI critical failure - damage to AI
aiLife -= aiDamage;
if (aiLife < 0) {
aiLife = 0;
}
instructionText.setText('TIE!\nAI CRITICAL FAILURE! -' + aiDamage + ' AI Life');
instructionText.tint = 0x00ff00;
if (aiLife <= 0) {
LK.showYouWin();
return;
}
} else if (playerIsFailure && aiIsFailure) {
// Both critical failures
playerLife -= playerDamage;
aiLife -= aiDamage;
if (playerLife < 0) {
playerLife = 0;
}
if (aiLife < 0) {
aiLife = 0;
}
instructionText.setText('TIE!\nBOTH CRITICAL FAILURES!');
instructionText.tint = 0xffff00;
if (playerLife <= 0 && aiLife <= 0) {
LK.showGameOver(); // Player dies first in simultaneous death
return;
} else if (playerLife <= 0) {
LK.showGameOver();
return;
} else if (aiLife <= 0) {
LK.showYouWin();
return;
}
} else {
instructionText.setText('TIE!');
instructionText.tint = 0xffff00;
}
}
// Make text bigger for result display
instructionText.size = 180;
// Update UI
playerLifeText.setText('Player Life: ' + playerLife);
aiLifeText.setText('AI Life: ' + aiLife);
// Update weather system
if (weatherRoundsLeft > 0) {
weatherRoundsLeft--;
updateWeatherDisplay();
if (weatherRoundsLeft === 0) {
currentWeather = null;
updateWeatherDisplay(); // Update display when weather ends
}
} else {
// 30% chance to start new weather when no weather is active
if (Math.random() < 0.3) {
startNewWeather();
}
}
// Return to selection after delay
LK.setTimeout(function () {
resetForNextRound();
}, 2000);
}
// 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 = 0xff0000; // Red for first half
} else {
lineSegment.tint = 0x00ff00; // 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;
// 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;
}
// Reset instruction text
instructionText.setText('Choose first element for combo');
instructionText.tint = 0xcccccc;
// Restore original font size
instructionText.size = 32;
// Clean up triangle if it exists
if (instructionText.triangle) {
instructionText.triangle.destroy();
instructionText.triangle = null;
}
// Clean up confirmation buttons if they exist
if (confirmationContainer) {
confirmationContainer.destroy();
confirmationContainer = null;
}
// Create victory arrow if we have a battle result and both elements are valid
var battleResult = checkBattleResult();
if (playerElement && aiElement && battleResult !== 'tie') {
if (battleResult === 'player') {
createVictoryArrow(aiElement, playerElement);
} else if (battleResult === 'ai') {
createVictoryArrow(playerElement, aiElement);
}
} // Show victory arrows again
for (var i = 0; i < victoryArrows.length; i++) {
tween(victoryArrows[i], {
alpha: 1
}, {
duration: 300
});
}
// 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;
}
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();
// 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;
}
}
}
if (gameState === 'battle' && playerBattleElement && aiBattleElement) {
battleTimer++;
// Show result after 3 seconds
if (battleTimer >= 180) {
// 3 seconds at 60 FPS
showBattleResult();
}
}
};
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