Skip to content

Commit

Permalink
Footprints Performance Updates (#1439)
Browse files Browse the repository at this point in the history
# Description

This is a port of Fansana/floofstation1#445
Except I despise that this wasn't upstreamed so much that I didn't even
cherrypick it.

# Changelog

:cl:
- add: Footprints are now cleaned in a small radius around the mouse
cursor when mopping them.
  • Loading branch information
VMSolidus authored Jan 5, 2025
1 parent 136be4e commit 11678af
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 108 deletions.
41 changes: 41 additions & 0 deletions Content.Server/Fluids/EntitySystems/AbsorbentSystem.Footprints.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System.Linq;
using Content.Shared.Chemistry.Components;
using Content.Shared.Fluids;
using Content.Shared.FootPrint;

namespace Content.Server.Fluids.EntitySystems;

public sealed partial class AbsorbentSystem
{
[Dependency] private readonly EntityLookupSystem _lookup = default!;

/// <summary>
/// Tries to clean a number of footprints in a range determined by the component. Returns the number of cleaned footprints.
/// </summary>
private int TryCleanNearbyFootprints(EntityUid user, EntityUid used, Entity<AbsorbentComponent> target, Entity<SolutionComponent> absorbentSoln)
{
var footprintQuery = GetEntityQuery<FootPrintComponent>();
var targetCoords = Transform(target).Coordinates;
var entities = _lookup.GetEntitiesInRange<FootPrintComponent>(targetCoords, target.Comp.FootprintCleaningRange, LookupFlags.Uncontained);

// Take up to [MaxCleanedFootprints] footprints closest to the target
var cleaned = entities.AsEnumerable()
.Select(uid => (uid, dst: Transform(uid).Coordinates.TryDistance(EntityManager, _transform, targetCoords, out var dst) ? dst : 0f))
.Where(ent => ent.dst > 0f)
.OrderBy(ent => ent.dst)
.Select(ent => (ent.uid, comp: footprintQuery.GetComponent(ent.uid)));

// And try to interact with each one of them, ignoring useDelay
var processed = 0;
foreach (var (uid, footprintComp) in cleaned)
{
if (TryPuddleInteract(user, used, uid, target.Comp, useDelay: null, absorbentSoln))
processed++;

if (processed >= target.Comp.MaxCleanedFootprints)
break;
}

return processed;
}
}
7 changes: 4 additions & 3 deletions Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Numerics;
using Content.Server.Chemistry.Containers.EntitySystems;
using Content.Server.Popups;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
Expand All @@ -18,15 +17,15 @@
namespace Content.Server.Fluids.EntitySystems;

/// <inheritdoc/>
public sealed class AbsorbentSystem : SharedAbsorbentSystem
public sealed partial class AbsorbentSystem : SharedAbsorbentSystem
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly PopupSystem _popups = default!;
[Dependency] private readonly PuddleSystem _puddleSystem = default!;
[Dependency] private readonly SharedMeleeWeaponSystem _melee = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly UseDelaySystem _useDelay = default!;
[Dependency] private readonly MapSystem _mapSystem = default!;

Expand Down Expand Up @@ -119,6 +118,8 @@ public void Mop(EntityUid user, EntityUid target, EntityUid used, AbsorbentCompo
if (!TryRefillableInteract(user, used, target, component, useDelay, absorberSoln.Value))
return;
}

TryCleanNearbyFootprints(user, used, (target, component), absorberSoln.Value);
}

/// <summary>
Expand Down
88 changes: 49 additions & 39 deletions Content.Server/FootPrint/FootPrintsSystem.cs
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
using System.Linq;
using Content.Server.Atmos.Components;
using Content.Shared.Inventory;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
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.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 InventorySystem _inventory = default!;
[Dependency] private readonly IMapManager _map = default!;
[Dependency] private readonly IPrototypeManager _protoMan = default!;

