BaseEntityReplaceVariationPassSystem.cs 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. using Content.Server.GameTicking.Rules.VariationPass.Components;
  2. using Content.Shared.Storage;
  3. using Robust.Shared.Map;
  4. using Robust.Shared.Random;
  5. using Robust.Shared.Timing;
  6. namespace Content.Server.GameTicking.Rules.VariationPass;
  7. /// <inheritdoc cref="EntityReplaceVariationPassComponent"/>
  8. /// <summary>
  9. /// A base system for fast replacement of entities utilizing a query, rather than having to iterate every entity
  10. /// To use, you must have a marker component to use for <see cref="TEntComp"/>--each replaceable entity must have it
  11. /// Then you need an inheriting system as well as a unique game rule component for <see cref="TGameRuleComp"/>
  12. ///
  13. /// This means a bit more boilerplate for each one, but significantly faster to actually execute.
  14. /// See <see cref="WallReplaceVariationPassSystem"/>
  15. /// </summary>
  16. public abstract class BaseEntityReplaceVariationPassSystem<TEntComp, TGameRuleComp> : VariationPassSystem<TGameRuleComp>
  17. where TEntComp: IComponent
  18. where TGameRuleComp: IComponent
  19. {
  20. /// <summary>
  21. /// Used so we don't modify while enumerating
  22. /// if the replaced entity also has <see cref="TEntComp"/>.
  23. ///
  24. /// Filled and cleared within the same tick so no persistence issues.
  25. /// </summary>
  26. private readonly Queue<(string, EntityCoordinates, Angle)> _queuedSpawns = new();
  27. protected override void ApplyVariation(Entity<TGameRuleComp> ent, ref StationVariationPassEvent args)
  28. {
  29. if (!TryComp<EntityReplaceVariationPassComponent>(ent, out var pass))
  30. return;
  31. var stopwatch = new Stopwatch();
  32. stopwatch.Start();
  33. var replacementMod = Random.NextGaussian(pass.EntitiesPerReplacementAverage, pass.EntitiesPerReplacementStdDev);
  34. var prob = (float) Math.Clamp(1 / replacementMod, 0f, 1f);
  35. if (prob == 0)
  36. return;
  37. var enumerator = AllEntityQuery<TEntComp, TransformComponent>();
  38. while (enumerator.MoveNext(out var uid, out _, out var xform))
  39. {
  40. if (!IsMemberOfStation((uid, xform), ref args))
  41. continue;
  42. if (RobustRandom.Prob(prob))
  43. QueueReplace((uid, xform), pass.Replacements);
  44. }
  45. while (_queuedSpawns.TryDequeue(out var tup))
  46. {
  47. var (spawn, coords, rot) = tup;
  48. var newEnt = Spawn(spawn, coords);
  49. Transform(newEnt).LocalRotation = rot;
  50. }
  51. Log.Debug($"Entity replacement took {stopwatch.Elapsed} with {Stations.GetTileCount(args.Station)} tiles");
  52. }
  53. private void QueueReplace(Entity<TransformComponent> ent, List<EntitySpawnEntry> replacements)
  54. {
  55. var coords = ent.Comp.Coordinates;
  56. var rot = ent.Comp.LocalRotation;
  57. QueueDel(ent);
  58. foreach (var spawn in EntitySpawnCollection.GetSpawns(replacements, RobustRandom))
  59. {
  60. _queuedSpawns.Enqueue((spawn, coords, rot));
  61. }
  62. }
  63. }