mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-09-30 16:17:27 +00:00
+24









Debug
deltanedas
metalgearsloth
Nemanja
PJBot
Psychpsyo
deltanedas
metalgearsloth
ravage
Pieter-Jan Briers
LEVELcat
Repo
nikthechampiongr
Doru991
DrSmugleaf
Skarletto
NULL882
lunarcomets
Miro Kavaliou
Kara
Kacper Urbańczyk
TsjipTsjip
daerSeebaer
Fluffiest Floofers
Flareguy
Leon Friedrich
Kevin Zheng
drteaspoon420
Ubaser
Nim
Kacper Urbańczyk
Tox Cruize
Ed
Michael Cu
fd2cc8570d
* Automatic changelog update * Make NPC names proper nouns & fix some genders (#20534) * Proper names & genders * Uppercase proper names * Make Smile female * fix bingus wrinkly head (#20531) * rouny meat and steak (#20526) * lost friendship flavor * add rouny steak * rouny special meat * rouny meat textures --------- Co-authored-by: deltanedas <@deltanedas:kde.org> * Automatic changelog update * Wearable Wet Floor Sign and 'Janitorial Suicide Vest' (#20311) * Explosive wet floor sign & janitorial suicide vest * fix attributions * Remove name & desc from explosive wet floor sign * Make wet floor sign chameleonable * Automatic changelog update * Minor slippery stuff (#20535) * Update submodule to 162.2.0 (#20570) * Predicted armor (#20560) * clean up some lines in smile the slime prototype (#20552) * Revert "Use full file path for temp replays (#19002)" (#20545) * Add EyesGlasses into ClothesMate (#20523) * Automatic changelog update * Fix Punpun crew monitor sensor (#20484) * Automatic changelog update * EasyPry airlocks for arrivals. Now also prying refactor I guess (#19394) Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> * Automatic changelog update * Make arcades hackable again (#20555) * Automatic changelog update * Health alert tweaks (#20557) * Automatic changelog update * Add active and ended game rule components, generic TryRoundStartAttempt and minPlayers field (#20564) * Improve active game rule querying, add generic try round start attempt method, move minPlayers to GameRuleComponent * Nukeops todo and cleanup * Remove Active field * Add EndedGameRuleComponent * bartender suit (#20521) * give me a drink bartender * guh * aARG * Necropolis and mine walls (#20578) * Automatic changelog update * archaic accent tweaks (#20567) * Automatic changelog update * Add confirmation to kick and respawn in the admin player actions panel (#20542) * Remove windows-latest CI runs, remove unused build-test-release.yml (#20540) * Change .editorconfig to keep existing attribute arrangement (#20538) * Automatic changelog update * Catch replay start and end errors on round restarts (#20565) * Update trivial components to use auto comp states (#20539) * Fix role unbans not applying in real time (#20547) * Disable AHelp buttons when no player is selected, update button styling (#20568) * Automatic changelog update * Added a toggle fullscreen button (default F11) (#20272) * Added a toggle fullscreen button (default F11) * Removed un-needed comments * Review Requested Changes * Fixed Acidental Spacing Change * bwoink, removed extraneous code * nothing, litterally * Automatic changelog update * Fix not networking markings (#20588) * Automatic changelog update * Update RobustToolbox to v162.2.1 (#20590) * Automatic changelog update * Glorfcode (force say on damage/stun/crit) (#20562) * Automatic changelog update * Fix AHelp progressively showing more AHelp panels (#20591) * Automatic changelog update * caninsert entitystorage tweaks (#20589) * Automatic changelog update * Fix mapping actions crashing on load (#20592) * Fix loading a map with a ThirstComponent crashing the game (#20594) * Fix electrocution displaying real name of disguised players (#20603) * Organ fixes (#20488) * Automatic changelog update * Kettle medical rework (#20435) * Initial try at medical rework for kettle * Re-add decal decorations to Kettle's medical * Add more lockers to kettle's med * Actually upload kettle's map, not its proto * Automatic changelog update * Added blocked visuals to volumetric pump (#20610) * Automatic changelog update * Move TimedDespawn to engine (#20515) * Update submodule to 163.0.0 (#20616) * Remove v0.1 version number from local main menu screen (#20617) * Remove cloneData parameter from AutoNetworkedField (#20596) * Update submodule to 164.0.0 (#20618) * fix cognizine ghost role (#20632) Co-authored-by: deltanedas <@deltanedas:kde.org> * Automatic changelog update * Move ID layer one pixel to correct alignment (#20630) * Update nukie hardsuit descriptions (#20529) * Make holofans destructable (#20445) * Automatic changelog update * bowl is open (#20453) Co-authored-by: deltanedas <@deltanedas:kde.org> * Add TestPair.WaitCommand() (#20615) * Rename ThreatPrototype and mark fields as required (#20611) * Adjust hard bomb shape (#20608) * dragon refactor, objectives and use GenericAntag (#20201) Co-authored-by: deltanedas <@deltanedas:kde.org> * Automatic changelog update * Added generic empty liquids tank (#20563) * Move view variables verb to the top of the list with no category and localize it (#20546) * saltern update (#20325) Co-authored-by: deltanedas <@deltanedas:kde.org> * Fix followers leaking (#20643) * Add Winter Boots (#20622) * add * fix prototype * Add QM mantle (#20621) * add * fix prototype * Automatic changelog update * Slime mobs breathe nitrogen and resprite their organs (#20577) * Slimes breathe nitrogen and resprite their organs * ups * mmm * Automatic changelog update * Fix the new lizard horn's consistency + issue (#20620) * fix * add * Revert "add" This reverts commit a054a3204a8f185a94ceb80b1bd3bc9f30423711. * Add RandomHumanoidAppearance component to for space ninjas (#20605) * Automatic changelog update * Space cat breathes space (#20550) * Space cat breathes space Made Space Cat lungs ROBUST * Made Space Cat's lungs ROBUST 2.0 * Automatic changelog update * Wide anomaly locator (#20581) * Add files via upload * Add files via upload * Add files via upload * Add files via upload * add textures * fix encoding * fix 2 * Automatic changelog update * Fixed Telescopic Shield Lighting (#20650) * Fixed Telescopic Shield Lighting Bug fix #20199 * no need for these at all --------- Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> * Automatic changelog update * Add a special hardsuit for nukie medic (#20471) * Automatic changelog update * Carry over other mutations when doing species mutation (#20551) * Automatic changelog update * Add Spanish accent to poncho and sombrero (#20377) --------- Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com> Co-authored-by: Psychpsyo <60073468+Psychpsyo@users.noreply.github.com> Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com> Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Co-authored-by: ravage <142820619+ravage123321@users.noreply.github.com> Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com> Co-authored-by: LEVELcat <68501903+LEVELcat@users.noreply.github.com> Co-authored-by: Repo <47093363+Titian3@users.noreply.github.com> Co-authored-by: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> Co-authored-by: Doru991 <75124791+Doru991@users.noreply.github.com> Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com> Co-authored-by: Skarletto <122584947+Skarletto@users.noreply.github.com> Co-authored-by: NULL882 <104377798+NULL882@users.noreply.github.com> Co-authored-by: lunarcomets <140772713+lunarcomets@users.noreply.github.com> Co-authored-by: Miro Kavaliou <miraslauk@gmail.com> Co-authored-by: Kara <lunarautomaton6@gmail.com> Co-authored-by: Kacper Urbańczyk <kacperjaroslawurbanczyk@gmail.com> Co-authored-by: TsjipTsjip <19798667+TsjipTsjip@users.noreply.github.com> Co-authored-by: daerSeebaer <61566539+daerSeebaer@users.noreply.github.com> Co-authored-by: Fluffiest Floofers <thebluewulf@gmail.com> Co-authored-by: Flareguy <78941145+Flareguy@users.noreply.github.com> Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Co-authored-by: Kevin Zheng <kevinz5000@gmail.com> Co-authored-by: drteaspoon420 <87363733+drteaspoon420@users.noreply.github.com> Co-authored-by: Ubaser <134914314+UbaserB@users.noreply.github.com> Co-authored-by: Nim <128169402+Nimfar11@users.noreply.github.com> Co-authored-by: Kacper Urbańczyk <mikrel071204@gmail.com> Co-authored-by: Tox Cruize <141375638+TexCruize@users.noreply.github.com> Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com> Co-authored-by: Michael Cu <43478115+michaelcu@users.noreply.github.com>
506 lines
20 KiB
C#
506 lines
20 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using Content.Shared.Alert;
|
|
using Content.Shared.Mobs;
|
|
using Content.Shared.Mobs.Components;
|
|
using Content.Shared.Mobs.Systems;
|
|
using Content.Shared.Rejuvenate;
|
|
using Robust.Shared.GameStates;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Timing;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Shared.StatusEffect
|
|
{
|
|
public sealed class StatusEffectsSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
UpdatesOutsidePrediction = true;
|
|
|
|
SubscribeLocalEvent<StatusEffectsComponent, ComponentGetState>(OnGetState);
|
|
SubscribeLocalEvent<StatusEffectsComponent, ComponentHandleState>(OnHandleState);
|
|
SubscribeLocalEvent<StatusEffectsComponent, RejuvenateEvent>(OnRejuvenate);
|
|
}
|
|
|
|
public override void Update(float frameTime)
|
|
{
|
|
base.Update(frameTime);
|
|
|
|
var curTime = _gameTiming.CurTime;
|
|
var enumerator = EntityQueryEnumerator<ActiveStatusEffectsComponent, StatusEffectsComponent>();
|
|
|
|
while (enumerator.MoveNext(out _, out var status))
|
|
{
|
|
foreach (var state in status.ActiveEffects.ToArray())
|
|
{
|
|
// if we're past the end point of the effect
|
|
if (curTime > state.Value.Cooldown.Item2)
|
|
{
|
|
TryRemoveStatusEffect(status.Owner, state.Key, status);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnGetState(EntityUid uid, StatusEffectsComponent component, ref ComponentGetState args)
|
|
{
|
|
// Using new(...) To avoid mispredictions due to MergeImplicitData. This will mean the server-side code is
|
|
// slightly slower, and really this function should just be overridden by the client...
|
|
args.State = new StatusEffectsComponentState(new(component.ActiveEffects), new(component.AllowedEffects));
|
|
}
|
|
|
|
private void OnHandleState(EntityUid uid, StatusEffectsComponent component, ref ComponentHandleState args)
|
|
{
|
|
if (args.Current is not StatusEffectsComponentState state)
|
|
return;
|
|
|
|
component.AllowedEffects.Clear();
|
|
component.AllowedEffects.AddRange(state.AllowedEffects);
|
|
|
|
// Remove non-existent effects.
|
|
foreach (var effect in component.ActiveEffects.Keys)
|
|
{
|
|
if (!state.ActiveEffects.ContainsKey(effect))
|
|
{
|
|
TryRemoveStatusEffect(uid, effect, component, remComp: false);
|
|
}
|
|
}
|
|
|
|
foreach (var (key, effect) in state.ActiveEffects)
|
|
{
|
|
// don't bother with anything if we already have it
|
|
if (component.ActiveEffects.ContainsKey(key))
|
|
{
|
|
component.ActiveEffects[key] = new(effect);
|
|
continue;
|
|
}
|
|
|
|
var time = effect.Cooldown.Item2 - effect.Cooldown.Item1;
|
|
|
|
TryAddStatusEffect(uid, key, time, true, component, effect.Cooldown.Item1);
|
|
component.ActiveEffects[key].RelevantComponent = effect.RelevantComponent;
|
|
// state handling should not add networked components, that is handled separately by the client game state manager.
|
|
}
|
|
}
|
|
|
|
private void OnRejuvenate(EntityUid uid, StatusEffectsComponent component, RejuvenateEvent args)
|
|
{
|
|
TryRemoveAllStatusEffects(uid, component);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to add a status effect to an entity, with a given component added as well.
|
|
/// </summary>
|
|
/// <param name="uid">The entity to add the effect to.</param>
|
|
/// <param name="key">The status effect ID to add.</param>
|
|
/// <param name="time">How long the effect should last for.</param>
|
|
/// <param name="refresh">The status effect cooldown should be refreshed (true) or accumulated (false).</param>
|
|
/// <param name="status">The status effects component to change, if you already have it.</param>
|
|
/// <returns>False if the effect could not be added or the component already exists, true otherwise.</returns>
|
|
/// <typeparam name="T">The component type to add and remove from the entity.</typeparam>
|
|
public bool TryAddStatusEffect<T>(EntityUid uid, string key, TimeSpan time, bool refresh,
|
|
StatusEffectsComponent? status = null)
|
|
where T : Component, new()
|
|
{
|
|
if (!Resolve(uid, ref status, false))
|
|
return false;
|
|
|
|
if (TryAddStatusEffect(uid, key, time, refresh, status))
|
|
{
|
|
// If they already have the comp, we just won't bother updating anything.
|
|
if (!EntityManager.HasComponent<T>(uid))
|
|
{
|
|
var comp = EntityManager.AddComponent<T>(uid);
|
|
status.ActiveEffects[key].RelevantComponent = _componentFactory.GetComponentName(comp.GetType());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh, string component,
|
|
StatusEffectsComponent? status = null)
|
|
{
|
|
if (!Resolve(uid, ref status, false))
|
|
return false;
|
|
|
|
if (TryAddStatusEffect(uid, key, time, refresh, status))
|
|
{
|
|
// If they already have the comp, we just won't bother updating anything.
|
|
if (!EntityManager.HasComponent(uid, _componentFactory.GetRegistration(component).Type))
|
|
{
|
|
// Fuck this shit I hate it
|
|
var newComponent = (Component) _componentFactory.GetComponent(component);
|
|
newComponent.Owner = uid;
|
|
|
|
EntityManager.AddComponent(uid, newComponent);
|
|
status.ActiveEffects[key].RelevantComponent = component;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to add a status effect to an entity with a certain timer.
|
|
/// </summary>
|
|
/// <param name="uid">The entity to add the effect to.</param>
|
|
/// <param name="key">The status effect ID to add.</param>
|
|
/// <param name="time">How long the effect should last for.</param>
|
|
/// <param name="refresh">The status effect cooldown should be refreshed (true) or accumulated (false).</param>
|
|
/// <param name="status">The status effects component to change, if you already have it.</param>
|
|
/// <param name="startTime">The time at which the status effect started. This exists mostly for prediction
|
|
/// resetting.</param>
|
|
/// <returns>False if the effect could not be added, or if the effect already existed.</returns>
|
|
/// <remarks>
|
|
/// This obviously does not add any actual 'effects' on its own. Use the generic overload,
|
|
/// which takes in a component type, if you want to automatically add and remove a component.
|
|
///
|
|
/// If the effect already exists, it will simply replace the cooldown with the new one given.
|
|
/// If you want special 'effect merging' behavior, do it your own damn self!
|
|
/// </remarks>
|
|
public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh,
|
|
StatusEffectsComponent? status = null, TimeSpan? startTime = null)
|
|
{
|
|
if (!Resolve(uid, ref status, false))
|
|
return false;
|
|
if (!CanApplyEffect(uid, key, status))
|
|
return false;
|
|
|
|
// we already checked if it has the index in CanApplyEffect so a straight index and not tryindex here
|
|
// is fine
|
|
var proto = _prototypeManager.Index<StatusEffectPrototype>(key);
|
|
|
|
var start = startTime ?? _gameTiming.CurTime;
|
|
(TimeSpan, TimeSpan) cooldown = (start, start + time);
|
|
|
|
if (HasStatusEffect(uid, key, status))
|
|
{
|
|
status.ActiveEffects[key].CooldownRefresh = refresh;
|
|
if (refresh)
|
|
{
|
|
//Making sure we don't reset a longer cooldown by applying a shorter one.
|
|
if ((status.ActiveEffects[key].Cooldown.Item2 - _gameTiming.CurTime) < time)
|
|
{
|
|
//Refresh cooldown time.
|
|
status.ActiveEffects[key].Cooldown = cooldown;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Accumulate cooldown time.
|
|
status.ActiveEffects[key].Cooldown.Item2 += time;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status.ActiveEffects.Add(key, new StatusEffectState(cooldown, refresh, null));
|
|
EnsureComp<ActiveStatusEffectsComponent>(uid);
|
|
}
|
|
|
|
if (proto.Alert != null)
|
|
{
|
|
var cooldown1 = GetAlertCooldown(uid, proto.Alert.Value, status);
|
|
_alertsSystem.ShowAlert(uid, proto.Alert.Value, null, cooldown1);
|
|
}
|
|
|
|
Dirty(status);
|
|
RaiseLocalEvent(uid, new StatusEffectAddedEvent(uid, key));
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the maximum cooldown among all status effects with the same alert
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This is mostly for stuns, since Stun and Knockdown share an alert key. Other times this pretty much
|
|
/// will not be useful.
|
|
/// </remarks>
|
|
private (TimeSpan, TimeSpan)? GetAlertCooldown(EntityUid uid, AlertType alert, StatusEffectsComponent status)
|
|
{
|
|
(TimeSpan, TimeSpan)? maxCooldown = null;
|
|
foreach (var kvp in status.ActiveEffects)
|
|
{
|
|
var proto = _prototypeManager.Index<StatusEffectPrototype>(kvp.Key);
|
|
|
|
if (proto.Alert == alert)
|
|
{
|
|
if (maxCooldown == null || kvp.Value.Cooldown.Item2 > maxCooldown.Value.Item2)
|
|
{
|
|
maxCooldown = kvp.Value.Cooldown;
|
|
}
|
|
}
|
|
}
|
|
|
|
return maxCooldown;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempts to remove a status effect from an entity.
|
|
/// </summary>
|
|
/// <param name="uid">The entity to remove an effect from.</param>
|
|
/// <param name="key">The effect ID to remove.</param>
|
|
/// <param name="status">The status effects component to change, if you already have it.</param>
|
|
/// <param name="remComp">If true, status effect removal will also remove the relevant component. This option
|
|
/// exists mostly for prediction resetting.</param>
|
|
/// <returns>False if the effect could not be removed, true otherwise.</returns>
|
|
/// <remarks>
|
|
/// Obviously this doesn't automatically clear any effects a status effect might have.
|
|
/// That's up to the removed component to handle itself when it's removed.
|
|
/// </remarks>
|
|
public bool TryRemoveStatusEffect(EntityUid uid, string key,
|
|
StatusEffectsComponent? status = null, bool remComp = true)
|
|
{
|
|
if (!Resolve(uid, ref status, false))
|
|
return false;
|
|
if (!status.ActiveEffects.ContainsKey(key))
|
|
return false;
|
|
if (!_prototypeManager.TryIndex<StatusEffectPrototype>(key, out var proto))
|
|
return false;
|
|
|
|
var state = status.ActiveEffects[key];
|
|
|
|
// There are cases where a status effect component might be server-only, so TryGetRegistration...
|
|
if (remComp
|
|
&& state.RelevantComponent != null
|
|
&& _componentFactory.TryGetRegistration(state.RelevantComponent, out var registration))
|
|
{
|
|
var type = registration.Type;
|
|
EntityManager.RemoveComponent(uid, type);
|
|
}
|
|
|
|
if (proto.Alert != null)
|
|
{
|
|
_alertsSystem.ClearAlert(uid, proto.Alert.Value);
|
|
}
|
|
|
|
status.ActiveEffects.Remove(key);
|
|
if (status.ActiveEffects.Count == 0)
|
|
{
|
|
RemComp<ActiveStatusEffectsComponent>(uid);
|
|
}
|
|
|
|
Dirty(status);
|
|
RaiseLocalEvent(uid, new StatusEffectEndedEvent(uid, key));
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to remove all status effects from a given entity.
|
|
/// </summary>
|
|
/// <param name="uid">The entity to remove effects from.</param>
|
|
/// <param name="status">The status effects component to change, if you already have it.</param>
|
|
/// <returns>False if any status effects failed to be removed, true if they all did.</returns>
|
|
public bool TryRemoveAllStatusEffects(EntityUid uid,
|
|
StatusEffectsComponent? status = null)
|
|
{
|
|
if (!Resolve(uid, ref status, false))
|
|
return false;
|
|
|
|
bool failed = false;
|
|
foreach (var effect in status.ActiveEffects)
|
|
{
|
|
if (!TryRemoveStatusEffect(uid, effect.Key, status))
|
|
failed = true;
|
|
}
|
|
|
|
Dirty(status);
|
|
return failed;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns whether a given entity has the status effect active.
|
|
/// </summary>
|
|
/// <param name="uid">The entity to check on.</param>
|
|
/// <param name="key">The status effect ID to check for</param>
|
|
/// <param name="status">The status effect component, should you already have it.</param>
|
|
public bool HasStatusEffect(EntityUid uid, string key,
|
|
StatusEffectsComponent? status = null)
|
|
{
|
|
if (!Resolve(uid, ref status, false))
|
|
return false;
|
|
if (!status.ActiveEffects.ContainsKey(key))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns whether a given entity can have a given effect applied to it.
|
|
/// </summary>
|
|
/// <param name="uid">The entity to check on.</param>
|
|
/// <param name="key">The status effect ID to check for</param>
|
|
/// <param name="status">The status effect component, should you already have it.</param>
|
|
public bool CanApplyEffect(EntityUid uid, string key,
|
|
StatusEffectsComponent? status = null)
|
|
{
|
|
// don't log since stuff calling this prolly doesn't care if we don't actually have it
|
|
if (!Resolve(uid, ref status, false))
|
|
return false;
|
|
|
|
var ev = new BeforeStatusEffectAddedEvent(key);
|
|
RaiseLocalEvent(uid, ref ev);
|
|
if (ev.Cancelled)
|
|
return false;
|
|
|
|
if (!_prototypeManager.TryIndex<StatusEffectPrototype>(key, out var proto))
|
|
return false;
|
|
if (!status.AllowedEffects.Contains(key) && !proto.AlwaysAllowed)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to add to the timer of an already existing status effect.
|
|
/// </summary>
|
|
/// <param name="uid">The entity to add time to.</param>
|
|
/// <param name="key">The status effect to add time to.</param>
|
|
/// <param name="time">The amount of time to add.</param>
|
|
/// <param name="status">The status effect component, should you already have it.</param>
|
|
public bool TryAddTime(EntityUid uid, string key, TimeSpan time,
|
|
StatusEffectsComponent? status = null)
|
|
{
|
|
if (!Resolve(uid, ref status, false))
|
|
return false;
|
|
|
|
if (!HasStatusEffect(uid, key, status))
|
|
return false;
|
|
|
|
var timer = status.ActiveEffects[key].Cooldown;
|
|
timer.Item2 += time;
|
|
status.ActiveEffects[key].Cooldown = timer;
|
|
|
|
if (_prototypeManager.TryIndex<StatusEffectPrototype>(key, out var proto)
|
|
&& proto.Alert != null)
|
|
{
|
|
(TimeSpan, TimeSpan)? cooldown = GetAlertCooldown(uid, proto.Alert.Value, status);
|
|
_alertsSystem.ShowAlert(uid, proto.Alert.Value, null, cooldown);
|
|
}
|
|
|
|
Dirty(status);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tries to remove time from the timer of an already existing status effect.
|
|
/// </summary>
|
|
/// <param name="uid">The entity to remove time from.</param>
|
|
/// <param name="key">The status effect to remove time from.</param>
|
|
/// <param name="time">The amount of time to add.</param>
|
|
/// <param name="status">The status effect component, should you already have it.</param>
|
|
public bool TryRemoveTime(EntityUid uid, string key, TimeSpan time,
|
|
StatusEffectsComponent? status = null)
|
|
{
|
|
if (!Resolve(uid, ref status, false))
|
|
return false;
|
|
|
|
if (!HasStatusEffect(uid, key, status))
|
|
return false;
|
|
|
|
var timer = status.ActiveEffects[key].Cooldown;
|
|
|
|
// what on earth are you doing, Gordon?
|
|
if (time > timer.Item2)
|
|
return false;
|
|
|
|
timer.Item2 -= time;
|
|
status.ActiveEffects[key].Cooldown = timer;
|
|
|
|
if (_prototypeManager.TryIndex<StatusEffectPrototype>(key, out var proto)
|
|
&& proto.Alert != null)
|
|
{
|
|
(TimeSpan, TimeSpan)? cooldown = GetAlertCooldown(uid, proto.Alert.Value, status);
|
|
_alertsSystem.ShowAlert(uid, proto.Alert.Value, null, cooldown);
|
|
}
|
|
|
|
Dirty(status);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use if you want to set a cooldown directly.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Not used internally; just sets it itself.
|
|
/// </remarks>
|
|
public bool TrySetTime(EntityUid uid, string key, TimeSpan time,
|
|
StatusEffectsComponent? status = null)
|
|
{
|
|
if (!Resolve(uid, ref status, false))
|
|
return false;
|
|
|
|
if (!HasStatusEffect(uid, key, status))
|
|
return false;
|
|
|
|
status.ActiveEffects[key].Cooldown = (_gameTiming.CurTime, _gameTiming.CurTime + time);
|
|
|
|
Dirty(status);
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the cooldown for a given status effect on an entity.
|
|
/// </summary>
|
|
/// <param name="uid">The entity to check for status effects on.</param>
|
|
/// <param name="key">The status effect to get time for.</param>
|
|
/// <param name="time">Out var for the time, if it exists.</param>
|
|
/// <param name="status">The status effects component to use, if any.</param>
|
|
/// <returns>False if the status effect was not active, true otherwise.</returns>
|
|
public bool TryGetTime(EntityUid uid, string key,
|
|
[NotNullWhen(true)] out (TimeSpan, TimeSpan)? time,
|
|
StatusEffectsComponent? status = null)
|
|
{
|
|
if (!Resolve(uid, ref status, false) || !HasStatusEffect(uid, key, status))
|
|
{
|
|
time = null;
|
|
return false;
|
|
}
|
|
|
|
time = status.ActiveEffects[key].Cooldown;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raised on an entity before a status effect is added to determine if adding it should be cancelled.
|
|
/// </summary>
|
|
[ByRefEvent]
|
|
public record struct BeforeStatusEffectAddedEvent(string Key, bool Cancelled=false);
|
|
|
|
public readonly struct StatusEffectAddedEvent
|
|
{
|
|
public readonly EntityUid Uid;
|
|
|
|
public readonly string Key;
|
|
|
|
public StatusEffectAddedEvent(EntityUid uid, string key)
|
|
{
|
|
Uid = uid;
|
|
Key = key;
|
|
}
|
|
}
|
|
|
|
public readonly struct StatusEffectEndedEvent
|
|
{
|
|
public readonly EntityUid Uid;
|
|
|
|
public readonly string Key;
|
|
|
|
public StatusEffectEndedEvent(EntityUid uid, string key)
|
|
{
|
|
Uid = uid;
|
|
Key = key;
|
|
}
|
|
}
|
|
}
|