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:
Leon Friedrich
2021-08-25 03:06:27 +10:00
committed by GitHub
parent 2a8dc0e9c7
commit 32d99eaba9
71 changed files with 2048 additions and 983 deletions

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Runtime.CompilerServices;
using Content.Server.Alert;
using Content.Server.Pressure;
@@ -7,6 +7,10 @@ using Content.Shared.Atmos;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
namespace Content.Server.Atmos.Components
{
@@ -18,6 +22,17 @@ namespace Content.Server.Atmos.Components
{
public override string Name => "Barotrauma";
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")] private readonly string _damageTypeID = "Blunt";
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Update(float airPressure)
{
@@ -40,11 +55,11 @@ namespace Content.Server.Atmos.Components
// Low pressure.
case var p when p <= Atmospherics.WarningLowPressure:
pressure *= lowPressureMultiplier;
if(pressure > Atmospherics.WarningLowPressure)
if (pressure > Atmospherics.WarningLowPressure)
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;
@@ -66,7 +81,8 @@ namespace Content.Server.Atmos.Components
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;

View File

@@ -20,6 +20,8 @@ using Robust.Shared.Localization;
using Robust.Shared.Physics;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
namespace Content.Server.Atmos.Components
{
@@ -43,6 +45,18 @@ namespace Content.Server.Atmos.Components
[DataField("canResistFire")]
public bool CanResistFire { get; private set; } = false;
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")]
private readonly string _damageTypeID = "Heat"!;
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
}
public void Extinguish()
{
if (!OnFire) return;
@@ -92,7 +106,7 @@ namespace Content.Server.Atmos.Components
{
// TODO ATMOS Fire resistance from armor
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));

View File

@@ -1,4 +1,4 @@
using Content.Shared.Body.Part;
using Content.Shared.Body.Part;
using Content.Shared.Damage;
namespace Content.Server.Body
@@ -10,7 +10,7 @@ namespace Content.Server.Body
}
// TODO BODY: Remove and pretend it never existed
public class BodyDamageChangeParams : DamageChangeParams, IBodyHealthChangeParams
public class BodyDamageChangeParams : IBodyHealthChangeParams
{
public BodyDamageChangeParams(BodyPartType part)
{

View File

@@ -13,6 +13,8 @@ using Content.Shared.Atmos;
using Content.Shared.Body.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Content.Shared.MobState;
using Content.Shared.Notification.Managers;
using Robust.Shared.GameObjects;
@@ -34,10 +36,6 @@ namespace Content.Server.Body.Respiratory
private bool _isShivering;
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("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(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)
{
var needs = new Dictionary<Gas, float>(NeedsGases);
@@ -349,7 +363,7 @@ namespace Content.Server.Body.Respiratory
return;
}
damageable.ChangeDamage(DamageType.Asphyxiation, _suffocationDamage, false);
damageable.TryChangeDamage(DamageType, _damage, false);
}
private void StopSuffocation()
@@ -358,7 +372,7 @@ namespace Content.Server.Body.Respiratory
if (Owner.TryGetComponent(out IDamageableComponent? damageable))
{
damageable.ChangeDamage(DamageType.Asphyxiation, -_suffocationDamageRecovery, false);
damageable.TryChangeDamage(DamageType, -_damageRecovery, false);
}
if (Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent))

View File

@@ -17,6 +17,7 @@ using Robust.Shared.Enums;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
namespace Content.Server.Chat.Commands
{
@@ -34,21 +35,22 @@ namespace Content.Server.Chat.Commands
var kind = suicide.Suicide(target, chat);
if (kind != SuicideKind.Special)
{
damageableComponent.SetDamage(kind switch
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
damageableComponent.TrySetDamage(kind switch
{
SuicideKind.Blunt => DamageType.Blunt,
SuicideKind.Slash => DamageType.Slash,
SuicideKind.Piercing => DamageType.Piercing,
SuicideKind.Heat => DamageType.Heat,
SuicideKind.Shock => DamageType.Shock,
SuicideKind.Cold => DamageType.Cold,
SuicideKind.Poison => DamageType.Poison,
SuicideKind.Radiation => DamageType.Radiation,
SuicideKind.Asphyxiation => DamageType.Asphyxiation,
SuicideKind.Bloodloss => DamageType.Bloodloss,
_ => DamageType.Blunt
SuicideKind.Blunt => prototypeManager.Index<DamageTypePrototype>("Blunt"),
SuicideKind.Slash => prototypeManager.Index<DamageTypePrototype>("Slash"),
SuicideKind.Piercing => prototypeManager.Index<DamageTypePrototype>("Piercing"),
SuicideKind.Heat => prototypeManager.Index<DamageTypePrototype>("Heat"),
SuicideKind.Shock => prototypeManager.Index<DamageTypePrototype>("Shock"),
SuicideKind.Cold => prototypeManager.Index<DamageTypePrototype>("Cold"),
SuicideKind.Poison => prototypeManager.Index<DamageTypePrototype>("Poison"),
SuicideKind.Radiation => prototypeManager.Index<DamageTypePrototype>("Radiation"),
SuicideKind.Asphyxiation => prototypeManager.Index<DamageTypePrototype>("Asphyxiation"),
SuicideKind.Bloodloss => prototypeManager.Index<DamageTypePrototype>("Bloodloss"),
_ => 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");
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.
// Note that mind cannot be null because otherwise owner would be null.

View File

@@ -5,6 +5,9 @@ using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
namespace Content.Server.Chemistry.ReagentEffects
{
@@ -12,7 +15,7 @@ namespace Content.Server.Chemistry.ReagentEffects
/// Default metabolism for medicine reagents. Attempts to find a DamageableComponent on the target,
/// and to update its damage values.
/// </summary>
public class HealthChange : ReagentEffect
public class HealthChange : ReagentEffect, ISerializationHooks
{
/// <summary>
/// How much damage is changed when 1u of the reagent is metabolized.
@@ -20,35 +23,45 @@ namespace Content.Server.Chemistry.ReagentEffects
[DataField("healthChange")]
public float AmountToChange { get; set; } = 1.0f;
/// <summary>
/// Class of damage changed, Brute, Burn, Toxin, Airloss.
/// </summary>
[DataField("damageClass")]
public DamageClass DamageType { get; set; } = DamageClass.Brute;
// TODO DAMAGE UNITS When damage units support decimals, get rid of this.
// See also _accumulatedDamage in ThirstComponent and HungerComponent
private float _accumulatedDamage;
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>
/// Changes damage if a DamageableComponent can be found.
/// </summary>
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);
float decHealthChange = (float) (AmountToChange - (int) AmountToChange);
_accumulatedHealth += decHealthChange;
damageComponent.TryChangeDamage(DamageGroup, (int)AmountToChange, true);
if (_accumulatedHealth >= 1)
float decHealthChange = (float) (AmountToChange - (int) AmountToChange);
_accumulatedDamage += decHealthChange;
if (_accumulatedDamage >= 1)
{
health.ChangeDamage(DamageType, 1, true);
_accumulatedHealth -= 1;
damageComponent.TryChangeDamage(DamageGroup, 1, true);
_accumulatedDamage -= 1;
}
else if(_accumulatedHealth <= -1)
else if(_accumulatedDamage <= -1)
{
health.ChangeDamage(DamageType, -1, true);
_accumulatedHealth += 1;
damageComponent.TryChangeDamage(DamageGroup, -1, true);
_accumulatedDamage += 1;
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using Content.Server.Administration;
using Content.Shared.Administration;
@@ -9,6 +10,7 @@ using Robust.Server.Player;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
namespace Content.Server.Damage.Commands
{
@@ -19,22 +21,24 @@ namespace Content.Server.Damage.Commands
public string Description => "Ouch";
public string Help => $"Usage: {Command} <type/?> <amount> (<entity uid/_>) (<ignoreResistances>)";
private readonly IPrototypeManager _prototypeManager = default!;
public HurtCommand() {
_prototypeManager = IoCManager.Resolve<IPrototypeManager>();
}
private string DamageTypes()
{
var msg = new StringBuilder();
foreach (var dClass in Enum.GetNames(typeof(DamageClass)))
foreach (var damageGroup in _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
{
msg.Append($"\n{dClass}");
var types = Enum.Parse<DamageClass>(dClass).ToTypes();
if (types.Count > 0)
msg.Append($"\n{damageGroup.ID}");
if (damageGroup.DamageTypes.Any())
{
msg.Append(": ");
msg.AppendJoin('|', types);
msg.AppendJoin('|', damageGroup.DamageTypes);
}
}
return $"Damage Types:{msg}";
}
@@ -85,6 +89,8 @@ namespace Content.Server.Damage.Commands
string[] args,
[NotNullWhen(true)] out Damage? func)
{
if (!int.TryParse(args[1], out var amount))
{
shell.WriteLine($"{args[1]} is not a valid damage integer.");
@@ -93,42 +99,42 @@ namespace Content.Server.Damage.Commands
return false;
}
if (Enum.TryParse<DamageClass>(args[0], true, out var damageClass))
if (_prototypeManager.TryIndex<DamageGroupPrototype>(args[0], out var damageGroup))
{
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;
}
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.");
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;
}
// 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) =>
{
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;
}
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.");
@@ -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." : ".")}");
};
};
return true;
}
else
{

View File

@@ -3,6 +3,9 @@ using Content.Shared.Damage;
using Content.Shared.Sound;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Damage.Components
{
@@ -14,8 +17,6 @@ namespace Content.Server.Damage.Components
{
public override string Name => "DamageOnHighSpeedImpact";
[DataField("damage")]
public DamageType Damage { get; set; } = DamageType.Blunt;
[DataField("minimumSpeed")]
public float MinimumSpeed { get; set; } = 20f;
[DataField("baseDamage")]
@@ -34,5 +35,17 @@ namespace Content.Server.Damage.Components
public float DamageCooldown { get; set; } = 2f;
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);
}
}
}

View File

@@ -3,6 +3,9 @@ using Content.Shared.Damage.Components;
using Content.Shared.Throwing;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
namespace Content.Server.Damage.Components
{
@@ -11,20 +14,31 @@ namespace Content.Server.Damage.Components
{
public override string Name => "DamageOnLand";
[DataField("damageType")]
private DamageType _damageType = DamageType.Blunt;
[DataField("amount")]
[ViewVariables(VVAccess.ReadWrite)]
private int _amount = 1;
[DataField("ignoreResistances")]
[ViewVariables(VVAccess.ReadWrite)]
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)
{
if (!Owner.TryGetComponent(out IDamageableComponent? damageable)) return;
damageable.ChangeDamage(_damageType, _amount, _ignoreResistances, eventArgs.User);
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
return;
damageable.TryChangeDamage(DamageType, _amount, _ignoreResistances);
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading.Tasks;
using Content.Server.Tools.Components;
using Content.Shared.Damage;
@@ -7,12 +7,16 @@ using Content.Shared.Interaction;
using Content.Shared.Tool;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
namespace Content.Server.Damage.Components
{
[RegisterComponent]
public class DamageOnToolInteractComponent : Component, IInteractUsing
{
public override string Name => "DamageOnToolInteract";
[DataField("damage")]
@@ -21,6 +25,23 @@ namespace Content.Server.Damage.Components
[DataField("tools")]
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)
{
if (eventArgs.Using.TryGetComponent<ToolComponent>(out var tool))
@@ -44,17 +65,15 @@ namespace Content.Server.Damage.Components
protected bool CallDamage(InteractUsingEventArgs eventArgs, ToolComponent tool)
{
if (eventArgs.Target.TryGetComponent<IDamageableComponent>(out var damageable))
{
damageable.ChangeDamage(tool.HasQuality(ToolQuality.Welding)
? DamageType.Heat
: DamageType.Blunt,
Damage, false, eventArgs.User);
if (!eventArgs.Target.TryGetComponent<IDamageableComponent>(out var damageable))
return false;
return true;
}
damageable.TryChangeDamage(tool.HasQuality(ToolQuality.Welding)
? WeldingDamageType
: DefaultDamageType,
Damage);
return false;
return true;
}
}
}

View File

@@ -2,6 +2,8 @@ using Content.Shared.Damage;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
namespace Content.Server.Damage.Components
{
@@ -11,13 +13,21 @@ namespace Content.Server.Damage.Components
{
public override string Name => "DamageOtherOnHit";
[DataField("damageType")]
public DamageType DamageType { get; } = DamageType.Blunt;
[DataField("amount")]
public int Amount { get; } = 1;
[DataField("ignoreResistances")]
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);
}
}
}

View File

@@ -46,7 +46,7 @@ namespace Content.Server.Damage
if (ComponentManager.TryGetComponent(uid, out StunnableComponent? stun) && _robustRandom.Prob(component.StunChance))
stun.Stun(component.StunSeconds);
damageable.ChangeDamage(component.Damage, damage, false, args.OtherFixture.Body.Owner);
damageable.TryChangeDamage(component.DamageType, damage);
}
}
}

View File

@@ -17,7 +17,7 @@ namespace Content.Server.Damage
if (!args.Target.TryGetComponent(out IDamageableComponent? damageable))
return;
damageable.ChangeDamage(component.DamageType, component.Amount, component.IgnoreResistances, args.User);
damageable.TryChangeDamage(component.DamageType, component.Amount, component.IgnoreResistances);
}
}
}

