Re-organize all projects (#4166)

This commit is contained in:
DrSmugleaf
2021-06-09 22:19:39 +02:00
committed by GitHub
parent 9f50e4061b
commit ff1a2d97ea
1773 changed files with 5258 additions and 5508 deletions

View File

@@ -1,110 +0,0 @@
#nullable enable
using System;
using System.Linq;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
namespace Content.Shared.GameObjects.EntitySystems
{
/// <summary>
/// This interface gives components behavior on getting destroyed.
/// </summary>
public interface IDestroyAct
{
/// <summary>
/// Called when object is destroyed
/// </summary>
void OnDestroy(DestructionEventArgs eventArgs);
}
public class DestructionEventArgs : EventArgs
{
public IEntity Owner { get; set; } = default!;
}
public class BreakageEventArgs : EventArgs
{
public IEntity Owner { get; set; } = default!;
}
public interface IBreakAct
{
/// <summary>
/// Called when object is broken
/// </summary>
void OnBreak(BreakageEventArgs eventArgs);
}
public interface IExAct
{
/// <summary>
/// Called when explosion reaches the entity
/// </summary>
void OnExplosion(ExplosionEventArgs eventArgs);
}
public class ExplosionEventArgs : EventArgs
{
public EntityCoordinates Source { get; set; }
public IEntity Target { get; set; } = default!;
public ExplosionSeverity Severity { get; set; }
}
[UsedImplicitly]
public sealed class ActSystem : EntitySystem
{
public void HandleDestruction(IEntity owner)
{
var eventArgs = new DestructionEventArgs
{
Owner = owner
};
var destroyActs = owner.GetAllComponents<IDestroyAct>().ToList();
foreach (var destroyAct in destroyActs)
{
destroyAct.OnDestroy(eventArgs);
}
owner.QueueDelete();
}
public void HandleExplosion(EntityCoordinates source, IEntity target, ExplosionSeverity severity)
{
var eventArgs = new ExplosionEventArgs
{
Source = source,
Target = target,
Severity = severity
};
var exActs = target.GetAllComponents<IExAct>().ToList();
foreach (var exAct in exActs)
{
exAct.OnExplosion(eventArgs);
}
}
public void HandleBreakage(IEntity owner)
{
var eventArgs = new BreakageEventArgs
{
Owner = owner,
};
var breakActs = owner.GetAllComponents<IBreakAct>().ToList();
foreach (var breakAct in breakActs)
{
breakAct.OnBreak(eventArgs);
}
}
}
public enum ExplosionSeverity
{
Light,
Heavy,
Destruction,
}
}

View File

@@ -1,78 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.EntitySystems.ActionBlocker
{
public static class ActionBlockerExtensions
{
public static bool CanMove(this IEntity entity)
{
return ActionBlockerSystem.CanMove(entity);
}
public static bool CanInteract(this IEntity entity)
{
return ActionBlockerSystem.CanInteract(entity);
}
public static bool CanUse(this IEntity entity)
{
return ActionBlockerSystem.CanUse(entity);
}
public static bool CanThrow(this IEntity entity)
{
return ActionBlockerSystem.CanThrow(entity);
}
public static bool CanSpeak(this IEntity entity)
{
return ActionBlockerSystem.CanSpeak(entity);
}
public static bool CanDrop(this IEntity entity)
{
return ActionBlockerSystem.CanDrop(entity);
}
public static bool CanPickup(this IEntity entity)
{
return ActionBlockerSystem.CanPickup(entity);
}
public static bool CanEmote(this IEntity entity)
{
return ActionBlockerSystem.CanEmote(entity);
}
public static bool CanAttack(this IEntity entity)
{
return ActionBlockerSystem.CanAttack(entity);
}
public static bool CanEquip(this IEntity entity)
{
return ActionBlockerSystem.CanEquip(entity);
}
public static bool CanUnequip(this IEntity entity)
{
return ActionBlockerSystem.CanUnequip(entity);
}
public static bool CanChangeDirection(this IEntity entity)
{
return ActionBlockerSystem.CanChangeDirection(entity);
}
public static bool CanShiver(this IEntity entity)
{
return ActionBlockerSystem.CanShiver(entity);
}
public static bool CanSweat(this IEntity entity)
{
return ActionBlockerSystem.CanSweat(entity);
}
}
}

View File

@@ -1,197 +0,0 @@
#nullable enable
using System.Diagnostics.CodeAnalysis;
using Content.Shared.GameObjects.Components.Mobs.Speech;
using Content.Shared.GameObjects.EntitySystems.EffectBlocker;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.EntitySystems.ActionBlocker
{
/// <summary>
/// Utility methods to check if a specific entity is allowed to perform an action.
/// For effects see <see cref="EffectBlockerSystem"/>
/// </summary>
[UsedImplicitly]
public class ActionBlockerSystem : EntitySystem
{
public static bool CanMove(IEntity entity)
{
var canMove = true;
foreach (var blocker in entity.GetAllComponents<IActionBlocker>())
{
canMove &= blocker.CanMove(); // Sets var to false if false
}
return canMove;
}
public static bool CanInteract([NotNullWhen(true)] IEntity? entity)
{
if (entity == null)
{
return false;
}
var canInteract = true;
foreach (var blocker in entity.GetAllComponents<IActionBlocker>())
{
canInteract &= blocker.CanInteract();
}
return canInteract;
}
public static bool CanUse([NotNullWhen(true)] IEntity? entity)
{
if (entity == null)
{
return false;
}
var canUse = true;
foreach (var blocker in entity.GetAllComponents<IActionBlocker>())
{
canUse &= blocker.CanUse();
}
return canUse;
}
public static bool CanThrow(IEntity entity)
{
var canThrow = true;
foreach (var blocker in entity.GetAllComponents<IActionBlocker>())
{
canThrow &= blocker.CanThrow();
}
return canThrow;
}
public static bool CanSpeak(IEntity entity)
{
if (!entity.HasComponent<SharedSpeechComponent>())
return false;
var canSpeak = true;
foreach (var blocker in entity.GetAllComponents<IActionBlocker>())
{
canSpeak &= blocker.CanSpeak();
}
return canSpeak;
}
public static bool CanDrop(IEntity entity)
{
var canDrop = true;
foreach (var blocker in entity.GetAllComponents<IActionBlocker>())
{
canDrop &= blocker.CanDrop();
}
return canDrop;
}
public static bool CanPickup(IEntity entity)
{
var canPickup = true;
foreach (var blocker in entity.GetAllComponents<IActionBlocker>())
{
canPickup &= blocker.CanPickup();
}
return canPickup;
}
public static bool CanEmote(IEntity entity)
{
if (!entity.HasComponent<SharedEmotingComponent>())
return false;
var canEmote = true;
foreach (var blocker in entity.GetAllComponents<IActionBlocker>())
{
canEmote &= blocker.CanEmote();
}
return canEmote;
}
public static bool CanAttack(IEntity entity)
{
var canAttack = true;
foreach (var blocker in entity.GetAllComponents<IActionBlocker>())
{
canAttack &= blocker.CanAttack();
}
return canAttack;
}
public static bool CanEquip(IEntity entity)
{
var canEquip = true;
foreach (var blocker in entity.GetAllComponents<IActionBlocker>())
{
canEquip &= blocker.CanEquip();
}
return canEquip;
}
public static bool CanUnequip(IEntity entity)
{
var canUnequip = true;
foreach (var blocker in entity.GetAllComponents<IActionBlocker>())
{
canUnequip &= blocker.CanUnequip();
}
return canUnequip;
}
public static bool CanChangeDirection(IEntity entity)
{
var canChangeDirection = true;
foreach (var blocker in entity.GetAllComponents<IActionBlocker>())
{
canChangeDirection &= blocker.CanChangeDirection();
}
return canChangeDirection;
}
public static bool CanShiver(IEntity entity)
{
var canShiver = true;
foreach (var component in entity.GetAllComponents<IActionBlocker>())
{
canShiver &= component.CanShiver();
}
return canShiver;
}
public static bool CanSweat(IEntity entity)
{
var canSweat = true;
foreach (var component in entity.GetAllComponents<IActionBlocker>())
{
canSweat &= component.CanSweat();
}
return canSweat;
}
}
}

View File

@@ -1,40 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.EntitySystems.EffectBlocker;
namespace Content.Shared.GameObjects.EntitySystems.ActionBlocker
{
/// <summary>
/// This interface gives components the ability to block certain actions from
/// being done by the owning entity. For effects see <see cref="IEffectBlocker"/>
/// </summary>
public interface IActionBlocker
{
bool CanMove() => true;
bool CanInteract() => true;
bool CanUse() => true;
bool CanThrow() => true;
bool CanSpeak() => true;
bool CanDrop() => true;
bool CanPickup() => true;
bool CanEmote() => true;
bool CanAttack() => true;
bool CanEquip() => true;
bool CanUnequip() => true;
bool CanChangeDirection() => true;
bool CanShiver() => true;
bool CanSweat() => true;
}
}

View File

@@ -1,74 +0,0 @@
#nullable enable
using System;
using System.Linq;
using Content.Shared.Chemistry;
using Content.Shared.GameObjects.Components.Chemistry;
using Content.Shared.Interfaces.GameObjects.Components;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
namespace Content.Shared.GameObjects.EntitySystems
{
/// <summary>
/// This interface gives components behavior on whether entities solution (implying SolutionComponent is in place) is changed
/// </summary>
public interface ISolutionChange
{
/// <summary>
/// Called when solution is mixed with some other solution, or when some part of the solution is removed
/// </summary>
void SolutionChanged(SolutionChangeEventArgs eventArgs);
}
public class SolutionChangeEventArgs : EventArgs
{
public IEntity Owner { get; set; } = default!;
}
[UsedImplicitly]
public class ChemistrySystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public void HandleSolutionChange(IEntity owner)
{
var eventArgs = new SolutionChangeEventArgs
{
Owner = owner,
};
var solutionChangeArgs = owner.GetAllComponents<ISolutionChange>().ToList();
foreach (var solutionChangeArg in solutionChangeArgs)
{
solutionChangeArg.SolutionChanged(eventArgs);
if (owner.Deleted)
return;
}
}
public void ReactionEntity(IEntity? entity, ReactionMethod method, string reagentId, ReagentUnit reactVolume, Solution? source)
{
// We throw if the reagent specified doesn't exist.
ReactionEntity(entity, method, _prototypeManager.Index<ReagentPrototype>(reagentId), reactVolume, source);
}
public void ReactionEntity(IEntity? entity, ReactionMethod method, ReagentPrototype reagent, ReagentUnit reactVolume, Solution? source)
{
if (entity == null || entity.Deleted || !entity.TryGetComponent(out ReactiveComponent? reactive))
return;
foreach (var reaction in reactive.Reactions)
{
// If we have a source solution, use the reagent quantity we have left. Otherwise, use the reaction volume specified.
reaction.React(method, entity, reagent, source?.GetReagentQuantity(reagent.ID) ?? reactVolume, source);
// Make sure we still have enough reagent to go...
if (source != null && !source.ContainsReagent(reagent.ID))
break;
}
}
}
}

