eq2go/internal/player/experience.go
2025-08-06 17:55:41 -05:00

304 lines
7.1 KiB
Go

package player
import (
"time"
"eq2emu/internal/spawn"
)
// GetXPVitality returns the player's adventure XP vitality
func (p *Player) GetXPVitality() float32 {
return p.GetInfoStructXPVitality()
}
// GetTSXPVitality returns the player's tradeskill XP vitality
func (p *Player) GetTSXPVitality() float32 {
return p.GetInfoStructTSXPVitality()
}
// AdventureXPEnabled returns whether adventure XP is enabled
func (p *Player) AdventureXPEnabled() bool {
return p.GetInfoStructXPDebt() < 95.0 && p.GetCharacterFlag(CF_COMBAT_EXPERIENCE_ENABLED)
}
// TradeskillXPEnabled returns whether tradeskill XP is enabled
func (p *Player) TradeskillXPEnabled() bool {
return p.GetInfoStructTSXPDebt() < 95.0 && p.GetCharacterFlag(CF_QUEST_EXPERIENCE_ENABLED)
}
// SetNeededXP sets the needed XP to a specific value
func (p *Player) SetNeededXP(val int32) {
p.SetInfoStructXPNeeded(float64(val))
}
// SetNeededXP sets the needed XP based on current level
func (p *Player) SetNeededXPByLevel() {
p.SetInfoStructXPNeeded(float64(GetNeededXPByLevel(p.GetLevel())))
}
// SetXP sets the current XP
func (p *Player) SetXP(val int32) {
p.SetInfoStructXP(float64(val))
}
// SetNeededTSXP sets the needed tradeskill XP to a specific value
func (p *Player) SetNeededTSXP(val int32) {
p.SetInfoStructTSXPNeeded(float64(val))
}
// SetNeededTSXPByLevel sets the needed tradeskill XP based on current level
func (p *Player) SetNeededTSXPByLevel() {
p.SetInfoStructTSXPNeeded(float64(GetNeededXPByLevel(p.GetTSLevel())))
}
// SetTSXP sets the current tradeskill XP
func (p *Player) SetTSXP(val int32) {
p.SetInfoStructTSXP(float64(val))
}
// GetNeededXP returns the XP needed for next level
func (p *Player) GetNeededXP() int32 {
return int32(p.GetInfoStructXPNeeded())
}
// GetXPDebt returns the current XP debt percentage
func (p *Player) GetXPDebt() float32 {
return p.GetInfoStructXPDebt()
}
// GetXP returns the current XP
func (p *Player) GetXP() int32 {
return int32(p.GetInfoStructXP())
}
// GetNeededTSXP returns the tradeskill XP needed for next level
func (p *Player) GetNeededTSXP() int32 {
return int32(p.GetInfoStructTSXPNeeded())
}
// GetTSXP returns the current tradeskill XP
func (p *Player) GetTSXP() int32 {
return int32(p.GetInfoStructTSXP())
}
// AddXP adds adventure XP to the player
func (p *Player) AddXP(xpAmount int32) bool {
if xpAmount <= 0 {
return false
}
currentXP := int32(p.GetInfoStructXP())
neededXP := int32(p.GetInfoStructXPNeeded())
totalXP := currentXP + xpAmount
// Check if we've reached next level
if totalXP >= neededXP {
// Level up!
if p.GetLevel() < 100 { // Assuming max level is 100
// Calculate overflow XP
overflow := totalXP - neededXP
// Level up
p.SetLevel(int16(p.GetLevel())+1)
p.SetNeededXPByLevel()
// Set XP to overflow amount
p.SetXP(overflow)
// TODO: Send level up packet/message
// TODO: Update stats for new level
// TODO: Check for new abilities/spells
return true
} else {
// At max level, just set to max
p.SetXP(neededXP - 1)
}
} else {
p.SetXP(totalXP)
}
// TODO: Send XP update packet
p.SetCharSheetChanged(true)
return false
}
// AddTSXP adds tradeskill XP to the player
func (p *Player) AddTSXP(xpAmount int32) bool {
if xpAmount <= 0 {
return false
}
currentXP := int32(p.GetInfoStructTSXP())
neededXP := int32(p.GetInfoStructTSXPNeeded())
totalXP := currentXP + xpAmount
// Check if we've reached next level
if totalXP >= neededXP {
// Level up!
if p.GetTSLevel() < 100 { // Assuming max TS level is 100
// Calculate overflow XP
overflow := totalXP - neededXP
// Level up
p.SetTSLevel(p.GetTSLevel() + 1)
p.SetNeededTSXPByLevel()
// Set XP to overflow amount
p.SetTSXP(overflow)
// TODO: Send level up packet/message
// TODO: Update stats for new level
// TODO: Check for new recipes
return true
} else {
// At max level, just set to max
p.SetTSXP(neededXP - 1)
}
} else {
p.SetTSXP(totalXP)
}
// TODO: Send XP update packet
p.SetCharSheetChanged(true)
return true
}
// DoubleXPEnabled returns whether double XP is enabled
func (p *Player) DoubleXPEnabled() bool {
// TODO: Check for double XP events, potions, etc.
return false
}
// CalculateXP calculates the XP reward from a victim
func (p *Player) CalculateXP(victim *spawn.Spawn) float32 {
if victim == nil {
return 0
}
// TODO: Implement full XP calculation formula
// This is a simplified version
victimLevel := victim.GetLevel()
playerLevel := p.GetLevel()
levelDiff := int(victimLevel) - int(playerLevel)
// Base XP value
baseXP := float32(100 + (victimLevel * 10))
// Level difference modifier
var levelMod float32 = 1.0
if levelDiff < -5 {
// Grey con, minimal XP
levelMod = 0.1
} else if levelDiff < -2 {
// Green con, reduced XP
levelMod = 0.5
} else if levelDiff <= 2 {
// Blue/White con, normal XP
levelMod = 1.0
} else if levelDiff <= 4 {
// Yellow con, bonus XP
levelMod = 1.2
} else {
// Orange/Red con, high bonus XP
levelMod = 1.5
}
// Group modifier
groupMod := float32(1.0)
if p.group != nil {
// TODO: Calculate group bonus
groupMod = 0.8 // Simplified group penalty
}
// Vitality modifier
vitalityMod := float32(1.0)
if p.GetXPVitality() > 0 {
vitalityMod = 2.0 // Double XP with vitality
}
// Double XP modifier
doubleXPMod := float32(1.0)
if p.DoubleXPEnabled() {
doubleXPMod = 2.0
}
totalXP := baseXP * levelMod * groupMod * vitalityMod * doubleXPMod
return totalXP
}
// CalculateTSXP calculates tradeskill XP for a given level
func (p *Player) CalculateTSXP(level int8) float32 {
// TODO: Implement tradeskill XP calculation
// This is a simplified version
levelDiff := int(level) - int(p.GetTSLevel())
baseXP := float32(50 + (level * 5))
// Level difference modifier
var levelMod float32 = 1.0
if levelDiff < -5 {
levelMod = 0.1
} else if levelDiff < -2 {
levelMod = 0.5
} else if levelDiff <= 2 {
levelMod = 1.0
} else if levelDiff <= 4 {
levelMod = 1.2
} else {
levelMod = 1.5
}
// Vitality modifier
vitalityMod := float32(1.0)
if p.GetTSXPVitality() > 0 {
vitalityMod = 2.0
}
return baseXP * levelMod * vitalityMod
}
// CalculateOfflineDebtRecovery calculates debt recovery while offline
func (p *Player) CalculateOfflineDebtRecovery(unixTimestamp int32) {
currentTime := int32(time.Now().Unix())
timeDiff := currentTime - unixTimestamp
if timeDiff <= 0 {
return
}
// Calculate hours offline
hoursOffline := float32(timeDiff) / 3600.0
// Debt recovery rate per hour (example: 1% per hour)
debtRecoveryRate := float32(1.0)
// Calculate adventure debt recovery
currentDebt := p.GetInfoStructXPDebt()
if currentDebt > 0 {
recovery := debtRecoveryRate * hoursOffline
newDebt := currentDebt - recovery
if newDebt < 0 {
newDebt = 0
}
p.SetInfoStructXPDebt(newDebt)
}
// Calculate tradeskill debt recovery
currentTSDebt := p.GetInfoStructTSXPDebt()
if currentTSDebt > 0 {
recovery := debtRecoveryRate * hoursOffline
newDebt := currentTSDebt - recovery
if newDebt < 0 {
newDebt = 0
}
p.SetInfoStructTSXPDebt(newDebt)
}
}
// Note: GetTSLevel is now implemented in stubs.go
// Note: SetTSLevel is now implemented in stubs.go