View File

@@ -1,9 +1,8 @@
using System.Collections.Generic;
using Content.Server.Atmos.Components;
using System.Collections.Generic;
using System.Linq;
using Content.Server.Atmos.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Resistances;
using Content.Shared.GameTicking;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
@@ -43,8 +42,9 @@ namespace Content.Server.Damage
if (entity.TryGetComponent(out IDamageableComponent? damageable))
{
damageable.SupportedTypes.Clear();
damageable.SupportedClasses.Clear();
damageable.SupportedDamageTypes.Clear();
damageable.FullySupportedDamageGroups.Clear();
damageable.ApplicableDamageGroups.Clear();
}
return true;
@@ -69,14 +69,19 @@ namespace Content.Server.Damage
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))
{
SupportedTypes = damageable.SupportedTypes.ToHashSet();
SupportedClasses = damageable.SupportedClasses.ToHashSet();
SupportedDamageTypes = damageable.SupportedDamageTypes.ToHashSet();
SupportedDamageGroups = damageable.FullySupportedDamageGroups.ToHashSet();
ApplicableDamageGroups = damageable.ApplicableDamageGroups.ToHashSet();
}
}
@@ -120,9 +126,11 @@ namespace Content.Server.Damage
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; }
}
}
}

View File

@@ -61,7 +61,7 @@ namespace Content.Server.Damage
{
if (target.TryGetComponent(out IDamageableComponent? damage))
{
damage.Heal();
damage.TrySetAllDamage(0);
}
if (target.TryGetComponent(out IMobStateComponent? mobState))

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using Content.Shared.Damage.Components;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -15,8 +15,8 @@ namespace Content.Server.Destructible.Thresholds.Triggers
/// <summary>
/// The amount of damage at which this threshold will trigger.
/// </summary>
[DataField("damage")]
public int Damage { get; set; }
[DataField("damage", required: true)]
public int Damage { get; set; } = default!;
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
{

View File

@@ -1,7 +1,9 @@
using System;
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
{
@@ -13,20 +15,24 @@ namespace Content.Server.Destructible.Thresholds.Triggers
[DataDefinition]
public class DamageTypeTrigger : IThresholdTrigger
{
[DataField("type")]
public DamageType? Type { get; set; }
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// While you're at it, maybe also combine damageGroup and damage into a dictionary, and allow it to test a sum
// of damage types?
[DataField("damageType", required:true)]
public string _damageTypeID { get; set; } = default!;
public DamageTypePrototype DamageType => IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
[DataField("damage")]
public int Damage { get; set; }
[DataField("damage", required: true)]
public int Damage { get; set; } = default!;
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
{
if (Type == null)
if (DamageType == null)
{
return false;
}
return damageable.TryGetDamage(Type.Value, out var damageReceived) &&
return damageable.TryGetDamage(DamageType, out var damageReceived) &&
damageReceived >= Damage;
}
}

View File

@@ -30,6 +30,8 @@ using Robust.Shared.Players;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using Timer = Robust.Shared.Timing.Timer;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
namespace Content.Server.Doors.Components
{
@@ -45,6 +47,18 @@ namespace Content.Server.Doors.Components
[DataField("tryOpenDoorSound")]
private SoundSpecifier _tryOpenDoorSound = new SoundPathSpecifier("/Audio/Effects/bang.ogg");
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")]
private readonly string _damageTypeID = "Blunt";
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
}
public override DoorState State
{
get => base.State;
@@ -536,7 +550,7 @@ namespace Content.Server.Doors.Components
hitsomebody = true;
CurrentlyCrushing.Add(e.Owner.Uid);
damage.ChangeDamage(DamageType.Blunt, DoorCrushDamage, false, Owner);
damage.TryChangeDamage(DamageType, DoorCrushDamage);
stun.Paralyze(DoorStunTime);
}

View File

@@ -10,6 +10,7 @@ using Robust.Server.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
namespace Content.Server.GameTicking.Presets
{
@@ -66,7 +67,8 @@ namespace Content.Server.GameTicking.Presets
if (playerEntity.TryGetComponent(out IDamageableComponent? damageable))
{
//todo: what if they dont breathe lol
damageable.SetDamage(DamageType.Asphyxiation, 200, playerEntity);
//cry deeply
damageable.TrySetDamage(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Asphyxiation"), 200);
}
}
}

View File

@@ -23,6 +23,7 @@ using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Map;
@@ -38,6 +39,7 @@ namespace Content.Server.GameTicking.Presets
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public string PDAPrototypeName => "CaptainPDA";
public string BeltPrototypeName => "ClothingBeltJanitorFilled";
@@ -192,11 +194,11 @@ namespace Content.Server.GameTicking.Presets
{
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))
{
//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())

View File

@@ -20,6 +20,7 @@ using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Localization;
using Robust.Shared.Player;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -77,6 +78,19 @@ namespace Content.Server.Light.Components
[ViewVariables] private ContainerSlot _lightBulbContainer = default!;
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
[DataField("damageType")]
private readonly string _damageTypeID = "Heat";
[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]
public LightBulbComponent? LightBulb
{
@@ -126,7 +140,7 @@ namespace Content.Server.Light.Components
void Burn()
{
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);
}
@@ -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)
{
base.HandleMessage(message, component);

View File

@@ -1,15 +1,17 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Threading.Tasks;
using Content.Server.Stack;
using Content.Shared.ActionBlocker;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Interaction.Helpers;
using Content.Shared.Stacks;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
namespace Content.Server.Medical.Components
{
@@ -18,7 +20,12 @@ namespace Content.Server.Medical.Components
{
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)
{
@@ -48,9 +55,9 @@ namespace Content.Server.Medical.Components
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;

View File

@@ -99,8 +99,8 @@ namespace Content.Server.Medical.Components
private static readonly MedicalScannerBoundUserInterfaceState EmptyUIState =
new(
null,
new Dictionary<DamageClass, int>(),
new Dictionary<DamageType, int>(),
new Dictionary<string, int>(),
new Dictionary<string, int>(),
false);
private MedicalScannerBoundUserInterfaceState GetUserInterfaceState()
@@ -121,12 +121,13 @@ namespace Content.Server.Medical.Components
return EmptyUIState;
}
var classes = new Dictionary<DamageClass, int>(damageable.DamageClasses);
var types = new Dictionary<DamageType, int>(damageable.DamageTypes);
// Get dictionaries of damage, by fully supported damage groups and types
var groups = new Dictionary<string, int>(damageable.GetDamagePerFullySupportedGroupIDs);
var types = new Dictionary<string, int>(damageable.GetDamagePerTypeIDs);
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>();
@@ -134,7 +135,7 @@ namespace Content.Server.Medical.Components
mindComponent.Mind != null &&
cloningSystem.HasDnaScan(mindComponent.Mind);
return new MedicalScannerBoundUserInterfaceState(body.Uid, classes, types, scanned);
return new MedicalScannerBoundUserInterfaceState(body.Uid, groups, types, scanned);
}
private void UpdateUserInterface()

View File

@@ -7,9 +7,12 @@ using Content.Shared.Mining;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Mining.Components
{
@@ -21,10 +24,16 @@ namespace Content.Server.Mining.Components
public override string Name => "AsteroidRock";
private static readonly string[] SpriteStates = {"0", "1", "2", "3", "4"};
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
[DataField("damageType")]
private readonly string _damageTypeID = "Blunt"!;
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
{
appearance.SetData(AsteroidRockVisuals.State, _random.Pick(SpriteStates));
@@ -37,7 +46,7 @@ namespace Content.Server.Mining.Components
if (!item.TryGetComponent(out MeleeWeaponComponent? meleeWeaponComponent))
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))
return true;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Content.Server.Alert;
using Content.Shared.Alert;
@@ -14,6 +14,7 @@ using Robust.Shared.Players;
using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using Robust.Shared.Prototypes;
namespace Content.Server.Nutrition.Components
{
@@ -22,6 +23,10 @@ namespace Content.Server.Nutrition.Components
{
[Dependency] private readonly IRobustRandom _random = default!;
// TODO DAMAGE UNITS When damage units support decimals, get rid of this.
// See also _accumulatedDamage in ThirstComponent and HealthChange.
private float _accumulatedDamage;
// Base stuff
[ViewVariables(VVAccess.ReadWrite)]
public float BaseDecayRate
@@ -29,7 +34,7 @@ namespace Content.Server.Nutrition.Components
get => _baseDecayRate;
set => _baseDecayRate = value;
}
[DataField("base_decay_rate")]
[DataField("baseDecayRate")]
private float _baseDecayRate = 0.1f;
[ViewVariables(VVAccess.ReadWrite)]
@@ -59,11 +64,11 @@ namespace Content.Server.Nutrition.Components
public Dictionary<HungerThreshold, float> HungerThresholds => _hungerThresholds;
private readonly Dictionary<HungerThreshold, float> _hungerThresholds = new()
{
{HungerThreshold.Overfed, 600.0f},
{HungerThreshold.Okay, 450.0f},
{HungerThreshold.Peckish, 300.0f},
{HungerThreshold.Starving, 150.0f},
{HungerThreshold.Dead, 0.0f},
{ HungerThreshold.Overfed, 600.0f },
{ HungerThreshold.Okay, 450.0f },
{ HungerThreshold.Peckish, 300.0f },
{ HungerThreshold.Starving, 150.0f },
{ HungerThreshold.Dead, 0.0f },
};
public static readonly Dictionary<HungerThreshold, AlertType> HungerThresholdAlertTypes = new()
@@ -73,6 +78,18 @@ namespace Content.Server.Nutrition.Components
{ HungerThreshold.Starving, AlertType.Starving },
};
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")]
private readonly string _damageTypeID = "Blunt"!;
[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)
{
if (_currentHungerThreshold != _lastHungerThreshold || force)
@@ -177,6 +194,7 @@ namespace Content.Server.Nutrition.Components
if (_currentHungerThreshold != HungerThreshold.Dead)
return;
// --> Current Hunger is below dead threshold
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
return;
@@ -186,7 +204,14 @@ namespace Content.Server.Nutrition.Components
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);
}
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using Content.Server.Alert;
using Content.Shared.Alert;
@@ -14,6 +14,7 @@ using Robust.Shared.Players;
using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using Robust.Shared.Prototypes;
namespace Content.Server.Nutrition.Components
{
@@ -22,6 +23,10 @@ namespace Content.Server.Nutrition.Components
{
[Dependency] private readonly IRobustRandom _random = default!;
// TODO DAMAGE UNITS When damage units support decimals, get rid of this.
// See also _accumulatedDamage in HungerComponent and HealthChange.
private float _accumulatedDamage;
// Base stuff
[ViewVariables(VVAccess.ReadWrite)]
public float BaseDecayRate
@@ -29,7 +34,7 @@ namespace Content.Server.Nutrition.Components
get => _baseDecayRate;
set => _baseDecayRate = value;
}
[DataField("base_decay_rate")]
[DataField("baseDecayRate")]
private float _baseDecayRate = 0.1f;
[ViewVariables(VVAccess.ReadWrite)]
@@ -72,6 +77,18 @@ namespace Content.Server.Nutrition.Components
{ThirstThreshold.Parched, AlertType.Parched},
};
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")]
private readonly string _damageTypeID = "Blunt";
[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)
{
if (_currentThirstThreshold != _lastThirstThreshold || force)
@@ -174,6 +191,7 @@ namespace Content.Server.Nutrition.Components
if (_currentThirstThreshold != ThirstThreshold.Dead)
return;
// --> Current Hunger is below dead threshold
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
return;
@@ -183,7 +201,15 @@ namespace Content.Server.Nutrition.Components
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);
}
}
}