View File

@@ -1,68 +0,0 @@
#nullable enable
using System.Collections.Generic;
using System.Collections.Immutable;
using Content.Shared.Damage;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.EntitySystems
{
[UsedImplicitly]
public class DamageSystem : EntitySystem
{
public static ImmutableDictionary<DamageClass, ImmutableList<DamageType>> ClassToType { get; } = DefaultClassToType();
public static ImmutableDictionary<DamageType, DamageClass> TypeToClass { get; } = DefaultTypeToClass();
private static ImmutableDictionary<DamageClass, ImmutableList<DamageType>> DefaultClassToType()
{
return new Dictionary<DamageClass, ImmutableList<DamageType>>
{
[DamageClass.Brute] = new List<DamageType>
{
DamageType.Blunt,
DamageType.Slash,
DamageType.Piercing
}.ToImmutableList(),
[DamageClass.Burn] = new List<DamageType>
{
DamageType.Heat,
DamageType.Shock,
DamageType.Cold
}.ToImmutableList(),
[DamageClass.Toxin] = new List<DamageType>
{
DamageType.Poison,
DamageType.Radiation
}.ToImmutableList(),
[DamageClass.Airloss] = new List<DamageType>
{
DamageType.Asphyxiation,
DamageType.Bloodloss
}.ToImmutableList(),
[DamageClass.Genetic] = new List<DamageType>
{
DamageType.Cellular
}.ToImmutableList()
}.ToImmutableDictionary();
}
private static ImmutableDictionary<DamageType, DamageClass> DefaultTypeToClass()
{
return new Dictionary<DamageType, DamageClass>
{
{DamageType.Blunt, DamageClass.Brute},
{DamageType.Slash, DamageClass.Brute},
{DamageType.Piercing, DamageClass.Brute},
{DamageType.Heat, DamageClass.Burn},
{DamageType.Shock, DamageClass.Burn},
{DamageType.Cold, DamageClass.Burn},
{DamageType.Poison, DamageClass.Toxin},
{DamageType.Radiation, DamageClass.Toxin},
{DamageType.Asphyxiation, DamageClass.Airloss},
{DamageType.Bloodloss, DamageClass.Airloss},
{DamageType.Cellular, DamageClass.Genetic}
}.ToImmutableDictionary();
}
}
}

View File

@@ -1,18 +0,0 @@
#nullable enable
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.EntitySystems.EffectBlocker
{
public static class EffectBlockerExtensions
{
public static bool CanFall(this IEntity entity)
{
return EffectBlockerSystem.CanFall(entity);
}
public static bool CanSlip(this IEntity entity)
{
return EffectBlockerSystem.CanSlip(entity);
}
}
}

View File

@@ -1,39 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.EntitySystems.EffectBlocker
{
/// <summary>
/// Utility methods to check if an effect is allowed to affect a specific entity.
/// For actions see <see cref="ActionBlockerSystem"/>
/// </summary>
[UsedImplicitly]
public class EffectBlockerSystem : EntitySystem
{
public static bool CanFall(IEntity entity)
{
var canFall = true;
foreach (var blocker in entity.GetAllComponents<IEffectBlocker>())
{
canFall &= blocker.CanFall(); // Sets var to false if false
}
return canFall;
}
public static bool CanSlip(IEntity entity)
{
var canSlip = true;
foreach (var blocker in entity.GetAllComponents<IEffectBlocker>())
{
canSlip &= blocker.CanSlip(); // Sets var to false if false
}
return canSlip;
}
}
}

View File

@@ -1,15 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
namespace Content.Shared.GameObjects.EntitySystems.EffectBlocker
{
/// <summary>
/// This interface gives components the ability to block certain effects
/// from affecting the owning entity. For actions see <see cref="IActionBlocker"/>
/// </summary>
public interface IEffectBlocker
{
bool CanFall() => true;
bool CanSlip() => true;
}
}

View File

