Class: Lich::Gemstone::CreatureInstance
- Inherits:
-
Object
- Object
- Lich::Gemstone::CreatureInstance
- Includes:
- Common::CreatureBase
- Defined in:
- documented/gemstone/creature.rb
Overview
Individual GemStone creature instance (runtime tracking with ID).
Shares its id-keyed registry, room roster and <crtrStatus> status/flag
handling with DragonRealms via the Common::CreatureBase mixin; the
GemStone-specific layer here adds bestiary templates, UCS (Unarmed Combat
System) tracking, HP/injury modelling and the GemStone valid_target?
exclusions.
Constant Summary collapse
- BODY_PARTS =
Valid body locations for injury and amputation tracking.
Matches the 17 body parts exposed by limb-targeting messages and the Limb Disruption (708) spell.
%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- STUN_ROUND_SECONDS =
Seconds per stun round. Critical tables express stun in rounds (a rank-5 crit's
stunned: 5means five rounds), while theirroundtimefield is already in seconds - the two units live side by side in the same CritRanks hash, so never mix them. 5- COUP_INCAP_STATUSES =
Statuses that satisfy Coup de Grace's "incapacitated in some way" requirement, unlocking the (rank * 10)% threshold instead of (rank * 5)%. Positional states (prone/kneeling/sitting) are deliberately excluded.
%w[stunned immobilized webbed sleeping bound].freeze
Constants included from Common::CreatureBase
Common::CreatureBase::ALL_CRTR_FLAGS, Common::CreatureBase::CRTR_CLASSIFICATION_FLAGS, Common::CreatureBase::CRTR_STATUS_FLAGS, Common::CreatureBase::KNOWN_FLAG_NAMES, Common::CreatureBase::STATUS_DURATIONS
Instance Attribute Summary collapse
-
#created_at ⇒ Object
Returns the value of attribute created_at.
-
#damage_taken ⇒ Object
Returns the value of attribute damage_taken.
-
#fatal_crit ⇒ Object
Returns the value of attribute fatal_crit.
-
#health ⇒ Object
Returns the value of attribute health.
-
#id ⇒ Object
Returns the value of attribute id.
-
#injuries ⇒ Object
Returns the value of attribute injuries.
-
#name ⇒ Object
Returns the value of attribute name.
-
#noun ⇒ Object
Returns the value of attribute noun.
-
#status ⇒ Object
Returns the value of attribute status.
-
#status_timestamps ⇒ Object
Returns the value of attribute status_timestamps.
-
#ucs_position ⇒ Object
Get UCS position tier (1-3, or nil if expired).
-
#ucs_smote ⇒ Object
Returns the value of attribute ucs_smote.
-
#ucs_tierup ⇒ Object
Get UCS tierup vulnerability (or nil if expired).
-
#ucs_updated ⇒ Object
Returns the value of attribute ucs_updated.
Instance Method Summary collapse
-
#add_damage(amount) ⇒ Object
Add damage to creature.
-
#add_injury(body_part, amount = 1) ⇒ Object
Add injury to body part.
-
#add_stun_estimate(rounds, at: Time.now) ⇒ void
Records a crit-table stun estimate, in rounds.
-
#amputate!(body_part) ⇒ void
Marks a body part as amputated.
-
#amputated?(body_part) ⇒ Boolean
Whether a body part has been amputated.
-
#amputated_parts ⇒ Array<String>
Every amputated body part.
-
#clear_smote ⇒ Object
Clear smote status.
-
#clear_stun_estimate ⇒ void
Clears any crit-derived stun estimate (creature shook off the stun).
-
#coup_eligible?(rank) ⇒ Boolean
Check if creature currently qualifies for Coup de Grace at the given trained rank: at or below (rank * 10)% of max HP when incapacitated, (rank * 5)% otherwise, hard-capped at 200 HP either way.
-
#current_hp ⇒ Object
Calculate current HP (max_hp - damage_taken).
-
#dead? ⇒ Boolean
Check if creature is dead (0 HP).
-
#essential_data ⇒ Object
Essential data for this instance.
-
#fatal_crit? ⇒ Boolean
Check if creature died from fatal crit.
-
#has_template? ⇒ Boolean
Check if creature has template data.
-
#hp_percent ⇒ Object
Calculate HP percentage (0-100).
-
#initialize(id, noun, name) ⇒ void
constructor
Initializes a tracked creature instance.
-
#injured?(location, threshold = 1) ⇒ Boolean
Check if injured at location.
-
#injured_locations(threshold = 1) ⇒ Object
Get all injured locations.
-
#low_hp?(threshold = 25) ⇒ Boolean
Check if creature is below HP threshold.
-
#mark_fatal_crit! ⇒ Object
Mark creature as killed by fatal critical hit.
-
#max_hp ⇒ Object
Get maximum HP from template, with fallback.
-
#muckled? ⇒ Boolean
Creature-side analog of Lich::Gemstone::Status.muckled? (the player's own "can't act right now" check).
-
#position_to_tier(pos) ⇒ Object
Convert position string/number to tier (1-3).
-
#reset_damage ⇒ Object
Reset damage (creature healed or respawned).
-
#set_ucs_position(position) ⇒ Object
Set UCS position tier.
-
#set_ucs_tierup(attack_type) ⇒ Object
Set UCS tierup vulnerability.
-
#smite! ⇒ Object
Mark creature as smote (crimson mist applied).
-
#smote? ⇒ Boolean
Check if creature is currently smote.
-
#stun_rounds ⇒ Integer?
Estimated stun rounds from the last crit, or nil when the estimate has lapsed or stun is no longer active.
-
#stunned_for ⇒ Float
Estimated seconds of stun remaining.
-
#template ⇒ Object
Get the template for this creature.
-
#ucs_expired? ⇒ Boolean
Check if UCS data has expired.
-
#valid_target? ⇒ Boolean
Checks whether this creature should be considered attackable.
Methods included from Common::CreatureBase
Constructor Details
#initialize(id, noun, name) ⇒ void
Initializes a tracked creature instance.
Sets up mutable runtime state: injury tracking per body part (initialized to 0), damage taken, UCS (Unarmed Combat System) position/tierup/smote tracking, and stun estimates from critical hits. Status tracking is delegated to the CreatureBase mixin.
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 |
# File 'documented/gemstone/creature.rb', line 373 def initialize(id, noun, name) @id = id.to_i @noun = noun @name = name initialize_status_tracking @injuries = Hash.new(0) @health = nil @damage_taken = 0 @created_at = Time.now @fatal_crit = false @ucs_position = nil @ucs_tierup = nil @ucs_smote = nil @ucs_updated = nil @amputated = [] @stun_rounds = nil @stun_estimated_until = nil end |
Instance Attribute Details
#created_at ⇒ Object
Returns the value of attribute created_at.
341 342 343 |
# File 'documented/gemstone/creature.rb', line 341 def created_at @created_at end |
#damage_taken ⇒ Object
Returns the value of attribute damage_taken.
341 342 343 |
# File 'documented/gemstone/creature.rb', line 341 def damage_taken @damage_taken end |
#fatal_crit ⇒ Object
Returns the value of attribute fatal_crit.
341 342 343 |
# File 'documented/gemstone/creature.rb', line 341 def fatal_crit @fatal_crit end |
#health ⇒ Object
Returns the value of attribute health.
341 342 343 |
# File 'documented/gemstone/creature.rb', line 341 def health @health end |
#id ⇒ Object
Returns the value of attribute id.
341 342 343 |
# File 'documented/gemstone/creature.rb', line 341 def id @id end |
#injuries ⇒ Object
Returns the value of attribute injuries.
341 342 343 |
# File 'documented/gemstone/creature.rb', line 341 def injuries @injuries end |
#name ⇒ Object
Returns the value of attribute name.
341 342 343 |
# File 'documented/gemstone/creature.rb', line 341 def name @name end |
#noun ⇒ Object
Returns the value of attribute noun.
341 342 343 |
# File 'documented/gemstone/creature.rb', line 341 def noun @noun end |
#status ⇒ Object
Returns the value of attribute status.
341 342 343 |
# File 'documented/gemstone/creature.rb', line 341 def status @status end |
#status_timestamps ⇒ Object
Returns the value of attribute status_timestamps.
341 342 343 |
# File 'documented/gemstone/creature.rb', line 341 def @status_timestamps end |
#ucs_position ⇒ Object
Get UCS position tier (1-3, or nil if expired)
473 474 475 476 |
# File 'documented/gemstone/creature.rb', line 473 def ucs_position return nil if ucs_expired? @ucs_position end |
#ucs_smote ⇒ Object
Returns the value of attribute ucs_smote.
341 342 343 |
# File 'documented/gemstone/creature.rb', line 341 def ucs_smote @ucs_smote end |
#ucs_tierup ⇒ Object
Get UCS tierup vulnerability (or nil if expired)
479 480 481 482 |
# File 'documented/gemstone/creature.rb', line 479 def ucs_tierup return nil if ucs_expired? @ucs_tierup end |
#ucs_updated ⇒ Object
Returns the value of attribute ucs_updated.
341 342 343 |
# File 'documented/gemstone/creature.rb', line 341 def ucs_updated @ucs_updated end |
Instance Method Details
#add_damage(amount) ⇒ Object
Add damage to creature
602 603 604 |
# File 'documented/gemstone/creature.rb', line 602 def add_damage(amount) @damage_taken += amount.to_i end |
#add_injury(body_part, amount = 1) ⇒ Object
Add injury to body part
571 572 573 574 575 576 577 578 579 |
# File 'documented/gemstone/creature.rb', line 571 def add_injury(body_part, amount = 1) unless BODY_PARTS.include?(body_part.to_s) raise ArgumentError, "Invalid body part: #{body_part}" end # An amputated limb cannot accrue further wound rank - it is gone. return if amputated?(body_part) @injuries[body_part.to_sym] += amount end |
#add_stun_estimate(rounds, at: Time.now) ⇒ void
This method returns an undefined value.
Records a crit-table stun estimate, in rounds.
This is deliberately not stored as a timed entry in @status. The
authoritative stun boolean is owned by muckled? false when acting is still unsafe. So the estimate lives
alongside the boolean and is advisory only.
Stun does not cleanly stack, so a new estimate extends but never shortens an existing one.
501 502 503 504 505 506 507 508 509 510 511 |
# File 'documented/gemstone/creature.rb', line 501 def add_stun_estimate(rounds, at: Time.now) rounds = rounds.to_i return if rounds <= 0 expires_at = at + (rounds * STUN_ROUND_SECONDS) return if @stun_estimated_until && @stun_estimated_until >= expires_at @stun_rounds = rounds @stun_estimated_until = expires_at debug_log("+stun estimate: #{rounds} round#{'s' unless rounds == 1} (~#{rounds * STUN_ROUND_SECONDS}s)") end |
#amputate!(body_part) ⇒ void
This method returns an undefined value.
Marks a body part as amputated.
Distinct from @injuries, which accumulates rank: an amputated limb is gone rather than wounded, cannot be wounded further, and stays gone.
550 551 552 553 554 555 556 557 558 |
# File 'documented/gemstone/creature.rb', line 550 def amputate!(body_part) unless BODY_PARTS.include?(body_part.to_s) raise ArgumentError, "Invalid body part: #{body_part}" end return if @amputated.include?(body_part.to_s) @amputated << body_part.to_s debug_log("+amputated: #{body_part}") end |
#amputated?(body_part) ⇒ Boolean
Returns whether a body part has been amputated.
561 562 563 |
# File 'documented/gemstone/creature.rb', line 561 def amputated?(body_part) @amputated.include?(body_part.to_s) end |
#amputated_parts ⇒ Array<String>
Returns every amputated body part.
566 567 568 |
# File 'documented/gemstone/creature.rb', line 566 def amputated_parts @amputated.dup end |
#clear_smote ⇒ Object
Clear smote status
460 461 462 463 464 |
# File 'documented/gemstone/creature.rb', line 460 def clear_smote @ucs_smote = nil @ucs_updated = Time.now debug_log("UCS: smote cleared") end |
#clear_stun_estimate ⇒ void
This method returns an undefined value.
Clears any crit-derived stun estimate (creature shook off the stun).
538 539 540 541 |
# File 'documented/gemstone/creature.rb', line 538 def clear_stun_estimate @stun_rounds = nil @stun_estimated_until = nil end |
#coup_eligible?(rank) ⇒ Boolean
Check if creature currently qualifies for Coup de Grace at the given trained rank: at or below (rank * 10)% of max HP when incapacitated, (rank * 5)% otherwise, hard-capped at 200 HP either way. The cap is what binds on large creatures, so this compares raw HP, not percent.
655 656 657 658 659 660 661 |
# File 'documented/gemstone/creature.rb', line 655 def coup_eligible?(rank) return false unless rank.to_i > 0 return false unless current_hp && max_hp && max_hp > 0 incap = COUP_INCAP_STATUSES.any? { |s| has_status?(s) } threshold = [(max_hp * rank.to_i * (incap ? 10 : 5)) / 100.0, 200].min current_hp <= threshold end |
#current_hp ⇒ Object
Calculate current HP (max_hp - damage_taken)
628 629 630 631 |
# File 'documented/gemstone/creature.rb', line 628 def current_hp return nil unless max_hp [max_hp - @damage_taken, 0].max end |
#dead? ⇒ Boolean
Check if creature is dead (0 HP)
664 665 666 |
# File 'documented/gemstone/creature.rb', line 664 def dead? current_hp == 0 end |
#essential_data ⇒ Object
Essential data for this instance
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 |
# File 'documented/gemstone/creature.rb', line 702 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?, amputated: amputated_parts, stun_rounds: stun_rounds, stunned_for: stunned_for } end |
#fatal_crit? ⇒ Boolean
Check if creature died from fatal crit
592 593 594 |
# File 'documented/gemstone/creature.rb', line 592 def fatal_crit? @fatal_crit end |
#has_template? ⇒ Boolean
Check if creature has template data
403 404 405 |
# File 'documented/gemstone/creature.rb', line 403 def has_template? !template.nil? end |
#hp_percent ⇒ Object
Calculate HP percentage (0-100)
634 635 636 637 |
# File 'documented/gemstone/creature.rb', line 634 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
582 583 584 |
# File 'documented/gemstone/creature.rb', line 582 def injured?(location, threshold = 1) @injuries[location.to_sym] >= threshold end |
#injured_locations(threshold = 1) ⇒ Object
Get all injured locations
597 598 599 |
# File 'documented/gemstone/creature.rb', line 597 def injured_locations(threshold = 1) @injuries.select { |_, value| value >= threshold }.keys end |
#low_hp?(threshold = 25) ⇒ Boolean
Check if creature is below HP threshold
640 641 642 643 |
# File 'documented/gemstone/creature.rb', line 640 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
587 588 589 |
# File 'documented/gemstone/creature.rb', line 587 def mark_fatal_crit! @fatal_crit = true end |
#max_hp ⇒ Object
Get maximum HP from template, with fallback
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 |
# File 'documented/gemstone/creature.rb', line 607 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 |
#muckled? ⇒ Boolean
Creature-side analog of Lich::Gemstone::Status.muckled? (the player's own "can't act right now" check). Deliberately narrower than everything tracked in @status: excludes penalty-only conditions (disoriented), positional ones (prone/kneeling/sitting/flying/ hovering), and calm (the player version excludes that too, tracking it separately) - this is only the statuses that actually prevent acting, not ones that merely penalize or reposition.
691 692 693 694 |
# File 'documented/gemstone/creature.rb', line 691 def muckled? has_status?('webbed') || crtr_flag?(:dead) || dead? || has_status?('stunned') || has_status?('sleeping') || has_status?('immobilized') || has_status?('rooted') end |
#position_to_tier(pos) ⇒ Object
Convert position string/number to tier (1-3)
410 411 412 413 414 415 416 417 |
# File 'documented/gemstone/creature.rb', line 410 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 |
#reset_damage ⇒ Object
Reset damage (creature healed or respawned)
697 698 699 |
# File 'documented/gemstone/creature.rb', line 697 def reset_damage @damage_taken = 0 end |
#set_ucs_position(position) ⇒ Object
Set UCS position tier
420 421 422 423 424 425 426 427 428 429 430 |
# File 'documented/gemstone/creature.rb', line 420 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 debug_log("UCS: position=#{new_tier}") end |
#set_ucs_tierup(attack_type) ⇒ Object
Set UCS tierup vulnerability
433 434 435 436 437 |
# File 'documented/gemstone/creature.rb', line 433 def set_ucs_tierup(attack_type) @ucs_tierup = attack_type @ucs_updated = Time.now debug_log("UCS: tierup=#{attack_type}") end |
#smite! ⇒ Object
Mark creature as smote (crimson mist applied)
440 441 442 443 444 |
# File 'documented/gemstone/creature.rb', line 440 def smite! @ucs_smote = Time.now @ucs_updated = Time.now debug_log("UCS: smote!") end |
#smote? ⇒ Boolean
Check if creature is currently smote
447 448 449 450 451 452 453 454 455 456 457 |
# File 'documented/gemstone/creature.rb', line 447 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 |
#stun_rounds ⇒ Integer?
Estimated stun rounds from the last crit, or nil when the estimate has lapsed or stun is no longer active.
517 518 519 520 |
# File 'documented/gemstone/creature.rb', line 517 def stun_rounds return nil unless stun_estimate_active? @stun_rounds end |
#stunned_for ⇒ Float
Estimated seconds of stun remaining.
Advisory: derived from the critical table, not observed. Returns 0.0
when no estimate is active. Callers deciding whether it is safe to act
should gate on muckled?/has_status?('stunned') and use this only to
size the window.
530 531 532 533 |
# File 'documented/gemstone/creature.rb', line 530 def stunned_for return 0.0 unless stun_estimate_active? [@stun_estimated_until - Time.now, 0.0].max.round(1) end |
#template ⇒ Object
Get the template for this creature. Sentinel-cached so a creature with
no template doesn't redo the name lookup (downcase + boon regex) on
every call the way ||= would.
395 396 397 398 399 400 |
# File 'documented/gemstone/creature.rb', line 395 def template return @template if defined?(@template_looked_up) @template_looked_up = true @template = CreatureTemplate[@name] end |
#ucs_expired? ⇒ Boolean
Check if UCS data has expired
467 468 469 470 |
# File 'documented/gemstone/creature.rb', line 467 def ucs_expired? return true unless @ucs_updated (Time.now - @ucs_updated) > UCS_TTL end |
#valid_target? ⇒ Boolean
Checks whether this creature should be considered attackable.
Uses the same decoy and appendage exclusions as GameObj.targets, but
uses structured death data from
675 676 677 678 679 680 681 682 |
# File 'documented/gemstone/creature.rb', line 675 def valid_target? return false if crtr_flag?(:dead) || dead? return false if @name =~ /^animated\b/i && @name !~ /^animated slush/i return false if @noun =~ /^(?:arm|appendage|claw|limb|pincer|tentacle)s?$|^(?:palpus|palpi)$/i && @name !~ /(?:amaranthine|ghostly|grizzled|ancient) kraken tentacle/i true end |