Skip to content

Commit

Permalink
Merge pull request #293 from TGRCdev/revenant-haunt
Browse files Browse the repository at this point in the history
Added the revenant Haunt action
  • Loading branch information
formlessnameless authored Sep 20, 2024
2 parents 1679f2f + 2db3e69 commit c796952
Show file tree
Hide file tree
Showing 19 changed files with 289 additions and 3 deletions.
56 changes: 56 additions & 0 deletions Content.Client/Revenant/RevenantRegenModifierSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System.Numerics;
using Content.Client.Alerts;
using Content.Shared.Revenant;
using Content.Shared.Revenant.Components;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using Timer = Robust.Shared.Timing.Timer;

namespace Content.Client.Revenant;

public sealed class RevenantRegenModifierSystem : EntitySystem
{
[Dependency] private readonly SpriteSystem _sprite = default!;

private readonly SpriteSpecifier _witnessIndicator = new SpriteSpecifier.Texture(new ResPath("Interface/Actions/scream.png"));

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<RevenantRegenModifierComponent, UpdateAlertSpriteEvent>(OnUpdateAlert);
SubscribeNetworkEvent<RevenantHauntWitnessEvent>(OnWitnesses);
}

private void OnWitnesses(RevenantHauntWitnessEvent args)
{
foreach (var witness in args.Witnesses)
{
var ent = GetEntity(witness);
if (TryComp<SpriteComponent>(ent, out var sprite))
{
var layerID = sprite.AddLayer(_witnessIndicator);
if (sprite.TryGetLayer(layerID, out var layer))
{
layer.Offset = new Vector2(0, 0.8f);
layer.Scale = new Vector2(0.65f, 0.65f);
}
Timer.Spawn(TimeSpan.FromSeconds(5), () => sprite.RemoveLayer(layerID));
}
}
}

private void OnUpdateAlert(Entity<RevenantRegenModifierComponent> ent, ref UpdateAlertSpriteEvent args)
{
if (args.Alert.ID != ent.Comp.Alert)
return;

var sprite = args.SpriteViewEnt.Comp;
var witnesses = Math.Clamp(ent.Comp.Witnesses.Count, 0, 99);
sprite.LayerSetState(RevenantVisualLayers.Digit1, $"{witnesses / 10}");
sprite.LayerSetState(RevenantVisualLayers.Digit2, $"{witnesses % 10}");
}
}
65 changes: 65 additions & 0 deletions Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction.Components;
using Robust.Shared.Player;
using Content.Shared.StatusEffect;
using Content.Shared.Flash.Components;
using Robust.Shared.Audio.Systems;

namespace Content.Server.Revenant.EntitySystems;

Expand All @@ -48,6 +52,13 @@ 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 SharedAudioSystem _audioSystem = default!;

[ValidatePrototypeId<StatusEffectPrototype>]
private const string RevenantEssenceRegen = "EssenceRegen";

[ValidatePrototypeId<StatusEffectPrototype>]
private const string FlashedId = "Flashed";

private void InitializeAbilities()
{
Expand All @@ -61,6 +72,7 @@ private void InitializeAbilities()
SubscribeLocalEvent<RevenantComponent, RevenantMalfunctionActionEvent>(OnMalfunctionAction);
SubscribeLocalEvent<RevenantComponent, RevenantBloodWritingEvent>(OnBloodWritingAction);
SubscribeLocalEvent<RevenantComponent, RevenantAnimateEvent>(OnAnimateAction);
SubscribeLocalEvent<RevenantComponent, RevenantHauntActionEvent>(OnHauntAction);
}

private void OnInteract(EntityUid uid, RevenantComponent component, UserActivateInWorldEvent args)
Expand Down Expand Up @@ -220,6 +232,59 @@ 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 witnessAndRevenantFilter = Filter.Pvs(uid).RemoveWhere(player =>
{
if (player.AttachedEntity == null)
return true;

var ent = player.AttachedEntity.Value;

if (!HasComp<MobStateComponent>(ent) || !HasComp<HumanoidAppearanceComponent>(ent) || HasComp<RevenantComponent>(ent))
return true;

return !_interact.InRangeUnobstructed((uid, Transform(uid)), (ent, Transform(ent)), range: 0, collisionMask: CollisionGroup.Impassable);
});

var witnesses = new HashSet<NetEntity>(witnessAndRevenantFilter.RemovePlayerByAttachedEntity(uid).Recipients.Select(ply => GetNetEntity(ply.AttachedEntity!.Value)));

// Give the witnesses a spook!
_audioSystem.PlayGlobal(comp.HauntSound, witnessAndRevenantFilter, true);