@@ -1,249 +0,0 @@
#nullable enable
using System;
using System.Linq;
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Utility;
using JetBrains.Annotations;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Utility;
using static Content.Shared.GameObjects.EntitySystems.SharedInteractionSystem;
namespace Content.Shared.GameObjects.EntitySystems
{
[Obsolete("Use ExaminedEvent instead.")]
public interface IExamine
{
/// <summary>
/// Returns a status examine value for components appended to the end of the description of the entity
/// </summary>
/// <param name="message">The message to append to which will be displayed.</param>
/// <param name="inDetailsRange">Whether the examiner is within the 'Details' range, allowing you to show information logically only availabe when close to the examined entity.</param>
void Examine(FormattedMessage message, bool inDetailsRange);
}
public abstract class ExamineSystemShared : EntitySystem
{
public const float ExamineRange = 16f;
public const float ExamineRangeSquared = ExamineRange * ExamineRange;
protected const float ExamineDetailsRange = 3f;
private static bool IsInDetailsRange(IEntity examiner, IEntity entity)
{
return examiner.InRangeUnobstructed(entity, ExamineDetailsRange, ignoreInsideBlocker: true) &&
examiner.IsInSameOrNoContainer(entity);
}
[Pure]
protected static bool CanExamine(IEntity examiner, IEntity examined)
{
if (!examiner.TryGetComponent(out ExaminerComponent? examinerComponent))
{
return false;
}
if (!examinerComponent.DoRangeCheck)
{
return true;
}
if (examiner.Transform.MapID != examined.Transform.MapID)
{
return false;
}
Ignored predicate = entity => entity == examiner || entity == examined;
if (examiner.TryGetContainer(out var container))
{
predicate += entity => entity == container.Owner;
}
return InRangeUnOccluded(
examiner.Transform.MapPosition,
examined.Transform.MapPosition,
ExamineRange,
predicate: predicate,
ignoreInsideBlocker: true);
}
public static bool InRangeUnOccluded(MapCoordinates origin, MapCoordinates other, float range, Ignored? predicate, bool ignoreInsideBlocker = true)
{
var occluderSystem = Get<OccluderSystem>();
if (!origin.InRange(other, range)) return false;
var dir = other.Position - origin.Position;
if (dir.LengthSquared.Equals(0f)) return true;
if (range > 0f && !(dir.LengthSquared <= range * range)) return false;
predicate ??= _ => false;
var ray = new Ray(origin.Position, dir.Normalized);
var rayResults = occluderSystem
.IntersectRayWithPredicate(origin.MapId, ray, dir.Length, predicate.Invoke, false).ToList();
if (rayResults.Count == 0) return true;
if (!ignoreInsideBlocker) return false;
foreach (var result in rayResults)
{
if (!result.HitEntity.TryGetComponent(out OccluderComponent? o))
{
continue;
}
var bBox = o.BoundingBox.Translated(o.Owner.Transform.WorldPosition);
if (bBox.Contains(origin.Position) || bBox.Contains(other.Position))
{
continue;
}
return false;
}
return true;
}
public static bool InRangeUnOccluded(IEntity origin, IEntity other, float range, Ignored? predicate, bool ignoreInsideBlocker = true)
{
var originPos = origin.Transform.MapPosition;
var otherPos = other.Transform.MapPosition;
return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker);
}
public static bool InRangeUnOccluded(IEntity origin, IComponent other, float range, Ignored? predicate, bool ignoreInsideBlocker = true)
{
var originPos = origin.Transform.MapPosition;
var otherPos = other.Owner.Transform.MapPosition;
return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker);
}
public static bool InRangeUnOccluded(IEntity origin, EntityCoordinates other, float range, Ignored? predicate, bool ignoreInsideBlocker = true)
{
var originPos = origin.Transform.MapPosition;
var otherPos = other.ToMap(origin.EntityManager);
return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker);
}
public static bool InRangeUnOccluded(IEntity origin, MapCoordinates other, float range, Ignored? predicate, bool ignoreInsideBlocker = true)
{
var originPos = origin.Transform.MapPosition;
return InRangeUnOccluded(originPos, other, range, predicate, ignoreInsideBlocker);
}
public static bool InRangeUnOccluded(ITargetedInteractEventArgs args, float range, Ignored? predicate, bool ignoreInsideBlocker = true)
{
var originPos = args.User.Transform.MapPosition;
var otherPos = args.Target.Transform.MapPosition;
return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker);
}
public static bool InRangeUnOccluded(DragDropEvent args, float range, Ignored? predicate, bool ignoreInsideBlocker = true)
{
var originPos = args.User.Transform.MapPosition;
var otherPos = args.DropLocation.ToMap(args.User.EntityManager);
return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker);
}
public static bool InRangeUnOccluded(AfterInteractEventArgs args, float range, Ignored? predicate, bool ignoreInsideBlocker = true)
{
var originPos = args.User.Transform.MapPosition;
var otherPos = args.Target?.Transform.MapPosition ?? args.ClickLocation.ToMap(args.User.EntityManager);
return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker);
}
public FormattedMessage GetExamineText(IEntity entity, IEntity? examiner)
{
var message = new FormattedMessage();
if (examiner == null)
{
return message;
}
var doNewline = false;
//Add an entity description if one is declared
if (!string.IsNullOrEmpty(entity.Description))
{
message.AddText(entity.Description);
doNewline = true;
}
message.PushColor(Color.DarkGray);
// Raise the event and let things that subscribe to it change the message...
RaiseLocalEvent(entity.Uid, new ExaminedEvent(message, entity, examiner, IsInDetailsRange(examiner, entity)));
//Add component statuses from components that report one
foreach (var examineComponent in entity.GetAllComponents<IExamine>())
{
var subMessage = new FormattedMessage();
examineComponent.Examine(subMessage, IsInDetailsRange(examiner, entity));
if (subMessage.Tags.Count == 0)
continue;
if (doNewline)
message.AddText("\n");
message.AddMessage(subMessage);
doNewline = true;
}
message.Pop();
return message;
}
}
/// <summary>
/// Raised when an entity is examined.
/// You have to manually add a newline at the start, and perform cleanup (popping state) at the end.
/// </summary>
public class ExaminedEvent : EntityEventArgs
{
/// <summary>
/// The message that will be displayed as the examine text.
/// Use the methods it exposes to change it, and don't forget to add a newline at the start!
/// Input/Output parameter.
/// </summary>
public FormattedMessage Message { get; }
/// <summary>
/// The entity performing the examining.
/// </summary>
public IEntity Examiner { get; }
/// <summary>
/// Entity being examined, for broadcast event purposes.
/// </summary>
public IEntity Examined { get; }
/// <summary>
/// Whether the examiner is in range of the entity to get some extra details.
/// </summary>
public bool IsInDetailsRange { get; }
public ExaminedEvent(FormattedMessage message, IEntity examined, IEntity examiner, bool isInDetailsRange)
{
Message = message;
Examined = examined;
Examiner = examiner;
IsInDetailsRange = isInDetailsRange;
}
}
}

View File

@@ -1,21 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Components.Body.Mechanism;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.EntitySystems
{
[UsedImplicitly]
public class MechanismSystem : EntitySystem
{
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var mechanism in ComponentManager.EntityQuery<IMechanism>(true))
{
mechanism.Update(frameTime);
}
}
}
}

View File

@@ -1,24 +0,0 @@
using Content.Shared.GameObjects.Components.Projectiles;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics.Dynamics;
namespace Content.Shared.GameObjects.EntitySystems
{
public sealed class ProjectileSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SharedProjectileComponent, PreventCollideEvent>(PreventCollision);
}
private void PreventCollision(EntityUid uid, SharedProjectileComponent component, PreventCollideEvent args)
{
if (component.IgnoreShooter && args.BodyB.Owner.Uid == component.Shooter)
{
args.Cancel();
return;
}
}
}
}

View File

@@ -1,30 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Components.Mobs;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.EntitySystems
{
/// <summary>
/// Evicts action states with expired cooldowns.
/// </summary>
public class SharedActionSystem : EntitySystem
{
private const float CooldownCheckIntervalSeconds = 10;
private float _timeSinceCooldownCheck;
public override void Update(float frameTime)
{
base.Update(frameTime);
_timeSinceCooldownCheck += frameTime;
if (_timeSinceCooldownCheck < CooldownCheckIntervalSeconds) return;
foreach (var comp in ComponentManager.EntityQuery<SharedActionsComponent>(false))
{
comp.ExpireCooldowns();
}
_timeSinceCooldownCheck -= CooldownCheckIntervalSeconds;
}
}
}

View File

@@ -1,26 +0,0 @@
using Content.Shared.GameObjects.Components.Buckle;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics.Dynamics;
namespace Content.Shared.GameObjects.EntitySystems
{
public abstract class SharedBuckleSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SharedBuckleComponent, PreventCollideEvent>(PreventCollision);
}
private void PreventCollision(EntityUid uid, SharedBuckleComponent component, PreventCollideEvent args)
{
if (args.BodyB.Owner.Uid != component.LastEntityBuckledTo) return;
component.IsOnStrapEntityThisFrame = true;
if (component.Buckled || component.DontCollide)
{
args.Cancel();
}
}
}
}

View File

