User prompt
The trail should disappear after 700 ms BUT the tail should stop spawning 2.25 seconds after the star spawned
User prompt
The trail shoul disappear after 2 second
User prompt
The trail should come when Shurikens be faster
User prompt
Shurikenlere yavaşça yok olan (fade) bir tail ekle ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
The game should start after the text goes out ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
The text should disappear 3 sec after the game start ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make a text that says "Shurikens Will Increase Every 10 Seconds Be Careful!"
User prompt
Enemy3 comes at 20 second
User prompt
Do it
User prompt
Please fix the bug: 'ReferenceError: Powerup is not defined' in or related to this line: 'var powerup = new Powerup();' Line Number: 184
User prompt
Please fix the bug: 'ReferenceError: Enemy is not defined' in or related to this line: 'enemy = new Enemy();' Line Number: 118
User prompt
Please fix the bug: 'Player is not defined' in or related to this line: 'player = new Player();' Line Number: 63
User prompt
Please fix the bug: 'Player is not defined' in or related to this line: 'player = new Player();' Line Number: 63
User prompt
Her on sanıyede hızlansın demiştim ya o hızlanmayı biraz değiştirelim 10 sn sonra farklı rakipler gelsin onun için yeni 2 asset oluştur
User prompt
Oluştur artık
User prompt
Sarı puan veren toplar daha çok spawn lansın
User prompt
Dokunduğumuz yere ışınlanmasın dokunup sürüklediğimizde ilerlesim
User prompt
Karakterimizin kontrolünü bütün ekrana taşı yani karakterin üzerine dokunduğumuzda değil ekranın herhangi bir yerine dokunduğumuzda hareket edebilsin
User prompt
Karakterimize gelen engeller her 10 sn de bir artsın ve daha hızlı gelsinler
Code edit (1 edits merged)
Please save this source code
User prompt
Survival IO
Initial prompt
Made a game named survival io
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ // --- Enemy Class --- var Enemy = Container.expand(function () { var self = Container.call(this); // Attach enemy asset (image) var enemyAsset = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); self.radius = enemyAsset.width / 2; self.speed = 3; self.angle = 0; self.hasSpawnedText = false; // Flag to track if this enemy already spawned text self.update = function () { // Store lastX, lastY for good practice (if needed for triggers) if (typeof self.lastX === "undefined") self.lastX = self.x; if (typeof self.lastY === "undefined") self.lastY = self.y; // Move in direction of angle self.x += Math.cos(self.angle) * self.speed * globalSpeedMultiplier; self.y += Math.sin(self.angle) * self.speed * globalSpeedMultiplier; // Update lastX, lastY self.lastX = self.x; self.lastY = self.y; }; return self; }); // --- Enemy2 Class (Red, smaller, faster) --- var Enemy2 = Container.expand(function () { var self = Container.call(this); // Attach enemy2 asset (image) var enemyAsset = self.attachAsset('enemy2', { anchorX: 0.5, anchorY: 0.5 }); self.radius = enemyAsset.width / 2; self.speed = 5; // Faster than regular enemy self.angle = 0; self.hasSpawnedText = false; // Flag to track if this enemy already spawned text self.update = function () { // Store lastX, lastY for good practice if (typeof self.lastX === "undefined") self.lastX = self.x; if (typeof self.lastY === "undefined") self.lastY = self.y; // Move in direction of angle self.x += Math.cos(self.angle) * self.speed * globalSpeedMultiplier; self.y += Math.sin(self.angle) * self.speed * globalSpeedMultiplier; // Update lastX, lastY self.lastX = self.x; self.lastY = self.y; }; return self; }); // --- Enemy3 Class (Purple, wide, special movement) --- var Enemy3 = Container.expand(function () { var self = Container.call(this); // Attach enemy3 asset (image) var enemyAsset = self.attachAsset('enemy3', { anchorX: 0.5, anchorY: 0.5 }); self.radius = enemyAsset.width / 2; self.speed = 2; // Slower but wider self.angle = 0; self.wobbleOffset = 0; // For special movement pattern self.hasSpawnedText = false; // Flag to track if this enemy already spawned text self.update = function () { // Store lastX, lastY for good practice if (typeof self.lastX === "undefined") self.lastX = self.x; if (typeof self.lastY === "undefined") self.lastY = self.y; // Special wobble movement self.wobbleOffset += 0.1; var wobbleX = Math.cos(self.wobbleOffset) * 50; // Side-to-side wobble var wobbleY = Math.sin(self.wobbleOffset * 0.5) * 30; // Up-down wobble // Move in direction of angle with wobble self.x += Math.cos(self.angle) * self.speed * globalSpeedMultiplier + wobbleX * 0.02; self.y += Math.sin(self.angle) * self.speed * globalSpeedMultiplier + wobbleY * 0.02; // Update lastX, lastY self.lastX = self.x; self.lastY = self.y; }; return self; }); // --- Player Class --- var Player = Container.expand(function () { var self = Container.call(this); // Attach player asset var playerAsset = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); // Set radius for collision self.radius = playerAsset.width / 2; // Flash effect on hit self.flash = function () { LK.effects.flashObject(self, 0xff0000, 600); }; return self; }); // --- Powerup Class --- var Powerup = Container.expand(function () { var self = Container.call(this); // Attach powerup asset var powerupAsset = self.attachAsset('powerup', { anchorX: 0.5, anchorY: 0.5 }); // Set radius for collision self.radius = powerupAsset.width / 2; return self; }); // --- Trail Class --- var Trail = Container.expand(function (assetId) { var self = Container.call(this); // Attach the same asset as the enemy var trailAsset = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Start with some transparency self.alpha = 0.6; // Fade out over time using tween tween(self, { alpha: 0 }, { duration: 700, onFinish: function onFinish() { if (self.parent) { self.destroy(); } } }); return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x181c24 }); /**** * Game Code ****/ // --- Game Variables --- // --- Asset Initialization --- // Red, smaller, faster // Purple, wide, special var player; var enemies = []; var powerups = []; var trails = []; var dragNode = null; var lastPlayerPos = { x: 0, y: 0 }; var lastHit = false; var score = 0; var scoreTxt; var survivalTime = 0; var timeTxt; var spawnTimer = 0; var spawnInterval = 120; // frames (reduced from 150 to 120 for better balance) var powerupTimer = 0; var powerupInterval = 300; // frames (was 600, now twice as frequent) var difficultyTimer = 0; var minSpawnInterval = 60; // Increased from 30 to 60 to keep a reasonable minimum spawn rate var gameStarted = false; // Control when gameplay begins globalSpeedMultiplier = 1.0; // Reset speed multiplier var globalSpeedMultiplier = 1.0; // Multiplier for all enemy speeds var enemy2SpawnMultiplier = 1.0; // Multiplier for Enemy2 spawn chance var enemy3SpawnMultiplier = 1.0; // Multiplier for Enemy3 spawn chance var bestScore = storage.bestScore || 0; // Load best score from storage var bestScoreTxt; var dodgeSoundIndex = 0; // Track which dodge sound to play next // --- UI --- scoreTxt = new Text2('Score: 0', { size: 100, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); timeTxt = new Text2('Time: 0.0', { size: 70, fill: 0xB0EAFF }); timeTxt.anchor.set(0.5, 0); LK.gui.top.addChild(timeTxt); timeTxt.y = 110; bestScoreTxt = new Text2('Best: ' + bestScore, { size: 60, fill: 0x00FF00 }); bestScoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(bestScoreTxt); bestScoreTxt.y = 180; // Warning text about shuriken increase var warningTxt = new Text2('Shurikens Will Increase Every 10 Seconds Be Careful!', { size: 60, fill: 0xFFFF00 }); warningTxt.anchor.set(0.5, 0); LK.gui.top.addChild(warningTxt); warningTxt.y = 300; // Fade out warning text after 3 seconds tween(warningTxt, { alpha: 0 }, { duration: 3000, onFinish: function onFinish() { warningTxt.visible = false; gameStarted = true; // Start the game when text fades out } }); // --- Player --- player = new Player(); player.x = 2048 / 2; player.y = 2732 / 2; game.addChild(player); // --- Helper Functions --- function resetGameVars() { // Check if current score is a new record before resetting if (score > bestScore) { bestScore = score; storage.bestScore = bestScore; // Save to storage } enemies = []; powerups = []; trails = []; score = 0; survivalTime = 0; spawnTimer = 0; spawnInterval = 90; powerupTimer = 0; difficultyTimer = 0; lastHit = false; gameStarted = false; // Reset game start state globalSpeedMultiplier = 1.0; // Reset speed multiplier enemy2SpawnMultiplier = 1.0; // Reset Enemy2 spawn multiplier enemy3SpawnMultiplier = 1.0; // Reset Enemy3 spawn multiplier scoreTxt.setText('Score: 0'); timeTxt.setText('Time: 0.0'); bestScoreTxt.setText('Best: ' + bestScore); player.x = 2048 / 2; player.y = 2732 / 2; // Show warning text again on reset warningTxt.visible = true; warningTxt.alpha = 1; // Fade out warning text after 3 seconds tween(warningTxt, { alpha: 0 }, { duration: 3000, onFinish: function onFinish() { warningTxt.visible = false; gameStarted = true; // Start the game when text fades out } }); } // --- Spawning --- function spawnEnemy() { // Spawn at random edge, move toward player var enemy; // After 10 seconds, start mixing in Enemy2, after 20 seconds add Enemy3 if (survivalTime >= 20) { var r = Math.random(); var enemy2Chance = Math.min(0.5, 0.3 * enemy2SpawnMultiplier); var enemy3Chance = Math.min(0.4, 0.2 * enemy3SpawnMultiplier); var enemyChance = Math.max(0.1, 0.4 - (enemy2Chance - 0.3) - (enemy3Chance - 0.2)); if (r < enemyChance) { enemy = new Enemy(); } else if (r < enemyChance + enemy2Chance) { enemy = new Enemy2(); } else { enemy = new Enemy3(); } } else if (survivalTime >= 10) { var r = Math.random(); var enemy2Chance = Math.min(0.7, 0.4 * enemy2SpawnMultiplier); var enemyChance = Math.max(0.3, 0.6 - (enemy2Chance - 0.4)); if (r < enemyChance) { enemy = new Enemy(); } else { enemy = new Enemy2(); } } else { enemy = new Enemy(); } var edge = Math.floor(Math.random() * 4); var x, y; if (edge === 0) { // Top x = 200 + Math.random() * (2048 - 400); y = -80; } else if (edge === 1) { // Bottom x = 200 + Math.random() * (2048 - 400); y = 2732 + 80; } else if (edge === 2) { // Left x = -80; y = 200 + Math.random() * (2732 - 400); } else { // Right x = 2048 + 80; y = 200 + Math.random() * (2732 - 400); } enemy.x = x; enemy.y = y; // Track spawn time for trail duration control enemy.spawnTime = LK.ticks; // Aim at player var dx = player.x - x; var dy = player.y - y; enemy.angle = Math.atan2(dy, dx); if (typeof baseEnemySpeed === "undefined") { baseEnemySpeed = 3; } // Only set speed for Enemy (default), others use their own if (enemy instanceof Enemy) { enemy.speed = baseEnemySpeed + Math.random() * 1 + survivalTime / 40; // Reduced random from 2 to 1, survival bonus from /20 to /40 } // Add more variety: after 30 seconds, occasionally spawn a random fast Enemy2 or a wobbly Enemy3 if (survivalTime >= 30 && Math.random() < 0.15) { if (Math.random() < 0.5) { enemy = new Enemy2(); enemy.speed += 2 + Math.random() * 2; // Super fast } else { enemy = new Enemy3(); enemy.speed += 1 + Math.random(); // Extra wobbly } enemy.x = x; enemy.y = y; enemy.spawnTime = LK.ticks; enemy.angle = Math.atan2(dy, dx); } enemies.push(enemy); game.addChild(enemy); } function spawnPowerup() { var powerup = new Powerup(); powerup.x = 200 + Math.random() * (2048 - 400); powerup.y = 200 + Math.random() * (2732 - 400); powerups.push(powerup); game.addChild(powerup); } // --- Collision --- function circleIntersect(a, b) { var dx = a.x - b.x; var dy = a.y - b.y; var dist = Math.sqrt(dx * dx + dy * dy); return dist < (a.radius + b.radius) * 0.85; } // --- Drag Controls --- function handleMove(x, y, obj) { if (dragNode) { // Only move player if dragging (i.e., finger/mouse is moving after down) // Calculate movement delta from lastPlayerPos if (lastPlayerPos.active) { var dx = x - lastPlayerPos.x; var dy = y - lastPlayerPos.y; // Only move if there is a delta (prevents teleport on first touch) if (dx !== 0 || dy !== 0) { var newX = dragNode.x + dx; var newY = dragNode.y + dy; // Clamp to game area (avoid top left 100x100) var px = Math.max(100 + player.radius, Math.min(2048 - player.radius, newX)); var py = Math.max(100 + player.radius, Math.min(2732 - player.radius, newY)); dragNode.x = px; dragNode.y = py; } } // Update lastPlayerPos for next move lastPlayerPos.x = x; lastPlayerPos.y = y; lastPlayerPos.active = true; } } game.move = handleMove; game.down = function (x, y, obj) { dragNode = player; // Record initial drag position, but do not move player yet lastPlayerPos.x = x; lastPlayerPos.y = y; lastPlayerPos.active = false; // Prevents teleport on first move }; game.up = function (x, y, obj) { dragNode = null; lastPlayerPos.active = false; }; // --- Main Update Loop --- game.update = function () { // Don't start gameplay until warning text has faded out if (!gameStarted) { return; } // Survival time survivalTime += 1 / 60; timeTxt.setText('Time: ' + survivalTime.toFixed(1)); // Difficulty scaling difficultyTimer++; if (difficultyTimer % 600 === 0) { // Every 10 seconds, increase difficulty // Decrease spawn interval (more frequent spawns) if (spawnInterval > minSpawnInterval) { spawnInterval -= 3; // Reduced from 8 to 3 for more gradual spawn rate increase if (spawnInterval < minSpawnInterval) spawnInterval = minSpawnInterval; } // Increase number of enemies spawned per interval (only every other interval) if (typeof enemiesPerSpawn === "undefined") { enemiesPerSpawn = 1; } if (difficultyTimer % 1200 === 0) { // Only increase every 20 seconds instead of 10 enemiesPerSpawn++; } // Increase base enemy speed if (typeof baseEnemySpeed === "undefined") { baseEnemySpeed = 3; } baseEnemySpeed += 0.3; // Reduced from 1.0 to 0.3 for more gradual speed increase // Increase global speed multiplier for all enemies globalSpeedMultiplier += 0.1; // Reduced from 0.3 to 0.1 for more gradual speed increase // Increase Enemy2 and Enemy3 spawn rates enemy2SpawnMultiplier += 0.2; // Reduced from 0.5 to 0.2 enemy3SpawnMultiplier += 0.15; // Reduced from 0.4 to 0.15 // Introduce new enemy types as time passes // (handled in spawnEnemy, but you can add more logic here if needed) } // Enemy spawn spawnTimer++; if (spawnTimer >= spawnInterval) { // Spawn multiple enemies per interval as difficulty increases if (typeof enemiesPerSpawn === "undefined") { enemiesPerSpawn = 1; } for (var s = 0; s < enemiesPerSpawn; s++) { spawnEnemy(); } spawnTimer = 0; } // Powerup spawn powerupTimer++; if (powerupTimer >= powerupInterval) { // Spawn 2 powerups per interval for more frequent yellow balls for (var i = 0; i < 2; i++) { spawnPowerup(); } powerupTimer = 0; } // Update enemies for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; e.update(); // Create trail only when enemy is moving faster than base speed AND within spawn time limits var shouldCreateTrail = false; var timeSinceSpawn = (LK.ticks - (e.spawnTime || 0)) / 60; // Convert to seconds // Enemy has 2.25 seconds, Enemy2 and Enemy3 have 1 second var trailTimeLimit = e instanceof Enemy ? 2.25 : 1.0; if (timeSinceSpawn <= trailTimeLimit) { if (e instanceof Enemy && e.speed > baseEnemySpeed + 1) { shouldCreateTrail = true; } else if (e instanceof Enemy2 && e.speed > 5) { shouldCreateTrail = true; } else if (e instanceof Enemy3 && e.speed > 2) { shouldCreateTrail = true; } } if (shouldCreateTrail && LK.ticks % 8 === 0) { var assetId = 'enemy'; // Default if (e instanceof Enemy2) assetId = 'enemy2'; if (e instanceof Enemy3) assetId = 'enemy3'; var trail = new Trail(assetId); trail.x = e.x; trail.y = e.y; trail.rotation = e.rotation || 0; trails.push(trail); game.addChild(trail); } // Remove if off screen if (e.x < -200 || e.x > 2248 || e.y < -200 || e.y > 2932) { e.destroy(); enemies.splice(i, 1); continue; } // Check for close calls (near misses) var dx = e.x - player.x; var dy = e.y - player.y; var distance = Math.sqrt(dx * dx + dy * dy); var closeCallDistance = (e.radius + player.radius) * 1.5; // 1.5x collision distance // Initialize lastWasClose if not set if (typeof e.lastWasClose === "undefined") e.lastWasClose = false; var isCloseNow = distance <= closeCallDistance; // Trigger close call effect when transitioning from not close to close, but only once per enemy if (!e.lastWasClose && isCloseNow && !circleIntersect(e, player) && !e.hasSpawnedText) { // Create encouraging text var messages = ['Good!', 'Wow!', 'Excellent!', 'Nice!', 'Amazing!']; var message = messages[Math.floor(Math.random() * messages.length)]; var encourageText = new Text2(message, { size: 120, fill: 0x00FF00 }); encourageText.anchor.set(0.5, 0.5); encourageText.x = player.x + (Math.random() - 0.5) * 200; // Random offset around player encourageText.y = player.y + (Math.random() - 0.5) * 200; // Ensure text stays within screen bounds encourageText.x = Math.max(100, Math.min(2048 - 100, encourageText.x)); encourageText.y = Math.max(100, Math.min(2732 - 100, encourageText.y)); game.addChild(encourageText); // Animate text: pop up, then fade out encourageText.scaleX = 0.1; encourageText.scaleY = 0.1; encourageText.alpha = 1; // --- Bonus Points and Bonus Text --- score += 10; scoreTxt.setText('Score: ' + score); // Show "Bonus Point +10" text near the score table (top center) var bonusText = new Text2("Bonus Point +10", { size: 80, fill: 0xFFD700 }); bonusText.anchor.set(0.5, 0); bonusText.x = scoreTxt.x; bonusText.y = scoreTxt.y + scoreTxt.height + 10; LK.gui.top.addChild(bonusText); bonusText.alpha = 1; bonusText.scaleX = 0.7; bonusText.scaleY = 0.7; tween(bonusText, { scaleX: 1.1, scaleY: 1.1, y: bonusText.y - 30 }, { duration: 180, easing: tween.easeOut, onFinish: function onFinish() { tween(bonusText, { alpha: 0, y: bonusText.y - 30 }, { duration: 600, onFinish: function onFinish() { if (bonusText.parent) bonusText.destroy(); } }); } }); // Mark this enemy as having spawned text e.hasSpawnedText = true; // Play dodge sound effect - alternate between two sounds var soundName = dodgeSoundIndex === 0 ? 'dodge' : 'dodge2'; LK.getSound(soundName).play(); dodgeSoundIndex = (dodgeSoundIndex + 1) % 2; // Alternate between 0 and 1 // Player pop and flash effect for juicy feedback tween(player, { scaleX: 1.25, scaleY: 1.25 }, { duration: 80, onFinish: function onFinish() { tween(player, { scaleX: 1, scaleY: 1 }, { duration: 120 }); } }); LK.effects.flashObject(player, 0x00ffcc, 200); // First tween: pop up tween(encourageText, { scaleX: 1.2, scaleY: 1.2 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { // Second tween: fade out tween(encourageText, { alpha: 0, scaleX: 0.8, scaleY: 0.8, y: encourageText.y - 50 }, { duration: 800, easing: tween.easeIn, onFinish: function onFinish() { if (encourageText.parent) { encourageText.destroy(); } } }); } }); } // Update last close state e.lastWasClose = isCloseNow; // Collision with player if (circleIntersect(e, player)) { if (!lastHit) { // Player hit: pop and flash effect tween(player, { scaleX: 1.4, scaleY: 1.4 }, { duration: 100, onFinish: function onFinish() { tween(player, { scaleX: 1, scaleY: 1 }, { duration: 180 }); } }); player.flash(); LK.effects.flashScreen(0xff0000, 600); // Update best score before showing game over if (score > bestScore) { bestScore = score; storage.bestScore = bestScore; // Save to storage bestScoreTxt.setText('Best: ' + bestScore); LK.showGameOver('New Record: ' + score); } else { LK.showGameOver(); } lastHit = true; return; } } } // Clean up destroyed trails for (var t = trails.length - 1; t >= 0; t--) { var trail = trails[t]; if (!trail.parent || trail.alpha <= 0) { if (trail.parent) trail.destroy(); trails.splice(t, 1); } } // Remove dead enemies if game over if (lastHit) { for (var j = 0; j < enemies.length; j++) { enemies[j].destroy(); } enemies = []; for (var k = 0; k < powerups.length; k++) { powerups[k].destroy(); } powerups = []; for (var l = 0; l < trails.length; l++) { trails[l].destroy(); } trails = []; return; } // Update powerups for (var p = powerups.length - 1; p >= 0; p--) { var pu = powerups[p]; // Collision with player if (circleIntersect(pu, player)) { score += 10; scoreTxt.setText('Score: ' + score); // Create pop effect before destroying powerup tween(pu, { scaleX: 2.5, scaleY: 2.5, alpha: 0 }, { duration: 300, easing: tween.easeOut, onFinish: function onFinish() { if (pu.parent) { pu.destroy(); } } }); // Player pop and flash effect for collecting powerup tween(player, { scaleX: 1.18, scaleY: 1.18 }, { duration: 80, onFinish: function onFinish() { tween(player, { scaleX: 1, scaleY: 1 }, { duration: 120 }); } }); LK.effects.flashObject(player, 0xffff00, 180); powerups.splice(p, 1); continue; } } // Score increases with time if (Math.floor(survivalTime * 10) % 10 === 0) { var newScore = Math.floor(survivalTime); if (newScore > score) { score = newScore; scoreTxt.setText('Score: ' + score); } } }; // --- Game Over Reset --- LK.on('gameover', function () { resetGameVars(); }); // --- You Win (not used in endless, but for completeness) --- LK.on('youwin', function () { resetGameVars(); }); // --- Start Game Music --- LK.playMusic('Game-music');
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// --- Enemy Class ---
var Enemy = Container.expand(function () {
var self = Container.call(this);
// Attach enemy asset (image)
var enemyAsset = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = enemyAsset.width / 2;
self.speed = 3;
self.angle = 0;
self.hasSpawnedText = false; // Flag to track if this enemy already spawned text
self.update = function () {
// Store lastX, lastY for good practice (if needed for triggers)
if (typeof self.lastX === "undefined") self.lastX = self.x;
if (typeof self.lastY === "undefined") self.lastY = self.y;
// Move in direction of angle
self.x += Math.cos(self.angle) * self.speed * globalSpeedMultiplier;
self.y += Math.sin(self.angle) * self.speed * globalSpeedMultiplier;
// Update lastX, lastY
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// --- Enemy2 Class (Red, smaller, faster) ---
var Enemy2 = Container.expand(function () {
var self = Container.call(this);
// Attach enemy2 asset (image)
var enemyAsset = self.attachAsset('enemy2', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = enemyAsset.width / 2;
self.speed = 5; // Faster than regular enemy
self.angle = 0;
self.hasSpawnedText = false; // Flag to track if this enemy already spawned text
self.update = function () {
// Store lastX, lastY for good practice
if (typeof self.lastX === "undefined") self.lastX = self.x;
if (typeof self.lastY === "undefined") self.lastY = self.y;
// Move in direction of angle
self.x += Math.cos(self.angle) * self.speed * globalSpeedMultiplier;
self.y += Math.sin(self.angle) * self.speed * globalSpeedMultiplier;
// Update lastX, lastY
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// --- Enemy3 Class (Purple, wide, special movement) ---
var Enemy3 = Container.expand(function () {
var self = Container.call(this);
// Attach enemy3 asset (image)
var enemyAsset = self.attachAsset('enemy3', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = enemyAsset.width / 2;
self.speed = 2; // Slower but wider
self.angle = 0;
self.wobbleOffset = 0; // For special movement pattern
self.hasSpawnedText = false; // Flag to track if this enemy already spawned text
self.update = function () {
// Store lastX, lastY for good practice
if (typeof self.lastX === "undefined") self.lastX = self.x;
if (typeof self.lastY === "undefined") self.lastY = self.y;
// Special wobble movement
self.wobbleOffset += 0.1;
var wobbleX = Math.cos(self.wobbleOffset) * 50; // Side-to-side wobble
var wobbleY = Math.sin(self.wobbleOffset * 0.5) * 30; // Up-down wobble
// Move in direction of angle with wobble
self.x += Math.cos(self.angle) * self.speed * globalSpeedMultiplier + wobbleX * 0.02;
self.y += Math.sin(self.angle) * self.speed * globalSpeedMultiplier + wobbleY * 0.02;
// Update lastX, lastY
self.lastX = self.x;
self.lastY = self.y;
};
return self;
});
// --- Player Class ---
var Player = Container.expand(function () {
var self = Container.call(this);
// Attach player asset
var playerAsset = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// Set radius for collision
self.radius = playerAsset.width / 2;
// Flash effect on hit
self.flash = function () {
LK.effects.flashObject(self, 0xff0000, 600);
};
return self;
});
// --- Powerup Class ---
var Powerup = Container.expand(function () {
var self = Container.call(this);
// Attach powerup asset
var powerupAsset = self.attachAsset('powerup', {
anchorX: 0.5,
anchorY: 0.5
});
// Set radius for collision
self.radius = powerupAsset.width / 2;
return self;
});
// --- Trail Class ---
var Trail = Container.expand(function (assetId) {
var self = Container.call(this);
// Attach the same asset as the enemy
var trailAsset = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
// Start with some transparency
self.alpha = 0.6;
// Fade out over time using tween
tween(self, {
alpha: 0
}, {
duration: 700,
onFinish: function onFinish() {
if (self.parent) {
self.destroy();
}
}
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x181c24
});
/****
* Game Code
****/
// --- Game Variables ---
// --- Asset Initialization ---
// Red, smaller, faster
// Purple, wide, special
var player;
var enemies = [];
var powerups = [];
var trails = [];
var dragNode = null;
var lastPlayerPos = {
x: 0,
y: 0
};
var lastHit = false;
var score = 0;
var scoreTxt;
var survivalTime = 0;
var timeTxt;
var spawnTimer = 0;
var spawnInterval = 120; // frames (reduced from 150 to 120 for better balance)
var powerupTimer = 0;
var powerupInterval = 300; // frames (was 600, now twice as frequent)
var difficultyTimer = 0;
var minSpawnInterval = 60; // Increased from 30 to 60 to keep a reasonable minimum spawn rate
var gameStarted = false; // Control when gameplay begins
globalSpeedMultiplier = 1.0; // Reset speed multiplier
var globalSpeedMultiplier = 1.0; // Multiplier for all enemy speeds
var enemy2SpawnMultiplier = 1.0; // Multiplier for Enemy2 spawn chance
var enemy3SpawnMultiplier = 1.0; // Multiplier for Enemy3 spawn chance
var bestScore = storage.bestScore || 0; // Load best score from storage
var bestScoreTxt;
var dodgeSoundIndex = 0; // Track which dodge sound to play next
// --- UI ---
scoreTxt = new Text2('Score: 0', {
size: 100,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
timeTxt = new Text2('Time: 0.0', {
size: 70,
fill: 0xB0EAFF
});
timeTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(timeTxt);
timeTxt.y = 110;
bestScoreTxt = new Text2('Best: ' + bestScore, {
size: 60,
fill: 0x00FF00
});
bestScoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(bestScoreTxt);
bestScoreTxt.y = 180;
// Warning text about shuriken increase
var warningTxt = new Text2('Shurikens Will Increase Every 10 Seconds Be Careful!', {
size: 60,
fill: 0xFFFF00
});
warningTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(warningTxt);
warningTxt.y = 300;
// Fade out warning text after 3 seconds
tween(warningTxt, {
alpha: 0
}, {
duration: 3000,
onFinish: function onFinish() {
warningTxt.visible = false;
gameStarted = true; // Start the game when text fades out
}
});
// --- Player ---
player = new Player();
player.x = 2048 / 2;
player.y = 2732 / 2;
game.addChild(player);
// --- Helper Functions ---
function resetGameVars() {
// Check if current score is a new record before resetting
if (score > bestScore) {
bestScore = score;
storage.bestScore = bestScore; // Save to storage
}
enemies = [];
powerups = [];
trails = [];
score = 0;
survivalTime = 0;
spawnTimer = 0;
spawnInterval = 90;
powerupTimer = 0;
difficultyTimer = 0;
lastHit = false;
gameStarted = false; // Reset game start state
globalSpeedMultiplier = 1.0; // Reset speed multiplier
enemy2SpawnMultiplier = 1.0; // Reset Enemy2 spawn multiplier
enemy3SpawnMultiplier = 1.0; // Reset Enemy3 spawn multiplier
scoreTxt.setText('Score: 0');
timeTxt.setText('Time: 0.0');
bestScoreTxt.setText('Best: ' + bestScore);
player.x = 2048 / 2;
player.y = 2732 / 2;
// Show warning text again on reset
warningTxt.visible = true;
warningTxt.alpha = 1;
// Fade out warning text after 3 seconds
tween(warningTxt, {
alpha: 0
}, {
duration: 3000,
onFinish: function onFinish() {
warningTxt.visible = false;
gameStarted = true; // Start the game when text fades out
}
});
}
// --- Spawning ---
function spawnEnemy() {
// Spawn at random edge, move toward player
var enemy;
// After 10 seconds, start mixing in Enemy2, after 20 seconds add Enemy3
if (survivalTime >= 20) {
var r = Math.random();
var enemy2Chance = Math.min(0.5, 0.3 * enemy2SpawnMultiplier);
var enemy3Chance = Math.min(0.4, 0.2 * enemy3SpawnMultiplier);
var enemyChance = Math.max(0.1, 0.4 - (enemy2Chance - 0.3) - (enemy3Chance - 0.2));
if (r < enemyChance) {
enemy = new Enemy();
} else if (r < enemyChance + enemy2Chance) {
enemy = new Enemy2();
} else {
enemy = new Enemy3();
}
} else if (survivalTime >= 10) {
var r = Math.random();
var enemy2Chance = Math.min(0.7, 0.4 * enemy2SpawnMultiplier);
var enemyChance = Math.max(0.3, 0.6 - (enemy2Chance - 0.4));
if (r < enemyChance) {
enemy = new Enemy();
} else {
enemy = new Enemy2();
}
} else {
enemy = new Enemy();
}
var edge = Math.floor(Math.random() * 4);
var x, y;
if (edge === 0) {
// Top
x = 200 + Math.random() * (2048 - 400);
y = -80;
} else if (edge === 1) {
// Bottom
x = 200 + Math.random() * (2048 - 400);
y = 2732 + 80;
} else if (edge === 2) {
// Left
x = -80;
y = 200 + Math.random() * (2732 - 400);
} else {
// Right
x = 2048 + 80;
y = 200 + Math.random() * (2732 - 400);
}
enemy.x = x;
enemy.y = y;
// Track spawn time for trail duration control
enemy.spawnTime = LK.ticks;
// Aim at player
var dx = player.x - x;
var dy = player.y - y;
enemy.angle = Math.atan2(dy, dx);
if (typeof baseEnemySpeed === "undefined") {
baseEnemySpeed = 3;
}
// Only set speed for Enemy (default), others use their own
if (enemy instanceof Enemy) {
enemy.speed = baseEnemySpeed + Math.random() * 1 + survivalTime / 40; // Reduced random from 2 to 1, survival bonus from /20 to /40
}
// Add more variety: after 30 seconds, occasionally spawn a random fast Enemy2 or a wobbly Enemy3
if (survivalTime >= 30 && Math.random() < 0.15) {
if (Math.random() < 0.5) {
enemy = new Enemy2();
enemy.speed += 2 + Math.random() * 2; // Super fast
} else {
enemy = new Enemy3();
enemy.speed += 1 + Math.random(); // Extra wobbly
}
enemy.x = x;
enemy.y = y;
enemy.spawnTime = LK.ticks;
enemy.angle = Math.atan2(dy, dx);
}
enemies.push(enemy);
game.addChild(enemy);
}
function spawnPowerup() {
var powerup = new Powerup();
powerup.x = 200 + Math.random() * (2048 - 400);
powerup.y = 200 + Math.random() * (2732 - 400);
powerups.push(powerup);
game.addChild(powerup);
}
// --- Collision ---
function circleIntersect(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt(dx * dx + dy * dy);
return dist < (a.radius + b.radius) * 0.85;
}
// --- Drag Controls ---
function handleMove(x, y, obj) {
if (dragNode) {
// Only move player if dragging (i.e., finger/mouse is moving after down)
// Calculate movement delta from lastPlayerPos
if (lastPlayerPos.active) {
var dx = x - lastPlayerPos.x;
var dy = y - lastPlayerPos.y;
// Only move if there is a delta (prevents teleport on first touch)
if (dx !== 0 || dy !== 0) {
var newX = dragNode.x + dx;
var newY = dragNode.y + dy;
// Clamp to game area (avoid top left 100x100)
var px = Math.max(100 + player.radius, Math.min(2048 - player.radius, newX));
var py = Math.max(100 + player.radius, Math.min(2732 - player.radius, newY));
dragNode.x = px;
dragNode.y = py;
}
}
// Update lastPlayerPos for next move
lastPlayerPos.x = x;
lastPlayerPos.y = y;
lastPlayerPos.active = true;
}
}
game.move = handleMove;
game.down = function (x, y, obj) {
dragNode = player;
// Record initial drag position, but do not move player yet
lastPlayerPos.x = x;
lastPlayerPos.y = y;
lastPlayerPos.active = false; // Prevents teleport on first move
};
game.up = function (x, y, obj) {
dragNode = null;
lastPlayerPos.active = false;
};
// --- Main Update Loop ---
game.update = function () {
// Don't start gameplay until warning text has faded out
if (!gameStarted) {
return;
}
// Survival time
survivalTime += 1 / 60;
timeTxt.setText('Time: ' + survivalTime.toFixed(1));
// Difficulty scaling
difficultyTimer++;
if (difficultyTimer % 600 === 0) {
// Every 10 seconds, increase difficulty
// Decrease spawn interval (more frequent spawns)
if (spawnInterval > minSpawnInterval) {
spawnInterval -= 3; // Reduced from 8 to 3 for more gradual spawn rate increase
if (spawnInterval < minSpawnInterval) spawnInterval = minSpawnInterval;
}
// Increase number of enemies spawned per interval (only every other interval)
if (typeof enemiesPerSpawn === "undefined") {
enemiesPerSpawn = 1;
}
if (difficultyTimer % 1200 === 0) {
// Only increase every 20 seconds instead of 10
enemiesPerSpawn++;
}
// Increase base enemy speed
if (typeof baseEnemySpeed === "undefined") {
baseEnemySpeed = 3;
}
baseEnemySpeed += 0.3; // Reduced from 1.0 to 0.3 for more gradual speed increase
// Increase global speed multiplier for all enemies
globalSpeedMultiplier += 0.1; // Reduced from 0.3 to 0.1 for more gradual speed increase
// Increase Enemy2 and Enemy3 spawn rates
enemy2SpawnMultiplier += 0.2; // Reduced from 0.5 to 0.2
enemy3SpawnMultiplier += 0.15; // Reduced from 0.4 to 0.15
// Introduce new enemy types as time passes
// (handled in spawnEnemy, but you can add more logic here if needed)
}
// Enemy spawn
spawnTimer++;
if (spawnTimer >= spawnInterval) {
// Spawn multiple enemies per interval as difficulty increases
if (typeof enemiesPerSpawn === "undefined") {
enemiesPerSpawn = 1;
}
for (var s = 0; s < enemiesPerSpawn; s++) {
spawnEnemy();
}
spawnTimer = 0;
}
// Powerup spawn
powerupTimer++;
if (powerupTimer >= powerupInterval) {
// Spawn 2 powerups per interval for more frequent yellow balls
for (var i = 0; i < 2; i++) {
spawnPowerup();
}
powerupTimer = 0;
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var e = enemies[i];
e.update();
// Create trail only when enemy is moving faster than base speed AND within spawn time limits
var shouldCreateTrail = false;
var timeSinceSpawn = (LK.ticks - (e.spawnTime || 0)) / 60; // Convert to seconds
// Enemy has 2.25 seconds, Enemy2 and Enemy3 have 1 second
var trailTimeLimit = e instanceof Enemy ? 2.25 : 1.0;
if (timeSinceSpawn <= trailTimeLimit) {
if (e instanceof Enemy && e.speed > baseEnemySpeed + 1) {
shouldCreateTrail = true;
} else if (e instanceof Enemy2 && e.speed > 5) {
shouldCreateTrail = true;
} else if (e instanceof Enemy3 && e.speed > 2) {
shouldCreateTrail = true;
}
}
if (shouldCreateTrail && LK.ticks % 8 === 0) {
var assetId = 'enemy'; // Default
if (e instanceof Enemy2) assetId = 'enemy2';
if (e instanceof Enemy3) assetId = 'enemy3';
var trail = new Trail(assetId);
trail.x = e.x;
trail.y = e.y;
trail.rotation = e.rotation || 0;
trails.push(trail);
game.addChild(trail);
}
// Remove if off screen
if (e.x < -200 || e.x > 2248 || e.y < -200 || e.y > 2932) {
e.destroy();
enemies.splice(i, 1);
continue;
}
// Check for close calls (near misses)
var dx = e.x - player.x;
var dy = e.y - player.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var closeCallDistance = (e.radius + player.radius) * 1.5; // 1.5x collision distance
// Initialize lastWasClose if not set
if (typeof e.lastWasClose === "undefined") e.lastWasClose = false;
var isCloseNow = distance <= closeCallDistance;
// Trigger close call effect when transitioning from not close to close, but only once per enemy
if (!e.lastWasClose && isCloseNow && !circleIntersect(e, player) && !e.hasSpawnedText) {
// Create encouraging text
var messages = ['Good!', 'Wow!', 'Excellent!', 'Nice!', 'Amazing!'];
var message = messages[Math.floor(Math.random() * messages.length)];
var encourageText = new Text2(message, {
size: 120,
fill: 0x00FF00
});
encourageText.anchor.set(0.5, 0.5);
encourageText.x = player.x + (Math.random() - 0.5) * 200; // Random offset around player
encourageText.y = player.y + (Math.random() - 0.5) * 200;
// Ensure text stays within screen bounds
encourageText.x = Math.max(100, Math.min(2048 - 100, encourageText.x));
encourageText.y = Math.max(100, Math.min(2732 - 100, encourageText.y));
game.addChild(encourageText);
// Animate text: pop up, then fade out
encourageText.scaleX = 0.1;
encourageText.scaleY = 0.1;
encourageText.alpha = 1;
// --- Bonus Points and Bonus Text ---
score += 10;
scoreTxt.setText('Score: ' + score);
// Show "Bonus Point +10" text near the score table (top center)
var bonusText = new Text2("Bonus Point +10", {
size: 80,
fill: 0xFFD700
});
bonusText.anchor.set(0.5, 0);
bonusText.x = scoreTxt.x;
bonusText.y = scoreTxt.y + scoreTxt.height + 10;
LK.gui.top.addChild(bonusText);
bonusText.alpha = 1;
bonusText.scaleX = 0.7;
bonusText.scaleY = 0.7;
tween(bonusText, {
scaleX: 1.1,
scaleY: 1.1,
y: bonusText.y - 30
}, {
duration: 180,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(bonusText, {
alpha: 0,
y: bonusText.y - 30
}, {
duration: 600,
onFinish: function onFinish() {
if (bonusText.parent) bonusText.destroy();
}
});
}
});
// Mark this enemy as having spawned text
e.hasSpawnedText = true;
// Play dodge sound effect - alternate between two sounds
var soundName = dodgeSoundIndex === 0 ? 'dodge' : 'dodge2';
LK.getSound(soundName).play();
dodgeSoundIndex = (dodgeSoundIndex + 1) % 2; // Alternate between 0 and 1
// Player pop and flash effect for juicy feedback
tween(player, {
scaleX: 1.25,
scaleY: 1.25
}, {
duration: 80,
onFinish: function onFinish() {
tween(player, {
scaleX: 1,
scaleY: 1
}, {
duration: 120
});
}
});
LK.effects.flashObject(player, 0x00ffcc, 200);
// First tween: pop up
tween(encourageText, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 200,
easing: tween.easeOut,
onFinish: function onFinish() {
// Second tween: fade out
tween(encourageText, {
alpha: 0,
scaleX: 0.8,
scaleY: 0.8,
y: encourageText.y - 50
}, {
duration: 800,
easing: tween.easeIn,
onFinish: function onFinish() {
if (encourageText.parent) {
encourageText.destroy();
}
}
});
}
});
}
// Update last close state
e.lastWasClose = isCloseNow;
// Collision with player
if (circleIntersect(e, player)) {
if (!lastHit) {
// Player hit: pop and flash effect
tween(player, {
scaleX: 1.4,
scaleY: 1.4
}, {
duration: 100,
onFinish: function onFinish() {
tween(player, {
scaleX: 1,
scaleY: 1
}, {
duration: 180
});
}
});
player.flash();
LK.effects.flashScreen(0xff0000, 600);
// Update best score before showing game over
if (score > bestScore) {
bestScore = score;
storage.bestScore = bestScore; // Save to storage
bestScoreTxt.setText('Best: ' + bestScore);
LK.showGameOver('New Record: ' + score);
} else {
LK.showGameOver();
}
lastHit = true;
return;
}
}
}
// Clean up destroyed trails
for (var t = trails.length - 1; t >= 0; t--) {
var trail = trails[t];
if (!trail.parent || trail.alpha <= 0) {
if (trail.parent) trail.destroy();
trails.splice(t, 1);
}
}
// Remove dead enemies if game over
if (lastHit) {
for (var j = 0; j < enemies.length; j++) {
enemies[j].destroy();
}
enemies = [];
for (var k = 0; k < powerups.length; k++) {
powerups[k].destroy();
}
powerups = [];
for (var l = 0; l < trails.length; l++) {
trails[l].destroy();
}
trails = [];
return;
}
// Update powerups
for (var p = powerups.length - 1; p >= 0; p--) {
var pu = powerups[p];
// Collision with player
if (circleIntersect(pu, player)) {
score += 10;
scoreTxt.setText('Score: ' + score);
// Create pop effect before destroying powerup
tween(pu, {
scaleX: 2.5,
scaleY: 2.5,
alpha: 0
}, {
duration: 300,
easing: tween.easeOut,
onFinish: function onFinish() {
if (pu.parent) {
pu.destroy();
}
}
});
// Player pop and flash effect for collecting powerup
tween(player, {
scaleX: 1.18,
scaleY: 1.18
}, {
duration: 80,
onFinish: function onFinish() {
tween(player, {
scaleX: 1,
scaleY: 1
}, {
duration: 120
});
}
});
LK.effects.flashObject(player, 0xffff00, 180);
powerups.splice(p, 1);
continue;
}
}
// Score increases with time
if (Math.floor(survivalTime * 10) % 10 === 0) {
var newScore = Math.floor(survivalTime);
if (newScore > score) {
score = newScore;
scoreTxt.setText('Score: ' + score);
}
}
};
// --- Game Over Reset ---
LK.on('gameover', function () {
resetGameVars();
});
// --- You Win (not used in endless, but for completeness) ---
LK.on('youwin', function () {
resetGameVars();
});
// --- Start Game Music ---
LK.playMusic('Game-music');