@page "/theory" @using CsWeb.Services @model TheoryModel @{ ViewData["Title"] = "Theorycrafting"; var b = Model.B; }

Theorycrafting

Numbers below are live — read from the running servers' effective config, including any hot-reloaded balance tuning. The curves are computed by the same compiled code that scales damage in game. Everything is also editable: change any knob to explore; Reset returns to server values.

Showing values for @Fmt.ModeName(b?.Mode) — view for TDM · Gun Game · Survival (modes can override handicap bands).

@if (b is null) {
No server reachable right now — theory needs a live config to be honest. Try again shortly.
} else { @if (Model.ModeMismatch) {
No @Fmt.ModeName(Model.ModeParam) server is reachable right now — showing @Fmt.ModeName(b.Mode) values (the only live config available).
} @if (Model.Balance is { Online: false }) {
Servers offline — showing the last known config (@Fmt.Age(Model.Balance!)).
}

The handicap

One signed index t summarizes how dominant you are (K/D, headshot rate, killstreak, level, and the mode's progress axis, weighted and eased). t = 0 is neutral; +1 is fully dominant; −1 is struggling. All three multipliers are driven by the same t, so they hit their extremes together: dominate and you deal less, take more, and level faster — all at once.

@if (Model.Panels.Count > 0) {
t = 0.00 @foreach (var p in Model.Panels) { ×1.00 } dashed green line = your simulated player
@foreach (var (p, i) in Model.Panels.Select((p, i) => (p, i))) { var last = i == Model.Panels.Count - 1; var height = TheoryModel.T + TheoryModel.PlotH + (last ? 26 : 8); @p.Title @p.YMax.ToString("0.#") ×1 0 @if (last) { @* SVG collides with Razor's literal pseudo-tag inside code blocks -> raw emit *@ foreach (var tv in new[] { -1.0, -0.5, 0.0, 0.5, 1.0 }) { var x = TheoryModel.L + (tv + 1) / 2 * Model.PlotW; @Html.Raw($"{tv:0.#}") } } }
← struggling  ·  t  ·  dominant →   (drag the slider or hover the chart; curves follow your edits below)
}

Simulator

Seeded from the live server config. Edit anything — player state, stat levels, card picks, handicap knobs — and every readout and curve recomputes. Abilities, crits and headshots are excluded (parity with the in-game HUD's base readout). Edited fields get an amber outline.

index0.00 deal band×1.00 take band×1.00 xp band×1.00 Out × (with stats)×1.00 In ××1.00 HS ××1.00 XP × (total)×1.00
Player state
Stat tree (invested levels)
@foreach (var s in Model.StatRows()) { }
StatBasePer lvlInvestedEffective
@s.Name @s.Base @s.PerLevel @s.Base
Survival cards (run-scoped picks)
@foreach (var c in Model.CardRows()) { dynamic card = c; }
CardPer pickPicks
@card.Name @(card.IsTeam ? Html.Raw("team") : Html.Raw("")) @card.PerPick
XP
Handicap knobs (@Fmt.ModeName(b.Mode) effective)
@foreach (var (group, rows) in Model.HandicapGroups()) {

@group

@foreach (var r in rows) { if (r.IsBool) { } else { } }
}

Key points (server values)

@foreach (var r in Model.KeyRows()) { }
tDeal ×Take ×XP ×
@r.T.ToString("0.0") @r.Deal.ToString("0.00") @r.Take.ToString("0.00") @r.Xp.ToString("0.00")

Weapon-by-weapon time-to-kill tables and per-weapon damage simulation are planned on top of this.

} @section Scripts { }