User prompt
Make it so you have to pay the amount in the shop to get each weapon
User prompt
Make killing him only give you 500 extra points
User prompt
Make the weapons cost more
User prompt
Increase the cost of the weapons
User prompt
Make the ninja headband in the middle of the head
User prompt
Remove the eyepatch from the game
User prompt
Remove the headphones from the game
User prompt
Remove the headphones
User prompt
Make the headphones go behind the head
User prompt
Make the head phones higher
User prompt
Still to high
User prompt
Still not enough
User prompt
Too high
User prompt
Not that much
User prompt
More the eyepatch higher
User prompt
Make the eyepatch a mask instead
User prompt
Make the costumes their own assets
User prompt
Make new assets for costumes
User prompt
Make more costumes
User prompt
Make a different sound for death and respawn
User prompt
Make clicking and attacking make a different sound
User prompt
Make so EVERY respawn he gets a new cosmetic
User prompt
Make the costumes less rare
User prompt
Make it so when the flying weapon hits the stickman the score goes up
User prompt
Don’t give a point for tapping the stickman
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { points: 0, damage: 1, tools: [], level: 1 }); /**** * Classes ****/ // Cosmetic item class var Cosmetic = Container.expand(function (type, itemId) { var self = Container.call(this); self.itemType = type; // "hat" or "accessory" self.itemId = itemId; // Create the visual element var graphic = self.attachAsset(itemId, { anchorX: 0.5, anchorY: 0.5 }); // We'll set positions dynamically in the update method // based on the stickman's head position // Add wobble animation self.update = function () { // Find the stickman's head position (assuming the parent of this cosmetic is the stickman) var stickmanParent = self.parent; if (stickmanParent) { // Find the head among the stickman's children var head = null; for (var i = 0; i < stickmanParent.children.length; i++) { var child = stickmanParent.children[i]; // Check if this is the head (based on its properties) if (child.originalY === -225) { head = child; break; } } // If we found the head, follow its position and rotation if (head) { // Position cosmetics relative to head's center point var offsetX = 0; // Adjust Y offset - move sunglasses higher up on the head var offsetY = self.itemType === "hat" ? -50 : self.itemId === "sunglasses" ? -10 : 20; // Higher position for sunglasses // Set position and rotation from the head's center self.x = head.x; self.y = head.y; // Set rotation to match head self.rotation = head.rotation; // Apply the offset after rotation to make it rotate around the head if (offsetY !== 0) { // Calculate the offset position after rotation var sinR = Math.sin(head.rotation); var cosR = Math.cos(head.rotation); // Apply rotated offset self.x += -sinR * offsetY; self.y += cosR * offsetY; } // Add small wobble/rotation effect for natural movement if (Math.random() < 0.1) { // Random jolts occasionally - but relative to head's rotation var joltRot = (Math.random() - 0.5) * 0.05; var joltY = (Math.random() - 0.5) * 2; tween(self, { rotation: head.rotation + joltRot, // Add jolt to head's rotation y: self.y + joltY }, { duration: 300, easing: tween.elasticOut }); } } } }; return self; }); var Stickman = Container.expand(function () { var self = Container.call(this); // Initialize physics properties for ragdoll effect var bodyParts = []; var isRagdolling = false; var restoreTimer = null; // Cosmetic items var currentHat = null; var currentAccessory = null; // Make isRagdolling accessible self.isRagdolling = isRagdolling; // Make startRagdoll accessible self.startRagdoll = startRagdoll; // Body parts with physics properties var body = self.attachAsset('stickman', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 1 }); body.velocityX = 0; body.velocityY = 0; body.rotationalVelocity = 0; body.mass = 10; body.originalX = 0; body.originalY = 0; body.originalRotation = 0; body.elasticity = 0.7; body.friction = 0.9; bodyParts.push(body); var head = self.attachAsset('head', { anchorX: 0.5, anchorY: 0.5, y: -225 }); head.velocityX = 0; head.velocityY = 0; head.rotationalVelocity = 0; head.mass = 5; head.originalX = 0; head.originalY = -225; head.originalRotation = 0; head.elasticity = 0.8; head.friction = 0.85; bodyParts.push(head); // Arms (using stickman asset but resized) var leftArm = self.attachAsset('stickman', { anchorX: 0.5, anchorY: 0, scaleX: 0.2, scaleY: 0.6, x: -70, y: -150, rotation: 0 }); leftArm.velocityX = 0; leftArm.velocityY = 0; leftArm.rotationalVelocity = 0; leftArm.mass = 3; leftArm.originalX = -70; leftArm.originalY = -150; leftArm.originalRotation = 0; leftArm.elasticity = 0.9; leftArm.friction = 0.8; bodyParts.push(leftArm); var rightArm = self.attachAsset('stickman', { anchorX: 0.5, anchorY: 0, scaleX: 0.2, scaleY: 0.6, x: 70, y: -150, rotation: 0 }); rightArm.velocityX = 0; rightArm.velocityY = 0; rightArm.rotationalVelocity = 0; rightArm.mass = 3; rightArm.originalX = 70; rightArm.originalY = -150; rightArm.originalRotation = 0; rightArm.elasticity = 0.9; rightArm.friction = 0.8; bodyParts.push(rightArm); // Legs (using stickman asset but resized) var leftLeg = self.attachAsset('stickman', { anchorX: 0.5, anchorY: 0, scaleX: 0.2, scaleY: 0.7, x: -40, y: 50, rotation: 0 }); leftLeg.velocityX = 0; leftLeg.velocityY = 0; leftLeg.rotationalVelocity = 0; leftLeg.mass = 4; leftLeg.originalX = -40; leftLeg.originalY = 50; leftLeg.originalRotation = 0; leftLeg.elasticity = 0.6; leftLeg.friction = 0.85; bodyParts.push(leftLeg); var rightLeg = self.attachAsset('stickman', { anchorX: 0.5, anchorY: 0, scaleX: 0.2, scaleY: 0.7, x: 40, y: 50, rotation: 0 }); rightLeg.velocityX = 0; rightLeg.velocityY = 0; rightLeg.rotationalVelocity = 0; rightLeg.mass = 4; rightLeg.originalX = 40; rightLeg.originalY = 50; rightLeg.originalRotation = 0; rightLeg.elasticity = 0.6; rightLeg.friction = 0.85; bodyParts.push(rightLeg); // Health properties self.maxHealth = 100; self.health = self.maxHealth; self.level = storage.level || 1; // Apply a force to a body part function applyForce(part, forceX, forceY, torque) { part.velocityX += forceX / part.mass; part.velocityY += forceY / part.mass; part.rotationalVelocity += torque / part.mass; } // Start ragdoll physics function startRagdoll() { isRagdolling = true; // Clear any existing restore timer if (restoreTimer) { LK.clearTimeout(restoreTimer); } // Set a timer to restore the stickman to normal position restoreTimer = LK.setTimeout(function () { restorePosition(); }, 3000); } // Restore stickman to normal position function restorePosition() { isRagdolling = false; // Reset velocities first to prevent further movement bodyParts.forEach(function (part) { part.velocityX = 0; part.velocityY = 0; part.rotationalVelocity = 0; }); // Animate each body part back to its original position bodyParts.forEach(function (part) { // Stop any ongoing tweens tween.stop(part, { x: true, y: true, rotation: true }); // Animate back to original position with staggered timing for more natural movement var delay = Math.random() * 200; // Stagger the animations slightly // Animate back to original position tween(part, { x: part.originalX, y: part.originalY, rotation: part.originalRotation }, { duration: 1200, easing: tween.elasticOut, delay: delay }); }); // Only add cosmetics in defeat() method, not here when unragdolling } // Update physics simulation self.update = function () { if (isRagdolling) { var gravity = 0.2; // Further reduced gravity for less falling apart bodyParts.forEach(function (part) { // Apply gravity - but less than before part.velocityY += gravity; // Add random jolts for more realism (every few frames randomly) if (Math.random() < 0.1) { // 10% chance each frame for a random jolt var joltX = (Math.random() - 0.5) * 2.0; var joltY = (Math.random() - 0.5) * 1.5; var joltRot = (Math.random() - 0.5) * 0.05; part.velocityX += joltX; part.velocityY += joltY; part.rotationalVelocity += joltRot; } // Always add a gentle force toward the center of the screen var centerX = 2048 / 2; var centerY = 2732 / 2; var towardsCenterX = centerX - stickman.x - part.x; var towardsCenterY = centerY - stickman.y - part.y; var distance = Math.sqrt(towardsCenterX * towardsCenterX + towardsCenterY * towardsCenterY); // Only apply centering force if we're not too close to center already if (distance > 50) { // Normalize the direction vector var dirX = towardsCenterX / distance; var dirY = towardsCenterY / distance; // Apply a constant stronger force toward center var centeringForce = 2.0; // Dramatically increased to make sliding to middle super fast part.velocityX += dirX * centeringForce; part.velocityY += dirY * centeringForce; } // Apply velocities part.x += part.velocityX; part.y += part.velocityY; part.rotation += part.rotationalVelocity; // Apply more realistic friction system with surface friction // Ground friction increases as parts get closer to the bottom of the screen var groundY = 2732 - 200; // Position where ground friction is strongest var distanceFromGround = Math.max(0, groundY - (stickman.y + part.y)); var groundFrictionFactor = Math.max(0, 1 - distanceFromGround / 1000); // Gradually increase friction near ground var surfaceFriction = 0.92 + groundFrictionFactor * 0.08; // 0.92 to 1.0 based on ground proximity // Apply friction with different coefficients for horizontal vs vertical movement // Less friction on X to allow more sliding on surfaces part.velocityX *= part.friction + 0.05 * (1 - groundFrictionFactor); // Less friction for X when sliding part.velocityY *= part.friction * surfaceFriction; // More friction for Y when on surfaces part.rotationalVelocity *= 0.95; // Add damping to prevent excessive movement if (Math.abs(part.velocityX) < 0.1) part.velocityX = 0; if (Math.abs(part.velocityY) < 0.1) part.velocityY = 0; if (Math.abs(part.rotationalVelocity) < 0.01) part.rotationalVelocity = 0; // Constrain rotation if (part.rotation > Math.PI * 2) { part.rotation -= Math.PI * 2; } else if (part.rotation < -Math.PI * 2) { part.rotation += Math.PI * 2; } // Always keep limbs in their original positions by tweening them back // This ensures limbs stay where they belong while allowing some movement if (part === head) { tween(part, { x: part.originalX, y: part.originalY - 100 // Make head hover higher by 100 pixels }, { duration: 300, easing: tween.easeOut }); } // Body stays in original position with tweening if (part === body) { tween(part, { x: part.originalX, y: part.originalY, rotation: part.originalRotation }, { duration: 300, easing: tween.easeOut }); } // Arms stay connected to body with tweening but hover closer together and lower if (part === leftArm || part === rightArm) { tween(part, { x: part === leftArm ? part.originalX - 10 : part.originalX + 10, // Position arms closer together but lower y: part.originalY - 30, // Make arms hover lower by only 30 pixels instead of 130 rotation: part.originalRotation }, { duration: 300, easing: tween.easeOut }); } // Legs stay connected to body with tweening but hover slightly lower if (part === leftLeg || part === rightLeg) { tween(part, { x: part.originalX, y: part.originalY + 20, // Make legs hover lower by adding 20 pixels rotation: part.originalRotation }, { duration: 300, easing: tween.easeOut }); } }); } else { // Bob animation when not ragdolling // Use LK.ticks to create a smooth bobbing effect var bobAmount = Math.sin(LK.ticks / 20) * 15; // Control bob height with multiplier var armBobAmount = Math.sin(LK.ticks / 15) * 10; // Slightly different timing for arms // Bob head up and down tween(head, { y: head.originalY + bobAmount * 0.8 // Head bobs slightly less than body }, { duration: 100, easing: tween.easeOut }); // Bob body up and down tween(body, { y: body.originalY + bobAmount }, { duration: 100, easing: tween.easeOut }); // Bob arms with slight delay compared to body for natural swinging effect // Add arm swing using sin wave with different frequency than bob var armSwingAmount = Math.sin(LK.ticks / 25) * 0.2; // Swinging rotation tween(leftArm, { x: leftArm.originalX - 10, // Keep arms closer together by moving them further inward y: leftArm.originalY - 30 + armBobAmount, // Arms bob with slight offset, hovering lower now rotation: armSwingAmount // Arms swing around slightly }, { duration: 100, easing: tween.easeOut }); tween(rightArm, { x: rightArm.originalX + 10, // Keep arms closer together by moving them further inward y: rightArm.originalY - 30 + armBobAmount, // Arms bob with slight offset, hovering lower now rotation: -armSwingAmount // Arms swing in opposite direction }, { duration: 100, easing: tween.easeOut }); // Bob legs with opposite timing to body for natural movement, but lower // Add leg swing using cos wave for offset from arm swing var legSwingAmount = Math.cos(LK.ticks / 30) * 0.15; // Smaller swing for legs tween(leftLeg, { y: leftLeg.originalY + 50 - bobAmount * 0.5, // Position legs lower by 50px and move opposite to body rotation: legSwingAmount // Legs swing around slightly }, { duration: 100, easing: tween.easeOut }); tween(rightLeg, { y: rightLeg.originalY + 50 - bobAmount * 0.5, // Position legs lower by 50px and move opposite to body rotation: -legSwingAmount // Legs swing in opposite direction }, { duration: 100, easing: tween.easeOut }); } }; // Hit reaction self.hit = function (damage) { // If health is already zero, don't allow further hits if (self.health <= 0) { return false; } // Decrease health self.health -= damage; if (self.health <= 0) { self.health = 0; self.defeat(); } // Update health UI updateHealthBar(); // Visual reaction - flash LK.effects.flashObject(self, 0xFF0000, 300); // Get random body part for hit location var parts = [body, head, leftArm, rightArm, leftLeg, rightLeg]; var randomPart = parts[Math.floor(Math.random() * parts.length)]; // Create hit effect var hitEffect = LK.getAsset('hitEffect', { anchorX: 0.5, anchorY: 0.5, x: randomPart.x, y: randomPart.y, alpha: 0.7, scaleX: 0.5, scaleY: 0.5 }); self.addChild(hitEffect); // Animate the hit effect tween(hitEffect, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 400, onFinish: function onFinish() { hitEffect.destroy(); } }); // Start ragdoll physics if not already in that state startRagdoll(); // Apply impulse to the hit body part and make entire stickman slide var randomDirection = Math.random() * Math.PI * 2; // Random angle in radians var slideForce = damage * 2.0; // Reduced sliding force for less movement var slideForceX = Math.cos(randomDirection) * slideForce; var slideForceY = Math.sin(randomDirection) * slideForce; // Apply sliding force to all body parts - but with more control bodyParts.forEach(function (part) { // Apply more balanced forces between X and Y for better overall movement // Cap the maximum force to prevent excessive displacement var maxForce = 30; // Reduced max force for lighter sliding var forceFactor = 3.0; // Reduced force factor for gentler middle movement var appliedForceX = Math.min(maxForce, slideForceX * forceFactor); var appliedForceY = Math.min(maxForce, slideForceY * forceFactor); // Apply the forces part.velocityX += appliedForceX; part.velocityY += appliedForceY; }); // Apply additional impulse to the specific hit body part // Apply more controlled force to avoid body parts flying apart var hitAngle = Math.random() * Math.PI * 2; var hitMagnitude = Math.min(damage * 1.5, 10); // Further reduced hit magnitude var hitForceX = Math.cos(hitAngle) * hitMagnitude; var hitForceY = Math.sin(hitAngle) * hitMagnitude; var hitTorque = (Math.random() - 0.5) * damage * 0.1; // Significantly reduced torque // Apply force with special effects based on the body part hit if (randomPart === head) { // Head hits are more impactful applyForce(randomPart, hitForceX * 1.5, hitForceY * 1.5, hitTorque * 2); // Also make the head wobble tween(randomPart, { rotation: randomPart.rotation + Math.PI * (Math.random() - 0.5) }, { duration: 500, easing: tween.elasticOut }); } else if (randomPart === leftArm || randomPart === rightArm) { // Arms swing more applyForce(randomPart, hitForceX, hitForceY, hitTorque * 3); } else if (randomPart === leftLeg || randomPart === rightLeg) { // Legs have more mass, less rotation applyForce(randomPart, hitForceX * 0.8, hitForceY * 0.8, hitTorque * 0.5); } else if (randomPart === body) { // Body affects all parts bodyParts.forEach(function (part) { applyForce(part, hitForceX * 0.6, hitForceY * 0.6, hitTorque * 0.3); }); } // Play sound LK.getSound('hit').play(); // No chance to get cosmetics on hit - only on defeat return true; }; // When stickman is defeated self.defeat = function () { // Massive ragdoll effect on defeat startRagdoll(); // Apply explosive force to all body parts bodyParts.forEach(function (part) { var explosiveForceX = (Math.random() - 0.5) * 50; var explosiveForceY = (Math.random() - 0.5) * 50; var explosiveTorque = (Math.random() - 0.5) * 2; applyForce(part, explosiveForceX, explosiveForceY, explosiveTorque); }); // Make cosmetics fly off dramatically if (currentHat) { var hatX = currentHat.x; var hatY = currentHat.y; tween(currentHat, { x: hatX + (Math.random() - 0.5) * 300, y: hatY - Math.random() * 400, rotation: Math.PI * (Math.random() * 4 - 2), alpha: 0 }, { duration: 1000, onFinish: function onFinish() { if (currentHat) { currentHat.destroy(); currentHat = null; } } }); } if (currentAccessory) { var accX = currentAccessory.x; var accY = currentAccessory.y; tween(currentAccessory, { x: accX + (Math.random() - 0.5) * 300, y: accY + Math.random() * 400, rotation: Math.PI * (Math.random() * 4 - 2), alpha: 0 }, { duration: 1000, onFinish: function onFinish() { if (currentAccessory) { currentAccessory.destroy(); currentAccessory = null; } } }); } // Create explosion effects around each body part bodyParts.forEach(function (part) { // Create multiple explosion particles for each body part for (var i = 0; i < 3; i++) { var offsetX = (Math.random() - 0.5) * 100; var offsetY = (Math.random() - 0.5) * 100; var explosion = LK.getAsset('hitEffect', { anchorX: 0.5, anchorY: 0.5, x: part.x + offsetX, y: part.y + offsetY, alpha: 0.9, scaleX: 0.8, scaleY: 0.8, tint: Math.random() > 0.5 ? 0xff0000 : 0xff6600 }); self.addChild(explosion); // Animate explosion tween(explosion, { scaleX: 2.5, scaleY: 2.5, alpha: 0 }, { duration: 600, onFinish: function onFinish() { explosion.destroy(); } }); } }); // Hide body parts temporarily (they'll reappear when respawned) bodyParts.forEach(function (part) { tween(part, { alpha: 0 }, { duration: 400 }); }); // Level up self.level++; storage.level = self.level; // Reward player with points var reward = 10 * self.level; addPoints(reward); // Health will remain the same after each kill (no increase) // Show level up message showLevelUpMessage(self.level, reward); // Reset health with increased max health after a delay (reduced to 2 seconds) LK.setTimeout(function () { // Make parts visible again bodyParts.forEach(function (part) { part.alpha = 1; }); // Increase max health by 25% each respawn self.maxHealth = Math.floor(self.maxHealth * 1.25); self.health = self.maxHealth; updateHealthBar(); restorePosition(); }, 2000); // Changed from 3000 to 2000 for 2-second respawn // Play unlock sound LK.getSound('unlock').play(); }; // Make applyRandomCosmetic a public method so it can be accessed from outside the class self.applyRandomCosmetic = function (cosmeticType) { // Remove current cosmetic of this type if exists if (cosmeticType === "hat" && currentHat) { if (currentHat) { currentHat.destroy(); currentHat = null; } } else if (cosmeticType === "accessory" && currentAccessory) { if (currentAccessory) { currentAccessory.destroy(); currentAccessory = null; } } // Available cosmetic items var hatOptions = ["cowboyHat", "topHat", "partyHat", "crown"]; var accessoryOptions = ["sunglasses", "mustache", "beard"]; // Choose a random item var options = cosmeticType === "hat" ? hatOptions : accessoryOptions; var randomItem = options[Math.floor(Math.random() * options.length)]; // Create and attach the new cosmetic item var newCosmetic = new Cosmetic(cosmeticType, randomItem); self.addChild(newCosmetic); // Store reference to the new item if (cosmeticType === "hat") { currentHat = newCosmetic; } else { currentAccessory = newCosmetic; } // Add entrance animation newCosmetic.scaleX = 0.1; newCosmetic.scaleY = 0.1; newCosmetic.alpha = 0; tween(newCosmetic, { scaleX: 1, scaleY: 1, alpha: 1 }, { duration: 500, easing: tween.elasticOut }); // Play unlock sound LK.getSound('unlock').play(); // Show message var itemName = randomItem.charAt(0).toUpperCase() + randomItem.slice(1); showMessage("New " + cosmeticType + ": " + itemName); }; // Event handler when clicking on stickman self.down = function (x, y, obj) { // Convert coordinates to local space var localX = x - self.x; var localY = y - self.y; // Find closest body part to the click point var closestPart = body; // Default to body var closestDistance = Number.MAX_VALUE; bodyParts.forEach(function (part) { var dx = part.x - localX; var dy = part.y - localY; var distance = dx * dx + dy * dy; if (distance < closestDistance) { closestDistance = distance; closestPart = part; } }); // Use the class method instead of local function // Now we use self.applyRandomCosmetic which is defined outside of this method // Apply current tool damage self.hit(currentDamage); // Additional force to the specific body part that was clicked - reduced to make knockback gentler var hitForceX = (Math.random() - 0.5) * currentDamage * 2; var hitForceY = (Math.random() - 0.5) * currentDamage * 2; var hitTorque = (Math.random() - 0.5) * currentDamage * 0.1; applyForce(closestPart, hitForceX, hitForceY, hitTorque); // Add points based on current damage addPoints(currentDamage); }; return self; }); var Tool = Container.expand(function (toolData) { var self = Container.call(this); self.data = toolData; var toolShape = self.attachAsset(toolData.id, { anchorX: 0.5, anchorY: 0.5 }); var nameText = new Text2(toolData.name, { size: 24, fill: 0xFFFFFF }); nameText.anchor.set(0.5, 0); nameText.y = 80; self.addChild(nameText); var damageText = new Text2("+" + toolData.damage + " DMG", { size: 22, fill: 0xFFFF00 }); damageText.anchor.set(0.5, 0); damageText.y = 110; self.addChild(damageText); var priceText = new Text2(toolData.price + " PTS", { size: 22, fill: 0xFFFFFF }); priceText.anchor.set(0.5, 0); priceText.y = 140; self.addChild(priceText); self.down = function (x, y, obj) { // Only interact with the shop when it's open if (!isShopOpen) return; if (points >= toolData.price && !isToolOwned(toolData.id)) { // Purchase the tool addPoints(-toolData.price); unlockTool(toolData); // Play unlock sound LK.getSound('unlock').play(); // Show purchase confirmation showMessage("You unlocked " + toolData.name + "!"); // Update shop updateShop(); } else if (isToolOwned(toolData.id)) { // Equip the tool if already owned equipTool(toolData.id); showMessage(toolData.name + " equipped!"); } else { // Show not enough points message showMessage("Not enough points!"); } }; return self; }); var Weapon = Container.expand(function () { var self = Container.call(this); // Create weapon graphic based on current tool var toolAsset = equippedTool ? equippedTool.id : 'fist'; var weaponGraphic = self.attachAsset(toolAsset, { anchorX: 0.5, anchorY: 0.5, scaleX: 0.7, scaleY: 0.7 }); // Weapon properties self.velocityX = 0; self.velocityY = 0; self.speed = 15; self.damage = 0; self.target = null; self.lastIntersecting = false; // Update method to move weapon towards target self.update = function () { if (!self.target) return; // Calculate direction to target var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // If weapon is close to target, hit it if (distance < 50) { if (self.target.health > 0) { self.target.hit(self.damage); createHitAnimation(self.x, self.y); } self.destroy(); return; } // Normalize direction vector var dirX = dx / distance; var dirY = dy / distance; // Move towards target self.x += dirX * self.speed; self.y += dirY * self.speed; // Rotate weapon to face movement direction self.rotation = Math.atan2(dy, dx) + Math.PI / 4; // Add spinning effect to weapons except for fist if (toolAsset !== 'fist') { // Add a continuous rotation on top of the directional rotation self.rotation += LK.ticks * 0.2; // Controls spin speed } // Check intersection with target var currentIntersecting = self.intersects(self.target); if (!self.lastIntersecting && currentIntersecting) { if (self.target.health > 0) { self.target.hit(self.damage); createHitAnimation(self.x, self.y); } self.destroy(); } self.lastIntersecting = currentIntersecting; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0xEEEEEE // Light gray background for better stickman visibility }); /**** * Game Code ****/ // Game state variables // Hat assets // Face accessory assets if (!tween) { console.error("Failed to import tween library"); // Create fallback implementation tween = function tween(obj, props, options) { if (options && options.onFinish) { setTimeout(options.onFinish, options.duration || 1000); } // Apply props immediately as fallback for (var key in props) { if (props.hasOwnProperty(key)) { obj[key] = props[key]; } } }; tween.easeOut = function (t) { return t; }; tween.easeIn = function (t) { return t; }; tween.elasticOut = function (t) { return t; }; tween.stop = function () {}; } var points = storage.points || 0; var baseDamage = storage.damage || 1; var currentDamage = baseDamage; var tools = storage.tools || []; var equippedTool = null; var isShopOpen = false; var shopContainer = null; var activeWeapons = []; // Tool definitions var availableTools = [{ id: 'fist', name: 'Fist', damage: 1, price: 0, color: 0xFFB380, owned: true }, { id: 'stick', name: 'Stick', damage: 2, price: 50, color: 0x8B4513 }, { id: 'bat', name: 'Baseball Bat', damage: 5, price: 200, color: 0x654321 }, { id: 'hammer', name: 'Hammer', damage: 10, price: 500, color: 0x808080 }, { id: 'pan', name: 'Frying Pan', damage: 15, price: 1000, color: 0x404040 }, { id: 'mallet', name: 'Cartoon Mallet', damage: 25, price: 2500, color: 0xFF0000 }, { id: 'anvil', name: 'Anvil', damage: 50, price: 5000, color: 0x303030 }, { id: 'piano', name: 'Grand Piano', damage: 100, price: 10000, color: 0x000000 }]; // Set up the stickman var stickman = new Stickman(); stickman.x = 2048 / 2; stickman.y = 2732 / 2; // Make stickman bigger for better visibility stickman.scale.set(1.5, 1.5); game.addChild(stickman); // Set up UI elements var pointsText = new Text2("POINTS: 0", { size: 70, fill: 0x000000 }); pointsText.anchor.set(0.5, 0); pointsText.y = 50; LK.gui.top.addChild(pointsText); var damageText = new Text2("DAMAGE: 1", { size: 50, fill: 0xFF0000 }); damageText.anchor.set(0.5, 0); damageText.y = 130; LK.gui.top.addChild(damageText); // Level counter removed // Health bar var healthBarBg = LK.getAsset('stickman', { anchorX: 0, anchorY: 0.5, width: 1000, height: 60, tint: 0x888888 }); healthBarBg.x = 2048 / 2 - 500; healthBarBg.y = 300; game.addChild(healthBarBg); var healthBar = LK.getAsset('stickman', { anchorX: 0, anchorY: 0.5, width: 1000, height: 60, tint: 0xFF0000 }); healthBar.x = 2048 / 2 - 500; healthBar.y = 300; game.addChild(healthBar); var healthText = new Text2("100/100", { size: 46, fill: 0xFFFFFF }); healthText.anchor.set(0.5, 0.5); healthText.x = 2048 / 2; healthText.y = 300; game.addChild(healthText); // Shop button var shopButton = LK.getAsset('shopButton', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); shopButton.x = 2048 - 150; shopButton.y = 100; game.addChild(shopButton); var shopText = new Text2("SHOP", { size: 60, fill: 0xFFFFFF }); shopText.anchor.set(0.5, 0.5); shopText.x = 2048 - 150; shopText.y = 100; game.addChild(shopText); // Message display var messageText = new Text2("", { size: 60, fill: 0xFF0000 }); messageText.anchor.set(0.5, 0.5); messageText.x = 2048 / 2; messageText.y = 500; messageText.alpha = 0; game.addChild(messageText); // Initialize the game function init() { // Set points to 0 when game starts points = 0; storage.points = 0; // Reset weapons when game starts currentDamage = baseDamage; equippedTool = availableTools[0]; // Reset to fist activeWeapons = []; tools = []; // Reset tools array to empty so no weapons are purchased storage.tools = []; // Update storage to persist the reset // Set up initial values updatePointsDisplay(); updateDamageDisplay(); updateHealthBar(); // Give stickman a random hat and/or accessory on start if (Math.random() < 0.7) { // 70% chance to start with a hat stickman.applyRandomCosmetic("hat"); } if (Math.random() < 0.5) { // 50% chance to start with an accessory stickman.applyRandomCosmetic("accessory"); } // Mark tools as owned if in storage tools.forEach(function (toolId) { for (var i = 0; i < availableTools.length; i++) { if (availableTools[i].id === toolId) { availableTools[i].owned = true; } } }); // Equip the first owned tool var ownedTools = availableTools.filter(function (tool) { return tool.owned; }); if (ownedTools.length > 0) { equipTool(ownedTools[0].id); } // Set shop button interaction shopButton.interactive = true; shopButton.down = function () { toggleShop(); }; // Play background music LK.playMusic('gameMusic'); } // Toggle shop visibility function toggleShop() { if (isShopOpen) { closeShop(); } else { openShop(); } } // Open the shop function openShop() { isShopOpen = true; // Create shop container shopContainer = new Container(); shopContainer.x = 2048 / 2; shopContainer.y = 2732 / 2; game.addChild(shopContainer); // Shop background var shopBg = LK.getAsset('stickman', { anchorX: 0.5, anchorY: 0.5, width: 1600, height: 1800, tint: 0x333333, alpha: 0.9 }); shopContainer.addChild(shopBg); // Shop title var shopTitle = new Text2("TOOL SHOP", { size: 80, fill: 0xFFFFFF }); shopTitle.anchor.set(0.5, 0); shopTitle.y = -800; shopContainer.addChild(shopTitle); // Close button var closeButton = LK.getAsset('shopButton', { anchorX: 0.5, anchorY: 0.5, x: 700, y: -800, tint: 0xFF0000 }); shopContainer.addChild(closeButton); var closeText = new Text2("CLOSE", { size: 40, fill: 0xFFFFFF }); closeText.anchor.set(0.5, 0.5); closeText.x = 700; closeText.y = -800; shopContainer.addChild(closeText); closeButton.interactive = true; closeButton.down = function () { closeShop(); }; // Layout tools in a grid var toolsPerRow = 3; var toolWidth = 300; var toolHeight = 350; var startX = -(toolsPerRow - 1) * toolWidth / 2; var startY = -600; availableTools.forEach(function (toolData, index) { var row = Math.floor(index / toolsPerRow); var col = index % toolsPerRow; var toolItem = new Tool(toolData); toolItem.x = startX + col * toolWidth; toolItem.y = startY + row * toolHeight; shopContainer.addChild(toolItem); // Show "OWNED" or "EQUIPPED" if applicable if (toolData.owned) { var statusText = new Text2(isToolEquipped(toolData.id) ? "EQUIPPED" : "OWNED", { size: 24, fill: isToolEquipped(toolData.id) ? "#00FF00" : "#FFFFFF" }); statusText.anchor.set(0.5, 0); statusText.y = 170; toolItem.addChild(statusText); } }); // Animate shop opening shopContainer.alpha = 0; shopContainer.scaleX = 0.8; shopContainer.scaleY = 0.8; tween(shopContainer, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.easeOut }); } // Close the shop function closeShop() { if (!isShopOpen || !shopContainer) return; tween(shopContainer, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { if (shopContainer) { shopContainer.destroy(); shopContainer = null; } isShopOpen = false; } }); } // Check if a tool is owned function isToolOwned(toolId) { return tools.indexOf(toolId) !== -1; } // Check if a tool is currently equipped function isToolEquipped(toolId) { return equippedTool && equippedTool.id === toolId; } // Unlock a new tool function unlockTool(toolData) { if (!isToolOwned(toolData.id)) { tools.push(toolData.id); storage.tools = tools; // Mark as owned for (var i = 0; i < availableTools.length; i++) { if (availableTools[i].id === toolData.id) { availableTools[i].owned = true; break; } } // Equip the newly unlocked tool equipTool(toolData.id); } } // Equip a tool function equipTool(toolId) { if (!isToolOwned(toolId)) return; // Find the tool data var toolData = null; for (var i = 0; i < availableTools.length; i++) { if (availableTools[i].id === toolId) { toolData = availableTools[i]; break; } } if (toolData) { equippedTool = toolData; currentDamage = baseDamage + toolData.damage; updateDamageDisplay(); } } // Update the shop display function updateShop() { if (isShopOpen) { closeShop(); openShop(); } } // Add points to the player's total function addPoints(amount) { points += amount; storage.points = points; updatePointsDisplay(); } // Update the points display function updatePointsDisplay() { pointsText.setText("POINTS: " + points); } // Update the damage display function updateDamageDisplay() { damageText.setText("DAMAGE: " + currentDamage); } // Level display function removed // Update the health bar function updateHealthBar() { var percentage = stickman.health / stickman.maxHealth; healthBar.width = 1000 * percentage; healthText.setText(stickman.health + "/" + stickman.maxHealth); } // Show a message temporarily function showMessage(text) { messageText.setText(text); messageText.alpha = 1; // Clear any existing tweens tween.stop(messageText, { alpha: true }); // Animate message tween(messageText, { alpha: 0 }, { duration: 2000 }); } // Show level up message function showLevelUpMessage(level, reward) { var levelUpText = new Text2("LEVEL UP!\n+" + reward + " Points", { size: 80, fill: 0xFFFF00 }); levelUpText.anchor.set(0.5, 0.5); levelUpText.x = 2048 / 2; levelUpText.y = 2732 / 2 - 400; levelUpText.alpha = 0; game.addChild(levelUpText); // Animate the level up text tween(levelUpText, { alpha: 1, y: levelUpText.y - 100 }, { duration: 500, onFinish: function onFinish() { tween(levelUpText, { alpha: 0 }, { duration: 1000, delay: 1000, onFinish: function onFinish() { levelUpText.destroy(); } }); } }); } // Create hit animation function createHitAnimation(x, y) { var hitEffect = LK.getAsset('hitEffect', { anchorX: 0.5, anchorY: 0.5, x: x, y: y, alpha: 0.7, scaleX: 0.5, scaleY: 0.5 }); game.addChild(hitEffect); tween(hitEffect, { scaleX: 2, scaleY: 2, alpha: 0 }, { duration: 400, onFinish: function onFinish() { hitEffect.destroy(); } }); } // Track dragging state var isDragging = false; var draggedBodyPart = null; var dragOffsetX = 0; var dragOffsetY = 0; // Mouse/touch handlers for the game game.down = function (x, y, obj) { // If the shop is open, don't register clicks on the game if (isShopOpen) return; // If stickman is dead, don't allow hitting if (stickman.health <= 0) { return; } // No longer add points on each click // Spawn weapon at tap location and make it fly towards stickman var weapon = new Weapon(); weapon.x = x; weapon.y = y; weapon.damage = currentDamage; weapon.target = stickman; // Add to game game.addChild(weapon); // Create a hit animation at the tap point createHitAnimation(x, y); // Play sound LK.getSound('hit').play(); }; // Handle move events for dragging game.move = function (x, y, obj) { // If stickman is dead, don't allow dragging if (stickman.health <= 0) { isDragging = false; draggedBodyPart = null; return; } if (isDragging && draggedBodyPart && !isShopOpen) { var localX = x - stickman.x; var localY = y - stickman.y; // Update dragged part position draggedBodyPart.x = localX - dragOffsetX; draggedBodyPart.y = localY - dragOffsetY; // Reset velocity for smooth dragging draggedBodyPart.velocityX = 0; draggedBodyPart.velocityY = 0; } }; // Handle up events for dragging game.up = function (x, y, obj) { if (isDragging && draggedBodyPart && !isShopOpen) { // On release, give the part some velocity based on movement var releaseVelocityX = (Math.random() - 0.5) * 10; var releaseVelocityY = (Math.random() - 0.5) * 10; draggedBodyPart.velocityX = releaseVelocityX; draggedBodyPart.velocityY = releaseVelocityY; isDragging = false; draggedBodyPart = null; } }; // Update function called every tick game.update = function () { // Clean up destroyed weapons for (var i = 0; i < game.children.length; i++) { var child = game.children[i]; if (child instanceof Weapon) { if (!child.parent) { game.children.splice(i, 1); i--; } } } }; // Initialize the game init();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
points: 0,
damage: 1,
tools: [],
level: 1
});
/****
* Classes
****/
// Cosmetic item class
var Cosmetic = Container.expand(function (type, itemId) {
var self = Container.call(this);
self.itemType = type; // "hat" or "accessory"
self.itemId = itemId;
// Create the visual element
var graphic = self.attachAsset(itemId, {
anchorX: 0.5,
anchorY: 0.5
});
// We'll set positions dynamically in the update method
// based on the stickman's head position
// Add wobble animation
self.update = function () {
// Find the stickman's head position (assuming the parent of this cosmetic is the stickman)
var stickmanParent = self.parent;
if (stickmanParent) {
// Find the head among the stickman's children
var head = null;
for (var i = 0; i < stickmanParent.children.length; i++) {
var child = stickmanParent.children[i];
// Check if this is the head (based on its properties)
if (child.originalY === -225) {
head = child;
break;
}
}
// If we found the head, follow its position and rotation
if (head) {
// Position cosmetics relative to head's center point
var offsetX = 0;
// Adjust Y offset - move sunglasses higher up on the head
var offsetY = self.itemType === "hat" ? -50 : self.itemId === "sunglasses" ? -10 : 20; // Higher position for sunglasses
// Set position and rotation from the head's center
self.x = head.x;
self.y = head.y;
// Set rotation to match head
self.rotation = head.rotation;
// Apply the offset after rotation to make it rotate around the head
if (offsetY !== 0) {
// Calculate the offset position after rotation
var sinR = Math.sin(head.rotation);
var cosR = Math.cos(head.rotation);
// Apply rotated offset
self.x += -sinR * offsetY;
self.y += cosR * offsetY;
}
// Add small wobble/rotation effect for natural movement
if (Math.random() < 0.1) {
// Random jolts occasionally - but relative to head's rotation
var joltRot = (Math.random() - 0.5) * 0.05;
var joltY = (Math.random() - 0.5) * 2;
tween(self, {
rotation: head.rotation + joltRot,
// Add jolt to head's rotation
y: self.y + joltY
}, {
duration: 300,
easing: tween.elasticOut
});
}
}
}
};
return self;
});
var Stickman = Container.expand(function () {
var self = Container.call(this);
// Initialize physics properties for ragdoll effect
var bodyParts = [];
var isRagdolling = false;
var restoreTimer = null;
// Cosmetic items
var currentHat = null;
var currentAccessory = null;
// Make isRagdolling accessible
self.isRagdolling = isRagdolling;
// Make startRagdoll accessible
self.startRagdoll = startRagdoll;
// Body parts with physics properties
var body = self.attachAsset('stickman', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 1
});
body.velocityX = 0;
body.velocityY = 0;
body.rotationalVelocity = 0;
body.mass = 10;
body.originalX = 0;
body.originalY = 0;
body.originalRotation = 0;
body.elasticity = 0.7;
body.friction = 0.9;
bodyParts.push(body);
var head = self.attachAsset('head', {
anchorX: 0.5,
anchorY: 0.5,
y: -225
});
head.velocityX = 0;
head.velocityY = 0;
head.rotationalVelocity = 0;
head.mass = 5;
head.originalX = 0;
head.originalY = -225;
head.originalRotation = 0;
head.elasticity = 0.8;
head.friction = 0.85;
bodyParts.push(head);
// Arms (using stickman asset but resized)
var leftArm = self.attachAsset('stickman', {
anchorX: 0.5,
anchorY: 0,
scaleX: 0.2,
scaleY: 0.6,
x: -70,
y: -150,
rotation: 0
});
leftArm.velocityX = 0;
leftArm.velocityY = 0;
leftArm.rotationalVelocity = 0;
leftArm.mass = 3;
leftArm.originalX = -70;
leftArm.originalY = -150;
leftArm.originalRotation = 0;
leftArm.elasticity = 0.9;
leftArm.friction = 0.8;
bodyParts.push(leftArm);
var rightArm = self.attachAsset('stickman', {
anchorX: 0.5,
anchorY: 0,
scaleX: 0.2,
scaleY: 0.6,
x: 70,
y: -150,
rotation: 0
});
rightArm.velocityX = 0;
rightArm.velocityY = 0;
rightArm.rotationalVelocity = 0;
rightArm.mass = 3;
rightArm.originalX = 70;
rightArm.originalY = -150;
rightArm.originalRotation = 0;
rightArm.elasticity = 0.9;
rightArm.friction = 0.8;
bodyParts.push(rightArm);
// Legs (using stickman asset but resized)
var leftLeg = self.attachAsset('stickman', {
anchorX: 0.5,
anchorY: 0,
scaleX: 0.2,
scaleY: 0.7,
x: -40,
y: 50,
rotation: 0
});
leftLeg.velocityX = 0;
leftLeg.velocityY = 0;
leftLeg.rotationalVelocity = 0;
leftLeg.mass = 4;
leftLeg.originalX = -40;
leftLeg.originalY = 50;
leftLeg.originalRotation = 0;
leftLeg.elasticity = 0.6;
leftLeg.friction = 0.85;
bodyParts.push(leftLeg);
var rightLeg = self.attachAsset('stickman', {
anchorX: 0.5,
anchorY: 0,
scaleX: 0.2,
scaleY: 0.7,
x: 40,
y: 50,
rotation: 0
});
rightLeg.velocityX = 0;
rightLeg.velocityY = 0;
rightLeg.rotationalVelocity = 0;
rightLeg.mass = 4;
rightLeg.originalX = 40;
rightLeg.originalY = 50;
rightLeg.originalRotation = 0;
rightLeg.elasticity = 0.6;
rightLeg.friction = 0.85;
bodyParts.push(rightLeg);
// Health properties
self.maxHealth = 100;
self.health = self.maxHealth;
self.level = storage.level || 1;
// Apply a force to a body part
function applyForce(part, forceX, forceY, torque) {
part.velocityX += forceX / part.mass;
part.velocityY += forceY / part.mass;
part.rotationalVelocity += torque / part.mass;
}
// Start ragdoll physics
function startRagdoll() {
isRagdolling = true;
// Clear any existing restore timer
if (restoreTimer) {
LK.clearTimeout(restoreTimer);
}
// Set a timer to restore the stickman to normal position
restoreTimer = LK.setTimeout(function () {
restorePosition();
}, 3000);
}
// Restore stickman to normal position
function restorePosition() {
isRagdolling = false;
// Reset velocities first to prevent further movement
bodyParts.forEach(function (part) {
part.velocityX = 0;
part.velocityY = 0;
part.rotationalVelocity = 0;
});
// Animate each body part back to its original position
bodyParts.forEach(function (part) {
// Stop any ongoing tweens
tween.stop(part, {
x: true,
y: true,
rotation: true
});
// Animate back to original position with staggered timing for more natural movement
var delay = Math.random() * 200; // Stagger the animations slightly
// Animate back to original position
tween(part, {
x: part.originalX,
y: part.originalY,
rotation: part.originalRotation
}, {
duration: 1200,
easing: tween.elasticOut,
delay: delay
});
});
// Only add cosmetics in defeat() method, not here when unragdolling
}
// Update physics simulation
self.update = function () {
if (isRagdolling) {
var gravity = 0.2; // Further reduced gravity for less falling apart
bodyParts.forEach(function (part) {
// Apply gravity - but less than before
part.velocityY += gravity;
// Add random jolts for more realism (every few frames randomly)
if (Math.random() < 0.1) {
// 10% chance each frame for a random jolt
var joltX = (Math.random() - 0.5) * 2.0;
var joltY = (Math.random() - 0.5) * 1.5;
var joltRot = (Math.random() - 0.5) * 0.05;
part.velocityX += joltX;
part.velocityY += joltY;
part.rotationalVelocity += joltRot;
}
// Always add a gentle force toward the center of the screen
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var towardsCenterX = centerX - stickman.x - part.x;
var towardsCenterY = centerY - stickman.y - part.y;
var distance = Math.sqrt(towardsCenterX * towardsCenterX + towardsCenterY * towardsCenterY);
// Only apply centering force if we're not too close to center already
if (distance > 50) {
// Normalize the direction vector
var dirX = towardsCenterX / distance;
var dirY = towardsCenterY / distance;
// Apply a constant stronger force toward center
var centeringForce = 2.0; // Dramatically increased to make sliding to middle super fast
part.velocityX += dirX * centeringForce;
part.velocityY += dirY * centeringForce;
}
// Apply velocities
part.x += part.velocityX;
part.y += part.velocityY;
part.rotation += part.rotationalVelocity;
// Apply more realistic friction system with surface friction
// Ground friction increases as parts get closer to the bottom of the screen
var groundY = 2732 - 200; // Position where ground friction is strongest
var distanceFromGround = Math.max(0, groundY - (stickman.y + part.y));
var groundFrictionFactor = Math.max(0, 1 - distanceFromGround / 1000); // Gradually increase friction near ground
var surfaceFriction = 0.92 + groundFrictionFactor * 0.08; // 0.92 to 1.0 based on ground proximity
// Apply friction with different coefficients for horizontal vs vertical movement
// Less friction on X to allow more sliding on surfaces
part.velocityX *= part.friction + 0.05 * (1 - groundFrictionFactor); // Less friction for X when sliding
part.velocityY *= part.friction * surfaceFriction; // More friction for Y when on surfaces
part.rotationalVelocity *= 0.95;
// Add damping to prevent excessive movement
if (Math.abs(part.velocityX) < 0.1) part.velocityX = 0;
if (Math.abs(part.velocityY) < 0.1) part.velocityY = 0;
if (Math.abs(part.rotationalVelocity) < 0.01) part.rotationalVelocity = 0;
// Constrain rotation
if (part.rotation > Math.PI * 2) {
part.rotation -= Math.PI * 2;
} else if (part.rotation < -Math.PI * 2) {
part.rotation += Math.PI * 2;
}
// Always keep limbs in their original positions by tweening them back
// This ensures limbs stay where they belong while allowing some movement
if (part === head) {
tween(part, {
x: part.originalX,
y: part.originalY - 100 // Make head hover higher by 100 pixels
}, {
duration: 300,
easing: tween.easeOut
});
}
// Body stays in original position with tweening
if (part === body) {
tween(part, {
x: part.originalX,
y: part.originalY,
rotation: part.originalRotation
}, {
duration: 300,
easing: tween.easeOut
});
}
// Arms stay connected to body with tweening but hover closer together and lower
if (part === leftArm || part === rightArm) {
tween(part, {
x: part === leftArm ? part.originalX - 10 : part.originalX + 10,
// Position arms closer together but lower
y: part.originalY - 30,
// Make arms hover lower by only 30 pixels instead of 130
rotation: part.originalRotation
}, {
duration: 300,
easing: tween.easeOut
});
}
// Legs stay connected to body with tweening but hover slightly lower
if (part === leftLeg || part === rightLeg) {
tween(part, {
x: part.originalX,
y: part.originalY + 20,
// Make legs hover lower by adding 20 pixels
rotation: part.originalRotation
}, {
duration: 300,
easing: tween.easeOut
});
}
});
} else {
// Bob animation when not ragdolling
// Use LK.ticks to create a smooth bobbing effect
var bobAmount = Math.sin(LK.ticks / 20) * 15; // Control bob height with multiplier
var armBobAmount = Math.sin(LK.ticks / 15) * 10; // Slightly different timing for arms
// Bob head up and down
tween(head, {
y: head.originalY + bobAmount * 0.8 // Head bobs slightly less than body
}, {
duration: 100,
easing: tween.easeOut
});
// Bob body up and down
tween(body, {
y: body.originalY + bobAmount
}, {
duration: 100,
easing: tween.easeOut
});
// Bob arms with slight delay compared to body for natural swinging effect
// Add arm swing using sin wave with different frequency than bob
var armSwingAmount = Math.sin(LK.ticks / 25) * 0.2; // Swinging rotation
tween(leftArm, {
x: leftArm.originalX - 10,
// Keep arms closer together by moving them further inward
y: leftArm.originalY - 30 + armBobAmount,
// Arms bob with slight offset, hovering lower now
rotation: armSwingAmount // Arms swing around slightly
}, {
duration: 100,
easing: tween.easeOut
});
tween(rightArm, {
x: rightArm.originalX + 10,
// Keep arms closer together by moving them further inward
y: rightArm.originalY - 30 + armBobAmount,
// Arms bob with slight offset, hovering lower now
rotation: -armSwingAmount // Arms swing in opposite direction
}, {
duration: 100,
easing: tween.easeOut
});
// Bob legs with opposite timing to body for natural movement, but lower
// Add leg swing using cos wave for offset from arm swing
var legSwingAmount = Math.cos(LK.ticks / 30) * 0.15; // Smaller swing for legs
tween(leftLeg, {
y: leftLeg.originalY + 50 - bobAmount * 0.5,
// Position legs lower by 50px and move opposite to body
rotation: legSwingAmount // Legs swing around slightly
}, {
duration: 100,
easing: tween.easeOut
});
tween(rightLeg, {
y: rightLeg.originalY + 50 - bobAmount * 0.5,
// Position legs lower by 50px and move opposite to body
rotation: -legSwingAmount // Legs swing in opposite direction
}, {
duration: 100,
easing: tween.easeOut
});
}
};
// Hit reaction
self.hit = function (damage) {
// If health is already zero, don't allow further hits
if (self.health <= 0) {
return false;
}
// Decrease health
self.health -= damage;
if (self.health <= 0) {
self.health = 0;
self.defeat();
}
// Update health UI
updateHealthBar();
// Visual reaction - flash
LK.effects.flashObject(self, 0xFF0000, 300);
// Get random body part for hit location
var parts = [body, head, leftArm, rightArm, leftLeg, rightLeg];
var randomPart = parts[Math.floor(Math.random() * parts.length)];
// Create hit effect
var hitEffect = LK.getAsset('hitEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: randomPart.x,
y: randomPart.y,
alpha: 0.7,
scaleX: 0.5,
scaleY: 0.5
});
self.addChild(hitEffect);
// Animate the hit effect
tween(hitEffect, {
scaleX: 1.5,
scaleY: 1.5,
alpha: 0
}, {
duration: 400,
onFinish: function onFinish() {
hitEffect.destroy();
}
});
// Start ragdoll physics if not already in that state
startRagdoll();
// Apply impulse to the hit body part and make entire stickman slide
var randomDirection = Math.random() * Math.PI * 2; // Random angle in radians
var slideForce = damage * 2.0; // Reduced sliding force for less movement
var slideForceX = Math.cos(randomDirection) * slideForce;
var slideForceY = Math.sin(randomDirection) * slideForce;
// Apply sliding force to all body parts - but with more control
bodyParts.forEach(function (part) {
// Apply more balanced forces between X and Y for better overall movement
// Cap the maximum force to prevent excessive displacement
var maxForce = 30; // Reduced max force for lighter sliding
var forceFactor = 3.0; // Reduced force factor for gentler middle movement
var appliedForceX = Math.min(maxForce, slideForceX * forceFactor);
var appliedForceY = Math.min(maxForce, slideForceY * forceFactor);
// Apply the forces
part.velocityX += appliedForceX;
part.velocityY += appliedForceY;
});
// Apply additional impulse to the specific hit body part
// Apply more controlled force to avoid body parts flying apart
var hitAngle = Math.random() * Math.PI * 2;
var hitMagnitude = Math.min(damage * 1.5, 10); // Further reduced hit magnitude
var hitForceX = Math.cos(hitAngle) * hitMagnitude;
var hitForceY = Math.sin(hitAngle) * hitMagnitude;
var hitTorque = (Math.random() - 0.5) * damage * 0.1; // Significantly reduced torque
// Apply force with special effects based on the body part hit
if (randomPart === head) {
// Head hits are more impactful
applyForce(randomPart, hitForceX * 1.5, hitForceY * 1.5, hitTorque * 2);
// Also make the head wobble
tween(randomPart, {
rotation: randomPart.rotation + Math.PI * (Math.random() - 0.5)
}, {
duration: 500,
easing: tween.elasticOut
});
} else if (randomPart === leftArm || randomPart === rightArm) {
// Arms swing more
applyForce(randomPart, hitForceX, hitForceY, hitTorque * 3);
} else if (randomPart === leftLeg || randomPart === rightLeg) {
// Legs have more mass, less rotation
applyForce(randomPart, hitForceX * 0.8, hitForceY * 0.8, hitTorque * 0.5);
} else if (randomPart === body) {
// Body affects all parts
bodyParts.forEach(function (part) {
applyForce(part, hitForceX * 0.6, hitForceY * 0.6, hitTorque * 0.3);
});
}
// Play sound
LK.getSound('hit').play();
// No chance to get cosmetics on hit - only on defeat
return true;
};
// When stickman is defeated
self.defeat = function () {
// Massive ragdoll effect on defeat
startRagdoll();
// Apply explosive force to all body parts
bodyParts.forEach(function (part) {
var explosiveForceX = (Math.random() - 0.5) * 50;
var explosiveForceY = (Math.random() - 0.5) * 50;
var explosiveTorque = (Math.random() - 0.5) * 2;
applyForce(part, explosiveForceX, explosiveForceY, explosiveTorque);
});
// Make cosmetics fly off dramatically
if (currentHat) {
var hatX = currentHat.x;
var hatY = currentHat.y;
tween(currentHat, {
x: hatX + (Math.random() - 0.5) * 300,
y: hatY - Math.random() * 400,
rotation: Math.PI * (Math.random() * 4 - 2),
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
if (currentHat) {
currentHat.destroy();
currentHat = null;
}
}
});
}
if (currentAccessory) {
var accX = currentAccessory.x;
var accY = currentAccessory.y;
tween(currentAccessory, {
x: accX + (Math.random() - 0.5) * 300,
y: accY + Math.random() * 400,
rotation: Math.PI * (Math.random() * 4 - 2),
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
if (currentAccessory) {
currentAccessory.destroy();
currentAccessory = null;
}
}
});
}
// Create explosion effects around each body part
bodyParts.forEach(function (part) {
// Create multiple explosion particles for each body part
for (var i = 0; i < 3; i++) {
var offsetX = (Math.random() - 0.5) * 100;
var offsetY = (Math.random() - 0.5) * 100;
var explosion = LK.getAsset('hitEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: part.x + offsetX,
y: part.y + offsetY,
alpha: 0.9,
scaleX: 0.8,
scaleY: 0.8,
tint: Math.random() > 0.5 ? 0xff0000 : 0xff6600
});
self.addChild(explosion);
// Animate explosion
tween(explosion, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0
}, {
duration: 600,
onFinish: function onFinish() {
explosion.destroy();
}
});
}
});
// Hide body parts temporarily (they'll reappear when respawned)
bodyParts.forEach(function (part) {
tween(part, {
alpha: 0
}, {
duration: 400
});
});
// Level up
self.level++;
storage.level = self.level;
// Reward player with points
var reward = 10 * self.level;
addPoints(reward);
// Health will remain the same after each kill (no increase)
// Show level up message
showLevelUpMessage(self.level, reward);
// Reset health with increased max health after a delay (reduced to 2 seconds)
LK.setTimeout(function () {
// Make parts visible again
bodyParts.forEach(function (part) {
part.alpha = 1;
});
// Increase max health by 25% each respawn
self.maxHealth = Math.floor(self.maxHealth * 1.25);
self.health = self.maxHealth;
updateHealthBar();
restorePosition();
}, 2000); // Changed from 3000 to 2000 for 2-second respawn
// Play unlock sound
LK.getSound('unlock').play();
};
// Make applyRandomCosmetic a public method so it can be accessed from outside the class
self.applyRandomCosmetic = function (cosmeticType) {
// Remove current cosmetic of this type if exists
if (cosmeticType === "hat" && currentHat) {
if (currentHat) {
currentHat.destroy();
currentHat = null;
}
} else if (cosmeticType === "accessory" && currentAccessory) {
if (currentAccessory) {
currentAccessory.destroy();
currentAccessory = null;
}
}
// Available cosmetic items
var hatOptions = ["cowboyHat", "topHat", "partyHat", "crown"];
var accessoryOptions = ["sunglasses", "mustache", "beard"];
// Choose a random item
var options = cosmeticType === "hat" ? hatOptions : accessoryOptions;
var randomItem = options[Math.floor(Math.random() * options.length)];
// Create and attach the new cosmetic item
var newCosmetic = new Cosmetic(cosmeticType, randomItem);
self.addChild(newCosmetic);
// Store reference to the new item
if (cosmeticType === "hat") {
currentHat = newCosmetic;
} else {
currentAccessory = newCosmetic;
}
// Add entrance animation
newCosmetic.scaleX = 0.1;
newCosmetic.scaleY = 0.1;
newCosmetic.alpha = 0;
tween(newCosmetic, {
scaleX: 1,
scaleY: 1,
alpha: 1
}, {
duration: 500,
easing: tween.elasticOut
});
// Play unlock sound
LK.getSound('unlock').play();
// Show message
var itemName = randomItem.charAt(0).toUpperCase() + randomItem.slice(1);
showMessage("New " + cosmeticType + ": " + itemName);
};
// Event handler when clicking on stickman
self.down = function (x, y, obj) {
// Convert coordinates to local space
var localX = x - self.x;
var localY = y - self.y;
// Find closest body part to the click point
var closestPart = body; // Default to body
var closestDistance = Number.MAX_VALUE;
bodyParts.forEach(function (part) {
var dx = part.x - localX;
var dy = part.y - localY;
var distance = dx * dx + dy * dy;
if (distance < closestDistance) {
closestDistance = distance;
closestPart = part;
}
});
// Use the class method instead of local function
// Now we use self.applyRandomCosmetic which is defined outside of this method
// Apply current tool damage
self.hit(currentDamage);
// Additional force to the specific body part that was clicked - reduced to make knockback gentler
var hitForceX = (Math.random() - 0.5) * currentDamage * 2;
var hitForceY = (Math.random() - 0.5) * currentDamage * 2;
var hitTorque = (Math.random() - 0.5) * currentDamage * 0.1;
applyForce(closestPart, hitForceX, hitForceY, hitTorque);
// Add points based on current damage
addPoints(currentDamage);
};
return self;
});
var Tool = Container.expand(function (toolData) {
var self = Container.call(this);
self.data = toolData;
var toolShape = self.attachAsset(toolData.id, {
anchorX: 0.5,
anchorY: 0.5
});
var nameText = new Text2(toolData.name, {
size: 24,
fill: 0xFFFFFF
});
nameText.anchor.set(0.5, 0);
nameText.y = 80;
self.addChild(nameText);
var damageText = new Text2("+" + toolData.damage + " DMG", {
size: 22,
fill: 0xFFFF00
});
damageText.anchor.set(0.5, 0);
damageText.y = 110;
self.addChild(damageText);
var priceText = new Text2(toolData.price + " PTS", {
size: 22,
fill: 0xFFFFFF
});
priceText.anchor.set(0.5, 0);
priceText.y = 140;
self.addChild(priceText);
self.down = function (x, y, obj) {
// Only interact with the shop when it's open
if (!isShopOpen) return;
if (points >= toolData.price && !isToolOwned(toolData.id)) {
// Purchase the tool
addPoints(-toolData.price);
unlockTool(toolData);
// Play unlock sound
LK.getSound('unlock').play();
// Show purchase confirmation
showMessage("You unlocked " + toolData.name + "!");
// Update shop
updateShop();
} else if (isToolOwned(toolData.id)) {
// Equip the tool if already owned
equipTool(toolData.id);
showMessage(toolData.name + " equipped!");
} else {
// Show not enough points message
showMessage("Not enough points!");
}
};
return self;
});
var Weapon = Container.expand(function () {
var self = Container.call(this);
// Create weapon graphic based on current tool
var toolAsset = equippedTool ? equippedTool.id : 'fist';
var weaponGraphic = self.attachAsset(toolAsset, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.7,
scaleY: 0.7
});
// Weapon properties
self.velocityX = 0;
self.velocityY = 0;
self.speed = 15;
self.damage = 0;
self.target = null;
self.lastIntersecting = false;
// Update method to move weapon towards target
self.update = function () {
if (!self.target) return;
// Calculate direction to target
var dx = self.target.x - self.x;
var dy = self.target.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// If weapon is close to target, hit it
if (distance < 50) {
if (self.target.health > 0) {
self.target.hit(self.damage);
createHitAnimation(self.x, self.y);
}
self.destroy();
return;
}
// Normalize direction vector
var dirX = dx / distance;
var dirY = dy / distance;
// Move towards target
self.x += dirX * self.speed;
self.y += dirY * self.speed;
// Rotate weapon to face movement direction
self.rotation = Math.atan2(dy, dx) + Math.PI / 4;
// Add spinning effect to weapons except for fist
if (toolAsset !== 'fist') {
// Add a continuous rotation on top of the directional rotation
self.rotation += LK.ticks * 0.2; // Controls spin speed
}
// Check intersection with target
var currentIntersecting = self.intersects(self.target);
if (!self.lastIntersecting && currentIntersecting) {
if (self.target.health > 0) {
self.target.hit(self.damage);
createHitAnimation(self.x, self.y);
}
self.destroy();
}
self.lastIntersecting = currentIntersecting;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xEEEEEE // Light gray background for better stickman visibility
});
/****
* Game Code
****/
// Game state variables
// Hat assets
// Face accessory assets
if (!tween) {
console.error("Failed to import tween library");
// Create fallback implementation
tween = function tween(obj, props, options) {
if (options && options.onFinish) {
setTimeout(options.onFinish, options.duration || 1000);
}
// Apply props immediately as fallback
for (var key in props) {
if (props.hasOwnProperty(key)) {
obj[key] = props[key];
}
}
};
tween.easeOut = function (t) {
return t;
};
tween.easeIn = function (t) {
return t;
};
tween.elasticOut = function (t) {
return t;
};
tween.stop = function () {};
}
var points = storage.points || 0;
var baseDamage = storage.damage || 1;
var currentDamage = baseDamage;
var tools = storage.tools || [];
var equippedTool = null;
var isShopOpen = false;
var shopContainer = null;
var activeWeapons = [];
// Tool definitions
var availableTools = [{
id: 'fist',
name: 'Fist',
damage: 1,
price: 0,
color: 0xFFB380,
owned: true
}, {
id: 'stick',
name: 'Stick',
damage: 2,
price: 50,
color: 0x8B4513
}, {
id: 'bat',
name: 'Baseball Bat',
damage: 5,
price: 200,
color: 0x654321
}, {
id: 'hammer',
name: 'Hammer',
damage: 10,
price: 500,
color: 0x808080
}, {
id: 'pan',
name: 'Frying Pan',
damage: 15,
price: 1000,
color: 0x404040
}, {
id: 'mallet',
name: 'Cartoon Mallet',
damage: 25,
price: 2500,
color: 0xFF0000
}, {
id: 'anvil',
name: 'Anvil',
damage: 50,
price: 5000,
color: 0x303030
}, {
id: 'piano',
name: 'Grand Piano',
damage: 100,
price: 10000,
color: 0x000000
}];
// Set up the stickman
var stickman = new Stickman();
stickman.x = 2048 / 2;
stickman.y = 2732 / 2;
// Make stickman bigger for better visibility
stickman.scale.set(1.5, 1.5);
game.addChild(stickman);
// Set up UI elements
var pointsText = new Text2("POINTS: 0", {
size: 70,
fill: 0x000000
});
pointsText.anchor.set(0.5, 0);
pointsText.y = 50;
LK.gui.top.addChild(pointsText);
var damageText = new Text2("DAMAGE: 1", {
size: 50,
fill: 0xFF0000
});
damageText.anchor.set(0.5, 0);
damageText.y = 130;
LK.gui.top.addChild(damageText);
// Level counter removed
// Health bar
var healthBarBg = LK.getAsset('stickman', {
anchorX: 0,
anchorY: 0.5,
width: 1000,
height: 60,
tint: 0x888888
});
healthBarBg.x = 2048 / 2 - 500;
healthBarBg.y = 300;
game.addChild(healthBarBg);
var healthBar = LK.getAsset('stickman', {
anchorX: 0,
anchorY: 0.5,
width: 1000,
height: 60,
tint: 0xFF0000
});
healthBar.x = 2048 / 2 - 500;
healthBar.y = 300;
game.addChild(healthBar);
var healthText = new Text2("100/100", {
size: 46,
fill: 0xFFFFFF
});
healthText.anchor.set(0.5, 0.5);
healthText.x = 2048 / 2;
healthText.y = 300;
game.addChild(healthText);
// Shop button
var shopButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
shopButton.x = 2048 - 150;
shopButton.y = 100;
game.addChild(shopButton);
var shopText = new Text2("SHOP", {
size: 60,
fill: 0xFFFFFF
});
shopText.anchor.set(0.5, 0.5);
shopText.x = 2048 - 150;
shopText.y = 100;
game.addChild(shopText);
// Message display
var messageText = new Text2("", {
size: 60,
fill: 0xFF0000
});
messageText.anchor.set(0.5, 0.5);
messageText.x = 2048 / 2;
messageText.y = 500;
messageText.alpha = 0;
game.addChild(messageText);
// Initialize the game
function init() {
// Set points to 0 when game starts
points = 0;
storage.points = 0;
// Reset weapons when game starts
currentDamage = baseDamage;
equippedTool = availableTools[0]; // Reset to fist
activeWeapons = [];
tools = []; // Reset tools array to empty so no weapons are purchased
storage.tools = []; // Update storage to persist the reset
// Set up initial values
updatePointsDisplay();
updateDamageDisplay();
updateHealthBar();
// Give stickman a random hat and/or accessory on start
if (Math.random() < 0.7) {
// 70% chance to start with a hat
stickman.applyRandomCosmetic("hat");
}
if (Math.random() < 0.5) {
// 50% chance to start with an accessory
stickman.applyRandomCosmetic("accessory");
}
// Mark tools as owned if in storage
tools.forEach(function (toolId) {
for (var i = 0; i < availableTools.length; i++) {
if (availableTools[i].id === toolId) {
availableTools[i].owned = true;
}
}
});
// Equip the first owned tool
var ownedTools = availableTools.filter(function (tool) {
return tool.owned;
});
if (ownedTools.length > 0) {
equipTool(ownedTools[0].id);
}
// Set shop button interaction
shopButton.interactive = true;
shopButton.down = function () {
toggleShop();
};
// Play background music
LK.playMusic('gameMusic');
}
// Toggle shop visibility
function toggleShop() {
if (isShopOpen) {
closeShop();
} else {
openShop();
}
}
// Open the shop
function openShop() {
isShopOpen = true;
// Create shop container
shopContainer = new Container();
shopContainer.x = 2048 / 2;
shopContainer.y = 2732 / 2;
game.addChild(shopContainer);
// Shop background
var shopBg = LK.getAsset('stickman', {
anchorX: 0.5,
anchorY: 0.5,
width: 1600,
height: 1800,
tint: 0x333333,
alpha: 0.9
});
shopContainer.addChild(shopBg);
// Shop title
var shopTitle = new Text2("TOOL SHOP", {
size: 80,
fill: 0xFFFFFF
});
shopTitle.anchor.set(0.5, 0);
shopTitle.y = -800;
shopContainer.addChild(shopTitle);
// Close button
var closeButton = LK.getAsset('shopButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 700,
y: -800,
tint: 0xFF0000
});
shopContainer.addChild(closeButton);
var closeText = new Text2("CLOSE", {
size: 40,
fill: 0xFFFFFF
});
closeText.anchor.set(0.5, 0.5);
closeText.x = 700;
closeText.y = -800;
shopContainer.addChild(closeText);
closeButton.interactive = true;
closeButton.down = function () {
closeShop();
};
// Layout tools in a grid
var toolsPerRow = 3;
var toolWidth = 300;
var toolHeight = 350;
var startX = -(toolsPerRow - 1) * toolWidth / 2;
var startY = -600;
availableTools.forEach(function (toolData, index) {
var row = Math.floor(index / toolsPerRow);
var col = index % toolsPerRow;
var toolItem = new Tool(toolData);
toolItem.x = startX + col * toolWidth;
toolItem.y = startY + row * toolHeight;
shopContainer.addChild(toolItem);
// Show "OWNED" or "EQUIPPED" if applicable
if (toolData.owned) {
var statusText = new Text2(isToolEquipped(toolData.id) ? "EQUIPPED" : "OWNED", {
size: 24,
fill: isToolEquipped(toolData.id) ? "#00FF00" : "#FFFFFF"
});
statusText.anchor.set(0.5, 0);
statusText.y = 170;
toolItem.addChild(statusText);
}
});
// Animate shop opening
shopContainer.alpha = 0;
shopContainer.scaleX = 0.8;
shopContainer.scaleY = 0.8;
tween(shopContainer, {
alpha: 1,
scaleX: 1,
scaleY: 1
}, {
duration: 300,
easing: tween.easeOut
});
}
// Close the shop
function closeShop() {
if (!isShopOpen || !shopContainer) return;
tween(shopContainer, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
if (shopContainer) {
shopContainer.destroy();
shopContainer = null;
}
isShopOpen = false;
}
});
}
// Check if a tool is owned
function isToolOwned(toolId) {
return tools.indexOf(toolId) !== -1;
}
// Check if a tool is currently equipped
function isToolEquipped(toolId) {
return equippedTool && equippedTool.id === toolId;
}
// Unlock a new tool
function unlockTool(toolData) {
if (!isToolOwned(toolData.id)) {
tools.push(toolData.id);
storage.tools = tools;
// Mark as owned
for (var i = 0; i < availableTools.length; i++) {
if (availableTools[i].id === toolData.id) {
availableTools[i].owned = true;
break;
}
}
// Equip the newly unlocked tool
equipTool(toolData.id);
}
}
// Equip a tool
function equipTool(toolId) {
if (!isToolOwned(toolId)) return;
// Find the tool data
var toolData = null;
for (var i = 0; i < availableTools.length; i++) {
if (availableTools[i].id === toolId) {
toolData = availableTools[i];
break;
}
}
if (toolData) {
equippedTool = toolData;
currentDamage = baseDamage + toolData.damage;
updateDamageDisplay();
}
}
// Update the shop display
function updateShop() {
if (isShopOpen) {
closeShop();
openShop();
}
}
// Add points to the player's total
function addPoints(amount) {
points += amount;
storage.points = points;
updatePointsDisplay();
}
// Update the points display
function updatePointsDisplay() {
pointsText.setText("POINTS: " + points);
}
// Update the damage display
function updateDamageDisplay() {
damageText.setText("DAMAGE: " + currentDamage);
}
// Level display function removed
// Update the health bar
function updateHealthBar() {
var percentage = stickman.health / stickman.maxHealth;
healthBar.width = 1000 * percentage;
healthText.setText(stickman.health + "/" + stickman.maxHealth);
}
// Show a message temporarily
function showMessage(text) {
messageText.setText(text);
messageText.alpha = 1;
// Clear any existing tweens
tween.stop(messageText, {
alpha: true
});
// Animate message
tween(messageText, {
alpha: 0
}, {
duration: 2000
});
}
// Show level up message
function showLevelUpMessage(level, reward) {
var levelUpText = new Text2("LEVEL UP!\n+" + reward + " Points", {
size: 80,
fill: 0xFFFF00
});
levelUpText.anchor.set(0.5, 0.5);
levelUpText.x = 2048 / 2;
levelUpText.y = 2732 / 2 - 400;
levelUpText.alpha = 0;
game.addChild(levelUpText);
// Animate the level up text
tween(levelUpText, {
alpha: 1,
y: levelUpText.y - 100
}, {
duration: 500,
onFinish: function onFinish() {
tween(levelUpText, {
alpha: 0
}, {
duration: 1000,
delay: 1000,
onFinish: function onFinish() {
levelUpText.destroy();
}
});
}
});
}
// Create hit animation
function createHitAnimation(x, y) {
var hitEffect = LK.getAsset('hitEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y,
alpha: 0.7,
scaleX: 0.5,
scaleY: 0.5
});
game.addChild(hitEffect);
tween(hitEffect, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 400,
onFinish: function onFinish() {
hitEffect.destroy();
}
});
}
// Track dragging state
var isDragging = false;
var draggedBodyPart = null;
var dragOffsetX = 0;
var dragOffsetY = 0;
// Mouse/touch handlers for the game
game.down = function (x, y, obj) {
// If the shop is open, don't register clicks on the game
if (isShopOpen) return;
// If stickman is dead, don't allow hitting
if (stickman.health <= 0) {
return;
}
// No longer add points on each click
// Spawn weapon at tap location and make it fly towards stickman
var weapon = new Weapon();
weapon.x = x;
weapon.y = y;
weapon.damage = currentDamage;
weapon.target = stickman;
// Add to game
game.addChild(weapon);
// Create a hit animation at the tap point
createHitAnimation(x, y);
// Play sound
LK.getSound('hit').play();
};
// Handle move events for dragging
game.move = function (x, y, obj) {
// If stickman is dead, don't allow dragging
if (stickman.health <= 0) {
isDragging = false;
draggedBodyPart = null;
return;
}
if (isDragging && draggedBodyPart && !isShopOpen) {
var localX = x - stickman.x;
var localY = y - stickman.y;
// Update dragged part position
draggedBodyPart.x = localX - dragOffsetX;
draggedBodyPart.y = localY - dragOffsetY;
// Reset velocity for smooth dragging
draggedBodyPart.velocityX = 0;
draggedBodyPart.velocityY = 0;
}
};
// Handle up events for dragging
game.up = function (x, y, obj) {
if (isDragging && draggedBodyPart && !isShopOpen) {
// On release, give the part some velocity based on movement
var releaseVelocityX = (Math.random() - 0.5) * 10;
var releaseVelocityY = (Math.random() - 0.5) * 10;
draggedBodyPart.velocityX = releaseVelocityX;
draggedBodyPart.velocityY = releaseVelocityY;
isDragging = false;
draggedBodyPart = null;
}
};
// Update function called every tick
game.update = function () {
// Clean up destroyed weapons
for (var i = 0; i < game.children.length; i++) {
var child = game.children[i];
if (child instanceof Weapon) {
if (!child.parent) {
game.children.splice(i, 1);
i--;
}
}
}
};
// Initialize the game
init();
Curved dark gray square with a white outline. In-Game asset. 2d. High contrast. No shadows
A dark gray circle with a white outline with 2 white dots as eyes. In-Game asset. 2d. High contrast. No shadows
Big light brown beard. In-Game asset. 2d. High contrast. No shadows
Cowboy hat. In-Game asset. 2d. High contrast. No shadows
Crown. In-Game asset. 2d. High contrast. No shadows
Anvil. In-Game asset. 2d. High contrast. No shadows
Mustache. In-Game asset. 2d. High contrast. No shadows
Cool sunglasses. In-Game asset. 2d. High contrast. No shadows. Facing camera
Top hat. In-Game asset. 2d. High contrast. No shadows
Party hat. In-Game asset. 2d. High contrast. No shadows
Green block with curved edges. In-Game asset. 2d. High contrast. No shadows
Baseball bat. In-Game asset. 2d. High contrast. No shadows
White glove curled into a fist. In-Game asset. 2d. High contrast. No shadows
Blue block with curved edges. In-Game asset. 2d. High contrast. No shadows
Rubber mallet. In-Game asset. 2d. High contrast. No shadows
Hammer. In-Game asset. 2d. High contrast. No shadows
Frying pan. In-Game asset. 2d. High contrast. No shadows
Stick. In-Game asset. 2d. High contrast. No shadows
Grand piano. In-Game asset. 2d. High contrast. No shadows
Gray circle with white outline. In-Game asset. 2d. High contrast. No shadows
Explosion. In-Game asset. 2d. High contrast. No shadows
Mohawk with no head. In-Game asset. 2d. High contrast. No shadows
Pirate hat. In-Game asset. 2d. High contrast. No shadows
Headband. In-Game asset. 2d. High contrast. No shadows
Propeller hat. In-Game asset. 2d. High contrast. No shadows