Sprite Movement working with AI movement (#33494)
* FINALLY * Update animals.yml
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Movement.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Controls the switching of motion and standing still animation
|
||||
/// </summary>
|
||||
public sealed class ClientSpriteMovementSystem : SharedSpriteMovementSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private EntityQuery<SpriteComponent> _spriteQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_spriteQuery = GetEntityQuery<SpriteComponent>();
|
||||
|
||||
SubscribeLocalEvent<SpriteMovementComponent, AfterAutoHandleStateEvent>(OnAfterAutoHandleState);
|
||||
}
|
||||
|
||||
private void OnAfterAutoHandleState(Entity<SpriteMovementComponent> ent, ref AfterAutoHandleStateEvent args)
|
||||
{
|
||||
if (!_spriteQuery.TryGetComponent(ent, out var sprite))
|
||||
return;
|
||||
|
||||
if (ent.Comp.IsMoving)
|
||||
{
|
||||
foreach (var (layer, state) in ent.Comp.MovementLayers)
|
||||
{
|
||||
sprite.LayerSetData(layer, state);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var (layer, state) in ent.Comp.NoMovementLayers)
|
||||
{
|
||||
sprite.LayerSetData(layer, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Movement.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// Handles setting sprite states based on whether an entity has movement input.
|
||||
/// </summary>
|
||||
public sealed class SpriteMovementSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
||||
private EntityQuery<SpriteComponent> _spriteQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<SpriteMovementComponent, MoveInputEvent>(OnSpriteMoveInput);
|
||||
_spriteQuery = GetEntityQuery<SpriteComponent>();
|
||||
}
|
||||
|
||||
private void OnSpriteMoveInput(EntityUid uid, SpriteMovementComponent component, ref MoveInputEvent args)
|
||||
{
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
var oldMoving = (SharedMoverController.GetNormalizedMovement(args.OldMovement) & MoveButtons.AnyDirection) != MoveButtons.None;
|
||||
var moving = (SharedMoverController.GetNormalizedMovement(args.Entity.Comp.HeldMoveButtons) & MoveButtons.AnyDirection) != MoveButtons.None;
|
||||
|
||||
if (oldMoving == moving || !_spriteQuery.TryGetComponent(uid, out var sprite))
|
||||
return;
|
||||
|
||||
if (moving)
|
||||
{
|
||||
foreach (var (layer, state) in component.MovementLayers)
|
||||
{
|
||||
sprite.LayerSetData(layer, state);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var (layer, state) in component.NoMovementLayers)
|
||||
{
|
||||
sprite.LayerSetData(layer, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
Content.Server/Movement/Systems/SpriteMovementSystem.cs
Normal file
7
Content.Server/Movement/Systems/SpriteMovementSystem.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Content.Shared.Movement.Systems;
|
||||
|
||||
namespace Content.Server.Movement.Systems;
|
||||
|
||||
public sealed class SpriteMovementSystem : SharedSpriteMovementSystem
|
||||
{
|
||||
}
|
||||
@@ -253,7 +253,7 @@ public sealed partial class NPCSteeringSystem
|
||||
|
||||
if (!targetCoordinates.IsValid(EntityManager))
|
||||
{
|
||||
SetDirection(mover, steering, Vector2.Zero);
|
||||
SetDirection(uid, mover, steering, Vector2.Zero);
|
||||
steering.Status = SteeringStatus.NoPath;
|
||||
return false;
|
||||
}
|
||||
@@ -263,7 +263,7 @@ public sealed partial class NPCSteeringSystem
|
||||
// Can't make it again.
|
||||
if (ourMap.MapId != targetMap.MapId)
|
||||
{
|
||||
SetDirection(mover, steering, Vector2.Zero);
|
||||
SetDirection(uid, mover, steering, Vector2.Zero);
|
||||
steering.Status = SteeringStatus.NoPath;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ using Content.Shared.Climbing.Systems;
|
||||
using Content.Shared.CombatMode;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.NPC;
|
||||
using Content.Shared.NPC.Components;
|
||||
@@ -207,6 +208,9 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
||||
if (EntityManager.TryGetComponent(uid, out InputMoverComponent? controller))
|
||||
{
|
||||
controller.CurTickSprintMovement = Vector2.Zero;
|
||||
|
||||
var ev = new SpriteMoveEvent(false);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
}
|
||||
|
||||
component.PathfindToken?.Cancel();
|
||||
@@ -270,7 +274,7 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDirection(InputMoverComponent component, NPCSteeringComponent steering, Vector2 value, bool clear = true)
|
||||
private void SetDirection(EntityUid uid, InputMoverComponent component, NPCSteeringComponent steering, Vector2 value, bool clear = true)
|
||||
{
|
||||
if (clear && value.Equals(Vector2.Zero))
|
||||
{
|
||||
@@ -282,6 +286,9 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
||||
component.CurTickSprintMovement = value;
|
||||
component.LastInputTick = _timing.CurTick;
|
||||
component.LastInputSubTick = ushort.MaxValue;
|
||||
|
||||
var ev = new SpriteMoveEvent(true);
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -297,7 +304,7 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
||||
{
|
||||
if (Deleted(steering.Coordinates.EntityId))
|
||||
{
|
||||
SetDirection(mover, steering, Vector2.Zero);
|
||||
SetDirection(uid, mover, steering, Vector2.Zero);
|
||||
steering.Status = SteeringStatus.NoPath;
|
||||
return;
|
||||
}
|
||||
@@ -305,14 +312,14 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
||||
// No path set from pathfinding or the likes.
|
||||
if (steering.Status == SteeringStatus.NoPath)
|
||||
{
|
||||
SetDirection(mover, steering, Vector2.Zero);
|
||||
SetDirection(uid, mover, steering, Vector2.Zero);
|
||||
return;
|
||||
}
|
||||
|
||||
// Can't move at all, just noop input.
|
||||
if (!mover.CanMove)
|
||||
{
|
||||
SetDirection(mover, steering, Vector2.Zero);
|
||||
SetDirection(uid, mover, steering, Vector2.Zero);
|
||||
steering.Status = SteeringStatus.NoPath;
|
||||
return;
|
||||
}
|
||||
@@ -341,7 +348,7 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
||||
|
||||
if (steering.CanSeek && !TrySeek(uid, mover, steering, body, xform, offsetRot, moveSpeed, interest, frameTime, ref forceSteer))
|
||||
{
|
||||
SetDirection(mover, steering, Vector2.Zero);
|
||||
SetDirection(uid, mover, steering, Vector2.Zero);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -354,7 +361,7 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
||||
|
||||
if (!forceSteer)
|
||||
{
|
||||
SetDirection(mover, steering, steering.LastSteerDirection, false);
|
||||
SetDirection(uid, mover, steering, steering.LastSteerDirection, false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -391,7 +398,7 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem
|
||||
|
||||
steering.LastSteerDirection = resultDirection;
|
||||
DebugTools.Assert(!float.IsNaN(resultDirection.X));
|
||||
SetDirection(mover, steering, resultDirection, false);
|
||||
SetDirection(uid, mover, steering, resultDirection, false);
|
||||
}
|
||||
|
||||
private EntityCoordinates GetCoordinates(PathPoly poly)
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Content.Shared.Movement.Components;
|
||||
/// <summary>
|
||||
/// Updates a sprite layer based on whether an entity is moving via input or not.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
|
||||
public sealed partial class SpriteMovementComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
@@ -19,4 +19,7 @@ public sealed partial class SpriteMovementComponent : Component
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Dictionary<string, PrototypeLayerData> NoMovementLayers = new();
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool IsMoving;
|
||||
}
|
||||
|
||||
15
Content.Shared/Movement/Events/SpriteMoveEvent.cs
Normal file
15
Content.Shared/Movement/Events/SpriteMoveEvent.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
namespace Content.Shared.Movement.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Raised on an entity whenever it should change movement sprite
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public readonly struct SpriteMoveEvent
|
||||
{
|
||||
public readonly bool IsMoving = false;
|
||||
|
||||
public SpriteMoveEvent(bool isMoving)
|
||||
{
|
||||
IsMoving = isMoving;
|
||||
}
|
||||
}
|
||||
@@ -96,6 +96,9 @@ namespace Content.Shared.Movement.Systems
|
||||
entity.Comp.HeldMoveButtons = buttons;
|
||||
RaiseLocalEvent(entity, ref moveEvent);
|
||||
Dirty(entity, entity.Comp);
|
||||
|
||||
var ev = new SpriteMoveEvent(entity.Comp.HeldMoveButtons != MoveButtons.None);
|
||||
RaiseLocalEvent(entity, ref ev);
|
||||
}
|
||||
|
||||
private void OnMoverHandleState(Entity<InputMoverComponent> entity, ref ComponentHandleState args)
|
||||
@@ -119,6 +122,9 @@ namespace Content.Shared.Movement.Systems
|
||||
var moveEvent = new MoveInputEvent(entity, entity.Comp.HeldMoveButtons);
|
||||
entity.Comp.HeldMoveButtons = state.HeldMoveButtons;
|
||||
RaiseLocalEvent(entity.Owner, ref moveEvent);
|
||||
|
||||
var ev = new SpriteMoveEvent(entity.Comp.HeldMoveButtons != MoveButtons.None);
|
||||
RaiseLocalEvent(entity, ref ev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Events;
|
||||
|
||||
namespace Content.Shared.Movement.Systems;
|
||||
|
||||
public abstract class SharedSpriteMovementSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SpriteMovementComponent, SpriteMoveEvent>(OnSpriteMoveInput);
|
||||
}
|
||||
|
||||
private void OnSpriteMoveInput(Entity<SpriteMovementComponent> ent, ref SpriteMoveEvent args)
|
||||
{
|
||||
if (ent.Comp.IsMoving == args.IsMoving)
|
||||
return;
|
||||
|
||||
ent.Comp.IsMoving = args.IsMoving;
|
||||
Dirty(ent);
|
||||
}
|
||||
}
|
||||
@@ -2283,24 +2283,10 @@
|
||||
# Code unique spider prototypes or combine them all into one spider and get a
|
||||
# random sprite state when you spawn it.
|
||||
- type: entity
|
||||
name: tarantula
|
||||
parent: [ SimpleMobBase, MobCombat ]
|
||||
id: MobGiantSpider
|
||||
description: Widely recognized to be the literal worst thing in existence.
|
||||
id: MobSpiderBase
|
||||
abstract: true
|
||||
components:
|
||||
- type: Sprite
|
||||
drawdepth: Mobs
|
||||
layers:
|
||||
- map: ["enum.DamageStateVisualLayers.Base", "movement"]
|
||||
state: tarantula
|
||||
sprite: Mobs/Animals/spider.rsi
|
||||
- type: SpriteMovement
|
||||
movementLayers:
|
||||
movement:
|
||||
state: tarantula-moving
|
||||
noMovementLayers:
|
||||
movement:
|
||||
state: tarantula
|
||||
- type: Physics
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
@@ -2313,14 +2299,6 @@
|
||||
- MobMask
|
||||
layer:
|
||||
- MobLayer
|
||||
- type: DamageStateVisuals
|
||||
states:
|
||||
Alive:
|
||||
Base: tarantula
|
||||
Critical:
|
||||
Base: tarantula_dead
|
||||
Dead:
|
||||
Base: tarantula_dead
|
||||
- type: Butcherable
|
||||
spawned:
|
||||
- id: FoodMeatSpider
|
||||
@@ -2332,15 +2310,6 @@
|
||||
thresholds:
|
||||
0: Alive
|
||||
90: Dead
|
||||
- type: MeleeWeapon
|
||||
altDisarm: false
|
||||
angle: 0
|
||||
animation: WeaponArcBite
|
||||
soundHit:
|
||||
path: /Audio/Effects/bite.ogg
|
||||
damage:
|
||||
types:
|
||||
Piercing: 6
|
||||
- type: SolutionContainerManager
|
||||
solutions:
|
||||
melee:
|
||||
@@ -2402,10 +2371,9 @@
|
||||
Burn: -0.07
|
||||
|
||||
- type: entity
|
||||
name: tarantula
|
||||
parent: MobGiantSpider
|
||||
id: MobGiantSpiderAngry
|
||||
suffix: Angry
|
||||
parent: MobSpiderBase
|
||||
id: MobSpiderAngryBase
|
||||
abstract: true
|
||||
components:
|
||||
- type: NpcFactionMember
|
||||
factions:
|
||||
@@ -2424,9 +2392,52 @@
|
||||
settings: short
|
||||
- type: GhostTakeoverAvailable
|
||||
|
||||
- type: entity
|
||||
name: tarantula
|
||||
parent: MobSpiderBase
|
||||
id: MobGiantSpider
|
||||
description: Widely recognized to be the literal worst thing in existence.
|
||||
components:
|
||||
- type: Sprite
|
||||
drawdepth: Mobs
|
||||
layers:
|
||||
- map: ["enum.DamageStateVisualLayers.Base", "movement"]
|
||||
state: tarantula
|
||||
sprite: Mobs/Animals/spider.rsi
|
||||
- type: SpriteMovement
|
||||
movementLayers:
|
||||
movement:
|
||||
state: tarantula-moving
|
||||
noMovementLayers:
|
||||
movement:
|
||||
state: tarantula
|
||||
- type: DamageStateVisuals
|
||||
states:
|
||||
Alive:
|
||||
Base: tarantula
|
||||
Critical:
|
||||
Base: tarantula_dead
|
||||
Dead:
|
||||
Base: tarantula_dead
|
||||
- type: MeleeWeapon
|
||||
altDisarm: false
|
||||
angle: 0
|
||||
animation: WeaponArcBite
|
||||
soundHit:
|
||||
path: /Audio/Effects/bite.ogg
|
||||
damage:
|
||||
types:
|
||||
Piercing: 6
|
||||
|
||||
- type: entity
|
||||
parent:
|
||||
- MobGiantSpider
|
||||
- MobSpiderAngryBase
|
||||
id: MobGiantSpiderAngry
|
||||
|
||||
- type: entity
|
||||
name: clown spider
|
||||
parent: MobGiantSpiderAngry
|
||||
parent: MobSpiderAngryBase
|
||||
id: MobClownSpider
|
||||
description: Combines the two most terrifying things in existence, spiders and clowns.
|
||||
components:
|
||||
|
||||
Reference in New Issue
Block a user