DPS Simulator
Experimental — the model is engine-decoded but young. Treat results as directionally correct, not exact: good for comparing builds, rotations and gear choices against each other, not for predicting live numbers — real fights can differ meaningfully (see Model notes for what is and isn't modeled). Set up your gear in the Build Planner, then pick the build below.

Character

Loading…

Party buffs (from allies, applied to you at 100% uptime — cast your own buffs in the rotation)

Target debuffs (applied to every dummy)

Summons (already active at fight start)

Pets you walk into the fight with — no rotation cast needed (a rotation entry for the same skill just parks). Counts are not mana-checked: the game bounds live summons by reserved mana, so only enter what you can actually maintain.

Targets single-target skills & autos hit the first dummy; AoE casts hit all

Monster stats are engine-derived estimates (Formula.GetMonsterBaseStats + rank bonuses, disasm) — in-game cards can differ while a boss buffs itself; every field stays editable.
melee range (≤6m)
Model notes & provenance
  • Every pipeline step is transcribed from GameAssembly.dll disasm (see source header). Nothing is guessed; simplifications are listed here, not hidden.
  • Expected value: crits/hits/multistrike are probability-blended, the ±5% physical variance averages to ×1.0 (magic has no variance in the engine). Crits bypass the flee roll (engine order: block → crit → flee).
  • Autos only fill time between skill casts. Cast lock (ForceCast disasm): every cast locks the caster for max(0.3s, weapon delay × 0.6) × (1 − AfterCastDelay/100) — the weapon-delay term applies to damage skills (Magic, and non-attached non-toggle Melee/Ranged); buffs and attached spins pay only the 0.3s floor. This is the in-game "0.3s Execute / 0.6s Stomp" feel: fast weapons hit the floor, a 1.0s-delay weapon locks 0.6s. A skill's Delay field is NOT a caster lock — the engine uses it as the spawned instance's fuse timing. The old model let 0-cast instants weave completely free (wrong: the 0x15c AnimationDelay timer is dead code in this build, but ForceCast writes the SAME gate via the 0x14c timer — corrected). Backslide/WeaponSwap skills (FlashBang, ShadowFeint, Weapon Swap) bypass the gate entirely (SkillState.IgnoreDelay).
  • Splash & area mods: the Splash stat makes every auto swing hit all other dummies with the SAME rolled packet (ApplyHit reuses the Damage struct); a skill's AoE radius = Area + SkillArea:<id> item mods, and instant-cast skills add the global SkillSplash stat (a SkillSplash grimoire turns instant bolts into small AoEs). StatusDuration:<id> applier mods extend applied status windows; BuffDuration has NO reader in the engine (inert stat) and is not modeled.
  • DoTs (full lifecycle, disasm-verified): every application (cast or on-hit proc) is tracked as its own event with the link's duration × (1+StatusDuration mods), stack count, and land/proc probability. Stackable DoTs (Poison, Bleeding, Burning, Decay, Anathema — uncapped; Rage/Regeneration cap 25) hold one timer PER STACK; StacksRefresh statuses share one duration that resets on every application; plain statuses just refresh. Ticks run 1/s from first application while stacks live: flat = round((Lv+STR+AGI+INT)·coeff/10) × stacks, read from the applier's LIVE sheet at tick time (buff windows count while they last, engine: TickEffects reads state.Source live); %HP part × stacks only when the per-tick percent < 1 (engine guard). Ticks also see the tick-time TARGET windows (armor breaks, DamageFromStatus). Approximations: expected (fractional) stacks for sub-100% procs, cap clamps the expectation, and tick-time sheets are read from the most recent simulated event (≤1 swing stale).
  • Skill %ATK coefficient scales with weapon StatScalings (+stat/10·s/100) — offhand weapon's row when dual-wielding, "None" row when unarmed.
  • The FULL build flows in: equipment×refine, cards, substats, sets, artifacts, gems (incl. gem refine), passives, grimoires — via the simulator's aggregate — PLUS the engine-layer gear stats the sim sheet doesn't display (SkillDamage:<skill> on skill gems, DamageElement/DamageTo/FromElement, DamageMelee/Ranged/Magic/Status, Def/MdefPierce, FinalDamage*, CooldownRecovery, close/far-range, and per-skill item mods: SkillLevel/SkillCooldown flat-sec (0.3s floor)/SkillCastTime(+Mult)/SkillHits — all per SkillState.Calibrate). SkillApplyStatus:<skill>_<status> mods (grimoires: Alpha Surge, Hexwell Current, Blessed Resonance…) apply the status to the victim for Value seconds on every landed application of that skill (ApplyDamageToTarget g__ApplyEffects disasm; chance 0 = guaranteed) — modeled for YOUR rotation casts; pet-kit carriers (Black Blade/Vortex Slash Stagger via the Werewolf) are still skipped with the rest of pet status application, and ally-heal carriers (Resonance Well → Might) ride the unmodeled heal path.
  • Buff mods with per-level terms scale with the source skill's level; stacking statuses apply base per stack. Buffs ticked in the PICKER are constant (party-style); buff skills placed in the ROTATION are cast with real timing — their statuses apply for the link's duration (stacks accumulate per the status's engine flags, toggles stay on once cast), the sheet changes inside the window, and they re-cast only when about to drop. On-hit SELF procs ramp: a buff whose procs target the attacker (Enrage → Rage, ×25 AtkMult, shared refreshing window) gains stacks on every landed physical hit — the sheet climbs swing by swing to the plateau (sub-100% chances add fractional expected stacks; per-stack expiry of non-refresh self buffs is approximated by the latest application's window).
  • Grimoire trigger effects (StatValue events, HandleEventModifiers disasm): the equipped grimoires' riders fire on real sim events — OnAutoAttack/OnApplyHit (per landed swing or skill wave), OnCast <skill>, OnApplyStatus, OnHeal (negative-coefficient casts), OnMaxStacks (a ramped buff hitting its cap chains its rider: Radiance ×5 → Radiant Bulwark). Chance is a fraction rolled per event (Spellshot: 10% CastReady per auto); Status triggers apply the ref for Value seconds to the caster (Target=3) or the victim (Vulnerability); Autocast triggers cast the ref at 0.3×MaxLv for free — damage lands in the autocasts log/row, and the proc'd cast applies its own statuses (Venom Bloom keeps Poison rolling). Conditions (Status:X / StatusSelf:X) check the live sim windows.
  • Summoner pets (SummoningComponent$$LoadSummonSkill + CopyModifiersAutocastsWeapons + ApplyStatShare/IsSummonInherited disasm): a summon skill maintains ONE pet with its OWN attribute block (SummonConfig stats at YOUR level — Formula$$Level; pets never get monster base stats) run through the same verify-locked derive() — and the pet INHERITS from you: your Mainhand/Offhand WeaponStates verbatim (weapon ATK/MATK/DoubleAttack/Chain/element + type — its stance, swing delay and skill scalings are your weapons'), SummonStatShare% of EVERY unqualified stat on your gear+passive layers (round(share×value) per StatType; any stat whose name lacks 'Summon' — Summon Mastery 10 = a full 100% copy of your gear layer; qualified stats like SkillDamage:<skill> gems are never shared), your gear's autocast tables at FULL chance (packets off the pet's sheet), and the Summon* translation family (SummonAtkMult→AtkMult, SummonCrit→Crit, SummonDamage→FinalDamage, SummonAutoattackDamage, SummonHit, SummonAllStats) — all read at SUMMON TIME, so buffs live at the cast count. SummonSpellEcho:<skill> gives each qualified cast a mod% chance the primary pet re-casts it off ITS sheet (AutocastMult, your cast level). The pet auto-attacks your target and runs its AI kit (per-entry Level/Cooldown/CastTime overrides; kit cast times ×0.1 — the pet's default CastTimeReduction 100 under the 90% cap) with the same aftercast pacing rules. Pets act INSIDE the event loop: every pet hit is computed against the live per-state target sheet (your armor breaks and debuff windows boost pet damage), and pet kit statuses apply for real — Skeleton spears ramp Vulnerability ×25, Wraith Harvest opens Stagger (−25% DEF), Skeleton Mage Death Coils stack Decay DoTs ticking off the PET's own sheet — which then boost YOUR casts too. NOT inherited (SkillState$Calibrate binds pet skills to the PET's controller; the attacker-side complex-modifier dict 0x1d8 is never copied): your per-skill qualified mods — SkillDamage:<skill> gems and SkillApplyStatus riders (Alpha Surge's pet-skill lines) do NOT boost pet casts. Remaining approximations: buff/toggle kit entries and self-buff maintenance are skipped, pet statuses land on the primary dummy only, entry cooldown 0 is paced by the 0.3s/weapon-delay lock, and pets never die (true vs dummies). The Summons panel assumes pets active from t=0 (count copies each, summoned pre-pull with your standing picker buffs); the engine’s reserved-mana bound on live summons is not enforced.
  • Shinobi shadow clones (SummoningComponent disasm): a Clone-spawning cast (Shadow Seal, Shadow Feint) adds a bot copy of the player — ATK/MATK ×0.25 (TotalAtk/TotalMatk −75), NoStatusApply (clone casts apply NO statuses and roll NO procs), DoT-immune, each reserving 25% max mana (oldest dies when max mana drops under 100 — practical cap 3, the sim's cap). Clones auto-attack your target continuously on their own swing timer (25% packets) and MIRROR every CloneCast-flagged skill (Fan of Knives, Shuriken Fan, the Releases…) with a full cast off their 25% sheet — flat damage parts survive unscaled (the engine only reduces the attack stats). Approximations: clones use the base sheet (buff windows are copied only at spawn in the engine), clone chains and clone gear autocasts are not rolled.
  • Zone/spin skills (SkillInstance$$Update disasm): a damaging skill with duration and no projectile velocity (Cyclone, Fire Field, Consecrated Ground, Arrow Shower — 27 skills) spawns an instance that deals HALF its packet to every target in radius every 0.5s (TargetDamageInterval: inits 0, resets to 0.5f) — one full packet per second per target, floor(dur/0.5)+1 waves. Enemy statuses (Cyclone's Bleeding) apply PER WAVE — stackable DoTs ramp hard; on-hit procs (Enrage → Rage) fire per wave; zone debuff windows ramp +1 stack per 0.5s. SkillDuration:<skill> gear (Chaos Reaver −1s Cyclone) adds flat seconds; HitLimit-bearing instances die after that many target-hits. Spins whose self status carries NoAttack+NoCast (Cyclone/Whirlwind/Death Spiral) LOCK the caster for the spin — the lock and the auto-block both run the full duration. The old one-packet model undercounted Cyclone ~3.5×.
  • Multiple dummies: single-target skills, autos and their DoTs hit the FIRST dummy; AoE casts (export: Area radius > 0 or ground cast, overridable per rotation entry) hit every dummy with no target cap — the engine exposes no per-skill max-target count.
  • Chains: auto attacks jump to Chain-stat-many extra dummies, Chain Lightning-style skills to their Chains count (+SkillChains item mods). Each jump is a full fresh hit vs that dummy's own profile (engine: the chain continuation re-runs the FULL ApplyHit on the next target within 9m, 0.1s later) — so every jump also rolls autocasts, fires on-hit procs (Enrage ramps per jump) and OnAutoAttack/OnApplyHit riders, per hand when dual-wielding. Skill chain victims likewise join the autocast roll set (3-target cap) and fire per-victim on-hit procs/riders. Jumps need spare dummies to land on.
  • Autocasts (engine: AutocastHandler.Trigger + SkillState(-1) ctor): AutocastAttack:<skill> items/cards roll chance% × (1+AutocastChance/100) on every landed damaging attack — auto attacks always, skill casts only for TriggerAutocast skills (Frost Blade, Shuriken Fan) — per target hit, max 3 targets per attack. AutocastHit rolls on landed physical hits (0.1s throttle). The proc casts at level max(1, round(0.3×MaxLv)) + SkillLevel:<skill> item mods (Calibrate; grimoire riders skip Calibrate and stay at the base) — NOT your allocated level — with its damage coefficient × AutocastMult (Firebolt-family: ×0.5), instantly and for free. The proc'd cast applies its statuses too: DoT links (Fireball → Burning) become probability-weighted applications, modded debuffs (EarthExposure) open expected-stack target windows on the specs hit. NoAttack gear (Nozzle Robot) blocks autos entirely per CanAttack. Multistrike never re-rolls procs: the engine multiplies ONE packet by 1+floor(MS/100)+Roll(frac) — one hit event, one autocast roll (ApplyHit disasm). Dual-wield doubles the events: an offhand WEAPON schedules a second ApplyHit per swing (AttackMainHit + AttackOffHit), so autocast rolls, on-hit procs and OnAutoAttack riders all fire twice — damage stays folded in the single-ATK packet. Skills never fire OnAutoAttack (that event lives in the auto ApplyHit path); "skills that count as attacks" are exactly the TriggerAutocast/TriggerHit flags.
  • Rotation casts auto-apply their enemy debuffs (WaterExposure, armor breaks…) as timed PER-SPEC target windows — single-target casts debuff the primary dummy (+chain-jump victims), AoE casts debuff every spec, and damage recomputes against each dummy's own debuffed sheet while windows last; stacks accumulate only for Stackable/StacksRefresh statuses; picker-ticked debuffs stay constant instead. Window expiries are simulation events, so idle stretches re-derive the sheet exactly (residual: with autos weaving, an expiry mid-swing resolves at the next swing — ≤1 swing stale). Charge skills bank Charges+1 casts (⚡chip), refill one per cooldown, 0.3s between casts.
  • Sequence mode: entries cast in exact listed order, looping; only cooldown/charge clocks gate a sequenced cast (a sequenced buff refreshes on its turn even mid-window — you dictate the order). Parked entries (toggles already on, picker-owned buffs' appliers with nothing else to do) are skipped; auto attacks fill the waits when "weave" is on. A ⚔ entry in the sequence is exactly ONE swing at that point.
  • Opener → loop: drop a loop divider into the rotation and the entries ABOVE it become a one-time opener — each casts once, in listed order, at the pull (autos weave the waits; cooldowns/cast-locks still gate). Entries BELOW repeat under the chosen loop mode. No divider = one repeating loop (unchanged). Per-entry cast policy (loop rows): auto (default — damage on cooldown, buffs/maintainers refresh on falloff), on cooldown (recast whenever the cooldown is ready, even a buff), on falloff (recast ONLY when the skill's own buff/debuff window is about to drop, ignoring spare cooldown — works in sequence mode too, where the entry parks on its turn while its window is healthy).
  • Auto-optimize (the ✨ button): searches for the highest-DPS priority rotation for the loaded build under the CURRENT fight config, by running this same engine over candidate combinations of the build's allocated, non-passive skills (each at its allocated level — no invented skills or levels; only skills the build can actually CAST with the equipped weapon are eligible, so a wrong-weapon skill is never chosen) with auto attack as the gap filler — steepest-ascent inclusion from several seeds (your current rotation, a support-first opener of spawners→buffs→damage, all skills by damage rate), then local refinement (drop / reorder / cast-policy). Because the priority list's order at the pull IS the opener order, the result keeps toggles (Berserk), party buffs and summon/clone spawners (e.g. Shadow Seal) ABOVE the damage skills whenever it costs nothing — they get established before the first nuke, then park. It applies the result only if it beats your current rotation, and reports the gain + how many simulations it ran. It's a strong local search under a sim budget, not a proof of global optimum — a hand-tuned opener/sequence may still edge it on rotations with tight ordering interactions.
  • Gunslinger dual loadout: the snapshot carries BOTH weapon sets' full aggregates (gear/cards/substats on each weapon pair, incl. their engine-layer stats). Rotation entries auto-assign to the set whose weapon satisfies the skill's WeaponTypes requirement (export; Point Blank → the Shotgun set) — the A/B pill overrides per entry; autos follow the "autos use" picker. Each cast computes on its own set's sheet (ATK, crit, delay, CDR, close/far-range mods, autocast tables). The swap itself is modeled as FREE: the demo assembly exposes no swap lock or cooldown method, so any real swap animation time is unmodeled (sim may run slightly hot on swap-heavy rotations).
  • Combo system (SkillsComponent.SpawnSkill + GetComboMult disasm): builder skills (ComboReady, e.g. Suppressive Shot/Jump Shot/Strafing Volley — 🔗builds chip) open the caster's combo window for 4s (extends, never shrinks); finisher skills (ComboFinisher — 🔗×1.5 chip: Point Blank, Shrapnel, Panic Burst, Sniper Shot) deal ×(1+CF/100) while the window is live. The window is NOT consumed by finishers — it just expires. Fight log shows "Combo window opened" and per-cast "combo ×1.50" notes.
  • Reflect & %-max-HP skills (SkillState$$GetDamage 0x644540): reflect-coefficient skills (Shield Bash, Shield Throw, Counter Slash — config DamageReflect) scale off caster level + ½(ATK + DEF + DefFlat) × coefficient × (1 + ReflectDamage%), NOT a plain %ATK (Formula$$GetReflectDamage 0x791640, then ×0.25); DEF/VIT matter as much as ATK. %-max-HP skills (Corpse Explosion, Umbral* — config DamageMax) deal round(target maxHP × pct/100) as TRUE damage (SkillState.ApplyDamageMax → GetTrueDamage, no def) added on top of any base — so they read 0 vs the maxHP-0 training dummy; use a boss/monster target. SelfDamageMax (caster %HP self-cost) is not counted as output.
  • MP economy (opt-in — the model MP toggle; OFF by default so saved/shared setups stay unchanged): casts spend MP = round((skill cost + SkillCost:<id>) × (1 + MpCost/100)); the pool is Formula$$MaxMana (the sheet's Max MP) and regenerates every ~1s (Formula$$ManaRegen amount, RegenTimer = 1/(1+MpRegenRate/100)s, capped at Max MP — SkillState / _skup.txt disasm), with SiphonMp refunded on landed attacks and SkillRecoverMp on cast. A cast that can't afford its cost waits for regen (SkillState$$Cast gate: Mana ≥ GetSkillCost) — so mana-hungry spam is throttled and CDR stops looking free. Autos cost 0. NOTE: MpCost% is the verified term; the flat SkillCost fold mirrors SkillCooldown (GetSkillCost's exact stat combination isn't disassembled).
  • Not modeled yet: SkillDamageLowHp (mana≤35%), GrantSkill items, dual-wield per-hand ATK split (uses the sim's single-ATK convention), autocast-off-autocast recursion, weapon-swap animation time, SpellAuto (engine-plumbed 'autos also cast X' — zero users in the demo data), and grimoire triggers on events that can't fire vs dummies (OnBlock/OnDodge/OnKill/OnHealthLow/OnCharge) or that carry no dummy DPS (RecoverMp/RemoveStatus).

Rotation

Result

expected DPS
total over fight
casts + autos

Fight log

casts buffs autos autocasts dots