AI Wander & Barker (#286)
* Retrofit the AI system with new IoC features. Fixed bug with turret rotation. * Added new AI WanderProcessor, and it works. * RNG walking directions are a bit more random now. * Wander now actually uses the MoverSystem to move. Wander now talks when he reaches his destination. * Adds a new Static Barker AI for vending machines, so that they periodically advertise their brand. * Barker now says some generic slogans. Misc bug cleanup. * Removed useless UsedImplicitly attribute from AI dependencies, suppressed unused variable warnings instead.
This commit is contained in:
committed by
Pieter-Jan Briers
parent
4b30c7e710
commit
8b593d28c6
@@ -20,9 +20,11 @@ namespace Content.Server.AI
|
|||||||
[AiLogicProcessor("AimShootLife")]
|
[AiLogicProcessor("AimShootLife")]
|
||||||
class AimShootLifeProcessor : AiLogicProcessor
|
class AimShootLifeProcessor : AiLogicProcessor
|
||||||
{
|
{
|
||||||
private readonly IPhysicsManager _physMan;
|
#pragma warning disable 649
|
||||||
private readonly IServerEntityManager _entMan;
|
[Dependency] private readonly IPhysicsManager _physMan;
|
||||||
private readonly IGameTiming _timeMan;
|
[Dependency] private readonly IServerEntityManager _entMan;
|
||||||
|
[Dependency] private readonly IGameTiming _timeMan;
|
||||||
|
#pragma warning restore 649
|
||||||
|
|
||||||
private readonly List<IEntity> _workList = new List<IEntity>();
|
private readonly List<IEntity> _workList = new List<IEntity>();
|
||||||
|
|
||||||
@@ -32,16 +34,6 @@ namespace Content.Server.AI
|
|||||||
|
|
||||||
private IEntity _curTarget;
|
private IEntity _curTarget;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates an instance of this LogicProcessor.
|
|
||||||
/// </summary>
|
|
||||||
public AimShootLifeProcessor()
|
|
||||||
{
|
|
||||||
_physMan = IoCManager.Resolve<IPhysicsManager>();
|
|
||||||
_entMan = IoCManager.Resolve<IServerEntityManager>();
|
|
||||||
_timeMan = IoCManager.Resolve<IGameTiming>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
|
|||||||
71
Content.Server/AI/StaticBarkerProcessor.cs
Normal file
71
Content.Server/AI/StaticBarkerProcessor.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Content.Server.Interfaces.Chat;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Server.AI;
|
||||||
|
using Robust.Shared.Interfaces.Timing;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Server.AI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Designed for a a stationary entity that regularly advertises things (vending machine).
|
||||||
|
/// </summary>
|
||||||
|
[AiLogicProcessor("StaticBarker")]
|
||||||
|
class StaticBarkerProcessor : AiLogicProcessor
|
||||||
|
{
|
||||||
|
#pragma warning disable 649
|
||||||
|
[Dependency] private readonly IGameTiming _timeMan;
|
||||||
|
[Dependency] private readonly IChatManager _chatMan;
|
||||||
|
#pragma warning restore 649
|
||||||
|
|
||||||
|
private static readonly TimeSpan MinimumDelay = TimeSpan.FromSeconds(15);
|
||||||
|
private TimeSpan _nextBark;
|
||||||
|
|
||||||
|
|
||||||
|
private static List<string> slogans = new List<string>
|
||||||
|
{
|
||||||
|
"Come try my great products today!",
|
||||||
|
"More value for the way you live.",
|
||||||
|
"Quality you'd expect at prices you wouldn't.",
|
||||||
|
"The right stuff. The right price.",
|
||||||
|
};
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
if(_timeMan.CurTime < _nextBark)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var rngState = GenSeed();
|
||||||
|
_nextBark = _timeMan.CurTime + MinimumDelay + TimeSpan.FromSeconds(Random01(ref rngState) * 10);
|
||||||
|
|
||||||
|
var pick = (int)Math.Round(Random01(ref rngState) * (slogans.Count - 1));
|
||||||
|
_chatMan.EntitySay(SelfEntity, slogans[pick]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint GenSeed()
|
||||||
|
{
|
||||||
|
return RotateRight((uint)_timeMan.CurTick.GetHashCode(), 11) ^ (uint)SelfEntity.Uid.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint RotateRight(uint n, int s)
|
||||||
|
{
|
||||||
|
return (n << (32 - s)) | (n >> s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float Random01(ref uint state)
|
||||||
|
{
|
||||||
|
DebugTools.Assert(state != 0);
|
||||||
|
|
||||||
|
//xorshift32
|
||||||
|
state ^= state << 13;
|
||||||
|
state ^= state >> 17;
|
||||||
|
state ^= state << 5;
|
||||||
|
return state / (float)uint.MaxValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
249
Content.Server/AI/WanderProcessor.cs
Normal file
249
Content.Server/AI/WanderProcessor.cs
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Server.GameObjects.Components.Movement;
|
||||||
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
|
using Content.Server.Interfaces.Chat;
|
||||||
|
using Content.Shared.Physics;
|
||||||
|
using Robust.Server.AI;
|
||||||
|
using Robust.Server.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Interfaces.Physics;
|
||||||
|
using Robust.Shared.Interfaces.Timing;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Server.AI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Designed to control a mob. The mob will wander around, then idle at a the destination for awhile.
|
||||||
|
/// </summary>
|
||||||
|
[AiLogicProcessor("Wander")]
|
||||||
|
class WanderProcessor : AiLogicProcessor
|
||||||
|
{
|
||||||
|
#pragma warning disable 649
|
||||||
|
[Dependency] private readonly IPhysicsManager _physMan;
|
||||||
|
[Dependency] private readonly IServerEntityManager _entMan;
|
||||||
|
[Dependency] private readonly IGameTiming _timeMan;
|
||||||
|
[Dependency] private readonly IEntitySystemManager _entSysMan;
|
||||||
|
[Dependency] private readonly IChatManager _chatMan;
|
||||||
|
#pragma warning restore 649
|
||||||
|
|
||||||
|
private static readonly TimeSpan IdleTimeSpan = TimeSpan.FromSeconds(1);
|
||||||
|
private static readonly TimeSpan WalkingTimeout = TimeSpan.FromSeconds(3);
|
||||||
|
private static readonly TimeSpan DisabledTimeout = TimeSpan.FromSeconds(10);
|
||||||
|
|
||||||
|
private static List<string> _normalAssistantConversation = new List<string>
|
||||||
|
{
|
||||||
|
"stat me",
|
||||||
|
"roll it easy!",
|
||||||
|
"waaaaaagh!!!",
|
||||||
|
"red wonz go fasta",
|
||||||
|
"FOR TEH EMPRAH",
|
||||||
|
"lol2cat",
|
||||||
|
"dem dwarfs man, dem dwarfs",
|
||||||
|
"SPESS MAHREENS",
|
||||||
|
"hwee did eet fhor khayosss",
|
||||||
|
"lifelike texture ;_;",
|
||||||
|
"luv can bloooom",
|
||||||
|
"PACKETS!!!",
|
||||||
|
"SARAH HALE DID IT!!!",
|
||||||
|
"Don't tell Chase",
|
||||||
|
"not so tough now huh",
|
||||||
|
"WERE NOT BAY!!",
|
||||||
|
"IF YOU DONT LIKE THE CYBORGS OR SLIMES WHY DONT YU O JUST MAKE YORE OWN!",
|
||||||
|
"DONT TALK TO ME ABOUT BALANCE!!!!",
|
||||||
|
"YOU AR JUS LAZY AND DUMB JAMITORS AND SERVICE ROLLS",
|
||||||
|
"BLAME HOSHI!!!",
|
||||||
|
"ARRPEE IZ DED!!!",
|
||||||
|
"THERE ALL JUS MEATAFRIENDS!",
|
||||||
|
"SOTP MESING WITH THE ROUNS SHITMAN!!!",
|
||||||
|
"SKELINGTON IS 4 SHITERS!",
|
||||||
|
"MOMMSI R THE WURST SCUM!!",
|
||||||
|
"How do we engiener=",
|
||||||
|
"try to live freely and automatically good bye",
|
||||||
|
"why woud i take a pin pointner??",
|
||||||
|
"How do I set up the. SHow do I set u p the Singu. how I the scrungularity????",
|
||||||
|
};
|
||||||
|
|
||||||
|
private const float MaxWalkDistance = 3; // meters
|
||||||
|
private const float AdditionalIdleTime = 2; // 0 to this many more seconds
|
||||||
|
|
||||||
|
private FsmState _CurrentState;
|
||||||
|
private TimeSpan _startStateTime;
|
||||||
|
private Vector2 _walkTargetPos;
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
if (SelfEntity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ProcessState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ProcessState()
|
||||||
|
{
|
||||||
|
switch (_CurrentState)
|
||||||
|
{
|
||||||
|
case FsmState.None:
|
||||||
|
_CurrentState = FsmState.Idle;
|
||||||
|
break;
|
||||||
|
case FsmState.Idle:
|
||||||
|
IdleState();
|
||||||
|
break;
|
||||||
|
case FsmState.Walking:
|
||||||
|
WalkingState();
|
||||||
|
break;
|
||||||
|
case FsmState.Disabled:
|
||||||
|
DisabledState();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void IdlePositiveEdge(ref uint rngState)
|
||||||
|
{
|
||||||
|
_startStateTime = _timeMan.CurTime + IdleTimeSpan + TimeSpan.FromSeconds(Random01(ref rngState) * AdditionalIdleTime);
|
||||||
|
_CurrentState = FsmState.Idle;
|
||||||
|
|
||||||
|
EmitProfanity(ref rngState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void IdleState()
|
||||||
|
{
|
||||||
|
if (!ActionBlockerSystem.CanMove(SelfEntity))
|
||||||
|
{
|
||||||
|
DisabledPositiveEdge();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_timeMan.CurTime < _startStateTime + IdleTimeSpan)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var entWorldPos = SelfEntity.Transform.WorldPosition;
|
||||||
|
|
||||||
|
if (SelfEntity.TryGetComponent<BoundingBoxComponent>(out var bounds))
|
||||||
|
entWorldPos = bounds.WorldAABB.Center;
|
||||||
|
|
||||||
|
var rngState = GenSeed();
|
||||||
|
for (var i = 0; i < 3; i++) // you get 3 chances to find a place to walk
|
||||||
|
{
|
||||||
|
var dir = new Vector2(Random01(ref rngState) * 2 - 1, Random01(ref rngState) *2 -1);
|
||||||
|
var ray = new Ray(entWorldPos, dir, (int) CollisionGroup.Grid);
|
||||||
|
var rayResult = _physMan.IntersectRay(ray, MaxWalkDistance, SelfEntity);
|
||||||
|
|
||||||
|
if (rayResult.DidHitObject && rayResult.Distance > 1) // hit an impassable object
|
||||||
|
{
|
||||||
|
// set the new position back from the wall a bit
|
||||||
|
_walkTargetPos = entWorldPos + dir * (rayResult.Distance - 0.5f);
|
||||||
|
WalkingPositiveEdge();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rayResult.DidHitObject) // hit nothing (path clear)
|
||||||
|
{
|
||||||
|
_walkTargetPos = dir * MaxWalkDistance;
|
||||||
|
WalkingPositiveEdge();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// can't find clear spot, do nothing, sleep longer
|
||||||
|
_startStateTime = _timeMan.CurTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WalkingPositiveEdge()
|
||||||
|
{
|
||||||
|
_startStateTime = _timeMan.CurTime;
|
||||||
|
_CurrentState = FsmState.Walking;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WalkingState()
|
||||||
|
{
|
||||||
|
var rngState = GenSeed();
|
||||||
|
if (_timeMan.CurTime > _startStateTime + WalkingTimeout) // walked too long, go idle
|
||||||
|
{
|
||||||
|
IdlePositiveEdge(ref rngState);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetDiff = _walkTargetPos - SelfEntity.Transform.WorldPosition;
|
||||||
|
|
||||||
|
if (targetDiff.LengthSquared < 0.1) // close enough
|
||||||
|
{
|
||||||
|
// stop walking
|
||||||
|
if (SelfEntity.TryGetComponent<AiControllerComponent>(out var mover))
|
||||||
|
{
|
||||||
|
mover.VelocityDir = Vector2.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdlePositiveEdge(ref rngState);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// continue walking
|
||||||
|
if (SelfEntity.TryGetComponent<AiControllerComponent>(out var moverTwo))
|
||||||
|
{
|
||||||
|
moverTwo.VelocityDir = targetDiff.Normalized;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisabledPositiveEdge()
|
||||||
|
{
|
||||||
|
_startStateTime = _timeMan.CurTime;
|
||||||
|
_CurrentState = FsmState.Disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisabledState()
|
||||||
|
{
|
||||||
|
if(_timeMan.CurTime < _startStateTime + DisabledTimeout)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ActionBlockerSystem.CanMove(SelfEntity))
|
||||||
|
{
|
||||||
|
var rngState = GenSeed();
|
||||||
|
IdlePositiveEdge(ref rngState);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
DisabledPositiveEdge();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EmitProfanity(ref uint rngState)
|
||||||
|
{
|
||||||
|
if(Random01(ref rngState) < 0.5f)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var pick = (int) Math.Round(Random01(ref rngState) * (_normalAssistantConversation.Count - 1));
|
||||||
|
_chatMan.EntitySay(SelfEntity, _normalAssistantConversation[pick]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint GenSeed()
|
||||||
|
{
|
||||||
|
return RotateRight((uint)_timeMan.CurTick.GetHashCode(), 11) ^ (uint)SelfEntity.Uid.GetHashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint RotateRight(uint n, int s)
|
||||||
|
{
|
||||||
|
return (n << (32 - s)) | (n >> s);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float Random01(ref uint state)
|
||||||
|
{
|
||||||
|
DebugTools.Assert(state != 0);
|
||||||
|
|
||||||
|
//xorshift32
|
||||||
|
state ^= state << 13;
|
||||||
|
state ^= state >> 17;
|
||||||
|
state ^= state << 5;
|
||||||
|
return state / (float)uint.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum FsmState
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Idle,
|
||||||
|
Walking,
|
||||||
|
Disabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,15 @@
|
|||||||
using Content.Server.Interfaces.GameObjects.Components.Movement;
|
using Content.Server.Interfaces.GameObjects.Components.Movement;
|
||||||
using Robust.Server.AI;
|
using Robust.Server.AI;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Movement
|
namespace Content.Server.GameObjects.Components.Movement
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent, ComponentReference(typeof(IMoverComponent))]
|
||||||
public class AiControllerComponent : Component, IMoverComponent
|
public class AiControllerComponent : Component, IMoverComponent
|
||||||
{
|
{
|
||||||
private string _logicName;
|
private string _logicName;
|
||||||
@@ -13,15 +17,37 @@ namespace Content.Server.GameObjects.Components.Movement
|
|||||||
|
|
||||||
public override string Name => "AiController";
|
public override string Name => "AiController";
|
||||||
|
|
||||||
public string LogicName => _logicName;
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public string LogicName
|
||||||
|
{
|
||||||
|
get => _logicName;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_logicName = value;
|
||||||
|
Processor = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public AiLogicProcessor Processor { get; set; }
|
public AiLogicProcessor Processor { get; set; }
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public float VisionRadius
|
public float VisionRadius
|
||||||
{
|
{
|
||||||
get => _visionRadius;
|
get => _visionRadius;
|
||||||
set => _visionRadius = value;
|
set => _visionRadius = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
// This component requires a physics component.
|
||||||
|
if (!Owner.HasComponent<PhysicsComponent>())
|
||||||
|
Owner.AddComponent<PhysicsComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override void ExposeData(ObjectSerializer serializer)
|
public override void ExposeData(ObjectSerializer serializer)
|
||||||
{
|
{
|
||||||
base.ExposeData(serializer);
|
base.ExposeData(serializer);
|
||||||
@@ -29,5 +55,34 @@ namespace Content.Server.GameObjects.Components.Movement
|
|||||||
serializer.DataField(ref _logicName, "logic", null);
|
serializer.DataField(ref _logicName, "logic", null);
|
||||||
serializer.DataField(ref _visionRadius, "vision", 8.0f);
|
serializer.DataField(ref _visionRadius, "vision", 8.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Movement speed (m/s) that the entity walks.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float WalkMoveSpeed { get; set; } = 4.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Movement speed (m/s) that the entity sprints.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float SprintMoveSpeed { get; set; } = 10.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is the entity Sprinting (running)?
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public bool Sprinting { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculated linear velocity direction of the entity.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public Vector2 VelocityDir { get; set; }
|
||||||
|
|
||||||
|
public GridCoordinates LastPosition { get; set; }
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float StepSoundDistance { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.GameObjects.Components.Movement;
|
using Content.Server.GameObjects.Components.Movement;
|
||||||
|
using Content.Server.Interfaces.GameObjects.Components.Movement;
|
||||||
using Robust.Server.AI;
|
using Robust.Server.AI;
|
||||||
|
using Robust.Server.Interfaces.Console;
|
||||||
|
using Robust.Server.Interfaces.Player;
|
||||||
using Robust.Server.Interfaces.Timing;
|
using Robust.Server.Interfaces.Timing;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.GameObjects.Systems;
|
using Robust.Shared.GameObjects.Systems;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
using Robust.Shared.Interfaces.Reflection;
|
using Robust.Shared.Interfaces.Reflection;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
@@ -12,17 +16,23 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
{
|
{
|
||||||
internal class AiSystem : EntitySystem
|
internal class AiSystem : EntitySystem
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, Type> _processorTypes = new Dictionary<string, Type>();
|
#pragma warning disable 649
|
||||||
private IPauseManager _pauseManager;
|
[Dependency] private readonly IPauseManager _pauseManager;
|
||||||
|
[Dependency] private readonly IDynamicTypeFactory _typeFactory;
|
||||||
|
[Dependency] private readonly IReflectionManager _reflectionManager;
|
||||||
|
#pragma warning restore 649
|
||||||
|
|
||||||
public AiSystem()
|
private readonly Dictionary<string, Type> _processorTypes = new Dictionary<string, Type>();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
// register entity query
|
// register entity query
|
||||||
EntityQuery = new TypeEntityQuery(typeof(AiControllerComponent));
|
EntityQuery = new TypeEntityQuery(typeof(AiControllerComponent));
|
||||||
_pauseManager = IoCManager.Resolve<IPauseManager>();
|
|
||||||
|
|
||||||
var reflectionMan = IoCManager.Resolve<IReflectionManager>();
|
var processors = _reflectionManager.GetAllChildren<AiLogicProcessor>();
|
||||||
var processors = reflectionMan.GetAllChildren<AiLogicProcessor>();
|
|
||||||
foreach (var processor in processors)
|
foreach (var processor in processors)
|
||||||
{
|
{
|
||||||
var att = (AiLogicProcessorAttribute)Attribute.GetCustomAttribute(processor, typeof(AiLogicProcessorAttribute));
|
var att = (AiLogicProcessorAttribute)Attribute.GetCustomAttribute(processor, typeof(AiLogicProcessorAttribute));
|
||||||
@@ -33,6 +43,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
var entities = EntityManager.GetEntities(EntityQuery);
|
var entities = EntityManager.GetEntities(EntityQuery);
|
||||||
@@ -61,11 +72,45 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
{
|
{
|
||||||
if (_processorTypes.TryGetValue(name, out var type))
|
if (_processorTypes.TryGetValue(name, out var type))
|
||||||
{
|
{
|
||||||
return (AiLogicProcessor)Activator.CreateInstance(type);
|
return (AiLogicProcessor)_typeFactory.CreateInstance(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
// processor needs to inherit AiLogicProcessor, and needs an AiLogicProcessorAttribute to define the YAML name
|
// processor needs to inherit AiLogicProcessor, and needs an AiLogicProcessorAttribute to define the YAML name
|
||||||
throw new ArgumentException($"Processor type {name} could not be found.", nameof(name));
|
throw new ArgumentException($"Processor type {name} could not be found.", nameof(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class AddAiCommand : IClientCommand
|
||||||
|
{
|
||||||
|
public string Command => "addai";
|
||||||
|
public string Description => "Add an ai component with a given processor to an entity.";
|
||||||
|
public string Help => "addai <processorId> <entityId>";
|
||||||
|
public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
|
||||||
|
{
|
||||||
|
if(args.Length != 2)
|
||||||
|
{
|
||||||
|
shell.SendText(player, "Wrong number of args.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var processorId = args[0];
|
||||||
|
var entId = new EntityUid(int.Parse(args[1]));
|
||||||
|
var ent = IoCManager.Resolve<IEntityManager>().GetEntity(entId);
|
||||||
|
|
||||||
|
if (ent.HasComponent<AiControllerComponent>())
|
||||||
|
{
|
||||||
|
shell.SendText(player, "Entity already has an AI component.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent.HasComponent<IMoverComponent>())
|
||||||
|
{
|
||||||
|
ent.RemoveComponent<IMoverComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
var comp = ent.AddComponent<AiControllerComponent>();
|
||||||
|
comp.LogicName = processorId;
|
||||||
|
shell.SendText(player, "AI component added.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
EntityQuery = new TypeEntityQuery(typeof(PlayerInputMoverComponent));
|
EntityQuery = new TypeEntityQuery(typeof(IMoverComponent));
|
||||||
|
|
||||||
var moveUpCmdHandler = InputCmdHandler.FromDelegate(
|
var moveUpCmdHandler = InputCmdHandler.FromDelegate(
|
||||||
session => HandleDirChange(session, Direction.North, true),
|
session => HandleDirChange(session, Direction.North, true),
|
||||||
@@ -117,14 +117,14 @@ namespace Content.Server.GameObjects.EntitySystems
|
|||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
var mover = entity.GetComponent<PlayerInputMoverComponent>();
|
var mover = entity.GetComponent<IMoverComponent>();
|
||||||
var physics = entity.GetComponent<PhysicsComponent>();
|
var physics = entity.GetComponent<PhysicsComponent>();
|
||||||
|
|
||||||
UpdateKinematics(entity.Transform, mover, physics);
|
UpdateKinematics(entity.Transform, mover, physics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateKinematics(ITransformComponent transform, PlayerInputMoverComponent mover, PhysicsComponent physics)
|
private void UpdateKinematics(ITransformComponent transform, IMoverComponent mover, PhysicsComponent physics)
|
||||||
{
|
{
|
||||||
if (mover.VelocityDir.LengthSquared < 0.001 || !ActionBlockerSystem.CanMove(mover.Owner))
|
if (mover.VelocityDir.LengthSquared < 0.001 || !ActionBlockerSystem.CanMove(mover.Owner))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using Robust.Shared.Interfaces.GameObjects;
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
|
||||||
namespace Content.Server.Interfaces.GameObjects.Components.Movement
|
namespace Content.Server.Interfaces.GameObjects.Components.Movement
|
||||||
{
|
{
|
||||||
@@ -6,6 +8,28 @@ namespace Content.Server.Interfaces.GameObjects.Components.Movement
|
|||||||
// There can only be one.
|
// There can only be one.
|
||||||
public interface IMoverComponent : IComponent
|
public interface IMoverComponent : IComponent
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Movement speed (m/s) that the entity walks.
|
||||||
|
/// </summary>
|
||||||
|
float WalkMoveSpeed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Movement speed (m/s) that the entity sprints.
|
||||||
|
/// </summary>
|
||||||
|
float SprintMoveSpeed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is the entity Sprinting (running)?
|
||||||
|
/// </summary>
|
||||||
|
bool Sprinting { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Calculated linear velocity direction of the entity.
|
||||||
|
/// </summary>
|
||||||
|
Vector2 VelocityDir { get; }
|
||||||
|
|
||||||
|
GridCoordinates LastPosition { get; set; }
|
||||||
|
|
||||||
|
float StepSoundDistance { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
drawdepth: WallMountedItems
|
drawdepth: WallMountedItems
|
||||||
texture: Buildings/TurrTop.png
|
texture: Buildings/TurrTop.png
|
||||||
|
directional: false
|
||||||
- type: AiController
|
- type: AiController
|
||||||
logic: AimShootLife
|
logic: AimShootLife
|
||||||
vision: 6.0
|
vision: 6.0
|
||||||
@@ -29,6 +30,7 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
drawdepth: WallMountedItems
|
drawdepth: WallMountedItems
|
||||||
texture: Buildings/TurrLamp.png
|
texture: Buildings/TurrLamp.png
|
||||||
|
directional: false
|
||||||
- type: AiController
|
- type: AiController
|
||||||
logic: AimShootLife
|
logic: AimShootLife
|
||||||
vision: 6.0
|
vision: 6.0
|
||||||
|
|||||||
Reference in New Issue
Block a user