diff options
Diffstat (limited to 'internal/behavior')
| -rw-r--r-- | internal/behavior/behavior.go | 128 | ||||
| -rw-r--r-- | internal/behavior/types.go | 24 |
2 files changed, 99 insertions, 53 deletions
diff --git a/internal/behavior/behavior.go b/internal/behavior/behavior.go index 2ef7fae..646495a 100644 --- a/internal/behavior/behavior.go +++ b/internal/behavior/behavior.go @@ -28,8 +28,7 @@ type TalkConfig struct { } type TalkNode struct { - Message MessageList `yaml:"message"` - Sequence []string `yaml:"sequence,omitempty"` + Messages []string `yaml:"messages,omitempty"` Condition *Condition `yaml:"condition,omitempty"` Options []TalkOption `yaml:"options"` Action *NodeAction `yaml:"action"` @@ -37,9 +36,9 @@ type TalkNode struct { } type TalkOption struct { - Text string `yaml:"text"` - Goto string `yaml:"goto"` - Condition *Condition `yaml:"condition"` + Text string `yaml:"text"` + Goto string `yaml:"goto"` + Condition *Condition `yaml:"condition"` Action *NodeAction `yaml:"action,omitempty"` } @@ -50,31 +49,102 @@ type NodeAction struct { TakeItem string `yaml:"take_item"` Teleport int `yaml:"teleport"` Heal int `yaml:"heal"` - Cost int `yaml:"cost"` - Shop *ShopConfig `yaml:"shop"` - AssignTask bool `yaml:"assign_task"` - SkipTask bool `yaml:"skip_task"` - ExtendTask bool `yaml:"extend_task"` + Credits int `yaml:"credits"` ReputationCost int `yaml:"reputation_cost"` - Sawmill bool `yaml:"sawmill"` ApsNode bool `yaml:"aps_node"` } +// ShopConfig is a root-level mob property (mob YAML `shop:`). Buy prices are +// always 100% of the item's value; sell prices follow the OSRS store formula: +// +// sell = value * max(10, buy_percentage - stock*change_percentage) / 100 +// +// where stock is the shop's current holding of that item. BuyPercentage +// defaults to 40 and ChangePercentage to 3 when unset (see ShopBuyPercentage / +// ShopChangePercentage). type ShopConfig struct { - Message string `yaml:"message"` - Items []ShopItem `yaml:"items"` + Message string `yaml:"message"` + BuyPercentage float64 `yaml:"buy_percentage"` // sell price at zero stock, % of value (default 40) + ChangePercentage float64 `yaml:"change_percentage"` // sell price drop per unit of stock, % of value (default 3) + RestockTicks float64 `yaml:"restock_ticks"` // shop-wide default restock interval + BuysAnything *bool `yaml:"buys_anything"` // buy items not in the list (default true) + Items []ShopItem `yaml:"items"` } type ShopItem struct { - ItemID string `yaml:"item_id"` - BuyPrice int `yaml:"buy_price"` - SellPrice int `yaml:"sell_price"` + ItemID string `yaml:"item_id"` + Stock int `yaml:"stock"` // target/max stock; also starting stock + RestockTicks float64 `yaml:"restock_ticks"` // per-item restock interval (overrides shop default) +} + +// Shop pricing defaults, applied when the YAML leaves a field at zero. +const ( + ShopBuyPercentage = 40.0 + ShopChangePercentage = 3.0 + ShopMinSellPercent = 10.0 + ShopDefaultRestock = 100.0 +) + +// EffectiveBuyPercentage returns BuyPercentage or the default. +func (c *ShopConfig) EffectiveBuyPercentage() float64 { + if c.BuyPercentage > 0 { + return c.BuyPercentage + } + return ShopBuyPercentage +} + +// EffectiveChangePercentage returns ChangePercentage or the default. +func (c *ShopConfig) EffectiveChangePercentage() float64 { + if c.ChangePercentage > 0 { + return c.ChangePercentage + } + return ShopChangePercentage +} + +// Buys reports whether the shop will buy items not in its item list. +func (c *ShopConfig) Buys() bool { + return c.BuysAnything == nil || *c.BuysAnything +} + +// SellPrice computes the per-unit price the shop pays for an item of the given +// value when the shop currently holds `stock` of it. +func (c *ShopConfig) SellPrice(value, stock int) int { + if stock < 0 { + stock = 0 + } + pct := c.EffectiveBuyPercentage() - float64(stock)*c.EffectiveChangePercentage() + if pct < ShopMinSellPercent { + pct = ShopMinSellPercent + } + return int(float64(value) * pct / 100.0) +} + +// RestockInterval returns the effective restock interval (in ticks) for an item, +// honoring the per-item override, then the shop default, then the global default. +func (c *ShopConfig) RestockInterval(item *ShopItem) int { + if item != nil && item.RestockTicks > 0 { + return int(item.RestockTicks) + } + if c.RestockTicks > 0 { + return int(c.RestockTicks) + } + return int(ShopDefaultRestock) +} + +// FindItem returns the configured shop item with the given id, or nil. +func (c *ShopConfig) FindItem(itemID string) *ShopItem { + for i := range c.Items { + if c.Items[i].ItemID == itemID { + return &c.Items[i] + } + } + return nil } type Condition struct { Flag string `yaml:"flag"` Value any `yaml:"value"` - Not bool `yaml:"not"` + Not bool `yaml:"not"` PlayerFlag string `yaml:"player_flag"` HasItem string `yaml:"has_item"` MinCredits int `yaml:"min_credits"` @@ -83,16 +153,16 @@ type Condition struct { } type UseConfig struct { - StartMessage string `yaml:"start_message"` - TicksPerCycle float64 `yaml:"ticks_per_cycle"` - Consume map[string]int `yaml:"consume"` - Reward DropEntry `yaml:"reward"` - FailMessage string `yaml:"fail_message"` - Success *SuccessFormula `yaml:"success"` - Skill string `yaml:"skill"` - Level int `yaml:"level"` - XP int `yaml:"xp"` - SuccessMessage string `yaml:"success_message"` - EndMessage string `yaml:"end_message"` - BroadcastMessage string `yaml:"broadcast_message"` + StartMessage string `yaml:"start_message"` + TicksPerCycle float64 `yaml:"ticks_per_cycle"` + Consume map[string]int `yaml:"consume"` + Reward DropEntry `yaml:"reward"` + FailMessage string `yaml:"fail_message"` + Success *SuccessFormula `yaml:"success"` + Skill string `yaml:"skill"` + Level int `yaml:"level"` + XP int `yaml:"xp"` + SuccessMessage string `yaml:"success_message"` + EndMessage string `yaml:"end_message"` + BroadcastMessage string `yaml:"broadcast_message"` } diff --git a/internal/behavior/types.go b/internal/behavior/types.go index c59a95e..29e2631 100644 --- a/internal/behavior/types.go +++ b/internal/behavior/types.go @@ -1,10 +1,7 @@ package behavior import ( - "math/rand" "strings" - - "gopkg.in/yaml.v3" ) func WordPrefixMatch(input, name string) bool { @@ -224,24 +221,3 @@ type DropEntry struct { type DropTableDef struct { Drops []DropEntry `yaml:"drops"` } - -type MessageList []string - -func (ml *MessageList) UnmarshalYAML(value *yaml.Node) error { - if value.Kind == yaml.ScalarNode { - var s string - if err := value.Decode(&s); err != nil { - return err - } - *ml = MessageList{s} - return nil - } - return value.Decode((*[]string)(ml)) -} - -func (ml MessageList) Pick() string { - if len(ml) == 0 { - return "" - } - return ml[rand.Intn(len(ml))] -} |
