From fc1446e73ac7da49c38f0683eed221747ebdd8f4 Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sat, 10 Aug 2024 11:14:58 -0400 Subject: [PATCH] Cursed Mask (#29659) * Cursed Mask * extra expressions * block ingestion * mind returning * okay fix the removal shit --- .../Clothing/Systems/CursedMaskSystem.cs | 6 ++ .../Clothing/Systems/CursedMaskSystem.cs | 92 ++++++++++++++++++ .../Components/CursedMaskComponent.cs | 65 +++++++++++++ .../Clothing/SharedCursedMaskSystem.cs | 73 ++++++++++++++ .../Inventory/Events/UnequipAttemptEvent.cs | 6 ++ .../Inventory/SelfEquipOnlyComponent.cs | 16 +++ .../Inventory/SelfEquipOnlySystem.cs | 45 +++++++++ .../Systems/MovementSpeedModifierSystem.cs | 5 + .../NPC/Systems/NpcFactionSystem.cs | 22 +++++ .../en-US/clothing/components/cursed-mask.ftl | 5 + .../Entities/Clothing/Masks/specific.yml | 38 ++++++++ .../Mask/goldenmask.rsi/equipped-MASK-vox.png | Bin 0 -> 453 bytes .../Mask/goldenmask.rsi/equipped-MASK.png | Bin 0 -> 401 bytes .../Mask/goldenmask.rsi/icon-anger.png | Bin 0 -> 422 bytes .../Mask/goldenmask.rsi/icon-despair.png | Bin 0 -> 414 bytes .../Clothing/Mask/goldenmask.rsi/icon-joy.png | Bin 0 -> 414 bytes .../Clothing/Mask/goldenmask.rsi/icon.png | Bin 0 -> 402 bytes .../Mask/goldenmask.rsi/inhand-left.png | Bin 0 -> 360 bytes .../Mask/goldenmask.rsi/inhand-right.png | Bin 0 -> 353 bytes .../Clothing/Mask/goldenmask.rsi/meta.json | 39 ++++++++ 20 files changed, 412 insertions(+) create mode 100644 Content.Client/Clothing/Systems/CursedMaskSystem.cs create mode 100644 Content.Server/Clothing/Systems/CursedMaskSystem.cs create mode 100644 Content.Shared/Clothing/Components/CursedMaskComponent.cs create mode 100644 Content.Shared/Clothing/SharedCursedMaskSystem.cs create mode 100644 Content.Shared/Inventory/SelfEquipOnlyComponent.cs create mode 100644 Content.Shared/Inventory/SelfEquipOnlySystem.cs create mode 100644 Resources/Locale/en-US/clothing/components/cursed-mask.ftl create mode 100644 Resources/Textures/Clothing/Mask/goldenmask.rsi/equipped-MASK-vox.png create mode 100644 Resources/Textures/Clothing/Mask/goldenmask.rsi/equipped-MASK.png create mode 100644 Resources/Textures/Clothing/Mask/goldenmask.rsi/icon-anger.png create mode 100644 Resources/Textures/Clothing/Mask/goldenmask.rsi/icon-despair.png create mode 100644 Resources/Textures/Clothing/Mask/goldenmask.rsi/icon-joy.png create mode 100644 Resources/Textures/Clothing/Mask/goldenmask.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Mask/goldenmask.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Mask/goldenmask.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Mask/goldenmask.rsi/meta.json diff --git a/Content.Client/Clothing/Systems/CursedMaskSystem.cs b/Content.Client/Clothing/Systems/CursedMaskSystem.cs new file mode 100644 index 0000000000..bc931d15fd --- /dev/null +++ b/Content.Client/Clothing/Systems/CursedMaskSystem.cs @@ -0,0 +1,6 @@ +using Content.Shared.Clothing; + +namespace Content.Client.Clothing.Systems; + +/// +public sealed class CursedMaskSystem : SharedCursedMaskSystem; diff --git a/Content.Server/Clothing/Systems/CursedMaskSystem.cs b/Content.Server/Clothing/Systems/CursedMaskSystem.cs new file mode 100644 index 0000000000..2045ff5ccd --- /dev/null +++ b/Content.Server/Clothing/Systems/CursedMaskSystem.cs @@ -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; + +/// +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 TakeoverRootTask = "SimpleHostileCompound"; + + protected override void TryTakeover(Entity ent, EntityUid wearer) + { + if (ent.Comp.CurrentState != CursedMaskExpression.Anger) + return; + + if (TryComp(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(wearer); + ent.Comp.OldFactions = npcFaction.Factions; + _npcFaction.ClearFactions((wearer, npcFaction), false); + _npcFaction.AddFaction((wearer, npcFaction), ent.Comp.CursedMaskFaction); + + ent.Comp.HasNpc = !EnsureComp(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 ent, ref ClothingGotUnequippedEvent args) + { + // If we are taking off the cursed mask + if (ent.Comp.CurrentState == CursedMaskExpression.Anger) + { + if (ent.Comp.HasNpc) + RemComp(args.Wearer); + + var npcFaction = EnsureComp(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); + } +} diff --git a/Content.Shared/Clothing/Components/CursedMaskComponent.cs b/Content.Shared/Clothing/Components/CursedMaskComponent.cs new file mode 100644 index 0000000000..6073bdf5fc --- /dev/null +++ b/Content.Shared/Clothing/Components/CursedMaskComponent.cs @@ -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; + +/// +/// This is used for a mask that takes over the host when worn. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedCursedMaskSystem))] +public sealed partial class CursedMaskComponent : Component +{ + /// + /// The current expression shown. Used to determine which effect is applied. + /// + [DataField] + public CursedMaskExpression CurrentState = CursedMaskExpression.Neutral; + + /// + /// Speed modifier applied when the "Joy" expression is present. + /// + [DataField] + public float JoySpeedModifier = 1.15f; + + /// + /// Damage modifier applied when the "Despair" expression is present. + /// + [DataField] + public DamageModifierSet DespairDamageModifier = new(); + + /// + /// Whether or not the mask is currently attached to an NPC. + /// + [DataField] + public bool HasNpc; + + /// + /// The mind that was booted from the wearer when the mask took over. + /// + [DataField] + public EntityUid? StolenMind; + + [DataField] + public ProtoId CursedMaskFaction = "SimpleHostile"; + + [DataField] + public HashSet> OldFactions = new(); +} + +[Serializable, NetSerializable] +public enum CursedMaskVisuals : byte +{ + State +} + +[Serializable, NetSerializable] +public enum CursedMaskExpression : byte +{ + Neutral, + Joy, + Despair, + Anger +} diff --git a/Content.Shared/Clothing/SharedCursedMaskSystem.cs b/Content.Shared/Clothing/SharedCursedMaskSystem.cs new file mode 100644 index 0000000000..8ba83be151 --- /dev/null +++ b/Content.Shared/Clothing/SharedCursedMaskSystem.cs @@ -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; + +/// +/// This handles +/// +public abstract class SharedCursedMaskSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnClothingEquip); + SubscribeLocalEvent(OnClothingUnequip); + SubscribeLocalEvent(OnExamine); + + SubscribeLocalEvent>(OnMovementSpeedModifier); + SubscribeLocalEvent>(OnModifyDamage); + } + + private void OnClothingEquip(Entity ent, ref ClothingGotEquippedEvent args) + { + RandomizeCursedMask(ent, args.Wearer); + TryTakeover(ent, args.Wearer); + } + + protected virtual void OnClothingUnequip(Entity ent, ref ClothingGotUnequippedEvent args) + { + RandomizeCursedMask(ent, args.Wearer); + } + + private void OnExamine(Entity ent, ref ExaminedEvent args) + { + args.PushMarkup(Loc.GetString($"cursed-mask-examine-{ent.Comp.CurrentState.ToString()}")); + } + + private void OnMovementSpeedModifier(Entity ent, ref InventoryRelayedEvent args) + { + if (ent.Comp.CurrentState == CursedMaskExpression.Joy) + args.Args.ModifySpeed(ent.Comp.JoySpeedModifier); + } + + private void OnModifyDamage(Entity ent, ref InventoryRelayedEvent args) + { + if (ent.Comp.CurrentState == CursedMaskExpression.Despair) + args.Args.Damage = DamageSpecifier.ApplyModifierSet(args.Args.Damage, ent.Comp.DespairDamageModifier); + } + + protected void RandomizeCursedMask(Entity ent, EntityUid wearer) + { + var random = new System.Random((int) _timing.CurTick.Value); + ent.Comp.CurrentState = random.Pick(Enum.GetValues()); + _appearance.SetData(ent, CursedMaskVisuals.State, ent.Comp.CurrentState); + _movementSpeedModifier.RefreshMovementSpeedModifiers(wearer); + } + + protected virtual void TryTakeover(Entity ent, EntityUid wearer) + { + + } +} diff --git a/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs b/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs index d8d0a2a23b..b647ee8e92 100644 --- a/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs +++ b/Content.Shared/Inventory/Events/UnequipAttemptEvent.cs @@ -17,6 +17,11 @@ public abstract class UnequipAttemptEventBase : CancellableEntityEventArgs /// public readonly EntityUid Equipment; + /// + /// The slotFlags of the slot this item is being removed from. + /// + public readonly SlotFlags SlotFlags; + /// /// The slot the entity is being unequipped from. /// @@ -33,6 +38,7 @@ public abstract class UnequipAttemptEventBase : CancellableEntityEventArgs UnEquipTarget = unEquipTarget; Equipment = equipment; Unequipee = unequipee; + SlotFlags = slotDefinition.SlotFlags; Slot = slotDefinition.Name; } } diff --git a/Content.Shared/Inventory/SelfEquipOnlyComponent.cs b/Content.Shared/Inventory/SelfEquipOnlyComponent.cs new file mode 100644 index 0000000000..ee1980ef8a --- /dev/null +++ b/Content.Shared/Inventory/SelfEquipOnlyComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Inventory; + +/// +/// This is used for an item that can only be equipped/unequipped by the user. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SelfEquipOnlySystem))] +public sealed partial class SelfEquipOnlyComponent : Component +{ + /// + /// Whether or not the self-equip only condition requires the person to be conscious. + /// + [DataField] + public bool UnequipRequireConscious = true; +} diff --git a/Content.Shared/Inventory/SelfEquipOnlySystem.cs b/Content.Shared/Inventory/SelfEquipOnlySystem.cs new file mode 100644 index 0000000000..2bd113e22b --- /dev/null +++ b/Content.Shared/Inventory/SelfEquipOnlySystem.cs @@ -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!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnBeingEquipped); + SubscribeLocalEvent(OnBeingUnequipped); + } + + private void OnBeingEquipped(Entity ent, ref BeingEquippedAttemptEvent args) + { + if (args.Cancelled) + return; + + if (TryComp(ent, out var clothing) && (clothing.Slots & args.SlotFlags) == SlotFlags.NONE) + return; + + if (args.Equipee != args.EquipTarget) + args.Cancel(); + } + + private void OnBeingUnequipped(Entity ent, ref BeingUnequippedAttemptEvent args) + { + if (args.Cancelled) + return; + + if (args.Unequipee == args.UnEquipTarget) + return; + + if (TryComp(ent, out var clothing) && (clothing.Slots & args.SlotFlags) == SlotFlags.NONE) + return; + + if (ent.Comp.UnequipRequireConscious && !_actionBlocker.CanConsciouslyPerformAction(args.UnEquipTarget)) + return; + args.Cancel(); + } +} diff --git a/Content.Shared/Movement/Systems/MovementSpeedModifierSystem.cs b/Content.Shared/Movement/Systems/MovementSpeedModifierSystem.cs index 7c793d5eb8..8e89e4b62b 100644 --- a/Content.Shared/Movement/Systems/MovementSpeedModifierSystem.cs +++ b/Content.Shared/Movement/Systems/MovementSpeedModifierSystem.cs @@ -69,5 +69,10 @@ namespace Content.Shared.Movement.Systems WalkSpeedModifier *= walk; SprintSpeedModifier *= sprint; } + + public void ModifySpeed(float mod) + { + ModifySpeed(mod, mod); + } } } diff --git a/Content.Shared/NPC/Systems/NpcFactionSystem.cs b/Content.Shared/NPC/Systems/NpcFactionSystem.cs index 355f5bbb3a..98f14afe2a 100644 --- a/Content.Shared/NPC/Systems/NpcFactionSystem.cs +++ b/Content.Shared/NPC/Systems/NpcFactionSystem.cs @@ -100,6 +100,28 @@ public sealed partial class NpcFactionSystem : EntitySystem RefreshFactions((ent, ent.Comp)); } + /// + /// Adds this entity to the particular faction. + /// + public void AddFactions(Entity ent, HashSet> factions, bool dirty = true) + { + ent.Comp ??= EnsureComp(ent); + + foreach (var faction in factions) + { + if (!_proto.HasIndex(faction)) + { + Log.Error($"Unable to find faction {faction}"); + continue; + } + + ent.Comp.Factions.Add(faction); + } + + if (dirty) + RefreshFactions((ent, ent.Comp)); + } + /// /// Removes this entity from the particular faction. /// diff --git a/Resources/Locale/en-US/clothing/components/cursed-mask.ftl b/Resources/Locale/en-US/clothing/components/cursed-mask.ftl new file mode 100644 index 0000000000..c93a6cfb87 --- /dev/null +++ b/Resources/Locale/en-US/clothing/components/cursed-mask.ftl @@ -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! diff --git a/Resources/Prototypes/Entities/Clothing/Masks/specific.yml b/Resources/Prototypes/Entities/Clothing/Masks/specific.yml index c3a07fa8e9..64a1adcebd 100644 --- a/Resources/Prototypes/Entities/Clothing/Masks/specific.yml +++ b/Resources/Prototypes/Entities/Clothing/Masks/specific.yml @@ -62,3 +62,41 @@ - type: HideLayerClothing slots: - Snout + +- type: entity + parent: ClothingMaskBase + id: ClothingMaskGoldenCursed + name: golden mask + description: Previously used in strange pantomimes, after one of the actors went mad on stage these masks have avoided use. You swear its face contorts when you're not looking. + components: + - type: Sprite + sprite: Clothing/Mask/goldenmask.rsi + layers: + - state: icon + map: [ "mask" ] + - type: Clothing + sprite: Clothing/Mask/goldenmask.rsi + - type: Appearance + - type: GenericVisualizer + visuals: + enum.CursedMaskVisuals.State: + mask: + Neutral: { state: icon } + Despair: { state: icon-despair } + Joy: { state: icon-joy } + Anger: { state: icon-anger } + - type: Tag + tags: [] # ignore "WhitelistChameleon" tag + - type: SelfEquipOnly + - type: CursedMask + despairDamageModifier: + coefficients: + Blunt: 0.6 + Slash: 0.6 + Piercing: 0.4 + - type: HideLayerClothing + slots: + - Snout + - type: IngestionBlocker + - type: StaticPrice + price: 5000 diff --git a/Resources/Textures/Clothing/Mask/goldenmask.rsi/equipped-MASK-vox.png b/Resources/Textures/Clothing/Mask/goldenmask.rsi/equipped-MASK-vox.png new file mode 100644 index 0000000000000000000000000000000000000000..1b4db3d093f3ce403532aad2a62ec4e9f8ad84d7 GIT binary patch literal 453 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%z>pfi@Ln`LH zz2)84Y{0{oU_9&hh6dO3mt8G_Bfi+^oM;kG6`OY2nP;&|nY-YB^@VH!!jD*1yb^HS zcQbg+-oN^1+14^AUHn&muC?MgZ|U18Ik&8$S6UbO<@1dFMAH2Y{r1eAy7sodWxd)< zF^PDaSBW>}`MKrg<5!+to+)=-`QP=Q#-5AhpBCC#6HS>P zxtLVzouBtTd#WhI&ga|bZ}s9jU=SqCH2rC#2LC>Rf6qfBv|cyuU3{bTPUg=w8$Y}5 zd0f8#kHD^_(dBYLe?q`Lol8%`R(@Y|bI;xC^X`;E&}P{}&-u?S>aHuX ru9st2^tW}I4RhYkcQBVRF!=ms5dXPZm)qYlA0+7M>gTe~DWM4fti--h literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Mask/goldenmask.rsi/equipped-MASK.png b/Resources/Textures/Clothing/Mask/goldenmask.rsi/equipped-MASK.png new file mode 100644 index 0000000000000000000000000000000000000000..d1353c49800fd1c34bbec12e444e800b915cf49e GIT binary patch literal 401 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%zy**tVLn`LH zy=7Q*SV5rm;c}mMilrQw59xWZdaN*K zTe|mn{bsIPT9Vg3imNZ+h&%c9XXNeUQS$DkXPcM#FE!M#t=iVLf5~6-?(hRIb$0&S zclPVfy{&l_F<vY;i1E_j_xbB5vjaT>0t*g_eP>yd6eiyv&6jlAaE+Hk zPbI_l<0)$+j>b)GI3#~7ZVJ$8@;Sev<~9_CF9;RNQ~&>Wo)hn6Q33Up(mGXDe&#o1 zkMEBz33|sJqcth6G$Lx%`#HW}RTr}y(X5{mSL;*vvie?{m}kV)2BQU=)T96Mp5n5Y o`~Sqnnh&|}*|+w=9PPx$U`a$lR9J=WRy!7hAP^k#avuvUJWmQelNLFTg%qBM6l8TJd=?Umyyr9q#GL_V z2*8CeVQ1KHzQ6rS$ms$?Gqp3US>x}1Dj-G_0tu7gE)@`h>Y(flt2(IK8vo;g5Kjqq+q_>YLhN3D*1mvHza$;m0shibaU$fi325!wF%hB^Xzhv-K|27d+sV!9+U>9lA@UrOCZvdPdF*aTFNU$_7OFJ?WsQYw_3 QuK)l507*qoM6N<$g6~we=>Px# literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Mask/goldenmask.rsi/icon-despair.png b/Resources/Textures/Clothing/Mask/goldenmask.rsi/icon-despair.png new file mode 100644 index 0000000000000000000000000000000000000000..71bdd72fc6fe15a98498be51aa83c4dd9641188b GIT binary patch literal 414 zcmV;P0b%}$P)Px$SV=@dR9J=WRy(r8APihG?>;U_;q$!0K2uWUJZ@C@M7)Agka>IrI7Mc>Gnv>5 z(XPY_8@MnCx5174|8|y;j|)gu>NYsq^S_-cAV-t}3)A37DFB=fStG$EgKvT7pWvHV}l zF`^@Jsrsq_32r4P1&-SxwoFOR8qsk?bL)o%0KobTNx2)Td%3QLp*JEPJ-~ON z2kj=LKpvk1(kEJUy~{loIC(GufxM;t-2$YDw}1}v3l{+359rLWDLe&DK>z>%07*qo IM6N<$f=9Ek8UO$Q literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Mask/goldenmask.rsi/icon-joy.png b/Resources/Textures/Clothing/Mask/goldenmask.rsi/icon-joy.png new file mode 100644 index 0000000000000000000000000000000000000000..93d0e26ddd995f797956bdedadd6c3886a852472 GIT binary patch literal 414 zcmV;P0b%}$P)Px$SV=@dR9J=WRxy%;FbrJZh=EfO+bmPR!o%#WC8qjBT=KpvtTt~wE%z3k2!7?VO|0yljXJY_^S6QaWRV9C!ShP z%vHv59_O}cR)D%(1n7U?NmEer=?-|mj#DB`OW^&QOv!J<`eQPV$+D4`kTs#6bX>Iv z@EG6U)tJaDajyEJ0ESMbCI!+Suo@7sEu*AZJvSK05|sAp)}pMXDUq33Vq*_x2hWs)0Q;e<2>f~N@)Hf zvNoo}1+WidQuc`zP2X~3kH7~Llh;gtvj7(HC!m4+#0da+0uW26unb2>O#lD@07*qo IM6N<$g8zoFF#rGn literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Mask/goldenmask.rsi/icon.png b/Resources/Textures/Clothing/Mask/goldenmask.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1da86e3c821b218b25d19b67a700f505e9d9410b GIT binary patch literal 402 zcmV;D0d4+?P)Px$Oi4sRR9J=WRa0492Q9{P> zLnOs{OCvI$zqbBJr3Iv=qF0f_dLnH=^EjNVJ=f%0|oiq=v4$ w+;m0Y9-oqrx3)dIFOw|dEue#Z#|{8^0z3MuXv#n8!~g&Q07*qoM6N<$f@_bkmjD0& literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Mask/goldenmask.rsi/inhand-left.png b/Resources/Textures/Clothing/Mask/goldenmask.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..3ce2895a3976461d1346d46c0044f2d76a6e9cd1 GIT binary patch literal 360 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%zl{{S>Ln`LH zz2%s7*g&B5q36rmT?OY;=6l9{v5AW4o|CaFpncZ_0h7uy?!Wd1H=25m1$}Tk&jU0D z2t00f{X9{zZQiZZl9T^%$DPf8JL~!#zqI=A*{5>eZH+g4c6HC0o#LHUzoWYB_yy0_ z{bgQMc;9Qkd~8eg*S;6A-*_{^H@`faqy4v1`|r-!(yx65fudTQf11qP`ps7C{Anw@ z^J}YSTJ?KyyIofN{ptIwz54YvjAyDY7p}iySZWJ#0|Uc?(qlLGY4tnUJ>W=}NnQP! zLuexZi8Lm+6`wh@Uiuv1NR@jSdUZU)g+<%{X}gP#a4;S}8b`TU2I{0s+#?HE^fU)9Wcd2kxYK2KLamvv4FO#m{p Bpb-E7 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Mask/goldenmask.rsi/inhand-right.png b/Resources/Textures/Clothing/Mask/goldenmask.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..ba7133027623e415e20fb05a1efb229af3b12553 GIT binary patch literal 353 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%zr9E97Ln`LH zy=5rWWFXM`aQWj}oedvc3YG;_$Z>1f1nNBM>sWMzQ{~K`_|$-#(?vIUr~r)s0*6Ug zEPu{;y!6-9^8H~G&VIc9{amTvzsnyd{hfRL>iWrFuN#z=-sF`?|EIm)_g~@v2<1Jo zjIHL1TJN72FWB#SHSkr8M7dti@90klJdZxR>$~@1*3VUwe*LRFbIjNJ@7_CY`@hZE z6R)1+Yp~QmuIlgY*^|FNPid3Yt^IWA+b1T76Bq(k&8cRaw(8)&1_{gWJUXF|1wKFg zCe-xiT;j=-PFts|E~;Ip>ls!b3Y6^0UT#x<#aQ9_yvD@0Q?@%_SvsZo{DB>1tEw)l xpWK&5w#_QSZ4@T!+`^TuzXnlL{nTISlL|H+$dAj