Combat Round Resolution

One melee swing end to end: swing budget, one attack roll against every affordable defense, three-channel damage

Combat Round Resolution One melee swing end to end: swing budget, one attack roll against every affordable defense, three-channel damage engaged with a target Combat is driven by the world tick, not by a command. Every 4 second round, handleCombatRound runs once per engaged pair. how many swings for this weapon? 1 to 4, after stamina and carried weight calcSwingCount: dexterity, weapon speed and skill set the base, then a stamina multiplier and an over-capacity weight penalty of up to 50 percent scale it down. Hard cap of 4 per weapon. resolve this swing at attack score S calcAttackScore builds S from dexterity, combat skill, a dual-wield penalty, stamina, prone position, darkness and flight. It never reads a mitigation field. Armor changes how much a landed hit hurts, never whether it lands. roll the swing ONCE, spread = StdDevFor(S) runBestOfAllDefense opens with atkStdDev := dice.StdDevFor(atkScore) and attackRoll := dice.Roll(atkScore, atkStdDev). This is the only attack roll in the whole exchange, no matter how many defenses follow. one value A, and the spread that made it Both halves are kept. A is the number every defense must beat, and the attacker's spread becomes the spread every defense rolls with, so the two sides of the contest share one shape of randomness. which defenses do you have? dodge, parry, block Dodge scores dexterity plus unarmed-combat skill. Parry scores dexterity plus weapon-combat skill plus the best equipped weapon's ParryRating. Block scores the mean of strength and dexterity plus weapon-combat skill plus the best shield's BlockRating. Those ratings are ItemSpec fields entirely separate from physical_mitigation. skipped: cannot pay this one's stamina Each defense has a stamina cost. If the defender's current stamina is below it the loop simply continues, so that defense is never rolled at all. Low stamina never lowers a defense's score. It removes the defense from the contest entirely, which costs the defender far more. roll dodge vs A, attacker's spread roll parry vs A, attacker's spread Every surviving defense gets its own independent dice.Roll(defenseScore, atkStdDev). They compete with each other over the same attack value rather than each facing a fresh swing, which is what stops a defender with three defenses from getting three separate chances at three different attacks. roll block vs A, attacker's spread a margin each: defenseRoll - A One number per defense. Positive means that defense beat the swing, negative means it lost and by how much. Position, darkness, rally, grapple and mutation modifiers have already been folded into each defense score before this point. widest margin picks the defense, win or lose; only that defense pays stamina The largest margin is kept even when every margin is negative, so the closest failure is the one that gets narrated. Stamina is deducted once, for the winning defense only, after all the rolls are done. MinDefenseChance: save anyway If the attack won the margin, resolveDefenseOutcome still gives the defender a flat MinDefenseChance roll, default 0.15. An outclassed defender is never a guaranteed target. MinAttackHitChance: land anyway The mirror image. If a defense won the margin, a flat MinAttackHitChance roll can still turn it into a hit. The two floors are symmetric, checked after the margin, and either can overturn the contest. hit, miss, crit or fumble resolveDefenseOutcome orders the result: double fumble first, then fumbles, then crit versus crit, then a one-sided crit, then the normal margin. Each floor sits inside one branch of that last step, so only ever one can fire. stat x skill x weapon x channel scale x global CalcRawDamage is one formula for all three channels: physical uses strength, magical uses willpower, conviction uses charisma. A configured per-channel scale and a global multiplier finish it. subtract mitigation, capped ApplyMitigation reduces raw damage by the defender's summed equipment percentage for that channel, clamped to the channel cap (default 75 percent). This is the only place armor is consulted in the entire round. A critical hit bypasses mitigation entirely: armor does not reduce crit damage at all. roll variance around the mean damage for this swing dice.RollStat derives its spread from the mean using the global RollSpread knob, so the shape of every roll in the engine moves with one number. turn the number into a phrase GetDamageDescription converts damage to a share of the target's maximum health and returns light wounds, serious wounds, critical injuries and so on. "causing serious wounds" The raw integer never leaves the engine. Attacker, defender and every spectator in the room read the same descriptive band. Swing budget One attack roll Every affordable defense, rolled separately Two floors, either direction Three-channel damage Player-facing text Attacker · player or mob · Sequence participant Attacker player or mob Round handler · handleCombatRound · Sequence participant Round handler handleCombatRound Swing budget · combat.calcSwingCount · Sequence participant Swing budget combat.calcSwingCount Dice · dice.Roll · StdDevFor · Sequence participant Dice dice.Roll · StdDevFor Defender · the target character · Sequence participant Defender the target character Opposed roll · runBestOfAllDefense · Sequence participant Opposed roll runBestOfAllDefense Damage · CalcRawDamage · Sequence participant Damage CalcRawDamage Room output · AttackResult messages · Sequence participant Room output AttackResult messages Legend request return security async trace default message

Swing budget

  • • Dexterity, weapon speed and combat skill set the base swing count
  • • Low stamina and carrying too much weight both scale it down
  • • Hard cap of 4 swings per weapon, minimum of 1

The opposed roll

  • • The attack score is dexterity plus combat skill, with no mitigation term
  • • The swing is rolled once, and its spread is reused by every defense roll
  • • Dodge, parry and block each roll separately and each produce a margin
  • • The widest margin wins, even when every margin is a loss
  • • Only the winning defense pays stamina, and it pays it after the rolls

What drops out and what overturns

  • • A defense the defender cannot pay for is skipped before it is ever rolled
  • • MinDefenseChance, default 0.15, can save a defender the swing already beat
  • • MinAttackHitChance runs the other way and lands a swing a defense stopped
  • • Weapon ParryRating and shield BlockRating feed defense, not mitigation

Damage and text

  • • One raw formula serves physical, magical and conviction damage
  • • Mitigation is a percentage with a per-channel cap, default 75 percent
  • • The final number is converted to a phrase before any player sees it

Traced from source

  • • github.com/pruuk/DOGMud at 80ffa24f9dca3010256241cd5418e25da47ebea7
  • • internal/combat/combat_helpers.go, damage_pipeline.go, combat.go
  • • internal/characters/combat.go, internal/hooks/NewRound_DoCombat_unified.go