@@ -1,155 +0,0 @@
#nullable enable
using Content.Shared.Chemistry;
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Prototypes;
using System.Collections.Generic;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.EntitySystems
{
public abstract class SharedChemicalReactionSystem : EntitySystem
{
private IEnumerable<ReactionPrototype> _reactions = default!;
private const int MaxReactionIterations = 20;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override void Initialize()
{
base.Initialize();
_reactions = _prototypeManager.EnumeratePrototypes<ReactionPrototype>();
}
/// <summary>
/// Checks if a solution can undergo a specified reaction.
/// </summary>
/// <param name="solution">The solution to check.</param>
/// <param name="reaction">The reaction to check.</param>
/// <param name="lowestUnitReactions">How many times this reaction can occur.</param>
/// <returns></returns>
private static bool CanReact(Solution solution, ReactionPrototype reaction, out ReagentUnit lowestUnitReactions)
{
lowestUnitReactions = ReagentUnit.MaxValue;
foreach (var reactantData in reaction.Reactants)
{
var reactantName = reactantData.Key;
var reactantCoefficient = reactantData.Value.Amount;
if (!solution.ContainsReagent(reactantName, out var reactantQuantity))
return false;
var unitReactions = reactantQuantity / reactantCoefficient;
if (unitReactions < lowestUnitReactions)
{
lowestUnitReactions = unitReactions;
}
}
return true;
}
/// <summary>
/// Perform a reaction on a solution. This assumes all reaction criteria are met.
/// Removes the reactants from the solution, then returns a solution with all products.
/// </summary>
private Solution PerformReaction(Solution solution, IEntity owner, ReactionPrototype reaction, ReagentUnit unitReactions)
{
//Remove reactants
foreach (var reactant in reaction.Reactants)
{
if (!reactant.Value.Catalyst)
{
var amountToRemove = unitReactions * reactant.Value.Amount;
solution.RemoveReagent(reactant.Key, amountToRemove);
}
}
//Create products
var products = new Solution();
foreach (var product in reaction.Products)
{
products.AddReagent(product.Key, product.Value * unitReactions);
}
// Trigger reaction effects
OnReaction(reaction, owner, unitReactions);
return products;
}
protected virtual void OnReaction(ReactionPrototype reaction, IEntity owner, ReagentUnit unitReactions)
{
foreach (var effect in reaction.Effects)
{
effect.React(owner, unitReactions.Double());
}
}
/// <summary>
/// Performs all chemical reactions that can be run on a solution.
/// Removes the reactants from the solution, then returns a solution with all products.
/// WARNING: Does not trigger reactions between solution and new products.
/// </summary>
private Solution ProcessReactions(Solution solution, IEntity owner)
{
//TODO: make a hashmap at startup and then look up reagents in the contents for a reaction
var overallProducts = new Solution();
foreach (var reaction in _reactions)
{
if (CanReact(solution, reaction, out var unitReactions))
{
var reactionProducts = PerformReaction(solution, owner, reaction, unitReactions);
overallProducts.AddSolution(reactionProducts);
break;
}
}
return overallProducts;
}
/// <summary>
/// Continually react a solution until no more reactions occur.
/// </summary>
public void FullyReactSolution(Solution solution, IEntity owner)
{
for (var i = 0; i < MaxReactionIterations; i++)
{
var products = ProcessReactions(solution, owner);
if (products.TotalVolume <= 0)
return;
solution.AddSolution(products);
}
Logger.Error($"{nameof(Solution)} on {owner} (Uid: {owner.Uid}) could not finish reacting in under {MaxReactionIterations} loops.");
}
/// <summary>
/// Continually react a solution until no more reactions occur, with a volume constraint.
/// If a reaction's products would exceed the max volume, some product is deleted.
/// </summary>
public void FullyReactSolution(Solution solution, IEntity owner, ReagentUnit maxVolume)
{
for (var i = 0; i < MaxReactionIterations; i++)
{
var products = ProcessReactions(solution, owner);
if (products.TotalVolume <= 0)
return;
var totalVolume = solution.TotalVolume + products.TotalVolume;
var excessVolume = totalVolume - maxVolume;
if (excessVolume > 0)
{
products.RemoveSolution(excessVolume); //excess product is deleted to fit under volume limit
}
solution.AddSolution(products);
}
Logger.Error($"{nameof(Solution)} on {owner} (Uid: {owner.Uid}) could not finish reacting in under {MaxReactionIterations} loops.");
}
}
}

View File

@@ -1,30 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystemMessages;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.EntitySystems
{
public abstract class SharedCombatModeSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<CombatModeSystemMessages.SetCombatModeActiveMessage>(CombatModeActiveHandler);
SubscribeLocalEvent<CombatModeSystemMessages.SetCombatModeActiveMessage>(CombatModeActiveHandler);
}
private void CombatModeActiveHandler(CombatModeSystemMessages.SetCombatModeActiveMessage ev, EntitySessionEventArgs eventArgs)
{
var entity = eventArgs.SenderSession?.AttachedEntity;
if (entity == null || !entity.TryGetComponent(out SharedCombatModeComponent? combatModeComponent))
{
return;
}
combatModeComponent.IsInCombatMode = ev.Active;
}
}
}

View File

@@ -1,77 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.EntitySystems
{
public class SharedConstructionSystem : EntitySystem
{
/// <summary>
/// Sent client -> server to to tell the server that we started building
/// a structure-construction.
/// </summary>
[Serializable, NetSerializable]
public class TryStartStructureConstructionMessage : EntityEventArgs
{
/// <summary>
/// Position to start building.
/// </summary>
public readonly EntityCoordinates Location;
/// <summary>
/// The construction prototype to start building.
/// </summary>
public readonly string PrototypeName;
public readonly Angle Angle;
/// <summary>
/// Identifier to be sent back in the acknowledgement so that the client can clean up its ghost.
/// </summary>
public readonly int Ack;
public TryStartStructureConstructionMessage(EntityCoordinates loc, string prototypeName, Angle angle, int ack)
{
Location = loc;
PrototypeName = prototypeName;
Angle = angle;
Ack = ack;
}
}
/// <summary>
/// Sent client -> server to to tell the server that we started building
/// an item-construction.
/// </summary>
[Serializable, NetSerializable]
public class TryStartItemConstructionMessage : EntityEventArgs
{
/// <summary>
/// The construction prototype to start building.
/// </summary>
public readonly string PrototypeName;
public TryStartItemConstructionMessage(string prototypeName)
{
PrototypeName = prototypeName;
}
}
/// <summary>
/// Send server -> client to tell the client that a ghost has started to be constructed.
/// </summary>
[Serializable, NetSerializable]
public class AckStructureConstructionMessage : EntityEventArgs
{
public readonly int GhostId;
public AckStructureConstructionMessage(int ghostId)
{
GhostId = ghostId;
}
}
}
}

View File

@@ -1,24 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Components.Disposal;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.EntitySystems
{
[UsedImplicitly]
public sealed class SharedDisposalUnitSystem : EntitySystem
{
public override void Update(float frameTime)
{
foreach (var comp in ComponentManager.EntityQuery<SharedDisposalUnitComponent>(true))
{
comp.Update(frameTime);
}
foreach (var comp in ComponentManager.EntityQuery<SharedDisposalMailingUnitComponent>(true))
{
comp.Update(frameTime);
}
}
}
}

View File

@@ -1,23 +0,0 @@
using Content.Shared.GameObjects.Components.Doors;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics.Dynamics;
namespace Content.Shared.GameObjects.EntitySystems
{
public abstract class SharedDoorSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SharedDoorComponent, PreventCollideEvent>(PreventCollision);
}
private void PreventCollision(EntityUid uid, SharedDoorComponent component, PreventCollideEvent args)
{
if (component.IsCrushing(args.BodyB.Owner))
{
args.Cancel();
}
}
}
}

View File

@@ -1,20 +0,0 @@
#nullable enable
using System;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.EntitySystems
{
public abstract class SharedGhostRoleSystem : EntitySystem
{
}
[Serializable, NetSerializable]
public class GhostRole
{
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public EntityUid Id;
}
}

View File

