]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Wizard Summon Guns/Magic (#32692)
authorActiveMammmoth <140334666+ActiveMammmoth@users.noreply.github.com>
Sun, 17 Nov 2024 16:46:31 +0000 (11:46 -0500)
committerGitHub <noreply@github.com>
Sun, 17 Nov 2024 16:46:31 +0000 (17:46 +0100)
* mostly done but there's a bug with spawning

* RandomGlobalSpawnSpellEvent now actually works

* Summon Guns/Magic is working

* Added sound, cap gun, and auto pick up

* Added all requested changes/fixes from reviews

* Halving cooldowns

Content.Server/Objectives/Systems/KillPersonConditionSystem.cs
Content.Server/Store/Systems/StoreSystem.Ui.cs
Content.Shared/Magic/Events/RandomGlobalSpawnSpellEvent.cs [new file with mode: 0644]
Content.Shared/Magic/SharedMagicSystem.cs
Content.Shared/Mind/SharedMindSystem.cs
Content.Shared/Store/ListingPrototype.cs
Resources/Locale/en-US/magic/spells-actions.ftl
Resources/Locale/en-US/store/spellbook-catalog.ftl
Resources/Prototypes/Catalog/spellbook_catalog.yml
Resources/Prototypes/Magic/event_spells.yml

index 8dcbf191b36632a91cdacef0b1a7683c0915a366..f662d6a578c80763d0aabe244979416026001576 100644 (file)
@@ -53,7 +53,7 @@ public sealed class KillPersonConditionSystem : EntitySystem
             return;
 
         // no other humans to kill
-        var allHumans = _mind.GetAliveHumansExcept(args.MindId);
+        var allHumans = _mind.GetAliveHumans(args.MindId);
         if (allHumans.Count == 0)
         {
             args.Cancelled = true;
@@ -77,14 +77,14 @@ public sealed class KillPersonConditionSystem : EntitySystem
             return;
 
         // no other humans to kill
-        var allHumans = _mind.GetAliveHumansExcept(args.MindId);
+        var allHumans = _mind.GetAliveHumans(args.MindId);
         if (allHumans.Count == 0)
         {
             args.Cancelled = true;
             return;
         }
 
-        var allHeads = new List<EntityUid>();
+        var allHeads = new HashSet<Entity<MindComponent>>();
         foreach (var person in allHumans)
         {
             if (TryComp<MindComponent>(person, out var mind) && mind.OwnedEntity is { } ent && HasComp<CommandStaffComponent>(ent))
index f1c0cb1e906bd85f81f75995fb44be69447ff750..5af6ce1c975ce3275351d267c98ec8189eefff2b 100644 (file)
@@ -256,6 +256,11 @@ public sealed partial class StoreSystem
                 RaiseLocalEvent(buyer, listing.ProductEvent);
         }
 
+        if (listing.DisableRefund)
+        {
+            component.RefundAllowed = false;
+        }
+
         //log dat shit.
         _admin.Add(LogType.StorePurchase,
             LogImpact.Low,
diff --git a/Content.Shared/Magic/Events/RandomGlobalSpawnSpellEvent.cs b/Content.Shared/Magic/Events/RandomGlobalSpawnSpellEvent.cs
new file mode 100644 (file)
index 0000000..c776075
--- /dev/null
@@ -0,0 +1,23 @@
+using Content.Shared.Actions;
+using Content.Shared.Storage;
+using Robust.Shared.Audio;
+
+namespace Content.Shared.Magic.Events;
+
+public sealed partial class RandomGlobalSpawnSpellEvent : InstantActionEvent, ISpeakSpell
+{
+    /// <summary>
+    /// The list of prototypes this spell can spawn, will select one randomly
+    /// </summary>
+    [DataField]
+    public List<EntitySpawnEntry> Spawns = new();
+
+    /// <summary>
+    /// Sound that will play globally when cast
+    /// </summary>
+    [DataField]
+    public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Magic/staff_animation.ogg");
+
+    [DataField]
+    public string? Speech { get; private set; }
+}
index 02d483c0a3f2c9e13369e3cfb496d24d98eadc0c..21e137346ef062f972cc779893ac10af492caea4 100644 (file)
@@ -1,4 +1,4 @@
-using System.Numerics;
+using System.Numerics;
 using Content.Shared.Actions;
 using Content.Shared.Body.Components;
 using Content.Shared.Body.Systems;
@@ -7,12 +7,17 @@ using Content.Shared.Doors.Components;
 using Content.Shared.Doors.Systems;
 using Content.Shared.Hands.Components;
 using Content.Shared.Hands.EntitySystems;
+using Content.Shared.Humanoid;
 using Content.Shared.Interaction;
 using Content.Shared.Inventory;
 using Content.Shared.Lock;
 using Content.Shared.Magic.Components;
 using Content.Shared.Magic.Events;
 using Content.Shared.Maps;
+using Content.Shared.Mind;
+using Content.Shared.Mind.Components;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Mobs.Systems;
 using Content.Shared.Physics;
 using Content.Shared.Popups;
 using Content.Shared.Speech.Muting;
@@ -20,6 +25,7 @@ using Content.Shared.Storage;
 using Content.Shared.Tag;
 using Content.Shared.Weapons.Ranged.Components;
 using Content.Shared.Weapons.Ranged.Systems;
+using Robust.Shared.Audio.Systems;
 using Robust.Shared.Map;
 using Robust.Shared.Map.Components;
 using Robust.Shared.Network;
@@ -53,6 +59,9 @@ public abstract class SharedMagicSystem : EntitySystem
     [Dependency] private readonly LockSystem _lock = default!;
     [Dependency] private readonly SharedHandsSystem _hands = default!;
     [Dependency] private readonly TagSystem _tag = default!;
+    [Dependency] private readonly MobStateSystem _mobState = default!;
+    [Dependency] private readonly SharedAudioSystem _audio = default!;
+    [Dependency] private readonly SharedMindSystem _mind = default!;
 
     public override void Initialize()
     {
@@ -67,6 +76,7 @@ public abstract class SharedMagicSystem : EntitySystem
         SubscribeLocalEvent<SmiteSpellEvent>(OnSmiteSpell);
         SubscribeLocalEvent<KnockSpellEvent>(OnKnockSpell);
         SubscribeLocalEvent<ChargeSpellEvent>(OnChargeSpell);
+        SubscribeLocalEvent<RandomGlobalSpawnSpellEvent>(OnRandomGlobalSpawnSpell);
 
         // Spell wishlist
         //  A wishlish of spells that I'd like to implement or planning on implementing in a future PR
@@ -501,6 +511,37 @@ public abstract class SharedMagicSystem : EntitySystem
         _gunSystem.UpdateBasicEntityAmmoCount(wand.Value, basicAmmoComp.Count.Value + ev.Charge, basicAmmoComp);
     }
     // End Charge Spells
+    #endregion
+    #region Global Spells
+
+    private void OnRandomGlobalSpawnSpell(RandomGlobalSpawnSpellEvent ev)
+    {
+        if (!_net.IsServer || ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer) || ev.Spawns is not { } spawns)
+            return;
+
+        ev.Handled = true;
+        Speak(ev);
+
+        var allHumans = _mind.GetAliveHumans();
+
+        foreach (var human in allHumans)
+        {
+            if (!human.Comp.OwnedEntity.HasValue)
+                continue;
+
+            var ent = human.Comp.OwnedEntity.Value;
+
+            var mapCoords = _transform.GetMapCoordinates(ent);
+            foreach (var spawn in EntitySpawnCollection.GetSpawns(spawns, _random))
+            {
+                var spawned = Spawn(spawn, mapCoords);
+                _hands.PickupOrDrop(ent, spawned);
+            }
+        }
+
+        _audio.PlayGlobal(ev.Sound, ev.Performer);
+    }
+
     #endregion
     // End Spells
     #endregion
index bf0b5f650adb616b17572cc1a130ee6483ac5e81..0f5f9172cc692c569051bcc13d64ecff092cf6c8 100644 (file)
@@ -532,22 +532,19 @@ public abstract class SharedMindSystem : EntitySystem
     /// <summary>
     /// Returns a list of every living humanoid player's minds, except for a single one which is exluded.
     /// </summary>
-    public List<EntityUid> GetAliveHumansExcept(EntityUid exclude)
+    public HashSet<Entity<MindComponent>> GetAliveHumans(EntityUid? exclude = null)
     {
-        var mindQuery = EntityQuery<MindComponent>();
-
-        var allHumans = new List<EntityUid>();
+        var allHumans = new HashSet<Entity<MindComponent>>();
         // HumanoidAppearanceComponent is used to prevent mice, pAIs, etc from being chosen
-        var query = EntityQueryEnumerator<MindContainerComponent, MobStateComponent, HumanoidAppearanceComponent>();
-        while (query.MoveNext(out var uid, out var mc, out var mobState, out _))
+        var query = EntityQueryEnumerator<MobStateComponent, HumanoidAppearanceComponent>();
+        while (query.MoveNext(out var uid, out var mobState, out _))
         {
-            // the player needs to have a mind and not be the excluded one
-            if (mc.Mind == null || mc.Mind == exclude)
+            // the player needs to have a mind and not be the excluded one +
+            // the player has to be alive
+            if (!TryGetMind(uid, out var mind, out var mindComp) || mind == exclude || !_mobState.IsAlive(uid, mobState))
                 continue;
 
-            // the player has to be alive
-            if (_mobState.IsAlive(uid, mobState))
-                allHumans.Add(mc.Mind.Value);
+            allHumans.Add(new Entity<MindComponent>(mind, mindComp));
         }
 
         return allHumans;
index 05ac5cc4cd5eb72ffa5512e2e47d816819f10139..e2339b72c67e25650db935137eb18179fd9cb3c7 100644 (file)
@@ -39,7 +39,8 @@ public partial class ListingData : IEquatable<ListingData>
         other.Categories,
         other.OriginalCost,
         other.RestockTime,
-        other.DiscountDownTo
+        other.DiscountDownTo,
+        other.DisableRefund
     )
     {
 
@@ -63,7 +64,8 @@ public partial class ListingData : IEquatable<ListingData>
         HashSet<ProtoId<StoreCategoryPrototype>> categories,
         IReadOnlyDictionary<ProtoId<CurrencyPrototype>, FixedPoint2> originalCost,
         TimeSpan restockTime,
-        Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> dataDiscountDownTo
+        Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> dataDiscountDownTo,
+        bool disableRefund
     )
     {
         Name = name;
@@ -84,6 +86,7 @@ public partial class ListingData : IEquatable<ListingData>
         OriginalCost = originalCost;
         RestockTime = restockTime;
         DiscountDownTo = new Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2>(dataDiscountDownTo);
+        DisableRefund = disableRefund;
     }
 
     [ViewVariables]
@@ -194,6 +197,12 @@ public partial class ListingData : IEquatable<ListingData>
     [DataField]
     public Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> DiscountDownTo = new();
 
+    /// <summary>
+    /// Whether or not to disable refunding for the store when the listing is purchased from it.
+    /// </summary>
+    [DataField]
+    public bool DisableRefund = false;
+
     public bool Equals(ListingData? listing)
     {
         if (listing == null)
@@ -287,7 +296,8 @@ public sealed partial class ListingDataWithCostModifiers : ListingData
             listingData.Categories,
             listingData.OriginalCost,
             listingData.RestockTime,
-            listingData.DiscountDownTo
+            listingData.DiscountDownTo,
+            listingData.DisableRefund
         )
     {
     }
index 40757cd694f2c2b1c433d1768d14190b87b9af7d..21109066d3deb3f57b86a2468b5924c836c1460a 100644 (file)
@@ -1,5 +1,7 @@
-action-speech-spell-forcewall = TARCOL MINTI ZHERI
+action-speech-spell-forcewall = TARCOL MINTI ZHERI
 action-speech-spell-knock = AULIE OXIN FIERA
 action-speech-spell-smite = EI NATH!
 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
index 1d970f6e3ac9c4842a84bcda787216e9a94b5aed..ed62c6fa82ba4b29f5ab3bba4866a376ca2fad41 100644 (file)
@@ -1,4 +1,4 @@
-# Spells
+# Spells
 spellbook-fireball-name = Fireball
 spellbook-fireball-desc = Get most crew exploding with rage when they see this fireball heading toward them!
 
@@ -33,6 +33,12 @@ spellbook-wand-polymorph-carp-description = For when you need a carp filet quick
 spellbook-event-summon-ghosts-name = Summon Ghosts
 spellbook-event-summon-ghosts-description = Who ya gonna call?
 
+spellbook-event-summon-guns-name = Summon Guns
+spellbook-event-summon-guns-description = AK47s for everyone! Places a random gun in front of everybody. Disables refunds when bought!
+
+spellbook-event-summon-magic-name = Summon Magic
+spellbook-event-summon-magic-description = Places a random magical item in front of everybody. Nothing could go wrong! Disables refunds when bought!
+
 # Upgrades
 spellbook-upgrade-fireball-name = Upgrade Fireball
 spellbook-upgrade-fireball-description = Upgrades Fireball to a maximum of level 3!
index 805c157f1e315dc68aca0aee30fa388e425f5d9c..6e9bba87a6a9906484dc3bb12e18624f673e4548 100644 (file)
@@ -1,4 +1,4 @@
-# Offensive
+# Offensive
 - type: listing
   id: SpellbookFireball
   name: spellbook-fireball-name
   - !type:ListingLimitedStockCondition
     stock: 1
 
+- type: listing
+  id: SpellbookEventSummonGuns
+  name: spellbook-event-summon-guns-name
+  description: spellbook-event-summon-guns-description
+  productAction: ActionSummonGuns
+  cost:
+    WizCoin: 2
+  categories:
+  - SpellbookEvents
+  conditions:
+  - !type:ListingLimitedStockCondition
+    stock: 1
+  disableRefund: true
+
+- type: listing
+  id: SpellbookEventSummonMagic
+  name: spellbook-event-summon-magic-name
+  description: spellbook-event-summon-magic-description
+  productAction: ActionSummonMagic
+  cost:
+    WizCoin: 2
+  categories:
+  - SpellbookEvents
+  conditions:
+  - !type:ListingLimitedStockCondition
+    stock: 1
+  disableRefund: true
+
 # Upgrades
 - type: listing
   id: SpellbookFireballUpgrade
index 01006b4ffe096b470c2ca378dba19d6022b970cc..5aad8925a60a27f6918ad7820bdb4685be3b0cb1 100644 (file)
@@ -1,4 +1,4 @@
-- type: entity
+- type: entity
   id: ActionSummonGhosts
   name: Summon Ghosts
   description: Makes all current ghosts permanently invisible
       sprite: Mobs/Ghosts/ghost_human.rsi
       state: icon
     event: !type:ToggleGhostVisibilityToAllEvent
+
+# TODO: Add Whitelist/Blacklist and Component support to EntitySpawnLists (to avoid making huge hardcoded lists like below).
+
+- type: entity
+  id: ActionSummonGuns
+  name: Summon Guns
+  description: AK47s for everyone! Places a random gun in front of everybody.
+  components:
+  - type: Magic
+  - type: InstantAction
+    useDelay: 300
+    itemIconStyle: BigAction
+    icon:
+      sprite: Objects/Weapons/Guns/Rifles/ak.rsi
+      state: base
+    event: !type:RandomGlobalSpawnSpellEvent
+      spawns:
+      - id: WeaponPistolViper
+        orGroup: Guns
+      - id: WeaponPistolCobra
+        orGroup: Guns
+      - id: WeaponPistolMk58
+        orGroup: Guns
+      - id: WeaponPistolN1984
+        orGroup: Guns
+      - id: WeaponRevolverDeckard
+        orGroup: Guns
+      - id: WeaponRevolverInspector
+        orGroup: Guns
+      - id: WeaponRevolverMateba
+        orGroup: Guns
+      - id: WeaponRevolverPython
+        orGroup: Guns
+      - id: WeaponRevolverPirate
+        orGroup: Guns
+      - id: WeaponRifleAk
+        orGroup: Guns
+      - id: WeaponRifleM90GrenadeLauncher
+        orGroup: Guns
+      - id: WeaponRifleLecter
+        orGroup: Guns
+      - id: WeaponShotgunBulldog
+        orGroup: Guns
+      - id: WeaponShotgunDoubleBarreled
+        orGroup: Guns
+      - id: WeaponShotgunEnforcer
+        orGroup: Guns
+      - id: WeaponShotgunKammerer
+        orGroup: Guns
+      - id: WeaponShotgunSawn
+        orGroup: Guns
+      - id: WeaponShotgunHandmade
+        orGroup: Guns
+      - id: WeaponShotgunBlunderbuss
+        orGroup: Guns
+      - id: WeaponShotgunImprovised
+        orGroup: Guns
+      - id: WeaponSubMachineGunAtreides
+        orGroup: Guns
+      - id: WeaponSubMachineGunC20r
+        orGroup: Guns
+      - id: WeaponSubMachineGunDrozd
+        orGroup: Guns
+      - id: WeaponSubMachineGunWt550
+        orGroup: Guns
+      - id: WeaponSniperMosin
+        orGroup: Guns
+      - id: WeaponSniperHristov
+        orGroup: Guns
+      - id: Musket
+        orGroup: Guns
+      - id: WeaponPistolFlintlock
+        orGroup: Guns
+      - id: WeaponLauncherChinaLake
+        orGroup: Guns
+      - id: WeaponLauncherRocket
+        orGroup: Guns
+      - id: WeaponLauncherPirateCannon
+        orGroup: Guns
+      - id: WeaponTetherGun
+        orGroup: Guns
+      - id: WeaponForceGun
+        orGroup: Guns
+      - id: WeaponGrapplingGun
+        orGroup: Guns
+      - id: WeaponLightMachineGunL6
+        orGroup: Guns
+      - id: WeaponLaserSvalinn
+        orGroup: Guns
+      - id: WeaponLaserGun
+        orGroup: Guns
+      - id: WeaponMakeshiftLaser
+        orGroup: Guns
+      - id: WeaponTeslaGun
+        orGroup: Guns
+      - id: WeaponLaserCarbinePractice
+        orGroup: Guns
+      - id: WeaponLaserCarbine
+        orGroup: Guns
+      - id: WeaponPulsePistol
+        orGroup: Guns
+      - id: WeaponPulseCarbine
+        orGroup: Guns
+      - id: WeaponPulseRifle
+        orGroup: Guns
+      - id: WeaponLaserCannon
+        orGroup: Guns
+      - id: WeaponParticleDecelerator
+        orGroup: Guns
+      - id: WeaponXrayCannon
+        orGroup: Guns
+      - id: WeaponDisablerPractice
+        orGroup: Guns
+      - id: WeaponDisabler
+        orGroup: Guns
+      - id: WeaponDisablerSMG
+        orGroup: Guns
+      - id: WeaponTaser
+        orGroup: Guns
+      - id: WeaponAntiqueLaser
+        orGroup: Guns
+      - id: WeaponAdvancedLaser
+        orGroup: Guns
+      - id: WeaponPistolCHIMP
+        orGroup: Guns
+      - id: WeaponBehonkerLaser
+        orGroup: Guns
+      - id: WeaponEnergyShotgun
+        orGroup: Guns
+      - id: WeaponMinigun
+        orGroup: Guns
+      - id: BowImprovised
+        orGroup: Guns
+      - id: WeaponFlareGun
+        orGroup: Guns
+      - id: WeaponImprovisedPneumaticCannon
+        orGroup: Guns
+      - id: WeaponWaterPistol
+        orGroup: Guns
+      - id: WeaponWaterBlaster
+        orGroup: Guns
+      - id: WeaponWaterBlasterSuper
+        orGroup: Guns
+      - id: RevolverCapGun
+        orGroup: Guns
+      - id: RevolverCapGunFake
+        orGroup: Guns
+      speech: action-speech-spell-summon-guns
+      
+- type: entity
+  id: ActionSummonMagic
+  name: Summon Magic
+  description: Places a random magical item in front of everybody. Nothing could go wrong!
+  components:
+  - type: Magic
+  - type: InstantAction
+    useDelay: 300
+    itemIconStyle: BigAction
+    icon:
+      sprite: Objects/Magic/magicactions.rsi
+      state: magicmissile
+    event: !type:RandomGlobalSpawnSpellEvent
+      spawns:
+      - id: SpawnSpellbook
+        orGroup: Magics
+      - id: ForceWallSpellbook
+        orGroup: Magics
+      - id: BlinkBook
+        orGroup: Magics
+      - id: SmiteBook
+        orGroup: Magics
+      - id: KnockSpellbook
+        orGroup: Magics
+      - id: FireballSpellbook
+        orGroup: Magics
+      - id: WeaponWandPolymorphCarp
+        orGroup: Magics
+      - id: WeaponWandPolymorphMonkey
+        orGroup: Magics
+      - id: WeaponWandFireball
+        orGroup: Magics
+      - id: WeaponWandPolymorphDoor
+        orGroup: Magics
+      - id: WeaponWandCluwne
+        orGroup: Magics
+      - id: WeaponWandPolymorphBread
+        orGroup: Magics
+      - id: WeaponStaffHealing
+        orGroup: Magics
+      - id: WeaponStaffPolymorphDoor
+        orGroup: Magics
+      speech: action-speech-spell-summon-magic