ash storms just the facts
This commit is contained in:
82
Content.Server/DeltaV/Weather/WeatherEffectsSystem.cs
Normal file
82
Content.Server/DeltaV/Weather/WeatherEffectsSystem.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Mobs;
|
||||||
|
using Content.Shared.Mobs.Components;
|
||||||
|
using Content.Shared.Weather;
|
||||||
|
using Content.Shared.Whitelist;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Shared.DeltaV.Weather;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles weather damage for exposed entities.
|
||||||
|
/// </summary>
|
||||||
|
public sealed partial class WeatherEffectsSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||||
|
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||||
|
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||||
|
[Dependency] private readonly SharedWeatherSystem _weather = default!;
|
||||||
|
|
||||||
|
private EntityQuery<MapGridComponent> _gridQuery;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
var now = _timing.CurTime;
|
||||||
|
var query = EntityQueryEnumerator<WeatherComponent>();
|
||||||
|
while (query.MoveNext(out var map, out var weather))
|
||||||
|
{
|
||||||
|
if (now < weather.NextUpdate)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
weather.NextUpdate = now + weather.UpdateDelay;
|
||||||
|
|
||||||
|
foreach (var (id, data) in weather.Weather)
|
||||||
|
{
|
||||||
|
// start and end do no damage
|
||||||
|
if (data.State != WeatherState.Running)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
UpdateDamage(map, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateDamage(EntityUid map, ProtoId<WeatherPrototype> id)
|
||||||
|
{
|
||||||
|
var weather = _proto.Index(id);
|
||||||
|
if (weather.Damage is not {} damage)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var query = EntityQueryEnumerator<MobStateComponent, TransformComponent>();
|
||||||
|
while (query.MoveNext(out var uid, out var mob, out var xform))
|
||||||
|
{
|
||||||
|
// don't give dead bodies 10000 burn, that's not fun for anyone
|
||||||
|
if (xform.MapUid != map || mob.CurrentState == MobState.Dead)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// if not in space, check for being indoors
|
||||||
|
if (xform.GridUid is {} gridUid && _gridQuery.TryComp(gridUid, out var grid))
|
||||||
|
{
|
||||||
|
var tile = _map.GetTileRef((gridUid, grid), xform.Coordinates);
|
||||||
|
if (!_weather.CanWeatherAffect(gridUid, grid, tile))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_whitelist.IsBlacklistFailOrNull(weather.DamageBlacklist, uid))
|
||||||
|
_damageable.TryChangeDamage(uid, damage, interruptsDoAfters: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
Content.Server/DeltaV/Weather/WeatherSchedulerComponent.cs
Normal file
58
Content.Server/DeltaV/Weather/WeatherSchedulerComponent.cs
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
using Content.Shared.Destructible.Thresholds;
|
||||||
|
using Content.Shared.Weather;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
|
||||||
|
namespace Content.Server.DeltaV.Weather;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes weather randomly happen every so often.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(WeatherSchedulerSystem))]
|
||||||
|
[AutoGenerateComponentPause]
|
||||||
|
public sealed partial class WeatherSchedulerComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Weather stages to schedule.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public List<WeatherStage> Stages = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The index of <see cref="Stages"/> to use next, wraps back to the start.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public int Stage;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When to go to the next step of the schedule.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
|
||||||
|
public TimeSpan NextUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A stage in a weather schedule.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, DataDefinition]
|
||||||
|
public partial struct WeatherStage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A range of how long the stage can last for, in seconds.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public MinMax Duration = new(0, 0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The weather prototype to add, or null for clear weather.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public ProtoId<WeatherPrototype>? Weather;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Alert message to send in chat for players on the map when it starts.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public LocId? Message;
|
||||||
|
}
|
||||||
75
Content.Server/DeltaV/Weather/WeatherSchedulerSystem.cs
Normal file
75
Content.Server/DeltaV/Weather/WeatherSchedulerSystem.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
using Content.Server.Chat.Managers;
|
||||||
|
using Content.Shared.Chat;
|
||||||
|
using Content.Shared.Weather;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Server.DeltaV.Weather;
|
||||||
|
|
||||||
|
public sealed class WeatherSchedulerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IChatManager _chat = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly SharedWeatherSystem _weather = default!;
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
var now = _timing.CurTime;
|
||||||
|
var query = EntityQueryEnumerator<WeatherSchedulerComponent>();
|
||||||
|
while (query.MoveNext(out var map, out var comp))
|
||||||
|
{
|
||||||
|
if (now < comp.NextUpdate)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (comp.Stage >= comp.Stages.Count)
|
||||||
|
comp.Stage = 0;
|
||||||
|
|
||||||
|
var stage = comp.Stages[comp.Stage++];
|
||||||
|
var duration = stage.Duration.Next(_random);
|
||||||
|
comp.NextUpdate = now + TimeSpan.FromSeconds(duration);
|
||||||
|
|
||||||
|
var mapId = Comp<MapComponent>(map).MapId;
|
||||||
|
if (stage.Weather is {} weather)
|
||||||
|
{
|
||||||
|
var ending = comp.NextUpdate;
|
||||||
|
// crossfade weather so as one ends the next starts
|
||||||
|
if (HasWeather(comp, comp.Stage - 1))
|
||||||
|
ending += WeatherComponent.ShutdownTime;
|
||||||
|
if (HasWeather(comp, comp.Stage + 1))
|
||||||
|
ending += WeatherComponent.StartupTime;
|
||||||
|
_weather.SetWeather(mapId, _proto.Index(weather), ending);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stage.Message is {} message)
|
||||||
|
{
|
||||||
|
var msg = Loc.GetString(message);
|
||||||
|
_chat.ChatMessageToManyFiltered(
|
||||||
|
Filter.BroadcastMap(mapId),
|
||||||
|
ChatChannel.Radio,
|
||||||
|
msg,
|
||||||
|
msg,
|
||||||
|
map,
|
||||||
|
false,
|
||||||
|
true,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasWeather(WeatherSchedulerComponent comp, int stage)
|
||||||
|
{
|
||||||
|
if (stage < 0)
|
||||||
|
stage = comp.Stages.Count + stage;
|
||||||
|
else if (stage >= comp.Stages.Count)
|
||||||
|
stage %= comp.Stages.Count;
|
||||||
|
|
||||||
|
return comp.Stages[stage].Weather != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.DeltaV.Weather.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Makes an entity not take damage from ash storms.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed partial class AshStormImmuneComponent : Component;
|
||||||
@@ -38,6 +38,7 @@ public abstract class SharedWeatherSystem : EntitySystem
|
|||||||
if (weather.EndTime != null)
|
if (weather.EndTime != null)
|
||||||
weather.EndTime = weather.EndTime.Value + args.PausedTime;
|
weather.EndTime = weather.EndTime.Value + args.PausedTime;
|
||||||
}
|
}
|
||||||
|
component.NextUpdate += args.PausedTime; // DeltaV
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanWeatherAffect(EntityUid uid, MapGridComponent grid, TileRef tileRef, RoofComponent? roofComp = null)
|
public bool CanWeatherAffect(EntityUid uid, MapGridComponent grid, TileRef tileRef, RoofComponent? roofComp = null)
|
||||||
|
|||||||
@@ -14,6 +14,18 @@ public sealed partial class WeatherComponent : Component
|
|||||||
[DataField]
|
[DataField]
|
||||||
public Dictionary<ProtoId<WeatherPrototype>, WeatherData> Weather = new();
|
public Dictionary<ProtoId<WeatherPrototype>, WeatherData> Weather = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DeltaV: How long to wait between updating weather effects.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public TimeSpan UpdateDelay = TimeSpan.FromSeconds(1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DeltaV: When to next update weather effects (damage).
|
||||||
|
/// </summary>
|
||||||
|
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
|
public TimeSpan NextUpdate = TimeSpan.Zero;
|
||||||
|
|
||||||
public static readonly TimeSpan StartupTime = TimeSpan.FromSeconds(15);
|
public static readonly TimeSpan StartupTime = TimeSpan.FromSeconds(15);
|
||||||
public static readonly TimeSpan ShutdownTime = TimeSpan.FromSeconds(15);
|
public static readonly TimeSpan ShutdownTime = TimeSpan.FromSeconds(15);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Whitelist;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
@@ -20,4 +22,17 @@ public sealed partial class WeatherPrototype : IPrototype
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("sound")]
|
[ViewVariables(VVAccess.ReadWrite), DataField("sound")]
|
||||||
public SoundSpecifier? Sound;
|
public SoundSpecifier? Sound;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DeltaV: Damage you can take from being in this weather.
|
||||||
|
/// Only applies when weather has fully set in.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public DamageSpecifier? Damage;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DeltaV: Don't damage entities that match this blacklist.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public EntityWhitelist? DamageBlacklist;
|
||||||
}
|
}
|
||||||
|
|||||||
3
Resources/Locale/en-US/deltav/weather/ashstorm.ftl
Normal file
3
Resources/Locale/en-US/deltav/weather/ashstorm.ftl
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
ash-storm-telegraph = [color=red][bold]An eerie moan rises on the wind. Sheets of burning ash blacken the horizon. Seek shelter.[/bold][/color]
|
||||||
|
ash-storm-alert = [color=red][bolditalic]Smoldering clouds of scorching ash billow down around you! Get inside![/bolditalic][/color]
|
||||||
|
ash-storm-clearing = [color=red]The shrieking wind whips away the last of the ash and falls to its usual murmur. It should be safe to go outside now.[/color]
|
||||||
50
Resources/Prototypes/Body/Prototypes/ashwalker.yml
Normal file
50
Resources/Prototypes/Body/Prototypes/ashwalker.yml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# TODO: will need updating with shitmed
|
||||||
|
- type: body
|
||||||
|
name: ashwalker
|
||||||
|
id: AshWalker
|
||||||
|
root: torso
|
||||||
|
slots:
|
||||||
|
head:
|
||||||
|
part: HeadReptilian
|
||||||
|
connections:
|
||||||
|
- torso
|
||||||
|
organs:
|
||||||
|
brain: OrganHumanBrain
|
||||||
|
eyes: OrganHumanEyes
|
||||||
|
torso:
|
||||||
|
part: TorsoReptilian
|
||||||
|
organs:
|
||||||
|
heart: OrganAnimalHeart
|
||||||
|
lungs: OrganAshWalkerLungs
|
||||||
|
stomach: OrganReptilianStomach
|
||||||
|
liver: OrganAnimalLiver
|
||||||
|
kidneys: OrganHumanKidneys
|
||||||
|
connections:
|
||||||
|
- right arm
|
||||||
|
- left arm
|
||||||
|
- right leg
|
||||||
|
- left leg
|
||||||
|
right arm:
|
||||||
|
part: RightArmReptilian
|
||||||
|
connections:
|
||||||
|
- right hand
|
||||||
|
left arm:
|
||||||
|
part: LeftArmReptilian
|
||||||
|
connections:
|
||||||
|
- left hand
|
||||||
|
right hand:
|
||||||
|
part: RightHandReptilian
|
||||||
|
left hand:
|
||||||
|
part: LeftHandReptilian
|
||||||
|
right leg:
|
||||||
|
part: RightLegReptilian
|
||||||
|
connections:
|
||||||
|
- right foot
|
||||||
|
left leg:
|
||||||
|
part: LeftLegReptilian
|
||||||
|
connections:
|
||||||
|
- left foot
|
||||||
|
right foot:
|
||||||
|
part: RightFootReptilian
|
||||||
|
left foot:
|
||||||
|
part: LeftFootReptilian
|
||||||
9
Resources/Prototypes/DeltaV/Body/Organs/ashwalker.yml
Normal file
9
Resources/Prototypes/DeltaV/Body/Organs/ashwalker.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
- type: entity
|
||||||
|
parent: OrganAnimalLungs
|
||||||
|
id: OrganAshWalkerLungs
|
||||||
|
name: ashwalker lungs
|
||||||
|
description: These lungs are adapted from isolation in lavaland, capable of withstanding the low oxygen content and ash storms.
|
||||||
|
components:
|
||||||
|
- type: Lung
|
||||||
|
saturationLoss: 0.5
|
||||||
|
# TODO SHITMED: add AshStormImmune when transplanted
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
- type: entity
|
||||||
|
save: false
|
||||||
|
parent: MobReptilian
|
||||||
|
id: MobAshWalker
|
||||||
|
name: Urist McAsh
|
||||||
|
suffix: ""
|
||||||
|
components:
|
||||||
|
- type: Body
|
||||||
|
prototype: AshWalker
|
||||||
|
- type: AshStormImmune
|
||||||
|
# TODO: shitmed stuff so you can steal ashwalker lungs
|
||||||
@@ -8,6 +8,12 @@
|
|||||||
params:
|
params:
|
||||||
loop: true
|
loop: true
|
||||||
volume: -6
|
volume: -6
|
||||||
|
damage: # DeltaV
|
||||||
|
types:
|
||||||
|
Heat: 4
|
||||||
|
damageBlacklist: # DeltaV
|
||||||
|
components:
|
||||||
|
- AshStormImmune
|
||||||
|
|
||||||
- type: weather
|
- type: weather
|
||||||
id: AshfallLight
|
id: AshfallLight
|
||||||
|
|||||||
Reference in New Issue
Block a user