From fb692c4cab3f411c11a3004fa73f30122e0ff7f9 Mon Sep 17 00:00:00 2001 From: TGRCDev Date: Thu, 19 Sep 2024 03:08:38 -0700 Subject: [PATCH] Added the revenant Haunt action --- .../EntitySystems/RevenantSystem.Abilities.cs | 47 +++++++++++++++++++ .../Revenant/EntitySystems/RevenantSystem.cs | 13 ++++- .../Revenant/Components/RevenantComponent.cs | 21 ++++++++- .../RevenantRegenModifierComponent.cs | 12 +++++ Content.Shared/Revenant/SharedRevenant.cs | 4 ++ Resources/Prototypes/Actions/revenant.yml | 13 +++++ Resources/Prototypes/Alerts/revenant.yml | 9 ++++ .../Entities/Mobs/NPCs/revenant.yml | 1 + Resources/Prototypes/status_effects.yml | 4 ++ 9 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 Content.Shared/Revenant/Components/RevenantRegenModifierComponent.cs diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs index 6526ff0fa8bf14..0c91d213940ede 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs @@ -32,6 +32,8 @@ using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction.Components; +using Robust.Shared.Player; +using Content.Shared.StatusEffect; namespace Content.Server.Revenant.EntitySystems; @@ -48,6 +50,10 @@ public sealed partial class RevenantSystem [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly RevenantAnimatedSystem _revenantAnimated = default!; + [Dependency] private readonly EntityLookupSystem _entityLookup = default!; + + [ValidatePrototypeId] + private const string RevenantEssenceRegen = "EssenceRegen"; private void InitializeAbilities() { @@ -61,6 +67,7 @@ private void InitializeAbilities() SubscribeLocalEvent(OnMalfunctionAction); SubscribeLocalEvent(OnBloodWritingAction); SubscribeLocalEvent(OnAnimateAction); + SubscribeLocalEvent(OnHauntAction); } private void OnInteract(EntityUid uid, RevenantComponent component, UserActivateInWorldEvent args) @@ -220,6 +227,46 @@ private void OnHarvest(EntityUid uid, RevenantComponent component, HarvestEvent args.Handled = true; } + private void OnHauntAction(EntityUid uid, RevenantComponent comp, RevenantHauntActionEvent args) + { + if (args.Handled) + return; + + if (!TryUseAbility(uid, comp, 0, comp.HauntDebuffs)) + return; + + args.Handled = true; + + // This is probably not the right way to do this... + var witnesses = new HashSet(Filter.PvsExcept(uid).RemoveWhere(player => + { + if (player.AttachedEntity == null) + return true; + + var ent = player.AttachedEntity.Value; + + if (!HasComp(ent) || !HasComp(ent) || HasComp(ent)) + return true; + + var haunted = _interact.InRangeUnobstructed((uid, Transform(uid)), (ent, Transform(ent)), range: 0, collisionMask: CollisionGroup.Impassable); + Log.Debug($"{ent} haunted: {haunted}"); + return !haunted; + }).Recipients.Select(ply => GetNetEntity(ply.AttachedEntity!.Value))); + + // TODO: Maybe an eyeball icon above witnesses on the revenant's client + + // TODO: Modify TryAddStatusEffect to add a premade instance of the component + if (witnesses.Count > 0 && _statusEffects.TryAddStatusEffect(uid, RevenantEssenceRegen, comp.HauntEssenceRegenDuration, true)) + { + _store.TryAddCurrency(new Dictionary + { {comp.StolenEssenceCurrencyPrototype, comp.HauntStolenEssencePerWitness * witnesses.Count} }, uid); + + var regen = Comp(uid); + regen.Witnesses = witnesses; + Dirty(uid, regen); + } + } + private void OnDefileAction(EntityUid uid, RevenantComponent component, RevenantDefileActionEvent args) { if (args.Handled) diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs index 496fe4d7ce5d60..9ac70ceadfc27c 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs @@ -55,6 +55,9 @@ public sealed partial class RevenantSystem : EntitySystem [ValidatePrototypeId] private const string RevenantShopId = "ActionRevenantShop"; + [ValidatePrototypeId] + private const string RevenantHauntId = "ActionRevenantHaunt"; + public override void Initialize() { base.Initialize(); @@ -98,7 +101,8 @@ private void OnStartup(EntityUid uid, RevenantComponent component, ComponentStar private void OnMapInit(EntityUid uid, RevenantComponent component, MapInitEvent args) { - _action.AddAction(uid, ref component.Action, RevenantShopId); + _action.AddAction(uid, ref component.ShopAction, RevenantShopId); + _action.AddAction(uid, ref component.HauntAction, RevenantHauntId); } private void OnStatusAdded(EntityUid uid, RevenantComponent component, StatusEffectAddedEvent args) @@ -232,7 +236,12 @@ public override void Update(float frameTime) if (rev.Essence < rev.EssenceRegenCap) { - ChangeEssenceAmount(uid, rev.EssencePerSecond, rev, regenCap: true); + var essence = rev.EssencePerSecond; + + if (TryComp(uid, out var regen)) + essence += rev.HauntEssenceRegenPerWitness * regen.Witnesses.Count; + + ChangeEssenceAmount(uid, essence, rev, regenCap: true); } } } diff --git a/Content.Shared/Revenant/Components/RevenantComponent.cs b/Content.Shared/Revenant/Components/RevenantComponent.cs index 8066291729e3d0..346587bc71a7a9 100644 --- a/Content.Shared/Revenant/Components/RevenantComponent.cs +++ b/Content.Shared/Revenant/Components/RevenantComponent.cs @@ -92,6 +92,24 @@ public sealed partial class RevenantComponent : Component public float MaxEssenceUpgradeAmount = 10; #endregion + // When used, the revenant reveals itself temporarily and gains stolen essence and a boost in + // essence regeneration for each crewmate that witnesses it + #region Haunt Ability + + [DataField("hauntDebuffs")] + public Vector2 HauntDebuffs = new(2, 6); + + [DataField("hauntStolenEssencePerWitness")] + public FixedPoint2 HauntStolenEssencePerWitness = 2.5; + + [DataField("hauntEssenceRegenPerWitness")] + public FixedPoint2 HauntEssenceRegenPerWitness = 0.5; + + [DataField("hauntEssenceRegenDuration")] + public TimeSpan HauntEssenceRegenDuration = TimeSpan.FromSeconds(10); + + #endregion + //In the nearby radius, causes various objects to be thrown, messed with, and containers opened //Generally just causes a mess #region Defile Ability @@ -260,5 +278,6 @@ public sealed partial class RevenantComponent : Component public string HarvestingState = "harvesting"; #endregion - [DataField] public EntityUid? Action; + [DataField] public EntityUid? ShopAction; + [DataField] public EntityUid? HauntAction; } diff --git a/Content.Shared/Revenant/Components/RevenantRegenModifierComponent.cs b/Content.Shared/Revenant/Components/RevenantRegenModifierComponent.cs new file mode 100644 index 00000000000000..d981cbfbc6a7f3 --- /dev/null +++ b/Content.Shared/Revenant/Components/RevenantRegenModifierComponent.cs @@ -0,0 +1,12 @@ + + +using Robust.Shared.GameStates; + +namespace Content.Shared.Revenant.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class RevenantRegenModifierComponent : Component +{ + [ViewVariables, AutoNetworkedField] + public HashSet Witnesses = new(); +} \ No newline at end of file diff --git a/Content.Shared/Revenant/SharedRevenant.cs b/Content.Shared/Revenant/SharedRevenant.cs index cfd7c779113026..d6eceb51fb54af 100644 --- a/Content.Shared/Revenant/SharedRevenant.cs +++ b/Content.Shared/Revenant/SharedRevenant.cs @@ -46,6 +46,10 @@ public sealed partial class RevenantShopActionEvent : InstantActionEvent { } +public sealed partial class RevenantHauntActionEvent : InstantActionEvent +{ +} + public sealed partial class RevenantDefileActionEvent : InstantActionEvent { } diff --git a/Resources/Prototypes/Actions/revenant.yml b/Resources/Prototypes/Actions/revenant.yml index c36a530f688d71..35ced8756f6f02 100644 --- a/Resources/Prototypes/Actions/revenant.yml +++ b/Resources/Prototypes/Actions/revenant.yml @@ -7,6 +7,19 @@ icon: Interface/Actions/shop.png event: !type:RevenantShopActionEvent +- type: entity + id: ActionRevenantHaunt + name: Haunt + description: Gives essence and stolen essence for every witness. + components: + - type: InstantAction + itemIconStyle: NoItem + icon: + sprite: Mobs/Ghosts/revenant.rsi + state: icon + event: !type:RevenantHauntActionEvent + useDelay: 15 + - type: entity id: ActionRevenantDefile name: Defile diff --git a/Resources/Prototypes/Alerts/revenant.yml b/Resources/Prototypes/Alerts/revenant.yml index fdd4e6c87ca763..24134572d28822 100644 --- a/Resources/Prototypes/Alerts/revenant.yml +++ b/Resources/Prototypes/Alerts/revenant.yml @@ -14,6 +14,15 @@ name: alerts-revenant-corporeal-name description: alerts-revenant-corporeal-desc +# TODO: Custom icon +- type: alert + id: EssenceRegen + name: alerts-revenant-essence-regen + description: alerts-revenant-essence-regen + icons: + - sprite: /Textures/Mobs/Ghosts/revenant.rsi + state: icon + - type: alert id: Stasis icons: diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml index e220bf73b52f73..3fc86a3fac4399 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml @@ -19,6 +19,7 @@ allowed: - Stun - Corporeal + - EssenceRegen - type: Hands showInHands: false disableExplosionRecursion: true diff --git a/Resources/Prototypes/status_effects.yml b/Resources/Prototypes/status_effects.yml index 2f3ba292296cdc..e269851375c690 100644 --- a/Resources/Prototypes/status_effects.yml +++ b/Resources/Prototypes/status_effects.yml @@ -50,6 +50,10 @@ alert: Stasis alwaysAllowed: true +- type: statusEffect + id: EssenceRegen + alert: EssenceRegen + - type: statusEffect id: ForcedSleep #I.e., they will not wake on damage or similar