Class: Quake::Game::ItemPickups

Inherits:
Object
  • Object
show all
Defined in:
lib/quake/game/item_pickups.rb

Overview

Handles item pickup logic: checks proximity between player and item entities, applies effects to PlayerState, marks items as picked up.

Constant Summary collapse

PICKUP_RADIUS =

units (roughly player bbox width)

32.0
PLAYER_MINS =
Math::Vec3.new(-16.0, -16.0, -24.0)
PLAYER_MAXS =
Math::Vec3.new(16.0, 16.0, 32.0)
ITEMS =

Pickup definitions: classname -> { type:, ... }

{
  # Health
  "item_health" => { type: :health, amount: 25 },

  # Armor
  "item_armor1" => { type: :armor, points: 100, armor_type: 1 }, # green
  "item_armor2" => { type: :armor, points: 150, armor_type: 2 }, # yellow
  "item_armorInv" => { type: :armor, points: 200, armor_type: 3 }, # red

  # Ammo
  "item_shells" => { type: :ammo, ammo_type: :shells, amount: 20, big_amount: 40 },
  "item_spikes" => { type: :ammo, ammo_type: :nails, amount: 25, big_amount: 50 },
  "item_rockets" => { type: :ammo, ammo_type: :rockets, amount: 5, big_amount: 10 },
  "item_cells" => { type: :ammo, ammo_type: :cells, amount: 6, big_amount: 12 },
  "item_weapon" => { type: :legacy_weapon_ammo },

  # Weapons (give weapon + starting ammo)
  "weapon_supershotgun" => {
    type: :weapon, weapon: :super_shotgun,
    ammo_type: :shells, ammo_amount: 5
  },
  "weapon_nailgun" => {
    type: :weapon, weapon: :nailgun,
    ammo_type: :nails, ammo_amount: 30
  },
  "weapon_supernailgun" => {
    type: :weapon, weapon: :super_nailgun,
    ammo_type: :nails, ammo_amount: 30
  },
  "weapon_grenadelauncher" => {
    type: :weapon, weapon: :grenade_launcher,
    ammo_type: :rockets, ammo_amount: 5
  },
  "weapon_rocketlauncher" => {
    type: :weapon, weapon: :rocket_launcher,
    ammo_type: :rockets, ammo_amount: 5
  },
  "weapon_lightning" => {
    type: :weapon, weapon: :lightning_gun,
    ammo_type: :cells, ammo_amount: 15
  },

  # Powerups
  "item_artifact_super_damage" => { type: :powerup, powerup: :quad },
  "item_artifact_invulnerability" => { type: :powerup, powerup: :pentagram },
  "item_artifact_envirosuit" => { type: :powerup, powerup: :biosuit },
  "item_artifact_invisibility" => { type: :powerup, powerup: :ring },

  # Keys
  "item_key1" => { type: :key, key: :silver },
  "item_key2" => { type: :key, key: :gold },

  # Episode runes
  "item_sigil" => { type: :sigil },

  # Dropped by killed players in QuakeC DropBackpack.
  "item_backpack" => { type: :backpack }
}.freeze
BACKPACK_WEAPONS =
{
  1 => :shotgun,
  2 => :super_shotgun,
  4 => :nailgun,
  8 => :super_nailgun,
  16 => :grenade_launcher,
  32 => :rocket_launcher,
  64 => :lightning_gun,
  4096 => :axe
}.freeze
SUB_REGEN_SOUND =
"items/itembk2.wav"
WEAPON_NETNAMES =

QuakeC items.qc netnames used by the "You got the ..." sprints.

{
  super_shotgun: "Double-barrelled Shotgun",
  nailgun: "nailgun",
  super_nailgun: "Super Nailgun",
  grenade_launcher: "Grenade Launcher",
  rocket_launcher: "Rocket Launcher",
  lightning_gun: "Thunderbolt"
}.freeze
AMMO_NETNAMES =
{
  shells: "shells",
  nails: "nails",
  rockets: "rockets",
  cells: "cells"
}.freeze
LEGACY_AMMO_NETNAMES =

Legacy item_weapon boxes use "spikes" for nails (items.qc).

{
  shells: "shells",
  nails: "spikes",
  rockets: "rockets"
}.freeze
POWERUP_NETNAMES =
{
  quad: "Quad Damage",
  pentagram: "Pentagram of Protection",
  ring: "Ring of Shadows",
  biosuit: "Biosuit"
}.freeze
KEY_NETNAMES =
{
  silver: "silver key",
  gold: "gold key"
}.freeze
BACKPACK_WEAPON_NETNAMES =

items.qc BackpackTouch netnames (set in DropBackpack spawns).

