2025-08-29 17:48:39 -05:00

422 lines
9.9 KiB
Go

package ai
import (
"encoding/json"
"fmt"
"time"
)
// AI tick constants
const (
DefaultThinkTick int32 = 250 // Default think tick in milliseconds (1/4 second)
FastThinkTick int32 = 100 // Fast think tick for active AI
SlowThinkTick int32 = 1000 // Slow think tick for idle AI
BlankBrainTick int32 = 50000 // Very slow tick for blank brain
MaxThinkTick int32 = 60000 // Maximum think tick (1 minute)
)
// Combat constants
const (
MaxChaseDistance float32 = 150.0 // Default max chase distance
MaxCombatRange float32 = 25.0 // Default max combat range
RunbackThreshold float32 = 1.0 // Distance threshold for runback
)
// Hate system constants
const (
MinHateValue int32 = 1 // Minimum hate value (0 or negative is invalid)
MaxHateValue int32 = 2147483647 // Maximum hate value (INT_MAX)
DefaultHateValue int32 = 100 // Default hate amount
MaxHateListSize int = 100 // Maximum entities in hate list
)
// Encounter system constants
const (
MaxEncounterSize int = 50 // Maximum entities in encounter list
)
// Spell recovery constants
const (
SpellRecoveryBuffer int32 = 2000 // Additional recovery time buffer (2 seconds)
)
// Brain type constants for identification
const (
BrainTypeDefault int8 = 0
BrainTypeCombatPet int8 = 1
BrainTypeNonCombatPet int8 = 2
BrainTypeBlank int8 = 3
BrainTypeCustom int8 = 4
BrainTypeDumbFire int8 = 5
)
// Pet movement constants
const (
PetMovementFollow int8 = 0
PetMovementStay int8 = 1
PetMovementGuard int8 = 2
)
// Encounter state constants
const (
EncounterStateAvailable int8 = 0
EncounterStateLocked int8 = 1
EncounterStateBroken int8 = 2
)
// Combat decision constants
const (
MeleeAttackChance int = 70 // Base chance for melee attack
SpellCastChance int = 30 // Base chance for spell casting
BuffCheckChance int = 50 // Chance to check for buffs
)
// AI state flags
const (
AIStateIdle int32 = 0
AIStateCombat int32 = 1
AIStateFollowing int32 = 2
AIStateRunback int32 = 3
AIStateCasting int32 = 4
AIStateMoving int32 = 5
)
// Debug levels
const (
DebugLevelNone int8 = 0
DebugLevelBasic int8 = 1
DebugLevelDetailed int8 = 2
DebugLevelVerbose int8 = 3
)
// Timer constants
const (
MillisecondsPerSecond int32 = 1000
RecoveryTimeMultiple int32 = 10 // Multiply cast/recovery times by 10
)
// MovementLocation represents a location for movement/runback
type MovementLocation struct {
X float32
Y float32
Z float32
GridID int32
Stage int32
}
// Brain interface defines all AI brain capabilities
type Brain interface {
// Core brain functions
Think() error
GetBrainType() int8
IsActive() bool
SetActive(bool)
GetLastThink() int64
SetLastThink(int64)
GetThinkTick() int32
SetThinkTick(int32)
// Hate management
AddHate(entityID, hate int32)
GetHate(entityID int32) int32
GetMostHated() int32
ClearHate()
ClearHateForEntity(entityID int32)
// Encounter management
AddToEncounter(entityID, characterID int32, isPlayer, isBot bool) bool
RemoveFromEncounter(entityID int32)
IsInEncounter(entityID int32) bool
ClearEncounter()
GetEncounterSize() int
// State management
GetState() int32
SetState(int32)
HasRecovered() bool
SetSpellRecovery(int64)
// Statistics
GetStatistics() *BrainStatistics
// Debugging
SetDebugLevel(int8)
GetDebugLevel() int8
}
// Logger interface for AI logging
type Logger interface {
LogInfo(message string, args ...any)
LogError(message string, args ...any)
LogDebug(message string, args ...any)
LogWarning(message string, args ...any)
}
// NPC interface defines the required NPC functionality for AI
type NPC interface {
// Basic NPC information
GetID() int32
GetName() string
GetHP() int32
GetTotalHP() int32
SetHP(int32)
IsAlive() bool
// Combat state
GetInCombat() bool
InCombat(bool)
GetTarget() Entity
SetTarget(Entity)
// Pet functionality
IsPet() bool
GetOwner() Entity
// Movement and positioning
GetX() float32
GetY() float32
GetZ() float32
GetDistance(Entity) float32
FaceTarget(Entity, bool)
IsFollowing() bool
SetFollowing(bool)
GetFollowTarget() Spawn
SetFollowTarget(Spawn, float32)
CalculateRunningLocation(bool)
ClearRunningLocations()
// Runback functionality
IsRunningBack() bool
GetRunbackLocation() *MovementLocation
GetRunbackDistance() float32
Runback(float32)
ShouldCallRunback() bool
SetCallRunback(bool)
// Status effects
IsMezzedOrStunned() bool
IsCasting() bool
IsDazed() bool
IsFeared() bool
IsStifled() bool
InWater() bool
IsWaterCreature() bool
IsFlyingCreature() bool
// Combat mechanics
AttackAllowed(Entity) bool
PrimaryWeaponReady() bool
SecondaryWeaponReady() bool
SetPrimaryLastAttackTime(int64)
SetSecondaryLastAttackTime(int64)
MeleeAttack(Entity, float32, bool)
// Spell casting
GetCastPercentage() int8
GetNextSpell(Entity, float32) Spell
GetNextBuffSpell(Spawn) Spell
SetCastOnAggroCompleted(bool)
CheckLoS(Entity) bool
// Movement pausing
IsPauseMovementTimerActive() bool
// Encounter state
SetEncounterState(int8)
// Scripts
GetSpawnScript() string
// Utility
KillSpawn(NPC)
}
// Entity interface for combat entities
type Entity interface {
Spawn
GetID() int32
GetName() string
GetHP() int32
GetTotalHP() int32
IsPlayer() bool
IsBot() bool
IsPet() bool
GetOwner() Entity
InWater() bool
}
// Spawn interface for basic spawn functionality
type Spawn interface {
GetID() int32
GetName() string
GetX() float32
GetY() float32
GetZ() float32
}
// Spell interface for spell data
type Spell interface {
GetSpellID() int32
GetName() string
IsFriendlySpell() bool
GetCastTime() int32
GetRecoveryTime() int32
GetRange() float32
GetMinRange() float32
}
// Zone interface for zone-related AI operations
type Zone interface {
GetSpawnByID(int32) Spawn
ProcessSpell(spell Spell, caster NPC, target Spawn) error
CallSpawnScript(npc NPC, scriptType string, args ...any) error
}
// Packet types for AI communication
type AIPacketType uint8
const (
AIPacketTypeUpdate AIPacketType = 1
AIPacketTypeStateUpdate AIPacketType = 2
AIPacketTypeHateUpdate AIPacketType = 3
AIPacketTypeEncounter AIPacketType = 4
AIPacketTypeCommand AIPacketType = 5
AIPacketTypeBrainType AIPacketType = 6
AIPacketTypeStatistics AIPacketType = 7
AIPacketTypeDebugUpdate AIPacketType = 8
)
// AIPacket represents an AI communication packet
type AIPacket struct {
Type AIPacketType `json:"type"`
EntityID int32 `json:"entity_id"`
Timestamp int64 `json:"timestamp"`
Data map[string]interface{} `json:"data"`
}
// AIClient interface for AI communication
type AIClient interface {
SendPacket(packet *AIPacket) error
BroadcastPacket(packet *AIPacket) error
}
// AIStatistics contains AI system statistics
type AIStatistics struct {
TotalBrains int `json:"total_brains"`
ActiveBrains int `json:"active_brains"`
TotalThinks int64 `json:"total_thinks"`
BrainsByType map[string]int `json:"brains_by_type"`
}
// BrainStatistics contains brain performance statistics
type BrainStatistics struct {
ThinkCycles int64 `json:"think_cycles"`
SpellsCast int64 `json:"spells_cast"`
MeleeAttacks int64 `json:"melee_attacks"`
HateEvents int64 `json:"hate_events"`
EncounterEvents int64 `json:"encounter_events"`
AverageThinkTime float64 `json:"average_think_time_ms"`
LastThinkTime int64 `json:"last_think_time"`
TotalActiveTime int64 `json:"total_active_time_ms"`
}
// NewBrainStatistics creates new brain statistics
func NewBrainStatistics() *BrainStatistics {
return &BrainStatistics{
ThinkCycles: 0,
SpellsCast: 0,
MeleeAttacks: 0,
HateEvents: 0,
EncounterEvents: 0,
AverageThinkTime: 0.0,
LastThinkTime: time.Now().UnixMilli(),
TotalActiveTime: 0,
}
}
// CreateBrain creates the appropriate brain type for an NPC
func CreateBrain(npc NPC, brainType int8, logger Logger, options ...any) Brain {
switch brainType {
case BrainTypeCombatPet:
return NewCombatPetBrain(npc, logger)
case BrainTypeNonCombatPet:
return NewNonCombatPetBrain(npc, logger)
case BrainTypeBlank:
return NewBlankBrain(npc, logger)
case BrainTypeCustom:
if len(options) > 0 {
if customAI, ok := options[0].(CustomAI); ok {
return NewCustomBrain(npc, logger, customAI)
}
}
return NewBaseBrain(npc, logger) // Fallback to default
case BrainTypeDumbFire:
if len(options) >= 2 {
if target, ok := options[0].(Entity); ok {
if expireTime, ok := options[1].(int32); ok {
return NewDumbFirePetBrain(npc, target, expireTime, logger)
}
}
}
return NewBaseBrain(npc, logger) // Fallback to default
default:
return NewBaseBrain(npc, logger)
}
}
// Helper functions
// getBrainTypeName returns the string name for a brain type
func getBrainTypeName(brainType int8) string {
switch brainType {
case BrainTypeDefault:
return "default"
case BrainTypeCombatPet:
return "combat_pet"
case BrainTypeNonCombatPet:
return "non_combat_pet"
case BrainTypeBlank:
return "blank"
case BrainTypeCustom:
return "custom"
case BrainTypeDumbFire:
return "dumbfire"
default:
return "unknown"
}
}
// currentTimeMillis returns current time in milliseconds
func currentTimeMillis() int64 {
return time.Now().UnixMilli()
}
// CreateAIPacket creates a new AI packet
func CreateAIPacket(packetType AIPacketType, entityID int32, data map[string]interface{}) *AIPacket {
return &AIPacket{
Type: packetType,
EntityID: entityID,
Timestamp: currentTimeMillis(),
Data: data,
}
}
// ToJSON converts AI packet to JSON
func (p *AIPacket) ToJSON() ([]byte, error) {
return json.Marshal(p)
}
// FromJSON creates AI packet from JSON
func FromJSON(data []byte) (*AIPacket, error) {
var packet AIPacket
err := json.Unmarshal(data, &packet)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal AI packet: %w", err)
}
return &packet, nil
}