Class: Lich::Gemstone::CreatureInstance

Inherits:
Object
  • Object
show all
Defined in:
documented/gemstone/creature.rb

Overview

Individual creature instance (runtime tracking with ID)

Represents a specific creature in the game world, tracked by unique ID. Manages runtime state including damage, injuries, status effects, and UCS data.

Instances are automatically registered and cleaned up based on age. Max registry size prevents memory exhaustion during extended play.

Examples:

Register and track a creature

creature = CreatureInstance.register("troll", 1234, "troll")
creature.add_damage(50)
creature.add_status(:stunned)
puts creature.hp_percent  # => 75.0

Constant Summary collapse

BODY_PARTS =
%w[abdomen back chest head leftArm leftEye leftFoot leftHand leftLeg neck nerves rightArm rightEye rightFoot rightHand rightLeg]
UCS_TTL =

UCS data expires after 2 minutes

120
UCS_SMITE_TTL =

Smite effect expires after 15 seconds

15
STATUS_DURATIONS =

Status effect durations (in seconds) for auto-cleanup nil = no auto-cleanup (waits for removal message)

{
  'breeze'      => 6, # 6 seconds roundtime
  'bind'        => 10, # 10 seconds typical
  'web'         => 8, # 8 seconds typical
  'entangle'    => 10, # 10 seconds typical
  'hypnotism'   => 12, # 12 seconds typical
  'calm'        => 15, # 15 seconds typical
  'mass_calm'   => 15, # 15 seconds typical
  'sleep'       => 8, # 8 seconds typical (can wake early)
  # Statuses with reliable removal messages - no duration needed
  'stunned'     => nil, # Has removal messages
  'immobilized' => nil, # Has removal messages
  'prone'       => nil,         # Has removal messages
  'blind'       => nil,         # Has removal messages
  'sunburst'    => nil, # Has removal messages
  'webbed'      => nil, # Has removal messages
  'poisoned'    => nil # Has removal messages
}.freeze
@@instances =
{}
@@max_size =
1000
@@auto_register =
true

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(id, noun, name) ⇒ CreatureInstance

Initialize a new creature instance

Parameters:

  • id (Integer, String)

    Unique creature ID from game

  • noun (String)

    Creature noun (short name)

  • name (String)

    Full creature name for template lookup



260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'documented/gemstone/creature.rb', line 260

def initialize(id, noun, name)
  @id = id.to_i
  @noun = noun
  @name = name
  @status = []
  @injuries = Hash.new(0)
  @health = nil
  @damage_taken = 0
  @created_at = Time.now
  @fatal_crit = false
  @status_timestamps = {}
  @ucs_position = nil
  @ucs_tierup = nil
  @ucs_smote = nil
  @ucs_updated = nil
end

Instance Attribute Details

#created_atObject

Returns the value of attribute created_at.



226
227
228
# File 'documented/gemstone/creature.rb', line 226

def created_at
  @created_at
end

#damage_takenObject

Returns the value of attribute damage_taken.



226
227
228
# File 'documented/gemstone/creature.rb', line 226

def damage_taken
  @damage_taken
end

#fatal_critObject

Returns the value of attribute fatal_crit.



226
227
228
# File 'documented/gemstone/creature.rb', line 226

def fatal_crit
  @fatal_crit
end

#healthObject

Returns the value of attribute health.



226
227
228
# File 'documented/gemstone/creature.rb', line 226

def health
  @health
end

#idObject

Returns the value of attribute id.



226
227
228
# File 'documented/gemstone/creature.rb', line 226

def id
  @id
end

#injuriesObject

Returns the value of attribute injuries.



226
227
228
# File 'documented/gemstone/creature.rb', line 226

def injuries
  @injuries
end

#nameObject

Returns the value of attribute name.



226
227
228
# File 'documented/gemstone/creature.rb', line 226

def name
  @name
end

#nounObject

Returns the value of attribute noun.



226
227
228
# File 'documented/gemstone/creature.rb', line 226

def noun
  @noun
end

#statusObject

Returns the value of attribute status.



226
227
228
# File 'documented/gemstone/creature.rb', line 226

def status
  @status
end

#status_timestampsObject

Returns the value of attribute status_timestamps.



226
227
228
# File 'documented/gemstone/creature.rb', line 226

def status_timestamps
  @status_timestamps
end

#ucs_positionObject

Get UCS position tier (1-3, or nil if expired)



423
424
425
# File 'documented/gemstone/creature.rb', line 423

def ucs_position
  @ucs_position
end

#ucs_smoteObject

Returns the value of attribute ucs_smote.



226
227
228
# File 'documented/gemstone/creature.rb', line 226

