Combat Mechanics Primer
This guide distills the melee and spell resolution rules straight from src/fight.c, src/handler.c,
src/magic.c, and src/merc.h.
Class touchstones
Every player class declares a prime attribute and THAC0 progression in class_table. The prime stat feeds the new
damage-cap formula while the THAC0 entries drive hit-roll interpolation.
| Class | Prime stat | THAC0 (level 0 / 32) |
|---|---|---|
| Mage | Intelligence | 20 → 6 |
| Cleric | Wisdom | 20 → 2 |
| Thief | Dexterity | 20 → −4 |
| Warrior | Strength | 20 → −10 |
Source: class_table definition.【F:src/const.c†L640-L659】
Attack roll flow
- Setup:
one_hitresolves the weapon type, skill, and damage type. Undefined attacks adopt the wielder’s weapon damage type or the character’s innatedam_type.【F:src/fight.c†L603-L676】 - THAC0: NPCs pick baseline values by role flag while PCs pull
thac0_00andthac0_32fromclass_table. Interpolation by level plus hitroll, skill, and backstab adjustments produces the final to-hit target.【F:src/fight.c†L695-L720】 - Armor check: The defender’s armor entry for the damage type is divided by ten. Extreme negatives suffer
diminishing returns via
(ac + 15) / 5 - 15. Visibility and posture then modify the effective AC before a 0–19 d20 roll determines success.【F:src/fight.c†L722-L763】 - Defences: On weapon hits the game checks parry, shield block, then dodge in that order. Each routine scales from either NPC level caps or the defender’s trained skill values.【F:src/fight.c†L1080-L1091】【F:src/fight.c†L1813-L1993】
Offensive scaling
- Base damage: Weapon attacks roll the weapon’s dice and multiply by skill%; fighting without a shield
grants a 5% bonus. Unarmed PCs use a level-scaled range. Enhanced damage, position-based multipliers, backstab/critical
bonuses, and damroll all stack before the result is handed to
damage(). Off-hand swings are reduced to 60% power. 【F:src/fight.c†L769-L867】 - Damage cap:
calculate_damage_cap()clamps each damage packet to150 + 6 × prime statusing permanent attributes. Divine strikes replace the capped value with one third of the victim’s current hit points after immunity checks.【F:src/fight.c†L950-L1113】 - Multi-hit loop:
multi_hitexecutes the primary swing, optional haste extra, second-attack (skill/2), third-attack (skill/4), and dual-wield dagger proc (skill/2) for player characters. NPCs use the same structure insidemob_hitwith OFF_FAST and OFF_AREA_ATTACK modifiers.【F:src/fight.c†L404-L520】
Armor and mitigation
- Character reset: When equipment is re-evaluated, all four armor slots reset to 100, hitroll/damroll and
saving throw bonuses clear, and each worn item is re-applied.
APPLY_ACmodifiers adjust every damage type simultaneously.【F:src/handler.c†L547-L624】 - Slot multipliers:
apply_ac()scales armor values by wear location: body ×3, head/legs ×2, cloaks ×2, and most accessories ×1. OnlyITEM_ARMORpieces contribute.【F:src/handler.c†L1910-L1954】 - Dexterity bonus:
GET_ACadds the defender’s dexterity defensive adjustment whenever they are awake. Hitroll and damroll macros likewise fold in strength-based bonuses.【F:src/merc.h†L2489-L2496】 - Resists and vulnerabilities: After defensive maneuvers the game applies immunity, resistance, or
vulnerability adjustments in
damage(), shaving or increasing the post-cap value before it is subtracted from hit points.【F:src/fight.c†L1094-L1113】
Saving throws
saves_spell() evaluates spell saves as 50 + (victim level − spell level) × 5 − 2 × saving_throw,
with berserkers gaining a level/2 bonus. Immunity short-circuits the check while resistance/vulnerability shift the target by
±2. The final save chance is clamped to 5–95% before rolling number_percent().【F:src/magic.c†L203-L220】