package player import "thirdcollapse/internal/action" import "thirdcollapse/internal/object" type SkillName string const ( Attack SkillName = "attack" Strength SkillName = "strength" Defense SkillName = "defense" Hitpoints SkillName = "hitpoints" Ranged SkillName = "ranged" Science SkillName = "science" Technology SkillName = "technology" Fishing SkillName = "fishing" Cooking SkillName = "cooking" Woodcutting SkillName = "woodcutting" Mining SkillName = "mining" Smithing SkillName = "smithing" Crafting SkillName = "crafting" Fletching SkillName = "fletching" Alchemy SkillName = "alchemy" Thieving SkillName = "thieving" Agility SkillName = "agility" Construction SkillName = "construction" Scavenging SkillName = "scavenging" Hunter SkillName = "hunter" Slayer SkillName = "slayer" ) var AllSkills = []SkillName{ Attack, Strength, Defense, Hitpoints, Ranged, Science, Technology, Fishing, Cooking, Woodcutting, Mining, Smithing, Crafting, Fletching, Alchemy, Thieving, Agility, Construction, Scavenging, Hunter, Slayer, } var SkillAbbr = map[SkillName]string{ Attack: "atk", Strength: "str", Defense: "def", Hitpoints: "hp", Ranged: "rng", Science: "sci", Technology: "tec", Fishing: "fis", Cooking: "cok", Woodcutting: "wct", Mining: "min", Smithing: "smt", Crafting: "cft", Fletching: "flt", Alchemy: "alc", Thieving: "thv", Agility: "agl", Construction: "con", Scavenging: "scv", Hunter: "hnt", Slayer: "sly", } type AttackStyle string const ( Accurate AttackStyle = "accurate" Aggressive AttackStyle = "aggressive" Defensive AttackStyle = "defensive" Balanced AttackStyle = "balanced" ) type Skill struct { Name SkillName XP int } func (s Skill) Level() int { return LevelForXP(s.XP) } type InventorySlot struct { ItemID string `yaml:"item_id"` Quantity int `yaml:"quantity"` } type Player struct { Name string `yaml:"name"` Skills map[SkillName]int `yaml:"skills"` Inventory map[int]*InventorySlot `yaml:"inventory"` Equipment map[object.EquipSlot]string `yaml:"equipment"` Toolbelt []string `yaml:"toolbelt"` RoomID int `yaml:"room_id"` HP int `yaml:"hp"` Credits int `yaml:"credits"` Description string `yaml:"description"` AttackStyle AttackStyle `yaml:"attack_style"` Toggles map[string]bool `yaml:"toggles"` Flags map[string]any `yaml:"flags"` RegenerateTick int Action *action.Action `yaml:"-"` } func (p *Player) InvSlot(i int) *InventorySlot { if p.Inventory == nil { return nil } return p.Inventory[i] } func (p *Player) SetInvSlot(i int, slot *InventorySlot) { if p.Inventory == nil { p.Inventory = make(map[int]*InventorySlot) } if slot == nil { delete(p.Inventory, i) } else { p.Inventory[i] = slot } } func (p *Player) FirstFreeSlot() int { for i := 0; i < 28; i++ { if p.Inventory[i] == nil { return i } } return -1 } func (p *Player) FreeSlots() int { used := len(p.Inventory) return 28 - used } func New(name string) *Player { p := &Player{ Name: name, Skills: make(map[SkillName]int), Equipment: make(map[object.EquipSlot]string), AttackStyle: Accurate, Toggles: map[string]bool{ "description": true, "tinymap": true, "xpdrops": true, "exits": true, "mobenter": true, "mobleave": true, "mobspawn": true, "reserve": true, }, RoomID: 0, } for _, s := range AllSkills { p.Skills[s] = 0 } p.Skills[Hitpoints] = XPForLevel(10) p.HP = p.MaxHP() return p } func (p *Player) Level(s SkillName) int { return LevelForXP(p.Skills[s]) } func (p *Player) AddXP(s SkillName, amount int) { if p.Skills == nil { p.Skills = make(map[SkillName]int) } p.Skills[s] += amount } func (p *Player) CombatLevel() int { base := 0.25 * float64(p.Level(Defense)+p.Level(Hitpoints)+p.Level(Science)) att := float64(p.Level(Attack)) str := float64(p.Level(Strength)) if float64(p.Level(Ranged))*1.5 > att+str { base += 0.375 * float64(p.Level(Ranged)) } else { base += 0.25 * (att + str) } base += 0.125 * float64(p.Level(Technology)) return int(base) } func (p *Player) MaxHP() int { return p.Level(Hitpoints) } func (p *Player) StartRegen() { if p.RegenerateTick == 0 { p.RegenerateTick = 100 } } func (p *Player) HasToolbeltItem(itemID string) bool { for _, id := range p.Toolbelt { if id == itemID { return true } } return false } func (p *Player) HasItem(itemID string) bool { for _, slot := range p.Inventory { if slot != nil && slot.ItemID == itemID && slot.Quantity > 0 { return true } } return false } func (p *Player) RemoveItem(itemID string, qty int) bool { remaining := qty for i, slot := range p.Inventory { if slot == nil || slot.ItemID != itemID { continue } if slot.Quantity <= remaining { remaining -= slot.Quantity delete(p.Inventory, i) } else { slot.Quantity -= remaining remaining = 0 } if remaining <= 0 { return true } } return remaining <= 0 }