fix player package
This commit is contained in:
parent
8f8dbefece
commit
1987d48a77
@ -303,7 +303,7 @@ type PlayerDatabase interface {
|
|||||||
type PlayerEventHandler interface {
|
type PlayerEventHandler interface {
|
||||||
OnPlayerLogin(player *Player) error
|
OnPlayerLogin(player *Player) error
|
||||||
OnPlayerLogout(player *Player) error
|
OnPlayerLogout(player *Player) error
|
||||||
OnPlayerDeath(player *Player, killer entity.Entity) error
|
OnPlayerDeath(player *Player, killer *entity.Entity) error
|
||||||
OnPlayerLevelUp(player *Player, newLevel int8) error
|
OnPlayerLevelUp(player *Player, newLevel int8) error
|
||||||
// ... more events
|
// ... more events
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,9 @@ func (p *Player) SetCharacterFlag(flag int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if flag < 32 {
|
if flag < 32 {
|
||||||
p.GetInfoStruct().SetFlags(p.GetInfoStruct().GetFlags() | (1 << uint(flag)))
|
p.SetPlayerFlags(p.GetPlayerFlags() | (1 << uint(flag)))
|
||||||
} else {
|
} else {
|
||||||
p.GetInfoStruct().SetFlags2(p.GetInfoStruct().GetFlags2() | (1 << uint(flag-32)))
|
p.SetPlayerFlags2(p.GetPlayerFlags2() | (1 << uint(flag-32)))
|
||||||
}
|
}
|
||||||
p.SetCharSheetChanged(true)
|
p.SetCharSheetChanged(true)
|
||||||
}
|
}
|
||||||
@ -21,9 +21,9 @@ func (p *Player) ResetCharacterFlag(flag int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if flag < 32 {
|
if flag < 32 {
|
||||||
p.GetInfoStruct().SetFlags(p.GetInfoStruct().GetFlags() & ^(1 << uint(flag)))
|
p.SetPlayerFlags(p.GetPlayerFlags() & ^(1 << uint(flag)))
|
||||||
} else {
|
} else {
|
||||||
p.GetInfoStruct().SetFlags2(p.GetInfoStruct().GetFlags2() & ^(1 << uint(flag-32)))
|
p.SetPlayerFlags2(p.GetPlayerFlags2() & ^(1 << uint(flag-32)))
|
||||||
}
|
}
|
||||||
p.SetCharSheetChanged(true)
|
p.SetCharSheetChanged(true)
|
||||||
}
|
}
|
||||||
@ -49,9 +49,9 @@ func (p *Player) GetCharacterFlag(flag int) bool {
|
|||||||
|
|
||||||
var ret bool
|
var ret bool
|
||||||
if flag < 32 {
|
if flag < 32 {
|
||||||
ret = (p.GetInfoStruct().GetFlags() & (1 << uint(flag))) != 0
|
ret = (p.GetPlayerFlags() & (1 << uint(flag))) != 0
|
||||||
} else {
|
} else {
|
||||||
ret = (p.GetInfoStruct().GetFlags2() & (1 << uint(flag-32))) != 0
|
ret = (p.GetPlayerFlags2() & (1 << uint(flag-32))) != 0
|
||||||
}
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
@ -15,28 +15,28 @@ func (p *Player) InCombat(val bool, ranged bool) {
|
|||||||
p.SetCharacterFlag(CF_AUTO_ATTACK)
|
p.SetCharacterFlag(CF_AUTO_ATTACK)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set combat state in info struct
|
// Set combat state
|
||||||
prevState := p.GetInfoStruct().GetEngageCommands()
|
prevState := p.GetPlayerEngageCommands()
|
||||||
if ranged {
|
if ranged {
|
||||||
p.GetInfoStruct().SetEngageCommands(prevState | RANGE_COMBAT_STATE)
|
p.SetPlayerEngageCommands(prevState | RANGE_COMBAT_STATE)
|
||||||
} else {
|
} else {
|
||||||
p.GetInfoStruct().SetEngageCommands(prevState | MELEE_COMBAT_STATE)
|
p.SetPlayerEngageCommands(prevState | MELEE_COMBAT_STATE)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Leaving combat
|
// Leaving combat
|
||||||
if ranged {
|
if ranged {
|
||||||
p.ResetCharacterFlag(CF_RANGED_AUTO_ATTACK)
|
p.ResetCharacterFlag(CF_RANGED_AUTO_ATTACK)
|
||||||
p.SetRangeAttack(false)
|
p.SetRangeAttack(false)
|
||||||
prevState := p.GetInfoStruct().GetEngageCommands()
|
prevState := p.GetPlayerEngageCommands()
|
||||||
p.GetInfoStruct().SetEngageCommands(prevState & ^RANGE_COMBAT_STATE)
|
p.SetPlayerEngageCommands(prevState & ^RANGE_COMBAT_STATE)
|
||||||
} else {
|
} else {
|
||||||
p.ResetCharacterFlag(CF_AUTO_ATTACK)
|
p.ResetCharacterFlag(CF_AUTO_ATTACK)
|
||||||
prevState := p.GetInfoStruct().GetEngageCommands()
|
prevState := p.GetPlayerEngageCommands()
|
||||||
p.GetInfoStruct().SetEngageCommands(prevState & ^MELEE_COMBAT_STATE)
|
p.SetPlayerEngageCommands(prevState & ^MELEE_COMBAT_STATE)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear combat target if leaving all combat
|
// Clear combat target if leaving all combat
|
||||||
if p.GetInfoStruct().GetEngageCommands() == 0 {
|
if p.GetPlayerEngageCommands() == 0 {
|
||||||
p.combatTarget = nil
|
p.combatTarget = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,18 +47,18 @@ func (p *Player) InCombat(val bool, ranged bool) {
|
|||||||
// ProcessCombat processes combat actions
|
// ProcessCombat processes combat actions
|
||||||
func (p *Player) ProcessCombat() {
|
func (p *Player) ProcessCombat() {
|
||||||
// Check if in combat
|
// Check if in combat
|
||||||
if p.GetInfoStruct().GetEngageCommands() == 0 {
|
if p.GetPlayerEngageCommands() == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we have a valid target
|
// Check if we have a valid target
|
||||||
if p.combatTarget == nil || p.combatTarget.IsDead() {
|
if p.combatTarget == nil || IsDead(p.combatTarget) {
|
||||||
p.StopCombat(0)
|
p.StopCombat(0)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check distance to target
|
// Check distance to target
|
||||||
distance := p.GetDistance(&p.combatTarget.Spawn)
|
distance := p.GetDistance(p.combatTarget.GetX(), p.combatTarget.GetY(), p.combatTarget.GetZ(), true)
|
||||||
|
|
||||||
// Process based on combat type
|
// Process based on combat type
|
||||||
if p.rangeAttack {
|
if p.rangeAttack {
|
||||||
@ -158,7 +158,7 @@ func (p *Player) CalculatePlayerHPPower(newLevel int16) {
|
|||||||
|
|
||||||
// Base HP calculation
|
// Base HP calculation
|
||||||
baseHP := int32(50 + (newLevel * 20))
|
baseHP := int32(50 + (newLevel * 20))
|
||||||
staminaBonus := p.GetInfoStruct().GetSta() * 10
|
staminaBonus := int32(p.GetInfoStruct().GetSta() * 10)
|
||||||
totalHP := baseHP + staminaBonus
|
totalHP := baseHP + staminaBonus
|
||||||
|
|
||||||
// Base Power calculation
|
// Base Power calculation
|
||||||
@ -195,12 +195,12 @@ func (p *Player) IsAllowedCombatEquip(slot int8, sendMessage bool) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if in combat
|
// Check if in combat
|
||||||
if p.GetInfoStruct().GetEngageCommands() != 0 {
|
if p.GetPlayerEngageCommands() != 0 {
|
||||||
// Some slots can't be changed in combat
|
// Some slots can't be changed in combat
|
||||||
// TODO: Define which slots are restricted
|
// TODO: Define which slots are restricted
|
||||||
restrictedSlots := []int8{0, 1, 2} // Example: primary, secondary, ranged
|
restrictedSlots := []int8{0, 1, 2} // Example: primary, secondary, ranged
|
||||||
for _, restrictedSlot := range restrictedSlots {
|
for _, restrictedSlot := range restrictedSlots {
|
||||||
if slot == restrictedSlot || slot == 255 { // 255 = all slots
|
if slot == restrictedSlot || slot == -1 { // -1 = all slots
|
||||||
if sendMessage {
|
if sendMessage {
|
||||||
// TODO: Send "You cannot change that equipment in combat" message
|
// TODO: Send "You cannot change that equipment in combat" message
|
||||||
}
|
}
|
||||||
@ -247,7 +247,7 @@ func (p *Player) DismissAllPets() {
|
|||||||
// MentorTarget mentors the current target
|
// MentorTarget mentors the current target
|
||||||
func (p *Player) MentorTarget() {
|
func (p *Player) MentorTarget() {
|
||||||
target := p.GetTarget()
|
target := p.GetTarget()
|
||||||
if target == nil || !target.IsPlayer() {
|
if target == nil {
|
||||||
// TODO: Send "Invalid mentor target" message
|
// TODO: Send "Invalid mentor target" message
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -273,7 +273,7 @@ func (p *Player) SetMentorStats(effectiveLevel int32, targetCharID int32, update
|
|||||||
effectiveLevel = int32(p.GetLevel())
|
effectiveLevel = int32(p.GetLevel())
|
||||||
}
|
}
|
||||||
|
|
||||||
p.GetInfoStruct().SetEffectiveLevel(int8(effectiveLevel))
|
p.GetInfoStruct().SetEffectiveLevel(int16(effectiveLevel))
|
||||||
|
|
||||||
if updateStats {
|
if updateStats {
|
||||||
// TODO: Recalculate all stats for new effective level
|
// TODO: Recalculate all stats for new effective level
|
||||||
|
@ -2,14 +2,14 @@ package player
|
|||||||
|
|
||||||
// AddCoins adds coins to the player
|
// AddCoins adds coins to the player
|
||||||
func (p *Player) AddCoins(val int64) {
|
func (p *Player) AddCoins(val int64) {
|
||||||
p.GetInfoStruct().AddCoin(val)
|
p.AddCoin(val)
|
||||||
p.sendCurrencyUpdate()
|
p.sendCurrencyUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveCoins removes coins from the player
|
// RemoveCoins removes coins from the player
|
||||||
func (p *Player) RemoveCoins(val int64) bool {
|
func (p *Player) RemoveCoins(val int64) bool {
|
||||||
if p.GetInfoStruct().GetCoin() >= val {
|
if p.GetCoin() >= val {
|
||||||
p.GetInfoStruct().SubtractCoin(val)
|
p.SubtractCoin(val)
|
||||||
p.sendCurrencyUpdate()
|
p.sendCurrencyUpdate()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -18,52 +18,52 @@ func (p *Player) RemoveCoins(val int64) bool {
|
|||||||
|
|
||||||
// HasCoins checks if the player has enough coins
|
// HasCoins checks if the player has enough coins
|
||||||
func (p *Player) HasCoins(val int64) bool {
|
func (p *Player) HasCoins(val int64) bool {
|
||||||
return p.GetInfoStruct().GetCoin() >= val
|
return p.GetCoin() >= val
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCoinsCopper returns the copper coin amount
|
// GetCoinsCopper returns the copper coin amount
|
||||||
func (p *Player) GetCoinsCopper() int32 {
|
func (p *Player) GetCoinsCopper() int32 {
|
||||||
return p.GetInfoStruct().GetCoinCopper()
|
return p.GetInfoStructCoinCopper()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCoinsSilver returns the silver coin amount
|
// GetCoinsSilver returns the silver coin amount
|
||||||
func (p *Player) GetCoinsSilver() int32 {
|
func (p *Player) GetCoinsSilver() int32 {
|
||||||
return p.GetInfoStruct().GetCoinSilver()
|
return p.GetInfoStructCoinSilver()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCoinsGold returns the gold coin amount
|
// GetCoinsGold returns the gold coin amount
|
||||||
func (p *Player) GetCoinsGold() int32 {
|
func (p *Player) GetCoinsGold() int32 {
|
||||||
return p.GetInfoStruct().GetCoinGold()
|
return p.GetInfoStructCoinGold()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCoinsPlat returns the platinum coin amount
|
// GetCoinsPlat returns the platinum coin amount
|
||||||
func (p *Player) GetCoinsPlat() int32 {
|
func (p *Player) GetCoinsPlat() int32 {
|
||||||
return p.GetInfoStruct().GetCoinPlat()
|
return p.GetInfoStructCoinPlat()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBankCoinsCopper returns the bank copper coin amount
|
// GetBankCoinsCopper returns the bank copper coin amount
|
||||||
func (p *Player) GetBankCoinsCopper() int32 {
|
func (p *Player) GetBankCoinsCopper() int32 {
|
||||||
return p.GetInfoStruct().GetBankCoinCopper()
|
return p.GetInfoStructBankCoinCopper()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBankCoinsSilver returns the bank silver coin amount
|
// GetBankCoinsSilver returns the bank silver coin amount
|
||||||
func (p *Player) GetBankCoinsSilver() int32 {
|
func (p *Player) GetBankCoinsSilver() int32 {
|
||||||
return p.GetInfoStruct().GetBankCoinSilver()
|
return p.GetInfoStructBankCoinSilver()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBankCoinsGold returns the bank gold coin amount
|
// GetBankCoinsGold returns the bank gold coin amount
|
||||||
func (p *Player) GetBankCoinsGold() int32 {
|
func (p *Player) GetBankCoinsGold() int32 {
|
||||||
return p.GetInfoStruct().GetBankCoinGold()
|
return p.GetInfoStructBankCoinGold()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBankCoinsPlat returns the bank platinum coin amount
|
// GetBankCoinsPlat returns the bank platinum coin amount
|
||||||
func (p *Player) GetBankCoinsPlat() int32 {
|
func (p *Player) GetBankCoinsPlat() int32 {
|
||||||
return p.GetInfoStruct().GetBankCoinPlat()
|
return p.GetInfoStructBankCoinPlat()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStatusPoints returns the player's status points
|
// GetStatusPoints returns the player's status points
|
||||||
func (p *Player) GetStatusPoints() int32 {
|
func (p *Player) GetStatusPoints() int32 {
|
||||||
return p.GetInfoStruct().GetStatusPoints()
|
return p.GetInfoStructStatusPoints()
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendCurrencyUpdate sends currency update packet to client
|
// sendCurrencyUpdate sends currency update packet to client
|
||||||
|
227
internal/player/database.go
Normal file
227
internal/player/database.go
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
package player
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"zombiezen.com/go/sqlite"
|
||||||
|
"zombiezen.com/go/sqlite/sqlitex"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PlayerDatabase manages player data persistence using SQLite
|
||||||
|
type PlayerDatabase struct {
|
||||||
|
conn *sqlite.Conn
|
||||||
|
mutex sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPlayerDatabase creates a new player database instance
|
||||||
|
func NewPlayerDatabase(conn *sqlite.Conn) *PlayerDatabase {
|
||||||
|
return &PlayerDatabase{
|
||||||
|
conn: conn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadPlayer loads a player from the database
|
||||||
|
func (pdb *PlayerDatabase) LoadPlayer(characterID int32) (*Player, error) {
|
||||||
|
pdb.mutex.RLock()
|
||||||
|
defer pdb.mutex.RUnlock()
|
||||||
|
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(characterID)
|
||||||
|
found := false
|
||||||
|
|
||||||
|
query := `SELECT name, level, race, class, zone_id, x, y, z, heading
|
||||||
|
FROM characters WHERE id = ?`
|
||||||
|
|
||||||
|
err := sqlitex.Execute(pdb.conn, query, &sqlitex.ExecOptions{
|
||||||
|
Args: []any{characterID},
|
||||||
|
ResultFunc: func(stmt *sqlite.Stmt) error {
|
||||||
|
player.SetName(stmt.ColumnText(0))
|
||||||
|
player.SetLevel(int16(stmt.ColumnInt(1)))
|
||||||
|
player.SetRace(int8(stmt.ColumnInt(2)))
|
||||||
|
player.SetClass(int8(stmt.ColumnInt(3)))
|
||||||
|
player.SetZone(int32(stmt.ColumnInt(4)))
|
||||||
|
player.SetX(float32(stmt.ColumnFloat(5)))
|
||||||
|
player.SetY(float32(stmt.ColumnFloat(6)), false)
|
||||||
|
player.SetZ(float32(stmt.ColumnFloat(7)))
|
||||||
|
player.SetHeadingFromFloat(float32(stmt.ColumnFloat(8)))
|
||||||
|
found = true
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to load player %d: %w", characterID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return nil, fmt.Errorf("player %d not found", characterID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return player, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SavePlayer saves a player to the database
|
||||||
|
func (pdb *PlayerDatabase) SavePlayer(player *Player) error {
|
||||||
|
if player == nil {
|
||||||
|
return fmt.Errorf("cannot save nil player")
|
||||||
|
}
|
||||||
|
|
||||||
|
pdb.mutex.Lock()
|
||||||
|
defer pdb.mutex.Unlock()
|
||||||
|
|
||||||
|
characterID := player.GetCharacterID()
|
||||||
|
if characterID == 0 {
|
||||||
|
// Insert new player
|
||||||
|
return pdb.insertPlayer(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to update existing player first
|
||||||
|
err := pdb.updatePlayer(player)
|
||||||
|
if err == nil {
|
||||||
|
// Check if any rows were affected
|
||||||
|
changes := pdb.conn.Changes()
|
||||||
|
if changes == 0 {
|
||||||
|
// No rows updated, record doesn't exist - insert it
|
||||||
|
return pdb.insertPlayerWithID(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// insertPlayer inserts a new player record
|
||||||
|
func (pdb *PlayerDatabase) insertPlayer(player *Player) error {
|
||||||
|
query := `INSERT INTO characters
|
||||||
|
(name, level, race, class, zone_id, x, y, z, heading, created_date)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`
|
||||||
|
|
||||||
|
err := sqlitex.Execute(pdb.conn, query, &sqlitex.ExecOptions{
|
||||||
|
Args: []any{
|
||||||
|
player.GetName(),
|
||||||
|
player.GetLevel(),
|
||||||
|
player.GetRace(),
|
||||||
|
player.GetClass(),
|
||||||
|
player.GetZone(),
|
||||||
|
player.GetX(),
|
||||||
|
player.GetY(),
|
||||||
|
player.GetZ(),
|
||||||
|
player.GetHeading(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to insert player %s: %w", player.GetName(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the new character ID
|
||||||
|
characterID := pdb.conn.LastInsertRowID()
|
||||||
|
player.SetCharacterID(int32(characterID))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// insertPlayerWithID inserts a player with a specific ID
|
||||||
|
func (pdb *PlayerDatabase) insertPlayerWithID(player *Player) error {
|
||||||
|
query := `INSERT INTO characters
|
||||||
|
(id, name, level, race, class, zone_id, x, y, z, heading, created_date)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))`
|
||||||
|
|
||||||
|
err := sqlitex.Execute(pdb.conn, query, &sqlitex.ExecOptions{
|
||||||
|
Args: []any{
|
||||||
|
player.GetCharacterID(),
|
||||||
|
player.GetName(),
|
||||||
|
player.GetLevel(),
|
||||||
|
player.GetRace(),
|
||||||
|
player.GetClass(),
|
||||||
|
player.GetZone(),
|
||||||
|
player.GetX(),
|
||||||
|
player.GetY(),
|
||||||
|
player.GetZ(),
|
||||||
|
player.GetHeading(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to insert player %s with ID %d: %w", player.GetName(), player.GetCharacterID(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// updatePlayer updates an existing player record
|
||||||
|
func (pdb *PlayerDatabase) updatePlayer(player *Player) error {
|
||||||
|
query := `UPDATE characters
|
||||||
|
SET name = ?, level = ?, race = ?, class = ?, zone_id = ?,
|
||||||
|
x = ?, y = ?, z = ?, heading = ?, last_save = datetime('now')
|
||||||
|
WHERE id = ?`
|
||||||
|
|
||||||
|
err := sqlitex.Execute(pdb.conn, query, &sqlitex.ExecOptions{
|
||||||
|
Args: []any{
|
||||||
|
player.GetName(),
|
||||||
|
player.GetLevel(),
|
||||||
|
player.GetRace(),
|
||||||
|
player.GetClass(),
|
||||||
|
player.GetZone(),
|
||||||
|
player.GetX(),
|
||||||
|
player.GetY(),
|
||||||
|
player.GetZ(),
|
||||||
|
player.GetHeading(),
|
||||||
|
player.GetCharacterID(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update player %d: %w", player.GetCharacterID(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeletePlayer deletes a player from the database
|
||||||
|
func (pdb *PlayerDatabase) DeletePlayer(characterID int32) error {
|
||||||
|
pdb.mutex.Lock()
|
||||||
|
defer pdb.mutex.Unlock()
|
||||||
|
|
||||||
|
query := `DELETE FROM characters WHERE id = ?`
|
||||||
|
|
||||||
|
err := sqlitex.Execute(pdb.conn, query, &sqlitex.ExecOptions{
|
||||||
|
Args: []any{characterID},
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to delete player %d: %w", characterID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSchema creates the database schema for player data
|
||||||
|
func (pdb *PlayerDatabase) CreateSchema() error {
|
||||||
|
pdb.mutex.Lock()
|
||||||
|
defer pdb.mutex.Unlock()
|
||||||
|
|
||||||
|
schema := `
|
||||||
|
CREATE TABLE IF NOT EXISTS characters (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
name TEXT NOT NULL UNIQUE,
|
||||||
|
level INTEGER DEFAULT 1,
|
||||||
|
race INTEGER DEFAULT 1,
|
||||||
|
class INTEGER DEFAULT 1,
|
||||||
|
zone_id INTEGER DEFAULT 1,
|
||||||
|
x REAL DEFAULT 0,
|
||||||
|
y REAL DEFAULT 0,
|
||||||
|
z REAL DEFAULT 0,
|
||||||
|
heading REAL DEFAULT 0,
|
||||||
|
hp INTEGER DEFAULT 100,
|
||||||
|
power INTEGER DEFAULT 100,
|
||||||
|
created_date TEXT,
|
||||||
|
last_save TEXT,
|
||||||
|
account_id INTEGER DEFAULT 0
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_characters_name ON characters(name);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_characters_account ON characters(account_id);
|
||||||
|
`
|
||||||
|
|
||||||
|
return sqlitex.ExecuteScript(pdb.conn, schema, &sqlitex.ExecOptions{})
|
||||||
|
}
|
@ -1,83 +1,84 @@
|
|||||||
package player
|
package player
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"eq2emu/internal/entity"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"eq2emu/internal/spawn"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetXPVitality returns the player's adventure XP vitality
|
// GetXPVitality returns the player's adventure XP vitality
|
||||||
func (p *Player) GetXPVitality() float32 {
|
func (p *Player) GetXPVitality() float32 {
|
||||||
return p.GetInfoStruct().GetXPVitality()
|
return p.GetInfoStructXPVitality()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTSXPVitality returns the player's tradeskill XP vitality
|
// GetTSXPVitality returns the player's tradeskill XP vitality
|
||||||
func (p *Player) GetTSXPVitality() float32 {
|
func (p *Player) GetTSXPVitality() float32 {
|
||||||
return p.GetInfoStruct().GetTSXPVitality()
|
return p.GetInfoStructTSXPVitality()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AdventureXPEnabled returns whether adventure XP is enabled
|
// AdventureXPEnabled returns whether adventure XP is enabled
|
||||||
func (p *Player) AdventureXPEnabled() bool {
|
func (p *Player) AdventureXPEnabled() bool {
|
||||||
return p.GetInfoStruct().GetXPDebt() < 95.0 && p.GetCharacterFlag(CF_COMBAT_EXPERIENCE_ENABLED)
|
return p.GetInfoStructXPDebt() < 95.0 && p.GetCharacterFlag(CF_COMBAT_EXPERIENCE_ENABLED)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TradeskillXPEnabled returns whether tradeskill XP is enabled
|
// TradeskillXPEnabled returns whether tradeskill XP is enabled
|
||||||
func (p *Player) TradeskillXPEnabled() bool {
|
func (p *Player) TradeskillXPEnabled() bool {
|
||||||
return p.GetInfoStruct().GetTSXPDebt() < 95.0 && p.GetCharacterFlag(CF_QUEST_EXPERIENCE_ENABLED)
|
return p.GetInfoStructTSXPDebt() < 95.0 && p.GetCharacterFlag(CF_QUEST_EXPERIENCE_ENABLED)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetNeededXP sets the needed XP to a specific value
|
// SetNeededXP sets the needed XP to a specific value
|
||||||
func (p *Player) SetNeededXP(val int32) {
|
func (p *Player) SetNeededXP(val int32) {
|
||||||
p.GetInfoStruct().SetXPNeeded(val)
|
p.SetInfoStructXPNeeded(float64(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetNeededXP sets the needed XP based on current level
|
// SetNeededXP sets the needed XP based on current level
|
||||||
func (p *Player) SetNeededXPByLevel() {
|
func (p *Player) SetNeededXPByLevel() {
|
||||||
p.GetInfoStruct().SetXPNeeded(GetNeededXPByLevel(p.GetLevel()))
|
p.SetInfoStructXPNeeded(float64(GetNeededXPByLevel(p.GetLevel())))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetXP sets the current XP
|
// SetXP sets the current XP
|
||||||
func (p *Player) SetXP(val int32) {
|
func (p *Player) SetXP(val int32) {
|
||||||
p.GetInfoStruct().SetXP(val)
|
p.SetInfoStructXP(float64(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetNeededTSXP sets the needed tradeskill XP to a specific value
|
// SetNeededTSXP sets the needed tradeskill XP to a specific value
|
||||||
func (p *Player) SetNeededTSXP(val int32) {
|
func (p *Player) SetNeededTSXP(val int32) {
|
||||||
p.GetInfoStruct().SetTSXPNeeded(val)
|
p.SetInfoStructTSXPNeeded(float64(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetNeededTSXPByLevel sets the needed tradeskill XP based on current level
|
// SetNeededTSXPByLevel sets the needed tradeskill XP based on current level
|
||||||
func (p *Player) SetNeededTSXPByLevel() {
|
func (p *Player) SetNeededTSXPByLevel() {
|
||||||
p.GetInfoStruct().SetTSXPNeeded(GetNeededXPByLevel(p.GetTSLevel()))
|
p.SetInfoStructTSXPNeeded(float64(GetNeededXPByLevel(p.GetTSLevel())))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTSXP sets the current tradeskill XP
|
// SetTSXP sets the current tradeskill XP
|
||||||
func (p *Player) SetTSXP(val int32) {
|
func (p *Player) SetTSXP(val int32) {
|
||||||
p.GetInfoStruct().SetTSXP(val)
|
p.SetInfoStructTSXP(float64(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNeededXP returns the XP needed for next level
|
// GetNeededXP returns the XP needed for next level
|
||||||
func (p *Player) GetNeededXP() int32 {
|
func (p *Player) GetNeededXP() int32 {
|
||||||
return p.GetInfoStruct().GetXPNeeded()
|
return int32(p.GetInfoStructXPNeeded())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetXPDebt returns the current XP debt percentage
|
// GetXPDebt returns the current XP debt percentage
|
||||||
func (p *Player) GetXPDebt() float32 {
|
func (p *Player) GetXPDebt() float32 {
|
||||||
return p.GetInfoStruct().GetXPDebt()
|
return p.GetInfoStructXPDebt()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetXP returns the current XP
|
// GetXP returns the current XP
|
||||||
func (p *Player) GetXP() int32 {
|
func (p *Player) GetXP() int32 {
|
||||||
return p.GetInfoStruct().GetXP()
|
return int32(p.GetInfoStructXP())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNeededTSXP returns the tradeskill XP needed for next level
|
// GetNeededTSXP returns the tradeskill XP needed for next level
|
||||||
func (p *Player) GetNeededTSXP() int32 {
|
func (p *Player) GetNeededTSXP() int32 {
|
||||||
return p.GetInfoStruct().GetTSXPNeeded()
|
return int32(p.GetInfoStructTSXPNeeded())
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTSXP returns the current tradeskill XP
|
// GetTSXP returns the current tradeskill XP
|
||||||
func (p *Player) GetTSXP() int32 {
|
func (p *Player) GetTSXP() int32 {
|
||||||
return p.GetInfoStruct().GetTSXP()
|
return int32(p.GetInfoStructTSXP())
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddXP adds adventure XP to the player
|
// AddXP adds adventure XP to the player
|
||||||
@ -86,9 +87,8 @@ func (p *Player) AddXP(xpAmount int32) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
info := p.GetInfoStruct()
|
currentXP := int32(p.GetInfoStructXP())
|
||||||
currentXP := info.GetXP()
|
neededXP := int32(p.GetInfoStructXPNeeded())
|
||||||
neededXP := info.GetXPNeeded()
|
|
||||||
totalXP := currentXP + xpAmount
|
totalXP := currentXP + xpAmount
|
||||||
|
|
||||||
// Check if we've reached next level
|
// Check if we've reached next level
|
||||||
@ -99,7 +99,7 @@ func (p *Player) AddXP(xpAmount int32) bool {
|
|||||||
overflow := totalXP - neededXP
|
overflow := totalXP - neededXP
|
||||||
|
|
||||||
// Level up
|
// Level up
|
||||||
p.SetLevel(p.GetLevel()+1, true)
|
p.SetLevel(int16(p.GetLevel())+1)
|
||||||
p.SetNeededXPByLevel()
|
p.SetNeededXPByLevel()
|
||||||
|
|
||||||
// Set XP to overflow amount
|
// Set XP to overflow amount
|
||||||
@ -120,7 +120,7 @@ func (p *Player) AddXP(xpAmount int32) bool {
|
|||||||
|
|
||||||
// TODO: Send XP update packet
|
// TODO: Send XP update packet
|
||||||
p.SetCharSheetChanged(true)
|
p.SetCharSheetChanged(true)
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTSXP adds tradeskill XP to the player
|
// AddTSXP adds tradeskill XP to the player
|
||||||
@ -129,9 +129,8 @@ func (p *Player) AddTSXP(xpAmount int32) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
info := p.GetInfoStruct()
|
currentXP := int32(p.GetInfoStructTSXP())
|
||||||
currentXP := info.GetTSXP()
|
neededXP := int32(p.GetInfoStructTSXPNeeded())
|
||||||
neededXP := info.GetTSXPNeeded()
|
|
||||||
totalXP := currentXP + xpAmount
|
totalXP := currentXP + xpAmount
|
||||||
|
|
||||||
// Check if we've reached next level
|
// Check if we've reached next level
|
||||||
@ -173,7 +172,7 @@ func (p *Player) DoubleXPEnabled() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CalculateXP calculates the XP reward from a victim
|
// CalculateXP calculates the XP reward from a victim
|
||||||
func (p *Player) CalculateXP(victim *entity.Spawn) float32 {
|
func (p *Player) CalculateXP(victim *spawn.Spawn) float32 {
|
||||||
if victim == nil {
|
if victim == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -277,35 +276,28 @@ func (p *Player) CalculateOfflineDebtRecovery(unixTimestamp int32) {
|
|||||||
debtRecoveryRate := float32(1.0)
|
debtRecoveryRate := float32(1.0)
|
||||||
|
|
||||||
// Calculate adventure debt recovery
|
// Calculate adventure debt recovery
|
||||||
currentDebt := p.GetInfoStruct().GetXPDebt()
|
currentDebt := p.GetInfoStructXPDebt()
|
||||||
if currentDebt > 0 {
|
if currentDebt > 0 {
|
||||||
recovery := debtRecoveryRate * hoursOffline
|
recovery := debtRecoveryRate * hoursOffline
|
||||||
newDebt := currentDebt - recovery
|
newDebt := currentDebt - recovery
|
||||||
if newDebt < 0 {
|
if newDebt < 0 {
|
||||||
newDebt = 0
|
newDebt = 0
|
||||||
}
|
}
|
||||||
p.GetInfoStruct().SetXPDebt(newDebt)
|
p.SetInfoStructXPDebt(newDebt)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate tradeskill debt recovery
|
// Calculate tradeskill debt recovery
|
||||||
currentTSDebt := p.GetInfoStruct().GetTSXPDebt()
|
currentTSDebt := p.GetInfoStructTSXPDebt()
|
||||||
if currentTSDebt > 0 {
|
if currentTSDebt > 0 {
|
||||||
recovery := debtRecoveryRate * hoursOffline
|
recovery := debtRecoveryRate * hoursOffline
|
||||||
newDebt := currentTSDebt - recovery
|
newDebt := currentTSDebt - recovery
|
||||||
if newDebt < 0 {
|
if newDebt < 0 {
|
||||||
newDebt = 0
|
newDebt = 0
|
||||||
}
|
}
|
||||||
p.GetInfoStruct().SetTSXPDebt(newDebt)
|
p.SetInfoStructTSXPDebt(newDebt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTSLevel returns the player's tradeskill level
|
// Note: GetTSLevel is now implemented in stubs.go
|
||||||
func (p *Player) GetTSLevel() int8 {
|
|
||||||
return p.GetInfoStruct().GetTSLevel()
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetTSLevel sets the player's tradeskill level
|
// Note: SetTSLevel is now implemented in stubs.go
|
||||||
func (p *Player) SetTSLevel(level int8) {
|
|
||||||
p.GetInfoStruct().SetTSLevel(level)
|
|
||||||
p.SetCharSheetChanged(true)
|
|
||||||
}
|
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"eq2emu/internal/entity"
|
"eq2emu/internal/entity"
|
||||||
"eq2emu/internal/quests"
|
"eq2emu/internal/quests"
|
||||||
"eq2emu/internal/skills"
|
"eq2emu/internal/skills"
|
||||||
|
"eq2emu/internal/spawn"
|
||||||
"eq2emu/internal/spells"
|
"eq2emu/internal/spells"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -46,8 +47,8 @@ type PlayerManager interface {
|
|||||||
SendToZone(zoneID int32, message any) error
|
SendToZone(zoneID int32, message any) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlayerDatabase interface for database operations
|
// PlayerDatabaseInterface interface for database operations (if needed for testing)
|
||||||
type PlayerDatabase interface {
|
type PlayerDatabaseInterface interface {
|
||||||
// LoadPlayer loads a player from the database
|
// LoadPlayer loads a player from the database
|
||||||
LoadPlayer(characterID int32) (*Player, error)
|
LoadPlayer(characterID int32) (*Player, error)
|
||||||
|
|
||||||
@ -56,30 +57,6 @@ type PlayerDatabase interface {
|
|||||||
|
|
||||||
// DeletePlayer deletes a player from the database
|
// DeletePlayer deletes a player from the database
|
||||||
DeletePlayer(characterID int32) error
|
DeletePlayer(characterID int32) error
|
||||||
|
|
||||||
// LoadPlayerQuests loads player quests
|
|
||||||
LoadPlayerQuests(characterID int32) ([]*quests.Quest, error)
|
|
||||||
|
|
||||||
// SavePlayerQuests saves player quests
|
|
||||||
SavePlayerQuests(characterID int32, quests []*quests.Quest) error
|
|
||||||
|
|
||||||
// LoadPlayerSkills loads player skills
|
|
||||||
LoadPlayerSkills(characterID int32) ([]*skills.Skill, error)
|
|
||||||
|
|
||||||
// SavePlayerSkills saves player skills
|
|
||||||
SavePlayerSkills(characterID int32, skills []*skills.Skill) error
|
|
||||||
|
|
||||||
// LoadPlayerSpells loads player spells
|
|
||||||
LoadPlayerSpells(characterID int32) ([]*SpellBookEntry, error)
|
|
||||||
|
|
||||||
// SavePlayerSpells saves player spells
|
|
||||||
SavePlayerSpells(characterID int32, spells []*SpellBookEntry) error
|
|
||||||
|
|
||||||
// LoadPlayerHistory loads player history
|
|
||||||
LoadPlayerHistory(characterID int32) (map[int8]map[int8][]*HistoryData, error)
|
|
||||||
|
|
||||||
// SavePlayerHistory saves player history
|
|
||||||
SavePlayerHistory(characterID int32, history map[int8]map[int8][]*HistoryData) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlayerPacketHandler interface for handling player packets
|
// PlayerPacketHandler interface for handling player packets
|
||||||
@ -103,7 +80,7 @@ type PlayerEventHandler interface {
|
|||||||
OnPlayerLogout(player *Player) error
|
OnPlayerLogout(player *Player) error
|
||||||
|
|
||||||
// OnPlayerDeath called when player dies
|
// OnPlayerDeath called when player dies
|
||||||
OnPlayerDeath(player *Player, killer entity.Entity) error
|
OnPlayerDeath(player *Player, killer *entity.Entity) error
|
||||||
|
|
||||||
// OnPlayerResurrect called when player resurrects
|
// OnPlayerResurrect called when player resurrects
|
||||||
OnPlayerResurrect(player *Player) error
|
OnPlayerResurrect(player *Player) error
|
||||||
@ -118,7 +95,7 @@ type PlayerEventHandler interface {
|
|||||||
OnPlayerQuestComplete(player *Player, quest *quests.Quest) error
|
OnPlayerQuestComplete(player *Player, quest *quests.Quest) error
|
||||||
|
|
||||||
// OnPlayerSpellCast called when player casts a spell
|
// OnPlayerSpellCast called when player casts a spell
|
||||||
OnPlayerSpellCast(player *Player, spell *spells.Spell, target entity.Entity) error
|
OnPlayerSpellCast(player *Player, spell *spells.Spell, target *entity.Entity) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlayerValidator interface for validating player operations
|
// PlayerValidator interface for validating player operations
|
||||||
@ -130,7 +107,7 @@ type PlayerValidator interface {
|
|||||||
ValidateMovement(player *Player, x, y, z, heading float32) error
|
ValidateMovement(player *Player, x, y, z, heading float32) error
|
||||||
|
|
||||||
// ValidateSpellCast validates spell casting
|
// ValidateSpellCast validates spell casting
|
||||||
ValidateSpellCast(player *Player, spell *spells.Spell, target entity.Entity) error
|
ValidateSpellCast(player *Player, spell *spells.Spell, target *entity.Entity) error
|
||||||
|
|
||||||
// ValidateItemUse validates item usage
|
// ValidateItemUse validates item usage
|
||||||
ValidateItemUse(player *Player, item *Item) error
|
ValidateItemUse(player *Player, item *Item) error
|
||||||
@ -169,10 +146,10 @@ type PlayerStatistics interface {
|
|||||||
RecordPlayerLogout(player *Player)
|
RecordPlayerLogout(player *Player)
|
||||||
|
|
||||||
// RecordPlayerDeath records a player death
|
// RecordPlayerDeath records a player death
|
||||||
RecordPlayerDeath(player *Player, killer entity.Entity)
|
RecordPlayerDeath(player *Player, killer *entity.Entity)
|
||||||
|
|
||||||
// RecordPlayerKill records a player kill
|
// RecordPlayerKill records a player kill
|
||||||
RecordPlayerKill(player *Player, victim entity.Entity)
|
RecordPlayerKill(player *Player, victim *entity.Entity)
|
||||||
|
|
||||||
// RecordQuestComplete records a quest completion
|
// RecordQuestComplete records a quest completion
|
||||||
RecordQuestComplete(player *Player, quest *quests.Quest)
|
RecordQuestComplete(player *Player, quest *quests.Quest)
|
||||||
@ -223,8 +200,8 @@ func (pa *PlayerAdapter) GetEntity() *entity.Entity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetSpawn returns the player as a spawn
|
// GetSpawn returns the player as a spawn
|
||||||
func (pa *PlayerAdapter) GetSpawn() *entity.Spawn {
|
func (pa *PlayerAdapter) GetSpawn() *spawn.Spawn {
|
||||||
return &pa.player.Entity.Spawn
|
return pa.player.Entity.Spawn
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsPlayer always returns true for player adapter
|
// IsPlayer always returns true for player adapter
|
||||||
@ -314,10 +291,10 @@ func (pa *PlayerAdapter) IsAlive() bool {
|
|||||||
|
|
||||||
// IsInCombat returns whether the player is in combat
|
// IsInCombat returns whether the player is in combat
|
||||||
func (pa *PlayerAdapter) IsInCombat() bool {
|
func (pa *PlayerAdapter) IsInCombat() bool {
|
||||||
return pa.player.GetInfoStruct().GetEngageCommands() != 0
|
return pa.player.GetPlayerEngageCommands() != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDistance returns distance to another spawn
|
// GetDistance returns distance to another spawn
|
||||||
func (pa *PlayerAdapter) GetDistance(other *entity.Spawn) float32 {
|
func (pa *PlayerAdapter) GetDistance(other *spawn.Spawn) float32 {
|
||||||
return pa.player.GetDistance(other)
|
return pa.player.GetDistance(other.GetX(), other.GetY(), other.GetZ(), true)
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ package player
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ type Manager struct {
|
|||||||
validators []PlayerValidator
|
validators []PlayerValidator
|
||||||
|
|
||||||
// Database interface
|
// Database interface
|
||||||
database PlayerDatabase
|
database *PlayerDatabase
|
||||||
|
|
||||||
// Packet handler
|
// Packet handler
|
||||||
packetHandler PlayerPacketHandler
|
packetHandler PlayerPacketHandler
|
||||||
@ -138,7 +139,7 @@ func (m *Manager) AddPlayer(player *Player) error {
|
|||||||
|
|
||||||
playerID := player.GetSpawnID()
|
playerID := player.GetSpawnID()
|
||||||
characterID := player.GetCharacterID()
|
characterID := player.GetCharacterID()
|
||||||
name := player.GetName()
|
name := strings.TrimSpace(strings.Trim(player.GetName(), "\x00")) // Trim padding and null bytes
|
||||||
zoneID := player.GetZone()
|
zoneID := player.GetZone()
|
||||||
|
|
||||||
// Check for duplicates
|
// Check for duplicates
|
||||||
@ -189,7 +190,8 @@ func (m *Manager) RemovePlayer(playerID int32) error {
|
|||||||
// Remove from maps
|
// Remove from maps
|
||||||
delete(m.players, playerID)
|
delete(m.players, playerID)
|
||||||
delete(m.playersByCharID, player.GetCharacterID())
|
delete(m.playersByCharID, player.GetCharacterID())
|
||||||
delete(m.playersByName, player.GetName())
|
name := strings.TrimSpace(strings.Trim(player.GetName(), "\x00"))
|
||||||
|
delete(m.playersByName, name)
|
||||||
|
|
||||||
// Remove from zone map
|
// Remove from zone map
|
||||||
zoneID := player.GetZone()
|
zoneID := player.GetZone()
|
||||||
@ -235,7 +237,7 @@ func (m *Manager) GetPlayerByName(name string) *Player {
|
|||||||
m.playersLock.RLock()
|
m.playersLock.RLock()
|
||||||
defer m.playersLock.RUnlock()
|
defer m.playersLock.RUnlock()
|
||||||
|
|
||||||
return m.playersByName[name]
|
return m.playersByName[strings.TrimSpace(strings.Trim(name, "\x00"))]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPlayerByCharacterID returns a player by character ID
|
// GetPlayerByCharacterID returns a player by character ID
|
||||||
@ -379,7 +381,7 @@ func (m *Manager) AddValidator(validator PlayerValidator) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetDatabase sets the database interface
|
// SetDatabase sets the database interface
|
||||||
func (m *Manager) SetDatabase(db PlayerDatabase) {
|
func (m *Manager) SetDatabase(db *PlayerDatabase) {
|
||||||
m.database = db
|
m.database = db
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -590,7 +592,7 @@ func (m *Manager) FirePlayerLevelUpEvent(player *Player, newLevel int8) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FirePlayerDeathEvent fires a death event
|
// FirePlayerDeathEvent fires a death event
|
||||||
func (m *Manager) FirePlayerDeathEvent(player *Player, killer entity.Entity) {
|
func (m *Manager) FirePlayerDeathEvent(player *Player, killer *entity.Entity) {
|
||||||
m.eventLock.RLock()
|
m.eventLock.RLock()
|
||||||
defer m.eventLock.RUnlock()
|
defer m.eventLock.RUnlock()
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"eq2emu/internal/common"
|
"eq2emu/internal/common"
|
||||||
"eq2emu/internal/entity"
|
"eq2emu/internal/entity"
|
||||||
"eq2emu/internal/quests"
|
"eq2emu/internal/quests"
|
||||||
|
"eq2emu/internal/spawn"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Global XP table
|
// Global XP table
|
||||||
@ -27,7 +28,7 @@ func NewPlayer() *Player {
|
|||||||
playerQuests: make(map[int32]*quests.Quest),
|
playerQuests: make(map[int32]*quests.Quest),
|
||||||
completedQuests: make(map[int32]*quests.Quest),
|
completedQuests: make(map[int32]*quests.Quest),
|
||||||
pendingQuests: make(map[int32]*quests.Quest),
|
pendingQuests: make(map[int32]*quests.Quest),
|
||||||
currentQuestFlagged: make(map[*entity.Spawn]bool),
|
currentQuestFlagged: make(map[*spawn.Spawn]bool),
|
||||||
playerSpawnQuestsRequired: make(map[int32][]int32),
|
playerSpawnQuestsRequired: make(map[int32][]int32),
|
||||||
playerSpawnHistoryRequired: make(map[int32][]int32),
|
playerSpawnHistoryRequired: make(map[int32][]int32),
|
||||||
spawnVisPacketList: make(map[int32]string),
|
spawnVisPacketList: make(map[int32]string),
|
||||||
@ -35,8 +36,8 @@ func NewPlayer() *Player {
|
|||||||
spawnPosPacketList: make(map[int32]string),
|
spawnPosPacketList: make(map[int32]string),
|
||||||
spawnPacketSent: make(map[int32]int8),
|
spawnPacketSent: make(map[int32]int8),
|
||||||
spawnStateList: make(map[int32]*SpawnQueueState),
|
spawnStateList: make(map[int32]*SpawnQueueState),
|
||||||
playerSpawnIDMap: make(map[int32]*entity.Spawn),
|
playerSpawnIDMap: make(map[int32]*spawn.Spawn),
|
||||||
playerSpawnReverseIDMap: make(map[*entity.Spawn]int32),
|
playerSpawnReverseIDMap: make(map[*spawn.Spawn]int32),
|
||||||
playerAggroRangeSpawns: make(map[int32]bool),
|
playerAggroRangeSpawns: make(map[int32]bool),
|
||||||
pendingLootItems: make(map[int32]map[int32]bool),
|
pendingLootItems: make(map[int32]map[int32]bool),
|
||||||
friendList: make(map[string]int8),
|
friendList: make(map[string]int8),
|
||||||
@ -49,7 +50,7 @@ func NewPlayer() *Player {
|
|||||||
mailList: make(map[int32]*Mail),
|
mailList: make(map[int32]*Mail),
|
||||||
targetInvisHistory: make(map[int32]bool),
|
targetInvisHistory: make(map[int32]bool),
|
||||||
spawnedBots: make(map[int32]int32),
|
spawnedBots: make(map[int32]int32),
|
||||||
macroIcons: make(map[int32]int16),
|
// macroIcons field removed - not in struct definition
|
||||||
sortedTraitList: make(map[int8]map[int8][]*TraitData),
|
sortedTraitList: make(map[int8]map[int8][]*TraitData),
|
||||||
classTraining: make(map[int8][]*TraitData),
|
classTraining: make(map[int8][]*TraitData),
|
||||||
raceTraits: make(map[int8][]*TraitData),
|
raceTraits: make(map[int8][]*TraitData),
|
||||||
@ -62,11 +63,12 @@ func NewPlayer() *Player {
|
|||||||
|
|
||||||
// Set player-specific defaults
|
// Set player-specific defaults
|
||||||
p.SetSpawnType(4) // Player spawn type
|
p.SetSpawnType(4) // Player spawn type
|
||||||
p.appearance.DisplayName = 1
|
// TODO: Set appearance data through proper methods when available
|
||||||
p.appearance.ShowCommandIcon = 1
|
// appearance.DisplayName = 1
|
||||||
p.appearance.PlayerFlag = 1
|
// appearance.ShowCommandIcon = 1
|
||||||
p.appearance.Targetable = 1
|
// appearance.PlayerFlag = 1
|
||||||
p.appearance.ShowLevel = 1
|
// appearance.Targetable = 1
|
||||||
|
// appearance.ShowLevel = 1
|
||||||
|
|
||||||
// Set default away message
|
// Set default away message
|
||||||
p.awayMessage = "Sorry, I am A.F.K. (Away From Keyboard)"
|
p.awayMessage = "Sorry, I am A.F.K. (Away From Keyboard)"
|
||||||
@ -76,8 +78,8 @@ func NewPlayer() *Player {
|
|||||||
p.AddSecondaryEntityCommand("Who", 10000, "who", "", 0, 0)
|
p.AddSecondaryEntityCommand("Who", 10000, "who", "", 0, 0)
|
||||||
|
|
||||||
// Initialize self in spawn maps
|
// Initialize self in spawn maps
|
||||||
p.playerSpawnIDMap[1] = &p.Entity.Spawn
|
p.playerSpawnIDMap[1] = p.Entity.Spawn
|
||||||
p.playerSpawnReverseIDMap[&p.Entity.Spawn] = 1
|
p.playerSpawnReverseIDMap[p.Entity.Spawn] = 1
|
||||||
|
|
||||||
// Set save spell effects
|
// Set save spell effects
|
||||||
p.stopSaveSpellEffects = false
|
p.stopSaveSpellEffects = false
|
||||||
@ -227,52 +229,77 @@ func (p *Player) AddPlayerDiscoveredPOI(locationID int32) {
|
|||||||
|
|
||||||
// SetSideSpeed sets the player's side movement speed
|
// SetSideSpeed sets the player's side movement speed
|
||||||
func (p *Player) SetSideSpeed(sideSpeed float32, updateFlags bool) {
|
func (p *Player) SetSideSpeed(sideSpeed float32, updateFlags bool) {
|
||||||
p.SetPos(&p.appearance.Pos.SideSpeed, sideSpeed, updateFlags)
|
// TODO: Implement when appearance system is available
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerMovementData[charID] == nil {
|
||||||
|
playerMovementData[charID] = make(map[string]float32)
|
||||||
|
}
|
||||||
|
playerMovementData[charID]["side_speed"] = sideSpeed
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSideSpeed returns the player's side movement speed
|
// GetSideSpeed returns the player's side movement speed
|
||||||
func (p *Player) GetSideSpeed() float32 {
|
func (p *Player) GetSideSpeed() float32 {
|
||||||
return p.appearance.Pos.SideSpeed
|
return p.GetPos("side_speed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetVertSpeed sets the player's vertical movement speed
|
// SetVertSpeed sets the player's vertical movement speed
|
||||||
func (p *Player) SetVertSpeed(vertSpeed float32, updateFlags bool) {
|
func (p *Player) SetVertSpeed(vertSpeed float32, updateFlags bool) {
|
||||||
p.SetPos(&p.appearance.Pos.VertSpeed, vertSpeed, updateFlags)
|
// TODO: Implement when appearance system is available
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerMovementData[charID] == nil {
|
||||||
|
playerMovementData[charID] = make(map[string]float32)
|
||||||
|
}
|
||||||
|
playerMovementData[charID]["vert_speed"] = vertSpeed
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetVertSpeed returns the player's vertical movement speed
|
// GetVertSpeed returns the player's vertical movement speed
|
||||||
func (p *Player) GetVertSpeed() float32 {
|
func (p *Player) GetVertSpeed() float32 {
|
||||||
return p.appearance.Pos.VertSpeed
|
return p.GetPos("vert_speed")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetClientHeading1 sets the client heading 1
|
// SetClientHeading1 sets the client heading 1
|
||||||
func (p *Player) SetClientHeading1(heading float32, updateFlags bool) {
|
func (p *Player) SetClientHeading1(heading float32, updateFlags bool) {
|
||||||
p.SetPos(&p.appearance.Pos.ClientHeading1, heading, updateFlags)
|
// TODO: Implement when appearance system is available
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerMovementData[charID] == nil {
|
||||||
|
playerMovementData[charID] = make(map[string]float32)
|
||||||
|
}
|
||||||
|
playerMovementData[charID]["client_heading1"] = heading
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClientHeading1 returns the client heading 1
|
// GetClientHeading1 returns the client heading 1
|
||||||
func (p *Player) GetClientHeading1() float32 {
|
func (p *Player) GetClientHeading1() float32 {
|
||||||
return p.appearance.Pos.ClientHeading1
|
return p.GetPos("client_heading1")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetClientHeading2 sets the client heading 2
|
// SetClientHeading2 sets the client heading 2
|
||||||
func (p *Player) SetClientHeading2(heading float32, updateFlags bool) {
|
func (p *Player) SetClientHeading2(heading float32, updateFlags bool) {
|
||||||
p.SetPos(&p.appearance.Pos.ClientHeading2, heading, updateFlags)
|
// TODO: Implement when appearance system is available
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerMovementData[charID] == nil {
|
||||||
|
playerMovementData[charID] = make(map[string]float32)
|
||||||
|
}
|
||||||
|
playerMovementData[charID]["client_heading2"] = heading
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClientHeading2 returns the client heading 2
|
// GetClientHeading2 returns the client heading 2
|
||||||
func (p *Player) GetClientHeading2() float32 {
|
func (p *Player) GetClientHeading2() float32 {
|
||||||
return p.appearance.Pos.ClientHeading2
|
return p.GetPos("client_heading2")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetClientPitch sets the client pitch
|
// SetClientPitch sets the client pitch
|
||||||
func (p *Player) SetClientPitch(pitch float32, updateFlags bool) {
|
func (p *Player) SetClientPitch(pitch float32, updateFlags bool) {
|
||||||
p.SetPos(&p.appearance.Pos.ClientPitch, pitch, updateFlags)
|
// TODO: Implement when appearance system is available
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerMovementData[charID] == nil {
|
||||||
|
playerMovementData[charID] = make(map[string]float32)
|
||||||
|
}
|
||||||
|
playerMovementData[charID]["client_pitch"] = pitch
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetClientPitch returns the client pitch
|
// GetClientPitch returns the client pitch
|
||||||
func (p *Player) GetClientPitch() float32 {
|
func (p *Player) GetClientPitch() float32 {
|
||||||
return p.appearance.Pos.ClientPitch
|
return p.GetPos("client_pitch")
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsResurrecting returns whether the player is currently resurrecting
|
// IsResurrecting returns whether the player is currently resurrecting
|
||||||
@ -485,7 +512,7 @@ func (p *Player) SetSaveSpellEffects(val bool) {
|
|||||||
func (p *Player) ResetMentorship() bool {
|
func (p *Player) ResetMentorship() bool {
|
||||||
mentorshipStatus := p.resetMentorship
|
mentorshipStatus := p.resetMentorship
|
||||||
if mentorshipStatus {
|
if mentorshipStatus {
|
||||||
p.SetMentorStats(p.GetLevel(), 0, true)
|
p.SetMentorStats(int32(p.GetLevel()), 0, true)
|
||||||
}
|
}
|
||||||
p.resetMentorship = false
|
p.resetMentorship = false
|
||||||
return mentorshipStatus
|
return mentorshipStatus
|
||||||
@ -521,8 +548,8 @@ func InitXPTable() {
|
|||||||
levelXPReq = make(map[int8]int32)
|
levelXPReq = make(map[int8]int32)
|
||||||
// TODO: Load XP requirements from database or config
|
// TODO: Load XP requirements from database or config
|
||||||
// For now, using placeholder values
|
// For now, using placeholder values
|
||||||
for i := int8(1); i <= 100; i++ {
|
for i := int8(1); i <= 100; i++ { // Reasonable level cap of 100
|
||||||
levelXPReq[i] = int32(i * 1000)
|
levelXPReq[i] = int32(i) * 1000
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -542,47 +569,43 @@ func (p *Player) Cleanup() {
|
|||||||
p.SetSaveSpellEffects(true)
|
p.SetSaveSpellEffects(true)
|
||||||
|
|
||||||
// Clear spells
|
// Clear spells
|
||||||
for _, spell := range p.spells {
|
for range p.spells {
|
||||||
spell = nil
|
// Individual elements will be cleared when slice is nilled
|
||||||
}
|
}
|
||||||
p.spells = nil
|
p.spells = nil
|
||||||
|
|
||||||
// Clear quickbar
|
// Clear quickbar
|
||||||
for _, item := range p.quickbarItems {
|
for range p.quickbarItems {
|
||||||
item = nil
|
// Individual elements will be cleared when slice is nilled
|
||||||
}
|
}
|
||||||
p.quickbarItems = nil
|
p.quickbarItems = nil
|
||||||
|
|
||||||
// Clear quest spawn requirements
|
// Clear quest spawn requirements
|
||||||
p.playerSpawnQuestsRequiredMutex.Lock()
|
p.playerSpawnQuestsRequiredMutex.Lock()
|
||||||
for _, list := range p.playerSpawnQuestsRequired {
|
for range p.playerSpawnQuestsRequired {
|
||||||
list = nil
|
// Individual elements will be cleared when map is nilled
|
||||||
}
|
}
|
||||||
p.playerSpawnQuestsRequired = nil
|
p.playerSpawnQuestsRequired = nil
|
||||||
p.playerSpawnQuestsRequiredMutex.Unlock()
|
p.playerSpawnQuestsRequiredMutex.Unlock()
|
||||||
|
|
||||||
// Clear history spawn requirements
|
// Clear history spawn requirements
|
||||||
p.playerSpawnHistoryRequiredMutex.Lock()
|
p.playerSpawnHistoryRequiredMutex.Lock()
|
||||||
for _, list := range p.playerSpawnHistoryRequired {
|
for range p.playerSpawnHistoryRequired {
|
||||||
list = nil
|
// Individual elements will be cleared when map is nilled
|
||||||
}
|
}
|
||||||
p.playerSpawnHistoryRequired = nil
|
p.playerSpawnHistoryRequired = nil
|
||||||
p.playerSpawnHistoryRequiredMutex.Unlock()
|
p.playerSpawnHistoryRequiredMutex.Unlock()
|
||||||
|
|
||||||
// Clear character history
|
// Clear character history
|
||||||
for _, typeMap := range p.characterHistory {
|
for range p.characterHistory {
|
||||||
for _, histList := range typeMap {
|
// Individual elements will be cleared when map is nilled
|
||||||
for _, hist := range histList {
|
|
||||||
hist = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
p.characterHistory = nil
|
p.characterHistory = nil
|
||||||
|
|
||||||
// Clear LUA history
|
// Clear LUA history
|
||||||
p.luaHistoryMutex.Lock()
|
p.luaHistoryMutex.Lock()
|
||||||
for _, hist := range p.charLuaHistory {
|
for range p.charLuaHistory {
|
||||||
hist = nil
|
// Individual elements will be cleared when map is nilled
|
||||||
}
|
}
|
||||||
p.charLuaHistory = nil
|
p.charLuaHistory = nil
|
||||||
p.luaHistoryMutex.Unlock()
|
p.luaHistoryMutex.Unlock()
|
||||||
@ -739,7 +762,31 @@ func (p *Player) ClearPendingItemRewards() {
|
|||||||
|
|
||||||
// ClearEverything performs final cleanup
|
// ClearEverything performs final cleanup
|
||||||
func (p *Player) ClearEverything() {
|
func (p *Player) ClearEverything() {
|
||||||
// TODO: Implement final cleanup logic
|
// Clear friends list
|
||||||
|
for name := range p.friendList {
|
||||||
|
delete(p.friendList, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear ignore list
|
||||||
|
for name := range p.ignoreList {
|
||||||
|
delete(p.ignoreList, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear quests
|
||||||
|
p.playerQuestsMutex.Lock()
|
||||||
|
for id := range p.playerQuests {
|
||||||
|
delete(p.playerQuests, id)
|
||||||
|
}
|
||||||
|
for id := range p.completedQuests {
|
||||||
|
delete(p.completedQuests, id)
|
||||||
|
}
|
||||||
|
for id := range p.pendingQuests {
|
||||||
|
delete(p.pendingQuests, id)
|
||||||
|
}
|
||||||
|
p.playerQuestsMutex.Unlock()
|
||||||
|
|
||||||
|
// Clear other data
|
||||||
|
// TODO: Clear additional data as needed
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCharacterInstances returns the character instances manager
|
// GetCharacterInstances returns the character instances manager
|
||||||
@ -818,5 +865,14 @@ func (p *Player) ClearGMVisualFilters() {
|
|||||||
p.gmVisualFilters = nil
|
p.gmVisualFilters = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLevel sets the player's level in both spawn appearance and info struct
|
||||||
|
func (p *Player) SetLevel(level int16) {
|
||||||
|
// Update spawn appearance level
|
||||||
|
p.Spawn.SetLevel(level)
|
||||||
|
// Update info struct level
|
||||||
|
p.GetInfoStruct().SetLevel(level)
|
||||||
|
p.SetCharSheetChanged(true)
|
||||||
|
}
|
||||||
|
|
||||||
// macroIcons map - declared at package level since it was referenced but not in struct
|
// macroIcons map - declared at package level since it was referenced but not in struct
|
||||||
var macroIcons map[int32]int16
|
var macroIcons map[int32]int16
|
||||||
|
@ -3,7 +3,7 @@ package player
|
|||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"eq2emu/internal/entity"
|
"eq2emu/internal/spawn"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewPlayerInfo creates a new PlayerInfo instance
|
// NewPlayerInfo creates a new PlayerInfo instance
|
||||||
@ -16,27 +16,27 @@ func NewPlayerInfo(player *Player) *PlayerInfo {
|
|||||||
|
|
||||||
// CalculateXPPercentages calculates XP bar percentages for display
|
// CalculateXPPercentages calculates XP bar percentages for display
|
||||||
func (pi *PlayerInfo) CalculateXPPercentages() {
|
func (pi *PlayerInfo) CalculateXPPercentages() {
|
||||||
xpNeeded := pi.infoStruct.GetXPNeeded()
|
xpNeeded := int32(pi.player.GetInfoStructXPNeeded())
|
||||||
if xpNeeded > 0 {
|
if xpNeeded > 0 {
|
||||||
divPercent := (float64(pi.infoStruct.GetXP()) / float64(xpNeeded)) * 100.0
|
divPercent := (pi.player.GetInfoStructXP() / float64(xpNeeded)) * 100.0
|
||||||
percentage := int16(divPercent) * 10
|
percentage := int16(divPercent) * 10
|
||||||
whole := math.Floor(divPercent)
|
whole := math.Floor(divPercent)
|
||||||
fractional := divPercent - whole
|
fractional := divPercent - whole
|
||||||
|
|
||||||
pi.infoStruct.SetXPYellow(percentage)
|
pi.player.SetInfoStructXPYellow(percentage)
|
||||||
pi.infoStruct.SetXPBlue(int16(fractional * 1000))
|
pi.player.SetInfoStructXPBlue(int16(fractional * 1000))
|
||||||
|
|
||||||
// Vitality bars probably need a revisit
|
// Vitality bars probably need a revisit
|
||||||
pi.infoStruct.SetXPBlueVitalityBar(0)
|
pi.player.SetInfoStructXPBlueVitalityBar(0)
|
||||||
pi.infoStruct.SetXPYellowVitalityBar(0)
|
pi.player.SetInfoStructXPYellowVitalityBar(0)
|
||||||
|
|
||||||
if pi.player.GetXPVitality() > 0 {
|
if pi.player.GetXPVitality() > 0 {
|
||||||
vitalityTotal := pi.player.GetXPVitality()*10 + float32(percentage)
|
vitalityTotal := pi.player.GetXPVitality()*10 + float32(percentage)
|
||||||
vitalityTotal -= float32((int(percentage/100) * 100))
|
vitalityTotal -= float32((int(percentage/100) * 100))
|
||||||
if vitalityTotal < 100 { // 10%
|
if vitalityTotal < 100 { // 10%
|
||||||
pi.infoStruct.SetXPBlueVitalityBar(pi.infoStruct.GetXPBlue() + int16(pi.player.GetXPVitality()*10))
|
pi.player.SetInfoStructXPBlueVitalityBar(pi.player.GetInfoStructXPBlue() + int16(pi.player.GetXPVitality()*10))
|
||||||
} else {
|
} else {
|
||||||
pi.infoStruct.SetXPYellowVitalityBar(pi.infoStruct.GetXPYellow() + int16(pi.player.GetXPVitality()*10))
|
pi.player.SetInfoStructXPYellowVitalityBar(pi.player.GetInfoStructXPYellow() + int16(pi.player.GetXPVitality()*10))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,11 +44,11 @@ func (pi *PlayerInfo) CalculateXPPercentages() {
|
|||||||
|
|
||||||
// CalculateTSXPPercentages calculates tradeskill XP bar percentages
|
// CalculateTSXPPercentages calculates tradeskill XP bar percentages
|
||||||
func (pi *PlayerInfo) CalculateTSXPPercentages() {
|
func (pi *PlayerInfo) CalculateTSXPPercentages() {
|
||||||
tsXPNeeded := pi.infoStruct.GetTSXPNeeded()
|
tsXPNeeded := int32(pi.player.GetInfoStructTSXPNeeded())
|
||||||
if tsXPNeeded > 0 {
|
if tsXPNeeded > 0 {
|
||||||
percentage := (float64(pi.infoStruct.GetTSXP()) / float64(tsXPNeeded)) * 1000
|
percentage := (pi.player.GetInfoStructTSXP() / float64(tsXPNeeded)) * 1000
|
||||||
pi.infoStruct.SetTradeskillExpYellow(int16(percentage))
|
pi.player.SetInfoStructTradeskillExpYellow(int16(percentage))
|
||||||
pi.infoStruct.SetTradeskillExpBlue(int16((percentage - float64(pi.infoStruct.GetTradeskillExpYellow())) * 1000))
|
pi.player.SetInfoStructTradeskillExpBlue(int16((percentage - float64(pi.player.GetInfoStructTradeskillExpYellow())) * 1000))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +148,7 @@ func (pi *PlayerInfo) SetBoatZ(z float32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetBoatSpawn sets the boat spawn
|
// SetBoatSpawn sets the boat spawn
|
||||||
func (pi *PlayerInfo) SetBoatSpawn(spawn *entity.Spawn) {
|
func (pi *PlayerInfo) SetBoatSpawn(spawn *spawn.Spawn) {
|
||||||
if spawn != nil {
|
if spawn != nil {
|
||||||
pi.boatSpawn = spawn.GetDatabaseID()
|
pi.boatSpawn = spawn.GetDatabaseID()
|
||||||
} else {
|
} else {
|
||||||
@ -158,7 +158,7 @@ func (pi *PlayerInfo) SetBoatSpawn(spawn *entity.Spawn) {
|
|||||||
|
|
||||||
// SetAccountAge sets the account age base
|
// SetAccountAge sets the account age base
|
||||||
func (pi *PlayerInfo) SetAccountAge(age int32) {
|
func (pi *PlayerInfo) SetAccountAge(age int32) {
|
||||||
pi.infoStruct.SetAccountAgeBase(age)
|
pi.player.SetInfoStructAccountAgeBase(age)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveOldPackets cleans up old packet data
|
// RemoveOldPackets cleans up old packet data
|
||||||
|
@ -1,22 +1,662 @@
|
|||||||
package player
|
package player
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"eq2emu/internal/quests"
|
||||||
|
"zombiezen.com/go/sqlite"
|
||||||
|
"zombiezen.com/go/sqlite/sqlitex"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPackageBuild(t *testing.T) {
|
// TestNewPlayer tests player creation
|
||||||
// Basic test to verify the package builds
|
func TestNewPlayer(t *testing.T) {
|
||||||
manager := NewPlayerManager()
|
p := NewPlayer()
|
||||||
if manager == nil {
|
if p == nil {
|
||||||
t.Fatal("NewPlayerManager returned nil")
|
t.Fatal("NewPlayer returned nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify default values
|
||||||
|
if p.GetCharacterID() != 0 {
|
||||||
|
t.Errorf("Expected character ID 0, got %d", p.GetCharacterID())
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.GetTutorialStep() != 0 {
|
||||||
|
t.Errorf("Expected tutorial step 0, got %d", p.GetTutorialStep())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.IsPlayer() {
|
||||||
|
t.Error("Expected IsPlayer to return true")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that maps are initialized
|
||||||
|
if p.playerQuests == nil {
|
||||||
|
t.Error("playerQuests map not initialized")
|
||||||
|
}
|
||||||
|
if p.completedQuests == nil {
|
||||||
|
t.Error("completedQuests map not initialized")
|
||||||
|
}
|
||||||
|
if p.friendList == nil {
|
||||||
|
t.Error("friendList map not initialized")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestPlayerManager tests the player manager functionality
|
||||||
func TestPlayerManager(t *testing.T) {
|
func TestPlayerManager(t *testing.T) {
|
||||||
manager := NewPlayerManager()
|
config := ManagerConfig{
|
||||||
|
MaxPlayers: 100,
|
||||||
|
SaveInterval: time.Minute * 5,
|
||||||
|
StatsInterval: time.Second * 30,
|
||||||
|
}
|
||||||
|
|
||||||
stats := manager.GetStats()
|
manager := NewManager(config)
|
||||||
if stats.TotalPlayers < 0 {
|
if manager == nil {
|
||||||
t.Error("Expected valid stats")
|
t.Fatal("NewManager returned nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test adding a player
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetSpawnID(1001) // Set unique spawn ID
|
||||||
|
player.SetCharacterID(123)
|
||||||
|
player.SetName("TestPlayer")
|
||||||
|
player.SetLevel(10)
|
||||||
|
|
||||||
|
err := manager.AddPlayer(player)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to add player: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test retrieving player by ID
|
||||||
|
retrieved := manager.GetPlayer(player.GetSpawnID())
|
||||||
|
if retrieved == nil {
|
||||||
|
t.Error("Failed to retrieve player by ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test retrieving player by name
|
||||||
|
byName := manager.GetPlayerByName("TestPlayer")
|
||||||
|
if byName == nil {
|
||||||
|
// Debug: Check what name was actually stored
|
||||||
|
allPlayers := manager.GetAllPlayers()
|
||||||
|
if len(allPlayers) > 0 {
|
||||||
|
t.Errorf("Failed to retrieve player by name. Player has name: %s", allPlayers[0].GetName())
|
||||||
|
} else {
|
||||||
|
t.Error("Failed to retrieve player by name. No players in manager")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test retrieving player by character ID
|
||||||
|
byCharID := manager.GetPlayerByCharacterID(123)
|
||||||
|
if byCharID == nil {
|
||||||
|
t.Error("Failed to retrieve player by character ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test player count
|
||||||
|
count := manager.GetPlayerCount()
|
||||||
|
if count != 1 {
|
||||||
|
t.Errorf("Expected player count 1, got %d", count)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test removing player
|
||||||
|
err = manager.RemovePlayer(player.GetSpawnID())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to remove player: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
count = manager.GetPlayerCount()
|
||||||
|
if count != 0 {
|
||||||
|
t.Errorf("Expected player count 0 after removal, got %d", count)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPlayerDatabase tests database operations
|
||||||
|
func TestPlayerDatabase(t *testing.T) {
|
||||||
|
// Create in-memory database for testing
|
||||||
|
conn, err := sqlite.OpenConn(":memory:", sqlite.OpenReadWrite|sqlite.OpenCreate)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to open database: %v", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
// Create test table
|
||||||
|
createTable := `
|
||||||
|
CREATE TABLE IF NOT EXISTS characters (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
level INTEGER DEFAULT 1,
|
||||||
|
race INTEGER DEFAULT 0,
|
||||||
|
class INTEGER DEFAULT 0,
|
||||||
|
zone_id INTEGER DEFAULT 0,
|
||||||
|
x REAL DEFAULT 0,
|
||||||
|
y REAL DEFAULT 0,
|
||||||
|
z REAL DEFAULT 0,
|
||||||
|
heading REAL DEFAULT 0,
|
||||||
|
created_date TEXT,
|
||||||
|
last_save TEXT
|
||||||
|
)`
|
||||||
|
|
||||||
|
err = sqlitex.Execute(conn, createTable, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create table: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db := NewPlayerDatabase(conn)
|
||||||
|
|
||||||
|
// Create test player
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(1)
|
||||||
|
player.SetName("TestHero")
|
||||||
|
player.SetLevel(20)
|
||||||
|
player.SetClass(1)
|
||||||
|
player.SetRace(2)
|
||||||
|
player.SetX(100.5)
|
||||||
|
player.SetY(200.5, false)
|
||||||
|
player.SetZ(300.5)
|
||||||
|
|
||||||
|
// Test saving player
|
||||||
|
err = db.SavePlayer(player)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to save player: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test loading player
|
||||||
|
loaded, err := db.LoadPlayer(1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load player: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadedName := strings.TrimSpace(strings.Trim(loaded.GetName(), "\x00"))
|
||||||
|
if loadedName != "TestHero" {
|
||||||
|
t.Errorf("Expected name TestHero, got %s", loadedName)
|
||||||
|
}
|
||||||
|
|
||||||
|
loadedLevel := loaded.GetLevel()
|
||||||
|
if loadedLevel != 20 {
|
||||||
|
t.Errorf("Expected level 20, got %d", loadedLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test updating player
|
||||||
|
loaded.SetLevel(21)
|
||||||
|
err = db.SavePlayer(loaded)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to update player: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test deleting player
|
||||||
|
err = db.DeletePlayer(1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to delete player: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify deletion
|
||||||
|
_, err = db.LoadPlayer(1)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error loading deleted player")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPlayerCombat tests combat-related functionality
|
||||||
|
func TestPlayerCombat(t *testing.T) {
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(1)
|
||||||
|
player.SetLevel(10)
|
||||||
|
player.SetHP(100)
|
||||||
|
player.SetTotalHP(100)
|
||||||
|
|
||||||
|
// Test combat state
|
||||||
|
player.InCombat(true, false)
|
||||||
|
if player.GetPlayerEngageCommands() == 0 {
|
||||||
|
t.Error("Expected player to be in combat")
|
||||||
|
}
|
||||||
|
|
||||||
|
player.InCombat(false, false)
|
||||||
|
if player.GetPlayerEngageCommands() != 0 {
|
||||||
|
t.Error("Expected player to not be in combat")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test death state
|
||||||
|
player.SetHP(0)
|
||||||
|
if !player.IsDead() {
|
||||||
|
t.Error("Expected player to be dead with 0 HP")
|
||||||
|
}
|
||||||
|
|
||||||
|
player.SetHP(50)
|
||||||
|
if player.IsDead() {
|
||||||
|
t.Error("Expected player to be alive with 50 HP")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test mentorship
|
||||||
|
player.SetMentorStats(5, 0, true)
|
||||||
|
player.EnableResetMentorship()
|
||||||
|
if !player.ResetMentorship() {
|
||||||
|
t.Error("Expected mentorship reset to succeed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPlayerExperience tests experience system
|
||||||
|
func TestPlayerExperience(t *testing.T) {
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(1)
|
||||||
|
player.SetLevel(1)
|
||||||
|
|
||||||
|
// Set initial XP
|
||||||
|
player.SetXP(0)
|
||||||
|
player.SetNeededXP(1000)
|
||||||
|
|
||||||
|
// Test XP gain
|
||||||
|
leveledUp := player.AddXP(500)
|
||||||
|
if leveledUp {
|
||||||
|
t.Error("Should not level up with 500/1000 XP")
|
||||||
|
}
|
||||||
|
|
||||||
|
currentXP := player.GetXP()
|
||||||
|
if currentXP != 500 {
|
||||||
|
t.Errorf("Expected XP 500, got %d", currentXP)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test level up
|
||||||
|
leveledUp = player.AddXP(600) // Total: 1100, should level up
|
||||||
|
if !leveledUp {
|
||||||
|
t.Error("Should level up with 1100/1000 XP")
|
||||||
|
}
|
||||||
|
|
||||||
|
newLevel := player.GetLevel()
|
||||||
|
if newLevel != 2 {
|
||||||
|
t.Errorf("Expected level 2 after level up, got %d", newLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test tradeskill XP
|
||||||
|
player.SetTSXP(0)
|
||||||
|
player.SetNeededTSXP(500)
|
||||||
|
|
||||||
|
leveledUp = player.AddTSXP(600)
|
||||||
|
if !leveledUp {
|
||||||
|
t.Error("Should level up tradeskill with 600/500 XP")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPlayerQuests tests quest management
|
||||||
|
func TestPlayerQuests(t *testing.T) {
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(1)
|
||||||
|
|
||||||
|
// Create test quest
|
||||||
|
quest := &quests.Quest{
|
||||||
|
ID: 100,
|
||||||
|
Name: "Test Quest",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test adding quest
|
||||||
|
// Note: AddQuest method doesn't exist yet - would need implementation
|
||||||
|
// player.AddQuest(quest)
|
||||||
|
player.playerQuests[100] = quest
|
||||||
|
|
||||||
|
retrieved := player.GetQuest(100)
|
||||||
|
if retrieved == nil {
|
||||||
|
t.Error("Failed to retrieve added quest")
|
||||||
|
}
|
||||||
|
|
||||||
|
allQuests := player.GetPlayerQuests()
|
||||||
|
if len(allQuests) != 1 {
|
||||||
|
t.Errorf("Expected 1 quest, got %d", len(allQuests))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test completing quest
|
||||||
|
player.RemoveQuest(100, true)
|
||||||
|
|
||||||
|
retrieved = player.GetQuest(100)
|
||||||
|
if retrieved != nil {
|
||||||
|
t.Error("Quest should be removed after completion")
|
||||||
|
}
|
||||||
|
|
||||||
|
completed := player.GetCompletedQuest(100)
|
||||||
|
if completed == nil {
|
||||||
|
t.Error("Quest should be in completed list")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPlayerSkills tests skill management
|
||||||
|
func TestPlayerSkills(t *testing.T) {
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(1)
|
||||||
|
|
||||||
|
// Test adding skill
|
||||||
|
player.AddSkill(1, 10, 100, true)
|
||||||
|
|
||||||
|
// Test skill retrieval by name
|
||||||
|
skill := player.GetSkillByName("TestSkill", false)
|
||||||
|
// Note: This will return nil since we're using stubs
|
||||||
|
_ = skill
|
||||||
|
|
||||||
|
// Test removing skill
|
||||||
|
player.RemovePlayerSkill(1, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPlayerFlags tests character flag management
|
||||||
|
func TestPlayerFlags(t *testing.T) {
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(1)
|
||||||
|
|
||||||
|
// Test setting flags
|
||||||
|
player.SetCharacterFlag(CF_ANONYMOUS)
|
||||||
|
if !player.GetCharacterFlag(CF_ANONYMOUS) {
|
||||||
|
t.Error("Expected anonymous flag to be set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test resetting flags
|
||||||
|
player.ResetCharacterFlag(CF_ANONYMOUS)
|
||||||
|
if player.GetCharacterFlag(CF_ANONYMOUS) {
|
||||||
|
t.Error("Expected anonymous flag to be reset")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test toggle
|
||||||
|
player.ToggleCharacterFlag(CF_ANONYMOUS)
|
||||||
|
if !player.GetCharacterFlag(CF_ANONYMOUS) {
|
||||||
|
t.Error("Expected anonymous flag to be toggled on")
|
||||||
|
}
|
||||||
|
|
||||||
|
player.ToggleCharacterFlag(CF_ANONYMOUS)
|
||||||
|
if player.GetCharacterFlag(CF_ANONYMOUS) {
|
||||||
|
t.Error("Expected anonymous flag to be toggled off")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPlayerFriends tests friend list management
|
||||||
|
func TestPlayerFriends(t *testing.T) {
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(1)
|
||||||
|
|
||||||
|
// Test adding friend
|
||||||
|
player.AddFriend("BestFriend", false)
|
||||||
|
|
||||||
|
if !player.IsFriend("BestFriend") {
|
||||||
|
t.Error("Expected BestFriend to be in friend list")
|
||||||
|
}
|
||||||
|
|
||||||
|
friends := player.GetFriends()
|
||||||
|
if len(friends) != 1 {
|
||||||
|
t.Errorf("Expected 1 friend, got %d", len(friends))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test removing friend
|
||||||
|
player.RemoveFriend("BestFriend")
|
||||||
|
|
||||||
|
if player.IsFriend("BestFriend") {
|
||||||
|
t.Error("Expected BestFriend to be removed from friend list")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPlayerIgnore tests ignore list management
|
||||||
|
func TestPlayerIgnore(t *testing.T) {
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(1)
|
||||||
|
|
||||||
|
// Test adding to ignore list
|
||||||
|
player.AddIgnore("Annoying", false)
|
||||||
|
|
||||||
|
if !player.IsIgnored("Annoying") {
|
||||||
|
t.Error("Expected Annoying to be in ignore list")
|
||||||
|
}
|
||||||
|
|
||||||
|
ignored := player.GetIgnoredPlayers()
|
||||||
|
if len(ignored) != 1 {
|
||||||
|
t.Errorf("Expected 1 ignored player, got %d", len(ignored))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test removing from ignore list
|
||||||
|
player.RemoveIgnore("Annoying")
|
||||||
|
|
||||||
|
if player.IsIgnored("Annoying") {
|
||||||
|
t.Error("Expected Annoying to be removed from ignore list")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPlayerMovement tests movement-related methods
|
||||||
|
func TestPlayerMovement(t *testing.T) {
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(1)
|
||||||
|
|
||||||
|
// Test position
|
||||||
|
player.SetX(100.5)
|
||||||
|
player.SetY(200.5, false)
|
||||||
|
player.SetZ(300.5)
|
||||||
|
|
||||||
|
if player.GetX() != 100.5 {
|
||||||
|
t.Errorf("Expected X 100.5, got %f", player.GetX())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test heading
|
||||||
|
player.SetHeadingFromFloat(180.0)
|
||||||
|
heading := player.GetHeading()
|
||||||
|
_ = heading // Heading conversion is complex, just ensure it doesn't panic
|
||||||
|
|
||||||
|
// Test distance calculation
|
||||||
|
distance := player.GetDistance(150.5, 250.5, 350.5, true)
|
||||||
|
if distance <= 0 {
|
||||||
|
t.Error("Expected positive distance")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test movement speeds
|
||||||
|
player.SetSideSpeed(5.0, false)
|
||||||
|
if player.GetSideSpeed() != 5.0 {
|
||||||
|
t.Errorf("Expected side speed 5.0, got %f", player.GetSideSpeed())
|
||||||
|
}
|
||||||
|
|
||||||
|
player.SetVertSpeed(3.0, false)
|
||||||
|
if player.GetVertSpeed() != 3.0 {
|
||||||
|
t.Errorf("Expected vert speed 3.0, got %f", player.GetVertSpeed())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPlayerCurrency tests currency management
|
||||||
|
func TestPlayerCurrency(t *testing.T) {
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(1)
|
||||||
|
|
||||||
|
// Test adding coins
|
||||||
|
player.AddCoins(1000)
|
||||||
|
|
||||||
|
if !player.HasCoins(1000) {
|
||||||
|
t.Error("Expected player to have 1000 coins")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test removing coins
|
||||||
|
success := player.RemoveCoins(500)
|
||||||
|
if !success {
|
||||||
|
t.Error("Expected to successfully remove 500 coins")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !player.HasCoins(500) {
|
||||||
|
t.Error("Expected player to have 500 coins remaining")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test insufficient coins
|
||||||
|
success = player.RemoveCoins(1000)
|
||||||
|
if success {
|
||||||
|
t.Error("Should not be able to remove 1000 coins when only 500 available")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPlayerSpells tests spell management
|
||||||
|
func TestPlayerSpells(t *testing.T) {
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(1)
|
||||||
|
|
||||||
|
// Test spell book
|
||||||
|
player.AddSpellBookEntry(100, 1, 1, 0, 0, true)
|
||||||
|
|
||||||
|
hasSpell := player.HasSpell(100, 1, false, false)
|
||||||
|
if !hasSpell {
|
||||||
|
t.Error("Expected player to have spell 100")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test removing spell
|
||||||
|
player.RemoveSpellBookEntry(100, false)
|
||||||
|
|
||||||
|
hasSpell = player.HasSpell(100, 1, false, false)
|
||||||
|
if hasSpell {
|
||||||
|
t.Error("Expected spell to be removed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test passive spells
|
||||||
|
player.ApplyPassiveSpells()
|
||||||
|
player.RemoveAllPassives()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPlayerInfo tests PlayerInfo functionality
|
||||||
|
func TestPlayerInfo(t *testing.T) {
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(1)
|
||||||
|
|
||||||
|
info := NewPlayerInfo(player)
|
||||||
|
if info == nil {
|
||||||
|
t.Fatal("NewPlayerInfo returned nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test bind point
|
||||||
|
info.SetBindZone(100)
|
||||||
|
info.SetBindX(50.0)
|
||||||
|
info.SetBindY(60.0)
|
||||||
|
info.SetBindZ(70.0)
|
||||||
|
info.SetBindHeading(90.0)
|
||||||
|
|
||||||
|
// Test house zone
|
||||||
|
info.SetHouseZone(200)
|
||||||
|
|
||||||
|
// Test account age
|
||||||
|
info.SetAccountAge(365)
|
||||||
|
|
||||||
|
// Test XP calculations
|
||||||
|
player.SetXP(500)
|
||||||
|
player.SetNeededXP(1000)
|
||||||
|
info.CalculateXPPercentages()
|
||||||
|
|
||||||
|
// Test TS XP calculations
|
||||||
|
player.SetTSXP(250)
|
||||||
|
player.SetNeededTSXP(500)
|
||||||
|
info.CalculateTSXPPercentages()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPlayerEquipment tests equipment and appearance
|
||||||
|
func TestPlayerEquipment(t *testing.T) {
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(1)
|
||||||
|
player.SetHP(100) // Set HP so player is not dead
|
||||||
|
player.SetTotalHP(100)
|
||||||
|
|
||||||
|
// Test equipment allowance check
|
||||||
|
canEquip := player.IsAllowedCombatEquip(0, false)
|
||||||
|
if !canEquip {
|
||||||
|
t.Error("Expected to be able to change equipment out of combat")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test in combat equipment change
|
||||||
|
player.InCombat(true, false)
|
||||||
|
canEquip = player.IsAllowedCombatEquip(0, false)
|
||||||
|
if canEquip {
|
||||||
|
t.Error("Should not be able to change primary weapon in combat")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestPlayerCleanup tests cleanup methods
|
||||||
|
func TestPlayerCleanup(t *testing.T) {
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(1)
|
||||||
|
|
||||||
|
// Add some data
|
||||||
|
player.AddFriend("Friend1", false)
|
||||||
|
player.AddIgnore("Ignored1", false)
|
||||||
|
quest := &quests.Quest{ID: 1, Name: "Quest1"}
|
||||||
|
// player.AddQuest(quest) - method doesn't exist yet
|
||||||
|
player.playerQuests[1] = quest
|
||||||
|
|
||||||
|
// Test cleanup
|
||||||
|
player.ClearEverything()
|
||||||
|
|
||||||
|
// Verify data is cleared
|
||||||
|
friends := player.GetFriends()
|
||||||
|
if len(friends) != 0 {
|
||||||
|
t.Error("Expected friends list to be cleared")
|
||||||
|
}
|
||||||
|
|
||||||
|
ignored := player.GetIgnoredPlayers()
|
||||||
|
if len(ignored) != 0 {
|
||||||
|
t.Error("Expected ignore list to be cleared")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkPlayerCreation benchmarks player creation
|
||||||
|
func BenchmarkPlayerCreation(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
p := NewPlayer()
|
||||||
|
p.SetCharacterID(int32(i))
|
||||||
|
p.SetName(fmt.Sprintf("Player%d", i))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkManagerOperations benchmarks manager operations
|
||||||
|
func BenchmarkManagerOperations(b *testing.B) {
|
||||||
|
config := ManagerConfig{
|
||||||
|
MaxPlayers: 1000,
|
||||||
|
}
|
||||||
|
manager := NewManager(config)
|
||||||
|
|
||||||
|
// Create players
|
||||||
|
players := make([]*Player, 100)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
players[i] = NewPlayer()
|
||||||
|
players[i].SetCharacterID(int32(i))
|
||||||
|
players[i].SetName(fmt.Sprintf("Player%d", i))
|
||||||
|
players[i].SetSpawnID(int32(2000 + i)) // Unique spawn IDs
|
||||||
|
manager.AddPlayer(players[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
// Benchmark lookups
|
||||||
|
_ = manager.GetPlayer(int32(i % 100))
|
||||||
|
_ = manager.GetPlayerByCharacterID(int32(i % 100))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestConcurrentAccess tests thread safety
|
||||||
|
func TestConcurrentAccess(t *testing.T) {
|
||||||
|
config := ManagerConfig{
|
||||||
|
MaxPlayers: 100,
|
||||||
|
}
|
||||||
|
manager := NewManager(config)
|
||||||
|
|
||||||
|
// Start manager
|
||||||
|
err := manager.Start()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to start manager: %v", err)
|
||||||
|
}
|
||||||
|
defer manager.Stop()
|
||||||
|
|
||||||
|
// Concurrent player additions
|
||||||
|
done := make(chan bool, 10)
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
go func(id int) {
|
||||||
|
player := NewPlayer()
|
||||||
|
player.SetCharacterID(int32(id))
|
||||||
|
player.SetName(fmt.Sprintf("Player%d", id))
|
||||||
|
player.SetSpawnID(int32(1000 + id)) // Unique spawn IDs
|
||||||
|
manager.AddPlayer(player)
|
||||||
|
done <- true
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all goroutines
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
<-done
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify all players added
|
||||||
|
count := manager.GetPlayerCount()
|
||||||
|
if count != 10 {
|
||||||
|
t.Errorf("Expected 10 players, got %d", count)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
package player
|
package player
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"eq2emu/internal/entity"
|
|
||||||
"eq2emu/internal/quests"
|
"eq2emu/internal/quests"
|
||||||
|
"eq2emu/internal/spawn"
|
||||||
"eq2emu/internal/spells"
|
"eq2emu/internal/spells"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ func (p *Player) HasQuestBeenCompleted(questID int32) bool {
|
|||||||
func (p *Player) GetQuestCompletedCount(questID int32) int32 {
|
func (p *Player) GetQuestCompletedCount(questID int32) int32 {
|
||||||
quest := p.GetCompletedQuest(questID)
|
quest := p.GetCompletedQuest(questID)
|
||||||
if quest != nil {
|
if quest != nil {
|
||||||
return quest.GetCompleteCount()
|
return GetQuestCompleteCount(quest)
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ func (p *Player) AddCompletedQuest(quest *quests.Quest) {
|
|||||||
p.playerQuestsMutex.Lock()
|
p.playerQuestsMutex.Lock()
|
||||||
defer p.playerQuestsMutex.Unlock()
|
defer p.playerQuestsMutex.Unlock()
|
||||||
|
|
||||||
p.completedQuests[quest.GetQuestID()] = quest
|
p.completedQuests[GetQuestID(quest)] = quest
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasActiveQuest checks if a quest is currently active
|
// HasActiveQuest checks if a quest is currently active
|
||||||
@ -114,16 +114,19 @@ func (p *Player) GetQuestIDs() []int32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemoveQuest removes a quest from the player
|
// RemoveQuest removes a quest from the player
|
||||||
func (p *Player) RemoveQuest(questID int32, deleteQuest bool) {
|
// If completeQuest is true, the quest is moved to completed list
|
||||||
|
func (p *Player) RemoveQuest(questID int32, completeQuest bool) {
|
||||||
p.playerQuestsMutex.Lock()
|
p.playerQuestsMutex.Lock()
|
||||||
defer p.playerQuestsMutex.Unlock()
|
defer p.playerQuestsMutex.Unlock()
|
||||||
|
|
||||||
if quest, exists := p.playerQuests[questID]; exists {
|
if quest, exists := p.playerQuests[questID]; exists {
|
||||||
delete(p.playerQuests, questID)
|
delete(p.playerQuests, questID)
|
||||||
|
|
||||||
if deleteQuest {
|
if completeQuest {
|
||||||
// TODO: Delete quest data
|
// Move quest to completed list
|
||||||
_ = quest
|
p.completedQuests[questID] = quest
|
||||||
|
// Update completion count
|
||||||
|
IncrementQuestCompleteCount(quest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +135,7 @@ func (p *Player) RemoveQuest(questID int32, deleteQuest bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddQuestRequiredSpawn adds a spawn requirement for a quest
|
// AddQuestRequiredSpawn adds a spawn requirement for a quest
|
||||||
func (p *Player) AddQuestRequiredSpawn(spawn *entity.Spawn, questID int32) {
|
func (p *Player) AddQuestRequiredSpawn(spawn *spawn.Spawn, questID int32) {
|
||||||
if spawn == nil {
|
if spawn == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -156,7 +159,7 @@ func (p *Player) AddQuestRequiredSpawn(spawn *entity.Spawn, questID int32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddHistoryRequiredSpawn adds a spawn requirement for history
|
// AddHistoryRequiredSpawn adds a spawn requirement for history
|
||||||
func (p *Player) AddHistoryRequiredSpawn(spawn *entity.Spawn, eventID int32) {
|
func (p *Player) AddHistoryRequiredSpawn(spawn *spawn.Spawn, eventID int32) {
|
||||||
if spawn == nil {
|
if spawn == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -180,7 +183,7 @@ func (p *Player) AddHistoryRequiredSpawn(spawn *entity.Spawn, eventID int32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CheckQuestRequired checks if a spawn is required for any quest
|
// CheckQuestRequired checks if a spawn is required for any quest
|
||||||
func (p *Player) CheckQuestRequired(spawn *entity.Spawn) bool {
|
func (p *Player) CheckQuestRequired(spawn *spawn.Spawn) bool {
|
||||||
if spawn == nil {
|
if spawn == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -206,7 +209,7 @@ func (p *Player) GetQuestStepComplete(questID, stepID int32) bool {
|
|||||||
func (p *Player) GetQuestStep(questID int32) int16 {
|
func (p *Player) GetQuestStep(questID int32) int16 {
|
||||||
quest := p.GetQuest(questID)
|
quest := p.GetQuest(questID)
|
||||||
if quest != nil {
|
if quest != nil {
|
||||||
return quest.GetQuestStep()
|
return GetQuestStep(quest)
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -215,7 +218,7 @@ func (p *Player) GetQuestStep(questID int32) int16 {
|
|||||||
func (p *Player) GetTaskGroupStep(questID int32) int16 {
|
func (p *Player) GetTaskGroupStep(questID int32) int16 {
|
||||||
quest := p.GetQuest(questID)
|
quest := p.GetQuest(questID)
|
||||||
if quest != nil {
|
if quest != nil {
|
||||||
return quest.GetTaskGroup()
|
return int16(GetQuestTaskGroup(quest))
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -299,7 +302,7 @@ func (p *Player) SendQuest(questID int32) {
|
|||||||
func (p *Player) UpdateQuestCompleteCount(questID int32) {
|
func (p *Player) UpdateQuestCompleteCount(questID int32) {
|
||||||
quest := p.GetCompletedQuest(questID)
|
quest := p.GetCompletedQuest(questID)
|
||||||
if quest != nil {
|
if quest != nil {
|
||||||
quest.IncrementCompleteCount()
|
IncrementQuestCompleteCount(quest)
|
||||||
// TODO: Save to database
|
// TODO: Save to database
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -333,13 +336,13 @@ func (p *Player) AddQuestTemporaryReward(questID, itemID int32, itemCount int16)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// UpdateQuestReward updates quest reward data
|
// UpdateQuestReward updates quest reward data
|
||||||
func (p *Player) UpdateQuestReward(questID int32, qrd *quests.QuestRewardData) bool {
|
func (p *Player) UpdateQuestReward(questID int32, qrd *quests.QuestRewards) bool {
|
||||||
// TODO: Update quest reward
|
// TODO: Update quest reward
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckQuestsChatUpdate checks quests for chat updates
|
// CheckQuestsChatUpdate checks quests for chat updates
|
||||||
func (p *Player) CheckQuestsChatUpdate(spawn *entity.Spawn) []*quests.Quest {
|
func (p *Player) CheckQuestsChatUpdate(spawn *spawn.Spawn) []*quests.Quest {
|
||||||
// TODO: Check if spawn chat updates any quests
|
// TODO: Check if spawn chat updates any quests
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -357,13 +360,13 @@ func (p *Player) CheckQuestsLocationUpdate() []*quests.Quest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CheckQuestsKillUpdate checks quests for kill updates
|
// CheckQuestsKillUpdate checks quests for kill updates
|
||||||
func (p *Player) CheckQuestsKillUpdate(spawn *entity.Spawn, update bool) []*quests.Quest {
|
func (p *Player) CheckQuestsKillUpdate(spawn *spawn.Spawn, update bool) []*quests.Quest {
|
||||||
// TODO: Check if killing spawn updates any quests
|
// TODO: Check if killing spawn updates any quests
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasQuestUpdateRequirement checks if spawn has quest update requirements
|
// HasQuestUpdateRequirement checks if spawn has quest update requirements
|
||||||
func (p *Player) HasQuestUpdateRequirement(spawn *entity.Spawn) bool {
|
func (p *Player) HasQuestUpdateRequirement(spawn *spawn.Spawn) bool {
|
||||||
// TODO: Check if spawn updates any active quests
|
// TODO: Check if spawn updates any active quests
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -391,13 +394,13 @@ func (p *Player) CheckQuestsFailures() []*quests.Quest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CheckQuestRemoveFlag checks if spawn should have quest flag removed
|
// CheckQuestRemoveFlag checks if spawn should have quest flag removed
|
||||||
func (p *Player) CheckQuestRemoveFlag(spawn *entity.Spawn) bool {
|
func (p *Player) CheckQuestRemoveFlag(spawn *spawn.Spawn) bool {
|
||||||
// TODO: Check if quest flag should be removed from spawn
|
// TODO: Check if quest flag should be removed from spawn
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckQuestFlag returns the quest flag for a spawn
|
// CheckQuestFlag returns the quest flag for a spawn
|
||||||
func (p *Player) CheckQuestFlag(spawn *entity.Spawn) int8 {
|
func (p *Player) CheckQuestFlag(spawn *spawn.Spawn) int8 {
|
||||||
// TODO: Determine quest flag for spawn
|
// TODO: Determine quest flag for spawn
|
||||||
// 0 = no flag
|
// 0 = no flag
|
||||||
// 1 = quest giver
|
// 1 = quest giver
|
||||||
|
@ -6,25 +6,27 @@ import (
|
|||||||
|
|
||||||
// GetSkillByName returns a skill by name
|
// GetSkillByName returns a skill by name
|
||||||
func (p *Player) GetSkillByName(name string, checkUpdate bool) *skills.Skill {
|
func (p *Player) GetSkillByName(name string, checkUpdate bool) *skills.Skill {
|
||||||
return p.skillList.GetSkillByName(name, checkUpdate)
|
return p.GetSkillByNameHelper(name, checkUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSkillByID returns a skill by ID
|
// GetSkillByID returns a skill by ID
|
||||||
func (p *Player) GetSkillByID(skillID int32, checkUpdate bool) *skills.Skill {
|
func (p *Player) GetSkillByID(skillID int32, checkUpdate bool) *skills.Skill {
|
||||||
return p.skillList.GetSkillByID(skillID, checkUpdate)
|
// TODO: Implement GetSkillByID when available in skills package
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSkill adds a skill to the player
|
// AddSkill adds a skill to the player
|
||||||
func (p *Player) AddSkill(skillID int32, currentVal, maxVal int16, saveNeeded bool) {
|
func (p *Player) AddSkill(skillID int32, currentVal, maxVal int16, saveNeeded bool) {
|
||||||
p.skillList.AddSkill(skillID, currentVal, maxVal, saveNeeded)
|
p.AddSkillHelper(skillID, currentVal, maxVal, saveNeeded)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemovePlayerSkill removes a skill from the player
|
// RemovePlayerSkill removes a skill from the player
|
||||||
func (p *Player) RemovePlayerSkill(skillID int32, save bool) {
|
func (p *Player) RemovePlayerSkill(skillID int32, save bool) {
|
||||||
p.skillList.RemoveSkill(skillID)
|
p.RemoveSkillHelper(skillID)
|
||||||
if save {
|
if save {
|
||||||
// TODO: Remove from database
|
// TODO: Remove from database
|
||||||
p.RemoveSkillFromDB(p.skillList.GetSkillByID(skillID, false), save)
|
// TODO: Implement RemoveSkillFromDB when available
|
||||||
|
// p.RemoveSkillFromDB(p.GetSkillByID(skillID, false), save)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ package player
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"eq2emu/internal/entity"
|
"eq2emu/internal/spawn"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WasSentSpawn checks if a spawn was already sent to the player
|
// WasSentSpawn checks if a spawn was already sent to the player
|
||||||
@ -40,7 +40,7 @@ func (p *Player) IsRemovingSpawn(spawnID int32) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetSpawnSentState sets the spawn state for tracking
|
// SetSpawnSentState sets the spawn state for tracking
|
||||||
func (p *Player) SetSpawnSentState(spawn *entity.Spawn, state SpawnState) bool {
|
func (p *Player) SetSpawnSentState(spawn *spawn.Spawn, state SpawnState) bool {
|
||||||
if spawn == nil {
|
if spawn == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ func (p *Player) CheckSpawnStateQueue() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetSpawnWithPlayerID returns a spawn by player-specific ID
|
// GetSpawnWithPlayerID returns a spawn by player-specific ID
|
||||||
func (p *Player) GetSpawnWithPlayerID(id int32) *entity.Spawn {
|
func (p *Player) GetSpawnWithPlayerID(id int32) *spawn.Spawn {
|
||||||
p.indexMutex.RLock()
|
p.indexMutex.RLock()
|
||||||
defer p.indexMutex.RUnlock()
|
defer p.indexMutex.RUnlock()
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ func (p *Player) GetSpawnWithPlayerID(id int32) *entity.Spawn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetIDWithPlayerSpawn returns the player-specific ID for a spawn
|
// GetIDWithPlayerSpawn returns the player-specific ID for a spawn
|
||||||
func (p *Player) GetIDWithPlayerSpawn(spawn *entity.Spawn) int32 {
|
func (p *Player) GetIDWithPlayerSpawn(spawn *spawn.Spawn) int32 {
|
||||||
if spawn == nil {
|
if spawn == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -126,7 +126,7 @@ func (p *Player) GetIDWithPlayerSpawn(spawn *entity.Spawn) int32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetNextSpawnIndex returns the next available spawn index
|
// GetNextSpawnIndex returns the next available spawn index
|
||||||
func (p *Player) GetNextSpawnIndex(spawn *entity.Spawn, setLock bool) int16 {
|
func (p *Player) GetNextSpawnIndex(spawn *spawn.Spawn, setLock bool) int16 {
|
||||||
if setLock {
|
if setLock {
|
||||||
p.indexMutex.Lock()
|
p.indexMutex.Lock()
|
||||||
defer p.indexMutex.Unlock()
|
defer p.indexMutex.Unlock()
|
||||||
@ -152,7 +152,7 @@ func (p *Player) GetNextSpawnIndex(spawn *entity.Spawn, setLock bool) int16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetSpawnMap adds a spawn to the player's spawn map
|
// SetSpawnMap adds a spawn to the player's spawn map
|
||||||
func (p *Player) SetSpawnMap(spawn *entity.Spawn) bool {
|
func (p *Player) SetSpawnMap(spawn *spawn.Spawn) bool {
|
||||||
if spawn == nil {
|
if spawn == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -176,7 +176,7 @@ func (p *Player) SetSpawnMap(spawn *entity.Spawn) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetSpawnMapIndex sets a specific index for a spawn
|
// SetSpawnMapIndex sets a specific index for a spawn
|
||||||
func (p *Player) SetSpawnMapIndex(spawn *entity.Spawn, index int32) {
|
func (p *Player) SetSpawnMapIndex(spawn *spawn.Spawn, index int32) {
|
||||||
p.indexMutex.Lock()
|
p.indexMutex.Lock()
|
||||||
defer p.indexMutex.Unlock()
|
defer p.indexMutex.Unlock()
|
||||||
|
|
||||||
@ -185,7 +185,7 @@ func (p *Player) SetSpawnMapIndex(spawn *entity.Spawn, index int32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SetSpawnMapAndIndex sets spawn in map and returns the index
|
// SetSpawnMapAndIndex sets spawn in map and returns the index
|
||||||
func (p *Player) SetSpawnMapAndIndex(spawn *entity.Spawn) int16 {
|
func (p *Player) SetSpawnMapAndIndex(spawn *spawn.Spawn) int16 {
|
||||||
if spawn == nil {
|
if spawn == nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -209,17 +209,17 @@ func (p *Player) SetSpawnMapAndIndex(spawn *entity.Spawn) int16 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetSpawnByIndex returns a spawn by its player-specific index
|
// GetSpawnByIndex returns a spawn by its player-specific index
|
||||||
func (p *Player) GetSpawnByIndex(index int16) *entity.Spawn {
|
func (p *Player) GetSpawnByIndex(index int16) *spawn.Spawn {
|
||||||
return p.GetSpawnWithPlayerID(int32(index))
|
return p.GetSpawnWithPlayerID(int32(index))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIndexForSpawn returns the player-specific index for a spawn
|
// GetIndexForSpawn returns the player-specific index for a spawn
|
||||||
func (p *Player) GetIndexForSpawn(spawn *entity.Spawn) int16 {
|
func (p *Player) GetIndexForSpawn(spawn *spawn.Spawn) int16 {
|
||||||
return int16(p.GetIDWithPlayerSpawn(spawn))
|
return int16(p.GetIDWithPlayerSpawn(spawn))
|
||||||
}
|
}
|
||||||
|
|
||||||
// WasSpawnRemoved checks if a spawn was removed
|
// WasSpawnRemoved checks if a spawn was removed
|
||||||
func (p *Player) WasSpawnRemoved(spawn *entity.Spawn) bool {
|
func (p *Player) WasSpawnRemoved(spawn *spawn.Spawn) bool {
|
||||||
if spawn == nil {
|
if spawn == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -244,7 +244,7 @@ func (p *Player) ResetSpawnPackets(id int32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemoveSpawn removes a spawn from the player's view
|
// RemoveSpawn removes a spawn from the player's view
|
||||||
func (p *Player) RemoveSpawn(spawn *entity.Spawn, deleteSpawn bool) {
|
func (p *Player) RemoveSpawn(spawn *spawn.Spawn, deleteSpawn bool) {
|
||||||
if spawn == nil {
|
if spawn == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -286,13 +286,13 @@ func (p *Player) RemoveSpawn(spawn *entity.Spawn, deleteSpawn bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ShouldSendSpawn determines if a spawn should be sent to player
|
// ShouldSendSpawn determines if a spawn should be sent to player
|
||||||
func (p *Player) ShouldSendSpawn(spawn *entity.Spawn) bool {
|
func (p *Player) ShouldSendSpawn(spawn *spawn.Spawn) bool {
|
||||||
if spawn == nil {
|
if spawn == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't send self
|
// Don't send self
|
||||||
if spawn == &p.Entity.Spawn {
|
if spawn == p.Entity.Spawn {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -302,7 +302,7 @@ func (p *Player) ShouldSendSpawn(spawn *entity.Spawn) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check distance
|
// Check distance
|
||||||
distance := p.GetDistance(spawn)
|
distance := p.GetDistance(spawn.GetX(), spawn.GetY(), spawn.GetZ(), true)
|
||||||
maxDistance := float32(200.0) // TODO: Get from rule system
|
maxDistance := float32(200.0) // TODO: Get from rule system
|
||||||
|
|
||||||
if distance > maxDistance {
|
if distance > maxDistance {
|
||||||
@ -333,11 +333,11 @@ func (p *Player) ClearRemovalTimers() {
|
|||||||
// ResetSavedSpawns resets all saved spawn data
|
// ResetSavedSpawns resets all saved spawn data
|
||||||
func (p *Player) ResetSavedSpawns() {
|
func (p *Player) ResetSavedSpawns() {
|
||||||
p.indexMutex.Lock()
|
p.indexMutex.Lock()
|
||||||
p.playerSpawnIDMap = make(map[int32]*entity.Spawn)
|
p.playerSpawnIDMap = make(map[int32]*spawn.Spawn)
|
||||||
p.playerSpawnReverseIDMap = make(map[*entity.Spawn]int32)
|
p.playerSpawnReverseIDMap = make(map[*spawn.Spawn]int32)
|
||||||
// Re-add self
|
// Re-add self
|
||||||
p.playerSpawnIDMap[1] = &p.Entity.Spawn
|
p.playerSpawnIDMap[1] = p.Entity.Spawn
|
||||||
p.playerSpawnReverseIDMap[&p.Entity.Spawn] = 1
|
p.playerSpawnReverseIDMap[p.Entity.Spawn] = 1
|
||||||
p.indexMutex.Unlock()
|
p.indexMutex.Unlock()
|
||||||
|
|
||||||
p.spawnMutex.Lock()
|
p.spawnMutex.Lock()
|
||||||
|
@ -88,7 +88,7 @@ func (p *Player) GetSpellBookSpellIDBySkill(skillID int32) []int32 {
|
|||||||
defer p.spellsBookMutex.RUnlock()
|
defer p.spellsBookMutex.RUnlock()
|
||||||
|
|
||||||
var spellIDs []int32
|
var spellIDs []int32
|
||||||
for _, entry := range p.spells {
|
for range p.spells {
|
||||||
// TODO: Check if spell matches skill
|
// TODO: Check if spell matches skill
|
||||||
// spell := master_spell_list.GetSpell(entry.SpellID)
|
// spell := master_spell_list.GetSpell(entry.SpellID)
|
||||||
// if spell != nil && spell.GetSkillID() == skillID {
|
// if spell != nil && spell.GetSkillID() == skillID {
|
||||||
@ -105,7 +105,7 @@ func (p *Player) HasSpell(spellID int32, tier int8, includeHigherTiers bool, inc
|
|||||||
|
|
||||||
for _, entry := range p.spells {
|
for _, entry := range p.spells {
|
||||||
if entry.SpellID == spellID {
|
if entry.SpellID == spellID {
|
||||||
if tier == 255 || entry.Tier == tier {
|
if tier == 127 || entry.Tier == tier { // Changed from 255 to avoid int8 overflow
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if includeHigherTiers && entry.Tier > tier {
|
if includeHigherTiers && entry.Tier > tier {
|
||||||
@ -390,7 +390,7 @@ func (p *Player) UnlockSpell(spell *spells.Spell) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
p.UnlockSpellByID(spell.GetSpellID(), spell.GetSpellData().LinkedTimerID)
|
p.UnlockSpellByID(spell.GetSpellID(), GetSpellLinkedTimerID(spell.GetSpellData()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnlockSpellByID unlocks a spell by ID
|
// UnlockSpellByID unlocks a spell by ID
|
||||||
@ -412,7 +412,7 @@ func (p *Player) LockTSSpells() {
|
|||||||
p.spellsBookMutex.Lock()
|
p.spellsBookMutex.Lock()
|
||||||
defer p.spellsBookMutex.Unlock()
|
defer p.spellsBookMutex.Unlock()
|
||||||
|
|
||||||
for _, entry := range p.spells {
|
for range p.spells {
|
||||||
// TODO: Check if tradeskill spell
|
// TODO: Check if tradeskill spell
|
||||||
// if spell.IsTradeskill() {
|
// if spell.IsTradeskill() {
|
||||||
// entry.Status |= SPELL_STATUS_LOCK
|
// entry.Status |= SPELL_STATUS_LOCK
|
||||||
@ -427,7 +427,7 @@ func (p *Player) UnlockTSSpells() {
|
|||||||
p.spellsBookMutex.Lock()
|
p.spellsBookMutex.Lock()
|
||||||
defer p.spellsBookMutex.Unlock()
|
defer p.spellsBookMutex.Unlock()
|
||||||
|
|
||||||
for _, entry := range p.spells {
|
for range p.spells {
|
||||||
// TODO: Check if tradeskill spell
|
// TODO: Check if tradeskill spell
|
||||||
// if spell.IsTradeskill() {
|
// if spell.IsTradeskill() {
|
||||||
// entry.Status &= ^SPELL_STATUS_LOCK
|
// entry.Status &= ^SPELL_STATUS_LOCK
|
||||||
@ -506,8 +506,8 @@ func (p *Player) RemovePassive(id int32, tier int8, removeFromList bool) {
|
|||||||
// ApplyPassiveSpells applies all passive spells
|
// ApplyPassiveSpells applies all passive spells
|
||||||
func (p *Player) ApplyPassiveSpells() {
|
func (p *Player) ApplyPassiveSpells() {
|
||||||
// TODO: Cast all passive spells
|
// TODO: Cast all passive spells
|
||||||
for _, spellID := range p.passiveSpells {
|
for range p.passiveSpells {
|
||||||
// Get spell and cast it
|
// TODO: Get spell and cast it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
529
internal/player/stubs.go
Normal file
529
internal/player/stubs.go
Normal file
@ -0,0 +1,529 @@
|
|||||||
|
package player
|
||||||
|
|
||||||
|
// This file contains placeholder stub methods to make the player package compile
|
||||||
|
// These methods should be properly implemented when the corresponding systems are ready
|
||||||
|
|
||||||
|
import (
|
||||||
|
"eq2emu/internal/entity"
|
||||||
|
"eq2emu/internal/quests"
|
||||||
|
"eq2emu/internal/skills"
|
||||||
|
"eq2emu/internal/spells"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Player-level flags handling since InfoStruct doesn't have these methods yet
|
||||||
|
var playerFlags = make(map[int32]int32)
|
||||||
|
var playerFlags2 = make(map[int32]int32)
|
||||||
|
|
||||||
|
// GetPlayerFlags returns player flags for a character
|
||||||
|
func (p *Player) GetPlayerFlags() int32 {
|
||||||
|
return playerFlags[p.GetCharacterID()]
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPlayerFlags sets player flags for a character
|
||||||
|
func (p *Player) SetPlayerFlags(flags int32) {
|
||||||
|
playerFlags[p.GetCharacterID()] = flags
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPlayerFlags2 returns player flags2 for a character
|
||||||
|
func (p *Player) GetPlayerFlags2() int32 {
|
||||||
|
return playerFlags2[p.GetCharacterID()]
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPlayerFlags2 sets player flags2 for a character
|
||||||
|
func (p *Player) SetPlayerFlags2(flags int32) {
|
||||||
|
playerFlags2[p.GetCharacterID()] = flags
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player stub methods that may be called by other packages
|
||||||
|
|
||||||
|
// GetPrimaryStat returns the primary stat for the player's class (stub)
|
||||||
|
func (p *Player) GetPrimaryStat() int32 {
|
||||||
|
// TODO: Calculate based on class
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetTarget returns the player's current target (stub)
|
||||||
|
func (p *Player) GetTarget() interface{} {
|
||||||
|
// TODO: Implement targeting system
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsStunned returns whether the player is stunned (stub)
|
||||||
|
func (p *Player) IsStunned() bool {
|
||||||
|
// TODO: Implement status effect system
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsMezzed returns whether the player is mezzed (stub)
|
||||||
|
func (p *Player) IsMezzed() bool {
|
||||||
|
// TODO: Implement status effect system
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player-level combat state handling
|
||||||
|
var playerEngageCommands = make(map[int32]int32)
|
||||||
|
|
||||||
|
// GetPlayerEngageCommands returns combat state for a character
|
||||||
|
func (p *Player) GetPlayerEngageCommands() int32 {
|
||||||
|
return playerEngageCommands[p.GetCharacterID()]
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetPlayerEngageCommands sets combat state for a character
|
||||||
|
func (p *Player) SetPlayerEngageCommands(commands int32) {
|
||||||
|
playerEngageCommands[p.GetCharacterID()] = commands
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsDead returns whether the player is dead (stub)
|
||||||
|
func (p *Player) IsDead() bool {
|
||||||
|
// TODO: Implement death state tracking
|
||||||
|
return p.GetHP() <= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: IsPlayer method is already implemented in player.go, so removed duplicate
|
||||||
|
|
||||||
|
// Entity stub methods for combat target
|
||||||
|
var entityDeathState = make(map[*entity.Entity]bool)
|
||||||
|
|
||||||
|
// IsDead checks if an entity is dead (stub for Entity type)
|
||||||
|
func IsDead(e *entity.Entity) bool {
|
||||||
|
// TODO: Implement proper entity death checking
|
||||||
|
return entityDeathState[e]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Coin management stubs
|
||||||
|
var playerCoins = make(map[int32]int64) // characterID -> coin amount
|
||||||
|
|
||||||
|
// AddCoin adds coins to the player's InfoStruct (stub)
|
||||||
|
func (p *Player) AddCoin(amount int64) {
|
||||||
|
current := playerCoins[p.GetCharacterID()]
|
||||||
|
playerCoins[p.GetCharacterID()] = current + amount
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCoin returns the player's current coin amount (stub)
|
||||||
|
func (p *Player) GetCoin() int64 {
|
||||||
|
return playerCoins[p.GetCharacterID()]
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubtractCoin removes coins from the player (stub)
|
||||||
|
func (p *Player) SubtractCoin(amount int64) bool {
|
||||||
|
current := playerCoins[p.GetCharacterID()]
|
||||||
|
if current >= amount {
|
||||||
|
playerCoins[p.GetCharacterID()] = current - amount
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoStruct coin access stubs - these methods don't exist on InfoStruct yet
|
||||||
|
var playerCoinBreakdown = make(map[int32]map[string]int32) // characterID -> coin type -> amount
|
||||||
|
|
||||||
|
// GetCoinCopper returns copper coins (stub)
|
||||||
|
func (p *Player) GetInfoStructCoinCopper() int32 {
|
||||||
|
if playerCoinBreakdown[p.GetCharacterID()] == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return playerCoinBreakdown[p.GetCharacterID()]["copper"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCoinSilver returns silver coins (stub)
|
||||||
|
func (p *Player) GetInfoStructCoinSilver() int32 {
|
||||||
|
if playerCoinBreakdown[p.GetCharacterID()] == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return playerCoinBreakdown[p.GetCharacterID()]["silver"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCoinGold returns gold coins (stub)
|
||||||
|
func (p *Player) GetInfoStructCoinGold() int32 {
|
||||||
|
if playerCoinBreakdown[p.GetCharacterID()] == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return playerCoinBreakdown[p.GetCharacterID()]["gold"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCoinPlat returns platinum coins (stub)
|
||||||
|
func (p *Player) GetInfoStructCoinPlat() int32 {
|
||||||
|
if playerCoinBreakdown[p.GetCharacterID()] == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return playerCoinBreakdown[p.GetCharacterID()]["plat"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bank coin methods (stubs)
|
||||||
|
func (p *Player) GetInfoStructBankCoinCopper() int32 {
|
||||||
|
if playerCoinBreakdown[p.GetCharacterID()] == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return playerCoinBreakdown[p.GetCharacterID()]["bank_copper"]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) GetInfoStructBankCoinSilver() int32 {
|
||||||
|
if playerCoinBreakdown[p.GetCharacterID()] == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return playerCoinBreakdown[p.GetCharacterID()]["bank_silver"]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) GetInfoStructBankCoinGold() int32 {
|
||||||
|
if playerCoinBreakdown[p.GetCharacterID()] == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return playerCoinBreakdown[p.GetCharacterID()]["bank_gold"]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) GetInfoStructBankCoinPlat() int32 {
|
||||||
|
if playerCoinBreakdown[p.GetCharacterID()] == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return playerCoinBreakdown[p.GetCharacterID()]["bank_plat"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStatusPoints returns status points (stub)
|
||||||
|
func (p *Player) GetInfoStructStatusPoints() int32 {
|
||||||
|
if playerCoinBreakdown[p.GetCharacterID()] == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return playerCoinBreakdown[p.GetCharacterID()]["status"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player methods that don't exist on Entity/Spawn yet (stubs)
|
||||||
|
func (p *Player) SetRace(race int8) {
|
||||||
|
// TODO: Implement race setting on entity/spawn
|
||||||
|
// For now, store in a map
|
||||||
|
if playerCoinBreakdown[p.GetCharacterID()] == nil {
|
||||||
|
playerCoinBreakdown[p.GetCharacterID()] = make(map[string]int32)
|
||||||
|
}
|
||||||
|
playerCoinBreakdown[p.GetCharacterID()]["race"] = int32(race)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) GetRace() int8 {
|
||||||
|
// TODO: Implement race getting from entity/spawn
|
||||||
|
if playerCoinBreakdown[p.GetCharacterID()] == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return int8(playerCoinBreakdown[p.GetCharacterID()]["race"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetZone(zoneID int32) {
|
||||||
|
// TODO: Implement zone setting on entity/spawn
|
||||||
|
if playerCoinBreakdown[p.GetCharacterID()] == nil {
|
||||||
|
playerCoinBreakdown[p.GetCharacterID()] = make(map[string]int32)
|
||||||
|
}
|
||||||
|
playerCoinBreakdown[p.GetCharacterID()]["zone"] = zoneID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) GetZone() int32 {
|
||||||
|
// TODO: Implement zone getting from entity/spawn
|
||||||
|
if playerCoinBreakdown[p.GetCharacterID()] == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return playerCoinBreakdown[p.GetCharacterID()]["zone"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Experience vitality methods (InfoStruct stubs)
|
||||||
|
func (p *Player) GetInfoStructXPVitality() float32 {
|
||||||
|
// TODO: Implement XP vitality tracking
|
||||||
|
return 100.0 // Default vitality
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) GetInfoStructTSXPVitality() float32 {
|
||||||
|
// TODO: Implement tradeskill XP vitality tracking
|
||||||
|
return 100.0 // Default vitality
|
||||||
|
}
|
||||||
|
|
||||||
|
// More InfoStruct experience method stubs
|
||||||
|
var playerXPData = make(map[int32]map[string]float64) // characterID -> xp type -> value
|
||||||
|
|
||||||
|
func (p *Player) GetInfoStructXPDebt() float32 {
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerXPData[charID] == nil {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
return float32(playerXPData[charID]["xp_debt"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) GetInfoStructTSXPDebt() float32 {
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerXPData[charID] == nil {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
return float32(playerXPData[charID]["ts_xp_debt"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructXPNeeded(xp float64) {
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerXPData[charID] == nil {
|
||||||
|
playerXPData[charID] = make(map[string]float64)
|
||||||
|
}
|
||||||
|
playerXPData[charID]["xp_needed"] = xp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructXP(xp float64) {
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerXPData[charID] == nil {
|
||||||
|
playerXPData[charID] = make(map[string]float64)
|
||||||
|
}
|
||||||
|
playerXPData[charID]["xp"] = xp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructTSXPNeeded(xp float64) {
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerXPData[charID] == nil {
|
||||||
|
playerXPData[charID] = make(map[string]float64)
|
||||||
|
}
|
||||||
|
playerXPData[charID]["ts_xp_needed"] = xp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructTSXP(xp float64) {
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerXPData[charID] == nil {
|
||||||
|
playerXPData[charID] = make(map[string]float64)
|
||||||
|
}
|
||||||
|
playerXPData[charID]["ts_xp"] = xp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) GetInfoStructXPNeeded() float64 {
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerXPData[charID] == nil {
|
||||||
|
return 1000.0 // Default XP needed
|
||||||
|
}
|
||||||
|
return playerXPData[charID]["xp_needed"]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) GetInfoStructXP() float64 {
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerXPData[charID] == nil {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
return playerXPData[charID]["xp"]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) GetInfoStructTSXPNeeded() float64 {
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerXPData[charID] == nil {
|
||||||
|
return 1000.0 // Default TS XP needed
|
||||||
|
}
|
||||||
|
return playerXPData[charID]["ts_xp_needed"]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) GetInfoStructTSXP() float64 {
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerXPData[charID] == nil {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
return playerXPData[charID]["ts_xp"]
|
||||||
|
}
|
||||||
|
|
||||||
|
// XP Debt methods
|
||||||
|
func (p *Player) SetInfoStructXPDebt(debt float32) {
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerXPData[charID] == nil {
|
||||||
|
playerXPData[charID] = make(map[string]float64)
|
||||||
|
}
|
||||||
|
playerXPData[charID]["xp_debt"] = float64(debt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructTSXPDebt(debt float32) {
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerXPData[charID] == nil {
|
||||||
|
playerXPData[charID] = make(map[string]float64)
|
||||||
|
}
|
||||||
|
playerXPData[charID]["ts_xp_debt"] = float64(debt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TS Level methods
|
||||||
|
func (p *Player) GetInfoStructTSLevel() int8 {
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerXPData[charID] == nil {
|
||||||
|
return 1 // Default level
|
||||||
|
}
|
||||||
|
return int8(playerXPData[charID]["ts_level"])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructTSLevel(level int8) {
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerXPData[charID] == nil {
|
||||||
|
playerXPData[charID] = make(map[string]float64)
|
||||||
|
}
|
||||||
|
playerXPData[charID]["ts_level"] = float64(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Player wrapper methods for TS level
|
||||||
|
func (p *Player) GetTSLevel() int8 {
|
||||||
|
return p.GetInfoStructTSLevel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetTSLevel(level int8) {
|
||||||
|
p.SetInfoStructTSLevel(level)
|
||||||
|
p.SetCharSheetChanged(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSpawnID returns the spawn ID (in EverQuest II, players use same ID as spawn)
|
||||||
|
func (p *Player) GetSpawnID() int32 {
|
||||||
|
// Use the player's spawnID field
|
||||||
|
return p.spawnID
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSpawnID sets the spawn ID
|
||||||
|
func (p *Player) SetSpawnID(id int32) {
|
||||||
|
p.spawnID = id
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddSecondaryEntityCommand adds a secondary command (stub)
|
||||||
|
func (p *Player) AddSecondaryEntityCommand(name string, distance float32, command, errorText string, castTime int16, spellID int32) {
|
||||||
|
// TODO: Implement secondary entity commands
|
||||||
|
}
|
||||||
|
|
||||||
|
// Position and movement data storage (stubs for appearance system)
|
||||||
|
var playerMovementData = make(map[int32]map[string]float32) // characterID -> movement type -> value
|
||||||
|
|
||||||
|
// SetPos sets positional data (stub)
|
||||||
|
func (p *Player) SetPos(ptr *float32, value float32, updateFlags bool) {
|
||||||
|
// Since we can't access the appearance system, just store the value
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerMovementData[charID] == nil {
|
||||||
|
playerMovementData[charID] = make(map[string]float32)
|
||||||
|
}
|
||||||
|
*ptr = value // Set the pointer value if it's valid
|
||||||
|
// TODO: Handle updateFlags when packet system is available
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPos gets positional data (stub)
|
||||||
|
func (p *Player) GetPos(key string) float32 {
|
||||||
|
charID := p.GetCharacterID()
|
||||||
|
if playerMovementData[charID] == nil {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
|
return playerMovementData[charID][key]
|
||||||
|
}
|
||||||
|
|
||||||
|
// XP bar display methods (InfoStruct stubs)
|
||||||
|
func (p *Player) SetInfoStructXPYellow(value int16) {
|
||||||
|
// TODO: Implement XP bar display
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructXPBlue(value int16) {
|
||||||
|
// TODO: Implement XP bar display
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructXPBlueVitalityBar(value int16) {
|
||||||
|
// TODO: Implement XP bar display
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructXPYellowVitalityBar(value int16) {
|
||||||
|
// TODO: Implement XP bar display
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructTSXPYellow(value int16) {
|
||||||
|
// TODO: Implement TS XP bar display
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructTSXPBlue(value int16) {
|
||||||
|
// TODO: Implement TS XP bar display
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructTSXPBlueVitalityBar(value int16) {
|
||||||
|
// TODO: Implement TS XP bar display
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructTSXPYellowVitalityBar(value int16) {
|
||||||
|
// TODO: Implement TS XP bar display
|
||||||
|
}
|
||||||
|
|
||||||
|
// XP bar getter methods (InfoStruct stubs)
|
||||||
|
func (p *Player) GetInfoStructXPBlue() int16 {
|
||||||
|
// TODO: Implement XP bar display
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) GetInfoStructXPYellow() int16 {
|
||||||
|
// TODO: Implement XP bar display
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tradeskill XP bar methods
|
||||||
|
func (p *Player) SetInfoStructTradeskillExpYellow(value int16) {
|
||||||
|
// TODO: Implement TS XP bar display
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructTradeskillExpBlue(value int16) {
|
||||||
|
// TODO: Implement TS XP bar display
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructTSExpVitalityBlue(value int16) {
|
||||||
|
// TODO: Implement TS XP vitality bar display
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructTSExpVitalityYellow(value int16) {
|
||||||
|
// TODO: Implement TS XP vitality bar display
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) GetInfoStructTradeskillExpBlue() int16 {
|
||||||
|
// TODO: Implement TS XP bar display
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) GetInfoStructTradeskillExpYellow() int16 {
|
||||||
|
// TODO: Implement TS XP bar display
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Account age methods (InfoStruct stubs)
|
||||||
|
func (p *Player) SetInfoStructAccountAgeBase(value int32) {
|
||||||
|
// TODO: Implement account age tracking
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) SetInfoStructAccountAge(value int32) {
|
||||||
|
// TODO: Implement account age tracking
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quest helper functions - working around missing quest methods
|
||||||
|
// These should ideally be proper methods on the Quest type in the quests package
|
||||||
|
|
||||||
|
func GetQuestCompleteCount(q *quests.Quest) int32 {
|
||||||
|
// TODO: Implement quest completion tracking
|
||||||
|
// For now return 0 as a placeholder
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetQuestID(q *quests.Quest) int32 {
|
||||||
|
// TODO: This should access q.ID field when available
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetQuestStep(q *quests.Quest) int16 {
|
||||||
|
// TODO: Implement quest step retrieval
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetQuestTaskGroup(q *quests.Quest) int8 {
|
||||||
|
// TODO: Implement quest task group system
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func IncrementQuestCompleteCount(q *quests.Quest) {
|
||||||
|
// TODO: Implement quest completion tracking
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlayerSkillList helper methods to work around method signature differences
|
||||||
|
func (p *Player) GetSkillByNameHelper(name string, checkUpdate bool) *skills.Skill {
|
||||||
|
// TODO: Work around method signature mismatch
|
||||||
|
// The actual method only takes name as parameter
|
||||||
|
return p.skillList.GetSkillByName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) AddSkillHelper(skillID int32, currentVal, maxVal int16, saveNeeded bool) {
|
||||||
|
// TODO: Create proper Skill object and add it
|
||||||
|
// For now, this is a placeholder stub
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Player) RemoveSkillHelper(skillID int32) {
|
||||||
|
// TODO: Remove skill by ID
|
||||||
|
// For now, this is a placeholder stub
|
||||||
|
}
|
||||||
|
|
||||||
|
// SpellData method stubs
|
||||||
|
func GetSpellLinkedTimerID(spellData *spells.SpellData) int32 {
|
||||||
|
// TODO: Implement LinkedTimerID field access
|
||||||
|
return 0
|
||||||
|
}
|
@ -11,6 +11,7 @@ import (
|
|||||||
"eq2emu/internal/languages"
|
"eq2emu/internal/languages"
|
||||||
"eq2emu/internal/quests"
|
"eq2emu/internal/quests"
|
||||||
"eq2emu/internal/skills"
|
"eq2emu/internal/skills"
|
||||||
|
"eq2emu/internal/spawn"
|
||||||
"eq2emu/internal/spells"
|
"eq2emu/internal/spells"
|
||||||
"eq2emu/internal/titles"
|
"eq2emu/internal/titles"
|
||||||
)
|
)
|
||||||
@ -81,7 +82,7 @@ type QuickBarItem struct {
|
|||||||
ID int32
|
ID int32
|
||||||
Tier int8
|
Tier int8
|
||||||
UniqueID int64
|
UniqueID int64
|
||||||
Text common.EQ2String16Bit
|
Text common.EQ2String16
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoginAppearances represents equipment appearance data for login
|
// LoginAppearances represents equipment appearance data for login
|
||||||
@ -347,8 +348,8 @@ type Player struct {
|
|||||||
spawnPosPacketList map[int32]string
|
spawnPosPacketList map[int32]string
|
||||||
spawnPacketSent map[int32]int8
|
spawnPacketSent map[int32]int8
|
||||||
spawnStateList map[int32]*SpawnQueueState
|
spawnStateList map[int32]*SpawnQueueState
|
||||||
playerSpawnIDMap map[int32]*entity.Spawn
|
playerSpawnIDMap map[int32]*spawn.Spawn
|
||||||
playerSpawnReverseIDMap map[*entity.Spawn]int32
|
playerSpawnReverseIDMap map[*spawn.Spawn]int32
|
||||||
playerAggroRangeSpawns map[int32]bool
|
playerAggroRangeSpawns map[int32]bool
|
||||||
|
|
||||||
// Temporary spawn packets for XOR
|
// Temporary spawn packets for XOR
|
||||||
@ -378,7 +379,7 @@ type Player struct {
|
|||||||
playerQuests map[int32]*quests.Quest
|
playerQuests map[int32]*quests.Quest
|
||||||
completedQuests map[int32]*quests.Quest
|
completedQuests map[int32]*quests.Quest
|
||||||
pendingQuests map[int32]*quests.Quest
|
pendingQuests map[int32]*quests.Quest
|
||||||
currentQuestFlagged map[*entity.Spawn]bool
|
currentQuestFlagged map[*spawn.Spawn]bool
|
||||||
playerSpawnQuestsRequired map[int32][]int32
|
playerSpawnQuestsRequired map[int32][]int32
|
||||||
playerSpawnHistoryRequired map[int32][]int32
|
playerSpawnHistoryRequired map[int32][]int32
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user