From: keronshb <54602815+keronshb@users.noreply.github.com>
Date: Wed, 4 Dec 2024 16:49:54 +0000 (-0500)
Subject: Adds Store on Collide and Wand of the Locker (#33710)
X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=82528dce377ed76399fedf570a96459288354c95;p=space-station-14.git
Adds Store on Collide and Wand of the Locker (#33710)
* 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
---
diff --git a/Content.Shared/Lock/LockSystem.cs b/Content.Shared/Lock/LockSystem.cs
index 3349034f32..411766d8c1 100644
--- a/Content.Shared/Lock/LockSystem.cs
+++ b/Content.Shared/Lock/LockSystem.cs
@@ -131,8 +131,24 @@ public sealed class LockSystem : EntitySystem
});
}
- _sharedPopupSystem.PopupClient(Loc.GetString("lock-comp-do-lock-success",
+ Lock(uid, user, lockComp);
+ return true;
+ }
+
+ ///
+ /// Forces a given entity to be locked, does not activate a do-after.
+ ///
+ 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;
}
///
diff --git a/Content.Shared/Storage/Components/StoreOnCollideComponent.cs b/Content.Shared/Storage/Components/StoreOnCollideComponent.cs
new file mode 100644
index 0000000000..d8fff31938
--- /dev/null
+++ b/Content.Shared/Storage/Components/StoreOnCollideComponent.cs
@@ -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
+{
+ ///
+ /// Entities that are allowed in the storage on collide
+ ///
+ [DataField]
+ public EntityWhitelist? Whitelist;
+
+ ///
+ /// Should this storage lock on collide, provided they have a lock component?
+ ///
+ [DataField]
+ public bool LockOnCollide;
+
+ ///
+ /// Should the behavior be disabled when the storage is first opened?
+ ///
+ [DataField]
+ public bool DisableWhenFirstOpened;
+
+ ///
+ /// If the behavior is disabled or not
+ ///
+ [DataField, AutoNetworkedField]
+ public bool Disabled;
+}
diff --git a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs
index 309ac0a2e0..829f574ad1 100644
--- a/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs
+++ b/Content.Shared/Storage/EntitySystems/SharedEntityStorageSystem.cs
@@ -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
index 0000000000..000e19faf3
--- /dev/null
+++ b/Content.Shared/Storage/EntitySystems/StoreOnCollideSystem.cs
@@ -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(OnCollide);
+ SubscribeLocalEvent(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 ent, ref StartCollideEvent args)
+ {
+ TryStoreTarget(ent, args.OtherEntity);
+
+ TryLockStorage(ent);
+ }
+
+ private void AfterOpen(Entity ent, ref StorageAfterOpenEvent args)
+ {
+ var comp = ent.Comp;
+
+ if (comp is { DisableWhenFirstOpened: true, Disabled: false })
+ comp.Disabled = true;
+ }
+
+ private void TryStoreTarget(Entity 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 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);
+ }
+}
diff --git a/Resources/Locale/en-US/prototypes/access/accesses.ftl b/Resources/Locale/en-US/prototypes/access/accesses.ftl
index 0e8b1d9ac7..5e54fcad22 100644
--- a/Resources/Locale/en-US/prototypes/access/accesses.ftl
+++ b/Resources/Locale/en-US/prototypes/access/accesses.ftl
@@ -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
diff --git a/Resources/Locale/en-US/store/spellbook-catalog.ftl b/Resources/Locale/en-US/store/spellbook-catalog.ftl
index ed62c6fa82..dff918b0bc 100644
--- a/Resources/Locale/en-US/store/spellbook-catalog.ftl
+++ b/Resources/Locale/en-US/store/spellbook-catalog.ftl
@@ -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
index 0000000000..cacb889936
--- /dev/null
+++ b/Resources/Prototypes/Access/wizard.yml
@@ -0,0 +1,3 @@
+- type: accessLevel
+ id: Wizard
+ name: id-card-access-level-wizard
diff --git a/Resources/Prototypes/Catalog/spellbook_catalog.yml b/Resources/Prototypes/Catalog/spellbook_catalog.yml
index 6e9bba87a6..57172dc665 100644
--- a/Resources/Prototypes/Catalog/spellbook_catalog.yml
+++ b/Resources/Prototypes/Catalog/spellbook_catalog.yml
@@ -118,6 +118,19 @@
- !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
diff --git a/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml b/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml
index ccb83b6aaa..a0e144d0ac 100644
--- a/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml
+++ b/Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml
@@ -810,3 +810,4 @@
- CentralCommand
- NuclearOperative
- SyndicateAgent
+ - Wizard
diff --git a/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml b/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml
index 66f4689099..d55437696a 100644
--- a/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml
+++ b/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml
@@ -119,6 +119,7 @@
- CentralCommand
- NuclearOperative
- SyndicateAgent
+ - Wizard
privilegedIdSlot:
name: id-card-console-privileged-id
ejectSound: /Audio/Machines/id_swipe.ogg
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/wands.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/wands.yml
index e723a1db5f..d3745288f6 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/wands.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/wands.yml
@@ -57,6 +57,42 @@
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
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml
index d11f96d875..cd736a33d0 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml
@@ -79,6 +79,86 @@
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
index 0000000000..77bcb4b364
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
index 0000000000..bcb330b9ed
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
index 0000000000..4be7ef2059
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
index 0000000000..f638769226
Binary files /dev/null and b/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/locker.png differ
diff --git a/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/meta.json b/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/meta.json
index 87b6f38677..59d423baac 100644
--- a/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/meta.json
+++ b/Resources/Textures/Objects/Weapons/Guns/Basic/wands.rsi/meta.json
@@ -63,6 +63,20 @@
{
"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
+}