foreach (var witness in witnesses)
{
_statusEffects.TryAddStatusEffect<FlashedComponent>(GetEntity(witness),
FlashedId,
comp.HauntFlashDuration,
false
);
}

if (witnesses.Count > 0 && _statusEffects.TryAddStatusEffect(uid,
RevenantEssenceRegen,
comp.HauntEssenceRegenDuration,
true,
component: new RevenantRegenModifierComponent(witnesses)
))
{
if (_mind.TryGetMind(uid, out var _, out var mind) && mind.Session != null)
RaiseNetworkEvent(new RevenantHauntWitnessEvent(witnesses), mind.Session);

_store.TryAddCurrency(new Dictionary<string, FixedPoint2>
{ {comp.StolenEssenceCurrencyPrototype, comp.HauntStolenEssencePerWitness * witnesses.Count} }, uid);
}
}

private void OnDefileAction(EntityUid uid, RevenantComponent component, RevenantDefileActionEvent args)
{
if (args.Handled)
Expand Down
13 changes: 11 additions & 2 deletions Content.Server/Revenant/EntitySystems/RevenantSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ public sealed partial class RevenantSystem : EntitySystem
[ValidatePrototypeId<EntityPrototype>]
private const string RevenantShopId = "ActionRevenantShop";

[ValidatePrototypeId<EntityPrototype>]
private const string RevenantHauntId = "ActionRevenantHaunt";

public override void Initialize()
{
base.Initialize();
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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<RevenantRegenModifierComponent>(uid, out var regen))
essence += rev.HauntEssenceRegenPerWitness * regen.Witnesses.Count;

ChangeEssenceAmount(uid, essence, rev, regenCap: true);
}
}
}
Expand Down
28 changes: 27 additions & 1 deletion Content.Shared/Revenant/Components/RevenantComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Content.Shared.FixedPoint;
using Content.Shared.Store;
using Content.Shared.Whitelist;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
Expand Down Expand Up @@ -92,6 +93,30 @@ 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"), ViewVariables(VVAccess.ReadWrite)]
public Vector2 HauntDebuffs = new(2, 6);

[DataField("hauntStolenEssencePerWitness"), ViewVariables(VVAccess.ReadWrite)]
public FixedPoint2 HauntStolenEssencePerWitness = 2.5;

[DataField("hauntEssenceRegenPerWitness"), ViewVariables(VVAccess.ReadWrite)]
public FixedPoint2 HauntEssenceRegenPerWitness = 0.5;

[DataField("hauntEssenceRegenDuration"), ViewVariables(VVAccess.ReadWrite)]
public TimeSpan HauntEssenceRegenDuration = TimeSpan.FromSeconds(10);

[DataField("hauntSound"), ViewVariables(VVAccess.ReadWrite)]
public SoundSpecifier? HauntSound = new SoundCollectionSpecifier("RevenantHaunt");

[DataField("hauntFlashDuration"), ViewVariables(VVAccess.ReadWrite)]
public TimeSpan HauntFlashDuration = TimeSpan.FromSeconds(2);

#endregion

//In the nearby radius, causes various objects to be thrown, messed with, and containers opened
//Generally just causes a mess
#region Defile Ability
Expand Down Expand Up @@ -260,5 +285,6 @@ public sealed partial class RevenantComponent : Component
public string HarvestingState = "harvesting";
#endregion

[DataField] public EntityUid? Action;
[DataField] public EntityUid? ShopAction;
[DataField] public EntityUid? HauntAction;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Content.Shared.Alert;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;

namespace Content.Shared.Revenant.Components;

[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class RevenantRegenModifierComponent : Component
{
[ViewVariables, AutoNetworkedField]
public HashSet<NetEntity> Witnesses;

[DataField]
public ProtoId<AlertPrototype> Alert = "EssenceRegen";

public RevenantRegenModifierComponent(HashSet<NetEntity> witnesses)
{
Witnesses = witnesses;
}

public RevenantRegenModifierComponent() : this(new())
{
}
}
19 changes: 19 additions & 0 deletions Content.Shared/Revenant/SharedRevenant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ public sealed partial class RevenantShopActionEvent : InstantActionEvent
{
}

public sealed partial class RevenantHauntActionEvent : InstantActionEvent
{
}

public sealed partial class RevenantDefileActionEvent : InstantActionEvent
{
}
Expand All @@ -70,6 +74,21 @@ public sealed partial class RevenantAnimateEvent : EntityTargetActionEvent
{
}

[Serializable, NetSerializable]
public sealed partial class RevenantHauntWitnessEvent : EntityEventArgs
{
public HashSet<NetEntity> Witnesses = new();

public RevenantHauntWitnessEvent(HashSet<NetEntity> witnesses)
{
Witnesses = witnesses;
}

public RevenantHauntWitnessEvent() : this(new())
{
}
}

[Serializable, NetSerializable]
public sealed partial class ExorciseRevenantDoAfterEvent : SimpleDoAfterEvent
{
Expand Down
16 changes: 16 additions & 0 deletions Content.Shared/StatusEffect/StatusEffectsSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,22 @@ public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool re
return false;
}

public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh, Component component,
StatusEffectsComponent? status = null)
{
if (!Resolve(uid, ref status, false))
return false;

if (TryAddStatusEffect(uid, key, time, refresh, status))
{
EntityManager.AddComponent(uid, component, true);
status.ActiveEffects[key].RelevantComponent = _componentFactory.GetComponentName(component.GetType());
return true;
}

return false;
}

