Wizard Summon Guns/Magic (#32692)

* 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
This commit is contained in:
ActiveMammmoth
2024-11-17 11:46:31 -05:00
committed by GitHub
parent c7f83523ef
commit 22987fc77f
10 changed files with 326 additions and 22 deletions

View File

@@ -53,7 +53,7 @@ public sealed class KillPersonConditionSystem : EntitySystem
return; return;
// no other humans to kill // no other humans to kill
var allHumans = _mind.GetAliveHumansExcept(args.MindId); var allHumans = _mind.GetAliveHumans(args.MindId);
if (allHumans.Count == 0) if (allHumans.Count == 0)
{ {
args.Cancelled = true; args.Cancelled = true;
@@ -77,14 +77,14 @@ public sealed class KillPersonConditionSystem : EntitySystem
return; return;
// no other humans to kill // no other humans to kill
var allHumans = _mind.GetAliveHumansExcept(args.MindId); var allHumans = _mind.GetAliveHumans(args.MindId);
if (allHumans.Count == 0) if (allHumans.Count == 0)
{ {
args.Cancelled = true; args.Cancelled = true;
return; return;
} }
var allHeads = new List<EntityUid>(); var allHeads = new HashSet<Entity<MindComponent>>();
foreach (var person in allHumans) foreach (var person in allHumans)
{ {
if (TryComp<MindComponent>(person, out var mind) && mind.OwnedEntity is { } ent && HasComp<CommandStaffComponent>(ent)) if (TryComp<MindComponent>(person, out var mind) && mind.OwnedEntity is { } ent && HasComp<CommandStaffComponent>(ent))

View File

@@ -256,6 +256,11 @@ public sealed partial class StoreSystem
RaiseLocalEvent(buyer, listing.ProductEvent); RaiseLocalEvent(buyer, listing.ProductEvent);
} }
if (listing.DisableRefund)
{
component.RefundAllowed = false;
}
//log dat shit. //log dat shit.
_admin.Add(LogType.StorePurchase, _admin.Add(LogType.StorePurchase,
LogImpact.Low, LogImpact.Low,

View File

@@ -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; }
}

View File

@@ -1,4 +1,4 @@
using System.Numerics; using System.Numerics;
using Content.Shared.Actions; using Content.Shared.Actions;
using Content.Shared.Body.Components; using Content.Shared.Body.Components;
using Content.Shared.Body.Systems; using Content.Shared.Body.Systems;
@@ -7,12 +7,17 @@ using Content.Shared.Doors.Components;
using Content.Shared.Doors.Systems; using Content.Shared.Doors.Systems;
using Content.Shared.Hands.Components; using Content.Shared.Hands.Components;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
using Content.Shared.Humanoid;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Lock; using Content.Shared.Lock;
using Content.Shared.Magic.Components; using Content.Shared.Magic.Components;
using Content.Shared.Magic.Events; using Content.Shared.Magic.Events;
using Content.Shared.Maps; 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.Physics;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Speech.Muting; using Content.Shared.Speech.Muting;
@@ -20,6 +25,7 @@ using Content.Shared.Storage;
using Content.Shared.Tag; using Content.Shared.Tag;
using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Components;
using Content.Shared.Weapons.Ranged.Systems; using Content.Shared.Weapons.Ranged.Systems;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Network; using Robust.Shared.Network;
@@ -53,6 +59,9 @@ public abstract class SharedMagicSystem : EntitySystem
[Dependency] private readonly LockSystem _lock = default!; [Dependency] private readonly LockSystem _lock = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly TagSystem _tag = 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() public override void Initialize()
{ {
@@ -67,6 +76,7 @@ public abstract class SharedMagicSystem : EntitySystem
SubscribeLocalEvent<SmiteSpellEvent>(OnSmiteSpell); SubscribeLocalEvent<SmiteSpellEvent>(OnSmiteSpell);
SubscribeLocalEvent<KnockSpellEvent>(OnKnockSpell); SubscribeLocalEvent<KnockSpellEvent>(OnKnockSpell);
SubscribeLocalEvent<ChargeSpellEvent>(OnChargeSpell); SubscribeLocalEvent<ChargeSpellEvent>(OnChargeSpell);
SubscribeLocalEvent<RandomGlobalSpawnSpellEvent>(OnRandomGlobalSpawnSpell);
// Spell wishlist // Spell wishlist
// A wishlish of spells that I'd like to implement or planning on implementing in a future PR // 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); _gunSystem.UpdateBasicEntityAmmoCount(wand.Value, basicAmmoComp.Count.Value + ev.Charge, basicAmmoComp);
} }
// End Charge Spells // 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 #endregion
// End Spells // End Spells
#endregion #endregion

View File

@@ -532,22 +532,19 @@ public abstract class SharedMindSystem : EntitySystem
/// <summary> /// <summary>
/// Returns a list of every living humanoid player's minds, except for a single one which is exluded. /// Returns a list of every living humanoid player's minds, except for a single one which is exluded.
/// </summary> /// </summary>
public List<EntityUid> GetAliveHumansExcept(EntityUid exclude) public HashSet<Entity<MindComponent>> GetAliveHumans(EntityUid? exclude = null)
{ {
var mindQuery = EntityQuery<MindComponent>(); var allHumans = new HashSet<Entity<MindComponent>>();
var allHumans = new List<EntityUid>();
// HumanoidAppearanceComponent is used to prevent mice, pAIs, etc from being chosen // HumanoidAppearanceComponent is used to prevent mice, pAIs, etc from being chosen
var query = EntityQueryEnumerator<MindContainerComponent, MobStateComponent, HumanoidAppearanceComponent>(); var query = EntityQueryEnumerator<MobStateComponent, HumanoidAppearanceComponent>();
while (query.MoveNext(out var uid, out var mc, out var mobState, out _)) while (query.MoveNext(out var uid, out var mobState, out _))
{ {
// the player needs to have a mind and not be the excluded one // the player needs to have a mind and not be the excluded one +
if (mc.Mind == null || mc.Mind == exclude) // the player has to be alive
if (!TryGetMind(uid, out var mind, out var mindComp) || mind == exclude || !_mobState.IsAlive(uid, mobState))
continue; continue;
// the player has to be alive allHumans.Add(new Entity<MindComponent>(mind, mindComp));
if (_mobState.IsAlive(uid, mobState))
allHumans.Add(mc.Mind.Value);
} }
return allHumans; return allHumans;

View File

@@ -39,7 +39,8 @@ public partial class ListingData : IEquatable<ListingData>
other.Categories, other.Categories,
other.OriginalCost, other.OriginalCost,
other.RestockTime, other.RestockTime,
other.DiscountDownTo other.DiscountDownTo,
other.DisableRefund
) )
{ {
@@ -63,7 +64,8 @@ public partial class ListingData : IEquatable<ListingData>
HashSet<ProtoId<StoreCategoryPrototype>> categories, HashSet<ProtoId<StoreCategoryPrototype>> categories,
IReadOnlyDictionary<ProtoId<CurrencyPrototype>, FixedPoint2> originalCost, IReadOnlyDictionary<ProtoId<CurrencyPrototype>, FixedPoint2> originalCost,
TimeSpan restockTime, TimeSpan restockTime,
Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> dataDiscountDownTo Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> dataDiscountDownTo,
bool disableRefund
) )
{ {
Name = name; Name = name;
@@ -84,6 +86,7 @@ public partial class ListingData : IEquatable<ListingData>
OriginalCost = originalCost; OriginalCost = originalCost;
RestockTime = restockTime; RestockTime = restockTime;
DiscountDownTo = new Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2>(dataDiscountDownTo); DiscountDownTo = new Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2>(dataDiscountDownTo);
DisableRefund = disableRefund;
} }
[ViewVariables] [ViewVariables]
@@ -194,6 +197,12 @@ public partial class ListingData : IEquatable<ListingData>
[DataField] [DataField]
public Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> DiscountDownTo = new(); 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) public bool Equals(ListingData? listing)
{ {
if (listing == null) if (listing == null)
@@ -287,7 +296,8 @@ public sealed partial class ListingDataWithCostModifiers : ListingData
listingData.Categories, listingData.Categories,
listingData.OriginalCost, listingData.OriginalCost,
listingData.RestockTime, listingData.RestockTime,
listingData.DiscountDownTo listingData.DiscountDownTo,
listingData.DisableRefund
) )
{ {
} }

View 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-knock = AULIE OXIN FIERA
action-speech-spell-smite = EI NATH! action-speech-spell-smite = EI NATH!
action-speech-spell-summon-magicarp = AIE KHUSE EU action-speech-spell-summon-magicarp = AIE KHUSE EU
action-speech-spell-fireball = ONI'SOMA! action-speech-spell-fireball = ONI'SOMA!
action-speech-spell-summon-guns = YOR'NEE VES-KORFA
action-speech-spell-summon-magic = RYGOIN FEMA-VERECO

View File

@@ -1,4 +1,4 @@
# Spells # Spells
spellbook-fireball-name = Fireball spellbook-fireball-name = Fireball
spellbook-fireball-desc = Get most crew exploding with rage when they see this fireball heading toward them! 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-name = Summon Ghosts
spellbook-event-summon-ghosts-description = Who ya gonna call? 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 # Upgrades
spellbook-upgrade-fireball-name = Upgrade Fireball spellbook-upgrade-fireball-name = Upgrade Fireball
spellbook-upgrade-fireball-description = Upgrades Fireball to a maximum of level 3! spellbook-upgrade-fireball-description = Upgrades Fireball to a maximum of level 3!

View File

@@ -1,4 +1,4 @@
# Offensive # Offensive
- type: listing - type: listing
id: SpellbookFireball id: SpellbookFireball
name: spellbook-fireball-name name: spellbook-fireball-name
@@ -132,6 +132,34 @@
- !type:ListingLimitedStockCondition - !type:ListingLimitedStockCondition
stock: 1 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 # Upgrades
- type: listing - type: listing
id: SpellbookFireballUpgrade id: SpellbookFireballUpgrade

View File

@@ -1,4 +1,4 @@
- type: entity - type: entity
id: ActionSummonGhosts id: ActionSummonGhosts
name: Summon Ghosts name: Summon Ghosts
description: Makes all current ghosts permanently invisible description: Makes all current ghosts permanently invisible
@@ -10,3 +10,195 @@
sprite: Mobs/Ghosts/ghost_human.rsi sprite: Mobs/Ghosts/ghost_human.rsi
state: icon state: icon
event: !type:ToggleGhostVisibilityToAllEvent 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