290 lines
7.1 KiB
Go
290 lines
7.1 KiB
Go
package player
|
|
|
|
import (
|
|
"eq2emu/internal/entity"
|
|
)
|
|
|
|
// InCombat sets the player's combat state
|
|
func (p *Player) InCombat(val bool, ranged bool) {
|
|
if val {
|
|
// Entering combat
|
|
if ranged {
|
|
p.SetCharacterFlag(CF_RANGED_AUTO_ATTACK)
|
|
p.SetRangeAttack(true)
|
|
} else {
|
|
p.SetCharacterFlag(CF_AUTO_ATTACK)
|
|
}
|
|
|
|
// Set combat state in info struct
|
|
prevState := p.GetInfoStruct().GetEngageCommands()
|
|
if ranged {
|
|
p.GetInfoStruct().SetEngageCommands(prevState | RANGE_COMBAT_STATE)
|
|
} else {
|
|
p.GetInfoStruct().SetEngageCommands(prevState | MELEE_COMBAT_STATE)
|
|
}
|
|
} else {
|
|
// Leaving combat
|
|
if ranged {
|
|
p.ResetCharacterFlag(CF_RANGED_AUTO_ATTACK)
|
|
p.SetRangeAttack(false)
|
|
prevState := p.GetInfoStruct().GetEngageCommands()
|
|
p.GetInfoStruct().SetEngageCommands(prevState & ^RANGE_COMBAT_STATE)
|
|
} else {
|
|
p.ResetCharacterFlag(CF_AUTO_ATTACK)
|
|
prevState := p.GetInfoStruct().GetEngageCommands()
|
|
p.GetInfoStruct().SetEngageCommands(prevState & ^MELEE_COMBAT_STATE)
|
|
}
|
|
|
|
// Clear combat target if leaving all combat
|
|
if p.GetInfoStruct().GetEngageCommands() == 0 {
|
|
p.combatTarget = nil
|
|
}
|
|
}
|
|
|
|
p.SetCharSheetChanged(true)
|
|
}
|
|
|
|
// ProcessCombat processes combat actions
|
|
func (p *Player) ProcessCombat() {
|
|
// Check if in combat
|
|
if p.GetInfoStruct().GetEngageCommands() == 0 {
|
|
return
|
|
}
|
|
|
|
// Check if we have a valid target
|
|
if p.combatTarget == nil || p.combatTarget.IsDead() {
|
|
p.StopCombat(0)
|
|
return
|
|
}
|
|
|
|
// Check distance to target
|
|
distance := p.GetDistance(&p.combatTarget.Spawn)
|
|
|
|
// Process based on combat type
|
|
if p.rangeAttack {
|
|
// Ranged combat
|
|
maxRange := p.GetRangeWeaponRange()
|
|
if distance > maxRange {
|
|
// Too far for ranged
|
|
// TODO: Send out of range message
|
|
return
|
|
}
|
|
|
|
// TODO: Process ranged auto-attack
|
|
} else {
|
|
// Melee combat
|
|
maxRange := p.GetMeleeWeaponRange()
|
|
if distance > maxRange {
|
|
// Too far for melee
|
|
// TODO: Send out of range message
|
|
return
|
|
}
|
|
|
|
// TODO: Process melee auto-attack
|
|
}
|
|
}
|
|
|
|
// GetRangeWeaponRange returns the range of the equipped ranged weapon
|
|
func (p *Player) GetRangeWeaponRange() float32 {
|
|
// TODO: Get from equipped ranged weapon
|
|
return 35.0 // Default bow range
|
|
}
|
|
|
|
// GetMeleeWeaponRange returns the range of melee weapons
|
|
func (p *Player) GetMeleeWeaponRange() float32 {
|
|
// TODO: Adjust based on weapon type and mob size
|
|
return 5.0 // Default melee range
|
|
}
|
|
|
|
// SetCombatTarget sets the current combat target
|
|
func (p *Player) SetCombatTarget(target *entity.Entity) {
|
|
p.combatTarget = target
|
|
}
|
|
|
|
// GetCombatTarget returns the current combat target
|
|
func (p *Player) GetCombatTarget() *entity.Entity {
|
|
return p.combatTarget
|
|
}
|
|
|
|
// DamageEquippedItems damages equipped items by durability
|
|
func (p *Player) DamageEquippedItems(amount int8, client *Client) bool {
|
|
// TODO: Implement item durability damage
|
|
// This would:
|
|
// 1. Get all equipped items
|
|
// 2. Reduce durability by amount
|
|
// 3. Check if any items broke
|
|
// 4. Send updates to client
|
|
return false
|
|
}
|
|
|
|
// GetTSArrowColor returns the arrow color for tradeskill con
|
|
func (p *Player) GetTSArrowColor(level int8) int8 {
|
|
levelDiff := int(level) - int(p.GetTSLevel())
|
|
|
|
if levelDiff >= 10 {
|
|
return 4 // Red
|
|
} else if levelDiff >= 5 {
|
|
return 3 // Orange
|
|
} else if levelDiff >= 1 {
|
|
return 2 // Yellow
|
|
} else if levelDiff >= -5 {
|
|
return 1 // White
|
|
} else if levelDiff >= -9 {
|
|
return 0 // Blue
|
|
} else {
|
|
return 6 // Green
|
|
}
|
|
}
|
|
|
|
// CheckLevelStatus checks and updates level-based statuses
|
|
func (p *Player) CheckLevelStatus(newLevel int16) bool {
|
|
// TODO: Implement level status checks
|
|
// This would check things like:
|
|
// - Mentoring status
|
|
// - Level-locked abilities
|
|
// - Zone level requirements
|
|
// - etc.
|
|
return true
|
|
}
|
|
|
|
// CalculatePlayerHPPower calculates HP and Power for the player
|
|
func (p *Player) CalculatePlayerHPPower(newLevel int16) {
|
|
if newLevel == 0 {
|
|
newLevel = int16(p.GetLevel())
|
|
}
|
|
|
|
// TODO: Implement proper HP/Power calculation
|
|
// This is a simplified version
|
|
|
|
// Base HP calculation
|
|
baseHP := int32(50 + (newLevel * 20))
|
|
staminaBonus := p.GetInfoStruct().GetSta() * 10
|
|
totalHP := baseHP + staminaBonus
|
|
|
|
// Base Power calculation
|
|
basePower := int32(50 + (newLevel * 10))
|
|
primaryStatBonus := p.GetPrimaryStat() * 10
|
|
totalPower := basePower + primaryStatBonus
|
|
|
|
// Set the values
|
|
p.SetTotalHP(totalHP)
|
|
p.SetTotalPower(totalPower)
|
|
|
|
// Set current values if needed
|
|
if p.GetHP() > totalHP {
|
|
p.SetHP(totalHP)
|
|
}
|
|
if p.GetPower() > totalPower {
|
|
p.SetPower(totalPower)
|
|
}
|
|
}
|
|
|
|
// IsAllowedCombatEquip checks if combat equipment changes are allowed
|
|
func (p *Player) IsAllowedCombatEquip(slot int8, sendMessage bool) bool {
|
|
// Can't change equipment while:
|
|
// - Dead
|
|
// - In combat (for certain slots)
|
|
// - Casting
|
|
// - Stunned/Mezzed
|
|
|
|
if p.IsDead() {
|
|
if sendMessage {
|
|
// TODO: Send "You cannot change equipment while dead" message
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Check if in combat
|
|
if p.GetInfoStruct().GetEngageCommands() != 0 {
|
|
// Some slots can't be changed in combat
|
|
// TODO: Define which slots are restricted
|
|
restrictedSlots := []int8{0, 1, 2} // Example: primary, secondary, ranged
|
|
for _, restrictedSlot := range restrictedSlots {
|
|
if slot == restrictedSlot || slot == 255 { // 255 = all slots
|
|
if sendMessage {
|
|
// TODO: Send "You cannot change that equipment in combat" message
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if casting
|
|
if p.IsCasting() {
|
|
if sendMessage {
|
|
// TODO: Send "You cannot change equipment while casting" message
|
|
}
|
|
return false
|
|
}
|
|
|
|
// Check control effects
|
|
if p.IsStunned() || p.IsMezzed() {
|
|
if sendMessage {
|
|
// TODO: Send appropriate message
|
|
}
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// IsCasting returns whether the player is currently casting
|
|
func (p *Player) IsCasting() bool {
|
|
// TODO: Check actual casting state
|
|
return false
|
|
}
|
|
|
|
// DismissAllPets dismisses all of the player's pets
|
|
func (p *Player) DismissAllPets() {
|
|
// TODO: Implement pet dismissal
|
|
// This would:
|
|
// 1. Get all pets (combat, non-combat, deity, etc.)
|
|
// 2. Remove them from world
|
|
// 3. Clear pet references
|
|
// 4. Send updates to client
|
|
}
|
|
|
|
// MentorTarget mentors the current target
|
|
func (p *Player) MentorTarget() {
|
|
target := p.GetTarget()
|
|
if target == nil || !target.IsPlayer() {
|
|
// TODO: Send "Invalid mentor target" message
|
|
return
|
|
}
|
|
|
|
targetPlayer, ok := target.(*Player)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
// Check if target is valid for mentoring
|
|
if targetPlayer.GetLevel() >= p.GetLevel() {
|
|
// TODO: Send "Target must be lower level" message
|
|
return
|
|
}
|
|
|
|
// Set mentor stats
|
|
p.SetMentorStats(int32(targetPlayer.GetLevel()), targetPlayer.GetCharacterID(), true)
|
|
}
|
|
|
|
// SetMentorStats sets the player's effective level for mentoring
|
|
func (p *Player) SetMentorStats(effectiveLevel int32, targetCharID int32, updateStats bool) {
|
|
if effectiveLevel < 1 || effectiveLevel > int32(p.GetLevel()) {
|
|
effectiveLevel = int32(p.GetLevel())
|
|
}
|
|
|
|
p.GetInfoStruct().SetEffectiveLevel(int8(effectiveLevel))
|
|
|
|
if updateStats {
|
|
// TODO: Recalculate all stats for new effective level
|
|
p.CalculatePlayerHPPower(int16(effectiveLevel))
|
|
// TODO: Update other stats (mitigation, avoidance, etc.)
|
|
}
|
|
|
|
if effectiveLevel < int32(p.GetLevel()) {
|
|
p.EnableResetMentorship()
|
|
}
|
|
|
|
p.SetCharSheetChanged(true)
|
|
}
|