/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Define a class for enemies var Enemy = Container.expand(function (enemyType) { var self = Container.call(this); var enemyGraphics = self.attachAsset(enemyType, { anchorX: 0.5, anchorY: 0.5 }); self.speed = 5; self.enemyType = enemyType; // Apply spinning animation to 'ls' enemy type if (enemyType === 'ls') { // Start spinning animation in left (counter-clockwise) direction tween(enemyGraphics, { rotation: Math.PI * 2 * -1 // Negative for left/counter-clockwise direction }, { duration: 2000, // 2 seconds for a full rotation easing: tween.linear, repeat: Infinity, // Use Infinity instead of loop: true for continuous spinning loop: true // Continuous spinning }); } self.update = function () { // Special behavior for ls asset - bounce down from top right if (self.enemyType === 'ls') { // Add vertical movement properties first time if (self.verticalSpeed === undefined) { self.verticalSpeed = 3; // Initial downward speed self.gravity = 0.1; // Gravity effect self.bounceStrength = 2; // How strong the bounce is // Start from top-right corner self.y = 100; // Near top self.x = 2048 + 100; // Off right edge } // Track if this asset is in the spinning out state if (self.spinningOut) { // Move toward right top corner var targetX = 2048 + 200; var targetY = -200; // Calculate direction vector var dirX = targetX - self.x; var dirY = targetY - self.y; // Normalize and apply speed var length = Math.sqrt(dirX * dirX + dirY * dirY); if (length > 0) { dirX = dirX / length * (self.spinSpeed || 15); dirY = dirY / length * (self.spinSpeed || 15); } // Move toward target self.x += dirX; self.y += dirY; // Increase rotation speed self.children[0].rotation += 0.2; } else { // Normal behavior when not spinning out // Apply gravity to vertical speed self.verticalSpeed += self.gravity; self.y += self.verticalSpeed; // Bounce when hitting bottom at 3/4 of the screen height instead of center if (self.y > 2732 * 0.75) { // Bounce at 3/4 screen height self.y = 2732 * 0.75; self.verticalSpeed = -self.verticalSpeed * (self.bounceStrength * 0.3); // Even lower bounce power // Reduce bounce strength much more quickly self.bounceStrength *= 0.5; } // Horizontal movement self.x -= self.speed; } } else if (self.enemyType === 'enemy3') { // Special behavior for enemy3 - attack from right bottom corner if (self.initialPosition === undefined) { // Set initial position at right bottom corner self.x = 2048 + 100; // Off right edge self.y = 2732 - 550; // Bottom of screen with small margin, moved up by 450 units self.initialPosition = true; } // Move diagonally up-left (towards Mario) self.x -= self.speed * 1.2; self.y -= self.speed * 0.6; } else { // Original behavior for other enemy types self.x -= self.speed; } // Destroy when off screen if (self.x < -50) { self.destroy(); } }; return self; // Return self to make the class inheritable }); // Define a class for displaying the God Mode text var GodModeText = Container.expand(function () { var self = Container.call(this); // Create the text var godModeText = new Text2('GOD MODE', { size: 80, fill: 0xFF8C00 // Orange color }); godModeText.anchor.set(0.5, 0.5); self.addChild(godModeText); // Initialize with invisible state godModeText.alpha = 0; // Create timer text var timerText = new Text2('', { size: 80, fill: 0xFF8C00 // Orange color for timer }); timerText.anchor.set(0.5, 0.5); timerText.y = 80; // Position below the GOD MODE text self.addChild(timerText); // Initialize timer value self.timerValue = 5; // 5 seconds duration self.timerInterval = null; // Method to show the animation self.showAnimation = function () { // Play god mode sound LK.getSound('godmode').play(); // Stop any existing animations tween.stop(godModeText); // Reset and start the timer self.timerValue = 5; timerText.setText(self.timerValue); timerText.alpha = 1; // Clear any existing timer if (self.timerInterval) { LK.clearInterval(self.timerInterval); } // Start countdown timer self.timerInterval = LK.setInterval(function () { self.timerValue -= 1; if (self.timerValue > 0) { timerText.setText(self.timerValue); } else if (self.timerValue === 0) { timerText.setText(self.timerValue); LK.clearInterval(self.timerInterval); self.timerInterval = null; } }, 1000); // Animate in with fireflame effect godModeText.tint = 0xFF5500; // Orange-red fire color tween(godModeText, { alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 300, easing: tween.elasticOut, onFinish: function onFinish() { // Create flickering flame animation function fireflameFlicker() { // Random fire colors between orange-red and yellow var fireColors = [0xFF5500, 0xFF8800, 0xFFAA00, 0xFFFF00]; var randomColor = fireColors[Math.floor(Math.random() * fireColors.length)]; // Random scale for fire flicker effect var randomScale = 1.0 + Math.random() * 0.3; // Apply the flame effect tween(godModeText, { tint: randomColor, scaleX: randomScale, scaleY: randomScale, alpha: 0.7 + Math.random() * 0.3 }, { duration: 100 + Math.random() * 150, easing: tween.easeOut, onFinish: function onFinish() { // Continue the flame effect if still visible if (godModeText.alpha > 0) { fireflameFlicker(); } } }); } // Start the flame flicker effect fireflameFlicker(); // Hide after 3 seconds LK.setTimeout(function () { // Stop the pulse animation tween.stop(godModeText); // Stop and hide the timer if (self.timerInterval) { LK.clearInterval(self.timerInterval); self.timerInterval = null; } tween(timerText, { alpha: 0 }, { duration: 300, easing: tween.easeIn }); // Animate out tween(godModeText, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 500, easing: tween.easeIn }); }, 3000); } }); }; return self; }); // Define a class for the life counter var LifeCounter = Container.expand(function () { var self = Container.call(this); // Create a simple white line instead of using an asset var lineGraphics = LK.getAsset('enemy2', { anchorX: 0.0, anchorY: 0.0, scaleX: 2048, scaleY: 10, tint: 0xFFFFFF // White color }); self.addChild(lineGraphics); self.width = 2048; // Full width self.update = function () { // Update the width of the line based on Mario's life if (game.Mario) { lineGraphics.scale.x = 2048 * game.Mario.life; } }; }); // Define a class for the dragon ball lightning effect var LightningEffect = Container.expand(function () { var self = Container.call(this); // Set active status flag self.isActive = false; // Create lightning bolts self.bolts = []; var colors = [0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00]; // Create multiple lightning bolts around Mario for (var i = 0; i < 5; i++) { var bolt = self.attachAsset('enemy2', { anchorX: 0.5, anchorY: 0.5, scaleX: 25 + Math.random() * 10, scaleY: 25 + Math.random() * 10 }); bolt.tint = colors[Math.floor(Math.random() * colors.length)]; bolt.alpha = 0; // Start with invisible bolts bolt.rotation = Math.random() * Math.PI * 2; bolt.distance = 80 + Math.random() * 40; bolt.speed = 0.1 + Math.random() * 0.1; bolt.originalScale = { x: bolt.scale.x, y: bolt.scale.y }; self.bolts.push(bolt); } // Animate the lightning self.animate = function () { // Stop any existing animations for (var i = 0; i < self.bolts.length; i++) { tween.stop(self.bolts[i]); } // Start new animations for (var i = 0; i < self.bolts.length; i++) { var bolt = self.bolts[i]; // Randomize the bolt appearance bolt.rotation = Math.random() * Math.PI * 2; bolt.scale.x = bolt.originalScale.x * (0.8 + Math.random() * 0.4); bolt.scale.y = bolt.originalScale.y * (0.8 + Math.random() * 0.4); // Animate the bolt with pulse effect tween(bolt, { alpha: 0.2 + Math.random() * 0.5, scaleX: bolt.originalScale.x * (0.7 + Math.random() * 0.6), scaleY: bolt.originalScale.y * (0.7 + Math.random() * 0.6) }, { duration: 300 + Math.random() * 400, easing: tween.easeOut, onFinish: function () { // Create a callback closure to maintain the correct bolt reference var currentBolt = bolt; return function () { // Animate back tween(currentBolt, { alpha: 0.6 + Math.random() * 0.4, scaleX: currentBolt.originalScale.x * (0.9 + Math.random() * 0.3), scaleY: currentBolt.originalScale.y * (0.9 + Math.random() * 0.3) }, { duration: 300 + Math.random() * 400, easing: tween.easeIn }); }; }() }); } }; self.update = function () { // Position the lightning bolts around Mario for (var i = 0; i < self.bolts.length; i++) { var bolt = self.bolts[i]; var angle = bolt.rotation + self.parent.rotation + bolt.speed * LK.ticks; bolt.x = Math.cos(angle) * bolt.distance; bolt.y = Math.sin(angle) * bolt.distance; // Make bolts invisible when effect is not active if (!self.isActive) { bolt.alpha = 0; } // Ensure bolts remain visible when effect is active else if (self.isActive && bolt.alpha < 0.7) { bolt.alpha = 0.7 + Math.random() * 0.3; } } // Periodically refresh animations only when not in active state if (LK.ticks % 30 === 0 && self.isActive) { self.animate(); } }; return self; }); //<Assets used in the game will automatically appear here> // Define a class for the player character var Player = Container.expand(function (characterType) { var self = Container.call(this); var assetType = characterType === 'YM' ? 'YM' : characterType === 'MB' ? 'MB' : 'Mario'; var MarioGraphics = self.attachAsset(assetType, { anchorX: 0.5, anchorY: 0.5 }); self.speed = 5; // Add the lightning effect self.lightning = self.addChild(new LightningEffect()); self.jumpHeight = 40; self.isJumping = false; self.velocityY = 0; self.update = function () { if (self.isJumping) { self.y += self.velocityY; self.velocityY += 0.7; // Decreased gravity effect by 30% if (self.y >= 2732 / 2) { // Ground level self.y = 2732 / 2; self.isJumping = false; self.velocityY = 0; } } // Update the lightning effect if (self.lightning) { self.lightning.update(); } }; self.jump = function () { if (!self.isJumping) { self.isJumping = true; self.velocityY = -self.jumpHeight; // Play jump sound LK.getSound('jump').play(); // Activate lightning effect animation with a more intense glow when jumping, but only if lightning is already active if (self.lightning && self.lightning.isActive) { // Intensify the lightning effect for (var i = 0; i < self.lightning.bolts.length; i++) { var bolt = self.lightning.bolts[i]; tween(bolt, { alpha: 0.9, scaleX: bolt.originalScale.x * 1.5, scaleY: bolt.originalScale.y * 1.5 }, { duration: 300, easing: tween.elasticOut }); } // Reset to normal after a short time LK.setTimeout(function () { self.lightning.animate(); }, 500); } } }; }); // Define a class for power-ups var PowerUp = Container.expand(function () { var self = Container.call(this); var powerGraphics = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 3; self.floatOffset = 0; self.collected = false; // Make power-up glow with tween animation and add shiny flashing effect tween(powerGraphics, { alpha: 0.7, tint: 0xffffff }, { duration: 1000, easing: tween.sinusoidalInOut, loop: true, yoyo: true }); // Add shiny flashing effect function createShinyEffect() { // Tween to bright yellow-white (shiny effect) tween(powerGraphics, { tint: 0xffffff, alpha: 1, scaleX: 1.2, scaleY: 1.2 }, { duration: 150, easing: tween.easeOut, onFinish: function onFinish() { // Tween back to normal gold tween(powerGraphics, { tint: 0xffd700, alpha: 0.8, scaleX: 1.0, scaleY: 1.0 }, { duration: 250, easing: tween.easeIn, onFinish: function onFinish() { // Schedule next flash after a shorter random delay LK.setTimeout(createShinyEffect, 300 + Math.random() * 400); } }); } }); } // Start the shiny effect sequence LK.setTimeout(createShinyEffect, 200 + Math.random() * 300); self.update = function () { // Move power-up from right to left self.x -= self.speed; // Floating up and down motion self.floatOffset += 0.05; self.y = self.originalY + Math.sin(self.floatOffset) * 20; // Remove if off screen if (self.x < -100) { self.destroy(); } }; return self; }); // Define a class for displaying the SHARKNADO title var SharknadoTitle = Container.expand(function () { var self = Container.call(this); // Create the text with chrome-silver-black styling var sharknadoText = new Text2('SHARKNADO!', { size: 240, fill: 0xDDDDDD, // Silver base color dropShadow: true, dropShadowColor: 0x333333, // Dark shadow for 3D effect dropShadowDistance: 3 }); // Set anchor to center horizontally sharknadoText.anchor.set(0.5, 0.5); self.addChild(sharknadoText); // Initialize as invisible self.visible = false; // Method to show the title self.show = function () { if (self.visible) return; // Already visible self.visible = true; // Clear any existing animations tween.stop(sharknadoText); // Reset and animate in sharknadoText.alpha = 0; sharknadoText.scale.set(0.5, 0.5); // Track original position for shake effect self.originalX = self.x; self.originalY = self.y; // Animate in with chrome shine effect tween(sharknadoText, { alpha: 1, scaleX: 1.1, scaleY: 1.1 }, { duration: 500, easing: tween.elasticOut, onFinish: function onFinish() { // Add pulsing animation tween(sharknadoText, { scaleX: 1.0, scaleY: 1.0 }, { duration: 800, easing: tween.sinusoidalInOut, yoyo: true, loop: true }); // Start shaking animation self.startShaking(); } }); }; // Method to hide the title self.hide = function () { if (!self.visible) return; // Already hidden // Stop any existing animations tween.stop(sharknadoText); // Stop shaking self.stopShaking(); // Animate out tween(sharknadoText, { alpha: 0, scaleX: 0.8, scaleY: 0.8 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { self.visible = false; } }); }; // Start shaking animation for SHARKNADO title self.startShaking = function () { // Clear any existing shake interval self.stopShaking(); // Create new shake interval self.shakeInterval = LK.setInterval(function () { // Random offset for shake effect (more horizontal than vertical) var offsetX = (Math.random() - 0.5) * 20; // -10 to +10 pixels var offsetY = (Math.random() - 0.5) * 10; // -5 to +5 pixels // Apply shake self.x = self.originalX + offsetX; self.y = self.originalY + offsetY; // Apply slight rotation for more dramatic effect sharknadoText.rotation = (Math.random() - 0.5) * 0.05; // Small random rotation }, 50); // Shake every 50ms for realistic earthquake effect }; // Stop shaking animation self.stopShaking = function () { if (self.shakeInterval) { LK.clearInterval(self.shakeInterval); self.shakeInterval = null; // Reset position and rotation if (self.originalX !== undefined) { self.x = self.originalX; self.y = self.originalY; sharknadoText.rotation = 0; } } }; return self; }); // Define a class for the TB popup var TbPopup = Container.expand(function () { var self = Container.call(this); // Get TB asset var tbGraphics = self.attachAsset('tb', { anchorX: 0.5, anchorY: 0.5 }); // Initialize as invisible self.alpha = 0; self.visible = false; // Method to show the popup self.show = function () { if (self.visible) return; // Already visible // Stop any existing animations tween.stop(self); // Reset and make visible self.visible = true; self.scale.set(0.2, 0.2); // Animate in tween(self, { alpha: 1, scaleX: 1.0, scaleY: 1.0 }, { duration: 500, easing: tween.elasticOut }); }; // Method to hide the popup self.hide = function () { if (!self.visible) return; // Already hidden // Animate out tween(self, { alpha: 0, scaleX: 0.2, scaleY: 0.2 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { self.visible = false; } }); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x87CEEB // Sky blue background }); /**** * Game Code ****/ // All references to 'Mri' asset have been renamed to 'MB' //<Assets used in the game will automatically appear here> game.lastSpawnTime = 0; var background = game.addChild(LK.getAsset('background', { anchorX: 0, anchorY: 0 })); background.x = 0; background.y = 0; // Play background music LK.playMusic('music1', { loop: true }); // Character selection menu implementation var characterSelectionContainer = new Container(); characterSelectionContainer.x = 2048 / 2; characterSelectionContainer.y = 2732 / 2; game.addChild(characterSelectionContainer); // Dim background overlay var overlay = LK.getAsset('enemy2', { anchorX: 0.5, anchorY: 0.5, scaleX: 2048, scaleY: 2732, tint: 0x000000, alpha: 0.7 }); characterSelectionContainer.addChild(overlay); // Title text var selectText = new Text2('Select Your Character', { size: 120, fill: 0xFFFFFF }); selectText.anchor.set(0.5, 0.5); selectText.y = -400; characterSelectionContainer.addChild(selectText); // (Mario option removed) // MB option var mbOption = LK.getAsset('MB', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); mbOption.x = 0; mbOption.y = 0; characterSelectionContainer.addChild(mbOption); var mbLabel = new Text2('MB', { size: 70, fill: 0xFFFFFF }); mbLabel.anchor.set(0.5, 0); mbLabel.x = mbOption.x; mbLabel.y = 280; characterSelectionContainer.addChild(mbLabel); // YM option var ymOption = LK.getAsset('YM', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.2, scaleY: 1.2 }); ymOption.x = 500; ymOption.y = 0; characterSelectionContainer.addChild(ymOption); var ymLabel = new Text2('YM', { size: 70, fill: 0xFFFFFF }); ymLabel.anchor.set(0.5, 0); ymLabel.x = ymOption.x; ymLabel.y = 280; // moved down by 100 units characterSelectionContainer.addChild(ymLabel); // Store selected character type var selectedCharacterType = null; // Helper to highlight selection function highlightOption(option) { mbOption.alpha = 0.7; ymOption.alpha = 0.7; option.alpha = 1.0; } // (Mario option event handler removed) // Touch/click handlers for MB mbOption.down = function (x, y, obj) { highlightOption(mbOption); selectedCharacterType = 'MB'; confirmButton.alpha = 1; confirmButton.interactive = true; }; // Touch/click handlers for YM ymOption.down = function (x, y, obj) { highlightOption(ymOption); selectedCharacterType = 'YM'; confirmButton.alpha = 1; confirmButton.interactive = true; }; // Confirm button var confirmButton = new Text2('Start Game', { size: 90, fill: 0x00FF00 }); confirmButton.anchor.set(0.5, 0.5); confirmButton.y = 400; confirmButton.alpha = 0.5; confirmButton.interactive = false; characterSelectionContainer.addChild(confirmButton); // Confirm button handler confirmButton.down = function (x, y, obj) { if (!selectedCharacterType) return; // Remove menu characterSelectionContainer.destroy(); // Proceed to game setup setupPlayer(selectedCharacterType); }; // Add down event listeners mbOption.interactive = true; ymOption.interactive = true; confirmButton.interactive = false; // Setup player function function setupPlayer(characterType) { // Initialize player with selected character var Mario = game.addChild(new Player(characterType)); Mario.x = 2048 / 2 - 200; Mario.y = 2732 / 2; Mario.life = 1; // Store reference globally for other code game.Mario = Mario; // Update all references in game code to use game.Mario if needed } // Patch Player class to accept characterType // (see Classes section for Player class patch) // Initialize the life counter and add it to the game var lifeCounter = game.addChild(new LifeCounter()); lifeCounter.y = 0; // Position it at the top of the screen // Create TB popup for enemy3 var tbPopup = game.addChild(new TbPopup()); tbPopup.x = 160; // Position on the left side, moved left by 40 units tbPopup.y = 2732 / 2 - 777; // Center vertically, then move up by 777 units // Create life percentage text var lifePercentText = new Text2('100%', { size: 40, fill: 0x7FFF00 // Bright neon green to match the life bar }); // Add the life percentage text to the GUI LK.gui.topRight.addChild(lifePercentText); // Position the text lifePercentText.anchor.set(1, 0); // Right align lifePercentText.x = -20; // Add right margin lifePercentText.y = 20; // Add top margin // Initialize enemies var enemies = []; var enemySpawnInterval = 100; var enemySpawnCounter = 0; // Initialize power-ups var powerups = []; var powerupSpawnInterval = 300; var powerupSpawnCounter = 0; // Create GodModeText instance var godModeText = game.addChild(new GodModeText()); godModeText.x = 2048 / 2; // Center horizontally godModeText.y = 2732 / 2 - 1050; // Position above the center (moved up by 650 units) // Create a new Text2 object to display the score var scoreLabelText = new Text2('Defeated enemies: ', { size: 40, fill: 0xFFFFFF }); var scoreText = new Text2('0', { size: 50, fill: 0xFFFFFF }); // Create the SHARKNADO title var sharknadoTitle = game.addChild(new SharknadoTitle()); sharknadoTitle.x = 2048 / 2; // Center horizontally sharknadoTitle.y = scoreText.y + 200; // Position further below the score text (moved down by 100 units) // Add the score label and text to the game GUI at the top center of the screen LK.gui.top.addChild(scoreLabelText); LK.gui.top.addChild(scoreText); // Position the label and score text scoreLabelText.anchor.set(1, 0); // Right align the label scoreText.anchor.set(0, 0); // Left align the score scoreLabelText.x = 2048 / 2 - 880; // Position to the left of center, moved 880 units left scoreLabelText.y = 20; // Add a small top margin scoreText.x = 2048 / 2 - 840; // Position to the right of center, moved 840 units left scoreText.y = 10; // Add a small top margin // Set up a regular interval to check and ensure enemy spawning var enemyWaveScheduler = LK.setInterval(function () { // If there are no enemies on screen for more than 3 seconds, spawn a new wave if (enemies.length === 0 && LK.ticks - game.lastSpawnTime > 180) { // 3 seconds at 60fps // Reset spawn counter to trigger spawn on next update enemySpawnCounter = enemySpawnInterval; } }, 1000); // Check every second // Handle game updates game.update = function () { if (game.Mario) game.Mario.update(); // Prevent enemies and powerups from spawning until player is selected if (!game.Mario) { return; } // Spawn enemies enemySpawnCounter++; // Define maximum number of active enemies allowed at once var maxEnemies = Math.min(40, LK.getScore() + 5); // Scales with score up to 40 enemies // Track time since last enemy spawn if (!game.lastSpawnTime) { game.lastSpawnTime = LK.ticks; } // Calculate time passed since last spawn (in frames, 60fps means 240 frames = 4 seconds) var framesSinceLastSpawn = LK.ticks - game.lastSpawnTime; // Force spawn if more than 4 seconds have passed without enemies var forceSpawn = framesSinceLastSpawn > 240; if (enemySpawnCounter >= enemySpawnInterval && enemies.length < maxEnemies || forceSpawn && enemies.length === 0) { // Define enemy types var enemyTypes = ['enemy', 'enemy1', 'enemy2', 'enemy3', 'jjb', 'ls']; // Keep track of the last enemy type to avoid repetition if (!game.lastEnemyTypeIndex) { game.lastEnemyTypeIndex = -1; } // Reset last spawn time game.lastSpawnTime = LK.ticks; // Only spawn one enemy at a time var _spawnEnemy = function spawnEnemy() { // Check if we reached the maximum number of enemies if (enemies.length >= maxEnemies) { // Check if we've reached a permanent limit (meaning we can't spawn more enemies) if (maxEnemies >= 40 && LK.getScore() >= 35) { // If all current enemies are defeated, show win message if (enemies.length === 0) { LK.showYouWin(); } } return; // Don't spawn more enemies if we reached the limit } // Check if we're already in process of spawning (avoid duplicate calls) if (game.isSpawningEnemy) { return; // Prevent multiple simultaneous spawns } game.isSpawningEnemy = true; // Get a new enemy type that is different from the last one var availableTypes = []; for (var t = 0; t < enemyTypes.length; t++) { // Skip enemy2 type which is too small to be visible (4x4) if (t !== game.lastEnemyTypeIndex && enemyTypes[t] !== 'enemy2') { availableTypes.push({ index: t, type: enemyTypes[t] }); } } // Randomly select from available types var selectedType = availableTypes[Math.floor(Math.random() * availableTypes.length)]; game.lastEnemyTypeIndex = selectedType.index; var enemy = new Enemy(selectedType.type); enemy.x = 2048 + 100; // Fixed distance from edge of screen enemy.y = 2732 / 2; enemies.push(enemy); game.addChild(enemy); // Schedule the next enemy spawn with a fixed 1 second delay to ensure sequential spawning if (enemies.length < maxEnemies) { game.nextEnemyTimeout = LK.setTimeout(function () { // Force an enemy spawn by setting the spawn counter to threshold enemySpawnCounter = enemySpawnInterval; // Update last spawn time to now game.lastSpawnTime = LK.ticks; // Reset spawning flag to allow next enemy spawn game.isSpawningEnemy = false; }, 1000); // Exactly 1 second delay between spawns } else { // Reset spawning flag if we're not spawning more game.isSpawningEnemy = false; } }; _spawnEnemy(); // Randomize the spawn interval for the next enemy, but ensure it's not too long enemySpawnInterval = Math.floor(Math.random() * 120) + 30; // Reduced max interval enemySpawnCounter = 0; } // Spawn power-ups powerupSpawnCounter++; if (powerupSpawnCounter >= powerupSpawnInterval) { var powerup = new PowerUp(); powerup.x = 2048 + 200; // Start a bit off screen powerup.y = 2732 / 2 - 200; // Above the players level powerup.originalY = powerup.y; // Store original Y for floating animation powerup.lastWasIntersecting = false; // For tracking intersection powerups.push(powerup); game.addChild(powerup); // Randomize the spawn interval for the next power-up powerupSpawnInterval = Math.floor(Math.random() * 400) + 300; powerupSpawnCounter = 0; } // Update enemies for (var j = enemies.length - 1; j >= 0; j--) { enemies[j].update(); if (game.Mario && game.Mario.intersects(enemies[j])) { // Check if enemy is jjb type and play JJBE sound if (enemies[j].enemyType === 'jjb' && !enemies[j].jjbeSoundPlayed && !(game.Mario && game.Mario.lightning && game.Mario.lightning.isActive) && !(game.Mario && game.Mario.isJumping) // Do not play if Mario is jumping on jjb ) { // Play JJBE sound when Mario hits jjb asset and mark it as played // Only play if not in godmode and not jumping LK.getSound('JJBE').play(); enemies[j].jjbeSoundPlayed = true; } else if (enemies[j].enemyType === 'jjb' && game.Mario && game.Mario.lightning && game.Mario.lightning.isActive && !enemies[j].aniSoundPlayed) { // Play Ani sound if godmode is active and Mario touches jjb asset LK.getSound('Ani').play(); enemies[j].aniSoundPlayed = true; } // Special handling for JJB assets during God mode - spin horizontally and disappear if (enemies[j].enemyType === 'jjb' && game.Mario && game.Mario.lightning && game.Mario.lightning.isActive) { // Start the spinning animation if not already spinning if (!enemies[j].spinningOut) { enemies[j].spinningOut = true; // Stop movement enemies[j].speed = 0; // Get the JJB asset graphic var jjbGraphic = enemies[j].children[0]; // First horizontal 360 spin tween(jjbGraphic, { scaleX: -1 // Flip horizontally }, { duration: 150, // 0.15 second for first half spin easing: tween.linear, onFinish: function onFinish() { // Continue to full 360 tween(jjbGraphic, { scaleX: 1 // Back to original }, { duration: 150, // 0.15 second for second half spin easing: tween.linear, onFinish: function onFinish() { // Second 360 spin tween(jjbGraphic, { scaleX: -1 // Flip horizontally again }, { duration: 150, // 0.15 second for third half spin easing: tween.linear, onFinish: function onFinish() { // Complete second 360 tween(jjbGraphic, { scaleX: 1 // Back to original }, { duration: 150, // 0.15 second for second full spin easing: tween.linear, onFinish: function onFinish() { // Third 360 spin tween(jjbGraphic, { scaleX: -1 // Flip horizontally third time }, { duration: 150, // 0.15 second for fifth half spin easing: tween.linear, onFinish: function onFinish() { // Complete third 360 tween(jjbGraphic, { scaleX: 1, // Back to original alpha: 0 // Fade out }, { duration: 150, // 0.15 second for final half spin and fade easing: tween.linear, onFinish: function onFinish() { // Remove the enemy after spinning enemies[j].destroy(); enemies.splice(j, 1); // Add points for defeating enemy LK.setScore(LK.getScore() + 1); scoreText.setText(LK.getScore()); } }); } }); } }); } }); } }); } }); // Mark as being handled to prevent other collision effects return; } } // Special handling for 'ls' or 'enemy3' assets during God mode if ((enemies[j].enemyType === 'ls' || enemies[j].enemyType === 'enemy3') && game.Mario && game.Mario.lightning && game.Mario.lightning.isActive) { // Start the spinning out animation if not already spinning if (!enemies[j].spinningOut) { enemies[j].spinningOut = true; enemies[j].spinSpeed = 15; // Speed at which it flies out // Play dog sound when ls or enemy3 starts spinning out LK.getSound('dog').play(); // Increase rotation animation tween(enemies[j].children[0], { rotation: Math.PI * 10 }, { duration: 2000, easing: tween.linear }); // Add points for special effect LK.setScore(LK.getScore() + 1); // Count by one to match other defeat methods scoreText.setText(LK.getScore()); } // Don't remove from array yet - let it spin out of screen } else if (game.Mario && (game.Mario.isJumping || game.Mario.lightning && game.Mario.lightning.isActive)) { // Mario jumped on top of the enemy or has active lightning shield, destroy the enemy enemies[j].destroy(); enemies.splice(j, 1); // Add points for defeating enemy if (game.Mario && game.Mario.lightning && game.Mario.lightning.isActive) { // Add 1 point when defeating with lightning shield - changed from 2 to 1 to count consistently LK.setScore(LK.getScore() + 1); scoreText.setText(LK.getScore()); } else if (game.Mario && game.Mario.isJumping) { // Add 1 point when jumping on enemy LK.setScore(LK.getScore() + 1); scoreText.setText(LK.getScore()); } } else { if (game.Mario) { game.Mario.life -= 0.005; // Decrease Mario's life by 0.5% if (game.Mario.life <= 0) { LK.effects.flashScreen(0xff0000, 1000); LK.showGameOver(); } } } } } // Update power-ups for (var k = powerups.length - 1; k >= 0; k--) { var powerup = powerups[k]; powerup.update(); // Check for collision with Mario var currentIntersecting = game.Mario && game.Mario.intersects(powerup); if (!powerup.lastWasIntersecting && currentIntersecting) { // Power-up collected - apply effect powerup.collected = true; // Apply power-up effect if (game.Mario) { game.Mario.life = Math.min(1, game.Mario.life + 0.3); // Restore 30% of life // Flash effect when collecting power-up LK.effects.flashObject(game.Mario, 0xFFD700, 500); } // Show God mode text animation for 3 seconds godModeText.showAnimation(); // Play god mode sound LK.getSound('godmode').play(); // Enhance lightning effect for 5 seconds if (game.Mario && game.Mario.lightning) { // Set effect active flag game.Mario.lightning.isActive = true; for (var i = 0; i < game.Mario.lightning.bolts.length; i++) { var bolt = game.Mario.lightning.bolts[i]; tween(bolt, { alpha: 1, scaleX: bolt.originalScale.x * 2, scaleY: bolt.originalScale.y * 2 }, { duration: 500, easing: tween.elasticOut }); } // Update timer with the godmode duration (5 seconds) godModeText.timerValue = 5; // Reset to normal after 5 seconds (5000ms) LK.setTimeout(function () { game.Mario.lightning.isActive = false; game.Mario.lightning.animate(); }, 5000); } // Remove the power-up powerup.destroy(); powerups.splice(k, 1); } // Update tracking state if (powerup) { powerup.lastWasIntersecting = currentIntersecting; } } // Sort enemies by their x position to ensure they never cover each other in display order enemies.sort(function (a, b) { return a.x - b.x; }); // Check if there's any active ls asset in the map to show/hide SHARKNADO title var hasActiveLS = false; var hasEnemy3 = false; for (var i = 0; i < enemies.length; i++) { if (enemies[i].enemyType === 'ls' && enemies[i].x > -50 && enemies[i].x < 2048 + 50) { hasActiveLS = true; } if (enemies[i].enemyType === 'enemy3' && enemies[i].x > -50 && enemies[i].x < 2048 + 50) { hasEnemy3 = true; } } // Show or hide SHARKNADO title based on ls asset presence if (hasActiveLS) { sharknadoTitle.show(); } else { sharknadoTitle.hide(); } // Show or hide TB popup based on enemy3 presence if (hasEnemy3) { tbPopup.show(); } else { tbPopup.hide(); } // Update the life counter lifeCounter.update(); // Update life percentage text if (game.Mario) { lifePercentText.setText(Math.round(game.Mario.life * 100) + '%'); } }; // Create watermark text var watermarkText = new Text2('@jzubora', { size: 20, fill: 0x888888 // Grey color }); // Position at bottom right watermarkText.anchor.set(1, 1); // Bottom right anchor LK.gui.bottomRight.addChild(watermarkText); watermarkText.x = -20; // 20px margin from right watermarkText.y = -20; // 20px margin from bottom // Handle player jump game.down = function (x, y, obj) { if (game.Mario) game.Mario.jump(); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Define a class for enemies
var Enemy = Container.expand(function (enemyType) {
var self = Container.call(this);
var enemyGraphics = self.attachAsset(enemyType, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
self.enemyType = enemyType;
// Apply spinning animation to 'ls' enemy type
if (enemyType === 'ls') {
// Start spinning animation in left (counter-clockwise) direction
tween(enemyGraphics, {
rotation: Math.PI * 2 * -1 // Negative for left/counter-clockwise direction
}, {
duration: 2000,
// 2 seconds for a full rotation
easing: tween.linear,
repeat: Infinity,
// Use Infinity instead of loop: true for continuous spinning
loop: true // Continuous spinning
});
}
self.update = function () {
// Special behavior for ls asset - bounce down from top right
if (self.enemyType === 'ls') {
// Add vertical movement properties first time
if (self.verticalSpeed === undefined) {
self.verticalSpeed = 3; // Initial downward speed
self.gravity = 0.1; // Gravity effect
self.bounceStrength = 2; // How strong the bounce is
// Start from top-right corner
self.y = 100; // Near top
self.x = 2048 + 100; // Off right edge
}
// Track if this asset is in the spinning out state
if (self.spinningOut) {
// Move toward right top corner
var targetX = 2048 + 200;
var targetY = -200;
// Calculate direction vector
var dirX = targetX - self.x;
var dirY = targetY - self.y;
// Normalize and apply speed
var length = Math.sqrt(dirX * dirX + dirY * dirY);
if (length > 0) {
dirX = dirX / length * (self.spinSpeed || 15);
dirY = dirY / length * (self.spinSpeed || 15);
}
// Move toward target
self.x += dirX;
self.y += dirY;
// Increase rotation speed
self.children[0].rotation += 0.2;
} else {
// Normal behavior when not spinning out
// Apply gravity to vertical speed
self.verticalSpeed += self.gravity;
self.y += self.verticalSpeed;
// Bounce when hitting bottom at 3/4 of the screen height instead of center
if (self.y > 2732 * 0.75) {
// Bounce at 3/4 screen height
self.y = 2732 * 0.75;
self.verticalSpeed = -self.verticalSpeed * (self.bounceStrength * 0.3); // Even lower bounce power
// Reduce bounce strength much more quickly
self.bounceStrength *= 0.5;
}
// Horizontal movement
self.x -= self.speed;
}
} else if (self.enemyType === 'enemy3') {
// Special behavior for enemy3 - attack from right bottom corner
if (self.initialPosition === undefined) {
// Set initial position at right bottom corner
self.x = 2048 + 100; // Off right edge
self.y = 2732 - 550; // Bottom of screen with small margin, moved up by 450 units
self.initialPosition = true;
}
// Move diagonally up-left (towards Mario)
self.x -= self.speed * 1.2;
self.y -= self.speed * 0.6;
} else {
// Original behavior for other enemy types
self.x -= self.speed;
}
// Destroy when off screen
if (self.x < -50) {
self.destroy();
}
};
return self; // Return self to make the class inheritable
});
// Define a class for displaying the God Mode text
var GodModeText = Container.expand(function () {
var self = Container.call(this);
// Create the text
var godModeText = new Text2('GOD MODE', {
size: 80,
fill: 0xFF8C00 // Orange color
});
godModeText.anchor.set(0.5, 0.5);
self.addChild(godModeText);
// Initialize with invisible state
godModeText.alpha = 0;
// Create timer text
var timerText = new Text2('', {
size: 80,
fill: 0xFF8C00 // Orange color for timer
});
timerText.anchor.set(0.5, 0.5);
timerText.y = 80; // Position below the GOD MODE text
self.addChild(timerText);
// Initialize timer value
self.timerValue = 5; // 5 seconds duration
self.timerInterval = null;
// Method to show the animation
self.showAnimation = function () {
// Play god mode sound
LK.getSound('godmode').play();
// Stop any existing animations
tween.stop(godModeText);
// Reset and start the timer
self.timerValue = 5;
timerText.setText(self.timerValue);
timerText.alpha = 1;
// Clear any existing timer
if (self.timerInterval) {
LK.clearInterval(self.timerInterval);
}
// Start countdown timer
self.timerInterval = LK.setInterval(function () {
self.timerValue -= 1;
if (self.timerValue > 0) {
timerText.setText(self.timerValue);
} else if (self.timerValue === 0) {
timerText.setText(self.timerValue);
LK.clearInterval(self.timerInterval);
self.timerInterval = null;
}
}, 1000);
// Animate in with fireflame effect
godModeText.tint = 0xFF5500; // Orange-red fire color
tween(godModeText, {
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Create flickering flame animation
function fireflameFlicker() {
// Random fire colors between orange-red and yellow
var fireColors = [0xFF5500, 0xFF8800, 0xFFAA00, 0xFFFF00];
var randomColor = fireColors[Math.floor(Math.random() * fireColors.length)];
// Random scale for fire flicker effect
var randomScale = 1.0 + Math.random() * 0.3;
// Apply the flame effect
tween(godModeText, {
tint: randomColor,
scaleX: randomScale,
scaleY: randomScale,
alpha: 0.7 + Math.random() * 0.3
}, {
duration: 100 + Math.random() * 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Continue the flame effect if still visible
if (godModeText.alpha > 0) {
fireflameFlicker();
}
}
});
}
// Start the flame flicker effect
fireflameFlicker();
// Hide after 3 seconds
LK.setTimeout(function () {
// Stop the pulse animation
tween.stop(godModeText);
// Stop and hide the timer
if (self.timerInterval) {
LK.clearInterval(self.timerInterval);
self.timerInterval = null;
}
tween(timerText, {
alpha: 0
}, {
duration: 300,
easing: tween.easeIn
});
// Animate out
tween(godModeText, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 500,
easing: tween.easeIn
});
}, 3000);
}
});
};
return self;
});
// Define a class for the life counter
var LifeCounter = Container.expand(function () {
var self = Container.call(this);
// Create a simple white line instead of using an asset
var lineGraphics = LK.getAsset('enemy2', {
anchorX: 0.0,
anchorY: 0.0,
scaleX: 2048,
scaleY: 10,
tint: 0xFFFFFF // White color
});
self.addChild(lineGraphics);
self.width = 2048; // Full width
self.update = function () {
// Update the width of the line based on Mario's life
if (game.Mario) {
lineGraphics.scale.x = 2048 * game.Mario.life;
}
};
});
// Define a class for the dragon ball lightning effect
var LightningEffect = Container.expand(function () {
var self = Container.call(this);
// Set active status flag
self.isActive = false;
// Create lightning bolts
self.bolts = [];
var colors = [0xFFFF00, 0xFFFF00, 0xFFFF00, 0xFFFF00];
// Create multiple lightning bolts around Mario
for (var i = 0; i < 5; i++) {
var bolt = self.attachAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 25 + Math.random() * 10,
scaleY: 25 + Math.random() * 10
});
bolt.tint = colors[Math.floor(Math.random() * colors.length)];
bolt.alpha = 0; // Start with invisible bolts
bolt.rotation = Math.random() * Math.PI * 2;
bolt.distance = 80 + Math.random() * 40;
bolt.speed = 0.1 + Math.random() * 0.1;
bolt.originalScale = {
x: bolt.scale.x,
y: bolt.scale.y
};
self.bolts.push(bolt);
}
// Animate the lightning
self.animate = function () {
// Stop any existing animations
for (var i = 0; i < self.bolts.length; i++) {
tween.stop(self.bolts[i]);
}
// Start new animations
for (var i = 0; i < self.bolts.length; i++) {
var bolt = self.bolts[i];
// Randomize the bolt appearance
bolt.rotation = Math.random() * Math.PI * 2;
bolt.scale.x = bolt.originalScale.x * (0.8 + Math.random() * 0.4);
bolt.scale.y = bolt.originalScale.y * (0.8 + Math.random() * 0.4);
// Animate the bolt with pulse effect
tween(bolt, {
alpha: 0.2 + Math.random() * 0.5,
scaleX: bolt.originalScale.x * (0.7 + Math.random() * 0.6),
scaleY: bolt.originalScale.y * (0.7 + Math.random() * 0.6)
}, {
duration: 300 + Math.random() * 400,
easing: tween.easeOut,
onFinish: function () {
// Create a callback closure to maintain the correct bolt reference
var currentBolt = bolt;
return function () {
// Animate back
tween(currentBolt, {
alpha: 0.6 + Math.random() * 0.4,
scaleX: currentBolt.originalScale.x * (0.9 + Math.random() * 0.3),
scaleY: currentBolt.originalScale.y * (0.9 + Math.random() * 0.3)
}, {
duration: 300 + Math.random() * 400,
easing: tween.easeIn
});
};
}()
});
}
};
self.update = function () {
// Position the lightning bolts around Mario
for (var i = 0; i < self.bolts.length; i++) {
var bolt = self.bolts[i];
var angle = bolt.rotation + self.parent.rotation + bolt.speed * LK.ticks;
bolt.x = Math.cos(angle) * bolt.distance;
bolt.y = Math.sin(angle) * bolt.distance;
// Make bolts invisible when effect is not active
if (!self.isActive) {
bolt.alpha = 0;
}
// Ensure bolts remain visible when effect is active
else if (self.isActive && bolt.alpha < 0.7) {
bolt.alpha = 0.7 + Math.random() * 0.3;
}
}
// Periodically refresh animations only when not in active state
if (LK.ticks % 30 === 0 && self.isActive) {
self.animate();
}
};
return self;
});
//<Assets used in the game will automatically appear here>
// Define a class for the player character
var Player = Container.expand(function (characterType) {
var self = Container.call(this);
var assetType = characterType === 'YM' ? 'YM' : characterType === 'MB' ? 'MB' : 'Mario';
var MarioGraphics = self.attachAsset(assetType, {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 5;
// Add the lightning effect
self.lightning = self.addChild(new LightningEffect());
self.jumpHeight = 40;
self.isJumping = false;
self.velocityY = 0;
self.update = function () {
if (self.isJumping) {
self.y += self.velocityY;
self.velocityY += 0.7; // Decreased gravity effect by 30%
if (self.y >= 2732 / 2) {
// Ground level
self.y = 2732 / 2;
self.isJumping = false;
self.velocityY = 0;
}
}
// Update the lightning effect
if (self.lightning) {
self.lightning.update();
}
};
self.jump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.velocityY = -self.jumpHeight;
// Play jump sound
LK.getSound('jump').play();
// Activate lightning effect animation with a more intense glow when jumping, but only if lightning is already active
if (self.lightning && self.lightning.isActive) {
// Intensify the lightning effect
for (var i = 0; i < self.lightning.bolts.length; i++) {
var bolt = self.lightning.bolts[i];
tween(bolt, {
alpha: 0.9,
scaleX: bolt.originalScale.x * 1.5,
scaleY: bolt.originalScale.y * 1.5
}, {
duration: 300,
easing: tween.elasticOut
});
}
// Reset to normal after a short time
LK.setTimeout(function () {
self.lightning.animate();
}, 500);
}
}
};
});
// Define a class for power-ups
var PowerUp = Container.expand(function () {
var self = Container.call(this);
var powerGraphics = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 3;
self.floatOffset = 0;
self.collected = false;
// Make power-up glow with tween animation and add shiny flashing effect
tween(powerGraphics, {
alpha: 0.7,
tint: 0xffffff
}, {
duration: 1000,
easing: tween.sinusoidalInOut,
loop: true,
yoyo: true
});
// Add shiny flashing effect
function createShinyEffect() {
// Tween to bright yellow-white (shiny effect)
tween(powerGraphics, {
tint: 0xffffff,
alpha: 1,
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Tween back to normal gold
tween(powerGraphics, {
tint: 0xffd700,
alpha: 0.8,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 250,
easing: tween.easeIn,
onFinish: function onFinish() {
// Schedule next flash after a shorter random delay
LK.setTimeout(createShinyEffect, 300 + Math.random() * 400);
}
});
}
});
}
// Start the shiny effect sequence
LK.setTimeout(createShinyEffect, 200 + Math.random() * 300);
self.update = function () {
// Move power-up from right to left
self.x -= self.speed;
// Floating up and down motion
self.floatOffset += 0.05;
self.y = self.originalY + Math.sin(self.floatOffset) * 20;
// Remove if off screen
if (self.x < -100) {
self.destroy();
}
};
return self;
});
// Define a class for displaying the SHARKNADO title
var SharknadoTitle = Container.expand(function () {
var self = Container.call(this);
// Create the text with chrome-silver-black styling
var sharknadoText = new Text2('SHARKNADO!', {
size: 240,
fill: 0xDDDDDD,
// Silver base color
dropShadow: true,
dropShadowColor: 0x333333,
// Dark shadow for 3D effect
dropShadowDistance: 3
});
// Set anchor to center horizontally
sharknadoText.anchor.set(0.5, 0.5);
self.addChild(sharknadoText);
// Initialize as invisible
self.visible = false;
// Method to show the title
self.show = function () {
if (self.visible) return; // Already visible
self.visible = true;
// Clear any existing animations
tween.stop(sharknadoText);
// Reset and animate in
sharknadoText.alpha = 0;
sharknadoText.scale.set(0.5, 0.5);
// Track original position for shake effect
self.originalX = self.x;
self.originalY = self.y;
// Animate in with chrome shine effect
tween(sharknadoText, {
alpha: 1,
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 500,
easing: tween.elasticOut,
onFinish: function onFinish() {
// Add pulsing animation
tween(sharknadoText, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 800,
easing: tween.sinusoidalInOut,
yoyo: true,
loop: true
});
// Start shaking animation
self.startShaking();
}
});
};
// Method to hide the title
self.hide = function () {
if (!self.visible) return; // Already hidden
// Stop any existing animations
tween.stop(sharknadoText);
// Stop shaking
self.stopShaking();
// Animate out
tween(sharknadoText, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
self.visible = false;
}
});
};
// Start shaking animation for SHARKNADO title
self.startShaking = function () {
// Clear any existing shake interval
self.stopShaking();
// Create new shake interval
self.shakeInterval = LK.setInterval(function () {
// Random offset for shake effect (more horizontal than vertical)
var offsetX = (Math.random() - 0.5) * 20; // -10 to +10 pixels
var offsetY = (Math.random() - 0.5) * 10; // -5 to +5 pixels
// Apply shake
self.x = self.originalX + offsetX;
self.y = self.originalY + offsetY;
// Apply slight rotation for more dramatic effect
sharknadoText.rotation = (Math.random() - 0.5) * 0.05; // Small random rotation
}, 50); // Shake every 50ms for realistic earthquake effect
};
// Stop shaking animation
self.stopShaking = function () {
if (self.shakeInterval) {
LK.clearInterval(self.shakeInterval);
self.shakeInterval = null;
// Reset position and rotation
if (self.originalX !== undefined) {
self.x = self.originalX;
self.y = self.originalY;
sharknadoText.rotation = 0;
}
}
};
return self;
});
// Define a class for the TB popup
var TbPopup = Container.expand(function () {
var self = Container.call(this);
// Get TB asset
var tbGraphics = self.attachAsset('tb', {
anchorX: 0.5,
anchorY: 0.5
});
// Initialize as invisible
self.alpha = 0;
self.visible = false;
// Method to show the popup
self.show = function () {
if (self.visible) return; // Already visible
// Stop any existing animations
tween.stop(self);
// Reset and make visible
self.visible = true;
self.scale.set(0.2, 0.2);
// Animate in
tween(self, {
alpha: 1,
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 500,
easing: tween.elasticOut
});
};
// Method to hide the popup
self.hide = function () {
if (!self.visible) return; // Already hidden
// Animate out
tween(self, {
alpha: 0,
scaleX: 0.2,
scaleY: 0.2
}, {
duration: 300,
easing: tween.easeIn,
onFinish: function onFinish() {
self.visible = false;
}
});
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Sky blue background
});
/****
* Game Code
****/
// All references to 'Mri' asset have been renamed to 'MB'
//<Assets used in the game will automatically appear here>
game.lastSpawnTime = 0;
var background = game.addChild(LK.getAsset('background', {
anchorX: 0,
anchorY: 0
}));
background.x = 0;
background.y = 0;
// Play background music
LK.playMusic('music1', {
loop: true
});
// Character selection menu implementation
var characterSelectionContainer = new Container();
characterSelectionContainer.x = 2048 / 2;
characterSelectionContainer.y = 2732 / 2;
game.addChild(characterSelectionContainer);
// Dim background overlay
var overlay = LK.getAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2048,
scaleY: 2732,
tint: 0x000000,
alpha: 0.7
});
characterSelectionContainer.addChild(overlay);
// Title text
var selectText = new Text2('Select Your Character', {
size: 120,
fill: 0xFFFFFF
});
selectText.anchor.set(0.5, 0.5);
selectText.y = -400;
characterSelectionContainer.addChild(selectText);
// (Mario option removed)
// MB option
var mbOption = LK.getAsset('MB', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
mbOption.x = 0;
mbOption.y = 0;
characterSelectionContainer.addChild(mbOption);
var mbLabel = new Text2('MB', {
size: 70,
fill: 0xFFFFFF
});
mbLabel.anchor.set(0.5, 0);
mbLabel.x = mbOption.x;
mbLabel.y = 280;
characterSelectionContainer.addChild(mbLabel);
// YM option
var ymOption = LK.getAsset('YM', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.2,
scaleY: 1.2
});
ymOption.x = 500;
ymOption.y = 0;
characterSelectionContainer.addChild(ymOption);
var ymLabel = new Text2('YM', {
size: 70,
fill: 0xFFFFFF
});
ymLabel.anchor.set(0.5, 0);
ymLabel.x = ymOption.x;
ymLabel.y = 280; // moved down by 100 units
characterSelectionContainer.addChild(ymLabel);
// Store selected character type
var selectedCharacterType = null;
// Helper to highlight selection
function highlightOption(option) {
mbOption.alpha = 0.7;
ymOption.alpha = 0.7;
option.alpha = 1.0;
}
// (Mario option event handler removed)
// Touch/click handlers for MB
mbOption.down = function (x, y, obj) {
highlightOption(mbOption);
selectedCharacterType = 'MB';
confirmButton.alpha = 1;
confirmButton.interactive = true;
};
// Touch/click handlers for YM
ymOption.down = function (x, y, obj) {
highlightOption(ymOption);
selectedCharacterType = 'YM';
confirmButton.alpha = 1;
confirmButton.interactive = true;
};
// Confirm button
var confirmButton = new Text2('Start Game', {
size: 90,
fill: 0x00FF00
});
confirmButton.anchor.set(0.5, 0.5);
confirmButton.y = 400;
confirmButton.alpha = 0.5;
confirmButton.interactive = false;
characterSelectionContainer.addChild(confirmButton);
// Confirm button handler
confirmButton.down = function (x, y, obj) {
if (!selectedCharacterType) return;
// Remove menu
characterSelectionContainer.destroy();
// Proceed to game setup
setupPlayer(selectedCharacterType);
};
// Add down event listeners
mbOption.interactive = true;
ymOption.interactive = true;
confirmButton.interactive = false;
// Setup player function
function setupPlayer(characterType) {
// Initialize player with selected character
var Mario = game.addChild(new Player(characterType));
Mario.x = 2048 / 2 - 200;
Mario.y = 2732 / 2;
Mario.life = 1;
// Store reference globally for other code
game.Mario = Mario;
// Update all references in game code to use game.Mario if needed
}
// Patch Player class to accept characterType
// (see Classes section for Player class patch)
// Initialize the life counter and add it to the game
var lifeCounter = game.addChild(new LifeCounter());
lifeCounter.y = 0; // Position it at the top of the screen
// Create TB popup for enemy3
var tbPopup = game.addChild(new TbPopup());
tbPopup.x = 160; // Position on the left side, moved left by 40 units
tbPopup.y = 2732 / 2 - 777; // Center vertically, then move up by 777 units
// Create life percentage text
var lifePercentText = new Text2('100%', {
size: 40,
fill: 0x7FFF00 // Bright neon green to match the life bar
});
// Add the life percentage text to the GUI
LK.gui.topRight.addChild(lifePercentText);
// Position the text
lifePercentText.anchor.set(1, 0); // Right align
lifePercentText.x = -20; // Add right margin
lifePercentText.y = 20; // Add top margin
// Initialize enemies
var enemies = [];
var enemySpawnInterval = 100;
var enemySpawnCounter = 0;
// Initialize power-ups
var powerups = [];
var powerupSpawnInterval = 300;
var powerupSpawnCounter = 0;
// Create GodModeText instance
var godModeText = game.addChild(new GodModeText());
godModeText.x = 2048 / 2; // Center horizontally
godModeText.y = 2732 / 2 - 1050; // Position above the center (moved up by 650 units)
// Create a new Text2 object to display the score
var scoreLabelText = new Text2('Defeated enemies: ', {
size: 40,
fill: 0xFFFFFF
});
var scoreText = new Text2('0', {
size: 50,
fill: 0xFFFFFF
});
// Create the SHARKNADO title
var sharknadoTitle = game.addChild(new SharknadoTitle());
sharknadoTitle.x = 2048 / 2; // Center horizontally
sharknadoTitle.y = scoreText.y + 200; // Position further below the score text (moved down by 100 units)
// Add the score label and text to the game GUI at the top center of the screen
LK.gui.top.addChild(scoreLabelText);
LK.gui.top.addChild(scoreText);
// Position the label and score text
scoreLabelText.anchor.set(1, 0); // Right align the label
scoreText.anchor.set(0, 0); // Left align the score
scoreLabelText.x = 2048 / 2 - 880; // Position to the left of center, moved 880 units left
scoreLabelText.y = 20; // Add a small top margin
scoreText.x = 2048 / 2 - 840; // Position to the right of center, moved 840 units left
scoreText.y = 10; // Add a small top margin
// Set up a regular interval to check and ensure enemy spawning
var enemyWaveScheduler = LK.setInterval(function () {
// If there are no enemies on screen for more than 3 seconds, spawn a new wave
if (enemies.length === 0 && LK.ticks - game.lastSpawnTime > 180) {
// 3 seconds at 60fps
// Reset spawn counter to trigger spawn on next update
enemySpawnCounter = enemySpawnInterval;
}
}, 1000); // Check every second
// Handle game updates
game.update = function () {
if (game.Mario) game.Mario.update();
// Prevent enemies and powerups from spawning until player is selected
if (!game.Mario) {
return;
}
// Spawn enemies
enemySpawnCounter++;
// Define maximum number of active enemies allowed at once
var maxEnemies = Math.min(40, LK.getScore() + 5); // Scales with score up to 40 enemies
// Track time since last enemy spawn
if (!game.lastSpawnTime) {
game.lastSpawnTime = LK.ticks;
}
// Calculate time passed since last spawn (in frames, 60fps means 240 frames = 4 seconds)
var framesSinceLastSpawn = LK.ticks - game.lastSpawnTime;
// Force spawn if more than 4 seconds have passed without enemies
var forceSpawn = framesSinceLastSpawn > 240;
if (enemySpawnCounter >= enemySpawnInterval && enemies.length < maxEnemies || forceSpawn && enemies.length === 0) {
// Define enemy types
var enemyTypes = ['enemy', 'enemy1', 'enemy2', 'enemy3', 'jjb', 'ls'];
// Keep track of the last enemy type to avoid repetition
if (!game.lastEnemyTypeIndex) {
game.lastEnemyTypeIndex = -1;
}
// Reset last spawn time
game.lastSpawnTime = LK.ticks;
// Only spawn one enemy at a time
var _spawnEnemy = function spawnEnemy() {
// Check if we reached the maximum number of enemies
if (enemies.length >= maxEnemies) {
// Check if we've reached a permanent limit (meaning we can't spawn more enemies)
if (maxEnemies >= 40 && LK.getScore() >= 35) {
// If all current enemies are defeated, show win message
if (enemies.length === 0) {
LK.showYouWin();
}
}
return; // Don't spawn more enemies if we reached the limit
}
// Check if we're already in process of spawning (avoid duplicate calls)
if (game.isSpawningEnemy) {
return; // Prevent multiple simultaneous spawns
}
game.isSpawningEnemy = true;
// Get a new enemy type that is different from the last one
var availableTypes = [];
for (var t = 0; t < enemyTypes.length; t++) {
// Skip enemy2 type which is too small to be visible (4x4)
if (t !== game.lastEnemyTypeIndex && enemyTypes[t] !== 'enemy2') {
availableTypes.push({
index: t,
type: enemyTypes[t]
});
}
}
// Randomly select from available types
var selectedType = availableTypes[Math.floor(Math.random() * availableTypes.length)];
game.lastEnemyTypeIndex = selectedType.index;
var enemy = new Enemy(selectedType.type);
enemy.x = 2048 + 100; // Fixed distance from edge of screen
enemy.y = 2732 / 2;
enemies.push(enemy);
game.addChild(enemy);
// Schedule the next enemy spawn with a fixed 1 second delay to ensure sequential spawning
if (enemies.length < maxEnemies) {
game.nextEnemyTimeout = LK.setTimeout(function () {
// Force an enemy spawn by setting the spawn counter to threshold
enemySpawnCounter = enemySpawnInterval;
// Update last spawn time to now
game.lastSpawnTime = LK.ticks;
// Reset spawning flag to allow next enemy spawn
game.isSpawningEnemy = false;
}, 1000); // Exactly 1 second delay between spawns
} else {
// Reset spawning flag if we're not spawning more
game.isSpawningEnemy = false;
}
};
_spawnEnemy();
// Randomize the spawn interval for the next enemy, but ensure it's not too long
enemySpawnInterval = Math.floor(Math.random() * 120) + 30; // Reduced max interval
enemySpawnCounter = 0;
}
// Spawn power-ups
powerupSpawnCounter++;
if (powerupSpawnCounter >= powerupSpawnInterval) {
var powerup = new PowerUp();
powerup.x = 2048 + 200; // Start a bit off screen
powerup.y = 2732 / 2 - 200; // Above the players level
powerup.originalY = powerup.y; // Store original Y for floating animation
powerup.lastWasIntersecting = false; // For tracking intersection
powerups.push(powerup);
game.addChild(powerup);
// Randomize the spawn interval for the next power-up
powerupSpawnInterval = Math.floor(Math.random() * 400) + 300;
powerupSpawnCounter = 0;
}
// Update enemies
for (var j = enemies.length - 1; j >= 0; j--) {
enemies[j].update();
if (game.Mario && game.Mario.intersects(enemies[j])) {
// Check if enemy is jjb type and play JJBE sound
if (enemies[j].enemyType === 'jjb' && !enemies[j].jjbeSoundPlayed && !(game.Mario && game.Mario.lightning && game.Mario.lightning.isActive) && !(game.Mario && game.Mario.isJumping) // Do not play if Mario is jumping on jjb
) {
// Play JJBE sound when Mario hits jjb asset and mark it as played
// Only play if not in godmode and not jumping
LK.getSound('JJBE').play();
enemies[j].jjbeSoundPlayed = true;
} else if (enemies[j].enemyType === 'jjb' && game.Mario && game.Mario.lightning && game.Mario.lightning.isActive && !enemies[j].aniSoundPlayed) {
// Play Ani sound if godmode is active and Mario touches jjb asset
LK.getSound('Ani').play();
enemies[j].aniSoundPlayed = true;
}
// Special handling for JJB assets during God mode - spin horizontally and disappear
if (enemies[j].enemyType === 'jjb' && game.Mario && game.Mario.lightning && game.Mario.lightning.isActive) {
// Start the spinning animation if not already spinning
if (!enemies[j].spinningOut) {
enemies[j].spinningOut = true;
// Stop movement
enemies[j].speed = 0;
// Get the JJB asset graphic
var jjbGraphic = enemies[j].children[0];
// First horizontal 360 spin
tween(jjbGraphic, {
scaleX: -1 // Flip horizontally
}, {
duration: 150,
// 0.15 second for first half spin
easing: tween.linear,
onFinish: function onFinish() {
// Continue to full 360
tween(jjbGraphic, {
scaleX: 1 // Back to original
}, {
duration: 150,
// 0.15 second for second half spin
easing: tween.linear,
onFinish: function onFinish() {
// Second 360 spin
tween(jjbGraphic, {
scaleX: -1 // Flip horizontally again
}, {
duration: 150,
// 0.15 second for third half spin
easing: tween.linear,
onFinish: function onFinish() {
// Complete second 360
tween(jjbGraphic, {
scaleX: 1 // Back to original
}, {
duration: 150,
// 0.15 second for second full spin
easing: tween.linear,
onFinish: function onFinish() {
// Third 360 spin
tween(jjbGraphic, {
scaleX: -1 // Flip horizontally third time
}, {
duration: 150,
// 0.15 second for fifth half spin
easing: tween.linear,
onFinish: function onFinish() {
// Complete third 360
tween(jjbGraphic, {
scaleX: 1,
// Back to original
alpha: 0 // Fade out
}, {
duration: 150,
// 0.15 second for final half spin and fade
easing: tween.linear,
onFinish: function onFinish() {
// Remove the enemy after spinning
enemies[j].destroy();
enemies.splice(j, 1);
// Add points for defeating enemy
LK.setScore(LK.getScore() + 1);
scoreText.setText(LK.getScore());
}
});
}
});
}
});
}
});
}
});
}
});
// Mark as being handled to prevent other collision effects
return;
}
}
// Special handling for 'ls' or 'enemy3' assets during God mode
if ((enemies[j].enemyType === 'ls' || enemies[j].enemyType === 'enemy3') && game.Mario && game.Mario.lightning && game.Mario.lightning.isActive) {
// Start the spinning out animation if not already spinning
if (!enemies[j].spinningOut) {
enemies[j].spinningOut = true;
enemies[j].spinSpeed = 15; // Speed at which it flies out
// Play dog sound when ls or enemy3 starts spinning out
LK.getSound('dog').play();
// Increase rotation animation
tween(enemies[j].children[0], {
rotation: Math.PI * 10
}, {
duration: 2000,
easing: tween.linear
});
// Add points for special effect
LK.setScore(LK.getScore() + 1); // Count by one to match other defeat methods
scoreText.setText(LK.getScore());
}
// Don't remove from array yet - let it spin out of screen
} else if (game.Mario && (game.Mario.isJumping || game.Mario.lightning && game.Mario.lightning.isActive)) {
// Mario jumped on top of the enemy or has active lightning shield, destroy the enemy
enemies[j].destroy();
enemies.splice(j, 1);
// Add points for defeating enemy
if (game.Mario && game.Mario.lightning && game.Mario.lightning.isActive) {
// Add 1 point when defeating with lightning shield - changed from 2 to 1 to count consistently
LK.setScore(LK.getScore() + 1);
scoreText.setText(LK.getScore());
} else if (game.Mario && game.Mario.isJumping) {
// Add 1 point when jumping on enemy
LK.setScore(LK.getScore() + 1);
scoreText.setText(LK.getScore());
}
} else {
if (game.Mario) {
game.Mario.life -= 0.005; // Decrease Mario's life by 0.5%
if (game.Mario.life <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
}
}
}
}
// Update power-ups
for (var k = powerups.length - 1; k >= 0; k--) {
var powerup = powerups[k];
powerup.update();
// Check for collision with Mario
var currentIntersecting = game.Mario && game.Mario.intersects(powerup);
if (!powerup.lastWasIntersecting && currentIntersecting) {
// Power-up collected - apply effect
powerup.collected = true;
// Apply power-up effect
if (game.Mario) {
game.Mario.life = Math.min(1, game.Mario.life + 0.3); // Restore 30% of life
// Flash effect when collecting power-up
LK.effects.flashObject(game.Mario, 0xFFD700, 500);
}
// Show God mode text animation for 3 seconds
godModeText.showAnimation();
// Play god mode sound
LK.getSound('godmode').play();
// Enhance lightning effect for 5 seconds
if (game.Mario && game.Mario.lightning) {
// Set effect active flag
game.Mario.lightning.isActive = true;
for (var i = 0; i < game.Mario.lightning.bolts.length; i++) {
var bolt = game.Mario.lightning.bolts[i];
tween(bolt, {
alpha: 1,
scaleX: bolt.originalScale.x * 2,
scaleY: bolt.originalScale.y * 2
}, {
duration: 500,
easing: tween.elasticOut
});
}
// Update timer with the godmode duration (5 seconds)
godModeText.timerValue = 5;
// Reset to normal after 5 seconds (5000ms)
LK.setTimeout(function () {
game.Mario.lightning.isActive = false;
game.Mario.lightning.animate();
}, 5000);
}
// Remove the power-up
powerup.destroy();
powerups.splice(k, 1);
}
// Update tracking state
if (powerup) {
powerup.lastWasIntersecting = currentIntersecting;
}
}
// Sort enemies by their x position to ensure they never cover each other in display order
enemies.sort(function (a, b) {
return a.x - b.x;
});
// Check if there's any active ls asset in the map to show/hide SHARKNADO title
var hasActiveLS = false;
var hasEnemy3 = false;
for (var i = 0; i < enemies.length; i++) {
if (enemies[i].enemyType === 'ls' && enemies[i].x > -50 && enemies[i].x < 2048 + 50) {
hasActiveLS = true;
}
if (enemies[i].enemyType === 'enemy3' && enemies[i].x > -50 && enemies[i].x < 2048 + 50) {
hasEnemy3 = true;
}
}
// Show or hide SHARKNADO title based on ls asset presence
if (hasActiveLS) {
sharknadoTitle.show();
} else {
sharknadoTitle.hide();
}
// Show or hide TB popup based on enemy3 presence
if (hasEnemy3) {
tbPopup.show();
} else {
tbPopup.hide();
}
// Update the life counter
lifeCounter.update();
// Update life percentage text
if (game.Mario) {
lifePercentText.setText(Math.round(game.Mario.life * 100) + '%');
}
};
// Create watermark text
var watermarkText = new Text2('@jzubora', {
size: 20,
fill: 0x888888 // Grey color
});
// Position at bottom right
watermarkText.anchor.set(1, 1); // Bottom right anchor
LK.gui.bottomRight.addChild(watermarkText);
watermarkText.x = -20; // 20px margin from right
watermarkText.y = -20; // 20px margin from bottom
// Handle player jump
game.down = function (x, y, obj) {
if (game.Mario) game.Mario.jump();
};
Ghiblis style
Ghibli style but more realistic
Ghibli style
NeonYellow powerup logo. In-Game asset. 2d. High contrast. No shadows
Running Sith jar-jar binks in ghibli style. In-Game asset. 2d. High contrast. No shadows
Shark with lasergun on his head, sideview, ghibli style. In-Game asset. 2d. High contrast. No shadows
White textbubble with thin black frame. 'Run away! A killer rabbit!' text in bubble. In-Game asset. 2d. High contrast. No shadows
Remove 3. Stripe from the shoe and the kit