mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-10 10:51:14 +03:00
Merge remote-tracking branch 'upstream/master' into upstream-sync
# Conflicts: # Resources/Prototypes/Entities/Markers/Spawners/Random/posters.yml # Resources/Prototypes/Maps/origin.yml # Resources/Textures/Interface/Misc/job_icons.rsi/meta.json # Resources/Textures/Tiles/dark.png # Resources/Textures/Tiles/white.png
This commit is contained in:
commit
228c19c52c
302 changed files with 431791 additions and 373490 deletions
1
.github/workflows/update-wiki.yml
vendored
1
.github/workflows/update-wiki.yml
vendored
|
|
@ -1,6 +1,7 @@
|
|||
name: Update Wiki
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ master, jsondump ]
|
||||
paths:
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public sealed class AccessOverlay : Overlay
|
|||
var xformQuery = _entityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var ent in _lookup.GetEntitiesIntersecting(args.MapId, args.WorldAABB,
|
||||
LookupFlags.Anchored | LookupFlags.Approximate))
|
||||
LookupFlags.Static | LookupFlags.Approximate))
|
||||
{
|
||||
if (!readerQuery.TryGetComponent(ent, out var reader) ||
|
||||
!xformQuery.TryGetComponent(ent, out var xform))
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ namespace Content.Client.Audio
|
|||
var ambientQuery = GetEntityQuery<AmbientSoundComponent>();
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
|
||||
foreach (var entity in _lookup.GetEntitiesInRange(coordinates, _maxAmbientRange + RangeBuffer, LookupFlags.Anchored | LookupFlags.Approximate))
|
||||
foreach (var entity in _lookup.GetEntitiesInRange(coordinates, _maxAmbientRange + RangeBuffer))
|
||||
{
|
||||
if (!ambientQuery.TryGetComponent(entity, out var ambientComp) ||
|
||||
!ambientComp.Enabled ||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Prototypes;
|
||||
using Content.Shared.Body.Systems;
|
||||
|
||||
namespace Content.Client.Body.Systems;
|
||||
|
||||
public sealed class BodySystem : SharedBodySystem
|
||||
{
|
||||
protected override void InitBody(BodyComponent body, BodyPrototype prototype)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ namespace Content.Client.Inventory
|
|||
|
||||
public override void Initialize()
|
||||
{
|
||||
UpdatesOutsidePrediction = true;
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ClientInventoryComponent, PlayerAttachedEvent>(OnPlayerAttached);
|
||||
|
|
@ -162,7 +163,7 @@ namespace Content.Client.Inventory
|
|||
public void ReloadInventory(ClientInventoryComponent? component = null)
|
||||
{
|
||||
var player = _playerManager.LocalPlayer?.ControlledEntity;
|
||||
if (player == null || !Resolve(player.Value, ref component))
|
||||
if (player == null || !Resolve(player.Value, ref component, false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using Content.Shared.Kitchen;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Kitchen.UI
|
||||
|
|
@ -68,15 +63,15 @@ namespace Content.Client.Kitchen.UI
|
|||
{
|
||||
switch (message)
|
||||
{
|
||||
case SharedReagentGrinderComponent.ReagentGrinderWorkStartedMessage workStarted:
|
||||
case ReagentGrinderWorkStartedMessage workStarted:
|
||||
GrindButton.Disabled = true;
|
||||
GrindButton.Modulate = workStarted.GrinderProgram == SharedReagentGrinderComponent.GrinderProgram.Grind ? Color.Green : Color.White;
|
||||
GrindButton.Modulate = workStarted.GrinderProgram == GrinderProgram.Grind ? Color.Green : Color.White;
|
||||
JuiceButton.Disabled = true;
|
||||
JuiceButton.Modulate = workStarted.GrinderProgram == SharedReagentGrinderComponent.GrinderProgram.Juice ? Color.Green : Color.White;
|
||||
JuiceButton.Modulate = workStarted.GrinderProgram == GrinderProgram.Juice ? Color.Green : Color.White;
|
||||
BeakerContentBox.EjectButton.Disabled = true;
|
||||
ChamberContentBox.EjectButton.Disabled = true;
|
||||
break;
|
||||
case SharedReagentGrinderComponent.ReagentGrinderWorkCompleteMessage:
|
||||
case ReagentGrinderWorkCompleteMessage:
|
||||
GrindButton.Disabled = false;
|
||||
JuiceButton.Disabled = false;
|
||||
GrindButton.Modulate = Color.White;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using Content.Shared.Kitchen;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.Shared.Chemistry.Components.Solution;
|
||||
|
||||
namespace Content.Client.Kitchen.UI
|
||||
{
|
||||
|
|
@ -55,10 +52,10 @@ namespace Content.Client.Kitchen.UI
|
|||
_menu?.HandleMessage(message);
|
||||
}
|
||||
|
||||
public void StartGrinding(BaseButton.ButtonEventArgs? args = null) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderGrindStartMessage());
|
||||
public void StartJuicing(BaseButton.ButtonEventArgs? args = null) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderJuiceStartMessage());
|
||||
public void EjectAll(BaseButton.ButtonEventArgs? args = null) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectChamberAllMessage());
|
||||
public void EjectBeaker(BaseButton.ButtonEventArgs? args = null) => SendMessage(new ItemSlotButtonPressedEvent(SharedReagentGrinderComponent.BeakerSlotId));
|
||||
public void EjectChamberContent(EntityUid uid) => SendMessage(new SharedReagentGrinderComponent.ReagentGrinderEjectChamberContentMessage(uid));
|
||||
public void StartGrinding(BaseButton.ButtonEventArgs? args = null) => SendMessage(new ReagentGrinderStartMessage(GrinderProgram.Grind));
|
||||
public void StartJuicing(BaseButton.ButtonEventArgs? args = null) => SendMessage(new ReagentGrinderStartMessage(GrinderProgram.Juice));
|
||||
public void EjectAll(BaseButton.ButtonEventArgs? args = null) => SendMessage(new ReagentGrinderEjectChamberAllMessage());
|
||||
public void EjectBeaker(BaseButton.ButtonEventArgs? args = null) => SendMessage(new ItemSlotButtonPressedEvent(SharedReagentGrinder.BeakerSlotId));
|
||||
public void EjectChamberContent(EntityUid uid) => SendMessage(new ReagentGrinderEjectChamberContentMessage(uid));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using static Content.Shared.Kitchen.Components.SharedReagentGrinderComponent;
|
||||
using Content.Shared.Kitchen;
|
||||
|
||||
namespace Content.Client.Kitchen.Visualizers
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,6 +4,9 @@
|
|||
x:Class="Content.Client.Options.UI.Tabs.NetworkTab">
|
||||
<BoxContainer Orientation="Vertical" >
|
||||
<BoxContainer Orientation="Vertical" Margin="8 8 8 8" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Horizontal" Margin="4 10 4 0">
|
||||
<CheckBox Name="NetPredictCheckbox" Text="{Loc 'ui-options-net-predict'}" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="4 10 4 0">
|
||||
<Label Text="{Loc 'ui-options-net-interp-ratio'}" />
|
||||
<Control MinSize="8 0" />
|
||||
|
|
@ -31,12 +34,26 @@
|
|||
<Label Name="NetPredictTickBiasLabel" MinSize="48 0" Align="Right" />
|
||||
<Control MinSize="4 0"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="4 10 4 0">
|
||||
<Label Text="{Loc 'ui-options-net-pvs-spawn'}" />
|
||||
<Control MinSize="8 0" />
|
||||
<Slider Name="NetPvsSpawnSlider"
|
||||
ToolTip="{Loc 'ui-options-net-pvs-spawn-tooltip'}"
|
||||
MaxValue="150"
|
||||
MinValue="20"
|
||||
HorizontalExpand="True"
|
||||
MinSize="80 0"
|
||||
Rounded="True" />
|
||||
<Control MinSize="8 0" />
|
||||
<Label Name="NetPvsSpawnLabel" MinSize="48 0" Align="Right" />
|
||||
<Control MinSize="4 0"/>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="4 10 4 0">
|
||||
<Label Text="{Loc 'ui-options-net-pvs-entry'}" />
|
||||
<Control MinSize="8 0" />
|
||||
<Slider Name="NetPvsEntrySlider"
|
||||
ToolTip="{Loc 'ui-options-net-pvs-entry-tooltip'}"
|
||||
MaxValue="150"
|
||||
MaxValue="500"
|
||||
MinValue="20"
|
||||
HorizontalExpand="True"
|
||||
MinSize="80 0"
|
||||
|
|
|
|||
|
|
@ -24,9 +24,11 @@ namespace Content.Client.Options.UI.Tabs
|
|||
ApplyButton.OnPressed += OnApplyButtonPressed;
|
||||
ResetButton.OnPressed += OnResetButtonPressed;
|
||||
DefaultButton.OnPressed += OnDefaultButtonPressed;
|
||||
NetPredictCheckbox.OnToggled += OnPredictToggled;
|
||||
NetInterpRatioSlider.OnValueChanged += OnSliderChanged;
|
||||
NetInterpRatioSlider.MinValue = _stateMan.MinBufferSize;
|
||||
NetPredictTickBiasSlider.OnValueChanged += OnSliderChanged;
|
||||
NetPvsSpawnSlider.OnValueChanged += OnSliderChanged;
|
||||
NetPvsEntrySlider.OnValueChanged += OnSliderChanged;
|
||||
NetPvsLeaveSlider.OnValueChanged += OnSliderChanged;
|
||||
|
||||
|
|
@ -38,13 +40,20 @@ namespace Content.Client.Options.UI.Tabs
|
|||
ApplyButton.OnPressed -= OnApplyButtonPressed;
|
||||
ResetButton.OnPressed -= OnResetButtonPressed;
|
||||
DefaultButton.OnPressed -= OnDefaultButtonPressed;
|
||||
NetPredictCheckbox.OnToggled -= OnPredictToggled;
|
||||
NetInterpRatioSlider.OnValueChanged -= OnSliderChanged;
|
||||
NetPredictTickBiasSlider.OnValueChanged -= OnSliderChanged;
|
||||
NetPvsSpawnSlider.OnValueChanged -= OnSliderChanged;
|
||||
NetPvsEntrySlider.OnValueChanged -= OnSliderChanged;
|
||||
NetPvsLeaveSlider.OnValueChanged -= OnSliderChanged;
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
private void OnPredictToggled(BaseButton.ButtonToggledEventArgs obj)
|
||||
{
|
||||
UpdateChanges();
|
||||
}
|
||||
|
||||
private void OnSliderChanged(Robust.Client.UserInterface.Controls.Range range)
|
||||
{
|
||||
UpdateChanges();
|
||||
|
|
@ -54,8 +63,10 @@ namespace Content.Client.Options.UI.Tabs
|
|||
{
|
||||
_cfg.SetCVar(CVars.NetBufferSize, (int) NetInterpRatioSlider.Value - _stateMan.MinBufferSize);
|
||||
_cfg.SetCVar(CVars.NetPredictTickBias, (int) NetPredictTickBiasSlider.Value);
|
||||
_cfg.SetCVar(CVars.NetPVSEntityBudget, (int) NetPvsEntrySlider.Value);
|
||||
_cfg.SetCVar(CVars.NetPVSEntityBudget, (int) NetPvsSpawnSlider.Value);
|
||||
_cfg.SetCVar(CVars.NetPVSEntityEnterBudget, (int) NetPvsEntrySlider.Value);
|
||||
_cfg.SetCVar(CVars.NetPVSEntityExitBudget, (int) NetPvsLeaveSlider.Value);
|
||||
_cfg.SetCVar(CVars.NetPredict, NetPredictCheckbox.Pressed);
|
||||
|
||||
_cfg.SaveToFile();
|
||||
UpdateChanges();
|
||||
|
|
@ -69,7 +80,8 @@ namespace Content.Client.Options.UI.Tabs
|
|||
private void OnDefaultButtonPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
NetPredictTickBiasSlider.Value = CVars.NetPredictTickBias.DefaultValue;
|
||||
NetPvsEntrySlider.Value = CVars.NetPVSEntityBudget.DefaultValue;
|
||||
NetPvsSpawnSlider.Value = CVars.NetPVSEntityBudget.DefaultValue;
|
||||
NetPvsEntrySlider.Value = CVars.NetPVSEntityEnterBudget.DefaultValue;
|
||||
NetPvsLeaveSlider.Value = CVars.NetPVSEntityExitBudget.DefaultValue;
|
||||
|
||||
// Apparently default value doesn't get updated when using override defaults, so using a const
|
||||
|
|
@ -82,8 +94,10 @@ namespace Content.Client.Options.UI.Tabs
|
|||
{
|
||||
NetInterpRatioSlider.Value = _cfg.GetCVar(CVars.NetBufferSize) + _stateMan.MinBufferSize;
|
||||
NetPredictTickBiasSlider.Value = _cfg.GetCVar(CVars.NetPredictTickBias);
|
||||
NetPvsEntrySlider.Value = _cfg.GetCVar(CVars.NetPVSEntityBudget);
|
||||
NetPvsSpawnSlider.Value = _cfg.GetCVar(CVars.NetPVSEntityBudget);
|
||||
NetPvsEntrySlider.Value = _cfg.GetCVar(CVars.NetPVSEntityEnterBudget);
|
||||
NetPvsLeaveSlider.Value = _cfg.GetCVar(CVars.NetPVSEntityExitBudget);
|
||||
NetPredictCheckbox.Pressed = _cfg.GetCVar(CVars.NetPredict);
|
||||
UpdateChanges();
|
||||
}
|
||||
|
||||
|
|
@ -92,15 +106,21 @@ namespace Content.Client.Options.UI.Tabs
|
|||
var isEverythingSame =
|
||||
NetInterpRatioSlider.Value == _cfg.GetCVar(CVars.NetBufferSize) + _stateMan.MinBufferSize &&
|
||||
NetPredictTickBiasSlider.Value == _cfg.GetCVar(CVars.NetPredictTickBias) &&
|
||||
NetPvsEntrySlider.Value == _cfg.GetCVar(CVars.NetPVSEntityBudget) &&
|
||||
NetPredictCheckbox.Pressed == _cfg.GetCVar(CVars.NetPredict) &&
|
||||
NetPvsSpawnSlider.Value == _cfg.GetCVar(CVars.NetPVSEntityBudget) &&
|
||||
NetPvsEntrySlider.Value == _cfg.GetCVar(CVars.NetPVSEntityEnterBudget) &&
|
||||
NetPvsLeaveSlider.Value == _cfg.GetCVar(CVars.NetPVSEntityExitBudget);
|
||||
|
||||
ApplyButton.Disabled = isEverythingSame;
|
||||
ResetButton.Disabled = isEverythingSame;
|
||||
NetInterpRatioLabel.Text = NetInterpRatioSlider.Value.ToString();
|
||||
NetPredictTickBiasLabel.Text = NetPredictTickBiasSlider.Value.ToString();
|
||||
NetPvsSpawnLabel.Text = NetPvsSpawnSlider.Value.ToString();
|
||||
NetPvsEntryLabel.Text = NetPvsEntrySlider.Value.ToString();
|
||||
NetPvsLeaveLabel.Text = NetPvsLeaveSlider.Value.ToString();
|
||||
|
||||
// TODO disable / grey-out the predict and interp sliders if prediction is disabled.
|
||||
// Currently no option to do this, but should be added to the slider control in general
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1099,6 +1099,7 @@ namespace Content.Client.Preferences.UI
|
|||
|
||||
private StripeBack _lockStripe;
|
||||
private Label _requirementsLabel;
|
||||
private Label _jobTitle;
|
||||
|
||||
public JobPrioritySelector(JobPrototype job)
|
||||
{
|
||||
|
|
@ -1156,13 +1157,25 @@ namespace Content.Client.Preferences.UI
|
|||
}
|
||||
};
|
||||
|
||||
_jobTitle = new Label()
|
||||
{
|
||||
Text = job.LocalizedName,
|
||||
MinSize = (175, 0)
|
||||
};
|
||||
|
||||
if (job.LocalizedDescription != null)
|
||||
{
|
||||
_jobTitle.ToolTip = job.LocalizedDescription;
|
||||
_jobTitle.TooltipDelay = 0.2f;
|
||||
}
|
||||
|
||||
AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children =
|
||||
{
|
||||
icon,
|
||||
new Label {Text = job.LocalizedName, MinSize = (175, 0)},
|
||||
_jobTitle,
|
||||
_optionButton,
|
||||
_lockStripe,
|
||||
}
|
||||
|
|
@ -1227,6 +1240,12 @@ namespace Content.Client.Preferences.UI
|
|||
_checkBox = new CheckBox {Text = $"{antag.Name}"};
|
||||
_checkBox.OnToggled += OnCheckBoxToggled;
|
||||
|
||||
if (antag.Description != null)
|
||||
{
|
||||
_checkBox.ToolTip = antag.Description;
|
||||
_checkBox.TooltipDelay = 0.2f;
|
||||
}
|
||||
|
||||
AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
|
|
@ -1263,6 +1282,12 @@ namespace Content.Client.Preferences.UI
|
|||
_checkBox = new CheckBox {Text = $"{trait.Name}"};
|
||||
_checkBox.OnToggled += OnCheckBoxToggled;
|
||||
|
||||
if (trait.Description != null)
|
||||
{
|
||||
_checkBox.ToolTip = trait.Description;
|
||||
_checkBox.TooltipDelay = 0.2f;
|
||||
}
|
||||
|
||||
AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared.Chat;
|
||||
using Content.Shared.Input;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Chat.Controls;
|
||||
|
|
@ -32,7 +33,7 @@ public class ChatInputBox : PanelContainer
|
|||
Input = new HistoryLineEdit
|
||||
{
|
||||
Name = "Input",
|
||||
PlaceHolder = Loc.GetString("hud-chatbox-info"),
|
||||
PlaceHolder = Loc.GetString("hud-chatbox-info", ("talk-key", BoundKeyHelper.ShortKeyName(ContentKeyFunctions.FocusChat)), ("cycle-key", BoundKeyHelper.ShortKeyName(ContentKeyFunctions.CycleChatChannelForward))),
|
||||
HorizontalExpand = true,
|
||||
StyleClasses = {"chatLineEdit"}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -263,21 +263,6 @@ namespace Content.Client.Verbs
|
|||
EntityManager.RaisePredictiveEvent(new ExecuteVerbEvent(target, verb));
|
||||
}
|
||||
|
||||
public override void ExecuteVerb(Verb verb, EntityUid user, EntityUid target, bool forced = false)
|
||||
{
|
||||
// invoke any relevant actions
|
||||
verb.Act?.Invoke();
|
||||
|
||||
// Maybe raise a local event
|
||||
if (verb.ExecutionEventArgs != null)
|
||||
{
|
||||
if (verb.EventTarget.IsValid())
|
||||
RaiseLocalEvent(verb.EventTarget, verb.ExecutionEventArgs, true);
|
||||
else
|
||||
RaiseLocalEvent(verb.ExecutionEventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleVerbResponse(VerbsResponseEvent msg)
|
||||
{
|
||||
if (!VerbMenu.RootMenu.Visible || VerbMenu.CurrentTarget != msg.Entity)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared.Examine;
|
||||
using Content.Shared.Weapons.Ranged;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
using Content.Shared.Weapons.Ranged.Events;
|
||||
using Robust.Shared.Containers;
|
||||
|
|
@ -12,16 +13,18 @@ public sealed partial class GunSystem
|
|||
base.InitializeChamberMagazine();
|
||||
SubscribeLocalEvent<ChamberMagazineAmmoProviderComponent, AmmoCounterControlEvent>(OnChamberMagazineCounter);
|
||||
SubscribeLocalEvent<ChamberMagazineAmmoProviderComponent, UpdateAmmoCounterEvent>(OnChamberMagazineAmmoUpdate);
|
||||
SubscribeLocalEvent<ChamberMagazineAmmoProviderComponent, EntRemovedFromContainerMessage>(OnChamberEntRemove);
|
||||
}
|
||||
|
||||
private void OnChamberEntRemove(EntityUid uid, ChamberMagazineAmmoProviderComponent component, EntRemovedFromContainerMessage args)
|
||||
protected override void OnMagazineSlotChange(EntityUid uid, MagazineAmmoProviderComponent component, ContainerModifiedMessage args)
|
||||
{
|
||||
if (args.Container.ID != ChamberSlot) return;
|
||||
base.OnMagazineSlotChange(uid, component, args);
|
||||
|
||||
if (ChamberSlot != args.Container.ID || args is not EntRemovedFromContainerMessage removedArgs)
|
||||
return;
|
||||
|
||||
// This is dirty af. Prediction moment.
|
||||
// We may be predicting spawning entities and the engine just removes them from the container so we'll just delete them.
|
||||
if (args.Entity.IsClientSide())
|
||||
if (removedArgs.Entity.IsClientSide())
|
||||
QueueDel(args.Entity);
|
||||
|
||||
// AFAIK the only main alternative is having some client-specific handling via a bool or otherwise for the state.
|
||||
|
|
|
|||
|
|
@ -34,28 +34,30 @@ namespace Content.IntegrationTests.Tests.Chemistry
|
|||
var prototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||
var testMap = await PoolManager.CreateTestMap(pairTracker);
|
||||
var coordinates = testMap.GridCoords;
|
||||
var solutionSystem = server.ResolveDependency<IEntitySystemManager>()
|
||||
.GetEntitySystem<SolutionContainerSystem>();
|
||||
|
||||
foreach (var reactionPrototype in prototypeManager.EnumeratePrototypes<ReactionPrototype>())
|
||||
{
|
||||
//since i have no clue how to isolate each loop assert-wise im just gonna throw this one in for good measure
|
||||
Console.WriteLine($"Testing {reactionPrototype.ID}");
|
||||
|
||||
EntityUid beaker;
|
||||
EntityUid beaker = default;
|
||||
Solution component = null;
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
beaker = entityManager.SpawnEntity("TestSolutionContainer", coordinates);
|
||||
Assert.That(EntitySystem.Get<SolutionContainerSystem>()
|
||||
Assert.That(solutionSystem
|
||||
.TryGetSolution(beaker, "beaker", out component));
|
||||
foreach (var (id, reactant) in reactionPrototype.Reactants)
|
||||
{
|
||||
Assert.That(EntitySystem.Get<SolutionContainerSystem>()
|
||||
Assert.That(solutionSystem
|
||||
.TryAddReagent(beaker, component, id, reactant.Amount, out var quantity));
|
||||
Assert.That(reactant.Amount, Is.EqualTo(quantity));
|
||||
}
|
||||
|
||||
EntitySystem.Get<SolutionContainerSystem>().SetTemperature(beaker, component, reactionPrototype.MinimumTemperature);
|
||||
solutionSystem.SetTemperature(beaker, component, reactionPrototype.MinimumTemperature);
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Server.Examine;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Utility;
|
||||
using Content.Shared.Inventory;
|
||||
|
||||
namespace Content.Server.Armor
|
||||
{
|
||||
|
|
@ -14,13 +15,13 @@ namespace Content.Server.Armor
|
|||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ArmorComponent, DamageModifyEvent>(OnDamageModify);
|
||||
SubscribeLocalEvent<ArmorComponent, InventoryRelayedEvent<DamageModifyEvent>>(OnDamageModify);
|
||||
SubscribeLocalEvent<ArmorComponent, GetVerbsEvent<ExamineVerb>>(OnArmorVerbExamine);
|
||||
}
|
||||
|
||||
private void OnDamageModify(EntityUid uid, ArmorComponent component, DamageModifyEvent args)
|
||||
private void OnDamageModify(EntityUid uid, ArmorComponent component, InventoryRelayedEvent<DamageModifyEvent> args)
|
||||
{
|
||||
args.Damage = DamageSpecifier.ApplyModifierSet(args.Damage, component.Modifiers);
|
||||
args.Args.Damage = DamageSpecifier.ApplyModifierSet(args.Args.Damage, component.Modifiers);
|
||||
}
|
||||
|
||||
private void OnArmorVerbExamine(EntityUid uid, ArmorComponent component, GetVerbsEvent<ExamineVerb> args)
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ public sealed class BeamSystem : SharedBeamSystem
|
|||
/// <param name="controller"></param>
|
||||
public void TryCreateBeam(EntityUid user, EntityUid target, string bodyPrototype, string? bodyState = null, string shader = "unshaded", EntityUid? controller = null)
|
||||
{
|
||||
if (!user.IsValid() || !target.IsValid())
|
||||
if (Deleted(user) || Deleted(target))
|
||||
return;
|
||||
|
||||
var userMapPos = Transform(user).MapPosition;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@ using Content.Server.Kitchen.Components;
|
|||
using Content.Server.Mind.Components;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Part;
|
||||
using Content.Shared.Body.Prototypes;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Coordinates;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.MobState.Components;
|
||||
using Content.Shared.Movement.Events;
|
||||
|
|
@ -113,6 +115,21 @@ public sealed class BodySystem : SharedBodySystem
|
|||
return true;
|
||||
}
|
||||
|
||||
protected override void InitBody(BodyComponent body, BodyPrototype prototype)
|
||||
{
|
||||
var root = prototype.Slots[prototype.Root];
|
||||
var bodyId = Spawn(root.Part, body.Owner.ToCoordinates());
|
||||
var partComponent = Comp<BodyPartComponent>(bodyId);
|
||||
var slot = new BodyPartSlot(root.Part, body.Owner, partComponent.PartType);
|
||||
body.Root = slot;
|
||||
partComponent.Body = bodyId;
|
||||
|
||||
Containers.EnsureContainer<Container>(body.Owner, BodyContainerId);
|
||||
|
||||
AttachPart(bodyId, slot, partComponent);
|
||||
InitPart(partComponent, prototype, prototype.Root);
|
||||
}
|
||||
|
||||
public override HashSet<EntityUid> GibBody(EntityUid? bodyId, bool gibOrgans = false, BodyComponent? body = null)
|
||||
{
|
||||
if (bodyId == null || !Resolve(bodyId.Value, ref body, false))
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ public sealed partial class ChatSystem : SharedChatSystem
|
|||
[Dependency] private readonly StationSystem _stationSystem = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||
|
||||
private const int VoiceRange = 7; // how far voice goes in world units
|
||||
private const int VoiceRange = 10; // how far voice goes in world units
|
||||
private const int WhisperRange = 2; // how far whisper goes in world units
|
||||
private const string DefaultAnnouncementSound = "/Audio/Announcements/announce.ogg";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
using System.Threading;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Server.UserInterface;
|
||||
using Robust.Server.GameObjects;
|
||||
using Content.Shared.Chemistry;
|
||||
|
||||
namespace Content.Server.Chemistry.Components
|
||||
{
|
||||
|
|
@ -22,6 +25,20 @@ namespace Content.Server.Chemistry.Components
|
|||
[DataField("injectOnly")]
|
||||
public bool InjectOnly;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum amount of solution that can be transferred at once from this solution.
|
||||
/// </summary>
|
||||
[DataField("minTransferAmount")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public FixedPoint2 MinimumTransferAmount { get; set; } = FixedPoint2.New(5);
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of solution that can be transferred at once from this solution.
|
||||
/// </summary>
|
||||
[DataField("maxTransferAmount")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public FixedPoint2 MaximumTransferAmount { get; set; } = FixedPoint2.New(50);
|
||||
|
||||
/// <summary>
|
||||
/// Amount to inject or draw on each usage. If the injector is inject only, it will
|
||||
/// attempt to inject it's entire contents upon use.
|
||||
|
|
|
|||
|
|
@ -145,9 +145,12 @@ namespace Content.Server.Chemistry.Components
|
|||
/// with the other area effects from the inception.</param>
|
||||
public void React(float averageExposures)
|
||||
{
|
||||
|
||||
if (!_entities.EntitySysManager.GetEntitySystem<SolutionContainerSystem>().TryGetSolution(Owner, SolutionName, out var solution))
|
||||
if (!_entities.EntitySysManager.GetEntitySystem<SolutionContainerSystem>()
|
||||
.TryGetSolution(Owner, SolutionName, out var solution) ||
|
||||
solution.Contents.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var xform = _entities.GetComponent<TransformComponent>(Owner);
|
||||
if (!MapManager.TryGetGrid(xform.GridUid, out var mapGrid))
|
||||
|
|
@ -158,6 +161,7 @@ namespace Content.Server.Chemistry.Components
|
|||
var lookup = _entities.EntitySysManager.GetEntitySystem<EntityLookupSystem>();
|
||||
|
||||
var solutionFraction = 1 / Math.Floor(averageExposures);
|
||||
var ents = lookup.GetEntitiesIntersecting(tile, LookupFlags.Uncontained).ToArray();
|
||||
|
||||
foreach (var reagentQuantity in solution.Contents.ToArray())
|
||||
{
|
||||
|
|
@ -173,14 +177,14 @@ namespace Content.Server.Chemistry.Components
|
|||
}
|
||||
|
||||
// Touch every entity on the tile
|
||||
foreach (var entity in lookup.GetEntitiesIntersecting(tile).ToArray())
|
||||
foreach (var entity in ents)
|
||||
{
|
||||
chemistry.ReactionEntity(entity, ReactionMethod.Touch, reagent,
|
||||
reagentQuantity.Quantity * solutionFraction, solution);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entity in lookup.GetEntitiesIntersecting(tile).ToArray())
|
||||
foreach (var entity in ents)
|
||||
{
|
||||
ReactWithEntity(entity, solutionFraction);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,13 +15,22 @@ using Content.Shared.MobState.Components;
|
|||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Player;
|
||||
using System.Threading;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Content.Shared.Popups;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems;
|
||||
|
||||
public sealed partial class ChemistrySystem
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Default transfer amounts for the set-transfer verb.
|
||||
/// </summary>
|
||||
public static readonly List<int> TransferAmounts = new() { 5, 10, 15};
|
||||
private void InitializeInjector()
|
||||
{
|
||||
SubscribeLocalEvent<InjectorComponent, GetVerbsEvent<AlternativeVerb>>(AddSetTransferVerbs);
|
||||
SubscribeLocalEvent<InjectorComponent, SolutionChangedEvent>(OnSolutionChange);
|
||||
SubscribeLocalEvent<InjectorComponent, HandDeselectedEvent>(OnInjectorDeselected);
|
||||
SubscribeLocalEvent<InjectorComponent, ComponentStartup>(OnInjectorStartup);
|
||||
|
|
@ -33,6 +42,38 @@ public sealed partial class ChemistrySystem
|
|||
SubscribeLocalEvent<InjectionCancelledEvent>(OnInjectionCancelled);
|
||||
}
|
||||
|
||||
private void AddSetTransferVerbs(EntityUid uid, InjectorComponent component, GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || args.Hands == null)
|
||||
return;
|
||||
|
||||
if (!EntityManager.TryGetComponent<ActorComponent?>(args.User, out var actor))
|
||||
return;
|
||||
|
||||
// Add specific transfer verbs according to the container's size
|
||||
var priority = 0;
|
||||
foreach (var amount in TransferAmounts)
|
||||
{
|
||||
if ( amount < component.MinimumTransferAmount.Int() || amount > component.MaximumTransferAmount.Int())
|
||||
continue;
|
||||
|
||||
AlternativeVerb verb = new();
|
||||
verb.Text = Loc.GetString("comp-solution-transfer-verb-amount", ("amount", amount));
|
||||
verb.Category = VerbCategory.SetTransferAmount;
|
||||
verb.Act = () =>
|
||||
{
|
||||
component.TransferAmount = FixedPoint2.New(amount);
|
||||
args.User.PopupMessage(Loc.GetString("comp-solution-transfer-set-amount", ("amount", amount)));
|
||||
};
|
||||
|
||||
// we want to sort by size, not alphabetically by the verb text.
|
||||
verb.Priority = priority;
|
||||
priority--;
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnInjectionCancelled(InjectionCancelledEvent ev)
|
||||
{
|
||||
ev.Component.CancelToken = null;
|
||||
|
|
@ -196,6 +237,10 @@ public sealed partial class ChemistrySystem
|
|||
return;
|
||||
|
||||
var actualDelay = MathF.Max(component.Delay, 1f);
|
||||
|
||||
// Injections take 1 second longer per additional 5u
|
||||
actualDelay += (float) component.TransferAmount / component.Delay - 1;
|
||||
|
||||
if (user != target)
|
||||
{
|
||||
// Create a pop-up for the target
|
||||
|
|
|
|||
|
|
@ -4,13 +4,6 @@ namespace Content.Server.Coordinates.Helpers
|
|||
{
|
||||
public static class SnapgridHelper
|
||||
{
|
||||
public static void SnapToGrid(this EntityUid entity, IEntityManager? entMan = null, IMapManager? mapManager = null)
|
||||
{
|
||||
IoCManager.Resolve(ref entMan, ref mapManager);
|
||||
var transform = entMan.GetComponent<TransformComponent>(entity);
|
||||
transform.Coordinates = transform.Coordinates.SnapToGrid(entMan, mapManager);
|
||||
}
|
||||
|
||||
public static EntityCoordinates SnapToGrid(this EntityCoordinates coordinates, IEntityManager? entMan = null, IMapManager? mapManager = null)
|
||||
{
|
||||
IoCManager.Resolve(ref entMan, ref mapManager);
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ namespace Content.Server.Cuffs
|
|||
Verb verb = new()
|
||||
{
|
||||
Act = () => component.TryUncuff(args.User),
|
||||
DoContactInteraction = true,
|
||||
Text = Loc.GetString("uncuff-verb-get-data-text")
|
||||
};
|
||||
//TODO VERB ICON add uncuffing symbol? may re-use the alert symbol showing that you are currently cuffed?
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ using Content.Shared.Disease.Components;
|
|||
using Content.Shared.Examine;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Inventory.Events;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.MobState.Components;
|
||||
using Content.Shared.Rejuvenate;
|
||||
using Robust.Shared.Player;
|
||||
|
|
@ -46,8 +46,7 @@ namespace Content.Server.Disease
|
|||
SubscribeLocalEvent<DiseaseCarrierComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<DiseaseCarrierComponent, CureDiseaseAttemptEvent>(OnTryCureDisease);
|
||||
SubscribeLocalEvent<DiseaseCarrierComponent, RejuvenateEvent>(OnRejuvenate);
|
||||
SubscribeLocalEvent<DiseasedComponent, UserInteractedWithItemEvent>(OnUserInteractDiseased);
|
||||
SubscribeLocalEvent<DiseasedComponent, ItemInteractedWithEvent>(OnTargetInteractDiseased);
|
||||
SubscribeLocalEvent<DiseasedComponent, ContactInteractionEvent>(OnContactInteraction);
|
||||
SubscribeLocalEvent<DiseasedComponent, EntitySpokeEvent>(OnEntitySpeak);
|
||||
SubscribeLocalEvent<DiseaseProtectionComponent, GotEquippedEvent>(OnEquipped);
|
||||
SubscribeLocalEvent<DiseaseProtectionComponent, GotUnequippedEvent>(OnUnequipped);
|
||||
|
|
@ -256,17 +255,9 @@ namespace Content.Server.Disease
|
|||
/// <summary>
|
||||
/// When a diseased person interacts with something, check infection.
|
||||
/// </summary>
|
||||
private void OnUserInteractDiseased(EntityUid uid, DiseasedComponent component, UserInteractedWithItemEvent args)
|
||||
private void OnContactInteraction(EntityUid uid, DiseasedComponent component, ContactInteractionEvent args)
|
||||
{
|
||||
InteractWithDiseased(args.User, args.Item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When a diseased person is interacted with, check infection.
|
||||
/// </summary>
|
||||
private void OnTargetInteractDiseased(EntityUid uid, DiseasedComponent component, ItemInteractedWithEvent args)
|
||||
{
|
||||
InteractWithDiseased(args.Item, args.User);
|
||||
InteractWithDiseased(uid, args.Other);
|
||||
}
|
||||
|
||||
private void OnEntitySpeak(EntityUid uid, DiseasedComponent component, EntitySpokeEvent args)
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
|
|||
Verb verb = new()
|
||||
{
|
||||
Act = () => TryInsert(component.Owner, args.User, args.User),
|
||||
DoContactInteraction = true,
|
||||
Text = Loc.GetString("disposal-self-insert-verb-get-data-text")
|
||||
};
|
||||
// TODO VERN ICON
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Content.Shared.Physics;
|
|||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
|
|
@ -188,7 +189,7 @@ public sealed partial class ExplosionSystem : EntitySystem
|
|||
/// Find entities on a grid tile using the EntityLookupComponent and apply explosion effects.
|
||||
/// </summary>
|
||||
/// <returns>True if the underlying tile can be uprooted, false if the tile is blocked by a dense entity</returns>
|
||||
internal bool ExplodeTile(EntityLookupComponent lookup,
|
||||
internal bool ExplodeTile(BroadphaseComponent lookup,
|
||||
IMapGrid grid,
|
||||
Vector2i tile,
|
||||
float throwForce,
|
||||
|
|
@ -209,7 +210,10 @@ public sealed partial class ExplosionSystem : EntitySystem
|
|||
var state = (list, processed, xformQuery);
|
||||
|
||||
// get entities:
|
||||
lookup.Tree.QueryAabb(ref state, GridQueryCallback, gridBox, true);
|
||||
lookup.DynamicTree.QueryAabb(ref state, GridQueryCallback, gridBox, true);
|
||||
lookup.StaticTree.QueryAabb(ref state, GridQueryCallback, gridBox, true);
|
||||
lookup.SundriesTree.QueryAabb(ref state, GridQueryCallback, gridBox, true);
|
||||
lookup.StaticSundriesTree.QueryAabb(ref state, GridQueryCallback, gridBox, true);
|
||||
|
||||
// process those entities
|
||||
foreach (var xform in list)
|
||||
|
|
@ -250,7 +254,8 @@ public sealed partial class ExplosionSystem : EntitySystem
|
|||
return !tileBlocked;
|
||||
|
||||
list.Clear();
|
||||
lookup.Tree.QueryAabb(ref state, GridQueryCallback, gridBox, true);
|
||||
lookup.DynamicTree.QueryAabb(ref state, GridQueryCallback, gridBox, true);
|
||||
lookup.SundriesTree.QueryAabb(ref state, GridQueryCallback, gridBox, true);
|
||||
|
||||
foreach (var xform in list)
|
||||
{
|
||||
|
|
@ -272,10 +277,18 @@ public sealed partial class ExplosionSystem : EntitySystem
|
|||
return true;
|
||||
}
|
||||
|
||||
private bool GridQueryCallback(
|
||||
ref (List<TransformComponent> List, HashSet<EntityUid> Processed, EntityQuery<TransformComponent> XformQuery) state,
|
||||
in FixtureProxy proxy)
|
||||
{
|
||||
var owner = proxy.Fixture.Body.Owner;
|
||||
return GridQueryCallback(ref state, in owner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Same as <see cref="ExplodeTile"/>, but for SPAAAAAAACE.
|
||||
/// </summary>
|
||||
internal void ExplodeSpace(EntityLookupComponent lookup,
|
||||
internal void ExplodeSpace(BroadphaseComponent lookup,
|
||||
Matrix3 spaceMatrix,
|
||||
Matrix3 invSpaceMatrix,
|
||||
Vector2i tile,
|
||||
|
|
@ -295,7 +308,10 @@ public sealed partial class ExplosionSystem : EntitySystem
|
|||
var state = (list, processed, invSpaceMatrix, lookup.Owner, xformQuery, gridBox);
|
||||
|
||||
// get entities:
|
||||
lookup.Tree.QueryAabb(ref state, SpaceQueryCallback, worldBox, true);
|
||||
lookup.DynamicTree.QueryAabb(ref state, SpaceQueryCallback, worldBox, true);
|
||||
lookup.StaticTree.QueryAabb(ref state, SpaceQueryCallback, worldBox, true);
|
||||
lookup.SundriesTree.QueryAabb(ref state, SpaceQueryCallback, worldBox, true);
|
||||
lookup.StaticSundriesTree.QueryAabb(ref state, SpaceQueryCallback, worldBox, true);
|
||||
|
||||
foreach (var xform in state.Item1)
|
||||
{
|
||||
|
|
@ -309,7 +325,8 @@ public sealed partial class ExplosionSystem : EntitySystem
|
|||
// Also, throw any entities that were spawned as shrapnel. Compared to entity spawning & destruction, this extra
|
||||
// lookup is relatively minor computational cost, and throwing is disabled for nukes anyways.
|
||||
list.Clear();
|
||||
lookup.Tree.QueryAabb(ref state, SpaceQueryCallback, worldBox, true);
|
||||
lookup.DynamicTree.QueryAabb(ref state, SpaceQueryCallback, worldBox, true);
|
||||
lookup.SundriesTree.QueryAabb(ref state, SpaceQueryCallback, worldBox, true);
|
||||
|
||||
foreach (var xform in list)
|
||||
{
|
||||
|
|
@ -342,6 +359,14 @@ public sealed partial class ExplosionSystem : EntitySystem
|
|||
return true;
|
||||
}
|
||||
|
||||
private bool SpaceQueryCallback(
|
||||
ref (List<TransformComponent> List, HashSet<EntityUid> Processed, Matrix3 InvSpaceMatrix, EntityUid LookupOwner, EntityQuery<TransformComponent> XformQuery, Box2 GridBox) state,
|
||||
in FixtureProxy proxy)
|
||||
{
|
||||
var owner = proxy.Fixture.Body.Owner;
|
||||
return SpaceQueryCallback(ref state, in owner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This function actually applies the explosion affects to an entity.
|
||||
/// </summary>
|
||||
|
|
@ -460,7 +485,7 @@ sealed class Explosion
|
|||
/// <summary>
|
||||
/// Lookup component for this grid (or space/map).
|
||||
/// </summary>
|
||||
public EntityLookupComponent Lookup;
|
||||
public BroadphaseComponent Lookup;
|
||||
|
||||
/// <summary>
|
||||
/// The actual grid that this corresponds to. If null, this implies space.
|
||||
|
|
@ -513,7 +538,7 @@ sealed class Explosion
|
|||
|
||||
// Variables used for enumerating over tiles, grids, etc
|
||||
private DamageSpecifier _currentDamage = default!;
|
||||
private EntityLookupComponent _currentLookup = default!;
|
||||
private BroadphaseComponent _currentLookup = default!;
|
||||
private IMapGrid? _currentGrid;
|
||||
private float _currentIntensity;
|
||||
private float _currentThrowForce;
|
||||
|
|
@ -600,7 +625,7 @@ sealed class Explosion
|
|||
_explosionData.Add(new()
|
||||
{
|
||||
TileLists = spaceData.TileLists,
|
||||
Lookup = entMan.GetComponent<EntityLookupComponent>(mapUid),
|
||||
Lookup = entMan.GetComponent<BroadphaseComponent>(mapUid),
|
||||
MapGrid = null
|
||||
});
|
||||
|
||||
|
|
@ -613,7 +638,7 @@ sealed class Explosion
|
|||
_explosionData.Add(new()
|
||||
{
|
||||
TileLists = grid.TileLists,
|
||||
Lookup = entMan.GetComponent<EntityLookupComponent>(grid.Grid.GridEntityId),
|
||||
Lookup = entMan.GetComponent<BroadphaseComponent>(grid.Grid.GridEntityId),
|
||||
MapGrid = grid.Grid
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using Content.Shared.Damage;
|
|||
using Content.Shared.Database;
|
||||
using Content.Shared.Explosion;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Throwing;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio;
|
||||
|
|
@ -63,6 +64,10 @@ public sealed partial class ExplosionSystem : EntitySystem
|
|||
SubscribeLocalEvent<GridRemovalEvent>(OnGridRemoved);
|
||||
SubscribeLocalEvent<GridStartupEvent>(OnGridStartup);
|
||||
SubscribeLocalEvent<ExplosionResistanceComponent, GetExplosionResistanceEvent>(OnGetResistance);
|
||||
|
||||
// as long as explosion-resistance mice are never added, this should be fine (otherwise a mouse-hat will transfer it's power to the wearer).
|
||||
SubscribeLocalEvent<ExplosionResistanceComponent, InventoryRelayedEvent<GetExplosionResistanceEvent>>((e, c, ev) => OnGetResistance(e, c, ev.Args));
|
||||
|
||||
SubscribeLocalEvent<TileChangedEvent>(OnTileChanged);
|
||||
|
||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnReset);
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ public sealed class SpillableSystem : EntitySystem
|
|||
};
|
||||
}
|
||||
verb.Impact = LogImpact.Medium; // dangerous reagent reaction are logged separately.
|
||||
verb.DoContactInteraction = true;
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Item;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Forensics
|
||||
|
|
@ -10,13 +10,13 @@ namespace Content.Server.Forensics
|
|||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<FingerprintComponent, UserInteractedWithItemEvent>(OnInteract);
|
||||
SubscribeLocalEvent<FingerprintComponent, ContactInteractionEvent>(OnInteract);
|
||||
SubscribeLocalEvent<FingerprintComponent, ComponentInit>(OnInit);
|
||||
}
|
||||
|
||||
private void OnInteract(EntityUid uid, FingerprintComponent component, UserInteractedWithItemEvent args)
|
||||
private void OnInteract(EntityUid uid, FingerprintComponent component, ContactInteractionEvent args)
|
||||
{
|
||||
ApplyEvidence(args.User, args.Item);
|
||||
ApplyEvidence(uid, args.Other);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, FingerprintComponent component, ComponentInit args)
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ namespace Content.Server.Ghost
|
|||
|
||||
SubscribeLocalEvent<GhostComponent, MindRemovedMessage>(OnMindRemovedMessage);
|
||||
SubscribeLocalEvent<GhostComponent, MindUnvisitedMessage>(OnMindUnvisitedMessage);
|
||||
SubscribeLocalEvent<GhostComponent, PlayerDetachedEvent>(OnPlayerDetached);
|
||||
|
||||
SubscribeLocalEvent<GhostOnMoveComponent, MoveInputEvent>(OnRelayMoveInput);
|
||||
|
||||
|
|
@ -58,6 +59,7 @@ namespace Content.Server.Ghost
|
|||
|
||||
SubscribeLocalEvent<RoundEndTextAppendEvent>(_ => MakeVisible(true));
|
||||
}
|
||||
|
||||
private void OnActionPerform(EntityUid uid, GhostComponent component, BooActionEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
|
|
@ -160,6 +162,11 @@ namespace Content.Server.Ghost
|
|||
DeleteEntity(uid);
|
||||
}
|
||||
|
||||
private void OnPlayerDetached(EntityUid uid, GhostComponent component, PlayerDetachedEvent args)
|
||||
{
|
||||
QueueDel(uid);
|
||||
}
|
||||
|
||||
private void OnGhostWarpsRequest(GhostWarpsRequestEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
if (args.SenderSession.AttachedEntity is not {Valid: true} entity ||
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ namespace Content.Server.Inventory
|
|||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<InventoryComponent, ModifyChangedTemperatureEvent>(RelayInventoryEvent);
|
||||
|
||||
SubscribeLocalEvent<ClothingComponent, UseInHandEvent>(OnUseInHand);
|
||||
|
||||
SubscribeNetworkEvent<OpenSlotStorageNetworkMessage>(OnOpenSlotStorage);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using Content.Shared.Kitchen;
|
||||
using Content.Server.Kitchen.EntitySystems;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Server.Kitchen.Components
|
||||
{
|
||||
|
|
@ -12,32 +10,36 @@ namespace Content.Server.Kitchen.Components
|
|||
/// converting something into its single juice form. E.g, grind an apple and get the nutriment and sugar
|
||||
/// it contained, juice an apple and get "apple juice".
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class ReagentGrinderComponent : SharedReagentGrinderComponent
|
||||
[Access(typeof(ReagentGrinderSystem)), RegisterComponent]
|
||||
public sealed class ReagentGrinderComponent : Component
|
||||
{
|
||||
//YAML serialization vars
|
||||
[DataField("storageMaxEntities"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public int StorageMaxEntities = 16;
|
||||
|
||||
[DataField("workTime"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan WorkTime = TimeSpan.FromSeconds(3.5); // Roughly matches the grind/juice sounds.
|
||||
|
||||
[DataField("clickSound"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public SoundSpecifier ClickSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");
|
||||
|
||||
[DataField("grindSound"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public SoundSpecifier GrindSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/blender.ogg");
|
||||
|
||||
[DataField("juiceSound"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public SoundSpecifier JuiceSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/juicer.ogg");
|
||||
}
|
||||
|
||||
[Access(typeof(ReagentGrinderSystem)), RegisterComponent]
|
||||
public sealed class ActiveReagentGrinderComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Can be null since we won't always have a beaker in the grinder.
|
||||
/// Remaining time until the grinder finishes grinding/juicing.
|
||||
/// </summary>
|
||||
[ViewVariables] public Solution? BeakerSolution;
|
||||
[ViewVariables]
|
||||
public float WorkTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Contains the things that are going to be ground or juiced.
|
||||
/// </summary>
|
||||
[ViewVariables] public Container Chamber = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Is the machine actively doing something and can't be used right now?
|
||||
/// </summary>
|
||||
public bool Busy;
|
||||
|
||||
//YAML serialization vars
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("chamberCapacity")] public int StorageCap = 16;
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("workTime")] public int WorkTime = 3500; //3.5 seconds, completely arbitrary for now.
|
||||
[DataField("clickSound")] public SoundSpecifier ClickSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");
|
||||
[DataField("grindSound")] public SoundSpecifier GrindSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/blender.ogg");
|
||||
[DataField("juiceSound")] public SoundSpecifier JuiceSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/juicer.ogg");
|
||||
|
||||
[DataField("beakerSlot")]
|
||||
public ItemSlot BeakerSlot = new();
|
||||
[ViewVariables]
|
||||
public GrinderProgram Program;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,15 @@
|
|||
using System.Linq;
|
||||
using Content.Server.Chemistry.EntitySystems;
|
||||
using Content.Server.Kitchen.Components;
|
||||
using Content.Server.Kitchen.Events;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Stack;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using Content.Shared.Kitchen;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Random.Helpers;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
|
|
@ -24,287 +25,272 @@ namespace Content.Server.Kitchen.EntitySystems
|
|||
{
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!;
|
||||
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
|
||||
|
||||
private Queue<ReagentGrinderComponent> _uiUpdateQueue = new();
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
|
||||
[Dependency] private readonly StackSystem _stackSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, ComponentRemove>(OnComponentRemove);
|
||||
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, PowerChangedEvent>(OnPowerChange);
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, ComponentStartup>((uid, component, _) => UpdateUiState(uid, component));
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, PowerChangedEvent>(
|
||||
(EntityUid uid, ReagentGrinderComponent component, ref PowerChangedEvent _) => UpdateUiState(uid, component));
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, InteractUsingEvent>(OnInteractUsing);
|
||||
SubscribeLocalEvent<StackComponent, ExtractableScalingEvent>(ExtractableScaling);
|
||||
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, EntInsertedIntoContainerMessage>(OnContainerModified);
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, EntRemovedFromContainerMessage>(OnContainerModified);
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, ContainerIsRemovingAttemptEvent>(OnEntRemoveAttempt);
|
||||
}
|
||||
|
||||
private void OnPowerChange(EntityUid uid, ReagentGrinderComponent component, ref PowerChangedEvent args)
|
||||
{
|
||||
EnqueueUiUpdate(component);
|
||||
}
|
||||
|
||||
private void OnEntRemoveAttempt(EntityUid uid, ReagentGrinderComponent component, ContainerIsRemovingAttemptEvent args)
|
||||
{
|
||||
if (component.Busy)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnContainerModified(EntityUid uid, ReagentGrinderComponent component, ContainerModifiedMessage args)
|
||||
{
|
||||
EnqueueUiUpdate(component);
|
||||
|
||||
if (args.Container.ID != SharedReagentGrinderComponent.BeakerSlotId)
|
||||
return;
|
||||
|
||||
if (TryComp(component.Owner, out AppearanceComponent? appearance))
|
||||
appearance.SetData(SharedReagentGrinderComponent.ReagentGrinderVisualState.BeakerAttached, component.BeakerSlot.HasItem);
|
||||
|
||||
component.BeakerSolution = null;
|
||||
if (component.BeakerSlot.Item != null)
|
||||
_solutionsSystem.TryGetFitsInDispenser(component.BeakerSlot.Item.Value, out component.BeakerSolution);
|
||||
}
|
||||
|
||||
private void ExtractableScaling(EntityUid uid, StackComponent component, ExtractableScalingEvent args)
|
||||
{
|
||||
args.Scalar *= component.Count; // multiply scalar by amount of items in stack
|
||||
}
|
||||
|
||||
private void OnInteractUsing(EntityUid uid, ReagentGrinderComponent component, InteractUsingEvent args)
|
||||
{
|
||||
if (args.Handled) return;
|
||||
|
||||
var heldEnt = args.Used;
|
||||
|
||||
//See if the user is trying to insert something they want to be ground/juiced.
|
||||
if (!HasComp<ExtractableComponent>(heldEnt))
|
||||
{
|
||||
//Entity did NOT pass the whitelist for grind/juice.
|
||||
//Wouldn't want the clown grinding up the Captain's ID card now would you?
|
||||
//Why am I asking you? You're biased.
|
||||
return;
|
||||
}
|
||||
|
||||
//Cap the chamber. Don't want someone putting in 500 entities and ejecting them all at once.
|
||||
//Maybe I should have done that for the microwave too?
|
||||
if (component.Chamber.ContainedEntities.Count >= component.StorageCap)
|
||||
return;
|
||||
|
||||
if (!component.Chamber.Insert(heldEnt, EntityManager))
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void EnqueueUiUpdate(ReagentGrinderComponent component)
|
||||
{
|
||||
if (!_uiUpdateQueue.Contains(component)) _uiUpdateQueue.Enqueue(component);
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, ReagentGrinderComponent component, ComponentInit args)
|
||||
{
|
||||
EnqueueUiUpdate(component);
|
||||
|
||||
_itemSlotsSystem.AddItemSlot(uid, SharedReagentGrinderComponent.BeakerSlotId, component.BeakerSlot);
|
||||
|
||||
//A container for the things that WILL be ground/juiced. Useful for ejecting them instead of deleting them from the hands of the user.
|
||||
component.Chamber =
|
||||
ContainerHelpers.EnsureContainer<Container>(component.Owner,
|
||||
$"{component.Name}-entityContainerContainer");
|
||||
|
||||
// TODO just directly subscribe to UI events.
|
||||
var bui = component.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key);
|
||||
if (bui != null)
|
||||
{
|
||||
bui.OnReceiveMessage += msg => OnUIMessageReceived(uid, component, msg);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnComponentRemove(EntityUid uid, ReagentGrinderComponent component, ComponentRemove args)
|
||||
{
|
||||
_itemSlotsSystem.RemoveItemSlot(uid, component.BeakerSlot);
|
||||
}
|
||||
|
||||
private void OnUIMessageReceived(EntityUid uid, ReagentGrinderComponent component,
|
||||
ServerBoundUserInterfaceMessage message)
|
||||
{
|
||||
if (component.Busy || message.Session.AttachedEntity is not {} attached)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (message.Message)
|
||||
{
|
||||
case SharedReagentGrinderComponent.ReagentGrinderGrindStartMessage msg:
|
||||
if (!this.IsPowered(component.Owner, EntityManager)) break;
|
||||
ClickSound(component);
|
||||
DoWork(component, attached,
|
||||
SharedReagentGrinderComponent.GrinderProgram.Grind);
|
||||
break;
|
||||
|
||||
case SharedReagentGrinderComponent.ReagentGrinderJuiceStartMessage msg:
|
||||
if (!this.IsPowered(component.Owner, EntityManager)) break;
|
||||
ClickSound(component);
|
||||
DoWork(component, attached,
|
||||
SharedReagentGrinderComponent.GrinderProgram.Juice);
|
||||
break;
|
||||
|
||||
case SharedReagentGrinderComponent.ReagentGrinderEjectChamberAllMessage msg:
|
||||
if (component.Chamber.ContainedEntities.Count > 0)
|
||||
{
|
||||
ClickSound(component);
|
||||
for (var i = component.Chamber.ContainedEntities.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var entity = component.Chamber.ContainedEntities[i];
|
||||
component.Chamber.Remove(entity);
|
||||
entity.RandomOffset(0.4f);
|
||||
}
|
||||
|
||||
EnqueueUiUpdate(component);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case SharedReagentGrinderComponent.ReagentGrinderEjectChamberContentMessage msg:
|
||||
if (component.Chamber.ContainedEntities.TryFirstOrNull(x => x == msg.EntityID, out var ent))
|
||||
{
|
||||
component.Chamber.Remove(ent.Value);
|
||||
SharedEntityExtensions.RandomOffset(ent.Value, 0.4f);
|
||||
EnqueueUiUpdate(component);
|
||||
ClickSound(component);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, ReagentGrinderStartMessage>(OnStartMessage);
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, ReagentGrinderEjectChamberAllMessage>(OnEjectChamberAllMessage);
|
||||
SubscribeLocalEvent<ReagentGrinderComponent, ReagentGrinderEjectChamberContentMessage>(OnEjectChamberContentMessage);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
while (_uiUpdateQueue.TryDequeue(out var comp))
|
||||
foreach (var (active, reagentGrinder) in EntityQuery<ActiveReagentGrinderComponent, ReagentGrinderComponent>())
|
||||
{
|
||||
if (comp.Deleted)
|
||||
var uid = reagentGrinder.Owner;
|
||||
active.WorkTimer -= frameTime;
|
||||
|
||||
if (active.WorkTimer > 0)
|
||||
continue;
|
||||
|
||||
bool canJuice = false;
|
||||
bool canGrind = false;
|
||||
if (comp.BeakerSlot.HasItem)
|
||||
{
|
||||
foreach (var entity in comp.Chamber.ContainedEntities)
|
||||
{
|
||||
if (canJuice || !EntityManager.TryGetComponent(entity, out ExtractableComponent? component)) continue;
|
||||
RemCompDeferred<ActiveReagentGrinderComponent>(uid);
|
||||
|
||||
canJuice = component.JuiceSolution != null;
|
||||
canGrind = component.GrindableSolution != null
|
||||
&& _solutionsSystem.TryGetSolution(entity, component.GrindableSolution, out _);
|
||||
var inputContainer = _containerSystem.EnsureContainer<Container>(uid, SharedReagentGrinder.InputContainerId);
|
||||
var outputContainer = _itemSlotsSystem.GetItemOrNull(uid, SharedReagentGrinder.BeakerSlotId);
|
||||
if (outputContainer is null || !_solutionsSystem.TryGetFitsInDispenser(outputContainer.Value, out var containerSolution))
|
||||
continue;
|
||||
|
||||
foreach (var item in inputContainer.ContainedEntities.ToList())
|
||||
{
|
||||
var solution = active.Program switch
|
||||
{
|
||||
GrinderProgram.Grind => GetGrindSolution(item),
|
||||
GrinderProgram.Juice => CompOrNull<ExtractableComponent>(item)?.JuiceSolution,
|
||||
_ => null,
|
||||
};
|
||||
|
||||
if (solution is null)
|
||||
continue;
|
||||
|
||||
if (TryComp<StackComponent>(item, out var stack))
|
||||
{
|
||||
var totalVolume = solution.TotalVolume * stack.Count;
|
||||
if (totalVolume <= 0)
|
||||
continue;
|
||||
|
||||
// Maximum number of items we can process in the stack without going over AvailableVolume
|
||||
// We add a small tolerance, because floats are inaccurate.
|
||||
var fitsCount = (int) (stack.Count * FixedPoint2.Min(containerSolution.AvailableVolume / totalVolume + 0.01, 1));
|
||||
if (fitsCount <= 0)
|
||||
continue;
|
||||
|
||||
solution.ScaleSolution(fitsCount);
|
||||
_stackSystem.SetCount(item, stack.Count - fitsCount); // Setting to 0 will QueueDel
|
||||
}
|
||||
else
|
||||
{
|
||||
if (solution.TotalVolume > containerSolution.AvailableVolume)
|
||||
continue;
|
||||
|
||||
QueueDel(item);
|
||||
}
|
||||
|
||||
_solutionsSystem.TryAddSolution(outputContainer.Value, containerSolution, solution);
|
||||
}
|
||||
|
||||
comp.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key)?.SetState(
|
||||
new ReagentGrinderInterfaceState
|
||||
(
|
||||
comp.Busy,
|
||||
comp.BeakerSlot.HasItem,
|
||||
this.IsPowered(comp.Owner, EntityManager),
|
||||
canJuice,
|
||||
canGrind,
|
||||
comp.Chamber.ContainedEntities.Select(item => item).ToArray(),
|
||||
//Remember the beaker can be null!
|
||||
comp.BeakerSolution?.Contents.ToArray()
|
||||
));
|
||||
_userInterfaceSystem.TrySendUiMessage(uid, ReagentGrinderUiKey.Key,
|
||||
new ReagentGrinderWorkCompleteMessage());
|
||||
|
||||
UpdateUiState(uid, reagentGrinder);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEntRemoveAttempt(EntityUid uid, ReagentGrinderComponent reagentGrinder, ContainerIsRemovingAttemptEvent args)
|
||||
{
|
||||
if (HasComp<ActiveReagentGrinderComponent>(uid))
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private void OnContainerModified(EntityUid uid, ReagentGrinderComponent reagentGrinder, ContainerModifiedMessage args)
|
||||
{
|
||||
UpdateUiState(uid, reagentGrinder);
|
||||
|
||||
var outputContainer = _itemSlotsSystem.GetItemOrNull(uid, SharedReagentGrinder.BeakerSlotId);
|
||||
_appearanceSystem.SetData(uid, ReagentGrinderVisualState.BeakerAttached, outputContainer.HasValue);
|
||||
}
|
||||
|
||||
private void OnInteractUsing(EntityUid uid, ReagentGrinderComponent reagentGrinder, InteractUsingEvent args)
|
||||
{
|
||||
var heldEnt = args.Used;
|
||||
var inputContainer = _containerSystem.EnsureContainer<Container>(uid, SharedReagentGrinder.InputContainerId);
|
||||
|
||||
if (!HasComp<ExtractableComponent>(heldEnt))
|
||||
{
|
||||
if (!HasComp<FitsInDispenserComponent>(heldEnt))
|
||||
{
|
||||
// This is ugly but we can't use whitelistFailPopup because there are 2 containers with different whitelists.
|
||||
_popupSystem.PopupEntity(Loc.GetString("reagent-grinder-component-cannot-put-entity-message"), uid, Filter.Entities(args.User));
|
||||
}
|
||||
|
||||
// Entity did NOT pass the whitelist for grind/juice.
|
||||
// Wouldn't want the clown grinding up the Captain's ID card now would you?
|
||||
// Why am I asking you? You're biased.
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
// Cap the chamber. Don't want someone putting in 500 entities and ejecting them all at once.
|
||||
// Maybe I should have done that for the microwave too?
|
||||
if (inputContainer.ContainedEntities.Count >= reagentGrinder.StorageMaxEntities)
|
||||
return;
|
||||
|
||||
if (!inputContainer.Insert(heldEnt, EntityManager))
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void UpdateUiState(EntityUid uid, ReagentGrinderComponent reagentGrinder)
|
||||
{
|
||||
var inputContainer = _containerSystem.EnsureContainer<Container>(uid, SharedReagentGrinder.InputContainerId);
|
||||
var outputContainer = _itemSlotsSystem.GetItemOrNull(uid, SharedReagentGrinder.BeakerSlotId);
|
||||
Solution? containerSolution = null;
|
||||
var isBusy = HasComp<ActiveReagentGrinderComponent>(uid);
|
||||
var canJuice = false;
|
||||
var canGrind = false;
|
||||
|
||||
if (outputContainer is not null
|
||||
&& _solutionsSystem.TryGetFitsInDispenser(outputContainer.Value, out containerSolution)
|
||||
&& inputContainer.ContainedEntities.Count > 0)
|
||||
{
|
||||
canGrind = inputContainer.ContainedEntities.All(CanGrind);
|
||||
canJuice = inputContainer.ContainedEntities.All(CanJuice);
|
||||
}
|
||||
|
||||
var state = new ReagentGrinderInterfaceState(
|
||||
isBusy,
|
||||
outputContainer.HasValue,
|
||||
this.IsPowered(uid, EntityManager),
|
||||
canJuice,
|
||||
canGrind,
|
||||
inputContainer.ContainedEntities.Select(item => item).ToArray(),
|
||||
containerSolution?.Contents.ToArray()
|
||||
);
|
||||
_userInterfaceSystem.TrySetUiState(uid, ReagentGrinderUiKey.Key, state);
|
||||
}
|
||||
|
||||
private void OnStartMessage(EntityUid uid, ReagentGrinderComponent reagentGrinder, ReagentGrinderStartMessage message)
|
||||
{
|
||||
if (!this.IsPowered(uid, EntityManager) || HasComp<ActiveReagentGrinderComponent>(uid))
|
||||
return;
|
||||
|
||||
DoWork(uid, reagentGrinder, message.Session.AttachedEntity, message.Program);
|
||||
}
|
||||
|
||||
private void OnEjectChamberAllMessage(EntityUid uid, ReagentGrinderComponent reagentGrinder, ReagentGrinderEjectChamberAllMessage message)
|
||||
{
|
||||
var inputContainer = _containerSystem.EnsureContainer<Container>(uid, SharedReagentGrinder.InputContainerId);
|
||||
|
||||
if (HasComp<ActiveReagentGrinderComponent>(uid) || inputContainer.ContainedEntities.Count <= 0)
|
||||
return;
|
||||
|
||||
ClickSound(uid, reagentGrinder);
|
||||
foreach (var entity in inputContainer.ContainedEntities.ToList())
|
||||
{
|
||||
inputContainer.Remove(entity);
|
||||
entity.RandomOffset(0.4f);
|
||||
}
|
||||
UpdateUiState(uid, reagentGrinder);
|
||||
}
|
||||
|
||||
private void OnEjectChamberContentMessage(EntityUid uid, ReagentGrinderComponent reagentGrinder, ReagentGrinderEjectChamberContentMessage message)
|
||||
{
|
||||
if (HasComp<ActiveReagentGrinderComponent>(uid))
|
||||
return;
|
||||
|
||||
var inputContainer = _containerSystem.EnsureContainer<Container>(uid, SharedReagentGrinder.InputContainerId);
|
||||
|
||||
if (inputContainer.Remove(message.EntityId))
|
||||
{
|
||||
message.EntityId.RandomOffset(0.4f);
|
||||
ClickSound(uid, reagentGrinder);
|
||||
UpdateUiState(uid, reagentGrinder);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The wzhzhzh of the grinder. Processes the contents of the grinder and puts the output in the beaker.
|
||||
/// </summary>
|
||||
/// <param name="isJuiceIntent">true for wanting to juice, false for wanting to grind.</param>
|
||||
private void DoWork(ReagentGrinderComponent component, EntityUid user,
|
||||
SharedReagentGrinderComponent.GrinderProgram program)
|
||||
/// <param name="program">Which program, such as grind or juice</param>
|
||||
private void DoWork(EntityUid uid, ReagentGrinderComponent reagentGrinder, EntityUid? user, GrinderProgram program)
|
||||
{
|
||||
//Have power, are we busy, chamber has anything to grind, a beaker for the grounds to go?
|
||||
if (!this.IsPowered(component.Owner, EntityManager) ||
|
||||
component.Busy || component.Chamber.ContainedEntities.Count <= 0 ||
|
||||
component.BeakerSlot.Item is not EntityUid beakerEntity ||
|
||||
component.BeakerSolution == null)
|
||||
{
|
||||
var inputContainer = _containerSystem.EnsureContainer<Container>(uid, SharedReagentGrinder.InputContainerId);
|
||||
var outputContainer = _itemSlotsSystem.GetItemOrNull(uid, SharedReagentGrinder.BeakerSlotId);
|
||||
|
||||
// Do we have anything to grind/juice and a container to put the reagents in?
|
||||
if (inputContainer.ContainedEntities.Count <= 0 || !HasComp<FitsInDispenserComponent>(outputContainer))
|
||||
return;
|
||||
}
|
||||
|
||||
component.Busy = true;
|
||||
|
||||
var bui = component.Owner.GetUIOrNull(SharedReagentGrinderComponent.ReagentGrinderUiKey.Key);
|
||||
bui?.SendMessage(new SharedReagentGrinderComponent.ReagentGrinderWorkStartedMessage(program));
|
||||
SoundSpecifier? sound = null;
|
||||
switch (program)
|
||||
{
|
||||
case SharedReagentGrinderComponent.GrinderProgram.Grind:
|
||||
SoundSystem.Play(component.GrindSound.GetSound(), Filter.Pvs(component.Owner), component.Owner, AudioParams.Default);
|
||||
// Get each item inside the chamber and get the reagents it contains.
|
||||
// Transfer those reagents to the beaker, given we have one in.
|
||||
component.Owner.SpawnTimer(component.WorkTime, () =>
|
||||
{
|
||||
foreach (var item in component.Chamber.ContainedEntities.ToList())
|
||||
{
|
||||
if (!EntityManager.TryGetComponent(item, out ExtractableComponent? extract)
|
||||
|| extract.GrindableSolution == null
|
||||
|| !_solutionsSystem.TryGetSolution(item, extract.GrindableSolution, out var solution)) continue;
|
||||
|
||||
var juiceEvent = new ExtractableScalingEvent(); // default of scalar is always 1.0
|
||||
RaiseLocalEvent(item, juiceEvent, false);
|
||||
if (component.BeakerSolution.CurrentVolume + solution.CurrentVolume * juiceEvent.Scalar >
|
||||
component.BeakerSolution.MaxVolume) continue;
|
||||
solution.ScaleSolution(juiceEvent.Scalar);
|
||||
_solutionsSystem.TryAddSolution(beakerEntity, component.BeakerSolution, solution);
|
||||
EntityManager.DeleteEntity(item);
|
||||
}
|
||||
|
||||
component.Busy = false;
|
||||
EnqueueUiUpdate(component);
|
||||
bui?.SendMessage(new SharedReagentGrinderComponent.ReagentGrinderWorkCompleteMessage());
|
||||
});
|
||||
case GrinderProgram.Grind when inputContainer.ContainedEntities.All(CanGrind):
|
||||
sound = reagentGrinder.GrindSound;
|
||||
break;
|
||||
|
||||
case SharedReagentGrinderComponent.GrinderProgram.Juice:
|
||||
SoundSystem.Play(component.JuiceSound.GetSound(), Filter.Pvs(component.Owner), component.Owner, AudioParams.Default);
|
||||
component.Owner.SpawnTimer(component.WorkTime, () =>
|
||||
{
|
||||
foreach (var item in component.Chamber.ContainedEntities.ToList())
|
||||
{
|
||||
if (!EntityManager.TryGetComponent<ExtractableComponent?>(item, out var juiceMe)
|
||||
|| juiceMe.JuiceSolution == null)
|
||||
{
|
||||
Logger.Warning("Couldn't find a juice solution on entityUid:{0}", item);
|
||||
continue;
|
||||
}
|
||||
var juiceEvent = new ExtractableScalingEvent(); // default of scalar is always 1.0
|
||||
if (EntityManager.HasComponent<StackComponent>(item))
|
||||
{
|
||||
RaiseLocalEvent(item, juiceEvent, true);
|
||||
}
|
||||
|
||||
if (component.BeakerSolution.CurrentVolume + juiceMe.JuiceSolution.TotalVolume * juiceEvent.Scalar > component.BeakerSolution.MaxVolume)
|
||||
continue;
|
||||
juiceMe.JuiceSolution.ScaleSolution(juiceEvent.Scalar);
|
||||
_solutionsSystem.TryAddSolution(beakerEntity, component.BeakerSolution, juiceMe.JuiceSolution);
|
||||
EntityManager.DeleteEntity(item);
|
||||
}
|
||||
|
||||
bui?.SendMessage(new SharedReagentGrinderComponent.ReagentGrinderWorkCompleteMessage());
|
||||
component.Busy = false;
|
||||
EnqueueUiUpdate(component);
|
||||
});
|
||||
case GrinderProgram.Juice when inputContainer.ContainedEntities.All(CanJuice):
|
||||
sound = reagentGrinder.JuiceSound;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
var active = AddComp<ActiveReagentGrinderComponent>(uid);
|
||||
active.WorkTimer = (float) reagentGrinder.WorkTime.TotalSeconds;
|
||||
active.Program = program;
|
||||
|
||||
_audioSystem.PlayPvs(sound, uid);
|
||||
_userInterfaceSystem.TrySendUiMessage(uid, ReagentGrinderUiKey.Key,
|
||||
new ReagentGrinderWorkStartedMessage(program));
|
||||
}
|
||||
|
||||
private void ClickSound(ReagentGrinderComponent component)
|
||||
private void ClickSound(EntityUid uid, ReagentGrinderComponent reagentGrinder)
|
||||
{
|
||||
SoundSystem.Play(component.ClickSound.GetSound(), Filter.Pvs(component.Owner), component.Owner, AudioParams.Default.WithVolume(-2f));
|
||||
_audioSystem.PlayPvs(reagentGrinder.ClickSound, uid, AudioParams.Default.WithVolume(-2f));
|
||||
}
|
||||
|
||||
private Solution? GetGrindSolution(EntityUid uid)
|
||||
{
|
||||
if (TryComp<ExtractableComponent>(uid, out var extractable)
|
||||
&& extractable.GrindableSolution is not null
|
||||
&& _solutionsSystem.TryGetSolution(uid, extractable.GrindableSolution, out var solution))
|
||||
{
|
||||
return solution;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool CanGrind(EntityUid uid)
|
||||
{
|
||||
var solutionName = CompOrNull<ExtractableComponent>(uid)?.GrindableSolution;
|
||||
|
||||
return solutionName is not null && _solutionsSystem.TryGetSolution(uid, solutionName, out _);
|
||||
}
|
||||
|
||||
private bool CanJuice(EntityUid uid)
|
||||
{
|
||||
return CompOrNull<ExtractableComponent>(uid)?.JuiceSolution is not null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
namespace Content.Server.Kitchen.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Used in scaling amount of solution to extract in juicing
|
||||
/// </summary>
|
||||
public sealed class ExtractableScalingEvent : EntityEventArgs
|
||||
{
|
||||
|
||||
public ExtractableScalingEvent()
|
||||
{
|
||||
Scalar = 1f;
|
||||
}
|
||||
|
||||
public float Scalar
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -57,6 +57,9 @@ public sealed class LagCompensationSystem : EntitySystem
|
|||
|
||||
private void OnLagMove(EntityUid uid, LagCompensationComponent component, ref MoveEvent args)
|
||||
{
|
||||
if (!args.NewPosition.EntityId.IsValid())
|
||||
return; // probably being sent to nullspace for deletion.
|
||||
|
||||
EnsureComp<ActiveLagCompensationComponent>(uid);
|
||||
component.Positions.Enqueue((_timing.CurTime, args.NewPosition, args.NewRotation));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -243,8 +243,10 @@ public sealed partial class PathfindingSystem
|
|||
return;
|
||||
}
|
||||
|
||||
var oldGridUid = ev.OldPosition.GetGridUid(EntityManager);
|
||||
var gridUid = ev.NewPosition.GetGridUid(EntityManager);
|
||||
var gridUid = ev.Component.GridUid;
|
||||
var oldGridUid = ev.OldPosition.EntityId == ev.NewPosition.EntityId
|
||||
? gridUid
|
||||
: ev.OldPosition.GetGridUid(EntityManager);
|
||||
|
||||
// Not on a grid at all so just ignore.
|
||||
if (oldGridUid == gridUid && oldGridUid == null)
|
||||
|
|
|
|||
|
|
@ -168,6 +168,9 @@ namespace Content.Server.Physics.Controllers
|
|||
|
||||
private void OnConveyorShutdown(EntityUid uid, ConveyorComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating)
|
||||
return;
|
||||
|
||||
RemComp<ActiveConveyorComponent>(uid);
|
||||
|
||||
if (!TryComp<PhysicsComponent>(uid, out var body))
|
||||
|
|
|
|||
|
|
@ -303,6 +303,7 @@ namespace Content.Server.PneumaticCannon
|
|||
Verb ejectItems = new();
|
||||
ejectItems.Act = () => TryEjectAllItems(component, args.User);
|
||||
ejectItems.Text = Loc.GetString("pneumatic-cannon-component-verb-eject-items-name");
|
||||
ejectItems.DoContactInteraction = true;
|
||||
args.Verbs.Add(ejectItems);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ namespace Content.Server.Rotatable
|
|||
Verb verb = new();
|
||||
verb.Act = () => TryFlip(component, args.User);
|
||||
verb.Text = Loc.GetString("flippable-verb-get-data-text");
|
||||
verb.DoContactInteraction = true;
|
||||
// TODO VERB ICONS Add Uno reverse card style icon?
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
|
@ -43,6 +44,7 @@ namespace Content.Server.Rotatable
|
|||
|
||||
Verb resetRotation = new ()
|
||||
{
|
||||
DoContactInteraction = true,
|
||||
Act = () => EntityManager.GetComponent<TransformComponent>(component.Owner).LocalRotation = Angle.Zero,
|
||||
Category = VerbCategory.Rotate,
|
||||
IconTexture = "/Textures/Interface/VerbIcons/refresh.svg.192dpi.png",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Content.Server.DoAfter;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Sticky.Components;
|
||||
using Content.Server.Sticky.Events;
|
||||
|
|
@ -53,6 +53,7 @@ public sealed class StickySystem : EntitySystem
|
|||
|
||||
args.Verbs.Add(new Verb
|
||||
{
|
||||
DoContactInteraction = true,
|
||||
Text = Loc.GetString("comp-sticky-unstick-verb-text"),
|
||||
IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png",
|
||||
Act = () => StartUnsticking(uid, args.User, component)
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ public sealed class EntityStorageSystem : EntitySystem
|
|||
|
||||
var targetCoordinates = new EntityCoordinates(uid, component.EnteringOffset);
|
||||
|
||||
var entities = _lookup.GetEntitiesInRange(targetCoordinates, component.EnteringRange, LookupFlags.Approximate);
|
||||
var entities = _lookup.GetEntitiesInRange(targetCoordinates, component.EnteringRange, LookupFlags.Approximate | LookupFlags.Dynamic | LookupFlags.Sundries);
|
||||
|
||||
var ev = new StorageBeforeCloseEvent(uid, entities);
|
||||
RaiseLocalEvent(uid, ev, true);
|
||||
|
|
|
|||
|
|
@ -298,7 +298,7 @@ namespace Content.Server.Storage.EntitySystems
|
|||
var validStorables = new List<EntityUid>();
|
||||
var itemQuery = GetEntityQuery<ItemComponent>();
|
||||
|
||||
foreach (var entity in _entityLookupSystem.GetEntitiesInRange(args.ClickLocation, storageComp.AreaInsertRadius, LookupFlags.None))
|
||||
foreach (var entity in _entityLookupSystem.GetEntitiesInRange(args.ClickLocation, storageComp.AreaInsertRadius, LookupFlags.Dynamic | LookupFlags.Sundries))
|
||||
{
|
||||
if (entity == args.User
|
||||
|| !itemQuery.HasComponent(entity)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ using Content.Shared.Alert;
|
|||
using Content.Shared.Damage;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Temperature;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
|
@ -39,7 +40,7 @@ namespace Content.Server.Temperature.Systems
|
|||
SubscribeLocalEvent<TemperatureComponent, OnTemperatureChangeEvent>(EnqueueDamage);
|
||||
SubscribeLocalEvent<TemperatureComponent, AtmosExposedUpdateEvent>(OnAtmosExposedUpdate);
|
||||
SubscribeLocalEvent<AlertsComponent, OnTemperatureChangeEvent>(ServerAlert);
|
||||
SubscribeLocalEvent<TemperatureProtectionComponent, ModifyChangedTemperatureEvent>(OnTemperatureChangeAttempt);
|
||||
SubscribeLocalEvent<TemperatureProtectionComponent, InventoryRelayedEvent<ModifyChangedTemperatureEvent>>(OnTemperatureChangeAttempt);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
|
|
@ -204,9 +205,9 @@ namespace Content.Server.Temperature.Systems
|
|||
}
|
||||
}
|
||||
|
||||
private void OnTemperatureChangeAttempt(EntityUid uid, TemperatureProtectionComponent component, ModifyChangedTemperatureEvent args)
|
||||
private void OnTemperatureChangeAttempt(EntityUid uid, TemperatureProtectionComponent component, InventoryRelayedEvent<ModifyChangedTemperatureEvent> args)
|
||||
{
|
||||
args.TemperatureDelta *= component.Coefficient;
|
||||
args.Args.TemperatureDelta *= component.Coefficient;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -223,16 +224,4 @@ namespace Content.Server.Temperature.Systems
|
|||
TemperatureDelta = delta;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ModifyChangedTemperatureEvent : EntityEventArgs, IInventoryRelayEvent
|
||||
{
|
||||
public SlotFlags TargetSlots { get; } = ~SlotFlags.POCKET;
|
||||
|
||||
public float TemperatureDelta;
|
||||
|
||||
public ModifyChangedTemperatureEvent(float temperature)
|
||||
{
|
||||
TemperatureDelta = temperature;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,17 +84,7 @@ namespace Content.Server.Verbs
|
|||
// first, lets log the verb. Just in case it ends up crashing the server or something.
|
||||
LogVerb(verb, user, target, forced);
|
||||
|
||||
// then invoke any relevant actions
|
||||
verb.Act?.Invoke();
|
||||
|
||||
// Maybe raise a local event
|
||||
if (verb.ExecutionEventArgs != null)
|
||||
{
|
||||
if (verb.EventTarget.IsValid())
|
||||
RaiseLocalEvent(verb.EventTarget, verb.ExecutionEventArgs, true);
|
||||
else
|
||||
RaiseLocalEvent(verb.ExecutionEventArgs);
|
||||
}
|
||||
base.ExecuteVerb(verb, user, target, forced);
|
||||
}
|
||||
|
||||
public void LogVerb(Verb verb, EntityUid user, EntityUid target, bool forced)
|
||||
|
|
|
|||
|
|
@ -150,6 +150,13 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
|||
ev.Target.Value
|
||||
};
|
||||
|
||||
_interaction.DoContactInteraction(ev.Weapon, ev.Target);
|
||||
_interaction.DoContactInteraction(user, ev.Weapon);
|
||||
|
||||
// If the user is using a long-range weapon, this probably shouldn't be happening? But I'll interpret melee as a
|
||||
// somewhat messy scuffle. See also, heavy attacks.
|
||||
_interaction.DoContactInteraction(user, ev.Target);
|
||||
|
||||
// For stuff that cares about it being attacked.
|
||||
RaiseLocalEvent(ev.Target.Value, new AttackedEvent(component.Owner, user, targetXform.Coordinates));
|
||||
|
||||
|
|
@ -249,9 +256,17 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
|||
var modifiers = itemDamage.ModifiersList;
|
||||
modifiers.AddRange(hitEvent.ModifiersList);
|
||||
|
||||
_interaction.DoContactInteraction(user, ev.Weapon);
|
||||
|
||||
// For stuff that cares about it being attacked.
|
||||
foreach (var target in targets)
|
||||
{
|
||||
_interaction.DoContactInteraction(ev.Weapon, target);
|
||||
|
||||
// If the user is using a long-range weapon, this probably shouldn't be happening? But I'll interpret melee as a
|
||||
// somewhat messy scuffle. See also, light attacks.
|
||||
_interaction.DoContactInteraction(user, target);
|
||||
|
||||
RaiseLocalEvent(target, new AttackedEvent(component.Owner, user, Transform(target).Coordinates));
|
||||
}
|
||||
|
||||
|
|
@ -338,6 +353,8 @@ public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
|||
inTargetHand = targetHandsComponent.ActiveHand.HeldEntity!.Value;
|
||||
}
|
||||
|
||||
_interaction.DoContactInteraction(user, ev.Target);
|
||||
|
||||
var attemptEvent = new DisarmAttemptEvent(target, user, inTargetHand);
|
||||
|
||||
if (inTargetHand != null)
|
||||
|
|
|
|||
|
|
@ -78,9 +78,6 @@ namespace Content.Shared.ActionBlocker
|
|||
var targetEv = new GettingInteractedWithAttemptEvent(user, target);
|
||||
RaiseLocalEvent(target.Value, targetEv);
|
||||
|
||||
if (!targetEv.Cancelled)
|
||||
InteractWithItem(user, target.Value);
|
||||
|
||||
return !targetEv.Cancelled;
|
||||
}
|
||||
|
||||
|
|
@ -136,11 +133,7 @@ namespace Content.Shared.ActionBlocker
|
|||
var itemEv = new GettingPickedUpAttemptEvent(user, item);
|
||||
RaiseLocalEvent(item, itemEv);
|
||||
|
||||
if (!itemEv.Cancelled)
|
||||
InteractWithItem(user, item);
|
||||
|
||||
return !itemEv.Cancelled;
|
||||
|
||||
}
|
||||
|
||||
public bool CanEmote(EntityUid uid)
|
||||
|
|
@ -186,13 +179,5 @@ namespace Content.Shared.ActionBlocker
|
|||
|
||||
return !ev.Cancelled;
|
||||
}
|
||||
|
||||
private void InteractWithItem(EntityUid user, EntityUid item)
|
||||
{
|
||||
var userEvent = new UserInteractedWithItemEvent(user, item);
|
||||
RaiseLocalEvent(user, userEvent);
|
||||
var itemEvent = new ItemInteractedWithEvent(user, item);
|
||||
RaiseLocalEvent(item, itemEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,23 +14,12 @@ public partial class SharedBodySystem
|
|||
{
|
||||
public void InitializeBody()
|
||||
{
|
||||
SubscribeLocalEvent<BodyComponent, MapInitEvent>(OnBodyMapInit);
|
||||
SubscribeLocalEvent<BodyComponent, ComponentInit>(OnBodyInit);
|
||||
|
||||
SubscribeLocalEvent<BodyComponent, ComponentGetState>(OnBodyGetState);
|
||||
SubscribeLocalEvent<BodyComponent, ComponentHandleState>(OnBodyHandleState);
|
||||
}
|
||||
|
||||
private void OnBodyMapInit(EntityUid bodyId, BodyComponent body, MapInitEvent args)
|
||||
{
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
if (body.Prototype == null || body.Root != null)
|
||||
return;
|
||||
|
||||
var prototype = Prototypes.Index<BodyPrototype>(body.Prototype);
|
||||
InitBody(body, prototype);
|
||||
}
|
||||
|
||||
private void OnBodyInit(EntityUid bodyId, BodyComponent body, ComponentInit args)
|
||||
{
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
|
|
@ -75,22 +64,9 @@ public partial class SharedBodySystem
|
|||
return true;
|
||||
}
|
||||
|
||||
private void InitBody(BodyComponent body, BodyPrototype prototype)
|
||||
{
|
||||
var root = prototype.Slots[prototype.Root];
|
||||
var bodyId = Spawn(root.Part, body.Owner.ToCoordinates());
|
||||
var partComponent = Comp<BodyPartComponent>(bodyId);
|
||||
var slot = new BodyPartSlot(root.Part, body.Owner, partComponent.PartType);
|
||||
body.Root = slot;
|
||||
partComponent.Body = bodyId;
|
||||
protected abstract void InitBody(BodyComponent body, BodyPrototype prototype);
|
||||
|
||||
Containers.EnsureContainer<Container>(body.Owner, BodyContainerId);
|
||||
|
||||
AttachPart(bodyId, slot, partComponent);
|
||||
InitPart(partComponent, prototype, prototype.Root);
|
||||
}
|
||||
|
||||
private void InitPart(BodyPartComponent parent, BodyPrototype prototype, string slotId, HashSet<string>? initialized = null)
|
||||
protected void InitPart(BodyPartComponent parent, BodyPrototype prototype, string slotId, HashSet<string>? initialized = null)
|
||||
{
|
||||
initialized ??= new HashSet<string>();
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ namespace Content.Shared.Body.Systems;
|
|||
|
||||
public abstract partial class SharedBodySystem : EntitySystem
|
||||
{
|
||||
private const string BodyContainerId = "BodyContainer";
|
||||
protected const string BodyContainerId = "BodyContainer";
|
||||
|
||||
[Dependency] protected readonly IPrototypeManager Prototypes = default!;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared.Examine;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Containers;
|
||||
|
|
@ -19,7 +20,7 @@ public sealed class ClothingSpeedModifierSystem : EntitySystem
|
|||
|
||||
SubscribeLocalEvent<ClothingSpeedModifierComponent, ComponentGetState>(OnGetState);
|
||||
SubscribeLocalEvent<ClothingSpeedModifierComponent, ComponentHandleState>(OnHandleState);
|
||||
SubscribeLocalEvent<ClothingSpeedModifierComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMoveSpeed);
|
||||
SubscribeLocalEvent<ClothingSpeedModifierComponent, InventoryRelayedEvent<RefreshMovementSpeedModifiersEvent>>(OnRefreshMoveSpeed);
|
||||
SubscribeLocalEvent<ClothingSpeedModifierComponent, GetVerbsEvent<ExamineVerb>>(OnClothingVerbExamine);
|
||||
}
|
||||
|
||||
|
|
@ -65,12 +66,12 @@ public sealed class ClothingSpeedModifierSystem : EntitySystem
|
|||
}
|
||||
}
|
||||
|
||||
private void OnRefreshMoveSpeed(EntityUid uid, ClothingSpeedModifierComponent component, RefreshMovementSpeedModifiersEvent args)
|
||||
private void OnRefreshMoveSpeed(EntityUid uid, ClothingSpeedModifierComponent component, InventoryRelayedEvent<RefreshMovementSpeedModifiersEvent> args)
|
||||
{
|
||||
if (!component.Enabled)
|
||||
return;
|
||||
|
||||
args.ModifySpeed(component.WalkModifier, component.SprintModifier);
|
||||
args.Args.ModifySpeed(component.WalkModifier, component.SprintModifier);
|
||||
}
|
||||
|
||||
private void OnClothingVerbExamine(EntityUid uid, ClothingSpeedModifierComponent component, GetVerbsEvent<ExamineVerb> args)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public abstract class SharedMagbootsSystem : EntitySystem
|
|||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MagbootsComponent, GetVerbsEvent<ActivationVerb>>(AddToggleVerb);
|
||||
SubscribeLocalEvent<MagbootsComponent, SlipAttemptEvent>(OnSlipAttempt);
|
||||
SubscribeLocalEvent<MagbootsComponent, InventoryRelayedEvent<SlipAttemptEvent>>(OnSlipAttempt);
|
||||
SubscribeLocalEvent<MagbootsComponent, GetItemActionsEvent>(OnGetActions);
|
||||
SubscribeLocalEvent<MagbootsComponent, ToggleActionEvent>(OnToggleAction);
|
||||
}
|
||||
|
|
@ -72,10 +72,10 @@ public abstract class SharedMagbootsSystem : EntitySystem
|
|||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private void OnSlipAttempt(EntityUid uid, MagbootsComponent component, SlipAttemptEvent args)
|
||||
private void OnSlipAttempt(EntityUid uid, MagbootsComponent component, InventoryRelayedEvent<SlipAttemptEvent> args)
|
||||
{
|
||||
if (component.On)
|
||||
args.Cancel();
|
||||
args.Args.Cancel();
|
||||
}
|
||||
|
||||
private void OnGetActions(EntityUid uid, MagbootsComponent component, GetItemActionsEvent args)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
using Content.Shared.Inventory;
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Electrocution
|
||||
|
|
@ -9,6 +10,8 @@ namespace Content.Shared.Electrocution
|
|||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<InsulatedComponent, ElectrocutionAttemptEvent>(OnInsulatedElectrocutionAttempt);
|
||||
// as long as legally distinct electric-mice are never added, this should be fine (otherwise a mouse-hat will transfer it's power to the wearer).
|
||||
SubscribeLocalEvent<InsulatedComponent, InventoryRelayedEvent<ElectrocutionAttemptEvent>>((e, c, ev) => OnInsulatedElectrocutionAttempt(e, c, ev.Args));
|
||||
SubscribeLocalEvent<InsulatedComponent, ComponentGetState>(OnInsulatedGetState);
|
||||
SubscribeLocalEvent<InsulatedComponent, ComponentHandleState>(OnInsulatedHandleState);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Content.Shared.IdentityManagement.Components;
|
||||
using Content.Shared.IdentityManagement.Components;
|
||||
using Content.Shared.Inventory;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Shared.IdentityManagement;
|
||||
|
|
@ -14,6 +15,7 @@ public abstract class SharedIdentitySystem : EntitySystem
|
|||
|
||||
SubscribeLocalEvent<IdentityComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<IdentityBlockerComponent, SeeIdentityAttemptEvent>(OnSeeIdentity);
|
||||
SubscribeLocalEvent<IdentityBlockerComponent, InventoryRelayedEvent<SeeIdentityAttemptEvent>>((e, c, ev) => OnSeeIdentity(e, c, ev.Args));
|
||||
}
|
||||
|
||||
private void OnSeeIdentity(EntityUid uid, IdentityBlockerComponent component, SeeIdentityAttemptEvent args)
|
||||
|
|
|
|||
17
Content.Shared/Interaction/Events/ContactInteractionEvent.cs
Normal file
17
Content.Shared/Interaction/Events/ContactInteractionEvent.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
namespace Content.Shared.Interaction.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Raised directed at two entities to indicate that they came into contact, usually as a result of some other interaction.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is currently used by the forensics and disease systems to perform on-contact interactions.
|
||||
/// </remarks>
|
||||
public sealed class ContactInteractionEvent : HandledEntityEventArgs
|
||||
{
|
||||
public readonly EntityUid Other;
|
||||
|
||||
public ContactInteractionEvent(EntityUid other)
|
||||
{
|
||||
Other = other;
|
||||
}
|
||||
}
|
||||
|
|
@ -261,7 +261,8 @@ namespace Content.Shared.Interaction
|
|||
if (target != null)
|
||||
{
|
||||
var ev = new InteractNoHandEvent(user, target.Value);
|
||||
RaiseLocalEvent(user, ev, true);
|
||||
RaiseLocalEvent(user, ev);
|
||||
DoContactInteraction(user, target.Value, ev);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -316,6 +317,7 @@ namespace Content.Shared.Interaction
|
|||
var message = new InteractHandEvent(user, target);
|
||||
RaiseLocalEvent(target, message, true);
|
||||
_adminLogger.Add(LogType.InteractHand, LogImpact.Low, $"{ToPrettyString(user):user} interacted with {ToPrettyString(target):target}");
|
||||
DoContactInteraction(user, target, message);
|
||||
if (message.Handled)
|
||||
return;
|
||||
|
||||
|
|
@ -337,6 +339,9 @@ namespace Content.Shared.Interaction
|
|||
var rangedMsg = new RangedInteractEvent(user, used, target.Value, clickLocation);
|
||||
RaiseLocalEvent(target.Value, rangedMsg, true);
|
||||
|
||||
// We contact the USED entity, but not the target.
|
||||
DoContactInteraction(user, used, rangedMsg);
|
||||
|
||||
if (rangedMsg.Handled)
|
||||
return;
|
||||
}
|
||||
|
|
@ -722,6 +727,9 @@ namespace Content.Shared.Interaction
|
|||
{
|
||||
var ev = new BeforeRangedInteractEvent(user, used, target, clickLocation, canReach);
|
||||
RaiseLocalEvent(used, ev);
|
||||
|
||||
// We contact the USED entity, but not the target.
|
||||
DoContactInteraction(user, used, ev);
|
||||
return ev.Handled;
|
||||
}
|
||||
|
||||
|
|
@ -750,6 +758,9 @@ namespace Content.Shared.Interaction
|
|||
// all interactions should only happen when in range / unobstructed, so no range check is needed
|
||||
var interactUsingEvent = new InteractUsingEvent(user, used, target, clickLocation);
|
||||
RaiseLocalEvent(target, interactUsingEvent, true);
|
||||
DoContactInteraction(user, used, interactUsingEvent);
|
||||
DoContactInteraction(user, target, interactUsingEvent);
|
||||
DoContactInteraction(used, target, interactUsingEvent);
|
||||
if (interactUsingEvent.Handled)
|
||||
return;
|
||||
|
||||
|
|
@ -765,7 +776,14 @@ namespace Content.Shared.Interaction
|
|||
target = null;
|
||||
|
||||
var afterInteractEvent = new AfterInteractEvent(user, used, target, clickLocation, canReach);
|
||||
RaiseLocalEvent(used, afterInteractEvent, false);
|
||||
RaiseLocalEvent(used, afterInteractEvent);
|
||||
DoContactInteraction(user, used, afterInteractEvent);
|
||||
if (canReach)
|
||||
{
|
||||
DoContactInteraction(user, target, afterInteractEvent);
|
||||
DoContactInteraction(used, target, afterInteractEvent);
|
||||
}
|
||||
|
||||
if (afterInteractEvent.Handled)
|
||||
return;
|
||||
|
||||
|
|
@ -774,6 +792,13 @@ namespace Content.Shared.Interaction
|
|||
|
||||
var afterInteractUsingEvent = new AfterInteractUsingEvent(user, used, target, clickLocation, canReach);
|
||||
RaiseLocalEvent(target.Value, afterInteractUsingEvent);
|
||||
|
||||
DoContactInteraction(user, used, afterInteractUsingEvent);
|
||||
if (canReach)
|
||||
{
|
||||
DoContactInteraction(user, target, afterInteractUsingEvent);
|
||||
DoContactInteraction(used, target, afterInteractUsingEvent);
|
||||
}
|
||||
}
|
||||
|
||||
#region ActivateItemInWorld
|
||||
|
|
@ -832,6 +857,7 @@ namespace Content.Shared.Interaction
|
|||
if (!activateMsg.Handled)
|
||||
return false;
|
||||
|
||||
DoContactInteraction(user, used, activateMsg);
|
||||
_useDelay.BeginDelay(used, delayComponent);
|
||||
_adminLogger.Add(LogType.InteractActivate, LogImpact.Low, $"{ToPrettyString(user):user} activated {ToPrettyString(used):used}");
|
||||
return true;
|
||||
|
|
@ -869,6 +895,7 @@ namespace Content.Shared.Interaction
|
|||
RaiseLocalEvent(used, useMsg, true);
|
||||
if (useMsg.Handled)
|
||||
{
|
||||
DoContactInteraction(user, used, useMsg);
|
||||
_useDelay.BeginDelay(used, delayComponent);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -977,6 +1004,18 @@ namespace Content.Shared.Interaction
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple convenience function to raise contact events (disease, forensics, etc).
|
||||
/// </summary>
|
||||
public void DoContactInteraction(EntityUid uidA, EntityUid? uidB, HandledEntityEventArgs? args = null)
|
||||
{
|
||||
if (uidB == null || args?.Handled == false)
|
||||
return;
|
||||
|
||||
RaiseLocalEvent(uidA, new ContactInteractionEvent(uidB.Value));
|
||||
RaiseLocalEvent(uidB.Value, new ContactInteractionEvent(uidA));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using Content.Shared.IdentityManagement.Components;
|
|||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Slippery;
|
||||
using Content.Shared.Strip.Components;
|
||||
using Content.Shared.Temperature;
|
||||
|
||||
namespace Content.Shared.Inventory;
|
||||
|
||||
|
|
@ -19,19 +20,43 @@ public partial class InventorySystem
|
|||
SubscribeLocalEvent<InventoryComponent, GetExplosionResistanceEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, BeforeStripEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, SeeIdentityAttemptEvent>(RelayInventoryEvent);
|
||||
SubscribeLocalEvent<InventoryComponent, ModifyChangedTemperatureEvent>(RelayInventoryEvent);
|
||||
}
|
||||
|
||||
protected void RelayInventoryEvent<T>(EntityUid uid, InventoryComponent component, T args) where T : EntityEventArgs, IInventoryRelayEvent
|
||||
{
|
||||
if (args.TargetSlots == SlotFlags.NONE)
|
||||
return;
|
||||
|
||||
var containerEnumerator = new ContainerSlotEnumerator(uid, component.TemplateId, _prototypeManager, this, args.TargetSlots);
|
||||
while(containerEnumerator.MoveNext(out var container))
|
||||
var ev = new InventoryRelayedEvent<T>(args);
|
||||
while (containerEnumerator.MoveNext(out var container))
|
||||
{
|
||||
if(!container.ContainedEntity.HasValue) continue;
|
||||
RaiseLocalEvent(container.ContainedEntity.Value, args, false);
|
||||
if (!container.ContainedEntity.HasValue) continue;
|
||||
RaiseLocalEvent(container.ContainedEntity.Value, ev, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event wrapper for relayed events.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This avoids nested inventory relays, and makes it easy to have certain events only handled by the initial
|
||||
/// target entity. E.g. health based movement speed modifiers should not be handled by a hat, even if that hat
|
||||
/// happens to be a dead mouse. Clothing that wishes to modify movement speed must subscribe to
|
||||
/// InventoryRelayedEvent<RefreshMovementSpeedModifiersEvent>
|
||||
/// </remarks>
|
||||
public sealed class InventoryRelayedEvent<TEvent> : EntityEventArgs where TEvent : EntityEventArgs, IInventoryRelayEvent
|
||||
{
|
||||
public readonly TEvent Args;
|
||||
|
||||
public InventoryRelayedEvent(TEvent args)
|
||||
{
|
||||
Args = args;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Events that should be relayed to inventory slots should implement this interface.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
namespace Content.Shared.Item;
|
||||
|
||||
/// <summary>
|
||||
/// Raised on the item after they do any sort of interaction with an item,
|
||||
/// useful for when you want a component on the user to do something to the user
|
||||
/// E.g. forensics, disease, etc.
|
||||
/// </summary>
|
||||
public sealed class ItemInteractedWithEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid User;
|
||||
public EntityUid Item;
|
||||
public ItemInteractedWithEvent(EntityUid user, EntityUid item)
|
||||
{
|
||||
User = user;
|
||||
Item = item;
|
||||
}
|
||||
}
|
||||
|
|
@ -111,14 +111,13 @@ public abstract class SharedItemSystem : EntitySystem
|
|||
if (args.Hands == null ||
|
||||
args.Using != null ||
|
||||
!args.CanAccess ||
|
||||
!args.CanInteract) //||
|
||||
//!_handsSystem.CanPickupAnyHand(args.User, args.Target, handsComp: args.Hands, item: component))
|
||||
!args.CanInteract ||
|
||||
!_handsSystem.CanPickupAnyHand(args.User, args.Target, handsComp: args.Hands, item: component))
|
||||
return;
|
||||
|
||||
InteractionVerb verb = new();
|
||||
// TODO ITEM
|
||||
//verb.Act = () => _handsSystem.TryPickupAnyHand(args.User, args.Target, checkActionBlocker: false,
|
||||
// handsComp: args.Hands, item: component);
|
||||
verb.Act = () => _handsSystem.TryPickupAnyHand(args.User, args.Target, checkActionBlocker: false,
|
||||
handsComp: args.Hands, item: component);
|
||||
verb.IconTexture = "/Textures/Interface/VerbIcons/pickup.svg.192dpi.png";
|
||||
|
||||
// if the item already in a container (that is not the same as the user's), then change the text.
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
namespace Content.Shared.Item;
|
||||
|
||||
/// <summary>
|
||||
/// Raised on the user after they do any sort of interaction with an item,
|
||||
/// useful for when you want a component on the user to do something to the item.
|
||||
/// E.g. forensics, disease, etc.
|
||||
/// </summary>
|
||||
public sealed class UserInteractedWithItemEvent : EntityEventArgs
|
||||
{
|
||||
public EntityUid User;
|
||||
public EntityUid Item;
|
||||
public UserInteractedWithItemEvent(EntityUid user, EntityUid item)
|
||||
{
|
||||
User = user;
|
||||
Item = item;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
using Content.Shared.Chemistry.Components;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Kitchen.Components
|
||||
{
|
||||
|
||||
public abstract class SharedReagentGrinderComponent : Component
|
||||
{
|
||||
public static string BeakerSlotId = "ReagentGrinder-reagentContainerContainer";
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ReagentGrinderGrindStartMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public ReagentGrinderGrindStartMessage()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ReagentGrinderJuiceStartMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public ReagentGrinderJuiceStartMessage()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ReagentGrinderEjectChamberAllMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public ReagentGrinderEjectChamberAllMessage()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ReagentGrinderEjectChamberContentMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public EntityUid EntityID;
|
||||
public ReagentGrinderEjectChamberContentMessage(EntityUid entityID)
|
||||
{
|
||||
EntityID = entityID;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ReagentGrinderWorkStartedMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public GrinderProgram GrinderProgram;
|
||||
public ReagentGrinderWorkStartedMessage(GrinderProgram grinderProgram)
|
||||
{
|
||||
GrinderProgram = grinderProgram;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ReagentGrinderWorkCompleteMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public ReagentGrinderWorkCompleteMessage()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum ReagentGrinderVisualState : byte
|
||||
{
|
||||
BeakerAttached
|
||||
}
|
||||
|
||||
[NetSerializable, Serializable]
|
||||
public enum ReagentGrinderUiKey : byte
|
||||
{
|
||||
Key
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum GrinderProgram : byte
|
||||
{
|
||||
Grind,
|
||||
Juice
|
||||
}
|
||||
}
|
||||
|
||||
[NetSerializable, Serializable]
|
||||
public sealed class ReagentGrinderInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
public bool IsBusy;
|
||||
public bool HasBeakerIn;
|
||||
public bool Powered;
|
||||
public bool CanJuice;
|
||||
public bool CanGrind;
|
||||
public EntityUid[] ChamberContents;
|
||||
public Solution.ReagentQuantity[]? ReagentQuantities;
|
||||
public ReagentGrinderInterfaceState(bool isBusy, bool hasBeaker, bool powered, bool canJuice, bool canGrind, EntityUid[] chamberContents, Solution.ReagentQuantity[]? heldBeakerContents)
|
||||
{
|
||||
IsBusy = isBusy;
|
||||
HasBeakerIn = hasBeaker;
|
||||
Powered = powered;
|
||||
CanJuice = canJuice;
|
||||
CanGrind = canGrind;
|
||||
ChamberContents = chamberContents;
|
||||
ReagentQuantities = heldBeakerContents;
|
||||
}
|
||||
}
|
||||
}
|
||||
99
Content.Shared/Kitchen/SharedReagentGrinder.cs
Normal file
99
Content.Shared/Kitchen/SharedReagentGrinder.cs
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
using Content.Shared.Chemistry.Components;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Kitchen
|
||||
{
|
||||
public sealed class SharedReagentGrinder
|
||||
{
|
||||
public static string BeakerSlotId = "beakerSlot";
|
||||
|
||||
public static string InputContainerId = "inputContainer";
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ReagentGrinderStartMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly GrinderProgram Program;
|
||||
public ReagentGrinderStartMessage(GrinderProgram program)
|
||||
{
|
||||
Program = program;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ReagentGrinderEjectChamberAllMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public ReagentGrinderEjectChamberAllMessage()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ReagentGrinderEjectChamberContentMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public EntityUid EntityId;
|
||||
public ReagentGrinderEjectChamberContentMessage(EntityUid entityId)
|
||||
{
|
||||
EntityId = entityId;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ReagentGrinderWorkStartedMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public GrinderProgram GrinderProgram;
|
||||
public ReagentGrinderWorkStartedMessage(GrinderProgram grinderProgram)
|
||||
{
|
||||
GrinderProgram = grinderProgram;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ReagentGrinderWorkCompleteMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public ReagentGrinderWorkCompleteMessage()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum ReagentGrinderVisualState : byte
|
||||
{
|
||||
BeakerAttached
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum GrinderProgram : byte
|
||||
{
|
||||
Grind,
|
||||
Juice
|
||||
}
|
||||
|
||||
[NetSerializable, Serializable]
|
||||
public enum ReagentGrinderUiKey : byte
|
||||
{
|
||||
Key
|
||||
}
|
||||
|
||||
[NetSerializable, Serializable]
|
||||
public sealed class ReagentGrinderInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
public bool IsBusy;
|
||||
public bool HasBeakerIn;
|
||||
public bool Powered;
|
||||
public bool CanJuice;
|
||||
public bool CanGrind;
|
||||
public EntityUid[] ChamberContents;
|
||||
public Solution.ReagentQuantity[]? ReagentQuantities;
|
||||
public ReagentGrinderInterfaceState(bool isBusy, bool hasBeaker, bool powered, bool canJuice, bool canGrind, EntityUid[] chamberContents, Solution.ReagentQuantity[]? heldBeakerContents)
|
||||
{
|
||||
IsBusy = isBusy;
|
||||
HasBeakerIn = hasBeaker;
|
||||
Powered = powered;
|
||||
CanJuice = canJuice;
|
||||
CanGrind = canGrind;
|
||||
ChamberContents = chamberContents;
|
||||
ReagentQuantities = heldBeakerContents;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ public sealed class SlowContactsSystem : EntitySystem
|
|||
if (!TryComp<SlowContactsComponent>(ent, out var slowContactsComponent))
|
||||
continue;
|
||||
|
||||
if (slowContactsComponent.IgnoreWhitelist != null && slowContactsComponent.IgnoreWhitelist.IsValid(ent))
|
||||
if (slowContactsComponent.IgnoreWhitelist != null && slowContactsComponent.IgnoreWhitelist.IsValid(uid))
|
||||
continue;
|
||||
|
||||
walkSpeed = Math.Min(walkSpeed, slowContactsComponent.WalkSpeedModifier);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Physics.Pull;
|
||||
using Content.Shared.Pulling.Components;
|
||||
using Content.Shared.Pulling.Events;
|
||||
|
|
@ -16,6 +17,7 @@ namespace Content.Shared.Pulling
|
|||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||
|
||||
public bool CanPull(EntityUid puller, EntityUid pulled)
|
||||
{
|
||||
|
|
@ -189,6 +191,8 @@ namespace Content.Shared.Pulling
|
|||
if (pullAttempt.Cancelled)
|
||||
return false;
|
||||
|
||||
_interaction.DoContactInteraction(pullable.Owner, puller.Owner);
|
||||
|
||||
_pullSm.ForceRelationship(puller, pullable);
|
||||
pullable.PrevFixedRotation = pullablePhysics.FixedRotation;
|
||||
pullablePhysics.FixedRotation = pullable.FixedRotationOnPull;
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ namespace Content.Shared.Pulling
|
|||
Verb verb = new();
|
||||
verb.Text = Loc.GetString("pulling-verb-get-data-text-stop-pulling");
|
||||
verb.Act = () => TryStopPull(component, args.User);
|
||||
verb.DoContactInteraction = false; // pulling handle its own contact interaction.
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
else if (CanPull(args.User, args.Target))
|
||||
|
|
@ -100,6 +101,7 @@ namespace Content.Shared.Pulling
|
|||
Verb verb = new();
|
||||
verb.Text = Loc.GetString("pulling-verb-get-data-text");
|
||||
verb.Act = () => TryStartPull(args.User, args.Target);
|
||||
verb.DoContactInteraction = false; // pulling handle its own contact interaction.
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ namespace Content.Shared.Roles
|
|||
{
|
||||
private string _name = string.Empty;
|
||||
private string _objective = string.Empty;
|
||||
private string? _description = string.Empty;
|
||||
|
||||
[ViewVariables]
|
||||
[IdDataFieldAttribute]
|
||||
|
|
@ -25,6 +26,16 @@ namespace Content.Shared.Roles
|
|||
private set => _name = Loc.GetString(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The description of this antag shown in a tooltip.
|
||||
/// </summary>
|
||||
[DataField("description")]
|
||||
public string? Description
|
||||
{
|
||||
get => _description;
|
||||
private set => _description = value is null ? null : Loc.GetString(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The antag's objective, displayed at round-start to the player.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -31,6 +31,15 @@ namespace Content.Shared.Roles
|
|||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public string LocalizedName => Loc.GetString(Name);
|
||||
|
||||
/// <summary>
|
||||
/// The name of this job as displayed to players.
|
||||
/// </summary>
|
||||
[DataField("description")]
|
||||
public string? Description { get; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public string? LocalizedDescription => Description is null ? null : Loc.GetString(Description);
|
||||
|
||||
[DataField("requirements")]
|
||||
public HashSet<JobRequirement>? Requirements;
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ namespace Content.Shared.Slippery
|
|||
SubscribeLocalEvent<SlipperyComponent, StepTriggerAttemptEvent>(HandleAttemptCollide);
|
||||
SubscribeLocalEvent<SlipperyComponent, StepTriggeredEvent>(HandleStepTrigger);
|
||||
SubscribeLocalEvent<NoSlipComponent, SlipAttemptEvent>(OnNoSlipAttempt);
|
||||
// as long as slip-resistant mice are never added, this should be fine (otherwise a mouse-hat will transfer it's power to the wearer).
|
||||
SubscribeLocalEvent<NoSlipComponent, InventoryRelayedEvent<SlipAttemptEvent>>((e, c, ev) => OnNoSlipAttempt(e, c, ev.Args));
|
||||
SubscribeLocalEvent<SlipperyComponent, ComponentGetState>(OnSlipperyGetState);
|
||||
SubscribeLocalEvent<SlipperyComponent, ComponentHandleState>(OnSlipperyHandleState);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Stealth.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// When added to an entity with stealth component, this component will change the visibility
|
||||
/// based on the entity's (lack of) movement.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed class StealthOnMoveComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ public sealed class ThievingSystem : EntitySystem
|
|||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ThievingComponent, BeforeStripEvent>(OnBeforeStrip);
|
||||
SubscribeLocalEvent<ThievingComponent, InventoryRelayedEvent<BeforeStripEvent>>((e, c, ev) => OnBeforeStrip(e, c, ev.Args));
|
||||
}
|
||||
|
||||
private void OnBeforeStrip(EntityUid uid, ThievingComponent component, BeforeStripEvent args)
|
||||
|
|
|
|||
15
Content.Shared/Temperature/TemperatureEvents.cs
Normal file
15
Content.Shared/Temperature/TemperatureEvents.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using Content.Shared.Inventory;
|
||||
|
||||
namespace Content.Shared.Temperature;
|
||||
|
||||
public sealed class ModifyChangedTemperatureEvent : EntityEventArgs, IInventoryRelayEvent
|
||||
{
|
||||
public SlotFlags TargetSlots { get; } = ~SlotFlags.POCKET;
|
||||
|
||||
public float TemperatureDelta;
|
||||
|
||||
public ModifyChangedTemperatureEvent(float temperature)
|
||||
{
|
||||
TemperatureDelta = temperature;
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,12 @@ namespace Content.Shared.Traits
|
|||
[DataField("name")]
|
||||
public string Name { get; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The description of this trait.
|
||||
/// </summary>
|
||||
[DataField("description")]
|
||||
public string? Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The components that get added to the player, when they pick this trait.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
namespace Content.Shared.Transform
|
||||
{
|
||||
public static class TransformExtensions
|
||||
{
|
||||
public static void AttachToGrandparent(this TransformComponent transform)
|
||||
{
|
||||
var grandParent = transform.Parent?.Parent;
|
||||
|
||||
if (grandParent == null)
|
||||
{
|
||||
transform.AttachToGridOrMap();
|
||||
return;
|
||||
}
|
||||
|
||||
transform.AttachParent(grandParent);
|
||||
}
|
||||
|
||||
public static void AttachToGrandparent(this EntityUid entity)
|
||||
{
|
||||
AttachToGrandparent(IoCManager.Resolve<IEntityManager>().GetComponent<TransformComponent>(entity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -154,6 +154,23 @@ namespace Content.Shared.Verbs
|
|||
/// <remarks>
|
||||
/// This will try to call the action delegates and raise the local events for the given verb.
|
||||
/// </remarks>
|
||||
public abstract void ExecuteVerb(Verb verb, EntityUid user, EntityUid target, bool forced = false);
|
||||
public virtual void ExecuteVerb(Verb verb, EntityUid user, EntityUid target, bool forced = false)
|
||||
{
|
||||
// invoke any relevant actions
|
||||
verb.Act?.Invoke();
|
||||
|
||||
// Maybe raise a local event
|
||||
if (verb.ExecutionEventArgs != null)
|
||||
{
|
||||
if (verb.EventTarget.IsValid())
|
||||
RaiseLocalEvent(verb.EventTarget, verb.ExecutionEventArgs);
|
||||
else
|
||||
RaiseLocalEvent(verb.ExecutionEventArgs);
|
||||
}
|
||||
|
||||
// Perform any contact interactions
|
||||
if (verb.DoContactInteraction ?? (verb.DefaultDoContactInteraction && _interactionSystem.InRangeUnobstructed(user, target)))
|
||||
_interactionSystem.DoContactInteraction(user, target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -146,6 +146,15 @@ namespace Content.Shared.Verbs
|
|||
/// </summary>
|
||||
public bool ConfirmationPopup = false;
|
||||
|
||||
/// <summary>
|
||||
/// If true, this verb will raise <see cref="ContactInteractionEvent"/>s when executed. If not explicitly
|
||||
/// specified, this will just default to raising the event if <see cref="DefaultDoContactInteraction"/> is
|
||||
/// true and the user is in range.
|
||||
/// </summary>
|
||||
public bool? DoContactInteraction;
|
||||
|
||||
public virtual bool DefaultDoContactInteraction => false;
|
||||
|
||||
/// <summary>
|
||||
/// Compares two verbs based on their <see cref="Priority"/>, <see cref="Category"/>, <see cref="Text"/>,
|
||||
/// and <see cref="IconTexture"/>.
|
||||
|
|
@ -235,6 +244,7 @@ namespace Content.Shared.Verbs
|
|||
{
|
||||
public new static string DefaultTextStyleClass = "InteractionVerb";
|
||||
public override int TypePriority => 4;
|
||||
public override bool DefaultDoContactInteraction => true;
|
||||
|
||||
public InteractionVerb() : base()
|
||||
{
|
||||
|
|
@ -256,6 +266,7 @@ namespace Content.Shared.Verbs
|
|||
public sealed class UtilityVerb : Verb
|
||||
{
|
||||
public override int TypePriority => 3;
|
||||
public override bool DefaultDoContactInteraction => true;
|
||||
|
||||
public UtilityVerb() : base()
|
||||
{
|
||||
|
|
@ -293,6 +304,7 @@ namespace Content.Shared.Verbs
|
|||
{
|
||||
public override int TypePriority => 2;
|
||||
public new static string DefaultTextStyleClass = "AlternativeVerb";
|
||||
public override bool DefaultDoContactInteraction => true;
|
||||
|
||||
public AlternativeVerb() : base()
|
||||
{
|
||||
|
|
@ -314,6 +326,7 @@ namespace Content.Shared.Verbs
|
|||
{
|
||||
public override int TypePriority => 1;
|
||||
public new static string DefaultTextStyleClass = "ActivationVerb";
|
||||
public override bool DefaultDoContactInteraction => true;
|
||||
|
||||
public ActivationVerb() : base()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -47,7 +47,11 @@ public abstract class SharedFlyBySoundSystem : EntitySystem
|
|||
|
||||
private void OnShutdown(EntityUid uid, FlyBySoundComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (!TryComp<PhysicsComponent>(uid, out var body)) return;
|
||||
if (!TryComp<PhysicsComponent>(uid, out var body) ||
|
||||
MetaData(uid).EntityLifeStage >= EntityLifeStage.Terminating)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_fixtures.DestroyFixture(body, FlyByFixture);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
|
|
@ -19,6 +17,7 @@ public abstract partial class SharedGunSystem
|
|||
SubscribeLocalEvent<ChamberMagazineAmmoProviderComponent, TakeAmmoEvent>(OnChamberMagazineTakeAmmo);
|
||||
SubscribeLocalEvent<ChamberMagazineAmmoProviderComponent, GetVerbsEvent<Verb>>(OnMagazineVerb);
|
||||
SubscribeLocalEvent<ChamberMagazineAmmoProviderComponent, EntInsertedIntoContainerMessage>(OnMagazineSlotChange);
|
||||
SubscribeLocalEvent<ChamberMagazineAmmoProviderComponent, EntRemovedFromContainerMessage>(OnMagazineSlotChange);
|
||||
SubscribeLocalEvent<ChamberMagazineAmmoProviderComponent, UseInHandEvent>(OnMagazineUse);
|
||||
SubscribeLocalEvent<ChamberMagazineAmmoProviderComponent, ExaminedEvent>(OnChamberMagazineExamine);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Weapons.Ranged.Components;
|
||||
|
|
@ -18,6 +16,7 @@ public abstract partial class SharedGunSystem
|
|||
SubscribeLocalEvent<MagazineAmmoProviderComponent, TakeAmmoEvent>(OnMagazineTakeAmmo);
|
||||
SubscribeLocalEvent<MagazineAmmoProviderComponent, GetVerbsEvent<Verb>>(OnMagazineVerb);
|
||||
SubscribeLocalEvent<MagazineAmmoProviderComponent, EntInsertedIntoContainerMessage>(OnMagazineSlotChange);
|
||||
SubscribeLocalEvent<MagazineAmmoProviderComponent, EntRemovedFromContainerMessage>(OnMagazineSlotChange);
|
||||
SubscribeLocalEvent<MagazineAmmoProviderComponent, UseInHandEvent>(OnMagazineUse);
|
||||
SubscribeLocalEvent<MagazineAmmoProviderComponent, ExaminedEvent>(OnMagazineExamine);
|
||||
}
|
||||
|
|
@ -37,7 +36,7 @@ public abstract partial class SharedGunSystem
|
|||
|
||||
if (magEnt == null) return;
|
||||
|
||||
RaiseLocalEvent(magEnt.Value, args, false);
|
||||
RaiseLocalEvent(magEnt.Value, args);
|
||||
UpdateAmmoCount(uid);
|
||||
UpdateMagazineAppearance(component, magEnt.Value);
|
||||
}
|
||||
|
|
@ -50,13 +49,16 @@ public abstract partial class SharedGunSystem
|
|||
|
||||
if (magEnt != null)
|
||||
{
|
||||
RaiseLocalEvent(magEnt.Value, args, false);
|
||||
RaiseLocalEvent(magEnt.Value, args);
|
||||
UpdateMagazineAppearance(component, magEnt.Value);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMagazineSlotChange(EntityUid uid, MagazineAmmoProviderComponent component, EntInsertedIntoContainerMessage args)
|
||||
protected virtual void OnMagazineSlotChange(EntityUid uid, MagazineAmmoProviderComponent component, ContainerModifiedMessage args)
|
||||
{
|
||||
if (MagazineSlot != args.Container.ID)
|
||||
return;
|
||||
|
||||
UpdateAmmoCount(uid);
|
||||
if (!TryComp<AppearanceComponent>(uid, out var appearance))
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,142 +1,4 @@
|
|||
Entries:
|
||||
- author: zerorulez
|
||||
changes:
|
||||
- {message: Entities without hands can no longer open/close storage and toggle locks.,
|
||||
type: Fix}
|
||||
id: 2015
|
||||
time: '2022-07-26T01:31:25.0000000+00:00'
|
||||
- author: BurninDreamer
|
||||
changes:
|
||||
- {message: The Syndicate headset no longer misinforms you in the description!,
|
||||
type: Fix}
|
||||
id: 2016
|
||||
time: '2022-07-26T01:38:30.0000000+00:00'
|
||||
- author: DogZeroX
|
||||
changes:
|
||||
- {message: The bread has been unplated., type: Remove}
|
||||
id: 2017
|
||||
time: '2022-07-26T02:37:51.0000000+00:00'
|
||||
- author: moony
|
||||
changes:
|
||||
- {message: Waystation has been pulled from rotation., type: Remove}
|
||||
id: 2018
|
||||
time: '2022-07-26T02:41:06.0000000+00:00'
|
||||
- author: DogZeroX
|
||||
changes:
|
||||
- {message: Lizards and slimes now have different speech sounds!, type: Add}
|
||||
id: 2019
|
||||
time: '2022-07-26T09:13:12.0000000+00:00'
|
||||
- author: Pancake
|
||||
changes:
|
||||
- {message: Syndicate layoffs., type: Tweak}
|
||||
id: 2020
|
||||
time: '2022-07-26T09:50:18.0000000+00:00'
|
||||
- author: DogZeroX
|
||||
changes:
|
||||
- {message: The emergency boxes now have tinned meat instead of chocolate., type: Tweak}
|
||||
id: 2021
|
||||
time: '2022-07-26T09:56:56.0000000+00:00'
|
||||
- author: Rane
|
||||
changes:
|
||||
- {message: Observers now have a warp for the nuke disk., type: Add}
|
||||
id: 2022
|
||||
time: '2022-07-26T13:55:15.0000000+00:00'
|
||||
- author: metalgearsloth
|
||||
changes:
|
||||
- {message: do_after bar no longer flickers for a frame., type: Fix}
|
||||
id: 2023
|
||||
time: '2022-07-26T13:57:31.0000000+00:00'
|
||||
- author: Elijahrane
|
||||
changes:
|
||||
- {message: 'If OOC is disabled in the middle of a round, dead players lose the
|
||||
ability to talk in LOOC.', type: Add}
|
||||
id: 2024
|
||||
time: '2022-07-26T13:58:19.0000000+00:00'
|
||||
- author: metalgearsloth
|
||||
changes:
|
||||
- {message: Bumped pulling modified speed from 0.75 to 0.9, type: Tweak}
|
||||
id: 2025
|
||||
time: '2022-07-26T13:58:40.0000000+00:00'
|
||||
- author: mirrorcult
|
||||
changes:
|
||||
- {message: People with empty-name ID cards no longer show up as having an empty
|
||||
identity., type: Fix}
|
||||
id: 2026
|
||||
time: '2022-07-26T23:49:15.0000000+00:00'
|
||||
- author: mirrorcult
|
||||
changes:
|
||||
- {message: Radio messages will properly capitalize the first letter of your message
|
||||
again., type: Fix}
|
||||
- {message: Sending a radio message will now make you whisper locally instead of
|
||||
speak., type: Add}
|
||||
id: 2027
|
||||
time: '2022-07-26T23:49:23.0000000+00:00'
|
||||
- author: EmoGarbage404
|
||||
changes:
|
||||
- {message: Zombies now move slower than normal people., type: Tweak}
|
||||
- {message: Zombies now do a bit of structure damage., type: Tweak}
|
||||
- {message: Zombies now do further decreased damage to other zombies., type: Tweak}
|
||||
- {message: Zombies that were cuffed before turning now behave properly after being
|
||||
uncuffed., type: Fix}
|
||||
- {message: Zombies no longer take damage over time from rotting., type: Fix}
|
||||
id: 2028
|
||||
time: '2022-07-27T00:35:35.0000000+00:00'
|
||||
- author: Rane
|
||||
changes:
|
||||
- {message: 'Boxers and boxing gloves have been ported from Nyanotrasen MRP! The
|
||||
role is only selectable on Lighthouse, but all boxing gloves work.', type: Add}
|
||||
- {message: Map-specific jobs no longer clog up the preference menu., type: Fix}
|
||||
id: 2029
|
||||
time: '2022-07-27T01:34:20.0000000+00:00'
|
||||
- author: Rane
|
||||
changes:
|
||||
- {message: Added sleeping., type: Add}
|
||||
- {message: Added nitrous oxide., type: Add}
|
||||
- {message: Slimes exhale nitrous oxide., type: Tweak}
|
||||
- {message: Disease interaction infection checks now use the same robust method
|
||||
that forensics do. No more edge cases where you don't check anything., type: Fix}
|
||||
id: 2030
|
||||
time: '2022-07-27T04:46:24.0000000+00:00'
|
||||
- author: mirrorcult
|
||||
changes:
|
||||
- {message: Transylvanian vampire cloak is now available rarely in the maint fluff
|
||||
spawner., type: Add}
|
||||
id: 2031
|
||||
time: '2022-07-27T08:15:47.0000000+00:00'
|
||||
- author: mirrorcult
|
||||
changes:
|
||||
- {message: 'Freon has been added. It is a refrigerant gas which reacts with nitrogen
|
||||
to rapidly cool down areas, increasing in effectiveness with temperature. It''s
|
||||
also a hallucinogenic, and you may feel woozy (and just a little brain damaged)
|
||||
after breathing it in.', type: Add}
|
||||
- {message: 'Freon can be produced by combining tritium, oxygen, and nitrogen as
|
||||
catalyst at temperatures below 73.15 kelvin.', type: Add}
|
||||
- {message: Miasma reacts with freon to subsume and convert it into more miasma.,
|
||||
type: Add}
|
||||
id: 2032
|
||||
time: '2022-07-27T09:55:28.0000000+00:00'
|
||||
- author: Peptide90
|
||||
changes:
|
||||
- {message: 'Ramens description points out you need to use boiling water, not just
|
||||
any water.', type: Fix}
|
||||
id: 2033
|
||||
time: '2022-07-27T14:18:16.0000000+00:00'
|
||||
- author: FunkySphere
|
||||
changes:
|
||||
- {message: Fixed name of mime jumpskirt (previously "mime suit"), type: Fix}
|
||||
id: 2034
|
||||
time: '2022-07-27T23:15:04.0000000+00:00'
|
||||
- author: EmoGarbage404
|
||||
changes:
|
||||
- {message: Fixed missing inhand sprites for various syndicate duffel bags., type: Fix}
|
||||
id: 2035
|
||||
time: '2022-07-27T23:32:43.0000000+00:00'
|
||||
- author: ElectroJr
|
||||
changes:
|
||||
- {message: Fixed a bug preventing the Identity system from working for most players.,
|
||||
type: Fix}
|
||||
id: 2036
|
||||
time: '2022-07-28T23:21:48.0000000+00:00'
|
||||
- author: Rane
|
||||
changes:
|
||||
- {message: Latejoiners are now eligible for traitor., type: Tweak}
|
||||
|
|
@ -2961,3 +2823,126 @@ Entries:
|
|||
- {message: Fixed portable scrubbers not scrubbing from adjacent tiles., type: Fix}
|
||||
id: 2514
|
||||
time: '2022-10-22T22:49:30.0000000+00:00'
|
||||
- author: Nairod
|
||||
changes:
|
||||
- {message: Added the head's mantles to the uniform printer., type: Add}
|
||||
id: 2515
|
||||
time: '2022-10-23T17:56:59.0000000+00:00'
|
||||
- author: checkraze
|
||||
changes:
|
||||
- {message: Added the Zookeeper. Find them patrolling and caring for Kettle station's
|
||||
various wildlife soon!, type: Add}
|
||||
id: 2516
|
||||
time: '2022-10-23T19:42:25.0000000+00:00'
|
||||
- author: Cheackraze
|
||||
changes:
|
||||
- {message: Added a Zoo and a few animals to Kettle!, type: Add}
|
||||
id: 2517
|
||||
time: '2022-10-24T02:30:37.0000000+00:00'
|
||||
- author: Peptide90
|
||||
changes:
|
||||
- {message: 'Pillar map major update. Atmos is now in engi, corridors are now smaller
|
||||
and more alive, maint has been spruced up and lots of spaces are bigger, more
|
||||
decorated and more loot sprawled.', type: Tweak}
|
||||
id: 2518
|
||||
time: '2022-10-24T17:30:50.0000000+00:00'
|
||||
- author: Morb0
|
||||
changes:
|
||||
- {message: Added two new jester suits in AutoDrobe, type: Add}
|
||||
id: 2519
|
||||
time: '2022-10-24T18:04:55.0000000+00:00'
|
||||
- author: metalgearsloth
|
||||
changes:
|
||||
- {message: Fix clientside organs being left around., type: Fix}
|
||||
id: 2520
|
||||
time: '2022-10-25T00:08:41.0000000+00:00'
|
||||
- author: EmoGarbage404
|
||||
changes:
|
||||
- {message: Some miscellaneous electronics have been additionally added to the circuit
|
||||
imprinter., type: Add}
|
||||
id: 2521
|
||||
time: '2022-10-25T00:22:12.0000000+00:00'
|
||||
- author: ElectroJr
|
||||
changes:
|
||||
- {message: Nanotrasen's experimental precrime police program has been defunded
|
||||
(fixed forensic evidence being applied and diseases spreading by simply examining
|
||||
or being in the vicinity of items)., type: Fix}
|
||||
id: 2522
|
||||
time: '2022-10-26T01:15:48.0000000+00:00'
|
||||
- author: 25203
|
||||
changes:
|
||||
- {message: Reagent grinder can now process partial stacks if the solution of the
|
||||
entire stack won't fit in the beaker., type: Fix}
|
||||
id: 2523
|
||||
time: '2022-10-26T06:34:56.0000000+00:00'
|
||||
- author: JustinTrotter
|
||||
changes:
|
||||
- {message: Syringes can now set how much solution to transfer., type: Add}
|
||||
id: 2524
|
||||
time: '2022-10-26T06:40:06.0000000+00:00'
|
||||
- author: exincore
|
||||
changes:
|
||||
- {message: added keybind awareness to chatbox hint, type: Add}
|
||||
id: 2525
|
||||
time: '2022-10-26T14:40:12.0000000+00:00'
|
||||
- author: metalgearsloth
|
||||
changes:
|
||||
- {message: Fix the item status for chambered guns flickering., type: Fix}
|
||||
id: 2526
|
||||
time: '2022-10-26T14:46:13.0000000+00:00'
|
||||
- author: ElectroJr
|
||||
changes:
|
||||
- {message: Fixed a bug that was causing bullets and other physics objects to not
|
||||
work properly., type: Fix}
|
||||
- {message: Fixed a bug that was causing crashes when attempting to follow or teleport
|
||||
to entities., type: Fix}
|
||||
id: 2527
|
||||
time: '2022-10-27T02:35:44.0000000+00:00'
|
||||
- author: checkraze
|
||||
changes:
|
||||
- {message: Nanotrasen has updated their Dart ERT shuttle for faster response times!,
|
||||
type: Tweak}
|
||||
id: 2528
|
||||
time: '2022-10-27T11:54:12.0000000+00:00'
|
||||
- author: metalgearsloth
|
||||
changes:
|
||||
- {message: Fix entity storage closing and some other erroneous flags., type: Fix}
|
||||
id: 2529
|
||||
time: '2022-10-27T14:09:16.0000000+00:00'
|
||||
- author: ShadowCommander
|
||||
changes:
|
||||
- {message: Ghostly bodies have been limited to one per person., type: Remove}
|
||||
id: 2530
|
||||
time: '2022-10-27T14:09:36.0000000+00:00'
|
||||
- author: moony
|
||||
changes:
|
||||
- {message: Removed split-station due to map rotation limits., type: Remove}
|
||||
id: 2531
|
||||
time: '2022-10-27T14:31:20.0000000+00:00'
|
||||
- author: JustinTrotter
|
||||
changes:
|
||||
- {message: Mice scream properly, type: Tweak}
|
||||
id: 2532
|
||||
time: '2022-10-28T00:42:36.0000000+00:00'
|
||||
- author: tsp01
|
||||
changes:
|
||||
- {message: The range from which you can hear players has been increased from 7
|
||||
to 10 units., type: Tweak}
|
||||
id: 2533
|
||||
time: '2022-10-28T03:25:48.0000000+00:00'
|
||||
- author: ElectroJr
|
||||
changes:
|
||||
- {message: Fixed a bug causing some entities not being interactable., type: Fix}
|
||||
id: 2534
|
||||
time: '2022-10-28T03:30:48.0000000+00:00'
|
||||
- author: Nairod
|
||||
changes:
|
||||
- {message: Changed it so lamps now spawn with power cells., type: Tweak}
|
||||
id: 2535
|
||||
time: '2022-10-28T05:22:09.0000000+00:00'
|
||||
- author: ElectroJr
|
||||
changes:
|
||||
- {message: Fixed being unable to interact with construction outlines / ghosts.,
|
||||
type: Fix}
|
||||
id: 2536
|
||||
time: '2022-10-28T06:08:47.0000000+00:00'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
hud-chatbox-info = T to talk, Tab to cycle channels.
|
||||
hud-chatbox-info = {$talk-key} to talk, {$cycle-key} to cycle channels.
|
||||
|
||||
hud-chatbox-select-name-prefixed = {$prefix} {$name}
|
||||
hud-chatbox-select-channel-Admin = Admin
|
||||
|
|
|
|||
|
|
@ -178,6 +178,8 @@ ui-options-function-shuttle-brake = Brake
|
|||
|
||||
## Network menu
|
||||
|
||||
ui-options-net-predict = Client-side prediction
|
||||
|
||||
ui-options-net-interp-ratio = State buffer size
|
||||
ui-options-net-interp-ratio-tooltip = Increasing this will generally make the game more resistant
|
||||
to server->client packet-loss, however in doing so it
|
||||
|
|
@ -190,11 +192,16 @@ ui-options-net-predict-tick-bias-tooltip = Increasing this will generally make t
|
|||
effectively adds slightly more latency and requires the
|
||||
client to predict more future ticks.
|
||||
|
||||
ui-options-net-pvs-entry = PVS entity budget
|
||||
ui-options-net-pvs-entry-tooltip = This limits the rate at which the server will send new
|
||||
ui-options-net-pvs-spawn = PVS entity spawn budget
|
||||
ui-options-net-pvs-spawn-tooltip = This limits the rate at which the server will send newly spawned
|
||||
entities to the client. Lowering this can help reduce
|
||||
stuttering due to entity spawning, but can lead to pop-in.
|
||||
|
||||
ui-options-net-pvs-entry = PVS entity budget
|
||||
ui-options-net-pvs-entry-tooltip = This limits the rate at which the server will send newly visible
|
||||
entities to the client. Lowering this can help reduce
|
||||
stuttering, but can lead to pop-in.
|
||||
|
||||
ui-options-net-pvs-leave = PVS detach rate
|
||||
ui-options-net-pvs-leave-tooltip = This limits the rate at which the client will remove
|
||||
out-of-view entities. Lowering this can help reduce
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ job-name-ertsecurity = ERT security
|
|||
job-name-ertmedic = ERT medic
|
||||
job-name-ertjanitor = ERT janitor
|
||||
job-name-boxer = boxer
|
||||
job-name-zookeeper = zookeeper
|
||||
|
||||
# Role timers - Make these alphabetical or I cut you
|
||||
JobAtmosphericTechnician = atmospheric technician
|
||||
|
|
@ -82,3 +83,4 @@ JobStationEngineer = station engineer
|
|||
JobTechnicalAssistant = technical assistant
|
||||
JobWarden = warden
|
||||
JobBoxer = boxer
|
||||
JobZookeeper = zookeeper
|
||||
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
reagent-grinder-bound-user-interface-instant-button = INSTANT
|
||||
reagent-grinder-bound-user-interface-cook-time-label = COOK TIME
|
||||
|
||||
reagent-grinder-component-cannot-put-entity-message = You can't put this in the reagent grinder!
|
||||
|
||||
grinder-menu-title = All-In-One Grinder 3000
|
||||
grinder-menu-grind-button = Grind
|
||||
grinder-menu-juice-button = Juice
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ tiles-steel-floor-mini = steel mini-tile
|
|||
tiles-steel-floor-pavement = steel pavement
|
||||
tiles-steel-floor-diagonal = steel diagonal tile
|
||||
tiles-steel-floor-offset = offset steel tile
|
||||
tiles-steel-floor-mono = steel mono tile
|
||||
tiles-steel-floor-pavement-vertical = steel vertical pavement
|
||||
tiles-steel-floor-herringbone = steel herringbone
|
||||
tiles-steel-floor-diagonal-mini = steel diagonal mini-tile
|
||||
tiles-plastic-floor = plastic tile
|
||||
tiles-wood = wood
|
||||
tiles-white-floor = white steel tile
|
||||
|
|
@ -13,12 +17,20 @@ tiles-white-floor-mini = white steel mini-tile
|
|||
tiles-white-floor-pavement = white steel pavement
|
||||
tiles-white-floor-diagonal = white steel diagonal tile
|
||||
tiles-white-floor-offset = offset white steel tile
|
||||
tiles-white-floor-mono = white steel mono tile
|
||||
tiles-white-floor-pavement-vertical = white steel vertical pavement
|
||||
tiles-white-floor-herringbone = white steel herringbone
|
||||
tiles-white-floor-diagonal-mini = white steel diagonal mini-tile
|
||||
tiles-plastic-white-floor = white plastic tile
|
||||
tiles-dark-floor = dark steel tile
|
||||
tiles-dark-floor-mini = dark steel mini-tile
|
||||
tiles-dark-floor-pavement = dark steel pavement
|
||||
tiles-dark-floor-diagonal = dark steel diagonal tile
|
||||
tiles-dark-floor-offset = offset dark steel tile
|
||||
tiles-dark-floor-mono = dark steel mono tile
|
||||
tiles-dark-floor-pavement-vertical = dark steel vertical pavement
|
||||
tiles-dark-floor-herringbone = dark steel herringbone
|
||||
tiles-dark-floor-diagonal-mini = dark steel diagonal mini-tile
|
||||
tiles-plastic-dark-floor = dark plastic tile
|
||||
tiles-techmaint-floor = techmaint floor
|
||||
tiles-reinforced-floor = reinforced floor
|
||||
|
|
@ -70,3 +82,6 @@ tiles-asteroid-ironsand-pebbles = asteroid ironsand pebbles
|
|||
tiles-asteroid-ironsand-rock = asteroid ironsand rock
|
||||
tiles-cave = cave
|
||||
tiles-cave-drought = cave drought
|
||||
tiles-techmaint3-floor = grated maintenance floor
|
||||
tiles-techmaint2-floor = steel maintenance floor
|
||||
tiles-wood2 = wood pattern floor
|
||||
|
|
|
|||
|
|
@ -663,7 +663,6 @@ entities:
|
|||
parent: 943
|
||||
type: Transform
|
||||
- type: MapGrid
|
||||
- type: EntityLookup
|
||||
- type: Broadphase
|
||||
- id: Bagel
|
||||
type: BecomesStation
|
||||
|
|
@ -31751,7 +31750,6 @@ entities:
|
|||
- type: Transform
|
||||
- index: 4
|
||||
type: Map
|
||||
- type: EntityLookup
|
||||
- type: Broadphase
|
||||
- type: PhysicsMap
|
||||
- type: OccluderTree
|
||||
|
|
|
|||
|
|
@ -5535,7 +5535,6 @@ entities:
|
|||
- type: Transform
|
||||
- index: 5
|
||||
type: Map
|
||||
- type: EntityLookup
|
||||
- type: Broadphase
|
||||
- type: PhysicsMap
|
||||
- type: OccluderTree
|
||||
|
|
@ -56709,7 +56708,6 @@ entities:
|
|||
parent: 780
|
||||
type: Transform
|
||||
- type: MapGrid
|
||||
- type: EntityLookup
|
||||
- type: Broadphase
|
||||
- id: Boxstation
|
||||
type: BecomesStation
|
||||
|
|
@ -113447,7 +113445,6 @@ entities:
|
|||
type: Transform
|
||||
- index: 1
|
||||
type: MapGrid
|
||||
- type: EntityLookup
|
||||
- type: Broadphase
|
||||
- angularDamping: 0.05
|
||||
linearDamping: 0.05
|
||||
|
|
|
|||
|
|
@ -134,7 +134,6 @@ entities:
|
|||
- parent: null
|
||||
type: Transform
|
||||
- type: MapGrid
|
||||
- type: EntityLookup
|
||||
- type: Broadphase
|
||||
- id: centcomm
|
||||
type: BecomesStation
|
||||
|
|
|
|||
|
|
@ -918,7 +918,6 @@ entities:
|
|||
- type: Transform
|
||||
- index: 7
|
||||
type: Map
|
||||
- type: EntityLookup
|
||||
- type: Broadphase
|
||||
- type: PhysicsMap
|
||||
- type: OccluderTree
|
||||
|
|
@ -953,7 +952,6 @@ entities:
|
|||
parent: 122
|
||||
type: Transform
|
||||
- type: MapGrid
|
||||
- type: EntityLookup
|
||||
- type: Broadphase
|
||||
- angularDamping: 0.05
|
||||
linearDamping: 0.05
|
||||
|
|
|
|||
15346
Resources/Maps/dart.yml
15346
Resources/Maps/dart.yml
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
380541
Resources/Maps/pillar.yml
380541
Resources/Maps/pillar.yml
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,11 @@
|
|||
- type: vendingMachineInventory
|
||||
id: AutoDrobeInventory
|
||||
startingInventory:
|
||||
ClothingHeadHatJester: 1
|
||||
ClothingUniformJumpsuitJester: 1
|
||||
ClothingHeadHatJesterAlt: 1
|
||||
ClothingUniformJumpsuitJesterAlt: 1
|
||||
ClothingShoesJester: 1
|
||||
ClothingOuterWinterClown: 1
|
||||
ClothingOuterWinterMime: 1
|
||||
ClothingOuterWinterMusician: 1
|
||||
|
|
|
|||
|
|
@ -102,3 +102,143 @@
|
|||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: checkerNWSE
|
||||
|
||||
- type: decal
|
||||
id: DiagonalOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: diagonal
|
||||
|
||||
- type: decal
|
||||
id: DiagonalCheckerAOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: diagonal_checker_a
|
||||
|
||||
- type: decal
|
||||
id: DiagonalCheckerBOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: diagonal_checker_b
|
||||
|
||||
- type: decal
|
||||
id: HerringboneOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: herringbone
|
||||
|
||||
- type: decal
|
||||
id: MiniTileOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: minitile
|
||||
|
||||
- type: decal
|
||||
id: MiniTileCheckerAOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: minitile_checker_a
|
||||
|
||||
- type: decal
|
||||
id: MiniTileCheckerBOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: minitile_checker_b
|
||||
|
||||
- type: decal
|
||||
id: MiniTileDiagonalOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: minitile_diagonal
|
||||
|
||||
- type: decal
|
||||
id: MiniTileDiagonalCheckerAOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: minitile_diagonal_a
|
||||
|
||||
- type: decal
|
||||
id: MiniTileDiagonalCheckerBOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: minitile_diagonal_b
|
||||
|
||||
- type: decal
|
||||
id: MonoOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: mono
|
||||
|
||||
- type: decal
|
||||
id: OffsetOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: offset
|
||||
|
||||
- type: decal
|
||||
id: OffsetCheckerAOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: offset_checker_a
|
||||
|
||||
- type: decal
|
||||
id: OffsetCheckerBOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: offset_checker_b
|
||||
|
||||
- type: decal
|
||||
id: PavementOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: pavement
|
||||
|
||||
- type: decal
|
||||
id: PavementCheckerAOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: pavement_checker_a
|
||||
|
||||
- type: decal
|
||||
id: PavementCheckerBOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: pavement_checker_b
|
||||
|
||||
- type: decal
|
||||
id: PavementVerticalOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: pavement_vertical
|
||||
|
||||
- type: decal
|
||||
id: PavementVerticalCheckerAOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: pavement_vertical_checker_a
|
||||
|
||||
- type: decal
|
||||
id: PavementVerticalCheckerBOverlay
|
||||
tags: ["station", "overlay"]
|
||||
sprite:
|
||||
sprite: Decals/Overlays/greyscale.rsi
|
||||
state: pavement_vertical_checker_b
|
||||
|
|
|
|||
118
Resources/Prototypes/Decals/bricktile_dark.yml
Normal file
118
Resources/Prototypes/Decals/bricktile_dark.yml
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
- type: decal
|
||||
id: BrickTileDarkBox
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_box
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkCornerNe
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_corner_ne
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkCornerSe
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_corner_se
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkCornerNw
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_corner_nw
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkCornerSw
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_corner_sw
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkInnerNe
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_inner_ne
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkInnerSe
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_inner_se
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkInnerNw
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_inner_nw
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkInnerSw
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_inner_sw
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkEndN
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_end_n
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkEndE
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_end_e
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkEndS
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_end_s
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkEndW
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_end_w
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkLineN
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_line_n
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkLineE
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_line_e
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkLineS
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_line_s
|
||||
|
||||
- type: decal
|
||||
id: BrickTileDarkLineW
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: dark_line_w
|
||||
118
Resources/Prototypes/Decals/bricktile_steel.yml
Normal file
118
Resources/Prototypes/Decals/bricktile_steel.yml
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
- type: decal
|
||||
id: BrickTileSteelBox
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_box
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelCornerNe
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_corner_ne
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelCornerSe
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_corner_se
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelCornerNw
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_corner_nw
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelCornerSw
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_corner_sw
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelInnerNe
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_inner_ne
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelInnerSe
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_inner_se
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelInnerNw
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_inner_nw
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelInnerSw
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_inner_sw
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelEndN
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_end_n
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelEndE
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_end_e
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelEndS
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_end_s
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelEndW
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_end_w
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelLineN
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_line_n
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelLineE
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_line_e
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelLineS
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_line_s
|
||||
|
||||
- type: decal
|
||||
id: BrickTileSteelLineW
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: steel_line_w
|
||||
118
Resources/Prototypes/Decals/bricktile_white.yml
Normal file
118
Resources/Prototypes/Decals/bricktile_white.yml
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
- type: decal
|
||||
id: BrickTileWhiteBox
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_box
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteCornerNe
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_corner_ne
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteCornerSe
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_corner_se
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteCornerNw
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_corner_nw
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteCornerSw
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_corner_sw
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteInnerNe
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_inner_ne
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteInnerSe
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_inner_se
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteInnerNw
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_inner_nw
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteInnerSw
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_inner_sw
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteEndN
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_end_n
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteEndE
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_end_e
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteEndS
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_end_s
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteEndW
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_end_w
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteLineN
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_line_n
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteLineE
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_line_e
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteLineS
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_line_s
|
||||
|
||||
- type: decal
|
||||
id: BrickTileWhiteLineW
|
||||
tags: ["station", "markings"]
|
||||
sprite:
|
||||
sprite: Decals/bricktile.rsi
|
||||
state: white_line_w
|
||||
92
Resources/Prototypes/Decals/originsign.yml
Normal file
92
Resources/Prototypes/Decals/originsign.yml
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
- type: decal
|
||||
id: OriginStationSign1
|
||||
tags: ["station"]
|
||||
sprite:
|
||||
sprite: Decals/originsign.rsi
|
||||
state: origin1
|
||||
|
||||
- type: decal
|
||||
id: OriginStationSign2
|
||||
tags: ["station"]
|
||||
sprite:
|
||||
sprite: Decals/originsign.rsi
|
||||
state: origin2
|
||||
|
||||
- type: decal
|
||||
id: OriginStationSign3
|
||||
tags: ["station"]
|
||||
sprite:
|
||||
sprite: Decals/originsign.rsi
|
||||
state: origin3
|
||||
|
||||
- type: decal
|
||||
id: OriginStationSign4
|
||||
tags: ["station"]
|
||||
sprite:
|
||||
sprite: Decals/originsign.rsi
|
||||
state: origin4
|
||||
|
||||
- type: decal
|
||||
id: OriginStationSign5
|
||||
tags: ["station"]
|
||||
sprite:
|
||||
sprite: Decals/originsign.rsi
|
||||
state: origin5
|
||||
|
||||
- type: decal
|
||||
id: OriginStationSign6
|
||||
tags: ["station"]
|
||||
sprite:
|
||||
sprite: Decals/originsign.rsi
|
||||
state: origin6
|
||||
|
||||
- type: decal
|
||||
id: OriginStationSign7
|
||||
tags: ["station"]
|
||||
sprite:
|
||||
sprite: Decals/originsign.rsi
|
||||
state: origin7
|
||||
|
||||
|
||||
- type: decal
|
||||
id: OriginStationSign8
|
||||
tags: ["station"]
|
||||
sprite:
|
||||
sprite: Decals/originsign.rsi
|
||||
state: origin8
|
||||
|
||||
- type: decal
|
||||
id: OriginStationSign9
|
||||
tags: ["station"]
|
||||
sprite:
|
||||
sprite: Decals/originsign.rsi
|
||||
state: origin9
|
||||
|
||||
- type: decal
|
||||
id: OriginStationSign10
|
||||
tags: ["station"]
|
||||
sprite:
|
||||
sprite: Decals/originsign.rsi
|
||||
state: origin10
|
||||
|
||||
- type: decal
|
||||
id: OriginStationSign11
|
||||
tags: ["station"]
|
||||
sprite:
|
||||
sprite: Decals/originsign.rsi
|
||||
state: origin11
|
||||
|
||||
- type: decal
|
||||
id: OriginStationSign12
|
||||
tags: ["station"]
|
||||
sprite:
|
||||
sprite: Decals/originsign.rsi
|
||||
state: origin12
|
||||
|
||||
- type: decal
|
||||
id: OriginStationSign13
|
||||
tags: ["station"]
|
||||
sprite:
|
||||
sprite: Decals/originsign.rsi
|
||||
state: origin13
|
||||
|
||||
|
|
@ -478,3 +478,34 @@
|
|||
sprite: Clothing/Head/Hats/rasta.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Hats/rasta.rsi
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
id: ClothingHeadSafari
|
||||
name: safari hat
|
||||
description: Keeps the sun out of your eyes. Makes you a target for the locals.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Clothing/Head/Hats/safarihat.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Hats/safarihat.rsi
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadBase
|
||||
id: ClothingHeadHatJester
|
||||
name: jester hat
|
||||
description: A hat with bells, to add some merriness to the suit.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Clothing/Head/Hats/jester.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Hats/jester.rsi
|
||||
|
||||
- type: entity
|
||||
parent: ClothingHeadHatJester
|
||||
id: ClothingHeadHatJesterAlt
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Clothing/Head/Hats/jester2.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Head/Hats/jester2.rsi
|
||||
|
|
|
|||
|
|
@ -130,3 +130,14 @@
|
|||
description: Stylish black shoes.
|
||||
components:
|
||||
- type: NoSlip
|
||||
|
||||
- type: entity
|
||||
parent: ClothingShoesClown
|
||||
id: ClothingShoesJester
|
||||
name: jester shoes
|
||||
description: A court jester's shoes, updated with modern squeaking technology.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Clothing/Shoes/Specific/jester.rsi
|
||||
- type: Clothing
|
||||
sprite: Clothing/Shoes/Specific/jester.rsi
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue