using System.Linq; using Content.Server.Atmos.Components; using Content.Shared.Inventory; using Content.Shared.FootPrint; using Content.Shared.Standing; using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Forensics; using Robust.Shared.GameStates; using Robust.Shared.Map; using Robust.Shared.Physics.Components; using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.Server.FootPrint; public sealed class FootPrintsSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _protoMan = default!; [Dependency] private readonly IMapManager _map = default!; // WD EDIT [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly SharedSolutionContainerSystem _solution = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; private EntityQuery _appearanceQuery; private EntityQuery _standingStateQuery; public override void Initialize() { base.Initialize(); _appearanceQuery = GetEntityQuery(); _standingStateQuery = GetEntityQuery(); SubscribeLocalEvent(OnStartupComponent); SubscribeLocalEvent(OnMove); SubscribeLocalEvent(OnGetState); } private void OnGetState(Entity ent, ref ComponentGetState args) { args.State = new FootPrintState(TerminatingOrDeleted(ent.Comp.PrintOwner) ? NetEntity.Invalid : GetNetEntity(ent.Comp.PrintOwner)); } private void OnStartupComponent(EntityUid uid, FootPrintsComponent component, ComponentStartup args) { component.StepSize = Math.Max(0f, component.StepSize + _random.NextFloat(-0.05f, 0.05f)); } private void OnMove(EntityUid uid, FootPrintsComponent component, ref MoveEvent args) { if (TerminatingOrDeleted(uid) || component.ContainedSolution.Volume <= 0 || TryComp(uid, out var physics) && physics.BodyStatus != BodyStatus.OnGround || !_map.TryFindGridAt(_transform.GetMapCoordinates((uid, args.Component)), out var gridUid, out _)) // WD EDIT return; var newPos = _transform.ToMapCoordinates(args.NewPosition).Position; var dragging = _standingStateQuery.TryComp(uid, out var standing) && standing.CurrentState == StandingState.Lying; var distance = (newPos - component.LastStepPos).Length(); var stepSize = dragging ? component.DragSize : component.StepSize; if (distance < stepSize) return; // are we on a puddle? we exit, ideally we would exchange liquid and DNA with the puddle but meh, too lazy to do that now. var entities = _lookup.GetEntitiesIntersecting(uid, LookupFlags.All); if (entities.Any(HasComp)) return; // Spawn the footprint var footprintUid = Spawn(component.StepProtoId, CalcCoords(gridUid, component, args.Component, dragging)); var stepTransform = Transform(footprintUid); var footPrintComponent = EnsureComp(footprintUid); // transfer owner DNA into the footsteps var forensics = EntityManager.EnsureComponent(footprintUid); if (TryComp(uid, out var ownerForensics)) forensics.DNAs.UnionWith(ownerForensics.DNAs); footPrintComponent.PrintOwner = uid; Dirty(footprintUid, footPrintComponent); if (_appearanceQuery.TryComp(footprintUid, out var appearance)) { var color = component.ContainedSolution.GetColor(_protoMan); color.A = Math.Max(0.3f, component.ContainedSolution.FillFraction); _appearance.SetData(footprintUid, FootPrintVisualState.State, PickState(uid, dragging), appearance); _appearance.SetData(footprintUid, FootPrintVisualState.Color, color, appearance); } stepTransform.LocalRotation = dragging ? (newPos - component.LastStepPos).ToAngle() + Angle.FromDegrees(-90f) : args.Component.LocalRotation + Math.PI; if (!TryComp(footprintUid, out var solutionContainer) || !_solution.ResolveSolution((footprintUid, solutionContainer), footPrintComponent.SolutionName, ref footPrintComponent.Solution, out var solution)) return; // Transfer from the component to the footprint var removedReagents = component.ContainedSolution.SplitSolution(component.FootprintVolume); _solution.ForceAddSolution(footPrintComponent.Solution.Value, removedReagents); component.RightStep = !component.RightStep; component.LastStepPos = newPos; } private EntityCoordinates CalcCoords(EntityUid uid, FootPrintsComponent component, TransformComponent transform, bool state) { if (state) return new(uid, transform.LocalPosition); var offset = component.RightStep ? new Angle(Math.PI + transform.LocalRotation).RotateVec(component.OffsetPrint) : new Angle(transform.LocalRotation).RotateVec(component.OffsetPrint); return new(uid, transform.LocalPosition + offset); } private FootPrintVisuals PickState(EntityUid uid, bool dragging) { var state = FootPrintVisuals.BareFootPrint; if (_inventory.TryGetSlotEntity(uid, "shoes", out _)) state = FootPrintVisuals.ShoesPrint; if (_inventory.TryGetSlotEntity(uid, "outerClothing", out var suit) && TryComp(suit, out _)) state = FootPrintVisuals.SuitPrint; if (dragging) state = FootPrintVisuals.Dragging; return state; } }