View File

@@ -19,7 +19,7 @@ namespace Content.Server.Nutrition.EntitySystems
{
comp.OnUpdate(_accumulatedFrameTime);
}
_accumulatedFrameTime -= 1;
_accumulatedFrameTime = 0;
}
}
}

View File

@@ -9,8 +9,10 @@ using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
using Robust.Shared.ViewVariables;
namespace Content.Server.Projectiles.Components
{
@@ -25,26 +27,18 @@ namespace Content.Server.Projectiles.Components
public override string Name => "Hitscan";
public CollisionGroup CollisionMask => (CollisionGroup) _collisionMask;
[DataField("layers")] //todo WithFormat.Flags<CollisionLayer>()
private int _collisionMask = (int) CollisionGroup.Opaque;
public float Damage
{
get => _damage;
set => _damage = value;
}
[DataField("damage")]
private float _damage = 10f;
public DamageType DamageType => _damageType;
[DataField("damageType")]
private DamageType _damageType = DamageType.Heat;
public float MaxLength => 20.0f;
public float Damage { get; set; } = 10f;
public float MaxLength => 20.0f;
private TimeSpan _startTime;
private TimeSpan _deathTime;
public float ColorModifier { get; set; } = 1.0f;
[DataField("spriteName")]
[DataField("spriteName")]
private string _spriteName = "Objects/Weapons/Guns/Projectiles/laser.png";
[DataField("muzzleFlash")]
private string? _muzzleFlash;
@@ -53,6 +47,19 @@ namespace Content.Server.Projectiles.Components
[DataField("soundHitWall")]
private SoundSpecifier _soundHitWall = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg");
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")]
private readonly string _damageTypeID = "Piercing";
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
}
public void FireEffects(IEntity user, float distance, Angle angle, IEntity? hitEntity = null)
{
var effectSystem = EntitySystem.Get<EffectSystem>();

View File

@@ -13,14 +13,12 @@ namespace Content.Server.Projectiles.Components
[ComponentReference(typeof(SharedProjectileComponent))]
public class ProjectileComponent : SharedProjectileComponent
{
[DataField("damages")] private Dictionary<DamageType, int> _damages = new();
[ViewVariables]
public Dictionary<DamageType, int> Damages
{
get => _damages;
set => _damages = value;
}
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// This also requires changing the dictionary type and modifying ProjectileSystem.cs, which uses it.
// While thats being done, also replace "damages" -> "damageTypes" For consistency.
[DataField("damages")]
[ViewVariables(VVAccess.ReadWrite)]
public Dictionary<string, int> Damages { get; set; } = new();
[DataField("deleteOnCollide")]
public bool DeleteOnCollide { get; } = true;

View File

@@ -7,12 +7,17 @@ using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Content.Shared.Damage;
namespace Content.Server.Projectiles
{
[UsedImplicitly]
internal sealed class ProjectileSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override void Initialize()
{
base.Initialize();
@@ -49,9 +54,9 @@ namespace Content.Server.Projectiles
{
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;

View File

@@ -36,7 +36,7 @@ namespace Content.Server.Repairable
{
if (!await welder.UseTool(eventArgs.User, Owner, _doAfterDelay, ToolQuality.Welding, _fuelCost))
return false;
damageable.Heal();
damageable.TrySetAllDamage(0);
Owner.PopupMessage(eventArgs.User,
Loc.GetString("comp-repairable-repair",

View File

@@ -18,7 +18,7 @@ namespace Content.Server.Spawners.Components
[DataField("job_id")]
private string? _jobId;
[field: ViewVariables(VVAccess.ReadWrite)]
[ViewVariables(VVAccess.ReadWrite)]
[DataField("spawn_type")]
public SpawnPointType SpawnType { get; } = SpawnPointType.Unset;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using Content.Server.Alert;
using Content.Shared.Alert;
using Content.Shared.Atmos;
@@ -8,6 +8,8 @@ using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
namespace Content.Server.Temperature.Components
{
@@ -22,11 +24,21 @@ namespace Content.Server.Temperature.Components
/// <inheritdoc />
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 ColdDamageThreshold => _coldDamageThreshold;
[ViewVariables] public float TempDamageCoefficient => _tempDamageCoefficient;
[ViewVariables] public float SpecificHeat => _specificHeat;
[ViewVariables] public float HeatCapacity {
get
{
@@ -39,33 +51,25 @@ namespace Content.Server.Temperature.Components
}
}
[ViewVariables] public float SpecificHeat => _specificHeat;
[DataField("heatDamageThreshold")]
private float _heatDamageThreshold = default;
[DataField("coldDamageThreshold")]
private float _coldDamageThreshold = default;
[DataField("tempDamageCoefficient")]
private float _tempDamageCoefficient = 1;
[DataField("currentTemperature")]
private float _currentTemperature = Atmospherics.T20C;
[DataField("specificHeat")]
private float _specificHeat = Atmospherics.MinimumHeatCapacity;
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("coldDamageType")]
private readonly string _coldDamageTypeID = "Cold";
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype ColdDamageType = default!;
[DataField("hotDamageType")]
private readonly string _hotDamageTypeID = "Heat";
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype HotDamageType = default!;
protected override void Initialize()
{
base.Initialize();
ColdDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_coldDamageTypeID);
HotDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_hotDamageTypeID);
}
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))
{
@@ -108,10 +112,19 @@ namespace Content.Server.Temperature.Components
}
}
if (!damageType.HasValue) 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>

View File

@@ -4,6 +4,8 @@ using Content.Shared.Sound;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
namespace Content.Server.Weapon.Melee.Components
{
@@ -48,15 +50,23 @@ namespace Content.Server.Weapon.Melee.Components
[DataField("damage")]
public int Damage { get; set; } = 5;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("damageType")]
public DamageType DamageType { get; set; } = DamageType.Blunt;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("clickAttackEffect")]
public bool ClickAttackEffect { get; set; } = true;
public TimeSpan LastAttackTime;
public TimeSpan CooldownEnd;
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")]
private readonly string _damageTypeID = "Blunt";
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
}
}
}

View File

@@ -25,6 +25,8 @@ namespace Content.Server.Weapon.Melee
public sealed class MeleeWeaponSystem : EntitySystem
{
[Dependency] private IGameTiming _gameTiming = default!;
public override void Initialize()
{
base.Initialize();
@@ -88,7 +90,7 @@ namespace Content.Server.Weapon.Melee
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);
@@ -157,7 +159,7 @@ namespace Content.Server.Weapon.Melee
{
if (entity.TryGetComponent<IDamageableComponent>(out var damageComponent))
{
damageComponent.ChangeDamage(comp.DamageType, comp.Damage, false, owner);
damageComponent.TryChangeDamage(comp.DamageType, comp.Damage);
}
}
}

View File

@@ -188,7 +188,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
{
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)
{
newDamages.Add(damageType, (int) (damage * energyRatio));

View File

@@ -399,7 +399,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
if (!result.HitEntity.TryGetComponent(out IDamageableComponent? damageable))
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
//even numbers if halfway between two numbers, rather than rounding to nearest
}

View File

@@ -27,6 +27,8 @@ using Robust.Shared.Players;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
using Robust.Shared.ViewVariables;
using Robust.Shared.Prototypes;
using System.Collections.Generic;
namespace Content.Server.Weapon.Ranged
{
@@ -55,6 +57,17 @@ namespace Content.Server.Weapon.Ranged
[DataField("clumsyWeaponShotSound")]
private SoundSpecifier _clumsyWeaponShotSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/bang.ogg");
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// This also requires changing the dictionary type and modifying TryFire(), which uses it.
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("clumsyDamage")]
public Dictionary<string, int> ClumsyDamage { get; set; } = new()
{
{ "Blunt", 10 },
{ "Heat", 5 }
};
public Func<bool>? WeaponCanFireHandler;
public Func<IEntity, bool>? UserCanFireHandler;
@@ -168,25 +181,30 @@ namespace Content.Server.Weapon.Ranged
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(),
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
SoundSystem.Play(
Filter.Pvs(Owner), _clumsyWeaponShotSound.GetSound(),
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"));
Owner.Delete();