aboutsummaryrefslogtreecommitdiff
path: root/internal/behavior
diff options
context:
space:
mode:
Diffstat (limited to 'internal/behavior')
-rw-r--r--internal/behavior/behavior.go13
-rw-r--r--internal/behavior/store.go86
2 files changed, 71 insertions, 28 deletions
diff --git a/internal/behavior/behavior.go b/internal/behavior/behavior.go
index 646495a..06e2715 100644
--- a/internal/behavior/behavior.go
+++ b/internal/behavior/behavior.go
@@ -66,7 +66,6 @@ type ShopConfig struct {
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"`
}
@@ -82,9 +81,14 @@ const (
ShopBuyPercentage = 40.0
ShopChangePercentage = 3.0
ShopMinSellPercent = 10.0
- ShopDefaultRestock = 100.0
)
+// ShopDefaultRestock is the fallback restock interval (in ticks) for a shop item
+// that does not set its own restock_ticks. It is configurable via
+// game_constants.shop_default_restock in config.yaml (wired up at startup); the
+// value here is only the default used when no config is loaded.
+var ShopDefaultRestock = 1000.0
+
// EffectiveBuyPercentage returns BuyPercentage or the default.
func (c *ShopConfig) EffectiveBuyPercentage() float64 {
if c.BuyPercentage > 0 {
@@ -120,14 +124,11 @@ func (c *ShopConfig) SellPrice(value, stock int) int {
}
// RestockInterval returns the effective restock interval (in ticks) for an item,
-// honoring the per-item override, then the shop default, then the global default.
+// honoring the per-item override, then the configurable 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)
}
diff --git a/internal/behavior/store.go b/internal/behavior/store.go
index 38cc72b..8112e1c 100644
--- a/internal/behavior/store.go
+++ b/internal/behavior/store.go
@@ -126,7 +126,9 @@ func LoadDropTable(dataDir, id string) (*DropTableDef, error) {
return &dt, nil
}
-func ResolveDrop(dataDir string, drops []DropEntry) *DropEntry {
+// pickWeighted returns a single entry chosen by weight, without resolving any
+// table reference on it. Returns nil if the list is empty or has no weight.
+func pickWeighted(drops []DropEntry) *DropEntry {
if len(drops) == 0 {
return nil
}
@@ -142,29 +144,69 @@ func ResolveDrop(dataDir string, drops []DropEntry) *DropEntry {
for i := range drops {
cumulative += drops[i].Weight
if roll < cumulative {
- if drops[i].Table != "" {
- sub, err := LoadDropTable(dataDir, drops[i].Table)
- if err == nil {
- if resolved := ResolveDrop(dataDir, sub.Drops); resolved != nil {
- qty := drops[i].Quantity
- if qty <= 0 {
- qty = resolved.Quantity
- }
- if qty <= 0 {
- qty = 1
- }
- return &DropEntry{
- ItemID: resolved.ItemID,
- Quantity: qty,
- Depletes: drops[i].Depletes,
- SuccessMessage: drops[i].SuccessMessage,
- }
- }
- }
- return nil
- }
return &drops[i]
}
}
return &drops[0]
}
+
+func ResolveDrop(dataDir string, drops []DropEntry) *DropEntry {
+ picked := pickWeighted(drops)
+ if picked == nil {
+ return nil
+ }
+ if picked.Table != "" {
+ sub, err := LoadDropTable(dataDir, picked.Table)
+ if err != nil {
+ return nil
+ }
+ resolved := ResolveDrop(dataDir, sub.Drops)
+ if resolved == nil {
+ return nil
+ }
+ qty := picked.Quantity
+ if qty <= 0 {
+ qty = resolved.Quantity
+ }
+ if qty <= 0 {
+ qty = 1
+ }
+ return &DropEntry{
+ ItemID: resolved.ItemID,
+ Quantity: qty,
+ Depletes: picked.Depletes,
+ SuccessMessage: picked.SuccessMessage,
+ }
+ }
+ return picked
+}
+
+// ResolveDropList performs one weighted pick from drops. A plain item entry
+// yields a single result. When the picked entry references a sub-table, its
+// Quantity is treated as the number of INDEPENDENT rolls against that sub-table
+// (default 1) — each roll may produce a different item — and all results are
+// returned. Tables are just weighted lists of items; this is skill-independent.
+func ResolveDropList(dataDir string, drops []DropEntry) []DropEntry {
+ picked := pickWeighted(drops)
+ if picked == nil {
+ return nil
+ }
+ if picked.Table == "" {
+ return []DropEntry{*picked}
+ }
+ sub, err := LoadDropTable(dataDir, picked.Table)
+ if err != nil {
+ return nil
+ }
+ rolls := picked.Quantity
+ if rolls <= 0 {
+ rolls = 1
+ }
+ var out []DropEntry
+ for i := 0; i < rolls; i++ {
+ if resolved := ResolveDrop(dataDir, sub.Drops); resolved != nil && resolved.ItemID != "" {
+ out = append(out, *resolved)
+ }
+ }
+ return out
+}