/**** * Classes ****/ var BeachToys = Container.expand(function (index) { var self = Container.call(this); self.shadow = self.attachAsset('beachToysShadow', { anchorX: 0.5, anchorY: 0.5, x: 150, y: -150, alpha: 0.3, scaleX: -1, scaleY: 1, tint: 0x000000, rotation: 2.3, visible: !index }); var alt = index ? "2" : ""; var beachToysGraphics = self.attachAsset('beachToys' + alt, { anchorX: 0.5, anchorY: 1.0 }); }); var Boat = Container.expand(function () { var self = Container.call(this); var boatGraphics = self.attachAsset('boat', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 2; // Speed of the boat movement self.direction = 1; // Direction of the boat movement, 1 for right, -1 for left self.update = function () { if (!self.startTime) { self.startTime = LK.ticks; } var elapsed = (LK.ticks - self.startTime) / 60; // Elapsed time in seconds var targetX = self.x + self.speed * self.direction; self.x = self.x + (targetX - self.x) * 0.1; // Linear interpolation for smoother movement if (self.x > 2048 + boatGraphics.width / 2) { self.x = -boatGraphics.width / 2; // Reset position to the left of the screen } else if (self.x < -boatGraphics.width / 2) { self.x = 2048 + boatGraphics.width / 2; // Reset position to the right of the screen } if (self.y > 2748 + self.height / 2) { self.visible = false; // Make the boat not visible when y > 2748 } }; /** * Reverses the direction of the boat. */ self.reverse = function () { self.direction *= -1; self.scale.x *= -1; self.visible = true; self.x = 2048 + self.width / 2; // Reset position to the right of the screen }; }); var Bucket = Container.expand(function () { var self = Container.call(this); var bucketGraphics = self.attachAsset('bucket', { anchorX: 0.5, anchorY: 0.0 }); self.scoreTxt = new Text2('00', { size: 200, fill: "#03a2c3", weight: 1000 }); self.scoreTxt.anchor.set(0.5, 0); self.scoreTxt.x = 0; self.scoreTxt.y = 330; self.addChild(self.scoreTxt); }); var Cloud = Container.expand(function (index) { var self = Container.call(this); var cloudGraphics = self.attachAsset('cloud' + index, { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0.4 + (2 - index) * 0.2 + Math.random() * 0.2; // Speed of the cloud movement self.update = function () { if (!self.visible) { return; } self.x += self.speed; if (self.x > 2048 + cloudGraphics.width / 2) { self.x = -cloudGraphics.width / 2 - Math.random() * 2048; // Reset position to the left of the screen self.y = Math.random() * 512; } }; }); var Confetti = Container.expand(function () { var self = Container.call(this); var confettiColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff]; var confettiPieces = []; var nbConfettis = 3 + level * 7; nbConfettis = Math.min(500, nbConfettis); for (var i = 0; i < nbConfettis; i++) { var confettiPiece = self.attachAsset('confetti', { anchorX: 0.5, anchorY: 0.5, width: 15, height: 50, tint: confettiColors[Math.floor(Math.random() * confettiColors.length)] }); confettiPiece.x = Math.random() * 2048; confettiPiece.y = Math.random() * 2732; confettiPiece.rotation = Math.random() * Math.PI * 2; confettiPiece.speedY = Math.random() * 5 + 4; confettiPiece.speedX = (Math.random() - 0.5) * 2; confettiPieces.push(confettiPiece); } self.update = function () { for (var i = 0; i < confettiPieces.length; i++) { var piece = confettiPieces[i]; piece.y += piece.speedY; piece.x += piece.speedX; if (piece.y > 2732) { piece.y = -piece.height; piece.x = Math.random() * 2048; } } }; }); var FlyingObject = Container.expand(function (index) { var self = Container.call(this); var flyingObjectGraphics = self.attachAsset('flyingObject' + index, { anchorX: 0.5, anchorY: 0.5 }); self.directionY = -1 * (Math.random() > 0.5); self.passed = true; self.speed = 3; // Speed of the flying object movement self.update = function () { if (!self.visible || self.passed) { return; } self.x += self.speed; self.y += self.speed * 0.1 * self.directionY; if (self.x > 2048 + flyingObjectGraphics.width / 2) { self.x = -flyingObjectGraphics.width / 2 - Math.random() * 1024; // Reset position to the left of the screen self.y = Math.random() * 512; // Reset position relative to the camera self.passed = true; self.visible = false; } }; }); var SandBlock = Container.expand(function () { var self = Container.call(this); var sandBlockGraphics = self.attachAsset('sandBlock', { anchorX: 0.5, anchorY: 0 }); self.height = sandBlockAssetHeight * sandBlockHeightBaseRatio; self.velocity = 0; // Initial velocity for natural falling speed increase self.update = function () { if (self.y > 2748 + self.height / 2) { self.visible = false; } }; }); /***********************************************************************************/ /********************************** SANDCASTLE CLASS ************************************/ /***********************************************************************************/ var Sandcastle = Container.expand(function () { var self = Container.call(this); self.shadow = self.attachAsset('shadow', { anchorX: 0.5, anchorY: 0.5, alpha: 0.5, visible: false }); var sandcastleGraphics = self.attachAsset('sandcastle', { anchorX: 0.5, anchorY: 0.5 }); self.shadow.width = sandcastleGraphics.width; self.shadow.height = sandcastleGraphics.height * 0.5; self.shadow.y = sandcastleGraphics.height * 0.3; self.shadow.visible = false; self.update = function () { // Sandcastle specific update logic }; }); /**** * Initialize Game ****/ // Utility function to draw a polygon using drawLine var game = new LK.Game({ backgroundColor: 0x000050 // Initialize game with a black background }); /**** * Game Code ****/ // Enumeration for game states /****************************************************************************************** */ /************************************** GLOBAL VARIABLES ********************************** */ /****************************************************************************************** */ function easeInOutQuad(t) { return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t; } var isBucketMovingUp = false; // Flag to track if the bucket is moving up var isBucketMovingDown = false; // Flag to track if the bucket is moving down var GAME_STATE = { INIT: 'INIT', MENU: 'MENU', NEW_ROUND: 'NEW_ROUND', PLAYING: 'PLAYING', SCORE: 'SCORE' }; var gameState = GAME_STATE.INIT; var score = 0; var level = 0; var scoreTxt; var isDebug = false; var debugText; var debugMarker; var backgroundImage; var bucket; var banner; var picketLeft; var picketRight; var sandBlocks = []; var lives = 3; var livesIcons = []; var cameraMoved = false; // Flag to track if the camera has moved for the current sandblock fall var sandBlockDropped = false; // Flag to track if a sand block has been dropped var isCameraMoving = false; // Flag to track if the camera is currently moving var bucketReady = false; // Flag to track if the bucket has reached y=0 var sandcastle; var sandcastleBaseY = 2212; var sandcastleHeight = 1640; var previousBlockX = 1024; var sandBlockWidth = 390; var sandBlockHalfWidth = 195; var sandBlockAssetHeight = 1618; // height of the asset var sandBlockHeightBaseRatio = 0.6; // Extend effect start ratio var sandBlockHeightRatio = sandBlockHeightBaseRatio; // Extend effect current ratio var sandBlockSize = 460; // height of the visible sand block var centralPointY = 1380 - sandBlockSize; var bgHeight = 1152; var bgHalfHeight = 576; var gravity = 2; // Gravity effect for sand block falling var bucketDirection = 1; // 1 for right, -1 for left var isBucketMovingHorizontally = false; // Flag to track if the bucket is moving horizontally var bucketMoveStep = 10; // Initial bucket move step size var currentSandBlock = null; // Global variable to keep track of the current sand block var startButton = null; // Global variable for the start button var isPlaying = false; // Global variable to track if the game is currently in the playing state var sandBlockMoveStep = 2; // Step size for sand block horizontal movement var bgMusic; var globalDelta = 0; var beachToys = null; // Global variable for beach toys var cloud1 = null; // Global variable for cloud1 var cloud2 = null; // Global variable for cloud2 var moveStep = 0; // Global variable for move step var flyingObject1 = null; // Global variable for flyingObject1 var flyingObject2 = null; // Global variable for flyingObject2 var flyingObject3 = null; // Global variable for flyingObject2 var flyingObject4 = null; // Global variable for flyingObject2 var fadeOutStartButtonStartTime = 0; var isCleaningNewRound = false; // Flag to prevent multiple calls to cleanNewRoundState var lastUpdateTime = 0; /****************************************************************************************** */ /*********************************** UTILITY FUNCTIONS ************************************ */ /****************************************************************************************** */ function moveCamera() { var targetY = backgroundImage.y + sandBlockSize; moveStep = sandBlockSize / 60; // Move over 1 second (60 frames) function progressiveMove() { if (backgroundImage.y < targetY) { log("Camera is moving. Current y position: " + backgroundImage.y); backgroundImage.y += moveStep; background2.y += moveStep; background3.y += moveStep; sandcastle.y += moveStep; background4.y += moveStep; background5.y += moveStep; banner.y += moveStep * 1.05; picketLeft.y += moveStep * 1.05; picketRight.y += moveStep * 1.05; for (var j = 0; j < sandBlocks.length; j++) { sandBlocks[j].y += moveStep; } if (cloud1) { cloud1.y += moveStep * 0.5; } if (cloud2) { cloud2.y += moveStep * 0.5; } if (flyingObject1 && flyingObject1.visible) { flyingObject1.y += moveStep * 0.75; } if (flyingObject2 && flyingObject2.visible) { flyingObject2.y += moveStep * 0.75; } if (flyingObject3 && flyingObject3.visible) { flyingObject3.y += moveStep * 0.75; } if (flyingObject4 && flyingObject4.visible) { flyingObject4.y += moveStep * 0.75; } if (boat) { boat.y += moveStep; } if (backgroundImage.y < targetY) { LK.setTimeout(progressiveMove, 1000 / 60); // Call progressiveMove every 1/60th of a second } else { isCameraMoving = false; // Set the flag to false when the camera stops moving var checkBucketUpCompletion = function checkBucketUpCompletion() { if (bucket.y <= -bucket.height && !isBucketMovingUp) { isBucketMovingHorizontally = true; // Resume horizontal movement of the bucket moveBucketDown(); // Progressively move the bucket down when the camera reaches the target game.addChild(bucket); // Ensure bucket is above sand blocks } else { LK.setTimeout(checkBucketUpCompletion, 1000 / 60); // Check again in the next frame } }; checkBucketUpCompletion(); } if (background2.y > 2732 + bgHalfHeight) { background2.y = background5.y - background5.height; } if (background3.y > 2732 + bgHalfHeight) { background3.y = background2.y - background2.height; } if (background4.y > 2732 + bgHalfHeight) { background4.y = background3.y - background3.height; } if (background5.y > 2732 + bgHalfHeight) { background5.y = background4.y - background4.height; } } } progressiveMove(); } function log() { if (isDebug) { var _console; (_console = console).log.apply(_console, arguments); } } /****************************************************************************************** */ /************************************** INPUT HANDLERS ************************************ */ /****************************************************************************************** */ game.on('down', function (x, y, obj) { handleGameState(x, y, obj); }); function handleGameState(x, y, obj) { switch (gameState) { case GAME_STATE.MENU: gameMenuDown(x, y, obj); break; case GAME_STATE.NEW_ROUND: gameNewRoundDown(x, y, obj); break; case GAME_STATE.PLAYING: gamePlayingDown(x, y, obj); break; case GAME_STATE.SCORE: gameScoreDown(x, y, obj); break; } } function gameMenuDown(x, y, obj) { log("gameMenuDown..."); if (gameState != GAME_STATE.MENU) { return; } cleanMenuState(); initNewRoundState(); } function gameNewRoundDown(x, y, obj) { log("gameNewRoundDown..."); if (gameState != GAME_STATE.NEW_ROUND || isCleaningNewRound) { return; } log("gameNewRoundDown Ok clean..."); cleanNewRoundState(); } function gamePlayingDown(x, y, obj) { log("gamePlayingDown...", "state=" + (gameState == GAME_STATE.PLAYING), "playing=" + isPlaying, "dropped=" + !sandBlockDropped, "camera=" + !isCameraMoving, "bucket=" + bucketReady); if (gameState != GAME_STATE.PLAYING || !isPlaying || sandBlockDropped || isCameraMoving || !bucketReady) { return; // Prevent taps during camera movement } sandBlockDropped = true; log("gamePlayingDown OK"); // Pause horizontal movement of the bucket isBucketMovingHorizontally = false; lastUpdateTime = 0; // Move the bucket vertically to hide over the screen top with shake moveBucketUp(); LK.setTimeout(createNewSandBlock, 512); // Delay creating new sand block until after shake } function gameScoreDown(x, y, obj) { log("gameScoreDown..."); cleanScoreState(); } /****************************************************************************************** */ /************************************* GAME FUNCTIONS ************************************* */ /****************************************************************************************** */ function moveBucketUp() { if (isBucketMovingDown) { return; } // Prevent concurrent moves isBucketMovingUp = true; // Set flag to true when bucket starts moving up var bucketMoveStep = 15; // Adjust the step size as needed var shakeStep = 40; // Step size for shake movement var shakeCount = 0; // Counter for shake movement function shakeBucket() { if (shakeCount < 5) { // Shake for 10 frames bucket.y += shakeCount % 2 === 0 ? -shakeStep : shakeStep; shakeCount++; LK.setTimeout(shakeBucket, 100); // Call shakeBucket every 1/60th of a second } else { moveBucketUpAfterShake(); // Move bucket up after shaking } } function moveBucketUpAfterShake() { if (isPlaying && bucket.y > -bucket.height) { isBucketMovingUp = true; // Ensure flag remains true while moving up //log("Bucket is moving up. Current y position: " + bucket.y); bucket.y -= bucketMoveStep; game.addChild(bucket); // Ensure bucket is above sand blocks LK.setTimeout(moveBucketUpAfterShake, 5); // Call moveBucketUp every 1/240th of a second } else { //log("Bucket end moving up. Current y position: " + bucket.y + "/-" + bucket.height); isBucketMovingUp = false; // Set flag to false when bucket finishes moving up } } shakeBucket(); // Start shaking the bucket } function moveBucketDown() { if (isBucketMovingUp) { return; } // Prevent concurrent moves isBucketMovingDown = true; // Set flag to true when bucket starts moving down var bucketMoveStep = 15; // Adjust the step size as needed if (isPlaying && bucket.y < -15) { log("Bucket is moving down. Current y position: " + bucket.y); bucketReady = false; // Reset the flag when the bucket is moving down bucket.y += bucketMoveStep; game.addChild(bucket); // Ensure bucket is above sand blocks LK.setTimeout(moveBucketDown, 1000 / 60); // Call moveBucketDown every 1/60th of a second } else { log("Bucket moving down. Ready "); bucketReady = true; // Set the flag to true when the bucket reaches y=0 isBucketMovingDown = false; // Set flag to false when bucket finishes moving down } } function createNewSandBlock() { sandBlockHeightRatio = sandBlockHeightBaseRatio; currentSandBlock = new SandBlock(); var sandBlock = currentSandBlock; sandBlock.x = bucket.x; sandBlock.y = 256; game.addChild(sandBlock); game.addChild(bucket); cameraMoved = false; // Reset the flag when a new sandblock is created sandBlockDropped = true; // Set the flag to true when a new sand block is created } function updateSandBlockPosition(sandBlock) { bucketReady = false; if (sandBlock.y < 512) { // Restore sand block height when out of bucket if (sandBlockHeightRatio < 1) { sandBlockHeightRatio += 0.05; sandBlock.height = sandBlockAssetHeight * Math.min(1, sandBlockHeightRatio); } else { sandBlockHeightRatio = 1; sandBlock.height = sandBlockAssetHeight; } //log("sandBlockHeightRatio=" + sandBlockHeightRatio); } if (sandBlock.y < centralPointY) { // Start of fall until center if (!sandBlock.falling) { LK.getSound('sandFall').play(); sandBlock.falling = true; } sandBlock.velocity += gravity; // Increase velocity due to gravity sandBlock.y += sandBlock.velocity; // Update position based on velocity } else { // Center : Check if on previous block or out if (Math.abs(sandBlock.x - previousBlockX) > sandBlockHalfWidth) { // Out : Continue falling if (sandBlock.y < 2732 + sandBlockSize) { // Fall until botom sandBlock.rotation += 0.07 * Math.sign(sandBlock.x - previousBlockX); sandBlock.x += 20 * Math.sign(sandBlock.x - previousBlockX); sandBlock.velocity += gravity; // Increase velocity due to gravity sandBlock.y += sandBlock.velocity; // Update position based on velocity } else { var checkBucketUpCompletion = function checkBucketUpCompletion() { if (!isBucketMovingUp) { sandBlockDropped = false; isBucketMovingHorizontally = true; // Resume horizontal movement of the bucket moveBucketDown(); // Progressively move the bucket down when the camera reaches the target game.addChild(bucket); // Ensure bucket is above sand blocks } else { LK.setTimeout(checkBucketUpCompletion, 1000 / 60); // Check again in the next frame } }; // Reached bottom checkBlockAlignmentAndUpdate(sandBlock); sandBlock.fallen = true; sandBlock.destroy(); currentSandBlock = null; // Ensure bucket has finished moving up before running the next steps checkBucketUpCompletion(); } } else { // Ok : Stop sandBlocks.push(currentSandBlock); globalDelta = Math.max(globalDelta, Math.abs(currentSandBlock.x - 1024)); sandBlockMoveStep = Math.floor(globalDelta / 100); previousBlockX = currentSandBlock.x; // Move background, sandcastle, and fallen sandblock vertically by sandBlockSize if (!cameraMoved) { cameraMoved = true; isCameraMoving = true; // Set the flag to true when the camera starts moving LK.setTimeout(function () { moveCamera(); // Set the flag to true after moving the camera //bucket.y = 0; // Reset bucket position when the camera moves }, 640); // Delay of 1 second before moving the camera // Check if sand block reached the base checkBlockAlignmentAndUpdate(sandBlock); } currentSandBlock = null; } } } function moveBucketHorizontally() { if (!level) { return; } if (!lastUpdateTime) { lastUpdateTime = Date.now(); } var currentTime = Date.now(); var dt = (currentTime - lastUpdateTime) / 1000; // Delta time in seconds lastUpdateTime = currentTime; var moveDistance = bucketMoveStep * bucketDirection * dt * 60; // Adjust for 60 FPS if (bucket.x >= 2048 - bucket.width / 2) { bucketDirection = -1; // Change direction to left } else if (bucket.x <= bucket.width / 2) { bucketDirection = 1; // Change direction to right } log("moveBucketHorizontally: x=" + bucket.x, "moveDistance=" + moveDistance, "dt=" + dt); bucket.x += moveDistance; } function checkBlockAlignmentAndUpdate(sandBlock) { if (Math.abs(sandBlock.x - previousBlockX) < sandBlockHalfWidth) { LK.getSound('dropped').play(); // Check for perfect alignment level += 1; score += 1; // Bonus points for perfect alignment LK.setScore(score); // Store the score in LK score if (level > 0 && level % 3 === 0) { bucketMoveStep += 4; // Increase bucket speed by 2 units } } else { LK.getSound('missed').play(); lives--; livesIcons[lives].destroy(); livesIcons.pop(); if (lives <= 0) { var checkBucketUpCompletionForScore = function checkBucketUpCompletionForScore() { if (!isBucketMovingUp) { cleanPlayingState(); initScoreState(); } else { LK.setTimeout(checkBucketUpCompletionForScore, 1000 / 60); // Check again in the next frame } }; checkBucketUpCompletionForScore(); return; } } sandBlock.falling = false; // Reset the falling flag sandBlockDropped = false; // Reset the flag after handling sand block reaching the base game.addChild(bucket); // Ensure bucket is above sand blocks } function initializeBackgrounds() { var mainBgY = 1566; var mainBgHeight = 2732; //var bgHeight = 1152; //var bgHalfHeight = 576; backgroundImage = LK.getAsset('backgroundImage', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: mainBgY }); game.addChild(backgroundImage); background2 = LK.getAsset('background2', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 }); background2.y = backgroundImage.y - mainBgHeight / 2 - bgHalfHeight; game.addChild(background2); background3 = LK.getAsset('background3', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 }); background3.y = background2.y - bgHeight; background4 = LK.getAsset('background2', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 }); background4.y = background3.y - bgHeight; game.addChild(background4); background5 = LK.getAsset('background3', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2 }); background5.y = background4.y - bgHeight; ; game.addChild(background5); game.addChild(background3); } function loopBgMusic() { if (bgMusic && Date.now() - bgMusic.lastPlayTime > 10000) { bgMusic.lastPlayTime = Date.now(); bgMusic.play(); } } /****************************************************************************************** */ /************************************* GAME STATES **************************************** */ /****************************************************************************************** */ function gameInitialize() { log("Game initialize..."); initializeBackgrounds(); boat = new Boat(); boat.x = boat.width; boat.y = 1090; boat.visible = true; game.addChild(boat); sandcastle = new Sandcastle(); sandcastle.x = 2048 / 2; sandcastle.y = -sandcastleHeight; // Hide the sandcastle above the screen initially game.addChild(sandcastle); picketLeft = LK.getAsset('picket', { anchorX: 0.5, anchorY: 1, x: 30, y: 2140 }); game.addChild(picketLeft); picketRight = LK.getAsset('picket', { anchorX: 0.5, anchorY: 1, scaleX: -1, x: 2018, y: 2140 }); game.addChild(picketRight); banner = LK.getAsset('banner', { anchorX: 0.5, anchorY: 0.5, x: 1024, y: 300 }); game.addChild(banner); game.addChild(sandcastle); level = 0; score = 0; LK.setScore(score); // Initialize LK score to zero at game start bgMusic = LK.getSound('music'); if (bgMusic && bgMusic.isPlaying) { bgMusic.stop(); } bgMusic.lastPlayTime = 0; startButton = LK.getAsset('startButton', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 1420 // Center of the screen }); startButton.visible = false; // Initially hidden game.addChild(startButton); beachToys = new BeachToys(); beachToys.x = 2048 / 2; beachToys.y = 2922; game.addChild(beachToys); // Initialize clouds at the beginning but keep them invisible cloud1 = new Cloud(1); cloud1.x = -cloud1.width; cloud1.y = -cloud1.height; cloud1.visible = false; game.addChild(cloud1); cloud2 = new Cloud(2); cloud2.x = -cloud2.width; cloud2.y = -cloud2.height; cloud2.visible = false; game.addChild(cloud2); flyingObject1 = new FlyingObject(1); flyingObject1.x = -Math.random() * 512; flyingObject1.y = flyingObject1.height + 256 * Math.random(); flyingObject1.visible = false; flyingObject1.passed = true; game.addChild(flyingObject1); flyingObject2 = new FlyingObject(2); flyingObject2.x = -Math.random() * 512; flyingObject2.y = flyingObject2.height + 256 * Math.random(); flyingObject2.visible = false; flyingObject2.passed = true; game.addChild(flyingObject2); flyingObject3 = new FlyingObject(3); flyingObject3.x = -Math.random() * 512; flyingObject3.y = flyingObject3.height + 256 * Math.random(); flyingObject3.visible = false; flyingObject3.passed = true; game.addChild(flyingObject3); flyingObject4 = new FlyingObject(4); flyingObject4.x = -Math.random() * 512; flyingObject4.y = flyingObject4.height + 256 * Math.random(); flyingObject4.visible = false; flyingObject4.passed = true; game.addChild(flyingObject4); initMenuState(); if (isDebug) { debugMarker = LK.getAsset('debugMarker', { anchorX: 0.5, anchorY: 0.5, x: 1300, y: 160 }); game.addChild(debugMarker); debugText = new Text2('Debug Info', { size: 50, fill: "#ffffff" }); debugText.anchor.set(0.5, 1); // Anchor to the bottom-right LK.gui.bottom.addChild(debugText); } } // GAME MENU function initMenuState() { log("initMenuState..."); gameState = GAME_STATE.MENU; } function handleMenuLoop() { // Menu animations here } function cleanMenuState() { log("cleanMenuState..."); } // NEW ROUND function initNewRoundState() { log("initNewRoundState..."); gameState = GAME_STATE.NEW_ROUND; // Fade in the start button startButton.visible = true; startButton.alpha = 0; var fadeInStep = 0.05; function fadeInStartButton() { if (startButton.alpha < 1) { startButton.alpha += fadeInStep; LK.setTimeout(fadeInStartButton, 1000 / 60); // Call fadeInStartButton every 1/60th of a second } } fadeInStartButton(); } function handleNewRoundLoop() { // New Round animations here loopBgMusic(); } function cleanNewRoundState() { if (isCleaningNewRound) { return; } // Prevent multiple calls isCleaningNewRound = true; log("cleanNewRoundState..."); // Animate the start button removal var fadeOutStep = 0.05; function fadeOutStartButton() { log("fadeOutStartButton...", startButton.alpha, "step=" + fadeOutStep); if (startButton.alpha > 0 && Date.now() - fadeOutStartButtonStartTime < 1000) { startButton.alpha -= fadeOutStep; LK.setTimeout(fadeOutStartButton, 1000 / 60); // Call fadeOutStartButton every 1/60th of a second } else { log("fadeOutStartButton End"); startButton.alpha = 0; var fadeOutBeachToys = function fadeOutBeachToys() { log("fadeOutBeachToys..."); if (beachToys.alpha > 0) { beachToys.alpha -= fadeOutStep; LK.setTimeout(fadeOutBeachToys, 1000 / 60); } else { log("fadeOutBeachToys End"); beachToys.visible = false; beachToys.alpha = 1; // Reset alpha for future use var animateSandcastleFall = function animateSandcastleFall() { log("animateSandcastleFall..."); if (sandcastle.y < sandcastleBaseY) { sandcastle.velocity += gravity; // Increase velocity due to gravity sandcastle.y += sandcastle.velocity; // Update position based on velocity LK.setTimeout(animateSandcastleFall, 1000 / 60); // Call animateSandcastleFall every 1/60th of a second } else { log("animateSandcastleFall End"); sandcastle.y = sandcastleBaseY; // Ensure it stops exactly at the base LK.getSound('dropped').play(); // Play drop sound when it reaches its position initPlayingState(); isCleaningNewRound = false; // Reset the flag after completion } }; startButton.visible = false; startButton.alpha = 1; // Reset alpha for future use sandcastle.velocity = 0; // Initial velocity for natural falling speed increase animateSandcastleFall(); } }; fadeOutBeachToys(); } } fadeOutStartButtonStartTime = Date.now(); // Prevent Bug alpha stucked on tap fadeOutStartButton(); } // PLAYING function initPlayingState() { log("initPlayingState..."); gameState = GAME_STATE.PLAYING; isPlaying = true; for (var i = 0; i < lives; i++) { var lifeIcon = LK.getAsset('bucketIcon', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 100, y: 1166 + i * 130 }); livesIcons.push(lifeIcon); game.addChild(lifeIcon); } bucket = new Bucket(); bucket.x = 2048 / 2; bucket.y = -bucket.height; game.addChild(bucket); // Ensure bucket is above sand blocks moveBucketDown(); } function handlePlayingLoop() { loopBgMusic(); if (isPlaying && currentSandBlock) { updateSandBlockPosition(currentSandBlock); //game.addChild(bucket); // Ensure bucket is above sand blocks } if (level >= 4) { for (var i = 0; i < sandBlocks.length; i++) { var sandBlock = sandBlocks[i]; sandBlock.x += Math.sin(LK.ticks / 20) * sandBlockMoveStep; // Move sand blocks back and forth } } if (bucket && bucket.scoreTxt) { if (score === 0) { bucket.scoreTxt.visible = false; } else { bucket.scoreTxt.visible = true; bucket.scoreTxt.setText(score); } } if (isBucketMovingHorizontally) { moveBucketHorizontally(); // Ensure horizontal movement is restored } if (level >= 6 && cloud1 && cloud2 && !cloud1.visible) { cloud1.visible = true; cloud2.visible = true; } if (level && flyingObject1 && level % 4 == 0 && level < 20 && !flyingObject1.visible && flyingObject1.passed) { flyingObject1.visible = true; flyingObject1.passed = false; } if (level && flyingObject2 && level % 15 == 0 && level < 60 && !flyingObject2.visible && flyingObject2.passed) { flyingObject2.visible = true; flyingObject2.passed = false; } if (level && flyingObject3 && level % 10 == 0 && level < 40 && !flyingObject3.visible && flyingObject3.passed) { flyingObject3.visible = true; flyingObject3.passed = false; } if (level && flyingObject4 && level % 21 == 0 && !flyingObject4.visible && flyingObject4.passed) { flyingObject4.visible = true; flyingObject4.passed = false; } if (isDebug) { //debugText.setText("X: " + bucket.x + " Y: " + bucket.y); debugText.setText("Delta: " + globalDelta); } } function cleanPlayingState() { log("cleanPlayingState..."); isPlaying = false; // TODO Remove elements } // SCORE function initScoreState() { log("initScoreState..."); gameState = GAME_STATE.SCORE; // Play camera sound LK.getSound('camera').play(); // Prepare final animation // Add a 1-second white screen flash LK.effects.flashScreen(0xffffff, 2000); LK.setTimeout(setupFinalPhoto, 1000); confetti = new Confetti(); confetti.visible = false; game.addChild(confetti); } function setupFinalPhoto() { // Set the background size to 2048x2732 backgroundImage.width = 2048; backgroundImage.height = 2732; // Set the background position to the center of the screen backgroundImage.x = 2048 / 2; backgroundImage.y = 2732 / 2; background2.visible = false; background3.visible = false; background4.visible = false; background5.visible = false; cloud1.visible = false; cloud2.visible = false; var finalScoreTxt = new Text2(score.toString(), { size: 150, fill: "#FFFFFF", weight: 1000, dropShadow: true }); finalScoreTxt.anchor.set(0.5, 0.2); LK.gui.top.addChild(finalScoreTxt); // Scale the sandcastle and the sandblocks down to fit in the screen var ratio = 1 / Math.ceil(sandBlocks.length / 3); sandcastle.width *= ratio; sandcastle.height *= ratio; sandcastle.x = 2048 / 2; sandcastle.y = sandcastleBaseY; sandcastle.shadow.visible = true; if (ratio <= 0.5) { beachToys = new BeachToys(); beachToys.width *= ratio; beachToys.height *= ratio; beachToys.x = 1724; beachToys.y = sandcastleBaseY + 300; game.addChild(beachToys); var sandcastle2 = new Sandcastle(); sandcastle2.x = 312; sandcastle2.y = sandcastleBaseY - 250; // Hide the sandcastle above the screen initially sandcastle2.width *= 0.8; sandcastle2.height *= 0.8; sandcastle2.width *= ratio; sandcastle2.height *= ratio; game.addChild(sandcastle2); sandcastle2.shadow.visible = true; beachToys2 = new BeachToys(2); beachToys2.width *= ratio; beachToys2.height *= ratio; beachToys2.shadow.visible = false; beachToys2.x = sandcastle2.x; // sandcastle2.x - sandcastle2.width / 2 - beachToys2.width / 2; beachToys2.y = sandcastle2.y + sandcastle2.height / 2 + 300 * ratio; //sandcastleBaseY + 350; game.addChild(beachToys2); } banner.width *= ratio; banner.height *= ratio; banner.x = 2048 / 2; banner.y = 2140 - picketLeft.height * ratio; picketLeft.width *= ratio; picketLeft.height *= ratio; picketLeft.x = 1024 - (1024 - 30) * ratio; picketLeft.y = 2140; picketRight.width *= ratio; picketRight.height *= ratio; picketRight.x = 1024 + (1024 - 30) * ratio; picketRight.y = 2140; var baseY = sandcastle.y - sandcastle.height / 2 - sandBlockSize * ratio; log("baseY ", baseY); for (var i = 0; i < sandBlocks.length; i++) { sandBlocks[i].width *= ratio; sandBlocks[i].height *= ratio; sandBlocks[i].x = 1024 - (1024 - sandBlocks[i].x) * ratio; sandBlocks[i].y = baseY - i * sandBlockSize * ratio; sandBlocks[i].visible = true; //log("block #", i, " h=" + sandBlockSize * ratio, " / y = ", sandBlocks[i].y); } // Show confetti confetti.visible = true; boat.y = 1000; boat.width *= ratio; boat.height *= ratio; boat.reverse(); } function handleScoreLoop() { // Score display logic here loopBgMusic(); } function cleanScoreState() { log("cleanScoreState..."); LK.showGameOver(); } /***********************************************************************************/ /******************************** MAIN GAME LOOP ***********************************/ /***********************************************************************************/ game.update = function () { switch (gameState) { case GAME_STATE.MENU: handleMenuLoop(); break; case GAME_STATE.NEW_ROUND: handleNewRoundLoop(); break; case GAME_STATE.PLAYING: handlePlayingLoop(); break; case GAME_STATE.SCORE: handleScoreLoop(); break; } }; gameInitialize(); // Initialize the game /********************************************************************************/ /**************************** GAME DESCRIPTION *********************************/ /******************************************************************************/ /* **Game Title**: **Sand Tower** **Description**: Build towering sandcastles, perfect your alignment, and climb to the top! Sand Tower awaits! 🏖️🏰 **Objective**: - Players aim to construct the tallest sandcastle tower. - Perfect alignment of sand blocks is crucial for stability and bonus points. **Visuals**: - The game features a sandy beach backdrop. - A basic sandcastle stands in the center, serving as the base. - The central tower is the focal point. **Gameplay Mechanics**: - **Beach Bucket Mechanism**: - Players use an upside-down beach bucket. - When the player taps, the bucket tips over, releasing sand. - **Sand Block Placement**: - Sand falls vertically from the bucket onto the base. - The player must time their taps to stack the sand blocks. - Perfect alignment grants bonus points. - **Lives System**: - The player starts with 3 lives (represented by small bucket icons). - Each time a sand block isn't centered enough and falls, 1 life is lost. - **Scoring**: - Points increase with tower height. - Bonus points for precise alignment. - **Game Over**: - The game ends if the player loses all their lives (buckets). **Audio**: - Gentle beach sounds (waves, distant chatter). - Encouraging music during gameplay. */
===================================================================
--- original.js
+++ change.js
@@ -92,9 +92,9 @@
var Confetti = Container.expand(function () {
var self = Container.call(this);
var confettiColors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
var confettiPieces = [];
- var nbConfettis = 10 + level * 10;
+ var nbConfettis = 3 + level * 7;
nbConfettis = Math.min(500, nbConfettis);
for (var i = 0; i < nbConfettis; i++) {
var confettiPiece = self.attachAsset('confetti', {
anchorX: 0.5,
@@ -128,9 +128,9 @@
anchorX: 0.5,
anchorY: 0.5
});
self.directionY = -1 * (Math.random() > 0.5);
- self.passed = false;
+ self.passed = true;
self.speed = 3; // Speed of the flying object movement
self.update = function () {
if (!self.visible || self.passed) {
return;
@@ -139,10 +139,10 @@
self.y += self.speed * 0.1 * self.directionY;
if (self.x > 2048 + flyingObjectGraphics.width / 2) {
self.x = -flyingObjectGraphics.width / 2 - Math.random() * 1024; // Reset position to the left of the screen
self.y = Math.random() * 512; // Reset position relative to the camera
- self.visible = false;
self.passed = true;
+ self.visible = false;
}
};
});
var SandBlock = Container.expand(function () {
@@ -193,12 +193,12 @@
/****
* Game Code
****/
+// Enumeration for game states
/****************************************************************************************** */
/************************************** GLOBAL VARIABLES ********************************** */
/****************************************************************************************** */
-// Enumeration for game states
function easeInOutQuad(t) {
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
}
var isBucketMovingUp = false; // Flag to track if the bucket is moving up
@@ -257,8 +257,10 @@
var cloud2 = null; // Global variable for cloud2
var moveStep = 0; // Global variable for move step
var flyingObject1 = null; // Global variable for flyingObject1
var flyingObject2 = null; // Global variable for flyingObject2
+var flyingObject3 = null; // Global variable for flyingObject2
+var flyingObject4 = null; // Global variable for flyingObject2
var fadeOutStartButtonStartTime = 0;
var isCleaningNewRound = false; // Flag to prevent multiple calls to cleanNewRoundState
var lastUpdateTime = 0;
/****************************************************************************************** */
@@ -293,8 +295,14 @@
}
if (flyingObject2 && flyingObject2.visible) {
flyingObject2.y += moveStep * 0.75;
}
+ if (flyingObject3 && flyingObject3.visible) {
+ flyingObject3.y += moveStep * 0.75;
+ }
+ if (flyingObject4 && flyingObject4.visible) {
+ flyingObject4.y += moveStep * 0.75;
+ }
if (boat) {
boat.y += moveStep;
}
if (backgroundImage.y < targetY) {
@@ -658,9 +666,9 @@
banner = LK.getAsset('banner', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
- y: 400
+ y: 300
});
game.addChild(banner);
game.addChild(sandcastle);
level = 0;
@@ -697,16 +705,28 @@
flyingObject1 = new FlyingObject(1);
flyingObject1.x = -Math.random() * 512;
flyingObject1.y = flyingObject1.height + 256 * Math.random();
flyingObject1.visible = false;
- flyingObject1.passed = false;
+ flyingObject1.passed = true;
game.addChild(flyingObject1);
flyingObject2 = new FlyingObject(2);
flyingObject2.x = -Math.random() * 512;
flyingObject2.y = flyingObject2.height + 256 * Math.random();
flyingObject2.visible = false;
- flyingObject2.passed = false;
+ flyingObject2.passed = true;
game.addChild(flyingObject2);
+ flyingObject3 = new FlyingObject(3);
+ flyingObject3.x = -Math.random() * 512;
+ flyingObject3.y = flyingObject3.height + 256 * Math.random();
+ flyingObject3.visible = false;
+ flyingObject3.passed = true;
+ game.addChild(flyingObject3);
+ flyingObject4 = new FlyingObject(4);
+ flyingObject4.x = -Math.random() * 512;
+ flyingObject4.y = flyingObject4.height + 256 * Math.random();
+ flyingObject4.visible = false;
+ flyingObject4.passed = true;
+ game.addChild(flyingObject4);
initMenuState();
if (isDebug) {
debugMarker = LK.getAsset('debugMarker', {
anchorX: 0.5,
@@ -748,16 +768,8 @@
LK.setTimeout(fadeInStartButton, 1000 / 60); // Call fadeInStartButton every 1/60th of a second
}
}
fadeInStartButton();
- // Reset flying objects passed property
- if (flyingObject1) {
- flyingObject1.passed = false;
- }
- if (flyingObject2) {
- flyingObject2.passed = false;
- }
- // Round preparation logic here.
}
function handleNewRoundLoop() {
// New Round animations here
loopBgMusic();
@@ -860,16 +872,24 @@
if (level >= 6 && cloud1 && cloud2 && !cloud1.visible) {
cloud1.visible = true;
cloud2.visible = true;
}
- if (level && flyingObject1 && level == 5 && !flyingObject1.visible && !flyingObject1.passed) {
+ if (level && flyingObject1 && level % 4 == 0 && level < 20 && !flyingObject1.visible && flyingObject1.passed) {
flyingObject1.visible = true;
flyingObject1.passed = false;
}
- if (level && flyingObject2 && level % 10 == 0 && !flyingObject2.visible && !flyingObject2.passed) {
+ if (level && flyingObject2 && level % 15 == 0 && level < 60 && !flyingObject2.visible && flyingObject2.passed) {
flyingObject2.visible = true;
flyingObject2.passed = false;
}
+ if (level && flyingObject3 && level % 10 == 0 && level < 40 && !flyingObject3.visible && flyingObject3.passed) {
+ flyingObject3.visible = true;
+ flyingObject3.passed = false;
+ }
+ if (level && flyingObject4 && level % 21 == 0 && !flyingObject4.visible && flyingObject4.passed) {
+ flyingObject4.visible = true;
+ flyingObject4.passed = false;
+ }
if (isDebug) {
//debugText.setText("X: " + bucket.x + " Y: " + bucket.y);
debugText.setText("Delta: " + globalDelta);
}
@@ -911,9 +931,9 @@
fill: "#FFFFFF",
weight: 1000,
dropShadow: true
});
- finalScoreTxt.anchor.set(0.5, -0.50);
+ finalScoreTxt.anchor.set(0.5, 0.2);
LK.gui.top.addChild(finalScoreTxt);
// Scale the sandcastle and the sandblocks down to fit in the screen
var ratio = 1 / Math.ceil(sandBlocks.length / 3);
sandcastle.width *= ratio;
Front close view of a calm sea from the beach. nothing on the beach just flat sand. no sun... photorealistic
Start button. Beach themed
face view of a red beach bucket with a blue handle.. photo
beach toys. photorealistic
beach construction toys. red bucket with blue handle.. photorealistic
an horizontal cloud. photorealistic
an air ballon. photorealistic
simple rectangular white ribon.
a soaring gull. lateral view