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:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>())
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
46
Content.Server/Repairable/RepairableSystem.cs
Normal file
46
Content.Server/Repairable/RepairableSystem.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
|
||||
@@ -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!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
19
Content.Server/Window/WindowSystem.cs
Normal file
19
Content.Server/Window/WindowSystem.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user