/// <summary>
/// Tries to add a status effect to an entity with a certain timer.
/// </summary>
Expand Down
24 changes: 24 additions & 0 deletions Resources/Audio/Effects/Revenant/attributions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
- files: ["haunt0.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Sound originally derived from SCP: Containment breach."
source: "https://github.com/Regalis11/scpcb/blob/edb8fe0840b78f14d1aef3a0bf6174630e7be296/SFX/Horror/Horror0.ogg"

- files: ["haunt1.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Sound originally derived from SCP: Containment breach."
source: "https://github.com/Regalis11/scpcb/blob/edb8fe0840b78f14d1aef3a0bf6174630e7be296/SFX/Horror/Horror4.ogg"

- files: ["haunt2.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Sound originally derived from SCP: Containment breach."
source: "https://github.com/Regalis11/scpcb/blob/edb8fe0840b78f14d1aef3a0bf6174630e7be296/SFX/Horror/Horror6.ogg"

- files: ["haunt3.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Sound originally derived from SCP: Containment breach."
source: "https://github.com/Regalis11/scpcb/blob/edb8fe0840b78f14d1aef3a0bf6174630e7be296/SFX/Horror/Horror11.ogg"

- files: ["haunt4.ogg"]
license: "CC-BY-SA-3.0"
copyright: "Sound originally derived from SCP: Containment breach. Edited down to a shorter length by TGRCDev."
source: "https://github.com/Regalis11/scpcb/blob/edb8fe0840b78f14d1aef3a0bf6174630e7be296/SFX/Horror/Horror12.ogg"
Binary file added Resources/Audio/Effects/Revenant/haunt0.ogg
Binary file not shown.
Binary file added Resources/Audio/Effects/Revenant/haunt1.ogg
Binary file not shown.
Binary file added Resources/Audio/Effects/Revenant/haunt2.ogg
Binary file not shown.
Binary file added Resources/Audio/Effects/Revenant/haunt3.ogg
Binary file not shown.
Binary file added Resources/Audio/Effects/Revenant/haunt4.ogg
Binary file not shown.
3 changes: 3 additions & 0 deletions Resources/Locale/en-US/alerts/alerts.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ alerts-revenant-essence-desc = The power of souls. It sustains you and is used f
alerts-revenant-corporeal-name = Corporeal
alerts-revenant-corporeal-desc = You have manifested physically. People around you can see and hurt you.
alerts-revenant-essence-regen-name = Haunting
alerts-revenant-essence-regen-desc = You are haunting the crew! You have a boosted essence regeneration rate based on how many people you haunted.
alerts-changeling-chemicals-name = Chemicals
alerts-changeling-chemicals-desc = Spend chemicals to use your abilities. Slowly regenerates.
Expand Down
13 changes: 13 additions & 0 deletions Resources/Prototypes/Actions/revenant.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions Resources/Prototypes/Alerts/revenant.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
name: alerts-revenant-corporeal-name
description: alerts-revenant-corporeal-desc

- type: alert
id: EssenceRegen
name: alerts-revenant-essence-regen-name
description: alerts-revenant-essence-regen-desc
icons: [ /Textures/Interface/Actions/scream.png ]
alertViewEntity: AlertEssenceRegenSpriteView

- type: alert
id: Stasis
icons:
Expand All @@ -35,3 +42,14 @@
offset: 0.125, 0
- map: [ "enum.RevenantVisualLayers.Digit3" ]
offset: 0.25, 0

- type: entity
id: AlertEssenceRegenSpriteView
components:
- type: Sprite
sprite: /Textures/Interface/Alerts/essence_counter.rsi
layers:
- map: [ "enum.AlertVisualLayers.Base" ]
- map: [ "enum.RevenantVisualLayers.Digit1" ]
- map: [ "enum.RevenantVisualLayers.Digit2" ]
offset: 0.125, 0
Loading

0 comments on commit c796952

Please sign in to comment.