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:
@@ -1,4 +1,5 @@
|
||||
using Content.Server.AI.Utility;
|
||||
#nullable enable
|
||||
using Content.Server.AI.Utility;
|
||||
using Content.Server.AI.WorldState.States.Inventory;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.Utility;
|
||||
@@ -34,7 +35,7 @@ namespace Content.Server.AI.Operators.Inventory
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
if (!container.Owner.TryGetComponent(out EntityStorageComponent storageComponent) ||
|
||||
if (!container.Owner.TryGetComponent(out EntityStorageComponent? storageComponent) ||
|
||||
storageComponent.IsWeldedShut)
|
||||
{
|
||||
return Outcome.Failed;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#nullable enable
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.GameObjects.EntitySystems.Click;
|
||||
@@ -20,11 +21,9 @@ namespace Content.Server.AI.Operators.Inventory
|
||||
_target = target;
|
||||
}
|
||||
|
||||
// TODO: When I spawn new entities they seem to duplicate clothing or something?
|
||||
public override Outcome Execute(float frameTime)
|
||||
{
|
||||
if (_target == null ||
|
||||
_target.Deleted ||
|
||||
if (_target.Deleted ||
|
||||
!_target.HasComponent<ItemComponent>() ||
|
||||
ContainerHelpers.IsInContainer(_target) ||
|
||||
!InteractionChecks.InRangeUnobstructed(_owner, _target.Transform.MapPosition))
|
||||
@@ -32,7 +31,7 @@ namespace Content.Server.AI.Operators.Inventory
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
if (!_owner.TryGetComponent(out HandsComponent handsComponent))
|
||||
if (!_owner.TryGetComponent(out HandsComponent? handsComponent))
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#nullable enable
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
@@ -7,12 +8,12 @@ namespace Content.Server.AI.Operators.Inventory
|
||||
/// <summary>
|
||||
/// Will find the item in storage, put it in an active hand, then use it
|
||||
/// </summary>
|
||||
public class UseItemInHandsOperator : AiOperator
|
||||
public class UseItemInInventoryOperator : AiOperator
|
||||
{
|
||||
private readonly IEntity _owner;
|
||||
private readonly IEntity _target;
|
||||
|
||||
public UseItemInHandsOperator(IEntity owner, IEntity target)
|
||||
public UseItemInInventoryOperator(IEntity owner, IEntity target)
|
||||
{
|
||||
_owner = owner;
|
||||
_target = target;
|
||||
@@ -20,18 +21,13 @@ namespace Content.Server.AI.Operators.Inventory
|
||||
|
||||
public override Outcome Execute(float frameTime)
|
||||
{
|
||||
if (_target == null)
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
// TODO: Also have this check storage a la backpack etc.
|
||||
if (!_owner.TryGetComponent(out HandsComponent handsComponent))
|
||||
if (!_owner.TryGetComponent(out HandsComponent? handsComponent))
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
if (!_target.TryGetComponent(out ItemComponent itemComponent))
|
||||
if (!_target.TryGetComponent(out ItemComponent? itemComponent))
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
@@ -16,12 +16,20 @@ namespace Content.Server.AI.Operators.Movement
|
||||
public float ArrivalDistance { get; }
|
||||
public float PathfindingProximity { get; }
|
||||
|
||||
public MoveToEntityOperator(IEntity owner, IEntity target, float arrivalDistance = 1.0f, float pathfindingProximity = 1.5f)
|
||||
private bool _requiresInRangeUnobstructed;
|
||||
|
||||
public MoveToEntityOperator(
|
||||
IEntity owner,
|
||||
IEntity target,
|
||||
float arrivalDistance = 1.0f,
|
||||
float pathfindingProximity = 1.5f,
|
||||
bool requiresInRangeUnobstructed = false)
|
||||
{
|
||||
_owner = owner;
|
||||
_target = target;
|
||||
ArrivalDistance = arrivalDistance;
|
||||
PathfindingProximity = pathfindingProximity;
|
||||
_requiresInRangeUnobstructed = requiresInRangeUnobstructed;
|
||||
}
|
||||
|
||||
public override bool TryStartup()
|
||||
@@ -32,7 +40,7 @@ namespace Content.Server.AI.Operators.Movement
|
||||
}
|
||||
|
||||
var steering = EntitySystem.Get<AiSteeringSystem>();
|
||||
_request = new EntityTargetSteeringRequest(_target, ArrivalDistance, PathfindingProximity);
|
||||
_request = new EntityTargetSteeringRequest(_target, ArrivalDistance, PathfindingProximity, _requiresInRangeUnobstructed);
|
||||
steering.Register(_owner, _request);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#nullable enable
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.GameObjects.Components.Nutrition;
|
||||
using Content.Shared.GameObjects.Components.Nutrition;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.AI.Operators.Nutrition
|
||||
{
|
||||
public class UseDrinkInInventoryOperator : AiOperator
|
||||
{
|
||||
private readonly IEntity _owner;
|
||||
private readonly IEntity _target;
|
||||
private float _interactionCooldown;
|
||||
|
||||
public UseDrinkInInventoryOperator(IEntity owner, IEntity target)
|
||||
{
|
||||
_owner = owner;
|
||||
_target = target;
|
||||
}
|
||||
|
||||
public override Outcome Execute(float frameTime)
|
||||
{
|
||||
if (_interactionCooldown >= 0)
|
||||
{
|
||||
_interactionCooldown -= frameTime;
|
||||
return Outcome.Continuing;
|
||||
}
|
||||
|
||||
// TODO: Also have this check storage a la backpack etc.
|
||||
if (_target.Deleted ||
|
||||
!_owner.TryGetComponent(out HandsComponent? handsComponent) ||
|
||||
!_target.TryGetComponent(out ItemComponent? itemComponent))
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
DrinkComponent? drinkComponent = null;
|
||||
|
||||
foreach (var slot in handsComponent.ActivePriorityEnumerable())
|
||||
{
|
||||
if (handsComponent.GetItem(slot) != itemComponent) continue;
|
||||
handsComponent.ActiveHand = slot;
|
||||
if (!_target.TryGetComponent(out drinkComponent))
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
// This should also implicitly open it.
|
||||
handsComponent.ActivateItem();
|
||||
_interactionCooldown = IoCManager.Resolve<IRobustRandom>().NextFloat() + 0.5f;
|
||||
}
|
||||
|
||||
if (drinkComponent == null)
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
if (drinkComponent.Deleted ||
|
||||
drinkComponent.Empty ||
|
||||
_owner.TryGetComponent(out ThirstComponent? thirstComponent) &&
|
||||
thirstComponent.CurrentThirst >= thirstComponent.ThirstThresholds[ThirstThreshold.Okay])
|
||||
{
|
||||
return Outcome.Success;
|
||||
}
|
||||
|
||||
return Outcome.Continuing;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#nullable enable
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.GameObjects.Components.Nutrition;
|
||||
using Content.Shared.GameObjects.Components.Nutrition;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.AI.Operators.Nutrition
|
||||
{
|
||||
public class UseFoodInInventoryOperator : AiOperator
|
||||
{
|
||||
private readonly IEntity _owner;
|
||||
private readonly IEntity _target;
|
||||
private float _interactionCooldown;
|
||||
|
||||
public UseFoodInInventoryOperator(IEntity owner, IEntity target)
|
||||
{
|
||||
_owner = owner;
|
||||
_target = target;
|
||||
}
|
||||
|
||||
public override Outcome Execute(float frameTime)
|
||||
{
|
||||
if (_interactionCooldown >= 0)
|
||||
{
|
||||
_interactionCooldown -= frameTime;
|
||||
return Outcome.Continuing;
|
||||
}
|
||||
|
||||
// TODO: Also have this check storage a la backpack etc.
|
||||
if (_target.Deleted ||
|
||||
!_owner.TryGetComponent(out HandsComponent? handsComponent) ||
|
||||
!_target.TryGetComponent(out ItemComponent? itemComponent))
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
FoodComponent? foodComponent = null;
|
||||
|
||||
foreach (var slot in handsComponent.ActivePriorityEnumerable())
|
||||
{
|
||||
if (handsComponent.GetItem(slot) != itemComponent) continue;
|
||||
handsComponent.ActiveHand = slot;
|
||||
if (!_target.TryGetComponent(out foodComponent))
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
// This should also implicitly open it.
|
||||
handsComponent.ActivateItem();
|
||||
_interactionCooldown = IoCManager.Resolve<IRobustRandom>().NextFloat() + 0.5f;
|
||||
}
|
||||
|
||||
if (foodComponent == null)
|
||||
{
|
||||
return Outcome.Failed;
|
||||
}
|
||||
|
||||
if (_target.Deleted ||
|
||||
foodComponent.UsesRemaining == 0 ||
|
||||
_owner.TryGetComponent(out HungerComponent? hungerComponent) &&
|
||||
hungerComponent.CurrentHunger >= hungerComponent.HungerThresholds[HungerThreshold.Okay])
|
||||
{
|
||||
return Outcome.Success;
|
||||
}
|
||||
|
||||
return Outcome.Continuing;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ namespace Content.Server.AI.Operators.Sequences
|
||||
{
|
||||
Sequence = new Queue<AiOperator>(new AiOperator[]
|
||||
{
|
||||
new MoveToEntityOperator(owner, target),
|
||||
new MoveToEntityOperator(owner, target, requiresInRangeUnobstructed: true),
|
||||
new OpenStorageOperator(owner, target),
|
||||
new PickupEntityOperator(owner, target),
|
||||
});
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Content.Server.AI.Utility.Actions.Clothing.Gloves
|
||||
ActionOperators = new Queue<AiOperator>(new AiOperator[]
|
||||
{
|
||||
new EquipEntityOperator(Owner, _entity),
|
||||
new UseItemInHandsOperator(Owner, _entity),
|
||||
new UseItemInInventoryOperator(Owner, _entity),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Content.Server.AI.Utility.Actions.Clothing.Head
|
||||
ActionOperators = new Queue<AiOperator>(new AiOperator[]
|
||||
{
|
||||
new EquipEntityOperator(Owner, _entity),
|
||||
new UseItemInHandsOperator(Owner, _entity),
|
||||
new UseItemInInventoryOperator(Owner, _entity),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Content.Server.AI.Utility.Actions.Clothing.OuterClothing
|
||||
ActionOperators = new Queue<AiOperator>(new AiOperator[]
|
||||
{
|
||||
new EquipEntityOperator(Owner, _entity),
|
||||
new UseItemInHandsOperator(Owner, _entity),
|
||||
new UseItemInInventoryOperator(Owner, _entity),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Content.Server.AI.Utility.Actions.Clothing.Shoes
|
||||
ActionOperators = new Queue<AiOperator>(new AiOperator[]
|
||||
{
|
||||
new EquipEntityOperator(Owner, _entity),
|
||||
new UseItemInHandsOperator(Owner, _entity),
|
||||
new UseItemInInventoryOperator(Owner, _entity),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -40,10 +40,6 @@ namespace Content.Server.AI.Utility.Actions.Nutrition.Drink
|
||||
|
||||
return new[]
|
||||
{
|
||||
considerationsManager.Get<FreeHandCon>()
|
||||
.BoolCurve(context),
|
||||
considerationsManager.Get<ThirstCon>()
|
||||
.PresetCurve(context, PresetCurve.Nutrition),
|
||||
considerationsManager.Get<TargetDistanceCon>()
|
||||
.PresetCurve(context, PresetCurve.Distance),
|
||||
considerationsManager.Get<DrinkValueCon>()
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Operators;
|
||||
using Content.Server.AI.Operators.Inventory;
|
||||
using Content.Server.AI.Operators.Nutrition;
|
||||
using Content.Server.AI.Utility.Considerations;
|
||||
using Content.Server.AI.Utility.Considerations.Inventory;
|
||||
using Content.Server.AI.Utility.Considerations.Nutrition.Drink;
|
||||
@@ -27,7 +28,7 @@ namespace Content.Server.AI.Utility.Actions.Nutrition.Drink
|
||||
ActionOperators = new Queue<AiOperator>(new AiOperator[]
|
||||
{
|
||||
new EquipEntityOperator(Owner, _entity),
|
||||
new UseItemInHandsOperator(Owner, _entity),
|
||||
new UseDrinkInInventoryOperator(Owner, _entity),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Operators;
|
||||
using Content.Server.AI.Operators.Inventory;
|
||||
using Content.Server.AI.Operators.Nutrition;
|
||||
using Content.Server.AI.Utility.Considerations;
|
||||
using Content.Server.AI.Utility.Considerations.Inventory;
|
||||
using Content.Server.AI.Utility.Considerations.Nutrition.Food;
|
||||
@@ -27,7 +28,7 @@ namespace Content.Server.AI.Utility.Actions.Nutrition.Food
|
||||
ActionOperators = new Queue<AiOperator>(new AiOperator[]
|
||||
{
|
||||
new EquipEntityOperator(Owner, _entity),
|
||||
new UseItemInHandsOperator(Owner, _entity),
|
||||
new UseFoodInInventoryOperator(Owner, _entity),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Content.Server.AI.Utility.BehaviorSets
|
||||
// TODO: Ideally long-term we should just store the weapons in backpack
|
||||
new EquipMeleeExp(),
|
||||
new PickUpMeleeWeaponExp(),
|
||||
new MeleeAttackNearbyPlayerExp(),
|
||||
new MeleeAttackNearbyExp(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace Content.Server.AI.Utility.Considerations.Combat
|
||||
{
|
||||
var target = context.GetState<TargetEntityState>().GetValue();
|
||||
|
||||
if (target == null || !target.TryGetComponent(out IDamageableComponent damageableComponent))
|
||||
if (target == null || target.Deleted || !target.TryGetComponent(out IDamageableComponent damageableComponent))
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Content.Server.AI.Utility.Considerations.Movement
|
||||
{
|
||||
var self = context.GetState<SelfState>().GetValue();
|
||||
var target = context.GetState<TargetEntityState>().GetValue();
|
||||
if (target == null || target.Transform.GridID != self.Transform.GridID)
|
||||
if (target == null || target.Deleted || target.Transform.GridID != self.Transform.GridID)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
@@ -4,17 +4,19 @@ using Content.Server.AI.Utility.Actions;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Melee;
|
||||
using Content.Server.AI.Utility.Considerations;
|
||||
using Content.Server.AI.Utility.Considerations.Combat.Melee;
|
||||
using Content.Server.AI.Utils;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.GameObjects.Components.AI;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Robust.Server.GameObjects;
|
||||
using Content.Server.GameObjects.EntitySystems.AI;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.AI.Utility.ExpandableActions.Combat.Melee
|
||||
{
|
||||
public sealed class MeleeAttackNearbyPlayerExp : ExpandableUtilityAction
|
||||
public sealed class MeleeAttackNearbyExp : ExpandableUtilityAction
|
||||
{
|
||||
public override float Bonus => UtilityAction.CombatBonus;
|
||||
|
||||
@@ -37,13 +39,10 @@ namespace Content.Server.AI.Utility.ExpandableActions.Combat.Melee
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
foreach (var entity in Visibility.GetEntitiesInRange(owner.Transform.GridPosition, typeof(IDamageableComponent),
|
||||
controller.VisionRadius))
|
||||
foreach (var target in EntitySystem.Get<AiFactionTagSystem>()
|
||||
.GetNearbyHostiles(owner, controller.VisionRadius))
|
||||
{
|
||||
if (entity.HasComponent<BasicActorComponent>() && entity != owner)
|
||||
{
|
||||
yield return new MeleeWeaponAttackEntity(owner, entity, Bonus);
|
||||
}
|
||||
yield return new MeleeWeaponAttackEntity(owner, target, Bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.AI.Utility.Actions;
|
||||
using Content.Server.AI.Utility.Actions.Combat.Melee;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.AI.WorldState.States.Mobs;
|
||||
|
||||
namespace Content.Server.AI.Utility.ExpandableActions.Combat.Melee
|
||||
{
|
||||
public sealed class MeleeAttackNearbySpeciesExp : ExpandableUtilityAction
|
||||
{
|
||||
public override float Bonus => UtilityAction.CombatBonus;
|
||||
|
||||
public override IEnumerable<UtilityAction> GetActions(Blackboard context)
|
||||
{
|
||||
var owner = context.GetState<SelfState>().GetValue();
|
||||
|
||||
foreach (var entity in context.GetState<NearbyBodiesState>().GetValue())
|
||||
{
|
||||
yield return new MeleeWeaponAttackEntity(owner, entity, Bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,10 @@ using Content.Server.AI.Utils;
|
||||
using Content.Server.AI.WorldState;
|
||||
using Content.Server.AI.WorldState.States;
|
||||
using Content.Server.GameObjects.Components.Movement;
|
||||
using Content.Server.GameObjects.EntitySystems.AI;
|
||||
using Content.Shared.GameObjects.Components.Body;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.AI.Utility.ExpandableActions.Combat.Melee
|
||||
@@ -37,13 +39,10 @@ namespace Content.Server.AI.Utility.ExpandableActions.Combat.Melee
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
foreach (var entity in Visibility.GetEntitiesInRange(owner.Transform.GridPosition, typeof(IBodyManagerComponent),
|
||||
controller.VisionRadius))
|
||||
foreach (var target in EntitySystem.Get<AiFactionTagSystem>()
|
||||
.GetNearbyHostiles(owner, controller.VisionRadius))
|
||||
{
|
||||
if (entity.HasComponent<BasicActorComponent>() && entity != owner)
|
||||
{
|
||||
yield return new UnarmedAttackEntity(owner, entity, Bonus);
|
||||
}
|
||||
yield return new UnarmedAttackEntity(owner, target, Bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,12 @@ namespace Content.Server.Administration
|
||||
}
|
||||
|
||||
var mind = player.ContentData().Mind;
|
||||
if (mind == null)
|
||||
{
|
||||
shell.SendText(player, "You can't ghost here!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mind.VisitingEntity != null && mind.VisitingEntity.Prototype.ID == "AdminObserver")
|
||||
{
|
||||
var visiting = mind.VisitingEntity;
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Server.GameObjects.Components.Atmos;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Server.Interfaces.Console;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
@@ -58,7 +60,9 @@ namespace Content.Server.Atmos
|
||||
public string Help => "listgases";
|
||||
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
|
||||
{
|
||||
foreach (var gasPrototype in Atmospherics.Gases)
|
||||
var atmosSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
|
||||
foreach (var gasPrototype in atmosSystem.Gases)
|
||||
{
|
||||
shell.SendText(player, $"{gasPrototype.Name} ID: {gasPrototype.ID}");
|
||||
}
|
||||
|
||||
16
Content.Server/Atmos/FireEvent.cs
Normal file
16
Content.Server/Atmos/FireEvent.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Atmos
|
||||
{
|
||||
public class FireActEvent : EntitySystemMessage
|
||||
{
|
||||
public float Temperature { get; }
|
||||
public float Volume { get; }
|
||||
|
||||
public FireActEvent(float temperature, float volume)
|
||||
{
|
||||
Temperature = temperature;
|
||||
Volume = volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,10 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Content.Server.Atmos.Reactions;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -20,12 +22,17 @@ namespace Content.Server.Atmos
|
||||
[Serializable]
|
||||
public class GasMixture : IExposeData, IEquatable<GasMixture>, ICloneable
|
||||
{
|
||||
private readonly AtmosphereSystem _atmosphereSystem;
|
||||
|
||||
[ViewVariables]
|
||||
private float[] _moles = new float[Atmospherics.TotalNumberOfGases];
|
||||
|
||||
[ViewVariables]
|
||||
private float[] _molesArchived = new float[Atmospherics.TotalNumberOfGases];
|
||||
|
||||
[ViewVariables]
|
||||
private float _temperature = Atmospherics.TCMB;
|
||||
|
||||
public IReadOnlyList<float> Gases => _moles;
|
||||
|
||||
[ViewVariables]
|
||||
@@ -51,7 +58,7 @@ namespace Content.Server.Atmos
|
||||
|
||||
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||
{
|
||||
capacity += Atmospherics.GetGas(i).SpecificHeat * _moles[i];
|
||||
capacity += _atmosphereSystem.GetGas(i).SpecificHeat * _moles[i];
|
||||
}
|
||||
|
||||
return MathF.Max(capacity, Atmospherics.MinimumHeatCapacity);
|
||||
@@ -68,7 +75,7 @@ namespace Content.Server.Atmos
|
||||
|
||||
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||
{
|
||||
capacity += Atmospherics.GetGas(i).SpecificHeat * _molesArchived[i];
|
||||
capacity += _atmosphereSystem.GetGas(i).SpecificHeat * _molesArchived[i];
|
||||
}
|
||||
|
||||
return MathF.Max(capacity, Atmospherics.MinimumHeatCapacity);
|
||||
@@ -122,15 +129,21 @@ namespace Content.Server.Atmos
|
||||
[ViewVariables]
|
||||
public float Volume { get; set; }
|
||||
|
||||
public GasMixture()
|
||||
public GasMixture() : this(null)
|
||||
{
|
||||
}
|
||||
|
||||
public GasMixture(float volume)
|
||||
public GasMixture(AtmosphereSystem? atmosphereSystem)
|
||||
{
|
||||
_atmosphereSystem = atmosphereSystem ?? EntitySystem.Get<AtmosphereSystem>();
|
||||
}
|
||||
|
||||
public GasMixture(float volume, AtmosphereSystem? atmosphereSystem = null)
|
||||
{
|
||||
if (volume < 0)
|
||||
volume = 0;
|
||||
Volume = volume;
|
||||
_atmosphereSystem = atmosphereSystem ?? EntitySystem.Get<AtmosphereSystem>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -215,12 +228,12 @@ namespace Content.Server.Atmos
|
||||
public GasMixture RemoveRatio(float ratio)
|
||||
{
|
||||
if(ratio <= 0)
|
||||
return new GasMixture(Volume);
|
||||
return new GasMixture(Volume, _atmosphereSystem);
|
||||
|
||||
if (ratio > 1)
|
||||
ratio = 1;
|
||||
|
||||
var removed = new GasMixture {Volume = Volume, Temperature = Temperature};
|
||||
var removed = new GasMixture(_atmosphereSystem) {Volume = Volume, Temperature = Temperature};
|
||||
|
||||
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||
{
|
||||
@@ -243,7 +256,7 @@ namespace Content.Server.Atmos
|
||||
public void CopyFromMutable(GasMixture sample)
|
||||
{
|
||||
if (Immutable) return;
|
||||
sample._moles.AsSpan().CopyTo(_moles.AsSpan());
|
||||
sample._moles.CopyTo(_moles, 0);
|
||||
Temperature = sample.Temperature;
|
||||
}
|
||||
|
||||
@@ -274,7 +287,7 @@ namespace Content.Server.Atmos
|
||||
if (!(MathF.Abs(delta) >= Atmospherics.GasMinMoles)) continue;
|
||||
if (absTemperatureDelta > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||
{
|
||||
var gasHeatCapacity = delta * Atmospherics.GetGas(i).SpecificHeat;
|
||||
var gasHeatCapacity = delta * _atmosphereSystem.GetGas(i).SpecificHeat;
|
||||
if (delta > 0)
|
||||
{
|
||||
heatCapacityToSharer += gasHeatCapacity;
|
||||
@@ -476,8 +489,7 @@ namespace Content.Server.Atmos
|
||||
var temperature = Temperature;
|
||||
var energy = ThermalEnergy;
|
||||
|
||||
// TODO ATMOS Take reaction priority into account!
|
||||
foreach (var prototype in IoCManager.Resolve<IPrototypeManager>().EnumeratePrototypes<GasReactionPrototype>())
|
||||
foreach (var prototype in _atmosphereSystem.GasReactions)
|
||||
{
|
||||
if (energy < prototype.MinimumEnergyRequirement ||
|
||||
temperature < prototype.MinimumTemperatureRequirement)
|
||||
@@ -499,7 +511,7 @@ namespace Content.Server.Atmos
|
||||
if (!doReaction)
|
||||
continue;
|
||||
|
||||
reaction = prototype.React(this, holder);
|
||||
reaction = prototype.React(this, holder, _atmosphereSystem.EventBus);
|
||||
if(reaction.HasFlag(ReactionResult.StopReactions))
|
||||
break;
|
||||
}
|
||||
@@ -579,7 +591,7 @@ namespace Content.Server.Atmos
|
||||
|
||||
public object Clone()
|
||||
{
|
||||
var newMixture = new GasMixture()
|
||||
var newMixture = new GasMixture(_atmosphereSystem)
|
||||
{
|
||||
_moles = (float[])_moles.Clone(),
|
||||
_molesArchived = (float[])_molesArchived.Clone(),
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Content.Shared.GameObjects.Components.Pointing;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
@@ -20,10 +19,8 @@ namespace Content.Server.Atmos
|
||||
[RegisterComponent]
|
||||
public class GasSprayerComponent : Component, IAfterInteract
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
|
||||
[Dependency] private readonly IServerEntityManager _serverEntityManager = default!;
|
||||
#pragma warning restore 649
|
||||
|
||||
//TODO: create a function that can create a gas based on a solution mix
|
||||
public override string Name => "GasSprayer";
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Server.GameObjects.Components.Atmos;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.Physics;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
@@ -24,7 +25,7 @@ namespace Content.Server.Atmos
|
||||
private const float ProbabilityBasePercent = 10f;
|
||||
private const float ThrowForce = 100f;
|
||||
|
||||
public void ExperiencePressureDifference(int cycle, float pressureDifference, Direction direction,
|
||||
public void ExperiencePressureDifference(int cycle, float pressureDifference, AtmosDirection direction,
|
||||
float pressureResistanceProbDelta, GridCoordinates throwTarget)
|
||||
{
|
||||
if (ControlledComponent == null)
|
||||
@@ -54,14 +55,14 @@ namespace Content.Server.Atmos
|
||||
if (throwTarget != GridCoordinates.InvalidGrid)
|
||||
{
|
||||
var moveForce = maxForce * MathHelper.Clamp(moveProb, 0, 100) / 150f;
|
||||
var pos = ((throwTarget.Position - transform.GridPosition.Position).Normalized + direction.ToVec()).Normalized;
|
||||
var pos = ((throwTarget.Position - transform.GridPosition.Position).Normalized + direction.ToDirection().ToVec()).Normalized;
|
||||
LinearVelocity = pos * moveForce;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
var moveForce = MathF.Min(maxForce * MathHelper.Clamp(moveProb, 0, 100) / 2500f, 20f);
|
||||
LinearVelocity = direction.ToVec() * moveForce;
|
||||
LinearVelocity = direction.ToDirection().ToVec() * moveForce;
|
||||
}
|
||||
|
||||
pressureComponent.LastHighPressureMovementAirCycle = cycle;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Atmos.Piping;
|
||||
using Content.Server.GameObjects.Components.NodeContainer.NodeGroups;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
@@ -12,11 +14,6 @@ namespace Content.Server.Atmos
|
||||
/// </summary>
|
||||
int UpdateCounter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// How many tiles have high pressure delta.
|
||||
/// </summary>
|
||||
int HighPressureDeltaCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Control variable for equalization.
|
||||
/// </summary>
|
||||
@@ -120,14 +117,14 @@ namespace Content.Server.Atmos
|
||||
/// </summary>
|
||||
/// <param name="indices"></param>
|
||||
/// <returns></returns>
|
||||
TileAtmosphere GetTile(MapIndices indices);
|
||||
TileAtmosphere GetTile(MapIndices indices, bool createSpace = true);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a tile.
|
||||
/// </summary>
|
||||
/// <param name="coordinates"></param>
|
||||
/// <returns></returns>
|
||||
TileAtmosphere GetTile(GridCoordinates coordinates);
|
||||
TileAtmosphere GetTile(GridCoordinates coordinates, bool createSpace = true);
|
||||
|
||||
/// <summary>
|
||||
/// Returns if the tile in question is air-blocked.
|
||||
@@ -158,5 +155,13 @@ namespace Content.Server.Atmos
|
||||
Dictionary<Direction, TileAtmosphere> GetAdjacentTiles(MapIndices indices, bool includeAirBlocked = false);
|
||||
|
||||
void Update(float frameTime);
|
||||
|
||||
void AddPipeNet(IPipeNet pipeNet);
|
||||
|
||||
void RemovePipeNet(IPipeNet pipeNet);
|
||||
|
||||
void AddPipeNetDevice(PipeNetDeviceComponent pipeNetDevice);
|
||||
|
||||
void RemovePipeNetDevice(PipeNetDeviceComponent pipeNetDevice);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
@@ -64,13 +65,13 @@ namespace Content.Server.Atmos.Reactions
|
||||
serializer.DataField(ref _effects, "effects", new List<IGasReactionEffect>());
|
||||
}
|
||||
|
||||
public ReactionResult React(GasMixture mixture, IGasMixtureHolder holder)
|
||||
public ReactionResult React(GasMixture mixture, IGasMixtureHolder holder, IEventBus eventBus)
|
||||
{
|
||||
var result = ReactionResult.NoReaction;
|
||||
|
||||
foreach (var effect in _effects)
|
||||
{
|
||||
result |= effect.React(mixture, holder);
|
||||
result |= effect.React(mixture, holder, eventBus);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Interaction;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Maps;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Atmos.Reactions
|
||||
@@ -10,7 +14,7 @@ namespace Content.Server.Atmos.Reactions
|
||||
[UsedImplicitly]
|
||||
public class PhoronFireReaction : IGasReactionEffect
|
||||
{
|
||||
public ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder)
|
||||
public ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder, IEventBus eventBus)
|
||||
{
|
||||
var energyReleased = 0f;
|
||||
var oldHeatCapacity = mixture.HeatCapacity;
|
||||
@@ -71,9 +75,7 @@ namespace Content.Server.Atmos.Reactions
|
||||
{
|
||||
location.HotspotExpose(temperature, mixture.Volume);
|
||||
|
||||
// TODO ATMOS Expose temperature all items on cell
|
||||
|
||||
location.TemperatureExpose(mixture, temperature, mixture.Volume);
|
||||
eventBus.QueueEvent(EventSource.Local, new TemperatureExposeEvent(location.GridIndices, location.GridIndex, mixture, temperature, mixture.Volume));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#nullable enable
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Interaction;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Maps;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Atmos.Reactions
|
||||
@@ -13,7 +16,7 @@ namespace Content.Server.Atmos.Reactions
|
||||
{
|
||||
}
|
||||
|
||||
public ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder)
|
||||
public ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder, IEventBus eventBus)
|
||||
{
|
||||
var energyReleased = 0f;
|
||||
var oldHeatCapacity = mixture.HeatCapacity;
|
||||
@@ -66,9 +69,7 @@ namespace Content.Server.Atmos.Reactions
|
||||
{
|
||||
location.HotspotExpose(temperature, mixture.Volume);
|
||||
|
||||
// TODO ATMOS Expose temperature all items on cell
|
||||
|
||||
location.TemperatureExpose(mixture, temperature, mixture.Volume);
|
||||
eventBus.QueueEvent(EventSource.Local, new TemperatureExposeEvent(location.GridIndices, location.GridIndex, mixture, temperature, mixture.Volume));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
23
Content.Server/Atmos/TemperatureExposeEvent.cs
Normal file
23
Content.Server/Atmos/TemperatureExposeEvent.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Server.Atmos
|
||||
{
|
||||
public class TemperatureExposeEvent : EntitySystemMessage
|
||||
{
|
||||
public MapIndices Indices { get; }
|
||||
public GridId Grid { get; }
|
||||
public GasMixture Air { get; }
|
||||
public float Temperature { get; }
|
||||
public float Volume { get; }
|
||||
|
||||
public TemperatureExposeEvent(MapIndices indices, GridId gridId, GasMixture air, float temperature, float volume)
|
||||
{
|
||||
Indices = indices;
|
||||
Grid = gridId;
|
||||
Air = air;
|
||||
Temperature = temperature;
|
||||
Volume = volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -30,15 +31,15 @@ namespace Content.Server.Atmos
|
||||
[ViewVariables]
|
||||
public float TransferDirectionSouth;
|
||||
|
||||
public float this[Direction direction]
|
||||
public float this[AtmosDirection direction]
|
||||
{
|
||||
get =>
|
||||
direction switch
|
||||
{
|
||||
Direction.East => TransferDirectionEast,
|
||||
Direction.West => TransferDirectionWest,
|
||||
Direction.North => TransferDirectionNorth,
|
||||
Direction.South => TransferDirectionSouth,
|
||||
AtmosDirection.East => TransferDirectionEast,
|
||||
AtmosDirection.West => TransferDirectionWest,
|
||||
AtmosDirection.North => TransferDirectionNorth,
|
||||
AtmosDirection.South => TransferDirectionSouth,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(direction))
|
||||
};
|
||||
|
||||
@@ -46,16 +47,16 @@ namespace Content.Server.Atmos
|
||||
{
|
||||
switch (direction)
|
||||
{
|
||||
case Direction.East:
|
||||
case AtmosDirection.East:
|
||||
TransferDirectionEast = value;
|
||||
break;
|
||||
case Direction.West:
|
||||
case AtmosDirection.West:
|
||||
TransferDirectionWest = value;
|
||||
break;
|
||||
case Direction.North:
|
||||
case AtmosDirection.North:
|
||||
TransferDirectionNorth = value;
|
||||
break;
|
||||
case Direction.South:
|
||||
case AtmosDirection.South:
|
||||
TransferDirectionSouth = value;
|
||||
break;
|
||||
default:
|
||||
@@ -64,10 +65,16 @@ namespace Content.Server.Atmos
|
||||
}
|
||||
}
|
||||
|
||||
public float this[int index]
|
||||
{
|
||||
get => this[(AtmosDirection) (1 << index)];
|
||||
set => this[(AtmosDirection) (1 << index)] = value;
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public float CurrentTransferAmount;
|
||||
|
||||
public Direction CurrentTransferDirection;
|
||||
public AtmosDirection CurrentTransferDirection;
|
||||
|
||||
[ViewVariables]
|
||||
public bool FastDone;
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Content.Server.Atmos.Reactions;
|
||||
using Content.Server.GameObjects.Components.Atmos;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.GameObjects.EntitySystems.Atmos;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Shared.Atmos;
|
||||
@@ -12,13 +11,13 @@ using Content.Shared.Audio;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Random;
|
||||
@@ -28,18 +27,15 @@ namespace Content.Server.Atmos
|
||||
{
|
||||
public class TileAtmosphere : IGasMixtureHolder
|
||||
{
|
||||
[Robust.Shared.IoC.Dependency] private IRobustRandom _robustRandom = default!;
|
||||
[Robust.Shared.IoC.Dependency] private IEntityManager _entityManager = default!;
|
||||
[Robust.Shared.IoC.Dependency] private IMapManager _mapManager = default!;
|
||||
[Robust.Shared.IoC.Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
[Robust.Shared.IoC.Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Robust.Shared.IoC.Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
|
||||
private static readonly TileAtmosphereComparer _comparer = new TileAtmosphereComparer();
|
||||
private static readonly TileAtmosphereComparer Comparer = new TileAtmosphereComparer();
|
||||
|
||||
[ViewVariables]
|
||||
private int _archivedCycle = 0;
|
||||
|
||||
[ViewVariables]
|
||||
private int _currentCycle = 0;
|
||||
[ViewVariables] private int _archivedCycle;
|
||||
[ViewVariables] private int _currentCycle;
|
||||
|
||||
[ViewVariables]
|
||||
private static GasTileOverlaySystem _gasTileOverlaySystem;
|
||||
@@ -51,13 +47,13 @@ namespace Content.Server.Atmos
|
||||
private float _temperatureArchived = Atmospherics.T20C;
|
||||
|
||||
// I know this being static is evil, but I seriously can't come up with a better solution to sound spam.
|
||||
private static int _soundCooldown = 0;
|
||||
private static int _soundCooldown;
|
||||
|
||||
[ViewVariables]
|
||||
public TileAtmosphere PressureSpecificTarget { get; set; } = null;
|
||||
public TileAtmosphere PressureSpecificTarget { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
public float PressureDifference { get; set; } = 0;
|
||||
public float PressureDifference { get; set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float HeatCapacity { get; set; } = 1f;
|
||||
@@ -66,13 +62,19 @@ namespace Content.Server.Atmos
|
||||
public float ThermalConductivity => Tile?.Tile.GetContentTileDefinition().ThermalConductivity ?? 0.05f;
|
||||
|
||||
[ViewVariables]
|
||||
public bool Excited { get; set; } = false;
|
||||
public bool Excited { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
private GridAtmosphereComponent _gridAtmosphereComponent;
|
||||
private readonly GridAtmosphereComponent _gridAtmosphereComponent;
|
||||
|
||||
/// <summary>
|
||||
/// Adjacent tiles in the same order as <see cref="AtmosDirection"/>. (NSEW)
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private readonly TileAtmosphere[] _adjacentTiles = new TileAtmosphere[Atmospherics.Directions];
|
||||
|
||||
[ViewVariables]
|
||||
private readonly Dictionary<Direction, TileAtmosphere> _adjacentTiles = new Dictionary<Direction, TileAtmosphere>();
|
||||
private AtmosDirection _adjacentBits = AtmosDirection.Invalid;
|
||||
|
||||
[ViewVariables]
|
||||
private TileAtmosInfo _tileAtmosInfo;
|
||||
@@ -80,7 +82,7 @@ namespace Content.Server.Atmos
|
||||
[ViewVariables]
|
||||
public Hotspot Hotspot;
|
||||
|
||||
private Direction _pressureDirection;
|
||||
private AtmosDirection _pressureDirection;
|
||||
|
||||
[ViewVariables]
|
||||
public GridId GridIndex { get; }
|
||||
@@ -100,14 +102,16 @@ namespace Content.Server.Atmos
|
||||
[ViewVariables]
|
||||
public bool BlocksAir => _gridAtmosphereComponent.IsAirBlocked(GridIndices);
|
||||
|
||||
public TileAtmosphere(GridAtmosphereComponent atmosphereComponent, GridId gridIndex, MapIndices gridIndices, GasMixture mixture = null)
|
||||
public TileAtmosphere(GridAtmosphereComponent atmosphereComponent, GridId gridIndex, MapIndices gridIndices, GasMixture mixture = null, bool immutable = false)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
_gridAtmosphereComponent = atmosphereComponent;
|
||||
GridIndex = gridIndex;
|
||||
GridIndices = gridIndices;
|
||||
Air = mixture;
|
||||
ResetTileAtmosInfo();
|
||||
|
||||
if(immutable)
|
||||
Air?.MarkImmutable();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -163,7 +167,7 @@ namespace Content.Server.Atmos
|
||||
}
|
||||
}
|
||||
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void HighPressureMovements()
|
||||
{
|
||||
// TODO ATMOS finish this
|
||||
@@ -175,7 +179,6 @@ namespace Content.Server.Atmos
|
||||
GridIndices.ToGridCoordinates(_mapManager, GridIndex), AudioHelpers.WithVariation(0.125f).WithVolume(MathHelper.Clamp(PressureDifference / 10, 10, 100)));
|
||||
}
|
||||
|
||||
|
||||
foreach (var entity in _entityManager.GetEntitiesIntersecting(_mapManager.GetGrid(GridIndex).ParentMapId, Box2.UnitCentered.Translated(GridIndices)))
|
||||
{
|
||||
if (!entity.TryGetComponent(out ICollidableComponent physics)
|
||||
@@ -195,7 +198,7 @@ namespace Content.Server.Atmos
|
||||
|
||||
if (PressureDifference > 100)
|
||||
{
|
||||
// Do space wind graphics here!
|
||||
// TODO ATMOS Do space wind graphics here!
|
||||
}
|
||||
|
||||
_soundCooldown++;
|
||||
@@ -220,20 +223,22 @@ namespace Content.Server.Atmos
|
||||
}
|
||||
}
|
||||
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void EqualizePressureInZone(int cycleNum)
|
||||
{
|
||||
if (Air == null || (_tileAtmosInfo.LastCycle >= cycleNum)) return; // Already done.
|
||||
|
||||
ResetTileAtmosInfo();
|
||||
|
||||
_tileAtmosInfo = new TileAtmosInfo();
|
||||
|
||||
var startingMoles = Air.TotalMoles;
|
||||
var runAtmos = false;
|
||||
|
||||
// We need to figure if this is necessary
|
||||
foreach (var (direction, other) in _adjacentTiles)
|
||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << i);
|
||||
if (!_adjacentBits.HasFlag(direction)) continue;
|
||||
var other = _adjacentTiles[i];
|
||||
if (other?.Air == null) continue;
|
||||
var comparisonMoles = other.Air.TotalMoles;
|
||||
if (!(MathF.Abs(comparisonMoles - startingMoles) > Atmospherics.MinimumMolesDeltaToMove)) continue;
|
||||
@@ -265,13 +270,15 @@ namespace Content.Server.Atmos
|
||||
totalMoles += tileMoles;
|
||||
}
|
||||
|
||||
foreach (var (_, adj) in exploring._adjacentTiles)
|
||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << j);
|
||||
if (!exploring._adjacentBits.HasFlag(direction)) continue;
|
||||
var adj = exploring._adjacentTiles[j];
|
||||
if (adj?.Air == null) continue;
|
||||
if(adj._tileAtmosInfo.LastQueueCycle == queueCycle) continue;
|
||||
adj.ResetTileAtmosInfo();
|
||||
adj._tileAtmosInfo = new TileAtmosInfo {LastQueueCycle = queueCycle};
|
||||
|
||||
adj._tileAtmosInfo.LastQueueCycle = queueCycle;
|
||||
if(tileCount < Atmospherics.ZumosHardTileLimit)
|
||||
tiles[tileCount++] = adj;
|
||||
if (adj.Air.Immutable)
|
||||
@@ -326,47 +333,42 @@ namespace Content.Server.Atmos
|
||||
if (giverTilesLength > logN && takerTilesLength > logN)
|
||||
{
|
||||
// Even if it fails, it will speed up the next part.
|
||||
Array.Sort(tiles, 0, tileCount, _comparer);
|
||||
Array.Sort(tiles, 0, tileCount, Comparer);
|
||||
|
||||
for (var i = 0; i < tileCount; i++)
|
||||
{
|
||||
var tile = tiles[i];
|
||||
tile._tileAtmosInfo.FastDone = true;
|
||||
if (!(tile._tileAtmosInfo.MoleDelta > 0)) continue;
|
||||
var eligibleDirections = ArrayPool<Direction>.Shared.Rent(4);
|
||||
var eligibleDirections = AtmosDirection.Invalid;
|
||||
var eligibleDirectionCount = 0;
|
||||
foreach (var direction in Cardinal)
|
||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||
{
|
||||
if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
|
||||
var direction = (AtmosDirection) (1 << j);
|
||||
if (!tile._adjacentBits.HasFlag(direction)) continue;
|
||||
var tile2 = tile._adjacentTiles[j];
|
||||
|
||||
// skip anything that isn't part of our current processing block.
|
||||
if (tile2._tileAtmosInfo.FastDone || tile2._tileAtmosInfo.LastQueueCycle != queueCycle)
|
||||
continue;
|
||||
|
||||
eligibleDirections[eligibleDirectionCount++] = direction;
|
||||
eligibleDirections |= direction;
|
||||
eligibleDirectionCount++;
|
||||
}
|
||||
|
||||
if (eligibleDirectionCount <= 0)
|
||||
continue; // Oof we've painted ourselves into a corner. Bad luck. Next part will handle this.
|
||||
|
||||
var molesToMove = tile._tileAtmosInfo.MoleDelta / eligibleDirectionCount;
|
||||
foreach (var direction in Cardinal)
|
||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||
{
|
||||
var hasDirection = false;
|
||||
for (var j = 0; j < eligibleDirectionCount; j++)
|
||||
{
|
||||
if (eligibleDirections[j] != direction) continue;
|
||||
hasDirection = true;
|
||||
break;
|
||||
}
|
||||
var direction = (AtmosDirection) (1 << j);
|
||||
if (!eligibleDirections.HasFlag(direction)) continue;
|
||||
|
||||
if (hasDirection || !tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
|
||||
tile.AdjustEqMovement(direction, molesToMove);
|
||||
tile._tileAtmosInfo.MoleDelta -= molesToMove;
|
||||
tile2._tileAtmosInfo.MoleDelta += molesToMove;
|
||||
tile._adjacentTiles[j]._tileAtmosInfo.MoleDelta += molesToMove;
|
||||
}
|
||||
|
||||
ArrayPool<Direction>.Shared.Return(eligibleDirections);
|
||||
}
|
||||
|
||||
giverTilesLength = 0;
|
||||
@@ -393,7 +395,7 @@ namespace Content.Server.Atmos
|
||||
for (var j = 0; j < giverTilesLength; j++)
|
||||
{
|
||||
var giver = giverTiles[j];
|
||||
giver._tileAtmosInfo.CurrentTransferDirection = (Direction) (-1);
|
||||
giver._tileAtmosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
|
||||
giver._tileAtmosInfo.CurrentTransferAmount = 0;
|
||||
var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
|
||||
var queueLength = 0;
|
||||
@@ -405,9 +407,11 @@ namespace Content.Server.Atmos
|
||||
break; // We're done here now. Let's not do more work than needed.
|
||||
|
||||
var tile = queue[i];
|
||||
foreach (var direction in Cardinal)
|
||||
for (var k = 0; k < Atmospherics.Directions; k++)
|
||||
{
|
||||
if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
|
||||
var direction = (AtmosDirection) (1 << k);
|
||||
if (!tile._adjacentBits.HasFlag(direction)) continue;
|
||||
var tile2 = tile._adjacentTiles[k];
|
||||
if (giver._tileAtmosInfo.MoleDelta <= 0) break; // We're done here now. Let's not do more work than needed.
|
||||
if (tile2._tileAtmosInfo.LastQueueCycle != queueCycle) continue;
|
||||
if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
||||
@@ -441,15 +445,11 @@ namespace Content.Server.Atmos
|
||||
for (var i = queueLength - 1; i >= 0; i--)
|
||||
{
|
||||
var tile = queue[i];
|
||||
if (tile._tileAtmosInfo.CurrentTransferAmount != 0 &&
|
||||
tile._tileAtmosInfo.CurrentTransferDirection != (Direction) (-1))
|
||||
if (tile._tileAtmosInfo.CurrentTransferAmount != 0 && tile._tileAtmosInfo.CurrentTransferDirection != AtmosDirection.Invalid)
|
||||
{
|
||||
tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection,
|
||||
tile._tileAtmosInfo.CurrentTransferAmount);
|
||||
if (tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection,
|
||||
out var adjacent))
|
||||
adjacent._tileAtmosInfo.CurrentTransferAmount +=
|
||||
tile._tileAtmosInfo.CurrentTransferAmount;
|
||||
tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount);
|
||||
tile._adjacentTiles[tile._tileAtmosInfo.CurrentTransferDirection.ToIndex()]
|
||||
._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
|
||||
tile._tileAtmosInfo.CurrentTransferAmount = 0;
|
||||
}
|
||||
}
|
||||
@@ -463,7 +463,7 @@ namespace Content.Server.Atmos
|
||||
for (var j = 0; j < takerTilesLength; j++)
|
||||
{
|
||||
var taker = takerTiles[j];
|
||||
taker._tileAtmosInfo.CurrentTransferDirection = Direction.Invalid;
|
||||
taker._tileAtmosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
|
||||
taker._tileAtmosInfo.CurrentTransferAmount = 0;
|
||||
var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
|
||||
var queueLength = 0;
|
||||
@@ -475,10 +475,11 @@ namespace Content.Server.Atmos
|
||||
break; // We're done here now. Let's not do more work than needed.
|
||||
|
||||
var tile = queue[i];
|
||||
foreach (var direction in Cardinal)
|
||||
for (var k = 0; k < Atmospherics.Directions; k++)
|
||||
{
|
||||
if (!tile._adjacentTiles.ContainsKey(direction)) continue;
|
||||
var tile2 = tile._adjacentTiles[direction];
|
||||
var direction = (AtmosDirection) (1 << k);
|
||||
if (!tile._adjacentBits.HasFlag(direction)) continue;
|
||||
var tile2 = tile._adjacentTiles[k];
|
||||
|
||||
if (taker._tileAtmosInfo.MoleDelta >= 0) break; // We're done here now. Let's not do more work than needed.
|
||||
if (tile2._tileAtmosInfo.LastQueueCycle != queueCycle) continue;
|
||||
@@ -512,16 +513,14 @@ namespace Content.Server.Atmos
|
||||
for (var i = queueLength - 1; i >= 0; i--)
|
||||
{
|
||||
var tile = queue[i];
|
||||
if (tile._tileAtmosInfo.CurrentTransferAmount == 0 || tile._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid)
|
||||
if (tile._tileAtmosInfo.CurrentTransferAmount == 0 || tile._tileAtmosInfo.CurrentTransferDirection == AtmosDirection.Invalid)
|
||||
continue;
|
||||
|
||||
tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount);
|
||||
|
||||
if (tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, out var adjacent))
|
||||
{
|
||||
adjacent._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
|
||||
tile._tileAtmosInfo.CurrentTransferAmount = 0;
|
||||
}
|
||||
tile._adjacentTiles[tile._tileAtmosInfo.CurrentTransferDirection.ToIndex()]
|
||||
._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
|
||||
tile._tileAtmosInfo.CurrentTransferAmount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,9 +536,11 @@ namespace Content.Server.Atmos
|
||||
for (var i = 0; i < tileCount; i++)
|
||||
{
|
||||
var tile = tiles[i];
|
||||
foreach (var direction in Cardinal)
|
||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||
{
|
||||
if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
|
||||
var direction = (AtmosDirection) (1 << j);
|
||||
if (!tile._adjacentBits.HasFlag(direction)) continue;
|
||||
var tile2 = tile._adjacentTiles[j];
|
||||
if (tile2?.Air?.Compare(Air) == GasMixture.GasCompareResult.NoExchange) continue;
|
||||
_gridAtmosphereComponent.AddActiveTile(tile2);
|
||||
break;
|
||||
@@ -555,69 +556,68 @@ namespace Content.Server.Atmos
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void FinalizeEq()
|
||||
{
|
||||
var transferDirections = new Dictionary<Direction, float>();
|
||||
var transferDirections = new float[Atmospherics.Directions];
|
||||
var hasTransferDirs = false;
|
||||
foreach (var direction in Cardinal)
|
||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
var amount = _tileAtmosInfo[direction];
|
||||
var amount = _tileAtmosInfo[i];
|
||||
if (amount == 0) continue;
|
||||
transferDirections[direction] = amount;
|
||||
_tileAtmosInfo[direction] = 0;
|
||||
transferDirections[i] = amount;
|
||||
_tileAtmosInfo[i] = 0; // Set them to 0 to prevent infinite recursion.
|
||||
hasTransferDirs = true;
|
||||
}
|
||||
|
||||
if (!hasTransferDirs) return;
|
||||
|
||||
foreach (var (direction, amount) in transferDirections)
|
||||
for(var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
if (!_adjacentTiles.TryGetValue(direction, out var tile) || tile.Air == null) continue;
|
||||
var direction = (AtmosDirection) (1 << i);
|
||||
if (!_adjacentBits.HasFlag(direction)) continue;
|
||||
var amount = transferDirections[i];
|
||||
var tile = _adjacentTiles[i];
|
||||
if (tile?.Air == null) continue;
|
||||
if (amount > 0)
|
||||
{
|
||||
if (Air.TotalMoles < amount)
|
||||
FinalizeEqNeighbors(transferDirections.Keys);
|
||||
FinalizeEqNeighbors(transferDirections);
|
||||
|
||||
tile._tileAtmosInfo[direction.GetOpposite()] = 0;
|
||||
tile.Air.Merge(Air.Remove(amount));
|
||||
UpdateVisuals();
|
||||
tile.UpdateVisuals();
|
||||
ConsiderPressureDifference(direction, amount);
|
||||
ConsiderPressureDifference(tile, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void FinalizeEqNeighbors(IEnumerable<Direction> directions)
|
||||
private void FinalizeEqNeighbors(in float[] transferDirs)
|
||||
{
|
||||
foreach (var direction in directions)
|
||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
var amount = _tileAtmosInfo[direction];
|
||||
if(amount < 0 && _adjacentTiles.TryGetValue(direction, out var adjacent))
|
||||
adjacent.FinalizeEq();
|
||||
var direction = (AtmosDirection) (1 << i);
|
||||
var amount = transferDirs[i];
|
||||
if(amount < 0 && _adjacentBits.HasFlag(direction))
|
||||
_adjacentTiles[i].FinalizeEq(); // A bit of recursion if needed.
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ConsiderPressureDifference(Direction direction, float difference)
|
||||
private void ConsiderPressureDifference(TileAtmosphere other, float difference)
|
||||
{
|
||||
_gridAtmosphereComponent.AddHighPressureDelta(this);
|
||||
if (difference > PressureDifference)
|
||||
{
|
||||
PressureDifference = difference;
|
||||
_pressureDirection = difference < 0 ? direction.GetOpposite() : direction;
|
||||
_pressureDirection = ((Vector2i)(GridIndices - other.GridIndices)).GetDir().ToAtmosDirection();
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void AdjustEqMovement(Direction direction, float molesToMove)
|
||||
private void AdjustEqMovement(AtmosDirection direction, float amount)
|
||||
{
|
||||
_tileAtmosInfo[direction] += molesToMove;
|
||||
if(direction != Direction.Invalid && _adjacentTiles.TryGetValue(direction, out var adj))
|
||||
adj._tileAtmosInfo[direction.GetOpposite()] -= molesToMove;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ResetTileAtmosInfo()
|
||||
{
|
||||
_tileAtmosInfo = new TileAtmosInfo {CurrentTransferDirection = Direction.Invalid};
|
||||
_tileAtmosInfo[direction] += amount;
|
||||
_adjacentTiles[direction.ToIndex()]._tileAtmosInfo[direction.GetOpposite()] -= amount;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@@ -635,9 +635,13 @@ namespace Content.Server.Atmos
|
||||
|
||||
_currentCycle = fireCount;
|
||||
var adjacentTileLength = 0;
|
||||
foreach (var (direction, enemyTile) in _adjacentTiles)
|
||||
for(var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
// If the tile is null or has no air, we don't do anything
|
||||
var direction = (AtmosDirection) (1 << i);
|
||||
if (!_adjacentBits.HasFlag(direction)) continue;
|
||||
var enemyTile = _adjacentTiles[i];
|
||||
|
||||
// If the tile is null or has no air, we don't do anything for it.
|
||||
if(enemyTile?.Air == null) continue;
|
||||
adjacentTileLength++;
|
||||
if (fireCount <= enemyTile._currentCycle) continue;
|
||||
@@ -685,11 +689,11 @@ namespace Content.Server.Atmos
|
||||
// Space wind!
|
||||
if (difference > 0)
|
||||
{
|
||||
ConsiderPressureDifference(direction, difference);
|
||||
ConsiderPressureDifference(enemyTile, difference);
|
||||
}
|
||||
else
|
||||
{
|
||||
enemyTile.ConsiderPressureDifference(direction.GetOpposite(), -difference);
|
||||
enemyTile.ConsiderPressureDifference(this, -difference);
|
||||
}
|
||||
|
||||
LastShareCheck();
|
||||
@@ -737,7 +741,7 @@ namespace Content.Server.Atmos
|
||||
if (Air.Temperature > Atmospherics.FireMinimumTemperatureToSpread)
|
||||
{
|
||||
var radiatedTemperature = Air.Temperature * Atmospherics.FireSpreadRadiosityScale;
|
||||
foreach (var (_, tile) in _adjacentTiles)
|
||||
foreach (var tile in _adjacentTiles)
|
||||
{
|
||||
if(!tile.Hotspot.Valid)
|
||||
tile.HotspotExpose(radiatedTemperature, Atmospherics.CellVolume/4);
|
||||
@@ -771,17 +775,19 @@ namespace Content.Server.Atmos
|
||||
else
|
||||
{
|
||||
var affected = Air.RemoveRatio(Hotspot.Volume / Air.Volume);
|
||||
if (affected != null)
|
||||
{
|
||||
affected.Temperature = Hotspot.Temperature;
|
||||
affected.React(this);
|
||||
Hotspot.Temperature = affected.Temperature;
|
||||
Hotspot.Volume = affected.ReactionResults[GasReaction.Fire] * Atmospherics.FireGrowthRate;
|
||||
AssumeAir(affected);
|
||||
}
|
||||
affected.Temperature = Hotspot.Temperature;
|
||||
affected.React(this);
|
||||
Hotspot.Temperature = affected.Temperature;
|
||||
Hotspot.Volume = affected.ReactionResults[GasReaction.Fire] * Atmospherics.FireGrowthRate;
|
||||
AssumeAir(affected);
|
||||
}
|
||||
|
||||
// TODO ATMOS Let all entities in this tile know about the fire?
|
||||
var tileRef = GridIndices.GetTileRef(GridIndex);
|
||||
|
||||
if (tileRef == null) return;
|
||||
|
||||
_gridAtmosphereComponent.Owner.EntityManager.
|
||||
EventBus.QueueEvent(EventSource.Local, new FireActEvent(Hotspot.Temperature, Hotspot.Volume));
|
||||
}
|
||||
|
||||
private bool ConsiderSuperconductivity()
|
||||
@@ -806,24 +812,24 @@ namespace Content.Server.Atmos
|
||||
public void Superconduct()
|
||||
{
|
||||
var directions = ConductivityDirections();
|
||||
var adjacentTiles = _gridAtmosphereComponent.GetAdjacentTiles(GridIndices, true);
|
||||
|
||||
if (directions.Length > 0)
|
||||
for(var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
foreach (var direction in directions)
|
||||
{
|
||||
if (!adjacentTiles.TryGetValue(direction, out var adjacent)) continue;
|
||||
var direction = (AtmosDirection) (1 << i);
|
||||
if (!directions.HasFlag(direction)) continue;
|
||||
|
||||
if (adjacent.ThermalConductivity == 0f)
|
||||
continue;
|
||||
var adjacent = _adjacentTiles[direction.ToIndex()];
|
||||
|
||||
if(adjacent._archivedCycle < _gridAtmosphereComponent.UpdateCounter)
|
||||
adjacent.Archive(_gridAtmosphereComponent.UpdateCounter);
|
||||
// TODO ATMOS handle adjacent being null.
|
||||
if (adjacent == null || adjacent.ThermalConductivity == 0f)
|
||||
continue;
|
||||
|
||||
adjacent.NeighborConductWithSource(this);
|
||||
if(adjacent._archivedCycle < _gridAtmosphereComponent.UpdateCounter)
|
||||
adjacent.Archive(_gridAtmosphereComponent.UpdateCounter);
|
||||
|
||||
adjacent.ConsiderSuperconductivity();
|
||||
}
|
||||
adjacent.NeighborConductWithSource(this);
|
||||
|
||||
adjacent.ConsiderSuperconductivity();
|
||||
}
|
||||
|
||||
RadiateToSpace();
|
||||
@@ -917,20 +923,20 @@ namespace Content.Server.Atmos
|
||||
}
|
||||
}
|
||||
|
||||
public Direction[] ConductivityDirections()
|
||||
public AtmosDirection ConductivityDirections()
|
||||
{
|
||||
if(BlocksAir)
|
||||
{
|
||||
if(_archivedCycle < _gridAtmosphereComponent.UpdateCounter)
|
||||
Archive(_gridAtmosphereComponent.UpdateCounter);
|
||||
return Cardinal;
|
||||
return AtmosDirection.All;
|
||||
}
|
||||
|
||||
// TODO ATMOS check if this is correct
|
||||
return Cardinal;
|
||||
return AtmosDirection.All;
|
||||
}
|
||||
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ExplosivelyDepressurize(int cycleNum)
|
||||
{
|
||||
if (Air == null) return;
|
||||
@@ -947,14 +953,13 @@ namespace Content.Server.Atmos
|
||||
|
||||
tiles[tileCount++] = this;
|
||||
|
||||
ResetTileAtmosInfo();
|
||||
_tileAtmosInfo.LastQueueCycle = queueCycle;
|
||||
_tileAtmosInfo = new TileAtmosInfo {LastQueueCycle = queueCycle};
|
||||
|
||||
for (var i = 0; i < tileCount; i++)
|
||||
{
|
||||
var tile = tiles[i];
|
||||
tile._tileAtmosInfo.LastCycle = cycleNum;
|
||||
tile._tileAtmosInfo.CurrentTransferDirection = Direction.Invalid;
|
||||
tile._tileAtmosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
|
||||
if (tile.Air.Immutable)
|
||||
{
|
||||
spaceTiles[spaceTileCount++] = tile;
|
||||
@@ -962,18 +967,19 @@ namespace Content.Server.Atmos
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var direction in Cardinal)
|
||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||
{
|
||||
if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
|
||||
var direction = (AtmosDirection) (1 << j);
|
||||
if (!tile._adjacentBits.HasFlag(direction)) continue;
|
||||
var tile2 = tile._adjacentTiles[j];
|
||||
if (tile2.Air == null) continue;
|
||||
if (tile2._tileAtmosInfo.LastQueueCycle == queueCycle) continue;
|
||||
|
||||
tile.ConsiderFirelocks(tile2);
|
||||
|
||||
// The firelocks might have closed on us.
|
||||
if (tile._adjacentTiles[direction]?.Air == null) continue;
|
||||
tile2.ResetTileAtmosInfo();
|
||||
tile2._tileAtmosInfo.LastQueueCycle = queueCycle;
|
||||
if (!tile._adjacentBits.HasFlag(direction)) continue;
|
||||
tile2._tileAtmosInfo = new TileAtmosInfo {LastQueueCycle = queueCycle};
|
||||
tiles[tileCount++] = tile2;
|
||||
}
|
||||
}
|
||||
@@ -991,18 +997,21 @@ namespace Content.Server.Atmos
|
||||
var tile = spaceTiles[i];
|
||||
progressionOrder[progressionCount++] = tile;
|
||||
tile._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
|
||||
tile._tileAtmosInfo.CurrentTransferDirection = Direction.Invalid;
|
||||
tile._tileAtmosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
|
||||
}
|
||||
|
||||
for (var i = 0; i < progressionCount; i++)
|
||||
{
|
||||
var tile = progressionOrder[i];
|
||||
foreach (var direction in Cardinal)
|
||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||
{
|
||||
if (!tile._adjacentTiles.TryGetValue(direction, out var tile2)) continue;
|
||||
var direction = (AtmosDirection) (1 << j);
|
||||
// TODO ATMOS This is a terrible hack that accounts for the mess that are space TileAtmospheres.
|
||||
if (!tile._adjacentBits.HasFlag(direction) && !tile.Air.Immutable) continue;
|
||||
var tile2 = tile._adjacentTiles[j];
|
||||
if (tile2?._tileAtmosInfo.LastQueueCycle != queueCycle) continue;
|
||||
if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
||||
if(tile2.Air.Immutable) continue;
|
||||
if(tile2.Air?.Immutable ?? false) continue;
|
||||
tile2._tileAtmosInfo.CurrentTransferDirection = direction.GetOpposite();
|
||||
tile2._tileAtmosInfo.CurrentTransferAmount = 0;
|
||||
tile2.PressureSpecificTarget = tile.PressureSpecificTarget;
|
||||
@@ -1014,10 +1023,11 @@ namespace Content.Server.Atmos
|
||||
for (var i = progressionCount - 1; i >= 0; i--)
|
||||
{
|
||||
var tile = progressionOrder[i];
|
||||
if (tile._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid) continue;
|
||||
if (tile._tileAtmosInfo.CurrentTransferDirection == AtmosDirection.Invalid) continue;
|
||||
_gridAtmosphereComponent.AddHighPressureDelta(tile);
|
||||
_gridAtmosphereComponent.AddActiveTile(tile);
|
||||
if (!tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, out var tile2) || tile2.Air == null) continue;
|
||||
var tile2 = tile._adjacentTiles[tile._tileAtmosInfo.CurrentTransferDirection.ToIndex()];
|
||||
if (tile2?.Air == null) continue;
|
||||
var sum = tile2.Air.TotalMoles;
|
||||
totalGasesRemoved += sum;
|
||||
tile._tileAtmosInfo.CurrentTransferAmount += sum;
|
||||
@@ -1025,7 +1035,7 @@ namespace Content.Server.Atmos
|
||||
tile.PressureDifference = tile._tileAtmosInfo.CurrentTransferAmount;
|
||||
tile._pressureDirection = tile._tileAtmosInfo.CurrentTransferDirection;
|
||||
|
||||
if (tile2._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid)
|
||||
if (tile2._tileAtmosInfo.CurrentTransferDirection == AtmosDirection.Invalid)
|
||||
{
|
||||
tile2.PressureDifference = tile2._tileAtmosInfo.CurrentTransferAmount;
|
||||
tile2._pressureDirection = tile._tileAtmosInfo.CurrentTransferDirection;
|
||||
@@ -1101,21 +1111,30 @@ namespace Content.Server.Atmos
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void UpdateAdjacent()
|
||||
{
|
||||
foreach (var direction in Cardinal)
|
||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
if (!_gridAtmosphereComponent.IsAirBlocked(GridIndices.Offset(direction)))
|
||||
var direction = (AtmosDirection) (1 << i);
|
||||
|
||||
var otherIndices = GridIndices.Offset(direction.ToDirection());
|
||||
|
||||
var isSpace = _gridAtmosphereComponent.IsSpace(GridIndices);
|
||||
var adjacent = _gridAtmosphereComponent.GetTile(otherIndices, !isSpace);
|
||||
_adjacentTiles[direction.ToIndex()] = adjacent;
|
||||
adjacent?.UpdateAdjacent(direction.GetOpposite());
|
||||
|
||||
if (adjacent != null && !_gridAtmosphereComponent.IsAirBlocked(adjacent.GridIndices))
|
||||
{
|
||||
var adjacent = _gridAtmosphereComponent.GetTile(GridIndices.Offset(direction));
|
||||
_adjacentTiles[direction] = adjacent;
|
||||
adjacent.UpdateAdjacent(direction.GetOpposite());
|
||||
_adjacentBits |= direction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateAdjacent(Direction direction)
|
||||
public void UpdateAdjacent(AtmosDirection direction)
|
||||
{
|
||||
if (!_gridAtmosphereComponent.IsAirBlocked(GridIndices.Offset(direction)))
|
||||
_adjacentTiles[direction] = _gridAtmosphereComponent.GetTile(GridIndices.Offset(direction));
|
||||
if (!_gridAtmosphereComponent.IsAirBlocked(GridIndices.Offset(direction.ToDirection())))
|
||||
{
|
||||
_adjacentTiles[direction.ToIndex()] = _gridAtmosphereComponent.GetTile(GridIndices.Offset(direction.ToDirection()));
|
||||
}
|
||||
}
|
||||
|
||||
private void LastShareCheck()
|
||||
@@ -1130,13 +1149,7 @@ namespace Content.Server.Atmos
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly Direction[] Cardinal =
|
||||
new Direction[]
|
||||
{
|
||||
Direction.North, Direction.East, Direction.South, Direction.West
|
||||
};
|
||||
|
||||
public void TemperatureExpose(GasMixture mixture, float temperature, float cellVolume)
|
||||
public void TemperatureExpose(GasMixture air, float temperature, float volume)
|
||||
{
|
||||
// TODO ATMOS do this
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#nullable enable
|
||||
#nullable enable
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.Body;
|
||||
using Content.Shared.Body.Part;
|
||||
@@ -42,7 +42,7 @@ namespace Content.Server.Body
|
||||
}
|
||||
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
prototypeManager.TryIndex("bodyPart.Hand.BasicHuman", out BodyPartPrototype prototype);
|
||||
prototypeManager.TryIndex("bodyPart.LHand.BasicHuman", out BodyPartPrototype prototype);
|
||||
|
||||
var part = new BodyPart(prototype);
|
||||
var slot = part.GetHashCode().ToString();
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.Components.Observer;
|
||||
using Content.Server.GameObjects.Components.Observer;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.Chat;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using NFluidsynth;
|
||||
using Robust.Server.Console;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
@@ -13,6 +11,10 @@ using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Network;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static Content.Server.Interfaces.Chat.IChatManager;
|
||||
|
||||
namespace Content.Server.Chat
|
||||
{
|
||||
@@ -33,14 +35,15 @@ namespace Content.Server.Chat
|
||||
/// </summary>
|
||||
private const string MaxLengthExceededMessage = "Your message exceeded {0} character limit";
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
|
||||
[Dependency] private readonly IServerNetManager _netManager;
|
||||
[Dependency] private readonly IPlayerManager _playerManager;
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||
[Dependency] private readonly IMoMMILink _mommiLink;
|
||||
[Dependency] private readonly IConGroupController _conGroupController;
|
||||
#pragma warning restore 649
|
||||
//TODO: make prio based?
|
||||
private List<TransformChat> _chatTransformHandlers;
|
||||
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
||||
[Dependency] private readonly IServerNetManager _netManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager = default!;
|
||||
[Dependency] private readonly IMoMMILink _mommiLink = default!;
|
||||
[Dependency] private readonly IConGroupController _conGroupController = default!;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
@@ -51,6 +54,8 @@ namespace Content.Server.Chat
|
||||
var msg = _netManager.CreateNetMessage<ChatMaxMsgLengthMessage>();
|
||||
msg.MaxMessageLength = MaxMessageLength;
|
||||
_netManager.ServerSendToAll(msg);
|
||||
|
||||
_chatTransformHandlers = new List<TransformChat>();
|
||||
}
|
||||
|
||||
public void DispatchServerAnnouncement(string message)
|
||||
@@ -98,6 +103,12 @@ namespace Content.Server.Chat
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var handler in _chatTransformHandlers)
|
||||
{
|
||||
//TODO: rather return a bool and use a out var?
|
||||
message = handler(source, message);
|
||||
}
|
||||
|
||||
var pos = source.Transform.GridPosition;
|
||||
var clients = _playerManager.GetPlayersInRange(pos, VoiceRange).Select(p => p.ConnectedClient);
|
||||
|
||||
@@ -217,5 +228,10 @@ namespace Content.Server.Chat
|
||||
response.MaxMessageLength = MaxMessageLength;
|
||||
_netManager.ServerSendMessage(response, msg.MsgChannel);
|
||||
}
|
||||
|
||||
public void RegisterChatTransform(TransformChat handler)
|
||||
{
|
||||
_chatTransformHandlers.Add(handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ using Robust.Shared.Interfaces.Log;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Timing;
|
||||
using Content.Server.GameObjects.Components.Mobs.Speech;
|
||||
|
||||
namespace Content.Server
|
||||
{
|
||||
@@ -64,6 +65,7 @@ namespace Content.Server
|
||||
IoCManager.Resolve<IServerPreferencesManager>().StartInit();
|
||||
IoCManager.Resolve<INodeGroupFactory>().Initialize();
|
||||
IoCManager.Resolve<ISandboxManager>().Initialize();
|
||||
IoCManager.Resolve<IAccentManager>().Initialize();
|
||||
}
|
||||
|
||||
public override void PostInit()
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user