forked from space-syndicate/space-station-14
Merge pull request #2002 from space-syndicate/upstream-sync
Upstream sync
This commit is contained in:
@@ -4,7 +4,6 @@ using JetBrains.Annotations;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Alerts;
|
||||
|
||||
@@ -13,7 +12,6 @@ public sealed class ClientAlertsSystem : AlertsSystem
|
||||
{
|
||||
public AlertOrderPrototype? AlertOrder { get; set; }
|
||||
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
|
||||
21
Content.Client/Alerts/UpdateAlertSpriteEvent.cs
Normal file
21
Content.Client/Alerts/UpdateAlertSpriteEvent.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Content.Shared.Alert;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Alerts;
|
||||
|
||||
/// <summary>
|
||||
/// Event raised on an entity with alerts in order to allow it to update visuals for the alert sprite entity.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public record struct UpdateAlertSpriteEvent
|
||||
{
|
||||
public Entity<SpriteComponent> SpriteViewEnt;
|
||||
|
||||
public AlertPrototype Alert;
|
||||
|
||||
public UpdateAlertSpriteEvent(Entity<SpriteComponent> spriteViewEnt, AlertPrototype alert)
|
||||
{
|
||||
SpriteViewEnt = spriteViewEnt;
|
||||
Alert = alert;
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,6 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
|
||||
private static AudioParams _params = AudioParams.Default
|
||||
.WithVariation(0.01f)
|
||||
.WithLoop(true)
|
||||
.WithAttenuation(Attenuation.LinearDistance)
|
||||
.WithMaxDistance(7f);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -23,8 +23,8 @@ public sealed partial class ContentAudioSystem
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
|
||||
private readonly AudioParams _lobbySoundtrackParams = new(-5f, 1, "Master", 0, 0, 0, false, 0f);
|
||||
private readonly AudioParams _roundEndSoundEffectParams = new(-5f, 1, "Master", 0, 0, 0, false, 0f);
|
||||
private readonly AudioParams _lobbySoundtrackParams = new(-5f, 1, 0, 0, 0, false, 0f);
|
||||
private readonly AudioParams _roundEndSoundEffectParams = new(-5f, 1, 0, 0, 0, false, 0f);
|
||||
|
||||
/// <summary>
|
||||
/// EntityUid of lobby restart sound component.
|
||||
|
||||
@@ -133,7 +133,7 @@ public sealed class ClientClothingSystem : ClothingSystem
|
||||
else if (TryComp(uid, out SpriteComponent? sprite))
|
||||
rsi = sprite.BaseRSI;
|
||||
|
||||
if (rsi == null || rsi.Path == null)
|
||||
if (rsi == null)
|
||||
return false;
|
||||
|
||||
var correctedSlot = slot;
|
||||
|
||||
@@ -4,14 +4,12 @@ using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Doors;
|
||||
|
||||
public sealed class DoorSystem : SharedDoorSystem
|
||||
{
|
||||
[Dependency] private readonly AnimationPlayerSystem _animationSystem = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
|
||||
public override void Initialize()
|
||||
|
||||
@@ -108,7 +108,6 @@ namespace Content.Client.Entry
|
||||
_prototypeManager.RegisterIgnore("gameMap");
|
||||
_prototypeManager.RegisterIgnore("gameMapPool");
|
||||
_prototypeManager.RegisterIgnore("lobbyBackground");
|
||||
_prototypeManager.RegisterIgnore("advertisementsPack");
|
||||
_prototypeManager.RegisterIgnore("gamePreset");
|
||||
_prototypeManager.RegisterIgnore("noiseChannel");
|
||||
_prototypeManager.RegisterIgnore("spaceBiome");
|
||||
|
||||
@@ -16,8 +16,6 @@ namespace Content.Client.IconSmoothing
|
||||
[UsedImplicitly]
|
||||
public sealed partial class IconSmoothSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
private readonly Queue<EntityUid> _dirtyEntities = new();
|
||||
private readonly Queue<EntityUid> _anchorChangedEntities = new();
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace Content.Client.Input
|
||||
common.AddFunction(ContentKeyFunctions.ToggleFullscreen);
|
||||
common.AddFunction(ContentKeyFunctions.MoveStoredItem);
|
||||
common.AddFunction(ContentKeyFunctions.RotateStoredItem);
|
||||
common.AddFunction(ContentKeyFunctions.SaveItemLocation);
|
||||
common.AddFunction(ContentKeyFunctions.Point);
|
||||
common.AddFunction(ContentKeyFunctions.ZoomOut);
|
||||
common.AddFunction(ContentKeyFunctions.ZoomIn);
|
||||
|
||||
@@ -93,7 +93,7 @@ public sealed class ItemSystem : SharedItemSystem
|
||||
else if (TryComp(uid, out SpriteComponent? sprite))
|
||||
rsi = sprite.BaseRSI;
|
||||
|
||||
if (rsi == null || rsi.Path == null)
|
||||
if (rsi == null)
|
||||
return false;
|
||||
|
||||
var state = (item.HeldPrefix == null)
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc grinder-menu-title}" MinSize="768 256">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<BoxContainer Orientation="Vertical" VerticalAlignment="Top" Margin="8">
|
||||
<Button Name="GrindButton" Text="{Loc grinder-menu-grind-button}" TextAlign="Center" MinSize="64 48"/>
|
||||
<Control MinSize="0 16"/>
|
||||
<Button Name="JuiceButton" Text="{Loc grinder-menu-juice-button}" TextAlign="Center" MinSize="64 48"/>
|
||||
<BoxContainer Orientation="Vertical" VerticalAlignment="Top" Margin="8" MinWidth="100">
|
||||
<Label Text="{Loc grinder-menu-auto-label}" HorizontalAlignment="Center"/>
|
||||
<Button Name="AutoModeButton" Text="{Loc grinder-menu-auto-button}" TextAlign="Center" MinSize="64 48" Margin="0 0 0 16" />
|
||||
<Label Text="{Loc grinder-menu-manual-label}" HorizontalAlignment="Center"/>
|
||||
<Button Name="GrindButton" Text="{Loc grinder-menu-grind-button}" TextAlign="Center" MinSize="64 48" Margin="0 0 0 16" />
|
||||
<Button Name="JuiceButton" Text="{Loc grinder-menu-juice-button}" TextAlign="Center" MinSize="64 48" />
|
||||
</BoxContainer>
|
||||
|
||||
<ui:LabelledContentBox Name="ChamberContentBox" LabelText="{Loc grinder-menu-chamber-content-box-label}" ButtonText="{Loc grinder-menu-chamber-content-box-button}" VerticalExpand="True" HorizontalExpand="True" Margin="8" SizeFlagsStretchRatio="2"/>
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace Content.Client.Kitchen.UI
|
||||
_entityManager = entityManager;
|
||||
_prototypeManager = prototypeManager;
|
||||
_owner = owner;
|
||||
AutoModeButton.OnPressed += owner.ToggleAutoMode;
|
||||
GrindButton.OnPressed += owner.StartGrinding;
|
||||
JuiceButton.OnPressed += owner.StartJuicing;
|
||||
ChamberContentBox.EjectButton.OnPressed += owner.EjectAll;
|
||||
@@ -56,6 +57,19 @@ namespace Content.Client.Kitchen.UI
|
||||
GrindButton.Disabled = !state.CanGrind || !state.Powered;
|
||||
JuiceButton.Disabled = !state.CanJuice || !state.Powered;
|
||||
|
||||
switch (state.AutoMode)
|
||||
{
|
||||
case GrinderAutoMode.Grind:
|
||||
AutoModeButton.Text = Loc.GetString("grinder-menu-grind-button");
|
||||
break;
|
||||
case GrinderAutoMode.Juice:
|
||||
AutoModeButton.Text = Loc.GetString("grinder-menu-juice-button");
|
||||
break;
|
||||
default:
|
||||
AutoModeButton.Text = Loc.GetString("grinder-menu-auto-button-off");
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO move this to a component state and ensure the net ids.
|
||||
RefreshContentsDisplay(state.ReagentQuantities, _entityManager.GetEntityArray(state.ChamberContents), state.HasBeakerIn);
|
||||
}
|
||||
|
||||
@@ -52,6 +52,11 @@ namespace Content.Client.Kitchen.UI
|
||||
_menu?.HandleMessage(message);
|
||||
}
|
||||
|
||||
public void ToggleAutoMode(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
SendMessage(new ReagentGrinderToggleAutoModeMessage());
|
||||
}
|
||||
|
||||
public void StartGrinding(BaseButton.ButtonEventArgs? _ = null)
|
||||
{
|
||||
SendMessage(new ReagentGrinderStartMessage(GrinderProgram.Grind));
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Content.Client.LateJoin
|
||||
private readonly SpriteSystem _sprites;
|
||||
private readonly CrewManifestSystem _crewManifest;
|
||||
|
||||
private readonly Dictionary<NetEntity, Dictionary<string, JobButton>> _jobButtons = new();
|
||||
private readonly Dictionary<NetEntity, Dictionary<string, List<JobButton>>> _jobButtons = new();
|
||||
private readonly Dictionary<NetEntity, Dictionary<string, BoxContainer>> _jobCategories = new();
|
||||
private readonly List<ScrollContainer> _jobLists = new();
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace Content.Client.LateJoin
|
||||
var jobListScroll = new ScrollContainer()
|
||||
{
|
||||
VerticalExpand = true,
|
||||
Children = {jobList},
|
||||
Children = { jobList },
|
||||
Visible = false,
|
||||
};
|
||||
|
||||
@@ -163,11 +163,12 @@ namespace Content.Client.LateJoin
|
||||
var departments = _prototypeManager.EnumeratePrototypes<DepartmentPrototype>().ToArray();
|
||||
Array.Sort(departments, DepartmentUIComparer.Instance);
|
||||
|
||||
_jobButtons[id] = new Dictionary<string, List<JobButton>>();
|
||||
|
||||
foreach (var department in departments)
|
||||
{
|
||||
var departmentName = Loc.GetString($"department-{department.ID}");
|
||||
_jobCategories[id] = new Dictionary<string, BoxContainer>();
|
||||
_jobButtons[id] = new Dictionary<string, JobButton>();
|
||||
var stationAvailable = _gameTicker.JobsAvailable[id];
|
||||
var jobsAvailable = new List<JobPrototype>();
|
||||
|
||||
@@ -223,7 +224,13 @@ namespace Content.Client.LateJoin
|
||||
foreach (var prototype in jobsAvailable)
|
||||
{
|
||||
var value = stationAvailable[prototype.ID];
|
||||
var jobButton = new JobButton(prototype.ID, value);
|
||||
|
||||
var jobLabel = new Label
|
||||
{
|
||||
Margin = new Thickness(5f, 0, 0, 0)
|
||||
};
|
||||
|
||||
var jobButton = new JobButton(jobLabel, prototype.ID, prototype.LocalizedName, value);
|
||||
|
||||
var jobSelector = new BoxContainer
|
||||
{
|
||||
@@ -241,14 +248,6 @@ namespace Content.Client.LateJoin
|
||||
icon.Texture = _sprites.Frame0(jobIcon.Icon);
|
||||
jobSelector.AddChild(icon);
|
||||
|
||||
var jobLabel = new Label
|
||||
{
|
||||
Margin = new Thickness(5f, 0, 0, 0),
|
||||
Text = value != null ?
|
||||
Loc.GetString("late-join-gui-job-slot-capped", ("jobName", prototype.LocalizedName), ("amount", value)) :
|
||||
Loc.GetString("late-join-gui-job-slot-uncapped", ("jobName", prototype.LocalizedName)),
|
||||
};
|
||||
|
||||
jobSelector.AddChild(jobLabel);
|
||||
jobButton.AddChild(jobSelector);
|
||||
category.AddChild(jobButton);
|
||||
@@ -280,15 +279,43 @@ namespace Content.Client.LateJoin
|
||||
jobButton.Disabled = true;
|
||||
}
|
||||
|
||||
_jobButtons[id][prototype.ID] = jobButton;
|
||||
if (!_jobButtons[id].ContainsKey(prototype.ID))
|
||||
{
|
||||
_jobButtons[id][prototype.ID] = new List<JobButton>();
|
||||
}
|
||||
|
||||
_jobButtons[id][prototype.ID].Add(jobButton);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void JobsAvailableUpdated(IReadOnlyDictionary<NetEntity, Dictionary<string, uint?>> _)
|
||||
private void JobsAvailableUpdated(IReadOnlyDictionary<NetEntity, Dictionary<string, uint?>> updatedJobs)
|
||||
{
|
||||
RebuildUI();
|
||||
foreach (var stationEntries in updatedJobs)
|
||||
{
|
||||
if (_jobButtons.ContainsKey(stationEntries.Key))
|
||||
{
|
||||
var jobsAvailable = stationEntries.Value;
|
||||
|
||||
var existingJobEntries = _jobButtons[stationEntries.Key];
|
||||
foreach (var existingJobEntry in existingJobEntries)
|
||||
{
|
||||
if (jobsAvailable.ContainsKey(existingJobEntry.Key))
|
||||
{
|
||||
var updatedJobValue = jobsAvailable[existingJobEntry.Key];
|
||||
foreach (var matchingJobButton in existingJobEntry.Value)
|
||||
{
|
||||
if (matchingJobButton.Amount != updatedJobValue)
|
||||
{
|
||||
matchingJobButton.RefreshLabel(updatedJobValue);
|
||||
matchingJobButton.Disabled |= matchingJobButton.Amount == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
@@ -307,14 +334,33 @@ namespace Content.Client.LateJoin
|
||||
|
||||
sealed class JobButton : ContainerButton
|
||||
{
|
||||
public Label JobLabel { get; }
|
||||
public string JobId { get; }
|
||||
public uint? Amount { get; }
|
||||
public string JobLocalisedName { get; }
|
||||
public uint? Amount { get; private set; }
|
||||
private bool _initialised = false;
|
||||
|
||||
public JobButton(string jobId, uint? amount)
|
||||
public JobButton(Label jobLabel, string jobId, string jobLocalisedName, uint? amount)
|
||||
{
|
||||
JobLabel = jobLabel;
|
||||
JobId = jobId;
|
||||
Amount = amount;
|
||||
JobLocalisedName = jobLocalisedName;
|
||||
RefreshLabel(amount);
|
||||
AddStyleClass(StyleClassButton);
|
||||
_initialised = true;
|
||||
}
|
||||
|
||||
public void RefreshLabel(uint? amount)
|
||||
{
|
||||
if (Amount == amount && _initialised)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Amount = amount;
|
||||
|
||||
JobLabel.Text = Amount != null ?
|
||||
Loc.GetString("late-join-gui-job-slot-capped", ("jobName", JobLocalisedName), ("amount", Amount)) :
|
||||
Loc.GetString("late-join-gui-job-slot-uncapped", ("jobName", JobLocalisedName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +289,6 @@ namespace Content.Client.NPC
|
||||
|
||||
var invGridMatrix = gridXform.InvWorldMatrix;
|
||||
DebugPathPoly? nearest = null;
|
||||
var nearestDistance = float.MaxValue;
|
||||
|
||||
foreach (var poly in tile)
|
||||
{
|
||||
|
||||
@@ -183,6 +183,7 @@ namespace Content.Client.Options.UI.Tabs
|
||||
AddButton(ContentKeyFunctions.SwapHands);
|
||||
AddButton(ContentKeyFunctions.MoveStoredItem);
|
||||
AddButton(ContentKeyFunctions.RotateStoredItem);
|
||||
AddButton(ContentKeyFunctions.SaveItemLocation);
|
||||
|
||||
AddHeader("ui-options-header-interaction-adv");
|
||||
AddButton(ContentKeyFunctions.SmartEquipBackpack);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Content.Client.Alerts;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Revenant;
|
||||
using Content.Shared.Revenant.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
@@ -13,6 +15,7 @@ public sealed class RevenantSystem : EntitySystem
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RevenantComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
SubscribeLocalEvent<RevenantComponent, UpdateAlertSpriteEvent>(OnUpdateAlert);
|
||||
}
|
||||
|
||||
private void OnAppearanceChange(EntityUid uid, RevenantComponent component, ref AppearanceChangeEvent args)
|
||||
@@ -36,4 +39,16 @@ public sealed class RevenantSystem : EntitySystem
|
||||
args.Sprite.LayerSetState(0, component.State);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUpdateAlert(Entity<RevenantComponent> ent, ref UpdateAlertSpriteEvent args)
|
||||
{
|
||||
if (args.Alert.AlertType != AlertType.Essence)
|
||||
return;
|
||||
|
||||
var sprite = args.SpriteViewEnt.Comp;
|
||||
var essence = Math.Clamp(ent.Comp.Essence.Int(), 0, 999);
|
||||
sprite.LayerSetState(RevenantVisualLayers.Digit1, $"{(essence / 100) % 10}");
|
||||
sprite.LayerSetState(RevenantVisualLayers.Digit2, $"{(essence / 10) % 10}");
|
||||
sprite.LayerSetState(RevenantVisualLayers.Digit3, $"{essence % 10}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,7 +88,6 @@ public partial class BaseShuttleControl : MapGridControl
|
||||
var cornerDistance = MathF.Sqrt(WorldRange * WorldRange + WorldRange * WorldRange);
|
||||
|
||||
var origin = ScalePosition(-new Vector2(Offset.X, -Offset.Y));
|
||||
var distOffset = -24f;
|
||||
|
||||
for (var radius = minDistance; radius <= maxDistance; radius *= EquatorialMultiplier)
|
||||
{
|
||||
|
||||
@@ -3,6 +3,8 @@ using Content.Client.Gameplay;
|
||||
using Content.Client.UserInterface.Systems.Alerts.Widgets;
|
||||
using Content.Client.UserInterface.Systems.Gameplay;
|
||||
using Content.Shared.Alert;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
|
||||
@@ -10,6 +12,8 @@ namespace Content.Client.UserInterface.Systems.Alerts;
|
||||
|
||||
public sealed class AlertsUIController : UIController, IOnStateEntered<GameplayState>, IOnSystemChanged<ClientAlertsSystem>
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
|
||||
[UISystemDependency] private readonly ClientAlertsSystem? _alertsSystem = default;
|
||||
|
||||
private AlertsUI? UI => UIManager.GetActiveUIWidgetOrNull<AlertsUI>();
|
||||
@@ -84,4 +88,16 @@ public sealed class AlertsUIController : UIController, IOnStateEntered<GameplayS
|
||||
SystemOnSyncAlerts(_alertsSystem, alerts);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateAlertSpriteEntity(EntityUid spriteViewEnt, AlertPrototype alert)
|
||||
{
|
||||
if (_player.LocalEntity is not { } player)
|
||||
return;
|
||||
|
||||
if (!EntityManager.TryGetComponent<SpriteComponent>(spriteViewEnt, out var sprite))
|
||||
return;
|
||||
|
||||
var ev = new UpdateAlertSpriteEvent((spriteViewEnt, sprite), alert);
|
||||
EntityManager.EventBus.RaiseLocalEvent(player, ref ev);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
using Content.Client.Actions.UI;
|
||||
using Content.Client.Cooldown;
|
||||
using Content.Shared.Alert;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -33,9 +35,12 @@ namespace Content.Client.UserInterface.Systems.Alerts.Controls
|
||||
|
||||
private short? _severity;
|
||||
private readonly IGameTiming _gameTiming;
|
||||
private readonly AnimatedTextureRect _icon;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly SpriteView _icon;
|
||||
private readonly CooldownGraphic _cooldownGraphic;
|
||||
|
||||
private EntityUid _spriteViewEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an alert control reflecting the indicated alert + state
|
||||
/// </summary>
|
||||
@@ -44,19 +49,30 @@ namespace Content.Client.UserInterface.Systems.Alerts.Controls
|
||||
public AlertControl(AlertPrototype alert, short? severity)
|
||||
{
|
||||
_gameTiming = IoCManager.Resolve<IGameTiming>();
|
||||
_entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
TooltipSupplier = SupplyTooltip;
|
||||
Alert = alert;
|
||||
_severity = severity;
|
||||
var specifier = alert.GetIcon(_severity);
|
||||
_icon = new AnimatedTextureRect
|
||||
{
|
||||
DisplayRect = {TextureScale = new Vector2(2, 2)}
|
||||
};
|
||||
|
||||
_icon.SetFromSpriteSpecifier(specifier);
|
||||
_spriteViewEntity = _entityManager.Spawn(Alert.AlertViewEntity);
|
||||
if (_entityManager.TryGetComponent<SpriteComponent>(_spriteViewEntity, out var sprite))
|
||||
{
|
||||
var icon = Alert.GetIcon(_severity);
|
||||
if (sprite.LayerMapTryGet(AlertVisualLayers.Base, out var layer))
|
||||
sprite.LayerSetSprite(layer, icon);
|
||||
}
|
||||
|
||||
_icon = new SpriteView
|
||||
{
|
||||
Scale = new Vector2(2, 2)
|
||||
};
|
||||
_icon.SetEntity(_spriteViewEntity);
|
||||
|
||||
Children.Add(_icon);
|
||||
_cooldownGraphic = new CooldownGraphic();
|
||||
_cooldownGraphic = new CooldownGraphic
|
||||
{
|
||||
MaxSize = new Vector2(64, 64)
|
||||
};
|
||||
Children.Add(_cooldownGraphic);
|
||||
}
|
||||
|
||||
@@ -72,16 +88,22 @@ namespace Content.Client.UserInterface.Systems.Alerts.Controls
|
||||
/// </summary>
|
||||
public void SetSeverity(short? severity)
|
||||
{
|
||||
if (_severity != severity)
|
||||
{
|
||||
_severity = severity;
|
||||
_icon.SetFromSpriteSpecifier(Alert.GetIcon(_severity));
|
||||
}
|
||||
if (_severity == severity)
|
||||
return;
|
||||
_severity = severity;
|
||||
|
||||
if (!_entityManager.TryGetComponent<SpriteComponent>(_spriteViewEntity, out var sprite))
|
||||
return;
|
||||
var icon = Alert.GetIcon(_severity);
|
||||
if (sprite.LayerMapTryGet(AlertVisualLayers.Base, out var layer))
|
||||
sprite.LayerSetSprite(layer, icon);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
UserInterfaceManager.GetUIController<AlertsUIController>().UpdateAlertSpriteEntity(_spriteViewEntity, Alert);
|
||||
|
||||
if (!Cooldown.HasValue)
|
||||
{
|
||||
_cooldownGraphic.Visible = false;
|
||||
@@ -91,5 +113,17 @@ namespace Content.Client.UserInterface.Systems.Alerts.Controls
|
||||
|
||||
_cooldownGraphic.FromTime(Cooldown.Value.Start, Cooldown.Value.End);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
_entityManager.DeleteEntity(_spriteViewEntity);
|
||||
}
|
||||
}
|
||||
|
||||
public enum AlertVisualLayers : byte
|
||||
{
|
||||
Base
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.UserInterface.Systems.Storage.Controls;
|
||||
|
||||
@@ -355,6 +356,40 @@ public sealed class StorageContainer : BaseWindow
|
||||
origin,
|
||||
currentLocation.Rotation);
|
||||
|
||||
foreach (var locations in storageComponent.SavedLocations)
|
||||
{
|
||||
if (!_entity.TryGetComponent<MetaDataComponent>(currentEnt, out var meta) || meta.EntityName != locations.Key)
|
||||
continue;
|
||||
|
||||
float spot = 0;
|
||||
var marked = new List<Control>();
|
||||
|
||||
foreach (var location in locations.Value)
|
||||
{
|
||||
var shape = itemSystem.GetAdjustedItemShape(currentEnt, location);
|
||||
var bound = shape.GetBoundingBox();
|
||||
|
||||
var spotFree = storageSystem.ItemFitsInGridLocation(currentEnt, StorageEntity.Value, location);
|
||||
|
||||
if (spotFree)
|
||||
spot++;
|
||||
|
||||
for (var y = bound.Bottom; y <= bound.Top; y++)
|
||||
{
|
||||
for (var x = bound.Left; x <= bound.Right; x++)
|
||||
{
|
||||
if (TryGetBackgroundCell(x, y, out var cell) && shape.Contains(x, y) && !marked.Contains(cell))
|
||||
{
|
||||
marked.Add(cell);
|
||||
cell.ModulateSelfOverride = spotFree
|
||||
? Color.FromHsv((0.18f, 1 / spot, 0.5f / spot + 0.5f, 1f))
|
||||
: Color.FromHex("#2222CC");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var validColor = usingInHand ? Color.Goldenrod : Color.FromHex("#1E8000");
|
||||
|
||||
for (var y = itemBounding.Bottom; y <= itemBounding.Top; y++)
|
||||
|
||||
@@ -240,6 +240,16 @@ public sealed class StorageUIController : UIController, IOnSystemChanged<Storage
|
||||
|
||||
args.Handle();
|
||||
}
|
||||
else if (args.Function == ContentKeyFunctions.SaveItemLocation)
|
||||
{
|
||||
if (_container?.StorageEntity is not {} storage)
|
||||
return;
|
||||
|
||||
_entity.RaisePredictiveEvent(new StorageSaveItemLocationEvent(
|
||||
_entity.GetNetEntity(control.Entity),
|
||||
_entity.GetNetEntity(storage)));
|
||||
args.Handle();
|
||||
}
|
||||
else if (args.Function == ContentKeyFunctions.ExamineEntity)
|
||||
{
|
||||
_entity.System<ExamineSystem>().DoExamine(control.Entity);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
using Content.Shared.VoiceMask;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.VoiceMask;
|
||||
|
||||
public sealed class VoiceMaskBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private VoiceMaskNameChangeWindow? _window;
|
||||
|
||||
@@ -16,11 +19,12 @@ public sealed class VoiceMaskBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new();
|
||||
_window = new(_proto);
|
||||
|
||||
_window.OpenCentered();
|
||||
_window.OnNameChange += OnNameSelected;
|
||||
_window.OnVoiceChange += (value) => SendMessage(new VoiceMaskChangeVoiceMessage(value)); // Corvax-TTS
|
||||
_window.OnVerbChange += verb => SendMessage(new VoiceMaskChangeVerbMessage(verb));
|
||||
_window.OnVoiceChange += voice => SendMessage(new VoiceMaskChangeVoiceMessage(voice)); // Corvax-TTS
|
||||
_window.OnClose += Close;
|
||||
}
|
||||
|
||||
@@ -36,7 +40,7 @@ public sealed class VoiceMaskBoundUserInterface : BoundUserInterface
|
||||
return;
|
||||
}
|
||||
|
||||
_window.UpdateState(cast.Name, cast.Voice); // Corvax-TTS
|
||||
_window.UpdateState(cast.Name, cast.Voice, cast.Verb); // Corvax-TTS
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
<DefaultWindow xmlns="https://spacestation14.io"
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
Title="{Loc 'voice-mask-name-change-window'}"
|
||||
MinSize="5 20">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
MinSize="5 30">
|
||||
<BoxContainer Orientation="Vertical" Margin="5">
|
||||
<Label Text="{Loc 'voice-mask-name-change-info'}" />
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<BoxContainer Orientation="Horizontal" Margin="5">
|
||||
<LineEdit Name="NameSelector" HorizontalExpand="True" />
|
||||
<Button Name="NameSelectorSet" Text="{Loc 'voice-mask-name-change-set'}" />
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Horizontal" Margin="5">
|
||||
<Label Text="{Loc 'voice-mask-name-change-speech-style'}" />
|
||||
<OptionButton Name="SpeechVerbSelector" /> <!-- Populated in LoadVerbs -->
|
||||
</BoxContainer>
|
||||
<!-- Corvax-TTS-Start -->
|
||||
<Label Text="{Loc 'voice-mask-voice-change-info'}" />
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<OptionButton Name="VoiceSelector" HorizontalExpand="True" />
|
||||
<BoxContainer Orientation="Horizontal" Margin="5">
|
||||
<Label Text="{Loc 'voice-mask-voice-change-info'}" />
|
||||
<OptionButton Name="VoiceSelector" /> <!-- Populated in LoadVerbs -->
|
||||
</BoxContainer>
|
||||
<!-- Corvax-TTS-End -->
|
||||
</BoxContainer>
|
||||
</DefaultWindow>
|
||||
</controls:FancyWindow>
|
||||
|
||||
@@ -1,37 +1,87 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Corvax.TTS;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Speech;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.VoiceMask;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class VoiceMaskNameChangeWindow : DefaultWindow
|
||||
public sealed partial class VoiceMaskNameChangeWindow : FancyWindow
|
||||
{
|
||||
private readonly List<TTSVoicePrototype> _voices; // Corvax-TTS
|
||||
|
||||
public Action<string>? OnNameChange;
|
||||
public Action<string?>? OnVerbChange;
|
||||
public Action<string>? OnVoiceChange; // Corvax-TTS
|
||||
|
||||
public VoiceMaskNameChangeWindow()
|
||||
private List<(string, string)> _verbs = new();
|
||||
private List<TTSVoicePrototype> _voices = new(); // Corvax-TTS
|
||||
|
||||
private string? _verb;
|
||||
|
||||
public VoiceMaskNameChangeWindow(IPrototypeManager proto)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
NameSelectorSet.OnPressed += _ =>
|
||||
{
|
||||
OnNameChange!(NameSelector.Text);
|
||||
OnNameChange?.Invoke(NameSelector.Text);
|
||||
};
|
||||
// Corvax-TTS-Start
|
||||
|
||||
SpeechVerbSelector.OnItemSelected += args =>
|
||||
{
|
||||
OnVerbChange?.Invoke((string?) args.Button.GetItemMetadata(args.Id));
|
||||
SpeechVerbSelector.SelectId(args.Id);
|
||||
};
|
||||
|
||||
ReloadVerbs(proto);
|
||||
ReloadVoices(proto); // Corvax-TTS
|
||||
|
||||
AddVerbs();
|
||||
}
|
||||
|
||||
private void ReloadVerbs(IPrototypeManager proto)
|
||||
{
|
||||
foreach (var verb in proto.EnumeratePrototypes<SpeechVerbPrototype>())
|
||||
{
|
||||
_verbs.Add((Loc.GetString(verb.Name), verb.ID));
|
||||
}
|
||||
_verbs.Sort((a, b) => a.Item1.CompareTo(b.Item1));
|
||||
}
|
||||
|
||||
private void AddVerbs()
|
||||
{
|
||||
SpeechVerbSelector.Clear();
|
||||
|
||||
AddVerb(Loc.GetString("chat-speech-verb-name-none"), null);
|
||||
foreach (var (name, id) in _verbs)
|
||||
{
|
||||
AddVerb(name, id);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddVerb(string name, string? verb)
|
||||
{
|
||||
var id = SpeechVerbSelector.ItemCount;
|
||||
SpeechVerbSelector.AddItem(name);
|
||||
if (verb is {} metadata)
|
||||
SpeechVerbSelector.SetItemMetadata(id, metadata);
|
||||
|
||||
if (verb == _verb)
|
||||
SpeechVerbSelector.SelectId(id);
|
||||
}
|
||||
|
||||
// Corvax-TTS-Start
|
||||
private void ReloadVoices(IPrototypeManager proto)
|
||||
{
|
||||
VoiceSelector.OnItemSelected += args =>
|
||||
{
|
||||
VoiceSelector.SelectId(args.Id);
|
||||
if (VoiceSelector.SelectedMetadata != null)
|
||||
OnVoiceChange!((string)VoiceSelector.SelectedMetadata);
|
||||
};
|
||||
_voices = IoCManager
|
||||
.Resolve<IPrototypeManager>()
|
||||
_voices = proto
|
||||
.EnumeratePrototypes<TTSVoicePrototype>()
|
||||
.Where(o => o.RoundStart)
|
||||
.OrderBy(o => Loc.GetString(o.Name))
|
||||
@@ -42,12 +92,22 @@ public sealed partial class VoiceMaskNameChangeWindow : DefaultWindow
|
||||
VoiceSelector.AddItem(name);
|
||||
VoiceSelector.SetItemMetadata(i, _voices[i].ID);
|
||||
}
|
||||
// Corvax-TTS-End
|
||||
}
|
||||
// Corvax-TTS-End
|
||||
|
||||
public void UpdateState(string name, string voice) // Corvax-TTS
|
||||
public void UpdateState(string name, string voice, string? verb) // Corvax-TTS
|
||||
{
|
||||
NameSelector.Text = name;
|
||||
_verb = verb;
|
||||
|
||||
for (int id = 0; id < SpeechVerbSelector.ItemCount; id++)
|
||||
{
|
||||
if (string.Equals(verb, SpeechVerbSelector.GetItemMetadata(id)))
|
||||
{
|
||||
SpeechVerbSelector.SelectId(id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Corvax-TTS-Start
|
||||
var voiceIdx = _voices.FindIndex(v => v.ID == voice);
|
||||
|
||||
@@ -306,11 +306,6 @@ public static partial class PoolManager
|
||||
Pairs[fallback!] = true;
|
||||
}
|
||||
|
||||
if (fallback == null && _pairId > 8)
|
||||
{
|
||||
var x = 2;
|
||||
}
|
||||
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Maths;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
@@ -61,12 +60,11 @@ namespace Content.IntegrationTests.Tests.Body
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapLoader = entityManager.System<MapLoaderSystem>();
|
||||
RespiratorSystem respSys = default;
|
||||
MetabolizerSystem metaSys = default;
|
||||
|
||||
MapId mapId;
|
||||
EntityUid? grid = null;
|
||||
BodyComponent body = default;
|
||||
RespiratorComponent resp = default;
|
||||
EntityUid human = default;
|
||||
GridAtmosphereComponent relevantAtmos = default;
|
||||
var startingMoles = 0.0f;
|
||||
@@ -99,17 +97,15 @@ namespace Content.IntegrationTests.Tests.Body
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var coords = new Vector2(0.5f, -1f);
|
||||
var coordinates = new EntityCoordinates(grid.Value, coords);
|
||||
var center = new Vector2(0.5f, 0.5f);
|
||||
var coordinates = new EntityCoordinates(grid.Value, center);
|
||||
human = entityManager.SpawnEntity("HumanLungDummy", coordinates);
|
||||
respSys = entityManager.System<RespiratorSystem>();
|
||||
metaSys = entityManager.System<MetabolizerSystem>();
|
||||
relevantAtmos = entityManager.GetComponent<GridAtmosphereComponent>(grid.Value);
|
||||
startingMoles = GetMapMoles();
|
||||
startingMoles = 100f; // Hardcoded because GetMapMoles returns 900 here for some reason.
|
||||
|
||||
#pragma warning disable NUnit2045
|
||||
Assert.That(entityManager.TryGetComponent(human, out body), Is.True);
|
||||
Assert.That(entityManager.HasComponent<RespiratorComponent>(human), Is.True);
|
||||
Assert.That(entityManager.TryGetComponent(human, out resp), Is.True);
|
||||
#pragma warning restore NUnit2045
|
||||
});
|
||||
|
||||
@@ -118,18 +114,19 @@ namespace Content.IntegrationTests.Tests.Body
|
||||
var inhaleCycles = 100;
|
||||
for (var i = 0; i < inhaleCycles; i++)
|
||||
{
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
// inhale
|
||||
respSys.Update(2.0f);
|
||||
Assert.That(GetMapMoles(), Is.LessThan(startingMoles));
|
||||
// Breathe in
|
||||
await PoolManager.WaitUntil(server, () => resp.Status == RespiratorStatus.Exhaling);
|
||||
Assert.That(
|
||||
GetMapMoles(), Is.LessThan(startingMoles),
|
||||
"Did not inhale in any gas"
|
||||
);
|
||||
|
||||
// metabolize + exhale
|
||||
metaSys.Update(1.0f);
|
||||
metaSys.Update(1.0f);
|
||||
respSys.Update(2.0f);
|
||||
Assert.That(GetMapMoles(), Is.EqualTo(startingMoles).Within(0.0002));
|
||||
});
|
||||
// Breathe out
|
||||
await PoolManager.WaitUntil(server, () => resp.Status == RespiratorStatus.Inhaling);
|
||||
Assert.That(
|
||||
GetMapMoles(), Is.EqualTo(startingMoles).Within(0.0002),
|
||||
"Did not exhale as much gas as was inhaled"
|
||||
);
|
||||
}
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
|
||||
@@ -181,9 +181,8 @@ namespace Content.IntegrationTests.Tests.Buckle
|
||||
#pragma warning restore NUnit2045
|
||||
|
||||
// Move away from the chair
|
||||
var xformQuery = entityManager.GetEntityQuery<TransformComponent>();
|
||||
var oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery);
|
||||
xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1000, 1000), xformQuery);
|
||||
var oldWorldPosition = xformSystem.GetWorldPosition(chair);
|
||||
xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1000, 1000));
|
||||
|
||||
// Out of range
|
||||
#pragma warning disable NUnit2045 // Interdependent asserts.
|
||||
@@ -193,8 +192,8 @@ namespace Content.IntegrationTests.Tests.Buckle
|
||||
#pragma warning restore NUnit2045
|
||||
|
||||
// Move near the chair
|
||||
oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery);
|
||||
xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(0.5f, 0), xformQuery);
|
||||
oldWorldPosition = xformSystem.GetWorldPosition(chair);
|
||||
xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(0.5f, 0));
|
||||
|
||||
// In range
|
||||
#pragma warning disable NUnit2045 // Interdependent asserts.
|
||||
@@ -220,8 +219,8 @@ namespace Content.IntegrationTests.Tests.Buckle
|
||||
Assert.That(buckleSystem.TryBuckle(human, human, chair, buckleComp: buckle));
|
||||
|
||||
// Move away from the chair
|
||||
oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery);
|
||||
xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1, 0), xformQuery);
|
||||
oldWorldPosition = xformSystem.GetWorldPosition(chair);
|
||||
xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(1, 0));
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(1);
|
||||
@@ -371,9 +370,8 @@ namespace Content.IntegrationTests.Tests.Buckle
|
||||
});
|
||||
|
||||
// Move the buckled entity away
|
||||
var xformQuery = entityManager.GetEntityQuery<TransformComponent>();
|
||||
var oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery);
|
||||
xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(100, 0), xformQuery);
|
||||
var oldWorldPosition = xformSystem.GetWorldPosition(chair);
|
||||
xformSystem.SetWorldPosition(human, oldWorldPosition + new Vector2(100, 0));
|
||||
});
|
||||
|
||||
await PoolManager.WaitUntil(server, () => !buckle.Buckled, 10);
|
||||
@@ -383,9 +381,8 @@ namespace Content.IntegrationTests.Tests.Buckle
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
// Move the now unbuckled entity back onto the chair
|
||||
var xformQuery = entityManager.GetEntityQuery<TransformComponent>();
|
||||
var oldWorldPosition = xformSystem.GetWorldPosition(chair, xformQuery);
|
||||
xformSystem.SetWorldPosition(human, oldWorldPosition, xformQuery);
|
||||
var oldWorldPosition = xformSystem.GetWorldPosition(chair);
|
||||
xformSystem.SetWorldPosition(human, oldWorldPosition);
|
||||
|
||||
// Buckle
|
||||
Assert.That(buckleSystem.TryBuckle(human, human, chair, buckleComp: buckle));
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Content.IntegrationTests.Tests.Fluids
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var coordinates = grid.ToCoordinates();
|
||||
var coordinates = grid.Owner.ToCoordinates();
|
||||
var solution = new Solution("Water", FixedPoint2.New(20));
|
||||
|
||||
Assert.That(spillSystem.TrySpillAt(coordinates, solution, out _), Is.False);
|
||||
|
||||
@@ -58,7 +58,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
|
||||
|
||||
var cuffableSys = entityManager.System<CuffableSystem>();
|
||||
var xformSys = entityManager.System<SharedTransformSystem>();
|
||||
var xformQuery = entityManager.GetEntityQuery<TransformComponent>();
|
||||
|
||||
// Spawn the entities
|
||||
human = entityManager.SpawnEntity("HumanHandcuffDummy", coordinates);
|
||||
@@ -66,8 +65,8 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
|
||||
cuffs = entityManager.SpawnEntity("HandcuffsDummy", coordinates);
|
||||
secondCuffs = entityManager.SpawnEntity("HandcuffsDummy", coordinates);
|
||||
|
||||
var coords = xformSys.GetWorldPosition(otherHuman, xformQuery);
|
||||
xformSys.SetWorldPosition(human, coords, xformQuery);
|
||||
var coords = xformSys.GetWorldPosition(otherHuman);
|
||||
xformSys.SetWorldPosition(human, coords);
|
||||
|
||||
// Test for components existing
|
||||
Assert.Multiple(() =>
|
||||
|
||||
@@ -176,16 +176,18 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
var map = mapManager.CreateMap();
|
||||
var grid = mapManager.CreateGrid(map);
|
||||
|
||||
var gridOwner = grid.Owner;
|
||||
|
||||
// Power only works when anchored
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
grid.SetTile(new Vector2i(0, i), new Tile(1));
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
|
||||
}
|
||||
|
||||
var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates());
|
||||
var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 1));
|
||||
var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2));
|
||||
var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates());
|
||||
var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 1));
|
||||
var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2));
|
||||
|
||||
supplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
|
||||
consumer1 = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt1);
|
||||
@@ -237,16 +239,18 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
var map = mapManager.CreateMap();
|
||||
var grid = mapManager.CreateGrid(map);
|
||||
|
||||
var gridOwner = grid.Owner;
|
||||
|
||||
// Power only works when anchored
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
grid.SetTile(new Vector2i(0, i), new Tile(1));
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
|
||||
}
|
||||
|
||||
var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates());
|
||||
var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 1));
|
||||
var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2));
|
||||
var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates());
|
||||
var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 1));
|
||||
var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2));
|
||||
|
||||
supplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
|
||||
consumer1 = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt1);
|
||||
@@ -292,16 +296,17 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
{
|
||||
var map = mapManager.CreateMap();
|
||||
var grid = mapManager.CreateGrid(map);
|
||||
var gridOwner = grid.Owner;
|
||||
|
||||
// Power only works when anchored
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
grid.SetTile(new Vector2i(0, i), new Tile(1));
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
|
||||
}
|
||||
|
||||
var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates());
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2));
|
||||
var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates());
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2));
|
||||
|
||||
supplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
|
||||
consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
|
||||
@@ -383,16 +388,17 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
{
|
||||
var map = mapManager.CreateMap();
|
||||
var grid = mapManager.CreateGrid(map);
|
||||
var gridOwner = grid.Owner;
|
||||
|
||||
// Power only works when anchored
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
grid.SetTile(new Vector2i(0, i), new Tile(1));
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
|
||||
}
|
||||
|
||||
var generatorEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.ToCoordinates());
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 2));
|
||||
var generatorEnt = entityManager.SpawnEntity("DischargingBatteryDummy", gridOwner.ToCoordinates());
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2));
|
||||
|
||||
netBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(generatorEnt);
|
||||
battery = entityManager.GetComponent<BatteryComponent>(generatorEnt);
|
||||
@@ -486,17 +492,18 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
{
|
||||
var map = mapManager.CreateMap();
|
||||
var grid = mapManager.CreateGrid(map);
|
||||
var gridOwner = grid.Owner;
|
||||
|
||||
// Power only works when anchored
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
grid.SetTile(new Vector2i(0, i), new Tile(1));
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
|
||||
}
|
||||
|
||||
var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates());
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 1));
|
||||
var batteryEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.ToCoordinates(0, 2));
|
||||
var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates());
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 1));
|
||||
var batteryEnt = entityManager.SpawnEntity("DischargingBatteryDummy", gridOwner.ToCoordinates(0, 2));
|
||||
netBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(batteryEnt);
|
||||
battery = entityManager.GetComponent<BatteryComponent>(batteryEnt);
|
||||
supplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
|
||||
@@ -577,16 +584,17 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
{
|
||||
var map = mapManager.CreateMap();
|
||||
var grid = mapManager.CreateGrid(map);
|
||||
var gridOwner = grid.Owner;
|
||||
|
||||
// Power only works when anchored
|
||||
for (var i = 0; i < 3; i++)
|
||||
{
|
||||
grid.SetTile(new Vector2i(0, i), new Tile(1));
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
|
||||
}
|
||||
|
||||
var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates());
|
||||
var batteryEnt = entityManager.SpawnEntity("ChargingBatteryDummy", grid.ToCoordinates(0, 2));
|
||||
var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates());
|
||||
var batteryEnt = entityManager.SpawnEntity("ChargingBatteryDummy", gridOwner.ToCoordinates(0, 2));
|
||||
|
||||
supplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
|
||||
var netBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(batteryEnt);
|
||||
@@ -635,20 +643,21 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
{
|
||||
var map = mapManager.CreateMap();
|
||||
var grid = mapManager.CreateGrid(map);
|
||||
var gridOwner = grid.Owner;
|
||||
|
||||
// Power only works when anchored
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
grid.SetTile(new Vector2i(0, i), new Tile(1));
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
|
||||
}
|
||||
|
||||
var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1));
|
||||
var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1));
|
||||
entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
|
||||
|
||||
var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2));
|
||||
var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0));
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 3));
|
||||
var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2));
|
||||
var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0));
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 3));
|
||||
|
||||
consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
|
||||
supplier = entityManager.GetComponent<PowerSupplierComponent>(supplyEnt);
|
||||
@@ -712,20 +721,21 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
{
|
||||
var map = mapManager.CreateMap();
|
||||
var grid = mapManager.CreateGrid(map);
|
||||
var gridOwner = grid.Owner;
|
||||
|
||||
// Power only works when anchored
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
grid.SetTile(new Vector2i(0, i), new Tile(1));
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
|
||||
}
|
||||
|
||||
var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1));
|
||||
var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1));
|
||||
entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
|
||||
|
||||
var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2));
|
||||
var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0));
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 3));
|
||||
var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2));
|
||||
var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0));
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 3));
|
||||
|
||||
consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
|
||||
supplier = entityManager.GetComponent<PowerSupplierComponent>(supplyEnt);
|
||||
@@ -787,6 +797,7 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
{
|
||||
var map = mapManager.CreateMap();
|
||||
var grid = mapManager.CreateGrid(map);
|
||||
var gridOwner = grid.Owner;
|
||||
|
||||
// Map layout here is
|
||||
// C - consumer
|
||||
@@ -800,18 +811,18 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
grid.SetTile(new Vector2i(0, i), new Tile(1));
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
|
||||
}
|
||||
|
||||
entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2));
|
||||
var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2));
|
||||
entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2));
|
||||
var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2));
|
||||
entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
|
||||
|
||||
var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 1));
|
||||
var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 3));
|
||||
var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 2));
|
||||
var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 0));
|
||||
var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 4));
|
||||
var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 1));
|
||||
var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 3));
|
||||
var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 2));
|
||||
var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 0));
|
||||
var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 4));
|
||||
|
||||
consumer1 = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt1);
|
||||
consumer2 = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt2);
|
||||
@@ -888,6 +899,7 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
{
|
||||
var map = mapManager.CreateMap();
|
||||
var grid = mapManager.CreateGrid(map);
|
||||
var gridOwner = grid.Owner;
|
||||
|
||||
// Layout is two generators, two batteries, and one load. As to why two: because previously this test
|
||||
// would fail ONLY if there were more than two batteries present, because each of them tries to supply
|
||||
@@ -900,16 +912,16 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
for (var i = -2; i <= 2; i++)
|
||||
{
|
||||
grid.SetTile(new Vector2i(0, i), new Tile(1));
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
|
||||
}
|
||||
|
||||
var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2));
|
||||
var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, -2));
|
||||
var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2));
|
||||
var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, -2));
|
||||
|
||||
var supplyEnt1 = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 1));
|
||||
var supplyEnt2 = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, -1));
|
||||
var supplyEnt1 = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 1));
|
||||
var supplyEnt2 = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, -1));
|
||||
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 0));
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 0));
|
||||
|
||||
consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
|
||||
supplier1 = entityManager.GetComponent<PowerSupplierComponent>(supplyEnt1);
|
||||
@@ -981,6 +993,7 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
{
|
||||
var map = mapManager.CreateMap();
|
||||
var grid = mapManager.CreateGrid(map);
|
||||
var gridOwner = grid.Owner;
|
||||
|
||||
// Map layout here is
|
||||
// C - consumer
|
||||
@@ -994,18 +1007,18 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
grid.SetTile(new Vector2i(0, i), new Tile(1));
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
|
||||
}
|
||||
|
||||
entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2));
|
||||
var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 2));
|
||||
entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2));
|
||||
var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2));
|
||||
entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
|
||||
|
||||
var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 1));
|
||||
var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 3));
|
||||
var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 2));
|
||||
var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 0));
|
||||
var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 4));
|
||||
var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 1));
|
||||
var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 3));
|
||||
var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 2));
|
||||
var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 0));
|
||||
var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 4));
|
||||
|
||||
consumer1 = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt1);
|
||||
consumer2 = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt2);
|
||||
@@ -1068,20 +1081,21 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
{
|
||||
var map = mapManager.CreateMap();
|
||||
var grid = mapManager.CreateGrid(map);
|
||||
var gridOwner = grid.Owner;
|
||||
|
||||
// Power only works when anchored
|
||||
for (var i = 0; i < 4; i++)
|
||||
{
|
||||
grid.SetTile(new Vector2i(0, i), new Tile(1));
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, i));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i));
|
||||
}
|
||||
|
||||
var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1));
|
||||
var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1));
|
||||
entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
|
||||
|
||||
var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2));
|
||||
var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0));
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.ToCoordinates(0, 3));
|
||||
var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2));
|
||||
var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0));
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 3));
|
||||
|
||||
consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
|
||||
supplier = entityManager.GetComponent<PowerSupplierComponent>(supplyEnt);
|
||||
@@ -1153,6 +1167,7 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
{
|
||||
var map = mapManager.CreateMap();
|
||||
var grid = mapManager.CreateGrid(map);
|
||||
var gridOwner = grid.Owner;
|
||||
|
||||
// Power only works when anchored
|
||||
for (var i = 0; i < 4; i++)
|
||||
@@ -1160,15 +1175,15 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
grid.SetTile(new Vector2i(0, i), new Tile(1));
|
||||
}
|
||||
|
||||
var leftEnt = entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 0));
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 1));
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 2));
|
||||
var rightEnt = entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 3));
|
||||
var leftEnt = entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 0));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 1));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 2));
|
||||
var rightEnt = entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 3));
|
||||
|
||||
var terminal = entityManager.SpawnEntity("CableTerminal", grid.ToCoordinates(0, 1));
|
||||
var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1));
|
||||
entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
|
||||
|
||||
var battery = entityManager.SpawnEntity("FullBatteryDummy", grid.ToCoordinates(0, 2));
|
||||
var battery = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2));
|
||||
var batteryNodeContainer = entityManager.GetComponent<NodeContainerComponent>(battery);
|
||||
|
||||
if (nodeContainer.TryGetNode<CableNode>(entityManager.GetComponent<NodeContainerComponent>(leftEnt),
|
||||
@@ -1216,6 +1231,7 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
{
|
||||
var map = mapManager.CreateMap();
|
||||
var grid = mapManager.CreateGrid(map);
|
||||
var gridOwner = grid.Owner;
|
||||
|
||||
// Power only works when anchored
|
||||
for (var i = 0; i < 3; i++)
|
||||
@@ -1223,14 +1239,14 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
grid.SetTile(new Vector2i(0, i), new Tile(1));
|
||||
}
|
||||
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 0));
|
||||
entityManager.SpawnEntity("CableHV", grid.ToCoordinates(0, 1));
|
||||
entityManager.SpawnEntity("CableMV", grid.ToCoordinates(0, 1));
|
||||
entityManager.SpawnEntity("CableMV", grid.ToCoordinates(0, 2));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 0));
|
||||
entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 1));
|
||||
entityManager.SpawnEntity("CableMV", gridOwner.ToCoordinates(0, 1));
|
||||
entityManager.SpawnEntity("CableMV", gridOwner.ToCoordinates(0, 2));
|
||||
|
||||
var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.ToCoordinates(0, 0));
|
||||
var substationEnt = entityManager.SpawnEntity("SubstationDummy", grid.ToCoordinates(0, 1));
|
||||
var apcEnt = entityManager.SpawnEntity("ApcDummy", grid.ToCoordinates(0, 2));
|
||||
var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0));
|
||||
var substationEnt = entityManager.SpawnEntity("SubstationDummy", gridOwner.ToCoordinates(0, 1));
|
||||
var apcEnt = entityManager.SpawnEntity("ApcDummy", gridOwner.ToCoordinates(0, 2));
|
||||
|
||||
var generatorSupplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
|
||||
substationNetBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(substationEnt);
|
||||
@@ -1273,6 +1289,7 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
{
|
||||
var map = mapManager.CreateMap();
|
||||
var grid = mapManager.CreateGrid(map);
|
||||
var gridOwner = grid.Owner;
|
||||
|
||||
const int range = 5;
|
||||
|
||||
@@ -1282,15 +1299,15 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
grid.SetTile(new Vector2i(0, i), new Tile(1));
|
||||
}
|
||||
|
||||
var apcEnt = entityManager.SpawnEntity("ApcDummy", grid.ToCoordinates(0, 0));
|
||||
var apcExtensionEnt = entityManager.SpawnEntity("CableApcExtension", grid.ToCoordinates(0, 0));
|
||||
var apcEnt = entityManager.SpawnEntity("ApcDummy", gridOwner.ToCoordinates(0, 0));
|
||||
var apcExtensionEnt = entityManager.SpawnEntity("CableApcExtension", gridOwner.ToCoordinates(0, 0));
|
||||
|
||||
// Create a powered receiver in range (range is 0 indexed)
|
||||
var powerReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", grid.ToCoordinates(0, range - 1));
|
||||
var powerReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", gridOwner.ToCoordinates(0, range - 1));
|
||||
receiver = entityManager.GetComponent<ApcPowerReceiverComponent>(powerReceiverEnt);
|
||||
|
||||
// Create an unpowered receiver outside range
|
||||
var unpoweredReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", grid.ToCoordinates(0, range));
|
||||
var unpoweredReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", gridOwner.ToCoordinates(0, range));
|
||||
unpoweredReceiver = entityManager.GetComponent<ApcPowerReceiverComponent>(unpoweredReceiverEnt);
|
||||
|
||||
var battery = entityManager.GetComponent<BatteryComponent>(apcEnt);
|
||||
|
||||
@@ -59,7 +59,7 @@ public sealed class PrototypeSaveTest
|
||||
|
||||
var tileDefinition = tileDefinitionManager["FloorSteel"]; // Wires n such disable ambiance while under the floor
|
||||
var tile = new Tile(tileDefinition.TileId);
|
||||
var coordinates = grid.ToCoordinates();
|
||||
var coordinates = grid.Owner.ToCoordinates();
|
||||
|
||||
grid.SetTile(coordinates, tile);
|
||||
});
|
||||
@@ -94,7 +94,7 @@ public sealed class PrototypeSaveTest
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(!mapManager.IsMapInitialized(mapId));
|
||||
var testLocation = grid.ToCoordinates();
|
||||
var testLocation = grid.Owner.ToCoordinates();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Ghost.Components;
|
||||
using Content.Server.Players;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Ghost;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Map;
|
||||
@@ -17,7 +12,6 @@ namespace Content.Server.Administration.Commands;
|
||||
public sealed class PersistenceSave : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _config = default!;
|
||||
[Dependency] private readonly IEntityManager _entities = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _system = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ using Content.Shared.Maps;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
@@ -11,7 +10,6 @@ namespace Content.Server.Administration.Commands;
|
||||
public sealed class VariantizeCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefManager = default!;
|
||||
|
||||
public string Command => "variantize";
|
||||
|
||||
@@ -154,7 +154,7 @@ namespace Content.Server.Administration.Managers
|
||||
plyData.ExplicitlyDeadminned = false;
|
||||
reg.Data.Active = true;
|
||||
|
||||
if (reg.Data.Stealth)
|
||||
if (!reg.Data.Stealth)
|
||||
{
|
||||
_chat.SendAdminAnnouncement(Loc.GetString("admin-manager-self-re-admin-message", ("newAdminName", session.Name)));
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
using Content.Server.Advertisements;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Advertise
|
||||
{
|
||||
[RegisterComponent, Access(typeof(AdvertiseSystem))]
|
||||
public sealed partial class AdvertiseComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Minimum time in seconds to wait before saying a new ad, in seconds. Has to be larger than or equal to 1.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("minWait")]
|
||||
public int MinimumWait { get; private set; } = 8 * 60;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum time in seconds to wait before saying a new ad, in seconds. Has to be larger than or equal
|
||||
/// to <see cref="MinimumWait"/>
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("maxWait")]
|
||||
public int MaximumWait { get; private set; } = 10 * 60;
|
||||
|
||||
/// <summary>
|
||||
/// The identifier for the advertisements pack prototype.
|
||||
/// </summary>
|
||||
[DataField("pack", customTypeSerializer:typeof(PrototypeIdSerializer<AdvertisementsPackPrototype>), required: true)]
|
||||
public string PackPrototypeId { get; private set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The next time an advertisement will be said.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public TimeSpan NextAdvertisementTime { get; set; } = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the entity will say advertisements or not.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Enabled { get; set; } = true;
|
||||
}
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
using Content.Server.Advertisements;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.VendingMachines;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Advertise
|
||||
{
|
||||
public sealed class AdvertiseSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of time between checking if advertisements should be displayed
|
||||
/// </summary>
|
||||
private readonly TimeSpan _maximumNextCheckDuration = TimeSpan.FromSeconds(15);
|
||||
|
||||
/// <summary>
|
||||
/// The next time the game will check if advertisements should be displayed
|
||||
/// </summary>
|
||||
private TimeSpan _nextCheckTime = TimeSpan.MaxValue;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<AdvertiseComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<AdvertiseComponent, PowerChangedEvent>(OnPowerChanged);
|
||||
|
||||
SubscribeLocalEvent<ApcPowerReceiverComponent, AdvertiseEnableChangeAttemptEvent>(OnPowerReceiverEnableChangeAttempt);
|
||||
SubscribeLocalEvent<VendingMachineComponent, AdvertiseEnableChangeAttemptEvent>(OnVendingEnableChangeAttempt);
|
||||
|
||||
// The component inits will lower this.
|
||||
_nextCheckTime = TimeSpan.MaxValue;
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, AdvertiseComponent advertise, ComponentInit args)
|
||||
{
|
||||
RefreshTimer(uid, advertise);
|
||||
}
|
||||
|
||||
private void OnPowerChanged(EntityUid uid, AdvertiseComponent advertise, ref PowerChangedEvent args)
|
||||
{
|
||||
SetEnabled(uid, args.Powered, advertise);
|
||||
}
|
||||
|
||||
public void RefreshTimer(EntityUid uid, AdvertiseComponent? advertise = null)
|
||||
{
|
||||
if (!Resolve(uid, ref advertise))
|
||||
return;
|
||||
|
||||
if (!advertise.Enabled)
|
||||
return;
|
||||
|
||||
var minDuration = Math.Max(1, advertise.MinimumWait);
|
||||
var maxDuration = Math.Max(minDuration, advertise.MaximumWait);
|
||||
var waitDuration = TimeSpan.FromSeconds(_random.Next(minDuration, maxDuration));
|
||||
var nextTime = _gameTiming.CurTime + waitDuration;
|
||||
|
||||
advertise.NextAdvertisementTime = nextTime;
|
||||
|
||||
_nextCheckTime = MathHelper.Min(nextTime, _nextCheckTime);
|
||||
}
|
||||
|
||||
public void SayAdvertisement(EntityUid uid, AdvertiseComponent? advertise = null)
|
||||
{
|
||||
if (!Resolve(uid, ref advertise))
|
||||
return;
|
||||
|
||||
if (_prototypeManager.TryIndex(advertise.PackPrototypeId, out AdvertisementsPackPrototype? advertisements))
|
||||
_chat.TrySendInGameICMessage(uid, Loc.GetString(_random.Pick(advertisements.Advertisements)), InGameICChatType.Speak, true);
|
||||
}
|
||||
|
||||
public void SayThankYou(EntityUid uid, AdvertiseComponent? advertise = null)
|
||||
{
|
||||
if (!Resolve(uid, ref advertise))
|
||||
return;
|
||||
|
||||
if (_prototypeManager.TryIndex(advertise.PackPrototypeId, out AdvertisementsPackPrototype? advertisements))
|
||||
{
|
||||
_chat.TrySendInGameICMessage(uid, Loc.GetString(_random.Pick(advertisements.ThankYous), ("name", Name(uid))), InGameICChatType.Speak, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetEnabled(EntityUid uid, bool enable, AdvertiseComponent? advertise = null)
|
||||
{
|
||||
if (!Resolve(uid, ref advertise))
|
||||
return;
|
||||
|
||||
if (advertise.Enabled == enable)
|
||||
return;
|
||||
|
||||
var attemptEvent = new AdvertiseEnableChangeAttemptEvent(enable);
|
||||
RaiseLocalEvent(uid, attemptEvent);
|
||||
|
||||
if (attemptEvent.Cancelled)
|
||||
return;
|
||||
|
||||
advertise.Enabled = enable;
|
||||
RefreshTimer(uid, advertise);
|
||||
}
|
||||
|
||||
private static void OnPowerReceiverEnableChangeAttempt(EntityUid uid, ApcPowerReceiverComponent component, AdvertiseEnableChangeAttemptEvent args)
|
||||
{
|
||||
if(args.Enabling && !component.Powered)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private static void OnVendingEnableChangeAttempt(EntityUid uid, VendingMachineComponent component, AdvertiseEnableChangeAttemptEvent args)
|
||||
{
|
||||
if(args.Enabling && component.Broken)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
var curTime = _gameTiming.CurTime;
|
||||
if (_nextCheckTime > curTime)
|
||||
return;
|
||||
|
||||
_nextCheckTime = curTime + _maximumNextCheckDuration;
|
||||
|
||||
var query = EntityQueryEnumerator<AdvertiseComponent>();
|
||||
while (query.MoveNext(out var uid, out var advert))
|
||||
{
|
||||
if (!advert.Enabled)
|
||||
continue;
|
||||
|
||||
// If this isn't advertising yet
|
||||
if (advert.NextAdvertisementTime > curTime)
|
||||
{
|
||||
_nextCheckTime = MathHelper.Min(advert.NextAdvertisementTime, _nextCheckTime);
|
||||
continue;
|
||||
}
|
||||
|
||||
SayAdvertisement(uid, advert);
|
||||
RefreshTimer(uid, advert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class AdvertiseEnableChangeAttemptEvent : CancellableEntityEventArgs
|
||||
{
|
||||
public bool Enabling { get; }
|
||||
|
||||
public AdvertiseEnableChangeAttemptEvent(bool enabling)
|
||||
{
|
||||
Enabling = enabling;
|
||||
}
|
||||
}
|
||||
}
|
||||
45
Content.Server/Advertise/Components/AdvertiseComponent.cs
Normal file
45
Content.Server/Advertise/Components/AdvertiseComponent.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using Content.Server.Advertise.EntitySystems;
|
||||
using Content.Shared.Advertise;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.Advertise.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Makes this entity periodically advertise by speaking a randomly selected
|
||||
/// message from a specified MessagePack into local chat.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(AdvertiseSystem))]
|
||||
public sealed partial class AdvertiseComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Minimum time in seconds to wait before saying a new ad, in seconds. Has to be larger than or equal to 1.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int MinimumWait { get; private set; } = 8 * 60;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum time in seconds to wait before saying a new ad, in seconds. Has to be larger than or equal
|
||||
/// to <see cref="MinimumWait"/>
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int MaximumWait { get; private set; } = 10 * 60;
|
||||
|
||||
/// <summary>
|
||||
/// The identifier for the advertisements pack prototype.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public ProtoId<MessagePackPrototype> Pack { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The next time an advertisement will be said.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan NextAdvertisementTime { get; set; } = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the entity will say advertisements or not.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Enabled { get; set; } = true;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Content.Shared.Advertise;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Advertise.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Causes the entity to speak using the Chat system when its ActivatableUI is closed, optionally
|
||||
/// requiring that a Flag be set as well.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(SpeakOnUIClosedSystem))]
|
||||
public sealed partial class SpeakOnUIClosedComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The identifier for the message pack prototype containing messages to be spoken by this entity.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public ProtoId<MessagePackPrototype> Pack { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is this component active? If false, no messages will be spoken.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Enabled = true;
|
||||
|
||||
/// <summary>
|
||||
/// Should messages be spoken only if the <see cref="Flag"/> is set (true), or every time the UI is closed (false)?
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool RequireFlag = true;
|
||||
|
||||
/// <summary>
|
||||
/// State variable only used if <see cref="RequireFlag"/> is true. Set with <see cref="SpeakOnUIClosedSystem.TrySetFlag"/>.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Flag;
|
||||
}
|
||||
137
Content.Server/Advertise/EntitySystems/AdvertiseSystem.cs
Normal file
137
Content.Server/Advertise/EntitySystems/AdvertiseSystem.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using Content.Server.Advertise.Components;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.VendingMachines;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Advertise.EntitySystems;
|
||||
|
||||
public sealed class AdvertiseSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of time between checking if advertisements should be displayed
|
||||
/// </summary>
|
||||
private readonly TimeSpan _maximumNextCheckDuration = TimeSpan.FromSeconds(15);
|
||||
|
||||
/// <summary>
|
||||
/// The next time the game will check if advertisements should be displayed
|
||||
/// </summary>
|
||||
private TimeSpan _nextCheckTime = TimeSpan.MaxValue;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<AdvertiseComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<AdvertiseComponent, PowerChangedEvent>(OnPowerChanged);
|
||||
|
||||
SubscribeLocalEvent<ApcPowerReceiverComponent, AdvertiseEnableChangeAttemptEvent>(OnPowerReceiverEnableChangeAttempt);
|
||||
SubscribeLocalEvent<VendingMachineComponent, AdvertiseEnableChangeAttemptEvent>(OnVendingEnableChangeAttempt);
|
||||
|
||||
// The component inits will lower this.
|
||||
_nextCheckTime = TimeSpan.MaxValue;
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, AdvertiseComponent advertise, MapInitEvent args)
|
||||
{
|
||||
RefreshTimer(uid, advertise);
|
||||
}
|
||||
|
||||
private void OnPowerChanged(EntityUid uid, AdvertiseComponent advertise, ref PowerChangedEvent args)
|
||||
{
|
||||
SetEnabled(uid, args.Powered, advertise);
|
||||
}
|
||||
|
||||
public void RefreshTimer(EntityUid uid, AdvertiseComponent? advertise = null)
|
||||
{
|
||||
if (!Resolve(uid, ref advertise))
|
||||
return;
|
||||
|
||||
if (!advertise.Enabled)
|
||||
return;
|
||||
|
||||
var minDuration = Math.Max(1, advertise.MinimumWait);
|
||||
var maxDuration = Math.Max(minDuration, advertise.MaximumWait);
|
||||
var waitDuration = TimeSpan.FromSeconds(_random.Next(minDuration, maxDuration));
|
||||
var nextTime = _gameTiming.CurTime + waitDuration;
|
||||
|
||||
advertise.NextAdvertisementTime = nextTime;
|
||||
|
||||
_nextCheckTime = MathHelper.Min(nextTime, _nextCheckTime);
|
||||
}
|
||||
|
||||
public void SayAdvertisement(EntityUid uid, AdvertiseComponent? advertise = null)
|
||||
{
|
||||
if (!Resolve(uid, ref advertise))
|
||||
return;
|
||||
|
||||
if (_prototypeManager.TryIndex(advertise.Pack, out var advertisements))
|
||||
_chat.TrySendInGameICMessage(uid, Loc.GetString(_random.Pick(advertisements.Messages)), InGameICChatType.Speak, hideChat: true);
|
||||
}
|
||||
|
||||
public void SetEnabled(EntityUid uid, bool enable, AdvertiseComponent? advertise = null)
|
||||
{
|
||||
if (!Resolve(uid, ref advertise))
|
||||
return;
|
||||
|
||||
if (advertise.Enabled == enable)
|
||||
return;
|
||||
|
||||
var attemptEvent = new AdvertiseEnableChangeAttemptEvent(enable);
|
||||
RaiseLocalEvent(uid, attemptEvent);
|
||||
|
||||
if (attemptEvent.Cancelled)
|
||||
return;
|
||||
|
||||
advertise.Enabled = enable;
|
||||
RefreshTimer(uid, advertise);
|
||||
}
|
||||
|
||||
private static void OnPowerReceiverEnableChangeAttempt(EntityUid uid, ApcPowerReceiverComponent component, AdvertiseEnableChangeAttemptEvent args)
|
||||
{
|
||||
if (args.Enabling && !component.Powered)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
private static void OnVendingEnableChangeAttempt(EntityUid uid, VendingMachineComponent component, AdvertiseEnableChangeAttemptEvent args)
|
||||
{
|
||||
if (args.Enabling && component.Broken)
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
var curTime = _gameTiming.CurTime;
|
||||
if (_nextCheckTime > curTime)
|
||||
return;
|
||||
|
||||
_nextCheckTime = curTime + _maximumNextCheckDuration;
|
||||
|
||||
var query = EntityQueryEnumerator<AdvertiseComponent>();
|
||||
while (query.MoveNext(out var uid, out var advert))
|
||||
{
|
||||
if (!advert.Enabled)
|
||||
continue;
|
||||
|
||||
// If this isn't advertising yet
|
||||
if (advert.NextAdvertisementTime > curTime)
|
||||
{
|
||||
_nextCheckTime = MathHelper.Min(advert.NextAdvertisementTime, _nextCheckTime);
|
||||
continue;
|
||||
}
|
||||
|
||||
SayAdvertisement(uid, advert);
|
||||
RefreshTimer(uid, advert);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class AdvertiseEnableChangeAttemptEvent(bool enabling) : CancellableEntityEventArgs
|
||||
{
|
||||
public bool Enabling { get; } = enabling;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using Content.Server.Advertise.Components;
|
||||
using Content.Server.Chat.Systems;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.Advertise;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Advertise;
|
||||
|
||||
public sealed partial class SpeakOnUIClosedSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly ChatSystem _chat = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SpeakOnUIClosedComponent, BoundUIClosedEvent>(OnBoundUIClosed);
|
||||
}
|
||||
private void OnBoundUIClosed(Entity<SpeakOnUIClosedComponent> entity, ref BoundUIClosedEvent args)
|
||||
{
|
||||
if (!TryComp(entity, out ActivatableUIComponent? activatable) || !args.UiKey.Equals(activatable.Key))
|
||||
return;
|
||||
|
||||
if (entity.Comp.RequireFlag && !entity.Comp.Flag)
|
||||
return;
|
||||
|
||||
TrySpeak((entity, entity.Comp));
|
||||
}
|
||||
|
||||
public bool TrySpeak(Entity<SpeakOnUIClosedComponent?> entity)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp))
|
||||
return false;
|
||||
|
||||
if (!entity.Comp.Enabled)
|
||||
return false;
|
||||
|
||||
if (!_prototypeManager.TryIndex(entity.Comp.Pack, out MessagePackPrototype? messagePack))
|
||||
return false;
|
||||
|
||||
var message = Loc.GetString(_random.Pick(messagePack.Messages), ("name", Name(entity)));
|
||||
_chat.TrySendInGameICMessage(entity, message, InGameICChatType.Speak, true);
|
||||
entity.Comp.Flag = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TrySetFlag(Entity<SpeakOnUIClosedComponent?> entity, bool value = true)
|
||||
{
|
||||
if (!Resolve(entity, ref entity.Comp))
|
||||
return false;
|
||||
|
||||
entity.Comp.Flag = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Advertisements
|
||||
{
|
||||
[Serializable, Prototype("advertisementsPack")]
|
||||
public sealed partial class AdvertisementsPackPrototype : IPrototype
|
||||
{
|
||||
[ViewVariables]
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
[DataField("advertisements")]
|
||||
public List<string> Advertisements { get; private set; } = new();
|
||||
|
||||
[DataField("thankyous")]
|
||||
public List<string> ThankYous { get; private set; } = new();
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ public sealed partial class AnomalySystem
|
||||
var tile = new Vector2i(randomX, randomY);
|
||||
|
||||
// no air-blocked areas.
|
||||
if (_atmosphere.IsTileSpace(grid, xform.MapUid, tile, mapGridComp: gridComp) ||
|
||||
if (_atmosphere.IsTileSpace(grid, xform.MapUid, tile) ||
|
||||
_atmosphere.IsTileAirBlocked(grid, tile, mapGridComp: gridComp))
|
||||
{
|
||||
continue;
|
||||
|
||||
@@ -8,6 +8,7 @@ using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Teleportation.Components;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Anomaly.Effects;
|
||||
@@ -35,20 +36,19 @@ public sealed class BluespaceAnomalySystem : EntitySystem
|
||||
var range = component.MaxShuffleRadius * args.Severity;
|
||||
var mobs = new HashSet<Entity<MobStateComponent>>();
|
||||
_lookup.GetEntitiesInRange(xform.Coordinates, range, mobs);
|
||||
var allEnts = new List<EntityUid>(mobs.Select(m => m.Owner)) { uid };
|
||||
var coords = new List<Vector2>();
|
||||
var allEnts = new ValueList<EntityUid>(mobs.Select(m => m.Owner)) { uid };
|
||||
var coords = new ValueList<Vector2>();
|
||||
foreach (var ent in allEnts)
|
||||
{
|
||||
if (xformQuery.TryGetComponent(ent, out var xf))
|
||||
coords.Add(xf.MapPosition.Position);
|
||||
if (xformQuery.TryGetComponent(ent, out var allXform))
|
||||
coords.Add(_xform.GetWorldPosition(allXform));
|
||||
}
|
||||
|
||||
_random.Shuffle(coords);
|
||||
for (var i = 0; i < allEnts.Count; i++)
|
||||
{
|
||||
|
||||
_adminLogger.Add(LogType.Teleport, $"{ToPrettyString(allEnts[i])} has been shuffled to {coords[i]} by the {ToPrettyString(uid)} at {xform.Coordinates}");
|
||||
_xform.SetWorldPosition(allEnts[i], coords[i], xformQuery);
|
||||
_xform.SetWorldPosition(allEnts[i], coords[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ using Content.Shared.Anomaly;
|
||||
using Content.Shared.Anomaly.Components;
|
||||
using Content.Shared.Anomaly.Effects;
|
||||
using Content.Shared.Anomaly.Effects.Components;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Random;
|
||||
@@ -12,7 +11,6 @@ namespace Content.Server.Anomaly.Effects;
|
||||
public sealed class EntityAnomalySystem : SharedEntityAnomalySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAnomalySystem _anomaly = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ public sealed class GasProducerAnomalySystem : EntitySystem
|
||||
if (tilerefs.Length == 0)
|
||||
return;
|
||||
|
||||
var mixture = _atmosphere.GetTileMixture((uid, xform), grid, true);
|
||||
var mixture = _atmosphere.GetTileMixture((uid, xform), true);
|
||||
if (mixture != null)
|
||||
{
|
||||
mixture.AdjustMoles(gas, mols);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Server.Advertise;
|
||||
using Content.Server.Advertise.Components;
|
||||
using Content.Shared.Arcade;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
@@ -9,6 +11,7 @@ namespace Content.Server.Arcade.BlockGame;
|
||||
public sealed class BlockGameArcadeSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
[Dependency] private readonly SpeakOnUIClosedSystem _speakOnUIClosed = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -88,8 +91,6 @@ public sealed class BlockGameArcadeSystem : EntitySystem
|
||||
component.Spectators.Remove(component.Player);
|
||||
UpdatePlayerStatus(uid, component.Player, blockGame: component);
|
||||
}
|
||||
else
|
||||
component.Player = null;
|
||||
|
||||
UpdatePlayerStatus(uid, temp, blockGame: component);
|
||||
}
|
||||
@@ -122,6 +123,9 @@ public sealed class BlockGameArcadeSystem : EntitySystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (TryComp<SpeakOnUIClosedComponent>(uid, out var speakComponent))
|
||||
_speakOnUIClosed.TrySetFlag((uid, speakComponent));
|
||||
|
||||
component.Game.ProcessInput(msg.PlayerAction);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Shared.UserInterface;
|
||||
using Content.Server.Advertise;
|
||||
using Content.Server.Advertise.Components;
|
||||
using static Content.Shared.Arcade.SharedSpaceVillainArcadeComponent;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
@@ -13,6 +15,7 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||
[Dependency] private readonly SpeakOnUIClosedSystem _speakOnUIClosed = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -79,6 +82,9 @@ public sealed partial class SpaceVillainArcadeSystem : EntitySystem
|
||||
case PlayerAction.Heal:
|
||||
case PlayerAction.Recharge:
|
||||
component.Game.ExecutePlayerAction(uid, msg.PlayerAction, component);
|
||||
// Any sort of gameplay action counts
|
||||
if (TryComp<SpeakOnUIClosedComponent>(uid, out var speakComponent))
|
||||
_speakOnUIClosed.TrySetFlag((uid, speakComponent));
|
||||
break;
|
||||
case PlayerAction.NewGame:
|
||||
_audioSystem.PlayPvs(component.NewGameSound, uid, AudioParams.Default.WithVolume(-4f));
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.Piping.Components;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
@@ -15,7 +13,6 @@ public sealed class AirFilterSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
|
||||
[Dependency] private readonly IMapManager _map = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
||||
@@ -4,7 +4,6 @@ using Content.Server.Atmos.Piping.Components;
|
||||
using Content.Server.Atmos.Reactions;
|
||||
using Content.Server.NodeContainer.NodeGroups;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -49,13 +48,7 @@ public partial class AtmosphereSystem
|
||||
return GetTileMixture(gridUid, mapUid, position, excite);
|
||||
}
|
||||
|
||||
public bool HasAtmosphere(EntityUid gridUid)
|
||||
{
|
||||
var ev = new HasAtmosphereMethodEvent(gridUid);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
|
||||
return ev.Result;
|
||||
}
|
||||
public bool HasAtmosphere(EntityUid gridUid) => _atmosQuery.HasComponent(gridUid);
|
||||
|
||||
public bool SetSimulatedGrid(EntityUid gridUid, bool simulated)
|
||||
{
|
||||
@@ -91,43 +84,60 @@ public partial class AtmosphereSystem
|
||||
entity.Comp.InvalidatedCoords.Add(tile);
|
||||
}
|
||||
|
||||
public GasMixture?[]? GetTileMixtures(EntityUid? gridUid, EntityUid? mapUid, List<Vector2i> tiles, bool excite = false)
|
||||
public GasMixture?[]? GetTileMixtures(Entity<GridAtmosphereComponent?>? grid, Entity<MapAtmosphereComponent?>? map, List<Vector2i> tiles, bool excite = false)
|
||||
{
|
||||
var ev = new GetTileMixturesMethodEvent(gridUid, mapUid, tiles, excite);
|
||||
GasMixture?[]? mixtures = null;
|
||||
var handled = false;
|
||||
|
||||
// If we've been passed a grid, try to let it handle it.
|
||||
if (gridUid.HasValue)
|
||||
if (grid is {} gridEnt && Resolve(gridEnt, ref gridEnt.Comp))
|
||||
{
|
||||
DebugTools.Assert(_mapManager.IsGrid(gridUid.Value));
|
||||
RaiseLocalEvent(gridUid.Value, ref ev, false);
|
||||
handled = true;
|
||||
mixtures = new GasMixture?[tiles.Count];
|
||||
|
||||
for (var i = 0; i < tiles.Count; i++)
|
||||
{
|
||||
var tile = tiles[i];
|
||||
if (!gridEnt.Comp.Tiles.TryGetValue(tile, out var atmosTile))
|
||||
{
|
||||
// need to get map atmosphere
|
||||
handled = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
mixtures[i] = atmosTile.Air;
|
||||
|
||||
if (excite)
|
||||
gridEnt.Comp.InvalidatedCoords.Add(tile);
|
||||
}
|
||||
}
|
||||
|
||||
if (ev.Handled)
|
||||
return ev.Mixtures;
|
||||
if (handled)
|
||||
return mixtures;
|
||||
|
||||
// We either don't have a grid, or the event wasn't handled.
|
||||
// Let the map handle it instead, and also broadcast the event.
|
||||
if (mapUid.HasValue)
|
||||
if (map is {} mapEnt && _mapAtmosQuery.Resolve(mapEnt, ref mapEnt.Comp))
|
||||
{
|
||||
DebugTools.Assert(_mapManager.IsMap(mapUid.Value));
|
||||
RaiseLocalEvent(mapUid.Value, ref ev, true);
|
||||
}
|
||||
else
|
||||
RaiseLocalEvent(ref ev);
|
||||
mixtures ??= new GasMixture?[tiles.Count];
|
||||
for (var i = 0; i < tiles.Count; i++)
|
||||
{
|
||||
mixtures[i] ??= mapEnt.Comp.Mixture;
|
||||
}
|
||||
|
||||
if (ev.Handled)
|
||||
return ev.Mixtures;
|
||||
return mixtures;
|
||||
}
|
||||
|
||||
// Default to a space mixture... This is a space game, after all!
|
||||
ev.Mixtures ??= new GasMixture?[tiles.Count];
|
||||
mixtures ??= new GasMixture?[tiles.Count];
|
||||
for (var i = 0; i < tiles.Count; i++)
|
||||
{
|
||||
ev.Mixtures[i] ??= GasMixture.SpaceGas;
|
||||
mixtures[i] ??= GasMixture.SpaceGas;
|
||||
}
|
||||
return ev.Mixtures;
|
||||
return mixtures;
|
||||
}
|
||||
|
||||
public GasMixture? GetTileMixture (Entity<TransformComponent?> entity, MapGridComponent? grid = null, bool excite = false)
|
||||
public GasMixture? GetTileMixture (Entity<TransformComponent?> entity, bool excite = false)
|
||||
{
|
||||
if (!Resolve(entity.Owner, ref entity.Comp))
|
||||
return null;
|
||||
@@ -136,32 +146,24 @@ public partial class AtmosphereSystem
|
||||
return GetTileMixture(entity.Comp.GridUid, entity.Comp.MapUid, indices, excite);
|
||||
}
|
||||
|
||||
public GasMixture? GetTileMixture(EntityUid? gridUid, EntityUid? mapUid, Vector2i gridTile, bool excite = false)
|
||||
public GasMixture? GetTileMixture(Entity<GridAtmosphereComponent?>? grid, Entity<MapAtmosphereComponent?>? map, Vector2i gridTile, bool excite = false)
|
||||
{
|
||||
var ev = new GetTileMixtureMethodEvent(gridUid, mapUid, gridTile, excite);
|
||||
|
||||
// If we've been passed a grid, try to let it handle it.
|
||||
if(gridUid.HasValue)
|
||||
if (grid is {} gridEnt
|
||||
&& Resolve(gridEnt, ref gridEnt.Comp, false)
|
||||
&& gridEnt.Comp.Tiles.TryGetValue(gridTile, out var tile))
|
||||
{
|
||||
DebugTools.Assert(_mapManager.IsGrid(gridUid.Value));
|
||||
RaiseLocalEvent(gridUid.Value, ref ev, false);
|
||||
if (excite)
|
||||
gridEnt.Comp.InvalidatedCoords.Add(gridTile);
|
||||
|
||||
return tile.Air;
|
||||
}
|
||||
|
||||
if (ev.Handled)
|
||||
return ev.Mixture;
|
||||
|
||||
// We either don't have a grid, or the event wasn't handled.
|
||||
// Let the map handle it instead, and also broadcast the event.
|
||||
if(mapUid.HasValue)
|
||||
{
|
||||
DebugTools.Assert(_mapManager.IsMap(mapUid.Value));
|
||||
RaiseLocalEvent(mapUid.Value, ref ev, true);
|
||||
}
|
||||
else
|
||||
RaiseLocalEvent(ref ev);
|
||||
if (map is {} mapEnt && _mapAtmosQuery.Resolve(mapEnt, ref mapEnt.Comp, false))
|
||||
return mapEnt.Comp.Mixture;
|
||||
|
||||
// Default to a space mixture... This is a space game, after all!
|
||||
return ev.Mixture ?? GasMixture.SpaceGas;
|
||||
return GasMixture.SpaceGas;
|
||||
}
|
||||
|
||||
public ReactionResult ReactTile(EntityUid gridId, Vector2i tile)
|
||||
@@ -176,66 +178,67 @@ public partial class AtmosphereSystem
|
||||
|
||||
public bool IsTileAirBlocked(EntityUid gridUid, Vector2i tile, AtmosDirection directions = AtmosDirection.All, MapGridComponent? mapGridComp = null)
|
||||
{
|
||||
if (!Resolve(gridUid, ref mapGridComp))
|
||||
if (!Resolve(gridUid, ref mapGridComp, false))
|
||||
return false;
|
||||
|
||||
var data = GetAirtightData(gridUid, mapGridComp, tile);
|
||||
return data.BlockedDirections.IsFlagSet(directions);
|
||||
}
|
||||
|
||||
public bool IsTileSpace(EntityUid? gridUid, EntityUid? mapUid, Vector2i tile, MapGridComponent? mapGridComp = null)
|
||||
public bool IsTileSpace(Entity<GridAtmosphereComponent?>? grid, Entity<MapAtmosphereComponent?>? map, Vector2i tile)
|
||||
{
|
||||
var ev = new IsTileSpaceMethodEvent(gridUid, mapUid, tile, mapGridComp);
|
||||
if (grid is {} gridEnt && _atmosQuery.Resolve(gridEnt, ref gridEnt.Comp, false)
|
||||
&& gridEnt.Comp.Tiles.TryGetValue(tile, out var tileAtmos))
|
||||
{
|
||||
return tileAtmos.Space;
|
||||
}
|
||||
|
||||
// Try to let the grid (if any) handle it...
|
||||
if (gridUid.HasValue)
|
||||
RaiseLocalEvent(gridUid.Value, ref ev, false);
|
||||
|
||||
// If we didn't have a grid or the event wasn't handled
|
||||
// we let the map know, and also broadcast the event while at it!
|
||||
if (mapUid.HasValue && !ev.Handled)
|
||||
RaiseLocalEvent(mapUid.Value, ref ev, true);
|
||||
|
||||
// We didn't have a map, and the event isn't handled, therefore broadcast the event.
|
||||
else if (!mapUid.HasValue && !ev.Handled)
|
||||
RaiseLocalEvent(ref ev);
|
||||
if (map is {} mapEnt && _mapAtmosQuery.Resolve(mapEnt, ref mapEnt.Comp, false))
|
||||
return mapEnt.Comp.Space;
|
||||
|
||||
// If nothing handled the event, it'll default to true.
|
||||
// Oh well, this is a space game after all, deal with it!
|
||||
return ev.Result;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsTileMixtureProbablySafe(EntityUid? gridUid, EntityUid mapUid, Vector2i tile)
|
||||
public bool IsTileMixtureProbablySafe(Entity<GridAtmosphereComponent?>? grid, Entity<MapAtmosphereComponent?> map, Vector2i tile)
|
||||
{
|
||||
return IsMixtureProbablySafe(GetTileMixture(gridUid, mapUid, tile));
|
||||
return IsMixtureProbablySafe(GetTileMixture(grid, map, tile));
|
||||
}
|
||||
|
||||
public float GetTileHeatCapacity(EntityUid? gridUid, EntityUid mapUid, Vector2i tile)
|
||||
public float GetTileHeatCapacity(Entity<GridAtmosphereComponent?>? grid, Entity<MapAtmosphereComponent?> map, Vector2i tile)
|
||||
{
|
||||
return GetHeatCapacity(GetTileMixture(gridUid, mapUid, tile) ?? GasMixture.SpaceGas);
|
||||
return GetHeatCapacity(GetTileMixture(grid, map, tile) ?? GasMixture.SpaceGas);
|
||||
}
|
||||
|
||||
public IEnumerable<Vector2i> GetAdjacentTiles(EntityUid gridUid, Vector2i tile)
|
||||
public TileMixtureEnumerator GetAdjacentTileMixtures(Entity<GridAtmosphereComponent?> grid, Vector2i tile, bool includeBlocked = false, bool excite = false)
|
||||
{
|
||||
var ev = new GetAdjacentTilesMethodEvent(gridUid, tile);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
if (!_atmosQuery.Resolve(grid, ref grid.Comp, false))
|
||||
return TileMixtureEnumerator.Empty;
|
||||
|
||||
return ev.Result ?? Enumerable.Empty<Vector2i>();
|
||||
return !grid.Comp.Tiles.TryGetValue(tile, out var atmosTile)
|
||||
? TileMixtureEnumerator.Empty
|
||||
: new(atmosTile.AdjacentTiles);
|
||||
}
|
||||
|
||||
public IEnumerable<GasMixture> GetAdjacentTileMixtures(EntityUid gridUid, Vector2i tile, bool includeBlocked = false, bool excite = false)
|
||||
{
|
||||
var ev = new GetAdjacentTileMixturesMethodEvent(gridUid, tile, includeBlocked, excite);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
|
||||
return ev.Result ?? Enumerable.Empty<GasMixture>();
|
||||
}
|
||||
|
||||
public void HotspotExpose(EntityUid gridUid, Vector2i tile, float exposedTemperature, float exposedVolume,
|
||||
public void HotspotExpose(Entity<GridAtmosphereComponent?> grid, Vector2i tile, float exposedTemperature, float exposedVolume,
|
||||
EntityUid? sparkSourceUid = null, bool soh = false)
|
||||
{
|
||||
var ev = new HotspotExposeMethodEvent(gridUid, sparkSourceUid, tile, exposedTemperature, exposedVolume, soh);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
if (!_atmosQuery.Resolve(grid, ref grid.Comp, false))
|
||||
return;
|
||||
|
||||
if (grid.Comp.Tiles.TryGetValue(tile, out var atmosTile))
|
||||
HotspotExpose(grid.Comp, atmosTile, exposedTemperature, exposedVolume, soh, sparkSourceUid);
|
||||
}
|
||||
|
||||
public void HotspotExpose(TileAtmosphere tile, float exposedTemperature, float exposedVolume,
|
||||
EntityUid? sparkSourceUid = null, bool soh = false)
|
||||
{
|
||||
if (!_atmosQuery.TryGetComponent(tile.GridIndex, out var atmos))
|
||||
return;
|
||||
|
||||
DebugTools.Assert(atmos.Tiles.TryGetValue(tile.GridIndices, out var tmp) && tmp == tile);
|
||||
HotspotExpose(atmos, tile, exposedTemperature, exposedVolume, soh, sparkSourceUid);
|
||||
}
|
||||
|
||||
public void HotspotExtinguish(EntityUid gridUid, Vector2i tile)
|
||||
@@ -253,39 +256,45 @@ public partial class AtmosphereSystem
|
||||
return ev.Result;
|
||||
}
|
||||
|
||||
public void AddPipeNet(EntityUid gridUid, PipeNet pipeNet)
|
||||
public bool AddPipeNet(Entity<GridAtmosphereComponent?> grid, PipeNet pipeNet)
|
||||
{
|
||||
var ev = new AddPipeNetMethodEvent(gridUid, pipeNet);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
return _atmosQuery.Resolve(grid, ref grid.Comp, false) && grid.Comp.PipeNets.Add(pipeNet);
|
||||
}
|
||||
|
||||
public void RemovePipeNet(EntityUid gridUid, PipeNet pipeNet)
|
||||
public bool RemovePipeNet(Entity<GridAtmosphereComponent?> grid, PipeNet pipeNet)
|
||||
{
|
||||
var ev = new RemovePipeNetMethodEvent(gridUid, pipeNet);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
return _atmosQuery.Resolve(grid, ref grid.Comp, false) && grid.Comp.PipeNets.Remove(pipeNet);
|
||||
}
|
||||
|
||||
public bool AddAtmosDevice(EntityUid gridUid, AtmosDeviceComponent device)
|
||||
public bool AddAtmosDevice(Entity<GridAtmosphereComponent?> grid, Entity<AtmosDeviceComponent> device)
|
||||
{
|
||||
// TODO: check device is on grid
|
||||
DebugTools.Assert(device.Comp.JoinedGrid == null);
|
||||
DebugTools.Assert(Transform(device).GridUid == grid);
|
||||
|
||||
var ev = new AddAtmosDeviceMethodEvent(gridUid, device);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
return ev.Result;
|
||||
if (!_atmosQuery.Resolve(grid, ref grid.Comp, false))
|
||||
return false;
|
||||
|
||||
if (!grid.Comp.AtmosDevices.Add(device))
|
||||
return false;
|
||||
|
||||
device.Comp.JoinedGrid = grid;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool RemoveAtmosDevice(EntityUid gridUid, AtmosDeviceComponent device)
|
||||
public bool RemoveAtmosDevice(Entity<GridAtmosphereComponent?> grid, Entity<AtmosDeviceComponent> device)
|
||||
{
|
||||
// TODO: check device is on grid
|
||||
DebugTools.Assert(device.Comp.JoinedGrid == grid);
|
||||
|
||||
var ev = new RemoveAtmosDeviceMethodEvent(gridUid, device);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
return ev.Result;
|
||||
if (!_atmosQuery.Resolve(grid, ref grid.Comp, false))
|
||||
return false;
|
||||
|
||||
if (!grid.Comp.AtmosDevices.Remove(device))
|
||||
return false;
|
||||
|
||||
device.Comp.JoinedGrid = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
[ByRefEvent] private record struct HasAtmosphereMethodEvent
|
||||
(EntityUid Grid, bool Result = false, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct SetSimulatedGridMethodEvent
|
||||
(EntityUid Grid, bool Simulated, bool Handled = false);
|
||||
|
||||
@@ -295,43 +304,12 @@ public partial class AtmosphereSystem
|
||||
[ByRefEvent] private record struct GetAllMixturesMethodEvent
|
||||
(EntityUid Grid, bool Excite = false, IEnumerable<GasMixture>? Mixtures = null, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct GetTileMixturesMethodEvent
|
||||
(EntityUid? GridUid, EntityUid? MapUid, List<Vector2i> Tiles, bool Excite = false, GasMixture?[]? Mixtures = null, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct GetTileMixtureMethodEvent
|
||||
(EntityUid? GridUid, EntityUid? MapUid, Vector2i Tile, bool Excite = false, GasMixture? Mixture = null, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct ReactTileMethodEvent
|
||||
(EntityUid GridId, Vector2i Tile, ReactionResult Result = default, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct IsTileSpaceMethodEvent
|
||||
(EntityUid? Grid, EntityUid? Map, Vector2i Tile, MapGridComponent? MapGridComponent = null, bool Result = true, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct GetAdjacentTilesMethodEvent
|
||||
(EntityUid Grid, Vector2i Tile, IEnumerable<Vector2i>? Result = null, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct GetAdjacentTileMixturesMethodEvent
|
||||
(EntityUid Grid, Vector2i Tile, bool IncludeBlocked, bool Excite,
|
||||
IEnumerable<GasMixture>? Result = null, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct HotspotExposeMethodEvent
|
||||
(EntityUid Grid, EntityUid? SparkSourceUid, Vector2i Tile, float ExposedTemperature, float ExposedVolume, bool soh, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct HotspotExtinguishMethodEvent
|
||||
(EntityUid Grid, Vector2i Tile, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct IsHotspotActiveMethodEvent
|
||||
(EntityUid Grid, Vector2i Tile, bool Result = false, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct AddPipeNetMethodEvent
|
||||
(EntityUid Grid, PipeNet PipeNet, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct RemovePipeNetMethodEvent
|
||||
(EntityUid Grid, PipeNet PipeNet, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct AddAtmosDeviceMethodEvent
|
||||
(EntityUid Grid, AtmosDeviceComponent Device, bool Result = false, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct RemoveAtmosDeviceMethodEvent
|
||||
(EntityUid Grid, AtmosDeviceComponent Device, bool Result = false, bool Handled = false);
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ public sealed partial class AtmosphereSystem
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tile.Immutable && !IsTileSpace(euid, transform.MapUid, indices, gridComp))
|
||||
if (tile.Immutable && !IsTileSpace(euid, transform.MapUid, indices))
|
||||
{
|
||||
tile = new GasMixture(tile.Volume) { Temperature = tile.Temperature };
|
||||
tileMain.Air = tile;
|
||||
|
||||
@@ -20,22 +20,11 @@ public sealed partial class AtmosphereSystem
|
||||
|
||||
#region Atmos API Subscriptions
|
||||
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, HasAtmosphereMethodEvent>(GridHasAtmosphere);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, IsSimulatedGridMethodEvent>(GridIsSimulated);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, GetAllMixturesMethodEvent>(GridGetAllMixtures);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, GetTileMixtureMethodEvent>(GridGetTileMixture);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, GetTileMixturesMethodEvent>(GridGetTileMixtures);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, ReactTileMethodEvent>(GridReactTile);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, IsTileSpaceMethodEvent>(GridIsTileSpace);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, GetAdjacentTilesMethodEvent>(GridGetAdjacentTiles);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, GetAdjacentTileMixturesMethodEvent>(GridGetAdjacentTileMixtures);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, HotspotExposeMethodEvent>(GridHotspotExpose);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, HotspotExtinguishMethodEvent>(GridHotspotExtinguish);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, IsHotspotActiveMethodEvent>(GridIsHotspotActive);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, AddPipeNetMethodEvent>(GridAddPipeNet);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, RemovePipeNetMethodEvent>(GridRemovePipeNet);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, AddAtmosDeviceMethodEvent>(GridAddAtmosDevice);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, RemoveAtmosDeviceMethodEvent>(GridRemoveAtmosDevice);
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -120,15 +109,6 @@ public sealed partial class AtmosphereSystem
|
||||
}
|
||||
}
|
||||
|
||||
private void GridHasAtmosphere(EntityUid uid, GridAtmosphereComponent component, ref HasAtmosphereMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Result = true;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridIsSimulated(EntityUid uid, GridAtmosphereComponent component, ref IsSimulatedGridMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
@@ -167,48 +147,6 @@ public sealed partial class AtmosphereSystem
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridGetTileMixture(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref GetTileMixtureMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!component.Tiles.TryGetValue(args.Tile, out var tile))
|
||||
return; // Do NOT handle the event if we don't have that tile, the map will handle it instead.
|
||||
|
||||
if (args.Excite)
|
||||
component.InvalidatedCoords.Add(args.Tile);
|
||||
|
||||
args.Mixture = tile.Air;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridGetTileMixtures(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref GetTileMixturesMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
args.Mixtures = new GasMixture?[args.Tiles.Count];
|
||||
|
||||
for (var i = 0; i < args.Tiles.Count; i++)
|
||||
{
|
||||
var tile = args.Tiles[i];
|
||||
if (!component.Tiles.TryGetValue(tile, out var atmosTile))
|
||||
{
|
||||
// need to get map atmosphere
|
||||
args.Handled = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (args.Excite)
|
||||
component.InvalidatedCoords.Add(tile);
|
||||
|
||||
args.Mixtures[i] = atmosTile.Air;
|
||||
}
|
||||
}
|
||||
|
||||
private void GridReactTile(EntityUid uid, GridAtmosphereComponent component, ref ReactTileMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
@@ -221,67 +159,6 @@ public sealed partial class AtmosphereSystem
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridIsTileSpace(EntityUid uid, GridAtmosphereComponent component, ref IsTileSpaceMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
// We don't have that tile, so let the map handle it.
|
||||
if (!component.Tiles.TryGetValue(args.Tile, out var tile))
|
||||
return;
|
||||
|
||||
args.Result = tile.Space;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridGetAdjacentTiles(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref GetAdjacentTilesMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!component.Tiles.TryGetValue(args.Tile, out var tile))
|
||||
return;
|
||||
|
||||
IEnumerable<Vector2i> EnumerateAdjacent(GridAtmosphereComponent grid, TileAtmosphere t)
|
||||
{
|
||||
foreach (var adj in t.AdjacentTiles)
|
||||
{
|
||||
if (adj == null)
|
||||
continue;
|
||||
|
||||
yield return adj.GridIndices;
|
||||
}
|
||||
}
|
||||
|
||||
args.Result = EnumerateAdjacent(component, tile);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridGetAdjacentTileMixtures(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref GetAdjacentTileMixturesMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!component.Tiles.TryGetValue(args.Tile, out var tile))
|
||||
return;
|
||||
|
||||
IEnumerable<GasMixture> EnumerateAdjacent(GridAtmosphereComponent grid, TileAtmosphere t)
|
||||
{
|
||||
foreach (var adj in t.AdjacentTiles)
|
||||
{
|
||||
if (adj?.Air == null)
|
||||
continue;
|
||||
|
||||
yield return adj.Air;
|
||||
}
|
||||
}
|
||||
|
||||
args.Result = EnumerateAdjacent(component, tile);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update array of adjacent tiles and the adjacency flags. Optionally activates all tiles with modified adjacencies.
|
||||
/// </summary>
|
||||
@@ -357,18 +234,6 @@ public sealed partial class AtmosphereSystem
|
||||
return (air, map.Space);
|
||||
}
|
||||
|
||||
private void GridHotspotExpose(EntityUid uid, GridAtmosphereComponent component, ref HotspotExposeMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!component.Tiles.TryGetValue(args.Tile, out var tile))
|
||||
return;
|
||||
|
||||
HotspotExpose(component, tile, args.ExposedTemperature, args.ExposedVolume, args.soh, args.SparkSourceUid);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridHotspotExtinguish(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref HotspotExtinguishMethodEvent args)
|
||||
{
|
||||
@@ -445,49 +310,6 @@ public sealed partial class AtmosphereSystem
|
||||
tile.Air.Temperature = totalTemperature / count;
|
||||
}
|
||||
|
||||
private void GridAddPipeNet(EntityUid uid, GridAtmosphereComponent component, ref AddPipeNetMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = component.PipeNets.Add(args.PipeNet);
|
||||
}
|
||||
|
||||
private void GridRemovePipeNet(EntityUid uid, GridAtmosphereComponent component, ref RemovePipeNetMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = component.PipeNets.Remove(args.PipeNet);
|
||||
}
|
||||
|
||||
private void GridAddAtmosDevice(Entity<GridAtmosphereComponent> grid, ref AddAtmosDeviceMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!grid.Comp.AtmosDevices.Add((args.Device.Owner, args.Device)))
|
||||
return;
|
||||
|
||||
args.Device.JoinedGrid = grid;
|
||||
args.Handled = true;
|
||||
args.Result = true;
|
||||
}
|
||||
|
||||
private void GridRemoveAtmosDevice(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref RemoveAtmosDeviceMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!component.AtmosDevices.Remove((args.Device.Owner, args.Device)))
|
||||
return;
|
||||
|
||||
args.Device.JoinedGrid = null;
|
||||
args.Handled = true;
|
||||
args.Result = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Repopulates all tiles on a grid atmosphere.
|
||||
/// </summary>
|
||||
|
||||
@@ -12,9 +12,6 @@ public partial class AtmosphereSystem
|
||||
{
|
||||
SubscribeLocalEvent<MapAtmosphereComponent, ComponentInit>(OnMapStartup);
|
||||
SubscribeLocalEvent<MapAtmosphereComponent, ComponentRemove>(OnMapRemove);
|
||||
SubscribeLocalEvent<MapAtmosphereComponent, IsTileSpaceMethodEvent>(MapIsTileSpace);
|
||||
SubscribeLocalEvent<MapAtmosphereComponent, GetTileMixtureMethodEvent>(MapGetTileMixture);
|
||||
SubscribeLocalEvent<MapAtmosphereComponent, GetTileMixturesMethodEvent>(MapGetTileMixtures);
|
||||
SubscribeLocalEvent<MapAtmosphereComponent, ComponentGetState>(OnMapGetState);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, EntParentChangedMessage>(OnGridParentChanged);
|
||||
}
|
||||
@@ -31,37 +28,6 @@ public partial class AtmosphereSystem
|
||||
RefreshAllGridMapAtmospheres(uid);
|
||||
}
|
||||
|
||||
private void MapIsTileSpace(EntityUid uid, MapAtmosphereComponent component, ref IsTileSpaceMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Result = component.Space;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void MapGetTileMixture(EntityUid uid, MapAtmosphereComponent component, ref GetTileMixtureMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Mixture = component.Mixture;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void MapGetTileMixtures(EntityUid uid, MapAtmosphereComponent component, ref GetTileMixturesMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
args.Handled = true;
|
||||
args.Mixtures ??= new GasMixture?[args.Tiles.Count];
|
||||
|
||||
for (var i = 0; i < args.Tiles.Count; i++)
|
||||
{
|
||||
args.Mixtures[i] ??= component.Mixture;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMapGetState(EntityUid uid, MapAtmosphereComponent component, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new MapAtmosphereComponentState(component.Overlay);
|
||||
|
||||
@@ -42,6 +42,7 @@ public sealed partial class AtmosphereSystem : SharedAtmosphereSystem
|
||||
private float _exposedTimer = 0f;
|
||||
|
||||
private EntityQuery<GridAtmosphereComponent> _atmosQuery;
|
||||
private EntityQuery<MapAtmosphereComponent> _mapAtmosQuery;
|
||||
private EntityQuery<AirtightComponent> _airtightQuery;
|
||||
private EntityQuery<FirelockComponent> _firelockQuery;
|
||||
private HashSet<EntityUid> _entSet = new();
|
||||
@@ -59,6 +60,7 @@ public sealed partial class AtmosphereSystem : SharedAtmosphereSystem
|
||||
InitializeGridAtmosphere();
|
||||
InitializeMap();
|
||||
|
||||
_mapAtmosQuery = GetEntityQuery<MapAtmosphereComponent>();
|
||||
_atmosQuery = GetEntityQuery<GridAtmosphereComponent>();
|
||||
_airtightQuery = GetEntityQuery<AirtightComponent>();
|
||||
_firelockQuery = GetEntityQuery<FirelockComponent>();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Numerics;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Body.Systems;
|
||||
@@ -17,8 +16,6 @@ using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems
|
||||
@@ -33,7 +30,6 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
[Dependency] private readonly SharedContainerSystem _containers = default!;
|
||||
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly ThrowingSystem _throwing = default!;
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
public sealed class GasVolumePumpSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
|
||||
[Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!;
|
||||
|
||||
@@ -28,7 +28,7 @@ public sealed partial class AtmosDeviceComponent : Component
|
||||
/// <summary>
|
||||
/// If non-null, the grid that this device is part of.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
[ViewVariables]
|
||||
public EntityUid? JoinedGrid = null;
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Atmos.Piping.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
{
|
||||
@@ -32,6 +34,14 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
|
||||
public void JoinAtmosphere(Entity<AtmosDeviceComponent> ent)
|
||||
{
|
||||
if (ent.Comp.JoinedGrid != null)
|
||||
{
|
||||
DebugTools.Assert(HasComp<GridAtmosphereComponent>(ent.Comp.JoinedGrid));
|
||||
DebugTools.Assert(Transform(ent).GridUid == ent.Comp.JoinedGrid);
|
||||
DebugTools.Assert(ent.Comp.RequireAnchored == Transform(ent).Anchored);
|
||||
return;
|
||||
}
|
||||
|
||||
var component = ent.Comp;
|
||||
var transform = Transform(ent);
|
||||
|
||||
@@ -39,7 +49,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
return;
|
||||
|
||||
// Attempt to add device to a grid atmosphere.
|
||||
bool onGrid = (transform.GridUid != null) && _atmosphereSystem.AddAtmosDevice(transform.GridUid!.Value, component);
|
||||
bool onGrid = (transform.GridUid != null) && _atmosphereSystem.AddAtmosDevice(transform.GridUid!.Value, ent);
|
||||
|
||||
if (!onGrid && component.JoinSystem)
|
||||
{
|
||||
@@ -55,7 +65,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
{
|
||||
var component = ent.Comp;
|
||||
// Try to remove the component from an atmosphere, and if not
|
||||
if (component.JoinedGrid != null && !_atmosphereSystem.RemoveAtmosDevice(component.JoinedGrid.Value, component))
|
||||
if (component.JoinedGrid != null && !_atmosphereSystem.RemoveAtmosDevice(component.JoinedGrid.Value, ent))
|
||||
{
|
||||
// The grid might have been removed but not us... This usually shouldn't happen.
|
||||
component.JoinedGrid = null;
|
||||
|
||||
@@ -13,7 +13,6 @@ using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Piping.Binary.Components;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Lock;
|
||||
using Robust.Server.GameObjects;
|
||||
@@ -29,8 +28,6 @@ public sealed class GasCanisterSystem : EntitySystem
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
|
||||
|
||||
@@ -77,7 +77,8 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
||||
return;
|
||||
|
||||
// Scrub adjacent tiles too.
|
||||
foreach (var adjacent in _atmosphereSystem.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true))
|
||||
var enumerator = _atmosphereSystem.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true);
|
||||
while (enumerator.MoveNext(out var adjacent))
|
||||
{
|
||||
Scrub(timeDelta, scrubber, adjacent, outlet);
|
||||
}
|
||||
|
||||
@@ -86,7 +86,8 @@ namespace Content.Server.Atmos.Portable
|
||||
if (!running)
|
||||
return;
|
||||
// widenet
|
||||
foreach (var adjacent in _atmosphereSystem.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true))
|
||||
var enumerator = _atmosphereSystem.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true);
|
||||
while (enumerator.MoveNext(out var adjacent))
|
||||
{
|
||||
Scrub(timeDelta, component, adjacent);
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace Content.Server.Atmos.Reactions
|
||||
var mixTemperature = mixture.Temperature;
|
||||
if (mixTemperature > Atmospherics.FireMinimumTemperatureToExist)
|
||||
{
|
||||
atmosphereSystem.HotspotExpose(location.GridIndex, location.GridIndices, mixTemperature, mixture.Volume);
|
||||
atmosphereSystem.HotspotExpose(location, mixTemperature, mixture.Volume);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Content.Server.Atmos.Reactions
|
||||
temperature = mixture.Temperature;
|
||||
if (temperature > Atmospherics.FireMinimumTemperatureToExist)
|
||||
{
|
||||
atmosphereSystem.HotspotExpose(location.GridIndex, location.GridIndices, temperature, mixture.Volume);
|
||||
atmosphereSystem.HotspotExpose(location, temperature, mixture.Volume);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
29
Content.Server/Atmos/TileMixtureEnumerator.cs
Normal file
29
Content.Server/Atmos/TileMixtureEnumerator.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Content.Server.Atmos;
|
||||
|
||||
public struct TileMixtureEnumerator
|
||||
{
|
||||
public readonly TileAtmosphere?[] Tiles;
|
||||
public int Index = 0;
|
||||
|
||||
public static readonly TileMixtureEnumerator Empty = new(Array.Empty<TileAtmosphere>());
|
||||
|
||||
internal TileMixtureEnumerator(TileAtmosphere?[] tiles)
|
||||
{
|
||||
Tiles = tiles;
|
||||
}
|
||||
|
||||
public bool MoveNext([NotNullWhen(true)] out GasMixture? mix)
|
||||
{
|
||||
while (Index < Tiles.Length)
|
||||
{
|
||||
mix = Tiles[Index++]?.Air;
|
||||
if (mix != null)
|
||||
return true;
|
||||
}
|
||||
|
||||
mix = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -93,9 +93,8 @@ namespace Content.Server.Bed
|
||||
if (!this.IsPowered(uid, EntityManager))
|
||||
return;
|
||||
|
||||
var metabolicEvent = new ApplyMetabolicMultiplierEvent
|
||||
{Uid = args.BuckledEntity, Multiplier = component.Multiplier, Apply = args.Buckling};
|
||||
RaiseLocalEvent(args.BuckledEntity, metabolicEvent);
|
||||
var metabolicEvent = new ApplyMetabolicMultiplierEvent(args.BuckledEntity, component.Multiplier, args.Buckling);
|
||||
RaiseLocalEvent(args.BuckledEntity, ref metabolicEvent);
|
||||
}
|
||||
|
||||
private void OnPowerChanged(EntityUid uid, StasisBedComponent component, ref PowerChangedEvent args)
|
||||
@@ -121,9 +120,8 @@ namespace Content.Server.Bed
|
||||
|
||||
foreach (var buckledEntity in strap.BuckledEntities)
|
||||
{
|
||||
var metabolicEvent = new ApplyMetabolicMultiplierEvent
|
||||
{Uid = buckledEntity, Multiplier = component.Multiplier, Apply = shouldApply};
|
||||
RaiseLocalEvent(buckledEntity, metabolicEvent);
|
||||
var metabolicEvent = new ApplyMetabolicMultiplierEvent(buckledEntity, component.Multiplier, shouldApply);
|
||||
RaiseLocalEvent(buckledEntity, ref metabolicEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Sound.Components;
|
||||
using Content.Server.Sound;
|
||||
using Content.Shared.Sound.Components;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Bed.Sleep;
|
||||
@@ -30,6 +31,7 @@ namespace Content.Server.Bed.Sleep
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!;
|
||||
[Dependency] private readonly EmitSoundSystem _emitSound = default!;
|
||||
|
||||
[ValidatePrototypeId<EntityPrototype>] public const string SleepActionId = "ActionSleep";
|
||||
|
||||
@@ -71,8 +73,8 @@ namespace Content.Server.Bed.Sleep
|
||||
{
|
||||
emitSound.Sound = sleepSound.Snore;
|
||||
}
|
||||
emitSound.PlayChance = sleepSound.Chance;
|
||||
emitSound.RollInterval = sleepSound.Interval;
|
||||
emitSound.MinInterval = sleepSound.Interval;
|
||||
emitSound.MaxInterval = sleepSound.MaxInterval;
|
||||
emitSound.PopUp = sleepSound.PopUp;
|
||||
}
|
||||
|
||||
@@ -128,7 +130,7 @@ namespace Content.Server.Bed.Sleep
|
||||
return;
|
||||
}
|
||||
if (TryComp<SpamEmitSoundComponent>(uid, out var spam))
|
||||
spam.Enabled = args.NewMobState == MobState.Alive;
|
||||
_emitSound.SetEnabled((uid, spam), args.NewMobState == MobState.Alive);
|
||||
}
|
||||
|
||||
private void AddWakeVerb(EntityUid uid, SleepingComponent component, GetVerbsEvent<AlternativeVerb> args)
|
||||
|
||||
@@ -34,7 +34,6 @@ namespace Content.Server.Body.Commands
|
||||
switch (args.Length)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
if (player == null)
|
||||
{
|
||||
shell.WriteLine("Only a player can run this command without arguments.");
|
||||
@@ -50,71 +49,68 @@ namespace Content.Server.Body.Commands
|
||||
entity = player.AttachedEntity.Value;
|
||||
hand = _entManager.SpawnEntity(DefaultHandPrototype, _entManager.GetComponent<TransformComponent>(entity).Coordinates);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
if (NetEntity.TryParse(args[0], out var uidNet) && _entManager.TryGetEntity(uidNet, out var uid))
|
||||
{
|
||||
if (NetEntity.TryParse(args[0], out var uidNet) && _entManager.TryGetEntity(uidNet, out var uid))
|
||||
{
|
||||
if (!_entManager.EntityExists(uid))
|
||||
{
|
||||
shell.WriteLine($"No entity found with uid {uid}");
|
||||
return;
|
||||
}
|
||||
|
||||
entity = uid.Value;
|
||||
hand = _entManager.SpawnEntity(DefaultHandPrototype, _entManager.GetComponent<TransformComponent>(entity).Coordinates);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (player == null)
|
||||
{
|
||||
shell.WriteLine("You must specify an entity to add a hand to when using this command from the server terminal.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.AttachedEntity == null)
|
||||
{
|
||||
shell.WriteLine("You don't have an entity to add a hand to.");
|
||||
return;
|
||||
}
|
||||
|
||||
entity = player.AttachedEntity.Value;
|
||||
hand = _entManager.SpawnEntity(args[0], _entManager.GetComponent<TransformComponent>(entity).Coordinates);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
if (!NetEntity.TryParse(args[0], out var netEnt) || !_entManager.TryGetEntity(netEnt, out var uid))
|
||||
{
|
||||
shell.WriteLine($"{args[0]} is not a valid entity uid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entManager.EntityExists(uid))
|
||||
{
|
||||
shell.WriteLine($"No entity found with uid {uid}");
|
||||
shell.WriteLine($"No entity exists with uid {uid}.");
|
||||
return;
|
||||
}
|
||||
|
||||
entity = uid.Value;
|
||||
hand = _entManager.SpawnEntity(DefaultHandPrototype, _entManager.GetComponent<TransformComponent>(entity).Coordinates);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (player == null)
|
||||
|
||||
if (!_protoManager.HasIndex<EntityPrototype>(args[1]))
|
||||
{
|
||||
shell.WriteLine("You must specify an entity to add a hand to when using this command from the server terminal.");
|
||||
shell.WriteLine($"No hand entity exists with id {args[1]}.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.AttachedEntity == null)
|
||||
{
|
||||
shell.WriteLine("You don't have an entity to add a hand to.");
|
||||
return;
|
||||
}
|
||||
hand = _entManager.SpawnEntity(args[1], _entManager.GetComponent<TransformComponent>(entity).Coordinates);
|
||||
|
||||
entity = player.AttachedEntity.Value;
|
||||
hand = _entManager.SpawnEntity(args[0], _entManager.GetComponent<TransformComponent>(entity).Coordinates);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
if (!NetEntity.TryParse(args[0], out var netEnt) || !_entManager.TryGetEntity(netEnt, out var uid))
|
||||
{
|
||||
shell.WriteLine($"{args[0]} is not a valid entity uid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_entManager.EntityExists(uid))
|
||||
{
|
||||
shell.WriteLine($"No entity exists with uid {uid}.");
|
||||
return;
|
||||
}
|
||||
|
||||
entity = uid.Value;
|
||||
|
||||
if (!_protoManager.HasIndex<EntityPrototype>(args[1]))
|
||||
{
|
||||
shell.WriteLine($"No hand entity exists with id {args[1]}.");
|
||||
return;
|
||||
}
|
||||
|
||||
hand = _entManager.SpawnEntity(args[1], _entManager.GetComponent<TransformComponent>(entity).Coordinates);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_entManager.TryGetComponent(entity, out BodyComponent? body) || body.RootContainer.ContainedEntity == null)
|
||||
@@ -139,7 +135,7 @@ namespace Content.Server.Body.Commands
|
||||
|
||||
var slotId = part.GetHashCode().ToString();
|
||||
|
||||
if (!bodySystem.TryCreatePartSlotAndAttach(attachAt.Id, slotId, hand, BodyPartType.Hand,attachAt.Component, part))
|
||||
if (!bodySystem.TryCreatePartSlotAndAttach(attachAt.Id, slotId, hand, BodyPartType.Hand, attachAt.Component, part))
|
||||
{
|
||||
shell.WriteError($"Couldn't create a slot with id {slotId} on entity {_entManager.ToPrettyString(entity)}");
|
||||
return;
|
||||
|
||||
@@ -103,11 +103,11 @@ namespace Content.Server.Body.Commands
|
||||
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||
if (body.RootContainer.ContainedEntity != null)
|
||||
{
|
||||
bodySystem.AttachPartToRoot(bodyId,partUid.Value, body ,part);
|
||||
bodySystem.AttachPartToRoot(bodyId, partUid.Value, body, part);
|
||||
}
|
||||
else
|
||||
{
|
||||
var (rootPartId,rootPart) = bodySystem.GetRootPartOrNull(bodyId, body)!.Value;
|
||||
var (rootPartId, rootPart) = bodySystem.GetRootPartOrNull(bodyId, body)!.Value;
|
||||
if (!bodySystem.TryCreatePartSlotAndAttach(rootPartId, slotId, partUid.Value, part.PartType, rootPart, part))
|
||||
{
|
||||
shell.WriteError($"Could not create slot {slotId} on entity {_entManager.ToPrettyString(bodyId)}");
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
namespace Content.Server.Body.Components;
|
||||
|
||||
public sealed class BeingGibbedEvent : EntityEventArgs
|
||||
{
|
||||
public readonly HashSet<EntityUid> GibbedParts;
|
||||
|
||||
public BeingGibbedEvent(HashSet<EntityUid> gibbedParts)
|
||||
{
|
||||
GibbedParts = gibbedParts;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Raised when a body gets gibbed, before it is deleted.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public readonly record struct BeingGibbedEvent(HashSet<EntityUid> GibbedParts);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.Body.Components
|
||||
{
|
||||
@@ -16,7 +18,17 @@ namespace Content.Server.Body.Components
|
||||
public static string DefaultBloodSolutionName = "bloodstream";
|
||||
public static string DefaultBloodTemporarySolutionName = "bloodstreamTemporary";
|
||||
|
||||
public float AccumulatedFrametime = 0.0f;
|
||||
/// <summary>
|
||||
/// The next time that blood level will be updated and bloodloss damage dealt.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan NextUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// The interval at which this component updates.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan UpdateInterval = TimeSpan.FromSeconds(3);
|
||||
|
||||
/// <summary>
|
||||
/// How much is this entity currently bleeding?
|
||||
@@ -32,7 +44,7 @@ namespace Content.Server.Body.Components
|
||||
public float BleedAmount;
|
||||
|
||||
/// <summary>
|
||||
/// How much should bleeding should be reduced every update interval?
|
||||
/// How much should bleeding be reduced every update interval?
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float BleedReductionAmount = 0.33f;
|
||||
@@ -63,18 +75,12 @@ namespace Content.Server.Body.Components
|
||||
[DataField(required: true)]
|
||||
public DamageSpecifier BloodlossHealDamage = new();
|
||||
|
||||
/// <summary>
|
||||
/// How frequently should this bloodstream update, in seconds?
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float UpdateInterval = 3.0f;
|
||||
|
||||
// TODO shouldn't be hardcoded, should just use some organ simulation like bone marrow or smth.
|
||||
/// <summary>
|
||||
/// How much reagent of blood should be restored each update interval?
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float BloodRefreshAmount = 1.0f;
|
||||
public FixedPoint2 BloodRefreshAmount = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// How much blood needs to be in the temporary solution in order to create a puddle?
|
||||
@@ -89,8 +95,8 @@ namespace Content.Server.Body.Components
|
||||
/// <remarks>
|
||||
/// For example, piercing damage is increased while poison damage is nullified entirely.
|
||||
/// </remarks>
|
||||
[DataField(customTypeSerializer:typeof(PrototypeIdSerializer<DamageModifierSetPrototype>))]
|
||||
public string DamageBleedModifiers = "BloodlossHuman";
|
||||
[DataField]
|
||||
public ProtoId<DamageModifierSetPrototype> DamageBleedModifiers = "BloodlossHuman";
|
||||
|
||||
/// <summary>
|
||||
/// The sound to be played when a weapon instantly deals blood loss damage.
|
||||
@@ -126,7 +132,7 @@ namespace Content.Server.Body.Components
|
||||
/// Slime-people might use slime as their blood or something like that.
|
||||
/// </remarks>
|
||||
[DataField]
|
||||
public string BloodReagent = "Blood";
|
||||
public ProtoId<ReagentPrototype> BloodReagent = "Blood";
|
||||
|
||||
/// <summary>Name/Key that <see cref="BloodSolution"/> is indexed by.</summary>
|
||||
[DataField]
|
||||
@@ -164,6 +170,6 @@ namespace Content.Server.Body.Components
|
||||
/// Variable that stores the amount of status time added by having a low blood level.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float StatusTime;
|
||||
public TimeSpan StatusTime;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Threading;
|
||||
namespace Content.Server.Body.Components
|
||||
{
|
||||
/// <summary>
|
||||
@@ -7,14 +6,17 @@ namespace Content.Server.Body.Components
|
||||
[RegisterComponent]
|
||||
public sealed partial class InternalsComponent : Component
|
||||
{
|
||||
[ViewVariables] public EntityUid? GasTankEntity { get; set; }
|
||||
[ViewVariables] public EntityUid? BreathToolEntity { get; set; }
|
||||
[ViewVariables]
|
||||
public EntityUid? GasTankEntity;
|
||||
|
||||
[ViewVariables]
|
||||
public EntityUid? BreathToolEntity;
|
||||
|
||||
/// <summary>
|
||||
/// Toggle Internals delay (seconds) when the target is not you.
|
||||
/// Toggle Internals delay when the target is not you.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("delay")]
|
||||
public float Delay = 3;
|
||||
[DataField]
|
||||
public TimeSpan Delay = TimeSpan.FromSeconds(3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Atmos;
|
||||
@@ -11,7 +11,7 @@ public sealed partial class LungComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
[Access(typeof(LungSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
|
||||
public GasMixture Air { get; set; } = new()
|
||||
public GasMixture Air = new()
|
||||
{
|
||||
Volume = 6,
|
||||
Temperature = Atmospherics.NormalBodyTemperature
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Shared.Body.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.Body.Components
|
||||
{
|
||||
@@ -12,20 +12,24 @@ namespace Content.Server.Body.Components
|
||||
[RegisterComponent, Access(typeof(MetabolizerSystem))]
|
||||
public sealed partial class MetabolizerComponent : Component
|
||||
{
|
||||
public float AccumulatedFrametime = 0.0f;
|
||||
/// <summary>
|
||||
/// The next time that reagents will be metabolized.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan NextUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// How often to metabolize reagents, in seconds.
|
||||
/// How often to metabolize reagents.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[DataField]
|
||||
public float UpdateFrequency = 1.0f;
|
||||
public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1);
|
||||
|
||||
/// <summary>
|
||||
/// From which solution will this metabolizer attempt to metabolize chemicals
|
||||
/// </summary>
|
||||
[DataField("solution")]
|
||||
public string SolutionName { get; set; } = BloodstreamComponent.DefaultChemicalsSolutionName;
|
||||
public string SolutionName = BloodstreamComponent.DefaultChemicalsSolutionName;
|
||||
|
||||
/// <summary>
|
||||
/// Does this component use a solution on it's parent entity (the body) or itself
|
||||
@@ -39,9 +43,9 @@ namespace Content.Server.Body.Components
|
||||
/// <summary>
|
||||
/// List of metabolizer types that this organ is. ex. Human, Slime, Felinid, w/e.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer:typeof(PrototypeIdHashSetSerializer<MetabolizerTypePrototype>))]
|
||||
[DataField]
|
||||
[Access(typeof(MetabolizerSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
|
||||
public HashSet<string>? MetabolizerTypes = null;
|
||||
public HashSet<ProtoId<MetabolizerTypePrototype>>? MetabolizerTypes = null;
|
||||
|
||||
/// <summary>
|
||||
/// Should this metabolizer remove chemicals that have no metabolisms defined?
|
||||
@@ -72,8 +76,8 @@ namespace Content.Server.Body.Components
|
||||
[DataDefinition]
|
||||
public sealed partial class MetabolismGroupEntry
|
||||
{
|
||||
[DataField(required: true, customTypeSerializer:typeof(PrototypeIdSerializer<MetabolismGroupPrototype>))]
|
||||
public string Id = default!;
|
||||
[DataField(required: true)]
|
||||
public ProtoId<MetabolismGroupPrototype> Id = default!;
|
||||
|
||||
[DataField("rateModifier")]
|
||||
public FixedPoint2 MetabolismRateModifier = 1.0;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Shared.Damage;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.Body.Components
|
||||
{
|
||||
@@ -7,36 +8,49 @@ namespace Content.Server.Body.Components
|
||||
public sealed partial class RespiratorComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Saturation level. Reduced by CycleDelay each tick.
|
||||
/// The next time that this body will inhale or exhale.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan NextUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// The interval between updates. Each update is either inhale or exhale,
|
||||
/// so a full cycle takes twice as long.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan UpdateInterval = TimeSpan.FromSeconds(2);
|
||||
|
||||
/// <summary>
|
||||
/// Saturation level. Reduced by UpdateInterval each tick.
|
||||
/// Can be thought of as 'how many seconds you have until you start suffocating' in this configuration.
|
||||
/// </summary>
|
||||
[DataField("saturation")]
|
||||
[DataField]
|
||||
public float Saturation = 5.0f;
|
||||
|
||||
/// <summary>
|
||||
/// At what level of saturation will you begin to suffocate?
|
||||
/// </summary>
|
||||
[DataField("suffocationThreshold")]
|
||||
[DataField]
|
||||
public float SuffocationThreshold;
|
||||
|
||||
[DataField("maxSaturation")]
|
||||
[DataField]
|
||||
public float MaxSaturation = 5.0f;
|
||||
|
||||
[DataField("minSaturation")]
|
||||
[DataField]
|
||||
public float MinSaturation = -2.0f;
|
||||
|
||||
// TODO HYPEROXIA?
|
||||
|
||||
[DataField("damage", required: true)]
|
||||
[DataField(required: true)]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageSpecifier Damage = default!;
|
||||
|
||||
[DataField("damageRecovery", required: true)]
|
||||
[DataField(required: true)]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageSpecifier DamageRecovery = default!;
|
||||
|
||||
[DataField("gaspPopupCooldown")]
|
||||
public TimeSpan GaspPopupCooldown { get; private set; } = TimeSpan.FromSeconds(8);
|
||||
[DataField]
|
||||
public TimeSpan GaspPopupCooldown = TimeSpan.FromSeconds(8);
|
||||
|
||||
[ViewVariables]
|
||||
public TimeSpan LastGaspPopupTime;
|
||||
@@ -55,11 +69,6 @@ namespace Content.Server.Body.Components
|
||||
|
||||
[ViewVariables]
|
||||
public RespiratorStatus Status = RespiratorStatus.Inhaling;
|
||||
|
||||
[DataField("cycleDelay")]
|
||||
public float CycleDelay = 2.0f;
|
||||
|
||||
public float AccumulatedFrametime;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Nutrition.EntitySystems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.Body.Components
|
||||
{
|
||||
[RegisterComponent, Access(typeof(StomachSystem), typeof(FoodSystem))]
|
||||
public sealed partial class StomachComponent : Component
|
||||
{
|
||||
public float AccumulatedFrameTime;
|
||||
/// <summary>
|
||||
/// The next time that the stomach will try to digest its contents.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan NextUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// How fast should this component update, in seconds?
|
||||
/// The interval at which this stomach digests its contents.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float UpdateInterval = 1.0f;
|
||||
public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1);
|
||||
|
||||
/// <summary>
|
||||
/// The solution inside of this stomach this transfers reagents to the body.
|
||||
@@ -30,11 +35,11 @@ namespace Content.Server.Body.Components
|
||||
public string BodySolutionName = BloodstreamComponent.DefaultChemicalsSolutionName;
|
||||
|
||||
/// <summary>
|
||||
/// Time in seconds between reagents being ingested and them being
|
||||
/// Time between reagents being ingested and them being
|
||||
/// transferred to <see cref="BloodstreamComponent"/>
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float DigestionDelay = 20;
|
||||
public TimeSpan DigestionDelay = TimeSpan.FromSeconds(20);
|
||||
|
||||
/// <summary>
|
||||
/// A whitelist for what special-digestible-required foods this stomach is capable of eating.
|
||||
@@ -54,15 +59,15 @@ namespace Content.Server.Body.Components
|
||||
public sealed class ReagentDelta
|
||||
{
|
||||
public readonly ReagentQuantity ReagentQuantity;
|
||||
public float Lifetime { get; private set; }
|
||||
public TimeSpan Lifetime { get; private set; }
|
||||
|
||||
public ReagentDelta(ReagentQuantity reagentQuantity)
|
||||
{
|
||||
ReagentQuantity = reagentQuantity;
|
||||
Lifetime = 0.0f;
|
||||
Lifetime = TimeSpan.Zero;
|
||||
}
|
||||
|
||||
public void Increment(float delta) => Lifetime += delta;
|
||||
public void Increment(TimeSpan delta) => Lifetime += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.Body.Systems;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.Body.Components;
|
||||
|
||||
@@ -6,48 +7,58 @@ namespace Content.Server.Body.Components;
|
||||
[Access(typeof(ThermalRegulatorSystem))]
|
||||
public sealed partial class ThermalRegulatorComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The next time that the body will regulate its heat.
|
||||
/// </summary>
|
||||
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan NextUpdate;
|
||||
|
||||
/// <summary>
|
||||
/// The interval at which thermal regulation is processed.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1);
|
||||
|
||||
/// <summary>
|
||||
/// Heat generated due to metabolism. It's generated via metabolism
|
||||
/// </summary>
|
||||
[DataField("metabolismHeat")]
|
||||
public float MetabolismHeat { get; private set; }
|
||||
[DataField]
|
||||
public float MetabolismHeat;
|
||||
|
||||
/// <summary>
|
||||
/// Heat output via radiation.
|
||||
/// </summary>
|
||||
[DataField("radiatedHeat")]
|
||||
public float RadiatedHeat { get; private set; }
|
||||
[DataField]
|
||||
public float RadiatedHeat;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum heat regulated via sweat
|
||||
/// </summary>
|
||||
[DataField("sweatHeatRegulation")]
|
||||
public float SweatHeatRegulation { get; private set; }
|
||||
[DataField]
|
||||
public float SweatHeatRegulation;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum heat regulated via shivering
|
||||
/// </summary>
|
||||
[DataField("shiveringHeatRegulation")]
|
||||
public float ShiveringHeatRegulation { get; private set; }
|
||||
[DataField]
|
||||
public float ShiveringHeatRegulation;
|
||||
|
||||
/// <summary>
|
||||
/// Amount of heat regulation that represents thermal regulation processes not
|
||||
/// explicitly coded.
|
||||
/// </summary>
|
||||
[DataField("implicitHeatRegulation")]
|
||||
public float ImplicitHeatRegulation { get; private set; }
|
||||
[DataField]
|
||||
public float ImplicitHeatRegulation;
|
||||
|
||||
/// <summary>
|
||||
/// Normal body temperature
|
||||
/// </summary>
|
||||
[DataField("normalBodyTemperature")]
|
||||
public float NormalBodyTemperature { get; private set; }
|
||||
[DataField]
|
||||
public float NormalBodyTemperature;
|
||||
|
||||
/// <summary>
|
||||
/// Deviation from normal temperature for body to start thermal regulation
|
||||
/// </summary>
|
||||
[DataField("thermalRegulationTemperatureThreshold")]
|
||||
public float ThermalRegulationTemperatureThreshold { get; private set; }
|
||||
|
||||
public float AccumulatedFrametime;
|
||||
[DataField]
|
||||
public float ThermalRegulationTemperatureThreshold;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.Drunk;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Rejuvenate;
|
||||
@@ -21,11 +20,13 @@ using Content.Shared.Speech.EntitySystems;
|
||||
using Robust.Server.Audio;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Body.Systems;
|
||||
|
||||
public sealed class BloodstreamSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
[Dependency] private readonly AudioSystem _audio = default!;
|
||||
@@ -44,6 +45,8 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BloodstreamComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<BloodstreamComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<BloodstreamComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<BloodstreamComponent, DamageChangedEvent>(OnDamageChanged);
|
||||
SubscribeLocalEvent<BloodstreamComponent, HealthBeingExaminedEvent>(OnHealthBeingExamined);
|
||||
SubscribeLocalEvent<BloodstreamComponent, BeingGibbedEvent>(OnBeingGibbed);
|
||||
@@ -53,6 +56,16 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
SubscribeLocalEvent<BloodstreamComponent, RejuvenateEvent>(OnRejuvenate);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<BloodstreamComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval;
|
||||
}
|
||||
|
||||
private void OnUnpaused(Entity<BloodstreamComponent> ent, ref EntityUnpausedEvent args)
|
||||
{
|
||||
ent.Comp.NextUpdate += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnReactionAttempt(Entity<BloodstreamComponent> entity, ref ReactionAttemptEvent args)
|
||||
{
|
||||
if (args.Cancelled)
|
||||
@@ -83,7 +96,9 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
if (args.Name != entity.Comp.BloodSolutionName
|
||||
&& args.Name != entity.Comp.ChemicalSolutionName
|
||||
&& args.Name != entity.Comp.BloodTemporarySolutionName)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OnReactionAttempt(entity, ref args.Event);
|
||||
}
|
||||
@@ -95,12 +110,10 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
var query = EntityQueryEnumerator<BloodstreamComponent>();
|
||||
while (query.MoveNext(out var uid, out var bloodstream))
|
||||
{
|
||||
bloodstream.AccumulatedFrametime += frameTime;
|
||||
|
||||
if (bloodstream.AccumulatedFrametime < bloodstream.UpdateInterval)
|
||||
if (_gameTiming.CurTime < bloodstream.NextUpdate)
|
||||
continue;
|
||||
|
||||
bloodstream.AccumulatedFrametime -= bloodstream.UpdateInterval;
|
||||
bloodstream.NextUpdate += bloodstream.UpdateInterval;
|
||||
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution))
|
||||
continue;
|
||||
@@ -128,13 +141,17 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
// bloodloss damage is based on the base value, and modified by how low your blood level is.
|
||||
var amt = bloodstream.BloodlossDamage / (0.1f + bloodPercentage);
|
||||
|
||||
_damageableSystem.TryChangeDamage(uid, amt, false, false);
|
||||
_damageableSystem.TryChangeDamage(uid, amt,
|
||||
ignoreResistances: false, interruptsDoAfters: false);
|
||||
|
||||
// Apply dizziness as a symptom of bloodloss.
|
||||
// The effect is applied in a way that it will never be cleared without being healthy.
|
||||
// Multiplying by 2 is arbitrary but works for this case, it just prevents the time from running out
|
||||
_drunkSystem.TryApplyDrunkenness(uid, bloodstream.UpdateInterval*2, false);
|
||||
_stutteringSystem.DoStutter(uid, TimeSpan.FromSeconds(bloodstream.UpdateInterval*2), false);
|
||||
_drunkSystem.TryApplyDrunkenness(
|
||||
uid,
|
||||
(float) bloodstream.UpdateInterval.TotalSeconds * 2,
|
||||
applySlur: false);
|
||||
_stutteringSystem.DoStutter(uid, bloodstream.UpdateInterval * 2, refresh: false);
|
||||
|
||||
// storing the drunk and stutter time so we can remove it independently from other effects additions
|
||||
bloodstream.StatusTime += bloodstream.UpdateInterval * 2;
|
||||
@@ -142,13 +159,16 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
else if (!_mobStateSystem.IsDead(uid))
|
||||
{
|
||||
// If they're healthy, we'll try and heal some bloodloss instead.
|
||||
_damageableSystem.TryChangeDamage(uid, bloodstream.BloodlossHealDamage * bloodPercentage, true, false);
|
||||
_damageableSystem.TryChangeDamage(
|
||||
uid,
|
||||
bloodstream.BloodlossHealDamage * bloodPercentage,
|
||||
ignoreResistances: true, interruptsDoAfters: false);
|
||||
|
||||
// Remove the drunk effect when healthy. Should only remove the amount of drunk and stutter added by low blood level
|
||||
_drunkSystem.TryRemoveDrunkenessTime(uid, bloodstream.StatusTime);
|
||||
_stutteringSystem.DoRemoveStutterTime(uid, bloodstream.StatusTime);
|
||||
_drunkSystem.TryRemoveDrunkenessTime(uid, bloodstream.StatusTime.TotalSeconds);
|
||||
_stutteringSystem.DoRemoveStutterTime(uid, bloodstream.StatusTime.TotalSeconds);
|
||||
// Reset the drunk and stutter time to zero
|
||||
bloodstream.StatusTime = 0;
|
||||
bloodstream.StatusTime = TimeSpan.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,17 +187,15 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
bloodSolution.AddReagent(entity.Comp.BloodReagent, entity.Comp.BloodMaxVolume - bloodSolution.Volume);
|
||||
}
|
||||
|
||||
private void OnDamageChanged(EntityUid uid, BloodstreamComponent component, DamageChangedEvent args)
|
||||
private void OnDamageChanged(Entity<BloodstreamComponent> ent, ref DamageChangedEvent args)
|
||||
{
|
||||
if (args.DamageDelta is null)
|
||||
return;
|
||||
|
||||
// definitely don't make them bleed if they got healed
|
||||
if (!args.DamageIncreased)
|
||||
if (args.DamageDelta is null || !args.DamageIncreased)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO probably cache this or something. humans get hurt a lot
|
||||
if (!_prototypeManager.TryIndex<DamageModifierSetPrototype>(component.DamageBleedModifiers, out var modifiers))
|
||||
if (!_prototypeManager.TryIndex<DamageModifierSetPrototype>(ent.Comp.DamageBleedModifiers, out var modifiers))
|
||||
return;
|
||||
|
||||
var bloodloss = DamageSpecifier.ApplyModifierSet(args.DamageDelta, modifiers);
|
||||
@@ -186,10 +204,10 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
return;
|
||||
|
||||
// Does the calculation of how much bleed rate should be added/removed, then applies it
|
||||
var oldBleedAmount = component.BleedAmount;
|
||||
var oldBleedAmount = ent.Comp.BleedAmount;
|
||||
var total = bloodloss.GetTotal();
|
||||
var totalFloat = total.Float();
|
||||
TryModifyBleedAmount(uid, totalFloat, component);
|
||||
TryModifyBleedAmount(ent, totalFloat, ent);
|
||||
|
||||
/// <summary>
|
||||
/// Critical hit. Causes target to lose blood, using the bleed rate modifier of the weapon, currently divided by 5
|
||||
@@ -199,8 +217,8 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
var prob = Math.Clamp(totalFloat / 25, 0, 1);
|
||||
if (totalFloat > 0 && _robustRandom.Prob(prob))
|
||||
{
|
||||
TryModifyBloodLevel(uid, (-total) / 5, component);
|
||||
_audio.PlayPvs(component.InstantBloodSound, uid);
|
||||
TryModifyBloodLevel(ent, (-total) / 5, ent);
|
||||
_audio.PlayPvs(ent.Comp.InstantBloodSound, ent);
|
||||
}
|
||||
|
||||
// Heat damage will cauterize, causing the bleed rate to be reduced.
|
||||
@@ -210,53 +228,52 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
// because it's burn damage that cauterized their wounds.
|
||||
|
||||
// We'll play a special sound and popup for feedback.
|
||||
_audio.PlayPvs(component.BloodHealedSound, uid);
|
||||
_popupSystem.PopupEntity(Loc.GetString("bloodstream-component-wounds-cauterized"), uid,
|
||||
uid, PopupType.Medium);
|
||||
_audio.PlayPvs(ent.Comp.BloodHealedSound, ent);
|
||||
_popupSystem.PopupEntity(Loc.GetString("bloodstream-component-wounds-cauterized"), ent,
|
||||
ent, PopupType.Medium);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Shows text on health examine, based on bleed rate and blood level.
|
||||
/// </summary>
|
||||
private void OnHealthBeingExamined(EntityUid uid, BloodstreamComponent component, HealthBeingExaminedEvent args)
|
||||
private void OnHealthBeingExamined(Entity<BloodstreamComponent> ent, ref HealthBeingExaminedEvent args)
|
||||
{
|
||||
// Shows profusely bleeding at half the max bleed rate.
|
||||
if (component.BleedAmount > component.MaxBleedAmount / 2)
|
||||
if (ent.Comp.BleedAmount > ent.Comp.MaxBleedAmount / 2)
|
||||
{
|
||||
args.Message.PushNewline();
|
||||
args.Message.AddMarkup(Loc.GetString("bloodstream-component-profusely-bleeding", ("target", Identity.Entity(uid, EntityManager))));
|
||||
args.Message.AddMarkup(Loc.GetString("bloodstream-component-profusely-bleeding", ("target", ent.Owner)));
|
||||
}
|
||||
// Shows bleeding message when bleeding, but less than profusely.
|
||||
else if (component.BleedAmount > 0)
|
||||
else if (ent.Comp.BleedAmount > 0)
|
||||
{
|
||||
args.Message.PushNewline();
|
||||
args.Message.AddMarkup(Loc.GetString("bloodstream-component-bleeding", ("target", Identity.Entity(uid, EntityManager))));
|
||||
args.Message.AddMarkup(Loc.GetString("bloodstream-component-bleeding", ("target", ent.Owner)));
|
||||
}
|
||||
|
||||
// If the mob's blood level is below the damage threshhold, the pale message is added.
|
||||
if (GetBloodLevelPercentage(uid, component) < component.BloodlossThreshold)
|
||||
if (GetBloodLevelPercentage(ent, ent) < ent.Comp.BloodlossThreshold)
|
||||
{
|
||||
args.Message.PushNewline();
|
||||
args.Message.AddMarkup(Loc.GetString("bloodstream-component-looks-pale", ("target", Identity.Entity(uid, EntityManager))));
|
||||
args.Message.AddMarkup(Loc.GetString("bloodstream-component-looks-pale", ("target", ent.Owner)));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBeingGibbed(EntityUid uid, BloodstreamComponent component, BeingGibbedEvent args)
|
||||
private void OnBeingGibbed(Entity<BloodstreamComponent> ent, ref BeingGibbedEvent args)
|
||||
{
|
||||
SpillAllSolutions(uid, component);
|
||||
SpillAllSolutions(ent, ent);
|
||||
}
|
||||
|
||||
private void OnApplyMetabolicMultiplier(EntityUid uid, BloodstreamComponent component, ApplyMetabolicMultiplierEvent args)
|
||||
private void OnApplyMetabolicMultiplier(
|
||||
Entity<BloodstreamComponent> ent,
|
||||
ref ApplyMetabolicMultiplierEvent args)
|
||||
{
|
||||
if (args.Apply)
|
||||
{
|
||||
component.UpdateInterval *= args.Multiplier;
|
||||
ent.Comp.UpdateInterval *= args.Multiplier;
|
||||
return;
|
||||
}
|
||||
component.UpdateInterval /= args.Multiplier;
|
||||
// Reset the accumulator properly
|
||||
if (component.AccumulatedFrametime >= component.UpdateInterval)
|
||||
component.AccumulatedFrametime = component.UpdateInterval;
|
||||
ent.Comp.UpdateInterval /= args.Multiplier;
|
||||
}
|
||||
|
||||
private void OnRejuvenate(Entity<BloodstreamComponent> entity, ref RejuvenateEvent args)
|
||||
@@ -275,21 +292,15 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
/// </summary>
|
||||
public bool TryAddToChemicals(EntityUid uid, Solution solution, BloodstreamComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return false;
|
||||
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution))
|
||||
return false;
|
||||
|
||||
return _solutionContainerSystem.TryAddSolution(component.ChemicalSolution.Value, solution);
|
||||
return Resolve(uid, ref component, logMissing: false)
|
||||
&& _solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution)
|
||||
&& _solutionContainerSystem.TryAddSolution(component.ChemicalSolution.Value, solution);
|
||||
}
|
||||
|
||||
public bool FlushChemicals(EntityUid uid, string excludedReagentID, FixedPoint2 quantity, BloodstreamComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return false;
|
||||
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution, out var chemSolution))
|
||||
if (!Resolve(uid, ref component, logMissing: false)
|
||||
|| !_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution, out var chemSolution))
|
||||
return false;
|
||||
|
||||
for (var i = chemSolution.Contents.Count - 1; i >= 0; i--)
|
||||
@@ -306,11 +317,11 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
|
||||
public float GetBloodLevelPercentage(EntityUid uid, BloodstreamComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return 0.0f;
|
||||
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution))
|
||||
if (!Resolve(uid, ref component)
|
||||
|| !_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return bloodSolution.FillFraction;
|
||||
}
|
||||
@@ -328,11 +339,11 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
/// </summary>
|
||||
public bool TryModifyBloodLevel(EntityUid uid, FixedPoint2 amount, BloodstreamComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return false;
|
||||
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution))
|
||||
if (!Resolve(uid, ref component, logMissing: false)
|
||||
|| !_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (amount >= 0)
|
||||
return _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, amount, out _);
|
||||
@@ -356,9 +367,9 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
tempSolution.AddSolution(temp, _prototypeManager);
|
||||
}
|
||||
|
||||
if (_puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, false))
|
||||
if (_puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, sound: false))
|
||||
{
|
||||
_forensicsSystem.TransferDna(puddleUid, uid, false);
|
||||
_forensicsSystem.TransferDna(puddleUid, uid, canDnaBeCleaned: false);
|
||||
}
|
||||
|
||||
tempSolution.RemoveAllSolution();
|
||||
@@ -374,7 +385,7 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
/// </summary>
|
||||
public bool TryModifyBleedAmount(EntityUid uid, float amount, BloodstreamComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
if (!Resolve(uid, ref component, logMissing: false))
|
||||
return false;
|
||||
|
||||
component.BleedAmount += amount;
|
||||
@@ -424,7 +435,7 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
|
||||
if (_puddleSystem.TrySpillAt(uid, tempSol, out var puddleUid))
|
||||
{
|
||||
_forensicsSystem.TransferDna(puddleUid, uid, false);
|
||||
_forensicsSystem.TransferDna(puddleUid, uid, canDnaBeCleaned: false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,11 +444,11 @@ public sealed class BloodstreamSystem : EntitySystem
|
||||
/// </summary>
|
||||
public void ChangeBloodReagent(EntityUid uid, string reagent, BloodstreamComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return;
|
||||
|
||||
if (reagent == component.BloodReagent)
|
||||
if (!Resolve(uid, ref component, logMissing: false)
|
||||
|| reagent == component.BloodReagent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution))
|
||||
{
|
||||
|
||||
@@ -1,23 +1,17 @@
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Humanoid;
|
||||
using Content.Server.Kitchen.Components;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Part;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Gibbing.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
|
||||
namespace Content.Server.Body.Systems;
|
||||
|
||||
@@ -27,10 +21,7 @@ public sealed class BodySystem : SharedBodySystem
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -40,7 +31,7 @@ public sealed class BodySystem : SharedBodySystem
|
||||
SubscribeLocalEvent<BodyComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
|
||||
}
|
||||
|
||||
private void OnRelayMoveInput(EntityUid uid, BodyComponent component, ref MoveInputEvent args)
|
||||
private void OnRelayMoveInput(Entity<BodyComponent> ent, ref MoveInputEvent args)
|
||||
{
|
||||
// If they haven't actually moved then ignore it.
|
||||
if ((args.Component.HeldMoveButtons &
|
||||
@@ -49,68 +40,67 @@ public sealed class BodySystem : SharedBodySystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (_mobState.IsDead(uid) && _mindSystem.TryGetMind(uid, out var mindId, out var mind))
|
||||
if (_mobState.IsDead(ent) && _mindSystem.TryGetMind(ent, out var mindId, out var mind))
|
||||
{
|
||||
mind.TimeOfDeath ??= _gameTiming.RealTime;
|
||||
_ticker.OnGhostAttempt(mindId, true, mind: mind);
|
||||
_ticker.OnGhostAttempt(mindId, canReturnGlobal: true, mind: mind);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplyMetabolicMultiplier(EntityUid uid, BodyComponent component,
|
||||
ApplyMetabolicMultiplierEvent args)
|
||||
private void OnApplyMetabolicMultiplier(
|
||||
Entity<BodyComponent> ent,
|
||||
ref ApplyMetabolicMultiplierEvent args)
|
||||
{
|
||||
foreach (var organ in GetBodyOrgans(uid, component))
|
||||
foreach (var organ in GetBodyOrgans(ent, ent))
|
||||
{
|
||||
RaiseLocalEvent(organ.Id, args);
|
||||
RaiseLocalEvent(organ.Id, ref args);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void AddPart(
|
||||
EntityUid bodyUid,
|
||||
EntityUid partUid,
|
||||
string slotId,
|
||||
BodyPartComponent component,
|
||||
BodyComponent? bodyComp = null)
|
||||
Entity<BodyComponent?> bodyEnt,
|
||||
Entity<BodyPartComponent> partEnt,
|
||||
string slotId)
|
||||
{
|
||||
// TODO: Predict this probably.
|
||||
base.AddPart(bodyUid, partUid, slotId, component, bodyComp);
|
||||
base.AddPart(bodyEnt, partEnt, slotId);
|
||||
|
||||
if (TryComp<HumanoidAppearanceComponent>(bodyUid, out var humanoid))
|
||||
if (TryComp<HumanoidAppearanceComponent>(bodyEnt, out var humanoid))
|
||||
{
|
||||
var layer = component.ToHumanoidLayers();
|
||||
var layer = partEnt.Comp.ToHumanoidLayers();
|
||||
if (layer != null)
|
||||
{
|
||||
var layers = HumanoidVisualLayersExtension.Sublayers(layer.Value);
|
||||
_humanoidSystem.SetLayersVisibility(bodyUid, layers, true, true, humanoid);
|
||||
_humanoidSystem.SetLayersVisibility(
|
||||
bodyEnt, layers, visible: true, permanent: true, humanoid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RemovePart(
|
||||
EntityUid bodyUid,
|
||||
EntityUid partUid,
|
||||
string slotId,
|
||||
BodyPartComponent component,
|
||||
BodyComponent? bodyComp = null)
|
||||
Entity<BodyComponent?> bodyEnt,
|
||||
Entity<BodyPartComponent> partEnt,
|
||||
string slotId)
|
||||
{
|
||||
base.RemovePart(bodyUid, partUid, slotId, component, bodyComp);
|
||||
base.RemovePart(bodyEnt, partEnt, slotId);
|
||||
|
||||
if (!TryComp<HumanoidAppearanceComponent>(bodyUid, out var humanoid))
|
||||
if (!TryComp<HumanoidAppearanceComponent>(bodyEnt, out var humanoid))
|
||||
return;
|
||||
|
||||
var layer = component.ToHumanoidLayers();
|
||||
var layer = partEnt.Comp.ToHumanoidLayers();
|
||||
|
||||
if (layer == null)
|
||||
if (layer is null)
|
||||
return;
|
||||
|
||||
var layers = HumanoidVisualLayersExtension.Sublayers(layer.Value);
|
||||
_humanoidSystem.SetLayersVisibility(bodyUid, layers, false, true, humanoid);
|
||||
_humanoidSystem.SetLayersVisibility(
|
||||
bodyEnt, layers, visible: false, permanent: true, humanoid);
|
||||
}
|
||||
|
||||
public override HashSet<EntityUid> GibBody(
|
||||
EntityUid bodyId,
|
||||
bool gibOrgans = false,
|
||||
BodyComponent? body = null ,
|
||||
BodyComponent? body = null,
|
||||
bool launchGibs = true,
|
||||
Vector2? splatDirection = null,
|
||||
float splatModifier = 1,
|
||||
@@ -118,19 +108,23 @@ public sealed class BodySystem : SharedBodySystem
|
||||
SoundSpecifier? gibSoundOverride = null
|
||||
)
|
||||
{
|
||||
if (!Resolve(bodyId, ref body, false))
|
||||
return new HashSet<EntityUid>();
|
||||
|
||||
if (TerminatingOrDeleted(bodyId) || EntityManager.IsQueuedForDeletion(bodyId))
|
||||
if (!Resolve(bodyId, ref body, logMissing: false)
|
||||
|| TerminatingOrDeleted(bodyId)
|
||||
|| EntityManager.IsQueuedForDeletion(bodyId))
|
||||
{
|
||||
return new HashSet<EntityUid>();
|
||||
}
|
||||
|
||||
var xform = Transform(bodyId);
|
||||
if (xform.MapUid == null)
|
||||
if (xform.MapUid is null)
|
||||
return new HashSet<EntityUid>();
|
||||
|
||||
var gibs = base.GibBody(bodyId, gibOrgans, body, launchGibs: launchGibs,
|
||||
splatDirection: splatDirection, splatModifier: splatModifier, splatCone:splatCone);
|
||||
RaiseLocalEvent(bodyId, new BeingGibbedEvent(gibs));
|
||||
|
||||
var ev = new BeingGibbedEvent(gibs);
|
||||
RaiseLocalEvent(bodyId, ref ev);
|
||||
|
||||
QueueDel(bodyId);
|
||||
|
||||
return gibs;
|
||||
|
||||
@@ -16,8 +16,8 @@ namespace Content.Server.Body.Systems
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<BrainComponent, AddedToPartInBodyEvent>((uid, _, args) => HandleMind(args.Body, uid));
|
||||
SubscribeLocalEvent<BrainComponent, RemovedFromPartInBodyEvent>((uid, _, args) => HandleMind(uid, args.OldBody));
|
||||
SubscribeLocalEvent<BrainComponent, OrganAddedToBodyEvent>((uid, _, args) => HandleMind(args.Body, uid));
|
||||
SubscribeLocalEvent<BrainComponent, OrganRemovedFromBodyEvent>((uid, _, args) => HandleMind(uid, args.OldBody));
|
||||
SubscribeLocalEvent<BrainComponent, PointAttemptEvent>(OnPointAttempt);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace Content.Server.Body.Systems
|
||||
_mindSystem.TransferTo(mindId, newEntity, mind: mind);
|
||||
}
|
||||
|
||||
private void OnPointAttempt(EntityUid uid, BrainComponent component, PointAttemptEvent args)
|
||||
private void OnPointAttempt(Entity<BrainComponent> ent, ref PointAttemptEvent args)
|
||||
{
|
||||
args.Cancel();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Hands.Systems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Atmos;
|
||||
@@ -11,19 +10,16 @@ using Content.Shared.Internals;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Body.Systems;
|
||||
|
||||
public sealed class InternalsSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
[Dependency] private readonly AlertsSystem _alerts = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmos = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly GasTankSystem _gasTank = default!;
|
||||
[Dependency] private readonly HandsSystem _hands = default!;
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
|
||||
@@ -40,28 +36,36 @@ public sealed class InternalsSystem : EntitySystem
|
||||
SubscribeLocalEvent<InternalsComponent, InternalsDoAfterEvent>(OnDoAfter);
|
||||
}
|
||||
|
||||
private void OnGetInteractionVerbs(EntityUid uid, InternalsComponent component, GetVerbsEvent<InteractionVerb> args)
|
||||
private void OnGetInteractionVerbs(
|
||||
Entity<InternalsComponent> ent,
|
||||
ref GetVerbsEvent<InteractionVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || args.Hands == null)
|
||||
if (!args.CanAccess || !args.CanInteract || args.Hands is null)
|
||||
return;
|
||||
|
||||
var user = args.User;
|
||||
|
||||
InteractionVerb verb = new()
|
||||
{
|
||||
Act = () =>
|
||||
{
|
||||
ToggleInternals(uid, args.User, false, component);
|
||||
ToggleInternals(ent, user, force: false, ent);
|
||||
},
|
||||
Message = Loc.GetString("action-description-internals-toggle"),
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/dot.svg.192dpi.png")),
|
||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/dot.svg.192dpi.png")),
|
||||
Text = Loc.GetString("action-name-internals-toggle"),
|
||||
};
|
||||
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
public void ToggleInternals(EntityUid uid, EntityUid user, bool force, InternalsComponent? internals = null)
|
||||
public void ToggleInternals(
|
||||
EntityUid uid,
|
||||
EntityUid user,
|
||||
bool force,
|
||||
InternalsComponent? internals = null)
|
||||
{
|
||||
if (!Resolve(uid, ref internals, false))
|
||||
if (!Resolve(uid, ref internals, logMissing: false))
|
||||
return;
|
||||
|
||||
// Toggle off if they're on
|
||||
@@ -73,12 +77,12 @@ public sealed class InternalsSystem : EntitySystem
|
||||
return;
|
||||
}
|
||||
|
||||
StartToggleInternalsDoAfter(user, uid, internals);
|
||||
StartToggleInternalsDoAfter(user, (uid, internals));
|
||||
return;
|
||||
}
|
||||
|
||||
// If they're not on then check if we have a mask to use
|
||||
if (internals.BreathToolEntity == null)
|
||||
if (internals.BreathToolEntity is null)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("internals-no-breath-tool"), uid, user);
|
||||
return;
|
||||
@@ -86,7 +90,7 @@ public sealed class InternalsSystem : EntitySystem
|
||||
|
||||
var tank = FindBestGasTank(uid);
|
||||
|
||||
if (tank == null)
|
||||
if (tank is null)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("internals-no-tank"), uid, user);
|
||||
return;
|
||||
@@ -94,20 +98,20 @@ public sealed class InternalsSystem : EntitySystem
|
||||
|
||||
if (!force)
|
||||
{
|
||||
StartToggleInternalsDoAfter(user, uid, internals);
|
||||
StartToggleInternalsDoAfter(user, (uid, internals));
|
||||
return;
|
||||
}
|
||||
|
||||
_gasTank.ConnectToInternals(tank.Value);
|
||||
}
|
||||
|
||||
private void StartToggleInternalsDoAfter(EntityUid user, EntityUid target, InternalsComponent internals)
|
||||
private void StartToggleInternalsDoAfter(EntityUid user, Entity<InternalsComponent> targetEnt)
|
||||
{
|
||||
// Is the target not you? If yes, use a do-after to give them time to respond.
|
||||
var isUser = user == target;
|
||||
var delay = !isUser ? internals.Delay : 0f;
|
||||
var isUser = user == targetEnt.Owner;
|
||||
var delay = !isUser ? targetEnt.Comp.Delay : TimeSpan.Zero;
|
||||
|
||||
_doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, delay, new InternalsDoAfterEvent(), target, target: target)
|
||||
_doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, delay, new InternalsDoAfterEvent(), targetEnt, target: targetEnt)
|
||||
{
|
||||
BreakOnDamage = true,
|
||||
BreakOnMove = true,
|
||||
@@ -115,66 +119,64 @@ public sealed class InternalsSystem : EntitySystem
|
||||
});
|
||||
}
|
||||
|
||||
private void OnDoAfter(EntityUid uid, InternalsComponent component, InternalsDoAfterEvent args)
|
||||
private void OnDoAfter(Entity<InternalsComponent> ent, ref InternalsDoAfterEvent args)
|
||||
{
|
||||
if (args.Cancelled || args.Handled)
|
||||
return;
|
||||
|
||||
ToggleInternals(uid, args.User, true, component);
|
||||
ToggleInternals(ent, args.User, force: true, ent);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnInternalsStartup(EntityUid uid, InternalsComponent component, ComponentStartup args)
|
||||
private void OnInternalsStartup(Entity<InternalsComponent> ent, ref ComponentStartup args)
|
||||
{
|
||||
_alerts.ShowAlert(uid, AlertType.Internals, GetSeverity(component));
|
||||
_alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent));
|
||||
}
|
||||
|
||||
private void OnInternalsShutdown(EntityUid uid, InternalsComponent component, ComponentShutdown args)
|
||||
private void OnInternalsShutdown(Entity<InternalsComponent> ent, ref ComponentShutdown args)
|
||||
{
|
||||
_alerts.ClearAlert(uid, AlertType.Internals);
|
||||
_alerts.ClearAlert(ent, AlertType.Internals);
|
||||
}
|
||||
|
||||
private void OnInhaleLocation(EntityUid uid, InternalsComponent component, InhaleLocationEvent args)
|
||||
private void OnInhaleLocation(Entity<InternalsComponent> ent, ref InhaleLocationEvent args)
|
||||
{
|
||||
if (AreInternalsWorking(component))
|
||||
if (AreInternalsWorking(ent))
|
||||
{
|
||||
var gasTank = Comp<GasTankComponent>(component.GasTankEntity!.Value);
|
||||
args.Gas = _gasTank.RemoveAirVolume((component.GasTankEntity.Value, gasTank), Atmospherics.BreathVolume);
|
||||
var gasTank = Comp<GasTankComponent>(ent.Comp.GasTankEntity!.Value);
|
||||
args.Gas = _gasTank.RemoveAirVolume((ent.Comp.GasTankEntity.Value, gasTank), Atmospherics.BreathVolume);
|
||||
// TODO: Should listen to gas tank updates instead I guess?
|
||||
_alerts.ShowAlert(uid, AlertType.Internals, GetSeverity(component));
|
||||
_alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent));
|
||||
}
|
||||
}
|
||||
public void DisconnectBreathTool(Entity<InternalsComponent> ent)
|
||||
{
|
||||
var (owner, component) = ent;
|
||||
var old = component.BreathToolEntity;
|
||||
component.BreathToolEntity = null;
|
||||
var old = ent.Comp.BreathToolEntity;
|
||||
ent.Comp.BreathToolEntity = null;
|
||||
|
||||
if (TryComp(old, out BreathToolComponent? breathTool) )
|
||||
if (TryComp(old, out BreathToolComponent? breathTool))
|
||||
{
|
||||
_atmos.DisconnectInternals(breathTool);
|
||||
DisconnectTank(ent);
|
||||
}
|
||||
|
||||
_alerts.ShowAlert(owner, AlertType.Internals, GetSeverity(component));
|
||||
_alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent));
|
||||
}
|
||||
|
||||
public void ConnectBreathTool(Entity<InternalsComponent> ent, EntityUid toolEntity)
|
||||
{
|
||||
var (owner, component) = ent;
|
||||
if (TryComp(component.BreathToolEntity, out BreathToolComponent? tool))
|
||||
if (TryComp(ent.Comp.BreathToolEntity, out BreathToolComponent? tool))
|
||||
{
|
||||
_atmos.DisconnectInternals(tool);
|
||||
}
|
||||
|
||||
component.BreathToolEntity = toolEntity;
|
||||
_alerts.ShowAlert(owner, AlertType.Internals, GetSeverity(component));
|
||||
ent.Comp.BreathToolEntity = toolEntity;
|
||||
_alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent));
|
||||
}
|
||||
|
||||
public void DisconnectTank(InternalsComponent? component)
|
||||
{
|
||||
if (component == null)
|
||||
if (component is null)
|
||||
return;
|
||||
|
||||
if (TryComp(component.GasTankEntity, out GasTankComponent? tank))
|
||||
@@ -186,46 +188,47 @@ public sealed class InternalsSystem : EntitySystem
|
||||
|
||||
public bool TryConnectTank(Entity<InternalsComponent> ent, EntityUid tankEntity)
|
||||
{
|
||||
var component = ent.Comp;
|
||||
if (component.BreathToolEntity == null)
|
||||
if (ent.Comp.BreathToolEntity is null)
|
||||
return false;
|
||||
|
||||
if (TryComp(component.GasTankEntity, out GasTankComponent? tank))
|
||||
_gasTank.DisconnectFromInternals((component.GasTankEntity.Value, tank));
|
||||
if (TryComp(ent.Comp.GasTankEntity, out GasTankComponent? tank))
|
||||
_gasTank.DisconnectFromInternals((ent.Comp.GasTankEntity.Value, tank));
|
||||
|
||||
component.GasTankEntity = tankEntity;
|
||||
_alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(component));
|
||||
ent.Comp.GasTankEntity = tankEntity;
|
||||
_alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent));
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool AreInternalsWorking(EntityUid uid, InternalsComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return false;
|
||||
|
||||
return AreInternalsWorking(component);
|
||||
return Resolve(uid, ref component, logMissing: false)
|
||||
&& AreInternalsWorking(component);
|
||||
}
|
||||
|
||||
public bool AreInternalsWorking(InternalsComponent component)
|
||||
{
|
||||
return TryComp(component.BreathToolEntity, out BreathToolComponent? breathTool) &&
|
||||
breathTool.IsFunctional &&
|
||||
TryComp(component.GasTankEntity, out GasTankComponent? _);
|
||||
return TryComp(component.BreathToolEntity, out BreathToolComponent? breathTool)
|
||||
&& breathTool.IsFunctional
|
||||
&& HasComp<GasTankComponent>(component.GasTankEntity);
|
||||
}
|
||||
|
||||
private short GetSeverity(InternalsComponent component)
|
||||
{
|
||||
if (component.BreathToolEntity == null || !AreInternalsWorking(component))
|
||||
if (component.BreathToolEntity is null || !AreInternalsWorking(component))
|
||||
return 2;
|
||||
|
||||
// If pressure in the tank is below low pressure threshhold, flash warning on internals UI
|
||||
if (TryComp<GasTankComponent>(component.GasTankEntity, out var gasTank) && gasTank.IsLowPressure)
|
||||
if (TryComp<GasTankComponent>(component.GasTankEntity, out var gasTank)
|
||||
&& gasTank.IsLowPressure)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public Entity<GasTankComponent>? FindBestGasTank(Entity<HandsComponent?, InventoryComponent?, ContainerManagerComponent?> user)
|
||||
public Entity<GasTankComponent>? FindBestGasTank(
|
||||
Entity<HandsComponent?, InventoryComponent?, ContainerManagerComponent?> user)
|
||||
{
|
||||
// Prioritise
|
||||
// 1. back equipped tanks
|
||||
@@ -233,17 +236,17 @@ public sealed class InternalsSystem : EntitySystem
|
||||
// 3. in-hand tanks
|
||||
// 4. pocket/belt tanks
|
||||
|
||||
if (!Resolve(user.Owner, ref user.Comp1, ref user.Comp2, ref user.Comp3))
|
||||
if (!Resolve(user, ref user.Comp1, ref user.Comp2, ref user.Comp3))
|
||||
return null;
|
||||
|
||||
if (_inventory.TryGetSlotEntity(user.Owner, "back", out var backEntity, user.Comp2, user.Comp3) &&
|
||||
if (_inventory.TryGetSlotEntity(user, "back", out var backEntity, user.Comp2, user.Comp3) &&
|
||||
TryComp<GasTankComponent>(backEntity, out var backGasTank) &&
|
||||
_gasTank.CanConnectToInternals(backGasTank))
|
||||
{
|
||||
return (backEntity.Value, backGasTank);
|
||||
}
|
||||
|
||||
if (_inventory.TryGetSlotEntity(user.Owner, "suitstorage", out var entity, user.Comp2, user.Comp3) &&
|
||||
if (_inventory.TryGetSlotEntity(user, "suitstorage", out var entity, user.Comp2, user.Comp3) &&
|
||||
TryComp<GasTankComponent>(entity, out var gasTank) &&
|
||||
_gasTank.CanConnectToInternals(gasTank))
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
@@ -26,21 +26,24 @@ public sealed class LungSystem : EntitySystem
|
||||
SubscribeLocalEvent<BreathToolComponent, ItemMaskToggledEvent>(OnMaskToggled);
|
||||
}
|
||||
|
||||
private void OnGotUnequipped(EntityUid uid, BreathToolComponent component, GotUnequippedEvent args)
|
||||
private void OnGotUnequipped(Entity<BreathToolComponent> ent, ref GotUnequippedEvent args)
|
||||
{
|
||||
_atmosphereSystem.DisconnectInternals(component);
|
||||
_atmosphereSystem.DisconnectInternals(ent);
|
||||
}
|
||||
|
||||
private void OnGotEquipped(EntityUid uid, BreathToolComponent component, GotEquippedEvent args)
|
||||
private void OnGotEquipped(Entity<BreathToolComponent> ent, ref GotEquippedEvent args)
|
||||
{
|
||||
if ((args.SlotFlags & ent.Comp.AllowedSlots) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ((args.SlotFlags & component.AllowedSlots) == 0) return;
|
||||
component.IsFunctional = true;
|
||||
ent.Comp.IsFunctional = true;
|
||||
|
||||
if (TryComp(args.Equipee, out InternalsComponent? internals))
|
||||
{
|
||||
component.ConnectedInternalsEntity = args.Equipee;
|
||||
_internals.ConnectBreathTool((args.Equipee, internals), uid);
|
||||
ent.Comp.ConnectedInternalsEntity = args.Equipee;
|
||||
_internals.ConnectBreathTool((args.Equipee, internals), ent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +84,7 @@ public sealed class LungSystem : EntitySystem
|
||||
if (moles <= 0)
|
||||
continue;
|
||||
var reagent = _atmosphereSystem.GasReagents[i];
|
||||
if (reagent == null) continue;
|
||||
if (reagent is null) continue;
|
||||
|
||||
var amount = moles * Atmospherics.BreathMolesToReagentMultiplier;
|
||||
solution.AddReagent(reagent, amount);
|
||||
|
||||
@@ -12,11 +12,13 @@ using Content.Shared.Mobs.Systems;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Body.Systems
|
||||
{
|
||||
public sealed class MetabolizerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
@@ -34,9 +36,21 @@ namespace Content.Server.Body.Systems
|
||||
_solutionQuery = GetEntityQuery<SolutionContainerManagerComponent>();
|
||||
|
||||
SubscribeLocalEvent<MetabolizerComponent, ComponentInit>(OnMetabolizerInit);
|
||||
SubscribeLocalEvent<MetabolizerComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<MetabolizerComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<MetabolizerComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<MetabolizerComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval;
|
||||
}
|
||||
|
||||
private void OnUnpaused(Entity<MetabolizerComponent> ent, ref EntityUnpausedEvent args)
|
||||
{
|
||||
ent.Comp.NextUpdate += args.PausedTime;
|
||||
}
|
||||
|
||||
private void OnMetabolizerInit(Entity<MetabolizerComponent> entity, ref ComponentInit args)
|
||||
{
|
||||
if (!entity.Comp.SolutionOnBody)
|
||||
@@ -49,19 +63,17 @@ namespace Content.Server.Body.Systems
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplyMetabolicMultiplier(EntityUid uid, MetabolizerComponent component,
|
||||
ApplyMetabolicMultiplierEvent args)
|
||||
private void OnApplyMetabolicMultiplier(
|
||||
Entity<MetabolizerComponent> ent,
|
||||
ref ApplyMetabolicMultiplierEvent args)
|
||||
{
|
||||
if (args.Apply)
|
||||
{
|
||||
component.UpdateFrequency *= args.Multiplier;
|
||||
ent.Comp.UpdateInterval *= args.Multiplier;
|
||||
return;
|
||||
}
|
||||
|
||||
component.UpdateFrequency /= args.Multiplier;
|
||||
// Reset the accumulator properly
|
||||
if (component.AccumulatedFrametime >= component.UpdateFrequency)
|
||||
component.AccumulatedFrametime = component.UpdateFrequency;
|
||||
ent.Comp.UpdateInterval /= args.Multiplier;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
@@ -78,50 +90,52 @@ namespace Content.Server.Body.Systems
|
||||
|
||||
foreach (var (uid, metab) in metabolizers)
|
||||
{
|
||||
metab.AccumulatedFrametime += frameTime;
|
||||
|
||||
// Only update as frequently as it should
|
||||
if (metab.AccumulatedFrametime < metab.UpdateFrequency)
|
||||
if (_gameTiming.CurTime < metab.NextUpdate)
|
||||
continue;
|
||||
|
||||
metab.AccumulatedFrametime -= metab.UpdateFrequency;
|
||||
TryMetabolize(uid, metab);
|
||||
metab.NextUpdate += metab.UpdateInterval;
|
||||
TryMetabolize((uid, metab));
|
||||
}
|
||||
}
|
||||
|
||||
private void TryMetabolize(EntityUid uid, MetabolizerComponent meta, OrganComponent? organ = null)
|
||||
private void TryMetabolize(Entity<MetabolizerComponent, OrganComponent?, SolutionContainerManagerComponent?> ent)
|
||||
{
|
||||
_organQuery.Resolve(uid, ref organ, false);
|
||||
_organQuery.Resolve(ent, ref ent.Comp2, logMissing: false);
|
||||
|
||||
// First step is get the solution we actually care about
|
||||
var solutionName = ent.Comp1.SolutionName;
|
||||
Solution? solution = null;
|
||||
Entity<SolutionComponent>? soln = default!;
|
||||
EntityUid? solutionEntityUid = null;
|
||||
|
||||
SolutionContainerManagerComponent? manager = null;
|
||||
|
||||
if (meta.SolutionOnBody)
|
||||
if (ent.Comp1.SolutionOnBody)
|
||||
{
|
||||
if (organ?.Body is { } body)
|
||||
if (ent.Comp2?.Body is { } body)
|
||||
{
|
||||
if (!_solutionQuery.Resolve(body, ref manager, false))
|
||||
if (!_solutionQuery.Resolve(body, ref ent.Comp3, logMissing: false))
|
||||
return;
|
||||
|
||||
_solutionContainerSystem.TryGetSolution((body, manager), meta.SolutionName, out soln, out solution);
|
||||
_solutionContainerSystem.TryGetSolution((body, ent.Comp3), solutionName, out soln, out solution);
|
||||
solutionEntityUid = body;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_solutionQuery.Resolve(uid, ref manager, false))
|
||||
if (!_solutionQuery.Resolve(ent, ref ent.Comp3, logMissing: false))
|
||||
return;
|
||||
|
||||
_solutionContainerSystem.TryGetSolution((uid, manager), meta.SolutionName, out soln, out solution);
|
||||
solutionEntityUid = uid;
|
||||
_solutionContainerSystem.TryGetSolution((ent, ent), solutionName, out soln, out solution);
|
||||
solutionEntityUid = ent;
|
||||
}
|
||||
|
||||
if (solutionEntityUid == null || soln is null || solution is null || solution.Contents.Count == 0)
|
||||
if (solutionEntityUid is null
|
||||
|| soln is null
|
||||
|| solution is null
|
||||
|| solution.Contents.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// randomize the reagent list so we don't have any weird quirks
|
||||
// like alphabetical order or insertion order mattering for processing
|
||||
@@ -135,9 +149,9 @@ namespace Content.Server.Body.Systems
|
||||
continue;
|
||||
|
||||
var mostToRemove = FixedPoint2.Zero;
|
||||
if (proto.Metabolisms == null)
|
||||
if (proto.Metabolisms is null)
|
||||
{
|
||||
if (meta.RemoveEmpty)
|
||||
if (ent.Comp1.RemoveEmpty)
|
||||
{
|
||||
solution.RemoveReagent(reagent, FixedPoint2.New(1));
|
||||
}
|
||||
@@ -146,15 +160,15 @@ namespace Content.Server.Body.Systems
|
||||
}
|
||||
|
||||
// we're done here entirely if this is true
|
||||
if (reagents >= meta.MaxReagentsProcessable)
|
||||
if (reagents >= ent.Comp1.MaxReagentsProcessable)
|
||||
return;
|
||||
|
||||
|
||||
// loop over all our groups and see which ones apply
|
||||
if (meta.MetabolismGroups == null)
|
||||
if (ent.Comp1.MetabolismGroups is null)
|
||||
continue;
|
||||
|
||||
foreach (var group in meta.MetabolismGroups)
|
||||
foreach (var group in ent.Comp1.MetabolismGroups)
|
||||
{
|
||||
if (!proto.Metabolisms.TryGetValue(group.Id, out var entry))
|
||||
continue;
|
||||
@@ -169,14 +183,14 @@ namespace Content.Server.Body.Systems
|
||||
// if it's possible for them to be dead, and they are,
|
||||
// then we shouldn't process any effects, but should probably
|
||||
// still remove reagents
|
||||
if (EntityManager.TryGetComponent<MobStateComponent>(solutionEntityUid.Value, out var state))
|
||||
if (TryComp<MobStateComponent>(solutionEntityUid.Value, out var state))
|
||||
{
|
||||
if (!proto.WorksOnTheDead && _mobStateSystem.IsDead(solutionEntityUid.Value, state))
|
||||
continue;
|
||||
}
|
||||
|
||||
var actualEntity = organ?.Body ?? solutionEntityUid.Value;
|
||||
var args = new ReagentEffectArgs(actualEntity, uid, solution, proto, mostToRemove,
|
||||
var actualEntity = ent.Comp2?.Body ?? solutionEntityUid.Value;
|
||||
var args = new ReagentEffectArgs(actualEntity, ent, solution, proto, mostToRemove,
|
||||
EntityManager, null, scale);
|
||||
|
||||
// do all effects, if conditions apply
|
||||
@@ -187,8 +201,14 @@ namespace Content.Server.Body.Systems
|
||||
|
||||
if (effect.ShouldLog)
|
||||
{
|
||||
_adminLogger.Add(LogType.ReagentEffect, effect.LogImpact,
|
||||
$"Metabolism effect {effect.GetType().Name:effect} of reagent {proto.LocalizedName:reagent} applied on entity {actualEntity:entity} at {Transform(actualEntity).Coordinates:coordinates}");
|
||||
_adminLogger.Add(
|
||||
LogType.ReagentEffect,
|
||||
effect.LogImpact,
|
||||
$"Metabolism effect {effect.GetType().Name:effect}"
|
||||
+ $" of reagent {proto.LocalizedName:reagent}"
|
||||
+ $" applied on entity {actualEntity:entity}"
|
||||
+ $" at {Transform(actualEntity).Coordinates:coordinates}"
|
||||
);
|
||||
}
|
||||
|
||||
effect.Effect(args);
|
||||
@@ -209,15 +229,25 @@ namespace Content.Server.Body.Systems
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ApplyMetabolicMultiplierEvent : EntityEventArgs
|
||||
[ByRefEvent]
|
||||
public readonly record struct ApplyMetabolicMultiplierEvent(
|
||||
EntityUid Uid,
|
||||
float Multiplier,
|
||||
bool Apply)
|
||||
{
|
||||
// The entity whose metabolism is being modified
|
||||
public EntityUid Uid;
|
||||
/// <summary>
|
||||
/// The entity whose metabolism is being modified.
|
||||
/// </summary>
|
||||
public readonly EntityUid Uid = Uid;
|
||||
|
||||
// What the metabolism's update rate will be multiplied by
|
||||
public float Multiplier;
|
||||
/// <summary>
|
||||
/// What the metabolism's update rate will be multiplied by.
|
||||
/// </summary>
|
||||
public readonly float Multiplier = Multiplier;
|
||||
|
||||
// Apply this multiplier or ignore / reset it?
|
||||
public bool Apply;
|
||||
/// <summary>
|
||||
/// If true, apply the multiplier. If false, revert it.
|
||||
/// </summary>
|
||||
public readonly bool Apply = Apply;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,9 +35,21 @@ public sealed class RespiratorSystem : EntitySystem
|
||||
|
||||
// We want to process lung reagents before we inhale new reagents.
|
||||
UpdatesAfter.Add(typeof(MetabolizerSystem));
|
||||
SubscribeLocalEvent<RespiratorComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<RespiratorComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<RespiratorComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<RespiratorComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval;
|
||||
}
|
||||
|
||||
private void OnUnpaused(Entity<RespiratorComponent> ent, ref EntityUnpausedEvent args)
|
||||
{
|
||||
ent.Comp.NextUpdate += args.PausedTime;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
@@ -45,17 +57,15 @@ public sealed class RespiratorSystem : EntitySystem
|
||||
var query = EntityQueryEnumerator<RespiratorComponent, BodyComponent>();
|
||||
while (query.MoveNext(out var uid, out var respirator, out var body))
|
||||
{
|
||||
if (_gameTiming.CurTime < respirator.NextUpdate)
|
||||
continue;
|
||||
|
||||
respirator.NextUpdate += respirator.UpdateInterval;
|
||||
|
||||
if (_mobState.IsDead(uid))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
respirator.AccumulatedFrametime += frameTime;
|
||||
|
||||
if (respirator.AccumulatedFrametime < respirator.CycleDelay)
|
||||
continue;
|
||||
respirator.AccumulatedFrametime -= respirator.CycleDelay;
|
||||
UpdateSaturation(uid, -respirator.CycleDelay, respirator);
|
||||
UpdateSaturation(uid, -(float) respirator.UpdateInterval.TotalSeconds, respirator);
|
||||
|
||||
if (!_mobState.IsIncapacitated(uid)) // cannot breathe in crit.
|
||||
{
|
||||
@@ -80,30 +90,30 @@ public sealed class RespiratorSystem : EntitySystem
|
||||
_popupSystem.PopupEntity(Loc.GetString("lung-behavior-gasp"), uid);
|
||||
}
|
||||
|
||||
TakeSuffocationDamage(uid, respirator);
|
||||
TakeSuffocationDamage((uid, respirator));
|
||||
respirator.SuffocationCycles += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
StopSuffocation(uid, respirator);
|
||||
StopSuffocation((uid, respirator));
|
||||
respirator.SuffocationCycles = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void Inhale(EntityUid uid, BodyComponent? body = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body, false))
|
||||
if (!Resolve(uid, ref body, logMissing: false))
|
||||
return;
|
||||
|
||||
var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid, body);
|
||||
|
||||
// Inhale gas
|
||||
var ev = new InhaleLocationEvent();
|
||||
RaiseLocalEvent(uid, ev);
|
||||
RaiseLocalEvent(uid, ref ev, broadcast: false);
|
||||
|
||||
ev.Gas ??= _atmosSys.GetContainingMixture(uid, false, true);
|
||||
ev.Gas ??= _atmosSys.GetContainingMixture(uid, excite: true);
|
||||
|
||||
if (ev.Gas == null)
|
||||
if (ev.Gas is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -122,7 +132,7 @@ public sealed class RespiratorSystem : EntitySystem
|
||||
|
||||
public void Exhale(EntityUid uid, BodyComponent? body = null)
|
||||
{
|
||||
if (!Resolve(uid, ref body, false))
|
||||
if (!Resolve(uid, ref body, logMissing: false))
|
||||
return;
|
||||
|
||||
var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid, body);
|
||||
@@ -130,11 +140,11 @@ public sealed class RespiratorSystem : EntitySystem
|
||||
// exhale gas
|
||||
|
||||
var ev = new ExhaleLocationEvent();
|
||||
RaiseLocalEvent(uid, ev, false);
|
||||
RaiseLocalEvent(uid, ref ev, broadcast: false);
|
||||
|
||||
if (ev.Gas == null)
|
||||
if (ev.Gas is null)
|
||||
{
|
||||
ev.Gas = _atmosSys.GetContainingMixture(uid, false, true);
|
||||
ev.Gas = _atmosSys.GetContainingMixture(uid, excite: true);
|
||||
|
||||
// Walls and grids without atmos comp return null. I guess it makes sense to not be able to exhale in walls,
|
||||
// but this also means you cannot exhale on some grids.
|
||||
@@ -154,37 +164,37 @@ public sealed class RespiratorSystem : EntitySystem
|
||||
_atmosSys.Merge(ev.Gas, outGas);
|
||||
}
|
||||
|
||||
private void TakeSuffocationDamage(EntityUid uid, RespiratorComponent respirator)
|
||||
private void TakeSuffocationDamage(Entity<RespiratorComponent> ent)
|
||||
{
|
||||
if (respirator.SuffocationCycles == 2)
|
||||
_adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(uid):entity} started suffocating");
|
||||
if (ent.Comp.SuffocationCycles == 2)
|
||||
_adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} started suffocating");
|
||||
|
||||
if (respirator.SuffocationCycles >= respirator.SuffocationCycleThreshold)
|
||||
if (ent.Comp.SuffocationCycles >= ent.Comp.SuffocationCycleThreshold)
|
||||
{
|
||||
// TODO: This is not going work with multiple different lungs, if that ever becomes a possibility
|
||||
var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid);
|
||||
var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(ent);
|
||||
foreach (var (comp, _) in organs)
|
||||
{
|
||||
_alertsSystem.ShowAlert(uid, comp.Alert);
|
||||
_alertsSystem.ShowAlert(ent, comp.Alert);
|
||||
}
|
||||
}
|
||||
|
||||
_damageableSys.TryChangeDamage(uid, respirator.Damage, false, false);
|
||||
_damageableSys.TryChangeDamage(ent, ent.Comp.Damage, interruptsDoAfters: false);
|
||||
}
|
||||
|
||||
private void StopSuffocation(EntityUid uid, RespiratorComponent respirator)
|
||||
private void StopSuffocation(Entity<RespiratorComponent> ent)
|
||||
{
|
||||
if (respirator.SuffocationCycles >= 2)
|
||||
_adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(uid):entity} stopped suffocating");
|
||||
if (ent.Comp.SuffocationCycles >= 2)
|
||||
_adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} stopped suffocating");
|
||||
|
||||
// TODO: This is not going work with multiple different lungs, if that ever becomes a possibility
|
||||
var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid);
|
||||
var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(ent);
|
||||
foreach (var (comp, _) in organs)
|
||||
{
|
||||
_alertsSystem.ClearAlert(uid, comp.Alert);
|
||||
_alertsSystem.ClearAlert(ent, comp.Alert);
|
||||
}
|
||||
|
||||
_damageableSys.TryChangeDamage(uid, respirator.DamageRecovery);
|
||||
_damageableSys.TryChangeDamage(ent, ent.Comp.DamageRecovery);
|
||||
}
|
||||
|
||||
public void UpdateSaturation(EntityUid uid, float amount,
|
||||
@@ -198,35 +208,29 @@ public sealed class RespiratorSystem : EntitySystem
|
||||
Math.Clamp(respirator.Saturation, respirator.MinSaturation, respirator.MaxSaturation);
|
||||
}
|
||||
|
||||
private void OnApplyMetabolicMultiplier(EntityUid uid, RespiratorComponent component,
|
||||
ApplyMetabolicMultiplierEvent args)
|
||||
private void OnApplyMetabolicMultiplier(
|
||||
Entity<RespiratorComponent> ent,
|
||||
ref ApplyMetabolicMultiplierEvent args)
|
||||
{
|
||||
if (args.Apply)
|
||||
{
|
||||
component.CycleDelay *= args.Multiplier;
|
||||
component.Saturation *= args.Multiplier;
|
||||
component.MaxSaturation *= args.Multiplier;
|
||||
component.MinSaturation *= args.Multiplier;
|
||||
ent.Comp.UpdateInterval *= args.Multiplier;
|
||||
ent.Comp.Saturation *= args.Multiplier;
|
||||
ent.Comp.MaxSaturation *= args.Multiplier;
|
||||
ent.Comp.MinSaturation *= args.Multiplier;
|
||||
return;
|
||||
}
|
||||
|
||||
// This way we don't have to worry about it breaking if the stasis bed component is destroyed
|
||||
component.CycleDelay /= args.Multiplier;
|
||||
component.Saturation /= args.Multiplier;
|
||||
component.MaxSaturation /= args.Multiplier;
|
||||
component.MinSaturation /= args.Multiplier;
|
||||
// Reset the accumulator properly
|
||||
if (component.AccumulatedFrametime >= component.CycleDelay)
|
||||
component.AccumulatedFrametime = component.CycleDelay;
|
||||
ent.Comp.UpdateInterval /= args.Multiplier;
|
||||
ent.Comp.Saturation /= args.Multiplier;
|
||||
ent.Comp.MaxSaturation /= args.Multiplier;
|
||||
ent.Comp.MinSaturation /= args.Multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class InhaleLocationEvent : EntityEventArgs
|
||||
{
|
||||
public GasMixture? Gas;
|
||||
}
|
||||
[ByRefEvent]
|
||||
public record struct InhaleLocationEvent(GasMixture? Gas);
|
||||
|
||||
public sealed class ExhaleLocationEvent : EntityEventArgs
|
||||
{
|
||||
public GasMixture? Gas;
|
||||
}
|
||||
[ByRefEvent]
|
||||
public record struct ExhaleLocationEvent(GasMixture? Gas);
|
||||
|
||||
@@ -3,32 +3,44 @@ using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Shared.Body.Organ;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Body.Systems
|
||||
{
|
||||
public sealed class StomachSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
|
||||
public const string DefaultSolutionName = "stomach";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<StomachComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<StomachComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
SubscribeLocalEvent<StomachComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<StomachComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval;
|
||||
}
|
||||
|
||||
private void OnUnpaused(Entity<StomachComponent> ent, ref EntityUnpausedEvent args)
|
||||
{
|
||||
ent.Comp.NextUpdate += args.PausedTime;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
var query = EntityQueryEnumerator<StomachComponent, OrganComponent, SolutionContainerManagerComponent>();
|
||||
while (query.MoveNext(out var uid, out var stomach, out var organ, out var sol))
|
||||
{
|
||||
stomach.AccumulatedFrameTime += frameTime;
|
||||
|
||||
if (stomach.AccumulatedFrameTime < stomach.UpdateInterval)
|
||||
if (_gameTiming.CurTime < stomach.NextUpdate)
|
||||
continue;
|
||||
|
||||
stomach.AccumulatedFrameTime -= stomach.UpdateInterval;
|
||||
stomach.NextUpdate += stomach.UpdateInterval;
|
||||
|
||||
// Get our solutions
|
||||
if (!_solutionContainerSystem.ResolveSolution((uid, sol), DefaultSolutionName, ref stomach.Solution, out var stomachSolution))
|
||||
@@ -70,49 +82,44 @@ namespace Content.Server.Body.Systems
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplyMetabolicMultiplier(EntityUid uid, StomachComponent component,
|
||||
ApplyMetabolicMultiplierEvent args)
|
||||
private void OnApplyMetabolicMultiplier(
|
||||
Entity<StomachComponent> ent,
|
||||
ref ApplyMetabolicMultiplierEvent args)
|
||||
{
|
||||
if (args.Apply)
|
||||
{
|
||||
component.UpdateInterval *= args.Multiplier;
|
||||
ent.Comp.UpdateInterval *= args.Multiplier;
|
||||
return;
|
||||
}
|
||||
|
||||
// This way we don't have to worry about it breaking if the stasis bed component is destroyed
|
||||
component.UpdateInterval /= args.Multiplier;
|
||||
// Reset the accumulator properly
|
||||
if (component.AccumulatedFrameTime >= component.UpdateInterval)
|
||||
component.AccumulatedFrameTime = component.UpdateInterval;
|
||||
ent.Comp.UpdateInterval /= args.Multiplier;
|
||||
}
|
||||
|
||||
public bool CanTransferSolution(EntityUid uid, Solution solution,
|
||||
public bool CanTransferSolution(
|
||||
EntityUid uid,
|
||||
Solution solution,
|
||||
StomachComponent? stomach = null,
|
||||
SolutionContainerManagerComponent? solutions = null)
|
||||
{
|
||||
if (!Resolve(uid, ref stomach, ref solutions, false))
|
||||
return false;
|
||||
|
||||
if (!_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution, out var stomachSolution))
|
||||
return false;
|
||||
|
||||
// TODO: For now no partial transfers. Potentially change by design
|
||||
if (!stomachSolution.CanAddSolution(solution))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return Resolve(uid, ref stomach, ref solutions, logMissing: false)
|
||||
&& _solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution, out var stomachSolution)
|
||||
// TODO: For now no partial transfers. Potentially change by design
|
||||
&& stomachSolution.CanAddSolution(solution);
|
||||
}
|
||||
|
||||
public bool TryTransferSolution(EntityUid uid, Solution solution,
|
||||
public bool TryTransferSolution(
|
||||
EntityUid uid,
|
||||
Solution solution,
|
||||
StomachComponent? stomach = null,
|
||||
SolutionContainerManagerComponent? solutions = null)
|
||||
{
|
||||
if (!Resolve(uid, ref stomach, ref solutions, false))
|
||||
return false;
|
||||
|
||||
if (!_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution)
|
||||
if (!Resolve(uid, ref stomach, ref solutions, logMissing: false)
|
||||
|| !_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution)
|
||||
|| !CanTransferSolution(uid, solution, stomach, solutions))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_solutionContainerSystem.TryAddSolution(stomach.Solution.Value, solution);
|
||||
// Add each reagent to ReagentDeltas. Used to track how long each reagent has been in the stomach
|
||||
|
||||
@@ -1,73 +1,95 @@
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Temperature.Components;
|
||||
using Content.Server.Temperature.Systems;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Body.Systems;
|
||||
|
||||
public sealed class ThermalRegulatorSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly TemperatureSystem _tempSys = default!;
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSys = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ThermalRegulatorComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<ThermalRegulatorComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<ThermalRegulatorComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval;
|
||||
}
|
||||
|
||||
private void OnUnpaused(Entity<ThermalRegulatorComponent> ent, ref EntityUnpausedEvent args)
|
||||
{
|
||||
ent.Comp.NextUpdate += args.PausedTime;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
var query = EntityQueryEnumerator<ThermalRegulatorComponent>();
|
||||
while (query.MoveNext(out var uid, out var regulator))
|
||||
{
|
||||
regulator.AccumulatedFrametime += frameTime;
|
||||
if (regulator.AccumulatedFrametime < 1)
|
||||
if (_gameTiming.CurTime < regulator.NextUpdate)
|
||||
continue;
|
||||
|
||||
regulator.AccumulatedFrametime -= 1;
|
||||
ProcessThermalRegulation(uid, regulator);
|
||||
regulator.NextUpdate += regulator.UpdateInterval;
|
||||
ProcessThermalRegulation((uid, regulator));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes thermal regulation for a mob
|
||||
/// </summary>
|
||||
private void ProcessThermalRegulation(EntityUid uid, ThermalRegulatorComponent comp)
|
||||
private void ProcessThermalRegulation(Entity<ThermalRegulatorComponent, TemperatureComponent?> ent)
|
||||
{
|
||||
if (!EntityManager.TryGetComponent(uid, out TemperatureComponent? temperatureComponent)) return;
|
||||
if (!Resolve(ent, ref ent.Comp2, logMissing: false))
|
||||
return;
|
||||
|
||||
var totalMetabolismTempChange = comp.MetabolismHeat - comp.RadiatedHeat;
|
||||
var totalMetabolismTempChange = ent.Comp1.MetabolismHeat - ent.Comp1.RadiatedHeat;
|
||||
|
||||
// implicit heat regulation
|
||||
var tempDiff = Math.Abs(temperatureComponent.CurrentTemperature - comp.NormalBodyTemperature);
|
||||
var heatCapacity = _tempSys.GetHeatCapacity(uid, temperatureComponent);
|
||||
var tempDiff = Math.Abs(ent.Comp2.CurrentTemperature - ent.Comp1.NormalBodyTemperature);
|
||||
var heatCapacity = _tempSys.GetHeatCapacity(ent, ent);
|
||||
var targetHeat = tempDiff * heatCapacity;
|
||||
if (temperatureComponent.CurrentTemperature > comp.NormalBodyTemperature)
|
||||
if (ent.Comp2.CurrentTemperature > ent.Comp1.NormalBodyTemperature)
|
||||
{
|
||||
totalMetabolismTempChange -= Math.Min(targetHeat, comp.ImplicitHeatRegulation);
|
||||
totalMetabolismTempChange -= Math.Min(targetHeat, ent.Comp1.ImplicitHeatRegulation);
|
||||
}
|
||||
else
|
||||
{
|
||||
totalMetabolismTempChange += Math.Min(targetHeat, comp.ImplicitHeatRegulation);
|
||||
totalMetabolismTempChange += Math.Min(targetHeat, ent.Comp1.ImplicitHeatRegulation);
|
||||
}
|
||||
|
||||
_tempSys.ChangeHeat(uid, totalMetabolismTempChange, true, temperatureComponent);
|
||||
_tempSys.ChangeHeat(ent, totalMetabolismTempChange, ignoreHeatResistance: true, ent);
|
||||
|
||||
// recalc difference and target heat
|
||||
tempDiff = Math.Abs(temperatureComponent.CurrentTemperature - comp.NormalBodyTemperature);
|
||||
tempDiff = Math.Abs(ent.Comp2.CurrentTemperature - ent.Comp1.NormalBodyTemperature);
|
||||
targetHeat = tempDiff * heatCapacity;
|
||||
|
||||
// if body temperature is not within comfortable, thermal regulation
|
||||
// processes starts
|
||||
if (tempDiff > comp.ThermalRegulationTemperatureThreshold)
|
||||
if (tempDiff > ent.Comp1.ThermalRegulationTemperatureThreshold)
|
||||
return;
|
||||
|
||||
if (temperatureComponent.CurrentTemperature > comp.NormalBodyTemperature)
|
||||
if (ent.Comp2.CurrentTemperature > ent.Comp1.NormalBodyTemperature)
|
||||
{
|
||||
if (!_actionBlockerSys.CanSweat(uid)) return;
|
||||
_tempSys.ChangeHeat(uid, -Math.Min(targetHeat, comp.SweatHeatRegulation), true,
|
||||
temperatureComponent);
|
||||
if (!_actionBlockerSys.CanSweat(ent))
|
||||
return;
|
||||
|
||||
_tempSys.ChangeHeat(ent, -Math.Min(targetHeat, ent.Comp1.SweatHeatRegulation), ignoreHeatResistance: true, ent);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_actionBlockerSys.CanShiver(uid)) return;
|
||||
_tempSys.ChangeHeat(uid, Math.Min(targetHeat, comp.ShiveringHeatRegulation), true,
|
||||
temperatureComponent);
|
||||
if (!_actionBlockerSys.CanShiver(ent))
|
||||
return;
|
||||
|
||||
_tempSys.ChangeHeat(ent, Math.Min(targetHeat, ent.Comp1.ShiveringHeatRegulation), ignoreHeatResistance: true, ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Content.Server.Cargo.Components;
|
||||
using Content.Server.Station.Systems;
|
||||
|
||||
namespace Content.Server.Cargo.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for marking containers as
|
||||
@@ -17,4 +19,10 @@ public sealed partial class CargoBountyLabelComponent : Component
|
||||
/// Used to prevent recursion in calculating the price.
|
||||
/// </summary>
|
||||
public bool Calculating;
|
||||
|
||||
/// <summary>
|
||||
/// The Station System to check and remove bounties from
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntityUid? AssociatedStationId;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Content.Server.Cargo.Components;
|
||||
using Content.Server.Labels;
|
||||
using Content.Server.NameIdentifier;
|
||||
using Content.Server.Paper;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared.Cargo;
|
||||
using Content.Shared.Cargo.Components;
|
||||
using Content.Shared.Cargo.Prototypes;
|
||||
@@ -65,16 +66,17 @@ public sealed partial class CargoSystem
|
||||
|
||||
var label = Spawn(component.BountyLabelId, Transform(uid).Coordinates);
|
||||
component.NextPrintTime = _timing.CurTime + component.PrintDelay;
|
||||
SetupBountyLabel(label, bounty.Value);
|
||||
SetupBountyLabel(label, station, bounty.Value);
|
||||
_audio.PlayPvs(component.PrintSound, uid);
|
||||
}
|
||||
|
||||
public void SetupBountyLabel(EntityUid uid, CargoBountyData bounty, PaperComponent? paper = null, CargoBountyLabelComponent? label = null)
|
||||
public void SetupBountyLabel(EntityUid uid, EntityUid stationId, CargoBountyData bounty, PaperComponent? paper = null, CargoBountyLabelComponent? label = null)
|
||||
{
|
||||
if (!Resolve(uid, ref paper, ref label) || !_protoMan.TryIndex<CargoBountyPrototype>(bounty.Bounty, out var prototype))
|
||||
return;
|
||||
|
||||
label.Id = bounty.Id;
|
||||
label.AssociatedStationId = stationId;
|
||||
var msg = new FormattedMessage();
|
||||
msg.AddText(Loc.GetString("bounty-manifest-header", ("id", bounty.Id)));
|
||||
msg.PushNewline();
|
||||
@@ -103,7 +105,7 @@ public sealed partial class CargoSystem
|
||||
if (!_container.TryGetContainingContainer(uid, out var container) || container.ID != LabelSystem.ContainerName)
|
||||
return;
|
||||
|
||||
if (_station.GetOwningStation(uid) is not { } station || !TryComp<StationCargoBountyDatabaseComponent>(station, out var database))
|
||||
if (component.AssociatedStationId is not { } station || !TryComp<StationCargoBountyDatabaseComponent>(station, out var database))
|
||||
return;
|
||||
|
||||
if (database.CheckedBounties.Contains(component.Id))
|
||||
@@ -131,14 +133,18 @@ public sealed partial class CargoSystem
|
||||
if (!TryGetBountyLabel(sold, out _, out var component))
|
||||
continue;
|
||||
|
||||
if (!TryGetBountyFromId(args.Station, component.Id, out var bounty))
|
||||
if (component.AssociatedStationId is not { } station || !TryGetBountyFromId(station, component.Id, out var bounty))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!IsBountyComplete(sold, bounty.Value))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TryRemoveBounty(args.Station, bounty.Value);
|
||||
FillBountyDatabase(args.Station);
|
||||
TryRemoveBounty(station, bounty.Value);
|
||||
FillBountyDatabase(station);
|
||||
_adminLogger.Add(LogType.Action, LogImpact.Low, $"Bounty \"{bounty.Value.Bounty}\" (id:{bounty.Value.Id}) was fulfilled");
|
||||
}
|
||||
}
|
||||
@@ -196,7 +202,7 @@ public sealed partial class CargoSystem
|
||||
FillBountyDatabase(entity);
|
||||
}
|
||||
|
||||
public bool IsBountyComplete(EntityUid container, EntityUid? station, out HashSet<EntityUid> bountyEntities)
|
||||
public bool IsBountyComplete(EntityUid container, out HashSet<EntityUid> bountyEntities)
|
||||
{
|
||||
if (!TryGetBountyLabel(container, out _, out var component))
|
||||
{
|
||||
@@ -204,7 +210,7 @@ public sealed partial class CargoSystem
|
||||
return false;
|
||||
}
|
||||
|
||||
station ??= _station.GetOwningStation(container);
|
||||
var station = component.AssociatedStationId;
|
||||
if (station == null)
|
||||
{
|
||||
bountyEntities = new();
|
||||
@@ -225,7 +231,7 @@ public sealed partial class CargoSystem
|
||||
return IsBountyComplete(container, data, out _);
|
||||
}
|
||||
|
||||
public bool IsBountyComplete(EntityUid container, CargoBountyData data, out HashSet<EntityUid> bountyEntities)
|
||||
public bool IsBountyComplete(EntityUid container, CargoBountyData data, out HashSet<EntityUid> bountyEntities)
|
||||
{
|
||||
if (!_protoMan.TryIndex(data.Bounty, out var proto))
|
||||
{
|
||||
@@ -314,7 +320,7 @@ public sealed partial class CargoSystem
|
||||
var children = GetBountyEntities(ent);
|
||||
foreach (var child in children)
|
||||
{
|
||||
entities.Add(child);
|
||||
entities.Add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,9 +230,8 @@ public sealed partial class CargoSystem
|
||||
|
||||
#region Station
|
||||
|
||||
private bool SellPallets(EntityUid gridUid, EntityUid? station, out double amount)
|
||||
private bool SellPallets(EntityUid gridUid, out double amount)
|
||||
{
|
||||
station ??= _station.GetOwningStation(gridUid);
|
||||
GetPalletGoods(gridUid, out var toSell, out amount);
|
||||
|
||||
Log.Debug($"Cargo sold {toSell.Count} entities for {amount}");
|
||||
@@ -240,11 +239,9 @@ public sealed partial class CargoSystem
|
||||
if (toSell.Count == 0)
|
||||
return false;
|
||||
|
||||
if (station != null)
|
||||
{
|
||||
var ev = new EntitySoldEvent(station.Value, toSell);
|
||||
RaiseLocalEvent(ref ev);
|
||||
}
|
||||
|
||||
var ev = new EntitySoldEvent(toSell);
|
||||
RaiseLocalEvent(ref ev);
|
||||
|
||||
foreach (var ent in toSell)
|
||||
{
|
||||
@@ -299,7 +296,7 @@ public sealed partial class CargoSystem
|
||||
return false;
|
||||
}
|
||||
|
||||
var complete = IsBountyComplete(uid, (EntityUid?) null, out var bountyEntities);
|
||||
var complete = IsBountyComplete(uid, out var bountyEntities);
|
||||
|
||||
// Recursively check for mobs at any point.
|
||||
var children = xform.ChildEnumerator;
|
||||
@@ -332,7 +329,7 @@ public sealed partial class CargoSystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SellPallets(gridUid, null, out var price))
|
||||
if (!SellPallets(gridUid, out var price))
|
||||
return;
|
||||
|
||||
var stackPrototype = _protoMan.Index<StackPrototype>(component.CashType);
|
||||
@@ -354,4 +351,4 @@ public sealed partial class CargoSystem
|
||||
/// deleted but after the price has been calculated.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public readonly record struct EntitySoldEvent(EntityUid Station, HashSet<EntityUid> Sold);
|
||||
public readonly record struct EntitySoldEvent(HashSet<EntityUid> Sold);
|
||||
|
||||
@@ -37,7 +37,6 @@ public sealed partial class CargoSystem : SharedCargoSystem
|
||||
[Dependency] private readonly PricingSystem _pricing = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
|
||||
[Dependency] private readonly ShuttleConsoleSystem _console = default!;
|
||||
[Dependency] private readonly StackSystem _stack = default!;
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
|
||||
@@ -57,7 +57,7 @@ public sealed class PriceGunSystem : EntitySystem
|
||||
return;
|
||||
|
||||
// Check if we're scanning a bounty crate
|
||||
if (_bountySystem.IsBountyComplete(args.Target.Value, (EntityUid?) null, out _))
|
||||
if (_bountySystem.IsBountyComplete(args.Target.Value, out _))
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("price-gun-bounty-complete"), args.User, args.User);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Stacks;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -27,7 +26,6 @@ public sealed class PricingSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IComponentFactory _factory = default!;
|
||||
[Dependency] private readonly IConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||
|
||||
@@ -4,7 +4,6 @@ using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Numerics;
|
||||
|
||||
@@ -12,8 +11,6 @@ namespace Content.Server.Chemistry.Containers.EntitySystems;
|
||||
|
||||
public sealed partial class SolutionContainerSystem : SharedSolutionContainerSystem
|
||||
{
|
||||
[Dependency] private readonly INetManager _netManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.Nutrition.EntitySystems;
|
||||
@@ -30,7 +29,6 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly OpenableSystem _openable = default!;
|
||||
|
||||
public override void Initialize()
|
||||
|
||||
@@ -170,7 +170,6 @@ namespace Content.Server.Communications
|
||||
|
||||
_uiSystem.SetUiState(ui, new CommunicationsConsoleInterfaceState(
|
||||
CanAnnounce(comp),
|
||||
CanBroadcast(comp),
|
||||
CanCallOrRecall(comp),
|
||||
levels,
|
||||
currentLevel,
|
||||
@@ -184,11 +183,6 @@ namespace Content.Server.Communications
|
||||
return comp.AnnouncementCooldownRemaining <= 0f;
|
||||
}
|
||||
|
||||
private static bool CanBroadcast(CommunicationsConsoleComponent comp)
|
||||
{
|
||||
return comp.AnnouncementCooldownRemaining <= 0f;
|
||||
}
|
||||
|
||||
private bool CanUse(EntityUid user, EntityUid console)
|
||||
{
|
||||
// This shouldn't technically be possible because of BUI but don't trust client.
|
||||
|
||||
@@ -2,7 +2,6 @@ using System.Numerics;
|
||||
using Content.Server.Administration;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
||||
namespace Content.Server.Decals;
|
||||
@@ -11,7 +10,6 @@ namespace Content.Server.Decals;
|
||||
public sealed class EditDecalCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
public string Command => "editdecal";
|
||||
public string Description => "Edits a decal.";
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using Content.Server.Administration;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using SQLitePCL;
|
||||
|
||||
namespace Content.Server.Decals.Commands
|
||||
{
|
||||
@@ -11,7 +9,6 @@ namespace Content.Server.Decals.Commands
|
||||
public sealed class RemoveDecalCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
public string Command => "rmdecal";
|
||||
public string Description => "removes a decal";
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Disposal.Tube;
|
||||
using Content.Server.Disposal.Tube.Components;
|
||||
@@ -12,14 +11,12 @@ using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Disposal.Unit.EntitySystems
|
||||
{
|
||||
public sealed class DisposableSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ThrowingSystem _throwing = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
[Dependency] private readonly DisposalUnitSystem _disposalUnitSystem = default!;
|
||||
|
||||
@@ -13,6 +13,7 @@ using Robust.Shared.Serialization.Manager;
|
||||
using System.Numerics;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Dragon;
|
||||
|
||||
@@ -69,8 +70,9 @@ public sealed class DragonRiftSystem : EntitySystem
|
||||
comp.State = DragonRiftState.AlmostFinished;
|
||||
Dirty(uid, comp);
|
||||
|
||||
var location = xform.LocalPosition;
|
||||
_chat.DispatchGlobalAnnouncement(Loc.GetString("carp-rift-warning", ("location", location)), playSound: false, colorOverride: Color.Red);
|
||||
var msg = Loc.GetString("carp-rift-warning",
|
||||
("location", FormattedMessage.RemoveMarkup(_navMap.GetNearestBeaconString((uid, xform)))));
|
||||
_chat.DispatchGlobalAnnouncement(msg, playSound: false, colorOverride: Color.Red);
|
||||
_audio.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), true);
|
||||
_navMap.SetBeaconEnabled(uid, true);
|
||||
}
|
||||
|
||||
@@ -41,8 +41,32 @@ public sealed partial class ElectrifiedComponent : Component
|
||||
[DataField("lowVoltageNode")]
|
||||
public string? LowVoltageNode;
|
||||
|
||||
/// <summary>
|
||||
/// Damage multiplier for HV electrocution
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float HighVoltageDamageMultiplier = 3f;
|
||||
|
||||
/// <summary>
|
||||
/// Shock time multiplier for HV electrocution
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float HighVoltageTimeMultiplier = 1.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Damage multiplier for MV electrocution
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float MediumVoltageDamageMultiplier = 2f;
|
||||
|
||||
/// <summary>
|
||||
/// Shock time multiplier for MV electrocution
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float MediumVoltageTimeMultiplier = 1.25f;
|
||||
|
||||
[DataField("shockDamage")]
|
||||
public int ShockDamage = 20;
|
||||
public float ShockDamage = 7.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Shock time, in seconds.
|
||||
|
||||
@@ -15,10 +15,4 @@ public sealed partial class ElectrocutionComponent : Component
|
||||
|
||||
[DataField("timeLeft")]
|
||||
public float TimeLeft;
|
||||
|
||||
[DataField("accumDamage")]
|
||||
public float AccumulatedDamage;
|
||||
|
||||
[DataField("baseDamage")]
|
||||
public float BaseDamage = 20f;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user