Merge remote-tracking branch 'upstream/master' into 20-10-30-admins
This commit is contained in:
@@ -6,6 +6,7 @@ using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.EntitySystems.DoAfter;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.GameObjects.Components.ActionBlocking;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
@@ -115,7 +116,7 @@ namespace Content.Server.GameObjects.Components.ActionBlocking
|
||||
CanStillInteract = _hands.Hands.Count() > CuffedHandCount;
|
||||
|
||||
OnCuffedStateChanged.Invoke();
|
||||
UpdateStatusEffect();
|
||||
UpdateAlert();
|
||||
UpdateHeldItems();
|
||||
Dirty();
|
||||
}
|
||||
@@ -181,17 +182,17 @@ namespace Content.Server.GameObjects.Components.ActionBlocking
|
||||
/// <summary>
|
||||
/// Updates the status effect indicator on the HUD.
|
||||
/// </summary>
|
||||
private void UpdateStatusEffect()
|
||||
private void UpdateAlert()
|
||||
{
|
||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent status))
|
||||
if (Owner.TryGetComponent(out ServerAlertsComponent status))
|
||||
{
|
||||
if (CanStillInteract)
|
||||
{
|
||||
status.RemoveStatusEffect(StatusEffect.Cuffed);
|
||||
status.ClearAlert(AlertType.Handcuffed);
|
||||
}
|
||||
else
|
||||
{
|
||||
status.ChangeStatusEffectIcon(StatusEffect.Cuffed, "/Textures/Interface/StatusEffects/Handcuffed/Handcuffed.png");
|
||||
status.ShowAlert(AlertType.Handcuffed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -282,7 +283,7 @@ namespace Content.Server.GameObjects.Components.ActionBlocking
|
||||
|
||||
CanStillInteract = _hands.Hands.Count() > CuffedHandCount;
|
||||
OnCuffedStateChanged.Invoke();
|
||||
UpdateStatusEffect();
|
||||
UpdateAlert();
|
||||
Dirty();
|
||||
|
||||
if (CuffedHandCount == 0)
|
||||
|
||||
@@ -299,6 +299,7 @@ namespace Content.Server.GameObjects.Components.Arcade
|
||||
_component = component;
|
||||
_allBlockGamePieces = (BlockGamePieceType[]) Enum.GetValues(typeof(BlockGamePieceType));
|
||||
_internalNextPiece = GetRandomBlockGamePiece(_component._random);
|
||||
InitializeNewBlock();
|
||||
}
|
||||
|
||||
private void SendHighscoreUpdate()
|
||||
@@ -315,8 +316,6 @@ namespace Content.Server.GameObjects.Components.Arcade
|
||||
|
||||
public void StartGame()
|
||||
{
|
||||
InitializeNewBlock();
|
||||
|
||||
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Game));
|
||||
|
||||
FullUpdate();
|
||||
@@ -569,10 +568,10 @@ namespace Content.Server.GameObjects.Components.Arcade
|
||||
break;
|
||||
case BlockGamePlayerAction.Pause:
|
||||
_running = false;
|
||||
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Pause));
|
||||
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Pause, _started));
|
||||
break;
|
||||
case BlockGamePlayerAction.Unpause:
|
||||
if (!_gameOver)
|
||||
if (!_gameOver && _started)
|
||||
{
|
||||
_running = true;
|
||||
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Game));
|
||||
@@ -583,7 +582,7 @@ namespace Content.Server.GameObjects.Components.Arcade
|
||||
break;
|
||||
case BlockGamePlayerAction.ShowHighscores:
|
||||
_running = false;
|
||||
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Highscores));
|
||||
_component.UserInterface?.SendMessage(new BlockGameMessages.BlockGameSetScreenMessage(BlockGameMessages.BlockGameScreen.Highscores, _started));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -654,6 +653,7 @@ namespace Content.Server.GameObjects.Components.Arcade
|
||||
}
|
||||
|
||||
private bool IsGameOver => _field.Any(block => block.Position.Y == 0);
|
||||
|
||||
private void InvokeGameover()
|
||||
{
|
||||
_running = false;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
@@ -22,7 +23,7 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
public void Update(float airPressure)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent damageable)) return;
|
||||
Owner.TryGetComponent(out ServerStatusEffectsComponent status);
|
||||
Owner.TryGetComponent(out ServerAlertsComponent status);
|
||||
|
||||
var highPressureMultiplier = 1f;
|
||||
var lowPressureMultiplier = 1f;
|
||||
@@ -50,11 +51,11 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
|
||||
if (pressure <= Atmospherics.HazardLowPressure)
|
||||
{
|
||||
status.ChangeStatusEffect(StatusEffect.Pressure, "/Textures/Interface/StatusEffects/Pressure/lowpressure2.png", null);
|
||||
status.ShowAlert(AlertType.LowPressure, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
status.ChangeStatusEffect(StatusEffect.Pressure, "/Textures/Interface/StatusEffects/Pressure/lowpressure1.png", null);
|
||||
status.ShowAlert(AlertType.LowPressure, 1);
|
||||
break;
|
||||
|
||||
// High pressure.
|
||||
@@ -72,16 +73,16 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
|
||||
if (pressure >= Atmospherics.HazardHighPressure)
|
||||
{
|
||||
status.ChangeStatusEffect(StatusEffect.Pressure, "/Textures/Interface/StatusEffects/Pressure/highpressure2.png", null);
|
||||
status.ShowAlert(AlertType.HighPressure, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
status.ChangeStatusEffect(StatusEffect.Pressure, "/Textures/Interface/StatusEffects/Pressure/highpressure1.png", null);
|
||||
status.ShowAlert(AlertType.HighPressure, 1);
|
||||
break;
|
||||
|
||||
// Normal pressure.
|
||||
default:
|
||||
status?.RemoveStatusEffect(StatusEffect.Pressure);
|
||||
status?.ClearAlertCategory(AlertCategory.Pressure);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,11 +44,6 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
AutoClose = false;
|
||||
Safety = false;
|
||||
|
||||
if (Occludes && Owner.TryGetComponent(out OccluderComponent occluder))
|
||||
{
|
||||
occluder.Enabled = false;
|
||||
}
|
||||
|
||||
State = DoorState.Open;
|
||||
SetAppearance(DoorVisualState.Open);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Content.Server.Atmos;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.Components.Temperature;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Damage;
|
||||
@@ -93,15 +94,15 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
FireStacks = MathF.Min(0, FireStacks + 1);
|
||||
}
|
||||
|
||||
Owner.TryGetComponent(out ServerStatusEffectsComponent status);
|
||||
Owner.TryGetComponent(out ServerAlertsComponent status);
|
||||
|
||||
if (!OnFire)
|
||||
{
|
||||
status?.RemoveStatusEffect(StatusEffect.Fire);
|
||||
status?.ClearAlert(AlertType.Fire);
|
||||
return;
|
||||
}
|
||||
|
||||
status?.ChangeStatusEffect(StatusEffect.Fire, "/Textures/Interface/StatusEffects/Fire/fire.png", null);
|
||||
status.ShowAlert(AlertType.Fire, onClickAlert: OnClickAlert);
|
||||
|
||||
if (FireStacks > 0)
|
||||
{
|
||||
@@ -153,6 +154,14 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClickAlert(ClickAlertEventArgs args)
|
||||
{
|
||||
if (args.Player.TryGetComponent(out FlammableComponent flammable))
|
||||
{
|
||||
flammable.Resist();
|
||||
}
|
||||
}
|
||||
|
||||
public void CollideWith(IEntity collidedWith)
|
||||
{
|
||||
if (!collidedWith.TryGetComponent(out FlammableComponent otherFlammable))
|
||||
|
||||
@@ -1,22 +1,14 @@
|
||||
#nullable enable
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.Mobs;
|
||||
using Content.Shared.GameObjects.Components.Body;
|
||||
using Content.Shared.GameObjects.Components.Body.Behavior;
|
||||
using Content.Shared.GameObjects.Components.Body.Part;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.ComponentDependencies;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Body.Behavior
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class BrainBehaviorComponent : MechanismBehaviorComponent
|
||||
public class BrainBehavior : MechanismBehavior
|
||||
{
|
||||
public override string Name => "Brain";
|
||||
|
||||
protected override void OnAddedToBody(IBody body)
|
||||
{
|
||||
base.OnAddedToBody(body);
|
||||
@@ -66,9 +58,5 @@ namespace Content.Server.GameObjects.Components.Body.Behavior
|
||||
|
||||
oldMind.Mind?.TransferTo(newEntity);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,17 @@
|
||||
using Content.Shared.GameObjects.Components.Body.Behavior;
|
||||
using Content.Shared.GameObjects.Components.Body.Networks;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Body.Behavior
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedHeartBehaviorComponent))]
|
||||
public class HeartBehaviorComponent : SharedHeartBehaviorComponent
|
||||
public class HeartBehavior : MechanismBehavior
|
||||
{
|
||||
private float _accumulatedFrameTime;
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
// TODO BODY do between pre and metabolism
|
||||
if (Mechanism?.Body == null ||
|
||||
!Mechanism.Body.Owner.HasComponent<SharedBloodstreamComponent>())
|
||||
if (Parent.Body == null ||
|
||||
!Parent.Body.Owner.HasComponent<SharedBloodstreamComponent>())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -8,19 +8,15 @@ using Content.Server.GameObjects.Components.Body.Respiratory;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.GameObjects.Components.Body.Behavior;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Body.Behavior
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedLungBehaviorComponent))]
|
||||
public class LungBehaviorComponent : SharedLungBehaviorComponent
|
||||
public class LungBehavior : MechanismBehavior
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
@@ -30,18 +26,24 @@ namespace Content.Server.GameObjects.Components.Body.Behavior
|
||||
|
||||
[ViewVariables] public GasMixture Air { get; set; } = default!;
|
||||
|
||||
[ViewVariables] public override float Temperature => Air.Temperature;
|
||||
[ViewVariables] public float Temperature => Air.Temperature;
|
||||
|
||||
[ViewVariables] public override float Volume => Air.Volume;
|
||||
[ViewVariables] public float Volume => Air.Volume;
|
||||
|
||||
[ViewVariables] public TimeSpan GaspPopupCooldown { get; private set; }
|
||||
|
||||
[ViewVariables] public LungStatus Status { get; set; }
|
||||
|
||||
[ViewVariables] public float CycleDelay { get; set; }
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
Air = new GasMixture {Temperature = Atmospherics.NormalBodyTemperature};
|
||||
|
||||
serializer.DataField(this, l => l.CycleDelay, "cycleDelay", 2);
|
||||
|
||||
serializer.DataReadWriteFunction(
|
||||
"volume",
|
||||
6,
|
||||
@@ -61,7 +63,7 @@ namespace Content.Server.GameObjects.Components.Body.Behavior
|
||||
() => GaspPopupCooldown.TotalSeconds);
|
||||
}
|
||||
|
||||
public override void Gasp()
|
||||
public void Gasp()
|
||||
{
|
||||
if (_gameTiming.CurTime >= _lastGaspPopupTime + GaspPopupCooldown)
|
||||
{
|
||||
@@ -148,7 +150,7 @@ namespace Content.Server.GameObjects.Components.Body.Behavior
|
||||
_accumulatedFrameTime = absoluteTime - delay;
|
||||
}
|
||||
|
||||
public override void Inhale(float frameTime)
|
||||
public void Inhale(float frameTime)
|
||||
{
|
||||
if (Body != null && Body.Owner.TryGetComponent(out InternalsComponent? internals)
|
||||
&& internals.BreathToolEntity != null && internals.GasTankEntity != null
|
||||
@@ -176,7 +178,7 @@ namespace Content.Server.GameObjects.Components.Body.Behavior
|
||||
ToBloodstream(Air);
|
||||
}
|
||||
|
||||
public override void Exhale(float frameTime)
|
||||
public void Exhale(float frameTime)
|
||||
{
|
||||
if (!Owner.Transform.Coordinates.TryGetTileAir(out var tileAir))
|
||||
{
|
||||
@@ -218,4 +220,11 @@ namespace Content.Server.GameObjects.Components.Body.Behavior
|
||||
Air.Merge(lungRemoved);
|
||||
}
|
||||
}
|
||||
|
||||
public enum LungStatus
|
||||
{
|
||||
None = 0,
|
||||
Inhaling,
|
||||
Exhaling
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#nullable enable
|
||||
using Content.Shared.GameObjects.Components.Body;
|
||||
using Content.Shared.GameObjects.Components.Body.Behavior;
|
||||
using Content.Shared.GameObjects.Components.Body.Mechanism;
|
||||
using Content.Shared.GameObjects.Components.Body.Part;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Body.Behavior
|
||||
{
|
||||
public abstract class MechanismBehavior : IMechanismBehavior
|
||||
{
|
||||
public IBody? Body => Part?.Body;
|
||||
|
||||
public IBodyPart? Part => Parent.Part;
|
||||
|
||||
public IMechanism Parent { get; private set; } = default!;
|
||||
|
||||
public IEntity Owner => Parent.Owner;
|
||||
|
||||
public virtual void ExposeData(ObjectSerializer serializer) { }
|
||||
|
||||
public virtual void Initialize(IMechanism parent)
|
||||
{
|
||||
Parent = parent;
|
||||
}
|
||||
|
||||
public virtual void Startup()
|
||||
{
|
||||
if (Part == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Body == null)
|
||||
{
|
||||
AddedToPart(Part);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddedToPartInBody(Body, Part);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddedToBody(IBody body)
|
||||
{
|
||||
DebugTools.AssertNotNull(Body);
|
||||
DebugTools.AssertNotNull(body);
|
||||
|
||||
OnAddedToBody(body);
|
||||
}
|
||||
|
||||
public void AddedToPart(IBodyPart part)
|
||||
{
|
||||
DebugTools.AssertNotNull(Part);
|
||||
DebugTools.AssertNotNull(part);
|
||||
|
||||
OnAddedToPart(part);
|
||||
}
|
||||
|
||||
public void AddedToPartInBody(IBody body, IBodyPart part)
|
||||
{
|
||||
DebugTools.AssertNotNull(Body);
|
||||
DebugTools.AssertNotNull(body);
|
||||
DebugTools.AssertNotNull(Part);
|
||||
DebugTools.AssertNotNull(part);
|
||||
|
||||
OnAddedToPartInBody(body, part);
|
||||
}
|
||||
|
||||
public void RemovedFromBody(IBody old)
|
||||
{
|
||||
DebugTools.AssertNull(Body);
|
||||
DebugTools.AssertNotNull(old);
|
||||
|
||||
OnRemovedFromBody(old);
|
||||
}
|
||||
|
||||
public void RemovedFromPart(IBodyPart old)
|
||||
{
|
||||
DebugTools.AssertNull(Part);
|
||||
DebugTools.AssertNotNull(old);
|
||||
|
||||
OnRemovedFromPart(old);
|
||||
}
|
||||
|
||||
public void RemovedFromPartInBody(IBody oldBody, IBodyPart oldPart)
|
||||
{
|
||||
DebugTools.AssertNull(Body);
|
||||
DebugTools.AssertNull(Part);
|
||||
DebugTools.AssertNotNull(oldBody);
|
||||
DebugTools.AssertNotNull(oldPart);
|
||||
|
||||
OnRemovedFromPartInBody(oldBody, oldPart);
|
||||
}
|
||||
|
||||
protected virtual void OnAddedToBody(IBody body) { }
|
||||
|
||||
protected virtual void OnAddedToPart(IBodyPart part) { }
|
||||
|
||||
protected virtual void OnAddedToPartInBody(IBody body, IBodyPart part) { }
|
||||
|
||||
protected virtual void OnRemovedFromBody(IBody old) { }
|
||||
|
||||
protected virtual void OnRemovedFromPart(IBodyPart old) { }
|
||||
|
||||
protected virtual void OnRemovedFromPartInBody(IBody oldBody, IBodyPart oldPart) { }
|
||||
|
||||
public virtual void Update(float frameTime) { }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Shared.GameObjects.Components.Body;
|
||||
using Content.Shared.GameObjects.Components.Body.Behavior;
|
||||
using Content.Shared.GameObjects.Components.Body.Part;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Body.Behavior
|
||||
{
|
||||
public static class MechanismExtensions
|
||||
{
|
||||
public static bool HasMechanismBehavior<T>(this IBody body) where T : IMechanismBehavior
|
||||
{
|
||||
return body.Parts.Values.Any(p => p.HasMechanismBehavior<T>());
|
||||
}
|
||||
|
||||
public static bool HasMechanismBehavior<T>(this IBodyPart part) where T : IMechanismBehavior
|
||||
{
|
||||
return part.Mechanisms.Any(m => m.HasBehavior<T>());
|
||||
}
|
||||
|
||||
public static IEnumerable<IMechanismBehavior> GetMechanismBehaviors(this IBody body)
|
||||
{
|
||||
foreach (var part in body.Parts.Values)
|
||||
foreach (var mechanism in part.Mechanisms)
|
||||
foreach (var behavior in mechanism.Behaviors.Values)
|
||||
{
|
||||
yield return behavior;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetMechanismBehaviors(this IBody body,
|
||||
[NotNullWhen(true)] out List<IMechanismBehavior>? behaviors)
|
||||
{
|
||||
behaviors = body.GetMechanismBehaviors().ToList();
|
||||
|
||||
if (behaviors.Count == 0)
|
||||
{
|
||||
behaviors = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static IEnumerable<T> GetMechanismBehaviors<T>(this IBody body) where T : class, IMechanismBehavior
|
||||
{
|
||||
foreach (var part in body.Parts.Values)
|
||||
foreach (var mechanism in part.Mechanisms)
|
||||
foreach (var behavior in mechanism.Behaviors.Values)
|
||||
{
|
||||
if (behavior is T tBehavior)
|
||||
{
|
||||
yield return tBehavior;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetMechanismBehaviors<T>(this IBody entity, [NotNullWhen(true)] out List<T>? behaviors)
|
||||
where T : class, IMechanismBehavior
|
||||
{
|
||||
behaviors = entity.GetMechanismBehaviors<T>().ToList();
|
||||
|
||||
if (behaviors.Count == 0)
|
||||
{
|
||||
behaviors = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.Chemistry;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Body.Networks;
|
||||
using Content.Shared.GameObjects.Components.Chemistry;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Body.Behavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Where reagents go when ingested. Tracks ingested reagents over time, and
|
||||
/// eventually transfers them to <see cref="SharedBloodstreamComponent"/> once digested.
|
||||
/// </summary>
|
||||
public class StomachBehavior : MechanismBehavior
|
||||
{
|
||||
private float _accumulatedFrameTime;
|
||||
|
||||
/// <summary>
|
||||
/// Updates digestion status of ingested reagents.
|
||||
/// Once reagents surpass _digestionDelay they are moved to the
|
||||
/// bloodstream, where they are then metabolized.
|
||||
/// </summary>
|
||||
/// <param name="frameTime">
|
||||
/// The time since the last update in seconds.
|
||||
/// </param>
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
if (Body == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_accumulatedFrameTime += frameTime;
|
||||
|
||||
// Update at most once per second
|
||||
if (_accumulatedFrameTime < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_accumulatedFrameTime -= 1;
|
||||
|
||||
if (!Owner.TryGetComponent(out SharedSolutionContainerComponent? solution) ||
|
||||
!Body.Owner.TryGetComponent(out SharedBloodstreamComponent? bloodstream))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Add reagents ready for transfer to bloodstream to transferSolution
|
||||
var transferSolution = new Solution();
|
||||
|
||||
// Use ToList here to remove entries while iterating
|
||||
foreach (var delta in _reagentDeltas.ToList())
|
||||
{
|
||||
//Increment lifetime of reagents
|
||||
delta.Increment(1);
|
||||
if (delta.Lifetime > _digestionDelay)
|
||||
{
|
||||
solution.TryRemoveReagent(delta.ReagentId, delta.Quantity);
|
||||
transferSolution.AddReagent(delta.ReagentId, delta.Quantity);
|
||||
_reagentDeltas.Remove(delta);
|
||||
}
|
||||
}
|
||||
|
||||
// Transfer digested reagents to bloodstream
|
||||
bloodstream.TryTransferSolution(transferSolution);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Max volume of internal solution storage
|
||||
/// </summary>
|
||||
public ReagentUnit MaxVolume
|
||||
{
|
||||
get => Owner.TryGetComponent(out SharedSolutionContainerComponent? solution) ? solution.MaxVolume : ReagentUnit.Zero;
|
||||
set
|
||||
{
|
||||
if (Owner.TryGetComponent(out SharedSolutionContainerComponent? solution))
|
||||
{
|
||||
solution.MaxVolume = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initial internal solution storage volume
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
protected ReagentUnit InitialMaxVolume { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Time in seconds between reagents being ingested and them being
|
||||
/// transferred to <see cref="SharedBloodstreamComponent"/>
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private float _digestionDelay;
|
||||
|
||||
/// <summary>
|
||||
/// Used to track how long each reagent has been in the stomach
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private readonly List<ReagentDelta> _reagentDeltas = new List<ReagentDelta>();
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(this, s => s.InitialMaxVolume, "maxVolume", ReagentUnit.New(100));
|
||||
serializer.DataField(ref _digestionDelay, "digestionDelay", 20);
|
||||
}
|
||||
|
||||
public override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
if (!Owner.EnsureComponent(out SolutionContainerComponent solution))
|
||||
{
|
||||
Logger.Warning($"Entity {Owner} at {Owner.Transform.MapPosition} didn't have a {nameof(SolutionContainerComponent)}");
|
||||
}
|
||||
|
||||
solution.MaxVolume = InitialMaxVolume;
|
||||
}
|
||||
|
||||
public bool CanTransferSolution(Solution solution)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SharedSolutionContainerComponent? solutionComponent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: For now no partial transfers. Potentially change by design
|
||||
if (!solutionComponent.CanAddSolution(solution))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryTransferSolution(Solution solution)
|
||||
{
|
||||
if (!CanTransferSolution(solution))
|
||||
return false;
|
||||
|
||||
if (!Owner.TryGetComponent(out SharedSolutionContainerComponent? solutionComponent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add solution to _stomachContents
|
||||
solutionComponent.TryAddSolution(solution, false, true);
|
||||
// Add each reagent to _reagentDeltas. Used to track how long each reagent has been in the stomach
|
||||
foreach (var reagent in solution.Contents)
|
||||
{
|
||||
_reagentDeltas.Add(new ReagentDelta(reagent.ReagentId, reagent.Quantity));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to track quantity changes when ingesting & digesting reagents
|
||||
/// </summary>
|
||||
protected class ReagentDelta
|
||||
{
|
||||
public readonly string ReagentId;
|
||||
public readonly ReagentUnit Quantity;
|
||||
public float Lifetime { get; private set; }
|
||||
|
||||
public ReagentDelta(string reagentId, ReagentUnit quantity)
|
||||
{
|
||||
ReagentId = reagentId;
|
||||
Quantity = quantity;
|
||||
Lifetime = 0.0f;
|
||||
}
|
||||
|
||||
public void Increment(float delta) => Lifetime += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Content.Server.GameObjects.Components.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Body.Behavior;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Body.Behavior
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedStomachBehaviorComponent))]
|
||||
public class StomachBehaviorComponent : SharedStomachBehaviorComponent
|
||||
{
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
if (!Owner.EnsureComponent(out SolutionContainerComponent solution))
|
||||
{
|
||||
Logger.Warning($"Entity {Owner} at {Owner.Transform.MapPosition} didn't have a {nameof(SolutionContainerComponent)}");
|
||||
}
|
||||
|
||||
solution.MaxVolume = InitialMaxVolume;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ using Content.Server.GameObjects.Components.Mobs.State;
|
||||
using Content.Server.GameObjects.Components.Pulling;
|
||||
using Content.Server.GameObjects.Components.Strap;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.GameObjects.Components.Buckle;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.Components.Strap;
|
||||
@@ -15,21 +16,16 @@ using Content.Shared.GameObjects.Verbs;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Content.Shared.Utility;
|
||||
using NFluidsynth;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.EntitySystemMessages;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.ComponentDependencies;
|
||||
using Robust.Shared.GameObjects.Components.Transform;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -39,12 +35,10 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
[RegisterComponent]
|
||||
public class BuckleComponent : SharedBuckleComponent, IInteractHand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
[ComponentDependency] public readonly AppearanceComponent? AppearanceComponent = null;
|
||||
[ComponentDependency] private readonly ServerStatusEffectsComponent? _serverStatusEffectsComponent = null;
|
||||
[ComponentDependency] private readonly ServerAlertsComponent? _serverAlertsComponent = null;
|
||||
[ComponentDependency] private readonly StunnableComponent? _stunnableComponent = null;
|
||||
[ComponentDependency] private readonly MobStateManagerComponent? _mobStateManagerComponent = null;
|
||||
|
||||
@@ -69,7 +63,10 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
[ViewVariables]
|
||||
private TimeSpan _buckleTime;
|
||||
|
||||
public Vector2? BuckleOffset { get; private set; }
|
||||
/// <summary>
|
||||
/// The position offset that is being applied to this entity if buckled.
|
||||
/// </summary>
|
||||
public Vector2 BuckleOffset { get; private set; }
|
||||
|
||||
private StrapComponent? _buckledTo;
|
||||
|
||||
@@ -91,20 +88,6 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
[ViewVariables]
|
||||
public override bool Buckled => BuckledTo != null;
|
||||
|
||||
/// <summary>
|
||||
/// True if the entity was inserted or removed from a container
|
||||
/// before updating, false otherwise.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private bool ContainerChanged { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// True if the entity was forcefully moved while buckled and should
|
||||
/// unbuckle next update, false otherwise
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private bool Moved { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount of space that this entity occupies in a
|
||||
/// <see cref="StrapComponent"/>.
|
||||
@@ -116,26 +99,38 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
/// Shows or hides the buckled status effect depending on if the
|
||||
/// entity is buckled or not.
|
||||
/// </summary>
|
||||
private void BuckleStatus()
|
||||
private void UpdateBuckleStatus()
|
||||
{
|
||||
if (_serverStatusEffectsComponent != null)
|
||||
if (_serverAlertsComponent == null)
|
||||
{
|
||||
if (Buckled)
|
||||
{
|
||||
_serverStatusEffectsComponent.ChangeStatusEffectIcon(StatusEffect.Buckled, BuckledTo!.BuckledIcon);
|
||||
}
|
||||
else
|
||||
{
|
||||
_serverStatusEffectsComponent.RemoveStatusEffect(StatusEffect.Buckled);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (Buckled)
|
||||
{
|
||||
_serverAlertsComponent.ShowAlert(BuckledTo != null ? BuckledTo.BuckledAlertType : AlertType.Buckled,
|
||||
onClickAlert: OnClickAlert);
|
||||
}
|
||||
else
|
||||
{
|
||||
_serverAlertsComponent.ClearAlertCategory(AlertCategory.Buckled);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnClickAlert(ClickAlertEventArgs args)
|
||||
{
|
||||
if (args.Player.TryGetComponent(out BuckleComponent? buckle))
|
||||
{
|
||||
buckle.TryUnbuckle(args.Player);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Reattaches this entity to the strap, modifying its position and rotation.
|
||||
/// </summary>
|
||||
/// <param name="strap">The strap to reattach to.</param>
|
||||
private void ReAttach(StrapComponent strap)
|
||||
public void ReAttach(StrapComponent strap)
|
||||
{
|
||||
var ownTransform = Owner.Transform;
|
||||
var strapTransform = strap.Owner.Transform;
|
||||
@@ -161,7 +156,7 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
if (strapTransform.WorldRotation.GetCardinalDir() == Direction.North)
|
||||
{
|
||||
BuckleOffset = (0, 0.15f);
|
||||
ownTransform.WorldPosition = strapTransform.WorldPosition + BuckleOffset!.Value;
|
||||
ownTransform.WorldPosition = strapTransform.WorldPosition + BuckleOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -266,8 +261,7 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
return false;
|
||||
}
|
||||
|
||||
_entitySystem.GetEntitySystem<AudioSystem>()
|
||||
.PlayFromEntity(strap.BuckleSound, Owner);
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity(strap.BuckleSound, Owner);
|
||||
|
||||
if (!strap.TryAdd(this))
|
||||
{
|
||||
@@ -283,13 +277,10 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
BuckledTo = strap;
|
||||
|
||||
ReAttach(strap);
|
||||
BuckleStatus();
|
||||
UpdateBuckleStatus();
|
||||
|
||||
SendMessage(new BuckleMessage(Owner, to));
|
||||
|
||||
Owner.EntityManager.EventBus.SubscribeEvent<MoveEvent>(EventSource.Local, this, MoveEvent);
|
||||
|
||||
|
||||
if (Owner.TryGetComponent(out PullableComponent? pullableComponent))
|
||||
{
|
||||
if (pullableComponent.Puller != null)
|
||||
@@ -315,12 +306,12 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
/// </returns>
|
||||
public bool TryUnbuckle(IEntity user, bool force = false)
|
||||
{
|
||||
if (!Buckled)
|
||||
if (BuckledTo == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
StrapComponent oldBuckledTo = BuckledTo!;
|
||||
var oldBuckledTo = BuckledTo;
|
||||
|
||||
if (!force)
|
||||
{
|
||||
@@ -360,21 +351,15 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
EntitySystem.Get<StandingStateSystem>().Standing(Owner);
|
||||
}
|
||||
|
||||
if (_mobStateManagerComponent != null)
|
||||
{
|
||||
_mobStateManagerComponent.CurrentMobState.EnterState(Owner);
|
||||
}
|
||||
_mobStateManagerComponent?.CurrentMobState.EnterState(Owner);
|
||||
|
||||
BuckleStatus();
|
||||
UpdateBuckleStatus();
|
||||
|
||||
oldBuckledTo.Remove(this);
|
||||
_entitySystem.GetEntitySystem<AudioSystem>()
|
||||
.PlayFromEntity(oldBuckledTo.UnbuckleSound, Owner);
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity(oldBuckledTo.UnbuckleSound, Owner);
|
||||
|
||||
SendMessage(new UnbuckleMessage(Owner, oldBuckledTo.Owner));
|
||||
|
||||
Owner.EntityManager.EventBus.UnsubscribeEvent<MoveEvent>(EventSource.Local, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -402,90 +387,6 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
return TryBuckle(user, to);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a buckled entity should be unbuckled from moving
|
||||
/// too far from its strap.
|
||||
/// </summary>
|
||||
/// <param name="moveEvent">The move event of a buckled entity.</param>
|
||||
private void MoveEvent(MoveEvent moveEvent)
|
||||
{
|
||||
if (moveEvent.Sender != Owner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (BuckledTo == null || !BuckleOffset.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var bucklePosition = BuckledTo.Owner.Transform.Coordinates.Offset(BuckleOffset.Value);
|
||||
|
||||
if (moveEvent.NewPosition.InRange(_entityManager, bucklePosition, 0.2f))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Moved = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the owner is inserted or removed from a container,
|
||||
/// to synchronize the state of buckling.
|
||||
/// </summary>
|
||||
/// <param name="message">The message received</param>
|
||||
private void InsertIntoContainer(ContainerModifiedMessage message)
|
||||
{
|
||||
if (message.Entity != Owner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ContainerChanged = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronizes the state of buckling depending on whether the entity
|
||||
/// was inserted or removed from a container, and whether or not
|
||||
/// its current strap (if there is one) has also been put into or removed
|
||||
/// from the same container as well.
|
||||
/// </summary>
|
||||
public void Update()
|
||||
{
|
||||
if (BuckledTo == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (Moved)
|
||||
{
|
||||
TryUnbuckle(Owner, true);
|
||||
Moved = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ContainerChanged)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var contained = ContainerHelpers.TryGetContainer(Owner, out var ownContainer);
|
||||
var strapContained = ContainerHelpers.TryGetContainer(BuckledTo.Owner, out var strapContainer);
|
||||
|
||||
if (contained != strapContained || ownContainer != strapContainer)
|
||||
{
|
||||
TryUnbuckle(Owner, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!contained && !strapContained)
|
||||
{
|
||||
ReAttach(BuckledTo);
|
||||
}
|
||||
|
||||
ContainerChanged = false;
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
@@ -499,32 +400,21 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
_unbuckleDelay = TimeSpan.FromSeconds(seconds);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_entityManager.EventBus.SubscribeEvent<EntInsertedIntoContainerMessage>(EventSource.Local, this, InsertIntoContainer);
|
||||
_entityManager.EventBus.SubscribeEvent<EntRemovedFromContainerMessage>(EventSource.Local, this, InsertIntoContainer);
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
BuckleStatus();
|
||||
UpdateBuckleStatus();
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
_entityManager.EventBus.UnsubscribeEvents(this);
|
||||
|
||||
BuckledTo?.Remove(this);
|
||||
|
||||
TryUnbuckle(Owner, true);
|
||||
|
||||
_buckleTime = default;
|
||||
BuckleStatus();
|
||||
UpdateBuckleStatus();
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
var trueTarget = target ?? user;
|
||||
|
||||
if (!trueTarget.TryGetComponent(out IBody body) ||
|
||||
!body.TryGetMechanismBehaviors<StomachBehaviorComponent>(out var stomachs))
|
||||
!body.TryGetMechanismBehaviors<StomachBehavior>(out var stomachs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -8,9 +8,7 @@ using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
@@ -28,18 +26,24 @@ namespace Content.Server.GameObjects.Components
|
||||
|
||||
private Regex _validation;
|
||||
|
||||
public event Action<Dictionary<string, string>> OnConfigUpdate;
|
||||
|
||||
public override void Initialize()
|
||||
public override void OnAdd()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
base.OnAdd();
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage -= UserInterfaceOnReceiveMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
@@ -48,7 +52,7 @@ namespace Content.Server.GameObjects.Components
|
||||
(list) => FillConfiguration(list, _config, ""),
|
||||
() => _config.Keys.ToList());
|
||||
|
||||
serializer.DataReadFunction("vailidation", "^[a-zA-Z0-9 ]*$", value => _validation = new Regex("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled));
|
||||
serializer.DataReadFunction("validation", "^[a-zA-Z0-9 ]*$", value => _validation = new Regex("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled));
|
||||
}
|
||||
|
||||
public string GetConfig(string name)
|
||||
@@ -90,22 +94,19 @@ namespace Content.Server.GameObjects.Components
|
||||
{
|
||||
var value = msg.Config.GetValueOrDefault(key);
|
||||
|
||||
if (_validation != null && !_validation.IsMatch(value) && value != "")
|
||||
if (value == null || _validation != null && !_validation.IsMatch(value) && value != "")
|
||||
continue;
|
||||
|
||||
_config[key] = value;
|
||||
}
|
||||
|
||||
OnConfigUpdate(_config);
|
||||
SendMessage(new ConfigUpdatedComponentMessage(config));
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateUserInterface()
|
||||
{
|
||||
if (UserInterface == null)
|
||||
return;
|
||||
|
||||
UserInterface.SetState(new ConfigurationBoundUserInterfaceState(_config));
|
||||
UserInterface?.SetState(new ConfigurationBoundUserInterfaceState(_config));
|
||||
}
|
||||
|
||||
private static void FillConfiguration<T>(List<string> list, Dictionary<string, T> configuration, T value){
|
||||
|
||||
@@ -13,6 +13,7 @@ using Content.Server.GameObjects.EntitySystems.DoAfter;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Content.Shared.GameObjects.Components.Body;
|
||||
using Content.Shared.GameObjects.Components.Disposal;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
@@ -603,12 +604,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
}
|
||||
|
||||
var network = IoCManager.Resolve<IDeviceNetwork>();
|
||||
_connection = new WiredNetworkConnection(OnReceiveNetMessage, false, Owner);
|
||||
|
||||
if (Owner.TryGetComponent<ConfigurationComponent>(out var configuration))
|
||||
configuration.OnConfigUpdate += OnConfigUpdate;
|
||||
|
||||
UpdateInterface();
|
||||
}
|
||||
|
||||
@@ -673,6 +669,9 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case SharedConfigurationComponent.ConfigUpdatedComponentMessage msg:
|
||||
OnConfigUpdate(msg.Config);
|
||||
break;
|
||||
case RelayMovementEntityMessage msg:
|
||||
if (!msg.Entity.TryGetComponent(out HandsComponent? hands) ||
|
||||
hands.Count == 0 ||
|
||||
@@ -745,7 +744,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
return false;
|
||||
}
|
||||
|
||||
// Duplicated code here, not sure how else to get actor inside to make UserInterface happy.
|
||||
// Duplicated code here, not sure how else to get actor inside to make UserInterface happy.
|
||||
|
||||
if (IsValidInteraction(eventArgs))
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.GameObjects.Components.Stack;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Utility;
|
||||
@@ -8,6 +9,7 @@ using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Items
|
||||
@@ -33,23 +35,52 @@ namespace Content.Server.GameObjects.Components.Items
|
||||
Owner.EnsureComponent<StackComponent>();
|
||||
}
|
||||
|
||||
private bool HasBaseTurf(ContentTileDefinition tileDef, string baseTurf)
|
||||
{
|
||||
foreach (var tileBaseTurf in tileDef.BaseTurfs)
|
||||
{
|
||||
if (baseTurf == tileBaseTurf)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void PlaceAt(IMapGrid mapGrid, EntityCoordinates location, ushort tileId, float offset = 0)
|
||||
{
|
||||
mapGrid.SetTile(location.Offset(new Vector2(offset, offset)), new Tile(tileId));
|
||||
EntitySystem.Get<AudioSystem>().PlayAtCoords("/Audio/Items/genhit.ogg", location, AudioHelpers.WithVariation(0.125f));
|
||||
}
|
||||
|
||||
public void AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return;
|
||||
if (!Owner.TryGetComponent(out StackComponent stack)) return;
|
||||
|
||||
var attacked = eventArgs.Target;
|
||||
var mapGrid = _mapManager.GetGrid(eventArgs.ClickLocation.GetGridId(Owner.EntityManager));
|
||||
var tile = mapGrid.GetTileRef(eventArgs.ClickLocation);
|
||||
var tileDef = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId];
|
||||
var location = eventArgs.ClickLocation.AlignWithClosestGridTile();
|
||||
var locationMap = location.ToMap(Owner.EntityManager);
|
||||
|
||||
if (tileDef.IsSubFloor && attacked == null && stack.Use(1))
|
||||
var desiredTile = (ContentTileDefinition)_tileDefinitionManager[_outputTile];
|
||||
|
||||
if (_mapManager.TryGetGrid(location.GetGridId(Owner.EntityManager), out var mapGrid))
|
||||
{
|
||||
var desiredTile = _tileDefinitionManager[_outputTile];
|
||||
mapGrid.SetTile(eventArgs.ClickLocation, new Tile(desiredTile.TileId));
|
||||
EntitySystem.Get<AudioSystem>().PlayAtCoords("/Audio/Items/genhit.ogg", eventArgs.ClickLocation);
|
||||
}
|
||||
var tile = mapGrid.GetTileRef(location);
|
||||
var baseTurf = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId];
|
||||
|
||||
if (HasBaseTurf(desiredTile, baseTurf.Name) && eventArgs.Target == null && stack.Use(1))
|
||||
{
|
||||
PlaceAt(mapGrid, location, desiredTile.TileId);
|
||||
}
|
||||
}
|
||||
else if(HasBaseTurf(desiredTile, "space"))
|
||||
{
|
||||
mapGrid = _mapManager.CreateGrid(locationMap.MapId);
|
||||
mapGrid.WorldPosition = locationMap.Position;
|
||||
location = new EntityCoordinates(mapGrid.GridEntityId, Vector2.Zero);
|
||||
PlaceAt(mapGrid, location, desiredTile.TileId, mapGrid.TileSize/2f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ namespace Content.Server.GameObjects.Components.Metabolism
|
||||
return;
|
||||
}
|
||||
|
||||
var lungs = _body.GetMechanismBehaviors<LungBehaviorComponent>().ToArray();
|
||||
var lungs = _body.GetMechanismBehaviors<LungBehavior>().ToArray();
|
||||
|
||||
var needs = NeedsAndDeficit(frameTime);
|
||||
var used = 0f;
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
using System;
|
||||
using Content.Server.Commands;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Robust.Server.Interfaces.Console;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Players;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Mobs
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedAlertsComponent))]
|
||||
public sealed class ServerAlertsComponent : SharedAlertsComponent
|
||||
{
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
if (EntitySystem.TryGet<WeightlessSystem>(out var weightlessSystem))
|
||||
{
|
||||
weightlessSystem.AddAlert(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.WarningS("alert", "weightlesssystem not found");
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
if (EntitySystem.TryGet<WeightlessSystem>(out var weightlessSystem))
|
||||
{
|
||||
weightlessSystem.RemoveAlert(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.WarningS("alert", "weightlesssystem not found");
|
||||
}
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new AlertsComponentState(CreateAlertStatesArray());
|
||||
}
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, netChannel, session);
|
||||
|
||||
if (session == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(session));
|
||||
}
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case ClickAlertMessage msg:
|
||||
{
|
||||
var player = session.AttachedEntity;
|
||||
|
||||
if (player != Owner)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Implement clicking other status effects in the HUD
|
||||
if (AlertManager.TryDecode(msg.EncodedAlert, out var alert))
|
||||
{
|
||||
PerformAlertClickCallback(alert, player);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.WarningS("alert", "unrecognized encoded alert {0}", msg.EncodedAlert);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ShowAlert : IClientCommand
|
||||
{
|
||||
public string Command => "showalert";
|
||||
public string Description => "Shows an alert for a player, defaulting to current player";
|
||||
public string Help => "showalert <alertType> <severity, -1 if no severity> <name or userID, omit for current player>";
|
||||
public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
|
||||
{
|
||||
var attachedEntity = player.AttachedEntity;
|
||||
if (args.Length > 2)
|
||||
{
|
||||
var target = args[2];
|
||||
if (!Commands.CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return;
|
||||
}
|
||||
|
||||
if (!CommandUtils.ValidateAttachedEntity(shell, player, attachedEntity)) return;
|
||||
|
||||
|
||||
if (!attachedEntity.TryGetComponent(out ServerAlertsComponent alertsComponent))
|
||||
{
|
||||
shell.SendText(player, "user has no alerts component");
|
||||
return;
|
||||
}
|
||||
|
||||
var alertType = args[0];
|
||||
var severity = args[1];
|
||||
var alertMgr = IoCManager.Resolve<AlertManager>();
|
||||
if (!alertMgr.TryGet(Enum.Parse<AlertType>(alertType), out var alert))
|
||||
{
|
||||
shell.SendText(player, "unrecognized alertType " + alertType);
|
||||
return;
|
||||
}
|
||||
if (!short.TryParse(severity, out var sevint))
|
||||
{
|
||||
shell.SendText(player, "invalid severity " + sevint);
|
||||
return;
|
||||
}
|
||||
alertsComponent.ShowAlert(alert.AlertType, sevint == -1 ? (short?) null : sevint);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ClearAlert : IClientCommand
|
||||
{
|
||||
public string Command => "clearalert";
|
||||
public string Description => "Clears an alert for a player, defaulting to current player";
|
||||
public string Help => "clearalert <alertType> <name or userID, omit for current player>";
|
||||
|
||||
public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
|
||||
{
|
||||
var attachedEntity = player.AttachedEntity;
|
||||
if (args.Length > 1)
|
||||
{
|
||||
var target = args[1];
|
||||
if (!CommandUtils.TryGetAttachedEntityByUsernameOrId(shell, target, player, out attachedEntity)) return;
|
||||
}
|
||||
|
||||
if (!CommandUtils.ValidateAttachedEntity(shell, player, attachedEntity)) return;
|
||||
|
||||
if (!attachedEntity.TryGetComponent(out ServerAlertsComponent alertsComponent))
|
||||
{
|
||||
shell.SendText(player, "user has no alerts component");
|
||||
return;
|
||||
}
|
||||
|
||||
var alertType = args[0];
|
||||
var alertMgr = IoCManager.Resolve<AlertManager>();
|
||||
if (!alertMgr.TryGet(Enum.Parse<AlertType>(alertType), out var alert))
|
||||
{
|
||||
shell.SendText(player, "unrecognized alertType " + alertType);
|
||||
return;
|
||||
}
|
||||
|
||||
alertsComponent.ClearAlert(alert.AlertType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Atmos;
|
||||
using Content.Server.GameObjects.Components.Buckle;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.Components.Pulling;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Mobs
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedStatusEffectsComponent))]
|
||||
public sealed class ServerStatusEffectsComponent : SharedStatusEffectsComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
private readonly Dictionary<StatusEffect, StatusEffectStatus> _statusEffects = new Dictionary<StatusEffect, StatusEffectStatus>();
|
||||
|
||||
public override IReadOnlyDictionary<StatusEffect, StatusEffectStatus> Statuses => _statusEffects;
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
EntitySystem.Get<WeightlessSystem>().AddStatus(this);
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
EntitySystem.Get<WeightlessSystem>().RemoveStatus(this);
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new StatusEffectComponentState(_statusEffects);
|
||||
}
|
||||
|
||||
public override void ChangeStatusEffectIcon(StatusEffect effect, string icon)
|
||||
{
|
||||
if (_statusEffects.TryGetValue(effect, out var value) && value.Icon == icon)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_statusEffects[effect] = new StatusEffectStatus()
|
||||
{Icon = icon, Cooldown = value.Cooldown};
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public void ChangeStatusEffectCooldown(StatusEffect effect, ValueTuple<TimeSpan, TimeSpan> cooldown)
|
||||
{
|
||||
if (_statusEffects.TryGetValue(effect, out var value)
|
||||
&& value.Cooldown == cooldown)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_statusEffects[effect] = new StatusEffectStatus()
|
||||
{
|
||||
Icon = value.Icon, Cooldown = cooldown
|
||||
};
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public override void ChangeStatusEffect(StatusEffect effect, string icon, ValueTuple<TimeSpan, TimeSpan>? cooldown)
|
||||
{
|
||||
_statusEffects[effect] = new StatusEffectStatus()
|
||||
{Icon = icon, Cooldown = cooldown};
|
||||
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public override void RemoveStatusEffect(StatusEffect effect)
|
||||
{
|
||||
if (!_statusEffects.Remove(effect))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, netChannel, session);
|
||||
|
||||
if (session == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(session));
|
||||
}
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case ClickStatusMessage msg:
|
||||
{
|
||||
var player = session.AttachedEntity;
|
||||
|
||||
if (player != Owner)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Implement clicking other status effects in the HUD
|
||||
switch (msg.Effect)
|
||||
{
|
||||
case StatusEffect.Buckled:
|
||||
if (!player.TryGetComponent(out BuckleComponent buckle))
|
||||
break;
|
||||
|
||||
buckle.TryUnbuckle(player);
|
||||
break;
|
||||
case StatusEffect.Piloting:
|
||||
if (!player.TryGetComponent(out ShuttleControllerComponent controller))
|
||||
break;
|
||||
|
||||
controller.RemoveController();
|
||||
break;
|
||||
case StatusEffect.Pulling:
|
||||
EntitySystem
|
||||
.Get<SharedPullingSystem>()
|
||||
.GetPulled(player)?
|
||||
.GetComponentOrNull<SharedPullableComponent>()?
|
||||
.TryStopPull();
|
||||
|
||||
break;
|
||||
case StatusEffect.Fire:
|
||||
if (!player.TryGetComponent(out FlammableComponent flammable))
|
||||
break;
|
||||
|
||||
flammable.Resist();
|
||||
break;
|
||||
default:
|
||||
player.PopupMessage(msg.Effect.ToString());
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
||||
@@ -17,10 +18,9 @@ namespace Content.Server.GameObjects.Components.Mobs.State
|
||||
appearance.SetData(DamageStateVisuals.State, DamageState.Critical);
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out ServerStatusEffectsComponent status))
|
||||
if (entity.TryGetComponent(out ServerAlertsComponent status))
|
||||
{
|
||||
status.ChangeStatusEffectIcon(StatusEffect.Health,
|
||||
"/Textures/Interface/StatusEffects/Human/humancrit-0.png"); //Todo: combine humancrit-0 and humancrit-1 into a gif and display it
|
||||
status.ShowAlert(AlertType.HumanCrit); //Todo: combine humancrit-0 and humancrit-1 into a gif and display it
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out ServerOverlayEffectsComponent overlay))
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
||||
@@ -18,10 +19,9 @@ namespace Content.Server.GameObjects.Components.Mobs.State
|
||||
appearance.SetData(DamageStateVisuals.State, DamageState.Dead);
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out ServerStatusEffectsComponent status))
|
||||
if (entity.TryGetComponent(out ServerAlertsComponent status))
|
||||
{
|
||||
status.ChangeStatusEffectIcon(StatusEffect.Health,
|
||||
"/Textures/Interface/StatusEffects/Human/humandead.png");
|
||||
status.ShowAlert(AlertType.HumanDead);
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out ServerOverlayEffectsComponent overlayComponent))
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
||||
@@ -51,9 +52,9 @@ namespace Content.Server.GameObjects.Components.Mobs.State
|
||||
// TODO: Might want to add an OnRemove() to IMobState since those are where these components are being used
|
||||
base.OnRemove();
|
||||
|
||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent status))
|
||||
if (Owner.TryGetComponent(out ServerAlertsComponent status))
|
||||
{
|
||||
status.RemoveStatusEffect(StatusEffect.Health);
|
||||
status.ClearAlert(AlertType.HumanHealth);
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out ServerOverlayEffectsComponent overlay))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Server.GameObjects.Components.Damage;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
||||
@@ -27,15 +28,14 @@ namespace Content.Server.GameObjects.Components.Mobs.State
|
||||
|
||||
public override void UpdateState(IEntity entity)
|
||||
{
|
||||
if (!entity.TryGetComponent(out ServerStatusEffectsComponent status))
|
||||
if (!entity.TryGetComponent(out ServerAlertsComponent status))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entity.TryGetComponent(out IDamageableComponent damageable))
|
||||
{
|
||||
status.ChangeStatusEffectIcon(StatusEffect.Health,
|
||||
"/Textures/Interface/StatusEffects/Human/human0.png");
|
||||
status.ShowAlert(AlertType.HumanHealth, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -49,10 +49,9 @@ namespace Content.Server.GameObjects.Components.Mobs.State
|
||||
return;
|
||||
}
|
||||
|
||||
var modifier = (int) (ruinable.TotalDamage / (threshold / 7f));
|
||||
var modifier = (short) (ruinable.TotalDamage / (threshold / 7f));
|
||||
|
||||
status.ChangeStatusEffectIcon(StatusEffect.Health,
|
||||
"/Textures/Interface/StatusEffects/Human/human" + modifier + ".png");
|
||||
status.ShowAlert(AlertType.HumanHealth, modifier);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -63,10 +62,9 @@ namespace Content.Server.GameObjects.Components.Mobs.State
|
||||
return;
|
||||
}
|
||||
|
||||
var modifier = (int) (damageable.TotalDamage / (threshold / 7f));
|
||||
var modifier = (short) (damageable.TotalDamage / (threshold / 7f));
|
||||
|
||||
status.ChangeStatusEffectIcon(StatusEffect.Health,
|
||||
"/Textures/Interface/StatusEffects/Human/human" + modifier + ".png");
|
||||
status.ShowAlert(AlertType.HumanHealth, modifier);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
@@ -89,7 +90,7 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
}
|
||||
|
||||
if (!StunStart.HasValue || !StunEnd.HasValue ||
|
||||
!Owner.TryGetComponent(out ServerStatusEffectsComponent status))
|
||||
!Owner.TryGetComponent(out ServerAlertsComponent status))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -102,7 +103,7 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
|
||||
if (progress >= length)
|
||||
{
|
||||
Owner.SpawnTimer(250, () => status.RemoveStatusEffect(StatusEffect.Stun), StatusRemoveCancellation.Token);
|
||||
Owner.SpawnTimer(250, () => status.ClearAlert(AlertType.Stun), StatusRemoveCancellation.Token);
|
||||
LastStun = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Shared.GameObjects.Components.Morgue;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Verbs;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -52,9 +53,20 @@ namespace Content.Server.GameObjects.Components.Morgue
|
||||
}
|
||||
}
|
||||
|
||||
public override bool CanOpen(IEntity user, bool silent = false)
|
||||
{
|
||||
if (Cooking)
|
||||
{
|
||||
if (!silent) Owner.PopupMessage(user, Loc.GetString("Safety first, not while it's active!"));
|
||||
return false;
|
||||
}
|
||||
return base.CanOpen(user, silent);
|
||||
}
|
||||
|
||||
public void Cremate()
|
||||
{
|
||||
if (Cooking) return;
|
||||
if (Open) return;
|
||||
|
||||
Appearance?.SetData(CrematoriumVisuals.Burning, true);
|
||||
Cooking = true;
|
||||
@@ -64,15 +76,18 @@ namespace Content.Server.GameObjects.Components.Morgue
|
||||
Appearance?.SetData(CrematoriumVisuals.Burning, false);
|
||||
Cooking = false;
|
||||
|
||||
for (var i = Contents.ContainedEntities.Count - 1; i >= 0; i--)
|
||||
if (Contents.ContainedEntities.Count > 0)
|
||||
{
|
||||
var item = Contents.ContainedEntities[i];
|
||||
Contents.Remove(item);
|
||||
item.Delete();
|
||||
}
|
||||
for (var i = Contents.ContainedEntities.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var item = Contents.ContainedEntities[i];
|
||||
Contents.Remove(item);
|
||||
item.Delete();
|
||||
}
|
||||
|
||||
var ash = Owner.EntityManager.SpawnEntity("Ash", Owner.Transform.Coordinates);
|
||||
Contents.Insert(ash);
|
||||
var ash = Owner.EntityManager.SpawnEntity("Ash", Owner.Transform.Coordinates);
|
||||
Contents.Insert(ash);
|
||||
}
|
||||
|
||||
TryOpenStorage(Owner);
|
||||
|
||||
@@ -85,7 +100,7 @@ namespace Content.Server.GameObjects.Components.Morgue
|
||||
{
|
||||
protected override void GetData(IEntity user, CrematoriumEntityStorageComponent component, VerbData data)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user) || component.Cooking)
|
||||
if (!ActionBlockerSystem.CanInteract(user) || component.Cooking || component.Open)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
#nullable enable
|
||||
using Content.Shared.GameObjects.Components.Buckle;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Content.Shared.Physics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Movement
|
||||
@@ -8,8 +11,8 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
[RegisterComponent]
|
||||
public class ClimbingComponent : SharedClimbingComponent
|
||||
{
|
||||
private bool _isClimbing = false;
|
||||
private ClimbController _climbController = default;
|
||||
private bool _isClimbing;
|
||||
private ClimbController? _climbController;
|
||||
|
||||
public override bool IsClimbing
|
||||
{
|
||||
@@ -29,6 +32,19 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
}
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||
{
|
||||
base.HandleMessage(message, component);
|
||||
switch (message)
|
||||
{
|
||||
case BuckleMessage msg:
|
||||
if (msg.Buckled)
|
||||
IsClimbing = false;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make the owner climb from one point to another
|
||||
/// </summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#nullable enable
|
||||
using Content.Server.GameObjects.Components.Buckle;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Content.Shared.GameObjects.Components.Strap;
|
||||
@@ -31,9 +32,9 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
private bool _movingRight;
|
||||
|
||||
/// <summary>
|
||||
/// The icon to be displayed when piloting from this chair.
|
||||
/// ID of the alert to show when piloting
|
||||
/// </summary>
|
||||
private string _pilotingIcon = default!;
|
||||
private AlertType _pilotingAlertType;
|
||||
|
||||
/// <summary>
|
||||
/// The entity that's currently controlling this component.
|
||||
@@ -137,7 +138,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
if (_controller != null ||
|
||||
!entity.TryGetComponent(out MindComponent? mind) ||
|
||||
mind.Mind == null ||
|
||||
!Owner.TryGetComponent(out ServerStatusEffectsComponent? status))
|
||||
!Owner.TryGetComponent(out ServerAlertsComponent? status))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -145,7 +146,15 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
mind.Mind.Visit(Owner);
|
||||
_controller = entity;
|
||||
|
||||
status.ChangeStatusEffectIcon(StatusEffect.Piloting, _pilotingIcon);
|
||||
status.ShowAlert(_pilotingAlertType, onClickAlert: OnClickAlert);
|
||||
}
|
||||
|
||||
private void OnClickAlert(ClickAlertEventArgs args)
|
||||
{
|
||||
if (args.Player.TryGetComponent(out ShuttleControllerComponent? controller))
|
||||
{
|
||||
controller.RemoveController();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -177,9 +186,9 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
/// <param name="entity">The entity to update</param>
|
||||
private void UpdateRemovedEntity(IEntity entity)
|
||||
{
|
||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent? status))
|
||||
if (Owner.TryGetComponent(out ServerAlertsComponent? status))
|
||||
{
|
||||
status.RemoveStatusEffect(StatusEffect.Piloting);
|
||||
status.ClearAlert(_pilotingAlertType);
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out MindComponent? mind))
|
||||
@@ -211,13 +220,13 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(ref _pilotingIcon, "pilotingIcon", "/Textures/Interface/StatusEffects/Buckle/buckled.png");
|
||||
serializer.DataField(ref _pilotingAlertType, "pilotingAlertType", AlertType.PilotingShuttle);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Owner.EnsureComponent<ServerStatusEffectsComponent>();
|
||||
Owner.EnsureComponent<ServerAlertsComponent>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Transform;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -34,8 +31,6 @@ namespace Content.Server.GameObjects.Components.NodeContainer
|
||||
{
|
||||
node.Initialize(Owner);
|
||||
}
|
||||
|
||||
Owner.EntityManager.EventBus.SubscribeEvent<RotateEvent>(EventSource.Local, this, RotateEvent);
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
@@ -55,16 +50,5 @@ namespace Content.Server.GameObjects.Components.NodeContainer
|
||||
}
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
private void RotateEvent(RotateEvent ev)
|
||||
{
|
||||
if (ev.Sender != Owner || ev.NewRotation == ev.OldRotation)
|
||||
return;
|
||||
|
||||
foreach (var rotatableNode in Nodes.OfType<IRotatableNode>())
|
||||
{
|
||||
rotatableNode.RotateEvent(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
}
|
||||
|
||||
if (!target.TryGetComponent(out IBody body) ||
|
||||
!body.TryGetMechanismBehaviors<StomachBehaviorComponent>(out var stomachs))
|
||||
!body.TryGetMechanismBehaviors<StomachBehavior>(out var stomachs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.Body.Behavior;
|
||||
using Content.Server.GameObjects.Components.Chemistry;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
@@ -133,7 +134,7 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
var trueTarget = target ?? user;
|
||||
|
||||
if (!trueTarget.TryGetComponent(out IBody? body) ||
|
||||
!body.TryGetMechanismBehaviors<SharedStomachBehaviorComponent>(out var stomachs))
|
||||
!body.TryGetMechanismBehaviors<StomachBehavior>(out var stomachs))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
@@ -70,11 +71,11 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
}
|
||||
|
||||
|
||||
public static readonly Dictionary<HungerThreshold, string> HungerThresholdImages = new Dictionary<HungerThreshold, string>
|
||||
public static readonly Dictionary<HungerThreshold, AlertType> HungerThresholdAlertTypes = new Dictionary<HungerThreshold, AlertType>
|
||||
{
|
||||
{ HungerThreshold.Overfed, "/Textures/Interface/StatusEffects/Hunger/Overfed.png" },
|
||||
{ HungerThreshold.Peckish, "/Textures/Interface/StatusEffects/Hunger/Peckish.png" },
|
||||
{ HungerThreshold.Starving, "/Textures/Interface/StatusEffects/Hunger/Starving.png" },
|
||||
{ HungerThreshold.Overfed, AlertType.Overfed },
|
||||
{ HungerThreshold.Peckish, AlertType.Peckish },
|
||||
{ HungerThreshold.Starving, AlertType.Starving },
|
||||
};
|
||||
|
||||
public void HungerThresholdEffect(bool force = false)
|
||||
@@ -89,15 +90,15 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
}
|
||||
|
||||
// Update UI
|
||||
Owner.TryGetComponent(out ServerStatusEffectsComponent statusEffectsComponent);
|
||||
Owner.TryGetComponent(out ServerAlertsComponent alertsComponent);
|
||||
|
||||
if (HungerThresholdImages.TryGetValue(_currentHungerThreshold, out var statusTexture))
|
||||
if (HungerThresholdAlertTypes.TryGetValue(_currentHungerThreshold, out var alertId))
|
||||
{
|
||||
statusEffectsComponent?.ChangeStatusEffectIcon(StatusEffect.Hunger, statusTexture);
|
||||
alertsComponent?.ShowAlert(alertId);
|
||||
}
|
||||
else
|
||||
{
|
||||
statusEffectsComponent?.RemoveStatusEffect(StatusEffect.Hunger);
|
||||
alertsComponent?.ClearAlertCategory(AlertCategory.Hunger);
|
||||
}
|
||||
|
||||
switch (_currentHungerThreshold)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
@@ -62,11 +63,11 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
{ThirstThreshold.Dead, 0.0f},
|
||||
};
|
||||
|
||||
public static readonly Dictionary<ThirstThreshold, string> ThirstThresholdImages = new Dictionary<ThirstThreshold, string>
|
||||
public static readonly Dictionary<ThirstThreshold, AlertType> ThirstThresholdAlertTypes = new Dictionary<ThirstThreshold, AlertType>
|
||||
{
|
||||
{ThirstThreshold.OverHydrated, "/Textures/Interface/StatusEffects/Thirst/OverHydrated.png"},
|
||||
{ThirstThreshold.Thirsty, "/Textures/Interface/StatusEffects/Thirst/Thirsty.png"},
|
||||
{ThirstThreshold.Parched, "/Textures/Interface/StatusEffects/Thirst/Parched.png"},
|
||||
{ThirstThreshold.OverHydrated, AlertType.Overhydrated},
|
||||
{ThirstThreshold.Thirsty, AlertType.Thirsty},
|
||||
{ThirstThreshold.Parched, AlertType.Parched},
|
||||
};
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
@@ -87,15 +88,15 @@ namespace Content.Server.GameObjects.Components.Nutrition
|
||||
}
|
||||
|
||||
// Update UI
|
||||
Owner.TryGetComponent(out ServerStatusEffectsComponent statusEffectsComponent);
|
||||
Owner.TryGetComponent(out ServerAlertsComponent alertsComponent);
|
||||
|
||||
if (ThirstThresholdImages.TryGetValue(_currentThirstThreshold, out var statusTexture))
|
||||
if (ThirstThresholdAlertTypes.TryGetValue(_currentThirstThreshold, out var alertId))
|
||||
{
|
||||
statusEffectsComponent?.ChangeStatusEffectIcon(StatusEffect.Thirst, statusTexture);
|
||||
alertsComponent?.ShowAlert(alertId);
|
||||
}
|
||||
else
|
||||
{
|
||||
statusEffectsComponent?.RemoveStatusEffect(StatusEffect.Thirst);
|
||||
alertsComponent?.ClearAlertCategory(AlertCategory.Thirst);
|
||||
}
|
||||
|
||||
switch (_currentThirstThreshold)
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.Access;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.GameObjects.Components.Paper;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Server.Interfaces.PDA;
|
||||
@@ -39,13 +40,19 @@ namespace Content.Server.GameObjects.Components.PDA
|
||||
[Dependency] private readonly IPDAUplinkManager _uplinkManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
[ViewVariables] private Container _idSlot = default!;
|
||||
[ViewVariables] private ContainerSlot _idSlot = default!;
|
||||
[ViewVariables] private ContainerSlot _penSlot = default!;
|
||||
|
||||
[ViewVariables] private bool _lightOn;
|
||||
[ViewVariables] private string _startingIdCard = default!;
|
||||
[ViewVariables] public bool IdSlotEmpty => _idSlot.ContainedEntities.Count < 1;
|
||||
|
||||
[ViewVariables] private string? _startingIdCard = default!;
|
||||
[ViewVariables] private string? _startingPen = default!;
|
||||
|
||||
[ViewVariables] public string? OwnerName { get; private set; }
|
||||
|
||||
[ViewVariables] public IdCardComponent? ContainedID { get; private set; }
|
||||
[ViewVariables] public bool IdSlotEmpty => _idSlot.ContainedEntity == null;
|
||||
[ViewVariables] public bool PenSlotEmpty => _penSlot.ContainedEntity == null;
|
||||
|
||||
[ViewVariables] private UplinkAccount? _syndicateUplinkAccount;
|
||||
|
||||
@@ -62,22 +69,34 @@ namespace Content.Server.GameObjects.Components.PDA
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(ref _startingIdCard, "idCard", "AssistantIDCard");
|
||||
serializer.DataField(ref _startingPen, "pen", "Pen");
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_idSlot = ContainerManagerComponent.Ensure<Container>("pda_entity_container", Owner, out var existed);
|
||||
_idSlot = ContainerManagerComponent.Ensure<ContainerSlot>("pda_entity_container", Owner);
|
||||
_penSlot = ContainerManagerComponent.Ensure<ContainerSlot>("pda_pen_slot", Owner);
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
|
||||
}
|
||||
|
||||
var idCard = _entityManager.SpawnEntity(_startingIdCard, Owner.Transform.Coordinates);
|
||||
var idCardComponent = idCard.GetComponent<IdCardComponent>();
|
||||
_idSlot.Insert(idCardComponent.Owner);
|
||||
ContainedID = idCardComponent;
|
||||
if (!string.IsNullOrEmpty(_startingIdCard))
|
||||
{
|
||||
var idCard = _entityManager.SpawnEntity(_startingIdCard, Owner.Transform.Coordinates);
|
||||
var idCardComponent = idCard.GetComponent<IdCardComponent>();
|
||||
_idSlot.Insert(idCardComponent.Owner);
|
||||
ContainedID = idCardComponent;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_startingPen))
|
||||
{
|
||||
var pen = _entityManager.SpawnEntity(_startingPen, Owner.Transform.Coordinates);
|
||||
_penSlot.Insert(pen);
|
||||
}
|
||||
|
||||
UpdatePDAAppearance();
|
||||
}
|
||||
|
||||
@@ -85,23 +104,29 @@ namespace Content.Server.GameObjects.Components.PDA
|
||||
{
|
||||
switch (message.Message)
|
||||
{
|
||||
case PDARequestUpdateInterfaceMessage msg:
|
||||
case PDARequestUpdateInterfaceMessage _:
|
||||
{
|
||||
UpdatePDAUserInterface();
|
||||
break;
|
||||
}
|
||||
case PDAToggleFlashlightMessage msg:
|
||||
case PDAToggleFlashlightMessage _:
|
||||
{
|
||||
ToggleLight();
|
||||
break;
|
||||
}
|
||||
|
||||
case PDAEjectIDMessage msg:
|
||||
case PDAEjectIDMessage _:
|
||||
{
|
||||
HandleIDEjection(message.Session.AttachedEntity!);
|
||||
break;
|
||||
}
|
||||
|
||||
case PDAEjectPenMessage _:
|
||||
{
|
||||
HandlePenEjection(message.Session.AttachedEntity!);
|
||||
break;
|
||||
}
|
||||
|
||||
case PDAUplinkBuyListingMessage buyMsg:
|
||||
{
|
||||
if (!_uplinkManager.TryPurchaseItem(_syndicateUplinkAccount, buyMsg.ItemId))
|
||||
@@ -131,11 +156,11 @@ namespace Content.Server.GameObjects.Components.PDA
|
||||
var accData = new UplinkAccountData(_syndicateUplinkAccount.AccountHolder,
|
||||
_syndicateUplinkAccount.Balance);
|
||||
var listings = _uplinkManager.FetchListings.Values.ToArray();
|
||||
UserInterface?.SetState(new PDAUpdateState(_lightOn, ownerInfo, accData, listings));
|
||||
UserInterface?.SetState(new PDAUpdateState(_lightOn, !PenSlotEmpty, ownerInfo, accData, listings));
|
||||
}
|
||||
else
|
||||
{
|
||||
UserInterface?.SetState(new PDAUpdateState(_lightOn, ownerInfo));
|
||||
UserInterface?.SetState(new PDAUpdateState(_lightOn, !PenSlotEmpty, ownerInfo));
|
||||
}
|
||||
|
||||
UpdatePDAAppearance();
|
||||
@@ -150,14 +175,11 @@ namespace Content.Server.GameObjects.Components.PDA
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
private bool TryInsertIdCard(InteractUsingEventArgs eventArgs, IdCardComponent idCardComponent)
|
||||
{
|
||||
var item = eventArgs.Using;
|
||||
|
||||
if (!item.TryGetComponent<IdCardComponent>(out var idCardComponent) || _idSlot.Contains(item))
|
||||
{
|
||||
if (_idSlot.Contains(item))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!eventArgs.User.TryGetComponent(out IHandsComponent? hands))
|
||||
{
|
||||
@@ -177,17 +199,70 @@ namespace Content.Server.GameObjects.Components.PDA
|
||||
return true;
|
||||
}
|
||||
|
||||
InsertIdCard(idCardComponent);
|
||||
|
||||
if (swap != null)
|
||||
{
|
||||
eventArgs.User.GetComponent<HandsComponent>().PutInHand(swap.GetComponent<ItemComponent>());
|
||||
hands.PutInHand(swap.GetComponent<ItemComponent>());
|
||||
}
|
||||
|
||||
InsertIdCard(idCardComponent);
|
||||
|
||||
UpdatePDAUserInterface();
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryInsertPen(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
var item = eventArgs.Using;
|
||||
if (_penSlot.Contains(item))
|
||||
return false;
|
||||
|
||||
if (!eventArgs.User.TryGetComponent(out IHandsComponent? hands))
|
||||
{
|
||||
Owner.PopupMessage(eventArgs.User, Loc.GetString("You have no hands!"));
|
||||
return true;
|
||||
}
|
||||
|
||||
IEntity? swap = null;
|
||||
if (!PenSlotEmpty)
|
||||
{
|
||||
// Swap
|
||||
swap = _penSlot.ContainedEntities[0];
|
||||
}
|
||||
|
||||
if (!hands.Drop(item))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (swap != null)
|
||||
{
|
||||
hands.PutInHand(swap.GetComponent<ItemComponent>());
|
||||
}
|
||||
|
||||
// Insert Pen
|
||||
_penSlot.Insert(item);
|
||||
|
||||
UpdatePDAUserInterface();
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
var item = eventArgs.Using;
|
||||
|
||||
if (item.TryGetComponent<IdCardComponent>(out var idCardComponent))
|
||||
{
|
||||
return TryInsertIdCard(eventArgs, idCardComponent);
|
||||
}
|
||||
|
||||
if (item.HasComponent<WriteComponent>())
|
||||
{
|
||||
return TryInsertPen(eventArgs);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent? actor))
|
||||
@@ -273,6 +348,21 @@ namespace Content.Server.GameObjects.Components.PDA
|
||||
UpdatePDAUserInterface();
|
||||
}
|
||||
|
||||
private void HandlePenEjection(IEntity pdaUser)
|
||||
{
|
||||
if (PenSlotEmpty)
|
||||
return;
|
||||
|
||||
var pen = _penSlot.ContainedEntities[0];
|
||||
_penSlot.Remove(pen);
|
||||
|
||||
var hands = pdaUser.GetComponent<HandsComponent>();
|
||||
var itemComponent = pen.GetComponent<ItemComponent>();
|
||||
hands.PutInHandOrDrop(itemComponent);
|
||||
|
||||
UpdatePDAUserInterface();
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class EjectIDVerb : Verb<PDAComponent>
|
||||
{
|
||||
@@ -294,6 +384,28 @@ namespace Content.Server.GameObjects.Components.PDA
|
||||
}
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class EjectPenVerb : Verb<PDAComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, PDAComponent component, VerbData data)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("Eject Pen");
|
||||
data.Visibility = component.PenSlotEmpty ? VerbVisibility.Invisible : VerbVisibility.Visible;
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, PDAComponent component)
|
||||
{
|
||||
component.HandlePenEjection(user);
|
||||
}
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class ToggleFlashlightVerb : Verb<PDAComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, PDAComponent component, VerbData data)
|
||||
|
||||
@@ -6,6 +6,8 @@ using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Rotatable
|
||||
{
|
||||
@@ -14,9 +16,21 @@ namespace Content.Server.GameObjects.Components.Rotatable
|
||||
{
|
||||
public override string Name => "Rotatable";
|
||||
|
||||
/// <summary>
|
||||
/// If true, this entity can be rotated even while anchored.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool RotateWhileAnchored { get; private set; }
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(this, x => x.RotateWhileAnchored, "rotateWhileAnchored", false);
|
||||
}
|
||||
|
||||
private void TryRotate(IEntity user, Angle angle)
|
||||
{
|
||||
if (Owner.TryGetComponent(out IPhysicsComponent physics))
|
||||
if (!RotateWhileAnchored && Owner.TryGetComponent(out IPhysicsComponent physics))
|
||||
{
|
||||
if (physics.Anchored)
|
||||
{
|
||||
@@ -33,7 +47,7 @@ namespace Content.Server.GameObjects.Components.Rotatable
|
||||
{
|
||||
protected override void GetData(IEntity user, RotatableComponent component, VerbData data)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user))
|
||||
if (!ActionBlockerSystem.CanInteract(user) || (!component.RotateWhileAnchored && component.Owner.TryGetComponent(out IPhysicsComponent physics) && physics.Anchored))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
@@ -55,7 +69,7 @@ namespace Content.Server.GameObjects.Components.Rotatable
|
||||
{
|
||||
protected override void GetData(IEntity user, RotatableComponent component, VerbData data)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user))
|
||||
if (!ActionBlockerSystem.CanInteract(user) || (!component.RotateWhileAnchored && component.Owner.TryGetComponent(out IPhysicsComponent physics) && physics.Anchored))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.Buckle;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.GameObjects.Components.Strap;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Verbs;
|
||||
@@ -27,7 +28,7 @@ namespace Content.Server.GameObjects.Components.Strap
|
||||
private StrapPosition _position;
|
||||
private string _buckleSound = null!;
|
||||
private string _unbuckleSound = null!;
|
||||
private string _buckledIcon = null!;
|
||||
private AlertType _buckledAlertType;
|
||||
|
||||
/// <summary>
|
||||
/// The angle in degrees to rotate the player by when they get strapped
|
||||
@@ -65,10 +66,10 @@ namespace Content.Server.GameObjects.Components.Strap
|
||||
public string UnbuckleSound => _unbuckleSound;
|
||||
|
||||
/// <summary>
|
||||
/// The icon to be displayed as a status when buckled
|
||||
/// ID of the alert to show when buckled
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string BuckledIcon => _buckledIcon;
|
||||
public AlertType BuckledAlertType => _buckledAlertType;
|
||||
|
||||
/// <summary>
|
||||
/// The sum of the sizes of all the buckled entities in this strap
|
||||
@@ -137,7 +138,7 @@ namespace Content.Server.GameObjects.Components.Strap
|
||||
serializer.DataField(ref _position, "position", StrapPosition.None);
|
||||
serializer.DataField(ref _buckleSound, "buckleSound", "/Audio/Effects/buckle.ogg");
|
||||
serializer.DataField(ref _unbuckleSound, "unbuckleSound", "/Audio/Effects/unbuckle.ogg");
|
||||
serializer.DataField(ref _buckledIcon, "buckledIcon", "/Textures/Interface/StatusEffects/Buckle/buckled.png");
|
||||
serializer.DataField(ref _buckledAlertType, "buckledAlertType", AlertType.Buckled);
|
||||
serializer.DataField(ref _rotation, "rotation", 0);
|
||||
|
||||
var defaultSize = 100;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
@@ -74,43 +75,43 @@ namespace Content.Server.GameObjects.Components.Temperature
|
||||
damageType = DamageType.Cold;
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent status))
|
||||
if (Owner.TryGetComponent(out ServerAlertsComponent status))
|
||||
{
|
||||
switch(CurrentTemperature)
|
||||
{
|
||||
// Cold strong.
|
||||
case var t when t <= 260:
|
||||
status.ChangeStatusEffect(StatusEffect.Temperature, "/Textures/Interface/StatusEffects/Temperature/cold3.png", null);
|
||||
status.ShowAlert(AlertType.Cold, 3);
|
||||
break;
|
||||
|
||||
// Cold mild.
|
||||
case var t when t <= 280 && t > 260:
|
||||
status.ChangeStatusEffect(StatusEffect.Temperature, "/Textures/Interface/StatusEffects/Temperature/cold2.png", null);
|
||||
status.ShowAlert(AlertType.Cold, 2);
|
||||
break;
|
||||
|
||||
// Cold weak.
|
||||
case var t when t <= 292 && t > 280:
|
||||
status.ChangeStatusEffect(StatusEffect.Temperature, "/Textures/Interface/StatusEffects/Temperature/cold1.png", null);
|
||||
status.ShowAlert(AlertType.Cold, 1);
|
||||
break;
|
||||
|
||||
// Safe.
|
||||
case var t when t <= 327 && t > 292:
|
||||
status.RemoveStatusEffect(StatusEffect.Temperature);
|
||||
status.ClearAlertCategory(AlertCategory.Temperature);
|
||||
break;
|
||||
|
||||
// Heat weak.
|
||||
case var t when t <= 335 && t > 327:
|
||||
status.ChangeStatusEffect(StatusEffect.Temperature, "/Textures/Interface/StatusEffects/Temperature/hot1.png", null);
|
||||
status.ShowAlert(AlertType.Hot, 1);
|
||||
break;
|
||||
|
||||
// Heat mild.
|
||||
case var t when t <= 345 && t > 335:
|
||||
status.ChangeStatusEffect(StatusEffect.Temperature, "/Textures/Interface/StatusEffects/Temperature/hot2.png", null);
|
||||
status.ShowAlert(AlertType.Hot, 2);
|
||||
break;
|
||||
|
||||
// Heat strong.
|
||||
case var t when t > 345:
|
||||
status.ChangeStatusEffect(StatusEffect.Temperature, "/Textures/Interface/StatusEffects/Temperature/hot3.png", null);
|
||||
status.ShowAlert(AlertType.Hot, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.Access;
|
||||
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.GameObjects.Components.VendingMachines;
|
||||
@@ -16,6 +17,7 @@ using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Timers;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -146,7 +148,7 @@ namespace Content.Server.GameObjects.Components.VendingMachines
|
||||
switch (message)
|
||||
{
|
||||
case VendingMachineEjectMessage msg:
|
||||
TryEject(msg.ID);
|
||||
TryEject(msg.ID, serverMsg.Session.AttachedEntity);
|
||||
break;
|
||||
case InventorySyncRequestMessage _:
|
||||
UserInterface?.SendMessage(new VendingMachineInventoryMessage(Inventory));
|
||||
@@ -195,6 +197,19 @@ namespace Content.Server.GameObjects.Components.VendingMachines
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity(_soundVend, Owner, AudioParams.Default.WithVolume(-2f));
|
||||
}
|
||||
|
||||
private void TryEject(string id, IEntity? sender)
|
||||
{
|
||||
if (Owner.TryGetComponent<AccessReader>(out var accessReader))
|
||||
{
|
||||
if (sender == null || !accessReader.IsAllowed(sender))
|
||||
{
|
||||
FlickDenyAnimation();
|
||||
return;
|
||||
}
|
||||
}
|
||||
TryEject(id);
|
||||
}
|
||||
|
||||
private void FlickDenyAnimation()
|
||||
{
|
||||
TrySetVisualState(VendingMachineVisualState.Deny);
|
||||
|
||||
Reference in New Issue
Block a user