401 lines
9.7 KiB
Go
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)
|
|
} |