Новая панель эмоутов. (#891)
* wacky * work * work * work * work * eff * a
This commit is contained in:
parent
d8547bf089
commit
5192d120ea
50 changed files with 1372 additions and 239 deletions
|
|
@ -65,7 +65,6 @@ internal sealed class BuckleSystem : SharedBuckleSystem
|
|||
!buckled ||
|
||||
args.Sprite == null)
|
||||
{
|
||||
_rotationVisualizerSystem.SetHorizontalAngle((uid, rotVisuals), rotVisuals.DefaultRotation);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public sealed partial class EmotesMenu : RadialMenu
|
|||
foreach (var emote in emotes)
|
||||
{
|
||||
var player = _playerManager.LocalSession?.AttachedEntity;
|
||||
if (emote.Category == EmoteCategory.Invalid ||
|
||||
if (emote.Category == EmoteCategory.Invalid || emote.Category == EmoteCategory.Verb ||
|
||||
emote.ChatTriggers.Count == 0 ||
|
||||
!(player.HasValue && whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player.Value)) ||
|
||||
whitelistSystem.IsBlacklistPass(emote.Blacklist, player.Value))
|
||||
|
|
|
|||
|
|
@ -86,6 +86,10 @@ namespace Content.Client.Input
|
|||
human.AddFunction(ContentKeyFunctions.Arcade2);
|
||||
human.AddFunction(ContentKeyFunctions.Arcade3);
|
||||
|
||||
// Sunrise
|
||||
human.AddFunction(ContentKeyFunctions.ToggleStanding);
|
||||
// Sunrise
|
||||
|
||||
// actions should be common (for ghosts, mobs, etc)
|
||||
common.AddFunction(ContentKeyFunctions.OpenActionsMenu);
|
||||
|
||||
|
|
|
|||
|
|
@ -187,6 +187,10 @@ namespace Content.Client.Options.UI.Tabs
|
|||
AddButton(ContentKeyFunctions.RotateStoredItem);
|
||||
AddButton(ContentKeyFunctions.SaveItemLocation);
|
||||
|
||||
// Sunrise
|
||||
AddButton(ContentKeyFunctions.ToggleStanding);
|
||||
// Sunrise
|
||||
|
||||
AddHeader("ui-options-header-interaction-adv");
|
||||
AddButton(ContentKeyFunctions.SmartEquipBackpack);
|
||||
AddButton(ContentKeyFunctions.SmartEquipBelt);
|
||||
|
|
|
|||
67
Content.Client/Standing/StandingStateSystem.cs
Normal file
67
Content.Client/Standing/StandingStateSystem.cs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
using Content.Shared.Input;
|
||||
using Content.Shared.Rotation;
|
||||
using Content.Shared.Standing;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Input.Binding;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client.Standing;
|
||||
|
||||
public sealed class StandingStateSystem : SharedStandingStateSystem
|
||||
{
|
||||
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
CommandBinds.Builder
|
||||
.Bind(ContentKeyFunctions.ToggleStanding, InputCmdHandler.FromDelegate(ToggleStanding))
|
||||
.Register<SharedStandingStateSystem>();
|
||||
|
||||
SubscribeLocalEvent<RotationVisualsComponent, MoveEvent>(OnMove);
|
||||
}
|
||||
|
||||
private void ToggleStanding(ICommonSession? session)
|
||||
{
|
||||
if (session is not { AttachedEntity: { Valid: true } uid } _
|
||||
|| !Exists(uid))
|
||||
return;
|
||||
|
||||
RaiseNetworkEvent(new ChangeLayingDownEvent());
|
||||
}
|
||||
|
||||
private void OnMove(EntityUid uid, RotationVisualsComponent component, ref MoveEvent args)
|
||||
{
|
||||
if (!TryComp<SpriteComponent>(uid, out var sprite) ||
|
||||
!TryComp<AppearanceComponent>(uid, out var appearance) ||
|
||||
!TryComp<TransformComponent>(uid, out var transform) ||
|
||||
component.DefaultRotation == 0)
|
||||
return;
|
||||
|
||||
_appearance.TryGetData<RotationState>(uid, RotationVisuals.RotationState, out var state, appearance);
|
||||
|
||||
var rotation = transform.LocalRotation + _eyeManager.CurrentEye.Rotation;
|
||||
if (rotation.GetDir() is Direction.East or Direction.North or Direction.NorthEast or Direction.SouthEast)
|
||||
{
|
||||
if (state != RotationState.Horizontal ||
|
||||
sprite.Rotation != component.DefaultRotation)
|
||||
return;
|
||||
|
||||
component.HorizontalRotation = Angle.FromDegrees(270);
|
||||
sprite.Rotation = Angle.FromDegrees(270);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (state != RotationState.Horizontal ||
|
||||
sprite.Rotation != Angle.FromDegrees(270))
|
||||
return;
|
||||
|
||||
component.HorizontalRotation = component.DefaultRotation;
|
||||
|
||||
sprite.Rotation = component.DefaultRotation;
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
xmlns:inventory="clr-namespace:Content.Client.UserInterface.Systems.Inventory.Widgets"
|
||||
xmlns:us="clr-namespace:Content.Client._Sunrise.UserActions"
|
||||
Name="SeparatedChatHud"
|
||||
VerticalExpand="False"
|
||||
VerticalAlignment="Bottom"
|
||||
|
|
@ -33,6 +34,7 @@
|
|||
|
||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SeparationOverride="10" Margin="10">
|
||||
<menuBar:GameTopMenuBar Name="TopBar" HorizontalExpand="True" Access="Protected" />
|
||||
<us:UserActionsPanel></us:UserActionsPanel>
|
||||
<chat:ChatBox VerticalExpand="True" HorizontalExpand="True" Name="Chat" Access="Protected" MinSize="0 0"/>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayS
|
|||
[Dependency] private readonly IClyde _displayManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
|
||||
private MenuButton? EmotesButton => UIManager.GetActiveUIWidgetOrNull<MenuBar.Widgets.GameTopMenuBar>()?.EmotesButton;
|
||||
private MenuButton? EmotesButton => null;
|
||||
private EmotesMenu? _menu;
|
||||
|
||||
public void OnStateEntered(GameplayState state)
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@
|
|||
HorizontalExpand="True"
|
||||
AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}"
|
||||
/>
|
||||
<!-- not needed because of new emote ui system
|
||||
<ui:MenuButton
|
||||
Name="EmotesButton"
|
||||
Access="Internal"
|
||||
|
|
@ -53,6 +54,7 @@
|
|||
HorizontalExpand="True"
|
||||
AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}"
|
||||
/>
|
||||
-->
|
||||
<ui:MenuButton
|
||||
Name="CraftingButton"
|
||||
Access="Internal"
|
||||
|
|
|
|||
132
Content.Client/_Sunrise/Animations/EmoteAnimationSystem.cs
Normal file
132
Content.Client/_Sunrise/Animations/EmoteAnimationSystem.cs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
using System.Numerics;
|
||||
using Content.Shared._Sunrise.Animations;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Client._Sunrise.Animations;
|
||||
|
||||
public sealed class EmoteAnimationSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _animationSystem = default!;
|
||||
|
||||
private readonly Dictionary<string, Action<EntityUid>> _emoteList = new();
|
||||
|
||||
public const string AnimationKey = "emoteAnimationKeyId";
|
||||
private const string AnimationKeyTurn = "emoteAnimationKeyId_rotate";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<EmoteAnimationComponent, ComponentHandleState>(OnHandleState);
|
||||
|
||||
_emoteList.Add("EmoteFlip", uid =>
|
||||
{
|
||||
if (_animationSystem.HasRunningAnimation(uid, AnimationKey))
|
||||
return;
|
||||
|
||||
var baseAngle = Angle.Zero;
|
||||
if (EntityManager.TryGetComponent(uid, out SpriteComponent? sprite))
|
||||
{
|
||||
baseAngle = sprite.Rotation;
|
||||
}
|
||||
|
||||
var animation = new Animation
|
||||
{
|
||||
Length = TimeSpan.FromMilliseconds(500),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty
|
||||
{
|
||||
ComponentType = typeof(SpriteComponent),
|
||||
Property = nameof(SpriteComponent.Rotation),
|
||||
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(baseAngle.Degrees - 10), 0f),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(baseAngle.Degrees + 180), 0.25f),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(baseAngle.Degrees + 360), 0.25f),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(baseAngle.Degrees), 0f),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_animationSystem.Play(uid, animation, AnimationKey);
|
||||
});
|
||||
|
||||
_emoteList.Add("EmoteJump", uid =>
|
||||
{
|
||||
if (_animationSystem.HasRunningAnimation(uid, AnimationKey))
|
||||
return;
|
||||
|
||||
var animation = new Animation
|
||||
{
|
||||
Length = TimeSpan.FromMilliseconds(250),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty
|
||||
{
|
||||
ComponentType = typeof(SpriteComponent),
|
||||
Property = nameof(SpriteComponent.Offset),
|
||||
InterpolationMode = AnimationInterpolationMode.Cubic,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(Vector2.Zero, 0f),
|
||||
new AnimationTrackProperty.KeyFrame(new Vector2(0, 0.7f), 0.125f),
|
||||
new AnimationTrackProperty.KeyFrame(Vector2.Zero, 0.125f),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_animationSystem.Play(uid, animation, AnimationKey);
|
||||
});
|
||||
|
||||
_emoteList.Add("EmoteTurn", uid =>
|
||||
{
|
||||
if (_animationSystem.HasRunningAnimation(uid, AnimationKeyTurn))
|
||||
return;
|
||||
|
||||
var animation = new Animation
|
||||
{
|
||||
Length = TimeSpan.FromMilliseconds(900),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackComponentProperty
|
||||
{
|
||||
ComponentType = typeof(TransformComponent),
|
||||
Property = nameof(TransformComponent.LocalRotation),
|
||||
InterpolationMode = AnimationInterpolationMode.Linear,
|
||||
KeyFrames =
|
||||
{
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(0), 0f),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(90), 0.075f),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(180), 0.075f),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(270), 0.075f),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.Zero, 0.075f),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(90), 0.075f),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(180), 0.075f),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(270), 0.075f),
|
||||
new AnimationTrackProperty.KeyFrame(Angle.Zero, 0.075f),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_animationSystem.Play(uid, animation, AnimationKeyTurn);
|
||||
});
|
||||
}
|
||||
|
||||
private void OnHandleState(EntityUid uid, EmoteAnimationComponent component, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not EmoteAnimationComponent.EmoteAnimationComponentState state)
|
||||
return;
|
||||
|
||||
component.AnimationId = state.AnimationId;
|
||||
if (_emoteList.TryGetValue(component.AnimationId, out var value))
|
||||
{
|
||||
value.Invoke(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using Robust.Client.UserInterface;
|
||||
|
||||
namespace Content.Client._Sunrise.UserActions.Tabs;
|
||||
|
||||
[Virtual]
|
||||
public class BaseTabControl : Control
|
||||
{
|
||||
public virtual bool UpdateState() { return true; }
|
||||
}
|
||||
183
Content.Client/_Sunrise/UserActions/Tabs/EmotesTabControl.cs
Normal file
183
Content.Client/_Sunrise/UserActions/Tabs/EmotesTabControl.cs
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client._Sunrise.UserActions.Tabs;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class EmotesTabControl : BaseTabControl
|
||||
{
|
||||
[Dependency] private readonly EntityManager _entManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private TimeSpan _lastEmoteTime;
|
||||
private static readonly TimeSpan EmoteCooldown = TimeSpan.FromSeconds(3);
|
||||
|
||||
public EmotesTabControl()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public override bool UpdateState()
|
||||
{
|
||||
EmotesList.RemoveAllChildren();
|
||||
|
||||
var player = _playerManager.LocalEntity;
|
||||
if (player is not { Valid: true })
|
||||
return false;
|
||||
|
||||
var emotes = _prototypeManager.EnumeratePrototypes<EmotePrototype>()
|
||||
.Where(emote => IsEmoteAvailable(emote, player.Value))
|
||||
.OrderBy(x => x.Category)
|
||||
.ThenBy(x => x.ID)
|
||||
.ToList();
|
||||
|
||||
if (emotes.Count == 0)
|
||||
return false;
|
||||
|
||||
var currentRow = CreateNewRow();
|
||||
EmotesList.AddChild(currentRow);
|
||||
|
||||
foreach (var emote in emotes)
|
||||
{
|
||||
var button = CreateEmoteButton(emote);
|
||||
currentRow.AddChild(button);
|
||||
}
|
||||
|
||||
UpdateButtonsLayout();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdateButtonsLayout()
|
||||
{
|
||||
if (!EmotesList.Children.Any())
|
||||
return;
|
||||
|
||||
var maxWidth = Math.Min(300, Width);
|
||||
var firstRow = EmotesList.Children.First() as BoxContainer;
|
||||
if (firstRow == null)
|
||||
return;
|
||||
|
||||
var currentRow = firstRow;
|
||||
var rowWidth = 0f;
|
||||
|
||||
var allButtons = EmotesList.Children
|
||||
.OfType<BoxContainer>()
|
||||
.SelectMany(row => row.Children.ToList())
|
||||
.ToList();
|
||||
|
||||
foreach (var button in allButtons)
|
||||
{
|
||||
button.Parent?.RemoveChild(button);
|
||||
}
|
||||
|
||||
foreach (var child in EmotesList.Children.Skip(1).ToList())
|
||||
{
|
||||
EmotesList.RemoveChild(child);
|
||||
}
|
||||
firstRow.RemoveAllChildren();
|
||||
|
||||
foreach (var button in allButtons)
|
||||
{
|
||||
if (rowWidth + 100 > maxWidth)
|
||||
{
|
||||
currentRow = CreateNewRow();
|
||||
EmotesList.AddChild(currentRow);
|
||||
rowWidth = 0;
|
||||
}
|
||||
|
||||
currentRow.AddChild(button);
|
||||
rowWidth += 100;
|
||||
}
|
||||
}
|
||||
|
||||
private BoxContainer CreateNewRow()
|
||||
{
|
||||
return new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||
HorizontalExpand = true,
|
||||
};
|
||||
}
|
||||
|
||||
private Button CreateEmoteButton(EmotePrototype emote)
|
||||
{
|
||||
var container = new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||
HorizontalExpand = true,
|
||||
MinSize = new Vector2(0, 24),
|
||||
Margin = new Thickness(1)
|
||||
};
|
||||
|
||||
var label = new RichTextLabel
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
VerticalExpand = true,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
Text = Loc.GetString(emote.Name)
|
||||
};
|
||||
|
||||
var button = new Button
|
||||
{
|
||||
StyleClasses = { "EmoteButton" },
|
||||
HorizontalExpand = true,
|
||||
MinSize = new Vector2(0, 24),
|
||||
Margin = new Thickness(1)
|
||||
};
|
||||
|
||||
container.AddChild(label);
|
||||
button.AddChild(container);
|
||||
|
||||
button.OnPressed += _ => OnPlayEmote(new ProtoId<EmotePrototype>(emote.ID));
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
private bool IsEmoteAvailable(EmotePrototype emote, EntityUid player)
|
||||
{
|
||||
var whitelistSystem = _entManager.System<EntityWhitelistSystem>();
|
||||
|
||||
if (emote.Category == EmoteCategory.Invalid || emote.Category == EmoteCategory.Verb || emote.ChatTriggers.Count == 0)
|
||||
return false;
|
||||
|
||||
if (!whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player) ||
|
||||
whitelistSystem.IsBlacklistPass(emote.Blacklist, player))
|
||||
return false;
|
||||
|
||||
if (!emote.Available &&
|
||||
_entManager.TryGetComponent<SpeechComponent>(player, out var speech) &&
|
||||
!speech.AllowedEmotes.Contains(emote.ID))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnPlayEmote(ProtoId<EmotePrototype> protoId)
|
||||
{
|
||||
var currentTime = _gameTiming.CurTime;
|
||||
if (currentTime - _lastEmoteTime < EmoteCooldown)
|
||||
return;
|
||||
|
||||
_lastEmoteTime = currentTime;
|
||||
_entManager.RaisePredictiveEvent(new PlayEmoteMessage(protoId));
|
||||
}
|
||||
|
||||
protected override void Resized()
|
||||
{
|
||||
UpdateButtonsLayout();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<controls:EmotesTabControl
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client._Sunrise.UserActions.Tabs">
|
||||
|
||||
<PanelContainer StyleClasses="BackgroundDark">
|
||||
<ScrollContainer VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
Margin="2"
|
||||
MinHeight="100"
|
||||
MaxHeight="200"
|
||||
HScrollEnabled="False">
|
||||
<BoxContainer Name="EmotesList"
|
||||
Orientation="Vertical"
|
||||
HorizontalExpand="True"
|
||||
Margin="2">
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</PanelContainer>
|
||||
|
||||
</controls:EmotesTabControl>
|
||||
184
Content.Client/_Sunrise/UserActions/Tabs/VerbsTabControl.cs
Normal file
184
Content.Client/_Sunrise/UserActions/Tabs/VerbsTabControl.cs
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Speech;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client._Sunrise.UserActions.Tabs;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class VerbsTabControl : BaseTabControl
|
||||
{
|
||||
[Dependency] private readonly EntityManager _entManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly ISharedPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private TimeSpan _lastVerbTime;
|
||||
private static readonly TimeSpan VerbCooldown = TimeSpan.FromSeconds(0.5f);
|
||||
|
||||
public VerbsTabControl()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public override bool UpdateState()
|
||||
{
|
||||
VerbsList.RemoveAllChildren();
|
||||
|
||||
var player = _playerManager.LocalEntity;
|
||||
if (player is not { Valid: true })
|
||||
return false;
|
||||
|
||||
var emotes = _prototypeManager.EnumeratePrototypes<EmotePrototype>()
|
||||
.Where(emote => IsVerbsAvailable(emote, player.Value))
|
||||
.OrderBy(x => x.Category)
|
||||
.ThenBy(x => x.ID)
|
||||
.ToList();
|
||||
|
||||
if (emotes.Count == 0)
|
||||
return false;
|
||||
|
||||
var currentRow = CreateNewRow();
|
||||
VerbsList.AddChild(currentRow);
|
||||
|
||||
foreach (var emote in emotes)
|
||||
{
|
||||
var button = CreateVerbButton(emote);
|
||||
currentRow.AddChild(button);
|
||||
}
|
||||
|
||||
UpdateButtonsLayout();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdateButtonsLayout()
|
||||
{
|
||||
if (!VerbsList.Children.Any())
|
||||
return;
|
||||
|
||||
var maxWidth = Math.Min(300, Width);
|
||||
var firstRow = VerbsList.Children.First() as BoxContainer;
|
||||
if (firstRow == null)
|
||||
return;
|
||||
|
||||
var currentRow = firstRow;
|
||||
var rowWidth = 0f;
|
||||
|
||||
var allButtons = VerbsList.Children
|
||||
.OfType<BoxContainer>()
|
||||
.SelectMany(row => row.Children.ToList())
|
||||
.ToList();
|
||||
|
||||
foreach (var button in allButtons)
|
||||
{
|
||||
button.Parent?.RemoveChild(button);
|
||||
}
|
||||
|
||||
foreach (var child in VerbsList.Children.Skip(1).ToList())
|
||||
{
|
||||
VerbsList.RemoveChild(child);
|
||||
}
|
||||
firstRow.RemoveAllChildren();
|
||||
|
||||
foreach (var button in allButtons)
|
||||
{
|
||||
if (rowWidth + 100 > maxWidth)
|
||||
{
|
||||
currentRow = CreateNewRow();
|
||||
VerbsList.AddChild(currentRow);
|
||||
rowWidth = 0;
|
||||
}
|
||||
|
||||
currentRow.AddChild(button);
|
||||
rowWidth += 100;
|
||||
}
|
||||
}
|
||||
|
||||
private BoxContainer CreateNewRow()
|
||||
{
|
||||
return new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
||||
HorizontalExpand = true,
|
||||
};
|
||||
}
|
||||
|
||||
private Button CreateVerbButton(EmotePrototype emote)
|
||||
{
|
||||
var container = new BoxContainer
|
||||
{
|
||||
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||
HorizontalExpand = true,
|
||||
MinSize = new Vector2(0, 24),
|
||||
Margin = new Thickness(1)
|
||||
};
|
||||
|
||||
var label = new RichTextLabel
|
||||
{
|
||||
HorizontalExpand = true,
|
||||
VerticalExpand = true,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
VerticalAlignment = VAlignment.Center,
|
||||
Text = Loc.GetString(emote.Name)
|
||||
};
|
||||
|
||||
var button = new Button
|
||||
{
|
||||
StyleClasses = { "EmoteButton" },
|
||||
HorizontalExpand = true,
|
||||
MinSize = new Vector2(0, 24),
|
||||
Margin = new Thickness(1)
|
||||
};
|
||||
|
||||
container.AddChild(label);
|
||||
button.AddChild(container);
|
||||
|
||||
button.OnPressed += _ => OnPlayVerb(new ProtoId<EmotePrototype>(emote.ID));
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
|
||||
private bool IsVerbsAvailable(EmotePrototype emote, EntityUid player)
|
||||
{
|
||||
var whitelistSystem = _entManager.System<EntityWhitelistSystem>();
|
||||
|
||||
if (emote.Category == EmoteCategory.Invalid || emote.Category != EmoteCategory.Verb)
|
||||
return false;
|
||||
|
||||
if (!whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player) ||
|
||||
whitelistSystem.IsBlacklistPass(emote.Blacklist, player))
|
||||
return false;
|
||||
|
||||
if (!emote.Available &&
|
||||
_entManager.TryGetComponent<SpeechComponent>(player, out var speech) &&
|
||||
!speech.AllowedEmotes.Contains(emote.ID))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnPlayVerb(ProtoId<EmotePrototype> protoId)
|
||||
{
|
||||
var currentTime = _gameTiming.CurTime;
|
||||
if (currentTime - _lastVerbTime < VerbCooldown)
|
||||
return;
|
||||
|
||||
_lastVerbTime = currentTime;
|
||||
_entManager.RaisePredictiveEvent(new PlayEmoteMessage(protoId));
|
||||
}
|
||||
|
||||
protected override void Resized()
|
||||
{
|
||||
UpdateButtonsLayout();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
<controls:VerbsTabControl
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client._Sunrise.UserActions.Tabs">
|
||||
|
||||
<PanelContainer StyleClasses="BackgroundDark">
|
||||
<ScrollContainer VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
Margin="2"
|
||||
MinHeight="100"
|
||||
MaxHeight="200"
|
||||
HScrollEnabled="False">
|
||||
<BoxContainer Name="VerbsList"
|
||||
Orientation="Vertical"
|
||||
HorizontalExpand="True"
|
||||
Margin="2">
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</PanelContainer>
|
||||
|
||||
</controls:VerbsTabControl>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
using Content.Client._Sunrise.UserActions.Tabs;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
|
||||
namespace Content.Client._Sunrise.UserActions;
|
||||
|
||||
public sealed class UserActionUIController : UIController, IOnSystemChanged<UserActionUISystem>
|
||||
{
|
||||
private UserActionsPanel? _panel;
|
||||
private List<BaseTabControl> _tabs = new();
|
||||
|
||||
public void OnSystemLoaded(UserActionUISystem system)
|
||||
{
|
||||
system.PlayerAttachedEvent += OnAttached;
|
||||
system.PlayerDetachedEvent += OnDetached;
|
||||
}
|
||||
|
||||
public void OnSystemUnloaded(UserActionUISystem system)
|
||||
{
|
||||
system.PlayerAttachedEvent -= OnAttached;
|
||||
system.PlayerDetachedEvent -= OnDetached;
|
||||
}
|
||||
|
||||
private void OnAttached()
|
||||
{
|
||||
_panel?.UpdateTabs();
|
||||
}
|
||||
|
||||
private void OnDetached()
|
||||
{
|
||||
_panel?.UpdateTabs();
|
||||
}
|
||||
|
||||
public void RegisterPanel(UserActionsPanel panel)
|
||||
{
|
||||
_panel = panel;
|
||||
}
|
||||
|
||||
public void RegisterTab(BaseTabControl tab)
|
||||
{
|
||||
if (!_tabs.Contains(tab))
|
||||
_tabs.Add(tab);
|
||||
}
|
||||
|
||||
public List<BaseTabControl> GetTabs() => _tabs;
|
||||
}
|
||||
23
Content.Client/_Sunrise/UserActions/UserActionUISystem.cs
Normal file
23
Content.Client/_Sunrise/UserActions/UserActionUISystem.cs
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client._Sunrise.UserActions;
|
||||
|
||||
public sealed class UserActionUISystem : EntitySystem
|
||||
{
|
||||
public event Action? PlayerAttachedEvent;
|
||||
public event Action? PlayerDetachedEvent;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<LocalPlayerAttachedEvent>(LocalPlayerAttached);
|
||||
SubscribeLocalEvent<LocalPlayerDetachedEvent>(LocalPlayerDetached);
|
||||
}
|
||||
|
||||
private void LocalPlayerAttached(LocalPlayerAttachedEvent ev)
|
||||
=> PlayerAttachedEvent?.Invoke();
|
||||
|
||||
private void LocalPlayerDetached(LocalPlayerDetachedEvent ev)
|
||||
=> PlayerDetachedEvent?.Invoke();
|
||||
}
|
||||
49
Content.Client/_Sunrise/UserActions/UserActionsPanel.cs
Normal file
49
Content.Client/_Sunrise/UserActions/UserActionsPanel.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client._Sunrise.UserActions;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class UserActionsPanel : Control
|
||||
{
|
||||
private UserActionUIController _controller;
|
||||
|
||||
public UserActionsPanel()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_controller = UserInterfaceManager.GetUIController<UserActionUIController>();
|
||||
_controller.RegisterPanel(this);
|
||||
|
||||
RegisterTabs();
|
||||
}
|
||||
|
||||
private void RegisterTabs()
|
||||
{
|
||||
_controller.RegisterTab(EmotesTabControl);
|
||||
TabContainer.SetTabTitle(EmotesTabControl, Loc.GetString("user-action-control-tab-emote"));
|
||||
|
||||
_controller.RegisterTab(VerbTabControl);
|
||||
TabContainer.SetTabTitle(VerbTabControl, Loc.GetString("user-action-control-tab-verbs"));
|
||||
}
|
||||
|
||||
public void UpdateTabs()
|
||||
{
|
||||
var anyVisible = false;
|
||||
|
||||
foreach (var tab in _controller.GetTabs())
|
||||
{
|
||||
var visible = tab.UpdateState();
|
||||
TabContainer.SetTabVisible(tab, visible);
|
||||
|
||||
if (visible)
|
||||
anyVisible = true;
|
||||
}
|
||||
|
||||
MainPanel.Visible = true;
|
||||
NoContentLabel.Visible = !anyVisible;
|
||||
}
|
||||
}
|
||||
27
Content.Client/_Sunrise/UserActions/UserActionsPanel.xaml
Normal file
27
Content.Client/_Sunrise/UserActions/UserActionsPanel.xaml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<controls:UserActionsPanel
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client._Sunrise.UserActions"
|
||||
xmlns:tabs="clr-namespace:Content.Client._Sunrise.UserActions.Tabs">
|
||||
|
||||
<PanelContainer StyleClasses="BackgroundDark" Name="MainPanel" MinWidth="300" MinHeight="200" HorizontalExpand="True">
|
||||
|
||||
<BoxContainer Orientation="Vertical" Name="BoxContainer" Margin="4" VerticalExpand="True">
|
||||
|
||||
<TabContainer Name="TabContainer" HorizontalExpand="True" VerticalExpand="True">
|
||||
<tabs:EmotesTabControl Name="EmotesTabControl"/>
|
||||
<tabs:VerbsTabControl Name="VerbTabControl"/>
|
||||
</TabContainer>
|
||||
|
||||
<Label Name="NoContentLabel"
|
||||
Text="{Loc 'user-action-control-nothing'}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
StyleClasses="LabelBig"
|
||||
Margin="0 8"
|
||||
Visible="False"/>
|
||||
|
||||
</BoxContainer>
|
||||
|
||||
</PanelContainer>
|
||||
|
||||
</controls:UserActionsPanel>
|
||||
|
|
@ -59,7 +59,7 @@ namespace Content.IntegrationTests.Tests.Buckle
|
|||
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||
var actionBlocker = entityManager.System<ActionBlockerSystem>();
|
||||
var buckleSystem = entityManager.System<SharedBuckleSystem>();
|
||||
var standingState = entityManager.System<StandingStateSystem>();
|
||||
var standingState = entityManager.System<SharedStandingStateSystem>();
|
||||
var xformSystem = entityManager.System<SharedTransformSystem>();
|
||||
|
||||
EntityUid human = default;
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public sealed class CrematoriumSystem : EntitySystem
|
|||
[Dependency] private readonly GhostSystem _ghostSystem = default!;
|
||||
[Dependency] private readonly EntityStorageSystem _entityStorage = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly StandingStateSystem _standing = default!;
|
||||
[Dependency] private readonly SharedStandingStateSystem _standing = default!;
|
||||
[Dependency] private readonly SharedMindSystem _minds = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containers = default!;
|
||||
|
||||
|
|
|
|||
|
|
@ -202,6 +202,7 @@ public sealed partial class NPCCombatSystem
|
|||
return;
|
||||
}
|
||||
|
||||
_gun.SetTarget(gun, comp.Target);
|
||||
_gun.AttemptShoot(uid, gunUid, gun, targetCordinates);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public sealed partial class EmotesMenuSystem : EntitySystem
|
|||
if (!player.HasValue)
|
||||
return;
|
||||
|
||||
if (!_prototypeManager.TryIndex(msg.ProtoId, out var proto) || proto.ChatTriggers.Count == 0)
|
||||
if (!_prototypeManager.TryIndex(msg.ProtoId, out var proto))
|
||||
return;
|
||||
|
||||
_chat.TryEmoteWithChat(player.Value, msg.ProtoId);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
using System.Numerics;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Gravity;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Standing;
|
||||
using Content.Shared.Throwing;
|
||||
using Robust.Shared.Physics.Components;
|
||||
|
|
@ -8,55 +12,60 @@ using Robust.Shared.Random;
|
|||
|
||||
namespace Content.Server.Standing;
|
||||
|
||||
public sealed class StandingStateSystem : EntitySystem
|
||||
public sealed class StandingStateSystem : SharedStandingStateSystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly ThrowingSystem _throwingSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedGravitySystem _gravity = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeNetworkEvent<ChangeLayingDownEvent>(OnChangeState);
|
||||
SubscribeLocalEvent<StandingStateComponent, DropHandItemsEvent>(FallOver);
|
||||
}
|
||||
|
||||
private void OnChangeState(ChangeLayingDownEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.SenderSession is not { AttachedEntity: { Valid: true } uid }
|
||||
|| !Exists(uid)
|
||||
|| !TryComp<StandingStateComponent>(args.SenderSession.AttachedEntity, out var standingStateComponent)
|
||||
|| _gravity.IsWeightless(args.SenderSession.AttachedEntity.Value))
|
||||
return;
|
||||
|
||||
if (standingStateComponent.CurrentState == StandingState.Laying)
|
||||
TryStandUp(uid, standingStateComponent);
|
||||
else
|
||||
{
|
||||
Fall(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void FallOver(EntityUid uid, StandingStateComponent component, DropHandItemsEvent args)
|
||||
{
|
||||
var direction = EntityManager.TryGetComponent(uid, out PhysicsComponent? comp) ? comp.LinearVelocity / 50 : Vector2.Zero;
|
||||
var dropAngle = _random.NextFloat(0.8f, 1.2f);
|
||||
|
||||
var fellEvent = new FellDownEvent(uid);
|
||||
RaiseLocalEvent(uid, fellEvent, false);
|
||||
|
||||
if (!TryComp(uid, out HandsComponent? handsComp))
|
||||
return;
|
||||
|
||||
var worldRotation = EntityManager.GetComponent<TransformComponent>(uid).WorldRotation.ToVec();
|
||||
var worldRotation = _transform.GetWorldRotation(uid).ToVec();
|
||||
foreach (var hand in handsComp.Hands.Values)
|
||||
{
|
||||
if (hand.HeldEntity is not EntityUid held)
|
||||
if (hand.HeldEntity is not { } held)
|
||||
continue;
|
||||
if (!_handsSystem.TryDrop(uid, hand, checkActionBlocker: false, handsComp: handsComp))
|
||||
continue;
|
||||
|
||||
if (!_handsSystem.TryDrop(uid, hand, null, checkActionBlocker: false, handsComp: handsComp))
|
||||
continue;
|
||||
|
||||
_throwingSystem.TryThrow(held,
|
||||
_throwingSystem.TryThrow(
|
||||
held,
|
||||
_random.NextAngle().RotateVec(direction / dropAngle + worldRotation / 50),
|
||||
0.5f * dropAngle * _random.NextFloat(-0.9f, 1.1f),
|
||||
uid, 0);
|
||||
uid,
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<StandingStateComponent, DropHandItemsEvent>(FallOver);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised after an entity falls down.
|
||||
/// </summary>
|
||||
public sealed class FellDownEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid Uid { get; }
|
||||
public FellDownEvent(EntityUid uid)
|
||||
{
|
||||
Uid = uid;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
52
Content.Server/_Sunrise/Animations/EmoteAnimationSystem.cs
Normal file
52
Content.Server/_Sunrise/Animations/EmoteAnimationSystem.cs
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
using Content.Server.Chat.Systems;
|
||||
using Content.Shared._Sunrise.Animations;
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Gravity;
|
||||
using Content.Shared.Standing;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Server._Sunrise.Animations;
|
||||
|
||||
public sealed class EmoteAnimationSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedStandingStateSystem _sharedStanding = default!;
|
||||
[Dependency] private readonly SharedGravitySystem _gravity = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<EmoteAnimationComponent, ComponentGetState>(OnGetState);
|
||||
SubscribeLocalEvent<EmoteAnimationComponent, EmoteEvent>(OnEmote);
|
||||
}
|
||||
|
||||
private void OnGetState(EntityUid uid, EmoteAnimationComponent component, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new EmoteAnimationComponent.EmoteAnimationComponentState(component.AnimationId);
|
||||
}
|
||||
|
||||
private void OnEmote(EntityUid uid, EmoteAnimationComponent component, ref EmoteEvent args)
|
||||
{
|
||||
if (args.Handled || !args.Emote.Category.HasFlag(EmoteCategory.Verb))
|
||||
return;
|
||||
|
||||
PlayEmoteAnimation(uid, component, args.Emote.ID);
|
||||
}
|
||||
|
||||
public void PlayEmoteAnimation(EntityUid uid, EmoteAnimationComponent component, string emoteId)
|
||||
{
|
||||
if (emoteId == "EmoteLay")
|
||||
{
|
||||
if (_gravity.IsWeightless(uid))
|
||||
return;
|
||||
|
||||
if (_sharedStanding.IsDown(uid))
|
||||
_sharedStanding.TryStandUp(uid);
|
||||
else
|
||||
_sharedStanding.TryLieDown(uid);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
component.AnimationId = emoteId;
|
||||
Dirty(uid, component);
|
||||
}
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ namespace Content.Server._Sunrise.Carrying
|
|||
[Dependency] private readonly SharedVirtualItemSystem _virtualItemSystem = default!;
|
||||
[Dependency] private readonly CarryingSlowdownSystem _slowdown = default!;
|
||||
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly StandingStateSystem _standingState = default!;
|
||||
[Dependency] private readonly SharedStandingStateSystem _standingState = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||
[Dependency] private readonly PullingSystem _pullingSystem = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ public sealed class FootprintSystem : EntitySystem
|
|||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
[Dependency] private readonly StandingStateSystem _standingStateSystem = default!;
|
||||
[Dependency] private readonly SharedStandingStateSystem _standingStateSystem = default!;
|
||||
#endregion
|
||||
|
||||
#region Entity Queries
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ using Content.Shared.Chemistry.Components;
|
|||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Fluids.Components;
|
||||
using Content.Shared.Standing;
|
||||
using Robust.Shared.Physics.Events;
|
||||
using StandingStateSystem = Content.Shared.Standing.StandingStateSystem;
|
||||
|
||||
namespace Content.Server._Sunrise.Footprints;
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ namespace Content.Server._Sunrise.Footprints;
|
|||
public sealed class PuddleFootprintSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionSystem = default!;
|
||||
[Dependency] private readonly StandingStateSystem _standingStateSystem = default!;
|
||||
[Dependency] private readonly SharedStandingStateSystem _standingStateSystem = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ public abstract partial class SharedBodySystem : EntitySystem
|
|||
[Dependency] protected readonly MovementSpeedModifierSystem Movement = default!;
|
||||
[Dependency] protected readonly SharedContainerSystem Containers = default!;
|
||||
[Dependency] protected readonly SharedTransformSystem SharedTransform = default!;
|
||||
[Dependency] protected readonly StandingStateSystem Standing = default!;
|
||||
[Dependency] protected readonly SharedStandingStateSystem Standing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public abstract partial class SharedBuckleSystem : EntitySystem
|
|||
[Dependency] private readonly SharedJointSystem _joints = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly StandingStateSystem _standing = default!;
|
||||
[Dependency] private readonly SharedStandingStateSystem _standing = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly SharedRotationVisualsSystem _rotationVisuals = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
|
|
|
|||
|
|
@ -81,5 +81,6 @@ public enum EmoteCategory : byte
|
|||
Invalid = 0,
|
||||
Vocal = 1 << 0,
|
||||
Hands = 1 << 1,
|
||||
Verb = 1 << 2,
|
||||
General = byte.MaxValue
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,10 @@ namespace Content.Shared.Input
|
|||
public static readonly BoundKeyFunction ZoomIn = "ZoomIn";
|
||||
public static readonly BoundKeyFunction ResetZoom = "ResetZoom";
|
||||
|
||||
// Sunrise
|
||||
public static readonly BoundKeyFunction ToggleStanding = "ToggleStanding";
|
||||
// Sunrise
|
||||
|
||||
public static readonly BoundKeyFunction ArcadeUp = "ArcadeUp";
|
||||
public static readonly BoundKeyFunction ArcadeDown = "ArcadeDown";
|
||||
public static readonly BoundKeyFunction ArcadeLeft = "ArcadeLeft";
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace Content.Shared.Medical.Cryogenics;
|
|||
public abstract partial class SharedCryoPodSystem: EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
|
||||
[Dependency] private readonly StandingStateSystem _standingStateSystem = default!;
|
||||
[Dependency] private readonly SharedStandingStateSystem _standingStateSystem = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||
|
|
|
|||
|
|
@ -104,13 +104,13 @@ public partial class MobStateSystem
|
|||
_appearance.SetData(target, MobStateVisuals.State, MobState.Alive);
|
||||
break;
|
||||
case MobState.Critical:
|
||||
_standing.Down(target);
|
||||
_appearance.SetData(target, MobStateVisuals.State, MobState.Critical);
|
||||
_standing.Down(target);
|
||||
break;
|
||||
case MobState.Dead:
|
||||
EnsureComp<CollisionWakeComponent>(target);
|
||||
_standing.Down(target);
|
||||
_appearance.SetData(target, MobStateVisuals.State, MobState.Dead);
|
||||
_standing.Down(target);
|
||||
break;
|
||||
case MobState.Invalid:
|
||||
//unused;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ public partial class MobStateSystem : EntitySystem
|
|||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly StandingStateSystem _standing = default!;
|
||||
[Dependency] private readonly SharedStandingStateSystem _standing = default!;
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ namespace Content.Shared.Morgue;
|
|||
|
||||
public sealed class EntityStorageLayingDownOverrideSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly StandingStateSystem _standing = default!;
|
||||
[Dependency] private readonly SharedStandingStateSystem _standing = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
|
|
|||
316
Content.Shared/Standing/SharedStandingStateSystem.cs
Normal file
316
Content.Shared/Standing/SharedStandingStateSystem.cs
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Buckle;
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Rotation;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Throwing;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
|
||||
namespace Content.Shared.Standing;
|
||||
|
||||
public abstract class SharedStandingStateSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movement = default!;
|
||||
[Dependency] private readonly SharedRotationVisualsSystem _rotation = default!;
|
||||
[Dependency] private readonly SharedBuckleSystem _buckle = default!;
|
||||
[Dependency] private readonly StaminaSystem _stamina = default!;
|
||||
[Dependency] private readonly SharedStunSystem _stun = default!;
|
||||
[Dependency] private readonly ThrowingSystem _throwing = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||
|
||||
private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<StandingStateComponent, StandUpDoAfterEvent>(OnStandUpDoAfter);
|
||||
SubscribeLocalEvent<StandingStateComponent, DownDoAfterEvent>(OnDownDoAfter);
|
||||
SubscribeLocalEvent<StandingStateComponent, MoveEvent>(OnMove);
|
||||
SubscribeLocalEvent<StandingStateComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeed);
|
||||
}
|
||||
|
||||
#region Implementation
|
||||
|
||||
private void OnStandUpDoAfter(EntityUid uid, StandingStateComponent component, StandUpDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled)
|
||||
return;
|
||||
if (_mobState.IsIncapacitated(uid))
|
||||
return;
|
||||
|
||||
Stand(uid);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnDownDoAfter(EntityUid uid, StandingStateComponent component, DownDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled)
|
||||
return;
|
||||
|
||||
Down(uid, dropHeldItems: false);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnMove(Entity<StandingStateComponent> ent, ref MoveEvent args)
|
||||
{
|
||||
if (IsStanding(ent))
|
||||
return;
|
||||
|
||||
_movement.RefreshMovementSpeedModifiers(ent);
|
||||
}
|
||||
|
||||
private void OnRefreshMovementSpeed(EntityUid uid, StandingStateComponent component, RefreshMovementSpeedModifiersEvent args)
|
||||
{
|
||||
if (IsDown(uid))
|
||||
{
|
||||
var baseSpeedModify = component.BaseSpeedModify;
|
||||
|
||||
if (!TryComp<HandsComponent>(uid, out var handsComponent) || handsComponent.Hands.Count == 0)
|
||||
{
|
||||
args.ModifySpeed(baseSpeedModify, baseSpeedModify);
|
||||
return;
|
||||
}
|
||||
|
||||
var totalHands = handsComponent.Hands.Count;
|
||||
var freeHands = handsComponent.CountFreeHands();
|
||||
|
||||
var occupiedHandsRatio = (float)(totalHands - freeHands) / totalHands;
|
||||
var speedModifier = baseSpeedModify * (1 - occupiedHandsRatio * 0.5f);
|
||||
|
||||
args.ModifySpeed(speedModifier, speedModifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
args.ModifySpeed(1f, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryStandUp(EntityUid uid, StandingStateComponent? standingState = null)
|
||||
{
|
||||
if (!Resolve(uid, ref standingState, false)
|
||||
|| standingState.CurrentState is not StandingState.Laying
|
||||
|| _mobState.IsIncapacitated(uid)
|
||||
|| HasComp<KnockedDownComponent>(uid)
|
||||
|| TerminatingOrDeleted(uid))
|
||||
return false;
|
||||
|
||||
var args = new DoAfterArgs(EntityManager, uid, standingState.CycleTime, new StandUpDoAfterEvent(), uid)
|
||||
{
|
||||
BreakOnHandChange = false,
|
||||
RequireCanInteract = false,
|
||||
BreakOnDamage = true
|
||||
};
|
||||
|
||||
return _doAfter.TryStartDoAfter(args);
|
||||
}
|
||||
|
||||
public bool TryLieDown(EntityUid uid, StandingStateComponent? standingState = null)
|
||||
{
|
||||
if (!Resolve(uid, ref standingState, false)
|
||||
|| standingState.CurrentState is not StandingState.Standing
|
||||
|| !_mobState.IsAlive(uid)
|
||||
|| TerminatingOrDeleted(uid))
|
||||
return false;
|
||||
|
||||
var args = new DoAfterArgs(EntityManager, uid, standingState.CycleTime, new DownDoAfterEvent(), uid)
|
||||
{
|
||||
BreakOnHandChange = false,
|
||||
RequireCanInteract = false
|
||||
};
|
||||
|
||||
return _doAfter.TryStartDoAfter(args);
|
||||
}
|
||||
|
||||
public void Fall(EntityUid uid, float rollDistance = 3f, float rollSpeed = 6f)
|
||||
{
|
||||
if (!TryComp<PhysicsComponent>(uid, out var physics))
|
||||
return;
|
||||
|
||||
var velocity = physics.LinearVelocity;
|
||||
if (velocity.LengthSquared() < 0.1f)
|
||||
{
|
||||
Down(uid, dropHeldItems: false);
|
||||
return;
|
||||
}
|
||||
|
||||
var direction = velocity.Normalized();
|
||||
|
||||
Down(uid, dropHeldItems: false);
|
||||
_stun.TryStun(uid, TimeSpan.FromSeconds(1.3f), true);
|
||||
_stamina.TakeStaminaDamage(uid, 20);
|
||||
|
||||
_throwing.TryThrow(
|
||||
uid,
|
||||
direction * rollDistance,
|
||||
rollSpeed,
|
||||
friction: 5f,
|
||||
compensateFriction: true,
|
||||
animated: false,
|
||||
playSound: true,
|
||||
doSpin: false
|
||||
);
|
||||
}
|
||||
|
||||
public bool Stand(EntityUid uid,
|
||||
StandingStateComponent? standingState = null,
|
||||
AppearanceComponent? appearance = null,
|
||||
bool force = false)
|
||||
{
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
Resolve(uid, ref appearance, false);
|
||||
|
||||
if (TryComp<BuckleComponent>(uid, out var buckleComponent) && buckleComponent.Buckled)
|
||||
_buckle.TryUnbuckle(uid, uid, buckleComp: buckleComponent);
|
||||
|
||||
if (IsStanding(uid, standingState))
|
||||
return true;
|
||||
|
||||
if (!force)
|
||||
{
|
||||
var msg = new StandAttemptEvent();
|
||||
RaiseLocalEvent(uid, msg);
|
||||
|
||||
if (msg.Cancelled)
|
||||
return false;
|
||||
}
|
||||
|
||||
standingState.CurrentState = StandingState.Standing;
|
||||
Dirty(uid, standingState);
|
||||
RaiseLocalEvent(uid, new StoodEvent());
|
||||
|
||||
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance);
|
||||
|
||||
if (TryComp(uid, out FixturesComponent? fixtureComponent))
|
||||
{
|
||||
foreach (var key in standingState.ChangedFixtures)
|
||||
{
|
||||
if (fixtureComponent.Fixtures.TryGetValue(key, out var fixture))
|
||||
_physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask | StandingCollisionLayer, fixtureComponent);
|
||||
}
|
||||
}
|
||||
|
||||
standingState.ChangedFixtures.Clear();
|
||||
_movement.RefreshMovementSpeedModifiers(uid);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Down(EntityUid uid,
|
||||
bool playSound = true,
|
||||
bool dropHeldItems = true,
|
||||
bool force = false,
|
||||
StandingStateComponent? standingState = null,
|
||||
AppearanceComponent? appearance = null,
|
||||
HandsComponent? hands = null)
|
||||
{
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
Resolve(uid, ref appearance, ref hands, false);
|
||||
|
||||
if (TryComp<BuckleComponent>(uid, out var buckleComponent) && buckleComponent.Buckled)
|
||||
_buckle.TryUnbuckle(uid, uid, buckleComp: buckleComponent);
|
||||
|
||||
if (IsDown(uid, standingState))
|
||||
return true;
|
||||
|
||||
if (dropHeldItems && hands != null)
|
||||
RaiseLocalEvent(uid, new DropHandItemsEvent());
|
||||
|
||||
if (!force)
|
||||
{
|
||||
var msg = new DownAttemptEvent();
|
||||
RaiseLocalEvent(uid, msg);
|
||||
|
||||
if (msg.Cancelled)
|
||||
return false;
|
||||
}
|
||||
|
||||
standingState.CurrentState = StandingState.Laying;
|
||||
Dirty(uid, standingState);
|
||||
|
||||
var transform = Transform(uid);
|
||||
var rotation = transform.LocalRotation;
|
||||
_appearance.TryGetData<bool>(uid, BuckleVisuals.Buckled, out var buckled, appearance);
|
||||
|
||||
if (!buckled && (!_appearance.TryGetData<MobState>(uid, MobStateVisuals.State, out var state, appearance) ||
|
||||
state is MobState.Alive))
|
||||
{
|
||||
if (rotation.GetDir() is Direction.East
|
||||
or Direction.North
|
||||
or Direction.NorthEast
|
||||
or Direction.SouthEast)
|
||||
_rotation.SetHorizontalAngle(uid, Angle.FromDegrees(270));
|
||||
else
|
||||
_rotation.ResetHorizontalAngle(uid);
|
||||
}
|
||||
|
||||
RaiseLocalEvent(uid, new DownedEvent());
|
||||
|
||||
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance);
|
||||
|
||||
if (TryComp(uid, out FixturesComponent? fixtureComponent))
|
||||
{
|
||||
foreach (var (key, fixture) in fixtureComponent.Fixtures)
|
||||
{
|
||||
if ((fixture.CollisionMask & StandingCollisionLayer) == 0)
|
||||
continue;
|
||||
|
||||
standingState.ChangedFixtures.Add(key);
|
||||
_physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask & ~StandingCollisionLayer, manager: fixtureComponent);
|
||||
}
|
||||
}
|
||||
|
||||
_movement.RefreshMovementSpeedModifiers(uid);
|
||||
|
||||
if (_net.IsServer && playSound)
|
||||
_audio.PlayPvs(standingState.DownSound, uid);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null)
|
||||
{
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
return standingState.CurrentState == StandingState.Laying;
|
||||
}
|
||||
|
||||
public bool IsStanding(EntityUid uid, StandingStateComponent? standingState = null)
|
||||
{
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
return standingState.CurrentState == StandingState.Standing;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
@ -1,24 +1,31 @@
|
|||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Standing
|
||||
namespace Content.Shared.Standing;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class StandingStateComponent : Component
|
||||
{
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
[Access(typeof(StandingStateSystem))]
|
||||
public sealed partial class StandingStateComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public SoundSpecifier? DownSound { get; private set; } = new SoundCollectionSpecifier("BodyFall");
|
||||
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
|
||||
public SoundSpecifier DownSound { get; set; } = new SoundCollectionSpecifier("BodyFall");
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public bool Standing { get; set; } = true;
|
||||
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadOnly)]
|
||||
public StandingState CurrentState = StandingState.Standing;
|
||||
|
||||
/// <summary>
|
||||
/// List of fixtures that had their collision mask changed when the entity was downed.
|
||||
/// Required for re-adding the collision mask.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<string> ChangedFixtures = new();
|
||||
}
|
||||
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadOnly)]
|
||||
public List<string> ChangedFixtures = new();
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public float CycleTime { get; set; } = 1f;
|
||||
|
||||
[DataField, AutoNetworkedField]
|
||||
public float BaseSpeedModify { get; set; } = 0.4f;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum StandingState : byte
|
||||
{
|
||||
Standing = 0,
|
||||
Laying = 1
|
||||
}
|
||||
|
|
|
|||
28
Content.Shared/Standing/StandingStateEvents.cs
Normal file
28
Content.Shared/Standing/StandingStateEvents.cs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Standing;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DropHandItemsEvent : EventArgs;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DownAttemptEvent : CancellableEntityEventArgs;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class StandAttemptEvent : CancellableEntityEventArgs;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class StoodEvent : EntityEventArgs;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DownedEvent : EntityEventArgs;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ChangeLayingDownEvent : CancellableEntityEventArgs;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class StandUpDoAfterEvent : SimpleDoAfterEvent;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class DownDoAfterEvent : SimpleDoAfterEvent;
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Rotation;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
|
||||
namespace Content.Shared.Standing
|
||||
{
|
||||
public sealed class StandingStateSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
|
||||
// If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited.
|
||||
private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable;
|
||||
|
||||
public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null)
|
||||
{
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
return !standingState.Standing;
|
||||
}
|
||||
|
||||
public bool Down(EntityUid uid,
|
||||
bool playSound = true,
|
||||
bool dropHeldItems = true,
|
||||
bool force = false,
|
||||
StandingStateComponent? standingState = null,
|
||||
AppearanceComponent? appearance = null,
|
||||
HandsComponent? hands = null)
|
||||
{
|
||||
// TODO: This should actually log missing comps...
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
// Optional component.
|
||||
Resolve(uid, ref appearance, ref hands, false);
|
||||
|
||||
if (!standingState.Standing)
|
||||
return true;
|
||||
|
||||
// This is just to avoid most callers doing this manually saving boilerplate
|
||||
// 99% of the time you'll want to drop items but in some scenarios (e.g. buckling) you don't want to.
|
||||
// We do this BEFORE downing because something like buckle may be blocking downing but we want to drop hand items anyway
|
||||
// and ultimately this is just to avoid boilerplate in Down callers + keep their behavior consistent.
|
||||
if (dropHeldItems && hands != null)
|
||||
{
|
||||
RaiseLocalEvent(uid, new DropHandItemsEvent(), false);
|
||||
}
|
||||
|
||||
if (!force)
|
||||
{
|
||||
var msg = new DownAttemptEvent();
|
||||
RaiseLocalEvent(uid, msg, false);
|
||||
|
||||
if (msg.Cancelled)
|
||||
return false;
|
||||
}
|
||||
|
||||
standingState.Standing = false;
|
||||
Dirty(uid, standingState);
|
||||
RaiseLocalEvent(uid, new DownedEvent(), false);
|
||||
|
||||
// Seemed like the best place to put it
|
||||
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Horizontal, appearance);
|
||||
|
||||
// Change collision masks to allow going under certain entities like flaps and tables
|
||||
if (TryComp(uid, out FixturesComponent? fixtureComponent))
|
||||
{
|
||||
foreach (var (key, fixture) in fixtureComponent.Fixtures)
|
||||
{
|
||||
if ((fixture.CollisionMask & StandingCollisionLayer) == 0)
|
||||
continue;
|
||||
|
||||
standingState.ChangedFixtures.Add(key);
|
||||
_physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask & ~StandingCollisionLayer, manager: fixtureComponent);
|
||||
}
|
||||
}
|
||||
|
||||
// check if component was just added or streamed to client
|
||||
// if true, no need to play sound - mob was down before player could seen that
|
||||
if (standingState.LifeStage <= ComponentLifeStage.Starting)
|
||||
return true;
|
||||
|
||||
if (playSound)
|
||||
{
|
||||
_audio.PlayPredicted(standingState.DownSound, uid, uid);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Stand(EntityUid uid,
|
||||
StandingStateComponent? standingState = null,
|
||||
AppearanceComponent? appearance = null,
|
||||
bool force = false)
|
||||
{
|
||||
// TODO: This should actually log missing comps...
|
||||
if (!Resolve(uid, ref standingState, false))
|
||||
return false;
|
||||
|
||||
// Optional component.
|
||||
Resolve(uid, ref appearance, false);
|
||||
|
||||
if (standingState.Standing)
|
||||
return true;
|
||||
|
||||
if (!force)
|
||||
{
|
||||
var msg = new StandAttemptEvent();
|
||||
RaiseLocalEvent(uid, msg, false);
|
||||
|
||||
if (msg.Cancelled)
|
||||
return false;
|
||||
}
|
||||
|
||||
standingState.Standing = true;
|
||||
Dirty(uid, standingState);
|
||||
RaiseLocalEvent(uid, new StoodEvent(), false);
|
||||
|
||||
_appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical, appearance);
|
||||
|
||||
if (TryComp(uid, out FixturesComponent? fixtureComponent))
|
||||
{
|
||||
foreach (var key in standingState.ChangedFixtures)
|
||||
{
|
||||
if (fixtureComponent.Fixtures.TryGetValue(key, out var fixture))
|
||||
_physics.SetCollisionMask(uid, key, fixture, fixture.CollisionMask | StandingCollisionLayer, fixtureComponent);
|
||||
}
|
||||
}
|
||||
standingState.ChangedFixtures.Clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class DropHandItemsEvent : EventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe if you can potentially block a down attempt.
|
||||
/// </summary>
|
||||
public sealed class DownAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe if you can potentially block a stand attempt.
|
||||
/// </summary>
|
||||
public sealed class StandAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity becomes standing
|
||||
/// </summary>
|
||||
public sealed class StoodEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when an entity is not standing
|
||||
/// </summary>
|
||||
public sealed class DownedEvent : EntityEventArgs
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -30,7 +30,7 @@ public abstract class SharedStunSystem : EntitySystem
|
|||
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!;
|
||||
[Dependency] private readonly StandingStateSystem _standingState = default!;
|
||||
[Dependency] private readonly SharedStandingStateSystem _standingState = default!;
|
||||
[Dependency] private readonly StatusEffectsSystem _statusEffect = default!;
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ namespace Content.Shared.Traits.Assorted;
|
|||
public sealed class LegsParalyzedSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifierSystem = default!;
|
||||
[Dependency] private readonly StandingStateSystem _standingSystem = default!;
|
||||
[Dependency] private readonly SharedStandingStateSystem _standingSystem = default!;
|
||||
[Dependency] private readonly SharedBodySystem _bodySystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
|
|
|
|||
|
|
@ -249,6 +249,11 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||
EntityManager.DirtyField(uid, gun, nameof(GunComponent.ShotCounter));
|
||||
}
|
||||
|
||||
public void SetTarget(GunComponent gun, EntityUid target)
|
||||
{
|
||||
gun.Target = target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to shoot at the target coordinates. Resets the shot counter after every shot.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
using Content.Shared.Actions;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._Sunrise.Animations;
|
||||
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class EmoteAnimationComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string AnimationId = "none";
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public partial class EmoteAnimationComponentState : ComponentState
|
||||
{
|
||||
public string AnimationId { get; init; }
|
||||
|
||||
public EmoteAnimationComponentState(string animationId)
|
||||
{
|
||||
AnimationId = animationId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed partial class EmoteActionEvent : InstantActionEvent
|
||||
{
|
||||
[ViewVariables, DataField("emote", readOnly: true, required: true)]
|
||||
public string Emote = default!;
|
||||
};
|
||||
|
|
@ -36,7 +36,7 @@ public abstract partial class SharedSurgerySystem : EntitySystem
|
|||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
[Dependency] private readonly RotateToFaceSystem _rotateToFace = default!;
|
||||
[Dependency] private readonly StandingStateSystem _standing = default!;
|
||||
[Dependency] private readonly SharedStandingStateSystem _standing = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly SharedBodySystem _body = default!;
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
user-action-control-nothing = Nothing to display here yet.
|
||||
user-action-control-tab-emote = Emotes
|
||||
user-action-control-tab-verbs = Verbs
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
user-action-control-nothing = Пока нечего отображать.
|
||||
user-action-control-tab-emote = Эмоции
|
||||
user-action-control-tab-verbs = Действия
|
||||
|
|
@ -249,6 +249,7 @@
|
|||
- type: Carriable
|
||||
- type: CanEscapeInventory
|
||||
- type: Mood
|
||||
- type: EmoteAnimation
|
||||
# Sunrise-End
|
||||
- type: Barotrauma
|
||||
damage:
|
||||
|
|
|
|||
|
|
@ -253,6 +253,7 @@
|
|||
- Hands
|
||||
blacklist:
|
||||
components:
|
||||
- Ghost
|
||||
- BorgChassis
|
||||
chatMessages: ["chat-emote-msg-clap"]
|
||||
chatTriggers:
|
||||
|
|
@ -281,6 +282,7 @@
|
|||
blacklist:
|
||||
components:
|
||||
- BorgChassis
|
||||
- Ghost
|
||||
chatMessages: ["chat-emote-msg-snap"] # snaps <{THEIR($ent)}> fingers?
|
||||
chatTriggers:
|
||||
- snap
|
||||
|
|
@ -319,6 +321,7 @@
|
|||
blacklist:
|
||||
components:
|
||||
- BorgChassis
|
||||
- Ghost
|
||||
chatMessages: ["chat-emote-msg-thump"]
|
||||
chatTriggers:
|
||||
- thump
|
||||
|
|
@ -343,6 +346,7 @@
|
|||
blacklist:
|
||||
components:
|
||||
- BorgChassis
|
||||
- Ghost
|
||||
chatMessages: ["chat-emote-msg-salute"]
|
||||
chatTriggers:
|
||||
- salute
|
||||
|
|
|
|||
69
Resources/Prototypes/_Sunrise/Actions/animations.yml
Normal file
69
Resources/Prototypes/_Sunrise/Actions/animations.yml
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
- type: emote
|
||||
id: EmoteFlip
|
||||
category: Verb
|
||||
name: Сальто
|
||||
whitelist:
|
||||
components:
|
||||
- Hands
|
||||
blacklist:
|
||||
components:
|
||||
- Ghost
|
||||
- BorgChassis
|
||||
chatMessages: [делает сальто]
|
||||
chatTriggers:
|
||||
- сделал сальто
|
||||
- сделала сальто
|
||||
- делает сальто
|
||||
- устроила сальто
|
||||
- устроил сальто
|
||||
|
||||
- type: emote
|
||||
id: EmoteJump
|
||||
category: Verb
|
||||
name: Прыгнуть
|
||||
whitelist:
|
||||
components:
|
||||
- Hands
|
||||
blacklist:
|
||||
components:
|
||||
- Ghost
|
||||
- BorgChassis
|
||||
chatMessages: [прыгает]
|
||||
chatTriggers:
|
||||
- прыгает
|
||||
- прыгнула
|
||||
- прыгнул
|
||||
- подпрыгнул
|
||||
- подпрыгнула
|
||||
- подскакивает
|
||||
|
||||
- type: emote
|
||||
id: EmoteTurn
|
||||
category: Verb
|
||||
name: Танцевать
|
||||
whitelist:
|
||||
components:
|
||||
- Hands
|
||||
blacklist:
|
||||
components:
|
||||
- Ghost
|
||||
- BorgChassis
|
||||
chatMessages: [танцует]
|
||||
chatTriggers:
|
||||
- танцует
|
||||
- кружится
|
||||
- оборачивается
|
||||
- покружилась
|
||||
- покружился
|
||||
|
||||
- type: emote
|
||||
id: EmoteLay
|
||||
category: Verb
|
||||
name: Лечь/Встать
|
||||
whitelist:
|
||||
components:
|
||||
- Hands
|
||||
blacklist:
|
||||
components:
|
||||
- Ghost
|
||||
- BorgChassis
|
||||
|
|
@ -587,3 +587,6 @@ binds:
|
|||
- function: CockGun # Sunrise-Edit
|
||||
type: State
|
||||
key: C
|
||||
- function: ToggleStanding
|
||||
type: State
|
||||
key: R
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue