User prompt
El asset "Idol" solo debe aparecer una vez
User prompt
Si se pierde el combo, y el asset "idolMic" está en su estado original, no debe volverse transparente ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Cuando esté en su estado original, haz que el asset "idolMic" se mueva con cada beat, como hace la idol ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Haz que el asset "IdolMic" se vea transparente, y cuando se alcance un combo de 15, se habilite a su estado original ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Ponlo en la esquina inferior derecha, y que se vea más grande
User prompt
Please fix the bug: 'Cannot set properties of undefined (setting 'visible')' in or related to this line: 'idolMic.visible = showGame;' Line Number: 512
User prompt
Utiliza el asset "idolMic", haz que aparezca en la esquina inferior izquierda de la pantalla cuando se inicia la partida
User prompt
Baja un poco más a las idols
User prompt
Baja un poco la posición del título
User prompt
Agranda el tamaño del título "Select your Idol"
User prompt
Baja un poco la posición de las idols y del botón "play"
User prompt
Sobre las imágenes de las Idol en el menú, coloca un titulo que diga "Select your Idol"
User prompt
Haz que cuando un enemigo sea derrotado, aparezca una animación de que se parte a la mitad, el asset se divide a la mitad y desaparece al siguiente beat de manera con una animación smooth ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
La puntuación debe aumentar en base al combo. Si el combo es un múltiplo de 5, se aumentará +5 a la puntuación por cada enemigo derrotado. Por ejemplo: 22mod5 = 4, entonces cada enemigo dará +20 puntos
User prompt
La vida extra que se obtiene cuando se llega al combo de 25 solo debe sumarse si se tiene menos de 3 vidas. Si ya hay 3 vidas, no se sumará
User prompt
Haz que aparezca el cuadro verde
User prompt
El cuadro verde súbelo un poco, para que su posición actual sea igual a la posición del beat donde estará el monstruo y reduce su tamaño al tamaño del monstruo
User prompt
Cada monstruo debe llegar al centro en 8 beats, por lo que para acertar su golpe, debe estar a 6 beats desde que aparece ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
El cuadro rojo súbelo un poco, para que su posición actual sea igual a la posición del beat donde estará el monstruo
User prompt
El cuadro rojo bájalo un poco de su posición actual, y reduce su tamaño al tamaño del monstruo
User prompt
El cuadro amarillo reducelo al tamaño del monstruo, reduce desde la parte derecha para que la parte izquierda se mantenga en su posición actual
User prompt
Realiza el mismo procedimiento con la parte derecha del recuadro amarillo
User prompt
El cuadro azul reducelo al tamaño del monstruo, reduce desde la parte izquierda para que la parte derecha se mantenga en su posición actual
User prompt
Quiero sincronizar los beats con la música. Quiero que cada beat combine con el momento justo del ataque del enemigo, y que el juego vaya en sincronía con la música. La canción va a 120BPM ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
El asset "kasuminbgmusic" debe seleccionarse si al iniciar la partida está seleccionada "idolKasumin"
/****
* 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
});
// Initialize idol character
idol = game.addChild(LK.getAsset('idol', {
anchorX: 0.5,
anchorY: 0.5
}));
idol.x = 1024; // Center horizontally
idol.y = 1200; // Slightly above center vertically
// Store reference to original idol graphics
var idolGraphics = idol; // idol itself is the graphics object when using LK.getAsset
var originalIdolTexture = idolGraphics.texture;
// 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;
// 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
if (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 left corner before updateVisibility
var idolMic = game.addChild(LK.getAsset('idolMic', {
anchorX: 0,
anchorY: 1,
x: 50,
y: 2732 - 50
}));
idolMic.visible = false; // Initially hidden, shown when game starts
// 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++;
// 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;
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();
// 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;
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
@@ -448,8 +448,16 @@
updateVisibility();
// Update UI to reflect reset state
updateUI();
};
+// Initialize idol microphone in bottom left corner before updateVisibility
+var idolMic = game.addChild(LK.getAsset('idolMic', {
+ anchorX: 0,
+ anchorY: 1,
+ x: 50,
+ y: 2732 - 50
+}));
+idolMic.visible = false; // Initially hidden, shown when game starts
// Initially hide game elements and show menu
function updateVisibility() {
var showGame = gameState === 'playing';
var showMenu = gameState === 'menu';
@@ -822,16 +830,8 @@
}
};
// Touch controls are already implemented above in game.down event handler
// No keyboard input needed for mobile-optimized game
-// Initialize idol microphone in bottom left corner
-var idolMic = game.addChild(LK.getAsset('idolMic', {
- anchorX: 0,
- anchorY: 1,
- x: 50,
- y: 2732 - 50
-}));
-idolMic.visible = false; // Initially hidden, shown when game starts
;
var titleGraphic = LK.getAsset('titleGame', {
anchorX: 0.5,
anchorY: 0.5,
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