-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Cursed Mask * extra expressions * block ingestion * mind returning * okay fix the removal shit
- Loading branch information
1 parent
53058df
commit fc1446e
Showing
20 changed files
with
412 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
using Content.Shared.Clothing; | ||
|
||
namespace Content.Client.Clothing.Systems; | ||
|
||
/// <inheritdoc/> | ||
public sealed class CursedMaskSystem : SharedCursedMaskSystem; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
using Content.Server.Administration.Logs; | ||
using Content.Server.GameTicking; | ||
using Content.Server.Mind; | ||
using Content.Server.NPC; | ||
using Content.Server.NPC.HTN; | ||
using Content.Server.NPC.Systems; | ||
using Content.Server.Popups; | ||
using Content.Shared.Clothing; | ||
using Content.Shared.Clothing.Components; | ||
using Content.Shared.Database; | ||
using Content.Shared.NPC.Components; | ||
using Content.Shared.NPC.Systems; | ||
using Content.Shared.Players; | ||
using Content.Shared.Popups; | ||
using Robust.Shared.Player; | ||
using Robust.Shared.Prototypes; | ||
|
||
namespace Content.Server.Clothing.Systems; | ||
|
||
/// <inheritdoc/> | ||
public sealed class CursedMaskSystem : SharedCursedMaskSystem | ||
{ | ||
[Dependency] private readonly IAdminLogManager _adminLog = default!; | ||
[Dependency] private readonly GameTicker _ticker = default!; | ||
[Dependency] private readonly HTNSystem _htn = default!; | ||
[Dependency] private readonly MindSystem _mind = default!; | ||
[Dependency] private readonly NPCSystem _npc = default!; | ||
[Dependency] private readonly NpcFactionSystem _npcFaction = default!; | ||
[Dependency] private readonly PopupSystem _popup = default!; | ||
|
||
// We can't store this info on the component easily | ||
private static readonly ProtoId<HTNCompoundPrototype> TakeoverRootTask = "SimpleHostileCompound"; | ||
|
||
protected override void TryTakeover(Entity<CursedMaskComponent> ent, EntityUid wearer) | ||
{ | ||
if (ent.Comp.CurrentState != CursedMaskExpression.Anger) | ||
return; | ||
|
||
if (TryComp<ActorComponent>(wearer, out var actor) && actor.PlayerSession.GetMind() is { } mind) | ||
{ | ||
var session = actor.PlayerSession; | ||
if (!_ticker.OnGhostAttempt(mind, false)) | ||
return; | ||
|
||
ent.Comp.StolenMind = mind; | ||
|
||
_popup.PopupEntity(Loc.GetString("cursed-mask-takeover-popup"), wearer, session, PopupType.LargeCaution); | ||
_adminLog.Add(LogType.Action, | ||
LogImpact.Extreme, | ||
$"{ToPrettyString(wearer):player} had their body taken over and turned into an enemy through the cursed mask {ToPrettyString(ent):entity}"); | ||
} | ||
|
||
var npcFaction = EnsureComp<NpcFactionMemberComponent>(wearer); | ||
ent.Comp.OldFactions = npcFaction.Factions; | ||
_npcFaction.ClearFactions((wearer, npcFaction), false); | ||
_npcFaction.AddFaction((wearer, npcFaction), ent.Comp.CursedMaskFaction); | ||
|
||
ent.Comp.HasNpc = !EnsureComp<HTNComponent>(wearer, out var htn); | ||
htn.RootTask = new HTNCompoundTask { Task = TakeoverRootTask }; | ||
htn.Blackboard.SetValue(NPCBlackboard.Owner, wearer); | ||
_npc.WakeNPC(wearer, htn); | ||
_htn.Replan(htn); | ||
} | ||
|
||
protected override void OnClothingUnequip(Entity<CursedMaskComponent> ent, ref ClothingGotUnequippedEvent args) | ||
{ | ||
// If we are taking off the cursed mask | ||
if (ent.Comp.CurrentState == CursedMaskExpression.Anger) | ||
{ | ||
if (ent.Comp.HasNpc) | ||
RemComp<HTNComponent>(args.Wearer); | ||
|
||
var npcFaction = EnsureComp<NpcFactionMemberComponent>(args.Wearer); | ||
_npcFaction.RemoveFaction((args.Wearer, npcFaction), ent.Comp.CursedMaskFaction, false); | ||
_npcFaction.AddFactions((args.Wearer, npcFaction), ent.Comp.OldFactions); | ||
|
||
ent.Comp.HasNpc = false; | ||
ent.Comp.OldFactions.Clear(); | ||
|
||
if (Exists(ent.Comp.StolenMind)) | ||
{ | ||
_mind.TransferTo(ent.Comp.StolenMind.Value, args.Wearer); | ||
_adminLog.Add(LogType.Action, | ||
LogImpact.Extreme, | ||
$"{ToPrettyString(args.Wearer):player} was restored to their body after the removal of {ToPrettyString(ent):entity}."); | ||
ent.Comp.StolenMind = null; | ||
} | ||
} | ||
|
||
RandomizeCursedMask(ent, args.Wearer); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
using Content.Shared.Damage; | ||
using Content.Shared.NPC.Prototypes; | ||
using Robust.Shared.GameStates; | ||
using Robust.Shared.Prototypes; | ||
using Robust.Shared.Serialization; | ||
|
||
namespace Content.Shared.Clothing.Components; | ||
|
||
/// <summary> | ||
/// This is used for a mask that takes over the host when worn. | ||
/// </summary> | ||
[RegisterComponent, NetworkedComponent, Access(typeof(SharedCursedMaskSystem))] | ||
public sealed partial class CursedMaskComponent : Component | ||
{ | ||
/// <summary> | ||
/// The current expression shown. Used to determine which effect is applied. | ||
/// </summary> | ||
[DataField] | ||
public CursedMaskExpression CurrentState = CursedMaskExpression.Neutral; | ||
|
||
/// <summary> | ||
/// Speed modifier applied when the "Joy" expression is present. | ||
/// </summary> | ||
[DataField] | ||
public float JoySpeedModifier = 1.15f; | ||
|
||
/// <summary> | ||
/// Damage modifier applied when the "Despair" expression is present. | ||
/// </summary> | ||
[DataField] | ||
public DamageModifierSet DespairDamageModifier = new(); | ||
|
||
/// <summary> | ||
/// Whether or not the mask is currently attached to an NPC. | ||
/// </summary> | ||
[DataField] | ||
public bool HasNpc; | ||
|
||
/// <summary> | ||
/// The mind that was booted from the wearer when the mask took over. | ||
/// </summary> | ||
[DataField] | ||
public EntityUid? StolenMind; | ||
|
||
[DataField] | ||
public ProtoId<NpcFactionPrototype> CursedMaskFaction = "SimpleHostile"; | ||
|
||
[DataField] | ||
public HashSet<ProtoId<NpcFactionPrototype>> OldFactions = new(); | ||
} | ||
|
||
[Serializable, NetSerializable] | ||
public enum CursedMaskVisuals : byte | ||
{ | ||
State | ||
} | ||
|
||
[Serializable, NetSerializable] | ||
public enum CursedMaskExpression : byte | ||
{ | ||
Neutral, | ||
Joy, | ||
Despair, | ||
Anger | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
using Content.Shared.Clothing.Components; | ||
using Content.Shared.Damage; | ||
using Content.Shared.Examine; | ||
using Content.Shared.Inventory; | ||
using Content.Shared.Movement.Systems; | ||
using Robust.Shared.Random; | ||
using Robust.Shared.Timing; | ||
|
||
namespace Content.Shared.Clothing; | ||
|
||
/// <summary> | ||
/// This handles <see cref="CursedMaskComponent"/> | ||
/// </summary> | ||
public abstract class SharedCursedMaskSystem : EntitySystem | ||
{ | ||
[Dependency] private readonly IGameTiming _timing = default!; | ||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; | ||
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!; | ||
|
||
/// <inheritdoc/> | ||
public override void Initialize() | ||
{ | ||
base.Initialize(); | ||
|
||
SubscribeLocalEvent<CursedMaskComponent, ClothingGotEquippedEvent>(OnClothingEquip); | ||
SubscribeLocalEvent<CursedMaskComponent, ClothingGotUnequippedEvent>(OnClothingUnequip); | ||
SubscribeLocalEvent<CursedMaskComponent, ExaminedEvent>(OnExamine); | ||
|
||
SubscribeLocalEvent<CursedMaskComponent, InventoryRelayedEvent<RefreshMovementSpeedModifiersEvent>>(OnMovementSpeedModifier); | ||
SubscribeLocalEvent<CursedMaskComponent, InventoryRelayedEvent<DamageModifyEvent>>(OnModifyDamage); | ||
} | ||
|
||
private void OnClothingEquip(Entity<CursedMaskComponent> ent, ref ClothingGotEquippedEvent args) | ||
{ | ||
RandomizeCursedMask(ent, args.Wearer); | ||
TryTakeover(ent, args.Wearer); | ||
} | ||
|
||
protected virtual void OnClothingUnequip(Entity<CursedMaskComponent> ent, ref ClothingGotUnequippedEvent args) | ||
{ | ||
RandomizeCursedMask(ent, args.Wearer); | ||
} | ||
|
||
private void OnExamine(Entity<CursedMaskComponent> ent, ref ExaminedEvent args) | ||
{ | ||
args.PushMarkup(Loc.GetString($"cursed-mask-examine-{ent.Comp.CurrentState.ToString()}")); | ||
} | ||
|
||
private void OnMovementSpeedModifier(Entity<CursedMaskComponent> ent, ref InventoryRelayedEvent<RefreshMovementSpeedModifiersEvent> args) | ||
{ | ||
if (ent.Comp.CurrentState == CursedMaskExpression.Joy) | ||
args.Args.ModifySpeed(ent.Comp.JoySpeedModifier); | ||
} | ||
|
||
private void OnModifyDamage(Entity<CursedMaskComponent> ent, ref InventoryRelayedEvent<DamageModifyEvent> args) | ||
{ | ||
if (ent.Comp.CurrentState == CursedMaskExpression.Despair) | ||
args.Args.Damage = DamageSpecifier.ApplyModifierSet(args.Args.Damage, ent.Comp.DespairDamageModifier); | ||
} | ||
|
||
protected void RandomizeCursedMask(Entity<CursedMaskComponent> ent, EntityUid wearer) | ||
{ | ||
var random = new System.Random((int) _timing.CurTick.Value); | ||
ent.Comp.CurrentState = random.Pick(Enum.GetValues<CursedMaskExpression>()); | ||
_appearance.SetData(ent, CursedMaskVisuals.State, ent.Comp.CurrentState); | ||
_movementSpeedModifier.RefreshMovementSpeedModifiers(wearer); | ||
} | ||
|
||
protected virtual void TryTakeover(Entity<CursedMaskComponent> ent, EntityUid wearer) | ||
{ | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
using Robust.Shared.GameStates; | ||
|
||
namespace Content.Shared.Inventory; | ||
|
||
/// <summary> | ||
/// This is used for an item that can only be equipped/unequipped by the user. | ||
/// </summary> | ||
[RegisterComponent, NetworkedComponent, Access(typeof(SelfEquipOnlySystem))] | ||
public sealed partial class SelfEquipOnlyComponent : Component | ||
{ | ||
/// <summary> | ||
/// Whether or not the self-equip only condition requires the person to be conscious. | ||
/// </summary> | ||
[DataField] | ||
public bool UnequipRequireConscious = true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
using Content.Shared.ActionBlocker; | ||
using Content.Shared.Clothing.Components; | ||
using Content.Shared.Inventory.Events; | ||
|
||
namespace Content.Shared.Inventory; | ||
|
||
public sealed class SelfEquipOnlySystem : EntitySystem | ||
{ | ||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; | ||
|
||
/// <inheritdoc/> | ||
public override void Initialize() | ||
{ | ||
SubscribeLocalEvent<SelfEquipOnlyComponent, BeingEquippedAttemptEvent>(OnBeingEquipped); | ||
SubscribeLocalEvent<SelfEquipOnlyComponent, BeingUnequippedAttemptEvent>(OnBeingUnequipped); | ||
} | ||
|
||
private void OnBeingEquipped(Entity<SelfEquipOnlyComponent> ent, ref BeingEquippedAttemptEvent args) | ||
{ | ||
if (args.Cancelled) | ||
return; | ||
|
||
if (TryComp<ClothingComponent>(ent, out var clothing) && (clothing.Slots & args.SlotFlags) == SlotFlags.NONE) | ||
return; | ||
|
||
if (args.Equipee != args.EquipTarget) | ||
args.Cancel(); | ||
} | ||
|
||
private void OnBeingUnequipped(Entity<SelfEquipOnlyComponent> ent, ref BeingUnequippedAttemptEvent args) | ||
{ | ||
if (args.Cancelled) | ||
return; | ||
|
||
if (args.Unequipee == args.UnEquipTarget) | ||
return; | ||
|
||
if (TryComp<ClothingComponent>(ent, out var clothing) && (clothing.Slots & args.SlotFlags) == SlotFlags.NONE) | ||
return; | ||
|
||
if (ent.Comp.UnequipRequireConscious && !_actionBlocker.CanConsciouslyPerformAction(args.UnEquipTarget)) | ||
return; | ||
args.Cancel(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
cursed-mask-examine-Neutral = It depicts an entirely unremarkable visage. | ||
cursed-mask-examine-Joy = It depicts a face basking in joy. | ||
cursed-mask-examine-Despair = It depicts a face wraught with despair. | ||
cursed-mask-examine-Anger = It depicts a furious expression locked in rage. | ||
cursed-mask-takeover-popup = The mask seizes control over your body! |
Oops, something went wrong.