Refactors radiation (#2009)
* Work on refactoring radiation. * mmmm grayons * fixes * Now you can specify whether the pulse will decay or not * whoops * Move IRadiationAct to shared, make DamageableComponent implement it instead and add metallic resistances to walls * General improvements, send draw and decay with state. Rename DPS to RadsPerSecond * E N T I T Y C O O R D I N A T E S * Entity coordinates goood * Remove unused using statements * resistances: metallicResistances * - type: Breakable moment Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
2927ab5cd1
commit
6ec2939f15
@@ -8,58 +8,116 @@ using Robust.Shared.Interfaces.Timing;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Timers;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.StationEvents
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedRadiationPulseComponent))]
|
||||
public sealed class RadiationPulseComponent : SharedRadiationPulseComponent
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
private const float MinPulseLifespan = 0.8f;
|
||||
private const float MaxPulseLifespan = 2.5f;
|
||||
|
||||
public float DPS => _dps;
|
||||
private float _dps;
|
||||
|
||||
private float _duration;
|
||||
private float _radsPerSecond;
|
||||
private float _range;
|
||||
private TimeSpan _endTime;
|
||||
private bool _draw;
|
||||
private bool _decay;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the entity will delete itself after a certain duration defined by
|
||||
/// <see cref="MinPulseLifespan"/> and <see cref="MaxPulseLifespan"/>
|
||||
/// </summary>
|
||||
public override bool Decay
|
||||
{
|
||||
get => _decay;
|
||||
set
|
||||
{
|
||||
_decay = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public float MinPulseLifespan { get; set; }
|
||||
|
||||
public float MaxPulseLifespan { get; set; }
|
||||
|
||||
public override float RadsPerSecond
|
||||
{
|
||||
get => _radsPerSecond;
|
||||
set
|
||||
{
|
||||
_radsPerSecond = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public string Sound { get; set; }
|
||||
|
||||
public override float Range
|
||||
{
|
||||
get => _range;
|
||||
set
|
||||
{
|
||||
_range = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Draw
|
||||
{
|
||||
get => _draw;
|
||||
set
|
||||
{
|
||||
_draw = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public override TimeSpan EndTime => _endTime;
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(ref _dps, "dps", 40.0f);
|
||||
serializer.DataField(this, x => x.RadsPerSecond, "dps", 40.0f);
|
||||
serializer.DataField(this, x => x.Sound, "sound", "/Audio/Weapons/Guns/Gunshots/laser3.ogg");
|
||||
serializer.DataField(this, x => x.Range, "range", 5.0f);
|
||||
serializer.DataField(this, x => x.Draw, "draw", true);
|
||||
serializer.DataField(this, x => x.Decay, "decay", true);
|
||||
serializer.DataField(this, x => x.MaxPulseLifespan, "maxPulseLifespan", 2.5f);
|
||||
serializer.DataField(this, x => x.MinPulseLifespan, "minPulseLifespan", 0.8f);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
public void DoPulse()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
var currentTime = _gameTiming.CurTime;
|
||||
var duration =
|
||||
TimeSpan.FromSeconds(
|
||||
_random.NextFloat() * (MaxPulseLifespan - MinPulseLifespan) +
|
||||
MinPulseLifespan);
|
||||
|
||||
_endTime = currentTime + duration;
|
||||
|
||||
Timer.Spawn(duration,
|
||||
() =>
|
||||
if (Decay)
|
||||
{
|
||||
if (!Owner.Deleted)
|
||||
{
|
||||
Owner.Delete();
|
||||
}
|
||||
});
|
||||
var currentTime = _gameTiming.CurTime;
|
||||
_duration = _random.NextFloat() * (MaxPulseLifespan - MinPulseLifespan) + MinPulseLifespan;
|
||||
_endTime = currentTime + TimeSpan.FromSeconds(_duration);
|
||||
}
|
||||
|
||||
if(!string.IsNullOrEmpty(Sound))
|
||||
EntitySystem.Get<AudioSystem>().PlayAtCoords(Sound, Owner.Transform.Coordinates);
|
||||
|
||||
EntitySystem.Get<AudioSystem>().PlayAtCoords("/Audio/Weapons/Guns/Gunshots/laser3.ogg", Owner.Transform.Coordinates);
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new RadiationPulseMessage(_endTime);
|
||||
return new RadiationPulseState(_radsPerSecond, _range, Draw, Decay, _endTime);
|
||||
}
|
||||
|
||||
public void Update(float frameTime)
|
||||
{
|
||||
if (!Decay || Owner.Deleted)
|
||||
return;
|
||||
|
||||
if(_duration <= 0f)
|
||||
Owner.Delete();
|
||||
|
||||
_duration -= frameTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,87 +1,56 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.StationEvents;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Body;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Server.GameObjects.EntitySystems.StationEvents
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class RadiationPulseSystem : EntitySystem
|
||||
{
|
||||
// Rather than stuffing around with collidables and checking entities on initialize etc. we'll just tick over
|
||||
// for each entity in range. Seemed easier than checking entities on spawn, then checking collidables, etc.
|
||||
// Especially considering each pulse is a big chonker, + no circle hitboxes yet.
|
||||
private const string RadiationPrototype = "RadiationPulse";
|
||||
|
||||
private TypeEntityQuery _speciesQuery;
|
||||
|
||||
/// <summary>
|
||||
/// Damage works with ints so we'll just accumulate damage and once we hit this threshold we'll apply it.
|
||||
/// </summary>
|
||||
/// This also server to stop spamming the damagethreshold with 1 damage continuously.
|
||||
private const int DamageThreshold = 10;
|
||||
|
||||
private Dictionary<IEntity, float> _accumulatedDamage = new Dictionary<IEntity, float>();
|
||||
|
||||
public override void Initialize()
|
||||
public IEntity RadiationPulse(EntityCoordinates coordinates, float range, int dps, bool decay = true, float minPulseLifespan = 0.8f, float maxPulseLifespan = 2.5f, string sound = null)
|
||||
{
|
||||
base.Initialize();
|
||||
_speciesQuery = new TypeEntityQuery(typeof(ISharedBodyManagerComponent));
|
||||
var radiationEntity = EntityManager.SpawnEntity(RadiationPrototype, coordinates);
|
||||
var radiation = radiationEntity.GetComponent<RadiationPulseComponent>();
|
||||
|
||||
radiation.Range = range;
|
||||
radiation.RadsPerSecond = dps;
|
||||
radiation.Draw = false;
|
||||
radiation.Decay = decay;
|
||||
radiation.MinPulseLifespan = minPulseLifespan;
|
||||
radiation.MaxPulseLifespan = maxPulseLifespan;
|
||||
radiation.Sound = sound;
|
||||
|
||||
radiation.DoPulse();
|
||||
|
||||
return radiationEntity;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
var anyPulses = false;
|
||||
|
||||
foreach (var comp in ComponentManager.EntityQuery<RadiationPulseComponent>())
|
||||
{
|
||||
anyPulses = true;
|
||||
comp.Update(frameTime);
|
||||
var ent = comp.Owner;
|
||||
|
||||
foreach (var species in EntityManager.GetEntities(_speciesQuery))
|
||||
if (ent.Deleted) continue;
|
||||
|
||||
foreach (var entity in EntityManager.GetEntitiesInRange(ent.Transform.Coordinates, comp.Range, true))
|
||||
{
|
||||
// Work out if we're in range and accumulate more damage
|
||||
// If we've hit the DamageThreshold we'll also apply that damage to the mob
|
||||
// If we're really lagging server can apply multiples of the DamageThreshold at once
|
||||
if (species.Transform.MapID != comp.Owner.Transform.MapID) continue;
|
||||
if (entity.Deleted) continue;
|
||||
|
||||
if ((species.Transform.WorldPosition - comp.Owner.Transform.WorldPosition).Length > comp.Range)
|
||||
foreach (var radiation in entity.GetAllComponents<IRadiationAct>())
|
||||
{
|
||||
continue;
|
||||
radiation.RadiationAct(frameTime, comp);
|
||||
}
|
||||
|
||||
var totalDamage = frameTime * comp.DPS;
|
||||
|
||||
if (!_accumulatedDamage.TryGetValue(species, out var accumulatedSpecies))
|
||||
{
|
||||
_accumulatedDamage[species] = 0.0f;
|
||||
}
|
||||
|
||||
totalDamage += accumulatedSpecies;
|
||||
_accumulatedDamage[species] = totalDamage;
|
||||
|
||||
if (totalDamage < DamageThreshold) continue;
|
||||
if (!species.TryGetComponent(out DamageableComponent damageableComponent)) continue;
|
||||
|
||||
var damageMultiple = (int) (totalDamage / DamageThreshold);
|
||||
_accumulatedDamage[species] = totalDamage % DamageThreshold;
|
||||
|
||||
damageableComponent.ChangeDamage(DamageType.Heat, damageMultiple * DamageThreshold, false, comp.Owner);
|
||||
}
|
||||
}
|
||||
|
||||
if (anyPulses)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// probably don't need to worry about clearing this at roundreset unless you have a radiation pulse at roundstart
|
||||
// (which is currently not possible)
|
||||
_accumulatedDamage.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user