User prompt
Reduce su tamaño un poco más, y haz que las animaciones de ataque tengan el mismo tamaño ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Reduce el tamaño de la idol cuando inicia la partida ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Cuando el personaje jugable es "idolKasumin", cuando se utilice el "idolMic" debe aparecer el asset "kasuminAnimationUltimate", y el sonido debe ser el asset "kasuminUltimate"
User prompt
Haz que cuando se active el asset "idolMic" suene el assets "ultimate"
User prompt
Cuadra la ubicación de los recuadros con el BPM en que se mueven los enemigos ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Separa los recuadros 1 BPM de la idol ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Aumenta el tamaño de los monstruos, de la idol y de los recuadros ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Aumenta el tamaño del asset "idolAnimationUltimate" a que ocupe el 40% de la pantalla ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Haz que el enemigo solo pueda ser golpeado cuando esté dentro del recuadro de su color
User prompt
Quita la validación de que el enemigo puede ser atacado luego de 6 BPM
User prompt
Luego de 30 segundos, haz que aparezca un enemigo con cada beat
User prompt
Haz que los enemigos empiecen a salir en el segundo beat desde que empiece la partida
User prompt
Cada 15 de combo, el asset "idolMic" se recarga, y si ya está activado, no hace nada ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Mientras la pantalla está en negro, la idol no puede recibir daño y el combo no se pierde
User prompt
Haz que la animación de pantalla blanca se muestre luego de que el asset se oculte, y justo cuando se active la animación de pantalla blanca, que todos los enemigos desaparezcan ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Ubicalo en el centro de la pantalla
User prompt
Haz que mientras se muestra el asset "idolAnimationUltimate" la pantalla se ponga negra, y bájalo más de su posición actual ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Haz que cuando se active la función del asset "idolMic" aparezca por un segundo el asset "idolAnimationUltimate" y desaparezca, el tamaño del asset debe ocupar un 25% de la pantalla vertical, y el 100% de la pantalla horizontal, y un poco separado de la parte superior de la pantalla ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Cambia la funcionalidad de abrir la boca por solo tocar el asset "idolMic", y quita la extensión FaceKit
User prompt
Please fix the bug: 'facekit.disableCamera is not a function' in or related to this line: 'facekit.disableCamera();' Line Number: 258 ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Haz que no salga la imagen de la cámara ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Haz que los FPS suban a 60
User prompt
Please fix the bug: 'game.setBackgroundAsset is not a function' in or related to this line: 'game.setBackgroundAsset(background);' Line Number: 302
User prompt
Utiliza el asset "background" como fondo para el juego
User prompt
Usando FaceKit, haz que cuando el asset "idolMic" se encuentre en su estado original, al abrir la boca todos los enemigos mueran, y el asset "idolMic" vuelva a su estado transparente ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var ActionButton = Container.expand(function (color, direction, keyText) {
var self = Container.call(this);
var assetName = color + 'Button';
var buttonGraphics = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.3,
scaleY: 1.3
});
// Add invisible larger hitbox
var hitbox = LK.getAsset('beatIndicator', {
anchorX: 0.5,
anchorY: 0.5,
width: 320,
height: 230
});
hitbox.alpha = 0; // Make completely invisible
hitbox.tint = 0x000000; // Ensure it's invisible
self.addChild(hitbox);
// Key text removed - button functions without text display
self.color = color;
self.direction = direction;
self.isPressed = false;
self.activate = function () {
self.isPressed = true;
buttonGraphics.scaleX = 1.17; // 0.9 * 1.3 to maintain same press effect
buttonGraphics.scaleY = 1.17;
tween(buttonGraphics, {
scaleX: 1.3,
scaleY: 1.3
}, {
duration: 150
});
// Button activation visual feedback only - enemy destruction handled in processInput
self.isPressed = false;
};
self.isEnemyInHitZone = function (enemy) {
var hitZoneSize = 200;
var centerX = 1024;
var centerY = 1366;
if (enemy.direction === 'up' && enemy.y > centerY - hitZoneSize && enemy.y < centerY + hitZoneSize) {
return true;
} else if (enemy.direction === 'down' && enemy.y > centerY - hitZoneSize && enemy.y < centerY + hitZoneSize) {
return true;
} else if (enemy.direction === 'left' && enemy.x > centerX - hitZoneSize && enemy.x < centerX + hitZoneSize) {
return true;
} else if (enemy.direction === 'right' && enemy.x > centerX - hitZoneSize && enemy.x < centerX + hitZoneSize) {
return true;
}
return false;
};
self.down = function (x, y, obj) {
self.activate();
processInput(self.direction);
updateUI();
};
return self;
});
var BeatIndicator = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('beatIndicator', {
anchorX: 0.5,
anchorY: 0.5
});
self.pulse = function () {
graphics.scaleX = 1.5;
graphics.scaleY = 1.5;
tween(graphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 200
});
};
return self;
});
var Enemy = Container.expand(function (color, direction) {
var self = Container.call(this);
var enemyAsset = color + 'Enemy';
var graphics = self.attachAsset(enemyAsset, {
anchorX: 0.5,
anchorY: 0.5
});
self.color = color;
self.direction = direction;
self.health = 3;
self.maxHealth = 3;
// Calculate speed based on 8 beat travel time from spawn to center (8 beats = 4000ms at 120 BPM)
// Distance from spawn to center varies by direction:
// - Up/Down: 1300 pixels (from y=-100 to y=1200 or y=2832 to y=1200)
// - Left/Right: 1124 pixels (from x=-100 to x=1024 or x=2148 to x=1024)
var distanceToCenter;
if (direction === 'up' || direction === 'down') {
distanceToCenter = 1300; // Distance from spawn edge to center
} else {
distanceToCenter = 1124; // Distance from spawn edge to center
}
// Speed = distance / time (8 beats = 4000ms = 4 seconds, but we update 60 times per second)
// So speed per frame = distance / (8 beats * 60fps) = distance / 480 frames for 8 beats
// But since we want them to arrive in 8 beats, we use 240 frames (4 seconds at 60fps)
self.speed = distanceToCenter / 240; // Speed per frame to reach center in 8 beats (4 seconds)
self.baseSpeed = self.speed;
self.rhythmMultiplier = 1;
self.targetX = 1024; // Center X
self.targetY = 1200; // Center Y (matching idol position)
self.lastY = self.y;
self.lastX = self.x;
self.pulseWithBeat = function () {
tween(graphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: beatInterval / 4,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(graphics, {
scaleX: 1,
scaleY: 1
}, {
duration: beatInterval / 4,
easing: tween.easeIn
});
}
});
};
self.takeDamage = function () {
self.health--;
graphics.alpha = 0.5 + self.health / self.maxHealth * 0.5;
if (self.health <= 0) {
LK.effects.flashObject(self, 0xffffff, 300);
return true; // Enemy defeated
}
return false;
};
self.update = function () {
// Calculate rhythm-based speed - enemies move in perfect sync with beat
var currentTime = Date.now();
var timeSinceLastBeat = (currentTime - lastBeatTime) % beatInterval;
var beatProgress = timeSinceLastBeat / beatInterval;
// Smooth sine wave movement that peaks on the beat for dramatic effect
var beatSyncMultiplier = 0.7 + 0.6 * Math.sin(beatProgress * 2 * Math.PI);
self.rhythmMultiplier = beatSyncMultiplier;
var currentSpeed = self.baseSpeed * self.rhythmMultiplier;
// Move toward center based on direction
if (self.direction === 'up') {
self.y += currentSpeed; // Moving down from top
} else if (self.direction === 'down') {
self.y -= currentSpeed; // Moving up from bottom
} else if (self.direction === 'left') {
self.x += currentSpeed; // Moving right from left
} else if (self.direction === 'right') {
self.x -= currentSpeed; // Moving left from right
}
};
return self;
});
var InputZone = Container.expand(function (color, direction, keyText) {
var self = Container.call(this);
var assetName = color + 'Outline';
var background = self.attachAsset(assetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Make background transparent to show only the outline
background.alpha = 0.3;
// Key text removed - zone functions without text display
self.color = color;
self.direction = direction;
self.active = false;
self.activate = function () {
self.active = true;
background.alpha = 0.8;
tween(background, {
alpha: 0.3
}, {
duration: 300
});
};
self.getColorValue = function () {
if (self.color === 'blue') {
return 0x0066ff;
}
if (self.color === 'yellow') {
return 0xffff00;
}
if (self.color === 'red') {
return 0xff0000;
}
if (self.color === 'green') {
return 0x00ff00;
}
return 0x0066ff;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a0d33
});
/****
* Game Code
****/
// Game state variables
var gameState = 'menu'; // menu, playing, gameOver
var lives = 3;
var score = 0;
var combo = 0;
var bpm = 120;
var beatInterval = 60 / bpm * 1000; // milliseconds per beat (500ms at 120 BPM)
var lastBeatTime = 0;
var beatTolerance = 125; // ms tolerance for timing - adjusted for 120 BPM precision (quarter beat window)
var menuSongPlaying = false; // Flag to track if menuSong is currently playing
// Game objects
var idol;
var enemies = [];
var beatIndicator;
var inputZones = [];
var actionButtons = [];
var hearts = [];
var nextBeatTime = 0;
var beatCounter = 0; // Counter to track beats for enemy spawning
var nextEnemySpawnBeat = Math.floor(Math.random() * 3) + 1; // Random interval for next enemy spawn (1-3 beats)
// Enemy configuration - blue, yellow, red and green
var enemyColors = ['blue', 'yellow', 'red', 'green'];
var enemyDirections = ['left', 'right', 'up', 'down'];
var colorDirectionMap = {
'blue': 'left',
// Blue enemies come from left
'yellow': 'right',
// Yellow enemies come from right
'red': 'up',
// Red enemies come from up
'green': 'down' // Green enemies come from down
};
var keyMappings = {
'left': 'A',
'right': 'D',
'up': 'W',
'down': 'S'
};
// Initialize background
var background = game.attachAsset('background', {
anchorX: 0,
anchorY: 0,
x: 0,
y: 0
});
// Position background to fill the screen
background.x = 0;
background.y = 0;
// Initialize idol character (will be properly set up when game starts)
idol = game.addChild(new Container());
idol.x = 1024; // Center horizontally
idol.y = 1200; // Slightly above center vertically
// Add pulse animation method to idol
idol.pulseWithBeat = function () {
tween(idol, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: beatInterval / 4,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(idol, {
scaleX: 1,
scaleY: 1
}, {
duration: beatInterval / 4,
easing: tween.easeIn
});
}
});
};
// Add attack animation method to idol
idol.playAttackAnimation = function (direction) {
// Change to attack asset based on direction and selected idol
var attackAssetPrefix = selectedIdol === 'idolKasumin' ? 'IdolKasumin' : 'idol';
var attackAssetName = attackAssetPrefix + 'Attack' + direction.charAt(0).toUpperCase() + direction.slice(1);
// Remove current graphics and add new attack graphics
if (idol.children.length > 0) {
idol.removeChild(idol.children[0]);
}
var attackGraphics = idol.attachAsset(attackAssetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Don't automatically return to original texture - let movement animation handle it
};
// Initialize beat indicator
beatIndicator = game.addChild(new BeatIndicator());
beatIndicator.x = 1024;
beatIndicator.y = 150;
// Initialize input zones - blue on left, yellow on right, red on top, green on bottom
var zonePositions = [{
x: 874,
y: 1200
}, {
x: 1174,
y: 1200
}, {
x: 1024,
y: 1040
}, {
x: 1024,
y: 1360
}];
for (var i = 0; i < 4; i++) {
var zone = game.addChild(new InputZone(enemyColors[i], enemyDirections[i], keyMappings[enemyDirections[i]]));
zone.x = zonePositions[i].x;
zone.y = zonePositions[i].y;
inputZones.push(zone);
}
// Initialize action buttons at bottom of screen - blue, yellow, red and green buttons
var buttonPositions = [{
x: 824,
y: 2400
}, {
x: 1224,
y: 2400
}, {
x: 1024,
y: 2200
}, {
x: 1024,
y: 2600
}];
for (var i = 0; i < 4; i++) {
var button = game.addChild(new ActionButton(enemyColors[i], enemyDirections[i], keyMappings[enemyDirections[i]]));
button.x = buttonPositions[i].x;
button.y = buttonPositions[i].y;
actionButtons.push(button);
}
// Initialize UI
var scoreTitle = new Text2('Score', {
size: 36,
fill: 0xFFFFFF
});
scoreTitle.anchor.set(1, 0);
scoreTitle.x = -20; // Offset from right edge
scoreTitle.y = 10;
LK.gui.topRight.addChild(scoreTitle); // Hidden on menu
var scoreText = new Text2('0', {
size: 48,
fill: 0xFFFFFF
});
scoreText.anchor.set(1, 0);
scoreText.x = -20; // Offset from right edge
scoreText.y = 50; // Below the title
LK.gui.topRight.addChild(scoreText); // Hidden on menu
var comboText = new Text2('Combo: 0', {
size: 36,
fill: 0xFFFF00
});
comboText.anchor.set(0.5, 0);
LK.gui.top.addChild(comboText); // Hidden on menu
var livesText = new Text2('Lives: ' + lives, {
size: 36,
fill: 0xFFFFFF
});
livesText.anchor.set(0, 0);
livesText.x = 20; // Offset from left edge
livesText.y = 10;
// Initialize hearts display
for (var h = 0; h < lives; h++) {
var heart = LK.getAsset('heart', {
anchorX: 0.5,
anchorY: 0.5
});
heart.x = 1800 + h * 80; // Position hearts horizontally with spacing in top right
heart.y = 250; // Moved further down to avoid overlapping with score
game.addChild(heart);
hearts.push(heart);
}
// Menu elements
var menuContainer = game.addChild(new Container());
// Selection state
var selectedIdol = 'idol'; // Default selection
var idolSelections = [];
// Menu container setup
menuContainer.addChild(titleGraphic);
var playButton = menuContainer.addChild(new InputZone('green', 'down', 'PLAY')); // Using InputZone for rounded button
// Replace the background asset with the startButton asset
playButton.removeChild(playButton.children[0]); // Remove the default outline asset
playButton.attachAsset('startButton', {
// Add the startButton asset
anchorX: 0.5,
anchorY: 0.5,
scaleX: 3,
scaleY: 2
});
var playButtonText = new Text2('▶ PLAY', {
size: 60,
fill: 0xFFFFFF
});
playButtonText.anchor.set(0.5, 0.5);
playButton.addChild(playButtonText);
playButton.x = 1024;
playButton.y = 1950;
playButton.down = function (x, y, obj) {
gameState = 'playing';
menuContainer.visible = false;
// Stop menu music and start background music when game begins
LK.stopMusic();
menuSongPlaying = false;
// Select music based on selected idol
var musicToPlay = selectedIdol === 'idolKasumin' ? 'kasuminbgmusic' : 'bgmusic';
LK.playMusic(musicToPlay);
// Initialize beat timing synchronized with music start
var musicStartTime = Date.now();
lastBeatTime = musicStartTime;
nextBeatTime = musicStartTime + beatInterval;
beatCounter = 0;
// Reset enemy spawn interval
nextEnemySpawnBeat = Math.floor(Math.random() * 3) + 1;
// Reset game state
lives = 3;
score = 0;
combo = 0;
// Reset idolMic to transparent when starting new game
idolMic.alpha = 0.3;
// Clear any existing enemies
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].destroy();
enemies.splice(i, 1);
}
// Update idol asset based on selection - clear all children first
while (idol.children.length > 0) {
idol.removeChild(idol.children[0]);
}
idol.attachAsset(selectedIdol, {
anchorX: 0.5,
anchorY: 0.5
});
// Update visibility to show game elements
updateVisibility();
// Update UI to reflect reset state
updateUI();
};
// Initialize idol microphone in bottom right corner before updateVisibility
var idolMic = game.addChild(LK.getAsset('idolMic', {
anchorX: 1,
anchorY: 1,
x: 2048 - 50,
y: 2732 - 50,
scaleX: 2,
scaleY: 2,
alpha: 0.3
}));
idolMic.visible = false; // Initially hidden, shown when game starts
// Add touch handler to idolMic for destroying all enemies
idolMic.down = function (x, y, obj) {
// Only work when idolMic is in enabled state (full opacity)
if (idolMic.alpha >= 1) {
// Create black screen overlay
var blackOverlay = game.addChild(LK.getAsset('swordBeat', {
anchorX: 0,
anchorY: 0,
width: 2048,
height: 2732,
x: 0,
y: 0
}));
blackOverlay.tint = 0x000000;
// Show idolAnimationUltimate for one second
var ultimateAnimation = game.addChild(LK.getAsset('idolAnimationUltimate', {
anchorX: 0.5,
anchorY: 0.5,
width: 2048,
// 100% of screen width
height: 683,
// 25% of screen height (2732 * 0.25)
x: 1024,
// Center horizontally
y: 1366 // Center vertically (2732 / 2)
}));
// Make the animation and black overlay disappear after 1 second
LK.setTimeout(function () {
ultimateAnimation.destroy();
blackOverlay.destroy();
}, 1000);
// Destroy all enemies
for (var j = enemies.length - 1; j >= 0; j--) {
enemies[j].destroy();
enemies.splice(j, 1);
}
// Reset idolMic to transparent state
tween(idolMic, {
alpha: 0.3
}, {
duration: 300,
easing: tween.easeOut
});
// Visual feedback for special attack
LK.effects.flashScreen(0xffffff, 300);
}
};
// Initially hide game elements and show menu
function updateVisibility() {
var showGame = gameState === 'playing';
var showMenu = gameState === 'menu';
menuContainer.visible = showMenu;
idol.visible = showGame;
beatIndicator.visible = showGame;
for (var i = 0; i < inputZones.length; i++) {
inputZones[i].visible = showGame;
}
for (var i = 0; i < actionButtons.length; i++) {
actionButtons[i].visible = showGame;
}
for (var i = 0; i < hearts.length; i++) {
hearts[i].visible = showGame;
}
livesText.visible = showGame;
scoreTitle.visible = showGame;
scoreText.visible = showGame;
comboText.visible = showGame;
idolMic.visible = showGame;
// Handle menu music
if (showMenu && !menuSongPlaying) {
LK.stopMusic(); // Stop any currently playing music first
LK.playMusic('menuSong');
menuSongPlaying = true;
} else if (showGame) {
// Stop menu music when transitioning to game
LK.stopMusic();
menuSongPlaying = false;
}
}
updateVisibility();
// Start menu music when application loads
if (!menuSongPlaying) {
LK.playMusic('menuSong');
menuSongPlaying = true;
}
// Input handling
var pressedKeys = {};
var inputBuffer = [];
function processInput(direction) {
// Check if input is on beat for bonus scoring
var currentTime = Date.now();
var timeToBeat = Math.abs((currentTime - lastBeatTime) % beatInterval - beatInterval / 2);
var isOnBeat = timeToBeat < beatTolerance;
var beatMultiplier = isOnBeat ? 2 : 1; // Double points for on-beat hits
// Find enemies of matching color that are in their corresponding color zones
var hitEnemy = null;
var minDistance = Infinity;
var hitZoneSize = 100; // Size of the color zone around center
var blueZoneX = 874; // Left zone X position
var yellowZoneX = 1174; // Right zone X position
var zoneY = 1200; // Zone Y position
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
// Check if enemy color matches the button direction and enemy is coming from correct direction
var colorMatch = false;
if (direction === 'left' && enemy.color === 'blue' && enemy.direction === 'left') {
colorMatch = true;
}
if (direction === 'right' && enemy.color === 'yellow' && enemy.direction === 'right') {
colorMatch = true;
}
if (direction === 'up' && enemy.color === 'red' && enemy.direction === 'up') {
colorMatch = true;
}
if (direction === 'down' && enemy.color === 'green' && enemy.direction === 'down') {
colorMatch = true;
}
if (colorMatch) {
// Check if enemy is within hitting range (expanded to allow hits at 6 beats)
// At 6 beats, enemies should be 3/4 of the way to center, so expand hit zones accordingly
var expandedHitZoneSize = 300; // Larger zone to catch enemies earlier in their path
var zoneX = enemy.color === 'blue' ? blueZoneX : enemy.color === 'yellow' ? yellowZoneX : 1024;
var checkZoneY = enemy.color === 'red' ? 1040 : enemy.color === 'green' ? 1200 : zoneY;
// For 6-beat timing, check if enemy is in the corridor leading to their target zone
var inHitCorridor = false;
if (enemy.color === 'blue' && enemy.direction === 'left') {
// Blue enemies coming from left - check if they're in the left corridor approaching blue zone
inHitCorridor = enemy.x > blueZoneX - expandedHitZoneSize && enemy.x < 1024 && Math.abs(enemy.y - zoneY) < expandedHitZoneSize;
} else if (enemy.color === 'yellow' && enemy.direction === 'right') {
// Yellow enemies coming from right - check if they're in the right corridor approaching yellow zone
inHitCorridor = enemy.x < yellowZoneX + expandedHitZoneSize && enemy.x > 1024 && Math.abs(enemy.y - zoneY) < expandedHitZoneSize;
} else if (enemy.color === 'red' && enemy.direction === 'up') {
// Red enemies coming from up - check if they're in the up corridor approaching red zone
inHitCorridor = enemy.y > 1040 - expandedHitZoneSize && enemy.y < 1200 && Math.abs(enemy.x - 1024) < expandedHitZoneSize;
} else if (enemy.color === 'green' && enemy.direction === 'down') {
// Green enemies coming from down - check if they're in the down corridor approaching green zone
inHitCorridor = enemy.y < 1200 + expandedHitZoneSize && enemy.y > 1200 && Math.abs(enemy.x - 1024) < expandedHitZoneSize;
}
if (inHitCorridor) {
var distance = Math.sqrt(Math.pow(enemy.x - zoneX, 2) + Math.pow(enemy.y - checkZoneY, 2));
if (distance < minDistance) {
minDistance = distance;
hitEnemy = enemy;
}
}
}
}
if (hitEnemy) {
// Play attack animation with correct asset
idol.playAttackAnimation(hitEnemy.direction);
// Animate idol movement towards enemy direction
var originalX = idol.x;
var originalY = idol.y;
var moveDistance = 50;
var targetX = originalX;
var targetY = originalY;
// Calculate movement direction based on enemy direction
if (hitEnemy.direction === 'left') {
targetX = originalX - moveDistance;
} else if (hitEnemy.direction === 'right') {
targetX = originalX + moveDistance;
} else if (hitEnemy.direction === 'up') {
targetY = originalY - moveDistance;
} else if (hitEnemy.direction === 'down') {
targetY = originalY + moveDistance;
}
// Move idol towards enemy direction
tween(idol, {
x: targetX,
y: targetY
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
// Always return idol to center position (1024, 1200)
tween(idol, {
x: 1024,
y: 1200
}, {
duration: 200,
easing: tween.easeInOut,
onFinish: function onFinish() {
// Restore original idol graphics when returning to center
if (idol.children.length > 0) {
idol.removeChild(idol.children[0]);
}
idol.attachAsset(selectedIdol, {
anchorX: 0.5,
anchorY: 0.5
});
}
});
}
});
// Create split animation when enemy is defeated
var index = enemies.indexOf(hitEnemy);
if (index > -1) {
// Create two halves of the enemy
var leftHalf = game.addChild(LK.getAsset(hitEnemy.color + 'Enemy', {
anchorX: 1,
anchorY: 0.5
}));
var rightHalf = game.addChild(LK.getAsset(hitEnemy.color + 'Enemy', {
anchorX: 0,
anchorY: 0.5
}));
// Position halves at enemy location
leftHalf.x = hitEnemy.x;
leftHalf.y = hitEnemy.y;
rightHalf.x = hitEnemy.x;
rightHalf.y = hitEnemy.y;
// Set initial scale to show only half width
leftHalf.scaleX = 0.5;
rightHalf.scaleX = 0.5;
// Calculate time until next beat for smooth disappearance
var currentTime = Date.now();
var timeToNextBeat = beatInterval - (currentTime - lastBeatTime) % beatInterval;
// Animate halves splitting apart
tween(leftHalf, {
x: hitEnemy.x - 40,
rotation: -0.3,
alpha: 0.7
}, {
duration: timeToNextBeat,
easing: tween.easeOut
});
tween(rightHalf, {
x: hitEnemy.x + 40,
rotation: 0.3,
alpha: 0.7
}, {
duration: timeToNextBeat,
easing: tween.easeOut,
onFinish: function onFinish() {
// Smooth fade out on beat
tween(leftHalf, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
leftHalf.destroy();
}
});
tween(rightHalf, {
alpha: 0
}, {
duration: 200,
onFinish: function onFinish() {
rightHalf.destroy();
}
});
}
});
// Remove original enemy immediately
hitEnemy.destroy();
enemies.splice(index, 1);
// Calculate score: fixed 10 points per enemy
var enemyScore = 10;
score += enemyScore * beatMultiplier;
combo++;
// Enable idolMic when combo reaches 15
if (combo === 15) {
tween(idolMic, {
alpha: 1
}, {
duration: 500,
easing: tween.easeOut
});
}
// Check if combo reached a multiple of 25 for extra life
if (combo % 25 === 0 && combo > 0 && lives < 3) {
lives++;
// Visual feedback for gaining a life
LK.effects.flashObject(idol, 0x00ff00, 500); // Green flash for extra life
LK.effects.flashScreen(0x00ff00, 300); // Green screen flash
// Update hearts display immediately
if (lives <= hearts.length) {
hearts[lives - 1].alpha = 1; // Show the new heart
}
}
// Visual feedback for on-beat hits
if (isOnBeat) {
LK.effects.flashObject(idol, 0xffff00, 200); // Golden flash for perfect timing
}
}
LK.getSound('hit').play();
} else {
// No matching enemy found in color zone
combo = 0;
// Only reset idolMic to transparent if it hasn't reached enabled state yet
if (idolMic.alpha < 1) {
tween(idolMic, {
alpha: 0.3
}, {
duration: 300,
easing: tween.easeOut
});
}
LK.getSound('error').play();
}
// Find matching input zone and activate
for (var j = 0; j < inputZones.length; j++) {
var zone = inputZones[j];
var shouldActivate = false;
// Map button directions to color zones correctly
if (direction === 'left' && zone.color === 'blue') {
shouldActivate = true;
} // Blue button activates blue zone
else if (direction === 'right' && zone.color === 'yellow') {
shouldActivate = true;
} // Yellow button activates yellow zone
else if (direction === 'up' && zone.color === 'red') {
shouldActivate = true;
} // Red button activates red zone
else if (direction === 'down' && zone.color === 'green') {
shouldActivate = true;
} // Green button activates green zone
if (shouldActivate) {
zone.activate();
break;
}
}
// Also activate corresponding action button
for (var k = 0; k < actionButtons.length; k++) {
if (actionButtons[k].direction === direction) {
actionButtons[k].activate();
break;
}
}
updateUI();
}
function updateUI() {
scoreText.setText(score.toString());
livesText.setText('Lives: ' + lives);
comboText.setText('Combo: ' + combo);
// Update hearts display
for (var h = 0; h < hearts.length; h++) {
if (h < lives) {
hearts[h].alpha = 1; // Show heart
} else {
hearts[h].alpha = 0.3; // Dim lost hearts
}
}
}
function spawnEnemy() {
var colorIndex = Math.floor(Math.random() * enemyColors.length);
var color = enemyColors[colorIndex];
var direction = colorDirectionMap[color];
var enemy = game.addChild(new Enemy(color, direction));
// Position enemy at center of each edge based on direction
if (direction === 'up') {
enemy.x = 1024; // Center X of top edge
enemy.y = -100; // Above screen
} else if (direction === 'down') {
enemy.x = 1024; // Center X of bottom edge
enemy.y = 2832; // Below screen
} else if (direction === 'left') {
enemy.x = -100; // Left of screen
enemy.y = 1200; // Center Y of left edge
} else if (direction === 'right') {
enemy.x = 2148; // Right of screen
enemy.y = 1200; // Center Y of right edge
}
enemy.lastY = enemy.y;
enemy.lastX = enemy.x;
enemies.push(enemy);
}
// Touch controls removed - buttons now only activate when directly touched
game.down = function (x, y, obj) {
// Game down handler - buttons handle their own touch events
};
// Main game loop
game.update = function () {
if (gameState === 'menu') {
updateVisibility();
return;
}
if (gameState !== 'playing') {
return;
}
var currentTime = Date.now();
// Beat tracking - maintain precise synchronization with music
if (currentTime >= nextBeatTime) {
// Calculate the exact beat time based on music start
var timeSinceMusicStart = currentTime - lastBeatTime;
var beatsElapsed = Math.floor(timeSinceMusicStart / beatInterval);
// Update to the precise beat time
lastBeatTime = lastBeatTime + (beatsElapsed + 1) * beatInterval;
nextBeatTime = lastBeatTime + beatInterval;
beatCounter += beatsElapsed + 1; // Account for any missed beats
// Visual beat feedback
beatIndicator.pulse();
// Make all enemies pulse with the beat
for (var j = 0; j < enemies.length; j++) {
enemies[j].pulseWithBeat();
}
// Make idol pulse with the beat
idol.pulseWithBeat();
// Make idolMic pulse with the beat when in enabled state (full opacity)
if (idolMic.alpha >= 1) {
tween(idolMic, {
scaleX: 2.2,
scaleY: 2.2
}, {
duration: beatInterval / 4,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(idolMic, {
scaleX: 2,
scaleY: 2
}, {
duration: beatInterval / 4,
easing: tween.easeIn
});
}
});
}
// Enemy spawning - spawn one enemy every 1-3 beats randomly
if (beatCounter % nextEnemySpawnBeat === 0) {
spawnEnemy();
// Set next random spawn interval (1-3 beats)
nextEnemySpawnBeat = Math.floor(Math.random() * 3) + 1; // Random number between 1 and 3
}
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
var enemy = enemies[i];
// Check if enemy reached center (idol position)
var reachedCenter = false;
if (enemy.direction === 'up' && enemy.lastY < 1200 && enemy.y >= 1200) {
reachedCenter = true;
} else if (enemy.direction === 'down' && enemy.lastY > 1200 && enemy.y <= 1200) {
reachedCenter = true;
} else if (enemy.direction === 'left' && enemy.lastX < 1024 && enemy.x >= 1024) {
reachedCenter = true;
} else if (enemy.direction === 'right' && enemy.lastX > 1024 && enemy.x <= 1024) {
reachedCenter = true;
}
if (reachedCenter) {
lives--;
enemy.destroy();
enemies.splice(i, 1);
combo = 0;
// Only reset idolMic to transparent if it hasn't reached enabled state yet
if (idolMic.alpha < 1) {
tween(idolMic, {
alpha: 0.3
}, {
duration: 300,
easing: tween.easeOut
});
}
LK.getSound('miss').play();
if (lives <= 0) {
gameState = 'gameOver';
LK.showGameOver();
return;
}
updateUI();
LK.effects.flashScreen(0xff0000, 500);
}
enemy.lastY = enemy.y;
enemy.lastX = enemy.x;
}
};
// Touch controls are already implemented above in game.down event handler
// No keyboard input needed for mobile-optimized game
;
var titleGraphic = LK.getAsset('titleGame', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 800,
width: 900,
height: 900
});
menuContainer.addChild(titleGraphic);
// Create idol selection container
var idolSelectionContainer = menuContainer.addChild(new Container());
// Add title for idol selection
var selectIdolTitle = new Text2('Select your Idol', {
size: 72,
fill: 0xFFFFFF
});
selectIdolTitle.anchor.set(0.5, 0.5);
selectIdolTitle.x = 1024;
selectIdolTitle.y = 1350; // Position above the idols
idolSelectionContainer.addChild(selectIdolTitle);
// First idol (left side)
var idolGraphic = LK.getAsset('idol', {
anchorX: 0.5,
anchorY: 0.5,
width: 200,
height: 250
});
idolGraphic.x = 1024 - 150; // Left of center
idolGraphic.y = 1620; // Moved down further
idolSelectionContainer.addChild(idolGraphic);
// Second idol (right side)
var idolKasuminGraphic = LK.getAsset('idolKasumin', {
anchorX: 0.5,
anchorY: 0.5,
width: 200,
height: 250
});
idolKasuminGraphic.x = 1024 + 150; // Right of center
idolKasuminGraphic.y = 1620; // Moved down further
idolSelectionContainer.addChild(idolKasuminGraphic);
// Selection boxes using menuSelectionCharacter
var idolSelectionBox = LK.getAsset('menuSelectionCharacter', {
anchorX: 0.5,
anchorY: 0.5,
width: 250,
height: 300
});
idolSelectionBox.x = idolGraphic.x;
idolSelectionBox.y = idolGraphic.y;
idolSelectionBox.alpha = 1;
idolSelectionContainer.addChild(idolSelectionBox);
var kasuminSelectionBox = LK.getAsset('menuSelectionCharacter', {
anchorX: 0.5,
anchorY: 0.5,
width: 250,
height: 300
});
kasuminSelectionBox.x = idolKasuminGraphic.x;
kasuminSelectionBox.y = idolKasuminGraphic.y;
kasuminSelectionBox.alpha = 0;
idolSelectionContainer.addChild(kasuminSelectionBox);
// Store references
idolSelections.push({
graphic: idolGraphic,
selectionBox: idolSelectionBox,
type: 'idol'
});
idolSelections.push({
graphic: idolKasuminGraphic,
selectionBox: kasuminSelectionBox,
type: 'idolKasumin'
});
// Add touch handlers for idol selection
idolGraphic.down = function (x, y, obj) {
selectedIdol = 'idol';
// Stop any ongoing tween animations on both selection boxes
tween.stop(idolSelectionBox);
tween.stop(kasuminSelectionBox);
// Show selection on chosen idol, hide on other
idolSelectionBox.alpha = 1;
kasuminSelectionBox.alpha = 0;
};
idolKasuminGraphic.down = function (x, y, obj) {
selectedIdol = 'idolKasumin';
// Stop any ongoing tween animations on both selection boxes
tween.stop(idolSelectionBox);
tween.stop(kasuminSelectionBox);
// Show selection on chosen idol, hide on other
idolSelectionBox.alpha = 0;
kasuminSelectionBox.alpha = 1;
};
// Pulsing animation for selected idol - every 1000ms (1 second)
LK.setInterval(function () {
if (gameState === 'menu') {
var activeBox = selectedIdol === 'idol' ? idolSelectionBox : kasuminSelectionBox;
if (activeBox.alpha > 0) {
// Smooth pulse animation using tween
tween(activeBox, {
alpha: 0.3
}, {
duration: 500,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(activeBox, {
alpha: 1
}, {
duration: 500,
easing: tween.easeInOut
});
}
});
}
}
}, 1000);
; ===================================================================
--- original.js
+++ change.js
@@ -482,9 +482,9 @@
height: 683,
// 25% of screen height (2732 * 0.25)
x: 1024,
// Center horizontally
- y: 650 // Moved down from original position (683/2 + 300 pixels from top)
+ y: 1366 // Center vertically (2732 / 2)
}));
// Make the animation and black overlay disappear after 1 second
LK.setTimeout(function () {
ultimateAnimation.destroy();
Flecha verde apuntando hacia abajo. In-Game asset. 2d. High contrast. No shadows
Flecha azul apuntando a la izquierda. In-Game asset. 2d. High contrast. No shadows
Flecha roja apuntando hacia arriba, con borde negro. In-Game asset. 2d. High contrast. No shadows
Flecha amarilla apuntando a la derecha. In-Game asset. 2d. High contrast. No shadows
Una mazmorra rocosa, sin contorno y de fondo se vean piedras algo difuminadas y una entrada a una mazmorra. In-Game asset. 2d. High contrast. No shadows. In-Game asset. 2d. High contrast. No shadows
Un panel cuadrado con contornos redondos, de color violeta. In-Game asset. 2d. High contrast. No shadows
Letras de color blanco, que digan "IDOL DUNGEON BEAT", que tengan un WordArt animado. En alta resolución para poner en una imagen 100 X 150 In-Game asset. 2d. High contrast. No shadows
espada dorada brillante, que da un toque de guerrera mágica o heroína valiente.. In-Game asset. 2d. High contrast. No shadows
Un recuadro blanco sin relleno interlineado. In-Game asset. 2d. High contrast. No shadows
Micrófono chibi, con un lazo rosado y una estrella como moño en el mango del micrófono. In-Game asset. 2d. High contrast. No shadows
Haz una animación en la que solo se vea su cara recortada, un aura de energía rosada rodeándola, y que aparezca como una viñeta que demuestre que está a punto de hacer su acción definitiva
Haz una animación en la que solo se vea su cara recortada, un aura de energía rosada rodeándola, y que aparezca como una viñeta que demuestre que está a punto de hacer su acción definitiva. Que escriba la palabra TWINKLE! abajo a la derecha. La imagen debe tener un fondo color blanco
Pintarlo de color verde
Recuadro de madera, parecido a un tablón de anuncios, animado. In-Game asset. 2d. High contrast. No shadows
Agrégale detalles en los ojos
Haz una animación simulando que ataca hacia la derecha con su escudo, y su mano y cuerpo debe seguir el movimiento del lanzamiento
Haz una animación simulando que ataca hacia abajo con su escudo, y su mano y cuerpo debe seguir el movimiento del golpe
Haz una animación en la que solo se vea su cara recortada, un aura de energía roja rodeándola, y que aparezca como una viñeta que demuestre que está a punto de hacer su acción definitiva. Que escriba la palabra SMILE! abajo a la derecha. La imagen debe tener un fondo color gris
hit
Sound effect
miss
Sound effect
error
Sound effect
bgmusic
Music
menuSong
Music
kasuminbgmusic
Music
ultimate
Sound effect
kasuminUltimate
Sound effect
IdolSelected
Sound effect
IdolKasuminSelected
Sound effect
maplebgmusic
Music
IdolMapleSelected
Sound effect
IdolMapleUltimate
Sound effect
hitKasumin
Sound effect
hitMaple
Sound effect