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;
// 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))
RaiseLocalEvent(buyer, listing.ProductEvent);
}
+ if (listing.DisableRefund)
+ {
+ component.RefundAllowed = false;
+ }
+
//log dat shit.
_admin.Add(LogType.StorePurchase,
LogImpact.Low,
--- /dev/null
+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; }
+}
-using System.Numerics;
+using System.Numerics;
using Content.Shared.Actions;
using Content.Shared.Body.Components;
using Content.Shared.Body.Systems;
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;
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;
[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()
{
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
_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
/// <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;
other.Categories,
other.OriginalCost,
other.RestockTime,
- other.DiscountDownTo
+ other.DiscountDownTo,
+ other.DisableRefund
)
{
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;
OriginalCost = originalCost;
RestockTime = restockTime;
DiscountDownTo = new Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2>(dataDiscountDownTo);
+ DisableRefund = disableRefund;
}
[ViewVariables]
[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)
listingData.Categories,
listingData.OriginalCost,
listingData.RestockTime,
- listingData.DiscountDownTo
+ listingData.DiscountDownTo,
+ listingData.DisableRefund
)
{
}
-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
-# Spells
+# Spells
spellbook-fireball-name = Fireball
spellbook-fireball-desc = Get most crew exploding with rage when they see this fireball heading toward them!
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!
-# 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
-- 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