ECS damageable (#4529)

* ECS and damage Data

* Comments and newlines

* Added Comments

* Make TryChangeDamageEvent immutable

* Remove SetAllDamage event

Use public SetAllDamage function instead

* Undo destructible mistakes

That was some shit code.

* Rename DamageData to DamageSpecifier

And misc small edits

misc

* Cache trigger prototypes.

* Renaming destructible classes & functions

* Revert "Cache trigger prototypes."

This reverts commit 86bae15ba6616884dba75f552dfdfbe2d1fb6586.

* Replace prototypes with prototype IDs.

* Split damage.yml into individual files

* move get/handle component state to system

* Update HealthChange doc

* Make godmode call Dirty() on damageable component

* Add Initialize() to fix damage test

* Make non-static

* uncache resistance set prototype and trim DamageableComponentState

* Remove unnecessary Dirty() calls during initialization

* RemoveTryChangeDamageEvent

* revert Dirty()

* Fix MobState relying on DamageableComponent.Dirty()

* Fix DisposalUnit Tests.

These were previously failing, but because the async was not await-ed, this never raised the exception.

After I fixed MobState component, this exception stopped happening and instead the assertions started being tested & failing

* Disposal test 2: electric boogaloo

* Fix typos/mistakes

also add comments and fix spacing.

* Use Uids instead of IEntity

* fix merge

* Comments, a merge issue, and making some damage ignore resistances

* Extend DamageSpecifier and use it for DamageableComponent

* fix master merge

* Fix Disposal unit test. Again.

Snapgrids were removed in master

* Execute Exectute
This commit is contained in:
Leon Friedrich
2021-09-15 03:07:37 +10:00
committed by GitHub
parent 22cc42ff50
commit df584ad446
212 changed files with 2876 additions and 3441 deletions

View File

@@ -16,7 +16,7 @@ namespace Content.Server.AI.Utility.Considerations.Combat.Melee
}
// Just went with max health
return meleeWeaponComponent.Damage / 300.0f;
return meleeWeaponComponent.Damage.Total / 300.0f;
}
}
}

View File

