aboutsummaryrefslogtreecommitdiff
path: root/internal/player/player.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-18 05:31:09 -0400
committerhistoria <[not public]>2026-09-18 05:31:09 -0400
commit153acb7f644d755c45533b7c5311e3ffa47e8973 (patch)
tree7645e6f7b65bc3830b078e3b9336d2e6b199d584 /internal/player/player.go
parent87d08f0151e643951769e323995f1c5ed89ee200 (diff)
downloadthehouseoficarus-153acb7f644d755c45533b7c5311e3ffa47e8973.tar.gz
fix: harden engine across casino, economy, concurrency, security, and content
Diffstat (limited to 'internal/player/player.go')
-rw-r--r--internal/player/player.go36
1 files changed, 33 insertions, 3 deletions
diff --git a/internal/player/player.go b/internal/player/player.go
index 0a48b04..f8d9a68 100644
--- a/internal/player/player.go
+++ b/internal/player/player.go
@@ -43,6 +43,21 @@ var AllSkills = []SkillName{
Pharmacy, Thieving, Agility, Construction, Scavenging, Hacking, Assassin, Farming,
}
+// ValidSkill reports whether name is a skill this game defines. A craft
+// recipe's declared skill is item.CraftDef.EffectiveSkill, which is the recipe
+// TYPE, and not every type is a skill: a combine recipe carries type "combine".
+// Level reads an undefined name as a map miss (level 1) and AddSkillXP would
+// persist a phantom entry under it, so callers that roll success or award XP
+// against a recipe type must check with this first.
+func ValidSkill(name SkillName) bool {
+ for _, s := range AllSkills {
+ if s == name {
+ return true
+ }
+ }
+ return false
+}
+
var SkillAbbr = map[SkillName]string{
Accuracy: "acc",
Strength: "str",
@@ -353,8 +368,13 @@ func (p *Player) SetInvSlot(i int, slot *InventorySlot) {
}
}
+// MaxInventorySlots is the inventory capacity: the valid slots are
+// 0..MaxInventorySlots-1. characterInventorySlots in store.go mirrors this
+// value for on-disk sanitization.
+const MaxInventorySlots = 28
+
func (p *Player) FirstFreeSlot() int {
- for i := 0; i < 28; i++ {
+ for i := 0; i < MaxInventorySlots; i++ {
if p.Inventory[i] == nil {
return i
}
@@ -362,9 +382,19 @@ func (p *Player) FirstFreeSlot() int {
return -1
}
+// FreeSlots counts the empty inventory slots. Only valid slots (0..27) with a
+// non-nil entry count as used: a corrupt, hand-edited, or partially-migrated
+// save can carry out-of-range or nil-valued map entries that no gameplay path
+// can free, and counting those would understate the free space (or drive the
+// count negative). For a valid character the result is unchanged.
func (p *Player) FreeSlots() int {
- used := len(p.Inventory)
- return 28 - used
+ used := 0
+ for i := 0; i < MaxInventorySlots; i++ {
+ if p.Inventory[i] != nil {
+ used++
+ }
+ }
+ return MaxInventorySlots - used
}
func New(name string) *Player {