diff --git a/Content.Client/Revenant/RevenantStasisComponent.cs b/Content.Client/Revenant/RevenantStasisComponent.cs new file mode 100644 index 000000000000..919b35d2eb1c --- /dev/null +++ b/Content.Client/Revenant/RevenantStasisComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Client.Revenant; + +[RegisterComponent] +[NetworkedComponent] +public sealed partial class RevenantStasisComponent : Component +{ +} \ No newline at end of file diff --git a/Content.Client/Revenant/RevenantStasisSystem.cs b/Content.Client/Revenant/RevenantStasisSystem.cs new file mode 100644 index 000000000000..b48f7c753560 --- /dev/null +++ b/Content.Client/Revenant/RevenantStasisSystem.cs @@ -0,0 +1,25 @@ +using Content.Shared.Examine; +using Content.Shared.Interaction.Events; + +namespace Content.Client.Revenant; + +public sealed partial class RevenantStasisSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAttemptDirection); + SubscribeLocalEvent(OnExamine); + } + + private void OnExamine(Entity entity, ref ExaminedEvent args) + { + args.PushMarkup(Loc.GetString("revenant-stasis-regenerating")); + } + + private void OnAttemptDirection(EntityUid uid, RevenantStasisComponent comp, ChangeDirectionAttemptEvent args) + { + args.Cancel(); + } +} diff --git a/Content.Server/Construction/ConstructionSystem.Initial.cs b/Content.Server/Construction/ConstructionSystem.Initial.cs index 6cc430b74f67..f6c82c2ff904 100644 --- a/Content.Server/Construction/ConstructionSystem.Initial.cs +++ b/Content.Server/Construction/ConstructionSystem.Initial.cs @@ -295,6 +295,12 @@ void ShutdownContainers() } } + // Inform consumed items that they have been consumed + foreach (var entity in container.ContainedEntities.ToArray()) + { + RaiseLocalEvent(entity, new ConstructionConsumedObjectEvent(entity, newEntity)); + } + // We now get rid of all them. ShutdownContainers(); diff --git a/Content.Server/Construction/ConstructionSystem.Interactions.cs b/Content.Server/Construction/ConstructionSystem.Interactions.cs index 426ab6e80fb9..7ecfec5783c5 100644 --- a/Content.Server/Construction/ConstructionSystem.Interactions.cs +++ b/Content.Server/Construction/ConstructionSystem.Interactions.cs @@ -628,4 +628,16 @@ public sealed class OnConstructionTemperatureEvent : HandledEntityEventArgs { public HandleResult? Result; } + + public sealed class ConstructionConsumedObjectEvent : EntityEventArgs + { + public EntityUid Old; + public EntityUid New; + + public ConstructionConsumedObjectEvent(EntityUid oldEnt, EntityUid newEnt) + { + Old = oldEnt; + New = newEnt; + } + } } diff --git a/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs b/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs index 71536ddf8ebe..e5dd154e44f3 100644 --- a/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/ReagentGrinderSystem.cs @@ -85,11 +85,14 @@ public override void Update(float frameTime) if (outputContainer is null || !_solutionContainersSystem.TryGetFitsInDispenser(outputContainer.Value, out var containerSoln, out var containerSolution)) continue; + List toDelete = new(); + List<(EntityUid, int)> toSet = new(); + foreach (var item in inputContainer.ContainedEntities.ToList()) { var solution = active.Program switch { - GrinderProgram.Grind => GetGrindSolution(item), + GrinderProgram.Grind => TryGrindSolution(item, (uid, reagentGrinder), inputContainer.ContainedEntities), GrinderProgram.Juice => CompOrNull(item)?.JuiceSolution, _ => null, }; @@ -115,19 +118,24 @@ public override void Update(float frameTime) scaledSolution.ScaleSolution(fitsCount); solution = scaledSolution; - _stackSystem.SetCount(item, stack.Count - fitsCount); // Setting to 0 will QueueDel + toSet.Add((item, stack.Count - fitsCount)); } else { if (solution.Volume > containerSolution.AvailableVolume) continue; - QueueDel(item); + toDelete.Add(item); } _solutionContainersSystem.TryAddSolution(containerSoln.Value, solution); } + foreach (var item in toDelete) + Del(item); + foreach (var (item, amount) in toSet) + _stackSystem.SetCount(item, amount); // Setting to 0 will QueueDel + _userInterfaceSystem.ServerSendUiMessage(uid, ReagentGrinderUiKey.Key, new ReagentGrinderWorkCompleteMessage()); @@ -315,12 +323,18 @@ private void ClickSound(Entity reagentGrinder) _audioSystem.PlayPvs(reagentGrinder.Comp.ClickSound, reagentGrinder.Owner, AudioParams.Default.WithVolume(-2f)); } - private Solution? GetGrindSolution(EntityUid uid) + private Solution? TryGrindSolution(EntityUid uid, Entity grinder, IReadOnlyList contents) { if (TryComp(uid, out var extractable) && extractable.GrindableSolution is not null && _solutionContainersSystem.TryGetSolution(uid, extractable.GrindableSolution, out _, out var solution)) { + var ev = new GrindAttemptEvent(grinder, contents); + RaiseLocalEvent(uid, ev); + + if (ev.Cancelled) + return null; + return solution; } else @@ -339,4 +353,16 @@ private bool CanJuice(EntityUid uid) return CompOrNull(uid)?.JuiceSolution is not null; } } + + public sealed partial class GrindAttemptEvent : CancellableEntityEventArgs + { + public Entity Grinder; + public IReadOnlyList Reagents; + + public GrindAttemptEvent(Entity grinder, IReadOnlyList reagents) + { + Grinder = grinder; + Reagents = reagents; + } + } } diff --git a/Content.Server/Revenant/Components/RevenantStatisComponent.cs b/Content.Server/Revenant/Components/RevenantStatisComponent.cs new file mode 100644 index 000000000000..b357f09ebfc9 --- /dev/null +++ b/Content.Server/Revenant/Components/RevenantStatisComponent.cs @@ -0,0 +1,23 @@ +using Content.Server.Revenant.EntitySystems; +using Content.Shared.Revenant.Components; +using Robust.Shared.GameStates; + +namespace Content.Server.Revenant.Components; + +[RegisterComponent] +[Access(typeof(RevenantStasisSystem))] +[NetworkedComponent] +public sealed partial class RevenantStasisComponent : Component +{ + [ViewVariables(VVAccess.ReadOnly)] + public Entity Revenant; + + [ViewVariables(VVAccess.ReadOnly)] + public TimeSpan StasisDuration = TimeSpan.FromSeconds(60); + + public RevenantStasisComponent(TimeSpan stasisDuration, Entity revenant) + { + StasisDuration = stasisDuration; + Revenant = revenant; + } +} diff --git a/Content.Server/Revenant/EntitySystems/RevenantAnimatedSystem.cs b/Content.Server/Revenant/EntitySystems/RevenantAnimatedSystem.cs index a8bda9cf2be0..c68be537fadd 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantAnimatedSystem.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantAnimatedSystem.cs @@ -73,7 +73,7 @@ private void OnComponentStartup(Entity ent, ref Compo if (HasComp(ent.Owner) && TryComp(ent.Owner, out var toggle)) _itemToggleSystem.TryActivate((ent.Owner, toggle)); - _popup.PopupEntity(Loc.GetString("revenant-animate-item-animate", ("name", Comp(ent.Owner).EntityName)), ent.Owner, Filter.Pvs(ent.Owner), true); + _popup.PopupEntity(Loc.GetString("revenant-animate-item-animate", ("target", ent.Owner)), ent.Owner, Filter.Pvs(ent.Owner), true); // Add melee damage if an item doesn't already have it if (EnsureHelper(ent, out var melee)) @@ -144,7 +144,7 @@ private void OnComponentShutdown(Entity ent, ref Comp RemCompDeferred(ent, comp); } - _popup.PopupEntity(Loc.GetString("revenant-animate-item-inanimate", ("name", Comp(ent).EntityName)), ent, Filter.Pvs(ent), true); + _popup.PopupEntity(Loc.GetString("revenant-animate-item-inanimate", ("target", ent)), ent, Filter.Pvs(ent), true); } private void OnMobStateChange(Entity ent, ref MobStateChangedEvent args) diff --git a/Content.Server/Revenant/EntitySystems/RevenantStasisSystem.cs b/Content.Server/Revenant/EntitySystems/RevenantStasisSystem.cs new file mode 100644 index 000000000000..d0885216f210 --- /dev/null +++ b/Content.Server/Revenant/EntitySystems/RevenantStasisSystem.cs @@ -0,0 +1,205 @@ +using Content.Server.Bible; +using Content.Server.Bible.Components; +using Content.Server.Construction; +using Content.Server.Explosion.EntitySystems; +using Content.Server.Ghost.Roles; +using Content.Server.Ghost.Roles.Components; +using Content.Server.Kitchen.EntitySystems; +using Content.Server.Mind; +using Content.Server.Revenant.Components; +using Content.Server.VoiceMask; +using Content.Shared.Alert; +using Content.Shared.DoAfter; +using Content.Shared.Examine; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.Movement.Components; +using Content.Shared.Popups; +using Content.Shared.Revenant; +using Content.Shared.Speech; +using Content.Shared.StatusEffect; +using Content.Shared.Tag; +using Robust.Shared.Player; + +namespace Content.Server.Revenant.EntitySystems; + +public sealed partial class RevenantStasisSystem : EntitySystem +{ + [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly SharedTransformSystem _transformSystem = default!; + [Dependency] private readonly GhostRoleSystem _ghostRoles = default!; + [Dependency] private readonly MetaDataSystem _meta = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly TagSystem _tags = default!; + [Dependency] private readonly ExplosionSystem _explosion = default!; + + [ValidatePrototypeId] + private const string RevenantStasisId = "Stasis"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnStatusEnded); + SubscribeLocalEvent(OnAttemptDirection); + SubscribeLocalEvent(OnExamine); + SubscribeLocalEvent(OnCrafted); + SubscribeLocalEvent(OnGrindAttempt); + + SubscribeLocalEvent(OnBibleInteract, before: [typeof(BibleSystem)]); + SubscribeLocalEvent(OnExorcise); + } + + private void OnStartup(EntityUid uid, RevenantStasisComponent component, ComponentStartup args) + { + EnsureComp(uid); + + EnsureComp(uid); + _statusEffects.TryAddStatusEffect(uid, RevenantStasisId, component.StasisDuration, true); + + var mover = EnsureComp(uid); + mover.CanMove = false; + Dirty(uid, mover); + + var speech = EnsureComp(uid); + speech.SpeechVerb = "Ghost"; + Dirty(uid, speech); + + var voice = EnsureComp(uid); + voice.VoiceName = Comp(component.Revenant).EntityName; + + if (TryComp(uid, out var ghostRole)) + _ghostRoles.UnregisterGhostRole((uid, ghostRole)); + } + + private void OnShutdown(EntityUid uid, RevenantStasisComponent component, ComponentShutdown args) + { + if (_statusEffects.HasStatusEffect(uid, RevenantStasisId)) + { + if (_mind.TryGetMind(uid, out var mindId, out var _)) + _mind.TransferTo(mindId, null); + QueueDel(component.Revenant); + } + } + + private void OnStatusEnded(EntityUid uid, RevenantStasisComponent component, StatusEffectEndedEvent args) + { + if (args.Key == "Stasis") + { + _transformSystem.SetCoordinates(component.Revenant, Transform(uid).Coordinates); + _transformSystem.AttachToGridOrMap(component.Revenant); + _meta.SetEntityPaused(component.Revenant, false); + if (_mind.TryGetMind(uid, out var mindId, out var _)) + _mind.TransferTo(mindId, component.Revenant); + QueueDel(uid); + } + } + + private void OnExamine(Entity entity, ref ExaminedEvent args) + { + args.PushMarkup(Loc.GetString("revenant-stasis-regenerating")); + } + + private void OnCrafted(EntityUid uid, RevenantStasisComponent comp, ConstructionConsumedObjectEvent args) + { + // Permanently sealed into revenant plushie + + var speech = EnsureComp(args.New); + speech.SpeechVerb = "Ghost"; + Dirty(args.New, speech); + + EnsureComp(args.New); + + var voice = EnsureComp(args.New); + voice.VoiceName = Comp(comp.Revenant).EntityName; + + if (_mind.TryGetMind(uid, out var mindId, out var _)) + _mind.TransferTo(mindId, args.New); + } + + private void OnGrindAttempt(EntityUid uid, RevenantStasisComponent comp, GrindAttemptEvent args) + { + foreach (var reagent in args.Reagents) + { + if (_tags.HasAnyTag(reagent, "Salt", "Holy")) + return; + } + + // Ripped off the changeling blood explosion variables + _explosion.QueueExplosion( + args.Grinder.Owner, + "Default", + 7.5f, // totalIntensity + 4f, // slope + 2f // maxTileIntensity + ); + + args.Cancel(); + } + + private void OnAttemptDirection(EntityUid uid, RevenantStasisComponent comp, ChangeDirectionAttemptEvent args) + { + args.Cancel(); + } + + private void OnBibleInteract(EntityUid uid, RevenantStasisComponent comp, ref AfterInteractUsingEvent args) + { + if (args.Handled) + return; + if (args.Target == null) + return; + var bible = args.Used; + var target = args.Target.Value; + var user = args.User; + if (!HasComp(args.Used)) + return; + + if (!TryComp(target, out var stasis)) + return; + + var revenant = stasis.Revenant; + + if (revenant.Comp.ExorcismRequiresBibleUser && !HasComp(args.User)) + { + _popup.PopupEntity(Loc.GetString("revenant-exorcise-fail", ("bible", bible)), user, user); + return; + } + + var doAfterEventArgs = new DoAfterArgs(EntityManager, user, TimeSpan.FromSeconds(10), new ExorciseRevenantDoAfterEvent(), target, target, bible) + { + BreakOnMove = true, + BreakOnWeightlessMove = false, + BreakOnDamage = true, + NeedHand = true, + DistanceThreshold = 1f + }; + + if (!_doAfter.TryStartDoAfter(doAfterEventArgs)) + return; + + args.Handled = true; + + _popup.PopupEntity(Loc.GetString("revenant-exorcise-begin-user", [("bible", bible), ("user", user), ("revenant", revenant.Owner)]), user, user); + _popup.PopupEntity(Loc.GetString("revenant-exorcise-begin-target", [("bible", bible), ("user", user), ("revenant", revenant.Owner)]), target, target, PopupType.MediumCaution); + _popup.PopupEntity(Loc.GetString("revenant-exorcise-begin-other", [("bible", bible), ("user", user), ("revenant", revenant.Owner)]), target, Filter.Pvs(target).RemovePlayersByAttachedEntity([user, target]), true); + } + + private void OnExorcise(EntityUid uid, RevenantStasisComponent comp, ExorciseRevenantDoAfterEvent args) + { + if (args.Cancelled) + return; + if (args.Target == null || args.Used == null) + return; + + var target = args.Target.Value; + var used = args.Used.Value; + + _popup.PopupEntity(Loc.GetString("revenant-exorcise-success", [("bible", used), ("user", args.User), ("revenant", comp.Revenant.Owner)]), target); + + RemComp(args.Target.Value); + } +} \ No newline at end of file diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs index 4cfceaf29747..496fe4d7ce5d 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs @@ -1,6 +1,8 @@ using System.Numerics; using Content.Server.Actions; using Content.Server.GameTicking; +using Content.Server.Mind; +using Content.Server.Revenant.Components; using Content.Server.Store.Components; using Content.Server.Store.Systems; using Content.Shared.Alert; @@ -23,6 +25,7 @@ using Robust.Server.GameObjects; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Timing; namespace Content.Server.Revenant.EntitySystems; @@ -45,6 +48,9 @@ public sealed partial class RevenantSystem : EntitySystem [Dependency] private readonly StoreSystem _store = default!; [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly VisibilitySystem _visibility = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly MetaDataSystem _meta = default!; [ValidatePrototypeId] private const string RevenantShopId = "ActionRevenantShop"; @@ -146,8 +152,15 @@ public bool ChangeEssenceAmount(EntityUid uid, FixedPoint2 amount, RevenantCompo if (component.Essence <= 0) { - Spawn(component.SpawnOnDeathPrototype, Transform(uid).Coordinates); - QueueDel(uid); + component.Essence = 0; + _statusEffects.TryRemoveAllStatusEffects(uid); + var stasisObj = Spawn(component.SpawnOnDeathPrototype, Transform(uid).Coordinates); + AddComp(stasisObj, new RevenantStasisComponent(component.StasisTime, (uid, component))); + // TODO: Make a RevenantInStasisComponent and attach that to the inert Revenant entity + if (_mind.TryGetMind(uid, out var mindId, out var _)) + _mind.TransferTo(mindId, stasisObj); + _transformSystem.DetachEntity(uid, Comp(uid)); + _meta.SetEntityPaused(uid, true); } return true; } diff --git a/Content.Shared/Inventory/InventorySystem.Slots.cs b/Content.Shared/Inventory/InventorySystem.Slots.cs index 52151d7289bf..314be9356feb 100644 --- a/Content.Shared/Inventory/InventorySystem.Slots.cs +++ b/Content.Shared/Inventory/InventorySystem.Slots.cs @@ -133,7 +133,7 @@ public bool TryJumpIntoSlots(EntityUid uid, EntityUid target) && _containerSystem.Insert(uid, pocket1) ) { - _popup.PopupEntity(Loc.GetString("item-jump-into-pocket", ("name", Comp(uid).EntityName)), target, target); + _popup.PopupEntity(Loc.GetString("item-jump-into-pocket", ("target", uid)), target, target); return true; } @@ -141,13 +141,13 @@ public bool TryJumpIntoSlots(EntityUid uid, EntityUid target) && _containerSystem.Insert(uid, pocket2) ) { - _popup.PopupEntity(Loc.GetString("item-jump-into-pocket", ("name", Comp(uid).EntityName)), target, target); + _popup.PopupEntity(Loc.GetString("item-jump-into-pocket", ("target", uid)), target, target); return true; } if (_handsSystem.TryPickupAnyHand(target, uid)) { - _popup.PopupEntity(Loc.GetString("item-jump-into-hands", ("name", Comp(uid).EntityName)), target, target); + _popup.PopupEntity(Loc.GetString("item-jump-into-hands", ("name", uid)), target, target); return true; } diff --git a/Content.Shared/Revenant/Components/RevenantComponent.cs b/Content.Shared/Revenant/Components/RevenantComponent.cs index 1cc83db732af..8066291729e3 100644 --- a/Content.Shared/Revenant/Components/RevenantComponent.cs +++ b/Content.Shared/Revenant/Components/RevenantComponent.cs @@ -30,6 +30,19 @@ public sealed partial class RevenantComponent : Component [DataField("spawnOnDeathPrototype", customTypeSerializer:typeof(PrototypeIdSerializer))] public string SpawnOnDeathPrototype = "Ectoplasm"; + [DataField("stasisTime"), ViewVariables(VVAccess.ReadWrite)] + public TimeSpan StasisTime = TimeSpan.FromSeconds(60); + + /// + /// If true, only bible users can exorcise this revenant + /// with a bible. + /// + /// If false, anyone who tries to exorcise a revenant with + /// a bible will be able to. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public bool ExorcismRequiresBibleUser = true; + /// /// The entity's current max amount of essence. Can be increased /// through harvesting player souls. diff --git a/Content.Shared/Revenant/SharedRevenant.cs b/Content.Shared/Revenant/SharedRevenant.cs index 672834012f54..cfd7c7791130 100644 --- a/Content.Shared/Revenant/SharedRevenant.cs +++ b/Content.Shared/Revenant/SharedRevenant.cs @@ -70,6 +70,11 @@ public sealed partial class RevenantAnimateEvent : EntityTargetActionEvent { } +[Serializable, NetSerializable] +public sealed partial class ExorciseRevenantDoAfterEvent : SimpleDoAfterEvent +{ +} + [NetSerializable, Serializable] public enum RevenantVisuals : byte diff --git a/Content.Shared/StatusEffect/StatusEffectsComponent.cs b/Content.Shared/StatusEffect/StatusEffectsComponent.cs index d61af625370d..f1f889d928a9 100644 --- a/Content.Shared/StatusEffect/StatusEffectsComponent.cs +++ b/Content.Shared/StatusEffect/StatusEffectsComponent.cs @@ -15,7 +15,7 @@ public sealed partial class StatusEffectsComponent : Component /// A list of status effect IDs to be allowed /// [DataField("allowed", required: true), Access(typeof(StatusEffectsSystem), Other = AccessPermissions.ReadExecute)] - public List AllowedEffects = default!; + public List AllowedEffects = new(); } [RegisterComponent] diff --git a/Content.Shared/StatusEffect/StatusEffectsSystem.cs b/Content.Shared/StatusEffect/StatusEffectsSystem.cs index 9806077f9bb8..000d3f3cc396 100644 --- a/Content.Shared/StatusEffect/StatusEffectsSystem.cs +++ b/Content.Shared/StatusEffect/StatusEffectsSystem.cs @@ -348,7 +348,7 @@ public bool CanApplyEffect(EntityUid uid, string key, if (!_prototypeManager.TryIndex(key, out var proto)) return false; - if (!status.AllowedEffects.Contains(key) && !proto.AlwaysAllowed) + if (!proto.AlwaysAllowed && !status.AllowedEffects.Contains(key)) return false; return true; diff --git a/Resources/Locale/en-US/revenant/revenant.ftl b/Resources/Locale/en-US/revenant/revenant.ftl index 2af2be895316..24c05b0c1e7b 100644 --- a/Resources/Locale/en-US/revenant/revenant.ftl +++ b/Resources/Locale/en-US/revenant/revenant.ftl @@ -22,8 +22,19 @@ revenant-user-interface-essence-amount = [color=plum]{$amount}[/color] Stolen Es revenant-user-interface-cost = {$price} Essence -item-jump-into-pocket = The {$name} jumps into your pocket! -item-jump-into-hands = The {$name} jumps into your hands! +item-jump-into-pocket = {CAPITALIZE(THE($target))} jumps into your pocket! +item-jump-into-hands = {CAPITALIZE(THE($target))} jumps into your hands! -revenant-animate-item-animate = The {$name} becomes aggressive! -revenant-animate-item-inanimate = The {$name} falls inert. \ No newline at end of file +revenant-animate-item-animate = {CAPITALIZE(THE($target))} becomes aggressive! +revenant-animate-item-inanimate = {CAPITALIZE(THE($target))} falls inert. + +alerts-revenant-stasis-name = [color=red]Stasis[/color] +alerts-revenant-stasis-desc = You are in stasis. Your ghostly form needs time to regenerate. +revenant-stasis-regenerating = [color=yellow]The revenant's energy is still present![/color] + +revenant-exorcise-fail = {CAPITALIZE(THE($bible))} has no effect! +revenant-exorcise-begin-user = You begin exorcising {$revenant} with {THE($bible)}... +revenant-exorcise-begin-other = {CAPITALIZE(THE($user))} begins to exorcise {$revenant} with {THE($bible)}... +revenant-exorcise-begin-target = {CAPITALIZE(THE($user))} is exorcising you with {THE($bible)}! + +revenant-exorcise-success = {$revenant}'s energy fades away... \ No newline at end of file diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index d1327ba65f0e..d12a5fd609d0 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -18,6 +18,7 @@ - category: Piloting - alertType: Corporeal - alertType: Stun + - alertType: Stasis - category: Breathing # Vox gang not calling this oxygen - category: Pressure - alertType: Bleed diff --git a/Resources/Prototypes/Alerts/revenant.yml b/Resources/Prototypes/Alerts/revenant.yml index 38933df4fe0c..fdd4e6c87ca7 100644 --- a/Resources/Prototypes/Alerts/revenant.yml +++ b/Resources/Prototypes/Alerts/revenant.yml @@ -14,6 +14,14 @@ name: alerts-revenant-corporeal-name description: alerts-revenant-corporeal-desc +- type: alert + id: Stasis + icons: + - sprite: Mobs/Ghosts/revenant.rsi + state: ectoplasm + name: alerts-revenant-stasis-name + description: alerts-revenant-stasis-desc + - type: entity id: AlertEssenceSpriteView categories: [ HideSpawnMenu ] diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml index ffccdae4654d..e220bf73b52f 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml @@ -43,6 +43,7 @@ settings: default - type: GhostTakeoverAvailable - type: Revenant + exorcismRequiresBibleUser: true # Escape hatch, change to false if revenant becomes too OP malfunctionWhitelist: components: # emag lockers open diff --git a/Resources/Prototypes/Entities/Objects/Materials/ore.yml b/Resources/Prototypes/Entities/Objects/Materials/ore.yml index a5cd959c47ac..bea0e09cfd9f 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/ore.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/ore.yml @@ -339,6 +339,9 @@ name: salt suffix: Full components: + - type: Tag + tags: + - Salt - type: Stack stackType: SaltOre - type: Sprite diff --git a/Resources/Prototypes/status_effects.yml b/Resources/Prototypes/status_effects.yml index 49e5ccc57941..2f3ba292296c 100644 --- a/Resources/Prototypes/status_effects.yml +++ b/Resources/Prototypes/status_effects.yml @@ -45,6 +45,11 @@ id: Corporeal alert: Corporeal +- type: statusEffect + id: Stasis + alert: Stasis + alwaysAllowed: true + - type: statusEffect id: ForcedSleep #I.e., they will not wake on damage or similar diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 8d6bcd2ef5d0..dae61afa150f 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -797,6 +797,9 @@ - type: Tag id: HolosignProjector +- type: Tag + id: Holy + - type: Tag id: HonkerCentralControlModule @@ -1174,6 +1177,9 @@ - type: Tag id: RollingPin +- type: Tag + id: Salt + - type: Tag id: SaltShaker