Merge branch 'master' into replace-sounds-with-sound-specifier
# Conflicts: # Content.Server/Hands/Components/HandsComponent.cs # Content.Server/Light/Components/ExpendableLightComponent.cs # Content.Shared/Light/Component/SharedExpendableLightComponent.cs
This commit is contained in:
@@ -1,9 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Content.Client.Actions.Assignments;
|
using Content.Client.Actions.Assignments;
|
||||||
using Content.Client.Actions.UI;
|
using Content.Client.Actions.UI;
|
||||||
using Content.Client.Hands;
|
using Content.Client.Hands;
|
||||||
using Content.Client.Inventory;
|
using Content.Client.Inventory;
|
||||||
using Content.Client.Items.UI;
|
using Content.Client.Items.Managers;
|
||||||
using Content.Shared.Actions.Components;
|
using Content.Shared.Actions.Components;
|
||||||
using Content.Shared.Actions.Prototypes;
|
using Content.Shared.Actions.Prototypes;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
@@ -26,12 +25,13 @@ namespace Content.Client.Actions
|
|||||||
public const byte Slots = 10;
|
public const byte Slots = 10;
|
||||||
|
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
|
[Dependency] private readonly IItemSlotManager _itemSlotManager = default!;
|
||||||
|
|
||||||
[ComponentDependency] private readonly HandsComponent? _handsComponent = null;
|
[ComponentDependency] private readonly HandsComponent? _handsComponent = null;
|
||||||
[ComponentDependency] private readonly ClientInventoryComponent? _inventoryComponent = null;
|
[ComponentDependency] private readonly ClientInventoryComponent? _inventoryComponent = null;
|
||||||
|
|
||||||
private ActionsUI? _ui;
|
private ActionsUI? _ui;
|
||||||
private readonly List<ItemSlotButton> _highlightingItemSlots = new();
|
private EntityUid _highlightedEntity;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Current assignments for all hotbars / slots for this entity.
|
/// Current assignments for all hotbars / slots for this entity.
|
||||||
@@ -225,26 +225,8 @@ namespace Content.Client.Actions
|
|||||||
{
|
{
|
||||||
StopHighlightingItemSlots();
|
StopHighlightingItemSlots();
|
||||||
|
|
||||||
// figure out if it's in hand or inventory and highlight it
|
_highlightedEntity = item.Uid;
|
||||||
foreach (var hand in _handsComponent!.Gui!.Hands)
|
_itemSlotManager.HighlightEntity(item.Uid);
|
||||||
{
|
|
||||||
if (hand.HeldItem != item || hand.HandButton == null) continue;
|
|
||||||
_highlightingItemSlots.Add(hand.HandButton);
|
|
||||||
hand.HandButton.Highlight(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var (slot, slotItem) in _inventoryComponent!.AllSlots)
|
|
||||||
{
|
|
||||||
if (slotItem != item) continue;
|
|
||||||
foreach (var itemSlotButton in
|
|
||||||
_inventoryComponent.InterfaceController.GetItemSlotButtons(slot))
|
|
||||||
{
|
|
||||||
_highlightingItemSlots.Add(itemSlotButton);
|
|
||||||
itemSlotButton.Highlight(true);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -252,11 +234,11 @@ namespace Content.Client.Actions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void StopHighlightingItemSlots()
|
public void StopHighlightingItemSlots()
|
||||||
{
|
{
|
||||||
foreach (var itemSlot in _highlightingItemSlots)
|
if (_highlightedEntity == default)
|
||||||
{
|
return;
|
||||||
itemSlot.Highlight(false);
|
|
||||||
}
|
_itemSlotManager.UnHighlightEntity(_highlightedEntity);
|
||||||
_highlightingItemSlots.Clear();
|
_highlightedEntity = default;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ToggleActionsMenu()
|
public void ToggleActionsMenu()
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Content.Shared.Clothing;
|
using Content.Shared.Clothing;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
namespace Content.Client.Cloning
|
namespace Content.Client.Clothing
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed class MagbootsComponent : SharedMagbootsComponent
|
public sealed class MagbootsComponent : SharedMagbootsComponent
|
||||||
@@ -81,7 +81,6 @@ namespace Content.Client.Entry
|
|||||||
factory.RegisterClass<SharedCargoConsoleComponent>();
|
factory.RegisterClass<SharedCargoConsoleComponent>();
|
||||||
factory.RegisterClass<SharedReagentDispenserComponent>();
|
factory.RegisterClass<SharedReagentDispenserComponent>();
|
||||||
factory.RegisterClass<SharedChemMasterComponent>();
|
factory.RegisterClass<SharedChemMasterComponent>();
|
||||||
factory.RegisterClass<SharedMicrowaveComponent>();
|
|
||||||
factory.RegisterClass<SharedGravityGeneratorComponent>();
|
factory.RegisterClass<SharedGravityGeneratorComponent>();
|
||||||
factory.RegisterClass<SharedAMEControllerComponent>();
|
factory.RegisterClass<SharedAMEControllerComponent>();
|
||||||
|
|
||||||
|
|||||||
3
Content.Client/Hands/HandVirtualPullItemStatus.xaml
Normal file
3
Content.Client/Hands/HandVirtualPullItemStatus.xaml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<Control xmlns="https://spacestation14.io">
|
||||||
|
<Label StyleClasses="ItemStatus" Text="Pulling" />
|
||||||
|
</Control>
|
||||||
13
Content.Client/Hands/HandVirtualPullItemStatus.xaml.cs
Normal file
13
Content.Client/Hands/HandVirtualPullItemStatus.xaml.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
|
namespace Content.Client.Hands
|
||||||
|
{
|
||||||
|
public sealed class HandVirtualPullItemStatus : Control
|
||||||
|
{
|
||||||
|
public HandVirtualPullItemStatus()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Client.Animations;
|
|
||||||
using Content.Client.HUD;
|
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Item;
|
using Content.Shared.Item;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Network;
|
|
||||||
using Robust.Shared.Players;
|
|
||||||
using Robust.Shared.Timing;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Client.Hands
|
namespace Content.Client.Hands
|
||||||
{
|
{
|
||||||
@@ -18,16 +11,7 @@ namespace Content.Client.Hands
|
|||||||
[ComponentReference(typeof(SharedHandsComponent))]
|
[ComponentReference(typeof(SharedHandsComponent))]
|
||||||
public class HandsComponent : SharedHandsComponent
|
public class HandsComponent : SharedHandsComponent
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameHud _gameHud = default!;
|
public HandsGui? Gui { get; set; }
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public HandsGui? Gui { get; private set; }
|
|
||||||
|
|
||||||
protected override void OnRemove()
|
|
||||||
{
|
|
||||||
ClearGui();
|
|
||||||
base.OnRemove();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||||
{
|
{
|
||||||
@@ -38,94 +22,23 @@ namespace Content.Client.Hands
|
|||||||
|
|
||||||
foreach (var handState in state.Hands)
|
foreach (var handState in state.Hands)
|
||||||
{
|
{
|
||||||
var newHand = new Hand(handState.Name, handState.Enabled, handState.Location);
|
var newHand = new Hand(handState.Name, handState.Location);
|
||||||
Hands.Add(newHand);
|
Hands.Add(newHand);
|
||||||
}
|
}
|
||||||
|
|
||||||
ActiveHand = state.ActiveHand;
|
ActiveHand = state.ActiveHand;
|
||||||
|
|
||||||
UpdateHandContainers();
|
UpdateHandContainers();
|
||||||
UpdateHandVisualizer();
|
UpdateHandVisualizer();
|
||||||
UpdateHandsGuiState();
|
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new HandsModifiedMessage { Hands = this });
|
||||||
}
|
|
||||||
|
|
||||||
public void SettupGui()
|
|
||||||
{
|
|
||||||
if (Gui == null)
|
|
||||||
{
|
|
||||||
Gui = new HandsGui();
|
|
||||||
_gameHud.HandsContainer.AddChild(Gui);
|
|
||||||
Gui.HandClick += args => OnHandClick(args.HandClicked);
|
|
||||||
Gui.HandActivate += args => OnActivateInHand(args.HandUsed);
|
|
||||||
UpdateHandsGuiState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearGui()
|
|
||||||
{
|
|
||||||
Gui?.Dispose();
|
|
||||||
Gui = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null)
|
|
||||||
{
|
|
||||||
base.HandleNetworkMessage(message, netChannel, session);
|
|
||||||
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case PickupAnimationMessage msg:
|
|
||||||
RunPickupAnimation(msg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void HandsModified()
|
public override void HandsModified()
|
||||||
{
|
{
|
||||||
base.HandsModified();
|
|
||||||
|
|
||||||
UpdateHandContainers();
|
UpdateHandContainers();
|
||||||
UpdateHandVisualizer();
|
UpdateHandVisualizer();
|
||||||
UpdateHandsGuiState();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnHandClick(string handClicked)
|
base.HandsModified();
|
||||||
{
|
|
||||||
if (!TryGetHand(handClicked, out var pressedHand))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!TryGetActiveHand(out var activeHand))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var pressedEntity = pressedHand.HeldEntity;
|
|
||||||
var activeEntity = activeHand.HeldEntity;
|
|
||||||
|
|
||||||
if (pressedHand == activeHand && activeEntity != null)
|
|
||||||
{
|
|
||||||
SendNetworkMessage(new UseInHandMsg()); //use item in hand
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pressedHand != activeHand && pressedEntity == null)
|
|
||||||
{
|
|
||||||
SendNetworkMessage(new ClientChangedHandMsg(pressedHand.Name)); //swap hand
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pressedHand != activeHand && pressedEntity != null && activeEntity != null)
|
|
||||||
{
|
|
||||||
SendNetworkMessage(new ClientAttackByInHandMsg(pressedHand.Name)); //use active item on held item
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pressedHand != activeHand && pressedEntity != null && activeEntity == null)
|
|
||||||
{
|
|
||||||
SendNetworkMessage(new MoveItemFromHandMsg(pressedHand.Name)); //move item in hand to active hand
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnActivateInHand(string handActivated)
|
|
||||||
{
|
|
||||||
SendNetworkMessage(new ActivateInHandMsg(handActivated));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateHandContainers()
|
public void UpdateHandContainers()
|
||||||
@@ -149,27 +62,10 @@ namespace Content.Client.Hands
|
|||||||
appearance.SetData(HandsVisuals.VisualState, GetHandsVisualState());
|
appearance.SetData(HandsVisuals.VisualState, GetHandsVisualState());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateHandsGuiState()
|
|
||||||
{
|
|
||||||
Gui?.SetState(GetHandsGuiState());
|
|
||||||
}
|
|
||||||
|
|
||||||
private HandsGuiState GetHandsGuiState()
|
|
||||||
{
|
|
||||||
var handStates = new List<GuiHand>();
|
|
||||||
|
|
||||||
foreach (var hand in ReadOnlyHands)
|
|
||||||
{
|
|
||||||
var handState = new GuiHand(hand.Name, hand.Location, hand.HeldEntity, hand.Enabled);
|
|
||||||
handStates.Add(handState);
|
|
||||||
}
|
|
||||||
return new HandsGuiState(handStates, ActiveHand);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HandsVisualState GetHandsVisualState()
|
private HandsVisualState GetHandsVisualState()
|
||||||
{
|
{
|
||||||
var hands = new List<HandVisualState>();
|
var hands = new List<HandVisualState>();
|
||||||
foreach (var hand in ReadOnlyHands)
|
foreach (var hand in Hands)
|
||||||
{
|
{
|
||||||
if (hand.HeldEntity == null)
|
if (hand.HeldEntity == null)
|
||||||
continue;
|
continue;
|
||||||
@@ -182,16 +78,5 @@ namespace Content.Client.Hands
|
|||||||
}
|
}
|
||||||
return new(hands);
|
return new(hands);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RunPickupAnimation(PickupAnimationMessage msg)
|
|
||||||
{
|
|
||||||
if (!Owner.EntityManager.TryGetEntity(msg.EntityUid, out var entity))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!IoCManager.Resolve<IGameTiming>().IsFirstTimePredicted)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ReusableAnimations.AnimateEntityPickup(entity, msg.InitialPosition, msg.PickupDirection);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
Content.Client/Hands/HandsGui.xaml
Normal file
6
Content.Client/Hands/HandsGui.xaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<Control xmlns="https://spacestation14.io">
|
||||||
|
<BoxContainer Orientation="Vertical">
|
||||||
|
<Control Name="StatusContainer" />
|
||||||
|
<BoxContainer Name="HandsContainer" Orientation="Horizontal" HorizontalAlignment="Center" />
|
||||||
|
</BoxContainer>
|
||||||
|
</Control>
|
||||||
@@ -1,87 +1,84 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
|
||||||
using Content.Client.HUD;
|
using Content.Client.HUD;
|
||||||
using Content.Client.Items.Managers;
|
using Content.Client.Items.Managers;
|
||||||
using Content.Client.Items.UI;
|
using Content.Client.Items.UI;
|
||||||
using Content.Client.Resources;
|
using Content.Client.Resources;
|
||||||
using Content.Shared;
|
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Input;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.Player;
|
|
||||||
using Robust.Client.ResourceManagement;
|
using Robust.Client.ResourceManagement;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Input;
|
using Robust.Shared.Input;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
|
||||||
|
|
||||||
namespace Content.Client.Hands
|
namespace Content.Client.Hands
|
||||||
{
|
{
|
||||||
public class HandsGui : Control
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class HandsGui : Control
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||||
[Dependency] private readonly IItemSlotManager _itemSlotManager = default!;
|
[Dependency] private readonly IItemSlotManager _itemSlotManager = default!;
|
||||||
[Dependency] private readonly IGameHud _gameHud = default!;
|
[Dependency] private readonly IGameHud _gameHud = default!;
|
||||||
[Dependency] private readonly INetConfigurationManager _configManager = default!;
|
[Dependency] private readonly INetConfigurationManager _configManager = default!;
|
||||||
|
|
||||||
|
private readonly HandsSystem _handsSystem;
|
||||||
|
private readonly HandsComponent _handsComponent;
|
||||||
|
|
||||||
private Texture StorageTexture => _gameHud.GetHudTexture("back.png");
|
private Texture StorageTexture => _gameHud.GetHudTexture("back.png");
|
||||||
private Texture BlockedTexture => _resourceCache.GetTexture("/Textures/Interface/Inventory/blocked.png");
|
private Texture BlockedTexture => _resourceCache.GetTexture("/Textures/Interface/Inventory/blocked.png");
|
||||||
|
|
||||||
private ItemStatusPanel StatusPanel { get; }
|
private ItemStatusPanel StatusPanel { get; }
|
||||||
|
|
||||||
private BoxContainer HandsContainer { get; }
|
[ViewVariables] private GuiHand[] _hands = Array.Empty<GuiHand>();
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public IReadOnlyList<GuiHand> Hands => _hands;
|
|
||||||
private List<GuiHand> _hands = new();
|
|
||||||
|
|
||||||
private string? ActiveHand { get; set; }
|
private string? ActiveHand { get; set; }
|
||||||
|
|
||||||
public Action<HandClickEventArgs>? HandClick; //TODO: Move to Eventbus
|
public HandsGui(HandsComponent hands, HandsSystem handsSystem)
|
||||||
|
|
||||||
public Action<HandActivateEventArgs>? HandActivate; //TODO: Move to Eventbus
|
|
||||||
|
|
||||||
public HandsGui()
|
|
||||||
{
|
{
|
||||||
IoCManager.InjectDependencies(this);
|
_handsComponent = hands;
|
||||||
_configManager.OnValueChanged(CCVars.HudTheme, UpdateHudTheme);
|
_handsSystem = handsSystem;
|
||||||
|
|
||||||
AddChild(new BoxContainer
|
RobustXamlLoader.Load(this);
|
||||||
{
|
IoCManager.InjectDependencies(this);
|
||||||
Orientation = LayoutOrientation.Horizontal,
|
|
||||||
SeparationOverride = 0,
|
StatusPanel = ItemStatusPanel.FromSide(HandLocation.Middle);
|
||||||
HorizontalAlignment = HAlignment.Center,
|
StatusContainer.AddChild(StatusPanel);
|
||||||
Children =
|
StatusPanel.SetPositionFirst();
|
||||||
{
|
|
||||||
new BoxContainer
|
|
||||||
{
|
|
||||||
Orientation = LayoutOrientation.Vertical,
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
(StatusPanel = ItemStatusPanel.FromSide(HandLocation.Middle)),
|
|
||||||
(HandsContainer = new BoxContainer
|
|
||||||
{
|
|
||||||
Orientation = LayoutOrientation.Horizontal,
|
|
||||||
HorizontalAlignment = HAlignment.Center
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetState(HandsGuiState state)
|
protected override void EnteredTree()
|
||||||
{
|
{
|
||||||
|
base.EnteredTree();
|
||||||
|
|
||||||
|
_handsSystem.GuiStateUpdated += HandsSystemOnGuiStateUpdated;
|
||||||
|
_configManager.OnValueChanged(CCVars.HudTheme, UpdateHudTheme);
|
||||||
|
|
||||||
|
HandsSystemOnGuiStateUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ExitedTree()
|
||||||
|
{
|
||||||
|
base.ExitedTree();
|
||||||
|
|
||||||
|
_handsSystem.GuiStateUpdated -= HandsSystemOnGuiStateUpdated;
|
||||||
|
_configManager.UnsubValueChanged(CCVars.HudTheme, UpdateHudTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandsSystemOnGuiStateUpdated()
|
||||||
|
{
|
||||||
|
var state = _handsSystem.GetGuiState();
|
||||||
|
|
||||||
ActiveHand = state.ActiveHand;
|
ActiveHand = state.ActiveHand;
|
||||||
_hands = state.GuiHands;
|
_hands = state.GuiHands;
|
||||||
|
Array.Sort(_hands, HandOrderComparer.Instance);
|
||||||
UpdateGui();
|
UpdateGui();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,12 +94,15 @@ namespace Content.Client.Hands
|
|||||||
|
|
||||||
var handName = hand.Name;
|
var handName = hand.Name;
|
||||||
newButton.OnPressed += args => OnHandPressed(args, handName);
|
newButton.OnPressed += args => OnHandPressed(args, handName);
|
||||||
newButton.OnStoragePressed += args => OnStoragePressed(handName);
|
newButton.OnStoragePressed += _ => OnStoragePressed(handName);
|
||||||
|
|
||||||
newButton.Blocked.Visible = !hand.Enabled;
|
|
||||||
|
|
||||||
_itemSlotManager.SetItemSlot(newButton, hand.HeldItem);
|
_itemSlotManager.SetItemSlot(newButton, hand.HeldItem);
|
||||||
|
|
||||||
|
// Show blocked overlay if hand is pulling.
|
||||||
|
newButton.Blocked.Visible =
|
||||||
|
hand.HeldItem != null && hand.HeldItem.HasComponent<HandVirtualPullComponent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TryGetActiveHand(out var activeHand))
|
if (TryGetActiveHand(out var activeHand))
|
||||||
{
|
{
|
||||||
activeHand.HandButton.SetActiveHand(true);
|
activeHand.HandButton.SetActiveHand(true);
|
||||||
@@ -114,7 +114,7 @@ namespace Content.Client.Hands
|
|||||||
{
|
{
|
||||||
if (args.Function == EngineKeyFunctions.UIClick)
|
if (args.Function == EngineKeyFunctions.UIClick)
|
||||||
{
|
{
|
||||||
HandClick?.Invoke(new HandClickEventArgs(handName));
|
_handsSystem.UIHandClick(_handsComponent, handName);
|
||||||
}
|
}
|
||||||
else if (TryGetHand(handName, out var hand))
|
else if (TryGetHand(handName, out var hand))
|
||||||
{
|
{
|
||||||
@@ -124,7 +124,7 @@ namespace Content.Client.Hands
|
|||||||
|
|
||||||
private void OnStoragePressed(string handName)
|
private void OnStoragePressed(string handName)
|
||||||
{
|
{
|
||||||
HandActivate?.Invoke(new HandActivateEventArgs(handName));
|
_handsSystem.UIHandActivate(handName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetActiveHand([NotNullWhen(true)] out GuiHand? activeHand)
|
private bool TryGetActiveHand([NotNullWhen(true)] out GuiHand? activeHand)
|
||||||
@@ -145,6 +145,7 @@ namespace Content.Client.Hands
|
|||||||
if (hand.Name == handName)
|
if (hand.Name == handName)
|
||||||
foundHand = hand;
|
foundHand = hand;
|
||||||
}
|
}
|
||||||
|
|
||||||
return foundHand != null;
|
return foundHand != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +154,9 @@ namespace Content.Client.Hands
|
|||||||
base.FrameUpdate(args);
|
base.FrameUpdate(args);
|
||||||
|
|
||||||
foreach (var hand in _hands)
|
foreach (var hand in _hands)
|
||||||
|
{
|
||||||
_itemSlotManager.UpdateCooldown(hand.HandButton, hand.HeldItem);
|
_itemSlotManager.UpdateCooldown(hand.HandButton, hand.HeldItem);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private HandButton MakeHandButton(HandLocation buttonLocation)
|
private HandButton MakeHandButton(HandLocation buttonLocation)
|
||||||
@@ -173,23 +176,31 @@ namespace Content.Client.Hands
|
|||||||
UpdateGui();
|
UpdateGui();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class HandClickEventArgs
|
private sealed class HandOrderComparer : IComparer<GuiHand>
|
||||||
{
|
{
|
||||||
public string HandClicked { get; }
|
public static readonly HandOrderComparer Instance = new();
|
||||||
|
|
||||||
public HandClickEventArgs(string handClicked)
|
public int Compare(GuiHand? x, GuiHand? y)
|
||||||
{
|
{
|
||||||
HandClicked = handClicked;
|
if (ReferenceEquals(x, y)) return 0;
|
||||||
}
|
if (ReferenceEquals(null, y)) return 1;
|
||||||
}
|
if (ReferenceEquals(null, x)) return -1;
|
||||||
|
|
||||||
public class HandActivateEventArgs
|
var orderX = Map(x.HandLocation);
|
||||||
{
|
var orderY = Map(y.HandLocation);
|
||||||
public string HandUsed { get; }
|
|
||||||
|
|
||||||
public HandActivateEventArgs(string handUsed)
|
return orderX.CompareTo(orderY);
|
||||||
{
|
|
||||||
HandUsed = handUsed;
|
static int Map(HandLocation loc)
|
||||||
|
{
|
||||||
|
return loc switch
|
||||||
|
{
|
||||||
|
HandLocation.Left => 3,
|
||||||
|
HandLocation.Middle => 2,
|
||||||
|
HandLocation.Right => 1,
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(loc), loc, null)
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,7 +214,7 @@ namespace Content.Client.Hands
|
|||||||
/// The set of hands to be displayed.
|
/// The set of hands to be displayed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public List<GuiHand> GuiHands { get; } = new();
|
public GuiHand[] GuiHands { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of the currently active hand.
|
/// The name of the currently active hand.
|
||||||
@@ -211,7 +222,7 @@ namespace Content.Client.Hands
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public string? ActiveHand { get; }
|
public string? ActiveHand { get; }
|
||||||
|
|
||||||
public HandsGuiState(List<GuiHand> guiHands, string? activeHand = null)
|
public HandsGuiState(GuiHand[] guiHands, string? activeHand = null)
|
||||||
{
|
{
|
||||||
GuiHands = guiHands;
|
GuiHands = guiHands;
|
||||||
ActiveHand = activeHand;
|
ActiveHand = activeHand;
|
||||||
@@ -247,18 +258,11 @@ namespace Content.Client.Hands
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public HandButton HandButton { get; set; } = default!;
|
public HandButton HandButton { get; set; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
public GuiHand(string name, HandLocation handLocation, IEntity? heldItem)
|
||||||
/// If this hand can be used by the player.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public bool Enabled { get; }
|
|
||||||
|
|
||||||
public GuiHand(string name, HandLocation handLocation, IEntity? heldItem, bool enabled)
|
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
HandLocation = handLocation;
|
HandLocation = handLocation;
|
||||||
HeldItem = heldItem;
|
HeldItem = heldItem;
|
||||||
Enabled = enabled;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
using Content.Shared.Hands;
|
|
||||||
using Content.Shared.Hands.Components;
|
|
||||||
using Content.Shared.Input;
|
|
||||||
using Robust.Client.GameObjects;
|
|
||||||
using Robust.Shared.Containers;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Input.Binding;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Players;
|
|
||||||
|
|
||||||
namespace Content.Client.Hands
|
|
||||||
{
|
|
||||||
internal sealed class HandsSystem : SharedHandsSystem
|
|
||||||
{
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<HandsComponent, PlayerAttachedEvent>((_, component, _) => component.SettupGui());
|
|
||||||
SubscribeLocalEvent<HandsComponent, PlayerDetachedEvent>((_, component, _) => component.ClearGui());
|
|
||||||
|
|
||||||
CommandBinds.Builder
|
|
||||||
.Bind(ContentKeyFunctions.SwapHands, InputCmdHandler.FromDelegate(SwapHandsPressed))
|
|
||||||
.Bind(ContentKeyFunctions.Drop, new PointerInputCmdHandler(DropPressed))
|
|
||||||
.Register<HandsSystem>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Shutdown()
|
|
||||||
{
|
|
||||||
CommandBinds.Unregister<HandsSystem>();
|
|
||||||
base.Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SwapHandsPressed(ICommonSession? session)
|
|
||||||
{
|
|
||||||
if (session == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var player = session.AttachedEntity;
|
|
||||||
|
|
||||||
if (player == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!player.TryGetComponent(out SharedHandsComponent? hands))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!hands.TryGetSwapHandsResult(out var nextHand))
|
|
||||||
return;
|
|
||||||
|
|
||||||
EntityManager.RaisePredictiveEvent(new RequestSetHandEvent(nextHand));
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool DropPressed(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
|
|
||||||
{
|
|
||||||
if (session == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var player = session.AttachedEntity;
|
|
||||||
|
|
||||||
if (player == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!player.TryGetComponent(out SharedHandsComponent? hands))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var activeHand = hands.ActiveHand;
|
|
||||||
|
|
||||||
if (activeHand == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
EntityManager.RaisePredictiveEvent(new RequestDropHeldEntityEvent(activeHand, coords));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void HandleContainerModified(EntityUid uid, SharedHandsComponent component, ContainerModifiedMessage args)
|
|
||||||
{
|
|
||||||
component.HandsModified();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
18
Content.Client/Hands/Systems/HandVirtualPullSystem.cs
Normal file
18
Content.Client/Hands/Systems/HandVirtualPullSystem.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using Content.Client.Items;
|
||||||
|
using Content.Shared.Hands.Components;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Client.Hands
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed class HandVirtualPullSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
Subs.ItemStatus<HandVirtualPullComponent>(_ => new HandVirtualPullItemStatus());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
149
Content.Client/Hands/Systems/HandsSystem.cs
Normal file
149
Content.Client/Hands/Systems/HandsSystem.cs
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Content.Client.Animations;
|
||||||
|
using Content.Client.HUD;
|
||||||
|
using Content.Shared.Hands;
|
||||||
|
using Content.Shared.Hands.Components;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Client.Player;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Input.Binding;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Client.Hands
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed class HandsSystem : SharedHandsSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
[Dependency] private readonly IGameHud _gameHud = default!;
|
||||||
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
|
|
||||||
|
public event Action? GuiStateUpdated;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<HandsComponent, PlayerAttachedEvent>(HandlePlayerAttached);
|
||||||
|
SubscribeLocalEvent<HandsComponent, PlayerDetachedEvent>(HandlePlayerDetached);
|
||||||
|
SubscribeLocalEvent<HandsComponent, ComponentRemove>(HandleCompRemove);
|
||||||
|
SubscribeLocalEvent<HandsModifiedMessage>(HandleHandsModified);
|
||||||
|
|
||||||
|
SubscribeNetworkEvent<PickupAnimationMessage>(HandlePickupAnimation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Shutdown()
|
||||||
|
{
|
||||||
|
CommandBinds.Unregister<HandsSystem>();
|
||||||
|
base.Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleHandsModified(HandsModifiedMessage ev)
|
||||||
|
{
|
||||||
|
if (ev.Hands.Owner == _playerManager.LocalPlayer?.ControlledEntity)
|
||||||
|
GuiStateUpdated?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void HandleContainerModified(EntityUid uid, SharedHandsComponent component, ContainerModifiedMessage args)
|
||||||
|
{
|
||||||
|
if (uid == _playerManager.LocalPlayer?.ControlledEntity?.Uid)
|
||||||
|
GuiStateUpdated?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandlePickupAnimation(PickupAnimationMessage msg)
|
||||||
|
{
|
||||||
|
if (!EntityManager.TryGetEntity(msg.EntityUid, out var entity))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_gameTiming.IsFirstTimePredicted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ReusableAnimations.AnimateEntityPickup(entity, msg.InitialPosition, msg.PickupDirection);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HandsGuiState GetGuiState()
|
||||||
|
{
|
||||||
|
var player = _playerManager.LocalPlayer?.ControlledEntity;
|
||||||
|
|
||||||
|
if (player == null || !player.TryGetComponent(out HandsComponent? hands))
|
||||||
|
return new HandsGuiState(Array.Empty<GuiHand>());
|
||||||
|
|
||||||
|
var states = hands.Hands
|
||||||
|
.Select(hand => new GuiHand(hand.Name, hand.Location, hand.HeldEntity))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
return new HandsGuiState(states, hands.ActiveHand);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UIHandClick(HandsComponent hands, string handName)
|
||||||
|
{
|
||||||
|
if (!hands.TryGetHand(handName, out var pressedHand))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!hands.TryGetActiveHand(out var activeHand))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var pressedEntity = pressedHand.HeldEntity;
|
||||||
|
var activeEntity = activeHand.HeldEntity;
|
||||||
|
|
||||||
|
if (pressedHand == activeHand && activeEntity != null)
|
||||||
|
{
|
||||||
|
// use item in hand
|
||||||
|
// it will always be attack_self() in my heart.
|
||||||
|
RaiseNetworkEvent(new UseInHandMsg());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pressedHand != activeHand && pressedEntity == null)
|
||||||
|
{
|
||||||
|
// change active hand
|
||||||
|
RaiseNetworkEvent(new RequestSetHandEvent(handName));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pressedHand != activeHand && pressedEntity != null && activeEntity != null)
|
||||||
|
{
|
||||||
|
// use active item on held item
|
||||||
|
RaiseNetworkEvent(new ClientInteractUsingInHandMsg(pressedHand.Name));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pressedHand != activeHand && pressedEntity != null && activeEntity == null)
|
||||||
|
{
|
||||||
|
// use active item on held item
|
||||||
|
RaiseNetworkEvent(new MoveItemFromHandMsg(pressedHand.Name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UIHandActivate(string handName)
|
||||||
|
{
|
||||||
|
RaiseNetworkEvent (new ActivateInHandMsg(handName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandlePlayerAttached(EntityUid uid, HandsComponent component, PlayerAttachedEvent args)
|
||||||
|
{
|
||||||
|
component.Gui = new HandsGui(component, this);
|
||||||
|
_gameHud.HandsContainer.AddChild(component.Gui);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandlePlayerDetached(EntityUid uid, HandsComponent component, PlayerDetachedEvent args)
|
||||||
|
{
|
||||||
|
ClearGui(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleCompRemove(EntityUid uid, HandsComponent component, ComponentRemove args)
|
||||||
|
{
|
||||||
|
ClearGui(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ClearGui(HandsComponent comp)
|
||||||
|
{
|
||||||
|
comp.Gui?.Orphan();
|
||||||
|
comp.Gui = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
32
Content.Client/Items/ItemStatusMessages.cs
Normal file
32
Content.Client/Items/ItemStatusMessages.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Client.Items
|
||||||
|
{
|
||||||
|
public sealed class ItemStatusCollectMessage : EntityEventArgs
|
||||||
|
{
|
||||||
|
public List<Control> Controls = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ItemStatusRegisterExt
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Register an item status control for a component.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="subs">The <see cref="EntitySystem.Subs"/> handle from within entity system initialize.</param>
|
||||||
|
/// <param name="createControl">A delegate to create the actual control.</param>
|
||||||
|
/// <typeparam name="TComp">The type of component for which this control should be made.</typeparam>
|
||||||
|
public static void ItemStatus<TComp>(
|
||||||
|
this EntitySystem.Subscriptions subs,
|
||||||
|
Func<EntityUid, Control> createControl)
|
||||||
|
where TComp : IComponent
|
||||||
|
{
|
||||||
|
subs.SubscribeLocalEvent<TComp, ItemStatusCollectMessage>((uid, _, args) =>
|
||||||
|
{
|
||||||
|
args.Controls.Add(createControl(uid));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Client.Items.UI;
|
using System;
|
||||||
|
using Content.Client.Items.UI;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
@@ -10,5 +11,21 @@ namespace Content.Client.Items.Managers
|
|||||||
void UpdateCooldown(ItemSlotButton? cooldownTexture, IEntity? entity);
|
void UpdateCooldown(ItemSlotButton? cooldownTexture, IEntity? entity);
|
||||||
bool SetItemSlot(ItemSlotButton button, IEntity? entity);
|
bool SetItemSlot(ItemSlotButton button, IEntity? entity);
|
||||||
void HoverInSlot(ItemSlotButton button, IEntity? entity, bool fits);
|
void HoverInSlot(ItemSlotButton button, IEntity? entity, bool fits);
|
||||||
|
event Action<EntitySlotHighlightedEventArgs>? EntityHighlightedUpdated;
|
||||||
|
bool IsHighlighted(EntityUid uid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Highlight all slot controls that contain the specified entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">The UID of the entity to highlight.</param>
|
||||||
|
/// <seealso cref="UnHighlightEntity"/>
|
||||||
|
void HighlightEntity(EntityUid uid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Remove highlighting for the specified entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid">The UID of the entity to unhighlight.</param>
|
||||||
|
/// <seealso cref="HighlightEntity"/>
|
||||||
|
void UnHighlightEntity(EntityUid uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Content.Client.Examine;
|
using Content.Client.Examine;
|
||||||
using Content.Client.Items.UI;
|
using Content.Client.Items.UI;
|
||||||
using Content.Client.Storage;
|
using Content.Client.Storage;
|
||||||
using Content.Client.Verbs;
|
using Content.Client.Verbs;
|
||||||
using Content.Shared.Cooldown;
|
using Content.Shared.Cooldown;
|
||||||
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
@@ -28,6 +31,11 @@ namespace Content.Client.Items.Managers
|
|||||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
[Dependency] private readonly IComponentManager _componentManager = default!;
|
||||||
|
|
||||||
|
private readonly HashSet<EntityUid> _highlightEntities = new();
|
||||||
|
|
||||||
|
public event Action<EntitySlotHighlightedEventArgs>? EntityHighlightedUpdated;
|
||||||
|
|
||||||
public bool SetItemSlot(ItemSlotButton button, IEntity? entity)
|
public bool SetItemSlot(ItemSlotButton button, IEntity? entity)
|
||||||
{
|
{
|
||||||
@@ -38,13 +46,26 @@ namespace Content.Client.Items.Managers
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!entity.TryGetComponent(out ISpriteComponent? sprite))
|
ISpriteComponent? sprite;
|
||||||
|
if (entity.TryGetComponent(out HandVirtualPullComponent? virtPull)
|
||||||
|
&& _componentManager.TryGetComponent(virtPull.PulledEntity, out ISpriteComponent pulledSprite))
|
||||||
|
{
|
||||||
|
sprite = pulledSprite;
|
||||||
|
}
|
||||||
|
else if (!entity.TryGetComponent(out sprite))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
button.ClearHover();
|
button.ClearHover();
|
||||||
button.SpriteView.Sprite = sprite;
|
button.SpriteView.Sprite = sprite;
|
||||||
button.StorageButton.Visible = entity.HasComponent<ClientStorageComponent>();
|
button.StorageButton.Visible = entity.HasComponent<ClientStorageComponent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.Entity = entity?.Uid ?? default;
|
||||||
|
|
||||||
|
// im lazy
|
||||||
|
button.UpdateSlotHighlighted();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,5 +166,38 @@ namespace Content.Client.Items.Managers
|
|||||||
|
|
||||||
button.HoverSpriteView.Sprite = hoverSprite;
|
button.HoverSpriteView.Sprite = hoverSprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsHighlighted(EntityUid uid)
|
||||||
|
{
|
||||||
|
return _highlightEntities.Contains(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HighlightEntity(EntityUid uid)
|
||||||
|
{
|
||||||
|
if (!_highlightEntities.Add(uid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
EntityHighlightedUpdated?.Invoke(new EntitySlotHighlightedEventArgs(uid, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnHighlightEntity(EntityUid uid)
|
||||||
|
{
|
||||||
|
if (!_highlightEntities.Remove(uid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
EntityHighlightedUpdated?.Invoke(new EntitySlotHighlightedEventArgs(uid, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct EntitySlotHighlightedEventArgs
|
||||||
|
{
|
||||||
|
public EntitySlotHighlightedEventArgs(EntityUid entity, bool newHighlighted)
|
||||||
|
{
|
||||||
|
Entity = entity;
|
||||||
|
NewHighlighted = newHighlighted;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityUid Entity { get; }
|
||||||
|
public bool NewHighlighted { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,24 @@
|
|||||||
using System;
|
using System;
|
||||||
using Content.Client.Cooldown;
|
using Content.Client.Cooldown;
|
||||||
|
using Content.Client.Items.Managers;
|
||||||
using Content.Client.Stylesheets;
|
using Content.Client.Stylesheets;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Input;
|
using Robust.Shared.Input;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
|
|
||||||
namespace Content.Client.Items.UI
|
namespace Content.Client.Items.UI
|
||||||
{
|
{
|
||||||
public class ItemSlotButton : Control
|
public class ItemSlotButton : Control, IEntityEventSubscriber
|
||||||
{
|
{
|
||||||
private const string HighlightShader = "SelectionOutlineInrange";
|
private const string HighlightShader = "SelectionOutlineInrange";
|
||||||
|
|
||||||
|
[Dependency] private readonly IItemSlotManager _itemSlotManager = default!;
|
||||||
|
|
||||||
|
public EntityUid Entity { get; set; }
|
||||||
public TextureRect Button { get; }
|
public TextureRect Button { get; }
|
||||||
public SpriteView SpriteView { get; }
|
public SpriteView SpriteView { get; }
|
||||||
public SpriteView HoverSpriteView { get; }
|
public SpriteView HoverSpriteView { get; }
|
||||||
@@ -32,6 +38,8 @@ namespace Content.Client.Items.UI
|
|||||||
|
|
||||||
public ItemSlotButton(Texture texture, Texture storageTexture, string textureName)
|
public ItemSlotButton(Texture texture, Texture storageTexture, string textureName)
|
||||||
{
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
MinSize = (64, 64);
|
MinSize = (64, 64);
|
||||||
|
|
||||||
TextureName = textureName;
|
TextureName = textureName;
|
||||||
@@ -101,6 +109,31 @@ namespace Content.Client.Items.UI
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void EnteredTree()
|
||||||
|
{
|
||||||
|
base.EnteredTree();
|
||||||
|
|
||||||
|
_itemSlotManager.EntityHighlightedUpdated += HandleEntitySlotHighlighted;
|
||||||
|
UpdateSlotHighlighted();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void ExitedTree()
|
||||||
|
{
|
||||||
|
base.ExitedTree();
|
||||||
|
|
||||||
|
_itemSlotManager.EntityHighlightedUpdated -= HandleEntitySlotHighlighted;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleEntitySlotHighlighted(EntitySlotHighlightedEventArgs entitySlotHighlightedEventArgs)
|
||||||
|
{
|
||||||
|
UpdateSlotHighlighted();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateSlotHighlighted()
|
||||||
|
{
|
||||||
|
Highlight(_itemSlotManager.IsHighlighted(Entity));
|
||||||
|
}
|
||||||
|
|
||||||
public void ClearHover()
|
public void ClearHover()
|
||||||
{
|
{
|
||||||
if (EntityHover)
|
if (EntityHover)
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ using Robust.Client.Graphics;
|
|||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using static Content.Client.IoC.StaticIoC;
|
using static Content.Client.IoC.StaticIoC;
|
||||||
@@ -18,6 +20,8 @@ namespace Content.Client.Items.UI
|
|||||||
{
|
{
|
||||||
public class ItemStatusPanel : Control
|
public class ItemStatusPanel : Control
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
private readonly List<(IItemStatus, Control)> _activeStatusComponents = new();
|
private readonly List<(IItemStatus, Control)> _activeStatusComponents = new();
|
||||||
|
|
||||||
@@ -33,6 +37,8 @@ namespace Content.Client.Items.UI
|
|||||||
|
|
||||||
public ItemStatusPanel(Texture texture, StyleBox.Margin cutout, StyleBox.Margin flat, Label.AlignMode textAlign)
|
public ItemStatusPanel(Texture texture, StyleBox.Margin cutout, StyleBox.Margin flat, Label.AlignMode textAlign)
|
||||||
{
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
var panel = new StyleBoxTexture
|
var panel = new StyleBoxTexture
|
||||||
{
|
{
|
||||||
Texture = texture
|
Texture = texture
|
||||||
@@ -117,6 +123,13 @@ namespace Content.Client.Items.UI
|
|||||||
return new ItemStatusPanel(ResC.GetTexture(texture), cutOut, flat, textAlign);
|
return new ItemStatusPanel(ResC.GetTexture(texture), cutOut, flat, textAlign);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
|
{
|
||||||
|
base.FrameUpdate(args);
|
||||||
|
|
||||||
|
UpdateItemName();
|
||||||
|
}
|
||||||
|
|
||||||
public void Update(IEntity? entity)
|
public void Update(IEntity? entity)
|
||||||
{
|
{
|
||||||
if (entity == null)
|
if (entity == null)
|
||||||
@@ -131,12 +144,29 @@ namespace Content.Client.Items.UI
|
|||||||
{
|
{
|
||||||
_entity = entity;
|
_entity = entity;
|
||||||
BuildNewEntityStatus();
|
BuildNewEntityStatus();
|
||||||
_itemNameLabel.Text = entity.Name;
|
|
||||||
|
UpdateItemName();
|
||||||
}
|
}
|
||||||
|
|
||||||
_panel.Visible = true;
|
_panel.Visible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateItemName()
|
||||||
|
{
|
||||||
|
if (_entity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_entity.TryGetComponent(out HandVirtualPullComponent? virtualPull)
|
||||||
|
&& _entityManager.TryGetEntity(virtualPull.PulledEntity, out var pulledEnt))
|
||||||
|
{
|
||||||
|
_itemNameLabel.Text = pulledEnt.Name;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_itemNameLabel.Text = _entity.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ClearOldStatus()
|
private void ClearOldStatus()
|
||||||
{
|
{
|
||||||
_statusContents.RemoveAllChildren();
|
_statusContents.RemoveAllChildren();
|
||||||
@@ -162,6 +192,14 @@ namespace Content.Client.Items.UI
|
|||||||
|
|
||||||
_activeStatusComponents.Add((statusComponent, control));
|
_activeStatusComponents.Add((statusComponent, control));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var collectMsg = new ItemStatusCollectMessage();
|
||||||
|
_entity.EntityManager.EventBus.RaiseLocalEvent(_entity.Uid, collectMsg);
|
||||||
|
|
||||||
|
foreach (var control in collectMsg.Controls)
|
||||||
|
{
|
||||||
|
_statusContents.AddChild(control);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using Content.Shared.DragDrop;
|
|||||||
using Content.Shared.Kitchen.Components;
|
using Content.Shared.Kitchen.Components;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
namespace Content.Client.Kitchen
|
namespace Content.Client.Kitchen.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
internal sealed class KitchenSpikeComponent : SharedKitchenSpikeComponent
|
internal sealed class KitchenSpikeComponent : SharedKitchenSpikeComponent
|
||||||
17
Content.Client/Kitchen/Components/MicrowaveComponent.cs
Normal file
17
Content.Client/Kitchen/Components/MicrowaveComponent.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using Content.Shared.Kitchen.Components;
|
||||||
|
using Content.Shared.Sound;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
|
namespace Content.Client.Kitchen.Components
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public class MicrowaveComponent : SharedMicrowaveComponent
|
||||||
|
{
|
||||||
|
public IPlayingAudioStream? PlayingStream { get; set; }
|
||||||
|
|
||||||
|
[DataField("loopingSound")]
|
||||||
|
public SoundSpecifier LoopingSound = new SoundPathSpecifier("/Audio/Machines/microwave_loop.ogg");
|
||||||
|
}
|
||||||
|
}
|
||||||
31
Content.Client/Kitchen/EntitySystems/MicrowaveSystem.cs
Normal file
31
Content.Client/Kitchen/EntitySystems/MicrowaveSystem.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Client.Kitchen.Components;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
|
||||||
|
namespace Content.Client.Kitchen.EntitySystems
|
||||||
|
{
|
||||||
|
public class MicrowaveSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public void StartSoundLoop(MicrowaveComponent microwave)
|
||||||
|
{
|
||||||
|
StopSoundLoop(microwave);
|
||||||
|
|
||||||
|
microwave.PlayingStream = SoundSystem.Play(Filter.Local(), microwave.LoopingSound.GetSound(), microwave.Owner,
|
||||||
|
AudioParams.Default.WithAttenuation(1).WithMaxDistance(5).WithLoop(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopSoundLoop(MicrowaveComponent microwave)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
microwave.PlayingStream?.Stop();
|
||||||
|
}
|
||||||
|
catch (Exception _)
|
||||||
|
{
|
||||||
|
// TODO: HOLY SHIT EXPOSE SOME DISPOSED PROPERTY ON PLAYING STREAM OR SOMETHING.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
using Content.Client.Sound;
|
using Content.Client.Kitchen.Components;
|
||||||
|
using Content.Client.Kitchen.EntitySystems;
|
||||||
using Content.Shared.Kitchen.Components;
|
using Content.Shared.Kitchen.Components;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.Sound;
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Log;
|
using Robust.Shared.Log;
|
||||||
|
|
||||||
namespace Content.Client.Kitchen.Visualizers
|
namespace Content.Client.Kitchen.Visualizers
|
||||||
@@ -17,35 +17,33 @@ namespace Content.Client.Kitchen.Visualizers
|
|||||||
base.OnChangeData(component);
|
base.OnChangeData(component);
|
||||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||||
|
|
||||||
var loopingSoundComponent = component.Owner.GetComponentOrNull<LoopingSoundComponent>();
|
var microwaveComponent = component.Owner.GetComponentOrNull<MicrowaveComponent>();
|
||||||
|
|
||||||
if (!component.TryGetData(PowerDeviceVisuals.VisualState, out MicrowaveVisualState state))
|
if (!component.TryGetData(PowerDeviceVisuals.VisualState, out MicrowaveVisualState state))
|
||||||
{
|
{
|
||||||
state = MicrowaveVisualState.Idle;
|
state = MicrowaveVisualState.Idle;
|
||||||
}
|
}
|
||||||
|
// The only reason we get the entity system so late is so that tests don't fail... Amazing, huh?
|
||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case MicrowaveVisualState.Broken:
|
case MicrowaveVisualState.Broken:
|
||||||
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mwb");
|
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mwb");
|
||||||
loopingSoundComponent?.StopAllSounds();
|
if(microwaveComponent != null)
|
||||||
|
EntitySystem.Get<MicrowaveSystem>().StopSoundLoop(microwaveComponent);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MicrowaveVisualState.Idle:
|
case MicrowaveVisualState.Idle:
|
||||||
sprite.LayerSetState(MicrowaveVisualizerLayers.Base, "mw");
|
sprite.LayerSetState(MicrowaveVisualizerLayers.Base, "mw");
|
||||||
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mw_unlit");
|
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mw_unlit");
|
||||||
loopingSoundComponent?.StopAllSounds();
|
if(microwaveComponent != null)
|
||||||
|
EntitySystem.Get<MicrowaveSystem>().StopSoundLoop(microwaveComponent);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MicrowaveVisualState.Cooking:
|
case MicrowaveVisualState.Cooking:
|
||||||
sprite.LayerSetState(MicrowaveVisualizerLayers.Base, "mw");
|
sprite.LayerSetState(MicrowaveVisualizerLayers.Base, "mw");
|
||||||
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mw_running_unlit");
|
sprite.LayerSetState(MicrowaveVisualizerLayers.BaseUnlit, "mw_running_unlit");
|
||||||
var audioParams = AudioParams.Default;
|
if(microwaveComponent != null)
|
||||||
audioParams.Loop = true;
|
EntitySystem.Get<MicrowaveSystem>().StartSoundLoop(microwaveComponent);
|
||||||
var scheduledSound = new ScheduledSound();
|
|
||||||
scheduledSound.Filename = "/Audio/Machines/microwave_loop.ogg";
|
|
||||||
scheduledSound.AudioParams = audioParams;
|
|
||||||
loopingSoundComponent?.StopAllSounds();
|
|
||||||
loopingSoundComponent?.AddScheduledSound(scheduledSound);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Shared.Light.Component;
|
using Content.Shared.Light.Component;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
namespace Content.Client.Light.Components
|
namespace Content.Client.Light.Components
|
||||||
@@ -9,6 +10,6 @@ namespace Content.Client.Light.Components
|
|||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class ExpendableLightComponent : SharedExpendableLightComponent
|
public class ExpendableLightComponent : SharedExpendableLightComponent
|
||||||
{
|
{
|
||||||
|
public IPlayingAudioStream? PlayingStream { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
using Content.Client.Light.Components;
|
using System;
|
||||||
|
using Content.Client.Light.Components;
|
||||||
using Content.Shared.Light.Component;
|
using Content.Shared.Light.Component;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
|
||||||
namespace Content.Client.Light.Visualizers
|
namespace Content.Client.Light.Visualizers
|
||||||
{
|
{
|
||||||
@@ -17,7 +20,7 @@ namespace Content.Client.Light.Visualizers
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (component.TryGetData(ExpendableLightVisuals.State, out string lightBehaviourID))
|
if (component.TryGetData(ExpendableLightVisuals.Behavior, out string lightBehaviourID))
|
||||||
{
|
{
|
||||||
if (component.Owner.TryGetComponent<LightBehaviourComponent>(out var lightBehaviour))
|
if (component.Owner.TryGetComponent<LightBehaviourComponent>(out var lightBehaviour))
|
||||||
{
|
{
|
||||||
@@ -33,6 +36,35 @@ namespace Content.Client.Light.Visualizers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TryStopStream(IPlayingAudioStream? stream)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
stream?.Stop();
|
||||||
|
}
|
||||||
|
catch (Exception _)
|
||||||
|
{
|
||||||
|
// TODO: HOLY SHIT EXPOSE SOME DISPOSED PROPERTY ON PLAYING STREAM OR SOMETHING.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.TryGetData(ExpendableLightVisuals.State, out ExpendableLightState state)
|
||||||
|
&& component.Owner.TryGetComponent<ExpendableLightComponent>(out var expendableLight))
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case ExpendableLightState.Lit:
|
||||||
|
TryStopStream(expendableLight.PlayingStream);
|
||||||
|
expendableLight.PlayingStream = SoundSystem.Play(Filter.Local(), expendableLight.LoopedSound,
|
||||||
|
expendableLight.Owner, SharedExpendableLightComponent.LoopedSoundParams.WithLoop(true));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ExpendableLightState.Dead:
|
||||||
|
TryStopStream(expendableLight.PlayingStream);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,105 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Content.Shared.Physics;
|
|
||||||
using Content.Shared.Sound;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Network;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Players;
|
|
||||||
using Robust.Shared.Random;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
|
|
||||||
namespace Content.Client.Sound
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public class LoopingSoundComponent : SharedLoopingSoundComponent
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
|
||||||
|
|
||||||
private readonly Dictionary<ScheduledSound, IPlayingAudioStream> _audioStreams = new();
|
|
||||||
|
|
||||||
[DataField("schedules", true)]
|
|
||||||
private List<ScheduledSound> _scheduledSounds
|
|
||||||
{
|
|
||||||
set => value.ForEach(AddScheduledSound);
|
|
||||||
get => new();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void StopAllSounds()
|
|
||||||
{
|
|
||||||
foreach (var kvp in _audioStreams)
|
|
||||||
{
|
|
||||||
kvp.Key.Play = false;
|
|
||||||
kvp.Value.Stop();
|
|
||||||
}
|
|
||||||
_audioStreams.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void StopScheduledSound(string filename)
|
|
||||||
{
|
|
||||||
foreach (var kvp in _audioStreams)
|
|
||||||
{
|
|
||||||
if (kvp.Key.Filename != filename) continue;
|
|
||||||
kvp.Key.Play = false;
|
|
||||||
kvp.Value.Stop();
|
|
||||||
_audioStreams.Remove(kvp.Key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void AddScheduledSound(ScheduledSound schedule)
|
|
||||||
{
|
|
||||||
Play(schedule);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Play(ScheduledSound schedule)
|
|
||||||
{
|
|
||||||
if (!schedule.Play) return;
|
|
||||||
|
|
||||||
Owner.SpawnTimer((int) schedule.Delay + (_random.Next((int) schedule.RandomDelay)),() =>
|
|
||||||
{
|
|
||||||
if (!schedule.Play) return; // We make sure this hasn't changed.
|
|
||||||
|
|
||||||
if (!_audioStreams.ContainsKey(schedule))
|
|
||||||
{
|
|
||||||
_audioStreams.Add(schedule, SoundSystem.Play(Filter.Local(), schedule.Filename, Owner, schedule.AudioParams)!);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_audioStreams[schedule] = SoundSystem.Play(Filter.Local(), schedule.Filename, Owner, schedule.AudioParams)!;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (schedule.Times == 0) return;
|
|
||||||
|
|
||||||
if (schedule.Times > 0) schedule.Times--;
|
|
||||||
|
|
||||||
Play(schedule);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession? session = null)
|
|
||||||
{
|
|
||||||
base.HandleNetworkMessage(message, channel, session);
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case ScheduledSoundMessage msg:
|
|
||||||
AddScheduledSound(msg.Schedule);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case StopSoundScheduleMessage msg:
|
|
||||||
StopScheduledSound(msg.Filename);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case StopAllSoundsMessage _:
|
|
||||||
StopAllSounds();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
SoundSystem.OcclusionCollisionMask = (int) CollisionGroup.Impassable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -52,7 +52,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs
|
|||||||
- type: PointLight
|
- type: PointLight
|
||||||
enabled: false
|
enabled: false
|
||||||
radius: 3
|
radius: 3
|
||||||
- type: LoopingSound
|
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
visuals:
|
visuals:
|
||||||
- type: FlashLightVisualizer
|
- type: FlashLightVisualizer
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ namespace Content.Server.Body.Behavior
|
|||||||
|
|
||||||
private float _accumulatedFrameTime;
|
private float _accumulatedFrameTime;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delay time that determines how often to metabolise blood contents (in seconds).
|
||||||
|
/// </summary>
|
||||||
|
private float _updateIntervalSeconds = 1.0f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether the liver is functional.
|
/// Whether the liver is functional.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -63,13 +68,13 @@ namespace Content.Server.Body.Behavior
|
|||||||
|
|
||||||
_accumulatedFrameTime += frameTime;
|
_accumulatedFrameTime += frameTime;
|
||||||
|
|
||||||
// Update at most once per second
|
// Update at most once every _updateIntervalSeconds
|
||||||
if (_accumulatedFrameTime < 1)
|
if (_accumulatedFrameTime < _updateIntervalSeconds)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_accumulatedFrameTime -= 1;
|
_accumulatedFrameTime -= _updateIntervalSeconds;
|
||||||
|
|
||||||
if (!Body.Owner.TryGetComponent(out BloodstreamComponent? bloodstream))
|
if (!Body.Owner.TryGetComponent(out BloodstreamComponent? bloodstream))
|
||||||
{
|
{
|
||||||
@@ -90,6 +95,10 @@ namespace Content.Server.Body.Behavior
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// How much reagent is available to metabolise?
|
||||||
|
// This needs to be passed to other functions that have metabolism rate information, such that they don't "overmetabolise" a reagent.
|
||||||
|
var availableReagent = bloodstream.Solution.Solution.GetReagentQuantity(reagent.ReagentId);
|
||||||
|
|
||||||
//TODO BODY Check if it's a Toxin. If volume < _toxinTolerance, just remove it. If greater, add damage = volume * _toxinLethality
|
//TODO BODY Check if it's a Toxin. If volume < _toxinTolerance, just remove it. If greater, add damage = volume * _toxinLethality
|
||||||
//TODO BODY Check if it has BoozePower > 0. Affect drunkenness, apply damage. Proposed formula (SS13-derived): damage = sqrt(volume) * BoozePower^_alcoholExponent * _alcoholLethality / 10
|
//TODO BODY Check if it has BoozePower > 0. Affect drunkenness, apply damage. Proposed formula (SS13-derived): damage = sqrt(volume) * BoozePower^_alcoholExponent * _alcoholLethality / 10
|
||||||
//TODO BODY Liver failure.
|
//TODO BODY Liver failure.
|
||||||
@@ -99,8 +108,9 @@ namespace Content.Server.Body.Behavior
|
|||||||
// Run metabolism code for each reagent
|
// Run metabolism code for each reagent
|
||||||
foreach (var metabolizable in prototype.Metabolism)
|
foreach (var metabolizable in prototype.Metabolism)
|
||||||
{
|
{
|
||||||
var reagentDelta = metabolizable.Metabolize(Body.Owner, reagent.ReagentId, frameTime);
|
var reagentDelta = metabolizable.Metabolize(Body.Owner, reagent.ReagentId, _updateIntervalSeconds, availableReagent);
|
||||||
bloodstream.Solution.TryRemoveReagent(reagent.ReagentId, reagentDelta);
|
bloodstream.Solution.TryRemoveReagent(reagent.ReagentId, reagentDelta);
|
||||||
|
availableReagent -= reagentDelta;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ namespace Content.Server.Body.Behavior
|
|||||||
/// </param>
|
/// </param>
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// Do not metabolise if the organ does not have a body.
|
||||||
if (Body == null)
|
if (Body == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -45,7 +47,9 @@ namespace Content.Server.Body.Behavior
|
|||||||
|
|
||||||
_accumulatedFrameTime -= 1;
|
_accumulatedFrameTime -= 1;
|
||||||
|
|
||||||
if (!Body.Owner.TryGetComponent(out SolutionContainerComponent? solution) ||
|
// Note that "Owner" should be the organ that has this behaviour/mechanism, and it should have a dedicated
|
||||||
|
// solution container. "Body.Owner" is something else, and may have more than one solution container.
|
||||||
|
if (!Owner.TryGetComponent(out SolutionContainerComponent? solution) ||
|
||||||
!Body.Owner.TryGetComponent(out BloodstreamComponent? bloodstream))
|
!Body.Owner.TryGetComponent(out BloodstreamComponent? bloodstream))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -61,8 +65,19 @@ namespace Content.Server.Body.Behavior
|
|||||||
delta.Increment(1);
|
delta.Increment(1);
|
||||||
if (delta.Lifetime > _digestionDelay)
|
if (delta.Lifetime > _digestionDelay)
|
||||||
{
|
{
|
||||||
solution.TryRemoveReagent(delta.ReagentId, delta.Quantity);
|
// This reagent has been in the somach long enough, TRY to transfer it.
|
||||||
transferSolution.AddReagent(delta.ReagentId, delta.Quantity);
|
// But first, check if the reagent still exists, and how much is left.
|
||||||
|
// Some poor spessman may have washed down a potassium snack with some water.
|
||||||
|
if (solution.Solution.ContainsReagent(delta.ReagentId, out ReagentUnit quantity)){
|
||||||
|
|
||||||
|
if (quantity > delta.Quantity) {
|
||||||
|
quantity = delta.Quantity;
|
||||||
|
}
|
||||||
|
|
||||||
|
solution.TryRemoveReagent(delta.ReagentId, quantity);
|
||||||
|
transferSolution.AddReagent(delta.ReagentId, quantity);
|
||||||
|
}
|
||||||
|
|
||||||
_reagentDeltas.Remove(delta);
|
_reagentDeltas.Remove(delta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,10 +148,10 @@ namespace Content.Server.Body.Behavior
|
|||||||
|
|
||||||
public bool TryTransferSolution(Solution solution)
|
public bool TryTransferSolution(Solution solution)
|
||||||
{
|
{
|
||||||
if (Body == null || !CanTransferSolution(solution))
|
if (Owner == null || !CanTransferSolution(solution))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!Body.Owner.TryGetComponent(out SolutionContainerComponent? solutionComponent))
|
if (!Owner.TryGetComponent(out SolutionContainerComponent? solutionComponent))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Server.Nutrition.Components;
|
using Content.Server.Nutrition.Components;
|
||||||
using Content.Shared.Chemistry;
|
using Content.Shared.Chemistry;
|
||||||
using Content.Shared.Chemistry.Metabolizable;
|
using Content.Shared.Chemistry.Metabolizable;
|
||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
@@ -9,28 +9,27 @@ namespace Content.Server.Chemistry.Metabolism
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default metabolism for drink reagents. Attempts to find a ThirstComponent on the target,
|
/// Default metabolism for drink reagents. Attempts to find a ThirstComponent on the target,
|
||||||
/// and to update it's thirst values.
|
/// and to update it's thirst values. Inherits metabolisation rate logic from DefaultMetabolizable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataDefinition]
|
[DataDefinition]
|
||||||
public class DefaultDrink : IMetabolizable
|
public class DefaultDrink : DefaultMetabolizable
|
||||||
{
|
{
|
||||||
//Rate of metabolism in units / second
|
|
||||||
[DataField("rate")]
|
|
||||||
public ReagentUnit MetabolismRate { get; set; } = ReagentUnit.New(1);
|
|
||||||
|
|
||||||
//How much thirst is satiated when 1u of the reagent is metabolized
|
//How much thirst is satiated when 1u of the reagent is metabolized
|
||||||
[DataField("hydrationFactor")]
|
[DataField("hydrationFactor")]
|
||||||
public float HydrationFactor { get; set; } = 30.0f;
|
public float HydrationFactor { get; set; } = 30.0f;
|
||||||
|
|
||||||
//Remove reagent at set rate, satiate thirst if a ThirstComponent can be found
|
//Remove reagent at set rate, satiate thirst if a ThirstComponent can be found
|
||||||
ReagentUnit IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime)
|
public override ReagentUnit Metabolize(IEntity solutionEntity, string reagentId, float tickTime, ReagentUnit availableReagent)
|
||||||
{
|
{
|
||||||
var metabolismAmount = MetabolismRate * tickTime;
|
// use DefaultMetabolism to determine how much reagent we should metabolize
|
||||||
|
var amountMetabolized = base.Metabolize(solutionEntity, reagentId, tickTime, availableReagent);
|
||||||
|
|
||||||
|
// If metabolizing entity has a ThirstComponent, hydrate them.
|
||||||
if (solutionEntity.TryGetComponent(out ThirstComponent? thirst))
|
if (solutionEntity.TryGetComponent(out ThirstComponent? thirst))
|
||||||
thirst.UpdateThirst(metabolismAmount.Float() * HydrationFactor);
|
thirst.UpdateThirst(amountMetabolized.Float() * HydrationFactor);
|
||||||
|
|
||||||
//Return amount of reagent to be removed, remove reagent regardless of ThirstComponent presence
|
//Return amount of reagent to be removed, remove reagent regardless of ThirstComponent presence
|
||||||
return metabolismAmount;
|
return amountMetabolized;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Server.Nutrition.Components;
|
using Content.Server.Nutrition.Components;
|
||||||
using Content.Shared.Chemistry;
|
using Content.Shared.Chemistry;
|
||||||
using Content.Shared.Chemistry.Metabolizable;
|
using Content.Shared.Chemistry.Metabolizable;
|
||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
@@ -9,30 +9,30 @@ namespace Content.Server.Chemistry.Metabolism
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default metabolism for food reagents. Attempts to find a HungerComponent on the target,
|
/// Default metabolism for food reagents. Attempts to find a HungerComponent on the target,
|
||||||
/// and to update it's hunger values.
|
/// and to update it's hunger values. Inherits metabolisation rate logic from DefaultMetabolizable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataDefinition]
|
[DataDefinition]
|
||||||
public class DefaultFood : IMetabolizable
|
public class DefaultFood : DefaultMetabolizable
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Rate of metabolism in units / second
|
|
||||||
/// </summary>
|
|
||||||
[DataField("rate")] public ReagentUnit MetabolismRate { get; private set; } = ReagentUnit.New(1.0);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How much hunger is satiated when 1u of the reagent is metabolized
|
/// How much hunger is satiated when 1u of the reagent is metabolized
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("nutritionFactor")] public float NutritionFactor { get; set; } = 30.0f;
|
[DataField("nutritionFactor")] public float NutritionFactor { get; set; } = 30.0f;
|
||||||
|
|
||||||
//Remove reagent at set rate, satiate hunger if a HungerComponent can be found
|
|
||||||
ReagentUnit IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime)
|
|
||||||
{
|
|
||||||
var metabolismAmount = MetabolismRate * tickTime;
|
|
||||||
if (solutionEntity.TryGetComponent(out HungerComponent? hunger))
|
|
||||||
hunger.UpdateFood(metabolismAmount.Float() * NutritionFactor);
|
|
||||||
|
|
||||||
//Return amount of reagent to be removed, remove reagent regardless of HungerComponent presence
|
//Remove reagent at set rate, satiate hunger if a HungerComponent can be found
|
||||||
return metabolismAmount;
|
public override ReagentUnit Metabolize(IEntity solutionEntity, string reagentId, float tickTime, ReagentUnit availableReagent)
|
||||||
|
{
|
||||||
|
// use DefaultMetabolism to determine how much reagent we should metabolize
|
||||||
|
var amountMetabolized = base.Metabolize(solutionEntity, reagentId, tickTime, availableReagent);
|
||||||
|
|
||||||
|
// If metabolizing entity has a HungerComponent, feed them.
|
||||||
|
if (solutionEntity.TryGetComponent(out HungerComponent? hunger))
|
||||||
|
hunger.UpdateFood(amountMetabolized.Float() * NutritionFactor);
|
||||||
|
|
||||||
|
//Return amount of reagent to be removed. Reagent is removed regardless of HungerComponent presence
|
||||||
|
return amountMetabolized;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,16 +10,11 @@ namespace Content.Server.Chemistry.Metabolism
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default metabolism for medicine reagents. Attempts to find a DamageableComponent on the target,
|
/// Default metabolism for medicine reagents. Attempts to find a DamageableComponent on the target,
|
||||||
/// and to update its damage values.
|
/// and to update its damage values. Inherits metabolisation rate logic from DefaultMetabolizable.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataDefinition]
|
[DataDefinition]
|
||||||
public class HealthChangeMetabolism : IMetabolizable
|
public class HealthChangeMetabolism : DefaultMetabolizable
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// How much of the reagent should be metabolized each sec.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("rate")]
|
|
||||||
public ReagentUnit MetabolismRate { get; set; } = ReagentUnit.New(1);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How much damage is changed when 1u of the reagent is metabolized.
|
/// How much damage is changed when 1u of the reagent is metabolized.
|
||||||
@@ -41,14 +36,23 @@ namespace Content.Server.Chemistry.Metabolism
|
|||||||
/// <param name="solutionEntity"></param>
|
/// <param name="solutionEntity"></param>
|
||||||
/// <param name="reagentId"></param>
|
/// <param name="reagentId"></param>
|
||||||
/// <param name="tickTime"></param>
|
/// <param name="tickTime"></param>
|
||||||
|
/// <param name="availableReagent">Reagent available to be metabolized.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
ReagentUnit IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime)
|
public override ReagentUnit Metabolize(IEntity solutionEntity, string reagentId, float tickTime, ReagentUnit availableReagent)
|
||||||
{
|
{
|
||||||
|
// use DefaultMetabolism to determine how much reagent we should metabolize
|
||||||
|
var amountMetabolized = base.Metabolize(solutionEntity, reagentId, tickTime, availableReagent);
|
||||||
|
|
||||||
|
// how much does this much reagent heal for
|
||||||
|
var healthChangeAmount = HealthChange * amountMetabolized.Float();
|
||||||
|
|
||||||
if (solutionEntity.TryGetComponent(out IDamageableComponent? health))
|
if (solutionEntity.TryGetComponent(out IDamageableComponent? health))
|
||||||
{
|
{
|
||||||
health.ChangeDamage(DamageType, (int)HealthChange, true);
|
// Heal damage by healthChangeAmmount, rounding down to nearest integer
|
||||||
float decHealthChange = (float) (HealthChange - (int) HealthChange);
|
health.ChangeDamage(DamageType, (int) healthChangeAmount, true);
|
||||||
_accumulatedHealth += decHealthChange;
|
|
||||||
|
// Store decimal remainder of healthChangeAmmount in _accumulatedHealth
|
||||||
|
_accumulatedHealth += (healthChangeAmount - (int) healthChangeAmount);
|
||||||
|
|
||||||
if (_accumulatedHealth >= 1)
|
if (_accumulatedHealth >= 1)
|
||||||
{
|
{
|
||||||
@@ -62,7 +66,7 @@ namespace Content.Server.Chemistry.Metabolism
|
|||||||
_accumulatedHealth += 1;
|
_accumulatedHealth += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MetabolismRate;
|
return amountMetabolized;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
|
|||||||
namespace Content.Server.Chemistry.ReagentEntityReactions
|
namespace Content.Server.Chemistry.ReagentEntityReactions
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
[DataDefinition]
|
|
||||||
public class AddToSolutionReaction : ReagentEntityReaction
|
public class AddToSolutionReaction : ReagentEntityReaction
|
||||||
{
|
{
|
||||||
[DataField("reagents", true, customTypeSerializer:typeof(PrototypeIdHashSetSerializer<ReagentPrototype>))]
|
[DataField("reagents", true, customTypeSerializer:typeof(PrototypeIdHashSetSerializer<ReagentPrototype>))]
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
|
|||||||
namespace Content.Server.Chemistry.ReagentEntityReactions
|
namespace Content.Server.Chemistry.ReagentEntityReactions
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
[DataDefinition]
|
|
||||||
public class ExtinguishReaction : ReagentEntityReaction
|
public class ExtinguishReaction : ReagentEntityReaction
|
||||||
{
|
{
|
||||||
[DataField("reagents", true, customTypeSerializer:typeof(PrototypeIdHashSetSerializer<ReagentPrototype>))]
|
[DataField("reagents", true, customTypeSerializer:typeof(PrototypeIdHashSetSerializer<ReagentPrototype>))]
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
|
|||||||
namespace Content.Server.Chemistry.ReagentEntityReactions
|
namespace Content.Server.Chemistry.ReagentEntityReactions
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
[DataDefinition]
|
|
||||||
public class FlammableReaction : ReagentEntityReaction
|
public class FlammableReaction : ReagentEntityReaction
|
||||||
{
|
{
|
||||||
[DataField("reagents", true, customTypeSerializer:typeof(PrototypeIdHashSetSerializer<ReagentPrototype>))]
|
[DataField("reagents", true, customTypeSerializer:typeof(PrototypeIdHashSetSerializer<ReagentPrototype>))]
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
|
|||||||
namespace Content.Server.Chemistry.ReagentEntityReactions
|
namespace Content.Server.Chemistry.ReagentEntityReactions
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
[DataDefinition]
|
|
||||||
public class WashCreamPieReaction : ReagentEntityReaction
|
public class WashCreamPieReaction : ReagentEntityReaction
|
||||||
{
|
{
|
||||||
[DataField("reagents", true, customTypeSerializer:typeof(PrototypeIdHashSetSerializer<ReagentPrototype>))]
|
[DataField("reagents", true, customTypeSerializer:typeof(PrototypeIdHashSetSerializer<ReagentPrototype>))]
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Content.Shared.ActionBlocker;
|
|||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using Content.Shared.Chemistry.Solution.Components;
|
using Content.Shared.Chemistry.Solution.Components;
|
||||||
using Content.Shared.DragDrop;
|
using Content.Shared.DragDrop;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Interaction.Events;
|
using Content.Shared.Interaction.Events;
|
||||||
using Content.Shared.Notification.Managers;
|
using Content.Shared.Notification.Managers;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -10,7 +11,6 @@ using Content.Shared.Audio;
|
|||||||
using Content.Shared.Body.Part;
|
using Content.Shared.Body.Part;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Notification.Managers;
|
using Content.Shared.Notification.Managers;
|
||||||
using Content.Shared.Physics.Pull;
|
|
||||||
using Content.Shared.Pulling.Components;
|
using Content.Shared.Pulling.Components;
|
||||||
using Content.Shared.Sound;
|
using Content.Shared.Sound;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
@@ -20,9 +20,7 @@ using Robust.Shared.GameObjects;
|
|||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Network;
|
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Players;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
namespace Content.Server.Hands.Components
|
namespace Content.Server.Hands.Components
|
||||||
@@ -39,48 +37,6 @@ namespace Content.Server.Hands.Components
|
|||||||
|
|
||||||
int IDisarmedAct.Priority => int.MaxValue; // We want this to be the last disarm act to run.
|
int IDisarmedAct.Priority => int.MaxValue; // We want this to be the last disarm act to run.
|
||||||
|
|
||||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
|
||||||
{
|
|
||||||
base.HandleMessage(message, component);
|
|
||||||
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case PullAttemptMessage msg:
|
|
||||||
AttemptPull(msg);
|
|
||||||
break;
|
|
||||||
case PullStartedMessage:
|
|
||||||
StartPulling();
|
|
||||||
break;
|
|
||||||
case PullStoppedMessage:
|
|
||||||
StopPulling();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession? session = null)
|
|
||||||
{
|
|
||||||
base.HandleNetworkMessage(message, channel, session);
|
|
||||||
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case ClientChangedHandMsg msg:
|
|
||||||
ActiveHand = msg.HandName;
|
|
||||||
break;
|
|
||||||
case ClientAttackByInHandMsg msg:
|
|
||||||
InteractHandWithActiveHand(msg.HandName);
|
|
||||||
break;
|
|
||||||
case UseInHandMsg:
|
|
||||||
UseActiveHeldEntity();
|
|
||||||
break;
|
|
||||||
case ActivateInHandMsg msg:
|
|
||||||
ActivateHeldEntity(msg.HandName);
|
|
||||||
break;
|
|
||||||
case MoveItemFromHandMsg msg:
|
|
||||||
TryMoveHeldEntityToActiveHand(msg.HandName);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnHeldEntityRemovedFromHand(IEntity heldEntity, HandState handState)
|
protected override void OnHeldEntityRemovedFromHand(IEntity heldEntity, HandState handState)
|
||||||
{
|
{
|
||||||
if (heldEntity.TryGetComponent(out ItemComponent? item))
|
if (heldEntity.TryGetComponent(out ItemComponent? item))
|
||||||
@@ -145,7 +101,8 @@ namespace Content.Server.Hands.Components
|
|||||||
if (pickupDirection == initialPosition.ToMapPos(Owner.EntityManager))
|
if (pickupDirection == initialPosition.ToMapPos(Owner.EntityManager))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SendNetworkMessage(new PickupAnimationMessage(entity.Uid, pickupDirection, initialPosition));
|
Owner.EntityManager.EntityNetManager!.SendSystemNetworkMessage(
|
||||||
|
new PickupAnimationMessage(entity.Uid, pickupDirection, initialPosition));
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Pull/Disarm
|
#region Pull/Disarm
|
||||||
@@ -155,9 +112,17 @@ namespace Content.Server.Hands.Components
|
|||||||
if (args.Part.PartType != BodyPartType.Hand)
|
if (args.Part.PartType != BodyPartType.Hand)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var handLocation = ReadOnlyHands.Count == 0 ? HandLocation.Right : HandLocation.Left; //TODO: make hand body part have a handlocation?
|
// If this annoys you, which it should.
|
||||||
|
// Ping Smugleaf.
|
||||||
|
var location = args.Part.Symmetry switch
|
||||||
|
{
|
||||||
|
BodyPartSymmetry.None => HandLocation.Middle,
|
||||||
|
BodyPartSymmetry.Left => HandLocation.Left,
|
||||||
|
BodyPartSymmetry.Right => HandLocation.Right,
|
||||||
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
|
};
|
||||||
|
|
||||||
AddHand(args.Slot, handLocation);
|
AddHand(args.Slot, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IBodyPartRemoved.BodyPartRemoved(BodyPartRemovedEventArgs args)
|
void IBodyPartRemoved.BodyPartRemoved(BodyPartRemovedEventArgs args)
|
||||||
@@ -209,41 +174,13 @@ namespace Content.Server.Hands.Components
|
|||||||
return pullable.TryStopPull();
|
return pullable.TryStopPull();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AttemptPull(PullAttemptMessage msg)
|
|
||||||
{
|
|
||||||
if (!ReadOnlyHands.Any(hand => hand.Enabled))
|
|
||||||
{
|
|
||||||
msg.Cancelled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StartPulling()
|
|
||||||
{
|
|
||||||
var firstFreeHand = Hands.FirstOrDefault(hand => hand.Enabled);
|
|
||||||
|
|
||||||
if (firstFreeHand == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DisableHand(firstFreeHand);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StopPulling()
|
|
||||||
{
|
|
||||||
var firstOccupiedHand = Hands.FirstOrDefault(hand => !hand.Enabled);
|
|
||||||
|
|
||||||
if (firstOccupiedHand == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
EnableHand(firstOccupiedHand);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Old public methods
|
#region Old public methods
|
||||||
|
|
||||||
public IEnumerable<string> HandNames => ReadOnlyHands.Select(h => h.Name);
|
public IEnumerable<string> HandNames => Hands.Select(h => h.Name);
|
||||||
|
|
||||||
public int Count => ReadOnlyHands.Count;
|
public int Count => Hands.Count;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a list of all hand names, with the active hand being first.
|
/// Returns a list of all hand names, with the active hand being first.
|
||||||
@@ -253,9 +190,9 @@ namespace Content.Server.Hands.Components
|
|||||||
if (ActiveHand != null)
|
if (ActiveHand != null)
|
||||||
yield return ActiveHand;
|
yield return ActiveHand;
|
||||||
|
|
||||||
foreach (var hand in ReadOnlyHands)
|
foreach (var hand in Hands)
|
||||||
{
|
{
|
||||||
if (hand.Name == ActiveHand || !hand.Enabled)
|
if (hand.Name == ActiveHand)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
yield return hand.Name;
|
yield return hand.Name;
|
||||||
|
|||||||
57
Content.Server/Hands/Systems/HandVirtualPullSystem.cs
Normal file
57
Content.Server/Hands/Systems/HandVirtualPullSystem.cs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
using Content.Server.Pulling;
|
||||||
|
using Content.Shared.Hands;
|
||||||
|
using Content.Shared.Hands.Components;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.Hands
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed class HandVirtualPullSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<HandVirtualPullComponent, DroppedEvent>(HandlePullerDropped);
|
||||||
|
SubscribeLocalEvent<HandVirtualPullComponent, UnequippedHandEvent>(HandlePullerUnequipped);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<HandVirtualPullComponent, BeforeInteractEvent>(HandleBeforeInteract);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void HandleBeforeInteract(
|
||||||
|
EntityUid uid,
|
||||||
|
HandVirtualPullComponent component,
|
||||||
|
BeforeInteractEvent args)
|
||||||
|
{
|
||||||
|
// No interactions with a virtual pull, please.
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the virtual pull gets removed from the hands for any reason, cancel the pull and delete it.
|
||||||
|
private void HandlePullerUnequipped(EntityUid uid, HandVirtualPullComponent component, UnequippedHandEvent args)
|
||||||
|
{
|
||||||
|
MaybeDelete(component, args.User);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandlePullerDropped(EntityUid uid, HandVirtualPullComponent component, DroppedEvent args)
|
||||||
|
{
|
||||||
|
MaybeDelete(component, args.User);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MaybeDelete(HandVirtualPullComponent comp, IEntity? user)
|
||||||
|
{
|
||||||
|
var pulled = comp.PulledEntity;
|
||||||
|
|
||||||
|
if (!ComponentManager.TryGetComponent(pulled, out PullableComponent? pullable))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (pullable.Puller != user)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pullable.TryStopPull(user);
|
||||||
|
comp.Owner.QueueDelete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,9 +13,9 @@ using Content.Shared.Hands;
|
|||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
using Content.Shared.Notification.Managers;
|
using Content.Shared.Notification.Managers;
|
||||||
|
using Content.Shared.Physics.Pull;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
using Robust.Shared.Containers;
|
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Input.Binding;
|
using Robust.Shared.Input.Binding;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
@@ -23,6 +23,7 @@ using Robust.Shared.Localization;
|
|||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
using Robust.Shared.Players;
|
using Robust.Shared.Players;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
using static Content.Shared.Inventory.EquipmentSlotDefines;
|
using static Content.Shared.Inventory.EquipmentSlotDefines;
|
||||||
|
|
||||||
namespace Content.Server.Hands
|
namespace Content.Server.Hands
|
||||||
@@ -38,15 +39,126 @@ namespace Content.Server.Hands
|
|||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<HandsComponent, ExaminedEvent>(HandleExamined);
|
SubscribeLocalEvent<HandsComponent, ExaminedEvent>(HandleExamined);
|
||||||
|
SubscribeNetworkEvent<ActivateInHandMsg>(HandleActivateInHand);
|
||||||
|
SubscribeNetworkEvent<ClientInteractUsingInHandMsg>(HandleInteractUsingInHand);
|
||||||
|
SubscribeNetworkEvent<UseInHandMsg>(HandleUseInHand);
|
||||||
|
SubscribeNetworkEvent<MoveItemFromHandMsg>(HandleMoveItemFromHand);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<HandsComponent, PullAttemptMessage>(HandlePullAttempt);
|
||||||
|
SubscribeLocalEvent<HandsComponent, PullStartedMessage>(HandlePullStarted);
|
||||||
|
SubscribeLocalEvent<HandsComponent, PullStoppedMessage>(HandlePullStopped);
|
||||||
|
|
||||||
CommandBinds.Builder
|
CommandBinds.Builder
|
||||||
.Bind(ContentKeyFunctions.ActivateItemInHand, InputCmdHandler.FromDelegate(HandleActivateItem))
|
.Bind(ContentKeyFunctions.ActivateItemInHand, InputCmdHandler.FromDelegate(HandleActivateItem))
|
||||||
.Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem))
|
.Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem))
|
||||||
.Bind(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack))
|
.Bind(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack))
|
||||||
.Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt))
|
.Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt))
|
||||||
|
.Bind(ContentKeyFunctions.SwapHands, InputCmdHandler.FromDelegate(SwapHandsPressed, handle: false))
|
||||||
|
.Bind(ContentKeyFunctions.Drop, new PointerInputCmdHandler(DropPressed))
|
||||||
.Register<HandsSystem>();
|
.Register<HandsSystem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void HandlePullAttempt(EntityUid uid, HandsComponent component, PullAttemptMessage args)
|
||||||
|
{
|
||||||
|
// Cancel pull if all hands full.
|
||||||
|
if (component.Hands.All(hand => !hand.IsEmpty))
|
||||||
|
args.Cancelled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandlePullStarted(EntityUid uid, HandsComponent component, PullStartedMessage args)
|
||||||
|
{
|
||||||
|
foreach (var handName in component.ActivePriorityEnumerable())
|
||||||
|
{
|
||||||
|
var hand = component.GetHand(handName);
|
||||||
|
if (!hand.IsEmpty)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var pos = component.Owner.Transform.Coordinates;
|
||||||
|
var virtualPull = EntityManager.SpawnEntity("HandVirtualPull", pos);
|
||||||
|
var virtualPullComp = virtualPull.GetComponent<HandVirtualPullComponent>();
|
||||||
|
virtualPullComp.PulledEntity = args.Pulled.Owner.Uid;
|
||||||
|
component.PutEntityIntoHand(hand, virtualPull);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugTools.Assert("Unable to find available hand when starting pulling??");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandlePullStopped(EntityUid uid, HandsComponent component, PullStoppedMessage args)
|
||||||
|
{
|
||||||
|
// Try find hand that is doing this pull.
|
||||||
|
// and clear it.
|
||||||
|
foreach (var hand in component.Hands)
|
||||||
|
{
|
||||||
|
if (hand.HeldEntity == null
|
||||||
|
|| !hand.HeldEntity.TryGetComponent(out HandVirtualPullComponent? virtualPull)
|
||||||
|
|| virtualPull.PulledEntity != args.Pulled.Owner.Uid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
hand.HeldEntity.Delete();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SwapHandsPressed(ICommonSession? session)
|
||||||
|
{
|
||||||
|
var player = session?.AttachedEntity;
|
||||||
|
|
||||||
|
if (player == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!player.TryGetComponent(out SharedHandsComponent? hands))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!hands.TryGetSwapHandsResult(out var nextHand))
|
||||||
|
return;
|
||||||
|
|
||||||
|
hands.ActiveHand = nextHand;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool DropPressed(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
|
||||||
|
{
|
||||||
|
var player = session?.AttachedEntity;
|
||||||
|
|
||||||
|
if (player == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!player.TryGetComponent(out SharedHandsComponent? hands))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var activeHand = hands.ActiveHand;
|
||||||
|
|
||||||
|
if (activeHand == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
hands.TryDropHand(activeHand, coords);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleMoveItemFromHand(MoveItemFromHandMsg msg, EntitySessionEventArgs args)
|
||||||
|
{
|
||||||
|
if (!TryGetHandsComp(args.SenderSession, out var hands))
|
||||||
|
return;
|
||||||
|
|
||||||
|
hands.TryMoveHeldEntityToActiveHand(msg.HandName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleUseInHand(UseInHandMsg msg, EntitySessionEventArgs args)
|
||||||
|
{
|
||||||
|
if (!TryGetHandsComp(args.SenderSession, out var hands))
|
||||||
|
return;
|
||||||
|
|
||||||
|
hands.UseActiveHeldEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleInteractUsingInHand(ClientInteractUsingInHandMsg msg, EntitySessionEventArgs args)
|
||||||
|
{
|
||||||
|
if (!TryGetHandsComp(args.SenderSession, out var hands))
|
||||||
|
return;
|
||||||
|
|
||||||
|
hands.InteractHandWithActiveHand(msg.HandName);
|
||||||
|
}
|
||||||
|
|
||||||
public override void Shutdown()
|
public override void Shutdown()
|
||||||
{
|
{
|
||||||
base.Shutdown();
|
base.Shutdown();
|
||||||
@@ -54,6 +166,14 @@ namespace Content.Server.Hands
|
|||||||
CommandBinds.Unregister<HandsSystem>();
|
CommandBinds.Unregister<HandsSystem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandleActivateInHand(ActivateInHandMsg msg, EntitySessionEventArgs args)
|
||||||
|
{
|
||||||
|
if (!TryGetHandsComp(args.SenderSession, out var hands))
|
||||||
|
return;
|
||||||
|
|
||||||
|
hands.ActivateHeldEntity(msg.HandName);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void DropAllItemsInHands(IEntity entity, bool doMobChecks = true)
|
protected override void DropAllItemsInHands(IEntity entity, bool doMobChecks = true)
|
||||||
{
|
{
|
||||||
base.DropAllItemsInHands(entity, doMobChecks);
|
base.DropAllItemsInHands(entity, doMobChecks);
|
||||||
@@ -75,25 +195,21 @@ namespace Content.Server.Hands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void HandleContainerModified(EntityUid uid, SharedHandsComponent component, ContainerModifiedMessage args)
|
private static bool TryGetHandsComp(
|
||||||
{
|
ICommonSession? session,
|
||||||
component.Dirty();
|
[NotNullWhen(true)] out SharedHandsComponent? hands)
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryGetHandsComp(ICommonSession? session, [NotNullWhen(true)] out SharedHandsComponent? hands)
|
|
||||||
{
|
{
|
||||||
hands = default;
|
hands = default;
|
||||||
|
|
||||||
if (session is not IPlayerSession playerSession)
|
if (session is not IPlayerSession playerSession)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var playerEnt = playerSession?.AttachedEntity;
|
var playerEnt = playerSession.AttachedEntity;
|
||||||
|
|
||||||
if (playerEnt == null || !playerEnt.IsValid())
|
if (playerEnt == null || !playerEnt.IsValid())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
playerEnt.TryGetComponent(out hands);
|
return playerEnt.TryGetComponent(out hands);
|
||||||
return hands != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleActivateItem(ICommonSession? session)
|
private void HandleActivateItem(ICommonSession? session)
|
||||||
@@ -387,6 +387,18 @@ namespace Content.Server.Interaction
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<bool> InteractDoBefore(
|
||||||
|
IEntity user,
|
||||||
|
IEntity used,
|
||||||
|
IEntity? target,
|
||||||
|
EntityCoordinates clickLocation,
|
||||||
|
bool canReach)
|
||||||
|
{
|
||||||
|
var ev = new BeforeInteractEvent(user, used, target, clickLocation, canReach);
|
||||||
|
RaiseLocalEvent(used.Uid, ev, false);
|
||||||
|
return ev.Handled;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uses a item/object on an entity
|
/// Uses a item/object on an entity
|
||||||
/// Finds components with the InteractUsing interface and calls their function
|
/// Finds components with the InteractUsing interface and calls their function
|
||||||
@@ -397,6 +409,9 @@ namespace Content.Server.Interaction
|
|||||||
if (!_actionBlockerSystem.CanInteract(user))
|
if (!_actionBlockerSystem.CanInteract(user))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (await InteractDoBefore(user, used, target, clickLocation, true))
|
||||||
|
return;
|
||||||
|
|
||||||
// all interactions should only happen when in range / unobstructed, so no range check is needed
|
// all interactions should only happen when in range / unobstructed, so no range check is needed
|
||||||
var interactUsingEvent = new InteractUsingEvent(user, used, target, clickLocation);
|
var interactUsingEvent = new InteractUsingEvent(user, used, target, clickLocation);
|
||||||
RaiseLocalEvent(target.Uid, interactUsingEvent);
|
RaiseLocalEvent(target.Uid, interactUsingEvent);
|
||||||
@@ -696,6 +711,9 @@ namespace Content.Server.Interaction
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task<bool> InteractUsingRanged(IEntity user, IEntity used, IEntity? target, EntityCoordinates clickLocation, bool inRangeUnobstructed)
|
public async Task<bool> InteractUsingRanged(IEntity user, IEntity used, IEntity? target, EntityCoordinates clickLocation, bool inRangeUnobstructed)
|
||||||
{
|
{
|
||||||
|
if (await InteractDoBefore(user, used, inRangeUnobstructed ? target : null, clickLocation, false))
|
||||||
|
return true;
|
||||||
|
|
||||||
if (target != null)
|
if (target != null)
|
||||||
{
|
{
|
||||||
var rangedMsg = new RangedInteractEvent(user, used, target, clickLocation);
|
var rangedMsg = new RangedInteractEvent(user, used, target, clickLocation);
|
||||||
@@ -715,10 +733,7 @@ namespace Content.Server.Interaction
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inRangeUnobstructed)
|
return await InteractDoAfter(user, used, inRangeUnobstructed ? target : null, clickLocation, false);
|
||||||
return await InteractDoAfter(user, used, target, clickLocation, false);
|
|
||||||
else
|
|
||||||
return await InteractDoAfter(user, used, null, clickLocation, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DoAttack(IEntity user, EntityCoordinates coordinates, bool wideAttack, EntityUid targetUid = default)
|
public void DoAttack(IEntity user, EntityCoordinates coordinates, bool wideAttack, EntityUid targetUid = default)
|
||||||
|
|||||||
@@ -195,6 +195,9 @@ namespace Content.Server.Kitchen.EntitySystems
|
|||||||
|
|
||||||
while (_uiUpdateQueue.TryDequeue(out var comp))
|
while (_uiUpdateQueue.TryDequeue(out var comp))
|
||||||
{
|
{
|
||||||
|
if(comp.Deleted)
|
||||||
|
continue;
|
||||||
|
|
||||||
bool canJuice = false;
|
bool canJuice = false;
|
||||||
bool canGrind = false;
|
bool canGrind = false;
|
||||||
if (comp.BeakerContainer.ContainedEntity != null)
|
if (comp.BeakerContainer.ContainedEntity != null)
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ namespace Content.Server.Light.Components
|
|||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class ExpendableLightComponent : SharedExpendableLightComponent, IUse
|
public class ExpendableLightComponent : SharedExpendableLightComponent, IUse
|
||||||
{
|
{
|
||||||
private static readonly AudioParams LoopedSoundParams = new(0, 1, "Master", 62.5f, 1, true, 0.3f);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Status of light, whether or not it is emitting light.
|
/// Status of light, whether or not it is emitting light.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -77,18 +75,20 @@ namespace Content.Server.Light.Components
|
|||||||
|
|
||||||
private void UpdateVisualizer()
|
private void UpdateVisualizer()
|
||||||
{
|
{
|
||||||
|
_appearance?.SetData(ExpendableLightVisuals.State, CurrentState);
|
||||||
|
|
||||||
switch (CurrentState)
|
switch (CurrentState)
|
||||||
{
|
{
|
||||||
case ExpendableLightState.Lit:
|
case ExpendableLightState.Lit:
|
||||||
_appearance?.SetData(ExpendableLightVisuals.State, TurnOnBehaviourID);
|
_appearance?.SetData(ExpendableLightVisuals.Behavior, TurnOnBehaviourID);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ExpendableLightState.Fading:
|
case ExpendableLightState.Fading:
|
||||||
_appearance?.SetData(ExpendableLightVisuals.State, FadeOutBehaviourID);
|
_appearance?.SetData(ExpendableLightVisuals.Behavior, FadeOutBehaviourID);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ExpendableLightState.Dead:
|
case ExpendableLightState.Dead:
|
||||||
_appearance?.SetData(ExpendableLightVisuals.State, string.Empty);
|
_appearance?.SetData(ExpendableLightVisuals.Behavior, string.Empty);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,12 +100,6 @@ namespace Content.Server.Light.Components
|
|||||||
switch (CurrentState)
|
switch (CurrentState)
|
||||||
{
|
{
|
||||||
case ExpendableLightState.Lit:
|
case ExpendableLightState.Lit:
|
||||||
|
|
||||||
if (LoopedSound != string.Empty && Owner.TryGetComponent<LoopingLoopingSoundComponent>(out var loopingSound))
|
|
||||||
{
|
|
||||||
loopingSound.Play(LoopedSound, LoopedSoundParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LitSound.TryGetSound(out var litSound))
|
if (LitSound.TryGetSound(out var litSound))
|
||||||
{
|
{
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), litSound, Owner);
|
SoundSystem.Play(Filter.Pvs(Owner), litSound, Owner);
|
||||||
@@ -131,11 +125,6 @@ namespace Content.Server.Light.Components
|
|||||||
SoundSystem.Play(Filter.Pvs(Owner), dieSound, Owner);
|
SoundSystem.Play(Filter.Pvs(Owner), dieSound, Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LoopedSound != string.Empty && Owner.TryGetComponent<LoopingLoopingSoundComponent>(out var loopSound))
|
|
||||||
{
|
|
||||||
loopSound.StopAllSounds();
|
|
||||||
}
|
|
||||||
|
|
||||||
sprite.LayerSetState(0, IconStateSpent);
|
sprite.LayerSetState(0, IconStateSpent);
|
||||||
sprite.LayerSetShader(0, "shaded");
|
sprite.LayerSetShader(0, "shaded");
|
||||||
sprite.LayerSetVisible(1, false);
|
sprite.LayerSetVisible(1, false);
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
private readonly HashSet<PowerNet> _powerNetReconnectQueue = new();
|
private readonly HashSet<PowerNet> _powerNetReconnectQueue = new();
|
||||||
private readonly HashSet<ApcNet> _apcNetReconnectQueue = new();
|
private readonly HashSet<ApcNet> _apcNetReconnectQueue = new();
|
||||||
|
|
||||||
private int _nextId = 1;
|
|
||||||
private readonly BatteryRampPegSolver _solver = new();
|
private readonly BatteryRampPegSolver _solver = new();
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -50,7 +49,7 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
private void ApcPowerReceiverShutdown(EntityUid uid, ApcPowerReceiverComponent component,
|
private void ApcPowerReceiverShutdown(EntityUid uid, ApcPowerReceiverComponent component,
|
||||||
ComponentShutdown args)
|
ComponentShutdown args)
|
||||||
{
|
{
|
||||||
_powerState.Loads.Remove(component.NetworkLoad.Id);
|
_powerState.Loads.Free(component.NetworkLoad.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ApcPowerReceiverPaused(
|
private static void ApcPowerReceiverPaused(
|
||||||
@@ -68,7 +67,7 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
|
|
||||||
private void BatteryShutdown(EntityUid uid, PowerNetworkBatteryComponent component, ComponentShutdown args)
|
private void BatteryShutdown(EntityUid uid, PowerNetworkBatteryComponent component, ComponentShutdown args)
|
||||||
{
|
{
|
||||||
_powerState.Batteries.Remove(component.NetworkBattery.Id);
|
_powerState.Batteries.Free(component.NetworkBattery.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void BatteryPaused(EntityUid uid, PowerNetworkBatteryComponent component, EntityPausedEvent args)
|
private static void BatteryPaused(EntityUid uid, PowerNetworkBatteryComponent component, EntityPausedEvent args)
|
||||||
@@ -83,7 +82,7 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
|
|
||||||
private void PowerConsumerShutdown(EntityUid uid, PowerConsumerComponent component, ComponentShutdown args)
|
private void PowerConsumerShutdown(EntityUid uid, PowerConsumerComponent component, ComponentShutdown args)
|
||||||
{
|
{
|
||||||
_powerState.Loads.Remove(component.NetworkLoad.Id);
|
_powerState.Loads.Free(component.NetworkLoad.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PowerConsumerPaused(EntityUid uid, PowerConsumerComponent component, EntityPausedEvent args)
|
private static void PowerConsumerPaused(EntityUid uid, PowerConsumerComponent component, EntityPausedEvent args)
|
||||||
@@ -98,7 +97,7 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
|
|
||||||
private void PowerSupplierShutdown(EntityUid uid, PowerSupplierComponent component, ComponentShutdown args)
|
private void PowerSupplierShutdown(EntityUid uid, PowerSupplierComponent component, ComponentShutdown args)
|
||||||
{
|
{
|
||||||
_powerState.Supplies.Remove(component.NetworkSupply.Id);
|
_powerState.Supplies.Free(component.NetworkSupply.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PowerSupplierPaused(EntityUid uid, PowerSupplierComponent component, EntityPausedEvent args)
|
private static void PowerSupplierPaused(EntityUid uid, PowerSupplierComponent component, EntityPausedEvent args)
|
||||||
@@ -113,7 +112,7 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
|
|
||||||
public void DestroyPowerNet(PowerNet powerNet)
|
public void DestroyPowerNet(PowerNet powerNet)
|
||||||
{
|
{
|
||||||
_powerState.Networks.Remove(powerNet.NetworkNode.Id);
|
_powerState.Networks.Free(powerNet.NetworkNode.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void QueueReconnectPowerNet(PowerNet powerNet)
|
public void QueueReconnectPowerNet(PowerNet powerNet)
|
||||||
@@ -128,7 +127,7 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
|
|
||||||
public void DestroyApcNet(ApcNet apcNet)
|
public void DestroyApcNet(ApcNet apcNet)
|
||||||
{
|
{
|
||||||
_powerState.Networks.Remove(apcNet.NetworkNode.Id);
|
_powerState.Networks.Free(apcNet.NetworkNode.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void QueueReconnectApcNet(ApcNet apcNet)
|
public void QueueReconnectApcNet(ApcNet apcNet)
|
||||||
@@ -213,26 +212,22 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
|
|
||||||
private void AllocLoad(PowerState.Load load)
|
private void AllocLoad(PowerState.Load load)
|
||||||
{
|
{
|
||||||
load.Id = AllocId();
|
_powerState.Loads.Allocate(out load.Id) = load;
|
||||||
_powerState.Loads.Add(load.Id, load);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AllocSupply(PowerState.Supply supply)
|
private void AllocSupply(PowerState.Supply supply)
|
||||||
{
|
{
|
||||||
supply.Id = AllocId();
|
_powerState.Supplies.Allocate(out supply.Id) = supply;
|
||||||
_powerState.Supplies.Add(supply.Id, supply);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AllocBattery(PowerState.Battery battery)
|
private void AllocBattery(PowerState.Battery battery)
|
||||||
{
|
{
|
||||||
battery.Id = AllocId();
|
_powerState.Batteries.Allocate(out battery.Id) = battery;
|
||||||
_powerState.Batteries.Add(battery.Id, battery);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AllocNetwork(PowerState.Network network)
|
private void AllocNetwork(PowerState.Network network)
|
||||||
{
|
{
|
||||||
network.Id = AllocId();
|
_powerState.Networks.Allocate(out network.Id) = network;
|
||||||
_powerState.Networks.Add(network.Id, network);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void DoReconnectApcNet(ApcNet net)
|
private static void DoReconnectApcNet(ApcNet net)
|
||||||
@@ -296,11 +291,6 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
battery.NetworkBattery.LinkedNetworkDischarging = netNode.Id;
|
battery.NetworkBattery.LinkedNetworkDischarging = netNode.Id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private PowerState.NodeId AllocId()
|
|
||||||
{
|
|
||||||
return new(_nextId++);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,181 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using static Content.Server.Power.Pow3r.PowerState;
|
|
||||||
|
|
||||||
namespace Content.Server.Power.Pow3r
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Partial implementation of full-graph-walking power solving under pow3r.
|
|
||||||
/// Concept described at https://hackmd.io/@ss14/lowpower
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Many features like batteries, cycle detection, join handling, etc... are not implemented at all.
|
|
||||||
/// Seriously, this implementation barely works. Ah well.
|
|
||||||
/// <see cref="BatteryRampPegSolver"/> is better.
|
|
||||||
/// </remarks>
|
|
||||||
public class GraphWalkSolver : IPowerSolver
|
|
||||||
{
|
|
||||||
public void Tick(float frameTime, PowerState state)
|
|
||||||
{
|
|
||||||
foreach (var load in state.Loads.Values)
|
|
||||||
{
|
|
||||||
load.ReceivingPower = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var supply in state.Supplies.Values)
|
|
||||||
{
|
|
||||||
supply.CurrentSupply = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var network in state.Networks.Values)
|
|
||||||
{
|
|
||||||
// Clear some stuff.
|
|
||||||
network.LocalDemandMet = 0;
|
|
||||||
|
|
||||||
// Add up demands in network.
|
|
||||||
network.LocalDemandTotal = network.Loads
|
|
||||||
.Select(l => state.Loads[l])
|
|
||||||
.Where(c => c.Enabled)
|
|
||||||
.Sum(c => c.DesiredPower);
|
|
||||||
|
|
||||||
// Add up supplies in network.
|
|
||||||
var availableSupplySum = 0f;
|
|
||||||
var maxSupplySum = 0f;
|
|
||||||
foreach (var supplyId in network.Supplies)
|
|
||||||
{
|
|
||||||
var supply = state.Supplies[supplyId];
|
|
||||||
if (!supply.Enabled)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var rampMax = supply.SupplyRampPosition + supply.SupplyRampTolerance;
|
|
||||||
var effectiveSupply = Math.Min(rampMax, supply.MaxSupply);
|
|
||||||
supply.EffectiveMaxSupply = effectiveSupply;
|
|
||||||
availableSupplySum += effectiveSupply;
|
|
||||||
maxSupplySum += supply.MaxSupply;
|
|
||||||
}
|
|
||||||
|
|
||||||
network.AvailableSupplyTotal = availableSupplySum;
|
|
||||||
network.TheoreticalSupplyTotal = maxSupplySum;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort networks by tree height so that suppliers that have less possible loads go FIRST.
|
|
||||||
// Idea being that a backup generator on a small subnet should do more work
|
|
||||||
// so that a larger generator that covers more networks can put its power elsewhere.
|
|
||||||
var sortedByHeight = state.Networks.Values.OrderBy(v => TotalSubLoadCount(state, v)).ToArray();
|
|
||||||
|
|
||||||
// Go over every network with supply to send power.
|
|
||||||
foreach (var network in sortedByHeight)
|
|
||||||
{
|
|
||||||
// Find all loads recursively, and sum them up.
|
|
||||||
var subNets = new List<Network>();
|
|
||||||
var totalDemand = 0f;
|
|
||||||
GetLoadingNetworksRecursively(state, network, subNets, ref totalDemand);
|
|
||||||
|
|
||||||
if (totalDemand == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Calculate power delivered.
|
|
||||||
var power = Math.Min(totalDemand, network.AvailableSupplyTotal);
|
|
||||||
|
|
||||||
// Distribute load across supplies in network.
|
|
||||||
foreach (var supplyId in network.Supplies)
|
|
||||||
{
|
|
||||||
var supply = state.Supplies[supplyId];
|
|
||||||
if (!supply.Enabled)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (supply.EffectiveMaxSupply != 0)
|
|
||||||
{
|
|
||||||
var ratio = supply.EffectiveMaxSupply / network.AvailableSupplyTotal;
|
|
||||||
|
|
||||||
supply.CurrentSupply = ratio * power;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
supply.CurrentSupply = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (supply.MaxSupply != 0)
|
|
||||||
{
|
|
||||||
var ratio = supply.MaxSupply / network.TheoreticalSupplyTotal;
|
|
||||||
|
|
||||||
supply.SupplyRampTarget = ratio * totalDemand;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
supply.SupplyRampTarget = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Distribute supply across subnet loads.
|
|
||||||
foreach (var subNet in subNets)
|
|
||||||
{
|
|
||||||
var rem = subNet.RemainingDemand;
|
|
||||||
var ratio = rem / totalDemand;
|
|
||||||
|
|
||||||
subNet.LocalDemandMet += ratio * power;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Distribute power across loads in networks.
|
|
||||||
foreach (var network in state.Networks.Values)
|
|
||||||
{
|
|
||||||
if (network.LocalDemandMet == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
foreach (var loadId in network.Loads)
|
|
||||||
{
|
|
||||||
var load = state.Loads[loadId];
|
|
||||||
if (!load.Enabled)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var ratio = load.DesiredPower / network.LocalDemandTotal;
|
|
||||||
load.ReceivingPower = ratio * network.LocalDemandMet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerSolverShared.UpdateRampPositions(frameTime, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int TotalSubLoadCount(PowerState state, Network network)
|
|
||||||
{
|
|
||||||
// TODO: Cycle detection.
|
|
||||||
var height = network.Loads.Count;
|
|
||||||
|
|
||||||
foreach (var batteryId in network.BatteriesCharging)
|
|
||||||
{
|
|
||||||
var battery = state.Batteries[batteryId];
|
|
||||||
if (battery.LinkedNetworkDischarging != default)
|
|
||||||
{
|
|
||||||
height += TotalSubLoadCount(state, state.Networks[battery.LinkedNetworkDischarging]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetLoadingNetworksRecursively(
|
|
||||||
PowerState state,
|
|
||||||
Network network,
|
|
||||||
List<Network> networks,
|
|
||||||
ref float totalDemand)
|
|
||||||
{
|
|
||||||
networks.Add(network);
|
|
||||||
totalDemand += network.LocalDemandTotal - network.LocalDemandMet;
|
|
||||||
|
|
||||||
foreach (var batteryId in network.BatteriesCharging)
|
|
||||||
{
|
|
||||||
var battery = state.Batteries[batteryId];
|
|
||||||
if (battery.LinkedNetworkDischarging != default)
|
|
||||||
{
|
|
||||||
GetLoadingNetworksRecursively(
|
|
||||||
state,
|
|
||||||
state.Networks[battery.LinkedNetworkDischarging],
|
|
||||||
networks,
|
|
||||||
ref totalDemand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +1,51 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Numerics;
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Server.Power.Pow3r
|
namespace Content.Server.Power.Pow3r
|
||||||
{
|
{
|
||||||
public sealed class PowerState
|
public sealed class PowerState
|
||||||
{
|
{
|
||||||
public const int MaxTickData = 180;
|
|
||||||
|
|
||||||
public static readonly JsonSerializerOptions SerializerOptions = new()
|
public static readonly JsonSerializerOptions SerializerOptions = new()
|
||||||
{
|
{
|
||||||
IncludeFields = true,
|
IncludeFields = true,
|
||||||
Converters = {new NodeIdJsonConverter()}
|
Converters = {new NodeIdJsonConverter()}
|
||||||
};
|
};
|
||||||
|
|
||||||
public Dictionary<NodeId, Supply> Supplies = new();
|
public GenIdStorage<Supply> Supplies = new();
|
||||||
public Dictionary<NodeId, Network> Networks = new();
|
public GenIdStorage<Network> Networks = new();
|
||||||
public Dictionary<NodeId, Load> Loads = new();
|
public GenIdStorage<Load> Loads = new();
|
||||||
public Dictionary<NodeId, Battery> Batteries = new();
|
public GenIdStorage<Battery> Batteries = new();
|
||||||
|
|
||||||
public readonly struct NodeId : IEquatable<NodeId>
|
public readonly struct NodeId : IEquatable<NodeId>
|
||||||
{
|
{
|
||||||
public readonly int Id;
|
public readonly int Index;
|
||||||
|
public readonly int Generation;
|
||||||
|
|
||||||
public NodeId(int id)
|
public long Combined => (uint) Index | ((long) Generation << 32);
|
||||||
|
|
||||||
|
public NodeId(int index, int generation)
|
||||||
{
|
{
|
||||||
Id = id;
|
Index = index;
|
||||||
|
Generation = generation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeId(long combined)
|
||||||
|
{
|
||||||
|
Index = (int) combined;
|
||||||
|
Generation = (int) (combined >> 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(NodeId other)
|
public bool Equals(NodeId other)
|
||||||
{
|
{
|
||||||
return Id == other.Id;
|
return Index == other.Index && Generation == other.Generation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
@@ -44,7 +55,7 @@ namespace Content.Server.Power.Pow3r
|
|||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return Id;
|
return HashCode.Combine(Index, Generation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool operator ==(NodeId left, NodeId right)
|
public static bool operator ==(NodeId left, NodeId right)
|
||||||
@@ -59,7 +70,261 @@ namespace Content.Server.Power.Pow3r
|
|||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return Id.ToString();
|
return $"{Index} (G{Generation})";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GenIdStorage
|
||||||
|
{
|
||||||
|
public static GenIdStorage<T> FromEnumerable<T>(IEnumerable<(NodeId, T)> enumerable)
|
||||||
|
{
|
||||||
|
return GenIdStorage<T>.FromEnumerable(enumerable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class GenIdStorage<T>
|
||||||
|
{
|
||||||
|
// This is an implementation of "generational index" storage.
|
||||||
|
//
|
||||||
|
// The advantage of this storage method is extremely fast, O(1) lookup (way faster than Dictionary).
|
||||||
|
// Resolving a value in the storage is a single array load and generation compare. Extremely fast.
|
||||||
|
// Indices can also be cached into temporary
|
||||||
|
// Disadvantages are that storage cannot be shrunk, and sparse storage is inefficient space wise.
|
||||||
|
// Also this implementation does not have optimizations necessary to make sparse iteration efficient.
|
||||||
|
//
|
||||||
|
// The idea here is that the index type (NodeId in this case) has both an index and a generation.
|
||||||
|
// The index is an integer index into the storage array, the generation is used to avoid use-after-free.
|
||||||
|
//
|
||||||
|
// Empty slots in the array form a linked list of free slots.
|
||||||
|
// When we allocate a new slot, we pop one link off this linked list and hand out its index + generation.
|
||||||
|
//
|
||||||
|
// When we free a node, we bump the generation of the slot and make it the head of the linked list.
|
||||||
|
// The generation being bumped means that any IDs to this slot will fail to resolve (generation mismatch).
|
||||||
|
//
|
||||||
|
|
||||||
|
// Index of the next free slot to use when allocating a new one.
|
||||||
|
// If this is int.MaxValue,
|
||||||
|
// it basically means "no slot available" and the next allocation call should resize the array storage.
|
||||||
|
private int _nextFree = int.MaxValue;
|
||||||
|
private Slot[] _storage;
|
||||||
|
|
||||||
|
public int Count { get; private set; }
|
||||||
|
|
||||||
|
public ref T this[NodeId id]
|
||||||
|
{
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ref var slot = ref _storage[id.Index];
|
||||||
|
if (slot.Generation != id.Generation)
|
||||||
|
ThrowKeyNotFound();
|
||||||
|
|
||||||
|
return ref slot.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public GenIdStorage()
|
||||||
|
{
|
||||||
|
_storage = Array.Empty<Slot>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GenIdStorage<T> FromEnumerable(IEnumerable<(NodeId, T)> enumerable)
|
||||||
|
{
|
||||||
|
var storage = new GenIdStorage<T>();
|
||||||
|
|
||||||
|
// Cache enumerable to array to do double enumeration.
|
||||||
|
var cache = enumerable.ToArray();
|
||||||
|
|
||||||
|
if (cache.Length == 0)
|
||||||
|
return storage;
|
||||||
|
|
||||||
|
// Figure out max size necessary and set storage size to that.
|
||||||
|
var maxSize = cache.Max(tup => tup.Item1.Index) + 1;
|
||||||
|
storage._storage = new Slot[maxSize];
|
||||||
|
|
||||||
|
// Fill in slots.
|
||||||
|
foreach (var (id, value) in cache)
|
||||||
|
{
|
||||||
|
DebugTools.Assert(id.Generation != 0, "Generation cannot be 0");
|
||||||
|
|
||||||
|
ref var slot = ref storage._storage[id.Index];
|
||||||
|
DebugTools.Assert(slot.Generation == 0, "Duplicate key index!");
|
||||||
|
|
||||||
|
slot.Generation = id.Generation;
|
||||||
|
slot.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go through empty slots and build the free chain.
|
||||||
|
var nextFree = int.MaxValue;
|
||||||
|
for (var i = 0; i < storage._storage.Length; i++)
|
||||||
|
{
|
||||||
|
ref var slot = ref storage._storage[i];
|
||||||
|
|
||||||
|
if (slot.Generation != 0)
|
||||||
|
// Slot in use.
|
||||||
|
continue;
|
||||||
|
|
||||||
|
slot.NextSlot = nextFree;
|
||||||
|
nextFree = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
storage.Count = cache.Length;
|
||||||
|
storage._nextFree = nextFree;
|
||||||
|
|
||||||
|
return storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ref T Allocate(out NodeId id)
|
||||||
|
{
|
||||||
|
if (_nextFree == int.MaxValue)
|
||||||
|
ReAllocate();
|
||||||
|
|
||||||
|
var idx = _nextFree;
|
||||||
|
ref var slot = ref _storage[idx];
|
||||||
|
|
||||||
|
Count += 1;
|
||||||
|
_nextFree = slot.NextSlot;
|
||||||
|
// NextSlot = -1 indicates filled.
|
||||||
|
slot.NextSlot = -1;
|
||||||
|
|
||||||
|
id = new NodeId(idx, slot.Generation);
|
||||||
|
return ref slot.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Free(NodeId id)
|
||||||
|
{
|
||||||
|
var idx = id.Index;
|
||||||
|
ref var slot = ref _storage[idx];
|
||||||
|
if (slot.Generation != id.Generation)
|
||||||
|
ThrowKeyNotFound();
|
||||||
|
|
||||||
|
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
||||||
|
slot.Value = default!;
|
||||||
|
|
||||||
|
Count -= 1;
|
||||||
|
slot.Generation += 1;
|
||||||
|
slot.NextSlot = _nextFree;
|
||||||
|
_nextFree = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private void ReAllocate()
|
||||||
|
{
|
||||||
|
var oldLength = _storage.Length;
|
||||||
|
var newLength = Math.Max(oldLength, 2) * 2;
|
||||||
|
|
||||||
|
ReAllocateTo(newLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReAllocateTo(int newSize)
|
||||||
|
{
|
||||||
|
var oldLength = _storage.Length;
|
||||||
|
DebugTools.Assert(newSize >= oldLength, "Cannot shrink GenIdStorage");
|
||||||
|
|
||||||
|
Array.Resize(ref _storage, newSize);
|
||||||
|
|
||||||
|
for (var i = oldLength; i < newSize - 1; i++)
|
||||||
|
{
|
||||||
|
// Build linked list chain for newly allocated segment.
|
||||||
|
ref var slot = ref _storage[i];
|
||||||
|
slot.NextSlot = i + 1;
|
||||||
|
// Every slot starts at generation 1.
|
||||||
|
slot.Generation = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_storage[^1].NextSlot = _nextFree;
|
||||||
|
|
||||||
|
_nextFree = oldLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValuesCollection Values => new(this);
|
||||||
|
|
||||||
|
private struct Slot
|
||||||
|
{
|
||||||
|
// Next link on the free list. if int.MaxValue then this is the tail.
|
||||||
|
// If negative, this slot is occupied.
|
||||||
|
public int NextSlot;
|
||||||
|
// Generation of this slot.
|
||||||
|
public int Generation;
|
||||||
|
public T Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
private static void ThrowKeyNotFound()
|
||||||
|
{
|
||||||
|
throw new KeyNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct ValuesCollection : IReadOnlyCollection<T>
|
||||||
|
{
|
||||||
|
private readonly GenIdStorage<T> _owner;
|
||||||
|
|
||||||
|
public ValuesCollection(GenIdStorage<T> owner)
|
||||||
|
{
|
||||||
|
_owner = owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Enumerator GetEnumerator()
|
||||||
|
{
|
||||||
|
return new Enumerator(_owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Count => _owner.Count;
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator<T> IEnumerable<T>.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Enumerator : IEnumerator<T>
|
||||||
|
{
|
||||||
|
// Save the array in the enumerator here to avoid a few pointer dereferences.
|
||||||
|
private readonly Slot[] _owner;
|
||||||
|
private int _index;
|
||||||
|
|
||||||
|
public Enumerator(GenIdStorage<T> owner)
|
||||||
|
{
|
||||||
|
_owner = owner._storage;
|
||||||
|
Current = default!;
|
||||||
|
_index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool MoveNext()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
_index += 1;
|
||||||
|
if (_index >= _owner.Length)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ref var slot = ref _owner[_index];
|
||||||
|
|
||||||
|
if (slot.NextSlot < 0)
|
||||||
|
{
|
||||||
|
Current = slot.Value;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_index = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
object IEnumerator.Current => Current!;
|
||||||
|
|
||||||
|
public T Current { get; private set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,12 +332,12 @@ namespace Content.Server.Power.Pow3r
|
|||||||
{
|
{
|
||||||
public override NodeId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override NodeId Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
return new(reader.GetInt32());
|
return new NodeId(reader.GetInt64());
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Write(Utf8JsonWriter writer, NodeId value, JsonSerializerOptions options)
|
public override void Write(Utf8JsonWriter writer, NodeId value, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
writer.WriteNumberValue(value.Id);
|
writer.WriteNumberValue(value.Combined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,22 +451,8 @@ namespace Content.Server.Power.Pow3r
|
|||||||
// "Supplying" means the network is connected to the OUTPUT port of the battery.
|
// "Supplying" means the network is connected to the OUTPUT port of the battery.
|
||||||
[ViewVariables] public List<NodeId> BatteriesDischarging = new();
|
[ViewVariables] public List<NodeId> BatteriesDischarging = new();
|
||||||
|
|
||||||
// Calculation parameters used by GraphWalkSolver.
|
|
||||||
// Unused by BatteryRampPegSolver.
|
|
||||||
[JsonIgnore] public float LocalDemandTotal;
|
|
||||||
[JsonIgnore] public float LocalDemandMet;
|
|
||||||
[JsonIgnore] public float GroupDemandTotal;
|
|
||||||
[JsonIgnore] public float GroupDemandMet;
|
|
||||||
|
|
||||||
[ViewVariables] [JsonIgnore] public int Height;
|
[ViewVariables] [JsonIgnore] public int Height;
|
||||||
[JsonIgnore] public bool HeightTouched;
|
[JsonIgnore] public bool HeightTouched;
|
||||||
|
|
||||||
// Supply available this tick.
|
|
||||||
[JsonIgnore] public float AvailableSupplyTotal;
|
|
||||||
|
|
||||||
// Max theoretical supply assuming max ramp.
|
|
||||||
[JsonIgnore] public float TheoreticalSupplyTotal;
|
|
||||||
public float RemainingDemand => LocalDemandTotal - LocalDemandMet;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
using Content.Shared.Sound;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Network;
|
|
||||||
|
|
||||||
namespace Content.Server.Sound.Components
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public class LoopingLoopingSoundComponent : SharedLoopingSoundComponent
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Stops all sounds.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="channel">User that will be affected.</param>
|
|
||||||
public void StopAllSounds(INetChannel? channel)
|
|
||||||
{
|
|
||||||
SendNetworkMessage(new StopAllSoundsMessage(), channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stops a certain scheduled sound from playing.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="channel">User that will be affected.</param>
|
|
||||||
public void StopScheduledSound(string filename, INetChannel? channel)
|
|
||||||
{
|
|
||||||
SendNetworkMessage(new StopSoundScheduleMessage() {Filename = filename}, channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds an scheduled sound to be played.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="channel">User that will be affected.</param>
|
|
||||||
public void AddScheduledSound(ScheduledSound schedule, INetChannel? channel)
|
|
||||||
{
|
|
||||||
SendNetworkMessage(new ScheduledSoundMessage() {Schedule = schedule}, channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void StopAllSounds()
|
|
||||||
{
|
|
||||||
StopAllSounds(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void StopScheduledSound(string filename)
|
|
||||||
{
|
|
||||||
StopScheduledSound(filename, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void AddScheduledSound(ScheduledSound schedule)
|
|
||||||
{
|
|
||||||
AddScheduledSound(schedule, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Play an audio file following the entity.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
|
||||||
/// <param name="channel">User that will be affected.</param>
|
|
||||||
public void Play(string filename, AudioParams? audioParams = null, INetChannel? channel = null)
|
|
||||||
{
|
|
||||||
AddScheduledSound(new ScheduledSound()
|
|
||||||
{
|
|
||||||
Filename = filename,
|
|
||||||
AudioParams = audioParams,
|
|
||||||
}, channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,13 @@
|
|||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
namespace Content.Shared.Chemistry.Metabolizable
|
namespace Content.Shared.Chemistry.Metabolizable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default metabolism for reagents. Metabolizes the reagent with no effects
|
/// Default metabolization for reagents. Returns the amount of reagents metabolized without applying effects.
|
||||||
|
/// Metabolizes reagents at a constant rate, limited by how much is available. Other classes are derived from
|
||||||
|
/// this class, so that they do not need their own metabolization quantity calculation.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataDefinition]
|
[DataDefinition]
|
||||||
public class DefaultMetabolizable : IMetabolizable
|
public class DefaultMetabolizable : IMetabolizable
|
||||||
@@ -13,12 +15,22 @@ namespace Content.Shared.Chemistry.Metabolizable
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rate of metabolism in units / second
|
/// Rate of metabolism in units / second
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("rate")]
|
[DataField("rate")] public ReagentUnit MetabolismRate { get; set; } = ReagentUnit.New(1);
|
||||||
public double MetabolismRate { get; set; } = 1;
|
|
||||||
|
|
||||||
ReagentUnit IMetabolizable.Metabolize(IEntity solutionEntity, string reagentId, float tickTime)
|
public virtual ReagentUnit Metabolize(IEntity solutionEntity, string reagentId, float tickTime, ReagentUnit availableReagent)
|
||||||
{
|
{
|
||||||
return ReagentUnit.New(MetabolismRate * tickTime);
|
|
||||||
|
// How much reagent should we metabolize
|
||||||
|
// The default behaviour is to metabolize at a constant rate, independent of the quantity of reagents.
|
||||||
|
var amountMetabolized = MetabolismRate * tickTime;
|
||||||
|
|
||||||
|
// is that much reagent actually available?
|
||||||
|
if (availableReagent < amountMetabolized)
|
||||||
|
{
|
||||||
|
return availableReagent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return amountMetabolized;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
namespace Content.Shared.Chemistry.Metabolizable
|
namespace Content.Shared.Chemistry.Metabolizable
|
||||||
@@ -16,7 +16,8 @@ namespace Content.Shared.Chemistry.Metabolizable
|
|||||||
/// <param name="solutionEntity">The entity containing the solution.</param>
|
/// <param name="solutionEntity">The entity containing the solution.</param>
|
||||||
/// <param name="reagentId">The reagent id</param>
|
/// <param name="reagentId">The reagent id</param>
|
||||||
/// <param name="tickTime">The time since the last metabolism tick in seconds.</param>
|
/// <param name="tickTime">The time since the last metabolism tick in seconds.</param>
|
||||||
|
/// <param name="availableReagent">Reagent available to be metabolized.</param>
|
||||||
/// <returns>The amount of reagent to be removed. The metabolizing organ should handle removing the reagent.</returns>
|
/// <returns>The amount of reagent to be removed. The metabolizing organ should handle removing the reagent.</returns>
|
||||||
ReagentUnit Metabolize(IEntity solutionEntity, string reagentId, float tickTime);
|
ReagentUnit Metabolize(IEntity solutionEntity, string reagentId, float tickTime, ReagentUnit availableReagent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Content.Shared.Chemistry.Reagent
|
|||||||
Ingestion,
|
Ingestion,
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataDefinition]
|
[ImplicitDataDefinitionForInheritors]
|
||||||
public abstract class ReagentEntityReaction
|
public abstract class ReagentEntityReaction
|
||||||
{
|
{
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
|
|||||||
50
Content.Shared/Hands/Components/HandVirtualPullComponent.cs
Normal file
50
Content.Shared/Hands/Components/HandVirtualPullComponent.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using System;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Players;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Hands.Components
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
[NetworkedComponent]
|
||||||
|
public sealed class HandVirtualPullComponent : Component
|
||||||
|
{
|
||||||
|
private EntityUid _pulledEntity;
|
||||||
|
public override string Name => "HandVirtualPull";
|
||||||
|
|
||||||
|
public EntityUid PulledEntity
|
||||||
|
{
|
||||||
|
get => _pulledEntity;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_pulledEntity = value;
|
||||||
|
Dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ComponentState GetComponentState(ICommonSession player)
|
||||||
|
{
|
||||||
|
return new VirtualPullComponentState(_pulledEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||||
|
{
|
||||||
|
if (curState is not VirtualPullComponentState pullState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_pulledEntity = pullState.PulledEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class VirtualPullComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public readonly EntityUid PulledEntity;
|
||||||
|
|
||||||
|
public VirtualPullComponentState(EntityUid pulledEntity)
|
||||||
|
{
|
||||||
|
PulledEntity = pulledEntity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -55,11 +55,11 @@ namespace Content.Shared.Hands.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string? _activeHand;
|
private string? _activeHand;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public IReadOnlyList<IReadOnlyHand> ReadOnlyHands => Hands;
|
public readonly List<Hand> Hands = new();
|
||||||
protected readonly List<Hand> Hands = new();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of throw impulse per distance the player is from the throw target.
|
/// The amount of throw impulse per distance the player is from the throw target.
|
||||||
@@ -89,7 +89,10 @@ namespace Content.Shared.Hands.Components
|
|||||||
|
|
||||||
public virtual void HandsModified()
|
public virtual void HandsModified()
|
||||||
{
|
{
|
||||||
|
// todo axe all this for ECS.
|
||||||
Dirty();
|
Dirty();
|
||||||
|
|
||||||
|
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new HandsModifiedMessage { Hands = this });
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddHand(string handName, HandLocation handLocation)
|
public void AddHand(string handName, HandLocation handLocation)
|
||||||
@@ -100,7 +103,7 @@ namespace Content.Shared.Hands.Components
|
|||||||
var container = ContainerHelpers.CreateContainer<ContainerSlot>(Owner, handName);
|
var container = ContainerHelpers.CreateContainer<ContainerSlot>(Owner, handName);
|
||||||
container.OccludesLight = false;
|
container.OccludesLight = false;
|
||||||
|
|
||||||
Hands.Add(new Hand(handName, true, handLocation, container));
|
Hands.Add(new Hand(handName, handLocation, container));
|
||||||
|
|
||||||
if (ActiveHand == null)
|
if (ActiveHand == null)
|
||||||
ActiveHand = handName;
|
ActiveHand = handName;
|
||||||
@@ -125,48 +128,55 @@ namespace Content.Shared.Hands.Components
|
|||||||
Hands.Remove(hand);
|
Hands.Remove(hand);
|
||||||
|
|
||||||
if (ActiveHand == hand.Name)
|
if (ActiveHand == hand.Name)
|
||||||
ActiveHand = ReadOnlyHands.FirstOrDefault()?.Name;
|
ActiveHand = Hands.FirstOrDefault()?.Name;
|
||||||
|
|
||||||
HandCountChanged();
|
HandCountChanged();
|
||||||
|
|
||||||
HandsModified();
|
HandsModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasHand(string handName)
|
|
||||||
{
|
|
||||||
foreach (var hand in Hands)
|
|
||||||
{
|
|
||||||
if (hand.Name == handName)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Hand? GetHand(string handName)
|
|
||||||
{
|
|
||||||
foreach (var hand in Hands)
|
|
||||||
{
|
|
||||||
if (hand.Name == handName)
|
|
||||||
return hand;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Hand? GetActiveHand()
|
private Hand? GetActiveHand()
|
||||||
{
|
{
|
||||||
if (ActiveHand == null)
|
if (ActiveHand == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return GetHand(ActiveHand);
|
return GetHandOrNull(ActiveHand);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool TryGetHand(string handName, [NotNullWhen(true)] out Hand? foundHand)
|
public bool HasHand(string handName)
|
||||||
{
|
{
|
||||||
foundHand = GetHand(handName);
|
return TryGetHand(handName, out _);
|
||||||
return foundHand != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool TryGetActiveHand([NotNullWhen(true)] out Hand? activeHand)
|
public Hand? GetHandOrNull(string handName)
|
||||||
|
{
|
||||||
|
return TryGetHand(handName, out var hand) ? hand : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Hand GetHand(string handName)
|
||||||
|
{
|
||||||
|
if (!TryGetHand(handName, out var hand))
|
||||||
|
throw new KeyNotFoundException($"Unable to find hand with name {handName}");
|
||||||
|
|
||||||
|
return hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetHand(string handName, [NotNullWhen(true)] out Hand? foundHand)
|
||||||
|
{
|
||||||
|
foreach (var hand in Hands)
|
||||||
|
{
|
||||||
|
if (hand.Name == handName)
|
||||||
|
{
|
||||||
|
foundHand = hand;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
foundHand = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryGetActiveHand([NotNullWhen(true)] out Hand? activeHand)
|
||||||
{
|
{
|
||||||
activeHand = GetActiveHand();
|
activeHand = GetActiveHand();
|
||||||
return activeHand != null;
|
return activeHand != null;
|
||||||
@@ -211,7 +221,7 @@ namespace Content.Shared.Hands.Components
|
|||||||
|
|
||||||
public IEnumerable<IEntity> GetAllHeldEntities()
|
public IEnumerable<IEntity> GetAllHeldEntities()
|
||||||
{
|
{
|
||||||
foreach (var hand in ReadOnlyHands)
|
foreach (var hand in Hands)
|
||||||
{
|
{
|
||||||
if (hand.HeldEntity != null)
|
if (hand.HeldEntity != null)
|
||||||
yield return hand.HeldEntity;
|
yield return hand.HeldEntity;
|
||||||
@@ -416,7 +426,7 @@ namespace Content.Shared.Hands.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Drops a hands contents to the target location.
|
/// Drops a hands contents to the target location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void DropHeldEntity(Hand hand, EntityCoordinates targetDropLocation, bool intentionalDrop = true)
|
public void DropHeldEntity(Hand hand, EntityCoordinates targetDropLocation, bool intentionalDrop = true)
|
||||||
{
|
{
|
||||||
var heldEntity = hand.HeldEntity;
|
var heldEntity = hand.HeldEntity;
|
||||||
|
|
||||||
@@ -538,16 +548,7 @@ namespace Content.Shared.Hands.Components
|
|||||||
|
|
||||||
public bool CanPickupEntityToActiveHand(IEntity entity, bool checkActionBlocker = true)
|
public bool CanPickupEntityToActiveHand(IEntity entity, bool checkActionBlocker = true)
|
||||||
{
|
{
|
||||||
if (!TryGetActiveHand(out var hand))
|
return ActiveHand != null && CanPickupEntity(ActiveHand, entity, checkActionBlocker);
|
||||||
return false;
|
|
||||||
|
|
||||||
if (checkActionBlocker && !PlayerCanPickup())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!CanInsertEntityIntoHand(hand, entity))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -563,10 +564,7 @@ namespace Content.Shared.Hands.Components
|
|||||||
|
|
||||||
public bool TryPickupEntityToActiveHand(IEntity entity, bool checkActionBlocker = true)
|
public bool TryPickupEntityToActiveHand(IEntity entity, bool checkActionBlocker = true)
|
||||||
{
|
{
|
||||||
if (!TryGetActiveHand(out var hand))
|
return ActiveHand != null && TryPickupEntity(ActiveHand, entity, checkActionBlocker);
|
||||||
return false;
|
|
||||||
|
|
||||||
return TryPickupEntity(hand, entity, checkActionBlocker);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -574,9 +572,6 @@ namespace Content.Shared.Hands.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected bool CanInsertEntityIntoHand(Hand hand, IEntity entity)
|
protected bool CanInsertEntityIntoHand(Hand hand, IEntity entity)
|
||||||
{
|
{
|
||||||
if (!hand.Enabled)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var handContainer = hand.Container;
|
var handContainer = hand.Container;
|
||||||
if (handContainer == null)
|
if (handContainer == null)
|
||||||
return false;
|
return false;
|
||||||
@@ -602,7 +597,7 @@ namespace Content.Shared.Hands.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Puts an entity into the player's hand, assumes that the insertion is allowed.
|
/// Puts an entity into the player's hand, assumes that the insertion is allowed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void PutEntityIntoHand(Hand hand, IEntity entity)
|
public void PutEntityIntoHand(Hand hand, IEntity entity)
|
||||||
{
|
{
|
||||||
var handContainer = hand.Container;
|
var handContainer = hand.Container;
|
||||||
if (handContainer == null)
|
if (handContainer == null)
|
||||||
@@ -658,7 +653,7 @@ namespace Content.Shared.Hands.Components
|
|||||||
if (newActiveIndex > finalHandIndex)
|
if (newActiveIndex > finalHandIndex)
|
||||||
newActiveIndex = 0;
|
newActiveIndex = 0;
|
||||||
|
|
||||||
nextHand = ReadOnlyHands[newActiveIndex].Name;
|
nextHand = Hands[newActiveIndex].Name;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,7 +747,7 @@ namespace Content.Shared.Hands.Components
|
|||||||
Hand? priorityHand = null;
|
Hand? priorityHand = null;
|
||||||
|
|
||||||
if (priorityHandName != null)
|
if (priorityHandName != null)
|
||||||
priorityHand = GetHand(priorityHandName);
|
priorityHand = GetHandOrNull(priorityHandName);
|
||||||
|
|
||||||
return TryPutInAnyHand(entity, priorityHand, checkActionBlocker);
|
return TryPutInAnyHand(entity, priorityHand, checkActionBlocker);
|
||||||
}
|
}
|
||||||
@@ -793,43 +788,16 @@ namespace Content.Shared.Hands.Components
|
|||||||
protected virtual void DoActivate(IEntity heldEntity) { }
|
protected virtual void DoActivate(IEntity heldEntity) { }
|
||||||
|
|
||||||
protected virtual void HandlePickupAnimation(IEntity entity) { }
|
protected virtual void HandlePickupAnimation(IEntity entity) { }
|
||||||
|
|
||||||
protected void EnableHand(Hand hand)
|
|
||||||
{
|
|
||||||
hand.Enabled = true;
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void DisableHand(Hand hand)
|
|
||||||
{
|
|
||||||
hand.Enabled = false;
|
|
||||||
DropHeldEntityToFloor(hand, intentionalDrop: false);
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IReadOnlyHand
|
public class Hand
|
||||||
{
|
{
|
||||||
|
[ViewVariables]
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
public bool Enabled { get; }
|
[ViewVariables]
|
||||||
|
|
||||||
public HandLocation Location { get; }
|
public HandLocation Location { get; }
|
||||||
|
|
||||||
public abstract IEntity? HeldEntity { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Hand : IReadOnlyHand
|
|
||||||
{
|
|
||||||
[ViewVariables]
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public bool Enabled { get; set; }
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public HandLocation Location { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The container used to hold the contents of this hand. Nullable because the client must get the containers via <see cref="ContainerManagerComponent"/>,
|
/// The container used to hold the contents of this hand. Nullable because the client must get the containers via <see cref="ContainerManagerComponent"/>,
|
||||||
/// which may not be synced with the server when the client hands are created.
|
/// which may not be synced with the server when the client hands are created.
|
||||||
@@ -840,37 +808,36 @@ namespace Content.Shared.Hands.Components
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public IEntity? HeldEntity => Container?.ContainedEntities?.FirstOrDefault();
|
public IEntity? HeldEntity => Container?.ContainedEntities?.FirstOrDefault();
|
||||||
|
|
||||||
public Hand(string name, bool enabled, HandLocation location, IContainer? container = null)
|
public bool IsEmpty => HeldEntity == null;
|
||||||
|
|
||||||
|
public Hand(string name, HandLocation location, IContainer? container = null)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
Enabled = enabled;
|
|
||||||
Location = location;
|
Location = location;
|
||||||
Container = container;
|
Container = container;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HandState ToHandState()
|
public HandState ToHandState()
|
||||||
{
|
{
|
||||||
return new(Name, Location, Enabled);
|
return new(Name, Location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class HandState
|
public struct HandState
|
||||||
{
|
{
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public HandLocation Location { get; }
|
public HandLocation Location { get; }
|
||||||
public bool Enabled { get; }
|
|
||||||
|
|
||||||
public HandState(string name, HandLocation location, bool enabled)
|
public HandState(string name, HandLocation location)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
Location = location;
|
Location = location;
|
||||||
Enabled = enabled;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public class HandsComponentState : ComponentState
|
public sealed class HandsComponentState : ComponentState
|
||||||
{
|
{
|
||||||
public HandState[] Hands { get; }
|
public HandState[] Hands { get; }
|
||||||
public string? ActiveHand { get; }
|
public string? ActiveHand { get; }
|
||||||
@@ -886,25 +853,20 @@ namespace Content.Shared.Hands.Components
|
|||||||
/// A message that calls the use interaction on an item in hand, presumed for now the interaction will occur only on the active hand.
|
/// A message that calls the use interaction on an item in hand, presumed for now the interaction will occur only on the active hand.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public class UseInHandMsg : ComponentMessage
|
public sealed class UseInHandMsg : EntityEventArgs
|
||||||
{
|
{
|
||||||
public UseInHandMsg()
|
|
||||||
{
|
|
||||||
Directed = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A message that calls the activate interaction on the item in the specified hand.
|
/// A message that calls the activate interaction on the item in the specified hand.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public class ActivateInHandMsg : ComponentMessage
|
public class ActivateInHandMsg : EntityEventArgs
|
||||||
{
|
{
|
||||||
public string HandName { get; }
|
public string HandName { get; }
|
||||||
|
|
||||||
public ActivateInHandMsg(string handName)
|
public ActivateInHandMsg(string handName)
|
||||||
{
|
{
|
||||||
Directed = true;
|
|
||||||
HandName = handName;
|
HandName = handName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -913,13 +875,12 @@ namespace Content.Shared.Hands.Components
|
|||||||
/// Uses the item in the active hand on the item in the specified hand.
|
/// Uses the item in the active hand on the item in the specified hand.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public class ClientAttackByInHandMsg : ComponentMessage
|
public class ClientInteractUsingInHandMsg : EntityEventArgs
|
||||||
{
|
{
|
||||||
public string HandName { get; }
|
public string HandName { get; }
|
||||||
|
|
||||||
public ClientAttackByInHandMsg(string handName)
|
public ClientInteractUsingInHandMsg(string handName)
|
||||||
{
|
{
|
||||||
Directed = true;
|
|
||||||
HandName = handName;
|
HandName = handName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -928,28 +889,12 @@ namespace Content.Shared.Hands.Components
|
|||||||
/// Moves an item from one hand to the active hand.
|
/// Moves an item from one hand to the active hand.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public class MoveItemFromHandMsg : ComponentMessage
|
public class MoveItemFromHandMsg : EntityEventArgs
|
||||||
{
|
{
|
||||||
public string HandName { get; }
|
public string HandName { get; }
|
||||||
|
|
||||||
public MoveItemFromHandMsg(string handName)
|
public MoveItemFromHandMsg(string handName)
|
||||||
{
|
{
|
||||||
Directed = true;
|
|
||||||
HandName = handName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the player's active hand to a specified hand.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public class ClientChangedHandMsg : ComponentMessage
|
|
||||||
{
|
|
||||||
public string HandName { get; }
|
|
||||||
|
|
||||||
public ClientChangedHandMsg(string handName)
|
|
||||||
{
|
|
||||||
Directed = true;
|
|
||||||
HandName = handName;
|
HandName = handName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -975,7 +920,7 @@ namespace Content.Shared.Hands.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public class PickupAnimationMessage : ComponentMessage
|
public class PickupAnimationMessage : EntityEventArgs
|
||||||
{
|
{
|
||||||
public EntityUid EntityUid { get; }
|
public EntityUid EntityUid { get; }
|
||||||
public EntityCoordinates InitialPosition { get; }
|
public EntityCoordinates InitialPosition { get; }
|
||||||
@@ -983,10 +928,15 @@ namespace Content.Shared.Hands.Components
|
|||||||
|
|
||||||
public PickupAnimationMessage(EntityUid entityUid, Vector2 pickupDirection, EntityCoordinates initialPosition)
|
public PickupAnimationMessage(EntityUid entityUid, Vector2 pickupDirection, EntityCoordinates initialPosition)
|
||||||
{
|
{
|
||||||
Directed = true;
|
|
||||||
EntityUid = entityUid;
|
EntityUid = entityUid;
|
||||||
PickupDirection = pickupDirection;
|
PickupDirection = pickupDirection;
|
||||||
InitialPosition = initialPosition;
|
InitialPosition = initialPosition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public struct HandsModifiedMessage
|
||||||
|
{
|
||||||
|
public SharedHandsComponent Hands;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@@ -16,11 +15,7 @@ namespace Content.Shared.Hands
|
|||||||
SubscribeLocalEvent<SharedHandsComponent, EntRemovedFromContainerMessage>(HandleContainerModified);
|
SubscribeLocalEvent<SharedHandsComponent, EntRemovedFromContainerMessage>(HandleContainerModified);
|
||||||
SubscribeLocalEvent<SharedHandsComponent, EntInsertedIntoContainerMessage>(HandleContainerModified);
|
SubscribeLocalEvent<SharedHandsComponent, EntInsertedIntoContainerMessage>(HandleContainerModified);
|
||||||
|
|
||||||
SubscribeLocalEvent<RequestSetHandEvent>(HandleSetHand);
|
SubscribeAllEvent<RequestSetHandEvent>(HandleSetHand);
|
||||||
SubscribeNetworkEvent<RequestSetHandEvent>(HandleSetHand);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<RequestDropHeldEntityEvent>(HandleDrop);
|
|
||||||
SubscribeNetworkEvent<RequestDropHeldEntityEvent>(HandleDrop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DropHandItems(IEntity entity, bool doMobChecks = true)
|
public void DropHandItems(IEntity entity, bool doMobChecks = true)
|
||||||
@@ -38,14 +33,16 @@ namespace Content.Shared.Hands
|
|||||||
|
|
||||||
eventBus.RaiseLocalEvent(uid, msg);
|
eventBus.RaiseLocalEvent(uid, msg);
|
||||||
|
|
||||||
if (msg.Cancelled) return;
|
if (msg.Cancelled)
|
||||||
|
return;
|
||||||
|
|
||||||
if (entity.TryGetContainerMan(out var containerManager))
|
if (entity.TryGetContainerMan(out var containerManager))
|
||||||
{
|
{
|
||||||
var parentMsg = new ContainedEntityDropHandItemsAttemptEvent(uid);
|
var parentMsg = new ContainedEntityDropHandItemsAttemptEvent(uid);
|
||||||
eventBus.RaiseLocalEvent(containerManager.Owner.Uid, parentMsg);
|
eventBus.RaiseLocalEvent(containerManager.Owner.Uid, parentMsg);
|
||||||
|
|
||||||
if (parentMsg.Cancelled) return;
|
if (parentMsg.Cancelled)
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DropAllItemsInHands(entity, doMobChecks);
|
DropAllItemsInHands(entity, doMobChecks);
|
||||||
@@ -55,9 +52,9 @@ namespace Content.Shared.Hands
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleSetHand(RequestSetHandEvent msg, EntitySessionEventArgs eventArgs)
|
private static void HandleSetHand(RequestSetHandEvent msg, EntitySessionEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
var entity = eventArgs.SenderSession?.AttachedEntity;
|
var entity = eventArgs.SenderSession.AttachedEntity;
|
||||||
|
|
||||||
if (entity == null || !entity.TryGetComponent(out SharedHandsComponent? hands))
|
if (entity == null || !entity.TryGetComponent(out SharedHandsComponent? hands))
|
||||||
return;
|
return;
|
||||||
@@ -65,17 +62,13 @@ namespace Content.Shared.Hands
|
|||||||
hands.ActiveHand = msg.HandName;
|
hands.ActiveHand = msg.HandName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleDrop(RequestDropHeldEntityEvent msg, EntitySessionEventArgs eventArgs)
|
protected virtual void HandleContainerModified(
|
||||||
|
EntityUid uid,
|
||||||
|
SharedHandsComponent component,
|
||||||
|
ContainerModifiedMessage args)
|
||||||
{
|
{
|
||||||
var entity = eventArgs.SenderSession?.AttachedEntity;
|
component.Dirty();
|
||||||
|
|
||||||
if (entity == null || !entity.TryGetComponent(out SharedHandsComponent? hands))
|
|
||||||
return;
|
|
||||||
|
|
||||||
hands.TryDropHand(msg.HandName, msg.DropTarget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void HandleContainerModified(EntityUid uid, SharedHandsComponent component, ContainerModifiedMessage args);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ContainedEntityDropHandItemsAttemptEvent : CancellableEntityEventArgs
|
public sealed class ContainedEntityDropHandItemsAttemptEvent : CancellableEntityEventArgs
|
||||||
@@ -103,21 +96,4 @@ namespace Content.Shared.Hands
|
|||||||
HandName = handName;
|
HandName = handName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public class RequestDropHeldEntityEvent : EntityEventArgs
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The hand to drop from.
|
|
||||||
/// </summary>
|
|
||||||
public string HandName { get; }
|
|
||||||
|
|
||||||
public EntityCoordinates DropTarget { get; }
|
|
||||||
|
|
||||||
public RequestDropHeldEntityEvent(string handName, EntityCoordinates dropTarget)
|
|
||||||
{
|
|
||||||
HandName = handName;
|
|
||||||
DropTarget = dropTarget;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
53
Content.Shared/Interaction/BeforeInteract.cs
Normal file
53
Content.Shared/Interaction/BeforeInteract.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
|
namespace Content.Shared.Interaction
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Raised directed on the used object when clicking on another object before an interaction is handled.
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
public class BeforeInteractEvent : HandledEntityEventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Entity that triggered the interaction.
|
||||||
|
/// </summary>
|
||||||
|
public IEntity User { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entity that the user used to interact.
|
||||||
|
/// </summary>
|
||||||
|
public IEntity Used { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entity that was interacted on. This can be null if the attack did not click on an entity.
|
||||||
|
/// </summary>
|
||||||
|
public IEntity? Target { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Location that the user clicked outside of their interaction range.
|
||||||
|
/// </summary>
|
||||||
|
public EntityCoordinates ClickLocation { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is the click location close enough to reach by the player? This does not check for obstructions, just that the target is within
|
||||||
|
/// reach radius around the user.
|
||||||
|
/// </summary>
|
||||||
|
public bool CanReach { get; }
|
||||||
|
|
||||||
|
public BeforeInteractEvent(
|
||||||
|
IEntity user,
|
||||||
|
IEntity used,
|
||||||
|
IEntity? target,
|
||||||
|
EntityCoordinates clickLocation,
|
||||||
|
bool canReach)
|
||||||
|
{
|
||||||
|
User = user;
|
||||||
|
Used = used;
|
||||||
|
Target = target;
|
||||||
|
ClickLocation = clickLocation;
|
||||||
|
CanReach = canReach;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ using JetBrains.Annotations;
|
|||||||
using Robust.Shared.Analyzers;
|
using Robust.Shared.Analyzers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
namespace Content.Shared.DragDrop
|
namespace Content.Shared.Interaction
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This interface gives components behavior when they're dropped by a mob.
|
/// This interface gives components behavior when they're dropped by a mob.
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using Content.Shared.Sound;
|
using Content.Shared.Sound;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Players;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
@@ -9,7 +13,8 @@ namespace Content.Shared.Light.Component
|
|||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public enum ExpendableLightVisuals
|
public enum ExpendableLightVisuals
|
||||||
{
|
{
|
||||||
State
|
State,
|
||||||
|
Behavior
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
@@ -21,8 +26,11 @@ namespace Content.Shared.Light.Component
|
|||||||
Dead
|
Dead
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[NetworkedComponent]
|
||||||
public abstract class SharedExpendableLightComponent: Robust.Shared.GameObjects.Component
|
public abstract class SharedExpendableLightComponent: Robust.Shared.GameObjects.Component
|
||||||
{
|
{
|
||||||
|
public static readonly AudioParams LoopedSoundParams = new(0, 1, "Master", 62.5f, 1, true, 0.3f);
|
||||||
|
|
||||||
public sealed override string Name => "ExpendableLight";
|
public sealed override string Name => "ExpendableLight";
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly)]
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
@@ -66,7 +74,7 @@ namespace Content.Shared.Light.Component
|
|||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
[DataField("loopedSound")]
|
[DataField("loopedSound")]
|
||||||
protected string LoopedSound { get; set; } = default!;
|
public string LoopedSound { get; set; } = string.Empty;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
[DataField("dieSound")]
|
[DataField("dieSound")]
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ namespace Content.Shared.Pulling.Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var eventBus = Owner.EntityManager.EventBus;
|
||||||
|
|
||||||
// New value. Abandon being pulled by any existing object.
|
// New value. Abandon being pulled by any existing object.
|
||||||
if (_puller != null)
|
if (_puller != null)
|
||||||
{
|
{
|
||||||
@@ -64,10 +66,9 @@ namespace Content.Shared.Pulling.Components
|
|||||||
{
|
{
|
||||||
var message = new PullStoppedMessage(oldPullerPhysics, _physics);
|
var message = new PullStoppedMessage(oldPullerPhysics, _physics);
|
||||||
|
|
||||||
oldPuller.SendMessage(null, message);
|
eventBus.RaiseLocalEvent(oldPuller.Uid, message, broadcast: false);
|
||||||
Owner.SendMessage(null, message);
|
eventBus.RaiseLocalEvent(Owner.Uid, message);
|
||||||
|
|
||||||
oldPuller.EntityManager.EventBus.RaiseEvent(EventSource.Local, message);
|
|
||||||
_physics.WakeBody();
|
_physics.WakeBody();
|
||||||
}
|
}
|
||||||
// else-branch warning is handled below
|
// else-branch warning is handled below
|
||||||
@@ -125,14 +126,14 @@ namespace Content.Shared.Pulling.Components
|
|||||||
|
|
||||||
var pullAttempt = new PullAttemptMessage(pullerPhysics, _physics);
|
var pullAttempt = new PullAttemptMessage(pullerPhysics, _physics);
|
||||||
|
|
||||||
value.SendMessage(null, pullAttempt);
|
eventBus.RaiseLocalEvent(value.Uid, pullAttempt, broadcast: false);
|
||||||
|
|
||||||
if (pullAttempt.Cancelled)
|
if (pullAttempt.Cancelled)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Owner.SendMessage(null, pullAttempt);
|
eventBus.RaiseLocalEvent(Owner.Uid, pullAttempt);
|
||||||
|
|
||||||
if (pullAttempt.Cancelled)
|
if (pullAttempt.Cancelled)
|
||||||
{
|
{
|
||||||
@@ -147,10 +148,8 @@ namespace Content.Shared.Pulling.Components
|
|||||||
|
|
||||||
var message = new PullStartedMessage(PullerPhysics, _physics);
|
var message = new PullStartedMessage(PullerPhysics, _physics);
|
||||||
|
|
||||||
_puller.SendMessage(null, message);
|
eventBus.RaiseLocalEvent(_puller.Uid, message, broadcast: false);
|
||||||
Owner.SendMessage(null, message);
|
eventBus.RaiseLocalEvent(Owner.Uid, message);
|
||||||
|
|
||||||
_puller.EntityManager.EventBus.RaiseEvent(EventSource.Local, message);
|
|
||||||
|
|
||||||
var union = PullerPhysics.GetWorldAABB().Union(_physics.GetWorldAABB());
|
var union = PullerPhysics.GetWorldAABB().Union(_physics.GetWorldAABB());
|
||||||
var length = Math.Max(union.Size.X, union.Size.Y) * 0.75f;
|
var length = Math.Max(union.Size.X, union.Size.Y) * 0.75f;
|
||||||
@@ -335,29 +334,6 @@ namespace Content.Shared.Pulling.Components
|
|||||||
Puller = entity;
|
Puller = entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
|
||||||
{
|
|
||||||
base.HandleMessage(message, component);
|
|
||||||
|
|
||||||
if (message is not PullMessage pullMessage ||
|
|
||||||
pullMessage.Pulled.Owner != Owner)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pulledStatus = Owner.GetComponentOrNull<SharedAlertsComponent>();
|
|
||||||
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case PullStartedMessage:
|
|
||||||
pulledStatus?.ShowAlert(AlertType.Pulled);
|
|
||||||
break;
|
|
||||||
case PullStoppedMessage:
|
|
||||||
pulledStatus?.ClearAlert(AlertType.Pulled);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnRemove()
|
protected override void OnRemove()
|
||||||
{
|
{
|
||||||
TryStopPull();
|
TryStopPull();
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
using Content.Shared.Alert;
|
using Content.Shared.Movement.Components;
|
||||||
using Content.Shared.Movement.Components;
|
|
||||||
using Content.Shared.Physics.Pull;
|
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Component = Robust.Shared.GameObjects.Component;
|
using Component = Robust.Shared.GameObjects.Component;
|
||||||
|
|
||||||
@@ -46,30 +44,5 @@ namespace Content.Shared.Pulling.Components
|
|||||||
|
|
||||||
base.OnRemove();
|
base.OnRemove();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
|
||||||
{
|
|
||||||
base.HandleMessage(message, component);
|
|
||||||
|
|
||||||
if (message is not PullMessage pullMessage ||
|
|
||||||
pullMessage.Puller.Owner != Owner)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SharedAlertsComponent? ownerStatus = Owner.GetComponentOrNull<SharedAlertsComponent>();
|
|
||||||
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case PullStartedMessage msg:
|
|
||||||
Pulling = msg.Pulled.Owner;
|
|
||||||
ownerStatus?.ShowAlert(AlertType.Pulling);
|
|
||||||
break;
|
|
||||||
case PullStoppedMessage _:
|
|
||||||
Pulling = null;
|
|
||||||
ownerStatus?.ClearAlert(AlertType.Pulling);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using Robust.Shared.Physics;
|
|||||||
|
|
||||||
namespace Content.Shared.Physics.Pull
|
namespace Content.Shared.Physics.Pull
|
||||||
{
|
{
|
||||||
public class PullMessage : ComponentMessage
|
public class PullMessage : EntityEventArgs
|
||||||
{
|
{
|
||||||
public readonly IPhysBody Puller;
|
public readonly IPhysBody Puller;
|
||||||
public readonly IPhysBody Pulled;
|
public readonly IPhysBody Pulled;
|
||||||
44
Content.Shared/Pulling/Systems/SharedPullerSystem.cs
Normal file
44
Content.Shared/Pulling/Systems/SharedPullerSystem.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using Content.Shared.Alert;
|
||||||
|
using Content.Shared.Physics.Pull;
|
||||||
|
using Content.Shared.Pulling.Components;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Shared.Pulling
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed class SharedPullerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<SharedPullerComponent, PullStartedMessage>(PullerHandlePullStarted);
|
||||||
|
SubscribeLocalEvent<SharedPullerComponent, PullStoppedMessage>(PullerHandlePullStopped);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PullerHandlePullStarted(
|
||||||
|
EntityUid uid,
|
||||||
|
SharedPullerComponent component,
|
||||||
|
PullStartedMessage args)
|
||||||
|
{
|
||||||
|
if (args.Puller.Owner.Uid != uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (component.Owner.TryGetComponent(out SharedAlertsComponent? alerts))
|
||||||
|
alerts.ShowAlert(AlertType.Pulling);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PullerHandlePullStopped(
|
||||||
|
EntityUid uid,
|
||||||
|
SharedPullerComponent component,
|
||||||
|
PullStoppedMessage args)
|
||||||
|
{
|
||||||
|
if (args.Puller.Owner.Uid != uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (component.Owner.TryGetComponent(out SharedAlertsComponent? alerts))
|
||||||
|
alerts.ClearAlert(AlertType.Pulling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Content.Shared.Alert;
|
||||||
using Content.Shared.GameTicking;
|
using Content.Shared.GameTicking;
|
||||||
using Content.Shared.Input;
|
using Content.Shared.Input;
|
||||||
using Content.Shared.Physics.Pull;
|
using Content.Shared.Physics.Pull;
|
||||||
@@ -56,12 +57,34 @@ namespace Content.Shared.Pulling
|
|||||||
SubscribeLocalEvent<MoveEvent>(PullerMoved);
|
SubscribeLocalEvent<MoveEvent>(PullerMoved);
|
||||||
SubscribeLocalEvent<EntInsertedIntoContainerMessage>(HandleContainerInsert);
|
SubscribeLocalEvent<EntInsertedIntoContainerMessage>(HandleContainerInsert);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<SharedPullableComponent, PullStartedMessage>(PullableHandlePullStarted);
|
||||||
|
SubscribeLocalEvent<SharedPullableComponent, PullStoppedMessage>(PullableHandlePullStopped);
|
||||||
|
|
||||||
CommandBinds.Builder
|
CommandBinds.Builder
|
||||||
.Bind(ContentKeyFunctions.MovePulledObject, new PointerInputCmdHandler(HandleMovePulledObject))
|
.Bind(ContentKeyFunctions.MovePulledObject, new PointerInputCmdHandler(HandleMovePulledObject))
|
||||||
.Bind(ContentKeyFunctions.ReleasePulledObject, InputCmdHandler.FromDelegate(HandleReleasePulledObject))
|
.Bind(ContentKeyFunctions.ReleasePulledObject, InputCmdHandler.FromDelegate(HandleReleasePulledObject))
|
||||||
.Register<SharedPullingSystem>();
|
.Register<SharedPullingSystem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Raise a "you are being pulled" alert if the pulled entity has alerts.
|
||||||
|
private static void PullableHandlePullStarted(EntityUid uid, SharedPullableComponent component, PullStartedMessage args)
|
||||||
|
{
|
||||||
|
if (args.Pulled.Owner.Uid != uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (component.Owner.TryGetComponent(out SharedAlertsComponent? alerts))
|
||||||
|
alerts.ShowAlert(AlertType.Pulled);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void PullableHandlePullStopped(EntityUid uid, SharedPullableComponent component, PullStoppedMessage args)
|
||||||
|
{
|
||||||
|
if (args.Pulled.Owner.Uid != uid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (component.Owner.TryGetComponent(out SharedAlertsComponent? alerts))
|
||||||
|
alerts.ClearAlert(AlertType.Pulled);
|
||||||
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
base.Update(frameTime);
|
base.Update(frameTime);
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.GameStates;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
|
|
||||||
namespace Content.Shared.Sound
|
|
||||||
{
|
|
||||||
[NetworkedComponent()]
|
|
||||||
public class SharedLoopingSoundComponent : Component
|
|
||||||
{
|
|
||||||
public override string Name => "LoopingSound";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stops all sounds.
|
|
||||||
/// </summary>
|
|
||||||
public virtual void StopAllSounds()
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stops a certain scheduled sound from playing.
|
|
||||||
/// </summary>
|
|
||||||
public virtual void StopScheduledSound(string filename)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds an scheduled sound to be played.
|
|
||||||
/// </summary>
|
|
||||||
public virtual void AddScheduledSound(ScheduledSound scheduledSound)
|
|
||||||
{}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Play an audio file following the entity.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filename">The resource path to the OGG Vorbis file to play.</param>
|
|
||||||
public void Play(string filename, AudioParams? audioParams = null)
|
|
||||||
{
|
|
||||||
AddScheduledSound(new ScheduledSound()
|
|
||||||
{
|
|
||||||
Filename = filename,
|
|
||||||
AudioParams = audioParams,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[NetSerializable, Serializable]
|
|
||||||
public class ScheduledSoundMessage : ComponentMessage
|
|
||||||
{
|
|
||||||
public ScheduledSound Schedule = new();
|
|
||||||
public ScheduledSoundMessage()
|
|
||||||
{
|
|
||||||
Directed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[NetSerializable, Serializable]
|
|
||||||
public class StopSoundScheduleMessage : ComponentMessage
|
|
||||||
{
|
|
||||||
public string Filename = string.Empty;
|
|
||||||
public StopSoundScheduleMessage()
|
|
||||||
{
|
|
||||||
Directed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[NetSerializable, Serializable]
|
|
||||||
public class StopAllSoundsMessage : ComponentMessage
|
|
||||||
{
|
|
||||||
public StopAllSoundsMessage()
|
|
||||||
{
|
|
||||||
Directed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
[DataDefinition]
|
|
||||||
public class ScheduledSound
|
|
||||||
{
|
|
||||||
[DataField("fileName")]
|
|
||||||
public string Filename = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The parameters to play the sound with.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("audioparams")]
|
|
||||||
public AudioParams? AudioParams;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Delay in milliseconds before playing the sound,
|
|
||||||
/// and delay between repetitions if Times is not 0.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("delay")]
|
|
||||||
public uint Delay;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maximum number of milliseconds to add to the delay randomly.
|
|
||||||
/// Useful for random ambience noises. Generated value differs from client to client.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("randomdelay")]
|
|
||||||
public uint RandomDelay;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// How many times to repeat the sound. If it's 0, it will play the sound once.
|
|
||||||
/// If it's less than 0, it will repeat the sound indefinitely.
|
|
||||||
/// If it's greater than 0, it will play the sound n+1 times.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("times")]
|
|
||||||
public int Times;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the sound will play or not.
|
|
||||||
/// </summary>
|
|
||||||
public bool Play = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -7,7 +7,7 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
|
|
||||||
namespace Content.Shared.Sound
|
namespace Content.Shared.Sound
|
||||||
{
|
{
|
||||||
[DataDefinition]
|
[ImplicitDataDefinitionForInheritors]
|
||||||
public abstract class SoundSpecifier
|
public abstract class SoundSpecifier
|
||||||
{
|
{
|
||||||
public abstract string GetSound();
|
public abstract string GetSound();
|
||||||
@@ -15,7 +15,6 @@ namespace Content.Shared.Sound
|
|||||||
public abstract bool TryGetSound([NotNullWhen(true)] out string? sound);
|
public abstract bool TryGetSound([NotNullWhen(true)] out string? sound);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataDefinition]
|
|
||||||
public sealed class SoundPathSpecifier : SoundSpecifier
|
public sealed class SoundPathSpecifier : SoundSpecifier
|
||||||
{
|
{
|
||||||
public const string Node = "path";
|
public const string Node = "path";
|
||||||
@@ -49,7 +48,6 @@ namespace Content.Shared.Sound
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataDefinition]
|
|
||||||
public sealed class SoundCollectionSpecifier : SoundSpecifier
|
public sealed class SoundCollectionSpecifier : SoundSpecifier
|
||||||
{
|
{
|
||||||
public const string Node = "collection";
|
public const string Node = "collection";
|
||||||
|
|||||||
@@ -20,15 +20,14 @@ namespace Pow3r
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
_paused = dat.Paused;
|
_paused = dat.Paused;
|
||||||
_nextId = dat.NextId;
|
|
||||||
_currentSolver = dat.Solver;
|
_currentSolver = dat.Solver;
|
||||||
|
|
||||||
_state = new PowerState
|
_state = new PowerState
|
||||||
{
|
{
|
||||||
Networks = dat.Networks.ToDictionary(n => n.Id, n => n),
|
Networks = GenIdStorage.FromEnumerable(dat.Networks.Select(n => (n.Id, n))),
|
||||||
Supplies = dat.Supplies.ToDictionary(s => s.Id, s => s),
|
Supplies = GenIdStorage.FromEnumerable(dat.Supplies.Select(s => (s.Id, s))),
|
||||||
Loads = dat.Loads.ToDictionary(l => l.Id, l => l),
|
Loads = GenIdStorage.FromEnumerable(dat.Loads.Select(l => (l.Id, l))),
|
||||||
Batteries = dat.Batteries.ToDictionary(b => b.Id, b => b)
|
Batteries = GenIdStorage.FromEnumerable(dat.Batteries.Select(b => (b.Id, b)))
|
||||||
};
|
};
|
||||||
|
|
||||||
_displayLoads = dat.Loads.ToDictionary(n => n.Id, _ => new DisplayLoad());
|
_displayLoads = dat.Loads.ToDictionary(n => n.Id, _ => new DisplayLoad());
|
||||||
@@ -44,7 +43,6 @@ namespace Pow3r
|
|||||||
var data = new DiskDat
|
var data = new DiskDat
|
||||||
{
|
{
|
||||||
Paused = _paused,
|
Paused = _paused,
|
||||||
NextId = _nextId,
|
|
||||||
Solver = _currentSolver,
|
Solver = _currentSolver,
|
||||||
|
|
||||||
Loads = _state.Loads.Values.ToList(),
|
Loads = _state.Loads.Values.ToList(),
|
||||||
@@ -59,7 +57,6 @@ namespace Pow3r
|
|||||||
private sealed class DiskDat
|
private sealed class DiskDat
|
||||||
{
|
{
|
||||||
public bool Paused;
|
public bool Paused;
|
||||||
public int NextId;
|
|
||||||
public int Solver;
|
public int Solver;
|
||||||
|
|
||||||
public List<Load> Loads;
|
public List<Load> Loads;
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ namespace Pow3r
|
|||||||
{
|
{
|
||||||
private const int MaxTickData = 180;
|
private const int MaxTickData = 180;
|
||||||
|
|
||||||
private int _nextId = 1;
|
|
||||||
private PowerState _state = new();
|
private PowerState _state = new();
|
||||||
private Network _linking;
|
private Network _linking;
|
||||||
private int _tickDataIdx;
|
private int _tickDataIdx;
|
||||||
@@ -18,13 +17,11 @@ namespace Pow3r
|
|||||||
|
|
||||||
private readonly string[] _solverNames =
|
private readonly string[] _solverNames =
|
||||||
{
|
{
|
||||||
nameof(GraphWalkSolver),
|
|
||||||
nameof(BatteryRampPegSolver),
|
nameof(BatteryRampPegSolver),
|
||||||
nameof(NoOpSolver)
|
nameof(NoOpSolver)
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly IPowerSolver[] _solvers = {
|
private readonly IPowerSolver[] _solvers = {
|
||||||
new GraphWalkSolver(),
|
|
||||||
new BatteryRampPegSolver(),
|
new BatteryRampPegSolver(),
|
||||||
new NoOpSolver()
|
new NoOpSolver()
|
||||||
};
|
};
|
||||||
@@ -35,11 +32,6 @@ namespace Pow3r
|
|||||||
private readonly Queue<object> _remQueue = new();
|
private readonly Queue<object> _remQueue = new();
|
||||||
private readonly Stopwatch _simStopwatch = new Stopwatch();
|
private readonly Stopwatch _simStopwatch = new Stopwatch();
|
||||||
|
|
||||||
private NodeId AllocId()
|
|
||||||
{
|
|
||||||
return new(_nextId++);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Tick(float frameTime)
|
private void Tick(float frameTime)
|
||||||
{
|
{
|
||||||
if (_paused)
|
if (_paused)
|
||||||
|
|||||||
@@ -36,30 +36,30 @@ namespace Pow3r
|
|||||||
|
|
||||||
if (Button("Generator"))
|
if (Button("Generator"))
|
||||||
{
|
{
|
||||||
var id = AllocId();
|
var supply = new Supply();
|
||||||
_state.Supplies.Add(id, new Supply { Id = id });
|
_state.Supplies.Allocate(out supply.Id) = supply;
|
||||||
_displaySupplies.Add(id, new DisplaySupply());
|
_displaySupplies.Add(supply.Id, new DisplaySupply());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Button("Load"))
|
if (Button("Load"))
|
||||||
{
|
{
|
||||||
var id = AllocId();
|
var load = new Load();
|
||||||
_state.Loads.Add(id, new Load { Id = id });
|
_state.Loads.Allocate(out load.Id) = load;
|
||||||
_displayLoads.Add(id, new DisplayLoad());
|
_displayLoads.Add(load.Id, new DisplayLoad());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Button("Network"))
|
if (Button("Network"))
|
||||||
{
|
{
|
||||||
var id = AllocId();
|
var network = new Network();
|
||||||
_state.Networks.Add(id, new Network { Id = id });
|
_state.Networks.Allocate(out network.Id) = network;
|
||||||
_displayNetworks.Add(id, new DisplayNetwork());
|
_displayNetworks.Add(network.Id, new DisplayNetwork());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Button("Battery"))
|
if (Button("Battery"))
|
||||||
{
|
{
|
||||||
var id = AllocId();
|
var battery = new Battery();
|
||||||
_state.Batteries.Add(id, new Battery { Id = id });
|
_state.Batteries.Allocate(out battery.Id) = battery;
|
||||||
_displayBatteries.Add(id, new DisplayBattery());
|
_displayBatteries.Add(battery.Id, new DisplayBattery());
|
||||||
}
|
}
|
||||||
|
|
||||||
Checkbox("Paused", ref _paused);
|
Checkbox("Paused", ref _paused);
|
||||||
@@ -355,25 +355,25 @@ namespace Pow3r
|
|||||||
switch (item)
|
switch (item)
|
||||||
{
|
{
|
||||||
case Network n:
|
case Network n:
|
||||||
_state.Networks.Remove(n.Id);
|
_state.Networks.Free(n.Id);
|
||||||
_displayNetworks.Remove(n.Id);
|
_displayNetworks.Remove(n.Id);
|
||||||
reLink = true;
|
reLink = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Supply s:
|
case Supply s:
|
||||||
_state.Supplies.Remove(s.Id);
|
_state.Supplies.Free(s.Id);
|
||||||
_state.Networks.Values.ForEach(n => n.Supplies.Remove(s.Id));
|
_state.Networks.Values.ForEach(n => n.Supplies.Remove(s.Id));
|
||||||
_displaySupplies.Remove(s.Id);
|
_displaySupplies.Remove(s.Id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Load l:
|
case Load l:
|
||||||
_state.Loads.Remove(l.Id);
|
_state.Loads.Free(l.Id);
|
||||||
_state.Networks.Values.ForEach(n => n.Loads.Remove(l.Id));
|
_state.Networks.Values.ForEach(n => n.Loads.Remove(l.Id));
|
||||||
_displayLoads.Remove(l.Id);
|
_displayLoads.Remove(l.Id);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Battery b:
|
case Battery b:
|
||||||
_state.Batteries.Remove(b.Id);
|
_state.Batteries.Free(b.Id);
|
||||||
_state.Networks.Values.ForEach(n => n.BatteriesCharging.Remove(b.Id));
|
_state.Networks.Values.ForEach(n => n.BatteriesCharging.Remove(b.Id));
|
||||||
_state.Networks.Values.ForEach(n => n.BatteriesDischarging.Remove(b.Id));
|
_state.Networks.Values.ForEach(n => n.BatteriesDischarging.Remove(b.Id));
|
||||||
_displayBatteries.Remove(b.Id);
|
_displayBatteries.Remove(b.Id);
|
||||||
|
|||||||
@@ -1613,3 +1613,10 @@ Entries:
|
|||||||
- {message: Added bio suits into bio lockers, type: Add}
|
- {message: Added bio suits into bio lockers, type: Add}
|
||||||
id: 287
|
id: 287
|
||||||
time: '2021-07-28T17:36:06.0000000+00:00'
|
time: '2021-07-28T17:36:06.0000000+00:00'
|
||||||
|
- author: PJB3005
|
||||||
|
changes:
|
||||||
|
- {message: Pulling is better integrated with hands now. It properly picks an empty
|
||||||
|
hand and dropping on the pulling hand stops the pull. You also see what you're
|
||||||
|
pulling in your hand!, type: Tweak}
|
||||||
|
id: 288
|
||||||
|
time: '2021-07-31T01:14:00.0000000+00:00'
|
||||||
|
|||||||
@@ -47,7 +47,6 @@
|
|||||||
- type: PointLight
|
- type: PointLight
|
||||||
enabled: false
|
enabled: false
|
||||||
radius: 3
|
radius: 3
|
||||||
- type: LoopingSound
|
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
visuals:
|
visuals:
|
||||||
- type: FlashLightVisualizer
|
- type: FlashLightVisualizer
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
- type: PointLight
|
- type: PointLight
|
||||||
enabled: false
|
enabled: false
|
||||||
radius: 3
|
radius: 3
|
||||||
- type: LoopingSound
|
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
visuals:
|
visuals:
|
||||||
- type: FlashLightVisualizer
|
- type: FlashLightVisualizer
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
sprite: Clothing/Shoes/Specific/clown.rsi
|
sprite: Clothing/Shoes/Specific/clown.rsi
|
||||||
- type: Clothing
|
- type: Clothing
|
||||||
sprite: Clothing/Shoes/Specific/clown.rsi
|
sprite: Clothing/Shoes/Specific/clown.rsi
|
||||||
- type: LoopingSound
|
|
||||||
- type: FootstepModifier
|
- type: FootstepModifier
|
||||||
footstepSoundCollection: footstep_clown
|
footstepSoundCollection: footstep_clown
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
- type: Puddle
|
- type: Puddle
|
||||||
spill_sound: /Audio/Effects/Fluids/splat.ogg
|
spill_sound: /Audio/Effects/Fluids/splat.ogg
|
||||||
recolor: true
|
recolor: true
|
||||||
- type: LoopingSound
|
|
||||||
- type: Clickable
|
- type: Clickable
|
||||||
- type: Slippery
|
- type: Slippery
|
||||||
- type: Physics
|
- type: Physics
|
||||||
|
|||||||
@@ -387,7 +387,6 @@
|
|||||||
# Eek! You can eat them alive for now until someone makes something that detects when
|
# Eek! You can eat them alive for now until someone makes something that detects when
|
||||||
# a mob is dead or something idk
|
# a mob is dead or something idk
|
||||||
- type: Food
|
- type: Food
|
||||||
- type: LoopingSound
|
|
||||||
- type: SolutionContainer
|
- type: SolutionContainer
|
||||||
contents:
|
contents:
|
||||||
reagents:
|
reagents:
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Consumable/Food/Baked/bread.rsi
|
sprite: Objects/Consumable/Food/Baked/bread.rsi
|
||||||
netsync: false
|
netsync: false
|
||||||
- type: LoopingSound
|
|
||||||
- type: SolutionContainer
|
- type: SolutionContainer
|
||||||
maxVol: 20
|
maxVol: 20
|
||||||
contents:
|
contents:
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Consumable/Food/Baked/cake.rsi
|
sprite: Objects/Consumable/Food/Baked/cake.rsi
|
||||||
netsync: false
|
netsync: false
|
||||||
- type: LoopingSound
|
|
||||||
- type: SolutionContainer
|
- type: SolutionContainer
|
||||||
maxVol: 20
|
maxVol: 20
|
||||||
contents:
|
contents:
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Consumable/Food/Baked/donut.rsi
|
sprite: Objects/Consumable/Food/Baked/donut.rsi
|
||||||
netsync: false
|
netsync: false
|
||||||
- type: LoopingSound
|
|
||||||
- type: SolutionContainer
|
- type: SolutionContainer
|
||||||
maxVol: 5
|
maxVol: 5
|
||||||
contents:
|
contents:
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Consumable/Food/Baked/misc.rsi
|
sprite: Objects/Consumable/Food/Baked/misc.rsi
|
||||||
netsync: false
|
netsync: false
|
||||||
- type: LoopingSound
|
|
||||||
- type: SolutionContainer
|
- type: SolutionContainer
|
||||||
maxVol: 5
|
maxVol: 5
|
||||||
contents:
|
contents:
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Consumable/Food/Baked/pizza.rsi
|
sprite: Objects/Consumable/Food/Baked/pizza.rsi
|
||||||
netsync: false
|
netsync: false
|
||||||
- type: LoopingSound
|
|
||||||
- type: SolutionContainer
|
- type: SolutionContainer
|
||||||
maxVol: 15
|
maxVol: 15
|
||||||
contents:
|
contents:
|
||||||
|
|||||||
@@ -92,7 +92,6 @@
|
|||||||
- box11
|
- box11
|
||||||
- box12
|
- box12
|
||||||
# Someday...
|
# Someday...
|
||||||
# - type: LoopingSound
|
|
||||||
# - type: DamageOnLand
|
# - type: DamageOnLand
|
||||||
# amount: 5
|
# amount: 5
|
||||||
# - type: DamageOtherOnHit
|
# - type: DamageOtherOnHit
|
||||||
|
|||||||
@@ -78,7 +78,6 @@
|
|||||||
reagents:
|
reagents:
|
||||||
- ReagentId: Nutriment
|
- ReagentId: Nutriment
|
||||||
Quantity: 15
|
Quantity: 15
|
||||||
- type: LoopingSound
|
|
||||||
- type: Food
|
- type: Food
|
||||||
trash: FoodTinPeachesTrash
|
trash: FoodTinPeachesTrash
|
||||||
|
|
||||||
@@ -127,7 +126,6 @@
|
|||||||
reagents:
|
reagents:
|
||||||
- ReagentId: Nutriment
|
- ReagentId: Nutriment
|
||||||
Quantity: 15
|
Quantity: 15
|
||||||
- type: LoopingSound
|
|
||||||
- type: Food
|
- type: Food
|
||||||
trash: FoodTinPeachesMaintTrash
|
trash: FoodTinPeachesMaintTrash
|
||||||
|
|
||||||
@@ -176,7 +174,6 @@
|
|||||||
reagents:
|
reagents:
|
||||||
- ReagentId: Nutriment
|
- ReagentId: Nutriment
|
||||||
Quantity: 15
|
Quantity: 15
|
||||||
- type: LoopingSound
|
|
||||||
- type: Food
|
- type: Food
|
||||||
trash: FoodTinBeansTrash
|
trash: FoodTinBeansTrash
|
||||||
|
|
||||||
@@ -227,7 +224,6 @@
|
|||||||
reagents:
|
reagents:
|
||||||
- ReagentId: Nutriment
|
- ReagentId: Nutriment
|
||||||
Quantity: 15
|
Quantity: 15
|
||||||
- type: LoopingSound
|
|
||||||
- type: Food
|
- type: Food
|
||||||
trash: FoodTinMRETrash
|
trash: FoodTinMRETrash
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Consumable/Food/burger.rsi
|
sprite: Objects/Consumable/Food/burger.rsi
|
||||||
netsync: false
|
netsync: false
|
||||||
- type: LoopingSound
|
|
||||||
- type: SolutionContainer
|
- type: SolutionContainer
|
||||||
maxVol: 15
|
maxVol: 15
|
||||||
contents:
|
contents:
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
reagents:
|
reagents:
|
||||||
- ReagentId: Egg
|
- ReagentId: Egg
|
||||||
Quantity: 5
|
Quantity: 5
|
||||||
- type: LoopingSound
|
|
||||||
- type: DamageOnLand
|
- type: DamageOnLand
|
||||||
amount: 1
|
amount: 1
|
||||||
- type: DamageOtherOnHit
|
- type: DamageOtherOnHit
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Consumable/Food/frozen.rsi
|
sprite: Objects/Consumable/Food/frozen.rsi
|
||||||
netsync: false
|
netsync: false
|
||||||
- type: LoopingSound
|
|
||||||
- type: SolutionContainer
|
- type: SolutionContainer
|
||||||
maxVol: 20 # For sprinkles or something? Idk.
|
maxVol: 20 # For sprinkles or something? Idk.
|
||||||
contents:
|
contents:
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Consumable/Food/meat.rsi
|
sprite: Objects/Consumable/Food/meat.rsi
|
||||||
netsync: false
|
netsync: false
|
||||||
- type: LoopingSound
|
|
||||||
- type: SolutionContainer
|
- type: SolutionContainer
|
||||||
maxVol: 20
|
maxVol: 20
|
||||||
contents:
|
contents:
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Consumable/Food/noodles.rsi
|
sprite: Objects/Consumable/Food/noodles.rsi
|
||||||
netsync: false
|
netsync: false
|
||||||
- type: LoopingSound
|
|
||||||
- type: SolutionContainer
|
- type: SolutionContainer
|
||||||
maxVol: 20
|
maxVol: 20
|
||||||
contents:
|
contents:
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Consumable/Food/skewer.rsi
|
sprite: Objects/Consumable/Food/skewer.rsi
|
||||||
netsync: false
|
netsync: false
|
||||||
- type: LoopingSound
|
|
||||||
- type: SolutionContainer
|
- type: SolutionContainer
|
||||||
maxVol: 20
|
maxVol: 20
|
||||||
contents:
|
contents:
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Objects/Consumable/Food/snacks.rsi
|
sprite: Objects/Consumable/Food/snacks.rsi
|
||||||
netsync: false
|
netsync: false
|
||||||
- type: LoopingSound
|
|
||||||
- type: SolutionContainer
|
- type: SolutionContainer
|
||||||
maxVol: 30 # Room for extra condiments
|
maxVol: 30 # Room for extra condiments
|
||||||
contents:
|
contents:
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: Food
|
- type: Food
|
||||||
trash: FoodBowlBig
|
trash: FoodBowlBig
|
||||||
- type: LoopingSound
|
|
||||||
- type: SolutionContainer
|
- type: SolutionContainer
|
||||||
maxVol: 20
|
maxVol: 20
|
||||||
contents:
|
contents:
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
id: DrinkBottleBaseEmpty
|
id: DrinkBottleBaseEmpty
|
||||||
description: That's an empty bottle.
|
description: That's an empty bottle.
|
||||||
components:
|
components:
|
||||||
- type: LoopingSound
|
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
state: icon
|
state: icon
|
||||||
- type: SolutionContainer
|
- type: SolutionContainer
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.PDAUiKey.Key
|
- key: enum.PDAUiKey.Key
|
||||||
type: PDABoundUserInterface
|
type: PDABoundUserInterface
|
||||||
- type: LoopingSound
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: BasePDA
|
parent: BasePDA
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
- type: Item
|
- type: Item
|
||||||
sprite: Objects/Misc/skub.rsi
|
sprite: Objects/Misc/skub.rsi
|
||||||
- type: ItemCooldown
|
- type: ItemCooldown
|
||||||
- type: LoopingSound
|
|
||||||
- type: EmitSoundOnUse
|
- type: EmitSoundOnUse
|
||||||
sound:
|
sound:
|
||||||
path: /Audio/Items/skub.ogg
|
path: /Audio/Items/skub.ogg
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
- type: EmitSoundOnActivate
|
- type: EmitSoundOnActivate
|
||||||
sound:
|
sound:
|
||||||
collection: ToySqueak
|
collection: ToySqueak
|
||||||
- type: LoopingSound
|
|
||||||
- type: ItemCooldown
|
- type: ItemCooldown
|
||||||
- type: UseDelay
|
- type: UseDelay
|
||||||
delay: 1.0
|
delay: 1.0
|
||||||
@@ -91,7 +90,6 @@
|
|||||||
sprite: Objects/Fun/toys.rsi
|
sprite: Objects/Fun/toys.rsi
|
||||||
state: plushie_snake
|
state: plushie_snake
|
||||||
- type: ItemCooldown
|
- type: ItemCooldown
|
||||||
- type: LoopingSound
|
|
||||||
- type: EmitSoundOnUse
|
- type: EmitSoundOnUse
|
||||||
sound:
|
sound:
|
||||||
path: /Audio/Items/Toys/rattle.ogg
|
path: /Audio/Items/Toys/rattle.ogg
|
||||||
@@ -108,7 +106,6 @@
|
|||||||
sprite: Objects/Fun/toys.rsi
|
sprite: Objects/Fun/toys.rsi
|
||||||
state: toy_mouse
|
state: toy_mouse
|
||||||
- type: ItemCooldown
|
- type: ItemCooldown
|
||||||
- type: LoopingSound
|
|
||||||
- type: EmitSoundOnUse
|
- type: EmitSoundOnUse
|
||||||
sound:
|
sound:
|
||||||
path: /Audio/Items/Toys/mousesqueek.ogg
|
path: /Audio/Items/Toys/mousesqueek.ogg
|
||||||
@@ -125,7 +122,6 @@
|
|||||||
sprite: Objects/Fun/toys.rsi
|
sprite: Objects/Fun/toys.rsi
|
||||||
state: plushie_vox
|
state: plushie_vox
|
||||||
- type: ItemCooldown
|
- type: ItemCooldown
|
||||||
- type: LoopingSound
|
|
||||||
- type: EmitSoundOnUse
|
- type: EmitSoundOnUse
|
||||||
sound:
|
sound:
|
||||||
path: /Audio/Voice/Vox/shriek1.ogg
|
path: /Audio/Voice/Vox/shriek1.ogg
|
||||||
@@ -151,7 +147,6 @@
|
|||||||
sound:
|
sound:
|
||||||
path: /Audio/Items/Toys/helpme.ogg
|
path: /Audio/Items/Toys/helpme.ogg
|
||||||
- type: ItemCooldown
|
- type: ItemCooldown
|
||||||
- type: LoopingSound
|
|
||||||
- type: EmitSoundOnUse
|
- type: EmitSoundOnUse
|
||||||
sound:
|
sound:
|
||||||
path: /Audio/Items/Toys/helpme.ogg
|
path: /Audio/Items/Toys/helpme.ogg
|
||||||
@@ -173,7 +168,6 @@
|
|||||||
sound:
|
sound:
|
||||||
path: /Audio/Items/Toys/hellothere.ogg
|
path: /Audio/Items/Toys/hellothere.ogg
|
||||||
- type: ItemCooldown
|
- type: ItemCooldown
|
||||||
- type: LoopingSound
|
|
||||||
- type: EmitSoundOnUse
|
- type: EmitSoundOnUse
|
||||||
sound:
|
sound:
|
||||||
path: /Audio/Items/Toys/hellothere.ogg
|
path: /Audio/Items/Toys/hellothere.ogg
|
||||||
@@ -195,7 +189,6 @@
|
|||||||
sound:
|
sound:
|
||||||
path: /Audio/Items/Toys/thankyou.ogg
|
path: /Audio/Items/Toys/thankyou.ogg
|
||||||
- type: ItemCooldown
|
- type: ItemCooldown
|
||||||
- type: LoopingSound
|
|
||||||
- type: EmitSoundOnUse
|
- type: EmitSoundOnUse
|
||||||
sound:
|
sound:
|
||||||
path: /Audio/Items/Toys/thankyou.ogg
|
path: /Audio/Items/Toys/thankyou.ogg
|
||||||
@@ -217,7 +210,6 @@
|
|||||||
sound:
|
sound:
|
||||||
path: /Audio/Items/Toys/verygood.ogg
|
path: /Audio/Items/Toys/verygood.ogg
|
||||||
- type: ItemCooldown
|
- type: ItemCooldown
|
||||||
- type: LoopingSound
|
|
||||||
- type: EmitSoundOnUse
|
- type: EmitSoundOnUse
|
||||||
sound:
|
sound:
|
||||||
path: /Audio/Items/Toys/verygood.ogg
|
path: /Audio/Items/Toys/verygood.ogg
|
||||||
@@ -239,7 +231,6 @@
|
|||||||
sound:
|
sound:
|
||||||
path: /Audio/Items/Toys/imsorry.ogg
|
path: /Audio/Items/Toys/imsorry.ogg
|
||||||
- type: ItemCooldown
|
- type: ItemCooldown
|
||||||
- type: LoopingSound
|
|
||||||
- type: EmitSoundOnUse
|
- type: EmitSoundOnUse
|
||||||
sound:
|
sound:
|
||||||
path: /Audio/Items/Toys/imsorry.ogg
|
path: /Audio/Items/Toys/imsorry.ogg
|
||||||
@@ -311,7 +302,6 @@
|
|||||||
sprite: Objects/Fun/toys.rsi
|
sprite: Objects/Fun/toys.rsi
|
||||||
state: ian
|
state: ian
|
||||||
- type: ItemCooldown
|
- type: ItemCooldown
|
||||||
- type: LoopingSound
|
|
||||||
- type: EmitSoundOnUse
|
- type: EmitSoundOnUse
|
||||||
sound:
|
sound:
|
||||||
path: /Audio/Items/Toys/ian.ogg
|
path: /Audio/Items/Toys/ian.ogg
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
- type: Item
|
- type: Item
|
||||||
sprite: Objects/Misc/torch.rsi
|
sprite: Objects/Misc/torch.rsi
|
||||||
HeldPrefix: unlit
|
HeldPrefix: unlit
|
||||||
- type: LoopingSound
|
|
||||||
- type: Construction
|
- type: Construction
|
||||||
graph: lightTorch
|
graph: lightTorch
|
||||||
node: torch
|
node: torch
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
sprite: Objects/Misc/flare.rsi
|
sprite: Objects/Misc/flare.rsi
|
||||||
color: "#FF0000"
|
color: "#FF0000"
|
||||||
HeldPrefix: unlit
|
HeldPrefix: unlit
|
||||||
- type: LoopingSound
|
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
visuals:
|
visuals:
|
||||||
- type: ExpendableLightVisualizer
|
- type: ExpendableLightVisualizer
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
- type: PointLight
|
- type: PointLight
|
||||||
enabled: false
|
enabled: false
|
||||||
radius: 3
|
radius: 3
|
||||||
- type: LoopingSound
|
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
visuals:
|
visuals:
|
||||||
- type: FlashLightVisualizer
|
- type: FlashLightVisualizer
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
radius: 3
|
radius: 3
|
||||||
energy: 2.5
|
energy: 2.5
|
||||||
color: "#FFC458"
|
color: "#FFC458"
|
||||||
- type: LoopingSound
|
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
visuals:
|
visuals:
|
||||||
- type: LanternVisualizer
|
- type: LanternVisualizer
|
||||||
@@ -39,4 +38,3 @@
|
|||||||
radius: 5
|
radius: 5
|
||||||
energy: 10
|
energy: 10
|
||||||
color: "#FFC458"
|
color: "#FFC458"
|
||||||
- type: LoopingSound
|
|
||||||
|
|||||||
@@ -17,4 +17,3 @@
|
|||||||
size: 24
|
size: 24
|
||||||
sprite: Objects/Weapons/Melee/pickaxe.rsi
|
sprite: Objects/Weapons/Melee/pickaxe.rsi
|
||||||
prefix: inhand
|
prefix: inhand
|
||||||
- type: LoopingSound
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.ReagentDispenserUiKey.Key
|
- key: enum.ReagentDispenserUiKey.Key
|
||||||
type: ReagentDispenserBoundUserInterface
|
type: ReagentDispenserBoundUserInterface
|
||||||
- type: LoopingSound
|
|
||||||
- type: Anchorable
|
- type: Anchorable
|
||||||
- type: Pullable
|
- type: Pullable
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
- type: Appearance
|
- type: Appearance
|
||||||
visuals:
|
visuals:
|
||||||
- type: MicrowaveVisualizer
|
- type: MicrowaveVisualizer
|
||||||
- type: LoopingSound
|
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.MicrowaveUiKey.Key
|
- key: enum.MicrowaveUiKey.Key
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
- type: Appearance
|
- type: Appearance
|
||||||
visuals:
|
visuals:
|
||||||
- type: ReagentGrinderVisualizer
|
- type: ReagentGrinderVisualizer
|
||||||
- type: LoopingSound
|
|
||||||
- type: Physics
|
- type: Physics
|
||||||
fixtures:
|
fixtures:
|
||||||
- shape:
|
- shape:
|
||||||
|
|||||||
@@ -57,4 +57,3 @@
|
|||||||
- type: StorageVisualizer
|
- type: StorageVisualizer
|
||||||
state_open: generic_open
|
state_open: generic_open
|
||||||
state_closed: generic_door
|
state_closed: generic_door
|
||||||
- type: LoopingSound
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user