User prompt
make the woods look more realistic make a sound when the wood explodes use a better effect for the sound of the wood breaking
User prompt
The timer bar should look better, have a hit animation, have an explosion animation after the tree explodes. The character should be more realistic, look like an anime and be an old kunfu master, have long moustache, long eyebrows and a long single beard, all white, look like a funny kunfu master
User prompt
He doesn't look like Jackie Chan and the character should be more realistic and focused on woods!!!!
User prompt
Make the yellow box a karate master and it will resemble Jackie Chan
User prompt
Let the clouds be more realistic, let them be all over the screen, slightly transparent and make it look like we are in the sky without affecting the gameplay.
User prompt
Let the clouds be more vivid and realistic, let them move slowly, and let the snowflakes pass from everywhere, not from a certain area.
User prompt
Let it snow lightly and the game takes place in the sky among the clouds.
User prompt
Let it snow lightly
Code edit (1 edits merged)
Please save this source code
User prompt
Timber Chop Frenzy
Initial prompt
Create an arcade-style reflex game similar to Timberman. The player controls a lumberjack standing next to a tall tree. The goal is to chop the tree by tapping. Each tap breaks one section of the tree and gives 1 point. Tree branches randomly appear on either the left or right side of the trunk. The player must switch sides (left/right) to avoid the branches. If the player is on the same side as a branch when they chop, the game ends. Controls: - Tap to chop the tree - Swipe left or right to switch sides The tree is made of multiple repeating segments stacked vertically. When a segment is chopped, a new one appears on top. Add a timer: the player must keep chopping quickly to stay alive. If the player stops or is too slow, the game ends.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // (Cloud class removed for space setting) // Dark Wader (Darth Vader inspired) character for space setting var Lumberjack = Container.expand(function () { var self = Container.call(this); // Side: 'left' or 'right' self.side = 'left'; // --- Construction --- // Boots (black) var bootL = self.attachAsset('karate_leg', { anchorX: 0.5, anchorY: 0.1, x: -18, y: 0, scaleX: 1.1, scaleY: 1.1, tint: 0x222222 }); var bootR = self.attachAsset('karate_leg', { anchorX: 0.5, anchorY: 0.1, x: 18, y: 0, scaleX: 1.1, scaleY: 1.1, tint: 0x222222 }); // Legs (dark gray) var pants = self.attachAsset('karate_leg', { anchorX: 0.5, anchorY: 1, x: 0, y: -10, scaleX: 1.3, scaleY: 1.2, tint: 0x444444 }); // Body armor (dark, Vader chest) var armor = self.attachAsset('karate_body', { anchorX: 0.5, anchorY: 1, x: 0, y: -60, scaleX: 1.2, scaleY: 1.15, tint: 0x222222 }); // Chest panel (Vader's control panel, colored) var chestPanel = self.attachAsset('karate_belt', { anchorX: 0.5, anchorY: 0, x: 0, y: -110, scaleX: 0.7, scaleY: 0.5, tint: 0x00ffcc }); // Add a red button var chestButton = self.attachAsset('karate_belt', { anchorX: 0.5, anchorY: 0, x: 10, y: -108, scaleX: 0.18, scaleY: 0.18, tint: 0xff2222 }); // Head (helmet, black) var helmet = self.attachAsset('karate_head', { anchorX: 0.5, anchorY: 1, x: 0, y: -160, scaleX: 1.15, scaleY: 1.15, tint: 0x222222 }); // Helmet dome (shine) var helmetShine = self.attachAsset('karate_hair', { anchorX: 0.5, anchorY: 1, x: -10, y: -180, scaleX: 0.5, scaleY: 0.7, tint: 0x888888 }); helmetShine.alpha = 0.25; // Face mask (gray, covers lower face) var mask = self.attachAsset('karate_belt', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -150, scaleX: 0.7, scaleY: 0.25, tint: 0x888888 }); // Eyes (red, menacing) var eyeL = self.attachAsset('karate_belt', { anchorX: 0.5, anchorY: 0.5, x: -10, y: -165, scaleX: 0.18, scaleY: 0.18, tint: 0xff2222 }); var eyeR = self.attachAsset('karate_belt', { anchorX: 0.5, anchorY: 0.5, x: 10, y: -165, scaleX: 0.18, scaleY: 0.18, tint: 0xff2222 }); // Cape (dark, behind body) var cape = self.attachAsset('karate_body', { anchorX: 0.5, anchorY: 1, x: 0, y: -60, scaleX: 1.5, scaleY: 1.5, tint: 0x111122 }); cape.alpha = 0.5; // Move cape to back self.setChildIndex(cape, 0); // Left Arm (sleeve, hand) var larm = self.attachAsset('karate_arm', { anchorX: 0.5, anchorY: 0.1, x: -38, y: -90, rotation: -0.25, scaleX: 1.1, scaleY: 1.1, tint: 0x222222 }); // Right Arm (sleeve, hand) var rarm = self.attachAsset('karate_arm', { anchorX: 0.5, anchorY: 0.1, x: 38, y: -90, rotation: 0.25, scaleX: 1.1, scaleY: 1.1, tint: 0x222222 }); // Lightsaber (replaces fist/axe, left hand) var saberBlade = self.attachAsset('katanaBlade', { anchorX: 0.1, anchorY: 0.5, x: -55, y: -100, scaleX: 1.1, scaleY: 0.5, tint: 0x00ffcc }); var saberGlow = self.attachAsset('katanaEdge', { anchorX: 0.1, anchorY: 0.5, x: -55, y: -100, scaleX: 1.1, scaleY: 0.7, tint: 0x00ffff }); saberGlow.alpha = 0.5; // Set side ('left' or 'right') self.setSide = function (side) { self.side = side; // Use armor width for offset var charWidth = armor.width; if (side === 'left') { self.x = treeX - trunkWidth / 2 - charWidth / 2 - 40; self.scaleX = 1; } else { self.x = treeX + trunkWidth / 2 + charWidth / 2 + 40; self.scaleX = -1; } }; // Chop animation: swing saber! self.chop = function () { // Animate a quick scale for feedback tween(self, { scaleY: 0.92 }, { duration: 60, easing: tween.cubicOut, onFinish: function onFinish() { tween(self, { scaleY: 1 }, { duration: 80, easing: tween.cubicIn }); } }); // Saber/arm swing animation (right arm if facing right, left arm if facing left) var arm = self.side === 'left' ? larm : rarm; var origRot = arm.rotation; tween(arm, { rotation: origRot + (self.side === 'left' ? -1.0 : 1.0) }, { duration: 60, easing: tween.cubicOut, onFinish: function onFinish() { tween(arm, { rotation: origRot }, { duration: 80, easing: tween.cubicIn }); } }); }; return self; }); // ShootingStar: A single shooting star streaking across the sky var ShootingStar = Container.expand(function () { var self = Container.call(this); // Use a snowflake as the star head var star = self.attachAsset('snowflake', { anchorX: 0.5, anchorY: 0.5 }); // Randomize size and speed var scale = 0.7 + Math.random() * 0.7; star.scaleX = scale * (1.2 + Math.random() * 0.6); star.scaleY = scale * (0.7 + Math.random() * 0.3); star.alpha = 0.95; // Color: white or pale blue star.tint = Math.random() < 0.5 ? 0xffffff : 0x99ccff; // Shooting direction: mostly left-to-right, sometimes right-to-left var leftToRight = Math.random() < 0.7; var angle = Math.PI / 4 + Math.random() * Math.PI / 6 * (leftToRight ? 1 : -1); var speed = 18 + Math.random() * 10; self.speedX = Math.cos(angle) * speed * (leftToRight ? 1 : -1); self.speedY = Math.sin(angle) * speed; // Start position: random at top or upper left/right if (leftToRight) { self.x = -80 - Math.random() * 100; self.y = 100 + Math.random() * 800; } else { self.x = 2048 + 80 + Math.random() * 100; self.y = 100 + Math.random() * 800; } // Add a faint trail (multiple faded ellipses) self.trail = []; for (var i = 0; i < 6; i++) { var t = self.attachAsset('snowflake', { anchorX: 0.5, anchorY: 0.5, x: -self.speedX * 0.08 * i, y: -self.speedY * 0.08 * i, scaleX: star.scaleX * (0.7 - i * 0.09), scaleY: star.scaleY * (0.7 - i * 0.09), tint: star.tint }); t.alpha = 0.18 - i * 0.025; self.trail.push(t); } // Update method for shooting self.update = function () { self.x += self.speedX; self.y += self.speedY; // Fade out as it moves star.alpha *= 0.985; for (var i = 0; i < self.trail.length; i++) { self.trail[i].alpha *= 0.985; } // Remove if out of screen if (self.x < -120 || self.x > 2048 + 120 || self.y > 2732 + 120) { self.toRemove = true; } }; return self; }); // TreeSegment: A single segment of the space pillar, may have a katana (left/right/none) var TreeSegment = Container.expand(function () { var self = Container.call(this); // Default: no branch self.branch = 'none'; // 'left', 'right', 'none' // Space pillar trunk (futuristic, glowing, layered) var trunk = self.attachAsset('spacePillar', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.18 + Math.random() * 0.08, scaleY: 1.08 + Math.random() * 0.06 }); // Add glowing blue highlight for (var hi = 0; hi < 2 + Math.floor(Math.random() * 2); hi++) { var trunkGlow = self.attachAsset('spacePillarGlow', { anchorX: 0.5, anchorY: 0.5, x: -trunk.width * 0.18 + Math.random() * trunk.width * 0.36, y: -trunk.height * 0.18 + Math.random() * trunk.height * 0.36, scaleX: 0.5 + Math.random() * 0.25, scaleY: 0.12 + Math.random() * 0.08, tint: 0x44aaff, alpha: 0.13 + Math.random() * 0.09 }); } // Add white highlight for shine for (var hi2 = 0; hi2 < 1; hi2++) { var trunkHighlight = self.attachAsset('spacePillarHighlight', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -trunk.height * 0.18 + Math.random() * trunk.height * 0.36, scaleX: 0.22 + Math.random() * 0.18, scaleY: 0.10 + Math.random() * 0.08, tint: 0xffffff, alpha: 0.10 + Math.random() * 0.09 }); } // Branch (if any) self.branchNode = null; // Set up the segment (branchSide: 'left', 'right', or 'none') self.setup = function (branchSide) { self.branch = branchSide; if (self.branchNode) { self.removeChild(self.branchNode); self.branchNode = null; } if (branchSide === 'left') { var group = new Container(); var blade = group.attachAsset('katanaBlade', { anchorX: 1, anchorY: 0.5, x: -trunk.width / 2, y: 0, scaleX: 1.12 + Math.random() * 0.13, scaleY: 1.0 + Math.random() * 0.10, tint: 0xc0c0c0 }); var edge = group.attachAsset('katanaEdge', { anchorX: 1, anchorY: 0.5, x: -trunk.width / 2, y: 0, scaleX: 1.12 + Math.random() * 0.13, scaleY: 0.35 + Math.random() * 0.10, tint: 0xffffff }); var guard = group.attachAsset('katanaGuard', { anchorX: 1, anchorY: 0.5, x: -trunk.width / 2 - blade.width + 18, y: 0, scaleX: 0.7 + Math.random() * 0.2, scaleY: 0.7 + Math.random() * 0.2, tint: 0xffd700 }); var handle = group.attachAsset('katanaHandle', { anchorX: 1, anchorY: 0.5, x: -trunk.width / 2 - blade.width - 10, y: 0, scaleX: 1.0, scaleY: 1.0, tint: 0x6b4f1d }); self.branchNode = group; self.addChild(group); } else if (branchSide === 'right') { var group = new Container(); var blade = group.attachAsset('katanaBlade', { anchorX: 0, anchorY: 0.5, x: trunk.width / 2, y: 0, scaleX: 1.12 + Math.random() * 0.13, scaleY: 1.0 + Math.random() * 0.10, tint: 0xc0c0c0, flipX: 1 }); var edge = group.attachAsset('katanaEdge', { anchorX: 0, anchorY: 0.5, x: trunk.width / 2, y: 0, scaleX: 1.12 + Math.random() * 0.13, scaleY: 0.35 + Math.random() * 0.10, tint: 0xffffff, flipX: 1 }); var guard = group.attachAsset('katanaGuard', { anchorX: 0, anchorY: 0.5, x: trunk.width / 2 + blade.width - 18, y: 0, scaleX: 0.7 + Math.random() * 0.2, scaleY: 0.7 + Math.random() * 0.2, tint: 0xffd700, flipX: 1 }); var handle = group.attachAsset('katanaHandle', { anchorX: 0, anchorY: 0.5, x: trunk.width / 2 + blade.width + 10, y: 0, scaleX: 1.0, scaleY: 1.0, tint: 0x6b4f1d, flipX: 1 }); self.branchNode = group; self.addChild(group); } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x07081a // Deep space blue-black }); /**** * Game Code ****/ // Star Wars lightsaber battle sounds (multiple for variety) // Fail sound // Chop sound // Lumberjack // Branch // Tree trunk segment // --- Asset Initialization (auto by LK engine) --- // --- Game Variables --- // Space setting: no clouds, just stars and shooting stars! var treeSegments = []; // Array of TreeSegment var treeX = 2048 / 2; var treeBaseY = 2732 - 350; // Y position of the bottom segment var trunkWidth = 210; var trunkHeight = 120; var visibleSegments = 13; // Number of segments visible on screen var branchChance = 0.35; // Probability of a branch per segment var lastBranch = 'none'; // To avoid impossible patterns var player = null; var scoreTxt = null; var timerBar = null; var timeLeft = 2000; // ms, time before game over var maxTime = 2000; // ms, resets to this on chop var timerInterval = null; var isGameOver = false; // --- Shooting Star System --- var snowflakes = []; // Now used for shooting stars var snowSpawnTimer = 0; var snowSpawnInterval = 18 + Math.floor(Math.random() * 12); // randomize interval for more natural effect // --- UI Setup --- scoreTxt = new Text2('0', { size: 120, fill: "#fff" }); scoreTxt.anchor.set(0.5, 0); // Place the score text at the very top, centered horizontally scoreTxt.x = 0; scoreTxt.y = 0; LK.gui.top.addChild(scoreTxt); // Timer bar with improved visuals, placed just below the score text timerBar = LK.getAsset('timerBar', { width: 800, height: 38, color: 0x00b050, shape: 'box', anchorX: 0.5, anchorY: 0, x: 0, y: scoreTxt.y + scoreTxt.height + 10 // just below score text with a small gap }); timerBar.alpha = 0.93; timerBar.border = LK.getAsset('timerBar', { width: 820, height: 48, color: 0x222222, shape: 'box', anchorX: 0.5, anchorY: 0, x: 0, y: timerBar.y // match timerBar y }); timerBar.border.alpha = 0.25; LK.gui.top.addChild(timerBar.border); LK.gui.top.addChild(timerBar); // Timer bar hit animation function timerBarHitAnim() { tween(timerBar, { scaleY: 1.25 }, { duration: 60, easing: tween.cubicOut, onFinish: function onFinish() { tween(timerBar, { scaleY: 1 }, { duration: 100, easing: tween.cubicIn }); } }); tween(timerBar, { alpha: 1 }, { duration: 60, onFinish: function onFinish() { tween(timerBar, { alpha: 0.93 }, { duration: 100 }); } }); } // Timer bar explosion animation function timerBarExplodeAnim() { tween(timerBar, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 350, easing: tween.cubicOut }); tween(timerBar.border, { alpha: 0 }, { duration: 350 }); } // --- Tree Setup --- function randomBranch(prevBranch) { // Avoid two branches on the same side in a row var r = Math.random(); if (r < branchChance) { if (prevBranch === 'left') return 'right'; if (prevBranch === 'right') return 'left'; return Math.random() < 0.5 ? 'left' : 'right'; } return 'none'; } function createInitialTree() { var prev = 'none'; for (var i = 0; i < visibleSegments + 2; i++) { var seg = new TreeSegment(); var branch = randomBranch(prev); seg.setup(branch); seg.x = treeX; seg.y = treeBaseY - i * trunkHeight; treeSegments.push(seg); game.addChild(seg); prev = branch; } } function shiftTree() { // Remove bottom segment var bottom = treeSegments.shift(); bottom.destroy(); // Move all segments down for (var i = 0; i < treeSegments.length; i++) { tween(treeSegments[i], { y: treeBaseY - i * trunkHeight }, { duration: 80, easing: tween.cubicOut }); } // Add new segment at the top var prevBranch = treeSegments[treeSegments.length - 1].branch; var newSeg = new TreeSegment(); var branch = randomBranch(prevBranch); newSeg.setup(branch); newSeg.x = treeX; newSeg.y = treeSegments[treeSegments.length - 1].y - trunkHeight; treeSegments.push(newSeg); game.addChild(newSeg); } // --- Player Setup --- player = new Lumberjack(); player.setSide('left'); player.y = treeBaseY + trunkHeight / 2 + 10; game.addChild(player); // --- Game State --- var score = 0; // --- Timer --- function resetTimer() { timeLeft = maxTime; updateTimerBar(); } function updateTimerBar() { var pct = Math.max(0, timeLeft / maxTime); timerBar.width = 800 * pct; if (pct > 0.5) { timerBar.tint = 0x00b050; } else if (pct > 0.2) { timerBar.tint = 0xffc300; } else { timerBar.tint = 0xff0000; } } // --- Game Over --- function triggerGameOver() { if (isGameOver) return; isGameOver = true; timerBarExplodeAnim(); LK.effects.flashScreen(0xff0000, 800); // Play a better wood explosion sound and effect LK.getSound('fail').play(); // Play Darth Vader shouting Obi Wan (using the fail sound asset as placeholder for the shout) LK.getSound('fail', { start: 4.0, end: 6.0 }).play(); LK.effects.flashObject(treeSegments[0], 0xffe066, 400); // flash the bottom segment yellow for a wood burst tween(treeSegments[0], { scaleX: 2.2, scaleY: 2.2, alpha: 0 }, { duration: 420, easing: tween.cubicOut }); LK.showGameOver(); } // --- Chop Action --- function chop() { if (isGameOver) return; // Check for collision with branch on current side at the bottom segment var bottomSeg = treeSegments[0]; if (bottomSeg.branch === player.side) { triggerGameOver(); return; } // Chop: remove bottom, shift tree, add new at top shiftTree(); // Animate player player.chop(); timerBarHitAnim(); // Play sound LK.getSound('chop').play(); // Score score += 1; LK.setScore(score); scoreTxt.setText(score); // Reset timer, but make it a bit shorter as score increases maxTime = Math.max(800, 2000 - score * 10); resetTimer(); } // --- Input Handling --- var touchStartX = null; var touchStartY = null; var dragThreshold = 40; // px function handleDown(x, y, obj) { touchStartX = x; touchStartY = y; } function handleUp(x, y, obj) { if (isGameOver) return; if (touchStartX === null || touchStartY === null) return; var dx = x - touchStartX; var dy = y - touchStartY; // Swipe detection if (Math.abs(dx) > dragThreshold && Math.abs(dx) > Math.abs(dy)) { // Horizontal swipe if (dx > 0 && player.side === 'left') { player.setSide('right'); } else if (dx < 0 && player.side === 'right') { player.setSide('left'); } } else if (Math.abs(dx) < dragThreshold && Math.abs(dy) < dragThreshold) { // Tap: chop chop(); } touchStartX = null; touchStartY = null; } game.down = handleDown; game.up = handleUp; // --- Main Update Loop --- // --- Darth Vader Speech System --- var vaderSpeechInterval = 4200; // ms between speeches var vaderSpeechTimer = 0; var vaderSpeechActive = false; var vaderSpeechLines = [{ start: 0.0, end: 2.0 }, // "You don't know the power of the dark side" { start: 2.1, end: 4.0 }, // "I am your father" { start: 4.1, end: 6.0 }, // "Obi-Wan has taught you well" { start: 6.1, end: 8.0 }, // "Impressive. Most impressive." { start: 8.1, end: 10.0 } // "You are unwise to lower your defenses" ]; // Use the 'fail' sound asset as placeholder for Vader speech (replace with real asset id when available) var vaderSpeechAssetId = 'fail'; // --- Darth Vader Random Sound System --- var vaderRandomSoundInterval = 8000; // 8 seconds in ms var vaderRandomSoundTimer = 0; var vaderRandomSoundList = ['darthvadersound1', 'darthvadersound2', 'darthvadersound3', 'darthvadersound5']; game.update = function () { // --- Shooting star update --- snowSpawnTimer++; if (snowSpawnTimer >= snowSpawnInterval) { snowSpawnTimer = 0; // Only a few shooting stars at a time if (snowflakes.length < 7) { var star = new ShootingStar(); snowflakes.push(star); game.addChild(star); } // Randomize next interval for more natural effect snowSpawnInterval = 18 + Math.floor(Math.random() * 12); } // Update and remove shooting stars for (var i = snowflakes.length - 1; i >= 0; i--) { var star = snowflakes[i]; if (star.update) star.update(); if (star.toRemove) { star.destroy(); snowflakes.splice(i, 1); } } // Darth Vader speech logic if (!isGameOver) { vaderSpeechTimer += 1000 / 60; if (vaderSpeechTimer >= vaderSpeechInterval && !vaderSpeechActive) { vaderSpeechTimer = 0; vaderSpeechActive = true; // Pick a random speech line var idx = Math.floor(Math.random() * vaderSpeechLines.length); var line = vaderSpeechLines[idx]; // Play the speech line (using fail asset as placeholder) LK.getSound(vaderSpeechAssetId, { start: line.start, end: line.end }).play(); // Prevent overlap: allow new speech after a short delay LK.setTimeout(function () { vaderSpeechActive = false; }, (line.end - line.start) * 1000 + 400); } // --- Darth Vader random sound logic --- vaderRandomSoundTimer += 1000 / 60; if (vaderRandomSoundTimer >= vaderRandomSoundInterval) { vaderRandomSoundTimer = 0; // 88% chance to play Obi Wan (fail sound), 12% chance to play a random Vader sound if (Math.random() < 0.88) { // Play Obi Wan (fail sound) from 4.0s to 6.0s LK.getSound('fail', { start: 4.0, end: 6.0 }).play(); } else { // Pick a random sound from the list var soundIdx = Math.floor(Math.random() * vaderRandomSoundList.length); var soundId = vaderRandomSoundList[soundIdx]; LK.getSound(soundId).play(); } } } if (isGameOver) return; // Timer timeLeft -= 1000 / 60; updateTimerBar(); if (timeLeft <= 0) { triggerGameOver(); } }; // --- Game Initialization --- function resetGame() { // Remove old tree for (var i = 0; i < treeSegments.length; i++) { treeSegments[i].destroy(); } treeSegments = []; // Remove old snowflakes for (var i = 0; i < snowflakes.length; i++) { snowflakes[i].destroy(); } snowflakes = []; // Reset variables score = 0; LK.setScore(0); scoreTxt.setText('0'); isGameOver = false; maxTime = 2000; timerBar.scaleX = 1; timerBar.scaleY = 1; timerBar.alpha = 0.93; if (timerBar.border) timerBar.border.alpha = 0.25; resetTimer(); // Recreate tree createInitialTree(); // Reset player player.setSide('left'); player.y = treeBaseY + trunkHeight / 2 + 10; } // Reset Vader speech system vaderSpeechTimer = 0; vaderSpeechActive = false; // Reset random Vader sound timer vaderRandomSoundTimer = 0; // No league system or leaderboard/login UI. Game starts immediately. resetGame(); scoreTxt.visible = true; timerBar.visible = true; if (timerBar.border) timerBar.border.visible = true;
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// (Cloud class removed for space setting)
// Dark Wader (Darth Vader inspired) character for space setting
var Lumberjack = Container.expand(function () {
var self = Container.call(this);
// Side: 'left' or 'right'
self.side = 'left';
// --- Construction ---
// Boots (black)
var bootL = self.attachAsset('karate_leg', {
anchorX: 0.5,
anchorY: 0.1,
x: -18,
y: 0,
scaleX: 1.1,
scaleY: 1.1,
tint: 0x222222
});
var bootR = self.attachAsset('karate_leg', {
anchorX: 0.5,
anchorY: 0.1,
x: 18,
y: 0,
scaleX: 1.1,
scaleY: 1.1,
tint: 0x222222
});
// Legs (dark gray)
var pants = self.attachAsset('karate_leg', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: -10,
scaleX: 1.3,
scaleY: 1.2,
tint: 0x444444
});
// Body armor (dark, Vader chest)
var armor = self.attachAsset('karate_body', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: -60,
scaleX: 1.2,
scaleY: 1.15,
tint: 0x222222
});
// Chest panel (Vader's control panel, colored)
var chestPanel = self.attachAsset('karate_belt', {
anchorX: 0.5,
anchorY: 0,
x: 0,
y: -110,
scaleX: 0.7,
scaleY: 0.5,
tint: 0x00ffcc
});
// Add a red button
var chestButton = self.attachAsset('karate_belt', {
anchorX: 0.5,
anchorY: 0,
x: 10,
y: -108,
scaleX: 0.18,
scaleY: 0.18,
tint: 0xff2222
});
// Head (helmet, black)
var helmet = self.attachAsset('karate_head', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: -160,
scaleX: 1.15,
scaleY: 1.15,
tint: 0x222222
});
// Helmet dome (shine)
var helmetShine = self.attachAsset('karate_hair', {
anchorX: 0.5,
anchorY: 1,
x: -10,
y: -180,
scaleX: 0.5,
scaleY: 0.7,
tint: 0x888888
});
helmetShine.alpha = 0.25;
// Face mask (gray, covers lower face)
var mask = self.attachAsset('karate_belt', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -150,
scaleX: 0.7,
scaleY: 0.25,
tint: 0x888888
});
// Eyes (red, menacing)
var eyeL = self.attachAsset('karate_belt', {
anchorX: 0.5,
anchorY: 0.5,
x: -10,
y: -165,
scaleX: 0.18,
scaleY: 0.18,
tint: 0xff2222
});
var eyeR = self.attachAsset('karate_belt', {
anchorX: 0.5,
anchorY: 0.5,
x: 10,
y: -165,
scaleX: 0.18,
scaleY: 0.18,
tint: 0xff2222
});
// Cape (dark, behind body)
var cape = self.attachAsset('karate_body', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: -60,
scaleX: 1.5,
scaleY: 1.5,
tint: 0x111122
});
cape.alpha = 0.5;
// Move cape to back
self.setChildIndex(cape, 0);
// Left Arm (sleeve, hand)
var larm = self.attachAsset('karate_arm', {
anchorX: 0.5,
anchorY: 0.1,
x: -38,
y: -90,
rotation: -0.25,
scaleX: 1.1,
scaleY: 1.1,
tint: 0x222222
});
// Right Arm (sleeve, hand)
var rarm = self.attachAsset('karate_arm', {
anchorX: 0.5,
anchorY: 0.1,
x: 38,
y: -90,
rotation: 0.25,
scaleX: 1.1,
scaleY: 1.1,
tint: 0x222222
});
// Lightsaber (replaces fist/axe, left hand)
var saberBlade = self.attachAsset('katanaBlade', {
anchorX: 0.1,
anchorY: 0.5,
x: -55,
y: -100,
scaleX: 1.1,
scaleY: 0.5,
tint: 0x00ffcc
});
var saberGlow = self.attachAsset('katanaEdge', {
anchorX: 0.1,
anchorY: 0.5,
x: -55,
y: -100,
scaleX: 1.1,
scaleY: 0.7,
tint: 0x00ffff
});
saberGlow.alpha = 0.5;
// Set side ('left' or 'right')
self.setSide = function (side) {
self.side = side;
// Use armor width for offset
var charWidth = armor.width;
if (side === 'left') {
self.x = treeX - trunkWidth / 2 - charWidth / 2 - 40;
self.scaleX = 1;
} else {
self.x = treeX + trunkWidth / 2 + charWidth / 2 + 40;
self.scaleX = -1;
}
};
// Chop animation: swing saber!
self.chop = function () {
// Animate a quick scale for feedback
tween(self, {
scaleY: 0.92
}, {
duration: 60,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(self, {
scaleY: 1
}, {
duration: 80,
easing: tween.cubicIn
});
}
});
// Saber/arm swing animation (right arm if facing right, left arm if facing left)
var arm = self.side === 'left' ? larm : rarm;
var origRot = arm.rotation;
tween(arm, {
rotation: origRot + (self.side === 'left' ? -1.0 : 1.0)
}, {
duration: 60,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(arm, {
rotation: origRot
}, {
duration: 80,
easing: tween.cubicIn
});
}
});
};
return self;
});
// ShootingStar: A single shooting star streaking across the sky
var ShootingStar = Container.expand(function () {
var self = Container.call(this);
// Use a snowflake as the star head
var star = self.attachAsset('snowflake', {
anchorX: 0.5,
anchorY: 0.5
});
// Randomize size and speed
var scale = 0.7 + Math.random() * 0.7;
star.scaleX = scale * (1.2 + Math.random() * 0.6);
star.scaleY = scale * (0.7 + Math.random() * 0.3);
star.alpha = 0.95;
// Color: white or pale blue
star.tint = Math.random() < 0.5 ? 0xffffff : 0x99ccff;
// Shooting direction: mostly left-to-right, sometimes right-to-left
var leftToRight = Math.random() < 0.7;
var angle = Math.PI / 4 + Math.random() * Math.PI / 6 * (leftToRight ? 1 : -1);
var speed = 18 + Math.random() * 10;
self.speedX = Math.cos(angle) * speed * (leftToRight ? 1 : -1);
self.speedY = Math.sin(angle) * speed;
// Start position: random at top or upper left/right
if (leftToRight) {
self.x = -80 - Math.random() * 100;
self.y = 100 + Math.random() * 800;
} else {
self.x = 2048 + 80 + Math.random() * 100;
self.y = 100 + Math.random() * 800;
}
// Add a faint trail (multiple faded ellipses)
self.trail = [];
for (var i = 0; i < 6; i++) {
var t = self.attachAsset('snowflake', {
anchorX: 0.5,
anchorY: 0.5,
x: -self.speedX * 0.08 * i,
y: -self.speedY * 0.08 * i,
scaleX: star.scaleX * (0.7 - i * 0.09),
scaleY: star.scaleY * (0.7 - i * 0.09),
tint: star.tint
});
t.alpha = 0.18 - i * 0.025;
self.trail.push(t);
}
// Update method for shooting
self.update = function () {
self.x += self.speedX;
self.y += self.speedY;
// Fade out as it moves
star.alpha *= 0.985;
for (var i = 0; i < self.trail.length; i++) {
self.trail[i].alpha *= 0.985;
}
// Remove if out of screen
if (self.x < -120 || self.x > 2048 + 120 || self.y > 2732 + 120) {
self.toRemove = true;
}
};
return self;
});
// TreeSegment: A single segment of the space pillar, may have a katana (left/right/none)
var TreeSegment = Container.expand(function () {
var self = Container.call(this);
// Default: no branch
self.branch = 'none'; // 'left', 'right', 'none'
// Space pillar trunk (futuristic, glowing, layered)
var trunk = self.attachAsset('spacePillar', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.18 + Math.random() * 0.08,
scaleY: 1.08 + Math.random() * 0.06
});
// Add glowing blue highlight
for (var hi = 0; hi < 2 + Math.floor(Math.random() * 2); hi++) {
var trunkGlow = self.attachAsset('spacePillarGlow', {
anchorX: 0.5,
anchorY: 0.5,
x: -trunk.width * 0.18 + Math.random() * trunk.width * 0.36,
y: -trunk.height * 0.18 + Math.random() * trunk.height * 0.36,
scaleX: 0.5 + Math.random() * 0.25,
scaleY: 0.12 + Math.random() * 0.08,
tint: 0x44aaff,
alpha: 0.13 + Math.random() * 0.09
});
}
// Add white highlight for shine
for (var hi2 = 0; hi2 < 1; hi2++) {
var trunkHighlight = self.attachAsset('spacePillarHighlight', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: -trunk.height * 0.18 + Math.random() * trunk.height * 0.36,
scaleX: 0.22 + Math.random() * 0.18,
scaleY: 0.10 + Math.random() * 0.08,
tint: 0xffffff,
alpha: 0.10 + Math.random() * 0.09
});
}
// Branch (if any)
self.branchNode = null;
// Set up the segment (branchSide: 'left', 'right', or 'none')
self.setup = function (branchSide) {
self.branch = branchSide;
if (self.branchNode) {
self.removeChild(self.branchNode);
self.branchNode = null;
}
if (branchSide === 'left') {
var group = new Container();
var blade = group.attachAsset('katanaBlade', {
anchorX: 1,
anchorY: 0.5,
x: -trunk.width / 2,
y: 0,
scaleX: 1.12 + Math.random() * 0.13,
scaleY: 1.0 + Math.random() * 0.10,
tint: 0xc0c0c0
});
var edge = group.attachAsset('katanaEdge', {
anchorX: 1,
anchorY: 0.5,
x: -trunk.width / 2,
y: 0,
scaleX: 1.12 + Math.random() * 0.13,
scaleY: 0.35 + Math.random() * 0.10,
tint: 0xffffff
});
var guard = group.attachAsset('katanaGuard', {
anchorX: 1,
anchorY: 0.5,
x: -trunk.width / 2 - blade.width + 18,
y: 0,
scaleX: 0.7 + Math.random() * 0.2,
scaleY: 0.7 + Math.random() * 0.2,
tint: 0xffd700
});
var handle = group.attachAsset('katanaHandle', {
anchorX: 1,
anchorY: 0.5,
x: -trunk.width / 2 - blade.width - 10,
y: 0,
scaleX: 1.0,
scaleY: 1.0,
tint: 0x6b4f1d
});
self.branchNode = group;
self.addChild(group);
} else if (branchSide === 'right') {
var group = new Container();
var blade = group.attachAsset('katanaBlade', {
anchorX: 0,
anchorY: 0.5,
x: trunk.width / 2,
y: 0,
scaleX: 1.12 + Math.random() * 0.13,
scaleY: 1.0 + Math.random() * 0.10,
tint: 0xc0c0c0,
flipX: 1
});
var edge = group.attachAsset('katanaEdge', {
anchorX: 0,
anchorY: 0.5,
x: trunk.width / 2,
y: 0,
scaleX: 1.12 + Math.random() * 0.13,
scaleY: 0.35 + Math.random() * 0.10,
tint: 0xffffff,
flipX: 1
});
var guard = group.attachAsset('katanaGuard', {
anchorX: 0,
anchorY: 0.5,
x: trunk.width / 2 + blade.width - 18,
y: 0,
scaleX: 0.7 + Math.random() * 0.2,
scaleY: 0.7 + Math.random() * 0.2,
tint: 0xffd700,
flipX: 1
});
var handle = group.attachAsset('katanaHandle', {
anchorX: 0,
anchorY: 0.5,
x: trunk.width / 2 + blade.width + 10,
y: 0,
scaleX: 1.0,
scaleY: 1.0,
tint: 0x6b4f1d,
flipX: 1
});
self.branchNode = group;
self.addChild(group);
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x07081a // Deep space blue-black
});
/****
* Game Code
****/
// Star Wars lightsaber battle sounds (multiple for variety)
// Fail sound
// Chop sound
// Lumberjack
// Branch
// Tree trunk segment
// --- Asset Initialization (auto by LK engine) ---
// --- Game Variables ---
// Space setting: no clouds, just stars and shooting stars!
var treeSegments = []; // Array of TreeSegment
var treeX = 2048 / 2;
var treeBaseY = 2732 - 350; // Y position of the bottom segment
var trunkWidth = 210;
var trunkHeight = 120;
var visibleSegments = 13; // Number of segments visible on screen
var branchChance = 0.35; // Probability of a branch per segment
var lastBranch = 'none'; // To avoid impossible patterns
var player = null;
var scoreTxt = null;
var timerBar = null;
var timeLeft = 2000; // ms, time before game over
var maxTime = 2000; // ms, resets to this on chop
var timerInterval = null;
var isGameOver = false;
// --- Shooting Star System ---
var snowflakes = []; // Now used for shooting stars
var snowSpawnTimer = 0;
var snowSpawnInterval = 18 + Math.floor(Math.random() * 12); // randomize interval for more natural effect
// --- UI Setup ---
scoreTxt = new Text2('0', {
size: 120,
fill: "#fff"
});
scoreTxt.anchor.set(0.5, 0);
// Place the score text at the very top, centered horizontally
scoreTxt.x = 0;
scoreTxt.y = 0;
LK.gui.top.addChild(scoreTxt);
// Timer bar with improved visuals, placed just below the score text
timerBar = LK.getAsset('timerBar', {
width: 800,
height: 38,
color: 0x00b050,
shape: 'box',
anchorX: 0.5,
anchorY: 0,
x: 0,
y: scoreTxt.y + scoreTxt.height + 10 // just below score text with a small gap
});
timerBar.alpha = 0.93;
timerBar.border = LK.getAsset('timerBar', {
width: 820,
height: 48,
color: 0x222222,
shape: 'box',
anchorX: 0.5,
anchorY: 0,
x: 0,
y: timerBar.y // match timerBar y
});
timerBar.border.alpha = 0.25;
LK.gui.top.addChild(timerBar.border);
LK.gui.top.addChild(timerBar);
// Timer bar hit animation
function timerBarHitAnim() {
tween(timerBar, {
scaleY: 1.25
}, {
duration: 60,
easing: tween.cubicOut,
onFinish: function onFinish() {
tween(timerBar, {
scaleY: 1
}, {
duration: 100,
easing: tween.cubicIn
});
}
});
tween(timerBar, {
alpha: 1
}, {
duration: 60,
onFinish: function onFinish() {
tween(timerBar, {
alpha: 0.93
}, {
duration: 100
});
}
});
}
// Timer bar explosion animation
function timerBarExplodeAnim() {
tween(timerBar, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 350,
easing: tween.cubicOut
});
tween(timerBar.border, {
alpha: 0
}, {
duration: 350
});
}
// --- Tree Setup ---
function randomBranch(prevBranch) {
// Avoid two branches on the same side in a row
var r = Math.random();
if (r < branchChance) {
if (prevBranch === 'left') return 'right';
if (prevBranch === 'right') return 'left';
return Math.random() < 0.5 ? 'left' : 'right';
}
return 'none';
}
function createInitialTree() {
var prev = 'none';
for (var i = 0; i < visibleSegments + 2; i++) {
var seg = new TreeSegment();
var branch = randomBranch(prev);
seg.setup(branch);
seg.x = treeX;
seg.y = treeBaseY - i * trunkHeight;
treeSegments.push(seg);
game.addChild(seg);
prev = branch;
}
}
function shiftTree() {
// Remove bottom segment
var bottom = treeSegments.shift();
bottom.destroy();
// Move all segments down
for (var i = 0; i < treeSegments.length; i++) {
tween(treeSegments[i], {
y: treeBaseY - i * trunkHeight
}, {
duration: 80,
easing: tween.cubicOut
});
}
// Add new segment at the top
var prevBranch = treeSegments[treeSegments.length - 1].branch;
var newSeg = new TreeSegment();
var branch = randomBranch(prevBranch);
newSeg.setup(branch);
newSeg.x = treeX;
newSeg.y = treeSegments[treeSegments.length - 1].y - trunkHeight;
treeSegments.push(newSeg);
game.addChild(newSeg);
}
// --- Player Setup ---
player = new Lumberjack();
player.setSide('left');
player.y = treeBaseY + trunkHeight / 2 + 10;
game.addChild(player);
// --- Game State ---
var score = 0;
// --- Timer ---
function resetTimer() {
timeLeft = maxTime;
updateTimerBar();
}
function updateTimerBar() {
var pct = Math.max(0, timeLeft / maxTime);
timerBar.width = 800 * pct;
if (pct > 0.5) {
timerBar.tint = 0x00b050;
} else if (pct > 0.2) {
timerBar.tint = 0xffc300;
} else {
timerBar.tint = 0xff0000;
}
}
// --- Game Over ---
function triggerGameOver() {
if (isGameOver) return;
isGameOver = true;
timerBarExplodeAnim();
LK.effects.flashScreen(0xff0000, 800);
// Play a better wood explosion sound and effect
LK.getSound('fail').play();
// Play Darth Vader shouting Obi Wan (using the fail sound asset as placeholder for the shout)
LK.getSound('fail', {
start: 4.0,
end: 6.0
}).play();
LK.effects.flashObject(treeSegments[0], 0xffe066, 400); // flash the bottom segment yellow for a wood burst
tween(treeSegments[0], {
scaleX: 2.2,
scaleY: 2.2,
alpha: 0
}, {
duration: 420,
easing: tween.cubicOut
});
LK.showGameOver();
}
// --- Chop Action ---
function chop() {
if (isGameOver) return;
// Check for collision with branch on current side at the bottom segment
var bottomSeg = treeSegments[0];
if (bottomSeg.branch === player.side) {
triggerGameOver();
return;
}
// Chop: remove bottom, shift tree, add new at top
shiftTree();
// Animate player
player.chop();
timerBarHitAnim();
// Play sound
LK.getSound('chop').play();
// Score
score += 1;
LK.setScore(score);
scoreTxt.setText(score);
// Reset timer, but make it a bit shorter as score increases
maxTime = Math.max(800, 2000 - score * 10);
resetTimer();
}
// --- Input Handling ---
var touchStartX = null;
var touchStartY = null;
var dragThreshold = 40; // px
function handleDown(x, y, obj) {
touchStartX = x;
touchStartY = y;
}
function handleUp(x, y, obj) {
if (isGameOver) return;
if (touchStartX === null || touchStartY === null) return;
var dx = x - touchStartX;
var dy = y - touchStartY;
// Swipe detection
if (Math.abs(dx) > dragThreshold && Math.abs(dx) > Math.abs(dy)) {
// Horizontal swipe
if (dx > 0 && player.side === 'left') {
player.setSide('right');
} else if (dx < 0 && player.side === 'right') {
player.setSide('left');
}
} else if (Math.abs(dx) < dragThreshold && Math.abs(dy) < dragThreshold) {
// Tap: chop
chop();
}
touchStartX = null;
touchStartY = null;
}
game.down = handleDown;
game.up = handleUp;
// --- Main Update Loop ---
// --- Darth Vader Speech System ---
var vaderSpeechInterval = 4200; // ms between speeches
var vaderSpeechTimer = 0;
var vaderSpeechActive = false;
var vaderSpeechLines = [{
start: 0.0,
end: 2.0
},
// "You don't know the power of the dark side"
{
start: 2.1,
end: 4.0
},
// "I am your father"
{
start: 4.1,
end: 6.0
},
// "Obi-Wan has taught you well"
{
start: 6.1,
end: 8.0
},
// "Impressive. Most impressive."
{
start: 8.1,
end: 10.0
} // "You are unwise to lower your defenses"
];
// Use the 'fail' sound asset as placeholder for Vader speech (replace with real asset id when available)
var vaderSpeechAssetId = 'fail';
// --- Darth Vader Random Sound System ---
var vaderRandomSoundInterval = 8000; // 8 seconds in ms
var vaderRandomSoundTimer = 0;
var vaderRandomSoundList = ['darthvadersound1', 'darthvadersound2', 'darthvadersound3', 'darthvadersound5'];
game.update = function () {
// --- Shooting star update ---
snowSpawnTimer++;
if (snowSpawnTimer >= snowSpawnInterval) {
snowSpawnTimer = 0;
// Only a few shooting stars at a time
if (snowflakes.length < 7) {
var star = new ShootingStar();
snowflakes.push(star);
game.addChild(star);
}
// Randomize next interval for more natural effect
snowSpawnInterval = 18 + Math.floor(Math.random() * 12);
}
// Update and remove shooting stars
for (var i = snowflakes.length - 1; i >= 0; i--) {
var star = snowflakes[i];
if (star.update) star.update();
if (star.toRemove) {
star.destroy();
snowflakes.splice(i, 1);
}
}
// Darth Vader speech logic
if (!isGameOver) {
vaderSpeechTimer += 1000 / 60;
if (vaderSpeechTimer >= vaderSpeechInterval && !vaderSpeechActive) {
vaderSpeechTimer = 0;
vaderSpeechActive = true;
// Pick a random speech line
var idx = Math.floor(Math.random() * vaderSpeechLines.length);
var line = vaderSpeechLines[idx];
// Play the speech line (using fail asset as placeholder)
LK.getSound(vaderSpeechAssetId, {
start: line.start,
end: line.end
}).play();
// Prevent overlap: allow new speech after a short delay
LK.setTimeout(function () {
vaderSpeechActive = false;
}, (line.end - line.start) * 1000 + 400);
}
// --- Darth Vader random sound logic ---
vaderRandomSoundTimer += 1000 / 60;
if (vaderRandomSoundTimer >= vaderRandomSoundInterval) {
vaderRandomSoundTimer = 0;
// 88% chance to play Obi Wan (fail sound), 12% chance to play a random Vader sound
if (Math.random() < 0.88) {
// Play Obi Wan (fail sound) from 4.0s to 6.0s
LK.getSound('fail', {
start: 4.0,
end: 6.0
}).play();
} else {
// Pick a random sound from the list
var soundIdx = Math.floor(Math.random() * vaderRandomSoundList.length);
var soundId = vaderRandomSoundList[soundIdx];
LK.getSound(soundId).play();
}
}
}
if (isGameOver) return;
// Timer
timeLeft -= 1000 / 60;
updateTimerBar();
if (timeLeft <= 0) {
triggerGameOver();
}
};
// --- Game Initialization ---
function resetGame() {
// Remove old tree
for (var i = 0; i < treeSegments.length; i++) {
treeSegments[i].destroy();
}
treeSegments = [];
// Remove old snowflakes
for (var i = 0; i < snowflakes.length; i++) {
snowflakes[i].destroy();
}
snowflakes = [];
// Reset variables
score = 0;
LK.setScore(0);
scoreTxt.setText('0');
isGameOver = false;
maxTime = 2000;
timerBar.scaleX = 1;
timerBar.scaleY = 1;
timerBar.alpha = 0.93;
if (timerBar.border) timerBar.border.alpha = 0.25;
resetTimer();
// Recreate tree
createInitialTree();
// Reset player
player.setSide('left');
player.y = treeBaseY + trunkHeight / 2 + 10;
}
// Reset Vader speech system
vaderSpeechTimer = 0;
vaderSpeechActive = false;
// Reset random Vader sound timer
vaderRandomSoundTimer = 0;
// No league system or leaderboard/login UI. Game starts immediately.
resetGame();
scoreTxt.visible = true;
timerBar.visible = true;
if (timerBar.border) timerBar.border.visible = true;