Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (3 edits merged)
Please save this source code
User prompt
Please fix the bug: 'Uncaught ReferenceError: resultMessage is not defined' in or related to this line: 'var resultTitle = new Text2(resultMessage, {' Line Number: 863
Code edit (9 edits merged)
Please save this source code
User prompt
Please fix the bug: 'orbitContainer.removeAllChildren is not a function' in or related to this line: 'orbitContainer.removeAllChildren();' Line Number: 534
Code edit (1 edits merged)
Please save this source code
User prompt
Please fix the bug: 'levelContainer.updateCost is not a function' in or related to this line: 'levelContainer.updateCost();' Line Number: 549
Code edit (1 edits merged)
Please save this source code
User prompt
Corrige: - Disminuye la velocidad de los ticks a una cuarta parte. - Los botones de los niveles no funcionan, deben gastar creditos y aumentar la velocidad de la ganancia por tick. - El pull del gacha no funciona, debe de indicar que personaje ganaste - La galeria de personajes no despliega nada
User prompt
Please fix the bug: 'Error: Invalid value. Only literals or 1-level deep objects/arrays containing literals are allowed.' in or related to this line: 'storage.gameState = gameState;' Line Number: 441 ↪💡 Consider importing and using the following plugins: @upit/storage.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Elemental Idle: Character Gacha
Initial prompt
Haremos un juego tipo idle/gacha, donde se obtienen personajes tipo anime, la subinspiración serán los elementos naturales, con la rareza: - Tierra (común) - Agua (común) - Fuego (común) - Viento (común) - Planta (poco común) - Lava (poco común) - Arena (poco común) - Vapor (poco común) - Lodo (poco común) - Ceniza (poco común) - Hielo (raro) - Metal (raro) - Electricidad (raro) - Arena (raro) - Cristal (raro) - Veneno (raro) - Niebla (raro) - Sangre (raro) - Sonido (ultrararo) - Luz (ultrararo) - Obscuridad (ultrararo) - Plasma (ultrararo) - Magnetismo (ultrararo) - Gravedad (ultrararo) - Espejismo (ultrararo) - Radiación (ultrararo) - Vida (legendario) - Muerte (legendario) - Tiempo (legendario) - Espacio (legendario) - Alma (legendario) -Sueño (legendario) - Vacío (único) - Éter (único) - Caos (único) - Orden (único) Se inicia sin personajes, y se puede realizar una tirada aleatoria cada 1000 creditos. En cada tirada se puede obtener un personaje repetido, en ese caso no se desbloquea nada nuevo. Los personajes desbloqueados se pueden ver en una ventana emergente que se despliega con un botón. Los porcentajes de obtención son: 50% común 20% poco común 15% raro 10% ultrararo 4% legendario 1% único Cada rareza da un multiplicador general: común - x2 raro - x3 poco común Al ser un idle, se debe mejorar con créditos ciertos niveles (de momento solo crea 10 niveles genéricos que sumen créditos +1, +2, +3, +5, +10, +20, +50, +100, +200, +300) De momento como no hay sprites se crea un asset con el nombre del elemento que representan.
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Character = Container.expand(function () {
var self = Container.call(this);
self.init = function (elementId, elementName, rarity, assetId, multiplier) {
// Obtenemos el color directamente de la configuración global
var rColor = GAME_CONFIG.RARITIES[rarity] ? GAME_CONFIG.RARITIES[rarity].colorNum : 0xffffff;
// 1. El Marco (Aura de fondo)
var frame = self.attachAsset('bg_character', {
anchorX: 0.5,
anchorY: 0.55
});
frame.scale.set(0.75, 1);
frame.tint = rColor;
frame.alpha = 0.6; // Resplandor sutil
// 2. El Gráfico del Personaje
var graphic = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
graphic.scale.set(0.5, 0.5);
// 3. El Nombre
var nameText = new Text2(elementName, {
size: 50,
fill: '#ffffff'
});
nameText.anchor.set(0.5, 0);
nameText.y = 130;
self.addChild(nameText);
// 4. La Rareza
var rarityText = new Text2('[' + rarity + ']', {
size: 45,
fill: rColor
});
rarityText.anchor.set(0.5, 0);
rarityText.y = 170;
self.addChild(rarityText);
// Guardamos referencias para modificarlas fácilmente en la galería
self.frameObj = frame;
self.graphicObj = graphic;
self.nameObj = nameText;
self.rarityObj = rarityText;
};
return self;
});
var FloatingText = Container.expand(function () {
var self = Container.call(this);
self.init = function (textValue, startX, startY, color) {
self.x = startX;
self.y = startY;
var txt = new Text2(textValue, {
size: 48,
fill: color || '#ffffff'
});
txt.anchor.set(0.5, 0.5);
self.addChild(txt);
tween(self, {
y: self.y - 150,
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
// Clases de UI: Income Level (AHORA MÁS GRANDE)
var IncomeLevel = Container.expand(function () {
var self = Container.call(this);
self.levelIndex = 0;
self.baseIncome = 0;
self.baseCost = 0;
self.currentCost = 0;
self.init = function (levelIndex, baseIncome) {
self.levelIndex = levelIndex;
self.baseIncome = baseIncome;
self.baseCost = 50 * (levelIndex + 1);
var bg = self.attachAsset('upgrade_button', {
anchorX: 0.5,
anchorY: 0.5
});
// Escala aumentada de 1.5 a 2.0
bg.scale.set(2.0, 2.0);
// Textos aumentados y reposicionados
var levelText = new Text2('', {
size: 38,
fill: '#ffffff'
});
levelText.anchor.set(0.5, 0.5);
levelText.y = -25;
self.addChild(levelText);
self.levelText = levelText;
var costText = new Text2('', {
size: 30,
fill: '#ffffff'
});
costText.anchor.set(0.5, 0.5);
costText.y = 25;
self.addChild(costText);
self.costText = costText;
};
self.updateData = function (purchases) {
self.currentCost = Math.floor(self.baseCost * Math.pow(1.5, purchases));
self.costText.setText('Cost: ' + self.currentCost);
self.levelText.setText('Lvl ' + (self.levelIndex + 1) + ' (x' + purchases + '): +' + self.baseIncome + '/t');
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x6f6fdb
});
/****
* Game Code
****/
var GAME_CONFIG = {
BASE_GACHA_COST: 1000,
GACHA_COST_MULTIPLIER: 1.10,
FPS: 60,
SAVE_INTERVAL: 300,
MAX_EQUIPPED: 6,
ORBIT_RADIUS: 400,
ORBIT_SPEED: 0.008,
// NUEVO: Diccionario centralizado de propiedades por rareza
RARITIES: {
'Common': {
colorNum: 0xFFFFFF,
colorStr: '#FFFFFF',
scale: 0.70
},
'Uncommon': {
colorNum: 0x007700,
colorStr: '#00FF00',
scale: 0.70
},
'Rare': {
colorNum: 0x0078a4,
colorStr: '#00BFFF',
scale: 0.70
},
'Ultra-Rare': {
colorNum: 0x8A2BE2,
colorStr: '#8A2BE2',
scale: 0.70
},
'Special': {
colorNum: 0x1d6a60,
colorStr: '#FF1493',
scale: 1.0
},
'Legendary': {
colorNum: 0xd0b100,
colorStr: '#FFD700',
scale: 1.0
},
'Unique': {
colorNum: 0xFF0000,
colorStr: '#FF0000',
scale: 1.0
}
}
};
var characterDatabase = [{
id: 0,
name: 'Fire',
rarity: 'Common',
asset: 'fire_common',
multiplier: 2
}, {
id: 1,
name: 'Water',
rarity: 'Common',
asset: 'water_common',
multiplier: 2
}, {
id: 2,
name: 'Earth',
rarity: 'Common',
asset: 'earth_common',
multiplier: 2
}, {
id: 3,
name: 'Wind',
rarity: 'Common',
asset: 'wind_common',
multiplier: 2
}, {
id: 4,
name: 'Lightning',
rarity: 'Uncommon',
asset: 'lightning_uncommon',
multiplier: 3
}, {
id: 5,
name: 'Ice',
rarity: 'Uncommon',
asset: 'ice_uncommon',
multiplier: 3
}, {
id: 6,
name: 'Nature',
rarity: 'Uncommon',
asset: 'nature_uncommon',
multiplier: 3
}, {
id: 7,
name: 'Metal',
rarity: 'Uncommon',
asset: 'metal_uncommon',
multiplier: 3
}, {
id: 8,
name: 'Sand',
rarity: 'Uncommon',
asset: 'sand_uncommon',
multiplier: 3
}, {
id: 9,
name: 'Steam',
rarity: 'Uncommon',
asset: 'steam_uncommon',
multiplier: 3
}, {
id: 10,
name: 'Magma',
rarity: 'Rare',
asset: 'magma_rare',
multiplier: 4
}, {
id: 11,
name: 'Frost',
rarity: 'Rare',
asset: 'frost_rare',
multiplier: 4
}, {
id: 12,
name: 'Crystal',
rarity: 'Rare',
asset: 'crystal_rare',
multiplier: 4
}, {
id: 13,
name: 'Storm',
rarity: 'Rare',
asset: 'storm_rare',
multiplier: 4
}, {
id: 14,
name: 'Coral',
rarity: 'Rare',
asset: 'coral_rare',
multiplier: 4
}, {
id: 15,
name: 'Forest',
rarity: 'Rare',
asset: 'forest_rare',
multiplier: 4
}, {
id: 16,
name: 'Shadow',
rarity: 'Rare',
asset: 'shadow_rare',
multiplier: 4
}, {
id: 17,
name: 'Plasma',
rarity: 'Ultra-Rare',
asset: 'plasma_ultra',
multiplier: 5
}, {
id: 18,
name: 'Abyss',
rarity: 'Ultra-Rare',
asset: 'abyss_ultra',
multiplier: 5
}, {
id: 19,
name: 'Aurora',
rarity: 'Ultra-Rare',
asset: 'aurora_ultra',
multiplier: 5
}, {
id: 20,
name: 'Inferno',
rarity: 'Ultra-Rare',
asset: 'inferno_ultra',
multiplier: 5
}, {
id: 21,
name: 'Glacier',
rarity: 'Ultra-Rare',
asset: 'glacier_ultra',
multiplier: 5
}, {
id: 22,
name: 'Mirage',
rarity: 'Ultra-Rare',
asset: 'mirage_ultra',
multiplier: 5
}, {
id: 23,
name: 'Celestial',
rarity: 'Legendary',
asset: 'celestial_legendary',
multiplier: 7
}, {
id: 24,
name: 'Cosmic',
rarity: 'Legendary',
asset: 'cosmic_legendary',
multiplier: 7
}, {
id: 25,
name: 'Void',
rarity: 'Legendary',
asset: 'void_legendary',
multiplier: 7
}, {
id: 26,
name: 'Radiance',
rarity: 'Legendary',
asset: 'radiance_legendary',
multiplier: 7
}, {
id: 27,
name: 'Eclipse',
rarity: 'Legendary',
asset: 'eclipse_legendary',
multiplier: 7
}, {
id: 28,
name: 'Genesis',
rarity: 'Unique',
asset: 'genesis_unique',
multiplier: 10
}, {
id: 29,
name: 'Apocalypse',
rarity: 'Unique',
asset: 'apocalypse_unique',
multiplier: 10
}, {
id: 30,
name: 'Paradox',
rarity: 'Unique',
asset: 'paradox_unique',
multiplier: 10
}, {
id: 31,
name: 'Nova',
rarity: 'Special',
asset: 'nova_special',
multiplier: 6
}, {
id: 32,
name: 'Nebula',
rarity: 'Special',
asset: 'nebula_special',
multiplier: 6
}, {
id: 33,
name: 'Titan',
rarity: 'Special',
asset: 'titan_special',
multiplier: 6
}, {
id: 34,
name: 'Phantom',
rarity: 'Special',
asset: 'phantom_special',
multiplier: 6
}, {
id: 35,
name: 'Sphinx',
rarity: 'Special',
asset: 'sphinx_special',
multiplier: 6
}, {
id: 36,
name: 'Oracle',
rarity: 'Special',
asset: 'oracle_special',
multiplier: 6
}, {
id: 37,
name: 'Dust',
rarity: 'Uncommon',
asset: 'dust_uncommon',
multiplier: 3
}, {
id: 38,
name: 'Spore',
rarity: 'Uncommon',
asset: 'spore_uncommon',
multiplier: 3
}, {
id: 39,
name: 'Tectonic',
rarity: 'Rare',
asset: 'tectonic_rare',
multiplier: 4
}, {
id: 40,
name: 'Biosphere',
rarity: 'Ultra-Rare',
asset: 'biosphere_ultra',
multiplier: 5
}, {
id: 41,
name: 'Pulsar',
rarity: 'Special',
asset: 'pulsar_special',
multiplier: 6
}, {
id: 42,
name: 'Antimatter',
rarity: 'Legendary',
asset: 'antimatter_legendary',
multiplier: 7
}, {
id: 43,
name: 'Singularity',
rarity: 'Legendary',
asset: 'singularity_legendary',
multiplier: 7
}, {
id: 44,
name: 'Entropy',
rarity: 'Unique',
asset: 'entropy_unique',
multiplier: 10
}];
// Definimos los dropRates base SIN los IDs hardcodeados
var dropRates = [{
rarity: 'Common',
chance: 45,
ids: []
}, {
rarity: 'Uncommon',
chance: 20,
ids: []
}, {
rarity: 'Rare',
chance: 15,
ids: []
}, {
rarity: 'Ultra-Rare',
chance: 10,
ids: []
}, {
rarity: 'Special',
chance: 6,
ids: []
}, {
rarity: 'Legendary',
chance: 3,
ids: []
}, {
rarity: 'Unique',
chance: 1,
ids: []
}];
var galleryDisplayOrder = [];
// Función para inicializar los datos dinámicamente
function initializeGameData() {
// 1. Llenar los IDs del Gacha automáticamente
for (var i = 0; i < characterDatabase.length; i++) {
var charData = characterDatabase[i];
for (var j = 0; j < dropRates.length; j++) {
if (dropRates[j].rarity === charData.rarity) {
dropRates[j].ids.push(charData.id);
break;
}
}
}
// 2. Generar el orden de la Galería (Por Rareza y luego por ID)
var rarityWeights = {
'Common': 1,
'Uncommon': 2,
'Rare': 3,
'Ultra-Rare': 4,
'Special': 5,
'Legendary': 6,
'Unique': 7
};
// Clonamos la base de datos para no alterar la original y la ordenamos
var sortedCharacters = characterDatabase.slice().sort(function (a, b) {
if (rarityWeights[a.rarity] !== rarityWeights[b.rarity]) {
return rarityWeights[a.rarity] - rarityWeights[b.rarity];
}
return a.id - b.id;
});
// Extraemos solo los IDs ordenados para la galería
for (var k = 0; k < sortedCharacters.length; k++) {
galleryDisplayOrder.push(sortedCharacters[k].id);
}
}
// Ejecutamos la inicialización
initializeGameData();
// Función de ayuda para obtener IDs por rareza (Útil para el Pity System)
function getIdsByRarity(targetRarity) {
for (var i = 0; i < dropRates.length; i++) {
if (dropRates[i].rarity === targetRarity) {
return dropRates[i].ids;
}
}
return [];
}
var gameState = {
credits: 0,
incomePerTick: 1,
collectedCharacters: {},
totalCharactersCollected: 0,
incomeLevels: [],
tickCounter: 0,
equippedCharacters: [],
pityCounter: 0 // Contador de Lástima
};
// Estado temporal para controlar la vista de la galería
var galleryState = {
currentPage: 0,
itemsPerPage: 9,
// Cuadrícula de 3x3
selectedElementId: 0
};
// --- Funciones Base de Estado ---
function getGachaCost() {
return Math.floor(GAME_CONFIG.BASE_GACHA_COST * Math.pow(GAME_CONFIG.GACHA_COST_MULTIPLIER, gameState.totalCharactersCollected));
}
function recalculateIncome() {
// Recalcula incomePerTick desde cero a partir del estado real del juego.
// Esto evita que el income guardado se desincronice con los personajes y upgrades.
var total = 1; // Base income
// Sumar aportes de personajes colectados
for (var charId in gameState.collectedCharacters) {
if (gameState.collectedCharacters[charId]) {
var cData = characterDatabase[parseInt(charId)];
if (cData) {
total += cData.multiplier;
}
}
}
// Sumar aportes de upgrades comprados
// Cada upgrade de nivel i comprado N veces aporta baseIncome * N
// baseIncome por nivel: [1, 2, 3, 5, 10, 20, 50, 100, 200, 300]
var baseIncomes = [1, 2, 3, 5, 10, 20, 50, 100, 200, 300];
for (var lvl = 0; lvl < gameState.incomeLevels.length; lvl++) {
total += baseIncomes[lvl] * gameState.incomeLevels[lvl];
}
gameState.incomePerTick = total;
}
function loadGame() {
gameState.collectedCharacters = {};
gameState.incomeLevels = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
gameState.equippedCharacters = [];
if (storage.credits !== undefined) {
gameState.credits = storage.credits;
gameState.totalCharactersCollected = storage.totalCharactersCollected;
gameState.tickCounter = storage.tickCounter || 0;
gameState.pityCounter = storage.pityCounter || 0;
var collectedIds = (storage.collectedCharacterIds || '').split(',');
for (var i = 0; i < collectedIds.length; i++) {
if (collectedIds[i] !== '') {
gameState.collectedCharacters[parseInt(collectedIds[i])] = true;
}
}
var upgradedLevels = (storage.upgradedLevelCounts || '').split(',');
for (var j = 0; j < upgradedLevels.length; j++) {
if (upgradedLevels[j] !== '') {
gameState.incomeLevels[j] = parseInt(upgradedLevels[j]) || 0;
}
}
var equippedStr = storage.equippedCharactersIds || '';
if (equippedStr !== '') {
var eqSplit = equippedStr.split(',');
for (var k = 0; k < eqSplit.length; k++) {
if (eqSplit[k] !== '') {
var eqId = parseInt(eqSplit[k]);
// Solo restaurar si el ID existe en la DB y fue colectado
if (characterDatabase[eqId] && gameState.collectedCharacters[eqId]) {
gameState.equippedCharacters.push(eqId);
}
}
}
}
// Recalcular income desde cero en lugar de confiar en el valor guardado
recalculateIncome();
}
}
function saveGame() {
storage.credits = gameState.credits;
// incomePerTick NO se guarda: se recalcula al cargar desde personajes y upgrades
storage.totalCharactersCollected = gameState.totalCharactersCollected;
storage.tickCounter = gameState.tickCounter;
storage.pityCounter = gameState.pityCounter;
var collectedIds = '';
for (var charId in gameState.collectedCharacters) {
if (gameState.collectedCharacters[charId]) {
collectedIds += charId + ',';
}
}
storage.collectedCharacterIds = collectedIds;
var upgradedLevels = '';
for (var i = 0; i < gameState.incomeLevels.length; i++) {
upgradedLevels += gameState.incomeLevels[i] + ',';
}
storage.upgradedLevelCounts = upgradedLevels;
var equippedIds = '';
for (var e = 0; e < gameState.equippedCharacters.length; e++) {
equippedIds += gameState.equippedCharacters[e] + ',';
}
storage.equippedCharactersIds = equippedIds;
}
loadGame();
// --- Play Background Music ---
LK.playMusic('background_music', {
loop: true
});
// --- UI Construction ---
var creditsText = new Text2('Credits: 0', {
size: 100,
fill: '#FFD700'
});
creditsText.anchor.set(0.5, 0);
creditsText.x = 1024;
creditsText.y = 50;
game.addChild(creditsText);
var incomeText = new Text2('Income: 1/tick', {
size: 80,
fill: '#00FF00'
});
incomeText.anchor.set(0.5, 0);
incomeText.x = 1024;
incomeText.y = 160;
game.addChild(incomeText);
var collectionText = new Text2('Collection: 0/' + characterDatabase.length, {
size: 60,
fill: '#87CEEB'
});
collectionText.anchor.set(0.5, 0);
collectionText.x = 1024;
collectionText.y = 250;
game.addChild(collectionText);
// --- SISTEMA DE ÓRBITA (A LA DERECHA) ---
var orbitContainer = new Container();
orbitContainer.x = 1536; // 3/4 de la pantalla
orbitContainer.y = 1366; // Centro vertical
game.addChild(orbitContainer);
var orbitingSprites = [];
var orbitAngle = 0;
function updateEquippedDisplay() {
while (orbitContainer.children.length > 0) {
orbitContainer.children[0].destroy();
}
orbitingSprites = [];
for (var i = 0; i < gameState.equippedCharacters.length; i++) {
var charId = gameState.equippedCharacters[i];
var charData = characterDatabase[charId];
var sprite = LK.getAsset(charData.asset, {
anchorX: 0.5,
anchorY: 0.5
});
sprite.scale.set(0.55, 0.55);
orbitContainer.addChild(sprite);
orbitingSprites.push(sprite);
}
}
updateEquippedDisplay();
// --- Manual Clicker Core (A LA DERECHA) ---
var coreGraphic = LK.getAsset('core_crystal', {
anchorX: 0.5,
anchorY: 0.5
});
var coreButton = new Container();
coreButton.addChild(coreGraphic);
coreButton.x = 1536; // 3/4 de la pantalla
coreButton.y = 1366; // Centro vertical
game.addChild(coreButton);
var coreText = new Text2('TAP', {
size: 50,
fill: '#ffffff'
});
coreText.anchor.set(0.5, 0.5);
coreButton.addChild(coreText);
// --- Botón Gacha Individual (Movido a la Izquierda) ---
var gachaBtnGraphic = LK.getAsset('gacha_button', {
anchorX: 0.5,
anchorY: 0.5
});
gachaBtnGraphic.scale.set(1.6, 1.8);
var gachaButton = new Container();
gachaButton.addChild(gachaBtnGraphic);
gachaButton.x = 600; // Antes 1024
gachaButton.y = 2500;
game.addChild(gachaButton);
var gachaText = new Text2('PULL (' + getGachaCost() + ')', {
size: 55,
fill: '#ffffff'
});
gachaText.anchor.set(0.5, 0.5);
gachaText.y = -20;
gachaButton.addChild(gachaText);
var pityText = new Text2('Pity: 0/10', {
size: 40,
fill: '#FFD700'
});
pityText.anchor.set(0.5, 0.5);
pityText.y = 30;
gachaButton.addChild(pityText);
var gachaEnabled = false;
// --- NUEVO: Botón Gacha x10 (A la Derecha) ---
var gacha10BtnGraphic = LK.getAsset('gacha_button', {
anchorX: 0.5,
anchorY: 0.5
});
gacha10BtnGraphic.scale.set(1.6, 1.8);
var gacha10Button = new Container();
gacha10Button.addChild(gacha10BtnGraphic);
gacha10Button.x = 1448;
gacha10Button.y = 2500;
game.addChild(gacha10Button);
var gacha10Text = new Text2('10x PULL', {
size: 55,
fill: '#ffffff'
});
gacha10Text.anchor.set(0.5, 0.5);
gacha10Text.y = -20;
gacha10Button.addChild(gacha10Text);
var pity10Text = new Text2('1 Guaranteed!', {
size: 40,
fill: '#00FF00'
});
pity10Text.anchor.set(0.5, 0.5);
pity10Text.y = 30;
gacha10Button.addChild(pity10Text);
var gacha10Enabled = false;
// Gallery Button
var galleryBtnGraphic = LK.getAsset('gallery_button', {
anchorX: 0.5,
anchorY: 0.5
});
var galleryButton = new Container();
galleryButton.addChild(galleryBtnGraphic);
galleryButton.x = 1800;
galleryButton.y = 500;
game.addChild(galleryButton);
var galleryText = new Text2('GALLERY', {
size: 50,
fill: '#ffffff'
});
galleryText.anchor.set(0.5, 0.5);
galleryButton.addChild(galleryText);
// Debug Reset Button
/*
var debugBtnGraphic = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
var debugButton = new Container();
debugButton.addChild(debugBtnGraphic);
debugButton.x = 300;
debugButton.y = 100;
game.addChild(debugButton);
var debugText = new Text2('RESET', {
size: 40,
fill: '#ff4444'
});
debugText.anchor.set(0.5, 0.5);
debugButton.addChild(debugText);*/
// Upgrade Levels Container (A LA IZQUIERDA Y MÁS GRANDES)
var levelContainers = [];
var levelStartY = 500; // Empezamos un poco más arriba
var levelSpacing = 180; // Más espacio vertical porque los botones crecieron
for (var i = 0; i < 10; i++) {
var levelContainer = new IncomeLevel();
levelContainer.init(i, [1, 2, 3, 5, 10, 20, 50, 100, 200, 300][i]);
levelContainer.updateData(gameState.incomeLevels[i]);
levelContainer.x = 512; // 1/4 de la pantalla hacia la izquierda
levelContainer.y = levelStartY + i * levelSpacing;
game.addChild(levelContainer);
levelContainers.push(levelContainer);
}
// Modal Variables
var modalOpen = false;
var modalContainer = null;
// --- Funciones Lógicas ---
function openGallery() {
if (modalOpen) {
return;
}
modalOpen = true;
galleryState.currentPage = 0;
galleryState.selectedElementId = 0;
// Buscar el primer personaje recolectado para mostrarlo por defecto al abrir
for (var s = 0; s < characterDatabase.length; s++) {
if (gameState.collectedCharacters[s]) {
galleryState.selectedElementId = s;
break;
}
}
modalContainer = new Container();
var modalBg = LK.getAsset('modal_bg', {
anchorX: 0.5,
anchorY: 0.5
});
modalContainer.addChild(modalBg);
modalContainer.x = 1024;
modalContainer.y = 1366;
var galleryTitle = new Text2('Element Gallery', {
size: 80,
fill: '#FFD700'
});
galleryTitle.anchor.set(0.5, 0);
galleryTitle.x = 0;
galleryTitle.y = -1100;
modalContainer.addChild(galleryTitle);
var closeBtnGraphic = LK.getAsset('modal_close_btn', {
anchorX: 0.5,
anchorY: 0.5
});
var closeBtn = new Container();
closeBtn.addChild(closeBtnGraphic);
closeBtn.x = 800;
closeBtn.y = -1050;
modalContainer.addChild(closeBtn);
var closeBtnText = new Text2('X', {
size: 60,
fill: '#ffffff'
});
closeBtnText.anchor.set(0.5, 0.5);
closeBtn.addChild(closeBtnText);
closeBtn.down = function () {
closeModal();
LK.getSound('ui_click').play();
};
// Contenedor dinámico que se refresca
var contentContainer = new Container();
modalContainer.addChild(contentContainer);
function renderGalleryContent() {
while (contentContainer.children.length > 0) {
contentContainer.children[0].destroy();
}
var totalItems = galleryDisplayOrder.length;
var totalPages = Math.ceil(totalItems / galleryState.itemsPerPage);
var startIndex = galleryState.currentPage * galleryState.itemsPerPage;
var endIndex = Math.min(startIndex + galleryState.itemsPerPage, totalItems);
// --- PANEL IZQUIERDO: CUADRÍCULA 3x3 ---
var gridStartX = -750;
var gridStartY = -700;
var spacing = 400;
for (var i = startIndex; i < endIndex; i++) {
var localIndex = i - startIndex;
var col = localIndex % 3;
var row = Math.floor(localIndex / 3);
// AHORA BUSCAMOS EL ID BASADO EN EL NUEVO ORDEN
var actualCharId = galleryDisplayOrder[i];
var charData = characterDatabase[actualCharId];
var isCollected = gameState.collectedCharacters[actualCharId];
var displayChar = new Character();
var displayName = isCollected ? charData.name : "???";
// Siempre revelamos la rareza (Spoiler visual)
var displayRarity = charData.rarity;
displayChar.init(actualCharId, displayName, displayRarity, charData.asset, charData.multiplier);
displayChar.x = gridStartX + col * spacing;
displayChar.y = gridStartY + row * (spacing * 1.2);
// Efecto Silueta Inteligente
if (!isCollected) {
displayChar.graphicObj.tint = 0x000000; // Personaje en negro
displayChar.nameObj.fill = '#555555'; // Letras del nombre en gris
displayChar.frameObj.alpha = 0.05; // Apagamos casi todo el brillo del marco
}
// Indicador visual de elemento seleccionado
if (galleryState.selectedElementId === actualCharId) {
displayChar.scale.set(1.15, 1.15); // Ligeramente más grande al seleccionarlo
}
(function (idToSelect) {
displayChar.down = function () {
if (galleryState.selectedElementId !== idToSelect) {
galleryState.selectedElementId = idToSelect;
LK.getSound('ui_click').play();
renderGalleryContent();
}
};
})(actualCharId);
contentContainer.addChild(displayChar);
}
var pageText = new Text2('Page ' + (galleryState.currentPage + 1) + ' / ' + totalPages, {
size: 80,
fill: '#FFFFFF'
});
pageText.anchor.set(0.5, 0.5);
pageText.x = -300;
pageText.y = 800;
contentContainer.addChild(pageText);
var collectionStatus = new Text2('Collected: ' + gameState.totalCharactersCollected + '/' + characterDatabase.length, {
size: 60,
fill: '#87CEEB'
});
collectionStatus.anchor.set(0.5, 0.5);
collectionStatus.x = -300;
collectionStatus.y = 900;
contentContainer.addChild(collectionStatus);
if (galleryState.currentPage > 0) {
var prevBtn = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
prevBtn.scale.set(0.6, 0.8);
prevBtn.x = -550;
prevBtn.y = 650;
var prevTxt = new Text2('< PREV', {
size: 70,
fill: '#FFF'
});
prevTxt.anchor.set(0.5, 0.5);
prevBtn.addChild(prevTxt);
prevBtn.down = function () {
galleryState.currentPage--;
LK.getSound('ui_click').play();
renderGalleryContent();
};
contentContainer.addChild(prevBtn);
}
if (galleryState.currentPage < totalPages - 1) {
var nextBtn = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
nextBtn.scale.set(0.6, 0.8);
nextBtn.x = -50;
nextBtn.y = 650;
var nextTxt = new Text2('NEXT >', {
size: 70,
fill: '#FFF'
});
nextTxt.anchor.set(0.5, 0.5);
nextBtn.addChild(nextTxt);
nextBtn.down = function () {
galleryState.currentPage++;
LK.getSound('ui_click').play();
renderGalleryContent();
};
contentContainer.addChild(nextBtn);
}
// --- PANEL DERECHO: DETALLES Y EQUIPAR ---
var rightPanelX = 550;
var detailData = characterDatabase[galleryState.selectedElementId];
var isDetailCollected = gameState.collectedCharacters[galleryState.selectedElementId];
if (detailData) {
var bigGraphic = LK.getAsset(detailData.asset, {
anchorX: 0.5,
anchorY: 0.5
});
bigGraphic.scale.set(1.35, 1.35);
bigGraphic.x = rightPanelX;
bigGraphic.y = -250;
if (!isDetailCollected) {
bigGraphic.tint = 0x000000;
}
contentContainer.addChild(bigGraphic);
var detailNameText = isDetailCollected ? detailData.name : "???";
var detailName = new Text2(detailNameText, {
size: 80,
fill: '#FFF'
});
detailName.anchor.set(0.5, 0);
detailName.x = rightPanelX;
detailName.y = 200;
contentContainer.addChild(detailName);
// Mapa de colores String para la UI de detalles
var rarityColorHexStr = GAME_CONFIG.RARITIES[detailData.rarity] ? GAME_CONFIG.RARITIES[detailData.rarity].colorStr : '#FFFFFF';
var detailRarity = new Text2('[' + detailData.rarity + ']', {
size: 60,
fill: rarityColorHexStr
});
detailRarity.anchor.set(0.5, 0);
detailRarity.x = rightPanelX;
detailRarity.y = 280;
contentContainer.addChild(detailRarity);
if (isDetailCollected) {
var incomeInfo = new Text2('Income Boost: +' + detailData.multiplier + '/tick', {
size: 55,
fill: '#00FF00'
});
incomeInfo.anchor.set(0.5, 0);
incomeInfo.x = rightPanelX;
incomeInfo.y = 350;
contentContainer.addChild(incomeInfo);
}
// LÓGICA DEL BOTÓN EQUIPAR
var orbitStatusText = new Text2('Orbiting: ' + gameState.equippedCharacters.length + ' / ' + GAME_CONFIG.MAX_EQUIPPED, {
size: 60,
fill: '#87CEEB'
});
orbitStatusText.anchor.set(0.5, 0);
orbitStatusText.x = rightPanelX;
orbitStatusText.y = 450;
contentContainer.addChild(orbitStatusText);
if (isDetailCollected) {
var isEquipped = gameState.equippedCharacters.indexOf(galleryState.selectedElementId) > -1;
var equipBtnGraphic = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
equipBtnGraphic.scale.set(1.5, 1.2);
equipBtnGraphic.tint = isEquipped ? 0xFF4444 : 0x32CD32;
var equipBtn = new Container();
equipBtn.addChild(equipBtnGraphic);
equipBtn.x = rightPanelX;
equipBtn.y = 650;
var equipBtnText = new Text2(isEquipped ? 'UNEQUIP' : 'EQUIP', {
size: 50,
fill: '#ffffff'
});
equipBtnText.anchor.set(0.5, 0.5);
equipBtn.addChild(equipBtnText);
equipBtn.down = function () {
if (isEquipped) {
var index = gameState.equippedCharacters.indexOf(galleryState.selectedElementId);
gameState.equippedCharacters.splice(index, 1);
LK.getSound('ui_click').play();
} else {
if (gameState.equippedCharacters.length >= GAME_CONFIG.MAX_EQUIPPED) {
// Órbita llena: mostrar texto de aviso en lugar de desplazar silenciosamente
var fullMsg = new FloatingText('Orbit full! Unequip one first.', 1024, 1366, '#FF4444');
game.addChild(fullMsg);
return;
}
gameState.equippedCharacters.push(galleryState.selectedElementId);
LK.getSound('ui_click').play();
}
updateEquippedDisplay();
saveGame();
renderGalleryContent();
};
contentContainer.addChild(equipBtn);
} else {
var lockText = new Text2('LOCKED', {
size: 50,
fill: '#FF4444'
});
lockText.anchor.set(0.5, 0.5);
lockText.x = rightPanelX;
lockText.y = 400;
contentContainer.addChild(lockText);
}
}
}
renderGalleryContent();
game.addChild(modalContainer);
}
function closeModal() {
if (modalContainer) {
modalContainer.destroy();
modalOpen = false;
}
}
// --- Lógica de tirada de Gacha centralizada ---
// Retorna una rareza de pity (Special, Legendary o Unique)
function rollPityRarity() {
var pityRoll = Math.random() * 100;
if (pityRoll <= 60) {
return {
rarity: 'Special',
ids: getIdsByRarity('Special')
};
} else if (pityRoll <= 90) {
return {
rarity: 'Legendary',
ids: getIdsByRarity('Legendary')
};
} else {
return {
rarity: 'Unique',
ids: getIdsByRarity('Unique')
};
}
}
// Tirada normal con soft pity: a partir del pull 6, la probabilidad de rarezas altas sube gradualmente
function rollNormalRarity() {
var softBonus = Math.max(0, gameState.pityCounter - 5) * 3;
var dynamicRates = [];
for (var i = 0; i < dropRates.length; i++) {
var rate = {
rarity: dropRates[i].rarity,
ids: dropRates[i].ids,
chance: dropRates[i].chance
};
if (rate.rarity === 'Special') {
rate.chance = Math.min(30, 6 + softBonus);
} else if (rate.rarity === 'Legendary') {
rate.chance = Math.min(20, 3 + softBonus * 0.5);
} else if (rate.rarity === 'Unique') {
rate.chance = Math.min(10, 1 + softBonus * 0.25);
}
dynamicRates.push(rate);
}
var roll = Math.random() * 100;
var cumulativeChance = 0;
for (var j = 0; j < dynamicRates.length; j++) {
cumulativeChance += dynamicRates[j].chance;
if (roll <= cumulativeChance) {
return dynamicRates[j];
}
}
return dynamicRates[0];
}
// Registra un personaje obtenido y retorna si es nuevo
function registerCharacterObtained(charId) {
var charData = characterDatabase[charId];
if (!gameState.collectedCharacters[charId]) {
gameState.collectedCharacters[charId] = true;
gameState.totalCharactersCollected++;
gameState.incomePerTick += charData.multiplier;
return true;
}
return false;
}
function performGachaPull() {
var currentCost = getGachaCost();
if (gameState.credits < currentCost) {
return;
}
gameState.credits -= currentCost;
gameState.pityCounter++;
var selectedRarity = null;
// Hard Pity en 10: garantiza rareza alta
if (gameState.pityCounter >= 10) {
selectedRarity = rollPityRarity();
gameState.pityCounter = 0;
} else {
// Soft pity: probabilidades aumentan gradualmente desde pull 6
selectedRarity = rollNormalRarity();
if (selectedRarity.rarity === 'Special' || selectedRarity.rarity === 'Legendary' || selectedRarity.rarity === 'Unique') {
gameState.pityCounter = 0;
}
}
var characterIdArray = selectedRarity.ids;
var selectedCharacterId = characterIdArray[Math.floor(Math.random() * characterIdArray.length)];
var wasNew = registerCharacterObtained(selectedCharacterId);
var charData = characterDatabase[selectedCharacterId];
saveGame();
LK.getSound('gacha_pull').play();
if (wasNew) {
LK.getSound('gacha_pull_success').play();
}
modalOpen = true; // Bloquear UI durante la animación del resultado
// Consumimos los efectos visuales desde la configuración global
var currentEffect = GAME_CONFIG.RARITIES[charData.rarity] || {
colorNum: 0xFFFFFF,
scale: 0.70
};
var resultMessage = wasNew ? 'NEW: ' + charData.name + '!' : 'Duplicate: ' + charData.name;
var resultColor = wasNew ? '#2d633b' : '#b89e14';
var resultContainer = new Container();
var resultBg = LK.getAsset('pull_bg', {
anchorX: 0.5,
anchorY: 0.5
});
resultContainer.addChild(resultBg);
resultContainer.x = 1024;
resultContainer.y = 1366;
resultContainer.scale.set(0.1, 0.1);
tween(resultContainer, {
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
var aura = LK.getAsset('core_crystal', {
anchorX: 0.5,
anchorY: 0.5
});
aura.tint = currentEffect.colorNum;
var auraScaleFactor = currentEffect.scale * 2.5;
aura.scale.set(auraScaleFactor, auraScaleFactor);
aura.alpha = 0.5;
resultContainer.addChild(aura);
tween(aura, {
rotation: Math.PI * 2
}, {
duration: 2000
});
var resultGraphic = LK.getAsset(charData.asset, {
anchorX: 0.5,
anchorY: 0.5
});
var scaleTarget = currentEffect.scale;
resultGraphic.scale.set(0.1, 0.1);
tween(resultGraphic, {
scaleX: scaleTarget + 0.05,
scaleY: scaleTarget + 0.05
}, {
duration: 250,
onFinish: function onFinish() {
tween(resultGraphic, {
scaleX: scaleTarget,
scaleY: scaleTarget
}, {
duration: 150
});
}
});
resultGraphic.y = 0;
resultContainer.addChild(resultGraphic);
var safePadding = 720 * scaleTarget / 2 + 80;
var resultTitle = new Text2(resultMessage, {
size: 150,
fill: resultColor
});
resultTitle.anchor.set(0.5, 0.5);
resultTitle.y = -safePadding;
resultContainer.addChild(resultTitle);
var rarityDisplayColor = GAME_CONFIG.RARITIES[charData.rarity] ? GAME_CONFIG.RARITIES[charData.rarity].colorStr : '#FFFFFF';
var resultRarity = new Text2('[' + charData.rarity + ']', {
size: 90,
fill: rarityDisplayColor
});
resultRarity.anchor.set(0.5, 0.5);
resultRarity.y = safePadding;
resultContainer.addChild(resultRarity);
game.addChild(resultContainer);
LK.setTimeout(function () {
tween(resultContainer, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 200,
onFinish: function onFinish() {
resultContainer.destroy();
modalOpen = false; // Liberar UI al cerrar el resultado
}
});
}, 2000);
}
function performGachaPull10() {
var singleCost = getGachaCost();
var totalCost = singleCost * 10;
if (gameState.credits < totalCost) {
return;
}
gameState.credits -= totalCost;
var pullResults = [];
// 1. Las 9 tiradas normales (con soft pity activo)
for (var i = 0; i < 9; i++) {
gameState.pityCounter++;
var selectedRarity = rollNormalRarity();
if (selectedRarity.rarity === 'Special' || selectedRarity.rarity === 'Legendary' || selectedRarity.rarity === 'Unique') {
gameState.pityCounter = 0;
}
var charIdArray = selectedRarity.ids;
var selectedCharId = charIdArray[Math.floor(Math.random() * charIdArray.length)];
pullResults.push(characterDatabase[selectedCharId]);
registerCharacterObtained(selectedCharId);
}
// 2. La 10ª tirada (Lástima Garantizada — siempre rareza alta)
var pityRarity = rollPityRarity();
var pityCharIdArray = pityRarity.ids;
var pityCharId = pityCharIdArray[Math.floor(Math.random() * pityCharIdArray.length)];
pullResults.push(characterDatabase[pityCharId]);
registerCharacterObtained(pityCharId);
// Reiniciamos el pity porque acabamos de dar uno garantizado
gameState.pityCounter = 0;
saveGame();
LK.getSound('gacha_pull').play();
showMultiPullResults(pullResults);
}
function showMultiPullResults(results) {
modalOpen = true; // Bloquea la UI de fondo
var resultContainer = new Container();
var modalBg = LK.getAsset('modal_bg', {
anchorX: 0.5,
anchorY: 0.5
});
resultContainer.addChild(modalBg);
resultContainer.x = 1024;
resultContainer.y = 1366;
resultContainer.scale.set(0.1, 0.1);
tween(resultContainer, {
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
var titleText = new Text2('10x PULL RESULTS', {
size: 100,
fill: '#FFD700'
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -900;
resultContainer.addChild(titleText);
// Configuramos la cuadrícula (2 filas x 5 columnas)
var startX = -600;
var startY = -400;
var spacingX = 300;
var spacingY = 400;
for (var i = 0; i < results.length; i++) {
var charData = results[i];
var col = i % 5;
var row = Math.floor(i / 5);
// Si es de rareza alta, le ponemos un aura detrás
if (charData.rarity === 'Special' || charData.rarity === 'Legendary' || charData.rarity === 'Unique') {
var aura = LK.getAsset('core_crystal', {
anchorX: 0.5,
anchorY: 0.5
});
aura.x = startX + col * spacingX;
aura.y = startY + row * spacingY;
aura.scale.set(1.5, 1.5);
aura.tint = 0xFFD700;
aura.alpha = 0.5;
resultContainer.addChild(aura);
tween(aura, {
rotation: Math.PI * 2
}, {
duration: 2000
});
}
var charGraphic = LK.getAsset(charData.asset, {
anchorX: 0.5,
anchorY: 0.5
});
charGraphic.x = startX + col * spacingX;
charGraphic.y = startY + row * spacingY;
charGraphic.scale.set(0.35, 0.35); // Más pequeños para que quepan
resultContainer.addChild(charGraphic);
}
// Botón para cerrar
var closeBtnGraphic = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
var closeBtn = new Container();
closeBtn.addChild(closeBtnGraphic);
closeBtn.y = 800;
resultContainer.addChild(closeBtn);
var closeText = new Text2('CONTINUE', {
size: 60,
fill: '#FFFFFF'
});
closeText.anchor.set(0.5, 0.5);
closeBtn.addChild(closeText);
closeBtn.down = function () {
LK.getSound('ui_click').play();
tween(resultContainer, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 200,
onFinish: function onFinish() {
resultContainer.destroy();
modalOpen = false; // Liberamos la UI
}
});
};
game.addChild(resultContainer);
}
function upgradeIncomeLevel(levelIndex) {
var level = levelContainers[levelIndex];
if (gameState.credits >= level.currentCost) {
gameState.credits -= level.currentCost;
gameState.incomeLevels[levelIndex]++;
gameState.incomePerTick += level.baseIncome;
level.updateData(gameState.incomeLevels[levelIndex]);
saveGame();
LK.getSound('level_up').play();
// Confirmar visualmente la compra
var floatUpgrade = new FloatingText('+' + level.baseIncome + '/tick', level.x + (Math.random() * 60 - 30), level.y - 60, '#00FF00');
game.addChild(floatUpgrade);
}
}
// --- Event Handlers Directos ---
gachaButton.down = function () {
if (modalOpen) {
return;
}
if (gachaEnabled) {
performGachaPull();
LK.getSound('ui_click').play();
}
};
gacha10Button.down = function () {
if (modalOpen) {
return;
}
if (gacha10Enabled) {
performGachaPull10();
}
};
galleryButton.down = function () {
if (modalOpen) {
return;
}
openGallery();
LK.getSound('ui_click').play();
};
/*
debugButton.down = function () {
if (modalOpen) {
return;
}
gameState.credits = 0;
gameState.incomePerTick = 1;
gameState.collectedCharacters = {};
gameState.totalCharactersCollected = 0;
gameState.incomeLevels = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
gameState.equippedCharacters = [];
for (var i = 0; i < levelContainers.length; i++) {
levelContainers[i].updateData(0);
}
updateEquippedDisplay();
saveGame();
LK.getSound('ui_click').play();
};
*/
coreButton.down = function () {
if (modalOpen) {
return;
}
var clickGain = 1 + Math.floor(gameState.incomePerTick * 0.2);
gameState.credits += clickGain;
var floatText = new FloatingText('+' + clickGain, coreButton.x + (Math.random() * 100 - 50), coreButton.y - 150, '#00ffff');
game.addChild(floatText);
tween(coreGraphic, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 50,
onFinish: function onFinish() {
tween(coreGraphic, {
scaleX: 1,
scaleY: 1
}, {
duration: 50
});
}
});
};
for (var i = 0; i < levelContainers.length; i++) {
(function (index) {
levelContainers[index].down = function () {
if (modalOpen) {
return;
}
upgradeIncomeLevel(index);
};
})(i);
}
// --- Variables para el Main Loop ---
var pulseTimer = 0;
var lastDisplayedCredits = -1;
var lastDisplayedIncome = -1;
var lastDisplayedCollection = -1;
var lastDisplayedGachaCost = -1;
var lastDisplayedPity = -1;
// --- Main Game Loop Optimizado ---
game.update = function () {
gameState.tickCounter++;
// LÓGICA DE ÓRBITA
var numOrbiting = orbitingSprites.length;
if (numOrbiting > 0) {
orbitAngle += GAME_CONFIG.ORBIT_SPEED;
var angleStep = Math.PI * 2 / numOrbiting;
for (var o = 0; o < numOrbiting; o++) {
var currentAngle = orbitAngle + o * angleStep;
orbitingSprites[o].x = Math.cos(currentAngle) * GAME_CONFIG.ORBIT_RADIUS;
orbitingSprites[o].y = Math.sin(currentAngle) * GAME_CONFIG.ORBIT_RADIUS;
//orbitingSprites[o].rotation += 0.01;
}
}
if (gameState.tickCounter % GAME_CONFIG.FPS === 0) {
gameState.credits += gameState.incomePerTick;
}
// --- Actualización de Costos y Textos ---
var currentCredits = Math.floor(gameState.credits);
var currentGachaCost = getGachaCost();
var currentGachaCost10 = currentGachaCost * 10;
// === INICIO DEL BLOQUE FALTANTE ===
if (currentCredits !== lastDisplayedCredits) {
creditsText.setText('Credits: ' + currentCredits);
lastDisplayedCredits = currentCredits;
}
if (gameState.incomePerTick !== lastDisplayedIncome) {
incomeText.setText('Income: ' + gameState.incomePerTick + '/tick');
lastDisplayedIncome = gameState.incomePerTick;
}
if (gameState.totalCharactersCollected !== lastDisplayedCollection) {
// Usamos characterDatabase.length para que se actualice solo si agregas más en el futuro
collectionText.setText('Collection: ' + gameState.totalCharactersCollected + '/' + characterDatabase.length);
lastDisplayedCollection = gameState.totalCharactersCollected;
}
if (currentGachaCost !== lastDisplayedGachaCost || gameState.pityCounter !== lastDisplayedPity) {
// Actualiza textos Botón x1
gachaText.setText('PULL (' + currentGachaCost + ')');
if (gameState.pityCounter >= 9) {
pityText.setText('Pity: READY!');
pityText.fill = '#00FF00';
} else if (gameState.pityCounter >= 6) {
// Soft pity activo: probabilidades subiendo
pityText.setText('Pity: ' + gameState.pityCounter + "/10 \u2191");
pityText.fill = '#FFA500';
} else {
pityText.setText('Pity: ' + gameState.pityCounter + '/10');
pityText.fill = '#FFD700';
}
// Actualiza textos Botón x10
gacha10Text.setText('10x (' + currentGachaCost10 + ')');
lastDisplayedGachaCost = currentGachaCost;
lastDisplayedPity = gameState.pityCounter;
}
// --- Control Visual de Botones Disponibles ---
gachaEnabled = gameState.credits >= currentGachaCost;
gacha10Enabled = gameState.credits >= currentGachaCost10;
// Efectos del Botón x1
if (gachaEnabled) {
gachaBtnGraphic.tint = 0xFF1493;
gachaText.tint = 0xFFFFFF;
pulseTimer += 0.05;
var scaleX = 1.6 + Math.sin(pulseTimer) * 0.05;
var scaleY = 1.8 + Math.sin(pulseTimer) * 0.05;
gachaBtnGraphic.scale.set(scaleX, scaleY);
} else {
gachaBtnGraphic.tint = 0x883366;
gachaText.tint = 0xAAAAAA;
gachaBtnGraphic.scale.set(1.6, 1.8);
}
// Efectos del Botón x10
if (gacha10Enabled) {
gacha10BtnGraphic.tint = 0xFF1493;
gacha10Text.tint = 0xFFFFFF;
var scaleX10 = 1.6 + Math.cos(pulseTimer) * 0.05; // Usamos cos para desfasar el pulso visual
var scaleY10 = 1.8 + Math.cos(pulseTimer) * 0.05;
gacha10BtnGraphic.scale.set(scaleX10, scaleY10);
} else {
gacha10BtnGraphic.tint = 0x883366;
gacha10Text.tint = 0xAAAAAA;
gacha10BtnGraphic.scale.set(1.6, 1.8);
}
for (var j = 0; j < levelContainers.length; j++) {
var level = levelContainers[j];
if (gameState.credits >= level.currentCost) {
level.children[0].tint = 0xffffff;
} else {
level.children[0].tint = 0x525252;
}
}
if (gameState.tickCounter % GAME_CONFIG.SAVE_INTERVAL === 0) {
saveGame();
}
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Character = Container.expand(function () {
var self = Container.call(this);
self.init = function (elementId, elementName, rarity, assetId, multiplier) {
// Obtenemos el color directamente de la configuración global
var rColor = GAME_CONFIG.RARITIES[rarity] ? GAME_CONFIG.RARITIES[rarity].colorNum : 0xffffff;
// 1. El Marco (Aura de fondo)
var frame = self.attachAsset('bg_character', {
anchorX: 0.5,
anchorY: 0.55
});
frame.scale.set(0.75, 1);
frame.tint = rColor;
frame.alpha = 0.6; // Resplandor sutil
// 2. El Gráfico del Personaje
var graphic = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
graphic.scale.set(0.5, 0.5);
// 3. El Nombre
var nameText = new Text2(elementName, {
size: 50,
fill: '#ffffff'
});
nameText.anchor.set(0.5, 0);
nameText.y = 130;
self.addChild(nameText);
// 4. La Rareza
var rarityText = new Text2('[' + rarity + ']', {
size: 45,
fill: rColor
});
rarityText.anchor.set(0.5, 0);
rarityText.y = 170;
self.addChild(rarityText);
// Guardamos referencias para modificarlas fácilmente en la galería
self.frameObj = frame;
self.graphicObj = graphic;
self.nameObj = nameText;
self.rarityObj = rarityText;
};
return self;
});
var FloatingText = Container.expand(function () {
var self = Container.call(this);
self.init = function (textValue, startX, startY, color) {
self.x = startX;
self.y = startY;
var txt = new Text2(textValue, {
size: 48,
fill: color || '#ffffff'
});
txt.anchor.set(0.5, 0.5);
self.addChild(txt);
tween(self, {
y: self.y - 150,
alpha: 0
}, {
duration: 800,
onFinish: function onFinish() {
self.destroy();
}
});
};
return self;
});
// Clases de UI: Income Level (AHORA MÁS GRANDE)
var IncomeLevel = Container.expand(function () {
var self = Container.call(this);
self.levelIndex = 0;
self.baseIncome = 0;
self.baseCost = 0;
self.currentCost = 0;
self.init = function (levelIndex, baseIncome) {
self.levelIndex = levelIndex;
self.baseIncome = baseIncome;
self.baseCost = 50 * (levelIndex + 1);
var bg = self.attachAsset('upgrade_button', {
anchorX: 0.5,
anchorY: 0.5
});
// Escala aumentada de 1.5 a 2.0
bg.scale.set(2.0, 2.0);
// Textos aumentados y reposicionados
var levelText = new Text2('', {
size: 38,
fill: '#ffffff'
});
levelText.anchor.set(0.5, 0.5);
levelText.y = -25;
self.addChild(levelText);
self.levelText = levelText;
var costText = new Text2('', {
size: 30,
fill: '#ffffff'
});
costText.anchor.set(0.5, 0.5);
costText.y = 25;
self.addChild(costText);
self.costText = costText;
};
self.updateData = function (purchases) {
self.currentCost = Math.floor(self.baseCost * Math.pow(1.5, purchases));
self.costText.setText('Cost: ' + self.currentCost);
self.levelText.setText('Lvl ' + (self.levelIndex + 1) + ' (x' + purchases + '): +' + self.baseIncome + '/t');
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x6f6fdb
});
/****
* Game Code
****/
var GAME_CONFIG = {
BASE_GACHA_COST: 1000,
GACHA_COST_MULTIPLIER: 1.10,
FPS: 60,
SAVE_INTERVAL: 300,
MAX_EQUIPPED: 6,
ORBIT_RADIUS: 400,
ORBIT_SPEED: 0.008,
// NUEVO: Diccionario centralizado de propiedades por rareza
RARITIES: {
'Common': {
colorNum: 0xFFFFFF,
colorStr: '#FFFFFF',
scale: 0.70
},
'Uncommon': {
colorNum: 0x007700,
colorStr: '#00FF00',
scale: 0.70
},
'Rare': {
colorNum: 0x0078a4,
colorStr: '#00BFFF',
scale: 0.70
},
'Ultra-Rare': {
colorNum: 0x8A2BE2,
colorStr: '#8A2BE2',
scale: 0.70
},
'Special': {
colorNum: 0x1d6a60,
colorStr: '#FF1493',
scale: 1.0
},
'Legendary': {
colorNum: 0xd0b100,
colorStr: '#FFD700',
scale: 1.0
},
'Unique': {
colorNum: 0xFF0000,
colorStr: '#FF0000',
scale: 1.0
}
}
};
var characterDatabase = [{
id: 0,
name: 'Fire',
rarity: 'Common',
asset: 'fire_common',
multiplier: 2
}, {
id: 1,
name: 'Water',
rarity: 'Common',
asset: 'water_common',
multiplier: 2
}, {
id: 2,
name: 'Earth',
rarity: 'Common',
asset: 'earth_common',
multiplier: 2
}, {
id: 3,
name: 'Wind',
rarity: 'Common',
asset: 'wind_common',
multiplier: 2
}, {
id: 4,
name: 'Lightning',
rarity: 'Uncommon',
asset: 'lightning_uncommon',
multiplier: 3
}, {
id: 5,
name: 'Ice',
rarity: 'Uncommon',
asset: 'ice_uncommon',
multiplier: 3
}, {
id: 6,
name: 'Nature',
rarity: 'Uncommon',
asset: 'nature_uncommon',
multiplier: 3
}, {
id: 7,
name: 'Metal',
rarity: 'Uncommon',
asset: 'metal_uncommon',
multiplier: 3
}, {
id: 8,
name: 'Sand',
rarity: 'Uncommon',
asset: 'sand_uncommon',
multiplier: 3
}, {
id: 9,
name: 'Steam',
rarity: 'Uncommon',
asset: 'steam_uncommon',
multiplier: 3
}, {
id: 10,
name: 'Magma',
rarity: 'Rare',
asset: 'magma_rare',
multiplier: 4
}, {
id: 11,
name: 'Frost',
rarity: 'Rare',
asset: 'frost_rare',
multiplier: 4
}, {
id: 12,
name: 'Crystal',
rarity: 'Rare',
asset: 'crystal_rare',
multiplier: 4
}, {
id: 13,
name: 'Storm',
rarity: 'Rare',
asset: 'storm_rare',
multiplier: 4
}, {
id: 14,
name: 'Coral',
rarity: 'Rare',
asset: 'coral_rare',
multiplier: 4
}, {
id: 15,
name: 'Forest',
rarity: 'Rare',
asset: 'forest_rare',
multiplier: 4
}, {
id: 16,
name: 'Shadow',
rarity: 'Rare',
asset: 'shadow_rare',
multiplier: 4
}, {
id: 17,
name: 'Plasma',
rarity: 'Ultra-Rare',
asset: 'plasma_ultra',
multiplier: 5
}, {
id: 18,
name: 'Abyss',
rarity: 'Ultra-Rare',
asset: 'abyss_ultra',
multiplier: 5
}, {
id: 19,
name: 'Aurora',
rarity: 'Ultra-Rare',
asset: 'aurora_ultra',
multiplier: 5
}, {
id: 20,
name: 'Inferno',
rarity: 'Ultra-Rare',
asset: 'inferno_ultra',
multiplier: 5
}, {
id: 21,
name: 'Glacier',
rarity: 'Ultra-Rare',
asset: 'glacier_ultra',
multiplier: 5
}, {
id: 22,
name: 'Mirage',
rarity: 'Ultra-Rare',
asset: 'mirage_ultra',
multiplier: 5
}, {
id: 23,
name: 'Celestial',
rarity: 'Legendary',
asset: 'celestial_legendary',
multiplier: 7
}, {
id: 24,
name: 'Cosmic',
rarity: 'Legendary',
asset: 'cosmic_legendary',
multiplier: 7
}, {
id: 25,
name: 'Void',
rarity: 'Legendary',
asset: 'void_legendary',
multiplier: 7
}, {
id: 26,
name: 'Radiance',
rarity: 'Legendary',
asset: 'radiance_legendary',
multiplier: 7
}, {
id: 27,
name: 'Eclipse',
rarity: 'Legendary',
asset: 'eclipse_legendary',
multiplier: 7
}, {
id: 28,
name: 'Genesis',
rarity: 'Unique',
asset: 'genesis_unique',
multiplier: 10
}, {
id: 29,
name: 'Apocalypse',
rarity: 'Unique',
asset: 'apocalypse_unique',
multiplier: 10
}, {
id: 30,
name: 'Paradox',
rarity: 'Unique',
asset: 'paradox_unique',
multiplier: 10
}, {
id: 31,
name: 'Nova',
rarity: 'Special',
asset: 'nova_special',
multiplier: 6
}, {
id: 32,
name: 'Nebula',
rarity: 'Special',
asset: 'nebula_special',
multiplier: 6
}, {
id: 33,
name: 'Titan',
rarity: 'Special',
asset: 'titan_special',
multiplier: 6
}, {
id: 34,
name: 'Phantom',
rarity: 'Special',
asset: 'phantom_special',
multiplier: 6
}, {
id: 35,
name: 'Sphinx',
rarity: 'Special',
asset: 'sphinx_special',
multiplier: 6
}, {
id: 36,
name: 'Oracle',
rarity: 'Special',
asset: 'oracle_special',
multiplier: 6
}, {
id: 37,
name: 'Dust',
rarity: 'Uncommon',
asset: 'dust_uncommon',
multiplier: 3
}, {
id: 38,
name: 'Spore',
rarity: 'Uncommon',
asset: 'spore_uncommon',
multiplier: 3
}, {
id: 39,
name: 'Tectonic',
rarity: 'Rare',
asset: 'tectonic_rare',
multiplier: 4
}, {
id: 40,
name: 'Biosphere',
rarity: 'Ultra-Rare',
asset: 'biosphere_ultra',
multiplier: 5
}, {
id: 41,
name: 'Pulsar',
rarity: 'Special',
asset: 'pulsar_special',
multiplier: 6
}, {
id: 42,
name: 'Antimatter',
rarity: 'Legendary',
asset: 'antimatter_legendary',
multiplier: 7
}, {
id: 43,
name: 'Singularity',
rarity: 'Legendary',
asset: 'singularity_legendary',
multiplier: 7
}, {
id: 44,
name: 'Entropy',
rarity: 'Unique',
asset: 'entropy_unique',
multiplier: 10
}];
// Definimos los dropRates base SIN los IDs hardcodeados
var dropRates = [{
rarity: 'Common',
chance: 45,
ids: []
}, {
rarity: 'Uncommon',
chance: 20,
ids: []
}, {
rarity: 'Rare',
chance: 15,
ids: []
}, {
rarity: 'Ultra-Rare',
chance: 10,
ids: []
}, {
rarity: 'Special',
chance: 6,
ids: []
}, {
rarity: 'Legendary',
chance: 3,
ids: []
}, {
rarity: 'Unique',
chance: 1,
ids: []
}];
var galleryDisplayOrder = [];
// Función para inicializar los datos dinámicamente
function initializeGameData() {
// 1. Llenar los IDs del Gacha automáticamente
for (var i = 0; i < characterDatabase.length; i++) {
var charData = characterDatabase[i];
for (var j = 0; j < dropRates.length; j++) {
if (dropRates[j].rarity === charData.rarity) {
dropRates[j].ids.push(charData.id);
break;
}
}
}
// 2. Generar el orden de la Galería (Por Rareza y luego por ID)
var rarityWeights = {
'Common': 1,
'Uncommon': 2,
'Rare': 3,
'Ultra-Rare': 4,
'Special': 5,
'Legendary': 6,
'Unique': 7
};
// Clonamos la base de datos para no alterar la original y la ordenamos
var sortedCharacters = characterDatabase.slice().sort(function (a, b) {
if (rarityWeights[a.rarity] !== rarityWeights[b.rarity]) {
return rarityWeights[a.rarity] - rarityWeights[b.rarity];
}
return a.id - b.id;
});
// Extraemos solo los IDs ordenados para la galería
for (var k = 0; k < sortedCharacters.length; k++) {
galleryDisplayOrder.push(sortedCharacters[k].id);
}
}
// Ejecutamos la inicialización
initializeGameData();
// Función de ayuda para obtener IDs por rareza (Útil para el Pity System)
function getIdsByRarity(targetRarity) {
for (var i = 0; i < dropRates.length; i++) {
if (dropRates[i].rarity === targetRarity) {
return dropRates[i].ids;
}
}
return [];
}
var gameState = {
credits: 0,
incomePerTick: 1,
collectedCharacters: {},
totalCharactersCollected: 0,
incomeLevels: [],
tickCounter: 0,
equippedCharacters: [],
pityCounter: 0 // Contador de Lástima
};
// Estado temporal para controlar la vista de la galería
var galleryState = {
currentPage: 0,
itemsPerPage: 9,
// Cuadrícula de 3x3
selectedElementId: 0
};
// --- Funciones Base de Estado ---
function getGachaCost() {
return Math.floor(GAME_CONFIG.BASE_GACHA_COST * Math.pow(GAME_CONFIG.GACHA_COST_MULTIPLIER, gameState.totalCharactersCollected));
}
function recalculateIncome() {
// Recalcula incomePerTick desde cero a partir del estado real del juego.
// Esto evita que el income guardado se desincronice con los personajes y upgrades.
var total = 1; // Base income
// Sumar aportes de personajes colectados
for (var charId in gameState.collectedCharacters) {
if (gameState.collectedCharacters[charId]) {
var cData = characterDatabase[parseInt(charId)];
if (cData) {
total += cData.multiplier;
}
}
}
// Sumar aportes de upgrades comprados
// Cada upgrade de nivel i comprado N veces aporta baseIncome * N
// baseIncome por nivel: [1, 2, 3, 5, 10, 20, 50, 100, 200, 300]
var baseIncomes = [1, 2, 3, 5, 10, 20, 50, 100, 200, 300];
for (var lvl = 0; lvl < gameState.incomeLevels.length; lvl++) {
total += baseIncomes[lvl] * gameState.incomeLevels[lvl];
}
gameState.incomePerTick = total;
}
function loadGame() {
gameState.collectedCharacters = {};
gameState.incomeLevels = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
gameState.equippedCharacters = [];
if (storage.credits !== undefined) {
gameState.credits = storage.credits;
gameState.totalCharactersCollected = storage.totalCharactersCollected;
gameState.tickCounter = storage.tickCounter || 0;
gameState.pityCounter = storage.pityCounter || 0;
var collectedIds = (storage.collectedCharacterIds || '').split(',');
for (var i = 0; i < collectedIds.length; i++) {
if (collectedIds[i] !== '') {
gameState.collectedCharacters[parseInt(collectedIds[i])] = true;
}
}
var upgradedLevels = (storage.upgradedLevelCounts || '').split(',');
for (var j = 0; j < upgradedLevels.length; j++) {
if (upgradedLevels[j] !== '') {
gameState.incomeLevels[j] = parseInt(upgradedLevels[j]) || 0;
}
}
var equippedStr = storage.equippedCharactersIds || '';
if (equippedStr !== '') {
var eqSplit = equippedStr.split(',');
for (var k = 0; k < eqSplit.length; k++) {
if (eqSplit[k] !== '') {
var eqId = parseInt(eqSplit[k]);
// Solo restaurar si el ID existe en la DB y fue colectado
if (characterDatabase[eqId] && gameState.collectedCharacters[eqId]) {
gameState.equippedCharacters.push(eqId);
}
}
}
}
// Recalcular income desde cero en lugar de confiar en el valor guardado
recalculateIncome();
}
}
function saveGame() {
storage.credits = gameState.credits;
// incomePerTick NO se guarda: se recalcula al cargar desde personajes y upgrades
storage.totalCharactersCollected = gameState.totalCharactersCollected;
storage.tickCounter = gameState.tickCounter;
storage.pityCounter = gameState.pityCounter;
var collectedIds = '';
for (var charId in gameState.collectedCharacters) {
if (gameState.collectedCharacters[charId]) {
collectedIds += charId + ',';
}
}
storage.collectedCharacterIds = collectedIds;
var upgradedLevels = '';
for (var i = 0; i < gameState.incomeLevels.length; i++) {
upgradedLevels += gameState.incomeLevels[i] + ',';
}
storage.upgradedLevelCounts = upgradedLevels;
var equippedIds = '';
for (var e = 0; e < gameState.equippedCharacters.length; e++) {
equippedIds += gameState.equippedCharacters[e] + ',';
}
storage.equippedCharactersIds = equippedIds;
}
loadGame();
// --- Play Background Music ---
LK.playMusic('background_music', {
loop: true
});
// --- UI Construction ---
var creditsText = new Text2('Credits: 0', {
size: 100,
fill: '#FFD700'
});
creditsText.anchor.set(0.5, 0);
creditsText.x = 1024;
creditsText.y = 50;
game.addChild(creditsText);
var incomeText = new Text2('Income: 1/tick', {
size: 80,
fill: '#00FF00'
});
incomeText.anchor.set(0.5, 0);
incomeText.x = 1024;
incomeText.y = 160;
game.addChild(incomeText);
var collectionText = new Text2('Collection: 0/' + characterDatabase.length, {
size: 60,
fill: '#87CEEB'
});
collectionText.anchor.set(0.5, 0);
collectionText.x = 1024;
collectionText.y = 250;
game.addChild(collectionText);
// --- SISTEMA DE ÓRBITA (A LA DERECHA) ---
var orbitContainer = new Container();
orbitContainer.x = 1536; // 3/4 de la pantalla
orbitContainer.y = 1366; // Centro vertical
game.addChild(orbitContainer);
var orbitingSprites = [];
var orbitAngle = 0;
function updateEquippedDisplay() {
while (orbitContainer.children.length > 0) {
orbitContainer.children[0].destroy();
}
orbitingSprites = [];
for (var i = 0; i < gameState.equippedCharacters.length; i++) {
var charId = gameState.equippedCharacters[i];
var charData = characterDatabase[charId];
var sprite = LK.getAsset(charData.asset, {
anchorX: 0.5,
anchorY: 0.5
});
sprite.scale.set(0.55, 0.55);
orbitContainer.addChild(sprite);
orbitingSprites.push(sprite);
}
}
updateEquippedDisplay();
// --- Manual Clicker Core (A LA DERECHA) ---
var coreGraphic = LK.getAsset('core_crystal', {
anchorX: 0.5,
anchorY: 0.5
});
var coreButton = new Container();
coreButton.addChild(coreGraphic);
coreButton.x = 1536; // 3/4 de la pantalla
coreButton.y = 1366; // Centro vertical
game.addChild(coreButton);
var coreText = new Text2('TAP', {
size: 50,
fill: '#ffffff'
});
coreText.anchor.set(0.5, 0.5);
coreButton.addChild(coreText);
// --- Botón Gacha Individual (Movido a la Izquierda) ---
var gachaBtnGraphic = LK.getAsset('gacha_button', {
anchorX: 0.5,
anchorY: 0.5
});
gachaBtnGraphic.scale.set(1.6, 1.8);
var gachaButton = new Container();
gachaButton.addChild(gachaBtnGraphic);
gachaButton.x = 600; // Antes 1024
gachaButton.y = 2500;
game.addChild(gachaButton);
var gachaText = new Text2('PULL (' + getGachaCost() + ')', {
size: 55,
fill: '#ffffff'
});
gachaText.anchor.set(0.5, 0.5);
gachaText.y = -20;
gachaButton.addChild(gachaText);
var pityText = new Text2('Pity: 0/10', {
size: 40,
fill: '#FFD700'
});
pityText.anchor.set(0.5, 0.5);
pityText.y = 30;
gachaButton.addChild(pityText);
var gachaEnabled = false;
// --- NUEVO: Botón Gacha x10 (A la Derecha) ---
var gacha10BtnGraphic = LK.getAsset('gacha_button', {
anchorX: 0.5,
anchorY: 0.5
});
gacha10BtnGraphic.scale.set(1.6, 1.8);
var gacha10Button = new Container();
gacha10Button.addChild(gacha10BtnGraphic);
gacha10Button.x = 1448;
gacha10Button.y = 2500;
game.addChild(gacha10Button);
var gacha10Text = new Text2('10x PULL', {
size: 55,
fill: '#ffffff'
});
gacha10Text.anchor.set(0.5, 0.5);
gacha10Text.y = -20;
gacha10Button.addChild(gacha10Text);
var pity10Text = new Text2('1 Guaranteed!', {
size: 40,
fill: '#00FF00'
});
pity10Text.anchor.set(0.5, 0.5);
pity10Text.y = 30;
gacha10Button.addChild(pity10Text);
var gacha10Enabled = false;
// Gallery Button
var galleryBtnGraphic = LK.getAsset('gallery_button', {
anchorX: 0.5,
anchorY: 0.5
});
var galleryButton = new Container();
galleryButton.addChild(galleryBtnGraphic);
galleryButton.x = 1800;
galleryButton.y = 500;
game.addChild(galleryButton);
var galleryText = new Text2('GALLERY', {
size: 50,
fill: '#ffffff'
});
galleryText.anchor.set(0.5, 0.5);
galleryButton.addChild(galleryText);
// Debug Reset Button
/*
var debugBtnGraphic = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
var debugButton = new Container();
debugButton.addChild(debugBtnGraphic);
debugButton.x = 300;
debugButton.y = 100;
game.addChild(debugButton);
var debugText = new Text2('RESET', {
size: 40,
fill: '#ff4444'
});
debugText.anchor.set(0.5, 0.5);
debugButton.addChild(debugText);*/
// Upgrade Levels Container (A LA IZQUIERDA Y MÁS GRANDES)
var levelContainers = [];
var levelStartY = 500; // Empezamos un poco más arriba
var levelSpacing = 180; // Más espacio vertical porque los botones crecieron
for (var i = 0; i < 10; i++) {
var levelContainer = new IncomeLevel();
levelContainer.init(i, [1, 2, 3, 5, 10, 20, 50, 100, 200, 300][i]);
levelContainer.updateData(gameState.incomeLevels[i]);
levelContainer.x = 512; // 1/4 de la pantalla hacia la izquierda
levelContainer.y = levelStartY + i * levelSpacing;
game.addChild(levelContainer);
levelContainers.push(levelContainer);
}
// Modal Variables
var modalOpen = false;
var modalContainer = null;
// --- Funciones Lógicas ---
function openGallery() {
if (modalOpen) {
return;
}
modalOpen = true;
galleryState.currentPage = 0;
galleryState.selectedElementId = 0;
// Buscar el primer personaje recolectado para mostrarlo por defecto al abrir
for (var s = 0; s < characterDatabase.length; s++) {
if (gameState.collectedCharacters[s]) {
galleryState.selectedElementId = s;
break;
}
}
modalContainer = new Container();
var modalBg = LK.getAsset('modal_bg', {
anchorX: 0.5,
anchorY: 0.5
});
modalContainer.addChild(modalBg);
modalContainer.x = 1024;
modalContainer.y = 1366;
var galleryTitle = new Text2('Element Gallery', {
size: 80,
fill: '#FFD700'
});
galleryTitle.anchor.set(0.5, 0);
galleryTitle.x = 0;
galleryTitle.y = -1100;
modalContainer.addChild(galleryTitle);
var closeBtnGraphic = LK.getAsset('modal_close_btn', {
anchorX: 0.5,
anchorY: 0.5
});
var closeBtn = new Container();
closeBtn.addChild(closeBtnGraphic);
closeBtn.x = 800;
closeBtn.y = -1050;
modalContainer.addChild(closeBtn);
var closeBtnText = new Text2('X', {
size: 60,
fill: '#ffffff'
});
closeBtnText.anchor.set(0.5, 0.5);
closeBtn.addChild(closeBtnText);
closeBtn.down = function () {
closeModal();
LK.getSound('ui_click').play();
};
// Contenedor dinámico que se refresca
var contentContainer = new Container();
modalContainer.addChild(contentContainer);
function renderGalleryContent() {
while (contentContainer.children.length > 0) {
contentContainer.children[0].destroy();
}
var totalItems = galleryDisplayOrder.length;
var totalPages = Math.ceil(totalItems / galleryState.itemsPerPage);
var startIndex = galleryState.currentPage * galleryState.itemsPerPage;
var endIndex = Math.min(startIndex + galleryState.itemsPerPage, totalItems);
// --- PANEL IZQUIERDO: CUADRÍCULA 3x3 ---
var gridStartX = -750;
var gridStartY = -700;
var spacing = 400;
for (var i = startIndex; i < endIndex; i++) {
var localIndex = i - startIndex;
var col = localIndex % 3;
var row = Math.floor(localIndex / 3);
// AHORA BUSCAMOS EL ID BASADO EN EL NUEVO ORDEN
var actualCharId = galleryDisplayOrder[i];
var charData = characterDatabase[actualCharId];
var isCollected = gameState.collectedCharacters[actualCharId];
var displayChar = new Character();
var displayName = isCollected ? charData.name : "???";
// Siempre revelamos la rareza (Spoiler visual)
var displayRarity = charData.rarity;
displayChar.init(actualCharId, displayName, displayRarity, charData.asset, charData.multiplier);
displayChar.x = gridStartX + col * spacing;
displayChar.y = gridStartY + row * (spacing * 1.2);
// Efecto Silueta Inteligente
if (!isCollected) {
displayChar.graphicObj.tint = 0x000000; // Personaje en negro
displayChar.nameObj.fill = '#555555'; // Letras del nombre en gris
displayChar.frameObj.alpha = 0.05; // Apagamos casi todo el brillo del marco
}
// Indicador visual de elemento seleccionado
if (galleryState.selectedElementId === actualCharId) {
displayChar.scale.set(1.15, 1.15); // Ligeramente más grande al seleccionarlo
}
(function (idToSelect) {
displayChar.down = function () {
if (galleryState.selectedElementId !== idToSelect) {
galleryState.selectedElementId = idToSelect;
LK.getSound('ui_click').play();
renderGalleryContent();
}
};
})(actualCharId);
contentContainer.addChild(displayChar);
}
var pageText = new Text2('Page ' + (galleryState.currentPage + 1) + ' / ' + totalPages, {
size: 80,
fill: '#FFFFFF'
});
pageText.anchor.set(0.5, 0.5);
pageText.x = -300;
pageText.y = 800;
contentContainer.addChild(pageText);
var collectionStatus = new Text2('Collected: ' + gameState.totalCharactersCollected + '/' + characterDatabase.length, {
size: 60,
fill: '#87CEEB'
});
collectionStatus.anchor.set(0.5, 0.5);
collectionStatus.x = -300;
collectionStatus.y = 900;
contentContainer.addChild(collectionStatus);
if (galleryState.currentPage > 0) {
var prevBtn = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
prevBtn.scale.set(0.6, 0.8);
prevBtn.x = -550;
prevBtn.y = 650;
var prevTxt = new Text2('< PREV', {
size: 70,
fill: '#FFF'
});
prevTxt.anchor.set(0.5, 0.5);
prevBtn.addChild(prevTxt);
prevBtn.down = function () {
galleryState.currentPage--;
LK.getSound('ui_click').play();
renderGalleryContent();
};
contentContainer.addChild(prevBtn);
}
if (galleryState.currentPage < totalPages - 1) {
var nextBtn = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
nextBtn.scale.set(0.6, 0.8);
nextBtn.x = -50;
nextBtn.y = 650;
var nextTxt = new Text2('NEXT >', {
size: 70,
fill: '#FFF'
});
nextTxt.anchor.set(0.5, 0.5);
nextBtn.addChild(nextTxt);
nextBtn.down = function () {
galleryState.currentPage++;
LK.getSound('ui_click').play();
renderGalleryContent();
};
contentContainer.addChild(nextBtn);
}
// --- PANEL DERECHO: DETALLES Y EQUIPAR ---
var rightPanelX = 550;
var detailData = characterDatabase[galleryState.selectedElementId];
var isDetailCollected = gameState.collectedCharacters[galleryState.selectedElementId];
if (detailData) {
var bigGraphic = LK.getAsset(detailData.asset, {
anchorX: 0.5,
anchorY: 0.5
});
bigGraphic.scale.set(1.35, 1.35);
bigGraphic.x = rightPanelX;
bigGraphic.y = -250;
if (!isDetailCollected) {
bigGraphic.tint = 0x000000;
}
contentContainer.addChild(bigGraphic);
var detailNameText = isDetailCollected ? detailData.name : "???";
var detailName = new Text2(detailNameText, {
size: 80,
fill: '#FFF'
});
detailName.anchor.set(0.5, 0);
detailName.x = rightPanelX;
detailName.y = 200;
contentContainer.addChild(detailName);
// Mapa de colores String para la UI de detalles
var rarityColorHexStr = GAME_CONFIG.RARITIES[detailData.rarity] ? GAME_CONFIG.RARITIES[detailData.rarity].colorStr : '#FFFFFF';
var detailRarity = new Text2('[' + detailData.rarity + ']', {
size: 60,
fill: rarityColorHexStr
});
detailRarity.anchor.set(0.5, 0);
detailRarity.x = rightPanelX;
detailRarity.y = 280;
contentContainer.addChild(detailRarity);
if (isDetailCollected) {
var incomeInfo = new Text2('Income Boost: +' + detailData.multiplier + '/tick', {
size: 55,
fill: '#00FF00'
});
incomeInfo.anchor.set(0.5, 0);
incomeInfo.x = rightPanelX;
incomeInfo.y = 350;
contentContainer.addChild(incomeInfo);
}
// LÓGICA DEL BOTÓN EQUIPAR
var orbitStatusText = new Text2('Orbiting: ' + gameState.equippedCharacters.length + ' / ' + GAME_CONFIG.MAX_EQUIPPED, {
size: 60,
fill: '#87CEEB'
});
orbitStatusText.anchor.set(0.5, 0);
orbitStatusText.x = rightPanelX;
orbitStatusText.y = 450;
contentContainer.addChild(orbitStatusText);
if (isDetailCollected) {
var isEquipped = gameState.equippedCharacters.indexOf(galleryState.selectedElementId) > -1;
var equipBtnGraphic = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
equipBtnGraphic.scale.set(1.5, 1.2);
equipBtnGraphic.tint = isEquipped ? 0xFF4444 : 0x32CD32;
var equipBtn = new Container();
equipBtn.addChild(equipBtnGraphic);
equipBtn.x = rightPanelX;
equipBtn.y = 650;
var equipBtnText = new Text2(isEquipped ? 'UNEQUIP' : 'EQUIP', {
size: 50,
fill: '#ffffff'
});
equipBtnText.anchor.set(0.5, 0.5);
equipBtn.addChild(equipBtnText);
equipBtn.down = function () {
if (isEquipped) {
var index = gameState.equippedCharacters.indexOf(galleryState.selectedElementId);
gameState.equippedCharacters.splice(index, 1);
LK.getSound('ui_click').play();
} else {
if (gameState.equippedCharacters.length >= GAME_CONFIG.MAX_EQUIPPED) {
// Órbita llena: mostrar texto de aviso en lugar de desplazar silenciosamente
var fullMsg = new FloatingText('Orbit full! Unequip one first.', 1024, 1366, '#FF4444');
game.addChild(fullMsg);
return;
}
gameState.equippedCharacters.push(galleryState.selectedElementId);
LK.getSound('ui_click').play();
}
updateEquippedDisplay();
saveGame();
renderGalleryContent();
};
contentContainer.addChild(equipBtn);
} else {
var lockText = new Text2('LOCKED', {
size: 50,
fill: '#FF4444'
});
lockText.anchor.set(0.5, 0.5);
lockText.x = rightPanelX;
lockText.y = 400;
contentContainer.addChild(lockText);
}
}
}
renderGalleryContent();
game.addChild(modalContainer);
}
function closeModal() {
if (modalContainer) {
modalContainer.destroy();
modalOpen = false;
}
}
// --- Lógica de tirada de Gacha centralizada ---
// Retorna una rareza de pity (Special, Legendary o Unique)
function rollPityRarity() {
var pityRoll = Math.random() * 100;
if (pityRoll <= 60) {
return {
rarity: 'Special',
ids: getIdsByRarity('Special')
};
} else if (pityRoll <= 90) {
return {
rarity: 'Legendary',
ids: getIdsByRarity('Legendary')
};
} else {
return {
rarity: 'Unique',
ids: getIdsByRarity('Unique')
};
}
}
// Tirada normal con soft pity: a partir del pull 6, la probabilidad de rarezas altas sube gradualmente
function rollNormalRarity() {
var softBonus = Math.max(0, gameState.pityCounter - 5) * 3;
var dynamicRates = [];
for (var i = 0; i < dropRates.length; i++) {
var rate = {
rarity: dropRates[i].rarity,
ids: dropRates[i].ids,
chance: dropRates[i].chance
};
if (rate.rarity === 'Special') {
rate.chance = Math.min(30, 6 + softBonus);
} else if (rate.rarity === 'Legendary') {
rate.chance = Math.min(20, 3 + softBonus * 0.5);
} else if (rate.rarity === 'Unique') {
rate.chance = Math.min(10, 1 + softBonus * 0.25);
}
dynamicRates.push(rate);
}
var roll = Math.random() * 100;
var cumulativeChance = 0;
for (var j = 0; j < dynamicRates.length; j++) {
cumulativeChance += dynamicRates[j].chance;
if (roll <= cumulativeChance) {
return dynamicRates[j];
}
}
return dynamicRates[0];
}
// Registra un personaje obtenido y retorna si es nuevo
function registerCharacterObtained(charId) {
var charData = characterDatabase[charId];
if (!gameState.collectedCharacters[charId]) {
gameState.collectedCharacters[charId] = true;
gameState.totalCharactersCollected++;
gameState.incomePerTick += charData.multiplier;
return true;
}
return false;
}
function performGachaPull() {
var currentCost = getGachaCost();
if (gameState.credits < currentCost) {
return;
}
gameState.credits -= currentCost;
gameState.pityCounter++;
var selectedRarity = null;
// Hard Pity en 10: garantiza rareza alta
if (gameState.pityCounter >= 10) {
selectedRarity = rollPityRarity();
gameState.pityCounter = 0;
} else {
// Soft pity: probabilidades aumentan gradualmente desde pull 6
selectedRarity = rollNormalRarity();
if (selectedRarity.rarity === 'Special' || selectedRarity.rarity === 'Legendary' || selectedRarity.rarity === 'Unique') {
gameState.pityCounter = 0;
}
}
var characterIdArray = selectedRarity.ids;
var selectedCharacterId = characterIdArray[Math.floor(Math.random() * characterIdArray.length)];
var wasNew = registerCharacterObtained(selectedCharacterId);
var charData = characterDatabase[selectedCharacterId];
saveGame();
LK.getSound('gacha_pull').play();
if (wasNew) {
LK.getSound('gacha_pull_success').play();
}
modalOpen = true; // Bloquear UI durante la animación del resultado
// Consumimos los efectos visuales desde la configuración global
var currentEffect = GAME_CONFIG.RARITIES[charData.rarity] || {
colorNum: 0xFFFFFF,
scale: 0.70
};
var resultMessage = wasNew ? 'NEW: ' + charData.name + '!' : 'Duplicate: ' + charData.name;
var resultColor = wasNew ? '#2d633b' : '#b89e14';
var resultContainer = new Container();
var resultBg = LK.getAsset('pull_bg', {
anchorX: 0.5,
anchorY: 0.5
});
resultContainer.addChild(resultBg);
resultContainer.x = 1024;
resultContainer.y = 1366;
resultContainer.scale.set(0.1, 0.1);
tween(resultContainer, {
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
var aura = LK.getAsset('core_crystal', {
anchorX: 0.5,
anchorY: 0.5
});
aura.tint = currentEffect.colorNum;
var auraScaleFactor = currentEffect.scale * 2.5;
aura.scale.set(auraScaleFactor, auraScaleFactor);
aura.alpha = 0.5;
resultContainer.addChild(aura);
tween(aura, {
rotation: Math.PI * 2
}, {
duration: 2000
});
var resultGraphic = LK.getAsset(charData.asset, {
anchorX: 0.5,
anchorY: 0.5
});
var scaleTarget = currentEffect.scale;
resultGraphic.scale.set(0.1, 0.1);
tween(resultGraphic, {
scaleX: scaleTarget + 0.05,
scaleY: scaleTarget + 0.05
}, {
duration: 250,
onFinish: function onFinish() {
tween(resultGraphic, {
scaleX: scaleTarget,
scaleY: scaleTarget
}, {
duration: 150
});
}
});
resultGraphic.y = 0;
resultContainer.addChild(resultGraphic);
var safePadding = 720 * scaleTarget / 2 + 80;
var resultTitle = new Text2(resultMessage, {
size: 150,
fill: resultColor
});
resultTitle.anchor.set(0.5, 0.5);
resultTitle.y = -safePadding;
resultContainer.addChild(resultTitle);
var rarityDisplayColor = GAME_CONFIG.RARITIES[charData.rarity] ? GAME_CONFIG.RARITIES[charData.rarity].colorStr : '#FFFFFF';
var resultRarity = new Text2('[' + charData.rarity + ']', {
size: 90,
fill: rarityDisplayColor
});
resultRarity.anchor.set(0.5, 0.5);
resultRarity.y = safePadding;
resultContainer.addChild(resultRarity);
game.addChild(resultContainer);
LK.setTimeout(function () {
tween(resultContainer, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 200,
onFinish: function onFinish() {
resultContainer.destroy();
modalOpen = false; // Liberar UI al cerrar el resultado
}
});
}, 2000);
}
function performGachaPull10() {
var singleCost = getGachaCost();
var totalCost = singleCost * 10;
if (gameState.credits < totalCost) {
return;
}
gameState.credits -= totalCost;
var pullResults = [];
// 1. Las 9 tiradas normales (con soft pity activo)
for (var i = 0; i < 9; i++) {
gameState.pityCounter++;
var selectedRarity = rollNormalRarity();
if (selectedRarity.rarity === 'Special' || selectedRarity.rarity === 'Legendary' || selectedRarity.rarity === 'Unique') {
gameState.pityCounter = 0;
}
var charIdArray = selectedRarity.ids;
var selectedCharId = charIdArray[Math.floor(Math.random() * charIdArray.length)];
pullResults.push(characterDatabase[selectedCharId]);
registerCharacterObtained(selectedCharId);
}
// 2. La 10ª tirada (Lástima Garantizada — siempre rareza alta)
var pityRarity = rollPityRarity();
var pityCharIdArray = pityRarity.ids;
var pityCharId = pityCharIdArray[Math.floor(Math.random() * pityCharIdArray.length)];
pullResults.push(characterDatabase[pityCharId]);
registerCharacterObtained(pityCharId);
// Reiniciamos el pity porque acabamos de dar uno garantizado
gameState.pityCounter = 0;
saveGame();
LK.getSound('gacha_pull').play();
showMultiPullResults(pullResults);
}
function showMultiPullResults(results) {
modalOpen = true; // Bloquea la UI de fondo
var resultContainer = new Container();
var modalBg = LK.getAsset('modal_bg', {
anchorX: 0.5,
anchorY: 0.5
});
resultContainer.addChild(modalBg);
resultContainer.x = 1024;
resultContainer.y = 1366;
resultContainer.scale.set(0.1, 0.1);
tween(resultContainer, {
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
var titleText = new Text2('10x PULL RESULTS', {
size: 100,
fill: '#FFD700'
});
titleText.anchor.set(0.5, 0.5);
titleText.y = -900;
resultContainer.addChild(titleText);
// Configuramos la cuadrícula (2 filas x 5 columnas)
var startX = -600;
var startY = -400;
var spacingX = 300;
var spacingY = 400;
for (var i = 0; i < results.length; i++) {
var charData = results[i];
var col = i % 5;
var row = Math.floor(i / 5);
// Si es de rareza alta, le ponemos un aura detrás
if (charData.rarity === 'Special' || charData.rarity === 'Legendary' || charData.rarity === 'Unique') {
var aura = LK.getAsset('core_crystal', {
anchorX: 0.5,
anchorY: 0.5
});
aura.x = startX + col * spacingX;
aura.y = startY + row * spacingY;
aura.scale.set(1.5, 1.5);
aura.tint = 0xFFD700;
aura.alpha = 0.5;
resultContainer.addChild(aura);
tween(aura, {
rotation: Math.PI * 2
}, {
duration: 2000
});
}
var charGraphic = LK.getAsset(charData.asset, {
anchorX: 0.5,
anchorY: 0.5
});
charGraphic.x = startX + col * spacingX;
charGraphic.y = startY + row * spacingY;
charGraphic.scale.set(0.35, 0.35); // Más pequeños para que quepan
resultContainer.addChild(charGraphic);
}
// Botón para cerrar
var closeBtnGraphic = LK.getAsset('button_bg', {
anchorX: 0.5,
anchorY: 0.5
});
var closeBtn = new Container();
closeBtn.addChild(closeBtnGraphic);
closeBtn.y = 800;
resultContainer.addChild(closeBtn);
var closeText = new Text2('CONTINUE', {
size: 60,
fill: '#FFFFFF'
});
closeText.anchor.set(0.5, 0.5);
closeBtn.addChild(closeText);
closeBtn.down = function () {
LK.getSound('ui_click').play();
tween(resultContainer, {
alpha: 0,
scaleX: 0.5,
scaleY: 0.5
}, {
duration: 200,
onFinish: function onFinish() {
resultContainer.destroy();
modalOpen = false; // Liberamos la UI
}
});
};
game.addChild(resultContainer);
}
function upgradeIncomeLevel(levelIndex) {
var level = levelContainers[levelIndex];
if (gameState.credits >= level.currentCost) {
gameState.credits -= level.currentCost;
gameState.incomeLevels[levelIndex]++;
gameState.incomePerTick += level.baseIncome;
level.updateData(gameState.incomeLevels[levelIndex]);
saveGame();
LK.getSound('level_up').play();
// Confirmar visualmente la compra
var floatUpgrade = new FloatingText('+' + level.baseIncome + '/tick', level.x + (Math.random() * 60 - 30), level.y - 60, '#00FF00');
game.addChild(floatUpgrade);
}
}
// --- Event Handlers Directos ---
gachaButton.down = function () {
if (modalOpen) {
return;
}
if (gachaEnabled) {
performGachaPull();
LK.getSound('ui_click').play();
}
};
gacha10Button.down = function () {
if (modalOpen) {
return;
}
if (gacha10Enabled) {
performGachaPull10();
}
};
galleryButton.down = function () {
if (modalOpen) {
return;
}
openGallery();
LK.getSound('ui_click').play();
};
/*
debugButton.down = function () {
if (modalOpen) {
return;
}
gameState.credits = 0;
gameState.incomePerTick = 1;
gameState.collectedCharacters = {};
gameState.totalCharactersCollected = 0;
gameState.incomeLevels = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
gameState.equippedCharacters = [];
for (var i = 0; i < levelContainers.length; i++) {
levelContainers[i].updateData(0);
}
updateEquippedDisplay();
saveGame();
LK.getSound('ui_click').play();
};
*/
coreButton.down = function () {
if (modalOpen) {
return;
}
var clickGain = 1 + Math.floor(gameState.incomePerTick * 0.2);
gameState.credits += clickGain;
var floatText = new FloatingText('+' + clickGain, coreButton.x + (Math.random() * 100 - 50), coreButton.y - 150, '#00ffff');
game.addChild(floatText);
tween(coreGraphic, {
scaleX: 1.1,
scaleY: 1.1
}, {
duration: 50,
onFinish: function onFinish() {
tween(coreGraphic, {
scaleX: 1,
scaleY: 1
}, {
duration: 50
});
}
});
};
for (var i = 0; i < levelContainers.length; i++) {
(function (index) {
levelContainers[index].down = function () {
if (modalOpen) {
return;
}
upgradeIncomeLevel(index);
};
})(i);
}
// --- Variables para el Main Loop ---
var pulseTimer = 0;
var lastDisplayedCredits = -1;
var lastDisplayedIncome = -1;
var lastDisplayedCollection = -1;
var lastDisplayedGachaCost = -1;
var lastDisplayedPity = -1;
// --- Main Game Loop Optimizado ---
game.update = function () {
gameState.tickCounter++;
// LÓGICA DE ÓRBITA
var numOrbiting = orbitingSprites.length;
if (numOrbiting > 0) {
orbitAngle += GAME_CONFIG.ORBIT_SPEED;
var angleStep = Math.PI * 2 / numOrbiting;
for (var o = 0; o < numOrbiting; o++) {
var currentAngle = orbitAngle + o * angleStep;
orbitingSprites[o].x = Math.cos(currentAngle) * GAME_CONFIG.ORBIT_RADIUS;
orbitingSprites[o].y = Math.sin(currentAngle) * GAME_CONFIG.ORBIT_RADIUS;
//orbitingSprites[o].rotation += 0.01;
}
}
if (gameState.tickCounter % GAME_CONFIG.FPS === 0) {
gameState.credits += gameState.incomePerTick;
}
// --- Actualización de Costos y Textos ---
var currentCredits = Math.floor(gameState.credits);
var currentGachaCost = getGachaCost();
var currentGachaCost10 = currentGachaCost * 10;
// === INICIO DEL BLOQUE FALTANTE ===
if (currentCredits !== lastDisplayedCredits) {
creditsText.setText('Credits: ' + currentCredits);
lastDisplayedCredits = currentCredits;
}
if (gameState.incomePerTick !== lastDisplayedIncome) {
incomeText.setText('Income: ' + gameState.incomePerTick + '/tick');
lastDisplayedIncome = gameState.incomePerTick;
}
if (gameState.totalCharactersCollected !== lastDisplayedCollection) {
// Usamos characterDatabase.length para que se actualice solo si agregas más en el futuro
collectionText.setText('Collection: ' + gameState.totalCharactersCollected + '/' + characterDatabase.length);
lastDisplayedCollection = gameState.totalCharactersCollected;
}
if (currentGachaCost !== lastDisplayedGachaCost || gameState.pityCounter !== lastDisplayedPity) {
// Actualiza textos Botón x1
gachaText.setText('PULL (' + currentGachaCost + ')');
if (gameState.pityCounter >= 9) {
pityText.setText('Pity: READY!');
pityText.fill = '#00FF00';
} else if (gameState.pityCounter >= 6) {
// Soft pity activo: probabilidades subiendo
pityText.setText('Pity: ' + gameState.pityCounter + "/10 \u2191");
pityText.fill = '#FFA500';
} else {
pityText.setText('Pity: ' + gameState.pityCounter + '/10');
pityText.fill = '#FFD700';
}
// Actualiza textos Botón x10
gacha10Text.setText('10x (' + currentGachaCost10 + ')');
lastDisplayedGachaCost = currentGachaCost;
lastDisplayedPity = gameState.pityCounter;
}
// --- Control Visual de Botones Disponibles ---
gachaEnabled = gameState.credits >= currentGachaCost;
gacha10Enabled = gameState.credits >= currentGachaCost10;
// Efectos del Botón x1
if (gachaEnabled) {
gachaBtnGraphic.tint = 0xFF1493;
gachaText.tint = 0xFFFFFF;
pulseTimer += 0.05;
var scaleX = 1.6 + Math.sin(pulseTimer) * 0.05;
var scaleY = 1.8 + Math.sin(pulseTimer) * 0.05;
gachaBtnGraphic.scale.set(scaleX, scaleY);
} else {
gachaBtnGraphic.tint = 0x883366;
gachaText.tint = 0xAAAAAA;
gachaBtnGraphic.scale.set(1.6, 1.8);
}
// Efectos del Botón x10
if (gacha10Enabled) {
gacha10BtnGraphic.tint = 0xFF1493;
gacha10Text.tint = 0xFFFFFF;
var scaleX10 = 1.6 + Math.cos(pulseTimer) * 0.05; // Usamos cos para desfasar el pulso visual
var scaleY10 = 1.8 + Math.cos(pulseTimer) * 0.05;
gacha10BtnGraphic.scale.set(scaleX10, scaleY10);
} else {
gacha10BtnGraphic.tint = 0x883366;
gacha10Text.tint = 0xAAAAAA;
gacha10BtnGraphic.scale.set(1.6, 1.8);
}
for (var j = 0; j < levelContainers.length; j++) {
var level = levelContainers[j];
if (gameState.credits >= level.currentCost) {
level.children[0].tint = 0xffffff;
} else {
level.children[0].tint = 0x525252;
}
}
if (gameState.tickCounter % GAME_CONFIG.SAVE_INTERVAL === 0) {
saveGame();
}
};
Crea un rectángulo para asset de botón. Debe ser: color amarillo, rojo, verde, azul, gris, rosa dispuestos en forma diagonal irregular por el rectángulo. Los colores deben ser planos, y los bordes redondeados. In-Game asset. 2d. High contrast. No shadows. anime. simple
Crea un rectángulo para asset de botón. Debe ser: color rojo escarlata con borde en forma de rectángulo. Los colores deben ser planos, y los bordes redondeados. In-Game asset. 2d. High contrast. No shadows. anime. simple
Crea un rectángulo para asset de botón. Debe ser: color verde esmeralda con borde en forma de rectángulo. Los colores deben ser planos, y los bordes redondeados. In-Game asset. 2d. High contrast. No shadows. anime. simple
Crea un fondo de textura verde tipo anime. In-Game asset. 2d. High contrast. No shadows. Background. anime