@@ -1,338 +0,0 @@
using System.Linq;
using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Physics;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
namespace Content.Shared.GameObjects.EntitySystems
{
/// <summary>
/// Governs interactions during clicking on entities
/// </summary>
[UsedImplicitly]
public class SharedInteractionSystem : EntitySystem
{
public const float InteractionRange = 2;
public const float InteractionRangeSquared = InteractionRange * InteractionRange;
public delegate bool Ignored(IEntity entity);
/// <summary>
/// Traces a ray from coords to otherCoords and returns the length
/// of the vector between coords and the ray's first hit.
/// </summary>
/// <param name="origin">Set of coordinates to use.</param>
/// <param name="other">Other set of coordinates to use.</param>
/// <param name="collisionMask">the mask to check for collisions</param>
/// <param name="predicate">
/// A predicate to check whether to ignore an entity or not.
/// If it returns true, it will be ignored.
/// </param>
/// <returns>Length of resulting ray.</returns>
public float UnobstructedDistance(
MapCoordinates origin,
MapCoordinates other,
int collisionMask = (int) CollisionGroup.Impassable,
Ignored? predicate = null)
{
var dir = other.Position - origin.Position;
if (dir.LengthSquared.Equals(0f)) return 0f;
predicate ??= _ => false;
var ray = new CollisionRay(origin.Position, dir.Normalized, collisionMask);
var rayResults = Get<SharedBroadPhaseSystem>().IntersectRayWithPredicate(origin.MapId, ray, dir.Length, predicate.Invoke, false).ToList();
if (rayResults.Count == 0) return dir.Length;
return (rayResults[0].HitPos - origin.Position).Length;
}
/// <summary>
/// Traces a ray from coords to otherCoords and returns the length
/// of the vector between coords and the ray's first hit.
/// </summary>
/// <param name="origin">Set of coordinates to use.</param>
/// <param name="other">Other set of coordinates to use.</param>
/// <param name="collisionMask">The mask to check for collisions</param>
/// <param name="ignoredEnt">
/// The entity to be ignored when checking for collisions.
/// </param>
/// <returns>Length of resulting ray.</returns>
public float UnobstructedDistance(
MapCoordinates origin,
MapCoordinates other,
int collisionMask = (int) CollisionGroup.Impassable,
IEntity? ignoredEnt = null)
{
var predicate = ignoredEnt == null
? null
: (Ignored) (e => e == ignoredEnt);
return UnobstructedDistance(origin, other, collisionMask, predicate);
}
/// <summary>
/// Checks that these coordinates are within a certain distance without any
/// entity that matches the collision mask obstructing them.
/// If the <paramref name="range"/> is zero or negative,
/// this method will only check if nothing obstructs the two sets
/// of coordinates.
/// </summary>
/// <param name="origin">Set of coordinates to use.</param>
/// <param name="other">Other set of coordinates to use.</param>
/// <param name="range">
/// Maximum distance between the two sets of coordinates.
/// </param>
/// <param name="collisionMask">The mask to check for collisions.</param>
/// <param name="predicate">
/// A predicate to check whether to ignore an entity or not.
/// If it returns true, it will be ignored.
/// </param>
/// <param name="ignoreInsideBlocker">
/// If true and <see cref="origin"/> or <see cref="other"/> are inside
/// the obstruction, ignores the obstruction and considers the interaction
/// unobstructed.
/// Therefore, setting this to true makes this check more permissive,
/// such as allowing an interaction to occur inside something impassable
/// (like a wall). The default, false, makes the check more restrictive.
/// </param>
/// <returns>
/// True if the two points are within a given range without being obstructed.
/// </returns>
public bool InRangeUnobstructed(
MapCoordinates origin,
MapCoordinates other,
float range = InteractionRange,
CollisionGroup collisionMask = CollisionGroup.Impassable,
Ignored? predicate = null,
bool ignoreInsideBlocker = false)
{
if (!origin.InRange(other, range)) return false;
var dir = other.Position - origin.Position;
if (dir.LengthSquared.Equals(0f)) return true;
if (range > 0f && !(dir.LengthSquared <= range * range)) return false;
predicate ??= _ => false;
var ray = new CollisionRay(origin.Position, dir.Normalized, (int) collisionMask);
var rayResults = Get<SharedBroadPhaseSystem>().IntersectRayWithPredicate(origin.MapId, ray, dir.Length, predicate.Invoke, false).ToList();
if (rayResults.Count == 0) return true;
if (!ignoreInsideBlocker) return false;
foreach (var result in rayResults)
{
if (!result.HitEntity.TryGetComponent(out IPhysBody? p))
{
continue;
}
var bBox = p.GetWorldAABB();
if (bBox.Contains(origin.Position) || bBox.Contains(other.Position))
{
continue;
}
return false;
}
return true;
}
/// <summary>
/// Checks that two entities are within a certain distance without any
/// entity that matches the collision mask obstructing them.
/// If the <paramref name="range"/> is zero or negative,
/// this method will only check if nothing obstructs the two entities.
/// </summary>
/// <param name="origin">The first entity to use.</param>
/// <param name="other">Other entity to use.</param>
/// <param name="range">
/// Maximum distance between the two entities.
/// </param>
/// <param name="collisionMask">The mask to check for collisions.</param>
/// <param name="predicate">
/// A predicate to check whether to ignore an entity or not.
/// If it returns true, it will be ignored.
/// </param>
/// <param name="ignoreInsideBlocker">
/// If true and <see cref="origin"/> or <see cref="other"/> are inside
/// the obstruction, ignores the obstruction and considers the interaction
/// unobstructed.
/// Therefore, setting this to true makes this check more permissive,
/// such as allowing an interaction to occur inside something impassable
/// (like a wall). The default, false, makes the check more restrictive.
/// </param>
/// <param name="popup">
/// Whether or not to popup a feedback message on the origin entity for
/// it to see.
/// </param>
/// <returns>
/// True if the two points are within a given range without being obstructed.
/// </returns>
public bool InRangeUnobstructed(
IEntity origin,
IEntity other,
float range = InteractionRange,
CollisionGroup collisionMask = CollisionGroup.Impassable,
Ignored? predicate = null,
bool ignoreInsideBlocker = false,
bool popup = false)
{
predicate ??= e => e == origin || e == other;
return InRangeUnobstructed(origin, other.Transform.MapPosition, range, collisionMask, predicate, ignoreInsideBlocker, popup);
}
/// <summary>
/// Checks that an entity and a component are within a certain
/// distance without any entity that matches the collision mask
/// obstructing them.
/// If the <paramref name="range"/> is zero or negative,
/// this method will only check if nothing obstructs the entity and component.
/// </summary>
/// <param name="origin">The entity to use.</param>
/// <param name="other">The component to use.</param>
/// <param name="range">
/// Maximum distance between the entity and component.
/// </param>
/// <param name="collisionMask">The mask to check for collisions.</param>
/// <param name="predicate">
/// A predicate to check whether to ignore an entity or not.
/// If it returns true, it will be ignored.
/// </param>
/// <param name="ignoreInsideBlocker">
/// If true and <see cref="origin"/> or <see cref="other"/> are inside
/// the obstruction, ignores the obstruction and considers the interaction
/// unobstructed.
/// Therefore, setting this to true makes this check more permissive,
/// such as allowing an interaction to occur inside something impassable
/// (like a wall). The default, false, makes the check more restrictive.
/// </param>
/// <param name="popup">
/// Whether or not to popup a feedback message on the origin entity for
/// it to see.
/// </param>
/// <returns>
/// True if the two points are within a given range without being obstructed.
/// </returns>
public bool InRangeUnobstructed(
IEntity origin,
IComponent other,
float range = InteractionRange,
CollisionGroup collisionMask = CollisionGroup.Impassable,
Ignored? predicate = null,
bool ignoreInsideBlocker = false,
bool popup = false)
{
return InRangeUnobstructed(origin, other.Owner, range, collisionMask, predicate, ignoreInsideBlocker, popup);
}
/// <summary>
/// Checks that an entity and a set of grid coordinates are within a certain
/// distance without any entity that matches the collision mask
/// obstructing them.
/// If the <paramref name="range"/> is zero or negative,
/// this method will only check if nothing obstructs the entity and component.
/// </summary>
/// <param name="origin">The entity to use.</param>
/// <param name="other">The grid coordinates to use.</param>
/// <param name="range">
/// Maximum distance between the two entity and set of grid coordinates.
/// </param>
/// <param name="collisionMask">The mask to check for collisions.</param>
/// <param name="predicate">
/// A predicate to check whether to ignore an entity or not.
/// If it returns true, it will be ignored.
/// </param>
/// <param name="ignoreInsideBlocker">
/// If true and <see cref="origin"/> or <see cref="other"/> are inside
/// the obstruction, ignores the obstruction and considers the interaction
/// unobstructed.
/// Therefore, setting this to true makes this check more permissive,
/// such as allowing an interaction to occur inside something impassable
/// (like a wall). The default, false, makes the check more restrictive.
/// </param>
/// <param name="popup">
/// Whether or not to popup a feedback message on the origin entity for
/// it to see.
/// </param>
/// <returns>
/// True if the two points are within a given range without being obstructed.
/// </returns>
public bool InRangeUnobstructed(
IEntity origin,
EntityCoordinates other,
float range = InteractionRange,
CollisionGroup collisionMask = CollisionGroup.Impassable,
Ignored? predicate = null,
bool ignoreInsideBlocker = false,
bool popup = false)
{
return InRangeUnobstructed(origin, other.ToMap(EntityManager), range, collisionMask, predicate, ignoreInsideBlocker, popup);
}
/// <summary>
/// Checks that an entity and a set of map coordinates are within a certain
/// distance without any entity that matches the collision mask
/// obstructing them.
/// If the <paramref name="range"/> is zero or negative,
/// this method will only check if nothing obstructs the entity and component.
/// </summary>
/// <param name="origin">The entity to use.</param>
/// <param name="other">The map coordinates to use.</param>
/// <param name="range">
/// Maximum distance between the two entity and set of map coordinates.
/// </param>
/// <param name="collisionMask">The mask to check for collisions.</param>
/// <param name="predicate">
/// A predicate to check whether to ignore an entity or not.
/// If it returns true, it will be ignored.
/// </param>
/// <param name="ignoreInsideBlocker">
/// If true and <see cref="origin"/> or <see cref="other"/> are inside
/// the obstruction, ignores the obstruction and considers the interaction
/// unobstructed.
/// Therefore, setting this to true makes this check more permissive,
/// such as allowing an interaction to occur inside something impassable
/// (like a wall). The default, false, makes the check more restrictive.
/// </param>
/// <param name="popup">
/// Whether or not to popup a feedback message on the origin entity for
/// it to see.
/// </param>
/// <returns>
/// True if the two points are within a given range without being obstructed.
/// </returns>
public bool InRangeUnobstructed(
IEntity origin,
MapCoordinates other,
float range = InteractionRange,
CollisionGroup collisionMask = CollisionGroup.Impassable,
Ignored? predicate = null,
bool ignoreInsideBlocker = false,
bool popup = false)
{
var originPosition = origin.Transform.MapPosition;
predicate ??= e => e == origin;
var inRange = InRangeUnobstructed(originPosition, other, range, collisionMask, predicate, ignoreInsideBlocker);
if (!inRange && popup)
{
var message = Loc.GetString("You can't reach there!");
origin.PopupMessage(message);
}
return inRange;
}
}
}

