aboutsummaryrefslogtreecommitdiff
path: root/internal/object
diff options
context:
space:
mode:
Diffstat (limited to 'internal/object')
-rw-r--r--internal/object/doc.go5
-rw-r--r--internal/object/item.go211
-rw-r--r--internal/object/item_store.go78
-rw-r--r--internal/object/object.go68
-rw-r--r--internal/object/store.go4
5 files changed, 37 insertions, 329 deletions
diff --git a/internal/object/doc.go b/internal/object/doc.go
new file mode 100644
index 0000000..04aa4f7
--- /dev/null
+++ b/internal/object/doc.go
@@ -0,0 +1,5 @@
+// Package object defines room-object definitions (ObjectDef), safespot
+// configuration, use interactions, and the ObjectStore YAML loader. Objects
+// are placed in rooms via room YAML and may provide gather/talk/use/safespot
+// behaviors. Distinct from the item package (item definitions).
+package object
diff --git a/internal/object/item.go b/internal/object/item.go
deleted file mode 100644
index b90432d..0000000
--- a/internal/object/item.go
+++ /dev/null
@@ -1,211 +0,0 @@
-package object
-
-import (
- "fmt"
- "strings"
-
- "gopkg.in/yaml.v3"
-)
-
-func WordPrefixMatch(input, name string) bool {
- inputWords := strings.Fields(strings.ToLower(input))
- if len(inputWords) == 0 {
- return false
- }
- nameWords := strings.Fields(strings.ToLower(name))
- for _, iw := range inputWords {
- found := false
- for _, nw := range nameWords {
- if strings.HasPrefix(nw, iw) || strings.HasPrefix(iw, nw) {
- found = true
- break
- }
- }
- if !found {
- return false
- }
- }
- return true
-}
-
-type EquipSlot string
-
-const (
- SlotHead EquipSlot = "head"
- SlotNeck EquipSlot = "neck"
- SlotTorso EquipSlot = "torso"
- SlotLegs EquipSlot = "legs"
- SlotHands EquipSlot = "hands"
- SlotFeet EquipSlot = "feet"
- SlotBack EquipSlot = "back"
- SlotAmmo EquipSlot = "ammo"
- SlotMainHand EquipSlot = "main_hand"
- SlotOffHand EquipSlot = "off_hand"
- SlotRing EquipSlot = "ring"
-)
-
-type WeaponType string
-
-const (
- WeaponMelee WeaponType = "melee"
- WeaponRanged WeaponType = "ranged"
- WeaponScience WeaponType = "science"
-)
-
-type ItemDef struct {
- ID string `yaml:"id"`
- Name string `yaml:"name"`
- Color string `yaml:"color"`
- Description string `yaml:"description"`
- Value int `yaml:"value"`
- Stackable bool `yaml:"stackable"`
- EquipSlot EquipSlot `yaml:"equip_slot"`
- WeaponType WeaponType `yaml:"weapon_type"`
- AttackType string `yaml:"attack_type"`
- Stats ItemStats `yaml:"stats"`
- Speed float64 `yaml:"speed"`
- Requirements map[string]int `yaml:"requirements,omitempty"`
- ToolType string `yaml:"tool_type"`
- ToolSpeed float64 `yaml:"tool_speed"`
- BurnTicks float64 `yaml:"burn_ticks"`
- FireLevel int `yaml:"fire_level"`
- FireXP int `yaml:"fire_xp"`
- Quality int `yaml:"quality"`
- MaxQuality int `yaml:"max_quality"`
- SearchTable string `yaml:"search_table"`
- SearchMiscTable string `yaml:"search_misc_table"`
- SearchTicks float64 `yaml:"search_ticks"`
- SearchMessage string `yaml:"search_message"`
- HealValue int `yaml:"heal_value"`
- EatMessage string `yaml:"eat_message"`
- Craft CraftList `yaml:"craft,omitempty"`
- ProvidesJunk string `yaml:"provides_junk,omitempty"`
- FarmPatchType string `yaml:"farm_patch_type"`
- FarmLevel int `yaml:"farm_level"`
- FarmPlantXP int `yaml:"farm_plant_xp"`
- FarmHarvestXP int `yaml:"farm_harvest_xp"`
- FarmStages int `yaml:"farm_stages"`
- FarmProduct string `yaml:"farm_product"`
- FarmMinYield int `yaml:"farm_min_yield"`
- FarmMaxYield int `yaml:"farm_max_yield"`
- PotionEffect string `yaml:"potion_effect"`
- PotionBonus int `yaml:"potion_bonus"`
- PotionDuration float64 `yaml:"potion_duration"`
- Recoverable bool `yaml:"recoverable"`
-}
-
-type CraftList []CraftDef
-
-func (cl *CraftList) UnmarshalYAML(value *yaml.Node) error {
- switch value.Kind {
- case yaml.SequenceNode:
- return value.Decode((*[]CraftDef)(cl))
- case yaml.MappingNode:
- var single CraftDef
- if err := value.Decode(&single); err != nil {
- return err
- }
- *cl = []CraftDef{single}
- return nil
- }
- return fmt.Errorf("craft: expected mapping or sequence")
-}
-
-func (d *ItemDef) FirstCraft() *CraftDef {
- if len(d.Craft) == 0 {
- return nil
- }
- return &d.Craft[0]
-}
-
-func (d *ItemDef) FindCraft(fn func(CraftDef) bool) *CraftDef {
- for i := range d.Craft {
- if fn(d.Craft[i]) {
- return &d.Craft[i]
- }
- }
- return nil
-}
-
-func (d *ItemDef) MatchingCrafts(fn func(CraftDef) bool) []*CraftDef {
- var result []*CraftDef
- for i := range d.Craft {
- if fn(d.Craft[i]) {
- result = append(result, &d.Craft[i])
- }
- }
- return result
-}
-
-type ConsumeEntry struct {
- Items []string `yaml:"items"`
- Quantity int `yaml:"quantity"`
- Byproducts []string `yaml:"byproducts"`
-}
-
-type CraftStep struct {
- Tick float64 `yaml:"tick"`
- Message string `yaml:"message"`
-}
-
-type SuccessFormula struct {
- Base float64 `yaml:"base"`
- PerLevel float64 `yaml:"per_level"`
- Cap float64 `yaml:"cap"`
-}
-
-type CraftDef struct {
- Type string `yaml:"type"`
- Skill string `yaml:"skill"`
- Level int `yaml:"level"`
- XP int `yaml:"xp"`
- Wait float64 `yaml:"wait"`
- Station []string `yaml:"station"`
- Tool string `yaml:"tool"`
- Consume []ConsumeEntry `yaml:"consume"`
- OutputQty int `yaml:"output_qty"`
- Fail string `yaml:"fail"`
- Message string `yaml:"message"`
- FailMessage string `yaml:"fail_message"`
- StartMessage string `yaml:"start_message"`
- EndMessage string `yaml:"end_message"`
- Steps []CraftStep `yaml:"steps"`
- Success *SuccessFormula `yaml:"success"`
-}
-
-func (c *CraftDef) EffectiveSkill() string {
- if c.Skill != "" {
- return c.Skill
- }
- return c.Type
-}
-
-type ItemStats struct {
- StabAttack int `yaml:"stab_attack"`
- SlashAttack int `yaml:"slash_attack"`
- CrushAttack int `yaml:"crush_attack"`
- ScienceAttack int `yaml:"science_attack"`
- RangedAttack int `yaml:"ranged_attack"`
-
- StabDefense int `yaml:"stab_defense"`
- SlashDefense int `yaml:"slash_defense"`
- CrushDefense int `yaml:"crush_defense"`
- ScienceDefense int `yaml:"science_defense"`
- RangedDefense int `yaml:"ranged_defense"`
-
- StrengthBonus int `yaml:"strength_bonus"`
- RangedStrength int `yaml:"ranged_strength"`
- ScienceDamage int `yaml:"science_damage"`
- TechnologyBonus int `yaml:"technology_bonus"`
-}
-
-func (d *ItemDef) MatchesName(input string) bool {
- lower := strings.ToLower(strings.TrimSpace(input))
- if lower == "" {
- return false
- }
- if strings.ToLower(d.Name) == lower {
- return true
- }
- return WordPrefixMatch(lower, d.Name)
-}
diff --git a/internal/object/item_store.go b/internal/object/item_store.go
deleted file mode 100644
index e930220..0000000
--- a/internal/object/item_store.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package object
-
-import (
- "fmt"
- "os"
- "path/filepath"
-
- "thehouseoficarus/internal/action"
- "gopkg.in/yaml.v3"
-)
-
-type ItemStore struct {
- dataDir string
- pathIndex map[string]string
- cache map[string]*ItemDef
-}
-
-func NewItemStore(dataDir string) *ItemStore {
- s := &ItemStore{
- dataDir: dataDir,
- pathIndex: action.BuildPathIndex(filepath.Join(dataDir, "items")),
- cache: make(map[string]*ItemDef),
- }
- return s
-}
-
-func (s *ItemStore) Load(id string) (*ItemDef, error) {
- if def, ok := s.cache[id]; ok {
- return def, nil
- }
- path, ok := s.pathIndex[id]
- if !ok {
- return nil, fmt.Errorf("read item %s: no such item", id)
- }
- data, err := os.ReadFile(path)
- if err != nil {
- return nil, fmt.Errorf("read item %s: %w", id, err)
- }
- var def ItemDef
- if err := yaml.Unmarshal(data, &def); err != nil {
- return nil, fmt.Errorf("parse item %s: %w", id, err)
- }
- def.ID = id
- s.cache[id] = &def
- return &def, nil
-}
-
-func (s *ItemStore) LoadAll() ([]*ItemDef, error) {
- dir := filepath.Join(s.dataDir, "items")
- var defs []*ItemDef
- err := action.WalkYAMLDir(dir, func(path, id string, data []byte) error {
- if def, ok := s.cache[id]; ok {
- defs = append(defs, def)
- return nil
- }
- var def ItemDef
- if err := yaml.Unmarshal(data, &def); err != nil {
- return fmt.Errorf("parse item %s: %w", id, err)
- }
- def.ID = id
- s.cache[id] = &def
- defs = append(defs, &def)
- return nil
- })
- return defs, err
-}
-
-func (s *ItemStore) PathIndex() map[string]string {
- return s.pathIndex
-}
-
-func (s *ItemStore) IDSet() map[string]bool {
- ids := make(map[string]bool)
- for id := range s.pathIndex {
- ids[id] = true
- }
- return ids
-}
diff --git a/internal/object/object.go b/internal/object/object.go
index f7a0e7b..8d1e02e 100644
--- a/internal/object/object.go
+++ b/internal/object/object.go
@@ -1,44 +1,34 @@
package object
-import "thehouseoficarus/internal/action"
+import "thehouseoficarus/internal/behavior"
type UseInteraction struct {
- Item string `yaml:"item_id"`
- Condition *action.Condition `yaml:"condition"`
- Message string `yaml:"message"`
- Action *action.NodeAction `yaml:"action"`
+ Item string `yaml:"item_id"`
+ Condition *behavior.Condition `yaml:"condition"`
+ Message string `yaml:"message"`
+ Action *behavior.NodeAction `yaml:"action"`
}
type ObjectDef struct {
- ID string `yaml:"id"`
- Name string `yaml:"name"`
- Aliases []string `yaml:"aliases"`
- Color string `yaml:"color"`
- Hidden bool `yaml:"hidden"`
- InRoomDescription string `yaml:"inroom_description"`
- RemovalItem string `yaml:"removal_item"`
- Description string `yaml:"description"`
- Descriptions []ConditionalDesc `yaml:"descriptions,omitempty"`
- UseInteractions []UseInteraction `yaml:"use_interactions"`
- StealTable string `yaml:"steal_table"`
- StealLevel int `yaml:"steal_level"`
- StealXP int `yaml:"steal_xp"`
- StealSpeed float64 `yaml:"steal_speed"`
- GuardMob string `yaml:"guard_mob"`
+ ID string `yaml:"id"`
+ Name string `yaml:"name"`
+ Aliases []string `yaml:"aliases"`
+ Color string `yaml:"color"`
+ Hidden bool `yaml:"hidden"`
+ InRoomDescription string `yaml:"inroom_description"`
+ RemovalItem string `yaml:"removal_item"`
+ Description behavior.DescList `yaml:"description"`
+ UseInteractions []UseInteraction `yaml:"use_interactions"`
+ StealTable string `yaml:"steal_table"`
+ StealLevel int `yaml:"steal_level"`
+ StealXP int `yaml:"steal_xp"`
+ StealSpeed float64 `yaml:"steal_speed"`
+ GuardMob string `yaml:"guard_mob"`
- Gather *action.GatherConfig `yaml:"gather,omitempty"`
- Talk *action.TalkConfig `yaml:"talk,omitempty"`
- Use *action.UseConfig `yaml:"use,omitempty"`
- Safespot *SafespotConfig `yaml:"safespot,omitempty"`
-}
-
-// ConditionalDesc is a description variant gated by a condition. They are
-// evaluated in order per-looker; the first whose condition passes (or has no
-// condition) wins. If a def has Descriptions but none match for a given player,
-// the object is treated as absent for that player.
-type ConditionalDesc struct {
- Text string `yaml:"text"`
- Condition *action.Condition `yaml:"condition"`
+ Gather *behavior.GatherConfig `yaml:"gather,omitempty"`
+ Talk *behavior.TalkConfig `yaml:"talk,omitempty"`
+ Use *behavior.UseConfig `yaml:"use,omitempty"`
+ Safespot *SafespotConfig `yaml:"safespot,omitempty"`
}
type SafespotConfig struct {
@@ -58,11 +48,13 @@ type SafespotLevel struct {
DegradeMessage string `yaml:"degrade_message"`
}
-func (d *ObjectDef) IsGatherable() bool { return d.Gather != nil }
-func (d *ObjectDef) IsTalkable() bool { return d.Talk != nil }
-func (d *ObjectDef) IsUsable() bool { return d.Use != nil }
-func (d *ObjectDef) IsSafespot() bool { return d.Safespot != nil }
-func (d *ObjectDef) IsInteractable() bool { return d.Gather != nil || d.Talk != nil || d.Use != nil || d.Safespot != nil }
+func (d *ObjectDef) IsGatherable() bool { return d.Gather != nil }
+func (d *ObjectDef) IsTalkable() bool { return d.Talk != nil }
+func (d *ObjectDef) IsUsable() bool { return d.Use != nil }
+func (d *ObjectDef) IsSafespot() bool { return d.Safespot != nil }
+func (d *ObjectDef) IsInteractable() bool {
+ return d.Gather != nil || d.Talk != nil || d.Use != nil || d.Safespot != nil
+}
func (d *ObjectDef) BehaviorType() string {
switch {
diff --git a/internal/object/store.go b/internal/object/store.go
index cf8b4f9..a441705 100644
--- a/internal/object/store.go
+++ b/internal/object/store.go
@@ -5,8 +5,8 @@ import (
"os"
"path/filepath"
- "thehouseoficarus/internal/action"
"gopkg.in/yaml.v3"
+ "thehouseoficarus/internal/behavior"
)
type ObjectStore struct {
@@ -18,7 +18,7 @@ type ObjectStore struct {
func NewObjectStore(dataDir string) *ObjectStore {
return &ObjectStore{
dataDir: dataDir,
- pathIndex: action.BuildPathIndex(filepath.Join(dataDir, "objects")),
+ pathIndex: behavior.BuildPathIndex(filepath.Join(dataDir, "objects")),
cache: make(map[string]*ObjectDef),
}
}