/// scales with severity.
/// </summary>
[DataField("maxSpawnAmount"), ViewVariables(VVAccess.ReadWrite)]
- public int MaxSpawnAmount = 8;
+ public int MaxSpawnAmount = 7;
/// <summary>
/// The maximum radius the entities will spawn in.
/// scales with stability
/// </summary>
[DataField("spawnRange"), ViewVariables(VVAccess.ReadWrite)]
- public float SpawnRange = 4f;
+ public float SpawnRange = 5f;
/// <summary>
/// The tile that is spawned by the anomaly's effect
--- /dev/null
+using Content.Shared.Whitelist;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Damage.Components;
+
+[NetworkedComponent, RegisterComponent]
+public sealed class DamageContactsComponent : Component
+{
+ /// <summary>
+ /// The damage done each second to those touching this entity
+ /// </summary>
+ [DataField("damage", required: true)]
+ public DamageSpecifier Damage = new();
+
+ /// <summary>
+ /// Entities that aren't damaged by this entity
+ /// </summary>
+ [DataField("ignoreWhitelist")]
+ public EntityWhitelist? IgnoreWhitelist;
+}
--- /dev/null
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Shared.Damage.Components;
+
+[NetworkedComponent, RegisterComponent]
+public sealed class DamagedByContactComponent : Component
+{
+ [DataField("nextSecond", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
+ public TimeSpan NextSecond = TimeSpan.Zero;
+
+ [ViewVariables]
+ public DamageSpecifier? Damage;
+}
--- /dev/null
+using Content.Shared.Damage.Components;
+using Robust.Shared.Physics.Components;
+using Robust.Shared.Physics.Events;
+using Robust.Shared.Physics.Systems;
+using Robust.Shared.Timing;
+
+namespace Content.Shared.Damage.Systems;
+
+public sealed class DamageContactsSystem : EntitySystem
+{
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly DamageableSystem _damageable = default!;
+ [Dependency] private readonly SharedPhysicsSystem _physics = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent<DamageContactsComponent, StartCollideEvent>(OnEntityEnter);
+ SubscribeLocalEvent<DamageContactsComponent, EndCollideEvent>(OnEntityExit);
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ foreach (var damaged in EntityQuery<DamagedByContactComponent>())
+ {
+ var ent = damaged.Owner;
+ if (_timing.CurTime < damaged.NextSecond)
+ continue;
+ damaged.NextSecond = _timing.CurTime + TimeSpan.FromSeconds(1);
+
+ if (damaged.Damage != null)
+ _damageable.TryChangeDamage(ent, damaged.Damage, interruptsDoAfters: false);
+ }
+ }
+
+ private void OnEntityExit(EntityUid uid, DamageContactsComponent component, ref EndCollideEvent args)
+ {
+ var otherUid = args.OtherFixture.Body.Owner;
+
+ if (!TryComp<PhysicsComponent>(uid, out var body))
+ return;
+
+ var damageQuery = GetEntityQuery<DamageContactsComponent>();
+ foreach (var contact in _physics.GetContactingEntities(body))
+ {
+ var ent = contact.Owner;
+ if (ent == uid)
+ continue;
+
+ if (damageQuery.HasComponent(ent))
+ return;
+ }
+
+ RemComp<DamagedByContactComponent>(otherUid);
+ }
+
+ private void OnEntityEnter(EntityUid uid, DamageContactsComponent component, ref StartCollideEvent args)
+ {
+ var otherUid = args.OtherFixture.Body.Owner;
+
+ if (HasComp<DamagedByContactComponent>(otherUid))
+ return;
+
+ if (component.IgnoreWhitelist?.IsValid(otherUid) ?? false)
+ return;
+
+ var damagedByContact = EnsureComp<DamagedByContactComponent>(otherUid);
+ damagedByContact.Damage = component.Damage;
+ }
+}