add: funny keys that can lock doors (#113)
110
Content.Server/_White/Keyhole/KeyholeSystem.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using Content.Shared._White.Keyhole.Components;
|
||||
using Content.Shared._White.Keyhole;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server._White.Keyhole;
|
||||
|
||||
public sealed partial class KeyholeSystem : EntitySystem
|
||||
{
|
||||
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<KeyComponent, ComponentInit>(OnKeyInit);
|
||||
|
||||
SubscribeLocalEvent<KeyComponent, AfterInteractEvent>(OnKeyInsert);
|
||||
SubscribeLocalEvent<KeyholeComponent, KeyInsertDoAfterEvent>(OnDoAfter);
|
||||
}
|
||||
|
||||
private void OnKeyInit(EntityUid uid, KeyComponent component, ComponentInit ev)
|
||||
{
|
||||
component.FormId = _random.Next(1000);
|
||||
}
|
||||
|
||||
private void OnKeyInsert(EntityUid uid, KeyComponent component, AfterInteractEvent ev)
|
||||
{
|
||||
if (TryComp<KeyformComponent>(ev.Target, out var keyformComponent))
|
||||
OnKeyInsertForm(uid, component, keyformComponent, ev);
|
||||
|
||||
if (!TryComp<KeyholeComponent>(ev.Target, out var keyholeComponent))
|
||||
return;
|
||||
|
||||
keyholeComponent.FormId ??= component.FormId;
|
||||
|
||||
if (!CanLock(keyholeComponent.Owner, keyholeComponent, component))
|
||||
return;
|
||||
|
||||
var doAfterEventArgs =
|
||||
new DoAfterArgs(EntityManager, ev.User, keyholeComponent.Delay, new KeyInsertDoAfterEvent(), ev.Target, ev.Used)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnDamage = true
|
||||
};
|
||||
_doAfter.TryStartDoAfter(doAfterEventArgs);
|
||||
}
|
||||
|
||||
private bool CanLock(EntityUid uid, KeyholeComponent keyholeComponent, KeyComponent keyComponent)
|
||||
{
|
||||
var can = TryComp<DoorComponent>(uid, out var doorComponent) &&
|
||||
keyholeComponent.FormId == keyComponent.FormId &&
|
||||
doorComponent.State == DoorState.Closed;
|
||||
|
||||
return can;
|
||||
}
|
||||
|
||||
private void OnDoAfter(EntityUid uid, KeyholeComponent component, KeyInsertDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled)
|
||||
return;
|
||||
|
||||
Lock(uid, component, args.User);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void Lock(EntityUid uid, KeyholeComponent component, EntityUid user)
|
||||
{
|
||||
var sound = component.Locked ? component.UnlockSound : component.LockSound;
|
||||
var message = Loc.GetString(component.Locked ? "key-unlock-message" : "key-lock-message", ("name", user), ("door", uid));
|
||||
|
||||
var audioParams = new AudioParams().WithVolume(-5f);
|
||||
|
||||
_audio.PlayPvs(sound, user, audioParams);
|
||||
_popupSystem.PopupEntity(message, uid);
|
||||
|
||||
component.Locked = !component.Locked;
|
||||
}
|
||||
|
||||
private void OnKeyInsertForm(EntityUid uid, KeyComponent keyComponent, KeyformComponent keyformComponent, AfterInteractEvent args)
|
||||
{
|
||||
if (!keyformComponent.IsUsed)
|
||||
{
|
||||
keyformComponent.FormId ??= keyComponent.FormId;
|
||||
_appearance.SetData(keyformComponent.Owner, KeyformVisuals.IsUsed, true);
|
||||
|
||||
_audio.PlayPvs(keyformComponent.PressSound, uid);
|
||||
_popupSystem.PopupEntity(Loc.GetString("key-pressed-in-keyform-message-first", ("user", args.User), ("key", uid)), uid);
|
||||
|
||||
keyformComponent.IsUsed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
keyComponent.FormId = keyformComponent.FormId;
|
||||
_popupSystem.PopupEntity(Loc.GetString("key-pressed-in-keyform-message", ("user", args.User), ("key", uid)), uid);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Shared._White.Keyhole.Components;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Damage;
|
||||
@@ -6,6 +7,7 @@ using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Prying.Components;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Tag;
|
||||
@@ -30,6 +32,7 @@ public abstract class SharedDoorSystem : EntitySystem
|
||||
[Dependency] protected readonly SharedAppearanceSystem AppearanceSystem = default!;
|
||||
[Dependency] private readonly OccluderSystem _occluder = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!; //WD edit
|
||||
|
||||
/// <summary>
|
||||
/// A body must have an intersection percentage larger than this in order to be considered as colliding with a
|
||||
@@ -211,6 +214,19 @@ public abstract class SharedDoorSystem : EntitySystem
|
||||
if (!Resolve(uid, ref door))
|
||||
return false;
|
||||
|
||||
// WD edit start
|
||||
if (TryComp<KeyholeComponent>(uid, out var keyholeComponent))
|
||||
{
|
||||
if (keyholeComponent.Locked)
|
||||
{
|
||||
PlaySound(uid, keyholeComponent.DoorLockedSound, AudioParams.Default.WithVolume(-3), uid, true);
|
||||
_popupSystem.PopupEntity(Loc.GetString("door-locked-via-key", ("door", uid)), uid);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
// WD edit end
|
||||
|
||||
if (door.State is DoorState.Closed or DoorState.Denying)
|
||||
{
|
||||
return TryOpen(uid, door, user, predicted, quiet: door.State == DoorState.Denying);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Content.Shared._White.Keyhole.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public partial class KeyBaseComponent : Component
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public int? FormId;
|
||||
}
|
||||
7
Content.Shared/_White/Keyhole/Components/KeyComponent.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace Content.Shared._White.Keyhole.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class KeyComponent : KeyBaseComponent
|
||||
{
|
||||
|
||||
}
|
||||
15
Content.Shared/_White/Keyhole/Components/KeyFormComponent.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Shared._White.Keyhole.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class KeyformComponent : KeyBaseComponent
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public bool IsUsed;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public SoundSpecifier PressSound = new SoundPathSpecifier("/Audio/White/Object/Tools/Form/press.ogg");
|
||||
}
|
||||
27
Content.Shared/_White/Keyhole/Components/KeyholeComponent.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Shared._White.Keyhole.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class KeyholeComponent: KeyBaseComponent
|
||||
{
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public bool Locked = false;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public float Delay = 1f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public SoundSpecifier DoorLockedSound = new SoundPathSpecifier("/Audio/White/Object/Tools/Keyhole/locked.ogg");
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public SoundSpecifier UnlockSound = new SoundPathSpecifier("/Audio/White/Object/Tools/Keyhole/unlock.ogg");
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField]
|
||||
public SoundSpecifier LockSound = new SoundPathSpecifier("/Audio/White/Object/Tools/Keyhole/lock.ogg");
|
||||
}
|
||||
8
Content.Shared/_White/Keyhole/KeyInsertEvent.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._White.Keyhole;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class KeyInsertDoAfterEvent : SimpleDoAfterEvent {}
|
||||
|
||||
9
Content.Shared/_White/Keyhole/KeyformVisuals.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared._White.Keyhole;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum KeyformVisuals : byte
|
||||
{
|
||||
IsUsed
|
||||
}
|
||||
BIN
Resources/Audio/White/Object/Tools/Form/hit.ogg
Normal file
BIN
Resources/Audio/White/Object/Tools/Form/pickup.ogg
Normal file
BIN
Resources/Audio/White/Object/Tools/Form/press.ogg
Normal file
BIN
Resources/Audio/White/Object/Tools/Key/drop.ogg
Normal file
BIN
Resources/Audio/White/Object/Tools/Key/pickup.ogg
Normal file
BIN
Resources/Audio/White/Object/Tools/Keyhole/lock.ogg
Normal file
BIN
Resources/Audio/White/Object/Tools/Keyhole/locked.ogg
Normal file
BIN
Resources/Audio/White/Object/Tools/Keyhole/unlock.ogg
Normal file
5
Resources/Locale/ru-RU/White/object/tools/key.ftl
Normal file
@@ -0,0 +1,5 @@
|
||||
ent-KeyMetal = металлический ключ
|
||||
.desc = заостренный кусок металла
|
||||
|
||||
key-lock-message = {$name} проворачивает ключ в замке {$door} и закрывает {POSS-ADJ($door)}
|
||||
key-unlock-message = {$name} проворачивает ключ в замке {$door} и открывает {POSS-ADJ($door)}
|
||||
5
Resources/Locale/ru-RU/white/object/tools/keyform.ftl
Normal file
@@ -0,0 +1,5 @@
|
||||
ent-KeyForm = пластиковая форма
|
||||
.desc = мягкий кусок пластика
|
||||
|
||||
key-pressed-in-keyform-message-first = {$user} вдавливает {$key} в пластик, создавая в нем форму ключа
|
||||
key-pressed-in-keyform-message = {$user} вставляет {$key} в форму, заставляя его скопировать уникальный узор
|
||||
1
Resources/Locale/ru-RU/white/object/tools/keyhole.ftl
Normal file
@@ -0,0 +1 @@
|
||||
door-locked-via-key = {$door} закрыта
|
||||
@@ -57,6 +57,7 @@
|
||||
mode: NoSprite
|
||||
- type: Occluder
|
||||
- type: ReflectAspectMark
|
||||
- type: Keyhole
|
||||
|
||||
- type: entity
|
||||
parent: BaseMaterialDoor
|
||||
|
||||
54
Resources/Prototypes/White/Objects/Tools/crafts.yml
Normal file
@@ -0,0 +1,54 @@
|
||||
- type: constructionGraph
|
||||
id: KeyFormGraph
|
||||
start: start
|
||||
graph:
|
||||
- node: start
|
||||
edges:
|
||||
- to: KeyFormNode
|
||||
steps:
|
||||
- material: Plastic
|
||||
amount: 1
|
||||
doAfter: 2
|
||||
- node: KeyFormNode
|
||||
entity: KeyForm
|
||||
|
||||
- type: construction
|
||||
name: ent-KeyForm
|
||||
id: KeyForm
|
||||
graph: KeyFormGraph
|
||||
startNode: start
|
||||
targetNode: KeyFormNode
|
||||
category: construction-category-tools
|
||||
objectType: Item
|
||||
description: ent-KeyForm.desc
|
||||
icon:
|
||||
sprite: White/Objects/Tools/form.rsi
|
||||
state: empty
|
||||
|
||||
|
||||
- type: constructionGraph
|
||||
id: KeyGraph
|
||||
start: start
|
||||
graph:
|
||||
- node: start
|
||||
edges:
|
||||
- to: KeyNode
|
||||
steps:
|
||||
- material: Steel
|
||||
amount: 1
|
||||
doAfter: 2
|
||||
- node: KeyNode
|
||||
entity: KeyMetal
|
||||
|
||||
- type: construction
|
||||
name: ent-KeyMetal
|
||||
id: KeyMetal
|
||||
graph: KeyGraph
|
||||
startNode: start
|
||||
targetNode: KeyNode
|
||||
category: construction-category-tools
|
||||
objectType: Item
|
||||
description: ent-KeyMetal.desc
|
||||
icon:
|
||||
sprite: White/Objects/Tools/key.rsi
|
||||
state: icon
|
||||
34
Resources/Prototypes/White/Objects/Tools/form.yml
Normal file
@@ -0,0 +1,34 @@
|
||||
- type: entity
|
||||
name: form
|
||||
parent: BaseItem
|
||||
id: KeyForm
|
||||
description: Makeshift piece of plastic
|
||||
components:
|
||||
- type: EmitSoundOnPickup
|
||||
sound:
|
||||
path: /Audio/White/Object/Tools/Form/pickup.ogg
|
||||
- type: EmitSoundOnLand
|
||||
sound:
|
||||
path: /Audio/White/Object/Tools/Form/hit.ogg
|
||||
- type: Sprite
|
||||
sprite: White/Objects/Tools/form.rsi
|
||||
state: empty
|
||||
- type: Item
|
||||
sprite: White/Objects/Tools/form.rsi
|
||||
storedRotation: -180
|
||||
- type: PhysicalComposition
|
||||
materialComposition:
|
||||
Steel: 100
|
||||
- type: StaticPrice
|
||||
price: 10
|
||||
- type: Keyform
|
||||
- type: GenericVisualizer
|
||||
visuals:
|
||||
enum.KeyformVisuals.IsUsed:
|
||||
base:
|
||||
True: { state: withkey }
|
||||
False: { state: empty }
|
||||
- type: Appearance
|
||||
- type: Construction
|
||||
graph: KeyFormGraph
|
||||
node: KeyFormNode
|
||||
38
Resources/Prototypes/White/Objects/Tools/key.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
- type: entity
|
||||
name: key
|
||||
parent: BaseItem
|
||||
id: KeyMetal
|
||||
description: Makeshift sharp piece of metal
|
||||
components:
|
||||
- type: EmitSoundOnPickup
|
||||
sound:
|
||||
path: /Audio/White/Object/Tools/Key/pickup.ogg
|
||||
- type: EmitSoundOnDrop
|
||||
sound:
|
||||
path: /Audio/White/Object/Tools/Key/drop.ogg
|
||||
- type: EmitSoundOnLand
|
||||
sound:
|
||||
path: /Audio/White/Object/Tools/Key/drop.ogg
|
||||
- type: Sprite
|
||||
sprite: White/Objects/Tools/key.rsi
|
||||
state: icon
|
||||
- type: Item
|
||||
sprite: White/Objects/Tools/key.rsi
|
||||
storedRotation: -180
|
||||
- type: MeleeWeapon
|
||||
wideAnimationRotation: -180
|
||||
attackRate: 1
|
||||
damage:
|
||||
types:
|
||||
Piercing: 1
|
||||
soundHit:
|
||||
path: "/Audio/Weapons/bladeslice.ogg"
|
||||
- type: PhysicalComposition
|
||||
materialComposition:
|
||||
Steel: 100
|
||||
- type: StaticPrice
|
||||
price: 10
|
||||
- type: Key
|
||||
- type: Construction
|
||||
graph: KeyGraph
|
||||
node: KeyNode
|
||||
BIN
Resources/Textures/White/Objects/Tools/form.rsi/empty.png
Normal file
|
After Width: | Height: | Size: 544 B |
BIN
Resources/Textures/White/Objects/Tools/form.rsi/inhand-left.png
Normal file
|
After Width: | Height: | Size: 298 B |
BIN
Resources/Textures/White/Objects/Tools/form.rsi/inhand-right.png
Normal file
|
After Width: | Height: | Size: 302 B |
25
Resources/Textures/White/Objects/Tools/form.rsi/meta.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "https://github.com/frosty-dev/white",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "empty"
|
||||
},
|
||||
{
|
||||
"name": "withkey"
|
||||
},
|
||||
{
|
||||
"name": "inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/White/Objects/Tools/form.rsi/withkey.png
Normal file
|
After Width: | Height: | Size: 541 B |
BIN
Resources/Textures/White/Objects/Tools/key.rsi/icon.png
Normal file
|
After Width: | Height: | Size: 300 B |
BIN
Resources/Textures/White/Objects/Tools/key.rsi/inhand-left.png
Normal file
|
After Width: | Height: | Size: 204 B |
BIN
Resources/Textures/White/Objects/Tools/key.rsi/inhand-right.png
Normal file
|
After Width: | Height: | Size: 198 B |
22
Resources/Textures/White/Objects/Tools/key.rsi/meta.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"version": 1,
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "https://github.com/frosty-dev/white",
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "icon"
|
||||
},
|
||||
{
|
||||
"name": "inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||