From c40af73e43c394edb1e96f2bfcc278ca0d54f94e Mon Sep 17 00:00:00 2001 From: Skubman Date: Sun, 8 Dec 2024 11:30:07 +0800 Subject: [PATCH] The Throwing Update (#1307) # Description Turns a plethora of items into throwing weapons that deal damage when thrown. Throwing weapons cost stamina to throw. ## Technical/Balance Details To make a melee weapon also a throwing weapon, just add `- type: DamageOtherOnHit`, and it will automatically inherit the damage from a light melee attack and the melee sound effect as the thrown hit sound effect. You can set a custom damage value with the `damage` field (necessary when the item is not a `MeleeWeapon`) and stamina cost with `staminaCost`. To make the throwing weapon embed and deal damage over time when embedded, add `- type: EmbeddableProjectile` and `- type: EmbedPassiveDamage`. By default, the embed damage per second is 5% of the throwing damage, but it can be modified on `EmbedPassiveDamage` with `throwingDamageMultiplier`. The default stamina cost for throwing is 3.5 stamina. The baseline cost for almost all DoT embeddables is 5 stamina, because of the extra damage the DoT brings. When a thrown item hits a target with body parts, it will randomly select a body part and only deal throwing damage to that body part. It will also embed to the same body part and only deal passive embed damage to it. ## TODO The unchecked checkmarks are best addressed in another PR but they will stay here for now.
Show Todo - [ ] Deal with prediction issue on embeddable projectile removal - [ ] This happens even before this PR so not really a big issue, maybe in a separate PR - [x] Add embeddable damage numbers to embeddables - [x] Fix throwing angle for surgery tools after the surgical tools sprite update - [ ] Try to make the throw knockback function as if it hit a wall - [x] Esword/desword/e-dagger toggle embed damage - [x] Don't start passive embed damage if EmbedPassiveDamageComponent has no damage - [x] Make DamageOtherOnHit.Damage not nullable - [x] Throwing damage only to a specific body part ### Traits - [ ] **Enraged Throw** (Oni) - [ ] Oni/trait damage bonus applied to throwing weapon too - [ ] Can throw carried bodies, which will do a MassContest between the thrown body and the hit body to determine blunt damage, and stun duration for each party - [ ] 15% resistance to thrown/embed damage - This helps when their enemy uses the items they throw against them. - [ ] **Sharpthrower** (Human) - [ ] 10% more Brute thrown damage - [ ] 50% chance of throw hitting targetted body part - [ ] 40% throwing stamina cost reduction - [ ] 15% resistance to thrown/embed damage ### Embeds - [x] Adjust embed damage per second to be like /tg/ (in /tg/ spear has around ~1.2 embed DPS, adjust for ~45% embed chance since we're not implementing embed chance and its 0.54) - [ ] Merge EmbeddableProjectileComponent and EmbeddablePassiveDamageComponent - [ ] Split SharedProjectileSystem into EmbeddableProjectileSystem - [x] Embed to a specific body part and deal damage only to that part, for now can randomly select body parts on embed - [ ] ~~Normal passive damage becomes x0.2 when lying down~~ - [ ] Increased damage when moving, more bonus damage for running (Jostle DPS on /tg/ is 0.2 running and 0.1 when walking/crawling) - [x] All embeddables have a fall out time (30 or 45 secs) - [ ] - [x] On damage examine, can see that an object is embeddable "It can embed on a target if thrown." - [ ] Negative moodlet for attached harmful embeddables - [ ] On health examine target with embeds, can see embedded objects "He has a spear embedded in his left arm." - [x] On examine item that is embedded, can see to which body part the item is embedded "The spear is embedded on Urist McHands's left arm." - [ ] An embeddable removed outside of surgery deals a lot of damage (x2 thrown damage) - [ ] Lying down prevents natural falling out and thus the damage with non-surgical removal - [ ] Surgical procedure on a body part to remove all embeds on it, using hemostat for removal - [x] Allow anyone to remove embedded cultist weapons even if they're not a cultist
## Media **Throwing Toolbox Tools** https://github.com/user-attachments/assets/4e20568f-adf0-4be8-ac38-fc6b21fed03c **Examine** ![image](https://github.com/user-attachments/assets/ef95e653-1491-4d9b-8f84-785c3df22763) **Examine After Embedding** ![image](https://github.com/user-attachments/assets/edc79c8f-db23-4bd3-9fa7-3b47f79c5881) ## Changelog :cl: Skubman - add: The Throwing Update is here. You can throw most melee weapons at the cost of stamina to deal damage from afar. - add: Dozens of throwable weapons, mainly sharp weapons will now embed on throw and deal damage every second until they're manually removed or naturally fall off after some time. - add: Examining the damage values of an item now shows its throwing damage, throwing stamina cost, whether or not it embeds on a throw, and if the embed deals damage over time. - add: Examining an embedded item now shows what body part it's embedded in. - tweak: The traits High Adrenaline, Adrenal Dysfunction, Masochism and Low Pain Tolerance now affect throwing attacks just like melee attacks. - tweak: The default time to remove embedded items has been increased from 3 to 5 seconds, and both the remover and the target with the embedded item need to stand still during the removal. - tweak: The time to pry up a floor tile with a crowbar and other tools has been decreased from 1 second to 0.5 seconds. The throwing damage of floor tiles has been increased. Go figure. - fix: Attempting to throw a Blood Cultist item without being a cultist will stun you and drop the item you're holding properly. --------- Co-authored-by: sleepyyapril <123355664+sleepyyapril@users.noreply.github.com> --- .../Damage/DamageOtherOnHitSystem.cs | 5 + .../Components/DamageOtherOnHitComponent.cs | 19 -- .../Damage/Systems/DamageOtherOnHitSystem.cs | 65 +++--- Content.Server/Flash/FlashSystem.cs | 20 +- Content.Server/Hands/Systems/HandsSystem.cs | 6 + .../Projectiles/ProjectileSystem.cs | 20 ++ .../Weapons/Melee/MeleeWeaponSystem.cs | 2 +- .../Systems/SharedBodySystem.Targeting.cs | 2 +- .../Components/DamageOtherOnHitComponent.cs | 75 +++++++ .../DamageOtherOnHitImmuneComponent.cs | 7 + .../Damage/Events/DamageOtherOnHitEvents.cs | 21 ++ .../Systems/SharedDamageOtherOnHitSystem.cs | 201 ++++++++++++++++++ .../ItemToggleDamageOtherOnHitComponent.cs | 60 ++++++ .../ItemToggleEmbedPassiveDamageComponent.cs | 23 ++ ...ItemToggleEmbeddableProjectileComponent.cs | 60 ++++++ .../ItemToggleThrowingAngleComponent.cs | 41 ++++ .../Item/ItemToggle/SharedItemToggleSystem.cs | 94 ++++++++ Content.Shared/Projectiles/EmbedEvent.cs | 18 +- .../EmbedPassiveDamageComponent.cs | 57 +++++ .../Projectiles/EmbedPassiveDamageSystem.cs | 115 ++++++++++ .../EmbeddableProjectileComponent.cs | 28 ++- .../Projectiles/SharedProjectileSystem.cs | 108 ++++++++-- Content.Shared/Throwing/BeforeThrowEvent.cs | 19 ++ Content.Shared/Throwing/ThrowEvents.cs | 12 +- .../Throwing/ThrownItemImmuneComponent.cs | 10 + Content.Shared/Throwing/ThrownItemSystem.cs | 15 +- .../Components/ToolTileCompatibleComponent.cs | 2 +- .../Systems/TraitStatModifierSystem.cs | 35 ++- .../Weapons/Melee/MeleeSoundSystem.cs | 5 +- .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 4 +- .../BloodCult/Items/CultItemSystem.cs | 22 +- Resources/Locale/en-US/body/body-parts.ftl | 19 ++ .../Locale/en-US/damage/damage-examine.ftl | 5 +- Resources/Locale/en-US/damage/stamina.ftl | 2 + Resources/Locale/en-US/traits/traits.ftl | 12 +- .../en-US/weapons/throwing/throwing.ftl | 6 + Resources/Prototypes/Body/Parts/base.yml | 46 ++++ .../Consumable/Food/Containers/lunchbox.yml | 7 +- .../Objects/Devices/Medical/portafib.yml | 7 + .../Entities/Clothing/Head/welding.yml | 7 + .../Entities/Clothing/Neck/pins.yml | 7 +- .../OuterClothing/base_clothingouter.yml | 9 +- .../Entities/Clothing/Shoes/magboots.yml | 20 ++ .../Objects/Consumable/Drinks/drinks_cans.yml | 6 + .../Consumable/Drinks/drinks_flasks.yml | 4 + .../Objects/Devices/holoprojectors.yml | 13 ++ .../Entities/Objects/Devices/pda.yml | 13 +- .../Fun/Instruments/instruments_string.yml | 14 ++ .../Entities/Objects/Fun/bike_horn.yml | 1 + .../Prototypes/Entities/Objects/Fun/pai.yml | 4 + .../Prototypes/Entities/Objects/Fun/toys.yml | 3 +- .../Entities/Objects/Materials/shards.yml | 11 +- .../Entities/Objects/Misc/briefcases.yml | 2 + .../Entities/Objects/Misc/broken_bottle.yml | 2 +- .../Objects/Misc/fire_extinguisher.yml | 2 + .../Entities/Objects/Misc/paper.yml | 20 +- .../Entities/Objects/Misc/tiles.yml | 13 +- .../Entities/Objects/Misc/utensils.yml | 14 ++ .../Objects/Specific/Hydroponics/tools.yml | 23 ++ .../Objects/Specific/Janitorial/janitor.yml | 10 + .../Objects/Specific/Medical/defib.yml | 7 + .../Medical/handheld_crew_monitor.yml | 11 +- .../Specific/Medical/healthanalyzer.yml | 4 + .../Objects/Specific/Medical/surgery.yml | 85 ++++++++ .../Objects/Specific/Service/barber.yml | 8 +- .../Service/vending_machine_restock.yml | 5 + .../Entities/Objects/Tools/emag.yml | 7 + .../Entities/Objects/Tools/flashlights.yml | 4 +- .../Entities/Objects/Tools/gas_tanks.yml | 8 + .../Entities/Objects/Tools/jaws_of_life.yml | 5 + .../Entities/Objects/Tools/lighters.yml | 24 +++ .../Entities/Objects/Tools/toolbox.yml | 11 + .../Entities/Objects/Tools/tools.yml | 30 +++ .../Entities/Objects/Tools/welders.yml | 6 + .../Weapons/Guns/Projectiles/arrows.yml | 1 + .../Objects/Weapons/Melee/baseball_bat.yml | 2 + .../Entities/Objects/Weapons/Melee/cane.yml | 1 + .../Entities/Objects/Weapons/Melee/cult.yml | 5 + .../Objects/Weapons/Melee/e_sword.yml | 33 +++ .../Objects/Weapons/Melee/fireaxe.yml | 5 + .../Objects/Weapons/Melee/home_run_bat.yml | 3 + .../Entities/Objects/Weapons/Melee/knife.yml | 33 ++- .../Entities/Objects/Weapons/Melee/mining.yml | 12 ++ .../Entities/Objects/Weapons/Melee/needle.yml | 5 + .../Objects/Weapons/Melee/pickaxe.yml | 6 + .../Objects/Weapons/Melee/sledgehammer.yml | 2 + .../Entities/Objects/Weapons/Melee/spear.yml | 1 + .../Objects/Weapons/Melee/stunprod.yml | 2 + .../Entities/Objects/Weapons/Melee/sword.yml | 33 +++ .../Weapons/Melee/telescopic_baton.yml | 5 +- .../Objects/Weapons/Melee/white_cane.yml | 2 +- .../Weapons/Throwable/throwing_stars.yml | 1 + .../Entities/Objects/Weapons/security.yml | 6 +- .../Furniture/Tables/base_structuretables.yml | 3 +- .../Closets/Lockers/base_structurelockers.yml | 5 + .../Storage/Closets/Lockers/lockers.yml | 5 + .../Storage/Closets/base_structureclosets.yml | 10 + .../Structures/Storage/Crates/crates.yml | 22 +- .../Entities/Structures/base_structure.yml | 5 + .../Entities/Objects/Fun/instruments.yml | 1 + .../Entities/Objects/Weapons/Melee/blunt.yml | 4 +- .../Weapons/Melee/breaching_hammer.yml | 4 +- .../Entities/Objects/Weapons/Melee/dulled.yml | 8 + .../Entities/Objects/Weapons/Melee/sword.yml | 4 + .../Entities/Objects/Weapons/Melee/cult.yml | 16 +- 105 files changed, 1773 insertions(+), 160 deletions(-) create mode 100644 Content.Client/Damage/DamageOtherOnHitSystem.cs delete mode 100644 Content.Server/Damage/Components/DamageOtherOnHitComponent.cs create mode 100644 Content.Shared/Damage/Components/DamageOtherOnHitComponent.cs create mode 100644 Content.Shared/Damage/Components/DamageOtherOnHitImmuneComponent.cs create mode 100644 Content.Shared/Damage/Events/DamageOtherOnHitEvents.cs create mode 100644 Content.Shared/Damage/Systems/SharedDamageOtherOnHitSystem.cs create mode 100644 Content.Shared/Item/ItemToggle/Components/ItemToggleDamageOtherOnHitComponent.cs create mode 100644 Content.Shared/Item/ItemToggle/Components/ItemToggleEmbedPassiveDamageComponent.cs create mode 100644 Content.Shared/Item/ItemToggle/Components/ItemToggleEmbeddableProjectileComponent.cs create mode 100644 Content.Shared/Item/ItemToggle/Components/ItemToggleThrowingAngleComponent.cs create mode 100644 Content.Shared/Projectiles/EmbedPassiveDamageComponent.cs create mode 100644 Content.Shared/Projectiles/EmbedPassiveDamageSystem.cs create mode 100644 Content.Shared/Throwing/ThrownItemImmuneComponent.cs create mode 100644 Resources/Locale/en-US/body/body-parts.ftl create mode 100644 Resources/Locale/en-US/weapons/throwing/throwing.ftl diff --git a/Content.Client/Damage/DamageOtherOnHitSystem.cs b/Content.Client/Damage/DamageOtherOnHitSystem.cs new file mode 100644 index 00000000000..1171e634dfe --- /dev/null +++ b/Content.Client/Damage/DamageOtherOnHitSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Damage.Systems; + +namespace Content.Client.Damage; + +public sealed class DamageOtherOnHitSystem : SharedDamageOtherOnHitSystem; diff --git a/Content.Server/Damage/Components/DamageOtherOnHitComponent.cs b/Content.Server/Damage/Components/DamageOtherOnHitComponent.cs deleted file mode 100644 index 3123e251af4..00000000000 --- a/Content.Server/Damage/Components/DamageOtherOnHitComponent.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Content.Server.Damage.Systems; -using Content.Shared.Damage; - -namespace Content.Server.Damage.Components -{ - [Access(typeof(DamageOtherOnHitSystem))] - [RegisterComponent] - public sealed partial class DamageOtherOnHitComponent : Component - { - [DataField("ignoreResistances")] - [ViewVariables(VVAccess.ReadWrite)] - public bool IgnoreResistances = false; - - [DataField("damage", required: true)] - [ViewVariables(VVAccess.ReadWrite)] - public DamageSpecifier Damage = default!; - - } -} diff --git a/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs b/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs index 0efa5349815..2ffd66fe068 100644 --- a/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs +++ b/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs @@ -1,65 +1,66 @@ -using Content.Server.Administration.Logs; -using Content.Server.Damage.Components; -using Content.Server.Weapons.Ranged.Systems; using Content.Shared.Camera; using Content.Shared.Damage; +using Content.Shared.Damage.Components; using Content.Shared.Damage.Events; using Content.Shared.Damage.Systems; using Content.Shared.Database; using Content.Shared.Effects; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Mobs.Components; +using Content.Shared.Projectiles; +using Content.Shared.Popups; using Content.Shared.Throwing; +using Content.Shared.Weapons.Melee; +using Robust.Server.GameObjects; +using Robust.Shared.Audio; using Robust.Shared.Physics.Components; using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; namespace Content.Server.Damage.Systems { - public sealed class DamageOtherOnHitSystem : EntitySystem + public sealed class DamageOtherOnHitSystem : SharedDamageOtherOnHitSystem { - [Dependency] private readonly IAdminLogManager _adminLogger = default!; - [Dependency] private readonly GunSystem _guns = default!; - [Dependency] private readonly DamageableSystem _damageable = default!; [Dependency] private readonly DamageExamineSystem _damageExamine = default!; - [Dependency] private readonly SharedCameraRecoilSystem _sharedCameraRecoil = default!; - [Dependency] private readonly SharedColorFlashEffectSystem _color = default!; - [Dependency] private readonly ThrownItemSystem _thrownItem = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly StaminaSystem _stamina = default!; public override void Initialize() { - SubscribeLocalEvent(OnDoHit); + base.Initialize(); + + SubscribeLocalEvent(OnBeforeThrow); SubscribeLocalEvent(OnDamageExamine); } - private void OnDoHit(EntityUid uid, DamageOtherOnHitComponent component, ThrowDoHitEvent args) + private void OnBeforeThrow(EntityUid uid, StaminaComponent component, ref BeforeThrowEvent args) { - var dmg = _damageable.TryChangeDamage(args.Target, component.Damage, component.IgnoreResistances, origin: args.Component.Thrower); - - // Log damage only for mobs. Useful for when people throw spears at each other, but also avoids log-spam when explosions send glass shards flying. - if (dmg != null && HasComp(args.Target)) - _adminLogger.Add(LogType.ThrowHit, $"{ToPrettyString(args.Target):target} received {dmg.GetTotal():damage} damage from collision"); - - if (dmg is { Empty: false }) - { - _color.RaiseEffect(Color.Red, new List() { args.Target }, Filter.Pvs(args.Target, entityManager: EntityManager)); - } + if (!TryComp(args.ItemUid, out var damage)) + return; - _guns.PlayImpactSound(args.Target, dmg, null, false); - if (TryComp(uid, out var body) && body.LinearVelocity.LengthSquared() > 0f) + if (component.CritThreshold - component.StaminaDamage <= damage.StaminaCost) { - var direction = body.LinearVelocity.Normalized(); - _sharedCameraRecoil.KickCamera(args.Target, direction); + args.Cancelled = true; + _popup.PopupEntity(Loc.GetString("throw-no-stamina", ("item", args.ItemUid)), uid, uid); + return; } - // TODO: If more stuff touches this then handle it after. - if (TryComp(uid, out var physics)) - { - _thrownItem.LandComponent(args.Thrown, args.Component, physics, false); - } + _stamina.TakeStaminaDamage(uid, damage.StaminaCost, component, visual: false); } private void OnDamageExamine(EntityUid uid, DamageOtherOnHitComponent component, ref DamageExamineEvent args) { - _damageExamine.AddDamageExamine(args.Message, component.Damage, Loc.GetString("damage-throw")); + _damageExamine.AddDamageExamine(args.Message, GetDamage(uid, component, args.User), Loc.GetString("damage-throw")); + + if (component.StaminaCost == 0) + return; + + var staminaCostMarkup = FormattedMessage.FromMarkupOrThrow( + Loc.GetString("damage-stamina-cost", + ("type", Loc.GetString("damage-throw")), ("cost", component.StaminaCost))); + args.Message.PushNewline(); + args.Message.AddMessage(staminaCostMarkup); } } } diff --git a/Content.Server/Flash/FlashSystem.cs b/Content.Server/Flash/FlashSystem.cs index bc91a307b08..994e74e68d4 100644 --- a/Content.Server/Flash/FlashSystem.cs +++ b/Content.Server/Flash/FlashSystem.cs @@ -14,6 +14,7 @@ using Content.Shared.Inventory; using Content.Shared.Physics; using Content.Shared.Tag; +using Content.Shared.Throwing; using Content.Shared.Weapons.Melee.Events; using Robust.Server.Audio; using Robust.Server.GameObjects; @@ -50,6 +51,7 @@ public override void Initialize() SubscribeLocalEvent(OnFlashMeleeHit); // ran before toggling light for extra-bright lantern SubscribeLocalEvent(OnFlashUseInHand, before: new []{ typeof(HandheldLightSystem) }); + SubscribeLocalEvent(OnFlashThrowHitEvent); SubscribeLocalEvent(OnInventoryFlashAttempt); SubscribeLocalEvent(OnFlashImmunityFlashAttempt); SubscribeLocalEvent(OnPermanentBlindnessFlashAttempt); @@ -60,10 +62,8 @@ private void OnFlashMeleeHit(EntityUid uid, FlashComponent comp, MeleeHitEvent a { if (!args.IsHit || !args.HitEntities.Any() || - !UseFlash(uid, comp, args.User)) - { + !UseFlash(uid, comp)) return; - } args.Handled = true; foreach (var e in args.HitEntities) @@ -74,14 +74,22 @@ private void OnFlashMeleeHit(EntityUid uid, FlashComponent comp, MeleeHitEvent a private void OnFlashUseInHand(EntityUid uid, FlashComponent comp, UseInHandEvent args) { - if (args.Handled || !UseFlash(uid, comp, args.User)) + if (args.Handled || !UseFlash(uid, comp)) return; args.Handled = true; FlashArea(uid, args.User, comp.Range, comp.AoeFlashDuration, comp.SlowTo, true, comp.Probability); } - private bool UseFlash(EntityUid uid, FlashComponent comp, EntityUid user) + private void OnFlashThrowHitEvent(EntityUid uid, FlashComponent comp, ThrowDoHitEvent args) + { + if (!UseFlash(uid, comp)) + return; + + FlashArea(uid, args.User, comp.Range, comp.AoeFlashDuration, comp.SlowTo, false, comp.Probability); + } + + private bool UseFlash(EntityUid uid, FlashComponent comp) { if (comp.Flashing) return false; @@ -99,7 +107,7 @@ private bool UseFlash(EntityUid uid, FlashComponent comp, EntityUid user) { _appearance.SetData(uid, FlashVisuals.Burnt, true); _tag.AddTag(uid, "Trash"); - _popup.PopupEntity(Loc.GetString("flash-component-becomes-empty"), user); + _popup.PopupEntity(Loc.GetString("flash-component-becomes-empty"), uid); } uid.SpawnTimer(400, () => diff --git a/Content.Server/Hands/Systems/HandsSystem.cs b/Content.Server/Hands/Systems/HandsSystem.cs index 8f0519b24cf..23b503caefd 100644 --- a/Content.Server/Hands/Systems/HandsSystem.cs +++ b/Content.Server/Hands/Systems/HandsSystem.cs @@ -237,6 +237,12 @@ hands.ActiveHandEntity is not { } throwEnt || // Let other systems change the thrown entity (useful for virtual items) // or the throw strength. + var itemEv = new BeforeGettingThrownEvent(throwEnt, direction, throwStrength, player); + RaiseLocalEvent(throwEnt, ref itemEv); + + if (itemEv.Cancelled) + return true; + var ev = new BeforeThrowEvent(throwEnt, direction, throwStrength, player); RaiseLocalEvent(player, ref ev); diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index 0061b16e47c..c5ec2d76ad5 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -1,3 +1,4 @@ +using Content.Shared.Damage.Events; using Content.Server.Administration.Logs; using Content.Server.Effects; using Content.Server.Weapons.Ranged.Systems; @@ -7,6 +8,7 @@ using Content.Shared.Projectiles; using Robust.Shared.Physics.Events; using Robust.Shared.Player; +using Robust.Shared.Utility; namespace Content.Server.Projectiles; @@ -22,6 +24,7 @@ public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnStartCollide); + SubscribeLocalEvent(OnDamageExamine); } private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref StartCollideEvent args) @@ -77,4 +80,21 @@ private void OnStartCollide(EntityUid uid, ProjectileComponent component, ref St RaiseNetworkEvent(new ImpactEffectEvent(component.ImpactEffect, GetNetCoordinates(xform.Coordinates)), Filter.Pvs(xform.Coordinates, entityMan: EntityManager)); } } + + private void OnDamageExamine(EntityUid uid, EmbeddableProjectileComponent component, ref DamageExamineEvent args) + { + if (!component.EmbedOnThrow) + return; + + if (!args.Message.IsEmpty) + args.Message.PushNewline(); + + var isHarmful = TryComp(uid, out var passiveDamage) && passiveDamage.Damage.Any(); + var loc = isHarmful + ? "damage-examine-embeddable-harmful" + : "damage-examine-embeddable"; + + var staminaCostMarkup = FormattedMessage.FromMarkupOrThrow(Loc.GetString(loc)); + args.Message.AddMessage(staminaCostMarkup); + } } diff --git a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs index a3719f8f397..daf76268a3c 100644 --- a/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapons/Melee/MeleeWeaponSystem.cs @@ -65,7 +65,7 @@ private void OnMeleeExamineDamage(EntityUid uid, MeleeWeaponComponent component, if (component.HeavyStaminaCost != 0) { var staminaCostMarkup = FormattedMessage.FromMarkupOrThrow( - Loc.GetString("damage-melee-heavy-stamina-cost", + Loc.GetString("damage-stamina-cost", ("type", Loc.GetString("damage-melee-heavy")), ("cost", component.HeavyStaminaCost))); args.Message.PushNewline(); args.Message.AddMessage(staminaCostMarkup); diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Targeting.cs b/Content.Shared/Body/Systems/SharedBodySystem.Targeting.cs index 09c3c5fa52d..202353d3eef 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Targeting.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Targeting.cs @@ -264,7 +264,7 @@ private static TargetBodyPart GetRandomPartSpread(IRobustRandom random, ushort t public TargetBodyPart? GetRandomBodyPart(EntityUid uid, TargetingComponent? target = null) { - if (!Resolve(uid, ref target)) + if (!Resolve(uid, ref target, false)) return null; var totalWeight = target.TargetOdds.Values.Sum(); diff --git a/Content.Shared/Damage/Components/DamageOtherOnHitComponent.cs b/Content.Shared/Damage/Components/DamageOtherOnHitComponent.cs new file mode 100644 index 00000000000..ca5179fe29d --- /dev/null +++ b/Content.Shared/Damage/Components/DamageOtherOnHitComponent.cs @@ -0,0 +1,75 @@ +using Content.Shared.Contests; +using Content.Shared.Damage.Systems; +using Content.Shared.Damage; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Damage.Components; + +/// +/// Deals damage when thrown. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class DamageOtherOnHitComponent : Component +{ + [DataField, AutoNetworkedField] + public bool IgnoreResistances = false; + + /// + /// The damage that a throw deals. + /// + [DataField, AutoNetworkedField] + public DamageSpecifier Damage = new(); + + /// + /// The stamina cost of throwing this entity. + /// + [DataField, AutoNetworkedField] + public float StaminaCost = 3.5f; + + /// + /// The maximum amount of hits per throw before landing on the floor. + /// + [DataField, AutoNetworkedField] + public int MaxHitQuantity = 1; + + /// + /// The tracked amount of hits in a single throw. + /// + [DataField, AutoNetworkedField] + public int HitQuantity = 0; + + /// + /// The multiplier to apply to the entity's light attack damage to calculate the throwing damage. + /// Only used if this component has a MeleeWeaponComponent and Damage is not set on the prototype. + /// + [DataField, AutoNetworkedField] + public float MeleeDamageMultiplier = 1f; + + /// + /// The sound to play when this entity hits on a throw. + /// If null, attempts to retrieve the SoundHit from MeleeWeaponComponent. + /// + [DataField, AutoNetworkedField] + public SoundSpecifier? SoundHit; + + /// + /// Arguments for modifying the throwing weapon damage with contests. + /// These are the same ContestArgs in MeleeWeaponComponent. + /// + [DataField] + public ContestArgs ContestArgs = new ContestArgs + { + DoStaminaInteraction = true, + StaminaDisadvantage = true, + DoHealthInteraction = true, + }; + + /// + /// Plays if no damage is done to the target entity. + /// If null, attempts to retrieve the SoundNoDamage from MeleeWeaponComponent. + /// + [DataField, AutoNetworkedField] + public SoundSpecifier SoundNoDamage { get; set; } = new SoundCollectionSpecifier("WeakHit"); +} diff --git a/Content.Shared/Damage/Components/DamageOtherOnHitImmuneComponent.cs b/Content.Shared/Damage/Components/DamageOtherOnHitImmuneComponent.cs new file mode 100644 index 00000000000..2be4886fbd8 --- /dev/null +++ b/Content.Shared/Damage/Components/DamageOtherOnHitImmuneComponent.cs @@ -0,0 +1,7 @@ +namespace Content.Shared.Damage.Components; + +/// +/// This is used for entities that are immune to getting hit by DamageOtherOnHit, and getting embedded from EmbeddableProjectile. +/// +[RegisterComponent] +public sealed partial class DamageOtherOnHitImmuneComponent : Component {} diff --git a/Content.Shared/Damage/Events/DamageOtherOnHitEvents.cs b/Content.Shared/Damage/Events/DamageOtherOnHitEvents.cs new file mode 100644 index 00000000000..512546fda50 --- /dev/null +++ b/Content.Shared/Damage/Events/DamageOtherOnHitEvents.cs @@ -0,0 +1,21 @@ +using Content.Shared.Damage; +using Content.Shared.Damage.Components; +using Content.Shared.Item.ItemToggle.Components; + +namespace Content.Shared.Damage.Events; + +/// +/// Raised on a throwing weapon to calculate potential damage bonuses or decreases. +/// +[ByRefEvent] +public record struct GetThrowingDamageEvent(EntityUid Weapon, DamageSpecifier Damage, List Modifiers, EntityUid? User); + +/// +/// Raised on a throwing weapon when DamageOtherOnHit has been successfully initialized. +/// +public record struct DamageOtherOnHitStartupEvent(Entity Weapon); + +/// +/// Raised on a throwing weapon when ItemToggleDamageOtherOnHit has been successfully initialized. +/// +public record struct ItemToggleDamageOtherOnHitStartupEvent(Entity Weapon); diff --git a/Content.Shared/Damage/Systems/SharedDamageOtherOnHitSystem.cs b/Content.Shared/Damage/Systems/SharedDamageOtherOnHitSystem.cs new file mode 100644 index 00000000000..8b3d29d7349 --- /dev/null +++ b/Content.Shared/Damage/Systems/SharedDamageOtherOnHitSystem.cs @@ -0,0 +1,201 @@ +using Content.Shared.Administration.Logs; +using Content.Shared.Camera; +using Content.Shared.Contests; +using Content.Shared.Damage; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Events; +using Content.Shared.Database; +using Content.Shared.Effects; +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Mobs.Components; +using Content.Shared.Projectiles; +using Content.Shared.Popups; +using Content.Shared.Throwing; +using Content.Shared.Weapons.Melee; +using Robust.Shared.GameObjects; +using Robust.Shared.Audio; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Shared.Damage.Systems +{ + public abstract partial class SharedDamageOtherOnHitSystem : EntitySystem + { + [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly SharedCameraRecoilSystem _sharedCameraRecoil = default!; + [Dependency] private readonly SharedColorFlashEffectSystem _color = default!; + [Dependency] private readonly ThrownItemSystem _thrownItem = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly MeleeSoundSystem _meleeSound = default!; + [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly ContestsSystem _contests = default!; + + public override void Initialize() + { + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnDoHit); + SubscribeLocalEvent(OnThrown); + + SubscribeLocalEvent(OnItemToggleMapInit); + SubscribeLocalEvent(OnItemToggle); + } + + /// + /// Inherit stats from MeleeWeapon. + /// + private void OnMapInit(EntityUid uid, DamageOtherOnHitComponent component, MapInitEvent args) + { + if (!TryComp(uid, out var melee)) + return; + + if (component.Damage.Empty) + component.Damage = melee.Damage * component.MeleeDamageMultiplier; + if (component.SoundHit == null) + component.SoundHit = melee.SoundHit; + if (component.SoundNoDamage == null) + { + if (melee.SoundNoDamage != null) + component.SoundNoDamage = melee.SoundNoDamage; + else + component.SoundNoDamage = new SoundCollectionSpecifier("WeakHit"); + } + + RaiseLocalEvent(uid, new DamageOtherOnHitStartupEvent((uid, component))); + } + + /// + /// Inherit stats from ItemToggleMeleeWeaponComponent. + /// + private void OnItemToggleMapInit(EntityUid uid, ItemToggleDamageOtherOnHitComponent component, MapInitEvent args) + { + if (!TryComp(uid, out var itemToggleMelee) || + !TryComp(uid, out var damage)) + return; + + if (component.ActivatedDamage == null && itemToggleMelee.ActivatedDamage is {} activatedDamage) + component.ActivatedDamage = activatedDamage * damage.MeleeDamageMultiplier; + if (component.ActivatedSoundHit == null) + component.ActivatedSoundHit = itemToggleMelee.ActivatedSoundOnHit; + if (component.ActivatedSoundNoDamage == null && itemToggleMelee.ActivatedSoundOnHitNoDamage is {} activatedSoundOnHitNoDamage) + component.ActivatedSoundNoDamage = activatedSoundOnHitNoDamage; + + RaiseLocalEvent(uid, new ItemToggleDamageOtherOnHitStartupEvent((uid, component))); + } + + private void OnDoHit(EntityUid uid, DamageOtherOnHitComponent component, ThrowDoHitEvent args) + { + if (component.HitQuantity >= component.MaxHitQuantity) + return; + + var modifiedDamage = _damageable.TryChangeDamage(args.Target, GetDamage(uid, component, args.Component.Thrower), + component.IgnoreResistances, origin: args.Component.Thrower, targetPart: args.TargetPart); + + // Log damage only for mobs. Useful for when people throw spears at each other, but also avoids log-spam when explosions send glass shards flying. + if (modifiedDamage != null) + { + if (HasComp(args.Target)) + _adminLogger.Add(LogType.ThrowHit, $"{ToPrettyString(args.Target):target} received {modifiedDamage.GetTotal():damage} damage from collision"); + + _meleeSound.PlayHitSound(args.Target, null, SharedMeleeWeaponSystem.GetHighestDamageSound(modifiedDamage, _protoManager), null, + component.SoundHit, component.SoundNoDamage); + } + + if (modifiedDamage is { Empty: false }) + _color.RaiseEffect(Color.Red, new List() { args.Target }, Filter.Pvs(args.Target, entityManager: EntityManager)); + + if (TryComp(uid, out var body) && body.LinearVelocity.LengthSquared() > 0f) + { + var direction = body.LinearVelocity.Normalized(); + _sharedCameraRecoil.KickCamera(args.Target, direction); + } + + // TODO: If more stuff touches this then handle it after. + if (TryComp(uid, out var physics)) + { + _thrownItem.LandComponent(args.Thrown, args.Component, physics, false); + + if (!HasComp(args.Thrown)) + { + var newVelocity = physics.LinearVelocity; + newVelocity.X = -newVelocity.X / 4; + newVelocity.Y = -newVelocity.Y / 4; + _physics.SetLinearVelocity(uid, newVelocity, body: physics); + } + } + + component.HitQuantity += 1; + } + + /// + /// Used to update the DamageOtherOnHit component on item toggle. + /// + private void OnItemToggle(EntityUid uid, DamageOtherOnHitComponent component, ItemToggledEvent args) + { + if (!TryComp(uid, out var itemToggle)) + return; + + if (args.Activated) + { + if (itemToggle.ActivatedDamage is {} activatedDamage) + { + itemToggle.DeactivatedDamage ??= component.Damage; + component.Damage = activatedDamage * component.MeleeDamageMultiplier; + } + + if (itemToggle.ActivatedStaminaCost is {} activatedStaminaCost) + { + itemToggle.DeactivatedStaminaCost ??= component.StaminaCost; + component.StaminaCost = activatedStaminaCost; + } + + itemToggle.DeactivatedSoundHit ??= component.SoundHit; + component.SoundHit = itemToggle.ActivatedSoundHit; + + if (itemToggle.ActivatedSoundNoDamage is {} activatedSoundNoDamage) + { + itemToggle.DeactivatedSoundNoDamage ??= component.SoundNoDamage; + component.SoundNoDamage = activatedSoundNoDamage; + } + } + else + { + if (itemToggle.DeactivatedDamage is {} deactivatedDamage) + component.Damage = deactivatedDamage; + + if (itemToggle.DeactivatedStaminaCost is {} deactivatedStaminaCost) + component.StaminaCost = deactivatedStaminaCost; + + component.SoundHit = itemToggle.DeactivatedSoundHit; + + if (itemToggle.DeactivatedSoundNoDamage is {} deactivatedSoundNoDamage) + component.SoundNoDamage = deactivatedSoundNoDamage; + } + } + + private void OnThrown(EntityUid uid, DamageOtherOnHitComponent component, ThrownEvent args) + { + component.HitQuantity = 0; + } + + /// + /// Gets the total damage a throwing weapon does. + /// + public DamageSpecifier GetDamage(EntityUid uid, DamageOtherOnHitComponent? component = null, EntityUid? user = null) + { + if (!Resolve(uid, ref component, false)) + return new DamageSpecifier(); + + var ev = new GetThrowingDamageEvent(uid, component.Damage, new(), user); + RaiseLocalEvent(uid, ref ev); + + if (component.ContestArgs is not null && user is EntityUid userUid) + ev.Damage *= _contests.ContestConstructor(userUid, component.ContestArgs); + + return DamageSpecifier.ApplyModifierSets(ev.Damage, ev.Modifiers); + } + } +} diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleDamageOtherOnHitComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleDamageOtherOnHitComponent.cs new file mode 100644 index 00000000000..844582f6fa8 --- /dev/null +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleDamageOtherOnHitComponent.cs @@ -0,0 +1,60 @@ +using Content.Shared.Damage.Systems; +using Content.Shared.Damage; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Item.ItemToggle.Components; + +/// +/// Handles changes to DamageOtherOnHitComponent when the item is toggled. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ItemToggleDamageOtherOnHitComponent : Component +{ + /// + /// The stamina cost of throwing this entity when activated. + /// + [DataField, AutoNetworkedField] + public float? ActivatedStaminaCost = null; + + /// + /// The stamina cost of throwing this entity when deactivated. + /// + [DataField, AutoNetworkedField] + public float? DeactivatedStaminaCost = null; + + /// + /// Damage done by this item when activated. + /// + [DataField, AutoNetworkedField] + public DamageSpecifier? ActivatedDamage = null; + + /// + /// Damage done by this item when deactivated. + /// + [DataField, AutoNetworkedField] + public DamageSpecifier? DeactivatedDamage = null; + + /// + /// The noise this item makes when hitting something with it on. + /// + [DataField, AutoNetworkedField] + public SoundSpecifier? ActivatedSoundHit; + + /// + /// The noise this item makes when hitting something with it off. + /// + public SoundSpecifier? DeactivatedSoundHit; + + /// + /// The noise this item makes when hitting something with it off and it does no damage. + /// + [DataField, AutoNetworkedField] + public SoundSpecifier ActivatedSoundNoDamage { get; set; } = new SoundCollectionSpecifier("WeakHit"); + + /// + /// The noise this item makes when hitting something with it off and it does no damage. + /// + public SoundSpecifier? DeactivatedSoundNoDamage; +} diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleEmbedPassiveDamageComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleEmbedPassiveDamageComponent.cs new file mode 100644 index 00000000000..76ecbec3604 --- /dev/null +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleEmbedPassiveDamageComponent.cs @@ -0,0 +1,23 @@ +using Content.Shared.Damage; +using Robust.Shared.GameStates; + +namespace Content.Shared.Item.ItemToggle.Components; + +/// +/// Handles the changes to the embed passive damage when toggled. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ItemToggleEmbedPassiveDamageComponent : Component +{ + /// + /// Damage per interval dealt to the entity every interval when activated. + /// + [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public DamageSpecifier? ActivatedDamage = null; + + /// + /// Damage per interval dealt to the entity every interval when deactivated. + /// + [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public DamageSpecifier? DeactivatedDamage = null; +} diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleEmbeddableProjectileComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleEmbeddableProjectileComponent.cs new file mode 100644 index 00000000000..bd8f68402dc --- /dev/null +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleEmbeddableProjectileComponent.cs @@ -0,0 +1,60 @@ +using System.Numerics; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared.Item.ItemToggle.Components; + +/// +/// Handles the embeddable stats for activated items. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ItemToggleEmbeddableProjectileComponent : Component +{ + /// + /// The removal time when this item is activated. + /// + [DataField, AutoNetworkedField] + public float? ActivatedRemovalTime; + + /// + /// The offset of the sprite when this item is activated. + /// + [DataField, AutoNetworkedField] + public Vector2? ActivatedOffset; + + /// + /// Whether this entity will embed when thrown when this item is activated. + /// + [DataField, AutoNetworkedField] + public bool? ActivatedEmbedOnThrow; + + /// + /// The sound to play after embedding when this item is activated. + /// + [DataField, AutoNetworkedField] + public SoundSpecifier? ActivatedSound; + + /// + /// The removal time when this item is deactivated. + /// + [DataField, AutoNetworkedField] + public float? DeactivatedRemovalTime; + + /// + /// The offset of the sprite when this item is deactivated. + /// + [DataField, AutoNetworkedField] + public Vector2? DeactivatedOffset; + + /// + /// Whether this entity will embed when thrown when this item is deactivated. + /// + [DataField, AutoNetworkedField] + public bool? DeactivatedEmbedOnThrow; + + /// + /// The sound to play after embedding when this item is deactivated. + /// + [DataField, AutoNetworkedField] + public SoundSpecifier? DeactivatedSound; +} diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleThrowingAngleComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleThrowingAngleComponent.cs new file mode 100644 index 00000000000..38590621c37 --- /dev/null +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleThrowingAngleComponent.cs @@ -0,0 +1,41 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Item.ItemToggle.Components; + +/// +/// Handles the changes to the throwing angle when the item is toggled. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ItemToggleThrowingAngleComponent : Component +{ + /// + /// Item's throwing spin status when activated. + /// + [DataField, AutoNetworkedField] + public bool? ActivatedAngularVelocity = null; + + /// + /// Item's angle when activated. + /// + [DataField, AutoNetworkedField] + public Angle? ActivatedAngle = null; + + /// + /// Item's throwing spin status when deactivated. + /// + [DataField, AutoNetworkedField] + public bool? DeactivatedAngularVelocity = null; + + /// + /// Item's angle when deactivated. + /// + [DataField, AutoNetworkedField] + public Angle? DeactivatedAngle = null; + + /// + /// When this is true, adds the ThrowingAngle component on activation + /// and deletes it on deactivation. + /// + [DataField, AutoNetworkedField] + public bool DeleteOnDeactivate = false; +} diff --git a/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs b/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs index fa23339223e..d07fd5a735e 100644 --- a/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs +++ b/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs @@ -1,12 +1,15 @@ using Content.Shared.Interaction.Events; using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Popups; +using Content.Shared.Projectiles; using Content.Shared.Temperature; +using Content.Shared.Throwing; using Content.Shared.Toggleable; using Content.Shared.Wieldable; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Network; +using System.Numerics; namespace Content.Shared.Item.ItemToggle; /// @@ -35,6 +38,8 @@ public override void Initialize() SubscribeLocalEvent(OnIsHotEvent); SubscribeLocalEvent(UpdateActiveSound); + SubscribeLocalEvent(UpdateThrowingAngle); + SubscribeLocalEvent(UpdateEmbeddableProjectile); } private void OnStartup(Entity ent, ref ComponentStartup args) @@ -268,4 +273,93 @@ private void UpdateActiveSound(EntityUid uid, ItemToggleActiveSoundComponent act activeSound.PlayingStream = _audio.Stop(activeSound.PlayingStream); } } + + /// + /// Used to update the throwing angle on item toggle. + /// + private void UpdateThrowingAngle(EntityUid uid, ItemToggleThrowingAngleComponent component, ItemToggledEvent args) + { + if (component.DeleteOnDeactivate) + { + if (args.Activated) + { + var newThrowingAngle = new ThrowingAngleComponent(); + + if (component.ActivatedAngle is {} activatedAngle) + newThrowingAngle.Angle = activatedAngle; + + if (component.ActivatedAngularVelocity is {} activatedAngularVelocity) + newThrowingAngle.AngularVelocity = activatedAngularVelocity; + + AddComp(uid, newThrowingAngle); + } + else + RemCompDeferred(uid); + return; + } + + if (!TryComp(uid, out var throwingAngle)) + return; + + if (args.Activated) + { + component.DeactivatedAngle ??= throwingAngle.Angle; + if (component.ActivatedAngle is {} activatedAngle) + throwingAngle.Angle = activatedAngle; + + component.DeactivatedAngularVelocity ??= throwingAngle.AngularVelocity; + if (component.ActivatedAngularVelocity is {} activatedAngularVelocity) + throwingAngle.AngularVelocity = activatedAngularVelocity; + } + else + { + if (component.DeactivatedAngle is {} deactivatedAngle) + throwingAngle.Angle = deactivatedAngle; + + if (component.DeactivatedAngularVelocity is {} deactivatedAngularVelocity) + throwingAngle.AngularVelocity = deactivatedAngularVelocity; + } + } + + /// + /// Used to update the embeddable stats on item toggle. + /// + private void UpdateEmbeddableProjectile(EntityUid uid, ItemToggleEmbeddableProjectileComponent component, ItemToggledEvent args) + { + if (!TryComp(uid, out var embeddable)) + return; + + if (args.Activated) + { + component.DeactivatedRemovalTime ??= embeddable.RemovalTime; + if (component.ActivatedRemovalTime is {} activatedRemovalTime) + embeddable.RemovalTime = activatedRemovalTime; + + component.DeactivatedOffset ??= embeddable.Offset; + if (component.ActivatedOffset is {} activatedOffset) + embeddable.Offset = activatedOffset; + + component.DeactivatedEmbedOnThrow ??= embeddable.EmbedOnThrow; + if (component.ActivatedEmbedOnThrow is {} activatedEmbedOnThrow) + embeddable.EmbedOnThrow = activatedEmbedOnThrow; + + component.DeactivatedSound ??= embeddable.Sound; + if (component.ActivatedSound is {} activatedSound) + embeddable.Sound = activatedSound; + } + else + { + if (component.DeactivatedRemovalTime is {} deactivatedRemovalTime) + embeddable.RemovalTime = deactivatedRemovalTime; + + if (component.DeactivatedOffset is {} deactivatedOffset) + embeddable.Offset = deactivatedOffset; + + if (component.DeactivatedEmbedOnThrow is {} deactivatedEmbedOnThrow) + embeddable.EmbedOnThrow = deactivatedEmbedOnThrow; + + if (component.DeactivatedSound is {} deactivatedSound) + embeddable.Sound = deactivatedSound; + } + } } diff --git a/Content.Shared/Projectiles/EmbedEvent.cs b/Content.Shared/Projectiles/EmbedEvent.cs index 521a691f45a..9d47a815d3c 100644 --- a/Content.Shared/Projectiles/EmbedEvent.cs +++ b/Content.Shared/Projectiles/EmbedEvent.cs @@ -1,10 +1,12 @@ +using Content.Shared.Targeting; + namespace Content.Shared.Projectiles; /// /// Raised directed on an entity when it embeds in another entity. /// [ByRefEvent] -public readonly record struct EmbedEvent(EntityUid? Shooter, EntityUid Embedded) +public readonly record struct EmbedEvent(EntityUid? Shooter, EntityUid Embedded, TargetBodyPart? BodyPart) { public readonly EntityUid? Shooter = Shooter; @@ -12,4 +14,18 @@ public readonly record struct EmbedEvent(EntityUid? Shooter, EntityUid Embedded) /// Entity that is embedded in. /// public readonly EntityUid Embedded = Embedded; + + /// + /// Body part that has the embedded entity. + /// + public readonly TargetBodyPart? BodyPart = BodyPart; +} + +/// +/// Raised on an entity when it stops embedding in another entity. +/// +[ByRefEvent] +public readonly record struct RemoveEmbedEvent(EntityUid? Remover) +{ + public readonly EntityUid? Remover = Remover; } diff --git a/Content.Shared/Projectiles/EmbedPassiveDamageComponent.cs b/Content.Shared/Projectiles/EmbedPassiveDamageComponent.cs new file mode 100644 index 00000000000..cfb08fcf7be --- /dev/null +++ b/Content.Shared/Projectiles/EmbedPassiveDamageComponent.cs @@ -0,0 +1,57 @@ +using Content.Shared.Damage; +using Content.Shared.Damage.Components; +using Content.Shared.FixedPoint; +using Content.Shared.Mobs.Components; +using Content.Shared.Targeting; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; +using Robust.Shared.GameStates; + +namespace Content.Shared.Projectiles; + +/// +/// Passively damages the mob this embeddable is attached to. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class EmbedPassiveDamageComponent : Component +{ + /// + /// The entity this embeddable is attached to. + /// + [ViewVariables(VVAccess.ReadWrite)] + public EntityUid? Embedded = null; + + /// + /// The damage component to deal damage to. + /// + [ViewVariables(VVAccess.ReadOnly)] + public DamageableComponent? EmbeddedDamageable = null; + + /// + /// The MobState component to check if the target is still alive. + /// + [ViewVariables(VVAccess.ReadOnly)] + public MobStateComponent? EmbeddedMobState = null; + + /// + /// The body part to apply damage to. + /// + [ViewVariables(VVAccess.ReadOnly)] + public TargetBodyPart? EmbeddedBodyPart = null; + + /// + /// Damage per interval dealt to the entity every interval. + /// If this is set manually, DamageMultiplier will be ignored. + /// + [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public DamageSpecifier Damage = new(); + + /// + /// Multiplier to be applied to the damage of DamageOtherOnHit to + /// calculate the damage per second. + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float ThrowingDamageMultiplier = 0.05f; + + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] + public TimeSpan NextDamage = TimeSpan.Zero; +} diff --git a/Content.Shared/Projectiles/EmbedPassiveDamageSystem.cs b/Content.Shared/Projectiles/EmbedPassiveDamageSystem.cs new file mode 100644 index 00000000000..55733ac5bb3 --- /dev/null +++ b/Content.Shared/Projectiles/EmbedPassiveDamageSystem.cs @@ -0,0 +1,115 @@ +using Content.Shared.Damage; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Events; +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Systems; +using Content.Shared.Mobs.Components; +using Content.Shared.FixedPoint; +using Robust.Shared.Timing; + +namespace Content.Shared.Projectiles; + +public sealed class EmbedPassiveDamageSystem : EntitySystem +{ + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnDamageOtherOnHitStartup); + SubscribeLocalEvent(OnItemToggleStartup); + SubscribeLocalEvent(OnEmbed); + SubscribeLocalEvent(OnRemoveEmbed); + SubscribeLocalEvent(OnItemToggle); + } + + /// + /// Inherit stats from DamageOtherOnHit. + /// + private void OnDamageOtherOnHitStartup(EntityUid uid, EmbedPassiveDamageComponent component, DamageOtherOnHitStartupEvent args) + { + if (component.Damage.Empty) + component.Damage = args.Weapon.Comp.Damage * component.ThrowingDamageMultiplier; + } + + /// + /// Inherit stats from ItemToggleDamageOtherOnHit. + /// + private void OnItemToggleStartup(EntityUid uid, ItemToggleEmbedPassiveDamageComponent component, ItemToggleDamageOtherOnHitStartupEvent args) + { + if (!TryComp(uid, out var embedPassiveDamage) || + component.ActivatedDamage != null || + !(args.Weapon.Comp.ActivatedDamage is {} activatedDamage)) + return; + + component.ActivatedDamage = activatedDamage * embedPassiveDamage.ThrowingDamageMultiplier; + } + + private void OnEmbed(EntityUid uid, EmbedPassiveDamageComponent component, EmbedEvent args) + { + if (component.Damage.Empty || component.Damage.GetTotal() == 0 || + !TryComp(args.Embedded, out var mobState) || + !TryComp(args.Embedded, out var damageable)) + return; + + component.Embedded = args.Embedded; + component.EmbeddedDamageable = damageable; + component.EmbeddedMobState = mobState; + component.EmbeddedBodyPart = args.BodyPart; + component.NextDamage = _timing.CurTime + TimeSpan.FromSeconds(1f); + + Dirty(uid, component); + } + + private void OnRemoveEmbed(EntityUid uid, EmbedPassiveDamageComponent component, RemoveEmbedEvent args) + { + component.Embedded = null; + component.EmbeddedDamageable = null; + component.EmbeddedMobState = null; + component.EmbeddedBodyPart = null; + component.NextDamage = TimeSpan.Zero; + + Dirty(uid, component); + } + + /// + /// Used to update the EmbedPassiveDamageComponent component on item toggle. + /// + private void OnItemToggle(EntityUid uid, EmbedPassiveDamageComponent component, ItemToggledEvent args) + { + if (!TryComp(uid, out var itemTogglePassiveDamage)) + return; + + if (args.Activated && itemTogglePassiveDamage.ActivatedDamage is {} activatedDamage) + { + itemTogglePassiveDamage.DeactivatedDamage ??= component.Damage; + component.Damage = activatedDamage; + } + else if (itemTogglePassiveDamage.DeactivatedDamage is {} deactivatedDamage) + component.Damage = deactivatedDamage; + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + var curTime = _timing.CurTime; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.Embedded is null || + comp.EmbeddedDamageable is null || + comp.NextDamage > curTime || // Make sure they're up for a damage tick + comp.EmbeddedMobState is null || + comp.EmbeddedMobState.CurrentState == MobState.Dead) // Don't damage dead mobs, they've already gone through too much + continue; + + comp.NextDamage = curTime + TimeSpan.FromSeconds(1f); + + _damageable.TryChangeDamage(comp.Embedded, comp.Damage, false, false, comp.EmbeddedDamageable, targetPart: comp.EmbeddedBodyPart); + } + } +} diff --git a/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs b/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs index 008b7c2ced4..2a0dc1b1da1 100644 --- a/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs +++ b/Content.Shared/Projectiles/EmbeddableProjectileComponent.cs @@ -1,5 +1,7 @@ +using Content.Shared.Targeting; using System.Numerics; using Robust.Shared.Audio; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.GameStates; namespace Content.Shared.Projectiles; @@ -27,7 +29,7 @@ public sealed partial class EmbeddableProjectileComponent : Component /// How long it takes to remove the embedded object. /// [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] - public float? RemovalTime = 3f; + public float? RemovalTime = 5f; /// /// Whether this entity will embed when thrown, or only when shot as a projectile. @@ -46,4 +48,28 @@ public sealed partial class EmbeddableProjectileComponent : Component /// [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] public SoundSpecifier? Sound; + + /// + /// The entity this embeddable is attached to. + /// + [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public EntityUid? Target = null; + + /// + /// The body part of the target this embeddable is attached to. + /// + [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + public TargetBodyPart? TargetBodyPart = null; + + /// + /// How much time before this entity automatically falls off? (0 is never) + /// + [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + public float AutoRemoveDuration = 40f; + + /// + /// The time when this entity automatically falls off after being attached. + /// + [ViewVariables(VVAccess.ReadWrite), DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField] + public TimeSpan? AutoRemoveTime = null; } diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index 593ad221b8d..960dab84611 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -1,10 +1,15 @@ using System.Numerics; +using Content.Shared.Body.Systems; using Content.Shared.CombatMode.Pacification; using Content.Shared.Damage; using Content.Shared.DoAfter; +using Content.Shared.Examine; +using Content.Shared.IdentityManagement; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Mobs.Components; +using Content.Shared.Popups; +using Content.Shared.Targeting; using Content.Shared.Throwing; using Robust.Shared.Audio.Systems; using Robust.Shared.Map; @@ -15,6 +20,7 @@ using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; using Robust.Shared.Serialization; +using Robust.Shared.Timing; namespace Content.Shared.Projectiles; @@ -28,6 +34,9 @@ public abstract partial class SharedProjectileSystem : EntitySystem [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedBodySystem _body = default!; public override void Initialize() { @@ -39,6 +48,27 @@ public override void Initialize() SubscribeLocalEvent(OnEmbedActivate); SubscribeLocalEvent(OnEmbedRemove); SubscribeLocalEvent(OnAttemptPacifiedThrow); + SubscribeLocalEvent(OnExamined); + } + + // TODO: rename Embedded to Target in every context + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + var curTime = _timing.CurTime; + + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.AutoRemoveTime == null || comp.AutoRemoveTime > curTime) + continue; + + if (comp.Target is {} targetUid) + _popup.PopupClient(Loc.GetString("throwing-embed-falloff", ("item", uid)), targetUid, targetUid); + + RemoveEmbed(uid, comp); + } } private void OnEmbedActivate(EntityUid uid, EmbeddableProjectileComponent component, ActivateInWorldEvent args) @@ -52,17 +82,39 @@ private void OnEmbedActivate(EntityUid uid, EmbeddableProjectileComponent compon args.Handled = true; + if (component.Target is {} targetUid) + _popup.PopupClient(Loc.GetString("throwing-embed-remove-alert-owner", ("item", uid), ("other", args.User)), + args.User, targetUid); + _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, component.RemovalTime.Value, new RemoveEmbeddedProjectileEvent(), eventTarget: uid, target: uid) { DistanceThreshold = SharedInteractionSystem.InteractionRange, + BreakOnUserMove = true, + BreakOnTargetMove = true, + NeedHand = true, }); } private void OnEmbedRemove(EntityUid uid, EmbeddableProjectileComponent component, RemoveEmbeddedProjectileEvent args) { + if (args.Cancelled) + return; + + RemoveEmbed(uid, component, args.User); + } + + private void RemoveEmbed(EntityUid uid, EmbeddableProjectileComponent component, EntityUid? remover = null) + { + component.AutoRemoveTime = null; + component.Target = null; + component.TargetBodyPart = null; + + var ev = new RemoveEmbedEvent(remover); + RaiseLocalEvent(uid, ref ev); + // Whacky prediction issues. - if (args.Cancelled || _netManager.IsClient) + if (_netManager.IsClient) return; if (component.DeleteOnRemove) @@ -85,20 +137,22 @@ private void OnEmbedRemove(EntityUid uid, EmbeddableProjectileComponent componen } // Land it just coz uhhh yeah - var landEv = new LandEvent(args.User, true); + var landEv = new LandEvent(remover, true); RaiseLocalEvent(uid, ref landEv); _physics.WakeBody(uid, body: physics); // try place it in the user's hand - _hands.TryPickupAnyHand(args.User, uid); + if (remover is {} removerUid) + _hands.TryPickupAnyHand(removerUid, uid); } private void OnEmbedThrowDoHit(EntityUid uid, EmbeddableProjectileComponent component, ThrowDoHitEvent args) { - if (!component.EmbedOnThrow) + if (!component.EmbedOnThrow || + HasComp(args.Target)) return; - Embed(uid, args.Target, null, component); + Embed(uid, args.Target, null, component, args.TargetPart); } private void OnEmbedProjectileHit(EntityUid uid, EmbeddableProjectileComponent component, ref ProjectileHitEvent args) @@ -113,7 +167,7 @@ private void OnEmbedProjectileHit(EntityUid uid, EmbeddableProjectileComponent c } } - private void Embed(EntityUid uid, EntityUid target, EntityUid? user, EmbeddableProjectileComponent component) + private void Embed(EntityUid uid, EntityUid target, EntityUid? user, EmbeddableProjectileComponent component, TargetBodyPart? targetPart = null) { TryComp(uid, out var physics); _physics.SetLinearVelocity(uid, Vector2.Zero, body: physics); @@ -128,8 +182,17 @@ private void Embed(EntityUid uid, EntityUid target, EntityUid? user, EmbeddableP } _audio.PlayPredicted(component.Sound, uid, null); - var ev = new EmbedEvent(user, target); + + component.TargetBodyPart = targetPart; + var ev = new EmbedEvent(user, target, targetPart); RaiseLocalEvent(uid, ref ev); + + if (component.AutoRemoveDuration != 0) + component.AutoRemoveTime = _timing.CurTime + TimeSpan.FromSeconds(component.AutoRemoveDuration); + + component.Target = target; + + Dirty(uid, component); } private void PreventCollision(EntityUid uid, ProjectileComponent component, ref PreventCollideEvent args) @@ -149,12 +212,6 @@ public void SetShooter(EntityUid id, ProjectileComponent component, EntityUid sh Dirty(id, component); } - [Serializable, NetSerializable] - private sealed partial class RemoveEmbeddedProjectileEvent : DoAfterEvent - { - public override DoAfterEvent Clone() => this; - } - /// /// Prevent players with the Pacified status effect from throwing embeddable projectiles. /// @@ -162,6 +219,31 @@ private void OnAttemptPacifiedThrow(Entity ent, r { args.Cancel("pacified-cannot-throw-embed"); } + + private void OnExamined(EntityUid uid, EmbeddableProjectileComponent component, ExaminedEvent args) + { + if (!(component.Target is {} target)) + return; + + var targetIdentity = Identity.Entity(target, EntityManager); + + var loc = component.TargetBodyPart == null + ? Loc.GetString("throwing-examine-embedded", + ("embedded", uid), + ("target", targetIdentity)) + : Loc.GetString("throwing-examine-embedded-part", + ("embedded", uid), + ("target", targetIdentity), + ("targetPart", Loc.GetString($"body-part-{component.TargetBodyPart.ToString()}"))); + + args.PushMarkup(loc); + } + + [Serializable, NetSerializable] + private sealed partial class RemoveEmbeddedProjectileEvent : DoAfterEvent + { + public override DoAfterEvent Clone() => this; + } } [Serializable, NetSerializable] diff --git a/Content.Shared/Throwing/BeforeThrowEvent.cs b/Content.Shared/Throwing/BeforeThrowEvent.cs index 36e7dd758b8..f949c16b0ea 100644 --- a/Content.Shared/Throwing/BeforeThrowEvent.cs +++ b/Content.Shared/Throwing/BeforeThrowEvent.cs @@ -20,3 +20,22 @@ public BeforeThrowEvent(EntityUid itemUid, Vector2 direction, float throwStrengt public bool Cancelled = false; } + +[ByRefEvent] +public struct BeforeGettingThrownEvent +{ + public BeforeGettingThrownEvent(EntityUid itemUid, Vector2 direction, float throwStrength, EntityUid playerUid) + { + ItemUid = itemUid; + Direction = direction; + ThrowStrength = throwStrength; + PlayerUid = playerUid; + } + + public EntityUid ItemUid { get; set; } + public Vector2 Direction { get; } + public float ThrowStrength { get; set;} + public EntityUid PlayerUid { get; } + + public bool Cancelled = false; +} diff --git a/Content.Shared/Throwing/ThrowEvents.cs b/Content.Shared/Throwing/ThrowEvents.cs index 5ea78b862ed..ea13a7dbe40 100644 --- a/Content.Shared/Throwing/ThrowEvents.cs +++ b/Content.Shared/Throwing/ThrowEvents.cs @@ -1,3 +1,5 @@ +using Content.Shared.Targeting; + namespace Content.Shared.Throwing { /// @@ -10,17 +12,19 @@ public abstract class ThrowEvent : HandledEntityEventArgs /// The entity that threw . /// public EntityUid? User { get; } - // End Nyano code. + // End Nyano code. public readonly EntityUid Thrown; public readonly EntityUid Target; public ThrownItemComponent Component; + public TargetBodyPart? TargetPart; - public ThrowEvent(EntityUid? user, EntityUid thrown, EntityUid target, ThrownItemComponent component) //Nyano - Summary: User added. + public ThrowEvent(EntityUid? user, EntityUid thrown, EntityUid target, ThrownItemComponent component, TargetBodyPart? targetPart) //Nyano - Summary: User added. { User = user; //Nyano - Summary: User added. Thrown = thrown; Target = target; Component = component; + TargetPart = targetPart; } } @@ -29,7 +33,7 @@ public ThrowEvent(EntityUid? user, EntityUid thrown, EntityUid target, ThrownIte /// public sealed class ThrowHitByEvent : ThrowEvent { - public ThrowHitByEvent(EntityUid? user, EntityUid thrown, EntityUid target, ThrownItemComponent component) : base(user, thrown, target, component) //Nyano - Summary: User added. + public ThrowHitByEvent(EntityUid? user, EntityUid thrown, EntityUid target, ThrownItemComponent component, TargetBodyPart? targetPart) : base(user, thrown, target, component, targetPart) //Nyano - Summary: User added. { } } @@ -39,7 +43,7 @@ public ThrowHitByEvent(EntityUid? user, EntityUid thrown, EntityUid target, Thro /// public sealed class ThrowDoHitEvent : ThrowEvent { - public ThrowDoHitEvent(EntityUid thrown, EntityUid target, ThrownItemComponent component) : base(null, thrown, target, component) //Nyano - Summary: User added. + public ThrowDoHitEvent(EntityUid thrown, EntityUid target, ThrownItemComponent component, TargetBodyPart? targetPart) : base(null, thrown, target, component, targetPart) //Nyano - Summary: User added. { } } diff --git a/Content.Shared/Throwing/ThrownItemImmuneComponent.cs b/Content.Shared/Throwing/ThrownItemImmuneComponent.cs new file mode 100644 index 00000000000..f2bbd29535a --- /dev/null +++ b/Content.Shared/Throwing/ThrownItemImmuneComponent.cs @@ -0,0 +1,10 @@ +namespace Content.Shared.Throwing +{ + /// + /// This is used for entities that are immune to getting hit by thrown items. + /// + [RegisterComponent] + public sealed partial class ThrownItemImmuneComponent : Component + { + } +} diff --git a/Content.Shared/Throwing/ThrownItemSystem.cs b/Content.Shared/Throwing/ThrownItemSystem.cs index ef28db26727..f607a2dc934 100644 --- a/Content.Shared/Throwing/ThrownItemSystem.cs +++ b/Content.Shared/Throwing/ThrownItemSystem.cs @@ -1,5 +1,6 @@ using System.Linq; using Content.Shared.Administration.Logs; +using Content.Shared.Body.Systems; using Content.Shared.Database; using Content.Shared.Gravity; using Content.Shared.Physics; @@ -23,6 +24,7 @@ public sealed class ThrownItemSystem : EntitySystem [Dependency] private readonly FixtureSystem _fixtures = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedGravitySystem _gravity = default!; + [Dependency] private readonly SharedBodySystem _body = default!; private const string ThrowingFixture = "throw-fixture"; @@ -133,15 +135,20 @@ public void LandComponent(EntityUid uid, ThrownItemComponent thrownItem, Physics /// public void ThrowCollideInteraction(ThrownItemComponent component, EntityUid thrown, EntityUid target) { + if (HasComp(target)) + return; + if (component.Thrower is not null) _adminLogger.Add(LogType.ThrowHit, LogImpact.Low, $"{ToPrettyString(thrown):thrown} thrown by {ToPrettyString(component.Thrower.Value):thrower} hit {ToPrettyString(target):target}."); - if (component.Thrower is not null)// Nyano - Summary: Gotta check if there was a thrower. - RaiseLocalEvent(target, new ThrowHitByEvent(component.Thrower.Value, thrown, target, component), true); // Nyano - Summary: Gotta update for who threw it. + var targetPart = _body.GetRandomBodyPart(target); + + if (component.Thrower is not null)// Nyano - Summary: Gotta check if there was a thrower. + RaiseLocalEvent(target, new ThrowHitByEvent(component.Thrower.Value, thrown, target, component, targetPart), true); // Nyano - Summary: Gotta update for who threw it. else - RaiseLocalEvent(target, new ThrowHitByEvent(null, thrown, target, component), true); // Nyano - Summary: No thrower. - RaiseLocalEvent(thrown, new ThrowDoHitEvent(thrown, target, component), true); + RaiseLocalEvent(target, new ThrowHitByEvent(null, thrown, target, component, targetPart), true); // Nyano - Summary: No thrower. + RaiseLocalEvent(thrown, new ThrowDoHitEvent(thrown, target, component, targetPart), true); } public override void Update(float frameTime) diff --git a/Content.Shared/Tools/Components/ToolTileCompatibleComponent.cs b/Content.Shared/Tools/Components/ToolTileCompatibleComponent.cs index caac41a3def..272a39cfbc3 100644 --- a/Content.Shared/Tools/Components/ToolTileCompatibleComponent.cs +++ b/Content.Shared/Tools/Components/ToolTileCompatibleComponent.cs @@ -18,7 +18,7 @@ public sealed partial class ToolTileCompatibleComponent : Component /// The time it takes to modify the tile. /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public TimeSpan Delay = TimeSpan.FromSeconds(1); + public TimeSpan Delay = TimeSpan.FromSeconds(0.5); /// /// Whether or not the tile being modified must be unobstructed diff --git a/Content.Shared/Traits/Assorted/Systems/TraitStatModifierSystem.cs b/Content.Shared/Traits/Assorted/Systems/TraitStatModifierSystem.cs index 85ecf151dd9..97b88f559e1 100644 --- a/Content.Shared/Traits/Assorted/Systems/TraitStatModifierSystem.cs +++ b/Content.Shared/Traits/Assorted/Systems/TraitStatModifierSystem.cs @@ -2,6 +2,7 @@ using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Traits.Assorted.Components; +using Content.Shared.Damage.Events; using Content.Shared.Weapons.Melee.Events; using Content.Shared.Damage.Components; @@ -17,8 +18,10 @@ public override void Initialize() SubscribeLocalEvent(OnCritStartup); SubscribeLocalEvent(OnDeadStartup); SubscribeLocalEvent(OnStaminaCritStartup); - SubscribeLocalEvent(OnAdrenalineGetDamage); - SubscribeLocalEvent(OnPainToleranceGetDamage); + SubscribeLocalEvent(OnAdrenalineGetMeleeDamage); + SubscribeLocalEvent(OnAdrenalineGetThrowingDamage); + SubscribeLocalEvent(OnPainToleranceGetMeleeDamage); + SubscribeLocalEvent(OnPainToleranceGetThrowingDamage); } private void OnCritStartup(EntityUid uid, CritModifierComponent component, ComponentStartup args) @@ -49,15 +52,35 @@ private void OnStaminaCritStartup(EntityUid uid, StaminaCritModifierComponent co stamina.CritThreshold += component.CritThresholdModifier; } - private void OnAdrenalineGetDamage(EntityUid uid, AdrenalineComponent component, ref GetMeleeDamageEvent args) + private void OnAdrenalineGetMeleeDamage(EntityUid uid, AdrenalineComponent component, ref GetMeleeDamageEvent args) + { + args.Damage *= GetAdrenalineMultiplier(uid, component); + } + + private void OnAdrenalineGetThrowingDamage(EntityUid uid, AdrenalineComponent component, ref GetThrowingDamageEvent args) + { + args.Damage *= GetAdrenalineMultiplier(uid, component); + } + + private float GetAdrenalineMultiplier(EntityUid uid, AdrenalineComponent component) { var modifier = _contests.HealthContest(uid, component.BypassClamp, component.RangeModifier); - args.Damage *= component.Inverse ? 1 / modifier : modifier; + return component.Inverse ? 1 / modifier : modifier; + } + + private void OnPainToleranceGetMeleeDamage(EntityUid uid, PainToleranceComponent component, ref GetMeleeDamageEvent args) + { + args.Damage *= GetPainToleranceMultiplier(uid, component); + } + + private void OnPainToleranceGetThrowingDamage(EntityUid uid, PainToleranceComponent component, ref GetThrowingDamageEvent args) + { + args.Damage *= GetPainToleranceMultiplier(uid, component); } - private void OnPainToleranceGetDamage(EntityUid uid, PainToleranceComponent component, ref GetMeleeDamageEvent args) + private float GetPainToleranceMultiplier(EntityUid uid, PainToleranceComponent component) { var modifier = _contests.StaminaContest(uid, component.BypassClamp, component.RangeModifier); - args.Damage *= component.Inverse ? 1 / modifier : modifier; + return component.Inverse ? 1 / modifier : modifier; } } diff --git a/Content.Shared/Weapons/Melee/MeleeSoundSystem.cs b/Content.Shared/Weapons/Melee/MeleeSoundSystem.cs index 315d752a2c0..6ec55a78846 100644 --- a/Content.Shared/Weapons/Melee/MeleeSoundSystem.cs +++ b/Content.Shared/Weapons/Melee/MeleeSoundSystem.cs @@ -31,11 +31,8 @@ public void PlaySwingSound(EntityUid userUid, EntityUid weaponUid, MeleeWeaponCo /// /// Serves as a lookup key for a hit sound /// A sound can be supplied by the itself to override everything else - public void PlayHitSound(EntityUid targetUid, EntityUid? userUid, string? damageType, SoundSpecifier? hitSoundOverride, MeleeWeaponComponent weaponComponent) + public void PlayHitSound(EntityUid targetUid, EntityUid? userUid, string? damageType, SoundSpecifier? hitSoundOverride, SoundSpecifier? hitSound, SoundSpecifier? noDamageSound) { - var hitSound = weaponComponent.SoundHit; - var noDamageSound = weaponComponent.SoundNoDamage; - var playedSound = false; if (Deleted(targetUid)) diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index 20bf6d5c4e1..7bc817dd24a 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -529,7 +529,7 @@ protected virtual void DoLightAttack(EntityUid user, LightAttackEvent ev, Entity } - _meleeSound.PlayHitSound(target.Value, user, GetHighestDamageSound(modifiedDamage, _protoManager), hitEvent.HitSoundOverride, component); + _meleeSound.PlayHitSound(target.Value, user, GetHighestDamageSound(modifiedDamage, _protoManager), hitEvent.HitSoundOverride, component.SoundHit, component.SoundNoDamage); if (damageResult?.GetTotal() > FixedPoint2.Zero) { @@ -678,7 +678,7 @@ private bool DoHeavyAttack(EntityUid user, HeavyAttackEvent ev, EntityUid meleeU if (entities.Count != 0) { var target = entities.First(); - _meleeSound.PlayHitSound(target, user, GetHighestDamageSound(appliedDamage, _protoManager), hitEvent.HitSoundOverride, component); + _meleeSound.PlayHitSound(target, user, GetHighestDamageSound(appliedDamage, _protoManager), hitEvent.HitSoundOverride, component.SoundHit, component.SoundNoDamage); } if (appliedDamage.GetTotal() > FixedPoint2.Zero) diff --git a/Content.Shared/WhiteDream/BloodCult/Items/CultItemSystem.cs b/Content.Shared/WhiteDream/BloodCult/Items/CultItemSystem.cs index 1dbbb769aad..a16d8dc9326 100644 --- a/Content.Shared/WhiteDream/BloodCult/Items/CultItemSystem.cs +++ b/Content.Shared/WhiteDream/BloodCult/Items/CultItemSystem.cs @@ -4,10 +4,12 @@ using Content.Shared.Interaction; using Content.Shared.Inventory.Events; using Content.Shared.Popups; +using Content.Shared.Projectiles; using Content.Shared.Stunnable; using Content.Shared.Throwing; using Content.Shared.Weapons.Melee.Events; using Content.Shared.WhiteDream.BloodCult.BloodCultist; +using Robust.Shared.Network; namespace Content.Shared.WhiteDream.BloodCult.Items; @@ -16,11 +18,12 @@ public sealed class CultItemSystem : EntitySystem [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedStunSystem _stun = default!; + [Dependency] private readonly INetManager _net = default!; public override void Initialize() { SubscribeLocalEvent(OnActivate); - SubscribeLocalEvent(OnBeforeThrow); + SubscribeLocalEvent(OnBeforeGettingThrown); SubscribeLocalEvent(OnEquipAttempt); SubscribeLocalEvent(OnMeleeAttempt); SubscribeLocalEvent(OnBeforeBlocking); @@ -28,20 +31,22 @@ public override void Initialize() private void OnActivate(Entity item, ref ActivateInWorldEvent args) { - if (CanUse(args.User)) + if (CanUse(args.User) || + // Allow non-cultists to remove embedded cultist weapons and getting knocked down afterwards on pickup + (TryComp(item.Owner, out var embeddable) && embeddable.Target != null)) return; args.Handled = true; KnockdownAndDropItem(item, args.User, Loc.GetString("cult-item-component-generic")); } - private void OnBeforeThrow(Entity item, ref BeforeThrowEvent args) + private void OnBeforeGettingThrown(Entity item, ref BeforeGettingThrownEvent args) { if (CanUse(args.PlayerUid)) return; args.Cancelled = true; - KnockdownAndDropItem(item, args.PlayerUid, Loc.GetString("cult-item-component-throw-fail")); + KnockdownAndDropItem(item, args.PlayerUid, Loc.GetString("cult-item-component-throw-fail"), true); } private void OnEquipAttempt(Entity item, ref BeingEquippedAttemptEvent args) @@ -71,9 +76,14 @@ private void OnBeforeBlocking(Entity item, ref BeforeBlocking KnockdownAndDropItem(item, args.User, Loc.GetString("cult-item-component-block-fail")); } - private void KnockdownAndDropItem(Entity item, EntityUid user, string message) + // serverOnly is a very rough hack to make sure OnBeforeGettingThrown (that is only run server-side) can + // show the popup while not causing several popups to show up with PopupEntity. + private void KnockdownAndDropItem(Entity item, EntityUid user, string message, bool serverOnly = false) { - _popup.PopupPredicted(message, item, user); + if (serverOnly) + _popup.PopupEntity(message, item, user); + else + _popup.PopupPredicted(message, item, user); _stun.TryKnockdown(user, item.Comp.KnockdownDuration, true); _hands.TryDrop(user); } diff --git a/Resources/Locale/en-US/body/body-parts.ftl b/Resources/Locale/en-US/body/body-parts.ftl new file mode 100644 index 00000000000..5dec76bb714 --- /dev/null +++ b/Resources/Locale/en-US/body/body-parts.ftl @@ -0,0 +1,19 @@ +# Locale values for TargetBodyPart + +body-part-Head = head +body-part-Torso = torso +body-part-Groin = groin +body-part-LeftArm = left arm +body-part-LeftHand = left hand +body-part-RightArm = right arm +body-part-RightHand = right hand +body-part-LeftLeg = left leg +body-part-LeftFoot = left foot +body-part-RightLeg = right leg +body-part-RightFoot = right foot + +body-part-Hands = hands +body-part-Arms = arms +body-part-Legs = legs +body-part-Feet = feet +body-part-All = body diff --git a/Resources/Locale/en-US/damage/damage-examine.ftl b/Resources/Locale/en-US/damage/damage-examine.ftl index 3a71fc72620..f6cfe75fa76 100644 --- a/Resources/Locale/en-US/damage/damage-examine.ftl +++ b/Resources/Locale/en-US/damage/damage-examine.ftl @@ -12,4 +12,7 @@ damage-examine = It does the following damage: damage-examine-type = It does the following [color=cyan]{$type}[/color] damage: damage-value = - [color=red]{$amount}[/color] units of [color=yellow]{$type}[/color]. -damage-melee-heavy-stamina-cost = A [color=cyan]{$type}[/color] costs [color=orange]{$cost}[/color] [color=yellow]Stamina[/color]. +damage-stamina-cost = A [color=cyan]{$type}[/color] costs [color=orange]{$cost}[/color] [color=yellow]Stamina[/color]. + +damage-examine-embeddable-harmful = It [color=cyan]embeds[/color] when thrown, doing damage over time. +damage-examine-embeddable = It [color=cyan]embeds[/color] harmlessly when thrown. diff --git a/Resources/Locale/en-US/damage/stamina.ftl b/Resources/Locale/en-US/damage/stamina.ftl index 0d14a52c1ee..657f32cb651 100644 --- a/Resources/Locale/en-US/damage/stamina.ftl +++ b/Resources/Locale/en-US/damage/stamina.ftl @@ -1 +1,3 @@ melee-stamina = Not enough stamina + +throw-no-stamina = You don't have enough stamina to throw the {$item}! diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index 74eb67bdaf2..10e13ae80ad 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -144,24 +144,24 @@ trait-description-GlassJaw = trait-name-HighAdrenaline = High Adrenaline trait-description-HighAdrenaline = Whether by natural causes, genetic or bionic augmentation, you have a more potent adrenal gland. - When injured, your melee attacks deal up to 10% more damage, in addition to the natural bonuses from adrenaline. - The standard adrenaline bonuses to melee damage are up to a 20% increase. + When injured, your melee/throwing attacks deal up to 10% more damage, in addition to the natural bonuses from adrenaline. + The standard adrenaline bonuses to melee/throwing damage are up to a 20% increase. trait-name-AdrenalDysfunction = Adrenal Dysfunction trait-description-AdrenalDysfunction = Your adrenal gland is completely nonfunctional, or potentially missing outright. - Your melee attacks do not benefit from Adrenaline when injured. - The standard adrenaline bonuses to melee damage are up to a 20% increase. + Your melee/throwing attacks do not benefit from Adrenaline when injured. + The standard adrenaline bonuses to melee/throwing damage are up to a 20% increase. trait-name-Masochism = Masochism trait-description-Masochism = Deriving enjoyment from your own pain, you are not as inhibited by it as others. - You ignore the first 10% of stamina damage penalties to your melee attacks. + You ignore the first 10% of stamina damage penalties to your melee/throwing attacks. trait-name-LowPainTolerance = Low Pain Tolerance trait-description-LowPainTolerance = Your tolerance for pain is far below average, and its effects are more inhibiting. - Your melee damage is penalized by up to an additional 15% when taking stamina damage. + Your melee/throwing damage is penalized by up to an additional 15% when taking stamina damage. trait-name-MartialArtist = Martial Artist trait-description-MartialArtist = diff --git a/Resources/Locale/en-US/weapons/throwing/throwing.ftl b/Resources/Locale/en-US/weapons/throwing/throwing.ftl new file mode 100644 index 00000000000..c7d2a8f663f --- /dev/null +++ b/Resources/Locale/en-US/weapons/throwing/throwing.ftl @@ -0,0 +1,6 @@ +throwing-falloff = The {$item} falls out of you! +throwing-embed-falloff = The {$item} falls out of you! +throwing-embed-remove-alert-owner = {$other} is removing the {$item} stuck on you! + +throwing-examine-embedded = {CAPITALIZE(OBJECT($embedded))} {CONJUGATE-BE($embedded)} [color=teal]embedded[/color] in [bold]{THE($target)}[/bold]. +throwing-examine-embedded-part = {CAPITALIZE(OBJECT($embedded))} {CONJUGATE-BE($embedded)} [color=teal]embedded[/color] in [bold]{THE($target)}[/bold]'s [color=red]{$targetPart}[/color]. diff --git a/Resources/Prototypes/Body/Parts/base.yml b/Resources/Prototypes/Body/Parts/base.yml index 7b90b09794f..356b9618560 100644 --- a/Resources/Prototypes/Body/Parts/base.yml +++ b/Resources/Prototypes/Body/Parts/base.yml @@ -66,6 +66,11 @@ - type: ContainerContainer containers: torso_slot: !type:ContainerSlot {} + - type: DamageOtherOnHit + damage: + types: + Blunt: 11 + staminaCost: 12 - type: entity id: BaseHead @@ -82,6 +87,11 @@ - type: Tag tags: - Head + - type: DamageOtherOnHit + damage: + types: + Blunt: 5 + staminaCost: 5 - type: entity id: BaseLeftArm @@ -93,6 +103,11 @@ partType: Arm symmetry: Left toolName: "a left arm" + - type: DamageOtherOnHit + damage: + types: + Blunt: 7 + staminaCost: 7 - type: entity id: BaseRightArm @@ -104,6 +119,11 @@ partType: Arm symmetry: Right toolName: "a right arm" + - type: DamageOtherOnHit + damage: + types: + Blunt: 7 + staminaCost: 7 - type: entity id: BaseLeftHand @@ -115,6 +135,10 @@ partType: Hand symmetry: Left toolName: "a left hand" + - type: DamageOtherOnHit + damage: + types: + Blunt: 3 - type: entity id: BaseRightHand @@ -126,6 +150,10 @@ partType: Hand symmetry: Right toolName: "a right hand" + - type: DamageOtherOnHit + damage: + types: + Blunt: 3 - type: entity id: BaseLeftLeg @@ -138,6 +166,11 @@ symmetry: Left toolName: "a left leg" - type: MovementBodyPart + - type: DamageOtherOnHit + damage: + types: + Blunt: 8 + staminaCost: 9 - type: entity id: BaseRightLeg @@ -150,6 +183,11 @@ symmetry: Right toolName: "a right leg" - type: MovementBodyPart + - type: DamageOtherOnHit + damage: + types: + Blunt: 8 + staminaCost: 9 - type: entity id: BaseLeftFoot @@ -161,6 +199,10 @@ partType: Foot symmetry: Left toolName: "a left foot" + - type: DamageOtherOnHit + damage: + types: + Blunt: 4 - type: entity id: BaseRightFoot @@ -172,5 +214,9 @@ partType: Foot symmetry: Right toolName: "a right foot" + - type: DamageOtherOnHit + damage: + types: + Blunt: 4 # Shitmed Change End diff --git a/Resources/Prototypes/DeltaV/Entities/Objects/Consumable/Food/Containers/lunchbox.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Consumable/Food/Containers/lunchbox.yml index c7aae33c76d..94e898955a7 100644 --- a/Resources/Prototypes/DeltaV/Entities/Objects/Consumable/Food/Containers/lunchbox.yml +++ b/Resources/Prototypes/DeltaV/Entities/Objects/Consumable/Food/Containers/lunchbox.yml @@ -27,9 +27,14 @@ - type: MeleeWeapon damage: types: - Blunt: 2 + Blunt: 5.5 + heavyRateModifier: 1.25 + heavyStaminaCost: 5 + angle: 80.5 soundHit: path: "/Audio/Weapons/click.ogg" + - type: DamageOtherOnHit + staminaCost: 6 - type: StaticPrice price: 10 diff --git a/Resources/Prototypes/DeltaV/Entities/Objects/Devices/Medical/portafib.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Devices/Medical/portafib.yml index a02023f4a8e..0f3095663d3 100644 --- a/Resources/Prototypes/DeltaV/Entities/Objects/Devices/Medical/portafib.yml +++ b/Resources/Prototypes/DeltaV/Entities/Objects/Devices/Medical/portafib.yml @@ -43,6 +43,13 @@ - type: GuideHelp guides: - Medical Doctor + - type: DamageOtherOnHit + damage: + types: + Blunt: 6.5 + staminaCost: 8 + soundHit: + path: /Audio/Weapons/smash.ogg - type: entity id: PortafibEmpty diff --git a/Resources/Prototypes/Entities/Clothing/Head/welding.yml b/Resources/Prototypes/Entities/Clothing/Head/welding.yml index c0ae440a56e..4854df983ba 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/welding.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/welding.yml @@ -23,6 +23,13 @@ - type: HideLayerClothing slots: - Snout + - type: DamageOtherOnHit + damage: + types: + Blunt: 7 + staminaCost: 5 + soundHit: + collection: MetalThud - type: entity parent: WeldingMaskBase diff --git a/Resources/Prototypes/Entities/Clothing/Neck/pins.yml b/Resources/Prototypes/Entities/Clothing/Neck/pins.yml index 0054a3645c7..b868c866998 100644 --- a/Resources/Prototypes/Entities/Clothing/Neck/pins.yml +++ b/Resources/Prototypes/Entities/Clothing/Neck/pins.yml @@ -7,6 +7,11 @@ components: - type: Item size: Tiny + - type: DamageOtherOnHit + damage: + types: + Blunt: 1 + staminaCost: 1 - type: entity parent: ClothingNeckPinBase @@ -151,7 +156,7 @@ clothingVisuals: neck: - state: trans-equipped - + - type: entity parent: ClothingNeckPinBase id: ClothingNeckAutismPin diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml index c18e5d6ab60..323895491af 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/base_clothingouter.yml @@ -151,6 +151,13 @@ coefficient: 0.75 # 25% - type: ClothingRequiredStepTriggerImmune slots: WITHOUT_POCKET + - type: DamageOtherOnHit + damage: + types: + Blunt: 19 + staminaCost: 44 + soundHit: + collection: MetalThud - type: entity abstract: true @@ -199,4 +206,4 @@ id: ClothingOuterBaseMedium components: - type: Item - size: Huge \ No newline at end of file + size: Huge diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml index 034064b9439..13fbc087164 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml @@ -32,6 +32,14 @@ difficulty: 2 recipes: - ClothingShoesBootsMag + - type: DamageOtherOnHit + damage: + types: + Blunt: 9 + staminaCost: 11.5 + soundHit: + path: /Audio/Weapons/smash.ogg + - type: entity parent: ClothingShoesBootsMag @@ -59,6 +67,11 @@ price: 750 - type: StealTarget stealGroup: ClothingShoesBootsMagAdv + - type: DamageOtherOnHit + damage: + types: + Blunt: 13 + staminaCost: 15 - type: entity parent: ClothingShoesBootsMag @@ -131,6 +144,13 @@ - type: Tag tags: - WhitelistChameleon + - type: DamageOtherOnHit + damage: + types: + Blunt: 20 + staminaCost: 25 + soundHit: + collection: MetalThud - type: entity id: ActionBaseToggleMagboots diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml index 319c8a634ed..728ca962f9f 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_cans.yml @@ -53,6 +53,12 @@ damage: types: Blunt: 0 + - type: DamageOtherOnHit + damage: + types: + Blunt: 3 + soundHit: + path: /Audio/SimpleStation14/Items/Handling/drinkglass_drop.ogg - type: Tool qualities: - Rolling diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_flasks.yml b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_flasks.yml index 0b2af4c97f4..454cd9b025a 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_flasks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Drinks/drinks_flasks.yml @@ -9,6 +9,10 @@ Steel: 300 - type: FitsInDispenser solution: drink + - type: DamageOtherOnHit + damage: + types: + Blunt: 5 - type: entity parent: FlaskBase diff --git a/Resources/Prototypes/Entities/Objects/Devices/holoprojectors.yml b/Resources/Prototypes/Entities/Objects/Devices/holoprojectors.yml index b7ad8ddd6a8..8ebcc7dc167 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/holoprojectors.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/holoprojectors.yml @@ -24,6 +24,19 @@ - type: Tag tags: - HolosignProjector + - type: MeleeWeapon + wideAnimationRotation: 90 + attackRate: 0.8 + damage: + types: + Blunt: 6.5 + bluntStaminaDamageFactor: 2 + heavyRateModifier: 0.9 + maxTargets: 1 + angle: 20 + soundHit: + collection: MetalThud + - type: DamageOtherOnHit - type: entity parent: Holoprojector diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index e32271f4cd9..2dbcfc60ab3 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -108,6 +108,10 @@ - type: LanguageKnowledge speaks: [TauCetiBasic, RobotTalk] understands: [TauCetiBasic, RobotTalk] + - type: DamageOtherOnHit + damage: + types: + Blunt: 4 - type: entity parent: BasePDA @@ -504,7 +508,7 @@ parent: BasePDA id: CaptainPDA name: captain PDA - description: Surprisingly no different from your PDA. + description: Surprisingly no different from your PDA... Wait, it's a fair bit heavy to hold. components: - type: Pda id: CaptainIDCard @@ -519,6 +523,13 @@ borderColor: "#7C5D00" - type: Icon state: pda-captain + - type: DamageOtherOnHit + damage: + types: + Blunt: 8 + staminaCost: 5 + soundHit: + collection: MetalThud - type: entity parent: BasePDA diff --git a/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_string.yml b/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_string.yml index 730d532930b..e8cf42f68d8 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_string.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_string.yml @@ -27,6 +27,8 @@ heavyDamageBaseModifier: 1.2 heavyStaminaCost: 7.5 angle: 75 + - type: DamageOtherOnHit + staminaCost: 8 - type: Item size: Normal sprite: Objects/Fun/Instruments/eguitar.rsi @@ -69,6 +71,8 @@ heavyDamageBaseModifier: 1.2 heavyStaminaCost: 7.5 angle: 75 + - type: DamageOtherOnHit + staminaCost: 8 - type: Item size: Normal sprite: Objects/Fun/Instruments/bassguitar.rsi @@ -112,6 +116,8 @@ heavyDamageBaseModifier: 1.2 heavyStaminaCost: 10 angle: 160 + - type: DamageOtherOnHit + staminaCost: 8 - type: Wieldable - type: IncreaseDamageOnWield damage: @@ -179,6 +185,10 @@ damage: types: Blunt: 20 + - type: DamageOnLand + damage: + types: + Blunt: 20 - type: MeleeWeapon range: 1.5 wideAnimationRotation: 45 @@ -190,6 +200,8 @@ heavyDamageBaseModifier: 1.2 heavyStaminaCost: 10 angle: 75 + - type: DamageOtherOnHit + staminaCost: 7 - type: IncreaseDamageOnWield damage: types: @@ -236,6 +248,8 @@ heavyDamageBaseModifier: 1.2 heavyStaminaCost: 7.5 angle: 75 + - type: DamageOtherOnHit + staminaCost: 8 - type: entity parent: BaseHandheldInstrument diff --git a/Resources/Prototypes/Entities/Objects/Fun/bike_horn.yml b/Resources/Prototypes/Entities/Objects/Fun/bike_horn.yml index 7c69aa09013..36c8563bce1 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/bike_horn.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/bike_horn.yml @@ -39,6 +39,7 @@ damage: types: Blunt: 0 + - type: DamageOtherOnHit - type: Tool qualities: - Honking diff --git a/Resources/Prototypes/Entities/Objects/Fun/pai.yml b/Resources/Prototypes/Entities/Objects/Fun/pai.yml index 5c0bbc84454..0adbc492308 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/pai.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/pai.yml @@ -88,6 +88,10 @@ - Elyran - RobotTalk - Sign # It's intentional that they don't "Speak" sign language. + - type: DamageOtherOnHit + damage: + types: + Blunt: 3 - type: entity parent: PersonalAI diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index fc771414b4a..987c56f0a11 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -35,6 +35,7 @@ damage: types: Blunt: 0 + - type: DamageOtherOnHit - type: PhysicalComposition materialComposition: Cloth: 100 @@ -1964,4 +1965,4 @@ components: - type: Sprite sprite: Objects/Fun/toys.rsi - state: shadowkin \ No newline at end of file + state: shadowkin diff --git a/Resources/Prototypes/Entities/Objects/Materials/shards.yml b/Resources/Prototypes/Entities/Objects/Materials/shards.yml index 28f206a4f05..e90aafa4142 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/shards.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/shards.yml @@ -43,11 +43,12 @@ mask: - ItemMask - type: DamageOtherOnHit - damage: - types: - Slash: 2 + meleeDamageMultiplier: 2 - type: EmbeddableProjectile sound: /Audio/Weapons/bladeslice.ogg + removalTime: 2.5 + autoRemoveDuration: 30 + - type: EmbedPassiveDamage - type: Tag tags: - Trash @@ -168,6 +169,10 @@ damage: types: Slash: 5.5 + - type: EmbedPassiveDamage + damage: + types: + Slash: 0.15 - type: WelderRefinable refineResult: - id: SheetGlass1 diff --git a/Resources/Prototypes/Entities/Objects/Misc/briefcases.yml b/Resources/Prototypes/Entities/Objects/Misc/briefcases.yml index 760a0bafb68..bd1b7c8b24e 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/briefcases.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/briefcases.yml @@ -21,6 +21,8 @@ heavyDamageBaseModifier: 2 heavyStaminaCost: 5 maxTargets: 8 + - type: DamageOtherOnHit + staminaCost: 5 - type: Tag tags: - Briefcase diff --git a/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml b/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml index a6cbe9a6e7e..d926609d3ff 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/broken_bottle.yml @@ -25,7 +25,7 @@ - type: DamageOtherOnHit damage: types: - Slash: 2 + Slash: 4 - type: Tag tags: - Trash diff --git a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml index 6194be48488..f3f86a3bd3c 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml @@ -50,6 +50,8 @@ maxTargets: 6 soundHit: path: /Audio/Weapons/smash.ogg + - type: DamageOtherOnHit + staminaCost: 9 - type: Tool qualities: - Rolling diff --git a/Resources/Prototypes/Entities/Objects/Misc/paper.yml b/Resources/Prototypes/Entities/Objects/Misc/paper.yml index d30fead363c..cc12965a953 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/paper.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/paper.yml @@ -339,12 +339,6 @@ path: /Audio/Medical/Surgery/retractor1.ogg endSound: path: /Audio/Medical/Surgery/hemostat1.ogg - -- type: entity - parent: Pen - id: PenEmbeddable - abstract: true - components: - type: EmbeddableProjectile offset: 0.3,0.0 removalTime: 0.0 @@ -355,6 +349,16 @@ types: Piercing: 3 +- type: entity + parent: Pen + id: PenEmbeddable + abstract: true + components: + - type: DamageOtherOnHit + damage: + types: + Piercing: 5 + #TODO: I want the luxury pen to write a cool font like Merriweather in the future. - type: entity @@ -408,6 +412,10 @@ - type: Sprite sprite: Objects/Misc/bureaucracy.rsi state: pen_cap + - type: DamageOtherOnHit + damage: + types: + Piercing: 8 - type: entity name: CentCom pen diff --git a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml index 99f9ccaa874..3d3675dd33d 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml @@ -12,9 +12,10 @@ - type: DamageOtherOnHit damage: types: - Blunt: 2 - - type: EmbeddableProjectile - sound: /Audio/Weapons/star_hit.ogg + Blunt: 5.5 + staminaCost: 5 + soundHit: + collection: MetalThud - type: Stack count: 1 - type: Tag @@ -67,9 +68,9 @@ - type: DamageOtherOnHit damage: types: - Blunt: 5 #Metal floor tiles deal more damage than standard - - type: EmbeddableProjectile - sound: /Audio/Weapons/block_metal1.ogg + Blunt: 9.5 #Metal floor tiles deal more damage than standard + staminaCost: 6 + soundHit: /Audio/Weapons/block_metal1.ogg - type: entity name: steel dark checker tile diff --git a/Resources/Prototypes/Entities/Objects/Misc/utensils.yml b/Resources/Prototypes/Entities/Objects/Misc/utensils.yml index 7dee744ff7d..f4aa9ac9678 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/utensils.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/utensils.yml @@ -59,6 +59,14 @@ Piercing: 5 - type: Tweezers # Forks are better than spoons speed: 0.35 + - type: DamageOtherOnHit + staminaCost: 2.5 + - type: EmbeddableProjectile + removalTime: 0.5 + autoRemoveDuration: 10 + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 180 - type: entity parent: UtensilBasePlastic @@ -96,6 +104,9 @@ damage: types: Blunt: 1 + - type: DamageOtherOnHit + - type: ThrowingAngle + angle: 180 - type: Shovel speedModifier: 0.1 # you can try @@ -154,5 +165,8 @@ damage: types: Blunt: 2 + - type: DamageOtherOnHit + - type: ThrowingAngle + angle: 180 - type: Shovel speedModifier: 0.05 # nah diff --git a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml index 316294787b3..d281f7bdee9 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml @@ -23,6 +23,10 @@ heavyDamageBaseModifier: 1.2 maxTargets: 5 angle: 100 + - type: DamageOtherOnHit + staminaCost: 5 + - type: ThrowingAngle + angle: 135 - type: Item sprite: Objects/Tools/Hydroponics/hoe.rsi @@ -49,6 +53,8 @@ heavyDamageBaseModifier: 1.2 maxTargets: 1 angle: 20 + - type: DamageOtherOnHit + staminaCost: 5 - type: Item sprite: Objects/Tools/Hydroponics/clippers.rsi storedRotation: -90 @@ -84,6 +90,13 @@ heavyStaminaCost: 5 maxTargets: 1 angle: 120 + - type: DamageOtherOnHit + staminaCost: 7 + - type: EmbeddableProjectile + sound: /Audio/Weapons/star_hit.ogg + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 315 - type: Item size: Normal - type: Clothing @@ -116,6 +129,12 @@ Slash: 10 heavyDamageBaseModifier: 1.2 heavyStaminaCost: 5 + - type: DamageOtherOnHit + meleeDamageMultiplier: 1.5 + staminaCost: 6.5 + - type: EmbeddableProjectile + sound: /Audio/Weapons/star_hit.ogg + - type: EmbedPassiveDamage - type: Item sprite: Objects/Tools/Hydroponics/hatchet.rsi - type: BoneSaw @@ -149,6 +168,10 @@ angle: 80 soundHit: collection: MetalThud + - type: DamageOtherOnHit + staminaCost: 5 + - type: ThrowingAngle + angle: 45 - type: Item sprite: Objects/Tools/Hydroponics/spade.rsi - type: Shovel diff --git a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml index a3a26299bf3..d39ae845385 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml @@ -21,6 +21,11 @@ angle: 180 soundHit: collection: MetalThud + - type: DamageOtherOnHit + damage: + types: + Blunt: 6 + staminaCost: 8 - type: Spillable solution: absorbed - type: Wieldable @@ -72,6 +77,11 @@ angle: 180 soundHit: collection: MetalThud + - type: DamageOtherOnHit + damage: + types: + Blunt: 6 + staminaCost: 8 - type: Spillable solution: absorbed - type: Wieldable diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml index 4a61074a8d5..1a905685934 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml @@ -42,6 +42,13 @@ - type: GuideHelp guides: - Medical Doctor + - type: DamageOtherOnHit + damage: + types: + Blunt: 16 + staminaCost: 22.5 + soundHit: + path: /Audio/Weapons/smash.ogg - type: entity id: Defibrillator diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml index ab338b9da95..a9d6bbb20f5 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml @@ -38,6 +38,10 @@ - HighRiskItem - type: StealTarget stealGroup: HandheldCrewMonitor + - type: DamageOtherOnHit + damage: + types: + Blunt: 5 - type: entity id: HandheldCrewMonitorEmpty @@ -47,7 +51,7 @@ - type: ItemSlots slots: cell_slot: - name: power-cell-slot-component-slot-name-default + name: power-cell-slot-component-slot-name-default - type: entity id: SpyCrewMonitor @@ -73,7 +77,7 @@ - type: ItemSlots slots: cell_slot: - name: power-cell-slot-component-slot-name-default + name: power-cell-slot-component-slot-name-default - type: entity id: SyndiCrewMonitor @@ -97,5 +101,4 @@ - type: ItemSlots slots: cell_slot: - name: power-cell-slot-component-slot-name-default - \ No newline at end of file + name: power-cell-slot-component-slot-name-default diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml index c01aaa84a9d..47e633276b8 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml @@ -37,6 +37,10 @@ - type: GuideHelp guides: - Medical Doctor + - type: DamageOtherOnHit + damage: + types: + Blunt: 5 - type: entity id: HandheldHealthAnalyzer diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml index 638b194c343..79831cab648 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml @@ -33,6 +33,9 @@ Heat: 5 soundHit: path: /Audio/Effects/lightburn.ogg + - type: DamageOtherOnHit + - type: ThrowingAngle + angle: 45 - type: SurgeryTool startSound: path: /Audio/Medical/Surgery/cautery1.ogg @@ -101,6 +104,15 @@ angle: 20 soundHit: path: /Audio/Items/drill_hit.ogg + - type: DamageOtherOnHit + damage: + types: + Piercing: 11 + staminaCost: 8 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 90 - type: StaticPrice price: 40 - type: SurgeryTool @@ -142,6 +154,12 @@ angle: 20 soundHit: path: /Audio/Weapons/bladeslice.ogg + - type: DamageOtherOnHit + staminaCost: 5 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 90 - type: SurgeryTool startSound: path: /Audio/Medical/Surgery/scalpel1.ogg @@ -248,6 +266,27 @@ - type: Item sprite: Objects/Specific/Medical/Surgery/retractor.rsi storedRotation: 90 + - type: MeleeWeapon + wideAnimationRotation: 90 + attackRate: 1.25 + range: 1.4 + damage: + types: + Slash: 2.5 + Blunt: 2.0 + heavyRateModifier: 0.8 + heavyDamageBaseModifier: 1.25 + heavyStaminaCost: 4 + maxTargets: 1 + angle: 20 + soundHit: + path: /Audio/Weapons/bladeslice.ogg + - type: DamageOtherOnHit + staminaCost: 5 + - type: ThrowingAngle + angle: 315 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage - type: SurgeryTool startSound: path: /Audio/Medical/Surgery/retractor1.ogg @@ -283,6 +322,8 @@ types: Slash: 6.5 Heat: 1 + - type: ThrowingAngle + angle: 270 - type: Hemostat speed: 1.5 - type: Retractor @@ -306,6 +347,26 @@ - type: Item sprite: Objects/Specific/Medical/Surgery/hemostat.rsi storedRotation: 90 + - type: MeleeWeapon + wideAnimationRotation: 90 + attackRate: 1.25 + range: 1.4 + damage: + types: + Slash: 6 + heavyRateModifier: 0.8 + heavyDamageBaseModifier: 1.25 + heavyStaminaCost: 4.5 + maxTargets: 1 + angle: 20 + soundHit: + path: /Audio/Weapons/bladeslice.ogg + - type: DamageOtherOnHit + staminaCost: 5 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 35 - type: SurgeryTool startSound: path: /Audio/Medical/Surgery/retractor1.ogg @@ -373,6 +434,14 @@ heavyStaminaCost: 20 maxTargets: 8 angle: 20 + soundHit: + path: /Audio/Weapons/bladeslice.ogg + - type: DamageOtherOnHit + staminaCost: 10 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 0 - type: BoneSaw # --No melee for regular saw because have you ever seen someone use a band saw as a weapon? It's dumb.-- # No, I'm going to saw through your bones. @@ -401,6 +470,8 @@ angle: 20 soundHit: path: /Audio/Weapons/bladeslice.ogg + - type: ThrowingAngle + angle: 90 - type: Tool qualities: - Sawing @@ -435,6 +506,13 @@ angle: 360 soundHit: path: /Audio/Items/drill_hit.ogg + - type: DamageOtherOnHit + damage: + types: + Slash: 10 + staminaCost: 14 + - type: ThrowingAngle + angle: 90 - type: Tool qualities: - Sawing @@ -471,6 +549,13 @@ angle: 360 soundHit: path: /Audio/Items/drill_hit.ogg + - type: DamageOtherOnHit + damage: + types: + Slash: 12 + staminaCost: 14 + - type: ThrowingAngle + angle: 90 - type: Tool qualities: - Sawing diff --git a/Resources/Prototypes/Entities/Objects/Specific/Service/barber.yml b/Resources/Prototypes/Entities/Objects/Specific/Service/barber.yml index 451230bbf1f..2a09c9ede91 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Service/barber.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Service/barber.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity id: BarberScissors name: barber scissors description: is able to reshape the hairstyle of any crew cut to your liking. @@ -28,3 +28,9 @@ Piercing: 6 soundHit: path: "/Audio/Weapons/bladeslice.ogg" + - type: DamageOtherOnHit + - type: EmbeddableProjectile + sound: /Audio/Weapons/star_hit.ogg + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 225 diff --git a/Resources/Prototypes/Entities/Objects/Specific/Service/vending_machine_restock.yml b/Resources/Prototypes/Entities/Objects/Specific/Service/vending_machine_restock.yml index 953c05f107e..478aec4ce49 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Service/vending_machine_restock.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Service/vending_machine_restock.yml @@ -20,6 +20,11 @@ path: /Audio/Weapons/genhit2.ogg soundSwing: path: /Audio/Weapons/punchmiss.ogg + - type: DamageOtherOnHit + damage: + types: + Blunt: 11 + staminaCost: 15 - type: Item size: Normal - type: Damageable diff --git a/Resources/Prototypes/Entities/Objects/Tools/emag.yml b/Resources/Prototypes/Entities/Objects/Tools/emag.yml index 0117d44d6d7..4461d952c03 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/emag.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/emag.yml @@ -12,6 +12,13 @@ - type: Item sprite: Objects/Tools/emag.rsi storedRotation: -90 + - type: DamageOtherOnHit # An emag has sharp edges + damage: + types: + Slash: 5 + - type: EmbeddableProjectile + sound: /Audio/Weapons/star_hit.ogg + - type: EmbedPassiveDamage - type: entity parent: EmagUnlimited diff --git a/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml b/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml index cbb5dfee0a1..56d4bba1def 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity name: flashlight parent: BaseItem id: FlashlightLantern @@ -62,6 +62,8 @@ Blunt: 6 soundHit: collection: MetalThud + - type: DamageOtherOnHit + staminaCost: 3.5 - type: Item sprite: Objects/Tools/flashlight.rsi storedRotation: -90 diff --git a/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml b/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml index 8590a32a6a6..eb5e96e22d1 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/gas_tanks.yml @@ -44,6 +44,10 @@ heavyStaminaCost: 10 maxTargets: 3 angle: 100 + soundHit: + path: /Audio/Weapons/smash.ogg + - type: DamageOtherOnHit + staminaCost: 8 - type: PhysicalComposition materialComposition: Steel: 185 @@ -111,6 +115,8 @@ damage: types: Blunt: 5 + - type: DamageOtherOnHit + staminaCost: 3.5 - type: PhysicalComposition materialComposition: Steel: 100 @@ -179,6 +185,8 @@ damage: types: Blunt: 7.5 + - type: DamageOtherOnHit + staminaCost: 5 - type: entity parent: DoubleEmergencyOxygenTank diff --git a/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml b/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml index 936e832224f..1ef7dfdc3fb 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/jaws_of_life.yml @@ -58,6 +58,10 @@ angle: 20 soundHit: collection: MetalThud + - type: DamageOtherOnHit + staminaCost: 7 + - type: ThrowingAngle + angle: 90 - type: ReverseEngineering # Delta difficulty: 3 recipes: @@ -102,3 +106,4 @@ types: Blunt: 12 Slash: 2 + - type: DamageOtherOnHit diff --git a/Resources/Prototypes/Entities/Objects/Tools/lighters.yml b/Resources/Prototypes/Entities/Objects/Tools/lighters.yml index 8a832c746e5..52b224a61ef 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/lighters.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/lighters.yml @@ -16,6 +16,17 @@ activatedDamage: types: Heat: 1 + activatedSoundOnHit: + path: /Audio/Weapons/Guns/Hits/energy_meat1.ogg + params: + variation: 0.250 + volume: -15 + activatedSoundOnHitNoDamage: + path: /Audio/Weapons/Guns/Hits/energy_meat1.ogg + params: + variation: 0.250 + volume: -17 + - type: ItemToggleDamageOtherOnHit - type: ItemToggleSize activatedSize: Small - type: ItemToggleHot @@ -78,6 +89,7 @@ damage: types: Blunt: 0 + - type: DamageOtherOnHit - type: Welder fuelConsumption: 0.01 fuelLitCost: 0.1 @@ -159,6 +171,17 @@ activatedDamage: types: Heat: 1 + activatedSoundOnHit: + path: /Audio/Weapons/Guns/Hits/energy_meat1.ogg + params: + variation: 0.250 + volume: -15 + activatedSoundOnHitNoDamage: + path: /Audio/Weapons/Guns/Hits/energy_meat1.ogg + params: + variation: 0.250 + volume: -17 + - type: ItemToggleDamageOtherOnHit - type: ItemToggleSize activatedSize: Small - type: ItemToggleHot @@ -207,6 +230,7 @@ damage: types: Blunt: 1 # does a little bit of damage on hit when off + - type: DamageOtherOnHit - type: PointLight enabled: false netsync: false diff --git a/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml b/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml index bfbb0573ca1..e5de7041a80 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml @@ -32,6 +32,9 @@ angle: 80.5 soundHit: path: "/Audio/Weapons/smash.ogg" + - type: DamageOtherOnHit + meleeDamageMultiplier: 1.33 + staminaCost: 12.5 - type: Tag tags: - Toolbox @@ -141,6 +144,10 @@ damage: types: Blunt: 11.5 + - type: DamageOtherOnHit + damage: + types: + Blunt: 15 - type: entity name: golden toolbox @@ -157,6 +164,10 @@ damage: types: Blunt: 12 + - type: DamageOtherOnHit + damage: + types: + Blunt: 16 - type: entity id: ToolboxThief diff --git a/Resources/Prototypes/Entities/Objects/Tools/tools.yml b/Resources/Prototypes/Entities/Objects/Tools/tools.yml index a58cf5fc660..17370ea04d8 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/tools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/tools.yml @@ -41,6 +41,12 @@ maxTargets: 4 soundHit: path: "/Audio/Items/wirecutter.ogg" + - type: DamageOtherOnHit + damage: + types: + Blunt: 4 + soundHit: + collection: MetalThud - type: Tool qualities: - Cutting @@ -118,6 +124,14 @@ angle: 20 soundHit: path: "/Audio/Weapons/bladeslice.ogg" + - type: DamageOtherOnHit + staminaCost: 5 + - type: ThrowingAngle + angle: 270 + - type: EmbeddableProjectile + sound: /Audio/Weapons/star_hit.ogg + removalTime: 1 + - type: EmbedPassiveDamage - type: Tool qualities: - Screwing @@ -187,6 +201,7 @@ angle: 100 soundHit: collection: MetalThud + - type: DamageOtherOnHit - type: Tool qualities: - Anchoring @@ -241,6 +256,8 @@ heavyStaminaCost: 5 soundHit: collection: MetalThud + - type: DamageOtherOnHit + staminaCost: 6 - type: Tool qualities: - Prying @@ -303,6 +320,7 @@ heavyDamageBaseModifier: 1.2 maxTargets: 1 angle: 20 + - type: DamageOtherOnHit - type: Item size: Small - type: Clothing @@ -449,6 +467,13 @@ angle: 20 soundHit: path: "/Audio/Items/drill_hit.ogg" + - type: DamageOtherOnHit + damage: + types: + Blunt: 8 + staminaCost: 7.5 + soundHit: + collection: MetalThud - type: ReverseEngineering # Nyano difficulty: 2 recipes: @@ -672,6 +697,10 @@ angle: 100 soundHit: collection: MetalThud + - type: DamageOtherOnHit + staminaCost: 8.5 + - type: ThrowingAngle + angle: 45 - type: Item size: Normal sprite: Objects/Tools/shovel.rsi @@ -720,6 +749,7 @@ angle: 20 soundHit: collection: MetalThud + - type: DamageOtherOnHit - type: Tool qualities: - Rolling diff --git a/Resources/Prototypes/Entities/Objects/Tools/welders.yml b/Resources/Prototypes/Entities/Objects/Tools/welders.yml index 69d6d80ab4e..eae5cdbf46f 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/welders.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/welders.yml @@ -56,6 +56,11 @@ activatedDamage: types: Heat: 7 + - type: ItemToggleDamageOtherOnHit + activatedDamage: + types: + Heat: 4 + Blunt: 3 - type: ItemToggleSize activatedSize: Large - type: ItemToggleHot @@ -78,6 +83,7 @@ Blunt: 6 #i mean... i GUESS you could use it like that soundHit: collection: MetalThud + - type: DamageOtherOnHit - type: RefillableSolution solution: Welder - type: SolutionContainerManager diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/arrows.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/arrows.yml index 6f925139fb1..bc041062678 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/arrows.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/arrows.yml @@ -28,6 +28,7 @@ - type: EmbeddableProjectile sound: /Audio/Weapons/star_hit.ogg embedOnThrow: false + - type: EmbedPassiveDamage - type: ThrowingAngle angle: 0 - type: Ammo diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml index 60f599af934..0c47eed9871 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml @@ -28,6 +28,8 @@ types: Blunt: 4 Structural: 10 + - type: DamageOtherOnHit + staminaCost: 10 - type: Item size: Normal - type: Tool diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml index 5c26020d72c..bc252fddbc6 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cane.yml @@ -16,6 +16,7 @@ damage: types: Blunt: 5 + - type: DamageOtherOnHit - type: StaminaDamageOnHit damage: 5 - type: Wieldable diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml index 3fee36a58af..045d5e3e6d3 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/cult.yml @@ -26,6 +26,11 @@ angle: 100 soundHit: collection: MetalThud + - type: DamageOtherOnHit + staminaCost: 9 + - type: EmbeddableProjectile + - type: ThrowingAngle + angle: 225 - type: Wieldable - type: IncreaseDamageOnWield damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml index ec1d8a82be0..04562d171cd 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml @@ -38,6 +38,14 @@ Slash: 8 Heat: 10 Structural: 20 + - type: ItemToggleDamageOtherOnHit + activatedStaminaCost: 6 + - type: ItemToggleEmbedPassiveDamage + - type: ItemToggleEmbeddableProjectile + activatedEmbedOnThrow: true + - type: ItemToggleThrowingAngle + activatedAngle: 225 + deleteOnDeactivate: true - type: Sprite sprite: Objects/Weapons/Melee/e_sword.rsi layers: @@ -53,6 +61,10 @@ damage: types: Blunt: 4.5 + - type: DamageOtherOnHit + - type: EmbeddableProjectile + embedOnThrow: false + - type: EmbedPassiveDamage - type: Item size: Small sprite: DeltaV/Objects/Weapons/Melee/e_sword.rsi # Delta-V @@ -145,6 +157,12 @@ volume: -6 - type: ItemToggleDisarmMalus activatedDisarmMalus: 0.4 + - type: ItemToggleEmbeddableProjectile + activatedOffset: 0.0,0.0 + activatedRemovalTime: 3 + - type: ItemToggleThrowingAngle + activatedAngle: 135 + deleteOnDeactivate: false - type: Sprite sprite: Objects/Weapons/Melee/e_dagger.rsi layers: @@ -161,6 +179,21 @@ damage: types: Blunt: 1 + - type: DamageOtherOnHit + damage: + types: + Piercing: 3 + staminaCost: 3.5 + - type: EmbeddableProjectile + offset: 0.3,0.0 + removalTime: 0.0 + embedOnThrow: true + - type: EmbedPassiveDamage + damage: + types: + Blunt: 0 + - type: ThrowingAngle + angle: 315 - type: Item size: Tiny sprite: Objects/Weapons/Melee/e_dagger.rsi diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml index ffbb1c6d057..52a2c7a207a 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml @@ -26,6 +26,11 @@ angle: 100 soundHit: collection: MetalThud + - type: DamageOtherOnHit + meleeDamageMultiplier: 1.5 + staminaCost: 18 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage - type: Wieldable - type: IncreaseDamageOnWield damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/home_run_bat.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/home_run_bat.yml index 43a4fb6c59b..75e0e0764c0 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/home_run_bat.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/home_run_bat.yml @@ -18,6 +18,9 @@ angle: 120 soundHit: collection: ExplosionSmall + - type: DamageOtherOnHit + soundHit: + collection: MetalThud # A throw won't knock them back so it's just a normal thud - type: MeleeRequiresWield # You can't hit a home run with one hand, jimbo. - type: MeleeThrowOnHit speed: 30 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml index 3705775dd68..51077687e07 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml @@ -23,6 +23,11 @@ angle: 40 soundHit: path: /Audio/Weapons/bladeslice.ogg + - type: DamageOtherOnHit + staminaCost: 5 + - type: EmbeddableProjectile + sound: /Audio/Weapons/star_hit.ogg + - type: EmbedPassiveDamage - type: Sprite - type: Item size: Small @@ -52,6 +57,8 @@ - type: Sprite sprite: Objects/Weapons/Melee/kitchen_knife.rsi state: icon + - type: ThrowingAngle + angle: 225 - type: Item sprite: Objects/Weapons/Melee/kitchen_knife.rsi - type: GuideHelp @@ -78,6 +85,10 @@ types: Slash: 8 Blunt: 1 + - type: DamageOtherOnHit + staminaCost: 7.5 + - type: ThrowingAngle + angle: 245 - type: Item size: Normal sprite: Objects/Weapons/Melee/cleaver.rsi @@ -104,12 +115,8 @@ damage: types: Slash: 9 - - type: EmbeddableProjectile - sound: /Audio/Weapons/star_hit.ogg - - type: DamageOtherOnHit - damage: - types: - Slash: 9 + - type: ThrowingAngle + angle: 225 - type: Item sprite: Objects/Weapons/Melee/combat_knife.rsi - type: DisarmMalus @@ -130,6 +137,8 @@ damage: types: Slash: 8 + - type: ThrowingAngle + angle: 225 - type: Item sprite: Objects/Weapons/Melee/survival_knife.rsi @@ -148,6 +157,8 @@ damage: types: Slash: 10 + - type: ThrowingAngle + angle: 225 - type: Item sprite: Objects/Weapons/Melee/kukri_knife.rsi @@ -168,9 +179,9 @@ types: Slash: 5 - type: DamageOtherOnHit - damage: - types: - Slash: 10 + staminaCost: 5 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage - type: Sprite sprite: Clothing/Head/Hats/greyflatcap.rsi - type: Clothing @@ -212,6 +223,8 @@ damage: types: Slash: 5.5 + - type: ThrowingAngle + angle: 200 - type: Item sprite: Objects/Weapons/Melee/shiv.rsi - type: DisarmMalus @@ -291,8 +304,6 @@ damage: types: Slash: 5 - - type: EmbeddableProjectile - sound: /Audio/Weapons/star_hit.ogg - type: DamageOtherOnHit ignoreResistances: true damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml index 38a203ce908..900950cb9ec 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml @@ -57,6 +57,8 @@ angle: 120 soundHit: collection: MetalThud + - type: DamageOtherOnHit + staminaCost: 8 - type: Wieldable - type: IncreaseDamageOnWield damage: @@ -94,6 +96,12 @@ heavyDamageBaseModifier: 1.2 maxTargets: 2 angle: 20 + - type: DamageOtherOnHit + staminaCost: 5 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 225 - type: Tag tags: - Knife @@ -114,6 +122,10 @@ groups: Brute: -21 - type: MeleeWeapon + - type: EmbeddableProjectile + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 225 - type: Tag tags: - Pickaxe diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/needle.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/needle.yml index 11efeba5f8c..7ebbf3bf11a 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/needle.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/needle.yml @@ -12,6 +12,11 @@ damage: types: Piercing: 1 + - type: DamageOtherOnHit + - type: EmbeddableProjectile + removalTime: 0 + - type: ThrowingAngle + angle: 225 - type: Item size: Tiny - type: BalloonPopper diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml index 92bb33d1773..69b4c81a3e1 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml @@ -26,6 +26,8 @@ heavyDamageBaseModifier: 1.75 maxTargets: 5 angle: 80 + - type: DamageOtherOnHit + staminaCost: 5 - type: Wieldable - type: IncreaseDamageOnWield damage: @@ -72,6 +74,10 @@ heavyRangeModifier: 2 heavyDamageBaseModifier: 1 angle: 20 + - type: DamageOtherOnHit + staminaCost: 8 + - type: ThrowingAngle + angle: 270 - type: ReverseEngineering # Nyano difficulty: 2 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sledgehammer.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sledgehammer.yml index ffe791ce0c4..09b0d0761e3 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sledgehammer.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sledgehammer.yml @@ -23,6 +23,8 @@ angle: 120 soundHit: collection: MetalThud + - type: DamageOtherOnHit + staminaCost: 10 - type: Wieldable - type: IncreaseDamageOnWield damage: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml index 576d0b2a0ce..b650343f75c 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml @@ -6,6 +6,7 @@ components: - type: EmbeddableProjectile offset: 0.15,0.15 + - type: EmbedPassiveDamage - type: ThrowingAngle angle: 225 - type: Tag diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/stunprod.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/stunprod.yml index 5214358ff96..9fb2ef9e8c8 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/stunprod.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/stunprod.yml @@ -26,6 +26,7 @@ activatedDamage: types: Shock: 5 + - type: ItemToggleDamageOtherOnHit - type: Stunbaton energyPerUse: 120 - type: MeleeWeapon @@ -39,6 +40,7 @@ heavyRateModifier: 0.8 heavyDamageBaseModifier: 1.2 animation: WeaponArcThrust + - type: DamageOtherOnHit - type: StaminaDamageOnHit damage: 22 sound: /Audio/Weapons/egloves.ogg diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index 046e9f76f7f..be0a4241270 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -23,6 +23,12 @@ heavyStaminaCost: 5 maxTargets: 7 angle: 80 + - type: DamageOtherOnHit + staminaCost: 5 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 225 - type: Reflect enabled: true # Design intent: a robust captain or tot can sacrifice movement to make the most of this weapon, but they have to @@ -67,6 +73,12 @@ heavyStaminaCost: 15 maxTargets: 1 angle: 20 + - type: DamageOtherOnHit + staminaCost: 10 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 225 - type: Item size: Normal sprite: DeltaV/Objects/Weapons/Melee/katana.rsi #DeltaV @@ -86,6 +98,8 @@ damage: types: Slash: 25 + - type: ThrowingAngle + angle: 300 - type: Item size: Normal sprite: Objects/Weapons/Melee/energykatana.rsi @@ -132,6 +146,12 @@ angle: 80 soundHit: path: /Audio/Weapons/bladeslice.ogg + - type: DamageOtherOnHit + staminaCost: 5 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 225 - type: Item size: Normal sprite: Objects/Weapons/Melee/machete.rsi @@ -164,6 +184,12 @@ angle: 200 soundHit: path: /Audio/Weapons/bladeslice.ogg + - type: DamageOtherOnHit + staminaCost: 18 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 225 - type: Item size: Normal - type: Clothing @@ -199,6 +225,12 @@ angle: 40 soundHit: path: /Audio/Weapons/bladeslice.ogg + - type: DamageOtherOnHit + staminaCost: 5 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 225 - type: Item size: Normal sprite: Objects/Weapons/Melee/cutlass.rsi @@ -227,6 +259,7 @@ Radiation: 10 soundHit: path: /Audio/Effects/explosion_small1.ogg + - type: DamageOtherOnHit - type: Reflect enabled: true reflectProb: 0.5 # In robust hands, deflects as well as an e-sword diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/telescopic_baton.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/telescopic_baton.yml index 5e797c85361..a1006a589a2 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/telescopic_baton.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/telescopic_baton.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity id: TelescopicBaton parent: BaseItem name: telescopic baton @@ -26,6 +26,7 @@ activatedDamage: types: Blunt: 12 + - type: ItemToggleDamageOtherOnHit - type: ItemToggleSize activatedSize: Normal - type: UseDelay @@ -44,6 +45,8 @@ damage: types: Blunt: 1 + - type: DamageOtherOnHit + staminaCost: 6 - type: Appearance - type: GenericVisualizer visuals: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/white_cane.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/white_cane.yml index 123de813cbd..749be946a2c 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/white_cane.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/white_cane.yml @@ -24,6 +24,7 @@ heavyStaminaCost: 0 maxTargets: 1 angle: 20 + - type: DamageOtherOnHit - type: Wieldable - type: IncreaseDamageOnWield damage: @@ -31,4 +32,3 @@ Blunt: 2 - type: UseDelay delay: 1 - diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/throwing_stars.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/throwing_stars.yml index c68feff0b5c..293d284d526 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/throwing_stars.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/throwing_stars.yml @@ -36,6 +36,7 @@ types: Slash: 8 Piercing: 10 + - type: EmbedPassiveDamage - type: StaminaDamageOnCollide damage: 45 - type: StaminaDamageOnEmbed diff --git a/Resources/Prototypes/Entities/Objects/Weapons/security.yml b/Resources/Prototypes/Entities/Objects/Weapons/security.yml index 1b07eab9faf..561af78a56c 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/security.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/security.yml @@ -29,6 +29,7 @@ activatedDamage: types: Blunt: 0 + - type: ItemToggleDamageOtherOnHit - type: MeleeWeapon wideAnimationRotation: -135 damage: @@ -39,6 +40,7 @@ heavyDamageBaseModifier: 1.75 heavyStaminaCost: 1 animation: WeaponArcSlash + - type: DamageOtherOnHit - type: StaminaDamageOnHit damage: 35 sound: /Audio/Weapons/egloves.ogg @@ -109,10 +111,12 @@ heavyRateModifier: 1 heavyDamageBaseModifier: 1.2 heavyStaminaCost: 7.5 + - type: DamageOtherOnHit + staminaCost: 9 - type: Item size: Normal - type: Tag - tags: + tags: - Truncheon - type: Clothing sprite: Objects/Weapons/Melee/truncheon.rsi diff --git a/Resources/Prototypes/Entities/Structures/Furniture/Tables/base_structuretables.yml b/Resources/Prototypes/Entities/Structures/Furniture/Tables/base_structuretables.yml index dbef5a7504a..7a926d66d37 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/Tables/base_structuretables.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/Tables/base_structuretables.yml @@ -40,7 +40,8 @@ footstepSoundCollection: collection: FootstepHull - type: RequireProjectileTarget - + - type: ThrownItemImmune + - type: entity id: CounterBase parent: TableBase diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml index 7d7bc94bb32..787421556db 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/base_structurelockers.yml @@ -49,6 +49,11 @@ node: done containers: - entity_storage + - type: DamageOtherOnHit + damage: + types: + Blunt: 10 + staminaCost: 35 - type: entity id: LockerBaseSecure diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml index c1efc5a63f8..aa1d53a065d 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/Lockers/lockers.yml @@ -43,6 +43,11 @@ stateBaseClosed: secure stateDoorOpen: secure_open stateDoorClosed: secure_door + - type: DamageOtherOnHit + damage: + types: + Blunt: 15 + staminaCost: 50 # Cargo - type: entity diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml index e966a41780c..9d89f86a7a5 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/base_structureclosets.yml @@ -98,6 +98,11 @@ stateDoorClosed: generic_door - type: StaticPrice price: 50 + - type: DamageOtherOnHit + damage: + types: + Blunt: 10 + staminaCost: 35 # steel closet base (that can be constructed/deconstructed) - type: entity @@ -109,6 +114,11 @@ node: done containers: - entity_storage + - type: DamageOtherOnHit + damage: + types: + Blunt: 15 + staminaCost: 50 #Wall Closet - type: entity diff --git a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml index bd76a87f557..bd8281f1949 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml @@ -12,6 +12,11 @@ - Energy reflectProb: 0.2 spread: 90 + - type: DamageOtherOnHit + damage: + types: + Blunt: 16 + staminaCost: 45 - type: entity parent: CrateBaseWeldable @@ -29,6 +34,11 @@ - entity_storage - type: StaticPrice price: 80 + - type: DamageOtherOnHit + damage: + types: + Blunt: 10 + staminaCost: 30 - type: entity parent: CratePlastic @@ -140,7 +150,7 @@ map: ["enum.StorageVisualLayers.Door"] - state: paper sprite: Structures/Storage/Crates/labels.rsi - map: ["enum.PaperLabelVisuals.Layer"] + map: ["enum.PaperLabelVisuals.Layer"] - type: Construction graph: WebStructures node: crate @@ -338,7 +348,7 @@ - state: paper sprite: Structures/Storage/Crates/labels.rsi offset: "-0.25,0.625" - map: ["enum.PaperLabelVisuals.Layer"] + map: ["enum.PaperLabelVisuals.Layer"] - type: Icon sprite: Structures/Storage/Crates/livestock.rsi state: base @@ -393,7 +403,7 @@ - state: paper sprite: Structures/Storage/Crates/labels.rsi offset: "0.0,0.125" - map: ["enum.PaperLabelVisuals.Layer"] + map: ["enum.PaperLabelVisuals.Layer"] - type: Icon sprite: Structures/Storage/Crates/cage.rsi - type: Destructible @@ -495,7 +505,7 @@ - state: closed map: ["enum.StorageVisualLayers.Door"] - state: paper - map: ["enum.PaperLabelVisuals.Layer"] + map: ["enum.PaperLabelVisuals.Layer"] - type: Icon sprite: Structures/Storage/Crates/coffin.rsi state: base @@ -537,7 +547,7 @@ - state: paper sprite: Structures/Storage/Crates/labels.rsi offset: "-0.28125,0.625" - map: ["enum.PaperLabelVisuals.Layer"] + map: ["enum.PaperLabelVisuals.Layer"] - type: Icon sprite: Structures/Storage/Crates/wooden_grave.rsi state: base @@ -585,7 +595,7 @@ - state: paper sprite: Structures/Storage/Crates/labels.rsi offset: "-0.3125,0.5625" - map: ["enum.PaperLabelVisuals.Layer"] + map: ["enum.PaperLabelVisuals.Layer"] - type: Icon sprite: Structures/Storage/Crates/stone_grave.rsi state: base diff --git a/Resources/Prototypes/Entities/Structures/base_structure.yml b/Resources/Prototypes/Entities/Structures/base_structure.yml index 71971a66243..207881894b8 100644 --- a/Resources/Prototypes/Entities/Structures/base_structure.yml +++ b/Resources/Prototypes/Entities/Structures/base_structure.yml @@ -25,6 +25,11 @@ - type: Tag tags: - Structure + - type: DamageOtherOnHit + damage: + types: + Blunt: 8 + staminaCost: 50 - type: entity # This means that it's not anchored on spawn. diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Fun/instruments.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Fun/instruments.yml index 67978f89fe0..3964a9f0b04 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Fun/instruments.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Fun/instruments.yml @@ -48,6 +48,7 @@ damage: types: Blunt: 7 + - type: DamageOtherOnHit - type: Wieldable - type: IncreaseDamageOnWield damage: diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/blunt.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/blunt.yml index a9a5a829377..1b163fe1988 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/blunt.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/blunt.yml @@ -21,6 +21,8 @@ # - type: MeleeStaminaCost # swing: 10 # wieldCoefficient: 0.35 #if wielded you will only consume 3.5 stam its a weapon after all + - type: DamageOtherOnHit + staminaCost: 10 - type: Wieldable - type: IncreaseDamageOnWield damage: @@ -52,10 +54,10 @@ collection: WoodDestroy # - type: MeleeStaminaCost # swing: 5 + - type: DamageOtherOnHit - type: StaminaDamageOnHit damage: 10 - type: Item size: Normal sprite: Nyanotrasen/Objects/Weapons/Melee/shinai.rsi - type: DisarmMalus - diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/breaching_hammer.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/breaching_hammer.yml index d019cee1360..316b00d7fd3 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/breaching_hammer.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/breaching_hammer.yml @@ -23,6 +23,8 @@ # - type: MeleeStaminaCost # swing: 10 # wieldCoefficient: 0.5 #if wielded you will only consume 5 + - type: DamageOtherOnHit + staminaCost: 12 - type: Wieldable - type: IncreaseDamageOnWield damage: @@ -46,5 +48,3 @@ quickEquip: false slots: - back - - diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/dulled.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/dulled.yml index 9cab55f2a71..83e7a7dbd0e 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/dulled.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/dulled.yml @@ -16,6 +16,10 @@ types: Blunt: 5 Slash: 1 + - type: DamageOtherOnHit + staminaCost: 10 + - type: ThrowingAngle + angle: 225 - type: Item size: Normal sprite: Objects/Weapons/Melee/katana.rsi @@ -36,6 +40,10 @@ types: Blunt: 6 Slash: 1 + - type: DamageOtherOnHit + staminaCost: 18 + - type: ThrowingAngle + angle: 225 - type: Item size: Normal - type: Clothing diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/sword.yml index f2604f044fd..c01b328bc79 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/sword.yml @@ -21,6 +21,10 @@ Slash: 12 soundHit: path: /Audio/Weapons/bladeslice.ogg + - type: DamageOtherOnHit + staminaCost: 5 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage - type: Item size: Normal sprite: Nyanotrasen/Objects/Weapons/Melee/wakizashi.rsi diff --git a/Resources/Prototypes/WhiteDream/Entities/Objects/Weapons/Melee/cult.yml b/Resources/Prototypes/WhiteDream/Entities/Objects/Weapons/Melee/cult.yml index c21c383e8e6..eb43cd2e7f4 100644 --- a/Resources/Prototypes/WhiteDream/Entities/Objects/Weapons/Melee/cult.yml +++ b/Resources/Prototypes/WhiteDream/Entities/Objects/Weapons/Melee/cult.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity name: ritual dagger parent: BaseKnife id: RitualDagger @@ -15,6 +15,12 @@ damage: types: Piercing: 15 + - type: DamageOtherOnHit + staminaCost: 5 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 225 - type: Clothing sprite: Objects/Weapons/Melee/cult_dagger.rsi slots: @@ -56,6 +62,12 @@ angle: 90 soundHit: path: /Audio/Weapons/bladeslice.ogg + - type: DamageOtherOnHit + staminaCost: 8 + - type: EmbeddableProjectile + - type: EmbedPassiveDamage + - type: ThrowingAngle + angle: 225 - type: Item size: Normal - type: Clothing @@ -79,6 +91,7 @@ state: icon - type: EmbeddableProjectile offset: 0.15,0.15 + - type: EmbedPassiveDamage - type: ThrowingAngle angle: 225 - type: Fixtures @@ -111,6 +124,7 @@ damage: types: Piercing: 40 + staminaCost: 18 - type: Item sprite: WhiteDream/BloodCult/Entities/Items/Weapons/cult_spear.rsi storedRotation: 44