--- /dev/null
+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);
+ }
+ }
+ }
+}
+++ /dev/null
-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);
- }
- }
- }
-}
--- /dev/null
+using Content.Shared.Movement.Systems;
+
+namespace Content.Server.Movement.Systems;
+
+public sealed class SpriteMovementSystem : SharedSpriteMovementSystem
+{
+}
if (!targetCoordinates.IsValid(EntityManager))
{
- SetDirection(mover, steering, Vector2.Zero);
+ SetDirection(uid, mover, steering, Vector2.Zero);
steering.Status = SteeringStatus.NoPath;
return false;
}
// 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;
}
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;
if (EntityManager.TryGetComponent(uid, out InputMoverComponent? controller))
{
controller.CurTickSprintMovement = Vector2.Zero;
+
+ var ev = new SpriteMoveEvent(false);
+ RaiseLocalEvent(uid, ref ev);
}
component.PathfindToken?.Cancel();
}
}
- 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))
{
component.CurTickSprintMovement = value;
component.LastInputTick = _timing.CurTick;
component.LastInputSubTick = ushort.MaxValue;
+
+ var ev = new SpriteMoveEvent(true);
+ RaiseLocalEvent(uid, ref ev);
}
/// <summary>
{
if (Deleted(steering.Coordinates.EntityId))
{
- SetDirection(mover, steering, Vector2.Zero);
+ SetDirection(uid, mover, steering, Vector2.Zero);
steering.Status = SteeringStatus.NoPath;
return;
}
// 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;
}
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;
}
if (!forceSteer)
{
- SetDirection(mover, steering, steering.LastSteerDirection, false);
+ SetDirection(uid, mover, steering, steering.LastSteerDirection, false);
return;
}
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)
/// <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>
/// </summary>
[DataField]
public Dictionary<string, PrototypeLayerData> NoMovementLayers = new();
+
+ [DataField, AutoNetworkedField]
+ public bool IsMoving;
}
--- /dev/null
+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;
+ }
+}
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)
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);
}
}
--- /dev/null
+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);
+ }
+}
# 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:
- MobMask
layer:
- MobLayer
- - type: DamageStateVisuals
- states:
- Alive:
- Base: tarantula
- Critical:
- Base: tarantula_dead
- Dead:
- Base: tarantula_dead
- type: Butcherable
spawned:
- id: FoodMeatSpider
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:
Brute: -0.07
Burn: -0.07
+- type: entity
+ parent: MobSpiderBase
+ id: MobSpiderAngryBase
+ abstract: true
+ components:
+ - type: NpcFactionMember
+ factions:
+ - Xeno
+ - type: InputMover
+ - type: MobMover
+ - type: HTN
+ rootTask:
+ task: SimpleHostileCompound
+ - type: GhostRole
+ makeSentient: true
+ name: ghost-role-information-giant-spider-name
+ description: ghost-role-information-giant-spider-description
+ rules: ghost-role-information-giant-spider-rules
+ raffle:
+ settings: short
+ - type: GhostTakeoverAvailable
+
- type: entity
name: tarantula
- parent: MobGiantSpider
- id: MobGiantSpiderAngry
- suffix: Angry
+ parent: MobSpiderBase
+ id: MobGiantSpider
+ description: Widely recognized to be the literal worst thing in existence.
components:
- - type: NpcFactionMember
- factions:
- - Xeno
- - type: InputMover
- - type: MobMover
- - type: HTN
- rootTask:
- task: SimpleHostileCompound
- - type: GhostRole
- makeSentient: true
- name: ghost-role-information-giant-spider-name
- description: ghost-role-information-giant-spider-description
- rules: ghost-role-information-giant-spider-rules
- raffle:
- settings: short
- - type: GhostTakeoverAvailable
+ - 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: