From df584ad44695777b879070dc529b31a2c80d51b3 Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Wed, 15 Sep 2021 03:07:37 +1000 Subject: [PATCH] ECS damageable (#4529) * ECS and damage Data * Comments and newlines * Added Comments * Make TryChangeDamageEvent immutable * Remove SetAllDamage event Use public SetAllDamage function instead * Undo destructible mistakes That was some shit code. * Rename DamageData to DamageSpecifier And misc small edits misc * Cache trigger prototypes. * Renaming destructible classes & functions * Revert "Cache trigger prototypes." This reverts commit 86bae15ba6616884dba75f552dfdfbe2d1fb6586. * Replace prototypes with prototype IDs. * Split damage.yml into individual files * move get/handle component state to system * Update HealthChange doc * Make godmode call Dirty() on damageable component * Add Initialize() to fix damage test * Make non-static * uncache resistance set prototype and trim DamageableComponentState * Remove unnecessary Dirty() calls during initialization * RemoveTryChangeDamageEvent * revert Dirty() * Fix MobState relying on DamageableComponent.Dirty() * Fix DisposalUnit Tests. These were previously failing, but because the async was not await-ed, this never raised the exception. After I fixed MobState component, this exception stopped happening and instead the assertions started being tested & failing * Disposal test 2: electric boogaloo * Fix typos/mistakes also add comments and fix spacing. * Use Uids instead of IEntity * fix merge * Comments, a merge issue, and making some damage ignore resistances * Extend DamageSpecifier and use it for DamageableComponent * fix master merge * Fix Disposal unit test. Again. Snapgrids were removed in master * Execute Exectute --- Content.Client/Body/UI/BodyScannerDisplay.cs | 4 +- .../HealthOverlay/HealthOverlaySystem.cs | 6 +- .../HealthOverlay/UI/HealthOverlayGui.cs | 4 +- .../MedicalScanner/UI/MedicalScannerWindow.cs | 45 +- Content.Client/MobState/MobStateComponent.cs | 14 - .../Tests/Commands/RejuvenateTest.cs | 10 +- .../Damageable/AllSupportDamageableTest.cs | 121 ----- .../Tests/Damageable/DamageableTest.cs | 493 +++++------------ .../DestructibleDamageGroupTest.cs | 93 ++-- .../DestructibleDamageTypeTest.cs | 92 ++-- .../DestructibleDestructionTest.cs | 24 +- .../DestructibleTestPrototypes.cs | 62 +-- .../DestructibleThresholdActivationTest.cs | 99 ++-- .../TestDestructibleListenerSystem.cs | 26 + .../TestThresholdListenerComponent.cs | 25 - .../Tests/Disposal/DisposalUnitTest.cs | 105 +++- .../Combat/Melee/MeleeWeaponDamageCon.cs | 2 +- .../Considerations/Combat/TargetHealthCon.cs | 6 +- .../States/Mobs/NearbyPlayersState.cs | 6 +- .../Atmos/Components/BarotraumaComponent.cs | 22 +- .../Atmos/Components/FlammableComponent.cs | 24 +- .../Body/Metabolism/MetabolizerSystem.cs | 2 +- .../Body/Respiratory/RespiratorComponent.cs | 37 +- .../Buckle/Components/BuckleComponent.cs | 2 +- .../Chat/Commands/SuicideCommand.cs | 16 +- .../Components/HyposprayComponent.cs | 2 +- .../Chemistry/ReagentEffects/HealthChange.cs | 55 +- Content.Server/Damage/Commands/HurtCommand.cs | 59 +-- .../DamageOnHighSpeedImpactComponent.cs | 16 +- .../Components/DamageOnLandComponent.cs | 15 +- .../DamageOnToolInteractComponent.cs | 49 +- .../Components/DamageOtherOnHitComponent.cs | 23 +- Content.Server/Damage/RejuvenateVerb.cs | 13 +- .../Systems/DamageOnHighSpeedImpactSystem.cs | 10 +- .../Damage/Systems/DamageOnLandSystem.cs | 15 +- .../Damage/Systems/DamageOtherOnHitSystem.cs | 10 +- .../Damage/Systems/GodmodeSystem.cs | 38 +- .../Destructible/DestructibleComponent.cs | 43 +- .../Destructible/DestructibleSystem.cs | 43 +- .../DestructibleThresholdReachedMessage.cs | 18 - .../{Threshold.cs => DamageThreshold.cs} | 8 +- .../Thresholds/Triggers/AndTrigger.cs | 6 +- .../Thresholds/Triggers/DamageGroupTrigger.cs | 23 +- .../Thresholds/Triggers/DamageTrigger.cs | 4 +- .../Thresholds/Triggers/DamageTypeTrigger.cs | 22 +- .../Thresholds/Triggers/IThresholdTrigger.cs | 4 +- .../Thresholds/Triggers/OrTrigger.cs | 6 +- Content.Server/DoAfter/DoAfterComponent.cs | 30 -- Content.Server/DoAfter/DoAfterSystem.cs | 23 + .../Doors/Components/ServerDoorComponent.cs | 24 +- .../GameTicking/Presets/GamePreset.cs | 12 +- .../Presets/PresetTraitorDeathMatch.cs | 13 +- .../GameTicking/Rules/RuleDeathMatch.cs | 6 +- .../Light/Components/PoweredLightComponent.cs | 21 +- .../Light/EntitySystems/PoweredLightSystem.cs | 22 +- .../Medical/Components/HealingComponent.cs | 18 +- .../Components/MedicalScannerComponent.cs | 14 +- .../Components/AsteroidRockComponent.cs | 10 +- .../MobState/States/MobStateManager.cs | 14 - .../MobState/States/NormalMobState.cs | 6 +- .../Nutrition/Components/HungerComponent.cs | 32 +- .../Nutrition/Components/ThirstComponent.cs | 31 +- .../Components/HitscanComponent.cs | 25 +- .../Components/ProjectileComponent.cs | 7 +- .../Projectiles/ProjectileSystem.cs | 20 +- .../Repairable/RepairableComponent.cs | 38 +- Content.Server/Repairable/RepairableSystem.cs | 46 ++ .../Components/TemperatureComponent.cs | 27 +- .../Melee/Components/MeleeWeaponComponent.cs | 18 +- .../Weapon/Melee/MeleeWeaponSystem.cs | 17 +- .../ServerBatteryBarrelComponent.cs | 10 +- .../Components/ServerRangedBarrelComponent.cs | 10 +- .../Ranged/ServerRangedWeaponComponent.cs | 30 +- Content.Server/Window/WindowComponent.cs | 20 +- Content.Server/Window/WindowSystem.cs | 19 + .../Body/Components/SharedBodyComponent.cs | 31 +- .../Damage/Components/DamageableComponent.cs | 499 ------------------ .../Damage/Components/IDamageableComponent.cs | 225 -------- .../Container/DamageContainerPrototype.cs | 151 ------ Content.Shared/Damage/DamageChangeData.cs | 31 -- .../Damage/DamageChangedEventArgs.cs | 35 -- Content.Shared/Damage/DamageChangedMessage.cs | 51 -- Content.Shared/Damage/DamageGroupPrototype.cs | 42 -- Content.Shared/Damage/DamageSpecifier.cs | 375 +++++++++++++ Content.Shared/Damage/DamageSystem.cs | 13 - Content.Shared/Damage/DamageableComponent.cs | 129 +++++ Content.Shared/Damage/DamageableSystem.cs | 243 +++++++++ .../Prototypes/DamageContainerPrototype.cs | 40 ++ .../Damage/Prototypes/DamageGroupPrototype.cs | 26 + .../{ => Prototypes}/DamageTypePrototype.cs | 2 +- .../Prototypes/ResistanceSetPrototype.cs | 31 ++ .../Damage/Resistances/ResistanceSet.cs | 66 --- .../Resistances/ResistanceSetPrototype.cs | 61 --- .../SharedMedicalScannerComponent.cs | 13 +- ...StateComponent.cs => MobStateComponent.cs} | 72 +-- .../MobState/EntitySystems/MobStateSystem.cs | 45 ++ .../EntitySystems/SharedMobStateSystem.cs | 38 -- Content.Shared/MobState/IMobStateComponent.cs | 2 +- Content.Tests/Shared/DamageTest.cs | 293 ++++++++++ .../Prototypes/Body/Mechanisms/human.yml | 100 ++-- Resources/Prototypes/Body/Parts/human.yml | 34 +- Resources/Prototypes/Body/Parts/slime.yml | 36 +- Resources/Prototypes/Body/Parts/vox.yml | 35 +- Resources/Prototypes/Damage/containers.yml | 16 + Resources/Prototypes/Damage/damage.yml | 97 ---- Resources/Prototypes/Damage/groups.yml | 36 ++ .../Prototypes/Damage/resistance_sets.yml | 112 +--- Resources/Prototypes/Damage/types.yml | 38 ++ .../Entities/Clothing/Eyes/glasses.yml | 12 +- .../Entities/Debugging/debug_sweps.yml | 17 +- .../Entities/Debugging/spanisharmyknife.yml | 4 +- .../Entities/Effects/chemistry_effects.yml | 3 +- .../Prototypes/Entities/Mobs/NPCs/mimic.yml | 4 +- .../Entities/Mobs/NPCs/simplemob.yml | 33 +- .../Prototypes/Entities/Mobs/NPCs/xeno.yml | 10 +- .../Entities/Mobs/Species/human.yml | 39 +- .../Prototypes/Entities/Mobs/Species/vox.yml | 6 + .../Consumable/Food/Containers/bowl.yml | 9 +- .../Consumable/Food/Containers/box.yml | 2 - .../Consumable/Food/Containers/plate.yml | 9 +- .../Consumable/Food/Containers/tin.yml | 9 +- .../Entities/Objects/Consumable/Food/egg.yml | 15 +- .../Objects/Consumable/Food/ingredients.yml | 18 +- .../Objects/Consumable/Food/produce.yml | 18 +- .../Entities/Objects/Consumable/Food/soup.yml | 9 +- .../Entities/Objects/Consumable/drinks.yml | 10 +- .../Objects/Consumable/drinks_bottles.yml | 9 +- .../Prototypes/Entities/Objects/Fun/toys.yml | 3 + .../Entities/Objects/Materials/shards.yml | 17 +- .../Objects/Misc/fire_extinguisher.yml | 5 +- .../Entities/Objects/Misc/fluff_lights.yml | 6 +- .../Entities/Objects/Misc/inflatable_wall.yml | 3 +- .../Entities/Objects/Misc/tiles.yml | 4 +- .../Entities/Objects/Power/lights.yml | 9 +- .../Objects/Specific/Hydroponics/tools.yml | 17 + .../Objects/Specific/Medical/healing.yml | 16 +- .../Objects/Specific/Medical/surgery.yml | 19 +- .../Objects/Specific/Security/barrier.yml | 3 +- .../Entities/Objects/Specific/chemistry.yml | 15 +- .../Entities/Objects/Tools/cowtools.yml | 15 + .../Entities/Objects/Tools/toolbox.yml | 8 +- .../Entities/Objects/Tools/tools.yml | 17 +- .../Entities/Objects/Tools/welders.yml | 3 + .../Ammunition/Projectiles/antimaterial.yml | 5 +- .../Guns/Ammunition/Projectiles/clrifle.yml | 25 +- .../Guns/Ammunition/Projectiles/lrifle.yml | 25 +- .../Guns/Ammunition/Projectiles/magnum.yml | 25 +- .../Guns/Ammunition/Projectiles/pistol.yml | 25 +- .../Guns/Ammunition/Projectiles/shotgun.yml | 35 +- .../Guns/Ammunition/Projectiles/srifle.yml | 25 +- .../Guns/Ammunition/Projectiles/toy.yml | 10 +- .../Weapons/Guns/Explosives/grenades.yml | 4 + .../Weapons/Guns/Projectiles/hitscan.yml | 15 +- .../Weapons/Guns/Projectiles/projectiles.yml | 63 ++- .../Objects/Weapons/Melee/baseball_bat.yml | 4 +- .../Objects/Weapons/Melee/fireaxe.yml | 4 +- .../Entities/Objects/Weapons/Melee/gohei.yml | 4 +- .../Entities/Objects/Weapons/Melee/knife.yml | 12 +- .../Objects/Weapons/Melee/pickaxe.yml | 7 +- .../Entities/Objects/Weapons/Melee/spear.yml | 8 +- .../Entities/Objects/Weapons/Melee/sword.yml | 16 +- .../Entities/Objects/Weapons/security.yml | 8 +- .../Prototypes/Entities/Objects/base.yml | 3 + .../Entities/Structures/Dispensers/base.yml | 3 +- .../Structures/Doors/Airlocks/airlocks.yml | 3 + .../Structures/Doors/Airlocks/assembly.yml | 3 +- .../Structures/Doors/Airlocks/base.yml | 6 +- .../Structures/Doors/Airlocks/external.yml | 3 + .../Structures/Doors/Firelocks/firelock.yml | 12 +- .../Structures/Doors/Firelocks/frame.yml | 3 +- .../Structures/Doors/Windoors/assembly.yml | 3 +- .../Structures/Doors/Windoors/base.yml | 6 +- .../Structures/Furniture/Tables/base.yml | 3 +- .../Structures/Furniture/Tables/tables.yml | 6 +- .../Entities/Structures/Furniture/beds.yml | 3 +- .../Structures/Furniture/bookshelf.yml | 2 +- .../Entities/Structures/Furniture/carpets.yml | 1 + .../Structures/Furniture/instruments.yml | 3 +- .../Structures/Furniture/potted_plants.yml | 1 + .../Entities/Structures/Furniture/seats.yml | 3 +- .../Structures/Machines/Computers/frame.yml | 3 +- .../Entities/Structures/Machines/base.yml | 3 +- .../Structures/Machines/chem_master.yml | 3 + .../Entities/Structures/Machines/frame.yml | 6 +- .../Structures/Piping/Atmospherics/miners.yml | 2 + .../Structures/Piping/Atmospherics/pipes.yml | 2 + .../Structures/Piping/Disposal/pipes.yml | 3 +- .../Power/Generation/PA/particles.yml | 5 +- .../Power/Generation/Singularity/emitter.yml | 3 +- .../Structures/Power/Generation/ame.yml | 6 +- .../Structures/Power/Generation/solar.yml | 9 +- .../Structures/Power/cable_terminal.yml | 3 +- .../Entities/Structures/Power/cables.yml | 3 +- .../Entities/Structures/Power/debug_power.yml | 3 +- .../Storage/Canisters/gas_canisters.yml | 6 +- .../Structures/Storage/Closets/base.yml | 6 +- .../Structures/Storage/Crates/base.yml | 3 +- .../Structures/Storage/Crates/crates.yml | 3 +- .../Structures/Storage/Tanks/base.yml | 3 +- .../Entities/Structures/Storage/storage.yml | 3 +- .../Structures/Wallmounts/Signs/base.yml | 3 +- .../Structures/Wallmounts/lighting.yml | 16 +- .../Entities/Structures/Walls/asteroid.yml | 3 +- .../Entities/Structures/Walls/barricades.yml | 6 +- .../Entities/Structures/Walls/base.yml | 3 +- .../Entities/Structures/Walls/girder.yml | 3 +- .../Entities/Structures/Walls/low.yml | 3 +- .../Entities/Structures/Windows/plasma.yml | 4 +- .../Entities/Structures/Windows/window.yml | 3 +- .../Entities/Structures/cargo_telepad.yml | 3 +- .../Entities/Structures/meat_spike.yml | 3 +- .../Prototypes/Entities/Structures/soil.yml | 3 +- 212 files changed, 2876 insertions(+), 3441 deletions(-) delete mode 100644 Content.Client/MobState/MobStateComponent.cs delete mode 100644 Content.IntegrationTests/Tests/Damageable/AllSupportDamageableTest.cs create mode 100644 Content.IntegrationTests/Tests/Destructible/TestDestructibleListenerSystem.cs delete mode 100644 Content.IntegrationTests/Tests/Destructible/TestThresholdListenerComponent.cs delete mode 100644 Content.Server/Destructible/DestructibleThresholdReachedMessage.cs rename Content.Server/Destructible/Thresholds/{Threshold.cs => DamageThreshold.cs} (94%) delete mode 100644 Content.Server/MobState/States/MobStateManager.cs create mode 100644 Content.Server/Repairable/RepairableSystem.cs create mode 100644 Content.Server/Window/WindowSystem.cs delete mode 100644 Content.Shared/Damage/Components/DamageableComponent.cs delete mode 100644 Content.Shared/Damage/Components/IDamageableComponent.cs delete mode 100644 Content.Shared/Damage/Container/DamageContainerPrototype.cs delete mode 100644 Content.Shared/Damage/DamageChangeData.cs delete mode 100644 Content.Shared/Damage/DamageChangedEventArgs.cs delete mode 100644 Content.Shared/Damage/DamageChangedMessage.cs delete mode 100644 Content.Shared/Damage/DamageGroupPrototype.cs create mode 100644 Content.Shared/Damage/DamageSpecifier.cs delete mode 100644 Content.Shared/Damage/DamageSystem.cs create mode 100644 Content.Shared/Damage/DamageableComponent.cs create mode 100644 Content.Shared/Damage/DamageableSystem.cs create mode 100644 Content.Shared/Damage/Prototypes/DamageContainerPrototype.cs create mode 100644 Content.Shared/Damage/Prototypes/DamageGroupPrototype.cs rename Content.Shared/Damage/{ => Prototypes}/DamageTypePrototype.cs (92%) create mode 100644 Content.Shared/Damage/Prototypes/ResistanceSetPrototype.cs delete mode 100644 Content.Shared/Damage/Resistances/ResistanceSet.cs delete mode 100644 Content.Shared/Damage/Resistances/ResistanceSetPrototype.cs rename Content.Shared/MobState/Components/{SharedMobStateComponent.cs => MobStateComponent.cs} (87%) create mode 100644 Content.Shared/MobState/EntitySystems/MobStateSystem.cs delete mode 100644 Content.Shared/MobState/EntitySystems/SharedMobStateSystem.cs create mode 100644 Content.Tests/Shared/DamageTest.cs create mode 100644 Resources/Prototypes/Damage/containers.yml delete mode 100644 Resources/Prototypes/Damage/damage.yml create mode 100644 Resources/Prototypes/Damage/groups.yml create mode 100644 Resources/Prototypes/Damage/types.yml diff --git a/Content.Client/Body/UI/BodyScannerDisplay.cs b/Content.Client/Body/UI/BodyScannerDisplay.cs index 428a2b4516..c0b98c1c82 100644 --- a/Content.Client/Body/UI/BodyScannerDisplay.cs +++ b/Content.Client/Body/UI/BodyScannerDisplay.cs @@ -2,7 +2,7 @@ using System.Linq; using Content.Shared.Body.Components; using Content.Shared.Body.Mechanism; using Content.Shared.Body.Part; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Shared.GameObjects; @@ -147,7 +147,7 @@ namespace Content.Client.Body.UI BodyPartLabel.Text = $"{Loc.GetString(slotName)}: {Loc.GetString(part.Owner.Name)}"; // TODO BODY Part damage - if (part.Owner.TryGetComponent(out IDamageableComponent? damageable)) + if (part.Owner.TryGetComponent(out DamageableComponent? damageable)) { BodyPartHealth.Text = Loc.GetString("body-scanner-display-body-part-damage-text",("damage", damageable.TotalDamage)); } diff --git a/Content.Client/HealthOverlay/HealthOverlaySystem.cs b/Content.Client/HealthOverlay/HealthOverlaySystem.cs index f3c5a79266..74414b0bed 100644 --- a/Content.Client/HealthOverlay/HealthOverlaySystem.cs +++ b/Content.Client/HealthOverlay/HealthOverlaySystem.cs @@ -1,6 +1,6 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Content.Client.HealthOverlay.UI; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; using Content.Shared.GameTicking; using Content.Shared.MobState; using JetBrains.Annotations; @@ -79,7 +79,7 @@ namespace Content.Client.HealthOverlay var viewBox = _eyeManager.GetWorldViewport().Enlarged(2.0f); - foreach (var (mobState, _) in ComponentManager.EntityQuery()) + foreach (var (mobState, _) in ComponentManager.EntityQuery()) { var entity = mobState.Owner; diff --git a/Content.Client/HealthOverlay/UI/HealthOverlayGui.cs b/Content.Client/HealthOverlay/UI/HealthOverlayGui.cs index c1d4eea275..4c0a157345 100644 --- a/Content.Client/HealthOverlay/UI/HealthOverlayGui.cs +++ b/Content.Client/HealthOverlay/UI/HealthOverlayGui.cs @@ -1,6 +1,6 @@ using Content.Client.IoC; using Content.Client.Resources; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; using Content.Shared.MobState; using Robust.Client.Graphics; using Robust.Client.UserInterface; @@ -76,7 +76,7 @@ namespace Content.Client.HealthOverlay.UI } if (!Entity.TryGetComponent(out IMobStateComponent? mobState) || - !Entity.TryGetComponent(out IDamageableComponent? damageable)) + !Entity.TryGetComponent(out DamageableComponent? damageable)) { CritBar.Visible = false; HealthBar.Visible = false; diff --git a/Content.Client/MedicalScanner/UI/MedicalScannerWindow.cs b/Content.Client/MedicalScanner/UI/MedicalScannerWindow.cs index 58aaeafa93..6580689045 100644 --- a/Content.Client/MedicalScanner/UI/MedicalScannerWindow.cs +++ b/Content.Client/MedicalScanner/UI/MedicalScannerWindow.cs @@ -1,6 +1,5 @@ using System.Text; using System.Collections.Generic; -using Content.Shared.Damage; using System.Linq; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; @@ -10,6 +9,7 @@ using Robust.Shared.Localization; using Robust.Shared.Prototypes; using static Content.Shared.MedicalScanner.SharedMedicalScannerComponent; using static Robust.Client.UserInterface.Controls.BoxContainer; +using Content.Shared.Damage.Prototypes; namespace Content.Client.MedicalScanner.UI { @@ -55,59 +55,36 @@ namespace Content.Client.MedicalScanner.UI { text.Append($"{Loc.GetString("medical-scanner-window-entity-health-text", ("entityName", entity.Name))}\n"); - // Show the total damage - var totalDamage = state.DamagePerTypeID.Values.Sum(); + var totalDamage = state.DamagePerType.Values.Sum(); text.Append($"{Loc.GetString("medical-scanner-window-entity-damage-total-text", ("amount", totalDamage))}\n"); - // Keep track of how many damage types we have shown - HashSet shownTypeIDs = new(); + HashSet shownTypes = new(); - // First show just the total damage and type breakdown for each damge group that is fully supported by that entitygroup. - foreach (var (damageGroupID, damageAmount) in state.DamagePerSupportedGroupID) + // Show the total damage and type breakdown for each damage group. + foreach (var (damageGroupID, damageAmount) in state.DamagePerGroup) { - - // Show total damage for the group text.Append($"\n{Loc.GetString("medical-scanner-window-damage-group-text", ("damageGroup", damageGroupID), ("amount", damageAmount))}"); - // Then show the damage for each type in that group. - // currently state has a dictionary mapping groupsIDs to damage, and typeIDs to damage, but does not know how types and groups are related. - // So use PrototypeManager. + // Show the damage for each type in that group. var group = IoCManager.Resolve().Index(damageGroupID); foreach (var type in group.DamageTypes) { - if (state.DamagePerTypeID.TryGetValue(type.ID, out var typeAmount)) + if (state.DamagePerType.TryGetValue(type, out var typeAmount)) { // If damage types are allowed to belong to more than one damage group, they may appear twice here. Mark them as duplicate. - if (!shownTypeIDs.Contains(type.ID)) + if (!shownTypes.Contains(type)) { - shownTypeIDs.Add(type.ID); - text.Append($"\n- {Loc.GetString("medical-scanner-window-damage-type-text", ("damageType", type.ID), ("amount", typeAmount))}"); + shownTypes.Add(type); + text.Append($"\n- {Loc.GetString("medical-scanner-window-damage-type-text", ("damageType", type), ("amount", typeAmount))}"); } else { - text.Append($"\n- {Loc.GetString("medical-scanner-window-damage-type-duplicate-text", ("damageType", type.ID), ("amount", typeAmount))}"); + text.Append($"\n- {Loc.GetString("medical-scanner-window-damage-type-duplicate-text", ("damageType", type), ("amount", typeAmount))}"); } } } text.Append('\n'); } - // Then, lets also list any damageType that was not fully Supported by the entity's damageContainer - var textAppendix = new StringBuilder(); - int totalMiscDamage = 0; - // Iterate over ids that have not been printed. - foreach (var damageTypeID in state.DamagePerTypeID.Keys.Where(typeID => !shownTypeIDs.Contains(typeID))) - { - //This damage type was not yet added to the text. - textAppendix.Append($"\n- {Loc.GetString("medical-scanner-window-damage-type-text", ("damageType", damageTypeID), ("amount", state.DamagePerTypeID[damageTypeID]))}"); - totalMiscDamage += state.DamagePerTypeID[damageTypeID]; - } - - // Is there any information to show? Did any damage types not belong to a group? - if (textAppendix.Length > 0) { - text.Append($"\n{Loc.GetString("medical-scanner-window-damage-group-text", ("damageGroup", "Miscellaneous"), ("amount", totalMiscDamage))}"); - text.Append(textAppendix); - } - _diagnostics.Text = text.ToString(); ScanButton.Disabled = state.IsScanned; diff --git a/Content.Client/MobState/MobStateComponent.cs b/Content.Client/MobState/MobStateComponent.cs deleted file mode 100644 index 55d67780ef..0000000000 --- a/Content.Client/MobState/MobStateComponent.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Content.Shared.MobState; -using Content.Shared.MobState.Components; -using Content.Shared.MobState.State; -using Robust.Shared.GameObjects; - -namespace Content.Client.MobState -{ - [RegisterComponent] - [ComponentReference(typeof(SharedMobStateComponent))] - [ComponentReference(typeof(IMobStateComponent))] - public class MobStateComponent : SharedMobStateComponent - { - } -} diff --git a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs index f5b9fc5294..0b242319cd 100644 --- a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs +++ b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; using Content.Server.Damage; using Content.Shared.Damage; -using Content.Shared.Damage.Components; +using Content.Shared.Damage.Prototypes; using Content.Shared.MobState; using NUnit.Framework; using Robust.Shared.GameObjects; @@ -21,7 +21,7 @@ namespace Content.IntegrationTests.Tests.Commands id: DamageableDummy components: - type: Damageable - damageContainer: biologicalDamageContainer + damageContainer: Biological - type: MobState thresholds: 0: !type:NormalMobState {} @@ -47,15 +47,17 @@ namespace Content.IntegrationTests.Tests.Commands var human = entityManager.SpawnEntity("DamageableDummy", MapCoordinates.Nullspace); // Sanity check - Assert.True(human.TryGetComponent(out IDamageableComponent damageable)); + Assert.True(human.TryGetComponent(out DamageableComponent damageable)); Assert.True(human.TryGetComponent(out IMobStateComponent mobState)); + mobState.UpdateState(0); Assert.That(mobState.IsAlive, Is.True); Assert.That(mobState.IsCritical, Is.False); Assert.That(mobState.IsDead, Is.False); Assert.That(mobState.IsIncapacitated, Is.False); // Kill the entity - damageable.TryChangeDamage(prototypeManager.Index("Toxin"), 10000000, true); + DamageSpecifier damage = new(prototypeManager.Index("Toxin"), 10000000); + EntitySystem.Get().TryChangeDamage(human.Uid, damage, true); // Check that it is dead Assert.That(mobState.IsAlive, Is.False); diff --git a/Content.IntegrationTests/Tests/Damageable/AllSupportDamageableTest.cs b/Content.IntegrationTests/Tests/Damageable/AllSupportDamageableTest.cs deleted file mode 100644 index 97db32a968..0000000000 --- a/Content.IntegrationTests/Tests/Damageable/AllSupportDamageableTest.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using Content.Shared.Damage; -using Content.Shared.Damage.Components; -using NUnit.Framework; -using Robust.Shared.GameObjects; -using Robust.Shared.Map; -using Robust.Shared.Prototypes; - -namespace Content.IntegrationTests.Tests.Damageable -{ - [TestFixture] - [TestOf(typeof(DamageableComponent))] - public class AllSupportDamageableTest : ContentIntegrationTest - { - private const string AllDamageDamageableEntityId = "TestAllDamageDamageableEntityId"; - - /// - /// Test a damageContainer with all types supported. - /// - /// - /// As this should also loads in the damage groups & types in the actual damage.yml, this should also act as a basic test to see if damage.yml is set up properly. - /// - [Test] - public async Task TestAllSupportDamageableComponent() - { - var server = StartServerDummyTicker(); - await server.WaitIdleAsync(); - - var sEntityManager = server.ResolveDependency(); - var sMapManager = server.ResolveDependency(); - var sPrototypeManager = server.ResolveDependency(); - - IEntity sFullyDamageableEntity; - IDamageableComponent sFullyDamageableComponent = null; - - await server.WaitPost(() => - { - var mapId = sMapManager.NextMapId(); - var coordinates = new MapCoordinates(0, 0, mapId); - sMapManager.CreateMap(mapId); - - // When prototypes are loaded using the ExtraPrototypes option, they seem to be loaded first? - // Or at least, no damage prototypes were loaded in by the time that the damageContainer here is loaded. - // So for now doing explicit loading of prototypes. - // I have no idea what I am doing, but it works. - sPrototypeManager.LoadString($@" -# we want to test the all damage container -- type: damageContainer - id: testAllDamageContainer - supportAll: true - -# create entities -- type: entity - id: {AllDamageDamageableEntityId} - name: {AllDamageDamageableEntityId} - components: - - type: Damageable - damageContainer: testAllDamageContainer -"); - - sFullyDamageableEntity = sEntityManager.SpawnEntity(AllDamageDamageableEntityId, coordinates); - sFullyDamageableComponent = sFullyDamageableEntity.GetComponent(); - - }); - - await server.WaitRunTicks(5); - - await server.WaitAssertion(() => - { - - // First check that there actually are any damage types/groups - // This test depends on a non-empty damage.yml - Assert.That(sPrototypeManager.EnumeratePrototypes().ToList().Count, Is.GreaterThan(0)); - Assert.That(sPrototypeManager.EnumeratePrototypes().ToList().Count, Is.GreaterThan(0)); - - - // Can we set and get all damage. - Assert.That(sFullyDamageableComponent.TrySetAllDamage(-10), Is.False); - Assert.That(sFullyDamageableComponent.TrySetAllDamage(0), Is.True); - - // Test that the all damage container supports every damage type, and that we can get, set, and change - // every type with the expected results. Notable: if the damage does not change, they all return false - var initialDamage = 10; - foreach (var damageType in sPrototypeManager.EnumeratePrototypes()) - { - var damage = initialDamage; - Assert.That(sFullyDamageableComponent.IsSupportedDamageType(damageType)); - Assert.That(sFullyDamageableComponent.TrySetDamage(damageType, -damage), Is.False); - Assert.That(sFullyDamageableComponent.TrySetDamage(damageType, damage), Is.True); - Assert.That(sFullyDamageableComponent.TrySetDamage(damageType, damage), Is.True); // intentional duplicate - Assert.That(sFullyDamageableComponent.GetDamage(damageType), Is.EqualTo(damage)); - Assert.That(sFullyDamageableComponent.TryChangeDamage(damageType, -damage / 2, true), Is.True); - Assert.That(sFullyDamageableComponent.TryGetDamage(damageType, out damage), Is.True); - Assert.That(damage, Is.EqualTo(initialDamage/2)); - Assert.That(sFullyDamageableComponent.TryChangeDamage(damageType, damage, true), Is.True); - Assert.That(sFullyDamageableComponent.GetDamage(damageType), Is.EqualTo(2* damage)); - Assert.That(sFullyDamageableComponent.TryChangeDamage(damageType, 0, true), Is.False); - } - // And again, for every group - foreach (var damageGroup in sPrototypeManager.EnumeratePrototypes()) - { - var damage = initialDamage; - var groupSize = damageGroup.DamageTypes.Count(); - Assert.That(sFullyDamageableComponent.IsFullySupportedDamageGroup(damageGroup)); - Assert.That(sFullyDamageableComponent.IsApplicableDamageGroup(damageGroup)); - Assert.That(sFullyDamageableComponent.TrySetDamage(damageGroup, -damage), Is.False); - Assert.That(sFullyDamageableComponent.TrySetDamage(damageGroup, damage), Is.True); - Assert.That(sFullyDamageableComponent.TrySetDamage(damageGroup, damage), Is.True); // intentional duplicate - Assert.That(sFullyDamageableComponent.GetDamage(damageGroup), Is.EqualTo(damage * groupSize)); - Assert.That(sFullyDamageableComponent.TryChangeDamage(damageGroup, -groupSize*damage / 2, true), Is.True); - Assert.That(sFullyDamageableComponent.TryGetDamage(damageGroup, out damage), Is.True); - Assert.That(damage, Is.EqualTo(groupSize* initialDamage/2)); - Assert.That(sFullyDamageableComponent.TryChangeDamage(damageGroup, damage, true), Is.True); - Assert.That(sFullyDamageableComponent.GetDamage(damageGroup), Is.EqualTo(2*damage)); - Assert.That(sFullyDamageableComponent.TryChangeDamage(damageGroup, 0, true), Is.False); - } - }); - } - } -} diff --git a/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs b/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs index f432ee559e..133b69c50b 100644 --- a/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs +++ b/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs @@ -1,7 +1,7 @@ using System.Linq; using System.Threading.Tasks; using Content.Shared.Damage; -using Content.Shared.Damage.Components; +using Content.Shared.Damage.Prototypes; using NUnit.Framework; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -11,75 +11,76 @@ namespace Content.IntegrationTests.Tests.Damageable { [TestFixture] [TestOf(typeof(DamageableComponent))] + [TestOf(typeof(DamageableSystem))] public class DamageableTest : ContentIntegrationTest { - private const string DamageableEntityId = "TestDamageableEntityId"; - private const string Group1Id = "TestGroup1"; - private const string Group2Id = "TestGroup2"; - private const string Group3Id = "TestGroup3"; - private string Prototypes = $@" + private const string Prototypes = @" # Define some damage groups - type: damageType - id: TestDamage11 + id: TestDamage1 - type: damageType - id: TestDamage21 + id: TestDamage2a - type: damageType - id: TestDamage22 + id: TestDamage2b - type: damageType - id: TestDamage31 + id: TestDamage3a - type: damageType - id: TestDamage32 + id: TestDamage3b - type: damageType - id: TestDamage33 + id: TestDamage3c # Define damage Groups with 1,2,3 damage types - type: damageGroup - id: {Group1Id} + id: TestGroup1 damageTypes: - - TestDamage11 + - TestDamage1 - type: damageGroup - id: {Group2Id} + id: TestGroup2 damageTypes: - - TestDamage21 - - TestDamage22 + - TestDamage2a + - TestDamage2b - type: damageGroup - id: {Group3Id} + id: TestGroup3 damageTypes: - - TestDamage31 - - TestDamage32 - - TestDamage33 + - TestDamage3a + - TestDamage3b + - TestDamage3c -# we want to test a container that supports only full groups -# we will also give full support for group 2 IMPLICITLY by specifying all of its members are supported. +- type: resistanceSet + id: testResistances +# this space is intentionally left blank + +# This container should not support TestDamage1 or TestDamage2b - type: damageContainer - id: testSomeDamageContainer + id: testDamageContainer + defaultResistanceSet: testResistances supportedGroups: - - {Group3Id} + - TestGroup3 supportedTypes: - - TestDamage21 - - TestDamage22 + - TestDamage2a - type: entity - id: {DamageableEntityId} - name: {DamageableEntityId} + id: TestDamageableEntityId + name: TestDamageableEntityId components: - type: Damageable - damageContainer: testSomeDamageContainer + damageContainer: testDamageContainer "; - /// - /// Test a standard damageable components - /// - /// - /// Only test scenarios where each damage type is a member of exactly one group, and all damageable components support whole groups, not lone damage types. - /// + // public bool & function to determine whether dealing damage resulted in actual damage change + public bool DamageChanged = false; + public void DamageChangedListener(EntityUid _, DamageableComponent comp, DamageChangedEvent args) + { + DamageChanged = true; + } + [Test] public async Task TestDamageableComponents() { @@ -93,360 +94,150 @@ namespace Content.IntegrationTests.Tests.Damageable var sEntityManager = server.ResolveDependency(); var sMapManager = server.ResolveDependency(); var sPrototypeManager = server.ResolveDependency(); + var sEntitySystemManager = server.ResolveDependency(); - IEntity sDamageableEntity; - IDamageableComponent sDamageableComponent = null; + sEntityManager.EventBus.SubscribeLocalEvent(DamageChangedListener); + + IEntity sDamageableEntity = null; + DamageableComponent sDamageableComponent = null; + DamageableSystem sDamageableSystem = null; DamageGroupPrototype group1 = default!; DamageGroupPrototype group2 = default!; DamageGroupPrototype group3 = default!; + DamageTypePrototype type1 = default!; + DamageTypePrototype type2a = default!; + DamageTypePrototype type2b = default!; + DamageTypePrototype type3a = default!; + DamageTypePrototype type3b = default!; + DamageTypePrototype type3c = default!; + + int typeDamage, groupDamage; + await server.WaitPost(() => { var mapId = sMapManager.NextMapId(); var coordinates = new MapCoordinates(0, 0, mapId); sMapManager.CreateMap(mapId); - sDamageableEntity = sEntityManager.SpawnEntity(DamageableEntityId, coordinates); - sDamageableComponent = sDamageableEntity.GetComponent(); + sDamageableEntity = sEntityManager.SpawnEntity("TestDamageableEntityId", coordinates); + sDamageableComponent = sDamageableEntity.GetComponent(); + sDamageableSystem = sEntitySystemManager.GetEntitySystem(); - group1 = sPrototypeManager.Index(Group1Id); - group2 = sPrototypeManager.Index(Group2Id); - group3 = sPrototypeManager.Index(Group3Id); + group1 = sPrototypeManager.Index("TestGroup1"); + group2 = sPrototypeManager.Index("TestGroup2"); + group3 = sPrototypeManager.Index("TestGroup3"); + type1 = sPrototypeManager.Index("TestDamage1"); + type2a = sPrototypeManager.Index("TestDamage2a"); + type2b = sPrototypeManager.Index("TestDamage2b"); + type3a = sPrototypeManager.Index("TestDamage3a"); + type3b = sPrototypeManager.Index("TestDamage3b"); + type3c = sPrototypeManager.Index("TestDamage3c"); }); await server.WaitRunTicks(5); await server.WaitAssertion(() => { - // Check that the correct groups are supported by the container - Assert.That(sDamageableComponent.IsApplicableDamageGroup(group1), Is.False); - Assert.That(sDamageableComponent.IsApplicableDamageGroup(group2), Is.True); - Assert.That(sDamageableComponent.IsApplicableDamageGroup(group3), Is.True); - Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group1), Is.False); - Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group2), Is.True); - Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group3), Is.True); + var uid = sDamageableEntity.Uid; - // Check that the correct types are supported: - foreach (var group in sPrototypeManager.EnumeratePrototypes()) + // Check that the correct types are supported. + Assert.That(sDamageableComponent.Damage.DamageDict.ContainsKey(type1.ID), Is.False); + Assert.That(sDamageableComponent.Damage.DamageDict.ContainsKey(type2a.ID), Is.True); + Assert.That(sDamageableComponent.Damage.DamageDict.ContainsKey(type2b.ID), Is.False); + Assert.That(sDamageableComponent.Damage.DamageDict.ContainsKey(type3a.ID), Is.True); + Assert.That(sDamageableComponent.Damage.DamageDict.ContainsKey(type3b.ID), Is.True); + Assert.That(sDamageableComponent.Damage.DamageDict.ContainsKey(type3c.ID), Is.True); + + // Check that damage is evenly distributed over a group if its a nice multiple + var types = group3.DamageTypes; + var damageToDeal = types.Count() * 5; + DamageSpecifier damage = new(group3, damageToDeal); + + sDamageableSystem.TryChangeDamage(uid, damage, true); + Assert.That(DamageChanged); + DamageChanged = false; + Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal)); + Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(damageToDeal)); + foreach (var type in types) { - foreach(var type in group.DamageTypes) - { - if (sDamageableComponent.IsFullySupportedDamageGroup(group)) - { - Assert.That(sDamageableComponent.IsSupportedDamageType(type), Is.True); - } - else - { - Assert.That(sDamageableComponent.IsSupportedDamageType(type), Is.False); - } - } + Assert.That(sDamageableComponent.Damage.DamageDict.TryGetValue(type, out typeDamage)); + Assert.That(typeDamage, Is.EqualTo(damageToDeal / types.Count())); } - - Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group1), Is.False); - Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group2), Is.True); - Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group3), Is.True); - - // Check that damage works properly if perfectly divisible among group members - int damageToDeal, groupDamage, typeDamage; ; - Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0)); - foreach (var damageGroup in sDamageableComponent.FullySupportedDamageGroups) + // Heal + sDamageableSystem.TryChangeDamage(uid, -damage); + Assert.That(DamageChanged); + DamageChanged = false; + Assert.That(sDamageableComponent.TotalDamage, Is.Zero); + Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(0)); + foreach (var type in types) { - var types = damageGroup.DamageTypes; - - // Damage - damageToDeal = types.Count() * 5; - Assert.That(sDamageableComponent.TryChangeDamage(damageGroup, damageToDeal, true), Is.True); - Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal)); - Assert.That(sDamageableComponent.TryGetDamage(damageGroup, out groupDamage), Is.True); - Assert.That(groupDamage, Is.EqualTo(damageToDeal)); - - foreach (var type in types) - { - Assert.That(sDamageableComponent.TryGetDamage(type, out typeDamage), Is.True); - Assert.That(typeDamage, Is.EqualTo(damageToDeal / types.Count())); - } - - // Heal - Assert.That(sDamageableComponent.TryChangeDamage(damageGroup, -damageToDeal, true), Is.True); - Assert.That(sDamageableComponent.TotalDamage, Is.Zero); - Assert.That(sDamageableComponent.TryGetDamage(damageGroup, out groupDamage), Is.True); - Assert.That(groupDamage, Is.Zero); - - foreach (var type in types) - { - Assert.That(sDamageableComponent.TryGetDamage(type, out typeDamage), Is.True); - Assert.That(typeDamage, Is.Zero); - } + Assert.That(sDamageableComponent.Damage.DamageDict.TryGetValue(type, out typeDamage)); + Assert.That(typeDamage, Is.Zero); } // Check that damage works properly if it is NOT perfectly divisible among group members - foreach (var damageGroup in sDamageableComponent.FullySupportedDamageGroups) + types = group3.DamageTypes; + damageToDeal = types.Count() * 5 - 1; + damage = new DamageSpecifier(group3, damageToDeal); + sDamageableSystem.TryChangeDamage(uid, damage, true); + Assert.That(DamageChanged); + DamageChanged = false; + Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal)); + Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(damageToDeal)); + // integer rounding. In this case, first member gets 1 less than others. + Assert.That(sDamageableComponent.Damage.DamageDict[type3a.ID], Is.EqualTo(damageToDeal / types.Count())); + Assert.That(sDamageableComponent.Damage.DamageDict[type3b.ID], Is.EqualTo(1 + damageToDeal / types.Count())); + Assert.That(sDamageableComponent.Damage.DamageDict[type3c.ID], Is.EqualTo(1 + damageToDeal / types.Count())); + + // Heal + sDamageableSystem.TryChangeDamage(uid, -damage); + Assert.That(DamageChanged); + DamageChanged = false; + Assert.That(sDamageableComponent.TotalDamage, Is.Zero); + Assert.That(sDamageableComponent.DamagePerGroup[group3.ID], Is.EqualTo(0)); + foreach (var type in types) { - var types = damageGroup.DamageTypes; - - // Damage - damageToDeal = types.Count() * 5 - 1; - Assert.That(sDamageableComponent.TryChangeDamage(damageGroup, damageToDeal, true), Is.True); - Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal)); - Assert.That(sDamageableComponent.TryGetDamage(damageGroup, out groupDamage), Is.True); - Assert.That(groupDamage, Is.EqualTo(damageToDeal)); - - foreach (var type in types) - { - Assert.That(sDamageableComponent.TryGetDamage(type, out typeDamage), Is.True); - float targetDamage = ((float) damageToDeal) / types.Count(); - Assert.That(typeDamage, Is.InRange(targetDamage - 1, targetDamage + 1)); - } - - // Heal - Assert.That(sDamageableComponent.TryChangeDamage(damageGroup, -damageToDeal, true), Is.True); - Assert.That(sDamageableComponent.TotalDamage, Is.Zero); - Assert.That(sDamageableComponent.TryGetDamage(damageGroup, out groupDamage), Is.True); - Assert.That(groupDamage, Is.Zero); - - foreach (var type in types) - { - Assert.That(sDamageableComponent.TryGetDamage(type, out typeDamage), Is.True); - Assert.That(typeDamage, Is.Zero); - } + Assert.That(sDamageableComponent.Damage.DamageDict.TryGetValue(type, out typeDamage)); + Assert.That(typeDamage, Is.Zero); } // Test that unsupported groups return false when setting/getting damage (and don't change damage) Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0)); - foreach (var damageGroup in sPrototypeManager.EnumeratePrototypes()) - { - if (sDamageableComponent.IsFullySupportedDamageGroup(damageGroup)) - { - continue; - } - - Assert.That(sDamageableComponent.IsApplicableDamageGroup(damageGroup), Is.False); - - var types = damageGroup.DamageTypes; - damageToDeal = types.Count() * 5; - - foreach (var type in types) - { - Assert.That(sDamageableComponent.IsSupportedDamageType(type), Is.False); - } -; - Assert.That(sDamageableComponent.TryChangeDamage(damageGroup, damageToDeal, true), Is.False); - Assert.That(sDamageableComponent.TryGetDamage(damageGroup, out groupDamage), Is.False); - - foreach (var type in types) - { - Assert.That(sDamageableComponent.TryChangeDamage(type, damageToDeal, true), Is.False); - Assert.That(sDamageableComponent.TryGetDamage(type, out typeDamage), Is.False); - } - } - // Did damage change? + damage = new DamageSpecifier(group1, 10) + new DamageSpecifier(type2b, 10); + sDamageableSystem.TryChangeDamage(uid, damage, true); + Assert.That(DamageChanged, Is.False); + Assert.That(sDamageableComponent.DamagePerGroup.TryGetValue(group1.ID, out groupDamage), Is.False); + Assert.That(sDamageableComponent.Damage.DamageDict.TryGetValue(type1.ID, out typeDamage), Is.False); Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0)); - - // Test total damage function - damageToDeal = 10; - - Assert.True(sDamageableComponent.TryChangeDamage(group3, damageToDeal, true)); - Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal)); - - var totalTypeDamage = 0; - - foreach (var damageType in sDamageableComponent.SupportedDamageTypes) - { - Assert.True(sDamageableComponent.TryGetDamage(damageType, out typeDamage)); - Assert.That(typeDamage, Is.LessThanOrEqualTo(damageToDeal)); - - totalTypeDamage += typeDamage; - } - Assert.That(totalTypeDamage, Is.EqualTo(damageToDeal)); - - - // Test healing all damage - Assert.That(sDamageableComponent.TrySetAllDamage(0)); + // Test SetAll function + sDamageableSystem.SetAllDamage(sDamageableComponent, 10); + Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(10 * sDamageableComponent.Damage.DamageDict.Count())); + sDamageableSystem.SetAllDamage(sDamageableComponent, 0); Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0)); - // Test preferential healing - damageToDeal = 12; - var damageTypes = group3.DamageTypes.ToArray(); + // Test 'wasted' healing + sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(type3a, 5)); + sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(type3b, 7)); + sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(group3, -11)); + Assert.That(sDamageableComponent.Damage.DamageDict[type3a.ID], Is.EqualTo(2)); + Assert.That(sDamageableComponent.Damage.DamageDict[type3b.ID], Is.EqualTo(3)); + Assert.That(sDamageableComponent.Damage.DamageDict[type3c.ID], Is.EqualTo(0)); - // Deal damage - Assert.True(sDamageableComponent.TryChangeDamage(damageTypes[0], 17)); - Assert.True(sDamageableComponent.TryChangeDamage(damageTypes[1], 31)); - Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(48)); - - // Heal group damage - Assert.True(sDamageableComponent.TryChangeDamage(group3, -11)); - - // Check healing (3 + 9) - Assert.That(sDamageableComponent.GetDamage(damageTypes[0]), Is.EqualTo(14)); - Assert.That(sDamageableComponent.GetDamage(damageTypes[1]), Is.EqualTo(23)); - Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(37)); - - // Heal group damage - Assert.True(sDamageableComponent.TryChangeDamage(group3, -36)); - - // Check healing (13 + 23) - Assert.That(sDamageableComponent.GetDamage(damageTypes[0]), Is.EqualTo(1)); - Assert.That(sDamageableComponent.GetDamage(damageTypes[1]), Is.EqualTo(0)); - Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(1)); - - //Check Damage - Assert.True(sDamageableComponent.TryGetDamage(damageTypes[0], out typeDamage)); - Assert.That(typeDamage, Is.LessThanOrEqualTo(damageToDeal)); - }); - } - - - private const string SharedDamageTypeId = "TestSharedDamage"; - private const string UnsupportedDamageTypeId = "TestUnsupportedDamage"; - private string Prototypes2 = $@" -- type: damageType - id: {SharedDamageTypeId} - -- type: damageType - id: {UnsupportedDamageTypeId} - -- type: damageType - id: TestDamage1 - -- type: damageType - id: TestDamage2 - -- type: damageGroup - id: {Group1Id} - damageTypes: - - {SharedDamageTypeId} - -- type: damageGroup - id: {Group2Id} - damageTypes: - - {SharedDamageTypeId} - - TestDamage1 - -- type: damageGroup - id: {Group3Id} - damageTypes: - - {SharedDamageTypeId} - - TestDamage2 - - {UnsupportedDamageTypeId} - -# we want to test a container that only partially supports a group: -- type: damageContainer - id: TestPartiallySupported - supportedGroups: - - {Group2Id} - supportedTypes: - - TestDamage2 - - TestDamage1 -# does NOT support type {UnsupportedDamageTypeId}, and thus does not fully support group {Group3Id} -# TestDamage1 is added twice because it is also in {Group2Id}. This should not cause errors. - -# create entities -- type: entity - id: {DamageableEntityId} - name: {DamageableEntityId} - components: - - type: Damageable - damageContainer: TestPartiallySupported -"; - - /// - /// Generalized damageable component tests. - /// - /// - /// Test scenarios where damage types are members of more than one group, or where a component only supports a subset of a group. - /// - [Test] - public async Task TestGeneralizedDamageableComponent() - { - var server = StartServerDummyTicker(new ServerContentIntegrationOption - { - ExtraPrototypes = Prototypes2 - }); - - await server.WaitIdleAsync(); - - var sEntityManager = server.ResolveDependency(); - var sMapManager = server.ResolveDependency(); - var sPrototypeManager = server.ResolveDependency(); - - IEntity sDamageableEntity; - IDamageableComponent sDamageableComponent = null; - - DamageGroupPrototype group1 = default!; - DamageGroupPrototype group2 = default!; - DamageGroupPrototype group3 = default!; - - DamageTypePrototype SharedDamageType = default!; - DamageTypePrototype UnsupportedDamageType = default!; - - await server.WaitPost(() => - { - var mapId = sMapManager.NextMapId(); - var coordinates = new MapCoordinates(0, 0, mapId); - sMapManager.CreateMap(mapId); - - sDamageableEntity = sEntityManager.SpawnEntity(DamageableEntityId, coordinates); - sDamageableComponent = sDamageableEntity.GetComponent(); - - group1 = sPrototypeManager.Index(Group1Id); - group2 = sPrototypeManager.Index(Group2Id); - group3 = sPrototypeManager.Index(Group3Id); - - SharedDamageType = sPrototypeManager.Index(SharedDamageTypeId); - UnsupportedDamageType = sPrototypeManager.Index(UnsupportedDamageTypeId); - }); - - await server.WaitRunTicks(5); - - await server.WaitAssertion(() => - { - // All damage types should be applicable - Assert.That(sDamageableComponent.IsApplicableDamageGroup(group1), Is.True); - Assert.That(sDamageableComponent.IsApplicableDamageGroup(group2), Is.True); - Assert.That(sDamageableComponent.IsApplicableDamageGroup(group3), Is.True); - - // But not all should be fully supported - Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group1), Is.True); - Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group2), Is.True); - Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group3), Is.False); - - // Check that the correct damage types are supported - Assert.That(sDamageableComponent.IsSupportedDamageType(SharedDamageType), Is.True); - - // Check that if we deal damage using a type appearing in multiple groups, nothing goes wrong. - var damage = 12; - Assert.That(sDamageableComponent.TryChangeDamage(SharedDamageType, damage), Is.True); - Assert.That(sDamageableComponent.GetDamage(SharedDamageType), Is.EqualTo(damage)); - Assert.That(sDamageableComponent.GetDamage(group1), Is.EqualTo(damage)); - Assert.That(sDamageableComponent.GetDamage(group2), Is.EqualTo(damage)); - Assert.That(sDamageableComponent.GetDamage(group3), Is.EqualTo(damage)); - Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damage)); - - // Check that if we deal damage using a group that is not fully supported, the damage is reduced - // Note that if damage2 were not neatly divisible by 3, the actual damage reduction would be subject to integer rounding. - // How much exactly the damage gets reduced then would depend on the order that the groups were defined in the yaml file - // Here we deal 9 damage. It should apply 3 damage to each type, but one type is ignored, resulting in 6 total damage. - // However, the damage in group2 and group3 only changes because of one type that overlaps, so they only change by 3 - Assert.That(sDamageableComponent.TryChangeDamage(group3, 9), Is.True); - Assert.That(sDamageableComponent.GetDamage(group1), Is.EqualTo(damage + 3)); - Assert.That(sDamageableComponent.GetDamage(group2), Is.EqualTo(damage + 3)); - Assert.That(sDamageableComponent.GetDamage(group3), Is.EqualTo(damage + 6)); - Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damage + 6)); - - // Now we check that when healing, no damage is wasted. - // Because SharedDamageType has the most damage in group3 (15 vs 3), it will be healed more than the other. - // Expect that, up to integer rounding, one is healed 5* more than the other. - // We will use a number that does not divide nicely, there will be some integer rounding. - Assert.That(sDamageableComponent.TryChangeDamage(group3, -7), Is.True); - Assert.That(sDamageableComponent.GetDamage(group1), Is.EqualTo(damage + 3 - 5)); - Assert.That(sDamageableComponent.GetDamage(group2), Is.EqualTo(damage + 3 - 5)); - Assert.That(sDamageableComponent.GetDamage(group3), Is.EqualTo(damage + 6 - 7)); - Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damage + 6 - 7)); + // Test Over-Healing + sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(group3, -100)); + Assert.That(DamageChanged); + DamageChanged = false; + Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0)); + // Test that if no health change occurred, returns false + sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(group3, -100)); + Assert.That(DamageChanged, Is.False); + Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0)); }); } } diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs index a8c735e64b..39b8ad74c9 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; using Content.Server.Destructible.Thresholds.Triggers; using Content.Shared.Damage; -using Content.Shared.Damage.Components; +using Content.Shared.Damage.Prototypes; using NUnit.Framework; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -21,11 +21,7 @@ namespace Content.IntegrationTests.Tests.Destructible { var server = StartServerDummyTicker(new ServerContentIntegrationOption { - ExtraPrototypes = Prototypes, - ContentBeforeIoC = () => - { - IoCManager.Resolve().RegisterClass(); - } + ExtraPrototypes = Prototypes }); await server.WaitIdleAsync(); @@ -33,10 +29,12 @@ namespace Content.IntegrationTests.Tests.Destructible var sEntityManager = server.ResolveDependency(); var sMapManager = server.ResolveDependency(); var sPrototypeManager = server.ResolveDependency(); + var sEntitySystemManager = server.ResolveDependency(); - IEntity sDestructibleEntity; - IDamageableComponent sDamageableComponent = null; - TestThresholdListenerComponent sThresholdListenerComponent = null; + IEntity sDestructibleEntity = null; + DamageableComponent sDamageableComponent = null; + TestDestructibleListenerSystem sTestThresholdListenerSystem = null; + DamageableSystem sDamageableSystem = null; await server.WaitPost(() => { @@ -45,15 +43,16 @@ namespace Content.IntegrationTests.Tests.Destructible sMapManager.CreateMap(mapId); sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDamageGroupEntityId, coordinates); - sDamageableComponent = sDestructibleEntity.GetComponent(); - sThresholdListenerComponent = sDestructibleEntity.GetComponent(); + sDamageableComponent = sDestructibleEntity.GetComponent(); + sTestThresholdListenerSystem = sEntitySystemManager.GetEntitySystem(); + sDamageableSystem = sEntitySystemManager.GetEntitySystem(); }); await server.WaitRunTicks(5); await server.WaitAssertion(() => { - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); }); await server.WaitAssertion(() => @@ -61,26 +60,29 @@ namespace Content.IntegrationTests.Tests.Destructible var bruteDamageGroup = sPrototypeManager.Index("TestBrute"); var burnDamageGroup = sPrototypeManager.Index("TestBurn"); + DamageSpecifier bruteDamage = new(bruteDamageGroup,5); + DamageSpecifier burnDamage = new(burnDamageGroup,5); + // Raise brute damage to 5 - Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 5, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bruteDamage, true); // No thresholds reached yet, the earliest one is at 10 damage - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise brute damage to 10 - Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 5, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bruteDamage, true); // No threshold reached, burn needs to be 10 as well - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise burn damage to 10 - Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, burnDamage * 2, true); // One threshold reached, brute 10 + burn 10 - Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); // Threshold brute 10 + burn 10 - var msg = sThresholdListenerComponent.ThresholdsReached[0]; + var msg = sTestThresholdListenerSystem.ThresholdsReached[0]; var threshold = msg.Threshold; // Check that it matches the YAML prototype @@ -94,55 +96,55 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.IsInstanceOf(trigger.Triggers[0]); Assert.IsInstanceOf(trigger.Triggers[1]); - sThresholdListenerComponent.ThresholdsReached.Clear(); + sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Raise brute damage to 20 - Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bruteDamage * 2, true); // No new thresholds reached - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise burn damage to 20 - Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, burnDamage * 2, true); // No new thresholds reached - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Lower brute damage to 0 - Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, -20, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bruteDamage * -10); + Assert.That(sDamageableComponent.TotalDamage,Is.EqualTo(20)); // No new thresholds reached, healing should not trigger it - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise brute damage back up to 10 - Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bruteDamage * 2, true); - // 10 brute + 10 burn threshold reached, brute was healed and brought back to its threshold amount and burn stayed the same - Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + // 10 brute + 10 burn threshold reached, brute was healed and brought back to its threshold amount and slash stayed the same + Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); - sThresholdListenerComponent.ThresholdsReached.Clear(); + sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Heal both classes of damage to 0 - Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, -10, true)); - Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, -20, true)); + sDamageableSystem.SetAllDamage(sDamageableComponent, 0); // No new thresholds reached, healing should not trigger it - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise brute damage to 10 - Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bruteDamage * 2, true); // No new thresholds reached - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise burn damage to 10 - Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, burnDamage * 2, true); // Both classes of damage were healed and then raised again, the threshold should have been reached as triggers once is default false - Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); // Threshold brute 10 + burn 10 - msg = sThresholdListenerComponent.ThresholdsReached[0]; + msg = sTestThresholdListenerSystem.ThresholdsReached[0]; threshold = msg.Threshold; // Check that it matches the YAML prototype @@ -156,29 +158,28 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.IsInstanceOf(trigger.Triggers[0]); Assert.IsInstanceOf(trigger.Triggers[1]); - sThresholdListenerComponent.ThresholdsReached.Clear(); + sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Change triggers once to true threshold.TriggersOnce = true; // Heal brute and burn back to 0 - Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, -10, true)); - Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, -10, true)); + sDamageableSystem.SetAllDamage(sDamageableComponent, 0); // No new thresholds reached from healing - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise brute damage to 10 - Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bruteDamage * 2, true); // No new thresholds reached - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise burn damage to 10 - Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, burnDamage * 2, true); // No new thresholds reached as triggers once is set to true and it already triggered before - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); }); } } diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs index 2868869de1..92f299791c 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs @@ -1,7 +1,7 @@ using System.Threading.Tasks; using Content.Server.Destructible.Thresholds.Triggers; using Content.Shared.Damage; -using Content.Shared.Damage.Components; +using Content.Shared.Damage.Prototypes; using NUnit.Framework; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -21,21 +21,19 @@ namespace Content.IntegrationTests.Tests.Destructible { var server = StartServerDummyTicker(new ServerContentIntegrationOption { - ExtraPrototypes = Prototypes, - ContentBeforeIoC = () => - { - IoCManager.Resolve().RegisterClass(); - } + ExtraPrototypes = Prototypes }); await server.WaitIdleAsync(); var sEntityManager = server.ResolveDependency(); var sMapManager = server.ResolveDependency(); + var sEntitySystemManager = server.ResolveDependency(); - IEntity sDestructibleEntity; - IDamageableComponent sDamageableComponent = null; - TestThresholdListenerComponent sThresholdListenerComponent = null; + IEntity sDestructibleEntity = null; + DamageableComponent sDamageableComponent = null; + TestDestructibleListenerSystem sTestThresholdListenerSystem = null; + DamageableSystem sDamageableSystem = null; await server.WaitPost(() => { @@ -44,15 +42,16 @@ namespace Content.IntegrationTests.Tests.Destructible sMapManager.CreateMap(mapId); sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDamageTypeEntityId, coordinates); - sDamageableComponent = sDestructibleEntity.GetComponent(); - sThresholdListenerComponent = sDestructibleEntity.GetComponent(); + sDamageableComponent = sDestructibleEntity.GetComponent(); + sTestThresholdListenerSystem = sEntitySystemManager.GetEntitySystem(); + sDamageableSystem = sEntitySystemManager.GetEntitySystem(); }); await server.WaitRunTicks(5); await server.WaitAssertion(() => { - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); }); await server.WaitAssertion(() => @@ -60,26 +59,29 @@ namespace Content.IntegrationTests.Tests.Destructible var bluntDamageType = IoCManager.Resolve().Index("TestBlunt"); var slashDamageType = IoCManager.Resolve().Index("TestSlash"); + var bluntDamage = new DamageSpecifier(bluntDamageType,5); + var slashDamage = new DamageSpecifier(slashDamageType,5); + // Raise blunt damage to 5 - Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 5, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage, true); // No thresholds reached yet, the earliest one is at 10 damage - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise blunt damage to 10 - Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 5, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage, true); // No threshold reached, slash needs to be 10 as well - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise slash damage to 10 - Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, slashDamage * 2, true); // One threshold reached, blunt 10 + slash 10 - Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); // Threshold blunt 10 + slash 10 - var msg = sThresholdListenerComponent.ThresholdsReached[0]; + var msg = sTestThresholdListenerSystem.ThresholdsReached[0]; var threshold = msg.Threshold; // Check that it matches the YAML prototype @@ -93,55 +95,55 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.IsInstanceOf(trigger.Triggers[0]); Assert.IsInstanceOf(trigger.Triggers[1]); - sThresholdListenerComponent.ThresholdsReached.Clear(); + sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Raise blunt damage to 20 - Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage * 2, true); // No new thresholds reached - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise slash damage to 20 - Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, slashDamage * 2, true); // No new thresholds reached - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Lower blunt damage to 0 - Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, -20, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage * -4, true); // No new thresholds reached, healing should not trigger it - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise blunt damage back up to 10 - Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage * 2, true); // 10 blunt + 10 slash threshold reached, blunt was healed and brought back to its threshold amount and slash stayed the same - Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); - sThresholdListenerComponent.ThresholdsReached.Clear(); + sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Heal both types of damage to 0 - Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, -10, true)); - Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, -20, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage * -2, true); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, slashDamage * -4, true); // No new thresholds reached, healing should not trigger it - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise blunt damage to 10 - Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage * 2, true); // No new thresholds reached - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise slash damage to 10 - Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, slashDamage * 2, true); // Both types of damage were healed and then raised again, the threshold should have been reached as triggers once is default false - Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); // Threshold blunt 10 + slash 10 - msg = sThresholdListenerComponent.ThresholdsReached[0]; + msg = sTestThresholdListenerSystem.ThresholdsReached[0]; threshold = msg.Threshold; // Check that it matches the YAML prototype @@ -155,29 +157,29 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.IsInstanceOf(trigger.Triggers[0]); Assert.IsInstanceOf(trigger.Triggers[1]); - sThresholdListenerComponent.ThresholdsReached.Clear(); + sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Change triggers once to true threshold.TriggersOnce = true; // Heal blunt and slash back to 0 - Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, -10, true)); - Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, -10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage * -2, true); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, slashDamage * -2, true); // No new thresholds reached from healing - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise blunt damage to 10 - Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage * 2, true); // No new thresholds reached - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Raise slash damage to 10 - Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, slashDamage * 2, true); // No new thresholds reached as triggers once is set to true and it already triggered before - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); }); } } diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs index faf3fd1c87..29ba6a1f12 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs @@ -3,7 +3,7 @@ using System.Threading.Tasks; using Content.Server.Destructible.Thresholds; using Content.Server.Destructible.Thresholds.Behaviors; using Content.Shared.Damage; -using Content.Shared.Damage.Components; +using Content.Shared.Damage.Prototypes; using NUnit.Framework; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -20,11 +20,7 @@ namespace Content.IntegrationTests.Tests.Destructible { var server = StartServerDummyTicker(new ServerContentIntegrationOption { - ExtraPrototypes = Prototypes, - ContentBeforeIoC = () => - { - IoCManager.Resolve().RegisterClass(); - } + ExtraPrototypes = Prototypes }); await server.WaitIdleAsync(); @@ -32,10 +28,11 @@ namespace Content.IntegrationTests.Tests.Destructible var sEntityManager = server.ResolveDependency(); var sMapManager = server.ResolveDependency(); var sPrototypeManager = server.ResolveDependency(); + var sEntitySystemManager = server.ResolveDependency(); IEntity sDestructibleEntity = null; - IDamageableComponent sDamageableComponent = null; - TestThresholdListenerComponent sThresholdListenerComponent = null; + DamageableComponent sDamageableComponent = null; + TestDestructibleListenerSystem sTestThresholdListenerSystem = null; await server.WaitPost(() => { @@ -44,23 +41,24 @@ namespace Content.IntegrationTests.Tests.Destructible sMapManager.CreateMap(mapId); sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDestructionEntityId, coordinates); - sDamageableComponent = sDestructibleEntity.GetComponent(); - sThresholdListenerComponent = sDestructibleEntity.GetComponent(); + sDamageableComponent = sDestructibleEntity.GetComponent(); + sTestThresholdListenerSystem = sEntitySystemManager.GetEntitySystem(); }); await server.WaitAssertion(() => { var coordinates = sDestructibleEntity.Transform.Coordinates; var bruteDamageGroup = sPrototypeManager.Index("TestBrute"); + DamageSpecifier bruteDamage = new(bruteDamageGroup,50); Assert.DoesNotThrow(() => { - Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 50, true)); + EntitySystem.Get().TryChangeDamage(sDestructibleEntity.Uid, bruteDamage, true); }); - Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); - var threshold = sThresholdListenerComponent.ThresholdsReached[0].Threshold; + var threshold = sTestThresholdListenerSystem.ThresholdsReached[0].Threshold; Assert.That(threshold.Triggered, Is.True); Assert.That(threshold.Behaviors.Count, Is.EqualTo(3)); diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleTestPrototypes.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleTestPrototypes.cs index 5bb0244b88..a5e27a7985 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleTestPrototypes.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleTestPrototypes.cs @@ -27,21 +27,6 @@ namespace Content.IntegrationTests.Tests.Destructible - type: damageType id: TestCold -- type: damageType - id: TestPoison - -- type: damageType - id: TestRadiation - -- type: damageType - id: TestAsphyxiation - -- type: damageType - id: TestBloodloss - -- type: damageType - id: TestCellular - - type: damageGroup id: TestBrute damageTypes: @@ -56,43 +41,6 @@ namespace Content.IntegrationTests.Tests.Destructible - TestShock - TestCold -- type: damageGroup - id: TestAirloss - damageTypes: - - TestAsphyxiation - - TestBloodloss - -- type: damageGroup - id: TestToxin - damageTypes: - - TestPoison - - TestRadiation - -- type: damageGroup - id: TestGenetic - damageTypes: - - TestCellular - -- type: damageContainer - id: TestAllDamageContainer - supportAll: true - - -- type: damageContainer - id: TestBiologicalDamageContainer - supportedGroups: - - TestBrute - - TestBurn - - TestToxin - - TestAirloss - - TestGenetic - -- type: damageContainer - id: TestMetallicDamageContainer - supportedGroups: - - TestBrute - - TestBurn - - type: entity id: {SpawnedEntityId} name: {SpawnedEntityId} @@ -102,7 +50,6 @@ namespace Content.IntegrationTests.Tests.Destructible name: {DestructibleEntityId} components: - type: Damageable - damageContainer: TestMetallicDamageContainer - type: Destructible thresholds: - trigger: @@ -124,14 +71,12 @@ namespace Content.IntegrationTests.Tests.Destructible max: 1 - !type:DoActsBehavior acts: [""Breakage""] - - type: TestThresholdListener - type: entity id: {DestructibleDestructionEntityId} name: {DestructibleDestructionEntityId} components: - type: Damageable - damageContainer: TestMetallicDamageContainer - type: Destructible thresholds: - trigger: @@ -148,14 +93,12 @@ namespace Content.IntegrationTests.Tests.Destructible max: 1 - !type:DoActsBehavior # This must come last as it destroys the entity. acts: [""Destruction""] - - type: TestThresholdListener - type: entity id: {DestructibleDamageTypeEntityId} name: {DestructibleDamageTypeEntityId} components: - type: Damageable - damageContainer: TestMetallicDamageContainer - type: Destructible thresholds: - trigger: @@ -167,14 +110,12 @@ namespace Content.IntegrationTests.Tests.Destructible - !type:DamageTypeTrigger damageType: TestSlash damage: 10 - - type: TestThresholdListener - type: entity id: {DestructibleDamageGroupEntityId} name: {DestructibleDamageGroupEntityId} components: - type: Damageable - damageContainer: TestMetallicDamageContainer - type: Destructible thresholds: - trigger: @@ -185,7 +126,6 @@ namespace Content.IntegrationTests.Tests.Destructible damage: 10 - !type:DamageGroupTrigger damageGroup: TestBurn - damage: 10 - - type: TestThresholdListener"; + damage: 10"; } } diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs index 7f6db82e69..e92225fe69 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs @@ -5,7 +5,7 @@ using Content.Server.Destructible.Thresholds; using Content.Server.Destructible.Thresholds.Behaviors; using Content.Server.Destructible.Thresholds.Triggers; using Content.Shared.Damage; -using Content.Shared.Damage.Components; +using Content.Shared.Damage.Prototypes; using NUnit.Framework; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -17,7 +17,7 @@ namespace Content.IntegrationTests.Tests.Destructible { [TestFixture] [TestOf(typeof(DestructibleComponent))] - [TestOf(typeof(Threshold))] + [TestOf(typeof(DamageThreshold))] public class DestructibleThresholdActivationTest : ContentIntegrationTest { [Test] @@ -25,11 +25,7 @@ namespace Content.IntegrationTests.Tests.Destructible { var server = StartServerDummyTicker(new ServerContentIntegrationOption { - ExtraPrototypes = Prototypes, - ContentBeforeIoC = () => - { - IoCManager.Resolve().RegisterClass(); - } + ExtraPrototypes = Prototypes }); await server.WaitIdleAsync(); @@ -37,11 +33,13 @@ namespace Content.IntegrationTests.Tests.Destructible var sEntityManager = server.ResolveDependency(); var sMapManager = server.ResolveDependency(); var sPrototypeManager = server.ResolveDependency(); + var sEntitySystemManager = server.ResolveDependency(); - IEntity sDestructibleEntity; - IDamageableComponent sDamageableComponent = null; + IEntity sDestructibleEntity = null; ; + DamageableComponent sDamageableComponent = null; DestructibleComponent sDestructibleComponent = null; - TestThresholdListenerComponent sThresholdListenerComponent = null; + TestDestructibleListenerSystem sTestThresholdListenerSystem = null; + DamageableSystem sDamageableSystem = null; await server.WaitPost(() => { @@ -50,34 +48,35 @@ namespace Content.IntegrationTests.Tests.Destructible sMapManager.CreateMap(mapId); sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleEntityId, coordinates); - sDamageableComponent = sDestructibleEntity.GetComponent(); + sDamageableComponent = sDestructibleEntity.GetComponent(); sDestructibleComponent = sDestructibleEntity.GetComponent(); - sThresholdListenerComponent = sDestructibleEntity.GetComponent(); + sTestThresholdListenerSystem = sEntitySystemManager.GetEntitySystem(); + sDamageableSystem = sEntitySystemManager.GetEntitySystem(); }); await server.WaitRunTicks(5); await server.WaitAssertion(() => { - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); }); await server.WaitAssertion(() => { - var bluntDamageType = sPrototypeManager.Index("TestBlunt"); + var bluntDamage = new DamageSpecifier(sPrototypeManager.Index("TestBlunt"), 10); - Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage, true); // No thresholds reached yet, the earliest one is at 20 damage - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); - Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage, true); // Only one threshold reached, 20 - Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); // Threshold 20 - var msg = sThresholdListenerComponent.ThresholdsReached[0]; + var msg = sTestThresholdListenerSystem.ThresholdsReached[0]; var threshold = msg.Threshold; // Check that it matches the YAML prototype @@ -85,15 +84,15 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); - sThresholdListenerComponent.ThresholdsReached.Clear(); + sTestThresholdListenerSystem.ThresholdsReached.Clear(); - Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 30, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage*3, true); // One threshold reached, 50, since 20 already triggered before and it has not been healed below that amount - Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); // Threshold 50 - msg = sThresholdListenerComponent.ThresholdsReached[0]; + msg = sTestThresholdListenerSystem.ThresholdsReached[0]; threshold = msg.Threshold; // Check that it matches the YAML prototype @@ -113,50 +112,50 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); - sThresholdListenerComponent.ThresholdsReached.Clear(); + sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Damage for 50 again, up to 100 now - Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 50, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage*5, true); // No thresholds reached as they weren't healed below the trigger amount - Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + Assert.IsEmpty(sTestThresholdListenerSystem.ThresholdsReached); // Set damage to 0 - sDamageableComponent.TrySetAllDamage(0); + sDamageableSystem.SetAllDamage(sDamageableComponent, 0); // Damage for 100, up to 100 - Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 100, true)); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage*10, true); // Two thresholds reached as damage increased past the previous, 20 and 50 - Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(2)); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(2)); - sThresholdListenerComponent.ThresholdsReached.Clear(); + sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Heal the entity for 40 damage, down to 60 - sDamageableComponent.TryChangeDamage(bluntDamageType, -40, true); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage*-4, true); // Thresholds don't work backwards - Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); // Damage for 10, up to 70 - sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage, true); // Not enough healing to de-trigger a threshold - Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); // Heal by 30, down to 40 - sDamageableComponent.TryChangeDamage(bluntDamageType, -30, true); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage*-3, true); // Thresholds don't work backwards - Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); // Damage up to 50 again - sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage, true); // The 50 threshold should have triggered again, after being healed - Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached.Count, Is.EqualTo(1)); - msg = sThresholdListenerComponent.ThresholdsReached[0]; + msg = sTestThresholdListenerSystem.ThresholdsReached[0]; threshold = msg.Threshold; // Check that it matches the YAML prototype @@ -178,22 +177,22 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.That(threshold.Triggered, Is.True); // Reset thresholds reached - sThresholdListenerComponent.ThresholdsReached.Clear(); + sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Heal all damage - sDamageableComponent.TrySetAllDamage(0); + sDamageableSystem.SetAllDamage(sDamageableComponent, 0); // Damage up to 50 - sDamageableComponent.TryChangeDamage(bluntDamageType, 50, true); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage*5, true); // Check that the total damage matches Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50)); // Both thresholds should have triggered - Assert.That(sThresholdListenerComponent.ThresholdsReached, Has.Exactly(2).Items); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Has.Exactly(2).Items); // Verify the first one, should be the lowest one (20) - msg = sThresholdListenerComponent.ThresholdsReached[0]; + msg = sTestThresholdListenerSystem.ThresholdsReached[0]; var trigger = (DamageTrigger) msg.Threshold.Trigger; Assert.NotNull(trigger); Assert.That(trigger.Damage, Is.EqualTo(20)); @@ -204,7 +203,7 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.That(threshold.Behaviors, Is.Empty); // Verify the second one, should be the highest one (50) - msg = sThresholdListenerComponent.ThresholdsReached[1]; + msg = sTestThresholdListenerSystem.ThresholdsReached[1]; trigger = (DamageTrigger) msg.Threshold.Trigger; Assert.NotNull(trigger); Assert.That(trigger.Damage, Is.EqualTo(50)); @@ -229,10 +228,10 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.That(threshold.Triggered, Is.True); // Reset thresholds reached - sThresholdListenerComponent.ThresholdsReached.Clear(); + sTestThresholdListenerSystem.ThresholdsReached.Clear(); // Heal the entity completely - sDamageableComponent.TrySetAllDamage(0); + sDamageableSystem.SetAllDamage(sDamageableComponent, 0); // Check that the entity has 0 damage Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0)); @@ -245,13 +244,13 @@ namespace Content.IntegrationTests.Tests.Destructible } // Damage the entity up to 50 damage again - sDamageableComponent.TryChangeDamage(bluntDamageType, 50, true); + sDamageableSystem.TryChangeDamage(sDestructibleEntity.Uid, bluntDamage*5, true); // Check that the total damage matches Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50)); // No thresholds should have triggered as they were already triggered before, and they are set to only trigger once - Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); // Set both thresholds to trigger multiple times foreach (var destructibleThreshold in sDestructibleComponent.Thresholds) @@ -264,7 +263,7 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50)); // They shouldn't have been triggered by changing TriggersOnce - Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty); + Assert.That(sTestThresholdListenerSystem.ThresholdsReached, Is.Empty); }); } } diff --git a/Content.IntegrationTests/Tests/Destructible/TestDestructibleListenerSystem.cs b/Content.IntegrationTests/Tests/Destructible/TestDestructibleListenerSystem.cs new file mode 100644 index 0000000000..657b27c412 --- /dev/null +++ b/Content.IntegrationTests/Tests/Destructible/TestDestructibleListenerSystem.cs @@ -0,0 +1,26 @@ +using Content.Server.Destructible; +using Robust.Shared.GameObjects; +using System.Collections.Generic; + +namespace Content.IntegrationTests.Tests.Destructible +{ + /// + /// This is just a system for testing destructible thresholds. Whenever any threshold is reached, this will add that + /// threshold to a list for checking during testing. + /// + public class TestDestructibleListenerSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(AddThresholdsToList); + } + + public void AddThresholdsToList(EntityUid _, DestructibleComponent comp, DamageThresholdReached args) + { + ThresholdsReached.Add(args); + } + + public List ThresholdsReached = new(); + } +} diff --git a/Content.IntegrationTests/Tests/Destructible/TestThresholdListenerComponent.cs b/Content.IntegrationTests/Tests/Destructible/TestThresholdListenerComponent.cs deleted file mode 100644 index 5374de4105..0000000000 --- a/Content.IntegrationTests/Tests/Destructible/TestThresholdListenerComponent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; -using Content.Server.Destructible; -using Robust.Shared.GameObjects; - -namespace Content.IntegrationTests.Tests.Destructible -{ - public class TestThresholdListenerComponent : Component - { - public override string Name => "TestThresholdListener"; - - public List ThresholdsReached { get; } = new(); - - public override void HandleMessage(ComponentMessage message, IComponent component) - { - base.HandleMessage(message, component); - - switch (message) - { - case DestructibleThresholdReachedMessage msg: - ThresholdsReached.Add(msg); - break; - } - } - } -} diff --git a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs index f81e4892f3..073c9ad2f4 100644 --- a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs +++ b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs @@ -1,15 +1,16 @@ -#nullable enable +#nullable enable +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Content.Server.Disposal.Tube.Components; using Content.Server.Disposal.Unit.Components; using Content.Server.Disposal.Unit.EntitySystems; using Content.Server.Power.Components; +using Content.Shared.Coordinates; using NUnit.Framework; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; using Robust.Shared.Map; -using Robust.Shared.Physics; +using Robust.Shared.Timing; namespace Content.IntegrationTests.Tests.Disposal { @@ -19,13 +20,13 @@ namespace Content.IntegrationTests.Tests.Disposal [TestOf(typeof(DisposalUnitComponent))] public class DisposalUnitTest : ContentIntegrationTest { - private void UnitInsert(DisposalUnitComponent unit, bool result, params IEntity[] entities) + private async Task UnitInsert(DisposalUnitComponent unit, bool result, params IEntity[] entities) { + List insertionTasks = new(); foreach (var entity in entities) { - var insertTask = unit.TryInsert(entity); Assert.That(EntitySystem.Get().CanInsert(unit, entity), Is.EqualTo(result)); - insertTask.ContinueWith(task => + var insertTask = unit.TryInsert(entity).ContinueWith(task => { Assert.That(task.Result, Is.EqualTo(result)); if (result) @@ -34,7 +35,9 @@ namespace Content.IntegrationTests.Tests.Disposal Assert.That(entity.Transform.Parent, Is.EqualTo(unit.Owner.Transform)); } }); + insertionTasks.Add(insertTask); } + Task.WaitAll(insertionTasks.ToArray()); } private void UnitContains(DisposalUnitComponent unit, bool result, params IEntity[] entities) @@ -45,9 +48,9 @@ namespace Content.IntegrationTests.Tests.Disposal } } - private void UnitInsertContains(DisposalUnitComponent unit, bool result, params IEntity[] entities) + private async void UnitInsertContains(DisposalUnitComponent unit, bool result, params IEntity[] entities) { - UnitInsert(unit, result, entities); + await UnitInsert(unit, result, entities); UnitContains(unit, result, entities); } @@ -68,7 +71,9 @@ namespace Content.IntegrationTests.Tests.Disposal - type: Body - type: MobState - type: Damageable - damagePrototype: biologicalDamageContainer + damageContainer: Biological + - type: Physics + bodyType: KinematicController - type: entity name: WrenchDummy @@ -78,6 +83,8 @@ namespace Content.IntegrationTests.Tests.Disposal - type: Tool qualities: - Anchoring + - type: Physics + bodyType: Dynamic - type: entity name: DisposalUnitDummy @@ -94,44 +101,78 @@ namespace Content.IntegrationTests.Tests.Disposal id: DisposalTrunkDummy components: - type: DisposalEntry + - type: Transform + anchored: true "; [Test] public async Task Test() { - var options = new ServerIntegrationOptions{ExtraPrototypes = Prototypes}; + var options = new ServerIntegrationOptions { ExtraPrototypes = Prototypes }; var server = StartServerDummyTicker(options); + await server.WaitIdleAsync(); - IEntity human; - IEntity wrench; - DisposalUnitComponent unit; + IEntity human = default!; + IEntity wrench = default!; + IEntity disposalUnit = default!; + IEntity disposalTrunk = default!; + DisposalUnitComponent unit = default!; + EntityCoordinates coordinates = default!; - server.Assert(async () => + var mapManager = server.ResolveDependency(); + var entityManager = server.ResolveDependency(); + var pauseManager = server.ResolveDependency(); + var componentFactory = server.ResolveDependency(); + var tileDefinitionManager = server.ResolveDependency(); + + // Build up test environment + server.Post(() => { - var mapManager = IoCManager.Resolve(); + // Create a one tile grid to anchor our disposal unit to. + var mapId = mapManager.CreateMap(); - mapManager.CreateNewMapEntity(MapId.Nullspace); + pauseManager.AddUninitializedMap(mapId); - var entityManager = IoCManager.Resolve(); + var gridId = new GridId(1); + if (!mapManager.TryGetGrid(gridId, out var grid)) + { + grid = mapManager.CreateGrid(mapId, gridId); + } + + var tileDefinition = tileDefinitionManager["underplating"]; + var tile = new Tile(tileDefinition.TileId); + coordinates = grid.ToCoordinates(); + + grid.SetTile(coordinates, tile); + + pauseManager.DoMapInitialize(mapId); + }); + + await server.WaitAssertion(() => + { // Spawn the entities - human = entityManager.SpawnEntity("HumanDummy", MapCoordinates.Nullspace); - wrench = entityManager.SpawnEntity("WrenchDummy", MapCoordinates.Nullspace); - var disposalUnit = entityManager.SpawnEntity("DisposalUnitDummy", MapCoordinates.Nullspace); - var disposalTrunk = entityManager.SpawnEntity("DisposalTrunkDummy", disposalUnit.Transform.MapPosition); + human = entityManager.SpawnEntity("HumanDummy", coordinates); + wrench = entityManager.SpawnEntity("WrenchDummy", coordinates); + disposalUnit = entityManager.SpawnEntity("DisposalUnitDummy", coordinates); + disposalTrunk = entityManager.SpawnEntity("DisposalTrunkDummy", disposalUnit.Transform.MapPosition); + + // Check that we have a grid, so that we can anchor our unit + Assert.That(mapManager.TryFindGridAt(disposalUnit.Transform.MapPosition, out var _)); // Test for components existing Assert.True(disposalUnit.TryGetComponent(out unit!)); Assert.True(disposalTrunk.HasComponent()); // Can't insert, unanchored and unpowered - var physics = disposalUnit.GetComponent(); - physics.BodyType = BodyType.Dynamic; - Assert.False(unit.Owner.Transform.Anchored); + unit.Owner.Transform.Anchored = false; UnitInsertContains(unit, false, human, wrench, disposalUnit, disposalTrunk); + }); + await server.WaitAssertion(() => + { // Anchor the disposal unit - physics.BodyType = BodyType.Static; + unit.Owner.Transform.Anchored = true; // No power Assert.False(unit.Powered); @@ -141,19 +182,28 @@ namespace Content.IntegrationTests.Tests.Disposal // Can insert mobs and items UnitInsertContains(unit, true, human, wrench); + }); + await server.WaitAssertion(() => + { // Move the disposal trunk away disposalTrunk.Transform.WorldPosition += (1, 0); // Fail to flush with a mob and an item Flush(unit, false, human, wrench); + }); + await server.WaitAssertion(() => + { // Move the disposal trunk back disposalTrunk.Transform.WorldPosition -= (1, 0); // Fail to flush with a mob and an item, no power Flush(unit, false, human, wrench); + }); + await server.WaitAssertion(() => + { // Remove power need Assert.True(disposalUnit.TryGetComponent(out ApcPowerReceiverComponent? power)); power!.NeedsPower = false; @@ -161,12 +211,13 @@ namespace Content.IntegrationTests.Tests.Disposal // Flush with a mob and an item Flush(unit, true, human, wrench); + }); + await server.WaitAssertion(() => + { // Re-pressurizing Flush(unit, false); }); - - await server.WaitIdleAsync(); } } } diff --git a/Content.Server/AI/Utility/Considerations/Combat/Melee/MeleeWeaponDamageCon.cs b/Content.Server/AI/Utility/Considerations/Combat/Melee/MeleeWeaponDamageCon.cs index a2e0cb148d..5a606679e9 100644 --- a/Content.Server/AI/Utility/Considerations/Combat/Melee/MeleeWeaponDamageCon.cs +++ b/Content.Server/AI/Utility/Considerations/Combat/Melee/MeleeWeaponDamageCon.cs @@ -16,7 +16,7 @@ namespace Content.Server.AI.Utility.Considerations.Combat.Melee } // Just went with max health - return meleeWeaponComponent.Damage / 300.0f; + return meleeWeaponComponent.Damage.Total / 300.0f; } } } diff --git a/Content.Server/AI/Utility/Considerations/Combat/TargetHealthCon.cs b/Content.Server/AI/Utility/Considerations/Combat/TargetHealthCon.cs index 368fdadd55..58feb5aa85 100644 --- a/Content.Server/AI/Utility/Considerations/Combat/TargetHealthCon.cs +++ b/Content.Server/AI/Utility/Considerations/Combat/TargetHealthCon.cs @@ -1,6 +1,6 @@ -using Content.Server.AI.WorldState; +using Content.Server.AI.WorldState; using Content.Server.AI.WorldState.States; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; namespace Content.Server.AI.Utility.Considerations.Combat { @@ -10,7 +10,7 @@ namespace Content.Server.AI.Utility.Considerations.Combat { var target = context.GetState().GetValue(); - if (target == null || target.Deleted || !target.TryGetComponent(out IDamageableComponent? damageableComponent)) + if (target == null || target.Deleted || !target.TryGetComponent(out DamageableComponent? damageableComponent)) { return 0.0f; } diff --git a/Content.Server/AI/WorldState/States/Mobs/NearbyPlayersState.cs b/Content.Server/AI/WorldState/States/Mobs/NearbyPlayersState.cs index 33ed838e46..908df67153 100644 --- a/Content.Server/AI/WorldState/States/Mobs/NearbyPlayersState.cs +++ b/Content.Server/AI/WorldState/States/Mobs/NearbyPlayersState.cs @@ -1,6 +1,6 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Content.Server.AI.Components; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; using JetBrains.Annotations; using Robust.Server.Player; using Robust.Shared.GameObjects; @@ -32,7 +32,7 @@ namespace Content.Server.AI.WorldState.States.Mobs continue; } - if (player.AttachedEntity != Owner && player.AttachedEntity.HasComponent()) + if (player.AttachedEntity != Owner && player.AttachedEntity.HasComponent()) { result.Add(player.AttachedEntity); } diff --git a/Content.Server/Atmos/Components/BarotraumaComponent.cs b/Content.Server/Atmos/Components/BarotraumaComponent.cs index fc81f93d47..c1cb079337 100644 --- a/Content.Server/Atmos/Components/BarotraumaComponent.cs +++ b/Content.Server/Atmos/Components/BarotraumaComponent.cs @@ -5,11 +5,8 @@ using Content.Server.Pressure; using Content.Shared.Alert; using Content.Shared.Atmos; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.Prototypes; -using Robust.Shared.IoC; using Robust.Shared.ViewVariables; namespace Content.Server.Atmos.Components @@ -22,21 +19,14 @@ namespace Content.Server.Atmos.Components { public override string Name => "Barotrauma"; - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // Also remove Initialize override, if no longer needed. - [DataField("damageType")] private readonly string _damageTypeID = "Blunt"; + [DataField("damage", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype DamageType = default!; - protected override void Initialize() - { - base.Initialize(); - DamageType = IoCManager.Resolve().Index(_damageTypeID); - } + public DamageSpecifier Damage = default!; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Update(float airPressure) { - if (!Owner.TryGetComponent(out IDamageableComponent? damageable)) return; + if (!Owner.HasComponent()) return; var status = Owner.GetComponentOrNull(); var highPressureMultiplier = 1f; @@ -59,7 +49,7 @@ namespace Content.Server.Atmos.Components goto default; // Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear. - damageable.TryChangeDamage(DamageType, Atmospherics.LowPressureDamage,true); + EntitySystem.Get().TryChangeDamage(Owner.Uid, Damage * Atmospherics.LowPressureDamage, true); if (status == null) break; @@ -79,10 +69,10 @@ namespace Content.Server.Atmos.Components if(pressure < Atmospherics.WarningHighPressure) goto default; - var damage = (int) MathF.Min((pressure / Atmospherics.HazardHighPressure) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage); + var damageScale = (int) MathF.Min((pressure / Atmospherics.HazardHighPressure) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage); // Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear. - damageable.TryChangeDamage(DamageType, damage,true); + EntitySystem.Get().TryChangeDamage(Owner.Uid, Damage * damageScale, true); if (status == null) break; diff --git a/Content.Server/Atmos/Components/FlammableComponent.cs b/Content.Server/Atmos/Components/FlammableComponent.cs index 86e1f3224b..53b608d0d7 100644 --- a/Content.Server/Atmos/Components/FlammableComponent.cs +++ b/Content.Server/Atmos/Components/FlammableComponent.cs @@ -10,7 +10,6 @@ using Content.Shared.Alert; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.Interaction; using Content.Shared.Notification.Managers; using Content.Shared.Temperature; @@ -20,8 +19,6 @@ using Robust.Shared.Localization; using Robust.Shared.Physics; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; -using Robust.Shared.Prototypes; -using Robust.Shared.IoC; namespace Content.Server.Atmos.Components { @@ -45,17 +42,9 @@ namespace Content.Server.Atmos.Components [DataField("canResistFire")] public bool CanResistFire { get; private set; } = false; - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // Also remove Initialize override, if no longer needed. - [DataField("damageType")] - private readonly string _damageTypeID = "Heat"!; + [DataField("damage", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype DamageType = default!; - protected override void Initialize() - { - base.Initialize(); - DamageType = IoCManager.Resolve().Index(_damageTypeID); - } + public DamageSpecifier Damage = default!; public void Extinguish() { @@ -102,12 +91,9 @@ namespace Content.Server.Atmos.Components temp.ReceiveHeat(200 * FireStacks); } - if (Owner.TryGetComponent(out IDamageableComponent? damageable)) - { - // TODO ATMOS Fire resistance from armor - var damage = Math.Min((int) (FireStacks * 2.5f), 10); - damageable.TryChangeDamage(DamageType, damage, false); - } + // TODO ATMOS Fire resistance from armor + var damageScale = Math.Min((int) (FireStacks * 2.5f), 10); + EntitySystem.Get().TryChangeDamage(Owner.Uid, Damage * damageScale); AdjustFireStacks(-0.1f * (_resisting ? 10f : 1f)); } diff --git a/Content.Server/Body/Metabolism/MetabolizerSystem.cs b/Content.Server/Body/Metabolism/MetabolizerSystem.cs index 2967412fbd..7579c2cd40 100644 --- a/Content.Server/Body/Metabolism/MetabolizerSystem.cs +++ b/Content.Server/Body/Metabolism/MetabolizerSystem.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Content.Server.Body.Circulatory; using Content.Shared.Body.Components; using Content.Shared.Body.Mechanism; diff --git a/Content.Server/Body/Respiratory/RespiratorComponent.cs b/Content.Server/Body/Respiratory/RespiratorComponent.cs index 48f2472263..277b95f6e8 100644 --- a/Content.Server/Body/Respiratory/RespiratorComponent.cs +++ b/Content.Server/Body/Respiratory/RespiratorComponent.cs @@ -12,9 +12,6 @@ using Content.Shared.Alert; using Content.Shared.Atmos; using Content.Shared.Body.Components; using Content.Shared.Damage; -using Content.Shared.Damage.Components; -using Robust.Shared.Prototypes; -using Robust.Shared.IoC; using Content.Shared.MobState; using Content.Shared.Notification.Managers; using Robust.Shared.GameObjects; @@ -92,21 +89,13 @@ namespace Content.Server.Body.Respiratory [ViewVariables] public bool Suffocating { get; private set; } - [ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamage")] private int _damage = 1; - - [ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamageRecovery")] private int _damageRecovery = 1; - - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // Also remove Initialize override, if no longer needed. - [DataField("damageType")] - private readonly string _damageTypeID = "Asphyxiation"!; + [DataField("damage", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype DamageType = default!; - protected override void Initialize() - { - base.Initialize(); - DamageType = IoCManager.Resolve().Index(_damageTypeID); - } + public DamageSpecifier Damage = default!; + + [DataField("damageRecovery", required: true)] + [ViewVariables(VVAccess.ReadWrite)] + public DamageSpecifier DamageRecovery = default!; private Dictionary NeedsAndDeficit(float frameTime) { @@ -358,27 +347,19 @@ namespace Content.Server.Body.Respiratory alertsComponent.ShowAlert(AlertType.LowOxygen); } - if (!Owner.TryGetComponent(out IDamageableComponent? damageable)) - { - return; - } - - damageable.TryChangeDamage(DamageType, _damage, false); + EntitySystem.Get().TryChangeDamage(Owner.Uid, Damage); } private void StopSuffocation() { Suffocating = false; - if (Owner.TryGetComponent(out IDamageableComponent? damageable)) - { - damageable.TryChangeDamage(DamageType, -_damageRecovery, false); - } - if (Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent)) { alertsComponent.ClearAlert(AlertType.LowOxygen); } + + EntitySystem.Get().TryChangeDamage(Owner.Uid, DamageRecovery); } public GasMixture Clean(BloodstreamComponent bloodstream) diff --git a/Content.Server/Buckle/Components/BuckleComponent.cs b/Content.Server/Buckle/Components/BuckleComponent.cs index 2232045831..aae2920187 100644 --- a/Content.Server/Buckle/Components/BuckleComponent.cs +++ b/Content.Server/Buckle/Components/BuckleComponent.cs @@ -2,13 +2,13 @@ using System; using System.Diagnostics.CodeAnalysis; using Content.Server.Alert; using Content.Server.Hands.Components; -using Content.Server.MobState.States; using Content.Server.Pulling; using Content.Server.Stunnable.Components; using Content.Shared.ActionBlocker; using Content.Shared.Alert; using Content.Shared.Buckle.Components; using Content.Shared.Interaction.Helpers; +using Content.Shared.MobState.Components; using Content.Shared.Notification.Managers; using Content.Shared.Standing; using Content.Shared.Verbs; diff --git a/Content.Server/Chat/Commands/SuicideCommand.cs b/Content.Server/Chat/Commands/SuicideCommand.cs index 78ad9088f2..8271987166 100644 --- a/Content.Server/Chat/Commands/SuicideCommand.cs +++ b/Content.Server/Chat/Commands/SuicideCommand.cs @@ -8,7 +8,7 @@ using Content.Server.Items; using Content.Server.Notification; using Content.Server.Players; using Content.Shared.Damage; -using Content.Shared.Damage.Components; +using Content.Shared.Damage.Prototypes; using Content.Shared.Notification; using Content.Shared.Notification.Managers; using Robust.Server.Player; @@ -30,13 +30,14 @@ namespace Content.Server.Chat.Commands public string Help => Loc.GetString("suicide-command-help-text"); - private void DealDamage(ISuicideAct suicide, IChatManager chat, IDamageableComponent damageableComponent, IEntity source, IEntity target) + private void DealDamage(ISuicideAct suicide, IChatManager chat, IEntity target) { var kind = suicide.Suicide(target, chat); if (kind != SuicideKind.Special) { + // TODO SUICIDE ..heh.. anyway, someone should fix this mess. var prototypeManager = IoCManager.Resolve(); - damageableComponent.TrySetDamage(kind switch + DamageSpecifier damage = new(kind switch { SuicideKind.Blunt => prototypeManager.Index("Blunt"), SuicideKind.Slash => prototypeManager.Index("Slash"), @@ -51,6 +52,7 @@ namespace Content.Server.Chat.Commands _ => prototypeManager.Index("Blunt") }, 200); + EntitySystem.Get().TryChangeDamage(target.Uid, damage, true); } } @@ -77,7 +79,6 @@ namespace Content.Server.Chat.Commands return; } - var dmgComponent = owner.GetComponent(); //TODO: needs to check if the mob is actually alive //TODO: maybe set a suicided flag to prevent resurrection? @@ -90,7 +91,7 @@ namespace Content.Server.Chat.Commands if (suicide != null) { - DealDamage(suicide, chat, dmgComponent, itemComponent.Owner, owner); + DealDamage(suicide, chat, owner); return; } } @@ -106,7 +107,7 @@ namespace Content.Server.Chat.Commands var suicide = entity.GetAllComponents().FirstOrDefault(); if (suicide != null) { - DealDamage(suicide, chat, dmgComponent, entity, owner); + DealDamage(suicide, chat, owner); return; } } @@ -119,7 +120,8 @@ namespace Content.Server.Chat.Commands var selfMessage = Loc.GetString("suicide-command-default-text-self"); owner.PopupMessage(selfMessage); - dmgComponent.TrySetDamage(IoCManager.Resolve().Index("Piercing"), 200); + DamageSpecifier damage = new(IoCManager.Resolve().Index("Bloodloss"), 200); + EntitySystem.Get().TryChangeDamage(owner.Uid, damage, true); // Prevent the player from returning to the body. // Note that mind cannot be null because otherwise owner would be null. diff --git a/Content.Server/Chemistry/Components/HyposprayComponent.cs b/Content.Server/Chemistry/Components/HyposprayComponent.cs index 96b648a354..947481699e 100644 --- a/Content.Server/Chemistry/Components/HyposprayComponent.cs +++ b/Content.Server/Chemistry/Components/HyposprayComponent.cs @@ -1,9 +1,9 @@ using Content.Server.Interaction.Components; -using Content.Server.MobState.States; using Content.Server.Weapon.Melee; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; +using Content.Shared.MobState.Components; using Content.Shared.Notification.Managers; using Content.Shared.Sound; using Robust.Shared.Audio; diff --git a/Content.Server/Chemistry/ReagentEffects/HealthChange.cs b/Content.Server/Chemistry/ReagentEffects/HealthChange.cs index 6dc85aeb2c..ccf16a352f 100644 --- a/Content.Server/Chemistry/ReagentEffects/HealthChange.cs +++ b/Content.Server/Chemistry/ReagentEffects/HealthChange.cs @@ -3,66 +3,23 @@ using Content.Shared.Chemistry.Reagent; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; using Content.Shared.Damage; -using Content.Shared.Damage.Components; -using Robust.Shared.Prototypes; -using Robust.Shared.IoC; -using Robust.Shared.Serialization; namespace Content.Server.Chemistry.ReagentEffects { /// - /// Default metabolism for medicine reagents. Attempts to find a DamageableComponent on the target, - /// and to update its damage values. + /// Default metabolism for medicine reagents. /// - public class HealthChange : ReagentEffect, ISerializationHooks + public class HealthChange : ReagentEffect { /// - /// How much damage is changed when 1u of the reagent is metabolized. + /// Damage to apply every metabolism cycle. Damage Ignores resistances. /// - [DataField("healthChange")] - public float AmountToChange { get; set; } = 1.0f; + [DataField("damage", required: true)] + public DamageSpecifier Damage = default!; - // TODO DAMAGE UNITS When damage units support decimals, get rid of this. - // See also _accumulatedDamage in ThirstComponent and HungerComponent - private float _accumulatedDamage; - - /// - /// Damage group to change. - /// - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // Also remove ISerializationHooks, if no longer needed. - [DataField("damageGroup", required: true)] - private readonly string _damageGroupID = default!; - public DamageGroupPrototype DamageGroup = default!; - void ISerializationHooks.AfterDeserialization() - { - DamageGroup = IoCManager.Resolve().Index(_damageGroupID); - } - - /// - /// Changes damage if a DamageableComponent can be found. - /// public override void Metabolize(IEntity solutionEntity, Solution.ReagentQuantity amount) { - if (solutionEntity.TryGetComponent(out IDamageableComponent? damageComponent)) - { - damageComponent.TryChangeDamage(DamageGroup, (int)AmountToChange, true); - - float decHealthChange = (float) (AmountToChange - (int) AmountToChange); - _accumulatedDamage += decHealthChange; - - if (_accumulatedDamage >= 1) - { - damageComponent.TryChangeDamage(DamageGroup, 1, true); - _accumulatedDamage -= 1; - } - - else if(_accumulatedDamage <= -1) - { - damageComponent.TryChangeDamage(DamageGroup, -1, true); - _accumulatedDamage += 1; - } - } + EntitySystem.Get().TryChangeDamage(solutionEntity.Uid, Damage, true); } } } diff --git a/Content.Server/Damage/Commands/HurtCommand.cs b/Content.Server/Damage/Commands/HurtCommand.cs index 851771a173..16ddeabb7f 100644 --- a/Content.Server/Damage/Commands/HurtCommand.cs +++ b/Content.Server/Damage/Commands/HurtCommand.cs @@ -5,7 +5,7 @@ using System.Text; using Content.Server.Administration; using Content.Shared.Administration; using Content.Shared.Damage; -using Content.Shared.Damage.Components; +using Content.Shared.Damage.Prototypes; using Robust.Server.Player; using Robust.Shared.Console; using Robust.Shared.GameObjects; @@ -42,7 +42,7 @@ namespace Content.Server.Damage.Commands return $"Damage Types:{msg}"; } - private delegate void Damage(IDamageableComponent damageable, bool ignoreResistances); + private delegate void Damage(IEntity entity, bool ignoreResistances); private bool TryParseEntity(IConsoleShell shell, IPlayerSession? player, string arg, [NotNullWhen(true)] out IEntity? entity) @@ -85,7 +85,7 @@ namespace Content.Server.Damage.Commands private bool TryParseDamageArgs( IConsoleShell shell, - IPlayerSession? player, + IEntity target, string[] args, [NotNullWhen(true)] out Damage? func) { @@ -101,23 +101,12 @@ namespace Content.Server.Damage.Commands if (_prototypeManager.TryIndex(args[0], out var damageGroup)) { - func = (damageable, ignoreResistances) => + func = (entity, ignoreResistances) => { - if (!damageable.ApplicableDamageGroups.Contains(damageGroup)) - { - shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage group {damageGroup}"); + var damage = new DamageSpecifier(damageGroup, amount); + EntitySystem.Get().TryChangeDamage(entity.Uid, damage, ignoreResistances); - return; - } - - if (!damageable.TryChangeDamage(damageGroup, amount, ignoreResistances)) - { - shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage."); - - return; - } - - shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageGroup} damage{(ignoreResistances ? ", ignoring resistances." : ".")}"); + shell.WriteLine($"Damaged entity {entity.Name} with id {entity.Uid} for {amount} {damageGroup} damage{(ignoreResistances ? ", ignoring resistances." : ".")}"); }; return true; @@ -125,23 +114,12 @@ namespace Content.Server.Damage.Commands // Fall back to DamageType else if (_prototypeManager.TryIndex(args[0], out var damageType)) { - func = (damageable, ignoreResistances) => + func = (entity, ignoreResistances) => { - if (!damageable.IsSupportedDamageType(damageType)) - { - shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage type {damageType}"); + var damage = new DamageSpecifier(damageType, amount); + EntitySystem.Get().TryChangeDamage(entity.Uid, damage, ignoreResistances); - return; - } - - if (!damageable.TryChangeDamage(damageType, amount, ignoreResistances)) - { - shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage."); - - return; - } - - shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageType} damage{(ignoreResistances ? ", ignoring resistances." : ".")}"); + shell.WriteLine($"Damaged entity {entity.Name} with id {entity.Uid} for {amount} {damageType} damage{(ignoreResistances ? ", ignoring resistances." : ".")}"); }; return true; @@ -185,10 +163,6 @@ namespace Content.Server.Damage.Commands shell.WriteLine($"Invalid number of arguments ({args.Length}).\n{Help}"); return; case var n when n >= 2 && n <= 4: - if (!TryParseDamageArgs(shell, player, args, out damageFunc)) - { - return; - } var entityUid = n == 2 ? "_" : args[2]; @@ -199,6 +173,11 @@ namespace Content.Server.Damage.Commands entity = parsedEntity; + if (!TryParseDamageArgs(shell, entity, args, out damageFunc)) + { + return; + } + if (n == 4) { if (!bool.TryParse(args[3], out ignoreResistances)) @@ -218,13 +197,13 @@ namespace Content.Server.Damage.Commands return; } - if (!entity.TryGetComponent(out IDamageableComponent? damageable)) + if (!entity.TryGetComponent(out DamageableComponent? damageable)) { - shell.WriteLine($"Entity {entity.Name} with id {entity.Uid} does not have a {nameof(IDamageableComponent)}."); + shell.WriteLine($"Entity {entity.Name} with id {entity.Uid} does not have a {nameof(DamageableComponent)}."); return; } - damageFunc(damageable, ignoreResistances); + damageFunc(entity, ignoreResistances); } } } diff --git a/Content.Server/Damage/Components/DamageOnHighSpeedImpactComponent.cs b/Content.Server/Damage/Components/DamageOnHighSpeedImpactComponent.cs index 96553b9b7b..2c211ffd3d 100644 --- a/Content.Server/Damage/Components/DamageOnHighSpeedImpactComponent.cs +++ b/Content.Server/Damage/Components/DamageOnHighSpeedImpactComponent.cs @@ -3,8 +3,6 @@ using Content.Shared.Damage; using Content.Shared.Sound; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.IoC; -using Robust.Shared.Prototypes; using Robust.Shared.ViewVariables; namespace Content.Server.Damage.Components @@ -19,8 +17,6 @@ namespace Content.Server.Damage.Components [DataField("minimumSpeed")] public float MinimumSpeed { get; set; } = 20f; - [DataField("baseDamage")] - public int BaseDamage { get; set; } = 5; [DataField("factor")] public float Factor { get; set; } = 1f; [DataField("soundHit", required: true)] @@ -36,16 +32,8 @@ namespace Content.Server.Damage.Components internal TimeSpan LastHit = TimeSpan.Zero; - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // Also remove Initialize override, if no longer needed. - [DataField("damageType")] - private readonly string _damageTypeID = "Blunt"; + [DataField("damage", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype DamageType = default!; - protected override void Initialize() - { - base.Initialize(); - DamageType = IoCManager.Resolve().Index(_damageTypeID); - } + public DamageSpecifier Damage = default!; } } diff --git a/Content.Server/Damage/Components/DamageOnLandComponent.cs b/Content.Server/Damage/Components/DamageOnLandComponent.cs index 5d9b1118c9..bd8bef2ccc 100644 --- a/Content.Server/Damage/Components/DamageOnLandComponent.cs +++ b/Content.Server/Damage/Components/DamageOnLandComponent.cs @@ -9,20 +9,13 @@ namespace Content.Server.Damage.Components public sealed class DamageOnLandComponent : Component { public override string Name => "DamageOnLand"; - - [DataField("amount")] - [ViewVariables(VVAccess.ReadWrite)] - public int Amount = 1; - + [DataField("ignoreResistances")] [ViewVariables(VVAccess.ReadWrite)] - public bool IgnoreResistances; - - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // Also remove Initialize override, if no longer needed. - [DataField("damageType")] public readonly string DamageTypeId = "Blunt"; + public bool IgnoreResistances = false; + [DataField("damage", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype DamageType = default!; + public DamageSpecifier Damage = default!; } } diff --git a/Content.Server/Damage/Components/DamageOnToolInteractComponent.cs b/Content.Server/Damage/Components/DamageOnToolInteractComponent.cs index 061ff309a7..7e07f3bd43 100644 --- a/Content.Server/Damage/Components/DamageOnToolInteractComponent.cs +++ b/Content.Server/Damage/Components/DamageOnToolInteractComponent.cs @@ -2,13 +2,10 @@ using System.Collections.Generic; using System.Threading.Tasks; using Content.Server.Tools.Components; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.Interaction; using Content.Shared.Tool; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.Prototypes; -using Robust.Shared.IoC; using Robust.Shared.ViewVariables; namespace Content.Server.Damage.Components @@ -19,28 +16,16 @@ namespace Content.Server.Damage.Components public override string Name => "DamageOnToolInteract"; - [DataField("damage")] - protected int Damage; - [DataField("tools")] private List _tools = new(); - // TODO PROTOTYPE Replace these datafield variable with prototype references, once they are supported. - // Also remove Initialize override, if no longer needed. - [DataField("weldingDamageType")] - private readonly string _weldingDamageTypeID = "Heat"; + [DataField("weldingDamage", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype WeldingDamageType = default!; - [DataField("defaultDamageType")] - private readonly string _defaultDamageTypeID = "Blunt"; + public DamageSpecifier WeldingDamage = default!; + + [DataField("defaultDamage", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype DefaultDamageType = default!; - protected override void Initialize() - { - base.Initialize(); - WeldingDamageType = IoCManager.Resolve().Index(_weldingDamageTypeID); - DefaultDamageType = IoCManager.Resolve().Index(_defaultDamageTypeID); - } + public DamageSpecifier DefaultDamage = default!; async Task IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) { @@ -50,30 +35,22 @@ namespace Content.Server.Damage.Components { if (tool.HasQuality(ToolQuality.Welding) && toolQuality == ToolQuality.Welding) { - if (eventArgs.Using.TryGetComponent(out WelderComponent? welder)) + if (eventArgs.Using.TryGetComponent(out WelderComponent? welder) && welder.WelderLit) { - if (welder.WelderLit) return CallDamage(eventArgs, tool); + EntitySystem.Get().TryChangeDamage(eventArgs.Target.Uid, WeldingDamage); + return true; } break; //If the tool quality is welding and its not lit or its not actually a welder that can be lit then its pointless to continue. } - if (tool.HasQuality(toolQuality)) return CallDamage(eventArgs, tool); + if (tool.HasQuality(toolQuality)) + { + EntitySystem.Get().TryChangeDamage(eventArgs.Target.Uid, DefaultDamage); + return true; + } } } return false; } - - protected bool CallDamage(InteractUsingEventArgs eventArgs, ToolComponent tool) - { - if (!eventArgs.Target.TryGetComponent(out var damageable)) - return false; - - damageable.TryChangeDamage(tool.HasQuality(ToolQuality.Welding) - ? WeldingDamageType - : DefaultDamageType, - Damage); - - return true; - } } } diff --git a/Content.Server/Damage/Components/DamageOtherOnHitComponent.cs b/Content.Server/Damage/Components/DamageOtherOnHitComponent.cs index ed005d5ef4..2a59592b0b 100644 --- a/Content.Server/Damage/Components/DamageOtherOnHitComponent.cs +++ b/Content.Server/Damage/Components/DamageOtherOnHitComponent.cs @@ -3,8 +3,7 @@ using Content.Shared.Damage; using Robust.Shared.Analyzers; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.Prototypes; -using Robust.Shared.IoC; +using Robust.Shared.ViewVariables; namespace Content.Server.Damage.Components { @@ -14,21 +13,13 @@ namespace Content.Server.Damage.Components { public override string Name => "DamageOtherOnHit"; - [DataField("amount")] - public int Amount { get; } = 1; - [DataField("ignoreResistances")] - public bool IgnoreResistances { get; } = false; + [ViewVariables(VVAccess.ReadWrite)] + public bool IgnoreResistances = false; + + [DataField("damage", required: true)] + [ViewVariables(VVAccess.ReadWrite)] + public DamageSpecifier Damage = default!; - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // Also remove Initialize override, if no longer needed. - [DataField("damageType")] - private readonly string _damageTypeID = "Blunt"; - public DamageTypePrototype DamageType { get; set; } = default!; - protected override void Initialize() - { - base.Initialize(); - DamageType = IoCManager.Resolve().Index(_damageTypeID); - } } } diff --git a/Content.Server/Damage/RejuvenateVerb.cs b/Content.Server/Damage/RejuvenateVerb.cs index 5a5ef2bff7..d58973a68d 100644 --- a/Content.Server/Damage/RejuvenateVerb.cs +++ b/Content.Server/Damage/RejuvenateVerb.cs @@ -2,7 +2,7 @@ using Content.Server.Atmos.Components; using Content.Server.Nutrition.Components; using Content.Server.Nutrition.EntitySystems; using Content.Server.Stunnable.Components; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; using Content.Shared.MobState; using Content.Shared.Nutrition.Components; using Content.Shared.Verbs; @@ -34,7 +34,7 @@ namespace Content.Server.Damage if (user.TryGetComponent(out var player)) { - if (!target.HasComponent() && !target.HasComponent() && + if (!target.HasComponent() && !target.HasComponent() && !target.HasComponent()) { return; @@ -59,14 +59,9 @@ namespace Content.Server.Damage public static void PerformRejuvenate(IEntity target) { - if (target.TryGetComponent(out IDamageableComponent? damage)) + if (target.TryGetComponent(out DamageableComponent? damageable)) { - damage.TrySetAllDamage(0); - } - - if (target.TryGetComponent(out IMobStateComponent? mobState)) - { - mobState.UpdateState(0); + EntitySystem.Get().SetAllDamage(damageable, 0); } if (target.TryGetComponent(out HungerComponent? hunger)) diff --git a/Content.Server/Damage/Systems/DamageOnHighSpeedImpactSystem.cs b/Content.Server/Damage/Systems/DamageOnHighSpeedImpactSystem.cs index 752e227f45..6e195eb438 100644 --- a/Content.Server/Damage/Systems/DamageOnHighSpeedImpactSystem.cs +++ b/Content.Server/Damage/Systems/DamageOnHighSpeedImpactSystem.cs @@ -1,7 +1,7 @@ using Content.Server.Damage.Components; using Content.Server.Stunnable.Components; using Content.Shared.Audio; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; using JetBrains.Annotations; using Robust.Shared.Audio; using Robust.Shared.GameObjects; @@ -18,6 +18,7 @@ namespace Content.Server.Damage.Systems { [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly DamageableSystem _damageableSystem = default!; public override void Initialize() { @@ -27,7 +28,7 @@ namespace Content.Server.Damage.Systems private void HandleCollide(EntityUid uid, DamageOnHighSpeedImpactComponent component, StartCollideEvent args) { - if (!ComponentManager.TryGetComponent(uid, out IDamageableComponent? damageable)) return; + if (!ComponentManager.HasComponent(uid)) return; var otherBody = args.OtherFixture.Body.Owner; var speed = args.OurFixture.Body.LinearVelocity.Length; @@ -41,12 +42,11 @@ namespace Content.Server.Damage.Systems component.LastHit = _gameTiming.CurTime; - var damage = (int) (component.BaseDamage * (speed / component.MinimumSpeed) * component.Factor); - if (ComponentManager.TryGetComponent(uid, out StunnableComponent? stun) && _robustRandom.Prob(component.StunChance)) stun.Stun(component.StunSeconds); - damageable.TryChangeDamage(component.DamageType, damage); + var damageScale = (speed / component.MinimumSpeed) * component.Factor; + _damageableSystem.TryChangeDamage(uid, component.Damage * damageScale); } } } diff --git a/Content.Server/Damage/Systems/DamageOnLandSystem.cs b/Content.Server/Damage/Systems/DamageOnLandSystem.cs index 49e89a8fec..ba2b789a1b 100644 --- a/Content.Server/Damage/Systems/DamageOnLandSystem.cs +++ b/Content.Server/Damage/Systems/DamageOnLandSystem.cs @@ -1,35 +1,24 @@ using Content.Server.Damage.Components; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.Throwing; using Robust.Shared.GameObjects; using Robust.Shared.IoC; -using Robust.Shared.Prototypes; namespace Content.Server.Damage.Systems { public sealed class DamageOnLandSystem : EntitySystem { - [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly DamageableSystem _damageableSystem = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(HandleInit); SubscribeLocalEvent(DamageOnLand); } - private void HandleInit(EntityUid uid, DamageOnLandComponent component, ComponentInit args) - { - component.DamageType = _protoManager.Index(component.DamageTypeId); - } - private void DamageOnLand(EntityUid uid, DamageOnLandComponent component, LandEvent args) { - if (!ComponentManager.TryGetComponent(uid, out var damageable)) - return; - - damageable.TryChangeDamage(component.DamageType, component.Amount, component.IgnoreResistances); + _damageableSystem.TryChangeDamage(uid, component.Damage, component.IgnoreResistances); } } } diff --git a/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs b/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs index 419f0838da..900ded4802 100644 --- a/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs +++ b/Content.Server/Damage/Systems/DamageOtherOnHitSystem.cs @@ -1,12 +1,15 @@ using Content.Server.Damage.Components; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; using Content.Shared.Throwing; using Robust.Shared.GameObjects; +using Robust.Shared.IoC; namespace Content.Server.Damage.Systems { public class DamageOtherOnHitSystem : EntitySystem { + [Dependency] private readonly DamageableSystem _damageableSystem = default!; + public override void Initialize() { SubscribeLocalEvent(OnDoHit); @@ -14,10 +17,7 @@ namespace Content.Server.Damage.Systems private void OnDoHit(EntityUid uid, DamageOtherOnHitComponent component, ThrowDoHitEvent args) { - if (!args.Target.TryGetComponent(out IDamageableComponent? damageable)) - return; - - damageable.TryChangeDamage(component.DamageType, component.Amount, component.IgnoreResistances); + _damageableSystem.TryChangeDamage(args.Target.Uid, component.Damage, component.IgnoreResistances); } } } diff --git a/Content.Server/Damage/Systems/GodmodeSystem.cs b/Content.Server/Damage/Systems/GodmodeSystem.cs index 8abf07a104..6420b0fd23 100644 --- a/Content.Server/Damage/Systems/GodmodeSystem.cs +++ b/Content.Server/Damage/Systems/GodmodeSystem.cs @@ -1,11 +1,10 @@ using System.Collections.Generic; -using System.Linq; using Content.Server.Atmos.Components; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.GameTicking; using JetBrains.Annotations; using Robust.Shared.GameObjects; +using Robust.Shared.IoC; namespace Content.Server.Damage.Systems { @@ -13,6 +12,7 @@ namespace Content.Server.Damage.Systems public class GodmodeSystem : EntitySystem { private readonly Dictionary _entities = new(); + [Dependency] private readonly DamageableSystem _damageableSystem = default!; public override void Initialize() { @@ -40,11 +40,9 @@ namespace Content.Server.Damage.Systems moved.Enabled = false; } - if (entity.TryGetComponent(out IDamageableComponent? damageable)) + if (entity.TryGetComponent(out DamageableComponent? damageable)) { - damageable.SupportedDamageTypes.Clear(); - damageable.FullySupportedDamageGroups.Clear(); - damageable.ApplicableDamageGroups.Clear(); + _damageableSystem.SetDamage(damageable, new DamageSpecifier()); } return true; @@ -67,21 +65,11 @@ namespace Content.Server.Damage.Systems moved.Enabled = old.MovedByPressure; } - if (entity.TryGetComponent(out IDamageableComponent? damageable)) + if (entity.TryGetComponent(out DamageableComponent? damageable)) { - if (old.SupportedDamageTypes != null) + if (old.Damage != null) { - damageable.SupportedDamageTypes.UnionWith(old.SupportedDamageTypes); - } - - if (old.SupportedDamageGroups != null) - { - damageable.FullySupportedDamageGroups.UnionWith(old.SupportedDamageGroups); - } - - if (old.ApplicableDamageGroups != null) - { - damageable.ApplicableDamageGroups.UnionWith(old.ApplicableDamageGroups); + _damageableSystem.SetDamage(damageable, old.Damage); } } @@ -114,11 +102,9 @@ namespace Content.Server.Damage.Systems Entity = entity; MovedByPressure = entity.IsMovedByPressure(); - if (entity.TryGetComponent(out IDamageableComponent? damageable)) + if (entity.TryGetComponent(out DamageableComponent? damageable)) { - SupportedDamageTypes = damageable.SupportedDamageTypes.ToHashSet(); - SupportedDamageGroups = damageable.FullySupportedDamageGroups.ToHashSet(); - ApplicableDamageGroups = damageable.ApplicableDamageGroups.ToHashSet(); + Damage = damageable.Damage; } } @@ -126,11 +112,7 @@ namespace Content.Server.Damage.Systems public bool MovedByPressure { get; } - public HashSet? SupportedDamageTypes { get; } - - public HashSet? SupportedDamageGroups { get; } - - public HashSet? ApplicableDamageGroups { get; } + public DamageSpecifier? Damage { get; } } } } diff --git a/Content.Server/Destructible/DestructibleComponent.cs b/Content.Server/Destructible/DestructibleComponent.cs index c7bdd18f9f..069a3be4ff 100644 --- a/Content.Server/Destructible/DestructibleComponent.cs +++ b/Content.Server/Destructible/DestructibleComponent.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Content.Server.Destructible.Thresholds; using Content.Shared.Damage; using Robust.Shared.GameObjects; @@ -14,50 +14,11 @@ namespace Content.Server.Destructible [RegisterComponent] public class DestructibleComponent : Component { - private DestructibleSystem _destructibleSystem = default!; - public override string Name => "Destructible"; [ViewVariables] [DataField("thresholds")] - private List _thresholds = new(); + public List Thresholds = new(); - public IReadOnlyList Thresholds => _thresholds; - - protected override void Initialize() - { - base.Initialize(); - - _destructibleSystem = EntitySystem.Get(); - } - - public override void HandleMessage(ComponentMessage message, IComponent? component) - { - base.HandleMessage(message, component); - - switch (message) - { - case DamageChangedMessage msg: - { - if (msg.Damageable.Owner != Owner) - { - break; - } - - foreach (var threshold in _thresholds) - { - if (threshold.Reached(msg.Damageable, _destructibleSystem)) - { - var thresholdMessage = new DestructibleThresholdReachedMessage(this, threshold); - SendMessage(thresholdMessage); - - threshold.Execute(Owner, _destructibleSystem); - } - } - - break; - } - } - } } } diff --git a/Content.Server/Destructible/DestructibleSystem.cs b/Content.Server/Destructible/DestructibleSystem.cs index 1be935c9f6..daa139479c 100644 --- a/Content.Server/Destructible/DestructibleSystem.cs +++ b/Content.Server/Destructible/DestructibleSystem.cs @@ -1,4 +1,6 @@ -using Content.Shared.Acts; +using Content.Server.Destructible.Thresholds; +using Content.Shared.Acts; +using Content.Shared.Damage; using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; @@ -13,5 +15,44 @@ namespace Content.Server.Destructible [Dependency] public readonly IRobustRandom Random = default!; [Dependency] public readonly AudioSystem AudioSystem = default!; [Dependency] public readonly ActSystem ActSystem = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(Execute); + } + + /// + /// Check if any thresholds were reached. if they were, execute them. + /// + public void Execute(EntityUid uid, DestructibleComponent component, DamageChangedEvent args) + { + foreach (var threshold in component.Thresholds) + { + if (threshold.Reached(args.Damageable, this)) + { + RaiseLocalEvent(uid, new DamageThresholdReached(component, threshold)); + + threshold.Execute(component.Owner, this); + } + } + } + } + + // Currently only used for destructible integration tests. Unless other uses are found for this, maybe this should just be removed and the tests redone. + /// + /// Event raised when a is reached. + /// + public class DamageThresholdReached : EntityEventArgs + { + public readonly DestructibleComponent Parent; + + public readonly DamageThreshold Threshold; + + public DamageThresholdReached(DestructibleComponent parent, DamageThreshold threshold) + { + Parent = parent; + Threshold = threshold; + } } } diff --git a/Content.Server/Destructible/DestructibleThresholdReachedMessage.cs b/Content.Server/Destructible/DestructibleThresholdReachedMessage.cs deleted file mode 100644 index 9a608c09e0..0000000000 --- a/Content.Server/Destructible/DestructibleThresholdReachedMessage.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Content.Server.Destructible.Thresholds; -using Robust.Shared.GameObjects; - -namespace Content.Server.Destructible -{ - public class DestructibleThresholdReachedMessage : ComponentMessage - { - public DestructibleThresholdReachedMessage(DestructibleComponent parent, Threshold threshold) - { - Parent = parent; - Threshold = threshold; - } - - public DestructibleComponent Parent { get; } - - public Threshold Threshold { get; } - } -} diff --git a/Content.Server/Destructible/Thresholds/Threshold.cs b/Content.Server/Destructible/Thresholds/DamageThreshold.cs similarity index 94% rename from Content.Server/Destructible/Thresholds/Threshold.cs rename to Content.Server/Destructible/Thresholds/DamageThreshold.cs index 27c4a557af..26ce161ba8 100644 --- a/Content.Server/Destructible/Thresholds/Threshold.cs +++ b/Content.Server/Destructible/Thresholds/DamageThreshold.cs @@ -1,7 +1,7 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Content.Server.Destructible.Thresholds.Behaviors; using Content.Server.Destructible.Thresholds.Triggers; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; @@ -9,7 +9,7 @@ using Robust.Shared.ViewVariables; namespace Content.Server.Destructible.Thresholds { [DataDefinition] - public class Threshold + public class DamageThreshold { [DataField("behaviors")] private List _behaviors = new(); @@ -49,7 +49,7 @@ namespace Content.Server.Destructible.Thresholds /// [ViewVariables] public IReadOnlyList Behaviors => _behaviors; - public bool Reached(IDamageableComponent damageable, DestructibleSystem system) + public bool Reached(DamageableComponent damageable, DestructibleSystem system) { if (Trigger == null) { diff --git a/Content.Server/Destructible/Thresholds/Triggers/AndTrigger.cs b/Content.Server/Destructible/Thresholds/Triggers/AndTrigger.cs index 55818309b5..13113d2448 100644 --- a/Content.Server/Destructible/Thresholds/Triggers/AndTrigger.cs +++ b/Content.Server/Destructible/Thresholds/Triggers/AndTrigger.cs @@ -1,6 +1,6 @@ -using System; +using System; using System.Collections.Generic; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; using Robust.Shared.Serialization.Manager.Attributes; namespace Content.Server.Destructible.Thresholds.Triggers @@ -15,7 +15,7 @@ namespace Content.Server.Destructible.Thresholds.Triggers [DataField("triggers")] public List Triggers { get; set; } = new(); - public bool Reached(IDamageableComponent damageable, DestructibleSystem system) + public bool Reached(DamageableComponent damageable, DestructibleSystem system) { foreach (var trigger in Triggers) { diff --git a/Content.Server/Destructible/Thresholds/Triggers/DamageGroupTrigger.cs b/Content.Server/Destructible/Thresholds/Triggers/DamageGroupTrigger.cs index ccddc1678f..f8a46179cf 100644 --- a/Content.Server/Destructible/Thresholds/Triggers/DamageGroupTrigger.cs +++ b/Content.Server/Destructible/Thresholds/Triggers/DamageGroupTrigger.cs @@ -1,9 +1,8 @@ using System; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.Prototypes; -using Robust.Shared.IoC; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Content.Shared.Damage.Prototypes; namespace Content.Server.Destructible.Thresholds.Triggers { @@ -15,12 +14,8 @@ namespace Content.Server.Destructible.Thresholds.Triggers [DataDefinition] public class DamageGroupTrigger : IThresholdTrigger { - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // While you're at it, maybe also combine damageGroup and damage into a dictionary, and allow it to test a sum - // of damage types? - [DataField("damageGroup", required: true)] - private string _damageGroupID { get; set; } = default!; - public DamageGroupPrototype DamageGroup => IoCManager.Resolve().Index(_damageGroupID); + [DataField("damageGroup", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))] + public string DamageGroup { get; set; } = default!; /// /// The amount of damage at which this threshold will trigger. @@ -28,15 +23,9 @@ namespace Content.Server.Destructible.Thresholds.Triggers [DataField("damage", required: true)] public int Damage { get; set; } = default!; - public bool Reached(IDamageableComponent damageable, DestructibleSystem system) + public bool Reached(DamageableComponent damageable, DestructibleSystem system) { - if (DamageGroup == null) - { - return false; - } - - return damageable.TryGetDamage(DamageGroup, out var damageReceived) && - damageReceived >= Damage; + return damageable.DamagePerGroup[DamageGroup] >= Damage; } } } diff --git a/Content.Server/Destructible/Thresholds/Triggers/DamageTrigger.cs b/Content.Server/Destructible/Thresholds/Triggers/DamageTrigger.cs index db51dd217f..257743ee05 100644 --- a/Content.Server/Destructible/Thresholds/Triggers/DamageTrigger.cs +++ b/Content.Server/Destructible/Thresholds/Triggers/DamageTrigger.cs @@ -1,5 +1,5 @@ using System; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; using Robust.Shared.Serialization.Manager.Attributes; namespace Content.Server.Destructible.Thresholds.Triggers @@ -18,7 +18,7 @@ namespace Content.Server.Destructible.Thresholds.Triggers [DataField("damage", required: true)] public int Damage { get; set; } = default!; - public bool Reached(IDamageableComponent damageable, DestructibleSystem system) + public bool Reached(DamageableComponent damageable, DestructibleSystem system) { return damageable.TotalDamage >= Damage; } diff --git a/Content.Server/Destructible/Thresholds/Triggers/DamageTypeTrigger.cs b/Content.Server/Destructible/Thresholds/Triggers/DamageTypeTrigger.cs index 7dbfb7ad87..20de7224cd 100644 --- a/Content.Server/Destructible/Thresholds/Triggers/DamageTypeTrigger.cs +++ b/Content.Server/Destructible/Thresholds/Triggers/DamageTypeTrigger.cs @@ -1,9 +1,8 @@ using System; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.Prototypes; -using Robust.Shared.IoC; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Content.Shared.Damage.Prototypes; namespace Content.Server.Destructible.Thresholds.Triggers { @@ -15,24 +14,15 @@ namespace Content.Server.Destructible.Thresholds.Triggers [DataDefinition] public class DamageTypeTrigger : IThresholdTrigger { - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // While you're at it, maybe also combine damageGroup and damage into a dictionary, and allow it to test a sum - // of damage types? - [DataField("damageType", required:true)] - public string _damageTypeID { get; set; } = default!; - public DamageTypePrototype DamageType => IoCManager.Resolve().Index(_damageTypeID); + [DataField("damageType", required:true, customTypeSerializer: typeof(PrototypeIdSerializer))] + public string DamageType { get; set; } = default!; [DataField("damage", required: true)] public int Damage { get; set; } = default!; - public bool Reached(IDamageableComponent damageable, DestructibleSystem system) + public bool Reached(DamageableComponent damageable, DestructibleSystem system) { - if (DamageType == null) - { - return false; - } - - return damageable.TryGetDamage(DamageType, out var damageReceived) && + return damageable.Damage.DamageDict.TryGetValue(DamageType, out var damageReceived) && damageReceived >= Damage; } } diff --git a/Content.Server/Destructible/Thresholds/Triggers/IThresholdTrigger.cs b/Content.Server/Destructible/Thresholds/Triggers/IThresholdTrigger.cs index 88d9a6d66a..dfceb31c9b 100644 --- a/Content.Server/Destructible/Thresholds/Triggers/IThresholdTrigger.cs +++ b/Content.Server/Destructible/Thresholds/Triggers/IThresholdTrigger.cs @@ -1,4 +1,4 @@ -using Content.Shared.Damage.Components; +using Content.Shared.Damage; namespace Content.Server.Destructible.Thresholds.Triggers { @@ -13,6 +13,6 @@ namespace Content.Server.Destructible.Thresholds.Triggers /// dependencies from, if any. /// /// true if this trigger has been reached, false otherwise. - bool Reached(IDamageableComponent damageable, DestructibleSystem system); + bool Reached(DamageableComponent damageable, DestructibleSystem system); } } diff --git a/Content.Server/Destructible/Thresholds/Triggers/OrTrigger.cs b/Content.Server/Destructible/Thresholds/Triggers/OrTrigger.cs index b19df41e51..39cf195083 100644 --- a/Content.Server/Destructible/Thresholds/Triggers/OrTrigger.cs +++ b/Content.Server/Destructible/Thresholds/Triggers/OrTrigger.cs @@ -1,6 +1,6 @@ -using System; +using System; using System.Collections.Generic; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; using Robust.Shared.Serialization.Manager.Attributes; namespace Content.Server.Destructible.Thresholds.Triggers @@ -15,7 +15,7 @@ namespace Content.Server.Destructible.Thresholds.Triggers [DataField("triggers")] public List Triggers { get; } = new(); - public bool Reached(IDamageableComponent damageable, DestructibleSystem system) + public bool Reached(DamageableComponent damageable, DestructibleSystem system) { foreach (var trigger in Triggers) { diff --git a/Content.Server/DoAfter/DoAfterComponent.cs b/Content.Server/DoAfter/DoAfterComponent.cs index c1136ab853..4692629971 100644 --- a/Content.Server/DoAfter/DoAfterComponent.cs +++ b/Content.Server/DoAfter/DoAfterComponent.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using Content.Shared.Damage; using Content.Shared.DoAfter; using Robust.Shared.GameObjects; using Robust.Shared.Players; @@ -40,35 +39,6 @@ namespace Content.Server.DoAfter return new DoAfterComponentState(toAdd); } - public override void HandleMessage(ComponentMessage message, IComponent? component) - { - base.HandleMessage(message, component); - - switch (message) - { - case DamageChangedMessage msg: - if (DoAfters.Count == 0) - { - return; - } - - if (!msg.TookDamage) - { - return; - } - - foreach (var doAfter in _doAfters.Keys) - { - if (doAfter.EventArgs.BreakOnDamage) - { - doAfter.TookDamage = true; - } - } - - break; - } - } - public void Add(DoAfter doAfter) { _doAfters.Add(doAfter, _runningIndex); diff --git a/Content.Server/DoAfter/DoAfterSystem.cs b/Content.Server/DoAfter/DoAfterSystem.cs index 13978df78e..8d0072a6ee 100644 --- a/Content.Server/DoAfter/DoAfterSystem.cs +++ b/Content.Server/DoAfter/DoAfterSystem.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Content.Shared.Damage; using JetBrains.Annotations; using Robust.Shared.GameObjects; @@ -14,6 +15,28 @@ namespace Content.Server.DoAfter private readonly List _cancelled = new(); private readonly List _finished = new(); + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(HandleDamage); + } + + public void HandleDamage(EntityUid _, DoAfterComponent component, DamageChangedEvent args) + { + if (component.DoAfters.Count == 0 || !args.DamageIncreased) + { + return; + } + + foreach (var doAfter in component.DoAfters) + { + if (doAfter.EventArgs.BreakOnDamage) + { + doAfter.TookDamage = true; + } + } + } + public override void Update(float frameTime) { base.Update(frameTime); diff --git a/Content.Server/Doors/Components/ServerDoorComponent.cs b/Content.Server/Doors/Components/ServerDoorComponent.cs index 7c14d435bb..c19d863d6f 100644 --- a/Content.Server/Doors/Components/ServerDoorComponent.cs +++ b/Content.Server/Doors/Components/ServerDoorComponent.cs @@ -11,7 +11,6 @@ using Content.Server.Hands.Components; using Content.Server.Stunnable.Components; using Content.Server.Tools.Components; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.Doors; using Content.Shared.Interaction; using Content.Shared.Sound; @@ -22,16 +21,11 @@ using Robust.Shared.GameObjects; using Robust.Shared.Log; using Robust.Shared.Maths; using Robust.Shared.Physics; -using Robust.Shared.Physics.Broadphase; -using Robust.Shared.Physics.Collision; -using Robust.Shared.Physics.Dynamics; using Robust.Shared.Player; using Robust.Shared.Players; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; using Timer = Robust.Shared.Timing.Timer; -using Robust.Shared.Prototypes; -using Robust.Shared.IoC; namespace Content.Server.Doors.Components { @@ -47,17 +41,9 @@ namespace Content.Server.Doors.Components [DataField("tryOpenDoorSound")] private SoundSpecifier _tryOpenDoorSound = new SoundPathSpecifier("/Audio/Effects/bang.ogg"); - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // Also remove Initialize override, if no longer needed. - [DataField("damageType")] - private readonly string _damageTypeID = "Blunt"; + [DataField("crushDamage", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype DamageType = default!; - protected override void Initialize() - { - base.Initialize(); - DamageType = IoCManager.Resolve().Index(_damageTypeID); - } + public DamageSpecifier CrushDamage = default!; public override DoorState State { @@ -90,7 +76,6 @@ namespace Content.Server.Doors.Components private CancellationTokenSource? _stateChangeCancelTokenSource; private CancellationTokenSource? _autoCloseCancelTokenSource; - private const int DoorCrushDamage = 15; private const float DoorStunTime = 5f; /// @@ -537,7 +522,7 @@ namespace Content.Server.Doors.Components foreach (var e in collidingentities) { if (!e.Owner.TryGetComponent(out StunnableComponent? stun) - || !e.Owner.TryGetComponent(out IDamageableComponent? damage)) + || !e.Owner.HasComponent()) { continue; } @@ -550,7 +535,8 @@ namespace Content.Server.Doors.Components hitsomebody = true; CurrentlyCrushing.Add(e.Owner.Uid); - damage.TryChangeDamage(DamageType, DoorCrushDamage); + EntitySystem.Get().TryChangeDamage(e.Owner.Uid, CrushDamage); + stun.Paralyze(DoorStunTime); } diff --git a/Content.Server/GameTicking/Presets/GamePreset.cs b/Content.Server/GameTicking/Presets/GamePreset.cs index 4138737f0f..2629ae3213 100644 --- a/Content.Server/GameTicking/Presets/GamePreset.cs +++ b/Content.Server/GameTicking/Presets/GamePreset.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using Content.Server.Ghost.Components; using Content.Shared.Damage; -using Content.Shared.Damage.Components; +using Content.Shared.Damage.Prototypes; using Content.Shared.Ghost; using Content.Shared.MobState; using Content.Shared.Preferences; @@ -64,12 +64,10 @@ namespace Content.Server.GameTicking.Presets { canReturn = true; - if (playerEntity.TryGetComponent(out IDamageableComponent? damageable)) - { - //todo: what if they dont breathe lol - //cry deeply - damageable.TrySetDamage(IoCManager.Resolve().Index("Asphyxiation"), 200); - } + //todo: what if they dont breathe lol + //cry deeply + DamageSpecifier damage = new(IoCManager.Resolve().Index("Asphyxiation"), 200); + EntitySystem.Get().TryChangeDamage(playerEntity.Uid, damage, true); } } diff --git a/Content.Server/GameTicking/Presets/PresetTraitorDeathMatch.cs b/Content.Server/GameTicking/Presets/PresetTraitorDeathMatch.cs index 7ab697642a..8f72350d76 100644 --- a/Content.Server/GameTicking/Presets/PresetTraitorDeathMatch.cs +++ b/Content.Server/GameTicking/Presets/PresetTraitorDeathMatch.cs @@ -15,7 +15,6 @@ using Content.Server.Traitor; using Content.Server.TraitorDeathMatch.Components; using Content.Shared.CCVar; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.Inventory; using Content.Shared.MobState; using Content.Shared.PDA; @@ -28,6 +27,7 @@ using Robust.Shared.Localization; using Robust.Shared.Log; using Robust.Shared.Map; using Robust.Shared.Random; +using Content.Shared.Damage.Prototypes; namespace Content.Server.GameTicking.Presets { @@ -194,13 +194,10 @@ namespace Content.Server.GameTicking.Presets { if (mobState.IsCritical()) { - // TODO: This is copy/pasted from ghost code. Really, IDamageableComponent needs a method to reliably kill the target. - if (entity.TryGetComponent(out IDamageableComponent? damageable)) - { - //todo: what if they dont breathe lol - damageable.TryChangeDamage(_prototypeManager.Index("Asphyxiation"), 100, true); - } - } + // TODO BODY SYSTEM KILL + var damage = new DamageSpecifier(_prototypeManager.Index("Asphyxiation"), 100); + EntitySystem.Get().TryChangeDamage(entity.Uid, damage, true); + } else if (!mobState.IsDead()) { if (entity.HasComponent()) diff --git a/Content.Server/GameTicking/Rules/RuleDeathMatch.cs b/Content.Server/GameTicking/Rules/RuleDeathMatch.cs index bdf1e380f6..672a9a688f 100644 --- a/Content.Server/GameTicking/Rules/RuleDeathMatch.cs +++ b/Content.Server/GameTicking/Rules/RuleDeathMatch.cs @@ -34,7 +34,7 @@ namespace Content.Server.GameTicking.Rules { _chatManager.DispatchServerAnnouncement(Loc.GetString("rule-death-match-added-announcement")); - _entityManager.EventBus.SubscribeEvent(EventSource.Local, this, OnHealthChanged); + _entityManager.EventBus.SubscribeEvent(EventSource.Local, this, OnHealthChanged); _playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged; } @@ -42,11 +42,11 @@ namespace Content.Server.GameTicking.Rules { base.Removed(); - _entityManager.EventBus.UnsubscribeEvent(EventSource.Local, this); + _entityManager.EventBus.UnsubscribeEvent(EventSource.Local, this); _playerManager.PlayerStatusChanged -= PlayerManagerOnPlayerStatusChanged; } - private void OnHealthChanged(DamageChangedEventArgs message) + private void OnHealthChanged(DamageChangedEvent _) { _runDelayedCheck(); } diff --git a/Content.Server/Light/Components/PoweredLightComponent.cs b/Content.Server/Light/Components/PoweredLightComponent.cs index d5d830bd01..3500926088 100644 --- a/Content.Server/Light/Components/PoweredLightComponent.cs +++ b/Content.Server/Light/Components/PoweredLightComponent.cs @@ -6,7 +6,6 @@ using Content.Server.Power.Components; using Content.Server.Temperature.Components; using Content.Shared.Audio; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.Interaction; using Content.Shared.Light; using Content.Shared.Notification.Managers; @@ -16,7 +15,6 @@ using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.IoC; -using Robust.Shared.Prototypes; using Robust.Shared.Localization; using Robust.Shared.Maths; using Robust.Shared.Player; @@ -81,16 +79,13 @@ namespace Content.Server.Light.Components [ViewVariables] private ContainerSlot _lightBulbContainer = default!; - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - [DataField("damageType")] - private readonly string _damageTypeID = "Heat"; + [DataField("damage", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype DamageType = default!; + public DamageSpecifier Damage = default!; protected override void Initialize() { base.Initialize(); - DamageType = IoCManager.Resolve().Index(_damageTypeID); _lightBulbContainer = Owner.EnsureContainer("light_bulb"); } @@ -116,7 +111,7 @@ namespace Content.Server.Light.Components bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs) { - if (!eventArgs.User.TryGetComponent(out IDamageableComponent? damageableComponent)) + if (!eventArgs.User.HasComponent()) { Eject(); return false; @@ -143,7 +138,7 @@ namespace Content.Server.Light.Components void Burn() { Owner.PopupMessage(eventArgs.User, Loc.GetString("powered-light-component-burn-hand")); - damageableComponent.TryChangeDamage(DamageType, 20); + EntitySystem.Get().TryChangeDamage(eventArgs.User.Uid, Damage); SoundSystem.Play(Filter.Pvs(Owner), _burnHandSound.GetSound(), Owner); } @@ -285,17 +280,11 @@ namespace Content.Server.Light.Components case PowerChangedMessage: UpdateLight(); break; - case DamageChangedMessage msg: - TryDestroyBulb(msg); - break; } } - private void TryDestroyBulb(DamageChangedMessage msg) + public void TryDestroyBulb() { - if (!msg.TookDamage) - return; - if (LightBulb == null || LightBulb.State == LightBulbState.Broken) return; diff --git a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs index 96c4dbb908..9307ad501c 100644 --- a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs +++ b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs @@ -1,17 +1,19 @@ using System; using Content.Server.Ghost; using Content.Server.Light.Components; +using Content.Server.MachineLinking.Events; using Content.Shared.Light; +using Content.Shared.Damage; using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Timing; -using Content.Server.Light.Components; -using Content.Server.MachineLinking.Events; -using Robust.Shared.GameObjects; namespace Content.Server.Light.EntitySystems { + /// + /// System for the PoweredLightComponent. Currently bare-bones, to handle events from the DamageableSystem + /// public class PoweredLightSystem : EntitySystem { [Dependency] private readonly IGameTiming _gameTiming = default!; @@ -21,6 +23,20 @@ namespace Content.Server.Light.EntitySystems base.Initialize(); SubscribeLocalEvent(OnGhostBoo); SubscribeLocalEvent(OnSignalReceived); + SubscribeLocalEvent(HandleLightDamaged); + } + + /// + /// Destroy the light bulb if the light took any damage. + /// + public void HandleLightDamaged(EntityUid uid, PoweredLightComponent component, DamageChangedEvent args) + { + // Was it being repaired, or did it take damage? + if (args.DamageIncreased) + { + // Eventually, this logic should all be done by this (or some other) system, not a component. + component.TryDestroyBulb(); + } } private void OnGhostBoo(EntityUid uid, PoweredLightComponent light, GhostBooEvent args) diff --git a/Content.Server/Medical/Components/HealingComponent.cs b/Content.Server/Medical/Components/HealingComponent.cs index 990a26b438..1548e1b908 100644 --- a/Content.Server/Medical/Components/HealingComponent.cs +++ b/Content.Server/Medical/Components/HealingComponent.cs @@ -1,16 +1,12 @@ -using System.Collections.Generic; using System.Threading.Tasks; using Content.Server.Stack; using Content.Shared.ActionBlocker; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; using Content.Shared.Stacks; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.Prototypes; -using Robust.Shared.IoC; using Robust.Shared.ViewVariables; namespace Content.Server.Medical.Components @@ -20,12 +16,9 @@ namespace Content.Server.Medical.Components { public override string Name => "Healing"; - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // This also requires changing the dictionary type, and removing a _prototypeManager.Index() call. - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [DataField("heal", required: true )] + [DataField("damage", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public Dictionary Heal = new(); + public DamageSpecifier Damage = default!; async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { @@ -34,7 +27,7 @@ namespace Content.Server.Medical.Components return false; } - if (!eventArgs.Target.TryGetComponent(out IDamageableComponent? damageable)) + if (!eventArgs.Target.HasComponent()) { return true; } @@ -55,10 +48,7 @@ namespace Content.Server.Medical.Components return true; } - foreach (var (damageTypeID, amount) in Heal) - { - damageable.TryChangeDamage(_prototypeManager.Index(damageTypeID), -amount, true); - } + EntitySystem.Get().TryChangeDamage(eventArgs.Target.Uid, Damage, true); return true; } diff --git a/Content.Server/Medical/Components/MedicalScannerComponent.cs b/Content.Server/Medical/Components/MedicalScannerComponent.cs index c1a6aff647..04c69c164b 100644 --- a/Content.Server/Medical/Components/MedicalScannerComponent.cs +++ b/Content.Server/Medical/Components/MedicalScannerComponent.cs @@ -8,7 +8,6 @@ using Content.Server.UserInterface; using Content.Shared.ActionBlocker; using Content.Shared.Acts; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.DragDrop; using Content.Shared.Interaction; using Content.Shared.MedicalScanner; @@ -99,8 +98,7 @@ namespace Content.Server.Medical.Components private static readonly MedicalScannerBoundUserInterfaceState EmptyUIState = new( null, - new Dictionary(), - new Dictionary(), + null, false); private MedicalScannerBoundUserInterfaceState GetUserInterfaceState() @@ -116,18 +114,14 @@ namespace Content.Server.Medical.Components return EmptyUIState; } - if (!body.TryGetComponent(out IDamageableComponent? damageable)) + if (!body.TryGetComponent(out DamageableComponent? damageable)) { return EmptyUIState; } - // Get dictionaries of damage, by fully supported damage groups and types - var groups = new Dictionary(damageable.GetDamagePerFullySupportedGroupIDs); - var types = new Dictionary(damageable.GetDamagePerTypeIDs); - if (_bodyContainer.ContainedEntity?.Uid == null) { - return new MedicalScannerBoundUserInterfaceState(body.Uid, groups, types, true); + return new MedicalScannerBoundUserInterfaceState(body.Uid, damageable, true); } var cloningSystem = EntitySystem.Get(); @@ -135,7 +129,7 @@ namespace Content.Server.Medical.Components mindComponent.Mind != null && cloningSystem.HasDnaScan(mindComponent.Mind); - return new MedicalScannerBoundUserInterfaceState(body.Uid, groups, types, scanned); + return new MedicalScannerBoundUserInterfaceState(body.Uid, damageable, scanned); } private void UpdateUserInterface() diff --git a/Content.Server/Mining/Components/AsteroidRockComponent.cs b/Content.Server/Mining/Components/AsteroidRockComponent.cs index a01af77651..d1a88ddab2 100644 --- a/Content.Server/Mining/Components/AsteroidRockComponent.cs +++ b/Content.Server/Mining/Components/AsteroidRockComponent.cs @@ -1,7 +1,6 @@ using System.Threading.Tasks; using Content.Server.Weapon.Melee.Components; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.Interaction; using Content.Shared.Mining; using Robust.Server.GameObjects; @@ -24,16 +23,9 @@ namespace Content.Server.Mining.Components public override string Name => "AsteroidRock"; private static readonly string[] SpriteStates = {"0", "1", "2", "3", "4"}; - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - [DataField("damageType")] - private readonly string _damageTypeID = "Blunt"!; - [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype DamageType = default!; - protected override void Initialize() { base.Initialize(); - DamageType = IoCManager.Resolve().Index(_damageTypeID); if (Owner.TryGetComponent(out AppearanceComponent? appearance)) { appearance.SetData(AsteroidRockVisuals.State, _random.Pick(SpriteStates)); @@ -46,7 +38,7 @@ namespace Content.Server.Mining.Components if (!item.TryGetComponent(out MeleeWeaponComponent? meleeWeaponComponent)) return false; - Owner.GetComponent().TryChangeDamage(DamageType, meleeWeaponComponent.Damage); + EntitySystem.Get().TryChangeDamage(Owner.Uid, meleeWeaponComponent.Damage); if (!item.TryGetComponent(out PickaxeComponent? pickaxeComponent)) return true; diff --git a/Content.Server/MobState/States/MobStateManager.cs b/Content.Server/MobState/States/MobStateManager.cs deleted file mode 100644 index ac15fba39a..0000000000 --- a/Content.Server/MobState/States/MobStateManager.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Content.Shared.MobState; -using Content.Shared.MobState.Components; -using Content.Shared.MobState.State; -using Robust.Shared.GameObjects; - -namespace Content.Server.MobState.States -{ - [RegisterComponent] - [ComponentReference(typeof(SharedMobStateComponent))] - [ComponentReference(typeof(IMobStateComponent))] - public class MobStateComponent : SharedMobStateComponent - { - } -} diff --git a/Content.Server/MobState/States/NormalMobState.cs b/Content.Server/MobState/States/NormalMobState.cs index 2238457d5a..34212a1d4f 100644 --- a/Content.Server/MobState/States/NormalMobState.cs +++ b/Content.Server/MobState/States/NormalMobState.cs @@ -1,6 +1,6 @@ -using Content.Server.Alert; +using Content.Server.Alert; using Content.Shared.Alert; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; using Content.Shared.MobState; using Content.Shared.MobState.State; using Robust.Shared.GameObjects; @@ -13,7 +13,7 @@ namespace Content.Server.MobState.States { base.UpdateState(entity, threshold); - if (!entity.TryGetComponent(out IDamageableComponent? damageable)) + if (!entity.TryGetComponent(out DamageableComponent? damageable)) { return; } diff --git a/Content.Server/Nutrition/Components/HungerComponent.cs b/Content.Server/Nutrition/Components/HungerComponent.cs index 3aae6b5bee..2eb83f55d4 100644 --- a/Content.Server/Nutrition/Components/HungerComponent.cs +++ b/Content.Server/Nutrition/Components/HungerComponent.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using Content.Server.Alert; using Content.Shared.Alert; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.MobState; using Content.Shared.Movement.Components; using Content.Shared.Nutrition.Components; @@ -14,7 +13,6 @@ using Robust.Shared.Players; using Robust.Shared.Random; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; -using Robust.Shared.Prototypes; namespace Content.Server.Nutrition.Components { @@ -23,9 +21,7 @@ namespace Content.Server.Nutrition.Components { [Dependency] private readonly IRobustRandom _random = default!; - // TODO DAMAGE UNITS When damage units support decimals, get rid of this. - // See also _accumulatedDamage in ThirstComponent and HealthChange. - private float _accumulatedDamage; + private float _accumulatedFrameTime; // Base stuff [ViewVariables(VVAccess.ReadWrite)] @@ -78,17 +74,9 @@ namespace Content.Server.Nutrition.Components { HungerThreshold.Starving, AlertType.Starving }, }; - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // Also remove Initialize override, if no longer needed. - [DataField("damageType")] - private readonly string _damageTypeID = "Blunt"!; + [DataField("damage", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype DamageType = default!; - protected override void Initialize() - { - base.Initialize(); - DamageType = IoCManager.Resolve().Index(_damageTypeID); - } + public DamageSpecifier Damage = default!; public void HungerThresholdEffect(bool force = false) { @@ -196,21 +184,17 @@ namespace Content.Server.Nutrition.Components return; // --> Current Hunger is below dead threshold - if (!Owner.TryGetComponent(out IDamageableComponent? damageable)) - return; - if (!Owner.TryGetComponent(out IMobStateComponent? mobState)) return; if (!mobState.IsDead()) { // --> But they are not dead yet. - var damage = 2 * frametime; - _accumulatedDamage += damage - ((int) damage); - damageable.TryChangeDamage(DamageType, (int) damage); - if (_accumulatedDamage >= 1) { - _accumulatedDamage -= 1; - damageable.TryChangeDamage(DamageType, 1, true); + _accumulatedFrameTime += frametime; + if (_accumulatedFrameTime >= 1) + { + EntitySystem.Get().TryChangeDamage(Owner.Uid, Damage * (int) _accumulatedFrameTime, true); + _accumulatedFrameTime -= (int) _accumulatedFrameTime; } } } diff --git a/Content.Server/Nutrition/Components/ThirstComponent.cs b/Content.Server/Nutrition/Components/ThirstComponent.cs index 3826e398b2..314bc2c007 100644 --- a/Content.Server/Nutrition/Components/ThirstComponent.cs +++ b/Content.Server/Nutrition/Components/ThirstComponent.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using Content.Server.Alert; using Content.Shared.Alert; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.MobState; using Content.Shared.Movement.Components; using Content.Shared.Nutrition.Components; @@ -14,7 +13,6 @@ using Robust.Shared.Players; using Robust.Shared.Random; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; -using Robust.Shared.Prototypes; namespace Content.Server.Nutrition.Components { @@ -23,9 +21,7 @@ namespace Content.Server.Nutrition.Components { [Dependency] private readonly IRobustRandom _random = default!; - // TODO DAMAGE UNITS When damage units support decimals, get rid of this. - // See also _accumulatedDamage in HungerComponent and HealthChange. - private float _accumulatedDamage; + private float _accumulatedFrameTime; // Base stuff [ViewVariables(VVAccess.ReadWrite)] @@ -77,17 +73,9 @@ namespace Content.Server.Nutrition.Components {ThirstThreshold.Parched, AlertType.Parched}, }; - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // Also remove Initialize override, if no longer needed. - [DataField("damageType")] - private readonly string _damageTypeID = "Blunt"; + [DataField("damage", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype DamageType = default!; - protected override void Initialize() - { - base.Initialize(); - DamageType = IoCManager.Resolve().Index(_damageTypeID); - } + public DamageSpecifier Damage = default!; public void ThirstThresholdEffect(bool force = false) { @@ -193,22 +181,17 @@ namespace Content.Server.Nutrition.Components return; // --> Current Hunger is below dead threshold - if (!Owner.TryGetComponent(out IDamageableComponent? damageable)) - return; - if (!Owner.TryGetComponent(out IMobStateComponent? mobState)) return; if (!mobState.IsDead()) { // --> But they are not dead yet. - var damage = 2 * frametime; - _accumulatedDamage += damage - ((int) damage); - damageable.TryChangeDamage(DamageType, (int) damage); - if (_accumulatedDamage >= 1) + _accumulatedFrameTime += frametime; + if (_accumulatedFrameTime >= 1) { - _accumulatedDamage -= 1; - damageable.TryChangeDamage(DamageType, 1, true); + EntitySystem.Get().TryChangeDamage(Owner.Uid, Damage * (int) _accumulatedFrameTime, true); + _accumulatedFrameTime -= (int) _accumulatedFrameTime; } } } diff --git a/Content.Server/Projectiles/Components/HitscanComponent.cs b/Content.Server/Projectiles/Components/HitscanComponent.cs index 2274774edf..4ddc367d2a 100644 --- a/Content.Server/Projectiles/Components/HitscanComponent.cs +++ b/Content.Server/Projectiles/Components/HitscanComponent.cs @@ -9,7 +9,6 @@ using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Maths; using Robust.Shared.Player; -using Robust.Shared.Prototypes; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Timing; using Robust.Shared.ViewVariables; @@ -27,18 +26,19 @@ namespace Content.Server.Projectiles.Components public override string Name => "Hitscan"; public CollisionGroup CollisionMask => (CollisionGroup) _collisionMask; - [DataField("layers")] //todo WithFormat.Flags() private int _collisionMask = (int) CollisionGroup.Opaque; - [DataField("damage")] - public float Damage { get; set; } = 10f; - public float MaxLength => 20.0f; + [DataField("damage", required: true)] + [ViewVariables(VVAccess.ReadWrite)] + public DamageSpecifier Damage = default!; + + public float MaxLength => 20.0f; private TimeSpan _startTime; private TimeSpan _deathTime; public float ColorModifier { get; set; } = 1.0f; - [DataField("spriteName")] + [DataField("spriteName")] private string _spriteName = "Objects/Weapons/Guns/Projectiles/laser.png"; [DataField("muzzleFlash")] private string? _muzzleFlash; @@ -47,19 +47,6 @@ namespace Content.Server.Projectiles.Components [DataField("soundHitWall")] private SoundSpecifier _soundHitWall = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg"); - - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // Also remove Initialize override, if no longer needed. - [DataField("damageType")] - private readonly string _damageTypeID = "Piercing"; - [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype DamageType = default!; - protected override void Initialize() - { - base.Initialize(); - DamageType = IoCManager.Resolve().Index(_damageTypeID); - } - public void FireEffects(IEntity user, float distance, Angle angle, IEntity? hitEntity = null) { var effectSystem = EntitySystem.Get(); diff --git a/Content.Server/Projectiles/Components/ProjectileComponent.cs b/Content.Server/Projectiles/Components/ProjectileComponent.cs index 36e7297fef..86f5743f60 100644 --- a/Content.Server/Projectiles/Components/ProjectileComponent.cs +++ b/Content.Server/Projectiles/Components/ProjectileComponent.cs @@ -13,12 +13,9 @@ namespace Content.Server.Projectiles.Components [ComponentReference(typeof(SharedProjectileComponent))] public class ProjectileComponent : SharedProjectileComponent { - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // This also requires changing the dictionary type and modifying ProjectileSystem.cs, which uses it. - // While thats being done, also replace "damages" -> "damageTypes" For consistency. - [DataField("damages")] + [DataField("damage", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public Dictionary Damages { get; set; } = new(); + public DamageSpecifier Damage = default!; [DataField("deleteOnCollide")] public bool DeleteOnCollide { get; } = true; diff --git a/Content.Server/Projectiles/ProjectileSystem.cs b/Content.Server/Projectiles/ProjectileSystem.cs index d6783440af..b59b29d9ec 100644 --- a/Content.Server/Projectiles/ProjectileSystem.cs +++ b/Content.Server/Projectiles/ProjectileSystem.cs @@ -1,22 +1,20 @@ using Content.Server.Camera; using Content.Server.Projectiles.Components; using Content.Shared.Body.Components; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; using JetBrains.Annotations; using Robust.Shared.Audio; using Robust.Shared.GameObjects; +using Robust.Shared.IoC; using Robust.Shared.Physics.Dynamics; using Robust.Shared.Player; -using Robust.Shared.Prototypes; -using Robust.Shared.IoC; -using Content.Shared.Damage; namespace Content.Server.Projectiles { [UsedImplicitly] internal sealed class ProjectileSystem : EntitySystem { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly DamageableSystem _damageableSystem = default!; public override void Initialize() { @@ -50,16 +48,12 @@ namespace Content.Server.Projectiles SoundSystem.Play(playerFilter, soundHit, coordinates); } - if (!otherEntity.Deleted && otherEntity.TryGetComponent(out IDamageableComponent? damage)) + if (!otherEntity.Deleted) { - EntityManager.TryGetEntity(component.Shooter, out var shooter); - - foreach (var (damageTypeID, amount) in component.Damages) - { - damage.TryChangeDamage(_prototypeManager.Index(damageTypeID), amount); - } - + _damageableSystem.TryChangeDamage(otherEntity.Uid, component.Damage); component.DamagedEntity = true; + // "DamagedEntity" is misleading. Hit entity may be more accurate, as the damage may have been resisted + // by resistance sets. } // Damaging it can delete it diff --git a/Content.Server/Repairable/RepairableComponent.cs b/Content.Server/Repairable/RepairableComponent.cs index 8d5e99ad2f..1398d88799 100644 --- a/Content.Server/Repairable/RepairableComponent.cs +++ b/Content.Server/Repairable/RepairableComponent.cs @@ -1,50 +1,18 @@ -using System.Threading.Tasks; -using Content.Server.Tools.Components; -using Content.Shared.Damage.Components; -using Content.Shared.Interaction; -using Content.Shared.Notification; -using Content.Shared.Notification.Managers; -using Content.Shared.Tool; using Robust.Shared.GameObjects; -using Robust.Shared.Localization; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; namespace Content.Server.Repairable { [RegisterComponent] - public class RepairableComponent : Component, IInteractUsing + public class RepairableComponent : Component { public override string Name => "Repairable"; [ViewVariables(VVAccess.ReadWrite)] [DataField("fuelCost")] - private int _fuelCost = 5; + public int FuelCost = 5; [ViewVariables(VVAccess.ReadWrite)] [DataField("doAfterDelay")] - private int _doAfterDelay = 1; - - async Task IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) - { - // Only repair if you are using a lit welder - if (!eventArgs.Using.TryGetComponent(out WelderComponent? welder) || !welder.WelderLit) - return false; - - if (Owner.TryGetComponent(out IDamageableComponent? damageable)) - { - // Repair the target if it is damaged, oherwise do nothing - if (damageable.TotalDamage > 0) - { - if (!await welder.UseTool(eventArgs.User, Owner, _doAfterDelay, ToolQuality.Welding, _fuelCost)) - return false; - damageable.TrySetAllDamage(0); - - Owner.PopupMessage(eventArgs.User, - Loc.GetString("comp-repairable-repair", - ("target", Owner), - ("welder", eventArgs.Using))); - } - } - return true; - } + public int DoAfterDelay = 1; } } diff --git a/Content.Server/Repairable/RepairableSystem.cs b/Content.Server/Repairable/RepairableSystem.cs new file mode 100644 index 0000000000..f5a529cdf1 --- /dev/null +++ b/Content.Server/Repairable/RepairableSystem.cs @@ -0,0 +1,46 @@ +using Content.Server.Tools.Components; +using Content.Shared.Damage; +using Content.Shared.Interaction; +using Content.Shared.Notification.Managers; +using Content.Shared.Tool; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; + +namespace Content.Server.Repairable +{ + public class ReairableSystem : EntitySystem + { + [Dependency] private readonly DamageableSystem _damageableSystem = default!; + + public override void Initialize() + { + SubscribeLocalEvent(Repair); + } + + public async void Repair(EntityUid uid, RepairableComponent component, InteractUsingEvent args) + { + // Only repair if you are using a lit welder + if (!args.Used.TryGetComponent(out WelderComponent? welder) || !welder.WelderLit) + return; + + // Only try repair the target if it is damaged + if (!component.Owner.TryGetComponent(out DamageableComponent? damageable) || damageable.TotalDamage == 0) + return; + + // Can the welder actually repair this, does it have enough fuel? + if (!await welder.UseTool(args.User, component.Owner, component.DoAfterDelay, ToolQuality.Welding, component.FuelCost)) + return; + + // Repair all damage + _damageableSystem.SetAllDamage(damageable, 0); + + component.Owner.PopupMessage(args.User, + Loc.GetString("comp-repairable-repair", + ("target", component.Owner), + ("welder", args.Used))); + + args.Handled = true; + } + } +} diff --git a/Content.Server/Temperature/Components/TemperatureComponent.cs b/Content.Server/Temperature/Components/TemperatureComponent.cs index 360c2666c6..653d6062d8 100644 --- a/Content.Server/Temperature/Components/TemperatureComponent.cs +++ b/Content.Server/Temperature/Components/TemperatureComponent.cs @@ -3,7 +3,6 @@ using Content.Server.Alert; using Content.Shared.Alert; using Content.Shared.Atmos; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Robust.Shared.GameObjects; using Robust.Shared.Physics; using Robust.Shared.Serialization.Manager.Attributes; @@ -51,22 +50,13 @@ namespace Content.Server.Temperature.Components } } - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // Also remove Initialize override, if no longer needed. - [DataField("coldDamageType")] - private readonly string _coldDamageTypeID = "Cold"; + [DataField("coldDamage", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype ColdDamageType = default!; - [DataField("hotDamageType")] - private readonly string _hotDamageTypeID = "Heat"; + public DamageSpecifier ColdDamage = default!; + + [DataField("heatDamage", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype HotDamageType = default!; - protected override void Initialize() - { - base.Initialize(); - ColdDamageType = IoCManager.Resolve().Index(_coldDamageTypeID); - HotDamageType = IoCManager.Resolve().Index(_hotDamageTypeID); - } + public DamageSpecifier HeatDamage = default!; public void Update() { @@ -112,19 +102,18 @@ namespace Content.Server.Temperature.Components } } - if (!Owner.TryGetComponent(out IDamageableComponent? component)) return; + if (!Owner.HasComponent()) return; if (CurrentTemperature >= _heatDamageThreshold) { int tempDamage = (int) Math.Floor((CurrentTemperature - _heatDamageThreshold) * _tempDamageCoefficient); - component.TryChangeDamage(HotDamageType, tempDamage, false); + EntitySystem.Get().TryChangeDamage(Owner.Uid, HeatDamage * tempDamage); } else if (CurrentTemperature <= _coldDamageThreshold) { int tempDamage = (int) Math.Floor((_coldDamageThreshold - CurrentTemperature) * _tempDamageCoefficient); - component.TryChangeDamage(ColdDamageType, tempDamage, false); + EntitySystem.Get().TryChangeDamage(Owner.Uid, ColdDamage * tempDamage); } - } /// diff --git a/Content.Server/Weapon/Melee/Components/MeleeWeaponComponent.cs b/Content.Server/Weapon/Melee/Components/MeleeWeaponComponent.cs index 92cddcb470..bcbdf53ae2 100644 --- a/Content.Server/Weapon/Melee/Components/MeleeWeaponComponent.cs +++ b/Content.Server/Weapon/Melee/Components/MeleeWeaponComponent.cs @@ -4,8 +4,6 @@ using Content.Shared.Sound; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; -using Robust.Shared.IoC; -using Robust.Shared.Prototypes; namespace Content.Server.Weapon.Melee.Components { @@ -46,10 +44,6 @@ namespace Content.Server.Weapon.Melee.Components [DataField("range")] public float Range { get; set; } = 1; - [ViewVariables(VVAccess.ReadWrite)] - [DataField("damage")] - public int Damage { get; set; } = 5; - [ViewVariables(VVAccess.ReadWrite)] [DataField("clickAttackEffect")] public bool ClickAttackEffect { get; set; } = true; @@ -57,16 +51,8 @@ namespace Content.Server.Weapon.Melee.Components public TimeSpan LastAttackTime; public TimeSpan CooldownEnd; - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // Also remove Initialize override, if no longer needed. - [DataField("damageType")] - private readonly string _damageTypeID = "Blunt"; + [DataField("damage", required:true)] [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype DamageType = default!; - protected override void Initialize() - { - base.Initialize(); - DamageType = IoCManager.Resolve().Index(_damageTypeID); - } + public DamageSpecifier Damage = default!; } } diff --git a/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs index e991a06199..bfa2649335 100644 --- a/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs @@ -6,7 +6,7 @@ using Content.Server.Chemistry.Components; using Content.Server.Cooldown; using Content.Server.Weapon.Melee.Components; using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; using Content.Shared.Hands; using Content.Shared.Interaction; using Content.Shared.Physics; @@ -25,6 +25,7 @@ namespace Content.Server.Weapon.Melee public sealed class MeleeWeaponSystem : EntitySystem { [Dependency] private IGameTiming _gameTiming = default!; + [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private SolutionContainerSystem _solutionsSystem = default!; public override void Initialize() @@ -87,12 +88,7 @@ namespace Content.Server.Weapon.Melee { var targets = new[] { target }; SendAnimation(comp.ClickArc, angle, args.User, owner, targets, comp.ClickAttackEffect, false); - - if (target.TryGetComponent(out IDamageableComponent? damageableComponent)) - { - damageableComponent.TryChangeDamage(comp.DamageType, comp.Damage); - } - + _damageableSystem.TryChangeDamage(target.Uid, comp.Damage); SoundSystem.Play(Filter.Pvs(owner), comp.HitSound.GetSound(), target); } } @@ -133,7 +129,7 @@ namespace Content.Server.Weapon.Melee if (!entity.Transform.IsMapTransform || entity == args.User) continue; - if (ComponentManager.HasComponent(entity.Uid)) + if (ComponentManager.HasComponent(entity.Uid)) { hitEntities.Add(entity); } @@ -157,10 +153,7 @@ namespace Content.Server.Weapon.Melee foreach (var entity in hitEntities) { - if (entity.TryGetComponent(out var damageComponent)) - { - damageComponent.TryChangeDamage(comp.DamageType, comp.Damage); - } + _damageableSystem.TryChangeDamage(entity.Uid, comp.Damage); } } diff --git a/Content.Server/Weapon/Ranged/Barrels/Components/ServerBatteryBarrelComponent.cs b/Content.Server/Weapon/Ranged/Barrels/Components/ServerBatteryBarrelComponent.cs index a406693da7..ce037f8119 100644 --- a/Content.Server/Weapon/Ranged/Barrels/Components/ServerBatteryBarrelComponent.cs +++ b/Content.Server/Weapon/Ranged/Barrels/Components/ServerBatteryBarrelComponent.cs @@ -1,12 +1,10 @@ using System; -using System.Collections.Generic; using System.Threading.Tasks; using Content.Server.Hands.Components; using Content.Server.Items; using Content.Server.Power.Components; using Content.Server.Projectiles.Components; using Content.Shared.ActionBlocker; -using Content.Shared.Damage; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Sound; @@ -188,13 +186,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components { if (energyRatio < 1.0) { - var newDamages = new Dictionary(projectileComponent.Damages.Count); - foreach (var (damageType, damage) in projectileComponent.Damages) - { - newDamages.Add(damageType, (int) (damage * energyRatio)); - } - - projectileComponent.Damages = newDamages; + projectileComponent.Damage *= energyRatio; } } else if (entity.TryGetComponent(out HitscanComponent? hitscanComponent)) { diff --git a/Content.Server/Weapon/Ranged/Barrels/Components/ServerRangedBarrelComponent.cs b/Content.Server/Weapon/Ranged/Barrels/Components/ServerRangedBarrelComponent.cs index 790a21497d..0649dc0800 100644 --- a/Content.Server/Weapon/Ranged/Barrels/Components/ServerRangedBarrelComponent.cs +++ b/Content.Server/Weapon/Ranged/Barrels/Components/ServerRangedBarrelComponent.cs @@ -6,7 +6,7 @@ using Content.Server.Camera; using Content.Server.Projectiles.Components; using Content.Server.Weapon.Ranged.Ammunition.Components; using Content.Shared.Audio; -using Content.Shared.Damage.Components; +using Content.Shared.Damage; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Sound; @@ -395,13 +395,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components var result = rayCastResults[0]; var distance = result.Distance; hitscan.FireEffects(shooter, distance, angle, result.HitEntity); - - if (!result.HitEntity.TryGetComponent(out IDamageableComponent? damageable)) - return; - - damageable.TryChangeDamage(hitscan.DamageType, (int)Math.Round(hitscan.Damage, MidpointRounding.AwayFromZero)); - //I used Math.Round over Convert.toInt32, as toInt32 always rounds to - //even numbers if halfway between two numbers, rather than rounding to nearest + EntitySystem.Get().TryChangeDamage(result.HitEntity.Uid, hitscan.Damage); } else { diff --git a/Content.Server/Weapon/Ranged/ServerRangedWeaponComponent.cs b/Content.Server/Weapon/Ranged/ServerRangedWeaponComponent.cs index ef890b15a7..b149de094e 100644 --- a/Content.Server/Weapon/Ranged/ServerRangedWeaponComponent.cs +++ b/Content.Server/Weapon/Ranged/ServerRangedWeaponComponent.cs @@ -1,5 +1,4 @@ using System; -using Content.Server.Atmos; using Content.Server.Atmos.EntitySystems; using Content.Server.CombatMode; using Content.Server.Hands.Components; @@ -8,9 +7,7 @@ using Content.Server.Stunnable.Components; using Content.Server.Weapon.Ranged.Barrels.Components; using Content.Shared.ActionBlocker; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.Hands; -using Content.Shared.Interaction.Events; using Content.Shared.Notification.Managers; using Content.Shared.Sound; using Content.Shared.Weapons.Ranged.Components; @@ -27,8 +24,6 @@ using Robust.Shared.Players; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Timing; using Robust.Shared.ViewVariables; -using Robust.Shared.Prototypes; -using System.Collections.Generic; namespace Content.Server.Weapon.Ranged { @@ -57,17 +52,10 @@ namespace Content.Server.Weapon.Ranged [DataField("clumsyWeaponShotSound")] private SoundSpecifier _clumsyWeaponShotSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/bang.ogg"); - - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - // This also requires changing the dictionary type and modifying TryFire(), which uses it. - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [ViewVariables(VVAccess.ReadWrite)] [DataField("clumsyDamage")] - public Dictionary ClumsyDamage { get; set; } = new() - { - { "Blunt", 10 }, - { "Heat", 5 } - }; + public DamageSpecifier? ClumsyDamage; public Func? WeaponCanFireHandler; public Func? UserCanFireHandler; @@ -179,16 +167,10 @@ namespace Content.Server.Weapon.Ranged _lastFireTime = curTime; - if (ClumsyCheck && ClumsyComponent.TryRollClumsy(user, ClumsyExplodeChance)) + if (ClumsyCheck && ClumsyDamage != null && ClumsyComponent.TryRollClumsy(user, ClumsyExplodeChance)) { //Wound them - if (user.TryGetComponent(out IDamageableComponent? health)) - { - foreach (KeyValuePair damage in ClumsyDamage) - { - health.TryChangeDamage(_prototypeManager.Index(damage.Key), damage.Value); - } - } + EntitySystem.Get().TryChangeDamage(user.Uid, ClumsyDamage); // Knock them down if (user.TryGetComponent(out StunnableComponent? stun)) @@ -197,14 +179,14 @@ namespace Content.Server.Weapon.Ranged } // Apply salt to the wound ("Honk!") - SoundSystem.Play( + SoundSystem.Play( Filter.Pvs(Owner), _clumsyWeaponHandlingSound.GetSound(), Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5)); SoundSystem.Play( Filter.Pvs(Owner), _clumsyWeaponShotSound.GetSound(), Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5)); - + user.PopupMessage(Loc.GetString("server-ranged-weapon-component-try-fire-clumsy")); Owner.Delete(); diff --git a/Content.Server/Window/WindowComponent.cs b/Content.Server/Window/WindowComponent.cs index 9eeca09d67..188e2bd782 100644 --- a/Content.Server/Window/WindowComponent.cs +++ b/Content.Server/Window/WindowComponent.cs @@ -4,7 +4,6 @@ using Content.Server.Destructible.Thresholds.Triggers; using Content.Server.Notification; using Content.Shared.Audio; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Rounding; @@ -41,22 +40,7 @@ namespace Content.Server.Window [DataField("knockSound")] private SoundSpecifier _knockSound = new SoundPathSpecifier("/Audio/Effects/glass_knock.ogg"); - public override void HandleMessage(ComponentMessage message, IComponent? component) - { - base.HandleMessage(message, component); - - switch (message) - { - case DamageChangedMessage msg: - { - var current = msg.Damageable.TotalDamage; - UpdateVisuals(current); - break; - } - } - } - - private void UpdateVisuals(int currentDamage) + public void UpdateVisuals(int currentDamage) { if (Owner.TryGetComponent(out AppearanceComponent? appearance) && Owner.TryGetComponent(out DestructibleComponent? destructible)) @@ -75,7 +59,7 @@ namespace Content.Server.Window void IExamine.Examine(FormattedMessage message, bool inDetailsRange) { - if (!Owner.TryGetComponent(out IDamageableComponent? damageable) || + if (!Owner.TryGetComponent(out DamageableComponent? damageable) || !Owner.TryGetComponent(out DestructibleComponent? destructible)) { return; diff --git a/Content.Server/Window/WindowSystem.cs b/Content.Server/Window/WindowSystem.cs new file mode 100644 index 0000000000..b711b9ea0a --- /dev/null +++ b/Content.Server/Window/WindowSystem.cs @@ -0,0 +1,19 @@ +using Content.Shared.Damage; +using Robust.Shared.GameObjects; + +namespace Content.Server.Window +{ + public class WindowSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(UpdateVisuals); + } + + public void UpdateVisuals(EntityUid _, WindowComponent component, DamageChangedEvent args) + { + component.UpdateVisuals(args.Damageable.TotalDamage); + } + } +} diff --git a/Content.Shared/Body/Components/SharedBodyComponent.cs b/Content.Shared/Body/Components/SharedBodyComponent.cs index 883cc771f4..d0580a93e3 100644 --- a/Content.Shared/Body/Components/SharedBodyComponent.cs +++ b/Content.Shared/Body/Components/SharedBodyComponent.cs @@ -9,7 +9,7 @@ using Content.Shared.Body.Preset; using Content.Shared.Body.Slot; using Content.Shared.Body.Template; using Content.Shared.Damage; -using Content.Shared.Damage.Components; +using Content.Shared.Damage.Prototypes; using Content.Shared.Movement.Components; using Content.Shared.Standing; using Robust.Shared.GameObjects; @@ -73,23 +73,6 @@ namespace Content.Shared.Body.Components public SharedBodyPartComponent? CenterPart => CenterSlot?.Part; - /// - /// Amount of damage to deal when all vital organs are removed. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("vitalPartsRemovedDamage")] - public int VitalPartsRemovedDamage { get; set; } = 300!; - - /// - /// Damage type to deal when all vital organs are removed. - /// - // TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported. - [ViewVariables] - [DataField("vitalPartsRemovedDamageType")] - private string _vitalPartsRemovedDamageTypeID { get; set; } = "Bloodloss"!; - [ViewVariables(VVAccess.ReadWrite)] - public DamageTypePrototype VitalPartsRemovedDamageType = default!; - protected override void Initialize() { base.Initialize(); @@ -98,7 +81,6 @@ namespace Content.Shared.Body.Components // TODO BODY Move to template or somewhere else if (TemplateId != null) { - VitalPartsRemovedDamageType = _prototypeManager.Index(_vitalPartsRemovedDamageTypeID); var template = _prototypeManager.Index(TemplateId); foreach (var (id, partType) in template.Slots) @@ -207,13 +189,11 @@ namespace Content.Shared.Body.Components EntitySystem.Get().Down(Owner); } - // creadth: immediately kill entity if last vital part removed - if (Owner.TryGetComponent(out IDamageableComponent? damageable)) + if (part.IsVital && SlotParts.Count(x => x.Value.PartType == part.PartType) == 0) { - if (part.IsVital && SlotParts.Count(x => x.Value.PartType == part.PartType) == 0) - { - damageable.TryChangeDamage(VitalPartsRemovedDamageType, VitalPartsRemovedDamage, true); // TODO BODY KILL - } + // TODO BODY SYSTEM KILL : Find a more elegant way of killing em than just dumping bloodloss damage. + var damage = new DamageSpecifier(_prototypeManager.Index("Bloodloss"), 300); + EntitySystem.Get().TryChangeDamage(part.Owner.Uid, damage); } OnBodyChanged(); @@ -490,6 +470,7 @@ namespace Content.Shared.Body.Components } } + private void OnBodyChanged() { Dirty(); diff --git a/Content.Shared/Damage/Components/DamageableComponent.cs b/Content.Shared/Damage/Components/DamageableComponent.cs deleted file mode 100644 index a547f2731f..0000000000 --- a/Content.Shared/Damage/Components/DamageableComponent.cs +++ /dev/null @@ -1,499 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Content.Shared.Acts; -using Content.Shared.Damage.Container; -using Content.Shared.Damage.Resistances; -using Content.Shared.Radiation; -using Robust.Shared.GameObjects; -using Robust.Shared.GameStates; -using Robust.Shared.IoC; -using Robust.Shared.Players; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; - -namespace Content.Shared.Damage.Components -{ - /// - /// Component that allows attached entities to take damage. - /// - /// - /// The supported damage types are specified using a s. DamageContainers - /// are effectively a dictionary of damage types and damage numbers, along with functions to modify them. Damage - /// groups are collections of damage types. A damage group is 'applicable' to a damageable component if it - /// supports at least one damage type in that group. A subset of these groups may be 'fully supported' when every - /// member of the group is supported by the container. This basic version never dies (thus can take an - /// indefinite amount of damage). - /// - [RegisterComponent] - [ComponentReference(typeof(IDamageableComponent))] - [NetworkedComponent()] - public class DamageableComponent : Component, IDamageableComponent, IRadiationAct, ISerializationHooks - { - public override string Name => "Damageable"; - - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - - /// - /// The main damage dictionary. All the damage information is stored in this dictionary with keys. - /// - private Dictionary _damageDict = new(); - - [DataField("resistances")] - public string ResistanceSetId { get; set; } = "defaultResistances"; - - [ViewVariables] public ResistanceSet Resistances { get; set; } = new(); - - // TODO DAMAGE Use as default values, specify overrides in a separate property through yaml for better (de)serialization - [ViewVariables] - [DataField("damageContainer")] - public string DamageContainerId { get; set; } = "metallicDamageContainer"; - - // TODO DAMAGE Cache this - // When moving logic from damageableComponent --> Damage System, make damageSystem update these on damage change. - [ViewVariables] public int TotalDamage => _damageDict.Values.Sum(); - [ViewVariables] public IReadOnlyDictionary GetDamagePerType => _damageDict; - [ViewVariables] public IReadOnlyDictionary GetDamagePerApplicableGroup => DamageTypeDictToDamageGroupDict(_damageDict, ApplicableDamageGroups); - [ViewVariables] public IReadOnlyDictionary GetDamagePerFullySupportedGroup => DamageTypeDictToDamageGroupDict(_damageDict, FullySupportedDamageGroups); - - // Whenever sending over network, also need a dictionary - // TODO DAMAGE MAYBE Cache this? - public IReadOnlyDictionary GetDamagePerApplicableGroupIDs => ConvertDictKeysToIDs(GetDamagePerApplicableGroup); - public IReadOnlyDictionary GetDamagePerFullySupportedGroupIDs => ConvertDictKeysToIDs(GetDamagePerFullySupportedGroup); - public IReadOnlyDictionary GetDamagePerTypeIDs => ConvertDictKeysToIDs(_damageDict); - - // TODO PROTOTYPE Replace these datafield variables with prototype references, once they are supported. - // Also requires appropriate changes in OnExplosion() and RadiationAct() - [ViewVariables] - [DataField("radiationDamageTypes")] - public List RadiationDamageTypeIDs { get; set; } = new() {"Radiation"}; - [ViewVariables] - [DataField("explosionDamageTypes")] - public List ExplosionDamageTypeIDs { get; set; } = new() { "Piercing", "Heat" }; - - public HashSet ApplicableDamageGroups { get; } = new(); - - public HashSet FullySupportedDamageGroups { get; } = new(); - - public HashSet SupportedDamageTypes { get; } = new(); - - protected override void Initialize() - { - base.Initialize(); - - // TODO DAMAGE Serialize damage done and resistance changes - var damageContainerPrototype = _prototypeManager.Index(DamageContainerId); - - ApplicableDamageGroups.Clear(); - FullySupportedDamageGroups.Clear(); - SupportedDamageTypes.Clear(); - - //Get Damage groups/types from the DamageContainerPrototype. - DamageContainerId = damageContainerPrototype.ID; - ApplicableDamageGroups.UnionWith(damageContainerPrototype.ApplicableDamageGroups); - FullySupportedDamageGroups.UnionWith(damageContainerPrototype.FullySupportedDamageGroups); - SupportedDamageTypes.UnionWith(damageContainerPrototype.SupportedDamageTypes); - - //initialize damage dictionary 0 damage - _damageDict = new(SupportedDamageTypes.Count); - foreach (var type in SupportedDamageTypes) - { - _damageDict.Add(type, 0); - } - - Resistances = new ResistanceSet(_prototypeManager.Index(ResistanceSetId)); - } - - protected override void Startup() - { - base.Startup(); - - ForceHealthChangedEvent(); - } - - public override ComponentState GetComponentState(ICommonSession player) - { - return new DamageableComponentState(GetDamagePerTypeIDs); - } - - public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) - { - base.HandleComponentState(curState, nextState); - - if (!(curState is DamageableComponentState state)) - { - return; - } - - _damageDict.Clear(); - - foreach (var (type, damage) in state.DamageDict) - { - _damageDict[_prototypeManager.Index(type)] = damage; - } - } - - public int GetDamage(DamageTypePrototype type) - { - return GetDamagePerType.GetValueOrDefault(type); - } - - public bool TryGetDamage(DamageTypePrototype type, out int damage) - { - return GetDamagePerType.TryGetValue(type, out damage); - } - - public int GetDamage(DamageGroupPrototype group) - { - return GetDamagePerApplicableGroup.GetValueOrDefault(group); - } - - public bool TryGetDamage(DamageGroupPrototype group, out int damage) - { - return GetDamagePerApplicableGroup.TryGetValue(group, out damage); - } - - public bool IsApplicableDamageGroup(DamageGroupPrototype group) - { - return ApplicableDamageGroups.Contains(group); - } - - public bool IsFullySupportedDamageGroup(DamageGroupPrototype group) - { - return FullySupportedDamageGroups.Contains(group); - } - - public bool IsSupportedDamageType(DamageTypePrototype type) - { - return SupportedDamageTypes.Contains(type); - } - - public bool TrySetDamage(DamageGroupPrototype group, int newValue) - { - if (!ApplicableDamageGroups.Contains(group)) - { - return false; - } - - if (newValue < 0) - { - // invalid value - return false; - } - - foreach (var type in group.DamageTypes) - { - TrySetDamage(type, newValue); - } - return true; - } - - public bool TrySetAllDamage(int newValue) - { - if (newValue < 0) - { - // invalid value - return false; - } - - foreach (var type in SupportedDamageTypes) - { - TrySetDamage(type, newValue); - } - - return true; - } - - public bool TryChangeDamage(DamageTypePrototype type, int amount, bool ignoreDamageResistances = false) - { - // Check if damage type is supported, and get the current value if it is. - if (!GetDamagePerType.TryGetValue(type, out var current)) - { - return false; - } - - if (amount == 0) - { - return false; - } - - // Apply resistances (does nothing if amount<0) - var finalDamage = amount; - if (!ignoreDamageResistances) - { - finalDamage = Resistances.CalculateDamage(type, amount); - } - - if (finalDamage == 0) - return false; - - // Are we healing below zero? - if (current + finalDamage < 0) - { - if (current == 0) - // Damage type is supported, but there is nothing to do - return false; - - // Cap healing down to zero - _damageDict[type] = 0; - finalDamage = -current; - } - else - { - _damageDict[type] = current + finalDamage; - } - - current = _damageDict[type]; - - var datum = new DamageChangeData(type, current, finalDamage); - var data = new List {datum}; - - OnHealthChanged(data); - - return true; - } - - public bool TryChangeDamage(DamageGroupPrototype group, int amount, bool ignoreDamageResistances = false) - { - var types = group.DamageTypes.ToArray(); - - if (amount < 0) - { - // We are Healing. Keep track of how much we can hand out (with a better var name for readability). - var availableHealing = -amount; - - // Get total group damage. - var damageToHeal = GetDamagePerApplicableGroup[group]; - - // Is there any damage to even heal? - if (damageToHeal == 0) - return false; - - // If total healing is more than there is damage, just set to 0 and return. - if (damageToHeal <= availableHealing) - { - TrySetDamage(group, 0); - return true; - } - - // Partially heal each damage group - int healing, damage; - foreach (var type in types) - { - if (!_damageDict.TryGetValue(type, out damage)) - { - // Damage Type is not supported. Continue without reducing availableHealing - continue; - } - - // Apply healing to the damage type. The healing amount may be zero if either damage==0, or if - // integer rounding made it zero (i.e., damage is small) - healing = (availableHealing * damage) / damageToHeal; - TryChangeDamage(type, -healing, ignoreDamageResistances); - - // remove this damage type from the damage we consider for future loops, regardless of how much we - // actually healed this type. - damageToHeal -= damage; - availableHealing -= healing; - - // If we now healed all the damage, exit. otherwise 1/0 and universe explodes. - if (damageToHeal == 0) - { - break; - } - } - - // Damage type is supported, there was damage to heal, and resistances were ignored - // --> Damage must have changed - return true; - } - else if (amount > 0) - { - // Resistances may result in no actual damage change. We need to keep track if any damage got through. - var damageChanged = false; - - // We are adding damage. Keep track of how much we can dish out (with a better var name for readability). - var availableDamage = amount; - - // How many damage types do we have to distribute over?. - var numberDamageTypes = types.Length; - - // Apply damage to each damage group - int damage; - foreach (var type in types) - { - // Distribute the remaining damage over the remaining damage types. - damage = availableDamage / numberDamageTypes; - - // Try apply the damage type. If damage type is not supported, this has no effect. - // We also use the return value to check whether any damage has changed - damageChanged = TryChangeDamage(type, damage, ignoreDamageResistances) || damageChanged; - - // regardless of whether we dealt damage, reduce the amount to distribute. - availableDamage -= damage; - numberDamageTypes -= 1; - - } - return damageChanged; - } - - // amount==0 no damage change. - return false; - } - - public bool TrySetDamage(DamageTypePrototype type, int newValue) - { - if (!_damageDict.TryGetValue(type, out var oldValue)) - { - return false; - } - - if (newValue < 0) - { - // invalid value - return false; - } - - if (oldValue == newValue) - { - // No health change. - // But we are trying to set, not trying to change. - return true; - } - - _damageDict[type] = newValue; - - var delta = newValue - oldValue; - var datum = new DamageChangeData(type, 0, delta); - var data = new List {datum}; - - OnHealthChanged(data); - - return true; - } - - public void ForceHealthChangedEvent() - { - var data = new List(); - - foreach (var type in SupportedDamageTypes) - { - var damage = GetDamage(type); - var datum = new DamageChangeData(type, damage, 0); - data.Add(datum); - } - - OnHealthChanged(data); - } - - private void OnHealthChanged(List changes) - { - var args = new DamageChangedEventArgs(this, changes); - OnHealthChanged(args); - } - - protected virtual void OnHealthChanged(DamageChangedEventArgs e) - { - Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, e); - - var message = new DamageChangedMessage(this, e.Data); - SendMessage(message); - - Dirty(); - } - - public void RadiationAct(float frameTime, SharedRadiationPulseComponent radiation) - { - var totalDamage = Math.Max((int)(frameTime * radiation.RadsPerSecond), 1); - - foreach (var typeID in RadiationDamageTypeIDs) - { - TryChangeDamage(_prototypeManager.Index(typeID), totalDamage); - } - - } - - public void OnExplosion(ExplosionEventArgs eventArgs) - { - var damage = eventArgs.Severity switch - { - ExplosionSeverity.Light => 20, - ExplosionSeverity.Heavy => 60, - ExplosionSeverity.Destruction => 250, - _ => throw new ArgumentOutOfRangeException() - }; - - foreach (var typeID in ExplosionDamageTypeIDs) - { - TryChangeDamage(_prototypeManager.Index(typeID), damage); - } - } - - /// - /// Take a dictionary with keys and return a dictionary using as keys - /// instead. - /// - /// - /// Useful when sending damage type and group prototypes dictionaries over the network. - /// - public static IReadOnlyDictionary - ConvertDictKeysToIDs(IReadOnlyDictionary prototypeDict) - where TPrototype : IPrototype - { - Dictionary idDict = new(prototypeDict.Count); - foreach (var entry in prototypeDict) - { - idDict.Add(entry.Key.ID, entry.Value); - } - return idDict; - } - - /// - /// Convert a dictionary with damage type keys to a dictionary of damage groups keys. - /// - /// - /// Takes a dictionary with damage types as keys and integers as values, and an iterable list of damage - /// groups. Returns a dictionary with damage group keys, with values calculated by adding up the values for - /// each damage type in that group. If a damage type is associated with more than one supported damage - /// group, it will contribute to the total of each group. Conversely, some damage types may not contribute - /// to the new dictionary if their associated group(s) are not in given list of groups. - /// - public static IReadOnlyDictionary - DamageTypeDictToDamageGroupDict(IReadOnlyDictionary damageTypeDict, IEnumerable groupKeys) - { - var damageGroupDict = new Dictionary(); - int damageGroupSumDamage, damageTypeDamage; - // iterate over the list of group keys for our new dictionary - foreach (var group in groupKeys) - { - // For each damage type in this group, add up the damage present in the given dictionary - damageGroupSumDamage = 0; - foreach (var type in group.DamageTypes) - { - // if the damage type is in the dictionary, add it's damage to the group total. - if (damageTypeDict.TryGetValue(type, out damageTypeDamage)) - { - damageGroupSumDamage += damageTypeDamage; - } - } - damageGroupDict.Add(group, damageGroupSumDamage); - } - return damageGroupDict; - } - - } - - [Serializable, NetSerializable] - public class DamageableComponentState : ComponentState - { - public readonly IReadOnlyDictionary DamageDict; - - public DamageableComponentState(IReadOnlyDictionary damageDict) - - { - DamageDict = damageDict; - } - } -} diff --git a/Content.Shared/Damage/Components/IDamageableComponent.cs b/Content.Shared/Damage/Components/IDamageableComponent.cs deleted file mode 100644 index 54fbece0dd..0000000000 --- a/Content.Shared/Damage/Components/IDamageableComponent.cs +++ /dev/null @@ -1,225 +0,0 @@ -using System.Collections.Generic; -using Content.Shared.Acts; -using Content.Shared.Damage.Resistances; -using Robust.Shared.GameObjects; - -namespace Content.Shared.Damage.Components -{ - public interface IDamageableComponent : IComponent, IExAct - { - /// - /// The sum of all damages types in the DamageableComponent. - /// - int TotalDamage { get; } - - /// - /// Returns a dictionary of the damage in the container, indexed by applicable . - /// - /// - /// The values represent the sum of all damage in each group. If a supported damage type is a member of more than one group, it will contribute to each one. - /// Therefore, the sum of the values may be greater than the sum of the values in the dictionary returned by - /// - IReadOnlyDictionary GetDamagePerApplicableGroup { get; } - - /// - /// Returns a dictionary of the damage in the container, indexed by fully supported instances of . - /// - /// - /// The values represent the sum of all damage in each group. As the damage container may have some damage - /// types that are not part of a fully supported damage group, the sum of the values may be less of the values - /// in the dictionary returned by . On the other hand, if a supported damage type - /// is a member of more than one group, it will contribute to each one. Therefore, the sum may also be greater - /// instead. - /// - IReadOnlyDictionary GetDamagePerFullySupportedGroup { get; } - - /// - /// Returns a dictionary of the damage in the container, indexed by . - /// - IReadOnlyDictionary GetDamagePerType { get; } - - /// - /// Like , but indexed by - /// - IReadOnlyDictionary GetDamagePerApplicableGroupIDs { get; } - - /// - /// Like , but indexed by - /// - IReadOnlyDictionary GetDamagePerFullySupportedGroupIDs { get; } - - /// - /// Like , but indexed by - /// - IReadOnlyDictionary GetDamagePerTypeIDs { get; } - - /// - /// Collection of damage types supported by this DamageableComponent. - /// - /// - /// Each of these damage types is fully supported. If any of these damage types is a - /// member of a damage group, these groups are represented in - /// - HashSet SupportedDamageTypes { get; } - - /// - /// Collection of damage groups that are fully supported by DamageableComponent. - /// - /// - /// This describes what damage groups this damage container explicitly supports. It supports every damage type - /// contained in these damage groups. It may also support other damage types not in these groups. To see all - /// damage types , and to see all applicable damage groups . - /// - HashSet FullySupportedDamageGroups { get; } - - /// - /// Collection of damage groups that could apply damage to this DamageableComponent. - /// - /// - /// This describes what damage groups could have an effect on this damage container. However not every damage - /// group has to be fully supported. For example, the container may support ONLY the piercing damage type. It should - /// therefore be affected by instances of brute damage, but does not necessarily support blunt or slash damage. - /// For a list of supported damage types, see . - /// - HashSet ApplicableDamageGroups { get; } - - /// - /// The resistances of this component. - /// - ResistanceSet Resistances { get; } - - /// - /// Tries to get the amount of damage of a type. - /// - /// The type to get the damage of. - /// The amount of damage of that type. - /// - /// True if the given is supported, false otherwise. - /// - bool TryGetDamage(DamageTypePrototype type, out int damage); - - /// - /// Returns the amount of damage of a given type, or zero if it is not supported. - /// - int GetDamage(DamageTypePrototype type); - - /// - /// Tries to get the total amount of damage in a damage group. - /// - /// The group to get the damage of. - /// The amount of damage in that group. - /// - /// True if the given group is applicable to this container, false otherwise. - /// - bool TryGetDamage(DamageGroupPrototype group, out int damage); - - /// - /// Returns the amount of damage present in an applicable group, or zero if no members are supported. - /// - int GetDamage(DamageGroupPrototype group); - - /// - /// Tries to change the specified , applying - /// resistance values only if it is dealing damage. - /// - /// Type of damage being changed. - /// - /// Amount of damage being received (positive for damage, negative for heals). - /// - /// - /// Whether or not to ignore resistances when taking damage. - /// Healing always ignores resistances, regardless of this input. - /// - /// - /// False if the given type is not supported or no damage change occurred; true otherwise. - /// - bool TryChangeDamage(DamageTypePrototype type, int amount, bool ignoreDamageResistances = false); - - /// - /// Tries to change damage of the specified , applying resistance values - /// only if it is damage. - /// - /// - /// - /// If dealing damage, this spreads the damage change amount evenly between the s in this group (subject to integer rounding). If only a subset of the - /// damage types in the group are actually supported, then the total damage dealt may be less than expected - /// (unsupported damage is ignored). - /// - /// - /// If healing damage, this spreads the damage change proportional to the current damage value of each (subject to integer rounding). If there is less damage than is being - /// healed, some healing is wasted. Unsupported damage types do not waste healing. - /// - /// - /// group of damage being changed. - /// - /// Amount of damage being received (positive for damage, negative for heals). - /// - /// - /// Whether to ignore resistances when taking damage. Healing always ignores resistances, regardless of this - /// input. - /// - /// - /// Returns false if the given group is not applicable or no damage change occurred; true otherwise. - /// - bool TryChangeDamage(DamageGroupPrototype group, int amount, bool ignoreDamageResistances = false); - - /// - /// Forcefully sets the specified to the given value, ignoring resistance - /// values. - /// - /// Type of damage being set. - /// New damage value to be set. - /// - /// Returns false if a given type is not supported or a negative value is provided; true otherwise. - /// - bool TrySetDamage(DamageTypePrototype type, int newValue); - - /// - /// Forcefully sets all damage types in a specified damage group using . - /// - /// - /// Note that the actual damage of this group will be equal to the given value times the number damage group - /// members that this container supports. - /// - /// Group of damage being set. - /// New damage value to be set. - /// - /// Returns false if the given group is not applicable or a negative value is provided; true otherwise. - /// - bool TrySetDamage(DamageGroupPrototype group, int newValue); - - /// - /// Sets all supported damage types to specified value using . - /// - /// New damage value to be set. - /// - /// Returns false if a negative value is provided; true otherwise. - /// - bool TrySetAllDamage(int newValue); - - /// - /// Returns true if the given damage group is applicable to this damage container. - /// - public bool IsApplicableDamageGroup(DamageGroupPrototype group); - - /// - /// Returns true if the given damage group is fully supported by this damage container. - /// - public bool IsFullySupportedDamageGroup(DamageGroupPrototype group); - - /// - /// Returns true if the given damage type is supported by this damage container. - /// - public bool IsSupportedDamageType(DamageTypePrototype type); - - - /// - /// Invokes the HealthChangedEvent with the current values of health. - /// - void ForceHealthChangedEvent(); - } -} diff --git a/Content.Shared/Damage/Container/DamageContainerPrototype.cs b/Content.Shared/Damage/Container/DamageContainerPrototype.cs deleted file mode 100644 index 51abd75224..0000000000 --- a/Content.Shared/Damage/Container/DamageContainerPrototype.cs +++ /dev/null @@ -1,151 +0,0 @@ -using System; -using System.Collections.Generic; -using Robust.Shared.IoC; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; - -namespace Content.Shared.Damage.Container -{ - /// - /// A damage container which can be used to specify support for various damage types. - /// - /// - /// This is effectively just a list of damage types that can be specified in YAML files using both damage types - /// and damage groups. Currently this is only used to specify what damage types a should support. - /// - [Prototype("damageContainer")] - [Serializable, NetSerializable] - public class DamageContainerPrototype : IPrototype, ISerializationHooks - { - private IPrototypeManager _prototypeManager = default!; - - [ViewVariables] - [DataField("id", required: true)] - public string ID { get; } = default!; - - /// - /// Determines whether this DamageContainerPrototype will support ALL damage types and groups. If true, - /// ignore all other options. - /// - [DataField("supportAll")] private bool _supportAll; - - [DataField("supportedGroups")] private HashSet _supportedDamageGroupIDs = new(); - [DataField("supportedTypes")] private HashSet _supportedDamageTypeIDs = new(); - - private HashSet _applicableDamageGroups = new(); - private HashSet _fullySupportedDamageGroups = new(); - private HashSet _supportedDamageTypes = new(); - - // TODO NET 5 IReadOnlySet - - /// - /// Collection of damage groups that can affect this container. - /// - /// - /// This describes what damage groups can have an effect on this damage container. However not every damage - /// group has to be fully supported. For example, the container may support ONLY the piercing damage type. - /// It should therefore be affected by instances of brute group damage, but does not necessarily support - /// blunt or slash damage. If damage containers are only specified by supported damage groups, and every - /// damage type is in only one damage group, then SupportedDamageTypes should be equal to - /// ApplicableDamageGroups. For a list of supported damage types, see . - /// - [ViewVariables] public IReadOnlyCollection ApplicableDamageGroups => _applicableDamageGroups; - - /// - /// Collection of damage groups that are fully supported by this container. - /// - /// - /// This describes what damage groups this damage container explicitly supports. It supports every damage - /// type contained in these damage groups. It may also support other damage types not in these groups. To - /// see all damage types , and to see all applicable damage groups . - /// - [ViewVariables] public IReadOnlyCollection FullySupportedDamageGroups => _fullySupportedDamageGroups; - - /// - /// Collection of damage types supported by this container. - /// - /// - /// Each of these damage types is fully supported by the DamageContainer. If any of these damage types is a - /// member of a damage group, these groups are added to - /// - [ViewVariables] public IReadOnlyCollection SupportedDamageTypes => _supportedDamageTypes; - - void ISerializationHooks.AfterDeserialization() - { - _prototypeManager = IoCManager.Resolve(); - - if (_supportAll) - { - foreach (var group in _prototypeManager.EnumeratePrototypes()) - { - _applicableDamageGroups.Add(group); - _fullySupportedDamageGroups.Add(group); - } - foreach (var type in _prototypeManager.EnumeratePrototypes()) - { - _supportedDamageTypes.Add(type); - } - return; - } - - // Add fully supported damage groups - foreach (var groupID in _supportedDamageGroupIDs) - { - var group = _prototypeManager.Index(groupID); - _fullySupportedDamageGroups.Add(group); - foreach (var type in group.DamageTypes) - { - _supportedDamageTypes.Add(type); - } - } - - // Add individual damage types, that are either not part of a group, or whose groups are (possibly) not fully supported - foreach (var supportedTypeID in _supportedDamageTypeIDs) - { - var type = _prototypeManager.Index(supportedTypeID); - _supportedDamageTypes.Add(type); - } - - // For whatever reason, someone may have listed all members of a group as supported instead of just listing - // the group as supported. Check for this. - foreach (var group in _prototypeManager.EnumeratePrototypes()) - { - if (_fullySupportedDamageGroups.Contains(group)) - { - continue; - } - // The group is not in the list of fully supported groups. Should it be? - var allMembersSupported = true; - foreach (var type in group.DamageTypes) - { - if (!_supportedDamageTypes.Contains(type)) - { - // not all members are supported - allMembersSupported = false; - break; - } - } - if (allMembersSupported) { - // All members are supported. The silly goose should have just used a damage group. - _fullySupportedDamageGroups.Add(group); - } - } - - // For each supported damage type, check whether it is in any existing group, If it is add it to _applicableDamageGroups - foreach (var type in _supportedDamageTypes) - { - foreach (var group in _prototypeManager.EnumeratePrototypes()) - { - if (group.DamageTypes.Contains(type)) - { - _applicableDamageGroups.Add(group); - } - } - } - } - } -} diff --git a/Content.Shared/Damage/DamageChangeData.cs b/Content.Shared/Damage/DamageChangeData.cs deleted file mode 100644 index fbe7a9fb8d..0000000000 --- a/Content.Shared/Damage/DamageChangeData.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Content.Shared.Damage -{ - /// - /// Data class with information on how the value of a - /// single has changed. - /// - public struct DamageChangeData - { - /// - /// Type of damage that changed. - /// - public DamageTypePrototype Type; - - /// - /// The new current value for that damage. - /// - public int NewValue; - - /// - /// How much the health value changed from its last value (negative is heals, positive is damage). - /// - public int Delta; - - public DamageChangeData(DamageTypePrototype type, int newValue, int delta) - { - Type = type; - NewValue = newValue; - Delta = delta; - } - } -} diff --git a/Content.Shared/Damage/DamageChangedEventArgs.cs b/Content.Shared/Damage/DamageChangedEventArgs.cs deleted file mode 100644 index 0956bcdbf6..0000000000 --- a/Content.Shared/Damage/DamageChangedEventArgs.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; -using Content.Shared.Damage.Components; - -namespace Content.Shared.Damage -{ - public class DamageChangedEventArgs : EventArgs - { - public DamageChangedEventArgs(IDamageableComponent damageable, IReadOnlyList data) - { - Damageable = damageable; - Data = data; - } - - public DamageChangedEventArgs(IDamageableComponent damageable, DamageTypePrototype type, int newValue, int delta) - { - Damageable = damageable; - - var datum = new DamageChangeData(type, newValue, delta); - var data = new List {datum}; - - Data = data; - } - - /// - /// Reference to the that invoked the event. - /// - public IDamageableComponent Damageable { get; } - - /// - /// List containing data on each that was changed. - /// - public IReadOnlyList Data { get; } - } -} diff --git a/Content.Shared/Damage/DamageChangedMessage.cs b/Content.Shared/Damage/DamageChangedMessage.cs deleted file mode 100644 index 01cd546ee4..0000000000 --- a/Content.Shared/Damage/DamageChangedMessage.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Collections.Generic; -using Content.Shared.Damage.Components; -using Robust.Shared.GameObjects; - -namespace Content.Shared.Damage -{ - public class DamageChangedMessage : ComponentMessage - { - public DamageChangedMessage(IDamageableComponent damageable, IReadOnlyList data) - { - Damageable = damageable; - Data = data; - } - - public DamageChangedMessage(IDamageableComponent damageable, DamageTypePrototype type, int newValue, int delta) - { - Damageable = damageable; - - var datum = new DamageChangeData(type, newValue, delta); - var data = new List {datum}; - - Data = data; - } - - /// - /// Reference to the that invoked the event. - /// - public IDamageableComponent Damageable { get; } - - /// - /// List containing data on each that was changed. - /// - public IReadOnlyList Data { get; } - - public bool TookDamage - { - get - { - foreach (var datum in Data) - { - if (datum.Delta > 0) - { - return true; - } - } - - return false; - } - } - } -} diff --git a/Content.Shared/Damage/DamageGroupPrototype.cs b/Content.Shared/Damage/DamageGroupPrototype.cs deleted file mode 100644 index d1463c7b59..0000000000 --- a/Content.Shared/Damage/DamageGroupPrototype.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Generic; -using Robust.Shared.IoC; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.Manager.Attributes; - -namespace Content.Shared.Damage -{ - /// - /// A Group of s. - /// - /// - /// These groups can be used to specify supported damage types of a , or to change/get/set damage in a . - /// - [Prototype("damageGroup")] - [Serializable, NetSerializable] - public class DamageGroupPrototype : IPrototype, ISerializationHooks - { - private IPrototypeManager _prototypeManager = default!; - - [DataField("id", required: true)] public string ID { get; } = default!; - - [DataField("damageTypes", required: true)] - public List TypeIDs { get; } = default!; - - public HashSet DamageTypes { get; } = new(); - - // Create set of damage types - void ISerializationHooks.AfterDeserialization() - { - _prototypeManager = IoCManager.Resolve(); - - foreach (var typeID in TypeIDs) - { - DamageTypes.Add(_prototypeManager.Index(typeID)); - } - } - } -} diff --git a/Content.Shared/Damage/DamageSpecifier.cs b/Content.Shared/Damage/DamageSpecifier.cs new file mode 100644 index 0000000000..e1accf899b --- /dev/null +++ b/Content.Shared/Damage/DamageSpecifier.cs @@ -0,0 +1,375 @@ +using Content.Shared.Damage.Prototypes; +using Robust.Shared.IoC; +using Robust.Shared.Log; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; +using Robust.Shared.Utility; +using Robust.Shared.ViewVariables; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Content.Shared.Damage +{ + // TODO DAMAGE UNITS Move this whole class away from, using integers. Also get rid of a lot of the rounding. Just + // use DamageUnit math operators. + + /// + /// This class represents a collection of damage types and damage values. + /// + /// + /// The actual damage information is stored in . This class provides + /// functions to apply resistance sets and supports basic math operations to modify this dictionary. + /// + [DataDefinition] + public class DamageSpecifier + { + [DataField("types", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + private readonly Dictionary? _damageTypeDictionary; + + [DataField("groups", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + private readonly Dictionary? _damageGroupDictionary; + + /// + /// Main DamageSpecifier dictionary. Most DamageSpecifier functions exist to somehow modifying this. + /// + [ViewVariables(VVAccess.ReadWrite)] + public Dictionary DamageDict + { + get + { + if (_damageDict == null) + DeserializeDamage(); + return _damageDict!; + } + set => _damageDict = value; + } + private Dictionary? _damageDict; + + /// + /// Sum of the damage values. + /// + /// + /// Note that this being zero does not mean this damage has no effect. Healing in one type may cancel damage + /// in another. For this purpose, you should instead use and then check the property. + /// + public int Total => DamageDict.Values.Sum(); + + /// + /// Whether this damage specifier has any entries. + /// + public bool Empty => DamageDict.Count == 0; + + #region constructors + /// + /// Constructor that just results in an empty dictionary. + /// + public DamageSpecifier() { } + + /// + /// Constructor that takes another DamageSpecifier instance and copies it. + /// + public DamageSpecifier(DamageSpecifier damageSpec) + { + DamageDict = new(damageSpec.DamageDict); + } + + /// + /// Constructor that takes a single damage type prototype and a damage value. + /// + public DamageSpecifier(DamageTypePrototype type, int value) + { + DamageDict = new() { { type.ID, value } }; + } + + /// + /// Constructor that takes a single damage group prototype and a damage value. The value is divided between members of the damage group. + /// + public DamageSpecifier(DamageGroupPrototype group, int value) + { + _damageGroupDictionary = new() { { group.ID, value } }; + } + #endregion constructors + + /// + /// Combines the damage group and type datafield dictionaries into a single damage dictionary. + /// + public void DeserializeDamage() + { + // Add all the damage types by just copying the type dictionary (if it is not null). + if (_damageTypeDictionary != null) + { + _damageDict = new(_damageTypeDictionary); + } + else + { + _damageDict = new(); + } + + if (_damageGroupDictionary == null) + return; + + // Then resolve damage groups and add them + var prototypeManager = IoCManager.Resolve(); + foreach (var entry in _damageGroupDictionary) + { + if (!prototypeManager.TryIndex(entry.Key, out var group)) + { + // This can happen if deserialized before prototypes are loaded. + Logger.Error($"Unknown damage group given to DamageSpecifier: {entry.Key}"); + continue; + } + + // Simply distribute evenly (except for rounding). + // We do this by reducing remaining the # of types and damage every loop. + var remainingTypes = group.DamageTypes.Count; + var remainingDamage = entry.Value; + foreach (var damageType in group.DamageTypes) + { + var damage = remainingDamage / remainingTypes; + if (!_damageDict.TryAdd(damageType, damage)) + { + // Key already exists, add values + _damageDict[damageType] += damage; + } + remainingDamage -= damage; + remainingTypes -= 1; + } + } + } + + /// + /// Reduce (or increase) damages by applying a resistance set. + /// + /// + /// Only applies resistance to a damage type if it is dealing damage, not healing. + /// + public static DamageSpecifier ApplyResistanceSet(DamageSpecifier damageSpec, ResistanceSetPrototype resistanceSet) + { + // Make a copy of the given data. Don't modify the one passed to this function. I did this before, and weapons became + // duller as you hit walls. Neat, but not intended. And confusing, when you realize your fists don't work no + // more cause they're just bloody stumps. + DamageSpecifier newDamage = new(damageSpec); + + foreach (var entry in newDamage.DamageDict) + { + if (entry.Value <= 0) continue; + + float newValue = entry.Value; + + if (resistanceSet.FlatReduction.TryGetValue(entry.Key, out var reduction)) + { + newValue -= reduction; + if (newValue <= 0) + { + // flat reductions cannot heal you + newDamage.DamageDict[entry.Key] = 0; + continue; + } + } + + if (resistanceSet.Coefficients.TryGetValue(entry.Key, out var coefficient)) + { + // negative coefficients **can** heal you. + newValue = MathF.Round(newValue*coefficient, MidpointRounding.AwayFromZero); + } + + newDamage.DamageDict[entry.Key] = (int) newValue; + } + + newDamage.TrimZeros(); + return newDamage; + } + + /// + /// Remove any damage entries with zero damage. + /// + public void TrimZeros() + { + foreach (var (key, value) in DamageDict) + { + if (value == 0) + { + DamageDict.Remove(key); + } + } + } + + /// + /// Clamps each damage value to be within the given range. + /// + public void Clamp(int minValue = 0, int maxValue = 0) + { + DebugTools.Assert(minValue < maxValue); + ClampMax(maxValue); + ClampMin(minValue); + } + + /// + /// Sets all damage values to be at least as large as the given number. + /// + /// + /// Note that this only acts on damage types present in the dictionary. It will not add new damage types. + /// + public void ClampMin(int minValue = 0) + { + foreach (var (key, value) in DamageDict) + { + if (value < minValue) + { + DamageDict[key] = minValue; + } + } + } + + /// + /// Sets all damage values to be at most some number. Note that if a damage type is not present in the + /// dictionary, these will not be added. + /// + public void ClampMax(int maxValue = 0) + { + foreach (var (key, value) in DamageDict) + { + if (value > maxValue) + { + DamageDict[key] = maxValue; + } + } + } + + /// + /// This adds the damage values of some other to the current one without + /// adding any new damage types. + /// + /// + /// This is used for s, such that only "supported" damage types are + /// actually added to the component. In most other instances, you can just use the addition operator. + /// + public void ExclusiveAdd(DamageSpecifier other) + { + foreach (var (type, value) in other.DamageDict) + { + if (DamageDict.ContainsKey(type)) + { + DamageDict[type] += value; + } + } + } + + /// + /// Add up all the damage values for damage types that are members of a given group. + /// + /// + /// If no members of the group are included in this specifier, returns false. + /// + public bool TryGetDamageInGroup(DamageGroupPrototype group, out int total) + { + bool containsMemeber = false; + total = 0; + + foreach (var type in group.DamageTypes) + { + if (DamageDict.TryGetValue(type, out var value)) + { + total += value; + containsMemeber = true; + } + } + return containsMemeber; + } + + /// + /// Returns a dictionary using keys, with values calculated by adding + /// up the values for each damage type in that group + /// + /// + /// If a damage type is associated with more than one supported damage group, it will contribute to the + /// total of each group. If no members of a group are present in this , the + /// group is not included in the resulting dictionary. + /// + public Dictionary GetDamagePerGroup() + { + var damageGroupDict = new Dictionary(); + foreach (var group in IoCManager.Resolve().EnumeratePrototypes()) + { + if (TryGetDamageInGroup(group, out var value)) + { + damageGroupDict.Add(group.ID, value); + } + } + return damageGroupDict; + } + + #region Operators + public static DamageSpecifier operator *(DamageSpecifier damageSpec, int factor) + { + DamageSpecifier newDamage = new(); + foreach (var entry in damageSpec.DamageDict) + { + newDamage.DamageDict.Add(entry.Key, entry.Value * factor); + } + return newDamage; + } + + public static DamageSpecifier operator *(DamageSpecifier damageSpec, float factor) + { + DamageSpecifier newDamage = new(); + foreach (var entry in damageSpec.DamageDict) + { + newDamage.DamageDict.Add(entry.Key, (int) MathF.Round(entry.Value * factor, MidpointRounding.AwayFromZero)); + } + return newDamage; + } + + public static DamageSpecifier operator /(DamageSpecifier damageSpec, int factor) + { + DamageSpecifier newDamage = new(); + foreach (var entry in damageSpec.DamageDict) + { + newDamage.DamageDict.Add(entry.Key, (int) MathF.Round(entry.Value / (float) factor, MidpointRounding.AwayFromZero)); + } + return newDamage; + } + + public static DamageSpecifier operator /(DamageSpecifier damageSpec, float factor) + { + DamageSpecifier newDamage = new(); + + foreach (var entry in damageSpec.DamageDict) + { + newDamage.DamageDict.Add(entry.Key, (int) MathF.Round(entry.Value / factor, MidpointRounding.AwayFromZero)); + } + return newDamage; + } + + public static DamageSpecifier operator +(DamageSpecifier damageSpecA, DamageSpecifier damageSpecB) + { + // Copy existing dictionary from dataA + DamageSpecifier newDamage = new(damageSpecA); + + // Then just add types in B + foreach (var entry in damageSpecB.DamageDict) + { + if (!newDamage.DamageDict.TryAdd(entry.Key, entry.Value)) + { + // Key already exists, add values + newDamage.DamageDict[entry.Key] += entry.Value; + } + } + return newDamage; + } + + public static DamageSpecifier operator -(DamageSpecifier damageSpecA, DamageSpecifier damageSpecB) => damageSpecA + -damageSpecB; + + public static DamageSpecifier operator +(DamageSpecifier damageSpec) => damageSpec; + + public static DamageSpecifier operator -(DamageSpecifier damageSpec) => damageSpec * -1; + + public static DamageSpecifier operator *(float factor, DamageSpecifier damageSpec) => damageSpec * factor; + + public static DamageSpecifier operator *(int factor, DamageSpecifier damageSpec) => damageSpec * factor; + } + #endregion +} diff --git a/Content.Shared/Damage/DamageSystem.cs b/Content.Shared/Damage/DamageSystem.cs deleted file mode 100644 index d40e0e8347..0000000000 --- a/Content.Shared/Damage/DamageSystem.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using JetBrains.Annotations; -using Robust.Shared.GameObjects; - -namespace Content.Shared.Damage -{ - [UsedImplicitly] - public class DamageSystem : EntitySystem - { - - } -} diff --git a/Content.Shared/Damage/DamageableComponent.cs b/Content.Shared/Damage/DamageableComponent.cs new file mode 100644 index 0000000000..b4af5437e1 --- /dev/null +++ b/Content.Shared/Damage/DamageableComponent.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using Content.Shared.Acts; +using Content.Shared.Damage.Prototypes; +using Content.Shared.Radiation; +using Robust.Shared.Analyzers; +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; +using Robust.Shared.ViewVariables; + +namespace Content.Shared.Damage +{ + /// + /// Component that allows entities to take damage. + /// + /// + /// The supported damage types are specified using a s. DamageContainers + /// may also have resistances to certain damage types, defined via a . + /// + [RegisterComponent] + [NetworkedComponent()] + [Friend(typeof(DamageableSystem))] + public class DamageableComponent : Component, IRadiationAct, IExAct + { + public override string Name => "Damageable"; + + /// + /// This specifies what damage types are supported by this component. + /// If null, all damage types will be supported. + /// + [DataField("damageContainer", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string? DamageContainerID; + + /// + /// This will be applied to any damage that is dealt to this container, + /// unless the damage explicitly ignores resistances. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("resistanceSet", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string? ResistanceSetID; + + /// + /// All the damage information is stored in this . + /// + /// + /// If this data-field is specified, this allows damageable components to be initialized with non-zero damage. + /// + [DataField("damage")] + [ViewVariables(VVAccess.ReadWrite)] + public DamageSpecifier Damage = new(); + + /// + /// Damage, indexed by ID keys. + /// + /// + /// Groups which have no members that are supported by this component will not be present in this + /// dictionary. + /// + [ViewVariables] public Dictionary DamagePerGroup = new(); + + /// + /// The sum of all damages in the DamageableComponent. + /// + [ViewVariables] public int TotalDamage; + + // Really these shouldn't be here. OnExplosion() and RadiationAct() should be handled elsewhere. + [ViewVariables] + [DataField("radiationDamageTypes", customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List RadiationDamageTypeIDs = new() {"Radiation"}; + [ViewVariables] + [DataField("explosionDamageTypes", customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List ExplosionDamageTypeIDs = new() { "Piercing", "Heat" }; + + // TODO RADIATION Remove this. + void IRadiationAct.RadiationAct(float frameTime, SharedRadiationPulseComponent radiation) + { + var damageValue = Math.Max((int) (frameTime * radiation.RadsPerSecond), 1); + + // Radiation should really just be a damage group instead of a list of types. + DamageSpecifier damage = new(); + foreach (var typeID in ExplosionDamageTypeIDs) + { + damage.DamageDict.Add(typeID, damageValue); + } + + EntitySystem.Get().TryChangeDamage(Owner.Uid, damage); + } + + // TODO EXPLOSION Remove this. + void IExAct.OnExplosion(ExplosionEventArgs eventArgs) + { + var damageValue = eventArgs.Severity switch + { + ExplosionSeverity.Light => 20, + ExplosionSeverity.Heavy => 60, + ExplosionSeverity.Destruction => 250, + _ => throw new ArgumentOutOfRangeException() + }; + + // Explosion should really just be a damage group instead of a list of types. + DamageSpecifier damage = new(); + foreach (var typeID in ExplosionDamageTypeIDs) + { + damage.DamageDict.Add(typeID, damageValue); + } + + EntitySystem.Get().TryChangeDamage(Owner.Uid, damage); + } + } + + [Serializable, NetSerializable] + public class DamageableComponentState : ComponentState + { + public readonly Dictionary DamageDict; + public readonly string? ResistanceSetID; + + public DamageableComponentState( + Dictionary damageDict, + string? resistanceSetID) + { + DamageDict = damageDict; + ResistanceSetID = resistanceSetID; + } + } +} diff --git a/Content.Shared/Damage/DamageableSystem.cs b/Content.Shared/Damage/DamageableSystem.cs new file mode 100644 index 0000000000..6e51ef25c1 --- /dev/null +++ b/Content.Shared/Damage/DamageableSystem.cs @@ -0,0 +1,243 @@ +using System.Collections.Generic; +using System.Linq; +using Content.Shared.Damage.Prototypes; +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.IoC; +using Robust.Shared.Log; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Damage +{ + public class DamageableSystem : EntitySystem + { + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + public override void Initialize() + { + SubscribeLocalEvent(DamageableInit); + SubscribeLocalEvent(DamageableHandleState); + SubscribeLocalEvent(DamageableGetState); + } + + /// + /// Initialize a damageable component + /// + private void DamageableInit(EntityUid uid, DamageableComponent component, ComponentInit _) + { + if (component.DamageContainerID != null && + _prototypeManager.TryIndex(component.DamageContainerID, + out var damageContainerPrototype)) + { + // Initialize damage dictionary, using the types and groups from the damage + // container prototype + foreach (var type in damageContainerPrototype.SupportedTypes) + { + component.Damage.DamageDict.TryAdd(type, 0); + } + + foreach (var groupID in damageContainerPrototype.SupportedGroups) + { + var group = _prototypeManager.Index(groupID); + foreach (var type in group.DamageTypes) + { + component.Damage.DamageDict.TryAdd(type, 0); + } + } + } + else + { + // No DamageContainerPrototype was given. So we will allow the container to support all damage types + foreach (var type in _prototypeManager.EnumeratePrototypes()) + { + component.Damage.DamageDict.TryAdd(type.ID, 0); + } + } + + component.DamagePerGroup = component.Damage.GetDamagePerGroup(); + component.TotalDamage = component.Damage.Total; + } + + /// + /// Directly sets the damage specifier of a damageable component. + /// + /// + /// Useful for some unfriendly folk. Also ensures that cached values are updated and that a damage changed + /// event is raised. + /// + public void SetDamage(DamageableComponent damageable, DamageSpecifier damage) + { + damageable.Damage = damage; + DamageChanged(damageable); + } + + /// + /// If the damage in a DamageableComponent was changed, this function should be called. + /// + /// + /// This updates cached damage information, flags the component as dirty, and raises a damage changed event. + /// The damage changed event is used by other systems, such as damage thresholds. + /// + public void DamageChanged(DamageableComponent component, DamageSpecifier? damageDelta = null) + { + component.DamagePerGroup = component.Damage.GetDamagePerGroup(); + component.TotalDamage = component.Damage.Total; + component.Dirty(); + RaiseLocalEvent(component.Owner.Uid, new DamageChangedEvent(component, damageDelta), false); + } + + /// + /// Applies damage specified via a . + /// + /// + /// is effectively just a dictionary of damage types and damage values. This + /// function just applies the container's resistances (unless otherwise specified) and then changes the + /// stored damage data. Division of group damage into types is managed by . + /// + /// + /// Returns a with information about the actual damage changes. This will be + /// null if the user had no applicable components that can take damage. + /// + public DamageSpecifier? TryChangeDamage(EntityUid uid, DamageSpecifier damage, bool ignoreResistances = false) + { + if (!ComponentManager.TryGetComponent(uid, out var damageable)) + { + // TODO BODY SYSTEM pass damage onto body system + return null; + } + + if (damage == null) + { + Logger.Error("Null DamageSpecifier. Probably because a required yaml field was not given."); + return null; + } + + if (damage.Empty) + { + return damage; + } + + // Apply resistances + if (!ignoreResistances && damageable.ResistanceSetID != null) + { + if (_prototypeManager.TryIndex(damageable.ResistanceSetID, out var resistanceSet)) + { + damage = DamageSpecifier.ApplyResistanceSet(damage, resistanceSet); + } + + if (damage.Empty) + { + return damage; + } + } + + // Copy the current damage, for calculating the difference + DamageSpecifier oldDamage = new(damageable.Damage); + + damageable.Damage.ExclusiveAdd(damage); + damageable.Damage.ClampMin(0); + + var delta = damageable.Damage - oldDamage; + delta.TrimZeros(); + + if (!delta.Empty) + { + DamageChanged(damageable, delta); + } + + return delta; + } + + /// + /// Sets all damage types supported by a to the specified value. + /// + /// + /// Does nothing If the given damage value is negative. + /// + public void SetAllDamage(DamageableComponent component, int newValue) + { + if (newValue < 0) + { + // invalid value + return; + } + + foreach (var type in component.Damage.DamageDict.Keys) + { + component.Damage.DamageDict[type] = newValue; + } + + // Setting damage does not count as 'dealing' damage, even if it is set to a larger value, so we pass an + // empty damage delta. + DamageChanged(component, new DamageSpecifier()); + } + + private void DamageableGetState(EntityUid uid, DamageableComponent component, ref ComponentGetState args) + { + args.State = new DamageableComponentState(component.Damage.DamageDict, component.ResistanceSetID); + } + + private void DamageableHandleState(EntityUid uid, DamageableComponent component, ref ComponentHandleState args) + { + if (args.Current is not DamageableComponentState state) + { + return; + } + + component.ResistanceSetID = state.ResistanceSetID; + + // Has the damage actually changed? + DamageSpecifier newDamage = new() { DamageDict = state.DamageDict }; + var delta = component.Damage - newDamage; + delta.TrimZeros(); + + if (!delta.Empty) + { + component.Damage = newDamage; + DamageChanged(component, delta); + } + } + } + + public class DamageChangedEvent : EntityEventArgs + { + /// + /// This is the component whose damage was changed. + /// + /// + /// Given that nearly every component that cares about a change in the damage, needs to know the + /// current damage values, directly passing this information prevents a lot of duplicate + /// Owner.TryGetComponent() calls. + /// + public readonly DamageableComponent Damageable; + + /// + /// The amount by which the damage has changed. If the damage was set directly to some number, this will be + /// null. + /// + public readonly DamageSpecifier? DamageDelta; + + /// + /// Was any of the damage change dealing damage, or was it all healing? + /// + public readonly bool DamageIncreased = false; + + public DamageChangedEvent(DamageableComponent damageable, DamageSpecifier? damageDelta) + { + Damageable = damageable; + DamageDelta = damageDelta; + + if (DamageDelta == null) + return; + + foreach (var damageChange in DamageDelta.DamageDict.Values) + { + if (damageChange > 0) + { + DamageIncreased = true; + break; + } + } + } + } +} diff --git a/Content.Shared/Damage/Prototypes/DamageContainerPrototype.cs b/Content.Shared/Damage/Prototypes/DamageContainerPrototype.cs new file mode 100644 index 0000000000..53d67a59ff --- /dev/null +++ b/Content.Shared/Damage/Prototypes/DamageContainerPrototype.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; +using Robust.Shared.ViewVariables; + +namespace Content.Shared.Damage.Prototypes +{ + /// + /// A damage container which can be used to specify support for various damage types. + /// + /// + /// This is effectively just a list of damage types that can be specified in YAML files using both damage types + /// and damage groups. Currently this is only used to specify what damage types a should support. + /// + [Prototype("damageContainer")] + [Serializable, NetSerializable] + public class DamageContainerPrototype : IPrototype + { + [ViewVariables] + [DataField("id", required: true)] + public string ID { get; } = default!; + + /// + /// List of damage groups that are supported by this container. + /// + [DataField("supportedGroups", customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List SupportedGroups = new(); + + /// + /// Partial List of damage types supported by this container. Note that members of the damage groups listed + /// in are also supported, but they are not included in this list. + /// + [DataField("supportedTypes", customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List SupportedTypes = new(); + } +} diff --git a/Content.Shared/Damage/Prototypes/DamageGroupPrototype.cs b/Content.Shared/Damage/Prototypes/DamageGroupPrototype.cs new file mode 100644 index 0000000000..14b89c47a0 --- /dev/null +++ b/Content.Shared/Damage/Prototypes/DamageGroupPrototype.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; + +namespace Content.Shared.Damage.Prototypes +{ + /// + /// A Group of s. + /// + /// + /// These groups can be used to specify supported damage types of a , or + /// to change/get/set damage in a . + /// + [Prototype("damageGroup")] + [Serializable, NetSerializable] + public class DamageGroupPrototype : IPrototype + { + [DataField("id", required: true)] public string ID { get; } = default!; + + [DataField("damageTypes", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer))] + public List DamageTypes { get; } = default!; + } +} diff --git a/Content.Shared/Damage/DamageTypePrototype.cs b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs similarity index 92% rename from Content.Shared/Damage/DamageTypePrototype.cs rename to Content.Shared/Damage/Prototypes/DamageTypePrototype.cs index e39e44f059..37d8365c1c 100644 --- a/Content.Shared/Damage/DamageTypePrototype.cs +++ b/Content.Shared/Damage/Prototypes/DamageTypePrototype.cs @@ -3,7 +3,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Serialization.Manager.Attributes; -namespace Content.Shared.Damage +namespace Content.Shared.Damage.Prototypes { /// /// A single damage type. These types are grouped together in s. diff --git a/Content.Shared/Damage/Prototypes/ResistanceSetPrototype.cs b/Content.Shared/Damage/Prototypes/ResistanceSetPrototype.cs new file mode 100644 index 0000000000..e09d9aa181 --- /dev/null +++ b/Content.Shared/Damage/Prototypes/ResistanceSetPrototype.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; +using Robust.Shared.ViewVariables; + +namespace Content.Shared.Damage.Prototypes +{ + /// + /// Prototype of damage resistance sets. Can be applied to using . This can be done several times as the + /// is passed to it's final target. By default the receiving , will + /// also apply it's own . + /// + [Prototype("resistanceSet")] + [Serializable, NetSerializable] + public class ResistanceSetPrototype : IPrototype + { + [ViewVariables] + [DataField("id", required: true)] + public string ID { get; } = default!; + + [DataField("coefficients", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + public Dictionary Coefficients = new(); + + [DataField("flatReductions", customTypeSerializer: typeof(PrototypeIdDictionarySerializer))] + public Dictionary FlatReduction = new(); + } +} diff --git a/Content.Shared/Damage/Resistances/ResistanceSet.cs b/Content.Shared/Damage/Resistances/ResistanceSet.cs deleted file mode 100644 index ba9c218494..0000000000 --- a/Content.Shared/Damage/Resistances/ResistanceSet.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using Robust.Shared.IoC; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; -using Robust.Shared.ViewVariables; - -namespace Content.Shared.Damage.Resistances -{ - /// - /// Set of resistances used by damageable objects. - /// Each has a multiplier and flat damage - /// reduction value. - /// - [Serializable, NetSerializable] - public class ResistanceSet - { - - [ViewVariables] - public string? ID { get; } = string.Empty; - - [ViewVariables] - public Dictionary Resistances { get; } = new(); - - public ResistanceSet() - { - } - - public ResistanceSet(ResistanceSetPrototype data) - { - ID = data.ID; - Resistances = data.Resistances; - } - - /// - /// Adjusts input damage with the resistance set values. - /// Only applies reduction if the amount is damage (positive), not - /// healing (negative). - /// - /// Type of damage. - /// Incoming amount of damage. - public int CalculateDamage(DamageTypePrototype damageType, int amount) - { - - // Do nothing if the damage type is not specified in resistance set. - if (!Resistances.TryGetValue(damageType, out var resistance)) - { - return amount; - } - - if (amount > 0) // Only apply reduction if it's healing, not damage. - { - amount -= resistance.FlatReduction; - - if (amount <= 0) - { - return 0; - } - } - - amount = (int) Math.Ceiling(amount * resistance.Coefficient); - - return amount; - } - } -} diff --git a/Content.Shared/Damage/Resistances/ResistanceSetPrototype.cs b/Content.Shared/Damage/Resistances/ResistanceSetPrototype.cs deleted file mode 100644 index 2f4b64a696..0000000000 --- a/Content.Shared/Damage/Resistances/ResistanceSetPrototype.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.CodeDom; -using System.Collections.Generic; -using Robust.Shared.IoC; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; - -namespace Content.Shared.Damage.Resistances -{ - /// - /// Prototype for the BodyPart class. - /// - [Prototype("resistanceSet")] - [Serializable, NetSerializable] - public class ResistanceSetPrototype : IPrototype, ISerializationHooks - { - [ViewVariables] - [DataField("id", required: true)] - public string ID { get; } = default!; - - [ViewVariables] - [DataField("coefficients", required: true)] - private Dictionary coefficients { get; } = new(); - - [ViewVariables] - [DataField("flatReductions", required: true)] - private Dictionary flatReductions { get; } = new(); - - [ViewVariables] - public Dictionary Resistances { get; private set; } = new(); - - void ISerializationHooks.AfterDeserialization() - { - var prototypeManager = IoCManager.Resolve(); - foreach (var damageTypeID in coefficients.Keys) - { - var resolvedDamageType = prototypeManager.Index(damageTypeID); - Resistances.Add(resolvedDamageType, new ResistanceSetSettings(coefficients[damageTypeID], flatReductions[damageTypeID])); - } - } - } - - /// - /// Resistance Settings for a specific DamageType. Flat reduction should always be applied before the coefficient. - /// - [Serializable, NetSerializable] - public readonly struct ResistanceSetSettings - { - [ViewVariables] public readonly float Coefficient; - [ViewVariables] public readonly int FlatReduction; - - public ResistanceSetSettings(float coefficient, int flatReduction) - { - Coefficient = coefficient; - FlatReduction = flatReduction; - } - } - -} diff --git a/Content.Shared/MedicalScanner/SharedMedicalScannerComponent.cs b/Content.Shared/MedicalScanner/SharedMedicalScannerComponent.cs index 00b78ff6a1..aa9aa2d1a3 100644 --- a/Content.Shared/MedicalScanner/SharedMedicalScannerComponent.cs +++ b/Content.Shared/MedicalScanner/SharedMedicalScannerComponent.cs @@ -16,25 +16,24 @@ namespace Content.Shared.MedicalScanner public class MedicalScannerBoundUserInterfaceState : BoundUserInterfaceState { public readonly EntityUid? Entity; - public readonly Dictionary DamagePerSupportedGroupID; - public readonly Dictionary DamagePerTypeID; + public readonly IReadOnlyDictionary DamagePerGroup; + public readonly IReadOnlyDictionary DamagePerType; public readonly bool IsScanned; public MedicalScannerBoundUserInterfaceState( EntityUid? entity, - Dictionary damagePerSupportedGroupID, - Dictionary damagePerTypeID, + DamageableComponent? damageable, bool isScanned) { Entity = entity; - DamagePerSupportedGroupID = damagePerSupportedGroupID; - DamagePerTypeID = damagePerTypeID; + DamagePerGroup = damageable?.DamagePerGroup ?? new(); + DamagePerType = damageable?.Damage?.DamageDict ?? new(); IsScanned = isScanned; } public bool HasDamage() { - return DamagePerSupportedGroupID.Count > 0 || DamagePerTypeID.Count > 0; + return DamagePerType.Count > 0; } } diff --git a/Content.Shared/MobState/Components/SharedMobStateComponent.cs b/Content.Shared/MobState/Components/MobStateComponent.cs similarity index 87% rename from Content.Shared/MobState/Components/SharedMobStateComponent.cs rename to Content.Shared/MobState/Components/MobStateComponent.cs index 5ded3365f1..d0138e0838 100644 --- a/Content.Shared/MobState/Components/SharedMobStateComponent.cs +++ b/Content.Shared/MobState/Components/MobStateComponent.cs @@ -5,7 +5,6 @@ using System.Linq; using Content.Shared.ActionBlocker; using Content.Shared.Alert; using Content.Shared.Damage; -using Content.Shared.Damage.Components; using Content.Shared.MobState.State; using Robust.Shared.GameObjects; using Robust.Shared.GameStates; @@ -17,18 +16,20 @@ using Robust.Shared.ViewVariables; namespace Content.Shared.MobState.Components { /// - /// When attached to an , + /// When attached to an , /// this component will handle critical and death behaviors for mobs. /// Additionally, it handles sending effects to clients /// (such as blur effect for unconsciousness) and managing the health HUD. /// + [RegisterComponent] + [ComponentReference(typeof(IMobStateComponent))] [NetworkedComponent()] - public abstract class SharedMobStateComponent : Component, IMobStateComponent, IActionBlocker + public class MobStateComponent : Component, IMobStateComponent, IActionBlocker { public override string Name => "MobState"; /// - /// States that this mapped to + /// States that this mapped to /// the amount of damage at which they are triggered. /// A threshold is reached when the total damage of an entity is equal /// to or higher than the int key, but lower than the next threshold. @@ -36,7 +37,7 @@ namespace Content.Shared.MobState.Components /// [ViewVariables] [DataField("thresholds")] - private readonly SortedDictionary _lowestToHighestStates = default!; + private readonly SortedDictionary _lowestToHighestStates = new(); // TODO Remove Nullability? [ViewVariables] @@ -53,7 +54,13 @@ namespace Content.Shared.MobState.Components if (CurrentState != null && CurrentThreshold != null) { - UpdateState(null, (CurrentState, CurrentThreshold.Value)); + // Initialize with given states + SetMobState(null, (CurrentState, CurrentThreshold.Value)); + } + else + { + // Initialize with some amount of damage, defaulting to 0. + UpdateState(Owner.GetComponentOrNull()?.TotalDamage ?? 0); } } @@ -88,29 +95,11 @@ namespace Content.Shared.MobState.Components if (state.CurrentThreshold == null) { - RemoveState(true); + RemoveState(); } else { - UpdateState(state.CurrentThreshold.Value, true); - } - } - - public override void HandleMessage(ComponentMessage message, IComponent? component) - { - base.HandleMessage(message, component); - - switch (message) - { - case DamageChangedMessage msg: - if (msg.Damageable.Owner != Owner) - { - break; - } - - UpdateState(msg.Damageable.TotalDamage); - - break; + UpdateState(state.CurrentThreshold.Value); } } @@ -136,7 +125,7 @@ namespace Content.Shared.MobState.Components public (IMobState state, int threshold)? GetState(int damage) { - foreach (var (threshold, state) in _lowestToHighestStates.Reverse()) + foreach (var (threshold, state) in _highestToLowestStates) { if (damage >= threshold) { @@ -273,36 +262,32 @@ namespace Content.Shared.MobState.Components return TryGetState(earliestState, out state, out threshold); } - private void RemoveState(bool syncing = false) + private void RemoveState() { var old = CurrentState; CurrentState = null; CurrentThreshold = null; - UpdateState(old, null); - - if (!syncing) - { - Dirty(); - } + SetMobState(old, null); } - public void UpdateState(int damage, bool syncing = false) + /// + /// Updates the mob state.. + /// + public void UpdateState(int damage) { if (!TryGetState(damage, out var newState, out var threshold)) { return; } - UpdateState(CurrentState, (newState, threshold)); - - if (!syncing) - { - Dirty(); - } + SetMobState(CurrentState, (newState, threshold)); } - private void UpdateState(IMobState? old, (IMobState state, int threshold)? current) + /// + /// Sets the mob state and marks the component as dirty. + /// + private void SetMobState(IMobState? old, (IMobState state, int threshold)? current) { if (!current.HasValue) { @@ -328,9 +313,10 @@ namespace Content.Shared.MobState.Components state.UpdateState(Owner, threshold); var message = new MobStateChangedMessage(this, old, state); - SendMessage(message); Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, message); + + Dirty(); } bool IActionBlocker.CanInteract() diff --git a/Content.Shared/MobState/EntitySystems/MobStateSystem.cs b/Content.Shared/MobState/EntitySystems/MobStateSystem.cs new file mode 100644 index 0000000000..045b3634f4 --- /dev/null +++ b/Content.Shared/MobState/EntitySystems/MobStateSystem.cs @@ -0,0 +1,45 @@ +using Content.Shared.Damage; +using Content.Shared.MobState.Components; +using Content.Shared.MobState.State; +using Content.Shared.Movement; +using Content.Shared.Pulling.Events; +using Robust.Shared.GameObjects; + +namespace Content.Shared.MobState.EntitySystems +{ + public class MobStateSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStartPullAttempt); + SubscribeLocalEvent(UpdateState); + SubscribeLocalEvent(OnMoveAttempt); + } + + private void OnStartPullAttempt(EntityUid uid, MobStateComponent component, StartPullAttemptEvent args) + { + if(component.IsIncapacitated()) + args.Cancel(); + } + + public void UpdateState(EntityUid _, MobStateComponent component, DamageChangedEvent args) + { + component.UpdateState(args.Damageable.TotalDamage); + } + + private void OnMoveAttempt(EntityUid uid, MobStateComponent component, MovementAttemptEvent args) + { + switch (component.CurrentState) + { + case SharedCriticalMobState: + case SharedDeadMobState: + args.Cancel(); + return; + default: + return; + } + } + } +} diff --git a/Content.Shared/MobState/EntitySystems/SharedMobStateSystem.cs b/Content.Shared/MobState/EntitySystems/SharedMobStateSystem.cs deleted file mode 100644 index bc7a0e9f6f..0000000000 --- a/Content.Shared/MobState/EntitySystems/SharedMobStateSystem.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Content.Shared.MobState.Components; -using Content.Shared.MobState.State; -using Content.Shared.Movement; -using Content.Shared.Pulling.Events; -using Robust.Shared.GameObjects; - -namespace Content.Shared.MobState.EntitySystems -{ - public class SharedMobStateSystem : EntitySystem - { - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnStartPullAttempt); - SubscribeLocalEvent(OnMoveAttempt); - } - - private void OnStartPullAttempt(EntityUid uid, SharedMobStateComponent component, StartPullAttemptEvent args) - { - if(component.IsIncapacitated()) - args.Cancel(); - } - - private void OnMoveAttempt(EntityUid uid, SharedMobStateComponent component, MovementAttemptEvent args) - { - switch (component.CurrentState) - { - case SharedCriticalMobState: - case SharedDeadMobState: - args.Cancel(); - return; - default: - return; - } - } - } -} diff --git a/Content.Shared/MobState/IMobStateComponent.cs b/Content.Shared/MobState/IMobStateComponent.cs index 5f709b763b..17d738205e 100644 --- a/Content.Shared/MobState/IMobStateComponent.cs +++ b/Content.Shared/MobState/IMobStateComponent.cs @@ -44,6 +44,6 @@ namespace Content.Shared.MobState [NotNullWhen(true)] out IMobState? state, out int threshold); - void UpdateState(int damage, bool syncing = false); + void UpdateState(int damage); } } diff --git a/Content.Tests/Shared/DamageTest.cs b/Content.Tests/Shared/DamageTest.cs new file mode 100644 index 0000000000..a1f0e89171 --- /dev/null +++ b/Content.Tests/Shared/DamageTest.cs @@ -0,0 +1,293 @@ +using Content.Shared.Damage; +using Content.Shared.Damage.Prototypes; +using NUnit.Framework; +using Robust.Shared.IoC; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager; +using System.Collections.Generic; + +namespace Content.Tests.Shared +{ + // Basic tests of various damage prototypes and classes. + [TestFixture] + [TestOf(typeof(DamageSpecifier))] + [TestOf(typeof(ResistanceSetPrototype))] + [TestOf(typeof(DamageGroupPrototype))] + public class DamageTest : ContentUnitTest + { + + static private Dictionary _resistanceCoefficientDict = new() + { + // "missing" blunt entry + { "Piercing", -2 },// Turn Piercing into Healing + { "Slash", 3 }, + { "Radiation", 1.06f }, // Small change, paired with fractional reduction + }; + + static private Dictionary _resistanceReductionDict = new() + { + { "Blunt", - 5 }, + // "missing" piercing entry + { "Slash", 8 }, + { "Radiation", 0.5f }, // Fractional adjustment + }; + + private IPrototypeManager _prototypeManager; + + private DamageSpecifier _damageSpec; + + [OneTimeSetUp] + public void OneTimeSetup() + { + IoCManager.Resolve().Initialize(); + _prototypeManager = IoCManager.Resolve(); + _prototypeManager.Initialize(); + _prototypeManager.LoadString(_damagePrototypes); + _prototypeManager.Resync(); + + // Create a damage data set + _damageSpec = new(_prototypeManager.Index("Brute"), 6); + _damageSpec += new DamageSpecifier(_prototypeManager.Index("Radiation"), 3); + _damageSpec += new DamageSpecifier(_prototypeManager.Index("Slash"), -1); // already exists in brute + } + + //Check that DamageSpecifier will split groups and can do arithmetic operations + [Test] + public void DamageSpecifierTest() + { + // Create a copy of the damage data + DamageSpecifier damageSpec = new(_damageSpec); + + // Check that it properly split up the groups into types + int damage; + Assert.That(damageSpec.Total, Is.EqualTo(8)); + Assert.That(damageSpec.DamageDict.TryGetValue("Blunt", out damage)); + Assert.That(damage, Is.EqualTo(2)); + Assert.That(damageSpec.DamageDict.TryGetValue("Piercing", out damage)); + Assert.That(damage, Is.EqualTo(2)); + Assert.That(damageSpec.DamageDict.TryGetValue("Slash", out damage)); + Assert.That(damage, Is.EqualTo(1)); + Assert.That(damageSpec.DamageDict.TryGetValue("Radiation", out damage)); + Assert.That(damage, Is.EqualTo(3)); + + // check that integer multiplication works + damageSpec = damageSpec * 2; + Assert.That(damageSpec.Total, Is.EqualTo(16)); + Assert.That(damageSpec.DamageDict.TryGetValue("Blunt", out damage)); + Assert.That(damage, Is.EqualTo(4)); + Assert.That(damageSpec.DamageDict.TryGetValue("Piercing", out damage)); + Assert.That(damage, Is.EqualTo(4)); + Assert.That(damageSpec.DamageDict.TryGetValue("Slash", out damage)); + Assert.That(damage, Is.EqualTo(2)); + Assert.That(damageSpec.DamageDict.TryGetValue("Radiation", out damage)); + Assert.That(damage, Is.EqualTo(6)); + + // check that float multiplication works + damageSpec = damageSpec * 2.2f; + Assert.That(damageSpec.DamageDict.TryGetValue("Blunt", out damage)); + Assert.That(damage, Is.EqualTo(9)); + Assert.That(damageSpec.DamageDict.TryGetValue("Piercing", out damage)); + Assert.That(damage, Is.EqualTo(9)); + Assert.That(damageSpec.DamageDict.TryGetValue("Slash", out damage)); + Assert.That(damage, Is.EqualTo(4)); + Assert.That(damageSpec.DamageDict.TryGetValue("Radiation", out damage)); + Assert.That(damage, Is.EqualTo(13)); + Assert.That(damageSpec.Total, Is.EqualTo(9 + 9 + 4 + 13)); + + // check that integer division works + damageSpec = damageSpec / 2; + Assert.That(damageSpec.DamageDict.TryGetValue("Blunt", out damage)); + Assert.That(damage, Is.EqualTo(5)); + Assert.That(damageSpec.DamageDict.TryGetValue("Piercing", out damage)); + Assert.That(damage, Is.EqualTo(5)); + Assert.That(damageSpec.DamageDict.TryGetValue("Slash", out damage)); + Assert.That(damage, Is.EqualTo(2)); + Assert.That(damageSpec.DamageDict.TryGetValue("Radiation", out damage)); + Assert.That(damage, Is.EqualTo(7)); + + // check that float division works + damageSpec = damageSpec / 2.4f; + Assert.That(damageSpec.DamageDict.TryGetValue("Blunt", out damage)); + Assert.That(damage, Is.EqualTo(2)); + Assert.That(damageSpec.DamageDict.TryGetValue("Piercing", out damage)); + Assert.That(damage, Is.EqualTo(2)); + Assert.That(damageSpec.DamageDict.TryGetValue("Slash", out damage)); + Assert.That(damage, Is.EqualTo(1)); + Assert.That(damageSpec.DamageDict.TryGetValue("Radiation", out damage)); + Assert.That(damage, Is.EqualTo(3)); + + // Lets also test the constructor with damage types and damage groups works properly. + damageSpec = new(_prototypeManager.Index("Brute"), 4); + Assert.That(damageSpec.DamageDict.TryGetValue("Blunt", out damage)); + Assert.That(damage, Is.EqualTo(1)); + Assert.That(damageSpec.DamageDict.TryGetValue("Piercing", out damage)); + Assert.That(damage, Is.EqualTo(2)); // integer rounding. Piercing is defined as last group member in yaml. + Assert.That(damageSpec.DamageDict.TryGetValue("Slash", out damage)); + Assert.That(damage, Is.EqualTo(1)); + + damageSpec = new(_prototypeManager.Index("Piercing"), 4); + Assert.That(damageSpec.DamageDict.TryGetValue("Piercing", out damage)); + Assert.That(damage, Is.EqualTo(4)); + } + + //Check that DamageSpecifier will be properly adjusted by a resistance set + [Test] + public void ResistanceSetTest() + { + // Create a copy of the damage data + DamageSpecifier damageSpec = 10 * new DamageSpecifier(_damageSpec); + + // Create a resistance set + ResistanceSetPrototype resistanceSet = new() + { + Coefficients = _resistanceCoefficientDict, + FlatReduction = _resistanceReductionDict + }; + + //damage is initially 20 / 20 / 10 / 30 + //Each time we subtract -5 / 0 / 8 / 0.5 + //then multiply by 1 / -2 / 3 / 1.06 + + // Apply once + damageSpec = DamageSpecifier.ApplyResistanceSet(damageSpec, resistanceSet); + Assert.That(damageSpec.DamageDict["Blunt"], Is.EqualTo(25)); + Assert.That(damageSpec.DamageDict["Piercing"], Is.EqualTo(-40)); // became healing + Assert.That(damageSpec.DamageDict["Slash"], Is.EqualTo(6)); + Assert.That(damageSpec.DamageDict["Radiation"], Is.EqualTo(31)); // would be 32 w/o fraction adjustment + + // And again, checking for some other behavior + damageSpec = DamageSpecifier.ApplyResistanceSet(damageSpec, resistanceSet); + Assert.That(damageSpec.DamageDict["Blunt"], Is.EqualTo(30)); + Assert.That(damageSpec.DamageDict["Piercing"], Is.EqualTo(-40)); // resistances don't apply to healing + Assert.That(!damageSpec.DamageDict.ContainsKey("Slash")); // Reduction reduced to 0, and removed from specifier + Assert.That(damageSpec.DamageDict["Radiation"], Is.EqualTo(32)); + } + + // Default damage Yaml + private string _damagePrototypes = @" +- type: damageType + id: Blunt + +- type: damageType + id: Slash + +- type: damageType + id: Piercing + +- type: damageType + id: Heat + +- type: damageType + id: Shock + +- type: damageType + id: Cold + +# Poison damage. Generally caused by various reagents being metabolised. +- type: damageType + id: Poison + +- type: damageType + id: Radiation + +# Damage due to being unable to breathe. +# Represents not enough oxygen (or equivalent) getting to the blood. +# Usually healed automatically if entity can breathe +- type: damageType + id: Asphyxiation + +# Damage representing not having enough blood. +# Represents there not enough blood to supply oxygen (or equivalent). +- type: damageType + id: Bloodloss + +- type: damageType + id: Cellular + +- type: damageGroup + id: Brute + damageTypes: + - Blunt + - Slash + - Piercing + +- type: damageGroup + id: Burn + damageTypes: + - Heat + - Shock + - Cold + +# Airloss (sometimes called oxyloss) +# Caused by asphyxiation or bloodloss. +# Note that most medicine and damaging effects should probably modify either asphyxiation or +# bloodloss, not this whole group, unless you have a wonder drug that affects both. +- type: damageGroup + id: Airloss + damageTypes: + - Asphyxiation + - Bloodloss + +# As with airloss, most medicine and damage effects should probably modify either poison or radiation. +# Though there are probably some radioactive poisons. +- type: damageGroup + id: Toxin + damageTypes: + - Poison + - Radiation + +- type: damageGroup + id: Genetic + damageTypes: + - Cellular + +- type: resistanceSet + id: Metallic + coefficients: + Blunt: 0.7 + Slash: 0.5 + Piercing: 0.7 + Shock: 1.2 + flatReductions: + Blunt: 5 + +- type: resistanceSet + id: Inflatable + coefficients: + Blunt: 0.5 + Piercing: 2.0 + Heat: 0.5 + Shock: 0 + flatReductions: + Blunt: 5 + +- type: resistanceSet + id: Glass + coefficients: + Blunt: 0.5 + Slash: 0.5 + Piercing: 0.5 + Heat: 0 + Shock: 0 + flatReductions: + Blunt: 5 + +- type: damageContainer + id: Biological + supportedGroups: + - Brute + - Burn + - Toxin + - Airloss + - Genetic + +- type: damageContainer + id: Inorganic + supportedGroups: + - Brute + supportedTypes: + - Heat + - Shock +"; + } +} diff --git a/Resources/Prototypes/Body/Mechanisms/human.yml b/Resources/Prototypes/Body/Mechanisms/human.yml index ce174ccc15..7a66c84733 100644 --- a/Resources/Prototypes/Body/Mechanisms/human.yml +++ b/Resources/Prototypes/Body/Mechanisms/human.yml @@ -103,6 +103,8 @@ behaviors: - !type:LungBehavior {} +# TODO DAMAGE UNITS. Some of these damage effects were scaled up to integers. +# Scale back down when damage units are in. - type: entity id: OrganHumanHeart parent: BaseHumanOrgan @@ -124,36 +126,40 @@ Arithrazine: effects: - !type:HealthChange - damageGroup: Toxin - healthChange: -1 - - !type:HealthChange - damageGroup: Brute - healthChange: 0.5 + damage: + groups: + Toxin: -2 # -1 Multiplying by 2. pls give damage units + Brute: 1 # 0.5 Bicaridine: effects: - !type:HealthChange - damageGroup: Brute - healthChange: -2 + damage: + groups: + Brute: -2 Dermaline: effects: - !type:HealthChange - damageGroup: Burn - healthChange: -3 + damage: + groups: + Burn: -3 Dexalin: effects: - !type:HealthChange - damageGroup: Airloss - healthChange: -1 + damage: + types: + Asphyxiation: -1 DexalinPlus: effects: - !type:HealthChange - damageGroup: Airloss - healthChange: -3 + damage: + types: + Asphyxiation: -3 Dylovene: effects: - !type:HealthChange - damageGroup: Toxin - healthChange: -1 + damage: + types: + Poison: -1 Ephedrine: effects: - !type:MovespeedModifier @@ -162,45 +168,45 @@ HeartbreakerToxin: effects: - !type:HealthChange - damageGroup: Airloss - healthChange: 4 + damage: + types: + Asphyxiation: 4 Kelotane: effects: - - !type:HealthChange - damageGroup: Burn - healthChange: -1 + - !type:HealthChange + damage: + groups: + Burn: -1 Lexorin: effects: - !type:HealthChange - damageGroup: Airloss - healthChange: 7 + damage: + groups: + Airloss: 7 Meth: effects: - - !type:HealthChange - healthChange: 2.5 - damageGroup: Toxin - - !type:MovespeedModifier - walkSpeedModifier: 1.3 - sprintSpeedModifier: 1.3 + - !type:HealthChange + damage: + types: + Poison: 3 # 2.5 + - !type:MovespeedModifier + walkSpeedModifier: 1.3 + sprintSpeedModifier: 1.3 Omnizine: effects: - !type:HealthChange - healthChange: -2 - damageGroup: Burn - - !type:HealthChange - healthChange: -2 - damageGroup: Toxin - - !type:HealthChange - healthChange: -2 - damageGroup: Airloss - - !type:HealthChange - healthChange: -2 - damageGroup: Brute + damage: + groups: + Burn: -3 # -2. w/o damage units did not divide into 3 types + Toxin: -2 + Airloss: -2 + Brute: -3 # -2. w/o damage units did not divide into 3 types Synaptizine: effects: - - !type:HealthChange - damageGroup: Toxin - healthChange: 0.5 + - !type:HealthChange + damage: + types: + Poison: 1 # 0.5 pls damage units - type: entity id: OrganHumanStomach @@ -261,8 +267,9 @@ effects: - !type:SatiateThirst - !type:HealthChange - damageGroup: Toxin - healthChange: 1 + damage: + types: + Poison: 1 JuiceWatermelon: effects: - !type:SatiateThirst @@ -306,8 +313,9 @@ - !type:SatiateThirst hydrationFactor: 2 - !type:HealthChange - damageGroup: Toxin - healthChange: 1 + damage: + types: + Poison: 1 - type: entity id: OrganHumanLiver diff --git a/Resources/Prototypes/Body/Parts/human.yml b/Resources/Prototypes/Body/Parts/human.yml index c680ced71d..3066f0743c 100644 --- a/Resources/Prototypes/Body/Parts/human.yml +++ b/Resources/Prototypes/Body/Parts/human.yml @@ -5,6 +5,9 @@ parent: BaseItem name: "human body part" abstract: true + components: + - type: Damageable + damageContainer: Biological - type: entity id: TorsoHuman @@ -29,10 +32,6 @@ - OrganHumanLiver - OrganHumanKidneys - type: BiologicalSurgeryData - - type: Damageable - # TODO BODY DettachableDamageableComponent? - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 100 # deadThreshold: 150 @@ -57,9 +56,6 @@ - OrganHumanBrain - OrganHumanEyes - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 50 # deadThreshold: 120 - type: Input @@ -85,9 +81,6 @@ compatibility: Biological symmetry: Left - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 40 # deadThreshold: 80 - type: Extension @@ -111,9 +104,6 @@ compatibility: Biological symmetry: Right - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 40 # deadThreshold: 80 - type: Extension @@ -137,9 +127,6 @@ compatibility: Biological symmetry: Left - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 30 # deadThreshold: 60 - type: Grasp @@ -162,9 +149,6 @@ compatibility: Biological symmetry: Right - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 30 # deadThreshold: 60 - type: Grasp @@ -187,9 +171,6 @@ compatibility: Biological symmetry: Left - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 45 # deadThreshold: 90 - type: Leg @@ -215,9 +196,6 @@ compatibility: Biological symmetry: Right - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 45 # deadThreshold: 90 - type: Leg @@ -243,9 +221,6 @@ compatibility: Biological symmetry: Left - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 30 # deadThreshold: 60 @@ -267,8 +242,5 @@ compatibility: Biological symmetry: Right - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 30 # deadThreshold: 60 diff --git a/Resources/Prototypes/Body/Parts/slime.yml b/Resources/Prototypes/Body/Parts/slime.yml index 5009347247..3909e35240 100644 --- a/Resources/Prototypes/Body/Parts/slime.yml +++ b/Resources/Prototypes/Body/Parts/slime.yml @@ -4,6 +4,9 @@ parent: BaseItem name: "slime body part" abstract: true + components: + - type: Damageable + damageContainer: Biological - type: entity id: TorsoSlime @@ -28,10 +31,6 @@ - OrganHumanLiver - OrganHumanKidneys - type: BiologicalSurgeryData - - type: Damageable - # TODO BODY DettachableDamageableComponent? - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 100 # deadThreshold: 150 @@ -56,9 +55,6 @@ - OrganHumanBrain - OrganHumanEyes - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 50 # deadThreshold: 120 - type: Input @@ -84,9 +80,6 @@ compatibility: Biological symmetry: Left - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 40 # deadThreshold: 80 - type: Extension @@ -110,9 +103,6 @@ compatibility: Biological symmetry: Right - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 40 # deadThreshold: 80 - type: Extension @@ -136,9 +126,6 @@ compatibility: Biological symmetry: Left - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 30 # deadThreshold: 60 - type: Grasp @@ -161,9 +148,6 @@ compatibility: Biological symmetry: Right - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 30 # deadThreshold: 60 - type: Grasp @@ -186,11 +170,6 @@ compatibility: Biological symmetry: Left - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances - # criticalThreshold: 45 - # deadThreshold: 90 - type: Leg speed: 2.6 - type: Extension @@ -214,9 +193,6 @@ compatibility: Biological symmetry: Right - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 45 # deadThreshold: 90 - type: Leg @@ -242,9 +218,6 @@ compatibility: Biological symmetry: Left - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 30 # deadThreshold: 60 @@ -266,8 +239,5 @@ compatibility: Biological symmetry: Right - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 30 # deadThreshold: 60 diff --git a/Resources/Prototypes/Body/Parts/vox.yml b/Resources/Prototypes/Body/Parts/vox.yml index 7b064ddcac..65b4d963eb 100644 --- a/Resources/Prototypes/Body/Parts/vox.yml +++ b/Resources/Prototypes/Body/Parts/vox.yml @@ -5,7 +5,10 @@ parent: BaseItem name: "vox body part" abstract: true - + components: + - type: Damageable + damageContainer: Biological + - type: entity id: TorsoVox name: "vox torso" @@ -29,10 +32,7 @@ - OrganHumanLiver - OrganHumanKidneys - type: BiologicalSurgeryData - - type: Damageable # TODO BODY DettachableDamageableComponent? - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 100 # deadThreshold: 150 @@ -57,9 +57,6 @@ - OrganHumanBrain - OrganHumanEyes - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 50 # deadThreshold: 120 - type: Input @@ -85,9 +82,6 @@ compatibility: Biological symmetry: Left - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 40 # deadThreshold: 80 - type: Extension @@ -111,9 +105,6 @@ compatibility: Biological symmetry: Right - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 40 # deadThreshold: 80 - type: Extension @@ -137,9 +128,6 @@ compatibility: Biological symmetry: Left - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 30 # deadThreshold: 60 - type: Grasp @@ -162,9 +150,6 @@ compatibility: Biological symmetry: Right - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 30 # deadThreshold: 60 - type: Grasp @@ -187,9 +172,6 @@ compatibility: Biological symmetry: Left - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 45 # deadThreshold: 90 - type: Leg @@ -215,9 +197,6 @@ compatibility: Biological symmetry: Right - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 45 # deadThreshold: 90 - type: Leg @@ -243,9 +222,6 @@ compatibility: Biological symmetry: Left - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 30 # deadThreshold: 60 @@ -267,8 +243,5 @@ compatibility: Biological symmetry: Right - type: BiologicalSurgeryData - - type: Damageable - damageContainer: biologicalDamageContainer - resistances: defaultResistances # criticalThreshold: 30 # deadThreshold: 60 diff --git a/Resources/Prototypes/Damage/containers.yml b/Resources/Prototypes/Damage/containers.yml new file mode 100644 index 0000000000..c4b37a4ac7 --- /dev/null +++ b/Resources/Prototypes/Damage/containers.yml @@ -0,0 +1,16 @@ +- type: damageContainer + id: Biological + supportedGroups: + - Brute + - Burn + - Toxin + - Airloss + - Genetic + +- type: damageContainer + id: Inorganic + supportedGroups: + - Brute + supportedTypes: + - Heat + - Shock diff --git a/Resources/Prototypes/Damage/damage.yml b/Resources/Prototypes/Damage/damage.yml deleted file mode 100644 index 139792242a..0000000000 --- a/Resources/Prototypes/Damage/damage.yml +++ /dev/null @@ -1,97 +0,0 @@ -# Silver: Todo break out into damage_type,damage_class, damage_container yml files when we support loading prototypes by priority. -- type: damageType - id: Blunt - -- type: damageType - id: Slash - -- type: damageType - id: Piercing - -- type: damageType - id: Heat - -- type: damageType - id: Shock - -- type: damageType - id: Cold - -# Poison damage. Generally caused by various reagents being metabolised. -- type: damageType - id: Poison - -- type: damageType - id: Radiation - -# Damage due to being unable to breathe. -# Represents not enough oxygen (or equivalent) getting to the blood. -# Usually healed automatically if entity can breathe -- type: damageType - id: Asphyxiation - -# Damage representing not having enough blood. -# Represents there not enough blood to supply oxygen (or equivalent). -- type: damageType - id: Bloodloss - -- type: damageType - id: Cellular - -- type: damageGroup - id: Brute - damageTypes: - - Blunt - - Slash - - Piercing - -- type: damageGroup - id: Burn - damageTypes: - - Heat - - Shock - - Cold - -# Airloss (sometimes called oxyloss) -# Caused by asphyxiation or bloodloss. -# Note that most medicine and damaging effects should probably modify either asphyxiation or -# bloodloss, not this whole group, unless you have a wonder drug that affects both. -- type: damageGroup - id: Airloss - damageTypes: - - Asphyxiation - - Bloodloss - -# As with airloss, most medicine and damage effects should probably modify either poison or radiation. -# Though there are probably some radioactive poisons. -- type: damageGroup - id: Toxin - damageTypes: - - Poison - - Radiation - - -- type: damageGroup - id: Genetic - damageTypes: - - Cellular - -- type: damageContainer - id: allDamageContainer - supportAll: true - - -- type: damageContainer - id: biologicalDamageContainer - supportedGroups: - - Brute - - Burn - - Toxin - - Airloss - - Genetic - -- type: damageContainer - id: metallicDamageContainer - supportedGroups: - - Brute - - Burn diff --git a/Resources/Prototypes/Damage/groups.yml b/Resources/Prototypes/Damage/groups.yml new file mode 100644 index 0000000000..6d782e601f --- /dev/null +++ b/Resources/Prototypes/Damage/groups.yml @@ -0,0 +1,36 @@ +- type: damageGroup + id: Brute + damageTypes: + - Blunt + - Slash + - Piercing + +- type: damageGroup + id: Burn + damageTypes: + - Heat + - Shock + - Cold + +# Airloss (sometimes called oxyloss) +# Caused by asphyxiation or bloodloss. +# Note that most medicine and damaging effects should probably modify either asphyxiation or +# bloodloss, not this whole group, unless you have a wonder drug that affects both. +- type: damageGroup + id: Airloss + damageTypes: + - Asphyxiation + - Bloodloss + +# As with airloss, most medicine and damage effects should probably modify either poison or radiation. +# Though there are probably some radioactive poisons. +- type: damageGroup + id: Toxin + damageTypes: + - Poison + - Radiation + +- type: damageGroup + id: Genetic + damageTypes: + - Cellular diff --git a/Resources/Prototypes/Damage/resistance_sets.yml b/Resources/Prototypes/Damage/resistance_sets.yml index b2ac9ac1aa..c04f895830 100644 --- a/Resources/Prototypes/Damage/resistance_sets.yml +++ b/Resources/Prototypes/Damage/resistance_sets.yml @@ -1,134 +1,30 @@ - type: resistanceSet - id: defaultResistances - coefficients: - Blunt: 1.0 - Slash: 1.0 - Piercing: 1.0 - Heat: 1.0 - Shock: 1.0 - Cold: 1.0 - Poison: 1.0 - Radiation: 1.0 - Asphyxiation: 1.0 - Bloodloss: 1.0 - Cellular: 1.0 - flatReductions: - Blunt: 0 - Slash: 0 - Piercing: 0 - Heat: 0 - Shock: 0 - Cold: 0 - Poison: 0 - Radiation: 0 - Asphyxiation: 0 - Bloodloss: 0 - Cellular: 0 - -- type: resistanceSet - id: dionaResistances - coefficients: - Blunt: 0.5 - Slash: 1.2 - Piercing: 0.7 - Heat: 1.8 - Shock: 0.5 - Cold: 1.5 - Poison: 0.8 - Radiation: 0 - Asphyxiation: 1.0 - Bloodloss: 1.0 - Cellular: 1.0 - flatReductions: - Blunt: 0 - Slash: 0 - Piercing: 0 - Heat: 0 - Shock: 0 - Cold: 0 - Poison: 0 - Radiation: 0 - Asphyxiation: 0 - Bloodloss: 0 - Cellular: 0 - -- type: resistanceSet - id: metallicResistances + id: Metallic coefficients: Blunt: 0.7 Slash: 0.5 Piercing: 0.7 - Heat: 1.0 Shock: 1.2 - Cold: 0 - Poison: 0 - Radiation: 0 - Asphyxiation: 0 - Bloodloss: 0 - Cellular: 0 flatReductions: Blunt: 5 - Slash: 0 - Piercing: 0 - Heat: 0 - Shock: 0 - Cold: 0 - Poison: 0 - Radiation: 0 - Asphyxiation: 0 - Bloodloss: 0 - Cellular: 0 - type: resistanceSet - id: inflatableResistances + id: Inflatable coefficients: Blunt: 0.5 - Slash: 1.0 Piercing: 2.0 Heat: 0.5 Shock: 0 - Cold: 0 - Poison: 0 - Radiation: 0 - Asphyxiation: 0 - Bloodloss: 0 - Cellular: 0 flatReductions: Blunt: 5 - Slash: 0 - Piercing: 0 - Heat: 0 - Shock: 0 - Cold: 0 - Poison: 0 - Radiation: 0 - Asphyxiation: 0 - Bloodloss: 0 - Cellular: 0 - type: resistanceSet - id: glassResistances + id: Glass coefficients: Blunt: 0.5 Slash: 0.5 Piercing: 0.5 Heat: 0 Shock: 0 - Cold: 0 - Poison: 0 - Radiation: 0 - Asphyxiation: 0 - Bloodloss: 0 - Cellular: 0 flatReductions: - Blunt: 5 - Slash: 0 - Piercing: 0 - Heat: 0 - Shock: 0 - Cold: 0 - Poison: 0 - Radiation: 0 - Asphyxiation: 0 - Bloodloss: 0 - Cellular: 0 + Blunt: 5 \ No newline at end of file diff --git a/Resources/Prototypes/Damage/types.yml b/Resources/Prototypes/Damage/types.yml new file mode 100644 index 0000000000..97d7566956 --- /dev/null +++ b/Resources/Prototypes/Damage/types.yml @@ -0,0 +1,38 @@ +- type: damageType + id: Blunt + +- type: damageType + id: Slash + +- type: damageType + id: Piercing + +- type: damageType + id: Heat + +- type: damageType + id: Shock + +- type: damageType + id: Cold + +# Poison damage. Generally caused by various reagents being metabolised. +- type: damageType + id: Poison + +- type: damageType + id: Radiation + +# Damage due to being unable to breathe. +# Represents not enough oxygen (or equivalent) getting to the blood. +# Usually healed automatically if entity can breathe +- type: damageType + id: Asphyxiation + +# Damage representing not having enough blood. +# Represents there not enough blood to supply oxygen (or equivalent). +- type: damageType + id: Bloodloss + +- type: damageType + id: Cellular diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml index 79301d8532..7572f232ab 100644 --- a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml +++ b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml @@ -20,7 +20,9 @@ - type: Clothing sprite: Clothing/Eyes/Glasses/gar.rsi - type: MeleeWeapon - damage: 10 + damage: + types: + Blunt: 10 - type: entity parent: ClothingEyesBase @@ -36,7 +38,9 @@ HeldPrefix: alt ClothingPrefix: alt - type: MeleeWeapon - damage: 10 + damage: + types: + Blunt: 10 - type: entity parent: ClothingEyesBase @@ -51,7 +55,9 @@ sprite: Clothing/Eyes/Glasses/gar.rsi ClothingPrefix: super - type: MeleeWeapon - damage: 10 + damage: + types: + Blunt: 10 - type: entity parent: ClothingEyesBase diff --git a/Resources/Prototypes/Entities/Debugging/debug_sweps.yml b/Resources/Prototypes/Entities/Debugging/debug_sweps.yml index 52f8021a6e..399b6441a9 100644 --- a/Resources/Prototypes/Entities/Debugging/debug_sweps.yml +++ b/Resources/Prototypes/Entities/Debugging/debug_sweps.yml @@ -51,8 +51,9 @@ tags: - Debug - type: Projectile - damages: - Blunt: 20000 + damage: + types: + Blunt: 20000 - type: entity id: CartridgeDebug @@ -80,7 +81,9 @@ sprite: Objects/Weapons/Melee/debug.rsi state: icon - type: MeleeWeapon - damage: 20000 + damage: + types: + Blunt: 20000 - type: Item size: 1 sprite: Objects/Weapons/Melee/debug.rsi @@ -95,7 +98,9 @@ tags: - Debug - type: MeleeWeapon - damage: 100 + damage: + types: + Blunt: 100 - type: entity name: bang stick 200dmg @@ -106,4 +111,6 @@ tags: - Debug - type: MeleeWeapon - damage: 200 + damage: + types: + Blunt: 200 diff --git a/Resources/Prototypes/Entities/Debugging/spanisharmyknife.yml b/Resources/Prototypes/Entities/Debugging/spanisharmyknife.yml index 928a9ad867..f06620fe12 100644 --- a/Resources/Prototypes/Entities/Debugging/spanisharmyknife.yml +++ b/Resources/Prototypes/Entities/Debugging/spanisharmyknife.yml @@ -17,7 +17,9 @@ Slots: - Belt - type: MeleeWeapon - damage: 10 + damage: + types: + Slash: 10 - type: TilePrying - type: Tool qualities: diff --git a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml index d2aea903b3..b363043657 100644 --- a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml +++ b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml @@ -125,7 +125,8 @@ anchored: true - type: Airtight - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/mimic.yml b/Resources/Prototypes/Entities/Mobs/NPCs/mimic.yml index a48d0baef1..bf0ae2693a 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/mimic.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/mimic.yml @@ -43,4 +43,6 @@ range: 1.5 arcwidth: 0 arc: fist - damage: 90 + damage: + types: + Blunt: 90 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml index 38c8e9040c..51235f6e12 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml @@ -22,7 +22,13 @@ - Clothing - Idle - type: Hunger + damage: + types: + Blunt: 2 - type: Thirst + damage: + types: + Blunt: 2 - type: Input context: "human" - type: AiFactionTag @@ -33,7 +39,13 @@ baseSprintSpeed : 4 - type: MovedByPressure - type: Barotrauma + damage: + types: + Blunt: 1 #per second, scales with pressure and other constants. - type: DamageOnHighSpeedImpact + damage: + types: + Blunt: 5 soundHit: path: /Audio/Effects/hit_kick.ogg - type: Sprite @@ -58,17 +70,26 @@ - type: Bloodstream max_volume: 100 - type: Damageable - damageContainer: biologicalDamageContainer + damageContainer: Biological - type: AtmosExposed - type: Flammable fireSpread: true canResistFire: true + damage: + types: + Heat : 1 - type: Temperature heatDamageThreshold: 360 coldDamageThreshold: 260 currentTemperature: 310.15 specificHeat: 42 tempDamageCoefficient: 0.1 + coldDamage: + types: + Cold : 1 #per second, scales with temperature & other constants + heatDamage: + types: + Heat : 1 #per second, scales with temperature & other constants - type: Respirator metabolismHeat: 5000 radiatedHeat: 400 @@ -82,6 +103,12 @@ producesGases: Oxygen: 0.00045572916 CarbonDioxide: 0.00015190972 + damage: + types: + Asphyxiation: 1 + damageRecovery: + types: + Asphyxiation: -1 - type: MobState thresholds: 0: !type:NormalMobState {} @@ -96,7 +123,9 @@ range: 1.5 arcwidth: 0 arc: bite - damage: 5 + damage: + groups: + Brute: 5 - type: Appearance visuals: - type: BuckleVisualizer diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml index 3d5613c740..a1d86e484c 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -39,11 +39,19 @@ 150: !type:CriticalMobState {} 200: !type:DeadMobState {} - type: Respirator + damage: + types: + Asphyxiation: 1 + damageRecovery: + types: + Asphyxiation: -1 - type: UnarmedCombat range: 1.5 arcwidth: 0 arc: claw - damage: 10 + damage: + groups: + Brute: 10 - type: Appearance visuals: - type: DamageStateVisualizer diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index 19b6cc811f..dc78b4ed6a 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -34,12 +34,23 @@ - type: MovementSpeedModifier - type: MovedByPressure - type: Barotrauma - damageType: Blunt + damage: + types: + Blunt: 1 #per second, scales with pressure and other constants. - type: DamageOnHighSpeedImpact - soundHit: + damage: + types: + Blunt: 5 + soundHit: path: /Audio/Effects/hit_kick.ogg - type: Hunger + damage: + types: + Blunt: 2 - type: Thirst + damage: + types: + Blunt: 2 # Organs - type: SolutionContainerManager solutions: @@ -162,20 +173,27 @@ - type: Flammable fireSpread: true canResistFire: true + damage: + types: + Heat : 1 #per second, scales with number of fire 'stacks' - type: Temperature - heatDamageType: Heat heatDamageThreshold: 360 - coldDamageType: Cold coldDamageThreshold: 260 currentTemperature: 310.15 specificHeat: 42 tempDamageCoefficient: 0.1 + coldDamage: + types: + Cold : 1 #per second, scales with temperature & other constants + heatDamage: + types: + Heat : 1 #per second, scales with temperature & other constants - type: HumanoidAppearance - type: Body template: HumanoidTemplate preset: HumanPreset - type: Damageable - damageContainer: biologicalDamageContainer + damageContainer: Biological - type: Respirator metabolismHeat: 5000 radiatedHeat: 400 @@ -189,6 +207,12 @@ producesGases: Oxygen: 0.00045572916 CarbonDioxide: 0.00015190972 + damage: + types: + Asphyxiation: 1 + damageRecovery: + types: + Asphyxiation: -1 - type: Internals - type: MobState thresholds: @@ -226,6 +250,9 @@ range: 0.8 arcwidth: 30 arc: fist + damage: + types: + Blunt: 5 - type: Pullable - type: DoAfter - type: CreamPied @@ -355,7 +382,7 @@ template: HumanoidTemplate preset: HumanPreset - type: Damageable - damageContainer: biologicalDamageContainer + damageContainer: Biological - type: MobState thresholds: 0: !type:NormalMobState {} diff --git a/Resources/Prototypes/Entities/Mobs/Species/vox.yml b/Resources/Prototypes/Entities/Mobs/Species/vox.yml index b85eaa1694..2c4b27fe5e 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/vox.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/vox.yml @@ -90,6 +90,12 @@ producesGases: Nitrogen: 0.00045572916 CarbonDioxide: 0.00015190972 + damage: + types: + Asphyxiation: 1 + damageRecovery: + types: + Asphyxiation: -1 # - type: Appearance # visuals: # - type: RotationVisualizer diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/bowl.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/bowl.yml index c2fde4d386..bf050b497a 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/bowl.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/bowl.yml @@ -15,11 +15,16 @@ state: bowl netsync: false - type: DamageOnLand - amount: 5 + damage: + types: + Blunt: 5 - type: DamageOtherOnHit - amount: 5 + damage: + types: + Blunt: 5 - type: Spillable - type: Damageable + damageContainer: Inorganic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/box.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/box.yml index cc8187d2aa..c9a70d7764 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/box.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/box.yml @@ -95,9 +95,7 @@ - box12 # Someday... # - type: DamageOnLand - # amount: 5 # - type: DamageOtherOnHit - # amount: 5 # - type: Damageable # - type: Destructible # thresholds: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/plate.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/plate.yml index cc73d5b243..d2dcaffa88 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/plate.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/plate.yml @@ -17,10 +17,15 @@ state: plate netsync: false - type: DamageOnLand - amount: 5 + damage: + types: + Blunt: 5 - type: DamageOtherOnHit - amount: 5 + damage: + types: + Blunt: 5 - type: Damageable + damageContainer: Inorganic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/tin.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/tin.yml index c8b1508128..f9292eaecb 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/tin.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/Containers/tin.yml @@ -19,10 +19,15 @@ HeldPrefix: packet color: gray - type: DamageOnLand - amount: 3 + damage: + types: + Blunt: 3 - type: DamageOtherOnHit - amount: 3 + damage: + types: + Blunt: 3 - type: Damageable + damageContainer: Inorganic - type: entity abstract: true diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/egg.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/egg.yml index 301215bd73..1088d8f565 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/egg.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/egg.yml @@ -25,10 +25,21 @@ - ReagentId: Egg Quantity: 5 - type: DamageOnLand - amount: 1 + damage: + types: + Blunt: 1 - type: DamageOtherOnHit - amount: 1 + damage: + types: + Blunt: 1 + #Dignity: 25 + - type: DamageOnHighSpeedImpact + minimumSpeed: 0.1 + damage: + types: + Blunt: 1 - type: Damageable + damageContainer: Biological - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml index d3bc2fcc22..4b9a52bf7c 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/ingredients.yml @@ -53,10 +53,15 @@ - ReagentId: Flour Quantity: 50 - type: DamageOnLand - amount: 1 + damage: + types: + Blunt: 1 - type: DamageOtherOnHit - amount: 1 + damage: + types: + Blunt: 1 - type: Damageable + damageContainer: Inorganic - type: Destructible thresholds: - trigger: @@ -90,10 +95,15 @@ - ReagentId: Flour Quantity: 20 - type: DamageOnLand - amount: 1 + damage: + types: + Blunt: 1 - type: DamageOtherOnHit - amount: 1 + damage: + types: + Blunt: 1 - type: Damageable + damageContainer: Inorganic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml index c8390b81bd..30e94f157e 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml @@ -77,6 +77,9 @@ sprite: Objects/Specific/Hydroponics/towercap.rsi - type: SolutionContainerManager - type: MeleeWeapon + damage: + types: + Blunt: 10 - type: Produce seed: towercap - type: Log @@ -253,10 +256,21 @@ - ReagentId: JuiceTomato Quantity: 10 - type: DamageOnLand - amount: 1 + damage: + types: + Blunt: 1 - type: DamageOtherOnHit - amount: 1 + damage: + types: + Blunt: 1 + #Dignity: 25 - type: Damageable + damageContainer: Biological + - type: DamageOnHighSpeedImpact + minimumSpeed: 0.1 + damage: + types: + Blunt: 1 - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml index 0ab9718224..3297ace1c6 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/soup.yml @@ -16,11 +16,16 @@ sprite: Objects/Consumable/Food/bowl.rsi netsync: false - type: DamageOnLand - amount: 5 + damage: + types: + Blunt: 5 - type: DamageOtherOnHit - amount: 5 + damage: + types: + Blunt: 5 - type: Spillable - type: Damageable + damageContainer: Inorganic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml index c504b18983..f620308048 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml @@ -29,6 +29,8 @@ - type: Drink isOpen: true - type: Damageable + damageContainer: Inorganic + resistanceSet: Glass - type: Destructible thresholds: - trigger: @@ -47,9 +49,13 @@ - !type:DoActsBehavior acts: [ "Destruction" ] - type: DamageOnLand - amount: 5 + damage: + types: + Blunt: 5 - type: DamageOtherOnHit - amount: 5 + damage: + types: + Blunt: 5 # Transformable container - normal glass - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml b/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml index dd11d2b0cf..662697f9ad 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml @@ -19,11 +19,16 @@ - type: Sprite state: icon - type: DamageOnLand - amount: 5 + damage: + types: + Blunt: 5 - type: DamageOtherOnHit - amount: 10 + damage: + types: + Blunt: 10 - type: Spillable - type: Damageable + damageContainer: Inorganic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index bd43b28d14..388d45acbb 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -565,6 +565,9 @@ range: 2.0 arcwidth: 0 arc: spear + damage: + types: + Blunt: 1 - type: Item size: 24 sprite: Objects/Fun/toys.rsi diff --git a/Resources/Prototypes/Entities/Objects/Materials/shards.yml b/Resources/Prototypes/Entities/Objects/Materials/shards.yml index fa3b2fc213..605fba885b 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/shards.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/shards.yml @@ -14,12 +14,15 @@ - shard3 - type: ItemCooldown - type: MeleeWeapon - damageType: Slash + damage: + types: + Slash: 5 - type: Item sprite: Objects/Materials/Shards/shard.rsi - type: DamageOtherOnHit - amount: 5 - damageType: Slash + damage: + types: + Slash: 5 - type: entity parent: ShardBase @@ -50,7 +53,9 @@ - SheetGlass1 - SheetSteel1 - type: DamageOtherOnHit - amount: 10 + damage: + types: + Slash: 10 - type: entity parent: ShardBase @@ -67,4 +72,6 @@ - SheetGlass1 - SheetPlasma1 - type: DamageOtherOnHit - amount: 15 + damage: + types: + Slash: 15 diff --git a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml index dfcb771a5a..1d366c1679 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml @@ -38,8 +38,9 @@ impulse: 50.0 - type: FireExtinguisher - type: MeleeWeapon - damage: 10 - damageType: Blunt + damage: + types: + Blunt: 10 hitSound: path: /Audio/Weapons/smash.ogg - type: Appearance diff --git a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml index 61f09d4b49..0b792b1874 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml @@ -107,7 +107,8 @@ energy: 5 - type: Anchorable - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: @@ -139,7 +140,8 @@ state: floodlight_broken - type: Anchorable - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Objects/Misc/inflatable_wall.yml b/Resources/Prototypes/Entities/Objects/Misc/inflatable_wall.yml index 58d499bce8..bdbff8a78f 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/inflatable_wall.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/inflatable_wall.yml @@ -21,7 +21,8 @@ - VaultImpassable - SmallImpassable - type: Damageable - resistances: inflatableResistances + damageContainer: Inorganic + resistanceSet: Inflatable - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml index 7ce2bb92e9..2d265a1b52 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/tiles.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/tiles.yml @@ -9,7 +9,9 @@ - type: Item sprite: Objects/Tiles/tile.rsi - type: DamageOtherOnHit - amount: 10 + damage: + types: + Blunt: 10 - type: Stack count: 1 diff --git a/Resources/Prototypes/Entities/Objects/Power/lights.yml b/Resources/Prototypes/Entities/Objects/Power/lights.yml index 4291e9d061..1bc3a8af96 100644 --- a/Resources/Prototypes/Entities/Objects/Power/lights.yml +++ b/Resources/Prototypes/Entities/Objects/Power/lights.yml @@ -6,10 +6,15 @@ components: - type: LightBulb - type: Damageable + damageContainer: Inorganic - type: DamageOnLand - amount: 5 + damage: + types: + Blunt: 5 - type: DamageOtherOnHit - amount: 5 + damage: + types: + Blunt: 5 - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml index 0ecfc5248c..6dcb33afa4 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/tools.yml @@ -13,6 +13,9 @@ state: icon - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Slash: 10 - type: Item sprite: Objects/Tools/Hydroponics/hoe.rsi @@ -30,6 +33,9 @@ state: icon - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Slash: 7 - type: Item sprite: Objects/Tools/Hydroponics/clippers.rsi @@ -47,6 +53,9 @@ state: icon - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Slash: 10 - type: Clothing sprite: Objects/Tools/Hydroponics/scythe.rsi size: 20 @@ -68,6 +77,10 @@ state: icon - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Slash: 10 + Piercing: 5 - type: Item sprite: Objects/Tools/Hydroponics/hatchet.rsi @@ -86,5 +99,9 @@ state: icon - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Blunt: 10 + Piercing: 5 # I guess you can stab it into them? - type: Item sprite: Objects/Tools/Hydroponics/spade.rsi diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml index 1d92807a62..dddfaf7b57 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml @@ -14,8 +14,9 @@ sprite: Objects/Specific/Medical/medical.rsi HeldPrefix: ointment - type: Healing - heal: - Heat: 10 + damage: + groups: + Burn: -10 # 5 for each type in the group - type: Stack stackType: Ointment max: 5 @@ -33,8 +34,9 @@ - type: Sprite state: brutepack - type: Healing - heal: - Blunt: 10 + damage: + groups: + Brute: -15 # 5 for each type in the group - type: Stack stackType: Brutepack @@ -50,7 +52,9 @@ - type: Sprite state: gauze - type: Healing - heal: - Blunt: 10 + damage: + types: + Slash: -5 + Piercing: -5 - type: Stack stackType: Gauze diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml index 7d5e861912..e5b6c49ce4 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml @@ -49,6 +49,9 @@ sprite: Objects/Specific/Medical/Surgery/drill.rsi - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Piercing: 10 hitSound: path: /Audio/Items/drill_hit.ogg @@ -73,9 +76,11 @@ sprite: Objects/Specific/Medical/Surgery/scalpel.rsi - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Slash: 12 hitSound: path: /Audio/Weapons/bladeslice.ogg - damage: 12 - type: entity name: shiv @@ -214,9 +219,11 @@ - type: Item HeldPrefix: improv - type: MeleeWeapon + damage: + groups: + Brute: 10 hitSound: path: /Audio/Weapons/bladeslice.ogg - damage: 10 - type: entity name: circular saw @@ -233,9 +240,11 @@ - type: Item HeldPrefix: electric - type: MeleeWeapon + damage: + groups: + Brute: 15 hitSound: path: /Audio/Items/drill_hit.ogg - damage: 15 - type: entity name: advanced circular saw @@ -252,6 +261,8 @@ - type: Item HeldPrefix: advanced - type: MeleeWeapon + damage: + groups: + Brute: 20 hitSound: path: /Audio/Items/drill_hit.ogg - damage: 20 diff --git a/Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml b/Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml index 6447a30d2f..8c549eed95 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml @@ -29,7 +29,8 @@ lockOnClick: true # toggle lock just by clicking on barrier - type: DeployableBarrier - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml b/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml index c1f60b379b..5861c91861 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/chemistry.yml @@ -44,6 +44,8 @@ maxFillLevels: 6 fillBaseName: beaker - type: Damageable + damageContainer: Inorganic + resistanceSet: Glass - type: Destructible thresholds: - trigger: @@ -62,9 +64,18 @@ - !type:DoActsBehavior acts: [ "Destruction" ] - type: DamageOnLand - amount: 5 + damage: + types: + Blunt: 10 # glass resistance set reduces damage. Need to land twice (w/o hitting wall) - type: DamageOtherOnHit - amount: 5 + damage: + types: + Blunt: 5 + - type: DamageOnHighSpeedImpact + minimumSpeed: 2 + damage: + types: + Blunt: 5 - type: entity name: large beaker diff --git a/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml b/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml index ec45872bbb..6e566fed11 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/cowtools.yml @@ -12,6 +12,9 @@ state: haycutters - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Blunt: 10 - type: Tool qualities: - Cutting @@ -34,6 +37,9 @@ sprite: Objects/Tools/Cowtools/moodriver.rsi - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Piercing: 10 - type: Tool qualities: - Screwing @@ -53,6 +59,9 @@ sprite: Objects/Tools/Cowtools/wronch.rsi - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Blunt: 10 - type: Tool qualities: - Anchoring @@ -73,6 +82,9 @@ sprite: Objects/Tools/Cowtools/cowbar.rsi - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Blunt: 10 - type: Tool qualities: - Prying @@ -116,6 +128,9 @@ HeldPrefix: off - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Blunt: 10 - type: ItemStatus - type: SolutionContainerManager solutions: diff --git a/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml b/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml index 9f42160368..63051f3736 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/toolbox.yml @@ -12,7 +12,9 @@ size: 9999 - type: ItemCooldown - type: MeleeWeapon - damage: 10 + damage: + types: + Blunt: 10 hitSound: path: "/Audio/Weapons/smash.ogg" @@ -76,7 +78,9 @@ - type: Item sprite: Objects/Tools/Toolboxes/toolbox_syn.rsi - type: MeleeWeapon - damage: 15 + damage: + types: + Blunt: 15 - type: entity name: golden toolbox diff --git a/Resources/Prototypes/Entities/Objects/Tools/tools.yml b/Resources/Prototypes/Entities/Objects/Tools/tools.yml index 04674c98d1..f9ba16a04d 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/tools.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/tools.yml @@ -18,6 +18,9 @@ - state: cutters-cutty-thingy - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Blunt: 10 - type: Tool qualities: - Cutting @@ -57,6 +60,9 @@ sprite: Objects/Tools/screwdriver.rsi - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Piercing: 10 - type: Tool qualities: - Screwing @@ -92,7 +98,9 @@ sprite: Objects/Tools/wrench.rsi - type: ItemCooldown - type: MeleeWeapon - damage: 7 + damage: + types: + Blunt: 10 - type: Tool qualities: - Anchoring @@ -119,7 +127,9 @@ size: 10 - type: ItemCooldown - type: MeleeWeapon - damage: 7 + damage: + types: + Blunt: 10 - type: Tool qualities: - Prying @@ -251,5 +261,8 @@ state: icon - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Blunt: 10 - type: Item sprite: Objects/Tools/shovel.rsi diff --git a/Resources/Prototypes/Entities/Objects/Tools/welders.yml b/Resources/Prototypes/Entities/Objects/Tools/welders.yml index 506f0da0b2..9d9230b038 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/welders.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/welders.yml @@ -20,6 +20,9 @@ HeldPrefix: off - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Blunt: 10 - type: ItemStatus - type: RefillableSolution solution: welder diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/antimaterial.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/antimaterial.yml index f0b24d2263..52f90b3a9d 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/antimaterial.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/antimaterial.yml @@ -5,5 +5,6 @@ name: bullet (.60 anti-material) components: - type: Projectile - damages: - Piercing: 70 + damage: + types: + Piercing: 70 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/clrifle.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/clrifle.yml index 3eae35edfa..74c5d6d510 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/clrifle.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/clrifle.yml @@ -5,8 +5,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 27 + damage: + types: + Piercing: 27 - type: entity id: BulletClRifleFlash @@ -15,8 +16,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 27 + damage: + types: + Piercing: 27 - type: entity id: BulletClRifleHV @@ -25,8 +27,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 32 + damage: + types: + Piercing: 32 - type: entity id: BulletClRiflePractice @@ -35,8 +38,9 @@ abstract: true components: - type: Projectile - damages: - Blunt: 2 + damage: + types: + Blunt: 2 - type: entity id: BulletClRifleRubber @@ -45,5 +49,6 @@ abstract: true components: - type: Projectile - damages: - Blunt: 3 + damage: + types: + Blunt: 3 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/lrifle.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/lrifle.yml index 082f492862..c20d364563 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/lrifle.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/lrifle.yml @@ -5,8 +5,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 28 + damage: + types: + Piercing: 28 - type: entity id: BulletLRifleFlash @@ -15,8 +16,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 28 + damage: + types: + Piercing: 28 - type: entity id: BulletLRifleHV @@ -25,8 +27,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 30 + damage: + types: + Piercing: 30 - type: entity id: BulletLRiflePractice @@ -35,8 +38,9 @@ abstract: true components: - type: Projectile - damages: - Blunt: 2 + damage: + types: + Blunt: 2 - type: entity id: BulletLRifleRubber @@ -45,5 +49,6 @@ abstract: true components: - type: Projectile - damages: - Blunt: 3 + damage: + types: + Blunt: 3 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/magnum.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/magnum.yml index 041120110e..0538ed1665 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/magnum.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/magnum.yml @@ -5,8 +5,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 32 + damage: + types: + Piercing: 32 - type: entity id: BulletMagnumFlash @@ -15,8 +16,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 32 + damage: + types: + Piercing: 32 - type: entity id: BulletMagnumHV @@ -25,8 +27,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 35 + damage: + types: + Piercing: 35 - type: entity id: BulletMagnumPractice @@ -35,8 +38,9 @@ abstract: true components: - type: Projectile - damages: - Blunt: 1 + damage: + types: + Blunt: 1 - type: entity id: BulletMagnumRubber @@ -45,5 +49,6 @@ abstract: true components: - type: Projectile - damages: - Blunt: 3 + damage: + types: + Blunt: 3 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/pistol.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/pistol.yml index 47394893de..9b1d6984df 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/pistol.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/pistol.yml @@ -5,8 +5,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 24 + damage: + types: + Piercing: 24 - type: entity id: BulletPistolFlash @@ -15,8 +16,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 24 + damage: + types: + Piercing: 24 - type: entity id: BulletPistolHV @@ -25,8 +27,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 28 + damage: + types: + Piercing: 28 - type: entity id: BulletPistolPractice @@ -35,8 +38,9 @@ abstract: true components: - type: Projectile - damages: - Blunt: 2 + damage: + types: + Blunt: 2 - type: entity id: BulletPistolRubber @@ -45,5 +49,6 @@ abstract: true components: - type: Projectile - damages: - Blunt: 3 + damage: + types: + Blunt: 3 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/shotgun.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/shotgun.yml index 078cbaf4a8..3202cf86cf 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/shotgun.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/shotgun.yml @@ -8,8 +8,9 @@ sprite: Objects/Weapons/Guns/Projectiles/slug.rsi state: base - type: Projectile - damages: - Piercing: 13 + damage: + types: + Piercing: 13 - type: entity id: PelletShotgunBeanbag @@ -21,8 +22,9 @@ sprite: Objects/Weapons/Guns/Projectiles/buckshot.rsi state: base - type: Projectile - damages: - Blunt: 10 + damage: + types: + Blunt: 10 - type: StunOnCollide - type: entity @@ -35,8 +37,9 @@ sprite: Objects/Weapons/Guns/Projectiles/buckshot.rsi state: base - type: Projectile - damages: - Piercing: 13 + damage: + types: + Piercing: 13 - type: entity id: PelletShotgunFlash @@ -48,8 +51,9 @@ sprite: Objects/Weapons/Guns/Projectiles/buckshot.rsi state: base - type: Projectile - damages: - Blunt: 13 + damage: + types: + Blunt: 13 - type: entity id: PelletShotgunIncendiary @@ -61,8 +65,9 @@ sprite: Objects/Weapons/Guns/Projectiles/buckshot.rsi state: base - type: Projectile - damages: - Blunt: 13 + damage: + types: + Blunt: 13 - type: entity id: PelletShotgunPractice @@ -74,8 +79,9 @@ sprite: Objects/Weapons/Guns/Projectiles/buckshot.rsi state: base - type: Projectile - damages: - Blunt: 1 + damage: + types: + Blunt: 1 - type: entity id: PelletShotgunTranquilizer @@ -87,8 +93,9 @@ sprite: Objects/Weapons/Guns/Projectiles/buckshot.rsi state: base - type: Projectile - damages: - Blunt: 1 + damage: + types: + Blunt: 1 - type: SolutionContainerManager solutions: ammo: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/srifle.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/srifle.yml index a86bde78ba..c1b3b91e29 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/srifle.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/srifle.yml @@ -5,8 +5,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 25 + damage: + types: + Piercing: 25 - type: entity id: BulletSRifleFlash @@ -15,8 +16,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 25 + damage: + types: + Piercing: 25 - type: entity id: BulletSRifleHV @@ -25,8 +27,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 30 + damage: + types: + Piercing: 30 - type: entity id: BulletSRiflePractice @@ -35,8 +38,9 @@ abstract: true components: - type: Projectile - damages: - Blunt: 2 + damage: + types: + Blunt: 2 - type: entity id: BulletSRifleRubber @@ -45,5 +49,6 @@ abstract: true components: - type: Projectile - damages: - Blunt: 3 + damage: + types: + Blunt: 3 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/toy.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/toy.yml index 90bfb7e918..ddf1df604f 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/toy.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/toy.yml @@ -5,8 +5,9 @@ abstract: true components: - type: Projectile - damages: - Blunt: 1 + damage: + types: + Blunt: 1 soundHit: path: /Audio/Weapons/Guns/Hits/bullet_hit.ogg @@ -25,5 +26,6 @@ sprite: Objects/Fun/toys.rsi state: foamdart - type: Projectile - damages: - Blunt: 1 + damage: + types: + Blunt: 1 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Explosives/grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Explosives/grenades.yml index 52c1b400c6..b302c6dd9b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Explosives/grenades.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Explosives/grenades.yml @@ -24,6 +24,7 @@ flashRange: 7 - type: ExplodeOnTrigger - type: Damageable + damageContainer: Inorganic - type: Destructible thresholds: - trigger: @@ -64,6 +65,7 @@ path: "/Audio/Effects/flash_bang.ogg" - type: DeleteOnTrigger - type: Damageable + damageContainer: Inorganic - type: Destructible thresholds: - trigger: @@ -100,6 +102,7 @@ flashRange: 10 - type: ExplodeOnTrigger - type: Damageable + damageContainer: Inorganic - type: Destructible thresholds: - trigger: @@ -135,6 +138,7 @@ flashRange: 50 - type: ExplodeOnTrigger - type: Damageable + damageContainer: Inorganic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml index f3513ded94..d14d3b03d9 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml @@ -4,11 +4,12 @@ abstract: true components: - type: Hitscan + damage: + types: + Heat: 10 spriteName: Objects/Weapons/Guns/Projectiles/laser.png muzzleFlash: Objects/Weapons/Guns/Projectiles/laser_muzzle.png impactFlash: Objects/Weapons/Guns/Projectiles/laser_impact.png - damageType: Heat - damage: 10 - type: entity name: red heavy laser @@ -19,8 +20,9 @@ spriteName: Objects/Weapons/Guns/Projectiles/heavy_laser.png muzzleFlash: Objects/Weapons/Guns/Projectiles/heavy_laser_muzzle.png impactFlash: Objects/Weapons/Guns/Projectiles/heavy_laser_impact.png - damageType: Heat - damage: 30 + damage: + types: + Heat: 30 - type: entity name: x-ray laser @@ -31,5 +33,6 @@ spriteName: Objects/Weapons/Guns/Projectiles/xray.png muzzleFlash: Objects/Weapons/Guns/Projectiles/xray_muzzle.png impactFlash: Objects/Weapons/Guns/Projectiles/xray_impact.png - damageType: Heat - damage: 60 + damage: + types: + Heat: 60 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml index ee15816217..f5786ef86c 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml @@ -25,10 +25,11 @@ linearDamping: 0 angularDamping: 0 - type: Projectile + damage: + types: + Piercing: 20 soundHit: path: /Audio/Weapons/Guns/Hits/bullet_hit.ogg - damages: - Piercing: 20 - type: entity id: BulletBaseFlash @@ -37,10 +38,11 @@ abstract: true components: - type: Projectile + damage: + types: + Piercing: 10 soundHit: path: /Audio/Weapons/Guns/Hits/snap.ogg - damages: - Piercing: 10 - type: FlashOnTrigger range: 1 - type: SoundOnTrigger @@ -56,8 +58,9 @@ abstract: true components: - type: Projectile - damages: - Piercing: 12 + damage: + types: + Piercing: 12 - type: entity id: BulletBasePractice @@ -66,8 +69,9 @@ abstract: true components: - type: Projectile - damages: - Blunt: 2 + damage: + types: + Blunt: 2 - type: entity id: BulletBaseRubber @@ -76,10 +80,11 @@ abstract: true components: - type: Projectile + damage: + types: + Blunt: 3 soundHit: path: /Audio/Weapons/Guns/Hits/snap.ogg - damages: - Blunt: 3 - type: StunOnCollide stunAmount: 2 knockdownAmount: 2 @@ -113,10 +118,11 @@ ammoVelocity: 20 caliber: Energy - type: Projectile + damage: + types: + Heat: 5 soundHit: path: "/Audio/Weapons/Guns/Hits/taser_hit.ogg" - damages: - Heat: 5 - type: StunOnCollide stunAmount: 5 knockdownAmount: 5 @@ -143,10 +149,11 @@ mask: - Opaque - type: Projectile + damage: + types: + Heat: 20 soundHit: path: /Audio/Weapons/Guns/Hits/bullet_hit.ogg - damages: - Heat: 20 - type: Tag tags: - EmitterBolt @@ -167,6 +174,9 @@ - type: TriggerOnCollide - type: Projectile deleteOnCollide: false + damage: + types: + Blunt: 1 - type: Explosive devastationRange: 1 heavyImpactRange: 2 @@ -189,6 +199,9 @@ state: grenade - type: Projectile deleteOnCollide: false + damage: + types: + Blunt: 1 soundHit: path: /Audio/Effects/gen_hit.ogg - type: StunOnCollide @@ -210,6 +223,9 @@ - type: TriggerOnCollide - type: Projectile deleteOnCollide: false + damage: + types: + Blunt: 1 - type: Explosive devastationRange: 1 heavyImpactRange: 2 @@ -228,6 +244,9 @@ state: grenade - type: Projectile deleteOnCollide: false + damage: + types: + Blunt: 1 soundHit: path: /Audio/Effects/flash_bang.ogg - type: FlashOnTrigger @@ -254,6 +273,9 @@ - type: TriggerOnCollide - type: Projectile deleteOnCollide: false + damage: + types: + Blunt: 1 - type: Explosive devastationRange: 0 heavyImpactRange: 1 @@ -272,10 +294,11 @@ state: foamdart - type: Projectile deleteOnCollide: true + damage: + types: + Blunt: 2 soundHit: path: /Audio/Weapons/Guns/Hits/snap.ogg - damages: - Blunt: 2 - type: entity id: BulletCap @@ -289,8 +312,9 @@ state: capbullet - type: Projectile deleteOnCollide: true - damages: - Piercing: 0 + damage: + types: + Piercing: 0 - type: entity id: BulletCreampie @@ -301,6 +325,9 @@ components: - type: Projectile deleteOnCollide: false # CreamPie component handles this. + damage: + types: + Blunt: 1 - type: CreamPie - type: ThrownItem - type: Sprite diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml index 029ccd21da..31b9c54fce 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/baseball_bat.yml @@ -8,7 +8,9 @@ sprite: Objects/Weapons/Melee/baseball_bat.rsi state: icon - type: MeleeWeapon - damage: 15 + damage: + types: + Blunt: 15 - type: Clothing size: 15 sprite: Objects/Weapons/Melee/baseball_bat.rsi diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml index 643d250a87..a4b2a73565 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/fireaxe.yml @@ -11,7 +11,9 @@ sprite: Objects/Weapons/Melee/fireaxe.rsi state: icon - type: MeleeWeapon - damage: 25 + damage: + groups: + Brute: 25 - type: Clothing size: 20 sprite: Objects/Weapons/Melee/fireaxe.rsi diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/gohei.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/gohei.yml index 18f173c5cc..0321dd2e7a 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/gohei.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/gohei.yml @@ -8,7 +8,9 @@ sprite: Objects/Weapons/Melee/gohei.rsi state: gohei - type: MeleeWeapon - damage: 3 ##You'd be better off punching people + damage: + types: + Blunt: 3 #You'd be better off punching people - type: Item size: 12 sprite: Objects/Weapons/Melee/gohei.rsi diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml index ecfa266739..d5e63a1f83 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml @@ -11,9 +11,11 @@ - Knife - type: ItemCooldown - type: MeleeWeapon + damage: + types: + Slash: 12 hitSound: path: /Audio/Weapons/bladeslice.ogg - damage: 12 - type: Sprite netsync: false - type: Item @@ -51,7 +53,9 @@ size: 4 state: butch - type: MeleeWeapon - damage: 15 + damage: + types: + Slash: 15 - type: Item size: 10 sprite: Objects/Weapons/Melee/cleaver.rsi @@ -72,7 +76,9 @@ size: 2 state: icon - type: MeleeWeapon - damage: 15 + damage: + types: + Slash: 15 - type: Item size: 10 sprite: Objects/Weapons/Melee/combat_knife.rsi diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml index 39f1b8bf96..e0f1bbfef0 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/pickaxe.yml @@ -7,12 +7,13 @@ - type: Sprite sprite: Objects/Weapons/Melee/pickaxe.rsi state: pickaxe - - - type: Pickaxe - type: ItemCooldown - type: MeleeWeapon - damage: 25 + damage: + types: + Piercing: 15 + Blunt: 5 - type: Item size: 24 sprite: Objects/Weapons/Melee/pickaxe.rsi diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml index d2c0ce7f83..24b6120d02 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/spear.yml @@ -11,12 +11,16 @@ sprite: Objects/Weapons/Melee/spear.rsi state: spear - type: MeleeWeapon - damage: 10 + damage: + types: + Piercing: 10 range: 1.5 arcwidth: 0 arc: spear - type: DamageOtherOnHit - amount: 15 + damage: + types: + Piercing: 15 - type: Clothing size: 24 sprite: Objects/Weapons/Melee/spear.rsi diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index 5c80e445df..81f99c4fae 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -8,7 +8,9 @@ sprite: Objects/Weapons/Melee/captain_sabre.rsi state: icon - type: MeleeWeapon - damage: 15 + damage: + types: + Slash: 26 #cmon, it has to be at least BETTER than the rest. - type: Item size: 15 sprite: Objects/Weapons/Melee/captain_sabre.rsi @@ -29,7 +31,9 @@ sprite: Objects/Weapons/Melee/katana.rsi state: icon - type: MeleeWeapon - damage: 25 + damage: + types: + Slash: 25 - type: Item size: 15 sprite: Objects/Weapons/Melee/katana.rsi @@ -47,7 +51,9 @@ sprite: Objects/Weapons/Melee/machete.rsi state: icon - type: MeleeWeapon - damage: 20 + damage: + types: + Slash: 20 - type: Item size: 15 sprite: Objects/Weapons/Melee/machete.rsi @@ -62,7 +68,9 @@ sprite: Objects/Weapons/Melee/claymore.rsi state: icon - type: MeleeWeapon - damage: 25 + damage: + types: + Slash: 25 - type: Clothing size: 20 sprite: Objects/Weapons/Melee/claymore.rsi diff --git a/Resources/Prototypes/Entities/Objects/Weapons/security.yml b/Resources/Prototypes/Entities/Objects/Weapons/security.yml index ad668ea4c0..ab4859b354 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/security.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/security.yml @@ -9,7 +9,9 @@ state: stunbaton_off - type: Stunbaton - type: MeleeWeapon - damage: 10 + damage: + types: + Blunt: 10 range: 1.5 arcwidth: 60 arc: default @@ -36,7 +38,9 @@ state: flash - type: Flash - type: MeleeWeapon - damage: 0 + damage: + types: + Blunt: 0 # why is this classed as a melee weapon? Is it needed for some interaction? range: 1 arcWidth: 10 arc: default diff --git a/Resources/Prototypes/Entities/Objects/base.yml b/Resources/Prototypes/Entities/Objects/base.yml index e38635dc94..530507f3a5 100644 --- a/Resources/Prototypes/Entities/Objects/base.yml +++ b/Resources/Prototypes/Entities/Objects/base.yml @@ -9,6 +9,9 @@ - type: InteractionOutline - type: MovedByPressure - type: DamageOnHighSpeedImpact + damage: + types: + Blunt: 5 soundHit: path: /Audio/Effects/hit_kick.ogg - type: CollisionWake diff --git a/Resources/Prototypes/Entities/Structures/Dispensers/base.yml b/Resources/Prototypes/Entities/Structures/Dispensers/base.yml index 860a32d74c..3eced3a2b7 100644 --- a/Resources/Prototypes/Entities/Structures/Dispensers/base.yml +++ b/Resources/Prototypes/Entities/Structures/Dispensers/base.yml @@ -29,7 +29,8 @@ - type: Anchorable - type: Pullable - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml index b7d264d589..720fc11fb6 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml @@ -71,6 +71,9 @@ components: - type: Door occludes: false + crushDamage: + types: + Blunt: 15 - type: Occluder enabled: false - type: Sprite diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/assembly.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/assembly.yml index a8285e0002..c023c30e29 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/assembly.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/assembly.yml @@ -26,7 +26,8 @@ - type: Transform anchored: true - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base.yml index c570660647..316aac7d39 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base.yml @@ -38,6 +38,9 @@ - SmallImpassable - type: Door board: DoorElectronics + crushDamage: + types: + Blunt: 15 openSound: path: /Audio/Machines/airlock_open.ogg closeSound: @@ -61,7 +64,8 @@ fixVacuum: true - type: Occluder - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml index 6435ab2a95..eea48b8ac0 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/external.yml @@ -6,6 +6,9 @@ components: - type: Door bumpOpen: false + crushDamage: + types: + Blunt: 15 openSound: path: /Audio/Machines/airlock_ext_open.ogg closeSound: diff --git a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml index 00051a2923..f4fd87908f 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/firelock.yml @@ -6,7 +6,8 @@ components: - type: InteractionOutline - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: @@ -54,6 +55,9 @@ startOpen: true bumpOpen: false inhibitCrush: false + crushDamage: + types: + Blunt: 15 openSound: path: /Audio/Machines/airlock_open.ogg closeSound: @@ -89,6 +93,9 @@ - type: Door occludes: false inhibitCrush: false + crushDamage: + types: + Blunt: 15 - type: Occluder enabled: false - type: Sprite @@ -102,6 +109,9 @@ - type: Door occludes: false inhibitCrush: false + crushDamage: + types: + Blunt: 15 - type: Sprite sprite: Structures/Doors/edge_door_hazard.rsi - type: Airtight diff --git a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/frame.yml b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/frame.yml index dd73940b31..aea9aa4f8e 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Firelocks/frame.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Firelocks/frame.yml @@ -12,7 +12,8 @@ node: frame1 - type: InteractionOutline - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Doors/Windoors/assembly.yml b/Resources/Prototypes/Entities/Structures/Doors/Windoors/assembly.yml index 6110444807..8dc65919ed 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Windoors/assembly.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Windoors/assembly.yml @@ -24,7 +24,8 @@ - type: Pullable - type: Rotatable - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Doors/Windoors/base.yml b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base.yml index 90b8cb783a..bb3bddefb8 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Windoors/base.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base.yml @@ -38,7 +38,8 @@ map: ["enum.WiresVisualLayers.MaintenancePanel"] - type: ApcPowerReceiver - type: Damageable - resistances: glassResistances + damageContainer: Inorganic + resistanceSet: Glass - type: Destructible thresholds: - trigger: @@ -60,6 +61,9 @@ openPanelVisible: true - type: Door weldable: false + crushDamage: + types: + Blunt: 10 openSound: path: /Audio/Machines/windoor_open.ogg closeSound: diff --git a/Resources/Prototypes/Entities/Structures/Furniture/Tables/base.yml b/Resources/Prototypes/Entities/Structures/Furniture/Tables/base.yml index 8fdc6370cd..5d1ea0ea2b 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/Tables/base.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/Tables/base.yml @@ -6,7 +6,8 @@ abstract: true components: - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: PlaceableSurface - type: Sprite netsync: false diff --git a/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml b/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml index 729ded7258..6c0ac989d1 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/Tables/tables.yml @@ -9,7 +9,8 @@ - type: Icon sprite: Structures/Furniture/Tables/frame.rsi - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: @@ -41,7 +42,8 @@ - type: Icon sprite: Structures/Furniture/Tables/generic.rsi - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Furniture/beds.yml b/Resources/Prototypes/Entities/Structures/Furniture/beds.yml index c4658109e5..fc39bb4cf1 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/beds.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/beds.yml @@ -24,7 +24,8 @@ position: Down rotation: -90 - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Furniture/bookshelf.yml b/Resources/Prototypes/Entities/Structures/Furniture/bookshelf.yml index d3121f063b..45964eee15 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/bookshelf.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/bookshelf.yml @@ -19,7 +19,7 @@ - book-4 - book-5 - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Furniture/carpets.yml b/Resources/Prototypes/Entities/Structures/Furniture/carpets.yml index 17ac1a5ab4..fa2d20d58f 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/carpets.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/carpets.yml @@ -19,6 +19,7 @@ layer: - Passable - type: Damageable + damageContainer: Inorganic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Furniture/instruments.yml b/Resources/Prototypes/Entities/Structures/Furniture/instruments.yml index f3a1f66670..83a6eae95b 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/instruments.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/instruments.yml @@ -12,7 +12,8 @@ - type: Sprite sprite: Structures/Furniture/instruments.rsi - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml b/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml index 17bfbd126b..b7ed907cf1 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/potted_plants.yml @@ -25,6 +25,7 @@ secretPartName: the plant - type: Pullable - type: Damageable + damageContainer: Inorganic # The pot. Not the plant. Or is it plastic? - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Furniture/seats.yml b/Resources/Prototypes/Entities/Structures/Furniture/seats.yml index 04cd7133dc..6e3f11d2dc 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/seats.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/seats.yml @@ -24,7 +24,8 @@ position: Stand - type: Pullable - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/frame.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/frame.yml index a7a54edd13..11c2368ce7 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/frame.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/frame.yml @@ -28,7 +28,8 @@ sprite: Structures/Machines/parts.rsi state: 0 - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Machines/base.yml b/Resources/Prototypes/Entities/Structures/Machines/base.yml index 777784c12e..01ee62866d 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/base.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/base.yml @@ -18,7 +18,8 @@ mask: - MobMask - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml b/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml index 6b99c105ba..5e38efd733 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/chem_master.yml @@ -32,6 +32,8 @@ - Opaque - MobImpassable - type: Damageable + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: @@ -81,6 +83,7 @@ - Opaque - MobImpassable - type: Damageable + damageContainer: Inorganic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Machines/frame.yml b/Resources/Prototypes/Entities/Structures/Machines/frame.yml index 2ed113a73a..0b80b0ccf4 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/frame.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/frame.yml @@ -31,7 +31,8 @@ graph: machine node: missingWires - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: @@ -78,7 +79,8 @@ graph: machine node: machineFrame - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/miners.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/miners.yml index 1f334fb945..12f36e2a14 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/miners.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/miners.yml @@ -12,6 +12,8 @@ - type: Transform anchored: true - type: Damageable + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml index 0691494cd4..f97f2c4582 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/pipes.yml @@ -12,6 +12,8 @@ - type: Transform anchored: true - type: Damageable + damageContainer: Inorganic + resistanceSet: Metallic - type: Anchorable - type: Rotatable - type: Pullable diff --git a/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml b/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml index 1c1e0871ef..8939bd4011 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Disposal/pipes.yml @@ -16,7 +16,8 @@ anchored: true - type: Anchorable - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml index 356549836d..d38655749f 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/PA/particles.yml @@ -13,8 +13,9 @@ deleteOnCollide: false soundHit: path: /Audio/Weapons/Guns/Hits/bullet_hit.ogg - damages: - Radiation: 10 + damage: + types: + Radiation: 10 - type: Physics fixtures: - shape: diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml index d6b56b6df0..3b4972bbc2 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/emitter.yml @@ -45,7 +45,8 @@ !type:CableDeviceNode nodeGroupID: MVPower - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/ame.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/ame.yml index b82f90b6a1..54bb3dd6b7 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/ame.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/ame.yml @@ -25,7 +25,8 @@ - MobImpassable - SmallImpassable - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: @@ -110,7 +111,8 @@ - VaultImpassable - SmallImpassable - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml index 2b79ce3959..6735cdc38d 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/solar.yml @@ -39,7 +39,8 @@ - type: Transform anchored: true - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: @@ -79,7 +80,8 @@ - type: Transform anchored: true - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: @@ -124,7 +126,8 @@ - type: Transform anchored: true - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml b/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml index b15dcc7cbe..381a0b0e3d 100644 --- a/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml +++ b/Resources/Prototypes/Entities/Structures/Power/cable_terminal.yml @@ -21,7 +21,8 @@ - type: Transform anchored: true - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Power/cables.yml b/Resources/Prototypes/Entities/Structures/Power/cables.yml index 52b3a70195..e21a3f2fe1 100644 --- a/Resources/Prototypes/Entities/Structures/Power/cables.yml +++ b/Resources/Prototypes/Entities/Structures/Power/cables.yml @@ -19,7 +19,8 @@ netsync: false drawdepth: BelowFloor - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Power/debug_power.yml b/Resources/Prototypes/Entities/Structures/Power/debug_power.yml index 0e728462b0..fb56873e7e 100644 --- a/Resources/Prototypes/Entities/Structures/Power/debug_power.yml +++ b/Resources/Prototypes/Entities/Structures/Power/debug_power.yml @@ -38,7 +38,8 @@ - type: PowerConsumer drawRate: 50 - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml index 6e17f9659d..4efac92347 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Canisters/gas_canisters.yml @@ -42,7 +42,8 @@ - !type:DoActsBehavior acts: [ "Destruction" ] - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Physics bodyType: Dynamic fixtures: @@ -362,7 +363,8 @@ - !type:DoActsBehavior acts: [ "Destruction" ] - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: InteractionOutline - type: Sprite sprite: Structures/Storage/canister.rsi diff --git a/Resources/Prototypes/Entities/Structures/Storage/Closets/base.yml b/Resources/Prototypes/Entities/Structures/Storage/Closets/base.yml index f75eb45548..50cb140f76 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Closets/base.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Closets/base.yml @@ -16,6 +16,9 @@ map: ["enum.StorageVisualLayers.Welded"] - type: MovedByPressure - type: DamageOnHighSpeedImpact + damage: + types: + Blunt: 5 soundHit: path: /Audio/Effects/bang.ogg - type: InteractionOutline @@ -37,7 +40,8 @@ - type: PlaceableSurface placeCentered: true - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Storage/Crates/base.yml b/Resources/Prototypes/Entities/Structures/Storage/Crates/base.yml index 933d1438a3..3254423ef0 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Crates/base.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Crates/base.yml @@ -35,7 +35,8 @@ CanWeldShut: true - type: PlaceableSurface - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml index 4080f82687..2c77192e6b 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml @@ -530,7 +530,8 @@ state_open: livestockcrate_open state_closed: livestockcrate_door - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Storage/Tanks/base.yml b/Resources/Prototypes/Entities/Structures/Storage/Tanks/base.yml index e438f47eee..744176c89f 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Tanks/base.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Tanks/base.yml @@ -24,7 +24,8 @@ - VaultImpassable - SmallImpassable - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Storage/storage.yml b/Resources/Prototypes/Entities/Structures/Storage/storage.yml index 9d572776f4..e36510ee71 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/storage.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/storage.yml @@ -26,7 +26,8 @@ - type: Pullable - type: Anchorable - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/base.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/base.yml index ec988ce0cc..18c4def4ef 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/base.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/Signs/base.yml @@ -12,7 +12,8 @@ - shape: !type:PhysShapeAabb {} - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/lighting.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/lighting.yml index cedc68e419..a004d2cb28 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/lighting.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/lighting.yml @@ -35,7 +35,8 @@ - key: enum.SignalReceiverUiKey.Key type: SignalPortSelectorBoundUserInterface - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: @@ -68,6 +69,9 @@ enabled: false - type: PoweredLight bulb: Tube + damage: + types: + Heat: 20 - type: ApcPowerReceiver - type: Appearance visuals: @@ -85,6 +89,9 @@ state: empty - type: PoweredLight hasLampOnSpawn: False + damage: + types: + Heat: 20 - type: entity name: small light @@ -107,6 +114,7 @@ softness: 1.1 enabled: true - type: Damageable + damageContainer: Inorganic - type: Destructible thresholds: - trigger: @@ -146,6 +154,9 @@ layer: [ Passable ] - type: PoweredLight bulb: Bulb + damage: + types: + Heat: 20 - type: ApcPowerReceiver - type: Appearance visuals: @@ -160,3 +171,6 @@ state: empty - type: PoweredLight hasLampOnSpawn: False + damage: + types: + Heat: 20 diff --git a/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml b/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml index da2a7bf71d..6880d32cae 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml @@ -10,7 +10,8 @@ sprite: Structures/Walls/asteroid_rock.rsi state: 0 - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Walls/barricades.yml b/Resources/Prototypes/Entities/Structures/Walls/barricades.yml index e79c7dfeac..1d8171f9ef 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/barricades.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/barricades.yml @@ -24,7 +24,8 @@ tags: - ExplosivePassable - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: @@ -41,6 +42,9 @@ - type: AtmosExposed - type: Flammable fireSpread: true + damage: + types: + Heat : 1 #per second, scales with number of fire 'stacks' - type: Reactive reactions: - !type:ExtinguishReaction diff --git a/Resources/Prototypes/Entities/Structures/Walls/base.yml b/Resources/Prototypes/Entities/Structures/Walls/base.yml index 77c788dac0..6c1592926b 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/base.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/base.yml @@ -18,7 +18,8 @@ - type: Icon state: full - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Physics bodyType: Static fixtures: diff --git a/Resources/Prototypes/Entities/Structures/Walls/girder.yml b/Resources/Prototypes/Entities/Structures/Walls/girder.yml index 6e357f0f9f..22e839ce8a 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/girder.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/girder.yml @@ -28,7 +28,8 @@ tags: - ExplosivePassable - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Walls/low.yml b/Resources/Prototypes/Entities/Structures/Walls/low.yml index 0ac03fead4..5ba591127a 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/low.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/low.yml @@ -15,7 +15,8 @@ sprite: Structures/Walls/low_wall.rsi state: metal - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/Windows/plasma.yml b/Resources/Prototypes/Entities/Structures/Windows/plasma.yml index 35fbbe09c8..3dfbe99d61 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/plasma.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/plasma.yml @@ -9,7 +9,8 @@ - type: Icon sprite: Structures/Windows/plasma_window.rsi - type: Damageable - resistances: glassResistances + damageContainer: Inorganic + resistanceSet: Glass - type: Destructible thresholds: - trigger: @@ -26,7 +27,6 @@ max: 2 - !type:DoActsBehavior acts: [ "Destruction" ] - resistances: glassResistances - type: Window base: pwindow - type: Construction diff --git a/Resources/Prototypes/Entities/Structures/Windows/window.yml b/Resources/Prototypes/Entities/Structures/Windows/window.yml index 6c7f820119..50635b5fcc 100644 --- a/Resources/Prototypes/Entities/Structures/Windows/window.yml +++ b/Resources/Prototypes/Entities/Structures/Windows/window.yml @@ -30,7 +30,8 @@ mask: - VaultImpassable - type: Damageable - resistances: glassResistances + damageContainer: Inorganic + resistanceSet: Glass - type: Repairable - type: Destructible thresholds: diff --git a/Resources/Prototypes/Entities/Structures/cargo_telepad.yml b/Resources/Prototypes/Entities/Structures/cargo_telepad.yml index 930f439f87..b500c48858 100644 --- a/Resources/Prototypes/Entities/Structures/cargo_telepad.yml +++ b/Resources/Prototypes/Entities/Structures/cargo_telepad.yml @@ -19,7 +19,8 @@ state: offline drawdepth: FloorObjects - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/meat_spike.yml b/Resources/Prototypes/Entities/Structures/meat_spike.yml index 5aa05a11f3..8e4b0fb2b7 100644 --- a/Resources/Prototypes/Entities/Structures/meat_spike.yml +++ b/Resources/Prototypes/Entities/Structures/meat_spike.yml @@ -11,7 +11,8 @@ sprite: Structures/meat_spike.rsi state: spike - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: diff --git a/Resources/Prototypes/Entities/Structures/soil.yml b/Resources/Prototypes/Entities/Structures/soil.yml index d88a9d5305..bb6f10e09c 100644 --- a/Resources/Prototypes/Entities/Structures/soil.yml +++ b/Resources/Prototypes/Entities/Structures/soil.yml @@ -25,7 +25,8 @@ - VaultImpassable - SmallImpassable - type: Damageable - resistances: metallicResistances + damageContainer: Inorganic + resistanceSet: Metallic - type: Destructible thresholds: - trigger: