From a8f1ffd43536c808b7360ee703bba52f740d59f0 Mon Sep 17 00:00:00 2001 From: Flipp Syder <76629141+vulppine@users.noreply.github.com> Date: Wed, 25 May 2022 13:16:34 -0700 Subject: [PATCH] Mousetraps (#8440) Co-authored-by: Kara --- Content.Client/Entry/IgnoredComponents.cs | 2 + .../Components/DamageOnTriggerComponent.cs | 12 +++ .../Damage/Systems/DamageOnTriggerSystem.cs | 49 +++++++++++ .../Mousetrap/MousetrapComponent.cs | 21 +++++ Content.Server/Mousetrap/MousetrapSystem.cs | 82 ++++++++++++++++++ Content.Shared/Mousetrap/MousetrapVisuals.cs | 11 +++ .../Catalog/Cargo/cargo_service.yml | 14 ++- .../Catalog/Fills/Boxes/general.yml | 15 ++++ .../Catalog/Fills/Crates/service.yml | 11 +++ .../Catalog/Fills/Lockers/service.yml | 4 + .../Prototypes/Entities/Mobs/NPCs/animals.yml | 12 +-- .../Prototypes/Entities/Mobs/NPCs/pets.yml | 16 ++-- .../Entities/Objects/Devices/mousetrap.yml | 58 +++++++++++++ .../Objects/Devices/mousetrap.rsi/meta.json | 23 +++++ .../Devices/mousetrap.rsi/mousetrap.png | Bin 0 -> 1342 bytes .../Devices/mousetrap.rsi/mousetraparmed.png | Bin 0 -> 1182 bytes 16 files changed, 315 insertions(+), 15 deletions(-) create mode 100644 Content.Server/Damage/Components/DamageOnTriggerComponent.cs create mode 100644 Content.Server/Damage/Systems/DamageOnTriggerSystem.cs create mode 100644 Content.Server/Mousetrap/MousetrapComponent.cs create mode 100644 Content.Server/Mousetrap/MousetrapSystem.cs create mode 100644 Content.Shared/Mousetrap/MousetrapVisuals.cs create mode 100644 Resources/Prototypes/Entities/Objects/Devices/mousetrap.yml create mode 100644 Resources/Textures/Objects/Devices/mousetrap.rsi/meta.json create mode 100644 Resources/Textures/Objects/Devices/mousetrap.rsi/mousetrap.png create mode 100644 Resources/Textures/Objects/Devices/mousetrap.rsi/mousetraparmed.png diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index f114e225ec..4c23856a4c 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -359,6 +359,8 @@ namespace Content.Client.Entry "Thirst", "CanEscapeInventory", "PowerSink", + "Mousetrap", + "DamageOnTrigger", "Wires", }; } diff --git a/Content.Server/Damage/Components/DamageOnTriggerComponent.cs b/Content.Server/Damage/Components/DamageOnTriggerComponent.cs new file mode 100644 index 0000000000..b73c0164ab --- /dev/null +++ b/Content.Server/Damage/Components/DamageOnTriggerComponent.cs @@ -0,0 +1,12 @@ +using Content.Shared.Damage; + +namespace Content.Server.Damage.Components; + +[RegisterComponent] +public sealed class DamageOnTriggerComponent : Component +{ + [DataField("ignoreResistances")] public bool IgnoreResistances; + + [DataField("damage", required: true)] + public DamageSpecifier Damage = default!; +} diff --git a/Content.Server/Damage/Systems/DamageOnTriggerSystem.cs b/Content.Server/Damage/Systems/DamageOnTriggerSystem.cs new file mode 100644 index 0000000000..e17baf6432 --- /dev/null +++ b/Content.Server/Damage/Systems/DamageOnTriggerSystem.cs @@ -0,0 +1,49 @@ +using Content.Server.Damage.Components; +using Content.Shared.Damage; +using Content.Shared.StepTrigger; + +namespace Content.Server.Damage.Systems; + +// System for damage that occurs on specific triggers. +// This is originally meant for mousetraps, but could +// probably be extended to fit other triggers as well. +public sealed class DamageOnTriggerSystem : EntitySystem +{ + [Dependency] private readonly DamageableSystem _damageableSystem = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnStepTrigger); + } + + private void OnStepTrigger(EntityUid uid, DamageOnTriggerComponent component, ref StepTriggeredEvent args) + { + OnDamageTrigger(uid, args.Tripper, component); + } + + private void OnDamageTrigger(EntityUid source, EntityUid target, DamageOnTriggerComponent? component = null) + { + if (!Resolve(source, ref component)) + { + return; + } + + var damage = new DamageSpecifier(component.Damage); + var ev = new BeforeDamageOnTriggerEvent(damage, target); + RaiseLocalEvent(source, ev); + + _damageableSystem.TryChangeDamage(target, ev.Damage, component.IgnoreResistances); + } +} + +public sealed class BeforeDamageOnTriggerEvent : EntityEventArgs +{ + public DamageSpecifier Damage { get; set; } + public EntityUid Tripper { get; } + + public BeforeDamageOnTriggerEvent(DamageSpecifier damage, EntityUid target) + { + Damage = damage; + Tripper = target; + } +} diff --git a/Content.Server/Mousetrap/MousetrapComponent.cs b/Content.Server/Mousetrap/MousetrapComponent.cs new file mode 100644 index 0000000000..c06986782f --- /dev/null +++ b/Content.Server/Mousetrap/MousetrapComponent.cs @@ -0,0 +1,21 @@ +namespace Content.Server.Mousetrap; + +[RegisterComponent] +public sealed class MousetrapComponent : Component +{ + [ViewVariables] + public bool IsActive; + + /// + /// Set this to change where the + /// inflection point in the scaling + /// equation will occur. + /// The default is 10. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("massBalance")] + public int MassBalance = 10; + + [DataField("ignoreDamageIfInventorySlotsFilled")] + public List IgnoreDamageIfSlotFilled = new(); +} diff --git a/Content.Server/Mousetrap/MousetrapSystem.cs b/Content.Server/Mousetrap/MousetrapSystem.cs new file mode 100644 index 0000000000..449e5f1d2b --- /dev/null +++ b/Content.Server/Mousetrap/MousetrapSystem.cs @@ -0,0 +1,82 @@ +using Content.Server.Damage.Systems; +using Content.Server.Explosion.EntitySystems; +using Content.Shared.Interaction.Events; +using Content.Shared.Inventory; +using Content.Shared.Mousetrap; +using Content.Shared.StepTrigger; + +namespace Content.Server.Mousetrap; + +public sealed class MousetrapSystem : EntitySystem +{ + [Dependency] private readonly InventorySystem _inventorySystem = default!; + [Dependency] private readonly TriggerSystem _triggerSystem = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnUseInHand); + SubscribeLocalEvent(BeforeDamageOnTrigger); + SubscribeLocalEvent(OnStepTriggerAttempt); + SubscribeLocalEvent(OnStepTrigger); + } + + private void OnUseInHand(EntityUid uid, MousetrapComponent component, UseInHandEvent args) + { + component.IsActive = !component.IsActive; + + UpdateVisuals(uid); + } + + private void OnStepTriggerAttempt(EntityUid uid, MousetrapComponent component, ref StepTriggerAttemptEvent args) + { + args.Continue = component.IsActive; + } + + private void BeforeDamageOnTrigger(EntityUid uid, MousetrapComponent component, BeforeDamageOnTriggerEvent args) + { + foreach (var slot in component.IgnoreDamageIfSlotFilled) + { + if (!_inventorySystem.TryGetSlotContainer(args.Tripper, slot, out var container, out _)) + { + continue; + } + + // This also means that wearing slippers won't + // hurt the entity. + if (container.ContainedEntity != null) + { + args.Damage *= 0; + return; + } + } + + if (TryComp(args.Tripper, out PhysicsComponent? physics)) + { + // The idea here is inverse, + // Small - big damage, + // Large - small damage + // yes i punched numbers into a calculator until the graph looked right + var scaledDamage = -50 * Math.Atan(physics.Mass - component.MassBalance) + (25 * Math.PI); + args.Damage *= scaledDamage; + } + } + + private void OnStepTrigger(EntityUid uid, MousetrapComponent component, ref StepTriggeredEvent args) + { + component.IsActive = false; + _triggerSystem.Trigger(uid); + + UpdateVisuals(uid); + } + + private void UpdateVisuals(EntityUid uid, MousetrapComponent? mousetrap = null, AppearanceComponent? appearance = null) + { + if (!Resolve(uid, ref mousetrap, ref appearance, false)) + { + return; + } + + appearance.SetData(MousetrapVisuals.Visual, + mousetrap.IsActive ? MousetrapVisuals.Armed : MousetrapVisuals.Unarmed); + } +} diff --git a/Content.Shared/Mousetrap/MousetrapVisuals.cs b/Content.Shared/Mousetrap/MousetrapVisuals.cs new file mode 100644 index 0000000000..9685157aad --- /dev/null +++ b/Content.Shared/Mousetrap/MousetrapVisuals.cs @@ -0,0 +1,11 @@ +using Robust.Shared.Serialization; + +namespace Content.Shared.Mousetrap; + +[Serializable, NetSerializable] +public enum MousetrapVisuals : byte +{ + Visual, + Armed, + Unarmed +} diff --git a/Resources/Prototypes/Catalog/Cargo/cargo_service.yml b/Resources/Prototypes/Catalog/Cargo/cargo_service.yml index 90423e55e1..9235bd1692 100644 --- a/Resources/Prototypes/Catalog/Cargo/cargo_service.yml +++ b/Resources/Prototypes/Catalog/Cargo/cargo_service.yml @@ -22,6 +22,18 @@ category: Service group: market +- type: cargoProduct + name: "mousetraps crate" + id: MousetrapBoxes + description: "Mousetraps, for when all of service is being haunted by an entire horde of rats. Use sparingly... or not." + icon: + sprite: Objects/Devices/mousetrap.rsi + state: normal + product: CrateMousetrapBoxes + cost: 1000 + category: Service + group: market + - type: cargoProduct name: "lung cancer crate" id: ServiceSmokeables @@ -57,7 +69,7 @@ cost: 1000 category: Service group: market - + - type: cargoProduct name: "personnel crate" id: ServicePersonnel diff --git a/Resources/Prototypes/Catalog/Fills/Boxes/general.yml b/Resources/Prototypes/Catalog/Fills/Boxes/general.yml index bbf620d3c6..c11b9e2bfe 100644 --- a/Resources/Prototypes/Catalog/Fills/Boxes/general.yml +++ b/Resources/Prototypes/Catalog/Fills/Boxes/general.yml @@ -7,6 +7,21 @@ - type: Sprite state: box +- type: entity + name: mousetrap box + parent: BoxCardboard + id: BoxMousetrap + description: This box is filled with mousetraps. Try not to get your hand stuck in one. + components: + - type: StorageFill + contents: + - id: Mousetrap + amount: 6 + - type: Sprite + layers: + - state: box + - state: mousetraps + - type: entity name: lightbulb box parent: BoxCardboard diff --git a/Resources/Prototypes/Catalog/Fills/Crates/service.yml b/Resources/Prototypes/Catalog/Fills/Crates/service.yml index 2f63f62905..d5e64325e6 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/service.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/service.yml @@ -34,6 +34,17 @@ - id: BoxLightbulb amount: 1 +- type: entity + id: CrateMousetrapBoxes + name: mousetraps crate + description: Mousetraps, for when all of service is being haunted by an entire horde of rats. Use sparingly... or not. + parent: CrateGenericSteel + components: + - type: StorageFill + contents: + - id: BoxMousetrap + amount: 1 + - type: entity id: CrateServiceSmokeables name: smokeables crate diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/service.yml b/Resources/Prototypes/Catalog/Fills/Lockers/service.yml index 232de81a80..7ac057d455 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/service.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/service.yml @@ -30,6 +30,8 @@ contents: - id: CrowbarRed - id: MonkeyCubeBox + - id: BoxMousetrap + amount: 2 - id: SprayBottleWater - id: ReagentContainerFlour amount: 2 @@ -49,6 +51,8 @@ - type: StorageFill contents: - id: MopItem + - id: BoxMousetrap + amount: 2 - id: WetFloorSign amount: 3 - id: TrashBag diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 979563bcdd..9ba6773557 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -507,7 +507,7 @@ - shape: !type:PhysShapeCircle radius: 0.48 - mass: 20 + mass: 60 mask: - MobMask layer: @@ -560,7 +560,7 @@ crit: kangaroo-dead dead: kangaroo-dead - type: Puller - + - type: entity name: boxing kangaroo parent: MobKangaroo @@ -997,7 +997,7 @@ - shape: !type:PhysShapeCircle radius: 0.25 - mass: 10 + mass: 20 mask: - MobMask layer: @@ -1213,7 +1213,7 @@ - shape: !type:PhysShapeCircle radius: 0.35 - mass: 10 + mass: 20 mask: - MobMask layer: @@ -1269,7 +1269,7 @@ - shape: !type:PhysShapeCircle radius: 0.35 - mass: 10 + mass: 20 mask: - MobMask layer: @@ -1325,7 +1325,7 @@ - shape: !type:PhysShapeCircle radius: 0.35 - mass: 10 + mass: 20 mask: - MobMask layer: diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml index 5f72794ecb..688e01bad0 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/pets.yml @@ -19,7 +19,7 @@ - shape: !type:PhysShapeCircle radius: 0.35 - mass: 10 + mass: 20 mask: - MobMask layer: @@ -138,7 +138,7 @@ spawned: - id: FoodMeatCorgi amount: 2 - + - type: entity name: Old Ian parent: MobCorgi @@ -225,7 +225,7 @@ - shape: !type:PhysShapeCircle radius: 0.35 - mass: 10 + mass: 20 mask: - MobMask layer: @@ -375,7 +375,7 @@ - shape: !type:PhysShapeCircle radius: 0.35 - mass: 10 + mass: 20 mask: - MobMask layer: @@ -418,7 +418,7 @@ - shape: !type:PhysShapeCircle radius: 0.35 - mass: 10 + mass: 20 mask: - MobMask layer: @@ -472,7 +472,7 @@ - shape: !type:PhysShapeCircle radius: 0.35 - mass: 10 + mass: 20 mask: - MobMask layer: @@ -515,7 +515,7 @@ - shape: !type:PhysShapeCircle radius: 0.35 - mass: 10 + mass: 20 mask: - MobMask layer: @@ -592,7 +592,7 @@ - shape: !type:PhysShapeCircle radius: 0.35 - mass: 10 + mass: 20 mask: - MobMask layer: diff --git a/Resources/Prototypes/Entities/Objects/Devices/mousetrap.yml b/Resources/Prototypes/Entities/Objects/Devices/mousetrap.yml new file mode 100644 index 0000000000..8ca251110d --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Devices/mousetrap.yml @@ -0,0 +1,58 @@ +- type: entity + name: mousetrap + parent: BaseItem + id: Mousetrap + components: + - type: Sprite + sprite: Objects/Devices/mousetrap.rsi + netsync: false + drawdepth: SmallMobs # if mice can hide under tables, so can mousetraps + layers: + - state: mousetrap + - type: StepTrigger + intersectRatio: 0.2 + requiredTriggeredSpeed: 0 + - type: Mousetrap + ignoreDamageIfInventorySlotsFilled: + - shoes # shoes + - type: DamageOnTrigger + damage: + types: + Blunt: 2 # base damage, scales based on mass + - type: EmitSoundOnUse + sound: "/Audio/Items/Handcuffs/cuff_end.ogg" + - type: EmitSoundOnTrigger + sound: "/Audio/Items/snap.ogg" + - type: Item + sprite: Objects/Devices/mousetrap.rsi + - type: Appearance + visuals: + - type: GenericEnumVisualizer + key: enum.MousetrapVisuals.Visual + layer: 0 + states: + enum.MousetrapVisuals.Armed: mousetraparmed + enum.MousetrapVisuals.Unarmed: mousetrap + - type: Physics + bodyType: Dynamic + - type: CollisionWake + enabled: false + - type: Fixtures + fixtures: + - shape: + !type:PhysShapeAabb + bounds: "-0.2,-0.2,0.2,0.2" + id: "slips" + hard: false + layer: + - LowImpassable + - shape: + !type:PhysShapeAabb + bounds: "-0.2,-0.2,0.2,0.2" + mass: 5 + mask: + - ItemMask + - type: Rotatable + - type: Tag + tags: + - DroneUsable diff --git a/Resources/Textures/Objects/Devices/mousetrap.rsi/meta.json b/Resources/Textures/Objects/Devices/mousetrap.rsi/meta.json new file mode 100644 index 0000000000..cf36cb1862 --- /dev/null +++ b/Resources/Textures/Objects/Devices/mousetrap.rsi/meta.json @@ -0,0 +1,23 @@ +{ + "version": 1, + + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/tgstation/tgstation/commit/4323540b6bec3ae93bbf13d685c2dbe0cb40a36e", + + "size": { + "x": 32, + "y": 32 + }, + + "states": [ + { + "name": "mousetrap", + "directions": 4 + }, + + { + "name": "mousetraparmed", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Objects/Devices/mousetrap.rsi/mousetrap.png b/Resources/Textures/Objects/Devices/mousetrap.rsi/mousetrap.png new file mode 100644 index 0000000000000000000000000000000000000000..d751807318acb601448dd4c83ecdf75b71eab6b4 GIT binary patch literal 1342 zcmV-E1;P4>P)m@p7$Ej}sLfeN+K+NHL=w!Qc4;UqV$)h6epn*+}e!o8g2eBYNZ z-#MJ#?_aQ3EEbF9g+)B?0Cd>Cah)vYbKhUQRP^_n@w@}j5btpotH zCYvET2i!*xj^N+z1Sd>r(MqH{KwbQxu1Reo@4TPAlgI~MhX^G5Qz)=WxE)z>AvNE%b3EC?+i$*MWJ`b1MkXPTah?49!)U#B}?7Q6|`?{{kzS|wz{fjJn$P zj1mBF<=5WjWxlLcVH&~(wlrhDumhD)%SR9`(8le-@8ubk{aVbMsyL8 z*${GY4xdJ*68Lwn;T$=mmc`6j+V)DW05LNPqr+g}+{jA2)nDt@gB@-25M&2w5x^i^XEGSS%Kc#bU8oEEbE!VligQ=l9bob};D` z{1^TUc7_T`Q%~J^$Va>112?03_uiQ5;D*Z(k=cl7MM>S9)?_nsID>QtltfSZN~l#< z4v`8@oII;U8&fcqXrrjsSRhReIqI5~sU1(YkWRNz+fa`<*bIP7CGrzP4u}$)MA12j zNCm?8f+0~C4}7K5ZLoPOwGH)IpV_aJ{d}P|o>p)0^4Zqle(i?!fjmYI_99TKja+bE zV~y(I=9-wMxQ`I=LxDYVe(Lul2YUl~&fa(53Pc+~wtHQeBD=#9kgST82d|NitATS- zJ|CDUoy)baOP0=sBgj-@f-*JdN0iSGeuR;Zs}thO#axJ@DnLHyQljK>bobmMoo-hZ zkh%ZV-PFHxt>h)(-|bX_?5D+*xt|wm?Wkhmg5~*n_pYnfeF2K905NYVp(U&7?zt7b zDyy6F6%bpj0?LV%P^t|lc@CU#F)p5s6UIrN%WD9}`4O?jK?PJ+H%*8yv}CoC(HEe& zJs>MbU$$ir(#-_A+??~=hLaO3fy%5*B}!Icgchv~?Bs0c_8ELH_z`Lw>Zz=5!tc4M zBf6$91Ac%(Z3L;c8+ zA*Du-#^6}9+FPUy^oj2IZ;kA~@CPA_#qwhF4^Ac*O=T*^DgXcg07*qoM6N<$g2M!t A(*OVf literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/mousetrap.rsi/mousetraparmed.png b/Resources/Textures/Objects/Devices/mousetrap.rsi/mousetraparmed.png new file mode 100644 index 0000000000000000000000000000000000000000..fc0257af242e24e1d72a8ec7e9f18f1ab5e1e06e GIT binary patch literal 1182 zcmV;P1Y!G$P)|6^~Jp{gE2}6D{fHGO@;17X(vb_O({xBYlyk$_;Bw%H@EF_?>Wtk(DQ+ibCcZf z`Q_YmfA{46z5x^pg+ig&vXE8>Aj0{Nk1{`>`{9>AeSfcQtqwqx(W}>-4O#GvDHKQQsPsH7OwaBo-2AUpN|Ms59jKG0-h2%brx?YDmr z{N!^4x)1PnD`3?Zz*}uVbrpcG3F<<41^@u40b{TidVdx=0Arv>ZX{*UbO6BJTcg+? z4gmm)zy655XZt|fcZti%#-R_7Iqi{~vNBDBRmxKP$<2UPLJe#*)wP}PO36vh0MefY z`}S+T&Au51>CZ|9AdGbi96WIh08lK=A(|MHmXn?VBz?(gUmTPUK$MS;A3{8S4u!&P z>v;n4=MlawiqrtGd=6}72_)13R$ef#!PEeL{$55-2=|v?|Ge+LBSLz@191J(GZa4j zplJb!l8i-=nwkb%Swbq41}iThmHi5!29mLe@H#|z02+;cJbn(dPyQ8^6o&L?`5NA} zb8)%1<}UjBl?y>f3^d4}i541pv?*5NrplEQ18!mI^?WX5ViRx^wd;+!0bJ6bgkx zp-?Ck3WY+UP$(1%g+d`s!~6Gs>i~X`sTI7A{yi&0qgSsvB`>=wL=8aGxnCz4i!{{O zOf|>LnR@-vGZYH9F?r`6kBi4>#oWj~D_I+40IhLnNNQ@DyTn{YN=-NGc$*}6CgG|( z*7s%GbwF!U+Cl?x_tq$HA3Sl4+hW+t5(rG!h8>(S*lU_8C2T8eA*qXwp50IS64=TT zKL{5-J;_ZQfL*=z#@io2YcP$84Ew_&{-{2<&pRif4l~d?*M?obZU!u$b2(G(en_Z; zAB5_I`>y!xn7w9(=K!$fMX<$L(D@nH>&4QXGXu&C8}3V=KJ=3J#eeCr@&Z2y#nPO6 zpZOWE#aYu%-vI9!py?nx_CV|EMzJ)