* serv3 in shared pt 1 * beginning of deepclone api * progress in implementing ideepclone & serv3 in content * adds target * its cant hurt you it cant hurt you * more changes to content.server * adds dataclasses * almost there * renamed & edited entry * finishes refactoring content to use serv3 * gasmixture runtimes, next: reagentunit * fucin hell that was an annoying one * adds flags * fixes some yaml errors * removes comment * fixes generic components for now * removes todo actually clones values my god paul fixes bug involving resolving custom data classes from other proj renames dataclass fixes spritecomp adds WithFormat.Constants support * adds deepclone to ResistanceSet * adds a bunch of deepclone implementations adds a deepclone analyzer (TODO) adds a deep clone fallback for classes & structs * fixes a bunch of runtimes * adds deepclone to entityuid * adds generator to sln * gets rid of warnings * fixes * argh * componentdata refactors * more deepclone impl * heck me i reworked all of content deepclone * renames custom dataclasstarget * misc * reworks prototypes * deepclone nuke * renamed customdataclass attribute * fixes everything * misc fixed * the killcommit * getting there * changed yamlfieldattribute namespace * adds back iselfserialize * renames everything to data(field/definition) * ouch * Fix most errors on content * Fix more errors in content * Fix some components * work on tests * fixes some customdataclasses * fuggin shit * yes * yeas * Remove data classes * Data field naming fixes * arg * Git resetti RobustToolbox * Merge fixes * General fixes * Fix startup serialization errors * Fix DamageContainerPrototype when supported classes or types are null * Implement construction graph step type serializer * Fix up construction serialization * Fix up construction serialization part 2 * Fix null list in technology database component * Fix body serialization * Fix entity storage serialization * Fix actions serialization * Fix AI serialization * Fix reaction serialization * Fix body serialization * Fix grid atmosphere serialization * Rename IServ3Manager to ISerializationManager * Convert every non generic serializer to the new format, general fixes * Serialization and body system fix * pushinheritance fix * Update all prototypes to have a parent and have consistent id/parent properties * Merge fixes * smh my head * cuddling slaps * Content commit for engine PR * stuff * more fixes * argh * yes even you are fixed * changelog fixes * fixes seeds * argh * Test fixes * Add writing for alert order prototype * Fix alert order writing * FIX * its been alot ok * Fix the rest of the visualizers * Fix server alerts component tests * Fix alert prototype tests not using the read value * Fix alert prototype tests initializing serialization multiple times * THIS IS AN AMERICAN CODEBASE GOD BLESS THE USA * Add ImplicitDataDefinitionForInheritors to IMechanismBehavior Fixes the behaviors not being found * Fix NRE in strap component Good night to the 1 buckle optimization * Fix clothing component slot flags serialization tag * Fix body component in all components test * Merge fixes * ffs * Make construction graph prototype use serialization hooks * human yaml linted * a * Do the thing for construction * stuff * a * monke see yaml linter * LINT HARDER * Remove redundant todo * yes * Add skip hook argument to readers and copiers * we gamin * test/datafield fixes * adds more verbose validation * moves linter to action * Improve construction graph step type serializer error message * Fix ammo box component NRE * gamin * some updates to the linter * yes * removes that test * misc fixes * array fix priority fix misc fixes * adds proper info the validation * adds alwaysrelevant usa * Make yaml linter take half as long to run (~50% less) * Make yaml linter 5 times faster (~80% less execution time) * based vera being based * fixes mapsaving * warning cleanup & moves surpressor * removes old msbuild targets * Revert "Make yaml linter 5 times faster (~80% less execution time)" This reverts commit 3e6091359a26252c3e98828199553de668031c63. * Add -nowarn to yaml linter run configuration * Improve yaml linter message feedback * Make dependencies an argument instead of a property on the serialization manager * yamllinting slaps * Clean up type serializers * Move yaml linter code to its own method * Fix yaml errors * Change yaml linter action name and remove -nowarn * yaml linter please shut * Git resetti robust toolbox Co-authored-by: Paul <ritter.paul1+git@googlemail.com> Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
322 lines
9.9 KiB
C#
322 lines
9.9 KiB
C#
#nullable enable
|
|
using System;
|
|
using System.Linq;
|
|
using Content.Server.Atmos;
|
|
using Content.Server.GameObjects.Components.Atmos.Piping;
|
|
using Content.Server.Interfaces;
|
|
using Content.Server.Utility;
|
|
using Content.Shared.Atmos;
|
|
using Content.Shared.GameObjects.Components.Atmos;
|
|
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
|
using Content.Shared.Interfaces.GameObjects.Components;
|
|
using Robust.Server.GameObjects;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.Serialization.Manager.Attributes;
|
|
using Robust.Shared.ViewVariables;
|
|
|
|
namespace Content.Server.GameObjects.Components.Atmos
|
|
{
|
|
/// <summary>
|
|
/// Component that manages gas mixtures temperature, pressure and output.
|
|
/// </summary>
|
|
[RegisterComponent]
|
|
[ComponentReference(typeof(IActivate))]
|
|
public class GasCanisterComponent : Component, IGasMixtureHolder, IActivate
|
|
{
|
|
public override string Name => "GasCanister";
|
|
|
|
private const int MaxLabelLength = 32;
|
|
|
|
[ViewVariables(VVAccess.ReadWrite)]
|
|
public string Label { get; set; } = "Gas Canister";
|
|
|
|
[ViewVariables(VVAccess.ReadWrite)]
|
|
public bool ValveOpened { get; set; } = false;
|
|
|
|
/// <summary>
|
|
/// What <see cref="GasMixture"/> the canister contains.
|
|
/// </summary>
|
|
[ViewVariables(VVAccess.ReadWrite)]
|
|
[DataField("gasMixture")]
|
|
public GasMixture Air { get; set; } = new (DefaultVolume);
|
|
|
|
[ViewVariables]
|
|
public bool Anchored => !Owner.TryGetComponent<IPhysicsComponent>(out var physics) || physics.Anchored;
|
|
|
|
/// <summary>
|
|
/// The floor connector port that the canister is attached to.
|
|
/// </summary>
|
|
[ViewVariables]
|
|
public GasCanisterPortComponent? ConnectedPort { get; private set; }
|
|
|
|
[ViewVariables]
|
|
public bool ConnectedToPort => ConnectedPort != null;
|
|
|
|
public const float DefaultVolume = 10;
|
|
|
|
[ViewVariables(VVAccess.ReadWrite)] public float ReleasePressure { get; set; }
|
|
|
|
/// <summary>
|
|
/// The user interface bound to the canister.
|
|
/// </summary>
|
|
private BoundUserInterface? UserInterface => Owner.GetUIOrNull(SharedGasCanisterComponent.GasCanisterUiKey.Key);
|
|
|
|
/// <summary>
|
|
/// Stores the last ui state after it's been casted into <see cref="GasCanisterBoundUserInterface"/>
|
|
/// </summary>
|
|
private GasCanisterBoundUserInterfaceState? _lastUiState;
|
|
|
|
private AppearanceComponent? _appearance;
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
if (Owner.TryGetComponent<IPhysicsComponent>(out var physics))
|
|
{
|
|
AnchorUpdate();
|
|
}
|
|
if (UserInterface != null)
|
|
{
|
|
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
|
}
|
|
|
|
// Init some variables
|
|
Label = Owner.Name;
|
|
Owner.TryGetComponent(out _appearance);
|
|
|
|
UpdateUserInterface();
|
|
UpdateAppearance();
|
|
}
|
|
|
|
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
|
{
|
|
base.HandleMessage(message, component);
|
|
switch (message)
|
|
{
|
|
case AnchoredChangedMessage:
|
|
AnchorUpdate();
|
|
break;
|
|
}
|
|
}
|
|
|
|
#region Connector port methods
|
|
|
|
public override void OnRemove()
|
|
{
|
|
base.OnRemove();
|
|
if (UserInterface != null)
|
|
{
|
|
UserInterface.OnReceiveMessage -= OnUiReceiveMessage;
|
|
}
|
|
DisconnectFromPort();
|
|
}
|
|
|
|
public void TryConnectToPort()
|
|
{
|
|
if (!Owner.TryGetComponent<SnapGridComponent>(out var snapGrid)) return;
|
|
var port = snapGrid.GetLocal()
|
|
.Select(entity => entity.TryGetComponent<GasCanisterPortComponent>(out var port) ? port : null)
|
|
.Where(port => port != null)
|
|
.Where(port => !port!.ConnectedToCanister)
|
|
.FirstOrDefault();
|
|
if (port == null) return;
|
|
ConnectedPort = port;
|
|
ConnectedPort.ConnectGasCanister(this);
|
|
}
|
|
|
|
|
|
public void DisconnectFromPort()
|
|
{
|
|
ConnectedPort?.DisconnectGasCanister();
|
|
ConnectedPort = null;
|
|
}
|
|
|
|
private void AnchorUpdate()
|
|
{
|
|
if (Anchored)
|
|
{
|
|
TryConnectToPort();
|
|
}
|
|
else
|
|
{
|
|
DisconnectFromPort();
|
|
}
|
|
UpdateAppearance();
|
|
}
|
|
|
|
#endregion
|
|
|
|
void IActivate.Activate(ActivateEventArgs eventArgs)
|
|
{
|
|
if (!eventArgs.User.TryGetComponent(out IActorComponent? actor))
|
|
return;
|
|
|
|
UserInterface?.Open(actor.playerSession);
|
|
}
|
|
|
|
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
|
{
|
|
if (obj.Session.AttachedEntity == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!PlayerCanUse(obj.Session.AttachedEntity))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If the label has been changed by a client
|
|
if (obj.Message is CanisterLabelChangedMessage canLabelMessage)
|
|
{
|
|
var newLabel = canLabelMessage.NewLabel;
|
|
if (newLabel.Length > MaxLabelLength)
|
|
newLabel = newLabel.Substring(0, MaxLabelLength);
|
|
Label = newLabel;
|
|
Owner.Name = Label;
|
|
UpdateUserInterface();
|
|
return;
|
|
}
|
|
|
|
// If the release pressure has been adjusted by the client on the gas canister
|
|
if (obj.Message is ReleasePressureButtonPressedMessage rPMessage)
|
|
{
|
|
ReleasePressure += rPMessage.ReleasePressure;
|
|
ReleasePressure = Math.Clamp(ReleasePressure, 0, 1000);
|
|
ReleasePressure = MathF.Round(ReleasePressure, 2);
|
|
UpdateUserInterface();
|
|
return;
|
|
}
|
|
|
|
|
|
if (obj.Message is UiButtonPressedMessage btnPressedMessage)
|
|
{
|
|
switch (btnPressedMessage.Button)
|
|
{
|
|
case UiButton.ValveToggle:
|
|
ToggleValve();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update the user interface if relevant
|
|
/// </summary>
|
|
private void UpdateUserInterface()
|
|
{
|
|
var state = GetUserInterfaceState();
|
|
|
|
if (_lastUiState != null && _lastUiState.Equals(state))
|
|
{
|
|
return;
|
|
}
|
|
|
|
_lastUiState = state;
|
|
UserInterface?.SetState(state);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update the canister's sprite
|
|
/// </summary>
|
|
private void UpdateAppearance()
|
|
{
|
|
_appearance?.SetData(GasCanisterVisuals.ConnectedState, ConnectedToPort);
|
|
// The Eris canisters are being used, so best to use the Eris light logic unless someone else has a better idea.
|
|
// https://github.com/discordia-space/CEV-Eris/blob/fdd6ee7012f46838a6711adb1737cd90c48ae448/code/game/machinery/atmoalter/canister.dm#L129
|
|
if (Air.Pressure < 10)
|
|
{
|
|
_appearance?.SetData(GasCanisterVisuals.PressureState, 0);
|
|
}
|
|
else if (Air.Pressure < Atmospherics.OneAtmosphere)
|
|
{
|
|
_appearance?.SetData(GasCanisterVisuals.PressureState, 1);
|
|
}
|
|
else if (Air.Pressure < (15 * Atmospherics.OneAtmosphere))
|
|
{
|
|
_appearance?.SetData(GasCanisterVisuals.PressureState, 2);
|
|
}
|
|
else
|
|
{
|
|
_appearance?.SetData(GasCanisterVisuals.PressureState, 3);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the current interface state from server data
|
|
/// </summary>
|
|
/// <returns>The state</returns>
|
|
private GasCanisterBoundUserInterfaceState GetUserInterfaceState()
|
|
{
|
|
// We round the pressure for ease of reading
|
|
return new GasCanisterBoundUserInterfaceState(Label,
|
|
MathF.Round(Air.Pressure, 2),
|
|
ReleasePressure,
|
|
ValveOpened);
|
|
}
|
|
|
|
public void AirWasUpdated()
|
|
{
|
|
UpdateUserInterface();
|
|
UpdateAppearance();
|
|
}
|
|
|
|
#region Check methods
|
|
|
|
private bool PlayerCanUse(IEntity? player)
|
|
{
|
|
if (player == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!ActionBlockerSystem.CanInteract(player) ||
|
|
!ActionBlockerSystem.CanUse(player))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
/// <summary>
|
|
/// Called when the canister's valve is toggled
|
|
/// </summary>
|
|
private void ToggleValve()
|
|
{
|
|
ValveOpened = !ValveOpened;
|
|
UpdateUserInterface();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called every frame
|
|
/// </summary>
|
|
/// <param name="frameTime"></param>
|
|
public void Update(in float frameTime)
|
|
{
|
|
if (ValveOpened)
|
|
{
|
|
var tileAtmosphere = Owner.Transform.Coordinates.GetTileAtmosphere();
|
|
if (tileAtmosphere != null)
|
|
{
|
|
// If tileAtmosphere.Air is null, then we're airblocked, so DON'T release
|
|
if (tileAtmosphere.Air != null)
|
|
{
|
|
Air.ReleaseGasTo(tileAtmosphere.Air, ReleasePressure);
|
|
tileAtmosphere.Invalidate();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Air.ReleaseGasTo(null, ReleasePressure);
|
|
}
|
|
|
|
AirWasUpdated();
|
|
}
|
|
}
|
|
}
|
|
}
|