2025-08-30 11:51:05 -05:00

401 lines
9.7 KiB
Go

package combat
import (
"math"
"sync"
"time"
)
// HateManager manages hate/threat for all entities in combat
type HateManager struct {
hateLists map[int32]*HateList
hatesMutex sync.RWMutex
config *CombatConfig
}
// NewHateManager creates a new hate manager
func NewHateManager(config *CombatConfig) *HateManager {
return &HateManager{
hateLists: make(map[int32]*HateList),
config: config,
}
}
// AddHate adds hate/threat to an entity's hate list
func (hm *HateManager) AddHate(ownerID int32, targetID int32, hateAmount int32, lockHate bool) {
hm.hatesMutex.Lock()
defer hm.hatesMutex.Unlock()
// Get or create hate list
hateList, exists := hm.hateLists[ownerID]
if !exists {
hateList = NewHateList(ownerID)
hm.hateLists[ownerID] = hateList
}
hateList.mutex.Lock()
defer hateList.mutex.Unlock()
// Get or create hate entry
hateEntry, exists := hateList.HateEntries[targetID]
if !exists {
hateEntry = NewHateEntry(targetID, 0)
hateList.HateEntries[targetID] = hateEntry
}
// Add hate
hateEntry.HateAmount += hateAmount
hateEntry.LastHit = time.Now()
if lockHate {
hateEntry.IsLocked = true
}
// Update most hated
hm.updateMostHated(hateList)
hateList.LastUpdate = time.Now()
}
// RemoveHate removes hate from a target
func (hm *HateManager) RemoveHate(ownerID int32, targetID int32, hateAmount int32) {
hm.hatesMutex.Lock()
defer hm.hatesMutex.Unlock()
hateList, exists := hm.hateLists[ownerID]
if !exists {
return
}
hateList.mutex.Lock()
defer hateList.mutex.Unlock()
hateEntry, exists := hateList.HateEntries[targetID]
if !exists {
return
}
// Remove hate
hateEntry.HateAmount -= hateAmount
if hateEntry.HateAmount <= 0 && !hateEntry.IsLocked {
delete(hateList.HateEntries, targetID)
}
// Update most hated
hm.updateMostHated(hateList)
hateList.LastUpdate = time.Now()
}
// SetHate sets the exact hate amount for a target
func (hm *HateManager) SetHate(ownerID int32, targetID int32, hateAmount int32) {
hm.hatesMutex.Lock()
defer hm.hatesMutex.Unlock()
hateList, exists := hm.hateLists[ownerID]
if !exists {
hateList = NewHateList(ownerID)
hm.hateLists[ownerID] = hateList
}
hateList.mutex.Lock()
defer hateList.mutex.Unlock()
hateEntry, exists := hateList.HateEntries[targetID]
if !exists {
hateEntry = NewHateEntry(targetID, hateAmount)
hateList.HateEntries[targetID] = hateEntry
} else {
hateEntry.HateAmount = hateAmount
}
hateEntry.LastHit = time.Now()
// Update most hated
hm.updateMostHated(hateList)
hateList.LastUpdate = time.Now()
}
// ClearHate clears all hate for a specific owner
func (hm *HateManager) ClearHate(ownerID int32) {
hm.hatesMutex.Lock()
defer hm.hatesMutex.Unlock()
delete(hm.hateLists, ownerID)
}
// ClearHateTarget removes a target from all hate lists
func (hm *HateManager) ClearHateTarget(targetID int32) {
hm.hatesMutex.Lock()
defer hm.hatesMutex.Unlock()
for _, hateList := range hm.hateLists {
hateList.mutex.Lock()
delete(hateList.HateEntries, targetID)
hm.updateMostHated(hateList)
hateList.mutex.Unlock()
}
}
// GetHateList returns a copy of the hate list for an entity
func (hm *HateManager) GetHateList(ownerID int32) *HateList {
hm.hatesMutex.RLock()
defer hm.hatesMutex.RUnlock()
originalList, exists := hm.hateLists[ownerID]
if !exists {
return nil
}
// Create a copy to prevent external modification
copyList := &HateList{
OwnerID: originalList.OwnerID,
HateEntries: make(map[int32]*HateEntry),
MostHated: originalList.MostHated,
LastUpdate: originalList.LastUpdate,
}
originalList.mutex.RLock()
defer originalList.mutex.RUnlock()
for id, entry := range originalList.HateEntries {
copyList.HateEntries[id] = &HateEntry{
TargetID: entry.TargetID,
HateAmount: entry.HateAmount,
DamageAmount: entry.DamageAmount,
LastHit: entry.LastHit,
IsLocked: entry.IsLocked,
}
}
return copyList
}
// GetMostHated returns the most hated target for an entity
func (hm *HateManager) GetMostHated(ownerID int32) int32 {
hm.hatesMutex.RLock()
defer hm.hatesMutex.RUnlock()
if hateList, exists := hm.hateLists[ownerID]; exists {
return hateList.MostHated
}
return 0
}
// GetHateAmount returns the hate amount for a specific target
func (hm *HateManager) GetHateAmount(ownerID int32, targetID int32) int32 {
hm.hatesMutex.RLock()
defer hm.hatesMutex.RUnlock()
hateList, exists := hm.hateLists[ownerID]
if !exists {
return 0
}
hateList.mutex.RLock()
defer hateList.mutex.RUnlock()
if hateEntry, exists := hateList.HateEntries[targetID]; exists {
return hateEntry.HateAmount
}
return 0
}
// HasHateTarget checks if an owner has hate toward a specific target
func (hm *HateManager) HasHateTarget(ownerID int32, targetID int32) bool {
return hm.GetHateAmount(ownerID, targetID) > 0
}
// GetTopHateTargets returns the top N hate targets for an entity
func (hm *HateManager) GetTopHateTargets(ownerID int32, count int) []int32 {
hateList := hm.GetHateList(ownerID)
if hateList == nil || len(hateList.HateEntries) == 0 {
return nil
}
// Create slice of entries for sorting
type hateTarget struct {
targetID int32
hate int32
}
targets := make([]hateTarget, 0, len(hateList.HateEntries))
for targetID, entry := range hateList.HateEntries {
targets = append(targets, hateTarget{
targetID: targetID,
hate: entry.HateAmount,
})
}
// Simple bubble sort for top N (sufficient for small hate lists)
for i := 0; i < len(targets)-1; i++ {
for j := 0; j < len(targets)-i-1; j++ {
if targets[j].hate < targets[j+1].hate {
targets[j], targets[j+1] = targets[j+1], targets[j]
}
}
}
// Return top N target IDs
result := make([]int32, 0, count)
for i := 0; i < count && i < len(targets); i++ {
result = append(result, targets[i].targetID)
}
return result
}
// ProcessHateDecay applies hate decay over time
func (hm *HateManager) ProcessHateDecay(currentTime time.Time) {
hm.hatesMutex.Lock()
defer hm.hatesMutex.Unlock()
for ownerID, hateList := range hm.hateLists {
hateList.mutex.Lock()
timeDiff := float32(currentTime.Sub(hateList.LastUpdate).Seconds())
decayAmount := int32(timeDiff * hm.config.HateDecayRate)
if decayAmount > 0 {
hasHate := false
for targetID, entry := range hateList.HateEntries {
if !entry.IsLocked {
entry.HateAmount -= decayAmount
if entry.HateAmount <= 0 {
delete(hateList.HateEntries, targetID)
} else {
hasHate = true
}
} else {
hasHate = true
}
}
hateList.LastUpdate = currentTime
// Remove empty hate lists
if !hasHate {
hateList.mutex.Unlock()
delete(hm.hateLists, ownerID)
continue
}
// Update most hated
hm.updateMostHated(hateList)
}
hateList.mutex.Unlock()
}
}
// LockHate locks hate for a target (prevents decay)
func (hm *HateManager) LockHate(ownerID int32, targetID int32, lock bool) {
hm.hatesMutex.Lock()
defer hm.hatesMutex.Unlock()
hateList, exists := hm.hateLists[ownerID]
if !exists {
return
}
hateList.mutex.Lock()
defer hateList.mutex.Unlock()
if hateEntry, exists := hateList.HateEntries[targetID]; exists {
hateEntry.IsLocked = lock
}
}
// CalculateHateModifier calculates hate modifier based on various factors
func (hm *HateManager) CalculateHateModifier(attacker Entity, victim Entity, damageAmount int32, isHealing bool) float32 {
modifier := float32(1.0)
// Level difference affects hate generation
levelDiff := float32(attacker.GetLevel() - victim.GetLevel())
if levelDiff > 0 {
// Higher level attacker generates more hate
modifier += levelDiff * 0.02
} else if levelDiff < 0 {
// Lower level attacker generates less hate
modifier += levelDiff * 0.01
}
// Distance affects hate (closer = more threatening)
distance := hm.calculateDistance(attacker, victim)
if distance > 100.0 {
modifier *= 0.8 // Reduced hate for distant attackers
}
// Healing generates less hate than damage
if isHealing {
modifier *= 0.5
}
// Critical hits generate more hate
if damageAmount > 0 {
// Check if this was a particularly high damage hit
avgLevel := float32(attacker.GetLevel() + victim.GetLevel()) / 2.0
expectedDamage := avgLevel * 10.0 // Rough estimate
if float32(damageAmount) > expectedDamage*1.5 {
modifier *= 1.25 // Bonus hate for high damage
}
}
// Cap modifier
if modifier > 2.0 {
modifier = 2.0
} else if modifier < 0.1 {
modifier = 0.1
}
return modifier
}
// GetHateListCount returns the number of active hate lists
func (hm *HateManager) GetHateListCount() int {
hm.hatesMutex.RLock()
defer hm.hatesMutex.RUnlock()
return len(hm.hateLists)
}
// GetTotalHateEntries returns the total number of hate entries across all lists
func (hm *HateManager) GetTotalHateEntries() int {
hm.hatesMutex.RLock()
defer hm.hatesMutex.RUnlock()
total := 0
for _, hateList := range hm.hateLists {
hateList.mutex.RLock()
total += len(hateList.HateEntries)
hateList.mutex.RUnlock()
}
return total
}
// updateMostHated updates the most hated target in a hate list (internal method)
func (hm *HateManager) updateMostHated(hateList *HateList) {
var maxHate int32
var mostHated int32
for targetID, entry := range hateList.HateEntries {
if entry.HateAmount > maxHate {
maxHate = entry.HateAmount
mostHated = targetID
}
}
hateList.MostHated = mostHated
}
// calculateDistance calculates distance between two entities
func (hm *HateManager) calculateDistance(entity1 Entity, entity2 Entity) float32 {
dx := entity1.GetX() - entity2.GetX()
dy := entity1.GetY() - entity2.GetY()
dz := entity1.GetZ() - entity2.GetZ()
return float32(math.Sqrt(float64(dx*dx + dy*dy + dz*dz)))
}
// Shutdown cleans up the hate manager
func (hm *HateManager) Shutdown() {
hm.hatesMutex.Lock()
defer hm.hatesMutex.Unlock()
hm.hateLists = make(map[int32]*HateList)
}