Merge branch 'master' into 2020-08-19-firelocks
# Conflicts: # Content.Server/GameObjects/Components/Atmos/AirtightComponent.cs # Content.Server/GameObjects/Components/Atmos/GridAtmosphereComponent.cs # SpaceStation14.sln.DotSettings
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.AI
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class AiFactionTagComponent : Component
|
||||
{
|
||||
public override string Name => "AiFactionTag";
|
||||
|
||||
public Faction Factions { get; private set; } = Faction.None;
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataReadWriteFunction(
|
||||
"factions",
|
||||
new List<Faction>(),
|
||||
factions => factions.ForEach(faction => Factions |= faction),
|
||||
() =>
|
||||
{
|
||||
var writeFactions = new List<Faction>();
|
||||
foreach (Faction fac in Enum.GetValues(typeof(Faction)))
|
||||
{
|
||||
if ((Factions & fac) != 0)
|
||||
{
|
||||
writeFactions.Add(fac);
|
||||
}
|
||||
}
|
||||
|
||||
return writeFactions;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum Faction
|
||||
{
|
||||
None = 0,
|
||||
NanoTransen = 1 << 0,
|
||||
SimpleHostile = 1 << 1,
|
||||
SimpleNeutral = 1 << 2,
|
||||
Syndicate = 1 << 3,
|
||||
Xeno = 1 << 4,
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Access;
|
||||
using Content.Shared.GameObjects.Components.Access;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
@@ -15,6 +17,7 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Access
|
||||
{
|
||||
@@ -22,16 +25,13 @@ namespace Content.Server.GameObjects.Components.Access
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
public class IdCardConsoleComponent : SharedIdCardConsoleComponent, IActivate
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager;
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
private BoundUserInterface _userInterface;
|
||||
private ContainerSlot _privilegedIdContainer;
|
||||
private ContainerSlot _targetIdContainer;
|
||||
private AccessReader _accessReader;
|
||||
private ContainerSlot _privilegedIdContainer = default!;
|
||||
private ContainerSlot _targetIdContainer = default!;
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(IdCardConsoleUiKey.Key);
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -40,16 +40,30 @@ namespace Content.Server.GameObjects.Components.Access
|
||||
_privilegedIdContainer = ContainerManagerComponent.Ensure<ContainerSlot>($"{Name}-privilegedId", Owner);
|
||||
_targetIdContainer = ContainerManagerComponent.Ensure<ContainerSlot>($"{Name}-targetId", Owner);
|
||||
|
||||
_accessReader = Owner.GetComponent<AccessReader>();
|
||||
if (!Owner.EnsureComponent(out AccessReader _))
|
||||
{
|
||||
Logger.Warning($"Entity {Owner} at {Owner.Transform.MapPosition} didn't have a {nameof(AccessReader)}");
|
||||
}
|
||||
|
||||
if (UserInterface == null)
|
||||
{
|
||||
Logger.Warning($"Entity {Owner} at {Owner.Transform.MapPosition} doesn't have a {nameof(ServerUserInterfaceComponent)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
}
|
||||
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(IdCardConsoleUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
UpdateUserInterface();
|
||||
}
|
||||
|
||||
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||
{
|
||||
if (obj.Session.AttachedEntity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (obj.Message)
|
||||
{
|
||||
case IdButtonPressedMessage msg:
|
||||
@@ -72,13 +86,19 @@ namespace Content.Server.GameObjects.Components.Access
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if there is an ID in <see cref="_privilegedIdContainer"/> and said ID satisfies the requirements of <see cref="_accessReader"/>.
|
||||
/// Returns true if there is an ID in <see cref="_privilegedIdContainer"/> and said ID satisfies the requirements of <see cref="AccessReader"/>.
|
||||
/// </summary>
|
||||
private bool PrivilegedIdIsAuthorized()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out AccessReader? reader))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var privilegedIdEntity = _privilegedIdContainer.ContainedEntity;
|
||||
return privilegedIdEntity != null && _accessReader.IsAllowed(privilegedIdEntity);
|
||||
return privilegedIdEntity != null && reader.IsAllowed(privilegedIdEntity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the "Submit" button in the UI gets pressed.
|
||||
/// Writes data passed from the UI into the ID stored in <see cref="_targetIdContainer"/>, if present.
|
||||
@@ -110,9 +130,9 @@ namespace Content.Server.GameObjects.Components.Access
|
||||
/// </summary>
|
||||
private void HandleId(IEntity user, ContainerSlot container)
|
||||
{
|
||||
if (!user.TryGetComponent(out IHandsComponent hands))
|
||||
if (!user.TryGetComponent(out IHandsComponent? hands))
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, user, _localizationManager.GetString("You have no hands."));
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, user, Loc.GetString("You have no hands."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -133,9 +153,15 @@ namespace Content.Server.GameObjects.Components.Access
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(!hands.Drop(hands.ActiveHand, container))
|
||||
|
||||
if (hands.ActiveHand == null)
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, user, _localizationManager.GetString("You can't let go of the ID card!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hands.Drop(hands.ActiveHand, container))
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, user, Loc.GetString("You can't let go of the ID card!"));
|
||||
return;
|
||||
}
|
||||
UpdateUserInterface();
|
||||
@@ -185,17 +211,17 @@ namespace Content.Server.GameObjects.Components.Access
|
||||
_privilegedIdContainer.ContainedEntity?.Name ?? "",
|
||||
_targetIdContainer.ContainedEntity?.Name ?? "");
|
||||
}
|
||||
_userInterface.SetState(newState);
|
||||
UserInterface?.SetState(newState);
|
||||
}
|
||||
|
||||
public void Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if(!eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
if(!eventArgs.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_userInterface.Open(actor.playerSession);
|
||||
UserInterface?.Open(actor.playerSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
|
||||
using Robust.Server.GameObjects;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Content.Server.GameObjects.EntitySystems.DoAfter;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Shared.GameObjects.Components.ActionBlocking;
|
||||
using Content.Shared.GameObjects.Verbs;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Robust.Shared.Log;
|
||||
using System.Linq;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Robust.Shared.Maths;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.ActionBlocking
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class CuffableComponent : SharedCuffableComponent
|
||||
{
|
||||
[Dependency]
|
||||
private readonly ISharedNotifyManager _notifyManager;
|
||||
|
||||
/// <summary>
|
||||
/// How many of this entity's hands are currently cuffed.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public int CuffedHandCount => _container.ContainedEntities.Count * 2;
|
||||
|
||||
protected IEntity LastAddedCuffs => _container.ContainedEntities[_container.ContainedEntities.Count - 1];
|
||||
|
||||
public IReadOnlyList<IEntity> StoredEntities => _container.ContainedEntities;
|
||||
|
||||
/// <summary>
|
||||
/// Container of various handcuffs currently applied to the entity.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
private Container _container = default!;
|
||||
|
||||
private float _interactRange;
|
||||
private IHandsComponent _hands;
|
||||
|
||||
public event Action OnCuffedStateChanged;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_container = ContainerManagerComponent.Ensure<Container>(Name, Owner);
|
||||
_interactRange = SharedInteractionSystem.InteractionRange / 2;
|
||||
|
||||
Owner.EntityManager.EventBus.SubscribeEvent<HandCountChangedEvent>(EventSource.Local, this, HandleHandCountChange);
|
||||
|
||||
if (!Owner.TryGetComponent(out _hands))
|
||||
{
|
||||
Logger.Warning("Player does not have an IHandsComponent!");
|
||||
}
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
// there are 2 approaches i can think of to handle the handcuff overlay on players
|
||||
// 1 - make the current RSI the handcuff type that's currently active. all handcuffs on the player will appear the same.
|
||||
// 2 - allow for several different player overlays for each different cuff type.
|
||||
// approach #2 would be more difficult/time consuming to do and the payoff doesn't make it worth it.
|
||||
// right now we're doing approach #1.
|
||||
|
||||
if (CuffedHandCount > 0)
|
||||
{
|
||||
if (LastAddedCuffs.TryGetComponent<HandcuffComponent>(out var cuffs))
|
||||
{
|
||||
return new CuffableComponentState(CuffedHandCount,
|
||||
CanStillInteract,
|
||||
cuffs.CuffedRSI,
|
||||
$"{cuffs.OverlayIconState}-{CuffedHandCount}",
|
||||
cuffs.Color);
|
||||
// the iconstate is formatted as blah-2, blah-4, blah-6, etc.
|
||||
// the number corresponds to how many hands are cuffed.
|
||||
}
|
||||
}
|
||||
|
||||
return new CuffableComponentState(CuffedHandCount,
|
||||
CanStillInteract,
|
||||
"/Objects/Misc/handcuffs.rsi",
|
||||
"body-overlay-2",
|
||||
Color.White);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a set of cuffs to an existing CuffedComponent.
|
||||
/// </summary>
|
||||
/// <param name="prototype"></param>
|
||||
public void AddNewCuffs(IEntity handcuff)
|
||||
{
|
||||
if (!handcuff.HasComponent<HandcuffComponent>())
|
||||
{
|
||||
Logger.Warning($"Handcuffs being applied to player are missing a {nameof(HandcuffComponent)}!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntitySystem.Get<SharedInteractionSystem>().InRangeUnobstructed(
|
||||
handcuff.Transform.MapPosition,
|
||||
Owner.Transform.MapPosition,
|
||||
_interactRange,
|
||||
ignoredEnt: Owner))
|
||||
{
|
||||
Logger.Warning("Handcuffs being applied to player are obstructed or too far away! This should not happen!");
|
||||
return;
|
||||
}
|
||||
|
||||
_container.Insert(handcuff);
|
||||
CanStillInteract = _hands.Hands.Count() > CuffedHandCount;
|
||||
|
||||
OnCuffedStateChanged.Invoke();
|
||||
UpdateStatusEffect();
|
||||
UpdateHeldItems();
|
||||
Dirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check the current amount of hands the owner has, and if there's less hands than active cuffs we remove some cuffs.
|
||||
/// </summary>
|
||||
private void UpdateHandCount()
|
||||
{
|
||||
var dirty = false;
|
||||
var handCount = _hands.Hands.Count();
|
||||
|
||||
while (CuffedHandCount > handCount && CuffedHandCount > 0)
|
||||
{
|
||||
dirty = true;
|
||||
|
||||
var entity = _container.ContainedEntities[_container.ContainedEntities.Count - 1];
|
||||
_container.Remove(entity);
|
||||
entity.Transform.WorldPosition = Owner.Transform.GridPosition.Position;
|
||||
}
|
||||
|
||||
if (dirty)
|
||||
{
|
||||
CanStillInteract = handCount > CuffedHandCount;
|
||||
OnCuffedStateChanged.Invoke();
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleHandCountChange(HandCountChangedEvent message)
|
||||
{
|
||||
if (message.Sender == Owner)
|
||||
{
|
||||
UpdateHandCount();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check how many items the user is holding and if it's more than the number of cuffed hands, drop some items.
|
||||
/// </summary>
|
||||
public void UpdateHeldItems()
|
||||
{
|
||||
var itemCount = _hands.GetAllHeldItems().Count();
|
||||
var freeHandCount = _hands.Hands.Count() - CuffedHandCount;
|
||||
|
||||
if (freeHandCount < itemCount)
|
||||
{
|
||||
foreach (ItemComponent item in _hands.GetAllHeldItems())
|
||||
{
|
||||
if (freeHandCount < itemCount)
|
||||
{
|
||||
freeHandCount++;
|
||||
_hands.Drop(item.Owner);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the status effect indicator on the HUD.
|
||||
/// </summary>
|
||||
private void UpdateStatusEffect()
|
||||
{
|
||||
if (Owner.TryGetComponent(out ServerStatusEffectsComponent status))
|
||||
{
|
||||
status.ChangeStatusEffectIcon(StatusEffect.Cuffed,
|
||||
CanStillInteract ? "/Textures/Interface/StatusEffects/Handcuffed/Uncuffed.png" : "/Textures/Interface/StatusEffects/Handcuffed/Handcuffed.png");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to uncuff a cuffed entity. Can be called by the cuffed entity, or another entity trying to help uncuff them.
|
||||
/// If the uncuffing succeeds, the cuffs will drop on the floor.
|
||||
/// </summary>
|
||||
/// <param name="user">The cuffed entity</param>
|
||||
/// <param name="cuffsToRemove">Optional param for the handcuff entity to remove from the cuffed entity. If null, uses the most recently added handcuff entity.</param>
|
||||
public async void TryUncuff(IEntity user, IEntity cuffsToRemove = null)
|
||||
{
|
||||
var isOwner = user == Owner;
|
||||
|
||||
if (cuffsToRemove == null)
|
||||
{
|
||||
cuffsToRemove = LastAddedCuffs;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_container.ContainedEntities.Contains(cuffsToRemove))
|
||||
{
|
||||
Logger.Warning("A user is trying to remove handcuffs that aren't in the owner's container. This should never happen!");
|
||||
}
|
||||
}
|
||||
|
||||
if (!cuffsToRemove.TryGetComponent<HandcuffComponent>(out var cuff))
|
||||
{
|
||||
Logger.Warning($"A user is trying to remove handcuffs without a {nameof(HandcuffComponent)}. This should never happen!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isOwner && !ActionBlockerSystem.CanInteract(user))
|
||||
{
|
||||
user.PopupMessage(user, Loc.GetString("You can't do that!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isOwner &&
|
||||
!EntitySystem.Get<SharedInteractionSystem>().InRangeUnobstructed(
|
||||
user.Transform.MapPosition,
|
||||
Owner.Transform.MapPosition,
|
||||
_interactRange,
|
||||
ignoredEnt: Owner))
|
||||
{
|
||||
user.PopupMessage(user, Loc.GetString("You are too far away to remove the cuffs."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntitySystem.Get<SharedInteractionSystem>().InRangeUnobstructed(
|
||||
cuffsToRemove.Transform.MapPosition,
|
||||
Owner.Transform.MapPosition,
|
||||
_interactRange,
|
||||
ignoredEnt: Owner))
|
||||
{
|
||||
Logger.Warning("Handcuffs being removed from player are obstructed or too far away! This should not happen!");
|
||||
return;
|
||||
}
|
||||
|
||||
user.PopupMessage(user, Loc.GetString("You start removing the cuffs."));
|
||||
|
||||
var audio = EntitySystem.Get<AudioSystem>();
|
||||
audio.PlayFromEntity(isOwner ? cuff.StartBreakoutSound : cuff.StartUncuffSound, Owner);
|
||||
|
||||
var uncuffTime = isOwner ? cuff.BreakoutTime : cuff.UncuffTime;
|
||||
var doAfterEventArgs = new DoAfterEventArgs(user, uncuffTime)
|
||||
{
|
||||
BreakOnUserMove = true,
|
||||
BreakOnDamage = true,
|
||||
BreakOnStun = true,
|
||||
NeedHand = true
|
||||
};
|
||||
|
||||
var doAfterSystem = EntitySystem.Get<DoAfterSystem>();
|
||||
var result = await doAfterSystem.DoAfter(doAfterEventArgs);
|
||||
|
||||
if (result != DoAfterStatus.Cancelled)
|
||||
{
|
||||
audio.PlayFromEntity(cuff.EndUncuffSound, Owner);
|
||||
|
||||
_container.ForceRemove(cuffsToRemove);
|
||||
cuffsToRemove.Transform.AttachToGridOrMap();
|
||||
cuffsToRemove.Transform.WorldPosition = Owner.Transform.WorldPosition;
|
||||
|
||||
if (cuff.BreakOnRemove)
|
||||
{
|
||||
cuff.Broken = true;
|
||||
|
||||
cuffsToRemove.Name = cuff.BrokenName;
|
||||
cuffsToRemove.Description = cuff.BrokenDesc;
|
||||
|
||||
if (cuffsToRemove.TryGetComponent<SpriteComponent>(out var sprite))
|
||||
{
|
||||
sprite.LayerSetState(0, cuff.BrokenState); // TODO: safety check to see if RSI contains the state?
|
||||
}
|
||||
}
|
||||
|
||||
CanStillInteract = _hands.Hands.Count() > CuffedHandCount;
|
||||
OnCuffedStateChanged.Invoke();
|
||||
UpdateStatusEffect();
|
||||
Dirty();
|
||||
|
||||
if (CuffedHandCount == 0)
|
||||
{
|
||||
_notifyManager.PopupMessage(user, user, Loc.GetString("You successfully remove the cuffs."));
|
||||
|
||||
if (!isOwner)
|
||||
{
|
||||
_notifyManager.PopupMessage(user, Owner, Loc.GetString("{0:theName} uncuffs your hands.", user));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isOwner)
|
||||
{
|
||||
_notifyManager.PopupMessage(user, user, Loc.GetString("You successfully remove the cuffs. {0} of {1:theName}'s hands remain cuffed.", CuffedHandCount, user));
|
||||
_notifyManager.PopupMessage(user, Owner, Loc.GetString("{0:theName} removes your cuffs. {1} of your hands remain cuffed.", user, CuffedHandCount));
|
||||
}
|
||||
else
|
||||
{
|
||||
_notifyManager.PopupMessage(user, user, Loc.GetString("You successfully remove the cuffs. {0} of your hands remain cuffed.", CuffedHandCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_notifyManager.PopupMessage(user, user, Loc.GetString("You fail to remove the cuffs."));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows the uncuffing of a cuffed person. Used by other people and by the component owner to break out of cuffs.
|
||||
/// </summary>
|
||||
[Verb]
|
||||
private sealed class UncuffVerb : Verb<CuffableComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, CuffableComponent component, VerbData data)
|
||||
{
|
||||
if ((user != component.Owner && !ActionBlockerSystem.CanInteract(user)) || component.CuffedHandCount == 0)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("Uncuff");
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, CuffableComponent component)
|
||||
{
|
||||
if (component.CuffedHandCount > 0)
|
||||
{
|
||||
component.TryUncuff(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
using Content.Server.GameObjects.EntitySystems.DoAfter;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Components.ActionBlocking;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Robust.Shared.Maths;
|
||||
using System;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.ActionBlocking
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class HandcuffComponent : SharedHandcuffComponent, IAfterInteract
|
||||
{
|
||||
[Dependency]
|
||||
private readonly ISharedNotifyManager _notifyManager;
|
||||
|
||||
/// <summary>
|
||||
/// The time it takes to apply a <see cref="CuffedComponent"/> to an entity.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public float CuffTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The time it takes to remove a <see cref="CuffedComponent"/> from an entity.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public float UncuffTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The time it takes for a cuffed entity to remove <see cref="CuffedComponent"/> from itself.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public float BreakoutTime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If an entity being cuffed is stunned, this amount of time is subtracted from the time it takes to add/remove their cuffs.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public float StunBonus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Will the cuffs break when removed?
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool BreakOnRemove { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The path of the RSI file used for the player cuffed overlay.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string CuffedRSI { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The iconstate used with the RSI file for the player cuffed overlay.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string OverlayIconState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The iconstate used for broken handcuffs
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string BrokenState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The iconstate used for broken handcuffs
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string BrokenName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The iconstate used for broken handcuffs
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string BrokenDesc { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
public bool Broken
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isBroken;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_isBroken != value)
|
||||
{
|
||||
_isBroken = value;
|
||||
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string StartCuffSound { get; set; }
|
||||
public string EndCuffSound { get; set; }
|
||||
public string StartBreakoutSound { get; set; }
|
||||
public string StartUncuffSound { get; set; }
|
||||
public string EndUncuffSound { get; set; }
|
||||
public Color Color { get; set; }
|
||||
|
||||
// Non-exposed data fields
|
||||
private bool _isBroken = false;
|
||||
private float _interactRange;
|
||||
private DoAfterSystem _doAfterSystem;
|
||||
private AudioSystem _audioSystem;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_audioSystem = EntitySystem.Get<AudioSystem>();
|
||||
_doAfterSystem = EntitySystem.Get<DoAfterSystem>();
|
||||
_interactRange = SharedInteractionSystem.InteractionRange / 2;
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(this, x => x.CuffTime, "cuffTime", 5.0f);
|
||||
serializer.DataField(this, x => x.BreakoutTime, "breakoutTime", 30.0f);
|
||||
serializer.DataField(this, x => x.UncuffTime, "uncuffTime", 5.0f);
|
||||
serializer.DataField(this, x => x.StunBonus, "stunBonus", 2.0f);
|
||||
serializer.DataField(this, x => x.StartCuffSound, "startCuffSound", "/Audio/Items/Handcuffs/cuff_start.ogg");
|
||||
serializer.DataField(this, x => x.EndCuffSound, "endCuffSound", "/Audio/Items/Handcuffs/cuff_end.ogg");
|
||||
serializer.DataField(this, x => x.StartUncuffSound, "startUncuffSound", "/Audio/Items/Handcuffs/cuff_takeoff_start.ogg");
|
||||
serializer.DataField(this, x => x.EndUncuffSound, "endUncuffSound", "/Audio/Items/Handcuffs/cuff_takeoff_end.ogg");
|
||||
serializer.DataField(this, x => x.StartBreakoutSound, "startBreakoutSound", "/Audio/Items/Handcuffs/cuff_breakout_start.ogg");
|
||||
serializer.DataField(this, x => x.CuffedRSI, "cuffedRSI", "Objects/Misc/handcuffs.rsi");
|
||||
serializer.DataField(this, x => x.OverlayIconState, "bodyIconState", "body-overlay");
|
||||
serializer.DataField(this, x => x.Color, "color", Color.White);
|
||||
serializer.DataField(this, x => x.BreakOnRemove, "breakOnRemove", false);
|
||||
serializer.DataField(this, x => x.BrokenState, "brokenIconState", string.Empty);
|
||||
serializer.DataField(this, x => x.BrokenName, "brokenName", string.Empty);
|
||||
serializer.DataField(this, x => x.BrokenDesc, "brokenDesc", string.Empty);
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new HandcuffedComponentState(Broken ? BrokenState : string.Empty);
|
||||
}
|
||||
|
||||
void IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Target == null || !ActionBlockerSystem.CanUse(eventArgs.User) || !eventArgs.Target.TryGetComponent<CuffableComponent>(out var cuffed))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventArgs.Target == eventArgs.User)
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't cuff yourself!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Broken)
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("The cuffs are broken!"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!eventArgs.Target.TryGetComponent<HandsComponent>(out var hands))
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("{0:theName} has no hands!", eventArgs.Target));
|
||||
return;
|
||||
}
|
||||
|
||||
if (cuffed.CuffedHandCount == hands.Count)
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("{0:theName} has no free hands to handcuff!", eventArgs.Target));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntitySystem.Get<SharedInteractionSystem>().InRangeUnobstructed(
|
||||
eventArgs.User.Transform.MapPosition,
|
||||
eventArgs.Target.Transform.MapPosition,
|
||||
_interactRange,
|
||||
ignoredEnt: Owner))
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You are too far away to use the cuffs!"));
|
||||
return;
|
||||
}
|
||||
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You start cuffing {0:theName}.", eventArgs.Target));
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.Target, Loc.GetString("{0:theName} starts cuffing you!", eventArgs.User));
|
||||
_audioSystem.PlayFromEntity(StartCuffSound, Owner);
|
||||
|
||||
TryUpdateCuff(eventArgs.User, eventArgs.Target, cuffed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the cuffed state of an entity
|
||||
/// </summary>
|
||||
private async void TryUpdateCuff(IEntity user, IEntity target, CuffableComponent cuffs)
|
||||
{
|
||||
var cuffTime = CuffTime;
|
||||
|
||||
if (target.TryGetComponent<StunnableComponent>(out var stun) && stun.Stunned)
|
||||
{
|
||||
cuffTime = MathF.Max(0.1f, cuffTime - StunBonus);
|
||||
}
|
||||
|
||||
var doAfterEventArgs = new DoAfterEventArgs(user, cuffTime, default, target)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnDamage = true,
|
||||
BreakOnStun = true,
|
||||
NeedHand = true
|
||||
};
|
||||
|
||||
var result = await _doAfterSystem.DoAfter(doAfterEventArgs);
|
||||
|
||||
if (result != DoAfterStatus.Cancelled)
|
||||
{
|
||||
_audioSystem.PlayFromEntity(EndCuffSound, Owner);
|
||||
_notifyManager.PopupMessage(user, user, Loc.GetString("You successfully cuff {0:theName}.", target));
|
||||
_notifyManager.PopupMessage(target, target, Loc.GetString("You have been cuffed by {0:theName}!", user));
|
||||
|
||||
if (user.TryGetComponent<HandsComponent>(out var hands))
|
||||
{
|
||||
hands.Drop(Owner);
|
||||
cuffs.AddNewCuffs(Owner);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning("Unable to remove handcuffs from player's hands! This should not happen!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
user.PopupMessage(user, Loc.GetString("You were interrupted while cuffing {0:theName}!", target));
|
||||
target.PopupMessage(target, Loc.GetString("You interrupt {0:theName} while they are cuffing you!", user));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.Transform;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -13,7 +16,8 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
[RegisterComponent]
|
||||
public class AirtightComponent : Component, IMapInit
|
||||
{
|
||||
private SnapGridComponent _snapGrid;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
private (GridId, MapIndices) _lastPosition;
|
||||
|
||||
public override string Name => "Airtight";
|
||||
@@ -28,7 +32,11 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
set
|
||||
{
|
||||
_airBlocked = value;
|
||||
EntitySystem.Get<AtmosphereSystem>().GetGridAtmosphere(Owner.Transform.GridID)?.Revalidate(_snapGrid.Position);
|
||||
|
||||
if (Owner.TryGetComponent(out SnapGridComponent? snapGrid))
|
||||
{
|
||||
EntitySystem.Get<AtmosphereSystem>().GetGridAtmosphere(Owner.Transform.GridID)?.Invalidate(snapGrid.Position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,19 +56,23 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
base.Initialize();
|
||||
|
||||
// Using the SnapGrid is critical for the performance of the room builder, and thus if
|
||||
// it is absent the component will not be airtight. An exception is much easier to track
|
||||
// down than the object magically not being airtight, so throw one if the SnapGrid component
|
||||
// it is absent the component will not be airtight. A warning is much easier to track
|
||||
// down than the object magically not being airtight, so log one if the SnapGrid component
|
||||
// is missing.
|
||||
if (!Owner.TryGetComponent(out _snapGrid))
|
||||
throw new Exception("Airtight entities must have a SnapGrid component");
|
||||
if (!Owner.EnsureComponent(out SnapGridComponent _))
|
||||
Logger.Warning($"Entity {Owner} at {Owner.Transform.MapPosition.ToString()} didn't have a {nameof(SnapGridComponent)}");
|
||||
|
||||
UpdatePosition();
|
||||
}
|
||||
|
||||
public void MapInit()
|
||||
{
|
||||
_snapGrid.OnPositionChanged += OnTransformMove;
|
||||
_lastPosition = (Owner.Transform.GridID, _snapGrid.Position);
|
||||
if (Owner.TryGetComponent(out SnapGridComponent? snapGrid))
|
||||
{
|
||||
snapGrid.OnPositionChanged += OnTransformMove;
|
||||
_lastPosition = (Owner.Transform.GridID, snapGrid.Position);
|
||||
}
|
||||
|
||||
UpdatePosition();
|
||||
}
|
||||
|
||||
@@ -70,11 +82,16 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
|
||||
_airBlocked = false;
|
||||
|
||||
_snapGrid.OnPositionChanged -= OnTransformMove;
|
||||
if (Owner.TryGetComponent(out SnapGridComponent? snapGrid))
|
||||
{
|
||||
snapGrid.OnPositionChanged -= OnTransformMove;
|
||||
}
|
||||
|
||||
if(_fixVacuum)
|
||||
EntitySystem.Get<AtmosphereSystem>().GetGridAtmosphere(Owner.Transform.GridID)?
|
||||
.FixVacuum(_snapGrid.Position);
|
||||
if (_fixVacuum)
|
||||
{
|
||||
var mapIndices = Owner.Transform.GridPosition.ToMapIndices(_mapManager);
|
||||
EntitySystem.Get<AtmosphereSystem>().GetGridAtmosphere(Owner.Transform.GridID)?.FixVacuum(mapIndices);
|
||||
}
|
||||
|
||||
UpdatePosition();
|
||||
}
|
||||
@@ -83,15 +100,22 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
{
|
||||
UpdatePosition(_lastPosition.Item1, _lastPosition.Item2);
|
||||
UpdatePosition();
|
||||
_lastPosition = (Owner.Transform.GridID, _snapGrid.Position);
|
||||
|
||||
if (Owner.TryGetComponent(out SnapGridComponent? snapGrid))
|
||||
{
|
||||
_lastPosition = (Owner.Transform.GridID, snapGrid.Position);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePosition() => UpdatePosition(Owner.Transform.GridID, _snapGrid.Position);
|
||||
private void UpdatePosition()
|
||||
{
|
||||
var mapIndices = Owner.Transform.GridPosition.ToMapIndices(_mapManager);
|
||||
UpdatePosition(Owner.Transform.GridID, mapIndices);
|
||||
}
|
||||
|
||||
private void UpdatePosition(GridId gridId, MapIndices pos)
|
||||
{
|
||||
EntitySystem.Get<AtmosphereSystem>().GetGridAtmosphere(gridId)?.Invalidate(pos);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
@@ -16,30 +17,32 @@ using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Atmos
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class GasAnalyzerComponent : SharedGasAnalyzerComponent, IAfterInteract, IDropped, IUse
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IServerNotifyManager _notifyManager = default!;
|
||||
[Dependency] private IMapManager _mapManager = default!;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
private BoundUserInterface _userInterface = default!;
|
||||
private GasAnalyzerDanger _pressureDanger;
|
||||
private float _timeSinceSync;
|
||||
private const float TimeBetweenSyncs = 2f;
|
||||
private bool _checkPlayer = false; // Check at the player pos or at some other tile?
|
||||
private GridCoordinates? _position; // The tile that we scanned
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(GasAnalyzerUiKey.Key);
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(GasAnalyzerUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
@@ -56,7 +59,7 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
{
|
||||
_checkPlayer = true;
|
||||
_position = null;
|
||||
_userInterface.Open(session);
|
||||
UserInterface?.Open(session);
|
||||
UpdateUserInterface();
|
||||
Resync();
|
||||
}
|
||||
@@ -71,7 +74,7 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
{
|
||||
_checkPlayer = false;
|
||||
_position = pos;
|
||||
_userInterface.Open(session);
|
||||
UserInterface?.Open(session);
|
||||
UpdateUserInterface();
|
||||
Resync();
|
||||
}
|
||||
@@ -79,7 +82,7 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
public void CloseInterface(IPlayerSession session)
|
||||
{
|
||||
_position = null;
|
||||
_userInterface.Close(session);
|
||||
UserInterface?.Close(session);
|
||||
Resync();
|
||||
}
|
||||
|
||||
@@ -123,10 +126,15 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
|
||||
private void UpdateUserInterface()
|
||||
{
|
||||
if (UserInterface == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string? error = null;
|
||||
|
||||
// Check if the player is still holding the gas analyzer => if not, don't update
|
||||
foreach (var session in _userInterface.SubscribedSessions)
|
||||
foreach (var session in UserInterface.SubscribedSessions)
|
||||
{
|
||||
if (session.AttachedEntity == null)
|
||||
return;
|
||||
@@ -151,12 +159,13 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
pos = _position.Value;
|
||||
}
|
||||
|
||||
var gam = EntitySystem.Get<AtmosphereSystem>().GetGridAtmosphere(pos.GridID);
|
||||
var atmosSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
var gam = atmosSystem.GetGridAtmosphere(pos.GridID);
|
||||
var tile = gam?.GetTile(pos).Air;
|
||||
if (tile == null)
|
||||
{
|
||||
error = "No Atmosphere!";
|
||||
_userInterface.SetState(
|
||||
UserInterface.SetState(
|
||||
new GasAnalyzerBoundUserInterfaceState(
|
||||
0,
|
||||
0,
|
||||
@@ -166,16 +175,17 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
}
|
||||
|
||||
var gases = new List<GasEntry>();
|
||||
for (int i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||
|
||||
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||
{
|
||||
var gas = Atmospherics.GetGas(i);
|
||||
var gas = atmosSystem.GetGas(i);
|
||||
|
||||
if (tile.Gases[i] <= Atmospherics.GasMinMoles) continue;
|
||||
|
||||
gases.Add(new GasEntry(gas.Name, tile.Gases[i], gas.Color));
|
||||
}
|
||||
|
||||
_userInterface.SetState(
|
||||
UserInterface.SetState(
|
||||
new GasAnalyzerBoundUserInterfaceState(
|
||||
tile.Pressure,
|
||||
tile.Temperature,
|
||||
|
||||
@@ -6,16 +6,23 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Content.Server.GameObjects.Components.Atmos
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class GasMixtureComponent : Component
|
||||
public class GasMixtureHolderComponent : Component
|
||||
{
|
||||
public override string Name => "GasMixture";
|
||||
public override string Name => "GasMixtureHolder";
|
||||
|
||||
[ViewVariables] public GasMixture GasMixture { get; set; } = new GasMixture();
|
||||
[ViewVariables] public GasMixture GasMixture { get; set; }
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(this, x => GasMixture.Volume, "volume", 0f);
|
||||
|
||||
GasMixture = new GasMixture();
|
||||
|
||||
serializer.DataReadWriteFunction(
|
||||
"volume",
|
||||
0f,
|
||||
vol => GasMixture.Volume = vol,
|
||||
() => GasMixture.Volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.GameObjects.Components.Atmos.Piping;
|
||||
using Content.Server.GameObjects.Components.NodeContainer.NodeGroups;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
@@ -33,7 +36,7 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
/// <summary>
|
||||
/// Check current execution time every n instances processed.
|
||||
/// </summary>
|
||||
private const int LagCheckIterations = 15;
|
||||
private const int LagCheckIterations = 30;
|
||||
|
||||
/// <summary>
|
||||
/// Max milliseconds allowed for atmos updates.
|
||||
@@ -47,32 +50,94 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
|
||||
public override string Name => "GridAtmosphere";
|
||||
|
||||
private bool _paused = false;
|
||||
private float _timer = 0f;
|
||||
private Stopwatch _stopwatch = new Stopwatch();
|
||||
|
||||
[ViewVariables]
|
||||
public int UpdateCounter { get; private set; } = 0;
|
||||
private IMapGrid _grid;
|
||||
|
||||
[ViewVariables]
|
||||
private double _tileEqualizeLastProcess;
|
||||
|
||||
[ViewVariables]
|
||||
private readonly HashSet<ExcitedGroup> _excitedGroups = new HashSet<ExcitedGroup>(1000);
|
||||
|
||||
[ViewVariables]
|
||||
private int ExcitedGroupCount => _excitedGroups.Count;
|
||||
|
||||
[ViewVariables]
|
||||
private double _excitedGroupLastProcess;
|
||||
|
||||
[ViewVariables]
|
||||
private readonly Dictionary<MapIndices, TileAtmosphere> _tiles = new Dictionary<MapIndices, TileAtmosphere>(1000);
|
||||
|
||||
[ViewVariables]
|
||||
private readonly HashSet<TileAtmosphere> _activeTiles = new HashSet<TileAtmosphere>(1000);
|
||||
|
||||
[ViewVariables]
|
||||
private int ActiveTilesCount => _activeTiles.Count;
|
||||
|
||||
[ViewVariables]
|
||||
private double _activeTilesLastProcess;
|
||||
|
||||
[ViewVariables]
|
||||
private readonly HashSet<TileAtmosphere> _hotspotTiles = new HashSet<TileAtmosphere>(1000);
|
||||
|
||||
[ViewVariables]
|
||||
private int HotspotTilesCount => _hotspotTiles.Count;
|
||||
|
||||
[ViewVariables]
|
||||
private double _hotspotsLastProcess;
|
||||
|
||||
[ViewVariables]
|
||||
private readonly HashSet<TileAtmosphere> _superconductivityTiles = new HashSet<TileAtmosphere>(1000);
|
||||
|
||||
[ViewVariables]
|
||||
private int SuperconductivityTilesCount => _superconductivityTiles.Count;
|
||||
|
||||
[ViewVariables]
|
||||
private double _superconductivityLastProcess;
|
||||
|
||||
[ViewVariables]
|
||||
private readonly HashSet<MapIndices> _invalidatedCoords = new HashSet<MapIndices>(1000);
|
||||
|
||||
[ViewVariables]
|
||||
private int InvalidatedCoordsCount => _invalidatedCoords.Count;
|
||||
|
||||
[ViewVariables]
|
||||
private HashSet<TileAtmosphere> _highPressureDelta = new HashSet<TileAtmosphere>(1000);
|
||||
|
||||
[ViewVariables]
|
||||
private int HighPressureDeltaCount => _highPressureDelta.Count;
|
||||
|
||||
[ViewVariables]
|
||||
private double _highPressureDeltaLastProcess;
|
||||
|
||||
[ViewVariables]
|
||||
private readonly HashSet<IPipeNet> _pipeNets = new HashSet<IPipeNet>();
|
||||
|
||||
[ViewVariables]
|
||||
private double _pipeNetLastProcess;
|
||||
|
||||
[ViewVariables]
|
||||
private readonly HashSet<PipeNetDeviceComponent> _pipeNetDevices = new HashSet<PipeNetDeviceComponent>();
|
||||
|
||||
[ViewVariables]
|
||||
private double _pipeNetDevicesLastProcess;
|
||||
|
||||
[ViewVariables]
|
||||
private Queue<TileAtmosphere> _currentRunTiles = new Queue<TileAtmosphere>();
|
||||
|
||||
[ViewVariables]
|
||||
private Queue<ExcitedGroup> _currentRunExcitedGroups = new Queue<ExcitedGroup>();
|
||||
|
||||
[ViewVariables]
|
||||
private Queue<IPipeNet> _currentRunPipeNet = new Queue<IPipeNet>();
|
||||
|
||||
[ViewVariables]
|
||||
private Queue<PipeNetDeviceComponent> _currentRunPipeNetDevice = new Queue<PipeNetDeviceComponent>();
|
||||
|
||||
[ViewVariables]
|
||||
private ProcessState _state = ProcessState.TileEqualize;
|
||||
|
||||
@@ -84,47 +149,47 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
HighPressureDelta,
|
||||
Hotspots,
|
||||
Superconductivity,
|
||||
PipeNet,
|
||||
PipeNetDevices,
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void PryTile(MapIndices indices)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out IMapGridComponent? mapGridComponent)) return;
|
||||
if (IsSpace(indices) || IsAirBlocked(indices)) return;
|
||||
|
||||
var tile = _grid.GetTileRef(indices).Tile;
|
||||
var mapGrid = mapGridComponent.Grid;
|
||||
var tile = mapGrid.GetTileRef(indices).Tile;
|
||||
|
||||
var tileDefinitionManager = IoCManager.Resolve<ITileDefinitionManager>();
|
||||
var tileDef = (ContentTileDefinition)tileDefinitionManager[tile.TypeId];
|
||||
|
||||
var underplating = tileDefinitionManager["underplating"];
|
||||
_grid.SetTile(indices, new Tile(underplating.TileId));
|
||||
mapGrid.SetTile(indices, new Tile(underplating.TileId));
|
||||
|
||||
//Actually spawn the relevant tile item at the right position and give it some offset to the corner.
|
||||
var tileItem = IoCManager.Resolve<IServerEntityManager>().SpawnEntity(tileDef.ItemDropPrototypeName, new GridCoordinates(indices.X, indices.Y, _grid));
|
||||
var tileItem = IoCManager.Resolve<IServerEntityManager>().SpawnEntity(tileDef.ItemDropPrototypeName, new GridCoordinates(indices.X, indices.Y, mapGrid));
|
||||
tileItem.Transform.WorldPosition += (0.2f, 0.2f);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_grid = Owner.GetComponent<IMapGridComponent>().Grid;
|
||||
|
||||
RepopulateTiles();
|
||||
}
|
||||
|
||||
public override void OnAdd()
|
||||
{
|
||||
base.OnAdd();
|
||||
|
||||
_grid = Owner.GetComponent<IMapGridComponent>().Grid;
|
||||
|
||||
RepopulateTiles();
|
||||
}
|
||||
|
||||
public void RepopulateTiles()
|
||||
{
|
||||
foreach (var tile in _grid.GetAllTiles())
|
||||
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return;
|
||||
|
||||
foreach (var tile in mapGrid.Grid.GetAllTiles())
|
||||
{
|
||||
if(!_tiles.ContainsKey(tile.GridIndices))
|
||||
_tiles.Add(tile.GridIndices, new TileAtmosphere(this, tile.GridIndex, tile.GridIndices, new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C}));
|
||||
@@ -145,69 +210,66 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
|
||||
private void Revalidate()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return;
|
||||
|
||||
foreach (var indices in _invalidatedCoords.ToArray())
|
||||
{
|
||||
Revalidate(indices);
|
||||
var tile = GetTile(indices);
|
||||
AddActiveTile(tile);
|
||||
|
||||
if (tile == null)
|
||||
{
|
||||
tile = new TileAtmosphere(this, mapGrid.Grid.Index, indices, new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C});
|
||||
_tiles[indices] = tile;
|
||||
}
|
||||
|
||||
if (IsSpace(indices))
|
||||
{
|
||||
tile.Air = new GasMixture(GetVolumeForCells(1));
|
||||
tile.Air.MarkImmutable();
|
||||
_tiles[indices] = tile;
|
||||
|
||||
} else if (IsAirBlocked(indices))
|
||||
{
|
||||
tile.Air = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var obs = GetObstructingComponent(indices);
|
||||
|
||||
if (obs != null)
|
||||
{
|
||||
if (tile.Air == null && obs.FixVacuum)
|
||||
{
|
||||
FixVacuum(tile.GridIndices);
|
||||
}
|
||||
}
|
||||
|
||||
tile.Air ??= new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C};
|
||||
}
|
||||
|
||||
tile.UpdateAdjacent();
|
||||
tile.UpdateVisuals();
|
||||
|
||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << i);
|
||||
var otherIndices = indices.Offset(direction.ToDirection());
|
||||
var otherTile = GetTile(otherIndices);
|
||||
AddActiveTile(otherTile);
|
||||
otherTile?.UpdateAdjacent(direction.GetOpposite());
|
||||
}
|
||||
}
|
||||
|
||||
_invalidatedCoords.Clear();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Revalidate(MapIndices indices)
|
||||
{
|
||||
var tile = GetTile(indices);
|
||||
AddActiveTile(tile);
|
||||
|
||||
if (tile == null)
|
||||
{
|
||||
tile = new TileAtmosphere(this, _grid.Index, indices, new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C});
|
||||
_tiles[indices] = tile;
|
||||
}
|
||||
|
||||
if (IsSpace(indices))
|
||||
{
|
||||
tile.Air = new GasMixture(GetVolumeForCells(1));
|
||||
tile.Air.MarkImmutable();
|
||||
_tiles[indices] = tile;
|
||||
|
||||
} else if (IsAirBlocked(indices))
|
||||
{
|
||||
tile.Air = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var obs = GetObstructingComponent(indices);
|
||||
|
||||
if (obs != null)
|
||||
{
|
||||
if (tile.Air == null && obs.FixVacuum)
|
||||
{
|
||||
FixVacuum(tile.GridIndices);
|
||||
}
|
||||
}
|
||||
|
||||
tile.Air ??= new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C};
|
||||
}
|
||||
|
||||
tile.UpdateAdjacent();
|
||||
tile.UpdateVisuals();
|
||||
|
||||
foreach (var direction in Cardinal)
|
||||
{
|
||||
var otherIndices = indices.Offset(direction);
|
||||
var otherTile = GetTile(otherIndices);
|
||||
AddActiveTile(otherTile);
|
||||
otherTile?.UpdateAdjacent(direction.GetOpposite());
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void FixVacuum(MapIndices indices)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return;
|
||||
var tile = GetTile(indices);
|
||||
if (tile?.GridIndex != _grid.Index) return;
|
||||
if (tile?.GridIndex != mapGrid.Grid.Index) return;
|
||||
var adjacent = GetAdjacentTiles(indices);
|
||||
tile.Air = new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.T20C};
|
||||
_tiles[indices] = tile;
|
||||
@@ -224,16 +286,17 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
|
||||
/// <inheritdoc />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AddActiveTile(TileAtmosphere tile)
|
||||
public void AddActiveTile(TileAtmosphere? tile)
|
||||
{
|
||||
if (tile?.GridIndex != _grid.Index || tile?.Air == null) return;
|
||||
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return;
|
||||
if (tile?.GridIndex != mapGrid.Grid.Index || tile?.Air == null) return;
|
||||
tile.Excited = true;
|
||||
_activeTiles.Add(tile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void RemoveActiveTile(TileAtmosphere tile)
|
||||
public void RemoveActiveTile(TileAtmosphere? tile)
|
||||
{
|
||||
if (tile == null) return;
|
||||
_activeTiles.Remove(tile);
|
||||
@@ -243,27 +306,29 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
|
||||
/// <inheritdoc />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AddHotspotTile(TileAtmosphere tile)
|
||||
public void AddHotspotTile(TileAtmosphere? tile)
|
||||
{
|
||||
if (tile?.GridIndex != _grid.Index || tile?.Air == null) return;
|
||||
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return;
|
||||
if (tile?.GridIndex != mapGrid.Grid.Index || tile?.Air == null) return;
|
||||
_hotspotTiles.Add(tile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void RemoveHotspotTile(TileAtmosphere tile)
|
||||
public void RemoveHotspotTile(TileAtmosphere? tile)
|
||||
{
|
||||
if (tile == null) return;
|
||||
_hotspotTiles.Remove(tile);
|
||||
}
|
||||
|
||||
public void AddSuperconductivityTile(TileAtmosphere tile)
|
||||
public void AddSuperconductivityTile(TileAtmosphere? tile)
|
||||
{
|
||||
if (tile?.GridIndex != _grid.Index) return;
|
||||
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return;
|
||||
if (tile?.GridIndex != mapGrid.Grid.Index) return;
|
||||
_superconductivityTiles.Add(tile);
|
||||
}
|
||||
|
||||
public void RemoveSuperconductivityTile(TileAtmosphere tile)
|
||||
public void RemoveSuperconductivityTile(TileAtmosphere? tile)
|
||||
{
|
||||
if (tile == null) return;
|
||||
_superconductivityTiles.Remove(tile);
|
||||
@@ -271,9 +336,10 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
|
||||
/// <inheritdoc />
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AddHighPressureDelta(TileAtmosphere tile)
|
||||
public void AddHighPressureDelta(TileAtmosphere? tile)
|
||||
{
|
||||
if (tile?.GridIndex != _grid.Index) return;
|
||||
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return;
|
||||
if (tile?.GridIndex != mapGrid.Grid.Index) return;
|
||||
_highPressureDelta.Add(tile);
|
||||
}
|
||||
|
||||
@@ -298,23 +364,43 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
_excitedGroups.Remove(excitedGroup);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public TileAtmosphere GetTile(GridCoordinates coordinates)
|
||||
public void AddPipeNet(IPipeNet pipeNet)
|
||||
{
|
||||
return GetTile(coordinates.ToMapIndices(_mapManager));
|
||||
_pipeNets.Add(pipeNet);
|
||||
}
|
||||
|
||||
public void RemovePipeNet(IPipeNet pipeNet)
|
||||
{
|
||||
_pipeNets.Remove(pipeNet);
|
||||
}
|
||||
|
||||
public void AddPipeNetDevice(PipeNetDeviceComponent pipeNetDevice)
|
||||
{
|
||||
_pipeNetDevices.Add(pipeNetDevice);
|
||||
}
|
||||
|
||||
public void RemovePipeNetDevice(PipeNetDeviceComponent pipeNetDevice)
|
||||
{
|
||||
_pipeNetDevices.Remove(pipeNetDevice);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public TileAtmosphere GetTile(MapIndices indices)
|
||||
public TileAtmosphere? GetTile(GridCoordinates coordinates, bool createSpace = true)
|
||||
{
|
||||
return GetTile(coordinates.ToMapIndices(_mapManager), createSpace);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public TileAtmosphere? GetTile(MapIndices indices, bool createSpace = true)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return null;
|
||||
|
||||
if (_tiles.TryGetValue(indices, out var tile)) return tile;
|
||||
|
||||
// We don't have that tile!
|
||||
if (IsSpace(indices))
|
||||
if (IsSpace(indices) && createSpace)
|
||||
{
|
||||
var space = new TileAtmosphere(this, _grid.Index, indices, new GasMixture(int.MaxValue){Temperature = Atmospherics.TCMB});
|
||||
space.Air.MarkImmutable();
|
||||
return space;
|
||||
return new TileAtmosphere(this, mapGrid.Grid.Index, indices, new GasMixture(GetVolumeForCells(1)){Temperature = Atmospherics.TCMB}, true);
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -331,32 +417,34 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
public bool IsSpace(MapIndices indices)
|
||||
{
|
||||
// TODO ATMOS use ContentTileDefinition to define in YAML whether or not a tile is considered space
|
||||
return _grid.GetTileRef(indices).Tile.IsEmpty;
|
||||
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return default;
|
||||
|
||||
return mapGrid.Grid.GetTileRef(indices).Tile.IsEmpty;
|
||||
}
|
||||
|
||||
public Dictionary<Direction, TileAtmosphere> GetAdjacentTiles(MapIndices indices, bool includeAirBlocked = false)
|
||||
public Dictionary<AtmosDirection, TileAtmosphere> GetAdjacentTiles(MapIndices indices, bool includeAirBlocked = false)
|
||||
{
|
||||
var sides = new Dictionary<Direction, TileAtmosphere>();
|
||||
foreach (var dir in Cardinal)
|
||||
var sides = new Dictionary<AtmosDirection, TileAtmosphere>();
|
||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
var side = indices.Offset(dir);
|
||||
var direction = (AtmosDirection) (1 << i);
|
||||
var side = indices.Offset(direction.ToDirection());
|
||||
var tile = GetTile(side);
|
||||
if(tile?.Air != null || includeAirBlocked)
|
||||
sides[dir] = tile;
|
||||
if (tile != null && (tile.Air != null || includeAirBlocked))
|
||||
sides[direction] = tile;
|
||||
}
|
||||
|
||||
return sides;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int HighPressureDeltaCount => _highPressureDelta.Count;
|
||||
|
||||
public long EqualizationQueueCycleControl { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public float GetVolumeForCells(int cellCount)
|
||||
{
|
||||
return _grid.TileSize * cellCount * Atmospherics.CellVolume;
|
||||
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return default;
|
||||
|
||||
return mapGrid.Grid.TileSize * cellCount * Atmospherics.CellVolume;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -376,27 +464,83 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
switch (_state)
|
||||
{
|
||||
case ProcessState.TileEqualize:
|
||||
ProcessTileEqualize();
|
||||
if (!ProcessTileEqualize(_paused))
|
||||
{
|
||||
_paused = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_paused = false;
|
||||
_state = ProcessState.ActiveTiles;
|
||||
return;
|
||||
case ProcessState.ActiveTiles:
|
||||
ProcessActiveTiles();
|
||||
if (!ProcessActiveTiles(_paused))
|
||||
{
|
||||
_paused = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_paused = false;
|
||||
_state = ProcessState.ExcitedGroups;
|
||||
return;
|
||||
case ProcessState.ExcitedGroups:
|
||||
ProcessExcitedGroups();
|
||||
if (!ProcessExcitedGroups(_paused))
|
||||
{
|
||||
_paused = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_paused = false;
|
||||
_state = ProcessState.HighPressureDelta;
|
||||
return;
|
||||
case ProcessState.HighPressureDelta:
|
||||
ProcessHighPressureDelta();
|
||||
if (!ProcessHighPressureDelta(_paused))
|
||||
{
|
||||
_paused = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_paused = false;
|
||||
_state = ProcessState.Hotspots;
|
||||
break;
|
||||
case ProcessState.Hotspots:
|
||||
ProcessHotspots();
|
||||
if (!ProcessHotspots(_paused))
|
||||
{
|
||||
_paused = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_paused = false;
|
||||
_state = ProcessState.Superconductivity;
|
||||
break;
|
||||
case ProcessState.Superconductivity:
|
||||
ProcessSuperconductivity();
|
||||
if (!ProcessSuperconductivity(_paused))
|
||||
{
|
||||
_paused = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_paused = false;
|
||||
_state = ProcessState.PipeNet;
|
||||
break;
|
||||
case ProcessState.PipeNet:
|
||||
if (!ProcessPipeNets(_paused))
|
||||
{
|
||||
_paused = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_paused = false;
|
||||
_state = ProcessState.PipeNetDevices;
|
||||
break;
|
||||
case ProcessState.PipeNetDevices:
|
||||
if (!ProcessPipeNetDevices(_paused))
|
||||
{
|
||||
_paused = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_paused = false;
|
||||
_state = ProcessState.TileEqualize;
|
||||
break;
|
||||
}
|
||||
@@ -404,47 +548,71 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
UpdateCounter++;
|
||||
}
|
||||
|
||||
public void ProcessTileEqualize()
|
||||
public bool ProcessTileEqualize(bool resumed = false)
|
||||
{
|
||||
_stopwatch.Restart();
|
||||
|
||||
if(!resumed)
|
||||
_currentRunTiles = new Queue<TileAtmosphere>(_activeTiles);
|
||||
|
||||
var number = 0;
|
||||
foreach (var tile in _activeTiles.ToArray())
|
||||
while (_currentRunTiles.Count > 0)
|
||||
{
|
||||
var tile = _currentRunTiles.Dequeue();
|
||||
tile.EqualizePressureInZone(UpdateCounter);
|
||||
|
||||
if (number++ < LagCheckIterations) continue;
|
||||
number = 0;
|
||||
// Process the rest next time.
|
||||
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
|
||||
return;
|
||||
{
|
||||
_tileEqualizeLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_tileEqualizeLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ProcessActiveTiles()
|
||||
public bool ProcessActiveTiles(bool resumed = false)
|
||||
{
|
||||
_stopwatch.Restart();
|
||||
|
||||
if(!resumed)
|
||||
_currentRunTiles = new Queue<TileAtmosphere>(_activeTiles);
|
||||
|
||||
var number = 0;
|
||||
foreach (var tile in _activeTiles.ToArray())
|
||||
while (_currentRunTiles.Count > 0)
|
||||
{
|
||||
var tile = _currentRunTiles.Dequeue();
|
||||
tile.ProcessCell(UpdateCounter);
|
||||
|
||||
if (number++ < LagCheckIterations) continue;
|
||||
number = 0;
|
||||
// Process the rest next time.
|
||||
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
|
||||
return;
|
||||
{
|
||||
_activeTilesLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_activeTilesLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ProcessExcitedGroups()
|
||||
public bool ProcessExcitedGroups(bool resumed = false)
|
||||
{
|
||||
_stopwatch.Restart();
|
||||
|
||||
if(!resumed)
|
||||
_currentRunExcitedGroups = new Queue<ExcitedGroup>(_excitedGroups);
|
||||
|
||||
var number = 0;
|
||||
foreach (var excitedGroup in _excitedGroups.ToArray())
|
||||
while (_currentRunExcitedGroups.Count > 0)
|
||||
{
|
||||
var excitedGroup = _currentRunExcitedGroups.Dequeue();
|
||||
excitedGroup.BreakdownCooldown++;
|
||||
excitedGroup.DismantleCooldown++;
|
||||
|
||||
@@ -458,17 +626,27 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
number = 0;
|
||||
// Process the rest next time.
|
||||
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
|
||||
return;
|
||||
{
|
||||
_excitedGroupLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_excitedGroupLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ProcessHighPressureDelta()
|
||||
public bool ProcessHighPressureDelta(bool resumed = false)
|
||||
{
|
||||
_stopwatch.Restart();
|
||||
|
||||
if(!resumed)
|
||||
_currentRunTiles = new Queue<TileAtmosphere>(_highPressureDelta);
|
||||
|
||||
var number = 0;
|
||||
foreach (var tile in _highPressureDelta.ToArray())
|
||||
while (_currentRunTiles.Count > 0)
|
||||
{
|
||||
var tile = _currentRunTiles.Dequeue();
|
||||
tile.HighPressureMovements();
|
||||
tile.PressureDifference = 0f;
|
||||
tile.PressureSpecificTarget = null;
|
||||
@@ -478,47 +656,129 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
number = 0;
|
||||
// Process the rest next time.
|
||||
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
|
||||
return;
|
||||
{
|
||||
_highPressureDeltaLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_highPressureDeltaLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ProcessHotspots()
|
||||
private bool ProcessHotspots(bool resumed = false)
|
||||
{
|
||||
_stopwatch.Restart();
|
||||
|
||||
if(!resumed)
|
||||
_currentRunTiles = new Queue<TileAtmosphere>(_hotspotTiles);
|
||||
|
||||
var number = 0;
|
||||
foreach (var hotspot in _hotspotTiles.ToArray())
|
||||
while (_currentRunTiles.Count > 0)
|
||||
{
|
||||
var hotspot = _currentRunTiles.Dequeue();
|
||||
hotspot.ProcessHotspot();
|
||||
|
||||
if (number++ < LagCheckIterations) continue;
|
||||
number = 0;
|
||||
// Process the rest next time.
|
||||
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
|
||||
return;
|
||||
{
|
||||
_hotspotsLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_hotspotsLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ProcessSuperconductivity()
|
||||
private bool ProcessSuperconductivity(bool resumed = false)
|
||||
{
|
||||
_stopwatch.Restart();
|
||||
|
||||
if(!resumed)
|
||||
_currentRunTiles = new Queue<TileAtmosphere>(_superconductivityTiles);
|
||||
|
||||
var number = 0;
|
||||
foreach (var superconductivity in _superconductivityTiles.ToArray())
|
||||
while (_currentRunTiles.Count > 0)
|
||||
{
|
||||
var superconductivity = _currentRunTiles.Dequeue();
|
||||
superconductivity.Superconduct();
|
||||
|
||||
if (number++ < LagCheckIterations) continue;
|
||||
number = 0;
|
||||
// Process the rest next time.
|
||||
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
|
||||
return;
|
||||
{
|
||||
_superconductivityLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_superconductivityLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return true;
|
||||
}
|
||||
|
||||
private AirtightComponent GetObstructingComponent(MapIndices indices)
|
||||
private bool ProcessPipeNets(bool resumed = false)
|
||||
{
|
||||
foreach (var v in _grid.GetSnapGridCell(indices, SnapGridOffset.Center))
|
||||
_stopwatch.Restart();
|
||||
|
||||
if(!resumed)
|
||||
_currentRunPipeNet = new Queue<IPipeNet>(_pipeNets);
|
||||
|
||||
var number = 0;
|
||||
while (_currentRunPipeNet.Count > 0)
|
||||
{
|
||||
var pipenet = _currentRunPipeNet.Dequeue();
|
||||
pipenet.Update();
|
||||
|
||||
if (number++ < LagCheckIterations) continue;
|
||||
number = 0;
|
||||
// Process the rest next time.
|
||||
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
|
||||
{
|
||||
_pipeNetLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_pipeNetLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ProcessPipeNetDevices(bool resumed = false)
|
||||
{
|
||||
_stopwatch.Restart();
|
||||
|
||||
if(!resumed)
|
||||
_currentRunPipeNetDevice = new Queue<PipeNetDeviceComponent>(_pipeNetDevices);
|
||||
|
||||
var number = 0;
|
||||
while (_currentRunPipeNet.Count > 0)
|
||||
{
|
||||
var device = _currentRunPipeNetDevice.Dequeue();
|
||||
device.Update();
|
||||
|
||||
if (number++ < LagCheckIterations) continue;
|
||||
number = 0;
|
||||
// Process the rest next time.
|
||||
if (_stopwatch.Elapsed.TotalMilliseconds >= LagCheckMaxMilliseconds)
|
||||
{
|
||||
_pipeNetDevicesLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_pipeNetDevicesLastProcess = _stopwatch.Elapsed.TotalMilliseconds;
|
||||
return true;
|
||||
}
|
||||
|
||||
private AirtightComponent? GetObstructingComponent(MapIndices indices)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out IMapGridComponent? mapGrid)) return default;
|
||||
|
||||
foreach (var v in mapGrid.Grid.GetSnapGridCell(indices, SnapGridOffset.Center))
|
||||
{
|
||||
if (v.Owner.TryGetComponent<AirtightComponent>(out var ac))
|
||||
return ac;
|
||||
@@ -527,12 +787,6 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
return null;
|
||||
}
|
||||
|
||||
private static readonly Direction[] Cardinal =
|
||||
new []
|
||||
{
|
||||
Direction.North, Direction.East, Direction.South, Direction.West
|
||||
};
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
@@ -541,22 +795,24 @@ namespace Content.Server.GameObjects.Components.Atmos
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
if (serializer.Reading)
|
||||
if (serializer.Reading &&
|
||||
Owner.TryGetComponent(out IMapGridComponent? mapGrid))
|
||||
{
|
||||
var gridId = Owner.GetComponent<IMapGridComponent>().Grid.Index;
|
||||
var gridId = mapGrid.Grid.Index;
|
||||
|
||||
if (!serializer.TryReadDataField("uniqueMixes", out List<GasMixture> uniqueMixes) ||
|
||||
!serializer.TryReadDataField("tiles", out Dictionary<MapIndices, int> tiles))
|
||||
if (!serializer.TryReadDataField("uniqueMixes", out List<GasMixture>? uniqueMixes) ||
|
||||
!serializer.TryReadDataField("tiles", out Dictionary<MapIndices, int>? tiles))
|
||||
return;
|
||||
|
||||
_tiles.Clear();
|
||||
|
||||
foreach (var (indices, mix) in tiles)
|
||||
foreach (var (indices, mix) in tiles!)
|
||||
{
|
||||
_tiles.Add(indices, new TileAtmosphere(this, gridId, indices, (GasMixture)uniqueMixes[mix].Clone()));
|
||||
_tiles.Add(indices, new TileAtmosphere(this, gridId, indices, (GasMixture)uniqueMixes![mix].Clone()));
|
||||
Invalidate(indices);
|
||||
}
|
||||
} else if (serializer.Writing)
|
||||
}
|
||||
else if (serializer.Writing)
|
||||
{
|
||||
var uniqueMixes = new List<GasMixture>();
|
||||
var uniqueMixHash = new Dictionary<GasMixture, int>();
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Atmos.Piping
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds itself to a <see cref="IGridAtmosphereComponent"/> to be updated by.
|
||||
/// TODO: Make compatible with unanchoring/anchoring. Currently assumes that the Owner does not move.
|
||||
/// </summary>
|
||||
public abstract class PipeNetDeviceComponent : Component
|
||||
{
|
||||
public abstract void Update();
|
||||
|
||||
protected IGridAtmosphereComponent JoinedGridAtmos { get; private set; }
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
JoinGridAtmos();
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
LeaveGridAtmos();
|
||||
}
|
||||
|
||||
private void JoinGridAtmos()
|
||||
{
|
||||
var gridAtmos = EntitySystem.Get<AtmosphereSystem>()
|
||||
.GetGridAtmosphere(Owner.Transform.GridID);
|
||||
if (gridAtmos == null)
|
||||
{
|
||||
Logger.Error($"{nameof(PipeNetDeviceComponent)} on entity {Owner.Uid} could not find an {nameof(IGridAtmosphereComponent)}.");
|
||||
return;
|
||||
}
|
||||
JoinedGridAtmos = gridAtmos;
|
||||
JoinedGridAtmos.AddPipeNetDevice(this);
|
||||
}
|
||||
|
||||
private void LeaveGridAtmos()
|
||||
{
|
||||
JoinedGridAtmos?.RemovePipeNetDevice(this);
|
||||
JoinedGridAtmos = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.GameObjects.Components.NodeContainer;
|
||||
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Atmos.Piping
|
||||
{
|
||||
/// <summary>
|
||||
/// Transfer gas from one <see cref="PipeNode"/> to another.
|
||||
/// </summary>
|
||||
public abstract class BasePumpComponent : PipeNetDeviceComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Needs to be same <see cref="PipeDirection"/> as that of a <see cref="Pipe"/> on this entity.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private PipeDirection _inletDirection;
|
||||
|
||||
/// <summary>
|
||||
/// Needs to be same <see cref="PipeDirection"/> as that of a <see cref="Pipe"/> on this entity.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private PipeDirection _outletDirection;
|
||||
|
||||
[ViewVariables]
|
||||
private PipeNode _inletPipe;
|
||||
|
||||
[ViewVariables]
|
||||
private PipeNode _outletPipe;
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(ref _inletDirection, "inletDirection", PipeDirection.None);
|
||||
serializer.DataField(ref _outletDirection, "outletDirection", PipeDirection.None);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
if (!Owner.TryGetComponent<NodeContainerComponent>(out var container))
|
||||
{
|
||||
JoinedGridAtmos?.RemovePipeNetDevice(this);
|
||||
Logger.Error($"{typeof(BasePumpComponent)} on entity {Owner.Uid} did not have a {nameof(NodeContainerComponent)}.");
|
||||
return;
|
||||
}
|
||||
var pipeNodes = container.Nodes.OfType<PipeNode>();
|
||||
_inletPipe = pipeNodes.Where(pipe => pipe.PipeDirection == _inletDirection).FirstOrDefault();
|
||||
_outletPipe = pipeNodes.Where(pipe => pipe.PipeDirection == _outletDirection).FirstOrDefault();
|
||||
if (_inletPipe == null | _outletPipe == null)
|
||||
{
|
||||
JoinedGridAtmos?.RemovePipeNetDevice(this);
|
||||
Logger.Error($"{typeof(BasePumpComponent)} on entity {Owner.Uid} could not find compatible {nameof(PipeNode)}s on its {nameof(NodeContainerComponent)}.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
PumpGas(_inletPipe.Air, _outletPipe.Air);
|
||||
}
|
||||
|
||||
protected abstract void PumpGas(GasMixture inletGas, GasMixture outletGas);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Content.Server.Atmos;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Atmos.Piping
|
||||
{
|
||||
/// <summary>
|
||||
/// Placeholder example of pump functionality.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(BasePumpComponent))]
|
||||
public class DebugPumpComponent : BasePumpComponent
|
||||
{
|
||||
public override string Name => "DebugPump";
|
||||
|
||||
protected override void PumpGas(GasMixture inletGas, GasMixture outletGas)
|
||||
{
|
||||
outletGas.Merge(inletGas);
|
||||
inletGas.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.GameObjects.Components.NodeContainer;
|
||||
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Atmos.Piping
|
||||
{
|
||||
/// <summary>
|
||||
/// Transfers gas from the tile it is on to a <see cref="PipeNode"/>.
|
||||
/// </summary>
|
||||
public abstract class BaseSiphonComponent : PipeNetDeviceComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
private PipeNode _scrubberOutlet;
|
||||
|
||||
private AtmosphereSystem _atmosSystem;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_atmosSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
if (!Owner.TryGetComponent<NodeContainerComponent>(out var container))
|
||||
{
|
||||
JoinedGridAtmos?.RemovePipeNetDevice(this);
|
||||
Logger.Error($"{typeof(BaseSiphonComponent)} on entity {Owner.Uid} did not have a {nameof(NodeContainerComponent)}.");
|
||||
return;
|
||||
}
|
||||
_scrubberOutlet = container.Nodes.OfType<PipeNode>().FirstOrDefault();
|
||||
if (_scrubberOutlet == null)
|
||||
{
|
||||
JoinedGridAtmos?.RemovePipeNetDevice(this);
|
||||
Logger.Error($"{typeof(BaseSiphonComponent)} on entity {Owner.Uid} could not find compatible {nameof(PipeNode)}s on its {nameof(NodeContainerComponent)}.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
var tileAtmos = AtmosHelpers.GetTileAtmosphere(Owner.Transform.GridPosition);
|
||||
if (tileAtmos == null)
|
||||
return;
|
||||
ScrubGas(tileAtmos.Air, _scrubberOutlet.Air);
|
||||
_atmosSystem.GetGridAtmosphere(Owner.Transform.GridID).Invalidate(tileAtmos.GridIndices);
|
||||
}
|
||||
|
||||
protected abstract void ScrubGas(GasMixture inletGas, GasMixture outletGas);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Content.Server.Atmos;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Atmos.Piping
|
||||
{
|
||||
/// <summary>
|
||||
/// Placeholder example of scrubber functionality.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(BaseSiphonComponent))]
|
||||
public class DebugSiphonComponent : BaseSiphonComponent
|
||||
{
|
||||
public override string Name => "DebugSiphon";
|
||||
|
||||
protected override void ScrubGas(GasMixture inletGas, GasMixture outletGas)
|
||||
{
|
||||
outletGas.Merge(inletGas);
|
||||
inletGas.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.GameObjects.Components.NodeContainer;
|
||||
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Atmos.Piping
|
||||
{
|
||||
/// <summary>
|
||||
/// Transfers gas from a <see cref="PipeNode"/> to the tile it is on.
|
||||
/// </summary>
|
||||
public abstract class BaseVentComponent : PipeNetDeviceComponent
|
||||
{
|
||||
[ViewVariables]
|
||||
private PipeNode _ventInlet;
|
||||
|
||||
private AtmosphereSystem _atmosSystem;
|
||||
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_atmosSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
if (!Owner.TryGetComponent<NodeContainerComponent>(out var container))
|
||||
{
|
||||
JoinedGridAtmos?.RemovePipeNetDevice(this);
|
||||
Logger.Error($"{typeof(BaseVentComponent)} on entity {Owner.Uid} did not have a {nameof(NodeContainerComponent)}.");
|
||||
return;
|
||||
}
|
||||
_ventInlet = container.Nodes.OfType<PipeNode>().FirstOrDefault();
|
||||
if (_ventInlet == null)
|
||||
{
|
||||
JoinedGridAtmos?.RemovePipeNetDevice(this);
|
||||
Logger.Error($"{typeof(BaseVentComponent)} on entity {Owner.Uid} could not find compatible {nameof(PipeNode)}s on its {nameof(NodeContainerComponent)}.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
var tileAtmos = AtmosHelpers.GetTileAtmosphere(Owner.Transform.GridPosition);
|
||||
if (tileAtmos == null)
|
||||
return;
|
||||
VentGas(_ventInlet.Air, tileAtmos.Air);
|
||||
_atmosSystem.GetGridAtmosphere(Owner.Transform.GridID).Invalidate(tileAtmos.GridIndices);
|
||||
}
|
||||
|
||||
protected abstract void VentGas(GasMixture inletGas, GasMixture outletGas);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Content.Server.Atmos;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Atmos.Piping
|
||||
{
|
||||
/// <summary>
|
||||
/// Placeholder example of vent functionality.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(BaseVentComponent))]
|
||||
public class DebugVentComponent : BaseVentComponent
|
||||
{
|
||||
public override string Name => "DebugVent";
|
||||
|
||||
protected override void VentGas(GasMixture inletGas, GasMixture outletGas)
|
||||
{
|
||||
outletGas.Merge(inletGas);
|
||||
inletGas.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
#nullable enable
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
@@ -19,18 +20,13 @@ namespace Content.Server.GameObjects.Components.BarSign
|
||||
{
|
||||
public override string Name => "BarSign";
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
|
||||
private string _currentSign;
|
||||
|
||||
private PowerReceiverComponent _power;
|
||||
private SpriteComponent _sprite;
|
||||
private string? _currentSign;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string CurrentSign
|
||||
public string? CurrentSign
|
||||
{
|
||||
get => _currentSign;
|
||||
set
|
||||
@@ -40,6 +36,8 @@ namespace Content.Server.GameObjects.Components.BarSign
|
||||
}
|
||||
}
|
||||
|
||||
private bool Powered => !Owner.TryGetComponent(out PowerReceiverComponent? receiver) || receiver.Powered;
|
||||
|
||||
private void UpdateSignInfo()
|
||||
{
|
||||
if (_currentSign == null)
|
||||
@@ -53,15 +51,18 @@ namespace Content.Server.GameObjects.Components.BarSign
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_power.Powered)
|
||||
if (Owner.TryGetComponent(out SpriteComponent? sprite))
|
||||
{
|
||||
_sprite.LayerSetState(0, "empty");
|
||||
_sprite.LayerSetShader(0, "shaded");
|
||||
}
|
||||
else
|
||||
{
|
||||
_sprite.LayerSetState(0, prototype.Icon);
|
||||
_sprite.LayerSetShader(0, "unshaded");
|
||||
if (!Powered)
|
||||
{
|
||||
sprite.LayerSetState(0, "empty");
|
||||
sprite.LayerSetShader(0, "shaded");
|
||||
}
|
||||
else
|
||||
{
|
||||
sprite.LayerSetState(0, prototype.Icon);
|
||||
sprite.LayerSetShader(0, "unshaded");
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(prototype.Name))
|
||||
@@ -80,21 +81,25 @@ namespace Content.Server.GameObjects.Components.BarSign
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_power = Owner.GetComponent<PowerReceiverComponent>();
|
||||
_sprite = Owner.GetComponent<SpriteComponent>();
|
||||
|
||||
_power.OnPowerStateChanged += PowerOnOnPowerStateChanged;
|
||||
if (Owner.TryGetComponent(out PowerReceiverComponent? receiver))
|
||||
{
|
||||
receiver.OnPowerStateChanged += PowerOnOnPowerStateChanged;
|
||||
}
|
||||
|
||||
UpdateSignInfo();
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
_power.OnPowerStateChanged -= PowerOnOnPowerStateChanged;
|
||||
if (Owner.TryGetComponent(out PowerReceiverComponent? receiver))
|
||||
{
|
||||
receiver.OnPowerStateChanged -= PowerOnOnPowerStateChanged;
|
||||
}
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
private void PowerOnOnPowerStateChanged(object sender, PowerStateEventArgs e)
|
||||
private void PowerOnOnPowerStateChanged(object? sender, PowerStateEventArgs e)
|
||||
{
|
||||
UpdateSignInfo();
|
||||
}
|
||||
|
||||
@@ -18,14 +18,12 @@ using Content.Shared.Body.Template;
|
||||
using Content.Shared.GameObjects.Components.Body;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Reflection;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -43,11 +41,9 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
[ComponentReference(typeof(IBodyManagerComponent))]
|
||||
public class BodyManagerComponent : SharedBodyManagerComponent, IBodyPartContainer, IRelayMoveInput
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IBodyNetworkFactory _bodyNetworkFactory = default!;
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
#pragma warning restore
|
||||
|
||||
[ViewVariables] private string _presetName = default!;
|
||||
|
||||
@@ -138,11 +134,6 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
base.Initialize();
|
||||
|
||||
LoadBodyPreset(Preset);
|
||||
|
||||
foreach (var behavior in Owner.GetAllComponents<IOnHealthChangedBehavior>())
|
||||
{
|
||||
HealthChangedEvent += behavior.OnHealthChanged;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Body;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Body.Scanner;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Body
|
||||
{
|
||||
@@ -12,32 +16,39 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
public class BodyScannerComponent : Component, IActivate
|
||||
{
|
||||
private BoundUserInterface _userInterface;
|
||||
public sealed override string Name => "BodyScanner";
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(BodyScannerUiKey.Key);
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent actor) ||
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent? actor) ||
|
||||
actor.playerSession.AttachedEntity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (actor.playerSession.AttachedEntity.TryGetComponent(out BodyManagerComponent attempt))
|
||||
if (actor.playerSession.AttachedEntity.TryGetComponent(out BodyManagerComponent? attempt))
|
||||
{
|
||||
var state = InterfaceState(attempt.Template, attempt.Parts);
|
||||
_userInterface.SetState(state);
|
||||
UserInterface?.SetState(state);
|
||||
}
|
||||
|
||||
_userInterface.Open(actor.playerSession);
|
||||
UserInterface?.Open(actor.playerSession);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(BodyScannerUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
|
||||
if (UserInterface == null)
|
||||
{
|
||||
Logger.Warning($"Entity {Owner} at {Owner.Transform.MapPosition} doesn't have a {nameof(ServerUserInterfaceComponent)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
UserInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
}
|
||||
}
|
||||
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg) { }
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Content.Server.GameObjects.Components.Body.Circulatory
|
||||
/// </summary>
|
||||
[ViewVariables] public ReagentUnit EmptyVolume => _internalSolution.EmptyVolume;
|
||||
|
||||
[ViewVariables] public GasMixture Air { get; set; } = new GasMixture(6);
|
||||
[ViewVariables] public GasMixture Air { get; set; }
|
||||
|
||||
[ViewVariables] public SolutionComponent Solution => _internalSolution;
|
||||
|
||||
@@ -45,6 +45,8 @@ namespace Content.Server.GameObjects.Components.Body.Circulatory
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
Air = new GasMixture(6);
|
||||
|
||||
serializer.DataField(ref _initialMaxVolume, "maxVolume", ReagentUnit.New(250));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.Body.Circulatory;
|
||||
using Content.Server.GameObjects.Components.Chemistry;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Nutrition;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -20,25 +19,21 @@ namespace Content.Server.GameObjects.Components.Body.Digestive
|
||||
[RegisterComponent]
|
||||
public class StomachComponent : SharedStomachComponent
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
/// <summary>
|
||||
/// Max volume of internal solution storage
|
||||
/// </summary>
|
||||
public ReagentUnit MaxVolume
|
||||
{
|
||||
get => _stomachContents.MaxVolume;
|
||||
set => _stomachContents.MaxVolume = value;
|
||||
get => Owner.TryGetComponent(out SolutionComponent? solution) ? solution.MaxVolume : ReagentUnit.Zero;
|
||||
set
|
||||
{
|
||||
if (Owner.TryGetComponent(out SolutionComponent? solution))
|
||||
{
|
||||
solution.MaxVolume = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal solution storage
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private SolutionComponent _stomachContents;
|
||||
|
||||
/// <summary>
|
||||
/// Initial internal solution storage volume
|
||||
/// </summary>
|
||||
@@ -68,20 +63,29 @@ namespace Content.Server.GameObjects.Components.Body.Digestive
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
_stomachContents = Owner.GetComponent<SolutionComponent>();
|
||||
_stomachContents.MaxVolume = _initialMaxVolume;
|
||||
if (!Owner.EnsureComponent(out SolutionComponent solution))
|
||||
{
|
||||
Logger.Warning($"Entity {Owner} at {Owner.Transform.MapPosition} didn't have a {nameof(SolutionComponent)}");
|
||||
}
|
||||
|
||||
solution.MaxVolume = _initialMaxVolume;
|
||||
}
|
||||
|
||||
public bool TryTransferSolution(Solution solution)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SolutionComponent? solutionComponent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: For now no partial transfers. Potentially change by design
|
||||
if (solution.TotalVolume + _stomachContents.CurrentVolume > _stomachContents.MaxVolume)
|
||||
if (solution.TotalVolume + solutionComponent.CurrentVolume > solutionComponent.MaxVolume)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add solution to _stomachContents
|
||||
_stomachContents.TryAddSolution(solution, false, true);
|
||||
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)
|
||||
{
|
||||
@@ -99,7 +103,8 @@ namespace Content.Server.GameObjects.Components.Body.Digestive
|
||||
/// <param name="frameTime">The time since the last update in seconds.</param>
|
||||
public void Update(float frameTime)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out BloodstreamComponent bloodstream))
|
||||
if (!Owner.TryGetComponent(out SolutionComponent? solutionComponent) ||
|
||||
!Owner.TryGetComponent(out BloodstreamComponent? bloodstream))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -114,7 +119,7 @@ namespace Content.Server.GameObjects.Components.Body.Digestive
|
||||
delta.Increment(frameTime);
|
||||
if (delta.Lifetime > _digestionDelay)
|
||||
{
|
||||
_stomachContents.TryRemoveReagent(delta.ReagentId, delta.Quantity);
|
||||
solutionComponent.TryRemoveReagent(delta.ReagentId, delta.Quantity);
|
||||
transferSolution.AddReagent(delta.ReagentId, delta.Quantity);
|
||||
_reagentDeltas.Remove(delta);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
using System.Collections.Generic;
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Content.Server.Body;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Body.Surgery;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
@@ -21,20 +24,18 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
[RegisterComponent]
|
||||
public class DroppedBodyPartComponent : Component, IAfterInteract, IBodyPartContainer
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ISharedNotifyManager _sharedNotifyManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly ISharedNotifyManager _sharedNotifyManager = default!;
|
||||
|
||||
private readonly Dictionary<int, object> _optionsCache = new Dictionary<int, object>();
|
||||
private BodyManagerComponent _bodyManagerComponentCache;
|
||||
private BodyManagerComponent? _bodyManagerComponentCache;
|
||||
private int _idHash;
|
||||
private IEntity _performerCache;
|
||||
|
||||
private BoundUserInterface _userInterface;
|
||||
private IEntity? _performerCache;
|
||||
|
||||
public sealed override string Name => "DroppedBodyPart";
|
||||
|
||||
[ViewVariables] public BodyPart ContainedBodyPart { get; private set; }
|
||||
[ViewVariables] public BodyPart ContainedBodyPart { get; private set; } = default!;
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(GenericSurgeryUiKey.Key);
|
||||
|
||||
void IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
@@ -48,7 +49,7 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
_performerCache = null;
|
||||
_bodyManagerComponentCache = null;
|
||||
|
||||
if (eventArgs.Target.TryGetComponent(out BodyManagerComponent bodyManager))
|
||||
if (eventArgs.Target.TryGetComponent(out BodyManagerComponent? bodyManager))
|
||||
{
|
||||
SendBodySlotListToUser(eventArgs, bodyManager);
|
||||
}
|
||||
@@ -58,9 +59,10 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(GenericSurgeryUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public void TransferBodyPartData(BodyPart data)
|
||||
@@ -68,7 +70,7 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
ContainedBodyPart = data;
|
||||
Owner.Name = Loc.GetString(ContainedBodyPart.Name);
|
||||
|
||||
if (Owner.TryGetComponent(out SpriteComponent component))
|
||||
if (Owner.TryGetComponent(out SpriteComponent? component))
|
||||
{
|
||||
component.LayerSetRSI(0, data.RSIPath);
|
||||
component.LayerSetState(0, data.RSIState);
|
||||
@@ -91,7 +93,7 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
foreach (var slot in unoccupiedSlots)
|
||||
{
|
||||
if (!bodyManager.TryGetSlotType(slot, out var typeResult) ||
|
||||
typeResult != ContainedBodyPart.PartType ||
|
||||
typeResult != ContainedBodyPart?.PartType ||
|
||||
!bodyManager.TryGetBodyPartConnections(slot, out var parts))
|
||||
{
|
||||
continue;
|
||||
@@ -129,7 +131,18 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
/// </summary>
|
||||
private void HandleReceiveBodyPartSlot(int key)
|
||||
{
|
||||
CloseSurgeryUI(_performerCache.GetComponent<BasicActorComponent>().playerSession);
|
||||
if (_performerCache == null ||
|
||||
!_performerCache.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CloseSurgeryUI(actor.playerSession);
|
||||
|
||||
if (_bodyManagerComponentCache == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: sanity checks to see whether user is in range, user is still able-bodied, target is still the same, etc etc
|
||||
if (!_optionsCache.TryGetValue(key, out var targetObject))
|
||||
@@ -138,34 +151,42 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
Loc.GetString("You see no useful way to attach {0:theName} anymore.", Owner));
|
||||
}
|
||||
|
||||
var target = targetObject as string;
|
||||
var target = (string) targetObject!;
|
||||
string message;
|
||||
|
||||
if (_bodyManagerComponentCache.InstallDroppedBodyPart(this, target))
|
||||
{
|
||||
message = Loc.GetString("You attach {0:theName}.", ContainedBodyPart);
|
||||
}
|
||||
else
|
||||
{
|
||||
message = Loc.GetString("You can't attach it!");
|
||||
}
|
||||
|
||||
_sharedNotifyManager.PopupMessage(
|
||||
_bodyManagerComponentCache.Owner,
|
||||
_performerCache,
|
||||
!_bodyManagerComponentCache.InstallDroppedBodyPart(this, target)
|
||||
? Loc.GetString("You can't attach it!")
|
||||
: Loc.GetString("You attach {0:theName}.", ContainedBodyPart));
|
||||
message);
|
||||
}
|
||||
|
||||
private void OpenSurgeryUI(IPlayerSession session)
|
||||
{
|
||||
_userInterface.Open(session);
|
||||
UserInterface?.Open(session);
|
||||
}
|
||||
|
||||
private void UpdateSurgeryUIBodyPartSlotRequest(IPlayerSession session, Dictionary<string, int> options)
|
||||
{
|
||||
_userInterface.SendMessage(new RequestBodyPartSlotSurgeryUIMessage(options), session);
|
||||
UserInterface?.SendMessage(new RequestBodyPartSlotSurgeryUIMessage(options), session);
|
||||
}
|
||||
|
||||
private void CloseSurgeryUI(IPlayerSession session)
|
||||
{
|
||||
_userInterface.Close(session);
|
||||
UserInterface?.Close(session);
|
||||
}
|
||||
|
||||
private void CloseAllSurgeryUIs()
|
||||
{
|
||||
_userInterface.CloseAll();
|
||||
UserInterface?.CloseAll();
|
||||
}
|
||||
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage message)
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Body;
|
||||
using Content.Server.Body.Mechanisms;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Body.Mechanism;
|
||||
using Content.Shared.Body.Surgery;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Body
|
||||
@@ -26,24 +28,22 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
[RegisterComponent]
|
||||
public class DroppedMechanismComponent : Component, IAfterInteract
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ISharedNotifyManager _sharedNotifyManager;
|
||||
[Dependency] private IPrototypeManager _prototypeManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly ISharedNotifyManager _sharedNotifyManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public sealed override string Name => "DroppedMechanism";
|
||||
|
||||
private readonly Dictionary<int, object> _optionsCache = new Dictionary<int, object>();
|
||||
|
||||
private BodyManagerComponent _bodyManagerComponentCache;
|
||||
private BodyManagerComponent? _bodyManagerComponentCache;
|
||||
|
||||
private int _idHash;
|
||||
|
||||
private IEntity _performerCache;
|
||||
private IEntity? _performerCache;
|
||||
|
||||
private BoundUserInterface _userInterface;
|
||||
[ViewVariables] public Mechanism ContainedMechanism { get; private set; } = default!;
|
||||
|
||||
[ViewVariables] public Mechanism ContainedMechanism { get; private set; }
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(GenericSurgeryUiKey.Key);
|
||||
|
||||
void IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
@@ -63,12 +63,7 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
}
|
||||
else if (eventArgs.Target.TryGetComponent<DroppedBodyPartComponent>(out var droppedBodyPart))
|
||||
{
|
||||
if (droppedBodyPart.ContainedBodyPart == null)
|
||||
{
|
||||
Logger.Debug(
|
||||
"Installing a mechanism was attempted on an IEntity with a DroppedBodyPartComponent that doesn't have a BodyPart in it!");
|
||||
throw new InvalidOperationException("A DroppedBodyPartComponent exists without a BodyPart in it!");
|
||||
}
|
||||
DebugTools.AssertNotNull(droppedBodyPart.ContainedBodyPart);
|
||||
|
||||
if (!droppedBodyPart.ContainedBodyPart.TryInstallDroppedMechanism(this))
|
||||
{
|
||||
@@ -82,9 +77,10 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(GenericSurgeryUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializeDroppedMechanism(Mechanism data)
|
||||
@@ -92,7 +88,7 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
ContainedMechanism = data;
|
||||
Owner.Name = Loc.GetString(ContainedMechanism.Name);
|
||||
|
||||
if (Owner.TryGetComponent(out SpriteComponent component))
|
||||
if (Owner.TryGetComponent(out SpriteComponent? component))
|
||||
{
|
||||
component.LayerSetRSI(0, data.RSIPath);
|
||||
component.LayerSetState(0, data.RSIState);
|
||||
@@ -111,7 +107,7 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
|
||||
if (serializer.Reading && debugLoadMechanismData != "")
|
||||
{
|
||||
_prototypeManager.TryIndex(debugLoadMechanismData, out MechanismPrototype data);
|
||||
_prototypeManager.TryIndex(debugLoadMechanismData!, out MechanismPrototype data);
|
||||
|
||||
var mechanism = new Mechanism(data);
|
||||
mechanism.EnsureInitialize();
|
||||
@@ -155,7 +151,18 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
/// </summary>
|
||||
private void HandleReceiveBodyPart(int key)
|
||||
{
|
||||
CloseSurgeryUI(_performerCache.GetComponent<BasicActorComponent>().playerSession);
|
||||
if (_performerCache == null ||
|
||||
!_performerCache.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CloseSurgeryUI(actor.playerSession);
|
||||
|
||||
if (_bodyManagerComponentCache == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: sanity checks to see whether user is in range, user is still able-bodied, target is still the same, etc etc
|
||||
if (!_optionsCache.TryGetValue(key, out var targetObject))
|
||||
@@ -165,36 +172,37 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
return;
|
||||
}
|
||||
|
||||
var target = targetObject as BodyPart;
|
||||
var target = (BodyPart) targetObject;
|
||||
var message = target.TryInstallDroppedMechanism(this)
|
||||
? Loc.GetString("You jam the {0} inside {1:them}.", ContainedMechanism.Name, _performerCache)
|
||||
: Loc.GetString("You can't fit it in!");
|
||||
|
||||
_sharedNotifyManager.PopupMessage(
|
||||
_bodyManagerComponentCache.Owner,
|
||||
_performerCache,
|
||||
!target.TryInstallDroppedMechanism(this)
|
||||
? Loc.GetString("You can't fit it in!")
|
||||
: Loc.GetString("You jam the {1} inside {0:them}.", _performerCache, ContainedMechanism.Name));
|
||||
message);
|
||||
|
||||
// TODO: {1:theName}
|
||||
}
|
||||
|
||||
private void OpenSurgeryUI(IPlayerSession session)
|
||||
{
|
||||
_userInterface.Open(session);
|
||||
UserInterface?.Open(session);
|
||||
}
|
||||
|
||||
private void UpdateSurgeryUIBodyPartRequest(IPlayerSession session, Dictionary<string, int> options)
|
||||
{
|
||||
_userInterface.SendMessage(new RequestBodyPartSurgeryUIMessage(options), session);
|
||||
UserInterface?.SendMessage(new RequestBodyPartSurgeryUIMessage(options), session);
|
||||
}
|
||||
|
||||
private void CloseSurgeryUI(IPlayerSession session)
|
||||
{
|
||||
_userInterface.Close(session);
|
||||
UserInterface?.Close(session);
|
||||
}
|
||||
|
||||
private void CloseAllSurgeryUIs()
|
||||
{
|
||||
_userInterface.CloseAll();
|
||||
UserInterface?.CloseAll();
|
||||
}
|
||||
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage message)
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Content.Server.GameObjects.Components.Body.Respiratory
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)] private float Pressure { get; set; }
|
||||
|
||||
[ViewVariables] public GasMixture Air { get; set; } = new GasMixture();
|
||||
[ViewVariables] public GasMixture Air { get; set; }
|
||||
|
||||
[ViewVariables] public LungStatus Status { get; set; }
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace Content.Server.GameObjects.Components.Body.Respiratory
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
Air = new GasMixture();
|
||||
|
||||
serializer.DataReadWriteFunction(
|
||||
"volume",
|
||||
6,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Body;
|
||||
using Content.Server.Body.Mechanisms;
|
||||
using Content.Server.Body.Surgery;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Body.Surgery;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Body;
|
||||
@@ -18,6 +20,8 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Body
|
||||
{
|
||||
@@ -30,9 +34,7 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
[RegisterComponent]
|
||||
public class SurgeryToolComponent : Component, ISurgeon, IAfterInteract
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ISharedNotifyManager _sharedNotifyManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly ISharedNotifyManager _sharedNotifyManager = default!;
|
||||
|
||||
public override string Name => "SurgeryTool";
|
||||
public override uint? NetID => ContentNetIDs.SURGERY;
|
||||
@@ -41,17 +43,17 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
|
||||
private float _baseOperateTime;
|
||||
|
||||
private BodyManagerComponent _bodyManagerComponentCache;
|
||||
private BodyManagerComponent? _bodyManagerComponentCache;
|
||||
|
||||
private ISurgeon.MechanismRequestCallback _callbackCache;
|
||||
private ISurgeon.MechanismRequestCallback? _callbackCache;
|
||||
|
||||
private int _idHash;
|
||||
|
||||
private IEntity _performerCache;
|
||||
private IEntity? _performerCache;
|
||||
|
||||
private SurgeryType _surgeryType;
|
||||
|
||||
private BoundUserInterface _userInterface;
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(GenericSurgeryUiKey.Key);
|
||||
|
||||
void IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
@@ -60,7 +62,7 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
return;
|
||||
}
|
||||
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -73,7 +75,7 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
_callbackCache = null;
|
||||
|
||||
// Attempt surgery on a BodyManagerComponent by sending a list of operable BodyParts to the client to choose from
|
||||
if (eventArgs.Target.TryGetComponent(out BodyManagerComponent body))
|
||||
if (eventArgs.Target.TryGetComponent(out BodyManagerComponent? body))
|
||||
{
|
||||
// Create dictionary to send to client (text to be shown : data sent back if selected)
|
||||
var toSend = new Dictionary<string, int>();
|
||||
@@ -105,13 +107,7 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
// Attempt surgery on a DroppedBodyPart - there's only one possible target so no need for selection UI
|
||||
_performerCache = eventArgs.User;
|
||||
|
||||
if (droppedBodyPart.ContainedBodyPart == null)
|
||||
{
|
||||
// Throw error if the DroppedBodyPart has no data in it.
|
||||
Logger.Debug(
|
||||
"Surgery was attempted on an IEntity with a DroppedBodyPartComponent that doesn't have a BodyPart in it!");
|
||||
throw new InvalidOperationException("A DroppedBodyPartComponent exists without a BodyPart in it!");
|
||||
}
|
||||
DebugTools.AssertNotNull(droppedBodyPart.ContainedBodyPart);
|
||||
|
||||
// If surgery can be performed...
|
||||
if (!droppedBodyPart.ContainedBodyPart.SurgeryCheck(_surgeryType))
|
||||
@@ -144,7 +140,7 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
toSend.Add(mechanism.Name, _idHash++);
|
||||
}
|
||||
|
||||
if (_optionsCache.Count > 0)
|
||||
if (_optionsCache.Count > 0 && _performerCache != null)
|
||||
{
|
||||
OpenSurgeryUI(_performerCache.GetComponent<BasicActorComponent>().playerSession);
|
||||
UpdateSurgeryUIMechanismRequest(_performerCache.GetComponent<BasicActorComponent>().playerSession,
|
||||
@@ -162,34 +158,35 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(GenericSurgeryUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenSurgeryUI(IPlayerSession session)
|
||||
{
|
||||
_userInterface.Open(session);
|
||||
UserInterface?.Open(session);
|
||||
}
|
||||
|
||||
private void UpdateSurgeryUIBodyPartRequest(IPlayerSession session, Dictionary<string, int> options)
|
||||
{
|
||||
_userInterface.SendMessage(new RequestBodyPartSurgeryUIMessage(options), session);
|
||||
UserInterface?.SendMessage(new RequestBodyPartSurgeryUIMessage(options), session);
|
||||
}
|
||||
|
||||
private void UpdateSurgeryUIMechanismRequest(IPlayerSession session, Dictionary<string, int> options)
|
||||
{
|
||||
_userInterface.SendMessage(new RequestMechanismSurgeryUIMessage(options), session);
|
||||
UserInterface?.SendMessage(new RequestMechanismSurgeryUIMessage(options), session);
|
||||
}
|
||||
|
||||
private void CloseSurgeryUI(IPlayerSession session)
|
||||
{
|
||||
_userInterface.Close(session);
|
||||
UserInterface?.Close(session);
|
||||
}
|
||||
|
||||
private void CloseAllSurgeryUIs()
|
||||
{
|
||||
_userInterface.CloseAll();
|
||||
UserInterface?.CloseAll();
|
||||
}
|
||||
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage message)
|
||||
@@ -211,14 +208,22 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
/// </summary>
|
||||
private void HandleReceiveBodyPart(int key)
|
||||
{
|
||||
CloseSurgeryUI(_performerCache.GetComponent<BasicActorComponent>().playerSession);
|
||||
// TODO: sanity checks to see whether user is in range, user is still able-bodied, target is still the same, etc etc
|
||||
if (!_optionsCache.TryGetValue(key, out var targetObject))
|
||||
if (_performerCache == null ||
|
||||
!_performerCache.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
SendNoUsefulWayToUseAnymorePopup();
|
||||
return;
|
||||
}
|
||||
|
||||
var target = targetObject as BodyPart;
|
||||
CloseSurgeryUI(actor.playerSession);
|
||||
// TODO: sanity checks to see whether user is in range, user is still able-bodied, target is still the same, etc etc
|
||||
if (!_optionsCache.TryGetValue(key, out var targetObject) ||
|
||||
_bodyManagerComponentCache == null)
|
||||
{
|
||||
SendNoUsefulWayToUseAnymorePopup();
|
||||
return;
|
||||
}
|
||||
|
||||
var target = (BodyPart) targetObject!;
|
||||
|
||||
if (!target.AttemptSurgery(_surgeryType, _bodyManagerComponentCache, this, _performerCache))
|
||||
{
|
||||
@@ -233,19 +238,27 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
private void HandleReceiveMechanism(int key)
|
||||
{
|
||||
// TODO: sanity checks to see whether user is in range, user is still able-bodied, target is still the same, etc etc
|
||||
if (!_optionsCache.TryGetValue(key, out var targetObject))
|
||||
if (!_optionsCache.TryGetValue(key, out var targetObject) ||
|
||||
_performerCache == null ||
|
||||
!_performerCache.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
SendNoUsefulWayToUseAnymorePopup();
|
||||
return;
|
||||
}
|
||||
|
||||
var target = targetObject as Mechanism;
|
||||
|
||||
CloseSurgeryUI(_performerCache.GetComponent<BasicActorComponent>().playerSession);
|
||||
_callbackCache(target, _bodyManagerComponentCache, this, _performerCache);
|
||||
CloseSurgeryUI(actor.playerSession);
|
||||
_callbackCache?.Invoke(target, _bodyManagerComponentCache, this, _performerCache);
|
||||
}
|
||||
|
||||
private void SendNoUsefulWayToUsePopup()
|
||||
{
|
||||
if (_bodyManagerComponentCache == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_sharedNotifyManager.PopupMessage(
|
||||
_bodyManagerComponentCache.Owner,
|
||||
_performerCache,
|
||||
@@ -254,6 +267,11 @@ namespace Content.Server.GameObjects.Components.Body
|
||||
|
||||
private void SendNoUsefulWayToUseAnymorePopup()
|
||||
{
|
||||
if (_bodyManagerComponentCache == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_sharedNotifyManager.PopupMessage(
|
||||
_bodyManagerComponentCache.Owner,
|
||||
_performerCache,
|
||||
|
||||
@@ -34,13 +34,11 @@ namespace Content.Server.GameObjects.Components.Buckle
|
||||
[RegisterComponent]
|
||||
public class BuckleComponent : SharedBuckleComponent, IInteractHand, IDragDrop
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
#pragma warning restore 649
|
||||
|
||||
private int _size;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Content.Server.Cargo;
|
||||
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.GameObjects.Components.Cargo;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Content.Shared.Prototypes.Cargo;
|
||||
@@ -10,6 +11,7 @@ using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -19,21 +21,11 @@ namespace Content.Server.GameObjects.Components.Cargo
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
public class CargoConsoleComponent : SharedCargoConsoleComponent, IActivate
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ICargoOrderDataManager _cargoOrderDataManager = default!;
|
||||
#pragma warning restore 649
|
||||
|
||||
[ViewVariables]
|
||||
public int Points = 1000;
|
||||
|
||||
private BoundUserInterface _userInterface = default!;
|
||||
|
||||
[ViewVariables]
|
||||
public GalacticMarketComponent Market { get; private set; } = default!;
|
||||
|
||||
[ViewVariables]
|
||||
public CargoOrderDatabaseComponent Orders { get; private set; } = default!;
|
||||
|
||||
private CargoBankAccount? _bankAccount;
|
||||
|
||||
[ViewVariables]
|
||||
@@ -65,22 +57,44 @@ namespace Content.Server.GameObjects.Components.Cargo
|
||||
|
||||
private bool _requestOnly = false;
|
||||
|
||||
private PowerReceiverComponent _powerReceiver = default!;
|
||||
private bool Powered => _powerReceiver.Powered;
|
||||
private bool Powered => !Owner.TryGetComponent(out PowerReceiverComponent? receiver) || receiver.Powered;
|
||||
private CargoConsoleSystem _cargoConsoleSystem = default!;
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(CargoConsoleUiKey.Key);
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Market = Owner.GetComponent<GalacticMarketComponent>();
|
||||
Orders = Owner.GetComponent<CargoOrderDatabaseComponent>();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>().GetBoundUserInterface(CargoConsoleUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
_powerReceiver = Owner.GetComponent<PowerReceiverComponent>();
|
||||
|
||||
if (!Owner.EnsureComponent(out GalacticMarketComponent _))
|
||||
{
|
||||
Logger.Warning($"Entity {Owner} at {Owner.Transform.MapPosition} had no {nameof(GalacticMarketComponent)}");
|
||||
}
|
||||
|
||||
if (!Owner.EnsureComponent(out CargoOrderDatabaseComponent _))
|
||||
{
|
||||
Logger.Warning($"Entity {Owner} at {Owner.Transform.MapPosition} had no {nameof(GalacticMarketComponent)}");
|
||||
}
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
}
|
||||
|
||||
_cargoConsoleSystem = EntitySystem.Get<CargoConsoleSystem>();
|
||||
BankAccount = _cargoConsoleSystem.StationAccount;
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
}
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads data from YAML
|
||||
/// </summary>
|
||||
@@ -93,8 +107,13 @@ namespace Content.Server.GameObjects.Components.Cargo
|
||||
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out CargoOrderDatabaseComponent? orders))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var message = serverMsg.Message;
|
||||
if (!Orders.ConnectedToDatabase)
|
||||
if (!orders.ConnectedToDatabase)
|
||||
return;
|
||||
if (!Powered)
|
||||
return;
|
||||
@@ -107,45 +126,45 @@ namespace Content.Server.GameObjects.Components.Cargo
|
||||
break;
|
||||
}
|
||||
|
||||
_cargoOrderDataManager.AddOrder(Orders.Database.Id, msg.Requester, msg.Reason, msg.ProductId, msg.Amount, _bankAccount.Id);
|
||||
_cargoOrderDataManager.AddOrder(orders.Database.Id, msg.Requester, msg.Reason, msg.ProductId, msg.Amount, _bankAccount.Id);
|
||||
break;
|
||||
}
|
||||
case CargoConsoleRemoveOrderMessage msg:
|
||||
{
|
||||
_cargoOrderDataManager.RemoveOrder(Orders.Database.Id, msg.OrderNumber);
|
||||
_cargoOrderDataManager.RemoveOrder(orders.Database.Id, msg.OrderNumber);
|
||||
break;
|
||||
}
|
||||
case CargoConsoleApproveOrderMessage msg:
|
||||
{
|
||||
if (_requestOnly ||
|
||||
!Orders.Database.TryGetOrder(msg.OrderNumber, out var order) ||
|
||||
!orders.Database.TryGetOrder(msg.OrderNumber, out var order) ||
|
||||
_bankAccount == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
_prototypeManager.TryIndex(order.ProductId, out CargoProductPrototype product);
|
||||
if (product == null)
|
||||
PrototypeManager.TryIndex(order.ProductId, out CargoProductPrototype product);
|
||||
if (product == null!)
|
||||
break;
|
||||
var capacity = _cargoOrderDataManager.GetCapacity(Orders.Database.Id);
|
||||
var capacity = _cargoOrderDataManager.GetCapacity(orders.Database.Id);
|
||||
if (capacity.CurrentCapacity == capacity.MaxCapacity)
|
||||
break;
|
||||
if (!_cargoConsoleSystem.ChangeBalance(_bankAccount.Id, (-product.PointCost) * order.Amount))
|
||||
break;
|
||||
_cargoOrderDataManager.ApproveOrder(Orders.Database.Id, msg.OrderNumber);
|
||||
_cargoOrderDataManager.ApproveOrder(orders.Database.Id, msg.OrderNumber);
|
||||
UpdateUIState();
|
||||
break;
|
||||
}
|
||||
case CargoConsoleShuttleMessage _:
|
||||
{
|
||||
var approvedOrders = _cargoOrderDataManager.RemoveAndGetApprovedFrom(Orders.Database);
|
||||
Orders.Database.ClearOrderCapacity();
|
||||
var approvedOrders = _cargoOrderDataManager.RemoveAndGetApprovedFrom(orders.Database);
|
||||
orders.Database.ClearOrderCapacity();
|
||||
// TODO replace with shuttle code
|
||||
|
||||
// TEMPORARY loop for spawning stuff on top of console
|
||||
foreach (var order in approvedOrders)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(order.ProductId, out CargoProductPrototype product))
|
||||
if (!PrototypeManager.TryIndex(order.ProductId, out CargoProductPrototype product))
|
||||
continue;
|
||||
for (var i = 0; i < order.Amount; i++)
|
||||
{
|
||||
@@ -166,12 +185,12 @@ namespace Content.Server.GameObjects.Components.Cargo
|
||||
if (!Powered)
|
||||
return;
|
||||
|
||||
_userInterface.Open(actor.playerSession);
|
||||
UserInterface?.Open(actor.playerSession);
|
||||
}
|
||||
|
||||
private void UpdateUIState()
|
||||
{
|
||||
if (_bankAccount == null)
|
||||
if (_bankAccount == null || !Owner.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -180,7 +199,7 @@ namespace Content.Server.GameObjects.Components.Cargo
|
||||
var name = _bankAccount.Name;
|
||||
var balance = _bankAccount.Balance;
|
||||
var capacity = _cargoOrderDataManager.GetCapacity(id);
|
||||
_userInterface.SetState(new CargoConsoleInterfaceState(_requestOnly, id, name, balance, capacity));
|
||||
UserInterface?.SetState(new CargoConsoleInterfaceState(_requestOnly, id, name, balance, capacity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,7 @@ namespace Content.Server.GameObjects.Components.Cargo
|
||||
[RegisterComponent]
|
||||
public class CargoOrderDatabaseComponent : SharedCargoOrderDatabaseComponent
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ICargoOrderDataManager _cargoOrderDataManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly ICargoOrderDataManager _cargoOrderDataManager = default!;
|
||||
|
||||
public CargoOrderDatabase Database { get; set; }
|
||||
public bool ConnectedToDatabase => Database != null;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
@@ -7,6 +9,7 @@ using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Chemistry.ChemMaster;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
@@ -40,24 +43,20 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
[ComponentReference(typeof(IInteractUsing))]
|
||||
public class ChemMasterComponent : SharedChemMasterComponent, IActivate, IInteractUsing, ISolutionChange
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager;
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
|
||||
[ViewVariables] private BoundUserInterface _userInterface;
|
||||
[ViewVariables] private ContainerSlot _beakerContainer;
|
||||
[ViewVariables] private string _packPrototypeId;
|
||||
[ViewVariables] private ContainerSlot _beakerContainer = default!;
|
||||
[ViewVariables] private string _packPrototypeId = "";
|
||||
|
||||
[ViewVariables] private bool HasBeaker => _beakerContainer.ContainedEntity != null;
|
||||
|
||||
[ViewVariables] private bool BufferModeTransfer = true;
|
||||
[ViewVariables] private bool _bufferModeTransfer = true;
|
||||
|
||||
private PowerReceiverComponent _powerReceiver;
|
||||
private bool Powered => _powerReceiver.Powered;
|
||||
private bool Powered => !Owner.TryGetComponent(out PowerReceiverComponent? receiver) || receiver.Powered;
|
||||
|
||||
private readonly SolutionComponent BufferSolution = new SolutionComponent();
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(ChemMasterUiKey.Key);
|
||||
|
||||
/// <summary>
|
||||
/// Shows the serializer how to save/load this components yaml prototype.
|
||||
@@ -77,14 +76,19 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(ChemMasterUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
}
|
||||
|
||||
_beakerContainer =
|
||||
ContainerManagerComponent.Ensure<ContainerSlot>($"{Name}-reagentContainerContainer", Owner);
|
||||
_powerReceiver = Owner.GetComponent<PowerReceiverComponent>();
|
||||
_powerReceiver.OnPowerStateChanged += OnPowerChanged;
|
||||
|
||||
if (Owner.TryGetComponent(out PowerReceiverComponent? receiver))
|
||||
{
|
||||
receiver.OnPowerStateChanged += OnPowerChanged;
|
||||
}
|
||||
|
||||
//BufferSolution = Owner.BufferSolution
|
||||
BufferSolution.Solution = new Solution();
|
||||
@@ -93,7 +97,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
UpdateUserInterface();
|
||||
}
|
||||
|
||||
private void OnPowerChanged(object sender, PowerStateEventArgs e)
|
||||
private void OnPowerChanged(object? sender, PowerStateEventArgs e)
|
||||
{
|
||||
UpdateUserInterface();
|
||||
}
|
||||
@@ -105,6 +109,11 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
/// <param name="obj">A user interface message from the client.</param>
|
||||
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||
{
|
||||
if (obj.Session.AttachedEntity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = (UiActionMessage) obj.Message;
|
||||
var needsPower = msg.action switch
|
||||
{
|
||||
@@ -124,11 +133,11 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
TransferReagent(msg.id, msg.amount, msg.isBuffer);
|
||||
break;
|
||||
case UiAction.Transfer:
|
||||
BufferModeTransfer = true;
|
||||
_bufferModeTransfer = true;
|
||||
UpdateUserInterface();
|
||||
break;
|
||||
case UiAction.Discard:
|
||||
BufferModeTransfer = false;
|
||||
_bufferModeTransfer = false;
|
||||
UpdateUserInterface();
|
||||
break;
|
||||
case UiAction.CreatePills:
|
||||
@@ -147,7 +156,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
/// </summary>
|
||||
/// <param name="playerEntity">The player entity.</param>
|
||||
/// <returns>Returns true if the entity can use the chem master, and false if it cannot.</returns>
|
||||
private bool PlayerCanUseChemMaster(IEntity playerEntity, bool needsPower = true)
|
||||
private bool PlayerCanUseChemMaster(IEntity? playerEntity, bool needsPower = true)
|
||||
{
|
||||
//Need player entity to check if they are still able to use the chem master
|
||||
if (playerEntity == null)
|
||||
@@ -172,18 +181,18 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
if (beaker == null)
|
||||
{
|
||||
return new ChemMasterBoundUserInterfaceState(Powered, false, ReagentUnit.New(0), ReagentUnit.New(0),
|
||||
"", Owner.Name, null, BufferSolution.ReagentList.ToList(), BufferModeTransfer, BufferSolution.CurrentVolume, BufferSolution.MaxVolume);
|
||||
"", Owner.Name, new List<Solution.ReagentQuantity>(), BufferSolution.ReagentList.ToList(), _bufferModeTransfer, BufferSolution.CurrentVolume, BufferSolution.MaxVolume);
|
||||
}
|
||||
|
||||
var solution = beaker.GetComponent<SolutionComponent>();
|
||||
return new ChemMasterBoundUserInterfaceState(Powered, true, solution.CurrentVolume, solution.MaxVolume,
|
||||
beaker.Name, Owner.Name, solution.ReagentList.ToList(), BufferSolution.ReagentList.ToList(), BufferModeTransfer, BufferSolution.CurrentVolume, BufferSolution.MaxVolume);
|
||||
beaker.Name, Owner.Name, solution.ReagentList.ToList(), BufferSolution.ReagentList.ToList(), _bufferModeTransfer, BufferSolution.CurrentVolume, BufferSolution.MaxVolume);
|
||||
}
|
||||
|
||||
private void UpdateUserInterface()
|
||||
{
|
||||
var state = GetUserInterfaceState();
|
||||
_userInterface.SetState(state);
|
||||
UserInterface?.SetState(state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -207,7 +216,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
|
||||
private void TransferReagent(string id, ReagentUnit amount, bool isBuffer)
|
||||
{
|
||||
if (!HasBeaker && BufferModeTransfer) return;
|
||||
if (!HasBeaker && _bufferModeTransfer) return;
|
||||
var beaker = _beakerContainer.ContainedEntity;
|
||||
var beakerSolution = beaker.GetComponent<SolutionComponent>();
|
||||
if (isBuffer)
|
||||
@@ -227,7 +236,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
}
|
||||
|
||||
BufferSolution.Solution.RemoveReagent(id, actualAmount);
|
||||
if (BufferModeTransfer)
|
||||
if (_bufferModeTransfer)
|
||||
{
|
||||
beakerSolution.Solution.AddReagent(id, actualAmount);
|
||||
}
|
||||
@@ -351,22 +360,22 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
/// <param name="args">Data relevant to the event such as the actor which triggered it.</param>
|
||||
void IActivate.Activate(ActivateEventArgs args)
|
||||
{
|
||||
if (!args.User.TryGetComponent(out IActorComponent actor))
|
||||
if (!args.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.User.TryGetComponent(out IHandsComponent hands))
|
||||
if (!args.User.TryGetComponent(out IHandsComponent? hands))
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
_localizationManager.GetString("You have no hands."));
|
||||
Loc.GetString("You have no hands."));
|
||||
return;
|
||||
}
|
||||
|
||||
var activeHandEntity = hands.GetActiveHand?.Owner;
|
||||
if (activeHandEntity == null)
|
||||
{
|
||||
_userInterface.Open(actor.playerSession);
|
||||
UserInterface?.Open(actor.playerSession);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,26 +388,33 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
/// <returns></returns>
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs args)
|
||||
{
|
||||
if (!args.User.TryGetComponent(out IHandsComponent hands))
|
||||
if (!args.User.TryGetComponent(out IHandsComponent? hands))
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
_localizationManager.GetString("You have no hands."));
|
||||
Loc.GetString("You have no hands."));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hands.GetActiveHand == null)
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
Loc.GetString("You have nothing on your hand."));
|
||||
return false;
|
||||
}
|
||||
|
||||
var activeHandEntity = hands.GetActiveHand.Owner;
|
||||
if (activeHandEntity.TryGetComponent<SolutionComponent>(out var solution))
|
||||
{
|
||||
if (HasBeaker)
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
_localizationManager.GetString("This ChemMaster already has a container in it."));
|
||||
Loc.GetString("This ChemMaster already has a container in it."));
|
||||
}
|
||||
else if ((solution.Capabilities & SolutionCaps.FitsInDispenser) == 0) //Close enough to a chem master...
|
||||
{
|
||||
//If it can't fit in the chem master, don't put it in. For example, buckets and mop buckets can't fit.
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
_localizationManager.GetString("That can't fit in the ChemMaster."));
|
||||
Loc.GetString("That can't fit in the ChemMaster."));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -409,7 +425,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
else
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
_localizationManager.GetString("You can't put this in the ChemMaster."));
|
||||
Loc.GetString("You can't put this in the ChemMaster."));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Server.GameObjects.Components.Body.Circulatory;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Utility;
|
||||
@@ -22,9 +23,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
[RegisterComponent]
|
||||
public class InjectorComponent : SharedInjectorComponent, IAfterInteract, IUse
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the injector is able to draw from containers or if it's a single use
|
||||
@@ -53,11 +52,6 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private InjectorToggleMode _toggleState;
|
||||
/// <summary>
|
||||
/// Internal solution container
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private SolutionComponent _internalContents;
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
@@ -69,9 +63,15 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
_internalContents = Owner.GetComponent<SolutionComponent>();
|
||||
_internalContents.Capabilities |= SolutionCaps.Injector;
|
||||
//Set _toggleState based on prototype
|
||||
|
||||
Owner.EnsureComponent<SolutionComponent>();
|
||||
|
||||
if (Owner.TryGetComponent(out SolutionComponent? solution))
|
||||
{
|
||||
solution.Capabilities |= SolutionCaps.Injector;
|
||||
}
|
||||
|
||||
// Set _toggleState based on prototype
|
||||
_toggleState = _injectOnly ? InjectorToggleMode.Inject : InjectorToggleMode.Draw;
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return;
|
||||
|
||||
//Make sure we have the attacking entity
|
||||
if (eventArgs.Target == null || !_internalContents.Injector)
|
||||
if (eventArgs.Target == null || !Owner.TryGetComponent(out SolutionComponent? solution) || !solution.Injector)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -134,7 +134,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
}
|
||||
else //Handle injecting into bloodstream
|
||||
{
|
||||
if (targetEntity.TryGetComponent(out BloodstreamComponent bloodstream) &&
|
||||
if (targetEntity.TryGetComponent(out BloodstreamComponent? bloodstream) &&
|
||||
_toggleState == InjectorToggleMode.Inject)
|
||||
{
|
||||
TryInjectIntoBloodstream(bloodstream, eventArgs.User);
|
||||
@@ -155,7 +155,8 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
|
||||
private void TryInjectIntoBloodstream(BloodstreamComponent targetBloodstream, IEntity user)
|
||||
{
|
||||
if (_internalContents.CurrentVolume == 0)
|
||||
if (!Owner.TryGetComponent(out SolutionComponent? solution) ||
|
||||
solution.CurrentVolume == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -170,7 +171,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
}
|
||||
|
||||
//Move units from attackSolution to targetSolution
|
||||
var removedSolution = _internalContents.SplitSolution(realTransferAmount);
|
||||
var removedSolution = solution.SplitSolution(realTransferAmount);
|
||||
if (!targetBloodstream.TryTransferSolution(removedSolution))
|
||||
{
|
||||
return;
|
||||
@@ -183,7 +184,8 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
|
||||
private void TryInject(SolutionComponent targetSolution, IEntity user)
|
||||
{
|
||||
if (_internalContents.CurrentVolume == 0)
|
||||
if (!Owner.TryGetComponent(out SolutionComponent? solution) ||
|
||||
solution.CurrentVolume == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -198,7 +200,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
}
|
||||
|
||||
//Move units from attackSolution to targetSolution
|
||||
var removedSolution = _internalContents.SplitSolution(realTransferAmount);
|
||||
var removedSolution = solution.SplitSolution(realTransferAmount);
|
||||
if (!targetSolution.TryAddSolution(removedSolution))
|
||||
{
|
||||
return;
|
||||
@@ -211,7 +213,8 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
|
||||
private void TryDraw(SolutionComponent targetSolution, IEntity user)
|
||||
{
|
||||
if (_internalContents.EmptyVolume == 0)
|
||||
if (!Owner.TryGetComponent(out SolutionComponent? solution) ||
|
||||
solution.EmptyVolume == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -227,7 +230,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
|
||||
//Move units from attackSolution to targetSolution
|
||||
var removedSolution = targetSolution.SplitSolution(realTransferAmount);
|
||||
if (!_internalContents.TryAddSolution(removedSolution))
|
||||
if (!solution.TryAddSolution(removedSolution))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -239,7 +242,12 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new InjectorComponentState(_internalContents.CurrentVolume, _internalContents.MaxVolume, _toggleState);
|
||||
Owner.TryGetComponent(out SolutionComponent? solution);
|
||||
|
||||
var currentVolume = solution?.CurrentVolume ?? ReagentUnit.Zero;
|
||||
var maxVolume = solution?.MaxVolume ?? ReagentUnit.Zero;
|
||||
|
||||
return new InjectorComponentState(currentVolume, maxVolume, _toggleState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,8 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
[ComponentReference(typeof(IAfterInteract))]
|
||||
public class PillComponent : FoodComponent, IUse, IAfterInteract
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
|
||||
|
||||
public override string Name => "Pill";
|
||||
|
||||
[ViewVariables]
|
||||
|
||||
@@ -19,10 +19,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
[RegisterComponent]
|
||||
class PourableComponent : Component, IInteractUsing
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager;
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
|
||||
public override string Name => "Pourable";
|
||||
|
||||
@@ -91,7 +88,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
if (realTransferAmount <= 0) //Special message if container is full
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, eventArgs.User,
|
||||
_localizationManager.GetString("Container is full"));
|
||||
Loc.GetString("Container is full"));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -101,7 +98,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
return false;
|
||||
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, eventArgs.User,
|
||||
_localizationManager.GetString("Transferred {0}u", removedSolution.TotalVolume));
|
||||
Loc.GetString("Transferred {0}u", removedSolution.TotalVolume));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
@@ -7,6 +8,7 @@ using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Chemistry.ReagentDispenser;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
@@ -38,14 +40,10 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
[ComponentReference(typeof(IInteractUsing))]
|
||||
public class ReagentDispenserComponent : SharedReagentDispenserComponent, IActivate, IInteractUsing, ISolutionChange
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager;
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
|
||||
[ViewVariables] private BoundUserInterface _userInterface;
|
||||
[ViewVariables] private ContainerSlot _beakerContainer;
|
||||
[ViewVariables] private string _packPrototypeId;
|
||||
[ViewVariables] private ContainerSlot _beakerContainer = default!;
|
||||
[ViewVariables] private string _packPrototypeId = "";
|
||||
|
||||
[ViewVariables] private bool HasBeaker => _beakerContainer.ContainedEntity != null;
|
||||
[ViewVariables] private ReagentUnit _dispenseAmount = ReagentUnit.New(10);
|
||||
@@ -53,9 +51,9 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
[ViewVariables]
|
||||
private SolutionComponent Solution => _beakerContainer.ContainedEntity.GetComponent<SolutionComponent>();
|
||||
|
||||
private PowerReceiverComponent _powerReceiver;
|
||||
private bool Powered => _powerReceiver.Powered;
|
||||
private bool Powered => !Owner.TryGetComponent(out PowerReceiverComponent? receiver) || receiver.Powered;
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(ReagentDispenserUiKey.Key);
|
||||
|
||||
/// <summary>
|
||||
/// Shows the serializer how to save/load this components yaml prototype.
|
||||
@@ -75,14 +73,19 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(ReagentDispenserUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
}
|
||||
|
||||
_beakerContainer =
|
||||
ContainerManagerComponent.Ensure<ContainerSlot>($"{Name}-reagentContainerContainer", Owner);
|
||||
_powerReceiver = Owner.GetComponent<PowerReceiverComponent>();
|
||||
_powerReceiver.OnPowerStateChanged += OnPowerChanged;
|
||||
|
||||
if (Owner.TryGetComponent(out PowerReceiverComponent? receiver))
|
||||
{
|
||||
receiver.OnPowerStateChanged += OnPowerChanged;
|
||||
}
|
||||
|
||||
InitializeFromPrototype();
|
||||
UpdateUserInterface();
|
||||
@@ -108,7 +111,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPowerChanged(object sender, PowerStateEventArgs e)
|
||||
private void OnPowerChanged(object? sender, PowerStateEventArgs e)
|
||||
{
|
||||
UpdateUserInterface();
|
||||
}
|
||||
@@ -120,6 +123,11 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
/// <param name="obj">A user interface message from the client.</param>
|
||||
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||
{
|
||||
if (obj.Session.AttachedEntity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = (UiButtonPressedMessage) obj.Message;
|
||||
var needsPower = msg.Button switch
|
||||
{
|
||||
@@ -175,7 +183,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
/// </summary>
|
||||
/// <param name="playerEntity">The player entity.</param>
|
||||
/// <returns>Returns true if the entity can use the dispenser, and false if it cannot.</returns>
|
||||
private bool PlayerCanUseDispenser(IEntity playerEntity, bool needsPower = true)
|
||||
private bool PlayerCanUseDispenser(IEntity? playerEntity, bool needsPower = true)
|
||||
{
|
||||
//Need player entity to check if they are still able to use the dispenser
|
||||
if (playerEntity == null)
|
||||
@@ -211,7 +219,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
private void UpdateUserInterface()
|
||||
{
|
||||
var state = GetUserInterfaceState();
|
||||
_userInterface.SetState(state);
|
||||
UserInterface?.SetState(state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -265,22 +273,22 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
/// <param name="args">Data relevant to the event such as the actor which triggered it.</param>
|
||||
void IActivate.Activate(ActivateEventArgs args)
|
||||
{
|
||||
if (!args.User.TryGetComponent(out IActorComponent actor))
|
||||
if (!args.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.User.TryGetComponent(out IHandsComponent hands))
|
||||
if (!args.User.TryGetComponent(out IHandsComponent? hands))
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
_localizationManager.GetString("You have no hands."));
|
||||
Loc.GetString("You have no hands."));
|
||||
return;
|
||||
}
|
||||
|
||||
var activeHandEntity = hands.GetActiveHand?.Owner;
|
||||
if (activeHandEntity == null)
|
||||
{
|
||||
_userInterface.Open(actor.playerSession);
|
||||
UserInterface?.Open(actor.playerSession);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,26 +301,33 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
/// <returns></returns>
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs args)
|
||||
{
|
||||
if (!args.User.TryGetComponent(out IHandsComponent hands))
|
||||
if (!args.User.TryGetComponent(out IHandsComponent? hands))
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
_localizationManager.GetString("You have no hands."));
|
||||
Loc.GetString("You have no hands."));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hands.GetActiveHand == null)
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
Loc.GetString("You have nothing on your hand."));
|
||||
return false;
|
||||
}
|
||||
|
||||
var activeHandEntity = hands.GetActiveHand.Owner;
|
||||
if (activeHandEntity.TryGetComponent<SolutionComponent>(out var solution))
|
||||
{
|
||||
if (HasBeaker)
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
_localizationManager.GetString("This dispenser already has a container in it."));
|
||||
Loc.GetString("This dispenser already has a container in it."));
|
||||
}
|
||||
else if ((solution.Capabilities & SolutionCaps.FitsInDispenser) == 0)
|
||||
{
|
||||
//If it can't fit in the dispenser, don't put it in. For example, buckets and mop buckets can't fit.
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
_localizationManager.GetString("That can't fit in the dispenser."));
|
||||
Loc.GetString("That can't fit in the dispenser."));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -323,7 +338,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
else
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
_localizationManager.GetString("You can't put this in the dispenser."));
|
||||
Loc.GetString("You can't put this in the dispenser."));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -29,11 +29,8 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
[RegisterComponent]
|
||||
public class SolutionComponent : SharedSolutionComponent, IExamine
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
[Dependency] private readonly ILocalizationManager _loc;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
|
||||
private IEnumerable<ReactionPrototype> _reactions;
|
||||
private AudioSystem _audioSystem;
|
||||
@@ -276,7 +273,7 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
return;
|
||||
}
|
||||
|
||||
message.AddText(_loc.GetString("Contains:\n"));
|
||||
message.AddText(Loc.GetString("Contains:\n"));
|
||||
if (ReagentList.Count == 0)
|
||||
{
|
||||
message.AddText("Nothing.\n");
|
||||
@@ -303,12 +300,12 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
colorIsh = "Blue";
|
||||
}
|
||||
|
||||
message.AddText(_loc.GetString("A {0} liquid\n", colorIsh));
|
||||
message.AddText(Loc.GetString("A {0} liquid\n", colorIsh));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
message.AddText(_loc.GetString("Unknown reagent: {0}u\n", reagent.Quantity));
|
||||
message.AddText(Loc.GetString("Unknown reagent: {0}u\n", reagent.Quantity));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
#nullable enable
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Chemistry;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
@@ -11,28 +13,27 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
[RegisterComponent]
|
||||
public class TransformableContainerComponent : Component, ISolutionChange
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override string Name => "TransformableContainer";
|
||||
|
||||
private bool _transformed = false;
|
||||
public bool Transformed { get => _transformed; }
|
||||
private SpriteSpecifier? _initialSprite;
|
||||
private string _initialName = default!;
|
||||
private string _initialDescription = default!;
|
||||
private ReagentPrototype? _currentReagent;
|
||||
|
||||
private SpriteSpecifier _initialSprite;
|
||||
private string _initialName;
|
||||
private string _initialDescription;
|
||||
private SpriteComponent _sprite;
|
||||
|
||||
private ReagentPrototype _currentReagent;
|
||||
public bool Transformed { get; private set; }
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_sprite = Owner.GetComponent<SpriteComponent>();
|
||||
_initialSprite = new SpriteSpecifier.Rsi(new ResourcePath(_sprite.BaseRSIPath), "icon");
|
||||
if (Owner.TryGetComponent(out SpriteComponent? sprite) &&
|
||||
sprite.BaseRSIPath != null)
|
||||
{
|
||||
_initialSprite = new SpriteSpecifier.Rsi(new ResourcePath(sprite.BaseRSIPath), "icon");
|
||||
}
|
||||
|
||||
_initialName = Owner.Name;
|
||||
_initialDescription = Owner.Description;
|
||||
}
|
||||
@@ -40,14 +41,27 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
Owner.GetComponent<SolutionComponent>().Capabilities |= SolutionCaps.FitsInDispenser;;
|
||||
|
||||
if (!Owner.EnsureComponent(out SolutionComponent solution))
|
||||
{
|
||||
Logger.Warning(
|
||||
$"Entity {Owner.Name} at {Owner.Transform.MapPosition} didn't have a {nameof(SolutionComponent)}");
|
||||
}
|
||||
|
||||
solution.Capabilities |= SolutionCaps.FitsInDispenser;
|
||||
}
|
||||
|
||||
public void CancelTransformation()
|
||||
{
|
||||
_currentReagent = null;
|
||||
_transformed = false;
|
||||
_sprite.LayerSetSprite(0, _initialSprite);
|
||||
Transformed = false;
|
||||
|
||||
if (Owner.TryGetComponent(out SpriteComponent? sprite) &&
|
||||
_initialSprite != null)
|
||||
{
|
||||
sprite.LayerSetSprite(0, _initialSprite);
|
||||
}
|
||||
|
||||
Owner.Name = _initialName;
|
||||
Owner.Description = _initialDescription;
|
||||
}
|
||||
@@ -76,11 +90,16 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
!string.IsNullOrWhiteSpace(proto.SpriteReplacementPath))
|
||||
{
|
||||
var spriteSpec = new SpriteSpecifier.Rsi(new ResourcePath("Objects/Drinks/" + proto.SpriteReplacementPath),"icon");
|
||||
_sprite.LayerSetSprite(0, spriteSpec);
|
||||
|
||||
if (Owner.TryGetComponent(out SpriteComponent? sprite))
|
||||
{
|
||||
sprite?.LayerSetSprite(0, spriteSpec);
|
||||
}
|
||||
|
||||
Owner.Name = proto.Name + " glass";
|
||||
Owner.Description = proto.Description;
|
||||
_currentReagent = proto;
|
||||
_transformed = true;
|
||||
Transformed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -19,8 +20,6 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
public override string Name => "Vapor";
|
||||
|
||||
[ViewVariables]
|
||||
private SolutionComponent _contents;
|
||||
[ViewVariables]
|
||||
private ReagentUnit _transferAmount;
|
||||
|
||||
@@ -28,11 +27,15 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
private Vector2 _direction;
|
||||
private float _velocity;
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_contents = Owner.GetComponent<SolutionComponent>();
|
||||
|
||||
if (!Owner.EnsureComponent(out SolutionComponent _))
|
||||
{
|
||||
Logger.Warning(
|
||||
$"Entity {Owner.Name} at {Owner.Transform.MapPosition} didn't have a {nameof(SolutionComponent)}");
|
||||
}
|
||||
}
|
||||
|
||||
public void Start(Vector2 dir, float velocity)
|
||||
@@ -56,6 +59,9 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SolutionComponent contents))
|
||||
return;
|
||||
|
||||
if (!_running)
|
||||
return;
|
||||
|
||||
@@ -70,11 +76,11 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
foreach (var tile in tiles)
|
||||
{
|
||||
var pos = tile.GridIndices.ToGridCoordinates(_mapManager, tile.GridIndex);
|
||||
SpillHelper.SpillAt(pos, _contents.SplitSolution(amount), "PuddleSmear", false); //make non PuddleSmear?
|
||||
SpillHelper.SpillAt(pos, contents.SplitSolution(amount), "PuddleSmear", false); //make non PuddleSmear?
|
||||
}
|
||||
}
|
||||
|
||||
if (_contents.CurrentVolume == 0)
|
||||
if (contents.CurrentVolume == 0)
|
||||
{
|
||||
// Delete this
|
||||
Owner.Delete();
|
||||
@@ -87,7 +93,14 @@ namespace Content.Server.GameObjects.Components.Chemistry
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var result = _contents.TryAddSolution(solution);
|
||||
|
||||
if (!Owner.TryGetComponent(out SolutionComponent contents))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var result = contents.TryAddSolution(solution);
|
||||
|
||||
if (!result)
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
||||
#nullable enable
|
||||
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.GameObjects.Components.Command;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
@@ -8,6 +10,7 @@ using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Command
|
||||
{
|
||||
@@ -15,22 +18,22 @@ namespace Content.Server.GameObjects.Components.Command
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
public class CommunicationsConsoleComponent : SharedCommunicationsConsoleComponent, IActivate
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IEntitySystemManager _entitySystemManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
|
||||
private bool Powered => !Owner.TryGetComponent(out PowerReceiverComponent? receiver) || receiver.Powered;
|
||||
|
||||
private BoundUserInterface _userInterface;
|
||||
private PowerReceiverComponent _powerReceiver;
|
||||
private bool Powered => _powerReceiver.Powered;
|
||||
private RoundEndSystem RoundEndSystem => _entitySystemManager.GetEntitySystem<RoundEndSystem>();
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(CommunicationsConsoleUiKey.Key);
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>().GetBoundUserInterface(CommunicationsConsoleUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
_powerReceiver = Owner.GetComponent<PowerReceiverComponent>();
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
}
|
||||
|
||||
RoundEndSystem.OnRoundEndCountdownStarted += UpdateBoundInterface;
|
||||
RoundEndSystem.OnRoundEndCountdownCancelled += UpdateBoundInterface;
|
||||
@@ -39,7 +42,7 @@ namespace Content.Server.GameObjects.Components.Command
|
||||
|
||||
private void UpdateBoundInterface()
|
||||
{
|
||||
_userInterface.SetState(new CommunicationsConsoleInterfaceState(RoundEndSystem.ExpectedCountdownEnd));
|
||||
UserInterface?.SetState(new CommunicationsConsoleInterfaceState(RoundEndSystem.ExpectedCountdownEnd));
|
||||
}
|
||||
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||
@@ -58,12 +61,12 @@ namespace Content.Server.GameObjects.Components.Command
|
||||
|
||||
public void OpenUserInterface(IPlayerSession session)
|
||||
{
|
||||
_userInterface.Open(session);
|
||||
UserInterface?.Open(session);
|
||||
}
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent? actor))
|
||||
return;
|
||||
|
||||
if (!Powered)
|
||||
|
||||
@@ -28,10 +28,8 @@ namespace Content.Server.GameObjects.Components.Conveyor
|
||||
[RegisterComponent]
|
||||
public class ConveyorComponent : Component, IInteractUsing
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override string Name => "Conveyor";
|
||||
|
||||
|
||||
@@ -21,10 +21,8 @@ namespace Content.Server.GameObjects.Components.Damage
|
||||
[ComponentReference(typeof(IDamageableComponent))]
|
||||
public class BreakableComponent : RuinableComponent, IExAct
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
|
||||
[Dependency] private readonly IRobustRandom _random;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
public override string Name => "Breakable";
|
||||
|
||||
@@ -41,8 +39,6 @@ namespace Content.Server.GameObjects.Components.Damage
|
||||
switch (eventArgs.Severity)
|
||||
{
|
||||
case ExplosionSeverity.Destruction:
|
||||
PerformDestruction();
|
||||
break;
|
||||
case ExplosionSeverity.Heavy:
|
||||
PerformDestruction();
|
||||
break;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.Interactable;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Interactable;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -27,12 +28,6 @@ namespace Content.Server.GameObjects.Components.Damage
|
||||
serializer.DataField(ref _tools, "tools", new List<ToolQuality>());
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Owner.EnsureComponent<DestructibleComponent>();
|
||||
}
|
||||
|
||||
public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Using.TryGetComponent<ToolComponent>(out var tool))
|
||||
@@ -56,7 +51,7 @@ namespace Content.Server.GameObjects.Components.Damage
|
||||
|
||||
protected bool CallDamage(InteractUsingEventArgs eventArgs, ToolComponent tool)
|
||||
{
|
||||
if (eventArgs.Target.TryGetComponent<DestructibleComponent>(out var damageable))
|
||||
if (eventArgs.Target.TryGetComponent<IDamageableComponent>(out var damageable))
|
||||
{
|
||||
damageable.ChangeDamage(tool.HasQuality(ToolQuality.Welding)
|
||||
? DamageType.Heat
|
||||
|
||||
@@ -16,9 +16,7 @@ namespace Content.Server.GameObjects.Components.Damage
|
||||
[ComponentReference(typeof(IDamageableComponent))]
|
||||
public class DestructibleComponent : RuinableComponent, IDestroyAct
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
|
||||
protected ActSystem ActSystem;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Damage
|
||||
{
|
||||
@@ -18,13 +17,6 @@ namespace Content.Server.GameObjects.Components.Damage
|
||||
{
|
||||
private DamageState _currentDamageState;
|
||||
|
||||
/// <summary>
|
||||
/// How much HP this component can sustain before triggering
|
||||
/// <see cref="PerformDestruction"/>.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int MaxHp { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sound played upon destruction.
|
||||
/// </summary>
|
||||
@@ -35,29 +27,24 @@ namespace Content.Server.GameObjects.Components.Damage
|
||||
|
||||
public override DamageState CurrentDamageState => _currentDamageState;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
HealthChangedEvent += OnHealthChanged;
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(this, ruinable => ruinable.MaxHp, "maxHP", 100);
|
||||
serializer.DataReadWriteFunction(
|
||||
"deadThreshold",
|
||||
100,
|
||||
t => DeadThreshold = t ,
|
||||
() => DeadThreshold ?? -1);
|
||||
|
||||
serializer.DataField(this, ruinable => ruinable.DestroySound, "destroySound", string.Empty);
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
protected override void EnterState(DamageState state)
|
||||
{
|
||||
base.OnRemove();
|
||||
HealthChangedEvent -= OnHealthChanged;
|
||||
}
|
||||
base.EnterState(state);
|
||||
|
||||
private void OnHealthChanged(HealthChangedEventArgs e)
|
||||
{
|
||||
if (CurrentDamageState != DamageState.Dead && TotalDamage >= MaxHp)
|
||||
if (state == DamageState.Dead)
|
||||
{
|
||||
PerformDestruction();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Disposal
|
||||
{
|
||||
@@ -9,6 +13,8 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
[ComponentReference(typeof(IDisposalTubeComponent))]
|
||||
public class DisposalEntryComponent : DisposalTubeComponent
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
private const string HolderPrototypeId = "DisposalHolder";
|
||||
|
||||
public override string Name => "DisposalEntry";
|
||||
@@ -43,8 +49,19 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
return new[] {Owner.Transform.LocalRotation.GetDir()};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ejects contents when they come from the same direction the entry is facing.
|
||||
/// </summary>
|
||||
public override Direction NextDirection(DisposalHolderComponent holder)
|
||||
{
|
||||
if (holder.PreviousTube != null && DirectionTo(holder.PreviousTube) == ConnectableDirections()[0])
|
||||
{
|
||||
var invalidDirections = new Direction[] { ConnectableDirections()[0], Direction.Invalid };
|
||||
var directions = System.Enum.GetValues(typeof(Direction))
|
||||
.Cast<Direction>().Except(invalidDirections).ToList();
|
||||
return _random.Pick<Direction>(directions);
|
||||
}
|
||||
|
||||
return ConnectableDirections()[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,9 +14,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
[ComponentReference(typeof(IDisposalTubeComponent))]
|
||||
public class DisposalJunctionComponent : DisposalTubeComponent
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IRobustRandom _random;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The angles to connect to.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.Interfaces;
|
||||
#nullable enable
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
@@ -16,6 +17,7 @@ using Robust.Shared.Maths;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Utility;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalRouterComponent;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Disposal
|
||||
@@ -25,22 +27,19 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
[ComponentReference(typeof(IDisposalTubeComponent))]
|
||||
public class DisposalRouterComponent : DisposalJunctionComponent, IActivate
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
public override string Name => "DisposalRouter";
|
||||
|
||||
[ViewVariables]
|
||||
private BoundUserInterface _userInterface;
|
||||
|
||||
[ViewVariables]
|
||||
private HashSet<string> _tags;
|
||||
private readonly HashSet<string> _tags = new HashSet<string>();
|
||||
|
||||
[ViewVariables]
|
||||
public bool Anchored =>
|
||||
!Owner.TryGetComponent(out CollidableComponent collidable) ||
|
||||
!Owner.TryGetComponent(out ICollidableComponent? collidable) ||
|
||||
collidable.Anchored;
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(DisposalRouterUiKey.Key);
|
||||
|
||||
public override Direction NextDirection(DisposalHolderComponent holder)
|
||||
{
|
||||
var directions = ConnectableDirections();
|
||||
@@ -53,15 +52,14 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
return Owner.Transform.LocalRotation.GetDir();
|
||||
}
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(DisposalRouterUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
|
||||
_tags = new HashSet<string>();
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
}
|
||||
|
||||
UpdateUserInterface();
|
||||
}
|
||||
@@ -73,6 +71,11 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
/// <param name="obj">A user interface message from the client.</param>
|
||||
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||
{
|
||||
if (obj.Session.AttachedEntity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = (UiActionMessage) obj.Message;
|
||||
|
||||
if (!PlayerCanUseDisposalTagger(obj.Session.AttachedEntity))
|
||||
@@ -112,10 +115,10 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
/// <summary>
|
||||
/// Gets component data to be used to update the user interface client-side.
|
||||
/// </summary>
|
||||
/// <returns>Returns a <see cref="SharedDisposalRouterComponent.DisposalRouterBoundUserInterfaceState"/></returns>
|
||||
/// <returns>Returns a <see cref="DisposalRouterUserInterfaceState"/></returns>
|
||||
private DisposalRouterUserInterfaceState GetUserInterfaceState()
|
||||
{
|
||||
if(_tags == null || _tags.Count <= 0)
|
||||
if(_tags.Count <= 0)
|
||||
{
|
||||
return new DisposalRouterUserInterfaceState("");
|
||||
}
|
||||
@@ -136,7 +139,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
private void UpdateUserInterface()
|
||||
{
|
||||
var state = GetUserInterfaceState();
|
||||
_userInterface.SetState(state);
|
||||
UserInterface?.SetState(state);
|
||||
}
|
||||
|
||||
private void ClickSound()
|
||||
@@ -150,12 +153,12 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
/// <param name="args">Data relevant to the event such as the actor which triggered it.</param>
|
||||
void IActivate.Activate(ActivateEventArgs args)
|
||||
{
|
||||
if (!args.User.TryGetComponent(out IActorComponent actor))
|
||||
if (!args.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.User.TryGetComponent(out IHandsComponent hands))
|
||||
if (!args.User.TryGetComponent(out IHandsComponent? hands))
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
Loc.GetString("You have no hands."));
|
||||
@@ -166,13 +169,13 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
if (activeHandEntity == null)
|
||||
{
|
||||
UpdateUserInterface();
|
||||
_userInterface.Open(actor.playerSession);
|
||||
UserInterface?.Open(actor.playerSession);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
_userInterface.CloseAll();
|
||||
UserInterface?.CloseAll();
|
||||
base.OnRemove();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using Content.Server.Interfaces;
|
||||
#nullable enable
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Items;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
@@ -23,35 +25,33 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
[ComponentReference(typeof(IDisposalTubeComponent))]
|
||||
public class DisposalTaggerComponent : DisposalTransitComponent, IActivate
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
public override string Name => "DisposalTagger";
|
||||
|
||||
[ViewVariables]
|
||||
private BoundUserInterface _userInterface;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private string _tag = "";
|
||||
|
||||
[ViewVariables]
|
||||
public bool Anchored =>
|
||||
!Owner.TryGetComponent(out CollidableComponent collidable) ||
|
||||
!Owner.TryGetComponent(out CollidableComponent? collidable) ||
|
||||
collidable.Anchored;
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(DisposalTaggerUiKey.Key);
|
||||
|
||||
public override Direction NextDirection(DisposalHolderComponent holder)
|
||||
{
|
||||
holder.Tags.Add(_tag);
|
||||
return base.NextDirection(holder);
|
||||
}
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(DisposalTaggerUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
}
|
||||
|
||||
UpdateUserInterface();
|
||||
}
|
||||
@@ -70,7 +70,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
//Check for correct message and ignore maleformed strings
|
||||
if (msg.Action == UiAction.Ok && TagRegex.IsMatch(msg.Tag))
|
||||
{
|
||||
{
|
||||
_tag = msg.Tag;
|
||||
ClickSound();
|
||||
}
|
||||
@@ -81,7 +81,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
/// </summary>
|
||||
/// <param name="playerEntity">The player entity.</param>
|
||||
/// <returns>Returns true if the entity can use the configuration interface, and false if it cannot.</returns>
|
||||
private bool PlayerCanUseDisposalTagger(IEntity playerEntity)
|
||||
private bool PlayerCanUseDisposalTagger(IEntity? playerEntity)
|
||||
{
|
||||
//Need player entity to check if they are still able to use the configuration interface
|
||||
if (playerEntity == null)
|
||||
@@ -98,7 +98,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
/// <summary>
|
||||
/// Gets component data to be used to update the user interface client-side.
|
||||
/// </summary>
|
||||
/// <returns>Returns a <see cref="SharedDisposalTaggerComponent.DisposalTaggerBoundUserInterfaceState"/></returns>
|
||||
/// <returns>Returns a <see cref="DisposalTaggerUserInterfaceState"/></returns>
|
||||
private DisposalTaggerUserInterfaceState GetUserInterfaceState()
|
||||
{
|
||||
return new DisposalTaggerUserInterfaceState(_tag);
|
||||
@@ -107,7 +107,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
private void UpdateUserInterface()
|
||||
{
|
||||
var state = GetUserInterfaceState();
|
||||
_userInterface.SetState(state);
|
||||
UserInterface?.SetState(state);
|
||||
}
|
||||
|
||||
private void ClickSound()
|
||||
@@ -121,12 +121,12 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
/// <param name="args">Data relevant to the event such as the actor which triggered it.</param>
|
||||
void IActivate.Activate(ActivateEventArgs args)
|
||||
{
|
||||
if (!args.User.TryGetComponent(out IActorComponent actor))
|
||||
if (!args.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.User.TryGetComponent(out IHandsComponent hands))
|
||||
if (!args.User.TryGetComponent(out IHandsComponent? hands))
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User,
|
||||
Loc.GetString("You have no hands."));
|
||||
@@ -137,14 +137,14 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
if (activeHandEntity == null)
|
||||
{
|
||||
UpdateUserInterface();
|
||||
_userInterface.Open(actor.playerSession);
|
||||
UserInterface?.Open(actor.playerSession);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
_userInterface.CloseAll();
|
||||
UserInterface?.CloseAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Disposal
|
||||
{
|
||||
// TODO: Make unanchored pipes pullable
|
||||
public abstract class DisposalTubeComponent : Component, IDisposalTubeComponent, IBreakAct
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
@@ -69,21 +68,11 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
{
|
||||
var nextDirection = NextDirection(holder);
|
||||
var snapGrid = Owner.GetComponent<SnapGridComponent>();
|
||||
var oppositeDirection = new Angle(nextDirection.ToAngle().Theta + Math.PI).GetDir();
|
||||
var tube = snapGrid
|
||||
.GetInDir(nextDirection)
|
||||
.Select(x => x.TryGetComponent(out IDisposalTubeComponent? c) ? c : null)
|
||||
.FirstOrDefault(x => x != null && x != this);
|
||||
|
||||
if (tube == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var oppositeDirection = new Angle(nextDirection.ToAngle().Theta + Math.PI).GetDir();
|
||||
if (!tube.CanConnect(oppositeDirection, this))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
.FirstOrDefault(x => x != null && x != this && x.CanConnect(oppositeDirection, this));
|
||||
|
||||
return tube;
|
||||
}
|
||||
@@ -192,6 +181,8 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
return;
|
||||
}
|
||||
|
||||
collidable.CanCollide = !collidable.Anchored;
|
||||
|
||||
if (collidable.Anchored)
|
||||
{
|
||||
OnAnchor();
|
||||
@@ -230,6 +221,8 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
var collidable = Owner.EnsureComponent<CollidableComponent>();
|
||||
|
||||
collidable.AnchoredChanged += AnchoredChanged;
|
||||
|
||||
collidable.CanCollide = !collidable.Anchored;
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
|
||||
@@ -7,8 +7,10 @@ using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
||||
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.Body;
|
||||
using Content.Shared.GameObjects.Components.Disposal;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
@@ -29,6 +31,7 @@ using Robust.Shared.Interfaces.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;
|
||||
using Timer = Robust.Shared.Timers.Timer;
|
||||
@@ -36,13 +39,12 @@ using Timer = Robust.Shared.Timers.Timer;
|
||||
namespace Content.Server.GameObjects.Components.Disposal
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedDisposalUnitComponent))]
|
||||
[ComponentReference(typeof(IInteractUsing))]
|
||||
public class DisposalUnitComponent : SharedDisposalUnitComponent, IInteractHand, IInteractUsing, IDragDropOn
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override string Name => "DisposalUnit";
|
||||
|
||||
@@ -67,6 +69,12 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
[ViewVariables]
|
||||
private TimeSpan _automaticEngageTime;
|
||||
|
||||
[ViewVariables]
|
||||
private TimeSpan _flushDelay;
|
||||
|
||||
[ViewVariables]
|
||||
private float _entryDelay;
|
||||
|
||||
/// <summary>
|
||||
/// Token used to cancel the automatic engage of a disposal unit
|
||||
/// after an entity enters it.
|
||||
@@ -81,9 +89,6 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
[ViewVariables] public IReadOnlyList<IEntity> ContainedEntities => _container.ContainedEntities;
|
||||
|
||||
[ViewVariables]
|
||||
private BoundUserInterface _userInterface = default!;
|
||||
|
||||
[ViewVariables]
|
||||
public bool Powered =>
|
||||
!Owner.TryGetComponent(out PowerReceiverComponent? receiver) ||
|
||||
@@ -115,6 +120,15 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(DisposalUnitUiKey.Key);
|
||||
|
||||
private DisposalUnitBoundUserInterfaceState? _lastUiState;
|
||||
|
||||
/// <summary>
|
||||
/// Store the translated state.
|
||||
/// </summary>
|
||||
private (PressureState State, string Localized) _locState;
|
||||
|
||||
public bool CanInsert(IEntity entity)
|
||||
{
|
||||
if (!Anchored)
|
||||
@@ -161,19 +175,40 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
if (entity.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
_userInterface.Close(actor.playerSession);
|
||||
UserInterface?.Close(actor.playerSession);
|
||||
}
|
||||
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
public bool TryInsert(IEntity entity)
|
||||
public async Task<bool> TryInsert(IEntity entity, IEntity? user = default)
|
||||
{
|
||||
if (!CanInsert(entity) || !_container.Insert(entity))
|
||||
{
|
||||
if (!CanInsert(entity))
|
||||
return false;
|
||||
|
||||
if (user != null && _entryDelay > 0f)
|
||||
{
|
||||
var doAfterSystem = EntitySystem.Get<DoAfterSystem>();
|
||||
|
||||
var doAfterArgs = new DoAfterEventArgs(user, _entryDelay, default, Owner)
|
||||
{
|
||||
BreakOnDamage = true,
|
||||
BreakOnStun = true,
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
NeedHand = false,
|
||||
};
|
||||
|
||||
var result = await doAfterSystem.DoAfter(doAfterArgs);
|
||||
|
||||
if (result == DoAfterStatus.Cancelled)
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if (!_container.Insert(entity))
|
||||
return false;
|
||||
|
||||
AfterInsert(entity);
|
||||
|
||||
return true;
|
||||
@@ -218,9 +253,9 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
{
|
||||
Engaged ^= true;
|
||||
|
||||
if (Engaged)
|
||||
if (Engaged && CanFlush())
|
||||
{
|
||||
TryFlush();
|
||||
Timer.Spawn(_flushDelay, () => TryFlush());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,17 +319,35 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
private DisposalUnitBoundUserInterfaceState GetInterfaceState()
|
||||
{
|
||||
var state = Loc.GetString($"{State}");
|
||||
return new DisposalUnitBoundUserInterfaceState(Owner.Name, state, _pressure, Powered, Engaged);
|
||||
string stateString;
|
||||
|
||||
if (_locState.State != State)
|
||||
{
|
||||
stateString = Loc.GetString($"{State}");
|
||||
_locState = (State, stateString);
|
||||
}
|
||||
else
|
||||
{
|
||||
stateString = _locState.Localized;
|
||||
}
|
||||
|
||||
return new DisposalUnitBoundUserInterfaceState(Owner.Name, stateString, _pressure, Powered, Engaged);
|
||||
}
|
||||
|
||||
private void UpdateInterface()
|
||||
{
|
||||
var state = GetInterfaceState();
|
||||
_userInterface.SetState(state);
|
||||
|
||||
if (_lastUiState != null && _lastUiState.Equals(state))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lastUiState = state;
|
||||
UserInterface?.SetState(state);
|
||||
}
|
||||
|
||||
private bool PlayerCanUse(IEntity player)
|
||||
private bool PlayerCanUse(IEntity? player)
|
||||
{
|
||||
if (player == null)
|
||||
{
|
||||
@@ -402,8 +455,9 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
: LightState.Ready);
|
||||
}
|
||||
|
||||
public void Update(float frameTime)
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
if (!Powered)
|
||||
{
|
||||
return;
|
||||
@@ -461,10 +515,16 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
() => (int) _automaticEngageTime.TotalSeconds);
|
||||
|
||||
serializer.DataReadWriteFunction(
|
||||
"automaticEngageTime",
|
||||
30,
|
||||
seconds => _automaticEngageTime = TimeSpan.FromSeconds(seconds),
|
||||
() => (int) _automaticEngageTime.TotalSeconds);
|
||||
"flushDelay",
|
||||
3,
|
||||
seconds => _flushDelay = TimeSpan.FromSeconds(seconds),
|
||||
() => (int) _flushDelay.TotalSeconds);
|
||||
|
||||
serializer.DataReadWriteFunction(
|
||||
"entryDelay",
|
||||
0.5f,
|
||||
seconds => _entryDelay = seconds,
|
||||
() => (int) _entryDelay);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
@@ -472,9 +532,11 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
base.Initialize();
|
||||
|
||||
_container = ContainerManagerComponent.Ensure<Container>(Name, Owner);
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(DisposalUnitUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
}
|
||||
|
||||
UpdateInterface();
|
||||
}
|
||||
@@ -483,10 +545,15 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
Owner.EnsureComponent<AnchorableComponent>();
|
||||
if(!Owner.HasComponent<AnchorableComponent>())
|
||||
{
|
||||
Logger.WarningS("VitalComponentMissing", $"Disposal unit {Owner.Uid} is missing an anchorable component");
|
||||
}
|
||||
|
||||
var collidable = Owner.EnsureComponent<CollidableComponent>();
|
||||
collidable.AnchoredChanged += UpdateVisualState;
|
||||
if (Owner.TryGetComponent(out CollidableComponent? collidable))
|
||||
{
|
||||
collidable.AnchoredChanged += UpdateVisualState;
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out PowerReceiverComponent? receiver))
|
||||
{
|
||||
@@ -513,7 +580,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
_container.ForceRemove(entity);
|
||||
}
|
||||
|
||||
_userInterface.CloseAll();
|
||||
UserInterface?.CloseAll();
|
||||
|
||||
_automaticEngageToken?.Cancel();
|
||||
_automaticEngageToken = null;
|
||||
@@ -571,7 +638,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
return false;
|
||||
}
|
||||
|
||||
_userInterface.Open(actor.playerSession);
|
||||
UserInterface?.Open(actor.playerSession);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -587,7 +654,8 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
bool IDragDropOn.DragDropOn(DragDropEventArgs eventArgs)
|
||||
{
|
||||
return TryInsert(eventArgs.Dropped);
|
||||
_ = TryInsert(eventArgs.Dropped, eventArgs.User);
|
||||
return true;
|
||||
}
|
||||
|
||||
[Verb]
|
||||
@@ -609,7 +677,7 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
|
||||
protected override void Activate(IEntity user, DisposalUnitComponent component)
|
||||
{
|
||||
component.TryInsert(user);
|
||||
_ = component.TryInsert(user, user);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.Interactable;
|
||||
@@ -34,10 +35,7 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
/// </summary>
|
||||
private static readonly TimeSpan PowerWiresTimeout = TimeSpan.FromSeconds(5.0);
|
||||
|
||||
private PowerReceiverComponent _powerReceiver;
|
||||
private WiresComponent _wires;
|
||||
|
||||
private CancellationTokenSource _powerWiresPulsedTimerCancel;
|
||||
private CancellationTokenSource _powerWiresPulsedTimerCancel = new CancellationTokenSource();
|
||||
|
||||
private bool _powerWiresPulsed;
|
||||
|
||||
@@ -89,13 +87,15 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
|
||||
private void UpdateWiresStatus()
|
||||
{
|
||||
WiresComponent? wires;
|
||||
var powerLight = new StatusLightData(Color.Yellow, StatusLightState.On, "POWR");
|
||||
if (PowerWiresPulsed)
|
||||
{
|
||||
powerLight = new StatusLightData(Color.Yellow, StatusLightState.BlinkingFast, "POWR");
|
||||
}
|
||||
else if (_wires.IsWireCut(Wires.MainPower) &&
|
||||
_wires.IsWireCut(Wires.BackupPower))
|
||||
else if (Owner.TryGetComponent(out wires) &&
|
||||
wires.IsWireCut(Wires.MainPower) &&
|
||||
wires.IsWireCut(Wires.BackupPower))
|
||||
{
|
||||
powerLight = new StatusLightData(Color.Red, StatusLightState.On, "POWR");
|
||||
}
|
||||
@@ -114,12 +114,17 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
var safetyStatus =
|
||||
new StatusLightData(Color.Red, Safety ? StatusLightState.On : StatusLightState.Off, "SAFE");
|
||||
|
||||
_wires.SetStatus(AirlockWireStatus.PowerIndicator, powerLight);
|
||||
_wires.SetStatus(AirlockWireStatus.BoltIndicator, boltStatus);
|
||||
_wires.SetStatus(AirlockWireStatus.BoltLightIndicator, boltLightsStatus);
|
||||
_wires.SetStatus(AirlockWireStatus.AIControlIndicator, new StatusLightData(Color.Purple, StatusLightState.BlinkingSlow, "AICT"));
|
||||
_wires.SetStatus(AirlockWireStatus.TimingIndicator, timingStatus);
|
||||
_wires.SetStatus(AirlockWireStatus.SafetyIndicator, safetyStatus);
|
||||
if (!Owner.TryGetComponent(out wires))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wires.SetStatus(AirlockWireStatus.PowerIndicator, powerLight);
|
||||
wires.SetStatus(AirlockWireStatus.BoltIndicator, boltStatus);
|
||||
wires.SetStatus(AirlockWireStatus.BoltLightIndicator, boltLightsStatus);
|
||||
wires.SetStatus(AirlockWireStatus.AIControlIndicator, new StatusLightData(Color.Purple, StatusLightState.BlinkingSlow, "AICT"));
|
||||
wires.SetStatus(AirlockWireStatus.TimingIndicator, timingStatus);
|
||||
wires.SetStatus(AirlockWireStatus.SafetyIndicator, safetyStatus);
|
||||
/*
|
||||
_wires.SetStatus(6, powerLight);
|
||||
_wires.SetStatus(7, powerLight);
|
||||
@@ -131,26 +136,45 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
|
||||
private void UpdatePowerCutStatus()
|
||||
{
|
||||
_powerReceiver.PowerDisabled = PowerWiresPulsed ||
|
||||
_wires.IsWireCut(Wires.MainPower) ||
|
||||
_wires.IsWireCut(Wires.BackupPower);
|
||||
if (!Owner.TryGetComponent(out PowerReceiverComponent? receiver))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (PowerWiresPulsed)
|
||||
{
|
||||
receiver.PowerDisabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Owner.TryGetComponent(out WiresComponent? wires))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
receiver.PowerDisabled =
|
||||
wires.IsWireCut(Wires.MainPower) ||
|
||||
wires.IsWireCut(Wires.BackupPower);
|
||||
}
|
||||
|
||||
private void UpdateBoltLightStatus()
|
||||
{
|
||||
if (Owner.TryGetComponent(out AppearanceComponent appearance))
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(DoorVisuals.BoltLights, BoltLightsVisible);
|
||||
}
|
||||
}
|
||||
|
||||
protected override DoorState State
|
||||
public override DoorState State
|
||||
{
|
||||
set
|
||||
protected set
|
||||
{
|
||||
base.State = value;
|
||||
// Only show the maintenance panel if the airlock is closed
|
||||
_wires.IsPanelVisible = value != DoorState.Open;
|
||||
if (Owner.TryGetComponent(out WiresComponent? wires))
|
||||
{
|
||||
wires.IsPanelVisible = value != DoorState.Open;
|
||||
}
|
||||
// If the door is closed, we should look if the bolt was locked while closing
|
||||
UpdateBoltLightStatus();
|
||||
}
|
||||
@@ -159,25 +183,32 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_powerReceiver = Owner.GetComponent<PowerReceiverComponent>();
|
||||
_wires = Owner.GetComponent<WiresComponent>();
|
||||
|
||||
_powerReceiver.OnPowerStateChanged += PowerDeviceOnOnPowerStateChanged;
|
||||
if (Owner.TryGetComponent(out AppearanceComponent appearance))
|
||||
if (Owner.TryGetComponent(out PowerReceiverComponent? receiver))
|
||||
{
|
||||
appearance.SetData(DoorVisuals.Powered, _powerReceiver.Powered);
|
||||
receiver.OnPowerStateChanged += PowerDeviceOnOnPowerStateChanged;
|
||||
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
|
||||
appearance.SetData(DoorVisuals.Powered, receiver.Powered);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
_powerReceiver.OnPowerStateChanged -= PowerDeviceOnOnPowerStateChanged;
|
||||
if (Owner.TryGetComponent(out PowerReceiverComponent? receiver))
|
||||
{
|
||||
receiver.OnPowerStateChanged -= PowerDeviceOnOnPowerStateChanged;
|
||||
}
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
private void PowerDeviceOnOnPowerStateChanged(object sender, PowerStateEventArgs e)
|
||||
private void PowerDeviceOnOnPowerStateChanged(object? sender, PowerStateEventArgs e)
|
||||
{
|
||||
if (Owner.TryGetComponent(out AppearanceComponent appearance))
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(DoorVisuals.Powered, e.Powered);
|
||||
}
|
||||
@@ -188,11 +219,12 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
|
||||
protected override void ActivateImpl(ActivateEventArgs args)
|
||||
{
|
||||
if (_wires.IsPanelOpen)
|
||||
if (Owner.TryGetComponent(out WiresComponent? wires) &&
|
||||
wires.IsPanelOpen)
|
||||
{
|
||||
if (args.User.TryGetComponent(out IActorComponent actor))
|
||||
if (args.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
_wires.OpenInterface(actor.playerSession);
|
||||
wires.OpenInterface(actor.playerSession);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -272,8 +304,7 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
case Wires.MainPower:
|
||||
case Wires.BackupPower:
|
||||
PowerWiresPulsed = true;
|
||||
_powerWiresPulsedTimerCancel?.Cancel();
|
||||
_powerWiresPulsedTimerCancel = new CancellationTokenSource();
|
||||
_powerWiresPulsedTimerCancel.Cancel();
|
||||
Timer.Spawn(PowerWiresTimeout,
|
||||
() => PowerWiresPulsed = false,
|
||||
_powerWiresPulsedTimerCancel.Token);
|
||||
@@ -377,7 +408,8 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
|
||||
private bool IsPowered()
|
||||
{
|
||||
return _powerReceiver.Powered;
|
||||
return !Owner.TryGetComponent(out PowerReceiverComponent? receiver)
|
||||
|| receiver.Powered;
|
||||
}
|
||||
|
||||
public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
@@ -388,11 +420,12 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
if (tool.HasQuality(ToolQuality.Cutting)
|
||||
|| tool.HasQuality(ToolQuality.Multitool))
|
||||
{
|
||||
if (_wires.IsPanelOpen)
|
||||
if (Owner.TryGetComponent(out WiresComponent? wires)
|
||||
&& wires.IsPanelOpen)
|
||||
{
|
||||
if (eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
if (eventArgs.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
_wires.OpenInterface(actor.playerSession);
|
||||
wires.OpenInterface(actor.playerSession);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Content.Server.Atmos;
|
||||
@@ -35,10 +36,10 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
|
||||
private DoorState _state = DoorState.Closed;
|
||||
|
||||
protected virtual DoorState State
|
||||
public virtual DoorState State
|
||||
{
|
||||
get => _state;
|
||||
set => _state = value;
|
||||
protected set => _state = value;
|
||||
}
|
||||
|
||||
protected float OpenTimeCounter;
|
||||
@@ -46,10 +47,7 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
protected const float AutoCloseDelay = 5;
|
||||
protected float CloseSpeed = AutoCloseDelay;
|
||||
|
||||
private AirtightComponent airtightComponent;
|
||||
private ICollidableComponent _collidableComponent;
|
||||
private AppearanceComponent _appearance;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
protected virtual TimeSpan CloseTimeOne => TimeSpan.FromSeconds(0.3f);
|
||||
protected virtual TimeSpan CloseTimeTwo => TimeSpan.FromSeconds(0.9f);
|
||||
@@ -72,21 +70,9 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
serializer.DataField(ref _occludes, "occludes", true);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
airtightComponent = Owner.GetComponent<AirtightComponent>();
|
||||
_collidableComponent = Owner.GetComponent<ICollidableComponent>();
|
||||
_appearance = Owner.GetComponent<AppearanceComponent>();
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
_collidableComponent = null;
|
||||
_appearance = null;
|
||||
_cancellationTokenSource?.Cancel();
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
@@ -108,7 +94,6 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
ActivateImpl(eventArgs);
|
||||
}
|
||||
|
||||
|
||||
void ICollideBehavior.CollideWith(IEntity entity)
|
||||
{
|
||||
if (State != DoorState.Closed)
|
||||
@@ -139,8 +124,10 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
|
||||
protected void SetAppearance(DoorVisualState state)
|
||||
{
|
||||
if (_appearance != null || Owner.TryGetComponent(out _appearance))
|
||||
_appearance.SetData(DoorVisuals.VisualState, state);
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(DoorVisuals.VisualState, state);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool CanOpen()
|
||||
@@ -151,28 +138,53 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
public virtual bool CanOpen(IEntity user)
|
||||
{
|
||||
if (!CanOpen()) return false;
|
||||
if (!Owner.TryGetComponent(out AccessReader accessReader))
|
||||
|
||||
if (!Owner.TryGetComponent<AccessReader>(out var accessReader))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return accessReader.IsAllowed(user);
|
||||
var doorSystem = EntitySystem.Get<DoorSystem>();
|
||||
var isAirlockExternal = HasAccessType("External");
|
||||
|
||||
return doorSystem.AccessType switch
|
||||
{
|
||||
DoorSystem.AccessTypes.AllowAll => true,
|
||||
DoorSystem.AccessTypes.AllowAllIdExternal => isAirlockExternal ? accessReader.IsAllowed(user) : true,
|
||||
DoorSystem.AccessTypes.AllowAllNoExternal => !isAirlockExternal,
|
||||
_ => accessReader.IsAllowed(user)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether a door has a certain access type. For example, maintenance doors will have access type
|
||||
/// "Maintenance" in their AccessReader.
|
||||
/// </summary>
|
||||
private bool HasAccessType(string accesType)
|
||||
{
|
||||
if(Owner.TryGetComponent<AccessReader>(out var accessReader))
|
||||
{
|
||||
return accessReader.AccessLists.Any(list => list.Contains(accesType));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void TryOpen(IEntity user)
|
||||
{
|
||||
if (!CanOpen(user))
|
||||
if (CanOpen(user))
|
||||
{
|
||||
Open();
|
||||
|
||||
if (user.TryGetComponent(out HandsComponent? hands) && hands.Count == 0)
|
||||
{
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Effects/bang.ogg", Owner,
|
||||
AudioParams.Default.WithVolume(-2));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Deny();
|
||||
return;
|
||||
}
|
||||
|
||||
Open();
|
||||
|
||||
if (user.TryGetComponent(out HandsComponent hands) && hands.Count == 0)
|
||||
{
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Effects/bang.ogg", Owner,
|
||||
AudioParams.Default.WithVolume(-2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,15 +197,22 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
|
||||
State = DoorState.Opening;
|
||||
SetAppearance(DoorVisualState.Opening);
|
||||
if (_occludes && Owner.TryGetComponent(out OccluderComponent occluder))
|
||||
if (_occludes && Owner.TryGetComponent(out OccluderComponent? occluder))
|
||||
{
|
||||
occluder.Enabled = false;
|
||||
}
|
||||
|
||||
Timer.Spawn(OpenTimeOne, async () =>
|
||||
{
|
||||
airtightComponent.AirBlocked = false;
|
||||
_collidableComponent.Hard = false;
|
||||
if (Owner.TryGetComponent(out AirtightComponent? airtight))
|
||||
{
|
||||
airtight.AirBlocked = false;
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out ICollidableComponent? collidable))
|
||||
{
|
||||
collidable.Hard = false;
|
||||
}
|
||||
|
||||
await Timer.Delay(OpenTimeTwo, _cancellationTokenSource.Token);
|
||||
|
||||
@@ -212,7 +231,7 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
public virtual bool CanClose(IEntity user)
|
||||
{
|
||||
if (!CanClose()) return false;
|
||||
if (!Owner.TryGetComponent(out AccessReader accessReader))
|
||||
if (!Owner.TryGetComponent(out AccessReader? accessReader))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -233,18 +252,22 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
|
||||
private void CheckCrush()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out ICollidableComponent? body))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if collides with something
|
||||
var collidesWith = _collidableComponent.GetCollidingEntities(Vector2.Zero, false);
|
||||
var collidesWith = body.GetCollidingEntities(Vector2.Zero, false);
|
||||
if (collidesWith.Count() != 0)
|
||||
{
|
||||
// Crush
|
||||
bool hitSomeone = false;
|
||||
foreach (var e in collidesWith)
|
||||
{
|
||||
if (!e.TryGetComponent(out StunnableComponent stun)
|
||||
|| !e.TryGetComponent(out IDamageableComponent damage)
|
||||
|| !e.TryGetComponent(out ICollidableComponent otherBody)
|
||||
|| !Owner.TryGetComponent(out ICollidableComponent body))
|
||||
if (!e.TryGetComponent(out StunnableComponent? stun)
|
||||
|| !e.TryGetComponent(out IDamageableComponent? damage)
|
||||
|| !e.TryGetComponent(out ICollidableComponent? otherBody))
|
||||
continue;
|
||||
|
||||
var percentage = otherBody.WorldAABB.IntersectPercentage(body.WorldAABB);
|
||||
@@ -319,7 +342,8 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
public bool Close()
|
||||
{
|
||||
bool shouldCheckCrush = false;
|
||||
if (_collidableComponent.IsColliding(Vector2.Zero, false))
|
||||
|
||||
if (Owner.TryGetComponent(out ICollidableComponent? collidable) && collidable.IsColliding(Vector2.Zero, false))
|
||||
{
|
||||
if (Safety)
|
||||
return false;
|
||||
@@ -331,7 +355,7 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
State = DoorState.Closing;
|
||||
OpenTimeCounter = 0;
|
||||
SetAppearance(DoorVisualState.Closing);
|
||||
if (_occludes && Owner.TryGetComponent(out OccluderComponent occluder))
|
||||
if (_occludes && Owner.TryGetComponent(out OccluderComponent? occluder))
|
||||
{
|
||||
occluder.Enabled = true;
|
||||
}
|
||||
@@ -343,8 +367,15 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
CheckCrush();
|
||||
}
|
||||
|
||||
airtightComponent.AirBlocked = true;
|
||||
_collidableComponent.Hard = true;
|
||||
if (Owner.TryGetComponent(out AirtightComponent? airtight))
|
||||
{
|
||||
airtight.AirBlocked = true;
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out ICollidableComponent? body))
|
||||
{
|
||||
body.Hard = true;
|
||||
}
|
||||
|
||||
await Timer.Delay(CloseTimeTwo, _cancellationTokenSource.Token);
|
||||
|
||||
@@ -391,7 +422,7 @@ namespace Content.Server.GameObjects.Components.Doors
|
||||
}
|
||||
}
|
||||
|
||||
protected enum DoorState
|
||||
public enum DoorState
|
||||
{
|
||||
Closed,
|
||||
Open,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.Chemistry;
|
||||
using Content.Shared.Chemistry;
|
||||
@@ -19,23 +20,25 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
[RegisterComponent]
|
||||
public class BucketComponent : Component, IInteractUsing
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override string Name => "Bucket";
|
||||
|
||||
public ReagentUnit MaxVolume
|
||||
{
|
||||
get => _contents.MaxVolume;
|
||||
set => _contents.MaxVolume = value;
|
||||
get => Owner.TryGetComponent(out SolutionComponent? solution) ? solution.MaxVolume : ReagentUnit.Zero;
|
||||
set
|
||||
{
|
||||
if (Owner.TryGetComponent(out SolutionComponent? solution))
|
||||
{
|
||||
solution.MaxVolume = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ReagentUnit CurrentVolume => _contents.CurrentVolume;
|
||||
public ReagentUnit CurrentVolume => Owner.TryGetComponent(out SolutionComponent? solution)
|
||||
? solution.CurrentVolume
|
||||
: ReagentUnit.Zero;
|
||||
|
||||
private SolutionComponent _contents;
|
||||
|
||||
private string _sound;
|
||||
private string? _sound;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
@@ -47,16 +50,28 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_contents = Owner.GetComponent<SolutionComponent>();
|
||||
Owner.EnsureComponent<SolutionComponent>();
|
||||
}
|
||||
|
||||
private bool TryGiveToMop(MopComponent mopComponent)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SolutionComponent? contents))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var mopContents = mopComponent.Contents;
|
||||
|
||||
if (mopContents == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Let's fill 'er up
|
||||
// If this is called the mop should be empty but just in case we'll do Max - Current
|
||||
var transferAmount = ReagentUnit.Min(mopComponent.MaxVolume - mopComponent.CurrentVolume, CurrentVolume);
|
||||
var solution = _contents.SplitSolution(transferAmount);
|
||||
if (!mopComponent.Contents.TryAddSolution(solution) || mopComponent.CurrentVolume == 0)
|
||||
var solution = contents.SplitSolution(transferAmount);
|
||||
if (!mopContents.TryAddSolution(solution) || mopComponent.CurrentVolume == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -73,7 +88,12 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
|
||||
public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.Using.TryGetComponent(out MopComponent mopComponent))
|
||||
if (!Owner.TryGetComponent(out SolutionComponent? contents))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!eventArgs.Using.TryGetComponent(out MopComponent? mopComponent))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -86,7 +106,7 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
return false;
|
||||
}
|
||||
|
||||
Owner.PopupMessage(eventArgs.User, _localizationManager.GetString("Splish"));
|
||||
Owner.PopupMessage(eventArgs.User, Loc.GetString("Splish"));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -96,15 +116,22 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
return false;
|
||||
}
|
||||
|
||||
var solution = mopComponent.Contents.SplitSolution(transferAmount);
|
||||
if (!_contents.TryAddSolution(solution))
|
||||
var mopContents = mopComponent.Contents;
|
||||
|
||||
if (mopContents == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var solution = mopContents.SplitSolution(transferAmount);
|
||||
if (!contents.TryAddSolution(solution))
|
||||
{
|
||||
//This really shouldn't happen
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
// Give some visual feedback shit's happening (for anyone who can't hear sound)
|
||||
Owner.PopupMessage(eventArgs.User, _localizationManager.GetString("Sploosh"));
|
||||
Owner.PopupMessage(eventArgs.User, Loc.GetString("Sploosh"));
|
||||
|
||||
if (_sound == null)
|
||||
{
|
||||
@@ -114,7 +141,6 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity(_sound, Owner);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Server.GameObjects.Components.Chemistry;
|
||||
#nullable enable
|
||||
using Content.Server.GameObjects.Components.Chemistry;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Interfaces;
|
||||
@@ -6,8 +7,8 @@ using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Fluids
|
||||
@@ -18,21 +19,24 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
[RegisterComponent]
|
||||
public class MopComponent : Component, IAfterInteract
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override string Name => "Mop";
|
||||
internal SolutionComponent Contents => _contents;
|
||||
private SolutionComponent _contents;
|
||||
|
||||
public SolutionComponent? Contents => Owner.GetComponentOrNull<SolutionComponent>();
|
||||
|
||||
public ReagentUnit MaxVolume
|
||||
{
|
||||
get => _contents.MaxVolume;
|
||||
set => _contents.MaxVolume = value;
|
||||
get => Owner.GetComponentOrNull<SolutionComponent>()?.MaxVolume ?? ReagentUnit.Zero;
|
||||
set
|
||||
{
|
||||
if (Owner.TryGetComponent(out SolutionComponent? solution))
|
||||
{
|
||||
solution.MaxVolume = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ReagentUnit CurrentVolume => _contents.CurrentVolume;
|
||||
public ReagentUnit CurrentVolume =>
|
||||
Owner.GetComponentOrNull<SolutionComponent>()?.CurrentVolume ?? ReagentUnit.Zero;
|
||||
|
||||
// Currently there's a separate amount for pickup and dropoff so
|
||||
// Picking up a puddle requires multiple clicks
|
||||
@@ -41,7 +45,7 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
public ReagentUnit PickupAmount => _pickupAmount;
|
||||
private ReagentUnit _pickupAmount;
|
||||
|
||||
private string _pickupSound;
|
||||
private string _pickupSound = "";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
@@ -54,12 +58,16 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_contents = Owner.GetComponent<SolutionComponent>();
|
||||
|
||||
if (!Owner.EnsureComponent(out SolutionComponent _))
|
||||
{
|
||||
Logger.Warning($"Entity {Owner.Name} at {Owner.Transform.MapPosition} didn't have a {nameof(SolutionComponent)}");
|
||||
}
|
||||
}
|
||||
|
||||
void IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SolutionComponent? contents)) return;
|
||||
if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return;
|
||||
|
||||
if (CurrentVolume <= 0)
|
||||
@@ -71,12 +79,12 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
if (eventArgs.Target == null)
|
||||
{
|
||||
// Drop the liquid on the mop on to the ground
|
||||
SpillHelper.SpillAt(eventArgs.ClickLocation, _contents.SplitSolution(CurrentVolume), "PuddleSmear");
|
||||
SpillHelper.SpillAt(eventArgs.ClickLocation, contents.SplitSolution(CurrentVolume), "PuddleSmear");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!eventArgs.Target.TryGetComponent(out PuddleComponent puddleComponent))
|
||||
if (!eventArgs.Target.TryGetComponent(out PuddleComponent? puddleComponent))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -107,23 +115,22 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
|
||||
if (puddleCleaned) //After cleaning the puddle, make a new puddle with solution from the mop as a "wet floor". Then evaporate it slowly.
|
||||
{
|
||||
SpillHelper.SpillAt(eventArgs.ClickLocation, _contents.SplitSolution(transferAmount), "PuddleSmear");
|
||||
SpillHelper.SpillAt(eventArgs.ClickLocation, contents.SplitSolution(transferAmount), "PuddleSmear");
|
||||
}
|
||||
else
|
||||
{
|
||||
_contents.SplitSolution(transferAmount);
|
||||
contents.SplitSolution(transferAmount);
|
||||
}
|
||||
|
||||
// Give some visual feedback shit's happening (for anyone who can't hear sound)
|
||||
Owner.PopupMessage(eventArgs.User, _localizationManager.GetString("Swish"));
|
||||
Owner.PopupMessage(eventArgs.User, Loc.GetString("Swish"));
|
||||
|
||||
if (_pickupSound == null)
|
||||
if (string.IsNullOrWhiteSpace(_pickupSound))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity(_pickupSound, Owner);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,10 +46,7 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
// based on behaviour (e.g. someone being punched vs slashed with a sword would have different blood sprite)
|
||||
// to check for low volumes for evaporation or whatever
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IMapManager _mapManager;
|
||||
[Dependency] private readonly ILocalizationManager _loc;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
public override string Name => "Puddle";
|
||||
|
||||
@@ -116,6 +113,7 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
if (Owner.TryGetComponent(out SolutionComponent solutionComponent))
|
||||
{
|
||||
_contents = solutionComponent;
|
||||
@@ -123,21 +121,27 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
else
|
||||
{
|
||||
_contents = Owner.AddComponent<SolutionComponent>();
|
||||
_contents.Initialize();
|
||||
}
|
||||
|
||||
_snapGrid = Owner.GetComponent<SnapGridComponent>();
|
||||
_snapGrid = Owner.EnsureComponent<SnapGridComponent>();
|
||||
|
||||
// Smaller than 1m^3 for now but realistically this shouldn't be hit
|
||||
MaxVolume = ReagentUnit.New(1000);
|
||||
|
||||
// Random sprite state set server-side so it's consistent across all clients
|
||||
_spriteComponent = Owner.GetComponent<SpriteComponent>();
|
||||
_spriteComponent = Owner.EnsureComponent<SpriteComponent>();
|
||||
|
||||
var robustRandom = IoCManager.Resolve<IRobustRandom>();
|
||||
var randomVariant = robustRandom.Next(0, _spriteVariants - 1);
|
||||
var baseName = new ResourcePath(_spriteComponent.BaseRSIPath).FilenameWithoutExtension;
|
||||
|
||||
_spriteComponent.LayerSetState(0, $"{baseName}-{randomVariant}"); // TODO: Remove hardcode
|
||||
if (_spriteComponent.BaseRSIPath != null)
|
||||
{
|
||||
var baseName = new ResourcePath(_spriteComponent.BaseRSIPath).FilenameWithoutExtension;
|
||||
|
||||
_spriteComponent.LayerSetState(0, $"{baseName}-{randomVariant}"); // TODO: Remove hardcode
|
||||
|
||||
}
|
||||
|
||||
// UpdateAppearance should get called soon after this so shouldn't need to call Dirty() here
|
||||
|
||||
UpdateStatus();
|
||||
@@ -153,7 +157,7 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
{
|
||||
if(_slippery)
|
||||
{
|
||||
message.AddText(_loc.GetString("It looks slippery."));
|
||||
message.AddText(Loc.GetString("It looks slippery."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -16,10 +17,9 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
[RegisterComponent]
|
||||
class SprayComponent : Component, IAfterInteract
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
[Dependency] private readonly IServerEntityManager _serverEntityManager = default!;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override string Name => "Spray";
|
||||
|
||||
private ReagentUnit _transferAmount;
|
||||
@@ -46,13 +46,17 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
set => _sprayVelocity = value;
|
||||
}
|
||||
|
||||
private SolutionComponent _contents;
|
||||
public ReagentUnit CurrentVolume => _contents.CurrentVolume;
|
||||
public ReagentUnit CurrentVolume => Owner.GetComponentOrNull<SolutionComponent>()?.CurrentVolume ?? ReagentUnit.Zero;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_contents = Owner.GetComponent<SolutionComponent>();
|
||||
|
||||
if (!Owner.EnsureComponent(out SolutionComponent _))
|
||||
{
|
||||
Logger.Warning(
|
||||
$"Entity {Owner.Name} at {Owner.Transform.MapPosition} didn't have a {nameof(SolutionComponent)}");
|
||||
}
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
@@ -75,8 +79,11 @@ namespace Content.Server.GameObjects.Components.Fluids
|
||||
if (eventArgs.ClickLocation.GridID != playerPos.GridID)
|
||||
return;
|
||||
|
||||
if (!Owner.TryGetComponent(out SolutionComponent contents))
|
||||
return;
|
||||
|
||||
var direction = (eventArgs.ClickLocation.Position - playerPos.Position).Normalized;
|
||||
var solution = _contents.SplitSolution(_transferAmount);
|
||||
var solution = contents.SplitSolution(_transferAmount);
|
||||
|
||||
playerPos = playerPos.Offset(direction); // Move a bit so we don't hit the player
|
||||
//TODO: check for wall?
|
||||
|
||||
@@ -29,6 +29,7 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Content.Server.GameObjects.Components.ActionBlocking;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.GUI
|
||||
{
|
||||
@@ -37,9 +38,7 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
[ComponentReference(typeof(ISharedHandsComponent))]
|
||||
public class HandsComponent : SharedHandsComponent, IHandsComponent, IBodyPartAdded, IBodyPartRemoved
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
#pragma warning restore 649
|
||||
|
||||
private string? _activeHand;
|
||||
private uint _nextHand;
|
||||
@@ -446,6 +445,7 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
ActiveHand ??= name;
|
||||
|
||||
OnItemChanged?.Invoke();
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new HandCountChangedEvent(Owner));
|
||||
|
||||
Dirty();
|
||||
}
|
||||
@@ -468,6 +468,7 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
}
|
||||
|
||||
OnItemChanged?.Invoke();
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new HandCountChangedEvent(Owner));
|
||||
|
||||
Dirty();
|
||||
}
|
||||
@@ -793,4 +794,14 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
return new SharedHand(index, Name, Entity?.Uid, location);
|
||||
}
|
||||
}
|
||||
|
||||
public class HandCountChangedEvent : EntitySystemMessage
|
||||
{
|
||||
public HandCountChangedEvent(IEntity sender)
|
||||
{
|
||||
Sender = sender;
|
||||
}
|
||||
|
||||
public IEntity Sender { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_inventory = Owner.GetComponent<InventoryComponent>();
|
||||
_inventory = Owner.EnsureComponent<InventoryComponent>();
|
||||
}
|
||||
|
||||
bool IInventoryController.CanEquip(Slots slot, IEntity entity, bool flagsCheck, out string reason)
|
||||
|
||||
@@ -28,10 +28,8 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
[RegisterComponent]
|
||||
public class InventoryComponent : SharedInventoryComponent, IExAct, IEffectBlocker, IPressureProtection
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
|
||||
[Dependency] private readonly IServerNotifyManager _serverNotifyManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
[Dependency] private readonly IServerNotifyManager _serverNotifyManager = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private readonly Dictionary<Slots, ContainerSlot> _slotContainers = new Dictionary<Slots, ContainerSlot>();
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Content.Server.GameObjects.Components.ActionBlocking;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.GameObjects.EntitySystems.DoAfter;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.GameObjects.Components.GUI;
|
||||
using Content.Shared.GameObjects.Components.Inventory;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Verbs;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
@@ -16,7 +19,6 @@ using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines;
|
||||
|
||||
@@ -25,27 +27,39 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
[RegisterComponent]
|
||||
public sealed class StrippableComponent : SharedStrippableComponent, IDragDrop
|
||||
{
|
||||
[Dependency] private IServerNotifyManager _notifyManager = default!;
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
|
||||
public const float StripDelay = 2f;
|
||||
|
||||
[ViewVariables]
|
||||
private BoundUserInterface _userInterface;
|
||||
|
||||
private InventoryComponent _inventoryComponent;
|
||||
private HandsComponent _handsComponent;
|
||||
[ViewVariables]
|
||||
private BoundUserInterface? UserInterface => Owner.GetUIOrNull(StrippingUiKey.Key);
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>().GetBoundUserInterface(StrippingUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += HandleUserInterfaceMessage;
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += HandleUserInterfaceMessage;
|
||||
}
|
||||
|
||||
_inventoryComponent = Owner.GetComponent<InventoryComponent>();
|
||||
_handsComponent = Owner.GetComponent<HandsComponent>();
|
||||
Owner.EnsureComponent<InventoryComponent>();
|
||||
Owner.EnsureComponent<HandsComponent>();
|
||||
Owner.EnsureComponent<CuffableComponent>();
|
||||
|
||||
if (Owner.TryGetComponent(out CuffableComponent? cuffed))
|
||||
{
|
||||
cuffed.OnCuffedStateChanged += UpdateSubscribed;
|
||||
}
|
||||
if (Owner.TryGetComponent(out InventoryComponent? inventory))
|
||||
{
|
||||
inventory.OnItemChanged += UpdateSubscribed;
|
||||
}
|
||||
|
||||
_inventoryComponent.OnItemChanged += UpdateSubscribed;
|
||||
if (Owner.TryGetComponent(out HandsComponent? hands))
|
||||
{
|
||||
hands.OnItemChanged += UpdateSubscribed;
|
||||
}
|
||||
|
||||
// Initial update.
|
||||
UpdateSubscribed();
|
||||
@@ -53,33 +67,69 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
|
||||
private void UpdateSubscribed()
|
||||
{
|
||||
if (UserInterface == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var inventory = GetInventorySlots();
|
||||
var hands = GetHandSlots();
|
||||
var cuffs = GetHandcuffs();
|
||||
|
||||
_userInterface.SetState(new StrippingBoundUserInterfaceState(inventory, hands));
|
||||
UserInterface.SetState(new StrippingBoundUserInterfaceState(inventory, hands, cuffs));
|
||||
}
|
||||
|
||||
public bool CanBeStripped(IEntity by)
|
||||
{
|
||||
return by != Owner
|
||||
&& by.HasComponent<HandsComponent>()
|
||||
&& ActionBlockerSystem.CanInteract(by);
|
||||
}
|
||||
|
||||
public bool CanDragDrop(DragDropEventArgs eventArgs)
|
||||
{
|
||||
return eventArgs.User.HasComponent<HandsComponent>()
|
||||
&& eventArgs.Target != eventArgs.Dropped && eventArgs.Target == eventArgs.User;
|
||||
return eventArgs.Target != eventArgs.Dropped
|
||||
&& eventArgs.Target == eventArgs.User
|
||||
&& CanBeStripped(eventArgs.User);
|
||||
}
|
||||
|
||||
public bool DragDrop(DragDropEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent actor)) return false;
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent? actor)) return false;
|
||||
|
||||
OpenUserInterface(actor.playerSession);
|
||||
return true;
|
||||
}
|
||||
|
||||
private Dictionary<EntityUid, string> GetHandcuffs()
|
||||
{
|
||||
var dictionary = new Dictionary<EntityUid, string>();
|
||||
|
||||
if (!Owner.TryGetComponent(out CuffableComponent? cuffed))
|
||||
{
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
foreach (IEntity entity in cuffed.StoredEntities)
|
||||
{
|
||||
dictionary.Add(entity.Uid, entity.Name);
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
private Dictionary<Slots, string> GetInventorySlots()
|
||||
{
|
||||
var dictionary = new Dictionary<Slots, string>();
|
||||
|
||||
foreach (var slot in _inventoryComponent.Slots)
|
||||
if (!Owner.TryGetComponent(out InventoryComponent? inventory))
|
||||
{
|
||||
dictionary[slot] = _inventoryComponent.GetSlotItem(slot)?.Owner.Name ?? "None";
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
foreach (var slot in inventory.Slots)
|
||||
{
|
||||
dictionary[slot] = inventory.GetSlotItem(slot)?.Owner.Name ?? "None";
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
@@ -89,9 +139,14 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
{
|
||||
var dictionary = new Dictionary<string, string>();
|
||||
|
||||
foreach (var hand in _handsComponent.Hands)
|
||||
if (!Owner.TryGetComponent(out HandsComponent? hands))
|
||||
{
|
||||
dictionary[hand] = _handsComponent.GetItem(hand)?.Owner.Name ?? "None";
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
foreach (var hand in hands.Hands)
|
||||
{
|
||||
dictionary[hand] = hands.GetItem(hand)?.Owner.Name ?? "None";
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
@@ -99,7 +154,7 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
|
||||
private void OpenUserInterface(IPlayerSession session)
|
||||
{
|
||||
_userInterface.Open(session);
|
||||
UserInterface?.Open(session);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -336,34 +391,80 @@ namespace Content.Server.GameObjects.Components.GUI
|
||||
private void HandleUserInterfaceMessage(ServerBoundUserInterfaceMessage obj)
|
||||
{
|
||||
var user = obj.Session.AttachedEntity;
|
||||
if (user == null || !(user.TryGetComponent(out HandsComponent userHands))) return;
|
||||
if (user == null || !(user.TryGetComponent(out HandsComponent? userHands))) return;
|
||||
|
||||
var placingItem = userHands.GetActiveHand != null;
|
||||
|
||||
switch (obj.Message)
|
||||
{
|
||||
case StrippingInventoryButtonPressed inventoryMessage:
|
||||
var inventory = Owner.GetComponent<InventoryComponent>();
|
||||
|
||||
if (inventory.TryGetSlotItem(inventoryMessage.Slot, out ItemComponent _))
|
||||
placingItem = false;
|
||||
if (Owner.TryGetComponent<InventoryComponent>(out var inventory))
|
||||
{
|
||||
if (inventory.TryGetSlotItem(inventoryMessage.Slot, out ItemComponent _))
|
||||
placingItem = false;
|
||||
|
||||
if(placingItem)
|
||||
PlaceActiveHandItemInInventory(user, inventoryMessage.Slot);
|
||||
else
|
||||
TakeItemFromInventory(user, inventoryMessage.Slot);
|
||||
if (placingItem)
|
||||
PlaceActiveHandItemInInventory(user, inventoryMessage.Slot);
|
||||
else
|
||||
TakeItemFromInventory(user, inventoryMessage.Slot);
|
||||
}
|
||||
break;
|
||||
|
||||
case StrippingHandButtonPressed handMessage:
|
||||
var hands = Owner.GetComponent<HandsComponent>();
|
||||
|
||||
if (hands.TryGetItem(handMessage.Hand, out _))
|
||||
placingItem = false;
|
||||
if (Owner.TryGetComponent<HandsComponent>(out var hands))
|
||||
{
|
||||
if (hands.TryGetItem(handMessage.Hand, out _))
|
||||
placingItem = false;
|
||||
|
||||
if(placingItem)
|
||||
PlaceActiveHandItemInHands(user, handMessage.Hand);
|
||||
else
|
||||
TakeItemFromHands(user, handMessage.Hand);
|
||||
if (placingItem)
|
||||
PlaceActiveHandItemInHands(user, handMessage.Hand);
|
||||
else
|
||||
TakeItemFromHands(user, handMessage.Hand);
|
||||
}
|
||||
break;
|
||||
|
||||
case StrippingHandcuffButtonPressed handcuffMessage:
|
||||
|
||||
if (Owner.TryGetComponent<CuffableComponent>(out var cuffed))
|
||||
{
|
||||
foreach (var entity in cuffed.StoredEntities)
|
||||
{
|
||||
if (entity.Uid == handcuffMessage.Handcuff)
|
||||
{
|
||||
cuffed.TryUncuff(user, entity);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
[Verb]
|
||||
private sealed class StripVerb : Verb<StrippableComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, StrippableComponent component, VerbData data)
|
||||
{
|
||||
if (!component.CanBeStripped(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
data.Text = Loc.GetString("Strip");
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, StrippableComponent component)
|
||||
{
|
||||
if (!user.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
component.OpenUserInterface(actor.playerSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
using System.Threading.Tasks;
|
||||
#nullable enable
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.Damage;
|
||||
using Content.Server.GameObjects.Components.Interactable;
|
||||
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.GameObjects.Components.Gravity;
|
||||
using Content.Shared.GameObjects.Components.Interactable;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
@@ -16,17 +17,13 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Gravity
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class GravityGeneratorComponent : SharedGravityGeneratorComponent, IInteractUsing, IBreakAct, IInteractHand
|
||||
{
|
||||
private BoundUserInterface _userInterface;
|
||||
|
||||
private PowerReceiverComponent _powerReceiver;
|
||||
|
||||
private SpriteComponent _sprite;
|
||||
|
||||
private bool _switchedOn;
|
||||
|
||||
@@ -34,7 +31,7 @@ namespace Content.Server.GameObjects.Components.Gravity
|
||||
|
||||
private GravityGeneratorStatus _status;
|
||||
|
||||
public bool Powered => _powerReceiver.Powered;
|
||||
public bool Powered => !Owner.TryGetComponent(out PowerReceiverComponent? receiver) || receiver.Powered;
|
||||
|
||||
public bool SwitchedOn => _switchedOn;
|
||||
|
||||
@@ -64,15 +61,17 @@ namespace Content.Server.GameObjects.Components.Gravity
|
||||
|
||||
public override string Name => "GravityGenerator";
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(GravityGeneratorUiKey.Key);
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(GravityGeneratorUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += HandleUIMessage;
|
||||
_powerReceiver = Owner.GetComponent<PowerReceiverComponent>();
|
||||
_sprite = Owner.GetComponent<SpriteComponent>();
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += HandleUIMessage;
|
||||
}
|
||||
|
||||
_switchedOn = true;
|
||||
_intact = true;
|
||||
_status = GravityGeneratorStatus.On;
|
||||
@@ -101,7 +100,7 @@ namespace Content.Server.GameObjects.Components.Gravity
|
||||
|
||||
public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.Using.TryGetComponent(out WelderComponent tool))
|
||||
if (!eventArgs.Using.TryGetComponent(out WelderComponent? tool))
|
||||
return false;
|
||||
|
||||
if (!await tool.UseTool(eventArgs.User, Owner, 2f, ToolQuality.Welding, 5f))
|
||||
@@ -150,7 +149,7 @@ namespace Content.Server.GameObjects.Components.Gravity
|
||||
switch (message.Message)
|
||||
{
|
||||
case GeneratorStatusRequestMessage _:
|
||||
_userInterface.SetState(new GeneratorState(Status == GravityGeneratorStatus.On));
|
||||
UserInterface?.SetState(new GeneratorState(Status == GravityGeneratorStatus.On));
|
||||
break;
|
||||
case SwitchGeneratorMessage msg:
|
||||
_switchedOn = msg.On;
|
||||
@@ -163,35 +162,51 @@ namespace Content.Server.GameObjects.Components.Gravity
|
||||
|
||||
private void OpenUserInterface(IPlayerSession playerSession)
|
||||
{
|
||||
_userInterface.Open(playerSession);
|
||||
UserInterface?.Open(playerSession);
|
||||
}
|
||||
|
||||
private void MakeBroken()
|
||||
{
|
||||
_status = GravityGeneratorStatus.Broken;
|
||||
_sprite.LayerSetState(0, "broken");
|
||||
_sprite.LayerSetVisible(1, false);
|
||||
|
||||
if (Owner.TryGetComponent(out SpriteComponent? sprite))
|
||||
{
|
||||
sprite.LayerSetState(0, "broken");
|
||||
sprite.LayerSetVisible(1, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void MakeUnpowered()
|
||||
{
|
||||
_status = GravityGeneratorStatus.Unpowered;
|
||||
_sprite.LayerSetState(0, "off");
|
||||
_sprite.LayerSetVisible(1, false);
|
||||
|
||||
if (Owner.TryGetComponent(out SpriteComponent? sprite))
|
||||
{
|
||||
sprite.LayerSetState(0, "off");
|
||||
sprite.LayerSetVisible(1, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void MakeOff()
|
||||
{
|
||||
_status = GravityGeneratorStatus.Off;
|
||||
_sprite.LayerSetState(0, "off");
|
||||
_sprite.LayerSetVisible(1, false);
|
||||
|
||||
if (Owner.TryGetComponent(out SpriteComponent? sprite))
|
||||
{
|
||||
sprite.LayerSetState(0, "off");
|
||||
sprite.LayerSetVisible(1, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void MakeOn()
|
||||
{
|
||||
_status = GravityGeneratorStatus.On;
|
||||
_sprite.LayerSetState(0, "on");
|
||||
_sprite.LayerSetVisible(1, true);
|
||||
|
||||
if (Owner.TryGetComponent(out SpriteComponent? sprite))
|
||||
{
|
||||
sprite.LayerSetState(0, "on");
|
||||
sprite.LayerSetVisible(1, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Mobs;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.GameObjects.Components.Instruments;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
@@ -34,12 +36,8 @@ namespace Content.Server.GameObjects.Components.Instruments
|
||||
IUse,
|
||||
IThrown
|
||||
{
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager;
|
||||
|
||||
[Dependency] private readonly IGameTiming _gameTiming;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private static readonly TimeSpan OneSecAgo = TimeSpan.FromSeconds(-1);
|
||||
|
||||
@@ -47,7 +45,7 @@ namespace Content.Server.GameObjects.Components.Instruments
|
||||
/// The client channel currently playing the instrument, or null if there's none.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private IPlayerSession _instrumentPlayer;
|
||||
private IPlayerSession? _instrumentPlayer;
|
||||
|
||||
private bool _handheld;
|
||||
|
||||
@@ -72,9 +70,6 @@ namespace Content.Server.GameObjects.Components.Instruments
|
||||
[ViewVariables]
|
||||
private int _midiEventCount = 0;
|
||||
|
||||
[ViewVariables]
|
||||
private BoundUserInterface _userInterface;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the instrument is an item which can be held or not.
|
||||
/// </summary>
|
||||
@@ -95,7 +90,7 @@ namespace Content.Server.GameObjects.Components.Instruments
|
||||
}
|
||||
}
|
||||
|
||||
public IPlayerSession InstrumentPlayer
|
||||
public IPlayerSession? InstrumentPlayer
|
||||
{
|
||||
get => _instrumentPlayer;
|
||||
private set
|
||||
@@ -108,11 +103,13 @@ namespace Content.Server.GameObjects.Components.Instruments
|
||||
_instrumentPlayer = value;
|
||||
|
||||
if (value != null)
|
||||
_instrumentPlayer.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
_instrumentPlayer!.PlayerStatusChanged += OnPlayerStatusChanged;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlayerStatusChanged(object sender, SessionStatusEventArgs e)
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(InstrumentUiKey.Key);
|
||||
|
||||
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
|
||||
{
|
||||
if (e.Session != _instrumentPlayer || e.NewStatus != SessionStatus.Disconnected) return;
|
||||
InstrumentPlayer = null;
|
||||
@@ -122,8 +119,11 @@ namespace Content.Server.GameObjects.Components.Instruments
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>().GetBoundUserInterface(InstrumentUiKey.Key);
|
||||
_userInterface.OnClosed += UserInterfaceOnClosed;
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnClosed += UserInterfaceOnClosed;
|
||||
}
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
@@ -137,14 +137,14 @@ namespace Content.Server.GameObjects.Components.Instruments
|
||||
return new InstrumentState(Playing, _lastSequencerTick);
|
||||
}
|
||||
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession session = null)
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession? session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, channel, session);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case InstrumentMidiEventMessage midiEventMsg:
|
||||
if (!Playing || session != _instrumentPlayer) return;
|
||||
if (!Playing || session != _instrumentPlayer || InstrumentPlayer == null) return;
|
||||
|
||||
var send = true;
|
||||
|
||||
@@ -231,7 +231,7 @@ namespace Content.Server.GameObjects.Components.Instruments
|
||||
Clean();
|
||||
SendNetworkMessage(new InstrumentStopMidiMessage());
|
||||
InstrumentPlayer = null;
|
||||
_userInterface.CloseAll();
|
||||
UserInterface?.CloseAll();
|
||||
}
|
||||
|
||||
public void Thrown(ThrownEventArgs eventArgs)
|
||||
@@ -239,7 +239,7 @@ namespace Content.Server.GameObjects.Components.Instruments
|
||||
Clean();
|
||||
SendNetworkMessage(new InstrumentStopMidiMessage());
|
||||
InstrumentPlayer = null;
|
||||
_userInterface.CloseAll();
|
||||
UserInterface?.CloseAll();
|
||||
}
|
||||
|
||||
public void HandSelected(HandSelectedEventArgs eventArgs)
|
||||
@@ -255,12 +255,12 @@ namespace Content.Server.GameObjects.Components.Instruments
|
||||
{
|
||||
Clean();
|
||||
SendNetworkMessage(new InstrumentStopMidiMessage());
|
||||
_userInterface.CloseAll();
|
||||
UserInterface?.CloseAll();
|
||||
}
|
||||
|
||||
public void Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (Handheld || !eventArgs.User.TryGetComponent(out IActorComponent actor)) return;
|
||||
if (Handheld || !eventArgs.User.TryGetComponent(out IActorComponent? actor)) return;
|
||||
|
||||
if (InstrumentPlayer != null) return;
|
||||
|
||||
@@ -270,7 +270,7 @@ namespace Content.Server.GameObjects.Components.Instruments
|
||||
|
||||
public bool UseEntity(UseEntityEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent actor)) return false;
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent? actor)) return false;
|
||||
|
||||
if (InstrumentPlayer == actor.playerSession)
|
||||
{
|
||||
@@ -291,7 +291,7 @@ namespace Content.Server.GameObjects.Components.Instruments
|
||||
|
||||
private void OpenUserInterface(IPlayerSession session)
|
||||
{
|
||||
_userInterface.Open(session);
|
||||
UserInterface?.Open(session);
|
||||
}
|
||||
|
||||
public override void Update(float delta)
|
||||
@@ -302,7 +302,7 @@ namespace Content.Server.GameObjects.Components.Instruments
|
||||
{
|
||||
InstrumentPlayer = null;
|
||||
Clean();
|
||||
_userInterface.CloseAll();
|
||||
UserInterface?.CloseAll();
|
||||
}
|
||||
|
||||
if ((_batchesDropped >= MaxMidiBatchDropped
|
||||
@@ -314,9 +314,9 @@ namespace Content.Server.GameObjects.Components.Instruments
|
||||
SendNetworkMessage(new InstrumentStopMidiMessage());
|
||||
Playing = false;
|
||||
|
||||
_userInterface.CloseAll();
|
||||
UserInterface?.CloseAll();
|
||||
|
||||
if (mob.TryGetComponent(out StunnableComponent stun))
|
||||
if (mob != null && mob.TryGetComponent(out StunnableComponent? stun))
|
||||
{
|
||||
stun.Stun(1);
|
||||
Clean();
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Threading.Tasks;
|
||||
#nullable enable
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Clothing;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
@@ -27,28 +28,26 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
/// Component that represents a handheld lightsource which can be toggled on and off.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
internal sealed class HandheldLightComponent : SharedHandheldLightComponent, IUse, IExamine, IInteractUsing, IMapInit
|
||||
internal sealed class HandheldLightComponent : SharedHandheldLightComponent, IUse, IExamine, IInteractUsing,
|
||||
IMapInit
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ISharedNotifyManager _notifyManager;
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly ISharedNotifyManager _notifyManager = default!;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] public float Wattage { get; set; } = 10;
|
||||
[ViewVariables] private ContainerSlot _cellContainer;
|
||||
private PointLightComponent _pointLight;
|
||||
private SpriteComponent _spriteComponent;
|
||||
private ClothingComponent _clothingComponent;
|
||||
[ViewVariables] private ContainerSlot _cellContainer = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private BatteryComponent Cell
|
||||
private BatteryComponent? Cell
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_cellContainer.ContainedEntity == null) return null;
|
||||
if (_cellContainer.ContainedEntity.TryGetComponent(out BatteryComponent? cell))
|
||||
{
|
||||
return cell;
|
||||
}
|
||||
|
||||
_cellContainer.ContainedEntity.TryGetComponent(out BatteryComponent cell);
|
||||
return cell;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,6 +57,8 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
[ViewVariables]
|
||||
public bool Activated { get; private set; }
|
||||
|
||||
[ViewVariables] protected override bool HasCell => Cell != null;
|
||||
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.Using.HasComponent<BatteryComponent>()) return false;
|
||||
@@ -81,11 +82,9 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
|
||||
void IExamine.Examine(FormattedMessage message, bool inDetailsRange)
|
||||
{
|
||||
var loc = IoCManager.Resolve<ILocalizationManager>();
|
||||
|
||||
if (Activated)
|
||||
{
|
||||
message.AddMarkup(loc.GetString("The light is currently [color=darkgreen]on[/color]."));
|
||||
message.AddMarkup(Loc.GetString("The light is currently [color=darkgreen]on[/color]."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,11 +97,10 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_pointLight = Owner.GetComponent<PointLightComponent>();
|
||||
_spriteComponent = Owner.GetComponent<SpriteComponent>();
|
||||
Owner.TryGetComponent(out _clothingComponent);
|
||||
Owner.EnsureComponent<PointLightComponent>();
|
||||
_cellContainer =
|
||||
ContainerManagerComponent.Ensure<ContainerSlot>("flashlight_cell_container", Owner, out _);
|
||||
|
||||
Dirty();
|
||||
}
|
||||
|
||||
@@ -140,7 +138,6 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
Activated = false;
|
||||
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Items/flashlight_toggle.ogg", Owner);
|
||||
|
||||
}
|
||||
|
||||
private void TurnOn(IEntity user)
|
||||
@@ -153,10 +150,9 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
var cell = Cell;
|
||||
if (cell == null)
|
||||
{
|
||||
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Machines/button.ogg", Owner);
|
||||
|
||||
_notifyManager.PopupMessage(Owner, user, _localizationManager.GetString("Cell missing..."));
|
||||
_notifyManager.PopupMessage(Owner, user, Loc.GetString("Cell missing..."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -166,7 +162,7 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
if (Wattage > cell.CurrentCharge)
|
||||
{
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Machines/button.ogg", Owner);
|
||||
_notifyManager.PopupMessage(Owner, user, _localizationManager.GetString("Dead cell..."));
|
||||
_notifyManager.PopupMessage(Owner, user, Loc.GetString("Dead cell..."));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -174,25 +170,46 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
SetState(true);
|
||||
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Items/flashlight_toggle.ogg", Owner);
|
||||
|
||||
}
|
||||
|
||||
private void SetState(bool on)
|
||||
{
|
||||
_spriteComponent.LayerSetVisible(1, on);
|
||||
_pointLight.Enabled = on;
|
||||
if (_clothingComponent != null)
|
||||
if (Owner.TryGetComponent(out SpriteComponent? sprite))
|
||||
{
|
||||
_clothingComponent.ClothingEquippedPrefix = on ? "On" : "Off";
|
||||
sprite.LayerSetVisible(1, on);
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out PointLightComponent? light))
|
||||
{
|
||||
light.Enabled = on;
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out ClothingComponent? clothing))
|
||||
{
|
||||
clothing.ClothingEquippedPrefix = on ? "On" : "Off";
|
||||
}
|
||||
}
|
||||
|
||||
public void OnUpdate(float frameTime)
|
||||
{
|
||||
if (!Activated) return;
|
||||
if (!Activated || Cell == null) return;
|
||||
|
||||
var cell = Cell;
|
||||
if (cell == null || !cell.TryUseCharge(Wattage * frameTime)) TurnOff();
|
||||
var appearanceComponent = Owner.GetComponent<AppearanceComponent>();
|
||||
|
||||
if (Cell.MaxCharge - Cell.CurrentCharge < Cell.MaxCharge * 0.70)
|
||||
{
|
||||
appearanceComponent.SetData(HandheldLightVisuals.Power, HandheldLightPowerStates.FullPower);
|
||||
}
|
||||
else if (Cell.MaxCharge - Cell.CurrentCharge < Cell.MaxCharge * 0.90)
|
||||
{
|
||||
appearanceComponent.SetData(HandheldLightVisuals.Power, HandheldLightPowerStates.LowPower);
|
||||
}
|
||||
else
|
||||
{
|
||||
appearanceComponent.SetData(HandheldLightVisuals.Power, HandheldLightPowerStates.Dying);
|
||||
}
|
||||
|
||||
if (Cell == null || !Cell.TryUseCharge(Wattage * frameTime)) TurnOff();
|
||||
|
||||
Dirty();
|
||||
}
|
||||
@@ -211,7 +228,9 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
return;
|
||||
}
|
||||
|
||||
if (!user.TryGetComponent(out HandsComponent hands))
|
||||
Dirty();
|
||||
|
||||
if (!user.TryGetComponent(out HandsComponent? hands))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -222,24 +241,23 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
}
|
||||
|
||||
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Items/pistol_magout.ogg", Owner);
|
||||
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
if (Cell == null)
|
||||
{
|
||||
return new HandheldLightComponentState(null);
|
||||
return new HandheldLightComponentState(null, false);
|
||||
}
|
||||
|
||||
if (Wattage > Cell.CurrentCharge)
|
||||
{
|
||||
// Practically zero.
|
||||
// This is so the item status works correctly.
|
||||
return new HandheldLightComponentState(0);
|
||||
return new HandheldLightComponentState(0, HasCell);
|
||||
}
|
||||
|
||||
return new HandheldLightComponentState(Cell.CurrentCharge / Cell.MaxCharge);
|
||||
return new HandheldLightComponentState(Cell.CurrentCharge / Cell.MaxCharge, HasCell);
|
||||
}
|
||||
|
||||
[Verb]
|
||||
|
||||
@@ -14,11 +14,9 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
[RegisterComponent]
|
||||
public class TilePryingComponent : Component, IAfterInteract
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager;
|
||||
[Dependency] private readonly IMapManager _mapManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
public override string Name => "TilePrying";
|
||||
private bool _toolComponentNeeded = true;
|
||||
|
||||
@@ -29,10 +29,8 @@ namespace Content.Server.GameObjects.Components.Interactable
|
||||
[ComponentReference(typeof(IToolComponent))]
|
||||
public class WelderComponent : ToolComponent, IExamine, IUse, ISuicideAct, ISolutionChange
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override string Name => "Welder";
|
||||
public override uint? NetID => ContentNetIDs.WELDER;
|
||||
|
||||
@@ -20,9 +20,7 @@ namespace Content.Server.GameObjects.Components.Items.Clothing
|
||||
[ComponentReference(typeof(IItemComponent))]
|
||||
public class ClothingComponent : ItemComponent, IUse
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _serverNotifyManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IServerNotifyManager _serverNotifyManager = default!;
|
||||
|
||||
public override string Name => "Clothing";
|
||||
public override uint? NetID => ContentNetIDs.CLOTHING;
|
||||
@@ -63,7 +61,6 @@ namespace Content.Server.GameObjects.Components.Items.Clothing
|
||||
});
|
||||
|
||||
serializer.DataField(ref _quickEquipEnabled, "QuickEquip", true);
|
||||
|
||||
serializer.DataFieldCached(ref _heatResistance, "HeatResistance", 323);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,10 +20,8 @@ namespace Content.Server.GameObjects.Components.Items
|
||||
[RegisterComponent]
|
||||
public class DiceComponent : Component, IActivate, IUse, ILand, IExamine
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
[Dependency] private readonly IRobustRandom _random;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
public override string Name => "Dice";
|
||||
|
||||
@@ -85,9 +83,9 @@ namespace Content.Server.GameObjects.Components.Items
|
||||
void IExamine.Examine(FormattedMessage message, bool inDetailsRange)
|
||||
{
|
||||
//No details check, since the sprite updates to show the side.
|
||||
var loc = IoCManager.Resolve<ILocalizationManager>();
|
||||
message.AddMarkup(loc.GetString("A dice with [color=lightgray]{0}[/color] sides.\n" +
|
||||
"It has landed on a [color=white]{1}[/color].", _sides, _currentSide));
|
||||
message.AddMarkup(Loc.GetString(
|
||||
"A dice with [color=lightgray]{0}[/color] sides.\n" + "It has landed on a [color=white]{1}[/color].",
|
||||
_sides, _currentSide));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,12 @@ namespace Content.Server.GameObjects.Components.Items
|
||||
[RegisterComponent]
|
||||
public class FloorTileItemComponent : Component, IAfterInteract
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager;
|
||||
[Dependency] private readonly IMapManager _mapManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
public override string Name => "FloorTile";
|
||||
private StackComponent _stack;
|
||||
private string _outputTile;
|
||||
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
@@ -34,18 +30,20 @@ namespace Content.Server.GameObjects.Components.Items
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_stack = Owner.GetComponent<StackComponent>();
|
||||
Owner.EnsureComponent<StackComponent>();
|
||||
}
|
||||
|
||||
public void AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
if (!InteractionChecks.InRangeUnobstructed(eventArgs)) return;
|
||||
if (!Owner.TryGetComponent(out StackComponent stack)) return;
|
||||
|
||||
var attacked = eventArgs.Target;
|
||||
var mapGrid = _mapManager.GetGrid(eventArgs.ClickLocation.GridID);
|
||||
var tile = mapGrid.GetTileRef(eventArgs.ClickLocation);
|
||||
var tileDef = (ContentTileDefinition)_tileDefinitionManager[tile.Tile.TypeId];
|
||||
|
||||
if (tileDef.IsSubFloor && attacked == null && _stack.Use(1))
|
||||
if (tileDef.IsSubFloor && attacked == null && stack.Use(1))
|
||||
{
|
||||
var desiredTile = _tileDefinitionManager[_outputTile];
|
||||
mapGrid.SetTile(eventArgs.ClickLocation, new Tile(desiredTile.TileId));
|
||||
|
||||
@@ -15,10 +15,8 @@ namespace Content.Server.GameObjects.Components.Items.RCD
|
||||
[RegisterComponent]
|
||||
public class RCDAmmoComponent : Component, IAfterInteract, IExamine
|
||||
{
|
||||
[Dependency] private IServerNotifyManager _serverNotifyManager = default!;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IServerNotifyManager _serverNotifyManager;
|
||||
#pragma warning restore 649
|
||||
public override string Name => "RCDAmmo";
|
||||
|
||||
//How much ammo we refill
|
||||
|
||||
@@ -25,14 +25,12 @@ namespace Content.Server.GameObjects.Components.Items.RCD
|
||||
[RegisterComponent]
|
||||
public class RCDComponent : Component, IAfterInteract, IUse, IExamine
|
||||
{
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IServerEntityManager _serverEntityManager = default!;
|
||||
[Dependency] private readonly IServerNotifyManager _serverNotifyManager = default!;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager;
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
|
||||
[Dependency] private readonly IMapManager _mapManager;
|
||||
[Dependency] private readonly IServerEntityManager _serverEntityManager;
|
||||
[Dependency] private IServerNotifyManager _serverNotifyManager;
|
||||
#pragma warning restore 649
|
||||
public override string Name => "RCD";
|
||||
private RcdMode _mode = 0; //What mode are we on? Can be floors, walls, deconstruct.
|
||||
private readonly RcdMode[] _modes = (RcdMode[]) Enum.GetValues(typeof(RcdMode));
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
[ComponentReference(typeof(IStorageComponent))]
|
||||
public class EntityStorageComponent : Component, IActivate, IStorageComponent, IInteractUsing, IDestroyAct, IActionBlocker
|
||||
public class EntityStorageComponent : Component, IActivate, IStorageComponent, IInteractUsing, IDestroyAct, IActionBlocker, IExAct
|
||||
{
|
||||
public override string Name => "EntityStorage";
|
||||
|
||||
@@ -430,5 +430,22 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
|
||||
data.Text = component.Open ? "Close" : "Open";
|
||||
}
|
||||
|
||||
void IExAct.OnExplosion(ExplosionEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Severity < ExplosionSeverity.Heavy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var entity in Contents.ContainedEntities)
|
||||
{
|
||||
var exActs = entity.GetAllComponents<IExAct>().ToArray();
|
||||
foreach (var exAct in exActs)
|
||||
{
|
||||
exAct.OnExplosion(eventArgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,9 @@ namespace Content.Server.GameObjects.Components.Items.Storage.Fill
|
||||
[RegisterComponent]
|
||||
internal sealed class MedkitFillComponent : Component, IMapInit
|
||||
{
|
||||
public override string Name => "MedkitFill";
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntityManager _entityManager;
|
||||
#pragma warning restore 649
|
||||
public override string Name => "MedkitFill";
|
||||
|
||||
void IMapInit.MapInit()
|
||||
{
|
||||
|
||||
@@ -10,14 +10,12 @@ namespace Content.Server.GameObjects.Components.Items.Storage.Fill
|
||||
[RegisterComponent]
|
||||
internal sealed class StorageFillComponent : Component, IMapInit
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
public override string Name => "StorageFill";
|
||||
|
||||
private List<string> _contents = new List<string>();
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntityManager _entityManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
@@ -10,11 +10,9 @@ namespace Content.Server.GameObjects.Components.Items.Storage.Fill
|
||||
[RegisterComponent]
|
||||
internal sealed class ToolboxElectricalFillComponent : Component, IMapInit
|
||||
{
|
||||
public override string Name => "ToolboxElectricalFill";
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntityManager _entityManager;
|
||||
#pragma warning restore 649
|
||||
public override string Name => "ToolboxElectricalFill";
|
||||
|
||||
void IMapInit.MapInit()
|
||||
{
|
||||
|
||||
@@ -10,11 +10,9 @@ namespace Content.Server.GameObjects.Components.Items.Storage.Fill
|
||||
[RegisterComponent]
|
||||
internal sealed class ToolboxEmergencyFillComponent : Component, IMapInit
|
||||
{
|
||||
public override string Name => "ToolboxEmergencyFill";
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntityManager _entityManager;
|
||||
#pragma warning restore 649
|
||||
public override string Name => "ToolboxEmergencyFill";
|
||||
|
||||
void IMapInit.MapInit()
|
||||
{
|
||||
|
||||
@@ -10,11 +10,9 @@ namespace Content.Server.GameObjects.Components.Items.Storage.Fill
|
||||
[RegisterComponent]
|
||||
internal sealed class ToolboxGoldFillComponent : Component, IMapInit
|
||||
{
|
||||
public override string Name => "ToolboxGoldFill";
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntityManager _entityManager;
|
||||
#pragma warning restore 649
|
||||
public override string Name => "ToolboxGoldFill";
|
||||
|
||||
void IMapInit.MapInit()
|
||||
{
|
||||
|
||||
@@ -8,11 +8,9 @@ namespace Content.Server.GameObjects.Components.Items.Storage.Fill
|
||||
[RegisterComponent]
|
||||
internal sealed class UtilityBeltClothingFillComponent : Component, IMapInit
|
||||
{
|
||||
public override string Name => "UtilityBeltClothingFill";
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntityManager _entityManager;
|
||||
#pragma warning restore 649
|
||||
public override string Name => "UtilityBeltClothingFill";
|
||||
|
||||
void IMapInit.MapInit()
|
||||
{
|
||||
|
||||
@@ -24,13 +24,11 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
[ComponentReference(typeof(IItemComponent))]
|
||||
public class ItemComponent : StorableComponent, IInteractHand, IExAct, IEquipped, IUnequipped, IItemComponent
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
public override string Name => "Item";
|
||||
public override uint? NetID => ContentNetIDs.ITEM;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IMapManager _mapManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
private string _equippedPrefix;
|
||||
|
||||
public string EquippedPrefix
|
||||
|
||||
@@ -37,10 +37,8 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
||||
public class ServerStorageComponent : SharedStorageComponent, IInteractUsing, IUse, IActivate, IStorageComponent, IDestroyAct, IExAct,
|
||||
IDragDrop
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
#pragma warning restore 649
|
||||
|
||||
private const string LoggerName = "Storage";
|
||||
|
||||
|
||||
@@ -16,10 +16,8 @@ namespace Content.Server.GameObjects.Components.Items
|
||||
[RegisterComponent]
|
||||
public class ToysComponent : Component, IActivate, IUse, ILand
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
[Dependency] private readonly IRobustRandom _random;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
public override string Name => "Toys";
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -14,6 +15,7 @@ using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.Chat;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components.Power;
|
||||
using Content.Shared.Interfaces;
|
||||
@@ -39,25 +41,19 @@ namespace Content.Server.GameObjects.Components.Kitchen
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
public class MicrowaveComponent : SharedMicrowaveComponent, IActivate, IInteractUsing, ISolutionChange, ISuicideAct
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntityManager _entityManager;
|
||||
[Dependency] private readonly RecipeManager _recipeManager;
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly RecipeManager _recipeManager = default!;
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
|
||||
#region YAMLSERIALIZE
|
||||
private int _cookTimeDefault;
|
||||
private int _cookTimeMultiplier; //For upgrades and stuff I guess?
|
||||
private string _badRecipeName;
|
||||
private string _startCookingSound;
|
||||
private string _cookingCompleteSound;
|
||||
private string _badRecipeName = "";
|
||||
private string _startCookingSound = "";
|
||||
private string _cookingCompleteSound = "";
|
||||
#endregion
|
||||
|
||||
#region VIEWVARIABLES
|
||||
[ViewVariables]
|
||||
private SolutionComponent _solution;
|
||||
|
||||
[ViewVariables]
|
||||
[ViewVariables]
|
||||
private bool _busy = false;
|
||||
|
||||
/// <summary>
|
||||
@@ -67,20 +63,18 @@ namespace Content.Server.GameObjects.Components.Kitchen
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private uint _currentCookTimerTime = 1;
|
||||
#endregion
|
||||
|
||||
private bool _powered => _powerReceiver.Powered;
|
||||
private bool _hasContents => _solution.ReagentList.Count > 0 || _storage.ContainedEntities.Count > 0;
|
||||
private bool Powered => !Owner.TryGetComponent(out PowerReceiverComponent? receiver) || receiver.Powered;
|
||||
private bool _hasContents => Owner.TryGetComponent(out SolutionComponent? solution) && (solution.ReagentList.Count > 0 || _storage.ContainedEntities.Count > 0);
|
||||
private bool _uiDirty = true;
|
||||
private bool _lostPower = false;
|
||||
private int _currentCookTimeButtonIndex = 0;
|
||||
|
||||
void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) => _uiDirty = true;
|
||||
private AudioSystem _audioSystem;
|
||||
private AppearanceComponent _appearance;
|
||||
private PowerReceiverComponent _powerReceiver;
|
||||
private BoundUserInterface _userInterface;
|
||||
private Container _storage;
|
||||
private AudioSystem _audioSystem = default!;
|
||||
private Container _storage = default!;
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(MicrowaveUiKey.Key);
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
@@ -95,22 +89,21 @@ namespace Content.Server.GameObjects.Components.Kitchen
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_solution ??= Owner.TryGetComponent(out SolutionComponent solutionComponent)
|
||||
? solutionComponent
|
||||
: Owner.AddComponent<SolutionComponent>();
|
||||
|
||||
Owner.EnsureComponent<SolutionComponent>();
|
||||
|
||||
_storage = ContainerManagerComponent.Ensure<Container>("microwave_entity_container", Owner, out var existed);
|
||||
_appearance = Owner.GetComponent<AppearanceComponent>();
|
||||
_powerReceiver = Owner.GetComponent<PowerReceiverComponent>();
|
||||
_audioSystem = EntitySystem.Get<AudioSystem>();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(MicrowaveUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
|
||||
}
|
||||
}
|
||||
|
||||
private void UserInterfaceOnReceiveMessage(ServerBoundUserInterfaceMessage message)
|
||||
{
|
||||
if (!_powered || _busy)
|
||||
if (!Powered || _busy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -158,13 +151,13 @@ namespace Content.Server.GameObjects.Components.Kitchen
|
||||
public void OnUpdate()
|
||||
{
|
||||
|
||||
if (!_powered)
|
||||
if (!Powered)
|
||||
{
|
||||
//TODO:If someone cuts power currently, microwave magically keeps going. FIX IT!
|
||||
SetAppearance(MicrowaveVisualState.Idle);
|
||||
}
|
||||
|
||||
if (_busy && !_powered)
|
||||
if (_busy && !Powered)
|
||||
{
|
||||
//we lost power while we were cooking/busy!
|
||||
_lostPower = true;
|
||||
@@ -174,11 +167,11 @@ namespace Content.Server.GameObjects.Components.Kitchen
|
||||
_uiDirty = true;
|
||||
}
|
||||
|
||||
if (_uiDirty)
|
||||
if (_uiDirty && Owner.TryGetComponent(out SolutionComponent? solution))
|
||||
{
|
||||
_userInterface.SetState(new MicrowaveUpdateUserInterfaceState
|
||||
UserInterface?.SetState(new MicrowaveUpdateUserInterfaceState
|
||||
(
|
||||
_solution.Solution.Contents.ToArray(),
|
||||
solution.Solution.Contents.ToArray(),
|
||||
_storage.ContainedEntities.Select(item => item.Uid).ToArray(),
|
||||
_busy,
|
||||
_currentCookTimeButtonIndex,
|
||||
@@ -190,26 +183,26 @@ namespace Content.Server.GameObjects.Components.Kitchen
|
||||
|
||||
private void SetAppearance(MicrowaveVisualState state)
|
||||
{
|
||||
if (_appearance != null || Owner.TryGetComponent(out _appearance))
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
_appearance.SetData(PowerDeviceVisuals.VisualState, state);
|
||||
appearance.SetData(PowerDeviceVisuals.VisualState, state);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent actor) || !_powered)
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent? actor) || !Powered)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_uiDirty = true;
|
||||
_userInterface.Open(actor.playerSession);
|
||||
UserInterface?.Open(actor.playerSession);
|
||||
}
|
||||
|
||||
public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (!_powered)
|
||||
if (!Powered)
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, eventArgs.User,
|
||||
Loc.GetString("It has no power!"));
|
||||
@@ -232,8 +225,13 @@ namespace Content.Server.GameObjects.Components.Kitchen
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Owner.TryGetComponent(out SolutionComponent? solution))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//Get transfer amount. May be smaller than _transferAmount if not enough room
|
||||
var realTransferAmount = ReagentUnit.Min(attackPourable.TransferAmount, _solution.EmptyVolume);
|
||||
var realTransferAmount = ReagentUnit.Min(attackPourable.TransferAmount, solution.EmptyVolume);
|
||||
if (realTransferAmount <= 0) //Special message if container is full
|
||||
{
|
||||
_notifyManager.PopupMessage(Owner.Transform.GridPosition, eventArgs.User,
|
||||
@@ -243,7 +241,7 @@ namespace Content.Server.GameObjects.Components.Kitchen
|
||||
|
||||
//Move units from attackSolution to targetSolution
|
||||
var removedSolution = attackSolution.SplitSolution(realTransferAmount);
|
||||
if (!_solution.TryAddSolution(removedSolution))
|
||||
if (!solution.TryAddSolution(removedSolution))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -280,6 +278,11 @@ namespace Content.Server.GameObjects.Components.Kitchen
|
||||
var solidsDict = new Dictionary<string, int>();
|
||||
foreach(var item in _storage.ContainedEntities)
|
||||
{
|
||||
if (item.Prototype == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(solidsDict.ContainsKey(item.Prototype.ID))
|
||||
{
|
||||
solidsDict[item.Prototype.ID]++;
|
||||
@@ -303,7 +306,7 @@ namespace Content.Server.GameObjects.Components.Kitchen
|
||||
}
|
||||
|
||||
// Check recipes
|
||||
FoodRecipePrototype recipeToCook = null;
|
||||
FoodRecipePrototype? recipeToCook = null;
|
||||
foreach (var r in _recipeManager.Recipes.Where(r => CanSatisfyRecipe(r, solidsDict) == MicrowaveSuccessState.RecipePass))
|
||||
{
|
||||
recipeToCook = r;
|
||||
@@ -330,7 +333,7 @@ namespace Content.Server.GameObjects.Components.Kitchen
|
||||
{
|
||||
if (goodMeal)
|
||||
{
|
||||
SubtractContents(recipeToCook);
|
||||
SubtractContents(recipeToCook!);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -357,12 +360,18 @@ namespace Content.Server.GameObjects.Components.Kitchen
|
||||
|
||||
private void VaporizeReagents()
|
||||
{
|
||||
_solution.RemoveAllSolution();
|
||||
if (Owner.TryGetComponent(out SolutionComponent? solution))
|
||||
{
|
||||
solution.RemoveAllSolution();
|
||||
}
|
||||
}
|
||||
|
||||
private void VaporizeReagentQuantity(Solution.ReagentQuantity reagentQuantity)
|
||||
{
|
||||
_solution.TryRemoveReagent(reagentQuantity.ReagentId, reagentQuantity.Quantity);
|
||||
if (Owner.TryGetComponent(out SolutionComponent? solution))
|
||||
{
|
||||
solution?.TryRemoveReagent(reagentQuantity.ReagentId, reagentQuantity.Quantity);
|
||||
}
|
||||
}
|
||||
|
||||
private void VaporizeSolids()
|
||||
@@ -395,9 +404,14 @@ namespace Content.Server.GameObjects.Components.Kitchen
|
||||
|
||||
private void SubtractContents(FoodRecipePrototype recipe)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SolutionComponent? solution))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach(var recipeReagent in recipe.IngredientsReagents)
|
||||
{
|
||||
_solution.TryRemoveReagent(recipeReagent.Key, ReagentUnit.New(recipeReagent.Value));
|
||||
solution?.TryRemoveReagent(recipeReagent.Key, ReagentUnit.New(recipeReagent.Value));
|
||||
}
|
||||
|
||||
foreach (var recipeSolid in recipe.IngredientsSolids)
|
||||
@@ -406,6 +420,11 @@ namespace Content.Server.GameObjects.Components.Kitchen
|
||||
{
|
||||
foreach (var item in _storage.ContainedEntities)
|
||||
{
|
||||
if (item.Prototype == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.Prototype.ID == recipeSolid.Key)
|
||||
{
|
||||
_storage.Remove(item);
|
||||
@@ -420,9 +439,14 @@ namespace Content.Server.GameObjects.Components.Kitchen
|
||||
|
||||
private MicrowaveSuccessState CanSatisfyRecipe(FoodRecipePrototype recipe, Dictionary<string,int> solids)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out SolutionComponent? solution))
|
||||
{
|
||||
return MicrowaveSuccessState.RecipeFail;
|
||||
}
|
||||
|
||||
foreach (var reagent in recipe.IngredientsReagents)
|
||||
{
|
||||
if (!_solution.ContainsReagent(reagent.Key, out var amount))
|
||||
if (!solution.ContainsReagent(reagent.Key, out var amount))
|
||||
{
|
||||
return MicrowaveSuccessState.RecipeFail;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
#nullable enable
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
@@ -8,6 +10,7 @@ using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components
|
||||
{
|
||||
@@ -15,19 +18,36 @@ namespace Content.Server.GameObjects.Components
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
public class MagicMirrorComponent : SharedMagicMirrorComponent, IActivate
|
||||
{
|
||||
private BoundUserInterface _userInterface;
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(MagicMirrorUiKey.Key);
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(MagicMirrorUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage -= OnUiReceiveMessage;
|
||||
}
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
private static void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||
{
|
||||
if (!obj.Session.AttachedEntity.TryGetComponent(out HumanoidAppearanceComponent looks))
|
||||
if (obj.Session.AttachedEntity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!obj.Session.AttachedEntity.TryGetComponent(out HumanoidAppearanceComponent? looks))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -70,23 +90,23 @@ namespace Content.Server.GameObjects.Components
|
||||
|
||||
public void Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!eventArgs.User.TryGetComponent(out HumanoidAppearanceComponent looks))
|
||||
if (!eventArgs.User.TryGetComponent(out HumanoidAppearanceComponent? looks))
|
||||
{
|
||||
Owner.PopupMessage(eventArgs.User, Loc.GetString("You can't have any hair!"));
|
||||
return;
|
||||
}
|
||||
|
||||
_userInterface.Open(actor.playerSession);
|
||||
UserInterface?.Open(actor.playerSession);
|
||||
|
||||
var msg = new MagicMirrorInitialDataMessage(looks.Appearance.HairColor, looks.Appearance.FacialHairColor, looks.Appearance.HairStyleName,
|
||||
looks.Appearance.FacialHairStyleName);
|
||||
|
||||
_userInterface.SendMessage(msg, actor.playerSession);
|
||||
UserInterface?.SendMessage(msg, actor.playerSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,12 @@ namespace Content.Server.GameObjects.Components.Markers
|
||||
[RegisterComponent]
|
||||
public class ConditionalSpawnerComponent : Component, IMapInit
|
||||
{
|
||||
public override string Name => "ConditionalSpawner";
|
||||
[Dependency] private readonly IGameTicker _gameTicker = default!;
|
||||
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IGameTicker _gameTicker;
|
||||
[Dependency] private IReflectionManager _reflectionManager;
|
||||
[Dependency] private IEntityManager _entityManager;
|
||||
[Dependency] private IRobustRandom _robustRandom;
|
||||
#pragma warning restore 649
|
||||
public override string Name => "ConditionalSpawner";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public List<string> Prototypes { get; set; } = new List<string>();
|
||||
|
||||
@@ -12,9 +12,7 @@ namespace Content.Server.GameObjects.Components.Markers
|
||||
[ComponentReference(typeof(SharedSpawnPointComponent))]
|
||||
public sealed class SpawnPointComponent : SharedSpawnPointComponent
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IPrototypeManager _prototypeManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private SpawnPointType _spawnType;
|
||||
|
||||
@@ -15,10 +15,8 @@ namespace Content.Server.GameObjects.Components.Markers
|
||||
[RegisterComponent]
|
||||
public class TimedSpawnerComponent : Component
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IEntityManager _entityManager;
|
||||
[Dependency] private IRobustRandom _robustRandom;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
|
||||
public override string Name => "TimedSpawner";
|
||||
|
||||
|
||||
@@ -14,12 +14,10 @@ namespace Content.Server.GameObjects.Components.Markers
|
||||
[RegisterComponent]
|
||||
public class TrashSpawnerComponent : ConditionalSpawnerComponent
|
||||
{
|
||||
public override string Name => "TrashSpawner";
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IEntityManager _entityManager;
|
||||
[Dependency] private IRobustRandom _robustRandom;
|
||||
#pragma warning restore 649
|
||||
public override string Name => "TrashSpawner";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public List<string> RarePrototypes { get; set; } = new List<string>();
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Stack;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Body;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Medical
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class HealingComponent : Component, IAfterInteract
|
||||
{
|
||||
public override string Name => "Healing";
|
||||
|
||||
public Dictionary<DamageType, int> Heal { get; private set; }
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(this, h => h.Heal, "heal", new Dictionary<DamageType, int>());
|
||||
}
|
||||
|
||||
public void AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Target == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!eventArgs.Target.TryGetComponent(out IBodyManagerComponent body))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ActionBlockerSystem.CanInteract(eventArgs.User))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventArgs.User != eventArgs.Target)
|
||||
{
|
||||
var interactionSystem = EntitySystem.Get<SharedInteractionSystem>();
|
||||
var from = eventArgs.User.Transform.MapPosition;
|
||||
var to = eventArgs.Target.Transform.MapPosition;
|
||||
bool Ignored(IEntity entity) => entity == eventArgs.User || entity == eventArgs.Target;
|
||||
var inRange = interactionSystem.InRangeUnobstructed(from, to, predicate: Ignored);
|
||||
|
||||
if (!inRange)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out StackComponent stack) &&
|
||||
!stack.Use(1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (type, amount) in Heal)
|
||||
{
|
||||
body.ChangeDamage(type, -amount, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Medical;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
@@ -16,6 +18,7 @@ using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Content.Shared.Damage;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Medical
|
||||
{
|
||||
@@ -23,29 +26,29 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
public class MedicalScannerComponent : SharedMedicalScannerComponent, IActivate
|
||||
{
|
||||
private AppearanceComponent _appearance;
|
||||
private BoundUserInterface _userInterface;
|
||||
private ContainerSlot _bodyContainer;
|
||||
private ContainerSlot _bodyContainer = default!;
|
||||
private readonly Vector2 _ejectOffset = new Vector2(-0.5f, 0f);
|
||||
public bool IsOccupied => _bodyContainer.ContainedEntity != null;
|
||||
|
||||
private PowerReceiverComponent _powerReceiver;
|
||||
private bool Powered => _powerReceiver.Powered;
|
||||
[ViewVariables]
|
||||
private bool Powered => !Owner.TryGetComponent(out PowerReceiverComponent? receiver) || receiver.Powered;
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(MedicalScannerUiKey.Key);
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_appearance = Owner.GetComponent<AppearanceComponent>();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
|
||||
.GetBoundUserInterface(MedicalScannerUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
_bodyContainer = ContainerManagerComponent.Ensure<ContainerSlot>($"{Name}-bodyContainer", Owner);
|
||||
_powerReceiver = Owner.GetComponent<PowerReceiverComponent>();
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
}
|
||||
|
||||
//TODO: write this so that it checks for a change in power events and acts accordingly.
|
||||
_bodyContainer = ContainerManagerComponent.Ensure<ContainerSlot>($"{Name}-bodyContainer", Owner);
|
||||
|
||||
// TODO: write this so that it checks for a change in power events and acts accordingly.
|
||||
var newState = GetUserInterfaceState();
|
||||
_userInterface.SetState(newState);
|
||||
UserInterface?.SetState(newState);
|
||||
|
||||
UpdateUserInterface();
|
||||
}
|
||||
@@ -62,11 +65,15 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
var body = _bodyContainer.ContainedEntity;
|
||||
if (body == null)
|
||||
{
|
||||
_appearance.SetData(MedicalScannerVisuals.Status, MedicalScannerStatus.Open);
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance?.SetData(MedicalScannerVisuals.Status, MedicalScannerStatus.Open);
|
||||
};
|
||||
|
||||
return EmptyUIState;
|
||||
}
|
||||
|
||||
if (!body.TryGetComponent(out IDamageableComponent damageable) ||
|
||||
if (!body.TryGetComponent(out IDamageableComponent? damageable) ||
|
||||
damageable.CurrentDamageState == DamageState.Dead)
|
||||
{
|
||||
return EmptyUIState;
|
||||
@@ -86,7 +93,7 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
}
|
||||
|
||||
var newState = GetUserInterfaceState();
|
||||
_userInterface.SetState(newState);
|
||||
UserInterface?.SetState(newState);
|
||||
}
|
||||
|
||||
private MedicalScannerStatus GetStatusFromDamageState(DamageState damageState)
|
||||
@@ -115,12 +122,15 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
|
||||
private void UpdateAppearance()
|
||||
{
|
||||
_appearance.SetData(MedicalScannerVisuals.Status, GetStatus());
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(MedicalScannerVisuals.Status, GetStatus());
|
||||
}
|
||||
}
|
||||
|
||||
public void Activate(ActivateEventArgs args)
|
||||
{
|
||||
if (!args.User.TryGetComponent(out IActorComponent actor))
|
||||
if (!args.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -128,7 +138,7 @@ namespace Content.Server.GameObjects.Components.Medical
|
||||
if (!Powered)
|
||||
return;
|
||||
|
||||
_userInterface.Open(actor.playerSession);
|
||||
UserInterface?.Open(actor.playerSession);
|
||||
}
|
||||
|
||||
[Verb]
|
||||
|
||||
@@ -20,9 +20,7 @@ namespace Content.Server.GameObjects.Components.Metabolism
|
||||
[RegisterComponent]
|
||||
public class MetabolismComponent : Component
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override string Name => "Metabolism";
|
||||
|
||||
|
||||
@@ -17,17 +17,16 @@ namespace Content.Server.GameObjects.Components.Mining
|
||||
[RegisterComponent]
|
||||
public class AsteroidRockComponent : Component, IInteractUsing
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
public override string Name => "AsteroidRock";
|
||||
private static readonly string[] SpriteStates = {"0", "1", "2", "3", "4"};
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IRobustRandom _random;
|
||||
#pragma warning restore 649
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
var spriteComponent = Owner.GetComponent<SpriteComponent>();
|
||||
|
||||
var spriteComponent = Owner.EnsureComponent<SpriteComponent>();
|
||||
spriteComponent.LayerSetState(0, _random.Pick(SpriteStates));
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Server.Mobs;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
@@ -163,6 +164,11 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
{
|
||||
public void EnterState(IEntity entity)
|
||||
{
|
||||
if (entity.TryGetComponent(out AppearanceComponent appearance))
|
||||
{
|
||||
appearance.SetData(DamageStateVisuals.State, DamageState.Alive);
|
||||
}
|
||||
|
||||
UpdateState(entity);
|
||||
}
|
||||
|
||||
@@ -187,7 +193,12 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
{
|
||||
case RuinableComponent ruinable:
|
||||
{
|
||||
var modifier = (int) (ruinable.TotalDamage / (ruinable.MaxHp / 7f));
|
||||
if (ruinable.DeadThreshold == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var modifier = (int) (ruinable.TotalDamage / (ruinable.DeadThreshold / 7f));
|
||||
|
||||
status.ChangeStatusEffectIcon(StatusEffect.Health,
|
||||
"/Textures/Interface/StatusEffects/Human/human" + modifier + ".png");
|
||||
@@ -196,8 +207,12 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
}
|
||||
case BodyManagerComponent body:
|
||||
{
|
||||
// TODO: Declare body max normal damage (currently 100)
|
||||
var modifier = (int) (body.TotalDamage / (100f / 7f));
|
||||
if (body.CriticalThreshold == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var modifier = (int) (body.TotalDamage / (body.CriticalThreshold / 7f));
|
||||
|
||||
status.ChangeStatusEffectIcon(StatusEffect.Health,
|
||||
"/Textures/Interface/StatusEffects/Human/human" + modifier + ".png");
|
||||
@@ -281,6 +296,11 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
{
|
||||
public void EnterState(IEntity entity)
|
||||
{
|
||||
if (entity.TryGetComponent(out AppearanceComponent appearance))
|
||||
{
|
||||
appearance.SetData(DamageStateVisuals.State, DamageState.Critical);
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out ServerStatusEffectsComponent status))
|
||||
{
|
||||
status.ChangeStatusEffectIcon(StatusEffect.Health,
|
||||
@@ -382,6 +402,11 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
{
|
||||
public void EnterState(IEntity entity)
|
||||
{
|
||||
if (entity.TryGetComponent(out AppearanceComponent appearance))
|
||||
{
|
||||
appearance.SetData(DamageStateVisuals.State, DamageState.Dead);
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out ServerStatusEffectsComponent status))
|
||||
{
|
||||
status.ChangeStatusEffectIcon(StatusEffect.Health,
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using Content.Server.Interfaces.Chat;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Mobs.Speech
|
||||
{
|
||||
public interface IAccentManager
|
||||
{
|
||||
public void Initialize();
|
||||
}
|
||||
|
||||
public class AccentManager : IAccentManager
|
||||
{
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
[Dependency] private readonly IComponentManager _componentManager = default!;
|
||||
|
||||
public static readonly Regex SentenceRegex = new Regex(@"(?<=[\.!\?])");
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_chatManager.RegisterChatTransform(AccentHandler);
|
||||
}
|
||||
|
||||
public string AccentHandler(IEntity player, string message)
|
||||
{
|
||||
//TODO: give accents a prio?
|
||||
var accents = _componentManager.GetComponents<IAccentComponent>(player.Uid);
|
||||
foreach (var accent in accents)
|
||||
{
|
||||
message = accent.Accentuate(message);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using System;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Mobs.Speech
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class BackwardsAccentComponent : Component, IAccentComponent
|
||||
{
|
||||
public override string Name => "BackwardsAccent";
|
||||
|
||||
public string Accentuate(string message)
|
||||
{
|
||||
var arr = message.ToCharArray();
|
||||
Array.Reverse(arr);
|
||||
return new string(arr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Random;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Mobs.Speech
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class OwOAccentComponent : Component, IAccentComponent
|
||||
{
|
||||
public override string Name => "OwOAccent";
|
||||
|
||||
private static readonly IReadOnlyList<string> Faces = new List<string>{
|
||||
" (・`ω´・)", " ;;w;;", " owo", " UwU", " >w<", " ^w^"
|
||||
}.AsReadOnly();
|
||||
private string RandomFace => IoCManager.Resolve<IRobustRandom>().Pick(Faces);
|
||||
|
||||
private static readonly Dictionary<string, string> SpecialWords = new Dictionary<string, string>
|
||||
{
|
||||
{ "you", "wu" },
|
||||
};
|
||||
|
||||
public string Accentuate(string message)
|
||||
{
|
||||
foreach ((var word,var repl) in SpecialWords)
|
||||
{
|
||||
message = message.Replace(word, repl);
|
||||
}
|
||||
|
||||
return message.Replace("!", RandomFace)
|
||||
.Replace("r", "w").Replace("R", "W")
|
||||
.Replace("l", "w").Replace("L", "W");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using Microsoft.CodeAnalysis.Operations;
|
||||
using Microsoft.EntityFrameworkCore.Internal;
|
||||
using Robust.Shared.GameObjects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Mobs.Speech
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class SpanishAccentComponent : Component, IAccentComponent
|
||||
{
|
||||
public override string Name => "SpanishAccent";
|
||||
|
||||
public string Accentuate(string message)
|
||||
{
|
||||
// Insert E before every S
|
||||
message = InsertS(message);
|
||||
// If a sentence ends with ?, insert a reverse ? at the beginning of the sentence
|
||||
message = ReplaceQuestionMark(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
private string InsertS(string message)
|
||||
{
|
||||
// Replace every new Word that starts with s/S
|
||||
var msg = message.Replace(" s", " es").Replace(" S", "Es");
|
||||
|
||||
// Still need to check if the beginning of the message starts
|
||||
if (msg.StartsWith("s"))
|
||||
{
|
||||
return msg.Remove(0, 1).Insert(0, "es");
|
||||
}
|
||||
else if (msg.StartsWith("S"))
|
||||
{
|
||||
return msg.Remove(0, 1).Insert(0, "Es");
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
private string ReplaceQuestionMark(string message)
|
||||
{
|
||||
var sentences = AccentManager.SentenceRegex.Split(message);
|
||||
var msg = "";
|
||||
foreach (var s in sentences)
|
||||
{
|
||||
if (s.EndsWith("?")) // We've got a question => add ¿ to the beginning
|
||||
{
|
||||
// Because we don't split by whitespace, we may have some spaces in front of the sentence.
|
||||
// So we add the symbol before the first non space char
|
||||
msg += s.Insert(s.Length - s.TrimStart().Length, "¿");
|
||||
}
|
||||
else
|
||||
{
|
||||
msg += s;
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using Robust.Server.Interfaces.Console;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Mobs.Speech
|
||||
{
|
||||
internal interface IAccentComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Transforms a message with the given Accent
|
||||
/// </summary>
|
||||
/// <param name="message">The spoken message</param>
|
||||
/// <returns>The message after the transformation</returns>
|
||||
public string Accentuate(string message);
|
||||
}
|
||||
|
||||
public class AddAccent : IClientCommand
|
||||
{
|
||||
public string Command => "addaccent";
|
||||
|
||||
public string Description => "Add a speech component to the current player";
|
||||
|
||||
public string Help => $"{Command} <component>/?";
|
||||
|
||||
public void Execute(IConsoleShell shell, IPlayerSession player, string[] args)
|
||||
{
|
||||
if (args.Length == 0)
|
||||
{
|
||||
shell.SendText(player, Help);
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.AttachedEntity == null)
|
||||
{
|
||||
shell.SendText(player, "You don't have a player!");
|
||||
return;
|
||||
}
|
||||
|
||||
var compFactory = IoCManager.Resolve<IComponentFactory>();
|
||||
|
||||
if (args[0] == "?")
|
||||
{
|
||||
// Get all components that implement the ISpeechComponent except
|
||||
var speeches = compFactory.GetAllRefTypes()
|
||||
.Where(c => typeof(IAccentComponent).IsAssignableFrom(c) && c.IsClass);
|
||||
var msg = "";
|
||||
foreach(var s in speeches)
|
||||
{
|
||||
msg += $"{compFactory.GetRegistration(s).Name}\n";
|
||||
}
|
||||
shell.SendText(player, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
var name = args[0];
|
||||
// Try to get the Component
|
||||
Type type;
|
||||
try
|
||||
{
|
||||
var comp = compFactory.GetComponent(name);
|
||||
type = comp.GetType();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
shell.SendText(player, $"Accent {name} not found. Try {Command} ? to get a list of all appliable accents.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if that already exists
|
||||
try
|
||||
{
|
||||
var comp = player.AttachedEntity.GetComponent(type);
|
||||
shell.SendText(player, "You already have this accent!");
|
||||
return;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Accent not found
|
||||
}
|
||||
|
||||
// Generic fuckery
|
||||
var ensure = typeof(IEntity).GetMethod("AddComponent");
|
||||
if (ensure == null)
|
||||
return;
|
||||
var method = ensure.MakeGenericMethod(type);
|
||||
method.Invoke(player.AttachedEntity, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,7 @@ namespace Content.Server.GameObjects.Components.Mobs
|
||||
[ComponentReference(typeof(SharedStunnableComponent))]
|
||||
public class StunnableComponent : SharedStunnableComponent
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IGameTiming _gameTiming;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
protected override void OnKnockdown()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using Content.Server.Mobs;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Mobs
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class VisitingMindComponent : Component
|
||||
{
|
||||
public override string Name => "VisitingMind";
|
||||
|
||||
public Mind Mind { get; set; }
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
Mind?.UnVisit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
using Content.Server.GameObjects.EntitySystems.AI;
|
||||
#nullable enable
|
||||
using Content.Server.GameObjects.EntitySystems.AI;
|
||||
using Content.Server.Interfaces.GameTicking;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Server.AI;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -14,23 +19,26 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
[RegisterComponent, ComponentReference(typeof(IMoverComponent))]
|
||||
public class AiControllerComponent : Component, IMoverComponent
|
||||
{
|
||||
private string _logicName;
|
||||
private string? _logicName;
|
||||
private float _visionRadius;
|
||||
|
||||
public override string Name => "AiController";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string LogicName
|
||||
public string? LogicName
|
||||
{
|
||||
get => _logicName;
|
||||
set
|
||||
{
|
||||
_logicName = value;
|
||||
Processor = null;
|
||||
Processor = null!;
|
||||
}
|
||||
}
|
||||
|
||||
public AiLogicProcessor Processor { get; set; }
|
||||
public AiLogicProcessor? Processor { get; set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string? StartingGearPrototype { get; set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float VisionRadius
|
||||
@@ -45,18 +53,34 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
base.Initialize();
|
||||
|
||||
// This component requires a collidable component.
|
||||
if (!Owner.HasComponent<ICollidableComponent>())
|
||||
Owner.AddComponent<CollidableComponent>();
|
||||
|
||||
Owner.EnsureComponent<CollidableComponent>();
|
||||
|
||||
EntitySystem.Get<AiSystem>().ProcessorInitialize(this);
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
if (StartingGearPrototype != null)
|
||||
{
|
||||
var startingGear = IoCManager.Resolve<IPrototypeManager>().Index<StartingGearPrototype>(StartingGearPrototype);
|
||||
IoCManager.Resolve<IGameTicker>().EquipStartingGear(Owner, startingGear);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(ref _logicName, "logic", null);
|
||||
serializer.DataReadWriteFunction(
|
||||
"startingGear",
|
||||
null,
|
||||
startingGear => StartingGearPrototype = startingGear,
|
||||
() => StartingGearPrototype);
|
||||
serializer.DataField(ref _visionRadius, "vision", 8.0f);
|
||||
}
|
||||
|
||||
@@ -74,7 +98,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent component))
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent? component))
|
||||
{
|
||||
return component.CurrentWalkSpeed;
|
||||
}
|
||||
@@ -91,7 +115,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent component))
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent? component))
|
||||
{
|
||||
return component.CurrentSprintSpeed;
|
||||
}
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
|
||||
using Content.Server.GameObjects.Components.Body;
|
||||
using Content.Server.GameObjects.EntitySystems.DoAfter;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Verbs;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Server.GameObjects.Components.Body;
|
||||
using Content.Server.GameObjects.EntitySystems.DoAfter;
|
||||
using Robust.Shared.Maths;
|
||||
using System;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Movement
|
||||
@@ -24,10 +24,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
[ComponentReference(typeof(IClimbable))]
|
||||
public class ClimbableComponent : SharedClimbableComponent, IDragDropOn
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
#pragma warning restore 649
|
||||
|
||||
/// <summary>
|
||||
/// The range from which this entity can be climbed.
|
||||
@@ -41,14 +38,17 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
[ViewVariables]
|
||||
private float _climbDelay;
|
||||
|
||||
private ICollidableComponent _collidableComponent;
|
||||
private DoAfterSystem _doAfterSystem;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_collidableComponent = Owner.GetComponent<ICollidableComponent>();
|
||||
if (!Owner.EnsureComponent(out CollidableComponent _))
|
||||
{
|
||||
Logger.Warning($"Entity {Owner.Name} at {Owner.Transform.MapPosition} didn't have a {nameof(CollidableComponent)}");
|
||||
}
|
||||
|
||||
_doAfterSystem = EntitySystem.Get<DoAfterSystem>();
|
||||
}
|
||||
|
||||
@@ -62,68 +62,101 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
|
||||
bool IDragDropOn.CanDragDropOn(DragDropEventArgs eventArgs)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(eventArgs.User))
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't do that!"));
|
||||
string reason;
|
||||
bool canVault;
|
||||
|
||||
if (eventArgs.User == eventArgs.Dropped)
|
||||
canVault = CanVault(eventArgs.User, eventArgs.Target, out reason);
|
||||
else
|
||||
canVault = CanVault(eventArgs.User, eventArgs.Dropped, eventArgs.Target, out reason);
|
||||
|
||||
if (!canVault)
|
||||
eventArgs.User.PopupMessage(eventArgs.User, reason);
|
||||
|
||||
return canVault;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the user can vault the target
|
||||
/// </summary>
|
||||
/// <param name="user">The entity that wants to vault</param>
|
||||
/// <param name="target">The object that is being vaulted</param>
|
||||
/// <param name="reason">The reason why it cant be dropped</param>
|
||||
/// <returns></returns>
|
||||
private bool CanVault(IEntity user, IEntity target, out string reason)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user))
|
||||
{
|
||||
reason = Loc.GetString("You can't do that!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eventArgs.User == eventArgs.Dropped) // user is dragging themselves onto a climbable
|
||||
if (!user.HasComponent<ClimbingComponent>())
|
||||
{
|
||||
if (!eventArgs.User.HasComponent<ClimbingComponent>())
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You are incapable of climbing!"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var bodyManager = eventArgs.User.GetComponent<BodyManagerComponent>();
|
||||
|
||||
if (bodyManager.GetBodyPartsOfType(Shared.GameObjects.Components.Body.BodyPartType.Leg).Count == 0 ||
|
||||
bodyManager.GetBodyPartsOfType(Shared.GameObjects.Components.Body.BodyPartType.Foot).Count == 0)
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You are unable to climb!"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var userPosition = eventArgs.User.Transform.MapPosition;
|
||||
var climbablePosition = eventArgs.Target.Transform.MapPosition;
|
||||
var interaction = EntitySystem.Get<SharedInteractionSystem>();
|
||||
bool Ignored(IEntity entity) => (entity == eventArgs.Target || entity == eventArgs.User);
|
||||
|
||||
if (!interaction.InRangeUnobstructed(userPosition, climbablePosition, _range, predicate: Ignored))
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't reach there!"));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else // user is dragging some other entity onto a climbable
|
||||
{
|
||||
if (eventArgs.Target == null || !eventArgs.Dropped.HasComponent<ClimbingComponent>())
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't do that!"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var userPosition = eventArgs.User.Transform.MapPosition;
|
||||
var otherUserPosition = eventArgs.Dropped.Transform.MapPosition;
|
||||
var climbablePosition = eventArgs.Target.Transform.MapPosition;
|
||||
var interaction = EntitySystem.Get<SharedInteractionSystem>();
|
||||
bool Ignored(IEntity entity) => (entity == eventArgs.Target || entity == eventArgs.User || entity == eventArgs.Dropped);
|
||||
|
||||
if (!interaction.InRangeUnobstructed(userPosition, climbablePosition, _range, predicate: Ignored) ||
|
||||
!interaction.InRangeUnobstructed(userPosition, otherUserPosition, _range, predicate: Ignored))
|
||||
{
|
||||
_notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't reach there!"));
|
||||
|
||||
return false;
|
||||
}
|
||||
reason = Loc.GetString("You are incapable of climbing!");
|
||||
return false;
|
||||
}
|
||||
|
||||
var bodyManager = user.GetComponent<BodyManagerComponent>();
|
||||
|
||||
if (bodyManager.GetBodyPartsOfType(Shared.GameObjects.Components.Body.BodyPartType.Leg).Count == 0 ||
|
||||
bodyManager.GetBodyPartsOfType(Shared.GameObjects.Components.Body.BodyPartType.Foot).Count == 0)
|
||||
{
|
||||
reason = Loc.GetString("You are unable to climb!");
|
||||
return false;
|
||||
}
|
||||
|
||||
var userPosition = user.Transform.MapPosition;
|
||||
var climbablePosition = target.Transform.MapPosition;
|
||||
var interaction = EntitySystem.Get<SharedInteractionSystem>();
|
||||
bool Ignored(IEntity entity) => (entity == target || entity == user);
|
||||
|
||||
if (!interaction.InRangeUnobstructed(userPosition, climbablePosition, _range, predicate: Ignored))
|
||||
{
|
||||
reason = Loc.GetString("You can't reach there!");
|
||||
return false;
|
||||
}
|
||||
|
||||
reason = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the user can vault the dragged entity onto the the target
|
||||
/// </summary>
|
||||
/// <param name="user">The user that wants to vault the entity</param>
|
||||
/// <param name="dragged">The entity that is being vaulted</param>
|
||||
/// <param name="target">The object that is being vaulted onto</param>
|
||||
/// <param name="reason">The reason why it cant be dropped</param>
|
||||
/// <returns></returns>
|
||||
private bool CanVault(IEntity user, IEntity dragged, IEntity target, out string reason)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user))
|
||||
{
|
||||
reason = Loc.GetString("You can't do that!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target == null || !dragged.HasComponent<ClimbingComponent>())
|
||||
{
|
||||
reason = Loc.GetString("You can't do that!");
|
||||
return false;
|
||||
}
|
||||
|
||||
var userPosition = user.Transform.MapPosition;
|
||||
var otherUserPosition = dragged.Transform.MapPosition;
|
||||
var climbablePosition = target.Transform.MapPosition;
|
||||
var interaction = EntitySystem.Get<SharedInteractionSystem>();
|
||||
bool Ignored(IEntity entity) => (entity == target || entity == user || entity == dragged);
|
||||
|
||||
if (!interaction.InRangeUnobstructed(userPosition, climbablePosition, _range, predicate: Ignored) ||
|
||||
!interaction.InRangeUnobstructed(userPosition, otherUserPosition, _range, predicate: Ignored))
|
||||
{
|
||||
reason = Loc.GetString("You can't reach there!");
|
||||
return false;
|
||||
}
|
||||
|
||||
reason = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -175,7 +208,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
// there's also the cases where the user might collide with the person they are forcing onto the climbable that i haven't accounted for
|
||||
|
||||
PopupMessageOtherClientsInRange(user, Loc.GetString("{0:theName} forces {1:theName} onto {2:theName}!", user, entityToMove, Owner), 15);
|
||||
_notifyManager.PopupMessage(user, user, Loc.GetString("You force {0:theName} onto {1:theName}!", entityToMove, Owner));
|
||||
user.PopupMessage(user, Loc.GetString("You force {0:theName} onto {1:theName}!", entityToMove, Owner));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +244,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
climbMode.TryMoveTo(user.Transform.WorldPosition, endPoint);
|
||||
|
||||
PopupMessageOtherClientsInRange(user, Loc.GetString("{0:theName} jumps onto {1:theName}!", user, Owner), 15);
|
||||
_notifyManager.PopupMessage(user, user, Loc.GetString("You jump onto {0:theName}!", Owner));
|
||||
user.PopupMessage(user, Loc.GetString("You jump onto {0:theName}!", Owner));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,5 +264,27 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
source.PopupMessage(viewer.AttachedEntity, message);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Allows you to vault an object with the ClimbableComponent through right click
|
||||
/// </summary>
|
||||
[Verb]
|
||||
private sealed class ClimbVerb : Verb<ClimbableComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, ClimbableComponent component, VerbData data)
|
||||
{
|
||||
if (!component.CanVault(user, component.Owner, out var _))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("Vault");
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, ClimbableComponent component)
|
||||
{
|
||||
component.TryClimb(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Robust.Server.GameObjects;
|
||||
@@ -18,9 +19,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
[RegisterComponent]
|
||||
public class ServerPortalComponent : SharedPortalComponent
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerEntityManager _serverEntityManager;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IServerEntityManager _serverEntityManager = default!;
|
||||
|
||||
// Potential improvements: Different sounds,
|
||||
// Add Gateways
|
||||
@@ -28,15 +27,14 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
// Put portal above most other things layer-wise
|
||||
// Add telefragging (get entities on connecting portal and force brute damage)
|
||||
|
||||
private AppearanceComponent _appearanceComponent;
|
||||
private IEntity _connectingTeleporter;
|
||||
private IEntity? _connectingTeleporter;
|
||||
private PortalState _state = PortalState.Pending;
|
||||
[ViewVariables(VVAccess.ReadWrite)] private float _individualPortalCooldown;
|
||||
[ViewVariables] private float _overallPortalCooldown;
|
||||
[ViewVariables] private bool _onCooldown;
|
||||
[ViewVariables] private string _departureSound;
|
||||
[ViewVariables] private string _arrivalSound;
|
||||
public List<IEntity> immuneEntities = new List<IEntity>(); // K
|
||||
[ViewVariables] private string _departureSound = "";
|
||||
[ViewVariables] private string _arrivalSound = "";
|
||||
public readonly List<IEntity> ImmuneEntities = new List<IEntity>(); // K
|
||||
[ViewVariables(VVAccess.ReadWrite)] private float _aliveTime;
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
@@ -52,12 +50,6 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
serializer.DataField(ref _arrivalSound, "arrival_sound", "/Audio/Effects/teleport_arrival.ogg");
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_appearanceComponent = Owner.GetComponent<AppearanceComponent>();
|
||||
}
|
||||
|
||||
public override void OnAdd()
|
||||
{
|
||||
// This will blow up an entity it's attached to
|
||||
@@ -74,13 +66,6 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
_appearanceComponent = null;
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
public bool CanBeConnected()
|
||||
{
|
||||
if (_connectingTeleporter == null)
|
||||
@@ -108,23 +93,24 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
}
|
||||
|
||||
_state = targetState;
|
||||
if (_appearanceComponent != null)
|
||||
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
_appearanceComponent.SetData(PortalVisuals.State, _state);
|
||||
appearance.SetData(PortalVisuals.State, _state);
|
||||
}
|
||||
}
|
||||
|
||||
private void releaseCooldown(IEntity entity)
|
||||
private void ReleaseCooldown(IEntity entity)
|
||||
{
|
||||
if (immuneEntities.Contains(entity))
|
||||
if (ImmuneEntities.Contains(entity))
|
||||
{
|
||||
immuneEntities.Remove(entity);
|
||||
ImmuneEntities.Remove(entity);
|
||||
}
|
||||
|
||||
if (_connectingTeleporter != null &&
|
||||
_connectingTeleporter.TryGetComponent<ServerPortalComponent>(out var otherPortal))
|
||||
{
|
||||
otherPortal.immuneEntities.Remove(entity);
|
||||
otherPortal.ImmuneEntities.Remove(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +128,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
private bool IsEntityPortable(IEntity entity)
|
||||
{
|
||||
// TODO: Check if it's slotted etc. Otherwise the slot item itself gets ported.
|
||||
if (!immuneEntities.Contains(entity) && entity.HasComponent<TeleportableComponent>())
|
||||
if (!ImmuneEntities.Contains(entity) && entity.HasComponent<TeleportableComponent>())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -192,7 +178,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
|
||||
public void TryPortalEntity(IEntity entity)
|
||||
{
|
||||
if (immuneEntities.Contains(entity) || _connectingTeleporter == null)
|
||||
if (ImmuneEntities.Contains(entity) || _connectingTeleporter == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -208,9 +194,9 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
soundPlayer.PlayAtCoords(_arrivalSound, entity.Transform.GridPosition);
|
||||
TryChangeState(PortalState.RecentlyTeleported);
|
||||
// To stop spam teleporting. Could potentially look at adding a timer to flush this from the portal
|
||||
immuneEntities.Add(entity);
|
||||
_connectingTeleporter.GetComponent<ServerPortalComponent>().immuneEntities.Add(entity);
|
||||
Timer.Spawn(TimeSpan.FromSeconds(_individualPortalCooldown), () => releaseCooldown(entity));
|
||||
ImmuneEntities.Add(entity);
|
||||
_connectingTeleporter.GetComponent<ServerPortalComponent>().ImmuneEntities.Add(entity);
|
||||
Timer.Spawn(TimeSpan.FromSeconds(_individualPortalCooldown), () => ReleaseCooldown(entity));
|
||||
StartCooldown();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
@@ -24,11 +25,10 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
[RegisterComponent]
|
||||
public class ServerTeleporterComponent : Component, IAfterInteract
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IMapManager _mapManager;
|
||||
[Dependency] private readonly IServerEntityManager _serverEntityManager;
|
||||
[Dependency] private readonly IRobustRandom _spreadRandom;
|
||||
#pragma warning restore 649
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IServerEntityManager _serverEntityManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _spreadRandom = default!;
|
||||
|
||||
// TODO: Look at MapManager.Map for Beacons to get all entities on grid
|
||||
public ItemTeleporterState State => _state;
|
||||
|
||||
@@ -39,15 +39,13 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
[ViewVariables] private int _range;
|
||||
[ViewVariables] private ItemTeleporterState _state;
|
||||
[ViewVariables] private TeleporterType _teleporterType;
|
||||
[ViewVariables] private string _departureSound;
|
||||
[ViewVariables] private string _arrivalSound;
|
||||
[ViewVariables] private string _cooldownSound;
|
||||
[ViewVariables] private string _departureSound = "";
|
||||
[ViewVariables] private string _arrivalSound = "";
|
||||
[ViewVariables] private string? _cooldownSound;
|
||||
// If the direct OR random teleport will try to avoid hitting collidables
|
||||
[ViewVariables] private bool _avoidCollidable;
|
||||
[ViewVariables] private float _portalAliveTime;
|
||||
|
||||
private AppearanceComponent _appearanceComponent;
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
@@ -63,22 +61,20 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
serializer.DataField(ref _portalAliveTime, "portal_alive_time", 5.0f); // TODO: Change this to 0 before PR?
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
_appearanceComponent = null;
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
private void SetState(ItemTeleporterState newState)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (newState == ItemTeleporterState.Cooldown)
|
||||
{
|
||||
_appearanceComponent.SetData(TeleporterVisuals.VisualState, TeleporterVisualState.Charging);
|
||||
appearance.SetData(TeleporterVisuals.VisualState, TeleporterVisualState.Charging);
|
||||
}
|
||||
else
|
||||
{
|
||||
_appearanceComponent.SetData(TeleporterVisuals.VisualState, TeleporterVisualState.Ready);
|
||||
appearance.SetData(TeleporterVisuals.VisualState, TeleporterVisualState.Ready);
|
||||
}
|
||||
_state = newState;
|
||||
}
|
||||
@@ -149,12 +145,11 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
_appearanceComponent = Owner.GetComponent<AppearanceComponent>();
|
||||
_state = ItemTeleporterState.Off;
|
||||
base.Initialize();
|
||||
_state = ItemTeleporterState.Off;
|
||||
}
|
||||
|
||||
private bool emptySpace(IEntity user, Vector2 target)
|
||||
private bool EmptySpace(IEntity user, Vector2 target)
|
||||
{
|
||||
// TODO: Check the user's spot? Upside is no stacking TPs but downside is they can't unstuck themselves from walls.
|
||||
foreach (var entity in _serverEntityManager.GetEntitiesIntersecting(user.Transform.MapID, target))
|
||||
@@ -167,7 +162,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
return true;
|
||||
}
|
||||
|
||||
private Vector2 randomEmptySpot(IEntity user, int range)
|
||||
private Vector2 RandomEmptySpot(IEntity user, int range)
|
||||
{
|
||||
Vector2 targetVector = user.Transform.GridPosition.Position;
|
||||
// Definitely a better way to do this
|
||||
@@ -176,7 +171,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
var randomRange = _spreadRandom.Next(0, range);
|
||||
var angle = Angle.FromDegrees(_spreadRandom.Next(0, 359));
|
||||
targetVector = user.Transform.GridPosition.Position + angle.ToVec() * randomRange;
|
||||
if (emptySpace(user, targetVector))
|
||||
if (EmptySpace(user, targetVector))
|
||||
{
|
||||
return targetVector;
|
||||
}
|
||||
@@ -200,7 +195,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
Vector2 targetVector;
|
||||
if (_avoidCollidable)
|
||||
{
|
||||
targetVector = randomEmptySpot(user, _range);
|
||||
targetVector = RandomEmptySpot(user, _range);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -227,7 +222,7 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
public void Teleport(IEntity user, Vector2 vector)
|
||||
{
|
||||
// Messy maybe?
|
||||
GridCoordinates targetGrid = new GridCoordinates(vector, user.Transform.GridID);
|
||||
var targetGrid = new GridCoordinates(vector, user.Transform.GridID);
|
||||
var soundPlayer = EntitySystem.Get<AudioSystem>();
|
||||
|
||||
// If portals use those, otherwise just move em over
|
||||
@@ -240,10 +235,11 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
|
||||
// Arrival portal
|
||||
var arrivalPortal = _serverEntityManager.SpawnEntity("Portal", targetGrid);
|
||||
arrivalPortal.TryGetComponent<ServerPortalComponent>(out var arrivalComponent);
|
||||
|
||||
// Connect. TODO: If the OnUpdate in ServerPortalComponent is changed this may need to change as well.
|
||||
arrivalComponent.TryConnectPortal(departurePortal);
|
||||
if (arrivalPortal.TryGetComponent<ServerPortalComponent>(out var arrivalComponent))
|
||||
{
|
||||
// Connect. TODO: If the OnUpdate in ServerPortalComponent is changed this may need to change as well.
|
||||
arrivalComponent.TryConnectPortal(departurePortal);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -22,10 +22,8 @@ namespace Content.Server.GameObjects.Components.Movement
|
||||
[ComponentReference(typeof(IMoverComponent))]
|
||||
internal class ShuttleControllerComponent : Component, IMoverComponent
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
#pragma warning restore 649
|
||||
|
||||
private bool _movingUp;
|
||||
private bool _movingDown;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user