def ucs_smote
  @ucs_smote
end

#ucs_tierupObject

Get UCS tierup vulnerability (or nil if expired)



429
430
431
# File 'documented/gemstone/creature.rb', line 429

def ucs_tierup
  @ucs_tierup
end

#ucs_updatedObject

Returns the value of attribute ucs_updated.



226
227
228
# File 'documented/gemstone/creature.rb', line 226

def ucs_updated
  @ucs_updated
end

Class Method Details

.[](id) ⇒ Object

Lookup creature by ID



606
607
608
# File 'documented/gemstone/creature.rb', line 606

def [](id)
  @@instances[id.to_i]
end

.allObject

Get all registered instances



611
612
613
# File 'documented/gemstone/creature.rb', line 611

def all
  @@instances.values
end

.auto_register?Boolean

Check if auto-registration is enabled

Returns:

  • (Boolean)


560
561
562
# File 'documented/gemstone/creature.rb', line 560

def auto_register?
  @@auto_register
end

.cleanup_old(max_age_seconds = 600) ⇒ Object

Remove old instances (cleanup)



621
622
623
624
625
626
627
628
629
630
631
632
633
# File 'documented/gemstone/creature.rb', line 621

def cleanup_old(max_age_seconds = 600)
  cutoff = Time.now - max_age_seconds
  removed = 0
  @@instances.reject! do |_id, instance|
    if instance.created_at < cutoff
      removed += 1
      true
    else
      false
    end
  end
  removed
end

.clearObject

Clear all instances (session reset)



616
617
618
# File 'documented/gemstone/creature.rb', line 616

def clear
  @@instances.clear
end

.configure(max_size: 1000, auto_register: true) ⇒ Object

Configure registry



554
555
556
557
# File 'documented/gemstone/creature.rb', line 554

def configure(max_size: 1000, auto_register: true)
  @@max_size = max_size
  @@auto_register = auto_register
end

.full?Boolean

Check if registry is full

Returns:

  • (Boolean)


570
571
572
# File 'documented/gemstone/creature.rb', line 570

def full?
  size >= @@max_size
end

.register(name, id, noun = nil) ⇒ CreatureInstance?

Note:

Returns nil if registry is full after cleanup attempts

Register a new creature instance in the global registry

Auto-cleanup triggers if registry is full, progressively removing older creatures (15min, 10min, 5min, 2min thresholds).

Parameters:

  • name (String)

    Creature name for template lookup

  • id (Integer, String)

    Unique creature ID

  • noun (String, nil) (defaults to: nil)

    Creature noun (short name)

Returns:



584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
# File 'documented/gemstone/creature.rb', line 584

def register(name, id, noun = nil)
  return nil unless auto_register?
  return @@instances[id.to_i] if @@instances[id.to_i] # Already exists

  # Auto-cleanup old instances if registry is full - get progressively more aggressive
  if full?
    # Try 15 minutes, then 10, then 5, then 2, then give up
    [900, 600, 300, 120].each do |age_threshold|
      removed = cleanup_old(age_threshold)
      puts "--- Auto-cleanup: removed #{removed} old creatures (threshold: #{age_threshold}s)" if removed > 0 && $creature_debug
      break unless full?
    end
    return nil if full? # Still full after all cleanup attempts
  end

  instance = new(id, noun, name)
  @@instances[id.to_i] = instance
  puts "--- Creature registered: #{name} (#{id})" if $creature_debug
  instance
end

.sizeObject

Get current registry size



565
566
567
# File 'documented/gemstone/creature.rb', line 565

def size
  @@instances.size
end

Instance Method Details

#add_damage(amount) ⇒ void

This method returns an undefined value.

Add damage to creature’s health pool

Examples:

creature.add_damage(50)
puts creature.current_hp  # => 350 (if max_hp is 400)

Parameters:

  • amount (Integer, String)

    Damage points to add



475
476
477
# File 'documented/gemstone/creature.rb', line 475

def add_damage(amount)
  @damage_taken += amount.to_i
end

#add_injury(body_part, amount = 1) ⇒ Object

Add injury/wound to a body part

Examples:

creature.add_injury('leftArm', 3)  # Rank 3 wound to left arm

Parameters:

  • body_part (String, Symbol)

    Body part from BODY_PARTS constant

  • amount (Integer) (defaults to: 1)

    Wound rank to add (default 1)

Raises:

  • (ArgumentError)

    If body part is invalid



441
442
443
444
445
446
# File 'documented/gemstone/creature.rb', line 441

def add_injury(body_part, amount = 1)
  unless BODY_PARTS.include?(body_part.to_s)
    raise ArgumentError, "Invalid body part: #{body_part}"
  end
  @injuries[body_part.to_sym] += amount
