diff --git a/Content.Shared/Magic/Events/MindSwapSpellEvent.cs b/Content.Shared/Magic/Events/MindSwapSpellEvent.cs new file mode 100644 index 00000000000000..89319090c1c797 --- /dev/null +++ b/Content.Shared/Magic/Events/MindSwapSpellEvent.cs @@ -0,0 +1,15 @@ +using Content.Shared.Actions; + +namespace Content.Shared.Magic.Events; + +public sealed partial class MindSwapSpellEvent : EntityTargetActionEvent, ISpeakSpell +{ + [DataField] + public TimeSpan PerformerStunDuration = TimeSpan.FromSeconds(10); + + [DataField] + public TimeSpan TargetStunDuration = TimeSpan.FromSeconds(10); + + [DataField] + public string? Speech { get; private set; } +} diff --git a/Content.Shared/Magic/SharedMagicSystem.cs b/Content.Shared/Magic/SharedMagicSystem.cs index 21e137346ef062..acedaf05f65755 100644 --- a/Content.Shared/Magic/SharedMagicSystem.cs +++ b/Content.Shared/Magic/SharedMagicSystem.cs @@ -22,6 +22,7 @@ using Content.Shared.Popups; using Content.Shared.Speech.Muting; using Content.Shared.Storage; +using Content.Shared.Stunnable; using Content.Shared.Tag; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Systems; @@ -62,6 +63,7 @@ public abstract class SharedMagicSystem : EntitySystem [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly SharedStunSystem _stun = default!; public override void Initialize() { @@ -77,6 +79,7 @@ public override void Initialize() SubscribeLocalEvent(OnKnockSpell); SubscribeLocalEvent(OnChargeSpell); SubscribeLocalEvent(OnRandomGlobalSpawnSpell); + SubscribeLocalEvent(OnMindSwapSpell); // Spell wishlist // A wishlish of spells that I'd like to implement or planning on implementing in a future PR @@ -542,6 +545,37 @@ private void OnRandomGlobalSpawnSpell(RandomGlobalSpawnSpellEvent ev) _audio.PlayGlobal(ev.Sound, ev.Performer); } + #endregion + #region Mindswap Spells + + private void OnMindSwapSpell(MindSwapSpellEvent ev) + { + if (ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer)) + return; + + ev.Handled = true; + Speak(ev); + + // Need performer mind, but target mind is unnecessary, such as taking over a NPC + // Need to get target mind before putting performer mind into their body if they have one + // Thus, assign bool before first transfer, then check afterwards + + if (!_mind.TryGetMind(ev.Performer, out var perMind, out var perMindComp)) + return; + + var tarHasMind = _mind.TryGetMind(ev.Target, out var tarMind, out var tarMindComp); + + _mind.TransferTo(perMind, ev.Target); + + if (tarHasMind) + { + _mind.TransferTo(tarMind, ev.Performer); + } + + _stun.TryParalyze(ev.Target, ev.TargetStunDuration, true); + _stun.TryParalyze(ev.Performer, ev.PerformerStunDuration, true); + } + #endregion // End Spells #endregion diff --git a/Resources/Locale/en-US/magic/spells-actions.ftl b/Resources/Locale/en-US/magic/spells-actions.ftl index 21109066d3deb3..faf5d774369613 100644 --- a/Resources/Locale/en-US/magic/spells-actions.ftl +++ b/Resources/Locale/en-US/magic/spells-actions.ftl @@ -5,3 +5,4 @@ action-speech-spell-summon-magicarp = AIE KHUSE EU action-speech-spell-fireball = ONI'SOMA! action-speech-spell-summon-guns = YOR'NEE VES-KORFA action-speech-spell-summon-magic = RYGOIN FEMA-VERECO +action-speech-spell-mind-swap = GIN'YU CAPAN! diff --git a/Resources/Locale/en-US/store/spellbook-catalog.ftl b/Resources/Locale/en-US/store/spellbook-catalog.ftl index dff918b0bc97b8..1450cc86082fe4 100644 --- a/Resources/Locale/en-US/store/spellbook-catalog.ftl +++ b/Resources/Locale/en-US/store/spellbook-catalog.ftl @@ -20,6 +20,9 @@ spellbook-charge-desc = Adds a charge back to your wand! spellbook-ethereal-jaunt-name = Ethereal Jaunt spellbook-ethereal-jaunt-description = Slip into the ethereal plane to slip away from your enemies! +spellbook-mind-swap-name = Mind Swap +spellbook-mind-swap-description = Exchange bodies with another person! + # Equipment spellbook-wand-polymorph-door-name = Wand of Entrance diff --git a/Resources/Prototypes/Catalog/spellbook_catalog.yml b/Resources/Prototypes/Catalog/spellbook_catalog.yml index 57172dc6657b1e..d5a9b0f1b9a3a1 100644 --- a/Resources/Prototypes/Catalog/spellbook_catalog.yml +++ b/Resources/Prototypes/Catalog/spellbook_catalog.yml @@ -91,6 +91,19 @@ - !type:ListingLimitedStockCondition stock: 1 +- type: listing + id: SpellbookMindSwap + name: spellbook-mind-swap-name + description: spellbook-mind-swap-description + productAction: ActionMindSwap + cost: + WizCoin: 2 + categories: + - SpellbookUtility + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + # Equipment - type: listing id: SpellbookWandDoor diff --git a/Resources/Prototypes/Magic/mindswap_spell.yml b/Resources/Prototypes/Magic/mindswap_spell.yml new file mode 100644 index 00000000000000..19b3d41e7078ad --- /dev/null +++ b/Resources/Prototypes/Magic/mindswap_spell.yml @@ -0,0 +1,20 @@ +- type: entity + id: ActionMindSwap + name: Mind Swap + description: Exchange bodies with another person! + components: + - type: EntityTargetAction + useDelay: 300 + itemIconStyle: BigAction + whitelist: + components: + - Body + canTargetSelf: false + interactOnMiss: false + sound: !type:SoundPathSpecifier + path: /Audio/Magic/staff_animation.ogg + icon: + sprite: Mobs/Species/Human/organs.rsi + state: brain + event: !type:MindSwapSpellEvent + speech: action-speech-spell-mind-swap