diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index 7e6901fabd..1141f3fb7d 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -110,6 +110,9 @@ namespace Content.Client factory.Register(); + factory.RegisterIgnore("AiController"); + factory.RegisterIgnore("PlayerInputMover"); + IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); diff --git a/Content.Server/AI/AimShootLifeProcessor.cs b/Content.Server/AI/AimShootLifeProcessor.cs index 1c63fc0aa4..da1426b040 100644 --- a/Content.Server/AI/AimShootLifeProcessor.cs +++ b/Content.Server/AI/AimShootLifeProcessor.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Content.Server.Interfaces.GameObjects.Components.Movement; using SS14.Server.AI; using SS14.Server.GameObjects; using SS14.Server.Interfaces.GameObjects; diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj index e82e38bd97..1ace32f240 100644 --- a/Content.Server/Content.Server.csproj +++ b/Content.Server/Content.Server.csproj @@ -87,6 +87,8 @@ + + @@ -110,11 +112,13 @@ + + @@ -132,6 +136,7 @@ + @@ -188,5 +193,4 @@ - diff --git a/Content.Server/EntryPoint.cs b/Content.Server/EntryPoint.cs index 950fa6f629..3024e3ce61 100644 --- a/Content.Server/EntryPoint.cs +++ b/Content.Server/EntryPoint.cs @@ -47,6 +47,8 @@ using Content.Shared.Interfaces; using SS14.Server.Interfaces.ServerStatus; using SS14.Shared.Timing; using Content.Server.GameObjects.Components.Destructible; +using Content.Server.GameObjects.Components.Movement; +using Content.Server.Interfaces.GameObjects.Components.Movement; namespace Content.Server { @@ -143,6 +145,11 @@ namespace Content.Server factory.RegisterIgnore("IconSmooth"); factory.RegisterIgnore("SubFloorHide"); + factory.Register(); + factory.RegisterReference(); + + factory.Register(); + IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); diff --git a/Content.Server/GameObjects/Components/Mobs/DamageStates.cs b/Content.Server/GameObjects/Components/Mobs/DamageStates.cs index f50e6b6269..8329b3f701 100644 --- a/Content.Server/GameObjects/Components/Mobs/DamageStates.cs +++ b/Content.Server/GameObjects/Components/Mobs/DamageStates.cs @@ -1,4 +1,5 @@ -using Content.Server.GameObjects.EntitySystems; +using Content.Server.GameObjects.Components.Movement; +using Content.Server.GameObjects.EntitySystems; using Content.Shared.GameObjects.Components.Mobs; using SS14.Server.GameObjects; using SS14.Shared.Interfaces.GameObjects; diff --git a/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs b/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs new file mode 100644 index 0000000000..10906d3f48 --- /dev/null +++ b/Content.Server/GameObjects/Components/Movement/AiControllerComponent.cs @@ -0,0 +1,32 @@ +using Content.Server.Interfaces.GameObjects.Components.Movement; +using SS14.Server.AI; +using SS14.Shared.GameObjects; +using SS14.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.Movement +{ + public class AiControllerComponent : Component, IMoverComponent + { + private string _logicName; + private float _visionRadius; + + public override string Name => "AiController"; + + public string LogicName => _logicName; + public AiLogicProcessor Processor { get; set; } + + public float VisionRadius + { + get => _visionRadius; + set => _visionRadius = value; + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _logicName, "logic", null); + serializer.DataField(ref _visionRadius, "vision", 8.0f); + } + } +} diff --git a/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs b/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs new file mode 100644 index 0000000000..879b02adc9 --- /dev/null +++ b/Content.Server/GameObjects/Components/Movement/PlayerInputMoverComponent.cs @@ -0,0 +1,120 @@ +using Content.Server.Interfaces.GameObjects.Components.Movement; +using SS14.Server.GameObjects; +using SS14.Shared.GameObjects; +using SS14.Shared.Log; +using SS14.Shared.Maths; +using SS14.Shared.Serialization; +using SS14.Shared.ViewVariables; + +namespace Content.Server.GameObjects.Components.Movement +{ + /// + /// Moves the entity based on input from a KeyBindingInputComponent. + /// + public class PlayerInputMoverComponent : Component, IMoverComponent + { + private bool _movingUp; + private bool _movingDown; + private bool _movingLeft; + private bool _movingRight; + + /// + public override string Name => "PlayerInputMover"; + + /// + /// Movement speed (m/s) that the entity walks. + /// + [ViewVariables(VVAccess.ReadWrite)] + public float WalkMoveSpeed { get; set; } = 4.0f; + + /// + /// Movement speed (m/s) that the entity sprints. + /// + [ViewVariables(VVAccess.ReadWrite)] + public float SprintMoveSpeed { get; set; } = 10.0f; + + /// + /// Is the entity Sprinting (running)? + /// + [ViewVariables] + public bool Sprinting { get; set; } + + /// + /// Calculated linear velocity direction of the entity. + /// + [ViewVariables] + public Vector2 VelocityDir { get; private set; } + + /// + /// Blocks entity's movement + /// + [ViewVariables] + public bool Disabled { get; set; } = false; + + /// + public override void OnAdd() + { + // This component requires that the entity has a PhysicsComponent. + if (!Owner.HasComponent()) + Logger.Error($"[ECS] {Owner.Prototype.Name} - {nameof(PlayerInputMoverComponent)} requires {nameof(PhysicsComponent)}. "); + + base.OnAdd(); + } + + /// + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataReadWriteFunction("wspd", 4.0f, value => WalkMoveSpeed = value, () => WalkMoveSpeed); + serializer.DataReadWriteFunction("rspd", 10.0f, value => SprintMoveSpeed = value, () => SprintMoveSpeed); + + // The velocity and moving directions is usually set from player or AI input, + // so we don't want to save/load these derived fields. + } + + /// + /// Toggles one of the four cardinal directions. Each of the four directions are + /// composed into a single direction vector, . Enabling + /// opposite directions will cancel each other out, resulting in no direction. + /// + /// Direction to toggle. + /// If the direction is active. + public void SetVelocityDirection(Direction direction, bool enabled) + { + switch (direction) + { + case Direction.East: + _movingRight = enabled; + break; + case Direction.North: + _movingUp = enabled; + break; + case Direction.West: + _movingLeft = enabled; + break; + case Direction.South: + _movingDown = enabled; + break; + } + + // key directions are in screen coordinates + // _moveDir is in world coordinates + // if the camera is moved, this needs to be changed + + var x = 0; + x -= _movingLeft ? 1 : 0; + x += _movingRight ? 1 : 0; + + var y = 0; + y -= _movingDown ? 1 : 0; + y += _movingUp ? 1 : 0; + + VelocityDir = new Vector2(x, y); + + // can't normalize zero length vector + if (VelocityDir.LengthSquared > 1.0e-6) + VelocityDir = VelocityDir.Normalized; + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/AiSystem.cs b/Content.Server/GameObjects/EntitySystems/AiSystem.cs new file mode 100644 index 0000000000..ff9f6ae310 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/AiSystem.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using Content.Server.GameObjects.Components.Movement; +using SS14.Server.AI; +using SS14.Server.Interfaces.Timing; +using SS14.Shared.GameObjects; +using SS14.Shared.GameObjects.Systems; +using SS14.Shared.Interfaces.Reflection; +using SS14.Shared.IoC; + +namespace Content.Server.GameObjects.EntitySystems +{ + internal class AiSystem : EntitySystem + { + private readonly Dictionary _processorTypes = new Dictionary(); + private IPauseManager _pauseManager; + + public AiSystem() + { + // register entity query + EntityQuery = new TypeEntityQuery(typeof(AiControllerComponent)); + _pauseManager = IoCManager.Resolve(); + + var reflectionMan = IoCManager.Resolve(); + var processors = reflectionMan.GetAllChildren(); + foreach (var processor in processors) + { + var att = (AiLogicProcessorAttribute)Attribute.GetCustomAttribute(processor, typeof(AiLogicProcessorAttribute)); + if (att != null) + { + _processorTypes.Add(att.SerializeName, processor); + } + } + } + + public override void Update(float frameTime) + { + var entities = EntityManager.GetEntities(EntityQuery); + foreach (var entity in entities) + { + if (_pauseManager.IsEntityPaused(entity)) + { + continue; + } + + var aiComp = entity.GetComponent(); + if (aiComp.Processor == null) + { + aiComp.Processor = CreateProcessor(aiComp.LogicName); + aiComp.Processor.SelfEntity = entity; + aiComp.Processor.VisionRadius = aiComp.VisionRadius; + } + + var processor = aiComp.Processor; + + processor.Update(frameTime); + } + } + + private AiLogicProcessor CreateProcessor(string name) + { + if (_processorTypes.TryGetValue(name, out var type)) + { + return (AiLogicProcessor)Activator.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)); + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/MoverSystem.cs b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs new file mode 100644 index 0000000000..8aa52ecf1d --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs @@ -0,0 +1,153 @@ +using Content.Server.GameObjects.Components.Movement; +using Content.Server.Interfaces.GameObjects.Components.Movement; +using JetBrains.Annotations; +using SS14.Server.GameObjects; +using SS14.Server.GameObjects.EntitySystems; +using SS14.Server.Interfaces.Player; +using SS14.Server.Interfaces.Timing; +using SS14.Shared.GameObjects; +using SS14.Shared.GameObjects.Systems; +using SS14.Shared.Input; +using SS14.Shared.Interfaces.GameObjects.Components; +using SS14.Shared.IoC; +using SS14.Shared.Maths; +using SS14.Shared.Players; + +namespace Content.Server.GameObjects.EntitySystems +{ + [UsedImplicitly] + internal class MoverSystem : EntitySystem + { +#pragma warning disable 649 + [Dependency] + private IPauseManager _pauseManager; +#pragma warning restore 649 + + /// + public override void Initialize() + { + IoCManager.InjectDependencies(this); + + EntityQuery = new TypeEntityQuery(typeof(PlayerInputMoverComponent)); + + var moveUpCmdHandler = InputCmdHandler.FromDelegate( + session => HandleDirChange(session, Direction.North, true), + session => HandleDirChange(session, Direction.North, false)); + var moveLeftCmdHandler = InputCmdHandler.FromDelegate( + session => HandleDirChange(session, Direction.West, true), + session => HandleDirChange(session, Direction.West, false)); + var moveRightCmdHandler = InputCmdHandler.FromDelegate( + session => HandleDirChange(session, Direction.East, true), + session => HandleDirChange(session, Direction.East, false)); + var moveDownCmdHandler = InputCmdHandler.FromDelegate( + session => HandleDirChange(session, Direction.South, true), + session => HandleDirChange(session, Direction.South, false)); + var runCmdHandler = InputCmdHandler.FromDelegate( + session => HandleRunChange(session, true), + session => HandleRunChange(session, false)); + + var input = EntitySystemManager.GetEntitySystem(); + + input.BindMap.BindFunction(EngineKeyFunctions.MoveUp, moveUpCmdHandler); + input.BindMap.BindFunction(EngineKeyFunctions.MoveLeft, moveLeftCmdHandler); + input.BindMap.BindFunction(EngineKeyFunctions.MoveRight, moveRightCmdHandler); + input.BindMap.BindFunction(EngineKeyFunctions.MoveDown, moveDownCmdHandler); + input.BindMap.BindFunction(EngineKeyFunctions.Run, runCmdHandler); + + SubscribeEvent(PlayerAttached); + SubscribeEvent(PlayerDetached); + } + + private static void PlayerAttached(object sender, PlayerAttachSystemMessage ev) + { + if (ev.Entity.HasComponent()) + { + ev.Entity.RemoveComponent(); + } + ev.Entity.AddComponent(); + } + + private static void PlayerDetached(object sender, PlayerDetachedSystemMessage ev) + { + ev.Entity.RemoveComponent(); + } + + /// + public override void Shutdown() + { + if (EntitySystemManager.TryGetEntitySystem(out InputSystem input)) + { + input.BindMap.UnbindFunction(EngineKeyFunctions.MoveUp); + input.BindMap.UnbindFunction(EngineKeyFunctions.MoveLeft); + input.BindMap.UnbindFunction(EngineKeyFunctions.MoveRight); + input.BindMap.UnbindFunction(EngineKeyFunctions.MoveDown); + input.BindMap.UnbindFunction(EngineKeyFunctions.Run); + } + + base.Shutdown(); + } + + /// + public override void Update(float frameTime) + { + foreach (var entity in RelevantEntities) + { + if (_pauseManager.IsEntityPaused(entity)) + { + continue; + } + var mover = entity.GetComponent(); + var physics = entity.GetComponent(); + + UpdateKinematics(entity.Transform, mover, physics); + } + } + + private static void UpdateKinematics(ITransformComponent transform, PlayerInputMoverComponent mover, PhysicsComponent physics) + { + if (mover.VelocityDir.LengthSquared < 0.001 || mover.Disabled) + { + if (physics.LinearVelocity != Vector2.Zero) + physics.LinearVelocity = Vector2.Zero; + } + else + { + physics.LinearVelocity = mover.VelocityDir * (mover.Sprinting ? mover.SprintMoveSpeed : mover.WalkMoveSpeed); + transform.LocalRotation = mover.VelocityDir.GetDir().ToAngle(); + } + } + + private static void HandleDirChange(ICommonSession session, Direction dir, bool state) + { + if(!TryGetAttachedComponent(session as IPlayerSession, out PlayerInputMoverComponent moverComp)) + return; + + moverComp.SetVelocityDirection(dir, state); + } + + private static void HandleRunChange(ICommonSession session, bool running) + { + if(!TryGetAttachedComponent(session as IPlayerSession, out PlayerInputMoverComponent moverComp)) + return; + + moverComp.Sprinting = running; + } + + private static bool TryGetAttachedComponent(IPlayerSession session, out T component) + where T: Component + { + component = default; + + var ent = session.AttachedEntity; + + if (ent == null || !ent.IsValid()) + return false; + + if (!ent.TryGetComponent(out T comp)) + return false; + + component = comp; + return true; + } + } +} diff --git a/Content.Server/Interfaces/GameObjects/Components/Movement/IMoverComponent.cs b/Content.Server/Interfaces/GameObjects/Components/Movement/IMoverComponent.cs new file mode 100644 index 0000000000..ba0776d581 --- /dev/null +++ b/Content.Server/Interfaces/GameObjects/Components/Movement/IMoverComponent.cs @@ -0,0 +1,11 @@ +using SS14.Shared.Interfaces.GameObjects; + +namespace Content.Server.Interfaces.GameObjects.Components.Movement +{ + // Does nothing except ensure uniqueness between mover components. + // There can only be one. + public interface IMoverComponent : IComponent + { + + } +} diff --git a/RobustToolbox b/RobustToolbox index 9ff6a7ec02..fa15950b91 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 9ff6a7ec02fabadaa49a8269f8f2588a215f7cf4 +Subproject commit fa15950b916b37e7af6a7b36429be73bc8d856af