Files
Delta-v/Content.Server/_DV/Footprints/Systems/FootPrintsSystem.cs
T
DisposableCrewmember42andpre-commit-ci[bot] 378cceb44f feat: Make footprints decals (#5226)
* feat(wip): footprints now decals instead of entities

* fix: apply proper offset to footprint decals

* refactor: basedecaloffset to readonly

* feat: add back dragging smears, also resolve conflicts

* refactor: rename dragging to beingDragged for clarity

* docs: properly document draggingdecals

* refactor: remove unused reagent code

* refactor: move under _DV

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* feat(wip): mops now able to remove decals

# Conflicts:
#	Content.Server/_DV/Footprints/Systems/PuddleFootPrintsSystem.cs

* fix: reset ColorQuantity if transparent prints

* feat: implement scrubbing

* refactor: remove unused import, ioc dependency

* feat: remove 5u water when scrubbing

* fix: remove unused attributes from protos

* docs: add summary to puddlefootprintscomp

* fix: colliding entity checked for flight, not the puddle

also minor refactor: renamed local variables/parameters of OnEndCollide for clarity

* style: remove newlines, redundant parens

* test: ensure FootPrints stepSize/dragSize are positive

* docs: add prediction todo

* feat: add bare footprints, restore sprite assignments

---------

Signed-off-by: DisposableCrewmember42 <disposablecrewmember42@proton.me>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2026-03-26 14:44:06 +01:00

125 lines
4.9 KiB
C#

using System.Numerics;
using Content.Server._DV.Footprints.Components;
using Content.Server.Decals;
using Content.Shared._EE.Flight;
using Content.Shared.Inventory;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Standing;
using Robust.Shared.Map;
using Robust.Shared.Random;
namespace Content.Server._DV.Footprints.Systems;
// TODO: If upstream ever adds prediction to SharedDecalSystem, move this to Shared and implement footprint prediction
public sealed partial class FootPrintsSystem : EntitySystem
{
[Dependency] private readonly IMapManager _map = default!;
[Dependency] private readonly DecalSystem _decalSystem = default!;
[Dependency] private readonly SharedFlightSystem _flight = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly StandingStateSystem _standing = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
private EntityQuery<FlightComponent> _flightQuery;
private EntityQuery<TransformComponent> _transformQuery;
private EntityQuery<StandingStateComponent> _standingQuery;
private EntityQuery<MobThresholdsComponent> _mobThresholdQuery;
// CrayonSystem uses a hardcoded offset of [-0.5, -0.5] for clicks
private static readonly Vector2 BaseDecalOffset = new(-0.5f, -0.5f);
public override void Initialize()
{
base.Initialize();
_flightQuery = GetEntityQuery<FlightComponent>();
_transformQuery = GetEntityQuery<TransformComponent>();
_standingQuery = GetEntityQuery<StandingStateComponent>();
_mobThresholdQuery = GetEntityQuery<MobThresholdsComponent>();
SubscribeLocalEvent<FootPrintsComponent, MoveEvent>(OnMove);
}
private void OnMove(Entity<FootPrintsComponent> ent, ref MoveEvent args)
{
if (ent.Comp.PrintsColor.A <= 0f)
{
ent.Comp.ColorQuantity = 0;
return;
}
if (_flightQuery.TryComp(ent, out var flight) && _flight.IsFlying((ent, flight)))
return;
if (!_transformQuery.TryComp(ent, out var transform))
return;
if (!_mobThresholdQuery.TryComp(ent, out var mobThresholds))
return;
var isCrit = mobThresholds.CurrentThresholdState is MobState.Critical or MobState.Dead;
var isDowned = isCrit || _standingQuery.TryComp(ent, out var standingComp) && _standing.IsDown((ent, standingComp));
var distance = (transform.LocalPosition - ent.Comp.LastPrintPosition).Length();
var stepSize = isDowned ? ent.Comp.DragSize : ent.Comp.StepSize;
if (distance <= stepSize)
return;
// Need to be on a grid to leave footprints
if (!_map.TryFindGridAt(_transform.GetMapCoordinates((ent, transform)), out var gridUid, out _))
return;
// Calculate spawn coordinates
var coords = CalcCoords(gridUid, ent.Comp, transform, isDowned);
var angle = isDowned
? (transform.LocalPosition - ent.Comp.LastPrintPosition).ToAngle() + Angle.FromDegrees(-90f)
: transform.LocalRotation + Angle.FromDegrees(180f);
string decal;
if (isDowned)
{
decal = _random.Pick(ent.Comp.DraggingDecals);
}
else if (!_inventory.GetSlotEnumerator(ent.Owner, SlotFlags.FEET).NextItem(out var item) // This assumes there can only be 1 feet slot.
|| item == EntityUid.Invalid)
{
decal = ent.Comp.RightStep ? ent.Comp.BareDecalRight : ent.Comp.BareDecalLeft;
}
else
{
decal = ent.Comp.RightStep ? ent.Comp.BootDecalRight : ent.Comp.BootDecalLeft;
}
if (!_decalSystem.TryAddDecal(decal, coords, out _, color: ent.Comp.PrintsColor, rotation: angle, cleanable: true))
return;
ent.Comp.LastPrintPosition = transform.LocalPosition;
// Alternate feet
ent.Comp.RightStep = !ent.Comp.RightStep;
ent.Comp.PrintsColor = ent.Comp.PrintsColor.WithAlpha(
Math.Max(0f, ent.Comp.PrintsColor.A - ent.Comp.ColorReduceAlpha));
}
private EntityCoordinates CalcCoords(EntityUid gridUid,
FootPrintsComponent footPrints,
TransformComponent transform,
bool dragging)
{
// For dragging, place footprint at center
if (dragging)
return new EntityCoordinates(gridUid, transform.LocalPosition + BaseDecalOffset);
// For walking, offset left or right based on which foot
var footOffset = footPrints.RightStep
? new Angle(Angle.FromDegrees(180f) + transform.LocalRotation).RotateVec(footPrints.OffsetPrint)
: new Angle(transform.LocalRotation).RotateVec(footPrints.OffsetPrint);
return new EntityCoordinates(gridUid, transform.LocalPosition + BaseDecalOffset + footOffset);
}
}