View File

@@ -1,41 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Components.Movement;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Physics.Dynamics;
namespace Content.Shared.GameObjects.EntitySystems
{
public sealed class SharedMobMoverSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
Get<SharedPhysicsSystem>().KinematicControllerCollision += HandleCollisionMessage;
}
public override void Shutdown()
{
base.Shutdown();
Get<SharedPhysicsSystem>().KinematicControllerCollision -= HandleCollisionMessage;
}
/// <summary>
/// Fake pushing for player collisions.
/// </summary>
private void HandleCollisionMessage(Fixture ourFixture, Fixture otherFixture, float frameTime, Manifold manifold)
{
var otherBody = otherFixture.Body;
if (otherBody.BodyType != BodyType.Dynamic || !otherFixture.Hard) return;
var normal = manifold.LocalNormal;
if (!ourFixture.Body.Owner.TryGetComponent(out IMobMoverComponent? mobMover) || normal == Vector2.Zero) return;
otherBody.ApplyLinearImpulse(-normal * mobMover.PushStrength * frameTime);
}
}
}

View File

@@ -1,139 +0,0 @@
#nullable enable
using System.Diagnostics.CodeAnalysis;
using Content.Shared.GameObjects.Components.Mobs.State;
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.GameObjects.Components.Storage;
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
using Content.Shared.Physics;
using Content.Shared.Physics.Pull;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Players;
namespace Content.Shared.GameObjects.EntitySystems
{
/// <summary>
/// Handles converting inputs into movement.
/// </summary>
public sealed class SharedMoverSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
var moveUpCmdHandler = new MoverDirInputCmdHandler(Direction.North);
var moveLeftCmdHandler = new MoverDirInputCmdHandler(Direction.West);
var moveRightCmdHandler = new MoverDirInputCmdHandler(Direction.East);
var moveDownCmdHandler = new MoverDirInputCmdHandler(Direction.South);
CommandBinds.Builder
.Bind(EngineKeyFunctions.MoveUp, moveUpCmdHandler)
.Bind(EngineKeyFunctions.MoveLeft, moveLeftCmdHandler)
.Bind(EngineKeyFunctions.MoveRight, moveRightCmdHandler)
.Bind(EngineKeyFunctions.MoveDown, moveDownCmdHandler)
.Bind(EngineKeyFunctions.Walk, new WalkInputCmdHandler())
.Register<SharedMoverSystem>();
}
/// <inheritdoc />
public override void Shutdown()
{
CommandBinds.Unregister<SharedMoverSystem>();
base.Shutdown();
}
private static void HandleDirChange(ICommonSession? session, Direction dir, ushort subTick, bool state)
{
if (!TryGetAttachedComponent<IMoverComponent>(session, out var moverComp))
return;
var owner = session?.AttachedEntity;
if (owner != null && session != null)
{
foreach (var comp in owner.GetAllComponents<IRelayMoveInput>())
{
comp.MoveInputPressed(session);
}
// For stuff like "Moving out of locker" or the likes
if (owner.IsInContainer() &&
(!owner.TryGetComponent(out IMobStateComponent? mobState) ||
mobState.IsAlive()))
{
var relayEntityMoveMessage = new RelayMovementEntityMessage(owner);
owner.Transform.Parent!.Owner.SendMessage(owner.Transform, relayEntityMoveMessage);
}
}
moverComp.SetVelocityDirection(dir, subTick, state);
}
private static void HandleRunChange(ICommonSession? session, ushort subTick, bool walking)
{
if (!TryGetAttachedComponent<IMoverComponent>(session, out var moverComp))
{
return;
}
moverComp.SetSprinting(subTick, walking);
}
private static bool TryGetAttachedComponent<T>(ICommonSession? session, [NotNullWhen(true)] out T? component)
where T : class, IComponent
{
component = default;
var ent = session?.AttachedEntity;
if (ent == null || !ent.IsValid())
return false;
if (!ent.TryGetComponent(out T? comp))
return false;
component = comp;
return true;
}
private sealed class MoverDirInputCmdHandler : InputCmdHandler
{
private readonly Direction _dir;
public MoverDirInputCmdHandler(Direction dir)
{
_dir = dir;
}
public override bool HandleCmdMessage(ICommonSession? session, InputCmdMessage message)
{
if (message is not FullInputCmdMessage full)
{
return false;
}
HandleDirChange(session, _dir, message.SubTick, full.State == BoundKeyState.Down);
return false;
}
}
private sealed class WalkInputCmdHandler : InputCmdHandler
{
public override bool HandleCmdMessage(ICommonSession? session, InputCmdMessage message)
{
if (message is not FullInputCmdMessage full)
{
return false;
}
HandleRunChange(session, full.SubTick, full.State == BoundKeyState.Down);
return false;
}
}
}
}

View File

@@ -1,243 +0,0 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Content.Shared.GameObjects.Components.Pulling;
using Content.Shared.GameObjects.Components.Rotatable;
using Content.Shared.GameObjects.EntitySystemMessages.Pulling;
using Content.Shared.GameTicking;
using Content.Shared.Input;
using Content.Shared.Physics.Pull;
using JetBrains.Annotations;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Input.Binding;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Dynamics.Joints;
using Robust.Shared.Players;
namespace Content.Shared.GameObjects.EntitySystems
{
[UsedImplicitly]
public abstract class SharedPullingSystem : EntitySystem, IResettingEntitySystem
{
/// <summary>
/// A mapping of pullers to the entity that they are pulling.
/// </summary>
private readonly Dictionary<IEntity, IEntity> _pullers =
new();
private readonly HashSet<SharedPullableComponent> _moving = new();
private readonly HashSet<SharedPullableComponent> _stoppedMoving = new();
/// <summary>
/// If distance between puller and pulled entity lower that this threshold,
/// pulled entity will not change its rotation.
/// Helps with small distance jittering
/// </summary>
private const float ThresholdRotDistance = 1;
/// <summary>
/// If difference between puller and pulled angle lower that this threshold,
/// pulled entity will not change its rotation.
/// Helps with diagonal movement jittering
/// </summary>
private const float ThresholdRotAngle = 30;
public IReadOnlySet<SharedPullableComponent> Moving => _moving;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PullStartedMessage>(OnPullStarted);
SubscribeLocalEvent<PullStoppedMessage>(OnPullStopped);
SubscribeLocalEvent<MoveEvent>(PullerMoved);
SubscribeLocalEvent<EntInsertedIntoContainerMessage>(HandleContainerInsert);
CommandBinds.Builder
.Bind(ContentKeyFunctions.MovePulledObject, new PointerInputCmdHandler(HandleMovePulledObject))
.Bind(ContentKeyFunctions.ReleasePulledObject, InputCmdHandler.FromDelegate(HandleReleasePulledObject))
.Register<SharedPullingSystem>();
}
public override void Update(float frameTime)
{
base.Update(frameTime);
_moving.ExceptWith(_stoppedMoving);
_stoppedMoving.Clear();
}
public void Reset()
{
_pullers.Clear();
_moving.Clear();
_stoppedMoving.Clear();
}
private void OnPullStarted(PullStartedMessage message)
{
if (_pullers.TryGetValue(message.Puller.Owner, out var pulled) &&
pulled.TryGetComponent(out SharedPullableComponent? pulledComponent))
{
pulledComponent.TryStopPull();
}
SetPuller(message.Puller.Owner, message.Pulled.Owner);
}
private void OnPullStopped(PullStoppedMessage message)
{
RemovePuller(message.Puller.Owner);
}
protected void OnPullableMove(EntityUid uid, SharedPullableComponent component, PullableMoveMessage args)
{
_moving.Add(component);
}
protected void OnPullableStopMove(EntityUid uid, SharedPullableComponent component, PullableStopMovingMessage args)
{
_stoppedMoving.Add(component);
}
private void PullerMoved(MoveEvent ev)
{
var puller = ev.Sender;
if (!TryGetPulled(ev.Sender, out var pulled))
{
return;
}
if (!pulled.TryGetComponent(out IPhysBody? physics))
{
return;
}
UpdatePulledRotation(puller, pulled);
physics.WakeBody();
if (pulled.TryGetComponent(out SharedPullableComponent? pullable))
{
pullable.MovingTo = null;
}
}
// TODO: When Joint networking is less shitcodey fix this to use a dedicated joints message.
private void HandleContainerInsert(EntInsertedIntoContainerMessage message)
{
if (message.Entity.TryGetComponent(out SharedPullableComponent? pullable))
{
pullable.TryStopPull();
}
if (message.Entity.TryGetComponent(out SharedPullerComponent? puller))
{
if (puller.Pulling == null) return;
if (!puller.Pulling.TryGetComponent(out SharedPullableComponent? pulling))
{
return;
}
pulling.TryStopPull();
}
}
private bool HandleMovePulledObject(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
{
var player = session?.AttachedEntity;
if (player == null)
{
return false;
}
if (!TryGetPulled(player, out var pulled))
{
return false;
}
if (!pulled.TryGetComponent(out SharedPullableComponent? pullable))
{
return false;
}
pullable.TryMoveTo(coords.ToMap(EntityManager));
return false;
}
private void HandleReleasePulledObject(ICommonSession? session)
{
var player = session?.AttachedEntity;
if (player == null)
{
return;
}
if (!TryGetPulled(player, out var pulled))
{
return;
}
if (!pulled.TryGetComponent(out SharedPullableComponent? pullable))
{
return;
}
pullable.TryStopPull();
}
private void SetPuller(IEntity puller, IEntity pulled)
{
_pullers[puller] = pulled;
}
private bool RemovePuller(IEntity puller)
{
return _pullers.Remove(puller);
}
public IEntity? GetPulled(IEntity by)
{
return _pullers.GetValueOrDefault(by);
}
public bool TryGetPulled(IEntity by, [NotNullWhen(true)] out IEntity? pulled)
{
return (pulled = GetPulled(by)) != null;
}
public bool IsPulling(IEntity puller)
{
return _pullers.ContainsKey(puller);
}
private void UpdatePulledRotation(IEntity puller, IEntity pulled)
{
// TODO: update once ComponentReference works with directed event bus.
if (!pulled.TryGetComponent(out SharedRotatableComponent? rotatable))
return;
if (!rotatable.RotateWhilePulling)
return;
var dir = puller.Transform.WorldPosition - pulled.Transform.WorldPosition;
if (dir.LengthSquared > ThresholdRotDistance * ThresholdRotDistance)
{
var oldAngle = pulled.Transform.WorldRotation;
var newAngle = Angle.FromWorldVec(dir);
var diff = newAngle - oldAngle;
if (Math.Abs(diff.Degrees) > ThresholdRotAngle)
pulled.Transform.WorldRotation = newAngle;
}
}
}
}