end

#add_status(status, duration = nil) ⇒ void

This method returns an undefined value.

Add a status effect to the creature

Status effects like :stunned, :prone, :webbed are tracked with optional auto-expiration based on STATUS_DURATIONS.

Examples:

creature.add_status(:stunned)
creature.add_status(:custom_debuff, 30)

Parameters:

  • status (String, Symbol)

    Status effect to add

  • duration (Integer, nil) (defaults to: nil)

    Duration in seconds (overrides default)



301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'documented/gemstone/creature.rb', line 301

def add_status(status, duration = nil)
  return if @status.include?(status)

  @status << status

  # Set expiration timestamp for timed statuses
  status_key = status.to_s.downcase
  duration ||= STATUS_DURATIONS[status_key]
  if duration
    @status_timestamps[status] = Time.now + duration
    puts "  +status: #{status} (expires in #{duration}s)" if $creature_debug
  else
    puts "  +status: #{status} (no auto-expiry)" if $creature_debug
  end
end

#cleanup_expired_statusesvoid

Note:

This is called frequently - performance sensitive

This method returns an undefined value.

Clean up expired status effects

Called automatically by has_status? and statuses methods. Removes any status whose expiration timestamp has passed.



331
332
333
334
335
336
337
338
339
340
# File 'documented/gemstone/creature.rb', line 331

def cleanup_expired_statuses
  return unless @status_timestamps && !@status_timestamps.empty?

  now = Time.now
  @status_timestamps.select { |_status, expires_at| expires_at <= now }.keys.each do |expired_status|
    @status.delete(expired_status)
    @status_timestamps.delete(expired_status)
    puts "  ~status: #{expired_status} (auto-expired)" if $creature_debug
  end
end

#clear_smoteObject

Clear smote status



410
411
412
413
414
# File 'documented/gemstone/creature.rb', line 410

def clear_smote
  @ucs_smote = nil
  @ucs_updated = Time.now
  puts "  UCS: smote cleared" if $creature_debug
end

#current_hpInteger?

Calculate current hit points

Returns:

  • (Integer, nil)

    Current HP (max_hp - damage_taken), or nil if max_hp unknown



503
504
505
506
# File 'documented/gemstone/creature.rb', line 503

def current_hp
  return nil unless max_hp
  [max_hp - @damage_taken, 0].max
end

#dead?Boolean

Check if creature is dead (0 HP)

Returns:

  • (Boolean)


521
522
523
# File 'documented/gemstone/creature.rb', line 521

def dead?
  current_hp == 0
end

#essential_dataObject

Essential data for this instance



531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
# File 'documented/gemstone/creature.rb', line 531

def essential_data
  {
    id: @id,
    noun: @noun,
    name: @name,
    status: @status,
    injuries: @injuries,
    health: @health,
    damage_taken: @damage_taken,
    max_hp: max_hp,
    current_hp: current_hp,
    hp_percent: hp_percent,
    has_template: has_template?,
    created_at: @created_at,
    ucs_position: ucs_position,
    ucs_tierup: ucs_tierup,
    ucs_smote: smote?
  }
end

#fatal_crit?Boolean

Check if creature died from fatal crit

Returns:

  • (Boolean)


459
460
461
# File 'documented/gemstone/creature.rb', line 459

def fatal_crit?
  @fatal_crit
end

#has_status?(status) ⇒ Boolean

Check if creature has a specific status

Returns:

  • (Boolean)


343
344
345
346
# File 'documented/gemstone/creature.rb', line 343

def has_status?(status)
  cleanup_expired_statuses # Clean up expired statuses first
  @status.include?(status.to_s)
end

#has_template?Boolean

Check if creature has template data

Returns:

  • (Boolean)


286
287
288
# File 'documented/gemstone/creature.rb', line 286

def has_template?
  !template.nil?
end

#hp_percentObject

Calculate HP percentage (0-100)



509
510
511
512
# File 'documented/gemstone/creature.rb', line 509

def hp_percent
  return nil unless max_hp && max_hp > 0
  ((current_hp.to_f / max_hp) * 100).round(1)
end

#injured?(location, threshold = 1) ⇒ Boolean

Check if injured at location

Returns:

  • (Boolean)


449
450
451
# File 'documented/gemstone/creature.rb', line 449

def injured?(location, threshold = 1)
  @injuries[location.to_sym] >= threshold
end

#injured_locations(threshold = 1) ⇒ Object

Get all injured locations



464
465
466
# File 'documented/gemstone/creature.rb', line 464

