350 lines
9.2 KiB
Go
350 lines
9.2 KiB
Go
package factions
|
|
|
|
import (
|
|
"sync"
|
|
)
|
|
|
|
// PlayerFaction manages faction standing for a single player
|
|
type PlayerFaction struct {
|
|
factionValues map[int32]int32 // Faction ID -> current value
|
|
factionPercent map[int32]int8 // Faction ID -> percentage within con level
|
|
factionUpdateNeeded []int32 // Factions that need client updates
|
|
masterFactionList *MasterList
|
|
updateMutex sync.Mutex // Thread safety for updates
|
|
mutex sync.RWMutex // Thread safety for faction data
|
|
}
|
|
|
|
// NewPlayerFaction creates a new player faction system
|
|
func NewPlayerFaction(masterFactionList *MasterList) *PlayerFaction {
|
|
return &PlayerFaction{
|
|
factionValues: make(map[int32]int32),
|
|
factionPercent: make(map[int32]int8),
|
|
factionUpdateNeeded: make([]int32, 0),
|
|
masterFactionList: masterFactionList,
|
|
}
|
|
}
|
|
|
|
// GetMaxValue returns the maximum faction value for a given consideration level
|
|
func (pf *PlayerFaction) GetMaxValue(con int8) int32 {
|
|
if con < 0 {
|
|
return int32(con) * ConMultiplier
|
|
}
|
|
return (int32(con) * ConMultiplier) + ConRemainder
|
|
}
|
|
|
|
// GetMinValue returns the minimum faction value for a given consideration level
|
|
func (pf *PlayerFaction) GetMinValue(con int8) int32 {
|
|
if con <= 0 {
|
|
return (int32(con) * ConMultiplier) - ConRemainder
|
|
}
|
|
return int32(con) * ConMultiplier
|
|
}
|
|
|
|
// ShouldAttack returns true if the player should attack based on faction
|
|
func (pf *PlayerFaction) ShouldAttack(factionID int32) bool {
|
|
return pf.GetCon(factionID) <= AttackThreshold
|
|
}
|
|
|
|
// GetCon returns the consideration level (-4 to 4) for a faction
|
|
func (pf *PlayerFaction) GetCon(factionID int32) int8 {
|
|
// Special faction IDs have predefined cons
|
|
if factionID <= SpecialFactionIDMax {
|
|
if factionID == 0 {
|
|
return ConIndiff
|
|
}
|
|
return int8(factionID - 5)
|
|
}
|
|
|
|
value := pf.GetFactionValue(factionID)
|
|
|
|
// Neutral range
|
|
if value >= ConNeutralMin && value <= ConNeutralMax {
|
|
return ConIndiff
|
|
}
|
|
|
|
// Maximum ally
|
|
if value >= ConAllyMin {
|
|
return ConAlly
|
|
}
|
|
|
|
// Maximum hostile
|
|
if value <= ConHostileMax {
|
|
return ConKOS
|
|
}
|
|
|
|
// Calculate con based on value
|
|
return int8(value / ConMultiplier)
|
|
}
|
|
|
|
// GetPercent returns the percentage within the current consideration level
|
|
func (pf *PlayerFaction) GetPercent(factionID int32) int8 {
|
|
// Special factions have no percentage
|
|
if factionID <= SpecialFactionIDMax {
|
|
return 0
|
|
}
|
|
|
|
con := pf.GetCon(factionID)
|
|
value := pf.GetFactionValue(factionID)
|
|
|
|
if con != ConIndiff {
|
|
// Make value positive for calculation
|
|
if value <= 0 {
|
|
value *= -1
|
|
}
|
|
|
|
// Make con positive for calculation
|
|
if con < 0 {
|
|
con *= -1
|
|
}
|
|
|
|
// Calculate percentage within the con level
|
|
value -= int32(con) * ConMultiplier
|
|
value *= PercentMultiplier
|
|
return int8(value / ConMultiplier)
|
|
} else {
|
|
// Neutral range calculation
|
|
value += PercentNeutralOffset
|
|
value *= PercentMultiplier
|
|
return int8(value / PercentNeutralDivisor)
|
|
}
|
|
}
|
|
|
|
// FactionUpdate builds a faction update packet for the client
|
|
func (pf *PlayerFaction) FactionUpdate(version int16) ([]byte, error) {
|
|
pf.updateMutex.Lock()
|
|
defer pf.updateMutex.Unlock()
|
|
|
|
if len(pf.factionUpdateNeeded) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
// This is a placeholder for packet building
|
|
// In the full implementation, this would use the PacketStruct system:
|
|
// packet := configReader.getStruct("WS_FactionUpdate", version)
|
|
// packet.setArrayLengthByName("num_factions", len(pf.factionUpdateNeeded))
|
|
// for i, factionID := range pf.factionUpdateNeeded {
|
|
// faction := pf.masterFactionList.GetFaction(factionID)
|
|
// if faction != nil {
|
|
// packet.setArrayDataByName("faction_id", faction.ID, i)
|
|
// packet.setArrayDataByName("name", faction.Name, i)
|
|
// packet.setArrayDataByName("description", faction.Description, i)
|
|
// packet.setArrayDataByName("category", faction.Type, i)
|
|
// packet.setArrayDataByName("con", pf.GetCon(faction.ID), i)
|
|
// packet.setArrayDataByName("percentage", pf.GetPercent(faction.ID), i)
|
|
// packet.setArrayDataByName("value", pf.GetFactionValue(faction.ID), i)
|
|
// }
|
|
// }
|
|
// return packet.serialize()
|
|
|
|
// Clear update list
|
|
pf.factionUpdateNeeded = pf.factionUpdateNeeded[:0]
|
|
|
|
// Return empty packet for now
|
|
return make([]byte, 0), nil
|
|
}
|
|
|
|
// GetFactionValue returns the current faction value for a faction
|
|
func (pf *PlayerFaction) GetFactionValue(factionID int32) int32 {
|
|
// Special factions always return 0
|
|
if factionID <= SpecialFactionIDMax {
|
|
return 0
|
|
}
|
|
|
|
pf.mutex.RLock()
|
|
defer pf.mutex.RUnlock()
|
|
|
|
// Return current value or 0 if not set
|
|
// Note: The C++ code has a comment about always returning the default value,
|
|
// but the actual implementation returns the stored value or 0
|
|
return pf.factionValues[factionID]
|
|
}
|
|
|
|
// ShouldIncrease returns true if the faction can be increased
|
|
func (pf *PlayerFaction) ShouldIncrease(factionID int32) bool {
|
|
if factionID <= SpecialFactionIDMax {
|
|
return false
|
|
}
|
|
|
|
if pf.masterFactionList == nil {
|
|
return false
|
|
}
|
|
|
|
return pf.masterFactionList.GetIncreaseAmount(factionID) != 0
|
|
}
|
|
|
|
// ShouldDecrease returns true if the faction can be decreased
|
|
func (pf *PlayerFaction) ShouldDecrease(factionID int32) bool {
|
|
if factionID <= SpecialFactionIDMax {
|
|
return false
|
|
}
|
|
|
|
if pf.masterFactionList == nil {
|
|
return false
|
|
}
|
|
|
|
return pf.masterFactionList.GetDecreaseAmount(factionID) != 0
|
|
}
|
|
|
|
// IncreaseFaction increases a faction value
|
|
func (pf *PlayerFaction) IncreaseFaction(factionID int32, amount int32) bool {
|
|
// Special factions cannot be changed
|
|
if factionID <= SpecialFactionIDMax {
|
|
return true
|
|
}
|
|
|
|
pf.mutex.Lock()
|
|
defer pf.mutex.Unlock()
|
|
|
|
// Use default amount if not specified
|
|
if amount == 0 && pf.masterFactionList != nil {
|
|
amount = pf.masterFactionList.GetIncreaseAmount(factionID)
|
|
}
|
|
|
|
// Increase the faction value
|
|
pf.factionValues[factionID] += amount
|
|
|
|
canContinue := true
|
|
|
|
// Cap at maximum value
|
|
if pf.factionValues[factionID] >= MaxFactionValue {
|
|
pf.factionValues[factionID] = MaxFactionValue
|
|
canContinue = false
|
|
}
|
|
|
|
// Mark for update
|
|
pf.addFactionUpdateNeeded(factionID)
|
|
|
|
return canContinue
|
|
}
|
|
|
|
// DecreaseFaction decreases a faction value
|
|
func (pf *PlayerFaction) DecreaseFaction(factionID int32, amount int32) bool {
|
|
// Special factions cannot be changed
|
|
if factionID <= SpecialFactionIDMax {
|
|
return true
|
|
}
|
|
|
|
pf.mutex.Lock()
|
|
defer pf.mutex.Unlock()
|
|
|
|
// Use default amount if not specified
|
|
if amount == 0 && pf.masterFactionList != nil {
|
|
amount = pf.masterFactionList.GetDecreaseAmount(factionID)
|
|
}
|
|
|
|
// Cannot decrease if no amount specified
|
|
if amount == 0 {
|
|
return false
|
|
}
|
|
|
|
// Decrease the faction value
|
|
pf.factionValues[factionID] -= amount
|
|
|
|
canContinue := true
|
|
|
|
// Cap at minimum value
|
|
if pf.factionValues[factionID] <= MinFactionValue {
|
|
pf.factionValues[factionID] = MinFactionValue
|
|
canContinue = false
|
|
}
|
|
|
|
// Mark for update
|
|
pf.addFactionUpdateNeeded(factionID)
|
|
|
|
return canContinue
|
|
}
|
|
|
|
// SetFactionValue sets a faction to a specific value
|
|
func (pf *PlayerFaction) SetFactionValue(factionID int32, value int32) bool {
|
|
pf.mutex.Lock()
|
|
defer pf.mutex.Unlock()
|
|
|
|
pf.factionValues[factionID] = value
|
|
|
|
// Mark for update
|
|
pf.addFactionUpdateNeeded(factionID)
|
|
|
|
return true
|
|
}
|
|
|
|
// GetFactionValues returns a copy of all faction values
|
|
func (pf *PlayerFaction) GetFactionValues() map[int32]int32 {
|
|
pf.mutex.RLock()
|
|
defer pf.mutex.RUnlock()
|
|
|
|
// Return a copy to prevent external modification
|
|
result := make(map[int32]int32)
|
|
for id, value := range pf.factionValues {
|
|
result[id] = value
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// HasFaction returns true if the player has a value for the given faction
|
|
func (pf *PlayerFaction) HasFaction(factionID int32) bool {
|
|
pf.mutex.RLock()
|
|
defer pf.mutex.RUnlock()
|
|
|
|
_, exists := pf.factionValues[factionID]
|
|
return exists
|
|
}
|
|
|
|
// GetFactionCount returns the number of factions the player has values for
|
|
func (pf *PlayerFaction) GetFactionCount() int {
|
|
pf.mutex.RLock()
|
|
defer pf.mutex.RUnlock()
|
|
|
|
return len(pf.factionValues)
|
|
}
|
|
|
|
// ClearFactionValues removes all faction values
|
|
func (pf *PlayerFaction) ClearFactionValues() {
|
|
pf.mutex.Lock()
|
|
defer pf.mutex.Unlock()
|
|
|
|
pf.factionValues = make(map[int32]int32)
|
|
pf.factionPercent = make(map[int32]int8)
|
|
}
|
|
|
|
// addFactionUpdateNeeded marks a faction as needing an update (internal use, assumes lock held)
|
|
func (pf *PlayerFaction) addFactionUpdateNeeded(factionID int32) {
|
|
// Note: This method assumes the mutex is already held by the caller
|
|
pf.updateMutex.Lock()
|
|
defer pf.updateMutex.Unlock()
|
|
|
|
pf.factionUpdateNeeded = append(pf.factionUpdateNeeded, factionID)
|
|
}
|
|
|
|
// GetPendingUpdates returns factions that need client updates
|
|
func (pf *PlayerFaction) GetPendingUpdates() []int32 {
|
|
pf.updateMutex.Lock()
|
|
defer pf.updateMutex.Unlock()
|
|
|
|
if len(pf.factionUpdateNeeded) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// Return a copy
|
|
result := make([]int32, len(pf.factionUpdateNeeded))
|
|
copy(result, pf.factionUpdateNeeded)
|
|
|
|
return result
|
|
}
|
|
|
|
// ClearPendingUpdates clears the pending update list
|
|
func (pf *PlayerFaction) ClearPendingUpdates() {
|
|
pf.updateMutex.Lock()
|
|
defer pf.updateMutex.Unlock()
|
|
|
|
pf.factionUpdateNeeded = pf.factionUpdateNeeded[:0]
|
|
}
|
|
|
|
// HasPendingUpdates returns true if there are pending faction updates
|
|
func (pf *PlayerFaction) HasPendingUpdates() bool {
|
|
pf.updateMutex.Lock()
|
|
defer pf.updateMutex.Unlock()
|
|
|
|
return len(pf.factionUpdateNeeded) > 0
|
|
}
|