View File

@@ -1,125 +0,0 @@
using Content.Shared.GameObjects.Components;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
namespace Content.Shared.GameObjects.EntitySystems
{
[UsedImplicitly]
public abstract class SharedStackSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SharedStackComponent, ComponentStartup>(OnStackStarted);
SubscribeLocalEvent<SharedStackComponent, StackChangeCountEvent>(OnStackCountChange);
SubscribeLocalEvent<SharedStackComponent, ExaminedEvent>(OnStackExamined);
}
private void OnStackStarted(EntityUid uid, SharedStackComponent component, ComponentStartup args)
{
if (!ComponentManager.TryGetComponent(uid, out SharedAppearanceComponent? appearance))
return;
appearance.SetData(StackVisuals.Actual, component.Count);
appearance.SetData(StackVisuals.MaxCount, component.MaxCount);
appearance.SetData(StackVisuals.Hide, false);
}
protected void OnStackCountChange(EntityUid uid, SharedStackComponent component, StackChangeCountEvent args)
{
if (args.Amount == component.Count)
return;
var old = component.Count;
if (args.Amount > component.MaxCount)
{
args.Amount = component.MaxCount;
args.Clamped = true;
}
if (args.Amount < 0)
{
args.Amount = 0;
args.Clamped = true;
}
component.Count = args.Amount;
component.Dirty();
// Queue delete stack if count reaches zero.
if(component.Count <= 0)
EntityManager.QueueDeleteEntity(uid);
// Change appearance data.
if (ComponentManager.TryGetComponent(uid, out SharedAppearanceComponent? appearance))
appearance.SetData(StackVisuals.Actual, component.Count);
RaiseLocalEvent(uid, new StackCountChangedEvent(old, component.Count));
}
private void OnStackExamined(EntityUid uid, SharedStackComponent component, ExaminedEvent args)
{
if (!args.IsInDetailsRange)
return;
args.Message.AddText("\n");
args.Message.AddMarkup(
Loc.GetString("comp-stack-examine-detail-count",
("count", component.Count),
("markupCountColor", "lightgray")
)
);
}
}
/// <summary>
/// Attempts to change the amount of things in a stack to a specific number.
/// If the amount had to be clamped to zero or the max amount, <see cref="Clamped"/> will be true
/// and the amount will be changed to match the value set.
/// Does nothing if the amount is the same as the stack count already.
/// </summary>
public class StackChangeCountEvent : EntityEventArgs
{
/// <summary>
/// Amount to set the stack to.
/// Input/Output parameter.
/// </summary>
public int Amount { get; set; }
/// <summary>
/// Whether the <see cref="Amount"/> had to be clamped.
/// Output parameter.
/// </summary>
public bool Clamped { get; set; }
public StackChangeCountEvent(int amount)
{
Amount = amount;
}
}
/// <summary>
/// Event raised when a stack's count has changed.
/// </summary>
public class StackCountChangedEvent : EntityEventArgs
{
/// <summary>
/// The old stack count.
/// </summary>
public int OldCount { get; }
/// <summary>
/// The new stack count.
/// </summary>
public int NewCount { get; }
public StackCountChangedEvent(int oldCount, int newCount)
{
OldCount = oldCount;
NewCount = newCount;
}
}
}

View File

@@ -1,51 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.EntitySystems.EffectBlocker;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.EntitySystems
{
public abstract class SharedStandingStateSystem : EntitySystem
{
protected abstract bool OnDown(IEntity entity, bool playSound = true, bool dropItems = true,
bool force = false);
protected abstract bool OnStand(IEntity entity);
/// <summary>
/// Set's the mob standing state to down.
/// </summary>
/// <param name="entity">The mob in question</param>
/// <param name="playSound">Whether to play a sound when falling down or not</param>
/// <param name="dropItems">Whether to make the mob drop all the items on his hands</param>
/// <param name="force">Whether or not to check if the entity can fall.</param>
/// <returns>False if the mob was already downed or couldn't set the state</returns>
public bool Down(IEntity entity, bool playSound = true, bool dropItems = true, bool force = false)
{
if (dropItems)
{
DropAllItemsInHands(entity, false);
}
if (!force && !EffectBlockerSystem.CanFall(entity))
{
return false;
}
return OnDown(entity, playSound, dropItems, force);
}
/// <summary>
/// Sets the mob's standing state to standing.
/// </summary>
/// <param name="entity">The mob in question.</param>
/// <returns>False if the mob was already standing or couldn't set the state</returns>
public bool Standing(IEntity entity)
{
return OnStand(entity);
}
public virtual void DropAllItemsInHands(IEntity entity, bool doMobChecks = true)
{
}
}
}

View File

@@ -1,21 +0,0 @@
#nullable enable
using System.Linq;
using Content.Shared.GameObjects.Components.Movement;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.EntitySystems
{
[UsedImplicitly]
public class SlipperySystem : EntitySystem
{
/// <inheritdoc />
public override void Update(float frameTime)
{
foreach (var slipperyComp in ComponentManager.EntityQuery<SlipperyComponent>().ToArray())
{
slipperyComp.Update();
}
}
}
}

View File

@@ -1,20 +0,0 @@
using Content.Shared.GameObjects.Components.Mobs;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
namespace Content.Shared.GameObjects.EntitySystems
{
[UsedImplicitly]
internal sealed class StunSystem : EntitySystem
{
public override void Update(float frameTime)
{
base.Update(frameTime);
foreach (var component in ComponentManager.EntityQuery<SharedStunnableComponent>(true))
{
component.Update(frameTime);
}
}
}
}

View File

