--- /dev/null
+using Content.Shared.Atmos.Rotting;
+
+namespace Content.Client.Atmos.Rotting;
+
+public sealed class RottingSystem : SharedRottingSystem
+{
+}
namespace Content.Server.Atmos.Rotting;
-public sealed class RottingSystem : EntitySystem
+public sealed class RottingSystem : SharedRottingSystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
SubscribeLocalEvent<RottingComponent, ComponentShutdown>(OnShutdown);
SubscribeLocalEvent<RottingComponent, MobStateChangedEvent>(OnRottingMobStateChanged);
SubscribeLocalEvent<RottingComponent, BeingGibbedEvent>(OnGibbed);
- SubscribeLocalEvent<RottingComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<RottingComponent, RejuvenateEvent>(OnRejuvenate);
SubscribeLocalEvent<TemperatureComponent, IsRottingEvent>(OnTempIsRotting);
private void OnPerishableMapInit(EntityUid uid, PerishableComponent component, MapInitEvent args)
{
- component.NextPerishUpdate = _timing.CurTime + component.PerishUpdateRate;
+ component.RotNextUpdate = _timing.CurTime + component.PerishUpdateRate;
}
private void OnPerishableUnpaused(EntityUid uid, PerishableComponent component, ref EntityUnpausedEvent args)
{
- component.NextPerishUpdate += args.PausedTime;
+ component.RotNextUpdate += args.PausedTime;
}
private void OnMobStateChanged(EntityUid uid, PerishableComponent component, MobStateChangedEvent args)
return;
component.RotAccumulator = TimeSpan.Zero;
- component.NextPerishUpdate = _timing.CurTime + component.PerishUpdateRate;
+ component.RotNextUpdate = _timing.CurTime + component.PerishUpdateRate;
}
private void OnRottingUnpaused(EntityUid uid, RottingComponent component, ref EntityUnpausedEvent args)
{
if (TryComp<PerishableComponent>(uid, out var perishable))
{
- perishable.NextPerishUpdate = TimeSpan.Zero;
+ perishable.RotNextUpdate = TimeSpan.Zero;
}
}
private void OnPerishableExamined(Entity<PerishableComponent> perishable, ref ExaminedEvent args)
{
- int maxStages = 3;
- int stage = PerishStage(perishable, maxStages);
- if (stage < 1 || stage > maxStages)
+ int stage = PerishStage(perishable, MaxStages);
+ if (stage < 1 || stage > MaxStages)
{
// We dont push an examined string if it hasen't started "perishing" or it's already rotting
return;
return (int)(1 + maxStages * perishable.Comp.RotAccumulator.TotalSeconds / perishable.Comp.RotAfter.TotalSeconds);
}
- private void OnExamined(EntityUid uid, RottingComponent component, ExaminedEvent args)
- {
- var stage = RotStage(uid, component);
- var description = stage switch
- {
- >= 2 => "rotting-extremely-bloated",
- >= 1 => "rotting-bloated",
- _ => "rotting-rotting"
- };
- args.PushMarkup(Loc.GetString(description, ("target", Identity.Entity(uid, EntityManager))));
- }
-
- /// <summary>
- /// Return the rot stage, usually from 0 to 2 inclusive.
- /// </summary>
- public int RotStage(EntityUid uid, RottingComponent? comp = null, PerishableComponent? perishable = null)
- {
- if (!Resolve(uid, ref comp, ref perishable))
- return 0;
-
- return (int) (comp.TotalRotTime.TotalSeconds / perishable.RotAfter.TotalSeconds);
- }
-
private void OnRejuvenate(EntityUid uid, RottingComponent component, RejuvenateEvent args)
{
RemCompDeferred<RottingComponent>(uid);
var perishQuery = EntityQueryEnumerator<PerishableComponent>();
while (perishQuery.MoveNext(out var uid, out var perishable))
{
- if (_timing.CurTime < perishable.NextPerishUpdate)
+ if (_timing.CurTime < perishable.RotNextUpdate)
continue;
- perishable.NextPerishUpdate += perishable.PerishUpdateRate;
+ perishable.RotNextUpdate += perishable.PerishUpdateRate;
+
+ var stage = PerishStage((uid, perishable), MaxStages);
+ if (stage != perishable.Stage)
+ {
+ perishable.Stage = stage;
+ Dirty(uid, perishable);
+ }
if (IsRotten(uid) || !IsRotProgressing(uid, perishable))
continue;
+using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.Atmos.Rotting;
/// This makes mobs eventually start rotting when they die.
/// It may be expanded to food at some point, but it's just for mobs right now.
/// </summary>
-[RegisterComponent]
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(SharedRottingSystem))]
public sealed partial class PerishableComponent : Component
{
/// <summary>
/// How long it takes after death to start rotting.
/// </summary>
- [DataField("rotAfter"), ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public TimeSpan RotAfter = TimeSpan.FromMinutes(10);
/// <summary>
/// How much rotting has occured
/// </summary>
- [DataField("rotAccumulator"), ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public TimeSpan RotAccumulator = TimeSpan.Zero;
/// <summary>
/// Gasses are released, this is when the next gas release update will be.
/// </summary>
- [DataField("rotNextUpdate", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
- public TimeSpan NextPerishUpdate = TimeSpan.Zero;
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+ public TimeSpan RotNextUpdate = TimeSpan.Zero;
/// <summary>
/// How often the rotting ticks.
/// Feel free to tweak this if there are perf concerns.
/// </summary>
- [DataField("perishUpdateRate"), ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public TimeSpan PerishUpdateRate = TimeSpan.FromSeconds(5);
/// <summary>
/// How many moles of gas released per second, per unit of mass.
/// </summary>
- [DataField("molsPerSecondPerUnitMass"), ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public float MolsPerSecondPerUnitMass = 0.0025f;
+
+ [DataField, AutoNetworkedField]
+ public int Stage;
}
/// <summary>
/// Tracking component for stuff that has started to rot.
+/// Only the current stage is networked to the client.
/// </summary>
[RegisterComponent, NetworkedComponent]
+[Access(typeof(SharedRottingSystem))]
public sealed partial class RottingComponent : Component
{
/// <summary>
/// Whether or not the rotting should deal damage
/// </summary>
- [DataField("dealDamage"), ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public bool DealDamage = true;
/// <summary>
/// When the next check will happen for rot progression + effects like damage and ammonia
/// </summary>
- [DataField("nextRotUpdate", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
public TimeSpan NextRotUpdate = TimeSpan.Zero;
/// <summary>
/// How long in between each rot update.
/// </summary>
- [DataField("rotUpdateRate"), ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public TimeSpan RotUpdateRate = TimeSpan.FromSeconds(5);
/// <summary>
/// How long has this thing been rotting?
/// </summary>
- [DataField("totalRotTime"), ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
public TimeSpan TotalRotTime = TimeSpan.Zero;
/// <summary>
/// The damage dealt by rotting.
/// </summary>
- [DataField("damage")]
+ [DataField]
public DamageSpecifier Damage = new()
{
DamageDict = new()
--- /dev/null
+using Content.Shared.Examine;
+using Content.Shared.IdentityManagement;
+
+namespace Content.Shared.Atmos.Rotting;
+
+public abstract class SharedRottingSystem : EntitySystem
+{
+ public const int MaxStages = 3;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<RottingComponent, ExaminedEvent>(OnExamined);
+ }
+
+ /// <summary>
+ /// Return the rot stage, usually from 0 to 2 inclusive.
+ /// </summary>
+ public int RotStage(EntityUid uid, RottingComponent? comp = null, PerishableComponent? perishable = null)
+ {
+ if (!Resolve(uid, ref comp, ref perishable))
+ return 0;
+
+ return (int) (comp.TotalRotTime.TotalSeconds / perishable.RotAfter.TotalSeconds);
+ }
+
+ private void OnExamined(EntityUid uid, RottingComponent component, ExaminedEvent args)
+ {
+ var stage = RotStage(uid, component);
+ var description = stage switch
+ {
+ >= 2 => "rotting-extremely-bloated",
+ >= 1 => "rotting-bloated",
+ _ => "rotting-rotting"
+ };
+ args.PushMarkup(Loc.GetString(description, ("target", Identity.Entity(uid, EntityManager))));
+ }
+}