[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<TransformComponent> _transformQuery;
private EntityQuery<MobThresholdsComponent> _mobThresholdQuery;
private EntityQuery<AppearanceComponent> _appearanceQuery;
private EntityQuery<LayingDownComponent> _layingQuery;
private EntityQuery<StandingStateComponent> _standingStateQuery;

public override void Initialize()
{
base.Initialize();

_transformQuery = GetEntityQuery<TransformComponent>();
_mobThresholdQuery = GetEntityQuery<MobThresholdsComponent>();
_appearanceQuery = GetEntityQuery<AppearanceComponent>();
_layingQuery = GetEntityQuery<LayingDownComponent>();
_standingStateQuery = GetEntityQuery<StandingStateComponent>();

SubscribeLocalEvent<FootPrintsComponent, ComponentStartup>(OnStartupComponent);
SubscribeLocalEvent<FootPrintsComponent, MoveEvent>(OnMove);
Expand All @@ -46,62 +46,72 @@ private void OnStartupComponent(EntityUid uid, FootPrintsComponent component, Co

private void OnMove(EntityUid uid, FootPrintsComponent component, ref MoveEvent args)
{
if (component.PrintsColor.A <= 0f
|| !_transformQuery.TryComp(uid, out var transform)
|| !_mobThresholdQuery.TryComp(uid, out var mobThreshHolds)
|| !_map.TryFindGridAt(_transform.GetMapCoordinates((uid, transform)), out var gridUid, out _))
if (component.ContainedSolution.Volume <= 0
|| TryComp<PhysicsComponent>(uid, out var physics) && physics.BodyStatus != BodyStatus.OnGround
|| args.Entity.Comp1.GridUid is not {} gridUid)
return;

var dragging = mobThreshHolds.CurrentThresholdState is MobState.Critical or MobState.Dead
|| _layingQuery.TryComp(uid, out var laying) && laying.IsCrawlingUnder;
var distance = (transform.LocalPosition - component.StepPos).Length();
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))
if (distance < stepSize)
return;

component.RightStep = !component.RightStep;
// 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<PuddleFootPrintsComponent>))
return;

// Spawn the footprint
var footprintUid = Spawn(component.StepProtoId, CalcCoords(gridUid, component, args.Component, dragging));
var stepTransform = Transform(footprintUid);
var footPrintComponent = EnsureComp<FootPrintComponent>(footprintUid);

var entity = Spawn(component.StepProtoId, CalcCoords(gridUid, component, transform, dragging));
var footPrintComponent = EnsureComp<FootPrintComponent>(entity);
// transfer owner DNA into the footsteps
var forensics = EntityManager.EnsureComponent<ForensicsComponent>(footprintUid);
if (TryComp<ForensicsComponent>(uid, out var ownerForensics))
forensics.DNAs.UnionWith(ownerForensics.DNAs);

footPrintComponent.PrintOwner = uid;
Dirty(entity, footPrintComponent);
Dirty(footprintUid, footPrintComponent);

if (_appearanceQuery.TryComp(entity, out var appearance))
if (_appearanceQuery.TryComp(footprintUid, out var appearance))
{
_appearance.SetData(entity, FootPrintVisualState.State, PickState(uid, dragging), appearance);
_appearance.SetData(entity, FootPrintVisualState.Color, component.PrintsColor, appearance);
}
var color = component.ContainedSolution.GetColor(_protoMan);
color.A = Math.Max(0.3f, component.ContainedSolution.FillFraction);

if (!_transformQuery.TryComp(entity, out var stepTransform))
return;
_appearance.SetData(footprintUid, FootPrintVisualState.State, PickState(uid, dragging), appearance);
_appearance.SetData(footprintUid, FootPrintVisualState.Color, color, appearance);
}

stepTransform.LocalRotation = dragging
? (transform.LocalPosition - component.StepPos).ToAngle() + Angle.FromDegrees(-90f)
: transform.LocalRotation + Angle.FromDegrees(180f);
? (newPos - component.LastStepPos).ToAngle() + Angle.FromDegrees(-90f)
: args.Component.LocalRotation + Angle.FromDegrees(180f);

component.PrintsColor = component.PrintsColor.WithAlpha(Math.Max(0f, component.PrintsColor.A - component.ColorReduceAlpha));
component.StepPos = transform.LocalPosition;

if (!TryComp<SolutionContainerManagerComponent>(entity, out var solutionContainer)
|| !_solution.ResolveSolution((entity, solutionContainer), footPrintComponent.SolutionName, ref footPrintComponent.Solution, out var solution)
|| string.IsNullOrWhiteSpace(component.ReagentToTransfer) || solution.Volume >= 1)
if (!TryComp<SolutionContainerManagerComponent>(footprintUid, out var solutionContainer)
|| !_solution.ResolveSolution((footprintUid, solutionContainer), footPrintComponent.SolutionName, ref footPrintComponent.Solution, out var solution))
return;

_solution.TryAddReagent(footPrintComponent.Solution.Value, component.ReagentToTransfer, 1, out _);
// 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 EntityCoordinates(uid, transform.LocalPosition);
return new(uid, transform.LocalPosition);

var offset = component.RightStep
? new Angle(Angle.FromDegrees(180f) + transform.LocalRotation).RotateVec(component.OffsetPrint)
: new Angle(transform.LocalRotation).RotateVec(component.OffsetPrint);

return new EntityCoordinates(uid, transform.LocalPosition + offset);
return new(uid, transform.LocalPosition + offset);
}

private FootPrintVisuals PickState(EntityUid uid, bool dragging)
Expand Down
48 changes: 22 additions & 26 deletions Content.Server/FootPrint/PuddleFootPrintsSystem.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
using System.Linq;
using Content.Shared.FootPrint;
using Content.Shared.Chemistry.Components.SolutionManager;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Fluids;
using Content.Shared.FixedPoint;
using Content.Shared.Fluids.Components;
using Content.Shared.Forensics;
using Robust.Shared.Physics.Events;
using Robust.Shared.Prototypes;


namespace Content.Server.FootPrint;

public sealed class PuddleFootPrintsSystem : EntitySystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;

public override void Initialize()
Expand All @@ -21,32 +22,27 @@ public override void Initialize()

private void OnStepTrigger(EntityUid uid, PuddleFootPrintsComponent component, ref EndCollideEvent args)
{
if (!TryComp<AppearanceComponent>(uid, out var appearance)
|| !TryComp<PuddleComponent>(uid, out var puddle)
|| !TryComp<FootPrintsComponent>(args.OtherEntity, out var tripper)
|| !TryComp<SolutionContainerManagerComponent>(uid, out var solutionManager)
|| !_solutionContainer.ResolveSolution((uid, solutionManager), puddle.SolutionName, ref puddle.Solution, out var solutions))
return;

var totalSolutionQuantity = solutions.Contents.Sum(sol => (float) sol.Quantity);
var waterQuantity = (from sol in solutions.Contents where sol.Reagent.Prototype == "Water" select (float) sol.Quantity).FirstOrDefault();

if (waterQuantity / (totalSolutionQuantity / 100f) > component.OffPercent || solutions.Contents.Count <= 0)
if (!TryComp<PuddleComponent>(uid, out var puddle) || !TryComp<FootPrintsComponent>(args.OtherEntity, out var tripper))
return;

tripper.ReagentToTransfer =
solutions.Contents.Aggregate((l, r) => l.Quantity > r.Quantity ? l : r).Reagent.Prototype;
// Transfer DNAs from the puddle to the tripper
if (TryComp<ForensicsComponent>(uid, out var puddleForensics))
{
tripper.DNAs.UnionWith(puddleForensics.DNAs);
if(TryComp<ForensicsComponent>(args.OtherEntity, out var tripperForensics))
tripperForensics.DNAs.UnionWith(puddleForensics.DNAs);
}

if (_appearance.TryGetData(uid, PuddleVisuals.SolutionColor, out var color, appearance)
&& _appearance.TryGetData(uid, PuddleVisuals.CurrentVolume, out var volume, appearance))
AddColor((Color) color, (float) volume * component.SizeRatio, tripper);
// Transfer reagents from the puddle to the tripper.
// Ideally it should be a two-way process, but that is too hard to simulate and will have very little effect outside of potassium-water spills.
var quantity = puddle.Solution?.Comp?.Solution?.Volume ?? 0;
var footprintsCapacity = tripper.ContainedSolution.AvailableVolume;

_solutionContainer.RemoveEachReagent(puddle.Solution.Value, 1);
}
if (quantity <= 0 || footprintsCapacity <= 0)
return;

private void AddColor(Color col, float quantity, FootPrintsComponent component)
{
component.PrintsColor = component.ColorQuantity == 0f ? col : Color.InterpolateBetween(component.PrintsColor, col, component.ColorInterpolationFactor);
component.ColorQuantity += quantity;
var transferAmount = FixedPoint2.Min(footprintsCapacity, quantity * component.SizeRatio);
var transferred = _solutionContainer.SplitSolution(puddle.Solution!.Value, transferAmount);
tripper.ContainedSolution.AddSolution(transferred, _protoMan);
}
}
9 changes: 9 additions & 0 deletions Content.Shared/Fluids/AbsorbentComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,13 @@ public sealed partial class AbsorbentComponent : Component
{
Params = AudioParams.Default.WithVariation(SharedContentAudioSystem.DefaultVariation).WithVolume(-3f),
};

[DataField]
public float FootprintCleaningRange = 0.2f;

/// <summary>
/// How many footprints within <see cref="FootprintCleaningRange"/> can be cleaned at once.
/// </summary>
[DataField]
public int MaxCleanedFootprints = 5;
}
Loading

0 comments on commit 11678af

Please sign in to comment.