]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Adds Store on Collide and Wand of the Locker (#33710)
authorkeronshb <54602815+keronshb@users.noreply.github.com>
Wed, 4 Dec 2024 16:49:54 +0000 (11:49 -0500)
committerGitHub <noreply@github.com>
Wed, 4 Dec 2024 16:49:54 +0000 (17:49 +0100)
* Adds wand of locker and locker projectile

* Adds IsOpen method to check if storage is open

* Adds store on collide

* Adds Store On Collide to Wizard Locker

* Adds Lock API

* Adds locking support

* Adds resist override and custom visual layers

* Fixes decursed states, adds comment for a future visualizer

* adds locker wand visuals and descriptions

* shrinks locker radius, moves TODO for throw support

* Adds whitelist and moves storage and lock logic into their own methods

* Adds support to disable store on collide after the first open. Fixes prediction issues with disabling.

* Adds wand of locker to the grimoire

* Adds wizard access prototype

* Adds Wizard to universal access

* Moves Lock on collide to on collide method

* Comments

* Changes layer order

* Fixes prediction issues when locking.

* Adds Wiz access to universal ID

17 files changed:
Content.Shared/Lock/LockSystem.cs
Content.Shared/Storage/Components/StoreOnCollideComponent.cs [new file with mode: 0644]
Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs
Content.Shared/Storage/EntitySystems/StoreOnCollideSystem.cs [new file with mode: 0644]
Resources/Locale/en-US/prototypes/access/accesses.ftl
Resources/Locale/en-US/store/spellbook-catalog.ftl
Resources/Prototypes/Access/wizard.yml [new file with mode: 0644]
Resources/Prototypes/Catalog/spellbook_catalog.yml
Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml
Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml
Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/wands.yml
Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml
Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker-effect.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker-inhand-left.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker-inhand-right.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker.png [new file with mode: 0644]
Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/meta.json

index 3349034f32e2dd189010c99348938bda956ef16a..411766d8c1f98992713d7d42c94094654316878f 100644 (file)
@@ -131,8 +131,24 @@ public sealed class LockSystem : EntitySystem
                 });
         }
 
-        _sharedPopupSystem.PopupClient(Loc.GetString("lock-comp-do-lock-success",
+        Lock(uid, user, lockComp);
+        return true;
+    }
+
+    /// <summary>
+    ///     Forces a given entity to be locked, does not activate a do-after.
+    /// </summary>
+    public void Lock(EntityUid uid, EntityUid? user, LockComponent? lockComp = null)
+    {
+        if (!Resolve(uid, ref lockComp))
+            return;
+
+        if (user is { Valid: true })
+        {
+            _sharedPopupSystem.PopupClient(Loc.GetString("lock-comp-do-lock-success",
                 ("entityName", Identity.Name(uid, EntityManager))), uid, user);
+        }
+
         _audio.PlayPredicted(lockComp.LockSound, uid, user);
 
         lockComp.Locked = true;
@@ -141,7 +157,6 @@ public sealed class LockSystem : EntitySystem
 
         var ev = new LockToggledEvent(true);
         RaiseLocalEvent(uid, ref ev, true);
-        return true;
     }
 
     /// <summary>
diff --git a/Content.Shared/Storage/Components/StoreOnCollideComponent.cs b/Content.Shared/Storage/Components/StoreOnCollideComponent.cs
new file mode 100644 (file)
index 0000000..d8fff31
--- /dev/null
@@ -0,0 +1,34 @@
+using Content.Shared.Storage.EntitySystems;
+using Content.Shared.Whitelist;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Storage.Components;
+
+// Use where you want an entity to store other entities on collide
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(StoreOnCollideSystem))]
+public sealed partial class StoreOnCollideComponent : Component
+{
+    /// <summary>
+    ///     Entities that are allowed in the storage on collide
+    /// </summary>
+    [DataField]
+    public EntityWhitelist? Whitelist;
+
+    /// <summary>
+    ///     Should this storage lock on collide, provided they have a lock component?
+    /// </summary>
+    [DataField]
+    public bool LockOnCollide;
+
+    /// <summary>
+    ///     Should the behavior be disabled when the storage is first opened?
+    /// </summary>
+    [DataField]
+    public bool DisableWhenFirstOpened;
+
+    /// <summary>
+    ///     If the behavior is disabled or not
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool Disabled;
+}
index 309ac0a2e09358717dc3062f4bcec4b7e5ca9014..829f574ad1d19305ac49a9649cf5df101b48cab8 100644 (file)
@@ -344,6 +344,14 @@ public abstract class SharedEntityStorageSystem : EntitySystem
         return true;
     }
 