@@ -1,132 +0,0 @@
#nullable enable
using Content.Shared.GameObjects.Components;
using Content.Shared.Maps;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.ViewVariables;
namespace Content.Shared.GameObjects.EntitySystems
{
/// <summary>
/// Entity system backing <see cref="SubFloorHideComponent"/>.
/// </summary>
[UsedImplicitly]
public class SubFloorHideSystem : EntitySystem
{
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
private bool _showAll;
[ViewVariables(VVAccess.ReadWrite)]
public bool ShowAll
{
get => _showAll;
set
{
if (_showAll == value) return;
_showAll = value;
UpdateAll();
}
}
private void UpdateAll()
{
foreach (var comp in ComponentManager.EntityQuery<SubFloorHideComponent>(true))
{
var transform = comp.Owner.Transform;
if (!_mapManager.TryGetGrid(transform.GridID, out var grid)) return;
UpdateTile(grid, grid.TileIndicesFor(transform.Coordinates));
}
}
public override void Initialize()
{
base.Initialize();
IoCManager.InjectDependencies(this);
_mapManager.GridChanged += MapManagerOnGridChanged;
_mapManager.TileChanged += MapManagerOnTileChanged;
SubscribeLocalEvent<SubFloorHideComponent, ComponentStartup>(OnSubFloorStarted);
SubscribeLocalEvent<SubFloorHideComponent, ComponentShutdown>(OnSubFloorTerminating);
SubscribeLocalEvent<SubFloorHideComponent, SnapGridPositionChangedEvent>(OnSnapGridPositionChanged);
}
public override void Shutdown()
{
base.Shutdown();
_mapManager.GridChanged -= MapManagerOnGridChanged;
_mapManager.TileChanged -= MapManagerOnTileChanged;
}
private void OnSubFloorStarted(EntityUid uid, SubFloorHideComponent component, ComponentStartup _)
{
UpdateEntity(uid);
}
private void OnSubFloorTerminating(EntityUid uid, SubFloorHideComponent component, ComponentShutdown _)
{
UpdateEntity(uid);
}
private void OnSnapGridPositionChanged(EntityUid uid, SubFloorHideComponent component, SnapGridPositionChangedEvent ev)
{
// We do this directly instead of calling UpdateEntity.
if(_mapManager.TryGetGrid(ev.NewGrid, out var grid))
UpdateTile(grid, ev.Position);
}
private void MapManagerOnTileChanged(object? sender, TileChangedEventArgs e)
{
UpdateTile(_mapManager.GetGrid(e.NewTile.GridIndex), e.NewTile.GridIndices);
}
private void MapManagerOnGridChanged(object? sender, GridChangedEventArgs e)
{
foreach (var modified in e.Modified)
{
UpdateTile(e.Grid, modified.position);
}
}
private void UpdateEntity(EntityUid uid)
{
if (!ComponentManager.TryGetComponent(uid, out ITransformComponent? transform) ||
!_mapManager.TryGetGrid(transform.GridID, out var grid)) return;
UpdateTile(grid, grid.WorldToTile(transform.WorldPosition));
}
private void UpdateTile(IMapGrid grid, Vector2i position)
{
var tile = grid.GetTileRef(position);
var tileDef = (ContentTileDefinition) _tileDefinitionManager[tile.Tile.TypeId];
foreach (var anchored in grid.GetAnchoredEntities(position))
{
if (!ComponentManager.TryGetComponent(anchored, out SubFloorHideComponent? subFloorComponent))
{
continue;
}
// Show sprite
if (ComponentManager.TryGetComponent(anchored, out SharedSpriteComponent ? spriteComponent))
{
spriteComponent.Visible = ShowAll || !subFloorComponent.Running || tileDef.IsSubFloor;
}
// So for collision all we care about is that the component is running.
if (ComponentManager.TryGetComponent(anchored, out PhysicsComponent ? physicsComponent))
{
physicsComponent.CanCollide = !subFloorComponent.Running;
}
}
}
}
}

View File

@@ -1,166 +0,0 @@
#nullable enable
using System.Collections.Generic;
using System.Linq;
using Content.Shared.GameObjects.Components.Items;
using Content.Shared.Physics.Pull;
using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Physics;
using Robust.Shared.GameObjects;
using Robust.Shared.Log;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Dynamics;
namespace Content.Shared.GameObjects.EntitySystems
{
/// <summary>
/// Handles throwing landing and collisions.
/// </summary>
public class ThrownItemSystem : EntitySystem
{
private List<IThrowCollide> _throwCollide = new();
private const string ThrowingFixture = "throw-fixture";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ThrownItemComponent, PhysicsSleepMessage>(HandleSleep);
SubscribeLocalEvent<ThrownItemComponent, StartCollideEvent>(HandleCollision);
SubscribeLocalEvent<ThrownItemComponent, PreventCollideEvent>(PreventCollision);
SubscribeLocalEvent<ThrownItemComponent, ThrownEvent>(ThrowItem);
SubscribeLocalEvent<ThrownItemComponent, LandEvent>(LandItem);
SubscribeLocalEvent<PullStartedMessage>(HandlePullStarted);
}
private void LandItem(EntityUid uid, ThrownItemComponent component, LandEvent args)
{
if (!component.Owner.TryGetComponent(out PhysicsComponent? physicsComponent)) return;
var fixture = physicsComponent.GetFixture(ThrowingFixture);
if (fixture == null)
{
Logger.Error($"Tried to remove throwing fixture for {component.Owner} but none found?");
return;
}
physicsComponent.RemoveFixture(fixture);
}
private void ThrowItem(EntityUid uid, ThrownItemComponent component, ThrownEvent args)
{
if (!component.Owner.TryGetComponent(out PhysicsComponent? physicsComponent) ||
physicsComponent.Fixtures.Count != 1) return;
if (physicsComponent.GetFixture(ThrowingFixture) != null)
{
Logger.Error($"Found existing throwing fixture on {component.Owner}");
return;
}
var shape = physicsComponent.Fixtures[0].Shape;
var fixture = new Fixture(physicsComponent, shape) {CollisionLayer = (int) CollisionGroup.ThrownItem, Hard = false, ID = ThrowingFixture};
physicsComponent.AddFixture(fixture);
}
private void HandleCollision(EntityUid uid, ThrownItemComponent component, StartCollideEvent args)
{
var thrower = component.Thrower;
var otherBody = args.OtherFixture.Body;
if (otherBody.Owner == thrower) return;
ThrowCollideInteraction(thrower, args.OurFixture.Body, otherBody);
}
private void PreventCollision(EntityUid uid, ThrownItemComponent component, PreventCollideEvent args)
{
if (args.BodyB.Owner == component.Thrower)
{
args.Cancel();
}
}
private void HandleSleep(EntityUid uid, ThrownItemComponent thrownItem, PhysicsSleepMessage message)
{
LandComponent(thrownItem);
}
private void HandlePullStarted(PullStartedMessage message)
{
// TODO: this isn't directed so things have to be done the bad way
if (message.Pulled.Owner.TryGetComponent(out ThrownItemComponent? thrownItem))
LandComponent(thrownItem);
}
private void LandComponent(ThrownItemComponent thrownItem)
{
if (thrownItem.Owner.Deleted) return;
var user = thrownItem.Thrower;
var landing = thrownItem.Owner;
var coordinates = landing.Transform.Coordinates;
// LandInteraction
// TODO: Refactor these to system messages
var landMsg = new LandEvent(user, landing, coordinates);
RaiseLocalEvent(landMsg);
if (landMsg.Handled)
{
return;
}
var comps = landing.GetAllComponents<ILand>().ToArray();
var landArgs = new LandEventArgs(user, coordinates);
// Call Land on all components that implement the interface
foreach (var comp in comps)
{
if (landing.Deleted) break;
comp.Land(landArgs);
}
ComponentManager.RemoveComponent(landing.Uid, thrownItem);
}
/// <summary>
/// Calls ThrowCollide on all components that implement the IThrowCollide interface
/// on a thrown entity and the target entity it hit.
/// </summary>
public void ThrowCollideInteraction(IEntity? user, IPhysBody thrown, IPhysBody target)
{
// TODO: Just pass in the bodies directly
var collideMsg = new ThrowCollideEvent(user, thrown.Owner, target.Owner);
RaiseLocalEvent(collideMsg);
if (collideMsg.Handled)
{
return;
}
var eventArgs = new ThrowCollideEventArgs(user, thrown.Owner, target.Owner);
foreach (var comp in target.Owner.GetAllComponents<IThrowCollide>())
{
_throwCollide.Add(comp);
}
foreach (var collide in _throwCollide)
{
if (target.Owner.Deleted) break;
collide.HitBy(eventArgs);
}
_throwCollide.Clear();
foreach (var comp in thrown.Owner.GetAllComponents<IThrowCollide>())
{
_throwCollide.Add(comp);
}
foreach (var collide in _throwCollide)
{
if (thrown.Owner.Deleted) break;
collide.DoHit(eventArgs);
}
_throwCollide.Clear();
}
}
}