aboutsummaryrefslogtreecommitdiff
path: root/internal/behavior/behavior.go
blob: 06e2715b08704fcb1f94a1089e5da0a12b36915c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package behavior

type GatherConfig struct {
	Skill            string         `yaml:"skill"`
	Tools            []string       `yaml:"tools"`
	Bait             string         `yaml:"bait"`
	Success          SuccessFormula `yaml:"success"`
	GatherMessage    string         `yaml:"gather_message"`
	DepletedMessage  string         `yaml:"depleted_message"`
	ExhaustedMessage string         `yaml:"exhausted_message"`
	FailMessage      string         `yaml:"fail_message"`
	Drops            []DropEntry    `yaml:"drops"`
	RespawnTimer     float64        `yaml:"respawn_timer"`
	RespawnBroadcast string         `yaml:"respawn_broadcast"`
	DepleteTimer     float64        `yaml:"deplete_timer"`
	NestChance       int            `yaml:"nest_chance"`
	BroadcastMessage string         `yaml:"broadcast_message"`
}

type SuccessFormula struct {
	Base     float64 `yaml:"base"`
	PerLevel float64 `yaml:"per_level"`
	Cap      float64 `yaml:"cap"`
}

type TalkConfig struct {
	Nodes map[string]TalkNode `yaml:"nodes"`
}

type TalkNode struct {
	Messages  []string     `yaml:"messages,omitempty"`
	Condition *Condition   `yaml:"condition,omitempty"`
	Options   []TalkOption `yaml:"options"`
	Action    *NodeAction  `yaml:"action"`
	Goto      string       `yaml:"goto,omitempty"`
}

type TalkOption struct {
	Text      string      `yaml:"text"`
	Goto      string      `yaml:"goto"`
	Condition *Condition  `yaml:"condition"`
	Action    *NodeAction `yaml:"action,omitempty"`
}

type NodeAction struct {
	SetFlags       map[string]any `yaml:"set_flags"`
	SetPlayerFlags map[string]any `yaml:"set_player_flags"`
	GiveItem       string         `yaml:"give_item"`
	TakeItem       string         `yaml:"take_item"`
	Teleport       int            `yaml:"teleport"`
	Heal           int            `yaml:"heal"`
	Credits        int            `yaml:"credits"`
	ReputationCost int            `yaml:"reputation_cost"`
	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"`
	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)
	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"`
	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 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 {
		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 configurable global default.
func (c *ShopConfig) RestockInterval(item *ShopItem) int {
	if item != nil && item.RestockTicks > 0 {
		return int(item.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"`
	PlayerFlag string      `yaml:"player_flag"`
	HasItem    string      `yaml:"has_item"`
	MinCredits int         `yaml:"min_credits"`
	AllOf      []Condition `yaml:"all_of"`
	AnyOf      []Condition `yaml:"any_of"`
}

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"`
}