User prompt
Dizleri üst bacak alt ucuna bağla
User prompt
Dizleri ust bacağin ucuna yerlestir.
User prompt
Alt bacak diz baglantisini kes
User prompt
Kalçadaki animasyonu kaldır.
User prompt
Üst bacak altı ile alt bacak ustünu dizde birlestir
User prompt
Alt bacakları swap yap
User prompt
Üst bacak ve alt bacak aynı hizada bağlansin.
User prompt
Sağ Üst bacak ve sağ alt bacağı dizde bağla
User prompt
Zıplamada üst bacakların bağlantısı ve yeri degismesin
User prompt
Üst bacaklardaki bağlantı ve ik yi tüm pozlarda bozulmadan ayni uygula
User prompt
Üst bacaklardaki ik yi tüm pozlarda aynen uygula
User prompt
Alt bacakları swap yap
User prompt
Kollardaki düzgün bağlantı ve hiyerarşiyi bacaklara da uygula.
User prompt
Alt bacakları swap yap
User prompt
Bacakların bağlantiları tüm pozlarda aynı kalsin
User prompt
Bacaklarım pozisyonu ve bağlantıları hiçbir pozda değişmesin.
User prompt
Üst bacakları dışa ic bacakları içe kır.
User prompt
Alt bacakları swap yap.
User prompt
Üst bacakları swap yap.
User prompt
Üst bacaklar ters bağlı.
User prompt
Alt bacaklar ters bağlı
User prompt
Ilk pozda kalçadan eğilip kalkma animasyonu ekle.
User prompt
Kalça, üst bacak, diz, alt bacak bağlantılarını tekrar güncelle.
User prompt
Ilk pozdaki alt kollardaki animasyonu kalçaya da ekle.
User prompt
Ilk pozdaki alt kollardaki animasyonu alt bacaklara da ekle.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Drum class var Drum = Container.expand(function () { var self = Container.call(this); self.drumType = 'none'; // 'large', 'left', 'right', 'sit', 'stand' self.action = null; // function to call on tap // Set up drum asset self.setDrum = function (type) { self.drumType = type; var assetId = type === 'large' ? 'drum_large' : 'drum_medium'; var drum = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // (Drum label removed) }; // Drum tap effect self.flash = function () { tween(self, { scaleX: 1.18, scaleY: 0.92 }, { duration: 80, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 120 }); } }); }; // Touch event self.down = function (x, y, obj) { self.flash(); // Play unique sound for each drum type if (self.drumType === 'large') { LK.getSound('drum_tap_center').play(); } else if (self.drumType === 'left') { // Play with very long reverb effect if (typeof reverb !== "undefined" && reverb.add) { var leftSound = LK.getSound('drum_tap_left'); reverb.add(leftSound, { reverbTime: 3.5 }); // simulate very long reverb (if plugin supports param) leftSound.play(); } else { LK.getSound('drum_tap_left').play(); } } else if (self.drumType === 'right') { // Play with very long reverb effect if (typeof reverb !== "undefined" && reverb.add) { var rightSound = LK.getSound('drum_tap_right'); reverb.add(rightSound, { reverbTime: 3.5 }); // simulate very long reverb (if plugin supports param) rightSound.play(); } else { LK.getSound('drum_tap_right').play(); } } else if (self.drumType === 'sit') { var sitSound = LK.getSound('drum_tap_sit'); sitSound.play(); LK.setTimeout(function () { sitSound.play(); }, 120); } else if (self.drumType === 'stand') { // Play the sound with 3 fast delay effects (echoes) var base = LK.getSound('drum_tap_stand'); base.play(); LK.setTimeout(function () { base.play(); }, 45); LK.setTimeout(function () { base.play(); }, 90); } else { // No fallback sound } if (self.action) self.action(); }; return self; }); // Puppet class: articulated body with simple kinematics var Puppet = Container.expand(function () { var self = Container.call(this); // --- Body part sizes (for positioning) --- var headH = 120 * 1.5, headW = 120 * 1.5; var bodyH = 200 * 1.5, bodyW = 80 * 1.5; var upperArmH = 100 * 1.5, upperArmW = 40 * 1.5; var lowerArmH = 90 * 1.5, lowerArmW = 32 * 1.5; var upperLegH = 120 * 1.5, upperLegW = 48 * 1.5; var lowerLegH = 110 * 1.5, lowerLegW = 40 * 1.5; // --- Create body parts --- // Head var head = self.attachAsset('puppet_head', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); // Neck (between head and body, sized to match hip) var neck = self.attachAsset('puppet_neck', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, // match hip scale scaleY: 1.5 }); // Shoulders (left and right) var lShoulder = self.attachAsset('puppet_shoulder', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); var rShoulder = self.attachAsset('puppet_shoulder', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); // Body var body = self.attachAsset('puppet_body', { anchorX: 0.5, anchorY: 0, scaleX: 1.5, scaleY: 1.5 }); // Single hip (center) var hip = self.attachAsset('puppet_hip', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); // Left upper arm var lUpperArm = self.attachAsset('puppet_upperarm', { anchorX: 0.5, anchorY: 0, scaleX: 1.5, scaleY: 1.5 }); // Left elbow var lElbow = self.attachAsset('puppet_elbow', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); // Left lower arm var lLowerArm = self.attachAsset('puppet_lowerarm', { anchorX: 0.5, anchorY: 0, scaleX: 1.5, scaleY: 1.5 }); // Right upper arm var rUpperArm = self.attachAsset('puppet_upperarm', { anchorX: 0.5, anchorY: 0, scaleX: 1.5, scaleY: 1.5 }); // Right elbow var rElbow = self.attachAsset('puppet_elbow', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); // Right lower arm var rLowerArm = self.attachAsset('puppet_lowerarm', { anchorX: 0.5, anchorY: 0, scaleX: 1.5, scaleY: 1.5 }); // Left upper leg var lUpperLeg = self.attachAsset('puppet_upperleg', { anchorX: 0.5, anchorY: 0, scaleX: 1.5, scaleY: 1.5 }); // Left knee var lKnee = self.attachAsset('puppet_knee', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); // Left lower leg var lLowerLeg = self.attachAsset('puppet_lowerleg', { anchorX: 0.5, anchorY: 0, scaleX: 1.5, scaleY: 1.5 }); // Right upper leg var rUpperLeg = self.attachAsset('puppet_upperleg', { anchorX: 0.5, anchorY: 0, scaleX: 1.5, scaleY: 1.5 }); // Right knee var rKnee = self.attachAsset('puppet_knee', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); // Right lower leg var rLowerLeg = self.attachAsset('puppet_lowerleg', { anchorX: 0.5, anchorY: 0, scaleX: 1.5, scaleY: 1.5 }); // --- Initial pose state --- self.state = { vx: 0, // horizontal velocity vy: 0, // vertical velocity grounded: false, pose: 'stand', // 'stand', 'sit', 'jump' headAngle: 0, // radians, for "pull head" actions x: 0, // world position y: 0 }; // --- Positioning offsets (relative to puppet center) --- function updateBodyParts() { // Head head.x = 0; // Move head higher above the neck for a more natural look (was +10, now -30 for more separation) head.y = -bodyH / 2 - headH / 2 - 30; head.rotation = self.state.headAngle; // Neck: place between head and body, sized to match hip // Use hip asset size for neck size/placement var hipH = 44 * 1.5; // puppet_hip height * scale var neckH = hipH; neck.x = 0; neck.y = -bodyH / 2 - neckH / 2 + 38; // 38 is a small offset to visually connect neck, adjust as needed neck.rotation = 0; // Body body.x = 0; body.y = -bodyH / 2; // Arms var armY = -bodyH / 2 + 30; var armX = bodyW / 2 + upperArmW / 2 - 10; // Shoulder positions var lShoulderX = -armX; var lShoulderY = armY; var rShoulderX = armX; var rShoulderY = armY; // Place shoulder assets at shoulder positions lShoulder.x = lShoulderX; lShoulder.y = lShoulderY; rShoulder.x = rShoulderX; rShoulder.y = rShoulderY; // --- Improved arm kinematics --- // Default arm pose angles (arms down) var lUpperArmAngle = -0.35; // Hafif sola açılmış sol üst kol var rUpperArmAngle = 0.35; // Hafif sağa açılmış sağ üst kol var lLowerArmAngle = 0; // straight var rLowerArmAngle = 0; // straight // Pose-based arm animation if (self.state.pose === 'sit') { lUpperArmAngle = -1.25; rUpperArmAngle = 1.25; lLowerArmAngle = 1.5; rLowerArmAngle = -1.5; } else if (self.state.pose === 'jump') { // In jump pose, upper arms are raised up lUpperArmAngle = -1.6; // sol üst kol yukarı rUpperArmAngle = 1.6; // sağ üst kol yukarı lLowerArmAngle = 2.0; rLowerArmAngle = -2.0; // --- Maintain shoulder-elbow connection in jump pose --- var lElbowX = lUpperArm.x + Math.sin(lUpperArmAngle) * upperArmH; var lElbowY = lUpperArm.y + Math.cos(lUpperArmAngle) * upperArmH; lElbow.x = lElbowX; lElbow.y = lElbowY; var rElbowX = rUpperArm.x + Math.sin(rUpperArmAngle) * upperArmH; var rElbowY = rUpperArm.y + Math.cos(rUpperArmAngle) * upperArmH; rElbow.x = rElbowX; rElbow.y = rElbowY; } else if (self.state.pose === 'stand') { // Gentle swing based on horizontal velocity for more life lUpperArmAngle += self.state.vx * 0.01; rUpperArmAngle -= self.state.vx * 0.01; lLowerArmAngle += Math.sin(Date.now() * 0.003) * 0.08; rLowerArmAngle -= Math.sin(Date.now() * 0.003) * 0.08; } // Left upper arm: connect from shoulder to elbow lUpperArm.x = lShoulderX; lUpperArm.y = lShoulderY; lUpperArm.rotation = Math.atan2( // target: elbow - shoulder lElbow.y - lShoulderY, lElbow.x - lShoulderX) - Math.PI / 2; var lElbowX = lUpperArm.x + Math.sin(lUpperArmAngle) * upperArmH; var lElbowY = lUpperArm.y + Math.cos(lUpperArmAngle) * upperArmH; lElbow.x = lElbowX; lElbow.y = lElbowY; // Left lower arm lLowerArm.x = lElbowX; lLowerArm.y = lElbowY; lLowerArm.rotation = lLowerArmAngle; // Right upper arm: connect from shoulder to elbow rUpperArm.x = rShoulderX; rUpperArm.y = rShoulderY; rUpperArm.rotation = Math.atan2( // target: elbow - shoulder rElbow.y - rShoulderY, rElbow.x - rShoulderX) - Math.PI / 2; var rElbowX = rUpperArm.x + Math.sin(rUpperArmAngle) * upperArmH; var rElbowY = rUpperArm.y + Math.cos(rUpperArmAngle) * upperArmH; rElbow.x = rElbowX; rElbow.y = rElbowY; // Right lower arm rLowerArm.x = rElbowX; rLowerArm.y = rElbowY; rLowerArm.rotation = rLowerArmAngle; // --- Improved leg kinematics --- var legY = bodyH / 2 - 10; var legX = bodyW / 2 - 18; // Hip position (center) var hipX = 0; var hipY = legY; hip.x = hipX; hip.y = hipY; // Both legs originate from the single hip var lHipX = hipX - legX; var lHipY = hipY; var rHipX = hipX + legX; var rHipY = hipY; // Default leg pose var lUpperLegAngle = 0.18; var rUpperLegAngle = -0.18; var lLowerLegAngle = 0.25; var rLowerLegAngle = -0.25; // Pose-based leg animation if (self.state.pose === 'sit') { lUpperLegAngle = 1.35; rUpperLegAngle = 1.35; lLowerLegAngle = lUpperLegAngle - Math.PI / 2 + 0.15; rLowerLegAngle = rUpperLegAngle - Math.PI / 2 - 0.15; } else if (self.state.pose === 'jump') { lUpperLegAngle = -1.2; rUpperLegAngle = -1.2; lLowerLegAngle = lUpperLegAngle + 2.1; rLowerLegAngle = rUpperLegAngle + 2.1; } else if (self.state.pose === 'stand') { // Gentle walk cycle if moving var walk = Math.abs(self.state.vx) > 0.5 ? Math.sin(Date.now() * 0.008) * 0.25 : 0; lUpperLegAngle += walk; rUpperLegAngle -= walk; // Ayakları hafif aç: stand pozunda alt bacak açılarını büyüt lLowerLegAngle += 0.32 + walk * 0.5; // Add gentle swing to lower legs, similar to lower arms lLowerLegAngle += Math.sin(Date.now() * 0.003) * 0.08; rLowerLegAngle -= 0.32 - walk * 0.5; rLowerLegAngle -= Math.sin(Date.now() * 0.003) * 0.08; } // SWAP: Left upper leg now uses right leg's kinematics, and vice versa // Right upper leg (now on left): always connect from hip to knee var lHipX_actual = hip.x - legX; var lHipY_actual = hip.y; var lKneeX = lHipX_actual + Math.sin(rUpperLegAngle) * upperLegH; var lKneeY = lHipY_actual + Math.cos(rUpperLegAngle) * upperLegH; lUpperLeg.x = lHipX_actual; lUpperLeg.y = lHipY_actual; lUpperLeg.rotation = Math.atan2(lKneeY - lHipY_actual, lKneeX - lHipX_actual) - Math.PI / 2; // Ensure upper leg's lower end is always at the knee (always connect) lKnee.x = lUpperLeg.x + Math.sin(rUpperLegAngle) * upperLegH; lKnee.y = lUpperLeg.y + Math.cos(rUpperLegAngle) * upperLegH; // Attach lower leg at knee, rotate for more natural foot placement (SWAPPED) lLowerLeg.x = lKnee.x; lLowerLeg.y = lKnee.y; lLowerLeg.rotation = lLowerLegAngle; // SWAP: use left lower leg angle for left lower leg // Left upper leg (now on right): always connect from hip to knee var rHipX_actual = hip.x + legX; var rHipY_actual = hip.y; var rKneeX = rHipX_actual + Math.sin(lUpperLegAngle) * upperLegH; var rKneeY = rHipY_actual + Math.cos(lUpperLegAngle) * upperLegH; rUpperLeg.x = rHipX_actual; rUpperLeg.y = rHipY_actual; rUpperLeg.rotation = Math.atan2(rKneeY - rHipY_actual, rKneeX - rHipX_actual) - Math.PI / 2; // Ensure upper leg's lower end is always at the knee (always connect) rKnee.x = rUpperLeg.x + Math.sin(lUpperLegAngle) * upperLegH; rKnee.y = rUpperLeg.y + Math.cos(lUpperLegAngle) * upperLegH; // Attach lower leg at knee, rotate for more natural foot placement (SWAPPED) rLowerLeg.x = rKnee.x; rLowerLeg.y = rKnee.y; rLowerLeg.rotation = rLowerLegAngle; // SWAP: use right lower leg angle for right lower leg // Pose adjustments if (self.state.pose === 'sit') { // Sitting: thighs forward, knees bent, shins down lUpperLeg.x = hip.x - legX; lUpperLeg.y = hip.y; lUpperLeg.rotation = 1.35; // Ensure upper leg's lower end is always at the knee (always connect) lKnee.x = lUpperLeg.x + Math.sin(lUpperLeg.rotation) * upperLegH; lKnee.y = lUpperLeg.y + Math.cos(lUpperLeg.rotation) * upperLegH; lLowerLeg.x = lKnee.x; lLowerLeg.y = lKnee.y; lLowerLeg.rotation = lUpperLeg.rotation - Math.PI / 2 + 0.15; rUpperLeg.x = hip.x + legX; rUpperLeg.y = hip.y; rUpperLeg.rotation = 1.35; // Ensure upper leg's lower end is always at the knee (always connect) rKnee.x = rUpperLeg.x + Math.sin(rUpperLeg.rotation) * upperLegH; rKnee.y = rUpperLeg.y + Math.cos(rUpperLeg.rotation) * upperLegH; rLowerLeg.x = rKnee.x; rLowerLeg.y = rKnee.y; rLowerLeg.rotation = rUpperLeg.rotation - Math.PI / 2 - 0.15; body.rotation = -0.18; } else if (self.state.pose === 'jump') { // Jump: legs up, knees bent, elbows and knees more pronounced lUpperLeg.x = hip.x - legX; lUpperLeg.y = hip.y; lUpperLeg.rotation = -1.2; // Ensure upper leg's lower end is always at the knee (always connect) lKnee.x = lUpperLeg.x + Math.sin(lUpperLeg.rotation) * upperLegH; lKnee.y = lUpperLeg.y + Math.cos(lUpperLeg.rotation) * upperLegH; lLowerLeg.x = lKnee.x; lLowerLeg.y = lKnee.y; lLowerLeg.rotation = lUpperLeg.rotation + 2.1; rUpperLeg.x = hip.x + legX; rUpperLeg.y = hip.y; rUpperLeg.rotation = -1.2; // Ensure upper leg's lower end is always at the knee (always connect) rKnee.x = rUpperLeg.x + Math.sin(rUpperLeg.rotation) * upperLegH; rKnee.y = rUpperLeg.y + Math.cos(rUpperLeg.rotation) * upperLegH; rLowerLeg.x = rKnee.x; rLowerLeg.y = rKnee.y; rLowerLeg.rotation = rUpperLeg.rotation + 2.1; // Elbows more bent in jump // Keep upper arms at default (down), but elbows and lower arms follow articulation // (Shoulder-elbow already set above for jump pose) lLowerArm.x = lElbow.x; lLowerArm.y = lElbow.y; lLowerArm.rotation = 2.0; rLowerArm.x = rElbow.x; rLowerArm.y = rElbow.y; rLowerArm.rotation = -2.0; body.rotation = 0.12; } else { body.rotation = 0; } } // --- Public puppet actions --- self.pullHeadLeft = function () { // Pull head left, add leftward velocity if (self.state.grounded) { self.state.vx -= 12; self.state.headAngle = -0.5; tween(self.state, { headAngle: 0 }, { duration: 400, easing: tween.easeOut }); } }; self.pullHeadRight = function () { if (self.state.grounded) { self.state.vx += 12; self.state.headAngle = 0.5; tween(self.state, { headAngle: 0 }, { duration: 400, easing: tween.easeOut }); } }; self.jump = function () { if (self.state.grounded) { self.state.vy = -48; // Increased jump velocity for higher jump self.state.grounded = false; self.state.pose = 'jump'; tween(self.state, {}, { duration: 0, onFinish: function onFinish() { // After 400ms, return to stand pose LK.setTimeout(function () { self.state.pose = 'stand'; }, 400); } }); } }; self.sit = function () { if (self.state.grounded && self.state.pose !== 'sit') { self.state.pose = 'sit'; // After 600ms, stand up automatically LK.setTimeout(function () { self.stand(); }, 600); } }; self.stand = function () { if (self.state.grounded && self.state.pose !== 'stand') { self.state.pose = 'stand'; } }; // --- Physics update --- self.update = function () { // Gravity if (!self.state.grounded) { self.state.vy += 2.2; // gravity if (self.state.vy > 40) self.state.vy = 40; } // Friction if (self.state.grounded) { self.state.vx *= 0.82; if (Math.abs(self.state.vx) < 0.5) self.state.vx = 0; } else { self.state.vx *= 0.98; } // Move self.state.x += self.state.vx; self.state.y += self.state.vy; // Clamp to world bounds if (self.state.x < 80) self.state.x = 80; if (self.state.x > 2048 - 80) self.state.x = 2048 - 80; // --- Ava: Adjust puppet Y so feet are above ground visually --- // The puppet's feet are at y + (bodyH/2) + upperLegH + lowerLegH - 10 - 10 // ground.y = 2732 - 80, ground height = 80 // So, puppet.y + (bodyH/2) + upperLegH + lowerLegH - 20 = 2732 - 80 // puppet.y = 2732 - 80 - (bodyH/2) - upperLegH - lowerLegH + 20 var puppetFeetOffset = bodyH / 2 + upperLegH + lowerLegH - 20; var puppetGroundY = 2732 - 80 - puppetFeetOffset; if (self.state.y > puppetGroundY) self.state.y = puppetGroundY; // above ground // Ground collision if (self.state.y >= puppetGroundY) { self.state.y = puppetGroundY; self.state.vy = 0; self.state.grounded = true; if (self.state.pose === 'jump') self.state.pose = 'stand'; } else { self.state.grounded = false; } // Apply to container self.x = self.state.x; self.y = self.state.y; updateBodyParts(); }; // --- Start position --- self.state.x = 600; // Moved puppet further right (was 180) // --- Ava: Set initial Y so feet are above ground visually --- var puppetFeetOffset = bodyH / 2 + upperLegH + lowerLegH - 20; self.state.y = 2732 - 80 - puppetFeetOffset; updateBodyParts(); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Define and initialize the reverb object before using it // Win/lose sound // Drum tap sound // Finish sign // Ground // Drums // Puppet body parts (shapes) // --- World setup --- // Apply reverb to all drum and win/lose sounds // Ava: background asset var reverb = { add: function add(sound) { // Dummy implementation: does nothing, but prevents errors // In a real environment, this would apply a reverb effect to the sound } }; reverb.add(LK.getSound('drum_tap_center')); reverb.add(LK.getSound('drum_tap_left')); reverb.add(LK.getSound('drum_tap_right')); reverb.add(LK.getSound('drum_tap_sit')); reverb.add(LK.getSound('drum_tap_stand')); reverb.add(LK.getSound('lose')); reverb.add(LK.getSound('win')); // --- Background music playlist (4 tracks, play in random order) --- var musicTracks = [{ id: 'musicId', volume: 0.1, start: 0, end: 1 }, { id: 'musicId2', volume: 0.1, start: 0, end: 1 }, { id: 'musicId3', volume: 0.1, start: 0, end: 1 }, { id: 'musicId4', volume: 0.1, start: 0, end: 1 }]; // Shuffle helper (Fisher-Yates) function shuffleArray(arr) { for (var i = arr.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } // Start with a shuffled playlist var shuffledMusicOrder = []; function reshuffleMusicOrder() { shuffledMusicOrder = []; for (var i = 0; i < musicTracks.length; ++i) shuffledMusicOrder.push(i); shuffleArray(shuffledMusicOrder); } reshuffleMusicOrder(); var currentMusicOrderIndex = 0; function playNextMusic() { // If we've played all, reshuffle for a new random order if (currentMusicOrderIndex >= shuffledMusicOrder.length) { reshuffleMusicOrder(); currentMusicOrderIndex = 0; } var trackIndex = shuffledMusicOrder[currentMusicOrderIndex]; var track = musicTracks[trackIndex]; LK.playMusic(track.id, { loop: false, volume: track.volume, start: track.start, end: track.end, onFinish: function onFinish() { currentMusicOrderIndex++; playNextMusic(); } }); } playNextMusic(); game.setBackgroundColor(0x000000); // --- Background --- // Ava: Add background image behind all elements var background = LK.getAsset('background', { anchorX: 0, anchorY: 0, x: 0, y: 0, scaleX: 1, scaleY: 1 }); game.addChild(background); // Ground var ground = LK.getAsset('ground', { anchorX: 0, anchorY: 0 }); ground.x = 0; ground.y = 2732 - 80; game.addChild(ground); // Finish sign var finishSign = LK.getAsset('finish_sign', { anchorX: 0.5, anchorY: 1, scaleX: 3, scaleY: 6 // 2x as tall as before }); finishSign.x = 2048 - 120; finishSign.y = 2732 - 80; game.addChild(finishSign); // Finish text removed // --- Puppet --- var puppet = new Puppet(); game.addChild(puppet); // --- Drums --- var drums = []; // Drum positions (relative to center) // Move drums higher: set drumCenterY above puppet's head var drumCenterX = 2048 / 2, drumCenterY = 900; // was 2732 / 2 + 220, now much higher (puppet head is around y=~1200) var drumRadius = 220 * 3; var angleList = [{ type: 'left', angle: -Math.PI / 2 - 0.7 }, { type: 'right', angle: -Math.PI / 2 + 0.7 }, { type: 'sit', angle: Math.PI / 2 + 0.7 }, { type: 'stand', angle: Math.PI / 2 - 0.7 }]; // --- Drum action pool --- // Each drum will have all actions, only their order will be shuffled // Ava: Increase the number of right movement actions (pullHeadRight) in the action pool var puppetActions = [function () { puppet.pullHeadLeft(); }, function () { puppet.pullHeadRight(); }, function () { puppet.pullHeadRight(); }, function () { puppet.jump(); }, function () { puppet.sit(); }, function () { puppet.stand(); }]; // Large center drum (fixed action sequence) var drumJump = new Drum(); drumJump.setDrum('large'); drumJump.x = drumCenterX; drumJump.y = drumCenterY; // Assign a fixed sequence of all 5 actions, and cycle through them drumJump._actionSequence = puppetActions.slice(); drumJump._actionIndex = 0; drumJump.action = function () { drumJump._actionSequence[drumJump._actionIndex](); drumJump._actionIndex = (drumJump._actionIndex + 1) % drumJump._actionSequence.length; }; game.addChild(drumJump); drums.push(drumJump); // Four medium drums for (var i = 0; i < angleList.length; ++i) { var d = new Drum(); d.setDrum(angleList[i].type); d.x = drumCenterX + Math.cos(angleList[i].angle) * drumRadius; d.y = drumCenterY + Math.sin(angleList[i].angle) * drumRadius; // Each drum gets a fixed sequence of all 5 actions, and cycles through them d._actionSequence = puppetActions.slice(); d._actionIndex = 0; d.action = function (drum) { return function () { drum._actionSequence[drum._actionIndex](); drum._actionIndex = (drum._actionIndex + 1) % drum._actionSequence.length; }; }(d); game.addChild(d); drums.push(d); } // --- Timer --- var timeLeft = 60; function formatTimeDigital(secs) { var m = Math.floor(secs / 60); var s = secs % 60; var mm = m < 10 ? "0" + m : "" + m; var ss = s < 10 ? "0" + s : "" + s; return mm + ":" + ss; } var timerText = new Text2(formatTimeDigital(60), { size: 120, fill: '#ffffff' }); timerText.anchor.set(0.5, 0); LK.gui.top.addChild(timerText); // --- Score/Win state --- var gameEnded = false; // --- Timer logic --- var timerInterval = LK.setInterval(function () { if (gameEnded) return; timeLeft -= 1; if (timeLeft < 0) timeLeft = 0; timerText.setText(formatTimeDigital(timeLeft)); if (timeLeft === 0 && !gameEnded) { gameEnded = true; LK.getSound('lose').play(); LK.effects.flashScreen(0xff0000, 800); LK.showGameOver(); } }, 1000); // --- Game update --- game.update = function () { if (gameEnded) return; puppet.update(); // Win condition: puppet body collides with finish sign using intersects if (!gameEnded && puppet.intersects(finishSign)) { gameEnded = true; LK.setScore(timeLeft); // Set score to remaining seconds LK.getSound('win').play(); LK.effects.flashScreen(0x44c767, 800); LK.showYouWin(); } }; // --- Clean up on game over --- game.onDestroy = function () { LK.clearInterval(timerInterval); }; // --- Prevent accidental tap on top left (menu) --- /* No elements are placed at (0,0)-(100,100) */ // --- UX: drums are always on top --- for (var i = 0; i < drums.length; ++i) { game.addChild(drums[i]); }
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Drum class
var Drum = Container.expand(function () {
var self = Container.call(this);
self.drumType = 'none'; // 'large', 'left', 'right', 'sit', 'stand'
self.action = null; // function to call on tap
// Set up drum asset
self.setDrum = function (type) {
self.drumType = type;
var assetId = type === 'large' ? 'drum_large' : 'drum_medium';
var drum = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// (Drum label removed)
};
// Drum tap effect
self.flash = function () {
tween(self, {
scaleX: 1.18,
scaleY: 0.92
}, {
duration: 80,
onFinish: function onFinish() {
tween(self, {
scaleX: 1,
scaleY: 1
}, {
duration: 120
});
}
});
};
// Touch event
self.down = function (x, y, obj) {
self.flash();
// Play unique sound for each drum type
if (self.drumType === 'large') {
LK.getSound('drum_tap_center').play();
} else if (self.drumType === 'left') {
// Play with very long reverb effect
if (typeof reverb !== "undefined" && reverb.add) {
var leftSound = LK.getSound('drum_tap_left');
reverb.add(leftSound, {
reverbTime: 3.5
}); // simulate very long reverb (if plugin supports param)
leftSound.play();
} else {
LK.getSound('drum_tap_left').play();
}
} else if (self.drumType === 'right') {
// Play with very long reverb effect
if (typeof reverb !== "undefined" && reverb.add) {
var rightSound = LK.getSound('drum_tap_right');
reverb.add(rightSound, {
reverbTime: 3.5
}); // simulate very long reverb (if plugin supports param)
rightSound.play();
} else {
LK.getSound('drum_tap_right').play();
}
} else if (self.drumType === 'sit') {
var sitSound = LK.getSound('drum_tap_sit');
sitSound.play();
LK.setTimeout(function () {
sitSound.play();
}, 120);
} else if (self.drumType === 'stand') {
// Play the sound with 3 fast delay effects (echoes)
var base = LK.getSound('drum_tap_stand');
base.play();
LK.setTimeout(function () {
base.play();
}, 45);
LK.setTimeout(function () {
base.play();
}, 90);
} else {
// No fallback sound
}
if (self.action) self.action();
};
return self;
});
// Puppet class: articulated body with simple kinematics
var Puppet = Container.expand(function () {
var self = Container.call(this);
// --- Body part sizes (for positioning) ---
var headH = 120 * 1.5,
headW = 120 * 1.5;
var bodyH = 200 * 1.5,
bodyW = 80 * 1.5;
var upperArmH = 100 * 1.5,
upperArmW = 40 * 1.5;
var lowerArmH = 90 * 1.5,
lowerArmW = 32 * 1.5;
var upperLegH = 120 * 1.5,
upperLegW = 48 * 1.5;
var lowerLegH = 110 * 1.5,
lowerLegW = 40 * 1.5;
// --- Create body parts ---
// Head
var head = self.attachAsset('puppet_head', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
// Neck (between head and body, sized to match hip)
var neck = self.attachAsset('puppet_neck', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
// match hip scale
scaleY: 1.5
});
// Shoulders (left and right)
var lShoulder = self.attachAsset('puppet_shoulder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
var rShoulder = self.attachAsset('puppet_shoulder', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
// Body
var body = self.attachAsset('puppet_body', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Single hip (center)
var hip = self.attachAsset('puppet_hip', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
// Left upper arm
var lUpperArm = self.attachAsset('puppet_upperarm', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Left elbow
var lElbow = self.attachAsset('puppet_elbow', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
// Left lower arm
var lLowerArm = self.attachAsset('puppet_lowerarm', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Right upper arm
var rUpperArm = self.attachAsset('puppet_upperarm', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Right elbow
var rElbow = self.attachAsset('puppet_elbow', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
// Right lower arm
var rLowerArm = self.attachAsset('puppet_lowerarm', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Left upper leg
var lUpperLeg = self.attachAsset('puppet_upperleg', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Left knee
var lKnee = self.attachAsset('puppet_knee', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
// Left lower leg
var lLowerLeg = self.attachAsset('puppet_lowerleg', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Right upper leg
var rUpperLeg = self.attachAsset('puppet_upperleg', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// Right knee
var rKnee = self.attachAsset('puppet_knee', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
// Right lower leg
var rLowerLeg = self.attachAsset('puppet_lowerleg', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1.5,
scaleY: 1.5
});
// --- Initial pose state ---
self.state = {
vx: 0,
// horizontal velocity
vy: 0,
// vertical velocity
grounded: false,
pose: 'stand',
// 'stand', 'sit', 'jump'
headAngle: 0,
// radians, for "pull head" actions
x: 0,
// world position
y: 0
};
// --- Positioning offsets (relative to puppet center) ---
function updateBodyParts() {
// Head
head.x = 0;
// Move head higher above the neck for a more natural look (was +10, now -30 for more separation)
head.y = -bodyH / 2 - headH / 2 - 30;
head.rotation = self.state.headAngle;
// Neck: place between head and body, sized to match hip
// Use hip asset size for neck size/placement
var hipH = 44 * 1.5; // puppet_hip height * scale
var neckH = hipH;
neck.x = 0;
neck.y = -bodyH / 2 - neckH / 2 + 38; // 38 is a small offset to visually connect neck, adjust as needed
neck.rotation = 0;
// Body
body.x = 0;
body.y = -bodyH / 2;
// Arms
var armY = -bodyH / 2 + 30;
var armX = bodyW / 2 + upperArmW / 2 - 10;
// Shoulder positions
var lShoulderX = -armX;
var lShoulderY = armY;
var rShoulderX = armX;
var rShoulderY = armY;
// Place shoulder assets at shoulder positions
lShoulder.x = lShoulderX;
lShoulder.y = lShoulderY;
rShoulder.x = rShoulderX;
rShoulder.y = rShoulderY;
// --- Improved arm kinematics ---
// Default arm pose angles (arms down)
var lUpperArmAngle = -0.35; // Hafif sola açılmış sol üst kol
var rUpperArmAngle = 0.35; // Hafif sağa açılmış sağ üst kol
var lLowerArmAngle = 0; // straight
var rLowerArmAngle = 0; // straight
// Pose-based arm animation
if (self.state.pose === 'sit') {
lUpperArmAngle = -1.25;
rUpperArmAngle = 1.25;
lLowerArmAngle = 1.5;
rLowerArmAngle = -1.5;
} else if (self.state.pose === 'jump') {
// In jump pose, upper arms are raised up
lUpperArmAngle = -1.6; // sol üst kol yukarı
rUpperArmAngle = 1.6; // sağ üst kol yukarı
lLowerArmAngle = 2.0;
rLowerArmAngle = -2.0;
// --- Maintain shoulder-elbow connection in jump pose ---
var lElbowX = lUpperArm.x + Math.sin(lUpperArmAngle) * upperArmH;
var lElbowY = lUpperArm.y + Math.cos(lUpperArmAngle) * upperArmH;
lElbow.x = lElbowX;
lElbow.y = lElbowY;
var rElbowX = rUpperArm.x + Math.sin(rUpperArmAngle) * upperArmH;
var rElbowY = rUpperArm.y + Math.cos(rUpperArmAngle) * upperArmH;
rElbow.x = rElbowX;
rElbow.y = rElbowY;
} else if (self.state.pose === 'stand') {
// Gentle swing based on horizontal velocity for more life
lUpperArmAngle += self.state.vx * 0.01;
rUpperArmAngle -= self.state.vx * 0.01;
lLowerArmAngle += Math.sin(Date.now() * 0.003) * 0.08;
rLowerArmAngle -= Math.sin(Date.now() * 0.003) * 0.08;
}
// Left upper arm: connect from shoulder to elbow
lUpperArm.x = lShoulderX;
lUpperArm.y = lShoulderY;
lUpperArm.rotation = Math.atan2(
// target: elbow - shoulder
lElbow.y - lShoulderY, lElbow.x - lShoulderX) - Math.PI / 2;
var lElbowX = lUpperArm.x + Math.sin(lUpperArmAngle) * upperArmH;
var lElbowY = lUpperArm.y + Math.cos(lUpperArmAngle) * upperArmH;
lElbow.x = lElbowX;
lElbow.y = lElbowY;
// Left lower arm
lLowerArm.x = lElbowX;
lLowerArm.y = lElbowY;
lLowerArm.rotation = lLowerArmAngle;
// Right upper arm: connect from shoulder to elbow
rUpperArm.x = rShoulderX;
rUpperArm.y = rShoulderY;
rUpperArm.rotation = Math.atan2(
// target: elbow - shoulder
rElbow.y - rShoulderY, rElbow.x - rShoulderX) - Math.PI / 2;
var rElbowX = rUpperArm.x + Math.sin(rUpperArmAngle) * upperArmH;
var rElbowY = rUpperArm.y + Math.cos(rUpperArmAngle) * upperArmH;
rElbow.x = rElbowX;
rElbow.y = rElbowY;
// Right lower arm
rLowerArm.x = rElbowX;
rLowerArm.y = rElbowY;
rLowerArm.rotation = rLowerArmAngle;
// --- Improved leg kinematics ---
var legY = bodyH / 2 - 10;
var legX = bodyW / 2 - 18;
// Hip position (center)
var hipX = 0;
var hipY = legY;
hip.x = hipX;
hip.y = hipY;
// Both legs originate from the single hip
var lHipX = hipX - legX;
var lHipY = hipY;
var rHipX = hipX + legX;
var rHipY = hipY;
// Default leg pose
var lUpperLegAngle = 0.18;
var rUpperLegAngle = -0.18;
var lLowerLegAngle = 0.25;
var rLowerLegAngle = -0.25;
// Pose-based leg animation
if (self.state.pose === 'sit') {
lUpperLegAngle = 1.35;
rUpperLegAngle = 1.35;
lLowerLegAngle = lUpperLegAngle - Math.PI / 2 + 0.15;
rLowerLegAngle = rUpperLegAngle - Math.PI / 2 - 0.15;
} else if (self.state.pose === 'jump') {
lUpperLegAngle = -1.2;
rUpperLegAngle = -1.2;
lLowerLegAngle = lUpperLegAngle + 2.1;
rLowerLegAngle = rUpperLegAngle + 2.1;
} else if (self.state.pose === 'stand') {
// Gentle walk cycle if moving
var walk = Math.abs(self.state.vx) > 0.5 ? Math.sin(Date.now() * 0.008) * 0.25 : 0;
lUpperLegAngle += walk;
rUpperLegAngle -= walk;
// Ayakları hafif aç: stand pozunda alt bacak açılarını büyüt
lLowerLegAngle += 0.32 + walk * 0.5;
// Add gentle swing to lower legs, similar to lower arms
lLowerLegAngle += Math.sin(Date.now() * 0.003) * 0.08;
rLowerLegAngle -= 0.32 - walk * 0.5;
rLowerLegAngle -= Math.sin(Date.now() * 0.003) * 0.08;
}
// SWAP: Left upper leg now uses right leg's kinematics, and vice versa
// Right upper leg (now on left): always connect from hip to knee
var lHipX_actual = hip.x - legX;
var lHipY_actual = hip.y;
var lKneeX = lHipX_actual + Math.sin(rUpperLegAngle) * upperLegH;
var lKneeY = lHipY_actual + Math.cos(rUpperLegAngle) * upperLegH;
lUpperLeg.x = lHipX_actual;
lUpperLeg.y = lHipY_actual;
lUpperLeg.rotation = Math.atan2(lKneeY - lHipY_actual, lKneeX - lHipX_actual) - Math.PI / 2;
// Ensure upper leg's lower end is always at the knee (always connect)
lKnee.x = lUpperLeg.x + Math.sin(rUpperLegAngle) * upperLegH;
lKnee.y = lUpperLeg.y + Math.cos(rUpperLegAngle) * upperLegH;
// Attach lower leg at knee, rotate for more natural foot placement (SWAPPED)
lLowerLeg.x = lKnee.x;
lLowerLeg.y = lKnee.y;
lLowerLeg.rotation = lLowerLegAngle; // SWAP: use left lower leg angle for left lower leg
// Left upper leg (now on right): always connect from hip to knee
var rHipX_actual = hip.x + legX;
var rHipY_actual = hip.y;
var rKneeX = rHipX_actual + Math.sin(lUpperLegAngle) * upperLegH;
var rKneeY = rHipY_actual + Math.cos(lUpperLegAngle) * upperLegH;
rUpperLeg.x = rHipX_actual;
rUpperLeg.y = rHipY_actual;
rUpperLeg.rotation = Math.atan2(rKneeY - rHipY_actual, rKneeX - rHipX_actual) - Math.PI / 2;
// Ensure upper leg's lower end is always at the knee (always connect)
rKnee.x = rUpperLeg.x + Math.sin(lUpperLegAngle) * upperLegH;
rKnee.y = rUpperLeg.y + Math.cos(lUpperLegAngle) * upperLegH;
// Attach lower leg at knee, rotate for more natural foot placement (SWAPPED)
rLowerLeg.x = rKnee.x;
rLowerLeg.y = rKnee.y;
rLowerLeg.rotation = rLowerLegAngle; // SWAP: use right lower leg angle for right lower leg
// Pose adjustments
if (self.state.pose === 'sit') {
// Sitting: thighs forward, knees bent, shins down
lUpperLeg.x = hip.x - legX;
lUpperLeg.y = hip.y;
lUpperLeg.rotation = 1.35;
// Ensure upper leg's lower end is always at the knee (always connect)
lKnee.x = lUpperLeg.x + Math.sin(lUpperLeg.rotation) * upperLegH;
lKnee.y = lUpperLeg.y + Math.cos(lUpperLeg.rotation) * upperLegH;
lLowerLeg.x = lKnee.x;
lLowerLeg.y = lKnee.y;
lLowerLeg.rotation = lUpperLeg.rotation - Math.PI / 2 + 0.15;
rUpperLeg.x = hip.x + legX;
rUpperLeg.y = hip.y;
rUpperLeg.rotation = 1.35;
// Ensure upper leg's lower end is always at the knee (always connect)
rKnee.x = rUpperLeg.x + Math.sin(rUpperLeg.rotation) * upperLegH;
rKnee.y = rUpperLeg.y + Math.cos(rUpperLeg.rotation) * upperLegH;
rLowerLeg.x = rKnee.x;
rLowerLeg.y = rKnee.y;
rLowerLeg.rotation = rUpperLeg.rotation - Math.PI / 2 - 0.15;
body.rotation = -0.18;
} else if (self.state.pose === 'jump') {
// Jump: legs up, knees bent, elbows and knees more pronounced
lUpperLeg.x = hip.x - legX;
lUpperLeg.y = hip.y;
lUpperLeg.rotation = -1.2;
// Ensure upper leg's lower end is always at the knee (always connect)
lKnee.x = lUpperLeg.x + Math.sin(lUpperLeg.rotation) * upperLegH;
lKnee.y = lUpperLeg.y + Math.cos(lUpperLeg.rotation) * upperLegH;
lLowerLeg.x = lKnee.x;
lLowerLeg.y = lKnee.y;
lLowerLeg.rotation = lUpperLeg.rotation + 2.1;
rUpperLeg.x = hip.x + legX;
rUpperLeg.y = hip.y;
rUpperLeg.rotation = -1.2;
// Ensure upper leg's lower end is always at the knee (always connect)
rKnee.x = rUpperLeg.x + Math.sin(rUpperLeg.rotation) * upperLegH;
rKnee.y = rUpperLeg.y + Math.cos(rUpperLeg.rotation) * upperLegH;
rLowerLeg.x = rKnee.x;
rLowerLeg.y = rKnee.y;
rLowerLeg.rotation = rUpperLeg.rotation + 2.1;
// Elbows more bent in jump
// Keep upper arms at default (down), but elbows and lower arms follow articulation
// (Shoulder-elbow already set above for jump pose)
lLowerArm.x = lElbow.x;
lLowerArm.y = lElbow.y;
lLowerArm.rotation = 2.0;
rLowerArm.x = rElbow.x;
rLowerArm.y = rElbow.y;
rLowerArm.rotation = -2.0;
body.rotation = 0.12;
} else {
body.rotation = 0;
}
}
// --- Public puppet actions ---
self.pullHeadLeft = function () {
// Pull head left, add leftward velocity
if (self.state.grounded) {
self.state.vx -= 12;
self.state.headAngle = -0.5;
tween(self.state, {
headAngle: 0
}, {
duration: 400,
easing: tween.easeOut
});
}
};
self.pullHeadRight = function () {
if (self.state.grounded) {
self.state.vx += 12;
self.state.headAngle = 0.5;
tween(self.state, {
headAngle: 0
}, {
duration: 400,
easing: tween.easeOut
});
}
};
self.jump = function () {
if (self.state.grounded) {
self.state.vy = -48; // Increased jump velocity for higher jump
self.state.grounded = false;
self.state.pose = 'jump';
tween(self.state, {}, {
duration: 0,
onFinish: function onFinish() {
// After 400ms, return to stand pose
LK.setTimeout(function () {
self.state.pose = 'stand';
}, 400);
}
});
}
};
self.sit = function () {
if (self.state.grounded && self.state.pose !== 'sit') {
self.state.pose = 'sit';
// After 600ms, stand up automatically
LK.setTimeout(function () {
self.stand();
}, 600);
}
};
self.stand = function () {
if (self.state.grounded && self.state.pose !== 'stand') {
self.state.pose = 'stand';
}
};
// --- Physics update ---
self.update = function () {
// Gravity
if (!self.state.grounded) {
self.state.vy += 2.2; // gravity
if (self.state.vy > 40) self.state.vy = 40;
}
// Friction
if (self.state.grounded) {
self.state.vx *= 0.82;
if (Math.abs(self.state.vx) < 0.5) self.state.vx = 0;
} else {
self.state.vx *= 0.98;
}
// Move
self.state.x += self.state.vx;
self.state.y += self.state.vy;
// Clamp to world bounds
if (self.state.x < 80) self.state.x = 80;
if (self.state.x > 2048 - 80) self.state.x = 2048 - 80;
// --- Ava: Adjust puppet Y so feet are above ground visually ---
// The puppet's feet are at y + (bodyH/2) + upperLegH + lowerLegH - 10 - 10
// ground.y = 2732 - 80, ground height = 80
// So, puppet.y + (bodyH/2) + upperLegH + lowerLegH - 20 = 2732 - 80
// puppet.y = 2732 - 80 - (bodyH/2) - upperLegH - lowerLegH + 20
var puppetFeetOffset = bodyH / 2 + upperLegH + lowerLegH - 20;
var puppetGroundY = 2732 - 80 - puppetFeetOffset;
if (self.state.y > puppetGroundY) self.state.y = puppetGroundY; // above ground
// Ground collision
if (self.state.y >= puppetGroundY) {
self.state.y = puppetGroundY;
self.state.vy = 0;
self.state.grounded = true;
if (self.state.pose === 'jump') self.state.pose = 'stand';
} else {
self.state.grounded = false;
}
// Apply to container
self.x = self.state.x;
self.y = self.state.y;
updateBodyParts();
};
// --- Start position ---
self.state.x = 600; // Moved puppet further right (was 180)
// --- Ava: Set initial Y so feet are above ground visually ---
var puppetFeetOffset = bodyH / 2 + upperLegH + lowerLegH - 20;
self.state.y = 2732 - 80 - puppetFeetOffset;
updateBodyParts();
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Define and initialize the reverb object before using it
// Win/lose sound
// Drum tap sound
// Finish sign
// Ground
// Drums
// Puppet body parts (shapes)
// --- World setup ---
// Apply reverb to all drum and win/lose sounds
// Ava: background asset
var reverb = {
add: function add(sound) {
// Dummy implementation: does nothing, but prevents errors
// In a real environment, this would apply a reverb effect to the sound
}
};
reverb.add(LK.getSound('drum_tap_center'));
reverb.add(LK.getSound('drum_tap_left'));
reverb.add(LK.getSound('drum_tap_right'));
reverb.add(LK.getSound('drum_tap_sit'));
reverb.add(LK.getSound('drum_tap_stand'));
reverb.add(LK.getSound('lose'));
reverb.add(LK.getSound('win'));
// --- Background music playlist (4 tracks, play in random order) ---
var musicTracks = [{
id: 'musicId',
volume: 0.1,
start: 0,
end: 1
}, {
id: 'musicId2',
volume: 0.1,
start: 0,
end: 1
}, {
id: 'musicId3',
volume: 0.1,
start: 0,
end: 1
}, {
id: 'musicId4',
volume: 0.1,
start: 0,
end: 1
}];
// Shuffle helper (Fisher-Yates)
function shuffleArray(arr) {
for (var i = arr.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
// Start with a shuffled playlist
var shuffledMusicOrder = [];
function reshuffleMusicOrder() {
shuffledMusicOrder = [];
for (var i = 0; i < musicTracks.length; ++i) shuffledMusicOrder.push(i);
shuffleArray(shuffledMusicOrder);
}
reshuffleMusicOrder();
var currentMusicOrderIndex = 0;
function playNextMusic() {
// If we've played all, reshuffle for a new random order
if (currentMusicOrderIndex >= shuffledMusicOrder.length) {
reshuffleMusicOrder();
currentMusicOrderIndex = 0;
}
var trackIndex = shuffledMusicOrder[currentMusicOrderIndex];
var track = musicTracks[trackIndex];
LK.playMusic(track.id, {
loop: false,
volume: track.volume,
start: track.start,
end: track.end,
onFinish: function onFinish() {
currentMusicOrderIndex++;
playNextMusic();
}
});
}
playNextMusic();
game.setBackgroundColor(0x000000);
// --- Background ---
// Ava: Add background image behind all elements
var background = LK.getAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0,
scaleX: 1,
scaleY: 1
});
game.addChild(background);
// Ground
var ground = LK.getAsset('ground', {
anchorX: 0,
anchorY: 0
});
ground.x = 0;
ground.y = 2732 - 80;
game.addChild(ground);
// Finish sign
var finishSign = LK.getAsset('finish_sign', {
anchorX: 0.5,
anchorY: 1,
scaleX: 3,
scaleY: 6 // 2x as tall as before
});
finishSign.x = 2048 - 120;
finishSign.y = 2732 - 80;
game.addChild(finishSign);
// Finish text removed
// --- Puppet ---
var puppet = new Puppet();
game.addChild(puppet);
// --- Drums ---
var drums = [];
// Drum positions (relative to center)
// Move drums higher: set drumCenterY above puppet's head
var drumCenterX = 2048 / 2,
drumCenterY = 900; // was 2732 / 2 + 220, now much higher (puppet head is around y=~1200)
var drumRadius = 220 * 3;
var angleList = [{
type: 'left',
angle: -Math.PI / 2 - 0.7
}, {
type: 'right',
angle: -Math.PI / 2 + 0.7
}, {
type: 'sit',
angle: Math.PI / 2 + 0.7
}, {
type: 'stand',
angle: Math.PI / 2 - 0.7
}];
// --- Drum action pool ---
// Each drum will have all actions, only their order will be shuffled
// Ava: Increase the number of right movement actions (pullHeadRight) in the action pool
var puppetActions = [function () {
puppet.pullHeadLeft();
}, function () {
puppet.pullHeadRight();
}, function () {
puppet.pullHeadRight();
}, function () {
puppet.jump();
}, function () {
puppet.sit();
}, function () {
puppet.stand();
}];
// Large center drum (fixed action sequence)
var drumJump = new Drum();
drumJump.setDrum('large');
drumJump.x = drumCenterX;
drumJump.y = drumCenterY;
// Assign a fixed sequence of all 5 actions, and cycle through them
drumJump._actionSequence = puppetActions.slice();
drumJump._actionIndex = 0;
drumJump.action = function () {
drumJump._actionSequence[drumJump._actionIndex]();
drumJump._actionIndex = (drumJump._actionIndex + 1) % drumJump._actionSequence.length;
};
game.addChild(drumJump);
drums.push(drumJump);
// Four medium drums
for (var i = 0; i < angleList.length; ++i) {
var d = new Drum();
d.setDrum(angleList[i].type);
d.x = drumCenterX + Math.cos(angleList[i].angle) * drumRadius;
d.y = drumCenterY + Math.sin(angleList[i].angle) * drumRadius;
// Each drum gets a fixed sequence of all 5 actions, and cycles through them
d._actionSequence = puppetActions.slice();
d._actionIndex = 0;
d.action = function (drum) {
return function () {
drum._actionSequence[drum._actionIndex]();
drum._actionIndex = (drum._actionIndex + 1) % drum._actionSequence.length;
};
}(d);
game.addChild(d);
drums.push(d);
}
// --- Timer ---
var timeLeft = 60;
function formatTimeDigital(secs) {
var m = Math.floor(secs / 60);
var s = secs % 60;
var mm = m < 10 ? "0" + m : "" + m;
var ss = s < 10 ? "0" + s : "" + s;
return mm + ":" + ss;
}
var timerText = new Text2(formatTimeDigital(60), {
size: 120,
fill: '#ffffff'
});
timerText.anchor.set(0.5, 0);
LK.gui.top.addChild(timerText);
// --- Score/Win state ---
var gameEnded = false;
// --- Timer logic ---
var timerInterval = LK.setInterval(function () {
if (gameEnded) return;
timeLeft -= 1;
if (timeLeft < 0) timeLeft = 0;
timerText.setText(formatTimeDigital(timeLeft));
if (timeLeft === 0 && !gameEnded) {
gameEnded = true;
LK.getSound('lose').play();
LK.effects.flashScreen(0xff0000, 800);
LK.showGameOver();
}
}, 1000);
// --- Game update ---
game.update = function () {
if (gameEnded) return;
puppet.update();
// Win condition: puppet body collides with finish sign using intersects
if (!gameEnded && puppet.intersects(finishSign)) {
gameEnded = true;
LK.setScore(timeLeft); // Set score to remaining seconds
LK.getSound('win').play();
LK.effects.flashScreen(0x44c767, 800);
LK.showYouWin();
}
};
// --- Clean up on game over ---
game.onDestroy = function () {
LK.clearInterval(timerInterval);
};
// --- Prevent accidental tap on top left (menu) ---
/* No elements are placed at (0,0)-(100,100) */
// --- UX: drums are always on top ---
for (var i = 0; i < drums.length; ++i) {
game.addChild(drums[i]);
}