Merge remote-tracking branch 'upstream/master' into 20-10-30-admins

This commit is contained in:
Pieter-Jan Briers
2020-11-10 16:59:17 +01:00
473 changed files with 5588 additions and 3584 deletions

View File

@@ -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)

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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))

View File

@@ -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)
{
}
}
}

View File

@@ -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;
}

View File

@@ -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
}
}

View File

@@ -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) { }
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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()

View File

@@ -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;
}

View File

@@ -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){

View File

@@ -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))
{

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}
}
}

View File

@@ -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))

View File

@@ -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))

View File

@@ -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))

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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>

View File

@@ -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 />

View File

@@ -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);
}
}
}
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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);