+    public bool IsOpen(EntityUid target, SharedEntityStorageComponent? component = null)
+    {
+        if (!ResolveStorage(target, ref component))
+            return false;
+
+        return component.Open;
+    }
+
     public bool CanOpen(EntityUid user, EntityUid target, bool silent = false, SharedEntityStorageComponent? component = null)
     {
         if (!ResolveStorage(target, ref component))
diff --git a/Content.Shared/Storage/EntitySystems/StoreOnCollideSystem.cs b/Content.Shared/Storage/EntitySystems/StoreOnCollideSystem.cs
new file mode 100644 (file)
index 0000000..000e19f
--- /dev/null
@@ -0,0 +1,71 @@
+using Content.Shared.Lock;
+using Content.Shared.Storage.Components;
+using Content.Shared.Whitelist;
+using Robust.Shared.Network;
+using Robust.Shared.Physics.Events;
+using Robust.Shared.Timing;
+
+namespace Content.Shared.Storage.EntitySystems;
+
+internal sealed class StoreOnCollideSystem : EntitySystem
+{
+    [Dependency] private readonly SharedEntityStorageSystem _storage = default!;
+    [Dependency] private readonly LockSystem _lock = default!;
+    [Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
+    [Dependency] private readonly INetManager _netMan = default!;
+    [Dependency] private readonly IGameTiming _gameTiming = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<StoreOnCollideComponent, StartCollideEvent>(OnCollide);
+        SubscribeLocalEvent<StoreOnCollideComponent, StorageAfterOpenEvent>(AfterOpen);
+        // TODO: Add support to stop colliding after throw, wands will need a WandComp
+    }
+
+    // We use Collide instead of Projectile to support different types of interactions
+    private void OnCollide(Entity<StoreOnCollideComponent> ent, ref StartCollideEvent args)
+    {
+        TryStoreTarget(ent, args.OtherEntity);
+
+        TryLockStorage(ent);
+    }
+
+    private void AfterOpen(Entity<StoreOnCollideComponent> ent, ref StorageAfterOpenEvent args)
+    {
+        var comp = ent.Comp;
+
+        if (comp is { DisableWhenFirstOpened: true, Disabled: false })
+            comp.Disabled = true;
+    }
+
+    private void TryStoreTarget(Entity<StoreOnCollideComponent> ent, EntityUid target)
+    {
+        var storageEnt = ent.Owner;
+        var comp = ent.Comp;
+
+        if (_netMan.IsClient || _gameTiming.ApplyingState)
+            return;
+
+        if (ent.Comp.Disabled || storageEnt == target || Transform(target).Anchored || _storage.IsOpen(storageEnt) || _whitelist.IsWhitelistFail(comp.Whitelist, target))
+            return;
+
+        _storage.Insert(target, storageEnt);
+
+    }
+
+    private void TryLockStorage(Entity<StoreOnCollideComponent> ent)
+    {
+        var storageEnt = ent.Owner;
+        var comp = ent.Comp;
+
+        if (_netMan.IsClient || _gameTiming.ApplyingState)
+            return;
+
+        if (ent.Comp.Disabled)
+            return;
+
+        if (comp.LockOnCollide && !_lock.IsLocked(storageEnt))
+            _lock.Lock(storageEnt, storageEnt);
+    }
+}
index 0e8b1d9ac79f92fc53783c01986e2ac51f45cc2c..5e54fcad223a6e5eaad536aafe126febeccaec1a 100644 (file)
@@ -42,3 +42,5 @@ id-card-access-level-nuclear-operative = Nuclear Operative
 id-card-access-level-syndicate-agent = Syndicate Agent
 
 id-card-access-level-central-command = Central Command
+
+id-card-access-level-wizard = Wizard
index ed62c6fa82ba4b29f5ab3bba4866a376ca2fad41..dff918b0bc97b85015f82e8337ec135918e7e28b 100644 (file)
@@ -28,6 +28,9 @@ spellbook-wand-polymorph-door-description = For when you need a get-away route.
 spellbook-wand-polymorph-carp-name = Wand of Carp Polymorph
 spellbook-wand-polymorph-carp-description = For when you need a carp filet quick and the clown is looking juicy.
 
+spellbook-wand-locker-name = Wand of the Locker
+spellbook-wand-locker-description = Shoot cursed lockers at your enemies and lock em away!
+
 # Events
 
 spellbook-event-summon-ghosts-name = Summon Ghosts
diff --git a/Resources/Prototypes/Access/wizard.yml b/Resources/Prototypes/Access/wizard.yml
new file mode 100644 (file)
index 0000000..cacb889
--- /dev/null
@@ -0,0 +1,3 @@
+- type: accessLevel
+  id: Wizard
+  name: id-card-access-level-wizard
index 6e9bba87a6a9906484dc3bb12e18624f673e4548..57172dc6657b1e01d1351f2b272fd2ac77a1ee45 100644 (file)
   - !type:ListingLimitedStockCondition
     stock: 1
 
+- type: listing
+  id: SpellbookWandLocker
+  name: spellbook-wand-locker-name
+  description: spellbook-wand-locker-description
+  productEntity: WeaponWandLocker
+  cost:
+    WizCoin: 3
+  categories:
+  - SpellbookEquipment
+  conditions:
+  - !type:ListingLimitedStockCondition
+    stock: 1
+
 # Event
 - type: listing
   id: SpellbookEventSummonGhosts
index ccb83b6aaa373c543393dbe6b2721b4c92055948..a0e144d0ac4f0a5e995c90130898d96aeea61dd3 100644 (file)
     - CentralCommand
     - NuclearOperative
     - SyndicateAgent
+    - Wizard
index 66f4689099e0ca281f08b74b704a7adb165ba00d..d55437696a4499687e5300e00083f0d5d131aef4 100644 (file)
     - CentralCommand
     - NuclearOperative
     - SyndicateAgent
+    - Wizard
     privilegedIdSlot:
       name: id-card-console-privileged-id
       ejectSound: /Audio/Machines/id_swipe.ogg
index e723a1db5ffe0d9307cffe0e7401ee0d69e8ddfd..d3745288f62939aa50d0a41957e8106eaef065dc 100644 (file)
     capacity: 5
     count: 5
 
+- type: entity
+  name: wand of the locker
+  description: Stuff nerds at a distance!
+  parent: WeaponWandBase
+  id: WeaponWandLocker
+  components:
+  - type: Sprite
+    layers:
+    - state: locker
+      map: ["base"]
+    - state: locker-effect
+      map: ["effect"]
+  - type: Gun
+    soundGunshot:
+      path: /Audio/Weapons/Guns/Gunshots/Magic/staff_animation.ogg
+  - type: BasicEntityAmmoProvider
+    proto: ProjectileLocker
+    capacity: 5
+    count: 5
+  - type: Item
+    size: Normal
+    inhandVisuals:
+      left:
+      - state: locker-inhand-left
+      right:
+      - state: locker-inhand-right
+  - type: GenericVisualizer
+    visuals:
+      enum.AmmoVisuals.HasAmmo:
+        effect:
+          True: { visible: False }
+          False: { visible: True }
+        base:
+          True: { visible: True }
+          False: { visible: False }
+
 - type: entity
   name: magical wand of instant death
   parent: WeaponWandBase
index d11f96d87520fc9fb2caaf09750c362f88e05f13..cd736a33d082633f501fe80f1cd923bdadedcd56 100644 (file)
     totalIntensity: 0.3
     maxTileBreak: 0
 
+- type: entity
+  id: ProjectileLocker
+  name: cursed locker
+  description: A cursed magical locker! Can you resist?
+  parent: ClosetSteelBase
+  categories: [ HideSpawnMenu ]
+  components:
+  - type: ResistLocker
+    resistTime: 30
+  - type: StoreOnCollide
+    lockOnCollide: true
+    disableWhenFirstOpened: true
+    whitelist:
+      components:
+        - Body
+  - type: LockVisuals
+    stateLocked: cursed_door
+    stateUnlocked: decursed_door
+  - type: Lock
+    breakOnEmag: false
+  - type: AccessReader
+    access: [["Wizard"]]
+    breakOnEmag: false
+  - type: Projectile
+    deleteOnCollide: false
+    onlyCollideWhenShot: true
+    damage:
+      types:
+        Brute: 0
+  - type: Sprite
+    noRot: true
+    sprite: Structures/Storage/closet.rsi
+    layers:
+    - state: cursed
+      map: [ "enum.StorageVisualLayers.Base" ]
+    - state: decursed_door
+      map: [ "enum.StorageVisualLayers.Door" ]
+    - state: paper
+      visible: false
+      sprite: Structures/Storage/closet_labels.rsi
+      map: [ "enum.PaperLabelVisuals.Layer" ]
+    - state: cursed_door
+      map: [ "enum.LockVisualLayers.Lock" ]
+    - state: welded
+      visible: false
+      map: [ "enum.WeldableLayers.BaseWelded" ]
+  #TODO: Will have to eventually make a custom visualizer for cursed lockers
+  - type: EntityStorageVisuals
+    stateBaseClosed: decursed
+    stateDoorOpen: decursed_open
+    stateDoorClosed: decursed_door
+  - type: Fixtures
+    fixtures:
+      fix1:
+        shape:
+          !type:PhysShapeAabb
+          bounds: "-0.25,-0.48,0.25,0.48"
+        density: 75
+        mask:
+        - MachineMask
+        layer:
+        - MachineLayer
+      projectile:
+        shape:
+          !type:PhysShapeAabb
+          bounds: "-0.15,-0.45,0.15,0.15"
+        hard: false
+        mask:
+        - Impassable
+        - BulletImpassable
+      fly-by: &flybyfixture
+        shape: !type:PhysShapeCircle
+          radius: 0.6
+        layer:
+        - Impassable
+        - MidImpassable
+        - HighImpassable
+        - LowImpassable
+        hard: false
+
 - type: entity
   id: ProjectilePolyboltBase
   parent: BaseBullet
diff --git a/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker-effect.png b/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker-effect.png
new file mode 100644 (file)
index 0000000..77bcb4b
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker-effect.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker-inhand-left.png b/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker-inhand-left.png
new file mode 100644 (file)
index 0000000..bcb330b
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker-inhand-left.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker-inhand-right.png b/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker-inhand-right.png
new file mode 100644 (file)
index 0000000..4be7ef2
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker-inhand-right.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker.png b/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker.png
new file mode 100644 (file)
index 0000000..f638769
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker.png differ
index 87b6f386770665c3246b65acd3c2b61b1ba13f10..59d423baac96a80c8aa94a56949305e525fecaf4 100644 (file)
     {
       "name": "wand-inhand-right",
          "directions": 4
+    },
+    {
+      "name": "locker"
+    },
+    {
+      "name": "locker-effect"
+    },
+    {
+      "name": "locker-inhand-right",
+      "directions": 4
+    },
+    {
+      "name": "locker-inhand-left",
+      "directions": 4
     }
   ]
-}
\ No newline at end of file
+}