Pain chance
The odds that a hit interrupts an enemy with a flinch.
On this page
A pain chance sets how often a hit makes an enemy flinch. Doom rolls once for each hit a monster survives, against a chance set for its type, so a zombieman flinches far more often than a Cyberdemon1. The flinch also lets the monster’s next ranged-attack check pass at once if it can see its target1. In the four Quake II monsters read, a hit plays a pain animation only once a 3-second cooldown has run out, and the tank adds a roll for light hits2.
- Per-type rollDoom (1993). Each hit a monster survives flinches it with a chance out of 256, from the zombieman's 200 to the Cyberdemon's 20.1
- CooldownQuake II (1997). In the four monsters read, a 3-second cooldown rather than a roll, 5 seconds with no pain animations on Nightmare; the tank adds a roll for light hits.2
Games
| Game | Year | How | Note |
|---|---|---|---|
| Doom | 1993 | Per-type roll | each hit a monster survives flinches it with a chance out of 256, from the zombieman's 200 to the Cyberdemon's 20 |
| Quake II | 1997 | Cooldown | in the four monsters read, a 3-second cooldown rather than a roll, 5 seconds with no pain animations on Nightmare; the tank adds a roll for light hits |
Every claim about this mechanic, by game (2)
References
- ^ abcA Doom monster that survives a hit enters its pain state when
P_Random()is below its pain chance, out of 256, unless it is a lost soul in mid-charge; the table gives the zombieman and imp 200, the demon 180, the shotgun guy 170, the cacodemon 128, the Baron of Hell 50, the Spider Mastermind 40, and the Cyberdemon 20, and the lost soul's 256 always succeeds. The pain setsMF_JUSTHIT, so the monster's next missile-range check passes at once if it can see its target.Codeid-Software/DOOMrevisedHow we know
887 target->health -= damage; 888 if (target->health <= 0) ⋮ 894 if ( (P_Random () < target->info->painchance) 895 && !(target->flags&MF_SKULLFLY) ) ⋮ 897 target->flags |= MF_JUSTHIT; // fight back! ⋮ 899 P_SetMobjState (target, target->info->painstate);
linuxdoom-1.10/p_inter.c lines 887–900 at a77dfb9 · upstream ↗ 197boolean P_CheckMissileRange (mobj_t* actor) ⋮ 201 if (! P_CheckSight (actor, actor->target) ) 202 return false; ⋮ 204 if ( actor->flags & MF_JUSTHIT ) ⋮ 206 // the target just hit the enemy, 207 // so fight back! 208 actor->flags &= ~MF_JUSTHIT; 209 return true;
linuxdoom-1.10/p_enemy.c lines 197–209 at a77dfb9 · upstream ↗ 1134 { // MT_POSSESSED ⋮ 1143 200, // painchance
linuxdoom-1.10/info.c lines 1134–1143 at a77dfb9 · upstream ↗ 1160 { // MT_SHOTGUY ⋮ 1169 170, // painchance
linuxdoom-1.10/info.c lines 1160–1169 at a77dfb9 · upstream ↗ 1394 { // MT_TROOP ⋮ 1403 200, // painchance
linuxdoom-1.10/info.c lines 1394–1403 at a77dfb9 · upstream ↗ 1420 { // MT_SERGEANT ⋮ 1429 180, // painchance
linuxdoom-1.10/info.c lines 1420–1429 at a77dfb9 · upstream ↗ 1472 { // MT_HEAD ⋮ 1481 128, // painchance
linuxdoom-1.10/info.c lines 1472–1481 at a77dfb9 · upstream ↗ 1498 { // MT_BRUISER ⋮ 1507 50, // painchance
linuxdoom-1.10/info.c lines 1498–1507 at a77dfb9 · upstream ↗ 1576 { // MT_SKULL ⋮ 1585 256, // painchance
linuxdoom-1.10/info.c lines 1576–1585 at a77dfb9 · upstream ↗ 1602 { // MT_SPIDER ⋮ 1611 40, // painchance
linuxdoom-1.10/info.c lines 1602–1611 at a77dfb9 · upstream ↗ 1654 { // MT_CYBORG ⋮ 1663 20, // painchance
linuxdoom-1.10/info.c lines 1654–1663 at a77dfb9 · upstream ↗ 341 {CC_ZOMBIE, MT_POSSESSED}, 342 {CC_SHOTGUN, MT_SHOTGUY}, ⋮ 344 {CC_IMP, MT_TROOP}, 345 {CC_DEMON, MT_SERGEANT}, 346 {CC_LOST, MT_SKULL}, 347 {CC_CACO, MT_HEAD}, ⋮ 349 {CC_BARON, MT_BRUISER}, ⋮ 355 {CC_SPIDER, MT_SPIDER}, 356 {CC_CYBER, MT_CYBORG},
linuxdoom-1.10/f_finale.c lines 341–356 at a77dfb9 · upstream ↗ 677#define CC_ZOMBIE "ZOMBIEMAN" 678#define CC_SHOTGUN "SHOTGUN GUY" ⋮ 680#define CC_IMP "IMP" 681#define CC_DEMON "DEMON" 682#define CC_LOST "LOST SOUL" 683#define CC_CACO "CACODEMON" ⋮ 685#define CC_BARON "BARON OF HELL" ⋮ 691#define CC_SPIDER "THE SPIDER MASTERMIND" 692#define CC_CYBER "THE CYBERDEMON"
linuxdoom-1.10/d_englsh.h lines 677–692 at a77dfb9 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-21.
Revised after a check by a second AI model, 2026-10-21.
- ^ abIn the four Quake II monsters read, a flinch is a cooldown rather than a roll: the soldier's, infantry's, and gunner's pain functions play a pain animation unless one started within the last 3 seconds (
pain_debounce_time), choosing which at random (soldier, infantry) or by the damage (gunner), and on Nightmare (skill 3) they return before the animation. The soldier has an exception both ways: knocked upward faster than 100, it switches to a fourth pain animation even within the cooldown (if already in one of the other three) and even on Nightmare. On Nightmare the cooldown is 5 seconds, not 3:T_Damageresets it to 5 after each hit that calls the pain function. The tank adds a roll: it ignores hits of 10 or less, and flinches from 11 to 30 only whenrandom()is at most 0.2; on hard and Nightmare it doesn't flinch during two of its attacks.Codeid-Software/Quake-2revisedHow we know
417 if (level.time < self->pain_debounce_time) ⋮ 419 if ((self->velocity[2] > 100) && ( (self->monsterinfo.currentmove == &soldier_move_pain1) || (self->monsterinfo.currentmove == &soldier_move_pain2) || (self->monsterinfo.currentmove == &soldier_move_pain3))) 420 self->monsterinfo.currentmove = &soldier_move_pain4; 421 return; ⋮ 424 self->pain_debounce_time = level.time + 3; ⋮ 434 if (self->velocity[2] > 100) ⋮ 436 self->monsterinfo.currentmove = &soldier_move_pain4; 437 return; ⋮ 440 if (skill->value == 3) 441 return; // no pain anims in nightmare ⋮ 443 r = random(); ⋮ 445 if (r < 0.33) ⋮
game/m_soldier.c lines 417–450 at 372afde · upstream ↗ 222 if (level.time < self->pain_debounce_time) 223 return; ⋮ 225 self->pain_debounce_time = level.time + 3; ⋮ 227 if (skill->value == 3) 228 return; // no pain anims in nightmare ⋮ 230 n = rand() % 2;
game/m_infantry.c lines 222–230 at 372afde · upstream ↗ 288 if (level.time < self->pain_debounce_time) 289 return; ⋮ 291 self->pain_debounce_time = level.time + 3; ⋮ 298 if (skill->value == 3) 299 return; // no pain anims in nightmare ⋮ 301 if (damage <= 10) 302 self->monsterinfo.currentmove = &gunner_move_pain3;
game/m_gunner.c lines 288–302 at 372afde · upstream ↗ 293 if (damage <= 10) 294 return; ⋮ 296 if (level.time < self->pain_debounce_time) 297 return; ⋮ 299 if (damage <= 30) 300 if (random() > 0.2) 301 return; ⋮ 303 // If hard or nightmare, don't go into pain while attacking 304 if ( skill->value >= 2) ⋮ 307 return; ⋮ 309 return; ⋮ 312 self->pain_debounce_time = level.time + 3;
game/m_tank.c lines 293–312 at 372afde · upstream ↗ 506 if (targ->svflags & SVF_MONSTER) ⋮ 508 M_ReactToDamage (targ, attacker); 509 if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take)) ⋮ 511 targ->pain (targ, attacker, knockback, take); 512 // nightmare mode monsters don't go into pain frames often 513 if (skill->value == 3) 514 targ->pain_debounce_time = level.time + 5;
game/g_combat.c lines 506–514 at 372afde · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
Sources
Code
- Software, id. 2024. “DOOM: the Linux source release (id-Software/DOOM, linuxdoom-1.10).” Commit a77dfb9. id-Software/DOOM @ a77dfb9 · licence, all uses
- Software, id. 2012. “Quake II: the GPL source release (id-Software/Quake-2, 3.19).” Commit 372afde. id-Software/Quake-2 @ 372afde · licence, all uses