User prompt
Ok, now there are two issues: 1. THe bezier lines are not rendered anymore. Fix them. 2. The labels of the elements are weirdly shown when the wheel spins, in different places. They shoudl kept their relative positions constant. 3. Hover does not work anymore. โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
Okyou can remove the debug frame of the top element โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
Ok now it works. However, if your wheel stops on a top element which is disabled, sping it until the closest non disabled element., โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
Ok now you have some problems with neighbours, and that' sbecause your wheel stops sometimes between elements. NEVER stop between elements. You should stop the wheel at a perfect position as before the spinning, with a top element perfectly aligned. โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
I see. Ok for you the TOP element is the LEFT element <- You neeed the element on the ROCK position, that is 4 positions if you go right on the wheel, from Metal (left). โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
Your top calculation is absolutely wrong and it's totally breaking the game. Can you add a debug frame to the element you consider the top element? So taht I can see what is your problem โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
Your top element calculation is wrong. I don't know what's so difficult: at the starting point, is ROCK. Just remember those coordinates. Spin the wheel. What is there instead of ROCK - that's the top. Always make sure the wheel after spinning stops with at element aligned on top as in the startt. โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
I don't think you are properly tracking the top element. I was lightning and you selected scissors. There is an issue with your top element calculation. Also please hide the labels of the elements while spinning. โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
No. The marker does not work. Implement it otherwise: don't use a marker, just keep track of the top element (in the beginning it's Rock). Spin the wheel. When it stops, make sure there is an element on top. That element is the AI element. This way, we always know the top element when the wheel stops is the AI element. โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
No. So the marker should determine which is the AI button. If the marker stops on top of Metal, AI will chose metal. But there are conditions: it can't never end on a disabled element. โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
There should be a bug - it stopped at lightning but gas was selected. I think the marker is pointing to another place?
User prompt
Ok after the VS battle, the wheel is weirdly rotated and it's not even showing the real place where it stopped, there should be a bug. Also all the elements are rotated. So please fix that, make sure the wheel starts after every VS place in the same position as it stars when the game starts. โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
Ok you spin the wheel correctly, but there are issues: 1. Elements can't be rotated, keep then facing / oriented teh same place. 2. Reset the wheel after each battle. 3. Add a marker to see which is the one that will be selected after the spinning wheel finishes. 4. Leave 2 seconds before moving to VS screen. โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
Ok, let's make something cool. I want you to mimic a spinning wheel after the user has selected an element. The wheel spins until it stops - that element button where it stops will be the one selected by the AI. Don't change the VS screen or anything else. Also, make sure the wheel never stops spinning on a disabled button. โช๐ก Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Hide the bezier lines when in the VS screen
User prompt
Remove the arrow heads from the bezier lines
Code edit (1 edits merged)
Please save this source code
User prompt
Ok about the bezier curves - let's change them. Let's make them have a control point that is always the center of the wheel. So they go from winning element to losing element throw the center of the wheel. Also, make much more segements and the half of the line going to the winning element should be green, the half from the losing element - red โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
Ok the bezier lines work but the curvature is in the opposite side. If the elements are on the right, they should be lie (, if the elements are on the left, then ). โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please do
Code edit (1 edits merged)
Please save this source code
User prompt
The arrows - don't make erase them after you draw them. Leave them. โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
Disabling is not working. I should NOT be able to reuse any element the user o the AI have used in previous rounds! โช๐ก Consider importing and using the following plugins: @upit/tween.v1
User prompt
The buttons I used are still enabled!!! Please correct. The should be disabled, meaning 1) you can't select them 2) you can't hover them 3) they should be alpha 0.5 โช๐ก Consider importing and using the following plugins: @upit/tween.v1
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BattleElement = Container.expand(function (elementType, isPlayer) {
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
});
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
});
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;
var offsetY = Math.sin(angle) * textOffsetTowardCenter;
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
var backgroundImage = game.attachAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.137,
scaleY: 1.013
});
backgroundImage.x = 1024;
backgroundImage.y = 1366;
// 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 consecutiveWins = 0;
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
// Function to create victory arrow between two element buttons
function createVictoryArrow(fromElement, toElement) {
var fromButton = null;
var toButton = null;
// Find the buttons for the given elements
for (var i = 0; i < elementButtons.length; i++) {
if (elementButtons[i].elementType === fromElement) {
fromButton = elementButtons[i];
}
if (elementButtons[i].elementType === toElement) {
toButton = elementButtons[i];
}
}
if (!fromButton || !toButton) {
return; // Can't create arrow if buttons not found
}
// Calculate arrow properties
var dx = toButton.x - fromButton.x;
var dy = toButton.y - fromButton.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var angle = Math.atan2(dy, dx);
// Calculate angular separation to determine curve direction
var centerX = 1024;
var centerY = 1400;
var fromAngle = Math.atan2(fromButton.y - centerY, fromButton.x - centerX);
var toAngle = Math.atan2(toButton.y - centerY, toButton.x - centerX);
var angularDiff = Math.abs(fromAngle - toAngle);
if (angularDiff > Math.PI) {
angularDiff = 2 * Math.PI - angularDiff;
}
// Determine curve intensity based on proximity
var curvature = 0;
if (angularDiff < Math.PI / 2) {
// Elements are within 90 degrees of each other
curvature = 400; // Strong outward curve
} else if (angularDiff < Math.PI) {
// Elements are within 180 degrees
curvature = 200; // Moderate curve
} else {
curvature = 100; // Gentle curve for opposite elements
}
// Use wheel center as control point for Bezier curve
var controlX = centerX;
var controlY = centerY;
// Create arrow container
var arrowContainer = new Container();
// Create curved arrow using Bezier curve segments with more segments
var numSegments = 50;
var segmentWidth = 15;
for (var i = 0; i < numSegments; i++) {
var t = i / (numSegments - 1);
// Quadratic Bezier curve calculation
var x = (1 - t) * (1 - t) * fromButton.x + 2 * (1 - t) * t * controlX + t * t * toButton.x;
var y = (1 - t) * (1 - t) * fromButton.y + 2 * (1 - t) * t * controlY + t * t * toButton.y;
// Calculate next point for rotation
var nextT = Math.min(1, (i + 1) / (numSegments - 1));
var nextX = (1 - nextT) * (1 - nextT) * fromButton.x + 2 * (1 - nextT) * nextT * controlX + nextT * nextT * toButton.x;
var nextY = (1 - nextT) * (1 - nextT) * fromButton.y + 2 * (1 - nextT) * nextT * controlY + nextT * nextT * toButton.y;
// Skip segments too close to buttons
var distFromStart = Math.sqrt((x - fromButton.x) * (x - fromButton.x) + (y - fromButton.y) * (y - fromButton.y));
var distFromEnd = Math.sqrt((x - toButton.x) * (x - toButton.x) + (y - toButton.y) * (y - toButton.y));
if (distFromStart < 100 || distFromEnd < 100) {
continue;
}
var segment = arrowContainer.attachAsset('line', {
width: 5,
height: segmentWidth,
anchorX: 0.5,
anchorY: 0.5
});
segment.x = x;
segment.y = y;
// Rotate segment to match curve direction
var segmentAngle = Math.atan2(nextY - y, nextX - x);
segment.rotation = segmentAngle;
// Color segments: first half green (winning element side), second half red (losing element side)
if (t < 0.5) {
segment.tint = 0x00ff00; // Green color for first half
} else {
segment.tint = 0xff0000; // Red color for second half
}
}
// Arrowheads removed - only curved line segments remain
// Position container at origin (segments are already positioned absolutely)
arrowContainer.x = 0;
arrowContainer.y = 0;
arrowContainer.alpha = 0;
// Add to game and animate
game.addChild(arrowContainer);
victoryArrows.push(arrowContainer);
// Animate arrow appearance
tween(arrowContainer, {
alpha: 1
}, {
duration: 500
});
}
// 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);
// Rock starts at -PI/2, so we need to adjust our calculation
// Convert to positive angle starting from rock's position
var adjustedRotation = normalizedRotation + Math.PI / 2;
// Calculate how many element positions we've rotated
var elementSteps = Math.round(adjustedRotation / anglePerElement);
// Find which element is now at the top (accounting for direction)
var topElementIndex = (elements.length - elementSteps % elements.length) % elements.length;
return elements[topElementIndex];
}
// 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
});
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
});
instructionText.anchor.set(0.5, 0.5);
game.addChild(instructionText);
instructionText.x = 1024;
instructionText.y = 400;
var scoreText = new Text2('Score: 0', {
size: 36,
fill: 0xFFFFFF
});
scoreText.anchor.set(1, 0);
LK.gui.topRight.addChild(scoreText);
scoreText.x = -20;
scoreText.y = 20;
var winsText = new Text2('Wins: 0', {
size: 32,
fill: 0xFFFFFF
});
winsText.anchor.set(1, 0);
LK.gui.topRight.addChild(winsText);
winsText.x = -20;
winsText.y = 70;
// Create element selection grid
function createElementButtons() {
var centerX = 1024;
var centerY = 1400;
var radius = 800;
// 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
elementButtons.push(button);
}
}
// 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
}
playerElement = elementType;
// Update button states
for (var i = 0; i < elementButtons.length; i++) {
elementButtons[i].setSelected(elementButtons[i].elementType === elementType);
}
// Start wheel spinning animation
startWheelSpin();
}
// Start wheel spinning animation
function startWheelSpin() {
gameState = 'spinning';
isWheelSpinning = true;
instructionText.setText('AI is choosing...');
// 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) {
tween(label, {
alpha: 0
}, {
duration: 300
});
}
}
// 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;
// Reset wheel rotation for animation
wheelContainer.rotation = 0;
// Animate wheel spinning
tween(wheelContainer, {
rotation: finalRotation
}, {
duration: 2000 + Math.random() * 1000,
// 2-3 seconds
easing: tween.easeOut,
onFinish: function onFinish() {
isWheelSpinning = false;
// Get the element at the top position after spinning
aiElement = getTopElement();
// Show element labels again
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) {
tween(label, {
alpha: 1
}, {
duration: 300
});
}
}
// Start battle after wheel stops
LK.setTimeout(function () {
startBattle();
}, 2000); // Wait 2 seconds before VS screen
}
});
}
// AI element selection
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();
}
// Basic AI that becomes more strategic with difficulty
if (difficultyLevel === 1) {
// Random selection from available elements
return availableElements[Math.floor(Math.random() * availableElements.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 < availableElements.length; i++) {
if (defeats[availableElements[i]] && defeats[availableElements[i]].indexOf(playerElement) !== -1) {
counters.push(availableElements[i]);
}
}
if (counters.length > 0) {
return counters[Math.floor(Math.random() * counters.length)];
}
}
return availableElements[Math.floor(Math.random() * availableElements.length)];
} else {
// 50% chance to counter player's choice
if (Math.random() < 0.5 && playerElement) {
var counters = [];
for (var i = 0; i < availableElements.length; i++) {
if (defeats[availableElements[i]] && defeats[availableElements[i]].indexOf(playerElement) !== -1) {
counters.push(availableElements[i]);
}
}
if (counters.length > 0) {
return counters[Math.floor(Math.random() * counters.length)];
}
}
return availableElements[Math.floor(Math.random() * availableElements.length)];
}
}
// Start battle phase
function startBattle() {
gameState = 'battle';
// aiElement is already set by wheel spin, only get it if not set
if (!aiElement) {
aiElement = getAIElement();
}
// Track used elements
if (usedPlayerElements.indexOf(playerElement) === -1) {
usedPlayerElements.push(playerElement);
}
if (usedAIElements.indexOf(aiElement) === -1) {
usedAIElements.push(aiElement);
}
// 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 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
playerBattleElement = game.addChild(new BattleElement(playerElement, true));
playerBattleElement.x = 400;
playerBattleElement.y = 1100;
playerBattleElement.alpha = 0;
aiBattleElement = game.addChild(new BattleElement(aiElement, false));
aiBattleElement.x = 1648;
aiBattleElement.y = 1100;
aiBattleElement.alpha = 0;
// 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
});
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() {
var playerWins = defeats[playerElement] && defeats[playerElement].indexOf(aiElement) !== -1;
var aiWins = defeats[aiElement] && defeats[aiElement].indexOf(playerElement) !== -1;
if (playerWins) {
return 'player';
} else if (aiWins) {
return 'ai';
} else {
return 'tie';
}
}
// Show battle result
function showBattleResult() {
gameState = 'result';
var result = checkBattleResult();
if (result === 'player') {
LK.getSound('win').play();
playerBattleElement.playWinAnimation();
aiBattleElement.playLoseAnimation();
consecutiveWins++;
var points = 10 * difficultyLevel * consecutiveWins;
LK.setScore(LK.getScore() + points);
var actionWord = getActionKeyword(playerElement, aiElement);
instructionText.setText(playerElement.toUpperCase() + ' ' + actionWord + ' ' + aiElement.toUpperCase() + '!\nYOU WIN! +' + points + ' points');
instructionText.tint = 0x00ff00;
// Increase difficulty every 3 wins
if (consecutiveWins % 3 === 0 && difficultyLevel < 3) {
difficultyLevel++;
}
} else if (result === 'ai') {
LK.getSound('lose').play();
playerBattleElement.playLoseAnimation();
aiBattleElement.playWinAnimation();
consecutiveWins = 0;
var actionWord = getActionKeyword(aiElement, playerElement);
instructionText.setText(aiElement.toUpperCase() + ' ' + actionWord + ' ' + playerElement.toUpperCase() + '!\nYOU LOSE!');
instructionText.tint = 0xff0000;
} else {
instructionText.setText('TIE!');
instructionText.tint = 0xffff00;
}
// Update UI
scoreText.setText('Score: ' + LK.getScore());
winsText.setText('Wins: ' + consecutiveWins);
// Return to selection after delay
LK.setTimeout(function () {
resetForNextRound();
}, 2000);
}
// 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 AI element selection
aiElement = null;
// 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 your element');
instructionText.tint = 0xcccccc;
// 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
});
}
// Create victory arrow after elements are visible again
LK.setTimeout(function () {
var result = checkBattleResult();
if (result === 'player') {
// Create victory arrow from player's element to AI's element
createVictoryArrow(playerElement, aiElement);
} else if (result === 'ai') {
// Create victory arrow from AI's element to player's element
createVictoryArrow(aiElement, playerElement);
}
}, 400); // Wait for wheel elements to fade in
// 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];
// Calculate distance from cursor to button center
var dx = x - button.x;
var dy = y - 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();
// 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;
}
}
if (gameState === 'battle' && playerBattleElement && aiBattleElement) {
battleTimer++;
// Show result after 3 seconds
if (battleTimer >= 180) {
// 3 seconds at 60 FPS
showBattleResult();
}
}
}; ===================================================================
--- original.js
+++ change.js
@@ -482,16 +482,21 @@
});
}
// Function to get the element at the top position
function getTopElement() {
- // Calculate which element is currently at the top (angle = -PI/2)
+ // 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);
- // The initial element (rock) is at index 0, positioned at -PI/2
- // Calculate how many elements we've rotated past
- var rotationFromStart = normalizedRotation / anglePerElement;
- var elementIndex = Math.round(rotationFromStart) % elements.length;
- return elements[elementIndex];
+ // Rock starts at -PI/2, so we need to adjust our calculation
+ // Convert to positive angle starting from rock's position
+ var adjustedRotation = normalizedRotation + Math.PI / 2;
+ // Calculate how many element positions we've rotated
+ var elementSteps = Math.round(adjustedRotation / anglePerElement);
+ // Find which element is now at the top (accounting for direction)
+ var topElementIndex = (elements.length - elementSteps % elements.length) % elements.length;
+ return elements[topElementIndex];
}
// Text positioning parameter - distance to move text toward wheel center
var textOffsetTowardCenter = 160; // Adjustable parameter for text positioning
// UI elements
@@ -589,22 +594,20 @@
}
}
// 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 finalRotation = spinAmount * Math.PI * 2;
- // Keep spinning until we find a valid stopping position
- var attempts = 0;
- var maxAttempts = 100;
- do {
- var testRotation = finalRotation + (Math.random() - 0.5) * Math.PI * 2;
- wheelContainer.rotation = testRotation;
- var testElement = getTopElement();
- if (availableElements.indexOf(testElement) !== -1) {
- finalRotation = testRotation;
- break;
- }
- attempts++;
- } while (attempts < maxAttempts);
+ 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;
// Reset wheel rotation for animation
wheelContainer.rotation = 0;
// Animate wheel spinning
tween(wheelContainer, {
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
A coin
Coin: heads side
Coin: tails side
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