def injured_locations(threshold = 1)
  @injuries.select { |_, value| value >= threshold }.keys
end

#low_hp?(threshold = 25) ⇒ Boolean

Check if creature is below HP threshold

Returns:

  • (Boolean)


515
516
517
518
# File 'documented/gemstone/creature.rb', line 515

def low_hp?(threshold = 25)
  return false unless hp_percent
  hp_percent <= threshold
end

#mark_fatal_crit!Object

Mark creature as killed by fatal critical hit



454
455
456
# File 'documented/gemstone/creature.rb', line 454

def mark_fatal_crit!
  @fatal_crit = true
end

#max_hpObject

Get maximum HP from template, with fallback



480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
# File 'documented/gemstone/creature.rb', line 480

def max_hp
  # Try template first
  hp = template&.max_hp
  return hp if hp && hp > 0

  # Fall back to combat tracker setting if available
  begin
    if defined?(Lich::Gemstone::Combat::Tracker) &&
       Lich::Gemstone::Combat::Tracker.respond_to?(:fallback_hp)
      fallback = Lich::Gemstone::Combat::Tracker.fallback_hp
      return fallback if fallback && fallback > 0
    end
  rescue
    # Ignore errors accessing tracker
  end

  # Last resort: hardcoded fallback
  400
end

#position_to_tier(pos) ⇒ Integer?

Convert UCS position string/number to tier (1-3)

Parameters:

  • pos (String, Integer)

    Position value (“decent”/“good”/“excellent” or 1/2/3)

Returns:

  • (Integer, nil)

    Tier number (1-3) or nil if invalid



360
361
362
363
364
365
366
367
# File 'documented/gemstone/creature.rb', line 360

def position_to_tier(pos)
  case pos
  when "decent", 1, "1" then 1
  when "good", 2, "2" then 2
  when "excellent", 3, "3" then 3
  else nil
  end
end

#remove_status(status) ⇒ Object

Remove status from creature



318
319
320
321
322
# File 'documented/gemstone/creature.rb', line 318

def remove_status(status)
  @status.delete(status)
  @status_timestamps.delete(status)
  puts "  -status: #{status}" if $creature_debug
end

#reset_damageObject

Reset damage (creature healed or respawned)



526
527
528
# File 'documented/gemstone/creature.rb', line 526

def reset_damage
  @damage_taken = 0
end

#set_ucs_position(position) ⇒ Object

Set UCS position tier



370
371
372
373
374
375
376
377
378
379
380
# File 'documented/gemstone/creature.rb', line 370

def set_ucs_position(position)
  new_tier = position_to_tier(position)
  return unless new_tier

  # Clear tierup if tier changed
  @ucs_tierup = nil if new_tier != @ucs_position

  @ucs_position = new_tier
  @ucs_updated = Time.now
  puts "  UCS: position=#{new_tier}" if $creature_debug
end

#set_ucs_tierup(attack_type) ⇒ Object

Set UCS tierup vulnerability



383
384
385
386
387
# File 'documented/gemstone/creature.rb', line 383

def set_ucs_tierup(attack_type)
  @ucs_tierup = attack_type
  @ucs_updated = Time.now
  puts "  UCS: tierup=#{attack_type}" if $creature_debug
end

#smite!Object

Mark creature as smote (crimson mist applied)



390
391
392
393
394
# File 'documented/gemstone/creature.rb', line 390

def smite!
  @ucs_smote = Time.now
  @ucs_updated = Time.now
  puts "  UCS: smote!" if $creature_debug
end

#smote?Boolean

Check if creature is currently smote

Returns:

  • (Boolean)


397
398
399
400
401
402
403
404
405
406
407
# File 'documented/gemstone/creature.rb', line 397

def smote?
  return false unless @ucs_smote

  # Check if smite effect has expired
  if Time.now - @ucs_smote > UCS_SMITE_TTL
    @ucs_smote = nil
    return false
  end

  true
end

#statusesObject

Get all current statuses



349
350
351
352
# File 'documented/gemstone/creature.rb', line 349

def statuses
  cleanup_expired_statuses # Clean up expired statuses first
  @status.dup
end

#templateCreatureTemplate?

Note:

Result is memoized for performance

Get the static template for this creature

Returns:



281
282
283
# File 'documented/gemstone/creature.rb', line 281

def template
  @template ||= CreatureTemplate[@name]
end

#ucs_expired?Boolean

Check if UCS data has expired

Returns:

  • (Boolean)


417
418
419
420
# File 'documented/gemstone/creature.rb', line 417

def ucs_expired?
  return true unless @ucs_updated
  (Time.now - @ucs_updated) > UCS_TTL
end