PageSourceSearch

https://seleccionados.es/keytwister/js/data/unlockableDefs.js

js seleccionados.es collected 2026-09-24 15:00:47 UTC 36,635 bytes, 675 lines download raw bytes

1// ══════════════════════════════════════════════
2//  unlockableDefs.js — Content gating definitions
3//
4//  Items NOT listed here → always in the base pool.
5//  Items listed here → locked until condition met.
6//
7//  Pools: joker | consumable | mutator | shop_variant
8//
9//  BASE POOLS (always available, no unlock needed):
10//    Jokers (6):  piano_fingers, rubber_hands, tempo,
11//                 piggy_bank, rubber_soul, lucky_star
12//    Consumables (5): time_freeze, key_insight, erase_strike,
13//                     emergency_fund, compass
14//    Mutators (12): speedup, fragile, slow_mo, merciful,
15//                   quick_hold, double_points, generosity,
16//                   momentum, golden_floor, second_chance_mut,
17//                   warmup, tailwind
18//
19//  7 TIERS — gradual unlock across many runs:
20//    T1 Primeros Pasos  (runs 2-3)     9 items
21//    T2 Aprendiz        (runs 4-7)    12 items
22//    T3 Competente      (runs 7-12)   16 items
23//    T4 Veterano        (runs 12-20)  18 items
24//    T5 Experto         (runs 20-30)  22 items
25//    T6 Maestro         (runs 30-50)  22 items
26//    T7 Exótico         (runs 50+)    16 items
27//  Total gated: 115 (45J + 30C + 38M + 2SV)
28// ══════════════════════════════════════════════
29
30import { t } from '../i18n.js';
31
32/** @type {UnlockableDef[]} */
33const UNLOCKABLE_DEFS = [
34
35  // ═══════════════════════════════════════════
36  //  TIER 1 — PRIMEROS PASOS (runs 2-3)
37  //  First unlocks, rewarding early play
38  // ═══════════════════════════════════════════
39
40  // ── Jokers T1 ──
41  { id:"velocity", pool:"joker",
42    condition:(d)=>(d.totalRuns??0)>=2,
43    get hint(){ return t('unlock_hints.velocity_hint','Juega un par de runs...'); },
44    get reason(){ return t('unlock_reasons.velocity','¡Velocidad desbloqueada! Speed bonus amplificado.'); } },
45
46  { id:"slow_starter", pool:"joker",
47    condition:(d)=>(d.totalRounds??0)>=25,
48    get hint(){ return t('unlock_hints.slow_starter_hint','Supera varias rondas...'); },
49    get reason(){ return t('unlock_reasons.slow_starter','¡Arranque Lento! Más tiempo en la primera ronda.'); } },
50
51  { id:"penny_pincher", pool:"joker",
52    condition:(d)=>(d.totalCoins??0)>=150,
53    get hint(){ return t('unlock_hints.penny_pincher_hint','Gana algunas coins...'); },
54    get reason(){ return t('unlock_reasons.penny_pincher','¡Tacaño desbloqueado! Rerolls más baratos.'); } },
55
56  // ── Consumables T1 ──
57  { id:"coin_burst", pool:"consumable",
58    condition:(d)=>(d.totalFloors??0)>=8,
59    get hint(){ return t('unlock_hints.coin_burst_hint','Completa algunos pisos...'); },
60    get reason(){ return t('unlock_reasons.coin_burst','¡Lluvia de Coins! Gana coins al instante.'); } },
61
62  { id:"power_check", pool:"consumable",
63    condition:(d)=>(d.totalChecksPassed??0)>=50,
64    get hint(){ return t('unlock_hints.power_check_hint','Pasa tus primeros checks...'); },
65    get reason(){ return t('unlock_reasons.power_check','¡Pulso Preciso! Ventana de check ampliada.'); } },
66
67  { id:"anchor", pool:"consumable",
68    condition:(d)=>(d.records?.bestStreak??0)>=5&&(d.totalRuns??0)>=2,
69    get hint(){ return t('unlock_hints.anchor_hint','Consigue una pequeña racha...'); },
70    get reason(){ return t('unlock_reasons.anchor','¡Ancla desbloqueada! Protege tu racha.'); } },
71
72  // ── Mutators T1 ──
73  { id:"jackpot", pool:"mutator",
74    condition:(d)=>(d.totalPerfectRounds??0)>=6,
75    get hint(){ return t('unlock_hints.jackpot_hint','Logra rondas perfectas...'); },
76    get reason(){ return t('unlock_reasons.jackpot','¡Jackpot! Perfectas pueden dar coins bonus.'); } },
77
78  { id:"treasure_floor", pool:"mutator",
79    condition:(d)=>(d.totalRuns??0)>=3,
80    get hint(){ return t('unlock_hints.treasure_floor_hint','Juega algunas runs...'); },
81    get reason(){ return t('unlock_reasons.treasure_floor','¡Piso del Tesoro! Cada ronda da coins.'); } },
82
83  { id:"phantom", pool:"mutator",
84    condition:(d)=>(d.totalFloors??0)>=12,
85    get hint(){ return t('unlock_hints.phantom_hint','Completa más pisos...'); },
86    get reason(){ return t('unlock_reasons.phantom','¡Fantasma! Aparecen teclas falsas.'); } },
87
88  // ═══════════════════════════════════════════
89  //  TIER 2 — APRENDIZ (runs 4-7)
90  //  Expanding the toolkit
91  // ═══════════════════════════════════════════
92
93  // ── Jokers T2 ──
94  { id:"coin_magnet", pool:"joker",
95    condition:(d)=>(d.totalFloors??0)>=25,
96    get hint(){ return t('unlock_hints.coin_magnet_hint','Completa bastantes pisos...'); },
97    get reason(){ return t('unlock_reasons.coin_magnet','¡Imán de Coins! +20% coins por ronda.'); } },
98
99  { id:"lucky_dice", pool:"joker",
100    condition:(d)=>(d.totalCoins??0)>=400,
101    get hint(){ return t('unlock_hints.lucky_dice_hint','Acumula coins...'); },
102    get reason(){ return t('unlock_reasons.lucky_dice','¡Dado de la Suerte! Chance de duplicar coins.'); } },
103
104  { id:"pain_lover", pool:"joker",
105    condition:(d)=>(d.totalRuns??0)>=5,
106    get hint(){ return t('unlock_hints.pain_lover_hint','Persiste varias runs...'); },
107    get reason(){ return t('unlock_reasons.pain_lover','¡Amante del Dolor! Bonus por dificultad alta.'); } },
108
109  { id:"turbo", pool:"joker",
110    condition:(d)=>(d.totalChecksPassed??0)>=150,
111    get hint(){ return t('unlock_hints.turbo_hint','Pasa más checks...'); },
112    get reason(){ return t('unlock_reasons.turbo','¡Turbo desbloqueado! Construye rápido para checks extra.'); } },
113
114  // ── Consumables T2 ──
115  { id:"shield", pool:"consumable",
116    condition:(d)=>(d.totalRuns??0)>=4,
117    get hint(){ return t('unlock_hints.shield_hint','Juega más runs...'); },
118    get reason(){ return t('unlock_reasons.shield','¡Escudo desbloqueado! Ignora un fallo.'); } },
119
120  { id:"simplify", pool:"consumable",
121    condition:(d)=>(d.totalRounds??0)>=55,
122    get hint(){ return t('unlock_hints.simplify_hint','Supera muchas rondas...'); },
123    get reason(){ return t('unlock_reasons.simplify','¡Simplificar! Quita una tecla del combo.'); } },
124
125  { id:"double_next", pool:"consumable",
126    condition:(d)=>(d.records?.bestFloor??0)>=5,
127    get hint(){ return t('unlock_hints.double_next_hint','Alcanza cierto piso...'); },
128    get reason(){ return t('unlock_reasons.double_next','¡Doble o Nada! Arriesga por puntos dobles.'); } },
129
130  { id:"safety_bubble", pool:"consumable",
131    condition:(d)=>(d.totalStrikeRepairs??0)>=4,
132    get hint(){ return t('unlock_hints.safety_bubble_hint','Repara strikes...'); },
133    get reason(){ return t('unlock_reasons.safety_bubble','¡Burbuja Segura! Absorbe checks fallidos.'); } },
134
135  // ── Mutators T2 ──
136  { id:"fog", pool:"mutator",
137    condition:(d)=>(d.records?.bestFloor??0)>=6,
138    get hint(){ return t('unlock_hints.fog_hint','Avanza más pisos...'); },
139    get reason(){ return t('unlock_reasons.fog','¡Niebla! Una tecla se oculta.'); } },
140
141  { id:"mirror", pool:"mutator",
142    condition:(d)=>(d.totalFloors??0)>=35,
143    get hint(){ return t('unlock_hints.mirror_hint','Explora más pisos...'); },
144    get reason(){ return t('unlock_reasons.mirror','¡Espejo! El teclado se inv
144ierte.'); } },
145
146  { id:"sprint", pool:"mutator",
147    condition:(d)=>(d.totalRuns??0)>=6,
148    get hint(){ return t('unlock_hints.sprint_hint','Más experiencia...'); },
149    get reason(){ return t('unlock_reasons.sprint','¡Sprint! Construye rápido o sufre penalización.'); } },
150
151  { id:"iron_fingers", pool:"mutator",
152    condition:(d)=>(d.totalChecksPassed??0)>=180,
153    get hint(){ return t('unlock_hints.iron_fingers_hint','Pasa muchos checks...'); },
154    get reason(){ return t('unlock_reasons.iron_fingers','¡Dedos de Hierro! Check window reducida.'); } },
155
156  // ═══════════════════════════════════════════
157  //  TIER 3 — COMPETENTE (runs 7-12)
158  //  Unlocking real strategy options
159  // ═══════════════════════════════════════════
160
161  // ── Jokers T3 ──
162  { id:"hot_hand", pool:"joker",
163    condition:(d)=>(d.records?.bestStreak??0)>=10&&(d.totalRuns??0)>=7,
164    get hint(){ return t('unlock_hints.hot_hand_hint','Consigue una buena racha...'); },
165    get reason(){ return t('unlock_reasons.hot_hand','¡Mano Caliente! Rachas largas mejoran tus checks.'); } },
166
167  { id:"flash_typist", pool:"joker",
168    condition:(d)=>(d.records?.bestFloor??0)>=7,
169    get hint(){ return t('unlock_hints.flash_typist_hint','Pisos avanzados...'); },
170    get reason(){ return t('unlock_reasons.flash_typist','¡Mecanógrafo Veloz! Construye rápido, gana más.'); } },
171
172  { id:"streak_master", pool:"joker",
173    condition:(d)=>(d.records?.bestStreak??0)>=12,
174    get hint(){ return t('unlock_hints.streak_master_hint','Racha impresionante...'); },
175    get reason(){ return t('unlock_reasons.streak_master','¡Maestro de Rachas desbloqueado!'); } },
176
177  { id:"golden_touch", pool:"joker",
178    condition:(d)=>(d.totalFlawlessFloors??0)>=6,
179    get hint(){ return t('unlock_hints.golden_touch_hint','Completa pisos sin fallos...'); },
180    get reason(){ return t('unlock_reasons.golden_touch','¡Toque Dorado! Bonus por pisos flawless.'); } },
181
182  { id:"quicksilver", pool:"joker",
183    condition:(d)=>(d.totalCoins??0)>=600,
184    get hint(){ return t('unlock_hints.quicksilver_hint','Acumula riqueza...'); },
185    get reason(){ return t('unlock_reasons.quicksilver','¡Mercurio! Velocidad genera coins.'); } },
186
187  // ── Consumables T3 ──
188  { id:"echo", pool:"consumable",
189    condition:(d)=>(d.totalChecksPassed??0)>=280,
190    get hint(){ return t('unlock_hints.echo_hint','Pasa muchos checks...'); },
191    get reason(){ return t('unlock_reasons.echo','¡Eco! Auto-pasa checks.'); } },
192
193  { id:"slow_mo_potion", pool:"consumable",
194    condition:(d)=>(d.totalRounds??0)>=100,
195    get hint(){ return t('unlock_hints.slow_mo_potion_hint','Supera rondas y rondas...'); },
196    get reason(){ return t('unlock_reasons.slow_mo_potion','¡Poción Lenta! +1.5 s durante 3 rondas.'); } },
197
198  { id:"quick_fix", pool:"consumable",
199    condition:(d)=>(d.totalStrikeRepairs??0)>=7,
200    get hint(){ return t('unlock_hints.quick_fix_hint','Repara varios strikes...'); },
201    get reason(){ return t('unlock_reasons.quick_fix','¡Parche Rápido! Cura + tiempo.'); } },
202
203  { id:"score_surge", pool:"consumable",
204    condition:(d)=>d.bestRuns?.some(r=>r.score>=2500),
205    get hint(){ return t('unlock_hints.score_surge_hint','Buen score en una run...'); },
206    get reason(){ return t('unlock_reasons.score_surge','¡Subidón! Puntos gratis.'); } },
207
208  // ── Mutators T3 ──
209  { id:"no_extras", pool:"mutator",
210    condition:(d)=>(d.totalRuns??0)>=8,
211    get hint(){ return t('unlock_hints.no_extras_hint','Veterano de runs...'); },
212    get reason(){ return t('unlock_reasons.no_extras','¡Sin Extras! Teclas extra = fallo.'); } },
213
214  { id:"growing", pool:"mutator",
215    condition:(d)=>(d.totalRounds??0)>=110,
216    get hint(){ return t('unlock_hints.growing_hint','Muchas rondas superadas...'); },
217    get reason(){ return t('unlock_reasons.growing','¡Creciente! Combos cada vez más grandes.'); } },
218
219  { id:"pulse", pool:"mutator",
220    condition:(d)=>(d.totalFloors??0)>=55,
221    get hint(){ return t('unlock_hints.pulse_hint','Explora muchos pisos...'); },
222    get reason(){ return t('unlock_reasons.pulse','¡Pulso! El timer se congela periódicamente.'); } },
223
224  { id:"acceleration", pool:"mutator",
225    condition:(d)=>(d.totalFloors??0)>=70,
226    get hint(){ return t('unlock_hints.acceleration_hint','Pisos y más pisos...'); },
227    get reason(){ return t('unlock_reasons.acceleration','¡Aceleración! El timer se acelera.'); } },
228
229  { id:"blink", pool:"mutator",
230    condition:(d)=>(d.totalRuns??0)>=10,
231    get hint(){ return t('unlock_hints.blink_hint','Persiste jugando...'); },
232    get reason(){ return t('unlock_reasons.blink','¡Parpadeo! El teclado visual parpadea.'); } },
233
234  { id:"bonanza", pool:"mutator",
235    condition:(d)=>(d.totalItemsBought??0)>=16,
236    get hint(){ return t('unlock_hints.bonanza_hint','Compra items en la tienda...'); },
237    get reason(){ return t('unlock_reasons.bonanza','¡Bonanza! Más puntos, pero tienda más cara.'); } },
238
239  { id:"pain_plus", pool:"mutator",
240    condition:(d)=>(d.records?.bestFloor??0)>=8,
241    get hint(){ return t('unlock_hints.pain_plus_hint','Llega a pisos altos...'); },
242    get reason(){ return t('unlock_reasons.pain_plus','¡Dolor+! Combos más difíciles.'); } },
243
244  // ═══════════════════════════════════════════
245  //  TIER 4 — VETERANO (runs 12-20)
246  //  Powerful items, deeper strategy
247  // ═══════════════════════════════════════════
248
249  // ── Jokers T4 ──
250  { id:"safety_net", pool:"joker",
251    condition:(d)=>(d.totalRuns??0)>=14,
252    get hint(){ return t('unlock_hints.safety_net_hint','Mucha experiencia...'); },
253    get reason(){ return t('unlock_reasons.safety_net','¡Red de Seguridad! +1 strike máximo.'); } },
254
255  { id:"unstoppable", pool:"joker",
256    condition:(d)=>(d.totalFloors??0)>=100,
257    get hint(){ return t('unlock_hints.unstoppable_hint','Completa muchos pisos...'); },
258    get reason(){ return t('unlock_reasons.unstoppable','¡Imparable! Conserva parte de tu racha al fallar.'); } },
259
260  { id:"virtuoso", pool:"joker",
261    condition:(d)=>(d.totalChecksPassed??0)>=500,
262    get hint(){ return t('unlock_hints.virtuoso_hint','Pasa cientos de checks...'); },
263    get reason(){ return t('unlock_reasons.virtuoso','¡Dedo Virtuoso! Tus checks valen más.'); } },
264
265  { id:"metronome_heart", pool:"joker",
266    condition:(d)=>(d.totalRounds??0)>=220,
267    get hint(){ return t('unlock_hints.metronome_heart_hint','Supera muchas rondas...'); },
268    get reason(){ return t('unlock_reasons.metronome_heart','¡Corazón Metrónomo! Timer mínimo garantizado.'); } },
269
270  { id:"fever", pool:"joker",
271    condition:(d)=>(d.records?.bestStreak??0)>=15,
272    get hint(){ return t('unlock_hints.fever_hint','Racha increíble...'); },
273    get reason(){ return t('unlock_reasons.fever','¡Fiebre! Rachas amplifican tus checks.'); } },
274
275  { id:"perfectionist", pool:"joker",
276    condition:(d)=>(d.totalPerfectRounds??0)>=45,
277    get hint(){ return t('unlock_hints.perfectionist_hint','Muchas rondas perfectas...'); },
278    get reason(){ return t('unlock_reasons.perfectionist','¡Perfeccionista! Bonus por rondas sin fallo.'); } },
279
280  { id:"catalyst", pool:"joker",
281    condition:(d)=>(d.seenMutators?.length??0)>=14,
282    get hint(){ return t('unlock_hints.catalyst_hint','Descubre mutadores...'); },
283    get reason(){ return t('unlock_reasons.catalyst','¡Catalizador! Bonus bajo mutadores nerf.'); } },
284
285  // ── Consumables T4 ──
286  { id:"adrenaline", pool:"consumable",
287    condition:(d)=>(d.records?.bestStreak??0)>=13&&(d.totalRuns??0)>=12,
288    get hint(){ return t('unlock_hints.adrenaline_hint','Racha intensa...'); },
289    get reason(){ return t('unlock_reasons.adrenaline','¡Adrenalina! Checks fáciles... luego difíciles.'); } },
290
291  { id:"skip_round", pool:"consumable",
292    condition:(d)=>(d.records?.bestFloor??0)>=9,
293    get hint(){ return t('unlock_hints.skip_round_hint','Avanza pisos...'); },
294    get reason(){ return t('unlock_reasons.skip_round','¡Saltar Ronda! Autocompleta la próxima.'); } },
295
296  { id:"transfusion", pool:"consumable",
297    condition:(d)=>(d.totalStrikeRepairs??0)>=12,
298    get hint(){ return t('unlock_hints.transfusion_hint','Repara muchos strikes...'); },
299    get reason(){ return t('unlock_reasons.transfusion','¡Transfusión! Convierte strikes en puntos.'); } },
300
301  { id:"gold_rush", pool:"consumable",
302    condition:(d)=>(d.totalCoins??0)>=900,
303    get hint(){ return t('unlock_hints.gold_rush_hint','Acumula riqueza...'); },
304    get reason(){ return t('unlock_reasons.gold_rush','¡Fiebre del Oro! Piso lucrativo.'); } },
305
306  { id:"lucky_coin", pool:"consumable",
307    condition:(d)=>(d.totalItemsBought??0)>=28,
308    get hint(){ return t('unlock_hints.lucky_coin_hint','Compra items...'); },
309    get reason(){ return t('unlock_reasons.lucky_coin','¡Moneda de la Suerte! Descuento garantizado.'); } },
310
311  // ── Mutators T4 ──
312  { id:"recycle", pool:"mutator",
313    condition:(d)=>(d.totalCoins??0)>=700,
314    get hint(){ return t('unlock_hints.recycle_hint','Acumula coins...'); },
315    get reason(){ return t('unlock_reasons.recycle','¡Reciclaje! Fallos dan coins, pero menos puntos.'); } },
316
317  { id:"overflow", pool:"mutator",
318    condition:(d)=>(d.totalChecksPassed??0)>=450,
319    get hint(){ return t('unlock_hints.overflow_hint','Pasa cientos de checks...'); },
320    get reason(){ return t('unlock_reasons.overflow','¡Desbordamiento! Checks extra dan puntos bonus.'); } },
321
322  { id:"boomerang", pool:"mutator",
323    condition:(d)=>(d.totalRuns??0)>=16,
324    get hint(){ return t('unlock_hints.boomerang_hint','Veterano...'); },
325    get reason(){ return t('unlock_reasons.boomerang','¡Boomerang! Checks alternan fácil/difícil.'); } },
326
327  { id:"mutation", pool:"mutator",
328    condition:(d)=>(d.seenMutators?.length??0)>=13,
329    get hint(){ return t('unlock_hints.mutation_hint','Descubre mutadores...'); },
330    get reason(){ return t('unlock_reasons.mutation','¡Mutación! Doble intercambio de teclas.'); } },
331
332  { id:"combustion", pool:"mutator",
333    condition:(d)=>(d.totalPerfectRounds??0)>=40,
334    get hint(){ return t('unlock_hints.combustion_hint','Rondas perfectas...'); },
335    get reason(){ return t('unlock_reasons.combustion','¡Combustión! Racha ×1.5, pero romperla duele.'); } },
336
337  { id:"crescendo", pool:"mutator",
338    condition:(d)=>(d.totalRounds??0)>=260,
339    get hint(){ return t('unlock_hints.crescendo_hint','Muchas rondas...'); },
340    get reason(){ return t('unlock_reasons.crescendo','¡Crescendo! Menos tiempo, más puntos.'); } },
341
342  // ═══════════════════════════════════════════
343  //  TIER 5 — EXPERTO (runs 20-30)
344  //  Advanced synergies and hard mutators
345  // ═══════════════════════════════════════════
346
347  // ── Jokers T5 ──
348  { id:"free_spirit", pool:"joker",
349    condition:(d)=>(d.totalRuns??0)>=20,
350    get hint(){ return t('unlock_hints.free_spirit_hint','Veterano de muchas runs...'); },
351    get reason(){ return t('unlock_reasons.free_spirit','¡Espíritu Libre! Sin supervivencia = más score.'); } },
352
353  { id:"opportunist", pool:"joker",
354    condition:(d)=>(d.seenMutators?.length??0)>=18,
355    get hint(){ return t('unlock_hints.opportunist_hint','Descubre muchos mutadores...'); },
356    get reason(){ return t('unlock_reasons.opportunist','¡Oportunista! Bonus bajo chaos.'); } },
357
358  { id:"alchemist", pool:"joker",
359    condition:(d)=>(d.totalCoins??0)>=1800,
360    get hint(){ return t('unlock_hints.alchemist_hint','Gran riqueza acumulada...'); },
361    get reason(){ return t('unlock_reasons.alchemist','¡Alquimista! Tus ahorros potencian tu score.'); } },
362
363  { id:"investor", pool:"joker",
364    condition:(d)=>(d.totalCoins??0)>=3000,
365    get hint(){ return t('unlock_hints.investor_hint','Fortuna inmensa...'); },
366    get reason(){ return t('unlock_reasons.investor','¡Inversor! Tus ahorros generan interés.'); } },
367
368  { id:"time_thief", pool:"joker",
369    condition:(d)=>(d.totalChecksPassed??0)>=900,
370    get hint(){ return t('unlock_hints.time_thief_hint','Miles de checks...'); },
371    get reason(){ return t('unlock_reasons.time_thief','¡Ladrón de Tiempo! Cada check te da tiempo.'); } },
372
373  { id:"recycler", pool:"joker",
374    condition:(d)=>(d.totalConsumablesUsed??0)>=35,
375    get hint(){ return t('unlock_hints.recycler_hint','Usa muchos consumibles...'); },
376    get reason(){ return t('unlock_reasons.recycler','¡Reciclador! Descartar = coins.'); } },
377
378  { id:"medic", pool:"joker",
379    condition:(d)=>(d.totalStrikeRepairs??0)>=16,
380    get hint(){ return t('unlock_hints.medic_hint','Repara muchos strikes...'); },
381    get reason(){ return t('unlock_reasons.medic','¡Médico! Perfección cura strikes.'); } },
382
383  { id:"tax_collector", pool:"joker",
384    condition:(d)=>(d.totalItemsBought??0)>=50,
385    get hint(){ return t('unlock_hints.tax_collector_hint','Compra mucho...'); },
386    get reason(){ return t('unlock_reasons.tax_collector','¡Recaudador! Las compras reembolsan.'); } },
387
388  { id:"mutant", pool:"joker",
389    condition:(d)=>(d.seenMutators?.length??0)>=22,
390    get hint(){ return t('unlock_hints.mutant_hint','Coleccionista de mutadores...'); },
391    get reason(){ return t('unlock_reasons.mutant','¡Mutante! Variedad de mutadores = score.'); } },
392
393  // ── Consumables T5 ──
394  { id:"rewind", pool:"consumable",
395    condition:(d)=>(d.records?.bestFloor??0)>=10,
396    get hint(){ return t('unlock_hints.rewind_hint','Pisos avanzados...'); },
397    get reason(){ return t('unlock_reasons.rewind','¡Rewind! Repite rondas fallidas.'); } },
398
399  { id:"banish", pool:"consumable",
400    condition:(d)=>(d.seenMutators?.length??0)>=18,
401    get hint(){ return t('unlock_hints.banish_hint','Conoce muchos mutadores...'); },
402    get reason(){ return t('unlock_reasons.banish','¡Destierro! Silencia mutadores.'); } },
403
404  { id:"oracle", pool:"consumable",
405    condition:(d)=>(d.totalFlawlessFloors??0)>=12,
406    get hint(){ return t('unlock_hints.oracle_hint','Pisos flawless...'); },
407    get reason(){ return t('unlock_reasons.oracle','¡Oráculo! Ve el futuro.'); } },
408
409  { id:"extra_checks", pool:"consumable",
410    condition:(d)=>(d.totalChecksPassed??0)>=750,
411    get hint(){ return t('unlock_hints.extra_checks_hint','Pasa cientos de checks...'); },
412    get reason(){ return t('unlock_reasons.extra_checks','¡Checks Extra! Más oportunidades.'); } },
413
414  { id:"restock", pool:"consumable",
415    condition:(d)=>(d.totalItemsBought??0)>=42,
416    get hint(){ return t('unlock_hints.restock_hint','Compra bastante...'); },
417    get reason(){ return t('unlock_reasons.restock','¡Reabastecimiento! Renueva la tienda.'); } },
418
419  // ── Mutators T5 ──
420  { id:"zen", pool:"mutator",
421    condition:(d)=>(d.totalRuns??0)>=22,
422    get hint(){ return t('unlock_hints.zen_hint','Mucha experiencia...'); },
423    get reason(){ return t('unlock_reasons.zen','¡Zen! Sin timer en BUILD, checks duros.'); } },
424
425  { id:"metronome", pool:"mutator",
426    condition:(d)=>(d.totalRounds??0)>=350,
427    get hint(){ return t('unlock_hints.metronome_hint','Cientos de rondas...'); },
428    get reason(){ return t('unlock_reasons.metronome','¡Metrónomo! Checks automáticos.'); } },
429
430  { id:"duality", pool:"mutator",
431    condition:(d)=>(d.totalFloors??0)>=150,
432    get hint(){ return t('unlock_hints.duality_hint','Muchos pisos explorados...'); },
433    get reason(){ return t('unlock_reasons.duality','¡Dualidad! Rondas pares/impares diferentes.'); } },
434
435  { id:"precision_mut", pool:"mutator",
436    condition:(d)=>(d.totalFlawlessFloors??0)>=10,
437    get hint(){ return t('unlock_hints.precision_mut_hint','Pisos perfectos...'); },
438    get reason(){ return t('unlock_reasons.precision_mut','¡Precisión! Menos checks pero más fáciles.'); } },
439
440  { id:"revenge", pool:"mutator",
441    condition:(d)=>(d.totalRuns??0)>=22,
442    get hint(){ return t('unlock_hints.revenge_hint','Persiste jugando...'); },
443    get reason(){ return t('unlock_reasons.revenge','¡Venganza! Strikes reducen tu check window.'); } },
444
445  { id:"amnesia", pool:"mutator",
446    condition:(d)=>(d.records?.bestFloor??0)>=11,
447    get hint(){ return t('unlock_hints.amnesia_hint','Llega a pisos altos...'); },
448    get reason(){ return t('unlock_reasons.amnesia','¡Amnesia! Memoriza el combo antes de que desaparezca.'); } },
449
450  { id:"shrinking", pool:"mutator",
451    condition:(d)=>(d.totalPerfectRounds??0)>=55,
452    get hint(){ return t('unlock_hints.shrinking_hint','Rondas perfectas...'); },
453    get reason(){ return t('unlock_reasons.shrinking','¡Encogimiento! Timer se reduce cada ronda.'); } },
454
455  // ── Shop Variant T5 ──
456  { id:"shop_free_reroll", pool:"shop_variant",
457    condition:(d)=>(d.totalRuns??0)>=25,
458    get hint(){ return t('unlock_hints.shop_free_reroll_hint','Muchas runs...'); },
459    get reason(){ return t('unlock_reasons.shop_free_reroll','¡Primer reroll gratis en cada tienda!'); } },
460
461  // ═══════════════════════════════════════════
462  //  TIER 6 — MAESTRO (runs 30-50)
463  //  Endgame builds and dangerous mutators
464  // ═══════════════════════════════════════════
465
466  // ── Jokers T6 ──
467  { id:"masochist", pool:"joker",
468    condition:(d)=>(d.records?.bestFloor??0)>=12,
469    get hint(){ return t('unlock_hints.masochist_hint','Piso 10+...'); },
470    get reason(){ return t('unlock_reasons.masochist','¡Masoquista! Más teclas = más puntos.'); } },
471
472  { id:"late_bloomer", pool:"joker",
473    condition:(d)=>(d.records?.bestFloor??0)>=13,
474    get hint(){ return t('unlock_hints.late_bloomer_hint','Llega muy lejos...'); },
475    get reason(){ return t('unlock_reasons.late_bloomer','¡Flor Tardía! Pisos avanzados dan más.'); } },
476
477  { id:"combo_king", pool:"joker",
478    condition:(d)=>(d.usedJokers?.length??0)>=22,
479    get hint(){ return t('unlock_hints.combo_king_hint','Usa muchos jokers distintos...'); },
480    get reason(){ return t('unlock_reasons.combo_king','¡Rey del Combo! Más jokers = más score.'); } },
481
482  { id:"gambler", pool:"joker",
483    condition:(d)=>(d.totalRuns??0)>=30,
484    get hint(){ return t('unlock_hints.gambler_hint','Muchas runs...'); },
485    get reason(){ return t('unlock_reasons.gambler','¡Apostador! Arriésgate por la gloria.'); } },
486
487  { id:"daredevil", pool:"joker",
488    condition:(d)=>(d.totalRuns??0)>=28,
489    get hint(){ return t('unlock_hints.daredevil_hint','Veterano curtido...'); },
490    get reason(){ return t('unlock_reasons.daredevil','¡Temerario! Al borde del abismo ganas más.'); } },
491
492  { id:"scavenger", pool:"joker",
493    condition:(d)=>(d.totalItemsBought??0)>=65,
494    get hint(){ return t('unlock_hints.scavenger_hint','Comprador compulsivo...'); },
495    get reason(){ return t('unlock_reasons.scavenger','¡Carroñero! Encuentra items gratis.'); } },
496
497  { id:"hoarder", pool:"joker",
498    condition:(d)=>(d.totalConsumablesUsed??0)>=55,
499    get hint(){ return t('unlock_hints.hoarder_hint','Maestro de consumibles...'); },
500    get reason(){ return t('unlock_reasons.hoarder','¡Acumulador! +1 slot de consumible.'); } },
501
502  { id:"all_in", pool:"joker",
503    condition:(d)=>(d.totalConsumablesUsed??0)>=45,
504    get hint(){ return t('unlock_hints.all_in_hint','Usa muchos consumibles...'); },
505    get reason(){ return t('unlock_reasons.all_in','¡Todo o Nada! Sin consumibles = más puntos.'); } },
506
507  { id:"overachiever", pool:"joker",
508    condition:(d)=>(d.totalFlawlessFloors??0)>=18,
509    get hint(){ return t('unlock_hints.overachiever_hint','Pisos perfectos...'); },
510    get reason(){ return t('unlock_reasons.overachiever','¡Sobresaliente! Flawless acumulativo.'); } },
511
512  { id:"berserker", pool:"joker",
513    condition:(d)=>(d.totalRuns??0)>=35,
514    get hint(){ return t('unlock_hints.berserker_hint','Persiste mucho...'); },
515    get reason(){ return t('unlock_reasons.berserker','¡Berserker! Strikes potencian tu score.'); } },
516
517  // ── Consumables T6 ──
518  { id:"overcharge", pool:"consumable",
519    condition:(d)=>d.bestRuns?.some(r=>r.score>=6000),
520    get hint(){ return t('unlock_hints.overcharge_hint','Score muy alto...'); },
521    get reason(){ return t('unlock_reasons.overcharge','¡Sobrecarga! ×3 puntos... con un coste.'); } },
522
523  { id:"perfect_gem", pool:"consumable",
524    condition:(d)=>(d.totalPerfectRounds??0)>=80,
525    get hint(){ return t('unlock_hints.perfect_gem_hint','Muchas rondas perfectas...'); },
526    get reason(){ return t('unlock_reasons.perfect_gem','¡Gema Perfecta! Perfección amplificada.'); } },
527
528  { id:"elixir", pool:"consumable",
529    condition:(d)=>(d.totalItemsBought??0)>=58,
530    get hint(){ return t('unlock_hints.elixir_hint','Comprador frecuente...'); },
531    get reason(){ return t('unlock_reasons.elixir','¡Elixir! Potencia jokers permanentemente.'); } },
532
533  { id:"mirror_break", pool:"consumable",
534    condition:(d)=>(d.seenMutators?.length??0)>=26,
535    get hint(){ return t('unlock_hints.mirror_break_hint','Experto en mutadores...'); },
536    get reason(){ return t('unlock_reasons.mirror_break','¡Rompe-Espejos! Cancela mutadores permanentemente.'); } },
537
538  { id:"second_wind", pool:"consumable",
539    condition:(d)=>(d.totalRuns??0)>=32,
540    get hint(){ return t('unlock_hints.second_wind_hint','Superviviente nato...'); },
541    get reason(){ return t('unlock_reasons.second_wind','¡Segundo Aire! Una vida extra.'); } },
542
543  // ── Mutators T6 ──
544  { id:"cascade", pool:"mutator",
545    condition:(d)=>(d.totalChecksPassed??0)>=1100,
546    get hint(){ return t('unlock_hints.cascade_hint','Miles de checks...'); },
547    get reason(){ return t('unlock_reasons.cascade','¡Cascada! Cada check reduce la ventana.'); } },
548
549  { id:"double_tap", pool:"mutator",
550    condition:(d)=>(d.totalRounds??0)>=450,
551    get hint(){ return t('unlock_hints.double_tap_hint','Cientos de rondas...'); },
552    get reason(){ return t('unlock_reasons.double_tap','¡Doble Toque! Cada check requiere pulsar dos veces.'); } },
553
554  { id:"sequential", pool:"mutator",
555    condition:(d)=>(d.totalFloors??0)>=200,
556    get hint(){ return t('unlock_hints.sequential_hint','Muchos pisos recorridos...'); },
557    get reason(){ return t('unlock_reasons.sequential','¡Secuencial! Teclas en orden obligatorio.'); } },
558
559  { id:"last_breath", pool:"mutator",
560    condition:(d)=>(d.totalRuns??0)>=36,
561    get hint(){ return t('unlock_hints.last_breath_hint','Persiste sin rendirte...'); },
562    get reason(){ return t('unlock_reasons.last_breath','¡Último Aliento! 2 strikes, pero curas al superar.'); } },
563
564  { id:"gravity", pool:"mutator",
565    condition:(d)=>(d.totalChecksPassed??0)>=1200,
566    get hint(){ return t('unlock_hints.gravity_hint','Checks por miles...'); },
567    get reason(){ return t('unlock_reasons.gravity','¡Gravedad! Checks fallidos roban tiempo.'); } },
568
569  { id:"naked", pool:"mutator",
570    condition:(d)=>(d.records?.bestFloor??0)>=14,
571    get hint(){ return t('unlock_hints.naked_hint','Llega a pisos muy altos...'); },
572    get reason(){ return t('unlock_reasons.naked','¡Desnudo! Sin teclado visual, solo texto.'); } },
573
574  { id:"echo_round", pool:"mutator",
575    condition:(d)=>(d.totalRuns??0)>=34,
576    get hint(){ return t('unlock_hints.echo_round_hint','Experiencia prolongada...'); },
577    get reason(){ return t('unlock_reasons.echo_round','¡Eco de Ronda! Combos repetidos.'); } },
578
579  // ═══════════════════════════════════════════
580  //  TIER 7 — EXÓTICO (runs 50+, extreme)
581  //  Endgame rewards for dedicated players
582  // ═══════════════════════════════════════════
583
584  // ── Jokers T7 (exotic) ──
585  { id:"chameleon", pool:"joker",
586    condition:(d)=>(d.seenMutators?.length??0)>=32,
587    get hint(){ return t('unlock_hints.chameleon_hint','Maestro de todos los mutadores...'); },
588    get reason(){ return t('unlock_reasons.chameleon','¡Camaleón! Domina cualquier mutador.'); } },
589
590  { id:"momentum_rider", pool:"joker",
591    condition:(d)=>(d.records?.bestStreak??0)>=20,
592    get hint(){ return t('unlock_hints.momentum_rider_hint','Racha legendaria...'); },
593    get reason(){ return t('unlock_reasons.momentum_rider','¡Jinete del Impulso! Rachas dan tiempo.'); } },
594
595  { id:"thick_skin", pool:"joker",
596    condition:(d)=>(d.records?.bestFloor??0)>=16,
597    get hint(){ return t('unlock_hints.thick_skin_hint','Pisos altísimos...'); },
598    get reason(){ return t('unlock_reasons.thick_skin','¡Piel Dura! Chance de ignorar strikes.'); } },
599
600  { id:"phoenix", pool:"joker",
601    condition:(d)=>(d.totalRuns??0)>=50&&(d.records?.bestFloor??0)>=14,
602    get hint(){ return t('unlock_hints.phoenix_hint','Muchas muertes, muchos renacimientos...'); },
603    get reason(){ return t('unlock_reasons.phoenix','¡Fénix desbloqueado! Una segunda vida.'); } },
604
605  { id:"glass_cannon", pool:"joker",
606    condition:(d)=>d.bestRuns?.some(r=>r.score>=10000),
607    get hint(){ return t('unlock_hints.glass_cannon_hint','Score legendario...'); },
608    get reason(){ return t('unlock_reasons.glass_cannon','¡Cañón de Cristal! Poder máximo, fragilidad máxima.'); } },
609
610  { id:"cursed_crown", pool:"joker",
611    condition:(d)=>(d.records?.bestFloor??0)>=18,
612    get hint(){ return t('unlock_hints.cursed_crown_hint','Llega a los pisos finales...'); },
613    get reason(){ return t('unlock_reasons.cursed_crown','¡Corona Maldita! Poder con un precio.'); } },
614
615  { id:"soul_bargain", pool:"joker",
616    condition:(d)=>(d.records?.bestStreak??0)>=25,
617    get hint(){ return t('unlock_hints.soul_bargain_hint','Racha divina...'); },
618    get reason(){ return t('unlock_reasons.soul_bargain','¡Pacto de Almas! Checks valen... pero un golpe mata.'); } },
619
620  // ── Consumables T7 ──
621  { id:"liquidate", pool:"consumable",
622    condition:(d)=>(d.totalItemsBought??0)>=75,
623    get hint(){ return t('unlock_hints.liquidate_hint','Comerciante experto...'); },
624    get reason(){ return t('unlock_reasons.liquidate','¡Liquidar! Sacrifica jokers por coins.'); } },
625
626  { id:"forge", pool:"consumable",
627    condition:(d)=>(d.usedJokers?.length??0)>=30,
628    get hint(){ return t('unlock_hints.forge_hint','Coleccionista de jokers...'); },
629    get reason(){ return t('unlock_reasons.forge','¡Forja! Mejora todos tus jokers.'); } },
630
631  { id:"clone_potion", pool:"consumable",
632    condition:(d)=>(d.totalConsumablesUsed??0)>=70,
633    get hint(){ return t('unlock_hints.clone_potion_hint','Maestro de consumibles...'); },
634    get reason(){ return t('unlock_reasons.clone_potion','¡Poción Clónica! Duplica consumibles.'); } },
635
636  { id:"amplifier", pool:"consumable",
637    condition:(d)=>(d.totalConsumablesUsed??0)>=80,
638    get hint(){ return t('unlock_hints.amplifier_hint','Experto absoluto en consumibles...'); },
639    get reason(){ return t('unlock_reasons.amplifier','¡Amplificador! Doble efecto.'); } },
640
641  // ── Mutators T7 ──
642  { id:"volatile", pool:"mutator",
643    condition:(d)=>(d.totalRuns??0)>=45,
644    get hint(){ return t('unlock_hints.volatile_hint','Veterano extremo...'); },
645    get reason(){ return t('unlock_reasons.volatile','¡Volátil! Eventos aleatorios cada ronda.'); } },
646
647  { id:"feast_famine", pool:"mutator",
648    condition:(d)=>(d.totalPerfectRounds??0)>=100,
649    get hint(){ return t('unlock_hints.feast_famine_hint','Perfección absoluta...'); },
650    get reason(){ return t('unlock_reasons.feast_famine','¡Festín y Hambruna! Alto riesgo, alta recompensa.'); } },
651
652  { id:"shuffle", pool:"mutator",
653    condition:(d)=>(d.seenMutators?.length??0)>=30,
654    get hint(){ return t('unlock_hints.shuffle_hint','Descubre casi todos los mutadores...'); },
655    get reason(){ return t('unlock_reasons.shuffle','¡Barajar desbloqueado! El teclado cambia.'); } },
656
657  { id:"ricochet", pool:"mutator",
658    condition:(d)=>(d.totalChecksPassed??0)>=1600,
659    get hint(){ return t('unlock_hints.ricochet_hint','Miles y miles de checks...'); },
660    get reason(){ return t('unlock_reasons.ricochet','¡Rebote! Segunda oportunidad en checks.'); } },
661
662  // ── Shop Variant T7 ──
663  { id:"shop_rare_sight", pool:"shop_variant",
664    condition:(d)=>(d.records?.bestFloor??0)>=15,
665    get hint(){ return t('unlock_hints.shop_rare_sight_hint','Llega a pisos extremos...'); },
666    get reason(){ return t('unlock_reasons.shop_rare_sight','¡Items raros aparecen más en tienda!'); } },
667];
668
669export default UNLOCKABLE_DEFS;
670
671/**
672 * Set of IDs gated behind unlock conditions.
673 * Items NOT in this set are always available.
674 */
675export const GATED_IDS = new Set(UNLOCKABLE_DEFS.map(d => d.id));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.