/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Gear class: represents a single gear that rotates in a given direction and speed
var Gear = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of 5 gear assets
var gearAssetIdx = Math.floor(Math.random() * 5) + 1;
var gear = self.attachAsset('gear' + gearAssetIdx, {
anchorX: 0.5,
anchorY: 0.5
});
// Assign unique sound for this gear type
// Ensure gear5 uses the correct unique sound asset
if (gearAssetIdx === 5) {
self._sound = LK.getSound('gearRotate5');
self._soundName = 'gearRotate5';
} else {
self._sound = LK.getSound('gearRotate' + gearAssetIdx);
self._soundName = 'gearRotate' + gearAssetIdx;
}
self._gearAssetIdx = gearAssetIdx;
// Set up default size for asset (will be overridden externally)
gear.width = 270 * 2;
gear.height = 270 * 2;
// Rotation speed in radians per frame (set externally)
self.rotationSpeed = 0;
// Removed all gear sound logic
// Update method: rotates the gear
self.update = function () {
// Track last rotation for step detection
if (typeof self._lastRotation === "undefined") {
self._lastRotation = self.rotation;
}
self.rotation += self.rotationSpeed;
// Play unique sound for this gear type on each full step (e.g. every 1/12 turn)
var step = Math.PI / 6; // 30 degrees per step
var lastStep = Math.floor(self._lastRotation / step);
var currentStep = Math.floor(self.rotation / step);
if (self.rotationSpeed !== 0 && self._sound && lastStep !== currentStep) {
// Only play sound if at least one gear of this type is present on screen
var found = false;
for (var i = 0; i < gears.length; i++) {
if (gears[i] && gears[i]._gearAssetIdx === self._gearAssetIdx) {
found = true;
break;
}
}
if (found) {
self._sound.play();
}
// Score logic: Only update score when a half rotation is completed
if (typeof score !== "undefined" && typeof scoreTxt !== "undefined") {
// Track completed half turns
if (typeof self._lastHalfTurns === "undefined") {
self._lastHalfTurns = Math.floor(self._lastRotation / Math.PI);
}
var currentHalfTurns = Math.floor(self.rotation / Math.PI);
if (currentHalfTurns !== self._lastHalfTurns) {
// Only update score when a half rotation is completed
if (self.rotationSpeed > 0) {
score += 1;
} else if (self.rotationSpeed < 0) {
score -= 1;
}
animateScoreTo(score);
self._lastHalfTurns = currentHalfTurns;
}
}
}
self._lastRotation = self.rotation;
};
// Handle down event for single tap: remove gear and stop sound
self.down = function (x, y, obj) {
// Stop the unique sound for this gear if playing
if (self._sound && typeof self._sound.stop === "function") {
self._sound.stop();
self._soundPlaying = false;
}
// Remove from game and gears array
if (self.parent) {
self.parent.removeChild(self);
}
for (var i = 0; i < gears.length; i++) {
if (gears[i] === self) {
gears.splice(i, 1);
break;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xffffff // White background for clarity
});
/****
* Game Code
****/
// We need tween for smooth rotation animations
// Add background image (covers the whole game area)
var bg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
game.addChild(bg);
// --- Animated gear at game start ---
// Add finger asset for swipe animation
var introGear = new Gear();
introGear.x = 2048 / 2;
introGear.y = 2732 / 2;
introGear.rotation = 0;
introGear.rotationSpeed = 0;
introGear.children[0].width = 900;
introGear.children[0].height = 900;
introGear.alpha = 0;
game.addChild(introGear);
var finger = LK.getAsset('finger', {
anchorX: 0.5,
anchorY: 0.5,
width: 320,
height: 320,
x: introGear.x - 300,
y: introGear.y + 350,
alpha: 0
});
game.addChild(finger);
// Animate: finger and gear intro at the same time (parallel animation), but start after 1 second delay
LK.setTimeout(function () {
// Determine direction based on targetSign
var fingerStartX, fingerEndX;
if (targetSign < 0) {
// Right to left
fingerStartX = introGear.x + 300;
fingerEndX = introGear.x - 200;
} else {
// Left to right (default)
fingerStartX = introGear.x - 300;
fingerEndX = introGear.x + 200;
}
finger.x = fingerStartX;
tween(finger, {
alpha: 1
}, {
duration: 200,
onFinish: function onFinish() {
// Start finger swipe and gear animation in parallel
tween(finger, {
x: fingerEndX
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(finger, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
if (finger.parent) finger.parent.removeChild(finger);
}
});
}
});
// Start gear animation at the same time as finger swipe
tween(introGear, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2,
rotation: targetSign < 0 ? -Math.PI * 2 : Math.PI * 2 // Animate right-to-left if negative
}, {
duration: 900,
easing: tween.easeOut,
onFinish: function onFinish() {
// Fade out gear after animation
tween(introGear, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
if (introGear.parent) introGear.parent.removeChild(introGear);
}
});
}
});
}
});
}, 1000);
// Play background music for the whole game
LK.playMusic('backgroundSong');
// List of all gears in the game
var gears = [];
// Score variable and score text
var score = 0;
var displayScore = 0;
var scoreTween = null;
function formatScore(val) {
var s = Math.abs(Math.round(val)).toString().padStart(4, "0");
return (val < 0 ? "-" : "") + s;
}
// Add random target to top left at game start
var targetSign = Math.random() < 0.5 ? -1 : 1;
var targetValue = Math.floor(Math.random() * 10000); // 0 to 9999
var targetText = (targetSign < 0 ? "-" : "") + targetValue.toString().padStart(4, "0");
var targetTxt = new Text2(targetText, {
size: 150,
fill: 0xFFD600 // yellow
});
targetTxt.anchor.set(0, 0); // top left
LK.gui.topLeft.addChild(targetTxt);
// Place with margin to avoid menu icon
targetTxt.x = 410;
targetTxt.y = 10;
var scoreTxt = new Text2(formatScore(displayScore), {
size: 170,
fill: 0xFFFFFF
});
// Anchor to top right
scoreTxt.anchor.set(1, 0); // right-top
LK.gui.topRight.addChild(scoreTxt);
// Place at top right, with a small margin from the edge
scoreTxt.x = -40;
scoreTxt.y = 10;
// Helper: animate score change smoothly, one by one
function animateScoreTo(target) {
if (scoreTween) {
tween.stop(scoreTween);
scoreTween = null;
}
var start = displayScore;
var end = target;
if (start === end) {
displayScore = end;
scoreTxt.setText(formatScore(displayScore));
return;
}
var step = start < end ? 1 : -1;
var steps = Math.abs(end - start);
var durationPerStep = 30; // ms per step, adjust for speed
var current = start;
function stepTween() {
if (current === end) {
scoreTween = null;
return;
}
current += step;
displayScore = current;
scoreTxt.setText(formatScore(displayScore));
scoreTween = LK.setTimeout(stepTween, durationPerStep);
}
stepTween();
}
// For drag/hold/tap detection
var dragStart = null; // {x, y, t}
var dragCurrent = null; // {x, y}
var dragTimer = null;
var dragHold = false;
var dragGearPreview = null;
var dragPreviewGearType = 1; // type of gear to preview/create this drag
var dragDirection = 1; // 1: CW, -1: CCW
var dragSize = 240; // default
var tapThreshold = 200; // ms
var holdThreshold = 220; // ms
var minGear = 120,
// 2x smaller than before
maxGear = 390 * 4; //{G} // Allow up to 2x larger gears
// Helper: calculate angle between two points (in radians)
function getAngle(x1, y1, x2, y2) {
return Math.atan2(y2 - y1, x2 - x1);
}
// Helper: clamp rotation speed
function clamp(val, min, max) {
if (val < min) return min;
if (val > max) return max;
return val;
}
// Helper: find gear at (x, y)
function findGearAt(x, y) {
for (var i = 0; i < gears.length; i++) {
var g = gears[i];
var dx = g.x - x;
var dy = g.y - y;
var r = g.children[0].width / 2;
if (dx * dx + dy * dy <= r * r) return g;
}
return null;
}
// On press down: start drag/tap/hold detection
game.down = function (x, y, obj) {
dragStart = {
x: x,
y: y,
t: Date.now()
};
dragCurrent = {
x: x,
y: y
};
dragHold = false;
dragDirection = 1;
// Default size is mid-way between min and max
dragSize = (minGear + maxGear) / 2;
// If tap on existing gear, mark for possible removal
var gear = findGearAt(x, y);
if (gear) {
dragGearPreview = gear;
return;
}
// Start hold timer for gear creation
dragTimer = LK.setTimeout(function () {
dragHold = true;
// Show preview gear
// Pick a random gear type and remember it for this drag
dragPreviewGearType = Math.floor(Math.random() * 5) + 1;
dragGearPreview = new Gear();
dragGearPreview.x = dragStart.x;
dragGearPreview.y = dragStart.y;
dragGearPreview.rotationSpeed = 0;
dragGearPreview.children[0].width = dragSize;
dragGearPreview.children[0].height = dragSize;
dragGearPreview.alpha = 0.5;
// Replace the gear asset with the selected type
if (dragGearPreview.children.length > 0) {
dragGearPreview.removeChild(dragGearPreview.children[0]);
}
var previewAsset = dragGearPreview.attachAsset('gear' + dragPreviewGearType, {
anchorX: 0.5,
anchorY: 0.5
});
previewAsset.width = dragSize;
previewAsset.height = dragSize;
game.addChild(dragGearPreview);
}, holdThreshold);
};
// On move: update drag current position, update preview gear size/direction
game.move = function (x, y, obj) {
if (!dragStart) return;
dragCurrent.x = x;
dragCurrent.y = y;
if (dragHold && dragGearPreview) {
// Calculate drag vector
var dx = dragCurrent.x - dragStart.x;
var dy = dragCurrent.y - dragStart.y;
var dist = Math.sqrt(dx * dx + dy * dy);
// Size: proportional to drag distance, clamp to min/max
// Map drag distance from 0..(max possible) to minGear..maxGear
// Let's use up to 900px drag for full range (since maxGear is now 2x larger)
var maxDragDist = 900;
var size = clamp(minGear + (maxGear - minGear) * Math.min(dist, maxDragDist) / maxDragDist, minGear, maxGear);
dragSize = size;
dragGearPreview.children[0].width = size;
dragGearPreview.children[0].height = size;
// Direction: right is CW, left is CCW
dragDirection = dx >= 0 ? 1 : -1;
dragGearPreview.scale.x = dragDirection;
// No speed text shown during drag preview
}
};
// On release: decide tap/hold/drag action
game.up = function (x, y, obj) {
if (dragTimer) {
LK.clearTimeout(dragTimer);
dragTimer = null;
}
var now = Date.now();
var dt = now - (dragStart ? dragStart.t : now);
// Tap on existing gear: remove it
if (dragGearPreview && gears.indexOf(dragGearPreview) !== -1) {
// Remove gear
if (dragGearPreview.parent) dragGearPreview.parent.removeChild(dragGearPreview);
var idx = gears.indexOf(dragGearPreview);
if (idx !== -1) gears.splice(idx, 1);
dragGearPreview = null;
dragStart = null;
return;
}
// Short tap: do nothing (no gear created)
if (!dragHold && dt < tapThreshold) {
// No speed text to remove
dragStart = null;
dragGearPreview = null;
return;
}
// Hold/drag: create gear at dragStart, with size/direction
if (dragHold && dragGearPreview) {
// No speed text to remove
// Remove preview
if (dragGearPreview.parent) dragGearPreview.parent.removeChild(dragGearPreview);
dragGearPreview = null;
// Prevent overlapping: check if new gear would overlap any existing gear
var overlap = false;
for (var i = 0; i < gears.length; i++) {
var other = gears[i];
var dxg = dragStart.x - other.x;
var dyg = dragStart.y - other.y;
var distg = Math.sqrt(dxg * dxg + dyg * dyg);
var otherGear = other.children[0].width;
if (distg < (dragSize + otherGear) / 2 + 10) {
overlap = true;
break;
}
}
if (!overlap) {
var gear = new Gear();
gear.x = dragStart.x;
gear.y = dragStart.y;
// Assign random speed between 0.01 and 0.06, use dragDirection for direction
var minSpeed = 0.01,
maxSpeed = 0.06;
var randomSpeed = minSpeed + Math.random() * (maxSpeed - minSpeed);
gear.rotationSpeed = randomSpeed * dragDirection;
// Replace the gear asset with the previewed type
if (gear.children.length > 0) {
gear.removeChild(gear.children[0]);
}
var asset = gear.attachAsset('gear' + dragPreviewGearType, {
anchorX: 0.5,
anchorY: 0.5
});
asset.width = dragSize;
asset.height = dragSize;
// Set correct sound for this gear type
if (dragPreviewGearType === 5) {
gear._sound = LK.getSound('gearRotate5');
gear._soundName = 'gearRotate5';
} else {
gear._sound = LK.getSound('gearRotate' + dragPreviewGearType);
gear._soundName = 'gearRotate' + dragPreviewGearType;
}
gear._gearAssetIdx = dragPreviewGearType;
game.addChild(gear);
gears.push(gear);
}
}
dragStart = null;
dragGearPreview = null;
};
// Update loop: update all gears
game.update = function () {
for (var i = 0; i < gears.length; i++) {
if (gears[i].update) {
gears[i].update();
}
}
// Check win condition: score is greater than or equal to target (with sign)
if (targetSign > 0 && score >= targetValue || targetSign < 0 && score <= -targetValue) {
LK.showYouWin();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Gear class: represents a single gear that rotates in a given direction and speed
var Gear = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of 5 gear assets
var gearAssetIdx = Math.floor(Math.random() * 5) + 1;
var gear = self.attachAsset('gear' + gearAssetIdx, {
anchorX: 0.5,
anchorY: 0.5
});
// Assign unique sound for this gear type
// Ensure gear5 uses the correct unique sound asset
if (gearAssetIdx === 5) {
self._sound = LK.getSound('gearRotate5');
self._soundName = 'gearRotate5';
} else {
self._sound = LK.getSound('gearRotate' + gearAssetIdx);
self._soundName = 'gearRotate' + gearAssetIdx;
}
self._gearAssetIdx = gearAssetIdx;
// Set up default size for asset (will be overridden externally)
gear.width = 270 * 2;
gear.height = 270 * 2;
// Rotation speed in radians per frame (set externally)
self.rotationSpeed = 0;
// Removed all gear sound logic
// Update method: rotates the gear
self.update = function () {
// Track last rotation for step detection
if (typeof self._lastRotation === "undefined") {
self._lastRotation = self.rotation;
}
self.rotation += self.rotationSpeed;
// Play unique sound for this gear type on each full step (e.g. every 1/12 turn)
var step = Math.PI / 6; // 30 degrees per step
var lastStep = Math.floor(self._lastRotation / step);
var currentStep = Math.floor(self.rotation / step);
if (self.rotationSpeed !== 0 && self._sound && lastStep !== currentStep) {
// Only play sound if at least one gear of this type is present on screen
var found = false;
for (var i = 0; i < gears.length; i++) {
if (gears[i] && gears[i]._gearAssetIdx === self._gearAssetIdx) {
found = true;
break;
}
}
if (found) {
self._sound.play();
}
// Score logic: Only update score when a half rotation is completed
if (typeof score !== "undefined" && typeof scoreTxt !== "undefined") {
// Track completed half turns
if (typeof self._lastHalfTurns === "undefined") {
self._lastHalfTurns = Math.floor(self._lastRotation / Math.PI);
}
var currentHalfTurns = Math.floor(self.rotation / Math.PI);
if (currentHalfTurns !== self._lastHalfTurns) {
// Only update score when a half rotation is completed
if (self.rotationSpeed > 0) {
score += 1;
} else if (self.rotationSpeed < 0) {
score -= 1;
}
animateScoreTo(score);
self._lastHalfTurns = currentHalfTurns;
}
}
}
self._lastRotation = self.rotation;
};
// Handle down event for single tap: remove gear and stop sound
self.down = function (x, y, obj) {
// Stop the unique sound for this gear if playing
if (self._sound && typeof self._sound.stop === "function") {
self._sound.stop();
self._soundPlaying = false;
}
// Remove from game and gears array
if (self.parent) {
self.parent.removeChild(self);
}
for (var i = 0; i < gears.length; i++) {
if (gears[i] === self) {
gears.splice(i, 1);
break;
}
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xffffff // White background for clarity
});
/****
* Game Code
****/
// We need tween for smooth rotation animations
// Add background image (covers the whole game area)
var bg = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
width: 2048,
height: 2732
});
game.addChild(bg);
// --- Animated gear at game start ---
// Add finger asset for swipe animation
var introGear = new Gear();
introGear.x = 2048 / 2;
introGear.y = 2732 / 2;
introGear.rotation = 0;
introGear.rotationSpeed = 0;
introGear.children[0].width = 900;
introGear.children[0].height = 900;
introGear.alpha = 0;
game.addChild(introGear);
var finger = LK.getAsset('finger', {
anchorX: 0.5,
anchorY: 0.5,
width: 320,
height: 320,
x: introGear.x - 300,
y: introGear.y + 350,
alpha: 0
});
game.addChild(finger);
// Animate: finger and gear intro at the same time (parallel animation), but start after 1 second delay
LK.setTimeout(function () {
// Determine direction based on targetSign
var fingerStartX, fingerEndX;
if (targetSign < 0) {
// Right to left
fingerStartX = introGear.x + 300;
fingerEndX = introGear.x - 200;
} else {
// Left to right (default)
fingerStartX = introGear.x - 300;
fingerEndX = introGear.x + 200;
}
finger.x = fingerStartX;
tween(finger, {
alpha: 1
}, {
duration: 200,
onFinish: function onFinish() {
// Start finger swipe and gear animation in parallel
tween(finger, {
x: fingerEndX
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(finger, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
if (finger.parent) finger.parent.removeChild(finger);
}
});
}
});
// Start gear animation at the same time as finger swipe
tween(introGear, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2,
rotation: targetSign < 0 ? -Math.PI * 2 : Math.PI * 2 // Animate right-to-left if negative
}, {
duration: 900,
easing: tween.easeOut,
onFinish: function onFinish() {
// Fade out gear after animation
tween(introGear, {
alpha: 0,
scaleX: 1.5,
scaleY: 1.5
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
if (introGear.parent) introGear.parent.removeChild(introGear);
}
});
}
});
}
});
}, 1000);
// Play background music for the whole game
LK.playMusic('backgroundSong');
// List of all gears in the game
var gears = [];
// Score variable and score text
var score = 0;
var displayScore = 0;
var scoreTween = null;
function formatScore(val) {
var s = Math.abs(Math.round(val)).toString().padStart(4, "0");
return (val < 0 ? "-" : "") + s;
}
// Add random target to top left at game start
var targetSign = Math.random() < 0.5 ? -1 : 1;
var targetValue = Math.floor(Math.random() * 10000); // 0 to 9999
var targetText = (targetSign < 0 ? "-" : "") + targetValue.toString().padStart(4, "0");
var targetTxt = new Text2(targetText, {
size: 150,
fill: 0xFFD600 // yellow
});
targetTxt.anchor.set(0, 0); // top left
LK.gui.topLeft.addChild(targetTxt);
// Place with margin to avoid menu icon
targetTxt.x = 410;
targetTxt.y = 10;
var scoreTxt = new Text2(formatScore(displayScore), {
size: 170,
fill: 0xFFFFFF
});
// Anchor to top right
scoreTxt.anchor.set(1, 0); // right-top
LK.gui.topRight.addChild(scoreTxt);
// Place at top right, with a small margin from the edge
scoreTxt.x = -40;
scoreTxt.y = 10;
// Helper: animate score change smoothly, one by one
function animateScoreTo(target) {
if (scoreTween) {
tween.stop(scoreTween);
scoreTween = null;
}
var start = displayScore;
var end = target;
if (start === end) {
displayScore = end;
scoreTxt.setText(formatScore(displayScore));
return;
}
var step = start < end ? 1 : -1;
var steps = Math.abs(end - start);
var durationPerStep = 30; // ms per step, adjust for speed
var current = start;
function stepTween() {
if (current === end) {
scoreTween = null;
return;
}
current += step;
displayScore = current;
scoreTxt.setText(formatScore(displayScore));
scoreTween = LK.setTimeout(stepTween, durationPerStep);
}
stepTween();
}
// For drag/hold/tap detection
var dragStart = null; // {x, y, t}
var dragCurrent = null; // {x, y}
var dragTimer = null;
var dragHold = false;
var dragGearPreview = null;
var dragPreviewGearType = 1; // type of gear to preview/create this drag
var dragDirection = 1; // 1: CW, -1: CCW
var dragSize = 240; // default
var tapThreshold = 200; // ms
var holdThreshold = 220; // ms
var minGear = 120,
// 2x smaller than before
maxGear = 390 * 4; //{G} // Allow up to 2x larger gears
// Helper: calculate angle between two points (in radians)
function getAngle(x1, y1, x2, y2) {
return Math.atan2(y2 - y1, x2 - x1);
}
// Helper: clamp rotation speed
function clamp(val, min, max) {
if (val < min) return min;
if (val > max) return max;
return val;
}
// Helper: find gear at (x, y)
function findGearAt(x, y) {
for (var i = 0; i < gears.length; i++) {
var g = gears[i];
var dx = g.x - x;
var dy = g.y - y;
var r = g.children[0].width / 2;
if (dx * dx + dy * dy <= r * r) return g;
}
return null;
}
// On press down: start drag/tap/hold detection
game.down = function (x, y, obj) {
dragStart = {
x: x,
y: y,
t: Date.now()
};
dragCurrent = {
x: x,
y: y
};
dragHold = false;
dragDirection = 1;
// Default size is mid-way between min and max
dragSize = (minGear + maxGear) / 2;
// If tap on existing gear, mark for possible removal
var gear = findGearAt(x, y);
if (gear) {
dragGearPreview = gear;
return;
}
// Start hold timer for gear creation
dragTimer = LK.setTimeout(function () {
dragHold = true;
// Show preview gear
// Pick a random gear type and remember it for this drag
dragPreviewGearType = Math.floor(Math.random() * 5) + 1;
dragGearPreview = new Gear();
dragGearPreview.x = dragStart.x;
dragGearPreview.y = dragStart.y;
dragGearPreview.rotationSpeed = 0;
dragGearPreview.children[0].width = dragSize;
dragGearPreview.children[0].height = dragSize;
dragGearPreview.alpha = 0.5;
// Replace the gear asset with the selected type
if (dragGearPreview.children.length > 0) {
dragGearPreview.removeChild(dragGearPreview.children[0]);
}
var previewAsset = dragGearPreview.attachAsset('gear' + dragPreviewGearType, {
anchorX: 0.5,
anchorY: 0.5
});
previewAsset.width = dragSize;
previewAsset.height = dragSize;
game.addChild(dragGearPreview);
}, holdThreshold);
};
// On move: update drag current position, update preview gear size/direction
game.move = function (x, y, obj) {
if (!dragStart) return;
dragCurrent.x = x;
dragCurrent.y = y;
if (dragHold && dragGearPreview) {
// Calculate drag vector
var dx = dragCurrent.x - dragStart.x;
var dy = dragCurrent.y - dragStart.y;
var dist = Math.sqrt(dx * dx + dy * dy);
// Size: proportional to drag distance, clamp to min/max
// Map drag distance from 0..(max possible) to minGear..maxGear
// Let's use up to 900px drag for full range (since maxGear is now 2x larger)
var maxDragDist = 900;
var size = clamp(minGear + (maxGear - minGear) * Math.min(dist, maxDragDist) / maxDragDist, minGear, maxGear);
dragSize = size;
dragGearPreview.children[0].width = size;
dragGearPreview.children[0].height = size;
// Direction: right is CW, left is CCW
dragDirection = dx >= 0 ? 1 : -1;
dragGearPreview.scale.x = dragDirection;
// No speed text shown during drag preview
}
};
// On release: decide tap/hold/drag action
game.up = function (x, y, obj) {
if (dragTimer) {
LK.clearTimeout(dragTimer);
dragTimer = null;
}
var now = Date.now();
var dt = now - (dragStart ? dragStart.t : now);
// Tap on existing gear: remove it
if (dragGearPreview && gears.indexOf(dragGearPreview) !== -1) {
// Remove gear
if (dragGearPreview.parent) dragGearPreview.parent.removeChild(dragGearPreview);
var idx = gears.indexOf(dragGearPreview);
if (idx !== -1) gears.splice(idx, 1);
dragGearPreview = null;
dragStart = null;
return;
}
// Short tap: do nothing (no gear created)
if (!dragHold && dt < tapThreshold) {
// No speed text to remove
dragStart = null;
dragGearPreview = null;
return;
}
// Hold/drag: create gear at dragStart, with size/direction
if (dragHold && dragGearPreview) {
// No speed text to remove
// Remove preview
if (dragGearPreview.parent) dragGearPreview.parent.removeChild(dragGearPreview);
dragGearPreview = null;
// Prevent overlapping: check if new gear would overlap any existing gear
var overlap = false;
for (var i = 0; i < gears.length; i++) {
var other = gears[i];
var dxg = dragStart.x - other.x;
var dyg = dragStart.y - other.y;
var distg = Math.sqrt(dxg * dxg + dyg * dyg);
var otherGear = other.children[0].width;
if (distg < (dragSize + otherGear) / 2 + 10) {
overlap = true;
break;
}
}
if (!overlap) {
var gear = new Gear();
gear.x = dragStart.x;
gear.y = dragStart.y;
// Assign random speed between 0.01 and 0.06, use dragDirection for direction
var minSpeed = 0.01,
maxSpeed = 0.06;
var randomSpeed = minSpeed + Math.random() * (maxSpeed - minSpeed);
gear.rotationSpeed = randomSpeed * dragDirection;
// Replace the gear asset with the previewed type
if (gear.children.length > 0) {
gear.removeChild(gear.children[0]);
}
var asset = gear.attachAsset('gear' + dragPreviewGearType, {
anchorX: 0.5,
anchorY: 0.5
});
asset.width = dragSize;
asset.height = dragSize;
// Set correct sound for this gear type
if (dragPreviewGearType === 5) {
gear._sound = LK.getSound('gearRotate5');
gear._soundName = 'gearRotate5';
} else {
gear._sound = LK.getSound('gearRotate' + dragPreviewGearType);
gear._soundName = 'gearRotate' + dragPreviewGearType;
}
gear._gearAssetIdx = dragPreviewGearType;
game.addChild(gear);
gears.push(gear);
}
}
dragStart = null;
dragGearPreview = null;
};
// Update loop: update all gears
game.update = function () {
for (var i = 0; i < gears.length; i++) {
if (gears[i].update) {
gears[i].update();
}
}
// Check win condition: score is greater than or equal to target (with sign)
if (targetSign > 0 && score >= targetValue || targetSign < 0 && score <= -targetValue) {
LK.showYouWin();
}
};
red gear top view tranparent. In-Game asset. 2d. High contrast. No shadows
yellow gear top view tranparent. In-Game asset. 2d. High contrast. No shadows
Green gear top view tranparent. In-Game asset. 2d. High contrast. No shadows
Mavi renk deniz manzarası. In-Game asset. 2d. High contrast. No shadows
Just a finger top view. In-Game asset. 2d. High contrast. No shadows