Class: Lich::Gemstone::CreatureTemplate
- Inherits:
-
Object
- Object
- Lich::Gemstone::CreatureTemplate
- Defined in:
- documented/gemstone/creature.rb
Overview
Static creature template data (ID-less reference information)
Constant Summary collapse
- BOON_ADJECTIVES =
Adjectives that boon creatures may spawn with, stripped during template name lookups to handle creature names like "adroit goblin" (stored and matched as "goblin").
%w[ adroit afflicted apt barbed belligerent blurry canny combative dazzling deft diseased drab dreary ethereal flashy flexile flickering flinty frenzied ghastly ghostly gleaming glittering glorious glowing grotesque hardy illustrious indistinct keen lanky luminous lustrous muculent nebulous oozing pestilent radiant raging ready resolute robust rune-covered shadowy shielded shifting shimmering shining sickly green sinuous slimy sparkling spindly spiny stalwart steadfast stout tattoed tattooed tenebrous tough twinkling unflinching unyielding wavering wispy ]
- BOON_REGEX =
Clean creature name by removing boon adjectives Optimized to use single compiled regex instead of 50+ sequential matches
/^(#{BOON_ADJECTIVES.join('|')})\s+/i.freeze
- @@templates =
{}
- @@loaded =
false
Instance Attribute Summary collapse
-
#abilities ⇒ Object
readonly
Returns the value of attribute abilities.
-
#alchemy ⇒ Object
readonly
Returns the value of attribute alchemy.
-
#areas ⇒ Object
readonly
Returns the value of attribute areas.
-
#attack_attributes ⇒ Object
readonly
Returns the value of attribute attack_attributes.
-
#bcs ⇒ Object
readonly
Returns the value of attribute bcs.
-
#boss ⇒ Object
readonly
Returns the value of attribute boss.
-
#boss_type ⇒ Object
readonly
Returns the value of attribute boss_type.
-
#defense_attributes ⇒ Object
readonly
Returns the value of attribute defense_attributes.
-
#equipment ⇒ Object
readonly
Returns the value of attribute equipment.
-
#family ⇒ Object
readonly
Returns the value of attribute family.
-
#height ⇒ Object
readonly
Returns the value of attribute height.
-
#level ⇒ Object
readonly
Returns the value of attribute level.
-
#max_hp ⇒ Object
readonly
Returns the value of attribute max_hp.
-
#messaging ⇒ Object
readonly
Returns the value of attribute messaging.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#otherclass ⇒ Object
readonly
Returns the value of attribute otherclass.
-
#picture ⇒ Object
readonly
Returns the value of attribute picture.
-
#size ⇒ Object
readonly
Returns the value of attribute size.
-
#special_other ⇒ Object
readonly
Returns the value of attribute special_other.
-
#speed ⇒ Object
readonly
Returns the value of attribute speed.
-
#treasure ⇒ Object
readonly
Returns the value of attribute treasure.
-
#type ⇒ Object
readonly
Returns the value of attribute type.
-
#undead ⇒ Object
readonly
Returns the value of attribute undead.
-
#url ⇒ Object
readonly
Returns the value of attribute url.
Class Method Summary collapse
-
.[](name) ⇒ Object
Lookup template by name.
-
.all ⇒ Object
Get all loaded templates.
-
.at_uid(uid) ⇒ Object
Templates for the creatures found at the given game room uid.
-
.fix_template_name(template_name) ⇒ String
Cleans a creature name by removing a leading boon adjective and lowercasing.
-
.load_all(dir = File.join(File.dirname(__FILE__), 'creatures')) ⇒ Object
Load all templates from files dir defaults to the real templates directory; overridable so tests can load an isolated fixture directory instead.
Instance Method Summary collapse
-
#boundary_rooms ⇒ Object
Rooms bordering the creature's rooms: connected by an edge - in either direction - to a room the creature is found in, without being one themselves (bigshot's perimeter list).
-
#found_at_uid?(uid) ⇒ Boolean
Whether the creature is found at the given game room uid.
-
#has_blood? ⇒ Boolean?
Returns whether the bestiary template says the creature has blood.
-
#has_bones? ⇒ Boolean?
Returns whether the bestiary template says the creature has bones.
-
#has_limbs? ⇒ Boolean?
Returns whether Limb Disruption (708) has limbs to target on this creature ("The X has no limbs left!" on a fresh target = false).
-
#initialize(data) ⇒ CreatureTemplate
constructor
A new instance of CreatureTemplate.
-
#muggable? ⇒ Boolean?
Returns whether the bestiary template says the creature can be mugged.
-
#rooms ⇒ Object
Every Lich room id the creature is found in, across all areas - ranges combined, deduped, sorted.
-
#rooms_by_area ⇒ Object
name => [Lich room ids] - the per-area display view (eBestiary and friends).
-
#sleepable? ⇒ Boolean?
Returns whether the creature can be put to sleep.
-
#sympathy? ⇒ Boolean?
Returns whether Sympathy (1120) can affect this creature.
-
#uids ⇒ Object
All game uids the creature is found at, expanded from the stored ranges (memoized - ranges stay compact on disk).
-
#witherable? ⇒ Boolean?
Returns whether Wither (1115) has a body to attack on this creature.
Constructor Details
#initialize(data) ⇒ CreatureTemplate
Returns a new instance of CreatureTemplate.
33 34 35 36 37 38 39 40 41 42 43 44 45 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 |
# File 'documented/gemstone/creature.rb', line 33 def initialize(data) @name = data[:name] @url = data[:url] @picture = data[:picture] @level = data[:level]&.to_i @family = data[:family] @type = data[:type] @undead = data[:undead] # Tri-state (true/false/nil) - nil means uncatalogued/unknown, not false. @blood = data[:blood] @bones = data[:bones] @limbs = data[:limbs] @witherable = data[:witherable] @sympathy = data[:sympathy] @muggable = data[:muggable] @sleepable = data[:sleepable] @boss = data[:boss] # nil | "pack" | "miniboss" | "boss" - bestiary classification # ("boss" = the once-per-30-days uniques). Zone-dependent for a # few creatures; the template carries the highest tier. @boss_type = data[:boss_type] @otherclass = data[:otherclass] || [] @areas = data[:areas] || [] @bcs = data[:bcs] @max_hp = data[:max_hp]&.to_i || data[:hitpoints]&.to_i @speed = data[:speed] @height = data[:height].to_i @size = data[:size] atk = data[:attack_attributes] || {} @attack_attributes = OpenStruct.new( physical_attacks: atk[:physical_attacks] || [], bolt_spells: atk[:bolt_spells] || [], warding_spells: normalize_spells(atk[:warding_spells]), offensive_spells: normalize_spells(atk[:offensive_spells]), maneuvers: atk[:maneuvers] || [], special_abilities: (atk[:special_abilities] || []).map { |s| SpecialAbility.new(s) } ) @defense_attributes = DefenseAttributes.new(data[:defense_attributes] || {}) @treasure = Treasure.new(data[:treasure] || {}) @messaging = Messaging.new(data[:messaging] || {}) @special_other = data[:special_other] @abilities = data[:abilities] || [] @alchemy = data[:alchemy] || [] # Items seen on the creature via LOOK, as plain strings. @equipment = data[:equipment] || [] end |
Instance Attribute Details
#abilities ⇒ Object (readonly)
Returns the value of attribute abilities.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def abilities @abilities end |
#alchemy ⇒ Object (readonly)
Returns the value of attribute alchemy.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def alchemy @alchemy end |
#areas ⇒ Object (readonly)
Returns the value of attribute areas.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def areas @areas end |
#attack_attributes ⇒ Object (readonly)
Returns the value of attribute attack_attributes.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def attack_attributes @attack_attributes end |
#bcs ⇒ Object (readonly)
Returns the value of attribute bcs.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def bcs @bcs end |
#boss ⇒ Object (readonly)
Returns the value of attribute boss.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def boss @boss end |
#boss_type ⇒ Object (readonly)
Returns the value of attribute boss_type.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def boss_type @boss_type end |
#defense_attributes ⇒ Object (readonly)
Returns the value of attribute defense_attributes.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def defense_attributes @defense_attributes end |
#equipment ⇒ Object (readonly)
Returns the value of attribute equipment.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def equipment @equipment end |
#family ⇒ Object (readonly)
Returns the value of attribute family.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def family @family end |
#height ⇒ Object (readonly)
Returns the value of attribute height.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def height @height end |
#level ⇒ Object (readonly)
Returns the value of attribute level.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def level @level end |
#max_hp ⇒ Object (readonly)
Returns the value of attribute max_hp.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def max_hp @max_hp end |
#messaging ⇒ Object (readonly)
Returns the value of attribute messaging.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def messaging @messaging end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def name @name end |
#otherclass ⇒ Object (readonly)
Returns the value of attribute otherclass.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def otherclass @otherclass end |
#picture ⇒ Object (readonly)
Returns the value of attribute picture.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def picture @picture end |
#size ⇒ Object (readonly)
Returns the value of attribute size.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def size @size end |
#special_other ⇒ Object (readonly)
Returns the value of attribute special_other.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def special_other @special_other end |
#speed ⇒ Object (readonly)
Returns the value of attribute speed.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def speed @speed end |
#treasure ⇒ Object (readonly)
Returns the value of attribute treasure.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def treasure @treasure end |
#type ⇒ Object (readonly)
Returns the value of attribute type.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def type @type end |
#undead ⇒ Object (readonly)
Returns the value of attribute undead.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def undead @undead end |
#url ⇒ Object (readonly)
Returns the value of attribute url.
14 15 16 |
# File 'documented/gemstone/creature.rb', line 14 def url @url end |
Class Method Details
.[](name) ⇒ Object
Lookup template by name
167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'documented/gemstone/creature.rb', line 167 def self.[](name) load_all unless @@loaded return nil unless name # Try exact match first template = @@templates[name.downcase] return template if template # Try with boon adjectives removed normalized_name = fix_template_name(name) @@templates[normalized_name] end |
.all ⇒ Object
Get all loaded templates
181 182 183 184 |
# File 'documented/gemstone/creature.rb', line 181 def self.all load_all unless @@loaded @@templates.values.uniq end |
.at_uid(uid) ⇒ Object
Templates for the creatures found at the given game room uid.
199 200 201 |
# File 'documented/gemstone/creature.rb', line 199 def self.at_uid(uid) all.select { |t| t.found_at_uid?(uid) } end |
.fix_template_name(template_name) ⇒ String
Cleans a creature name by removing a leading boon adjective and lowercasing.
Produces the lookup key for template storage and retrieval: "Adroit Orc" becomes "orc". Uses a precompiled regex for efficiency (50+ adjectives checked in one pass).
146 147 148 149 150 |
# File 'documented/gemstone/creature.rb', line 146 def self.fix_template_name(template_name) name = template_name.dup.downcase name.sub!(BOON_REGEX, '') name.strip end |
.load_all(dir = File.join(File.dirname(__FILE__), 'creatures')) ⇒ Object
Load all templates from files dir defaults to the real templates directory; overridable so tests can load an isolated fixture directory instead.
85 86 87 88 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 121 122 123 124 125 126 127 128 |
# File 'documented/gemstone/creature.rb', line 85 def self.load_all(dir = File.join(File.dirname(__FILE__), 'creatures')) return if @@loaded return unless File.directory?(dir) template_count = 0 Dir[File.join(dir, '*.rb')].each do |path| next if File.basename(path) == '_creature_template.rb' # Filename-derived name, used only as a fallback for files that # don't set their own :name - the file's own value takes priority # so a name with characters the filename can't represent (a # hyphen, an apostrophe) still round-trips correctly. Both the # display name and the lookup key come from the same source now; # previously both were overwritten from the filename regardless of # what the file itself said, silently breaking lookup for any # creature whose real name a slugified filename can't represent # exactly (e.g. "shield-maiden" -> "shield_maiden" -> "shield maiden"). fallback_name = File.basename(path, '.rb').tr('_', ' ') begin # Safer loading with validation file_content = File.read(path) data = load_template_data(file_content, path) next unless data.is_a?(Hash) data[:name] = fallback_name if data[:name].to_s.strip.empty? normalized_name = fix_template_name(data[:name]) if @@templates.key?(normalized_name) && $creature_debug respond "--- warning: '#{fallback_name}' collides with an already-loaded template on lookup key '#{normalized_name}' - one will silently overwrite the other" end template = new(data) @@templates[normalized_name] = template template_count += 1 rescue StandardError, ScriptError => e # ScriptError (SyntaxError's parent) isn't a StandardError, so a # single template file with malformed Ruby would otherwise abort # load_all entirely instead of just being skipped. respond "--- error loading template #{fallback_name}: #{e.}" if $creature_debug end end @@loaded = true respond "--- loaded #{template_count} creature templates" if $creature_debug end |
Instance Method Details
#boundary_rooms ⇒ Object
Rooms bordering the creature's rooms: connected by an edge - in either direction - to a room the creature is found in, without being one themselves (bigshot's perimeter list). The reverse-edge pass scans the map once; the result is memoized.
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'documented/gemstone/creature.rb', line 229 def boundary_rooms @boundary_rooms ||= begin inside = {} rooms.each { |id| inside[id] = true } border = {} inside.each_key do |id| room = Lich::Common::Map[id] or next (room.wayto || {}).each_key do |dest| d = dest.to_i border[d] = true unless inside[d] end end Lich::Common::Map.list.compact.each do |room| next if inside[room.id] || border[room.id] border[room.id] = true if (room.wayto || {}).keys.any? { |dest| inside[dest.to_i] } end border.keys.sort end end |
#found_at_uid?(uid) ⇒ Boolean
Whether the creature is found at the given game room uid. Checks the ranges directly, so no expansion cost.
194 195 196 |
# File 'documented/gemstone/creature.rb', line 194 def found_at_uid?(uid) @areas.any? { |a| Array(a[:uids]).any? { |r| r.cover?(uid) } } end |
#has_blood? ⇒ Boolean?
Returns whether the bestiary template says the creature has blood.
253 254 255 |
# File 'documented/gemstone/creature.rb', line 253 def has_blood? @blood end |
#has_bones? ⇒ Boolean?
Returns whether the bestiary template says the creature has bones.
260 261 262 |
# File 'documented/gemstone/creature.rb', line 260 def has_bones? @bones end |
#has_limbs? ⇒ Boolean?
Returns whether Limb Disruption (708) has limbs to target on this creature ("The X has no limbs left!" on a fresh target = false).
268 269 270 |
# File 'documented/gemstone/creature.rb', line 268 def has_limbs? @limbs end |
#muggable? ⇒ Boolean?
Returns whether the bestiary template says the creature can be mugged.
294 295 296 |
# File 'documented/gemstone/creature.rb', line 294 def muggable? @muggable end |
#rooms ⇒ Object
Every Lich room id the creature is found in, across all areas - ranges combined, deduped, sorted.
212 213 214 |
# File 'documented/gemstone/creature.rb', line 212 def rooms @rooms ||= uids.flat_map { |u| Lich::Common::Map.ids_from_uid(u) }.uniq.sort end |
#rooms_by_area ⇒ Object
name => [Lich room ids] - the per-area display view (eBestiary and friends).
218 219 220 221 222 223 |
# File 'documented/gemstone/creature.rb', line 218 def rooms_by_area @rooms_by_area ||= @areas.to_h do |a| ids = Array(a[:uids]).flat_map { |r| r.flat_map { |u| Lich::Common::Map.ids_from_uid(u) } } [a[:name], ids.uniq.sort] end end |
#sleepable? ⇒ Boolean?
Returns whether the creature can be put to sleep. False comes from the game's own refusal ("does not seem to be affected").
302 303 304 |
# File 'documented/gemstone/creature.rb', line 302 def sleepable? @sleepable end |
#sympathy? ⇒ Boolean?
Returns whether Sympathy (1120) can affect this creature.
287 288 289 |
# File 'documented/gemstone/creature.rb', line 287 def sympathy? @sympathy end |
#uids ⇒ Object
All game uids the creature is found at, expanded from the stored ranges (memoized - ranges stay compact on disk).
188 189 190 |
# File 'documented/gemstone/creature.rb', line 188 def uids @uids ||= @areas.flat_map { |a| Array(a[:uids]).flat_map(&:to_a) }.uniq.sort end |
#witherable? ⇒ Boolean?
Returns whether Wither (1115) has a body to attack on this creature. Not about limbs - the spell strikes all thirteen locations, chest and abdomen most often - and not about corporeality either: it works on non-corporeal undead (wraiths, spectres, lost souls) and fails on the golem and elemental families.
280 281 282 |
# File 'documented/gemstone/creature.rb', line 280 def witherable? @witherable end |