User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'align')' in or related to this line: 'self.addChild(titleText);' Line Number: 396
User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'align')' in or related to this line: 'titleText.style.align = 'center';' Line Number: 396
User prompt
The VS and MONSTERS are not centered, they are aligned to the left
User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'align')' in or related to this line: 'titleText.style.align = 'center';' Line Number: 395
User prompt
center the text of the main menu
User prompt
Sorry, it should be: Classic Mario Vs Monsters
User prompt
Rename menu to Classi Mario vs Monsters
User prompt
Combo should continue not stop at 2
User prompt
remove combo aand speed from hud
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of null (reading 'destroy')' in or related to this line: 'mainMenu.destroy();' Line Number: 905
User prompt
Please fix the bug: 'Uncaught TypeError: Cannot read properties of null (reading 'destroy')' in or related to this line: 'mainMenu.destroy();' Line Number: 905
User prompt
after game title drop, also drop the player, and have him start moving to the right and add parallax, player will not be able to play until they tap when the game will be presented. this will only be to animate the main manu backorund
User prompt
add tween to game title, make it drop from the top and shake before settling in its final position. then star playing game theme ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
there is a small gap in paralax scrolling
User prompt
remove upgrades section. player should always double jump. remove dadsh
User prompt
add a frame for game title in main menu
User prompt
Rename OG Mario to Mario VS Mario and make it black
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'createElement')' in or related to this line: 'var fontLink = document.createElement ? document.createElement('link') : null;' Line Number: 885
User prompt
lets us press 2 play font for game title
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'createElement')' in or related to this line: 'var fontLink = document.createElement ? document.createElement('link') : null;' Line Number: 888
User prompt
Please fix the bug: 'LK.loadFont is not a function' in or related to this line: 'LK.loadFont('Press Start 2P');' Line Number: 887
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'createElement')' in or related to this line: 'var fontLink = document.createElement('link');' Line Number: 887
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'font')' in or related to this line: 'LK.init.font('Press Start 2P', {' Line Number: 887
User prompt
i want to add a pixelart font
User prompt
star should also have speed forward not only up and down
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { coins: 0 }); /**** * Classes ****/ var Background = Container.expand(function () { var self = Container.call(this); var backgrounds = ['background1', 'background2', 'background3']; var randomBackground = backgrounds[Math.floor(Math.random() * backgrounds.length)]; var backgroundGraphics = self.attachAsset(randomBackground, { anchorX: 0, anchorY: 0 }); self.sprites = [backgroundGraphics]; // Store sprites for access self.speed = 3; self.update = function () { if (!game.playerDead) { // Increase speed over time, up to a reasonable maximum self.speed = 3 + Math.min(8, Math.floor(LK.ticks / 300) * 0.3); // Double speed during star power if (starPowerActive) { self.speed *= 2; } self.x -= self.speed; } if (self.x <= -2048) { self.x = 2048; } }; }); var Background2 = Container.expand(function () { var self = Container.call(this); var backgrounds = ['background1', 'background2', 'background3']; var randomBackground = backgrounds[Math.floor(Math.random() * backgrounds.length)]; var backgroundGraphics = self.attachAsset(randomBackground, { anchorX: 0, anchorY: 0 }); self.sprites = [backgroundGraphics]; // Store sprites for access self.speed = 3; self.update = function () { if (!game.playerDead) { // Increase speed over time, up to a reasonable maximum self.speed = 3 + Math.min(8, Math.floor(LK.ticks / 300) * 0.3); // Double speed during star power if (starPowerActive) { self.speed *= 2; } self.x -= self.speed; } if (self.x <= -2048) { self.x = 2048; } }; }); var Coin = Container.expand(function () { var self = Container.call(this); var coinGraphics = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); self.speed = 3; self.collected = false; self.y = 2732 - 600; // Position coins higher than the ground self.update = function () { if (!game.playerDead) { // Increase speed over time, up to a reasonable maximum self.speed = 3 + Math.min(8, Math.floor(LK.ticks / 300) * 0.3); // Double speed during star power if (starPowerActive) { self.speed *= 2; } self.x -= self.speed; // Removed oscillation to keep coins from moving up and down if (!self.oscillating) { self.oscillating = true; // No pulsing animation for coins self.scale.x = 1; self.scale.y = 1; } } if (self.x < -100) { // Cancel any active tweens when destroying tween.stop(self); tween.stop(self.scale); self.destroy(); } }; }); // ComboDisplay handles showing combo text on screen var ComboDisplay = Container.expand(function () { var self = Container.call(this); var comboText = new Text2('', { size: 60, fill: 0xFFD700, // Gold color for combo text font: "'Press Start 2P', monospace" }); comboText.anchor.set(0.5, 0.5); self.addChild(comboText); self.showCombo = function (comboCount, points) { comboText.setText('COMBO x' + comboCount + '!\n+' + points + ' POINTS!'); comboText.alpha = 1; // Animate the combo text with a scale effect comboText.scale.x = 1.2; comboText.scale.y = 1.2; tween(comboText, { alpha: 0, scaleX: 0.8, scaleY: 0.8, y: comboText.y - 60 // Move up as it fades but not as far }, { duration: 800, easing: tween.easeOut }); }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); self.runFrames = ['turtle1', 'turtle2']; self.sprites = []; // Pre-attach all sprites for (var i = 0; i < self.runFrames.length; i++) { var sprite = self.attachAsset(self.runFrames[i], { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); sprite.alpha = i === 0 ? 1 : 0; self.sprites.push(sprite); } self.speed = 6 + Math.random() * 2; self.passed = false; self.isJumping = false; self.velocityY = 0; self.canHarmPlayer = true; // Flag to track if enemy can harm player self.groundY = 2732 - 400; // 400 pixels from bottom self.y = self.groundY; // Set initial position to ground level self.runFrameIndex = 0; self.runFrameCounter = 0; self.runFrameDelay = 15; self.showFrame = function (index) { // Hide all sprites first for (var i = 0; i < self.sprites.length; i++) { self.sprites[i].alpha = 0; } // Show the appropriate sprite self.sprites[index].alpha = 1; }; self.update = function () { // Calculate base speed var currentSpeed = self.speed + Math.floor(LK.ticks / 300) * 0.2; // Increase speed over time faster // Double speed during star power if (starPowerActive) { currentSpeed *= 2; } self.x -= currentSpeed; if (self.isJumping) { self.y += self.velocityY; self.velocityY += 0.5; // Gravity effect if (self.y > 2732) { self.destroy(); } } else { self.runFrameCounter++; if (self.runFrameCounter >= self.runFrameDelay) { self.runFrameIndex = (self.runFrameIndex + 1) % self.runFrames.length; self.showFrame(self.runFrameIndex); self.runFrameCounter = 0; } } if (self.x < -100) { self.destroy(); } }; }); var FlyKoopa = Container.expand(function () { var self = Container.call(this); self.runFrames = ['flykoopa1', 'flykoopa2']; self.sprites = []; // Pre-attach all sprites for (var i = 0; i < self.runFrames.length; i++) { var sprite = self.attachAsset(self.runFrames[i], { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); sprite.alpha = i === 0 ? 1 : 0; self.sprites.push(sprite); } self.speed = 4 + Math.random() * 2; self.runFrameIndex = 0; self.runFrameCounter = 0; self.runFrameDelay = 15; self.isJumping = false; self.velocityY = 0; self.canHarmPlayer = true; // Flag to track if enemy can harm player self.showFrame = function (index) { // Hide all sprites first for (var i = 0; i < self.sprites.length; i++) { self.sprites[i].alpha = 0; } // Show the appropriate sprite self.sprites[index].alpha = 1; }; self.update = function () { // Increase speed over time, up to a reasonable maximum self.speed = 4 + Math.random() * 2 + Math.min(6, Math.floor(LK.ticks / 300) * 0.3); // Double speed during star power if (starPowerActive) { self.speed *= 2; } self.x -= self.speed; if (self.isJumping) { self.y += self.velocityY; self.velocityY += 0.5; // Gravity effect if (self.y > 2732) { self.destroy(); } } else { self.runFrameCounter++; if (self.runFrameCounter >= self.runFrameDelay) { self.runFrameIndex = (self.runFrameIndex + 1) % self.runFrames.length; self.showFrame(self.runFrameIndex); self.runFrameCounter = 0; } } if (self.x < -100) { self.destroy(); } }; }); var Goomba = Container.expand(function () { var self = Container.call(this); self.runFrames = ['goomba1', 'goomba2']; self.sprites = []; // Pre-attach all sprites for (var i = 0; i < self.runFrames.length; i++) { var sprite = self.attachAsset(self.runFrames[i], { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); sprite.alpha = i === 0 ? 1 : 0; self.sprites.push(sprite); } self.speed = 4 + Math.random() * 2; self.passed = false; self.isJumping = false; self.velocityY = 0; self.canHarmPlayer = true; // Flag to track if enemy can harm player self.groundY = 2732 - 400; // 400 pixels from bottom self.y = self.groundY; // Set initial position to ground level self.runFrameIndex = 0; self.runFrameCounter = 0; self.runFrameDelay = 15; self.showFrame = function (index) { // Hide all sprites first for (var i = 0; i < self.sprites.length; i++) { self.sprites[i].alpha = 0; } // Show the appropriate sprite self.sprites[index].alpha = 1; }; self.update = function () { // Increase speed over time, up to a reasonable maximum self.speed = 4 + Math.random() * 2 + Math.min(6, Math.floor(LK.ticks / 300) * 0.3); // Double speed during star power if (starPowerActive) { self.speed *= 2; } self.x -= self.speed; if (self.isJumping) { self.y += self.velocityY; self.velocityY += 0.5; // Gravity effect if (self.y > 2732) { self.destroy(); } } else { self.runFrameCounter++; if (self.runFrameCounter >= self.runFrameDelay) { self.runFrameIndex = (self.runFrameIndex + 1) % self.runFrames.length; self.showFrame(self.runFrameIndex); self.runFrameCounter = 0; } } if (self.x < -100) { self.destroy(); } }; }); var MainMenu = Container.expand(function () { var self = Container.call(this); // Add background image var background = self.attachAsset('background3', { anchorX: 0, anchorY: 0 }); // Title text var titleText = new Text2('OG MARIO', { size: 180, fill: 0xE75A10, //{3t} // NES Mario brick-red color font: "'Press Start 2P', monospace" }); titleText.anchor.set(0.5, 0.5); titleText.x = 2048 / 2; titleText.y = 2732 / 4; // Position higher on screen self.addChild(titleText); // No subtitle text // Play button var playButton = new Text2('TAP TO START', { size: 120, fill: 0xFCA817, // NES Mario gold coin color font: "'Press Start 2P', monospace" }); playButton.anchor.set(0.5, 0.5); playButton.x = 2048 / 2; playButton.y = 2732 / 2; // Centered vertically self.addChild(playButton); // Upgrade button var upgradeButton = new Text2('UPGRADES', { size: 90, fill: 0x4AC5FF, // NES Mario sky blue color font: "'Press Start 2P', monospace" }); upgradeButton.anchor.set(0.5, 0.5); upgradeButton.x = 2048 / 2; upgradeButton.y = 2732 / 2 + 200; // Closer to play button self.addChild(upgradeButton); // Make the play button "pulse" for attention self.animationCounter = 0; self.update = function () { self.animationCounter += 0.05; playButton.scale.x = 1 + Math.sin(self.animationCounter) * 0.1; playButton.scale.y = 1 + Math.sin(self.animationCounter) * 0.1; }; // Handle button clicks self.down = function (x, y, obj) { // Check if upgrade button was clicked if (x > upgradeButton.x - 200 && x < upgradeButton.x + 200 && y > upgradeButton.y - 50 && y < upgradeButton.y + 50) { if (self.onUpgradeClick) { self.onUpgradeClick(); } return true; // Prevent propagation } // Default behavior - start game if (self.onPlayClick) { self.onPlayClick(); } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); self.runAnimation = ['player_run1', 'player_run2', 'player_run3']; self.jumpAnimation = ['player_jump']; self.currentState = 'running'; self.sprites = []; self.runFrame = 0; self.jumpFrame = 0; self.animationCounter = 0; self.animationSpeed = 0.1; // Pre-attach all sprites for (var i = 0; i < self.runAnimation.length; i++) { var sprite = self.attachAsset(self.runAnimation[i], { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); sprite.alpha = i === 0 ? 1 : 0; self.sprites.push(sprite); } for (var j = 0; j < self.jumpAnimation.length; j++) { var jumpSprite = self.attachAsset(self.jumpAnimation[j], { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5 }); jumpSprite.alpha = 0; self.sprites.push(jumpSprite); } self.speed = 5; // Base speed, will increase in update function self.jumpHeight = 25; self.isJumping = false; self.canDoubleJump = false; // Track if double jump is available self.hasDoubleJumped = false; // Track if double jump was used self.isDashing = false; // Track if player is currently dashing self.isInvincible = false; // Track if player is invincible self.dashCooldown = false; // Dash cooldown self.swipeStartX = null; // Track swipe start position self.swipeStartY = null; self.velocityY = 0; self.groundY = 2732 - 400; // 400 pixels from bottom self.y = self.groundY; // Set initial position to ground level self.runFrameDelay = 10; self.showFrame = function (frameType, index) { // Hide all sprites first for (var i = 0; i < self.sprites.length; i++) { self.sprites[i].alpha = 0; } // Show the appropriate sprite based on state and frame index if (frameType === 'run') { self.sprites[index].alpha = 1; } else if (frameType === 'jump') { self.sprites[self.runAnimation.length + index].alpha = 1; } }; self.dash = function () { // Only dash if the upgrade is purchased and not on cooldown if (storage.dashPurchased && !self.dashCooldown && !self.isDashing) { self.isDashing = true; self.isInvincible = true; // Create a distinct visual effect for dash - cyan trail and blue flash LK.effects.flashObject(self, 0x00FFFF, 500); // Cyan flash for dash // Store original position to create trail effect var startX = self.x; // Move player forward quickly self.x += 300; // Create "speed lines" effect for (var i = 0; i < 5; i++) { var trailEffect = LK.getAsset('player_jump', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5 - i * 0.2, scaleY: 1.5 - i * 0.2, x: startX + i * 60, y: self.y, alpha: 0.7 - i * 0.1 }); trailEffect.tint = 0x00FFFF; // Cyan trail game.addChild(trailEffect); // Fade out and remove the trail effect var trailIndex = i; LK.setTimeout(function () { if (trailEffect && trailEffect.parent) { trailEffect.parent.removeChild(trailEffect); } }, 100 + trailIndex * 50); } // Set invincibility LK.setTimeout(function () { self.isInvincible = false; self.isDashing = false; }, 500); // Set cooldown self.dashCooldown = true; LK.setTimeout(function () { self.dashCooldown = false; }, 2000); // 2 second cooldown } }; self.checkSwipe = function (endX, endY) { if (self.swipeStartX === null || self.swipeStartY === null) { return; } var deltaX = endX - self.swipeStartX; var deltaY = endY - self.swipeStartY; // Only consider horizontal swipes with minimal vertical movement if (Math.abs(deltaX) > 100 && Math.abs(deltaY) < 50 && deltaX > 0) { self.dash(); } // Reset swipe tracking self.swipeStartX = null; self.swipeStartY = null; }; self.down = function (x, y, obj) { self.swipeStartX = x; self.swipeStartY = y; }; self.up = function (x, y, obj) { self.checkSwipe(x, y); }; self.move = function (x, y, obj) { // Optional: For more responsive swipe detection // If pointer moves far enough horizontally, trigger dash if (self.swipeStartX !== null && Math.abs(x - self.swipeStartX) > 150) { self.checkSwipe(x, y); } }; self.update = function () { // Update player speed based on game progress and background speed // Use background speed as base for player speed scaling if available var speedMultiplier = 1; if (game.background && game.background.speed) { speedMultiplier = game.background.speed / 3; } self.speed = 5 + Math.min(5, Math.floor(LK.ticks / 300) * 0.3 * speedMultiplier); // Make player blink fast during star power if (starPowerActive) { // Cancel any existing tween on player tween.stop(self); // Use tween to create a more advanced flashing effect with color changes if (!self.starTweenActive) { var _flashNextColor = function flashNextColor() { // Apply the next color in the sequence tween(self, { tint: colors[colorIndex] }, { duration: 150, // Fast color transitions easing: tween.linear, onFinish: function onFinish() { // Cycle to the next color colorIndex = (colorIndex + 1) % colors.length; // Continue the cycle if star power is still active if (starPowerActive) { _flashNextColor(); } else { self.tint = 0xFFFFFF; // Reset tint when star power ends self.starTweenActive = false; } } }); }; // Start the flash sequence self.starTweenActive = true; // Create rapid color cycling effect var colors = [0xFFFF00, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF00FF]; var colorIndex = 0; _flashNextColor(); } } else { // Ensure player is fully visible when star power ends self.alpha = 1; self.tint = 0xFFFFFF; // Reset tint self.starTweenActive = false; } if (self.isJumping) { self.y += self.velocityY; // Scale gravity based on game speed - faster speed = faster falling var gravityMultiplier = speedMultiplier > 1 ? speedMultiplier * 0.8 : 1; self.velocityY += 0.3 * gravityMultiplier; // Show jump sprite self.showFrame('jump', 0); if (self.y >= self.groundY - 30) { if (self.y < 0) { // Prevent jump from exceeding the top of the screen self.y = 0; self.velocityY = 0; } if (!self.isDead) { self.y = self.groundY - 30; self.isJumping = false; self.hasDoubleJumped = false; // Reset double jump when landing self.velocityY = 0; self.currentState = 'running'; // Reset combo when landing if (comboActive) { comboActive = false; // If we had a combo going, display the final combo if (comboCount > 1) { if (!comboDisplayObj) { comboDisplayObj = game.addChild(new ComboDisplay()); comboDisplayObj.x = 2048 / 2; comboDisplayObj.y = 2732 / 2 - 300; var comboPoints = comboCount * 50 * 2; // Double points for combos comboDisplayObj.showCombo(comboCount, comboPoints); } } comboCount = 0; // Reset comboDisplayObj to ensure fresh display for next combo comboDisplayObj = null; } } } } else { // Running animation self.animationCounter += self.animationSpeed; if (self.animationCounter >= 1) { self.animationCounter = 0; self.runFrame = (self.runFrame + 1) % self.runAnimation.length; self.showFrame('run', self.runFrame); } } }; self.jump = function () { // Get speed multiplier based on background speed var speedMultiplier = 1; if (game.background && game.background.speed) { speedMultiplier = game.background.speed / 3; } // Increase jump height slightly with game speed to help player clear obstacles var jumpHeightMultiplier = Math.min(1.3, 1 + (speedMultiplier - 1) * 0.3); // Regular jump when not jumping if (!self.isJumping) { self.isJumping = true; LK.getSound('jump').play(); self.velocityY = -self.jumpHeight * jumpHeightMultiplier; self.currentState = 'jumping'; self.showFrame('jump', 0); } // Double jump when already jumping and upgrade purchased else if (self.isJumping && storage.doubleJumpPurchased && !self.hasDoubleJumped) { LK.getSound('jump').play(); self.velocityY = -self.jumpHeight * 0.8 * jumpHeightMultiplier; // Slightly lower second jump self.hasDoubleJumped = true; // No flash effect for double jump to make it distinct from dash } }; }); var Star = Container.expand(function () { var self = Container.call(this); // Use star asset directly instead of recoloring coin var starGraphics = self.attachAsset('star', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); // Set star color to bright yellow for emphasis starGraphics.tint = 0xFFFF00; self.speed = 3; self.collected = false; self.y = 2732 - 600; // Position stars higher than the ground // Add animation self.animationCounter = 0; self.update = function () { if (!game.playerDead) { // Increase speed over time, matching background speed self.speed = 3 + Math.min(8, Math.floor(LK.ticks / 300) * 0.3); // Double speed during star power if (starPowerActive) { self.speed *= 2; } self.x -= self.speed; // If we haven't started oscillation animation yet, initialize it if (!self.oscillating) { self.originalY = self.y; // Store original position self.originalX = self.x; // Store original X position self.oscillating = true; // Set up horizontal forward oscillation - star moves forward and backward tween(self, { xOffset: 300 // Move forward by 300px }, { duration: 2000, easing: tween.sineInOut, onUpdate: function onUpdate(obj, progress) { // Apply horizontal oscillation while still allowing normal movement // This creates a zigzag forward motion as the star still moves left overall var oscillationX = Math.sin(progress * Math.PI * 2) * 150; self.x += oscillationX - (self.lastOscillationX || 0); self.lastOscillationX = oscillationX; }, onFinish: function onFinish() { self.lastOscillationX = 0; // Restart horizontal oscillation to create continuous movement self.oscillating = false; } }); // Set up vertical oscillation with tween - full screen height movement tween(self, { y: 100 // Move to the top of the screen }, { duration: 3000, // Slower, smoother movement easing: tween.sineInOut, // Smoother easing onFinish: function onFinish() { // When reaching the top, go all the way down tween(self, { y: 2732 - 100 // Move to the bottom of the screen }, { duration: 3000, // Matching duration for smooth cycle easing: tween.sineInOut, onFinish: function onFinish() { // Reset animation when complete self.oscillating = false; } }); } }); // Setup enhanced pulsing animation with tween - larger size variation tween(self.scale, { x: 1.3, // Increase more (was 1.15) y: 1.3 // Increase more (was 1.15) }, { duration: 900, easing: tween.sineInOut, onFinish: function onFinish() { tween(self.scale, { x: 0.8, // Decrease more (was 0.85) y: 0.8 // Decrease more (was 0.85) }, { duration: 900, easing: tween.sineInOut, onFinish: function onFinish() { // Scale animation will reset when complete if (self.parent) { self.scale.x = 1; self.scale.y = 1; } } }); } }); } } if (self.x < -100) { // Cancel any active tweens when destroying tween.stop(self); tween.stop(self.scale); self.destroy(); } }; return self; }); var UpgradeMenu = Container.expand(function () { var self = Container.call(this); // Background panel var panel = self.attachAsset('gauge_background', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 4 }); panel.alpha = 0.9; // Title var titleText = new Text2('UPGRADES', { size: 120, fill: 0xE75A10, //{5b} // NES Mario brick-red color font: "'Press Start 2P', monospace" }); titleText.anchor.set(0.5, 0.5); titleText.x = 0; titleText.y = -350; self.addChild(titleText); // Coin display var coinDisplay = new Text2('Coins: ' + (storage.coins || 0), { size: 80, fill: 0xFCA817, //{5h} // NES Mario gold coin color font: "'Press Start 2P', monospace" }); coinDisplay.anchor.set(0.5, 0.5); coinDisplay.x = 0; coinDisplay.y = -220; self.addChild(coinDisplay); // Double Jump upgrade button var doubleJumpButton = new Text2('Double Jump: ' + (storage.doubleJumpPurchased ? 'PURCHASED' : '5 COINS'), { size: 70, fill: storage.doubleJumpPurchased ? 0x00FF00 : 0xFFFFFF, font: "'Press Start 2P', monospace" }); doubleJumpButton.anchor.set(0.5, 0.5); doubleJumpButton.x = 0; doubleJumpButton.y = -50; self.addChild(doubleJumpButton); // Dash upgrade button var dashButton = new Text2('Dash: ' + (storage.dashPurchased ? 'PURCHASED' : '8 COINS'), { size: 70, fill: storage.dashPurchased ? 0x00FF00 : 0xFFFFFF, font: "'Press Start 2P', monospace" }); dashButton.anchor.set(0.5, 0.5); dashButton.x = 0; dashButton.y = 80; self.addChild(dashButton); // Close button var closeButton = new Text2('BACK TO MENU', { size: 80, fill: 0xE75A10, //{5t} // NES Mario brick-red color font: "'Press Start 2P', monospace" }); closeButton.anchor.set(0.5, 0.5); closeButton.x = 0; closeButton.y = 300; self.addChild(closeButton); // Update coin display self.updateCoinDisplay = function () { coinDisplay.setText('Coins: ' + (storage.coins || 0)); doubleJumpButton.setText('Double Jump: ' + (storage.doubleJumpPurchased ? 'PURCHASED' : '5 COINS'), { size: 70, fill: storage.doubleJumpPurchased ? 0x20D060 : 0xFCE29F, //{5x} // NES green for purchased, light cream for not font: "'Press Start 2P', monospace" }); dashButton.setText('Dash: ' + (storage.dashPurchased ? 'PURCHASED' : '8 COINS'), { size: 70, fill: storage.dashPurchased ? 0x20D060 : 0xFCE29F, //{5B} // NES green for purchased, light cream for not font: "'Press Start 2P', monospace" }); }; // Handle purchase self.purchaseDoubleJump = function () { if (!storage.doubleJumpPurchased && (storage.coins || 0) >= 5) { storage.coins -= 5; storage.doubleJumpPurchased = true; self.updateCoinDisplay(); } }; // Handle dash purchase self.purchaseDash = function () { if (!storage.dashPurchased && (storage.coins || 0) >= 8) { storage.coins -= 8; storage.dashPurchased = true; self.updateCoinDisplay(); } }; // Handle button presses self.down = function (x, y, obj) { // Direct comparison without conversion as x,y are already in local coordinates // Check if double jump button was pressed if (Math.abs(x - doubleJumpButton.x) < 400 && Math.abs(y - doubleJumpButton.y) < 50) { self.purchaseDoubleJump(); } // Check if dash button was pressed if (Math.abs(x - dashButton.x) < 400 && Math.abs(y - dashButton.y) < 50) { self.purchaseDash(); } // Check if close button was pressed if (Math.abs(x - closeButton.x) < 300 && Math.abs(y - closeButton.y) < 50) { if (self.onClose) { self.onClose(); } } }; return self; }); /**** * Initialize Game ****/ // Helper function to adjust music playback rate as LK.setMusicPlaybackRate is not available /**** *-Seperator- ****/ var game = new LK.Game({ backgroundColor: 0x5C94FC // NES Mario sky blue background color }); /**** * Game Code ****/ // Helper function to adjust music playback rate as LK.setMusicPlaybackRate is not available // Game state variables function setMusicPlaybackRate(rate) { // Check if music is playing before trying to adjust it // Use the sound API instead of the non-existent LK.getMusic function var music = LK.getSound('theme'); if (music) { // Set the playback rate if available on the audio element if (music.playbackRate !== undefined) { music.playbackRate = rate; } } } var gameStarted = false; var showingUpgradeMenu = false; var mainMenu; var upgradeMenu; var background; var background2; var player; var enemies = []; var enemySpawnInterval = 80; var enemySpawnCounter = 0; var coinObjects = []; // Renamed from 'coins' to avoid conflict with coin counter var coinSpawnInterval = Math.max(40, 150 - Math.floor(LK.ticks / 300) * 2); // Decrease interval faster, minimum 40 var coinSpawnCounter = 0; var stars = []; // Array to store star power-ups var starSpawnInterval = 500; // Spawn stars less frequently than coins var starSpawnCounter = 0; var starPowerActive = false; // Track if star power is active var starPowerDuration = 10 * 60; // 10 seconds at 60fps var starPowerTimer = 0; // Timer for star power // Combo system variables var comboCount = 0; var comboActive = false; var comboDisplayObj = null; // Score system var score = 0; var coins = storage.coins || 0; // Get persistent coin count var scoreText = new Text2('Score: 0', { size: 60, fill: 0xFCE29F, //{67} // NES light tan/cream color font: "'Press Start 2P', monospace" }); // Add a speed indicator to show the increasing difficulty var speedText = new Text2('Speed: 1x', { size: 40, fill: 0xFCE29F, //{6a} // NES light tan/cream color font: "'Press Start 2P', monospace" }); speedText.anchor.set(1, 0); speedText.x = -50; speedText.y = 180; // Position below coin count LK.gui.topRight.addChild(speedText); speedText.alpha = 0; // Hide initially scoreText.anchor.set(1, 0); scoreText.x = -50; scoreText.y = 50; LK.gui.topRight.addChild(scoreText); // Coin count display var coinText = new Text2('Coins: ' + coins, { size: 50, fill: 0xFCA817, //{6d} // NES Mario gold coin color // Gold color for coins font: "'Press Start 2P', monospace" }); coinText.anchor.set(1, 0); coinText.x = -50; coinText.y = 120; // Position below score LK.gui.topRight.addChild(coinText); // Combo indicator for HUD var comboText = new Text2('Combo: 0', { size: 50, fill: 0xFFD700, // Gold color for combo text font: "'Press Start 2P', monospace" }); comboText.anchor.set(1, 0); comboText.x = -50; comboText.y = 240; // Position below coin count LK.gui.topRight.addChild(comboText); comboText.alpha = 0; // Hide combo count initially scoreText.alpha = 0; // Hide score initially coinText.alpha = 0; // Hide coin count initially // Initialize upgrades in storage if needed if (storage.doubleJumpPurchased === undefined) { storage.doubleJumpPurchased = false; } if (storage.dashPurchased === undefined) { storage.dashPurchased = false; } // Show main menu first mainMenu = game.addChild(new MainMenu()); mainMenu.onUpgradeClick = function () { showingUpgradeMenu = true; mainMenu.visible = false; // Create upgrade menu upgradeMenu = game.addChild(new UpgradeMenu()); upgradeMenu.x = 2048 / 2; upgradeMenu.y = 2732 / 2; upgradeMenu.updateCoinDisplay(); upgradeMenu.onClose = function () { showingUpgradeMenu = false; upgradeMenu.destroy(); upgradeMenu = null; mainMenu.visible = true; // Update coin display on main menu coinText.setText('Coins: ' + (storage.coins || 0)); }; }; mainMenu.onPlayClick = function () { // Start the game gameStarted = true; mainMenu.destroy(); mainMenu = null; // Initialize game elements - handled in game.down }; game.update = function () { // Handle menu states if (!gameStarted) { if (showingUpgradeMenu) { // When in upgrade menu, no need to update main menu return; } // When in menu mode, just update the menu if (mainMenu) { mainMenu.update(); } return; } // Game hasn't been initialized yet - start it if (!background) { // Initialize game elements LK.playMusic('theme'); // Create background background = game.addChild(new Background()); background.x = 0; background.y = 0; background2 = game.addChild(new Background2()); background2.x = 2048; background2.y = 0; // Store a reference to the background in the game object for access from other objects game.background = background; // Create player player = game.addChild(new Player()); player.x = 2048 / 4; player.y = player.groundY - 30; // Show score, coin count and speed scoreText.alpha = 1; coinText.alpha = 1; speedText.alpha = 1; } // Game play update logic background.update(); background2.update(); player.update(); enemySpawnCounter++; coinSpawnCounter++; starSpawnCounter++; // Handle star power-up timer if (starPowerActive) { starPowerTimer--; if (starPowerTimer <= 0) { starPowerActive = false; player.isInvincible = false; // Return background tint to normal background.sprites[0].tint = 0xFFFFFF; background2.sprites[0].tint = 0xFFFFFF; // Switch back to regular theme music LK.stopMusic(); LK.playMusic('theme'); } } if (coinSpawnCounter >= coinSpawnInterval) { var coin = new Coin(); coin.x = 2048 + Math.random() * 200; // Set a reasonable height range for coins (not too low, not too high) coin.y = Math.max(300, Math.min(2732 - 600, Math.random() * (2732 - 800))); coinObjects.push(coin); game.addChild(coin); coinSpawnCounter = 0; } // Spawn star power-ups occasionally, but only if star power is not active if (starSpawnCounter >= starSpawnInterval && !starPowerActive) { var star = new Star(); star.x = 2048 + Math.random() * 200; // Set a reasonable height range for stars (not too low, not too high) star.y = Math.max(300, Math.min(2732 - 600, Math.random() * (2732 - 800))); stars.push(star); game.addChild(star); starSpawnCounter = 0; starSpawnInterval = Math.floor(Math.random() * 300) + 300; // Random interval between 300-600 ticks } if (enemySpawnCounter >= enemySpawnInterval) { var enemyType = Math.random() < 0.33 ? 'Enemy' : Math.random() < 0.5 ? 'Goomba' : 'FlyKoopa'; var enemy; if (enemyType === 'Enemy') { enemy = new Enemy(); } else if (enemyType === 'Goomba') { enemy = new Goomba(); } else { enemy = new FlyKoopa(); // Ensure FlyKoopas spawn in the middle to upper part of the screen, not too low enemy.y = Math.max(300, Math.min(2732 - 600, Math.random() * (2732 - 800))); } enemy.x = 2048 + Math.random() * 200; if (enemyType === 'FlyKoopa') { // Height for FlyKoopa is already set earlier, no need to set it again } else { enemy.y = enemy.groundY; // Use groundY for initial position } enemies.push(enemy); game.addChild(enemy); enemySpawnInterval = Math.max(15, Math.floor(Math.random() * 100) + 60 - Math.floor(LK.ticks / 300) * 2); // Decrease interval faster, minimum 15 enemySpawnCounter = 0; } for (var j = enemies.length - 1; j >= 0; j--) { enemies[j].update(); if (player.intersects(enemies[j])) { if (starPowerActive && enemies[j].canHarmPlayer !== false) { // Star power is active - instantly defeat enemy with special effect enemies[j].velocityY = -15; // Stronger pop up effect for star power enemies[j].isJumping = true; enemies[j].canHarmPlayer = false; LK.getSound('stomp').play(); // Create a sparkle effect LK.effects.flashObject(enemies[j], 0xFFFF00, 300); // Handle combo tracking - activate combo mode if not already active if (!comboActive) { comboActive = true; comboCount = 0; } // Increment combo count comboCount++; // Calculate score with combo bonus var starEnemyPoints = 100 * comboCount; score += starEnemyPoints; // Display flash text for combo count if more than 1 if (comboCount > 1) { if (!comboDisplayObj) { comboDisplayObj = game.addChild(new ComboDisplay()); comboDisplayObj.x = enemies[j].x; comboDisplayObj.y = enemies[j].y - 50; comboDisplayObj.showCombo(comboCount, starEnemyPoints); } } scoreText.setText('Score: ' + score); LK.setScore(score); } else if (!player.isDead && player.isJumping && player.velocityY > 0 && player.y < enemies[j].y) { // Player is falling and lands on enemy enemies[j].velocityY = -10; // Pop up effect for enemy player.velocityY = -15; // Bounce Mario upwards LK.getSound('stomp').play(); // Play stomp sound // Handle combo tracking - activate combo mode if not already active if (!comboActive) { comboActive = true; comboCount = 0; } // Increment combo count comboCount++; // Calculate score with combo bonus var enemyPoints = 50; if (comboCount > 1) { // Apply combo multiplier - each enemy in combo is worth more enemyPoints = 50 * comboCount; } score += enemyPoints; // Display flash text for combo count if more than 1 if (comboCount > 1) { if (!comboDisplayObj) { comboDisplayObj = game.addChild(new ComboDisplay()); comboDisplayObj.x = enemies[j].x; comboDisplayObj.y = enemies[j].y - 50; comboDisplayObj.showCombo(comboCount, enemyPoints); } } scoreText.setText('Score: ' + score); LK.setScore(score); enemies[j].isJumping = true; enemies[j].canHarmPlayer = false; // Enemy can no longer harm player } else if (enemies[j].canHarmPlayer !== false && !player.isInvincible) { if (!player.isDead && enemies[j].canHarmPlayer !== false) { LK.getSound('dead').play(); // Play dead sound player.isDead = true; game.playerDead = true; player.velocityY = -10; // Pop up effect similar to enemy player.isJumping = true; LK.setTimeout(function () { // Save final score before game over LK.setScore(score); LK.showGameOver(); }, 3000); // Delay game over by 3 seconds } } } else if (player.x > enemies[j].x && !enemies[j].passed) { enemies[j].passed = true; } if (enemies[j].x < -100) { enemies.splice(j, 1); } } for (var k = coinObjects.length - 1; k >= 0; k--) { coinObjects[k].update(); if (player.intersects(coinObjects[k])) { if (!coinObjects[k].collected) { coinObjects[k].collected = true; LK.getSound('Coin').play(); // Play coin sound // Increase score when collecting coins score += 10; // Separate coin counter coins++; // Update storage for persistence storage.coins = coins; // Update displays scoreText.setText('Score: ' + score); coinText.setText('Coins: ' + coins); LK.setScore(score); // Create a coin pickup effect // Visual effects for coin collection - similar to star power but smaller LK.effects.flashObject(player, 0xFCA817, 300); // Gold flash // Create a coin burst effect for (var i = 0; i < 8; i++) { var angle = i / 8 * Math.PI * 2; var distance = 60; var particleX = coinObjects[k].x + Math.cos(angle) * distance; var particleY = coinObjects[k].y + Math.sin(angle) * distance; var particle = LK.getAsset('coin', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.4, scaleY: 0.4, x: coinObjects[k].x, y: coinObjects[k].y, alpha: 0.8 }); particle.tint = 0xFCA817; // Gold tint game.addChild(particle); // Animate particles outward tween(particle, { x: particleX, y: particleY, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 800, easing: tween.easeOut }); // Remove particles after animation LK.setTimeout(function (p) { return function () { if (p && p.parent) { p.parent.removeChild(p); } }; }(particle), 800); } coinObjects[k].destroy(); coinObjects.splice(k, 1); } } } // Update and check star power-ups for (var s = stars.length - 1; s >= 0; s--) { stars[s].update(); if (player.intersects(stars[s])) { if (!stars[s].collected) { stars[s].collected = true; // Switch music to star power theme LK.stopMusic(); LK.playMusic('starman2'); // Activate star power starPowerActive = true; starPowerTimer = starPowerDuration; player.isInvincible = true; // Visual effects for star power LK.effects.flashObject(player, 0xFFFF00, 500); // Yellow flash // Create a star burst effect for (var i = 0; i < 12; i++) { var angle = i / 12 * Math.PI * 2; var distance = 100; var particleX = player.x + Math.cos(angle) * distance; var particleY = player.y + Math.sin(angle) * distance; var particle = LK.getAsset('coin', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5, x: player.x, y: player.y, alpha: 0.8 }); particle.tint = 0xFFFF00; // Yellow tint game.addChild(particle); // Animate particles outward tween(particle, { x: particleX, y: particleY, alpha: 0, scaleX: 0.1, scaleY: 0.1 }, { duration: 1000, easing: tween.easeOut }); // Remove particles after animation LK.setTimeout(function (p) { return function () { if (p && p.parent) { p.parent.removeChild(p); } }; }(particle), 1000); } // Change background tint for star power effect if (background.sprites && background.sprites.length > 0) { background.sprites[0].tint = 0xFFFFAA; // Slight yellow tint } if (background2.sprites && background2.sprites.length > 0) { background2.sprites[0].tint = 0xFFFFAA; // Slight yellow tint } // Add extra points for star score += 100; scoreText.setText('Score: ' + score); LK.setScore(score); // Remove the star stars[s].destroy(); stars.splice(s, 1); } } else if (stars[s].x < -100) { stars[s].destroy(); stars.splice(s, 1); } } // Update speed display and music speed if (background && !game.playerDead) { var speedMultiplier = (background.speed / 3).toFixed(1); // Show star power status in speed text if active if (starPowerActive) { var starTimeLeft = Math.ceil(starPowerTimer / 60); speedText.setText('STAR POWER: ' + starTimeLeft + 's', { size: 40, fill: 0xFFFF00, // Yellow for star power font: "'Press Start 2P', monospace" }); } else { speedText.setText('Speed: ' + speedMultiplier + 'x', { size: 40, fill: 0xFCE29F, // Return to normal color font: "'Press Start 2P', monospace" }); } // Update combo text in HUD comboText.setText('Combo: ' + comboCount); comboText.alpha = gameStarted ? 1 : 0; // Adjust music playback rate based on game speed var musicPlaybackRate = Math.min(1.5, 0.8 + background.speed / 3 * 0.2); // Increase music speed during star power if (starPowerActive) { musicPlaybackRate = Math.min(2.0, musicPlaybackRate * 1.2); } setMusicPlaybackRate(musicPlaybackRate); } }; game.down = function (x, y, obj) { if (showingUpgradeMenu) { // Let the upgrade menu handle this event return; } if (!gameStarted) { // Let the main menu handle this event return; } else { // Pass event to player to track swipes player.down(x, y, obj); // In-game tap makes player jump player.jump(); } }; game.up = function (x, y, obj) { if (showingUpgradeMenu || !gameStarted) { return; } // Pass touch up event to player for swipe detection if (player) { player.up(x, y, obj); } }; game.move = function (x, y, obj) { if (showingUpgradeMenu || !gameStarted) { return; } // Pass touch move event to player for swipe detection if (player) { player.move(x, y, obj); } };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
coins: 0
});
/****
* Classes
****/
var Background = Container.expand(function () {
var self = Container.call(this);
var backgrounds = ['background1', 'background2', 'background3'];
var randomBackground = backgrounds[Math.floor(Math.random() * backgrounds.length)];
var backgroundGraphics = self.attachAsset(randomBackground, {
anchorX: 0,
anchorY: 0
});
self.sprites = [backgroundGraphics]; // Store sprites for access
self.speed = 3;
self.update = function () {
if (!game.playerDead) {
// Increase speed over time, up to a reasonable maximum
self.speed = 3 + Math.min(8, Math.floor(LK.ticks / 300) * 0.3);
// Double speed during star power
if (starPowerActive) {
self.speed *= 2;
}
self.x -= self.speed;
}
if (self.x <= -2048) {
self.x = 2048;
}
};
});
var Background2 = Container.expand(function () {
var self = Container.call(this);
var backgrounds = ['background1', 'background2', 'background3'];
var randomBackground = backgrounds[Math.floor(Math.random() * backgrounds.length)];
var backgroundGraphics = self.attachAsset(randomBackground, {
anchorX: 0,
anchorY: 0
});
self.sprites = [backgroundGraphics]; // Store sprites for access
self.speed = 3;
self.update = function () {
if (!game.playerDead) {
// Increase speed over time, up to a reasonable maximum
self.speed = 3 + Math.min(8, Math.floor(LK.ticks / 300) * 0.3);
// Double speed during star power
if (starPowerActive) {
self.speed *= 2;
}
self.x -= self.speed;
}
if (self.x <= -2048) {
self.x = 2048;
}
};
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
self.speed = 3;
self.collected = false;
self.y = 2732 - 600; // Position coins higher than the ground
self.update = function () {
if (!game.playerDead) {
// Increase speed over time, up to a reasonable maximum
self.speed = 3 + Math.min(8, Math.floor(LK.ticks / 300) * 0.3);
// Double speed during star power
if (starPowerActive) {
self.speed *= 2;
}
self.x -= self.speed;
// Removed oscillation to keep coins from moving up and down
if (!self.oscillating) {
self.oscillating = true;
// No pulsing animation for coins
self.scale.x = 1;
self.scale.y = 1;
}
}
if (self.x < -100) {
// Cancel any active tweens when destroying
tween.stop(self);
tween.stop(self.scale);
self.destroy();
}
};
});
// ComboDisplay handles showing combo text on screen
var ComboDisplay = Container.expand(function () {
var self = Container.call(this);
var comboText = new Text2('', {
size: 60,
fill: 0xFFD700,
// Gold color for combo text
font: "'Press Start 2P', monospace"
});
comboText.anchor.set(0.5, 0.5);
self.addChild(comboText);
self.showCombo = function (comboCount, points) {
comboText.setText('COMBO x' + comboCount + '!\n+' + points + ' POINTS!');
comboText.alpha = 1;
// Animate the combo text with a scale effect
comboText.scale.x = 1.2;
comboText.scale.y = 1.2;
tween(comboText, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8,
y: comboText.y - 60 // Move up as it fades but not as far
}, {
duration: 800,
easing: tween.easeOut
});
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
self.runFrames = ['turtle1', 'turtle2'];
self.sprites = [];
// Pre-attach all sprites
for (var i = 0; i < self.runFrames.length; i++) {
var sprite = self.attachAsset(self.runFrames[i], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
sprite.alpha = i === 0 ? 1 : 0;
self.sprites.push(sprite);
}
self.speed = 6 + Math.random() * 2;
self.passed = false;
self.isJumping = false;
self.velocityY = 0;
self.canHarmPlayer = true; // Flag to track if enemy can harm player
self.groundY = 2732 - 400; // 400 pixels from bottom
self.y = self.groundY; // Set initial position to ground level
self.runFrameIndex = 0;
self.runFrameCounter = 0;
self.runFrameDelay = 15;
self.showFrame = function (index) {
// Hide all sprites first
for (var i = 0; i < self.sprites.length; i++) {
self.sprites[i].alpha = 0;
}
// Show the appropriate sprite
self.sprites[index].alpha = 1;
};
self.update = function () {
// Calculate base speed
var currentSpeed = self.speed + Math.floor(LK.ticks / 300) * 0.2; // Increase speed over time faster
// Double speed during star power
if (starPowerActive) {
currentSpeed *= 2;
}
self.x -= currentSpeed;
if (self.isJumping) {
self.y += self.velocityY;
self.velocityY += 0.5; // Gravity effect
if (self.y > 2732) {
self.destroy();
}
} else {
self.runFrameCounter++;
if (self.runFrameCounter >= self.runFrameDelay) {
self.runFrameIndex = (self.runFrameIndex + 1) % self.runFrames.length;
self.showFrame(self.runFrameIndex);
self.runFrameCounter = 0;
}
}
if (self.x < -100) {
self.destroy();
}
};
});
var FlyKoopa = Container.expand(function () {
var self = Container.call(this);
self.runFrames = ['flykoopa1', 'flykoopa2'];
self.sprites = [];
// Pre-attach all sprites
for (var i = 0; i < self.runFrames.length; i++) {
var sprite = self.attachAsset(self.runFrames[i], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
sprite.alpha = i === 0 ? 1 : 0;
self.sprites.push(sprite);
}
self.speed = 4 + Math.random() * 2;
self.runFrameIndex = 0;
self.runFrameCounter = 0;
self.runFrameDelay = 15;
self.isJumping = false;
self.velocityY = 0;
self.canHarmPlayer = true; // Flag to track if enemy can harm player
self.showFrame = function (index) {
// Hide all sprites first
for (var i = 0; i < self.sprites.length; i++) {
self.sprites[i].alpha = 0;
}
// Show the appropriate sprite
self.sprites[index].alpha = 1;
};
self.update = function () {
// Increase speed over time, up to a reasonable maximum
self.speed = 4 + Math.random() * 2 + Math.min(6, Math.floor(LK.ticks / 300) * 0.3);
// Double speed during star power
if (starPowerActive) {
self.speed *= 2;
}
self.x -= self.speed;
if (self.isJumping) {
self.y += self.velocityY;
self.velocityY += 0.5; // Gravity effect
if (self.y > 2732) {
self.destroy();
}
} else {
self.runFrameCounter++;
if (self.runFrameCounter >= self.runFrameDelay) {
self.runFrameIndex = (self.runFrameIndex + 1) % self.runFrames.length;
self.showFrame(self.runFrameIndex);
self.runFrameCounter = 0;
}
}
if (self.x < -100) {
self.destroy();
}
};
});
var Goomba = Container.expand(function () {
var self = Container.call(this);
self.runFrames = ['goomba1', 'goomba2'];
self.sprites = [];
// Pre-attach all sprites
for (var i = 0; i < self.runFrames.length; i++) {
var sprite = self.attachAsset(self.runFrames[i], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
sprite.alpha = i === 0 ? 1 : 0;
self.sprites.push(sprite);
}
self.speed = 4 + Math.random() * 2;
self.passed = false;
self.isJumping = false;
self.velocityY = 0;
self.canHarmPlayer = true; // Flag to track if enemy can harm player
self.groundY = 2732 - 400; // 400 pixels from bottom
self.y = self.groundY; // Set initial position to ground level
self.runFrameIndex = 0;
self.runFrameCounter = 0;
self.runFrameDelay = 15;
self.showFrame = function (index) {
// Hide all sprites first
for (var i = 0; i < self.sprites.length; i++) {
self.sprites[i].alpha = 0;
}
// Show the appropriate sprite
self.sprites[index].alpha = 1;
};
self.update = function () {
// Increase speed over time, up to a reasonable maximum
self.speed = 4 + Math.random() * 2 + Math.min(6, Math.floor(LK.ticks / 300) * 0.3);
// Double speed during star power
if (starPowerActive) {
self.speed *= 2;
}
self.x -= self.speed;
if (self.isJumping) {
self.y += self.velocityY;
self.velocityY += 0.5; // Gravity effect
if (self.y > 2732) {
self.destroy();
}
} else {
self.runFrameCounter++;
if (self.runFrameCounter >= self.runFrameDelay) {
self.runFrameIndex = (self.runFrameIndex + 1) % self.runFrames.length;
self.showFrame(self.runFrameIndex);
self.runFrameCounter = 0;
}
}
if (self.x < -100) {
self.destroy();
}
};
});
var MainMenu = Container.expand(function () {
var self = Container.call(this);
// Add background image
var background = self.attachAsset('background3', {
anchorX: 0,
anchorY: 0
});
// Title text
var titleText = new Text2('OG MARIO', {
size: 180,
fill: 0xE75A10,
//{3t} // NES Mario brick-red color
font: "'Press Start 2P', monospace"
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 2048 / 2;
titleText.y = 2732 / 4; // Position higher on screen
self.addChild(titleText);
// No subtitle text
// Play button
var playButton = new Text2('TAP TO START', {
size: 120,
fill: 0xFCA817,
// NES Mario gold coin color
font: "'Press Start 2P', monospace"
});
playButton.anchor.set(0.5, 0.5);
playButton.x = 2048 / 2;
playButton.y = 2732 / 2; // Centered vertically
self.addChild(playButton);
// Upgrade button
var upgradeButton = new Text2('UPGRADES', {
size: 90,
fill: 0x4AC5FF,
// NES Mario sky blue color
font: "'Press Start 2P', monospace"
});
upgradeButton.anchor.set(0.5, 0.5);
upgradeButton.x = 2048 / 2;
upgradeButton.y = 2732 / 2 + 200; // Closer to play button
self.addChild(upgradeButton);
// Make the play button "pulse" for attention
self.animationCounter = 0;
self.update = function () {
self.animationCounter += 0.05;
playButton.scale.x = 1 + Math.sin(self.animationCounter) * 0.1;
playButton.scale.y = 1 + Math.sin(self.animationCounter) * 0.1;
};
// Handle button clicks
self.down = function (x, y, obj) {
// Check if upgrade button was clicked
if (x > upgradeButton.x - 200 && x < upgradeButton.x + 200 && y > upgradeButton.y - 50 && y < upgradeButton.y + 50) {
if (self.onUpgradeClick) {
self.onUpgradeClick();
}
return true; // Prevent propagation
}
// Default behavior - start game
if (self.onPlayClick) {
self.onPlayClick();
}
};
return self;
});
var Player = Container.expand(function () {
var self = Container.call(this);
self.runAnimation = ['player_run1', 'player_run2', 'player_run3'];
self.jumpAnimation = ['player_jump'];
self.currentState = 'running';
self.sprites = [];
self.runFrame = 0;
self.jumpFrame = 0;
self.animationCounter = 0;
self.animationSpeed = 0.1;
// Pre-attach all sprites
for (var i = 0; i < self.runAnimation.length; i++) {
var sprite = self.attachAsset(self.runAnimation[i], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
sprite.alpha = i === 0 ? 1 : 0;
self.sprites.push(sprite);
}
for (var j = 0; j < self.jumpAnimation.length; j++) {
var jumpSprite = self.attachAsset(self.jumpAnimation[j], {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5
});
jumpSprite.alpha = 0;
self.sprites.push(jumpSprite);
}
self.speed = 5; // Base speed, will increase in update function
self.jumpHeight = 25;
self.isJumping = false;
self.canDoubleJump = false; // Track if double jump is available
self.hasDoubleJumped = false; // Track if double jump was used
self.isDashing = false; // Track if player is currently dashing
self.isInvincible = false; // Track if player is invincible
self.dashCooldown = false; // Dash cooldown
self.swipeStartX = null; // Track swipe start position
self.swipeStartY = null;
self.velocityY = 0;
self.groundY = 2732 - 400; // 400 pixels from bottom
self.y = self.groundY; // Set initial position to ground level
self.runFrameDelay = 10;
self.showFrame = function (frameType, index) {
// Hide all sprites first
for (var i = 0; i < self.sprites.length; i++) {
self.sprites[i].alpha = 0;
}
// Show the appropriate sprite based on state and frame index
if (frameType === 'run') {
self.sprites[index].alpha = 1;
} else if (frameType === 'jump') {
self.sprites[self.runAnimation.length + index].alpha = 1;
}
};
self.dash = function () {
// Only dash if the upgrade is purchased and not on cooldown
if (storage.dashPurchased && !self.dashCooldown && !self.isDashing) {
self.isDashing = true;
self.isInvincible = true;
// Create a distinct visual effect for dash - cyan trail and blue flash
LK.effects.flashObject(self, 0x00FFFF, 500); // Cyan flash for dash
// Store original position to create trail effect
var startX = self.x;
// Move player forward quickly
self.x += 300;
// Create "speed lines" effect
for (var i = 0; i < 5; i++) {
var trailEffect = LK.getAsset('player_jump', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5 - i * 0.2,
scaleY: 1.5 - i * 0.2,
x: startX + i * 60,
y: self.y,
alpha: 0.7 - i * 0.1
});
trailEffect.tint = 0x00FFFF; // Cyan trail
game.addChild(trailEffect);
// Fade out and remove the trail effect
var trailIndex = i;
LK.setTimeout(function () {
if (trailEffect && trailEffect.parent) {
trailEffect.parent.removeChild(trailEffect);
}
}, 100 + trailIndex * 50);
}
// Set invincibility
LK.setTimeout(function () {
self.isInvincible = false;
self.isDashing = false;
}, 500);
// Set cooldown
self.dashCooldown = true;
LK.setTimeout(function () {
self.dashCooldown = false;
}, 2000); // 2 second cooldown
}
};
self.checkSwipe = function (endX, endY) {
if (self.swipeStartX === null || self.swipeStartY === null) {
return;
}
var deltaX = endX - self.swipeStartX;
var deltaY = endY - self.swipeStartY;
// Only consider horizontal swipes with minimal vertical movement
if (Math.abs(deltaX) > 100 && Math.abs(deltaY) < 50 && deltaX > 0) {
self.dash();
}
// Reset swipe tracking
self.swipeStartX = null;
self.swipeStartY = null;
};
self.down = function (x, y, obj) {
self.swipeStartX = x;
self.swipeStartY = y;
};
self.up = function (x, y, obj) {
self.checkSwipe(x, y);
};
self.move = function (x, y, obj) {
// Optional: For more responsive swipe detection
// If pointer moves far enough horizontally, trigger dash
if (self.swipeStartX !== null && Math.abs(x - self.swipeStartX) > 150) {
self.checkSwipe(x, y);
}
};
self.update = function () {
// Update player speed based on game progress and background speed
// Use background speed as base for player speed scaling if available
var speedMultiplier = 1;
if (game.background && game.background.speed) {
speedMultiplier = game.background.speed / 3;
}
self.speed = 5 + Math.min(5, Math.floor(LK.ticks / 300) * 0.3 * speedMultiplier);
// Make player blink fast during star power
if (starPowerActive) {
// Cancel any existing tween on player
tween.stop(self);
// Use tween to create a more advanced flashing effect with color changes
if (!self.starTweenActive) {
var _flashNextColor = function flashNextColor() {
// Apply the next color in the sequence
tween(self, {
tint: colors[colorIndex]
}, {
duration: 150,
// Fast color transitions
easing: tween.linear,
onFinish: function onFinish() {
// Cycle to the next color
colorIndex = (colorIndex + 1) % colors.length;
// Continue the cycle if star power is still active
if (starPowerActive) {
_flashNextColor();
} else {
self.tint = 0xFFFFFF; // Reset tint when star power ends
self.starTweenActive = false;
}
}
});
}; // Start the flash sequence
self.starTweenActive = true;
// Create rapid color cycling effect
var colors = [0xFFFF00, 0xFF0000, 0x00FF00, 0x0000FF, 0xFF00FF];
var colorIndex = 0;
_flashNextColor();
}
} else {
// Ensure player is fully visible when star power ends
self.alpha = 1;
self.tint = 0xFFFFFF; // Reset tint
self.starTweenActive = false;
}
if (self.isJumping) {
self.y += self.velocityY;
// Scale gravity based on game speed - faster speed = faster falling
var gravityMultiplier = speedMultiplier > 1 ? speedMultiplier * 0.8 : 1;
self.velocityY += 0.3 * gravityMultiplier;
// Show jump sprite
self.showFrame('jump', 0);
if (self.y >= self.groundY - 30) {
if (self.y < 0) {
// Prevent jump from exceeding the top of the screen
self.y = 0;
self.velocityY = 0;
}
if (!self.isDead) {
self.y = self.groundY - 30;
self.isJumping = false;
self.hasDoubleJumped = false; // Reset double jump when landing
self.velocityY = 0;
self.currentState = 'running';
// Reset combo when landing
if (comboActive) {
comboActive = false;
// If we had a combo going, display the final combo
if (comboCount > 1) {
if (!comboDisplayObj) {
comboDisplayObj = game.addChild(new ComboDisplay());
comboDisplayObj.x = 2048 / 2;
comboDisplayObj.y = 2732 / 2 - 300;
var comboPoints = comboCount * 50 * 2; // Double points for combos
comboDisplayObj.showCombo(comboCount, comboPoints);
}
}
comboCount = 0;
// Reset comboDisplayObj to ensure fresh display for next combo
comboDisplayObj = null;
}
}
}
} else {
// Running animation
self.animationCounter += self.animationSpeed;
if (self.animationCounter >= 1) {
self.animationCounter = 0;
self.runFrame = (self.runFrame + 1) % self.runAnimation.length;
self.showFrame('run', self.runFrame);
}
}
};
self.jump = function () {
// Get speed multiplier based on background speed
var speedMultiplier = 1;
if (game.background && game.background.speed) {
speedMultiplier = game.background.speed / 3;
}
// Increase jump height slightly with game speed to help player clear obstacles
var jumpHeightMultiplier = Math.min(1.3, 1 + (speedMultiplier - 1) * 0.3);
// Regular jump when not jumping
if (!self.isJumping) {
self.isJumping = true;
LK.getSound('jump').play();
self.velocityY = -self.jumpHeight * jumpHeightMultiplier;
self.currentState = 'jumping';
self.showFrame('jump', 0);
}
// Double jump when already jumping and upgrade purchased
else if (self.isJumping && storage.doubleJumpPurchased && !self.hasDoubleJumped) {
LK.getSound('jump').play();
self.velocityY = -self.jumpHeight * 0.8 * jumpHeightMultiplier; // Slightly lower second jump
self.hasDoubleJumped = true;
// No flash effect for double jump to make it distinct from dash
}
};
});
var Star = Container.expand(function () {
var self = Container.call(this);
// Use star asset directly instead of recoloring coin
var starGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
// Set star color to bright yellow for emphasis
starGraphics.tint = 0xFFFF00;
self.speed = 3;
self.collected = false;
self.y = 2732 - 600; // Position stars higher than the ground
// Add animation
self.animationCounter = 0;
self.update = function () {
if (!game.playerDead) {
// Increase speed over time, matching background speed
self.speed = 3 + Math.min(8, Math.floor(LK.ticks / 300) * 0.3);
// Double speed during star power
if (starPowerActive) {
self.speed *= 2;
}
self.x -= self.speed;
// If we haven't started oscillation animation yet, initialize it
if (!self.oscillating) {
self.originalY = self.y; // Store original position
self.originalX = self.x; // Store original X position
self.oscillating = true;
// Set up horizontal forward oscillation - star moves forward and backward
tween(self, {
xOffset: 300 // Move forward by 300px
}, {
duration: 2000,
easing: tween.sineInOut,
onUpdate: function onUpdate(obj, progress) {
// Apply horizontal oscillation while still allowing normal movement
// This creates a zigzag forward motion as the star still moves left overall
var oscillationX = Math.sin(progress * Math.PI * 2) * 150;
self.x += oscillationX - (self.lastOscillationX || 0);
self.lastOscillationX = oscillationX;
},
onFinish: function onFinish() {
self.lastOscillationX = 0;
// Restart horizontal oscillation to create continuous movement
self.oscillating = false;
}
});
// Set up vertical oscillation with tween - full screen height movement
tween(self, {
y: 100 // Move to the top of the screen
}, {
duration: 3000,
// Slower, smoother movement
easing: tween.sineInOut,
// Smoother easing
onFinish: function onFinish() {
// When reaching the top, go all the way down
tween(self, {
y: 2732 - 100 // Move to the bottom of the screen
}, {
duration: 3000,
// Matching duration for smooth cycle
easing: tween.sineInOut,
onFinish: function onFinish() {
// Reset animation when complete
self.oscillating = false;
}
});
}
});
// Setup enhanced pulsing animation with tween - larger size variation
tween(self.scale, {
x: 1.3,
// Increase more (was 1.15)
y: 1.3 // Increase more (was 1.15)
}, {
duration: 900,
easing: tween.sineInOut,
onFinish: function onFinish() {
tween(self.scale, {
x: 0.8,
// Decrease more (was 0.85)
y: 0.8 // Decrease more (was 0.85)
}, {
duration: 900,
easing: tween.sineInOut,
onFinish: function onFinish() {
// Scale animation will reset when complete
if (self.parent) {
self.scale.x = 1;
self.scale.y = 1;
}
}
});
}
});
}
}
if (self.x < -100) {
// Cancel any active tweens when destroying
tween.stop(self);
tween.stop(self.scale);
self.destroy();
}
};
return self;
});
var UpgradeMenu = Container.expand(function () {
var self = Container.call(this);
// Background panel
var panel = self.attachAsset('gauge_background', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 4
});
panel.alpha = 0.9;
// Title
var titleText = new Text2('UPGRADES', {
size: 120,
fill: 0xE75A10,
//{5b} // NES Mario brick-red color
font: "'Press Start 2P', monospace"
});
titleText.anchor.set(0.5, 0.5);
titleText.x = 0;
titleText.y = -350;
self.addChild(titleText);
// Coin display
var coinDisplay = new Text2('Coins: ' + (storage.coins || 0), {
size: 80,
fill: 0xFCA817,
//{5h} // NES Mario gold coin color
font: "'Press Start 2P', monospace"
});
coinDisplay.anchor.set(0.5, 0.5);
coinDisplay.x = 0;
coinDisplay.y = -220;
self.addChild(coinDisplay);
// Double Jump upgrade button
var doubleJumpButton = new Text2('Double Jump: ' + (storage.doubleJumpPurchased ? 'PURCHASED' : '5 COINS'), {
size: 70,
fill: storage.doubleJumpPurchased ? 0x00FF00 : 0xFFFFFF,
font: "'Press Start 2P', monospace"
});
doubleJumpButton.anchor.set(0.5, 0.5);
doubleJumpButton.x = 0;
doubleJumpButton.y = -50;
self.addChild(doubleJumpButton);
// Dash upgrade button
var dashButton = new Text2('Dash: ' + (storage.dashPurchased ? 'PURCHASED' : '8 COINS'), {
size: 70,
fill: storage.dashPurchased ? 0x00FF00 : 0xFFFFFF,
font: "'Press Start 2P', monospace"
});
dashButton.anchor.set(0.5, 0.5);
dashButton.x = 0;
dashButton.y = 80;
self.addChild(dashButton);
// Close button
var closeButton = new Text2('BACK TO MENU', {
size: 80,
fill: 0xE75A10,
//{5t} // NES Mario brick-red color
font: "'Press Start 2P', monospace"
});
closeButton.anchor.set(0.5, 0.5);
closeButton.x = 0;
closeButton.y = 300;
self.addChild(closeButton);
// Update coin display
self.updateCoinDisplay = function () {
coinDisplay.setText('Coins: ' + (storage.coins || 0));
doubleJumpButton.setText('Double Jump: ' + (storage.doubleJumpPurchased ? 'PURCHASED' : '5 COINS'), {
size: 70,
fill: storage.doubleJumpPurchased ? 0x20D060 : 0xFCE29F,
//{5x} // NES green for purchased, light cream for not
font: "'Press Start 2P', monospace"
});
dashButton.setText('Dash: ' + (storage.dashPurchased ? 'PURCHASED' : '8 COINS'), {
size: 70,
fill: storage.dashPurchased ? 0x20D060 : 0xFCE29F,
//{5B} // NES green for purchased, light cream for not
font: "'Press Start 2P', monospace"
});
};
// Handle purchase
self.purchaseDoubleJump = function () {
if (!storage.doubleJumpPurchased && (storage.coins || 0) >= 5) {
storage.coins -= 5;
storage.doubleJumpPurchased = true;
self.updateCoinDisplay();
}
};
// Handle dash purchase
self.purchaseDash = function () {
if (!storage.dashPurchased && (storage.coins || 0) >= 8) {
storage.coins -= 8;
storage.dashPurchased = true;
self.updateCoinDisplay();
}
};
// Handle button presses
self.down = function (x, y, obj) {
// Direct comparison without conversion as x,y are already in local coordinates
// Check if double jump button was pressed
if (Math.abs(x - doubleJumpButton.x) < 400 && Math.abs(y - doubleJumpButton.y) < 50) {
self.purchaseDoubleJump();
}
// Check if dash button was pressed
if (Math.abs(x - dashButton.x) < 400 && Math.abs(y - dashButton.y) < 50) {
self.purchaseDash();
}
// Check if close button was pressed
if (Math.abs(x - closeButton.x) < 300 && Math.abs(y - closeButton.y) < 50) {
if (self.onClose) {
self.onClose();
}
}
};
return self;
});
/****
* Initialize Game
****/
// Helper function to adjust music playback rate as LK.setMusicPlaybackRate is not available
/****
*-Seperator-
****/
var game = new LK.Game({
backgroundColor: 0x5C94FC // NES Mario sky blue background color
});
/****
* Game Code
****/
// Helper function to adjust music playback rate as LK.setMusicPlaybackRate is not available
// Game state variables
function setMusicPlaybackRate(rate) {
// Check if music is playing before trying to adjust it
// Use the sound API instead of the non-existent LK.getMusic function
var music = LK.getSound('theme');
if (music) {
// Set the playback rate if available on the audio element
if (music.playbackRate !== undefined) {
music.playbackRate = rate;
}
}
}
var gameStarted = false;
var showingUpgradeMenu = false;
var mainMenu;
var upgradeMenu;
var background;
var background2;
var player;
var enemies = [];
var enemySpawnInterval = 80;
var enemySpawnCounter = 0;
var coinObjects = []; // Renamed from 'coins' to avoid conflict with coin counter
var coinSpawnInterval = Math.max(40, 150 - Math.floor(LK.ticks / 300) * 2); // Decrease interval faster, minimum 40
var coinSpawnCounter = 0;
var stars = []; // Array to store star power-ups
var starSpawnInterval = 500; // Spawn stars less frequently than coins
var starSpawnCounter = 0;
var starPowerActive = false; // Track if star power is active
var starPowerDuration = 10 * 60; // 10 seconds at 60fps
var starPowerTimer = 0; // Timer for star power
// Combo system variables
var comboCount = 0;
var comboActive = false;
var comboDisplayObj = null;
// Score system
var score = 0;
var coins = storage.coins || 0; // Get persistent coin count
var scoreText = new Text2('Score: 0', {
size: 60,
fill: 0xFCE29F,
//{67} // NES light tan/cream color
font: "'Press Start 2P', monospace"
});
// Add a speed indicator to show the increasing difficulty
var speedText = new Text2('Speed: 1x', {
size: 40,
fill: 0xFCE29F,
//{6a} // NES light tan/cream color
font: "'Press Start 2P', monospace"
});
speedText.anchor.set(1, 0);
speedText.x = -50;
speedText.y = 180; // Position below coin count
LK.gui.topRight.addChild(speedText);
speedText.alpha = 0; // Hide initially
scoreText.anchor.set(1, 0);
scoreText.x = -50;
scoreText.y = 50;
LK.gui.topRight.addChild(scoreText);
// Coin count display
var coinText = new Text2('Coins: ' + coins, {
size: 50,
fill: 0xFCA817,
//{6d} // NES Mario gold coin color
// Gold color for coins
font: "'Press Start 2P', monospace"
});
coinText.anchor.set(1, 0);
coinText.x = -50;
coinText.y = 120; // Position below score
LK.gui.topRight.addChild(coinText);
// Combo indicator for HUD
var comboText = new Text2('Combo: 0', {
size: 50,
fill: 0xFFD700,
// Gold color for combo text
font: "'Press Start 2P', monospace"
});
comboText.anchor.set(1, 0);
comboText.x = -50;
comboText.y = 240; // Position below coin count
LK.gui.topRight.addChild(comboText);
comboText.alpha = 0; // Hide combo count initially
scoreText.alpha = 0; // Hide score initially
coinText.alpha = 0; // Hide coin count initially
// Initialize upgrades in storage if needed
if (storage.doubleJumpPurchased === undefined) {
storage.doubleJumpPurchased = false;
}
if (storage.dashPurchased === undefined) {
storage.dashPurchased = false;
}
// Show main menu first
mainMenu = game.addChild(new MainMenu());
mainMenu.onUpgradeClick = function () {
showingUpgradeMenu = true;
mainMenu.visible = false;
// Create upgrade menu
upgradeMenu = game.addChild(new UpgradeMenu());
upgradeMenu.x = 2048 / 2;
upgradeMenu.y = 2732 / 2;
upgradeMenu.updateCoinDisplay();
upgradeMenu.onClose = function () {
showingUpgradeMenu = false;
upgradeMenu.destroy();
upgradeMenu = null;
mainMenu.visible = true;
// Update coin display on main menu
coinText.setText('Coins: ' + (storage.coins || 0));
};
};
mainMenu.onPlayClick = function () {
// Start the game
gameStarted = true;
mainMenu.destroy();
mainMenu = null;
// Initialize game elements - handled in game.down
};
game.update = function () {
// Handle menu states
if (!gameStarted) {
if (showingUpgradeMenu) {
// When in upgrade menu, no need to update main menu
return;
}
// When in menu mode, just update the menu
if (mainMenu) {
mainMenu.update();
}
return;
}
// Game hasn't been initialized yet - start it
if (!background) {
// Initialize game elements
LK.playMusic('theme');
// Create background
background = game.addChild(new Background());
background.x = 0;
background.y = 0;
background2 = game.addChild(new Background2());
background2.x = 2048;
background2.y = 0;
// Store a reference to the background in the game object for access from other objects
game.background = background;
// Create player
player = game.addChild(new Player());
player.x = 2048 / 4;
player.y = player.groundY - 30;
// Show score, coin count and speed
scoreText.alpha = 1;
coinText.alpha = 1;
speedText.alpha = 1;
}
// Game play update logic
background.update();
background2.update();
player.update();
enemySpawnCounter++;
coinSpawnCounter++;
starSpawnCounter++;
// Handle star power-up timer
if (starPowerActive) {
starPowerTimer--;
if (starPowerTimer <= 0) {
starPowerActive = false;
player.isInvincible = false;
// Return background tint to normal
background.sprites[0].tint = 0xFFFFFF;
background2.sprites[0].tint = 0xFFFFFF;
// Switch back to regular theme music
LK.stopMusic();
LK.playMusic('theme');
}
}
if (coinSpawnCounter >= coinSpawnInterval) {
var coin = new Coin();
coin.x = 2048 + Math.random() * 200;
// Set a reasonable height range for coins (not too low, not too high)
coin.y = Math.max(300, Math.min(2732 - 600, Math.random() * (2732 - 800)));
coinObjects.push(coin);
game.addChild(coin);
coinSpawnCounter = 0;
}
// Spawn star power-ups occasionally, but only if star power is not active
if (starSpawnCounter >= starSpawnInterval && !starPowerActive) {
var star = new Star();
star.x = 2048 + Math.random() * 200;
// Set a reasonable height range for stars (not too low, not too high)
star.y = Math.max(300, Math.min(2732 - 600, Math.random() * (2732 - 800)));
stars.push(star);
game.addChild(star);
starSpawnCounter = 0;
starSpawnInterval = Math.floor(Math.random() * 300) + 300; // Random interval between 300-600 ticks
}
if (enemySpawnCounter >= enemySpawnInterval) {
var enemyType = Math.random() < 0.33 ? 'Enemy' : Math.random() < 0.5 ? 'Goomba' : 'FlyKoopa';
var enemy;
if (enemyType === 'Enemy') {
enemy = new Enemy();
} else if (enemyType === 'Goomba') {
enemy = new Goomba();
} else {
enemy = new FlyKoopa();
// Ensure FlyKoopas spawn in the middle to upper part of the screen, not too low
enemy.y = Math.max(300, Math.min(2732 - 600, Math.random() * (2732 - 800)));
}
enemy.x = 2048 + Math.random() * 200;
if (enemyType === 'FlyKoopa') {
// Height for FlyKoopa is already set earlier, no need to set it again
} else {
enemy.y = enemy.groundY; // Use groundY for initial position
}
enemies.push(enemy);
game.addChild(enemy);
enemySpawnInterval = Math.max(15, Math.floor(Math.random() * 100) + 60 - Math.floor(LK.ticks / 300) * 2); // Decrease interval faster, minimum 15
enemySpawnCounter = 0;
}
for (var j = enemies.length - 1; j >= 0; j--) {
enemies[j].update();
if (player.intersects(enemies[j])) {
if (starPowerActive && enemies[j].canHarmPlayer !== false) {
// Star power is active - instantly defeat enemy with special effect
enemies[j].velocityY = -15; // Stronger pop up effect for star power
enemies[j].isJumping = true;
enemies[j].canHarmPlayer = false;
LK.getSound('stomp').play();
// Create a sparkle effect
LK.effects.flashObject(enemies[j], 0xFFFF00, 300);
// Handle combo tracking - activate combo mode if not already active
if (!comboActive) {
comboActive = true;
comboCount = 0;
}
// Increment combo count
comboCount++;
// Calculate score with combo bonus
var starEnemyPoints = 100 * comboCount;
score += starEnemyPoints;
// Display flash text for combo count if more than 1
if (comboCount > 1) {
if (!comboDisplayObj) {
comboDisplayObj = game.addChild(new ComboDisplay());
comboDisplayObj.x = enemies[j].x;
comboDisplayObj.y = enemies[j].y - 50;
comboDisplayObj.showCombo(comboCount, starEnemyPoints);
}
}
scoreText.setText('Score: ' + score);
LK.setScore(score);
} else if (!player.isDead && player.isJumping && player.velocityY > 0 && player.y < enemies[j].y) {
// Player is falling and lands on enemy
enemies[j].velocityY = -10; // Pop up effect for enemy
player.velocityY = -15; // Bounce Mario upwards
LK.getSound('stomp').play(); // Play stomp sound
// Handle combo tracking - activate combo mode if not already active
if (!comboActive) {
comboActive = true;
comboCount = 0;
}
// Increment combo count
comboCount++;
// Calculate score with combo bonus
var enemyPoints = 50;
if (comboCount > 1) {
// Apply combo multiplier - each enemy in combo is worth more
enemyPoints = 50 * comboCount;
}
score += enemyPoints;
// Display flash text for combo count if more than 1
if (comboCount > 1) {
if (!comboDisplayObj) {
comboDisplayObj = game.addChild(new ComboDisplay());
comboDisplayObj.x = enemies[j].x;
comboDisplayObj.y = enemies[j].y - 50;
comboDisplayObj.showCombo(comboCount, enemyPoints);
}
}
scoreText.setText('Score: ' + score);
LK.setScore(score);
enemies[j].isJumping = true;
enemies[j].canHarmPlayer = false; // Enemy can no longer harm player
} else if (enemies[j].canHarmPlayer !== false && !player.isInvincible) {
if (!player.isDead && enemies[j].canHarmPlayer !== false) {
LK.getSound('dead').play(); // Play dead sound
player.isDead = true;
game.playerDead = true;
player.velocityY = -10; // Pop up effect similar to enemy
player.isJumping = true;
LK.setTimeout(function () {
// Save final score before game over
LK.setScore(score);
LK.showGameOver();
}, 3000); // Delay game over by 3 seconds
}
}
} else if (player.x > enemies[j].x && !enemies[j].passed) {
enemies[j].passed = true;
}
if (enemies[j].x < -100) {
enemies.splice(j, 1);
}
}
for (var k = coinObjects.length - 1; k >= 0; k--) {
coinObjects[k].update();
if (player.intersects(coinObjects[k])) {
if (!coinObjects[k].collected) {
coinObjects[k].collected = true;
LK.getSound('Coin').play(); // Play coin sound
// Increase score when collecting coins
score += 10;
// Separate coin counter
coins++;
// Update storage for persistence
storage.coins = coins;
// Update displays
scoreText.setText('Score: ' + score);
coinText.setText('Coins: ' + coins);
LK.setScore(score);
// Create a coin pickup effect
// Visual effects for coin collection - similar to star power but smaller
LK.effects.flashObject(player, 0xFCA817, 300); // Gold flash
// Create a coin burst effect
for (var i = 0; i < 8; i++) {
var angle = i / 8 * Math.PI * 2;
var distance = 60;
var particleX = coinObjects[k].x + Math.cos(angle) * distance;
var particleY = coinObjects[k].y + Math.sin(angle) * distance;
var particle = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.4,
scaleY: 0.4,
x: coinObjects[k].x,
y: coinObjects[k].y,
alpha: 0.8
});
particle.tint = 0xFCA817; // Gold tint
game.addChild(particle);
// Animate particles outward
tween(particle, {
x: particleX,
y: particleY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 800,
easing: tween.easeOut
});
// Remove particles after animation
LK.setTimeout(function (p) {
return function () {
if (p && p.parent) {
p.parent.removeChild(p);
}
};
}(particle), 800);
}
coinObjects[k].destroy();
coinObjects.splice(k, 1);
}
}
}
// Update and check star power-ups
for (var s = stars.length - 1; s >= 0; s--) {
stars[s].update();
if (player.intersects(stars[s])) {
if (!stars[s].collected) {
stars[s].collected = true;
// Switch music to star power theme
LK.stopMusic();
LK.playMusic('starman2');
// Activate star power
starPowerActive = true;
starPowerTimer = starPowerDuration;
player.isInvincible = true;
// Visual effects for star power
LK.effects.flashObject(player, 0xFFFF00, 500); // Yellow flash
// Create a star burst effect
for (var i = 0; i < 12; i++) {
var angle = i / 12 * Math.PI * 2;
var distance = 100;
var particleX = player.x + Math.cos(angle) * distance;
var particleY = player.y + Math.sin(angle) * distance;
var particle = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5,
x: player.x,
y: player.y,
alpha: 0.8
});
particle.tint = 0xFFFF00; // Yellow tint
game.addChild(particle);
// Animate particles outward
tween(particle, {
x: particleX,
y: particleY,
alpha: 0,
scaleX: 0.1,
scaleY: 0.1
}, {
duration: 1000,
easing: tween.easeOut
});
// Remove particles after animation
LK.setTimeout(function (p) {
return function () {
if (p && p.parent) {
p.parent.removeChild(p);
}
};
}(particle), 1000);
}
// Change background tint for star power effect
if (background.sprites && background.sprites.length > 0) {
background.sprites[0].tint = 0xFFFFAA; // Slight yellow tint
}
if (background2.sprites && background2.sprites.length > 0) {
background2.sprites[0].tint = 0xFFFFAA; // Slight yellow tint
}
// Add extra points for star
score += 100;
scoreText.setText('Score: ' + score);
LK.setScore(score);
// Remove the star
stars[s].destroy();
stars.splice(s, 1);
}
} else if (stars[s].x < -100) {
stars[s].destroy();
stars.splice(s, 1);
}
}
// Update speed display and music speed
if (background && !game.playerDead) {
var speedMultiplier = (background.speed / 3).toFixed(1);
// Show star power status in speed text if active
if (starPowerActive) {
var starTimeLeft = Math.ceil(starPowerTimer / 60);
speedText.setText('STAR POWER: ' + starTimeLeft + 's', {
size: 40,
fill: 0xFFFF00,
// Yellow for star power
font: "'Press Start 2P', monospace"
});
} else {
speedText.setText('Speed: ' + speedMultiplier + 'x', {
size: 40,
fill: 0xFCE29F,
// Return to normal color
font: "'Press Start 2P', monospace"
});
}
// Update combo text in HUD
comboText.setText('Combo: ' + comboCount);
comboText.alpha = gameStarted ? 1 : 0;
// Adjust music playback rate based on game speed
var musicPlaybackRate = Math.min(1.5, 0.8 + background.speed / 3 * 0.2);
// Increase music speed during star power
if (starPowerActive) {
musicPlaybackRate = Math.min(2.0, musicPlaybackRate * 1.2);
}
setMusicPlaybackRate(musicPlaybackRate);
}
};
game.down = function (x, y, obj) {
if (showingUpgradeMenu) {
// Let the upgrade menu handle this event
return;
}
if (!gameStarted) {
// Let the main menu handle this event
return;
} else {
// Pass event to player to track swipes
player.down(x, y, obj);
// In-game tap makes player jump
player.jump();
}
};
game.up = function (x, y, obj) {
if (showingUpgradeMenu || !gameStarted) {
return;
}
// Pass touch up event to player for swipe detection
if (player) {
player.up(x, y, obj);
}
};
game.move = function (x, y, obj) {
if (showingUpgradeMenu || !gameStarted) {
return;
}
// Pass touch move event to player for swipe detection
if (player) {
player.move(x, y, obj);
}
};