}
}
};
+
+ if (AppearanceSystem.TryGetData<bool>(uid, VaporVisuals.State, out var state) &&
+ state &&
+ TryComp<AnimationPlayerComponent>(uid, out var animPlayer) &&
+ !AnimationSystem.HasRunningAnimation(uid, animPlayer, VaporVisualsComponent.AnimationKey))
+ {
+ AnimationSystem.Play(uid, animPlayer, comp.VaporFlick, VaporVisualsComponent.AnimationKey);
+ }
}
/// <summary>
{
args.Sprite.Color = color;
}
-
- if ((AppearanceSystem.TryGetData<bool>(uid, VaporVisuals.State, out var state, args.Component) && state) &&
- TryComp<AnimationPlayerComponent>(uid, out var animPlayer) &&
- !AnimationSystem.HasRunningAnimation(uid, animPlayer, VaporVisualsComponent.AnimationKey))
- {
- AnimationSystem.Play(uid, animPlayer, comp.VaporFlick, VaporVisualsComponent.AnimationKey);
- }
}
}
_physics.SetLinearDamping(physics, 0f);
_physics.SetAngularDamping(physics, 0f);
- _throwing.TryThrow(vapor.Owner, dir * speed, speed, user: user, pushbackRatio: 50f);
+ _throwing.TryThrow(vapor.Owner, dir, speed, user: user, pushbackRatio: 50f);
var distance = (target.Position - vaporXform.WorldPosition).Length;
var time = (distance / physics.LinearVelocity.Length);
--- /dev/null
+using Content.Server.Decals;
+using Content.Shared.Chemistry.Reaction;
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Decals;
+using Content.Shared.FixedPoint;
+using Robust.Shared.Map;
+using Robust.Shared.Map.Components;
+
+namespace Content.Server.Chemistry.TileReactions;
+
+/// <summary>
+/// Purges all cleanable decals on a tile.
+/// </summary>
+[DataDefinition]
+public sealed class CleanDecalsReaction : ITileReaction
+{
+ /// <summary>
+ /// For every cleaned decal we lose this much reagent.
+ /// </summary>
+ [DataField("cleanCost")]
+ public FixedPoint2 CleanCost { get; private set; } = FixedPoint2.New(0.25f);
+
+ public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume)
+ {
+ var entMan = IoCManager.Resolve<IEntityManager>();
+
+ if (reactVolume <= CleanCost ||
+ !entMan.TryGetComponent<MapGridComponent>(tile.GridUid, out var grid) ||
+ !entMan.TryGetComponent<DecalGridComponent>(tile.GridUid, out var decalGrid))
+ {
+ return FixedPoint2.Zero;
+ }
+
+ var lookupSystem = entMan.System<EntityLookupSystem>();
+ var decalSystem = entMan.System<DecalSystem>();
+ // Very generous hitbox.
+ var decals = decalSystem
+ .GetDecalsIntersecting(tile.GridUid, lookupSystem.GetLocalBounds(tile, grid.TileSize).Enlarged(0.5f).Translated(new Vector2(-0.5f, -0.5f)));
+ var amount = FixedPoint2.Zero;
+
+ foreach (var decal in decals)
+ {
+ if (!decal.Decal.Cleanable)
+ continue;
+
+ decalSystem.RemoveDecal(tile.GridUid, decal.Index, decalGrid);
+ amount += CleanCost;
+
+ if (amount > reactVolume)
+ break;
+ }
+
+ return amount;
+ }
+}
using System.Linq;
-using Content.Server.Cleanable;
-using Content.Server.Decals;
+using Content.Server.Chemistry.EntitySystems;
+using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint;
+using Content.Shared.Fluids.Components;
using Robust.Shared.Map;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-namespace Content.Server.Chemistry.TileReactions
+namespace Content.Server.Chemistry.TileReactions;
+
+/// <summary>
+/// Turns all of the reagents on a puddle into water.
+/// </summary>
+[DataDefinition]
+public sealed class CleanTileReaction : ITileReaction
{
- [DataDefinition]
- public sealed class CleanTileReaction : ITileReaction
+ /// <summary>
+ /// How much it costs to clean 1 unit of reagent.
+ /// </summary>
+ /// <remarks>
+ /// In terms of space cleaner can clean 1 average puddle per 5 units.
+ /// </remarks>
+ [DataField("cleanCost")]
+ public float CleanAmountMultiplier { get; private set; } = 0.25f;
+
+ /// <summary>
+ /// What reagent to replace the tile conents with.
+ /// </summary>
+ [DataField("reagent", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentPrototype>))]
+ public string ReplacementReagent = "Water";
+
+ FixedPoint2 ITileReaction.TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume)
{
- /// <summary>
- /// Multiplier used in CleanTileReaction.
- /// 1 (default) means normal consumption rate of the cleaning reagent.
- /// 0 means no consumption of the cleaning reagent, i.e. the reagent is inexhaustible.
- /// </summary>
- [DataField("cleanAmountMultiplier")]
- public float CleanAmountMultiplier { get; private set; } = 1.0f;
-
- FixedPoint2 ITileReaction.TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume)
+ var entMan = IoCManager.Resolve<IEntityManager>();
+ var entities = entMan.System<EntityLookupSystem>().GetEntitiesIntersecting(tile).ToArray();
+ var puddleQuery = entMan.GetEntityQuery<PuddleComponent>();
+ var solutionContainerSystem = entMan.System<SolutionContainerSystem>();
+ // Multiply as the amount we can actually purge is higher than the react amount.
+ var purgeAmount = reactVolume / CleanAmountMultiplier;
+
+ foreach (var entity in entities)
{
- var entities = EntitySystem.Get<EntityLookupSystem>().GetEntitiesIntersecting(tile).ToArray();
- var amount = FixedPoint2.Zero;
- var entMan = IoCManager.Resolve<IEntityManager>();
- foreach (var entity in entities)
+ if (!puddleQuery.TryGetComponent(entity, out var puddle) ||
+ !solutionContainerSystem.TryGetSolution(entity, puddle.SolutionName, out var puddleSolution))
{
- if (entMan.TryGetComponent(entity, out CleanableComponent? cleanable))
- {
- var next = amount + (cleanable.CleanAmount * CleanAmountMultiplier);
- // Nothing left?
- if (reactVolume < next)
- break;
-
- amount = next;
- entMan.QueueDeleteEntity(entity);
- }
+ continue;
}
- var decalSystem = EntitySystem.Get<DecalSystem>();
- foreach (var (uid, _) in decalSystem.GetDecalsInRange(tile.GridUid, tile.GridIndices+new Vector2(0.5f, 0.5f), validDelegate: x => x.Cleanable))
- {
- decalSystem.RemoveDecal(tile.GridUid, uid);
- }
+ var purgeable =
+ solutionContainerSystem.SplitSolutionWithout(entity, puddleSolution, purgeAmount, ReplacementReagent, reagent.ID);
+
+ purgeAmount -= purgeable.Volume;
- return amount;
+ solutionContainerSystem.TryAddSolution(entity, puddleSolution, new Solution(ReplacementReagent, purgeable.Volume));
+
+ if (purgeable.Volume <= FixedPoint2.Zero)
+ break;
}
+
+ return (reactVolume / CleanAmountMultiplier - purgeAmount) * CleanAmountMultiplier;
}
}
+++ /dev/null
-using Content.Shared.FixedPoint;
-
-namespace Content.Server.Cleanable
-{
- [RegisterComponent]
- public sealed class CleanableComponent : Component
- {
- [DataField("cleanAmount")]
- private FixedPoint2 _cleanAmount = FixedPoint2.Zero;
- [ViewVariables(VVAccess.ReadWrite)]
- public FixedPoint2 CleanAmount => _cleanAmount;
- }
-}
{
public const string SolutionName = "spray";
- [DataField("sprayDistance")] public float SprayDistance = 3f;
+ [ViewVariables(VVAccess.ReadWrite), DataField("sprayDistance")]
+ public float SprayDistance = 3.5f;
- [DataField("transferAmount")] public FixedPoint2 TransferAmount = FixedPoint2.New(10);
+ [ViewVariables(VVAccess.ReadWrite), DataField("sprayVelocity")]
+ public float SprayVelocity = 3.5f;
- [DataField("sprayVelocity")] public float SprayVelocity = 1.5f;
-
- [DataField("sprayAliveTime")] public float SprayAliveTime = 0.75f;
-
- [DataField("cooldownTime")] public float CooldownTime = 0.5f;
-
- [DataField("sprayedPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
+ [ViewVariables(VVAccess.ReadWrite), DataField("sprayedPrototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string SprayedPrototype = "Vapor";
- [DataField("vaporAmount")] public int VaporAmount = 1;
-
- [DataField("vaporSpread")] public float VaporSpread = 90f;
+ [ViewVariables(VVAccess.ReadWrite), DataField("vaporAmount")]
+ public int VaporAmount = 1;
- [DataField("impulse")] public float Impulse;
+ [ViewVariables(VVAccess.ReadWrite), DataField("vaporSpread")]
+ public float VaporSpread = 90f;
- [DataField("spraySound", required: true)]
+ [ViewVariables(VVAccess.ReadWrite), DataField("spraySound", required: true)]
[Access(typeof(SpraySystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
public SoundSpecifier SpraySound { get; } = default!;
}
using Content.Server.Extinguisher;
using Content.Server.Fluids.Components;
using Content.Server.Popups;
+using Content.Shared.Chemistry.Components;
using Content.Shared.Cooldown;
using Content.Shared.FixedPoint;
using Content.Shared.Interaction;
var curTime = _gameTiming.CurTime;
if (TryComp<ItemCooldownComponent>(uid, out var cooldown)
&& curTime < cooldown.CooldownEnd)
+ {
return;
+ }
if (solution.Volume <= 0)
{
return;
}
+ if (!TryComp<SolutionTransferComponent>(uid, out var transfer))
+ return;
+
var xformQuery = GetEntityQuery<TransformComponent>();
var userXform = xformQuery.GetComponent(args.User);
var userMapPos = userXform.MapPosition;
- var clickMapPos = args.ClickLocation.ToMap(EntityManager);
+ var clickMapPos = args.ClickLocation.ToMap(EntityManager, _transform);
var diffPos = clickMapPos.Position - userMapPos.Position;
if (diffPos == Vector2.Zero || diffPos == Vector2.NaN)
return;
- var diffLength = diffPos.Length;
var diffNorm = diffPos.Normalized;
+ var diffLength = diffPos.Length;
+
+ if (diffLength > component.SprayDistance)
+ {
+ diffLength = component.SprayDistance;
+ }
+
var diffAngle = diffNorm.ToAngle();
// Vectors to determine the spawn offset of the vapor clouds.
var threeQuarters = diffNorm * 0.75f;
var quarter = diffNorm * 0.25f;
- var amount = Math.Max(Math.Min((solution.Volume / component.TransferAmount).Int(), component.VaporAmount), 1);
+ var amount = Math.Max(Math.Min((solution.Volume / transfer.TransferAmount).Int(), component.VaporAmount), 1);
var spread = component.VaporSpread / amount;
+ // TODO: Just use usedelay homie.
+ var cooldownTime = 0f;
for (var i = 0; i < amount; i++)
{
var target = userMapPos
.Offset((diffNorm + rotation.ToVec()).Normalized * diffLength + quarter);
- var distance = target.Position.Length;
+ var distance = (target.Position - userMapPos.Position).Length;
if (distance > component.SprayDistance)
target = userMapPos.Offset(diffNorm * component.SprayDistance);
- var newSolution = _solutionContainer.SplitSolution(uid, solution, component.TransferAmount);
+ var newSolution = _solutionContainer.SplitSolution(uid, solution, transfer.TransferAmount);
if (newSolution.Volume <= FixedPoint2.Zero)
break;
// impulse direction is defined in world-coordinates, not local coordinates
var impulseDirection = rotation.ToVec();
- _vapor.Start(vaporComponent, vaporXform, impulseDirection, component.SprayVelocity, target, component.SprayAliveTime, args.User);
+ var time = diffLength / component.SprayVelocity;
+ cooldownTime = MathF.Max(time, cooldownTime);
+
+ _vapor.Start(vaporComponent, vaporXform, impulseDirection * diffLength, component.SprayVelocity, target, time, args.User);
}
_audio.PlayPvs(component.SpraySound, uid, component.SpraySound.Params.WithVariation(0.125f));
RaiseLocalEvent(uid,
- new RefreshItemCooldownEvent(curTime, curTime + TimeSpan.FromSeconds(component.CooldownTime)), true);
+ new RefreshItemCooldownEvent(curTime, curTime + TimeSpan.FromSeconds(cooldownTime)), true);
}
}
using Robust.Shared.Serialization;
-namespace Content.Shared.Vapor
+namespace Content.Shared.Vapor;
+
+[Serializable, NetSerializable]
+public enum VaporVisuals
{
- [Serializable, NetSerializable]
- public enum VaporVisuals
- {
- Rotation,
- Color,
- State,
- }
+ Color,
+ State,
}
canChangeTransferAmount: true
- type: ItemCooldown
- type: Spray
- transferAmount: 10
sprayVelocity: 2
spraySound:
path: /Audio/Effects/spray2.ogg
maxVol: 250
- type: Spray
sprayedPrototype: BigVapor
- transferAmount: 10
sprayVelocity: 5
- sprayAliveTime: 1.5
+ sprayAliveTime: 5
spraySound:
path: /Audio/Effects/spray2.ogg
meltingPoint: -11.0
tileReactions:
- !type:CleanTileReaction {}
+ - !type:CleanDecalsReaction {}
- type: reagent
id: SpaceLube
maxOnTileWhitelist:
tags: [ Bee ]
- !type:CleanTileReaction # Bees are extremely obsessive about cleanliness within what they consider their hive.
- cleanAmountMultiplier: 0 # Consume absolutely zero bees. Buzz buzz.
+ cleanCost: 0 # Consume absolutely zero bees. Buzz buzz.
metabolisms:
Poison:
effects: