Formulas
Every equation in the game, exactly as written in the code, with the path of the file that implements it. The constants come from the balancing catalogue and are regenerated on every change: if a number moves in the game, it moves here, or the test suite refuses to pass.
What this page guarantees
A theorycrafting page is worth only its reliability. Here is exactly what is guaranteed, and by what means.
- The numbers are not transcribed. They are extracted from the server’s balancing catalogue by a script, and a test compares the extract against the source. A rebalance that is not propagated fails the test suite — the docs break the build instead of lying.
- Every formula cites its file. You do not have to take our word: the path under each equation is that of the module that actually computes it.
- The calculators run the game’s code. They import the same pure functions the server does, not a reimplementation.
The catch
One thing is not published here and will not be: the anti-cheat detection thresholds. Publishing a safeguard means publishing the way around it. Everything else about balancing is on this page.
Economy
Total = (1+ΣDistrict) × (1+ΣBâtiment) × (1+ΣEncadrement) × (1+ΣCarte) × (1+ΣTemporaires) × (1+ΣPermanents)The game’s central rule. Bonuses within a category add up, categories multiply. That is why a sixth building bonus is worth less than a first management bonus: opening an empty category always beats padding a full one.
Implemented in src/domain/multipliers.ts
Gain = (base + perCharme × Charme_eff) × T_client × Multiplicateur_totalThe gain from ONE client. `T_client` is the drawn client type’s multiplier (from a Standard at ×1 up to a Celebrity at ×8). Effective Charme enters linearly: doubling Charme does not double the gain, it only doubles the `perCharme × Charme_eff` term.
Implemented in src/domain/clients.ts
Gain_tick = ⌊ Gain × M_bonheur × M_nuit × M_épuisée × M_buff ⌋ + boisson + contrebandeThe corrections applied at tick time, after the base gain. `M_night` is 1+night bonus between 22:00 and 06:00 UTC, `M_exhausted` removes 25 % below the Endurance threshold, and `M_happiness` follows the mood tiers — dropping to 0 below 10 Happiness: an unhappy girl earns nothing at all.
Implemented in src/domain/kernel.ts
N_clients ~ Poisson(λ) ; λ = λ_lieu × (1 + freq) × durée_du_tick_en_minutesThe client count per tick is a Poisson draw, not a fixed average: two identical ticks do not yield the same number of clients. `λ_place` is `streetBase` on the street, and `buildingBase + buildingPerLevel × level` in a building — the real reason levelling a building pays off, even with no gain bonus.
Implemented in src/domain/clients.ts
Taux_affiché = espérance(clients/s) × espérance(gain/client)The rate the interface shows is an expectation, not a promise: over any given minute, Poisson decides. This function must mirror the kernel exactly — its comment documents three past divergences, each paid for in free Cash. Read this one, never a reconstruction.
Implemented in src/server/game/service.ts
Boisson = p × cashPerBarLevel × niveau_du_bar_du_district × (1 + bonus_encadrement)The side sale, added to every client. It depends on the level of the district’s Bar, not on where the girl works: a high-level Bar waters every spot in the neighbourhood. Probability `p` doubles on Party Street.
Implemented in src/server/game/service.ts
Salaire_cycle = Σ salaire(rareté) × (si Diva : divaMultiplier) × (1 + bonus_de_salaire)Charged every payroll cycle. Wages follow rarity, not level: a level-1 Platinum already costs twenty Bronzes. Two unpaid cycles trigger a strike, three a resignation.
Implemented in src/domain/payroll.ts
p_démission = max(0,05 ; (30 − Bonheur) / 100)On the third unpaid cycle, every girl rolls for resignation. The 5 % floor applies even to a perfectly happy girl: debt always ends up costing a card. At 0 Happiness, it is 30 % per cycle.
Implemented in src/domain/payroll.ts
Cards & progression
Stat_eff = ⌊ Base_lignée × M_forme × (1 + 0,05 × (niveau − 1)) × (1 + 0,02 × éveil) × M_shiny ⌋ + points_entraînésThe single formula behind every stat in the game. `M_shiny` is 1.10 on a Shiny form, 1 otherwise. Training points are added after multiplication: they benefit from neither level, nor form, nor awakening, nor Shiny. Training a card early that you plan to evolve is a bad trade.
Implemented in src/domain/stats.ts
XP(n) = ⌊ 100 × n^1,45 ⌋XP needed to go from level n to the next. The exponent was softened from 1.6 to 1.45 on 14 Aug: the 49 → 50 step fell from about 52,300 to 29,000 XP, because the endgame had turned into a wall.
Implemented in src/domain/kernel.ts
XP_gagnée = (2 × T_client + bonus_xp_du_client) × multiplicateur_xp_de_la_carteXP follows the client type, not the Cash gain. A Tourist (×1.2, +1 XP) therefore levels better than a Big Spender (×2, +1 XP) relative to what it pays. Each level gained also grants +5 Happiness.
Implemented in src/domain/kernel.ts
Coût(niveau → niveau+1) = round( baseCost × growth^niveau × mult_rareté × mult_ville )The second path: paying for the level. The step grows at every level, so pushing a card to the cap costs several times what it earns meanwhile. That is deliberate — it is the game’s uncapped Cash sink, a targeted boost and never the normal way to progress.
Implemented in src/domain/leveling.ts
Coût_évo = base × mult_objet(rareté) pour les objets, base × mult_cash(rareté) pour le CashTwo steps: Stage II at level 15 (Gold Chains), Stage III at level 30 (Diamond Rings). The scale follows rarity on two different ladders — items at 2/3/4/5, Cash at 1/2/5/12. Evolving a Platinum costs twelve times a Bronze’s Cash, but only two and a half times its items.
Implemented in src/domain/evolution.ts
Bonus_éveil = +2 % par palier, multiplicatif dans Stat_eff ; 5 paliers au maximumA duplicate is never wasted: it awakens the card you already own. The duplicate IS the price — no Stars are charged. The game automatically picks the best copy as the target (rarity, then awakening, then stage, then level).
Implemented in src/domain/gacha.ts
XP_Boss(niveau) = round( 100 × niveau^1,5 )Your own level. It no longer unlocks anything since unlocks moved to Cash: it only drives Mentor messages and the leaderboard. Its XP comes from actions (building, evolving, winning a contract), not from time spent.
Implemented in src/domain/progression.ts
Work, rest & heat
Perte_end = (enduranceLossBase + T_client + extra_client) × (1 − réduction) × (rue : ×1,25)Big clients tire more: the client’s multiplier enters the loss directly. Reductions add up and are then capped at 60 % — the only cap there is, and what prevents infinite endurance.
Implemented in src/domain/kernel.ts
Perte_bonheur = bonheurLossPerClient × (1 − réduction), par client serviHappiness drops per client, not per minute: a busy spot wears mood down far faster than a quiet one over the same period. That is the real hidden cost of good locations.
Implemented in src/domain/kernel.ts
Bonheur −= (rue sans surveillance) + (endurance à 0) + (sacoche pleine au-delà du délai de grâce) + (laissée au vivier)Three passive erosions hit a girl at work, calibrated together to take 100 → 0 in about 48 h each taken alone. They stack: a girl on the street, exhausted and with a full satchel, collapses in one night. The fourth is on a wholly different scale: a girl left idle in the pool withers by about 3 points a day — a month to become stealable, which is how long it takes to truly forget her.
Implemented in src/domain/kernel.ts
Endurance = clamp( Endurance − postEnduranceLossPerMin × minutes_postées ; 0 ; max )A steward’s wear is computed in closed form, not tick by tick: it stays exact even after a long absence. At 0 she keeps her post but all her effects drop — her perimeter simply stops turning, with no message at all.
Implemented in src/domain/kernel.ts
Regain/min = taux_du_lit × (1 + 0,1 × (niveau_bâtiment − 1)) × (1 + bonus_Confort)A rest ends when Endurance is full — that is what a bed is for first of all. Every bed has its own rate, improved 10 % per building level. Happiness stays a top-up: none at HQ, one to four points per full rest in paid beds depending on tier and level — never enough to offset a shift, which costs close to five. The best beds also grant a buff, whose timer only starts on redeployment.
Implemented in src/server/game/service.ts
CALME < 50 ≤ SURVEILLANCE < 80 ≤ ALERTE < 100 = DESCENTE ; pénalité de gains dès SURVEILLANCEThe gauge never falls on its own: every client served feeds it, and only the Police page, the raid itself or an item resets it. The gain penalty applies all or nothing from 50: going from 49 to 50 instantly costs 20 % of income, and letting it drift beyond costs no more in gains — but brings the raid closer. At 80 it is announced; at 100 it lands, whether you are online or not.
Implemented in src/domain/heat.ts
Heat/min = Σ(clients servis × tarif du lieu) × (1 − min(plafond ; S / (S + 4 × postes)))Heat is the shadow of your output: it rises with clients served, faster in exposed venues (the street first, the Private Mansion last). Lookouts do not lower it — they slow its rise, and the cap guarantees they never cancel it. A district working more girls needs more composure for the same reprieve.
Implemented in src/domain/heat.ts
p_arrestation = plancher + (base − plancher) × arrestRefScore / (S + arrestRefScore)The district’s Surveillance lowers arrest risk, but never below the floor: no wall of composure makes a raid harmless. The curve is continuous — every point of S still counts at the very top of the scale. The Long Arm ability can additionally cancel the whole raid, capped at 45 %.
Implemented in src/domain/heat.ts
S = Sangfroid_eff × (1 + ΣŒilDeLynx) × (1 + InstinctDeMeute) × (1 + RegardDAcier) + points_SirèneThe score that protects a district. Neighbouring districts count for only 30 %: a lookout truly covers only where she is posted. Siren points are flat and added after the percentages.
Implemented in src/domain/heat.ts
Recruitment
P(carte de rareté r) = cardChance(palier) × rates(palier)[r]⚠️ A tier’s rates are conditional: they only apply once it is decided that a card drops at all. At tier 6, a Platinum does not appear 2 times in 100 runs but 2 % × 60 % ≈ 1.2 %. This is the table that matters — the base rarity table is vestigial.
Implemented in src/domain/find-girls-run.ts
si compteur_Platinum + 1 ≥ seuil → PLATINUM ; sinon si compteur_Gold + 1 ≥ seuil et rareté < GOLD → GOLDThe guarantee is checked before the roll for Platinum, and after for Gold: a naturally drawn Platinum therefore does not consume the Gold guarantee. Both counters are persistent and displayed.
Implemented in src/domain/gacha.ts
EV(continuer) = P(réussite) × valeur(palier suivant) − valeur(palier acquis)The table’s real decision: push on or cash out. The gift pool’s value is bounded by config validation (it must stay under ~2.4 Star-equivalents), otherwise pushing on would become EV-negative and the mode would sabotage itself.
Implemented in src/domain/find-girls-run.ts
Game modes
Dégâts = ⌊ Stat × facteur_couleur × longueur × (1 + 0,1 × (longueur − 1)) × (1 + 0,25 × index_tracé) × élémentaire ⌋Length counts twice (linearly, then as a bonus): a 6-chain does far more than double a 3-chain. And the trace index rewards chaining within a turn — the fourth trace hits at +75 %.
Implemented in src/domain/duel.ts
facteur_couleur : charme ×1 · ruse ×2,2 · sangfroid ×2,45 · tchatche ×2,6These factors are not arbitrary: they are the inverse of the squad-power ratio measured in game (charme 1002 · ruse 462 · sangfroid 408 · tchatche 381). Without them, Charme — already king of the economy — would have won the duel too. This is an anti-cannibalisation correction, not a bonus.
Implemented in src/domain/duel.ts
PV_max = round( pReq × hpTurnFactor × tours_cible )A House’s HP is derived from the power it demands and the intended turn count, never hand-set. Two invariants are checked by config validation: the House must be winnable (its attacks over the target turns stay under 100 % of your HP) and bounded (enrage climbs 20 %/turn, uncapped).
Implemented in src/domain/duel.ts
Dégâts_ennemi = round( dégâts_de_base × (1 + stepPct × max(0 ; tour − afterTurn)) )Past its patience turn, the opponent ramps up without ceiling: stalling always loses. Its attacks, however, follow a scripted rotation (turn − 1) modulo the attack count — no randomness, which is why the game can telegraph them.
Implemented in src/domain/duel.ts
Cash = round( cash_base × (première victoire : ×2) × (rejoué hors Passe du soir : ×0,5) )Stars drop on the first win only. The 50 % replay share exists because at the reference scale — one rung is worth about 6× working — an unbridled repeatable mode would replace the economy instead of complementing it.
Implemented in src/domain/duel.ts
XP = round( ⌊ XP_requise(niveau) × fraction_du_palier ⌋ × (1 + bonus_perso) × (1 + bonus_Stratège) )The reward is a fraction of the current level, so Coaching stays useful at every level instead of becoming negligible. Tier 3 grants half a level in one go.
Implemented in src/domain/zip.ts
XP/h = max( plancher ; XP_requise(niveau) × niveaux_par_jour / 24 ) × bonus, bonus plafonnéThe rate is recomputed at every level gained, so time per level stays constant: the Academy grants a cadence, not a quantity. The bonus cap is the only capping point — without it, the five stacked sources reached ×5.24, about ten levels a day.
Implemented in src/domain/training.ts
Puissance = Σ stat_dominante_de_chaque_membre × (1 + Σ % du domaine / 100)Only the stat matching the contract’s domain counts: a Charme squad is worthless on a Ruse contract. The domain’s ability percentages add up first, then multiply the sum.
Implemented in src/domain/contracts.ts
CRITIQUE ≥ 1,5 × pReq · RÉUSSITE ≥ pReq · PARTIEL ≥ 0,8 × pReq · sinon ÉCHECRewards ×1.5 / ×1 / ×0.5 / ×0, and Happiness +5 / 0 / 0 / −10. Aiming for a critical at 150 % therefore beats chaining two bare successes — but a failure costs Happiness on top of the time tied up.
Implemented in src/domain/contracts.ts
pReq = max( 1 ; round( pReq_du_palier × facteur_du_domaine ) )The exact mirror of the duel’s colour factor, in the other direction: Charme demands 100 % of the listed power, other domains 40–45 %. Without this correction, non-Charme contracts would have been impossible.
Implemented in src/domain/contracts.ts
Cible = round( (base + perBoss × Boss) × croissance^Boss )Missions track your Boss level, affine then geometric. Volume missions use a gentler growth, because the number of clients served saturates (around 8,200/day) where income keeps climbing.
Implemented in src/domain/missions.ts
I = Σ Ruse_eff × (1 + ΣPassePartout + BeauParleur) × (1 + SangDeGlace) × (1 + bonus_archétype) + points_platsYour infiltration squad’s score. Against it, the defender’s stealth is `I × (1 + reduction)` — the code applies (1 + r) where the design doc wrote (1 − r); the divergence is deliberate and documented.
Implemented in src/domain/poach.ts
p_vol = clamp( 0,20 + 0,012 × (25 − Bonheur) + 0,0006 × (I − S) ; 0,05 ; 0,90 )Despite its name, `p_vol` is no longer a probability: a theft is played, not rolled. It does two things — at the floor (0.05) the target is out of reach and the attack is refused with no charge; above it, it becomes a handicap on your squad (×0.80 to ×1.25) for the deciding round. The target’s Happiness weighs twenty times more than the score gap: keeping your girls happy is still the first PvP defence — no longer the last one, since you can now defend by playing.
Implemented in src/domain/poach.ts
vol = score_attaquant > score_défenseur, sur 5 tableaux × 3 tracésThe decision. Both sides play the same boards (same seed) with 3 girls each, on the Midnight Pass engine; the score is total damage. The defender has 24 h: not answering is a forfeit, and a forfeit costs the card. On a tie the defender keeps her. All six committed girls leave for 24 h on Operation — unstealable, but unproductive.
Implemented in src/server/game/poach-duel.ts
p_détection = clamp( 0,10 + 0,0035 × (S − D) ; 0,05 ; 0,95 )The defender’s surveillance against your stealth. It is now the only thing detection decides: the defender is always told someone is coming for one of his girls — otherwise he could not defend her — but he only learns your name (and your squad’s) if he spotted you. A detection also locks that defender for 48 h.
Implemented in src/domain/poach.ts
Prime = max( base × rareté × ville ; Cash_total_de_montée(1 → niveau) × part ) × (1 − meilleure_réduction)A theft’s price is the maximum of two scales: the rarity flat rate, and a share of what the card actually cost to level. Stealing a heavily levelled card costs what it is worth, not what its rarity suggests.
Implemented in src/domain/poach.ts
City & buildings
C(n) = C(1) × upgradeFactor^(n−1)The factor was raised from 2.2 to 2.4: taking a building to level 5 went from 42.1 to 56.2 times its initial price. The slots gained per level do not follow that curve — hence the trade-off between levelling a building and opening another.
Implemented in src/server/config/catalog.ts
Coût_emplacement = coût_de_base × multiplicateur_du_district × multiplicateur_de_la_villeThe two multipliers compose: a building slot Downtown in Tokyo costs 2.5 × 27 = 67.5 times its Suburbs price in Los Angeles. That is what makes each new city genuinely hard, not merely slower.
Implemented in src/server/services/config.ts
Meta progression
Disques = max( 0 ; ⌊ √(Cash_total_gagné / cashPerDo) × mult_ville ⌋ − déjà_gagnés )The square root: quadrupling your earnings only doubles your Records. The calculation uses total Cash ever earned, never your balance — hoarding before a Prestige gains you nothing.
Implemented in src/domain/prestige.ts
Cap_effectif = min( capMax ; cap_de_base + levelCapPerRank × rang_Confort )The Comfort branch is the only way past the starting level cap, and its ten ranks lead exactly there: each adds five, a full branch is worth the absolute cap. Ranks grow steadily more expensive, from the first Record to the last.
Implemented in src/domain/prestige.ts
Points = Cash_de_la_fenêtre / cashPerPoint + victoires_PvP + contrats_haut_palier + galasThe season score reads over a rolling window, not from the beginning: a player returning after a break is not doomed. Cash remains the main source, but PvP wins and galas are each worth hundreds of thousands of Cash.
Implemented in src/domain/seasons.ts
Bonus = Σ lignées_complètes × bonus_lignée + Σ raretés_complètes × bonus_raretéA permanent gain bonus, hence a whole category of the multiplier stack. A lineage is only “complete” when all its final forms are owned — which the lineage’s Shadow-domain count determines.
Implemented in src/domain/album.ts
Invariants held by CI
Some properties are not balancing choices: they are constraints that config validation refuses to let pass. They are checked on every change, and are therefore worth more than an intention.
| Invariant | What it guarantees the player |
|---|---|
| Rarity rates sum to exactly 1, at every tier. | No hidden residual probability: the published tables are complete. |
| Client probabilities sum to 1, and the average gain per client stays in a narrow band. | An exotic encounter table cannot accidentally double your income. |
| The recruitment tables’ target success rates (6 tiers) are measured, with a ±8-point tolerance. | A tier’s stated difficulty is verified, not eyeballed. |
| Every duel House is winnable: its attacks over its 4 target turns stay under your hit points. | No House is mathematically unbeatable at the required power. |
| Every duel House is bounded: rage climbs uncapped past its patience turn. | No duel can be won by stalling indefinitely. |
| The recruitment gift pool’s value stays under a threshold. | Climbing a tier stays EV-positive: the risk is variance, not the payout scale. |
Calculators
Calculators
These run the game’s own functions, not a copy: what they show is what the server will compute.
XP for a level climb
The starting level cap is 50; the Prestige Comfort branch can push it to 100.
Cash for a paid climb
Where the code diverges from the design doc
The game was built from a design document, then corrected in practice. When the two disagree, the code is authoritative — and the code is what is documented here. Three divergences are worth knowing, because they still circulate in their old form.
- The live draw rates are those of the recruitment tiers, not the old “standard/premium” rarity table, which is now vestigial. It is the most commonly misquoted number.
- Steward rotations happen every 15 minutes, aligned with the server sweep — not every 30 minutes as is sometimes stated.
- The defender’s stealth in poaching multiplies by (1 + reduction) where the design doc wrote (1 − reduction). The divergence is deliberate: it is documented in the module itself.
Worth knowing
The XP curve moved too: the exponent went from 1.6 to 1.45. Progression tables published before that change significantly overestimate the endgame grind.
