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) }