Merge remote-tracking branch 'upstream/master' into staging
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
<DefaultWindow xmlns="https://spacestation14.io"
|
<DefaultWindow xmlns="https://spacestation14.io"
|
||||||
MinSize="650 290">
|
MinSize="650 290">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
|
<!-- Privileged and target IDs, crew manifest button. -->
|
||||||
<GridContainer Columns="2">
|
<GridContainer Columns="2">
|
||||||
<GridContainer Columns="3" HorizontalExpand="True">
|
<GridContainer Columns="3" HorizontalExpand="True">
|
||||||
<Label Text="{Loc 'id-card-console-window-privileged-id'}" />
|
<Label Text="{Loc 'id-card-console-window-privileged-id'}" />
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</GridContainer>
|
</GridContainer>
|
||||||
<Control MinSize="0 8" />
|
<Control MinSize="0 8" />
|
||||||
|
<!-- Full name and job title editing. -->
|
||||||
<GridContainer Columns="3" HSeparationOverride="4">
|
<GridContainer Columns="3" HSeparationOverride="4">
|
||||||
<Label Name="FullNameLabel" Text="{Loc 'id-card-console-window-full-name-label'}" />
|
<Label Name="FullNameLabel" Text="{Loc 'id-card-console-window-full-name-label'}" />
|
||||||
<LineEdit Name="FullNameLineEdit" HorizontalExpand="True" />
|
<LineEdit Name="FullNameLineEdit" HorizontalExpand="True" />
|
||||||
@@ -26,10 +28,19 @@
|
|||||||
<Button Name="JobTitleSaveButton" Text="{Loc 'id-card-console-window-save-button'}" Disabled="True" />
|
<Button Name="JobTitleSaveButton" Text="{Loc 'id-card-console-window-save-button'}" Disabled="True" />
|
||||||
</GridContainer>
|
</GridContainer>
|
||||||
<Control MinSize="0 8" />
|
<Control MinSize="0 8" />
|
||||||
<GridContainer Columns="2">
|
<!-- Job preset selection, grant/revoke all access buttons. -->
|
||||||
<Label Text="{Loc 'id-card-console-window-job-selection-label'}" />
|
<BoxContainer Margin="0 8 0 4">
|
||||||
<OptionButton Name="JobPresetOptionButton" />
|
<BoxContainer>
|
||||||
</GridContainer>
|
<Label Text="{Loc 'id-card-console-window-job-selection-label'}" />
|
||||||
|
<OptionButton Name="JobPresetOptionButton" />
|
||||||
|
</BoxContainer>
|
||||||
|
<Control HorizontalExpand="True"/>
|
||||||
|
<BoxContainer>
|
||||||
|
<Button Name="SelectAllButton" Text="{Loc 'id-card-console-window-select-all-button'}" />
|
||||||
|
<Button Name="DeselectAllButton" Text="{Loc 'id-card-console-window-deselect-all-button'}" />
|
||||||
|
</BoxContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
<!-- Individual access buttons -->
|
||||||
<Control Name="AccessLevelControlContainer" />
|
<Control Name="AccessLevelControlContainer" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</DefaultWindow>
|
</DefaultWindow>
|
||||||
|
|||||||
@@ -79,6 +79,18 @@ namespace Content.Client.Access.UI
|
|||||||
JobPresetOptionButton.AddItem(Loc.GetString(job.Name), _jobPrototypeIds.Count - 1);
|
JobPresetOptionButton.AddItem(Loc.GetString(job.Name), _jobPrototypeIds.Count - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SelectAllButton.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
SetAllAccess(true);
|
||||||
|
SubmitData();
|
||||||
|
};
|
||||||
|
|
||||||
|
DeselectAllButton.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
SetAllAccess(false);
|
||||||
|
SubmitData();
|
||||||
|
};
|
||||||
|
|
||||||
JobPresetOptionButton.OnItemSelected += SelectJobPreset;
|
JobPresetOptionButton.OnItemSelected += SelectJobPreset;
|
||||||
_accessButtons.Populate(accessLevels, prototypeManager);
|
_accessButtons.Populate(accessLevels, prototypeManager);
|
||||||
AccessLevelControlContainer.AddChild(_accessButtons);
|
AccessLevelControlContainer.AddChild(_accessButtons);
|
||||||
@@ -89,14 +101,13 @@ namespace Content.Client.Access.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearAllAccess()
|
/// <param name="enabled">If true, every individual access button will be pressed. If false, each will be depressed.</param>
|
||||||
|
private void SetAllAccess(bool enabled)
|
||||||
{
|
{
|
||||||
foreach (var button in _accessButtons.ButtonsList.Values)
|
foreach (var button in _accessButtons.ButtonsList.Values)
|
||||||
{
|
{
|
||||||
if (button.Pressed)
|
if (!button.Disabled && button.Pressed != enabled)
|
||||||
{
|
button.Pressed = enabled;
|
||||||
button.Pressed = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,7 +121,7 @@ namespace Content.Client.Access.UI
|
|||||||
JobTitleLineEdit.Text = Loc.GetString(job.Name);
|
JobTitleLineEdit.Text = Loc.GetString(job.Name);
|
||||||
args.Button.SelectId(args.Id);
|
args.Button.SelectId(args.Id);
|
||||||
|
|
||||||
ClearAllAccess();
|
SetAllAccess(false);
|
||||||
|
|
||||||
// this is a sussy way to do this
|
// this is a sussy way to do this
|
||||||
foreach (var access in job.Access)
|
foreach (var access in job.Access)
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ public sealed partial class BanPanel : DefaultWindow
|
|||||||
var roleGroupCheckbox = new Button
|
var roleGroupCheckbox = new Button
|
||||||
{
|
{
|
||||||
Name = $"{groupName}GroupCheckbox",
|
Name = $"{groupName}GroupCheckbox",
|
||||||
Text = "Ban all",
|
Text = Loc.GetString("role-bans-ban-group"),
|
||||||
Margin = new Thickness(0, 0, 5, 0),
|
Margin = new Thickness(0, 0, 5, 0),
|
||||||
ToggleMode = true,
|
ToggleMode = true,
|
||||||
};
|
};
|
||||||
@@ -391,7 +391,7 @@ public sealed partial class BanPanel : DefaultWindow
|
|||||||
TimeLine.Text = args.Text;
|
TimeLine.Text = args.Text;
|
||||||
if (!double.TryParse(args.Text, out var result))
|
if (!double.TryParse(args.Text, out var result))
|
||||||
{
|
{
|
||||||
ExpiresLabel.Text = "err";
|
ExpiresLabel.Text = Loc.GetString("ban-panel-expiry-error");
|
||||||
ErrorLevel |= ErrorLevelEnum.Minutes;
|
ErrorLevel |= ErrorLevelEnum.Minutes;
|
||||||
TimeLine.ModulateSelfOverride = Color.Red;
|
TimeLine.ModulateSelfOverride = Color.Red;
|
||||||
UpdateSubmitEnabled();
|
UpdateSubmitEnabled();
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ public sealed class AdminLogsEui : BaseEui
|
|||||||
ClydeWindow = _clyde.CreateWindow(new WindowCreateParameters
|
ClydeWindow = _clyde.CreateWindow(new WindowCreateParameters
|
||||||
{
|
{
|
||||||
Maximized = false,
|
Maximized = false,
|
||||||
Title = "Admin Logs",
|
Title = Loc.GetString("admin-logs-title"),
|
||||||
Monitor = monitor,
|
Monitor = monitor,
|
||||||
Width = 1100,
|
Width = 1100,
|
||||||
Height = 400
|
Height = 400
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
using Content.Shared.Crayon;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Client.Crayon
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed partial class CrayonComponent : SharedCrayonComponent
|
|
||||||
{
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] public bool UIUpdateNeeded;
|
|
||||||
[ViewVariables] public int Charges { get; set; }
|
|
||||||
[ViewVariables] public int Capacity { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +1,52 @@
|
|||||||
using Content.Client.Items;
|
using Content.Client.Items;
|
||||||
using Content.Client.Message;
|
using Content.Client.Message;
|
||||||
using Content.Client.Stylesheets;
|
using Content.Client.Stylesheets;
|
||||||
|
using Content.Shared.Charges.Components;
|
||||||
|
using Content.Shared.Charges.Systems;
|
||||||
using Content.Shared.Crayon;
|
using Content.Shared.Crayon;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.GameStates;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Client.Crayon;
|
namespace Content.Client.Crayon;
|
||||||
|
|
||||||
public sealed class CrayonSystem : SharedCrayonSystem
|
public sealed class CrayonSystem : SharedCrayonSystem
|
||||||
{
|
{
|
||||||
// Didn't do in shared because I don't think most of the server stuff can be predicted.
|
[Dependency] private readonly SharedChargesSystem _charges = default!;
|
||||||
|
[Dependency] private readonly EntityManager _entityManager = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
SubscribeLocalEvent<CrayonComponent, ComponentHandleState>(OnCrayonHandleState);
|
|
||||||
Subs.ItemStatus<CrayonComponent>(ent => new StatusControl(ent));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnCrayonHandleState(EntityUid uid, CrayonComponent component, ref ComponentHandleState args)
|
Subs.ItemStatus<CrayonComponent>(ent => new StatusControl(ent, _charges, _entityManager));
|
||||||
{
|
|
||||||
if (args.Current is not CrayonComponentState state) return;
|
|
||||||
|
|
||||||
component.Color = state.Color;
|
|
||||||
component.SelectedState = state.State;
|
|
||||||
component.Charges = state.Charges;
|
|
||||||
component.Capacity = state.Capacity;
|
|
||||||
|
|
||||||
component.UIUpdateNeeded = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class StatusControl : Control
|
private sealed class StatusControl : Control
|
||||||
{
|
{
|
||||||
private readonly CrayonComponent _parent;
|
private readonly Entity<CrayonComponent> _crayon;
|
||||||
|
private readonly SharedChargesSystem _charges;
|
||||||
private readonly RichTextLabel _label;
|
private readonly RichTextLabel _label;
|
||||||
|
private readonly int _capacity;
|
||||||
|
|
||||||
public StatusControl(CrayonComponent parent)
|
public StatusControl(Entity<CrayonComponent> crayon, SharedChargesSystem charges, EntityManager entityManage)
|
||||||
{
|
{
|
||||||
_parent = parent;
|
_crayon = crayon;
|
||||||
|
_charges = charges;
|
||||||
|
_capacity = entityManage.GetComponent<LimitedChargesComponent>(_crayon.Owner).MaxCharges;
|
||||||
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||||
AddChild(_label);
|
AddChild(_label);
|
||||||
|
|
||||||
parent.UIUpdateNeeded = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void FrameUpdate(FrameEventArgs args)
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
{
|
{
|
||||||
base.FrameUpdate(args);
|
base.FrameUpdate(args);
|
||||||
|
|
||||||
if (!_parent.UIUpdateNeeded)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_parent.UIUpdateNeeded = false;
|
|
||||||
_label.SetMarkup(Robust.Shared.Localization.Loc.GetString("crayon-drawing-label",
|
_label.SetMarkup(Robust.Shared.Localization.Loc.GetString("crayon-drawing-label",
|
||||||
("color",_parent.Color),
|
("color",_crayon.Comp.Color),
|
||||||
("state",_parent.SelectedState),
|
("state",_crayon.Comp.SelectedState),
|
||||||
("charges", _parent.Charges),
|
("charges", _charges.GetCurrentCharges(_crayon.Owner)),
|
||||||
("capacity",_parent.Capacity)));
|
("capacity", _capacity)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,11 +100,11 @@ public sealed partial class CreditsWindow : DefaultWindow
|
|||||||
|
|
||||||
var container = new BoxContainer { Orientation = LayoutOrientation.Horizontal };
|
var container = new BoxContainer { Orientation = LayoutOrientation.Horizontal };
|
||||||
|
|
||||||
var previousPageButton = new Button { Text = "Previous Page" };
|
var previousPageButton = new Button { Text = Loc.GetString("credits-window-previous-page-button") };
|
||||||
previousPageButton.OnPressed +=
|
previousPageButton.OnPressed +=
|
||||||
_ => PopulateAttributions(attributionsContainer, count - AttributionsSourcesPerPage);
|
_ => PopulateAttributions(attributionsContainer, count - AttributionsSourcesPerPage);
|
||||||
|
|
||||||
var nextPageButton = new Button { Text = "Next Page" };
|
var nextPageButton = new Button { Text = Loc.GetString("credits-window-next-page-button") };
|
||||||
nextPageButton.OnPressed +=
|
nextPageButton.OnPressed +=
|
||||||
_ => PopulateAttributions(attributionsContainer, count + AttributionsSourcesPerPage);
|
_ => PopulateAttributions(attributionsContainer, count + AttributionsSourcesPerPage);
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
StyleClasses="LabelBig" />
|
StyleClasses="LabelBig" />
|
||||||
<BoxContainer Orientation="Horizontal"
|
<BoxContainer Orientation="Horizontal"
|
||||||
Margin="0 0 0 5">
|
Margin="0 0 0 5">
|
||||||
<Label Text="{Loc 'crew-monitoring-user-interface-job'}"
|
<Label Text="{Loc 'crew-monitoring-ui-job-label'}"
|
||||||
FontColorOverride="DarkGray" />
|
FontColorOverride="DarkGray" />
|
||||||
<TextureRect Name="PersonJobIcon"
|
<TextureRect Name="PersonJobIcon"
|
||||||
TextureScale="2 2"
|
TextureScale="2 2"
|
||||||
|
|||||||
5
Content.Client/Damage/Systems/DamageOtherOnHitSystem.cs
Normal file
5
Content.Client/Damage/Systems/DamageOtherOnHitSystem.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
using Content.Shared.Damage.Systems;
|
||||||
|
|
||||||
|
namespace Content.Client.Damage.Systems;
|
||||||
|
|
||||||
|
public sealed class DamageOtherOnHitSystem : SharedDamageOtherOnHitSystem;
|
||||||
@@ -4,11 +4,11 @@
|
|||||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
||||||
<humanoid:MarkingPicker Name="MarkingPickerWidget" />
|
<humanoid:MarkingPicker Name="MarkingPickerWidget" />
|
||||||
<BoxContainer>
|
<BoxContainer>
|
||||||
<CheckBox Name="MarkingForced" Text="Force" Pressed="True" />
|
<CheckBox Name="MarkingForced" Text="{Loc humanoid-marking-modifier-force}" Pressed="True" />
|
||||||
<CheckBox Name="MarkingIgnoreSpecies" Text="Ignore Species" Pressed="True" />
|
<CheckBox Name="MarkingIgnoreSpecies" Text="{Loc humanoid-marking-modifier-ignore-species}" Pressed="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Collapsible HorizontalExpand="True">
|
<Collapsible HorizontalExpand="True">
|
||||||
<CollapsibleHeading Title="Base layers" />
|
<CollapsibleHeading Title="{Loc humanoid-marking-modifier-base-layers}" />
|
||||||
<CollapsibleBody HorizontalExpand="True">
|
<CollapsibleBody HorizontalExpand="True">
|
||||||
<BoxContainer Name="BaseLayersContainer" Orientation="Vertical" HorizontalExpand="True" />
|
<BoxContainer Name="BaseLayersContainer" Orientation="Vertical" HorizontalExpand="True" />
|
||||||
</CollapsibleBody>
|
</CollapsibleBody>
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ public sealed partial class HumanoidMarkingModifierWindow : DefaultWindow
|
|||||||
});
|
});
|
||||||
_enable = new CheckBox
|
_enable = new CheckBox
|
||||||
{
|
{
|
||||||
Text = "Enable",
|
Text = Loc.GetString("humanoid-marking-modifier-enable"),
|
||||||
HorizontalAlignment = HAlignment.Right
|
HorizontalAlignment = HAlignment.Right
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -134,8 +134,8 @@ public sealed partial class HumanoidMarkingModifierWindow : DefaultWindow
|
|||||||
OnStateChanged!();
|
OnStateChanged!();
|
||||||
};
|
};
|
||||||
|
|
||||||
var lineEditBox = new BoxContainer();
|
var lineEditBox = new BoxContainer { SeparationOverride = 4 };
|
||||||
lineEditBox.AddChild(new Label { Text = "Prototype id: "});
|
lineEditBox.AddChild(new Label { Text = Loc.GetString("humanoid-marking-modifier-prototype-id") });
|
||||||
|
|
||||||
// TODO: This line edit should really be an options / dropdown selector, not text.
|
// TODO: This line edit should really be an options / dropdown selector, not text.
|
||||||
_lineEdit = new() { MinWidth = 200 };
|
_lineEdit = new() { MinWidth = 200 };
|
||||||
|
|||||||
@@ -607,6 +607,7 @@ namespace Content.Client.Lobby.UI
|
|||||||
_species.Clear();
|
_species.Clear();
|
||||||
|
|
||||||
_species.AddRange(_prototypeManager.EnumeratePrototypes<SpeciesPrototype>().Where(o => o.RoundStart));
|
_species.AddRange(_prototypeManager.EnumeratePrototypes<SpeciesPrototype>().Where(o => o.RoundStart));
|
||||||
|
_species.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.CurrentCultureIgnoreCase));
|
||||||
var speciesIds = _species.Select(o => o.ID).ToList();
|
var speciesIds = _species.Select(o => o.ID).ToList();
|
||||||
|
|
||||||
for (var i = 0; i < _species.Count; i++)
|
for (var i = 0; i < _species.Count; i++)
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public sealed partial class CrewMonitoringNavMapControl : NavMapControl
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!LocalizedNames.TryGetValue(netEntity, out var name))
|
if (!LocalizedNames.TryGetValue(netEntity, out var name))
|
||||||
name = "Unknown";
|
name = Loc.GetString("navmap-unknown-entity");
|
||||||
|
|
||||||
var message = name + "\n" + Loc.GetString("navmap-location",
|
var message = name + "\n" + Loc.GetString("navmap-location",
|
||||||
("x", MathF.Round(blip.Coordinates.X)),
|
("x", MathF.Round(blip.Coordinates.X)),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||||
xmlns:ui="clr-namespace:Content.Client.Medical.CrewMonitoring"
|
xmlns:ui="clr-namespace:Content.Client.Medical.CrewMonitoring"
|
||||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
Title="{Loc 'crew-monitoring-user-interface-title'}"
|
Title="{Loc crew-monitoring-ui-title}"
|
||||||
Resizable="False"
|
Resizable="False"
|
||||||
SetSize="1210 700"
|
SetSize="1210 700"
|
||||||
MinSize="1210 700">
|
MinSize="1210 700">
|
||||||
@@ -11,12 +11,12 @@
|
|||||||
<BoxContainer Orientation="Vertical" Margin="0 0 10 0">
|
<BoxContainer Orientation="Vertical" Margin="0 0 10 0">
|
||||||
<controls:StripeBack>
|
<controls:StripeBack>
|
||||||
<PanelContainer>
|
<PanelContainer>
|
||||||
<Label Name="StationName" Text="Unknown station" Align="Center" Margin="0 5 0 3"/>
|
<Label Name="StationName" Text="{Loc crew-monitoring-ui-no-station-label}" Align="Center" Margin="0 5 0 3"/>
|
||||||
</PanelContainer>
|
</PanelContainer>
|
||||||
</controls:StripeBack>
|
</controls:StripeBack>
|
||||||
|
|
||||||
<LineEdit Name="SearchLineEdit" HorizontalExpand="True"
|
<LineEdit Name="SearchLineEdit" HorizontalExpand="True"
|
||||||
PlaceHolder="{Loc crew-monitor-filter-line-placeholder}" />
|
PlaceHolder="{Loc crew-monitoring-ui-filter-line-placeholder}" />
|
||||||
|
|
||||||
<ScrollContainer Name="SensorScroller"
|
<ScrollContainer Name="SensorScroller"
|
||||||
VerticalExpand="True"
|
VerticalExpand="True"
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
<!-- Table rows are filled by code -->
|
<!-- Table rows are filled by code -->
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Label Name="NoServerLabel"
|
<Label Name="NoServerLabel"
|
||||||
Text="{Loc 'crew-monitoring-user-interface-no-server'}"
|
Text="{Loc crew-monitoring-ui-no-server-label}"
|
||||||
StyleClasses="LabelHeading"
|
StyleClasses="LabelHeading"
|
||||||
FontColorOverride="Red"
|
FontColorOverride="Red"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
@@ -42,8 +42,8 @@
|
|||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<PanelContainer StyleClasses="LowDivider" />
|
<PanelContainer StyleClasses="LowDivider" />
|
||||||
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
|
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
|
||||||
<Label Text="{Loc 'crew-monitoring-user-interface-flavor-left'}" StyleClasses="WindowFooterText" />
|
<Label Text="{Loc crew-monitoring-ui-flavor-left-label}" StyleClasses="WindowFooterText" />
|
||||||
<Label Text="{Loc 'crew-monitoring-user-interface-flavor-right'}" StyleClasses="WindowFooterText"
|
<Label Text="{Loc crew-monitoring-ui-flavor-right-label}" StyleClasses="WindowFooterText"
|
||||||
HorizontalAlignment="Right" HorizontalExpand="True" Margin="0 0 5 0" />
|
HorizontalAlignment="Right" HorizontalExpand="True" Margin="0 0 5 0" />
|
||||||
<TextureRect StyleClasses="NTLogoDark" Stretch="KeepAspectCentered"
|
<TextureRect StyleClasses="NTLogoDark" Stretch="KeepAspectCentered"
|
||||||
VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="19 19"/>
|
VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="19 19"/>
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||||||
HorizontalExpand = true,
|
HorizontalExpand = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
deparmentLabel.SetMessage(Loc.GetString("crew-monitoring-user-interface-no-department"));
|
deparmentLabel.SetMessage(Loc.GetString("crew-monitoring-ui-no-department-label"));
|
||||||
deparmentLabel.StyleClasses.Add(StyleNano.StyleClassTooltipActionDescription);
|
deparmentLabel.StyleClasses.Add(StyleNano.StyleClassTooltipActionDescription);
|
||||||
|
|
||||||
SensorsTable.AddChild(deparmentLabel);
|
SensorsTable.AddChild(deparmentLabel);
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
Title="NPC debug"
|
Title="{Loc npc-debug-overlay-window-title}"
|
||||||
MinSize="200 200">
|
MinSize="200 200">
|
||||||
<BoxContainer Name="Options" Orientation="Vertical" Margin="8 8">
|
<BoxContainer Name="Options" Orientation="Vertical" Margin="8 8">
|
||||||
<controls:StripeBack>
|
<controls:StripeBack>
|
||||||
<Label Text="NPC" HorizontalAlignment="Center"/>
|
<Label Text="{Loc npc-debug-overlay-window-section-npc-label}" HorizontalAlignment="Center"/>
|
||||||
</controls:StripeBack>
|
</controls:StripeBack>
|
||||||
<BoxContainer Name="NPCBox" Orientation="Vertical">
|
<BoxContainer Name="NPCBox" Orientation="Vertical">
|
||||||
<CheckBox Name="NPCThonk" Text="Thonk"/>
|
<CheckBox Name="NPCThonk" Text="{Loc npc-debug-overlay-window-show-htn-tree-checkbox}"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<controls:StripeBack>
|
<controls:StripeBack>
|
||||||
<Label Text="Pathfinder" HorizontalAlignment="Center"/>
|
<Label Text="{Loc npc-debug-overlay-window-section-pathfinder-label}" HorizontalAlignment="Center"/>
|
||||||
</controls:StripeBack>
|
</controls:StripeBack>
|
||||||
<BoxContainer Name="PathfinderBox" Orientation="Vertical">
|
<BoxContainer Name="PathfinderBox" Orientation="Vertical">
|
||||||
<CheckBox Name="PathCrumbs" Text="Breadcrumbs"/>
|
<CheckBox Name="PathCrumbs" Text="{Loc npc-debug-overlay-window-path-breadcrumbs-checkbox}"/>
|
||||||
<CheckBox Name="PathPolys" Text="Polygons"/>
|
<CheckBox Name="PathPolys" Text="{Loc npc-debug-overlay-window-path-polygons-checkbox}"/>
|
||||||
<CheckBox Name="PathNeighbors" Text="Neighbors"/>
|
<CheckBox Name="PathNeighbors" Text="{Loc npc-debug-overlay-window-path-neighbors-checkbox}"/>
|
||||||
<CheckBox Name="PathRouteCosts" Text="Route costs"/>
|
<CheckBox Name="PathRouteCosts" Text="{Loc npc-debug-overlay-window-path-route-costs-checkbox}"/>
|
||||||
<CheckBox Name="PathRoutes" Text="Routes"/>
|
<CheckBox Name="PathRoutes" Text="{Loc npc-debug-overlay-window-path-routes-checkbox}"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</controls:FancyWindow>
|
</controls:FancyWindow>
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
using Content.Shared.PAI;
|
|
||||||
|
|
||||||
namespace Content.Client.PAI
|
|
||||||
{
|
|
||||||
public sealed class PAISystem : SharedPAISystem
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -36,7 +36,7 @@ public sealed class JointVisualsOverlay : Overlay
|
|||||||
if (xform.MapID != args.MapId)
|
if (xform.MapID != args.MapId)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var other = _entManager.GetEntity(visuals.Target);
|
var other = visuals.Target;
|
||||||
|
|
||||||
if (!xformQuery.TryGetComponent(other, out var otherXform))
|
if (!xformQuery.TryGetComponent(other, out var otherXform))
|
||||||
continue;
|
continue;
|
||||||
@@ -45,7 +45,7 @@ public sealed class JointVisualsOverlay : Overlay
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
var texture = spriteSystem.Frame0(visuals.Sprite);
|
var texture = spriteSystem.Frame0(visuals.Sprite);
|
||||||
var width = texture.Width / (float) EyeManager.PixelsPerMeter;
|
var width = texture.Width / (float)EyeManager.PixelsPerMeter;
|
||||||
|
|
||||||
var coordsA = xform.Coordinates;
|
var coordsA = xform.Coordinates;
|
||||||
var coordsB = otherXform.Coordinates;
|
var coordsB = otherXform.Coordinates;
|
||||||
@@ -58,7 +58,7 @@ public sealed class JointVisualsOverlay : Overlay
|
|||||||
|
|
||||||
var posA = xformSystem.ToMapCoordinates(coordsA).Position;
|
var posA = xformSystem.ToMapCoordinates(coordsA).Position;
|
||||||
var posB = xformSystem.ToMapCoordinates(coordsB).Position;
|
var posB = xformSystem.ToMapCoordinates(coordsB).Position;
|
||||||
var diff = (posB - posA);
|
var diff = posB - posA;
|
||||||
var length = diff.Length();
|
var length = diff.Length();
|
||||||
|
|
||||||
var midPoint = diff / 2f + posA;
|
var midPoint = diff / 2f + posA;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<!-- Station name -->
|
<!-- Station name -->
|
||||||
<controls:StripeBack>
|
<controls:StripeBack>
|
||||||
<PanelContainer>
|
<PanelContainer>
|
||||||
<Label Name="StationName" Text="Unknown station" StyleClasses="LabelBig" Align="Center"/>
|
<Label Name="StationName" Text="{Loc 'station-map-unknown-station'}" StyleClasses="LabelBig" Align="Center"/>
|
||||||
</PanelContainer>
|
</PanelContainer>
|
||||||
</controls:StripeBack>
|
</controls:StripeBack>
|
||||||
|
|
||||||
|
|||||||
@@ -58,25 +58,6 @@ public sealed class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prototype.SpriteBodyMovementState is { } movementState)
|
|
||||||
{
|
|
||||||
var spriteMovement = EnsureComp<SpriteMovementComponent>(entity);
|
|
||||||
spriteMovement.NoMovementLayers.Clear();
|
|
||||||
spriteMovement.NoMovementLayers["movement"] = new PrototypeLayerData
|
|
||||||
{
|
|
||||||
State = prototype.SpriteBodyState,
|
|
||||||
};
|
|
||||||
spriteMovement.MovementLayers.Clear();
|
|
||||||
spriteMovement.MovementLayers["movement"] = new PrototypeLayerData
|
|
||||||
{
|
|
||||||
State = movementState,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RemComp<SpriteMovementComponent>(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
base.UpdateEntityAppearance(entity, prototype);
|
base.UpdateEntityAppearance(entity, prototype);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
Content.Client/Silicons/Bots/HugBotSystem.cs
Normal file
5
Content.Client/Silicons/Bots/HugBotSystem.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
using Content.Shared.Silicons.Bots;
|
||||||
|
|
||||||
|
namespace Content.Client.Silicons.Bots;
|
||||||
|
|
||||||
|
public sealed partial class HugBotSystem : SharedHugBotSystem;
|
||||||
@@ -11,7 +11,7 @@ public sealed class StatValuesEui : BaseEui
|
|||||||
public StatValuesEui()
|
public StatValuesEui()
|
||||||
{
|
{
|
||||||
_window = new StatsWindow();
|
_window = new StatsWindow();
|
||||||
_window.Title = "Melee stats";
|
_window.Title = Loc.GetString("stat-values-ui-title");
|
||||||
_window.OpenCentered();
|
_window.OpenCentered();
|
||||||
_window.OnClose += Closed;
|
_window.OnClose += Closed;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ public sealed class AHelpUIController: UIController, IOnSystemChanged<BwoinkSyst
|
|||||||
helper.ClydeWindow = _clyde.CreateWindow(new WindowCreateParameters
|
helper.ClydeWindow = _clyde.CreateWindow(new WindowCreateParameters
|
||||||
{
|
{
|
||||||
Maximized = false,
|
Maximized = false,
|
||||||
Title = "Admin Help",
|
Title = Loc.GetString("bwoink-admin-title"),
|
||||||
Monitor = monitor,
|
Monitor = monitor,
|
||||||
Width = 900,
|
Width = 900,
|
||||||
Height = 500
|
Height = 500
|
||||||
|
|||||||
@@ -1,47 +1,47 @@
|
|||||||
<DefaultWindow Title="{Loc Make Ghost Role}"
|
<DefaultWindow Title="{Loc make-ghost-roles-window-title}"
|
||||||
xmlns="https://spacestation14.io">
|
xmlns="https://spacestation14.io">
|
||||||
|
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Name="RoleEntityLabel" Text="Entity" />
|
<Label Name="RoleEntityLabel" Text="{Loc make-ghost-roles-window-entity-label}" />
|
||||||
<Label Name="RoleEntity" Text="" />
|
<Label Name="RoleEntity" Text="" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Name="RoleNameLabel" Text="Role Name" />
|
<Label Name="RoleNameLabel" Text="{Loc make-ghost-roles-window-role-name-label}" />
|
||||||
<LineEdit Name="RoleName" HorizontalExpand="True" />
|
<LineEdit Name="RoleName" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Name="RoleDescriptionLabel" Text="Role Description" />
|
<Label Name="RoleDescriptionLabel" Text="{Loc make-ghost-roles-window-role-description-label}" />
|
||||||
<LineEdit Name="RoleDescription" HorizontalExpand="True" />
|
<LineEdit Name="RoleDescription" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Name="RoleRulesLabel" Text="Role Rules" />
|
<Label Name="RoleRulesLabel" Text="{Loc make-ghost-roles-window-role-rules-label}" />
|
||||||
<LineEdit Name="RoleRules" HorizontalExpand="True" Text="{Loc ghost-role-component-default-rules}" />
|
<LineEdit Name="RoleRules" HorizontalExpand="True" Text="{Loc ghost-role-component-default-rules}" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Name="MakeSentientLabel" Text="Make Sentient" />
|
<Label Name="MakeSentientLabel" Text="{Loc make-ghost-roles-window-make-sentient-label}" />
|
||||||
<CheckBox Name="MakeSentientCheckbox" />
|
<CheckBox Name="MakeSentientCheckbox" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Name="RaffleLabel" Text="Raffle Role?" />
|
<Label Name="RaffleLabel" Text="{Loc make-ghost-roles-window-raffle-role-label}" />
|
||||||
<OptionButton Name="RaffleButton" />
|
<OptionButton Name="RaffleButton" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Name="RaffleCustomSettingsContainer" Orientation="Vertical" Visible="False">
|
<BoxContainer Name="RaffleCustomSettingsContainer" Orientation="Vertical" Visible="False">
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Name="RaffleInitialDurationLabel" Text="Initial Duration (s)" />
|
<Label Name="RaffleInitialDurationLabel" Text="{Loc make-ghost-roles-window-initial-duration-label}" />
|
||||||
<SpinBox Name="RaffleInitialDuration" HorizontalExpand="True" />
|
<SpinBox Name="RaffleInitialDuration" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Name="RaffleJoinExtendsDurationByLabel" Text="Joins Extend By (s)" />
|
<Label Name="RaffleJoinExtendsDurationByLabel" Text="{Loc make-ghost-roles-window-join-extends-by-label}" />
|
||||||
<SpinBox Name="RaffleJoinExtendsDurationBy" HorizontalExpand="True" />
|
<SpinBox Name="RaffleJoinExtendsDurationBy" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Label Name="RaffleMaxDurationLabel" Text="Max Duration (s)" />
|
<Label Name="RaffleMaxDurationLabel" Text="{Loc make-ghost-roles-window-max-duration-label}" />
|
||||||
<SpinBox Name="RaffleMaxDuration" HorizontalExpand="True" />
|
<SpinBox Name="RaffleMaxDuration" HorizontalExpand="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal">
|
||||||
<Button Name="MakeButton" Text="Make" />
|
<Button Name="MakeButton" Text="{Loc make-ghost-roles-window-make-button}" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
|
|
||||||
|
|||||||
@@ -62,8 +62,8 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
|
|||||||
RaffleMaxDuration.ValueChanged += OnRaffleDurationChanged;
|
RaffleMaxDuration.ValueChanged += OnRaffleDurationChanged;
|
||||||
|
|
||||||
|
|
||||||
RaffleButton.AddItem("Don't raffle", RaffleDontRaffleId);
|
RaffleButton.AddItem(Loc.GetString("make-ghost-roles-window-raffle-not-button"), RaffleDontRaffleId);
|
||||||
RaffleButton.AddItem("Custom settings", RaffleCustomRaffleId);
|
RaffleButton.AddItem(Loc.GetString("make-ghost-roles-window-raffle-custom-settings-button"), RaffleCustomRaffleId);
|
||||||
|
|
||||||
var raffleProtos =
|
var raffleProtos =
|
||||||
_prototypeManager.EnumeratePrototypes<GhostRoleRaffleSettingsPrototype>();
|
_prototypeManager.EnumeratePrototypes<GhostRoleRaffleSettingsPrototype>();
|
||||||
@@ -74,7 +74,7 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
|
|||||||
_rafflePrototypes.Add(raffleProto);
|
_rafflePrototypes.Add(raffleProto);
|
||||||
var s = raffleProto.Settings;
|
var s = raffleProto.Settings;
|
||||||
var label =
|
var label =
|
||||||
$"{raffleProto.ID} (initial {s.InitialDuration}s, max {s.MaxDuration}s, join adds {s.JoinExtendsDurationBy}s)";
|
Loc.GetString("make-ghost-roles-window-raffle-settings-label", ("id", raffleProto.ID), ("initialDuration", s.InitialDuration), ("maxDuration", s.MaxDuration), ("joinExtendsDurationBy", s.JoinExtendsDurationBy));
|
||||||
RaffleButton.AddItem(label, idx++);
|
RaffleButton.AddItem(label, idx++);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,7 +92,7 @@ namespace Content.Client.UserInterface.Systems.Ghost.Controls.Roles
|
|||||||
if (RaffleInitialDuration.Value > RaffleMaxDuration.Value)
|
if (RaffleInitialDuration.Value > RaffleMaxDuration.Value)
|
||||||
{
|
{
|
||||||
MakeButton.Disabled = true;
|
MakeButton.Disabled = true;
|
||||||
MakeButton.ToolTip = "The initial duration must not exceed the maximum duration.";
|
MakeButton.ToolTip = Loc.GetString("make-ghost-roles-window-raffle-warning-tooltip");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ public sealed class StorageWindow : BaseWindow
|
|||||||
HorizontalExpand = true,
|
HorizontalExpand = true,
|
||||||
Name = "StorageLabel",
|
Name = "StorageLabel",
|
||||||
ClipText = true,
|
ClipText = true,
|
||||||
Text = "Dummy",
|
Text = Loc.GetString("comp-storage-window-dummy"),
|
||||||
StyleClasses =
|
StyleClasses =
|
||||||
{
|
{
|
||||||
"FancyWindowTitle",
|
"FancyWindowTitle",
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public sealed class MaterialArbitrageTest
|
|||||||
[
|
[
|
||||||
"BaseChemistryEmptyVial", "DrinkShotGlass", "SodiumLightTube", "DrinkGlassCoupeShaped",
|
"BaseChemistryEmptyVial", "DrinkShotGlass", "SodiumLightTube", "DrinkGlassCoupeShaped",
|
||||||
"LedLightBulb", "ExteriorLightTube", "LightTube", "DrinkGlass", "DimLightBulb", "LightBulb", "LedLightTube",
|
"LedLightBulb", "ExteriorLightTube", "LightTube", "DrinkGlass", "DimLightBulb", "LightBulb", "LedLightTube",
|
||||||
"SheetRGlass1", "ChemistryEmptyBottle01", "WarmLightBulb",
|
"ChemistryEmptyBottle01", "WarmLightBulb",
|
||||||
];
|
];
|
||||||
|
|
||||||
private readonly HashSet<string> _compositionArbitrageIgnore =
|
private readonly HashSet<string> _compositionArbitrageIgnore =
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb bolt = new()
|
Verb bolt = new()
|
||||||
{
|
{
|
||||||
Text = bolts.BoltsDown ? "Unbolt" : "Bolt",
|
Text = Loc.GetString(bolts.BoltsDown ? "admin-verbs-unbolt" : "admin-verbs-bolt"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = bolts.BoltsDown
|
Icon = bolts.BoltsDown
|
||||||
? new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/unbolt.png"))
|
? new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/unbolt.png"))
|
||||||
@@ -91,7 +91,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb emergencyAccess = new()
|
Verb emergencyAccess = new()
|
||||||
{
|
{
|
||||||
Text = airlockComp.EmergencyAccess ? "Emergency Access Off" : "Emergency Access On",
|
Text = Loc.GetString(airlockComp.EmergencyAccess ? "admin-verbs-emergency-access-off" : "admin-verbs-emergency-access-on"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/emergency_access.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/emergency_access.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -111,7 +111,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb rejuvenate = new()
|
Verb rejuvenate = new()
|
||||||
{
|
{
|
||||||
Text = "Rejuvenate",
|
Text = Loc.GetString("admin-verbs-rejuvenate"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/rejuvenate.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/rejuvenate.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -129,7 +129,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb makeIndestructible = new()
|
Verb makeIndestructible = new()
|
||||||
{
|
{
|
||||||
Text = "Make Indestructible",
|
Text = Loc.GetString("admin-verbs-make-indestructible"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/plus.svg.192dpi.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/plus.svg.192dpi.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -146,7 +146,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb makeVulnerable = new()
|
Verb makeVulnerable = new()
|
||||||
{
|
{
|
||||||
Text = "Make Vulnerable",
|
Text = Loc.GetString("admin-verbs-make-vulnerable"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/plus.svg.192dpi.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/plus.svg.192dpi.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -164,7 +164,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb refillBattery = new()
|
Verb refillBattery = new()
|
||||||
{
|
{
|
||||||
Text = "Refill Battery",
|
Text = Loc.GetString("admin-verbs-refill-battery"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/fill_battery.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/fill_battery.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -179,7 +179,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb drainBattery = new()
|
Verb drainBattery = new()
|
||||||
{
|
{
|
||||||
Text = "Drain Battery",
|
Text = Loc.GetString("admin-verbs-drain-battery"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/drain_battery.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/drain_battery.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -194,7 +194,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb infiniteBattery = new()
|
Verb infiniteBattery = new()
|
||||||
{
|
{
|
||||||
Text = "Infinite Battery",
|
Text = Loc.GetString("admin-verbs-infinite-battery"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/infinite_battery.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/infinite_battery.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -215,7 +215,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb blockUnanchor = new()
|
Verb blockUnanchor = new()
|
||||||
{
|
{
|
||||||
Text = "Block Unanchoring",
|
Text = Loc.GetString("admin-verbs-block-unanchoring"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/anchor.svg.192dpi.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/anchor.svg.192dpi.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -233,7 +233,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb refillInternalsO2 = new()
|
Verb refillInternalsO2 = new()
|
||||||
{
|
{
|
||||||
Text = "Refill Internals Oxygen",
|
Text = Loc.GetString("admin-verbs-refill-internals-oxygen"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
|
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -248,7 +248,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb refillInternalsN2 = new()
|
Verb refillInternalsN2 = new()
|
||||||
{
|
{
|
||||||
Text = "Refill Internals Nitrogen",
|
Text = Loc.GetString("admin-verbs-refill-internals-nitrogen"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/red.rsi"), "icon"),
|
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/red.rsi"), "icon"),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -263,7 +263,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb refillInternalsPlasma = new()
|
Verb refillInternalsPlasma = new()
|
||||||
{
|
{
|
||||||
Text = "Refill Internals Plasma",
|
Text = Loc.GetString("admin-verbs-refill-internals-plasma"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
|
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -281,7 +281,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb refillInternalsO2 = new()
|
Verb refillInternalsO2 = new()
|
||||||
{
|
{
|
||||||
Text = "Refill Internals Oxygen",
|
Text = Loc.GetString("admin-verbs-refill-internals-oxygen"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
|
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
|
||||||
Act = () => RefillEquippedTanks(args.User, Gas.Oxygen),
|
Act = () => RefillEquippedTanks(args.User, Gas.Oxygen),
|
||||||
@@ -293,7 +293,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb refillInternalsN2 = new()
|
Verb refillInternalsN2 = new()
|
||||||
{
|
{
|
||||||
Text = "Refill Internals Nitrogen",
|
Text = Loc.GetString("admin-verbs-refill-internals-nitrogen"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/red.rsi"), "icon"),
|
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/red.rsi"), "icon"),
|
||||||
Act = () => RefillEquippedTanks(args.User, Gas.Nitrogen),
|
Act = () => RefillEquippedTanks(args.User, Gas.Nitrogen),
|
||||||
@@ -305,7 +305,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb refillInternalsPlasma = new()
|
Verb refillInternalsPlasma = new()
|
||||||
{
|
{
|
||||||
Text = "Refill Internals Plasma",
|
Text = Loc.GetString("admin-verbs-refill-internals-plasma"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
|
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
|
||||||
Act = () => RefillEquippedTanks(args.User, Gas.Plasma),
|
Act = () => RefillEquippedTanks(args.User, Gas.Plasma),
|
||||||
@@ -318,7 +318,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb sendToTestArena = new()
|
Verb sendToTestArena = new()
|
||||||
{
|
{
|
||||||
Text = "Send to test arena",
|
Text = Loc.GetString("admin-verbs-send-to-test-arena"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/eject.svg.192dpi.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/eject.svg.192dpi.png")),
|
||||||
|
|
||||||
@@ -339,7 +339,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb grantAllAccess = new()
|
Verb grantAllAccess = new()
|
||||||
{
|
{
|
||||||
Text = "Grant All Access",
|
Text = Loc.GetString("admin-verbs-grant-all-access"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "centcom"),
|
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "centcom"),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -354,7 +354,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb revokeAllAccess = new()
|
Verb revokeAllAccess = new()
|
||||||
{
|
{
|
||||||
Text = "Revoke All Access",
|
Text = Loc.GetString("admin-verbs-revoke-all-access"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "default"),
|
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "default"),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -372,7 +372,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb grantAllAccess = new()
|
Verb grantAllAccess = new()
|
||||||
{
|
{
|
||||||
Text = "Grant All Access",
|
Text = Loc.GetString("admin-verbs-grant-all-access"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "centcom"),
|
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "centcom"),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -387,7 +387,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb revokeAllAccess = new()
|
Verb revokeAllAccess = new()
|
||||||
{
|
{
|
||||||
Text = "Revoke All Access",
|
Text = Loc.GetString("admin-verbs-revoke-all-access"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "default"),
|
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Misc/id_cards.rsi"), "default"),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -405,13 +405,13 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb adjustStack = new()
|
Verb adjustStack = new()
|
||||||
{
|
{
|
||||||
Text = "Adjust Stack",
|
Text = Loc.GetString("admin-verbs-adjust-stack"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/adjust-stack.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/adjust-stack.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
{
|
{
|
||||||
// Unbounded intentionally.
|
// Unbounded intentionally.
|
||||||
_quickDialog.OpenDialog(player, "Adjust stack", $"Amount (max {_stackSystem.GetMaxCount(stack)})", (int newAmount) =>
|
_quickDialog.OpenDialog(player, Loc.GetString("admin-verbs-adjust-stack"), Loc.GetString("admin-verbs-dialog-adjust-stack-amount", ("max", _stackSystem.GetMaxCount(stack))), (int newAmount) =>
|
||||||
{
|
{
|
||||||
_stackSystem.SetCount(args.Target, newAmount, stack);
|
_stackSystem.SetCount(args.Target, newAmount, stack);
|
||||||
});
|
});
|
||||||
@@ -424,7 +424,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb fillStack = new()
|
Verb fillStack = new()
|
||||||
{
|
{
|
||||||
Text = "Fill Stack",
|
Text = Loc.GetString("admin-verbs-fill-stack"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/fill-stack.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/fill-stack.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -440,12 +440,12 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb rename = new()
|
Verb rename = new()
|
||||||
{
|
{
|
||||||
Text = "Rename",
|
Text = Loc.GetString("admin-verbs-rename"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/rename.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/rename.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
{
|
{
|
||||||
_quickDialog.OpenDialog(player, "Rename", "Name", (string newName) =>
|
_quickDialog.OpenDialog(player, Loc.GetString("admin-verbs-dialog-rename-title"), Loc.GetString("admin-verbs-dialog-rename-name"), (string newName) =>
|
||||||
{
|
{
|
||||||
_metaSystem.SetEntityName(args.Target, newName);
|
_metaSystem.SetEntityName(args.Target, newName);
|
||||||
});
|
});
|
||||||
@@ -458,12 +458,12 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb redescribe = new()
|
Verb redescribe = new()
|
||||||
{
|
{
|
||||||
Text = "Redescribe",
|
Text = Loc.GetString("admin-verbs-redescribe"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/redescribe.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/redescribe.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
{
|
{
|
||||||
_quickDialog.OpenDialog(player, "Redescribe", "Description", (LongString newDescription) =>
|
_quickDialog.OpenDialog(player, Loc.GetString("admin-verbs-dialog-redescribe-title"), Loc.GetString("admin-verbs-dialog-redescribe-description"), (LongString newDescription) =>
|
||||||
{
|
{
|
||||||
_metaSystem.SetEntityDescription(args.Target, newDescription.String);
|
_metaSystem.SetEntityDescription(args.Target, newDescription.String);
|
||||||
});
|
});
|
||||||
@@ -476,12 +476,12 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb renameAndRedescribe = new()
|
Verb renameAndRedescribe = new()
|
||||||
{
|
{
|
||||||
Text = "Redescribe",
|
Text = Loc.GetString("admin-verbs-rename-and-redescribe"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/rename_and_redescribe.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/rename_and_redescribe.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
{
|
{
|
||||||
_quickDialog.OpenDialog(player, "Rename & Redescribe", "Name", "Description",
|
_quickDialog.OpenDialog(player, Loc.GetString("admin-verbs-dialog-rename-and-redescribe-title"), Loc.GetString("admin-verbs-dialog-rename-name"), Loc.GetString("admin-verbs-dialog-redescribe-description"),
|
||||||
(string newName, LongString newDescription) =>
|
(string newName, LongString newDescription) =>
|
||||||
{
|
{
|
||||||
var meta = MetaData(args.Target);
|
var meta = MetaData(args.Target);
|
||||||
@@ -501,7 +501,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb barJobSlots = new()
|
Verb barJobSlots = new()
|
||||||
{
|
{
|
||||||
Text = "Bar job slots",
|
Text = Loc.GetString("admin-verbs-bar-job-slots"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/bar_jobslots.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/bar_jobslots.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -520,7 +520,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb locateCargoShuttle = new()
|
Verb locateCargoShuttle = new()
|
||||||
{
|
{
|
||||||
Text = "Locate Cargo Shuttle",
|
Text = Loc.GetString("admin-verbs-locate-cargo-shuttle"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Clothing/Head/Soft/cargosoft.rsi"), "icon"),
|
Icon = new SpriteSpecifier.Rsi(new("/Textures/Clothing/Head/Soft/cargosoft.rsi"), "icon"),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -543,7 +543,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb refillBattery = new()
|
Verb refillBattery = new()
|
||||||
{
|
{
|
||||||
Text = "Refill Battery",
|
Text = Loc.GetString("admin-verbs-refill-battery"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/fill_battery.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/fill_battery.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -564,7 +564,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb drainBattery = new()
|
Verb drainBattery = new()
|
||||||
{
|
{
|
||||||
Text = "Drain Battery",
|
Text = Loc.GetString("admin-verbs-drain-battery"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/drain_battery.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/drain_battery.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -585,7 +585,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
|
|
||||||
Verb infiniteBattery = new()
|
Verb infiniteBattery = new()
|
||||||
{
|
{
|
||||||
Text = "Infinite Battery",
|
Text = Loc.GetString("admin-verbs-infinite-battery"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/infinite_battery.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/infinite_battery.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -615,7 +615,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb haltMovement = new()
|
Verb haltMovement = new()
|
||||||
{
|
{
|
||||||
Text = "Halt Movement",
|
Text = Loc.GetString("admin-verbs-halt-movement"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/halt.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/halt.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -638,7 +638,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb unpauseMap = new()
|
Verb unpauseMap = new()
|
||||||
{
|
{
|
||||||
Text = "Unpause Map",
|
Text = Loc.GetString("admin-verbs-unpause-map"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/play.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/play.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -655,7 +655,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb pauseMap = new()
|
Verb pauseMap = new()
|
||||||
{
|
{
|
||||||
Text = "Pause Map",
|
Text = Loc.GetString("admin-verbs-pause-map"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/pause.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/pause.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -675,7 +675,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb snapJoints = new()
|
Verb snapJoints = new()
|
||||||
{
|
{
|
||||||
Text = "Snap Joints",
|
Text = Loc.GetString("admin-verbs-snap-joints"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/snap_joints.png")),
|
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/snap_joints.png")),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -693,7 +693,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb minigunFire = new()
|
Verb minigunFire = new()
|
||||||
{
|
{
|
||||||
Text = "Make Minigun",
|
Text = Loc.GetString("admin-verbs-make-minigun"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Weapons/Guns/HMGs/minigun.rsi"), "icon"),
|
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Weapons/Guns/HMGs/minigun.rsi"), "icon"),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
@@ -712,12 +712,12 @@ public sealed partial class AdminVerbSystem
|
|||||||
{
|
{
|
||||||
Verb setCapacity = new()
|
Verb setCapacity = new()
|
||||||
{
|
{
|
||||||
Text = "Set Bullet Amount",
|
Text = Loc.GetString("admin-verbs-set-bullet-amount"),
|
||||||
Category = VerbCategory.Tricks,
|
Category = VerbCategory.Tricks,
|
||||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Fun/caps.rsi"), "mag-6"),
|
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Fun/caps.rsi"), "mag-6"),
|
||||||
Act = () =>
|
Act = () =>
|
||||||
{
|
{
|
||||||
_quickDialog.OpenDialog(player, "Set Bullet Amount", $"Amount (standard {ballisticAmmo.Capacity}):", (string amount) =>
|
_quickDialog.OpenDialog(player, Loc.GetString("admin-verbs-dialog-set-bullet-amount-title"), Loc.GetString("admin-verbs-dialog-set-bullet-amount-amount", ("cap", ballisticAmmo.Capacity)), (string amount) =>
|
||||||
{
|
{
|
||||||
if (!int.TryParse(amount, out var result))
|
if (!int.TryParse(amount, out var result))
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -46,7 +46,11 @@ public sealed class AutoEmoteSystem : EntitySystem
|
|||||||
|
|
||||||
if (autoEmotePrototype.WithChat)
|
if (autoEmotePrototype.WithChat)
|
||||||
{
|
{
|
||||||
_chatSystem.TryEmoteWithChat(uid, autoEmotePrototype.EmoteId, autoEmotePrototype.HiddenFromChatWindow ? ChatTransmitRange.HideChat : ChatTransmitRange.Normal);
|
_chatSystem.TryEmoteWithChat(uid,
|
||||||
|
autoEmotePrototype.EmoteId,
|
||||||
|
autoEmotePrototype.HiddenFromChatWindow ? ChatTransmitRange.HideChat : ChatTransmitRange.Normal,
|
||||||
|
ignoreActionBlocker: autoEmotePrototype.IgnoreActionBlocker,
|
||||||
|
forceEmote: autoEmotePrototype.Force);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Content.Shared.Access.Components;
|
|||||||
using Content.Shared.Clothing;
|
using Content.Shared.Clothing;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Humanoid;
|
using Content.Shared.Humanoid;
|
||||||
|
using Content.Shared.Interaction.Components;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.PDA;
|
using Content.Shared.PDA;
|
||||||
using Content.Shared.Preferences;
|
using Content.Shared.Preferences;
|
||||||
@@ -23,7 +24,7 @@ public sealed class OutfitSystem : EntitySystem
|
|||||||
[Dependency] private readonly InventorySystem _invSystem = default!;
|
[Dependency] private readonly InventorySystem _invSystem = default!;
|
||||||
[Dependency] private readonly SharedStationSpawningSystem _spawningSystem = default!;
|
[Dependency] private readonly SharedStationSpawningSystem _spawningSystem = default!;
|
||||||
|
|
||||||
public bool SetOutfit(EntityUid target, string gear, Action<EntityUid, EntityUid>? onEquipped = null)
|
public bool SetOutfit(EntityUid target, string gear, Action<EntityUid, EntityUid>? onEquipped = null, bool unremovable = false)
|
||||||
{
|
{
|
||||||
if (!EntityManager.TryGetComponent(target, out InventoryComponent? inventoryComponent))
|
if (!EntityManager.TryGetComponent(target, out InventoryComponent? inventoryComponent))
|
||||||
return false;
|
return false;
|
||||||
@@ -60,6 +61,8 @@ public sealed class OutfitSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
_invSystem.TryEquip(target, equipmentEntity, slot.Name, silent: true, force: true, inventory: inventoryComponent);
|
_invSystem.TryEquip(target, equipmentEntity, slot.Name, silent: true, force: true, inventory: inventoryComponent);
|
||||||
|
if (unremovable)
|
||||||
|
EnsureComp<UnremoveableComponent>(equipmentEntity);
|
||||||
|
|
||||||
onEquipped?.Invoke(target, equipmentEntity);
|
onEquipped?.Invoke(target, equipmentEntity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ using Content.Server.Clothing.Systems;
|
|||||||
using Content.Shared.Chat.Prototypes;
|
using Content.Shared.Chat.Prototypes;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Content.Shared.Stunnable;
|
using Content.Shared.Stunnable;
|
||||||
using Content.Shared.Damage.Prototypes;
|
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Content.Server.Emoting.Systems;
|
using Content.Server.Emoting.Systems;
|
||||||
@@ -21,7 +20,6 @@ namespace Content.Server.Cluwne;
|
|||||||
|
|
||||||
public sealed class CluwneSystem : EntitySystem
|
public sealed class CluwneSystem : EntitySystem
|
||||||
{
|
{
|
||||||
private static readonly ProtoId<DamageGroupPrototype> GeneticDamageGroup = "Genetic";
|
|
||||||
|
|
||||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
@@ -48,15 +46,14 @@ public sealed class CluwneSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// On death removes active comps and gives genetic damage to prevent cloning, reduce this to allow cloning.
|
/// On death removes active comps and gives genetic damage to prevent cloning, reduce this to allow cloning.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnMobState(EntityUid uid, CluwneComponent component, MobStateChangedEvent args)
|
private void OnMobState(Entity<CluwneComponent> ent, ref MobStateChangedEvent args)
|
||||||
{
|
{
|
||||||
if (args.NewMobState == MobState.Dead)
|
if (args.NewMobState == MobState.Dead)
|
||||||
{
|
{
|
||||||
RemComp<CluwneComponent>(uid);
|
RemComp<CluwneComponent>(ent.Owner);
|
||||||
RemComp<ClumsyComponent>(uid);
|
RemComp<ClumsyComponent>(ent.Owner);
|
||||||
RemComp<AutoEmoteComponent>(uid);
|
RemComp<AutoEmoteComponent>(ent.Owner);
|
||||||
var damageSpec = new DamageSpecifier(_prototypeManager.Index(GeneticDamageGroup), 300);
|
_damageableSystem.TryChangeDamage(ent.Owner, ent.Comp.RevertDamage);
|
||||||
_damageableSystem.TryChangeDamage(uid, damageSpec);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,52 +62,65 @@ public sealed class CluwneSystem : EntitySystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// OnStartup gives the cluwne outfit, ensures clumsy, and makes sure emote sounds are laugh.
|
/// OnStartup gives the cluwne outfit, ensures clumsy, and makes sure emote sounds are laugh.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnComponentStartup(EntityUid uid, CluwneComponent component, ComponentStartup args)
|
private void OnComponentStartup(Entity<CluwneComponent> ent, ref ComponentStartup args)
|
||||||
{
|
{
|
||||||
if (component.EmoteSoundsId == null)
|
if (ent.Comp.EmoteSoundsId == null)
|
||||||
return;
|
return;
|
||||||
_prototypeManager.TryIndex(component.EmoteSoundsId, out EmoteSounds);
|
|
||||||
|
|
||||||
EnsureComp<AutoEmoteComponent>(uid);
|
_prototypeManager.TryIndex(ent.Comp.EmoteSoundsId, out EmoteSounds);
|
||||||
_autoEmote.AddEmote(uid, "CluwneGiggle");
|
|
||||||
EnsureComp<ClumsyComponent>(uid);
|
|
||||||
|
|
||||||
_popupSystem.PopupEntity(Loc.GetString("cluwne-transform", ("target", uid)), uid, PopupType.LargeCaution);
|
|
||||||
_audio.PlayPvs(component.SpawnSound, uid);
|
|
||||||
|
|
||||||
_nameMod.RefreshNameModifiers(uid);
|
if (ent.Comp.RandomEmote && ent.Comp.AutoEmoteId != null)
|
||||||
|
{
|
||||||
|
EnsureComp<AutoEmoteComponent>(ent.Owner);
|
||||||
|
_autoEmote.AddEmote(ent.Owner, ent.Comp.AutoEmoteId);
|
||||||
|
}
|
||||||
|
|
||||||
_outfitSystem.SetOutfit(uid, "CluwneGear");
|
EnsureComp<ClumsyComponent>(ent.Owner);
|
||||||
|
|
||||||
|
var transformMessage = Loc.GetString(ent.Comp.TransformMessage, ("target", ent.Owner));
|
||||||
|
|
||||||
|
_popupSystem.PopupEntity(transformMessage, ent.Owner, PopupType.LargeCaution);
|
||||||
|
_audio.PlayPvs(ent.Comp.SpawnSound, ent.Owner);
|
||||||
|
|
||||||
|
_nameMod.RefreshNameModifiers(ent.Owner);
|
||||||
|
|
||||||
|
|
||||||
|
_outfitSystem.SetOutfit(ent.Owner, ent.Comp.OutfitId, unremovable: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles the timing on autoemote as well as falling over and honking.
|
/// Handles the timing on autoemote as well as falling over and honking.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnEmote(EntityUid uid, CluwneComponent component, ref EmoteEvent args)
|
private void OnEmote(Entity<CluwneComponent> ent, ref EmoteEvent args)
|
||||||
{
|
{
|
||||||
if (args.Handled)
|
if (args.Handled)
|
||||||
return;
|
return;
|
||||||
args.Handled = _chat.TryPlayEmoteSound(uid, EmoteSounds, args.Emote);
|
|
||||||
|
|
||||||
if (_robustRandom.Prob(component.GiggleRandomChance))
|
if (!ent.Comp.RandomEmote)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args.Handled = _chat.TryPlayEmoteSound(ent.Owner, EmoteSounds, args.Emote);
|
||||||
|
|
||||||
|
if (_robustRandom.Prob(ent.Comp.GiggleRandomChance))
|
||||||
{
|
{
|
||||||
_audio.PlayPvs(component.SpawnSound, uid);
|
_audio.PlayPvs(ent.Comp.SpawnSound, ent.Owner);
|
||||||
_chat.TrySendInGameICMessage(uid, "honks", InGameICChatType.Emote, ChatTransmitRange.Normal);
|
_chat.TrySendInGameICMessage(ent.Owner, Loc.GetString(ent.Comp.GiggleEmote), InGameICChatType.Emote, ChatTransmitRange.Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (_robustRandom.Prob(component.KnockChance))
|
else if (_robustRandom.Prob(ent.Comp.KnockChance))
|
||||||
{
|
{
|
||||||
_audio.PlayPvs(component.KnockSound, uid);
|
_audio.PlayPvs(ent.Comp.KnockSound, ent.Owner);
|
||||||
_stunSystem.TryUpdateParalyzeDuration(uid, TimeSpan.FromSeconds(component.ParalyzeTime));
|
_stunSystem.TryUpdateParalyzeDuration(ent.Owner, TimeSpan.FromSeconds(ent.Comp.ParalyzeTime));
|
||||||
_chat.TrySendInGameICMessage(uid, "spasms", InGameICChatType.Emote, ChatTransmitRange.Normal);
|
_chat.TrySendInGameICMessage(ent.Owner, Loc.GetString(ent.Comp.KnockEmote), InGameICChatType.Emote, ChatTransmitRange.Normal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies "Cluwnified" prefix
|
/// Applies "Cluwnified" prefix
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnRefreshNameModifiers(Entity<CluwneComponent> entity, ref RefreshNameModifiersEvent args)
|
private void OnRefreshNameModifiers(Entity<CluwneComponent> ent, ref RefreshNameModifiersEvent args)
|
||||||
{
|
{
|
||||||
args.AddModifier("cluwne-name-prefix");
|
args.AddModifier(ent.Comp.NamePrefix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
using Content.Server.UserInterface;
|
|
||||||
using Content.Shared.Crayon;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
|
|
||||||
namespace Content.Server.Crayon
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed partial class CrayonComponent : SharedCrayonComponent
|
|
||||||
{
|
|
||||||
[DataField("useSound")] public SoundSpecifier? UseSound;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("selectableColor")]
|
|
||||||
public bool SelectableColor { get; set; }
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public int Charges { get; set; }
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("capacity")]
|
|
||||||
public int Capacity { get; set; } = 30;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("deleteEmpty")]
|
|
||||||
public bool DeleteEmpty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,7 @@ using System.Numerics;
|
|||||||
using Content.Server.Administration.Logs;
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.Decals;
|
using Content.Server.Decals;
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
|
using Content.Shared.Charges.Systems;
|
||||||
using Content.Shared.Crayon;
|
using Content.Shared.Crayon;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Decals;
|
using Content.Shared.Decals;
|
||||||
@@ -12,7 +13,6 @@ using Content.Shared.Nutrition.EntitySystems;
|
|||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
using Robust.Shared.GameStates;
|
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.Crayon;
|
namespace Content.Server.Crayon;
|
||||||
@@ -24,23 +24,27 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
|||||||
[Dependency] private readonly DecalSystem _decals = default!;
|
[Dependency] private readonly DecalSystem _decals = default!;
|
||||||
[Dependency] private readonly PopupSystem _popup = default!;
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
|
[Dependency] private readonly SharedChargesSystem _charges = default!;
|
||||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
SubscribeLocalEvent<CrayonComponent, ComponentInit>(OnCrayonInit);
|
|
||||||
|
SubscribeLocalEvent<CrayonComponent, MapInitEvent>(OnMapInit);
|
||||||
SubscribeLocalEvent<CrayonComponent, CrayonSelectMessage>(OnCrayonBoundUI);
|
SubscribeLocalEvent<CrayonComponent, CrayonSelectMessage>(OnCrayonBoundUI);
|
||||||
SubscribeLocalEvent<CrayonComponent, CrayonColorMessage>(OnCrayonBoundUIColor);
|
SubscribeLocalEvent<CrayonComponent, CrayonColorMessage>(OnCrayonBoundUIColor);
|
||||||
SubscribeLocalEvent<CrayonComponent, UseInHandEvent>(OnCrayonUse, before: new[] { typeof(FoodSystem) });
|
SubscribeLocalEvent<CrayonComponent, UseInHandEvent>(OnCrayonUse, before: new[] { typeof(FoodSystem) });
|
||||||
SubscribeLocalEvent<CrayonComponent, AfterInteractEvent>(OnCrayonAfterInteract, after: new[] { typeof(FoodSystem) });
|
SubscribeLocalEvent<CrayonComponent, AfterInteractEvent>(OnCrayonAfterInteract, after: new[] { typeof(FoodSystem) });
|
||||||
SubscribeLocalEvent<CrayonComponent, DroppedEvent>(OnCrayonDropped);
|
SubscribeLocalEvent<CrayonComponent, DroppedEvent>(OnCrayonDropped);
|
||||||
SubscribeLocalEvent<CrayonComponent, ComponentGetState>(OnCrayonGetState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void OnCrayonGetState(EntityUid uid, CrayonComponent component, ref ComponentGetState args)
|
private void OnMapInit(Entity<CrayonComponent> ent, ref MapInitEvent args)
|
||||||
{
|
{
|
||||||
args.State = new CrayonComponentState(component.Color, component.SelectedState, component.Charges, component.Capacity);
|
// Get the first one from the catalog and set it as default
|
||||||
|
var decal = _prototypeManager.EnumeratePrototypes<DecalPrototype>().FirstOrDefault(x => x.Tags.Contains("crayon"));
|
||||||
|
ent.Comp.SelectedState = decal?.ID ?? string.Empty;
|
||||||
|
Dirty(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCrayonAfterInteract(EntityUid uid, CrayonComponent component, AfterInteractEvent args)
|
private void OnCrayonAfterInteract(EntityUid uid, CrayonComponent component, AfterInteractEvent args)
|
||||||
@@ -48,7 +52,7 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
|||||||
if (args.Handled || !args.CanReach)
|
if (args.Handled || !args.CanReach)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (component.Charges <= 0)
|
if (_charges.IsEmpty(uid))
|
||||||
{
|
{
|
||||||
if (component.DeleteEmpty)
|
if (component.DeleteEmpty)
|
||||||
UseUpCrayon(uid, args.User);
|
UseUpCrayon(uid, args.User);
|
||||||
@@ -72,17 +76,15 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
|||||||
if (component.UseSound != null)
|
if (component.UseSound != null)
|
||||||
_audio.PlayPvs(component.UseSound, uid, AudioParams.Default.WithVariation(0.125f));
|
_audio.PlayPvs(component.UseSound, uid, AudioParams.Default.WithVariation(0.125f));
|
||||||
|
|
||||||
// Decrease "Ammo"
|
_charges.TryUseCharge(uid);
|
||||||
component.Charges--;
|
|
||||||
Dirty(uid, component);
|
|
||||||
|
|
||||||
_adminLogger.Add(LogType.CrayonDraw, LogImpact.Low, $"{ToPrettyString(args.User):user} drew a {component.Color:color} {component.SelectedState}");
|
_adminLogger.Add(LogType.CrayonDraw, LogImpact.Low, $"{ToPrettyString(args.User):user} drew a {component.Color:color} {component.SelectedState}");
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
|
|
||||||
if (component.DeleteEmpty && component.Charges <= 0)
|
if (component.DeleteEmpty && _charges.IsEmpty(uid))
|
||||||
UseUpCrayon(uid, args.User);
|
UseUpCrayon(uid, args.User);
|
||||||
else
|
else
|
||||||
_uiSystem.ServerSendUiMessage(uid, SharedCrayonComponent.CrayonUiKey.Key, new CrayonUsedMessage(component.SelectedState));
|
_uiSystem.ServerSendUiMessage(uid, CrayonUiKey.Key, new CrayonUsedMessage(component.SelectedState));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCrayonUse(EntityUid uid, CrayonComponent component, UseInHandEvent args)
|
private void OnCrayonUse(EntityUid uid, CrayonComponent component, UseInHandEvent args)
|
||||||
@@ -91,14 +93,12 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
|||||||
if (args.Handled)
|
if (args.Handled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_uiSystem.HasUi(uid, SharedCrayonComponent.CrayonUiKey.Key))
|
if (!_uiSystem.HasUi(uid, CrayonUiKey.Key))
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
_uiSystem.TryToggleUi(uid, SharedCrayonComponent.CrayonUiKey.Key, args.User);
|
_uiSystem.TryToggleUi(uid, CrayonUiKey.Key, args.User);
|
||||||
|
|
||||||
_uiSystem.SetUiState(uid, SharedCrayonComponent.CrayonUiKey.Key, new CrayonBoundUserInterfaceState(component.SelectedState, component.SelectableColor, component.Color));
|
_uiSystem.SetUiState(uid, CrayonUiKey.Key, new CrayonBoundUserInterfaceState(component.SelectedState, component.SelectableColor, component.Color));
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,35 +109,23 @@ public sealed class CrayonSystem : SharedCrayonSystem
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
component.SelectedState = args.State;
|
component.SelectedState = args.State;
|
||||||
|
|
||||||
Dirty(uid, component);
|
Dirty(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCrayonBoundUIColor(EntityUid uid, CrayonComponent component, CrayonColorMessage args)
|
private void OnCrayonBoundUIColor(EntityUid uid, CrayonComponent component, CrayonColorMessage args)
|
||||||
{
|
{
|
||||||
// you still need to ensure that the given color is a valid color
|
// Ensure that the given color can be changed or already matches
|
||||||
if (!component.SelectableColor || args.Color == component.Color)
|
if (!component.SelectableColor || args.Color == component.Color)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
component.Color = args.Color;
|
component.Color = args.Color;
|
||||||
Dirty(uid, component);
|
Dirty(uid, component);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCrayonInit(EntityUid uid, CrayonComponent component, ComponentInit args)
|
|
||||||
{
|
|
||||||
component.Charges = component.Capacity;
|
|
||||||
|
|
||||||
// Get the first one from the catalog and set it as default
|
|
||||||
var decal = _prototypeManager.EnumeratePrototypes<DecalPrototype>().FirstOrDefault(x => x.Tags.Contains("crayon"));
|
|
||||||
component.SelectedState = decal?.ID ?? string.Empty;
|
|
||||||
Dirty(uid, component);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCrayonDropped(EntityUid uid, CrayonComponent component, DroppedEvent args)
|
private void OnCrayonDropped(EntityUid uid, CrayonComponent component, DroppedEvent args)
|
||||||
{
|
{
|
||||||
// TODO: Use the existing event.
|
// TODO: Use the existing event.
|
||||||
_uiSystem.CloseUi(uid, SharedCrayonComponent.CrayonUiKey.Key, args.User);
|
_uiSystem.CloseUi(uid, CrayonUiKey.Key, args.User);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UseUpCrayon(EntityUid uid, EntityUid user)
|
private void UseUpCrayon(EntityUid uid, EntityUid user)
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
using Content.Server.Damage.Systems;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
|
|
||||||
namespace Content.Server.Damage.Components
|
|
||||||
{
|
|
||||||
[Access(typeof(DamageOtherOnHitSystem))]
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed partial class DamageOtherOnHitComponent : Component
|
|
||||||
{
|
|
||||||
[DataField("ignoreResistances")]
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public bool IgnoreResistances = false;
|
|
||||||
|
|
||||||
[DataField("damage", required: true)]
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public DamageSpecifier Damage = default!;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +1,54 @@
|
|||||||
using Content.Server.Administration.Logs;
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.Damage.Components;
|
|
||||||
using Content.Server.Weapons.Ranged.Systems;
|
using Content.Server.Weapons.Ranged.Systems;
|
||||||
using Content.Shared.CombatMode.Pacification;
|
|
||||||
using Content.Shared.Camera;
|
using Content.Shared.Camera;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Events;
|
using Content.Shared.Damage.Components;
|
||||||
using Content.Shared.Damage.Systems;
|
using Content.Shared.Damage.Systems;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Effects;
|
using Content.Shared.Effects;
|
||||||
using Content.Shared.Mobs.Components;
|
using Content.Shared.Mobs.Components;
|
||||||
using Content.Shared.Throwing;
|
using Content.Shared.Throwing;
|
||||||
using Content.Shared.Wires;
|
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
|
|
||||||
namespace Content.Server.Damage.Systems
|
namespace Content.Server.Damage.Systems;
|
||||||
|
|
||||||
|
public sealed class DamageOtherOnHitSystem : SharedDamageOtherOnHitSystem
|
||||||
{
|
{
|
||||||
public sealed class DamageOtherOnHitSystem : EntitySystem
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
|
[Dependency] private readonly GunSystem _guns = default!;
|
||||||
|
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||||
|
[Dependency] private readonly SharedCameraRecoilSystem _sharedCameraRecoil = default!;
|
||||||
|
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
base.Initialize();
|
||||||
[Dependency] private readonly GunSystem _guns = default!;
|
|
||||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
|
||||||
[Dependency] private readonly DamageExamineSystem _damageExamine = default!;
|
|
||||||
[Dependency] private readonly SharedCameraRecoilSystem _sharedCameraRecoil = default!;
|
|
||||||
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
SubscribeLocalEvent<DamageOtherOnHitComponent, ThrowDoHitEvent>(OnDoHit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDoHit(EntityUid uid, DamageOtherOnHitComponent component, ThrowDoHitEvent args)
|
||||||
|
{
|
||||||
|
if (TerminatingOrDeleted(args.Target))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var dmg = _damageable.TryChangeDamage(args.Target, component.Damage * _damageable.UniversalThrownDamageModifier, component.IgnoreResistances, origin: args.Component.Thrower);
|
||||||
|
|
||||||
|
// Log damage only for mobs. Useful for when people throw spears at each other, but also avoids log-spam when explosions send glass shards flying.
|
||||||
|
if (dmg != null && HasComp<MobStateComponent>(args.Target))
|
||||||
|
_adminLogger.Add(LogType.ThrowHit, $"{ToPrettyString(args.Target):target} received {dmg.GetTotal():damage} damage from collision");
|
||||||
|
|
||||||
|
if (dmg is { Empty: false })
|
||||||
{
|
{
|
||||||
SubscribeLocalEvent<DamageOtherOnHitComponent, ThrowDoHitEvent>(OnDoHit);
|
_color.RaiseEffect(Color.Red, [args.Target], Filter.Pvs(args.Target, entityManager: EntityManager));
|
||||||
SubscribeLocalEvent<DamageOtherOnHitComponent, DamageExamineEvent>(OnDamageExamine);
|
|
||||||
SubscribeLocalEvent<DamageOtherOnHitComponent, AttemptPacifiedThrowEvent>(OnAttemptPacifiedThrow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDoHit(EntityUid uid, DamageOtherOnHitComponent component, ThrowDoHitEvent args)
|
_guns.PlayImpactSound(args.Target, dmg, null, false);
|
||||||
|
if (TryComp<PhysicsComponent>(uid, out var body) && body.LinearVelocity.LengthSquared() > 0f)
|
||||||
{
|
{
|
||||||
if (TerminatingOrDeleted(args.Target))
|
var direction = body.LinearVelocity.Normalized();
|
||||||
return;
|
_sharedCameraRecoil.KickCamera(args.Target, direction);
|
||||||
|
|
||||||
var dmg = _damageable.TryChangeDamage(args.Target, component.Damage * _damageable.UniversalThrownDamageModifier, component.IgnoreResistances, origin: args.Component.Thrower);
|
|
||||||
|
|
||||||
// Log damage only for mobs. Useful for when people throw spears at each other, but also avoids log-spam when explosions send glass shards flying.
|
|
||||||
if (dmg != null && HasComp<MobStateComponent>(args.Target))
|
|
||||||
_adminLogger.Add(LogType.ThrowHit, $"{ToPrettyString(args.Target):target} received {dmg.GetTotal():damage} damage from collision");
|
|
||||||
|
|
||||||
if (dmg is { Empty: false })
|
|
||||||
{
|
|
||||||
_color.RaiseEffect(Color.Red, new List<EntityUid>() { args.Target }, Filter.Pvs(args.Target, entityManager: EntityManager));
|
|
||||||
}
|
|
||||||
|
|
||||||
_guns.PlayImpactSound(args.Target, dmg, null, false);
|
|
||||||
if (TryComp<PhysicsComponent>(uid, out var body) && body.LinearVelocity.LengthSquared() > 0f)
|
|
||||||
{
|
|
||||||
var direction = body.LinearVelocity.Normalized();
|
|
||||||
_sharedCameraRecoil.KickCamera(args.Target, direction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDamageExamine(EntityUid uid, DamageOtherOnHitComponent component, ref DamageExamineEvent args)
|
|
||||||
{
|
|
||||||
_damageExamine.AddDamageExamine(args.Message, _damageable.ApplyUniversalAllModifiers(component.Damage * _damageable.UniversalThrownDamageModifier), Loc.GetString("damage-throw"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Prevent players with the Pacified status effect from throwing things that deal damage.
|
|
||||||
/// </summary>
|
|
||||||
private void OnAttemptPacifiedThrow(Entity<DamageOtherOnHitComponent> ent, ref AttemptPacifiedThrowEvent args)
|
|
||||||
{
|
|
||||||
args.Cancel("pacified-cannot-throw");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -404,6 +404,9 @@ public sealed class HolopadSystem : SharedHolopadSystem
|
|||||||
if (!this.IsPowered(entity, EntityManager))
|
if (!this.IsPowered(entity, EntityManager))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (HasComp<StationAiCoreComponent>(entity))
|
||||||
|
return;
|
||||||
|
|
||||||
if (!TryComp<TelephoneComponent>(entity, out var entityTelephone) ||
|
if (!TryComp<TelephoneComponent>(entity, out var entityTelephone) ||
|
||||||
_telephoneSystem.IsTelephoneEngaged((entity, entityTelephone)))
|
_telephoneSystem.IsTelephoneEngaged((entity, entityTelephone)))
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using Content.Shared.Emag.Systems;
|
||||||
|
|
||||||
|
namespace Content.Server.NPC.HTN.Preconditions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A precondition which is met if the NPC is emagged with <see cref="EmagType"/>, as computed by
|
||||||
|
/// <see cref="EmagSystem.CheckFlag"/>. This is useful for changing NPC behavior in the case that the NPC is emagged,
|
||||||
|
/// eg. like a helper NPC bot turning evil.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class IsEmaggedPrecondition : HTNPrecondition
|
||||||
|
{
|
||||||
|
private EmagSystem _emag;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The type of emagging to check for.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public EmagType EmagType = EmagType.Interaction;
|
||||||
|
|
||||||
|
public override void Initialize(IEntitySystemManager sysManager)
|
||||||
|
{
|
||||||
|
base.Initialize(sysManager);
|
||||||
|
_emag = sysManager.GetEntitySystem<EmagSystem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool IsMet(NPCBlackboard blackboard)
|
||||||
|
{
|
||||||
|
var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
|
||||||
|
return _emag.CheckFlag(owner, EmagType);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
using Content.Server.NPC.HTN.PrimitiveTasks.Operators.Interactions;
|
||||||
|
using Content.Shared.CombatMode;
|
||||||
|
using Content.Shared.Weapons.Melee;
|
||||||
|
|
||||||
|
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat.Melee;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Something between <see cref="MeleeOperator"/> and <see cref="InteractWithOperator"/>, this operator causes the NPC
|
||||||
|
/// to attempt a SINGLE <see cref="SharedMeleeWeaponSystem.AttemptLightAttack">melee attack</see> on the specified
|
||||||
|
/// <see cref="TargetKey">target</see>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class MeleeAttackOperator : HTNOperator
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
|
private SharedMeleeWeaponSystem _melee;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Key that contains the target entity.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public string TargetKey = default!;
|
||||||
|
|
||||||
|
public override void Initialize(IEntitySystemManager sysManager)
|
||||||
|
{
|
||||||
|
base.Initialize(sysManager);
|
||||||
|
_melee = sysManager.GetEntitySystem<SharedMeleeWeaponSystem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void TaskShutdown(NPCBlackboard blackboard, HTNOperatorStatus status)
|
||||||
|
{
|
||||||
|
base.TaskShutdown(blackboard, status);
|
||||||
|
|
||||||
|
ExitCombatMode(blackboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PlanShutdown(NPCBlackboard blackboard)
|
||||||
|
{
|
||||||
|
base.PlanShutdown(blackboard);
|
||||||
|
|
||||||
|
ExitCombatMode(blackboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
|
||||||
|
{
|
||||||
|
var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
|
||||||
|
|
||||||
|
if (!_entManager.TryGetComponent<CombatModeComponent>(owner, out var combatMode) ||
|
||||||
|
!_melee.TryGetWeapon(owner, out var weaponUid, out var weapon))
|
||||||
|
{
|
||||||
|
return HTNOperatorStatus.Failed;
|
||||||
|
}
|
||||||
|
|
||||||
|
_entManager.System<SharedCombatModeSystem>().SetInCombatMode(owner, true, combatMode);
|
||||||
|
|
||||||
|
|
||||||
|
if (!blackboard.TryGetValue<EntityUid>(TargetKey, out var target, _entManager) ||
|
||||||
|
!_melee.AttemptLightAttack(owner, weaponUid, weapon, target))
|
||||||
|
{
|
||||||
|
return HTNOperatorStatus.Continuing;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HTNOperatorStatus.Finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExitCombatMode(NPCBlackboard blackboard)
|
||||||
|
{
|
||||||
|
var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
|
||||||
|
_entManager.System<SharedCombatModeSystem>().SetInCombatMode(owner, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,21 @@
|
|||||||
using Content.Server.Chat.Systems;
|
using Content.Server.Chat.Systems;
|
||||||
|
using Content.Shared.Dataset;
|
||||||
|
using Content.Shared.Random.Helpers;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
using static Content.Server.NPC.HTN.PrimitiveTasks.Operators.SpeakOperator.SpeakOperatorSpeech;
|
||||||
|
|
||||||
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators;
|
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators;
|
||||||
|
|
||||||
public sealed partial class SpeakOperator : HTNOperator
|
public sealed partial class SpeakOperator : HTNOperator
|
||||||
{
|
{
|
||||||
private ChatSystem _chat = default!;
|
private ChatSystem _chat = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
[DataField(required: true)]
|
[DataField(required: true)]
|
||||||
public string Speech = string.Empty;
|
public SpeakOperatorSpeech Speech;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether to hide message from chat window and logs.
|
/// Whether to hide message from chat window and logs.
|
||||||
@@ -18,15 +26,51 @@ public sealed partial class SpeakOperator : HTNOperator
|
|||||||
public override void Initialize(IEntitySystemManager sysManager)
|
public override void Initialize(IEntitySystemManager sysManager)
|
||||||
{
|
{
|
||||||
base.Initialize(sysManager);
|
base.Initialize(sysManager);
|
||||||
|
|
||||||
_chat = sysManager.GetEntitySystem<ChatSystem>();
|
_chat = sysManager.GetEntitySystem<ChatSystem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
|
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
|
||||||
{
|
{
|
||||||
|
LocId speechLocId;
|
||||||
|
switch (Speech)
|
||||||
|
{
|
||||||
|
case LocalizedSetSpeakOperatorSpeech localizedDataSet:
|
||||||
|
if (!_proto.TryIndex(localizedDataSet.LineSet, out var speechSet))
|
||||||
|
return HTNOperatorStatus.Failed;
|
||||||
|
speechLocId = _random.Pick(speechSet);
|
||||||
|
break;
|
||||||
|
case SingleSpeakOperatorSpeech single:
|
||||||
|
speechLocId = single.Line;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(Speech));
|
||||||
|
}
|
||||||
|
|
||||||
var speaker = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
|
var speaker = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
|
||||||
_chat.TrySendInGameICMessage(speaker, Loc.GetString(Speech), InGameICChatType.Speak, hideChat: Hidden, hideLog: Hidden);
|
_chat.TrySendInGameICMessage(
|
||||||
|
speaker,
|
||||||
|
Loc.GetString(speechLocId),
|
||||||
|
InGameICChatType.Speak,
|
||||||
|
hideChat: Hidden,
|
||||||
|
hideLog: Hidden
|
||||||
|
);
|
||||||
|
|
||||||
return base.Update(blackboard, frameTime);
|
return base.Update(blackboard, frameTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ImplicitDataDefinitionForInheritors, MeansImplicitUse]
|
||||||
|
public abstract partial class SpeakOperatorSpeech
|
||||||
|
{
|
||||||
|
public sealed partial class SingleSpeakOperatorSpeech : SpeakOperatorSpeech
|
||||||
|
{
|
||||||
|
[DataField(required: true)]
|
||||||
|
public string Line;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed partial class LocalizedSetSpeakOperatorSpeech : SpeakOperatorSpeech
|
||||||
|
{
|
||||||
|
[DataField(required: true)]
|
||||||
|
public ProtoId<LocalizedDatasetPrototype> LineSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Specific;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raises an <see cref="HTNRaisedEvent"/> on the <see cref="NPCBlackboard.Owner">owner</see>. The event will contain
|
||||||
|
/// the specified <see cref="Args"/>, and if not null, the value of <see cref="TargetKey"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class RaiseEventForOwnerOperator : HTNOperator
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The conceptual "target" of this event. Note that this is NOT the entity for which the event is raised. If null,
|
||||||
|
/// <see cref="HTNRaisedEvent.Target"/> will be null.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public string? TargetKey;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The data contained in the raised event. Since <see cref="HTNRaisedEvent"/> is itself pretty meaningless, this is
|
||||||
|
/// included to give some context of what the event is actually supposed to mean.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public EntityEventArgs Args;
|
||||||
|
|
||||||
|
public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime)
|
||||||
|
{
|
||||||
|
_entMan.EventBus.RaiseLocalEvent(
|
||||||
|
blackboard.GetValue<EntityUid>(NPCBlackboard.Owner),
|
||||||
|
new HTNRaisedEvent(
|
||||||
|
blackboard.GetValue<EntityUid>(NPCBlackboard.Owner),
|
||||||
|
TargetKey is { } targetKey ? blackboard.GetValue<EntityUid>(targetKey) : null,
|
||||||
|
Args
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return HTNOperatorStatus.Finished;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed partial class HTNRaisedEvent(EntityUid owner, EntityUid? target, EntityEventArgs args) : EntityEventArgs
|
||||||
|
{
|
||||||
|
// Owner and target are both included here in case we want to add a "RaiseEventForTargetOperator" in the future
|
||||||
|
// while reusing this event.
|
||||||
|
public EntityUid Owner = owner;
|
||||||
|
public EntityUid? Target = target;
|
||||||
|
public EntityEventArgs Args = args;
|
||||||
|
}
|
||||||
@@ -9,4 +9,11 @@ public sealed partial class ComponentFilter : UtilityQueryFilter
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("components", required: true)]
|
[DataField("components", required: true)]
|
||||||
public ComponentRegistry Components = new();
|
public ComponentRegistry Components = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, this filter retains entities with ALL of the specified components. If false, this filter removes
|
||||||
|
/// entities with ANY of the specified components.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool RetainWithComp = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -512,11 +512,12 @@ public sealed class NPCUtilitySystem : EntitySystem
|
|||||||
{
|
{
|
||||||
foreach (var comp in compFilter.Components)
|
foreach (var comp in compFilter.Components)
|
||||||
{
|
{
|
||||||
if (HasComp(ent, comp.Value.Component.GetType()))
|
var hasComp = HasComp(ent, comp.Value.Component.GetType());
|
||||||
continue;
|
if (!compFilter.RetainWithComp == hasComp)
|
||||||
|
{
|
||||||
_entityList.Add(ent);
|
_entityList.Add(ent);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Content.Server.Ninja.Systems;
|
using Content.Server.Ninja.Systems;
|
||||||
using Content.Server.Objectives.Systems;
|
using Content.Server.Objectives.Systems;
|
||||||
|
using Content.Shared.Whitelist;
|
||||||
|
|
||||||
namespace Content.Server.Objectives.Components;
|
namespace Content.Server.Objectives.Components;
|
||||||
|
|
||||||
@@ -14,4 +15,11 @@ public sealed partial class SpiderChargeConditionComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
public EntityUid? Target;
|
public EntityUid? Target;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tags that should be used to exclude Warp Points
|
||||||
|
/// from the list of valid bombing targets
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public EntityWhitelist? Blacklist;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using Content.Server.Objectives.Components;
|
using Content.Server.Objectives.Components;
|
||||||
using Content.Shared.Objectives.Components;
|
using Content.Shared.Objectives.Components;
|
||||||
using Content.Shared.Ninja.Components;
|
|
||||||
using Content.Shared.Roles;
|
using Content.Shared.Roles;
|
||||||
using Content.Shared.Roles.Components;
|
using Content.Shared.Roles.Components;
|
||||||
using Content.Shared.Warps;
|
using Content.Shared.Warps;
|
||||||
|
using Content.Shared.Whitelist;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
namespace Content.Server.Objectives.Systems;
|
namespace Content.Server.Objectives.Systems;
|
||||||
@@ -14,6 +14,7 @@ namespace Content.Server.Objectives.Systems;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class NinjaConditionsSystem : EntitySystem
|
public sealed class NinjaConditionsSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||||
[Dependency] private readonly NumberObjectiveSystem _number = default!;
|
[Dependency] private readonly NumberObjectiveSystem _number = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
@@ -53,10 +54,13 @@ public sealed class NinjaConditionsSystem : EntitySystem
|
|||||||
|
|
||||||
// choose spider charge detonation point
|
// choose spider charge detonation point
|
||||||
var warps = new List<EntityUid>();
|
var warps = new List<EntityUid>();
|
||||||
var query = EntityQueryEnumerator<BombingTargetComponent, WarpPointComponent>();
|
var allEnts = EntityQueryEnumerator<WarpPointComponent>();
|
||||||
while (query.MoveNext(out var warpUid, out _, out var warp))
|
var bombingBlacklist = comp.Blacklist;
|
||||||
|
|
||||||
|
while (allEnts.MoveNext(out var warpUid, out var warp))
|
||||||
{
|
{
|
||||||
if (warp.Location != null)
|
if (_whitelist.IsBlacklistFail(bombingBlacklist, warpUid)
|
||||||
|
&& !string.IsNullOrWhiteSpace(warp.Location))
|
||||||
{
|
{
|
||||||
warps.Add(warpUid);
|
warps.Add(warpUid);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,27 +2,22 @@ using Content.Server.Ghost.Roles;
|
|||||||
using Content.Server.Ghost.Roles.Components;
|
using Content.Server.Ghost.Roles.Components;
|
||||||
using Content.Server.Instruments;
|
using Content.Server.Instruments;
|
||||||
using Content.Server.Kitchen.Components;
|
using Content.Server.Kitchen.Components;
|
||||||
using Content.Server.Store.Systems;
|
|
||||||
using Content.Shared.Interaction.Events;
|
using Content.Shared.Interaction.Events;
|
||||||
using Content.Shared.Mind.Components;
|
using Content.Shared.Mind.Components;
|
||||||
using Content.Shared.PAI;
|
using Content.Shared.PAI;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Store;
|
|
||||||
using Content.Shared.Store.Components;
|
|
||||||
using Content.Shared.Instruments;
|
using Content.Shared.Instruments;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Content.Server.PAI;
|
namespace Content.Server.PAI;
|
||||||
|
|
||||||
public sealed class PAISystem : SharedPAISystem
|
public sealed class PAISystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly InstrumentSystem _instrumentSystem = default!;
|
[Dependency] private readonly InstrumentSystem _instrumentSystem = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
[Dependency] private readonly MetaDataSystem _metaData = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
[Dependency] private readonly StoreSystem _store = default!;
|
|
||||||
[Dependency] private readonly ToggleableGhostRoleSystem _toggleableGhostRole = default!;
|
[Dependency] private readonly ToggleableGhostRoleSystem _toggleableGhostRole = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -38,8 +33,6 @@ public sealed class PAISystem : SharedPAISystem
|
|||||||
SubscribeLocalEvent<PAIComponent, MindAddedMessage>(OnMindAdded);
|
SubscribeLocalEvent<PAIComponent, MindAddedMessage>(OnMindAdded);
|
||||||
SubscribeLocalEvent<PAIComponent, MindRemovedMessage>(OnMindRemoved);
|
SubscribeLocalEvent<PAIComponent, MindRemovedMessage>(OnMindRemoved);
|
||||||
SubscribeLocalEvent<PAIComponent, BeingMicrowavedEvent>(OnMicrowaved);
|
SubscribeLocalEvent<PAIComponent, BeingMicrowavedEvent>(OnMicrowaved);
|
||||||
|
|
||||||
SubscribeLocalEvent<PAIComponent, PAIShopActionEvent>(OnShop);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUseInHand(EntityUid uid, PAIComponent component, UseInHandEvent args)
|
private void OnUseInHand(EntityUid uid, PAIComponent component, UseInHandEvent args)
|
||||||
@@ -106,15 +99,6 @@ public sealed class PAISystem : SharedPAISystem
|
|||||||
var val = Loc.GetString("pai-system-pai-name-raw", ("name", name.ToString()));
|
var val = Loc.GetString("pai-system-pai-name-raw", ("name", name.ToString()));
|
||||||
_metaData.SetEntityName(uid, val);
|
_metaData.SetEntityName(uid, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnShop(Entity<PAIComponent> ent, ref PAIShopActionEvent args)
|
|
||||||
{
|
|
||||||
if (!TryComp<StoreComponent>(ent, out var store))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_store.ToggleUi(args.Performer, ent, store);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PAITurningOff(EntityUid uid)
|
public void PAITurningOff(EntityUid uid)
|
||||||
{
|
{
|
||||||
// Close the instrument interface if it was open
|
// Close the instrument interface if it was open
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ namespace Content.Server.Power.EntitySystems
|
|||||||
if (!Resolve(uid, ref battery))
|
if (!Resolve(uid, ref battery))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
var newValue = Math.Clamp(0, battery.CurrentCharge + value, battery.MaxCharge);
|
var newValue = Math.Clamp(battery.CurrentCharge + value, 0, battery.MaxCharge);
|
||||||
var delta = newValue - battery.CurrentCharge;
|
var delta = newValue - battery.CurrentCharge;
|
||||||
battery.CurrentCharge = newValue;
|
battery.CurrentCharge = newValue;
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ namespace Content.Server.PowerSink
|
|||||||
if (!transform.Anchored)
|
if (!transform.Anchored)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
_battery.SetCharge(entity, battery.CurrentCharge + networkLoad.NetworkLoad.ReceivingPower / 1000, battery);
|
_battery.ChangeCharge(entity, networkLoad.NetworkLoad.ReceivingPower * frameTime, battery);
|
||||||
|
|
||||||
var currentBatteryThreshold = battery.CurrentCharge / battery.MaxCharge;
|
var currentBatteryThreshold = battery.CurrentCharge / battery.MaxCharge;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Server.Actions;
|
using Content.Server.Actions;
|
||||||
using Content.Server.GameTicking;
|
using Content.Server.GameTicking;
|
||||||
using Content.Server.Store.Components;
|
|
||||||
using Content.Server.Store.Systems;
|
using Content.Server.Store.Systems;
|
||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
@@ -21,7 +20,6 @@ using Content.Shared.Store.Components;
|
|||||||
using Content.Shared.Stunnable;
|
using Content.Shared.Stunnable;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
namespace Content.Server.Revenant.EntitySystems;
|
namespace Content.Server.Revenant.EntitySystems;
|
||||||
@@ -46,17 +44,12 @@ public sealed partial class RevenantSystem : EntitySystem
|
|||||||
[Dependency] private readonly TagSystem _tag = default!;
|
[Dependency] private readonly TagSystem _tag = default!;
|
||||||
[Dependency] private readonly VisibilitySystem _visibility = default!;
|
[Dependency] private readonly VisibilitySystem _visibility = default!;
|
||||||
[Dependency] private readonly TurfSystem _turf = default!;
|
[Dependency] private readonly TurfSystem _turf = default!;
|
||||||
|
|
||||||
private static readonly EntProtoId RevenantShopId = "ActionRevenantShop";
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<RevenantComponent, ComponentStartup>(OnStartup);
|
SubscribeLocalEvent<RevenantComponent, ComponentStartup>(OnStartup);
|
||||||
SubscribeLocalEvent<RevenantComponent, MapInitEvent>(OnMapInit);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<RevenantComponent, RevenantShopActionEvent>(OnShop);
|
|
||||||
SubscribeLocalEvent<RevenantComponent, DamageChangedEvent>(OnDamage);
|
SubscribeLocalEvent<RevenantComponent, DamageChangedEvent>(OnDamage);
|
||||||
SubscribeLocalEvent<RevenantComponent, ExaminedEvent>(OnExamine);
|
SubscribeLocalEvent<RevenantComponent, ExaminedEvent>(OnExamine);
|
||||||
SubscribeLocalEvent<RevenantComponent, StatusEffectAddedEvent>(OnStatusAdded);
|
SubscribeLocalEvent<RevenantComponent, StatusEffectAddedEvent>(OnStatusAdded);
|
||||||
@@ -94,11 +87,6 @@ public sealed partial class RevenantSystem : EntitySystem
|
|||||||
_eye.RefreshVisibilityMask(uid);
|
_eye.RefreshVisibilityMask(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMapInit(EntityUid uid, RevenantComponent component, MapInitEvent args)
|
|
||||||
{
|
|
||||||
_action.AddAction(uid, ref component.Action, RevenantShopId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnStatusAdded(EntityUid uid, RevenantComponent component, StatusEffectAddedEvent args)
|
private void OnStatusAdded(EntityUid uid, RevenantComponent component, StatusEffectAddedEvent args)
|
||||||
{
|
{
|
||||||
if (args.Key == "Stun")
|
if (args.Key == "Stun")
|
||||||
@@ -182,13 +170,6 @@ public sealed partial class RevenantSystem : EntitySystem
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnShop(EntityUid uid, RevenantComponent component, RevenantShopActionEvent args)
|
|
||||||
{
|
|
||||||
if (!TryComp<StoreComponent>(uid, out var store))
|
|
||||||
return;
|
|
||||||
_store.ToggleUi(uid, uid, store);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MakeVisible(bool visible)
|
public void MakeVisible(bool visible)
|
||||||
{
|
{
|
||||||
var query = EntityQueryEnumerator<RevenantComponent, VisibilityComponent>();
|
var query = EntityQueryEnumerator<RevenantComponent, VisibilityComponent>();
|
||||||
|
|||||||
65
Content.Server/Silicons/Bots/HugBotSystem.cs
Normal file
65
Content.Server/Silicons/Bots/HugBotSystem.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using Content.Server.NPC.HTN.PrimitiveTasks.Operators.Specific;
|
||||||
|
using Content.Shared.Silicons.Bots;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Server.Silicons.Bots;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Beyond what <see cref="SharedHugBotSystem"/> does, this system manages the "lifecycle" of
|
||||||
|
/// <see cref="RecentlyHuggedByHugBotComponent"/>.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class HugBotSystem : SharedHugBotSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<HugBotComponent, HTNRaisedEvent>(OnHtnRaisedEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHtnRaisedEvent(Entity<HugBotComponent> entity, ref HTNRaisedEvent args)
|
||||||
|
{
|
||||||
|
if (args.Args is not HugBotDidHugEvent ||
|
||||||
|
args.Target is not {} target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ev = new HugBotHugEvent(GetNetEntity(entity));
|
||||||
|
RaiseLocalEvent(target, ev);
|
||||||
|
|
||||||
|
ApplyHugBotCooldown(entity, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies <see cref="RecentlyHuggedByHugBotComponent"/> to <paramref name="target"/> based on the configuration of
|
||||||
|
/// <paramref name="hugBot"/>.
|
||||||
|
/// </summary>
|
||||||
|
public void ApplyHugBotCooldown(Entity<HugBotComponent> hugBot, EntityUid target)
|
||||||
|
{
|
||||||
|
var hugged = EnsureComp<RecentlyHuggedByHugBotComponent>(target);
|
||||||
|
hugged.CooldownCompleteAfter = _gameTiming.CurTime + hugBot.Comp.HugCooldown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
// Iterate through all RecentlyHuggedByHugBot entities...
|
||||||
|
var huggedEntities = AllEntityQuery<RecentlyHuggedByHugBotComponent>();
|
||||||
|
while (huggedEntities.MoveNext(out var huggedEnt, out var huggedComp))
|
||||||
|
{
|
||||||
|
// ... and if their cooldown is complete...
|
||||||
|
if (huggedComp.CooldownCompleteAfter <= _gameTiming.CurTime)
|
||||||
|
{
|
||||||
|
// ... remove it, allowing them to receive the blessing of hugs once more.
|
||||||
|
RemCompDeferred<RecentlyHuggedByHugBotComponent>(huggedEnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This event is indirectly raised (by being <see cref="HTNRaisedEvent.Args"/>) on a HugBot when it hugs (or emaggedly
|
||||||
|
/// punches) an entity.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, DataDefinition]
|
||||||
|
public sealed partial class HugBotDidHugEvent : EntityEventArgs;
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using Content.Shared.Silicons.Bots;
|
||||||
|
|
||||||
|
namespace Content.Server.Silicons.Bots;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This marker component indicates that its entity has been recently hugged by a HugBot and should not be hugged again
|
||||||
|
/// before <see cref="CooldownCompleteAfter">a cooldown period</see> in order to prevent hug spam.
|
||||||
|
/// </summary>
|
||||||
|
/// <see cref="SharedHugBotSystem"/>
|
||||||
|
[RegisterComponent, AutoGenerateComponentPause]
|
||||||
|
public sealed partial class RecentlyHuggedByHugBotComponent : Component
|
||||||
|
{
|
||||||
|
[DataField, AutoPausedField]
|
||||||
|
public TimeSpan CooldownCompleteAfter = TimeSpan.MinValue;
|
||||||
|
}
|
||||||
@@ -176,9 +176,10 @@ public sealed class SingularityGeneratorSystem : SharedSingularityGeneratorSyste
|
|||||||
|
|
||||||
foreach (var result in rayCastResults)
|
foreach (var result in rayCastResults)
|
||||||
{
|
{
|
||||||
if (genQuery.HasComponent(result.HitEntity))
|
if (!genQuery.HasComponent(result.HitEntity))
|
||||||
closestResult = result;
|
continue;
|
||||||
|
|
||||||
|
closestResult = result;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
|
using System.Linq;
|
||||||
using Content.Server.Store.Components;
|
using Content.Server.Store.Components;
|
||||||
using Content.Shared.UserInterface;
|
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Implants.Components;
|
using Content.Shared.Implants.Components;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Stacks;
|
using Content.Shared.Stacks;
|
||||||
using Content.Shared.Store.Components;
|
using Content.Shared.Store.Components;
|
||||||
using JetBrains.Annotations;
|
using Content.Shared.Store.Events;
|
||||||
|
using Content.Shared.UserInterface;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Utility;
|
|
||||||
using System.Linq;
|
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Content.Shared.Mind;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Store.Systems;
|
namespace Content.Server.Store.Systems;
|
||||||
|
|
||||||
@@ -37,6 +36,7 @@ public sealed partial class StoreSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<StoreComponent, ComponentStartup>(OnStartup);
|
SubscribeLocalEvent<StoreComponent, ComponentStartup>(OnStartup);
|
||||||
SubscribeLocalEvent<StoreComponent, ComponentShutdown>(OnShutdown);
|
SubscribeLocalEvent<StoreComponent, ComponentShutdown>(OnShutdown);
|
||||||
SubscribeLocalEvent<StoreComponent, OpenUplinkImplantEvent>(OnImplantActivate);
|
SubscribeLocalEvent<StoreComponent, OpenUplinkImplantEvent>(OnImplantActivate);
|
||||||
|
SubscribeLocalEvent<StoreComponent, IntrinsicStoreActionEvent>(OnIntrinsicStoreAction);
|
||||||
|
|
||||||
InitializeUi();
|
InitializeUi();
|
||||||
InitializeCommand();
|
InitializeCommand();
|
||||||
@@ -187,6 +187,12 @@ public sealed partial class StoreSystem : EntitySystem
|
|||||||
UpdateUserInterface(null, uid, store);
|
UpdateUserInterface(null, uid, store);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnIntrinsicStoreAction(Entity<StoreComponent> ent, ref IntrinsicStoreActionEvent args)
|
||||||
|
{
|
||||||
|
ToggleUi(args.Performer, ent.Owner, ent.Comp);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class CurrencyInsertAttemptEvent : CancellableEntityEventArgs
|
public sealed class CurrencyInsertAttemptEvent : CancellableEntityEventArgs
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
using Content.Server.Chat.Systems;
|
using Content.Server.Chat.Systems;
|
||||||
using Content.Server.Movement.Systems;
|
using Content.Server.Movement.Systems;
|
||||||
using Content.Shared.Damage.Events;
|
|
||||||
using Content.Shared.Damage.Systems;
|
|
||||||
using Content.Shared.Effects;
|
using Content.Shared.Effects;
|
||||||
using Content.Shared.Speech.Components;
|
using Content.Shared.Speech.Components;
|
||||||
using Content.Shared.Weapons.Melee;
|
using Content.Shared.Weapons.Melee;
|
||||||
@@ -16,28 +14,14 @@ namespace Content.Server.Weapons.Melee;
|
|||||||
public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
public sealed class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly ChatSystem _chat = default!;
|
[Dependency] private readonly ChatSystem _chat = default!;
|
||||||
[Dependency] private readonly DamageExamineSystem _damageExamine = default!;
|
|
||||||
[Dependency] private readonly LagCompensationSystem _lag = default!;
|
[Dependency] private readonly LagCompensationSystem _lag = default!;
|
||||||
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
|
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<MeleeSpeechComponent, MeleeHitEvent>(OnSpeechHit);
|
SubscribeLocalEvent<MeleeSpeechComponent, MeleeHitEvent>(OnSpeechHit);
|
||||||
SubscribeLocalEvent<MeleeWeaponComponent, DamageExamineEvent>(OnMeleeExamineDamage);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnMeleeExamineDamage(EntityUid uid, MeleeWeaponComponent component, ref DamageExamineEvent args)
|
|
||||||
{
|
|
||||||
if (component.Hidden)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var damageSpec = GetDamage(uid, args.User, component);
|
|
||||||
|
|
||||||
if (damageSpec.Empty)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_damageExamine.AddDamageExamine(args.Message, Damageable.ApplyUniversalAllModifiers(damageSpec), Loc.GetString("damage-melee"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool ArcRaySuccessful(EntityUid targetUid,
|
protected override bool ArcRaySuccessful(EntityUid targetUid,
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.Damage.Events;
|
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.PowerCell.Components;
|
using Content.Shared.PowerCell.Components;
|
||||||
using Content.Shared.Projectiles;
|
|
||||||
using Content.Shared.Weapons.Ranged;
|
|
||||||
using Content.Shared.Weapons.Ranged.Components;
|
using Content.Shared.Weapons.Ranged.Components;
|
||||||
using Content.Shared.Weapons.Ranged.Events;
|
using Content.Shared.Weapons.Ranged.Events;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Server.Weapons.Ranged.Systems;
|
namespace Content.Server.Weapons.Ranged.Systems;
|
||||||
|
|
||||||
@@ -19,13 +14,11 @@ public sealed partial class GunSystem
|
|||||||
// Hitscan
|
// Hitscan
|
||||||
SubscribeLocalEvent<HitscanBatteryAmmoProviderComponent, ComponentStartup>(OnBatteryStartup);
|
SubscribeLocalEvent<HitscanBatteryAmmoProviderComponent, ComponentStartup>(OnBatteryStartup);
|
||||||
SubscribeLocalEvent<HitscanBatteryAmmoProviderComponent, ChargeChangedEvent>(OnBatteryChargeChange);
|
SubscribeLocalEvent<HitscanBatteryAmmoProviderComponent, ChargeChangedEvent>(OnBatteryChargeChange);
|
||||||
SubscribeLocalEvent<HitscanBatteryAmmoProviderComponent, DamageExamineEvent>(OnBatteryDamageExamine);
|
|
||||||
SubscribeLocalEvent<HitscanBatteryAmmoProviderComponent, PowerCellChangedEvent>(OnPowerCellChanged);
|
SubscribeLocalEvent<HitscanBatteryAmmoProviderComponent, PowerCellChangedEvent>(OnPowerCellChanged);
|
||||||
|
|
||||||
// Projectile
|
// Projectile
|
||||||
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, ComponentStartup>(OnBatteryStartup);
|
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, ComponentStartup>(OnBatteryStartup);
|
||||||
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, ChargeChangedEvent>(OnBatteryChargeChange);
|
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, ChargeChangedEvent>(OnBatteryChargeChange);
|
||||||
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, DamageExamineEvent>(OnBatteryDamageExamine);
|
|
||||||
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, PowerCellChangedEvent>(OnPowerCellChanged);
|
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, PowerCellChangedEvent>(OnPowerCellChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,50 +66,6 @@ public sealed partial class GunSystem
|
|||||||
RaiseLocalEvent(uid, ref updateAmmoEv);
|
RaiseLocalEvent(uid, ref updateAmmoEv);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBatteryDamageExamine<T>(Entity<T> entity, ref DamageExamineEvent args) where T : BatteryAmmoProviderComponent
|
|
||||||
{
|
|
||||||
var damageSpec = GetDamage(entity.Comp);
|
|
||||||
|
|
||||||
if (damageSpec == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var damageType = entity.Comp switch
|
|
||||||
{
|
|
||||||
HitscanBatteryAmmoProviderComponent => Loc.GetString("damage-hitscan"),
|
|
||||||
ProjectileBatteryAmmoProviderComponent => Loc.GetString("damage-projectile"),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(),
|
|
||||||
};
|
|
||||||
|
|
||||||
_damageExamine.AddDamageExamine(args.Message, Damageable.ApplyUniversalAllModifiers(damageSpec), damageType);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DamageSpecifier? GetDamage(BatteryAmmoProviderComponent component)
|
|
||||||
{
|
|
||||||
if (component is ProjectileBatteryAmmoProviderComponent battery)
|
|
||||||
{
|
|
||||||
if (ProtoManager.Index<EntityPrototype>(battery.Prototype).Components
|
|
||||||
.TryGetValue(Factory.GetComponentName<ProjectileComponent>(), out var projectile))
|
|
||||||
{
|
|
||||||
var p = (ProjectileComponent) projectile.Component;
|
|
||||||
|
|
||||||
if (!p.Damage.Empty)
|
|
||||||
{
|
|
||||||
return p.Damage * Damageable.UniversalProjectileDamageModifier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component is HitscanBatteryAmmoProviderComponent hitscan)
|
|
||||||
{
|
|
||||||
var dmg = ProtoManager.Index<HitscanPrototype>(hitscan.Prototype).Damage;
|
|
||||||
return dmg == null ? dmg : dmg * Damageable.UniversalHitscanDamageModifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void TakeCharge(Entity<BatteryAmmoProviderComponent> entity)
|
protected override void TakeCharge(Entity<BatteryAmmoProviderComponent> entity)
|
||||||
{
|
{
|
||||||
var ev = new ChangeChargeEvent(-entity.Comp.FireCost);
|
var ev = new ChangeChargeEvent(-entity.Comp.FireCost);
|
||||||
|
|||||||
@@ -1,87 +1,25 @@
|
|||||||
using Content.Server.Body.Systems;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.Popups;
|
|
||||||
using Content.Server.Power.EntitySystems;
|
|
||||||
using Content.Server.Stack;
|
using Content.Server.Stack;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.Power;
|
|
||||||
using Content.Shared.Storage.Components;
|
using Content.Shared.Storage.Components;
|
||||||
using Content.Shared.Verbs;
|
|
||||||
using Content.Shared.Whitelist;
|
using Content.Shared.Whitelist;
|
||||||
using Content.Shared.Xenoarchaeology.Equipment;
|
using Content.Shared.Xenoarchaeology.Equipment;
|
||||||
using Content.Shared.Xenoarchaeology.Equipment.Components;
|
using Content.Shared.Xenoarchaeology.Equipment.Components;
|
||||||
using Robust.Shared.Collections;
|
using Robust.Shared.Collections;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Timing;
|
|
||||||
|
|
||||||
namespace Content.Server.Xenoarchaeology.Equipment.Systems;
|
namespace Content.Server.Xenoarchaeology.Equipment.Systems;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public sealed class ArtifactCrusherSystem : SharedArtifactCrusherSystem
|
public sealed class ArtifactCrusherSystem : SharedArtifactCrusherSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly BodySystem _body = default!;
|
[Dependency] private readonly BodySystem _body = default!;
|
||||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
|
||||||
[Dependency] private readonly StackSystem _stack = default!;
|
[Dependency] private readonly StackSystem _stack = default!;
|
||||||
[Dependency] private readonly PopupSystem _popup = default!;
|
|
||||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
// TODO: Move to shared once StackSystem spawning is in Shared and we have RandomPredicted
|
||||||
public override void Initialize()
|
public override void FinishCrushing(Entity<ArtifactCrusherComponent, EntityStorageComponent> ent)
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<ArtifactCrusherComponent, GetVerbsEvent<AlternativeVerb>>(OnGetVerbs);
|
|
||||||
SubscribeLocalEvent<ArtifactCrusherComponent, PowerChangedEvent>(OnPowerChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGetVerbs(Entity<ArtifactCrusherComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
|
||||||
{
|
|
||||||
if (!args.CanAccess || !args.CanInteract || args.Hands == null || ent.Comp.Crushing)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!TryComp<EntityStorageComponent>(ent, out var entityStorageComp) ||
|
|
||||||
entityStorageComp.Contents.ContainedEntities.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!this.IsPowered(ent, EntityManager))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var verb = new AlternativeVerb
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("artifact-crusher-verb-start-crushing"),
|
|
||||||
Priority = 2,
|
|
||||||
Act = () => StartCrushing((ent, ent.Comp, entityStorageComp))
|
|
||||||
};
|
|
||||||
args.Verbs.Add(verb);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPowerChanged(Entity<ArtifactCrusherComponent> ent, ref PowerChangedEvent args)
|
|
||||||
{
|
|
||||||
if (!args.Powered)
|
|
||||||
StopCrushing(ent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void StartCrushing(Entity<ArtifactCrusherComponent, EntityStorageComponent> ent)
|
|
||||||
{
|
|
||||||
var (uid, crusher, _) = ent;
|
|
||||||
|
|
||||||
if (crusher.Crushing)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (crusher.AutoLock)
|
|
||||||
_popup.PopupEntity(Loc.GetString("artifact-crusher-autolocks-enable"), uid);
|
|
||||||
|
|
||||||
crusher.Crushing = true;
|
|
||||||
crusher.NextSecond = _timing.CurTime + TimeSpan.FromSeconds(1);
|
|
||||||
crusher.CrushEndTime = _timing.CurTime + crusher.CrushDuration;
|
|
||||||
crusher.CrushingSoundEntity = AudioSystem.PlayPvs(crusher.CrushingSound, ent);
|
|
||||||
Appearance.SetData(ent, ArtifactCrusherVisuals.Crushing, true);
|
|
||||||
Dirty(ent, ent.Comp1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FinishCrushing(Entity<ArtifactCrusherComponent, EntityStorageComponent> ent)
|
|
||||||
{
|
{
|
||||||
var (_, crusher, storage) = ent;
|
var (_, crusher, storage) = ent;
|
||||||
StopCrushing((ent, ent.Comp1), false);
|
StopCrushing((ent, ent.Comp1), false);
|
||||||
@@ -113,32 +51,4 @@ public sealed class ArtifactCrusherSystem : SharedArtifactCrusherSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
|
||||||
{
|
|
||||||
base.Update(frameTime);
|
|
||||||
|
|
||||||
var query = EntityQueryEnumerator<ArtifactCrusherComponent, EntityStorageComponent>();
|
|
||||||
while (query.MoveNext(out var uid, out var crusher, out var storage))
|
|
||||||
{
|
|
||||||
if (!crusher.Crushing)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (crusher.NextSecond < _timing.CurTime)
|
|
||||||
{
|
|
||||||
var contents = new ValueList<EntityUid>(storage.Contents.ContainedEntities);
|
|
||||||
foreach (var contained in contents)
|
|
||||||
{
|
|
||||||
_damageable.TryChangeDamage(contained, crusher.CrushingDamage);
|
|
||||||
}
|
|
||||||
crusher.NextSecond += TimeSpan.FromSeconds(1);
|
|
||||||
Dirty(uid, crusher);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (crusher.CrushEndTime < _timing.CurTime)
|
|
||||||
{
|
|
||||||
FinishCrushing((uid, crusher, storage));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,30 +14,44 @@ public sealed partial class AutoEmotePrototype : IPrototype
|
|||||||
/// The ID of the emote prototype.
|
/// The ID of the emote prototype.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("emote", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EmotePrototype>))]
|
[DataField("emote", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EmotePrototype>))]
|
||||||
public string EmoteId = String.Empty;
|
public string EmoteId = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How often an attempt at the emote will be made.
|
/// How often an attempt at the emote will be made.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("interval", required: true)]
|
[DataField(required: true)]
|
||||||
public TimeSpan Interval;
|
public TimeSpan Interval;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Probability of performing the emote each interval.
|
/// Probability of performing the emote each interval.
|
||||||
/// <summary>
|
/// </summary>
|
||||||
[DataField("chance")]
|
[DataField("chance")]
|
||||||
public float Chance = 1;
|
public float Chance = 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Also send the emote in chat.
|
/// Also send the emote in chat.
|
||||||
/// <summary>
|
/// </summary>
|
||||||
[DataField("withChat")]
|
[DataField]
|
||||||
public bool WithChat = true;
|
public bool WithChat = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should we ignore action blockers?
|
||||||
|
/// This does nothing if WithChat is false.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool IgnoreActionBlocker;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should we ignore whitelists and force the emote?
|
||||||
|
/// This does nothing if WithChat is false.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool Force;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Hide the chat message from the chat window, only showing the popup.
|
/// Hide the chat message from the chat window, only showing the popup.
|
||||||
/// This does nothing if WithChat is false.
|
/// This does nothing if WithChat is false.
|
||||||
/// <summary>
|
/// </summary>
|
||||||
[DataField("hiddenFromChatWindow")]
|
[DataField]
|
||||||
public bool HiddenFromChatWindow = false;
|
public bool HiddenFromChatWindow;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,27 +62,7 @@ public sealed class MaskSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnGotUnequipped(EntityUid uid, MaskComponent mask, GotUnequippedEvent args)
|
private void OnGotUnequipped(EntityUid uid, MaskComponent mask, GotUnequippedEvent args)
|
||||||
{
|
{
|
||||||
if (!mask.IsToggled || !mask.IsToggleable)
|
SetToggled(uid, false);
|
||||||
return;
|
|
||||||
|
|
||||||
mask.IsToggled = false;
|
|
||||||
ToggleMaskComponents(uid, mask, args.Equipee, mask.EquippedPrefix, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called after setting IsToggled, raises events and dirties.
|
|
||||||
/// </summary>
|
|
||||||
private void ToggleMaskComponents(EntityUid uid, MaskComponent mask, EntityUid wearer, string? equippedPrefix = null, bool isEquip = false)
|
|
||||||
{
|
|
||||||
Dirty(uid, mask);
|
|
||||||
if (mask.ToggleActionEntity is { } action)
|
|
||||||
_actionSystem.SetToggled(action, mask.IsToggled);
|
|
||||||
|
|
||||||
var maskEv = new ItemMaskToggledEvent((uid, mask), wearer);
|
|
||||||
RaiseLocalEvent(uid, ref maskEv);
|
|
||||||
|
|
||||||
var wearerEv = new WearerMaskToggledEvent((uid, mask));
|
|
||||||
RaiseLocalEvent(wearer, ref wearerEv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFolded(Entity<MaskComponent> ent, ref FoldedEvent args)
|
private void OnFolded(Entity<MaskComponent> ent, ref FoldedEvent args)
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Content.Shared.Chat.Prototypes;
|
using Content.Shared.Chat.Prototypes;
|
||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Roles;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
|
|
||||||
namespace Content.Shared.Cluwne;
|
namespace Content.Shared.Cluwne;
|
||||||
@@ -12,22 +15,74 @@ public sealed partial class CluwneComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// timings for giggles and knocks.
|
/// timings for giggles and knocks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public TimeSpan DamageGiggleCooldown = TimeSpan.FromSeconds(2);
|
public TimeSpan DamageGiggleCooldown = TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
/// <summary>
|
||||||
|
/// Amount of genetic damage dealt when they revert
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public DamageSpecifier RevertDamage = new()
|
||||||
|
{
|
||||||
|
DamageDict = new()
|
||||||
|
{
|
||||||
|
{ "Genetic", 300.0 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Chance that the Cluwne will be knocked over and paralyzed.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
public float KnockChance = 0.05f;
|
public float KnockChance = 0.05f;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
/// <summary>
|
||||||
|
/// Chance that the Cluwne will randomly giggle
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
public float GiggleRandomChance = 0.1f;
|
public float GiggleRandomChance = 0.1f;
|
||||||
|
|
||||||
[DataField("emoteId", customTypeSerializer: typeof(PrototypeIdSerializer<EmoteSoundsPrototype>))]
|
/// <summary>
|
||||||
public string? EmoteSoundsId = "Cluwne";
|
/// Enable random emoting?
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool RandomEmote = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Emote sound collection that the Cluwne should use.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("emoteId")]
|
||||||
|
public ProtoId<EmoteSoundsPrototype>? EmoteSoundsId = "Cluwne";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Emote to use for the Cluwne Giggling
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public ProtoId<AutoEmotePrototype>? AutoEmoteId = "CluwneGiggle";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Message to popup when the Cluwne is transformed
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public LocId TransformMessage = "cluwne-transform";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name prefix for the Cluwne.
|
||||||
|
/// Example "Urist McHuman" will be "Cluwned Urist McHuman"
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public LocId NamePrefix = "cluwne-name-prefix";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Outfit ID that the cluwne will spawn with.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public ProtoId<StartingGearPrototype> OutfitId = "CluwneGear";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Amount of time cluwne is paralyzed for when falling over.
|
/// Amount of time cluwne is paralyzed for when falling over.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public float ParalyzeTime = 2f;
|
public float ParalyzeTime = 2f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -36,6 +91,21 @@ public sealed partial class CluwneComponent : Component
|
|||||||
[DataField("spawnsound")]
|
[DataField("spawnsound")]
|
||||||
public SoundSpecifier SpawnSound = new SoundPathSpecifier("/Audio/Items/bikehorn.ogg");
|
public SoundSpecifier SpawnSound = new SoundPathSpecifier("/Audio/Items/bikehorn.ogg");
|
||||||
|
|
||||||
[DataField("knocksound")]
|
/// <summary>
|
||||||
|
/// Emote to use for the cluwne giggling
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public LocId GiggleEmote = "cluwne-giggle-emote";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sound to play when the Cluwne is knocked over and paralyzed
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
public SoundSpecifier KnockSound = new SoundPathSpecifier("/Audio/Items/airhorn.ogg");
|
public SoundSpecifier KnockSound = new SoundPathSpecifier("/Audio/Items/airhorn.ogg");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Emote thats used when the cluwne getting knocked over
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public LocId KnockEmote = "cluwne-knock-emote";
|
||||||
}
|
}
|
||||||
|
|||||||
119
Content.Shared/Crayon/CrayonComponent.cs
Normal file
119
Content.Shared/Crayon/CrayonComponent.cs
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Crayon;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Component holding the state of a crayon-like component
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
[Access(typeof(SharedCrayonSystem))]
|
||||||
|
public sealed partial class CrayonComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of currently selected decal prototype that will be placed when the crayon is used.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public string SelectedState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Color with which the crayon will draw.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public Color Color;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Play a sound when drawing if specified.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public SoundSpecifier? UseSound;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Can the color can be changed?
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool SelectableColor;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should the crayon be deleted when all charges are consumed?
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool DeleteEmpty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens the crayon window for decal and color selection.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum CrayonUiKey : byte
|
||||||
|
{
|
||||||
|
Key,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used by the client to notify the server about the selected decal ID
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class CrayonSelectMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly string State;
|
||||||
|
public CrayonSelectMessage(string selected)
|
||||||
|
{
|
||||||
|
State = selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the color of the crayon, used by Rainbow Crayon
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class CrayonColorMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly Color Color;
|
||||||
|
public CrayonColorMessage(Color color)
|
||||||
|
{
|
||||||
|
Color = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Server to CLIENT. Notifies the BUI that a decal with given ID has been drawn.
|
||||||
|
/// Allows the client UI to advance forward in the client-only ephemeral queue,
|
||||||
|
/// preventing the crayon from becoming a magic text storage device.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class CrayonUsedMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly string DrawnDecal;
|
||||||
|
|
||||||
|
public CrayonUsedMessage(string drawn)
|
||||||
|
{
|
||||||
|
DrawnDecal = drawn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The state of the crayon UI as sent by the server
|
||||||
|
/// </summary>
|
||||||
|
/// <summary>
|
||||||
|
/// TODO: Delete this and use component states and predict the UI.
|
||||||
|
/// This info is already networked on its own.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class CrayonBoundUserInterfaceState : BoundUserInterfaceState
|
||||||
|
{
|
||||||
|
public string Selected;
|
||||||
|
/// <summary>
|
||||||
|
/// Can the color can be changed
|
||||||
|
/// </summary>
|
||||||
|
public bool SelectableColor;
|
||||||
|
public Color Color;
|
||||||
|
|
||||||
|
public CrayonBoundUserInterfaceState(string selected, bool selectableColor, Color color)
|
||||||
|
{
|
||||||
|
Selected = selected;
|
||||||
|
SelectableColor = selectableColor;
|
||||||
|
Color = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
using Robust.Shared.GameStates;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Crayon
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Component holding the state of a crayon-like component
|
|
||||||
/// </summary>
|
|
||||||
[NetworkedComponent, ComponentProtoName("Crayon"), Access(typeof(SharedCrayonSystem))]
|
|
||||||
public abstract partial class SharedCrayonComponent : Component
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The ID of currently selected decal prototype that will be placed when the crayon is used
|
|
||||||
/// </summary>
|
|
||||||
public string SelectedState { get; set; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Color with which the crayon will draw
|
|
||||||
/// </summary>
|
|
||||||
[DataField("color")]
|
|
||||||
public Color Color;
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public enum CrayonUiKey : byte
|
|
||||||
{
|
|
||||||
Key,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used by the client to notify the server about the selected decal ID
|
|
||||||
/// </summary>
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class CrayonSelectMessage : BoundUserInterfaceMessage
|
|
||||||
{
|
|
||||||
public readonly string State;
|
|
||||||
public CrayonSelectMessage(string selected)
|
|
||||||
{
|
|
||||||
State = selected;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets the color of the crayon, used by Rainbow Crayon
|
|
||||||
/// </summary>
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class CrayonColorMessage : BoundUserInterfaceMessage
|
|
||||||
{
|
|
||||||
public readonly Color Color;
|
|
||||||
public CrayonColorMessage(Color color)
|
|
||||||
{
|
|
||||||
Color = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Server to CLIENT. Notifies the BUI that a decal with given ID has been drawn.
|
|
||||||
/// Allows the client UI to advance forward in the client-only ephemeral queue,
|
|
||||||
/// preventing the crayon from becoming a magic text storage device.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class CrayonUsedMessage : BoundUserInterfaceMessage
|
|
||||||
{
|
|
||||||
public readonly string DrawnDecal;
|
|
||||||
|
|
||||||
public CrayonUsedMessage(string drawn)
|
|
||||||
{
|
|
||||||
DrawnDecal = drawn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Component state, describes how many charges are left in the crayon in the near-hand UI
|
|
||||||
/// </summary>
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class CrayonComponentState : ComponentState
|
|
||||||
{
|
|
||||||
public readonly Color Color;
|
|
||||||
public readonly string State;
|
|
||||||
public readonly int Charges;
|
|
||||||
public readonly int Capacity;
|
|
||||||
|
|
||||||
public CrayonComponentState(Color color, string state, int charges, int capacity)
|
|
||||||
{
|
|
||||||
Color = color;
|
|
||||||
State = state;
|
|
||||||
Charges = charges;
|
|
||||||
Capacity = capacity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The state of the crayon UI as sent by the server
|
|
||||||
/// </summary>
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class CrayonBoundUserInterfaceState : BoundUserInterfaceState
|
|
||||||
{
|
|
||||||
public string Selected;
|
|
||||||
/// <summary>
|
|
||||||
/// Whether or not the color can be selected
|
|
||||||
/// </summary>
|
|
||||||
public bool SelectableColor;
|
|
||||||
public Color Color;
|
|
||||||
|
|
||||||
public CrayonBoundUserInterfaceState(string selected, bool selectableColor, Color color)
|
|
||||||
{
|
|
||||||
Selected = selected;
|
|
||||||
SelectableColor = selectableColor;
|
|
||||||
Color = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,8 @@ using Robust.Shared.GameStates;
|
|||||||
|
|
||||||
namespace Content.Shared.Damage.Components;
|
namespace Content.Shared.Damage.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows a detailed examine window with this entity's damage stats when examined.
|
||||||
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent]
|
[RegisterComponent, NetworkedComponent]
|
||||||
public sealed partial class DamageExaminableComponent : Component
|
public sealed partial class DamageExaminableComponent : Component;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using Content.Shared.Damage.Systems;
|
||||||
|
|
||||||
|
namespace Content.Shared.Damage.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes this entity deal damage when thrown at something.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
[Access(typeof(SharedDamageOtherOnHitSystem))]
|
||||||
|
public sealed partial class DamageOtherOnHitComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether to ignore damage modifiers.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public bool IgnoreResistances = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The damage amount to deal on hit.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public DamageSpecifier Damage = default!;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
|
using Content.Shared.Damage.Components;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Shared.Damage.Events;
|
namespace Content.Shared.Damage.Events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on an entity with <see cref="DamageExaminableComponent"/> when examined to get the damage values displayed in the examine window.
|
||||||
|
/// </summary>
|
||||||
[ByRefEvent]
|
[ByRefEvent]
|
||||||
public readonly record struct DamageExamineEvent(FormattedMessage Message, EntityUid User);
|
public readonly record struct DamageExamineEvent(FormattedMessage Message, EntityUid User);
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
using Content.Shared.CombatMode.Pacification;
|
||||||
|
using Content.Shared.Damage.Components;
|
||||||
|
using Content.Shared.Damage.Events;
|
||||||
|
|
||||||
|
namespace Content.Shared.Damage.Systems;
|
||||||
|
|
||||||
|
public abstract class SharedDamageOtherOnHitSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||||
|
[Dependency] private readonly DamageExamineSystem _damageExamine = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<DamageOtherOnHitComponent, DamageExamineEvent>(OnDamageExamine);
|
||||||
|
SubscribeLocalEvent<DamageOtherOnHitComponent, AttemptPacifiedThrowEvent>(OnAttemptPacifiedThrow);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDamageExamine(Entity<DamageOtherOnHitComponent> ent, ref DamageExamineEvent args)
|
||||||
|
{
|
||||||
|
_damageExamine.AddDamageExamine(args.Message, _damageable.ApplyUniversalAllModifiers(ent.Comp.Damage * _damageable.UniversalThrownDamageModifier), Loc.GetString("damage-throw"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prevent players with the Pacified status effect from throwing things that deal damage.
|
||||||
|
/// </summary>
|
||||||
|
private void OnAttemptPacifiedThrow(Entity<DamageOtherOnHitComponent> ent, ref AttemptPacifiedThrowEvent args)
|
||||||
|
{
|
||||||
|
args.Cancel("pacified-cannot-throw");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
namespace Content.Shared.Destructible;
|
namespace Content.Shared.Destructible;
|
||||||
|
|
||||||
public abstract class SharedDestructibleSystem : EntitySystem
|
public abstract class SharedDestructibleSystem : EntitySystem
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Force entity to be destroyed and deleted.
|
/// Force entity to be destroyed and deleted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool DestroyEntity(EntityUid owner)
|
public bool DestroyEntity(EntityUid owner)
|
||||||
{
|
{
|
||||||
@@ -15,12 +15,12 @@ public abstract class SharedDestructibleSystem : EntitySystem
|
|||||||
var eventArgs = new DestructionEventArgs();
|
var eventArgs = new DestructionEventArgs();
|
||||||
RaiseLocalEvent(owner, eventArgs);
|
RaiseLocalEvent(owner, eventArgs);
|
||||||
|
|
||||||
QueueDel(owner);
|
PredictedQueueDel(owner);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Force entity to break.
|
/// Force entity to break.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void BreakEntity(EntityUid owner)
|
public void BreakEntity(EntityUid owner)
|
||||||
{
|
{
|
||||||
@@ -30,7 +30,7 @@ public abstract class SharedDestructibleSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raised before an entity is about to be destroyed and deleted
|
/// Raised before an entity is about to be destroyed and deleted
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class DestructionAttemptEvent : CancellableEntityEventArgs
|
public sealed class DestructionAttemptEvent : CancellableEntityEventArgs
|
||||||
{
|
{
|
||||||
@@ -38,7 +38,7 @@ public sealed class DestructionAttemptEvent : CancellableEntityEventArgs
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raised when entity is destroyed and about to be deleted.
|
/// Raised when entity is destroyed and about to be deleted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class DestructionEventArgs : EntityEventArgs
|
public sealed class DestructionEventArgs : EntityEventArgs
|
||||||
{
|
{
|
||||||
@@ -46,7 +46,7 @@ public sealed class DestructionEventArgs : EntityEventArgs
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raised when entity was heavy damage and about to break.
|
/// Raised when entity was heavy damage and about to break.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class BreakageEventArgs : EntityEventArgs
|
public sealed class BreakageEventArgs : EntityEventArgs
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -214,6 +214,17 @@ public abstract class SharedDeviceLinkSystem : EntitySystem
|
|||||||
return links;
|
return links;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the entities linked to a specific source port.
|
||||||
|
/// </summary>
|
||||||
|
public HashSet<EntityUid> GetLinkedSinks(Entity<DeviceLinkSourceComponent?> source, ProtoId<SourcePortPrototype> port)
|
||||||
|
{
|
||||||
|
if (!Resolve(source, ref source.Comp) || !source.Comp.Outputs.TryGetValue(port, out var linked))
|
||||||
|
return new HashSet<EntityUid>(); // not a source or not linked
|
||||||
|
|
||||||
|
return new HashSet<EntityUid>(linked); // clone to prevent modifying the original
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the default links for the given list of source port prototypes
|
/// Returns the default links for the given list of source port prototypes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -15,4 +15,10 @@ public sealed partial class FlashImmunityComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public bool Enabled = true;
|
public bool Enabled = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should the flash protection be shown when examining the entity?
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool ShowInExamine = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -268,6 +268,7 @@ public abstract class SharedFlashSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnExamine(Entity<FlashImmunityComponent> ent, ref ExaminedEvent args)
|
private void OnExamine(Entity<FlashImmunityComponent> ent, ref ExaminedEvent args)
|
||||||
{
|
{
|
||||||
args.PushMarkup(Loc.GetString("flash-protection"));
|
if (ent.Comp.ShowInExamine)
|
||||||
|
args.PushMarkup(Loc.GetString("flash-protection"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Content.Shared.Ninja.Components;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Makes this warp point a valid bombing target for ninja's spider charge.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed partial class BombingTargetComponent : Component;
|
|
||||||
@@ -1,8 +1,4 @@
|
|||||||
using Content.Shared.FixedPoint;
|
|
||||||
using Content.Shared.Store;
|
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
|
||||||
|
|
||||||
namespace Content.Shared.PAI;
|
namespace Content.Shared.PAI;
|
||||||
|
|
||||||
@@ -16,7 +12,7 @@ namespace Content.Shared.PAI;
|
|||||||
/// and there's not always enough players and ghost roles to justify it.
|
/// and there's not always enough players and ghost roles to justify it.
|
||||||
/// All logic in PAISystem.
|
/// All logic in PAISystem.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent]
|
||||||
public sealed partial class PAIComponent : Component
|
public sealed partial class PAIComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -26,12 +22,6 @@ public sealed partial class PAIComponent : Component
|
|||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
public EntityUid? LastUser;
|
public EntityUid? LastUser;
|
||||||
|
|
||||||
[DataField]
|
|
||||||
public EntProtoId ShopActionId = "ActionPAIOpenShop";
|
|
||||||
|
|
||||||
[DataField, AutoNetworkedField]
|
|
||||||
public EntityUid? ShopAction;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When microwaved there is this chance to brick the pai, kicking out its player and preventing it from being used again.
|
/// When microwaved there is this chance to brick the pai, kicking out its player and preventing it from being used again.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
using Content.Shared.Actions;
|
|
||||||
|
|
||||||
namespace Content.Shared.PAI;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// pAIs, or Personal AIs, are essentially portable ghost role generators.
|
|
||||||
/// In their current implementation, they create a ghost role anyone can access,
|
|
||||||
/// and that a player can also "wipe" (reset/kick out player).
|
|
||||||
/// Theoretically speaking pAIs are supposed to use a dedicated "offer and select" system,
|
|
||||||
/// with the player holding the pAI being able to choose one of the ghosts in the round.
|
|
||||||
/// This seems too complicated for an initial implementation, though,
|
|
||||||
/// and there's not always enough players and ghost roles to justify it.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class SharedPAISystem : EntitySystem
|
|
||||||
{
|
|
||||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<PAIComponent, MapInitEvent>(OnMapInit);
|
|
||||||
SubscribeLocalEvent<PAIComponent, ComponentShutdown>(OnShutdown);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnMapInit(Entity<PAIComponent> ent, ref MapInitEvent args)
|
|
||||||
{
|
|
||||||
_actions.AddAction(ent, ent.Comp.ShopActionId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnShutdown(Entity<PAIComponent> ent, ref ComponentShutdown args)
|
|
||||||
{
|
|
||||||
_actions.RemoveAction(ent.Owner, ent.Comp.ShopAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public sealed partial class PAIShopActionEvent : InstantActionEvent
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -38,7 +38,10 @@ public sealed partial class ParcelWrappingSystem
|
|||||||
private void OnGetVerbsForWrappedParcel(Entity<WrappedParcelComponent> entity,
|
private void OnGetVerbsForWrappedParcel(Entity<WrappedParcelComponent> entity,
|
||||||
ref GetVerbsEvent<InteractionVerb> args)
|
ref GetVerbsEvent<InteractionVerb> args)
|
||||||
{
|
{
|
||||||
if (!args.CanAccess)
|
if (!args.CanAccess || !args.CanComplexInteract)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (entity.Comp.Contents.Contains(args.User))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// "Capture" the values from `args` because C# doesn't like doing the capturing for `ref` values.
|
// "Capture" the values from `args` because C# doesn't like doing the capturing for `ref` values.
|
||||||
|
|||||||
@@ -10,21 +10,30 @@ namespace Content.Shared.Physics;
|
|||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
public sealed partial class JointVisualsComponent : Component
|
public sealed partial class JointVisualsComponent : Component
|
||||||
{
|
{
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("sprite", required: true), AutoNetworkedField]
|
/// <summary>
|
||||||
|
/// The sprite to use for the line.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true), AutoNetworkedField]
|
||||||
public SpriteSpecifier Sprite = default!;
|
public SpriteSpecifier Sprite = default!;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("target"), AutoNetworkedField]
|
/// <summary>
|
||||||
public NetEntity? Target;
|
/// The line is drawn between this target and the entity owning the component.
|
||||||
|
/// </summary>
|
||||||
|
/// <summary>
|
||||||
|
/// TODO: WeakEntityReference.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public EntityUid? Target;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Offset from Body A.
|
/// Offset from Body A.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("offsetA"), AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public Vector2 OffsetA;
|
public Vector2 OffsetA;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Offset from Body B.
|
/// Offset from Body B.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("offsetB"), AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public Vector2 OffsetB;
|
public Vector2 OffsetB;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -214,6 +214,4 @@ public sealed partial class RevenantComponent : Component
|
|||||||
[DataField("harvestingState")]
|
[DataField("harvestingState")]
|
||||||
public string HarvestingState = "harvesting";
|
public string HarvestingState = "harvesting";
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
[DataField] public EntityUid? Action;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,10 +42,6 @@ public sealed class HarvestDoAfterCancelled : EntityEventArgs
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed partial class RevenantShopActionEvent : InstantActionEvent
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed partial class RevenantDefileActionEvent : InstantActionEvent
|
public sealed partial class RevenantDefileActionEvent : InstantActionEvent
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,19 +17,19 @@ namespace Content.Shared.Roles
|
|||||||
[IdDataField]
|
[IdDataField]
|
||||||
public string ID { get; private set; } = default!;
|
public string ID { get; private set; } = default!;
|
||||||
|
|
||||||
[DataField("playTimeTracker", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<PlayTimeTrackerPrototype>))]
|
[DataField(required: true, customTypeSerializer: typeof(PrototypeIdSerializer<PlayTimeTrackerPrototype>))]
|
||||||
public string PlayTimeTracker { get; private set; } = string.Empty;
|
public string PlayTimeTracker { get; private set; } = string.Empty;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Who is the supervisor for this job.
|
/// Who is the supervisor for this job.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("supervisors")]
|
[DataField]
|
||||||
public string Supervisors { get; private set; } = "nobody";
|
public LocId Supervisors = "job-supervisors-nobody";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of this job as displayed to players.
|
/// The name of this job as displayed to players.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("name")]
|
[DataField]
|
||||||
public string Name { get; private set; } = string.Empty;
|
public string Name { get; private set; } = string.Empty;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly)]
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
@@ -38,7 +38,7 @@ namespace Content.Shared.Roles
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name of this job as displayed to players.
|
/// The name of this job as displayed to players.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("description")]
|
[DataField]
|
||||||
public string? Description { get; private set; }
|
public string? Description { get; private set; }
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly)]
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
@@ -53,19 +53,19 @@ namespace Content.Shared.Roles
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// When true - the station will have anouncement about arrival of this player.
|
/// When true - the station will have anouncement about arrival of this player.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("joinNotifyCrew")]
|
[DataField]
|
||||||
public bool JoinNotifyCrew { get; private set; } = false;
|
public bool JoinNotifyCrew { get; private set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When true - the player will recieve a message about importancy of their job.
|
/// When true - the player will recieve a message about importancy of their job.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("requireAdminNotify")]
|
[DataField]
|
||||||
public bool RequireAdminNotify { get; private set; } = false;
|
public bool RequireAdminNotify { get; private set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Should this job appear in preferences menu?
|
/// Should this job appear in preferences menu?
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("setPreference")]
|
[DataField]
|
||||||
public bool SetPreference { get; private set; } = true;
|
public bool SetPreference { get; private set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -81,14 +81,14 @@ namespace Content.Shared.Roles
|
|||||||
[DataField]
|
[DataField]
|
||||||
public bool? OverrideConsoleVisibility { get; private set; } = null;
|
public bool? OverrideConsoleVisibility { get; private set; } = null;
|
||||||
|
|
||||||
[DataField("canBeAntag")]
|
[DataField]
|
||||||
public bool CanBeAntag { get; private set; } = true;
|
public bool CanBeAntag { get; private set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The "weight" or importance of this job. If this number is large, the job system will assign this job
|
/// The "weight" or importance of this job. If this number is large, the job system will assign this job
|
||||||
/// before assigning other jobs.
|
/// before assigning other jobs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("weight")]
|
[DataField]
|
||||||
public int Weight { get; private set; }
|
public int Weight { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -105,7 +105,7 @@ namespace Content.Shared.Roles
|
|||||||
/// A numerical score for how much easier this job is for antagonists.
|
/// A numerical score for how much easier this job is for antagonists.
|
||||||
/// For traitors, reduces starting TC by this amount. Other gamemodes can use it for whatever they find fitting.
|
/// For traitors, reduces starting TC by this amount. Other gamemodes can use it for whatever they find fitting.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("antagAdvantage")]
|
[DataField]
|
||||||
public int AntagAdvantage = 0;
|
public int AntagAdvantage = 0;
|
||||||
|
|
||||||
[DataField]
|
[DataField]
|
||||||
@@ -116,7 +116,7 @@ namespace Content.Shared.Roles
|
|||||||
/// Starting gear will be ignored.
|
/// Starting gear will be ignored.
|
||||||
/// If you want to just add special attributes to a humanoid, use AddComponentSpecial instead.
|
/// If you want to just add special attributes to a humanoid, use AddComponentSpecial instead.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("jobEntity", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField(customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||||
public string? JobEntity = null;
|
public string? JobEntity = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -129,19 +129,19 @@ namespace Content.Shared.Roles
|
|||||||
[DataField]
|
[DataField]
|
||||||
public ProtoId<JobIconPrototype> Icon { get; private set; } = "JobIconUnknown";
|
public ProtoId<JobIconPrototype> Icon { get; private set; } = "JobIconUnknown";
|
||||||
|
|
||||||
[DataField("special", serverOnly: true)]
|
[DataField(serverOnly: true)]
|
||||||
public JobSpecial[] Special { get; private set; } = Array.Empty<JobSpecial>();
|
public JobSpecial[] Special { get; private set; } = Array.Empty<JobSpecial>();
|
||||||
|
|
||||||
[DataField("access")]
|
[DataField]
|
||||||
public IReadOnlyCollection<ProtoId<AccessLevelPrototype>> Access { get; private set; } = Array.Empty<ProtoId<AccessLevelPrototype>>();
|
public IReadOnlyCollection<ProtoId<AccessLevelPrototype>> Access { get; private set; } = Array.Empty<ProtoId<AccessLevelPrototype>>();
|
||||||
|
|
||||||
[DataField("accessGroups")]
|
[DataField]
|
||||||
public IReadOnlyCollection<ProtoId<AccessGroupPrototype>> AccessGroups { get; private set; } = Array.Empty<ProtoId<AccessGroupPrototype>>();
|
public IReadOnlyCollection<ProtoId<AccessGroupPrototype>> AccessGroups { get; private set; } = Array.Empty<ProtoId<AccessGroupPrototype>>();
|
||||||
|
|
||||||
[DataField("extendedAccess")]
|
[DataField]
|
||||||
public IReadOnlyCollection<ProtoId<AccessLevelPrototype>> ExtendedAccess { get; private set; } = Array.Empty<ProtoId<AccessLevelPrototype>>();
|
public IReadOnlyCollection<ProtoId<AccessLevelPrototype>> ExtendedAccess { get; private set; } = Array.Empty<ProtoId<AccessLevelPrototype>>();
|
||||||
|
|
||||||
[DataField("extendedAccessGroups")]
|
[DataField]
|
||||||
public IReadOnlyCollection<ProtoId<AccessGroupPrototype>> ExtendedAccessGroups { get; private set; } = Array.Empty<ProtoId<AccessGroupPrototype>>();
|
public IReadOnlyCollection<ProtoId<AccessGroupPrototype>> ExtendedAccessGroups { get; private set; } = Array.Empty<ProtoId<AccessGroupPrototype>>();
|
||||||
|
|
||||||
[DataField]
|
[DataField]
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ public abstract class SharedRoleSystem : EntitySystem
|
|||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
[Dependency] protected readonly ISharedPlayerManager Player = default!;
|
[Dependency] protected readonly ISharedPlayerManager Player = default!;
|
||||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
|
||||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||||
[Dependency] private readonly SharedMindSystem _minds = default!;
|
[Dependency] private readonly SharedMindSystem _minds = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||||
@@ -400,7 +399,7 @@ public abstract class SharedRoleSystem : EntitySystem
|
|||||||
|
|
||||||
foreach (var role in delete)
|
foreach (var role in delete)
|
||||||
{
|
{
|
||||||
_entityManager.DeleteEntity(role);
|
PredictedDel(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
var update = MindRolesUpdate(mind);
|
var update = MindRolesUpdate(mind);
|
||||||
|
|||||||
@@ -120,5 +120,24 @@ public abstract class SharedBorgSwitchableTypeSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
footstepModifier.FootstepSoundCollection = prototype.FootstepCollection;
|
footstepModifier.FootstepSoundCollection = prototype.FootstepCollection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (prototype.SpriteBodyMovementState is { } movementState)
|
||||||
|
{
|
||||||
|
var spriteMovement = EnsureComp<SpriteMovementComponent>(entity);
|
||||||
|
spriteMovement.NoMovementLayers.Clear();
|
||||||
|
spriteMovement.NoMovementLayers["movement"] = new PrototypeLayerData
|
||||||
|
{
|
||||||
|
State = prototype.SpriteBodyState,
|
||||||
|
};
|
||||||
|
spriteMovement.MovementLayers.Clear();
|
||||||
|
spriteMovement.MovementLayers["movement"] = new PrototypeLayerData
|
||||||
|
{
|
||||||
|
State = movementState,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RemComp<SpriteMovementComponent>(entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
Content.Shared/Silicons/Bots/HugBotComponent.cs
Normal file
12
Content.Shared/Silicons/Bots/HugBotComponent.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Content.Shared.Silicons.Bots;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This component describes how a HugBot hugs.
|
||||||
|
/// </summary>
|
||||||
|
/// <see cref="SharedHugBotSystem"/>
|
||||||
|
[RegisterComponent, AutoGenerateComponentState]
|
||||||
|
public sealed partial class HugBotComponent : Component
|
||||||
|
{
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public TimeSpan HugCooldown = TimeSpan.FromMinutes(2);
|
||||||
|
}
|
||||||
38
Content.Shared/Silicons/Bots/SharedHugBotSystem.cs
Normal file
38
Content.Shared/Silicons/Bots/SharedHugBotSystem.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using Content.Shared.Emag.Systems;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Silicons.Bots;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This system handles HugBots.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SharedHugBotSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly EmagSystem _emag = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<HugBotComponent, GotEmaggedEvent>(OnEmagged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEmagged(Entity<HugBotComponent> entity, ref GotEmaggedEvent args)
|
||||||
|
{
|
||||||
|
if (!_emag.CompareFlag(args.Type, EmagType.Interaction) ||
|
||||||
|
_emag.CheckFlag(entity, EmagType.Interaction) ||
|
||||||
|
!TryComp<HugBotComponent>(entity, out var hugBot))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// HugBot HTN checks for emag state within its own logic, so we don't need to change anything here.
|
||||||
|
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This event is raised on an entity when it is hugged by a HugBot.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed partial class HugBotHugEvent(NetEntity hugBot) : EntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly NetEntity HugBot = hugBot;
|
||||||
|
}
|
||||||
@@ -1,15 +1,25 @@
|
|||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Shared.Stacks;
|
namespace Content.Shared.Stacks;
|
||||||
|
|
||||||
[Prototype]
|
[Prototype]
|
||||||
public sealed partial class StackPrototype : IPrototype
|
public sealed partial class StackPrototype : IPrototype, IInheritingPrototype
|
||||||
{
|
{
|
||||||
[ViewVariables]
|
/// <inheritdoc />
|
||||||
[IdDataField]
|
[IdDataField]
|
||||||
public string ID { get; private set; } = default!;
|
public string ID { get; private set; } = default!;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[ParentDataField(typeof(AbstractPrototypeIdArraySerializer<StackPrototype>))]
|
||||||
|
public string[]? Parents { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[NeverPushInheritance]
|
||||||
|
[AbstractDataField]
|
||||||
|
public bool Abstract { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Human-readable name for this stack type e.g. "Steel"
|
/// Human-readable name for this stack type e.g. "Steel"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -587,7 +587,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
_entList.Add(entity);
|
_entList.Add(entity);
|
||||||
delay += itemSize.Weight * AreaInsertDelayPerItem;
|
delay += itemSize.Weight;
|
||||||
|
|
||||||
if (_entList.Count >= StorageComponent.AreaPickupLimit)
|
if (_entList.Count >= StorageComponent.AreaPickupLimit)
|
||||||
break;
|
break;
|
||||||
@@ -596,7 +596,7 @@ public abstract class SharedStorageSystem : EntitySystem
|
|||||||
//If there's only one then let's be generous
|
//If there's only one then let's be generous
|
||||||
if (_entList.Count >= 1)
|
if (_entList.Count >= 1)
|
||||||
{
|
{
|
||||||
var doAfterArgs = new DoAfterArgs(EntityManager, args.User, delay, new AreaPickupDoAfterEvent(GetNetEntityList(_entList)), uid, target: uid)
|
var doAfterArgs = new DoAfterArgs(EntityManager, args.User, delay * AreaInsertDelayPerItem, new AreaPickupDoAfterEvent(GetNetEntityList(_entList)), uid, target: uid)
|
||||||
{
|
{
|
||||||
BreakOnDamage = true,
|
BreakOnDamage = true,
|
||||||
BreakOnMove = true,
|
BreakOnMove = true,
|
||||||
|
|||||||
11
Content.Shared/Store/Events/IntrinsicStoreActionEvent.cs
Normal file
11
Content.Shared/Store/Events/IntrinsicStoreActionEvent.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Content.Shared.Actions;
|
||||||
|
|
||||||
|
namespace Content.Shared.Store.Events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens a store specified by <see cref="StoreComponent"/>
|
||||||
|
/// Used for entities with a store built into themselves like Revenant or PAI
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class IntrinsicStoreActionEvent : InstantActionEvent
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using Content.Shared.Whitelist;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Trigger.Components.Conditions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a triggered entity or the user of a trigger has a certain mindrole.
|
||||||
|
/// Cancels the trigger otherwise.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Mind roles are only networked to their owner! So if you use this on any other entity than yourself it won't be predicted.
|
||||||
|
/// </remarks>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
public sealed partial class MindRoleTriggerConditionComponent : BaseTriggerConditionComponent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whitelist for what mind role components on the owning entity allow this trigger.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public EntityWhitelist? EntityWhitelist;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whitelist for what mind role components on the User allow this trigger.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public EntityWhitelist? UserWhitelist;
|
||||||
|
}
|
||||||
@@ -4,20 +4,20 @@ using Robust.Shared.GameStates;
|
|||||||
namespace Content.Shared.Trigger.Components.Conditions;
|
namespace Content.Shared.Trigger.Components.Conditions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if the user of a trigger satisfies a whitelist and blacklist condition for the triggered entity or the one triggering it.
|
/// Checks if the user of a trigger satisfies a whitelist and blacklist condition.
|
||||||
/// Cancels the trigger otherwise.
|
/// Cancels the trigger otherwise.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
public sealed partial class WhitelistTriggerConditionComponent : BaseTriggerConditionComponent
|
public sealed partial class WhitelistTriggerConditionComponent : BaseTriggerConditionComponent
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whitelist for what entites can cause this trigger.
|
/// Whitelist for what entities can cause this trigger.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityWhitelist? UserWhitelist;
|
public EntityWhitelist? UserWhitelist;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Blacklist for what entites can cause this trigger.
|
/// Blacklist for what entities can cause this trigger.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public EntityWhitelist? UserBlacklist;
|
public EntityWhitelist? UserBlacklist;
|
||||||
|
|||||||
@@ -58,5 +58,17 @@ public sealed class DnaScrambleOnTriggerSystem : EntitySystem
|
|||||||
|
|
||||||
// Can't use PopupClient or PopupPredicted because the trigger might be unpredicted.
|
// Can't use PopupClient or PopupPredicted because the trigger might be unpredicted.
|
||||||
_popup.PopupEntity(Loc.GetString("scramble-on-trigger-popup"), target.Value, target.Value);
|
_popup.PopupEntity(Loc.GetString("scramble-on-trigger-popup"), target.Value, target.Value);
|
||||||
|
|
||||||
|
var ev = new DnaScrambledEvent(target.Value);
|
||||||
|
RaiseLocalEvent(target.Value, ref ev, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised after an entity has been DNA Scrambled.
|
||||||
|
/// Useful for forks that need to run their own updates here.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="flag">The entity that had its DNA scrambled.</param>
|
||||||
|
|
||||||
|
[ByRefEvent]
|
||||||
|
public record struct DnaScrambledEvent(EntityUid Target);
|
||||||
|
|||||||
@@ -10,31 +10,36 @@ public sealed partial class TriggerSystem
|
|||||||
private void InitializeCondition()
|
private void InitializeCondition()
|
||||||
{
|
{
|
||||||
SubscribeLocalEvent<WhitelistTriggerConditionComponent, AttemptTriggerEvent>(OnWhitelistTriggerAttempt);
|
SubscribeLocalEvent<WhitelistTriggerConditionComponent, AttemptTriggerEvent>(OnWhitelistTriggerAttempt);
|
||||||
|
|
||||||
SubscribeLocalEvent<UseDelayTriggerConditionComponent, AttemptTriggerEvent>(OnUseDelayTriggerAttempt);
|
SubscribeLocalEvent<UseDelayTriggerConditionComponent, AttemptTriggerEvent>(OnUseDelayTriggerAttempt);
|
||||||
|
|
||||||
SubscribeLocalEvent<ToggleTriggerConditionComponent, AttemptTriggerEvent>(OnToggleTriggerAttempt);
|
SubscribeLocalEvent<ToggleTriggerConditionComponent, AttemptTriggerEvent>(OnToggleTriggerAttempt);
|
||||||
SubscribeLocalEvent<ToggleTriggerConditionComponent, GetVerbsEvent<AlternativeVerb>>(OnToggleGetAltVerbs);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<RandomChanceTriggerConditionComponent, AttemptTriggerEvent>(OnRandomChanceTriggerAttempt);
|
SubscribeLocalEvent<RandomChanceTriggerConditionComponent, AttemptTriggerEvent>(OnRandomChanceTriggerAttempt);
|
||||||
|
SubscribeLocalEvent<MindRoleTriggerConditionComponent, AttemptTriggerEvent>(OnMindRoleTriggerAttempt);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ToggleTriggerConditionComponent, GetVerbsEvent<AlternativeVerb>>(OnToggleGetAltVerbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnWhitelistTriggerAttempt(Entity<WhitelistTriggerConditionComponent> ent, ref AttemptTriggerEvent args)
|
private void OnWhitelistTriggerAttempt(Entity<WhitelistTriggerConditionComponent> ent, ref AttemptTriggerEvent args)
|
||||||
{
|
{
|
||||||
if (args.Key == null || ent.Comp.Keys.Contains(args.Key))
|
if (args.Key != null && !ent.Comp.Keys.Contains(args.Key))
|
||||||
args.Cancelled |= !_whitelist.CheckBoth(args.User, ent.Comp.UserBlacklist, ent.Comp.UserWhitelist);
|
return;
|
||||||
|
|
||||||
|
args.Cancelled |= !_whitelist.CheckBoth(args.User, ent.Comp.UserBlacklist, ent.Comp.UserWhitelist);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUseDelayTriggerAttempt(Entity<UseDelayTriggerConditionComponent> ent, ref AttemptTriggerEvent args)
|
private void OnUseDelayTriggerAttempt(Entity<UseDelayTriggerConditionComponent> ent, ref AttemptTriggerEvent args)
|
||||||
{
|
{
|
||||||
if (args.Key == null || ent.Comp.Keys.Contains(args.Key))
|
if (args.Key != null && !ent.Comp.Keys.Contains(args.Key))
|
||||||
args.Cancelled |= _useDelay.IsDelayed(ent.Owner, ent.Comp.UseDelayId);
|
return;
|
||||||
|
|
||||||
|
args.Cancelled |= _useDelay.IsDelayed(ent.Owner, ent.Comp.UseDelayId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnToggleTriggerAttempt(Entity<ToggleTriggerConditionComponent> ent, ref AttemptTriggerEvent args)
|
private void OnToggleTriggerAttempt(Entity<ToggleTriggerConditionComponent> ent, ref AttemptTriggerEvent args)
|
||||||
{
|
{
|
||||||
if (args.Key == null || ent.Comp.Keys.Contains(args.Key))
|
if (args.Key != null && !ent.Comp.Keys.Contains(args.Key))
|
||||||
args.Cancelled |= !ent.Comp.Enabled;
|
return;
|
||||||
|
|
||||||
|
args.Cancelled |= !ent.Comp.Enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnToggleGetAltVerbs(Entity<ToggleTriggerConditionComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
private void OnToggleGetAltVerbs(Entity<ToggleTriggerConditionComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
||||||
@@ -62,19 +67,51 @@ public sealed partial class TriggerSystem
|
|||||||
private void OnRandomChanceTriggerAttempt(Entity<RandomChanceTriggerConditionComponent> ent,
|
private void OnRandomChanceTriggerAttempt(Entity<RandomChanceTriggerConditionComponent> ent,
|
||||||
ref AttemptTriggerEvent args)
|
ref AttemptTriggerEvent args)
|
||||||
{
|
{
|
||||||
if (args.Key == null || ent.Comp.Keys.Contains(args.Key))
|
if (args.Key != null && !ent.Comp.Keys.Contains(args.Key))
|
||||||
{
|
return;
|
||||||
// TODO: Replace with RandomPredicted once the engine PR is merged
|
|
||||||
var hash = new List<int>
|
|
||||||
{
|
|
||||||
(int)_timing.CurTick.Value,
|
|
||||||
GetNetEntity(ent).Id,
|
|
||||||
args.User == null ? 0 : GetNetEntity(args.User.Value).Id,
|
|
||||||
};
|
|
||||||
var seed = SharedRandomExtensions.HashCodeCombine(hash);
|
|
||||||
var rand = new System.Random(seed);
|
|
||||||
|
|
||||||
args.Cancelled |= !rand.Prob(ent.Comp.SuccessChance); // When not successful, Cancelled = true
|
// TODO: Replace with RandomPredicted once the engine PR is merged
|
||||||
|
var hash = new List<int>
|
||||||
|
{
|
||||||
|
(int)_timing.CurTick.Value,
|
||||||
|
GetNetEntity(ent).Id,
|
||||||
|
args.User == null ? 0 : GetNetEntity(args.User.Value).Id,
|
||||||
|
};
|
||||||
|
var seed = SharedRandomExtensions.HashCodeCombine(hash);
|
||||||
|
var rand = new System.Random(seed);
|
||||||
|
|
||||||
|
args.Cancelled |= !rand.Prob(ent.Comp.SuccessChance); // When not successful, Cancelled = true
|
||||||
|
}
|
||||||
|
private void OnMindRoleTriggerAttempt(Entity<MindRoleTriggerConditionComponent> ent, ref AttemptTriggerEvent args)
|
||||||
|
{
|
||||||
|
if (args.Key != null && !ent.Comp.Keys.Contains(args.Key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ent.Comp.EntityWhitelist != null)
|
||||||
|
{
|
||||||
|
if (!_mind.TryGetMind(ent.Owner, out var entMindId, out var entMindComp))
|
||||||
|
{
|
||||||
|
args.Cancelled = true; // the entity has no mind
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!_role.MindHasRole((entMindId, entMindComp), ent.Comp.EntityWhitelist))
|
||||||
|
{
|
||||||
|
args.Cancelled = true; // the entity does not have the required role
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ent.Comp.UserWhitelist != null)
|
||||||
|
{
|
||||||
|
if (args.User == null || !_mind.TryGetMind(args.User.Value, out var userMindId, out var userMindComp))
|
||||||
|
{
|
||||||
|
args.Cancelled = true; // no user or the user has no mind
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!_role.MindHasRole((userMindId, userMindComp), ent.Comp.UserWhitelist))
|
||||||
|
{
|
||||||
|
args.Cancelled = true; // the user does not have the required role
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ using Content.Shared.Database;
|
|||||||
using Content.Shared.DeviceLinking;
|
using Content.Shared.DeviceLinking;
|
||||||
using Content.Shared.EntityTable;
|
using Content.Shared.EntityTable;
|
||||||
using Content.Shared.Item.ItemToggle;
|
using Content.Shared.Item.ItemToggle;
|
||||||
|
using Content.Shared.Mind;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Roles;
|
||||||
using Content.Shared.Timing;
|
using Content.Shared.Timing;
|
||||||
using Content.Shared.Trigger.Components;
|
using Content.Shared.Trigger.Components;
|
||||||
using Content.Shared.Whitelist;
|
using Content.Shared.Whitelist;
|
||||||
@@ -39,6 +41,8 @@ public sealed partial class TriggerSystem : EntitySystem
|
|||||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||||
[Dependency] private readonly ItemToggleSystem _itemToggle = default!;
|
[Dependency] private readonly ItemToggleSystem _itemToggle = default!;
|
||||||
[Dependency] private readonly SharedDeviceLinkSystem _deviceLink = default!;
|
[Dependency] private readonly SharedDeviceLinkSystem _deviceLink = default!;
|
||||||
|
[Dependency] private readonly SharedRoleSystem _role = default!;
|
||||||
|
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||||
[Dependency] private readonly EntityTableSystem _entityTable = default!;
|
[Dependency] private readonly EntityTableSystem _entityTable = default!;
|
||||||
|
|
||||||
public const string DefaultTriggerKey = "trigger";
|
public const string DefaultTriggerKey = "trigger";
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Content.Shared.Administration.Components;
|
|||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.CombatMode;
|
using Content.Shared.CombatMode;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Damage.Events;
|
||||||
using Content.Shared.Damage.Systems;
|
using Content.Shared.Damage.Systems;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
@@ -63,6 +64,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
|||||||
[Dependency] protected readonly SharedPopupSystem PopupSystem = default!;
|
[Dependency] protected readonly SharedPopupSystem PopupSystem = default!;
|
||||||
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
|
[Dependency] protected readonly SharedTransformSystem TransformSystem = default!;
|
||||||
[Dependency] private readonly SharedStaminaSystem _stamina = default!;
|
[Dependency] private readonly SharedStaminaSystem _stamina = default!;
|
||||||
|
[Dependency] private readonly DamageExamineSystem _damageExamine = default!;
|
||||||
|
|
||||||
private const int AttackMask = (int) (CollisionGroup.MobMask | CollisionGroup.Opaque);
|
private const int AttackMask = (int) (CollisionGroup.MobMask | CollisionGroup.Opaque);
|
||||||
|
|
||||||
@@ -83,6 +85,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<MeleeWeaponComponent, HandSelectedEvent>(OnMeleeSelected);
|
SubscribeLocalEvent<MeleeWeaponComponent, HandSelectedEvent>(OnMeleeSelected);
|
||||||
SubscribeLocalEvent<MeleeWeaponComponent, ShotAttemptedEvent>(OnMeleeShotAttempted);
|
SubscribeLocalEvent<MeleeWeaponComponent, ShotAttemptedEvent>(OnMeleeShotAttempted);
|
||||||
SubscribeLocalEvent<MeleeWeaponComponent, GunShotEvent>(OnMeleeShot);
|
SubscribeLocalEvent<MeleeWeaponComponent, GunShotEvent>(OnMeleeShot);
|
||||||
|
SubscribeLocalEvent<MeleeWeaponComponent, DamageExamineEvent>(OnMeleeExamineDamage);
|
||||||
SubscribeLocalEvent<BonusMeleeDamageComponent, GetMeleeDamageEvent>(OnGetBonusMeleeDamage);
|
SubscribeLocalEvent<BonusMeleeDamageComponent, GetMeleeDamageEvent>(OnGetBonusMeleeDamage);
|
||||||
SubscribeLocalEvent<BonusMeleeDamageComponent, GetHeavyDamageModifierEvent>(OnGetBonusHeavyDamageModifier);
|
SubscribeLocalEvent<BonusMeleeDamageComponent, GetHeavyDamageModifierEvent>(OnGetBonusHeavyDamageModifier);
|
||||||
SubscribeLocalEvent<BonusMeleeAttackRateComponent, GetMeleeAttackRateEvent>(OnGetBonusMeleeAttackRate);
|
SubscribeLocalEvent<BonusMeleeAttackRateComponent, GetMeleeAttackRateEvent>(OnGetBonusMeleeAttackRate);
|
||||||
@@ -95,8 +98,7 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
|||||||
SubscribeAllEvent<StopAttackEvent>(OnStopAttack);
|
SubscribeAllEvent<StopAttackEvent>(OnStopAttack);
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
SubscribeLocalEvent<MeleeWeaponComponent,
|
SubscribeLocalEvent<MeleeWeaponComponent, MapInitEvent>(OnMapInit);
|
||||||
MapInitEvent> (OnMapInit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMapInit(EntityUid uid, MeleeWeaponComponent component, MapInitEvent args)
|
private void OnMapInit(EntityUid uid, MeleeWeaponComponent component, MapInitEvent args)
|
||||||
@@ -124,6 +126,18 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnMeleeExamineDamage(EntityUid uid, MeleeWeaponComponent component, ref DamageExamineEvent args)
|
||||||
|
{
|
||||||
|
if (component.Hidden)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var damageSpec = GetDamage(uid, args.User, component);
|
||||||
|
|
||||||
|
if (damageSpec.Empty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_damageExamine.AddDamageExamine(args.Message, Damageable.ApplyUniversalAllModifiers(damageSpec), Loc.GetString("damage-melee"));
|
||||||
|
}
|
||||||
private void OnMeleeSelected(EntityUid uid, MeleeWeaponComponent component, HandSelectedEvent args)
|
private void OnMeleeSelected(EntityUid uid, MeleeWeaponComponent component, HandSelectedEvent args)
|
||||||
{
|
{
|
||||||
var attackRate = GetAttackRate(uid, args.User, component);
|
var attackRate = GetAttackRate(uid, args.User, component);
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public abstract class SharedGrapplingGunSystem : EntitySystem
|
|||||||
var visuals = EnsureComp<JointVisualsComponent>(shotUid.Value);
|
var visuals = EnsureComp<JointVisualsComponent>(shotUid.Value);
|
||||||
visuals.Sprite = component.RopeSprite;
|
visuals.Sprite = component.RopeSprite;
|
||||||
visuals.OffsetA = new Vector2(0f, 0.5f);
|
visuals.OffsetA = new Vector2(0f, 0.5f);
|
||||||
visuals.Target = GetNetEntity(uid);
|
visuals.Target = uid;
|
||||||
Dirty(shotUid.Value, visuals);
|
Dirty(shotUid.Value, visuals);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ public sealed partial class CartridgeAmmoComponent : AmmoComponent
|
|||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public bool Spent;
|
public bool Spent;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this cartridge automatically marked as trash once spent?
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool MarkSpentAsTrash = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Caseless ammunition.
|
/// Caseless ammunition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Damage.Events;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.Projectiles;
|
||||||
using Content.Shared.Weapons.Ranged.Components;
|
using Content.Shared.Weapons.Ranged.Components;
|
||||||
using Content.Shared.Weapons.Ranged.Events;
|
using Content.Shared.Weapons.Ranged.Events;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.Weapons.Ranged.Systems;
|
namespace Content.Shared.Weapons.Ranged.Systems;
|
||||||
@@ -18,6 +22,7 @@ public abstract partial class SharedGunSystem
|
|||||||
SubscribeLocalEvent<HitscanBatteryAmmoProviderComponent, TakeAmmoEvent>(OnBatteryTakeAmmo);
|
SubscribeLocalEvent<HitscanBatteryAmmoProviderComponent, TakeAmmoEvent>(OnBatteryTakeAmmo);
|
||||||
SubscribeLocalEvent<HitscanBatteryAmmoProviderComponent, GetAmmoCountEvent>(OnBatteryAmmoCount);
|
SubscribeLocalEvent<HitscanBatteryAmmoProviderComponent, GetAmmoCountEvent>(OnBatteryAmmoCount);
|
||||||
SubscribeLocalEvent<HitscanBatteryAmmoProviderComponent, ExaminedEvent>(OnBatteryExamine);
|
SubscribeLocalEvent<HitscanBatteryAmmoProviderComponent, ExaminedEvent>(OnBatteryExamine);
|
||||||
|
SubscribeLocalEvent<HitscanBatteryAmmoProviderComponent, DamageExamineEvent>(OnBatteryDamageExamine);
|
||||||
|
|
||||||
// Projectile
|
// Projectile
|
||||||
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, ComponentGetState>(OnBatteryGetState);
|
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, ComponentGetState>(OnBatteryGetState);
|
||||||
@@ -25,6 +30,7 @@ public abstract partial class SharedGunSystem
|
|||||||
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, TakeAmmoEvent>(OnBatteryTakeAmmo);
|
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, TakeAmmoEvent>(OnBatteryTakeAmmo);
|
||||||
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, GetAmmoCountEvent>(OnBatteryAmmoCount);
|
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, GetAmmoCountEvent>(OnBatteryAmmoCount);
|
||||||
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, ExaminedEvent>(OnBatteryExamine);
|
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, ExaminedEvent>(OnBatteryExamine);
|
||||||
|
SubscribeLocalEvent<ProjectileBatteryAmmoProviderComponent, DamageExamineEvent>(OnBatteryDamageExamine);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBatteryHandleState(EntityUid uid, BatteryAmmoProviderComponent component, ref ComponentHandleState args)
|
private void OnBatteryHandleState(EntityUid uid, BatteryAmmoProviderComponent component, ref ComponentHandleState args)
|
||||||
@@ -53,6 +59,51 @@ public abstract partial class SharedGunSystem
|
|||||||
args.PushMarkup(Loc.GetString("gun-battery-examine", ("color", AmmoExamineColor), ("count", component.Shots)));
|
args.PushMarkup(Loc.GetString("gun-battery-examine", ("color", AmmoExamineColor), ("count", component.Shots)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnBatteryDamageExamine<T>(Entity<T> entity, ref DamageExamineEvent args) where T : BatteryAmmoProviderComponent
|
||||||
|
{
|
||||||
|
var damageSpec = GetDamage(entity.Comp);
|
||||||
|
|
||||||
|
if (damageSpec == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var damageType = entity.Comp switch
|
||||||
|
{
|
||||||
|
HitscanBatteryAmmoProviderComponent => Loc.GetString("damage-hitscan"),
|
||||||
|
ProjectileBatteryAmmoProviderComponent => Loc.GetString("damage-projectile"),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(),
|
||||||
|
};
|
||||||
|
|
||||||
|
_damageExamine.AddDamageExamine(args.Message, Damageable.ApplyUniversalAllModifiers(damageSpec), damageType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DamageSpecifier? GetDamage(BatteryAmmoProviderComponent component)
|
||||||
|
{
|
||||||
|
if (component is ProjectileBatteryAmmoProviderComponent battery)
|
||||||
|
{
|
||||||
|
if (ProtoManager.Index<EntityPrototype>(battery.Prototype)
|
||||||
|
.Components
|
||||||
|
.TryGetValue(Factory.GetComponentName<ProjectileComponent>(), out var projectile))
|
||||||
|
{
|
||||||
|
var p = (ProjectileComponent)projectile.Component;
|
||||||
|
|
||||||
|
if (!p.Damage.Empty)
|
||||||
|
{
|
||||||
|
return p.Damage * Damageable.UniversalProjectileDamageModifier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component is HitscanBatteryAmmoProviderComponent hitscan)
|
||||||
|
{
|
||||||
|
var dmg = ProtoManager.Index<HitscanPrototype>(hitscan.Prototype).Damage;
|
||||||
|
return dmg == null ? dmg : dmg * Damageable.UniversalHitscanDamageModifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnBatteryTakeAmmo(EntityUid uid, BatteryAmmoProviderComponent component, TakeAmmoEvent args)
|
private void OnBatteryTakeAmmo(EntityUid uid, BatteryAmmoProviderComponent component, TakeAmmoEvent args)
|
||||||
{
|
{
|
||||||
var shots = Math.Min(args.Shots, component.Shots);
|
var shots = Math.Min(args.Shots, component.Shots);
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||||
|
|
||||||
|
private static readonly ProtoId<TagPrototype> TrashTag = "Trash";
|
||||||
|
|
||||||
private const float InteractNextFire = 0.3f;
|
private const float InteractNextFire = 0.3f;
|
||||||
private const double SafetyNextFire = 0.5;
|
private const double SafetyNextFire = 0.5;
|
||||||
private const float EjectOffset = 0.4f;
|
private const float EjectOffset = 0.4f;
|
||||||
@@ -452,6 +454,14 @@ public abstract partial class SharedGunSystem : EntitySystem
|
|||||||
|
|
||||||
cartridge.Spent = spent;
|
cartridge.Spent = spent;
|
||||||
Appearance.SetData(uid, AmmoVisuals.Spent, spent);
|
Appearance.SetData(uid, AmmoVisuals.Spent, spent);
|
||||||
|
|
||||||
|
if (!cartridge.MarkSpentAsTrash)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (spent)
|
||||||
|
TagSystem.AddTag(uid, TrashTag);
|
||||||
|
else
|
||||||
|
TagSystem.RemoveTag(uid, TrashTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public sealed partial class AnalysisConsoleComponent : Component
|
|||||||
/// Can be null if not linked.
|
/// Can be null if not linked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public NetEntity? AnalyzerEntity;
|
public EntityUid? AnalyzerEntity;
|
||||||
|
|
||||||
[DataField]
|
[DataField]
|
||||||
public SoundSpecifier? ScanFinishedSound = new SoundPathSpecifier("/Audio/Machines/scan_finish.ogg");
|
public SoundSpecifier? ScanFinishedSound = new SoundPathSpecifier("/Audio/Machines/scan_finish.ogg");
|
||||||
@@ -35,7 +35,7 @@ public sealed partial class AnalysisConsoleComponent : Component
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The machine linking port for the analyzer
|
/// The machine linking port for linking the console with the analyzer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public ProtoId<SourcePortPrototype> LinkingPort = "ArtifactAnalyzerSender";
|
public ProtoId<SourcePortPrototype> LinkingPort = "ArtifactAnalyzerSender";
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Robust.Shared.Audio;
|
using Content.Shared.DeviceLinking;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Shared.Xenoarchaeology.Equipment.Components;
|
namespace Content.Shared.Xenoarchaeology.Equipment.Components;
|
||||||
|
|
||||||
@@ -35,4 +36,10 @@ public sealed partial class ArtifactAnalyzerComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public bool ReadyToPrint = false;
|
public bool ReadyToPrint = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The machine linking port for linking the analyzer with the console.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public ProtoId<SinkPortPrototype> LinkingPort = "ArtifactAnalyzerReceiver";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Content.Shared.Damage;
|
|||||||
using Content.Shared.Stacks;
|
using Content.Shared.Stacks;
|
||||||
using Content.Shared.Whitelist;
|
using Content.Shared.Whitelist;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Audio.Components;
|
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
@@ -14,7 +13,8 @@ namespace Content.Shared.Xenoarchaeology.Equipment.Components;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is an entity storage that, when activated, crushes the artifact inside of it and gives artifact fragments.
|
/// This is an entity storage that, when activated, crushes the artifact inside of it and gives artifact fragments.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
[AutoGenerateComponentState, AutoGenerateComponentPause]
|
||||||
[Access(typeof(SharedArtifactCrusherSystem))]
|
[Access(typeof(SharedArtifactCrusherSystem))]
|
||||||
public sealed partial class ArtifactCrusherComponent : Component
|
public sealed partial class ArtifactCrusherComponent : Component
|
||||||
{
|
{
|
||||||
@@ -27,19 +27,21 @@ public sealed partial class ArtifactCrusherComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// When the current crushing will end.
|
/// When the current crushing will end.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
|
[AutoNetworkedField, AutoPausedField]
|
||||||
public TimeSpan CrushEndTime;
|
public TimeSpan CrushEndTime;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The next second. Used to apply damage over time.
|
/// The next second. Used to apply damage over time.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
|
[AutoNetworkedField, AutoPausedField]
|
||||||
public TimeSpan NextSecond;
|
public TimeSpan NextSecond;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The total duration of the crushing.
|
/// The total duration of the crushing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public TimeSpan CrushDuration = TimeSpan.FromSeconds(10);
|
public TimeSpan CrushDuration = TimeSpan.FromSeconds(10);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -51,19 +53,19 @@ public sealed partial class ArtifactCrusherComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The minimum amount of fragments spawned.
|
/// The minimum amount of fragments spawned.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public int MinFragments = 2;
|
public int MinFragments = 2;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum amount of fragments spawned, non-inclusive.
|
/// The maximum amount of fragments spawned, non-inclusive.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public int MaxFragments = 5;
|
public int MaxFragments = 5;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The material for the fragments.
|
/// The material for the fragments.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public ProtoId<StackPrototype> FragmentStackProtoId = "ArtifactFragment";
|
public ProtoId<StackPrototype> FragmentStackProtoId = "ArtifactFragment";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -100,12 +102,12 @@ public sealed partial class ArtifactCrusherComponent : Component
|
|||||||
/// Stores entity of <see cref="CrushingSound"/> to allow ending it early.
|
/// Stores entity of <see cref="CrushingSound"/> to allow ending it early.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField]
|
||||||
public (EntityUid, AudioComponent)? CrushingSoundEntity;
|
public EntityUid? CrushingSoundEntity;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When enabled, stops the artifact crusher from being opened when it is being crushed.
|
/// When enabled, stops the artifact crusher from being opened when it is being crushed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
|
[DataField, AutoNetworkedField]
|
||||||
public bool AutoLock = false;
|
public bool AutoLock = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ namespace Content.Shared.Xenoarchaeology.Equipment;
|
|||||||
public abstract class SharedArtifactAnalyzerSystem : EntitySystem
|
public abstract class SharedArtifactAnalyzerSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly SharedPowerReceiverSystem _powerReceiver = default!;
|
[Dependency] private readonly SharedPowerReceiverSystem _powerReceiver = default!;
|
||||||
|
[Dependency] private readonly SharedDeviceLinkSystem _deviceLink = default!;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -23,10 +24,14 @@ public abstract class SharedArtifactAnalyzerSystem : EntitySystem
|
|||||||
|
|
||||||
SubscribeLocalEvent<ArtifactAnalyzerComponent, ItemPlacedEvent>(OnItemPlaced);
|
SubscribeLocalEvent<ArtifactAnalyzerComponent, ItemPlacedEvent>(OnItemPlaced);
|
||||||
SubscribeLocalEvent<ArtifactAnalyzerComponent, ItemRemovedEvent>(OnItemRemoved);
|
SubscribeLocalEvent<ArtifactAnalyzerComponent, ItemRemovedEvent>(OnItemRemoved);
|
||||||
SubscribeLocalEvent<ArtifactAnalyzerComponent, MapInitEvent>(OnMapInit);
|
SubscribeLocalEvent<ArtifactAnalyzerComponent, NewLinkEvent>(OnNewLinkAnalyzer);
|
||||||
|
SubscribeLocalEvent<ArtifactAnalyzerComponent, LinkAttemptEvent>(OnLinkAttemptAnalyzer);
|
||||||
|
SubscribeLocalEvent<ArtifactAnalyzerComponent, PortDisconnectedEvent>(OnPortDisconnectedAnalyzer);
|
||||||
|
|
||||||
SubscribeLocalEvent<AnalysisConsoleComponent, NewLinkEvent>(OnNewLink);
|
SubscribeLocalEvent<AnalysisConsoleComponent, MapInitEvent>(OnMapInit);
|
||||||
SubscribeLocalEvent<AnalysisConsoleComponent, PortDisconnectedEvent>(OnPortDisconnected);
|
SubscribeLocalEvent<AnalysisConsoleComponent, NewLinkEvent>(OnNewLinkConsole);
|
||||||
|
SubscribeLocalEvent<AnalysisConsoleComponent, LinkAttemptEvent>(OnLinkAttemptConsole);
|
||||||
|
SubscribeLocalEvent<AnalysisConsoleComponent, PortDisconnectedEvent>(OnPortDisconnectedConsole);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnItemPlaced(Entity<ArtifactAnalyzerComponent> ent, ref ItemPlacedEvent args)
|
private void OnItemPlaced(Entity<ArtifactAnalyzerComponent> ent, ref ItemPlacedEvent args)
|
||||||
@@ -44,52 +49,74 @@ public abstract class SharedArtifactAnalyzerSystem : EntitySystem
|
|||||||
Dirty(ent);
|
Dirty(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMapInit(Entity<ArtifactAnalyzerComponent> ent, ref MapInitEvent args)
|
private void OnMapInit(Entity<AnalysisConsoleComponent> ent, ref MapInitEvent args)
|
||||||
{
|
{
|
||||||
if (!TryComp<DeviceLinkSinkComponent>(ent, out var sink))
|
if (!TryComp<DeviceLinkSourceComponent>(ent, out var source))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var source in sink.LinkedSources)
|
var linkedEntities = _deviceLink.GetLinkedSinks((ent.Owner, source), ent.Comp.LinkingPort);
|
||||||
|
|
||||||
|
foreach (var sink in linkedEntities)
|
||||||
{
|
{
|
||||||
if (!TryComp<AnalysisConsoleComponent>(source, out var analysis))
|
if (!TryComp<ArtifactAnalyzerComponent>(sink, out var analyzer))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
analysis.AnalyzerEntity = GetNetEntity(ent);
|
ent.Comp.AnalyzerEntity = sink;
|
||||||
ent.Comp.Console = source;
|
analyzer.Console = ent.Owner;
|
||||||
Dirty(source, analysis);
|
|
||||||
Dirty(ent);
|
Dirty(ent);
|
||||||
|
Dirty(sink, analyzer);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnNewLink(Entity<AnalysisConsoleComponent> ent, ref NewLinkEvent args)
|
private void OnNewLinkConsole(Entity<AnalysisConsoleComponent> ent, ref NewLinkEvent args)
|
||||||
{
|
{
|
||||||
if (!TryComp<ArtifactAnalyzerComponent>(args.Sink, out var analyzer))
|
if (args.SourcePort != ent.Comp.LinkingPort || !HasComp<ArtifactAnalyzerComponent>(args.Sink))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ent.Comp.AnalyzerEntity = GetNetEntity(args.Sink);
|
ent.Comp.AnalyzerEntity = args.Sink;
|
||||||
analyzer.Console = ent;
|
|
||||||
Dirty(args.Sink, analyzer);
|
|
||||||
Dirty(ent);
|
Dirty(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPortDisconnected(Entity<AnalysisConsoleComponent> ent, ref PortDisconnectedEvent args)
|
private void OnNewLinkAnalyzer(Entity<ArtifactAnalyzerComponent> ent, ref NewLinkEvent args)
|
||||||
{
|
{
|
||||||
var analyzerNetEntity = ent.Comp.AnalyzerEntity;
|
if (args.SinkPort != ent.Comp.LinkingPort || !HasComp<AnalysisConsoleComponent>(args.Source))
|
||||||
if (args.Port != ent.Comp.LinkingPort || analyzerNetEntity == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var analyzerEntityUid = GetEntity(analyzerNetEntity);
|
ent.Comp.Console = args.Source;
|
||||||
if (TryComp<ArtifactAnalyzerComponent>(analyzerEntityUid, out var analyzer))
|
Dirty(ent);
|
||||||
{
|
}
|
||||||
analyzer.Console = null;
|
|
||||||
Dirty(analyzerEntityUid.Value, analyzer);
|
private void OnLinkAttemptConsole(Entity<AnalysisConsoleComponent> ent, ref LinkAttemptEvent args)
|
||||||
}
|
{
|
||||||
|
if (ent.Comp.AnalyzerEntity != null)
|
||||||
|
args.Cancel(); // can only link to one device at a time
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLinkAttemptAnalyzer(Entity<ArtifactAnalyzerComponent> ent, ref LinkAttemptEvent args)
|
||||||
|
{
|
||||||
|
if (ent.Comp.Console != null)
|
||||||
|
args.Cancel(); // can only link to one device at a time
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPortDisconnectedConsole(Entity<AnalysisConsoleComponent> ent, ref PortDisconnectedEvent args)
|
||||||
|
{
|
||||||
|
if (args.Port != ent.Comp.LinkingPort || ent.Comp.AnalyzerEntity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
ent.Comp.AnalyzerEntity = null;
|
ent.Comp.AnalyzerEntity = null;
|
||||||
Dirty(ent);
|
Dirty(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnPortDisconnectedAnalyzer(Entity<ArtifactAnalyzerComponent> ent, ref PortDisconnectedEvent args)
|
||||||
|
{
|
||||||
|
if (args.Port != ent.Comp.LinkingPort || ent.Comp.Console == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent.Comp.Console = null;
|
||||||
|
Dirty(ent);
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryGetAnalyzer(Entity<AnalysisConsoleComponent> ent, [NotNullWhen(true)] out Entity<ArtifactAnalyzerComponent>? analyzer)
|
public bool TryGetAnalyzer(Entity<AnalysisConsoleComponent> ent, [NotNullWhen(true)] out Entity<ArtifactAnalyzerComponent>? analyzer)
|
||||||
{
|
{
|
||||||
analyzer = null;
|
analyzer = null;
|
||||||
@@ -98,14 +125,13 @@ public abstract class SharedArtifactAnalyzerSystem : EntitySystem
|
|||||||
if (!_powerReceiver.IsPowered(consoleEnt))
|
if (!_powerReceiver.IsPowered(consoleEnt))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var analyzerUid = GetEntity(ent.Comp.AnalyzerEntity);
|
if (!TryComp<ArtifactAnalyzerComponent>(ent.Comp.AnalyzerEntity, out var analyzerComp))
|
||||||
if (!TryComp<ArtifactAnalyzerComponent>(analyzerUid, out var analyzerComp))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!_powerReceiver.IsPowered(analyzerUid.Value))
|
if (!_powerReceiver.IsPowered(ent.Comp.AnalyzerEntity.Value))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
analyzer = (analyzerUid.Value, analyzerComp);
|
analyzer = (ent.Comp.AnalyzerEntity.Value, analyzerComp);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
using Content.Shared.Examine;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Storage.Components;
|
|
||||||
using Robust.Shared.Audio.Systems;
|
|
||||||
using Robust.Shared.Containers;
|
|
||||||
using Content.Shared.Emag.Systems;
|
using Content.Shared.Emag.Systems;
|
||||||
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Power;
|
||||||
|
using Content.Shared.Power.EntitySystems;
|
||||||
|
using Content.Shared.Storage.Components;
|
||||||
|
using Content.Shared.Verbs;
|
||||||
using Content.Shared.Xenoarchaeology.Equipment.Components;
|
using Content.Shared.Xenoarchaeology.Equipment.Components;
|
||||||
|
using Robust.Shared.Audio.Systems;
|
||||||
|
using Robust.Shared.Collections;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Shared.Xenoarchaeology.Equipment;
|
namespace Content.Shared.Xenoarchaeology.Equipment;
|
||||||
|
|
||||||
@@ -12,10 +19,14 @@ namespace Content.Shared.Xenoarchaeology.Equipment;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class SharedArtifactCrusherSystem : EntitySystem
|
public abstract class SharedArtifactCrusherSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
|
|
||||||
[Dependency] protected readonly SharedAudioSystem AudioSystem = default!;
|
[Dependency] protected readonly SharedAudioSystem AudioSystem = default!;
|
||||||
[Dependency] protected readonly SharedContainerSystem ContainerSystem = default!;
|
[Dependency] protected readonly SharedContainerSystem ContainerSystem = default!;
|
||||||
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
[Dependency] private readonly EmagSystem _emag = default!;
|
[Dependency] private readonly EmagSystem _emag = default!;
|
||||||
|
[Dependency] private readonly SharedPowerReceiverSystem _power = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -27,6 +38,8 @@ public abstract class SharedArtifactCrusherSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<ArtifactCrusherComponent, StorageOpenAttemptEvent>(OnStorageOpenAttempt);
|
SubscribeLocalEvent<ArtifactCrusherComponent, StorageOpenAttemptEvent>(OnStorageOpenAttempt);
|
||||||
SubscribeLocalEvent<ArtifactCrusherComponent, ExaminedEvent>(OnExamine);
|
SubscribeLocalEvent<ArtifactCrusherComponent, ExaminedEvent>(OnExamine);
|
||||||
SubscribeLocalEvent<ArtifactCrusherComponent, GotEmaggedEvent>(OnEmagged);
|
SubscribeLocalEvent<ArtifactCrusherComponent, GotEmaggedEvent>(OnEmagged);
|
||||||
|
SubscribeLocalEvent<ArtifactCrusherComponent, GetVerbsEvent<AlternativeVerb>>(OnGetVerbs);
|
||||||
|
SubscribeLocalEvent<ArtifactCrusherComponent, PowerChangedEvent>(OnPowerChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnInit(Entity<ArtifactCrusherComponent> ent, ref ComponentInit args)
|
private void OnInit(Entity<ArtifactCrusherComponent> ent, ref ComponentInit args)
|
||||||
@@ -53,6 +66,7 @@ public abstract class SharedArtifactCrusherSystem : EntitySystem
|
|||||||
|
|
||||||
ent.Comp.AutoLock = true;
|
ent.Comp.AutoLock = true;
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
|
Dirty(ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStorageOpenAttempt(Entity<ArtifactCrusherComponent> ent, ref StorageOpenAttemptEvent args)
|
private void OnStorageOpenAttempt(Entity<ArtifactCrusherComponent> ent, ref StorageOpenAttemptEvent args)
|
||||||
@@ -66,22 +80,94 @@ public abstract class SharedArtifactCrusherSystem : EntitySystem
|
|||||||
args.PushMarkup(ent.Comp.AutoLock ? Loc.GetString("artifact-crusher-examine-autolocks") : Loc.GetString("artifact-crusher-examine-no-autolocks"));
|
args.PushMarkup(ent.Comp.AutoLock ? Loc.GetString("artifact-crusher-examine-autolocks") : Loc.GetString("artifact-crusher-examine-no-autolocks"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopCrushing(Entity<ArtifactCrusherComponent> ent, bool early = true)
|
private void OnGetVerbs(Entity<ArtifactCrusherComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
||||||
{
|
{
|
||||||
var (_, crusher) = ent;
|
if (!args.CanAccess || !args.CanInteract || args.Hands == null || ent.Comp.Crushing)
|
||||||
|
|
||||||
if (!crusher.Crushing)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
crusher.Crushing = false;
|
if (!TryComp<EntityStorageComponent>(ent, out var entityStorageComp) ||
|
||||||
Appearance.SetData(ent, ArtifactCrusherVisuals.Crushing, false);
|
entityStorageComp.Contents.ContainedEntities.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!_power.IsPowered(ent.Owner))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var user = args.User;
|
||||||
|
var verb = new AlternativeVerb
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("artifact-crusher-verb-start-crushing"),
|
||||||
|
Priority = 2,
|
||||||
|
Act = () => StartCrushing((ent, ent.Comp, entityStorageComp), user)
|
||||||
|
};
|
||||||
|
args.Verbs.Add(verb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPowerChanged(Entity<ArtifactCrusherComponent> ent, ref PowerChangedEvent args)
|
||||||
|
{
|
||||||
|
if (!args.Powered)
|
||||||
|
StopCrushing(ent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartCrushing(Entity<ArtifactCrusherComponent, EntityStorageComponent> ent, EntityUid? user = null)
|
||||||
|
{
|
||||||
|
var (uid, crusher, _) = ent;
|
||||||
|
|
||||||
|
if (crusher.Crushing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (crusher.AutoLock)
|
||||||
|
_popup.PopupPredicted(Loc.GetString("artifact-crusher-autolocks-enable"), uid, user);
|
||||||
|
|
||||||
|
crusher.Crushing = true;
|
||||||
|
crusher.NextSecond = _timing.CurTime + TimeSpan.FromSeconds(1);
|
||||||
|
crusher.CrushEndTime = _timing.CurTime + crusher.CrushDuration;
|
||||||
|
crusher.CrushingSoundEntity = AudioSystem.PlayPvs(crusher.CrushingSound, ent)?.Entity;
|
||||||
|
_appearance.SetData(ent, ArtifactCrusherVisuals.Crushing, true);
|
||||||
|
Dirty(ent, ent.Comp1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopCrushing(Entity<ArtifactCrusherComponent> ent, bool early = true)
|
||||||
|
{
|
||||||
|
if (!ent.Comp.Crushing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ent.Comp.Crushing = false;
|
||||||
|
_appearance.SetData(ent, ArtifactCrusherVisuals.Crushing, false);
|
||||||
|
|
||||||
if (early)
|
if (early)
|
||||||
{
|
{
|
||||||
AudioSystem.Stop(crusher.CrushingSoundEntity?.Item1, crusher.CrushingSoundEntity?.Item2);
|
AudioSystem.Stop(ent.Comp.CrushingSoundEntity);
|
||||||
crusher.CrushingSoundEntity = null;
|
ent.Comp.CrushingSoundEntity = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Dirty(ent, ent.Comp);
|
Dirty(ent, ent.Comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void FinishCrushing(Entity<ArtifactCrusherComponent, EntityStorageComponent> ent) { }
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
var query = EntityQueryEnumerator<ArtifactCrusherComponent, EntityStorageComponent>();
|
||||||
|
while (query.MoveNext(out var uid, out var crusher, out var storage))
|
||||||
|
{
|
||||||
|
if (!crusher.Crushing)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (crusher.NextSecond < _timing.CurTime)
|
||||||
|
{
|
||||||
|
var contents = new ValueList<EntityUid>(storage.Contents.ContainedEntities);
|
||||||
|
foreach (var contained in contents)
|
||||||
|
{
|
||||||
|
_damageable.TryChangeDamage(contained, crusher.CrushingDamage);
|
||||||
|
}
|
||||||
|
crusher.NextSecond += TimeSpan.FromSeconds(1);
|
||||||
|
Dirty(uid, crusher);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crusher.CrushEndTime < _timing.CurTime)
|
||||||
|
FinishCrushing((uid, crusher, storage));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,4 @@
|
|||||||
Entries:
|
Entries:
|
||||||
- author: CoconutThunder
|
|
||||||
changes:
|
|
||||||
- message: Fixed lubed items being thrown when looking at the pickup verb.
|
|
||||||
type: Fix
|
|
||||||
- message: Fixed multihanded items showing a popup when looking at the pickup verb.
|
|
||||||
type: Fix
|
|
||||||
- message: Fixed a bug with lubed handcuffs.
|
|
||||||
type: Fix
|
|
||||||
id: 8561
|
|
||||||
time: '2025-05-25T05:10:58.0000000+00:00'
|
|
||||||
url: https://github.com/space-wizards/space-station-14/pull/38705
|
|
||||||
- author: CoconutThunder
|
- author: CoconutThunder
|
||||||
changes:
|
changes:
|
||||||
- message: The Chief Medical Officer should now appear with the correct precedence
|
- message: The Chief Medical Officer should now appear with the correct precedence
|
||||||
@@ -3960,3 +3949,183 @@
|
|||||||
id: 9062
|
id: 9062
|
||||||
time: '2025-10-09T14:00:07.0000000+00:00'
|
time: '2025-10-09T14:00:07.0000000+00:00'
|
||||||
url: https://github.com/space-wizards/space-station-14/pull/40790
|
url: https://github.com/space-wizards/space-station-14/pull/40790
|
||||||
|
- author: Princess-Cheeseballs
|
||||||
|
changes:
|
||||||
|
- message: The thieving beacon can now detect the officer's handgun objective.
|
||||||
|
type: Fix
|
||||||
|
id: 9063
|
||||||
|
time: '2025-10-10T04:59:26.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40811
|
||||||
|
- author: Hitlinemoss
|
||||||
|
changes:
|
||||||
|
- message: The Paladin AI lawset has been rewritten.
|
||||||
|
type: Tweak
|
||||||
|
id: 9064
|
||||||
|
time: '2025-10-10T12:41:57.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40343
|
||||||
|
- author: kontakt
|
||||||
|
changes:
|
||||||
|
- message: Ninjas can now have a bombing target at any warp point.
|
||||||
|
type: Tweak
|
||||||
|
id: 9065
|
||||||
|
time: '2025-10-10T21:27:36.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40726
|
||||||
|
- author: frigid-dev
|
||||||
|
changes:
|
||||||
|
- message: Ore Crabs now collide with the player if mob collisions are enabled.
|
||||||
|
type: Tweak
|
||||||
|
- message: Ore Crabs now show they are stunned
|
||||||
|
type: Fix
|
||||||
|
id: 9066
|
||||||
|
time: '2025-10-10T23:31:04.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40764
|
||||||
|
- author: Crude Oil
|
||||||
|
changes:
|
||||||
|
- message: Removed 'Activate Holopad Projector' verb from the station AI core.
|
||||||
|
type: Fix
|
||||||
|
id: 9067
|
||||||
|
time: '2025-10-11T00:03:14.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/39937
|
||||||
|
- author: Hitlinemoss
|
||||||
|
changes:
|
||||||
|
- message: Ice now satiates thirst.
|
||||||
|
type: Tweak
|
||||||
|
- message: Ice now evaporates.
|
||||||
|
type: Tweak
|
||||||
|
id: 9068
|
||||||
|
time: '2025-10-11T00:20:22.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40149
|
||||||
|
- author: slarticodefast
|
||||||
|
changes:
|
||||||
|
- message: The artifact analysis console no longer breaks when trying to link it
|
||||||
|
to multiple artifact analyzers or vice versa.
|
||||||
|
type: Fix
|
||||||
|
id: 9069
|
||||||
|
time: '2025-10-11T00:33:04.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/39984
|
||||||
|
- author: qwerltaz
|
||||||
|
changes:
|
||||||
|
- message: The ID card computer now has buttons to grant or revoke all access from
|
||||||
|
the target ID card.
|
||||||
|
type: Add
|
||||||
|
id: 9070
|
||||||
|
time: '2025-10-11T00:52:01.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/39375
|
||||||
|
- author: Centronias
|
||||||
|
changes:
|
||||||
|
- message: The HugBot, which is similar in purpose and construction to other small
|
||||||
|
robots like the CleanBot.
|
||||||
|
type: Add
|
||||||
|
id: 9071
|
||||||
|
time: '2025-10-11T01:05:08.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/37557
|
||||||
|
- author: PicklOH
|
||||||
|
changes:
|
||||||
|
- message: Added more Syndie ammo to the EMAG lathe inventory
|
||||||
|
type: Add
|
||||||
|
id: 9072
|
||||||
|
time: '2025-10-11T05:14:34.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40822
|
||||||
|
- author: TrixxedHeart
|
||||||
|
changes:
|
||||||
|
- message: Updated Xenoarchaeology guidebook page to current system.
|
||||||
|
type: Tweak
|
||||||
|
id: 9073
|
||||||
|
time: '2025-10-11T17:32:21.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40621
|
||||||
|
- author: HTMLSystem
|
||||||
|
changes:
|
||||||
|
- message: Added infectious anom sprites for moths and arachnids.
|
||||||
|
type: Add
|
||||||
|
id: 9074
|
||||||
|
time: '2025-10-11T18:01:49.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/39508
|
||||||
|
- author: ScarKy0
|
||||||
|
changes:
|
||||||
|
- message: Mice, cockroaches and other small mobs can no longer unwrap parcels.
|
||||||
|
type: Fix
|
||||||
|
id: 9075
|
||||||
|
time: '2025-10-11T21:32:59.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40838
|
||||||
|
- author: Hitlinemoss
|
||||||
|
changes:
|
||||||
|
- message: Autolathes can now print durathread.
|
||||||
|
type: Add
|
||||||
|
id: 9076
|
||||||
|
time: '2025-10-11T21:46:46.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40837
|
||||||
|
- author: cloudyias
|
||||||
|
changes:
|
||||||
|
- message: Added the ability for droppers to be printed via autolathes and medfabs
|
||||||
|
type: Add
|
||||||
|
id: 9077
|
||||||
|
time: '2025-10-11T23:15:41.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40074
|
||||||
|
- author: jessicamaybe
|
||||||
|
changes:
|
||||||
|
- message: Added swabs and an emag inventory to the biogenerator
|
||||||
|
type: Tweak
|
||||||
|
id: 9078
|
||||||
|
time: '2025-10-12T00:15:18.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/39037
|
||||||
|
- author: SuperGDPWYL
|
||||||
|
changes:
|
||||||
|
- message: Added the Syndicate ID Card to the uplink for 1 TC.
|
||||||
|
type: Add
|
||||||
|
id: 9079
|
||||||
|
time: '2025-10-12T00:56:09.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/38381
|
||||||
|
- author: aada
|
||||||
|
changes:
|
||||||
|
- message: Cannabis no longer stacks infinitely. Sorry, botanists!
|
||||||
|
type: Fix
|
||||||
|
id: 9080
|
||||||
|
time: '2025-10-12T01:29:01.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/38412
|
||||||
|
- author: GovnokradZXC
|
||||||
|
changes:
|
||||||
|
- message: New hair named Pigtail (Over Eye)
|
||||||
|
type: Add
|
||||||
|
id: 9081
|
||||||
|
time: '2025-10-12T05:45:59.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/39850
|
||||||
|
- author: Princess-Cheeseballs
|
||||||
|
changes:
|
||||||
|
- message: Meat Kudzu gasps less often.
|
||||||
|
type: Tweak
|
||||||
|
id: 9082
|
||||||
|
time: '2025-10-12T10:47:30.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/39304
|
||||||
|
- author: PJB3005
|
||||||
|
changes:
|
||||||
|
- message: The patrons list in the in-game credits works again.
|
||||||
|
type: Fix
|
||||||
|
id: 9083
|
||||||
|
time: '2025-10-12T11:02:33.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40840
|
||||||
|
- author: DrSmugleaf
|
||||||
|
changes:
|
||||||
|
- message: Fixed species not being ordered alphabetically in the character customization
|
||||||
|
UI.
|
||||||
|
type: Fix
|
||||||
|
id: 9084
|
||||||
|
time: '2025-10-12T11:14:46.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/39359
|
||||||
|
- author: Callmore
|
||||||
|
changes:
|
||||||
|
- message: Bullet casings can now be picked up again.
|
||||||
|
type: Fix
|
||||||
|
id: 9085
|
||||||
|
time: '2025-10-12T18:45:39.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40829
|
||||||
|
- author: CoconutThunder
|
||||||
|
changes:
|
||||||
|
- message: Fixed lubed items being thrown when looking at the pickup verb.
|
||||||
|
type: Fix
|
||||||
|
- message: Fixed multihanded items showing a popup when looking at the pickup verb.
|
||||||
|
type: Fix
|
||||||
|
- message: Fixed a bug with lubed handcuffs.
|
||||||
|
type: Fix
|
||||||
|
id: 9086
|
||||||
|
time: '2025-05-25T05:10:58.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/38705
|
||||||
|
|||||||
@@ -764,4 +764,25 @@
|
|||||||
id: 92
|
id: 92
|
||||||
time: '2025-10-08T20:41:46.0000000+00:00'
|
time: '2025-10-08T20:41:46.0000000+00:00'
|
||||||
url: https://github.com/space-wizards/space-station-14/pull/40785
|
url: https://github.com/space-wizards/space-station-14/pull/40785
|
||||||
|
- author: ToastEnjoyer
|
||||||
|
changes:
|
||||||
|
- message: On Plasma, added more nitrogen canisters to the maintenance hallways
|
||||||
|
type: Add
|
||||||
|
id: 93
|
||||||
|
time: '2025-10-10T01:11:33.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40794
|
||||||
|
- author: opl
|
||||||
|
changes:
|
||||||
|
- message: On Packed, prisoners can now use the Megaseed in brig.
|
||||||
|
type: Fix
|
||||||
|
id: 94
|
||||||
|
time: '2025-10-10T20:33:03.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40821
|
||||||
|
- author: CoconutThunder
|
||||||
|
changes:
|
||||||
|
- message: Reduced frequency of syndicate shark attacks
|
||||||
|
type: Tweak
|
||||||
|
id: 95
|
||||||
|
time: '2025-10-12T18:45:54.0000000+00:00'
|
||||||
|
url: https://github.com/space-wizards/space-station-14/pull/40855
|
||||||
Order: 1
|
Order: 1
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user