Merge branch 'master' into stable
This commit is contained in:
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
/Content.*/Stunnable/ @Princess-Cheeseballs
|
/Content.*/Stunnable/ @Princess-Cheeseballs
|
||||||
/Content.*/Nutrition/ @Princess-Cheeseballs
|
/Content.*/Nutrition/ @Princess-Cheeseballs
|
||||||
|
/Content.*/EntityEffects @Princess-Cheeseballs @sowelipililimute
|
||||||
|
|
||||||
# SKREEEE
|
# SKREEEE
|
||||||
/Content.*.Database/ @PJB3005 @DrSmugleaf
|
/Content.*.Database/ @PJB3005 @DrSmugleaf
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from typing import List
|
|||||||
|
|
||||||
SOLUTION_PATH = Path("..") / "SpaceStation14.sln"
|
SOLUTION_PATH = Path("..") / "SpaceStation14.sln"
|
||||||
# If this doesn't match the saved version we overwrite them all.
|
# If this doesn't match the saved version we overwrite them all.
|
||||||
CURRENT_HOOKS_VERSION = "3"
|
CURRENT_HOOKS_VERSION = "4"
|
||||||
QUIET = len(sys.argv) == 2 and sys.argv[1] == "--quiet"
|
QUIET = len(sys.argv) == 2 and sys.argv[1] == "--quiet"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
gitroot=$(git rev-parse --show-toplevel)
|
gitroot=$(git rev-parse --show-toplevel)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# Just call post-checkout since it does the same thing.
|
# Just call post-checkout since it does the same thing.
|
||||||
gitroot=$(git rev-parse --git-path hooks)
|
gitroot=$(git rev-parse --git-path hooks)
|
||||||
|
|||||||
@@ -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. -->
|
||||||
|
<BoxContainer Margin="0 8 0 4">
|
||||||
|
<BoxContainer>
|
||||||
<Label Text="{Loc 'id-card-console-window-job-selection-label'}" />
|
<Label Text="{Loc 'id-card-console-window-job-selection-label'}" />
|
||||||
<OptionButton Name="JobPresetOptionButton" />
|
<OptionButton Name="JobPresetOptionButton" />
|
||||||
</GridContainer>
|
</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;
|
||||||
8
Content.Client/Temperature/Systems/TemperatureSystem.cs
Normal file
8
Content.Client/Temperature/Systems/TemperatureSystem.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Content.Shared.Temperature.Systems;
|
||||||
|
|
||||||
|
namespace Content.Client.Temperature.Systems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This exists so <see cref="SharedTemperatureSystem"/> runs on client/>
|
||||||
|
/// </summary>
|
||||||
|
public sealed class TemperatureSystem : SharedTemperatureSystem;
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Content.Server.Administration.Logs;
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.Atmos.Components;
|
using Content.Server.Atmos.Components;
|
||||||
using Content.Server.Stunnable;
|
using Content.Server.Stunnable;
|
||||||
using Content.Server.Temperature.Components;
|
|
||||||
using Content.Server.Temperature.Systems;
|
using Content.Server.Temperature.Systems;
|
||||||
using Content.Server.Damage.Components;
|
using Content.Server.Damage.Components;
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
@@ -24,6 +23,7 @@ using Content.Shared.Toggleable;
|
|||||||
using Content.Shared.Weapons.Melee.Events;
|
using Content.Shared.Weapons.Melee.Events;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Hands;
|
using Content.Shared.Hands;
|
||||||
|
using Content.Shared.Temperature.Components;
|
||||||
using Robust.Server.Audio;
|
using Robust.Server.Audio;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Physics.Events;
|
using Robust.Shared.Physics.Events;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using Content.Server.Atmos.EntitySystems;
|
using Content.Server.Atmos.EntitySystems;
|
||||||
using Content.Server.Temperature.Components;
|
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Atmos.Rotting;
|
using Content.Shared.Atmos.Rotting;
|
||||||
using Content.Shared.Body.Events;
|
using Content.Shared.Body.Events;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Temperature.Components;
|
||||||
using Robust.Server.Containers;
|
using Robust.Server.Containers;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|||||||
@@ -10,13 +10,13 @@ namespace Content.Server.Body.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handles metabolizing various reagents with given effects.
|
/// Handles metabolizing various reagents with given effects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent, Access(typeof(MetabolizerSystem))]
|
[RegisterComponent, AutoGenerateComponentPause, Access(typeof(MetabolizerSystem))]
|
||||||
public sealed partial class MetabolizerComponent : Component
|
public sealed partial class MetabolizerComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The next time that reagents will be metabolized.
|
/// The next time that reagents will be metabolized.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
[DataField, AutoPausedField]
|
||||||
public TimeSpan NextUpdate;
|
public TimeSpan NextUpdate;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Components;
|
||||||
using Content.Shared.Administration.Logs;
|
|
||||||
using Content.Shared.Body.Events;
|
using Content.Shared.Body.Events;
|
||||||
using Content.Shared.Body.Organ;
|
using Content.Shared.Body.Organ;
|
||||||
|
using Content.Shared.Body.Prototypes;
|
||||||
using Content.Shared.Body.Systems;
|
using Content.Shared.Body.Systems;
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||||
using Content.Shared.Chemistry.EntitySystems;
|
using Content.Shared.Chemistry.EntitySystems;
|
||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.EntityConditions;
|
||||||
|
using Content.Shared.EntityConditions.Conditions;
|
||||||
|
using Content.Shared.EntityConditions.Conditions.Body;
|
||||||
using Content.Shared.EntityEffects;
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Body;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Solution;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Mobs.Components;
|
using Content.Shared.Mobs.Components;
|
||||||
using Content.Shared.Mobs.Systems;
|
using Content.Shared.Mobs.Systems;
|
||||||
@@ -17,20 +21,22 @@ using Robust.Shared.Prototypes;
|
|||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Server.Body.Systems
|
namespace Content.Server.Body.Systems;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public sealed class MetabolizerSystem : SharedMetabolizerSystem
|
||||||
{
|
{
|
||||||
/// <inheritdoc/>
|
|
||||||
public sealed class MetabolizerSystem : SharedMetabolizerSystem
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
|
||||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||||
|
[Dependency] private readonly SharedEntityConditionsSystem _entityConditions = default!;
|
||||||
|
[Dependency] private readonly SharedEntityEffectsSystem _entityEffects = default!;
|
||||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
||||||
|
|
||||||
private EntityQuery<OrganComponent> _organQuery;
|
private EntityQuery<OrganComponent> _organQuery;
|
||||||
private EntityQuery<SolutionContainerManagerComponent> _solutionQuery;
|
private EntityQuery<SolutionContainerManagerComponent> _solutionQuery;
|
||||||
|
private static readonly ProtoId<MetabolismGroupPrototype> Gas = "Gas";
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -41,7 +47,6 @@ namespace Content.Server.Body.Systems
|
|||||||
|
|
||||||
SubscribeLocalEvent<MetabolizerComponent, ComponentInit>(OnMetabolizerInit);
|
SubscribeLocalEvent<MetabolizerComponent, ComponentInit>(OnMetabolizerInit);
|
||||||
SubscribeLocalEvent<MetabolizerComponent, MapInitEvent>(OnMapInit);
|
SubscribeLocalEvent<MetabolizerComponent, MapInitEvent>(OnMapInit);
|
||||||
SubscribeLocalEvent<MetabolizerComponent, EntityUnpausedEvent>(OnUnpaused);
|
|
||||||
SubscribeLocalEvent<MetabolizerComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
|
SubscribeLocalEvent<MetabolizerComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,11 +55,6 @@ namespace Content.Server.Body.Systems
|
|||||||
ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.AdjustedUpdateInterval;
|
ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.AdjustedUpdateInterval;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUnpaused(Entity<MetabolizerComponent> ent, ref EntityUnpausedEvent args)
|
|
||||||
{
|
|
||||||
ent.Comp.NextUpdate += args.PausedTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnMetabolizerInit(Entity<MetabolizerComponent> entity, ref ComponentInit args)
|
private void OnMetabolizerInit(Entity<MetabolizerComponent> entity, ref ComponentInit args)
|
||||||
{
|
{
|
||||||
if (!entity.Comp.SolutionOnBody)
|
if (!entity.Comp.SolutionOnBody)
|
||||||
@@ -164,6 +164,7 @@ namespace Content.Server.Body.Systems
|
|||||||
if (ent.Comp1.MetabolismGroups is null)
|
if (ent.Comp1.MetabolismGroups is null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// TODO: Kill MetabolismGroups!
|
||||||
foreach (var group in ent.Comp1.MetabolismGroups)
|
foreach (var group in ent.Comp1.MetabolismGroups)
|
||||||
{
|
{
|
||||||
if (!proto.Metabolisms.TryGetValue(group.Id, out var entry))
|
if (!proto.Metabolisms.TryGetValue(group.Id, out var entry))
|
||||||
@@ -174,7 +175,12 @@ namespace Content.Server.Body.Systems
|
|||||||
// Remove $rate, as long as there's enough reagent there to actually remove that much
|
// Remove $rate, as long as there's enough reagent there to actually remove that much
|
||||||
mostToRemove = FixedPoint2.Clamp(rate, 0, quantity);
|
mostToRemove = FixedPoint2.Clamp(rate, 0, quantity);
|
||||||
|
|
||||||
float scale = (float) mostToRemove / (float) rate;
|
var scale = (float) mostToRemove;
|
||||||
|
|
||||||
|
// TODO: This is a very stupid workaround to lungs heavily relying on scale = reagent quantity. Needs lung and metabolism refactors to remove.
|
||||||
|
// TODO: Lungs just need to have their scale be equal to the mols consumed, scale needs to be not hardcoded either and configurable per metabolizer...
|
||||||
|
if (group.Id != Gas)
|
||||||
|
scale /= (float) entry.MetabolismRate;
|
||||||
|
|
||||||
// if it's possible for them to be dead, and they are,
|
// if it's possible for them to be dead, and they are,
|
||||||
// then we shouldn't process any effects, but should probably
|
// then we shouldn't process any effects, but should probably
|
||||||
@@ -186,27 +192,36 @@ namespace Content.Server.Body.Systems
|
|||||||
}
|
}
|
||||||
|
|
||||||
var actualEntity = ent.Comp2?.Body ?? solutionEntityUid.Value;
|
var actualEntity = ent.Comp2?.Body ?? solutionEntityUid.Value;
|
||||||
var args = new EntityEffectReagentArgs(actualEntity, EntityManager, ent, solution, mostToRemove, proto, null, scale);
|
|
||||||
|
|
||||||
// do all effects, if conditions apply
|
// do all effects, if conditions apply
|
||||||
foreach (var effect in entry.Effects)
|
foreach (var effect in entry.Effects)
|
||||||
{
|
{
|
||||||
if (!effect.ShouldApply(args, _random))
|
if (scale < effect.MinScale)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (effect.ShouldLog)
|
// See if conditions apply
|
||||||
{
|
if (effect.Conditions != null && !CanMetabolizeEffect(actualEntity, ent, soln.Value, effect.Conditions))
|
||||||
_adminLogger.Add(
|
continue;
|
||||||
LogType.ReagentEffect,
|
|
||||||
effect.LogImpact,
|
ApplyEffect(effect);
|
||||||
$"Metabolism effect {effect.GetType().Name:effect}"
|
|
||||||
+ $" of reagent {proto.LocalizedName:reagent}"
|
|
||||||
+ $" applied on entity {actualEntity:entity}"
|
|
||||||
+ $" at {Transform(actualEntity).Coordinates:coordinates}"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
effect.Effect(args);
|
// TODO: We should have to do this with metabolism. ReagentEffect struct needs refactoring and so does metabolism!
|
||||||
|
void ApplyEffect(EntityEffect effect)
|
||||||
|
{
|
||||||
|
switch (effect)
|
||||||
|
{
|
||||||
|
case ModifyLungGas:
|
||||||
|
_entityEffects.ApplyEffect(ent, effect, scale);
|
||||||
|
break;
|
||||||
|
case AdjustReagent:
|
||||||
|
_entityEffects.ApplyEffect(soln.Value, effect, scale);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_entityEffects.ApplyEffect(actualEntity, effect, scale);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,5 +237,42 @@ namespace Content.Server.Body.Systems
|
|||||||
|
|
||||||
_solutionContainerSystem.UpdateChemicals(soln.Value);
|
_solutionContainerSystem.UpdateChemicals(soln.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public API to check if a certain metabolism effect can be applied to an entity.
|
||||||
|
/// TODO: With metabolism refactor make this logic smarter and unhardcode the old hardcoding entity effects used to have for metabolism!
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="body">The body metabolizing the effects</param>
|
||||||
|
/// <param name="organ">The organ doing the metabolizing</param>
|
||||||
|
/// <param name="solution">The solution we are metabolizing from</param>
|
||||||
|
/// <param name="conditions">The conditions that need to be met to metabolize</param>
|
||||||
|
/// <returns>True if we can metabolize! False if we cannot!</returns>
|
||||||
|
public bool CanMetabolizeEffect(EntityUid body, EntityUid organ, Entity<SolutionComponent> solution, EntityCondition[] conditions)
|
||||||
|
{
|
||||||
|
foreach (var condition in conditions)
|
||||||
|
{
|
||||||
|
switch (condition)
|
||||||
|
{
|
||||||
|
// Need specific handling of specific conditions since Metabolism is funny like that.
|
||||||
|
// TODO: MetabolizerTypes should be handled well before this stage by metabolism itself.
|
||||||
|
case MetabolizerTypeCondition:
|
||||||
|
if (_entityConditions.TryCondition(organ, condition))
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
case ReagentCondition:
|
||||||
|
if (_entityConditions.TryCondition(solution, condition))
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (_entityConditions.TryCondition(body, condition))
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Content.Server.Administration.Logs;
|
|||||||
using Content.Server.Atmos.EntitySystems;
|
using Content.Server.Atmos.EntitySystems;
|
||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Components;
|
||||||
using Content.Server.Chat.Systems;
|
using Content.Server.Chat.Systems;
|
||||||
using Content.Server.EntityEffects;
|
|
||||||
using Content.Shared.Body.Systems;
|
using Content.Shared.Body.Systems;
|
||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
@@ -14,9 +13,11 @@ using Content.Shared.Chemistry.EntitySystems;
|
|||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.EntityConditions;
|
||||||
|
using Content.Shared.EntityConditions.Conditions.Body;
|
||||||
using Content.Shared.EntityEffects;
|
using Content.Shared.EntityEffects;
|
||||||
using Content.Shared.EntityEffects.EffectConditions;
|
|
||||||
using Content.Shared.EntityEffects.Effects;
|
using Content.Shared.EntityEffects.Effects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Body;
|
||||||
using Content.Shared.Mobs.Systems;
|
using Content.Shared.Mobs.Systems;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
@@ -29,16 +30,16 @@ public sealed class RespiratorSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||||
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
|
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
|
||||||
[Dependency] private readonly AtmosphereSystem _atmosSys = default!;
|
[Dependency] private readonly AtmosphereSystem _atmosSys = default!;
|
||||||
[Dependency] private readonly BodySystem _bodySystem = default!;
|
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||||
|
[Dependency] private readonly ChatSystem _chat = default!;
|
||||||
[Dependency] private readonly DamageableSystem _damageableSys = default!;
|
[Dependency] private readonly DamageableSystem _damageableSys = default!;
|
||||||
[Dependency] private readonly LungSystem _lungSystem = default!;
|
[Dependency] private readonly LungSystem _lungSystem = default!;
|
||||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
[Dependency] private readonly SharedEntityConditionsSystem _entityConditions = default!;
|
||||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
|
||||||
[Dependency] private readonly ChatSystem _chat = default!;
|
|
||||||
[Dependency] private readonly EntityEffectSystem _entityEffect = default!;
|
|
||||||
|
|
||||||
private static readonly ProtoId<MetabolismGroupPrototype> GasId = new("Gas");
|
private static readonly ProtoId<MetabolismGroupPrototype> GasId = new("Gas");
|
||||||
|
|
||||||
@@ -340,7 +341,6 @@ public sealed class RespiratorSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO generalize condition checks
|
|
||||||
// this is pretty janky, but I just want to bodge a method that checks if an entity can breathe a gas mixture
|
// this is pretty janky, but I just want to bodge a method that checks if an entity can breathe a gas mixture
|
||||||
// Applying actual reaction effects require a full ReagentEffectArgs struct.
|
// Applying actual reaction effects require a full ReagentEffectArgs struct.
|
||||||
bool CanMetabolize(EntityEffect effect)
|
bool CanMetabolize(EntityEffect effect)
|
||||||
@@ -348,9 +348,10 @@ public sealed class RespiratorSystem : EntitySystem
|
|||||||
if (effect.Conditions == null)
|
if (effect.Conditions == null)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// TODO: Use Metabolism Public API to do this instead, once that API has been built.
|
||||||
foreach (var cond in effect.Conditions)
|
foreach (var cond in effect.Conditions)
|
||||||
{
|
{
|
||||||
if (cond is OrganType organ && !_entityEffect.OrganCondition(organ, lung))
|
if (cond is MetabolizerTypeCondition organ && !_entityConditions.TryCondition(lung, organ))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Components;
|
||||||
using Content.Server.Temperature.Components;
|
|
||||||
using Content.Server.Temperature.Systems;
|
using Content.Server.Temperature.Systems;
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
|
using Content.Shared.Temperature.Components;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Server.Body.Systems;
|
namespace Content.Server.Body.Systems;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
using Content.Server.Botany.Components;
|
using Content.Server.Botany.Components;
|
||||||
using Content.Server.Botany.Systems;
|
using Content.Server.Botany.Systems;
|
||||||
using Content.Server.EntityEffects;
|
using Content.Server.EntityEffects.Effects.Botany;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
using Content.Shared.Random;
|
using Content.Shared.Random;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
@@ -79,9 +80,13 @@ public partial struct SeedChemQuantity
|
|||||||
[DataField("Inherent")] public bool Inherent = true;
|
[DataField("Inherent")] public bool Inherent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO reduce the number of friends to a reasonable level. Requires ECS-ing things like plant holder component.
|
// TODO Make Botany ECS and give it a proper API. I removed the limited access of this class because it's egregious how many systems needed access to it due to a lack of an actual API.
|
||||||
|
/// <remarks>
|
||||||
|
/// SeedData is no longer restricted because the number of friends is absolutely unreasonable.
|
||||||
|
/// This entire data definition is unreasonable. I felt genuine fear looking at this, this is horrific. Send help.
|
||||||
|
/// </remarks>
|
||||||
|
// TODO: Hit Botany with hammers
|
||||||
[Virtual, DataDefinition]
|
[Virtual, DataDefinition]
|
||||||
[Access(typeof(BotanySystem), typeof(PlantHolderSystem), typeof(SeedExtractorSystem), typeof(PlantHolderComponent), typeof(EntityEffectSystem), typeof(MutationSystem))]
|
|
||||||
public partial class SeedData
|
public partial class SeedData
|
||||||
{
|
{
|
||||||
#region Tracking
|
#region Tracking
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ namespace Content.Server.Botany.Systems;
|
|||||||
|
|
||||||
public sealed partial class BotanySystem
|
public sealed partial class BotanySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly SharedEntityEffectsSystem _entityEffects = default!;
|
||||||
|
|
||||||
public void ProduceGrown(EntityUid uid, ProduceComponent produce)
|
public void ProduceGrown(EntityUid uid, ProduceComponent produce)
|
||||||
{
|
{
|
||||||
if (!TryGetSeed(produce, out var seed))
|
if (!TryGetSeed(produce, out var seed))
|
||||||
@@ -15,10 +17,7 @@ public sealed partial class BotanySystem
|
|||||||
foreach (var mutation in seed.Mutations)
|
foreach (var mutation in seed.Mutations)
|
||||||
{
|
{
|
||||||
if (mutation.AppliesToProduce)
|
if (mutation.AppliesToProduce)
|
||||||
{
|
_entityEffects.TryApplyEffect(uid, mutation.Effect);
|
||||||
var args = new EntityEffectBaseArgs(uid, EntityManager);
|
|
||||||
mutation.Effect.Effect(args);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_solutionContainerSystem.EnsureSolution(uid,
|
if (!_solutionContainerSystem.EnsureSolution(uid,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ public sealed class MutationSystem : EntitySystem
|
|||||||
|
|
||||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
[Dependency] private readonly SharedEntityEffectsSystem _entityEffects = default!;
|
||||||
private RandomPlantMutationListPrototype _randomMutations = default!;
|
private RandomPlantMutationListPrototype _randomMutations = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -32,10 +33,8 @@ public sealed class MutationSystem : EntitySystem
|
|||||||
if (Random(Math.Min(mutation.BaseOdds * severity, 1.0f)))
|
if (Random(Math.Min(mutation.BaseOdds * severity, 1.0f)))
|
||||||
{
|
{
|
||||||
if (mutation.AppliesToPlant)
|
if (mutation.AppliesToPlant)
|
||||||
{
|
_entityEffects.TryApplyEffect(plantHolder, mutation.Effect);
|
||||||
var args = new EntityEffectBaseArgs(plantHolder, EntityManager);
|
|
||||||
mutation.Effect.Effect(args);
|
|
||||||
}
|
|
||||||
// Stat adjustments do not persist by being an attached effect, they just change the stat.
|
// Stat adjustments do not persist by being an attached effect, they just change the stat.
|
||||||
if (mutation.Persists && !seed.Mutations.Any(m => m.Name == mutation.Name))
|
if (mutation.Persists && !seed.Mutations.Any(m => m.Name == mutation.Name))
|
||||||
seed.Mutations.Add(mutation);
|
seed.Mutations.Add(mutation);
|
||||||
|
|||||||
@@ -24,8 +24,10 @@ using Robust.Shared.Prototypes;
|
|||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
|
using Content.Shared.Chemistry.Reaction;
|
||||||
using Content.Shared.Containers.ItemSlots;
|
using Content.Shared.Containers.ItemSlots;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
using Content.Shared.Kitchen.Components;
|
using Content.Shared.Kitchen.Components;
|
||||||
using Content.Shared.Labels.Components;
|
using Content.Shared.Labels.Components;
|
||||||
|
|
||||||
@@ -48,6 +50,7 @@ public sealed class PlantHolderSystem : EntitySystem
|
|||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly ItemSlotsSystem _itemSlots = default!;
|
[Dependency] private readonly ItemSlotsSystem _itemSlots = default!;
|
||||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||||
|
[Dependency] private readonly SharedEntityEffectsSystem _entityEffects = default!;
|
||||||
|
|
||||||
public const float HydroponicsSpeedMultiplier = 1f;
|
public const float HydroponicsSpeedMultiplier = 1f;
|
||||||
public const float HydroponicsConsumptionMultiplier = 2f;
|
public const float HydroponicsConsumptionMultiplier = 2f;
|
||||||
@@ -887,7 +890,7 @@ public sealed class PlantHolderSystem : EntitySystem
|
|||||||
foreach (var entry in _solutionContainerSystem.RemoveEachReagent(component.SoilSolution.Value, amt))
|
foreach (var entry in _solutionContainerSystem.RemoveEachReagent(component.SoilSolution.Value, amt))
|
||||||
{
|
{
|
||||||
var reagentProto = _prototype.Index<ReagentPrototype>(entry.Reagent.Prototype);
|
var reagentProto = _prototype.Index<ReagentPrototype>(entry.Reagent.Prototype);
|
||||||
reagentProto.ReactionPlant(uid, entry, solution, EntityManager, _random, _adminLogger);
|
_entityEffects.ApplyEffects(uid, reagentProto.PlantMetabolisms.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public sealed class DumpReagentGuideText : LocalizedEntityCommands
|
|||||||
{
|
{
|
||||||
foreach (var effect in entry.Effects)
|
foreach (var effect in entry.Effects)
|
||||||
{
|
{
|
||||||
shell.WriteLine(effect.GuidebookEffectDescription(_prototype, EntityManager.EntitySysManager) ??
|
shell.WriteLine(reagent.GuidebookReagentEffectDescription(_prototype, EntityManager.EntitySysManager, effect, entry.MetabolismRate) ??
|
||||||
Loc.GetString($"cmd-dumpreagentguidetext-skipped", ("effect", effect.GetType())));
|
Loc.GetString($"cmd-dumpreagentguidetext-skipped", ("effect", effect.GetType())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ using Content.Shared.Prying.Systems;
|
|||||||
using Content.Shared.Radio.EntitySystems;
|
using Content.Shared.Radio.EntitySystems;
|
||||||
using Content.Shared.Stacks;
|
using Content.Shared.Stacks;
|
||||||
using Content.Shared.Temperature;
|
using Content.Shared.Temperature;
|
||||||
|
using Content.Shared.Temperature.Components;
|
||||||
using Content.Shared.Tools.Systems;
|
using Content.Shared.Tools.Systems;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|||||||
@@ -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,35 +1,31 @@
|
|||||||
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 IAdminLogManager _adminLogger = default!;
|
||||||
[Dependency] private readonly GunSystem _guns = default!;
|
[Dependency] private readonly GunSystem _guns = default!;
|
||||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||||
[Dependency] private readonly DamageExamineSystem _damageExamine = default!;
|
|
||||||
[Dependency] private readonly SharedCameraRecoilSystem _sharedCameraRecoil = default!;
|
[Dependency] private readonly SharedCameraRecoilSystem _sharedCameraRecoil = default!;
|
||||||
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
|
[Dependency] private readonly SharedColorFlashEffectSystem _color = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<DamageOtherOnHitComponent, ThrowDoHitEvent>(OnDoHit);
|
SubscribeLocalEvent<DamageOtherOnHitComponent, ThrowDoHitEvent>(OnDoHit);
|
||||||
SubscribeLocalEvent<DamageOtherOnHitComponent, DamageExamineEvent>(OnDamageExamine);
|
|
||||||
SubscribeLocalEvent<DamageOtherOnHitComponent, AttemptPacifiedThrowEvent>(OnAttemptPacifiedThrow);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDoHit(EntityUid uid, DamageOtherOnHitComponent component, ThrowDoHitEvent args)
|
private void OnDoHit(EntityUid uid, DamageOtherOnHitComponent component, ThrowDoHitEvent args)
|
||||||
@@ -45,7 +41,7 @@ namespace Content.Server.Damage.Systems
|
|||||||
|
|
||||||
if (dmg is { Empty: false })
|
if (dmg is { Empty: false })
|
||||||
{
|
{
|
||||||
_color.RaiseEffect(Color.Red, new List<EntityUid>() { args.Target }, Filter.Pvs(args.Target, entityManager: EntityManager));
|
_color.RaiseEffect(Color.Red, [args.Target], Filter.Pvs(args.Target, entityManager: EntityManager));
|
||||||
}
|
}
|
||||||
|
|
||||||
_guns.PlayImpactSound(args.Target, dmg, null, false);
|
_guns.PlayImpactSound(args.Target, dmg, null, false);
|
||||||
@@ -55,18 +51,4 @@ namespace Content.Server.Damage.Systems
|
|||||||
_sharedCameraRecoil.KickCamera(args.Target, direction);
|
_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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Content.Server.Body.Components;
|
||||||
|
using Content.Server.Body.Systems;
|
||||||
|
using Content.Shared.EntityConditions;
|
||||||
|
using Content.Shared.EntityConditions.Conditions.Body;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityConditions.Conditions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if this entity is both able to breathe and is currently breathing.
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="EntityConditionSystem{T, TCondition}"/>
|
||||||
|
public sealed partial class IsBreathingEntityConditionSystem : EntityConditionSystem<RespiratorComponent, BreathingCondition>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly RespiratorSystem _respirator = default!;
|
||||||
|
protected override void Condition(Entity<RespiratorComponent> entity, ref EntityConditionEvent<BreathingCondition> args)
|
||||||
|
{
|
||||||
|
args.Result = _respirator.IsBreathing(entity.AsNullable());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Content.Server.Body.Components;
|
||||||
|
using Content.Shared.EntityConditions;
|
||||||
|
using Content.Shared.EntityConditions.Conditions.Body;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityConditions.Conditions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if this entity has any of the listed metabolizer types.
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="EntityConditionSystem{T, TCondition}"/>
|
||||||
|
public sealed partial class MetabolizerTypeEntityConditionSystem : EntityConditionSystem<MetabolizerComponent, MetabolizerTypeCondition>
|
||||||
|
{
|
||||||
|
protected override void Condition(Entity<MetabolizerComponent> entity, ref EntityConditionEvent<MetabolizerTypeCondition> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.MetabolizerTypes == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args.Result = entity.Comp.MetabolizerTypes.Overlaps(args.Condition.Type);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using Content.Server.Atmos.EntitySystems;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Atmos;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Atmos;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This effect adjusts a gas at the tile this entity is currently on.
|
||||||
|
/// The amount changed is modified by scale.
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
|
||||||
|
public sealed partial class CreateGasEntityEffectSystem : EntityEffectSystem<TransformComponent, CreateGas>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<TransformComponent> entity, ref EntityEffectEvent<CreateGas> args)
|
||||||
|
{
|
||||||
|
var tileMix = _atmosphere.GetContainingMixture(entity.AsNullable(), false, true);
|
||||||
|
|
||||||
|
tileMix?.AdjustMoles(args.Effect.Gas, args.Scale * args.Effect.Moles);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using Content.Server.Atmos.EntitySystems;
|
||||||
|
using Content.Shared.Atmos.Components;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Atmos;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Atmos;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a number of FireStacks modified by scale to this entity.
|
||||||
|
/// The amount of FireStacks added is modified by scale.
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
|
||||||
|
public sealed partial class FlammableEntityEffectSystem : EntityEffectSystem<FlammableComponent, Flammable>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly FlammableSystem _flammable = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<FlammableComponent> entity, ref EntityEffectEvent<Flammable> args)
|
||||||
|
{
|
||||||
|
// The multiplier is determined by if the entity is already on fire, and if the multiplier for existing FireStacks has a value.
|
||||||
|
// If both of these are true, we use the MultiplierOnExisting value, otherwise we use the standard Multiplier.
|
||||||
|
var multiplier = entity.Comp.FireStacks == 0f || args.Effect.MultiplierOnExisting == null ? args.Effect.Multiplier : args.Effect.MultiplierOnExisting.Value;
|
||||||
|
|
||||||
|
_flammable.AdjustFireStacks(entity, args.Scale * multiplier, entity.Comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using Content.Server.Atmos.EntitySystems;
|
||||||
|
using Content.Shared.Atmos.Components;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Atmos;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Atmos;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets this entity on fire.
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
|
||||||
|
public sealed partial class IngiteEntityEffectSystem : EntityEffectSystem<FlammableComponent, Ignite>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly FlammableSystem _flammable = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<FlammableComponent> entity, ref EntityEffectEvent<Ignite> args)
|
||||||
|
{
|
||||||
|
// TODO: Proper BodySystem Metabolism Effect relay...
|
||||||
|
// TODO: If this fucks over downstream shitmed, I give you full approval to use whatever shitcode method you need to fix it. Metabolism is awful.
|
||||||
|
_flammable.Ignite(entity, entity, flammable: entity.Comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using Content.Server.Body.Components;
|
||||||
|
using Content.Server.Body.Systems;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Body;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Body;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This effect adjusts a respirator's saturation value.
|
||||||
|
/// The saturation adjustment is modified by scale.
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
|
||||||
|
public sealed partial class OxygenateEntityEffectsSystem : EntityEffectSystem<RespiratorComponent, Oxygenate>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly RespiratorSystem _respirator = default!;
|
||||||
|
protected override void Effect(Entity<RespiratorComponent> entity, ref EntityEffectEvent<Oxygenate> args)
|
||||||
|
{
|
||||||
|
_respirator.UpdateSaturation(entity, args.Scale * args.Effect.Factor, entity.Comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Server.Botany.Systems;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
public sealed partial class PlantAdjustHealthEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantAdjustHealth>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly PlantHolderSystem _plantHolder = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantAdjustHealth> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Dead)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entity.Comp.MutationLevel += args.Effect.Amount * entity.Comp.MutationMod;
|
||||||
|
_plantHolder.CheckHealth(entity, entity.Comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Server.Botany.Systems;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
public sealed partial class PlantAdjustMutationLevelEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantAdjustMutationLevel>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly PlantHolderSystem _plantHolder = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantAdjustMutationLevel> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Dead)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entity.Comp.Health += args.Effect.Amount;
|
||||||
|
_plantHolder.CheckHealth(entity, entity.Comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
public sealed partial class PlantAdjustMutationModEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantAdjustMutationMod>
|
||||||
|
{
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantAdjustMutationMod> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Dead)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entity.Comp.MutationMod += args.Effect.Amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Server.Botany.Systems;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
public sealed partial class PlantAdjustNutritionEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantAdjustNutrition>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly PlantHolderSystem _plantHolder = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantAdjustNutrition> args)
|
||||||
|
{
|
||||||
|
_plantHolder.AdjustNutrient(entity, args.Effect.Amount, entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
public sealed partial class PlantAdjustPestsEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantAdjustPests>
|
||||||
|
{
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantAdjustPests> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Dead)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entity.Comp.PestLevel += args.Effect.Amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Server.Botany.Systems;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
public sealed partial class PlantAdjustPotencyEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantAdjustPotency>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly PlantHolderSystem _plantHolder = default!;
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantAdjustPotency> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Dead)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_plantHolder.EnsureUniqueSeed(entity, entity.Comp);
|
||||||
|
entity.Comp.Seed.Potency = Math.Max(entity.Comp.Seed.Potency + args.Effect.Amount, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
public sealed partial class PlantAdjustToxinsEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantAdjustToxins>
|
||||||
|
{
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantAdjustToxins> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Dead)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entity.Comp.Toxins += args.Effect.Amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Server.Botany.Systems;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
public sealed partial class PlantAdjustWaterEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantAdjustWater>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly PlantHolderSystem _plantHolder = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantAdjustWater> args)
|
||||||
|
{
|
||||||
|
_plantHolder.AdjustWater(entity, args.Effect.Amount, entity.Comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
public sealed partial class PlantAdjustWeedsEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantAdjustWeeds>
|
||||||
|
{
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantAdjustWeeds> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Dead)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entity.Comp.WeedLevel += args.Effect.Amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Server.Botany.Systems;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
public sealed partial class PlantAffectGrowthEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantAffectGrowth>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly PlantHolderSystem _plantHolder = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantAffectGrowth> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Dead)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_plantHolder.AffectGrowth(entity, (int)args.Effect.Amount, entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This system mutates an inputted stat for a PlantHolder, only works for floats, integers, and bools.
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
|
||||||
|
public sealed partial class PlantChangeStatEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantChangeStat>
|
||||||
|
{
|
||||||
|
// TODO: This is awful. I do not have the strength to refactor this. I want it gone.
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantChangeStat> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Dead)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var effect = args.Effect;
|
||||||
|
var member = entity.Comp.Seed.GetType().GetField(args.Effect.TargetValue);
|
||||||
|
|
||||||
|
if (member == null)
|
||||||
|
{
|
||||||
|
Log.Error($"{ effect.GetType().Name } Error: Member { args.Effect.TargetValue} not found on { entity.Comp.Seed.GetType().Name }. Did you misspell it?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentValObj = member.GetValue(entity.Comp.Seed);
|
||||||
|
if (currentValObj == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (member.FieldType == typeof(float))
|
||||||
|
{
|
||||||
|
var floatVal = (float)currentValObj;
|
||||||
|
MutateFloat(ref floatVal, args.Effect.MinValue, args.Effect.MaxValue, args.Effect.Steps);
|
||||||
|
member.SetValue(entity.Comp.Seed, floatVal);
|
||||||
|
}
|
||||||
|
else if (member.FieldType == typeof(int))
|
||||||
|
{
|
||||||
|
var intVal = (int)currentValObj;
|
||||||
|
MutateInt(ref intVal, (int)args.Effect.MinValue, (int)args.Effect.MaxValue, args.Effect.Steps);
|
||||||
|
member.SetValue(entity.Comp.Seed, intVal);
|
||||||
|
}
|
||||||
|
else if (member.FieldType == typeof(bool))
|
||||||
|
{
|
||||||
|
var boolVal = (bool)currentValObj;
|
||||||
|
boolVal = !boolVal;
|
||||||
|
member.SetValue(entity.Comp.Seed, boolVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mutate reference 'val' between 'min' and 'max' by pretending the value
|
||||||
|
// is representable by a thermometer code with 'bits' number of bits and
|
||||||
|
// randomly flipping some of them.
|
||||||
|
private void MutateFloat(ref float val, float min, float max, int bits)
|
||||||
|
{
|
||||||
|
if (min == max)
|
||||||
|
{
|
||||||
|
val = min;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Starting number of bits that are high, between 0 and bits.
|
||||||
|
// In other words, it's val mapped linearly from range [min, max] to range [0, bits], and then rounded.
|
||||||
|
int valInt = (int)MathF.Round((val - min) / (max - min) * bits);
|
||||||
|
// val may be outside the range of min/max due to starting prototype values, so clamp.
|
||||||
|
valInt = Math.Clamp(valInt, 0, bits);
|
||||||
|
|
||||||
|
// Probability that the bit flip increases n.
|
||||||
|
// The higher the current value is, the lower the probability of increasing value is, and the higher the probability of decreasive it it.
|
||||||
|
// In other words, it tends to go to the middle.
|
||||||
|
float probIncrease = 1 - (float)valInt / bits;
|
||||||
|
int valIntMutated;
|
||||||
|
if (_random.Prob(probIncrease))
|
||||||
|
{
|
||||||
|
valIntMutated = valInt + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
valIntMutated = valInt - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set value based on mutated thermometer code.
|
||||||
|
float valMutated = Math.Clamp((float)valIntMutated / bits * (max - min) + min, min, max);
|
||||||
|
val = valMutated;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MutateInt(ref int val, int min, int max, int bits)
|
||||||
|
{
|
||||||
|
if (min == max)
|
||||||
|
{
|
||||||
|
val = min;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Starting number of bits that are high, between 0 and bits.
|
||||||
|
// In other words, it's val mapped linearly from range [min, max] to range [0, bits], and then rounded.
|
||||||
|
int valInt = (int)MathF.Round((val - min) / (max - min) * bits);
|
||||||
|
// val may be outside the range of min/max due to starting prototype values, so clamp.
|
||||||
|
valInt = Math.Clamp(valInt, 0, bits);
|
||||||
|
|
||||||
|
// Probability that the bit flip increases n.
|
||||||
|
// The higher the current value is, the lower the probability of increasing value is, and the higher the probability of decreasing it.
|
||||||
|
// In other words, it tends to go to the middle.
|
||||||
|
float probIncrease = 1 - (float)valInt / bits;
|
||||||
|
int valMutated;
|
||||||
|
if (_random.Prob(probIncrease))
|
||||||
|
{
|
||||||
|
valMutated = val + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
valMutated = val - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
valMutated = Math.Clamp(valMutated, min, max);
|
||||||
|
val = valMutated;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
public sealed partial class PlantCryoxadoneEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantCryoxadone>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantCryoxadone> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Dead)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var deviation = 0;
|
||||||
|
var seed = entity.Comp.Seed;
|
||||||
|
if (seed == null)
|
||||||
|
return;
|
||||||
|
if (entity.Comp.Age > seed.Maturation)
|
||||||
|
deviation = (int) Math.Max(seed.Maturation - 1, entity.Comp.Age - _random.Next(7, 10));
|
||||||
|
else
|
||||||
|
deviation = (int) (seed.Maturation / seed.GrowthStages);
|
||||||
|
entity.Comp.Age -= deviation;
|
||||||
|
entity.Comp.LastProduce = entity.Comp.Age;
|
||||||
|
entity.Comp.SkipAging++;
|
||||||
|
entity.Comp.ForceUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Server.Botany.Systems;
|
||||||
|
using Content.Server.Popups;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
public sealed partial class PlantDestroySeedsEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantDestroySeeds>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly PlantHolderSystem _plantHolder = default!;
|
||||||
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantDestroySeeds> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Dead || entity.Comp.Seed.Immutable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (entity.Comp.Seed.Seedless)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_plantHolder.EnsureUniqueSeed(entity, entity.Comp);
|
||||||
|
_popup.PopupEntity(
|
||||||
|
Loc.GetString("botany-plant-seedsdestroyed"),
|
||||||
|
entity,
|
||||||
|
PopupType.SmallCaution
|
||||||
|
);
|
||||||
|
entity.Comp.Seed.Seedless = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Server.Botany.Systems;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
public sealed partial class PlantDiethylamineEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantDiethylamine>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly PlantHolderSystem _plantHolder = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantDiethylamine> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Dead || entity.Comp.Seed.Immutable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_random.Prob(0.1f))
|
||||||
|
{
|
||||||
|
_plantHolder.EnsureUniqueSeed(entity, entity);
|
||||||
|
entity.Comp.Seed!.Lifespan++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_random.Prob(0.1f))
|
||||||
|
{
|
||||||
|
_plantHolder.EnsureUniqueSeed(entity, entity);
|
||||||
|
entity.Comp.Seed!.Endurance++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
public sealed partial class PlantPhalanximineEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantPhalanximine>
|
||||||
|
{
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantPhalanximine> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Dead || entity.Comp.Seed.Immutable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entity.Comp.Seed.Viable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Server.Botany.Systems;
|
||||||
|
using Content.Server.Popups;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
public sealed partial class PlantRestoreSeedsEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantRestoreSeeds>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly PlantHolderSystem _plantHolder = default!;
|
||||||
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantRestoreSeeds> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Dead || entity.Comp.Seed.Immutable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!entity.Comp.Seed.Seedless)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_plantHolder.EnsureUniqueSeed(entity, entity.Comp);
|
||||||
|
_popup.PopupEntity(Loc.GetString("botany-plant-seedsrestored"), entity);
|
||||||
|
entity.Comp.Seed.Seedless = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Server.Botany.Systems;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany.PlantAttributes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This effect directly increases the potency of a PlantHolder's plant provided it exists and isn't dead.
|
||||||
|
/// Potency directly correlates to the size of the plant's produce.
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
|
||||||
|
public sealed partial class RobustHarvestEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, RobustHarvest>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly PlantHolderSystem _plantHolder = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<RobustHarvest> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Dead)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (entity.Comp.Seed.Potency < args.Effect.PotencyLimit)
|
||||||
|
{
|
||||||
|
_plantHolder.EnsureUniqueSeed(entity, entity.Comp);
|
||||||
|
entity.Comp.Seed.Potency = Math.Min(entity.Comp.Seed.Potency + args.Effect.PotencyIncrease, args.Effect.PotencyLimit);
|
||||||
|
|
||||||
|
if (entity.Comp.Seed.Potency > args.Effect.PotencySeedlessThreshold)
|
||||||
|
{
|
||||||
|
entity.Comp.Seed.Seedless = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (entity.Comp.Seed.Yield > 1 && _random.Prob(0.1f))
|
||||||
|
{
|
||||||
|
// Too much of a good thing reduces yield
|
||||||
|
_plantHolder.EnsureUniqueSeed(entity, entity.Comp);
|
||||||
|
entity.Comp.Seed.Yield--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using Content.Server.Botany;
|
||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany;
|
||||||
|
|
||||||
|
public sealed partial class PlantMutateChemicalsEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantMutateChemicals>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantMutateChemicals> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var chemicals = entity.Comp.Seed.Chemicals;
|
||||||
|
var randomChems = _proto.Index(args.Effect.RandomPickBotanyReagent).Fills;
|
||||||
|
|
||||||
|
// Add a random amount of a random chemical to this set of chemicals
|
||||||
|
var pick = _random.Pick(randomChems);
|
||||||
|
var chemicalId = _random.Pick(pick.Reagents);
|
||||||
|
var amount = _random.Next(1, (int)pick.Quantity);
|
||||||
|
var seedChemQuantity = new SeedChemQuantity();
|
||||||
|
if (chemicals.ContainsKey(chemicalId))
|
||||||
|
{
|
||||||
|
seedChemQuantity.Min = chemicals[chemicalId].Min;
|
||||||
|
seedChemQuantity.Max = chemicals[chemicalId].Max + amount;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
seedChemQuantity.Min = 1;
|
||||||
|
seedChemQuantity.Max = 1 + amount;
|
||||||
|
seedChemQuantity.Inherent = false;
|
||||||
|
}
|
||||||
|
var potencyDivisor = (int)Math.Ceiling(100.0f / seedChemQuantity.Max);
|
||||||
|
seedChemQuantity.PotencyDivisor = potencyDivisor;
|
||||||
|
chemicals[chemicalId] = seedChemQuantity;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Shared.Atmos;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany;
|
||||||
|
|
||||||
|
public sealed partial class PlantMutateExudeGasesEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantMutateExudeGases>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantMutateExudeGases> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var gasses = entity.Comp.Seed.ExudeGasses;
|
||||||
|
|
||||||
|
// Add a random amount of a random gas to this gas dictionary
|
||||||
|
float amount = _random.NextFloat(args.Effect.MinValue, args.Effect.MaxValue);
|
||||||
|
var gas = _random.Pick(Enum.GetValues(typeof(Gas)).Cast<Gas>().ToList());
|
||||||
|
|
||||||
|
if (!gasses.TryAdd(gas, amount))
|
||||||
|
{
|
||||||
|
gasses[gas] += amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed partial class PlantMutateConsumeGasesEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantMutateConsumeGases>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantMutateConsumeGases> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var gasses = entity.Comp.Seed.ConsumeGasses;
|
||||||
|
|
||||||
|
// Add a random amount of a random gas to this gas dictionary
|
||||||
|
var amount = _random.NextFloat(args.Effect.MinValue, args.Effect.MaxValue);
|
||||||
|
var gas = _random.Pick(Enum.GetValues(typeof(Gas)).Cast<Gas>().ToList());
|
||||||
|
|
||||||
|
if (!gasses.TryAdd(gas, amount))
|
||||||
|
{
|
||||||
|
gasses[gas] += amount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using Content.Server.Botany;
|
||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany;
|
||||||
|
|
||||||
|
public sealed partial class PlantMutateHarvestEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantMutateHarvest>
|
||||||
|
{
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantMutateHarvest> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (entity.Comp.Seed.HarvestRepeat)
|
||||||
|
{
|
||||||
|
case HarvestType.NoRepeat:
|
||||||
|
entity.Comp.Seed.HarvestRepeat = HarvestType.Repeat;
|
||||||
|
break;
|
||||||
|
case HarvestType.Repeat:
|
||||||
|
entity.Comp.Seed.HarvestRepeat = HarvestType.SelfHarvest;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using Content.Server.Botany;
|
||||||
|
using Content.Server.Botany.Components;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Botany;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Botany;
|
||||||
|
|
||||||
|
public sealed partial class PlantMutateSpeciesChangeEntityEffectSystem : EntityEffectSystem<PlantHolderComponent, PlantMutateSpeciesChange>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PlantHolderComponent> entity, ref EntityEffectEvent<PlantMutateSpeciesChange> args)
|
||||||
|
{
|
||||||
|
if (entity.Comp.Seed == null || entity.Comp.Seed.MutationPrototypes.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var targetProto = _random.Pick(entity.Comp.Seed.MutationPrototypes);
|
||||||
|
_proto.TryIndex(targetProto, out SeedPrototype? protoSeed);
|
||||||
|
|
||||||
|
if (protoSeed == null)
|
||||||
|
{
|
||||||
|
Log.Error($"Seed prototype could not be found: {targetProto}!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.Comp.Seed = entity.Comp.Seed.SpeciesChange(protoSeed);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
using Content.Server.Chat.Systems;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes this entity emote.
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
|
||||||
|
public sealed partial class EmoteEntityEffectSystem : EntityEffectSystem<MetaDataComponent, Emote>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly ChatSystem _chat = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<MetaDataComponent> entity, ref EntityEffectEvent<Emote> args)
|
||||||
|
{
|
||||||
|
if (args.Effect.ShowInChat)
|
||||||
|
_chat.TryEmoteWithChat(entity, args.Effect.EmoteId, ChatTransmitRange.GhostRangeLimit, forceEmote: args.Effect.Force);
|
||||||
|
else
|
||||||
|
_chat.TryEmoteWithoutChat(entity, args.Effect.EmoteId);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
using Content.Server.Ghost.Roles.Components;
|
||||||
|
using Content.Server.Speech.Components;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects;
|
||||||
|
using Content.Shared.Mind.Components;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes this entity sentient. Allows ghost to take it over if it's not already occupied.
|
||||||
|
/// Optionally also allows this entity to speak.
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
|
||||||
|
public sealed partial class MakeSentientEntityEffectSystem : EntityEffectSystem<MetaDataComponent, MakeSentient>
|
||||||
|
{
|
||||||
|
protected override void Effect(Entity<MetaDataComponent> entity, ref EntityEffectEvent<MakeSentient> args)
|
||||||
|
{
|
||||||
|
// Let affected entities speak normally to make this effect different from, say, the "random sentience" event
|
||||||
|
// This also works on entities that already have a mind
|
||||||
|
// We call this before the mind check to allow things like player-controlled mice to be able to benefit from the effect
|
||||||
|
if (args.Effect.AllowSpeech)
|
||||||
|
{
|
||||||
|
RemComp<ReplacementAccentComponent>(entity);
|
||||||
|
// TODO: Make MonkeyAccent a replacement accent and remove MonkeyAccent code-smell.
|
||||||
|
RemComp<MonkeyAccentComponent>(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stops from adding a ghost role to things like people who already have a mind
|
||||||
|
if (TryComp<MindContainerComponent>(entity, out var mindContainer) && mindContainer.HasMind)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Don't add a ghost role to things that already have ghost roles
|
||||||
|
if (TryComp(entity, out GhostRoleComponent? ghostRole))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ghostRole = AddComp<GhostRoleComponent>(entity);
|
||||||
|
EnsureComp<GhostTakeoverAvailableComponent>(entity);
|
||||||
|
|
||||||
|
ghostRole.RoleName = entity.Comp.EntityName;
|
||||||
|
ghostRole.RoleDescription = Loc.GetString("ghost-role-information-cognizine-description");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Content.Server.Polymorph.Components;
|
||||||
|
using Content.Server.Polymorph.Systems;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Polymorphs this entity into another entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
|
||||||
|
public sealed partial class PolymorphEntityEffectSystem : EntityEffectSystem<PolymorphableComponent, Shared.EntityEffects.Effects.Polymorph>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly PolymorphSystem _polymorph = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<PolymorphableComponent> entity, ref EntityEffectEvent<Shared.EntityEffects.Effects.Polymorph> args)
|
||||||
|
{
|
||||||
|
_polymorph.PolymorphEntity(entity, args.Effect.Prototype);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
using Content.Server.Fluids.EntitySystems;
|
||||||
|
using Content.Server.Spreader;
|
||||||
|
using Content.Shared.Chemistry.Components;
|
||||||
|
using Content.Shared.Coordinates.Helpers;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Solution;
|
||||||
|
using Content.Shared.Maps;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Audio.Systems;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Solution;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This effect creates smoke at this solution's position.
|
||||||
|
/// The amount of smoke created is modified by scale.
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
|
||||||
|
public sealed partial class AreaReactionEntityEffectsSystem : EntityEffectSystem<SolutionComponent, AreaReactionEffect>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
|
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _xform = default!;
|
||||||
|
[Dependency] private readonly SmokeSystem _smoke = default!;
|
||||||
|
[Dependency] private readonly SpreaderSystem _spreader = default!;
|
||||||
|
[Dependency] private readonly TurfSystem _turf = default!;
|
||||||
|
|
||||||
|
// TODO: A sane way to make Smoke without a solution.
|
||||||
|
protected override void Effect(Entity<SolutionComponent> entity, ref EntityEffectEvent<AreaReactionEffect> args)
|
||||||
|
{
|
||||||
|
var xform = Transform(entity);
|
||||||
|
var mapCoords = _xform.GetMapCoordinates(entity);
|
||||||
|
var spreadAmount = (int) Math.Max(0, Math.Ceiling(args.Scale / args.Effect.OverflowThreshold));
|
||||||
|
var effect = args.Effect;
|
||||||
|
|
||||||
|
if (!_mapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid) ||
|
||||||
|
!_map.TryGetTileRef(gridUid, grid, xform.Coordinates, out var tileRef))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_spreader.RequiresFloorToSpread(effect.PrototypeId.ToString()) && _turf.IsSpace(tileRef))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var coords = _map.MapToGrid(gridUid, mapCoords);
|
||||||
|
var ent = Spawn(args.Effect.PrototypeId, coords.SnapToGrid());
|
||||||
|
|
||||||
|
_smoke.StartSmoke(ent, entity.Comp.Solution, args.Effect.Duration, spreadAmount);
|
||||||
|
|
||||||
|
_audio.PlayPvs(args.Effect.Sound, entity, AudioParams.Default.WithVariation(0.25f));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using Content.Server.Explosion.EntitySystems;
|
||||||
|
using Content.Shared.EntityEffects;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Transform;
|
||||||
|
|
||||||
|
namespace Content.Server.EntityEffects.Effects.Transform;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an explosion at this entity's position.
|
||||||
|
/// Intensity is modified by scale.
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
|
||||||
|
public sealed partial class ExplosionEntityEffectSystem : EntityEffectSystem<TransformComponent, ExplosionEffect>
|
||||||
|
{
|
||||||
|
[Dependency] private readonly ExplosionSystem _explosion = default!;
|
||||||
|
|
||||||
|
protected override void Effect(Entity<TransformComponent> entity, ref EntityEffectEvent<ExplosionEffect> args)
|
||||||
|
{
|
||||||
|
var intensity = MathF.Min(args.Effect.IntensityPerUnit * args.Scale, args.Effect.MaxTotalIntensity);
|
||||||
|
|
||||||
|
_explosion.QueueExplosion(
|
||||||
|
entity,
|
||||||
|
args.Effect.ExplosionType,
|
||||||
|
intensity,
|
||||||
|
args.Effect.IntensitySlope,
|
||||||
|
args.Effect.MaxIntensity,
|
||||||
|
args.Effect.TileBreakScale);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,976 +0,0 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
|
||||||
using Content.Server.Atmos.EntitySystems;
|
|
||||||
using Content.Server.Body.Components;
|
|
||||||
using Content.Server.Body.Systems;
|
|
||||||
using Content.Server.Botany.Components;
|
|
||||||
using Content.Server.Botany.Systems;
|
|
||||||
using Content.Server.Botany;
|
|
||||||
using Content.Server.Chat.Systems;
|
|
||||||
using Content.Server.Emp;
|
|
||||||
using Content.Server.Explosion.EntitySystems;
|
|
||||||
using Content.Server.Fluids.EntitySystems;
|
|
||||||
using Content.Server.Ghost.Roles.Components;
|
|
||||||
using Content.Server.Polymorph.Components;
|
|
||||||
using Content.Server.Polymorph.Systems;
|
|
||||||
using Content.Server.Speech.Components;
|
|
||||||
using Content.Server.Spreader;
|
|
||||||
using Content.Server.Temperature.Components;
|
|
||||||
using Content.Server.Temperature.Systems;
|
|
||||||
using Content.Server.Zombies;
|
|
||||||
using Content.Shared.Atmos;
|
|
||||||
using Content.Shared.Atmos.Components;
|
|
||||||
using Content.Shared.Body.Components;
|
|
||||||
using Content.Shared.Coordinates.Helpers;
|
|
||||||
using Content.Shared.EntityEffects.EffectConditions;
|
|
||||||
using Content.Shared.EntityEffects.Effects.PlantMetabolism;
|
|
||||||
using Content.Shared.EntityEffects.Effects;
|
|
||||||
using Content.Shared.EntityEffects;
|
|
||||||
using Content.Shared.Flash;
|
|
||||||
using Content.Shared.Maps;
|
|
||||||
using Content.Shared.Medical;
|
|
||||||
using Content.Shared.Mind.Components;
|
|
||||||
using Content.Shared.Popups;
|
|
||||||
using Content.Shared.Random;
|
|
||||||
using Content.Shared.Traits.Assorted;
|
|
||||||
using Content.Shared.Zombies;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.Audio.Systems;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Random;
|
|
||||||
|
|
||||||
using TemperatureCondition = Content.Shared.EntityEffects.EffectConditions.Temperature; // disambiguate the namespace
|
|
||||||
using PolymorphEffect = Content.Shared.EntityEffects.Effects.Polymorph;
|
|
||||||
|
|
||||||
namespace Content.Server.EntityEffects;
|
|
||||||
|
|
||||||
public sealed class EntityEffectSystem : EntitySystem
|
|
||||||
{
|
|
||||||
private static readonly ProtoId<WeightedRandomFillSolutionPrototype> RandomPickBotanyReagent = "RandomPickBotanyReagent";
|
|
||||||
|
|
||||||
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
|
|
||||||
[Dependency] private readonly BloodstreamSystem _bloodstream = default!;
|
|
||||||
[Dependency] private readonly ChatSystem _chat = default!;
|
|
||||||
[Dependency] private readonly EmpSystem _emp = default!;
|
|
||||||
[Dependency] private readonly ExplosionSystem _explosion = default!;
|
|
||||||
[Dependency] private readonly FlammableSystem _flammable = default!;
|
|
||||||
[Dependency] private readonly SharedFlashSystem _flash = default!;
|
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
|
||||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
|
||||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
|
||||||
[Dependency] private readonly MutationSystem _mutation = default!;
|
|
||||||
[Dependency] private readonly NarcolepsySystem _narcolepsy = default!;
|
|
||||||
[Dependency] private readonly PlantHolderSystem _plantHolder = default!;
|
|
||||||
[Dependency] private readonly PolymorphSystem _polymorph = default!;
|
|
||||||
[Dependency] private readonly RespiratorSystem _respirator = default!;
|
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
|
||||||
[Dependency] private readonly SharedPointLightSystem _pointLight = default!;
|
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
|
||||||
[Dependency] private readonly SmokeSystem _smoke = default!;
|
|
||||||
[Dependency] private readonly SpreaderSystem _spreader = default!;
|
|
||||||
[Dependency] private readonly TemperatureSystem _temperature = default!;
|
|
||||||
[Dependency] private readonly SharedTransformSystem _xform = default!;
|
|
||||||
[Dependency] private readonly VomitSystem _vomit = default!;
|
|
||||||
[Dependency] private readonly TurfSystem _turf = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<CheckEntityEffectConditionEvent<TemperatureCondition>>(OnCheckTemperature);
|
|
||||||
SubscribeLocalEvent<CheckEntityEffectConditionEvent<Breathing>>(OnCheckBreathing);
|
|
||||||
SubscribeLocalEvent<CheckEntityEffectConditionEvent<OrganType>>(OnCheckOrganType);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustHealth>>(OnExecutePlantAdjustHealth);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustMutationLevel>>(OnExecutePlantAdjustMutationLevel);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustMutationMod>>(OnExecutePlantAdjustMutationMod);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustNutrition>>(OnExecutePlantAdjustNutrition);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustPests>>(OnExecutePlantAdjustPests);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustPotency>>(OnExecutePlantAdjustPotency);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustToxins>>(OnExecutePlantAdjustToxins);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustWater>>(OnExecutePlantAdjustWater);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAdjustWeeds>>(OnExecutePlantAdjustWeeds);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantAffectGrowth>>(OnExecutePlantAffectGrowth);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantChangeStat>>(OnExecutePlantChangeStat);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantCryoxadone>>(OnExecutePlantCryoxadone);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantDestroySeeds>>(OnExecutePlantDestroySeeds);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantDiethylamine>>(OnExecutePlantDiethylamine);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantPhalanximine>>(OnExecutePlantPhalanximine);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantRestoreSeeds>>(OnExecutePlantRestoreSeeds);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<RobustHarvest>>(OnExecuteRobustHarvest);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<AdjustTemperature>>(OnExecuteAdjustTemperature);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<AreaReactionEffect>>(OnExecuteAreaReactionEffect);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<CauseZombieInfection>>(OnExecuteCauseZombieInfection);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<ChemCleanBloodstream>>(OnExecuteChemCleanBloodstream);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<ChemVomit>>(OnExecuteChemVomit);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<CreateEntityReactionEffect>>(OnExecuteCreateEntityReactionEffect);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<CreateGas>>(OnExecuteCreateGas);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<CureZombieInfection>>(OnExecuteCureZombieInfection);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<Emote>>(OnExecuteEmote);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<EmpReactionEffect>>(OnExecuteEmpReactionEffect);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<ExplosionReactionEffect>>(OnExecuteExplosionReactionEffect);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<FlammableReaction>>(OnExecuteFlammableReaction);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<FlashReactionEffect>>(OnExecuteFlashReactionEffect);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<Ignite>>(OnExecuteIgnite);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<MakeSentient>>(OnExecuteMakeSentient);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<ModifyBleedAmount>>(OnExecuteModifyBleedAmount);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<ModifyBloodLevel>>(OnExecuteModifyBloodLevel);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<ModifyLungGas>>(OnExecuteModifyLungGas);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<Oxygenate>>(OnExecuteOxygenate);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantMutateChemicals>>(OnExecutePlantMutateChemicals);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantMutateConsumeGasses>>(OnExecutePlantMutateConsumeGasses);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantMutateExudeGasses>>(OnExecutePlantMutateExudeGasses);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantMutateHarvest>>(OnExecutePlantMutateHarvest);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PlantSpeciesChange>>(OnExecutePlantSpeciesChange);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<PolymorphEffect>>(OnExecutePolymorph);
|
|
||||||
SubscribeLocalEvent<ExecuteEntityEffectEvent<ResetNarcolepsy>>(OnExecuteResetNarcolepsy);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCheckTemperature(ref CheckEntityEffectConditionEvent<TemperatureCondition> args)
|
|
||||||
{
|
|
||||||
args.Result = false;
|
|
||||||
if (TryComp(args.Args.TargetEntity, out TemperatureComponent? temp))
|
|
||||||
{
|
|
||||||
if (temp.CurrentTemperature >= args.Condition.Min && temp.CurrentTemperature <= args.Condition.Max)
|
|
||||||
args.Result = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCheckBreathing(ref CheckEntityEffectConditionEvent<Breathing> args)
|
|
||||||
{
|
|
||||||
if (!TryComp(args.Args.TargetEntity, out RespiratorComponent? respiratorComp))
|
|
||||||
{
|
|
||||||
args.Result = !args.Condition.IsBreathing;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var breathingState = _respirator.IsBreathing((args.Args.TargetEntity, respiratorComp));
|
|
||||||
args.Result = args.Condition.IsBreathing == breathingState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnCheckOrganType(ref CheckEntityEffectConditionEvent<OrganType> args)
|
|
||||||
{
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
{
|
|
||||||
if (reagentArgs.OrganEntity == null)
|
|
||||||
{
|
|
||||||
args.Result = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.Result = OrganCondition(args.Condition, reagentArgs.OrganEntity.Value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Someone needs to figure out how to do this for non-reagent effects.
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool OrganCondition(OrganType condition, Entity<MetabolizerComponent?> metabolizer)
|
|
||||||
{
|
|
||||||
metabolizer.Comp ??= EntityManager.GetComponentOrNull<MetabolizerComponent>(metabolizer.Owner);
|
|
||||||
if (metabolizer.Comp != null
|
|
||||||
&& metabolizer.Comp.MetabolizerTypes != null
|
|
||||||
&& metabolizer.Comp.MetabolizerTypes.Contains(condition.Type))
|
|
||||||
return condition.ShouldHave;
|
|
||||||
return !condition.ShouldHave;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if the plant holder can metabolize the reagent or not. Checks if it has an alive plant by default.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="plantHolder">The entity holding the plant</param>
|
|
||||||
/// <param name="plantHolderComponent">The plant holder component</param>
|
|
||||||
/// <param name="entityManager">The entity manager</param>
|
|
||||||
/// <param name="mustHaveAlivePlant">Whether to check if it has an alive plant or not</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private bool CanMetabolizePlant(EntityUid plantHolder, [NotNullWhen(true)] out PlantHolderComponent? plantHolderComponent,
|
|
||||||
bool mustHaveAlivePlant = true, bool mustHaveMutableSeed = false)
|
|
||||||
{
|
|
||||||
plantHolderComponent = null;
|
|
||||||
|
|
||||||
if (!TryComp(plantHolder, out plantHolderComponent))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (mustHaveAlivePlant && (plantHolderComponent.Seed == null || plantHolderComponent.Dead))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (mustHaveMutableSeed && (plantHolderComponent.Seed == null || plantHolderComponent.Seed.Immutable))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantAdjustHealth(ref ExecuteEntityEffectEvent<PlantAdjustHealth> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
plantHolderComp.Health += args.Effect.Amount;
|
|
||||||
_plantHolder.CheckHealth(args.Args.TargetEntity, plantHolderComp);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantAdjustMutationLevel(ref ExecuteEntityEffectEvent<PlantAdjustMutationLevel> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
plantHolderComp.MutationLevel += args.Effect.Amount * plantHolderComp.MutationMod;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantAdjustMutationMod(ref ExecuteEntityEffectEvent<PlantAdjustMutationMod> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
plantHolderComp.MutationMod += args.Effect.Amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantAdjustNutrition(ref ExecuteEntityEffectEvent<PlantAdjustNutrition> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveAlivePlant: false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_plantHolder.AdjustNutrient(args.Args.TargetEntity, args.Effect.Amount, plantHolderComp);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantAdjustPests(ref ExecuteEntityEffectEvent<PlantAdjustPests> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
plantHolderComp.PestLevel += args.Effect.Amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantAdjustPotency(ref ExecuteEntityEffectEvent<PlantAdjustPotency> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (plantHolderComp.Seed == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp);
|
|
||||||
plantHolderComp.Seed.Potency = Math.Max(plantHolderComp.Seed.Potency + args.Effect.Amount, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantAdjustToxins(ref ExecuteEntityEffectEvent<PlantAdjustToxins> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
plantHolderComp.Toxins += args.Effect.Amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantAdjustWater(ref ExecuteEntityEffectEvent<PlantAdjustWater> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveAlivePlant: false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_plantHolder.AdjustWater(args.Args.TargetEntity, args.Effect.Amount, plantHolderComp);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantAdjustWeeds(ref ExecuteEntityEffectEvent<PlantAdjustWeeds> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
plantHolderComp.WeedLevel += args.Effect.Amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantAffectGrowth(ref ExecuteEntityEffectEvent<PlantAffectGrowth> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_plantHolder.AffectGrowth(args.Args.TargetEntity, (int) args.Effect.Amount, plantHolderComp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mutate reference 'val' between 'min' and 'max' by pretending the value
|
|
||||||
// is representable by a thermometer code with 'bits' number of bits and
|
|
||||||
// randomly flipping some of them.
|
|
||||||
private void MutateFloat(ref float val, float min, float max, int bits)
|
|
||||||
{
|
|
||||||
if (min == max)
|
|
||||||
{
|
|
||||||
val = min;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Starting number of bits that are high, between 0 and bits.
|
|
||||||
// In other words, it's val mapped linearly from range [min, max] to range [0, bits], and then rounded.
|
|
||||||
int valInt = (int)MathF.Round((val - min) / (max - min) * bits);
|
|
||||||
// val may be outside the range of min/max due to starting prototype values, so clamp.
|
|
||||||
valInt = Math.Clamp(valInt, 0, bits);
|
|
||||||
|
|
||||||
// Probability that the bit flip increases n.
|
|
||||||
// The higher the current value is, the lower the probability of increasing value is, and the higher the probability of decreasive it it.
|
|
||||||
// In other words, it tends to go to the middle.
|
|
||||||
float probIncrease = 1 - (float)valInt / bits;
|
|
||||||
int valIntMutated;
|
|
||||||
if (_random.Prob(probIncrease))
|
|
||||||
{
|
|
||||||
valIntMutated = valInt + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
valIntMutated = valInt - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set value based on mutated thermometer code.
|
|
||||||
float valMutated = Math.Clamp((float)valIntMutated / bits * (max - min) + min, min, max);
|
|
||||||
val = valMutated;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MutateInt(ref int val, int min, int max, int bits)
|
|
||||||
{
|
|
||||||
if (min == max)
|
|
||||||
{
|
|
||||||
val = min;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Starting number of bits that are high, between 0 and bits.
|
|
||||||
// In other words, it's val mapped linearly from range [min, max] to range [0, bits], and then rounded.
|
|
||||||
int valInt = (int)MathF.Round((val - min) / (max - min) * bits);
|
|
||||||
// val may be outside the range of min/max due to starting prototype values, so clamp.
|
|
||||||
valInt = Math.Clamp(valInt, 0, bits);
|
|
||||||
|
|
||||||
// Probability that the bit flip increases n.
|
|
||||||
// The higher the current value is, the lower the probability of increasing value is, and the higher the probability of decreasing it.
|
|
||||||
// In other words, it tends to go to the middle.
|
|
||||||
float probIncrease = 1 - (float)valInt / bits;
|
|
||||||
int valMutated;
|
|
||||||
if (_random.Prob(probIncrease))
|
|
||||||
{
|
|
||||||
valMutated = val + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
valMutated = val - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
valMutated = Math.Clamp(valMutated, min, max);
|
|
||||||
val = valMutated;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantChangeStat(ref ExecuteEntityEffectEvent<PlantChangeStat> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (plantHolderComp.Seed == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var member = plantHolderComp.Seed.GetType().GetField(args.Effect.TargetValue);
|
|
||||||
|
|
||||||
if (member == null)
|
|
||||||
{
|
|
||||||
_mutation.Log.Error(args.Effect.GetType().Name + " Error: Member " + args.Effect.TargetValue + " not found on " + plantHolderComp.Seed.GetType().Name + ". Did you misspell it?");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentValObj = member.GetValue(plantHolderComp.Seed);
|
|
||||||
if (currentValObj == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (member.FieldType == typeof(float))
|
|
||||||
{
|
|
||||||
var floatVal = (float)currentValObj;
|
|
||||||
MutateFloat(ref floatVal, args.Effect.MinValue, args.Effect.MaxValue, args.Effect.Steps);
|
|
||||||
member.SetValue(plantHolderComp.Seed, floatVal);
|
|
||||||
}
|
|
||||||
else if (member.FieldType == typeof(int))
|
|
||||||
{
|
|
||||||
var intVal = (int)currentValObj;
|
|
||||||
MutateInt(ref intVal, (int)args.Effect.MinValue, (int)args.Effect.MaxValue, args.Effect.Steps);
|
|
||||||
member.SetValue(plantHolderComp.Seed, intVal);
|
|
||||||
}
|
|
||||||
else if (member.FieldType == typeof(bool))
|
|
||||||
{
|
|
||||||
var boolVal = (bool)currentValObj;
|
|
||||||
boolVal = !boolVal;
|
|
||||||
member.SetValue(plantHolderComp.Seed, boolVal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantCryoxadone(ref ExecuteEntityEffectEvent<PlantCryoxadone> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var deviation = 0;
|
|
||||||
var seed = plantHolderComp.Seed;
|
|
||||||
if (seed == null)
|
|
||||||
return;
|
|
||||||
if (plantHolderComp.Age > seed.Maturation)
|
|
||||||
deviation = (int) Math.Max(seed.Maturation - 1, plantHolderComp.Age - _random.Next(7, 10));
|
|
||||||
else
|
|
||||||
deviation = (int) (seed.Maturation / seed.GrowthStages);
|
|
||||||
plantHolderComp.Age -= deviation;
|
|
||||||
plantHolderComp.LastProduce = plantHolderComp.Age;
|
|
||||||
plantHolderComp.SkipAging++;
|
|
||||||
plantHolderComp.ForceUpdate = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantDestroySeeds(ref ExecuteEntityEffectEvent<PlantDestroySeeds> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveMutableSeed: true))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (plantHolderComp.Seed!.Seedless == false)
|
|
||||||
{
|
|
||||||
_plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp);
|
|
||||||
_popup.PopupEntity(
|
|
||||||
Loc.GetString("botany-plant-seedsdestroyed"),
|
|
||||||
args.Args.TargetEntity,
|
|
||||||
PopupType.SmallCaution
|
|
||||||
);
|
|
||||||
plantHolderComp.Seed.Seedless = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantDiethylamine(ref ExecuteEntityEffectEvent<PlantDiethylamine> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveMutableSeed: true))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_random.Prob(0.1f))
|
|
||||||
{
|
|
||||||
_plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp);
|
|
||||||
plantHolderComp.Seed!.Lifespan++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_random.Prob(0.1f))
|
|
||||||
{
|
|
||||||
_plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp);
|
|
||||||
plantHolderComp.Seed!.Endurance++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantPhalanximine(ref ExecuteEntityEffectEvent<PlantPhalanximine> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveMutableSeed: true))
|
|
||||||
return;
|
|
||||||
|
|
||||||
plantHolderComp.Seed!.Viable = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantRestoreSeeds(ref ExecuteEntityEffectEvent<PlantRestoreSeeds> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp, mustHaveMutableSeed: true))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (plantHolderComp.Seed!.Seedless)
|
|
||||||
{
|
|
||||||
_plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp);
|
|
||||||
_popup.PopupEntity(Loc.GetString("botany-plant-seedsrestored"), args.Args.TargetEntity);
|
|
||||||
plantHolderComp.Seed.Seedless = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteRobustHarvest(ref ExecuteEntityEffectEvent<RobustHarvest> args)
|
|
||||||
{
|
|
||||||
if (!CanMetabolizePlant(args.Args.TargetEntity, out var plantHolderComp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (plantHolderComp.Seed == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (plantHolderComp.Seed.Potency < args.Effect.PotencyLimit)
|
|
||||||
{
|
|
||||||
_plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp);
|
|
||||||
plantHolderComp.Seed.Potency = Math.Min(plantHolderComp.Seed.Potency + args.Effect.PotencyIncrease, args.Effect.PotencyLimit);
|
|
||||||
|
|
||||||
if (plantHolderComp.Seed.Potency > args.Effect.PotencySeedlessThreshold)
|
|
||||||
{
|
|
||||||
plantHolderComp.Seed.Seedless = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (plantHolderComp.Seed.Yield > 1 && _random.Prob(0.1f))
|
|
||||||
{
|
|
||||||
// Too much of a good thing reduces yield
|
|
||||||
_plantHolder.EnsureUniqueSeed(args.Args.TargetEntity, plantHolderComp);
|
|
||||||
plantHolderComp.Seed.Yield--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteAdjustTemperature(ref ExecuteEntityEffectEvent<AdjustTemperature> args)
|
|
||||||
{
|
|
||||||
if (TryComp(args.Args.TargetEntity, out TemperatureComponent? temp))
|
|
||||||
{
|
|
||||||
var amount = args.Effect.Amount;
|
|
||||||
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
{
|
|
||||||
amount *= reagentArgs.Scale.Float();
|
|
||||||
}
|
|
||||||
|
|
||||||
_temperature.ChangeHeat(args.Args.TargetEntity, amount, true, temp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteAreaReactionEffect(ref ExecuteEntityEffectEvent<AreaReactionEffect> args)
|
|
||||||
{
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
{
|
|
||||||
if (reagentArgs.Source == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var spreadAmount = (int) Math.Max(0, Math.Ceiling((reagentArgs.Quantity / args.Effect.OverflowThreshold).Float()));
|
|
||||||
var splitSolution = reagentArgs.Source.SplitSolution(reagentArgs.Source.Volume);
|
|
||||||
var transform = Comp<TransformComponent>(reagentArgs.TargetEntity);
|
|
||||||
var mapCoords = _xform.GetMapCoordinates(reagentArgs.TargetEntity, xform: transform);
|
|
||||||
|
|
||||||
if (!_mapManager.TryFindGridAt(mapCoords, out var gridUid, out var grid) ||
|
|
||||||
!_map.TryGetTileRef(gridUid, grid, transform.Coordinates, out var tileRef))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_spreader.RequiresFloorToSpread(args.Effect.PrototypeId) && _turf.IsSpace(tileRef))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var coords = _map.MapToGrid(gridUid, mapCoords);
|
|
||||||
var ent = Spawn(args.Effect.PrototypeId, coords.SnapToGrid());
|
|
||||||
|
|
||||||
_smoke.StartSmoke(ent, splitSolution, args.Effect.Duration, spreadAmount);
|
|
||||||
|
|
||||||
_audio.PlayPvs(args.Effect.Sound, reagentArgs.TargetEntity, AudioParams.Default.WithVariation(0.25f));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Someone needs to figure out how to do this for non-reagent effects.
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteCauseZombieInfection(ref ExecuteEntityEffectEvent<CauseZombieInfection> args)
|
|
||||||
{
|
|
||||||
EnsureComp<ZombifyOnDeathComponent>(args.Args.TargetEntity);
|
|
||||||
EnsureComp<PendingZombieComponent>(args.Args.TargetEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteChemCleanBloodstream(ref ExecuteEntityEffectEvent<ChemCleanBloodstream> args)
|
|
||||||
{
|
|
||||||
var cleanseRate = args.Effect.CleanseRate;
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
{
|
|
||||||
if (reagentArgs.Source == null || reagentArgs.Reagent == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
cleanseRate *= reagentArgs.Scale.Float();
|
|
||||||
_bloodstream.FlushChemicals(args.Args.TargetEntity, reagentArgs.Reagent, cleanseRate);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_bloodstream.FlushChemicals(args.Args.TargetEntity, null, cleanseRate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteChemVomit(ref ExecuteEntityEffectEvent<ChemVomit> args)
|
|
||||||
{
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
if (reagentArgs.Scale != 1f)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_vomit.Vomit(args.Args.TargetEntity, args.Effect.ThirstAmount, args.Effect.HungerAmount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteCreateEntityReactionEffect(ref ExecuteEntityEffectEvent<CreateEntityReactionEffect> args)
|
|
||||||
{
|
|
||||||
var transform = Comp<TransformComponent>(args.Args.TargetEntity);
|
|
||||||
var quantity = (int)args.Effect.Number;
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
quantity *= reagentArgs.Quantity.Int();
|
|
||||||
|
|
||||||
for (var i = 0; i < quantity; i++)
|
|
||||||
{
|
|
||||||
var uid = Spawn(args.Effect.Entity, _xform.GetMapCoordinates(args.Args.TargetEntity, xform: transform));
|
|
||||||
_xform.AttachToGridOrMap(uid);
|
|
||||||
|
|
||||||
// TODO figure out how to properly spawn inside of containers
|
|
||||||
// e.g. cheese:
|
|
||||||
// if the user is holding a bowl milk & enzyme, should drop to floor, not attached to the user.
|
|
||||||
// if reaction happens in a backpack, should insert cheese into backpack.
|
|
||||||
// --> if it doesn't fit, iterate through parent storage until it attaches to the grid (again, DON'T attach to players).
|
|
||||||
// if the reaction happens INSIDE a stomach? the bloodstream? I have no idea how to handle that.
|
|
||||||
// presumably having cheese materialize inside of your blood would have "disadvantages".
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteCreateGas(ref ExecuteEntityEffectEvent<CreateGas> args)
|
|
||||||
{
|
|
||||||
var tileMix = _atmosphere.GetContainingMixture(args.Args.TargetEntity, false, true);
|
|
||||||
|
|
||||||
if (tileMix != null)
|
|
||||||
{
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
{
|
|
||||||
tileMix.AdjustMoles(args.Effect.Gas, reagentArgs.Quantity.Float() * args.Effect.Multiplier);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tileMix.AdjustMoles(args.Effect.Gas, args.Effect.Multiplier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteCureZombieInfection(ref ExecuteEntityEffectEvent<CureZombieInfection> args)
|
|
||||||
{
|
|
||||||
if (HasComp<IncurableZombieComponent>(args.Args.TargetEntity))
|
|
||||||
return;
|
|
||||||
|
|
||||||
RemComp<ZombifyOnDeathComponent>(args.Args.TargetEntity);
|
|
||||||
RemComp<PendingZombieComponent>(args.Args.TargetEntity);
|
|
||||||
|
|
||||||
if (args.Effect.Innoculate)
|
|
||||||
{
|
|
||||||
EnsureComp<ZombieImmuneComponent>(args.Args.TargetEntity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteEmote(ref ExecuteEntityEffectEvent<Emote> args)
|
|
||||||
{
|
|
||||||
if (args.Effect.EmoteId == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (args.Effect.ShowInChat)
|
|
||||||
_chat.TryEmoteWithChat(args.Args.TargetEntity, args.Effect.EmoteId, ChatTransmitRange.GhostRangeLimit, forceEmote: args.Effect.Force);
|
|
||||||
else
|
|
||||||
_chat.TryEmoteWithoutChat(args.Args.TargetEntity, args.Effect.EmoteId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteEmpReactionEffect(ref ExecuteEntityEffectEvent<EmpReactionEffect> args)
|
|
||||||
{
|
|
||||||
var transform = Comp<TransformComponent>(args.Args.TargetEntity);
|
|
||||||
|
|
||||||
var range = args.Effect.EmpRangePerUnit;
|
|
||||||
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
{
|
|
||||||
range = MathF.Min((float) (reagentArgs.Quantity * args.Effect.EmpRangePerUnit), args.Effect.EmpMaxRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
_emp.EmpPulse(_xform.GetMapCoordinates(args.Args.TargetEntity, xform: transform),
|
|
||||||
range,
|
|
||||||
args.Effect.EnergyConsumption,
|
|
||||||
args.Effect.DisableDuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteExplosionReactionEffect(ref ExecuteEntityEffectEvent<ExplosionReactionEffect> args)
|
|
||||||
{
|
|
||||||
var intensity = args.Effect.IntensityPerUnit;
|
|
||||||
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
{
|
|
||||||
intensity = MathF.Min((float) reagentArgs.Quantity * args.Effect.IntensityPerUnit, args.Effect.MaxTotalIntensity);
|
|
||||||
}
|
|
||||||
|
|
||||||
_explosion.QueueExplosion(
|
|
||||||
args.Args.TargetEntity,
|
|
||||||
args.Effect.ExplosionType,
|
|
||||||
intensity,
|
|
||||||
args.Effect.IntensitySlope,
|
|
||||||
args.Effect.MaxIntensity,
|
|
||||||
args.Effect.TileBreakScale);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteFlammableReaction(ref ExecuteEntityEffectEvent<FlammableReaction> args)
|
|
||||||
{
|
|
||||||
if (!TryComp(args.Args.TargetEntity, out FlammableComponent? flammable))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Sets the multiplier for FireStacks to MultiplierOnExisting is 0 or greater and target already has FireStacks
|
|
||||||
var multiplier = flammable.FireStacks != 0f && args.Effect.MultiplierOnExisting >= 0 ? args.Effect.MultiplierOnExisting : args.Effect.Multiplier;
|
|
||||||
var quantity = 1f;
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
{
|
|
||||||
quantity = reagentArgs.Quantity.Float();
|
|
||||||
_flammable.AdjustFireStacks(args.Args.TargetEntity, quantity * multiplier, flammable);
|
|
||||||
if (reagentArgs.Reagent != null)
|
|
||||||
reagentArgs.Source?.RemoveReagent(reagentArgs.Reagent.ID, reagentArgs.Quantity);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_flammable.AdjustFireStacks(args.Args.TargetEntity, multiplier, flammable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteFlashReactionEffect(ref ExecuteEntityEffectEvent<FlashReactionEffect> args)
|
|
||||||
{
|
|
||||||
var transform = Comp<TransformComponent>(args.Args.TargetEntity);
|
|
||||||
|
|
||||||
var range = 1f;
|
|
||||||
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
range = MathF.Min((float)(reagentArgs.Quantity * args.Effect.RangePerUnit), args.Effect.MaxRange);
|
|
||||||
|
|
||||||
_flash.FlashArea(
|
|
||||||
args.Args.TargetEntity,
|
|
||||||
null,
|
|
||||||
range,
|
|
||||||
args.Effect.Duration,
|
|
||||||
slowTo: args.Effect.SlowTo,
|
|
||||||
sound: args.Effect.Sound);
|
|
||||||
|
|
||||||
if (args.Effect.FlashEffectPrototype == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var uid = EntityManager.SpawnEntity(args.Effect.FlashEffectPrototype, _xform.GetMapCoordinates(transform));
|
|
||||||
_xform.AttachToGridOrMap(uid);
|
|
||||||
|
|
||||||
if (!TryComp<PointLightComponent>(uid, out var pointLightComp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_pointLight.SetRadius(uid, MathF.Max(1.1f, range), pointLightComp);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteIgnite(ref ExecuteEntityEffectEvent<Ignite> args)
|
|
||||||
{
|
|
||||||
if (!TryComp(args.Args.TargetEntity, out FlammableComponent? flammable))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
{
|
|
||||||
_flammable.Ignite(reagentArgs.TargetEntity, reagentArgs.OrganEntity ?? reagentArgs.TargetEntity, flammable: flammable);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_flammable.Ignite(args.Args.TargetEntity, args.Args.TargetEntity, flammable: flammable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteMakeSentient(ref ExecuteEntityEffectEvent<MakeSentient> args)
|
|
||||||
{
|
|
||||||
var uid = args.Args.TargetEntity;
|
|
||||||
|
|
||||||
// Let affected entities speak normally to make this effect different from, say, the "random sentience" event
|
|
||||||
// This also works on entities that already have a mind
|
|
||||||
// We call this before the mind check to allow things like player-controlled mice to be able to benefit from the effect
|
|
||||||
RemComp<ReplacementAccentComponent>(uid);
|
|
||||||
RemComp<MonkeyAccentComponent>(uid);
|
|
||||||
|
|
||||||
// Stops from adding a ghost role to things like people who already have a mind
|
|
||||||
if (TryComp<MindContainerComponent>(uid, out var mindContainer) && mindContainer.HasMind)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't add a ghost role to things that already have ghost roles
|
|
||||||
if (TryComp(uid, out GhostRoleComponent? ghostRole))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ghostRole = AddComp<GhostRoleComponent>(uid);
|
|
||||||
EnsureComp<GhostTakeoverAvailableComponent>(uid);
|
|
||||||
|
|
||||||
var entityData = Comp<MetaDataComponent>(uid);
|
|
||||||
ghostRole.RoleName = entityData.EntityName;
|
|
||||||
ghostRole.RoleDescription = Loc.GetString("ghost-role-information-cognizine-description");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteModifyBleedAmount(ref ExecuteEntityEffectEvent<ModifyBleedAmount> args)
|
|
||||||
{
|
|
||||||
if (TryComp<BloodstreamComponent>(args.Args.TargetEntity, out var blood))
|
|
||||||
{
|
|
||||||
var amt = args.Effect.Amount;
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs) {
|
|
||||||
if (args.Effect.Scaled)
|
|
||||||
amt *= reagentArgs.Quantity.Float();
|
|
||||||
amt *= reagentArgs.Scale.Float();
|
|
||||||
}
|
|
||||||
|
|
||||||
_bloodstream.TryModifyBleedAmount((args.Args.TargetEntity, blood), amt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteModifyBloodLevel(ref ExecuteEntityEffectEvent<ModifyBloodLevel> args)
|
|
||||||
{
|
|
||||||
if (TryComp<BloodstreamComponent>(args.Args.TargetEntity, out var blood))
|
|
||||||
{
|
|
||||||
var amt = args.Effect.Amount;
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
{
|
|
||||||
if (args.Effect.Scaled)
|
|
||||||
amt *= reagentArgs.Quantity;
|
|
||||||
amt *= reagentArgs.Scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
_bloodstream.TryModifyBloodLevel((args.Args.TargetEntity, blood), amt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteModifyLungGas(ref ExecuteEntityEffectEvent<ModifyLungGas> args)
|
|
||||||
{
|
|
||||||
LungComponent? lung;
|
|
||||||
float amount = 1f;
|
|
||||||
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
{
|
|
||||||
if (!TryComp<LungComponent>(reagentArgs.OrganEntity, out var organLung))
|
|
||||||
return;
|
|
||||||
lung = organLung;
|
|
||||||
amount = reagentArgs.Quantity.Float();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!TryComp<LungComponent>(args.Args.TargetEntity, out var organLung)) //Likely needs to be modified to ensure it works correctly
|
|
||||||
return;
|
|
||||||
lung = organLung;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lung != null)
|
|
||||||
{
|
|
||||||
foreach (var (gas, ratio) in args.Effect.Ratios)
|
|
||||||
{
|
|
||||||
var quantity = ratio * amount / Atmospherics.BreathMolesToReagentMultiplier;
|
|
||||||
if (quantity < 0)
|
|
||||||
quantity = Math.Max(quantity, -lung.Air[(int) gas]);
|
|
||||||
lung.Air.AdjustMoles(gas, quantity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteOxygenate(ref ExecuteEntityEffectEvent<Oxygenate> args)
|
|
||||||
{
|
|
||||||
var multiplier = 1f;
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
{
|
|
||||||
multiplier = reagentArgs.Quantity.Float();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TryComp<RespiratorComponent>(args.Args.TargetEntity, out var resp))
|
|
||||||
{
|
|
||||||
_respirator.UpdateSaturation(args.Args.TargetEntity, multiplier * args.Effect.Factor, resp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantMutateChemicals(ref ExecuteEntityEffectEvent<PlantMutateChemicals> args)
|
|
||||||
{
|
|
||||||
var plantholder = Comp<PlantHolderComponent>(args.Args.TargetEntity);
|
|
||||||
|
|
||||||
if (plantholder.Seed == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var chemicals = plantholder.Seed.Chemicals;
|
|
||||||
var randomChems = _protoManager.Index(RandomPickBotanyReagent).Fills;
|
|
||||||
|
|
||||||
// Add a random amount of a random chemical to this set of chemicals
|
|
||||||
if (randomChems != null)
|
|
||||||
{
|
|
||||||
var pick = _random.Pick<RandomFillSolution>(randomChems);
|
|
||||||
var chemicalId = _random.Pick(pick.Reagents);
|
|
||||||
var amount = _random.Next(1, (int)pick.Quantity);
|
|
||||||
var seedChemQuantity = new SeedChemQuantity();
|
|
||||||
if (chemicals.ContainsKey(chemicalId))
|
|
||||||
{
|
|
||||||
seedChemQuantity.Min = chemicals[chemicalId].Min;
|
|
||||||
seedChemQuantity.Max = chemicals[chemicalId].Max + amount;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
seedChemQuantity.Min = 1;
|
|
||||||
seedChemQuantity.Max = 1 + amount;
|
|
||||||
seedChemQuantity.Inherent = false;
|
|
||||||
}
|
|
||||||
var potencyDivisor = (int)Math.Ceiling(100.0f / seedChemQuantity.Max);
|
|
||||||
seedChemQuantity.PotencyDivisor = potencyDivisor;
|
|
||||||
chemicals[chemicalId] = seedChemQuantity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantMutateConsumeGasses(ref ExecuteEntityEffectEvent<PlantMutateConsumeGasses> args)
|
|
||||||
{
|
|
||||||
var plantholder = Comp<PlantHolderComponent>(args.Args.TargetEntity);
|
|
||||||
|
|
||||||
if (plantholder.Seed == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var gasses = plantholder.Seed.ConsumeGasses;
|
|
||||||
|
|
||||||
// Add a random amount of a random gas to this gas dictionary
|
|
||||||
float amount = _random.NextFloat(args.Effect.MinValue, args.Effect.MaxValue);
|
|
||||||
Gas gas = _random.Pick(Enum.GetValues(typeof(Gas)).Cast<Gas>().ToList());
|
|
||||||
if (gasses.ContainsKey(gas))
|
|
||||||
{
|
|
||||||
gasses[gas] += amount;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gasses.Add(gas, amount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantMutateExudeGasses(ref ExecuteEntityEffectEvent<PlantMutateExudeGasses> args)
|
|
||||||
{
|
|
||||||
var plantholder = Comp<PlantHolderComponent>(args.Args.TargetEntity);
|
|
||||||
|
|
||||||
if (plantholder.Seed == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var gasses = plantholder.Seed.ExudeGasses;
|
|
||||||
|
|
||||||
// Add a random amount of a random gas to this gas dictionary
|
|
||||||
float amount = _random.NextFloat(args.Effect.MinValue, args.Effect.MaxValue);
|
|
||||||
Gas gas = _random.Pick(Enum.GetValues(typeof(Gas)).Cast<Gas>().ToList());
|
|
||||||
if (gasses.ContainsKey(gas))
|
|
||||||
{
|
|
||||||
gasses[gas] += amount;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gasses.Add(gas, amount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantMutateHarvest(ref ExecuteEntityEffectEvent<PlantMutateHarvest> args)
|
|
||||||
{
|
|
||||||
var plantholder = Comp<PlantHolderComponent>(args.Args.TargetEntity);
|
|
||||||
|
|
||||||
if (plantholder.Seed == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (plantholder.Seed.HarvestRepeat == HarvestType.NoRepeat)
|
|
||||||
plantholder.Seed.HarvestRepeat = HarvestType.Repeat;
|
|
||||||
else if (plantholder.Seed.HarvestRepeat == HarvestType.Repeat)
|
|
||||||
plantholder.Seed.HarvestRepeat = HarvestType.SelfHarvest;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePlantSpeciesChange(ref ExecuteEntityEffectEvent<PlantSpeciesChange> args)
|
|
||||||
{
|
|
||||||
var plantholder = Comp<PlantHolderComponent>(args.Args.TargetEntity);
|
|
||||||
if (plantholder.Seed == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (plantholder.Seed.MutationPrototypes.Count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var targetProto = _random.Pick(plantholder.Seed.MutationPrototypes);
|
|
||||||
if (!_protoManager.TryIndex(targetProto, out SeedPrototype? protoSeed))
|
|
||||||
{
|
|
||||||
Log.Error($"Seed prototype could not be found: {targetProto}!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
plantholder.Seed = plantholder.Seed.SpeciesChange(protoSeed);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecutePolymorph(ref ExecuteEntityEffectEvent<PolymorphEffect> args)
|
|
||||||
{
|
|
||||||
// Make it into a prototype
|
|
||||||
EnsureComp<PolymorphableComponent>(args.Args.TargetEntity);
|
|
||||||
_polymorph.PolymorphEntity(args.Args.TargetEntity, args.Effect.PolymorphPrototype);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnExecuteResetNarcolepsy(ref ExecuteEntityEffectEvent<ResetNarcolepsy> args)
|
|
||||||
{
|
|
||||||
if (args.Args is EntityEffectReagentArgs reagentArgs)
|
|
||||||
if (reagentArgs.Scale != 1f)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_narcolepsy.AdjustNarcolepsyTimer(args.Args.TargetEntity, args.Effect.TimerReset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,7 +21,7 @@ using Robust.Shared.Prototypes;
|
|||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Shared.EntityEffects.Effects.Solution;
|
||||||
using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
|
using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
|
||||||
|
|
||||||
namespace Content.Server.Fluids.EntitySystems;
|
namespace Content.Server.Fluids.EntitySystems;
|
||||||
@@ -278,11 +278,10 @@ public sealed class SmokeSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
if (reagentQuantity.Quantity == FixedPoint2.Zero)
|
if (reagentQuantity.Quantity == FixedPoint2.Zero)
|
||||||
continue;
|
continue;
|
||||||
var reagentProto = _prototype.Index<ReagentPrototype>(reagentQuantity.Reagent.Prototype);
|
|
||||||
|
|
||||||
_reactive.ReactionEntity(entity, ReactionMethod.Touch, reagentProto, reagentQuantity, transferSolution);
|
_reactive.ReactionEntity(entity, ReactionMethod.Touch, reagentQuantity);
|
||||||
if (!blockIngestion)
|
if (!blockIngestion)
|
||||||
_reactive.ReactionEntity(entity, ReactionMethod.Ingestion, reagentProto, reagentQuantity, transferSolution);
|
_reactive.ReactionEntity(entity, ReactionMethod.Ingestion, reagentQuantity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blockIngestion)
|
if (blockIngestion)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Text.Json.Serialization;
|
|||||||
using Content.Shared.Chemistry.Reaction;
|
using Content.Shared.Chemistry.Reaction;
|
||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.EntityConditions;
|
||||||
using Content.Shared.EntityEffects;
|
using Content.Shared.EntityEffects;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
@@ -42,7 +43,7 @@ public sealed class ChemistryJsonGenerator
|
|||||||
Converters =
|
Converters =
|
||||||
{
|
{
|
||||||
new UniversalJsonConverter<EntityEffect>(),
|
new UniversalJsonConverter<EntityEffect>(),
|
||||||
new UniversalJsonConverter<EntityEffectCondition>(),
|
new UniversalJsonConverter<EntityCondition>(),
|
||||||
new UniversalJsonConverter<ReagentEffectsEntry>(),
|
new UniversalJsonConverter<ReagentEffectsEntry>(),
|
||||||
new UniversalJsonConverter<DamageSpecifier>(),
|
new UniversalJsonConverter<DamageSpecifier>(),
|
||||||
new FixedPointJsonConverter()
|
new FixedPointJsonConverter()
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public sealed class ReactionEntry
|
|||||||
proto.Products
|
proto.Products
|
||||||
.Select(x => KeyValuePair.Create(x.Key, x.Value.Float()))
|
.Select(x => KeyValuePair.Create(x.Key, x.Value.Float()))
|
||||||
.ToDictionary(x => x.Key, x => x.Value);
|
.ToDictionary(x => x.Key, x => x.Value);
|
||||||
Effects = proto.Effects;
|
Effects = proto.Effects.ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ using Content.Shared.Stacks;
|
|||||||
using Content.Server.Construction.Components;
|
using Content.Server.Construction.Components;
|
||||||
using Content.Shared.Chat;
|
using Content.Shared.Chat;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Temperature.Components;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Kitchen.EntitySystems
|
namespace Content.Server.Kitchen.EntitySystems
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ using Content.Server.Medical.Components;
|
|||||||
using Content.Server.NodeContainer.EntitySystems;
|
using Content.Server.NodeContainer.EntitySystems;
|
||||||
using Content.Server.NodeContainer.NodeGroups;
|
using Content.Server.NodeContainer.NodeGroups;
|
||||||
using Content.Server.NodeContainer.Nodes;
|
using Content.Server.NodeContainer.Nodes;
|
||||||
using Content.Server.Temperature.Components;
|
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Content.Shared.Chemistry.EntitySystems;
|
using Content.Shared.Chemistry.EntitySystems;
|
||||||
using Content.Shared.Medical.Cryogenics;
|
using Content.Shared.Medical.Cryogenics;
|
||||||
using Content.Shared.MedicalScanner;
|
using Content.Shared.MedicalScanner;
|
||||||
|
using Content.Shared.Temperature.Components;
|
||||||
using Content.Shared.UserInterface;
|
using Content.Shared.UserInterface;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Content.Server.Medical.Components;
|
using Content.Server.Medical.Components;
|
||||||
using Content.Server.PowerCell;
|
using Content.Server.PowerCell;
|
||||||
using Content.Server.Temperature.Components;
|
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Content.Shared.Chemistry.EntitySystems;
|
using Content.Shared.Chemistry.EntitySystems;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
@@ -13,6 +12,7 @@ using Content.Shared.Item.ItemToggle.Components;
|
|||||||
using Content.Shared.MedicalScanner;
|
using Content.Shared.MedicalScanner;
|
||||||
using Content.Shared.Mobs.Components;
|
using Content.Shared.Mobs.Components;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Temperature.Components;
|
||||||
using Content.Shared.Traits.Assorted;
|
using Content.Shared.Traits.Assorted;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using Content.Server.NPC.Queries.Considerations;
|
|||||||
using Content.Server.NPC.Queries.Curves;
|
using Content.Server.NPC.Queries.Curves;
|
||||||
using Content.Server.NPC.Queries.Queries;
|
using Content.Server.NPC.Queries.Queries;
|
||||||
using Content.Server.Nutrition.Components;
|
using Content.Server.Nutrition.Components;
|
||||||
using Content.Server.Temperature.Components;
|
|
||||||
using Content.Shared.Chemistry.EntitySystems;
|
using Content.Shared.Chemistry.EntitySystems;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
@@ -30,6 +29,7 @@ using Robust.Shared.Prototypes;
|
|||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using Content.Shared.Atmos.Components;
|
using Content.Shared.Atmos.Components;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Shared.Temperature.Components;
|
||||||
|
|
||||||
namespace Content.Server.NPC.Systems;
|
namespace Content.Server.NPC.Systems;
|
||||||
|
|
||||||
@@ -512,13 +512,14 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var ent in _entityList)
|
foreach (var ent in _entityList)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user