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_hit
resolves 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_00
andthac0_32
fromclass_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 stat
using 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_hit
executes 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_hit
with 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_AC
modifiers 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_ARMOR
pieces contribute.【F:src/handler.c†L1910-L1954】 - Dexterity bonus:
GET_AC
adds 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】