Add sprite movement states (#22940)
* Add sprite movement states Did it for borgs for reference for future implementations. * Fix * Fix prediction issue * review
51
Content.Client/Movement/Systems/SpriteMovementSystem.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
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.None;
|
||||||
|
var moving = SharedMoverController.GetNormalizedMovement(args.Component.HeldMoveButtons) != 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,13 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Shared.Movement.Systems;
|
using Content.Shared.Movement.Systems;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Shared.Movement.Components
|
namespace Content.Shared.Movement.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
|
[RegisterComponent, NetworkedComponent]
|
||||||
public sealed partial class InputMoverComponent : Component
|
public sealed partial class InputMoverComponent : Component
|
||||||
{
|
{
|
||||||
// This class has to be able to handle server TPS being lower than client FPS.
|
// This class has to be able to handle server TPS being lower than client FPS.
|
||||||
@@ -40,32 +41,30 @@ namespace Content.Shared.Movement.Components
|
|||||||
public Vector2 CurTickWalkMovement;
|
public Vector2 CurTickWalkMovement;
|
||||||
public Vector2 CurTickSprintMovement;
|
public Vector2 CurTickSprintMovement;
|
||||||
|
|
||||||
[AutoNetworkedField]
|
|
||||||
public MoveButtons HeldMoveButtons = MoveButtons.None;
|
public MoveButtons HeldMoveButtons = MoveButtons.None;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Entity our movement is relative to.
|
/// Entity our movement is relative to.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AutoNetworkedField]
|
|
||||||
public EntityUid? RelativeEntity;
|
public EntityUid? RelativeEntity;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Although our movement might be relative to a particular entity we may have an additional relative rotation
|
/// Although our movement might be relative to a particular entity we may have an additional relative rotation
|
||||||
/// e.g. if we've snapped to a different cardinal direction
|
/// e.g. if we've snapped to a different cardinal direction
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables, AutoNetworkedField]
|
[ViewVariables]
|
||||||
public Angle TargetRelativeRotation = Angle.Zero;
|
public Angle TargetRelativeRotation = Angle.Zero;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current relative rotation. This will lerp towards the <see cref="TargetRelativeRotation"/>.
|
/// The current relative rotation. This will lerp towards the <see cref="TargetRelativeRotation"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables, AutoNetworkedField]
|
[ViewVariables]
|
||||||
public Angle RelativeRotation;
|
public Angle RelativeRotation;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If we traverse on / off a grid then set a timer to update our relative inputs.
|
/// If we traverse on / off a grid then set a timer to update our relative inputs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField]
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public TimeSpan LerpTarget;
|
public TimeSpan LerpTarget;
|
||||||
|
|
||||||
@@ -73,7 +72,18 @@ namespace Content.Shared.Movement.Components
|
|||||||
|
|
||||||
public bool Sprinting => (HeldMoveButtons & MoveButtons.Walk) == 0x0;
|
public bool Sprinting => (HeldMoveButtons & MoveButtons.Walk) == 0x0;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public bool CanMove { get; set; } = true;
|
public bool CanMove = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class InputMoverComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public MoveButtons HeldMoveButtons;
|
||||||
|
public NetEntity? RelativeEntity;
|
||||||
|
public Angle TargetRelativeRotation;
|
||||||
|
public Angle RelativeRotation;
|
||||||
|
public TimeSpan LerpTarget;
|
||||||
|
public bool CanMove;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Movement.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates a sprite layer based on whether an entity is moving via input or not.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class SpriteMovementComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Layer and sprite state to use when moving.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public Dictionary<string, PrototypeLayerData> MovementLayers = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Layer and sprite state to use when not moving.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public Dictionary<string, PrototypeLayerData> NoMovementLayers = new();
|
||||||
|
}
|
||||||
@@ -1,15 +1,22 @@
|
|||||||
|
using Content.Shared.Movement.Components;
|
||||||
|
using Content.Shared.Movement.Systems;
|
||||||
|
|
||||||
namespace Content.Shared.Movement.Events;
|
namespace Content.Shared.Movement.Events;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raised on an entity whenever it has a movement input.
|
/// Raised on an entity whenever it has a movement input change.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ByRefEvent]
|
[ByRefEvent]
|
||||||
public readonly struct MoveInputEvent
|
public readonly struct MoveInputEvent
|
||||||
{
|
{
|
||||||
public readonly EntityUid Entity;
|
public readonly EntityUid Entity;
|
||||||
|
public readonly InputMoverComponent Component;
|
||||||
|
public readonly MoveButtons OldMovement;
|
||||||
|
|
||||||
public MoveInputEvent(EntityUid entity)
|
public MoveInputEvent(EntityUid entity, InputMoverComponent component, MoveButtons oldMovement)
|
||||||
{
|
{
|
||||||
Entity = entity;
|
Entity = entity;
|
||||||
|
Component = component;
|
||||||
|
OldMovement = oldMovement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Content.Shared.Follower.Components;
|
|||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
using Content.Shared.Movement.Components;
|
using Content.Shared.Movement.Components;
|
||||||
using Content.Shared.Movement.Events;
|
using Content.Shared.Movement.Events;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Input;
|
using Robust.Shared.Input;
|
||||||
using Robust.Shared.Input.Binding;
|
using Robust.Shared.Input.Binding;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
@@ -48,7 +49,8 @@ namespace Content.Shared.Movement.Systems
|
|||||||
.Register<SharedMoverController>();
|
.Register<SharedMoverController>();
|
||||||
|
|
||||||
SubscribeLocalEvent<InputMoverComponent, ComponentInit>(OnInputInit);
|
SubscribeLocalEvent<InputMoverComponent, ComponentInit>(OnInputInit);
|
||||||
SubscribeLocalEvent<InputMoverComponent, AfterAutoHandleStateEvent>(OnInputHandleState);
|
SubscribeLocalEvent<InputMoverComponent, ComponentGetState>(OnMoverGetState);
|
||||||
|
SubscribeLocalEvent<InputMoverComponent, ComponentHandleState>(OnMoverHandleState);
|
||||||
SubscribeLocalEvent<InputMoverComponent, EntParentChangedMessage>(OnInputParentChange);
|
SubscribeLocalEvent<InputMoverComponent, EntParentChangedMessage>(OnInputParentChange);
|
||||||
|
|
||||||
SubscribeLocalEvent<AutoOrientComponent, EntParentChangedMessage>(OnAutoParentChange);
|
SubscribeLocalEvent<AutoOrientComponent, EntParentChangedMessage>(OnAutoParentChange);
|
||||||
@@ -64,19 +66,76 @@ namespace Content.Shared.Movement.Systems
|
|||||||
CameraRotationLocked = obj;
|
CameraRotationLocked = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the buttons held with opposites cancelled out.
|
||||||
|
/// </summary>
|
||||||
|
public static MoveButtons GetNormalizedMovement(MoveButtons buttons)
|
||||||
|
{
|
||||||
|
var oldMovement = buttons;
|
||||||
|
|
||||||
|
if ((oldMovement & (MoveButtons.Left | MoveButtons.Right)) == (MoveButtons.Left | MoveButtons.Right))
|
||||||
|
{
|
||||||
|
oldMovement &= ~MoveButtons.Left;
|
||||||
|
oldMovement &= ~MoveButtons.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((oldMovement & (MoveButtons.Up | MoveButtons.Down)) == (MoveButtons.Up | MoveButtons.Down))
|
||||||
|
{
|
||||||
|
oldMovement &= ~MoveButtons.Up;
|
||||||
|
oldMovement &= ~MoveButtons.Down;
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldMovement;
|
||||||
|
}
|
||||||
|
|
||||||
protected void SetMoveInput(InputMoverComponent component, MoveButtons buttons)
|
protected void SetMoveInput(InputMoverComponent component, MoveButtons buttons)
|
||||||
{
|
{
|
||||||
if (component.HeldMoveButtons == buttons)
|
if (component.HeldMoveButtons == buttons)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Relay the fact we had any movement event.
|
||||||
|
// TODO: Ideally we'd do these in a tick instead of out of sim.
|
||||||
|
var moveEvent = new MoveInputEvent(component.Owner, component, component.HeldMoveButtons);
|
||||||
component.HeldMoveButtons = buttons;
|
component.HeldMoveButtons = buttons;
|
||||||
Dirty(component);
|
RaiseLocalEvent(component.Owner, ref moveEvent);
|
||||||
|
Dirty(component.Owner, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnInputHandleState(EntityUid uid, InputMoverComponent component, ref AfterAutoHandleStateEvent args)
|
private void OnMoverHandleState(EntityUid uid, InputMoverComponent component, ComponentHandleState args)
|
||||||
{
|
{
|
||||||
|
if (args.Current is not InputMoverComponentState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Handle state
|
||||||
|
component.LerpTarget = state.LerpTarget;
|
||||||
|
component.RelativeRotation = state.RelativeRotation;
|
||||||
|
component.TargetRelativeRotation = state.TargetRelativeRotation;
|
||||||
|
component.CanMove = state.CanMove;
|
||||||
|
component.RelativeEntity = EnsureEntity<InputMoverComponent>(state.RelativeEntity, uid);
|
||||||
|
|
||||||
|
// Reset
|
||||||
component.LastInputTick = GameTick.Zero;
|
component.LastInputTick = GameTick.Zero;
|
||||||
component.LastInputSubTick = 0;
|
component.LastInputSubTick = 0;
|
||||||
|
|
||||||
|
if (component.HeldMoveButtons != state.HeldMoveButtons)
|
||||||
|
{
|
||||||
|
var moveEvent = new MoveInputEvent(uid, component, component.HeldMoveButtons);
|
||||||
|
component.HeldMoveButtons = state.HeldMoveButtons;
|
||||||
|
RaiseLocalEvent(uid, ref moveEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMoverGetState(EntityUid uid, InputMoverComponent component, ref ComponentGetState args)
|
||||||
|
{
|
||||||
|
args.State = new InputMoverComponentState()
|
||||||
|
{
|
||||||
|
CanMove = component.CanMove,
|
||||||
|
RelativeEntity = GetNetEntity(component.RelativeEntity),
|
||||||
|
LerpTarget = component.LerpTarget,
|
||||||
|
HeldMoveButtons = component.HeldMoveButtons,
|
||||||
|
RelativeRotation = component.RelativeRotation,
|
||||||
|
TargetRelativeRotation = component.TargetRelativeRotation,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ShutdownInput()
|
private void ShutdownInput()
|
||||||
@@ -260,11 +319,6 @@ namespace Content.Shared.Movement.Systems
|
|||||||
if (!MoverQuery.TryGetComponent(entity, out var moverComp))
|
if (!MoverQuery.TryGetComponent(entity, out var moverComp))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Relay the fact we had any movement event.
|
|
||||||
// TODO: Ideally we'd do these in a tick instead of out of sim.
|
|
||||||
var moveEvent = new MoveInputEvent(entity);
|
|
||||||
RaiseLocalEvent(entity, ref moveEvent);
|
|
||||||
|
|
||||||
// For stuff like "Moving out of locker" or the likes
|
// For stuff like "Moving out of locker" or the likes
|
||||||
// We'll relay a movement input to the parent.
|
// We'll relay a movement input to the parent.
|
||||||
if (_container.IsEntityInContainer(entity) &&
|
if (_container.IsEntityInContainer(entity) &&
|
||||||
@@ -276,7 +330,7 @@ namespace Content.Shared.Movement.Systems
|
|||||||
RaiseLocalEvent(xform.ParentUid, ref relayMoveEvent);
|
RaiseLocalEvent(xform.ParentUid, ref relayMoveEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetVelocityDirection(moverComp, dir, subTick, state);
|
SetVelocityDirection(entity, moverComp, dir, subTick, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnInputInit(EntityUid uid, InputMoverComponent component, ComponentInit args)
|
private void OnInputInit(EntityUid uid, InputMoverComponent component, ComponentInit args)
|
||||||
@@ -308,7 +362,7 @@ namespace Content.Shared.Movement.Systems
|
|||||||
|
|
||||||
if (moverComp == null) return;
|
if (moverComp == null) return;
|
||||||
|
|
||||||
SetSprinting(moverComp, subTick, walking);
|
SetSprinting(uid, moverComp, subTick, walking);
|
||||||
}
|
}
|
||||||
|
|
||||||
public (Vector2 Walking, Vector2 Sprinting) GetVelocityInput(InputMoverComponent mover)
|
public (Vector2 Walking, Vector2 Sprinting) GetVelocityInput(InputMoverComponent mover)
|
||||||
@@ -359,7 +413,7 @@ namespace Content.Shared.Movement.Systems
|
|||||||
/// composed into a single direction vector, <see cref="VelocityDir"/>. Enabling
|
/// composed into a single direction vector, <see cref="VelocityDir"/>. Enabling
|
||||||
/// opposite directions will cancel each other out, resulting in no direction.
|
/// opposite directions will cancel each other out, resulting in no direction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SetVelocityDirection(InputMoverComponent component, Direction direction, ushort subTick, bool enabled)
|
public void SetVelocityDirection(EntityUid entity, InputMoverComponent component, Direction direction, ushort subTick, bool enabled)
|
||||||
{
|
{
|
||||||
// Logger.Info($"[{_gameTiming.CurTick}/{subTick}] {direction}: {enabled}");
|
// Logger.Info($"[{_gameTiming.CurTick}/{subTick}] {direction}: {enabled}");
|
||||||
|
|
||||||
@@ -372,10 +426,10 @@ namespace Content.Shared.Movement.Systems
|
|||||||
_ => throw new ArgumentException(nameof(direction))
|
_ => throw new ArgumentException(nameof(direction))
|
||||||
};
|
};
|
||||||
|
|
||||||
SetMoveInput(component, subTick, enabled, bit);
|
SetMoveInput(entity, component, subTick, enabled, bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetMoveInput(InputMoverComponent component, ushort subTick, bool enabled, MoveButtons bit)
|
private void SetMoveInput(EntityUid entity, InputMoverComponent component, ushort subTick, bool enabled, MoveButtons bit)
|
||||||
{
|
{
|
||||||
// Modifies held state of a movement button at a certain sub tick and updates current tick movement vectors.
|
// Modifies held state of a movement button at a certain sub tick and updates current tick movement vectors.
|
||||||
ResetSubtick(component);
|
ResetSubtick(component);
|
||||||
@@ -415,11 +469,11 @@ namespace Content.Shared.Movement.Systems
|
|||||||
component.LastInputSubTick = 0;
|
component.LastInputSubTick = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSprinting(InputMoverComponent component, ushort subTick, bool walking)
|
public void SetSprinting(EntityUid entity, InputMoverComponent component, ushort subTick, bool walking)
|
||||||
{
|
{
|
||||||
// Logger.Info($"[{_gameTiming.CurTick}/{subTick}] Sprint: {enabled}");
|
// Logger.Info($"[{_gameTiming.CurTick}/{subTick}] Sprint: {enabled}");
|
||||||
|
|
||||||
SetMoveInput(component, subTick, walking, MoveButtons.Walk);
|
SetMoveInput(entity, component, subTick, walking, MoveButtons.Walk);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
layers:
|
layers:
|
||||||
- state: miner
|
- state: miner
|
||||||
|
map: ["movement"]
|
||||||
- state: miner_e_r
|
- state: miner_e_r
|
||||||
map: ["enum.BorgVisualLayers.Light"]
|
map: ["enum.BorgVisualLayers.Light"]
|
||||||
shader: unshaded
|
shader: unshaded
|
||||||
@@ -41,6 +42,13 @@
|
|||||||
shader: unshaded
|
shader: unshaded
|
||||||
map: ["light"]
|
map: ["light"]
|
||||||
visible: false
|
visible: false
|
||||||
|
- type: SpriteMovement
|
||||||
|
movementLayers:
|
||||||
|
movement:
|
||||||
|
state: miner_moving
|
||||||
|
noMovementLayers:
|
||||||
|
movement:
|
||||||
|
state: miner
|
||||||
- type: BorgChassis
|
- type: BorgChassis
|
||||||
maxModules: 4
|
maxModules: 4
|
||||||
moduleWhitelist:
|
moduleWhitelist:
|
||||||
@@ -119,6 +127,7 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
layers:
|
layers:
|
||||||
- state: janitor
|
- state: janitor
|
||||||
|
map: ["movement"]
|
||||||
- state: janitor_e_r
|
- state: janitor_e_r
|
||||||
map: ["enum.BorgVisualLayers.Light"]
|
map: ["enum.BorgVisualLayers.Light"]
|
||||||
shader: unshaded
|
shader: unshaded
|
||||||
@@ -127,6 +136,13 @@
|
|||||||
shader: unshaded
|
shader: unshaded
|
||||||
map: ["light"]
|
map: ["light"]
|
||||||
visible: false
|
visible: false
|
||||||
|
- type: SpriteMovement
|
||||||
|
movementLayers:
|
||||||
|
movement:
|
||||||
|
state: janitor_moving
|
||||||
|
noMovementLayers:
|
||||||
|
movement:
|
||||||
|
state: janitor
|
||||||
- type: BorgChassis
|
- type: BorgChassis
|
||||||
maxModules: 4
|
maxModules: 4
|
||||||
moduleWhitelist:
|
moduleWhitelist:
|
||||||
@@ -162,6 +178,7 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
layers:
|
layers:
|
||||||
- state: medical
|
- state: medical
|
||||||
|
map: ["movement"]
|
||||||
- state: medical_e_r
|
- state: medical_e_r
|
||||||
map: ["enum.BorgVisualLayers.Light"]
|
map: ["enum.BorgVisualLayers.Light"]
|
||||||
shader: unshaded
|
shader: unshaded
|
||||||
@@ -170,6 +187,13 @@
|
|||||||
shader: unshaded
|
shader: unshaded
|
||||||
map: ["light"]
|
map: ["light"]
|
||||||
visible: false
|
visible: false
|
||||||
|
- type: SpriteMovement
|
||||||
|
movementLayers:
|
||||||
|
movement:
|
||||||
|
state: medical_moving
|
||||||
|
noMovementLayers:
|
||||||
|
movement:
|
||||||
|
state: medical
|
||||||
- type: BorgChassis
|
- type: BorgChassis
|
||||||
maxModules: 4
|
maxModules: 4
|
||||||
moduleWhitelist:
|
moduleWhitelist:
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 3.0 KiB |
BIN
Resources/Textures/Mobs/Silicon/chassis.rsi/janitor_moving.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.1 KiB |
BIN
Resources/Textures/Mobs/Silicon/chassis.rsi/medical_moving.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
@@ -41,6 +41,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "janitor",
|
"name": "janitor",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "janitor_moving",
|
||||||
"directions": 4,
|
"directions": 4,
|
||||||
"delays": [
|
"delays": [
|
||||||
[
|
[
|
||||||
@@ -107,6 +111,32 @@
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "medical_moving",
|
||||||
|
"directions": 4,
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.1,
|
||||||
|
0.2,
|
||||||
|
0.1
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.1,
|
||||||
|
0.2,
|
||||||
|
0.1
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.1,
|
||||||
|
0.2,
|
||||||
|
0.1
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.1,
|
||||||
|
0.2,
|
||||||
|
0.1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "medical_e",
|
"name": "medical_e",
|
||||||
"directions": 4
|
"directions": 4
|
||||||
@@ -121,6 +151,10 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "miner",
|
"name": "miner",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "miner_moving",
|
||||||
"directions": 4,
|
"directions": 4,
|
||||||
"delays": [
|
"delays": [
|
||||||
[
|
[
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.2 KiB |
BIN
Resources/Textures/Mobs/Silicon/chassis.rsi/miner_moving.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |