aboutsummaryrefslogtreecommitdiff
path: root/internal/world/hazard.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/world/hazard.go')
-rw-r--r--internal/world/hazard.go98
1 files changed, 76 insertions, 22 deletions
diff --git a/internal/world/hazard.go b/internal/world/hazard.go
index ac2a6a6..c08c2bb 100644
--- a/internal/world/hazard.go
+++ b/internal/world/hazard.go
@@ -7,31 +7,85 @@ import (
"gopkg.in/yaml.v3"
"thehouseoficarus/internal/behavior"
+ "thehouseoficarus/internal/combat"
)
-// HazardDef describes an environmental threat attached to a room (a sandstorm,
-// solar radiation, falling rocks, etc.). It is a shared, ID-referenced
-// definition loaded from data/hazards/<id>.yaml, so one hazard can be reused
-// across a whole area and resolved by name for a future `examine` command.
-//
-// A hazard rolls an attack against everyone in the room every Speed ticks using
-// the SAME combat formulas as a mob: accuracy comes from Attack/AttackBonus, max
-// hit from MaxHit, and the player defends with their Defense level + equipment
-// bonus selected by AttackType (so existing armour and the Defense skill apply).
-// Tech protection (Kinetic Barrier / Projectile Screen / Neural Firewall) keys
-// off AttackType automatically.
+type HazardCombatBonuses struct {
+ AttackBonus int `yaml:"attack_bonus"`
+ StrengthBonus int `yaml:"strength_bonus"`
+ RangedBonus int `yaml:"ranged_bonus"`
+ RangedStrengthBonus int `yaml:"ranged_strength_bonus"`
+ ScienceBonus int `yaml:"science_bonus"`
+ SciencePercentBonus int `yaml:"science_percent_bonus"`
+}
+
+type HazardCombatStats struct {
+ Attack int `yaml:"attack"`
+ Strength int `yaml:"strength"`
+ Ranged int `yaml:"ranged"`
+ Science int `yaml:"science"`
+ Speed float64 `yaml:"speed"`
+ MaxMeleeHit int `yaml:"max_melee_hit"`
+ MaxRangedHit int `yaml:"max_ranged_hit"`
+ MaxScienceHit int `yaml:"max_science_hit"`
+ Bonuses HazardCombatBonuses `yaml:"bonuses"`
+}
+
+type HazardCombat struct {
+ AttackTypes []string `yaml:"attack_types"`
+ Stats HazardCombatStats `yaml:"stats"`
+}
+
+// HazardDef describes an environmental threat attached to a room. Its combat
+// block mirrors a mob's combat block (attack_types, stats with per-type max hits
+// and bonuses) but has NO defense stats — the hazard is never attacked, it only
+// attacks the player. Speed is read from combat.stats.speed (default 5).
type HazardDef struct {
- Name string `yaml:"name"`
- Description string `yaml:"description"`
- AttackType string `yaml:"attack_type"` // stab/slash/crush/ranged/science
- Attack int `yaml:"attack"`
- AttackBonus int `yaml:"attack_bonus"`
- MaxHit int `yaml:"max_hit"`
- Speed float64 `yaml:"speed"` // ticks between hazard rolls
- WarnMessage string `yaml:"warn_message"`
- HitMessage string `yaml:"hit_message"` // accepts a single %d for damage
- MissMessage string `yaml:"miss_message"`
- RequiredItem string `yaml:"required_item"` // equipped item that negates the hazard
+ Name string `yaml:"name"`
+ Description string `yaml:"description"`
+ Combat *HazardCombat `yaml:"combat,omitempty"`
+ WarnMessage string `yaml:"warn_message"`
+ HitMessage string `yaml:"hit_message"` // accepts a single %d for damage
+ MissMessage string `yaml:"miss_message"`
+ RequiredItem string `yaml:"required_item"` // equipped item that negates the hazard
+}
+
+// AttackType returns the hazard's active attack type: the first melee type from
+// attack_types, falling back to the first entry, defaulting to crush.
+func (d *HazardDef) AttackType() string {
+ if d.Combat == nil || len(d.Combat.AttackTypes) == 0 {
+ return combat.DefaultAttackType
+ }
+ for _, t := range d.Combat.AttackTypes {
+ if combat.IsMeleeType(t) {
+ return t
+ }
+ }
+ return d.Combat.AttackTypes[0]
+}
+
+// SpeedTicks returns the hazard's tick interval (default 5).
+func (d *HazardDef) SpeedTicks() float64 {
+ if d.Combat == nil || d.Combat.Stats.Speed <= 0 {
+ return 5
+ }
+ return d.Combat.Stats.Speed
+}
+
+// EffectiveMaxScienceHit applies the hazard's science percent bonus to its max
+// science hit.
+func (d *HazardDef) EffectiveMaxScienceHit() int {
+ if d.Combat == nil {
+ return 0
+ }
+ base := d.Combat.Stats.MaxScienceHit
+ if d.Combat.Stats.Bonuses.SciencePercentBonus > 0 {
+ base = int(float64(base) * (1.0 + float64(d.Combat.Stats.Bonuses.SciencePercentBonus)/100.0))
+ }
+ if base < 1 {
+ base = 1
+ }
+ return base
}
// LoadHazard returns the shared hazard definition for id, caching results.