Class: Doom::Game::Combat
- Inherits:
-
Object
- Object
- Doom::Game::Combat
- Defined in:
- lib/doom/game/combat.rb
Overview
Hitscan weapon firing and monster state tracking. Matches Chocolate Doom’s P_LineAttack / P_AimLineAttack from p_map.c.
Defined Under Namespace
Classes: Projectile
Constant Summary collapse
- MONSTER_HP =
Monster starting HP (from mobjinfo[] in info.c)
{ 3004 => 20, # Zombieman 9 => 30, # Shotgun Guy 3001 => 60, # Imp 3002 => 150, # Demon 58 => 150, # Spectre 3003 => 1000, # Baron of Hell 69 => 500, # Hell Knight 3005 => 400, # Cacodemon 3006 => 100, # Lost Soul 16 => 4000, # Cyberdemon 7 => 3000, # Spider Mastermind 65 => 70, # Heavy Weapon Dude 64 => 700, # Archvile 71 => 400, # Pain Elemental 84 => 20, # Wolfenstein SS 2035 => 20, # Explosive barrel }.freeze
- MONSTER_RADIUS =
{ 3004 => 20, 9 => 20, 3001 => 20, 3002 => 30, 58 => 30, 3003 => 24, 69 => 24, 3005 => 31, 3006 => 16, 16 => 40, 7 => 128, 65 => 20, 64 => 20, 71 => 31, 84 => 20, 2035 => 10, # Barrel }.freeze
- BARREL_TYPE =
Barrel (explosive, not a monster but damageable)
2035- BARREL_HP =
20- BARREL_SPLASH_RADIUS =
128.0- BARREL_SPLASH_DAMAGE =
128- DEATH_FRAMES =
Normal death frame sequences per sprite prefix (rotation 0 only) Identified by sprite heights: frames go from standing height to flat on ground
{ 'POSS' => %w[H I J K L], # Zombieman: 55→46→34→27→17 'SPOS' => %w[H I J K L], # Shotgun Guy: 60→50→35→27→17 'TROO' => %w[I J K L M], # Imp: 62→59→54→46→22 'SARG' => %w[I J K L M N], # Demon/Spectre: 56→56→53→57→46→32 'BOSS' => %w[H I J K L M N], # Baron 'BOS2' => %w[H I J K L M N], # Hell Knight 'HEAD' => %w[G H I J K L], # Cacodemon 'SKUL' => %w[G H I J K], # Lost Soul 'CYBR' => %w[I J], # Cyberdemon 'SPID' => %w[I J K], # Spider Mastermind 'CPOS' => %w[H I J K L M N], # Heavy Weapon Dude 'PAIN' => %w[H I J K L M], # Pain Elemental 'SSWV' => %w[I J K L M], # Wolfenstein SS 'BEXP' => %w[A B C D E], # Barrel explosion }.freeze
- DEATH_ANIM_TICS =
Tics per death frame
6- PAIN_CHANCE =
Pain chance per monster (out of 256, from mobjinfo)
{ 3004 => 200, 9 => 170, 3001 => 200, 3002 => 180, 58 => 180, 3003 => 50, 69 => 50, 3005 => 128, 3006 => 256, 16 => 40, 7 => 40, 65 => 170, 64 => 10, 71 => 128, 84 => 170, }.freeze
- PAIN_DURATION =
Tics monster is stunned when in pain
6- ROCKET_SPEED =
Projectile constants
20.0- ROCKET_DAMAGE =
Map units per tic (matches DOOM’s mobjinfo MISSILESPEED)
20- ROCKET_RADIUS =
Direct hit base (DOOM: 1d8 * 20)
11- SPLASH_RADIUS =
Collision radius
128.0- SPLASH_DAMAGE =
Splash damage radius
128- MONSTER_PROJECTILES =
Monster projectile definitions
{ imp: { sprite: 'BAL1', speed: 10.0, damage: [3, 24], radius: 6, splash: false }, baron: { sprite: 'BAL7', speed: 15.0, damage: [8, 64], radius: 6, splash: false }, caco: { sprite: 'BAL2', speed: 10.0, damage: [5, 40], radius: 6, splash: false }, }.freeze
- MONSTER_PROJECTILE_TYPE =
Map monster type to projectile type
{ 3001 => :imp, # Imp 3003 => :baron, # Baron 69 => :baron, # Hell Knight 3005 => :caco, # Cacodemon }.freeze
Instance Attribute Summary collapse
-
#dead_things ⇒ Object
readonly
Returns the value of attribute dead_things.
-
#explosions ⇒ Object
readonly
Returns the value of attribute explosions.
-
#projectiles ⇒ Object
readonly
Returns the value of attribute projectiles.
-
#puffs ⇒ Object
readonly
Returns the value of attribute puffs.
Instance Method Summary collapse
- #dead?(thing_idx) ⇒ Boolean
-
#death_sprite(thing_idx, thing_type, viewer_angle, thing_angle) ⇒ Object
Get the current death frame sprite for a dead monster/barrel.
-
#fire(px, py, pz, cos_a, sin_a, weapon) ⇒ Object
Fire the current weapon.
- #in_pain?(thing_idx) ⇒ Boolean
-
#initialize(map, player_state, sprites, hidden_things = {}, sound_engine = nil) ⇒ Combat
constructor
Weapon damage: DOOM does (P_Random()%3 + 1) * multiplier Pistol/chaingun: 1*5..3*5 = 5-15 per bullet Shotgun: 7 pellets, each 1*5..3*5 = 5-15 Fist/chainsaw: 1*2..3*2 = 2-10.
-
#spawn_monster_projectile(monster_x, monster_y, monster_z, monster_type, damage_multiplier) ⇒ Object
Spawn a monster projectile (fireball, etc.) Matches Chocolate Doom’s P_SpawnMissile: calculates momz for vertical aim.
-
#update ⇒ Object
Called each game tic.
- #update_player_pos(x, y, z = nil) ⇒ Object
Constructor Details
#initialize(map, player_state, sprites, hidden_things = {}, sound_engine = nil) ⇒ Combat
Weapon damage: DOOM does (P_Random()%3 + 1) * multiplier Pistol/chaingun: 1*5..3*5 = 5-15 per bullet Shotgun: 7 pellets, each 1*5..3*5 = 5-15 Fist/chainsaw: 1*2..3*2 = 2-10
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/doom/game/combat.rb', line 99 def initialize(map, player_state, sprites, hidden_things = {}, sound_engine = nil) @map = map @player = player_state @sprites = sprites @hidden_things = hidden_things @sound = sound_engine @monster_hp = {} # thing_idx => current HP @dead_things = {} # thing_idx => { tic: death_start_tic, prefix: sprite_prefix } @pain_until = {} # thing_idx => tic when pain ends @projectiles = [] # Active projectiles in flight @explosions = [] # Active explosions (for rendering) @puffs = [] # Bullet puff effects @player_x = 0.0 @player_y = 0.0 @player_z = 0.0 @tic = 0 end |
Instance Attribute Details
#dead_things ⇒ Object (readonly)
Returns the value of attribute dead_things.
155 156 157 |
# File 'lib/doom/game/combat.rb', line 155 def dead_things @dead_things end |
#explosions ⇒ Object (readonly)
Returns the value of attribute explosions.
155 156 157 |
# File 'lib/doom/game/combat.rb', line 155 def explosions @explosions end |
#projectiles ⇒ Object (readonly)
Returns the value of attribute projectiles.
155 156 157 |
# File 'lib/doom/game/combat.rb', line 155 def projectiles @projectiles end |
#puffs ⇒ Object (readonly)
Returns the value of attribute puffs.
155 156 157 |
# File 'lib/doom/game/combat.rb', line 155 def puffs @puffs end |
Instance Method Details
#dead?(thing_idx) ⇒ Boolean
161 162 163 |
# File 'lib/doom/game/combat.rb', line 161 def dead?(thing_idx) @dead_things.key?(thing_idx) end |
#death_sprite(thing_idx, thing_type, viewer_angle, thing_angle) ⇒ Object
Get the current death frame sprite for a dead monster/barrel
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/doom/game/combat.rb', line 166 def death_sprite(thing_idx, thing_type, viewer_angle, thing_angle) info = @dead_things[thing_idx] return nil unless info prefix = info[:prefix] frames = DEATH_FRAMES[prefix] return nil unless frames elapsed = @tic - info[:tic] frame_idx = elapsed / DEATH_ANIM_TICS # Barrels disappear after explosion animation (S_NULL in Chocolate Doom) if thing_type == BARREL_TYPE && frame_idx >= frames.size return nil end frame_idx = frame_idx.clamp(0, frames.size - 1) frame_letter = frames[frame_idx] # Use prefix directly if it differs from the thing's sprite (e.g. BEXP for barrels) thing_prefix = @sprites.prefix_for(thing_type) if prefix != thing_prefix @sprites.get_frame_by_prefix(prefix, frame_letter) else @sprites.get_frame(thing_type, frame_letter, viewer_angle, thing_angle) end end |
#fire(px, py, pz, cos_a, sin_a, weapon) ⇒ Object
Fire the current weapon
203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/doom/game/combat.rb', line 203 def fire(px, py, pz, cos_a, sin_a, weapon) case weapon when PlayerState::WEAPON_PISTOL, PlayerState::WEAPON_CHAINGUN hitscan(px, py, cos_a, sin_a, 1, 0.0, 5) when PlayerState::WEAPON_SHOTGUN hitscan(px, py, cos_a, sin_a, 7, Math::PI / 32, 5) when PlayerState::WEAPON_ROCKET spawn_rocket(px, py, pz, cos_a, sin_a) when PlayerState::WEAPON_FIST melee(px, py, cos_a, sin_a, 2, 64) when PlayerState::WEAPON_CHAINSAW melee(px, py, cos_a, sin_a, 2, 64) end end |
#in_pain?(thing_idx) ⇒ Boolean
157 158 159 |
# File 'lib/doom/game/combat.rb', line 157 def in_pain?(thing_idx) @pain_until[thing_idx] && @tic < @pain_until[thing_idx] end |
#spawn_monster_projectile(monster_x, monster_y, monster_z, monster_type, damage_multiplier) ⇒ Object
Spawn a monster projectile (fireball, etc.) Matches Chocolate Doom’s P_SpawnMissile: calculates momz for vertical aim
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
# File 'lib/doom/game/combat.rb', line 125 def spawn_monster_projectile(monster_x, monster_y, monster_z, monster_type, damage_multiplier) proj_type = MONSTER_PROJECTILE_TYPE[monster_type] return unless proj_type info = MONSTER_PROJECTILES[proj_type] return unless info dx = @player_x - monster_x dy = @player_y - monster_y dist = Math.sqrt(dx * dx + dy * dy) return if dist < 1 # Normalize direction and apply speed speed = info[:speed] ndx = dx / dist * speed ndy = dy / dist * speed # P_SpawnMissile: momz = (target.z - source.z) / (dist / speed) # This makes the projectile arc toward the target's height target_z = @player_z - 16 # Aim at player center (z + height/2, roughly) travel_tics = dist / speed travel_tics = 1.0 if travel_tics < 1.0 ndz = (target_z - monster_z) / travel_tics @projectiles << Projectile.new( monster_x + ndx * 2, monster_y + ndy * 2, monster_z, ndx, ndy, ndz, proj_type, @tic, info[:sprite], :player ) end |
#update ⇒ Object
Called each game tic
195 196 197 198 199 200 |
# File 'lib/doom/game/combat.rb', line 195 def update @tic += 1 update_projectiles update_explosions @puffs.reject! { |p| @tic - p[:tic] > 12 } end |
#update_player_pos(x, y, z = nil) ⇒ Object
117 118 119 120 121 |
# File 'lib/doom/game/combat.rb', line 117 def update_player_pos(x, y, z = nil) @player_x = x @player_y = y @player_z = z if z end |