Files
wwdpublic/Content.Server/Medical/CPR/CPRSystem.cs
sleepyyapril 67ea5d8c3e Station AI Features and Fixes (Also General Fixes) (#1525)
<!--
This is a semi-strict format, you can add/remove sections as needed but
the order/format should be kept the same
Remove these comments before submitting
-->

# Description

<!--
Explain this PR in as much detail as applicable

Some example prompts to consider:
How might this affect the game? The codebase?
What might be some alternatives to this?
How/Who does this benefit/hurt [the game/codebase]?
-->

Check the changelog for the full list.

---

# Changelog

<!--
You can add an author after the `🆑` to change the name that appears
in the changelog (ex: `🆑 Death`)
Leaving it blank will default to your GitHub display name
This includes all available types for the changelog
-->

🆑
- add: Added Holopads (unmapped)
- add: Intellicards are now useful for removing/adding a Station AI's
brain.
- add: Added the Communications Console to Station AI actions.
- add: AI now has a warp point.
- add: Added more things for the AI to press.
- add: More AI laws have been added.
- fix: Fixed the mail system
- fix: Fixed AI actions
- fix: Fixed invalid spawns for station AI breaking and ruining your
ability to play it.
- fix: The Station AI's name will now properly send in "arrived to the
station" announcements.
- fix: Changed the CPR sound to simply not loop until fixed.
- fix: Fixed unlocalized messages being sent for the random sentience
event.

---------

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com>
Co-authored-by: Zachary Higgs <compgeek223@gmail.com>
Co-authored-by: MendaxxDev <153332064+MendaxxDev@users.noreply.github.com>
Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com>
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

(cherry picked from commit 3e8a7d9b00e19e160321eb81d69a884189dfa4e6)
2025-01-15 00:12:29 +03:00

125 lines
5.0 KiB
C#

using Content.Server.Atmos.Rotting;
using Content.Server.DoAfter;
using Content.Server.Nutrition.EntitySystems;
using Content.Server.Popups;
using Content.Shared.Atmos.Rotting;
using Content.Shared.Damage;
using Content.Shared.DoAfter;
using Content.Shared.Inventory;
using Content.Shared.Medical;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Verbs;
using Robust.Server.Audio;
using Robust.Shared.Audio;
using Robust.Shared.Random;
using Robust.Shared.Utility;
namespace Content.Server.Medical.CPR;
public sealed class CPRSystem : EntitySystem
{
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly FoodSystem _foodSystem = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly RottingSystem _rottingSystem = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly AudioSystem _audio = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CPRTrainingComponent, GetVerbsEvent<InnateVerb>>(AddCPRVerb);
SubscribeLocalEvent<CPRTrainingComponent, CPRDoAfterEvent>(OnCPRDoAfter);
}
private void AddCPRVerb(Entity<CPRTrainingComponent> performer, ref GetVerbsEvent<InnateVerb> args)
{
if (!args.CanInteract || !args.CanAccess || !TryComp<MobStateComponent>(args.Target, out var targetState)
|| targetState.CurrentState == MobState.Alive)
return;
var target = args.Target;
InnateVerb verb = new()
{
Act = () => { StartCPR(performer, target); },
Text = Loc.GetString("cpr-verb"),
Icon = new SpriteSpecifier.Rsi(new("Interface/Alerts/human_alive.rsi"), "health4"),
Priority = 2
};
args.Verbs.Add(verb);
}
private void StartCPR(Entity<CPRTrainingComponent> performer, EntityUid target)
{
if (HasComp<RottingComponent>(target))
{
_popupSystem.PopupEntity(Loc.GetString("cpr-target-rotting", ("entity", target)), performer, performer);
return;
}
if (_inventory.TryGetSlotEntity(target, "outerClothing", out var outer))
{
_popupSystem.PopupEntity(Loc.GetString("cpr-must-remove", ("clothing", outer)), performer, performer);
return;
}
if (_foodSystem.IsMouthBlocked(performer, performer) || _foodSystem.IsMouthBlocked(target, performer))
return;
_popupSystem.PopupEntity(Loc.GetString("cpr-start-second-person", ("target", target)), target, performer);
_popupSystem.PopupEntity(Loc.GetString("cpr-start-second-person-patient", ("user", performer)), target, target);
var doAfterArgs = new DoAfterArgs(
EntityManager, performer, performer.Comp.DoAfterDuration, new CPRDoAfterEvent(), performer, target,
performer)
{
BreakOnMove = true,
NeedHand = true,
BlockDuplicate = true
};
_doAfterSystem.TryStartDoAfter(doAfterArgs);
var playingStream = _audio.PlayPvs(performer.Comp.CPRSound, performer, AudioParams.Default);
if (!playingStream.HasValue)
return;
performer.Comp.CPRPlayingStream = playingStream.Value.Entity;
}
private void OnCPRDoAfter(Entity<CPRTrainingComponent> performer, ref CPRDoAfterEvent args)
{
if (args.Cancelled || args.Handled || !args.Target.HasValue)
{
performer.Comp.CPRPlayingStream = _audio.Stop(performer.Comp.CPRPlayingStream);
return;
}
if (!performer.Comp.CPRHealing.Empty)
_damageable.TryChangeDamage(args.Target, performer.Comp.CPRHealing, true, origin: performer);
if (performer.Comp.RotReductionMultiplier > 0)
_rottingSystem.ReduceAccumulator(
(EntityUid)args.Target, performer.Comp.DoAfterDuration * performer.Comp.RotReductionMultiplier);
if (_robustRandom.Prob(performer.Comp.ResuscitationChance)
&& _mobThreshold.TryGetThresholdForState((EntityUid)args.Target, MobState.Dead, out var threshold)
&& TryComp<DamageableComponent>(args.Target, out var damageableComponent)
&& TryComp<MobStateComponent>(args.Target, out var state)
&& damageableComponent.TotalDamage < threshold)
_mobStateSystem.ChangeMobState(args.Target.Value, MobState.Critical, state, performer);
var isAlive = _mobStateSystem.IsAlive(args.Target.Value);
args.Repeat = !isAlive;
if (isAlive)
performer.Comp.CPRPlayingStream = _audio.Stop(performer.Comp.CPRPlayingStream);
}
}