This page explains exactly how this simulator models melee damage, hit chance, haste, offense, and target defenses. Formulas are based on EQMacEmu combat logic and this simulator's implementation.
In-Game Mechanic — direct simulation of EQ server behavior. Sim Feature — specific to this simulator; not how the game itself works internally.
The first time you open gSim (or after any release that changes the app version), you'll see a picture split in two — click the Easy side or the Advanced side to pick your starting mode. You can always switch modes later with the Easy/Advanced toggle in the header.
Your choice is saved in your browser (not on a server), so you won't be asked again on that same device/browser until gSim ships a new version. A different device or browser, or clearing your browser data, will show the picker again.
Every weapon starts from base damage and delay. Delay is in deciseconds
(40 = 4.0s).
Faster effective delay means more swings per minute and generally more DPS.
The header shows Total TPS — threat per second. It is not your damage per second; it is a separate aggregate used to reason about tank snap aggro and weapon proc contribution.
Over a simulated fight of length T seconds, the sim accumulates integer threat from allowed sources. The report value is:
Ranged uses the same idea: bow shot swing threat plus ranged proc threat.
Displayed TPS uses:
So TPS is an average rate over the whole fight. It scales with how often you generate swing hate and proc hate, not with the size of individual damage rolls (for swings). Swing threat is always weaponBaseDamage + damageBonus — it is not affected by target AC, mitigation, or whether the swing actually hit. TPS will stay constant when you change the target AC; DPS will not.
max(0, cappedWeaponDamage + mainHandDamageBonus). This matches EQMacEmu-style pre-roll hate from displayed base + damage bonus (threat.js: meleeSwingThreatPrimary).
max(0, cappedOffhandWeaponDamage) only — no main-hand damage bonus on the secondary slot (meleeSwingThreatOffhand).
meleeSwingThreatPrimary(fistBase, 0)).
!CanClassCastSpell(spell_id) (mob.h) — whether the spell is on the caster's own class spell list. Every proc, item click, or rune this sim models is not on the player's own class list (they're weapon-triggered or clicky-triggered spells), so the cap always applies here — but even then it only clamps the non-damage hate component. Direct-damage hate is added afterward, always uncapped: combinedHate = min(nonDamageHate, 400) + damageHate. So a capped proc that also deals a large hit can still generate well over 400 total threat.
min(400, min(1200, max(25, floor(targetMaxHp / 15)))) (e.g. Thunder Strike) — the DD portion next to it is not reduced by this cap.
min(1200, max(25, floor(targetMaxHp / 15))), then capped at 400 per proc event. targetMaxHp comes from the selected NPC's hp when you pick a target from search; otherwise a generic HP curve (e.g. 22,105 at mob level 60, scaled by level).
effect_base_value on SE 92 is a direct hate adjustment, applied once per instant proc (not each DoT tick), signed: positive adds hate, negative reduces it (e.g. Khalshazar's Deceit). Unlike the detrimental max-HP/15 slice above, it is not subject to the 400 cap — it's added after that cap is applied, so a spell like Enraging Blow (+700) generates the full 700 hate per successful proc, not 400. Spells that are only SE 92 (no HP damage) do not use the max-HP/15 non-DD bucket at all—they use this flat value only.
runeValue × 2, where runeValue is the HP absorb amount from that first effect slot (e.g. Rune III absorbs 168, generating base hate 336). This is non-damage hate (a rune deals no damage to the mob), so it is:
CanClassCastSpell-false assumption as every other proc/click this sim models.
This cap-scope rule is the same reason the Rotation Sim's TPS is never capped: every spell it casts comes from the caster's own class spellbook (CanClassCastSpell is always true there), so its direct-damage hate is uncapped from the start — see the Rotation Sim section below.
A pet is coded server-side as its own NPC, and it hates independently: when a pet hits the mob, the mob's hate list gets a full entry for the pet, and the owner is only added to that same hate list with zero hate and zero damage — enough for the mob to notice and potentially retarget the owner, but no share of the pet's threat is ever credited to the player. The player's Total TPS in the header therefore never includes pet damage, matching this.
The pet's own threat (shown as "Pet TPS" in the Pet Damage report section) uses a different formula than the player's weapon-rating hate: NPCs (and pets, which route through the same code) generate
floor(baseDamage / 2) hate on every swing attempt — hit, miss, or block alike — where baseDamage is the same pre-mitigation base damage value used for the pet's damage roll (NpcCombat.getBaseDamage). The main-hand damage bonus is not part of this hate formula. Any buffed pet proc (e.g. a Beastlord's "Spirit of X" line) adds proc threat using the same uncapped direct-damage hate as player weapon procs (see 2.2 above — the 400 cap only ever clamps non-damage hate).
Implementation: js/modules/petCombat.js. Reference: EQMacEmu NPC::Attack (zone/attack.cpp), distinct from the client hate path used for the player's own TPS above.
Suppose a 60-second fight, main hand only, no offhand, no procs, no specials: weapon shows 15 damage, main-hand damage bonus 35, effective delay 2.0s → about 30 main-hand swings. Each swing adds swing hate 15 + 35 = 50.
swingThreatAcc ≈ 30 × 50 = 1,500procThreatAcc = 0totalThreat = 1,500 → TPS ≈ 1,500 / 60 = 25.0If you also enable a special on cooldown (e.g. kick) and it fires 6 times in 60s, each attempt adds another 50 swing hate (same as one MH auto), even if some miss:
6 × 50 = 300totalThreat ≈ 1,500 + 300 = 1,800 → TPS ≈ 30.0
If the weapon then procs an instant DD with base 200, each proc adds the full 200 to procThreatAcc (not reduced by resists in the threat math, and not capped — direct-damage proc hate is always uncapped). Ten procs would add 2,000 proc threat on top of swing threat, pushing TPS up accordingly. A proc with base damage above 400 (say, 600) would add the full 600, not 400 — the 400 cap only ever applies to a proc's non-damage hate component (see 2.2 above), never to this DD figure.
Implementation: threat.js + combat.js. Reference: EQMacEmu-style swing and proc hate (approximate; not identical to live in every edge case).
Actual haste rules used by the simulator:
Actual result examples:
Haste changes attack frequency, not hit chance directly.
These are different and used in different parts of the calculation:
weaponSkill is the weapon type skill for whatever you're wielding (e.g. 1H Slash, 2H Slash), capped by class. A warrior using 1H Slash caps at 200, while most other melee classes cap at 252 for their primary weapon. This means warriors can have a lower offense rating for damage than other classes even at max skill — but their damage bonus, triple attack, and flurry compensate.
Some weapons (e.g. Shadowed Torment) carry a built-in bonus to their own weapon skill (1H Blunt, 1H Slash, 2H Blunt, 2H Slash, or Piercing). This is a percentage bonus to your current skill, not a flat number of skill points, and it's capped at 252 either way:
The report's Offense & To-Hit Model section shows the base skill, the modifier, and the resulting post-modifier skill whenever an equipped weapon carries one of these bonuses. The same percentage formula is used for Archery skill mods and for Backstab skill mods on rogue weapons.
This isn't limited to weapons — any equipped item (rings, armor, etc.) can carry the same skillmodtype/skillmodvalue bonus. On Import Inventory, the sim scans every equipped slot and keeps the highest value seen per skill type, then combines that with the currently-equipped weapon's own mod (again taking the higher of the two, not adding them) — this matches the server's item-bonus logic, which keeps a single max value per skill rather than stacking every item that touches it. The Rank Weapons tool applies this the same way: every candidate weapon's own mod is combined with your gear-wide mods (from your last import) when scoring it, so switching to a weapon with a bigger built-in mod, or one that shares a skill type with your already-equipped gear mod, is reflected correctly in the rankings.
The Weapon 1/Weapon 2 cards on the Melee tab show this the same way they already show Backstab and Archery mods: whenever the equipped weapon (or gear) carries a skill mod for that weapon's own type, the small skill line under the weapon name adds e.g. "2HS +3% (225→231)" — the percentage and the resulting base→post-modifier skill, using your currently-set base skill for that weapon type (or the class/level cap if you haven't set one).
The same mechanic also covers the non-weapon skills the sim models: Offense, Double Attack, and Dual Wield on the Melee tab; Defense, Dodge, Parry, Riposte, and Block on the Tanking tab; and Kick, Bash, Flying Kick, Dragon Punch, Eagle Strike, Tiger Claw, and Round Kick for whichever special attack is selected (see section 7.5.1). Import Inventory scans every equipped item for these skill mods too, keeping only the highest value seen per skill (a Dodge-mod ring and a Dodge-mod cloak don't add together — only the bigger one counts), and the Equipped Items summary lists each one it finds under a "Worn +<Skill> %" line, with a note when more than one item carries the same mod. Base training in these skills still has to be entered by hand (trainers grant it, not gear, so it isn't in an inventory export) — the gear mod is applied on top of whatever base skill value is in each field.
When dual wielding, the mainhand and offhand weapons roll to-hit and offense rating using their own weapon skill — a 1H Slash mainhand paired with a 1H Blunt offhand uses each weapon's own skill (and its own item skill mod, if any) independently. Whenever a genuine offhand weapon is equipped, the report breaks the skill and offense rating out into "MH" and "OH" lines. (Fistweaving and two-hand weaving reswing the same mainhand weapon, so they use one shared skill line instead.)
STR contributes through:
Stat cap: STR and DEX are capped before contributing to offense rating. The base cap is 255 through level 60. In Planes of Power, levels 61–65 raise this cap automatically by +5 per level (260 at 61, up to 280 at 65) — this happens regardless of AAs. The Planar Power AA (ranks 1–5) then adds a further +5 per rank on top of whatever the level-based cap already is, for a maximum of 305 (level 65 + Planar Power rank 5). The cap is enforced at simulation time for both melee (STR → offense rating) and ranged (DEX → archery offense rating). The Gear Advisor targets this same 305 ceiling whenever the Planes of Power era is selected, regardless of the Planar Power AA rank actually entered — by the time a character has farmed BIS gear they've almost certainly picked up the AA too. It also raises its worn haste target from 41% (pre-PoP) to 46% (the highest worn-haste item available in Planes of Power; used here as a hard target, not a server-enforced cap) under the same era condition — without that, a genuine 44–46%-haste PoP item scores no better than an older 41%-haste item (both hit the target), so it can lose ties to gear it should outrank. Because only the single highest worn-haste item in a build ever applies in-game, the Gear Advisor also treats haste as "already covered" for scoring purposes as soon as any one slot supplies it at all, rather than only once the full target is reached — every other stat still has to reach its own target before its priority weight steps down, but haste is a one-item-wins stat, so a second or third slot with haste never adds real value and shouldn't crowd out a bigger gain elsewhere.
How the Gear Advisor searches for a full set: picking each slot's best item one at a time, in a fixed order, and never revisiting earlier picks would bias the result toward whichever slot happened to be evaluated first — that slot always "sees" an empty set of other gear, so it can lock in an item that a different fill order wouldn't have chosen. Instead, the advisor runs several full passes that each fill every slot in a different order, and after each pass it repeatedly revisits every slot and tries swapping in one of that slot's next-best candidates (not only its single top pick) to see whether the change raises the entire set's total value — not just that one slot's score in isolation. It keeps whichever swap helps most, repeats until a full sweep finds no further improvement, and then keeps whichever of the several full passes produced the highest-scoring complete set. This is what lets it find, say, a slightly lower-scoring helm that frees up more of a capped stat's room for the chest or legs slot to use, rather than greedily grabbing the single best-looking helm and never reconsidering it.
AC is not a single value in this sim. It is split into two defense concepts:
There is no cap on toHit — Eagle Eye, high weapon skill, and accuracy AAs can push it above 550 and do so freely.
Tactical Mastery AA (Warrior, PoP): reduces the NPC's effective avoidance by 10/20/30 at rank 1/2/3 before the hit-chance formula runs. A lower avoidance value shifts the hit-chance formula in the attacker's favour, increasing landed-hit rate.
Mitigation is the defender value used only for the damage roll (how hard a landed hit hits). It does not affect hit chance. It is computed from mob level and target AC, then used in the RollD20 contest below.
How mitigation is computed (GetMitigation):
mit = level × 3 (and +2 if level < 3).mit = floor(level × 41 / 10) − 15.+ floor(4×itemAC/3) + floor(spellAC/4).For each landed hit, the simulator rolls a rollIndex from 1 to 20. That index decides where the hit lands in the damage range:
damageBonus is the melee damage bonus (level/class/delay-based, warrior classes only). It applies to mainhand swings only — offhand swings always use damageBonus = 0.
So rollIndex = 1 gives minimum damage; rollIndex = 20 gives maximum damage (for that weapon's base range, before crits etc.).
How rollIndex is derived (the atk/def roll competition):
atkRoll = random(0, offenseRating+5) — uniform integer from 0 up to (offenseRating+5)−1.
defRoll = random(0, mitigation+5) — uniform integer from 0 up to (mitigation+5)−1.
avg = floor((offenseRating + mitigation + 10) / 2).
raw = (atkRoll − defRoll) + floor(avg/2), then scaled and clamped into 0..19, and rollIndex = raw + 1 so the final value is 1..20.
What causes a hit to hit for max vs min?
Higher offense rating (STR, worn ATK, spell ATK, weapon skill) increases the range of atkRoll, so you can roll higher and get more big hits. Higher mitigation (e.g. high-AC or high-level mobs) increases the range of defRoll, so the defender often "wins" more and you see more low (min) hits and fewer max hits. That is why high-mitigation targets feel like they reduce your damage spread and push hits toward the bottom of the range.
Choosing Behind does not force all swings to land. Misses still happen from to-hit vs avoidance.
Each check uses the NPC's class and level to derive a skill value (min(level × 5, class_cap)), then converts it to a chance with EQMacEmu's divisor formula:
Checks are sequential; each is only reached if the previous failed. The combined avoidance probability is:
Warrior-type mobs (all four skills) have significantly higher front avoidance than casters (dodge only). A Level 60 Warrior avoids roughly 28% of front-facing swings that pass the to-hit check; a Level 60 Wizard avoids roughly 7%. Skill caps vary by class — for example, Warriors cap at 200/230/200/260 while Wizards cap at 0/50/0/175.
Use Front for a conservative estimate that accounts for NPC defensive skills; use Behind for DPS-from-rear or when the mob is mezzed/snared and facing away.
For a single landed non-crit swing before proc damage:
Mainhand only: damageBonus is 0 for offhand swings.
So the non-crit range is roughly ~0.1x to ~2.0x of baseDamage (plus damage bonus).
Then multipliers/crit logic can further scale landed damage.
Warriors have an innate chance to critically hit with weapons starting at level 12
(0.5% + DEX/90%). Every other class only gets melee crit chance from AAs — with none
trained, their critical hit chance is 0%.
Combat Fury AA (War/Pal/Rng/SHD/Monk/Brd/Rog/Bst, Luclin): rank 1/2/3 adds a flat
+2%/+4%/+6% crit chance. On non-Warriors, training any rank also unlocks a small DEX-scaled base
crit chance (0.275% + DEX/150%) on top of the flat bonus — this is what lets other
classes "catch up" toward the Warrior's innate crit, though Warriors remain ahead.
This flat total is what the sim exposes as critChanceMult. It does not affect crit
damage — only the roll chance; crit damage size is a fixed multiplier applied separately.
Note: Ingenuity (War/Monk/Rog) sounds like it belongs here but doesn't — it's a
weapon-proc crit chance AA, not a melee swing crit chance AA. See Section 9.1.
The simulator computes:
Approximate chance:
Ferocity AA (War/Rng/Monk/Rog, PoP): adds +10 to doubleAttackEffective per rank (+2% DA chance per rank, up to rank 3 = +6%).
Knight's Advantage AA (Pal/SHD, PoP): adds +10/+20/+30 to doubleAttackEffective per rank (+2%/+4%/+6% DA chance). Stacks with Ferocity.
Speed of the Knight AA (Pal/SHD, PoP): after a double attack, there is a 2%/4%/8% chance at rank 1/2/3 to perform an extra primary-hand 2H attack. Requires a 2H weapon equipped.
Punishing Blade AA (War/Rng/Monk, PoP): after a double attack, there is a 2%/4%/8% chance at rank 1/2/3 to perform an extra primary-hand 2H attack. Requires a 2H weapon equipped. Fires independently of Speed of the Knight in the same round.
Bard and Beastlord are excluded from the normal double-attack skill check. Instead they use a flat AA-driven chance:
These flat chances are rolled independently after the normal DA check fails for those classes.
Raging Flurry AA (Warrior, PoP): on rounds where triple attack succeeds, there is a 10%/20%/30% chance (rank 1/2/3) of an additional primary-hand flurry swing. Requires level 60+.
Offhand rounds are gated by dual-wield chance:
Ambidexterity AA (Luclin): adds +32 to dualWieldEffective (approximately +8.5% offhand round chance at max DW skill).
Offhand swing timing is independent from main hand.
This means offhand does not "wait" for main-hand events.
The Melee tab's Special attack dropdown lets each class fire its class-specific special on cooldown, alongside normal weapon swings: Kick (Warrior/Ranger/Beastlord), Bash/Slam (Warrior/Paladin/Shadow Knight/Cleric), and the Monk's Flying Kick, Dragon Punch, Eagle Strike, Tiger Claw, and Round Kick (Monk chooses one at a time to simulate). Unlike a normal weapon swing, none of these use your weapon's damage or weapon-type skill — each is driven entirely by its own dedicated skill, exactly as EQMacEmu special_attacks.cpp and attack.cpp compute it server-side:
specialSkill is the actual current skill value entered in the "<Special> skill" field next to the dropdown (default 252, i.e. assume capped) — worn +skill% item mods for that specific skill are folded in automatically using the same non-stacking rule as every other worn skill mod (see the worn skill-mod section above): only the single highest item carrying that skill's mod counts.
| Skill | skillBase | Base damage range (skill 0→252) | Min damage |
|---|---|---|---|
| Bash/Slam | 2 | 2 – 6 (before shield bonus) | 1 |
| Kick | 3 | 3 – 7 | 1 |
| Round Kick | 5 | 5 – 9 | 1 |
| Tiger Claw | 4 | 4 – 8 | 1 |
| Eagle Strike | 7 | 7 – 11 | 1 |
| Dragon Punch | 12 | 12 – 16 | 1 |
| Flying Kick | 25 | 25 – 29 | max(1, level × 4 / 5) |
Bash's shield bonus: a shield in the secondary slot adds its AC straight onto Bash's base damage, capped at base + floor(level/5) + 2 — enter the shield's AC in the "Shield AC" field that appears when Bash is selected. This is usually where most of a warrior/paladin/shadowknight's bash damage actually comes from; a bare-fisted or 2H-weapon bash (no shield) only gets the raw skill-based base above. Furious Bash (item focus) and the racial Slam ability (Ogre/Troll/Barbarian bash without a shield) are not modeled.
Backstab uses its own separate formula (see the Backstab section) and is not part of this shared mechanic.
After each Flying Kick attempt (hit or miss), if Technique of Master Wu is trained, two independent rolls determine whether bonus strikes fire.
Source: EQMacEmu special_attacks.cpp.
The two rolls are independent (not a chain):
rng() × 100 < wuChance. If it passes, fire 1 extra monk strike.rng() × 100 < wuChance / 4 (independent of the main roll). If it passes, fire 1 additional extra strike.Each extra strike is randomly selected from four monk combat skills:
| Skill | Skill base damage (max skill 225) | Min damage formula |
|---|---|---|
| Flying Kick | 29 | max(1, level × 4 / 5) |
| Eagle Strike | 11 | 1 |
| Tiger Claw | 8 | 1 |
| Round Kick | 9 | 1 |
Wu's random extra strikes always assume max skill (225) for whichever of the four it picks, regardless of the skill value entered for your manually-selected special above — modeling Wu's picks off your own configured skill would require tracking all four skills independently rather than the one currently selected. Each extra strike rolls its own hit/miss check, damage roll, and crit roll independently.
The report's Technique of Master Wu section shows: Flying Kicks with Wu active, main-roll trigger count, extra strikes attempted/landed per skill, total Wu damage, Wu DPS, and average Wu damage per FK use compared to the average base FK hit.
Because a single Flying Kick "round" can now include bonus Wu strikes, the damage-per-swing chart and pie chart account for this when Technique of Master Wu is active:
Flurry gives a chance of an extra primary-hand attack on rounds where triple attack succeeds. Ranks 1/2/3 give a 10%/20%/30% chance. Requires level 60+.
Flurry only fires after a successful triple attack — it cannot occur on single or double attack rounds. Flurry attacks use the same primary-hand damage roll as normal swings.
Slay Undead is a Paladin-exclusive class AA (3 ranks, available from Luclin onward). On each melee swing that lands against an undead target, the engine first rolls for Slay Undead before checking for a regular melee crit. If the slay roll succeeds the hit becomes a Slay hit and the normal crit path is skipped entirely for that swing. It is not a proc — it rides the melee swing itself, requires no weapon and fires no spell.
The target must have one of the undead body types: 3 (Undead), 8 (Undead Pet), or 12 (Vampire).
If the target does not match, the Slay roll is never attempted.
One flat-percent roll is made independently for every individual swing that hits — each primary-hand hit, double-attack hit, triple-attack hit, and flurry hit all get their own roll. Offhand swings are not checked (Slay only applies to weapon1 / primary hand).
| Rank | Slay chance | Damage multiplier |
|---|---|---|
| Rank 1 | 2.25% | 15× (1500%) |
| Rank 2 | 2.35% | 16× (1600%) |
| Rank 3 | 2.40% | 17× (1700%) |
When the slay roll succeeds, the hit damage is replaced by:
A minimum floor is also enforced to prevent extremely weak rolls from deflating the slay:
Where rolledDamage is the weapon roll after all modifiers (including STR/ATK scaling) but before the crit multiplier,
damageBonus is the melee damage bonus from STR/ATK/offense,
and minBase = 1 (the minimum weapon base roll).
The +6 offset in the numerator reflects the EQMac source convention for undead slay damage and keeps slay hits proportional even on minimum rolls.
If the slay roll fires, the swing is recorded as a Slay hit and the regular melee crit roll is not attempted. SCF (Spell Casting Fury) and Combat Fury crit rolls do not apply to a slay hit. A slay hit also suppresses any additional per-swing damage bonuses that would normally be gated on the crit result. In practice the slay multiplier (15–17×) is large enough that missing the crit is negligible.
The sim report's Slay Undead (AA) section lists:
Slay Undead only appears in the report when the selected class is Paladin. The section is omitted for all other classes.
By default, Paladin and Shadow Knight characters cannot bash while wielding a two-handed weapon in the primary slot — the bash special attack is suppressed. The Two Hand Bash Luclin class AA removes this restriction, enabling bash on cooldown regardless of weapon type.
If the AA is not trained and a 2H weapon is equipped, the sim sets canFireSpecial = false for the bash attack type, so no bash attempts fire during the simulation.
Warriors, Rangers, and Beastlords can "weave" a 1H offhand weapon alongside a two-handed primary by enabling 2H Weave. Monks using a 2H weapon with a 1H weapon in the offhand slot use the same mechanic whenever Force Fistweaving is checked — the sim always assumes non-epic fistweaving (bare fists, 27-decisecond swing, 14 base damage when not weaving an offhand weapon), since that's the general case; the Epic 1.0 fist weapon's faster/lighter numbers (16 decisecond, 9 base) aren't exposed in the UI. In both cases, every mainhand round produces one weave opportunity.
Each weave opportunity resolves as follows:
dualWieldEffective > random(0, 374)). Every DW roll counts as one weave attempt. Failures are counted separately.The offhand cooldown uses the equipped weapon's actual delay (subject to haste), not any fist-punch delay. As long as the offhand weapon's delay is ≤ the mainhand weapon's delay, the offhand will always be ready for the next weave attempt and the only gating factor is the dual wield check.
The sim report shows: Weave attempts (DW rolls made), Blocked by dual wield check (DW failures), and Rounds (DW check passed) (swings that actually executed).
The offhand weapon must be a 1H type. The dual-wield skill field must be non-zero. Ambidexterity AA adds +32 to effective dual wield skill, increasing weave success rate.
Elemental weapon damage is treated as an extra base-damage adder before the melee damage roll. It is first reduced by target resist for that element (MR/FR/CR/PR/DR).
Then this adder is included in swing base damage:
High target resists can heavily suppress elemental contribution; this is why elemental-heavy weapons can vary by target profile.
The sim uses five independent RNG streams per fight, each seeded separately so that rolls in one category never influence rolls in another: the main stream (mainhand hit/damage/DA/crit), a proc stream (proc trigger, resist, and SCF crit rolls), an elemental stream (elemental base adder rolls), a weave stream (fistweave/2H-weave DW checks), and a special stream (backstab/flying-kick hit, damage, crit, double-backstab DA check, and Master Wu trigger/skill rolls). This ensures that swapping a weapon between mainhand and offhand — which changes the number of mainhand swing rolls between special events — cannot shift the RNG state seen by backstab rolls, and vice versa.
Proc chance scales with effective delay and DEX. Offhand has a dual-wield adjustment. Then proc-rate modifiers are applied.
Level-gated procs do not fire below required proc level.
For resistable proc spells, the sim checks immunity and then runs a resist calculation using target resist type (MR/FR/CR/DR/PR), level delta, and spell resist modifier.
Resulting proc damage is:
So procs can be:
This is why two weapons with similar listed proc damage can perform differently against different target resist profiles.
Soul Abrasion AA (Shadow Knight, Luclin, ranks 1–3): multiplies the base proc spell damage by 1.10 / 1.20 / 1.30 before the resist roll and damage calculation. Applied to both primary and offhand proc weapons.
Weapon procs are spell casts, so they roll against the same server crit-chance slot
(aabonuses.CriticalSpellChance) as direct-cast nukes/DoTs. Two AAs feed this slot, and a
class only ever has one of them — they're never both active at once:
The report's Proc spell crits (SCF/Ingenuity) line under each weapon's Procs & Specials section counts how many procs landed a crit under either AA. This is separate from — and does not affect — regular melee swing crit chance (Combat Fury, Section 7.4b).
The ranged tab simulates bow + arrow archery for a level 60 Ranger.
The damage pipeline follows special_attacks.cpp Mob::DoArcheryAttackDmg and attack.cpp CalcMeleeDamage.
In order, for each shot that hits:
bow.damage + arrow.damage. This is multiplied by
RuleR(Combat, ArcheryBaseDamageBonus) (default 1.0 on Project Quarm — no change).
aabonuses.ArcheryDamageModifier):
spellbonuses.ArcheryDamageModifier = 105):
CalcMeleeDamage (attack.cpp) explicitly halves the base for SkillArchery before the D20 roll:
RollD20(offenseRating, mitigation) contest as melee (see Section 5).
damage++ at level 55+ is explicitly excluded for archery.
1.35 + DEX/34% (at DEX 255 ≈ 8.8%). Requires level > 16.
The D20 roll produces a value from 1–20; roll = 20 gives the maximum non-crit hit. There is no melee damage bonus for archery (damageBonus = 0), so:
At Archery Mastery rank 3 (+100%), the mastery exactly offsets the server ÷2, so shotBase = bow + arrow and max non-crit = floor((20 × (bow+arrow) + 5) / 10) ≈ 2 × (bow+arrow).
With the Ranger stationary bonus the post-D20 result is doubled before the crit roll, so:
When a Ranger above level 50 shoots a target that is an NPC, not currently moving, and not temporarily rooted, the arrow deals double damage. This is implemented in beacon.cpp Beacon::Update and fires before TryCriticalHit, so the doubled value feeds into the multiplier and crit rolls.
Server rules on Project Quarm:
ArcheryBonusRequiresStationary = true — stationary check is enforced.UseArcheryBonusRoll = false — no random roll; the bonus fires unconditionally when the stationary check passes.ArcheryBonusChance = 50 — only relevant when UseArcheryBonusRoll = true (not active here).The Mob stationary checkbox in the ranged tab controls whether this bonus applies in the simulation. It is checked by default because most raid and camp targets are stationary. Uncheck it when simulating a kiting or moving target.
Archery uses DEX, not STR, for the stat bonus:
Rangers above level 54 also receive a linear bonus:
The DEX contribution to offense rating is:
Every 1.5 DEX = +1 offense rating. At the 255 cap, DEX contributes +120 offense rating over the zero-bonus baseline:
| DEX | dexBonus (offense rating) |
|---|---|
| 75 | 0 |
| 150 | +50 |
| 200 | +83 |
| 255 | +120 |
DEX also raises Ranger archery crit chance and proc chance:
1.35 + DEX/34% — going from DEX 75 to 255 adds roughly +5.3% crit chance.~0.00001144 × DEX × delay per swing — a small but real factor on slow bows.In practice, DEX past the 255 stat cap provides a small additional bonus to crit chance and proc chance (each extra DEX point above 255 is divided by 400 for crits, 200 for procs), but the main payoff from DEX comes from reaching 255.
Applied separately from standard haste (not subject to the 125% cap). If a quiver provides weight reduction (WR%), quiver haste = WR% ÷ 4 ÷ 100.
The Sony rule prevents the quiver from pushing delay below 1000ms (1 second).
The ranged tab also answers: how much DPS should be credited to the bow vs the arrow so they can be ranked independently?
The simulator assumes that, before procs, the bow and arrow contribute proportionally to base DPS according to their relative base damages:
Base DPS is then split using these shares, while all proc DPS is credited to the bow:
This preserves the total ranged DPS from the full simulation while giving a per-combo breakdown of how much the bow and arrow each contribute. The ranged ranking UI shows these values as Bow DPS, Arrow DPS, and Total DPS.
To rank bows and arrows independently, the simulator first computes DPS for a grid of bow+arrow combinations, then derives slot values from those combos:
totalDps using that bow.totalDps using that arrow.In slot mode, the ranged ranking table shows:
Bow: <name> with the best total DPS that bow can achieve in any tested combo (and its bow/arrow DPS split).Arrow: <name> with the best total DPS that arrow can achieve in any tested combo (and its bow/arrow DPS split).This mirrors the melee slot-ranking behavior but is tailored for bows/arrows.
The full grid of combinations is #bows × #arrows. To keep the simulation responsive while still ranking
arrows meaningfully, the simulator prunes bows using a simple damage/delay heuristic:
Bows are sorted descending by score, and only the top K (currently 40) are used when building the bow+arrow
combination grid for ranged ranking. Arrows are still tested against these high-quality bows, which is sufficient to
determine a good relative ordering for arrows while dramatically reducing total simulations.
| Stat | Main Effect | When It Matters Most |
|---|---|---|
| Offense skill / weapon skill | Raises hit chance via to-hit. | Most noticeable when accuracy is not near cap. |
| Worn ATK / spell ATK / STR | Raises offense rating, improving damage roll quality. | Most noticeable when fighting higher mitigation targets. |
| Target avoidance | Reduces landed hit rate. | Large impact on total DPS when to-hit is modest. |
| Target mitigation (AC path) | Reduces landed hit size. | Large impact on max-hit frequency and average hit. |
Practical rule in this model: if your hit rate is already strong, more offense rating (ATK/STR) often gives better returns; if your hit rate is weak, improving to-hit inputs can be more valuable first.
This is a reporting transform and not itself the core swing resolution formula.
A mob does not attack on one steady rhythm. The server runs several independent timers at once, and everything below fires on its own schedule — which is why a mob's real damage output is much higher than "swings per round × damage per swing" would suggest. The tanking tab simulates each timer separately.
| Timer | How often | What happens |
|---|---|---|
| Main hand | The mob's attack delay, adjusted for haste or slow | A full main-hand round (see below) |
| Off hand | Same delay, only if the mob dual wields | A dual-wield check first (see below); on success, one off-hand swing plus its own double attack chance |
| Class attack | Every 8 seconds (10 for backstab), scaled by haste | Kick, bash, backstab or a monk special |
| Spell casting | Rechecked every 0.75–2 seconds | A nuke, DoT or stun from the mob's spell list |
Each time the main-hand timer fires, in this order:
In practice that means a level 60 mob double attacks about 62% of the time. Only if the double succeeds does a Warrior or Monk mob at level 60+ roll a further 13.5% for a triple attack.
Having the DualWield special ability doesn't mean the mob swings its off hand every time the off-hand timer fires. Each tick first rolls a separate dual-wield check, same shape as the double-attack check above but against a smaller divisor:
For a level 60+ mob with capped skill that's roughly 84% per tick, not 100% — the other ~16% of ticks, the off hand simply does nothing that round (no swing, no proc). If that check succeeds, the off hand swings once, then rolls its own double-attack chance (same formula as main hand) for a second off-hand swing. The report breaks these apart as "Off hand" and "Off-hand double attack" so the two hands' output is directly comparable — before this split, a dual-wielding mob's off hand could look like it was hitting far harder than the main hand, when in reality the main hand's own double/triple attacks were just being counted separately instead of folded together.
These come from the mob's special_abilities database field, shown on the Target Mob card once
you pick an NPC. A mob may only perform one special per round, checked in this order:
A Disable dual wield checkbox next to the spell-casting toggle lets you turn off a mob's off-hand round even when its database entry grants the DualWield ability — useful for testing what the fight looks like without it, or for mobs whose dual wield the sim can't otherwise detect.
A casting mob rechecks whether to cast every 0.75–2 seconds. Each check first rolls a flat detrimental chance — 25% for most classes, only 3% for hybrids (Paladin, Ranger, Shadow Knight, Bard, Beastlord) — before it even looks at which spell to cast. Fail that roll (or have nothing off cooldown) and the mob does nothing until the next recheck.
Passing that roll doesn't guarantee a cast: each spell type then rolls its own success chance — 40% for nukes, DoTs and roots, 50% for lifetaps and debuffs, 70% for slows (melee targets only), lower for mez/charm/snare (30%) and dispel (10%). This roll shifts by how many spells the mob knows: +10% with fewer than 4, −5% with 7–9, −10% with more than 9. This is why a mob with only one or two spells in its list looks far more trigger-happy than one with a dozen.
Raid-boss spells (priority 0 in the database) are the one exception — they skip both rolls entirely and fire the instant they're off cooldown, which is how dragon breath and other AE-on-a-timer abilities keep to their real cadence instead of getting diluted by the normal chance checks.
When a mob has a spell list, the report's Damage Taken section breaks down every spell it actually cast during the fight, by name, along with how many nukes/DoTs were fully or partially resisted — so you can see not just the total spell DPS but which specific spells are responsible for it.
Not every spell on a mob's list deals damage — dragon roars and similar fear effects (Lord Nagafen's Dragon Roar, for example) don't hit for anything, but they're still a real cast the mob makes on its own timer. The sim tracks these too, so they show up in the spell-cast breakdown and as a "Times feared" count under Special Attacks, even though they add nothing to your DTPS.
A mob's weapon proc (npc_spells.attack_proc) is rolled once per main-hand
round and once per off-hand round, regardless of whether the swing itself connects — separately from the
normal spell-casting checks above. The report names it directly (e.g. "Weapon procs (Stun
Breath)") rather than lumping it in as a generic damage source, since some procs — Zlandicar's
Stun Breath being the standout example, a 900-damage breath with a 23% proc chance and a 12-second
stun — are a bigger part of the fight than the mob's regular spell list. Any stun a proc carries adds to
the same "Times stunned" count as kicks and bashes, and the total stun time applied is
shown alongside it so a mob that can chain-stun you off a fast proc doesn't get lost in a single count.
Class attacks run the same hit, avoidance and damage chain as an ordinary swing, and they receive the mob's full flat damage bonus, so they are not trivial. Bash, kick and dragon punch also roll a stun:
A stun lasts exactly 2 seconds, during which you avoid nothing — no dodge, parry, riposte or block.
Enter a negative mob haste % to model a slow (−60 for a 60% slow). Mobs receive spell haste only, never item haste, and the mob's own slow mitigation gives part of a slow back:
Slow is the single most effective thing a group can do for a tank, and it now shows up directly in DTPS.
Each incoming swing is resolved in the order the server uses (NPC::Attack in attack.cpp):
the four skill checks come first, and only a swing that survives all of them rolls to hit.
divisor 25. Monks and beastlords only; it is a skill, not a shield property.divisor 50. Front only, melee only.divisor 55. Requires a weapon; negates the swing and counter-attacks.divisor 45.The first check that succeeds negates the swing. Each skill's chance is:
Percentage bonuses from AAs and buffs apply before the divisor, which makes them worth more than they appear.
Combat Agility and Lightning Reflexes multiply this value — they are not a flat reduction of the mob's hit chance. Warrior Evasive Discipline adds a further +50% while it is running.
The AC number your client displays is cosmetic. Combat uses two separate hidden values: avoidance (above) decides whether you are hit, and mitigation decides how hard. The report shows every component so you can see how yours was built.
| Component | Formula | Notes |
|---|---|---|
| Worn AC | floor(wornAC × 4/3) | Pure casters get the raw value. Capped at level × 6 + 25 below level 50. |
| Class bonus | Varies | Monk (scales off carried weight), Rogue (max 12), Beastlord (max 16). |
| Iksar natural armour | +10 / +level / +35 | Below level 10, levels 10–35, above 35. |
| Defense skill | floor(skill / 3) | Divided by 2 for pure casters. |
| Spell & buff AC | floor(spellAC / 4) | Divided by 3 for pure casters. |
| AGI | floor(AGI / 20) | Only above 70 AGI. |
Buff AC belongs in the Spell AC field, not Worn AC. The two are treated very differently — worn AC is multiplied by 4/3, buff AC is divided by 4 — so putting buffs in the wrong box overstates their contribution by roughly five times.
Everything above is summed, then clamped against a softcap that depends on class, level and era:
| Class | Softcap above level 50 |
|---|---|
| Warrior | 430 (405 in Kunark) |
| Paladin, Shadow Knight, Cleric, Bard | 403 |
| Ranger, Shaman | 375 |
| Everyone else, and all classes at level 50 or below | 350 |
Two things raise the cap rather than adding mitigation directly:
Above the cap, extra AC returns at a steep discount:
Returns are 1:12 in Luclin. In PoP they depend on class and level — a Warrior gets 1:5 at level 60 and 1:3 at 64+, while a Monk or Rogue gets 1:20. Before Luclin the cap is hard and excess AC is simply discarded.
Natural Durability gives 2/5/10%; Planar Durability adds 1.5/3/4.5% on top (Warrior, Paladin, Shadow Knight).
A successful riposte negates the swing and counter-attacks with your main hand. The counter-attack is a full attack in its own right, so the mob can dodge, parry, riposte, block or simply avoid it — the report shows how many of your counters actually connected.
A mob that is enraged, or flagged riposte-immune, cannot be riposted at all.
While Defensive Discipline is running your own damage output is cut by 55%, which applies to these counter-attacks too.
The simulation records every point of damage along with the time it landed. That timeline is what drives the Complete Heal chain analysis, and it is the reason the answer can be trusted.
The obvious approach is maxInterval = HP ÷ DTPS. It is also wrong, because tanks do not die to
average damage — they die to a flurry landing on top of a double attack and a nuke. Two mobs with identical
DTPS can need very different chains.
So instead of dividing and applying a guessed safety factor, the sim replays each recorded fight with a Complete Heal landing every T seconds and counts how often you actually die. Sweeping T produces a measured risk curve.
| Reported figure | What it means |
|---|---|
| Max safe CH interval | The longest gap between heals that still keeps death risk under 1%. |
| Death risk by interval | How quickly survival falls apart as the chain slips. |
| Burst damage in one cast | Median, 95th, 99th percentile and worst observed damage inside a single CH cast. |
| Clerics needed | ceil(cast time ÷ safe interval) — how many healers must rotate. |
| CH call threshold | The HP at which a cleric must begin casting, sized from 99th-percentile burst rather than the mean. |
| Death rate | Share of runs in which you died unhealed, with the spread of death times. |
The report also prints the naive HP ÷ DTPS figure for comparison. On a spiky mob it is
noticeably optimistic, and the gap between the two numbers is exactly the burst risk the average hides.
More runs and longer fights sharpen the estimate.
The report splits damage taken by where it came from — main hand, off hand, double attacks, flurries, kicks and bashes, spells, DoTs and procs — so you can tell whether you need more mitigation, a slow, or resistance gear.
Above the text report is a DTPS Over Time line chart. The headline DTPS figure is an average across every simulated run, which is exactly what makes burst damage invisible in a single number — so the chart draws one representative run's actual hit-by-hit timeline instead (fixed seed, the same run every time you re-run identical inputs), bucketed into a jagged instantaneous-DTPS line plus a smoother trailing 15-second average on top. A dashed line marks the averaged DTPS across all runs, so you can see how far this one run's peaks overshoot the mean; the amber dot marks the single largest hit taken in that run, with its source and timestamp on hover. With more than one run configured, a note reminds you this is one run's shape, not the average — re-running will land on a different (but reproducible) spike pattern depending on the seed.
AAs are era-gated. The era selector controls which AA sections are visible in the configurator. Only AAs available in the selected era (and earlier) are shown and applied to the sim.
| Era | AAs unlocked |
|---|---|
| Classic / Kunark / Velious | None — AA system not yet available. |
| Luclin | Archetype AAs (Combat Fury, Combat Stability, Combat Agility, Natural Durability, Spell Casting Fury, Mental Clarity, Channeling Focus, Spell Casting Reinforcement [not Wizard], Spell Casting Expertise, Spell Casting Deftness, Spell Casting Subtlety) and Class AAs (Ambidexterity, Flurry, Slay Undead, Archery Mastery, Double Riposte, Return Kick, Two Hand Bash [Pal/SHD], Soul Abrasion [SHD], Spell Casting Fury Mastery, Quick Damage, Spell Casting Mastery). |
| Planes of Power | All Luclin AAs plus: Planar Power, Planar Durability [War/Pal/SHD], Technique of Master Wu, Fury of the Ages, Ferocity, Knight's Advantage [Pal/SHD], Speed of the Knight [Pal/SHD], Raging Flurry [War], Tactical Mastery [War], Bestial Frenzy, Harmonious Attack, Flash of Steel, Innate Defense, Lightning Reflexes, Punishing Blade, Ingenuity [War/Monk/Rog]. |
In buildAASimOptions, each AA value is gated by both era availability and class eligibility. An AA that is out-of-era or not available to the selected class returns 0 regardless of the configurator setting.
Rank Weapons evaluates every eligible weapon in the database against your current character settings and produces a ranked list of the best mainhand, offhand, and combination options. It runs in four sequential phases, each feeding results into the next.
Before any simulations run, all weapons in the database are filtered down to a candidate pool. A weapon is included only if it passes every check:
Surviving weapons are split into a primary list (can equip in mainhand) and a secondary list (1H types that can go in the offhand). Every valid mainhand+offhand pair — including 2H solo, and 2H+offhand for Monk fistweave and 2H Weave classes — is pre-built as a combinations list. For bane-only targets (Seru, Emperor Ssraeshza), any combination where neither weapon has the required bane is removed immediately.
All of your current character settings (haste, STR, DEX, worn ATK, spell ATK, offense skill, target AC/level/resists, fight duration, all AAs, disc flags, backstab skill, etc.) are captured into a shared base configuration that every simulation in every phase will use.
Every weapon on the primary list is simulated alone in the mainhand with no offhand — a full fight simulation using the correct weapon skill cap, worn ATK contribution from that weapon, backstab modifier from that weapon (rogues), and bane damage applied if the target matches. The result is a raw DPS (and TPS) number for each weapon as a solo mainhand.
Simulations run in small chunks per animation frame so the page stays responsive. At the end of Phase 1, the top 10 mainhand weapons are saved for the phase breakdown panel.
Every weapon on the secondary list is simulated alone in the offhand with no mainhand. For Monk non-epic fistweave and 2H Weave (Warrior/Ranger/Beastlord), the offhand is instead paired with the best 2H weapon found in Phase 1 as a reference primary — this captures the correct combined swing contribution for those mechanics rather than attributing all output to the offhand alone.
At the end of Phase 2, the top 10 offhand weapons are saved. Then coarse combination scores are computed for every pair by simply adding the individual mainhand DPS + offhand DPS scores from the solo phases. This is an approximation (it ignores dual-wield interaction effects) but it is fast and sufficient to identify the top 500 candidates to carry forward.
The top 500 coarse-ranked combinations are each run through a full joint simulation — both weapons together in a single fight. This captures real dual-wield interaction: the actual dual-wield offhand proc rate, shared proc behavior, mainhand/offhand damage split as tracked by the sim, and correct worn ATK combining both weapons (capped at 250). Bane damage is applied per weapon independently. Any combination where neither weapon has bane on a bane-only target does 0 damage and scores 0.
Any combinations outside the top 500 keep their coarse additive estimate and are still shown in the final results — they just have lower confidence scores.
After Phase 3, all combinations are sorted and the top 40 are re-simulated N times (N = the Runs setting, default 20) with different random seeds. The results are averaged. This smooths out RNG variance from proc crits, dual-wield rolls, and backstab rolls, so the final ordering reflects expected performance rather than a single lucky run. The averaged DPS/TPS replaces the single-run Phase 3 score for each of those 40 entries.
The final results are sorted by total DPS (or TPS if that toggle is on) and displayed in two view modes:
A bar chart shows the top 40 combinations relative to #1 (100% = BIS). A phase breakdown panel (expandable) shows the top 10 from each phase so you can see how rankings evolved through the pipeline.
Weapon labels annotate worn ATK bonuses ([ATK+N]) and backstab modifiers ([BS+N%]) directly in the combination name so you can see at a glance which items are contributing stat bonuses beyond raw damage.
The Rotation tab answers a different question than Melee/Ranged/Tanking: given everything a caster can cast, what order maximizes damage — and what does that actually cost in mana? It simulates a real cast-by-cast timeline rather than just averaging a single spell's damage over its cast time.
The pool is pulled live from the spell database for the class and level set in the Character module — every spell that class can cast by that level which resolves to positive direct damage. DoTs, stuns, buffs and heals are left out; this planner is nukes only. You can uncheck any spell to exclude it from consideration.
Some nukes only work on a specific kind of target — Expulse Undead only affects undead, Hsagra's Wrath only affects certain raid giants, Porlos' Fury only affects Velious dragons, and so on. The planner detects this automatically from the spell's own target-type flag and checks it against the selected Target NPC's body type, so a restricted spell simply doesn't appear in the pool until a matching target is chosen — no need to remember which nukes are situational.
Rain spells (Tears of Prexus, Pogonip, and similar) don't hit once — they land in 3 (or
more, depending on the spell) separate waves about 2.5 seconds apart, starting the instant the cast
completes. Each wave is its own independent damage instance with its own resist roll, and the planner's
listed damage/pool-ranking figures already account for the full total across every wave. Because those
waves land after the cast bar ends and don't hold the GCD open, a rain works well as a "fire and forget" —
you're free to start the next nuke right away while its waves keep landing in the background, exactly the
short-burn-DoT role rains fill in practice. The spell pool tags these with (rain ×3), and both
the Cast Order and DPS Over Time charts (below) show where the waves actually land.
Damage-over-time spells are in the pool too now, alongside nukes and rains. A DoT ticks for
its listed damage every 6 seconds for its full duration once applied — but recasting the same DoT
while it's still active doesn't stack or extend it, it simply overwrites it, throwing away whatever ticks
were left. So the planner never recasts a DoT until its current application would have finished anyway
(modeled as its own "isn't available yet" gate, on top of its normal recast timer) — which is exactly what
makes a good rotation cycle between several DoTs (and nukes) instead of refreshing the same one
early. The spell pool tags these with (DoT ×N ticks), showing total damage across the whole
duration; the Cast Order and DPS Over Time charts show the individual ticks landing every 6s, same as rain
waves.
Click Recommend top 8 to auto-check the 8 highest-scoring spells in the current pool for whatever priority mode and resist settings are selected — the same per-spell score the simulator's greedy pick uses, just ranked once instead of scheduled. It doesn't try to reason about synergy between spells (e.g. pairing a rain with fast fillers); the event-driven simulator already finds good interleaving on its own once the right spells are in the pool, since a rain's low ongoing GCD "cost" is exactly what makes it score well without any special-casing.
A character can only have 8 spells memorized at once, so the rotation never draws from more than 8 distinct spells regardless of how large the pool is — the spell-pool checklist defaults to the top 8 by raw damage, and checking a 9th requires unchecking one first. The simulator also enforces this limit internally as a hard cap, independent of the checklist.
A server regen tick fires every 6 seconds (Client::CalcManaRegen, client_mods.cpp).
What it grants depends only on whether you're sitting at the instant it lands: 1 mana if standing,
2 if sitting, or up to 4 + floor(skill/15) — around 17 at max Meditate skill — if sitting with
trained Meditate skill (plus +1 at level 62 and another +1 at level 64, always). You cannot cast while
sitting, so catching the higher rate on a given tick means deliberately not being mid-cast when it lands —
the classic "sit for the tick" habit.
The Med tick handling radio group (Simulation card) picks which of three ways the sim models that timing decision, instead of just assuming a flat average rate:
Meditate skill (used by Sit for Med Ticks and Mounted) is assumed trained to the class/level cap
(5 × level + 5, capped at 200 — the same "assume skills are trained to cap"
convention used elsewhere in the sim). Regardless of mode, these base rates are computed automatically from
level and added on top of three more auto-detected sources — Mental Clarity (AA, +1 per
rank up to +3, aabonuses.ManaRegen), item Mana Regen (worn-effect spells like
"Flowing Thought I-X" found on real items — e.g. Circlet/Robe/Cape of Flowing Thought — via
item.worneffect, summed and capped at 15 total,
Client::CalcManaRegenCap/RuleI(Character, ItemManaRegenCap)), and
buff Mana Regen (any mana-regen buffs checked in the Buff Panel — Koadic's Endless
Intellect, Protection of the Glades/Cabbage, Spiritual Purity — using the standard buff stacking rule: the
highest value per stacking group is kept, and different groups add together) — read from the shared AA
Configurator, Import Inventory, and Buff Panel the same way Spell Casting Fury and focus effects already
are, no extra input needed. All three apply on every tick regardless of sit state, same as the level-62/64
bonus. Whatever's left in the Mana regen/tick field is for anything not auto-detected
(spell bonuses like Donal's BP, etc.) — enter those manually the same way you always did. The AA &
Gear card shows all of this — computed base rates, Mental Clarity/item/buff regen if present — once a
spell pool is loaded, and again live if you change the radio mode, move the slider, or change buffs.
In Sit for Med Ticks or Mounted mode, both the Cast Order timeline and the report make the outcome visible rather than leaving it implicit in the mana numbers. The timeline gets a thin marker row above the cast bars — a green dashed line/triangle at every tick that landed at the sitting/meditate rate (sat for it, or mounted), amber for one a cast ran through at only the standing rate — so you can see at a glance which ticks a rotation catches versus skips (hover a marker for the exact time and mana gained), with a small legend below the chart counting each color. The report adds a Med ticks (sat for / cast through) line with the same two counts out of the total ticks that landed during the fight.
The planner reuses the shared Alternate Advancement panel and Import Inventory feature above rather than duplicating those inputs — set them once and every tab, including this one, sees them.
Client::ApplyDurationFocus,
spell_effects.cpp) floors ticksRemaining × (100 + focusPct) ÷ 100 onto the buff
the instant it lands; the planner applies the identical formula to the spell's tick count before the fight
starts. A longer DoT is also correctly worth less-frequent recasting — the same "never recast a DoT before
its current application has fully worn off" rule described below automatically extends to the new, longer
duration, since it's driven by the same tick count this focus grows. This only ever applies to DoTs
(detrimental, multi-tick spells) — the real focus effect only touches buff-type spells server-side, so it
has nothing to do on an instant nuke or a rain, and every real item granting one of these foci already
restricts itself to detrimental spells with a minimum tick count for exactly that reason.((min(INT,255) + min(DEX,255)) / 2 + 32) / 10000, starting at level 12,
landing for a random +1-50% damage (not a fixed step like the AA). It only gets a chance to fire on a cast
where Spell Casting Fury's own roll didn't already crit — the two aren't independent/additive, matching
how the server resolves it (Spell Casting Fury is checked first; the wizard's innate crit is the
fallback). INT and WIS fields appear in the Character module above when this tab is active (DEX and level
are already there for every tab) — WIS isn't used by anything yet, but is there alongside INT since both
are the two casting stats. Every class other than Wizard has no innate crit chance at all. INT/DEX/WIS used
for this (and for spell specialization skill) include any active buffs checked in the Buff Panel on top of
the Character card's base+gear stat (e.g. Koadic's Endless Intellect's +25 INT counts), capped by Planar
Power same as the Character card's own Buffed column. The AA & Gear card shows this
as the bare formula rather than a computed percentage, since the Character card's INT/DEX may not be
finalized yet when that card renders — the Rotation Report shown after simulating always
has the real calculated chance, using whatever INT/DEX the run actually used.Both AA and focus effects are era-filtered — an AA rank beyond what the selected era allows is ignored, and a focus item's min-expansion tag (the same field BIS Advisor uses) is checked against the era selector the same way gear already is elsewhere in the sim. The AA & Gear card on this tab shows exactly what was detected after era filtering, so nothing is applied silently.
Crits are rolled per cast now, not blended into an averaged rate, so the report can show what actually happened: max hit (the single largest cast, headline stats), and — whenever a crit source is active — crits landed (count and %, split by AA vs. innate when both apply) and DPS from crits (the extra damage the crit chance itself contributed, isolating its value from the base damage under it). Each run of Simulate Rotation uses a fresh random seed — same convention as Melee/Tanking's runs — so crit outcomes (and therefore max hit/crit count/DPS) vary a little between clicks with identical inputs, matching real fight-to-fight variance instead of always returning the exact same number. Only the crit outcome is rolled per cast; resist and mana regen stay the deterministic expected-value model described above.
Because the rotation sim only replays one seeded run per click (unlike Melee/Tanking, which average many trials internally), "crits landed" is that single run's actual roll outcome, not a long-run average — a build with a 4% crit chance and 40 casts genuinely can land anywhere from 0 to several crits on any given run. The report also shows avg crits per run: the analytic expectation (each cast's own crit probability — SCF's chance, plus the wizard innate chance on casts SCF didn't already crit — summed across every cast), which is what "crits landed" converges to over many runs. The two numbers are shown side by side so the single realized roll isn't mistaken for the average.
The rest of the Luclin/PoP caster AA line — Mental Clarity, Channeling Focus, Spell Casting Reinforcement (not available to Wizard — that AA is only on the Cleric/Druid/Shaman/Necromancer/Magician/Enchanter caster line), the Spell Casting Expertise/Deftness/Subtlety archetype line, Innate Enlightenment, and the Magician-only Fury of Magic — are all selectable in the AA Configurator too, so casters see the same breadth of options melee/hybrid classes do. Their numeric effects aren't applied anywhere, though: unlike Spell Casting Fury, none of them grant their bonus through an actual spell record, so there's no data to derive an exact per-rank number from (interrupt/fizzle/aggro reduction, stat caps — several of these mechanics also aren't simulated here at all, e.g. there's no interrupt or fizzle mechanic). They're there for completeness and record-keeping, not because they move any computed number. Spell Casting Mastery is the one exception — see Spell Specialization below.
On the server, Spell Specialization and Spell Casting Mastery each grant two separate, additive bonuses: a mana-cost reduction and a fizzle-chance reduction. Specialization skill (e.g. Specialize Abjuration) always grants both, scaled by skill value. Spell Casting Mastery (ranks 1–3) adds a flat bonus on top of whatever specialization already provides — but only if specialization skill in that school is above 0 — rather than improving the odds specialization "kicks in": +2/+5/+10 to the fizzle-avoidance chance (capped at 98%), and +2.5%/5.0%/10.0% additional mana reduction, both stacked directly onto the specialization-derived numbers. The sim has no fizzle mechanic at all (see above), so only the mana-cost side is modeled here; the fizzle-reduction side of both Specialization and Mastery has no effect on any computed number.
The Rotation planner's AA & Gear card has a Spell Specialization
dropdown (Abjuration / Alteration / Conjuration / Divination / Evocation) that discounts the mana cost of
every spell in that school — matching the server's GetActSpellCost:
percent off = 1 + (specialization skill ÷ 20) + Spell Casting Mastery bonus (2.5% / 5% / 10% per rank)
Only spells whose school matches the chosen specialization get the discount; every other school is treated
as unspecialized (no reduction) — this server only allows one specialization skill to be
trained past 50 at a time, so raising a second one resets/caps the others. Rather than adding a raw skill-
value input, the planner assumes that one chosen school is trained to the class/level skill cap
(5 × level + 5, capped at 200) — the same "assume skills are trained to cap" convention
used everywhere else in the sim (see the weapon/combat/defensive skill caps above). The dropdown auto-picks
whichever school the checked spell pool leans on most (e.g. an all-Evocation Wizard nuke set auto-selects
Evocation) but can be overridden manually. Spell Casting Mastery (the AA, not to be
confused with the AA Configurator's placeholder tooltip on the melee tabs) stacks additively on top of the
skill-based discount, and only applies once the skill-based portion is active (i.e. specialization is
actually selected). The discount reduces the mana shown per cast and the totals in the report the same way
item mana-cost-reduction focus effects do, and both stack together (focus effect discount applied first,
specialization discount applied on top of that).
Also not modeled: the handful of AA-granted "spells" whose damage depends on the caster's own current HP or mana rather than a fixed amount (Wizard Lifeburn, Magician Mana Burn) — the planner doesn't track a caster HP/mana-drain-to-zero state, so these aren't in the spell pool. Item focus effects that affect beneficial-buff duration (e.g. "Extended Enhancement," which only targets goodEffect spells — AC/ haste/stat buffs, not DoTs) or non-damage utility (healing, resurrection, etc.) also aren't modeled, since the spell pool here is detrimental-only and they have nothing to contribute to a DPS rotation.
After a normal spell finishes casting, there is a 2.25 second lockout (the GCD) before the next normal spell can begin. This is on top of that spell's own recast timer, not instead of it. You may designate one spell in the pool as an "off-global filler" — it still takes its full cast time on the bar (only one spell can be mid-cast at any moment), but starting it is never blocked by the GCD lock, and casting it never imposes a GCD lock on what comes next. It is limited only by its own recast timer. This models the small set of spells that behave this way rather than a general assumption that every spell is off-global.
The GCD clicky after each spell checkbox (Spell Pool card) models a different trick: a global-cooldown-reset item that, when clicked right after a cast finishes, lets a new normal spell begin immediately instead of waiting out the 2.25s lockout. The click itself still takes a moment — the Click delay (ms) field below the checkbox (default 200ms) — so the sim starts the next spell that many milliseconds after the previous one ends, rather than 2.25s later. This only works for a next spell whose own cast time is at least 2.25s; a faster spell fails to cast this way in practice, so the sim doesn't offer it the shortcut and it simply waits out the normal GCD, same as with the checkbox off. It's independent of, and stacks with, the off-global filler above — that spell already ignores the GCD entirely and is unaffected either way. When on, the report adds a GCD-clicky casts line counting how many of the run's casts actually used the click to skip the wait.
At every point the caster is free to start a cast, the sim picks the highest-scoring spell that is currently castable (off recast, and — for normal spells — off the GCD lock). Three scoring modes are available:
| Mode | Maximizes | Use when |
|---|---|---|
| DPS | damage ÷ (cast time + GCD, or just cast time for the off-global spell) | Mana isn't a concern — burn phases, short fights. |
| Sustain | damage ÷ mana | The fight will outlast your mana pool. |
| Balanced | an even 50/50 blend of the two, each normalized against the pool's best-in-slot | You want a reasonable rotation without picking one extreme. |
Once the caster hits the mana wall — the same "ran out of mana" moment described below — the scoring switches to pure damage-per-mana for the rest of the fight, no matter which mode was selected. Spending the last of the pool on the DPS mode's ideal (but mana-hungry) nuke would just run the caster dry faster; efficiency is what stretches output once the pool can't keep up.
Switch Rotation Mode to Custom order to bypass the greedy optimizer entirely and try your own priority list instead. Add checked spells to an ordered list (reorder or remove them freely); at every decision point the sim casts the first entry in that list that's currently castable — off recast, affordable, off the GCD unless it's your off-global pick — skipping over anything on cooldown and re-checking from the top next time, the same way a player working down a fixed cast-priority list would. It isn't "consumed": the same list is re-evaluated at every decision point, so it naturally forms a repeating rotation on its own — a good way to answer "is the optimizer's pick actually better than my gut instinct?" by comparing the two DPS numbers directly.
With a fixed 8-spell pool and a greedy pick, the rotation settles into a repeating cycle once recast timers reach steady state — the Rotation line in the report spells it out explicitly (e.g. "Fire Bolt → Ice Comet → Fire Bolt ...") and separately shows the mana-starved cycle if the two differ.
After a run, the Cast Order chart shows the same timeline visually — one bar per cast, labeled with the spell name where it fits, positioned and sized by actual cast time, colored by spell, on a GCD-chain lane and (if one is set) a separate off-global lane so you can see the two interleave. Hover a bar for its exact time, damage and mana cost; the legend below shows each spell's share of total damage. It renders up to the first 5 minutes of the fight (long fights beyond that repeat the same pattern anyway).
The DPS Over Time chart covers the whole fight instead, bucketing damage into windows (sized to the fight's length) and plotting the resulting DPS as a line, with a dashed reference line at the overall average. This is where the mana wall actually shows up: a dashed amber marker at the moment mana first ran out, and — since scoring switches to damage-per-mana casting from that point on (see above) — a visible step down to a new, lower steady-state DPS for the rest of the fight, rather than that transition being hidden inside a single averaged number.
A second line tracks a trailing 30-second average DPS at every point in the fight — smoothing out the bucket-to-bucket jitter a single big rain wave or DoT tick can cause, so the underlying trend (and exactly how sharp the mana-wall drop is) reads clearly even on a noisy bucketed line.
A third line tracks TPS (threat per second). Every spell in a rotation comes from the
caster's own class spellbook, so EQMacEmu's CanClassCastSpell hate cap (the same 400 cap that
clamps weapon-proc non-damage hate on the Melee tab — see the Threat section above) never applies here:
each discrete damage instance generates hate, uncapped — a rain wave or DoT tick is its
own hate-generating instance, credited its own uncapped share, not the cast's full total in one lump.
Unlike DPS, that per-instance hate is not the damage the cast actually deals — it's the
spell's plain listed/base damage, exactly as it would be with no gear, no resist, and no crit involved.
This matches how EQMacEmu itself computes it: a detrimental cast's threat comes from
Mob::CheckAggroAmount reading the spell's raw formula value directly off the spell data, with
no resist or crit rolled in at all — that's true even on a full resist, which still
generates the same threat as a clean hit (Mob::ResistSpell calls the same
CheckAggroAmount). The actual, resist-and-crit-adjusted damage is tracked separately
server-side purely for kill-credit/XP bookkeeping and never feeds the hate the mob's AI uses to pick a
target. So on the Rotation tab: a resisted cast doesn't lose any threat, and a crit doesn't gain any —
only DPS moves. TPS therefore reads a bit lower than a naive "TPS = DPS" assumption would suggest whenever
crits or gear/focus damage bonuses are in play, since those inflate damage without inflating threat.
Two modes decide how much of a nuke's listed damage actually lands:
A partial resist is treated as landing for 25% of listed damage; a full resist lands for 0%.
The Target Mob card's level and MR/FR/CR/PR/DR fields are shared with the Melee tab's "Target resistances" and the Ranged tab — searching for an NPC on any of those tabs fills in all three at once, and so does clearing it or leaving no target selected (which falls back to a generic level-matched mob with MR/FR/CR/PR/DR of 35 each, same as Melee's own default). Whatever those fields show on the Rotation tab is exactly what the resist check above uses — there's no separate, independently-stale copy.
Set a max mana pool and a regen-per-tick figure (include buffs and Meditation — the sim just takes a flat number per 6-second tick). With unlimited mana (max mana = 0) the pool never gates spell choice. Otherwise, the report notes the exact moment the caster first had a spell ready to go (off recast and off the GCD) but couldn't afford it — the simulation keeps running after that point on whatever regen allows, rather than stopping.
Max mana auto-fills whenever gear is imported (Client::CalcBaseMana/
CalcMaxMana, client_mods.cpp): INT casters (Wizard/Magician/Necromancer/
Enchanter/Shadowknight/Bard) scale off final INT, WIS casters (Cleric/Druid/Shaman/Paladin/Ranger/
Beastlord) off final WIS — base race stat plus creation points plus gear, plus any INT/WIS bonus from
buffs checked in the Buff Panel (e.g. Koadic's Endless Intellect) — run through the server's base-mana
curve for the caster's level, then a flat mana-pool bonus is added on top from gear's "Mana" stat and
buff mana-pool effects (also Koadic's, and Voice of Quellious). Paladins, Rangers, and Beastlords get no
mana pool at all below level 9, matching the server. Non-casters (Warrior, Monk, Rogue) are left alone.
The field stays a normal editable number afterward — type over it and that value is what the simulation
uses, until the next gear import re-fills it.
Magician, Necromancer, Shaman, Shadowknight, and Beastlord all summon a full-time companion pet that fights alongside the player; Wizard (Flaming Sword of Xuzl) and Cleric (Unswerving Hammer of Faith) each get one situational combat-pet spell instead. The Pet panel (Melee and Spells tabs, shown for all seven of these classes, positioned below the weapon cards on Melee and at the top of the Spells tab) simulates the pet's own melee output against the same target and adds it to whichever tab's report was just run, so you can see Pet DPS and Combined DPS (Player + Pet) next to your own number.
Wizard and Cleric's pets aren't kept up full-time in real play — the sim assumes the spell is cast at the very start of the fight and the pet stays up for the whole duration, the same as every other pet here, rather than modeling its actual in-game expiration (which isn't exposed anywhere in the spell data).
There is no dedicated "pet stats" table in the client data — pet-summon spells are ordinary spells that
happen to carry a summon effect, and the specific pet they create is looked up the same way the server does
it: the spell's teleport_zone field is repurposed to hold the exact name of the pet's NPC
template, which is then resolved against the same NPC database (npc_types) the Target NPC
search elsewhere in the sim uses. That gives the pet's real level, HP, min/max melee damage, AC, ATK, DEX,
and attack delay — not an estimate. Shadowknight needs no special handling: SK shares the same skeleton-pet
spell line as Necromancer in this era, just unlocked at a much higher level, so it falls out of the same
class/level scan as every other class here.
The Pet summon spell dropdown lists every pet-summon spell the selected class can cast by the selected level (a Magician has up to four parallel entries per tier — Earth/Water/Fire/Air — since they're separate spells with identical stats). Pick the one you'd actually memorize.
A summoned pet is coded server-side as an NPC, not a player — it rolls flat min/max melee damage the same way a mob does, not the weapon-rating formula the Melee tab uses for the player's own swings. So the pet sim reuses the same NPC attack engine the Tanking sim uses to simulate a mob attacking the player, just aimed the other way: the pet is the attacker, and the target mob's AC/level/avoidance (from the Pet Target AC field, defaulting from a selected Target NPC) stand in for what it's swinging at.
This is a one-directional simulation: it only measures the pet's outgoing damage, not damage the pet takes.
The pet is assumed to survive the whole fight — there's no pet death, avoidance-of-incoming-damage, or HP
tracking modeled. Flurry, rampage, and class-attacks are also not modeled, since real summoned pets have no
attack AI script (every pet's npc_types.npc_spells_id is 0).
In-game, a summoned pet only dual-wields if it's physically handed a second weapon — pets are explicitly
excluded from the "innate ability" dual-wield path that some regular mobs have (Mob::IsDualWielding
skips that check specifically for summoned client pets). The Always Dual Wield toggle
models the effect of handing the pet a weapon: checking it unlocks a second attack round every swing cycle,
using the pet's own innate damage (not a separate weapon item's stats) — matching the in-game payoff of
roughly doubling the pet's swing count without needing a weapon-item picker for a pet's off hand.
Pets normally have no weapon item and therefore nothing to proc from — with one documented exception. Beastlords can cast a buff directly on their pet (Spirit of Lightning, Spirit of the Blizzard, Spirit of Inferno, ...) that grants it a chance to proc a direct-damage spell on its own melee swings, via the same underlying mechanism as a weapon item proc. If the selected class is Beastlord, the Pet proc buff (optional) dropdown lists every one of these your level qualifies for; picking one adds a per-swing proc chance to the pet's simulated damage and shows it as its own line under Pet DPS.
The proc-chance formula is the exact same one this sim already uses for a player's own weapon item procs
(Mob::GetProcChance — a function of attack delay and DEX), scaled by the buff's own rate
multiplier. That multiplier is hardcoded server-side to 375% of baseline for exactly these ten spells
(Pet::LoadPetPets) rather than trusting each spell's own listed rate field, which the sim
matches rather than reading the (unused, misleading) data value directly.
When the pet sim runs, the report gets its own Pet Damage section, placed right after
Procs & Specials and before Final Totals — same === Section === / padded-column layout as
every other section of the report (Weapon Overview, Procs & Specials, ...), reading as one more section
of the fight rather than a trailing footnote. It breaks the pet's damage down by main hand and off hand
(swings, hits, accuracy, damage, DPS each), the proc buff's own contribution if one is selected, and closes
with total pet damage, Pet DPS, and Combined DPS (Player + Pet).
The Pet panel's own DPS is always shown in the report regardless of anything else — that's informational. Separately, the Melee tab's header Total DPS ticker and Session History table (the numbers you actually use to compare one weapon against another) default to player-only, so a pet's flat per-run baseline doesn't uniformly inflate every entry and quietly compress the differences you're trying to see between weapons. Checking Include pet DPS in Total DPS in the Pet panel folds the pet's DPS into both of those numbers instead, for when you want the group's real combined output rather than an apples-to-apples weapon comparison. The report's own Pet Damage section always states which mode was active for that run, and any Session History row logged with the pet folded in gets a small 🐾 next to its DPS value so you can tell those runs apart from player-only ones at a glance.
Every run of the Rotation Planner (Spells tab) now logs a row into the same shared Session History table the Melee and Ranged tabs already use, so you can compare a spell rotation's DPS against your melee/ranged runs side by side instead of losing it as soon as you run another sim. The row's "weapon" column instead shows the spell pool used for that run (spell count plus the first few spell names), the off-global spell if one was selected, the caster's level, target name (if a target was chosen via search), the resulting DPS, and which era and UI mode (Easy/Advanced) were active — the same fields every other Session History row carries, just populated from a spell rotation instead of a weapon loadout.
Some items list a recommended level rather than a hard required level. A required-level item simply can't be equipped below that level; a recommended-level item can be worn early, but all of its stats — AC, HP, Mana, STR/STA/DEX/AGI/WIS/INT/CHA, resists, and (for weapons) damage, elemental damage, and bane damage — are scaled down in proportion to how far below the recommended level the wearer is. At or above the recommended level, the item gives its full, unscaled stats.
The sim reproduces the server's own formula exactly (Client::CalcRecommendedLevelBonus): each
scaled stat is floor(level × 10000 ÷ recLevel) × baseStat, rounded to the nearest whole number
(half-away-from-zero), then divided back down by 10000. Because the server does this with integer math, the
sim replicates the same truncation behavior rather than using a plain floating-point ratio, so the numbers
it shows match what you'd see in-game rather than a rounded approximation.
This applies automatically whenever you equip an item (via item search or by importing an inventory) whose recommended level is above your current character level — the gear summary's AC/stat totals reflect the scaled-down values, and they re-scale live if you change the level field afterward. Weapon damage scaling applies the same way when a weapon with a recommended level is selected via item search.
The Ranged tab's Spell Casting (between shots) card lets a Ranger weave nukes in between arrows. It reuses the exact same casting engine as the Rotation Planner (Spells tab) — global cooldown, per-spell recast timers, mana pool and regen, resist checks, Spell Casting Fury and item focus effects all work identically — rather than a separate, simplified model.
The card sits above Target & Fight on the Ranged tab, right below the weapon/arrow cards. Only three spells are offered, matching the nukes a Ranger can actually fit into a quiver rotation on Quarm: Calefaction, Call of Flame, and Firestrike (Drones of Doom, a damage-over-time spell, was dropped from the option list — its long cast time and recast/duration gating make it an impractical pick for a Ranger weaving casts between shots in practice). Check on whichever you have memorized; Refresh spell list looks each one up against your current level (and selected target, for the handful of nukes that are body-type restricted) and shows its damage, mana cost, and level requirement, or disables the checkbox with a "requires level N" note if you're not high enough level to have it yet. This refresh also happens automatically whenever you change class or level in the Character module above — no need to remember to click it after leveling up or switching classes, though switching to a class other than Ranger will show every spell as unavailable, since these are Ranger-only.
Checked spells are cast in whatever order the list is arranged in — reorder the rows with the ↑/↓ buttons next to each one, defaulting to Calefaction, Call of Flame, Firestrike. This is the same "first castable entry in the list" logic as the Rotation Planner's Custom order mode: at every decision point the sim casts the highest (topmost) checked spell that's off recast and affordable, skipping anything on cooldown, until the mana pool (set below the checkboxes, 0 = unlimited) runs dry.
The spell-casting timeline runs independently of the archery timeline above it — ranged shots in this sim are never delayed by a spell's cast time or its GCD, and vice versa. This is the same simplification gSim already uses to combine a player's melee/ranged output with a summoned pet's or a weapon proc's: two separate timelines simulated in parallel and their totals summed, rather than one fully merged event queue. The report adds a Spell Casting section (identical in format to a Rotation Planner report), positioned just above Final Totals — Executive Summary, Weapon Overview, and every other section above it are still exactly the archery sim's own numbers, isolated and unchanged, so the physical ranged contribution is never obscured by the spellcasting timeline. Final Totals is where the two are actually combined: it shows Ranged (physical) damage and Spell damage (and their TPS equivalents) as their own clearly-labeled lines, immediately followed by the headline Combined total damage, Combined DPS, and Combined TPS — so the ranged-only figure stays visible right next to the number it feeds into, rather than requiring a second pass through Executive Summary to find it.
Bard songs scale up based on the bard's skill with the instrument used to play them, and separately on how good that specific instrument item is. In-game there are five instrument skills — Singing, Stringed, Brass, Percussion, and Wind — and each bard song is tied to exactly one of them (it's determined by the song itself, not a choice you make). A better instrument (or higher instrument-mastery AAs) raises the effective modifier applied to that song's numbers, on top of the level-based scaling every song already has.
The Buffs card includes a Bard Instrument Mods row with one slider per instrument type. Each slider has five stops: 1.0× (no instrument bonus), 1.8× (an epic-tier instrument — the default for all five, matching what a well-equipped raid bard usually carries), and two more stops for progressively better instruments/AA combinations, topping out at 3.6× on Luclin and earlier or 4.0× once the era is set to Planes of Power (which raised the achievable instrument mastery ceiling). Moving a slider only affects bard songs that use that instrument type — for example, raising the Brass slider changes songs like Warsong of Zek but has no effect on a Singing-type song like Rizlona's Call of Flame.
Internally, each bard song buff is defined by its raw spell slot values (base, per-level scaling, and cap) exactly as they exist in the spell data, and the instrument modifier is applied to that computed value after level-scaling and before the slot's hard cap is re-applied — so a slider change never lets a song exceed its actual in-game ceiling. This is applied automatically whenever you toggle a bard song on in the Buffs panel; there's nothing else to configure.
Haste is exempt. Not every number on a bard song is instrument-modded — the server's own
moddable-effect list (IsInstrumentModdableSpellEffect) covers stats, ATK, AC, resists, and a
few other effect types, but leaves both haste effects (v1 and the bard-only v3) out entirely. So on a song
like McVaxius' Rousing Rondo or Warsong of Zek, the instrument sliders scale the STR/ATK numbers but never
touch the haste percentage — that part of the song is fixed regardless of instrument quality.
Level scaling always assumes the bard singing the song is at the current era's level cap (60, or 65 in Planes of Power) — not the simulated character's own level. In practice you're buffed by whichever bard is in your group or raid, and that bard's level has nothing to do with the level of the character being simmed, so gSim always computes bard songs at the highest level a bard could realistically be in that era.
When you check multiple buffs at once (say, several ATK-boosting buffs), the sim mirrors EverQuest's rule that some buffs of "the same kind" don't fully add together — one wins and the other is wasted, just like in-game. Buffs are grouped, and within a group only the strongest one counts toward your stats; buffs in different groups always add on top of each other normally.
Two exceptions: worn/spell haste (the most common haste buffs) always uses the single best value active, regardless of grouping, and bard-only haste is a separate pool on top of that — this matches how haste actually works in-game (it doesn't stack additively even across different spell lines).
v1.82 fix — Dance of the Blade. The bard epic 1.0 proc (Singing Short Sword) was previously grouped with the shaman Avatar line (Avatar / Ancient: Feral Avatar / Ferine Avatar), so having both up only counted the stronger one. In-game, EverQuest actually checks buffs against each other very literally, effect by effect — two buffs only compete if they're carrying the exact same kind of bonus in the exact same "slot" of their spell data. Dance of the Blade's ATK and STR bonuses live in different slots than the Avatar line's, so in real EQ they never compete — both fully apply. The sim now reflects that: Dance of the Blade stacks with any of the Avatar-line buffs instead of being suppressed by them. (Also fixed: its third bonus was mislabeled as +30 AC when it's actually +30 STR.)
Reference implementation: EQMacEmu attack.cpp. Return to main simulator.