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")]
|
||||
class AimShootLifeProcessor : AiLogicProcessor
|
||||
{
|
||||
private readonly IPhysicsManager _physMan;
|
||||
private readonly IServerEntityManager _entMan;
|
||||
private readonly IGameTiming _timeMan;
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPhysicsManager _physMan;
|
||||
[Dependency] private readonly IServerEntityManager _entMan;
|
||||
[Dependency] private readonly IGameTiming _timeMan;
|
||||
#pragma warning restore 649
|
||||
|
||||
private readonly List<IEntity> _workList = new List<IEntity>();
|
||||
|
||||
@@ -32,16 +34,6 @@ namespace Content.Server.AI
|
||||
|
||||
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 />
|
||||
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 Robust.Server.AI;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Movement
|
||||
{
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, ComponentReference(typeof(IMoverComponent))]
|
||||
public class AiControllerComponent : Component, IMoverComponent
|
||||
{
|
||||
private string _logicName;
|
||||
@@ -13,15 +17,37 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
|
||||
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; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float VisionRadius
|
||||
{
|
||||
get => _visionRadius;
|
||||
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)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
@@ -29,5 +55,34 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
serializer.DataField(ref _logicName, "logic", null);
|
||||
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.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Movement;
|
||||
using Robust.Server.AI;
|
||||
using Robust.Server.Interfaces.Console;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Server.Interfaces.Timing;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Reflection;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
@@ -12,17 +16,23 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
{
|
||||
internal class AiSystem : EntitySystem
|
||||
{
|
||||
private readonly Dictionary<string, Type> _processorTypes = new Dictionary<string, Type>();
|
||||
private IPauseManager _pauseManager;
|
||||
#pragma warning disable 649
|
||||
[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
|
||||
EntityQuery = new TypeEntityQuery(typeof(AiControllerComponent));
|
||||
_pauseManager = IoCManager.Resolve<IPauseManager>();
|
||||
|
||||
var reflectionMan = IoCManager.Resolve<IReflectionManager>();
|
||||
var processors = reflectionMan.GetAllChildren<AiLogicProcessor>();
|
||||
var processors = _reflectionManager.GetAllChildren<AiLogicProcessor>();
|
||||
foreach (var processor in processors)
|
||||
{
|
||||
var att = (AiLogicProcessorAttribute)Attribute.GetCustomAttribute(processor, typeof(AiLogicProcessorAttribute));
|
||||
@@ -33,6 +43,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
var entities = EntityManager.GetEntities(EntityQuery);
|
||||
@@ -61,11 +72,45 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
{
|
||||
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
|
||||
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 />
|
||||
public override void Initialize()
|
||||
{
|
||||
EntityQuery = new TypeEntityQuery(typeof(PlayerInputMoverComponent));
|
||||
EntityQuery = new TypeEntityQuery(typeof(IMoverComponent));
|
||||
|
||||
var moveUpCmdHandler = InputCmdHandler.FromDelegate(
|
||||
session => HandleDirChange(session, Direction.North, true),
|
||||
@@ -117,14 +117,14 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var mover = entity.GetComponent<PlayerInputMoverComponent>();
|
||||
var mover = entity.GetComponent<IMoverComponent>();
|
||||
var physics = entity.GetComponent<PhysicsComponent>();
|
||||
|
||||
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))
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -6,6 +8,28 @@ namespace Content.Server.Interfaces.GameObjects.Components.Movement
|
||||
// There can only be one.
|
||||
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
|
||||
drawdepth: WallMountedItems
|
||||
texture: Buildings/TurrTop.png
|
||||
directional: false
|
||||
- type: AiController
|
||||
logic: AimShootLife
|
||||
vision: 6.0
|
||||
@@ -29,6 +30,7 @@
|
||||
- type: Sprite
|
||||
drawdepth: WallMountedItems
|
||||
texture: Buildings/TurrLamp.png
|
||||
directional: false
|
||||
- type: AiController
|
||||
logic: AimShootLife
|
||||
vision: 6.0
|
||||
|
||||
Reference in New Issue
Block a user