Class: Quake::Game::BrushEntities
- Inherits:
-
Object
- Object
- Quake::Game::BrushEntities
- Defined in:
- lib/quake/game/brush_entities.rb
Overview
Manages brush entity behavior: doors, platforms, buttons, triggers. Each entity has a state machine and moves between positions.
Constant Summary collapse
- BUTTON_TOUCH_RADIUS =
40.0- STATE_IDLE =
Entity states
:idle- STATE_OPENING =
:opening- STATE_OPEN =
:open- STATE_CLOSING =
:closing- STATE_CLOSED =
:closed- PLAT_LOW_TRIGGER =
1- FL_FLY =
1- FL_SWIM =
2- FL_MONSTER =
32- FL_ONGROUND =
512- VERTICAL_RIDER_CLASSNAMES =
If the player is standing on a moving platform, return a new position snapped to the platform's top surface (so on_ground stays true and the player rides smoothly). Returns nil if not on a moving platform.
%w[func_plat plat func_door door].freeze
Instance Attribute Summary collapse
-
#found_secrets ⇒ Object
readonly
Returns the value of attribute found_secrets.
-
#total_secrets ⇒ Object
readonly
Returns the value of attribute total_secrets.
Instance Method Summary collapse
-
#check_triggers(player_pos, player_radius: 16.0, player_forward: nil) ⇒ Object
Check if player touches any trigger entities.
- #damage_brush_entity(ent, amount, activator: nil) ⇒ Object
- #damage_trigger(ent, amount, activator: nil) ⇒ Object
-
#initialize(entities, level, target_map, registered: false, sound_events: nil, serverflags: 0, worldtype: 0) ⇒ BrushEntities
constructor
A new instance of BrushEntities.
-
#platform_motion_for(player_pos) ⇒ Object
Backward-compatible delta-only method.
- #snap_to_platform(player_pos) ⇒ Object
- #touch_trigger(ent, activator: nil) ⇒ Object
-
#update(dt, player_pos, player_state: nil) ⇒ Object
Update all brush entities.
-
#use_targets(source, activator: nil) ⇒ Object
QuakeC SUB_UseTargets, limited to local entity state transitions.
Constructor Details
#initialize(entities, level, target_map, registered: false, sound_events: nil, serverflags: 0, worldtype: 0) ⇒ BrushEntities
Returns a new instance of BrushEntities.
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/quake/game/brush_entities.rb', line 23 def initialize(entities, level, target_map, registered: false, sound_events: nil, serverflags: 0, worldtype: 0) @entities = entities @level = level @target_map = target_map @registered = registered @sound_events = sound_events @serverflags = serverflags.to_i @worldtype = worldtype.to_i @time = 0.0 @total_secrets = 0 @found_secrets = 0 @brush_entities = entities.select { |ent| ent.brush_entity? || ent.classname == "misc_teleporttrain" } @delayed_uses = [] @entities.each { |ent| init_point_trigger(ent) } # Initialize movement data for each brush entity @brush_entities.each { |ent| init_brush_entity(ent) } link_doors puts "Initialized #{@brush_entities.size} brush entities" end |
Instance Attribute Details
#found_secrets ⇒ Object (readonly)
Returns the value of attribute found_secrets.
21 22 23 |
# File 'lib/quake/game/brush_entities.rb', line 21 def found_secrets @found_secrets end |
#total_secrets ⇒ Object (readonly)
Returns the value of attribute total_secrets.
21 22 23 |
# File 'lib/quake/game/brush_entities.rb', line 21 def total_secrets @total_secrets end |
Instance Method Details
#check_triggers(player_pos, player_radius: 16.0, player_forward: nil) ⇒ Object
Check if player touches any trigger entities
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/quake/game/brush_entities.rb', line 130 def check_triggers(player_pos, player_radius: 16.0, player_forward: nil) @entities.each do |ent| next unless ent.classname.start_with?("trigger_") next unless trigger_model_index(ent) next if ent.instance_variable_get(:@removed) next if ent.instance_variable_get(:@touch_disabled) next if notouch_trigger?(ent) next if health_trigger?(ent) next unless trigger_facing_allowed?(ent, player_forward) next if trigger_cooldown_active?(ent) model = @level.models[trigger_model_index(ent)] next unless model # Simple AABB check against player mins = model.mins maxs = model.maxs origin = ent.position if player_pos.x >= origin.x + mins.x - player_radius && player_pos.x <= origin.x + maxs.x + player_radius && player_pos.y >= origin.y + mins.y - player_radius && player_pos.y <= origin.y + maxs.y + player_radius && player_pos.z >= origin.z + mins.z - player_radius && player_pos.z <= origin.z + maxs.z + player_radius yield ent if block_given? end end end |
#damage_brush_entity(ent, amount, activator: nil) ⇒ Object
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/quake/game/brush_entities.rb', line 220 def damage_brush_entity(ent, amount, activator: nil) return (ent, amount, activator: activator) if ent.classname == "func_button" return damage_door(ent, amount, activator: activator) if regular_door?(ent) return damage_secret_door(ent, activator: activator) if secret_door?(ent) return false unless ["trigger_once", "trigger_multiple", "trigger_secret"].include?(ent.classname) return false unless ent.health.positive? ent.instance_variable_set(:@max_health, ent.health) unless ent.instance_variable_get(:@max_health) ent.health -= amount.to_f.ceil return true if ent.health.positive? ent.health = -99.0 if ent.health < -99.0 activate_multi_trigger(ent, activator: activator || ent) true end |
#damage_trigger(ent, amount, activator: nil) ⇒ Object
216 217 218 |
# File 'lib/quake/game/brush_entities.rb', line 216 def damage_trigger(ent, amount, activator: nil) damage_brush_entity(ent, amount, activator: activator) end |
#platform_motion_for(player_pos) ⇒ Object
Backward-compatible delta-only method
123 124 125 126 127 |
# File 'lib/quake/game/brush_entities.rb', line 123 def platform_motion_for(player_pos) snapped = snap_to_platform(player_pos) return Math::Vec3::ORIGIN unless snapped snapped - player_pos end |
#snap_to_platform(player_pos) ⇒ Object
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/quake/game/brush_entities.rb', line 89 def snap_to_platform(player_pos) @brush_entities.each do |ent| next unless VERTICAL_RIDER_CLASSNAMES.include?(ent.classname) model = @level.models[ent.model_index] next unless model prev = ent.instance_variable_get(:@prev_pos) next unless prev ent_delta_z = ent.position.z - prev.z # Doors join the rider check only while actually moving # vertically (elevators like e1m1's t1 are vertical func_doors -- # SV_PushMove carries riders for every pusher, not just plats). next if ent.classname.include?("door") && ent_delta_z.zero? # Use the OLD platform position for the on_top check, since # player_pos hasn't been updated for this frame's movement yet. # The player origin rides 24 above the surface (hull mins.z -24). old_top_z = prev.z + model.maxs.z next unless player_pos.z >= old_top_z + 16 && player_pos.z <= old_top_z + 48 next unless player_pos.x >= ent.position.x + model.mins.x && player_pos.x <= ent.position.x + model.maxs.x && player_pos.y >= ent.position.y + model.mins.y && player_pos.y <= ent.position.y + model.maxs.y # Snap the origin to 24 above the new top surface (feet slightly # above it so the trace-down ground check detects it cleanly). new_top_z = ent.position.z + model.maxs.z return Math::Vec3.new(player_pos.x, player_pos.y, new_top_z + 24.5) end nil end |
#touch_trigger(ent, activator: nil) ⇒ Object
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/quake/game/brush_entities.rb', line 184 def touch_trigger(ent, activator: nil) return false if ent.instance_variable_get(:@touch_disabled) return false if trigger_cooldown_active?(ent) case ent.classname when "trigger_once" return false unless activator&.classname == "player" return false unless trigger_touch_facing_allowed?(ent, activator) activate_multi_trigger(ent, activator: activator) when "trigger_secret" return false unless trigger_touch_facing_allowed?(ent, activator) touch_secret_trigger(ent, activator: activator) when "trigger_multiple" return false unless activator&.classname == "player" return false unless trigger_touch_facing_allowed?(ent, activator) activate_multi_trigger(ent, activator: activator) when "trigger_onlyregistered" touch_onlyregistered(ent, activator: activator) when "trigger_monsterjump" touch_monsterjump(ent, activator) when "trigger_push" touch_push(ent, activator) when "trigger_hurt" touch_hurt(ent, activator) else false end end |
#update(dt, player_pos, player_state: nil) ⇒ Object
Update all brush entities. Returns entities that need rendering.
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/quake/game/brush_entities.rb', line 46 def update(dt, player_pos, player_state: nil) @time_step = dt @time += dt update_delayed_uses(dt) @entities.each { |ent| update_scheduled_remove(ent, dt) } # Save previous positions for platform-riding @brush_entities.each do |ent| ent.instance_variable_set(:@prev_pos, ent.position) end @brush_entities.each do |ent| next if ent.instance_variable_get(:@removed) update_trigger_cooldown(ent, dt) case ent.classname when "func_door", "door" if secret_door?(ent) update_secret_door(ent, player_pos, player_state) else update_door(ent, dt, player_pos, player_state) end when "func_plat", "plat" update_platform(ent, dt, player_pos, player_state) when "func_button" (ent, dt, player_pos, player_state) when "func_train", "train", "misc_teleporttrain" update_train(ent, dt, player_pos, player_state) when "func_door_secret" update_secret_door(ent, player_pos, player_state) end end @brush_entities end |
#use_targets(source, activator: nil) ⇒ Object
QuakeC SUB_UseTargets, limited to local entity state transitions. The engine owns side effects like changelevel and teleport movement.
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/quake/game/brush_entities.rb', line 162 def use_targets(source, activator: nil) delay = (source["delay"] || "0").to_f unless delay.zero? @delayed_uses << { remaining: delay, message: source., killtarget: source.killtarget, target: source.target, activator: activator || source } return end fire_use_targets( source.killtarget, source.target, activator: activator || source, message: source., source_noise: source.instance_variable_get(:@noise) ) end |