User prompt
I'm working on a mobile rhythm game made with JavaScript and Upit Engine. The game has red, green, and black orbs that fall vertically from the top. At the bottom of the screen, there’s a targetZone where players must tap the orbs to score. There’s also a character (ninja, wizard, or swordmaster) with a special ability. Please help fix and improve the following issues in my current code: Orbs are not clickable in the target zone. 👉 Fix: Orbs should only be tappable when they are within the targetZone rectangle. Make sure the click detection is constrained to that area (X and Y bounds). Character ability is triggering when tapping the target zone. 👉 Fix: Ability should only activate when tapping directly on the character sprite, not when tapping the targetZone or screen. Swordmaster’s clone ability is not working. 👉 Fix: The clone spawns correctly, but it does not auto-hit orbs. Make the clone auto-hit the nearest orb inside the targetZone every 100ms for 1 second. TargetZone visibility is too low. 👉 Fix: Increase the visibility (e.g., set alpha to 0.5) and optionally give it a clearer color like dark blue for better UX. Additional Notes: Orbs fall straight down (this part is already correct). The game runs at ~60 FPS. The code is written in Upit’s framework with Container, LK.init.shape, and LK.getAsset. Please fix these problems and give me a corrected code block that I can directly paste into Upit Studio. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please update the game logic for a more intuitive tap-timing rhythm style: 1. Modify the orb movement: - All orbs should fall straight down from top to bottom (no target-based vector movement). - Remove all references to orb.targetX and orb.targetY. - Instead, orbs just increase their y position by `orb.speed` each frame. 2. Modify the target zone: - Make the targetZone much taller (e.g., height: 300) and stretch it across the full bottom of the screen. - Place the targetZone at the bottom of the screen (e.g., y: 2300 or 2400 depending on canvas size). 3. Modify the hit detection: - Players can only successfully hit orbs if they are within the targetZone bounds. - Tapping anywhere outside the zone does nothing. - Taps must be on orbs that are inside the vertical range of the targetZone (collision check). 4. Update swordmaster's clone AI: - The clone should only auto-hit orbs when they are inside the targetZone. - The clone should behave like a player: look for orbs inside the zone and auto-hit one every 100ms. 5. Bonus: Make orb speed slightly slower (e.g., speed: 4). This turns the game into a clearer rhythm-timing challenge like Friday Night Funkin' or Piano Tiles. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
In this Upit rhythm game code, please change the swordmaster character's ability. Instead of "dual strike", make it so that when the swordmaster uses their ability, it spawns a temporary clone of themselves. This clone should appear next to the original swordmaster, automatically hit orbs for 1 second (as if it perfectly taps the correct ones), and then disappear. The clone should visually look like the swordmaster and help the player for 1 second. Update only the related parts (the ability and orb interaction logic), and do not affect other characters. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
In this Upit game code, the character selection screen shows the characters (ninja, wizard, swordmaster), but tapping on them doesn’t trigger the selection function. Please fix the input detection so that tapping on the characters correctly selects them using selectCharacter(type) as intended. Do not change unrelated parts of the game.
User prompt
Redesign the gameplay to be more like Beat Saber. - Orbs should move toward the player from the top (not lanes) - Spawning should match BPM of the background music - Tapping or swiping should break orbs (use color to determine side or action) - Add simple feedback effects like screen shake or color flash - Allow player to use abilities like time-slow or auto-hit You can refactor the orb system and remove lane dependency if needed. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Scene 1 – Character Selection Screen: The player starts in a mystical dojo-like space with soft ambient music playing. On screen are 3 animated character avatars. Player must tap on one to begin the rhythm game. Character Options (Selectable): 🔹 Blue Mage • Appearance: Tall figure wearing a glowing blue robe, wide wizard hat, and holding a crystal staff. Mystical blue eyes, long white beard. • Animation: Calm idle floating motion, magic particles flowing around • Ability: Can cast “Ice Freeze” every 10 seconds to slow down incoming beat orbs for 3 seconds • Difficulty: Medium • Tagline: “Slow the rhythm, control the chaos.” 🔴 Dual Blade Swordmaster • Appearance: White torn martial arts outfit, red belt, bandaged arms, wild hair, two glowing swords • Animation: Breathing heavily, does quick sword swings while idle • Ability: Can slash both left and right lanes at the same time (multi-touch bonus), 2x combo window • Difficulty: Hard • Tagline: “Two blades. One rhythm.” ⚫ Shadow Ninja • Appearance: Full black suit, masked face, glowing dagger in hand, red eyes • Animation: Quick shadow dashes, fades in/out • Ability: Increased tap/slash speed; auto-slice chain combo when timing is perfect • Difficulty: Easy to pick, hard to master • Tagline: “Strike with silence. Dance with speed.” Gameplay Transition: Once the player selects a character, fade-in transition with music rising leads into rhythm gameplay arena (Scene 2). The player’s class ability is activated and UI adapts accordingly.
User prompt
Scene 1 – Character Selection Screen: The player starts in a mystical dojo-like space with soft ambient music playing. On screen are 3 animated character avatars. Player must tap on one to begin the rhythm game. Character Options (Selectable): 🔹 Blue Mage • Appearance: Tall figure wearing a glowing blue robe, wide wizard hat, and holding a crystal staff. Mystical blue eyes, long white beard. • Animation: Calm idle floating motion, magic particles flowing around • Ability: Can cast “Ice Freeze” every 10 seconds to slow down incoming beat orbs for 3 seconds • Difficulty: Medium • Tagline: “Slow the rhythm, control the chaos.” 🔴 Dual Blade Swordmaster • Appearance: White torn martial arts outfit, red belt, bandaged arms, wild hair, two glowing swords • Animation: Breathing heavily, does quick sword swings while idle • Ability: Can slash both left and right lanes at the same time (multi-touch bonus), 2x combo window • Difficulty: Hard • Tagline: “Two blades. One rhythm.” ⚫ Shadow Ninja • Appearance: Full black suit, masked face, glowing dagger in hand, red eyes • Animation: Quick shadow dashes, fades in/out • Ability: Increased tap/slash speed; auto-slice chain combo when timing is perfect • Difficulty: Easy to pick, hard to master • Tagline: “Strike with silence. Dance with speed.” Gameplay Transition: Once the player selects a character, fade-in transition with music rising leads into rhythm gameplay arena (Scene 2). The player’s class ability is activated and UI adapts accordingly. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Scene 1 – Character Selection Screen: The player starts in a mystical dojo-like space with soft ambient music playing. On screen are 3 animated character avatars. Player must tap on one to begin the rhythm game. Character Options (Selectable): 🔹 Blue Mage • Appearance: Tall figure wearing a glowing blue robe, wide wizard hat, and holding a crystal staff. Mystical blue eyes, long white beard. • Animation: Calm idle floating motion, magic particles flowing around • Ability: Can cast “Ice Freeze” every 10 seconds to slow down incoming beat orbs for 3 seconds • Difficulty: Medium • Tagline: “Slow the rhythm, control the chaos.” 🔴 Dual Blade Swordmaster • Appearance: White torn martial arts outfit, red belt, bandaged arms, wild hair, two glowing swords • Animation: Breathing heavily, does quick sword swings while idle • Ability: Can slash both left and right lanes at the same time (multi-touch bonus), 2x combo window • Difficulty: Hard • Tagline: “Two blades. One rhythm.” ⚫ Shadow Ninja • Appearance: Full black suit, masked face, glowing dagger in hand, red eyes • Animation: Quick shadow dashes, fades in/out • Ability: Increased tap/slash speed; auto-slice chain combo when timing is perfect • Difficulty: Easy to pick, hard to master • Tagline: “Strike with silence. Dance with speed.” Gameplay Transition: Once the player selects a character, fade-in transition with music rising leads into rhythm gameplay arena (Scene 2). The player’s class ability is activated and UI adapts accordingly.
User prompt
Scene 1 – Character Selection Screen: The player starts in a mystical dojo-like space with soft ambient music playing. On screen are 3 animated character avatars. Player must tap on one to begin the rhythm game. Character Options (Selectable): 🔹 Blue Mage • Appearance: Tall figure wearing a glowing blue robe, wide wizard hat, and holding a crystal staff. Mystical blue eyes, long white beard. • Animation: Calm idle floating motion, magic particles flowing around • Ability: Can cast “Ice Freeze” every 10 seconds to slow down incoming beat orbs for 3 seconds • Difficulty: Medium • Tagline: “Slow the rhythm, control the chaos.” 🔴 Dual Blade Swordmaster • Appearance: White torn martial arts outfit, red belt, bandaged arms, wild hair, two glowing swords • Animation: Breathing heavily, does quick sword swings while idle • Ability: Can slash both left and right lanes at the same time (multi-touch bonus), 2x combo window • Difficulty: Hard • Tagline: “Two blades. One rhythm.” ⚫ Shadow Ninja • Appearance: Full black suit, masked face, glowing dagger in hand, red eyes • Animation: Quick shadow dashes, fades in/out • Ability: Increased tap/slash speed; auto-slice chain combo when timing is perfect • Difficulty: Easy to pick, hard to master • Tagline: “Strike with silence. Dance with speed.” Gameplay Transition: Once the player selects a character, fade-in transition with music rising leads into rhythm gameplay arena (Scene 2). The player’s class ability is activated and UI adapts accordingly. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Rhythm Warriors: Beat Battle Arena
Initial prompt
Game Type: Music rhythm-based action game with fantasy RPG elements Visual Style: 3D stylized / vibrant / slightly cartoonish Theme: Players choose one of 3 classes — Ninja, Wizard, or Swordmaster — and enter a rhythm battle arena where colorful beat orbs come toward the player in sync with popular music tracks. Mechanics: • Notes come at the player in 3 lanes, color-coded: • 🔴 Red = hit left side • 🟢 Green = hit right side • ⚫ Black = bomb — must be dodged or ignored (touch = instant fail) • Orbs move faster as music progresses • Player interacts by tapping or slashing based on character’s ability Classes & Abilities: • Ninja: Fast dashes, quicker tap/slash speed, special move: “Shadow Slice” (auto-hits a fast series of beats) • Wizard: Normal speed, casts freeze spell every 10 seconds to slow incoming beats for 3 seconds • Swordmaster: Dual-wielding – can tap both left and right lanes at once (multi-touch), higher combo multiplier Game Objective: • Survive the full duration of the song without hitting bombs • Score is based on accuracy, combo, and class skill use • Unlock skins and new songs after each win Popular Track Suggestions: • “Blinding Lights” by The Weeknd • “Levitating” by Dua Lipa • “Believer” by Imagine Dragons • “Bad Guy” by Billie Eilish Interface Notes: • Color-coded lanes with beatballs moving toward player • Touch screen input or FaceKit interaction (head tilt to dodge bombs?) • Energy bar depletes if notes missed • Flashy effects for class abilities, visual feedback on perfect/good/miss
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var BeatOrb = Container.expand(function (orbType) { var self = Container.call(this); self.orbType = orbType; self.speed = 6; self.hit = false; self.targetX = 2048 / 2; // Move toward center of screen self.targetY = 2200; // Target zone Y position var orbGraphics = self.attachAsset(orbType, { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { var speedMultiplier = 1; if (selectedCharacter && selectedCharacter.freezeActive) { speedMultiplier = 0.3; } // Move toward target position (Beat Saber style) var dx = self.targetX - self.x; var dy = self.targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 0) { self.x += dx / distance * self.speed * speedMultiplier; self.y += dy / distance * self.speed * speedMultiplier; } }; return self; }); var Character = Container.expand(function (characterType) { var self = Container.call(this); self.type = characterType; self.abilityReady = true; self.abilityCooldown = 0; self.freezeActive = false; self.freezeTime = 0; self.baseY = 0; self.floatOffset = 0; self.animSpeed = 0.05 + Math.random() * 0.03; var characterGraphics = self.attachAsset(characterType, { anchorX: 0.5, anchorY: 0.5 }); self.useAbility = function () { if (!self.abilityReady) return false; LK.getSound('ability').play(); if (self.type === 'ninja') { // Shadow Slice: Auto-hit next 3 orbs comboMode = 3; LK.effects.flashObject(self, 0x9932cc, 500); } else if (self.type === 'wizard') { // Time Freeze: Slow orbs for 3 seconds self.freezeActive = true; self.freezeTime = 3000; LK.effects.flashObject(self, 0x00ffff, 3000); } else if (self.type === 'swordmaster') { // Clone Ability: Spawn temporary clone that auto-hits orbs spawnSwordmasterClone(); LK.effects.flashObject(self, 0xffd700, 1000); } self.abilityReady = false; self.abilityCooldown = 10000; // 10 second cooldown return true; }; self.update = function () { if (!self.abilityReady) { self.abilityCooldown -= 16; // ~60fps if (self.abilityCooldown <= 0) { self.abilityReady = true; } } if (self.freezeActive) { self.freezeTime -= 16; if (self.freezeTime <= 0) { self.freezeActive = false; } } // Floating animation for character selection if (gameState === 'classSelection') { self.floatOffset += self.animSpeed; self.y = self.baseY + Math.sin(self.floatOffset) * 20; // Add mystical particle effect for wizard if (self.type === 'wizard' && LK.ticks % 20 === 0) { LK.effects.flashObject(self, 0x4169e1, 400); } // Add shadow dash effect for ninja if (self.type === 'ninja' && LK.ticks % 30 === 0) { LK.effects.flashObject(self, 0x9932cc, 300); } // Add sword gleam for swordmaster if (self.type === 'swordmaster' && LK.ticks % 25 === 0) { LK.effects.flashObject(self, 0xffd700, 350); } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a2e }); /**** * Game Code ****/ // Game state var gameState = 'classSelection'; // 'classSelection', 'playing', 'gameOver' var selectedCharacter = null; var score = 0; var combo = 0; var maxCombo = 0; var lives = 3; var comboMode = 0; // Ninja ability counter var dualStrikeActive = false; // Swordmaster ability var swordmasterClone = null; var cloneActiveTime = 0; var songProgress = 0; var songDuration = 120000; // 2 minutes // Game objects var orbs = []; var lanes = []; var characters = {}; var targetZone = null; // UI elements var scoreText = null; var comboText = null; var livesText = null; var abilityText = null; var instructionText = null; var ninjaDescText = null; var wizardDescText = null; var swordmasterDescText = null; var titleText = null; // Target position for Beat Saber style var targetY = 2200; var lastSpawnTime = 0; var spawnInterval = 500; // Base spawn interval in ms // Start ambient music for character selection LK.playMusic('ambient', { volume: 0.6 }); // Target zone targetZone = game.addChild(LK.getAsset('targetZone', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: targetY, alpha: 0.3 })); // Create character selection characters.ninja = game.addChild(new Character('ninja')); characters.ninja.x = 2048 * 0.25; characters.ninja.y = 1000; characters.ninja.baseY = 1000; characters.wizard = game.addChild(new Character('wizard')); characters.wizard.x = 2048 * 0.5; characters.wizard.y = 1000; characters.wizard.baseY = 1000; characters.swordmaster = game.addChild(new Character('swordmaster')); characters.swordmaster.x = 2048 * 0.75; characters.swordmaster.y = 1000; characters.swordmaster.baseY = 1000; // UI Setup scoreText = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreText.anchor.set(0, 0); LK.gui.topLeft.addChild(scoreText); scoreText.x = 120; scoreText.y = 20; comboText = new Text2('Combo: 0', { size: 50, fill: 0xFFFF00 }); comboText.anchor.set(0.5, 0); LK.gui.top.addChild(comboText); comboText.y = 80; livesText = new Text2('Lives: 3', { size: 50, fill: 0xFF4444 }); livesText.anchor.set(1, 0); LK.gui.topRight.addChild(livesText); livesText.x = -20; livesText.y = 20; abilityText = new Text2('Ability Ready!', { size: 40, fill: 0x00FF00 }); abilityText.anchor.set(0.5, 1); LK.gui.bottom.addChild(abilityText); abilityText.y = -100; // Title text titleText = new Text2('RHYTHM WARRIORS\nBeat Battle Arena', { size: 80, fill: 0xFFD700 }); titleText.anchor.set(0.5, 0); LK.gui.top.addChild(titleText); titleText.y = 200; // Character descriptions ninjaDescText = new Text2('⚫ SHADOW NINJA\n"Strike with silence. Dance with speed."\nAbility: Shadow Slice auto-combo\nDifficulty: Easy to pick, hard to master', { size: 35, fill: 0x9932cc }); ninjaDescText.anchor.set(0.5, 0); game.addChild(ninjaDescText); ninjaDescText.x = 2048 * 0.25; ninjaDescText.y = 1200; wizardDescText = new Text2('🔵 BLUE MAGE\n"Slow the rhythm, control the chaos."\nAbility: Ice Freeze slows orbs\nDifficulty: Medium', { size: 35, fill: 0x4169e1 }); wizardDescText.anchor.set(0.5, 0); game.addChild(wizardDescText); wizardDescText.x = 2048 * 0.5; wizardDescText.y = 1200; swordmasterDescText = new Text2('⚔️ DUAL BLADE SWORDMASTER\n"Two blades. One rhythm."\nAbility: Multi-touch dual strike\nDifficulty: Hard', { size: 35, fill: 0x8b4513 }); swordmasterDescText.anchor.set(0.5, 0); game.addChild(swordmasterDescText); swordmasterDescText.x = 2048 * 0.75; swordmasterDescText.y = 1200; instructionText = new Text2('TAP A CHARACTER TO BEGIN', { size: 50, fill: 0xFFFFFF }); instructionText.anchor.set(0.5, 1); LK.gui.bottom.addChild(instructionText); instructionText.y = -50; // Input handling game.down = function (x, y, obj) { if (gameState === 'classSelection') { // Check character selection using bounds checking var ninjaLeft = characters.ninja.x - 60; var ninjaRight = characters.ninja.x + 60; var ninjaTop = characters.ninja.y - 60; var ninjaBottom = characters.ninja.y + 60; if (x >= ninjaLeft && x <= ninjaRight && y >= ninjaTop && y <= ninjaBottom) { selectCharacter('ninja'); } else { var wizardLeft = characters.wizard.x - 60; var wizardRight = characters.wizard.x + 60; var wizardTop = characters.wizard.y - 60; var wizardBottom = characters.wizard.y + 60; if (x >= wizardLeft && x <= wizardRight && y >= wizardTop && y <= wizardBottom) { selectCharacter('wizard'); } else { var swordmasterLeft = characters.swordmaster.x - 60; var swordmasterRight = characters.swordmaster.x + 60; var swordmasterTop = characters.swordmaster.y - 60; var swordmasterBottom = characters.swordmaster.y + 60; if (x >= swordmasterLeft && x <= swordmasterRight && y >= swordmasterTop && y <= swordmasterBottom) { selectCharacter('swordmaster'); } } } } else if (gameState === 'playing') { // Handle orb hits and abilities if (y > 2000) { // Ability zone if (selectedCharacter && selectedCharacter.abilityReady) { selectedCharacter.useAbility(); updateAbilityText(); } } else { // Determine hit type based on tap position (Beat Saber style) var hitType = x < 2048 * 0.5 ? 'red' : 'green'; handleOrbHit(x, y, hitType); } } }; function selectCharacter(type) { selectedCharacter = characters[type]; gameState = 'playing'; // Hide character descriptions and selection UI titleText.visible = false; ninjaDescText.visible = false; wizardDescText.visible = false; swordmasterDescText.visible = false; instructionText.visible = false; // Hide unselected characters for (var charType in characters) { if (charType !== type) { characters[charType].visible = false; } } // Position selected character selectedCharacter.x = 2048 / 2; selectedCharacter.y = 2400; // Character-specific selection effects if (type === 'ninja') { LK.effects.flashScreen(0x9932cc, 800); } else if (type === 'wizard') { LK.effects.flashScreen(0x4169e1, 800); } else if (type === 'swordmaster') { LK.effects.flashScreen(0xffd700, 800); } // Start music and orb spawning LK.playMusic('bgtrack'); } function handleOrbHit(tapX, tapY, expectedType) { var hitOrb = null; var bestDistance = Infinity; // Find closest orb near tap position (Beat Saber style) for (var i = 0; i < orbs.length; i++) { var orb = orbs[i]; if (!orb.hit) { var dx = orb.x - tapX; var dy = orb.y - tapY; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 120 && distance < bestDistance) { hitOrb = orb; bestDistance = distance; } } } if (hitOrb) { if (hitOrb.orbType === 'blackBomb') { // Hit a bomb - lose life lives--; combo = 0; LK.getSound('bomb').play(); LK.effects.flashScreen(0xff0000, 300); hitOrb.hit = true; if (lives <= 0) { endGame(); return; } } else if (hitOrb.orbType === 'redOrb' && expectedType === 'red' || hitOrb.orbType === 'greenOrb' && expectedType === 'green' || comboMode > 0) { // Successful hit var points = 100; if (comboMode > 0) { comboMode--; points *= 2; // Ninja bonus } if (dualStrikeActive) { points *= 2; // Swordmaster bonus dualStrikeActive = false; } combo++; if (combo > maxCombo) maxCombo = combo; // Combo multiplier points *= Math.min(combo, 10); score += points; LK.getSound('hit').play(); // Enhanced visual feedback for Beat Saber style var hitColor = hitOrb.orbType === 'redOrb' ? 0xff4444 : 0x44ff44; LK.effects.flashScreen(hitColor, 150); tween(hitOrb, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 300 }); hitOrb.hit = true; } else { // Wrong color hit combo = 0; LK.getSound('miss').play(); } } else { // Missed - no orb in range combo = 0; LK.getSound('miss').play(); } updateUI(); } function spawnSwordmasterClone() { if (swordmasterClone) return; // Only one clone at a time swordmasterClone = game.addChild(LK.getAsset('swordmaster', { anchorX: 0.5, anchorY: 0.5, x: selectedCharacter.x + 150, y: selectedCharacter.y, alpha: 0.7 })); cloneActiveTime = 1000; // 1 second // Flash effect for clone spawn LK.effects.flashObject(swordmasterClone, 0xffd700, 300); } function spawnOrb() { var orbTypes = ['redOrb', 'greenOrb', 'blackBomb']; var weights = [0.4, 0.4, 0.2]; // 40% red, 40% green, 20% bomb var rand = Math.random(); var orbType = 'redOrb'; if (rand < weights[0]) orbType = 'redOrb';else if (rand < weights[0] + weights[1]) orbType = 'greenOrb';else orbType = 'blackBomb'; var orb = new BeatOrb(orbType); // Spawn from random position at top of screen orb.x = 400 + Math.random() * 1248; // Random X within safe bounds orb.y = -100; // Start above screen orbs.push(orb); game.addChild(orb); // Add spawn effect LK.effects.flashObject(orb, 0xffffff, 200); } function autoHitOrbWithClone() { if (!swordmasterClone) return; // Find closest non-bomb orb to auto-hit var closestOrb = null; var closestDistance = Infinity; for (var i = 0; i < orbs.length; i++) { var orb = orbs[i]; if (!orb.hit && orb.orbType !== 'blackBomb') { var dx = orb.x - swordmasterClone.x; var dy = orb.y - swordmasterClone.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 200 && distance < closestDistance) { closestOrb = orb; closestDistance = distance; } } } // Auto-hit the closest orb if (closestOrb) { var points = 150; // Clone hits give bonus points combo++; if (combo > maxCombo) maxCombo = combo; points *= Math.min(combo, 10); score += points; LK.getSound('hit').play(); // Visual feedback var hitColor = closestOrb.orbType === 'redOrb' ? 0xff4444 : 0x44ff44; LK.effects.flashScreen(hitColor, 100); tween(closestOrb, { scaleX: 1.5, scaleY: 1.5, alpha: 0 }, { duration: 300 }); closestOrb.hit = true; updateUI(); } } function updateUI() { scoreText.setText('Score: ' + score); comboText.setText('Combo: ' + combo); livesText.setText('Lives: ' + lives); LK.setScore(score); } function updateAbilityText() { if (!selectedCharacter) return; if (selectedCharacter.abilityReady) { abilityText.setText('Ability Ready!'); abilityText.tint = 0x00ff00; } else { var cooldownSeconds = Math.ceil(selectedCharacter.abilityCooldown / 1000); abilityText.setText('Ability: ' + cooldownSeconds + 's'); abilityText.tint = 0xff4444; } } function endGame() { gameState = 'gameOver'; // Save high score var highScore = storage.highScore || 0; if (score > highScore) { storage.highScore = score; } // Save stats storage.gamesPlayed = (storage.gamesPlayed || 0) + 1; if (maxCombo > (storage.maxCombo || 0)) { storage.maxCombo = maxCombo; } LK.showGameOver(); } function checkWinCondition() { if (songProgress >= songDuration) { // Survived the full song! var bonusScore = lives * 1000 + maxCombo * 100; score += bonusScore; LK.setScore(score); // Save completion storage.songsCompleted = (storage.songsCompleted || 0) + 1; LK.showYouWin(); } } // Main game update loop game.update = function () { if (gameState !== 'playing') return; songProgress += 16; // ~60fps // Update character abilities if (selectedCharacter) { selectedCharacter.update(); updateAbilityText(); } // Update swordmaster clone if (swordmasterClone && cloneActiveTime > 0) { cloneActiveTime -= 16; // ~60fps // Clone auto-hits orbs every 100ms if (LK.ticks % 6 === 0) { // Every ~100ms at 60fps autoHitOrbWithClone(); } // Remove clone when time expires if (cloneActiveTime <= 0) { tween(swordmasterClone, { alpha: 0, scaleX: 0.5, scaleY: 0.5 }, { duration: 300, onFinish: function onFinish() { if (swordmasterClone) { swordmasterClone.destroy(); swordmasterClone = null; } } }); } } // Spawn orbs based on BPM timing (Beat Saber style) var currentTime = Date.now(); if (currentTime - lastSpawnTime > spawnInterval) { spawnOrb(); lastSpawnTime = currentTime; // Gradually increase spawn rate if (spawnInterval > 300) { spawnInterval -= 2; } } // Update orbs for (var i = orbs.length - 1; i >= 0; i--) { var orb = orbs[i]; // Remove hit orbs after animation if (orb.hit) { orb.alpha -= 0.1; if (orb.alpha <= 0) { orb.destroy(); orbs.splice(i, 1); } continue; } // Check if orb reached target or passed it (Beat Saber style) var dx = orb.x - orb.targetX; var dy = orb.y - orb.targetY; var distanceToTarget = Math.sqrt(dx * dx + dy * dy); if (distanceToTarget < 30) { if (orb.orbType === 'blackBomb') { // Bomb reached target zone - lose life lives--; combo = 0; LK.getSound('bomb').play(); LK.effects.flashScreen(0xff0000, 500); } else { // Missed a good orb combo = 0; LK.getSound('miss').play(); } orb.destroy(); orbs.splice(i, 1); if (lives <= 0) { endGame(); return; } updateUI(); continue; } else if (orb.y > 2800) { // Orb went off screen orb.destroy(); orbs.splice(i, 1); continue; } } // Check win condition checkWinCondition(); };
===================================================================
--- original.js
+++ change.js
@@ -60,10 +60,10 @@
self.freezeActive = true;
self.freezeTime = 3000;
LK.effects.flashObject(self, 0x00ffff, 3000);
} else if (self.type === 'swordmaster') {
- // Dual Strike: Next hit scores double
- dualStrikeActive = true;
+ // Clone Ability: Spawn temporary clone that auto-hits orbs
+ spawnSwordmasterClone();
LK.effects.flashObject(self, 0xffd700, 1000);
}
self.abilityReady = false;
self.abilityCooldown = 10000; // 10 second cooldown
@@ -121,8 +121,10 @@
var maxCombo = 0;
var lives = 3;
var comboMode = 0; // Ninja ability counter
var dualStrikeActive = false; // Swordmaster ability
+var swordmasterClone = null;
+var cloneActiveTime = 0;
var songProgress = 0;
var songDuration = 120000; // 2 minutes
// Game objects
var orbs = [];
@@ -377,8 +379,21 @@
LK.getSound('miss').play();
}
updateUI();
}
+function spawnSwordmasterClone() {
+ if (swordmasterClone) return; // Only one clone at a time
+ swordmasterClone = game.addChild(LK.getAsset('swordmaster', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: selectedCharacter.x + 150,
+ y: selectedCharacter.y,
+ alpha: 0.7
+ }));
+ cloneActiveTime = 1000; // 1 second
+ // Flash effect for clone spawn
+ LK.effects.flashObject(swordmasterClone, 0xffd700, 300);
+}
function spawnOrb() {
var orbTypes = ['redOrb', 'greenOrb', 'blackBomb'];
var weights = [0.4, 0.4, 0.2]; // 40% red, 40% green, 20% bomb
var rand = Math.random();
@@ -392,8 +407,47 @@
game.addChild(orb);
// Add spawn effect
LK.effects.flashObject(orb, 0xffffff, 200);
}
+function autoHitOrbWithClone() {
+ if (!swordmasterClone) return;
+ // Find closest non-bomb orb to auto-hit
+ var closestOrb = null;
+ var closestDistance = Infinity;
+ for (var i = 0; i < orbs.length; i++) {
+ var orb = orbs[i];
+ if (!orb.hit && orb.orbType !== 'blackBomb') {
+ var dx = orb.x - swordmasterClone.x;
+ var dy = orb.y - swordmasterClone.y;
+ var distance = Math.sqrt(dx * dx + dy * dy);
+ if (distance < 200 && distance < closestDistance) {
+ closestOrb = orb;
+ closestDistance = distance;
+ }
+ }
+ }
+ // Auto-hit the closest orb
+ if (closestOrb) {
+ var points = 150; // Clone hits give bonus points
+ combo++;
+ if (combo > maxCombo) maxCombo = combo;
+ points *= Math.min(combo, 10);
+ score += points;
+ LK.getSound('hit').play();
+ // Visual feedback
+ var hitColor = closestOrb.orbType === 'redOrb' ? 0xff4444 : 0x44ff44;
+ LK.effects.flashScreen(hitColor, 100);
+ tween(closestOrb, {
+ scaleX: 1.5,
+ scaleY: 1.5,
+ alpha: 0
+ }, {
+ duration: 300
+ });
+ closestOrb.hit = true;
+ updateUI();
+ }
+}
function updateUI() {
scoreText.setText('Score: ' + score);
comboText.setText('Combo: ' + combo);
livesText.setText('Lives: ' + lives);
@@ -443,8 +497,33 @@
if (selectedCharacter) {
selectedCharacter.update();
updateAbilityText();
}
+ // Update swordmaster clone
+ if (swordmasterClone && cloneActiveTime > 0) {
+ cloneActiveTime -= 16; // ~60fps
+ // Clone auto-hits orbs every 100ms
+ if (LK.ticks % 6 === 0) {
+ // Every ~100ms at 60fps
+ autoHitOrbWithClone();
+ }
+ // Remove clone when time expires
+ if (cloneActiveTime <= 0) {
+ tween(swordmasterClone, {
+ alpha: 0,
+ scaleX: 0.5,
+ scaleY: 0.5
+ }, {
+ duration: 300,
+ onFinish: function onFinish() {
+ if (swordmasterClone) {
+ swordmasterClone.destroy();
+ swordmasterClone = null;
+ }
+ }
+ });
+ }
+ }
// Spawn orbs based on BPM timing (Beat Saber style)
var currentTime = Date.now();
if (currentTime - lastSpawnTime > spawnInterval) {
spawnOrb();
A ninja wearing tight black clothes, purple scarf, masked face, white skin, glowing purple eyes, slim and agile body, simple background, front-facing character with no background, standing pose, 2D game character. In-Game asset. 2d. High contrast. No shadows
A white-clothed male samurai with torn clothes, long gray hair tied back, pale skin, red belt on his waist, holding two swords, standing confidently, simple pose, no background, front-facing, 2D game character. In-Game asset. 2d. High contrast. No shadows
A female mage with pale skin, long white hair, wearing a long blue robe and a pointed blue hat, holding a glowing icy staff with a blue crystal, snowflake patterns on robe, cold expression, simple background, front-facing, no background, 2D game character. In-Game asset. 2d. High contrast. No shadows
Green goblin without backround. In-Game asset. 2d. High contrast. No shadows
Red wild monster. In-Game asset. 2d. High contrast. No shadows
Black monster , white eyes looks like bomb. In-Game asset. 2d. High contrast. No shadows