package skills import "eq2emu/internal/common" // SkillBonusValue represents a single skill bonus value type SkillBonusValue struct { SkillID int32 // Skill being modified Value float32 // Bonus value } // SkillBonus represents skill bonuses from a spell type SkillBonus struct { SpellID int32 // Spell providing the bonus Skills map[int32]*SkillBonusValue // Map of skill ID to bonus value } // Skill represents a character skill type Skill struct { SkillID int32 // Unique skill identifier CurrentVal int16 // Current skill value PreviousVal int16 // Previous skill value (for deltas) MaxVal int16 // Maximum skill value SkillType int32 // Skill category type Display int8 // Display setting ShortName common.EQ2String16 // Short skill name Name common.EQ2String16 // Full skill name Description common.EQ2String16 // Skill description SaveNeeded bool // Whether skill needs database save ActiveSkill bool // Whether skill is active/usable } // NewSkill creates a new skill with default values func NewSkill() *Skill { return &Skill{ SkillID: 0, CurrentVal: 0, PreviousVal: 0, MaxVal: 0, SkillType: 0, Display: 0, SaveNeeded: false, ActiveSkill: true, } } // NewSkillFromSkill creates a copy of an existing skill func NewSkillFromSkill(skill *Skill) *Skill { if skill == nil { return NewSkill() } return &Skill{ SkillID: skill.SkillID, CurrentVal: skill.CurrentVal, PreviousVal: skill.CurrentVal, // Copy current as previous MaxVal: skill.MaxVal, SkillType: skill.SkillType, Display: skill.Display, ShortName: skill.ShortName, Name: skill.Name, Description: skill.Description, SaveNeeded: false, ActiveSkill: true, } } // CheckDisarmSkill checks disarm skill against a chest // Returns 1 for success, 0 for fail (no trigger), -1 for fail with trigger func (s *Skill) CheckDisarmSkill(targetLevel int16, chestDifficulty int8) int { if chestDifficulty < 2 { return DisarmSuccess // No triggers on easy chests } if targetLevel < 1 { targetLevel = 1 } chestDiffResult := int32(targetLevel) * int32(chestDifficulty) baseDifficulty := float32(15.0) failThreshold := float32(10.0) // Calculate success chance chance := (100.0 - baseDifficulty) * (float32(s.CurrentVal) / float32(chestDiffResult)) if chance > (100.0 - baseDifficulty) { chance = 100.0 - baseDifficulty } // Roll d100 roll := makeRandomFloat(0, 100) if roll <= chance { return DisarmSuccess } else if roll > (chance + failThreshold) { return DisarmTrigger } return DisarmFail } // GetCurrentValue returns the current skill value func (s *Skill) GetCurrentValue() int32 { return int32(s.CurrentVal) } // GetMaxValue returns the maximum skill value func (s *Skill) GetMaxValue() int32 { return int32(s.MaxVal) } // GetName returns the skill name func (s *Skill) GetName() string { return s.Name.Data } // GetShortName returns the skill short name func (s *Skill) GetShortName() string { return s.ShortName.Data } // GetDescription returns the skill description func (s *Skill) GetDescription() string { return s.Description.Data } // GetSkillType returns the skill type func (s *Skill) GetSkillType() int32 { return s.SkillType } // IsActive returns whether the skill is active func (s *Skill) IsActive() bool { return s.ActiveSkill } // NeedsSave returns whether the skill needs to be saved func (s *Skill) NeedsSave() bool { return s.SaveNeeded } // SetSaveNeeded marks the skill as needing to be saved func (s *Skill) SetSaveNeeded(needed bool) { s.SaveNeeded = needed } // SetActive sets whether the skill is active func (s *Skill) SetActive(active bool) { s.ActiveSkill = active } // makeRandomFloat generates a random float between min and max // TODO: Replace with proper random number generation when integrated func makeRandomFloat(min, max float32) float32 { // Placeholder implementation return min + ((max - min) / 2.0) }