227 lines
6.7 KiB
Go
227 lines
6.7 KiB
Go
package ai
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
// CustomAI defines a Go-based custom AI behavior interface
|
|
type CustomAI interface {
|
|
// Think is called every AI tick for custom behavior
|
|
Think(npc NPC, target Entity, brain Brain) error
|
|
|
|
// OnCombatStart is called when entering combat
|
|
OnCombatStart(npc NPC, target Entity, brain Brain) error
|
|
|
|
// OnCombatEnd is called when leaving combat
|
|
OnCombatEnd(npc NPC, brain Brain) error
|
|
|
|
// OnDamageReceived is called when NPC takes damage
|
|
OnDamageReceived(npc NPC, attacker Entity, damage int32, brain Brain) error
|
|
|
|
// OnTargetChanged is called when NPC changes target
|
|
OnTargetChanged(npc NPC, oldTarget, newTarget Entity, brain Brain) error
|
|
}
|
|
|
|
// CustomBrain allows AI to be controlled by Go-based custom AI implementations
|
|
type CustomBrain struct {
|
|
*BaseBrain
|
|
customAI CustomAI
|
|
}
|
|
|
|
// NewCustomBrain creates a new custom AI brain
|
|
func NewCustomBrain(npc NPC, logger Logger, customAI CustomAI) *CustomBrain {
|
|
brain := &CustomBrain{
|
|
BaseBrain: NewBaseBrain(npc, logger),
|
|
customAI: customAI,
|
|
}
|
|
brain.brainType = BrainTypeCustom
|
|
return brain
|
|
}
|
|
|
|
// Think calls the custom AI's Think function
|
|
func (cb *CustomBrain) Think() error {
|
|
if cb.customAI == nil {
|
|
// Fall back to default behavior if no custom AI
|
|
return cb.BaseBrain.Think()
|
|
}
|
|
|
|
if cb.npc == nil {
|
|
return fmt.Errorf("brain has no body")
|
|
}
|
|
|
|
target := cb.npc.GetTarget()
|
|
err := cb.customAI.Think(cb.npc, target, cb)
|
|
if err != nil {
|
|
if cb.logger != nil {
|
|
cb.logger.LogError("Custom AI Think function failed: %v", err)
|
|
}
|
|
return fmt.Errorf("custom AI Think function failed: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SetCustomAI sets the custom AI implementation
|
|
func (cb *CustomBrain) SetCustomAI(customAI CustomAI) {
|
|
cb.customAI = customAI
|
|
}
|
|
|
|
// GetCustomAI returns the custom AI implementation
|
|
func (cb *CustomBrain) GetCustomAI() CustomAI {
|
|
return cb.customAI
|
|
}
|
|
|
|
// NotifyCombatStart notifies custom AI of combat start
|
|
func (cb *CustomBrain) NotifyCombatStart(target Entity) {
|
|
if cb.customAI != nil {
|
|
if err := cb.customAI.OnCombatStart(cb.npc, target, cb); err != nil && cb.logger != nil {
|
|
cb.logger.LogError("Custom AI OnCombatStart failed: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// NotifyCombatEnd notifies custom AI of combat end
|
|
func (cb *CustomBrain) NotifyCombatEnd() {
|
|
if cb.customAI != nil {
|
|
if err := cb.customAI.OnCombatEnd(cb.npc, cb); err != nil && cb.logger != nil {
|
|
cb.logger.LogError("Custom AI OnCombatEnd failed: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// NotifyDamageReceived notifies custom AI of damage received
|
|
func (cb *CustomBrain) NotifyDamageReceived(attacker Entity, damage int32) {
|
|
if cb.customAI != nil {
|
|
if err := cb.customAI.OnDamageReceived(cb.npc, attacker, damage, cb); err != nil && cb.logger != nil {
|
|
cb.logger.LogError("Custom AI OnDamageReceived failed: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// NotifyTargetChanged notifies custom AI of target change
|
|
func (cb *CustomBrain) NotifyTargetChanged(oldTarget, newTarget Entity) {
|
|
if cb.customAI != nil {
|
|
if err := cb.customAI.OnTargetChanged(cb.npc, oldTarget, newTarget, cb); err != nil && cb.logger != nil {
|
|
cb.logger.LogError("Custom AI OnTargetChanged failed: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Example Custom AI implementations for demonstration
|
|
|
|
// AggressiveAI implements CustomAI for aggressive behavior
|
|
type AggressiveAI struct{}
|
|
|
|
// Think implements aggressive AI behavior
|
|
func (a *AggressiveAI) Think(npc NPC, target Entity, brain Brain) error {
|
|
// Always try to attack if we have a target
|
|
if target != nil {
|
|
distance := npc.GetDistance(target)
|
|
if distance <= 25.0 { // Within range
|
|
// Force attack more frequently than normal
|
|
if npc.PrimaryWeaponReady() {
|
|
npc.MeleeAttack(target, distance, true)
|
|
}
|
|
}
|
|
}
|
|
// Fall back to base brain behavior for movement, spells, etc.
|
|
return brain.(*CustomBrain).BaseBrain.Think()
|
|
}
|
|
|
|
// OnCombatStart implements aggressive combat start
|
|
func (a *AggressiveAI) OnCombatStart(npc NPC, target Entity, brain Brain) error {
|
|
// Add extra hate when combat starts
|
|
if target != nil {
|
|
brain.AddHate(target.GetID(), DefaultHateValue*2)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// OnCombatEnd implements aggressive combat end
|
|
func (a *AggressiveAI) OnCombatEnd(npc NPC, brain Brain) error {
|
|
// Stay alert longer after combat
|
|
brain.SetThinkTick(FastThinkTick)
|
|
return nil
|
|
}
|
|
|
|
// OnDamageReceived implements aggressive damage response
|
|
func (a *AggressiveAI) OnDamageReceived(npc NPC, attacker Entity, damage int32, brain Brain) error {
|
|
// Add extra hate when damaged
|
|
if attacker != nil {
|
|
brain.AddHate(attacker.GetID(), damage*2)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// OnTargetChanged implements aggressive target change
|
|
func (a *AggressiveAI) OnTargetChanged(npc NPC, oldTarget, newTarget Entity, brain Brain) error {
|
|
// No special behavior for target changes in aggressive AI
|
|
return nil
|
|
}
|
|
|
|
// DefensiveAI implements CustomAI for defensive behavior
|
|
type DefensiveAI struct {
|
|
fleeThreshold int32 // HP threshold to flee at
|
|
}
|
|
|
|
// NewDefensiveAI creates a defensive AI with flee threshold
|
|
func NewDefensiveAI(fleeThreshold int32) *DefensiveAI {
|
|
return &DefensiveAI{
|
|
fleeThreshold: fleeThreshold,
|
|
}
|
|
}
|
|
|
|
// Think implements defensive AI behavior
|
|
func (d *DefensiveAI) Think(npc NPC, target Entity, brain Brain) error {
|
|
// Check if we should flee
|
|
if npc.GetHP() <= d.fleeThreshold {
|
|
// Try to run back to spawn point
|
|
if npc.ShouldCallRunback() {
|
|
npc.Runback(npc.GetRunbackDistance())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Otherwise use normal behavior but with slower think tick
|
|
brain.SetThinkTick(SlowThinkTick)
|
|
return brain.(*CustomBrain).BaseBrain.Think()
|
|
}
|
|
|
|
// OnCombatStart implements defensive combat start
|
|
func (d *DefensiveAI) OnCombatStart(npc NPC, target Entity, brain Brain) error {
|
|
// No special behavior for defensive AI
|
|
return nil
|
|
}
|
|
|
|
// OnCombatEnd implements defensive combat end
|
|
func (d *DefensiveAI) OnCombatEnd(npc NPC, brain Brain) error {
|
|
// Slow down thinking after combat
|
|
brain.SetThinkTick(SlowThinkTick)
|
|
return nil
|
|
}
|
|
|
|
// OnDamageReceived implements defensive damage response
|
|
func (d *DefensiveAI) OnDamageReceived(npc NPC, attacker Entity, damage int32, brain Brain) error {
|
|
// Check if we should start fleeing
|
|
if npc.GetHP() <= d.fleeThreshold && attacker != nil {
|
|
// Remove hate and try to flee
|
|
brain.ClearHateForEntity(attacker.GetID())
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// OnTargetChanged implements defensive target change
|
|
func (d *DefensiveAI) OnTargetChanged(npc NPC, oldTarget, newTarget Entity, brain Brain) error {
|
|
// No special behavior for target changes in defensive AI
|
|
return nil
|
|
}
|
|
|
|
// CreateAggressiveBrain creates a brain with aggressive AI behavior
|
|
func CreateAggressiveBrain(npc NPC, logger Logger) Brain {
|
|
return NewCustomBrain(npc, logger, &AggressiveAI{})
|
|
}
|
|
|
|
// CreateDefensiveBrain creates a brain with defensive AI behavior
|
|
func CreateDefensiveBrain(npc NPC, logger Logger, fleeThreshold int32) Brain {
|
|
return NewCustomBrain(npc, logger, NewDefensiveAI(fleeThreshold))
|
|
} |