Damageable Refactor 3: Revenge of the Instamerge (#4524)
* Add DamageType And DamageGroup Prototypes * Remove DamageTypePrototype Field "name" as its redundant * Change I/DamageableComponent to use prototypes * Update DamageContainer, ReisistanceSet and DamageChangeData * Change Barotrauma Component to use DamageType from DamageSystem * Update AsteroidRockComponent * update some more components * update some more components * Fix m o r e c o m p o n e n t s and their damageType * all thats left is bug/missing node hunting then verification. * push changes * update submodule * Merge fixes * push DGP for example * update damagecomponent across shared and server * fix a few bugs * Fix Merge issues * Refactor damageablecomponent update (#4406) * Fixing merge. I messed up part of the merge. this should fix it? * Barotrauma now uses prototypeManager As System.Runtime.CompilerServices also has a [Dependency], I think I had to use the full path [Robust.Shared.IoC.Dependency] * FlammableComponent now uses prototypeManager * SuicideCommands now use prototypeManager * Changed many files to use prototypeManager to resolve damaege prototypes Yeah.... prototype references would be very nice. maybe this was all a waste of time. * Grouping prototypeManager.Index with datafield definitions This will make it easier to eventually add prototype references * removed unused variable * Moved lines around. Lines now consistent with other TODO PROTOTYPE blocks * Grouping more prototypeManager.Index with datafield definitions * Removed unnecessary code * Added more prototypeManager indexing These ones weren't pointed out by DrSmug. But I think this is all of them? That or my regex is shit. * Remove redundant _damage field * Remove redundant _currentTemperature * Moved variables down * Added prototypeManager indexing to TemperatureComponent * WeaponComponent/System now use ProtptypeManager And as far as I can tell damageType is required, and therefore should never have been null anyway? * Make ranged weapon clumsy fire effects datafields And yes, the order in which the clumsy effects occur is very important. * Made damage on vital body part loss a datafield * Renamed several damageGroup variables to group * Capitalised DamageListToDamageGroup * Make radiation and explosion damage types datafields * Renamed _supportedDamageGroupIDs and _supportedDamageTypeIDs * Fixed mistakes Frogot to remove prototypeManager index DamageTypeTrigger, and wrong variable visibility in TemperatureComponent * Added necessary code Is something tragically wrong? * MeleeWeapon damageType is not actually required * Fixing someone else's mistakes A search comes up with nothing in the yaml files, and its not a required field. So no one uses it? Hopefully? * Changed and renamed damageTypeToDamageGroup Previously would incorrectly return the total container damage for each group, not the total in the group * renaming varitables * Renamed variable DamageClasses * Added dictionary converting functions * Added ID-keyed dictionaries * Making MedicalScanner use ID dictionaries, instead of prototype dictionaries Oh oh no. I've been able to avoid UI & networking up until now. I have no Idea what I am doing. * Fix Medical Scanner * Summary (required) The joke here is that this fixes the empty summary. * Removed DamageableComponent.GetDamageGroup/Type * Renamed "damage classes" to groups. * Update ChangeDamage description * Replaced Heal() with SettAllDamage() Heal() was just a confusing name, * More Class -> Group renaming * Replace Class with Group in yaml files DamageClassTrigger does not appear in any yaml? only in testing? DamageTypeTrigger appears only in human.yaml? HealthChangeMetabolism is Mostly in medicine.yml and one in soad.yaml Why the hell is Cola metabolizable by plants? Who is pouring cola on their plants!?!? * Fix _prototypeManager being null errors. * Changing comments Where are the prototype references * MetabolismComponent doesn't give free heals anymore. * Changes HungerComponent healing. Previously I think it would actually damage you. Only did this as I though it was causing the fast healing. Turns out that was just BREATHING. * Generalised a function in DamageableComponent and moved it to DamageGroupPrototype previously DamageTypesDictToDamageGroupDict was private to DamageableComponent, but was also quite general (nearly a static function). As this sort of function may be needed by other components using DamageGroupPrototypes in the future, I moved it there as a static function instead. * modified DamageableComponent.ChangeDamage() ignoreResistances was renamed to ignoreDamageResistances to make it clearer that it had no effect on healing. Now uses default argument for ignoreDamageResistances, so when healing you are not forced to specify an argument that does nothing. Also made some general changes to ignoreResistances() * Changed class->group and added missing damage type functionality to DamageContainerPrototypes * Added Comments to damage.yml * Misc Changes to DamageableComponent * Differentiated between group support and group applicability So far, every damage type is a member of one, and only one, damage group. So this change has no real effect so far. * Added proposed alternative to ChangeDamage() * fixed error in DamageGroupPrototype * Changes to DamageableComponent Lots of changes to comments. Some variables renamed in IDamageableComponent and DamageableComponent (also required renaming in other files) Some minor logic changes, mostly for incorrect descirptions of boolean return values. Also further differentiating between ApplicableGroups and SupportedGroups... if that will ever even matter * Generalised MedicalScannerComponent If needed, can print miscellaneous damage types now * Fixed HealthChangeMetabolism bug * Changing Comments around * More questions * Made Barotrauma default to blunt * Fix RejuvenateTest.cs * Comments * Coments and variable names * fix some master-merge issues * Removed redundant fields * Misc changes for readbility of PR diff * Consistent naming * Fixed atmos damage bug * Removed Ranting * Fixed Hunger after I broke it * Fixing Bugs * Removed stupid question * Removed more stupid questions * Fix potential null errors. * Made boolean return values consistent Also renamed several functions, to make it clear they return a bool. Docs were also updated. * Removed IoCManager.InjectDependencies() * Removed unnecessary 'suffocation' prefix * Fixed Spelling Also removed accidentally left in logger call * Fixed Medical Scanner * Apply suggestions from code review Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> * Changing comments and whitespaces * Made damage thresholds trigger datafields required * So many typos * Changes to DamageableComponents Changed documentation in IDamageableComponent Made testing code more readable. Relabelled groups as 'Applicable' either 'Fully Supported' * Removed function and degeneralised * Update DamageableComponent.cs Removed unused parameters Fixed Networking * Added IoCManager.Resolve * Now using alternative TryChangeDamage() * Removed function from DamageGroupPrototype * Removing comments * Remove bad if statement? * Fix damageChanged ordering * Fix hurt server command * Changed //TODO PROTOTYPE blocks Now use PrototypeManager differently. Wherever possible, only retrieve the prototype once. Also added default damage types to some more datafields * Update Content.Shared/Damage/Container/DamageContainerPrototype.cs Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> * renamed _accumulatedHealth -> _accumulatedDamage and added TODOs * Another class-> group * Fix bug in generalisation of damage container prototypes * Addes Tests to make sure I dont keep adding bugs to my own code. * Changed Return values when setting * Removed unused class * Added more tests, split tests into three files * Made damage types public and VV read-write-able * Minor changes to DamageableComponent Replaced internal use of GetDamagePerType with _damageDict and removed some unnecessary fields * Fix Suicide, by adding IoC Resolve() * Fix DamageGroupTrigger bug * Fix typos in tests * Change comments./docstrings & spacing * Merge tests, use test prototypes Co-authored-by: Leon Friedrich <60421075+leonsfriedrich@users.noreply.github.com> Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> * Fix merge issues Co-authored-by: Silver <Silvertorch5@gmail.com> Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com> Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Co-authored-by: Leon Friedrich <60421075+leonsfriedrich@users.noreply.github.com>
This commit is contained in:
@@ -1,10 +1,13 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Collections.Generic;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
|
using System.Linq;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using static Content.Shared.MedicalScanner.SharedMedicalScannerComponent;
|
using static Content.Shared.MedicalScanner.SharedMedicalScannerComponent;
|
||||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||||
|
|
||||||
@@ -14,6 +17,7 @@ namespace Content.Client.MedicalScanner.UI
|
|||||||
{
|
{
|
||||||
public readonly Button ScanButton;
|
public readonly Button ScanButton;
|
||||||
private readonly Label _diagnostics;
|
private readonly Label _diagnostics;
|
||||||
|
|
||||||
public MedicalScannerWindow()
|
public MedicalScannerWindow()
|
||||||
{
|
{
|
||||||
SetSize = (250, 100);
|
SetSize = (250, 100);
|
||||||
@@ -51,27 +55,65 @@ namespace Content.Client.MedicalScanner.UI
|
|||||||
{
|
{
|
||||||
text.Append($"{Loc.GetString("medical-scanner-window-entity-health-text", ("entityName", entity.Name))}\n");
|
text.Append($"{Loc.GetString("medical-scanner-window-entity-health-text", ("entityName", entity.Name))}\n");
|
||||||
|
|
||||||
foreach (var (@class, classAmount) in state.DamageClasses)
|
// Show the total damage
|
||||||
|
var totalDamage = state.DamagePerTypeID.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<string> shownTypeIDs = 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)
|
||||||
{
|
{
|
||||||
text.Append($"\n{Loc.GetString("medical-scanner-window-damage-class-text", ("damageClass", @class), ("amount", classAmount))}");
|
|
||||||
|
|
||||||
foreach (var type in @class.ToTypes())
|
// 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.
|
||||||
|
var group = IoCManager.Resolve<IPrototypeManager>().Index<DamageGroupPrototype>(damageGroupID);
|
||||||
|
foreach (var type in group.DamageTypes)
|
||||||
{
|
{
|
||||||
if (!state.DamageTypes.TryGetValue(type, out var typeAmount))
|
if (state.DamagePerTypeID.TryGetValue(type.ID, out var typeAmount))
|
||||||
{
|
{
|
||||||
continue;
|
// 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))
|
||||||
|
{
|
||||||
|
shownTypeIDs.Add(type.ID);
|
||||||
|
text.Append($"\n- {Loc.GetString("medical-scanner-window-damage-type-text", ("damageType", type.ID), ("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-text", ("damageType",type) ,("amount", typeAmount))}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
text.Append('\n');
|
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();
|
_diagnostics.Text = text.ToString();
|
||||||
ScanButton.Disabled = state.IsScanned;
|
ScanButton.Disabled = state.IsScanned;
|
||||||
|
|
||||||
SetSize = (250, 575);
|
// TODO MEDICALSCANNER resize window based on the length of text / number of damage types?
|
||||||
|
// Also, maybe add color schemes for specific damage groups?
|
||||||
|
SetSize = (250, 600);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Damage;
|
using Content.Server.Damage;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
@@ -7,6 +7,7 @@ using NUnit.Framework;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Commands
|
namespace Content.IntegrationTests.Tests.Commands
|
||||||
{
|
{
|
||||||
@@ -20,7 +21,7 @@ namespace Content.IntegrationTests.Tests.Commands
|
|||||||
id: DamageableDummy
|
id: DamageableDummy
|
||||||
components:
|
components:
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damagePrototype: biologicalDamageContainer
|
damageContainer: biologicalDamageContainer
|
||||||
- type: MobState
|
- type: MobState
|
||||||
thresholds:
|
thresholds:
|
||||||
0: !type:NormalMobState {}
|
0: !type:NormalMobState {}
|
||||||
@@ -41,6 +42,7 @@ namespace Content.IntegrationTests.Tests.Commands
|
|||||||
mapManager.CreateNewMapEntity(MapId.Nullspace);
|
mapManager.CreateNewMapEntity(MapId.Nullspace);
|
||||||
|
|
||||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
|
|
||||||
var human = entityManager.SpawnEntity("DamageableDummy", MapCoordinates.Nullspace);
|
var human = entityManager.SpawnEntity("DamageableDummy", MapCoordinates.Nullspace);
|
||||||
|
|
||||||
@@ -53,7 +55,7 @@ namespace Content.IntegrationTests.Tests.Commands
|
|||||||
Assert.That(mobState.IsIncapacitated, Is.False);
|
Assert.That(mobState.IsIncapacitated, Is.False);
|
||||||
|
|
||||||
// Kill the entity
|
// Kill the entity
|
||||||
damageable.ChangeDamage(DamageClass.Brute, 10000000, true);
|
damageable.TryChangeDamage(prototypeManager.Index<DamageGroupPrototype>("Toxin"), 10000000, true);
|
||||||
|
|
||||||
// Check that it is dead
|
// Check that it is dead
|
||||||
Assert.That(mobState.IsAlive, Is.False);
|
Assert.That(mobState.IsAlive, Is.False);
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
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";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test a damageContainer with all types supported.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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.
|
||||||
|
/// </remarks>
|
||||||
|
[Test]
|
||||||
|
public async Task TestAllSupportDamageableComponent()
|
||||||
|
{
|
||||||
|
var server = StartServerDummyTicker();
|
||||||
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
|
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||||
|
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||||
|
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||||
|
|
||||||
|
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<IDamageableComponent>();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
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<DamageTypePrototype>().ToList().Count, Is.GreaterThan(0));
|
||||||
|
Assert.That(sPrototypeManager.EnumeratePrototypes<DamageGroupPrototype>().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<DamageTypePrototype>())
|
||||||
|
{
|
||||||
|
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<DamageGroupPrototype>())
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
using System;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Damageable
|
namespace Content.IntegrationTests.Tests.Damageable
|
||||||
{
|
{
|
||||||
@@ -12,18 +13,75 @@ namespace Content.IntegrationTests.Tests.Damageable
|
|||||||
[TestOf(typeof(DamageableComponent))]
|
[TestOf(typeof(DamageableComponent))]
|
||||||
public class DamageableTest : ContentIntegrationTest
|
public class DamageableTest : ContentIntegrationTest
|
||||||
{
|
{
|
||||||
private const string DamageableEntityId = "DamageableEntityId";
|
private const string DamageableEntityId = "TestDamageableEntityId";
|
||||||
|
private const string Group1Id = "TestGroup1";
|
||||||
|
private const string Group2Id = "TestGroup2";
|
||||||
|
private const string Group3Id = "TestGroup3";
|
||||||
|
private string Prototypes = $@"
|
||||||
|
# Define some damage groups
|
||||||
|
- type: damageType
|
||||||
|
id: TestDamage11
|
||||||
|
|
||||||
|
- type: damageType
|
||||||
|
id: TestDamage21
|
||||||
|
|
||||||
|
- type: damageType
|
||||||
|
id: TestDamage22
|
||||||
|
|
||||||
|
- type: damageType
|
||||||
|
id: TestDamage31
|
||||||
|
|
||||||
|
- type: damageType
|
||||||
|
id: TestDamage32
|
||||||
|
|
||||||
|
- type: damageType
|
||||||
|
id: TestDamage33
|
||||||
|
|
||||||
|
# Define damage Groups with 1,2,3 damage types
|
||||||
|
- type: damageGroup
|
||||||
|
id: {Group1Id}
|
||||||
|
damageTypes:
|
||||||
|
- TestDamage11
|
||||||
|
|
||||||
|
- type: damageGroup
|
||||||
|
id: {Group2Id}
|
||||||
|
damageTypes:
|
||||||
|
- TestDamage21
|
||||||
|
- TestDamage22
|
||||||
|
|
||||||
|
- type: damageGroup
|
||||||
|
id: {Group3Id}
|
||||||
|
damageTypes:
|
||||||
|
- TestDamage31
|
||||||
|
- TestDamage32
|
||||||
|
- TestDamage33
|
||||||
|
|
||||||
|
# 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: damageContainer
|
||||||
|
id: testSomeDamageContainer
|
||||||
|
supportedGroups:
|
||||||
|
- {Group3Id}
|
||||||
|
supportedTypes:
|
||||||
|
- TestDamage21
|
||||||
|
- TestDamage22
|
||||||
|
|
||||||
private static readonly string Prototypes = $@"
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: {DamageableEntityId}
|
id: {DamageableEntityId}
|
||||||
name: {DamageableEntityId}
|
name: {DamageableEntityId}
|
||||||
components:
|
components:
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: allDamageContainer";
|
damageContainer: testSomeDamageContainer
|
||||||
|
";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Test a standard damageable components
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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.
|
||||||
|
/// </remarks>
|
||||||
[Test]
|
[Test]
|
||||||
public async Task TestDamageTypeDamageAndHeal()
|
public async Task TestDamageableComponents()
|
||||||
{
|
{
|
||||||
var server = StartServerDummyTicker(new ServerContentIntegrationOption
|
var server = StartServerDummyTicker(new ServerContentIntegrationOption
|
||||||
{
|
{
|
||||||
@@ -34,10 +92,15 @@ namespace Content.IntegrationTests.Tests.Damageable
|
|||||||
|
|
||||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||||
|
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||||
|
|
||||||
IEntity sDamageableEntity;
|
IEntity sDamageableEntity;
|
||||||
IDamageableComponent sDamageableComponent = null;
|
IDamageableComponent sDamageableComponent = null;
|
||||||
|
|
||||||
|
DamageGroupPrototype group1 = default!;
|
||||||
|
DamageGroupPrototype group2 = default!;
|
||||||
|
DamageGroupPrototype group3 = default!;
|
||||||
|
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
var mapId = sMapManager.NextMapId();
|
var mapId = sMapManager.NextMapId();
|
||||||
@@ -46,123 +109,280 @@ namespace Content.IntegrationTests.Tests.Damageable
|
|||||||
|
|
||||||
sDamageableEntity = sEntityManager.SpawnEntity(DamageableEntityId, coordinates);
|
sDamageableEntity = sEntityManager.SpawnEntity(DamageableEntityId, coordinates);
|
||||||
sDamageableComponent = sDamageableEntity.GetComponent<IDamageableComponent>();
|
sDamageableComponent = sDamageableEntity.GetComponent<IDamageableComponent>();
|
||||||
|
|
||||||
|
group1 = sPrototypeManager.Index<DamageGroupPrototype>(Group1Id);
|
||||||
|
group2 = sPrototypeManager.Index<DamageGroupPrototype>(Group2Id);
|
||||||
|
group3 = sPrototypeManager.Index<DamageGroupPrototype>(Group3Id);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.WaitRunTicks(5);
|
await server.WaitRunTicks(5);
|
||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
// 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 damageToDeal = 7;
|
// Check that the correct types are supported:
|
||||||
|
foreach (var group in sPrototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
||||||
foreach (var type in Enum.GetValues<DamageType>())
|
|
||||||
{
|
{
|
||||||
Assert.That(sDamageableComponent.SupportsDamageType(type));
|
foreach(var type in group.DamageTypes)
|
||||||
|
{
|
||||||
// Damage
|
if (sDamageableComponent.IsFullySupportedDamageGroup(group))
|
||||||
Assert.That(sDamageableComponent.ChangeDamage(type, damageToDeal, true), Is.True);
|
{
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal));
|
Assert.That(sDamageableComponent.IsSupportedDamageType(type), Is.True);
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(type, out var damage), Is.True);
|
}
|
||||||
Assert.That(damage, Is.EqualTo(damageToDeal));
|
else
|
||||||
|
{
|
||||||
// Heal
|
Assert.That(sDamageableComponent.IsSupportedDamageType(type), Is.False);
|
||||||
Assert.That(sDamageableComponent.ChangeDamage(type, -damageToDeal, true), Is.True);
|
}
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.Zero);
|
}
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(type, out damage), Is.True);
|
|
||||||
Assert.That(damage, Is.Zero);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public async Task TestDamageClassDamageAndHeal()
|
|
||||||
{
|
|
||||||
var server = StartServerDummyTicker(new ServerContentIntegrationOption
|
|
||||||
{
|
|
||||||
ExtraPrototypes = Prototypes
|
|
||||||
});
|
|
||||||
|
|
||||||
await server.WaitIdleAsync();
|
Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group1), Is.False);
|
||||||
|
Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group2), Is.True);
|
||||||
|
Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group3), Is.True);
|
||||||
|
|
||||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
// Check that damage works properly if perfectly divisible among group members
|
||||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
int damageToDeal, groupDamage, typeDamage; ;
|
||||||
|
|
||||||
IEntity sDamageableEntity;
|
|
||||||
IDamageableComponent sDamageableComponent = null;
|
|
||||||
|
|
||||||
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<IDamageableComponent>();
|
|
||||||
});
|
|
||||||
|
|
||||||
await server.WaitRunTicks(5);
|
|
||||||
|
|
||||||
await server.WaitAssertion(() =>
|
|
||||||
{
|
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
||||||
|
foreach (var damageGroup in sDamageableComponent.FullySupportedDamageGroups)
|
||||||
foreach (var @class in Enum.GetValues<DamageClass>())
|
|
||||||
{
|
{
|
||||||
Assert.That(sDamageableComponent.SupportsDamageClass(@class));
|
var types = damageGroup.DamageTypes;
|
||||||
|
|
||||||
var types = @class.ToTypes();
|
|
||||||
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
Assert.That(sDamageableComponent.SupportsDamageType(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
var damageToDeal = types.Count * 5;
|
|
||||||
|
|
||||||
// Damage
|
// Damage
|
||||||
Assert.That(sDamageableComponent.ChangeDamage(@class, damageToDeal, true), Is.True);
|
damageToDeal = types.Count() * 5;
|
||||||
|
Assert.That(sDamageableComponent.TryChangeDamage(damageGroup, damageToDeal, true), Is.True);
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal));
|
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal));
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(@class, out var classDamage), Is.True);
|
Assert.That(sDamageableComponent.TryGetDamage(damageGroup, out groupDamage), Is.True);
|
||||||
Assert.That(classDamage, Is.EqualTo(damageToDeal));
|
Assert.That(groupDamage, Is.EqualTo(damageToDeal));
|
||||||
|
|
||||||
foreach (var type in types)
|
foreach (var type in types)
|
||||||
{
|
{
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(type, out var typeDamage), Is.True);
|
Assert.That(sDamageableComponent.TryGetDamage(type, out typeDamage), Is.True);
|
||||||
Assert.That(typeDamage, Is.EqualTo(damageToDeal / types.Count));
|
Assert.That(typeDamage, Is.EqualTo(damageToDeal / types.Count()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Heal
|
// Heal
|
||||||
Assert.That(sDamageableComponent.ChangeDamage(@class, -damageToDeal, true), Is.True);
|
Assert.That(sDamageableComponent.TryChangeDamage(damageGroup, -damageToDeal, true), Is.True);
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.Zero);
|
Assert.That(sDamageableComponent.TotalDamage, Is.Zero);
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(@class, out classDamage), Is.True);
|
Assert.That(sDamageableComponent.TryGetDamage(damageGroup, out groupDamage), Is.True);
|
||||||
Assert.That(classDamage, Is.Zero);
|
Assert.That(groupDamage, Is.Zero);
|
||||||
|
|
||||||
foreach (var type in types)
|
foreach (var type in types)
|
||||||
{
|
{
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(type, out var typeDamage), Is.True);
|
Assert.That(sDamageableComponent.TryGetDamage(type, out typeDamage), Is.True);
|
||||||
Assert.That(typeDamage, Is.Zero);
|
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)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<DamageGroupPrototype>())
|
||||||
|
{
|
||||||
|
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?
|
||||||
|
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));
|
||||||
|
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
||||||
|
|
||||||
|
// Test preferential healing
|
||||||
|
damageToDeal = 12;
|
||||||
|
var damageTypes = group3.DamageTypes.ToArray();
|
||||||
|
|
||||||
|
// 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
|
||||||
|
";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generalized damageable component tests.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Test scenarios where damage types are members of more than one group, or where a component only supports a subset of a group.
|
||||||
|
/// </remarks>
|
||||||
[Test]
|
[Test]
|
||||||
public async Task TotalDamageTest()
|
public async Task TestGeneralizedDamageableComponent()
|
||||||
{
|
{
|
||||||
var server = StartServerDummyTicker(new ServerContentIntegrationOption
|
var server = StartServerDummyTicker(new ServerContentIntegrationOption
|
||||||
{
|
{
|
||||||
ExtraPrototypes = Prototypes
|
ExtraPrototypes = Prototypes2
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.WaitIdleAsync();
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||||
|
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||||
|
|
||||||
IEntity sDamageableEntity;
|
IEntity sDamageableEntity;
|
||||||
IDamageableComponent sDamageableComponent = null;
|
IDamageableComponent sDamageableComponent = null;
|
||||||
|
|
||||||
|
DamageGroupPrototype group1 = default!;
|
||||||
|
DamageGroupPrototype group2 = default!;
|
||||||
|
DamageGroupPrototype group3 = default!;
|
||||||
|
|
||||||
|
DamageTypePrototype SharedDamageType = default!;
|
||||||
|
DamageTypePrototype UnsupportedDamageType = default!;
|
||||||
|
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
var mapId = sMapManager.NextMapId();
|
var mapId = sMapManager.NextMapId();
|
||||||
@@ -171,27 +391,62 @@ namespace Content.IntegrationTests.Tests.Damageable
|
|||||||
|
|
||||||
sDamageableEntity = sEntityManager.SpawnEntity(DamageableEntityId, coordinates);
|
sDamageableEntity = sEntityManager.SpawnEntity(DamageableEntityId, coordinates);
|
||||||
sDamageableComponent = sDamageableEntity.GetComponent<IDamageableComponent>();
|
sDamageableComponent = sDamageableEntity.GetComponent<IDamageableComponent>();
|
||||||
|
|
||||||
|
group1 = sPrototypeManager.Index<DamageGroupPrototype>(Group1Id);
|
||||||
|
group2 = sPrototypeManager.Index<DamageGroupPrototype>(Group2Id);
|
||||||
|
group3 = sPrototypeManager.Index<DamageGroupPrototype>(Group3Id);
|
||||||
|
|
||||||
|
SharedDamageType = sPrototypeManager.Index<DamageTypePrototype>(SharedDamageTypeId);
|
||||||
|
UnsupportedDamageType = sPrototypeManager.Index<DamageTypePrototype>(UnsupportedDamageTypeId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await server.WaitRunTicks(5);
|
||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
var damageType = DamageClass.Brute;
|
// All damage types should be applicable
|
||||||
var damage = 10;
|
Assert.That(sDamageableComponent.IsApplicableDamageGroup(group1), Is.True);
|
||||||
|
Assert.That(sDamageableComponent.IsApplicableDamageGroup(group2), Is.True);
|
||||||
|
Assert.That(sDamageableComponent.IsApplicableDamageGroup(group3), Is.True);
|
||||||
|
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, damage, true));
|
// But not all should be fully supported
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(10));
|
Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group1), Is.True);
|
||||||
|
Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group2), Is.True);
|
||||||
|
Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group3), Is.False);
|
||||||
|
|
||||||
var totalTypeDamage = 0;
|
// Check that the correct damage types are supported
|
||||||
|
Assert.That(sDamageableComponent.IsSupportedDamageType(SharedDamageType), Is.True);
|
||||||
|
|
||||||
foreach (var type in damageType.ToTypes())
|
// Check that if we deal damage using a type appearing in multiple groups, nothing goes wrong.
|
||||||
{
|
var damage = 12;
|
||||||
Assert.True(sDamageableComponent.TryGetDamage(type, out var typeDamage));
|
Assert.That(sDamageableComponent.TryChangeDamage(SharedDamageType, damage), Is.True);
|
||||||
Assert.That(typeDamage, Is.LessThanOrEqualTo(damage));
|
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));
|
||||||
|
|
||||||
totalTypeDamage += typeDamage;
|
// 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));
|
||||||
|
|
||||||
Assert.That(totalTypeDamage, Is.EqualTo(damage));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Destructible.Thresholds.Triggers;
|
using Content.Server.Destructible.Thresholds.Triggers;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
@@ -6,14 +6,15 @@ using NUnit.Framework;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Destructible
|
namespace Content.IntegrationTests.Tests.Destructible
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
[TestOf(typeof(DamageClassTrigger))]
|
[TestOf(typeof(DamageGroupTrigger))]
|
||||||
[TestOf(typeof(AndTrigger))]
|
[TestOf(typeof(AndTrigger))]
|
||||||
public class DestructibleDamageClassTest : ContentIntegrationTest
|
public class DestructibleDamageGroupTest : ContentIntegrationTest
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public async Task AndTest()
|
public async Task AndTest()
|
||||||
@@ -31,6 +32,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||||
|
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||||
|
|
||||||
IEntity sDestructibleEntity;
|
IEntity sDestructibleEntity;
|
||||||
IDamageableComponent sDamageableComponent = null;
|
IDamageableComponent sDamageableComponent = null;
|
||||||
@@ -42,7 +44,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
var coordinates = new MapCoordinates(0, 0, mapId);
|
var coordinates = new MapCoordinates(0, 0, mapId);
|
||||||
sMapManager.CreateMap(mapId);
|
sMapManager.CreateMap(mapId);
|
||||||
|
|
||||||
sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDamageClassEntityId, coordinates);
|
sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDamageGroupEntityId, coordinates);
|
||||||
sDamageableComponent = sDestructibleEntity.GetComponent<IDamageableComponent>();
|
sDamageableComponent = sDestructibleEntity.GetComponent<IDamageableComponent>();
|
||||||
sThresholdListenerComponent = sDestructibleEntity.GetComponent<TestThresholdListenerComponent>();
|
sThresholdListenerComponent = sDestructibleEntity.GetComponent<TestThresholdListenerComponent>();
|
||||||
});
|
});
|
||||||
@@ -56,20 +58,23 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
|
var bruteDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>("TestBrute");
|
||||||
|
var burnDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>("TestBurn");
|
||||||
|
|
||||||
// Raise brute damage to 5
|
// Raise brute damage to 5
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 5, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 5, true));
|
||||||
|
|
||||||
// No thresholds reached yet, the earliest one is at 10 damage
|
// No thresholds reached yet, the earliest one is at 10 damage
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise brute damage to 10
|
// Raise brute damage to 10
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 5, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 5, true));
|
||||||
|
|
||||||
// No threshold reached, burn needs to be 10 as well
|
// No threshold reached, burn needs to be 10 as well
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise burn damage to 10
|
// Raise burn damage to 10
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, 10, true));
|
||||||
|
|
||||||
// One threshold reached, brute 10 + burn 10
|
// One threshold reached, brute 10 + burn 10
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -86,52 +91,52 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
var trigger = (AndTrigger) threshold.Trigger;
|
var trigger = (AndTrigger) threshold.Trigger;
|
||||||
|
|
||||||
Assert.IsInstanceOf<DamageClassTrigger>(trigger.Triggers[0]);
|
Assert.IsInstanceOf<DamageGroupTrigger>(trigger.Triggers[0]);
|
||||||
Assert.IsInstanceOf<DamageClassTrigger>(trigger.Triggers[1]);
|
Assert.IsInstanceOf<DamageGroupTrigger>(trigger.Triggers[1]);
|
||||||
|
|
||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Raise brute damage to 20
|
// Raise brute damage to 20
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise burn damage to 20
|
// Raise burn damage to 20
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Lower brute damage to 0
|
// Lower brute damage to 0
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, -20, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, -20, true));
|
||||||
|
|
||||||
// No new thresholds reached, healing should not trigger it
|
// No new thresholds reached, healing should not trigger it
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise brute damage back up to 10
|
// Raise brute damage back up to 10
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 10, true));
|
||||||
|
|
||||||
// 10 brute + 10 burn threshold reached, brute was healed and brought back to its threshold amount and slash stayed the same
|
// 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));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
|
|
||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Heal both classes of damage to 0
|
// Heal both classes of damage to 0
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, -10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, -10, true));
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, -20, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, -20, true));
|
||||||
|
|
||||||
// No new thresholds reached, healing should not trigger it
|
// No new thresholds reached, healing should not trigger it
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise brute damage to 10
|
// Raise brute damage to 10
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise burn damage to 10
|
// Raise burn damage to 10
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, 10, true));
|
||||||
|
|
||||||
// Both classes of damage were healed and then raised again, the threshold should have been reached as triggers once is default false
|
// 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(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -148,8 +153,8 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
trigger = (AndTrigger) threshold.Trigger;
|
trigger = (AndTrigger) threshold.Trigger;
|
||||||
|
|
||||||
Assert.IsInstanceOf<DamageClassTrigger>(trigger.Triggers[0]);
|
Assert.IsInstanceOf<DamageGroupTrigger>(trigger.Triggers[0]);
|
||||||
Assert.IsInstanceOf<DamageClassTrigger>(trigger.Triggers[1]);
|
Assert.IsInstanceOf<DamageGroupTrigger>(trigger.Triggers[1]);
|
||||||
|
|
||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
@@ -157,20 +162,20 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
threshold.TriggersOnce = true;
|
threshold.TriggersOnce = true;
|
||||||
|
|
||||||
// Heal brute and burn back to 0
|
// Heal brute and burn back to 0
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, -10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, -10, true));
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, -10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, -10, true));
|
||||||
|
|
||||||
// No new thresholds reached from healing
|
// No new thresholds reached from healing
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise brute damage to 10
|
// Raise brute damage to 10
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise burn damage to 10
|
// Raise burn damage to 10
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached as triggers once is set to true and it already triggered before
|
// No new thresholds reached as triggers once is set to true and it already triggered before
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Destructible.Thresholds.Triggers;
|
using Content.Server.Destructible.Thresholds.Triggers;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
@@ -6,6 +6,7 @@ using NUnit.Framework;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Destructible
|
namespace Content.IntegrationTests.Tests.Destructible
|
||||||
@@ -56,20 +57,23 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
|
var bluntDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("TestBlunt");
|
||||||
|
var slashDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("TestSlash");
|
||||||
|
|
||||||
// Raise blunt damage to 5
|
// Raise blunt damage to 5
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 5, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 5, true));
|
||||||
|
|
||||||
// No thresholds reached yet, the earliest one is at 10 damage
|
// No thresholds reached yet, the earliest one is at 10 damage
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise blunt damage to 10
|
// Raise blunt damage to 10
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 5, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 5, true));
|
||||||
|
|
||||||
// No threshold reached, slash needs to be 10 as well
|
// No threshold reached, slash needs to be 10 as well
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise slash damage to 10
|
// Raise slash damage to 10
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, 10, true));
|
||||||
|
|
||||||
// One threshold reached, blunt 10 + slash 10
|
// One threshold reached, blunt 10 + slash 10
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -92,25 +96,25 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Raise blunt damage to 20
|
// Raise blunt damage to 20
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise slash damage to 20
|
// Raise slash damage to 20
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Lower blunt damage to 0
|
// Lower blunt damage to 0
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, -20, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, -20, true));
|
||||||
|
|
||||||
// No new thresholds reached, healing should not trigger it
|
// No new thresholds reached, healing should not trigger it
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise blunt damage back up to 10
|
// Raise blunt damage back up to 10
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true));
|
||||||
|
|
||||||
// 10 blunt + 10 slash threshold reached, blunt was healed and brought back to its threshold amount and slash stayed the same
|
// 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(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -118,20 +122,20 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Heal both types of damage to 0
|
// Heal both types of damage to 0
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, -10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, -10, true));
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, -20, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, -20, true));
|
||||||
|
|
||||||
// No new thresholds reached, healing should not trigger it
|
// No new thresholds reached, healing should not trigger it
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise blunt damage to 10
|
// Raise blunt damage to 10
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise slash damage to 10
|
// Raise slash damage to 10
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, 10, true));
|
||||||
|
|
||||||
// Both types of damage were healed and then raised again, the threshold should have been reached as triggers once is default false
|
// 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(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -157,20 +161,20 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
threshold.TriggersOnce = true;
|
threshold.TriggersOnce = true;
|
||||||
|
|
||||||
// Heal blunt and slash back to 0
|
// Heal blunt and slash back to 0
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, -10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, -10, true));
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, -10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, -10, true));
|
||||||
|
|
||||||
// No new thresholds reached from healing
|
// No new thresholds reached from healing
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise blunt damage to 10
|
// Raise blunt damage to 10
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise slash damage to 10
|
// Raise slash damage to 10
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached as triggers once is set to true and it already triggered before
|
// No new thresholds reached as triggers once is set to true and it already triggered before
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Destructible.Thresholds;
|
using Content.Server.Destructible.Thresholds;
|
||||||
using Content.Server.Destructible.Thresholds.Behaviors;
|
using Content.Server.Destructible.Thresholds.Behaviors;
|
||||||
@@ -8,6 +8,7 @@ using NUnit.Framework;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Destructible
|
namespace Content.IntegrationTests.Tests.Destructible
|
||||||
@@ -30,6 +31,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||||
|
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||||
|
|
||||||
IEntity sDestructibleEntity = null;
|
IEntity sDestructibleEntity = null;
|
||||||
IDamageableComponent sDamageableComponent = null;
|
IDamageableComponent sDamageableComponent = null;
|
||||||
@@ -49,10 +51,11 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
var coordinates = sDestructibleEntity.Transform.Coordinates;
|
var coordinates = sDestructibleEntity.Transform.Coordinates;
|
||||||
|
var bruteDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>("TestBrute");
|
||||||
|
|
||||||
Assert.DoesNotThrow(() =>
|
Assert.DoesNotThrow(() =>
|
||||||
{
|
{
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 50, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 50, true));
|
||||||
});
|
});
|
||||||
|
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
|
|||||||
@@ -6,9 +6,93 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
public const string DestructibleEntityId = "DestructibleTestsDestructibleEntity";
|
public const string DestructibleEntityId = "DestructibleTestsDestructibleEntity";
|
||||||
public const string DestructibleDestructionEntityId = "DestructibleTestsDestructibleDestructionEntity";
|
public const string DestructibleDestructionEntityId = "DestructibleTestsDestructibleDestructionEntity";
|
||||||
public const string DestructibleDamageTypeEntityId = "DestructibleTestsDestructibleDamageTypeEntity";
|
public const string DestructibleDamageTypeEntityId = "DestructibleTestsDestructibleDamageTypeEntity";
|
||||||
public const string DestructibleDamageClassEntityId = "DestructibleTestsDestructibleDamageClassEntity";
|
public const string DestructibleDamageGroupEntityId = "DestructibleTestsDestructibleDamageGroupEntity";
|
||||||
|
|
||||||
public static readonly string Prototypes = $@"
|
public static readonly string Prototypes = $@"
|
||||||
|
- type: damageType
|
||||||
|
id: TestBlunt
|
||||||
|
|
||||||
|
- type: damageType
|
||||||
|
id: TestSlash
|
||||||
|
|
||||||
|
- type: damageType
|
||||||
|
id: TestPiercing
|
||||||
|
|
||||||
|
- type: damageType
|
||||||
|
id: TestHeat
|
||||||
|
|
||||||
|
- type: damageType
|
||||||
|
id: TestShock
|
||||||
|
|
||||||
|
- 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:
|
||||||
|
- TestBlunt
|
||||||
|
- TestSlash
|
||||||
|
- TestPiercing
|
||||||
|
|
||||||
|
- type: damageGroup
|
||||||
|
id: TestBurn
|
||||||
|
damageTypes:
|
||||||
|
- TestHeat
|
||||||
|
- 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
|
- type: entity
|
||||||
id: {SpawnedEntityId}
|
id: {SpawnedEntityId}
|
||||||
name: {SpawnedEntityId}
|
name: {SpawnedEntityId}
|
||||||
@@ -18,6 +102,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
name: {DestructibleEntityId}
|
name: {DestructibleEntityId}
|
||||||
components:
|
components:
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
|
damageContainer: TestMetallicDamageContainer
|
||||||
- type: Destructible
|
- type: Destructible
|
||||||
thresholds:
|
thresholds:
|
||||||
- trigger:
|
- trigger:
|
||||||
@@ -46,6 +131,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
name: {DestructibleDestructionEntityId}
|
name: {DestructibleDestructionEntityId}
|
||||||
components:
|
components:
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
|
damageContainer: TestMetallicDamageContainer
|
||||||
- type: Destructible
|
- type: Destructible
|
||||||
thresholds:
|
thresholds:
|
||||||
- trigger:
|
- trigger:
|
||||||
@@ -69,34 +155,36 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
name: {DestructibleDamageTypeEntityId}
|
name: {DestructibleDamageTypeEntityId}
|
||||||
components:
|
components:
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
|
damageContainer: TestMetallicDamageContainer
|
||||||
- type: Destructible
|
- type: Destructible
|
||||||
thresholds:
|
thresholds:
|
||||||
- trigger:
|
- trigger:
|
||||||
!type:AndTrigger
|
!type:AndTrigger
|
||||||
triggers:
|
triggers:
|
||||||
- !type:DamageTypeTrigger
|
- !type:DamageTypeTrigger
|
||||||
type: Blunt
|
damageType: TestBlunt
|
||||||
damage: 10
|
damage: 10
|
||||||
- !type:DamageTypeTrigger
|
- !type:DamageTypeTrigger
|
||||||
type: Slash
|
damageType: TestSlash
|
||||||
damage: 10
|
damage: 10
|
||||||
- type: TestThresholdListener
|
- type: TestThresholdListener
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: {DestructibleDamageClassEntityId}
|
id: {DestructibleDamageGroupEntityId}
|
||||||
name: {DestructibleDamageClassEntityId}
|
name: {DestructibleDamageGroupEntityId}
|
||||||
components:
|
components:
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
|
damageContainer: TestMetallicDamageContainer
|
||||||
- type: Destructible
|
- type: Destructible
|
||||||
thresholds:
|
thresholds:
|
||||||
- trigger:
|
- trigger:
|
||||||
!type:AndTrigger
|
!type:AndTrigger
|
||||||
triggers:
|
triggers:
|
||||||
- !type:DamageClassTrigger
|
- !type:DamageGroupTrigger
|
||||||
class: Brute
|
damageGroup: TestBrute
|
||||||
damage: 10
|
damage: 10
|
||||||
- !type:DamageClassTrigger
|
- !type:DamageGroupTrigger
|
||||||
class: Burn
|
damageGroup: TestBurn
|
||||||
damage: 10
|
damage: 10
|
||||||
- type: TestThresholdListener";
|
- type: TestThresholdListener";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using NUnit.Framework;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Destructible
|
namespace Content.IntegrationTests.Tests.Destructible
|
||||||
@@ -35,6 +36,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||||
|
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
|
||||||
|
|
||||||
IEntity sDestructibleEntity;
|
IEntity sDestructibleEntity;
|
||||||
IDamageableComponent sDamageableComponent = null;
|
IDamageableComponent sDamageableComponent = null;
|
||||||
@@ -62,12 +64,14 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true));
|
var bluntDamageType = sPrototypeManager.Index<DamageTypePrototype>("TestBlunt");
|
||||||
|
|
||||||
|
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true));
|
||||||
|
|
||||||
// No thresholds reached yet, the earliest one is at 20 damage
|
// No thresholds reached yet, the earliest one is at 20 damage
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true));
|
||||||
|
|
||||||
// Only one threshold reached, 20
|
// Only one threshold reached, 20
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -83,7 +87,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 30, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 30, true));
|
||||||
|
|
||||||
// One threshold reached, 50, since 20 already triggered before and it has not been healed below that amount
|
// 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(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -112,16 +116,16 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Damage for 50 again, up to 100 now
|
// Damage for 50 again, up to 100 now
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 50, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 50, true));
|
||||||
|
|
||||||
// No thresholds reached as they weren't healed below the trigger amount
|
// No thresholds reached as they weren't healed below the trigger amount
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Heal down to 0
|
// Set damage to 0
|
||||||
sDamageableComponent.Heal();
|
sDamageableComponent.TrySetAllDamage(0);
|
||||||
|
|
||||||
// Damage for 100, up to 100
|
// Damage for 100, up to 100
|
||||||
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 100, true));
|
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 100, true));
|
||||||
|
|
||||||
// Two thresholds reached as damage increased past the previous, 20 and 50
|
// Two thresholds reached as damage increased past the previous, 20 and 50
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(2));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(2));
|
||||||
@@ -129,25 +133,25 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Heal the entity for 40 damage, down to 60
|
// Heal the entity for 40 damage, down to 60
|
||||||
sDamageableComponent.ChangeDamage(DamageType.Blunt, -40, true);
|
sDamageableComponent.TryChangeDamage(bluntDamageType, -40, true);
|
||||||
|
|
||||||
// Thresholds don't work backwards
|
// Thresholds don't work backwards
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty);
|
Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty);
|
||||||
|
|
||||||
// Damage for 10, up to 70
|
// Damage for 10, up to 70
|
||||||
sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true);
|
sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true);
|
||||||
|
|
||||||
// Not enough healing to de-trigger a threshold
|
// Not enough healing to de-trigger a threshold
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty);
|
Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty);
|
||||||
|
|
||||||
// Heal by 30, down to 40
|
// Heal by 30, down to 40
|
||||||
sDamageableComponent.ChangeDamage(DamageType.Blunt, -30, true);
|
sDamageableComponent.TryChangeDamage(bluntDamageType, -30, true);
|
||||||
|
|
||||||
// Thresholds don't work backwards
|
// Thresholds don't work backwards
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty);
|
Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty);
|
||||||
|
|
||||||
// Damage up to 50 again
|
// Damage up to 50 again
|
||||||
sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true);
|
sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true);
|
||||||
|
|
||||||
// The 50 threshold should have triggered again, after being healed
|
// The 50 threshold should have triggered again, after being healed
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -177,10 +181,10 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Heal all damage
|
// Heal all damage
|
||||||
sDamageableComponent.Heal();
|
sDamageableComponent.TrySetAllDamage(0);
|
||||||
|
|
||||||
// Damage up to 50
|
// Damage up to 50
|
||||||
sDamageableComponent.ChangeDamage(DamageType.Blunt, 50, true);
|
sDamageableComponent.TryChangeDamage(bluntDamageType, 50, true);
|
||||||
|
|
||||||
// Check that the total damage matches
|
// Check that the total damage matches
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50));
|
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50));
|
||||||
@@ -228,7 +232,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Heal the entity completely
|
// Heal the entity completely
|
||||||
sDamageableComponent.Heal();
|
sDamageableComponent.TrySetAllDamage(0);
|
||||||
|
|
||||||
// Check that the entity has 0 damage
|
// Check that the entity has 0 damage
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
||||||
@@ -241,7 +245,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Damage the entity up to 50 damage again
|
// Damage the entity up to 50 damage again
|
||||||
sDamageableComponent.ChangeDamage(DamageType.Blunt, 50, true);
|
sDamageableComponent.TryChangeDamage(bluntDamageType, 50, true);
|
||||||
|
|
||||||
// Check that the total damage matches
|
// Check that the total damage matches
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50));
|
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50));
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Content.Server.Alert;
|
using Content.Server.Alert;
|
||||||
using Content.Server.Pressure;
|
using Content.Server.Pressure;
|
||||||
@@ -7,6 +7,10 @@ using Content.Shared.Atmos;
|
|||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
using Robust.Shared.GameObjects;
|
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
|
namespace Content.Server.Atmos.Components
|
||||||
{
|
{
|
||||||
@@ -18,6 +22,17 @@ namespace Content.Server.Atmos.Components
|
|||||||
{
|
{
|
||||||
public override string Name => "Barotrauma";
|
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";
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public DamageTypePrototype DamageType = default!;
|
||||||
|
protected override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Update(float airPressure)
|
public void Update(float airPressure)
|
||||||
{
|
{
|
||||||
@@ -40,11 +55,11 @@ namespace Content.Server.Atmos.Components
|
|||||||
// Low pressure.
|
// Low pressure.
|
||||||
case var p when p <= Atmospherics.WarningLowPressure:
|
case var p when p <= Atmospherics.WarningLowPressure:
|
||||||
pressure *= lowPressureMultiplier;
|
pressure *= lowPressureMultiplier;
|
||||||
|
if (pressure > Atmospherics.WarningLowPressure)
|
||||||
if(pressure > Atmospherics.WarningLowPressure)
|
|
||||||
goto default;
|
goto default;
|
||||||
|
|
||||||
damageable.ChangeDamage(DamageType.Blunt, Atmospherics.LowPressureDamage, false, Owner);
|
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
|
||||||
|
damageable.TryChangeDamage(DamageType, Atmospherics.LowPressureDamage,true);
|
||||||
|
|
||||||
if (status == null) break;
|
if (status == null) break;
|
||||||
|
|
||||||
@@ -66,7 +81,8 @@ namespace Content.Server.Atmos.Components
|
|||||||
|
|
||||||
var damage = (int) MathF.Min((pressure / Atmospherics.HazardHighPressure) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage);
|
var damage = (int) MathF.Min((pressure / Atmospherics.HazardHighPressure) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage);
|
||||||
|
|
||||||
damageable.ChangeDamage(DamageType.Blunt, damage, false, Owner);
|
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
|
||||||
|
damageable.TryChangeDamage(DamageType, damage,true);
|
||||||
|
|
||||||
if (status == null) break;
|
if (status == null) break;
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ using Robust.Shared.Localization;
|
|||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.Components
|
namespace Content.Server.Atmos.Components
|
||||||
{
|
{
|
||||||
@@ -43,6 +45,18 @@ namespace Content.Server.Atmos.Components
|
|||||||
[DataField("canResistFire")]
|
[DataField("canResistFire")]
|
||||||
public bool CanResistFire { get; private set; } = false;
|
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"!;
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public DamageTypePrototype DamageType = default!;
|
||||||
|
protected override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||||
|
}
|
||||||
|
|
||||||
public void Extinguish()
|
public void Extinguish()
|
||||||
{
|
{
|
||||||
if (!OnFire) return;
|
if (!OnFire) return;
|
||||||
@@ -92,7 +106,7 @@ namespace Content.Server.Atmos.Components
|
|||||||
{
|
{
|
||||||
// TODO ATMOS Fire resistance from armor
|
// TODO ATMOS Fire resistance from armor
|
||||||
var damage = Math.Min((int) (FireStacks * 2.5f), 10);
|
var damage = Math.Min((int) (FireStacks * 2.5f), 10);
|
||||||
damageable.ChangeDamage(DamageClass.Burn, damage, false);
|
damageable.TryChangeDamage(DamageType, damage, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
AdjustFireStacks(-0.1f * (_resisting ? 10f : 1f));
|
AdjustFireStacks(-0.1f * (_resisting ? 10f : 1f));
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Shared.Body.Part;
|
using Content.Shared.Body.Part;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
|
|
||||||
namespace Content.Server.Body
|
namespace Content.Server.Body
|
||||||
@@ -10,7 +10,7 @@ namespace Content.Server.Body
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO BODY: Remove and pretend it never existed
|
// TODO BODY: Remove and pretend it never existed
|
||||||
public class BodyDamageChangeParams : DamageChangeParams, IBodyHealthChangeParams
|
public class BodyDamageChangeParams : IBodyHealthChangeParams
|
||||||
{
|
{
|
||||||
public BodyDamageChangeParams(BodyPartType part)
|
public BodyDamageChangeParams(BodyPartType part)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ using Content.Shared.Atmos;
|
|||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
using Content.Shared.MobState;
|
using Content.Shared.MobState;
|
||||||
using Content.Shared.Notification.Managers;
|
using Content.Shared.Notification.Managers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
@@ -34,10 +36,6 @@ namespace Content.Server.Body.Respiratory
|
|||||||
private bool _isShivering;
|
private bool _isShivering;
|
||||||
private bool _isSweating;
|
private bool _isSweating;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamage")] private int _suffocationDamage = 1;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamageRecovery")] private int _suffocationDamageRecovery = 1;
|
|
||||||
|
|
||||||
[ViewVariables] [DataField("needsGases")] public Dictionary<Gas, float> NeedsGases { get; set; } = new();
|
[ViewVariables] [DataField("needsGases")] public Dictionary<Gas, float> NeedsGases { get; set; } = new();
|
||||||
|
|
||||||
[ViewVariables] [DataField("producesGases")] public Dictionary<Gas, float> ProducesGases { get; set; } = new();
|
[ViewVariables] [DataField("producesGases")] public Dictionary<Gas, float> ProducesGases { get; set; } = new();
|
||||||
@@ -94,6 +92,22 @@ namespace Content.Server.Body.Respiratory
|
|||||||
|
|
||||||
[ViewVariables] public bool Suffocating { get; private set; }
|
[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"!;
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public DamageTypePrototype DamageType = default!;
|
||||||
|
protected override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||||
|
}
|
||||||
|
|
||||||
private Dictionary<Gas, float> NeedsAndDeficit(float frameTime)
|
private Dictionary<Gas, float> NeedsAndDeficit(float frameTime)
|
||||||
{
|
{
|
||||||
var needs = new Dictionary<Gas, float>(NeedsGases);
|
var needs = new Dictionary<Gas, float>(NeedsGases);
|
||||||
@@ -349,7 +363,7 @@ namespace Content.Server.Body.Respiratory
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
damageable.ChangeDamage(DamageType.Asphyxiation, _suffocationDamage, false);
|
damageable.TryChangeDamage(DamageType, _damage, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StopSuffocation()
|
private void StopSuffocation()
|
||||||
@@ -358,7 +372,7 @@ namespace Content.Server.Body.Respiratory
|
|||||||
|
|
||||||
if (Owner.TryGetComponent(out IDamageableComponent? damageable))
|
if (Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
{
|
{
|
||||||
damageable.ChangeDamage(DamageType.Asphyxiation, -_suffocationDamageRecovery, false);
|
damageable.TryChangeDamage(DamageType, -_damageRecovery, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent))
|
if (Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent))
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ using Robust.Shared.Enums;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.Chat.Commands
|
namespace Content.Server.Chat.Commands
|
||||||
{
|
{
|
||||||
@@ -34,21 +35,22 @@ namespace Content.Server.Chat.Commands
|
|||||||
var kind = suicide.Suicide(target, chat);
|
var kind = suicide.Suicide(target, chat);
|
||||||
if (kind != SuicideKind.Special)
|
if (kind != SuicideKind.Special)
|
||||||
{
|
{
|
||||||
damageableComponent.SetDamage(kind switch
|
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
|
damageableComponent.TrySetDamage(kind switch
|
||||||
{
|
{
|
||||||
SuicideKind.Blunt => DamageType.Blunt,
|
SuicideKind.Blunt => prototypeManager.Index<DamageTypePrototype>("Blunt"),
|
||||||
SuicideKind.Slash => DamageType.Slash,
|
SuicideKind.Slash => prototypeManager.Index<DamageTypePrototype>("Slash"),
|
||||||
SuicideKind.Piercing => DamageType.Piercing,
|
SuicideKind.Piercing => prototypeManager.Index<DamageTypePrototype>("Piercing"),
|
||||||
SuicideKind.Heat => DamageType.Heat,
|
SuicideKind.Heat => prototypeManager.Index<DamageTypePrototype>("Heat"),
|
||||||
SuicideKind.Shock => DamageType.Shock,
|
SuicideKind.Shock => prototypeManager.Index<DamageTypePrototype>("Shock"),
|
||||||
SuicideKind.Cold => DamageType.Cold,
|
SuicideKind.Cold => prototypeManager.Index<DamageTypePrototype>("Cold"),
|
||||||
SuicideKind.Poison => DamageType.Poison,
|
SuicideKind.Poison => prototypeManager.Index<DamageTypePrototype>("Poison"),
|
||||||
SuicideKind.Radiation => DamageType.Radiation,
|
SuicideKind.Radiation => prototypeManager.Index<DamageTypePrototype>("Radiation"),
|
||||||
SuicideKind.Asphyxiation => DamageType.Asphyxiation,
|
SuicideKind.Asphyxiation => prototypeManager.Index<DamageTypePrototype>("Asphyxiation"),
|
||||||
SuicideKind.Bloodloss => DamageType.Bloodloss,
|
SuicideKind.Bloodloss => prototypeManager.Index<DamageTypePrototype>("Bloodloss"),
|
||||||
_ => DamageType.Blunt
|
_ => prototypeManager.Index<DamageTypePrototype>("Blunt")
|
||||||
},
|
},
|
||||||
200, source);
|
200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +119,7 @@ namespace Content.Server.Chat.Commands
|
|||||||
var selfMessage = Loc.GetString("suicide-command-default-text-self");
|
var selfMessage = Loc.GetString("suicide-command-default-text-self");
|
||||||
owner.PopupMessage(selfMessage);
|
owner.PopupMessage(selfMessage);
|
||||||
|
|
||||||
dmgComponent.SetDamage(DamageType.Piercing, 200, owner);
|
dmgComponent.TrySetDamage(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Piercing"), 200);
|
||||||
|
|
||||||
// Prevent the player from returning to the body.
|
// Prevent the player from returning to the body.
|
||||||
// Note that mind cannot be null because otherwise owner would be null.
|
// Note that mind cannot be null because otherwise owner would be null.
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ using Robust.Shared.GameObjects;
|
|||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Server.Chemistry.ReagentEffects
|
namespace Content.Server.Chemistry.ReagentEffects
|
||||||
{
|
{
|
||||||
@@ -12,7 +15,7 @@ namespace Content.Server.Chemistry.ReagentEffects
|
|||||||
/// Default metabolism for medicine reagents. Attempts to find a DamageableComponent on the target,
|
/// Default metabolism for medicine reagents. Attempts to find a DamageableComponent on the target,
|
||||||
/// and to update its damage values.
|
/// and to update its damage values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class HealthChange : ReagentEffect
|
public class HealthChange : ReagentEffect, ISerializationHooks
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How much damage is changed when 1u of the reagent is metabolized.
|
/// How much damage is changed when 1u of the reagent is metabolized.
|
||||||
@@ -20,35 +23,45 @@ namespace Content.Server.Chemistry.ReagentEffects
|
|||||||
[DataField("healthChange")]
|
[DataField("healthChange")]
|
||||||
public float AmountToChange { get; set; } = 1.0f;
|
public float AmountToChange { get; set; } = 1.0f;
|
||||||
|
|
||||||
/// <summary>
|
// TODO DAMAGE UNITS When damage units support decimals, get rid of this.
|
||||||
/// Class of damage changed, Brute, Burn, Toxin, Airloss.
|
// See also _accumulatedDamage in ThirstComponent and HungerComponent
|
||||||
/// </summary>
|
private float _accumulatedDamage;
|
||||||
[DataField("damageClass")]
|
|
||||||
public DamageClass DamageType { get; set; } = DamageClass.Brute;
|
|
||||||
|
|
||||||
private float _accumulatedHealth;
|
/// <summary>
|
||||||
|
/// Damage group to change.
|
||||||
|
/// </summary>
|
||||||
|
// 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<IPrototypeManager>().Index<DamageGroupPrototype>(_damageGroupID);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes damage if a DamageableComponent can be found.
|
/// Changes damage if a DamageableComponent can be found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override void Metabolize(IEntity solutionEntity, Solution.ReagentQuantity amount)
|
public override void Metabolize(IEntity solutionEntity, Solution.ReagentQuantity amount)
|
||||||
{
|
{
|
||||||
if (solutionEntity.TryGetComponent(out IDamageableComponent? health))
|
if (solutionEntity.TryGetComponent(out IDamageableComponent? damageComponent))
|
||||||
{
|
{
|
||||||
health.ChangeDamage(DamageType, (int)AmountToChange, true);
|
damageComponent.TryChangeDamage(DamageGroup, (int)AmountToChange, true);
|
||||||
float decHealthChange = (float) (AmountToChange - (int) AmountToChange);
|
|
||||||
_accumulatedHealth += decHealthChange;
|
|
||||||
|
|
||||||
if (_accumulatedHealth >= 1)
|
float decHealthChange = (float) (AmountToChange - (int) AmountToChange);
|
||||||
|
_accumulatedDamage += decHealthChange;
|
||||||
|
|
||||||
|
if (_accumulatedDamage >= 1)
|
||||||
{
|
{
|
||||||
health.ChangeDamage(DamageType, 1, true);
|
damageComponent.TryChangeDamage(DamageGroup, 1, true);
|
||||||
_accumulatedHealth -= 1;
|
_accumulatedDamage -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(_accumulatedHealth <= -1)
|
else if(_accumulatedDamage <= -1)
|
||||||
{
|
{
|
||||||
health.ChangeDamage(DamageType, -1, true);
|
damageComponent.TryChangeDamage(DamageGroup, -1, true);
|
||||||
_accumulatedHealth += 1;
|
_accumulatedDamage += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Content.Server.Administration;
|
using Content.Server.Administration;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
@@ -9,6 +10,7 @@ using Robust.Server.Player;
|
|||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.Damage.Commands
|
namespace Content.Server.Damage.Commands
|
||||||
{
|
{
|
||||||
@@ -19,22 +21,24 @@ namespace Content.Server.Damage.Commands
|
|||||||
public string Description => "Ouch";
|
public string Description => "Ouch";
|
||||||
public string Help => $"Usage: {Command} <type/?> <amount> (<entity uid/_>) (<ignoreResistances>)";
|
public string Help => $"Usage: {Command} <type/?> <amount> (<entity uid/_>) (<ignoreResistances>)";
|
||||||
|
|
||||||
|
private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
public HurtCommand() {
|
||||||
|
_prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
|
}
|
||||||
|
|
||||||
private string DamageTypes()
|
private string DamageTypes()
|
||||||
{
|
{
|
||||||
var msg = new StringBuilder();
|
var msg = new StringBuilder();
|
||||||
foreach (var dClass in Enum.GetNames(typeof(DamageClass)))
|
|
||||||
|
foreach (var damageGroup in _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
||||||
{
|
{
|
||||||
msg.Append($"\n{dClass}");
|
msg.Append($"\n{damageGroup.ID}");
|
||||||
|
if (damageGroup.DamageTypes.Any())
|
||||||
var types = Enum.Parse<DamageClass>(dClass).ToTypes();
|
|
||||||
|
|
||||||
if (types.Count > 0)
|
|
||||||
{
|
{
|
||||||
msg.Append(": ");
|
msg.Append(": ");
|
||||||
msg.AppendJoin('|', types);
|
msg.AppendJoin('|', damageGroup.DamageTypes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $"Damage Types:{msg}";
|
return $"Damage Types:{msg}";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +89,8 @@ namespace Content.Server.Damage.Commands
|
|||||||
string[] args,
|
string[] args,
|
||||||
[NotNullWhen(true)] out Damage? func)
|
[NotNullWhen(true)] out Damage? func)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
if (!int.TryParse(args[1], out var amount))
|
if (!int.TryParse(args[1], out var amount))
|
||||||
{
|
{
|
||||||
shell.WriteLine($"{args[1]} is not a valid damage integer.");
|
shell.WriteLine($"{args[1]} is not a valid damage integer.");
|
||||||
@@ -93,42 +99,42 @@ namespace Content.Server.Damage.Commands
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Enum.TryParse<DamageClass>(args[0], true, out var damageClass))
|
if (_prototypeManager.TryIndex<DamageGroupPrototype>(args[0], out var damageGroup))
|
||||||
{
|
{
|
||||||
func = (damageable, ignoreResistances) =>
|
func = (damageable, ignoreResistances) =>
|
||||||
{
|
{
|
||||||
if (!damageable.DamageClasses.ContainsKey(damageClass))
|
if (!damageable.ApplicableDamageGroups.Contains(damageGroup))
|
||||||
{
|
{
|
||||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage class {damageClass}");
|
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage group {damageGroup}");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!damageable.ChangeDamage(damageClass, amount, ignoreResistances))
|
if (!damageable.TryChangeDamage(damageGroup, amount, ignoreResistances))
|
||||||
{
|
{
|
||||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage.");
|
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage.");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageClass} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
|
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageGroup} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
|
||||||
};
|
};
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Fall back to DamageType
|
// Fall back to DamageType
|
||||||
else if (Enum.TryParse<DamageType>(args[0], true, out var damageType))
|
else if (_prototypeManager.TryIndex<DamageTypePrototype>(args[0], out var damageType))
|
||||||
{
|
{
|
||||||
func = (damageable, ignoreResistances) =>
|
func = (damageable, ignoreResistances) =>
|
||||||
{
|
{
|
||||||
if (!damageable.DamageTypes.ContainsKey(damageType))
|
if (!damageable.IsSupportedDamageType(damageType))
|
||||||
{
|
{
|
||||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage class {damageType}");
|
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage type {damageType}");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!damageable.ChangeDamage(damageType, amount, ignoreResistances))
|
if (!damageable.TryChangeDamage(damageType, amount, ignoreResistances))
|
||||||
{
|
{
|
||||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage.");
|
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage.");
|
||||||
|
|
||||||
@@ -136,9 +142,10 @@ namespace Content.Server.Damage.Commands
|
|||||||
}
|
}
|
||||||
|
|
||||||
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageType} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
|
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageType} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
|
||||||
};
|
|
||||||
|
|
||||||
|
};
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ using Content.Shared.Damage;
|
|||||||
using Content.Shared.Sound;
|
using Content.Shared.Sound;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Server.Damage.Components
|
namespace Content.Server.Damage.Components
|
||||||
{
|
{
|
||||||
@@ -14,8 +17,6 @@ namespace Content.Server.Damage.Components
|
|||||||
{
|
{
|
||||||
public override string Name => "DamageOnHighSpeedImpact";
|
public override string Name => "DamageOnHighSpeedImpact";
|
||||||
|
|
||||||
[DataField("damage")]
|
|
||||||
public DamageType Damage { get; set; } = DamageType.Blunt;
|
|
||||||
[DataField("minimumSpeed")]
|
[DataField("minimumSpeed")]
|
||||||
public float MinimumSpeed { get; set; } = 20f;
|
public float MinimumSpeed { get; set; } = 20f;
|
||||||
[DataField("baseDamage")]
|
[DataField("baseDamage")]
|
||||||
@@ -34,5 +35,17 @@ namespace Content.Server.Damage.Components
|
|||||||
public float DamageCooldown { get; set; } = 2f;
|
public float DamageCooldown { get; set; } = 2f;
|
||||||
|
|
||||||
internal TimeSpan LastHit = TimeSpan.Zero;
|
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";
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public DamageTypePrototype DamageType = default!;
|
||||||
|
protected override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ using Content.Shared.Damage.Components;
|
|||||||
using Content.Shared.Throwing;
|
using Content.Shared.Throwing;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Server.Damage.Components
|
namespace Content.Server.Damage.Components
|
||||||
{
|
{
|
||||||
@@ -11,20 +14,31 @@ namespace Content.Server.Damage.Components
|
|||||||
{
|
{
|
||||||
public override string Name => "DamageOnLand";
|
public override string Name => "DamageOnLand";
|
||||||
|
|
||||||
[DataField("damageType")]
|
|
||||||
private DamageType _damageType = DamageType.Blunt;
|
|
||||||
|
|
||||||
[DataField("amount")]
|
[DataField("amount")]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
private int _amount = 1;
|
private int _amount = 1;
|
||||||
|
|
||||||
[DataField("ignoreResistances")]
|
[DataField("ignoreResistances")]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
private bool _ignoreResistances;
|
private 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")]
|
||||||
|
private readonly string _damageTypeID = "Blunt";
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public DamageTypePrototype DamageType = default!;
|
||||||
|
protected override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||||
|
}
|
||||||
|
|
||||||
void ILand.Land(LandEventArgs eventArgs)
|
void ILand.Land(LandEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable)) return;
|
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
|
return;
|
||||||
damageable.ChangeDamage(_damageType, _amount, _ignoreResistances, eventArgs.User);
|
damageable.TryChangeDamage(DamageType, _amount, _ignoreResistances);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Tools.Components;
|
using Content.Server.Tools.Components;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
@@ -7,12 +7,16 @@ using Content.Shared.Interaction;
|
|||||||
using Content.Shared.Tool;
|
using Content.Shared.Tool;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Server.Damage.Components
|
namespace Content.Server.Damage.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class DamageOnToolInteractComponent : Component, IInteractUsing
|
public class DamageOnToolInteractComponent : Component, IInteractUsing
|
||||||
{
|
{
|
||||||
|
|
||||||
public override string Name => "DamageOnToolInteract";
|
public override string Name => "DamageOnToolInteract";
|
||||||
|
|
||||||
[DataField("damage")]
|
[DataField("damage")]
|
||||||
@@ -21,6 +25,23 @@ namespace Content.Server.Damage.Components
|
|||||||
[DataField("tools")]
|
[DataField("tools")]
|
||||||
private List<ToolQuality> _tools = new();
|
private List<ToolQuality> _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";
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public DamageTypePrototype WeldingDamageType = default!;
|
||||||
|
[DataField("defaultDamageType")]
|
||||||
|
private readonly string _defaultDamageTypeID = "Blunt";
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public DamageTypePrototype DefaultDamageType = default!;
|
||||||
|
protected override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
WeldingDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_weldingDamageTypeID);
|
||||||
|
DefaultDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_defaultDamageTypeID);
|
||||||
|
}
|
||||||
|
|
||||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (eventArgs.Using.TryGetComponent<ToolComponent>(out var tool))
|
if (eventArgs.Using.TryGetComponent<ToolComponent>(out var tool))
|
||||||
@@ -44,17 +65,15 @@ namespace Content.Server.Damage.Components
|
|||||||
|
|
||||||
protected bool CallDamage(InteractUsingEventArgs eventArgs, ToolComponent tool)
|
protected bool CallDamage(InteractUsingEventArgs eventArgs, ToolComponent tool)
|
||||||
{
|
{
|
||||||
if (eventArgs.Target.TryGetComponent<IDamageableComponent>(out var damageable))
|
if (!eventArgs.Target.TryGetComponent<IDamageableComponent>(out var damageable))
|
||||||
{
|
return false;
|
||||||
damageable.ChangeDamage(tool.HasQuality(ToolQuality.Welding)
|
|
||||||
? DamageType.Heat
|
|
||||||
: DamageType.Blunt,
|
|
||||||
Damage, false, eventArgs.User);
|
|
||||||
|
|
||||||
return true;
|
damageable.TryChangeDamage(tool.HasQuality(ToolQuality.Welding)
|
||||||
}
|
? WeldingDamageType
|
||||||
|
: DefaultDamageType,
|
||||||
|
Damage);
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ using Content.Shared.Damage;
|
|||||||
using Robust.Shared.Analyzers;
|
using Robust.Shared.Analyzers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
namespace Content.Server.Damage.Components
|
namespace Content.Server.Damage.Components
|
||||||
{
|
{
|
||||||
@@ -11,13 +13,21 @@ namespace Content.Server.Damage.Components
|
|||||||
{
|
{
|
||||||
public override string Name => "DamageOtherOnHit";
|
public override string Name => "DamageOtherOnHit";
|
||||||
|
|
||||||
[DataField("damageType")]
|
|
||||||
public DamageType DamageType { get; } = DamageType.Blunt;
|
|
||||||
|
|
||||||
[DataField("amount")]
|
[DataField("amount")]
|
||||||
public int Amount { get; } = 1;
|
public int Amount { get; } = 1;
|
||||||
|
|
||||||
[DataField("ignoreResistances")]
|
[DataField("ignoreResistances")]
|
||||||
public bool IgnoreResistances { get; } = false;
|
public bool IgnoreResistances { get; } = 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 = "Blunt";
|
||||||
|
public DamageTypePrototype DamageType { get; set; } = default!;
|
||||||
|
protected override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace Content.Server.Damage
|
|||||||
if (ComponentManager.TryGetComponent(uid, out StunnableComponent? stun) && _robustRandom.Prob(component.StunChance))
|
if (ComponentManager.TryGetComponent(uid, out StunnableComponent? stun) && _robustRandom.Prob(component.StunChance))
|
||||||
stun.Stun(component.StunSeconds);
|
stun.Stun(component.StunSeconds);
|
||||||
|
|
||||||
damageable.ChangeDamage(component.Damage, damage, false, args.OtherFixture.Body.Owner);
|
damageable.TryChangeDamage(component.DamageType, damage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace Content.Server.Damage
|
|||||||
if (!args.Target.TryGetComponent(out IDamageableComponent? damageable))
|
if (!args.Target.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
damageable.ChangeDamage(component.DamageType, component.Amount, component.IgnoreResistances, args.User);
|
damageable.TryChangeDamage(component.DamageType, component.Amount, component.IgnoreResistances);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.Atmos.Components;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Server.Atmos.Components;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
using Content.Shared.Damage.Resistances;
|
|
||||||
using Content.Shared.GameTicking;
|
using Content.Shared.GameTicking;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
@@ -43,8 +42,9 @@ namespace Content.Server.Damage
|
|||||||
|
|
||||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
{
|
{
|
||||||
damageable.SupportedTypes.Clear();
|
damageable.SupportedDamageTypes.Clear();
|
||||||
damageable.SupportedClasses.Clear();
|
damageable.FullySupportedDamageGroups.Clear();
|
||||||
|
damageable.ApplicableDamageGroups.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -69,14 +69,19 @@ namespace Content.Server.Damage
|
|||||||
|
|
||||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
{
|
{
|
||||||
if (old.SupportedTypes != null)
|
if (old.SupportedDamageTypes != null)
|
||||||
{
|
{
|
||||||
damageable.SupportedTypes.UnionWith(old.SupportedTypes);
|
damageable.SupportedDamageTypes.UnionWith(old.SupportedDamageTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (old.SupportedClasses != null)
|
if (old.SupportedDamageGroups != null)
|
||||||
{
|
{
|
||||||
damageable.SupportedClasses.UnionWith(old.SupportedClasses);
|
damageable.FullySupportedDamageGroups.UnionWith(old.SupportedDamageGroups);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (old.ApplicableDamageGroups != null)
|
||||||
|
{
|
||||||
|
damageable.ApplicableDamageGroups.UnionWith(old.ApplicableDamageGroups);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,8 +116,9 @@ namespace Content.Server.Damage
|
|||||||
|
|
||||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
{
|
{
|
||||||
SupportedTypes = damageable.SupportedTypes.ToHashSet();
|
SupportedDamageTypes = damageable.SupportedDamageTypes.ToHashSet();
|
||||||
SupportedClasses = damageable.SupportedClasses.ToHashSet();
|
SupportedDamageGroups = damageable.FullySupportedDamageGroups.ToHashSet();
|
||||||
|
ApplicableDamageGroups = damageable.ApplicableDamageGroups.ToHashSet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,9 +126,11 @@ namespace Content.Server.Damage
|
|||||||
|
|
||||||
public bool MovedByPressure { get; }
|
public bool MovedByPressure { get; }
|
||||||
|
|
||||||
public HashSet<DamageType>? SupportedTypes { get; }
|
public HashSet<DamageTypePrototype>? SupportedDamageTypes { get; }
|
||||||
|
|
||||||
public HashSet<DamageClass>? SupportedClasses { get; }
|
public HashSet<DamageGroupPrototype>? SupportedDamageGroups { get; }
|
||||||
|
|
||||||
|
public HashSet<DamageGroupPrototype>? ApplicableDamageGroups { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ namespace Content.Server.Damage
|
|||||||
{
|
{
|
||||||
if (target.TryGetComponent(out IDamageableComponent? damage))
|
if (target.TryGetComponent(out IDamageableComponent? damage))
|
||||||
{
|
{
|
||||||
damage.Heal();
|
damage.TrySetAllDamage(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target.TryGetComponent(out IMobStateComponent? mobState))
|
if (target.TryGetComponent(out IMobStateComponent? mobState))
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.Damage.Components;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
|
|
||||||
namespace Content.Server.Destructible.Thresholds.Triggers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A trigger that will activate when the amount of damage received
|
|
||||||
/// of the specified class is above the specified threshold.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable]
|
|
||||||
[DataDefinition]
|
|
||||||
public class DamageClassTrigger : IThresholdTrigger
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The class to check the damage of.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("class")]
|
|
||||||
public DamageClass? Class { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of damage at which this threshold will trigger.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("damage")]
|
|
||||||
public int Damage { get; set; }
|
|
||||||
|
|
||||||
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
|
||||||
{
|
|
||||||
if (Class == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return damageable.TryGetDamage(Class.Value, out var damageReceived) &&
|
|
||||||
damageReceived >= Damage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
namespace Content.Server.Destructible.Thresholds.Triggers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A trigger that will activate when the amount of damage received
|
||||||
|
/// of the specified class is above the specified threshold.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable]
|
||||||
|
[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<IPrototypeManager>().Index<DamageGroupPrototype>(_damageGroupID);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of damage at which this threshold will trigger.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("damage", required: true)]
|
||||||
|
public int Damage { get; set; } = default!;
|
||||||
|
|
||||||
|
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
||||||
|
{
|
||||||
|
if (DamageGroup == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return damageable.TryGetDamage(DamageGroup, out var damageReceived) &&
|
||||||
|
damageReceived >= Damage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
@@ -15,8 +15,8 @@ namespace Content.Server.Destructible.Thresholds.Triggers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of damage at which this threshold will trigger.
|
/// The amount of damage at which this threshold will trigger.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("damage")]
|
[DataField("damage", required: true)]
|
||||||
public int Damage { get; set; }
|
public int Damage { get; set; } = default!;
|
||||||
|
|
||||||
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
namespace Content.Server.Destructible.Thresholds.Triggers
|
namespace Content.Server.Destructible.Thresholds.Triggers
|
||||||
{
|
{
|
||||||
@@ -13,20 +15,24 @@ namespace Content.Server.Destructible.Thresholds.Triggers
|
|||||||
[DataDefinition]
|
[DataDefinition]
|
||||||
public class DamageTypeTrigger : IThresholdTrigger
|
public class DamageTypeTrigger : IThresholdTrigger
|
||||||
{
|
{
|
||||||
[DataField("type")]
|
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||||
public DamageType? Type { get; set; }
|
// 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<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||||
|
|
||||||
[DataField("damage")]
|
[DataField("damage", required: true)]
|
||||||
public int Damage { get; set; }
|
public int Damage { get; set; } = default!;
|
||||||
|
|
||||||
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
||||||
{
|
{
|
||||||
if (Type == null)
|
if (DamageType == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return damageable.TryGetDamage(Type.Value, out var damageReceived) &&
|
return damageable.TryGetDamage(DamageType, out var damageReceived) &&
|
||||||
damageReceived >= Damage;
|
damageReceived >= Damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ using Robust.Shared.Players;
|
|||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using Timer = Robust.Shared.Timing.Timer;
|
using Timer = Robust.Shared.Timing.Timer;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
namespace Content.Server.Doors.Components
|
namespace Content.Server.Doors.Components
|
||||||
{
|
{
|
||||||
@@ -45,6 +47,18 @@ namespace Content.Server.Doors.Components
|
|||||||
[DataField("tryOpenDoorSound")]
|
[DataField("tryOpenDoorSound")]
|
||||||
private SoundSpecifier _tryOpenDoorSound = new SoundPathSpecifier("/Audio/Effects/bang.ogg");
|
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";
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public DamageTypePrototype DamageType = default!;
|
||||||
|
protected override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||||
|
}
|
||||||
|
|
||||||
public override DoorState State
|
public override DoorState State
|
||||||
{
|
{
|
||||||
get => base.State;
|
get => base.State;
|
||||||
@@ -536,7 +550,7 @@ namespace Content.Server.Doors.Components
|
|||||||
hitsomebody = true;
|
hitsomebody = true;
|
||||||
CurrentlyCrushing.Add(e.Owner.Uid);
|
CurrentlyCrushing.Add(e.Owner.Uid);
|
||||||
|
|
||||||
damage.ChangeDamage(DamageType.Blunt, DoorCrushDamage, false, Owner);
|
damage.TryChangeDamage(DamageType, DoorCrushDamage);
|
||||||
stun.Paralyze(DoorStunTime);
|
stun.Paralyze(DoorStunTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using Robust.Server.Player;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.GameTicking.Presets
|
namespace Content.Server.GameTicking.Presets
|
||||||
{
|
{
|
||||||
@@ -66,7 +67,8 @@ namespace Content.Server.GameTicking.Presets
|
|||||||
if (playerEntity.TryGetComponent(out IDamageableComponent? damageable))
|
if (playerEntity.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
{
|
{
|
||||||
//todo: what if they dont breathe lol
|
//todo: what if they dont breathe lol
|
||||||
damageable.SetDamage(DamageType.Asphyxiation, 200, playerEntity);
|
//cry deeply
|
||||||
|
damageable.TrySetDamage(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Asphyxiation"), 200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ using Robust.Server.Player;
|
|||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Log;
|
using Robust.Shared.Log;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
@@ -38,6 +39,7 @@ namespace Content.Server.GameTicking.Presets
|
|||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
|
||||||
public string PDAPrototypeName => "CaptainPDA";
|
public string PDAPrototypeName => "CaptainPDA";
|
||||||
public string BeltPrototypeName => "ClothingBeltJanitorFilled";
|
public string BeltPrototypeName => "ClothingBeltJanitorFilled";
|
||||||
@@ -192,11 +194,11 @@ namespace Content.Server.GameTicking.Presets
|
|||||||
{
|
{
|
||||||
if (mobState.IsCritical())
|
if (mobState.IsCritical())
|
||||||
{
|
{
|
||||||
// TODO: This is copy/pasted from ghost code. Really, IDamagableComponent needs a method to reliably kill the target.
|
// TODO: This is copy/pasted from ghost code. Really, IDamageableComponent needs a method to reliably kill the target.
|
||||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
{
|
{
|
||||||
//todo: what if they dont breathe lol
|
//todo: what if they dont breathe lol
|
||||||
damageable.ChangeDamage(DamageType.Asphyxiation, 100, true);
|
damageable.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>("Asphyxiation"), 100, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!mobState.IsDead())
|
else if (!mobState.IsDead())
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ using Robust.Shared.Audio;
|
|||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
@@ -77,6 +78,19 @@ namespace Content.Server.Light.Components
|
|||||||
|
|
||||||
[ViewVariables] private ContainerSlot _lightBulbContainer = default!;
|
[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";
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public DamageTypePrototype DamageType = default!;
|
||||||
|
|
||||||
|
protected override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||||
|
_lightBulbContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, "light_bulb");
|
||||||
|
}
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public LightBulbComponent? LightBulb
|
public LightBulbComponent? LightBulb
|
||||||
{
|
{
|
||||||
@@ -126,7 +140,7 @@ namespace Content.Server.Light.Components
|
|||||||
void Burn()
|
void Burn()
|
||||||
{
|
{
|
||||||
Owner.PopupMessage(eventArgs.User, Loc.GetString("powered-light-component-burn-hand"));
|
Owner.PopupMessage(eventArgs.User, Loc.GetString("powered-light-component-burn-hand"));
|
||||||
damageableComponent.ChangeDamage(DamageType.Heat, 20, false, Owner);
|
damageableComponent.TryChangeDamage(DamageType, 20);
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), _burnHandSound.GetSound(), Owner);
|
SoundSystem.Play(Filter.Pvs(Owner), _burnHandSound.GetSound(), Owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,13 +263,6 @@ namespace Content.Server.Light.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
_lightBulbContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, "light_bulb");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||||
{
|
{
|
||||||
base.HandleMessage(message, component);
|
base.HandleMessage(message, component);
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Stack;
|
using Content.Server.Stack;
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Interaction.Events;
|
|
||||||
using Content.Shared.Interaction.Helpers;
|
using Content.Shared.Interaction.Helpers;
|
||||||
using Content.Shared.Stacks;
|
using Content.Shared.Stacks;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Server.Medical.Components
|
namespace Content.Server.Medical.Components
|
||||||
{
|
{
|
||||||
@@ -18,7 +20,12 @@ namespace Content.Server.Medical.Components
|
|||||||
{
|
{
|
||||||
public override string Name => "Healing";
|
public override string Name => "Healing";
|
||||||
|
|
||||||
[DataField("heal")] public Dictionary<DamageType, int> Heal { get; private set; } = new();
|
// 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 )]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public Dictionary<string, int> Heal = new();
|
||||||
|
|
||||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
@@ -48,9 +55,9 @@ namespace Content.Server.Medical.Components
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var (type, amount) in Heal)
|
foreach (var (damageTypeID, amount) in Heal)
|
||||||
{
|
{
|
||||||
damageable.ChangeDamage(type, -amount, true);
|
damageable.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(damageTypeID), -amount, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -99,8 +99,8 @@ namespace Content.Server.Medical.Components
|
|||||||
private static readonly MedicalScannerBoundUserInterfaceState EmptyUIState =
|
private static readonly MedicalScannerBoundUserInterfaceState EmptyUIState =
|
||||||
new(
|
new(
|
||||||
null,
|
null,
|
||||||
new Dictionary<DamageClass, int>(),
|
new Dictionary<string, int>(),
|
||||||
new Dictionary<DamageType, int>(),
|
new Dictionary<string, int>(),
|
||||||
false);
|
false);
|
||||||
|
|
||||||
private MedicalScannerBoundUserInterfaceState GetUserInterfaceState()
|
private MedicalScannerBoundUserInterfaceState GetUserInterfaceState()
|
||||||
@@ -121,12 +121,13 @@ namespace Content.Server.Medical.Components
|
|||||||
return EmptyUIState;
|
return EmptyUIState;
|
||||||
}
|
}
|
||||||
|
|
||||||
var classes = new Dictionary<DamageClass, int>(damageable.DamageClasses);
|
// Get dictionaries of damage, by fully supported damage groups and types
|
||||||
var types = new Dictionary<DamageType, int>(damageable.DamageTypes);
|
var groups = new Dictionary<string, int>(damageable.GetDamagePerFullySupportedGroupIDs);
|
||||||
|
var types = new Dictionary<string, int>(damageable.GetDamagePerTypeIDs);
|
||||||
|
|
||||||
if (_bodyContainer.ContainedEntity?.Uid == null)
|
if (_bodyContainer.ContainedEntity?.Uid == null)
|
||||||
{
|
{
|
||||||
return new MedicalScannerBoundUserInterfaceState(body.Uid, classes, types, true);
|
return new MedicalScannerBoundUserInterfaceState(body.Uid, groups, types, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var cloningSystem = EntitySystem.Get<CloningSystem>();
|
var cloningSystem = EntitySystem.Get<CloningSystem>();
|
||||||
@@ -134,7 +135,7 @@ namespace Content.Server.Medical.Components
|
|||||||
mindComponent.Mind != null &&
|
mindComponent.Mind != null &&
|
||||||
cloningSystem.HasDnaScan(mindComponent.Mind);
|
cloningSystem.HasDnaScan(mindComponent.Mind);
|
||||||
|
|
||||||
return new MedicalScannerBoundUserInterfaceState(body.Uid, classes, types, scanned);
|
return new MedicalScannerBoundUserInterfaceState(body.Uid, groups, types, scanned);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateUserInterface()
|
private void UpdateUserInterface()
|
||||||
|
|||||||
@@ -7,9 +7,12 @@ using Content.Shared.Mining;
|
|||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Server.Mining.Components
|
namespace Content.Server.Mining.Components
|
||||||
{
|
{
|
||||||
@@ -21,10 +24,16 @@ namespace Content.Server.Mining.Components
|
|||||||
public override string Name => "AsteroidRock";
|
public override string Name => "AsteroidRock";
|
||||||
private static readonly string[] SpriteStates = {"0", "1", "2", "3", "4"};
|
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()
|
protected override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||||
{
|
{
|
||||||
appearance.SetData(AsteroidRockVisuals.State, _random.Pick(SpriteStates));
|
appearance.SetData(AsteroidRockVisuals.State, _random.Pick(SpriteStates));
|
||||||
@@ -37,7 +46,7 @@ namespace Content.Server.Mining.Components
|
|||||||
if (!item.TryGetComponent(out MeleeWeaponComponent? meleeWeaponComponent))
|
if (!item.TryGetComponent(out MeleeWeaponComponent? meleeWeaponComponent))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Owner.GetComponent<IDamageableComponent>().ChangeDamage(DamageType.Blunt, meleeWeaponComponent.Damage, false, item);
|
Owner.GetComponent<IDamageableComponent>().TryChangeDamage(DamageType, meleeWeaponComponent.Damage);
|
||||||
|
|
||||||
if (!item.TryGetComponent(out PickaxeComponent? pickaxeComponent))
|
if (!item.TryGetComponent(out PickaxeComponent? pickaxeComponent))
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.Alert;
|
using Content.Server.Alert;
|
||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
@@ -14,6 +14,7 @@ using Robust.Shared.Players;
|
|||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.Nutrition.Components
|
namespace Content.Server.Nutrition.Components
|
||||||
{
|
{
|
||||||
@@ -22,6 +23,10 @@ namespace Content.Server.Nutrition.Components
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[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;
|
||||||
|
|
||||||
// Base stuff
|
// Base stuff
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public float BaseDecayRate
|
public float BaseDecayRate
|
||||||
@@ -29,7 +34,7 @@ namespace Content.Server.Nutrition.Components
|
|||||||
get => _baseDecayRate;
|
get => _baseDecayRate;
|
||||||
set => _baseDecayRate = value;
|
set => _baseDecayRate = value;
|
||||||
}
|
}
|
||||||
[DataField("base_decay_rate")]
|
[DataField("baseDecayRate")]
|
||||||
private float _baseDecayRate = 0.1f;
|
private float _baseDecayRate = 0.1f;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
@@ -59,11 +64,11 @@ namespace Content.Server.Nutrition.Components
|
|||||||
public Dictionary<HungerThreshold, float> HungerThresholds => _hungerThresholds;
|
public Dictionary<HungerThreshold, float> HungerThresholds => _hungerThresholds;
|
||||||
private readonly Dictionary<HungerThreshold, float> _hungerThresholds = new()
|
private readonly Dictionary<HungerThreshold, float> _hungerThresholds = new()
|
||||||
{
|
{
|
||||||
{HungerThreshold.Overfed, 600.0f},
|
{ HungerThreshold.Overfed, 600.0f },
|
||||||
{HungerThreshold.Okay, 450.0f},
|
{ HungerThreshold.Okay, 450.0f },
|
||||||
{HungerThreshold.Peckish, 300.0f},
|
{ HungerThreshold.Peckish, 300.0f },
|
||||||
{HungerThreshold.Starving, 150.0f},
|
{ HungerThreshold.Starving, 150.0f },
|
||||||
{HungerThreshold.Dead, 0.0f},
|
{ HungerThreshold.Dead, 0.0f },
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly Dictionary<HungerThreshold, AlertType> HungerThresholdAlertTypes = new()
|
public static readonly Dictionary<HungerThreshold, AlertType> HungerThresholdAlertTypes = new()
|
||||||
@@ -73,6 +78,18 @@ namespace Content.Server.Nutrition.Components
|
|||||||
{ HungerThreshold.Starving, AlertType.Starving },
|
{ 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"!;
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public DamageTypePrototype DamageType = default!;
|
||||||
|
protected override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||||
|
}
|
||||||
|
|
||||||
public void HungerThresholdEffect(bool force = false)
|
public void HungerThresholdEffect(bool force = false)
|
||||||
{
|
{
|
||||||
if (_currentHungerThreshold != _lastHungerThreshold || force)
|
if (_currentHungerThreshold != _lastHungerThreshold || force)
|
||||||
@@ -177,6 +194,7 @@ namespace Content.Server.Nutrition.Components
|
|||||||
|
|
||||||
if (_currentHungerThreshold != HungerThreshold.Dead)
|
if (_currentHungerThreshold != HungerThreshold.Dead)
|
||||||
return;
|
return;
|
||||||
|
// --> Current Hunger is below dead threshold
|
||||||
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
return;
|
return;
|
||||||
@@ -186,7 +204,14 @@ namespace Content.Server.Nutrition.Components
|
|||||||
|
|
||||||
if (!mobState.IsDead())
|
if (!mobState.IsDead())
|
||||||
{
|
{
|
||||||
damageable.ChangeDamage(DamageType.Blunt, 2, true);
|
// --> 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.Alert;
|
using Content.Server.Alert;
|
||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
@@ -14,6 +14,7 @@ using Robust.Shared.Players;
|
|||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.Nutrition.Components
|
namespace Content.Server.Nutrition.Components
|
||||||
{
|
{
|
||||||
@@ -22,6 +23,10 @@ namespace Content.Server.Nutrition.Components
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[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;
|
||||||
|
|
||||||
// Base stuff
|
// Base stuff
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public float BaseDecayRate
|
public float BaseDecayRate
|
||||||
@@ -29,7 +34,7 @@ namespace Content.Server.Nutrition.Components
|
|||||||
get => _baseDecayRate;
|
get => _baseDecayRate;
|
||||||
set => _baseDecayRate = value;
|
set => _baseDecayRate = value;
|
||||||
}
|
}
|
||||||
[DataField("base_decay_rate")]
|
[DataField("baseDecayRate")]
|
||||||
private float _baseDecayRate = 0.1f;
|
private float _baseDecayRate = 0.1f;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
@@ -72,6 +77,18 @@ namespace Content.Server.Nutrition.Components
|
|||||||
{ThirstThreshold.Parched, AlertType.Parched},
|
{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";
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public DamageTypePrototype DamageType = default!;
|
||||||
|
protected override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||||
|
}
|
||||||
|
|
||||||
public void ThirstThresholdEffect(bool force = false)
|
public void ThirstThresholdEffect(bool force = false)
|
||||||
{
|
{
|
||||||
if (_currentThirstThreshold != _lastThirstThreshold || force)
|
if (_currentThirstThreshold != _lastThirstThreshold || force)
|
||||||
@@ -174,6 +191,7 @@ namespace Content.Server.Nutrition.Components
|
|||||||
|
|
||||||
if (_currentThirstThreshold != ThirstThreshold.Dead)
|
if (_currentThirstThreshold != ThirstThreshold.Dead)
|
||||||
return;
|
return;
|
||||||
|
// --> Current Hunger is below dead threshold
|
||||||
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
return;
|
return;
|
||||||
@@ -183,7 +201,15 @@ namespace Content.Server.Nutrition.Components
|
|||||||
|
|
||||||
if (!mobState.IsDead())
|
if (!mobState.IsDead())
|
||||||
{
|
{
|
||||||
damageable.ChangeDamage(DamageType.Blunt, 2, true);
|
// --> 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
{
|
{
|
||||||
comp.OnUpdate(_accumulatedFrameTime);
|
comp.OnUpdate(_accumulatedFrameTime);
|
||||||
}
|
}
|
||||||
_accumulatedFrameTime -= 1;
|
_accumulatedFrameTime = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,10 @@ using Robust.Shared.IoC;
|
|||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Server.Projectiles.Components
|
namespace Content.Server.Projectiles.Components
|
||||||
{
|
{
|
||||||
@@ -25,26 +27,18 @@ namespace Content.Server.Projectiles.Components
|
|||||||
public override string Name => "Hitscan";
|
public override string Name => "Hitscan";
|
||||||
public CollisionGroup CollisionMask => (CollisionGroup) _collisionMask;
|
public CollisionGroup CollisionMask => (CollisionGroup) _collisionMask;
|
||||||
|
|
||||||
|
|
||||||
[DataField("layers")] //todo WithFormat.Flags<CollisionLayer>()
|
[DataField("layers")] //todo WithFormat.Flags<CollisionLayer>()
|
||||||
private int _collisionMask = (int) CollisionGroup.Opaque;
|
private int _collisionMask = (int) CollisionGroup.Opaque;
|
||||||
|
|
||||||
public float Damage
|
|
||||||
{
|
|
||||||
get => _damage;
|
|
||||||
set => _damage = value;
|
|
||||||
}
|
|
||||||
[DataField("damage")]
|
[DataField("damage")]
|
||||||
private float _damage = 10f;
|
public float Damage { get; set; } = 10f;
|
||||||
public DamageType DamageType => _damageType;
|
public float MaxLength => 20.0f;
|
||||||
[DataField("damageType")]
|
|
||||||
private DamageType _damageType = DamageType.Heat;
|
|
||||||
public float MaxLength => 20.0f;
|
|
||||||
|
|
||||||
private TimeSpan _startTime;
|
private TimeSpan _startTime;
|
||||||
private TimeSpan _deathTime;
|
private TimeSpan _deathTime;
|
||||||
|
|
||||||
public float ColorModifier { get; set; } = 1.0f;
|
public float ColorModifier { get; set; } = 1.0f;
|
||||||
[DataField("spriteName")]
|
[DataField("spriteName")]
|
||||||
private string _spriteName = "Objects/Weapons/Guns/Projectiles/laser.png";
|
private string _spriteName = "Objects/Weapons/Guns/Projectiles/laser.png";
|
||||||
[DataField("muzzleFlash")]
|
[DataField("muzzleFlash")]
|
||||||
private string? _muzzleFlash;
|
private string? _muzzleFlash;
|
||||||
@@ -53,6 +47,19 @@ namespace Content.Server.Projectiles.Components
|
|||||||
[DataField("soundHitWall")]
|
[DataField("soundHitWall")]
|
||||||
private SoundSpecifier _soundHitWall = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg");
|
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<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||||
|
}
|
||||||
|
|
||||||
public void FireEffects(IEntity user, float distance, Angle angle, IEntity? hitEntity = null)
|
public void FireEffects(IEntity user, float distance, Angle angle, IEntity? hitEntity = null)
|
||||||
{
|
{
|
||||||
var effectSystem = EntitySystem.Get<EffectSystem>();
|
var effectSystem = EntitySystem.Get<EffectSystem>();
|
||||||
|
|||||||
@@ -13,14 +13,12 @@ namespace Content.Server.Projectiles.Components
|
|||||||
[ComponentReference(typeof(SharedProjectileComponent))]
|
[ComponentReference(typeof(SharedProjectileComponent))]
|
||||||
public class ProjectileComponent : SharedProjectileComponent
|
public class ProjectileComponent : SharedProjectileComponent
|
||||||
{
|
{
|
||||||
[DataField("damages")] private Dictionary<DamageType, int> _damages = new();
|
// 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.
|
||||||
[ViewVariables]
|
// While thats being done, also replace "damages" -> "damageTypes" For consistency.
|
||||||
public Dictionary<DamageType, int> Damages
|
[DataField("damages")]
|
||||||
{
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
get => _damages;
|
public Dictionary<string, int> Damages { get; set; } = new();
|
||||||
set => _damages = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[DataField("deleteOnCollide")]
|
[DataField("deleteOnCollide")]
|
||||||
public bool DeleteOnCollide { get; } = true;
|
public bool DeleteOnCollide { get; } = true;
|
||||||
|
|||||||
@@ -7,12 +7,17 @@ using Robust.Shared.Audio;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Physics.Dynamics;
|
using Robust.Shared.Physics.Dynamics;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Content.Shared.Damage;
|
||||||
|
|
||||||
namespace Content.Server.Projectiles
|
namespace Content.Server.Projectiles
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
internal sealed class ProjectileSystem : EntitySystem
|
internal sealed class ProjectileSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -49,9 +54,9 @@ namespace Content.Server.Projectiles
|
|||||||
{
|
{
|
||||||
EntityManager.TryGetEntity(component.Shooter, out var shooter);
|
EntityManager.TryGetEntity(component.Shooter, out var shooter);
|
||||||
|
|
||||||
foreach (var (damageType, amount) in component.Damages)
|
foreach (var (damageTypeID, amount) in component.Damages)
|
||||||
{
|
{
|
||||||
damage.ChangeDamage(damageType, amount, false, shooter);
|
damage.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(damageTypeID), amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
component.DamagedEntity = true;
|
component.DamagedEntity = true;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace Content.Server.Repairable
|
|||||||
{
|
{
|
||||||
if (!await welder.UseTool(eventArgs.User, Owner, _doAfterDelay, ToolQuality.Welding, _fuelCost))
|
if (!await welder.UseTool(eventArgs.User, Owner, _doAfterDelay, ToolQuality.Welding, _fuelCost))
|
||||||
return false;
|
return false;
|
||||||
damageable.Heal();
|
damageable.TrySetAllDamage(0);
|
||||||
|
|
||||||
Owner.PopupMessage(eventArgs.User,
|
Owner.PopupMessage(eventArgs.User,
|
||||||
Loc.GetString("comp-repairable-repair",
|
Loc.GetString("comp-repairable-repair",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace Content.Server.Spawners.Components
|
|||||||
[DataField("job_id")]
|
[DataField("job_id")]
|
||||||
private string? _jobId;
|
private string? _jobId;
|
||||||
|
|
||||||
[field: ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("spawn_type")]
|
[DataField("spawn_type")]
|
||||||
public SpawnPointType SpawnType { get; } = SpawnPointType.Unset;
|
public SpawnPointType SpawnType { get; } = SpawnPointType.Unset;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Content.Server.Alert;
|
using Content.Server.Alert;
|
||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
@@ -8,6 +8,8 @@ using Robust.Shared.GameObjects;
|
|||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.Temperature.Components
|
namespace Content.Server.Temperature.Components
|
||||||
{
|
{
|
||||||
@@ -22,11 +24,21 @@ namespace Content.Server.Temperature.Components
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string Name => "Temperature";
|
public override string Name => "Temperature";
|
||||||
|
|
||||||
[ViewVariables] public float CurrentTemperature { get => _currentTemperature; set => _currentTemperature = value; }
|
[DataField("heatDamageThreshold")]
|
||||||
|
private float _heatDamageThreshold = default;
|
||||||
|
[DataField("coldDamageThreshold")]
|
||||||
|
private float _coldDamageThreshold = default;
|
||||||
|
[DataField("tempDamageCoefficient")]
|
||||||
|
private float _tempDamageCoefficient = 1;
|
||||||
|
[DataField("currentTemperature")]
|
||||||
|
public float CurrentTemperature { get; set; } = Atmospherics.T20C;
|
||||||
|
[DataField("specificHeat")]
|
||||||
|
private float _specificHeat = Atmospherics.MinimumHeatCapacity;
|
||||||
|
|
||||||
[ViewVariables] public float HeatDamageThreshold => _heatDamageThreshold;
|
[ViewVariables] public float HeatDamageThreshold => _heatDamageThreshold;
|
||||||
[ViewVariables] public float ColdDamageThreshold => _coldDamageThreshold;
|
[ViewVariables] public float ColdDamageThreshold => _coldDamageThreshold;
|
||||||
[ViewVariables] public float TempDamageCoefficient => _tempDamageCoefficient;
|
[ViewVariables] public float TempDamageCoefficient => _tempDamageCoefficient;
|
||||||
|
[ViewVariables] public float SpecificHeat => _specificHeat;
|
||||||
[ViewVariables] public float HeatCapacity {
|
[ViewVariables] public float HeatCapacity {
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@@ -39,33 +51,25 @@ namespace Content.Server.Temperature.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[ViewVariables] public float SpecificHeat => _specificHeat;
|
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||||
|
// Also remove Initialize override, if no longer needed.
|
||||||
[DataField("heatDamageThreshold")]
|
[DataField("coldDamageType")]
|
||||||
private float _heatDamageThreshold = default;
|
private readonly string _coldDamageTypeID = "Cold";
|
||||||
[DataField("coldDamageThreshold")]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
private float _coldDamageThreshold = default;
|
public DamageTypePrototype ColdDamageType = default!;
|
||||||
[DataField("tempDamageCoefficient")]
|
[DataField("hotDamageType")]
|
||||||
private float _tempDamageCoefficient = 1;
|
private readonly string _hotDamageTypeID = "Heat";
|
||||||
[DataField("currentTemperature")]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
private float _currentTemperature = Atmospherics.T20C;
|
public DamageTypePrototype HotDamageType = default!;
|
||||||
[DataField("specificHeat")]
|
protected override void Initialize()
|
||||||
private float _specificHeat = Atmospherics.MinimumHeatCapacity;
|
{
|
||||||
|
base.Initialize();
|
||||||
|
ColdDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_coldDamageTypeID);
|
||||||
|
HotDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_hotDamageTypeID);
|
||||||
|
}
|
||||||
|
|
||||||
public void Update()
|
public void Update()
|
||||||
{
|
{
|
||||||
var tempDamage = 0;
|
|
||||||
DamageType? damageType = null;
|
|
||||||
if (CurrentTemperature >= _heatDamageThreshold)
|
|
||||||
{
|
|
||||||
tempDamage = (int) Math.Floor((CurrentTemperature - _heatDamageThreshold) * _tempDamageCoefficient);
|
|
||||||
damageType = DamageType.Heat;
|
|
||||||
}
|
|
||||||
else if (CurrentTemperature <= _coldDamageThreshold)
|
|
||||||
{
|
|
||||||
tempDamage = (int) Math.Floor((_coldDamageThreshold - CurrentTemperature) * _tempDamageCoefficient);
|
|
||||||
damageType = DamageType.Cold;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Owner.TryGetComponent(out ServerAlertsComponent? status))
|
if (Owner.TryGetComponent(out ServerAlertsComponent? status))
|
||||||
{
|
{
|
||||||
@@ -108,10 +112,19 @@ namespace Content.Server.Temperature.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!damageType.HasValue) return;
|
|
||||||
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? component)) return;
|
if (!Owner.TryGetComponent(out IDamageableComponent? component)) return;
|
||||||
component.ChangeDamage(damageType.Value, tempDamage, false);
|
|
||||||
|
if (CurrentTemperature >= _heatDamageThreshold)
|
||||||
|
{
|
||||||
|
int tempDamage = (int) Math.Floor((CurrentTemperature - _heatDamageThreshold) * _tempDamageCoefficient);
|
||||||
|
component.TryChangeDamage(HotDamageType, tempDamage, false);
|
||||||
|
}
|
||||||
|
else if (CurrentTemperature <= _coldDamageThreshold)
|
||||||
|
{
|
||||||
|
int tempDamage = (int) Math.Floor((_coldDamageThreshold - CurrentTemperature) * _tempDamageCoefficient);
|
||||||
|
component.TryChangeDamage(ColdDamageType, tempDamage, false);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ using Content.Shared.Sound;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.Weapon.Melee.Components
|
namespace Content.Server.Weapon.Melee.Components
|
||||||
{
|
{
|
||||||
@@ -48,15 +50,23 @@ namespace Content.Server.Weapon.Melee.Components
|
|||||||
[DataField("damage")]
|
[DataField("damage")]
|
||||||
public int Damage { get; set; } = 5;
|
public int Damage { get; set; } = 5;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("damageType")]
|
|
||||||
public DamageType DamageType { get; set; } = DamageType.Blunt;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("clickAttackEffect")]
|
[DataField("clickAttackEffect")]
|
||||||
public bool ClickAttackEffect { get; set; } = true;
|
public bool ClickAttackEffect { get; set; } = true;
|
||||||
|
|
||||||
public TimeSpan LastAttackTime;
|
public TimeSpan LastAttackTime;
|
||||||
public TimeSpan CooldownEnd;
|
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";
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public DamageTypePrototype DamageType = default!;
|
||||||
|
protected override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ namespace Content.Server.Weapon.Melee
|
|||||||
public sealed class MeleeWeaponSystem : EntitySystem
|
public sealed class MeleeWeaponSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private IGameTiming _gameTiming = default!;
|
[Dependency] private IGameTiming _gameTiming = default!;
|
||||||
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -88,7 +90,7 @@ namespace Content.Server.Weapon.Melee
|
|||||||
|
|
||||||
if (target.TryGetComponent(out IDamageableComponent? damageableComponent))
|
if (target.TryGetComponent(out IDamageableComponent? damageableComponent))
|
||||||
{
|
{
|
||||||
damageableComponent.ChangeDamage(comp.DamageType, comp.Damage, false, owner);
|
damageableComponent.TryChangeDamage(comp.DamageType, comp.Damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
SoundSystem.Play(Filter.Pvs(owner), comp.HitSound.GetSound(), target);
|
SoundSystem.Play(Filter.Pvs(owner), comp.HitSound.GetSound(), target);
|
||||||
@@ -157,7 +159,7 @@ namespace Content.Server.Weapon.Melee
|
|||||||
{
|
{
|
||||||
if (entity.TryGetComponent<IDamageableComponent>(out var damageComponent))
|
if (entity.TryGetComponent<IDamageableComponent>(out var damageComponent))
|
||||||
{
|
{
|
||||||
damageComponent.ChangeDamage(comp.DamageType, comp.Damage, false, owner);
|
damageComponent.TryChangeDamage(comp.DamageType, comp.Damage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
{
|
{
|
||||||
if (energyRatio < 1.0)
|
if (energyRatio < 1.0)
|
||||||
{
|
{
|
||||||
var newDamages = new Dictionary<DamageType, int>(projectileComponent.Damages.Count);
|
var newDamages = new Dictionary<string, int>(projectileComponent.Damages.Count);
|
||||||
foreach (var (damageType, damage) in projectileComponent.Damages)
|
foreach (var (damageType, damage) in projectileComponent.Damages)
|
||||||
{
|
{
|
||||||
newDamages.Add(damageType, (int) (damage * energyRatio));
|
newDamages.Add(damageType, (int) (damage * energyRatio));
|
||||||
|
|||||||
@@ -399,7 +399,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
if (!result.HitEntity.TryGetComponent(out IDamageableComponent? damageable))
|
if (!result.HitEntity.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
damageable.ChangeDamage(hitscan.DamageType, (int)Math.Round(hitscan.Damage, MidpointRounding.AwayFromZero), false, Owner);
|
damageable.TryChangeDamage(hitscan.DamageType, (int)Math.Round(hitscan.Damage, MidpointRounding.AwayFromZero));
|
||||||
//I used Math.Round over Convert.toInt32, as toInt32 always rounds to
|
//I used Math.Round over Convert.toInt32, as toInt32 always rounds to
|
||||||
//even numbers if halfway between two numbers, rather than rounding to nearest
|
//even numbers if halfway between two numbers, rather than rounding to nearest
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ using Robust.Shared.Players;
|
|||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Content.Server.Weapon.Ranged
|
namespace Content.Server.Weapon.Ranged
|
||||||
{
|
{
|
||||||
@@ -55,6 +57,17 @@ namespace Content.Server.Weapon.Ranged
|
|||||||
|
|
||||||
[DataField("clumsyWeaponShotSound")]
|
[DataField("clumsyWeaponShotSound")]
|
||||||
private SoundSpecifier _clumsyWeaponShotSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/bang.ogg");
|
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<string, int> ClumsyDamage { get; set; } = new()
|
||||||
|
{
|
||||||
|
{ "Blunt", 10 },
|
||||||
|
{ "Heat", 5 }
|
||||||
|
};
|
||||||
|
|
||||||
public Func<bool>? WeaponCanFireHandler;
|
public Func<bool>? WeaponCanFireHandler;
|
||||||
public Func<IEntity, bool>? UserCanFireHandler;
|
public Func<IEntity, bool>? UserCanFireHandler;
|
||||||
@@ -168,25 +181,30 @@ namespace Content.Server.Weapon.Ranged
|
|||||||
|
|
||||||
if (ClumsyCheck && ClumsyComponent.TryRollClumsy(user, ClumsyExplodeChance))
|
if (ClumsyCheck && ClumsyComponent.TryRollClumsy(user, ClumsyExplodeChance))
|
||||||
{
|
{
|
||||||
SoundSystem.Play(
|
//Wound them
|
||||||
|
if (user.TryGetComponent(out IDamageableComponent? health))
|
||||||
|
{
|
||||||
|
foreach (KeyValuePair<string, int> damage in ClumsyDamage)
|
||||||
|
{
|
||||||
|
health.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(damage.Key), damage.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Knock them down
|
||||||
|
if (user.TryGetComponent(out StunnableComponent? stun))
|
||||||
|
{
|
||||||
|
stun.Paralyze(3f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply salt to the wound ("Honk!")
|
||||||
|
SoundSystem.Play(
|
||||||
Filter.Pvs(Owner), _clumsyWeaponHandlingSound.GetSound(),
|
Filter.Pvs(Owner), _clumsyWeaponHandlingSound.GetSound(),
|
||||||
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
||||||
|
|
||||||
SoundSystem.Play(
|
SoundSystem.Play(
|
||||||
Filter.Pvs(Owner), _clumsyWeaponShotSound.GetSound(),
|
Filter.Pvs(Owner), _clumsyWeaponShotSound.GetSound(),
|
||||||
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
||||||
|
|
||||||
if (user.TryGetComponent(out IDamageableComponent? health))
|
|
||||||
{
|
|
||||||
health.ChangeDamage(DamageType.Blunt, 10, false, user);
|
|
||||||
health.ChangeDamage(DamageType.Heat, 5, false, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.TryGetComponent(out StunnableComponent? stun))
|
|
||||||
{
|
|
||||||
stun.Paralyze(3f);
|
|
||||||
}
|
|
||||||
|
|
||||||
user.PopupMessage(Loc.GetString("server-ranged-weapon-component-try-fire-clumsy"));
|
user.PopupMessage(Loc.GetString("server-ranged-weapon-component-try-fire-clumsy"));
|
||||||
|
|
||||||
Owner.Delete();
|
Owner.Delete();
|
||||||
|
|||||||
@@ -73,6 +73,23 @@ namespace Content.Shared.Body.Components
|
|||||||
|
|
||||||
public SharedBodyPartComponent? CenterPart => CenterSlot?.Part;
|
public SharedBodyPartComponent? CenterPart => CenterSlot?.Part;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Amount of damage to deal when all vital organs are removed.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("vitalPartsRemovedDamage")]
|
||||||
|
public int VitalPartsRemovedDamage { get; set; } = 300!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Damage type to deal when all vital organs are removed.
|
||||||
|
/// </summary>
|
||||||
|
// 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()
|
protected override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -81,6 +98,7 @@ namespace Content.Shared.Body.Components
|
|||||||
// TODO BODY Move to template or somewhere else
|
// TODO BODY Move to template or somewhere else
|
||||||
if (TemplateId != null)
|
if (TemplateId != null)
|
||||||
{
|
{
|
||||||
|
VitalPartsRemovedDamageType = _prototypeManager.Index<DamageTypePrototype>(_vitalPartsRemovedDamageTypeID);
|
||||||
var template = _prototypeManager.Index<BodyTemplatePrototype>(TemplateId);
|
var template = _prototypeManager.Index<BodyTemplatePrototype>(TemplateId);
|
||||||
|
|
||||||
foreach (var (id, partType) in template.Slots)
|
foreach (var (id, partType) in template.Slots)
|
||||||
@@ -194,7 +212,7 @@ namespace Content.Shared.Body.Components
|
|||||||
{
|
{
|
||||||
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.ChangeDamage(DamageType.Bloodloss, 300, true); // TODO BODY KILL
|
damageable.TryChangeDamage(VitalPartsRemovedDamageType, VitalPartsRemovedDamage, true); // TODO BODY KILL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,15 @@ namespace Content.Shared.Damage.Components
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Component that allows attached entities to take damage.
|
/// Component that allows attached entities to take damage.
|
||||||
/// This basic version never dies (thus can take an indefinite amount of damage).
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// The supported damage types are specified using a <see cref="DamageContainerPrototype"/>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).
|
||||||
|
/// </remarks>
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(IDamageableComponent))]
|
[ComponentReference(typeof(IDamageableComponent))]
|
||||||
[NetworkedComponent()]
|
[NetworkedComponent()]
|
||||||
@@ -27,58 +34,76 @@ namespace Content.Shared.Damage.Components
|
|||||||
{
|
{
|
||||||
public override string Name => "Damageable";
|
public override string Name => "Damageable";
|
||||||
|
|
||||||
// TODO define these in yaml?
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
public const string DefaultResistanceSet = "defaultResistances";
|
|
||||||
public const string DefaultDamageContainer = "metallicDamageContainer";
|
|
||||||
|
|
||||||
private readonly Dictionary<DamageType, int> _damageList = DamageTypeExtensions.ToNewDictionary();
|
/// <summary>
|
||||||
|
/// The main damage dictionary. All the damage information is stored in this dictionary with <see cref="DamageTypePrototype"/> keys.
|
||||||
|
/// </summary>
|
||||||
|
private Dictionary<DamageTypePrototype, int> _damageDict = new();
|
||||||
|
|
||||||
[DataField("resistances")] public string ResistanceSetId = DefaultResistanceSet;
|
[DataField("resistances")]
|
||||||
|
public string ResistanceSetId { get; set; } = "defaultResistances";
|
||||||
// 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; } = DefaultDamageContainer;
|
|
||||||
|
|
||||||
[ViewVariables] public ResistanceSet Resistances { get; set; } = new();
|
[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
|
// TODO DAMAGE Cache this
|
||||||
[ViewVariables] public int TotalDamage => _damageList.Values.Sum();
|
// When moving logic from damageableComponent --> Damage System, make damageSystem update these on damage change.
|
||||||
|
[ViewVariables] public int TotalDamage => _damageDict.Values.Sum();
|
||||||
|
[ViewVariables] public IReadOnlyDictionary<DamageTypePrototype, int> GetDamagePerType => _damageDict;
|
||||||
|
[ViewVariables] public IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerApplicableGroup => DamageTypeDictToDamageGroupDict(_damageDict, ApplicableDamageGroups);
|
||||||
|
[ViewVariables] public IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerFullySupportedGroup => DamageTypeDictToDamageGroupDict(_damageDict, FullySupportedDamageGroups);
|
||||||
|
|
||||||
[ViewVariables] public IReadOnlyDictionary<DamageClass, int> DamageClasses => _damageList.ToClassDictionary();
|
// Whenever sending over network, also need a <string, int> dictionary
|
||||||
|
// TODO DAMAGE MAYBE Cache this?
|
||||||
|
public IReadOnlyDictionary<string, int> GetDamagePerApplicableGroupIDs => ConvertDictKeysToIDs(GetDamagePerApplicableGroup);
|
||||||
|
public IReadOnlyDictionary<string, int> GetDamagePerFullySupportedGroupIDs => ConvertDictKeysToIDs(GetDamagePerFullySupportedGroup);
|
||||||
|
public IReadOnlyDictionary<string, int> GetDamagePerTypeIDs => ConvertDictKeysToIDs(_damageDict);
|
||||||
|
|
||||||
[ViewVariables] public IReadOnlyDictionary<DamageType, int> DamageTypes => _damageList;
|
// 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<string> RadiationDamageTypeIDs { get; set; } = new() {"Radiation"};
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("explosionDamageTypes")]
|
||||||
|
public List<string> ExplosionDamageTypeIDs { get; set; } = new() { "Piercing", "Heat" };
|
||||||
|
|
||||||
[ViewVariables] public HashSet<DamageType> SupportedTypes { get; } = new();
|
public HashSet<DamageGroupPrototype> ApplicableDamageGroups { get; } = new();
|
||||||
|
|
||||||
[ViewVariables] public HashSet<DamageClass> SupportedClasses { get; } = new();
|
public HashSet<DamageGroupPrototype> FullySupportedDamageGroups { get; } = new();
|
||||||
|
|
||||||
public bool SupportsDamageClass(DamageClass @class)
|
public HashSet<DamageTypePrototype> SupportedDamageTypes { get; } = new();
|
||||||
{
|
|
||||||
return SupportedClasses.Contains(@class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool SupportsDamageType(DamageType type)
|
|
||||||
{
|
|
||||||
return SupportedTypes.Contains(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Initialize()
|
protected override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
|
||||||
|
|
||||||
// TODO DAMAGE Serialize damage done and resistance changes
|
// TODO DAMAGE Serialize damage done and resistance changes
|
||||||
var damagePrototype = prototypeManager.Index<DamageContainerPrototype>(DamageContainerId);
|
var damageContainerPrototype = _prototypeManager.Index<DamageContainerPrototype>(DamageContainerId);
|
||||||
|
|
||||||
SupportedClasses.Clear();
|
ApplicableDamageGroups.Clear();
|
||||||
SupportedTypes.Clear();
|
FullySupportedDamageGroups.Clear();
|
||||||
|
SupportedDamageTypes.Clear();
|
||||||
|
|
||||||
DamageContainerId = damagePrototype.ID;
|
//Get Damage groups/types from the DamageContainerPrototype.
|
||||||
SupportedClasses.UnionWith(damagePrototype.SupportedClasses);
|
DamageContainerId = damageContainerPrototype.ID;
|
||||||
SupportedTypes.UnionWith(damagePrototype.SupportedTypes);
|
ApplicableDamageGroups.UnionWith(damageContainerPrototype.ApplicableDamageGroups);
|
||||||
|
FullySupportedDamageGroups.UnionWith(damageContainerPrototype.FullySupportedDamageGroups);
|
||||||
|
SupportedDamageTypes.UnionWith(damageContainerPrototype.SupportedDamageTypes);
|
||||||
|
|
||||||
var resistancePrototype = prototypeManager.Index<ResistanceSetPrototype>(ResistanceSetId);
|
//initialize damage dictionary 0 damage
|
||||||
Resistances = new ResistanceSet(resistancePrototype);
|
_damageDict = new(SupportedDamageTypes.Count);
|
||||||
|
foreach (var type in SupportedDamageTypes)
|
||||||
|
{
|
||||||
|
_damageDict.Add(type, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Resistances = new ResistanceSet(_prototypeManager.Index<ResistanceSetPrototype>(ResistanceSetId));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Startup()
|
protected override void Startup()
|
||||||
@@ -90,7 +115,7 @@ namespace Content.Shared.Damage.Components
|
|||||||
|
|
||||||
public override ComponentState GetComponentState(ICommonSession player)
|
public override ComponentState GetComponentState(ICommonSession player)
|
||||||
{
|
{
|
||||||
return new DamageableComponentState(_damageList);
|
return new DamageableComponentState(GetDamagePerTypeIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||||
@@ -102,113 +127,101 @@ namespace Content.Shared.Damage.Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_damageList.Clear();
|
_damageDict.Clear();
|
||||||
|
|
||||||
foreach (var (type, damage) in state.DamageList)
|
foreach (var (type, damage) in state.DamageDict)
|
||||||
{
|
{
|
||||||
_damageList[type] = damage;
|
_damageDict[_prototypeManager.Index<DamageTypePrototype>(type)] = damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetDamage(DamageType type)
|
public int GetDamage(DamageTypePrototype type)
|
||||||
{
|
{
|
||||||
return _damageList.GetValueOrDefault(type);
|
return GetDamagePerType.GetValueOrDefault(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetDamage(DamageType type, out int damage)
|
public bool TryGetDamage(DamageTypePrototype type, out int damage)
|
||||||
{
|
{
|
||||||
return _damageList.TryGetValue(type, out damage);
|
return GetDamagePerType.TryGetValue(type, out damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetDamage(DamageClass @class)
|
public int GetDamage(DamageGroupPrototype group)
|
||||||
{
|
{
|
||||||
if (!SupportsDamageClass(@class))
|
return GetDamagePerApplicableGroup.GetValueOrDefault(group);
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var damage = 0;
|
|
||||||
|
|
||||||
foreach (var type in @class.ToTypes())
|
|
||||||
{
|
|
||||||
damage += GetDamage(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return damage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetDamage(DamageClass @class, out int damage)
|
public bool TryGetDamage(DamageGroupPrototype group, out int damage)
|
||||||
{
|
{
|
||||||
if (!SupportsDamageClass(@class))
|
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))
|
||||||
{
|
{
|
||||||
damage = 0;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
damage = GetDamage(@class);
|
if (newValue < 0)
|
||||||
|
{
|
||||||
|
// invalid value
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var type in group.DamageTypes)
|
||||||
|
{
|
||||||
|
TrySetDamage(type, newValue);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public bool TrySetAllDamage(int newValue)
|
||||||
/// Attempts to set the damage value for the given <see cref="DamageType"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// True if successful, false if this container does not support that type.
|
|
||||||
/// </returns>
|
|
||||||
public bool TrySetDamage(DamageType type, int newValue)
|
|
||||||
{
|
{
|
||||||
if (newValue < 0)
|
if (newValue < 0)
|
||||||
{
|
{
|
||||||
|
// invalid value
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var damageClass = type.ToClass();
|
foreach (var type in SupportedDamageTypes)
|
||||||
|
|
||||||
if (SupportedClasses.Contains(damageClass))
|
|
||||||
{
|
{
|
||||||
var old = _damageList[type] = newValue;
|
TrySetDamage(type, newValue);
|
||||||
_damageList[type] = newValue;
|
|
||||||
|
|
||||||
var delta = newValue - old;
|
|
||||||
var datum = new DamageChangeData(type, newValue, delta);
|
|
||||||
var data = new List<DamageChangeData> {datum};
|
|
||||||
|
|
||||||
OnHealthChanged(data);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Heal(DamageType type)
|
public bool TryChangeDamage(DamageTypePrototype type, int amount, bool ignoreDamageResistances = false)
|
||||||
{
|
{
|
||||||
SetDamage(type, 0);
|
// Check if damage type is supported, and get the current value if it is.
|
||||||
}
|
if (!GetDamagePerType.TryGetValue(type, out var current))
|
||||||
|
|
||||||
public void Heal()
|
|
||||||
{
|
|
||||||
foreach (var type in SupportedTypes)
|
|
||||||
{
|
|
||||||
Heal(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ChangeDamage(
|
|
||||||
DamageType type,
|
|
||||||
int amount,
|
|
||||||
bool ignoreResistances,
|
|
||||||
IEntity? source = null,
|
|
||||||
DamageChangeParams? extraParams = null)
|
|
||||||
{
|
|
||||||
if (!SupportsDamageType(type))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (amount == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply resistances (does nothing if amount<0)
|
||||||
var finalDamage = amount;
|
var finalDamage = amount;
|
||||||
|
if (!ignoreDamageResistances)
|
||||||
if (!ignoreResistances)
|
|
||||||
{
|
{
|
||||||
finalDamage = Resistances.CalculateDamage(type, amount);
|
finalDamage = Resistances.CalculateDamage(type, amount);
|
||||||
}
|
}
|
||||||
@@ -216,24 +229,23 @@ namespace Content.Shared.Damage.Components
|
|||||||
if (finalDamage == 0)
|
if (finalDamage == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!_damageList.TryGetValue(type, out var current))
|
// Are we healing below zero?
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current + finalDamage < 0)
|
if (current + finalDamage < 0)
|
||||||
{
|
{
|
||||||
if (current == 0)
|
if (current == 0)
|
||||||
|
// Damage type is supported, but there is nothing to do
|
||||||
return false;
|
return false;
|
||||||
_damageList[type] = 0;
|
|
||||||
|
// Cap healing down to zero
|
||||||
|
_damageDict[type] = 0;
|
||||||
finalDamage = -current;
|
finalDamage = -current;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_damageList[type] = current + finalDamage;
|
_damageDict[type] = current + finalDamage;
|
||||||
}
|
}
|
||||||
|
|
||||||
current = _damageList[type];
|
current = _damageDict[type];
|
||||||
|
|
||||||
var datum = new DamageChangeData(type, current, finalDamage);
|
var datum = new DamageChangeData(type, current, finalDamage);
|
||||||
var data = new List<DamageChangeData> {datum};
|
var data = new List<DamageChangeData> {datum};
|
||||||
@@ -243,107 +255,117 @@ namespace Content.Shared.Damage.Components
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ChangeDamage(DamageClass @class, int amount, bool ignoreResistances,
|
public bool TryChangeDamage(DamageGroupPrototype group, int amount, bool ignoreDamageResistances = false)
|
||||||
IEntity? source = null,
|
|
||||||
DamageChangeParams? extraParams = null)
|
|
||||||
{
|
{
|
||||||
if (!SupportsDamageClass(@class))
|
var types = group.DamageTypes.ToArray();
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var types = @class.ToTypes();
|
|
||||||
|
|
||||||
if (amount < 0)
|
if (amount < 0)
|
||||||
{
|
{
|
||||||
// Changing multiple types is a bit more complicated. Might be a better way (formula?) to do this,
|
// We are Healing. Keep track of how much we can hand out (with a better var name for readability).
|
||||||
// but essentially just loops between each damage category until all healing is used up.
|
var availableHealing = -amount;
|
||||||
var healingLeft = -amount;
|
|
||||||
var healThisCycle = 1;
|
|
||||||
|
|
||||||
// While we have healing left...
|
// Get total group damage.
|
||||||
while (healingLeft > 0 && healThisCycle != 0)
|
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)
|
||||||
{
|
{
|
||||||
// Infinite loop fallback, if no healing was done in a cycle
|
TrySetDamage(group, 0);
|
||||||
// then exit
|
return true;
|
||||||
healThisCycle = 0;
|
|
||||||
|
|
||||||
int healPerType;
|
|
||||||
if (healingLeft < types.Count)
|
|
||||||
{
|
|
||||||
// Say we were to distribute 2 healing between 3
|
|
||||||
// this will distribute 1 to each (and stop after 2 are given)
|
|
||||||
healPerType = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Say we were to distribute 62 healing between 3
|
|
||||||
// this will distribute 20 to each, leaving 2 for next loop
|
|
||||||
healPerType = healingLeft / types.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
var damage = GetDamage(type);
|
|
||||||
var healAmount = Math.Min(healingLeft, damage);
|
|
||||||
healAmount = Math.Min(healAmount, healPerType);
|
|
||||||
|
|
||||||
ChangeDamage(type, -healAmount, true);
|
|
||||||
healThisCycle += healAmount;
|
|
||||||
healingLeft -= healAmount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var damageLeft = amount;
|
|
||||||
|
|
||||||
while (damageLeft > 0)
|
|
||||||
{
|
|
||||||
int damagePerType;
|
|
||||||
|
|
||||||
if (damageLeft < types.Count)
|
|
||||||
{
|
|
||||||
damagePerType = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
damagePerType = damageLeft / types.Count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Partially heal each damage group
|
||||||
|
int healing, damage;
|
||||||
foreach (var type in types)
|
foreach (var type in types)
|
||||||
{
|
{
|
||||||
var damageAmount = Math.Min(damagePerType, damageLeft);
|
if (!_damageDict.TryGetValue(type, out damage))
|
||||||
ChangeDamage(type, damageAmount, true);
|
{
|
||||||
damageLeft -= damageAmount;
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
// amount==0 no damage change.
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool SetDamage(DamageType type, int newValue, IEntity? source = null, DamageChangeParams? extraParams = null)
|
public bool TrySetDamage(DamageTypePrototype type, int newValue)
|
||||||
{
|
{
|
||||||
if (newValue >= TotalDamage)
|
if (!_damageDict.TryGetValue(type, out var oldValue))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newValue < 0)
|
if (newValue < 0)
|
||||||
{
|
{
|
||||||
|
// invalid value
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_damageList.ContainsKey(type))
|
if (oldValue == newValue)
|
||||||
{
|
{
|
||||||
return false;
|
// No health change.
|
||||||
|
// But we are trying to set, not trying to change.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var old = _damageList[type];
|
_damageDict[type] = newValue;
|
||||||
_damageList[type] = newValue;
|
|
||||||
|
|
||||||
var delta = newValue - old;
|
var delta = newValue - oldValue;
|
||||||
var datum = new DamageChangeData(type, 0, delta);
|
var datum = new DamageChangeData(type, 0, delta);
|
||||||
var data = new List<DamageChangeData> {datum};
|
var data = new List<DamageChangeData> {datum};
|
||||||
|
|
||||||
@@ -356,7 +378,7 @@ namespace Content.Shared.Damage.Components
|
|||||||
{
|
{
|
||||||
var data = new List<DamageChangeData>();
|
var data = new List<DamageChangeData>();
|
||||||
|
|
||||||
foreach (var type in SupportedTypes)
|
foreach (var type in SupportedDamageTypes)
|
||||||
{
|
{
|
||||||
var damage = GetDamage(type);
|
var damage = GetDamage(type);
|
||||||
var datum = new DamageChangeData(type, damage, 0);
|
var datum = new DamageChangeData(type, damage, 0);
|
||||||
@@ -382,11 +404,15 @@ namespace Content.Shared.Damage.Components
|
|||||||
Dirty();
|
Dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRadiationAct.RadiationAct(float frameTime, SharedRadiationPulseComponent radiation)
|
public void RadiationAct(float frameTime, SharedRadiationPulseComponent radiation)
|
||||||
{
|
{
|
||||||
var totalDamage = Math.Max((int)(frameTime * radiation.RadsPerSecond), 1);
|
var totalDamage = Math.Max((int)(frameTime * radiation.RadsPerSecond), 1);
|
||||||
|
|
||||||
ChangeDamage(DamageType.Radiation, totalDamage, false, radiation.Owner);
|
foreach (var typeID in RadiationDamageTypeIDs)
|
||||||
|
{
|
||||||
|
TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(typeID), totalDamage);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnExplosion(ExplosionEventArgs eventArgs)
|
public void OnExplosion(ExplosionEventArgs eventArgs)
|
||||||
@@ -399,19 +425,75 @@ namespace Content.Shared.Damage.Components
|
|||||||
_ => throw new ArgumentOutOfRangeException()
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
};
|
};
|
||||||
|
|
||||||
ChangeDamage(DamageType.Piercing, damage, false);
|
foreach (var typeID in ExplosionDamageTypeIDs)
|
||||||
ChangeDamage(DamageType.Heat, damage, false);
|
{
|
||||||
|
TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(typeID), damage);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Take a dictionary with <see cref="IPrototype"/> keys and return a dictionary using <see cref="IPrototype.ID"/> as keys
|
||||||
|
/// instead.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Useful when sending damage type and group prototypes dictionaries over the network.
|
||||||
|
/// </remarks>
|
||||||
|
public static IReadOnlyDictionary<string, int>
|
||||||
|
ConvertDictKeysToIDs<TPrototype>(IReadOnlyDictionary<TPrototype, int> prototypeDict)
|
||||||
|
where TPrototype : IPrototype
|
||||||
|
{
|
||||||
|
Dictionary<string, int> idDict = new(prototypeDict.Count);
|
||||||
|
foreach (var entry in prototypeDict)
|
||||||
|
{
|
||||||
|
idDict.Add(entry.Key.ID, entry.Value);
|
||||||
|
}
|
||||||
|
return idDict;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert a dictionary with damage type keys to a dictionary of damage groups keys.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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.
|
||||||
|
/// </remarks>
|
||||||
|
public static IReadOnlyDictionary<DamageGroupPrototype, int>
|
||||||
|
DamageTypeDictToDamageGroupDict(IReadOnlyDictionary<DamageTypePrototype, int> damageTypeDict, IEnumerable<DamageGroupPrototype> groupKeys)
|
||||||
|
{
|
||||||
|
var damageGroupDict = new Dictionary<DamageGroupPrototype, int>();
|
||||||
|
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]
|
[Serializable, NetSerializable]
|
||||||
public class DamageableComponentState : ComponentState
|
public class DamageableComponentState : ComponentState
|
||||||
{
|
{
|
||||||
public readonly Dictionary<DamageType, int> DamageList;
|
public readonly IReadOnlyDictionary<string, int> DamageDict;
|
||||||
|
|
||||||
|
public DamageableComponentState(IReadOnlyDictionary<string, int> damageDict)
|
||||||
|
|
||||||
public DamageableComponentState(Dictionary<DamageType, int> damageList)
|
|
||||||
{
|
{
|
||||||
DamageList = damageList;
|
DamageDict = damageDict;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,137 +8,214 @@ namespace Content.Shared.Damage.Components
|
|||||||
public interface IDamageableComponent : IComponent, IExAct
|
public interface IDamageableComponent : IComponent, IExAct
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sum of all damages taken.
|
/// The sum of all damages types in the DamageableComponent.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int TotalDamage { get; }
|
int TotalDamage { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of damage mapped by <see cref="DamageClass"/>.
|
/// Returns a dictionary of the damage in the container, indexed by applicable <see cref="DamageGroupPrototype"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyDictionary<DamageClass, int> DamageClasses { get; }
|
/// <remarks>
|
||||||
|
/// 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 <see cref="GetDamagePerType"/>
|
||||||
|
/// </remarks>
|
||||||
|
IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerApplicableGroup { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of damage mapped by <see cref="DamageType"/>.
|
/// Returns a dictionary of the damage in the container, indexed by fully supported instances of <see
|
||||||
|
/// cref="DamageGroupPrototype"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IReadOnlyDictionary<DamageType, int> DamageTypes { get; }
|
/// <remarks>
|
||||||
|
/// 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 <see cref="GetDamagePerType"/>. 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.
|
||||||
|
/// </remarks>
|
||||||
|
IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerFullySupportedGroup { get; }
|
||||||
|
|
||||||
HashSet<DamageType> SupportedTypes { get; }
|
/// <summary>
|
||||||
|
/// Returns a dictionary of the damage in the container, indexed by <see cref="DamageTypePrototype"/>.
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyDictionary<DamageTypePrototype, int> GetDamagePerType { get; }
|
||||||
|
|
||||||
HashSet<DamageClass> SupportedClasses { get; }
|
/// <summary>
|
||||||
|
/// Like <see cref="GetDamagePerApplicableGroup"/>, but indexed by <see cref="DamageGroupPrototype.ID"/>
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyDictionary<string, int> GetDamagePerApplicableGroupIDs { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Like <see cref="GetDamagePerFullySupportedGroup"/>, but indexed by <see cref="DamageGroupPrototype.ID"/>
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyDictionary<string, int> GetDamagePerFullySupportedGroupIDs { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Like <see cref="GetDamagePerType"/>, but indexed by <see cref="DamageTypePrototype.ID"/>
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyDictionary<string, int> GetDamagePerTypeIDs { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collection of damage types supported by this DamageableComponent.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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 <see cref="ApplicableDamageGroups"></see>
|
||||||
|
/// </remarks>
|
||||||
|
HashSet<DamageTypePrototype> SupportedDamageTypes { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collection of damage groups that are fully supported by DamageableComponent.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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 <see cref="SupportedDamageTypes"/>, and to see all applicable damage groups <see
|
||||||
|
/// cref="ApplicableDamageGroups"/>.
|
||||||
|
/// </remarks>
|
||||||
|
HashSet<DamageGroupPrototype> FullySupportedDamageGroups { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collection of damage groups that could apply damage to this DamageableComponent.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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 <see cref="SupportedDamageTypes"/>.
|
||||||
|
/// </remarks>
|
||||||
|
HashSet<DamageGroupPrototype> ApplicableDamageGroups { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The resistances of this component.
|
/// The resistances of this component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ResistanceSet Resistances { get; }
|
ResistanceSet Resistances { get; }
|
||||||
|
|
||||||
bool SupportsDamageClass(DamageClass @class);
|
|
||||||
|
|
||||||
bool SupportsDamageType(DamageType type);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the amount of damage of a type.
|
/// Tries to get the amount of damage of a type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">The type to get the damage of.</param>
|
/// <param name="type">The type to get the damage of.</param>
|
||||||
/// <param name="damage">The amount of damage of that type.</param>
|
/// <param name="damage">The amount of damage of that type.</param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// True if the given <see cref="type"/> is supported, false otherwise.
|
/// True if the given <see cref="type"/> is supported, false otherwise.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
bool TryGetDamage(DamageType type, out int damage);
|
bool TryGetDamage(DamageTypePrototype type, out int damage);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the amount of damage of a class.
|
/// Returns the amount of damage of a given type, or zero if it is not supported.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="class">The class to get the damage of.</param>
|
int GetDamage(DamageTypePrototype type);
|
||||||
/// <param name="damage">The amount of damage of that class.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// True if the given <see cref="@class"/> is supported, false otherwise.
|
|
||||||
/// </returns>
|
|
||||||
bool TryGetDamage(DamageClass @class, out int damage);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the specified <see cref="DamageType"/>, applying
|
/// Tries to get the total amount of damage in a damage group.
|
||||||
/// resistance values only if it is damage.
|
/// </summary>
|
||||||
|
/// <param name="group">The group to get the damage of.</param>
|
||||||
|
/// <param name="damage">The amount of damage in that group.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// True if the given group is applicable to this container, false otherwise.
|
||||||
|
/// </returns>
|
||||||
|
bool TryGetDamage(DamageGroupPrototype group, out int damage);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the amount of damage present in an applicable group, or zero if no members are supported.
|
||||||
|
/// </summary>
|
||||||
|
int GetDamage(DamageGroupPrototype group);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to change the specified <see cref="DamageTypePrototype"/>, applying
|
||||||
|
/// resistance values only if it is dealing damage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">Type of damage being changed.</param>
|
/// <param name="type">Type of damage being changed.</param>
|
||||||
/// <param name="amount">
|
/// <param name="amount">
|
||||||
/// Amount of damage being received (positive for damage, negative for heals).
|
/// Amount of damage being received (positive for damage, negative for heals).
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="ignoreResistances">
|
/// <param name="ignoreDamageResistances">
|
||||||
/// Whether or not to ignore resistances.
|
/// Whether or not to ignore resistances when taking damage.
|
||||||
/// Healing always ignores resistances, regardless of this input.
|
/// Healing always ignores resistances, regardless of this input.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="source">
|
|
||||||
/// The entity that dealt or healed the damage, if any.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="extraParams">
|
|
||||||
/// Extra parameters that some components may require, such as a specific limb to target.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// False if the given type is not supported or improper
|
/// False if the given type is not supported or no damage change occurred; true otherwise.
|
||||||
/// <see cref="DamageChangeParams"/> were provided; true otherwise.
|
|
||||||
/// </returns>
|
/// </returns>
|
||||||
bool ChangeDamage(
|
bool TryChangeDamage(DamageTypePrototype type, int amount, bool ignoreDamageResistances = false);
|
||||||
DamageType type,
|
|
||||||
int amount,
|
|
||||||
bool ignoreResistances,
|
|
||||||
IEntity? source = null,
|
|
||||||
DamageChangeParams? extraParams = null);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the specified <see cref="DamageClass"/>, applying
|
/// Tries to change damage of the specified <see cref="DamageGroupPrototype"/>, applying resistance values
|
||||||
/// resistance values only if it is damage.
|
/// only if it is damage.
|
||||||
/// Spreads amount evenly between the <see cref="DamageType"></see>s
|
|
||||||
/// represented by that class.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="class">Class of damage being changed.</param>
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
|
/// If dealing damage, this spreads the damage change amount evenly between the <see
|
||||||
|
/// cref="DamageTypePrototype"></see>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).
|
||||||
|
/// </para>
|
||||||
|
/// <para>
|
||||||
|
/// If healing damage, this spreads the damage change proportional to the current damage value of each <see
|
||||||
|
/// cref="DamageTypePrototype"></see> (subject to integer rounding). If there is less damage than is being
|
||||||
|
/// healed, some healing is wasted. Unsupported damage types do not waste healing.
|
||||||
|
/// </para>
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="group">group of damage being changed.</param>
|
||||||
/// <param name="amount">
|
/// <param name="amount">
|
||||||
/// Amount of damage being received (positive for damage, negative for heals).
|
/// Amount of damage being received (positive for damage, negative for heals).
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="ignoreResistances">
|
/// <param name="ignoreDamageResistances">
|
||||||
/// Whether to ignore resistances.
|
/// Whether to ignore resistances when taking damage. Healing always ignores resistances, regardless of this
|
||||||
/// Healing always ignores resistances, regardless of this input.
|
/// input.
|
||||||
/// </param>
|
|
||||||
/// <param name="source">Entity that dealt or healed the damage, if any.</param>
|
|
||||||
/// <param name="extraParams">
|
|
||||||
/// Extra parameters that some components may require,
|
|
||||||
/// such as a specific limb to target.
|
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// Returns false if the given class is not supported or improper
|
/// Returns false if the given group is not applicable or no damage change occurred; true otherwise.
|
||||||
/// <see cref="DamageChangeParams"/> were provided; true otherwise.
|
|
||||||
/// </returns>
|
/// </returns>
|
||||||
bool ChangeDamage(
|
bool TryChangeDamage(DamageGroupPrototype group, int amount, bool ignoreDamageResistances = false);
|
||||||
DamageClass @class,
|
|
||||||
int amount,
|
|
||||||
bool ignoreResistances,
|
|
||||||
IEntity? source = null,
|
|
||||||
DamageChangeParams? extraParams = null);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Forcefully sets the specified <see cref="DamageType"/> to the given
|
/// Forcefully sets the specified <see cref="DamageTypePrototype"/> to the given value, ignoring resistance
|
||||||
/// value, ignoring resistance values.
|
/// values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">Type of damage being changed.</param>
|
/// <param name="type">Type of damage being set.</param>
|
||||||
/// <param name="newValue">New damage value to be set.</param>
|
/// <param name="newValue">New damage value to be set.</param>
|
||||||
/// <param name="source">Entity that set the new damage value.</param>
|
|
||||||
/// <param name="extraParams">
|
|
||||||
/// Extra parameters that some components may require,
|
|
||||||
/// such as a specific limb to target.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// Returns false if the given type is not supported or improper
|
/// Returns false if a given type is not supported or a negative value is provided; true otherwise.
|
||||||
/// <see cref="DamageChangeParams"/> were provided; true otherwise.
|
|
||||||
/// </returns>
|
/// </returns>
|
||||||
bool SetDamage(
|
bool TrySetDamage(DamageTypePrototype type, int newValue);
|
||||||
DamageType type,
|
|
||||||
int newValue,
|
|
||||||
IEntity? source = null,
|
|
||||||
DamageChangeParams? extraParams = null);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets all damage values to zero.
|
/// Forcefully sets all damage types in a specified damage group using <see cref="TrySetDamage"></see>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Heal();
|
/// <remarks>
|
||||||
|
/// 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.
|
||||||
|
/// </remarks>
|
||||||
|
/// <param name="group">Group of damage being set.</param>
|
||||||
|
/// <param name="newValue">New damage value to be set.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Returns false if the given group is not applicable or a negative value is provided; true otherwise.
|
||||||
|
/// </returns>
|
||||||
|
bool TrySetDamage(DamageGroupPrototype group, int newValue);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets all supported damage types to specified value using <see cref="TrySetDamage"></see>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="newValue">New damage value to be set.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// Returns false if a negative value is provided; true otherwise.
|
||||||
|
/// </returns>
|
||||||
|
bool TrySetAllDamage(int newValue);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the given damage group is applicable to this damage container.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsApplicableDamageGroup(DamageGroupPrototype group);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the given damage group is fully supported by this damage container.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFullySupportedDamageGroup(DamageGroupPrototype group);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the given damage type is supported by this damage container.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSupportedDamageType(DamageTypePrototype type);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invokes the HealthChangedEvent with the current values of health.
|
/// Invokes the HealthChangedEvent with the current values of health.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
@@ -8,45 +9,142 @@ using Robust.Shared.ViewVariables;
|
|||||||
namespace Content.Shared.Damage.Container
|
namespace Content.Shared.Damage.Container
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prototype for the DamageContainer class.
|
/// A damage container which can be used to specify support for various damage types.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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 <see
|
||||||
|
/// cref="Components.DamageableComponent"/> should support.
|
||||||
|
/// </remarks>
|
||||||
[Prototype("damageContainer")]
|
[Prototype("damageContainer")]
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public class DamageContainerPrototype : IPrototype, ISerializationHooks
|
public class DamageContainerPrototype : IPrototype, ISerializationHooks
|
||||||
{
|
{
|
||||||
[DataField("supportAll")] private bool _supportAll;
|
private IPrototypeManager _prototypeManager = default!;
|
||||||
[DataField("supportedClasses")] private HashSet<DamageClass> _supportedClasses = new();
|
|
||||||
[DataField("supportedTypes")] private HashSet<DamageType> _supportedTypes = new();
|
|
||||||
|
|
||||||
// TODO NET 5 IReadOnlySet
|
|
||||||
[ViewVariables] public IReadOnlyCollection<DamageClass> SupportedClasses => _supportedClasses;
|
|
||||||
|
|
||||||
[ViewVariables] public IReadOnlyCollection<DamageType> SupportedTypes => _supportedTypes;
|
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
[DataField("id", required: true)]
|
[DataField("id", required: true)]
|
||||||
public string ID { get; } = default!;
|
public string ID { get; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether this DamageContainerPrototype will support ALL damage types and groups. If true,
|
||||||
|
/// ignore all other options.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("supportAll")] private bool _supportAll;
|
||||||
|
|
||||||
|
[DataField("supportedGroups")] private HashSet<string> _supportedDamageGroupIDs = new();
|
||||||
|
[DataField("supportedTypes")] private HashSet<string> _supportedDamageTypeIDs = new();
|
||||||
|
|
||||||
|
private HashSet<DamageGroupPrototype> _applicableDamageGroups = new();
|
||||||
|
private HashSet<DamageGroupPrototype> _fullySupportedDamageGroups = new();
|
||||||
|
private HashSet<DamageTypePrototype> _supportedDamageTypes = new();
|
||||||
|
|
||||||
|
// TODO NET 5 IReadOnlySet
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collection of damage groups that can affect this container.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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 <see cref="SupportedDamageTypes"/>.
|
||||||
|
/// </remarks>
|
||||||
|
[ViewVariables] public IReadOnlyCollection<DamageGroupPrototype> ApplicableDamageGroups => _applicableDamageGroups;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collection of damage groups that are fully supported by this container.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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 <see cref="SupportedDamageTypes"/>, and to see all applicable damage groups <see
|
||||||
|
/// cref="ApplicableDamageGroups"/>.
|
||||||
|
/// </remarks>
|
||||||
|
[ViewVariables] public IReadOnlyCollection<DamageGroupPrototype> FullySupportedDamageGroups => _fullySupportedDamageGroups;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collection of damage types supported by this container.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 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 <see cref="ApplicableDamageGroups"></see>
|
||||||
|
/// </remarks>
|
||||||
|
[ViewVariables] public IReadOnlyCollection<DamageTypePrototype> SupportedDamageTypes => _supportedDamageTypes;
|
||||||
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
void ISerializationHooks.AfterDeserialization()
|
||||||
{
|
{
|
||||||
|
_prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
|
|
||||||
if (_supportAll)
|
if (_supportAll)
|
||||||
{
|
{
|
||||||
_supportedClasses.UnionWith(Enum.GetValues<DamageClass>());
|
foreach (var group in _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
||||||
_supportedTypes.UnionWith(Enum.GetValues<DamageType>());
|
{
|
||||||
|
_applicableDamageGroups.Add(group);
|
||||||
|
_fullySupportedDamageGroups.Add(group);
|
||||||
|
}
|
||||||
|
foreach (var type in _prototypeManager.EnumeratePrototypes<DamageTypePrototype>())
|
||||||
|
{
|
||||||
|
_supportedDamageTypes.Add(type);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var supportedClass in _supportedClasses)
|
// Add fully supported damage groups
|
||||||
|
foreach (var groupID in _supportedDamageGroupIDs)
|
||||||
{
|
{
|
||||||
foreach (var supportedType in supportedClass.ToTypes())
|
var group = _prototypeManager.Index<DamageGroupPrototype>(groupID);
|
||||||
|
_fullySupportedDamageGroups.Add(group);
|
||||||
|
foreach (var type in group.DamageTypes)
|
||||||
{
|
{
|
||||||
_supportedTypes.Add(supportedType);
|
_supportedDamageTypes.Add(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var originalType in _supportedTypes)
|
// 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)
|
||||||
{
|
{
|
||||||
_supportedClasses.Add(originalType.ToClass());
|
var type = _prototypeManager.Index<DamageTypePrototype>(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<DamageGroupPrototype>())
|
||||||
|
{
|
||||||
|
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<DamageGroupPrototype>())
|
||||||
|
{
|
||||||
|
if (group.DamageTypes.Contains(type))
|
||||||
|
{
|
||||||
|
_applicableDamageGroups.Add(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data class with information on how the value of a
|
/// Data class with information on how the value of a
|
||||||
/// single <see cref="DamageType"/> has changed.
|
/// single <see cref="DamageTypePrototype"/> has changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct DamageChangeData
|
public struct DamageChangeData
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Type of damage that changed.
|
/// Type of damage that changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DamageType Type;
|
public DamageTypePrototype Type;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The new current value for that damage.
|
/// The new current value for that damage.
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int Delta;
|
public int Delta;
|
||||||
|
|
||||||
public DamageChangeData(DamageType type, int newValue, int delta)
|
public DamageChangeData(DamageTypePrototype type, int newValue, int delta)
|
||||||
{
|
{
|
||||||
Type = type;
|
Type = type;
|
||||||
NewValue = newValue;
|
NewValue = newValue;
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Content.Shared.Body.Components;
|
|
||||||
using Content.Shared.Damage.Components;
|
|
||||||
|
|
||||||
namespace Content.Shared.Damage
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Data class with information on how to damage a
|
|
||||||
/// <see cref="IDamageableComponent"/>.
|
|
||||||
/// While not necessary to damage for all instances, classes such as
|
|
||||||
/// <see cref="SharedBodyComponent"/> may require it for extra data
|
|
||||||
/// (such as selecting which limb to target).
|
|
||||||
/// </summary>
|
|
||||||
// TODO BODY: Remove and pretend it never existed
|
|
||||||
public class DamageChangeParams : EventArgs
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,7 +12,7 @@ namespace Content.Shared.Damage
|
|||||||
Data = data;
|
Data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DamageChangedEventArgs(IDamageableComponent damageable, DamageType type, int newValue, int delta)
|
public DamageChangedEventArgs(IDamageableComponent damageable, DamageTypePrototype type, int newValue, int delta)
|
||||||
{
|
{
|
||||||
Damageable = damageable;
|
Damageable = damageable;
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ namespace Content.Shared.Damage
|
|||||||
public IDamageableComponent Damageable { get; }
|
public IDamageableComponent Damageable { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List containing data on each <see cref="DamageType"/> that was changed.
|
/// List containing data on each <see cref="DamageTypePrototype"/> that was changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyList<DamageChangeData> Data { get; }
|
public IReadOnlyList<DamageChangeData> Data { get; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Content.Shared.Damage
|
|||||||
Data = data;
|
Data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DamageChangedMessage(IDamageableComponent damageable, DamageType type, int newValue, int delta)
|
public DamageChangedMessage(IDamageableComponent damageable, DamageTypePrototype type, int newValue, int delta)
|
||||||
{
|
{
|
||||||
Damageable = damageable;
|
Damageable = damageable;
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ namespace Content.Shared.Damage
|
|||||||
public IDamageableComponent Damageable { get; }
|
public IDamageableComponent Damageable { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List containing data on each <see cref="DamageType"/> that was changed.
|
/// List containing data on each <see cref="DamageTypePrototype"/> that was changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyList<DamageChangeData> Data { get; }
|
public IReadOnlyList<DamageChangeData> Data { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Immutable;
|
|
||||||
using System.Linq;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Damage
|
|
||||||
{
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public enum DamageClass
|
|
||||||
{
|
|
||||||
Brute,
|
|
||||||
Burn,
|
|
||||||
Toxin,
|
|
||||||
Airloss,
|
|
||||||
Genetic
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DamageClassExtensions
|
|
||||||
{
|
|
||||||
public static ImmutableList<DamageType> ToTypes(this DamageClass @class)
|
|
||||||
{
|
|
||||||
return DamageSystem.ClassToType[@class];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Dictionary<DamageClass, T> ToNewDictionary<T>() where T : struct
|
|
||||||
{
|
|
||||||
return Enum.GetValues(typeof(DamageClass))
|
|
||||||
.Cast<DamageClass>()
|
|
||||||
.ToDictionary(@class => @class, _ => default(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Dictionary<DamageClass, int> ToNewDictionary()
|
|
||||||
{
|
|
||||||
return ToNewDictionary<int>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
42
Content.Shared/Damage/DamageGroupPrototype.cs
Normal file
42
Content.Shared/Damage/DamageGroupPrototype.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A Group of <see cref="DamageTypePrototype"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// These groups can be used to specify supported damage types of a <see
|
||||||
|
/// cref="Container.DamageContainerPrototype"/>, or to change/get/set damage in a <see
|
||||||
|
/// cref="Components.DamageableComponent"/>.
|
||||||
|
/// </remarks>
|
||||||
|
[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<string> TypeIDs { get; } = default!;
|
||||||
|
|
||||||
|
public HashSet<DamageTypePrototype> DamageTypes { get; } = new();
|
||||||
|
|
||||||
|
// Create set of damage types
|
||||||
|
void ISerializationHooks.AfterDeserialization()
|
||||||
|
{
|
||||||
|
_prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
|
|
||||||
|
foreach (var typeID in TypeIDs)
|
||||||
|
{
|
||||||
|
DamageTypes.Add(_prototypeManager.Index<DamageTypePrototype>(typeID));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,59 +8,6 @@ namespace Content.Shared.Damage
|
|||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public class DamageSystem : EntitySystem
|
public class DamageSystem : EntitySystem
|
||||||
{
|
{
|
||||||
public static ImmutableDictionary<DamageClass, ImmutableList<DamageType>> ClassToType { get; } = DefaultClassToType();
|
|
||||||
|
|
||||||
public static ImmutableDictionary<DamageType, DamageClass> TypeToClass { get; } = DefaultTypeToClass();
|
|
||||||
|
|
||||||
private static ImmutableDictionary<DamageClass, ImmutableList<DamageType>> DefaultClassToType()
|
|
||||||
{
|
|
||||||
return new Dictionary<DamageClass, ImmutableList<DamageType>>
|
|
||||||
{
|
|
||||||
[DamageClass.Brute] = new List<DamageType>
|
|
||||||
{
|
|
||||||
DamageType.Blunt,
|
|
||||||
DamageType.Slash,
|
|
||||||
DamageType.Piercing
|
|
||||||
}.ToImmutableList(),
|
|
||||||
[DamageClass.Burn] = new List<DamageType>
|
|
||||||
{
|
|
||||||
DamageType.Heat,
|
|
||||||
DamageType.Shock,
|
|
||||||
DamageType.Cold
|
|
||||||
}.ToImmutableList(),
|
|
||||||
[DamageClass.Toxin] = new List<DamageType>
|
|
||||||
{
|
|
||||||
DamageType.Poison,
|
|
||||||
DamageType.Radiation
|
|
||||||
}.ToImmutableList(),
|
|
||||||
[DamageClass.Airloss] = new List<DamageType>
|
|
||||||
{
|
|
||||||
DamageType.Asphyxiation,
|
|
||||||
DamageType.Bloodloss
|
|
||||||
}.ToImmutableList(),
|
|
||||||
[DamageClass.Genetic] = new List<DamageType>
|
|
||||||
{
|
|
||||||
DamageType.Cellular
|
|
||||||
}.ToImmutableList()
|
|
||||||
}.ToImmutableDictionary();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ImmutableDictionary<DamageType, DamageClass> DefaultTypeToClass()
|
|
||||||
{
|
|
||||||
return new Dictionary<DamageType, DamageClass>
|
|
||||||
{
|
|
||||||
{DamageType.Blunt, DamageClass.Brute},
|
|
||||||
{DamageType.Slash, DamageClass.Brute},
|
|
||||||
{DamageType.Piercing, DamageClass.Brute},
|
|
||||||
{DamageType.Heat, DamageClass.Burn},
|
|
||||||
{DamageType.Shock, DamageClass.Burn},
|
|
||||||
{DamageType.Cold, DamageClass.Burn},
|
|
||||||
{DamageType.Poison, DamageClass.Toxin},
|
|
||||||
{DamageType.Radiation, DamageClass.Toxin},
|
|
||||||
{DamageType.Asphyxiation, DamageClass.Airloss},
|
|
||||||
{DamageType.Bloodloss, DamageClass.Airloss},
|
|
||||||
{DamageType.Cellular, DamageClass.Genetic}
|
|
||||||
}.ToImmutableDictionary();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Damage
|
|
||||||
{
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public enum DamageType
|
|
||||||
{
|
|
||||||
Blunt,
|
|
||||||
Slash,
|
|
||||||
Piercing,
|
|
||||||
Heat,
|
|
||||||
Shock,
|
|
||||||
Cold,
|
|
||||||
Poison,
|
|
||||||
Radiation,
|
|
||||||
Asphyxiation,
|
|
||||||
Bloodloss,
|
|
||||||
Cellular
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DamageTypeExtensions
|
|
||||||
{
|
|
||||||
public static DamageClass ToClass(this DamageType type)
|
|
||||||
{
|
|
||||||
return DamageSystem.TypeToClass[type];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Dictionary<DamageType, T> ToNewDictionary<T>() where T : struct
|
|
||||||
{
|
|
||||||
return Enum.GetValues(typeof(DamageType))
|
|
||||||
.Cast<DamageType>()
|
|
||||||
.ToDictionary(type => type, _ => default(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Dictionary<DamageType, int> ToNewDictionary()
|
|
||||||
{
|
|
||||||
return ToNewDictionary<int>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Dictionary<DamageClass, int> ToClassDictionary(this IReadOnlyDictionary<DamageType, int> types)
|
|
||||||
{
|
|
||||||
var classes = DamageClassExtensions.ToNewDictionary();
|
|
||||||
|
|
||||||
foreach (var @class in classes.Keys.ToList())
|
|
||||||
{
|
|
||||||
foreach (var type in @class.ToTypes())
|
|
||||||
{
|
|
||||||
if (!types.TryGetValue(type, out var damage))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
classes[@class] += damage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return classes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
18
Content.Shared/Damage/DamageTypePrototype.cs
Normal file
18
Content.Shared/Damage/DamageTypePrototype.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Damage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A single damage type. These types are grouped together in <see cref="DamageGroupPrototype"/>s.
|
||||||
|
/// </summary>
|
||||||
|
[Prototype("damageType")]
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class DamageTypePrototype : IPrototype
|
||||||
|
{
|
||||||
|
[DataField("id", required: true)]
|
||||||
|
public string ID { get; } = default!;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
@@ -7,18 +9,21 @@ namespace Content.Shared.Damage.Resistances
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set of resistances used by damageable objects.
|
/// Set of resistances used by damageable objects.
|
||||||
/// Each <see cref="DamageType"/> has a multiplier and flat damage
|
/// Each <see cref="DamageTypePrototype"/> has a multiplier and flat damage
|
||||||
/// reduction value.
|
/// reduction value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public class ResistanceSet
|
public class ResistanceSet
|
||||||
{
|
{
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string? ID { get; } = string.Empty;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public Dictionary<DamageTypePrototype, ResistanceSetSettings> Resistances { get; } = new();
|
||||||
|
|
||||||
public ResistanceSet()
|
public ResistanceSet()
|
||||||
{
|
{
|
||||||
foreach (var damageType in (DamageType[]) Enum.GetValues(typeof(DamageType)))
|
|
||||||
{
|
|
||||||
Resistances.Add(damageType, new ResistanceSetSettings(1f, 0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResistanceSet(ResistanceSetPrototype data)
|
public ResistanceSet(ResistanceSetPrototype data)
|
||||||
@@ -27,12 +32,6 @@ namespace Content.Shared.Damage.Resistances
|
|||||||
Resistances = data.Resistances;
|
Resistances = data.Resistances;
|
||||||
}
|
}
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public string ID { get; } = string.Empty;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public Dictionary<DamageType, ResistanceSetSettings> Resistances { get; } = new();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adjusts input damage with the resistance set values.
|
/// Adjusts input damage with the resistance set values.
|
||||||
/// Only applies reduction if the amount is damage (positive), not
|
/// Only applies reduction if the amount is damage (positive), not
|
||||||
@@ -40,11 +39,18 @@ namespace Content.Shared.Damage.Resistances
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="damageType">Type of damage.</param>
|
/// <param name="damageType">Type of damage.</param>
|
||||||
/// <param name="amount">Incoming amount of damage.</param>
|
/// <param name="amount">Incoming amount of damage.</param>
|
||||||
public int CalculateDamage(DamageType damageType, int amount)
|
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.
|
if (amount > 0) // Only apply reduction if it's healing, not damage.
|
||||||
{
|
{
|
||||||
amount -= Resistances[damageType].FlatReduction;
|
amount -= resistance.FlatReduction;
|
||||||
|
|
||||||
if (amount <= 0)
|
if (amount <= 0)
|
||||||
{
|
{
|
||||||
@@ -52,25 +58,9 @@ namespace Content.Shared.Damage.Resistances
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
amount = (int) Math.Ceiling(amount * Resistances[damageType].Coefficient);
|
amount = (int) Math.Ceiling(amount * resistance.Coefficient);
|
||||||
|
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Settings for a specific damage type in a resistance set. Flat reduction is applied before the coefficient.
|
|
||||||
/// </summary>
|
|
||||||
[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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.CodeDom;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
@@ -14,29 +16,46 @@ namespace Content.Shared.Damage.Resistances
|
|||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public class ResistanceSetPrototype : IPrototype, ISerializationHooks
|
public class ResistanceSetPrototype : IPrototype, ISerializationHooks
|
||||||
{
|
{
|
||||||
[ViewVariables]
|
|
||||||
[DataField("coefficients")]
|
|
||||||
public Dictionary<DamageType, float> Coefficients { get; } = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("flatReductions")]
|
|
||||||
public Dictionary<DamageType, int> FlatReductions { get; } = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public Dictionary<DamageType, ResistanceSetSettings> Resistances { get; private set; } = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
[DataField("id", required: true)]
|
[DataField("id", required: true)]
|
||||||
public string ID { get; } = default!;
|
public string ID { get; } = default!;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("coefficients", required: true)]
|
||||||
|
private Dictionary<string, float> coefficients { get; } = new();
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("flatReductions", required: true)]
|
||||||
|
private Dictionary<string, int> flatReductions { get; } = new();
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public Dictionary<DamageTypePrototype, ResistanceSetSettings> Resistances { get; private set; } = new();
|
||||||
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
void ISerializationHooks.AfterDeserialization()
|
||||||
{
|
{
|
||||||
Resistances = new Dictionary<DamageType, ResistanceSetSettings>();
|
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
foreach (var damageType in (DamageType[]) Enum.GetValues(typeof(DamageType)))
|
foreach (var damageTypeID in coefficients.Keys)
|
||||||
{
|
{
|
||||||
Resistances.Add(damageType,
|
var resolvedDamageType = prototypeManager.Index<DamageTypePrototype>(damageTypeID);
|
||||||
new ResistanceSetSettings(Coefficients[damageType], FlatReductions[damageType]));
|
Resistances.Add(resolvedDamageType, new ResistanceSetSettings(coefficients[damageTypeID], flatReductions[damageTypeID]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resistance Settings for a specific DamageType. Flat reduction should always be applied before the coefficient.
|
||||||
|
/// </summary>
|
||||||
|
[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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,25 +16,25 @@ namespace Content.Shared.MedicalScanner
|
|||||||
public class MedicalScannerBoundUserInterfaceState : BoundUserInterfaceState
|
public class MedicalScannerBoundUserInterfaceState : BoundUserInterfaceState
|
||||||
{
|
{
|
||||||
public readonly EntityUid? Entity;
|
public readonly EntityUid? Entity;
|
||||||
public readonly Dictionary<DamageClass, int> DamageClasses;
|
public readonly Dictionary<string, int> DamagePerSupportedGroupID;
|
||||||
public readonly Dictionary<DamageType, int> DamageTypes;
|
public readonly Dictionary<string, int> DamagePerTypeID;
|
||||||
public readonly bool IsScanned;
|
public readonly bool IsScanned;
|
||||||
|
|
||||||
public MedicalScannerBoundUserInterfaceState(
|
public MedicalScannerBoundUserInterfaceState(
|
||||||
EntityUid? entity,
|
EntityUid? entity,
|
||||||
Dictionary<DamageClass, int> damageClasses,
|
Dictionary<string, int> damagePerSupportedGroupID,
|
||||||
Dictionary<DamageType, int> damageTypes,
|
Dictionary<string, int> damagePerTypeID,
|
||||||
bool isScanned)
|
bool isScanned)
|
||||||
{
|
{
|
||||||
Entity = entity;
|
Entity = entity;
|
||||||
DamageClasses = damageClasses;
|
DamagePerSupportedGroupID = damagePerSupportedGroupID;
|
||||||
DamageTypes = damageTypes;
|
DamagePerTypeID = damagePerTypeID;
|
||||||
IsScanned = isScanned;
|
IsScanned = isScanned;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasDamage()
|
public bool HasDamage()
|
||||||
{
|
{
|
||||||
return DamageClasses.Count > 0 || DamageTypes.Count > 0;
|
return DamagePerSupportedGroupID.Count > 0 || DamagePerTypeID.Count > 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,5 +16,7 @@ medical-scanner-eject-verb-get-data-text = Eject
|
|||||||
medical-scanner-window-save-button-text = Scan and Save DNA
|
medical-scanner-window-save-button-text = Scan and Save DNA
|
||||||
medical-scanner-window-no-patient-data-text = No patient data.
|
medical-scanner-window-no-patient-data-text = No patient data.
|
||||||
medical-scanner-window-entity-health-text = {$entityName}'s health:
|
medical-scanner-window-entity-health-text = {$entityName}'s health:
|
||||||
medical-scanner-window-damage-class-text = {$damageClass}: {$amount}
|
medical-scanner-window-entity-damage-total-text = Total Damage: {$amount}
|
||||||
|
medical-scanner-window-damage-group-text = {$damageGroup}: {$amount}
|
||||||
medical-scanner-window-damage-type-text = {$damageType}: {$amount}
|
medical-scanner-window-damage-type-text = {$damageType}: {$amount}
|
||||||
|
medical-scanner-window-damage-type-duplicate-text = {$damageType}: {$amount} (duplicate)
|
||||||
|
|||||||
@@ -124,36 +124,36 @@
|
|||||||
Arithrazine:
|
Arithrazine:
|
||||||
effects:
|
effects:
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
damageClass: Toxin
|
damageGroup: Toxin
|
||||||
healthChange: -1
|
healthChange: -1
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
damageClass: Brute
|
damageGroup: Brute
|
||||||
healthChange: 0.5
|
healthChange: 0.5
|
||||||
Bicaridine:
|
Bicaridine:
|
||||||
effects:
|
effects:
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
damageClass: Brute
|
damageGroup: Brute
|
||||||
healthChange: -2
|
healthChange: -2
|
||||||
Dermaline:
|
Dermaline:
|
||||||
effects:
|
effects:
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
damageClass: Burn
|
damageGroup: Burn
|
||||||
healthChange: -3
|
healthChange: -3
|
||||||
Dexalin:
|
Dexalin:
|
||||||
effects:
|
effects:
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
damageClass: Airloss
|
damageGroup: Airloss
|
||||||
healthChange: -1
|
healthChange: -1
|
||||||
DexalinPlus:
|
DexalinPlus:
|
||||||
effects:
|
effects:
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
damageClass: Airloss
|
damageGroup: Airloss
|
||||||
healthChange: -3
|
healthChange: -3
|
||||||
Dylovene:
|
Dylovene:
|
||||||
effects:
|
effects:
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
damageClass: Toxin
|
damageGroup: Toxin
|
||||||
healthChange: -1
|
healthChange: -1
|
||||||
Ephedrine:
|
Ephedrine:
|
||||||
effects:
|
effects:
|
||||||
- !type:MovespeedModifier
|
- !type:MovespeedModifier
|
||||||
@@ -162,23 +162,23 @@
|
|||||||
HeartbreakerToxin:
|
HeartbreakerToxin:
|
||||||
effects:
|
effects:
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
damageClass: Airloss
|
damageGroup: Airloss
|
||||||
healthChange: 4
|
healthChange: 4
|
||||||
Kelotane:
|
Kelotane:
|
||||||
effects:
|
effects:
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
damageClass: Burn
|
damageGroup: Burn
|
||||||
healthChange: -1
|
healthChange: -1
|
||||||
Lexorin:
|
Lexorin:
|
||||||
effects:
|
effects:
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
damageClass: Airloss
|
damageGroup: Airloss
|
||||||
healthChange: 7
|
healthChange: 7
|
||||||
Meth:
|
Meth:
|
||||||
effects:
|
effects:
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
healthChange: 2.5
|
healthChange: 2.5
|
||||||
damageClass: Toxin
|
damageGroup: Toxin
|
||||||
- !type:MovespeedModifier
|
- !type:MovespeedModifier
|
||||||
walkSpeedModifier: 1.3
|
walkSpeedModifier: 1.3
|
||||||
sprintSpeedModifier: 1.3
|
sprintSpeedModifier: 1.3
|
||||||
@@ -186,20 +186,20 @@
|
|||||||
effects:
|
effects:
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
healthChange: -2
|
healthChange: -2
|
||||||
damageClass: Burn
|
damageGroup: Burn
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
healthChange: -2
|
healthChange: -2
|
||||||
damageClass: Toxin
|
damageGroup: Toxin
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
healthChange: -2
|
healthChange: -2
|
||||||
damageClass: Airloss
|
damageGroup: Airloss
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
healthChange: -2
|
healthChange: -2
|
||||||
damageClass: Brute
|
damageGroup: Brute
|
||||||
Synaptizine:
|
Synaptizine:
|
||||||
effects:
|
effects:
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
damageClass: Toxin
|
damageGroup: Toxin
|
||||||
healthChange: 0.5
|
healthChange: 0.5
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -259,7 +259,7 @@
|
|||||||
effects:
|
effects:
|
||||||
- !type:SatiateThirst
|
- !type:SatiateThirst
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
damageClass: Toxin
|
damageGroup: Toxin
|
||||||
healthChange: 1
|
healthChange: 1
|
||||||
JuiceWatermelon:
|
JuiceWatermelon:
|
||||||
effects:
|
effects:
|
||||||
@@ -304,7 +304,7 @@
|
|||||||
- !type:SatiateThirst
|
- !type:SatiateThirst
|
||||||
hydrationFactor: 2
|
hydrationFactor: 2
|
||||||
- !type:HealthChange
|
- !type:HealthChange
|
||||||
damageClass: Toxin
|
damageGroup: Toxin
|
||||||
healthChange: 1
|
healthChange: 1
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
|
|||||||
97
Resources/Prototypes/Damage/damage.yml
Normal file
97
Resources/Prototypes/Damage/damage.yml
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
# 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
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
- type: damageContainer
|
|
||||||
id: allDamageContainer
|
|
||||||
supportAll: true
|
|
||||||
|
|
||||||
- type: damageContainer
|
|
||||||
id: biologicalDamageContainer
|
|
||||||
supportedClasses:
|
|
||||||
- Brute
|
|
||||||
- Burn
|
|
||||||
- Toxin
|
|
||||||
- Airloss
|
|
||||||
- Genetic
|
|
||||||
|
|
||||||
- type: damageContainer
|
|
||||||
id: metallicDamageContainer
|
|
||||||
supportedClasses:
|
|
||||||
- Brute
|
|
||||||
- Burn
|
|
||||||
@@ -34,6 +34,7 @@
|
|||||||
- type: MovementSpeedModifier
|
- type: MovementSpeedModifier
|
||||||
- type: MovedByPressure
|
- type: MovedByPressure
|
||||||
- type: Barotrauma
|
- type: Barotrauma
|
||||||
|
damageType: Blunt
|
||||||
- type: DamageOnHighSpeedImpact
|
- type: DamageOnHighSpeedImpact
|
||||||
soundHit:
|
soundHit:
|
||||||
path: /Audio/Effects/hit_kick.ogg
|
path: /Audio/Effects/hit_kick.ogg
|
||||||
@@ -157,7 +158,9 @@
|
|||||||
fireSpread: true
|
fireSpread: true
|
||||||
canResistFire: true
|
canResistFire: true
|
||||||
- type: Temperature
|
- type: Temperature
|
||||||
|
heatDamageType: Heat
|
||||||
heatDamageThreshold: 360
|
heatDamageThreshold: 360
|
||||||
|
coldDamageType: Cold
|
||||||
coldDamageThreshold: 260
|
coldDamageThreshold: 260
|
||||||
currentTemperature: 310.15
|
currentTemperature: 310.15
|
||||||
specificHeat: 42
|
specificHeat: 42
|
||||||
@@ -191,7 +194,7 @@
|
|||||||
thresholds:
|
thresholds:
|
||||||
- trigger:
|
- trigger:
|
||||||
!type:DamageTypeTrigger
|
!type:DamageTypeTrigger
|
||||||
type: Blunt
|
damageType: Blunt
|
||||||
damage: 400
|
damage: 400
|
||||||
behaviors:
|
behaviors:
|
||||||
- !type:GibBehavior { }
|
- !type:GibBehavior { }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# SKREEEEEEEEEEE
|
# Vox bad. moff best.
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: HumanMob_Content
|
parent: HumanMob_Content
|
||||||
abstract: True
|
abstract: True
|
||||||
|
|||||||
Reference in New Issue
Block a user