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
This commit is contained in:
@@ -131,8 +131,24 @@ public sealed class LockSystem : EntitySystem
|
||||
});
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
34
Content.Shared/Storage/Components/StoreOnCollideComponent.cs
Normal file
34
Content.Shared/Storage/Components/StoreOnCollideComponent.cs
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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))
|
||||
|
||||
71
Content.Shared/Storage/EntitySystems/StoreOnCollideSystem.cs
Normal file
71
Content.Shared/Storage/EntitySystems/StoreOnCollideSystem.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
3
Resources/Prototypes/Access/wizard.yml
Normal file
3
Resources/Prototypes/Access/wizard.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
- type: accessLevel
|
||||
id: Wizard
|
||||
name: id-card-access-level-wizard
|
||||
@@ -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
|
||||
|
||||
@@ -810,3 +810,4 @@
|
||||
- CentralCommand
|
||||
- NuclearOperative
|
||||
- SyndicateAgent
|
||||
- Wizard
|
||||
|
||||
@@ -119,6 +119,7 @@
|
||||
- CentralCommand
|
||||
- NuclearOperative
|
||||
- SyndicateAgent
|
||||
- Wizard
|
||||
privilegedIdSlot:
|
||||
name: id-card-console-privileged-id
|
||||
ejectSound: /Audio/Machines/id_swipe.ogg
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 260 B |
Binary file not shown.
|
After Width: | Height: | Size: 794 B |
Binary file not shown.
|
After Width: | Height: | Size: 803 B |
Binary file not shown.
|
After Width: | Height: | Size: 624 B |
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user