@@ -1,6 +1,6 @@
using Content.Server.AI.WorldState;
using Content.Server.AI.WorldState;
using Content.Server.AI.WorldState.States;
using Content.Shared.Damage.Components;
using Content.Shared.Damage;
namespace Content.Server.AI.Utility.Considerations.Combat
{
@@ -10,7 +10,7 @@ namespace Content.Server.AI.Utility.Considerations.Combat
{
var target = context.GetState<TargetEntityState>().GetValue();
if (target == null || target.Deleted || !target.TryGetComponent(out IDamageableComponent? damageableComponent))
if (target == null || target.Deleted || !target.TryGetComponent(out DamageableComponent? damageableComponent))
{
return 0.0f;
}

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Content.Server.AI.Components;
using Content.Shared.Damage.Components;
using Content.Shared.Damage;
using JetBrains.Annotations;
using Robust.Server.Player;
using Robust.Shared.GameObjects;
@@ -32,7 +32,7 @@ namespace Content.Server.AI.WorldState.States.Mobs
continue;
}
if (player.AttachedEntity != Owner && player.AttachedEntity.HasComponent<IDamageableComponent>())
if (player.AttachedEntity != Owner && player.AttachedEntity.HasComponent<DamageableComponent>())
{
result.Add(player.AttachedEntity);
}

View File

@@ -5,11 +5,8 @@ using Content.Server.Pressure;
using Content.Shared.Alert;
using Content.Shared.Atmos;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
namespace Content.Server.Atmos.Components
@@ -22,21 +19,14 @@ namespace Content.Server.Atmos.Components
{
public override string Name => "Barotrauma";
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")] private readonly string _damageTypeID = "Blunt";
[DataField("damage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
}
public DamageSpecifier Damage = default!;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Update(float airPressure)
{
if (!Owner.TryGetComponent(out IDamageableComponent? damageable)) return;
if (!Owner.HasComponent<DamageableComponent>()) return;
var status = Owner.GetComponentOrNull<ServerAlertsComponent>();
var highPressureMultiplier = 1f;
@@ -59,7 +49,7 @@ namespace Content.Server.Atmos.Components
goto default;
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
damageable.TryChangeDamage(DamageType, Atmospherics.LowPressureDamage,true);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(Owner.Uid, Damage * Atmospherics.LowPressureDamage, true);
if (status == null) break;
@@ -79,10 +69,10 @@ namespace Content.Server.Atmos.Components
if(pressure < Atmospherics.WarningHighPressure)
goto default;
var damage = (int) MathF.Min((pressure / Atmospherics.HazardHighPressure) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage);
var damageScale = (int) MathF.Min((pressure / Atmospherics.HazardHighPressure) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage);
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
damageable.TryChangeDamage(DamageType, damage,true);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(Owner.Uid, Damage * damageScale, true);
if (status == null) break;

View File

@@ -10,7 +10,6 @@ using Content.Shared.Alert;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Interaction;
using Content.Shared.Notification.Managers;
using Content.Shared.Temperature;
@@ -20,8 +19,6 @@ using Robust.Shared.Localization;
using Robust.Shared.Physics;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
namespace Content.Server.Atmos.Components
{
@@ -45,17 +42,9 @@ namespace Content.Server.Atmos.Components
[DataField("canResistFire")]
public bool CanResistFire { get; private set; } = false;
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")]
private readonly string _damageTypeID = "Heat"!;
[DataField("damage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
}
public DamageSpecifier Damage = default!;
public void Extinguish()
{
@@ -102,12 +91,9 @@ namespace Content.Server.Atmos.Components
temp.ReceiveHeat(200 * FireStacks);
}
if (Owner.TryGetComponent(out IDamageableComponent? damageable))
{
// TODO ATMOS Fire resistance from armor
var damage = Math.Min((int) (FireStacks * 2.5f), 10);
damageable.TryChangeDamage(DamageType, damage, false);
}
// TODO ATMOS Fire resistance from armor
var damageScale = Math.Min((int) (FireStacks * 2.5f), 10);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(Owner.Uid, Damage * damageScale);
AdjustFireStacks(-0.1f * (_resisting ? 10f : 1f));
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Content.Server.Body.Circulatory;
using Content.Shared.Body.Components;
using Content.Shared.Body.Mechanism;

View File

@@ -12,9 +12,6 @@ using Content.Shared.Alert;
using Content.Shared.Atmos;
using Content.Shared.Body.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Content.Shared.MobState;
using Content.Shared.Notification.Managers;
using Robust.Shared.GameObjects;
@@ -92,21 +89,13 @@ namespace Content.Server.Body.Respiratory
[ViewVariables] public bool Suffocating { get; private set; }
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamage")] private int _damage = 1;
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamageRecovery")] private int _damageRecovery = 1;
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")]
private readonly string _damageTypeID = "Asphyxiation"!;
[DataField("damage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
}
public DamageSpecifier Damage = default!;
[DataField("damageRecovery", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageSpecifier DamageRecovery = default!;
private Dictionary<Gas, float> NeedsAndDeficit(float frameTime)
{
@@ -358,27 +347,19 @@ namespace Content.Server.Body.Respiratory
alertsComponent.ShowAlert(AlertType.LowOxygen);
}
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
{
return;
}
damageable.TryChangeDamage(DamageType, _damage, false);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(Owner.Uid, Damage);
}
private void StopSuffocation()
{
Suffocating = false;
if (Owner.TryGetComponent(out IDamageableComponent? damageable))
{
damageable.TryChangeDamage(DamageType, -_damageRecovery, false);
}
if (Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent))
{
alertsComponent.ClearAlert(AlertType.LowOxygen);
}
EntitySystem.Get<DamageableSystem>().TryChangeDamage(Owner.Uid, DamageRecovery);
}
public GasMixture Clean(BloodstreamComponent bloodstream)

View File

@@ -2,13 +2,13 @@ using System;
using System.Diagnostics.CodeAnalysis;
using Content.Server.Alert;
using Content.Server.Hands.Components;
using Content.Server.MobState.States;
using Content.Server.Pulling;
using Content.Server.Stunnable.Components;
using Content.Shared.ActionBlocker;
using Content.Shared.Alert;
using Content.Shared.Buckle.Components;
using Content.Shared.Interaction.Helpers;
using Content.Shared.MobState.Components;
using Content.Shared.Notification.Managers;
using Content.Shared.Standing;
using Content.Shared.Verbs;

View File

@@ -8,7 +8,7 @@ using Content.Server.Items;
using Content.Server.Notification;
using Content.Server.Players;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.Notification;
using Content.Shared.Notification.Managers;
using Robust.Server.Player;
@@ -30,13 +30,14 @@ namespace Content.Server.Chat.Commands
public string Help => Loc.GetString("suicide-command-help-text");
private void DealDamage(ISuicideAct suicide, IChatManager chat, IDamageableComponent damageableComponent, IEntity source, IEntity target)
private void DealDamage(ISuicideAct suicide, IChatManager chat, IEntity target)
{
var kind = suicide.Suicide(target, chat);
if (kind != SuicideKind.Special)
{
// TODO SUICIDE ..heh.. anyway, someone should fix this mess.
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
damageableComponent.TrySetDamage(kind switch
DamageSpecifier damage = new(kind switch
{
SuicideKind.Blunt => prototypeManager.Index<DamageTypePrototype>("Blunt"),
SuicideKind.Slash => prototypeManager.Index<DamageTypePrototype>("Slash"),
@@ -51,6 +52,7 @@ namespace Content.Server.Chat.Commands
_ => prototypeManager.Index<DamageTypePrototype>("Blunt")
},
200);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(target.Uid, damage, true);
}
}
@@ -77,7 +79,6 @@ namespace Content.Server.Chat.Commands
return;
}
var dmgComponent = owner.GetComponent<IDamageableComponent>();
//TODO: needs to check if the mob is actually alive
//TODO: maybe set a suicided flag to prevent resurrection?
@@ -90,7 +91,7 @@ namespace Content.Server.Chat.Commands
if (suicide != null)
{
DealDamage(suicide, chat, dmgComponent, itemComponent.Owner, owner);
DealDamage(suicide, chat, owner);
return;
}
}
@@ -106,7 +107,7 @@ namespace Content.Server.Chat.Commands
var suicide = entity.GetAllComponents<ISuicideAct>().FirstOrDefault();
if (suicide != null)
{
DealDamage(suicide, chat, dmgComponent, entity, owner);
DealDamage(suicide, chat, owner);
return;
}
}
@@ -119,7 +120,8 @@ namespace Content.Server.Chat.Commands
var selfMessage = Loc.GetString("suicide-command-default-text-self");
owner.PopupMessage(selfMessage);
dmgComponent.TrySetDamage(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Piercing"), 200);
DamageSpecifier damage = new(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Bloodloss"), 200);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(owner.Uid, damage, true);
// Prevent the player from returning to the body.
// Note that mind cannot be null because otherwise owner would be null.

View File

@@ -1,9 +1,9 @@
using Content.Server.Interaction.Components;
using Content.Server.MobState.States;
using Content.Server.Weapon.Melee;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.MobState.Components;
using Content.Shared.Notification.Managers;
using Content.Shared.Sound;
using Robust.Shared.Audio;

View File

@@ -3,66 +3,23 @@ using Content.Shared.Chemistry.Reagent;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
namespace Content.Server.Chemistry.ReagentEffects
{
/// <summary>
/// Default metabolism for medicine reagents. Attempts to find a DamageableComponent on the target,
/// and to update its damage values.
/// Default metabolism for medicine reagents.
/// </summary>
public class HealthChange : ReagentEffect, ISerializationHooks
public class HealthChange : ReagentEffect
{
/// <summary>
/// How much damage is changed when 1u of the reagent is metabolized.
/// Damage to apply every metabolism cycle. Damage Ignores resistances.
/// </summary>
[DataField("healthChange")]
public float AmountToChange { get; set; } = 1.0f;
[DataField("damage", required: true)]
public DamageSpecifier Damage = default!;
// TODO DAMAGE UNITS When damage units support decimals, get rid of this.
// See also _accumulatedDamage in ThirstComponent and HungerComponent
private float _accumulatedDamage;
/// <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? damageComponent))
{
damageComponent.TryChangeDamage(DamageGroup, (int)AmountToChange, true);
float decHealthChange = (float) (AmountToChange - (int) AmountToChange);
_accumulatedDamage += decHealthChange;
if (_accumulatedDamage >= 1)
{
damageComponent.TryChangeDamage(DamageGroup, 1, true);
_accumulatedDamage -= 1;
}
else if(_accumulatedDamage <= -1)
{
damageComponent.TryChangeDamage(DamageGroup, -1, true);
_accumulatedDamage += 1;
}
}
EntitySystem.Get<DamageableSystem>().TryChangeDamage(solutionEntity.Uid, Damage, true);
}
}
}

View File

@@ -5,7 +5,7 @@ using System.Text;
using Content.Server.Administration;
using Content.Shared.Administration;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Robust.Server.Player;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
@@ -42,7 +42,7 @@ namespace Content.Server.Damage.Commands
return $"Damage Types:{msg}";
}
private delegate void Damage(IDamageableComponent damageable, bool ignoreResistances);
private delegate void Damage(IEntity entity, bool ignoreResistances);
private bool TryParseEntity(IConsoleShell shell, IPlayerSession? player, string arg,
[NotNullWhen(true)] out IEntity? entity)
@@ -85,7 +85,7 @@ namespace Content.Server.Damage.Commands
private bool TryParseDamageArgs(
IConsoleShell shell,
IPlayerSession? player,
IEntity target,
string[] args,
[NotNullWhen(true)] out Damage? func)
{
@@ -101,23 +101,12 @@ namespace Content.Server.Damage.Commands
if (_prototypeManager.TryIndex<DamageGroupPrototype>(args[0], out var damageGroup))
{
func = (damageable, ignoreResistances) =>
func = (entity, ignoreResistances) =>
{
if (!damageable.ApplicableDamageGroups.Contains(damageGroup))
{
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage group {damageGroup}");
var damage = new DamageSpecifier(damageGroup, amount);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(entity.Uid, damage, ignoreResistances);
return;
}
if (!damageable.TryChangeDamage(damageGroup, amount, ignoreResistances))
{
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage.");
return;
}
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageGroup} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
shell.WriteLine($"Damaged entity {entity.Name} with id {entity.Uid} for {amount} {damageGroup} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
};
return true;
@@ -125,23 +114,12 @@ namespace Content.Server.Damage.Commands
// Fall back to DamageType
else if (_prototypeManager.TryIndex<DamageTypePrototype>(args[0], out var damageType))
{
func = (damageable, ignoreResistances) =>
func = (entity, ignoreResistances) =>
{
if (!damageable.IsSupportedDamageType(damageType))
{
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage type {damageType}");
var damage = new DamageSpecifier(damageType, amount);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(entity.Uid, damage, ignoreResistances);
return;
}
if (!damageable.TryChangeDamage(damageType, amount, ignoreResistances))
{
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage.");
return;
}
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageType} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
shell.WriteLine($"Damaged entity {entity.Name} with id {entity.Uid} for {amount} {damageType} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
};
return true;
@@ -185,10 +163,6 @@ namespace Content.Server.Damage.Commands
shell.WriteLine($"Invalid number of arguments ({args.Length}).\n{Help}");
return;
case var n when n >= 2 && n <= 4:
if (!TryParseDamageArgs(shell, player, args, out damageFunc))
{
return;
}
var entityUid = n == 2 ? "_" : args[2];
@@ -199,6 +173,11 @@ namespace Content.Server.Damage.Commands
entity = parsedEntity;
if (!TryParseDamageArgs(shell, entity, args, out damageFunc))
{
return;
}
if (n == 4)
{
if (!bool.TryParse(args[3], out ignoreResistances))
@@ -218,13 +197,13 @@ namespace Content.Server.Damage.Commands
return;
}
if (!entity.TryGetComponent(out IDamageableComponent? damageable))
if (!entity.TryGetComponent(out DamageableComponent? damageable))
{
shell.WriteLine($"Entity {entity.Name} with id {entity.Uid} does not have a {nameof(IDamageableComponent)}.");
shell.WriteLine($"Entity {entity.Name} with id {entity.Uid} does not have a {nameof(DamageableComponent)}.");
return;
}
damageFunc(damageable, ignoreResistances);
damageFunc(entity, ignoreResistances);
}
}
}

View File

@@ -3,8 +3,6 @@ using Content.Shared.Damage;
using Content.Shared.Sound;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Damage.Components
@@ -19,8 +17,6 @@ namespace Content.Server.Damage.Components
[DataField("minimumSpeed")]
public float MinimumSpeed { get; set; } = 20f;
[DataField("baseDamage")]
public int BaseDamage { get; set; } = 5;
[DataField("factor")]
public float Factor { get; set; } = 1f;
[DataField("soundHit", required: true)]
@@ -36,16 +32,8 @@ namespace Content.Server.Damage.Components
internal TimeSpan LastHit = TimeSpan.Zero;
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")]
private readonly string _damageTypeID = "Blunt";
[DataField("damage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
}
public DamageSpecifier Damage = default!;
}
}

View File

@@ -9,20 +9,13 @@ namespace Content.Server.Damage.Components
public sealed class DamageOnLandComponent : Component
{
public override string Name => "DamageOnLand";
[DataField("amount")]
[ViewVariables(VVAccess.ReadWrite)]
public int Amount = 1;
[DataField("ignoreResistances")]
[ViewVariables(VVAccess.ReadWrite)]
public bool IgnoreResistances;
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")] public readonly string DamageTypeId = "Blunt";
public bool IgnoreResistances = false;
[DataField("damage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
public DamageSpecifier Damage = default!;
}
}

View File

@@ -2,13 +2,10 @@ using System.Collections.Generic;
using System.Threading.Tasks;
using Content.Server.Tools.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Interaction;
using Content.Shared.Tool;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
namespace Content.Server.Damage.Components
@@ -19,28 +16,16 @@ namespace Content.Server.Damage.Components
public override string Name => "DamageOnToolInteract";
[DataField("damage")]
protected int Damage;
[DataField("tools")]
private List<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";
[DataField("weldingDamage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype WeldingDamageType = default!;
[DataField("defaultDamageType")]
private readonly string _defaultDamageTypeID = "Blunt";
public DamageSpecifier WeldingDamage = default!;
[DataField("defaultDamage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DefaultDamageType = default!;
protected override void Initialize()
{
base.Initialize();
WeldingDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_weldingDamageTypeID);
DefaultDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_defaultDamageTypeID);
}
public DamageSpecifier DefaultDamage = default!;
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
{
@@ -50,30 +35,22 @@ namespace Content.Server.Damage.Components
{
if (tool.HasQuality(ToolQuality.Welding) && toolQuality == ToolQuality.Welding)
{
if (eventArgs.Using.TryGetComponent(out WelderComponent? welder))
if (eventArgs.Using.TryGetComponent(out WelderComponent? welder) && welder.WelderLit)
{
if (welder.WelderLit) return CallDamage(eventArgs, tool);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(eventArgs.Target.Uid, WeldingDamage);
return true;
}
break; //If the tool quality is welding and its not lit or its not actually a welder that can be lit then its pointless to continue.
}
if (tool.HasQuality(toolQuality)) return CallDamage(eventArgs, tool);
if (tool.HasQuality(toolQuality))
{
EntitySystem.Get<DamageableSystem>().TryChangeDamage(eventArgs.Target.Uid, DefaultDamage);
return true;
}
}
}
return false;
}
protected bool CallDamage(InteractUsingEventArgs eventArgs, ToolComponent tool)
{
if (!eventArgs.Target.TryGetComponent<IDamageableComponent>(out var damageable))
return false;
damageable.TryChangeDamage(tool.HasQuality(ToolQuality.Welding)
? WeldingDamageType
: DefaultDamageType,
Damage);
return true;
}
}
}

View File

@@ -3,8 +3,7 @@ using Content.Shared.Damage;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
namespace Content.Server.Damage.Components
{
@@ -14,21 +13,13 @@ namespace Content.Server.Damage.Components
{
public override string Name => "DamageOtherOnHit";
[DataField("amount")]
public int Amount { get; } = 1;
[DataField("ignoreResistances")]
public bool IgnoreResistances { get; } = false;
[ViewVariables(VVAccess.ReadWrite)]
public bool IgnoreResistances = false;
[DataField("damage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageSpecifier Damage = default!;
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")]
private readonly string _damageTypeID = "Blunt";
public DamageTypePrototype DamageType { get; set; } = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
}
}
}

View File

@@ -2,7 +2,7 @@ using Content.Server.Atmos.Components;
using Content.Server.Nutrition.Components;
using Content.Server.Nutrition.EntitySystems;
using Content.Server.Stunnable.Components;
using Content.Shared.Damage.Components;
using Content.Shared.Damage;
using Content.Shared.MobState;
using Content.Shared.Nutrition.Components;
using Content.Shared.Verbs;
@@ -34,7 +34,7 @@ namespace Content.Server.Damage
if (user.TryGetComponent<ActorComponent>(out var player))
{
if (!target.HasComponent<IDamageableComponent>() && !target.HasComponent<HungerComponent>() &&
if (!target.HasComponent<DamageableComponent>() && !target.HasComponent<HungerComponent>() &&
!target.HasComponent<ThirstComponent>())
{
return;
@@ -59,14 +59,9 @@ namespace Content.Server.Damage
public static void PerformRejuvenate(IEntity target)
{
if (target.TryGetComponent(out IDamageableComponent? damage))
if (target.TryGetComponent(out DamageableComponent? damageable))
{
damage.TrySetAllDamage(0);
}
if (target.TryGetComponent(out IMobStateComponent? mobState))
{
mobState.UpdateState(0);
EntitySystem.Get<DamageableSystem>().SetAllDamage(damageable, 0);
}
if (target.TryGetComponent(out HungerComponent? hunger))

View File

@@ -1,7 +1,7 @@
using Content.Server.Damage.Components;
using Content.Server.Stunnable.Components;
using Content.Shared.Audio;
using Content.Shared.Damage.Components;
using Content.Shared.Damage;
using JetBrains.Annotations;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
@@ -18,6 +18,7 @@ namespace Content.Server.Damage.Systems
{
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
public override void Initialize()
{
@@ -27,7 +28,7 @@ namespace Content.Server.Damage.Systems
private void HandleCollide(EntityUid uid, DamageOnHighSpeedImpactComponent component, StartCollideEvent args)
{
if (!ComponentManager.TryGetComponent(uid, out IDamageableComponent? damageable)) return;
if (!ComponentManager.HasComponent<DamageableComponent>(uid)) return;
var otherBody = args.OtherFixture.Body.Owner;
var speed = args.OurFixture.Body.LinearVelocity.Length;
@@ -41,12 +42,11 @@ namespace Content.Server.Damage.Systems
component.LastHit = _gameTiming.CurTime;
var damage = (int) (component.BaseDamage * (speed / component.MinimumSpeed) * component.Factor);
if (ComponentManager.TryGetComponent(uid, out StunnableComponent? stun) && _robustRandom.Prob(component.StunChance))
stun.Stun(component.StunSeconds);
damageable.TryChangeDamage(component.DamageType, damage);
var damageScale = (speed / component.MinimumSpeed) * component.Factor;
_damageableSystem.TryChangeDamage(uid, component.Damage * damageScale);
}
}
}

View File

@@ -1,35 +1,24 @@
using Content.Server.Damage.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Throwing;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
namespace Content.Server.Damage.Systems
{
public sealed class DamageOnLandSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DamageOnLandComponent, ComponentInit>(HandleInit);
SubscribeLocalEvent<DamageOnLandComponent, LandEvent>(DamageOnLand);
}
private void HandleInit(EntityUid uid, DamageOnLandComponent component, ComponentInit args)
{
component.DamageType = _protoManager.Index<DamageTypePrototype>(component.DamageTypeId);
}
private void DamageOnLand(EntityUid uid, DamageOnLandComponent component, LandEvent args)
{
if (!ComponentManager.TryGetComponent<IDamageableComponent>(uid, out var damageable))
return;
damageable.TryChangeDamage(component.DamageType, component.Amount, component.IgnoreResistances);
_damageableSystem.TryChangeDamage(uid, component.Damage, component.IgnoreResistances);
}
}
}

View File

@@ -1,12 +1,15 @@
using Content.Server.Damage.Components;
using Content.Shared.Damage.Components;
using Content.Shared.Damage;
using Content.Shared.Throwing;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.Damage.Systems
{
public class DamageOtherOnHitSystem : EntitySystem
{
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
public override void Initialize()
{
SubscribeLocalEvent<DamageOtherOnHitComponent, ThrowDoHitEvent>(OnDoHit);
@@ -14,10 +17,7 @@ namespace Content.Server.Damage.Systems
private void OnDoHit(EntityUid uid, DamageOtherOnHitComponent component, ThrowDoHitEvent args)
{
if (!args.Target.TryGetComponent(out IDamageableComponent? damageable))
return;
damageable.TryChangeDamage(component.DamageType, component.Amount, component.IgnoreResistances);
_damageableSystem.TryChangeDamage(args.Target.Uid, component.Damage, component.IgnoreResistances);
}
}
}

View File

@@ -1,11 +1,10 @@
using System.Collections.Generic;
using System.Linq;
using Content.Server.Atmos.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.GameTicking;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.Damage.Systems
{
@@ -13,6 +12,7 @@ namespace Content.Server.Damage.Systems
public class GodmodeSystem : EntitySystem
{
private readonly Dictionary<IEntity, OldEntityInformation> _entities = new();
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
public override void Initialize()
{
@@ -40,11 +40,9 @@ namespace Content.Server.Damage.Systems
moved.Enabled = false;
}
if (entity.TryGetComponent(out IDamageableComponent? damageable))
if (entity.TryGetComponent(out DamageableComponent? damageable))
{
damageable.SupportedDamageTypes.Clear();
damageable.FullySupportedDamageGroups.Clear();
damageable.ApplicableDamageGroups.Clear();
_damageableSystem.SetDamage(damageable, new DamageSpecifier());
}
return true;
@@ -67,21 +65,11 @@ namespace Content.Server.Damage.Systems
moved.Enabled = old.MovedByPressure;
}
if (entity.TryGetComponent(out IDamageableComponent? damageable))
if (entity.TryGetComponent(out DamageableComponent? damageable))
{
if (old.SupportedDamageTypes != null)
if (old.Damage != null)
{
damageable.SupportedDamageTypes.UnionWith(old.SupportedDamageTypes);
}
if (old.SupportedDamageGroups != null)
{
damageable.FullySupportedDamageGroups.UnionWith(old.SupportedDamageGroups);
}
if (old.ApplicableDamageGroups != null)
{
damageable.ApplicableDamageGroups.UnionWith(old.ApplicableDamageGroups);
_damageableSystem.SetDamage(damageable, old.Damage);
}
}
@@ -114,11 +102,9 @@ namespace Content.Server.Damage.Systems
Entity = entity;
MovedByPressure = entity.IsMovedByPressure();
if (entity.TryGetComponent(out IDamageableComponent? damageable))
if (entity.TryGetComponent(out DamageableComponent? damageable))
{
SupportedDamageTypes = damageable.SupportedDamageTypes.ToHashSet();
SupportedDamageGroups = damageable.FullySupportedDamageGroups.ToHashSet();
ApplicableDamageGroups = damageable.ApplicableDamageGroups.ToHashSet();
Damage = damageable.Damage;
}
}
@@ -126,11 +112,7 @@ namespace Content.Server.Damage.Systems
public bool MovedByPressure { get; }
public HashSet<DamageTypePrototype>? SupportedDamageTypes { get; }
public HashSet<DamageGroupPrototype>? SupportedDamageGroups { get; }
public HashSet<DamageGroupPrototype>? ApplicableDamageGroups { get; }
public DamageSpecifier? Damage { get; }
}
}
}

View File

@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Content.Server.Destructible.Thresholds;
using Content.Shared.Damage;
using Robust.Shared.GameObjects;
@@ -14,50 +14,11 @@ namespace Content.Server.Destructible
[RegisterComponent]
public class DestructibleComponent : Component
{
private DestructibleSystem _destructibleSystem = default!;
public override string Name => "Destructible";
[ViewVariables]
[DataField("thresholds")]
private List<Threshold> _thresholds = new();
public List<DamageThreshold> Thresholds = new();
public IReadOnlyList<Threshold> Thresholds => _thresholds;
protected override void Initialize()
{
base.Initialize();
_destructibleSystem = EntitySystem.Get<DestructibleSystem>();
}
public override void HandleMessage(ComponentMessage message, IComponent? component)
{
base.HandleMessage(message, component);
switch (message)
{
case DamageChangedMessage msg:
{
if (msg.Damageable.Owner != Owner)
{
break;
}
foreach (var threshold in _thresholds)
{
if (threshold.Reached(msg.Damageable, _destructibleSystem))
{
var thresholdMessage = new DestructibleThresholdReachedMessage(this, threshold);
SendMessage(thresholdMessage);
threshold.Execute(Owner, _destructibleSystem);
}
}
break;
}
}
}
}
}

View File

@@ -1,4 +1,6 @@
using Content.Shared.Acts;
using Content.Server.Destructible.Thresholds;
using Content.Shared.Acts;
using Content.Shared.Damage;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
@@ -13,5 +15,44 @@ namespace Content.Server.Destructible
[Dependency] public readonly IRobustRandom Random = default!;
[Dependency] public readonly AudioSystem AudioSystem = default!;
[Dependency] public readonly ActSystem ActSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DestructibleComponent, DamageChangedEvent>(Execute);
}
/// <summary>
/// Check if any thresholds were reached. if they were, execute them.
/// </summary>
public void Execute(EntityUid uid, DestructibleComponent component, DamageChangedEvent args)
{
foreach (var threshold in component.Thresholds)
{
if (threshold.Reached(args.Damageable, this))
{
RaiseLocalEvent(uid, new DamageThresholdReached(component, threshold));
threshold.Execute(component.Owner, this);
}
}
}
}
// Currently only used for destructible integration tests. Unless other uses are found for this, maybe this should just be removed and the tests redone.
/// <summary>
/// Event raised when a <see cref="DamageThreshold"/> is reached.
/// </summary>
public class DamageThresholdReached : EntityEventArgs
{
public readonly DestructibleComponent Parent;
public readonly DamageThreshold Threshold;
public DamageThresholdReached(DestructibleComponent parent, DamageThreshold threshold)
{
Parent = parent;
Threshold = threshold;
}
}
}

View File

@@ -1,18 +0,0 @@
using Content.Server.Destructible.Thresholds;
using Robust.Shared.GameObjects;
namespace Content.Server.Destructible
{
public class DestructibleThresholdReachedMessage : ComponentMessage
{
public DestructibleThresholdReachedMessage(DestructibleComponent parent, Threshold threshold)
{
Parent = parent;
Threshold = threshold;
}
public DestructibleComponent Parent { get; }
public Threshold Threshold { get; }
}
}

View File

@@ -1,7 +1,7 @@
using System.Collections.Generic;
using System.Collections.Generic;
using Content.Server.Destructible.Thresholds.Behaviors;
using Content.Server.Destructible.Thresholds.Triggers;
using Content.Shared.Damage.Components;
using Content.Shared.Damage;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
@@ -9,7 +9,7 @@ using Robust.Shared.ViewVariables;
namespace Content.Server.Destructible.Thresholds
{
[DataDefinition]
public class Threshold
public class DamageThreshold
{
[DataField("behaviors")]
private List<IThresholdBehavior> _behaviors = new();
@@ -49,7 +49,7 @@ namespace Content.Server.Destructible.Thresholds
/// </summary>
[ViewVariables] public IReadOnlyList<IThresholdBehavior> Behaviors => _behaviors;
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
public bool Reached(DamageableComponent damageable, DestructibleSystem system)
{
if (Trigger == null)
{

View File

@@ -1,6 +1,6 @@
using System;
using System;
using System.Collections.Generic;
using Content.Shared.Damage.Components;
using Content.Shared.Damage;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Server.Destructible.Thresholds.Triggers
@@ -15,7 +15,7 @@ namespace Content.Server.Destructible.Thresholds.Triggers
[DataField("triggers")]
public List<IThresholdTrigger> Triggers { get; set; } = new();
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
public bool Reached(DamageableComponent damageable, DestructibleSystem system)
{
foreach (var trigger in Triggers)
{

View File

@@ -1,9 +1,8 @@
using System;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Content.Shared.Damage.Prototypes;
namespace Content.Server.Destructible.Thresholds.Triggers
{
@@ -15,12 +14,8 @@ namespace Content.Server.Destructible.Thresholds.Triggers
[DataDefinition]
public class DamageGroupTrigger : IThresholdTrigger
{
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// While you're at it, maybe also combine damageGroup and damage into a dictionary, and allow it to test a sum
// of damage types?
[DataField("damageGroup", required: true)]
private string _damageGroupID { get; set; } = default!;
public DamageGroupPrototype DamageGroup => IoCManager.Resolve<IPrototypeManager>().Index<DamageGroupPrototype>(_damageGroupID);
[DataField("damageGroup", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<DamageGroupPrototype>))]
public string DamageGroup { get; set; } = default!;
/// <summary>
/// The amount of damage at which this threshold will trigger.
@@ -28,15 +23,9 @@ namespace Content.Server.Destructible.Thresholds.Triggers
[DataField("damage", required: true)]
public int Damage { get; set; } = default!;
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
public bool Reached(DamageableComponent damageable, DestructibleSystem system)
{
if (DamageGroup == null)
{
return false;
}
return damageable.TryGetDamage(DamageGroup, out var damageReceived) &&
damageReceived >= Damage;
return damageable.DamagePerGroup[DamageGroup] >= Damage;
}
}
}

View File

@@ -1,5 +1,5 @@
using System;
using Content.Shared.Damage.Components;
using Content.Shared.Damage;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Server.Destructible.Thresholds.Triggers
@@ -18,7 +18,7 @@ namespace Content.Server.Destructible.Thresholds.Triggers
[DataField("damage", required: true)]
public int Damage { get; set; } = default!;
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
public bool Reached(DamageableComponent damageable, DestructibleSystem system)
{
return damageable.TotalDamage >= Damage;
}

View File

@@ -1,9 +1,8 @@
using System;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Content.Shared.Damage.Prototypes;
namespace Content.Server.Destructible.Thresholds.Triggers
{
@@ -15,24 +14,15 @@ namespace Content.Server.Destructible.Thresholds.Triggers
[DataDefinition]
public class DamageTypeTrigger : IThresholdTrigger
{
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// While you're at it, maybe also combine damageGroup and damage into a dictionary, and allow it to test a sum
// of damage types?
[DataField("damageType", required:true)]
public string _damageTypeID { get; set; } = default!;
public DamageTypePrototype DamageType => IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
[DataField("damageType", required:true, customTypeSerializer: typeof(PrototypeIdSerializer<DamageTypePrototype>))]
public string DamageType { get; set; } = default!;
[DataField("damage", required: true)]
public int Damage { get; set; } = default!;
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
public bool Reached(DamageableComponent damageable, DestructibleSystem system)
{
if (DamageType == null)
{
return false;
}
return damageable.TryGetDamage(DamageType, out var damageReceived) &&
return damageable.Damage.DamageDict.TryGetValue(DamageType, out var damageReceived) &&
damageReceived >= Damage;
}
}

View File

@@ -1,4 +1,4 @@
using Content.Shared.Damage.Components;
using Content.Shared.Damage;
namespace Content.Server.Destructible.Thresholds.Triggers
{
@@ -13,6 +13,6 @@ namespace Content.Server.Destructible.Thresholds.Triggers
/// dependencies from, if any.
/// </param>
/// <returns>true if this trigger has been reached, false otherwise.</returns>
bool Reached(IDamageableComponent damageable, DestructibleSystem system);
bool Reached(DamageableComponent damageable, DestructibleSystem system);
}
}

View File

@@ -1,6 +1,6 @@
using System;
using System;
using System.Collections.Generic;
using Content.Shared.Damage.Components;
using Content.Shared.Damage;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Server.Destructible.Thresholds.Triggers
@@ -15,7 +15,7 @@ namespace Content.Server.Destructible.Thresholds.Triggers
[DataField("triggers")]
public List<IThresholdTrigger> Triggers { get; } = new();
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
public bool Reached(DamageableComponent damageable, DestructibleSystem system)
{
foreach (var trigger in Triggers)
{

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic;
using Content.Shared.Damage;
using Content.Shared.DoAfter;
using Robust.Shared.GameObjects;
using Robust.Shared.Players;
@@ -40,35 +39,6 @@ namespace Content.Server.DoAfter
return new DoAfterComponentState(toAdd);
}
public override void HandleMessage(ComponentMessage message, IComponent? component)
{
base.HandleMessage(message, component);
switch (message)
{
case DamageChangedMessage msg:
if (DoAfters.Count == 0)
{
return;
}
if (!msg.TookDamage)
{
return;
}
foreach (var doAfter in _doAfters.Keys)
{
if (doAfter.EventArgs.BreakOnDamage)
{
doAfter.TookDamage = true;
}
}
break;
}
}
public void Add(DoAfter doAfter)
{
_doAfters.Add(doAfter, _runningIndex);

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Content.Shared.Damage;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
@@ -14,6 +15,28 @@ namespace Content.Server.DoAfter
private readonly List<DoAfter> _cancelled = new();
private readonly List<DoAfter> _finished = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DoAfterComponent, DamageChangedEvent>(HandleDamage);
}
public void HandleDamage(EntityUid _, DoAfterComponent component, DamageChangedEvent args)
{
if (component.DoAfters.Count == 0 || !args.DamageIncreased)
{
return;
}
foreach (var doAfter in component.DoAfters)
{
if (doAfter.EventArgs.BreakOnDamage)
{
doAfter.TookDamage = true;
}
}
}
public override void Update(float frameTime)
{
base.Update(frameTime);

View File

@@ -11,7 +11,6 @@ using Content.Server.Hands.Components;
using Content.Server.Stunnable.Components;
using Content.Server.Tools.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Doors;
using Content.Shared.Interaction;
using Content.Shared.Sound;
@@ -22,16 +21,11 @@ using Robust.Shared.GameObjects;
using Robust.Shared.Log;
using Robust.Shared.Maths;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Broadphase;
using Robust.Shared.Physics.Collision;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Player;
using Robust.Shared.Players;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using Timer = Robust.Shared.Timing.Timer;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
namespace Content.Server.Doors.Components
{
@@ -47,17 +41,9 @@ namespace Content.Server.Doors.Components
[DataField("tryOpenDoorSound")]
private SoundSpecifier _tryOpenDoorSound = new SoundPathSpecifier("/Audio/Effects/bang.ogg");
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")]
private readonly string _damageTypeID = "Blunt";
[DataField("crushDamage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
}
public DamageSpecifier CrushDamage = default!;
public override DoorState State
{
@@ -90,7 +76,6 @@ namespace Content.Server.Doors.Components
private CancellationTokenSource? _stateChangeCancelTokenSource;
private CancellationTokenSource? _autoCloseCancelTokenSource;
private const int DoorCrushDamage = 15;
private const float DoorStunTime = 5f;
/// <summary>
@@ -537,7 +522,7 @@ namespace Content.Server.Doors.Components
foreach (var e in collidingentities)
{
if (!e.Owner.TryGetComponent(out StunnableComponent? stun)
|| !e.Owner.TryGetComponent(out IDamageableComponent? damage))
|| !e.Owner.HasComponent<DamageableComponent>())
{
continue;
}
@@ -550,7 +535,8 @@ namespace Content.Server.Doors.Components
hitsomebody = true;
CurrentlyCrushing.Add(e.Owner.Uid);
damage.TryChangeDamage(DamageType, DoorCrushDamage);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(e.Owner.Uid, CrushDamage);
stun.Paralyze(DoorStunTime);
}

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic;
using Content.Server.Ghost.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.Ghost;
using Content.Shared.MobState;
using Content.Shared.Preferences;
@@ -64,12 +64,10 @@ namespace Content.Server.GameTicking.Presets
{
canReturn = true;
if (playerEntity.TryGetComponent(out IDamageableComponent? damageable))
{
//todo: what if they dont breathe lol
//cry deeply
damageable.TrySetDamage(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Asphyxiation"), 200);
}
//todo: what if they dont breathe lol
//cry deeply
DamageSpecifier damage = new(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Asphyxiation"), 200);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(playerEntity.Uid, damage, true);
}
}

View File

@@ -15,7 +15,6 @@ using Content.Server.Traitor;
using Content.Server.TraitorDeathMatch.Components;
using Content.Shared.CCVar;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Inventory;
using Content.Shared.MobState;
using Content.Shared.PDA;
@@ -28,6 +27,7 @@ using Robust.Shared.Localization;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Random;
using Content.Shared.Damage.Prototypes;
namespace Content.Server.GameTicking.Presets
{
@@ -194,13 +194,10 @@ namespace Content.Server.GameTicking.Presets
{
if (mobState.IsCritical())
{
// TODO: This is copy/pasted from ghost code. Really, IDamageableComponent needs a method to reliably kill the target.
if (entity.TryGetComponent(out IDamageableComponent? damageable))
{
//todo: what if they dont breathe lol
damageable.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>("Asphyxiation"), 100, true);
}
}
// TODO BODY SYSTEM KILL
var damage = new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>("Asphyxiation"), 100);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(entity.Uid, damage, true);
}
else if (!mobState.IsDead())
{
if (entity.HasComponent<HandsComponent>())

View File

@@ -34,7 +34,7 @@ namespace Content.Server.GameTicking.Rules
{
_chatManager.DispatchServerAnnouncement(Loc.GetString("rule-death-match-added-announcement"));
_entityManager.EventBus.SubscribeEvent<DamageChangedEventArgs>(EventSource.Local, this, OnHealthChanged);
_entityManager.EventBus.SubscribeEvent<DamageChangedEvent>(EventSource.Local, this, OnHealthChanged);
_playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged;
}
@@ -42,11 +42,11 @@ namespace Content.Server.GameTicking.Rules
{
base.Removed();
_entityManager.EventBus.UnsubscribeEvent<DamageChangedEventArgs>(EventSource.Local, this);
_entityManager.EventBus.UnsubscribeEvent<DamageChangedEvent>(EventSource.Local, this);
_playerManager.PlayerStatusChanged -= PlayerManagerOnPlayerStatusChanged;
}
private void OnHealthChanged(DamageChangedEventArgs message)
private void OnHealthChanged(DamageChangedEvent _)
{
_runDelayedCheck();
}

View File

@@ -6,7 +6,6 @@ using Content.Server.Power.Components;
using Content.Server.Temperature.Components;
using Content.Shared.Audio;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Interaction;
using Content.Shared.Light;
using Content.Shared.Notification.Managers;
@@ -16,7 +15,6 @@ using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Player;
@@ -81,16 +79,13 @@ namespace Content.Server.Light.Components
[ViewVariables] private ContainerSlot _lightBulbContainer = default!;
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
[DataField("damageType")]
private readonly string _damageTypeID = "Heat";
[DataField("damage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
public DamageSpecifier Damage = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
_lightBulbContainer = Owner.EnsureContainer<ContainerSlot>("light_bulb");
}
@@ -116,7 +111,7 @@ namespace Content.Server.Light.Components
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
{
if (!eventArgs.User.TryGetComponent(out IDamageableComponent? damageableComponent))
if (!eventArgs.User.HasComponent<DamageableComponent>())
{
Eject();
return false;
@@ -143,7 +138,7 @@ namespace Content.Server.Light.Components
void Burn()
{
Owner.PopupMessage(eventArgs.User, Loc.GetString("powered-light-component-burn-hand"));
damageableComponent.TryChangeDamage(DamageType, 20);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(eventArgs.User.Uid, Damage);
SoundSystem.Play(Filter.Pvs(Owner), _burnHandSound.GetSound(), Owner);
}
@@ -285,17 +280,11 @@ namespace Content.Server.Light.Components
case PowerChangedMessage:
UpdateLight();
break;
case DamageChangedMessage msg:
TryDestroyBulb(msg);
break;
}
}
private void TryDestroyBulb(DamageChangedMessage msg)
public void TryDestroyBulb()
{
if (!msg.TookDamage)
return;
if (LightBulb == null || LightBulb.State == LightBulbState.Broken)
return;

View File

@@ -1,17 +1,19 @@
using System;
using Content.Server.Ghost;
using Content.Server.Light.Components;
using Content.Server.MachineLinking.Events;
using Content.Shared.Light;
using Content.Shared.Damage;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Timing;
using Content.Server.Light.Components;
using Content.Server.MachineLinking.Events;
using Robust.Shared.GameObjects;
namespace Content.Server.Light.EntitySystems
{
/// <summary>
/// System for the PoweredLightComponent. Currently bare-bones, to handle events from the DamageableSystem
/// </summary>
public class PoweredLightSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
@@ -21,6 +23,20 @@ namespace Content.Server.Light.EntitySystems
base.Initialize();
SubscribeLocalEvent<PoweredLightComponent, GhostBooEvent>(OnGhostBoo);
SubscribeLocalEvent<PoweredLightComponent, SignalReceivedEvent>(OnSignalReceived);
SubscribeLocalEvent<PoweredLightComponent, DamageChangedEvent>(HandleLightDamaged);
}
/// <summary>
/// Destroy the light bulb if the light took any damage.
/// </summary>
public void HandleLightDamaged(EntityUid uid, PoweredLightComponent component, DamageChangedEvent args)
{
// Was it being repaired, or did it take damage?
if (args.DamageIncreased)
{
// Eventually, this logic should all be done by this (or some other) system, not a component.
component.TryDestroyBulb();
}
}
private void OnGhostBoo(EntityUid uid, PoweredLightComponent light, GhostBooEvent args)

View File

@@ -1,16 +1,12 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Content.Server.Stack;
using Content.Shared.ActionBlocker;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Helpers;
using Content.Shared.Stacks;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
namespace Content.Server.Medical.Components
@@ -20,12 +16,9 @@ namespace Content.Server.Medical.Components
{
public override string Name => "Healing";
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// This also requires changing the dictionary type, and removing a _prototypeManager.Index() call.
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[DataField("heal", required: true )]
[DataField("damage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public Dictionary<string, int> Heal = new();
public DamageSpecifier Damage = default!;
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
{
@@ -34,7 +27,7 @@ namespace Content.Server.Medical.Components
return false;
}
if (!eventArgs.Target.TryGetComponent(out IDamageableComponent? damageable))
if (!eventArgs.Target.HasComponent<DamageableComponent>())
{
return true;
}
@@ -55,10 +48,7 @@ namespace Content.Server.Medical.Components
return true;
}
foreach (var (damageTypeID, amount) in Heal)
{
damageable.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(damageTypeID), -amount, true);
}
EntitySystem.Get<DamageableSystem>().TryChangeDamage(eventArgs.Target.Uid, Damage, true);
return true;
}

View File

@@ -8,7 +8,6 @@ using Content.Server.UserInterface;
using Content.Shared.ActionBlocker;
using Content.Shared.Acts;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.DragDrop;
using Content.Shared.Interaction;
using Content.Shared.MedicalScanner;
@@ -99,8 +98,7 @@ namespace Content.Server.Medical.Components
private static readonly MedicalScannerBoundUserInterfaceState EmptyUIState =
new(
null,
new Dictionary<string, int>(),
new Dictionary<string, int>(),
null,
false);
private MedicalScannerBoundUserInterfaceState GetUserInterfaceState()
@@ -116,18 +114,14 @@ namespace Content.Server.Medical.Components
return EmptyUIState;
}
if (!body.TryGetComponent(out IDamageableComponent? damageable))
if (!body.TryGetComponent(out DamageableComponent? damageable))
{
return EmptyUIState;
}
// Get dictionaries of damage, by fully supported damage groups and types
var groups = new Dictionary<string, int>(damageable.GetDamagePerFullySupportedGroupIDs);
var types = new Dictionary<string, int>(damageable.GetDamagePerTypeIDs);
if (_bodyContainer.ContainedEntity?.Uid == null)
{
return new MedicalScannerBoundUserInterfaceState(body.Uid, groups, types, true);
return new MedicalScannerBoundUserInterfaceState(body.Uid, damageable, true);
}
var cloningSystem = EntitySystem.Get<CloningSystem>();
@@ -135,7 +129,7 @@ namespace Content.Server.Medical.Components
mindComponent.Mind != null &&
cloningSystem.HasDnaScan(mindComponent.Mind);
return new MedicalScannerBoundUserInterfaceState(body.Uid, groups, types, scanned);
return new MedicalScannerBoundUserInterfaceState(body.Uid, damageable, scanned);
}
private void UpdateUserInterface()

View File

@@ -1,7 +1,6 @@
using System.Threading.Tasks;
using Content.Server.Weapon.Melee.Components;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Interaction;
using Content.Shared.Mining;
using Robust.Server.GameObjects;
@@ -24,16 +23,9 @@ namespace Content.Server.Mining.Components
public override string Name => "AsteroidRock";
private static readonly string[] SpriteStates = {"0", "1", "2", "3", "4"};
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
[DataField("damageType")]
private readonly string _damageTypeID = "Blunt"!;
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
{
appearance.SetData(AsteroidRockVisuals.State, _random.Pick(SpriteStates));
@@ -46,7 +38,7 @@ namespace Content.Server.Mining.Components
if (!item.TryGetComponent(out MeleeWeaponComponent? meleeWeaponComponent))
return false;
Owner.GetComponent<IDamageableComponent>().TryChangeDamage(DamageType, meleeWeaponComponent.Damage);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(Owner.Uid, meleeWeaponComponent.Damage);
if (!item.TryGetComponent(out PickaxeComponent? pickaxeComponent))
return true;

View File

@@ -1,14 +0,0 @@
using Content.Shared.MobState;
using Content.Shared.MobState.Components;
using Content.Shared.MobState.State;
using Robust.Shared.GameObjects;
namespace Content.Server.MobState.States
{
[RegisterComponent]
[ComponentReference(typeof(SharedMobStateComponent))]
[ComponentReference(typeof(IMobStateComponent))]
public class MobStateComponent : SharedMobStateComponent
{
}
}

View File

@@ -1,6 +1,6 @@
using Content.Server.Alert;
using Content.Server.Alert;
using Content.Shared.Alert;
using Content.Shared.Damage.Components;
using Content.Shared.Damage;
using Content.Shared.MobState;
using Content.Shared.MobState.State;
using Robust.Shared.GameObjects;
@@ -13,7 +13,7 @@ namespace Content.Server.MobState.States
{
base.UpdateState(entity, threshold);
if (!entity.TryGetComponent(out IDamageableComponent? damageable))
if (!entity.TryGetComponent(out DamageableComponent? damageable))
{
return;
}

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using Content.Server.Alert;
using Content.Shared.Alert;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.MobState;
using Content.Shared.Movement.Components;
using Content.Shared.Nutrition.Components;
@@ -14,7 +13,6 @@ using Robust.Shared.Players;
using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using Robust.Shared.Prototypes;
namespace Content.Server.Nutrition.Components
{
@@ -23,9 +21,7 @@ namespace Content.Server.Nutrition.Components
{
[Dependency] private readonly IRobustRandom _random = default!;
// TODO DAMAGE UNITS When damage units support decimals, get rid of this.
// See also _accumulatedDamage in ThirstComponent and HealthChange.
private float _accumulatedDamage;
private float _accumulatedFrameTime;
// Base stuff
[ViewVariables(VVAccess.ReadWrite)]
@@ -78,17 +74,9 @@ namespace Content.Server.Nutrition.Components
{ HungerThreshold.Starving, AlertType.Starving },
};
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")]
private readonly string _damageTypeID = "Blunt"!;
[DataField("damage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
}
public DamageSpecifier Damage = default!;
public void HungerThresholdEffect(bool force = false)
{
@@ -196,21 +184,17 @@ namespace Content.Server.Nutrition.Components
return;
// --> Current Hunger is below dead threshold
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
return;
if (!Owner.TryGetComponent(out IMobStateComponent? mobState))
return;
if (!mobState.IsDead())
{
// --> But they are not dead yet.
var damage = 2 * frametime;
_accumulatedDamage += damage - ((int) damage);
damageable.TryChangeDamage(DamageType, (int) damage);
if (_accumulatedDamage >= 1) {
_accumulatedDamage -= 1;
damageable.TryChangeDamage(DamageType, 1, true);
_accumulatedFrameTime += frametime;
if (_accumulatedFrameTime >= 1)
{
EntitySystem.Get<DamageableSystem>().TryChangeDamage(Owner.Uid, Damage * (int) _accumulatedFrameTime, true);
_accumulatedFrameTime -= (int) _accumulatedFrameTime;
}
}
}

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using Content.Server.Alert;
using Content.Shared.Alert;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.MobState;
using Content.Shared.Movement.Components;
using Content.Shared.Nutrition.Components;
@@ -14,7 +13,6 @@ using Robust.Shared.Players;
using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using Robust.Shared.Prototypes;
namespace Content.Server.Nutrition.Components
{
@@ -23,9 +21,7 @@ namespace Content.Server.Nutrition.Components
{
[Dependency] private readonly IRobustRandom _random = default!;
// TODO DAMAGE UNITS When damage units support decimals, get rid of this.
// See also _accumulatedDamage in HungerComponent and HealthChange.
private float _accumulatedDamage;
private float _accumulatedFrameTime;
// Base stuff
[ViewVariables(VVAccess.ReadWrite)]
@@ -77,17 +73,9 @@ namespace Content.Server.Nutrition.Components
{ThirstThreshold.Parched, AlertType.Parched},
};
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("damageType")]
private readonly string _damageTypeID = "Blunt";
[DataField("damage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype DamageType = default!;
protected override void Initialize()
{
base.Initialize();
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
}
public DamageSpecifier Damage = default!;
public void ThirstThresholdEffect(bool force = false)
{
@@ -193,22 +181,17 @@ namespace Content.Server.Nutrition.Components
return;
// --> Current Hunger is below dead threshold
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
return;
if (!Owner.TryGetComponent(out IMobStateComponent? mobState))
return;
if (!mobState.IsDead())
{
// --> But they are not dead yet.
var damage = 2 * frametime;
_accumulatedDamage += damage - ((int) damage);
damageable.TryChangeDamage(DamageType, (int) damage);
if (_accumulatedDamage >= 1)
_accumulatedFrameTime += frametime;
if (_accumulatedFrameTime >= 1)
{
_accumulatedDamage -= 1;
damageable.TryChangeDamage(DamageType, 1, true);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(Owner.Uid, Damage * (int) _accumulatedFrameTime, true);
_accumulatedFrameTime -= (int) _accumulatedFrameTime;
}
}
}

View File

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

View File

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

View File

@@ -1,22 +1,20 @@
using Content.Server.Camera;
using Content.Server.Projectiles.Components;
using Content.Shared.Body.Components;
using Content.Shared.Damage.Components;
using Content.Shared.Damage;
using JetBrains.Annotations;
using Robust.Shared.Audio;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.IoC;
using Content.Shared.Damage;
namespace Content.Server.Projectiles
{
[UsedImplicitly]
internal sealed class ProjectileSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
public override void Initialize()
{
@@ -50,16 +48,12 @@ namespace Content.Server.Projectiles
SoundSystem.Play(playerFilter, soundHit, coordinates);
}
if (!otherEntity.Deleted && otherEntity.TryGetComponent(out IDamageableComponent? damage))
if (!otherEntity.Deleted)
{
EntityManager.TryGetEntity(component.Shooter, out var shooter);
foreach (var (damageTypeID, amount) in component.Damages)
{
damage.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(damageTypeID), amount);
}
_damageableSystem.TryChangeDamage(otherEntity.Uid, component.Damage);
component.DamagedEntity = true;
// "DamagedEntity" is misleading. Hit entity may be more accurate, as the damage may have been resisted
// by resistance sets.
}
// Damaging it can delete it

View File

@@ -1,50 +1,18 @@
using System.Threading.Tasks;
using Content.Server.Tools.Components;
using Content.Shared.Damage.Components;
using Content.Shared.Interaction;
using Content.Shared.Notification;
using Content.Shared.Notification.Managers;
using Content.Shared.Tool;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Repairable
{
[RegisterComponent]
public class RepairableComponent : Component, IInteractUsing
public class RepairableComponent : Component
{
public override string Name => "Repairable";
[ViewVariables(VVAccess.ReadWrite)] [DataField("fuelCost")]
private int _fuelCost = 5;
public int FuelCost = 5;
[ViewVariables(VVAccess.ReadWrite)] [DataField("doAfterDelay")]
private int _doAfterDelay = 1;
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
{
// Only repair if you are using a lit welder
if (!eventArgs.Using.TryGetComponent(out WelderComponent? welder) || !welder.WelderLit)
return false;
if (Owner.TryGetComponent(out IDamageableComponent? damageable))
{
// Repair the target if it is damaged, oherwise do nothing
if (damageable.TotalDamage > 0)
{
if (!await welder.UseTool(eventArgs.User, Owner, _doAfterDelay, ToolQuality.Welding, _fuelCost))
return false;
damageable.TrySetAllDamage(0);
Owner.PopupMessage(eventArgs.User,
Loc.GetString("comp-repairable-repair",
("target", Owner),
("welder", eventArgs.Using)));
}
}
return true;
}
public int DoAfterDelay = 1;
}
}

View File

@@ -0,0 +1,46 @@
using Content.Server.Tools.Components;
using Content.Shared.Damage;
using Content.Shared.Interaction;
using Content.Shared.Notification.Managers;
using Content.Shared.Tool;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
namespace Content.Server.Repairable
{
public class ReairableSystem : EntitySystem
{
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
public override void Initialize()
{
SubscribeLocalEvent<RepairableComponent, InteractUsingEvent>(Repair);
}
public async void Repair(EntityUid uid, RepairableComponent component, InteractUsingEvent args)
{
// Only repair if you are using a lit welder
if (!args.Used.TryGetComponent(out WelderComponent? welder) || !welder.WelderLit)
return;
// Only try repair the target if it is damaged
if (!component.Owner.TryGetComponent(out DamageableComponent? damageable) || damageable.TotalDamage == 0)
return;
// Can the welder actually repair this, does it have enough fuel?
if (!await welder.UseTool(args.User, component.Owner, component.DoAfterDelay, ToolQuality.Welding, component.FuelCost))
return;
// Repair all damage
_damageableSystem.SetAllDamage(damageable, 0);
component.Owner.PopupMessage(args.User,
Loc.GetString("comp-repairable-repair",
("target", component.Owner),
("welder", args.Used)));
args.Handled = true;
}
}
}

View File

@@ -3,7 +3,6 @@ using Content.Server.Alert;
using Content.Shared.Alert;
using Content.Shared.Atmos;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -51,22 +50,13 @@ namespace Content.Server.Temperature.Components
}
}
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// Also remove Initialize override, if no longer needed.
[DataField("coldDamageType")]
private readonly string _coldDamageTypeID = "Cold";
[DataField("coldDamage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype ColdDamageType = default!;
[DataField("hotDamageType")]
private readonly string _hotDamageTypeID = "Heat";
public DamageSpecifier ColdDamage = default!;
[DataField("heatDamage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageTypePrototype HotDamageType = default!;
protected override void Initialize()
{
base.Initialize();
ColdDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_coldDamageTypeID);
HotDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_hotDamageTypeID);
}
public DamageSpecifier HeatDamage = default!;
public void Update()
{
@@ -112,19 +102,18 @@ namespace Content.Server.Temperature.Components
}
}
if (!Owner.TryGetComponent(out IDamageableComponent? component)) return;
if (!Owner.HasComponent<DamageableComponent>()) return;
if (CurrentTemperature >= _heatDamageThreshold)
{
int tempDamage = (int) Math.Floor((CurrentTemperature - _heatDamageThreshold) * _tempDamageCoefficient);
component.TryChangeDamage(HotDamageType, tempDamage, false);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(Owner.Uid, HeatDamage * tempDamage);
}
else if (CurrentTemperature <= _coldDamageThreshold)
{
int tempDamage = (int) Math.Floor((_coldDamageThreshold - CurrentTemperature) * _tempDamageCoefficient);
component.TryChangeDamage(ColdDamageType, tempDamage, false);
EntitySystem.Get<DamageableSystem>().TryChangeDamage(Owner.Uid, ColdDamage * tempDamage);
}
}
/// <summary>

View File

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

View File

@@ -6,7 +6,7 @@ using Content.Server.Chemistry.Components;
using Content.Server.Cooldown;
using Content.Server.Weapon.Melee.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Damage.Components;
using Content.Shared.Damage;
using Content.Shared.Hands;
using Content.Shared.Interaction;
using Content.Shared.Physics;
@@ -25,6 +25,7 @@ namespace Content.Server.Weapon.Melee
public sealed class MeleeWeaponSystem : EntitySystem
{
[Dependency] private IGameTiming _gameTiming = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private SolutionContainerSystem _solutionsSystem = default!;
public override void Initialize()
@@ -87,12 +88,7 @@ namespace Content.Server.Weapon.Melee
{
var targets = new[] { target };
SendAnimation(comp.ClickArc, angle, args.User, owner, targets, comp.ClickAttackEffect, false);
if (target.TryGetComponent(out IDamageableComponent? damageableComponent))
{
damageableComponent.TryChangeDamage(comp.DamageType, comp.Damage);
}
_damageableSystem.TryChangeDamage(target.Uid, comp.Damage);
SoundSystem.Play(Filter.Pvs(owner), comp.HitSound.GetSound(), target);
}
}
@@ -133,7 +129,7 @@ namespace Content.Server.Weapon.Melee
if (!entity.Transform.IsMapTransform || entity == args.User)
continue;
if (ComponentManager.HasComponent<IDamageableComponent>(entity.Uid))
if (ComponentManager.HasComponent<DamageableComponent>(entity.Uid))
{
hitEntities.Add(entity);
}
@@ -157,10 +153,7 @@ namespace Content.Server.Weapon.Melee
foreach (var entity in hitEntities)
{
if (entity.TryGetComponent<IDamageableComponent>(out var damageComponent))
{
damageComponent.TryChangeDamage(comp.DamageType, comp.Damage);
}
_damageableSystem.TryChangeDamage(entity.Uid, comp.Damage);
}
}

View File

@@ -1,12 +1,10 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Content.Server.Hands.Components;
using Content.Server.Items;
using Content.Server.Power.Components;
using Content.Server.Projectiles.Components;
using Content.Shared.ActionBlocker;
using Content.Shared.Damage;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Sound;
@@ -188,13 +186,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
{
if (energyRatio < 1.0)
{
var newDamages = new Dictionary<string, int>(projectileComponent.Damages.Count);
foreach (var (damageType, damage) in projectileComponent.Damages)
{
newDamages.Add(damageType, (int) (damage * energyRatio));
}
projectileComponent.Damages = newDamages;
projectileComponent.Damage *= energyRatio;
}
} else if (entity.TryGetComponent(out HitscanComponent? hitscanComponent))
{

View File

@@ -6,7 +6,7 @@ using Content.Server.Camera;
using Content.Server.Projectiles.Components;
using Content.Server.Weapon.Ranged.Ammunition.Components;
using Content.Shared.Audio;
using Content.Shared.Damage.Components;
using Content.Shared.Damage;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Sound;
@@ -395,13 +395,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
var result = rayCastResults[0];
var distance = result.Distance;
hitscan.FireEffects(shooter, distance, angle, result.HitEntity);
if (!result.HitEntity.TryGetComponent(out IDamageableComponent? damageable))
return;
damageable.TryChangeDamage(hitscan.DamageType, (int)Math.Round(hitscan.Damage, MidpointRounding.AwayFromZero));
//I used Math.Round over Convert.toInt32, as toInt32 always rounds to
//even numbers if halfway between two numbers, rather than rounding to nearest
EntitySystem.Get<DamageableSystem>().TryChangeDamage(result.HitEntity.Uid, hitscan.Damage);
}
else
{

View File

@@ -1,5 +1,4 @@
using System;
using Content.Server.Atmos;
using Content.Server.Atmos.EntitySystems;
using Content.Server.CombatMode;
using Content.Server.Hands.Components;
@@ -8,9 +7,7 @@ using Content.Server.Stunnable.Components;
using Content.Server.Weapon.Ranged.Barrels.Components;
using Content.Shared.ActionBlocker;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Hands;
using Content.Shared.Interaction.Events;
using Content.Shared.Notification.Managers;
using Content.Shared.Sound;
using Content.Shared.Weapons.Ranged.Components;
@@ -27,8 +24,6 @@ using Robust.Shared.Players;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
using Robust.Shared.ViewVariables;
using Robust.Shared.Prototypes;
using System.Collections.Generic;
namespace Content.Server.Weapon.Ranged
{
@@ -57,17 +52,10 @@ namespace Content.Server.Weapon.Ranged
[DataField("clumsyWeaponShotSound")]
private SoundSpecifier _clumsyWeaponShotSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/bang.ogg");
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
// This also requires changing the dictionary type and modifying TryFire(), which uses it.
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("clumsyDamage")]
public Dictionary<string, int> ClumsyDamage { get; set; } = new()
{
{ "Blunt", 10 },
{ "Heat", 5 }
};
public DamageSpecifier? ClumsyDamage;
public Func<bool>? WeaponCanFireHandler;
public Func<IEntity, bool>? UserCanFireHandler;
@@ -179,16 +167,10 @@ namespace Content.Server.Weapon.Ranged
_lastFireTime = curTime;
if (ClumsyCheck && ClumsyComponent.TryRollClumsy(user, ClumsyExplodeChance))
if (ClumsyCheck && ClumsyDamage != null && ClumsyComponent.TryRollClumsy(user, ClumsyExplodeChance))
{
//Wound them
if (user.TryGetComponent(out IDamageableComponent? health))
{
foreach (KeyValuePair<string, int> damage in ClumsyDamage)
{
health.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(damage.Key), damage.Value);
}
}
EntitySystem.Get<DamageableSystem>().TryChangeDamage(user.Uid, ClumsyDamage);
// Knock them down
if (user.TryGetComponent(out StunnableComponent? stun))
@@ -197,14 +179,14 @@ namespace Content.Server.Weapon.Ranged
}
// Apply salt to the wound ("Honk!")
SoundSystem.Play(
SoundSystem.Play(
Filter.Pvs(Owner), _clumsyWeaponHandlingSound.GetSound(),
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
SoundSystem.Play(
Filter.Pvs(Owner), _clumsyWeaponShotSound.GetSound(),
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
user.PopupMessage(Loc.GetString("server-ranged-weapon-component-try-fire-clumsy"));
Owner.Delete();

View File

@@ -4,7 +4,6 @@ using Content.Server.Destructible.Thresholds.Triggers;
using Content.Server.Notification;
using Content.Shared.Audio;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Rounding;
@@ -41,22 +40,7 @@ namespace Content.Server.Window
[DataField("knockSound")]
private SoundSpecifier _knockSound = new SoundPathSpecifier("/Audio/Effects/glass_knock.ogg");
public override void HandleMessage(ComponentMessage message, IComponent? component)
{
base.HandleMessage(message, component);
switch (message)
{
case DamageChangedMessage msg:
{
var current = msg.Damageable.TotalDamage;
UpdateVisuals(current);
break;
}
}
}
private void UpdateVisuals(int currentDamage)
public void UpdateVisuals(int currentDamage)
{
if (Owner.TryGetComponent(out AppearanceComponent? appearance) &&
Owner.TryGetComponent(out DestructibleComponent? destructible))
@@ -75,7 +59,7 @@ namespace Content.Server.Window
void IExamine.Examine(FormattedMessage message, bool inDetailsRange)
{
if (!Owner.TryGetComponent(out IDamageableComponent? damageable) ||
if (!Owner.TryGetComponent(out DamageableComponent? damageable) ||
!Owner.TryGetComponent(out DestructibleComponent? destructible))
{
return;

View File

@@ -0,0 +1,19 @@
using Content.Shared.Damage;
using Robust.Shared.GameObjects;
namespace Content.Server.Window
{
public class WindowSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<WindowComponent, DamageChangedEvent>(UpdateVisuals);
}
public void UpdateVisuals(EntityUid _, WindowComponent component, DamageChangedEvent args)
{
component.UpdateVisuals(args.Damageable.TotalDamage);
}
}
}