Mechanics reference
These are the original research notes behind the guide pages, preserved against build boot-DIJlLGwy.js / sim-DQNACfZS.js. They describe that historical build, not the current game. Use the individual guide and entity pages for current values. The notes and their Verified or Inferred labels are unchanged; the original source bundles were unavailable for rechecking in this update. See Versions & updates.
1. Formulas
The sim runs at 60 ticks per second; second-valued config is converted with round(60*sec). Verified.[1]
XP needed per level
- XP needed to go from level L to L+1 =
round(xpFirst + xpLinear*(L-1) + xpCurve*(L-1)^xpExponent)=round(5 + 5(L-1) + (L-1)^2.5). Verified.[2] - Values (computed from the formula): L1→2: 5, L2→3: 11, L3→4: 21, L4→5: 36, L5→6: 57, L10: 293, L20: 1674, L30: 4679, L40: 9699, L50: 17057. Verified formula, values computed by us.
- Picked-up XP is multiplied by the
growthMultstat; extra levels queue up as pending level-ups. Verified.[3][4]
Stat derivation (all passives, items, dog auras, shrine picks summed, then converted)
All raw stat totals are summed (passives perLevel*level, items mods*count, dog auras, shrine totals) and turned into final multipliers by one function. Verified.[5][6]
- Damage:
max(0.1, 1+might). Area:max(0.2, 1+area). Projectile speedmax(0.2, 1+speed). Move speedmax(0.3, 1+moveSpeed). Verified.[7] - Duration:
max(0.2, 1 + duration + 0.25)(player.baseDuration = 0.25, so base duration multiplier is 1.25). Verified.[8] - Amount:
floor(amount) + 1(baseAmount 1). Not applied to aura, cone, nuke and ring weapons. Verified.[9][10] - Max HP:
max(1, (100 + maxHp) * (1 + maxHpPct) * difficulty.maxHp). Verified.[11] - Pierce bonus
floor(pierce), knockbackmax(0, 1+knockback), elite damage1+eliteDamage. Verified.[12]
Cooldown cap
- Cooldown multiplier =
1 - clamp(cooldown, -1, 0.6), so cooldown reduction is capped at 60% (multiplier floor 0.4). Same cap for dash cooldown. Verified.[13][14] - A weapon's final cooldown never drops below 0.05 s (15 s for
nukeweapons,reset.minCooldownSec), and that floor is multiplied by the Frenzy Fryer rate. Verified.[15]
Diminishing-returns stats (crit chance, evasion, explode-on-kill, chest discount)
- Helper
Ee(x,k) = x/(k+x)(0 for x ≤ 0). Verified.[16] - Effective crit chance =
critChance/(1+critChance); evasion =evasion/(1+evasion); explode-on-kill chance =x/(1+x); chest price multiplier =1 - chestDiscount/(1+chestDiscount)(all K constants = 1). Verified.[17][18] - Example (computed): +0.10 raw crit chance gives 9.1% effective; +1.0 gives 50%.
Crit
- On each hit, crit chance = effective crit chance + the weapon's own
critBonus; on crit damage is multiplied bycritMult = 2 + critDamage. Verified.[19] - Elite/boss hits are also multiplied by
eliteMult(1+eliteDamage) before crit. Verified.[20]
Evasion
- When the player would take damage, evasion is rolled right after the invulnerability checks; a success cancels the hit entirely (a
dodgeevent). Damage flagged as unavoidable (biome terrain, hazard pools) skips the roll. Verified.[21]
Armor
- Damage taken =
damage * (1 - min(0.85, armor / (5*attackerLevel + armor))). Verified.[22][23] - Attacker level =
10 + floor(runMinutes * 1); elites +5, bosses +10. Enemy bullets and ground telegraphs use the same time-based level. Verified.[24][25] - Example (computed): 50 armor vs a level-10 pest (minute 0) = 50/(50+50) = 50% reduction; vs a level-30 pest (minute 20) = 50/200 = 25%.
- A
guardweapon with active charges absorbs (soak) damage before armor. Verified.[26]
Curse
- Curse stat = summed curse +
difficulty.globalCurse(0). Verified.[27] - Rewards: XP gain and gold gain are each multiplied by
1 + 0.5*curse(curseRewardShare). Verified.[28] - Pests: HP ×(1+curse), speed ×(1+0.3·curse). Verified.[29][30]
- Crowd: the alive-enemy ceiling, the minimum crowd and the spawn timer rate are each ×(1+curse) (ceiling still capped at spawns.maxEnemies = 500). Verified.[31][32]
- Elites and bosses: HP ×(1+curse). Verified.[33]
- Curses taken (a counter, separate from the curse stat; +1 per Moldy Shrine accepted, +1 per Nightmare start curse) add extra copies: every elite spawn adds
floor(cursesTaken * 1)extra elites, every boss spawn addsfloor(cursesTaken * 0.5)extra bosses. Verified.[34] - Extra (cursed) elites/bosses each drop
chestsPerCursedExtra= 1 chest. Verified.[35] - Game UI wording: "Curse makes enemies tougher, faster and more numerous, and raises XP and gold." Verified.[36]
Luck
- 4th level-up card: chance =
min(1, luck / 1). Verified.[37] - Item rarity: weights
[59,25,10,4,2](Common..Legendary) with weight of rarity n multiplied by(1+luck)^n. Verified.[38] - Chest size: 5 rewards if roll < 0.02·(1+luck); 3 rewards if roll < 0.20·(1+luck); else 1. Verified.[39]
- Game UI wording: "Better odds of a fourth level-up card, bigger chests and rarer items." Verified.[40]
Greed (gold gain)
- Every gold pickup is worth
value * greedMult(×2 during a gold-rush event). Verified.[41] - Chest gold:
random(12..30) * rewardCount * greedMult * goldScale, where goldScale =min(2, 1 + 0.05*runMinutes)for boss and elite chests, else 1. Verified.[42][43]
Growth
growthMult = (1+growth) * (1+0.5*curse) * difficulty.xp. Verified.[44]
Magnet (pickup range)
- Pickup radius =
40 * max(0.2, 1+magnet). Gems, coins and other small pickups inside it fly to the player at 300 units/s; chests and items are never magnetised (you stand on them to open). Verified.[45][46]
Recovery
- Heals
recoveryHP per second, capped at max HP. Verified.[47][48] - Heal on kill:
healOnKillHP per kill. Verified.[49]
Revivals
- Revives available =
floor(revival) + difficulty.addedRevives(0 if the difficulty disallows revives). Verified.[50] - On death with a revive left: revive at 50% max HP, 2 s invulnerability, enemies within 120 are knocked back. Verified.[51]
- On difficulties without revives, items whose only mod is
revival(Second Spud) are removed from the item pool. Verified.[52]
Enemy scaling over time
From Br(scale, cfg, runClock); a = min(minutes, 20), o = overtime minutes past 20:00 (+ difficulty overtimeHoldSec). Verified.[53]
- Pest HP multiplier =
(1 + 0.03a + 0.009a²) * 4^o * difficulty.pestHp. At 20:00 that is 1 + 0.6 + 3.6 = 5.2×; after 20:00 it multiplies by 4 every further minute. Verified.[54] - Pest damage multiplier =
(1 + 0·a + 0.0025a²) * 4^o * early * late * difficulty.damage, whereearlyramps linearly from 0.4 at 0:00 to 1.0 at 15:00, andlate=1 + 0.2*max(0, a-15). Verified.[55] - Overtime only (after 20:00): enemy speed ×1.15^o (max 2.2×), count ×1.5^o, spawn rate ×1.5^o. Verified.[56]
- Enemy bullets: count ×(1+0.04/min, max 2.5), fire rate ×(1+0.03/min, max 2.2), speed ×(1+0.015/min, max 1.6), based on total run minutes. Verified.[57]
- Elite and boss HP use the overtime factor
hpPost(4^o) but not the pre-20 pest curve; see section 7. Verified.[58]
Chest (Potato Crate) price
- Price of the n-th paid crate (n = crates bought so far, starting at 0) =
round(round(30 + 41.5n + 0.5n² + 0.898*max(0,n-3)³) * chestPriceMult). Verified.[59] - Computed base prices before discount: 30, 72, 115, 159, 205, 257, 321, 402, 506, 638 (n = 0..9).
- Tater Slots spin price =
ceil(crate price * 0.5). Verified.[60]
Item power per minute
items.powerPerMinute = 0.15is used in exactly one place: projectiles fired byonHurtitem procs dealdamage * damageMult * (1 + 0.15*runMinutes)(+ a thorns share). Verified.[61][62]
hpEdge (low-HP bonus)
- Only
slashweapons with anhpEdgefield (Mezzaluna lowHpBonus 1.2, Moon Cleaver 1.6). Damage multiplier =0.8 + (lowHpBonus + 0.2) * missingHpFraction: 0.8× at full HP, up to (1+lowHpBonus)× near 0 HP. Verified.[63]
2. Stats, mods, effects, behaviors
Full machine-readable mapping: labels.json. Passive stats and item mods share one stat table: passives add perLevel × level (or the per-level array value at that level), items add mods × copies, dog auras and shrine picks add too, then the totals are converted (section 1). Verified.[64][65]
In-game short names come from the stats/dog-aura label switch (dog-level chunk; the 11 shrine stats reuse content.shrine.*.label), tooltips from deferred-ui. Verified.[66][67]
Stat keys (passive stat and item mods)
| Key | Game label | Game tooltip | What it does (Verified, see quote) |
|---|---|---|---|
might |
Damage | Raises the damage of every weapon. | Final damage multiplier = max(0.1, 1 + might).[68] |
area |
Area | Makes attacks and blasts bigger. | Area multiplier = max(0.2, 1 + area); radius-type effects (item blasts, chains) scale by sqrt(areaMult).[69] |
cooldown |
Cooldown | Shortens the wait between weapon attacks. | Cooldown multiplier = 1 - clamp(cooldown, -1, 0.6); max 60% reduction.[13] |
amount |
Amount | Extra shots or pieces for each weapon. | +floor(amount) extra projectiles per attack (base +1 from player.baseAmount); not used by aura, cone, nuke, ring weapons.[9] |
duration |
Duration | Makes weapon effects last longer. | Duration multiplier = max(0.2, 1 + duration + 0.25 base). Also lengthens freeze/slow/burn statuses.[8] |
speed |
Projectile speed | Makes Tibo's shots fly faster. | Projectile speed multiplier = max(0.2, 1 + speed); orbit weapons get half the bonus.[70] |
moveSpeed |
Move speed | How fast Tibo walks. | Move speed = 62 × max(0.3, 1 + moveSpeed) × terrain/slow/frenzy factors.[71] |
maxHp |
Max HP | The most HP Tibo can have. | Flat max HP added to the base 100.[11] |
maxHpPct |
(none found) | (none found) | Percent max HP: (100 + maxHp) × (1 + maxHpPct). No stats-panel label found in the label switch (not determined).[72] |
recovery |
Recovery | HP Tibo regains each second. | HP regenerated per second.[47] |
armor |
Armor | Cuts the damage Tibo takes from each hit. Enemies get stronger each minute, so the cut shrinks, and elites and bosses get through more. | Damage taken × (1 - min(0.85, armor/(5×attackerLevel + armor))); attacker level 10 + 1/min, elites +5, bosses +10.[22] |
magnet |
Pickup range | How far away Tibo pulls in gems, gold and snacks. | Pickup radius = 40 × max(0.2, 1 + magnet).[45] |
luck |
Luck | Better odds of a fourth level-up card, bigger chests and rarer items. | 4th card chance = min(1, luck); item rarity weights × (1+luck)^rarity; 3/5-reward chest odds × (1+luck).[73] |
growth |
XP gain | Extra XP from every gem. | XP multiplier = (1+growth) × (1 + 0.5×curse) × difficulty xp.[44] |
greed |
Gold gain | Extra gold from every coin and chest. | Gold multiplier = (1+greed) × (1 + 0.5×curse); applies to coins, chest gold, shrine gold, Poteto gold.[28] |
curse |
Curse | Curse makes enemies tougher, faster and more numerous, and raises XP and gold. | Enemy HP ×(1+curse), pest speed ×(1+0.3curse), crowd size and spawn rate ×(1+curse), XP/gold ×(1+0.5curse).[30] |
critChance |
Crit chance | The chance that a hit is critical. | Effective crit chance = x/(1+x), plus the weapon's own critBonus.[74] |
critDamage |
Crit damage | How much a critical hit multiplies damage. | Crit multiplier = 2 + critDamage.[75] |
evasion |
Evasion | The chance to dodge a hit. | Dodge chance = x/(1+x); a dodge cancels the hit.[76] |
knockback |
Knockback | How far hits push enemies back. | Knockback multiplier = max(0, 1 + knockback); push = weapon kb × mult × (1 - enemy mass) × 70.[77] |
pierce |
Pierce | Extra enemies each shot passes through. | +floor(pierce) enemies each projectile passes through.[78] |
revival |
Revival | How many times Tibo gets back up when his HP runs out. | Extra lives: floor(revival) + difficulty bonus; revive at 50% HP with 2 s invulnerability.[79] |
eliteDamage |
Elite damage | Extra damage to elites and bosses. | Damage to elites and bosses × (1 + eliteDamage).[80] |
healOnKill |
Heal on kill | HP Tibo regains for each kill. | Heals this many HP per kill.[49] |
explodeOnKill |
Explode on kill | The chance that a defeated enemy explodes. | Chance x/(1+x) that a killed pest explodes for 20 × damage multiplier in radius 22.[81] |
thorns |
Thorns | Damage dealt back to an enemy that touches Tibo. | Flat damage dealt to an enemy each time its body touches Tibo (contact hit); also adds to guard-weapon shard bursts and onHurt quills. [sim-DQNACfZS.js: ``s<=0 |
chestDiscount |
Chest price | How much less chests and shop items cost. | Price multiplier = 1 - d/(1+d) for crates, vendors, kennel training and slot spins.[18] |
dashCharges |
Dash charges | Extra dashes Tibo can make in a row. Spent charges come back one at a time. | +floor(dashCharges) dash charges on top of the base 1.[82] |
dashCooldown |
Dash cooldown | Shortens the wait for each dash charge to come back. | Dash recharge = 5 s × (1 - clamp(dashCooldown, -1, 0.6)).[83] |
dashGrace |
(none found) | (none found) | Extra seconds of dash shield after a dash (shield = 9 dash ticks + dashGrace s); while shielded, normal damage is ignored. No stats-panel label found (not determined).[84] |
Item mods keys used: amount, area, armor, chestDiscount, cooldown, critChance, critDamage, curse, dashGrace, duration, eliteDamage, evasion, explodeOnKill, greed, growth, healOnKill, luck, magnet, maxHp, might, moveSpeed, pierce, recovery, revival, speed, thorns. Same meaning as above. Verified (registry).
Item effect.kind
onHit: Item proc rolled when one of your weapon hits lands. Proc chance with N copies = 1-(1-chance)^N; procs per second capped at perSec × N (token bucket). Items: coal-baked-spud, heavy-russet, spicy-croquette, static-tot, bonfire-spud, magma-croquette, meteor-russet, storm-tot. No UI label in code. Verified.[85]onKill: Item proc rolled when an enemy dies (frozenOnly: only if it was frozen). Same stacking as onHit. Items: freezer-burn, wandering-eyes, all-seeing-spud, glacier-spud, kaboom-spud. No UI label in code. Verified.[86]onHurt: Item proc when Tibo is hurt, at most once percooldownseconds; fires projectiles outward whose damage grows +15%/minute. Items: porcupine-spud, fry-volcano. No UI label in code. Verified.[62]
Item proc.kind
burn: Sets the hit enemy on fire for share × hit damage per second forseconds× duration;spreadalso ignites enemies in a radius. Items: coal-baked-spud, bonfire-spud. Game wording: coal-baked-spud: "15% of hits set enemies on fire.". Verified.[87]blast: Explosion around the target for share × damage in radius × sqrt(area); optional burn. Items: spicy-croquette, kaboom-spud, magma-croquette. Game wording: spicy-croquette: "12% of hits burst in a fiery blast.". Verified.[88]chain: Lightning jumps tojumpsmore enemies within range × sqrt(area) for share × damage;clapadds a small blast at each. Items: static-tot, storm-tot. Game wording: static-tot: "10% of hits arc lightning to 3 more enemies.". Verified.[89]bonk: Multiplies that hit bymult(15× / 20×);meteoralso craters enemies around. Items: heavy-russet, meteor-russet. Game wording: heavy-russet: "2% of hits BONK for 15× damage.". Verified.[90]seekers: On kill, launches homing potato eyes for share × the dead enemy's max HP. Items: wandering-eyes, all-seeing-spud. Game wording: wandering-eyes: "20% of kills send 2 potato eyes after other enemies.". Verified.[91]shatter: On kill, bursts for share × the dead enemy's max HP and applies a status (freeze);frostslows a wider ring. Items: freezer-burn, glacier-spud. Game wording: freezer-burn: "Frozen enemies shatter when they die and freeze enemies nearby.". Verified.[92]quills: onHurt projectiles:countfries in a ring, damage × damage mult × (1+0.15/min) + thornsShare × thorns; optional blast. Items: porcupine-spud, fry-volcano. Game wording: porcupine-spud: "When Tibo is hit, he fires 10 fries outward.". Verified.[93]
Weapon onHit.kind (status effects)
slow: Status: enemy speed reduced byfactorforseconds× duration. Weapons: butter-streak, potato-battery, salt-line, ghee-glide, mashquake, salt-flats, spud-grid. Verified.[94]freeze: Status: enemy frozen forseconds× duration (bosses are not frozen). Weapons: frozen-fries, root-cellar, cold-storage, hash-bulwark, permafrost-fries. Verified.[95]burn: Status: damage over timedpsforseconds× duration (strongest burn wins). Weapons: inferno-basket. Verified.[96]
Weapon behavior.kind
No UI label exists for behavior kinds. The explanations below are Inferred from the game's own weapon descriptions (en-content) and the behavior fields; the game wording is verbatim.
pool: Leaves damaging ground pools/trails. Game wording: butter-streak: "Each dash leaves a strip of melted butter that slows pests and burns them for 2 s. Between dashes, it also leaves one on the path Tibo just walked.". Weapons: butter-streak, gravy-boat, salt-line, whisk-vortex, ghee-glide, gravy-flood, salt-flats, stand-mixer. Inferred.spiral: Projectiles spiral outward. Game wording: chip-shuriken: "Spinning chips that spiral outward.". Weapons: chip-shuriken, kettle-cyclone. Inferred.cone: Cone spray in front of Tibo. Game wording: deep-fryer: "Sprays hot oil in a cone.". Weapons: deep-fryer, inferno-basket. Inferred.targetShot: Fires projectiles at the nearest enemy. Game wording: frozen-fries: "Shoots frozen fries that freeze enemies in place.". Weapons: frozen-fries, grab-bag, small-fry, tot-toss, big-fry, bottomless-sack, permafrost-fries, tot-gatling. Inferred.facingShot: Fires projectiles in the direction Tibo faces. Game wording: fry-knives: "Fires sharp fries in the direction Tibo faces.". Weapons: fry-knives, thousand-fries. Inferred.beam: Long line attack that pierces a whole line. Game wording: fry-lance: "One very long fry that pierces a whole line.". Weapons: fry-lance, julienne-dash, railfry, waffle-cut. Inferred.turret: Places a turret that shoots nearby enemies. Game wording: fry-sentry: "Drops a fry-cook turret that shoots nearby enemies.". Weapons: fry-sentry. Inferred.slash: Melee swing in front of Tibo (some reflect bullets, charge on elite kills, heal, or gain low-HP damage via hpEdge). Game wording: frying-pan: "Swings a pan in front of Tibo. Enemy bullets it swats fly back at whoever shot them. Each elite Tibo kills charges the next swing bigger and harder.". Weapons: frying-pan, mezzaluna, peeler, cast-iron, mandoline-fury, moon-cleaver. Inferred.boomerang: Projectile flies out and returns. Game wording: hash-boomerang: "A crispy hash brown that flies out and comes back.". Weapons: hash-boomerang, hashnado. Inferred.guard: Orbiting shields that absorb (soak) damage then burst. Game wording: hash-brown-shield: "Hash browns circle Tibo. Each takes up to 20 damage of one hit or bullet, then bursts into shards that hurt nearby enemies. Thorns add to shard damage. Broken ones re-crisp one at a time.". Weapons: hash-brown-shield, hash-bulwark. Inferred.homing: Seeking projectile. Game wording: hot-potato: "Hops from pest to pest and blows up on the last one when its count runs out.". Weapons: hot-potato, seeker-sprouts, overgrowth, spicy-hot-potato. Inferred.nova: Expanding ring from Tibo. Game wording: mash-wave: "A ring of mashed potato rolls out from Tibo.". Weapons: mash-wave, mashquake. Inferred.tether: Clips a wire to a pest that zaps and slows what it touches. Game wording: potato-battery: "Clips a live wire onto a pest. The wire zaps and slows every pest it touches. The clipped pest can't die until the wire lets go.". Weapons: potato-battery, spud-grid. Inferred.rain: Projectiles fall around Tibo. Game wording: potato-rain: "Calls down a shower of potatoes around Tibo.". Weapons: potato-rain, golden-harvest. Inferred.nuke: Screen clear on a long cooldown (min 15 s); bosses only take a fraction. Game wording: reset-button: "We reset the limits. Clears every enemy on screen. Bosses only flinch.". Weapons: reset-button, factory-reset. Inferred.ring: Ring around Tibo that damages/statuses enemies crossing it. Game wording: root-cellar: "A ring of cellar cold around Tibo. Pests that cross it take frostbite and freeze for a moment. Bosses only slow.". Weapons: root-cellar, cold-storage. Inferred.strike: Hits appear under enemies (some chain/jump). Game wording: root-spikes: "Roots burst out of the ground under enemies.". Weapons: root-spikes, starch-bolt, taproot-eruption, thunder-tuber, starch-supercell. Inferred.bouncer: Projectile bounces off the edges of the view. Game wording: rosti-disc: "A flat rösti that bounces off the edges of the view.". Weapons: rosti-disc, rosti-storm. Inferred.orbit: Projectiles circle Tibo for a duration. Game wording: sprout-satellites: "Potato sprouts circle Tibo for a while.". Weapons: sprout-satellites, eternal-sprouts. Inferred.explosive: Projectile that explodes on impact (some cluster). Game wording: spud-cannon: "A classic potato gun. Shots burst on impact.". Weapons: spud-cannon, spud-dice, loaded-dice, spudzooka. Inferred.lob: Arcing thrown projectile that lands and explodes. Game wording: spud-lob: "Lobs heavy potatoes high. They crash down through crowds.". Weapons: spud-lob, sack-of-russets, spud-artillery. Inferred.chain: Spark that jumps between enemies. Game wording: starch-chain: "A spark that jumps from enemy to enemy.". Weapons: starch-chain, gratin-arc. Inferred.aura: Constant damage zone around Tibo. Game wording: steam-vent: "Hot steam from a baked potato hurts everything close.". Weapons: steam-vent, pressure-cooker. Inferred.mine: Places mines that burst when enemies come close. Game wording: tater-mines: "Buries spuds that burst when enemies step close.". Weapons: tater-mines, landmine-harvest. Inferred.familiar: Companion that follows Tibo and shoots. Game wording: tater-pup: "A loyal tot that follows Tibo and shoots.". Weapons: tater-pup, fry-brigade. Inferred.
Not determined: in-game labels for maxHpPct and dashGrace (absent from the label switch, so likely never shown as a stat row; inference).
3. Level-up rules
- Slots: 6 weapons, 6 passives. Verified.[97]
- Max levels: base weapons
min(8, levels+1)(all 37 base weapons reach 8); evolved/union weapons are level 1 only (cannot be levelled). Passives: maxLevel 5 (all 22). Verified.[98] - Number of cards: 3, plus a 4th with chance
min(1, luck). Verified (see Luck). - Offer pool and weights (
da): Verified.[99][100]- Upgrade an owned base weapon (not max, not banished):
max(30, weight) × 3. - New base weapon (only if a weapon slot is free; not owned, not already evolved/united away, not banished):
weight × 0.35 × (1926 / 2970), ×4 if it is a union partner of a weapon you hold. (2970 = sum of current base-weapon weights; normaliserca(target,total)=target/totalwhen total > target.) - Upgrade an owned passive:
weight × 3 × 0.8, ×4 if it is the catalyst for an item you hold. - New passive (only if a passive slot is free):
weight × 0.35 × (1720 / 1840), ×4 if it is an item catalyst for a held item. Verified.[101][102] - Cards are drawn without replacement by weight. Verified.[103]
- Upgrade an owned base weapon (not max, not banished):
- catalystGuaranteeLevel = 4 (6 in Story): if you have a free passive slot and own a base weapon at level ≥ 4 whose evolution catalyst you do not own (and have not banished), the last card is replaced by that catalyst passive (unless it is already offered). Verified.[104]
- Empty pool fallback: "Sack of Gold" (+25 gold) and "Baked Potato" (heal 30). If HP is full and only these remain, the gold is taken automatically (
autoGold). Verified.[105] - Reroll: spends 1 reroll, draws a fresh set (only allowed if the pool has something not already shown). Skip: spends 1 skip, closes the level-up with nothing. Banish: spends 1 banish, removes that weapon/passive from offers for the rest of the run, then redraws. Verified.[106][107]
- In-game tutorial wording: "reroll redraws, skip passes, banish bans a card for the run." Verified.[108]
- Rerolls/skips/banishes are not given at the start in normal config; they come from pickups (see section 6), the Seed Vault (+2/+1/+1) and dog digs. Inferred from the absence of any starting grant in config; the Loadout class starts at 0.[109]
- Whetstone offers up to 3 random owned, non-max, non-banished base weapons; picking one levels it. Verified.[110]
4. Evolution rules
- Evolutions, unions and item evolutions happen only when a chest is opened (boss/elite chests, Poteto's gift, Tater Slots jackpot). Paid crates, vendors and Microwave give items, not chest rolls. Verified.[111]
evolutionGateSec= 0, so there is no time gate. Verified.[112]- Weapon evolution requirement: the base weapon is at its max level (8) and you own its catalyst passive at any level. Verified.[113]
- Union requirement: you own both partner weapons (or the evolution of one), each is either max-level base or evolved, and at least one is a max-level base. The union replaces the first weapon's slot and removes the second, freeing a weapon slot. Verified.[114][115]
- UI wording: "Unites with {partner} into {result} when both reach LV {level}." (the UI simplifies the rule above). Verified.[116]
- Fork: if a weapon can both evolve and unite, that chest slot becomes a choice "Evolve or unite?" (cards tagged EVOLVE / UNITE). Verified.[117][118]
- Each chest reward slot is, in order of priority: an evolution/union if one is ready; otherwise a level-up of your highest-level non-max base weapon (ties go to the one with more damage dealt); otherwise a random non-max passive level; otherwise
20 × greed × goldScalegold. Verified.[119][120] - Chest size: 1 / 3 / 5 rewards, P(5) = 2%·(1+luck), P(3) = 18%·(1+luck) (cumulative check, see Luck). A 5-reward chest is shown as "Jackpot chest!" (inferred: title uses jackpot tier reward count).[121]
- Item evolution: you hold the base item and the catalyst passive is at max level (5). On the next chest opening, all copies of the item turn into the evolved item. Verified.[122][123]
- UI wording: "Evolves into {result} when {catalyst} reaches LV {level}." Verified.[124]
- Once an item has evolved, the base item stops dropping. Verified.[125]
- Recipe tables: see Evolutions.
Recipe tables (from sim.pt, names from en-content)
| Base weapon | Catalyst passive | Evolution |
|---|---|---|
Small Fry (small-fry) |
Fertilizer (fertilizer) |
Big Fry (big-fry) |
Grab Bag (grab-bag) |
Sprouting Eyes (sprouting-eyes) |
Bottomless Sack (bottomless-sack) |
Frying Pan (frying-pan) |
Starch Plating (starch-plating) |
Cast Iron (cast-iron) |
Root Cellar (root-cellar) |
Slow Cooker (slow-cooker) |
Cold Storage (cold-storage) |
Sprout Satellites (sprout-satellites) |
Slow Cooker (slow-cooker) |
Eternal Sprouts (eternal-sprouts) |
Reset Button (reset-button) |
Four-Eyed Spud (four-eyed-spud) |
Factory Reset (factory-reset) |
Butter Streak (butter-streak) |
Spud Sneakers (spud-sneakers) |
Ghee Glide (ghee-glide) |
Potato Rain (potato-rain) |
Golden Fry (golden-fry) |
Golden Harvest (golden-harvest) |
Starch Chain (starch-chain) |
Hot Sauce (hot-sauce) |
Gratin Arc (gratin-arc) |
Gravy Boat (gravy-boat) |
Potato Magnet (potato-magnet) |
Gravy Flood (gravy-flood) |
Hash Brown Shield (hash-brown-shield) |
Starch Plating (starch-plating) |
Hash Bulwark (hash-bulwark) |
Hash Boomerang (hash-boomerang) |
Sour Cream (sour-cream) |
Hashnado (hashnado) |
Deep Fryer (deep-fryer) |
Bacon Bits (bacon-bits) |
Inferno Basket (inferno-basket) |
Chip Shuriken (chip-shuriken) |
GPU (gpu) |
Kettle Cyclone (kettle-cyclone) |
Tater Mines (tater-mines) |
Rotten Spud (rotten-spud) |
Landmine Harvest (landmine-harvest) |
Spud Dice (spud-dice) |
Four-Eyed Spud (four-eyed-spud) |
Loaded Dice (loaded-dice) |
Peeler (peeler) |
Gravy Heart (gravy-heart) |
Mandoline Fury (mandoline-fury) |
Mash Wave (mash-wave) |
Big Bowl (big-bowl) |
Mashquake (mashquake) |
Mezzaluna (mezzaluna) |
Rotten Spud (rotten-spud) |
Moon Cleaver (moon-cleaver) |
Seeker Sprouts (seeker-sprouts) |
Fertilizer (fertilizer) |
Overgrowth (overgrowth) |
Frozen Fries (frozen-fries) |
Thick Skin (thick-skin) |
Permafrost Fries (permafrost-fries) |
Steam Vent (steam-vent) |
Root Network (root-network) |
Pressure Cooker (pressure-cooker) |
Fry Lance (fry-lance) |
Spud Sneakers (spud-sneakers) |
Railfry (railfry) |
Rösti Disc (rosti-disc) |
Starch Plating (starch-plating) |
Rösti Storm (rosti-storm) |
Spud Lob (spud-lob) |
Butter Pat (butter-pat) |
Sack of Russets (sack-of-russets) |
Salt Line (salt-line) |
Spud Sneakers (spud-sneakers) |
Salt Flats (salt-flats) |
Hot Potato (hot-potato) |
Hot Sauce (hot-sauce) |
Spicy Hot Potato (spicy-hot-potato) |
Potato Battery (potato-battery) |
Root Network (root-network) |
Spud Grid (spud-grid) |
Spud Cannon (spud-cannon) |
Hot Sauce (hot-sauce) |
Spudzooka (spudzooka) |
Whisk Vortex (whisk-vortex) |
Big Bowl (big-bowl) |
Stand Mixer (stand-mixer) |
Root Spikes (root-spikes) |
Masher Grip (masher-grip) |
Taproot Eruption (taproot-eruption) |
Fry Knives (fry-knives) |
Salt Shaker (salt-shaker) |
Thousand Fries (thousand-fries) |
Starch Bolt (starch-bolt) |
Sprouting Eyes (sprouting-eyes) |
Thunder Tuber (thunder-tuber) |
Tot Toss (tot-toss) |
Chives (chives) |
Tot Gatling (tot-gatling) |
Julienne Dash (julienne-dash) |
Butter Socks (butter-socks) |
Waffle Cut (waffle-cut) |
| Weapon A | Weapon B | Union |
|---|---|---|
Fry Cook Sentry (fry-sentry) |
Tater Tot Pup (tater-pup) |
Fry Brigade (fry-brigade) |
Spud Lob (spud-lob) |
Spud Cannon (spud-cannon) |
Spud Artillery (spud-artillery) |
Starch Bolt (starch-bolt) |
Starch Chain (starch-chain) |
Starch Supercell (starch-supercell) |
| Item | Catalyst passive (must be max level) | Evolved item |
|---|---|---|
Wandering Eyes (wandering-eyes) |
Four-Eyed Spud (four-eyed-spud) |
All-Seeing Spud (all-seeing-spud) |
Coal-Baked Spud (coal-baked-spud) |
Hot Sauce (hot-sauce) |
Bonfire Spud (bonfire-spud) |
Porcupine Spud (porcupine-spud) |
Starch Plating (starch-plating) |
Fry Volcano (fry-volcano) |
Freezer Burn (freezer-burn) |
Slow Cooker (slow-cooker) |
Glacier Spud (glacier-spud) |
Boom Spud (boom-spud) |
Rotten Spud (rotten-spud) |
Kaboom Spud (kaboom-spud) |
Spicy Croquette (spicy-croquette) |
Big Bowl (big-bowl) |
Magma Croquette (magma-croquette) |
Heavy Russet (heavy-russet) |
Masher Grip (masher-grip) |
Meteor Russet (meteor-russet) |
Static Tot (static-tot) |
Root Network (root-network) |
Storm Tot (storm-tot) |
5. Unlocks and progression
- No gameplay meta-progression found. All weapons, passives, items, dogs and difficulties are available from the first run; there is no persistent currency, upgrade shop or stat carry-over in the sim. The only things saved between runs are settings, the run-picker state, the encyclopedia "discovered" lists and the list of evolution/union/item-evolution results you have made. Inferred from the full list of storage keys found in the bundle (
tibo.settings.v1,tibo.discovery.v1,tibo.evolutions.v1, plustibo.page.*locks and thetibo.safe-startIndexedDB crash guard) and from the sim taking no saved state.[126] - Encyclopedia discovery (
tibo.discovery.v1): tracks seven shelves: weapon, passive, item, enemy, boss, biome, dog. Verified.[127]- An entry is discovered when it is offered or seen, not only when taken: level-up/whetstone cards, chest rewards, items found, vendor stock, microwave offers, dog swap offers, plus a scan every 30 ticks of your loadout, enemies and bosses on the field, your dogs and the biomes visited. Verified.[128][129]
- Undiscovered entries show "?" and "Not found yet". Shelves: Weapons, Evolutions, Passives, Items, Enemies, Bosses, Biomes, Dogs. Verified.[130]
- Recipe knowledge (
tibo.evolutions.v1): every evolution, union and item evolution you complete is remembered. Item-evolution results you have never made appear as "???" in recipe views. Verified.[131][132] - Story → difficulties: difficulties are not locked. Before Story is finished the run picker shows Story + Easy/Normal/Hard/Nightmare with Story selected and featured; once a Story run reaches its ending, Story is dropped from the picker and the default becomes Easy (then your last pick). Verified.[133][134]
- Dog breeds / legendary dogs: nothing is unlocked permanently; legendary dogs are rolled per run seed (section 10). Verified by absence of dog data in any store (inference for "nothing permanent").
- Medals: the
sprite-medalchunk is only used to draw icons in the end-of-run stats panel; no medal/achievement unlock logic was found. Inferred.[135]
6. POIs, shrines, events, helpers, pickups
Activation: you stand inside a POI's radius; charge goes +1 per tick while inside (and the POI's condition holds) and −2 per tick outside; it fires at activation.seconds[kind] × 60 ticks. Verified.[136][137]
Charge times (s): item 0.75, chest 1, crops 1, taterLantern 1, taterMagnet 1, dog 1, boss/Poteto gold chest 1.5, curseShrine 1.5, dinnerBell 1.5, eliteNest 1.5, microwave 1.5, paidChest 1.5, taterSlots 1.5, vendor 1.5, whetstone 1.5, dogKennel 1.5, spudFreezer 1.5, botDock 2, seedVault 2, rootTunnel 2, frenzyFryer 2, bossLair 3, chargeShrine 5, goldenShrine 5. Verified.[138]
POIs are placed per 256-unit chunk using per-biome poiWeights. Verified (registry biomes). Game names below are from en-content (content.poi.*.name).
- Potato Crate (
paidChest): pay the crate price (section 1), get one random item (rarity by luck). Verified.[139] UI: "Costs gold. Holds an item. Each crate costs more than the last." - Spud Shrine (
chargeShrine, 5 s): offers 3 random stats from the shrine list, each with a random rarity; value = base × rarityMult [1,1.2,1.4,1.7,2]. Stats and bases: Damage 0.06, Area 0.06, Cooldown 0.03, Move speed 0.05, Max HP 12, Armor 10, Luck 0.06, Gold gain 0.1, XP gain 0.06, Crit 0.04, Duration 0.06, Pickup range 0.15. Verified.[140] - Golden Spud Shrine (
goldenShrine, 5 s): same, but every offer's rarity is Epic (3), or Legendary (4) with 30% chance. Verified.[141] - Moldy Shrine (
curseShrine): accept to gainround((45 + 9*minutes) * greed)gold, +0.1 curse and +1 curse taken. Verified.[142][143] - Fry Cart (
vendor): stocks 3 items; the third gets a bumped rarity (Common→Uncommon, Uncommon→Rare, Rare→Epic, Epic/Legendary→Legendary). Price =vendorPrice[rarity] × (1 + 0.06·minutes) × chestPriceMult, vendorPrice = [80,140,240,400,640]. One purchase per cart, then it leaves. Verified.[144][145] - Microwave: copy one of your base items; cost mode is
melt: another base item of the same rarity (the one you hold fewest of) loses one copy. Verified.[146] UI: "Copy an item. Another of its rarity melts."[147] (Config also defines unusedgoldandcursecost modes. Verified config, unused = inferred fromcost:"melt".) - Crop Patch (
crops): drops 3–7 coins (×biome gold mult) and a 35% chance of a 30-HP snack. Verified.[148] - Beetle Nest (
eliteNest): spawns an elite; tier bonusfloor(tier/3), HP ×(1 + 0.15·tier) (tier = the POI's tier, inferred to grow with distance). Verified.[149] - Boss Lair (
bossLair, 3 s): wakes a boss chosen frombossOrderat indexclamp(floor(minutes/2) + floor(tier/2), 1, 11), HP ×(0.8 + 0.1·tier). Verified.[150] - Stray Dog (
dog): see section 10. - Bot Dock (
botDock, 2 s): calls a Grok Bot swarm (same as a Bot Beacon). Verified.[151] - Spud Whetstone (
whetstone): level up one of up to 3 offered weapons (section 3). Only chargeable if a weapon can still level. Verified.[152] - Dinner Bell (
dinnerBell): spawns 20 pests in a ring of radius 130 around you; they give 3× XP, always drop gold and despawn after 30 s. Verified.[153] - Tater Slots (
taterSlots): pay half a crate price; three reels of 4 symbols. Three of a kind = a 1-reward chest; two of a kind = a random item; otherwise nothing. Verified.[154] (UI desc says "Match two for an item.") - Seed Vault (
seedVault, 2 s): +2 rerolls, +1 skip, +1 banish. Verified.[155] - Tater Magnet (
taterMagnet): pulls every gem and coin on the map to you (only chargeable if any exist). Verified.[156] - Root Tunnel (
rootTunnel, 2 s): digs you to a far POI at least 900 units away; you are invulnerable during dig+rise (90 ticks). Verified.[157] - Tater Lantern (
taterLantern): breaks into one token tossed just past your pickup range: skip (weight 4), magnet (4), reroll (3), banish (2), Bot Beacon (1). Verified.[158] - Spud Freezer (
spudFreezer): freezes pests within 200 for 5 s; bosses are slowed 50% instead. Verified.[159] - Frenzy Fryer (
frenzyFryer, 2 s): 20 s frenzy: weapon timers run 1.4× faster and move speed ×1.2. Verified.[160][161] - Dog Kennel (
dogKennel): see section 10.
World events
Events fire from the wave table each minute (section 7) or once per landmark; non-gold-rush events need spawn room. Verified.[162]
swarm(Fry Fly Swarm 26 flies, Lunch Rush 24 gulls): a band of enemies crossing from one side, count scaled by enemy-count scaling (max 2×), despawn after 14 s. Verified.[163]wall(Ring of Hay Bales 36, Strike 10 bowling balls): a ring of enemies closing in, count × difficultywallPests(0.15 on Easy). Verified.[164]lootRunner(Golden Scarab, Lost Saucer): a fleeing enemy that despawns afterescapeAfter; killing a loot runner drops an item. Verified.[165]goldRush(Harvest Moon 30 s, Loose Change 20 s): coins worth 2×. Verified.[166]
Grok bots
- A wandering bot spawns 280 units ahead at 0:20 and then every 35 s (max 2 out). If you come within 140 it hops to you (8 s), helps when it touches you, cheers and beams away; otherwise it leaves after 40 s or at 700 distance. Verified.[167]
- Help kind is weighted: Zapper 3, Gift Bot 3, Bubble Bot 2, Tidy Bot 2. Verified (registry botHelps).
- Zapper (
zap): hits the 6 nearest targets within 120: pests die outright, elites lose 15% max HP, bosses are excluded. Verified.[168] UI: "Zaps the closest bugs. Elites only get a jolt, and bosses are too big to zap." - Gift Bot (
gift):round(6 + 1.5·minutes)coins, an XP gem worth 40% of your current level, 5% chance of a Bot Beacon. Verified.[169] - Bubble Bot (
shield): 3 s invulnerability and heals 20% max HP. Verified.[170] - Tidy Bot (
tidy): pulls all gems and coins within 600. Verified.[171]
- Zapper (
- Bot Beacon pickup / Bot Dock: spawns a swarm of 8 Zapper-type bots for 6 s around you. Verified.[172]
Poteto visits
- Visits once per run (visitChance 1) at a random time between 4:00 and 12:00. Verified.[173]
- Walks up and bonks Tibo: a 1.5 s stun (outside the story ending). With 25% chance it also drops a gift chest. Verified.[174][175][176]
- Gift chest: 5 rewards. First up to 1 evolution/union if one is ready; then the 2 non-gold gifts are taken in order from a table, each kind at most once per chest when possible: (1)
catalyst: grants the missing catalyst passive of a max-level base weapon (only if no evolution was used and a passive slot is free); (2)weaponLevel: +1 level to a non-max base weapon (sorted to prefer weapons that have an evolution); (3)newWeapon: the first weapon you lack from Peeler, Mash Wave, Tot Toss, Starch Bolt, Hash Boomerang, Spud Lob, Gravy Boat, Starch Chain, Deep Fryer, Chip Shuriken, at level 1; (4)itemLevel(despite the name, +1 level to a non-max passive). Remaining slots are gold worthround(100 × greed)each. Verified.[177][178][179]
Alex
- Revealed when the boss "amusing" dies; joins with 25% chance in standard runs (100% in Story), otherwise "asleep". Verified.[180]
- When joined: follows Tibo; every 9 s throws its head 70 units behind you; for 4 s pests within 85 are lured to it, then it bursts for
30 × damageMultin radius 51 with knockback. Verified.[181]
Pickups (boot si: xp, gold, heal, magnet, chest, reroll, skip, banish, item, beacon)
Effects on collection. Verified.[182]
- xp: value × growthMult. gold: value × greedMult (× gold rush). heal: restores its value (30 HP from drops). magnet: pulls all gems and coins. reroll/skip/banish: +1 each. beacon: Grok bot swarm. chest/item: must be stood on to open (1 s / 0.75 s; boss & Poteto chests 1.5 s). Verified.
- Normal pest kill drops (independent of XP gem): heal 0.4%, magnet 0.12%, reroll 0.18%, skip 0.12%, banish 0.08%. Verified.[183]
- Elite kill: gold coin worth 10 × the biome's goldMult (always), chest, 50% reroll, 25% banish, 10% Bot Beacon. Verified.[184]
- Boss kill: XP gem, 25-gold coin (× biome) and a chest. Verified.[185]
7. Boss and elite schedule
The wave table (boot zi, rows per minute) drives bosses, elites and events at each new minute. Verified.[186][187]
| Minute | Boss (game name) | Elite | Events |
|---|---|---|---|
| 0 | |||
| 1 | yes | ||
| 2 | amusing | Fry Fly Swarm | |
| 3 | Colorado Queen (beetle-queen) |
yes | Golden Scarab |
| 4 | The Masher | ||
| 5 | yes | Fry Fly Swarm | |
| 6 | Late Blight | Ring of Hay Bales | |
| 7 | Sir Strawman | yes | Golden Scarab |
| 8 | The Fryer King | Fry Fly Swarm, Harvest Moon | |
| 9 | yes | Ring of Hay Bales | |
| 10 | The Great Wireworm | Golden Scarab | |
| 11 | yes | Fry Fly Swarm | |
| 12 | The Mandoline | ||
| 13 | yes | Ring of Hay Bales | |
| 14 | The Deep Freezer | Golden Scarab | |
| 15 | yes | Fry Fly Swarm, Harvest Moon | |
| 16 | Grater Golem | Ring of Hay Bales | |
| 17 | Compost Titan | yes | Golden Scarab |
| 18 | Ketchup Kraken | Fry Fly Swarm | |
| 19 | yes | Ring of Hay Bales | |
| 20 | The Harvester | Fry Fly Swarm |
Source row example:[188] Verified.
- Bosses spawn in a random direction at half the smaller view dimension + 20 from you (so roughly at the screen edge; "off-screen" is inference). Verified.[189]
- Boss HP =
def.hp × (1+curse) × 4^overtime × hpStep × difficulty.bossHp, hpStep = 1 before 16:00, 3 from 16:00, 4 from 18:00, 6 from 20:00. Damage scales with the pest damage curve. Verified.[190] - Elites: HP is interpolated along the curve (300 at 1:00, 390 at 3:00, 750 at 5:00, 1300 at 8:00, 2400 at 10:00, 10000 at 15:00, 18000 at 17:00, 32000 at 20:00), × (1+curse) × 4^overtime × difficulty.eliteHp; damage ×1.6; speed 0.95–1.3; body 1.7× larger; 12× XP; always drop gold and a chest; enemy tier from the curve (2→5). Verified.[191][192]
- At 20:00 (
win.timeSec1200) the run is marked won and a "You survived 20:00!" message is shown;win.modeiscontinue, so the run simply keeps going. Verified.[193][194] win.finalBoss= harvester is only used by thefinalBosswin mode (invincible Harvester with 1.6× speed, 10× damage); no difficulty or Story config selects that mode in this build, so The Harvester arrives as the normal minute-20 wave boss. Verified that the code path exists and thatIedoes not overridewinfor standard difficulties; "unused" is inference.[195]- The UI also contains an unused-by-default win panel: "Keep going" / "Enemies grow stronger every minute" and "End the run" / "Take the win" (shown for the
choosemode). Verified strings; reachability is inference.[196] - After the last row (from minute 21 on,
postWin): every minute an elite spawns, a boss spawns cycling The Fryer King → The Mandoline → The Masher → Grater Golem → Compost Titan → Ketchup Kraken → The Harvester, and an event alternates Fry Fly Swarm (even minutes) / Ring of Hay Bales (odd). Verified.[197][198] - Overtime scaling makes everything ×4 HP/damage per minute after 20:00 (section 1), so the post-win phase is effectively endless and lethal. Verified formula; "effectively endless" is inference.
- Nightmare uses the
classicopening: minutes 0–2 use denser rows (34–35, 75–100, 75–100 enemies). Verified.[199] - Difficulty
tierLag(Easy 3) makes enemy tiers follow the wave row from N minutes earlier. Verified.[200]
8. Controls and settings
Keyboard (from T.keys in boot, plus input code)
- Move: WASD or arrow keys (the on-screen hint relabels WASD for AZERTY/QWERTZ/Dvorak/Colemak layouts). Verified.[201]
- Dash Space; Reroll R; Skip X; Banish B; Back Escape; Play again R; Share S; auto-advance story lines T; skip lines Space. Verified.[202]
- Pause: P or Escape. Mute: M. Verified.[203][204]
- Choice cards get number hotkeys 1, 2, 3… in order; Enter/Space activate the focused button; arrow keys move focus in lists. Verified.[205][206]
- Dash rules: 48 units over 9 ticks, 5 s recharge per charge, 1 charge base, 6-tick input buffer. Verified.[207] During a dash (and
dashGraceafter) Tibo ignores normal damage. Verified.[208]
Touch
- "Drag anywhere to move", "Flick to dash" (a flick of ≥ 22 px within 260 ms), pause button, "Tap to resume". Verified strings/config.[209][210]
Gamepad
- Not determined / no gamepad controls found: the only gamepad reference records that a pad was connected (input-device analytics). Inferred.[211]
Settings (boot st defaults; labels from settings-panel)
Defaults:[212] Verified. Tabs: Sound, Language, Comfort, Graphics, Privacy.[213]
| Key | UI label | Default | Notes |
|---|---|---|---|
effectsOpacity |
Weapon effects | 1 | slider |
shake |
Screen shake | full | Off / Low / Full |
flashes |
Flashing effects | on | |
damageNumbers |
Damage numbers | on | |
hitbox |
Hitbox | off | help: "Shows where shots hit Tibo." |
recommendedPicks |
Recommended picks | on | marks one card as recommended (heuristic in picks chunk) |
musicVolume |
Music volume | 1 | Sound tab |
soundVolume |
Sound volume | 1 | Sound tab |
quality |
Quality | auto | Auto / Low / Medium / High |
colorVision |
Color-blind mode | off | Off / Protanopia / Deuteranopia / Tritanopia |
picker |
(not shown) | {storyDone:false} | run-picker memory |
autoAdvance |
(T key) | true | auto-advances story lines |
- Recommended pick heuristic (what the "recommended" tag picks): upgrading a weapon whose catalyst you own scores 20; new passives that are a catalyst for one of your weapons get a bonus that rises as the weapon nears max level; Peeler, Mash Wave and Tot Toss get an extra +5; heal is preferred under 50% HP; chest forks pick Unite if weapon slots are full, else Evolve; Moldy Shrine is always declined. Verified.[216][217]
- The Analytics toggle lives in Privacy: "Anonymous gameplay stats help poteto improve the game. No name, account or IP address is recorded." Verified.[218]
9. Modes
- Picks and captions (UI): Story "A short first game. Play this first."; Easy "Fewer, weaker pests and a tougher Tibo."; Normal "The game as designed."; Hard "More pests, harder hits, tougher bosses."; Nightmare "Crowds, a curse and no revives." Verified.[219][220]
- Story runs use the Normal difficulty table plus the story script. Verified.[221]
Difficulty table (Ne in sim, applied by Ie)
| Easy | Normal | Hard | Nightmare | |
|---|---|---|---|---|
crowd size (alive) |
×0.55 | ×1 | ×1.25 | ×1.36 |
| pest HP | ×0.5 | 1 | ×1.2 | ×1.75 |
| enemy damage | ×0.4 | 1 | ×1.1 | ×1.3 |
| boss HP | ×0.5 | 1 | ×1.25 | ×1.25 |
| elite HP | ×0.5 | 1 | ×1.25 | ×2 |
| Tibo max HP | ×2 | 1 | 1 | 1 |
| XP | ×1.5 | 1 | 1 | ×1.5 |
| extra revives | +2 | 0 | 0 | revives disabled |
| start curses | 0 | 0 | 0 | 1 (+0.1 curse, 1 curse taken) |
| enemy tier lag | 3 min | 0 | 0 | 0 |
| wall-event pests | ×0.15 | 1 | 1 | 1 |
| overtime hold | 60 s | 0 | 0 | 0 |
| crowd ramp / floor | default | default | own ramp, floor 85% | own faster ramp, floor 85%, early damage ramp time ×0.2, classic dense opening |
- Nightmare's start curse: +0.1 curse and +1 curse taken per start curse. Verified.[225]
Story mode script (sim.Yt)
All Verified from config.json sim.Yt / sim-DQNACfZS.js M={startChest:.
- Start chest 56 units to the right. Tibo is on a leash (radius 220) until he opens it; nag lines at 8/16/24 s, then he is pulled in ("jailed") at 32 s. Contents: Peeler L4, Tot Toss L3, Thick Skin L2.[226]
- First rush: crowd ramps 10→200 over the rush, then the boss "amusing" lands (
landTicks1800 = 30 s into the story phase; itsheal:1field is not traced, so "heals Tibo" is inference). First-rush pests have ×3 HP, but Tibo's first hit on a non-elite always kills, and his first kill grants level 2.[227][228] - Killing amusing reveals Alex (100% join in Story) and starts the second rush (crowd 220→400, pests ×0.3 HP, no XP), followed by Poteto's visit.[229]
- In Story, HP floors at 1 during the first rush, and during the second rush once Poteto's visit is due. Verified.[230] In other story phases, reaching 0 HP "knocks" Tibo and the story revives him, each time with a growing boost (+0.15 damage and +0.1 move speed scaled by
n + n(n-1)/2).[231] - Ending: Poteto visits 900 ticks after Alex joins and delivers lines (
inTheGame,foundYou,storyMode,noobs,madeSure,laugh,napTime), knocks Tibo out, thenrealDifficulty. Reaching this ending marks Story done for the run picker.[232] - Story config overrides: catalystGuaranteeLevel 6, no random Poteto visit, only POIs crops, Tater Magnet, Spud Freezer, Frenzy Fryer, Bot Dock, Tater Lantern, Seed Vault; waves are only
reset-zombiepests.[233] - End-of-story picker text: "Now play the real game." Verified.[234]
Run sharing links (/r/)
- The end-of-run share builds a URL
https://tibo.dog/r/?run=…&<stats>=…with base64url-encoded run data (seed, stats, biomes, Alex, tally, build). Links over 8192 chars or with a bad format/ID are ignored. Verified.[235][236] - Opening a link shows a "Shared run" stats panel with a "Play" button that starts a new game (test id
play-new; not a replay of the shared seed, inferred).[237] - Share copy says the post is for X ("Share on X", "Copy the post, then paste it into a post or reply on X."). Verified.[238]
10. Dogs
- First dog: a stray-dog POI is placed 110 units from the start (at angle 0.6 rad), or at the nearest free spot if that overlaps another POI. Verified.[239]
- Other strays appear as
dogPOIs (biome poiWeights). Breed is weighted: Retriever 12, Terrier 12, Beagle 10, Collie 10, Corgi 10, Dachshund 10, Greyhound 8, Saint Bernard 8; legendary breeds have weight 0. Verified.[240] - Legendary dogs: per run seed, each non-fusion legendary (Link the Pomeranian, Zelda the Chihuahua-Pomeranian) is independently "in the run" with chance 1/6; dog POIs in chunks whose centre is at least 1200 units from the origin take them from that queue. Verified.[241][242]
- Fusion: holding Link and Zelda fuses them into Zelda & Link (keeps the higher level). Verified.[243]
- Joining: walking onto a stray of a breed you already have levels that dog up; otherwise it joins if the pack has room (maxPack 3); if full you get a swap choice ("Swap out {member}" / "Keep my pack"). Verified.[244]``]
- Dog levels: max level = number of levelUps + 1 = 5. Per level: ability power ×
power, ability cooldown ×every, aura × (1 + sum ofaura). Verified.[245][246] - Dog Kennel (training): buy levels for a pack dog at
kennelPrices[level-1] × chestPriceMult= 120 / 240 / 450 / 720; up to 3 buys per kennel. Verified.[247] UI: "Train up to # more levels here, then the kennel closes".[248] - Every dog's aura is added to your stats like an item (e.g. Beagle luck +0.1, Corgi armor +6). Verified.[249]
- Ability kinds (Verified from
Tl/xlin sim-DQNACfZS.js unless noted):bite(Terrier "Pip"): darts at the nearest enemy within range 110 and deals22 × power × damageMult.[250]rescue(Saint Bernard "Barrel"): when HP < 40%, runs to you and heals25 × power.zoomies(Greyhound "Zoom"): sprints 150 units through enemies,30 × power × damageMultto all in the line.fetch(Retriever "Biscuit"): pulls gems/coins/snacks within120 × sqrt(power)(not chests/items).herd(Collie "Moss"): pushes enemies around you within46 × sqrt(power)back.dig(Dachshund "Frank"): digsround(6 × power)coins; 20% chance of an extra find (70% a 30-HP snack, else a reroll).[251]sniff(Beagle "Scout"): points to the nearest crate/shrine/dog-type POI and dropsround(3 × power)coins.bark(Corgi "Waffles"): freezes nearby non-hidden enemies within60 × sqrt(power)for1 × sqrt(power)s; bosses are not affected per UI text.[252]puff(Link, legendary): holds fluff charges (1, 2 from level 3) that cancel the next hit; when a charge is spent it sneezes pests away (push 220, stun 0.5 s from level 5).[253]listen(Zelda, legendary): marks the nearest elite/loot runner within 600 (1/1/2/2/3 marks by level) for 8 s × power: +25% damage taken.[254]eclipse(Zelda & Link): both of the above, plus every 50 s a corona that adds a fluff charge and marks every pest within 110 (+10% for 1 s). Verified in run-extras-CdCBynDO.js.
- Dog names/descriptions are in en-content (
content.dog.<id>.name/.breed/.desc), e.g. "Darts at the nearest enemy and bites it, about once a second." Verified.
Sources
Game version 75: main-CSuyb4iv.js / boot-CPW8VVmR.js / sim-BkfBhQfw.js from https://tibo.dog/, researched 2026-11-05. See Versions & updates.
- Deployed tibo.dog JavaScript, quoted inline
Code references
- boot-DIJlLGwy.js
function v(e){return _(Math.round(60*e))} - sim-DQNACfZS.js
function ot(e,t){let n=Math.max(0,t-1);return Math.round(e.xpFirst+e.xpLinear*n+e.xpCurve*xe(n,e.xpExponent))} - sim-DQNACfZS.js
o===D.xp)e.gemCount--,e.counters.gems++,Ci(e,a*i.growthMult) - sim-DQNACfZS.js
n.xp-=n.xpNext,n.level++,n.xpNext=ot(e.cfg.levelUp,n.level),n.pendingLevels++ - sim-DQNACfZS.js
for(let e of this.passives)n[e.def.stat]+=xt(e.def.perLevel,e.level) - sim-DQNACfZS.js
for(let e of ht)n[e]+=this.shrineTotals[e] - sim-DQNACfZS.js
i[r.damageMult]=Math.max(.1,1+e.might),i[r.areaMult]=Math.max(.2,1+e.area) - sim-DQNACfZS.js
i[r.durationMult]=Math.max(.2,1+e.duration+t.player.baseDuration) - sim-DQNACfZS.js
i[r.amountBonus]=Math.floor(e.amount)+t.player.baseAmount - sim-DQNACfZS.js ``return e!==
aura&&e!==cone&&e!==nuke&&e!==`ring``` - sim-DQNACfZS.js
i[r.maxHp]=Math.max(1,(t.player.baseMaxHp+e.maxHp)*(1+e.maxHpPct)*t.level.maxHp) - sim-DQNACfZS.js
i[r.knockbackMult]=Math.max(0,1+e.knockback),i[r.pierceBonus]=Math.floor(e.pierce) - sim-DQNACfZS.js
i[r.cooldownMult]=1-we(e.cooldown,-1,n.cooldownCap) - sim-DQNACfZS.js
cooldownCap:.6 - sim-DQNACfZS.js
let o=(a===`nuke`?e.cfg.reset.minCooldownSec:.05)*Xa(e);return n.cooldown=i.cooldown<=0?0:Math.max(o,i.cooldown*r.cooldownMult) - sim-DQNACfZS.js
function Ee(e,t){return e<=0?0:e/(t+e)} - sim-DQNACfZS.js
i[r.critChance]=Ee(e.critChance,n.critK),i[r.critMult]=n.baseCritMult+e.critDamage,i[r.evasion]=Ee(e.evasion,n.evasionK) - sim-DQNACfZS.js
i[r.chestPriceMult]=1-Ee(e.chestDiscount,n.discountK) - sim-DQNACfZS.js
let s=o.critChance+a.critBonus[r];s>0&&e.rng.next()<s&&(l*=o.critMult,u=!0) - sim-DQNACfZS.js
if((i||t.has(n,x.Elite))&&(l*=o.eliteMult*a.eliteMult[r]) - sim-DQNACfZS.js
if(!r&&s.evasion>0&&e.rng.chance(s.evasion))return function(e){e.recordEvents&&F(e,{type:`dodge`})}(e),!1 - sim-DQNACfZS.js
function Rr(e,t,n){let r=n.armor;return Math.min(r.cap,Ee(e,r.k*t))} - sim-DQNACfZS.js
let u=l*(1-Rr(s.armor,i,e.cfg)) - sim-DQNACfZS.js
function Lr(e,t){let n=e.armor;return n.firstLevel+Math.floor(t/60*n.levelsPerMinute)} - sim-DQNACfZS.js
n.level[r]=Lr(o=e.cfg,e.runClock)+o.armor.bossLevels - sim-DQNACfZS.js
if(!(i.kind!==`guard`||r.active<1))return r.active--,r.counter++,Xe(e),i.soak - sim-DQNACfZS.js
i[r.curse]=Math.max(0,e.curse)+t.difficulty.globalCurse - sim-DQNACfZS.js
i[r.greedMult]=(1+e.greed)*(1+n.curseRewardShare*Math.max(0,e.curse)) - sim-DQNACfZS.js
e.scratch.hp=t.hp*(1+n) - sim-DQNACfZS.js
e.scratch.speed=t.speed*(1+.3*n) - sim-DQNACfZS.js
d=Math.min(e.cfg.spawns.maxEnemies,Math.round(u*e.cfg.spawns.ceilingScale*l.count*(1+s))) - sim-DQNACfZS.js
n.spawnTimer-=t*l.rate*(1+s)*ls.density - sim-DQNACfZS.js
let o=r.hp*t.hpMult*(1+e.loadout.stats.curse)*i.hpPost*e.cfg.level.eliteHp - sim-DQNACfZS.js
function Ba(e,t){return Math.floor(t*e.extraElitesPerCurse)}function Va(e,t){return Math.floor(t*e.extraBossesPerCurse)} - sim-DQNACfZS.js
function Qs(e,t,n){return t.has(n,x.Chest)?t.has(n,x.CursedExtra)?Ha(e.cfg.difficulty):1:0} - choices-7yGAe9Ru.js
Curse makes enemies tougher, faster and more numerous, and raises XP and gold. - sim-DQNACfZS.js
return t.baseChoices+ +(e.lootRng.next()<Math.min(1,e.loadout.stats.luck/t.luckForFourthChoice)) - sim-DQNACfZS.js
e.cfg.economy.rarityWeights.map((e,n)=>e*se(1+t,n)) - sim-DQNACfZS.js
return r<t.chanceFive*n?5:r<(t.chanceFive+t.chanceThree)*n?3:1 - deferred-ui-BYWEYMjM.js
Better odds of a fourth level-up card, bigger chests and rarer items. - sim-DQNACfZS.js
let t=1*a*i.greedMult*(e.time<e.goldRushUntil?e.goldRushMult:1) - sim-DQNACfZS.js
let s=Math.round(e.lootRng.range(it[0],it[1])*n*e.loadout.stats.greedMult*a) - sim-DQNACfZS.js
return Math.min(n.goldCap,1+n.goldPerMinute*(e.runClock/60)) - sim-DQNACfZS.js
i[r.growthMult]=(1+e.growth)*(1+n.curseRewardShare*Math.max(0,e.curse))*t.level.xp - sim-DQNACfZS.js
i[r.magnetRadius]=t.player.baseMagnet*Math.max(.2,1+e.magnet) - sim-DQNACfZS.js
let f=!rc(d),p=r-l,m=i-u,h=p*p+m*m;if(n.vacuum[o]===1||f&&h<s) - sim-DQNACfZS.js
i.recovery>0&&Si(e,i.recovery*t) - sim-DQNACfZS.js
n.hp[e.me]=Math.min(e.loadout.stats.maxHp,n.hp[e.me]+t) - sim-DQNACfZS.js
let i=e.loadout.stats.healOnKill;i>0&&Si(e,i) - sim-DQNACfZS.js
i[r.revivals]=t.level.allowRevives?Math.floor(e.revival)+t.level.addedRevives:0 - sim-DQNACfZS.js
t.revivesUsed++,e.run.revivesUsed++,n.hp[e.me]=.5*t.stats.maxHp,n.invuln[e.me]=2 - sim-DQNACfZS.js
function Ii(e){return e.level.allowRevives?c.items:Fi} - sim-DQNACfZS.js
function zr(e,t){return Math.max(0,(t-e.win.timeSec-e.level.overtimeHoldSec)/60)} - sim-DQNACfZS.js
e.hp=(1+r.hpLinearPerMin*a+r.hpQuadraticPerMin*a*a)*s*t.level.pestHp,e.hpPost=s - sim-DQNACfZS.js
let c=n<r.earlyDamageFullAtSec?r.earlyDamageStart+(1-r.earlyDamageStart)*n/r.earlyDamageFullAtSec:1 - sim-DQNACfZS.js
e.speed=Math.min(r.maxSpeedMult,xe(r.speedPerMin,o)),e.count=xe(r.countPerMin,o),e.rate=xe(r.spawnRatePerMin,o) - sim-DQNACfZS.js
e.bulletCount=Math.min(r.maxBulletCountMult,1+r.bulletCountLinearPerMin*u) - sim-DQNACfZS.js
n.maxHp[r]=i.hp*t.hpMult*(1+e.loadout.stats.curse)*a.hpPost - sim-DQNACfZS.js
Math.round(n.chestPriceBase+n.chestPriceLinear*t+n.chestPriceQuad*t*t+n.chestPriceCubic*se(Math.max(0,t-n.chestPriceKnee),3)) - sim-DQNACfZS.js
function wo(e){return Math.ceil(ki(e)*e.cfg.taterSlots.priceFraction)} - sim-DQNACfZS.js
c=n.damage*function(e){let t=e.cfg.items.powerPerMinute;return e.loadout.stats.damageMult*(1+t*e.runClock/60)}(e)+n.thornsShare*i.thorns*i.damageMult - sim-DQNACfZS.js
i.kind!==`onHurt`||e.tick<r.readyAt||(r.readyAt=e.tick+re(i.cooldown),Us(e,r,i.proc)) - sim-DQNACfZS.js
let n=1-we(e.player.hp[e.me]/e.loadout.stats.maxHp,0,1),r=e.cfg.hpEdge.fullHpPenalty;return 1-r+(t.hpEdge.lowHpBonus+r)*n - sim-DQNACfZS.js
for(let[e,t]of this.items)gt(n,{mods:c.item(e).mods,times:t}) - sim-DQNACfZS.js
function xt(e,t){return t<=0?0:typeof e==`number`?e*t:e[Math.min(t,e.length)-1]??0} - dog-level-DtjeDJwC.js
case`speed`:return e._({id:`dsbK5U`,message:[`Projectile speed`]}) - deferred-ui-BYWEYMjM.js
case`growth`:return e._({id:`aPdxfP`,message:[`Extra XP from every gem.`]}) - sim-DQNACfZS.js
i[r.damageMult]=Math.max(.1,1+e.might) - sim-DQNACfZS.js
function Ns(e){return Math.sqrt(e.loadout.stats.areaMult)} - sim-DQNACfZS.js
n.speed=i.speed*(a===`orbit`?1+.5*(r.projSpeedMult-1):r.projSpeedMult) - sim-DQNACfZS.js
i[r.moveSpeedMult]=Math.max(.3,1+e.moveSpeed) - sim-DQNACfZS.js
(t.player.baseMaxHp+e.maxHp)*(1+e.maxHpPct) - sim-DQNACfZS.js
Math.min(1,e.loadout.stats.luck/t.luckForFourthChoice) - sim-DQNACfZS.js
i[r.critChance]=Ee(e.critChance,n.critK) - sim-DQNACfZS.js
i[r.critMult]=n.baseCritMult+e.critDamage - sim-DQNACfZS.js
i[r.evasion]=Ee(e.evasion,n.evasionK) - sim-DQNACfZS.js
d=o*e.loadout.stats.knockbackMult*(1-s)*70 - sim-DQNACfZS.js
i[r.pierceBonus]=Math.floor(e.pierce) - sim-DQNACfZS.js
n.hp[e.me]=.5*t.stats.maxHp,n.invuln[e.me]=2 - sim-DQNACfZS.js
i[r.eliteMult]=1+e.eliteDamage - sim-DQNACfZS.js
Y(e,20*n.damageMult,R(e,`boom-spud`,Zt(`boom-spud`)),-1,1),vi(e,a,o,22) - sim-DQNACfZS.js
function hi(e){return e.cfg.dash.charges+e.loadout.stats.dashCharges} - sim-DQNACfZS.js
function mi(e){return S(e.cfg.dash.cooldownSec*e.loadout.stats.dashCooldownMult)} - sim-DQNACfZS.js
t.dashShield[n]=e.cfg.dash.ticks+function(e){return Math.round(60*e.loadout.stats.dashGraceSec)}(e) - sim-DQNACfZS.js
e.chance=function(e,t){return 1-se(1-Math.min(1,Math.max(0,e)),t)}(r.chance,t),e.rate=r.perSec*t - sim-DQNACfZS.js
o.kind!==`onKill`||o.frozenOnly&&t.freezeT[n]<=0||!js(e,a) - sim-DQNACfZS.js
case`burn`:{if(yi(r,i,a*n.share,n.seconds*e.loadout.stats.durationMult,t.src),n.spread===void 0)return - sim-DQNACfZS.js
case`blast`:{let o=n.radius*Ns(e);Y(e,a*n.share,t.src,-1,.5) - sim-DQNACfZS.js
for(let r=0;r<n.jumps;r++){let r=Ar(e,l,u,s,o);if(r===void 0)break - sim-DQNACfZS.js
return e.itemFx.bonkSrc=o.src,s.proc.mult - sim-DQNACfZS.js
case`seekers`:(function(e,t,n,r,i,a){let o=e.projectiles - sim-DQNACfZS.js
case`shatter`:{let o=n.radius*Ns(e);if(Y(e,a*n.share,t.src,-1,.3) - sim-DQNACfZS.js
Hs(e,f,a,o,u+d/n.count*j,s),r.radius[f]=3*Ns(e),r.damage[f]=c - sim-DQNACfZS.js
case`slow`:e.slowF[t]=Math.max(e.slowF[t],n.factor),e.slowT[t]=Math.max(e.slowT[t],n.seconds*i) - sim-DQNACfZS.js
case`freeze`:r||(e.freezeT[t]=Math.max(e.freezeT[t],n.seconds*i)) - sim-DQNACfZS.js
case`burn`:yi(e,t,n.dps,n.seconds*i,a);return - sim-DQNACfZS.js
weaponSlots:6,passiveSlots:6 - sim-DQNACfZS.js
function Z(e){return e.tier===`base`?Math.min(8,e.levels.length+1):1} - sim-DQNACfZS.js
weight:Math.max(30,e.def.weight)*ia}) - sim-DQNACfZS.js
weight:a.weight*aa*i*(ua(t,a.id)?e.cfg.levelUp.unionPartnerWeight:1) - sim-DQNACfZS.js
weight:r.def.weight*ia*.8*la(e,r.def.id) - sim-DQNACfZS.js
var ia=3,aa=.35 - sim-DQNACfZS.js
let n=e.lootRng.weightedIndex(t.map(e=>e.weight)),i=t[n];if(i===void 0)break;r.push(i.offer),t.splice(n,1) - sim-DQNACfZS.js
if(e.def.tier!==`base`||e.level<n)continue;let r=c.evolutionOf(e.def.id)?.catalyst;if(r!==void 0&&t.passive(r)===void 0&&!t.banished.has(r))return r - sim-DQNACfZS.js
r.push({type:`gold`,name:`Sack of Gold`,desc:`+25 gold.`,amount:25}),r.push({type:`heal`,name:`Baked Potato`,desc:`Heal 30 HP.`,amount:30}) - sim-DQNACfZS.js
case`skip`:if(n.skips<=0)return;n.skips--,e.choiceQueue.shift();return;case`banish`: - sim-DQNACfZS.js
n.banishes--,n.banished.add(i.id) - levelup-lesson-run-B_SQzpEA.js
reroll redraws, skip passes, banish bans a card for the run. - sim-DQNACfZS.js
rerolls=0;skips=0;banishes=0 - sim-DQNACfZS.js
n=new Set(e.lootRng.shuffle([...t]).slice(0,e.cfg.levelUp.baseChoices)) - sim-DQNACfZS.js
function Ea(e,t){if(e.runClock>=e.cfg.chests.evolutionGateSec){let n=ka(e,t.pending);if(n!==void 0)return n}return Ma(e,t.goldScale)} - sim-DQNACfZS.js
evolutionGateSec:0 - sim-DQNACfZS.js
function Pa(e,t){let n=e.loadout,r=n.weapon(t.weapon);if(r!==void 0&&Na(r)&&n.passive(t.catalyst)!==void 0)return{w:r,recipe:t}} - sim-DQNACfZS.js
if(n!==void 0&&r!==void 0&&Fa(n)&&Fa(r))return Na(n)||Na(r)?[n,r]:void 0 - sim-DQNACfZS.js
Zi(e,i.slot),Qi(e,i.slot),n.weapons.splice(n.weapons.indexOf(i),1) - deferred-ui-BYWEYMjM.js
message:[`Unites with `,[`partner`],` into `,[`result`],` when both reach LV `,[`level`],`.`] - sim-DQNACfZS.js
return t===void 0?Aa(e,r):{type:`fork`,evolve:r.recipe,unite:t.recipe} - choices-7yGAe9Ru.js
message:[`Evolve or unite?`] - sim-DQNACfZS.js
n.def.tier!==`base`||n.level>=Z(n.def)||(t===void 0||n.level>t.level||n.level===t.level&&n.damageDealt>t.damageDealt)&&(t=n) - sim-DQNACfZS.js
let o=Math.round(20*n.stats.greedMult*t) - choices-7yGAe9Ru.js
message:[`Jackpot chest!`] - sim-DQNACfZS.js
if(r?.def.id===t.catalyst)return r.level>=r.def.maxLevel - sim-DQNACfZS.js
return n.items.delete(t.item),n.items.set(t.result,n.itemCount(t.result)+r) - deferred-ui-BYWEYMjM.js
message:[`Evolves into `,[`result`],` when `,[`catalyst`],` reaches LV `,[`level`],`.`] - sim-DQNACfZS.js
let n=ir.itemEvolutionOf(t);return n!==void 0&&e.itemCount(n.result)>0 - boot-DIJlLGwy.js
var Kc=`tibo.settings.v1```] [evolution-store-h8UcGvvT.js:key:tibo.discovery.v1,parse:f,merge:d] [evolution-store-h8UcGvvT.js:key:tibo.evolutions.v1,parse:a,merge:i`` - evolution-store-h8UcGvvT.js
c=[`weapon`,`passive`,`item`,`enemy`,`boss`,`biome`,`dog`] - run-records-Iz0UhFh9.js
case`levelUp`:case`whetstone`:for(let t of e.offers)this.offer(t);return; - run-records-Iz0UhFh9.js
e.tick-this.scannedAt>=30&&(this.scannedAt=e.tick,this.scan(e)) - encyclopedia-screen-RAUoo72F.js
if(!t.found)return e._({id:`vROlcq`,message:[`Not found yet`]}) - run-records-Iz0UhFh9.js
unlocked=e=>this.known.has(e) - recipe-view-DJMaj6Un.js ``case
itemEvolution:return n.unlocked(i.result)?r(item,e.item(i.result)):`???``` - boot-DIJlLGwy.js
return e.storyDone?{picks:sa,selected:(n=e.lastPick,(n===fa?void 0:n)??t.afterStory),featured:void 0}:{picks:la,selected:fa,featured:fa} - boot-DIJlLGwy.js
runPicker:{afterStory:`easy`,storyDoneOn:`ending`,storyOnStats:!0} - stats-panel-Bw-pgRkU.js
function T(e){return e.classList.add(`stats-medal`),e} - sim-DQNACfZS.js
u.charge=lc(u.charge,h&&u.armed&&_s(e,l),i),u.charge<nc(e.cfg,l.kind)||(u.charge=0,u.armed=!1,ys(e,l)) - sim-DQNACfZS.js
function lc(e,t,n){return t?e+1:Math.max(0,e-n)} - sim-DQNACfZS.js
activation:{seconds:{item:.75,chest:1,crops:1,taterLantern:1,taterMagnet:1,dog:1,curseShrine:1.5 - sim-DQNACfZS.js
r.gold-=i,e.run.goldSpent+=i,e.paidChestsBought++,e.run.paidChests++,n.used=!0;let a=Ri(e) - sim-DQNACfZS.js
value:(i=n.stat,a=n.base*e.cfg.economy.rarityMult[r],i===`armor`?Math.round(a):a),rarity:r - sim-DQNACfZS.js
function Ui(e){return e.lootRng.chance(e.cfg.goldenShrine.legendaryChance)?4:3} - sim-DQNACfZS.js
gold:Math.round((45+e.runClock/60*9)*e.loadout.stats.greedMult),curse:.1 - sim-DQNACfZS.js
n.shrineTotals.curse+=t.curse,n.cursesTaken++ - sim-DQNACfZS.js
let c=Math.round(e.cfg.economy.vendorPrice[s.rarity]*(1+.06*n)*e.loadout.stats.chestPriceMult) - sim-DQNACfZS.js
purchasesPerVendor:1 - sim-DQNACfZS.js
i.id!==e.id&&i.rarity===e.rarity&&(r===void 0||t.itemCount(i.id)<t.itemCount(r.id))&&(r=i) - choices-7yGAe9Ru.js
Copy an item. Another of its rarity melts. - sim-DQNACfZS.js
e.lootRng.chance(.35)&&J(e,`heal`,t.x,t.y-8,30) - sim-DQNACfZS.js
tierBonus:Math.floor(t.tier/n.nestTierEvery),hpMult:1+t.tier*n.nestHpPerTier - sim-DQNACfZS.js
n[Math.max(1,Math.min(n.length-2,Math.floor(e.runClock/120)+Math.floor(t.tier/2)))] - sim-DQNACfZS.js
case`botDock`:yo(e)>0&&(t.state.used=!0);return - sim-DQNACfZS.js
case`whetstone`:n.used=!0,e.choiceQueue.push({kind:`whetstone`,offers:pa(e)}) - sim-DQNACfZS.js
r.xp[a]=(r.xp[a]??0)*n.xpMult,r.goldChance[a]=1,r.despawnAt[a]=e.time+n.lifeSec - sim-DQNACfZS.js
let r=So(n.reels,e.cfg.taterSlots.symbols);if(r!==3){if(r===2){let t=Ri(e) - sim-DQNACfZS.js
r.rerolls+=n.rerolls,r.skips+=n.skips,r.banishes+=n.banishes - sim-DQNACfZS.js
case`taterMagnet`:(function(e,t){let n=gs(e) - sim-DQNACfZS.js
let s=e.me,c=(2*r.digTicks+r.riseTicks)/60;e.player.invuln[s]=Math.max(e.player.invuln[s]??0,c) - sim-DQNACfZS.js
tokens:[{kind:`skip`,weight:4},{kind:`magnet`,weight:4},{kind:`reroll`,weight:3},{kind:`banish`,weight:2},{kind:`beacon`,weight:1}] - sim-DQNACfZS.js
effect:{kind:`slow`,factor:n.bossSlow,seconds:n.freezeSec} - sim-DQNACfZS.js
function Xa(e){return Ya(e)?e.cfg.frenzyFryer.fireRateMult:1} - sim-DQNACfZS.js
return Ya(e)?e.cfg.frenzyFryer.moveSpeedMult:1 - sim-DQNACfZS.js
a!==void 0&&To(e,c.event(a))&&e.world.spendLandmark(r) - sim-DQNACfZS.js
f=Math.round(t.count*Math.min(2,Br(e.scale,e.cfg,e.runClock).count)) - sim-DQNACfZS.js
o=Math.max(1,Math.round(t.count*e.cfg.level.wallPests)) - sim-DQNACfZS.js
Ks[n.def[t]]===1&&li(e,`item`,i,a,1,`lootRunner`) - sim-DQNACfZS.js
e.goldRushUntil=Math.max(e.goldRushUntil,e.time+t.duration),e.goldRushMult=t.goldMult - sim-DQNACfZS.js
e.runClock<e.nextGrokBotAt||(e.nextGrokBotAt=e.runClock+n.everySec,l<n.maxOut&& - sim-DQNACfZS.js
Y(e,u?c.maxHp[l]*r:0,R(e,so,a),-1,1),e.attack.flags=u?0:ge.Kill - sim-DQNACfZS.js
J(e,`xp`,i,a-6,e.loadout.xpNext*t.help.levelFraction),r.chance(t.help.beaconChance)&&J(e,`beacon`,i,a+6,1) - sim-DQNACfZS.js
n.invuln[e.me]=Math.max(n.invuln[e.me]??0,t.help.seconds),Si(e,e.loadout.stats.maxHp*t.help.healFraction) - sim-DQNACfZS.js
s*s+c*c<=a&&(n.vacuum[t]=1)}q(e,`pickup`,r,i,40,t.paint) - sim-DQNACfZS.js
let s=o/t.swarmSize*j,c=vo(e,{x:r+C(s)*t.swarmRadius*3,y:i+A(s)*t.swarmRadius*3,help:ho - boot-DIJlLGwy.js
function xn(e,t){let n=e.next()<t.visitChance,r=e.range(t.windowSec[0],t.windowSec[1]);return n?Math.round(60*r):-1} - sim-DQNACfZS.js
e.player.stunTicks[e.me]=Math.round(60*e.cfg.poteto.stunSec) - sim-DQNACfZS.js
r.gift?function(e,t){ - boot-DIJlLGwy.js
function Sn(e,t){return e.chance(t.giftChance)} - sim-DQNACfZS.js
var Yo=[[`catalyst`,function(e,t){ - sim-DQNACfZS.js
i=r.newWeapons.find(e=>n.weapon(e)===void 0&&!n.retired.has(e)&&!n.banished.has(e)) - sim-DQNACfZS.js
let t=Math.round(e.cfg.poteto.giftGold*e.loadout.stats.greedMult) - sim-DQNACfZS.js
n.def[t]===Os&&fe(e.alex)===`pending```] [boot-DIJlLGwy.js:function st(e){return e.revealedTick===-1?pending:e.due?joined:asleep}`` - sim-DQNACfZS.js
Y(e,n.burstDamage*e.loadout.stats.damageMult,r,-1,n.burstKnockback),vi(e,t.headX,t.headY,n.lureRadius*n.burstRadiusShare) - sim-DQNACfZS.js
o===D.magnet?gs(e):o===D.reroll?r.rerolls++:o===D.skip?r.skips++:o===D.banish?r.banishes++:o===D.beacon&&yo(e) - sim-DQNACfZS.js
let r=e.lootRng.next(),i=.004;r<i?J(e,`heal`,t,n,30):(i+=.0012,r<i?J(e,`magnet`,t,n,1) - sim-DQNACfZS.js
r.chance(.5)&&J(e,`reroll`,i+8,a,1),r.chance(.25)&&J(e,`banish`,i-8,a,1),void(r.chance(.1)&&J(e,`beacon`,i,a+8,1)) - sim-DQNACfZS.js
J(e,`xp`,i,a,o.xp*s.xpMult),J(e,`gold`,i+6,a,25*s.goldMult) - sim-DQNACfZS.js
!0===n.elite&&cs(e),n.boss===void 0|| - boot-DIJlLGwy.js
je={rows:Ae,afterLast:`postWin`} - boot-DIJlLGwy.js
{minute:20,minEnemies:480,maxEnemies:500,interval:.14,batch:14,tiers:[0,0,0,0,1],boss:`harvester`,events:[`fly-swarm`]} - sim-DQNACfZS.js
r=.5*Math.min(e.field.width,e.field.height)+20 - sim-DQNACfZS.js
let n=e[0].hpScale;for(let r of e)r.minute<=t&&(n=r.hpScale);return n}(e.cfg.bosses.hpSteps,e.runClock/60)*e.cfg.level.bossHp - sim-DQNACfZS.js
a.radius[n]=1.7*(a.radius[n]??0),a.damage[n]=(a.damage[n]??0)*r.damage - sim-DQNACfZS.js
a.xp[n]=12*(a.xp[n]??0),a.goldChance[n]=1 - sim-DQNACfZS.js
let t=e.cfg.win.mode;switch(t){case`continue`:return; - boot-DIJlLGwy.js
message:[`You survived `,[`minutes`],`:00!`] - sim-DQNACfZS.js
n.set(t,x.Invincible),n.speed[t]=1.6*(n.speed[t]??0),n.damage[t]=10*(n.damage[t]??0) - choices-7yGAe9Ru.js
message:[`Keep going`]}),desc:e._({id:`tnJm5o`,message:[`Enemies grow stronger every minute`] - sim-DQNACfZS.js
case`postWin`:(function(e,t){cs(e);let n=t-e.waves.rows.length; - boot-DIJlLGwy.js
Me=[`fryer-king`,`the-mandoline`,`the-masher`,`grater-golem`,`compost-titan`,`ketchup-kraken`,`harvester`],Ne=[`fly-swarm`,`bale-ring`] - boot-DIJlLGwy.js
Pe={graceSec:30,graceDensity:.8,rows:[{minEnemies:34,maxEnemies:35 - sim-DQNACfZS.js
function ps(e,t){return fs(e,Math.max(0,t-e.cfg.level.tierLag))} - boot-DIJlLGwy.js
var Ur={KeyW:[0,-1],ArrowUp:[0,-1],KeyS:[0,1],ArrowDown:[0,1],KeyA:[-1,0],ArrowLeft:[-1,0],KeyD:[1,0],ArrowRight:[1,0]} - boot-DIJlLGwy.js
keys:{reroll:`KeyR`,skip:`KeyX`,banish:`KeyB`,back:`Escape`,playAgain:`KeyR`,share:`KeyS`,dash:`Space`,autoAdvance:`KeyT`,skipLines:`Space`} - main.js
playKey(e){e!==`KeyP`&&e!==`Escape`||!this.canPause()||this.setPause(this.pause===`none`?`player`:`none`) - main.js
if(e===`KeyM`)this.controls.toggleSound() - choices-7yGAe9Ru.js
let s=re(this.hotkeys.length+1) - boot-DIJlLGwy.js
function qr(e){return`Digit${e}`} - sim-DQNACfZS.js
dash:{distance:48,ticks:9,cooldownSec:5,charges:1,bufferTicks:6} - sim-DQNACfZS.js
function pi(e){return e.player.dashShield[e.me]>0} - boot-DIJlLGwy.js
flickPx:22,flickMs:260 - boot-DIJlLGwy.js
message:[`Flick to dash`] - boot-DIJlLGwy.js
e.addEventListener(`gamepadconnected`,()=>this.seen.add(`pad`),t) - boot-DIJlLGwy.js
var zc={effectsOpacity:Rc(1),shake:`full`,flashes:!0,damageNumbers:!0,hitbox:!1,recommendedPicks:!0 - settings-panel-C5ucFufB.js
{tab:`comfort`,name:e._({id:`jMucll`,message:[`Comfort`]}) - settings-panel-C5ucFufB.js
M({key:`hitbox`,label:e._({id:`PSN6Nm`,message:[`Hitbox`]}),help:e._({id:`dfnSLw`,message:[`Shows where shots hit Tibo.`]}) - settings-panel-C5ucFufB.js
M({key:`picks`,label:e._({id:`MmtEzI`,message:[`Recommended picks`]}),value:n.recommendedPicks - picks-D6O4eKt0.js
return!r.isNew&&n?20:r.id===`reset-button`?9.5: - picks-D6O4eKt0.js
case`curseShrine`:return{type:`close`} - settings-panel-C5ucFufB.js
Anonymous gameplay stats help poteto improve the game. No name, account or IP address is recorded. - boot-DIJlLGwy.js
caption:h._({id:`IyKM4g`,message:[`A short first game. Play this first.`]}) - boot-DIJlLGwy.js
caption:h._({id:`_c47F1`,message:[`Crowds, a curse and no revives.`]}) - boot-DIJlLGwy.js
function ua(e){return e===`story`?{mode:`story`,difficulty:ca}:{mode:`standard`,difficulty:e}} - sim-DQNACfZS.js
Ne={easy:{...Me,alive:.55,pestHp:.5,damage:.4,bossHp:.5,eliteHp:.5,maxHp:2,xp:1.5,addedRevives:2,tierLag:3,wallPests:.15,overtimeHoldSec:60 - sim-DQNACfZS.js
nightmare:{...Me,alive:1.36,floorShare:.85,earlyRamp:1,earlyRampTime:.2 - sim-DQNACfZS.js
opening:`classic`,pestHp:1.75,damage:1.3,bossHp:1.25,eliteHp:2,xp:1.5,allowRevives:!1,startCurses:1 - sim-DQNACfZS.js
n.shrineTotals.curse+=.1*t,n.cursesTaken+=t,B(e) - sim-DQNACfZS.js
contents:{weapons:[{id:`peeler`,level:4},{id:`tot-toss`,level:3}],passives:[{id:`thick-skin`,level:2}]} - sim-DQNACfZS.js
story:{firstRushHp:3,secondRushHp:.3,secondRushXp:0,bossHp:2.2,bossDamage:1.6,bossSpeed:2.4 - sim-DQNACfZS.js
firstKillLevel:2,firstHitKills:!0 - sim-DQNACfZS.js
secondRush:{peakTicks:900,alive:{from:220,to:400},everySec:{from:.2,to:.06},batch:3,reach:200} - sim-DQNACfZS.js
return t===`firstRush`||t===`secondRush`&&function(e){let t=e.visitor - sim-DQNACfZS.js
function ft(e,t){return t+e.boostGrowth*t*(t-1)/2}function pt(e,t){return ft(e,t)*e.boostMight} - sim-DQNACfZS.js
ending:[{kind:`visit`,afterJoinTicks:900} - sim-DQNACfZS.js
onlyPois:[`crops`,`taterMagnet`,`spudFreezer`,`frenzyFryer`,`botDock`,`taterLantern`,`seedVault`] - death-flow-C6ztfY2s.js
message:[`Now play the real game.`] - run-address-BpTkpKsy.js
function Ye(e,t){let n=new URL(`/r/`,e) - run-address-BpTkpKsy.js
if(t.length>8192)return{kind:`invalid`,fault:`too-long`} - shared-run-2uiYdqA3.js
message:[`Shared run`] - death-flow-C6ztfY2s.js
message:[`Share on X`] - sim-DQNACfZS.js
let n=this.cfg.world.chunkSize,r=this.cfg.dogs.firstDogDistance,i=C(.6)*r,a=A(.6)*r - sim-DQNACfZS.js
let n=t.kind===`dog`?function(e){let t=e.weighted(c.dogs,e=>e.weight);return t===void 0?`retriever`:t.id}(t.rng):null - run-extras-CdCBynDO.js
let t=I.filter(t=>new l(a(`${e.seed}:legend:${t.id}`)).chance(e.chance)).map(e=>e.id) - sim-DQNACfZS.js
rollLegend(e){if(this.legendary&&!(e<1200))return this.legendQueue.shift()} - boot-DIJlLGwy.js
ue=[{parts:[`pomeranian`,`pomchi`],into:`zelda-and-link`}] - sim-DQNACfZS.js
if(i>=0)return mr(e,i),`leveled`;if(n.pool.count>=e.cfg.dogs.maxPack)return`full```] [choices-7yGAe9Ru.js:message:[Keep my pack - sim-DQNACfZS.js
function ct(e,t){let n=e.levelUps,r=1;for(let e=0;e<t-1;e++)r*=n[e]?.power??1;return r} - sim-DQNACfZS.js
function ut(e,t){let n=e.levelUps,r=1;for(let e=0;e<t-1;e++)r+=n[e]?.aura??0;return r} - picks-D6O4eKt0.js
let o=t.cfg.dogs.kennelPrices[i-1];return o===void 0?-1:Math.round(o*t.loadout.stats.chestPriceMult) - choices-7yGAe9Ru.js
Train up to `,`#`,` more levels here, then the kennel closes - sim-DQNACfZS.js
e.pool.isAlive(r)&>(n,{mods:i.aura,times:ut(i,e.level[r]??1)}) - sim-DQNACfZS.js
Y(e,l.damage*o*e.loadout.stats.damageMult,s,7,1.5) - sim-DQNACfZS.js
e.lootRng.chance(n.bonusChance)&&(J(e,e.lootRng.chance(.7)?`heal`:`reroll`,a,o-6,30) - sim-DQNACfZS.js
a.has(s,x.Hidden)||(a.freezeT[s]=Math.max(a.freezeT[s],r) - sim-DQNACfZS.js
if(!r&&c!==null&&e.legend.fluff>0)return c.sneeze(e,t*(1-Rr(s.armor,i,e.cfg))),!1 - run-extras-CdCBynDO.js
return n.marks[Math.min(r,n.marks.length)-1]??1