{
  axe: "Axe",
  shotgun: "Shotgun",
  super_shotgun: "Double-barrelled Shotgun",
  nailgun: "Nailgun",
  super_nailgun: "Super Nailgun",
  grenade_launcher: "Grenade Launcher",
  rocket_launcher: "Rocket Launcher",
  lightning_gun: "Thunderbolt"
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(entities, deathmatch: 0) ⇒ ItemPickups

Returns a new instance of ItemPickups.



134
135
136
137
138
# File 'lib/quake/game/item_pickups.rb', line 134

def initialize(entities, deathmatch: 0)
  @item_entities = entities.select { |e| ITEMS.key?(e.classname) }
  @picked_up = Set.new # entity object_ids that have been collected
  @deathmatch = deathmatch.to_i
end

Instance Method Details

#add_entity(ent) ⇒ Object



140
141
142
# File 'lib/quake/game/item_pickups.rb', line 140

def add_entity(ent)
  @item_entities << ent if ITEMS.key?(ent.classname)
end

#check_pickups(player_pos, player_state, game_time: 0.0, water_level: 0) ⇒ Object

Check for pickups near player position. Returns array of pickup event hashes (for sound/effect triggering).



170
171
172
173
174
175
176
177
178
179
180
181
182
183
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
# File 'lib/quake/game/item_pickups.rb', line 170

def check_pickups(player_pos, player_state, game_time: 0.0, water_level: 0)
  return [] unless player_state.alive?

  events = []

  @item_entities.each do |ent|
    next if ent.removed?
    next if @picked_up.include?(ent.object_id)
    next if ent.instance_variable_get(:@solid) == 0

    next unless touches_item?(player_pos, ent)

    defn = ITEMS[ent.classname]
    next unless defn

    # Built before try_pickup mutates PlayerState (QuakeC prints
    # from pre-pickup state, e.g. BackpackTouch weapon ownership).
    message = pickup_message(defn, ent, player_state)
    picked = try_pickup(player_state, defn, ent, game_time: game_time, water_level: water_level)
    if picked
      leaves_item = leave_weapon_pickup?(defn)
      unless leaves_item
        @picked_up.add(ent.object_id)
        hide_picked_item(ent, defn, game_time)
      end
      events << {
        classname: ent.classname,
        type: defn[:type],
        entity: ent,
        sound: pickup_sound(defn, ent),
        extra_sounds: ent.instance_variable_get(:@extra_pickup_sounds),
        use_targets: !leaves_item,
        message: message
      }
    end
  end

  events
end

#hide_picked_item(ent, defn, game_time) ⇒ Object



222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/quake/game/item_pickups.rb', line 222

def hide_picked_item(ent, defn, game_time)
  if defn[:type] == :backpack
    ent.instance_variable_set(:@removed, true)
  else
    ent.instance_variable_set(:@mdl, ent["model"]) unless ent.instance_variable_get(:@mdl)
    ent.properties["model"] = ""
    ent.instance_variable_set(:@solid, 0)
    return if dropped_powerup?(ent, defn)

    schedule_regen(ent, defn, game_time)
  end
end

#owns_megahealth_rot_for?(player_state) ⇒ Boolean

Returns:

  • (Boolean)


214
215
216
217
218
219
220
# File 'lib/quake/game/item_pickups.rb', line 214

def owns_megahealth_rot_for?(player_state)
  @item_entities.any? do |ent|
    @picked_up.include?(ent.object_id) &&
      ent.instance_variable_get(:@think) == :item_megahealth_rot &&
      ent.instance_variable_get(:@owner).equal?(player_state)
  end
end

#picked_up?(entity) ⇒ Boolean

Returns:

  • (Boolean)


210
211
212
# File 'lib/quake/game/item_pickups.rb', line 210

def picked_up?(entity)
  @picked_up.include?(entity.object_id)
end

#update(game_time, player_state: nil) ⇒ Object



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/quake/game/item_pickups.rb', line 144

def update(game_time, player_state: nil)
  events = []
  @item_entities.each do |ent|
    next if ent.removed?

    if ent.instance_variable_get(:@think) == :sub_remove && ent.think_time <= game_time
      ent.instance_variable_set(:@removed, true)
      next
    end

    next unless @picked_up.include?(ent.object_id)
    next if ent.think_time > game_time

    case ent.instance_variable_get(:@think)
    when :sub_regen
      regen_item(ent)
      events << { classname: ent.classname, type: :respawn, entity: ent, sound: SUB_REGEN_SOUND }
    when :item_megahealth_rot
      update_megahealth_item(ent, game_time)
    end
  end
  events
end