diff --git a/Content.Server/_White/Keyhole/KeyholeSystem.cs b/Content.Server/_White/Keyhole/KeyholeSystem.cs new file mode 100644 index 0000000000..7bba988ae4 --- /dev/null +++ b/Content.Server/_White/Keyhole/KeyholeSystem.cs @@ -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(OnKeyInit); + + SubscribeLocalEvent(OnKeyInsert); + SubscribeLocalEvent(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(ev.Target, out var keyformComponent)) + OnKeyInsertForm(uid, component, keyformComponent, ev); + + if (!TryComp(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(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); + } + + } + +} diff --git a/Content.Shared/Doors/Systems/SharedDoorSystem.cs b/Content.Shared/Doors/Systems/SharedDoorSystem.cs index 20ea94b9df..3400d67d60 100644 --- a/Content.Shared/Doors/Systems/SharedDoorSystem.cs +++ b/Content.Shared/Doors/Systems/SharedDoorSystem.cs @@ -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 /// /// 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(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); diff --git a/Content.Shared/_White/Keyhole/Components/KeyBaseComponent.cs b/Content.Shared/_White/Keyhole/Components/KeyBaseComponent.cs new file mode 100644 index 0000000000..8a6b818cab --- /dev/null +++ b/Content.Shared/_White/Keyhole/Components/KeyBaseComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Shared._White.Keyhole.Components; + +[RegisterComponent] +public partial class KeyBaseComponent : Component +{ + [ViewVariables(VVAccess.ReadWrite)] + [DataField] + public int? FormId; +} diff --git a/Content.Shared/_White/Keyhole/Components/KeyComponent.cs b/Content.Shared/_White/Keyhole/Components/KeyComponent.cs new file mode 100644 index 0000000000..fe03179142 --- /dev/null +++ b/Content.Shared/_White/Keyhole/Components/KeyComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Shared._White.Keyhole.Components; + +[RegisterComponent] +public sealed partial class KeyComponent : KeyBaseComponent +{ + +} diff --git a/Content.Shared/_White/Keyhole/Components/KeyFormComponent.cs b/Content.Shared/_White/Keyhole/Components/KeyFormComponent.cs new file mode 100644 index 0000000000..a9b97a1597 --- /dev/null +++ b/Content.Shared/_White/Keyhole/Components/KeyFormComponent.cs @@ -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"); +} diff --git a/Content.Shared/_White/Keyhole/Components/KeyholeComponent.cs b/Content.Shared/_White/Keyhole/Components/KeyholeComponent.cs new file mode 100644 index 0000000000..ecff5f9abd --- /dev/null +++ b/Content.Shared/_White/Keyhole/Components/KeyholeComponent.cs @@ -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"); +} diff --git a/Content.Shared/_White/Keyhole/KeyInsertEvent.cs b/Content.Shared/_White/Keyhole/KeyInsertEvent.cs new file mode 100644 index 0000000000..48a6f2c049 --- /dev/null +++ b/Content.Shared/_White/Keyhole/KeyInsertEvent.cs @@ -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 {} + diff --git a/Content.Shared/_White/Keyhole/KeyformVisuals.cs b/Content.Shared/_White/Keyhole/KeyformVisuals.cs new file mode 100644 index 0000000000..6874ea8000 --- /dev/null +++ b/Content.Shared/_White/Keyhole/KeyformVisuals.cs @@ -0,0 +1,9 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared._White.Keyhole; + +[Serializable, NetSerializable] +public enum KeyformVisuals : byte +{ + IsUsed +} diff --git a/Resources/Audio/White/Object/Tools/Form/hit.ogg b/Resources/Audio/White/Object/Tools/Form/hit.ogg new file mode 100644 index 0000000000..78604d212a Binary files /dev/null and b/Resources/Audio/White/Object/Tools/Form/hit.ogg differ diff --git a/Resources/Audio/White/Object/Tools/Form/pickup.ogg b/Resources/Audio/White/Object/Tools/Form/pickup.ogg new file mode 100644 index 0000000000..cc3fe905ab Binary files /dev/null and b/Resources/Audio/White/Object/Tools/Form/pickup.ogg differ diff --git a/Resources/Audio/White/Object/Tools/Form/press.ogg b/Resources/Audio/White/Object/Tools/Form/press.ogg new file mode 100644 index 0000000000..09c38340d3 Binary files /dev/null and b/Resources/Audio/White/Object/Tools/Form/press.ogg differ diff --git a/Resources/Audio/White/Object/Tools/Key/drop.ogg b/Resources/Audio/White/Object/Tools/Key/drop.ogg new file mode 100644 index 0000000000..74aff50dda Binary files /dev/null and b/Resources/Audio/White/Object/Tools/Key/drop.ogg differ diff --git a/Resources/Audio/White/Object/Tools/Key/pickup.ogg b/Resources/Audio/White/Object/Tools/Key/pickup.ogg new file mode 100644 index 0000000000..f0d0be3ce3 Binary files /dev/null and b/Resources/Audio/White/Object/Tools/Key/pickup.ogg differ diff --git a/Resources/Audio/White/Object/Tools/Keyhole/lock.ogg b/Resources/Audio/White/Object/Tools/Keyhole/lock.ogg new file mode 100644 index 0000000000..ba271c01c1 Binary files /dev/null and b/Resources/Audio/White/Object/Tools/Keyhole/lock.ogg differ diff --git a/Resources/Audio/White/Object/Tools/Keyhole/locked.ogg b/Resources/Audio/White/Object/Tools/Keyhole/locked.ogg new file mode 100644 index 0000000000..f6efa069f2 Binary files /dev/null and b/Resources/Audio/White/Object/Tools/Keyhole/locked.ogg differ diff --git a/Resources/Audio/White/Object/Tools/Keyhole/unlock.ogg b/Resources/Audio/White/Object/Tools/Keyhole/unlock.ogg new file mode 100644 index 0000000000..09cc0cbc9d Binary files /dev/null and b/Resources/Audio/White/Object/Tools/Keyhole/unlock.ogg differ diff --git a/Resources/Locale/ru-RU/White/object/tools/key.ftl b/Resources/Locale/ru-RU/White/object/tools/key.ftl new file mode 100644 index 0000000000..d28ec271a6 --- /dev/null +++ b/Resources/Locale/ru-RU/White/object/tools/key.ftl @@ -0,0 +1,5 @@ +ent-KeyMetal = металлический ключ + .desc = заостренный кусок металла + +key-lock-message = {$name} проворачивает ключ в замке {$door} и закрывает {POSS-ADJ($door)} +key-unlock-message = {$name} проворачивает ключ в замке {$door} и открывает {POSS-ADJ($door)} diff --git a/Resources/Locale/ru-RU/white/object/tools/keyform.ftl b/Resources/Locale/ru-RU/white/object/tools/keyform.ftl new file mode 100644 index 0000000000..7a6ad6026a --- /dev/null +++ b/Resources/Locale/ru-RU/white/object/tools/keyform.ftl @@ -0,0 +1,5 @@ +ent-KeyForm = пластиковая форма + .desc = мягкий кусок пластика + +key-pressed-in-keyform-message-first = {$user} вдавливает {$key} в пластик, создавая в нем форму ключа +key-pressed-in-keyform-message = {$user} вставляет {$key} в форму, заставляя его скопировать уникальный узор diff --git a/Resources/Locale/ru-RU/white/object/tools/keyhole.ftl b/Resources/Locale/ru-RU/white/object/tools/keyhole.ftl new file mode 100644 index 0000000000..788328b305 --- /dev/null +++ b/Resources/Locale/ru-RU/white/object/tools/keyhole.ftl @@ -0,0 +1 @@ +door-locked-via-key = {$door} закрыта diff --git a/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml b/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml index 05ca51d5eb..e5473dfa35 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/MaterialDoors/material_doors.yml @@ -57,6 +57,7 @@ mode: NoSprite - type: Occluder - type: ReflectAspectMark + - type: Keyhole - type: entity parent: BaseMaterialDoor diff --git a/Resources/Prototypes/White/Objects/Tools/crafts.yml b/Resources/Prototypes/White/Objects/Tools/crafts.yml new file mode 100644 index 0000000000..527d2d9dd6 --- /dev/null +++ b/Resources/Prototypes/White/Objects/Tools/crafts.yml @@ -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 diff --git a/Resources/Prototypes/White/Objects/Tools/form.yml b/Resources/Prototypes/White/Objects/Tools/form.yml new file mode 100644 index 0000000000..07b4d4aab9 --- /dev/null +++ b/Resources/Prototypes/White/Objects/Tools/form.yml @@ -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 diff --git a/Resources/Prototypes/White/Objects/Tools/key.yml b/Resources/Prototypes/White/Objects/Tools/key.yml new file mode 100644 index 0000000000..fc05973197 --- /dev/null +++ b/Resources/Prototypes/White/Objects/Tools/key.yml @@ -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 diff --git a/Resources/Textures/White/Objects/Tools/form.rsi/empty.png b/Resources/Textures/White/Objects/Tools/form.rsi/empty.png new file mode 100644 index 0000000000..3d8300b19f Binary files /dev/null and b/Resources/Textures/White/Objects/Tools/form.rsi/empty.png differ diff --git a/Resources/Textures/White/Objects/Tools/form.rsi/inhand-left.png b/Resources/Textures/White/Objects/Tools/form.rsi/inhand-left.png new file mode 100644 index 0000000000..f4c6e89e04 Binary files /dev/null and b/Resources/Textures/White/Objects/Tools/form.rsi/inhand-left.png differ diff --git a/Resources/Textures/White/Objects/Tools/form.rsi/inhand-right.png b/Resources/Textures/White/Objects/Tools/form.rsi/inhand-right.png new file mode 100644 index 0000000000..4e04590af2 Binary files /dev/null and b/Resources/Textures/White/Objects/Tools/form.rsi/inhand-right.png differ diff --git a/Resources/Textures/White/Objects/Tools/form.rsi/meta.json b/Resources/Textures/White/Objects/Tools/form.rsi/meta.json new file mode 100644 index 0000000000..0e57e0b255 --- /dev/null +++ b/Resources/Textures/White/Objects/Tools/form.rsi/meta.json @@ -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 + } + ] + } diff --git a/Resources/Textures/White/Objects/Tools/form.rsi/withkey.png b/Resources/Textures/White/Objects/Tools/form.rsi/withkey.png new file mode 100644 index 0000000000..83fe288158 Binary files /dev/null and b/Resources/Textures/White/Objects/Tools/form.rsi/withkey.png differ diff --git a/Resources/Textures/White/Objects/Tools/key.rsi/icon.png b/Resources/Textures/White/Objects/Tools/key.rsi/icon.png new file mode 100644 index 0000000000..4325e0bcc8 Binary files /dev/null and b/Resources/Textures/White/Objects/Tools/key.rsi/icon.png differ diff --git a/Resources/Textures/White/Objects/Tools/key.rsi/inhand-left.png b/Resources/Textures/White/Objects/Tools/key.rsi/inhand-left.png new file mode 100644 index 0000000000..01d5f195b4 Binary files /dev/null and b/Resources/Textures/White/Objects/Tools/key.rsi/inhand-left.png differ diff --git a/Resources/Textures/White/Objects/Tools/key.rsi/inhand-right.png b/Resources/Textures/White/Objects/Tools/key.rsi/inhand-right.png new file mode 100644 index 0000000000..75929b807d Binary files /dev/null and b/Resources/Textures/White/Objects/Tools/key.rsi/inhand-right.png differ diff --git a/Resources/Textures/White/Objects/Tools/key.rsi/meta.json b/Resources/Textures/White/Objects/Tools/key.rsi/meta.json new file mode 100644 index 0000000000..e0aae79953 --- /dev/null +++ b/Resources/Textures/White/Objects/Tools/key.rsi/meta.json @@ -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 + } + ] +}