AI/ActionSystem/Actions/Staff/ExterminateAction.cs
namespace HC3;
[Icon( "pest_control" )]
public sealed class ExterminateAction : BehaviorTreeAction
{
private Animal _targetAnimal;
private Vector3 _lastTargetPosition;
private const float RepathDistance = 256f;
private const float CaptureDistance = 100f;
public override float Score()
{
// Find any escaped animal
_targetAnimal = FindEscapedAnimal();
if ( !_targetAnimal.IsValid() )
return 0f;
return 100f;
}
private Animal FindEscapedAnimal()
{
// TODO: order by closest (?)
return Scene.GetAll<Animal>()
.FirstOrDefault( animal => animal.Tags.Has( "escaped" ) );
}
public override void StartAction()
{
base.StartAction();
if ( _targetAnimal == null )
{
_targetAnimal = FindEscapedAnimal();
if ( _targetAnimal == null )
return;
}
_lastTargetPosition = _targetAnimal.WorldPosition;
Agent.Controller.IsRunning = true;
}
public override void StopAction()
{
base.StopAction();
Agent.Controller.IsRunning = false;
Agent.SetSequenceOverride( null );
}
protected override Node BuildTree()
{
if ( !_targetAnimal.IsValid() || !_targetAnimal.Tags.Has( "escaped" ) )
return null;
return new SequenceNode(
[
// 1. Move to the animal, following it if needed
new FollowTargetNode( Agent, _targetAnimal.GameObject, CaptureDistance, RepathDistance, "Pursuing escaped animal" ),
// 2. Aim at the animal
new LookAtNode( Agent, _targetAnimal.WorldPosition ),
new AgentSequenceNode( Agent, "Action_Gun_Aim", 0.5f, "Taking aim" ),
// 3. Shoot the animal with tranquilizer
new TimedSequenceNode(
Agent,
"Action_Gun_Fire",
0.4f,
onStart: () => {
// Play sound effect
Sound.Play( "sounds/gameplay/tranquilizer.sound", Agent.WorldPosition );
// Affect the animal
_targetAnimal.Tags.Set( "tranquilized", true );
},
conditionCheck: () => _targetAnimal.IsValid(),
reason: "Firing tranquilizer dart",
icon: "gps_fixed"
),
// 4. Wait for tranquilizer to take effect and return animal to enclosure
new TimedActionNode(
10.0f,
() => ReturnAnimalToEnclosure( _targetAnimal ),
() => _targetAnimal.IsValid(),
"Tranquilizer taking effect",
"local_hospital"
)
] );
}
private void ReturnAnimalToEnclosure( Animal animal )
{
if ( !animal.IsValid() )
return;
// Find an enclosure to return the animal to
var enclosureArea = Scene.GetAllComponents<AreaTile>()
.FirstOrDefault( area => area.AreaType == AreaType.Enclosure );
if ( enclosureArea.IsValid() )
{
// Move the animal to that enclosure
animal.GameObject.WorldPosition = enclosureArea.WorldPosition + Vector3.Up * 10f;
animal.GameObject.Transform.ClearInterpolation();
// DebugOverlay.Sphere( new Sphere( enclosureArea.WorldPosition + Vector3.Up * 10f, 32f ), Color.Green, 15f );
}
// Remove escaped/tranq tags
animal.Tags.Set( "escaped", false );
animal.Tags.Set( "tranquilized", false );
}
public override ActionDisplayInfo? GetDisplay()
{
if ( !_targetAnimal.IsValid() )
return new ActionDisplayInfo( "search", "Looking for escaped animals", 0 );
return null; // The individual nodes will provide their own display info
}
}