Revert "Refactor Damage to use Protoypes (#4262)"
This reverts commit 20bf5739a9.
This commit is contained in:
@@ -1,77 +0,0 @@
|
|||||||
using System.Text;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.Maths;
|
|
||||||
using static Content.Shared.GameObjects.Components.Medical.SharedMedicalScannerComponent;
|
|
||||||
|
|
||||||
namespace Content.Client.GameObjects.Components.MedicalScanner
|
|
||||||
{
|
|
||||||
public class MedicalScannerWindow : SS14Window
|
|
||||||
{
|
|
||||||
public readonly Button ScanButton;
|
|
||||||
private readonly Label _diagnostics;
|
|
||||||
public MedicalScannerWindow()
|
|
||||||
{
|
|
||||||
SetSize = (250, 100);
|
|
||||||
|
|
||||||
Contents.AddChild(new VBoxContainer
|
|
||||||
{
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
(ScanButton = new Button
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("Scan and Save DNA")
|
|
||||||
}),
|
|
||||||
(_diagnostics = new Label
|
|
||||||
{
|
|
||||||
Text = ""
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Populate(MedicalScannerBoundUserInterfaceState state)
|
|
||||||
{
|
|
||||||
var text = new StringBuilder();
|
|
||||||
|
|
||||||
if (!state.Entity.HasValue ||
|
|
||||||
!state.HasDamage() ||
|
|
||||||
!IoCManager.Resolve<IEntityManager>().TryGetEntity(state.Entity.Value, out var entity))
|
|
||||||
{
|
|
||||||
_diagnostics.Text = Loc.GetString("No patient data.");
|
|
||||||
ScanButton.Disabled = true;
|
|
||||||
SetSize = (250, 100);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
text.Append($"{entity.Name}{Loc.GetString("'s health:")}\n");
|
|
||||||
|
|
||||||
foreach (var (damageGroup, damageAmount) in state.DamageGroup)
|
|
||||||
{
|
|
||||||
text.Append($"\n{Loc.GetString("{0}: {1}", damageGroup, damageAmount)}");
|
|
||||||
|
|
||||||
foreach (var type in damageGroup.DamageTypes)
|
|
||||||
{
|
|
||||||
if (!state.DamageTypes.TryGetValue(type, out var typeAmount))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
text.Append($"\n- {Loc.GetString("{0}: {1}", type, typeAmount)}");
|
|
||||||
}
|
|
||||||
|
|
||||||
text.Append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
_diagnostics.Text = text.ToString();
|
|
||||||
ScanButton.Disabled = state.IsScanned;
|
|
||||||
|
|
||||||
SetSize = (250, 575);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using System.Linq;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using static Content.Shared.MedicalScanner.SharedMedicalScannerComponent;
|
using static Content.Shared.MedicalScanner.SharedMedicalScannerComponent;
|
||||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||||
|
|
||||||
@@ -17,7 +14,6 @@ namespace Content.Client.MedicalScanner.UI
|
|||||||
{
|
{
|
||||||
public readonly Button ScanButton;
|
public readonly Button ScanButton;
|
||||||
private readonly Label _diagnostics;
|
private readonly Label _diagnostics;
|
||||||
|
|
||||||
public MedicalScannerWindow()
|
public MedicalScannerWindow()
|
||||||
{
|
{
|
||||||
SetSize = (250, 100);
|
SetSize = (250, 100);
|
||||||
@@ -55,90 +51,27 @@ namespace Content.Client.MedicalScanner.UI
|
|||||||
{
|
{
|
||||||
text.Append($"{Loc.GetString("medical-scanner-window-entity-health-text", ("entityName", entity.Name))}\n");
|
text.Append($"{Loc.GetString("medical-scanner-window-entity-health-text", ("entityName", entity.Name))}\n");
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
foreach (var (@class, classAmount) in state.DamageClasses)
|
foreach (var (@class, classAmount) in state.DamageClasses)
|
||||||
=======
|
|
||||||
// Show the total damage
|
|
||||||
var totalDamage = state.DamagePerTypeID.Values.Sum();
|
|
||||||
text.Append($"{Loc.GetString("medical-scanner-window-entity-damage-total-text", ("amount", totalDamage))}\n");
|
|
||||||
|
|
||||||
// Keep track of how many damage types we have shown
|
|
||||||
HashSet<string> shownTypeIDs = new();
|
|
||||||
|
|
||||||
// First show just the total damage and type breakdown for each damge group that is fully supported by that entitygroup.
|
|
||||||
foreach (var (damageGroupID, damageAmount) in state.DamagePerSupportedGroupID)
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
{
|
{
|
||||||
|
text.Append($"\n{Loc.GetString("medical-scanner-window-damage-class-text", ("damageClass", @class), ("amount", classAmount))}");
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
foreach (var type in @class.ToTypes())
|
foreach (var type in @class.ToTypes())
|
||||||
=======
|
|
||||||
// Show the total damage
|
|
||||||
var totalDamage = state.DamagePerTypeID.Values.Sum();
|
|
||||||
text.Append($"{Loc.GetString("medical-scanner-window-entity-damage-total-text", ("amount", totalDamage))}\n");
|
|
||||||
|
|
||||||
// Keep track of how many damage types we have shown
|
|
||||||
HashSet<string> shownTypeIDs = new();
|
|
||||||
|
|
||||||
// First show just the total damage and type breakdown for each damge group that is fully supported by that entitygroup.
|
|
||||||
foreach (var (damageGroupID, damageAmount) in state.DamagePerSupportedGroupID)
|
|
||||||
{
|
{
|
||||||
|
if (!state.DamageTypes.TryGetValue(type, out var typeAmount))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
=======
|
text.Append($"\n- {Loc.GetString("medical-scanner-window-damage-type-text", ("damageType",type) ,("amount", typeAmount))}");
|
||||||
>>>>>>> refactor-damageablecomponent
|
}
|
||||||
// Show total damage for the group
|
|
||||||
text.Append($"\n{Loc.GetString("medical-scanner-window-damage-group-text", ("damageGroup", damageGroupID), ("amount", damageAmount))}");
|
|
||||||
|
|
||||||
// Then show the damage for each type in that group.
|
|
||||||
// currently state has a dictionary mapping groupsIDs to damage, and typeIDs to damage, but does not know how types and groups are related.
|
|
||||||
// So use PrototypeManager.
|
|
||||||
var group = IoCManager.Resolve<IPrototypeManager>().Index<DamageGroupPrototype>(damageGroupID);
|
|
||||||
foreach (var type in group.DamageTypes)
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
{
|
|
||||||
if (state.DamagePerTypeID.TryGetValue(type.ID, out var typeAmount))
|
|
||||||
{
|
|
||||||
// If damage types are allowed to belong to more than one damage group, they may appear twice here. Mark them as duplicate.
|
|
||||||
if (!shownTypeIDs.Contains(type.ID))
|
|
||||||
{
|
|
||||||
shownTypeIDs.Add(type.ID);
|
|
||||||
text.Append($"\n- {Loc.GetString("medical-scanner-window-damage-type-text", ("damageType", type.ID), ("amount", typeAmount))}");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
text.Append($"\n- {Loc.GetString("medical-scanner-window-damage-type-duplicate-text", ("damageType", type.ID), ("amount", typeAmount))}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text.Append('\n');
|
text.Append('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then, lets also list any damageType that was not fully Supported by the entity's damageContainer
|
|
||||||
var textAppendix = new StringBuilder();
|
|
||||||
int totalMiscDamage = 0;
|
|
||||||
// Iterate over ids that have not been printed.
|
|
||||||
foreach (var damageTypeID in state.DamagePerTypeID.Keys.Where(typeID => !shownTypeIDs.Contains(typeID)))
|
|
||||||
{
|
|
||||||
//This damage type was not yet added to the text.
|
|
||||||
textAppendix.Append($"\n- {Loc.GetString("medical-scanner-window-damage-type-text", ("damageType", damageTypeID), ("amount", state.DamagePerTypeID[damageTypeID]))}");
|
|
||||||
totalMiscDamage += state.DamagePerTypeID[damageTypeID];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is there any information to show? Did any damage types not belong to a group?
|
|
||||||
if (textAppendix.Length > 0) {
|
|
||||||
text.Append($"\n{Loc.GetString("medical-scanner-window-damage-group-text", ("damageGroup", "Miscellaneous"), ("amount", totalMiscDamage))}");
|
|
||||||
text.Append(textAppendix);
|
|
||||||
}
|
|
||||||
|
|
||||||
_diagnostics.Text = text.ToString();
|
_diagnostics.Text = text.ToString();
|
||||||
ScanButton.Disabled = state.IsScanned;
|
ScanButton.Disabled = state.IsScanned;
|
||||||
|
|
||||||
// TODO MEDICALSCANNER resize window based on the length of text / number of damage types?
|
SetSize = (250, 575);
|
||||||
// Also, maybe add color schemes for specific damage groups?
|
|
||||||
SetSize = (250, 600);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Damage;
|
using Content.Server.Damage;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
@@ -7,7 +7,6 @@ using NUnit.Framework;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Commands
|
namespace Content.IntegrationTests.Tests.Commands
|
||||||
{
|
{
|
||||||
@@ -21,7 +20,7 @@ namespace Content.IntegrationTests.Tests.Commands
|
|||||||
id: DamageableDummy
|
id: DamageableDummy
|
||||||
components:
|
components:
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: biologicalDamageContainer
|
damagePrototype: biologicalDamageContainer
|
||||||
- type: MobState
|
- type: MobState
|
||||||
thresholds:
|
thresholds:
|
||||||
0: !type:NormalMobState {}
|
0: !type:NormalMobState {}
|
||||||
@@ -42,7 +41,6 @@ namespace Content.IntegrationTests.Tests.Commands
|
|||||||
mapManager.CreateNewMapEntity(MapId.Nullspace);
|
mapManager.CreateNewMapEntity(MapId.Nullspace);
|
||||||
|
|
||||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
|
||||||
|
|
||||||
var human = entityManager.SpawnEntity("DamageableDummy", MapCoordinates.Nullspace);
|
var human = entityManager.SpawnEntity("DamageableDummy", MapCoordinates.Nullspace);
|
||||||
|
|
||||||
@@ -55,7 +53,7 @@ namespace Content.IntegrationTests.Tests.Commands
|
|||||||
Assert.That(mobState.IsIncapacitated, Is.False);
|
Assert.That(mobState.IsIncapacitated, Is.False);
|
||||||
|
|
||||||
// Kill the entity
|
// Kill the entity
|
||||||
damageable.TryChangeDamage(prototypeManager.Index<DamageGroupPrototype>("Toxin"), 10000000, true);
|
damageable.ChangeDamage(DamageClass.Brute, 10000000, true);
|
||||||
|
|
||||||
// Check that it is dead
|
// Check that it is dead
|
||||||
Assert.That(mobState.IsAlive, Is.False);
|
Assert.That(mobState.IsAlive, Is.False);
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.Damage.Components;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Damageable
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
[TestOf(typeof(DamageableComponent))]
|
|
||||||
public class AllSupportDamageableTest : ContentIntegrationTest
|
|
||||||
{
|
|
||||||
private const string AllDamageDamageableEntityId = "TestAllDamageDamageableEntityId";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Test a damageContainer with all types supported.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// As this should also loads in the damage groups & types in the actual damage.yml, this should also act as a basic test to see if damage.yml is set up properly.
|
|
||||||
/// </remarks>
|
|
||||||
[Test]
|
|
||||||
public async Task TestAllSupportDamageableComponent()
|
|
||||||
{
|
|
||||||
var server = StartServerDummyTicker();
|
|
||||||
await server.WaitIdleAsync();
|
|
||||||
|
|
||||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
|
||||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
|
||||||
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
|
|
||||||
|
|
||||||
IEntity sFullyDamageableEntity;
|
|
||||||
IDamageableComponent sFullyDamageableComponent = null;
|
|
||||||
|
|
||||||
await server.WaitPost(() =>
|
|
||||||
{
|
|
||||||
var mapId = sMapManager.NextMapId();
|
|
||||||
var coordinates = new MapCoordinates(0, 0, mapId);
|
|
||||||
sMapManager.CreateMap(mapId);
|
|
||||||
|
|
||||||
// When prototypes are loaded using the ExtraPrototypes option, they seem to be loaded first?
|
|
||||||
// Or at least, no damage prototypes were loaded in by the time that the damageContainer here is loaded.
|
|
||||||
// So for now doing explicit loading of prototypes.
|
|
||||||
// I have no idea what I am doing, but it works.
|
|
||||||
sPrototypeManager.LoadString($@"
|
|
||||||
# we want to test the all damage container
|
|
||||||
- type: damageContainer
|
|
||||||
id: testAllDamageContainer
|
|
||||||
supportAll: true
|
|
||||||
|
|
||||||
# create entities
|
|
||||||
- type: entity
|
|
||||||
id: {AllDamageDamageableEntityId}
|
|
||||||
name: {AllDamageDamageableEntityId}
|
|
||||||
components:
|
|
||||||
- type: Damageable
|
|
||||||
damageContainer: testAllDamageContainer
|
|
||||||
");
|
|
||||||
|
|
||||||
sFullyDamageableEntity = sEntityManager.SpawnEntity(AllDamageDamageableEntityId, coordinates);
|
|
||||||
sFullyDamageableComponent = sFullyDamageableEntity.GetComponent<IDamageableComponent>();
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
await server.WaitRunTicks(5);
|
|
||||||
|
|
||||||
await server.WaitAssertion(() =>
|
|
||||||
{
|
|
||||||
|
|
||||||
// First check that there actually are any damage types/groups
|
|
||||||
// This test depends on a non-empty damage.yml
|
|
||||||
Assert.That(sPrototypeManager.EnumeratePrototypes<DamageTypePrototype>().ToList().Count, Is.GreaterThan(0));
|
|
||||||
Assert.That(sPrototypeManager.EnumeratePrototypes<DamageGroupPrototype>().ToList().Count, Is.GreaterThan(0));
|
|
||||||
|
|
||||||
|
|
||||||
// Can we set and get all damage.
|
|
||||||
Assert.That(sFullyDamageableComponent.TrySetAllDamage(-10), Is.False);
|
|
||||||
Assert.That(sFullyDamageableComponent.TrySetAllDamage(0), Is.True);
|
|
||||||
|
|
||||||
// Test that the all damage container supports every damage type, and that we can get, set, and change
|
|
||||||
// every type with the expected results. Notable: if the damage does not change, they all return false
|
|
||||||
var initialDamage = 10;
|
|
||||||
foreach (var damageType in sPrototypeManager.EnumeratePrototypes<DamageTypePrototype>())
|
|
||||||
{
|
|
||||||
var damage = initialDamage;
|
|
||||||
Assert.That(sFullyDamageableComponent.IsSupportedDamageType(damageType));
|
|
||||||
Assert.That(sFullyDamageableComponent.TrySetDamage(damageType, -damage), Is.False);
|
|
||||||
Assert.That(sFullyDamageableComponent.TrySetDamage(damageType, damage), Is.True);
|
|
||||||
Assert.That(sFullyDamageableComponent.TrySetDamage(damageType, damage), Is.True); // intentional duplicate
|
|
||||||
Assert.That(sFullyDamageableComponent.GetDamage(damageType), Is.EqualTo(damage));
|
|
||||||
Assert.That(sFullyDamageableComponent.TryChangeDamage(damageType, -damage / 2, true), Is.True);
|
|
||||||
Assert.That(sFullyDamageableComponent.TryGetDamage(damageType, out damage), Is.True);
|
|
||||||
Assert.That(damage, Is.EqualTo(initialDamage/2));
|
|
||||||
Assert.That(sFullyDamageableComponent.TryChangeDamage(damageType, damage, true), Is.True);
|
|
||||||
Assert.That(sFullyDamageableComponent.GetDamage(damageType), Is.EqualTo(2* damage));
|
|
||||||
Assert.That(sFullyDamageableComponent.TryChangeDamage(damageType, 0, true), Is.False);
|
|
||||||
}
|
|
||||||
// And again, for every group
|
|
||||||
foreach (var damageGroup in sPrototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
|
||||||
{
|
|
||||||
var damage = initialDamage;
|
|
||||||
var groupSize = damageGroup.DamageTypes.Count();
|
|
||||||
Assert.That(sFullyDamageableComponent.IsFullySupportedDamageGroup(damageGroup));
|
|
||||||
Assert.That(sFullyDamageableComponent.IsApplicableDamageGroup(damageGroup));
|
|
||||||
Assert.That(sFullyDamageableComponent.TrySetDamage(damageGroup, -damage), Is.False);
|
|
||||||
Assert.That(sFullyDamageableComponent.TrySetDamage(damageGroup, damage), Is.True);
|
|
||||||
Assert.That(sFullyDamageableComponent.TrySetDamage(damageGroup, damage), Is.True); // intentional duplicate
|
|
||||||
Assert.That(sFullyDamageableComponent.GetDamage(damageGroup), Is.EqualTo(damage * groupSize));
|
|
||||||
Assert.That(sFullyDamageableComponent.TryChangeDamage(damageGroup, -groupSize*damage / 2, true), Is.True);
|
|
||||||
Assert.That(sFullyDamageableComponent.TryGetDamage(damageGroup, out damage), Is.True);
|
|
||||||
Assert.That(damage, Is.EqualTo(groupSize* initialDamage/2));
|
|
||||||
Assert.That(sFullyDamageableComponent.TryChangeDamage(damageGroup, damage, true), Is.True);
|
|
||||||
Assert.That(sFullyDamageableComponent.GetDamage(damageGroup), Is.EqualTo(2*damage));
|
|
||||||
Assert.That(sFullyDamageableComponent.TryChangeDamage(damageGroup, 0, true), Is.False);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
using System.Linq;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Damageable
|
namespace Content.IntegrationTests.Tests.Damageable
|
||||||
{
|
{
|
||||||
@@ -13,75 +12,18 @@ namespace Content.IntegrationTests.Tests.Damageable
|
|||||||
[TestOf(typeof(DamageableComponent))]
|
[TestOf(typeof(DamageableComponent))]
|
||||||
public class DamageableTest : ContentIntegrationTest
|
public class DamageableTest : ContentIntegrationTest
|
||||||
{
|
{
|
||||||
private const string DamageableEntityId = "TestDamageableEntityId";
|
private const string DamageableEntityId = "DamageableEntityId";
|
||||||
private const string Group1Id = "TestGroup1";
|
|
||||||
private const string Group2Id = "TestGroup2";
|
|
||||||
private const string Group3Id = "TestGroup3";
|
|
||||||
private string Prototypes = $@"
|
|
||||||
# Define some damage groups
|
|
||||||
- type: damageType
|
|
||||||
id: TestDamage11
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestDamage21
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestDamage22
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestDamage31
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestDamage32
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestDamage33
|
|
||||||
|
|
||||||
# Define damage Groups with 1,2,3 damage types
|
|
||||||
- type: damageGroup
|
|
||||||
id: {Group1Id}
|
|
||||||
damageTypes:
|
|
||||||
- TestDamage11
|
|
||||||
|
|
||||||
- type: damageGroup
|
|
||||||
id: {Group2Id}
|
|
||||||
damageTypes:
|
|
||||||
- TestDamage21
|
|
||||||
- TestDamage22
|
|
||||||
|
|
||||||
- type: damageGroup
|
|
||||||
id: {Group3Id}
|
|
||||||
damageTypes:
|
|
||||||
- TestDamage31
|
|
||||||
- TestDamage32
|
|
||||||
- TestDamage33
|
|
||||||
|
|
||||||
# we want to test a container that supports only full groups
|
|
||||||
# we will also give full support for group 2 IMPLICITLY by specifying all of its members are supported.
|
|
||||||
- type: damageContainer
|
|
||||||
id: testSomeDamageContainer
|
|
||||||
supportedGroups:
|
|
||||||
- {Group3Id}
|
|
||||||
supportedTypes:
|
|
||||||
- TestDamage21
|
|
||||||
- TestDamage22
|
|
||||||
|
|
||||||
|
private static readonly string Prototypes = $@"
|
||||||
- type: entity
|
- type: entity
|
||||||
id: {DamageableEntityId}
|
id: {DamageableEntityId}
|
||||||
name: {DamageableEntityId}
|
name: {DamageableEntityId}
|
||||||
components:
|
components:
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: testSomeDamageContainer
|
damageContainer: allDamageContainer";
|
||||||
";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Test a standard damageable components
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Only test scenarios where each damage type is a member of exactly one group, and all damageable components support whole groups, not lone damage types.
|
|
||||||
/// </remarks>
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task TestDamageableComponents()
|
public async Task TestDamageTypeDamageAndHeal()
|
||||||
{
|
{
|
||||||
var server = StartServerDummyTicker(new ServerContentIntegrationOption
|
var server = StartServerDummyTicker(new ServerContentIntegrationOption
|
||||||
{
|
{
|
||||||
@@ -92,15 +34,10 @@ namespace Content.IntegrationTests.Tests.Damageable
|
|||||||
|
|
||||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||||
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
|
|
||||||
|
|
||||||
IEntity sDamageableEntity;
|
IEntity sDamageableEntity;
|
||||||
IDamageableComponent sDamageableComponent = null;
|
IDamageableComponent sDamageableComponent = null;
|
||||||
|
|
||||||
DamageGroupPrototype group1 = default!;
|
|
||||||
DamageGroupPrototype group2 = default!;
|
|
||||||
DamageGroupPrototype group3 = default!;
|
|
||||||
|
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
var mapId = sMapManager.NextMapId();
|
var mapId = sMapManager.NextMapId();
|
||||||
@@ -109,280 +46,51 @@ namespace Content.IntegrationTests.Tests.Damageable
|
|||||||
|
|
||||||
sDamageableEntity = sEntityManager.SpawnEntity(DamageableEntityId, coordinates);
|
sDamageableEntity = sEntityManager.SpawnEntity(DamageableEntityId, coordinates);
|
||||||
sDamageableComponent = sDamageableEntity.GetComponent<IDamageableComponent>();
|
sDamageableComponent = sDamageableEntity.GetComponent<IDamageableComponent>();
|
||||||
|
|
||||||
group1 = sPrototypeManager.Index<DamageGroupPrototype>(Group1Id);
|
|
||||||
group2 = sPrototypeManager.Index<DamageGroupPrototype>(Group2Id);
|
|
||||||
group3 = sPrototypeManager.Index<DamageGroupPrototype>(Group3Id);
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.WaitRunTicks(5);
|
await server.WaitRunTicks(5);
|
||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
// Check that the correct groups are supported by the container
|
|
||||||
Assert.That(sDamageableComponent.IsApplicableDamageGroup(group1), Is.False);
|
|
||||||
Assert.That(sDamageableComponent.IsApplicableDamageGroup(group2), Is.True);
|
|
||||||
Assert.That(sDamageableComponent.IsApplicableDamageGroup(group3), Is.True);
|
|
||||||
Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group1), Is.False);
|
|
||||||
Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group2), Is.True);
|
|
||||||
Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group3), Is.True);
|
|
||||||
|
|
||||||
// Check that the correct types are supported:
|
|
||||||
foreach (var group in sPrototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
|
||||||
{
|
|
||||||
foreach(var type in group.DamageTypes)
|
|
||||||
{
|
|
||||||
if (sDamageableComponent.IsFullySupportedDamageGroup(group))
|
|
||||||
{
|
|
||||||
Assert.That(sDamageableComponent.IsSupportedDamageType(type), Is.True);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Assert.That(sDamageableComponent.IsSupportedDamageType(type), Is.False);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group1), Is.False);
|
|
||||||
Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group2), Is.True);
|
|
||||||
Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group3), Is.True);
|
|
||||||
|
|
||||||
// Check that damage works properly if perfectly divisible among group members
|
|
||||||
int damageToDeal, groupDamage, typeDamage; ;
|
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
||||||
foreach (var damageGroup in sDamageableComponent.FullySupportedDamageGroups)
|
|
||||||
|
var damageToDeal = 7;
|
||||||
|
|
||||||
|
foreach (var type in Enum.GetValues<DamageType>())
|
||||||
{
|
{
|
||||||
var types = damageGroup.DamageTypes;
|
Assert.That(sDamageableComponent.SupportsDamageType(type));
|
||||||
|
|
||||||
// Damage
|
// Damage
|
||||||
damageToDeal = types.Count() * 5;
|
Assert.That(sDamageableComponent.ChangeDamage(type, damageToDeal, true), Is.True);
|
||||||
Assert.That(sDamageableComponent.TryChangeDamage(damageGroup, damageToDeal, true), Is.True);
|
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal));
|
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal));
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(damageGroup, out groupDamage), Is.True);
|
Assert.That(sDamageableComponent.TryGetDamage(type, out var damage), Is.True);
|
||||||
Assert.That(groupDamage, Is.EqualTo(damageToDeal));
|
Assert.That(damage, Is.EqualTo(damageToDeal));
|
||||||
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(type, out typeDamage), Is.True);
|
|
||||||
Assert.That(typeDamage, Is.EqualTo(damageToDeal / types.Count()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Heal
|
// Heal
|
||||||
Assert.That(sDamageableComponent.TryChangeDamage(damageGroup, -damageToDeal, true), Is.True);
|
Assert.That(sDamageableComponent.ChangeDamage(type, -damageToDeal, true), Is.True);
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.Zero);
|
Assert.That(sDamageableComponent.TotalDamage, Is.Zero);
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(damageGroup, out groupDamage), Is.True);
|
Assert.That(sDamageableComponent.TryGetDamage(type, out damage), Is.True);
|
||||||
Assert.That(groupDamage, Is.Zero);
|
Assert.That(damage, Is.Zero);
|
||||||
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(type, out typeDamage), Is.True);
|
|
||||||
Assert.That(typeDamage, Is.Zero);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Check that damage works properly if it is NOT perfectly divisible among group members
|
|
||||||
foreach (var damageGroup in sDamageableComponent.FullySupportedDamageGroups)
|
|
||||||
{
|
|
||||||
var types = damageGroup.DamageTypes;
|
|
||||||
|
|
||||||
// Damage
|
|
||||||
damageToDeal = types.Count() * 5 - 1;
|
|
||||||
Assert.That(sDamageableComponent.TryChangeDamage(damageGroup, damageToDeal, true), Is.True);
|
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal));
|
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(damageGroup, out groupDamage), Is.True);
|
|
||||||
Assert.That(groupDamage, Is.EqualTo(damageToDeal));
|
|
||||||
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(type, out typeDamage), Is.True);
|
|
||||||
float targetDamage = ((float) damageToDeal) / types.Count();
|
|
||||||
Assert.That(typeDamage, Is.InRange(targetDamage - 1, targetDamage + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Heal
|
|
||||||
Assert.That(sDamageableComponent.TryChangeDamage(damageGroup, -damageToDeal, true), Is.True);
|
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.Zero);
|
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(damageGroup, out groupDamage), Is.True);
|
|
||||||
Assert.That(groupDamage, Is.Zero);
|
|
||||||
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(type, out typeDamage), Is.True);
|
|
||||||
Assert.That(typeDamage, Is.Zero);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test that unsupported groups return false when setting/getting damage (and don't change damage)
|
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
|
||||||
foreach (var damageGroup in sPrototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
|
||||||
{
|
|
||||||
if (sDamageableComponent.IsFullySupportedDamageGroup(damageGroup))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.That(sDamageableComponent.IsApplicableDamageGroup(damageGroup), Is.False);
|
|
||||||
|
|
||||||
var types = damageGroup.DamageTypes;
|
|
||||||
damageToDeal = types.Count() * 5;
|
|
||||||
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
Assert.That(sDamageableComponent.IsSupportedDamageType(type), Is.False);
|
|
||||||
}
|
|
||||||
;
|
|
||||||
Assert.That(sDamageableComponent.TryChangeDamage(damageGroup, damageToDeal, true), Is.False);
|
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(damageGroup, out groupDamage), Is.False);
|
|
||||||
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
Assert.That(sDamageableComponent.TryChangeDamage(type, damageToDeal, true), Is.False);
|
|
||||||
Assert.That(sDamageableComponent.TryGetDamage(type, out typeDamage), Is.False);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Did damage change?
|
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
|
||||||
|
|
||||||
|
|
||||||
// Test total damage function
|
|
||||||
damageToDeal = 10;
|
|
||||||
|
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(group3, damageToDeal, true));
|
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal));
|
|
||||||
|
|
||||||
var totalTypeDamage = 0;
|
|
||||||
|
|
||||||
foreach (var damageType in sDamageableComponent.SupportedDamageTypes)
|
|
||||||
{
|
|
||||||
Assert.True(sDamageableComponent.TryGetDamage(damageType, out typeDamage));
|
|
||||||
Assert.That(typeDamage, Is.LessThanOrEqualTo(damageToDeal));
|
|
||||||
|
|
||||||
totalTypeDamage += typeDamage;
|
|
||||||
}
|
|
||||||
Assert.That(totalTypeDamage, Is.EqualTo(damageToDeal));
|
|
||||||
|
|
||||||
|
|
||||||
// Test healing all damage
|
|
||||||
Assert.That(sDamageableComponent.TrySetAllDamage(0));
|
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
|
||||||
|
|
||||||
// Test preferential healing
|
|
||||||
damageToDeal = 12;
|
|
||||||
var damageTypes = group3.DamageTypes.ToArray();
|
|
||||||
|
|
||||||
// Deal damage
|
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(damageTypes[0], 17));
|
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(damageTypes[1], 31));
|
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(48));
|
|
||||||
|
|
||||||
// Heal group damage
|
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(group3, -11));
|
|
||||||
|
|
||||||
// Check healing (3 + 9)
|
|
||||||
Assert.That(sDamageableComponent.GetDamage(damageTypes[0]), Is.EqualTo(14));
|
|
||||||
Assert.That(sDamageableComponent.GetDamage(damageTypes[1]), Is.EqualTo(23));
|
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(37));
|
|
||||||
|
|
||||||
// Heal group damage
|
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(group3, -36));
|
|
||||||
|
|
||||||
// Check healing (13 + 23)
|
|
||||||
Assert.That(sDamageableComponent.GetDamage(damageTypes[0]), Is.EqualTo(1));
|
|
||||||
Assert.That(sDamageableComponent.GetDamage(damageTypes[1]), Is.EqualTo(0));
|
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(1));
|
|
||||||
|
|
||||||
//Check Damage
|
|
||||||
Assert.True(sDamageableComponent.TryGetDamage(damageTypes[0], out typeDamage));
|
|
||||||
Assert.That(typeDamage, Is.LessThanOrEqualTo(damageToDeal));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private const string SharedDamageTypeId = "TestSharedDamage";
|
|
||||||
private const string UnsupportedDamageTypeId = "TestUnsupportedDamage";
|
|
||||||
private string Prototypes2 = $@"
|
|
||||||
- type: damageType
|
|
||||||
id: {SharedDamageTypeId}
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: {UnsupportedDamageTypeId}
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestDamage1
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestDamage2
|
|
||||||
|
|
||||||
- type: damageGroup
|
|
||||||
id: {Group1Id}
|
|
||||||
damageTypes:
|
|
||||||
- {SharedDamageTypeId}
|
|
||||||
|
|
||||||
- type: damageGroup
|
|
||||||
id: {Group2Id}
|
|
||||||
damageTypes:
|
|
||||||
- {SharedDamageTypeId}
|
|
||||||
- TestDamage1
|
|
||||||
|
|
||||||
- type: damageGroup
|
|
||||||
id: {Group3Id}
|
|
||||||
damageTypes:
|
|
||||||
- {SharedDamageTypeId}
|
|
||||||
- TestDamage2
|
|
||||||
- {UnsupportedDamageTypeId}
|
|
||||||
|
|
||||||
# we want to test a container that only partially supports a group:
|
|
||||||
- type: damageContainer
|
|
||||||
id: TestPartiallySupported
|
|
||||||
supportedGroups:
|
|
||||||
- {Group2Id}
|
|
||||||
supportedTypes:
|
|
||||||
- TestDamage2
|
|
||||||
- TestDamage1
|
|
||||||
# does NOT support type {UnsupportedDamageTypeId}, and thus does not fully support group {Group3Id}
|
|
||||||
# TestDamage1 is added twice because it is also in {Group2Id}. This should not cause errors.
|
|
||||||
|
|
||||||
# create entities
|
|
||||||
- type: entity
|
|
||||||
id: {DamageableEntityId}
|
|
||||||
name: {DamageableEntityId}
|
|
||||||
components:
|
|
||||||
- type: Damageable
|
|
||||||
damageContainer: TestPartiallySupported
|
|
||||||
";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generalized damageable component tests.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Test scenarios where damage types are members of more than one group, or where a component only supports a subset of a group.
|
|
||||||
/// </remarks>
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task TestGeneralizedDamageableComponent()
|
public async Task TestDamageClassDamageAndHeal()
|
||||||
{
|
{
|
||||||
var server = StartServerDummyTicker(new ServerContentIntegrationOption
|
var server = StartServerDummyTicker(new ServerContentIntegrationOption
|
||||||
{
|
{
|
||||||
ExtraPrototypes = Prototypes2
|
ExtraPrototypes = Prototypes
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.WaitIdleAsync();
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||||
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
|
|
||||||
|
|
||||||
IEntity sDamageableEntity;
|
IEntity sDamageableEntity;
|
||||||
IDamageableComponent sDamageableComponent = null;
|
IDamageableComponent sDamageableComponent = null;
|
||||||
|
|
||||||
DamageGroupPrototype group1 = default!;
|
|
||||||
DamageGroupPrototype group2 = default!;
|
|
||||||
DamageGroupPrototype group3 = default!;
|
|
||||||
|
|
||||||
DamageTypePrototype SharedDamageType = default!;
|
|
||||||
DamageTypePrototype UnsupportedDamageType = default!;
|
|
||||||
|
|
||||||
await server.WaitPost(() =>
|
await server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
var mapId = sMapManager.NextMapId();
|
var mapId = sMapManager.NextMapId();
|
||||||
@@ -391,62 +99,99 @@ namespace Content.IntegrationTests.Tests.Damageable
|
|||||||
|
|
||||||
sDamageableEntity = sEntityManager.SpawnEntity(DamageableEntityId, coordinates);
|
sDamageableEntity = sEntityManager.SpawnEntity(DamageableEntityId, coordinates);
|
||||||
sDamageableComponent = sDamageableEntity.GetComponent<IDamageableComponent>();
|
sDamageableComponent = sDamageableEntity.GetComponent<IDamageableComponent>();
|
||||||
|
|
||||||
group1 = sPrototypeManager.Index<DamageGroupPrototype>(Group1Id);
|
|
||||||
group2 = sPrototypeManager.Index<DamageGroupPrototype>(Group2Id);
|
|
||||||
group3 = sPrototypeManager.Index<DamageGroupPrototype>(Group3Id);
|
|
||||||
|
|
||||||
SharedDamageType = sPrototypeManager.Index<DamageTypePrototype>(SharedDamageTypeId);
|
|
||||||
UnsupportedDamageType = sPrototypeManager.Index<DamageTypePrototype>(UnsupportedDamageTypeId);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await server.WaitRunTicks(5);
|
await server.WaitRunTicks(5);
|
||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
// All damage types should be applicable
|
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
||||||
Assert.That(sDamageableComponent.IsApplicableDamageGroup(group1), Is.True);
|
|
||||||
Assert.That(sDamageableComponent.IsApplicableDamageGroup(group2), Is.True);
|
|
||||||
Assert.That(sDamageableComponent.IsApplicableDamageGroup(group3), Is.True);
|
|
||||||
|
|
||||||
// But not all should be fully supported
|
foreach (var @class in Enum.GetValues<DamageClass>())
|
||||||
Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group1), Is.True);
|
{
|
||||||
Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group2), Is.True);
|
Assert.That(sDamageableComponent.SupportsDamageClass(@class));
|
||||||
Assert.That(sDamageableComponent.IsFullySupportedDamageGroup(group3), Is.False);
|
|
||||||
|
|
||||||
// Check that the correct damage types are supported
|
var types = @class.ToTypes();
|
||||||
Assert.That(sDamageableComponent.IsSupportedDamageType(SharedDamageType), Is.True);
|
|
||||||
|
|
||||||
// Check that if we deal damage using a type appearing in multiple groups, nothing goes wrong.
|
foreach (var type in types)
|
||||||
var damage = 12;
|
{
|
||||||
Assert.That(sDamageableComponent.TryChangeDamage(SharedDamageType, damage), Is.True);
|
Assert.That(sDamageableComponent.SupportsDamageType(type));
|
||||||
Assert.That(sDamageableComponent.GetDamage(SharedDamageType), Is.EqualTo(damage));
|
}
|
||||||
Assert.That(sDamageableComponent.GetDamage(group1), Is.EqualTo(damage));
|
|
||||||
Assert.That(sDamageableComponent.GetDamage(group2), Is.EqualTo(damage));
|
|
||||||
Assert.That(sDamageableComponent.GetDamage(group3), Is.EqualTo(damage));
|
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damage));
|
|
||||||
|
|
||||||
// Check that if we deal damage using a group that is not fully supported, the damage is reduced
|
var damageToDeal = types.Count * 5;
|
||||||
// Note that if damage2 were not neatly divisible by 3, the actual damage reduction would be subject to integer rounding.
|
|
||||||
// How much exactly the damage gets reduced then would depend on the order that the groups were defined in the yaml file
|
|
||||||
// Here we deal 9 damage. It should apply 3 damage to each type, but one type is ignored, resulting in 6 total damage.
|
|
||||||
// However, the damage in group2 and group3 only changes because of one type that overlaps, so they only change by 3
|
|
||||||
Assert.That(sDamageableComponent.TryChangeDamage(group3, 9), Is.True);
|
|
||||||
Assert.That(sDamageableComponent.GetDamage(group1), Is.EqualTo(damage + 3));
|
|
||||||
Assert.That(sDamageableComponent.GetDamage(group2), Is.EqualTo(damage + 3));
|
|
||||||
Assert.That(sDamageableComponent.GetDamage(group3), Is.EqualTo(damage + 6));
|
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damage + 6));
|
|
||||||
|
|
||||||
// Now we check that when healing, no damage is wasted.
|
// Damage
|
||||||
// Because SharedDamageType has the most damage in group3 (15 vs 3), it will be healed more than the other.
|
Assert.That(sDamageableComponent.ChangeDamage(@class, damageToDeal, true), Is.True);
|
||||||
// Expect that, up to integer rounding, one is healed 5* more than the other.
|
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damageToDeal));
|
||||||
// We will use a number that does not divide nicely, there will be some integer rounding.
|
Assert.That(sDamageableComponent.TryGetDamage(@class, out var classDamage), Is.True);
|
||||||
Assert.That(sDamageableComponent.TryChangeDamage(group3, -7), Is.True);
|
Assert.That(classDamage, Is.EqualTo(damageToDeal));
|
||||||
Assert.That(sDamageableComponent.GetDamage(group1), Is.EqualTo(damage + 3 - 5));
|
|
||||||
Assert.That(sDamageableComponent.GetDamage(group2), Is.EqualTo(damage + 3 - 5));
|
|
||||||
Assert.That(sDamageableComponent.GetDamage(group3), Is.EqualTo(damage + 6 - 7));
|
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(damage + 6 - 7));
|
|
||||||
|
|
||||||
|
foreach (var type in types)
|
||||||
|
{
|
||||||
|
Assert.That(sDamageableComponent.TryGetDamage(type, out var typeDamage), Is.True);
|
||||||
|
Assert.That(typeDamage, Is.EqualTo(damageToDeal / types.Count));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Heal
|
||||||
|
Assert.That(sDamageableComponent.ChangeDamage(@class, -damageToDeal, true), Is.True);
|
||||||
|
Assert.That(sDamageableComponent.TotalDamage, Is.Zero);
|
||||||
|
Assert.That(sDamageableComponent.TryGetDamage(@class, out classDamage), Is.True);
|
||||||
|
Assert.That(classDamage, Is.Zero);
|
||||||
|
|
||||||
|
foreach (var type in types)
|
||||||
|
{
|
||||||
|
Assert.That(sDamageableComponent.TryGetDamage(type, out var typeDamage), Is.True);
|
||||||
|
Assert.That(typeDamage, Is.Zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task TotalDamageTest()
|
||||||
|
{
|
||||||
|
var server = StartServerDummyTicker(new ServerContentIntegrationOption
|
||||||
|
{
|
||||||
|
ExtraPrototypes = Prototypes
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
|
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||||
|
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||||
|
|
||||||
|
IEntity sDamageableEntity;
|
||||||
|
IDamageableComponent sDamageableComponent = null;
|
||||||
|
|
||||||
|
await server.WaitPost(() =>
|
||||||
|
{
|
||||||
|
var mapId = sMapManager.NextMapId();
|
||||||
|
var coordinates = new MapCoordinates(0, 0, mapId);
|
||||||
|
sMapManager.CreateMap(mapId);
|
||||||
|
|
||||||
|
sDamageableEntity = sEntityManager.SpawnEntity(DamageableEntityId, coordinates);
|
||||||
|
sDamageableComponent = sDamageableEntity.GetComponent<IDamageableComponent>();
|
||||||
|
});
|
||||||
|
|
||||||
|
await server.WaitAssertion(() =>
|
||||||
|
{
|
||||||
|
var damageType = DamageClass.Brute;
|
||||||
|
var damage = 10;
|
||||||
|
|
||||||
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, damage, true));
|
||||||
|
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(10));
|
||||||
|
|
||||||
|
var totalTypeDamage = 0;
|
||||||
|
|
||||||
|
foreach (var type in damageType.ToTypes())
|
||||||
|
{
|
||||||
|
Assert.True(sDamageableComponent.TryGetDamage(type, out var typeDamage));
|
||||||
|
Assert.That(typeDamage, Is.LessThanOrEqualTo(damage));
|
||||||
|
|
||||||
|
totalTypeDamage += typeDamage;
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.That(totalTypeDamage, Is.EqualTo(damage));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Destructible.Thresholds.Triggers;
|
using Content.Server.Destructible.Thresholds.Triggers;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
@@ -6,15 +6,14 @@ using NUnit.Framework;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Destructible
|
namespace Content.IntegrationTests.Tests.Destructible
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
[TestOf(typeof(DamageGroupTrigger))]
|
[TestOf(typeof(DamageClassTrigger))]
|
||||||
[TestOf(typeof(AndTrigger))]
|
[TestOf(typeof(AndTrigger))]
|
||||||
public class DestructibleDamageGroupTest : ContentIntegrationTest
|
public class DestructibleDamageClassTest : ContentIntegrationTest
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public async Task AndTest()
|
public async Task AndTest()
|
||||||
@@ -32,7 +31,6 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||||
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
|
|
||||||
|
|
||||||
IEntity sDestructibleEntity;
|
IEntity sDestructibleEntity;
|
||||||
IDamageableComponent sDamageableComponent = null;
|
IDamageableComponent sDamageableComponent = null;
|
||||||
@@ -44,7 +42,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
var coordinates = new MapCoordinates(0, 0, mapId);
|
var coordinates = new MapCoordinates(0, 0, mapId);
|
||||||
sMapManager.CreateMap(mapId);
|
sMapManager.CreateMap(mapId);
|
||||||
|
|
||||||
sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDamageGroupEntityId, coordinates);
|
sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDamageClassEntityId, coordinates);
|
||||||
sDamageableComponent = sDestructibleEntity.GetComponent<IDamageableComponent>();
|
sDamageableComponent = sDestructibleEntity.GetComponent<IDamageableComponent>();
|
||||||
sThresholdListenerComponent = sDestructibleEntity.GetComponent<TestThresholdListenerComponent>();
|
sThresholdListenerComponent = sDestructibleEntity.GetComponent<TestThresholdListenerComponent>();
|
||||||
});
|
});
|
||||||
@@ -58,23 +56,20 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
var bruteDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>("TestBrute");
|
|
||||||
var burnDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>("TestBurn");
|
|
||||||
|
|
||||||
// Raise brute damage to 5
|
// Raise brute damage to 5
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 5, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 5, true));
|
||||||
|
|
||||||
// No thresholds reached yet, the earliest one is at 10 damage
|
// No thresholds reached yet, the earliest one is at 10 damage
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise brute damage to 10
|
// Raise brute damage to 10
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 5, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 5, true));
|
||||||
|
|
||||||
// No threshold reached, burn needs to be 10 as well
|
// No threshold reached, burn needs to be 10 as well
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise burn damage to 10
|
// Raise burn damage to 10
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, 10, true));
|
||||||
|
|
||||||
// One threshold reached, brute 10 + burn 10
|
// One threshold reached, brute 10 + burn 10
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -91,52 +86,52 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
var trigger = (AndTrigger) threshold.Trigger;
|
var trigger = (AndTrigger) threshold.Trigger;
|
||||||
|
|
||||||
Assert.IsInstanceOf<DamageGroupTrigger>(trigger.Triggers[0]);
|
Assert.IsInstanceOf<DamageClassTrigger>(trigger.Triggers[0]);
|
||||||
Assert.IsInstanceOf<DamageGroupTrigger>(trigger.Triggers[1]);
|
Assert.IsInstanceOf<DamageClassTrigger>(trigger.Triggers[1]);
|
||||||
|
|
||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Raise brute damage to 20
|
// Raise brute damage to 20
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise burn damage to 20
|
// Raise burn damage to 20
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Lower brute damage to 0
|
// Lower brute damage to 0
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, -20, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, -20, true));
|
||||||
|
|
||||||
// No new thresholds reached, healing should not trigger it
|
// No new thresholds reached, healing should not trigger it
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise brute damage back up to 10
|
// Raise brute damage back up to 10
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 10, true));
|
||||||
|
|
||||||
// 10 brute + 10 burn threshold reached, brute was healed and brought back to its threshold amount and burn stayed the same
|
// 10 brute + 10 burn threshold reached, brute was healed and brought back to its threshold amount and slash stayed the same
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
|
|
||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Heal both classes of damage to 0
|
// Heal both classes of damage to 0
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, -10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, -10, true));
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, -20, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, -20, true));
|
||||||
|
|
||||||
// No new thresholds reached, healing should not trigger it
|
// No new thresholds reached, healing should not trigger it
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise brute damage to 10
|
// Raise brute damage to 10
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise burn damage to 10
|
// Raise burn damage to 10
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, 10, true));
|
||||||
|
|
||||||
// Both classes of damage were healed and then raised again, the threshold should have been reached as triggers once is default false
|
// Both classes of damage were healed and then raised again, the threshold should have been reached as triggers once is default false
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -153,8 +148,8 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
trigger = (AndTrigger) threshold.Trigger;
|
trigger = (AndTrigger) threshold.Trigger;
|
||||||
|
|
||||||
Assert.IsInstanceOf<DamageGroupTrigger>(trigger.Triggers[0]);
|
Assert.IsInstanceOf<DamageClassTrigger>(trigger.Triggers[0]);
|
||||||
Assert.IsInstanceOf<DamageGroupTrigger>(trigger.Triggers[1]);
|
Assert.IsInstanceOf<DamageClassTrigger>(trigger.Triggers[1]);
|
||||||
|
|
||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
@@ -162,20 +157,20 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
threshold.TriggersOnce = true;
|
threshold.TriggersOnce = true;
|
||||||
|
|
||||||
// Heal brute and burn back to 0
|
// Heal brute and burn back to 0
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, -10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, -10, true));
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, -10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, -10, true));
|
||||||
|
|
||||||
// No new thresholds reached from healing
|
// No new thresholds reached from healing
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise brute damage to 10
|
// Raise brute damage to 10
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise burn damage to 10
|
// Raise burn damage to 10
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(burnDamageGroup, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached as triggers once is set to true and it already triggered before
|
// No new thresholds reached as triggers once is set to true and it already triggered before
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Destructible.Thresholds.Triggers;
|
using Content.Server.Destructible.Thresholds.Triggers;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
@@ -6,7 +6,6 @@ using NUnit.Framework;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Destructible
|
namespace Content.IntegrationTests.Tests.Destructible
|
||||||
@@ -57,23 +56,20 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
var bluntDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("TestBlunt");
|
|
||||||
var slashDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("TestSlash");
|
|
||||||
|
|
||||||
// Raise blunt damage to 5
|
// Raise blunt damage to 5
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 5, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 5, true));
|
||||||
|
|
||||||
// No thresholds reached yet, the earliest one is at 10 damage
|
// No thresholds reached yet, the earliest one is at 10 damage
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise blunt damage to 10
|
// Raise blunt damage to 10
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 5, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 5, true));
|
||||||
|
|
||||||
// No threshold reached, slash needs to be 10 as well
|
// No threshold reached, slash needs to be 10 as well
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise slash damage to 10
|
// Raise slash damage to 10
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true));
|
||||||
|
|
||||||
// One threshold reached, blunt 10 + slash 10
|
// One threshold reached, blunt 10 + slash 10
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -96,25 +92,25 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Raise blunt damage to 20
|
// Raise blunt damage to 20
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise slash damage to 20
|
// Raise slash damage to 20
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Lower blunt damage to 0
|
// Lower blunt damage to 0
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, -20, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, -20, true));
|
||||||
|
|
||||||
// No new thresholds reached, healing should not trigger it
|
// No new thresholds reached, healing should not trigger it
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise blunt damage back up to 10
|
// Raise blunt damage back up to 10
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true));
|
||||||
|
|
||||||
// 10 blunt + 10 slash threshold reached, blunt was healed and brought back to its threshold amount and slash stayed the same
|
// 10 blunt + 10 slash threshold reached, blunt was healed and brought back to its threshold amount and slash stayed the same
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -122,20 +118,20 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Heal both types of damage to 0
|
// Heal both types of damage to 0
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, -10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, -10, true));
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, -20, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, -20, true));
|
||||||
|
|
||||||
// No new thresholds reached, healing should not trigger it
|
// No new thresholds reached, healing should not trigger it
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise blunt damage to 10
|
// Raise blunt damage to 10
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise slash damage to 10
|
// Raise slash damage to 10
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true));
|
||||||
|
|
||||||
// Both types of damage were healed and then raised again, the threshold should have been reached as triggers once is default false
|
// Both types of damage were healed and then raised again, the threshold should have been reached as triggers once is default false
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -161,20 +157,20 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
threshold.TriggersOnce = true;
|
threshold.TriggersOnce = true;
|
||||||
|
|
||||||
// Heal blunt and slash back to 0
|
// Heal blunt and slash back to 0
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, -10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, -10, true));
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, -10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, -10, true));
|
||||||
|
|
||||||
// No new thresholds reached from healing
|
// No new thresholds reached from healing
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise blunt damage to 10
|
// Raise blunt damage to 10
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached
|
// No new thresholds reached
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Raise slash damage to 10
|
// Raise slash damage to 10
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(slashDamageType, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true));
|
||||||
|
|
||||||
// No new thresholds reached as triggers once is set to true and it already triggered before
|
// No new thresholds reached as triggers once is set to true and it already triggered before
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Destructible.Thresholds;
|
using Content.Server.Destructible.Thresholds;
|
||||||
using Content.Server.Destructible.Thresholds.Behaviors;
|
using Content.Server.Destructible.Thresholds.Behaviors;
|
||||||
@@ -8,7 +8,6 @@ using NUnit.Framework;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Destructible
|
namespace Content.IntegrationTests.Tests.Destructible
|
||||||
@@ -31,7 +30,6 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||||
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
|
|
||||||
|
|
||||||
IEntity sDestructibleEntity = null;
|
IEntity sDestructibleEntity = null;
|
||||||
IDamageableComponent sDamageableComponent = null;
|
IDamageableComponent sDamageableComponent = null;
|
||||||
@@ -51,11 +49,10 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
var coordinates = sDestructibleEntity.Transform.Coordinates;
|
var coordinates = sDestructibleEntity.Transform.Coordinates;
|
||||||
var bruteDamageGroup = sPrototypeManager.Index<DamageGroupPrototype>("TestBrute");
|
|
||||||
|
|
||||||
Assert.DoesNotThrow(() =>
|
Assert.DoesNotThrow(() =>
|
||||||
{
|
{
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bruteDamageGroup, 50, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 50, true));
|
||||||
});
|
});
|
||||||
|
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
|
|||||||
@@ -6,93 +6,9 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
public const string DestructibleEntityId = "DestructibleTestsDestructibleEntity";
|
public const string DestructibleEntityId = "DestructibleTestsDestructibleEntity";
|
||||||
public const string DestructibleDestructionEntityId = "DestructibleTestsDestructibleDestructionEntity";
|
public const string DestructibleDestructionEntityId = "DestructibleTestsDestructibleDestructionEntity";
|
||||||
public const string DestructibleDamageTypeEntityId = "DestructibleTestsDestructibleDamageTypeEntity";
|
public const string DestructibleDamageTypeEntityId = "DestructibleTestsDestructibleDamageTypeEntity";
|
||||||
public const string DestructibleDamageGroupEntityId = "DestructibleTestsDestructibleDamageGroupEntity";
|
public const string DestructibleDamageClassEntityId = "DestructibleTestsDestructibleDamageClassEntity";
|
||||||
|
|
||||||
public static readonly string Prototypes = $@"
|
public static readonly string Prototypes = $@"
|
||||||
- type: damageType
|
|
||||||
id: TestBlunt
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestSlash
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestPiercing
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestHeat
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestShock
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestCold
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestPoison
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestRadiation
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestAsphyxiation
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestBloodloss
|
|
||||||
|
|
||||||
- type: damageType
|
|
||||||
id: TestCellular
|
|
||||||
|
|
||||||
- type: damageGroup
|
|
||||||
id: TestBrute
|
|
||||||
damageTypes:
|
|
||||||
- TestBlunt
|
|
||||||
- TestSlash
|
|
||||||
- TestPiercing
|
|
||||||
|
|
||||||
- type: damageGroup
|
|
||||||
id: TestBurn
|
|
||||||
damageTypes:
|
|
||||||
- TestHeat
|
|
||||||
- TestShock
|
|
||||||
- TestCold
|
|
||||||
|
|
||||||
- type: damageGroup
|
|
||||||
id: TestAirloss
|
|
||||||
damageTypes:
|
|
||||||
- TestAsphyxiation
|
|
||||||
- TestBloodloss
|
|
||||||
|
|
||||||
- type: damageGroup
|
|
||||||
id: TestToxin
|
|
||||||
damageTypes:
|
|
||||||
- TestPoison
|
|
||||||
- TestRadiation
|
|
||||||
|
|
||||||
- type: damageGroup
|
|
||||||
id: TestGenetic
|
|
||||||
damageTypes:
|
|
||||||
- TestCellular
|
|
||||||
|
|
||||||
- type: damageContainer
|
|
||||||
id: TestAllDamageContainer
|
|
||||||
supportAll: true
|
|
||||||
|
|
||||||
|
|
||||||
- type: damageContainer
|
|
||||||
id: TestBiologicalDamageContainer
|
|
||||||
supportedGroups:
|
|
||||||
- TestBrute
|
|
||||||
- TestBurn
|
|
||||||
- TestToxin
|
|
||||||
- TestAirloss
|
|
||||||
- TestGenetic
|
|
||||||
|
|
||||||
- type: damageContainer
|
|
||||||
id: TestMetallicDamageContainer
|
|
||||||
supportedGroups:
|
|
||||||
- TestBrute
|
|
||||||
- TestBurn
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: {SpawnedEntityId}
|
id: {SpawnedEntityId}
|
||||||
name: {SpawnedEntityId}
|
name: {SpawnedEntityId}
|
||||||
@@ -102,7 +18,6 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
name: {DestructibleEntityId}
|
name: {DestructibleEntityId}
|
||||||
components:
|
components:
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: TestMetallicDamageContainer
|
|
||||||
- type: Destructible
|
- type: Destructible
|
||||||
thresholds:
|
thresholds:
|
||||||
- trigger:
|
- trigger:
|
||||||
@@ -131,7 +46,6 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
name: {DestructibleDestructionEntityId}
|
name: {DestructibleDestructionEntityId}
|
||||||
components:
|
components:
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: TestMetallicDamageContainer
|
|
||||||
- type: Destructible
|
- type: Destructible
|
||||||
thresholds:
|
thresholds:
|
||||||
- trigger:
|
- trigger:
|
||||||
@@ -155,36 +69,34 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
name: {DestructibleDamageTypeEntityId}
|
name: {DestructibleDamageTypeEntityId}
|
||||||
components:
|
components:
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: TestMetallicDamageContainer
|
|
||||||
- type: Destructible
|
- type: Destructible
|
||||||
thresholds:
|
thresholds:
|
||||||
- trigger:
|
- trigger:
|
||||||
!type:AndTrigger
|
!type:AndTrigger
|
||||||
triggers:
|
triggers:
|
||||||
- !type:DamageTypeTrigger
|
- !type:DamageTypeTrigger
|
||||||
damageType: TestBlunt
|
type: Blunt
|
||||||
damage: 10
|
damage: 10
|
||||||
- !type:DamageTypeTrigger
|
- !type:DamageTypeTrigger
|
||||||
damageType: TestSlash
|
type: Slash
|
||||||
damage: 10
|
damage: 10
|
||||||
- type: TestThresholdListener
|
- type: TestThresholdListener
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: {DestructibleDamageGroupEntityId}
|
id: {DestructibleDamageClassEntityId}
|
||||||
name: {DestructibleDamageGroupEntityId}
|
name: {DestructibleDamageClassEntityId}
|
||||||
components:
|
components:
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: TestMetallicDamageContainer
|
|
||||||
- type: Destructible
|
- type: Destructible
|
||||||
thresholds:
|
thresholds:
|
||||||
- trigger:
|
- trigger:
|
||||||
!type:AndTrigger
|
!type:AndTrigger
|
||||||
triggers:
|
triggers:
|
||||||
- !type:DamageGroupTrigger
|
- !type:DamageClassTrigger
|
||||||
damageGroup: TestBrute
|
class: Brute
|
||||||
damage: 10
|
damage: 10
|
||||||
- !type:DamageGroupTrigger
|
- !type:DamageClassTrigger
|
||||||
damageGroup: TestBurn
|
class: Burn
|
||||||
damage: 10
|
damage: 10
|
||||||
- type: TestThresholdListener";
|
- type: TestThresholdListener";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using NUnit.Framework;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Destructible
|
namespace Content.IntegrationTests.Tests.Destructible
|
||||||
@@ -36,7 +35,6 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||||
var sPrototypeManager = server.ResolveDependency<IPrototypeManager>();
|
|
||||||
|
|
||||||
IEntity sDestructibleEntity;
|
IEntity sDestructibleEntity;
|
||||||
IDamageableComponent sDamageableComponent = null;
|
IDamageableComponent sDamageableComponent = null;
|
||||||
@@ -64,14 +62,12 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
var bluntDamageType = sPrototypeManager.Index<DamageTypePrototype>("TestBlunt");
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true));
|
||||||
|
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true));
|
|
||||||
|
|
||||||
// No thresholds reached yet, the earliest one is at 20 damage
|
// No thresholds reached yet, the earliest one is at 20 damage
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true));
|
||||||
|
|
||||||
// Only one threshold reached, 20
|
// Only one threshold reached, 20
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -87,7 +83,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
|
|
||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 30, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 30, true));
|
||||||
|
|
||||||
// One threshold reached, 50, since 20 already triggered before and it has not been healed below that amount
|
// One threshold reached, 50, since 20 already triggered before and it has not been healed below that amount
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -116,16 +112,16 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Damage for 50 again, up to 100 now
|
// Damage for 50 again, up to 100 now
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 50, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 50, true));
|
||||||
|
|
||||||
// No thresholds reached as they weren't healed below the trigger amount
|
// No thresholds reached as they weren't healed below the trigger amount
|
||||||
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached);
|
||||||
|
|
||||||
// Set damage to 0
|
// Heal down to 0
|
||||||
sDamageableComponent.TrySetAllDamage(0);
|
sDamageableComponent.Heal();
|
||||||
|
|
||||||
// Damage for 100, up to 100
|
// Damage for 100, up to 100
|
||||||
Assert.True(sDamageableComponent.TryChangeDamage(bluntDamageType, 100, true));
|
Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 100, true));
|
||||||
|
|
||||||
// Two thresholds reached as damage increased past the previous, 20 and 50
|
// Two thresholds reached as damage increased past the previous, 20 and 50
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(2));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(2));
|
||||||
@@ -133,25 +129,25 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Heal the entity for 40 damage, down to 60
|
// Heal the entity for 40 damage, down to 60
|
||||||
sDamageableComponent.TryChangeDamage(bluntDamageType, -40, true);
|
sDamageableComponent.ChangeDamage(DamageType.Blunt, -40, true);
|
||||||
|
|
||||||
// Thresholds don't work backwards
|
// Thresholds don't work backwards
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty);
|
Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty);
|
||||||
|
|
||||||
// Damage for 10, up to 70
|
// Damage for 10, up to 70
|
||||||
sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true);
|
sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true);
|
||||||
|
|
||||||
// Not enough healing to de-trigger a threshold
|
// Not enough healing to de-trigger a threshold
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty);
|
Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty);
|
||||||
|
|
||||||
// Heal by 30, down to 40
|
// Heal by 30, down to 40
|
||||||
sDamageableComponent.TryChangeDamage(bluntDamageType, -30, true);
|
sDamageableComponent.ChangeDamage(DamageType.Blunt, -30, true);
|
||||||
|
|
||||||
// Thresholds don't work backwards
|
// Thresholds don't work backwards
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty);
|
Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty);
|
||||||
|
|
||||||
// Damage up to 50 again
|
// Damage up to 50 again
|
||||||
sDamageableComponent.TryChangeDamage(bluntDamageType, 10, true);
|
sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true);
|
||||||
|
|
||||||
// The 50 threshold should have triggered again, after being healed
|
// The 50 threshold should have triggered again, after being healed
|
||||||
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1));
|
||||||
@@ -181,10 +177,10 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Heal all damage
|
// Heal all damage
|
||||||
sDamageableComponent.TrySetAllDamage(0);
|
sDamageableComponent.Heal();
|
||||||
|
|
||||||
// Damage up to 50
|
// Damage up to 50
|
||||||
sDamageableComponent.TryChangeDamage(bluntDamageType, 50, true);
|
sDamageableComponent.ChangeDamage(DamageType.Blunt, 50, true);
|
||||||
|
|
||||||
// Check that the total damage matches
|
// Check that the total damage matches
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50));
|
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50));
|
||||||
@@ -232,7 +228,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
sThresholdListenerComponent.ThresholdsReached.Clear();
|
sThresholdListenerComponent.ThresholdsReached.Clear();
|
||||||
|
|
||||||
// Heal the entity completely
|
// Heal the entity completely
|
||||||
sDamageableComponent.TrySetAllDamage(0);
|
sDamageableComponent.Heal();
|
||||||
|
|
||||||
// Check that the entity has 0 damage
|
// Check that the entity has 0 damage
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0));
|
||||||
@@ -245,7 +241,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Damage the entity up to 50 damage again
|
// Damage the entity up to 50 damage again
|
||||||
sDamageableComponent.TryChangeDamage(bluntDamageType, 50, true);
|
sDamageableComponent.ChangeDamage(DamageType.Blunt, 50, true);
|
||||||
|
|
||||||
// Check that the total damage matches
|
// Check that the total damage matches
|
||||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50));
|
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(50));
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Content.Server.Alert;
|
using Content.Server.Alert;
|
||||||
using Content.Server.Pressure;
|
using Content.Server.Pressure;
|
||||||
@@ -7,24 +7,6 @@ using Content.Shared.Atmos;
|
|||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
=======
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
using Dependency = Robust.Shared.IoC.DependencyAttribute;
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
namespace Content.Server.Atmos.Components
|
namespace Content.Server.Atmos.Components
|
||||||
{
|
{
|
||||||
@@ -36,32 +18,6 @@ namespace Content.Server.Atmos.Components
|
|||||||
{
|
{
|
||||||
public override string Name => "Barotrauma";
|
public override string Name => "Barotrauma";
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
=======
|
|
||||||
[DataField("damageType", required: true)]
|
|
||||||
private readonly string _damageType = default!;
|
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
|
||||||
// Also remove Initialize override, if no longer needed.
|
|
||||||
[DataField("damageType")] private readonly string _damageTypeID = "Blunt";
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public DamageTypePrototype DamageType = default!;
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
|
||||||
}
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Update(float airPressure)
|
public void Update(float airPressure)
|
||||||
{
|
{
|
||||||
@@ -84,20 +40,11 @@ namespace Content.Server.Atmos.Components
|
|||||||
// Low pressure.
|
// Low pressure.
|
||||||
case var p when p <= Atmospherics.WarningLowPressure:
|
case var p when p <= Atmospherics.WarningLowPressure:
|
||||||
pressure *= lowPressureMultiplier;
|
pressure *= lowPressureMultiplier;
|
||||||
|
|
||||||
if(pressure > Atmospherics.WarningLowPressure)
|
if(pressure > Atmospherics.WarningLowPressure)
|
||||||
goto default;
|
goto default;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
damageable.ChangeDamage(DamageType.Blunt, Atmospherics.LowPressureDamage, false, Owner);
|
damageable.ChangeDamage(DamageType.Blunt, Atmospherics.LowPressureDamage, false, Owner);
|
||||||
=======
|
|
||||||
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
|
|
||||||
damageable.TryChangeDamage(DamageType, Atmospherics.LowPressureDamage,true);
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
|
|
||||||
damageable.TryChangeDamage(DamageType, Atmospherics.LowPressureDamage,true);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
if (status == null) break;
|
if (status == null) break;
|
||||||
|
|
||||||
@@ -119,21 +66,7 @@ namespace Content.Server.Atmos.Components
|
|||||||
|
|
||||||
var damage = (int) MathF.Min((pressure / Atmospherics.HazardHighPressure) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage);
|
var damage = (int) MathF.Min((pressure / Atmospherics.HazardHighPressure) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage);
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
damageable.ChangeDamage(DamageType.Blunt, damage, false, Owner);
|
damageable.ChangeDamage(DamageType.Blunt, damage, false, Owner);
|
||||||
=======
|
|
||||||
damageable.ChangeDamage(damageable.GetDamageType(_damageType), damage, false, Owner);
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
|
|
||||||
damageable.TryChangeDamage(DamageType, damage,true);
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
|
|
||||||
damageable.TryChangeDamage(DamageType, damage,true);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
if (status == null) break;
|
if (status == null) break;
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,6 @@ using Robust.Shared.Localization;
|
|||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
|
|
||||||
namespace Content.Server.Atmos.Components
|
namespace Content.Server.Atmos.Components
|
||||||
{
|
{
|
||||||
@@ -45,18 +43,6 @@ namespace Content.Server.Atmos.Components
|
|||||||
[DataField("canResistFire")]
|
[DataField("canResistFire")]
|
||||||
public bool CanResistFire { get; private set; } = false;
|
public bool CanResistFire { get; private set; } = false;
|
||||||
|
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
|
||||||
// Also remove Initialize override, if no longer needed.
|
|
||||||
[DataField("damageType")]
|
|
||||||
private readonly string _damageTypeID = "Heat"!;
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public DamageTypePrototype DamageType = default!;
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Extinguish()
|
public void Extinguish()
|
||||||
{
|
{
|
||||||
if (!OnFire) return;
|
if (!OnFire) return;
|
||||||
@@ -106,15 +92,7 @@ namespace Content.Server.Atmos.Components
|
|||||||
{
|
{
|
||||||
// TODO ATMOS Fire resistance from armor
|
// TODO ATMOS Fire resistance from armor
|
||||||
var damage = Math.Min((int) (FireStacks * 2.5f), 10);
|
var damage = Math.Min((int) (FireStacks * 2.5f), 10);
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
damageable.ChangeDamage(DamageClass.Burn, damage, false);
|
damageable.ChangeDamage(DamageClass.Burn, damage, false);
|
||||||
=======
|
|
||||||
damageable.TryChangeDamage(DamageType, damage, false);
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
damageable.TryChangeDamage(DamageType, damage, false);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AdjustFireStacks(-0.1f * (_resisting ? 10f : 1f));
|
AdjustFireStacks(-0.1f * (_resisting ? 10f : 1f));
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Shared.Body.Part;
|
using Content.Shared.Body.Part;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
|
|
||||||
namespace Content.Server.Body
|
namespace Content.Server.Body
|
||||||
@@ -10,7 +10,7 @@ namespace Content.Server.Body
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO BODY: Remove and pretend it never existed
|
// TODO BODY: Remove and pretend it never existed
|
||||||
public class BodyDamageChangeParams : IBodyHealthChangeParams
|
public class BodyDamageChangeParams : DamageChangeParams, IBodyHealthChangeParams
|
||||||
{
|
{
|
||||||
public BodyDamageChangeParams(BodyPartType part)
|
public BodyDamageChangeParams(BodyPartType part)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ using Content.Shared.Atmos;
|
|||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Content.Shared.MobState;
|
using Content.Shared.MobState;
|
||||||
using Content.Shared.Notification.Managers;
|
using Content.Shared.Notification.Managers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
@@ -36,22 +34,10 @@ namespace Content.Server.Body.Respiratory
|
|||||||
private bool _isShivering;
|
private bool _isShivering;
|
||||||
private bool _isSweating;
|
private bool _isSweating;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master:Content.Server/Body/Respiratory/RespiratorComponent.cs
|
|
||||||
=======
|
|
||||||
[DataField("damageType", required: true)]
|
|
||||||
private readonly string _damageType = default!;
|
|
||||||
|
|
||||||
>>>>>>> update damagecomponent across shared and server:Content.Server/Metabolism/MetabolismComponent.cs
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamage")] private int _suffocationDamage = 1;
|
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamage")] private int _suffocationDamage = 1;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamageRecovery")] private int _suffocationDamageRecovery = 1;
|
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamageRecovery")] private int _suffocationDamageRecovery = 1;
|
||||||
|
|
||||||
=======
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
[ViewVariables] [DataField("needsGases")] public Dictionary<Gas, float> NeedsGases { get; set; } = new();
|
[ViewVariables] [DataField("needsGases")] public Dictionary<Gas, float> NeedsGases { get; set; } = new();
|
||||||
|
|
||||||
[ViewVariables] [DataField("producesGases")] public Dictionary<Gas, float> ProducesGases { get; set; } = new();
|
[ViewVariables] [DataField("producesGases")] public Dictionary<Gas, float> ProducesGases { get; set; } = new();
|
||||||
@@ -108,22 +94,6 @@ namespace Content.Server.Body.Respiratory
|
|||||||
|
|
||||||
[ViewVariables] public bool Suffocating { get; private set; }
|
[ViewVariables] public bool Suffocating { get; private set; }
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamage")] private int _damage = 1;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamageRecovery")] private int _damageRecovery = 1;
|
|
||||||
|
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
|
||||||
// Also remove Initialize override, if no longer needed.
|
|
||||||
[DataField("damageType")]
|
|
||||||
private readonly string _damageTypeID = "Asphyxiation"!;
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public DamageTypePrototype DamageType = default!;
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Dictionary<Gas, float> NeedsAndDeficit(float frameTime)
|
private Dictionary<Gas, float> NeedsAndDeficit(float frameTime)
|
||||||
{
|
{
|
||||||
var needs = new Dictionary<Gas, float>(NeedsGases);
|
var needs = new Dictionary<Gas, float>(NeedsGases);
|
||||||
@@ -379,19 +349,7 @@ namespace Content.Server.Body.Respiratory
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master:Content.Server/Body/Respiratory/RespiratorComponent.cs
|
|
||||||
damageable.ChangeDamage(DamageType.Asphyxiation, _suffocationDamage, false);
|
damageable.ChangeDamage(DamageType.Asphyxiation, _suffocationDamage, false);
|
||||||
=======
|
|
||||||
damageable.ChangeDamage(damageable.GetDamageType(_damageType), _suffocationDamage, false);
|
|
||||||
>>>>>>> update damagecomponent across shared and server:Content.Server/Metabolism/MetabolismComponent.cs
|
|
||||||
=======
|
|
||||||
damageable.TryChangeDamage(DamageType, _damage, false);
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
damageable.TryChangeDamage(DamageType, _damage, false);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void StopSuffocation()
|
private void StopSuffocation()
|
||||||
@@ -400,19 +358,7 @@ namespace Content.Server.Body.Respiratory
|
|||||||
|
|
||||||
if (Owner.TryGetComponent(out IDamageableComponent? damageable))
|
if (Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master:Content.Server/Body/Respiratory/RespiratorComponent.cs
|
|
||||||
damageable.ChangeDamage(DamageType.Asphyxiation, -_suffocationDamageRecovery, false);
|
damageable.ChangeDamage(DamageType.Asphyxiation, -_suffocationDamageRecovery, false);
|
||||||
=======
|
|
||||||
damageable.ChangeDamage(damageable.GetDamageType(_damageType), -_suffocationDamageRecovery, false);
|
|
||||||
>>>>>>> update damagecomponent across shared and server:Content.Server/Metabolism/MetabolismComponent.cs
|
|
||||||
=======
|
|
||||||
damageable.TryChangeDamage(DamageType, -_damageRecovery, false);
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
damageable.TryChangeDamage(DamageType, -_damageRecovery, false);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent))
|
if (Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent))
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ using Robust.Shared.Enums;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Server.Chat.Commands
|
namespace Content.Server.Chat.Commands
|
||||||
{
|
{
|
||||||
@@ -35,11 +34,8 @@ namespace Content.Server.Chat.Commands
|
|||||||
var kind = suicide.Suicide(target, chat);
|
var kind = suicide.Suicide(target, chat);
|
||||||
if (kind != SuicideKind.Special)
|
if (kind != SuicideKind.Special)
|
||||||
{
|
{
|
||||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
damageableComponent.SetDamage(kind switch
|
||||||
damageableComponent.TrySetDamage(kind switch
|
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
SuicideKind.Blunt => DamageType.Blunt,
|
SuicideKind.Blunt => DamageType.Blunt,
|
||||||
SuicideKind.Slash => DamageType.Slash,
|
SuicideKind.Slash => DamageType.Slash,
|
||||||
SuicideKind.Piercing => DamageType.Piercing,
|
SuicideKind.Piercing => DamageType.Piercing,
|
||||||
@@ -51,26 +47,8 @@ namespace Content.Server.Chat.Commands
|
|||||||
SuicideKind.Asphyxiation => DamageType.Asphyxiation,
|
SuicideKind.Asphyxiation => DamageType.Asphyxiation,
|
||||||
SuicideKind.Bloodloss => DamageType.Bloodloss,
|
SuicideKind.Bloodloss => DamageType.Bloodloss,
|
||||||
_ => DamageType.Blunt
|
_ => DamageType.Blunt
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
SuicideKind.Blunt => prototypeManager.Index<DamageTypePrototype>("Blunt"),
|
|
||||||
SuicideKind.Slash => prototypeManager.Index<DamageTypePrototype>("Slash"),
|
|
||||||
SuicideKind.Piercing => prototypeManager.Index<DamageTypePrototype>("Piercing"),
|
|
||||||
SuicideKind.Heat => prototypeManager.Index<DamageTypePrototype>("Heat"),
|
|
||||||
SuicideKind.Shock => prototypeManager.Index<DamageTypePrototype>("Shock"),
|
|
||||||
SuicideKind.Cold => prototypeManager.Index<DamageTypePrototype>("Cold"),
|
|
||||||
SuicideKind.Poison => prototypeManager.Index<DamageTypePrototype>("Poison"),
|
|
||||||
SuicideKind.Radiation => prototypeManager.Index<DamageTypePrototype>("Radiation"),
|
|
||||||
SuicideKind.Asphyxiation => prototypeManager.Index<DamageTypePrototype>("Asphyxiation"),
|
|
||||||
SuicideKind.Bloodloss => prototypeManager.Index<DamageTypePrototype>("Bloodloss"),
|
|
||||||
_ => prototypeManager.Index<DamageTypePrototype>("Blunt")
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
},
|
},
|
||||||
200);
|
200, source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,15 +117,7 @@ namespace Content.Server.Chat.Commands
|
|||||||
var selfMessage = Loc.GetString("suicide-command-default-text-self");
|
var selfMessage = Loc.GetString("suicide-command-default-text-self");
|
||||||
owner.PopupMessage(selfMessage);
|
owner.PopupMessage(selfMessage);
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
dmgComponent.SetDamage(DamageType.Piercing, 200, owner);
|
dmgComponent.SetDamage(DamageType.Piercing, 200, owner);
|
||||||
=======
|
|
||||||
dmgComponent.TrySetDamage(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Piercing"), 200);
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
dmgComponent.TrySetDamage(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Piercing"), 200);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
// Prevent the player from returning to the body.
|
// Prevent the player from returning to the body.
|
||||||
// Note that mind cannot be null because otherwise owner would be null.
|
// Note that mind cannot be null because otherwise owner would be null.
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ using Robust.Shared.GameObjects;
|
|||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Server.Chemistry.ReagentEffects
|
namespace Content.Server.Chemistry.ReagentEffects
|
||||||
{
|
{
|
||||||
@@ -15,7 +12,7 @@ namespace Content.Server.Chemistry.ReagentEffects
|
|||||||
/// Default metabolism for medicine reagents. Attempts to find a DamageableComponent on the target,
|
/// Default metabolism for medicine reagents. Attempts to find a DamageableComponent on the target,
|
||||||
/// and to update its damage values.
|
/// and to update its damage values.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class HealthChange : ReagentEffect, ISerializationHooks
|
public class HealthChange : ReagentEffect
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How much damage is changed when 1u of the reagent is metabolized.
|
/// How much damage is changed when 1u of the reagent is metabolized.
|
||||||
@@ -23,67 +20,35 @@ namespace Content.Server.Chemistry.ReagentEffects
|
|||||||
[DataField("healthChange")]
|
[DataField("healthChange")]
|
||||||
public float AmountToChange { get; set; } = 1.0f;
|
public float AmountToChange { get; set; } = 1.0f;
|
||||||
|
|
||||||
// TODO DAMAGE UNITS When damage units support decimals, get rid of this.
|
|
||||||
// See also _accumulatedDamage in ThirstComponent and HungerComponent
|
|
||||||
private float _accumulatedDamage;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Damage group to change.
|
/// Class of damage changed, Brute, Burn, Toxin, Airloss.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
[DataField("damageClass")]
|
||||||
// Also remove ISerializationHooks, if no longer needed.
|
public DamageClass DamageType { get; set; } = DamageClass.Brute;
|
||||||
[DataField("damageGroup", required: true)]
|
|
||||||
private readonly string _damageGroupID = default!;
|
private float _accumulatedHealth;
|
||||||
public DamageGroupPrototype DamageGroup = default!;
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
|
||||||
{
|
|
||||||
DamageGroup = IoCManager.Resolve<IPrototypeManager>().Index<DamageGroupPrototype>(_damageGroupID);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes damage if a DamageableComponent can be found.
|
/// Changes damage if a DamageableComponent can be found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override void Metabolize(IEntity solutionEntity, Solution.ReagentQuantity amount)
|
public override void Metabolize(IEntity solutionEntity, Solution.ReagentQuantity amount)
|
||||||
{
|
{
|
||||||
if (solutionEntity.TryGetComponent(out IDamageableComponent? damageComponent))
|
if (solutionEntity.TryGetComponent(out IDamageableComponent? health))
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master:Content.Server/Chemistry/ReagentEffects/HealthChange.cs
|
|
||||||
health.ChangeDamage(DamageType, (int)AmountToChange, true);
|
health.ChangeDamage(DamageType, (int)AmountToChange, true);
|
||||||
=======
|
|
||||||
damageComponent.ChangeDamage(damageComponent.GetDamageType(damageType), (int)AmountToChange, true);
|
|
||||||
>>>>>>> Fix Merge issues
|
|
||||||
float decHealthChange = (float) (AmountToChange - (int) AmountToChange);
|
float decHealthChange = (float) (AmountToChange - (int) AmountToChange);
|
||||||
=======
|
|
||||||
damageComponent.ChangeDamage(damageComponent.GetDamageType(damageType), (int)HealthChange, true);
|
|
||||||
float decHealthChange = (float) (HealthChange - (int) HealthChange);
|
|
||||||
>>>>>>> update damagecomponent across shared and server:Content.Server/Chemistry/Metabolism/HealthChangeMetabolism.cs
|
|
||||||
_accumulatedHealth += decHealthChange;
|
_accumulatedHealth += decHealthChange;
|
||||||
=======
|
|
||||||
damageComponent.TryChangeDamage(DamageGroup, (int)AmountToChange, true);
|
|
||||||
|
|
||||||
float decHealthChange = (float) (AmountToChange - (int) AmountToChange);
|
if (_accumulatedHealth >= 1)
|
||||||
_accumulatedDamage += decHealthChange;
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
damageComponent.TryChangeDamage(DamageGroup, (int)AmountToChange, true);
|
|
||||||
|
|
||||||
float decHealthChange = (float) (AmountToChange - (int) AmountToChange);
|
|
||||||
_accumulatedDamage += decHealthChange;
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
if (_accumulatedDamage >= 1)
|
|
||||||
{
|
{
|
||||||
damageComponent.TryChangeDamage(DamageGroup, 1, true);
|
health.ChangeDamage(DamageType, 1, true);
|
||||||
_accumulatedDamage -= 1;
|
_accumulatedHealth -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if(_accumulatedDamage <= -1)
|
else if(_accumulatedHealth <= -1)
|
||||||
{
|
{
|
||||||
damageComponent.TryChangeDamage(DamageGroup, -1, true);
|
health.ChangeDamage(DamageType, -1, true);
|
||||||
_accumulatedDamage += 1;
|
_accumulatedHealth += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using Content.Server.Administration;
|
|
||||||
using Content.Server.Commands.Observer;
|
|
||||||
using Content.Server.GameObjects.Components.GUI;
|
|
||||||
using Content.Server.GameObjects.Components.Items.Storage;
|
|
||||||
using Content.Server.Interfaces.Chat;
|
|
||||||
using Content.Server.Interfaces.GameObjects;
|
|
||||||
using Content.Server.Players;
|
|
||||||
using Content.Server.Utility;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.Interfaces;
|
|
||||||
using Robust.Server.Player;
|
|
||||||
using Robust.Shared.Console;
|
|
||||||
using Robust.Shared.Enums;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
|
|
||||||
namespace Content.Server.Commands.Chat
|
|
||||||
{
|
|
||||||
[AnyCommand]
|
|
||||||
internal class SuicideCommand : IConsoleCommand
|
|
||||||
{
|
|
||||||
public string Command => "suicide";
|
|
||||||
|
|
||||||
public string Description => "Commits suicide";
|
|
||||||
|
|
||||||
public string Help => "The suicide command gives you a quick way out of a round while remaining in-character.\n" +
|
|
||||||
"The method varies, first it will attempt to use the held item in your active hand.\n" +
|
|
||||||
"If that fails, it will attempt to use an object in the environment.\n" +
|
|
||||||
"Finally, if neither of the above worked, you will die by biting your tongue.";
|
|
||||||
|
|
||||||
private void DealDamage(ISuicideAct suicide, IChatManager chat, IDamageableComponent damageableComponent, IEntity source, IEntity target)
|
|
||||||
{
|
|
||||||
var kind = suicide.Suicide(target, chat);
|
|
||||||
if (kind != SuicideKind.Special)
|
|
||||||
{
|
|
||||||
damageableComponent.SetDamage(kind switch
|
|
||||||
{
|
|
||||||
SuicideKind.Blunt => damageableComponent.GetDamageType("Blunt"),
|
|
||||||
SuicideKind.Slash => damageableComponent.GetDamageType("Slash"),
|
|
||||||
SuicideKind.Piercing => damageableComponent.GetDamageType("Piercing"),
|
|
||||||
SuicideKind.Heat => damageableComponent.GetDamageType("Heat"),
|
|
||||||
SuicideKind.Shock => damageableComponent.GetDamageType("Shock"),
|
|
||||||
SuicideKind.Cold => damageableComponent.GetDamageType("Cold"),
|
|
||||||
SuicideKind.Poison => damageableComponent.GetDamageType("Poison"),
|
|
||||||
SuicideKind.Radiation => damageableComponent.GetDamageType("Radiation"),
|
|
||||||
SuicideKind.Asphyxiation => damageableComponent.GetDamageType("Asphyxiation"),
|
|
||||||
SuicideKind.Bloodloss => damageableComponent.GetDamageType("Bloodloss"),
|
|
||||||
_ => damageableComponent.GetDamageType("Blunt")
|
|
||||||
},
|
|
||||||
200, source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
|
||||||
{
|
|
||||||
var player = shell.Player as IPlayerSession;
|
|
||||||
if (player == null)
|
|
||||||
{
|
|
||||||
shell.WriteLine("You cannot run this command from the server.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player.Status != SessionStatus.InGame)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var chat = IoCManager.Resolve<IChatManager>();
|
|
||||||
var owner = player.ContentData()?.Mind?.OwnedComponent?.Owner;
|
|
||||||
|
|
||||||
if (owner == null)
|
|
||||||
{
|
|
||||||
shell.WriteLine("You don't have a mind!");
|
|
||||||
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?
|
|
||||||
|
|
||||||
// Held item suicide
|
|
||||||
var handsComponent = owner.GetComponent<HandsComponent>();
|
|
||||||
var itemComponent = handsComponent.GetActiveHand;
|
|
||||||
if (itemComponent != null)
|
|
||||||
{
|
|
||||||
var suicide = itemComponent.Owner.GetAllComponents<ISuicideAct>().FirstOrDefault();
|
|
||||||
|
|
||||||
if (suicide != null)
|
|
||||||
{
|
|
||||||
DealDamage(suicide, chat, dmgComponent, itemComponent.Owner, owner);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Get all entities in range of the suicider
|
|
||||||
var entities = IoCManager.Resolve<IEntityLookup>().GetEntitiesInRange(owner, 1, true).ToArray();
|
|
||||||
|
|
||||||
if (entities.Length > 0)
|
|
||||||
{
|
|
||||||
foreach (var entity in entities)
|
|
||||||
{
|
|
||||||
if (entity.HasComponent<ItemComponent>())
|
|
||||||
continue;
|
|
||||||
var suicide = entity.GetAllComponents<ISuicideAct>().FirstOrDefault();
|
|
||||||
if (suicide != null)
|
|
||||||
{
|
|
||||||
DealDamage(suicide, chat, dmgComponent, entity, owner);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default suicide, bite your tongue
|
|
||||||
var othersMessage = Loc.GetString("{0:theName} is attempting to bite {0:their} own tongue!", owner);
|
|
||||||
owner.PopupMessageOtherClients(othersMessage);
|
|
||||||
|
|
||||||
var selfMessage = Loc.GetString("You attempt to bite your own tongue!");
|
|
||||||
owner.PopupMessage(selfMessage);
|
|
||||||
|
|
||||||
dmgComponent.SetDamage(dmgComponent.GetDamageType("Piercing"), 200, owner);
|
|
||||||
|
|
||||||
// Prevent the player from returning to the body. Yes, this is an ugly hack.
|
|
||||||
var ghost = new Ghost(){CanReturn = false};
|
|
||||||
ghost.Execute(shell, argStr, Array.Empty<string>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,228 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using Content.Server.Administration;
|
|
||||||
using Content.Shared.Administration;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Robust.Server.Player;
|
|
||||||
using Robust.Shared.Console;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Server.Commands
|
|
||||||
{
|
|
||||||
[AdminCommand(AdminFlags.Fun)]
|
|
||||||
class HurtCommand : IConsoleCommand
|
|
||||||
{
|
|
||||||
[Dependency]
|
|
||||||
private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
public string Command => "hurt";
|
|
||||||
public string Description => "Ouch";
|
|
||||||
public string Help => $"Usage: {Command} <type/?> <amount> (<entity uid/_>) (<ignoreResistances>)";
|
|
||||||
|
|
||||||
private string DamageTypes()
|
|
||||||
{
|
|
||||||
var msg = new StringBuilder();
|
|
||||||
|
|
||||||
foreach (var damageGroup in _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
|
||||||
{
|
|
||||||
msg.Append($"\n{damageGroup.ID}");
|
|
||||||
if (damageGroup.DamageTypes.Any())
|
|
||||||
{
|
|
||||||
msg.Append(": ");
|
|
||||||
msg.AppendJoin('|', damageGroup.DamageTypes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $"Damage Types:{msg}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private delegate void Damage(IDamageableComponent damageable, bool ignoreResistances);
|
|
||||||
|
|
||||||
private bool TryParseEntity(IConsoleShell shell, IPlayerSession? player, string arg,
|
|
||||||
[NotNullWhen(true)] out IEntity? entity)
|
|
||||||
{
|
|
||||||
entity = null;
|
|
||||||
|
|
||||||
if (arg == "_")
|
|
||||||
{
|
|
||||||
var playerEntity = player?.AttachedEntity;
|
|
||||||
|
|
||||||
if (playerEntity == null)
|
|
||||||
{
|
|
||||||
shell.WriteLine($"You must have a player entity to use this command without specifying an entity.\n{Help}");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity = playerEntity;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!EntityUid.TryParse(arg, out var entityUid))
|
|
||||||
{
|
|
||||||
shell.WriteLine($"{arg} is not a valid entity uid.\n{Help}");
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
|
||||||
|
|
||||||
if (!entityManager.TryGetEntity(entityUid, out var parsedEntity))
|
|
||||||
{
|
|
||||||
shell.WriteLine($"No entity found with uid {entityUid}");
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity = parsedEntity;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryParseDamageArgs(
|
|
||||||
IConsoleShell shell,
|
|
||||||
IPlayerSession? player,
|
|
||||||
string[] args,
|
|
||||||
[NotNullWhen(true)] out Damage? func)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
if (!int.TryParse(args[1], out var amount))
|
|
||||||
{
|
|
||||||
shell.WriteLine($"{args[1]} is not a valid damage integer.");
|
|
||||||
|
|
||||||
func = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_prototypeManager.TryIndex<DamageGroupPrototype>(args[0], out var damageClass))
|
|
||||||
{
|
|
||||||
func = (damageable, ignoreResistances) =>
|
|
||||||
{
|
|
||||||
if (!damageable.DamageClasses.ContainsKey(damageClass))
|
|
||||||
{
|
|
||||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage class {damageClass}");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!damageable.ChangeDamage(damageClass, amount, ignoreResistances))
|
|
||||||
{
|
|
||||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage.");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageClass} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
|
|
||||||
};
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Fall back to DamageType
|
|
||||||
else if (_prototypeManager.TryIndex<DamageTypePrototype>(args[0], out var damageType))
|
|
||||||
{
|
|
||||||
func = (damageable, ignoreResistances) =>
|
|
||||||
{
|
|
||||||
if (!damageable.DamageTypes.ContainsKey(damageType))
|
|
||||||
{
|
|
||||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage type {damageType}");
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!damageable.ChangeDamage(damageType, amount, ignoreResistances))
|
|
||||||
{
|
|
||||||
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." : ".")}");
|
|
||||||
|
|
||||||
};
|
|
||||||
return true;
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
shell.WriteLine($"{args[0]} is not a valid damage class or type.");
|
|
||||||
|
|
||||||
var types = DamageTypes();
|
|
||||||
shell.WriteLine(types);
|
|
||||||
|
|
||||||
func = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
|
||||||
{
|
|
||||||
var player = shell.Player as IPlayerSession;
|
|
||||||
bool ignoreResistances;
|
|
||||||
IEntity entity;
|
|
||||||
Damage? damageFunc;
|
|
||||||
|
|
||||||
switch (args.Length)
|
|
||||||
{
|
|
||||||
// Check if we have enough for the dmg types to show
|
|
||||||
case var n when n > 0 && (args[0] == "?" || args[0] == "¿"):
|
|
||||||
var types = DamageTypes();
|
|
||||||
|
|
||||||
if (args[0] == "¿")
|
|
||||||
{
|
|
||||||
types = types.Replace('e', 'é');
|
|
||||||
}
|
|
||||||
|
|
||||||
shell.WriteLine(types);
|
|
||||||
|
|
||||||
return;
|
|
||||||
// Not enough args
|
|
||||||
case var n when n < 2:
|
|
||||||
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];
|
|
||||||
|
|
||||||
if (!TryParseEntity(shell, player, entityUid, out var parsedEntity))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity = parsedEntity;
|
|
||||||
|
|
||||||
if (n == 4)
|
|
||||||
{
|
|
||||||
if (!bool.TryParse(args[3], out ignoreResistances))
|
|
||||||
{
|
|
||||||
shell.WriteLine($"{args[3]} is not a valid boolean value for ignoreResistances.\n{Help}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ignoreResistances = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
shell.WriteLine($"Invalid amount of arguments ({args.Length}).\n{Help}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!entity.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
{
|
|
||||||
shell.WriteLine($"Entity {entity.Name} with id {entity.Uid} does not have a {nameof(IDamageableComponent)}.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
damageFunc(damageable, ignoreResistances);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Content.Server.Administration;
|
using Content.Server.Administration;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
@@ -10,7 +9,6 @@ using Robust.Server.Player;
|
|||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Server.Damage.Commands
|
namespace Content.Server.Damage.Commands
|
||||||
{
|
{
|
||||||
@@ -21,24 +19,22 @@ namespace Content.Server.Damage.Commands
|
|||||||
public string Description => "Ouch";
|
public string Description => "Ouch";
|
||||||
public string Help => $"Usage: {Command} <type/?> <amount> (<entity uid/_>) (<ignoreResistances>)";
|
public string Help => $"Usage: {Command} <type/?> <amount> (<entity uid/_>) (<ignoreResistances>)";
|
||||||
|
|
||||||
private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
public HurtCommand() {
|
|
||||||
_prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
|
||||||
}
|
|
||||||
|
|
||||||
private string DamageTypes()
|
private string DamageTypes()
|
||||||
{
|
{
|
||||||
var msg = new StringBuilder();
|
var msg = new StringBuilder();
|
||||||
|
foreach (var dClass in Enum.GetNames(typeof(DamageClass)))
|
||||||
foreach (var damageGroup in _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
|
||||||
{
|
{
|
||||||
msg.Append($"\n{damageGroup.ID}");
|
msg.Append($"\n{dClass}");
|
||||||
if (damageGroup.DamageTypes.Any())
|
|
||||||
|
var types = Enum.Parse<DamageClass>(dClass).ToTypes();
|
||||||
|
|
||||||
|
if (types.Count > 0)
|
||||||
{
|
{
|
||||||
msg.Append(": ");
|
msg.Append(": ");
|
||||||
msg.AppendJoin('|', damageGroup.DamageTypes);
|
msg.AppendJoin('|', types);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $"Damage Types:{msg}";
|
return $"Damage Types:{msg}";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,8 +85,6 @@ namespace Content.Server.Damage.Commands
|
|||||||
string[] args,
|
string[] args,
|
||||||
[NotNullWhen(true)] out Damage? func)
|
[NotNullWhen(true)] out Damage? func)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
if (!int.TryParse(args[1], out var amount))
|
if (!int.TryParse(args[1], out var amount))
|
||||||
{
|
{
|
||||||
shell.WriteLine($"{args[1]} is not a valid damage integer.");
|
shell.WriteLine($"{args[1]} is not a valid damage integer.");
|
||||||
@@ -99,50 +93,42 @@ namespace Content.Server.Damage.Commands
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
if (Enum.TryParse<DamageClass>(args[0], true, out var damageClass))
|
if (Enum.TryParse<DamageClass>(args[0], true, out var damageClass))
|
||||||
=======
|
|
||||||
if (_prototypeManager.TryIndex<DamageGroupPrototype>(args[0], out var damageGroup))
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
if (_prototypeManager.TryIndex<DamageGroupPrototype>(args[0], out var damageGroup))
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
{
|
{
|
||||||
func = (damageable, ignoreResistances) =>
|
func = (damageable, ignoreResistances) =>
|
||||||
{
|
{
|
||||||
if (!damageable.ApplicableDamageGroups.Contains(damageGroup))
|
if (!damageable.DamageClasses.ContainsKey(damageClass))
|
||||||
{
|
{
|
||||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage group {damageGroup}");
|
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage class {damageClass}");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!damageable.TryChangeDamage(damageGroup, amount, ignoreResistances))
|
if (!damageable.ChangeDamage(damageClass, amount, ignoreResistances))
|
||||||
{
|
{
|
||||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage.");
|
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage.");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageGroup} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
|
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageClass} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
|
||||||
};
|
};
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Fall back to DamageType
|
// Fall back to DamageType
|
||||||
else if (_prototypeManager.TryIndex<DamageTypePrototype>(args[0], out var damageType))
|
else if (Enum.TryParse<DamageType>(args[0], true, out var damageType))
|
||||||
{
|
{
|
||||||
func = (damageable, ignoreResistances) =>
|
func = (damageable, ignoreResistances) =>
|
||||||
{
|
{
|
||||||
if (!damageable.IsSupportedDamageType(damageType))
|
if (!damageable.DamageTypes.ContainsKey(damageType))
|
||||||
{
|
{
|
||||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage type {damageType}");
|
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage class {damageType}");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!damageable.TryChangeDamage(damageType, amount, ignoreResistances))
|
if (!damageable.ChangeDamage(damageType, amount, ignoreResistances))
|
||||||
{
|
{
|
||||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage.");
|
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage.");
|
||||||
|
|
||||||
@@ -150,10 +136,9 @@ namespace Content.Server.Damage.Commands
|
|||||||
}
|
}
|
||||||
|
|
||||||
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageType} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
|
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageType} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
|
||||||
|
|
||||||
};
|
};
|
||||||
return true;
|
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,9 +3,6 @@ using Content.Shared.Damage;
|
|||||||
using Content.Shared.Sound;
|
using Content.Shared.Sound;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.Damage.Components
|
namespace Content.Server.Damage.Components
|
||||||
{
|
{
|
||||||
@@ -17,14 +14,8 @@ namespace Content.Server.Damage.Components
|
|||||||
{
|
{
|
||||||
public override string Name => "DamageOnHighSpeedImpact";
|
public override string Name => "DamageOnHighSpeedImpact";
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
[DataField("damage")]
|
[DataField("damage")]
|
||||||
public DamageType Damage { get; set; } = DamageType.Blunt;
|
public DamageType Damage { get; set; } = DamageType.Blunt;
|
||||||
=======
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
[DataField("minimumSpeed")]
|
[DataField("minimumSpeed")]
|
||||||
public float MinimumSpeed { get; set; } = 20f;
|
public float MinimumSpeed { get; set; } = 20f;
|
||||||
[DataField("baseDamage")]
|
[DataField("baseDamage")]
|
||||||
@@ -43,17 +34,5 @@ namespace Content.Server.Damage.Components
|
|||||||
public float DamageCooldown { get; set; } = 2f;
|
public float DamageCooldown { get; set; } = 2f;
|
||||||
|
|
||||||
internal TimeSpan LastHit = TimeSpan.Zero;
|
internal TimeSpan LastHit = TimeSpan.Zero;
|
||||||
|
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
|
||||||
// Also remove Initialize override, if no longer needed.
|
|
||||||
[DataField("damageType")]
|
|
||||||
private readonly string _damageTypeID = "Blunt";
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public DamageTypePrototype DamageType = default!;
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,6 @@ using Content.Shared.Damage.Components;
|
|||||||
using Content.Shared.Throwing;
|
using Content.Shared.Throwing;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.Damage.Components
|
namespace Content.Server.Damage.Components
|
||||||
{
|
{
|
||||||
@@ -14,59 +11,20 @@ namespace Content.Server.Damage.Components
|
|||||||
{
|
{
|
||||||
public override string Name => "DamageOnLand";
|
public override string Name => "DamageOnLand";
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
[DataField("damageType")]
|
[DataField("damageType")]
|
||||||
private DamageType _damageType = DamageType.Blunt;
|
private DamageType _damageType = DamageType.Blunt;
|
||||||
|
|
||||||
=======
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
[DataField("amount")]
|
[DataField("amount")]
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
private int _amount = 1;
|
private int _amount = 1;
|
||||||
|
|
||||||
[DataField("ignoreResistances")]
|
[DataField("ignoreResistances")]
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
private bool _ignoreResistances;
|
private bool _ignoreResistances;
|
||||||
|
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
|
||||||
// Also remove Initialize override, if no longer needed.
|
|
||||||
[DataField("damageType")]
|
|
||||||
private readonly string _damageTypeID = "Blunt";
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public DamageTypePrototype DamageType = default!;
|
|
||||||
protected override void Initialize()
|
|
||||||
<<<<<<< HEAD
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ILand.Land(LandEventArgs eventArgs)
|
void ILand.Land(LandEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable)) return;
|
if (!Owner.TryGetComponent(out IDamageableComponent? damageable)) return;
|
||||||
|
|
||||||
damageable.ChangeDamage(_damageType, _amount, _ignoreResistances, eventArgs.User);
|
damageable.ChangeDamage(_damageType, _amount, _ignoreResistances, eventArgs.User);
|
||||||
=======
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
return;
|
|
||||||
damageable.TryChangeDamage(DamageType, _amount, _ignoreResistances);
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ILand.Land(LandEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
return;
|
|
||||||
damageable.TryChangeDamage(DamageType, _amount, _ignoreResistances);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Tools.Components;
|
using Content.Server.Tools.Components;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
@@ -7,16 +7,12 @@ using Content.Shared.Interaction;
|
|||||||
using Content.Shared.Tool;
|
using Content.Shared.Tool;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.Damage.Components
|
namespace Content.Server.Damage.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class DamageOnToolInteractComponent : Component, IInteractUsing
|
public class DamageOnToolInteractComponent : Component, IInteractUsing
|
||||||
{
|
{
|
||||||
|
|
||||||
public override string Name => "DamageOnToolInteract";
|
public override string Name => "DamageOnToolInteract";
|
||||||
|
|
||||||
[DataField("damage")]
|
[DataField("damage")]
|
||||||
@@ -25,32 +21,6 @@ namespace Content.Server.Damage.Components
|
|||||||
[DataField("tools")]
|
[DataField("tools")]
|
||||||
private List<ToolQuality> _tools = new();
|
private List<ToolQuality> _tools = new();
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// TODO PROTOTYPE Replace these datafield variable with prototype references, once they are supported.
|
|
||||||
// Also remove Initialize override, if no longer needed.
|
|
||||||
[DataField("weldingDamageType")]
|
|
||||||
private readonly string _weldingDamageTypeID = "Heat";
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public DamageTypePrototype WeldingDamageType = default!;
|
|
||||||
[DataField("defaultDamageType")]
|
|
||||||
private readonly string _defaultDamageTypeID = "Blunt";
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public DamageTypePrototype DefaultDamageType = default!;
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
WeldingDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_weldingDamageTypeID);
|
|
||||||
DefaultDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_defaultDamageTypeID);
|
|
||||||
}
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (eventArgs.Using.TryGetComponent<ToolComponent>(out var tool))
|
if (eventArgs.Using.TryGetComponent<ToolComponent>(out var tool))
|
||||||
@@ -74,26 +44,17 @@ namespace Content.Server.Damage.Components
|
|||||||
|
|
||||||
protected bool CallDamage(InteractUsingEventArgs eventArgs, ToolComponent tool)
|
protected bool CallDamage(InteractUsingEventArgs eventArgs, ToolComponent tool)
|
||||||
{
|
{
|
||||||
if (!eventArgs.Target.TryGetComponent<IDamageableComponent>(out var damageable))
|
if (eventArgs.Target.TryGetComponent<IDamageableComponent>(out var damageable))
|
||||||
|
{
|
||||||
|
damageable.ChangeDamage(tool.HasQuality(ToolQuality.Welding)
|
||||||
|
? DamageType.Heat
|
||||||
|
: DamageType.Blunt,
|
||||||
|
Damage, false, eventArgs.User);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
damageable.TryChangeDamage(tool.HasQuality(ToolQuality.Welding)
|
|
||||||
? WeldingDamageType
|
|
||||||
: DefaultDamageType,
|
|
||||||
Damage);
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ using Content.Shared.Damage;
|
|||||||
using Robust.Shared.Analyzers;
|
using Robust.Shared.Analyzers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
|
|
||||||
namespace Content.Server.Damage.Components
|
namespace Content.Server.Damage.Components
|
||||||
{
|
{
|
||||||
@@ -13,47 +11,13 @@ namespace Content.Server.Damage.Components
|
|||||||
{
|
{
|
||||||
public override string Name => "DamageOtherOnHit";
|
public override string Name => "DamageOtherOnHit";
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
[DataField("damageType")]
|
[DataField("damageType")]
|
||||||
public DamageType DamageType { get; } = DamageType.Blunt;
|
public DamageType DamageType { get; } = DamageType.Blunt;
|
||||||
=======
|
|
||||||
[DataField("damageType",required: true)]
|
|
||||||
private readonly string _damageType = default!;
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
|
|
||||||
=======
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
[DataField("amount")]
|
[DataField("amount")]
|
||||||
public int Amount { get; } = 1;
|
public int Amount { get; } = 1;
|
||||||
|
|
||||||
[DataField("ignoreResistances")]
|
[DataField("ignoreResistances")]
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
public bool IgnoreResistances { get; } = false;
|
public bool IgnoreResistances { get; } = false;
|
||||||
=======
|
|
||||||
private bool _ignoreResistances;
|
|
||||||
=======
|
|
||||||
public bool IgnoreResistances { get; } = false;
|
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
=======
|
|
||||||
public bool IgnoreResistances { get; } = false;
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace Content.Server.Damage
|
|||||||
if (ComponentManager.TryGetComponent(uid, out StunnableComponent? stun) && _robustRandom.Prob(component.StunChance))
|
if (ComponentManager.TryGetComponent(uid, out StunnableComponent? stun) && _robustRandom.Prob(component.StunChance))
|
||||||
stun.Stun(component.StunSeconds);
|
stun.Stun(component.StunSeconds);
|
||||||
|
|
||||||
damageable.TryChangeDamage(component.DamageType, damage);
|
damageable.ChangeDamage(component.Damage, damage, false, args.OtherFixture.Body.Owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,15 +17,7 @@ namespace Content.Server.Damage
|
|||||||
if (!args.Target.TryGetComponent(out IDamageableComponent? damageable))
|
if (!args.Target.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
damageable.ChangeDamage(component.DamageType, component.Amount, component.IgnoreResistances, args.User);
|
damageable.ChangeDamage(component.DamageType, component.Amount, component.IgnoreResistances, args.User);
|
||||||
=======
|
|
||||||
damageable.TryChangeDamage(component.DamageType, component.Amount, component.IgnoreResistances);
|
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
=======
|
|
||||||
damageable.TryChangeDamage(component.DamageType, component.Amount, component.IgnoreResistances);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,9 @@
|
|||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.Atmos.Components;
|
using Content.Server.Atmos.Components;
|
||||||
=======
|
|
||||||
#nullable enable
|
|
||||||
using System.Collections.Generic;
|
|
||||||
>>>>>>> Merge fixes
|
|
||||||
=======
|
|
||||||
using System.Collections.Generic;
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
using System.Collections.Generic;
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.Atmos.Components;
|
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
|
using Content.Shared.Damage.Resistances;
|
||||||
using Content.Shared.GameTicking;
|
using Content.Shared.GameTicking;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
@@ -56,9 +43,8 @@ namespace Content.Server.Damage
|
|||||||
|
|
||||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
{
|
{
|
||||||
damageable.SupportedDamageTypes.Clear();
|
damageable.SupportedTypes.Clear();
|
||||||
damageable.FullySupportedDamageGroups.Clear();
|
damageable.SupportedClasses.Clear();
|
||||||
damageable.ApplicableDamageGroups.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -83,33 +69,14 @@ namespace Content.Server.Damage
|
|||||||
|
|
||||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
{
|
{
|
||||||
if (old.SupportedDamageTypes != null)
|
if (old.SupportedTypes != null)
|
||||||
<<<<<<< HEAD
|
|
||||||
{
|
{
|
||||||
damageable.SupportedDamageTypes.UnionWith(old.SupportedDamageTypes);
|
damageable.SupportedTypes.UnionWith(old.SupportedTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (old.SupportedDamageGroups != null)
|
if (old.SupportedClasses != null)
|
||||||
{
|
{
|
||||||
damageable.FullySupportedDamageGroups.UnionWith(old.SupportedDamageGroups);
|
damageable.SupportedClasses.UnionWith(old.SupportedClasses);
|
||||||
}
|
|
||||||
|
|
||||||
if (old.ApplicableDamageGroups != null)
|
|
||||||
{
|
|
||||||
=======
|
|
||||||
{
|
|
||||||
damageable.SupportedDamageTypes.UnionWith(old.SupportedDamageTypes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (old.SupportedDamageGroups != null)
|
|
||||||
{
|
|
||||||
damageable.FullySupportedDamageGroups.UnionWith(old.SupportedDamageGroups);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (old.ApplicableDamageGroups != null)
|
|
||||||
{
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
damageable.ApplicableDamageGroups.UnionWith(old.ApplicableDamageGroups);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,9 +111,8 @@ namespace Content.Server.Damage
|
|||||||
|
|
||||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
{
|
{
|
||||||
SupportedDamageTypes = damageable.SupportedDamageTypes.ToHashSet();
|
SupportedTypes = damageable.SupportedTypes.ToHashSet();
|
||||||
SupportedDamageGroups = damageable.FullySupportedDamageGroups.ToHashSet();
|
SupportedClasses = damageable.SupportedClasses.ToHashSet();
|
||||||
ApplicableDamageGroups = damageable.ApplicableDamageGroups.ToHashSet();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,11 +120,9 @@ namespace Content.Server.Damage
|
|||||||
|
|
||||||
public bool MovedByPressure { get; }
|
public bool MovedByPressure { get; }
|
||||||
|
|
||||||
public HashSet<DamageTypePrototype>? SupportedDamageTypes { get; }
|
public HashSet<DamageType>? SupportedTypes { get; }
|
||||||
|
|
||||||
public HashSet<DamageGroupPrototype>? SupportedDamageGroups { get; }
|
public HashSet<DamageClass>? SupportedClasses { get; }
|
||||||
|
|
||||||
public HashSet<DamageGroupPrototype>? ApplicableDamageGroups { get; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ namespace Content.Server.Damage
|
|||||||
{
|
{
|
||||||
if (target.TryGetComponent(out IDamageableComponent? damage))
|
if (target.TryGetComponent(out IDamageableComponent? damage))
|
||||||
{
|
{
|
||||||
damage.TrySetAllDamage(0);
|
damage.Heal();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target.TryGetComponent(out IMobStateComponent? mobState))
|
if (target.TryGetComponent(out IMobStateComponent? mobState))
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
#nullable enable
|
using System;
|
||||||
using System;
|
|
||||||
using Content.Server.GameObjects.EntitySystems;
|
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
using Content.Shared.Damage.Components;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers
|
namespace Content.Server.Destructible.Thresholds.Triggers
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A trigger that will activate when the amount of damage received
|
/// A trigger that will activate when the amount of damage received
|
||||||
@@ -19,7 +17,7 @@ namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers
|
|||||||
/// The class to check the damage of.
|
/// The class to check the damage of.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("class")]
|
[DataField("class")]
|
||||||
public DamageGroupPrototype? Group { get; set; }
|
public DamageClass? Class { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of damage at which this threshold will trigger.
|
/// The amount of damage at which this threshold will trigger.
|
||||||
@@ -29,12 +27,12 @@ namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers
|
|||||||
|
|
||||||
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
||||||
{
|
{
|
||||||
if (Group == null)
|
if (Class == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return damageable.TryGetDamage(Group, out var damageReceived) &&
|
return damageable.TryGetDamage(Class.Value, out var damageReceived) &&
|
||||||
damageReceived >= Damage;
|
damageReceived >= Damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.Damage.Components;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
|
|
||||||
namespace Content.Server.Destructible.Thresholds.Triggers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A trigger that will activate when the amount of damage received
|
|
||||||
/// of the specified class is above the specified threshold.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable]
|
|
||||||
[DataDefinition]
|
|
||||||
public class DamageGroupTrigger : IThresholdTrigger
|
|
||||||
{
|
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
|
||||||
// While you're at it, maybe also combine damageGroup and damage into a dictionary, and allow it to test a sum
|
|
||||||
// of damage types?
|
|
||||||
[DataField("damageGroup", required: true)]
|
|
||||||
private string _damageGroupID { get; set; } = default!;
|
|
||||||
public DamageGroupPrototype DamageGroup => IoCManager.Resolve<IPrototypeManager>().Index<DamageGroupPrototype>(_damageGroupID);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The amount of damage at which this threshold will trigger.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("damage", required: true)]
|
|
||||||
public int Damage { get; set; } = default!;
|
|
||||||
|
|
||||||
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
|
||||||
{
|
|
||||||
if (DamageGroup == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return damageable.TryGetDamage(DamageGroup, out var damageReceived) &&
|
|
||||||
damageReceived >= Damage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
@@ -15,8 +15,8 @@ namespace Content.Server.Destructible.Thresholds.Triggers
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of damage at which this threshold will trigger.
|
/// The amount of damage at which this threshold will trigger.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("damage", required: true)]
|
[DataField("damage")]
|
||||||
public int Damage { get; set; } = default!;
|
public int Damage { get; set; }
|
||||||
|
|
||||||
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
|
|
||||||
namespace Content.Server.Destructible.Thresholds.Triggers
|
namespace Content.Server.Destructible.Thresholds.Triggers
|
||||||
{
|
{
|
||||||
@@ -15,52 +13,20 @@ namespace Content.Server.Destructible.Thresholds.Triggers
|
|||||||
[DataDefinition]
|
[DataDefinition]
|
||||||
public class DamageTypeTrigger : IThresholdTrigger
|
public class DamageTypeTrigger : IThresholdTrigger
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
[DataField("type")]
|
[DataField("type")]
|
||||||
public DamageType? Type { get; set; }
|
public DamageType? Type { get; set; }
|
||||||
=======
|
|
||||||
[DataField("damageType")]
|
|
||||||
public string? DamageType { get; set; }
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// 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);
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
[DataField("damage", required: true)]
|
[DataField("damage")]
|
||||||
public int Damage { get; set; } = default!;
|
public int Damage { get; set; }
|
||||||
|
|
||||||
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
||||||
{
|
{
|
||||||
if (DamageType == null)
|
if (Type == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
return damageable.TryGetDamage(Type.Value, out var damageReceived) &&
|
return damageable.TryGetDamage(Type.Value, out var damageReceived) &&
|
||||||
=======
|
|
||||||
return damageable.TryGetDamage(damageable.GetDamageType(DamageType), out var damageReceived) &&
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
return damageable.TryGetDamage(DamageType, out var damageReceived) &&
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
return damageable.TryGetDamage(DamageType, out var damageReceived) &&
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
damageReceived >= Damage;
|
damageReceived >= Damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ using Robust.Shared.Players;
|
|||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using Timer = Robust.Shared.Timing.Timer;
|
using Timer = Robust.Shared.Timing.Timer;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
|
|
||||||
namespace Content.Server.Doors.Components
|
namespace Content.Server.Doors.Components
|
||||||
{
|
{
|
||||||
@@ -44,37 +42,8 @@ namespace Content.Server.Doors.Components
|
|||||||
[DataField("board")]
|
[DataField("board")]
|
||||||
private string? _boardPrototype;
|
private string? _boardPrototype;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
[DataField("tryOpenDoorSound")]
|
[DataField("tryOpenDoorSound")]
|
||||||
private SoundSpecifier _tryOpenDoorSound = new SoundPathSpecifier("/Audio/Effects/bang.ogg");
|
private SoundSpecifier _tryOpenDoorSound = new SoundPathSpecifier("/Audio/Effects/bang.ogg");
|
||||||
=======
|
|
||||||
=======
|
|
||||||
[DataField("tryOpenDoorSound")]
|
|
||||||
private SoundSpecifier _tryOpenDoorSound = new SoundPathSpecifier("/Audio/Effects/bang.ogg");
|
|
||||||
|
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
=======
|
|
||||||
[DataField("tryOpenDoorSound")]
|
|
||||||
private SoundSpecifier _tryOpenDoorSound = new SoundPathSpecifier("/Audio/Effects/bang.ogg");
|
|
||||||
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
|
||||||
// Also remove Initialize override, if no longer needed.
|
|
||||||
[DataField("damageType")]
|
|
||||||
private readonly string _damageTypeID = "Blunt";
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public DamageTypePrototype DamageType = default!;
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
|
||||||
}
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
public override DoorState State
|
public override DoorState State
|
||||||
{
|
{
|
||||||
@@ -567,15 +536,7 @@ namespace Content.Server.Doors.Components
|
|||||||
hitsomebody = true;
|
hitsomebody = true;
|
||||||
CurrentlyCrushing.Add(e.Owner.Uid);
|
CurrentlyCrushing.Add(e.Owner.Uid);
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
damage.ChangeDamage(DamageType.Blunt, DoorCrushDamage, false, Owner);
|
damage.ChangeDamage(DamageType.Blunt, DoorCrushDamage, false, Owner);
|
||||||
=======
|
|
||||||
damage.TryChangeDamage(DamageType, DoorCrushDamage);
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
damage.TryChangeDamage(DamageType, DoorCrushDamage);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
stun.Paralyze(DoorStunTime);
|
stun.Paralyze(DoorStunTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Content.Server.GameObjects.Components.Mobs;
|
|
||||||
using Content.Server.Interfaces.GameObjects;
|
|
||||||
using Content.Shared.Alert;
|
|
||||||
using Content.Shared.Atmos;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Dependency = Robust.Shared.IoC.DependencyAttribute;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Atmos
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Barotrauma: injury because of changes in air pressure.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent]
|
|
||||||
public class BarotraumaComponent : Component
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
|
|
||||||
public override string Name => "Barotrauma";
|
|
||||||
|
|
||||||
[DataField("damageType",required: true)]
|
|
||||||
private readonly DamageTypePrototype _damageType = default!;
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public void Update(float airPressure)
|
|
||||||
{
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable)) return;
|
|
||||||
|
|
||||||
var status = Owner.GetComponentOrNull<ServerAlertsComponent>();
|
|
||||||
var highPressureMultiplier = 1f;
|
|
||||||
var lowPressureMultiplier = 1f;
|
|
||||||
|
|
||||||
foreach (var protection in Owner.GetAllComponents<IPressureProtection>())
|
|
||||||
{
|
|
||||||
highPressureMultiplier *= protection.HighPressureMultiplier;
|
|
||||||
lowPressureMultiplier *= protection.LowPressureMultiplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pressure = MathF.Max(airPressure, 1f);
|
|
||||||
|
|
||||||
switch (pressure)
|
|
||||||
{
|
|
||||||
// Low pressure.
|
|
||||||
case var p when p <= Atmospherics.WarningLowPressure:
|
|
||||||
pressure *= lowPressureMultiplier;
|
|
||||||
|
|
||||||
if(pressure > Atmospherics.WarningLowPressure)
|
|
||||||
goto default;
|
|
||||||
|
|
||||||
damageable.ChangeDamage(damageable.GetDamageType("Blunt"), Atmospherics.LowPressureDamage, false, Owner);
|
|
||||||
|
|
||||||
if (status == null) break;
|
|
||||||
|
|
||||||
if (pressure <= Atmospherics.HazardLowPressure)
|
|
||||||
{
|
|
||||||
status.ShowAlert(AlertType.LowPressure, 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
status.ShowAlert(AlertType.LowPressure, 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// High pressure.
|
|
||||||
case var p when p >= Atmospherics.WarningHighPressure:
|
|
||||||
pressure *= highPressureMultiplier;
|
|
||||||
|
|
||||||
if(pressure < Atmospherics.WarningHighPressure)
|
|
||||||
goto default;
|
|
||||||
|
|
||||||
var damage = (int) MathF.Min((pressure / Atmospherics.HazardHighPressure) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage);
|
|
||||||
|
|
||||||
damageable.ChangeDamage(_damageType, damage, false, Owner);
|
|
||||||
|
|
||||||
if (status == null) break;
|
|
||||||
|
|
||||||
if (pressure >= Atmospherics.HazardHighPressure)
|
|
||||||
{
|
|
||||||
status.ShowAlert(AlertType.HighPressure, 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
status.ShowAlert(AlertType.HighPressure, 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Normal pressure.
|
|
||||||
default:
|
|
||||||
status?.ClearAlertCategory(AlertCategory.Pressure);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Content.Server.Atmos;
|
|
||||||
using Content.Server.GameObjects.Components.Interactable;
|
|
||||||
using Content.Server.GameObjects.Components.Mobs;
|
|
||||||
using Content.Server.GameObjects.Components.Temperature;
|
|
||||||
using Content.Shared.Alert;
|
|
||||||
using Content.Shared.Atmos;
|
|
||||||
using Content.Shared.Chemistry;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Atmos;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
|
||||||
using Content.Shared.Interfaces;
|
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.Physics;
|
|
||||||
using Robust.Shared.Physics.Collision;
|
|
||||||
using Robust.Shared.Physics.Dynamics;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Atmos
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public class FlammableComponent : SharedFlammableComponent, IStartCollide, IFireAct, IInteractUsing
|
|
||||||
{
|
|
||||||
private bool _resisting = false;
|
|
||||||
private readonly List<EntityUid> _collided = new();
|
|
||||||
|
|
||||||
[DataField("damageType", required:true)]
|
|
||||||
private readonly DamageTypePrototype _damageType = default!;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public bool OnFire { get; private set; }
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public float FireStacks { get; private set; }
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("fireSpread")]
|
|
||||||
public bool FireSpread { get; private set; } = false;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("canResistFire")]
|
|
||||||
public bool CanResistFire { get; private set; } = false;
|
|
||||||
|
|
||||||
public void Ignite()
|
|
||||||
{
|
|
||||||
if (FireStacks > 0 && !OnFire)
|
|
||||||
{
|
|
||||||
OnFire = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateAppearance();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Extinguish()
|
|
||||||
{
|
|
||||||
if (!OnFire) return;
|
|
||||||
OnFire = false;
|
|
||||||
FireStacks = 0;
|
|
||||||
|
|
||||||
_collided.Clear();
|
|
||||||
|
|
||||||
UpdateAppearance();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AdjustFireStacks(float relativeFireStacks)
|
|
||||||
{
|
|
||||||
FireStacks = MathF.Min(MathF.Max(-10f, FireStacks + relativeFireStacks), 20f);
|
|
||||||
if (OnFire && FireStacks <= 0)
|
|
||||||
Extinguish();
|
|
||||||
|
|
||||||
UpdateAppearance();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(TileAtmosphere tile)
|
|
||||||
{
|
|
||||||
// Slowly dry ourselves off if wet.
|
|
||||||
if (FireStacks < 0)
|
|
||||||
{
|
|
||||||
FireStacks = MathF.Min(0, FireStacks + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Owner.TryGetComponent(out ServerAlertsComponent? status);
|
|
||||||
|
|
||||||
if (!OnFire)
|
|
||||||
{
|
|
||||||
status?.ClearAlert(AlertType.Fire);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
status?.ShowAlert(AlertType.Fire);
|
|
||||||
|
|
||||||
if (FireStacks > 0)
|
|
||||||
{
|
|
||||||
if (Owner.TryGetComponent(out TemperatureComponent? temp))
|
|
||||||
{
|
|
||||||
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.ChangeDamage(_damageType, damage, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
AdjustFireStacks(-0.1f * (_resisting ? 10f : 1f));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Extinguish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're in an oxygenless environment, put the fire out.
|
|
||||||
if (tile.Air?.GetMoles(Gas.Oxygen) < 1f)
|
|
||||||
{
|
|
||||||
Extinguish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
tile.HotspotExpose(700, 50, true);
|
|
||||||
|
|
||||||
var physics = Owner.GetComponent<IPhysBody>();
|
|
||||||
|
|
||||||
foreach (var uid in _collided.ToArray())
|
|
||||||
{
|
|
||||||
if (!uid.IsValid() || !Owner.EntityManager.EntityExists(uid))
|
|
||||||
{
|
|
||||||
_collided.Remove(uid);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var entity = Owner.EntityManager.GetEntity(uid);
|
|
||||||
var otherPhysics = entity.GetComponent<IPhysBody>();
|
|
||||||
|
|
||||||
if (!physics.GetWorldAABB().Intersects(otherPhysics.GetWorldAABB()))
|
|
||||||
{
|
|
||||||
_collided.Remove(uid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void IStartCollide.CollideWith(Fixture ourFixture, Fixture otherFixture, in Manifold manifold)
|
|
||||||
{
|
|
||||||
if (!otherFixture.Body.Owner.TryGetComponent(out FlammableComponent? otherFlammable))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!FireSpread || !otherFlammable.FireSpread)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (OnFire)
|
|
||||||
{
|
|
||||||
if (otherFlammable.OnFire)
|
|
||||||
{
|
|
||||||
var fireSplit = (FireStacks + otherFlammable.FireStacks) / 2;
|
|
||||||
FireStacks = fireSplit;
|
|
||||||
otherFlammable.FireStacks = fireSplit;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FireStacks /= 2;
|
|
||||||
otherFlammable.FireStacks += FireStacks;
|
|
||||||
otherFlammable.Ignite();
|
|
||||||
}
|
|
||||||
} else if (otherFlammable.OnFire)
|
|
||||||
{
|
|
||||||
otherFlammable.FireStacks /= 2;
|
|
||||||
FireStacks += otherFlammable.FireStacks;
|
|
||||||
Ignite();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAppearance()
|
|
||||||
{
|
|
||||||
if (Owner.Deleted || !Owner.TryGetComponent(out AppearanceComponent? appearanceComponent)) return;
|
|
||||||
appearanceComponent.SetData(FireVisuals.OnFire, OnFire);
|
|
||||||
appearanceComponent.SetData(FireVisuals.FireStacks, FireStacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FireAct(float temperature, float volume)
|
|
||||||
{
|
|
||||||
AdjustFireStacks(3);
|
|
||||||
Ignite();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This needs some improvements...
|
|
||||||
public void Resist()
|
|
||||||
{
|
|
||||||
if (!OnFire || !ActionBlockerSystem.CanInteract(Owner) || _resisting || !Owner.TryGetComponent(out StunnableComponent? stunnable)) return;
|
|
||||||
|
|
||||||
_resisting = true;
|
|
||||||
|
|
||||||
Owner.PopupMessage(Loc.GetString("You stop, drop, and roll!"));
|
|
||||||
stunnable.Paralyze(2f);
|
|
||||||
|
|
||||||
Owner.SpawnTimer(2000, () =>
|
|
||||||
{
|
|
||||||
_resisting = false;
|
|
||||||
FireStacks -= 3f;
|
|
||||||
UpdateAppearance();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
foreach (var hotItem in eventArgs.Using.GetAllComponents<IHotItem>())
|
|
||||||
{
|
|
||||||
if (hotItem.IsCurrentlyHot())
|
|
||||||
{
|
|
||||||
Ignite();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Content.Server.GameObjects.Components.Mobs;
|
|
||||||
using Content.Shared.Audio;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Physics;
|
|
||||||
using Robust.Shared.Physics.Collision;
|
|
||||||
using Robust.Shared.Physics.Dynamics;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Random;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.Timing;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Damage
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public class DamageOnHighSpeedImpactComponent : Component, IStartCollide
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
||||||
|
|
||||||
public override string Name => "DamageOnHighSpeedImpact";
|
|
||||||
|
|
||||||
[DataField("damageType", required: true)]
|
|
||||||
public DamageTypePrototype DamageType { get; set; } = default!;
|
|
||||||
[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")]
|
|
||||||
public string SoundHit { get; set; } = "";
|
|
||||||
[DataField("stunChance")]
|
|
||||||
public float StunChance { get; set; } = 0.25f;
|
|
||||||
[DataField("stunMinimumDamage")]
|
|
||||||
public int StunMinimumDamage { get; set; } = 10;
|
|
||||||
[DataField("stunSeconds")]
|
|
||||||
public float StunSeconds { get; set; } = 1f;
|
|
||||||
[DataField("damageCooldown")]
|
|
||||||
public float DamageCooldown { get; set; } = 2f;
|
|
||||||
private TimeSpan _lastHit = TimeSpan.Zero;
|
|
||||||
|
|
||||||
void IStartCollide.CollideWith(Fixture ourFixture, Fixture otherFixture, in Manifold manifold)
|
|
||||||
{
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable)) return;
|
|
||||||
|
|
||||||
var speed = ourFixture.Body.LinearVelocity.Length;
|
|
||||||
|
|
||||||
if (speed < MinimumSpeed) return;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(SoundHit))
|
|
||||||
SoundSystem.Play(Filter.Pvs(otherFixture.Body.Owner), SoundHit, otherFixture.Body.Owner, AudioHelpers.WithVariation(0.125f).WithVolume(-0.125f));
|
|
||||||
|
|
||||||
if ((_gameTiming.CurTime - _lastHit).TotalSeconds < DamageCooldown)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_lastHit = _gameTiming.CurTime;
|
|
||||||
|
|
||||||
var damage = (int) (BaseDamage * (speed / MinimumSpeed) * Factor);
|
|
||||||
|
|
||||||
if (Owner.TryGetComponent(out StunnableComponent? stun) && _robustRandom.Prob(StunChance))
|
|
||||||
stun.Stun(StunSeconds);
|
|
||||||
|
|
||||||
damageable.ChangeDamage(DamageType, damage, false, otherFixture.Body.Owner);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Damage
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public class DamageOnLandComponent : Component, ILand
|
|
||||||
{
|
|
||||||
public override string Name => "DamageOnLand";
|
|
||||||
|
|
||||||
[DataField("damageType", required: true)]
|
|
||||||
private DamageTypePrototype _damageType = default!;
|
|
||||||
|
|
||||||
[DataField("amount")]
|
|
||||||
private int _amount = 1;
|
|
||||||
|
|
||||||
[DataField("ignoreResistances")]
|
|
||||||
private bool _ignoreResistances;
|
|
||||||
|
|
||||||
void ILand.Land(LandEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
return;
|
|
||||||
damageable.ChangeDamage(_damageType, _amount, _ignoreResistances, eventArgs.User);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Content.Server.GameObjects.Components.Interactable;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Interactable;
|
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Damage
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public class DamageOnToolInteractComponent : Component, IInteractUsing
|
|
||||||
{
|
|
||||||
public override string Name => "DamageOnToolInteract";
|
|
||||||
|
|
||||||
[DataField("damage")]
|
|
||||||
protected int Damage;
|
|
||||||
|
|
||||||
[DataField("tools")]
|
|
||||||
private List<ToolQuality> _tools = new();
|
|
||||||
|
|
||||||
[DataField("weldingDamageType",required: true)]
|
|
||||||
private readonly DamageTypePrototype _weldingDamageType = default!;
|
|
||||||
|
|
||||||
[DataField("defaultDamageType",required: true)]
|
|
||||||
private readonly DamageTypePrototype _defaultDamageType = default!;
|
|
||||||
|
|
||||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (eventArgs.Using.TryGetComponent<ToolComponent>(out var tool))
|
|
||||||
{
|
|
||||||
foreach (var toolQuality in _tools)
|
|
||||||
{
|
|
||||||
if (tool.HasQuality(ToolQuality.Welding) && toolQuality == ToolQuality.Welding)
|
|
||||||
{
|
|
||||||
if (eventArgs.Using.TryGetComponent(out WelderComponent? welder))
|
|
||||||
{
|
|
||||||
if (welder.WelderLit) return CallDamage(eventArgs, tool);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected bool CallDamage(InteractUsingEventArgs eventArgs, ToolComponent tool)
|
|
||||||
{
|
|
||||||
if (!eventArgs.Target.TryGetComponent<IDamageableComponent>(out var damageable))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
damageable.ChangeDamage(tool.HasQuality(ToolQuality.Welding)
|
|
||||||
? _weldingDamageType
|
|
||||||
: _defaultDamageType,
|
|
||||||
Damage, false, eventArgs.User);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Damage
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public class DamageOtherOnHitComponent : Component, IThrowCollide
|
|
||||||
{
|
|
||||||
public override string Name => "DamageOtherOnHit";
|
|
||||||
|
|
||||||
[DataField("damageType",required: true)]
|
|
||||||
private readonly DamageTypePrototype _damageType = default!;
|
|
||||||
|
|
||||||
[DataField("amount")]
|
|
||||||
private int _amount = 1;
|
|
||||||
|
|
||||||
[DataField("ignoreResistances")]
|
|
||||||
private bool _ignoreResistances;
|
|
||||||
|
|
||||||
void IThrowCollide.DoHit(ThrowCollideEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (!eventArgs.Target.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
return;
|
|
||||||
damageable.ChangeDamage(_damageType, _amount, _ignoreResistances, eventArgs.User);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using Content.Server.GameObjects.EntitySystems;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A trigger that will activate when the amount of damage received
|
|
||||||
/// of the specified type is above the specified threshold.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable]
|
|
||||||
[DataDefinition]
|
|
||||||
public class DamageTypeTrigger : IThresholdTrigger
|
|
||||||
{
|
|
||||||
[DataField("type")]
|
|
||||||
public DamageTypePrototype? Type { get; set; }
|
|
||||||
|
|
||||||
[DataField("damage")]
|
|
||||||
public int Damage { get; set; }
|
|
||||||
|
|
||||||
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
|
||||||
{
|
|
||||||
if (Type == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return damageable.TryGetDamage(Type, out var damageReceived) &&
|
|
||||||
damageReceived >= Damage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,679 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Content.Server.GameObjects.Components.Access;
|
|
||||||
using Content.Server.GameObjects.Components.Atmos;
|
|
||||||
using Content.Server.GameObjects.Components.GUI;
|
|
||||||
using Content.Server.GameObjects.Components.Interactable;
|
|
||||||
using Content.Server.GameObjects.Components.Mobs;
|
|
||||||
using Content.Server.GameObjects.EntitySystems;
|
|
||||||
using Content.Server.Interfaces.GameObjects.Components.Doors;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Body;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Doors;
|
|
||||||
using Content.Shared.GameObjects.Components.Interactable;
|
|
||||||
using Content.Shared.GameObjects.Components.Movement;
|
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Log;
|
|
||||||
using Robust.Shared.Maths;
|
|
||||||
using Robust.Shared.Physics;
|
|
||||||
using Robust.Shared.Players;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.Physics.Broadphase;
|
|
||||||
using Robust.Shared.Physics.Collision;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
using Timer = Robust.Shared.Timing.Timer;
|
|
||||||
using Content.Server.GameObjects.Components.Construction;
|
|
||||||
using Robust.Shared.Containers;
|
|
||||||
using Robust.Shared.Physics.Dynamics;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Doors
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
[ComponentReference(typeof(IActivate))]
|
|
||||||
[ComponentReference(typeof(SharedDoorComponent))]
|
|
||||||
public class ServerDoorComponent : SharedDoorComponent, IActivate, IStartCollide, IInteractUsing, IMapInit
|
|
||||||
{
|
|
||||||
[ComponentDependency]
|
|
||||||
private readonly IDoorCheck? _doorCheck = null;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("board")]
|
|
||||||
private string? _boardPrototype;
|
|
||||||
|
|
||||||
[DataField("damageType", required: true)]
|
|
||||||
private readonly DamageTypePrototype _damageType = default!;
|
|
||||||
|
|
||||||
public override DoorState State
|
|
||||||
{
|
|
||||||
get => base.State;
|
|
||||||
protected set
|
|
||||||
{
|
|
||||||
if (State == value)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
base.State = value;
|
|
||||||
|
|
||||||
StateChangeStartTime = State switch
|
|
||||||
{
|
|
||||||
DoorState.Open or DoorState.Closed => null,
|
|
||||||
DoorState.Opening or DoorState.Closing => GameTiming.CurTime,
|
|
||||||
_ => throw new ArgumentOutOfRangeException(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (_doorCheck != null)
|
|
||||||
{
|
|
||||||
_doorCheck.OnStateChange(State);
|
|
||||||
RefreshAutoClose();
|
|
||||||
}
|
|
||||||
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly TimeSpan AutoCloseDelay = TimeSpan.FromSeconds(5);
|
|
||||||
|
|
||||||
private CancellationTokenSource? _stateChangeCancelTokenSource;
|
|
||||||
private CancellationTokenSource? _autoCloseCancelTokenSource;
|
|
||||||
|
|
||||||
private const int DoorCrushDamage = 15;
|
|
||||||
private const float DoorStunTime = 5f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the door will ever crush.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("inhibitCrush")]
|
|
||||||
private bool _inhibitCrush;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the door blocks light.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("occludes")]
|
|
||||||
private bool _occludes = true;
|
|
||||||
public bool Occludes => _occludes;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the door will open when it is bumped into.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("bumpOpen")]
|
|
||||||
private bool _bumpOpen = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the door starts open when it's first loaded from prototype. A door won't start open if its prototype is also welded shut.
|
|
||||||
/// Handled in Startup().
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("startOpen")]
|
|
||||||
private bool _startOpen;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the airlock is welded shut. Can be set by the prototype, although this will fail if the door isn't weldable.
|
|
||||||
/// When set by prototype, handled in Startup().
|
|
||||||
/// </summary>
|
|
||||||
[DataField("welded")]
|
|
||||||
private bool _isWeldedShut;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the airlock is welded shut.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public bool IsWeldedShut
|
|
||||||
{
|
|
||||||
get => _isWeldedShut;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_isWeldedShut == value)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_isWeldedShut = value;
|
|
||||||
SetAppearance(_isWeldedShut ? DoorVisualState.Welded : DoorVisualState.Closed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the door can ever be welded shut.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("weldable")]
|
|
||||||
private bool _weldable = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the door can currently be welded.
|
|
||||||
/// </summary>
|
|
||||||
private bool CanWeldShut => _weldable && State == DoorState.Closed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether something is currently using a welder on this so DoAfter isn't spammed.
|
|
||||||
/// </summary>
|
|
||||||
private bool _beingWelded;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("canCrush")]
|
|
||||||
private bool _canCrush = true;
|
|
||||||
|
|
||||||
protected override void Startup()
|
|
||||||
{
|
|
||||||
base.Startup();
|
|
||||||
|
|
||||||
if (IsWeldedShut)
|
|
||||||
{
|
|
||||||
if (!CanWeldShut)
|
|
||||||
{
|
|
||||||
Logger.Warning("{0} prototype loaded with incompatible flags: 'welded' is true, but door cannot be welded.", Owner.Name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SetAppearance(DoorVisualState.Welded);
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateDoorElectronicsBoard();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnRemove()
|
|
||||||
{
|
|
||||||
_stateChangeCancelTokenSource?.Cancel();
|
|
||||||
_autoCloseCancelTokenSource?.Cancel();
|
|
||||||
|
|
||||||
base.OnRemove();
|
|
||||||
}
|
|
||||||
|
|
||||||
void IMapInit.MapInit()
|
|
||||||
{
|
|
||||||
if (_startOpen)
|
|
||||||
{
|
|
||||||
if (IsWeldedShut)
|
|
||||||
{
|
|
||||||
Logger.Warning("{0} prototype loaded with incompatible flags: 'welded' and 'startOpen' are both true.", Owner.Name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QuickOpen();
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateDoorElectronicsBoard();
|
|
||||||
}
|
|
||||||
|
|
||||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (_doorCheck != null && _doorCheck.BlockActivate(eventArgs))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (State == DoorState.Open)
|
|
||||||
{
|
|
||||||
TryClose(eventArgs.User);
|
|
||||||
}
|
|
||||||
else if (State == DoorState.Closed)
|
|
||||||
{
|
|
||||||
TryOpen(eventArgs.User);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void IStartCollide.CollideWith(Fixture ourFixture, Fixture otherFixture, in Manifold manifold)
|
|
||||||
{
|
|
||||||
if (State != DoorState.Closed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_bumpOpen)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disabled because it makes it suck hard to walk through double doors.
|
|
||||||
|
|
||||||
TryOpen(otherFixture.Body.Owner);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Opening
|
|
||||||
|
|
||||||
public void TryOpen(IEntity user)
|
|
||||||
{
|
|
||||||
if (CanOpenByEntity(user))
|
|
||||||
{
|
|
||||||
Open();
|
|
||||||
|
|
||||||
if (user.TryGetComponent(out HandsComponent? hands) && hands.Count == 0)
|
|
||||||
{
|
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), "/Audio/Effects/bang.ogg", Owner,
|
|
||||||
AudioParams.Default.WithVolume(-2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Deny();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CanOpenByEntity(IEntity user)
|
|
||||||
{
|
|
||||||
if(!CanOpenGeneric())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Owner.TryGetComponent(out AccessReader? access))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var doorSystem = EntitySystem.Get<ServerDoorSystem>();
|
|
||||||
var isAirlockExternal = HasAccessType("External");
|
|
||||||
|
|
||||||
return doorSystem.AccessType switch
|
|
||||||
{
|
|
||||||
ServerDoorSystem.AccessTypes.AllowAll => true,
|
|
||||||
ServerDoorSystem.AccessTypes.AllowAllIdExternal => isAirlockExternal || access.IsAllowed(user),
|
|
||||||
ServerDoorSystem.AccessTypes.AllowAllNoExternal => !isAirlockExternal,
|
|
||||||
_ => access.IsAllowed(user)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns whether a door has a certain access type. For example, maintenance doors will have access type
|
|
||||||
/// "Maintenance" in their AccessReader.
|
|
||||||
/// </summary>
|
|
||||||
private bool HasAccessType(string accessType)
|
|
||||||
{
|
|
||||||
if (Owner.TryGetComponent(out AccessReader? access))
|
|
||||||
{
|
|
||||||
return access.AccessLists.Any(list => list.Contains(accessType));
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if we can open at all, for anyone or anything. Will return false if inhibited by an IDoorCheck component.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Boolean describing whether this door can open.</returns>
|
|
||||||
public bool CanOpenGeneric()
|
|
||||||
{
|
|
||||||
// note the welded check -- CanCloseGeneric does not have this
|
|
||||||
if (IsWeldedShut)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(_doorCheck != null)
|
|
||||||
{
|
|
||||||
return _doorCheck.OpenCheck();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Opens the door. Does not check if this is possible.
|
|
||||||
/// </summary>
|
|
||||||
public void Open()
|
|
||||||
{
|
|
||||||
State = DoorState.Opening;
|
|
||||||
if (Occludes && Owner.TryGetComponent(out OccluderComponent? occluder))
|
|
||||||
{
|
|
||||||
occluder.Enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_stateChangeCancelTokenSource?.Cancel();
|
|
||||||
_stateChangeCancelTokenSource = new();
|
|
||||||
|
|
||||||
Owner.SpawnTimer(OpenTimeOne, async () =>
|
|
||||||
{
|
|
||||||
OnPartialOpen();
|
|
||||||
await Timer.Delay(OpenTimeTwo, _stateChangeCancelTokenSource.Token);
|
|
||||||
|
|
||||||
State = DoorState.Open;
|
|
||||||
}, _stateChangeCancelTokenSource.Token);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnPartialOpen()
|
|
||||||
{
|
|
||||||
if (Owner.TryGetComponent(out AirtightComponent? airtight))
|
|
||||||
{
|
|
||||||
airtight.AirBlocked = false;
|
|
||||||
}
|
|
||||||
base.OnPartialOpen();
|
|
||||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new AccessReaderChangeMessage(Owner, false));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void QuickOpen()
|
|
||||||
{
|
|
||||||
if (Occludes && Owner.TryGetComponent(out OccluderComponent? occluder))
|
|
||||||
{
|
|
||||||
occluder.Enabled = false;
|
|
||||||
}
|
|
||||||
OnPartialOpen();
|
|
||||||
State = DoorState.Open;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Closing
|
|
||||||
|
|
||||||
public void TryClose(IEntity user)
|
|
||||||
{
|
|
||||||
if (!CanCloseByEntity(user))
|
|
||||||
{
|
|
||||||
Deny();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CanCloseByEntity(IEntity user)
|
|
||||||
{
|
|
||||||
if (!CanCloseGeneric())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Owner.TryGetComponent(out AccessReader? access))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return access.IsAllowed(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if we can close at all, for anyone or anything. Will return false if inhibited by an IDoorCheck component or if we are colliding with somebody while our Safety is on.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Boolean describing whether this door can close.</returns>
|
|
||||||
public bool CanCloseGeneric()
|
|
||||||
{
|
|
||||||
if (_doorCheck != null && !_doorCheck.CloseCheck())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !IsSafetyColliding();
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool SafetyCheck()
|
|
||||||
{
|
|
||||||
return (_doorCheck != null && _doorCheck.SafetyCheck()) || _inhibitCrush;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Checks if we care about safety, and if so, if something is colliding with it; ignores the CanCollide of the door's PhysicsComponent.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if something is colliding with us and we shouldn't crush things, false otherwise.</returns>
|
|
||||||
private bool IsSafetyColliding()
|
|
||||||
{
|
|
||||||
var safety = SafetyCheck();
|
|
||||||
|
|
||||||
if (safety && Owner.TryGetComponent(out PhysicsComponent? physicsComponent))
|
|
||||||
{
|
|
||||||
var broadPhaseSystem = EntitySystem.Get<SharedBroadPhaseSystem>();
|
|
||||||
|
|
||||||
// Use this version so we can ignore the CanCollide being false
|
|
||||||
foreach(var e in broadPhaseSystem.GetCollidingEntities(physicsComponent.Owner.Transform.MapID, physicsComponent.GetWorldAABB()))
|
|
||||||
{
|
|
||||||
if ((physicsComponent.CollisionMask & e.CollisionLayer) != 0 && broadPhaseSystem.IntersectionPercent(physicsComponent, e) > 0.01f) return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Closes the door. Does not check if this is possible.
|
|
||||||
/// </summary>
|
|
||||||
public void Close()
|
|
||||||
{
|
|
||||||
State = DoorState.Closing;
|
|
||||||
|
|
||||||
// no more autoclose; we ARE closed
|
|
||||||
_autoCloseCancelTokenSource?.Cancel();
|
|
||||||
|
|
||||||
_stateChangeCancelTokenSource?.Cancel();
|
|
||||||
_stateChangeCancelTokenSource = new();
|
|
||||||
Owner.SpawnTimer(CloseTimeOne, async () =>
|
|
||||||
{
|
|
||||||
// if somebody walked into the door as it was closing, and we don't crush things
|
|
||||||
if (IsSafetyColliding())
|
|
||||||
{
|
|
||||||
Open();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnPartialClose();
|
|
||||||
await Timer.Delay(CloseTimeTwo, _stateChangeCancelTokenSource.Token);
|
|
||||||
|
|
||||||
if (Occludes && Owner.TryGetComponent(out OccluderComponent? occluder))
|
|
||||||
{
|
|
||||||
occluder.Enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
State = DoorState.Closed;
|
|
||||||
}, _stateChangeCancelTokenSource.Token);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnPartialClose()
|
|
||||||
{
|
|
||||||
base.OnPartialClose();
|
|
||||||
|
|
||||||
// if safety is off, crushes people inside of the door, temporarily turning off collisions with them while doing so.
|
|
||||||
var becomeairtight = SafetyCheck() || !TryCrush();
|
|
||||||
|
|
||||||
if (becomeairtight && Owner.TryGetComponent(out AirtightComponent? airtight))
|
|
||||||
{
|
|
||||||
airtight.AirBlocked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new AccessReaderChangeMessage(Owner, true));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Crushes everyone colliding with us by more than 10%.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if we crushed somebody, false if we did not.</returns>
|
|
||||||
private bool TryCrush()
|
|
||||||
{
|
|
||||||
if (PhysicsComponent == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var collidingentities = PhysicsComponent.GetCollidingEntities(Vector2.Zero, false);
|
|
||||||
|
|
||||||
if (!collidingentities.Any())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var doorAABB = PhysicsComponent.GetWorldAABB();
|
|
||||||
var hitsomebody = false;
|
|
||||||
|
|
||||||
// Crush
|
|
||||||
foreach (var e in collidingentities)
|
|
||||||
{
|
|
||||||
if (!e.Owner.TryGetComponent(out StunnableComponent? stun)
|
|
||||||
|| !e.Owner.TryGetComponent(out IDamageableComponent? damage))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var percentage = e.GetWorldAABB().IntersectPercentage(doorAABB);
|
|
||||||
|
|
||||||
if (percentage < 0.1f)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
hitsomebody = true;
|
|
||||||
CurrentlyCrushing.Add(e.Owner.Uid);
|
|
||||||
|
|
||||||
damage.ChangeDamage(_damageType, DoorCrushDamage, false, Owner);
|
|
||||||
stun.Paralyze(DoorStunTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we hit someone, open up after stun (opens right when stun ends)
|
|
||||||
if (hitsomebody)
|
|
||||||
{
|
|
||||||
Owner.SpawnTimer(TimeSpan.FromSeconds(DoorStunTime) - OpenTimeOne - OpenTimeTwo, Open);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public void Deny()
|
|
||||||
{
|
|
||||||
if (_doorCheck != null && !_doorCheck.DenyCheck())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (State == DoorState.Open || IsWeldedShut)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_stateChangeCancelTokenSource?.Cancel();
|
|
||||||
_stateChangeCancelTokenSource = new();
|
|
||||||
SetAppearance(DoorVisualState.Deny);
|
|
||||||
Owner.SpawnTimer(DenyTime, () =>
|
|
||||||
{
|
|
||||||
SetAppearance(DoorVisualState.Closed);
|
|
||||||
}, _stateChangeCancelTokenSource.Token);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stops the current auto-close timer if there is one. Starts a new one if this is appropriate (i.e. entity has an IDoorCheck component that allows auto-closing).
|
|
||||||
/// </summary>
|
|
||||||
public void RefreshAutoClose()
|
|
||||||
{
|
|
||||||
_autoCloseCancelTokenSource?.Cancel();
|
|
||||||
|
|
||||||
if (State != DoorState.Open || _doorCheck == null || !_doorCheck.AutoCloseCheck())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_autoCloseCancelTokenSource = new();
|
|
||||||
|
|
||||||
var realCloseTime = _doorCheck.GetCloseSpeed() ?? AutoCloseDelay;
|
|
||||||
|
|
||||||
Owner.SpawnRepeatingTimer(realCloseTime, async () =>
|
|
||||||
{
|
|
||||||
if (CanCloseGeneric())
|
|
||||||
{
|
|
||||||
// Close() cancels _autoCloseCancellationTokenSource, so we're fine.
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
}, _autoCloseCancelTokenSource.Token);
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if(!eventArgs.Using.TryGetComponent(out ToolComponent? tool))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for prying doors
|
|
||||||
if (tool.HasQuality(ToolQuality.Prying) && !IsWeldedShut)
|
|
||||||
{
|
|
||||||
var successfulPry = false;
|
|
||||||
|
|
||||||
if (_doorCheck != null)
|
|
||||||
{
|
|
||||||
_doorCheck.OnStartPry(eventArgs);
|
|
||||||
successfulPry = await tool.UseTool(eventArgs.User, Owner,
|
|
||||||
_doorCheck.GetPryTime() ?? 0.5f, ToolQuality.Prying, () => _doorCheck.CanPryCheck(eventArgs));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
successfulPry = await tool.UseTool(eventArgs.User, Owner, 0.5f, ToolQuality.Prying);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (successfulPry && !IsWeldedShut)
|
|
||||||
{
|
|
||||||
if (State == DoorState.Closed)
|
|
||||||
{
|
|
||||||
Open();
|
|
||||||
}
|
|
||||||
else if (State == DoorState.Open)
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// for welding doors
|
|
||||||
if (CanWeldShut && tool.Owner.TryGetComponent(out WelderComponent? welder) && welder.WelderLit)
|
|
||||||
{
|
|
||||||
if(!_beingWelded)
|
|
||||||
{
|
|
||||||
_beingWelded = true;
|
|
||||||
if(await welder.UseTool(eventArgs.User, Owner, 3f, ToolQuality.Welding, 3f, () => CanWeldShut))
|
|
||||||
{
|
|
||||||
// just in case
|
|
||||||
if (!CanWeldShut)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_beingWelded = false;
|
|
||||||
IsWeldedShut = !IsWeldedShut;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
_beingWelded = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_beingWelded = false;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates the corresponding door electronics board on the door.
|
|
||||||
/// This exists so when you deconstruct doors that were serialized with the map,
|
|
||||||
/// you can retrieve the door electronics board.
|
|
||||||
/// </summary>
|
|
||||||
private void CreateDoorElectronicsBoard()
|
|
||||||
{
|
|
||||||
// Ensure that the construction component is aware of the board container.
|
|
||||||
if (Owner.TryGetComponent(out ConstructionComponent? construction))
|
|
||||||
construction.AddContainer("board");
|
|
||||||
|
|
||||||
// We don't do anything if this is null or empty.
|
|
||||||
if (string.IsNullOrEmpty(_boardPrototype))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var container = Owner.EnsureContainer<Container>("board", out var existed);
|
|
||||||
|
|
||||||
return;
|
|
||||||
/* // TODO ShadowCommander: Re-enable when access is added to boards. Requires map update.
|
|
||||||
if (existed)
|
|
||||||
{
|
|
||||||
// We already contain a board. Note: We don't check if it's the right one!
|
|
||||||
if (container.ContainedEntities.Count != 0)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var board = Owner.EntityManager.SpawnEntity(_boardPrototype, Owner.Transform.Coordinates);
|
|
||||||
|
|
||||||
if(!container.Insert(board))
|
|
||||||
Logger.Warning($"Couldn't insert board {board} into door {Owner}!");
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
public override ComponentState GetComponentState(ICommonSession player)
|
|
||||||
{
|
|
||||||
return new DoorComponentState(State, StateChangeStartTime, CurrentlyCrushing, GameTiming.CurTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Content.Server.GameObjects.Components.Stack;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
|
||||||
using Content.Shared.Utility;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Medical
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public class HealingComponent : Component, IAfterInteract
|
|
||||||
{
|
|
||||||
public override string Name => "Healing";
|
|
||||||
|
|
||||||
[DataField("heal")] public Dictionary<DamageTypePrototype, int> Heal { get; private set; } = new();
|
|
||||||
|
|
||||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (eventArgs.Target == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!eventArgs.Target.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ActionBlockerSystem.CanInteract(eventArgs.User))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (eventArgs.User != eventArgs.Target &&
|
|
||||||
!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Owner.TryGetComponent(out StackComponent? stack) &&
|
|
||||||
!stack.Use(1))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var (type, amount) in Heal)
|
|
||||||
{
|
|
||||||
damageable.ChangeDamage(type, -amount, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,298 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Content.Server.GameObjects.Components.Mobs;
|
|
||||||
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
|
||||||
using Content.Server.GameObjects.EntitySystems;
|
|
||||||
using Content.Server.Utility;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Medical;
|
|
||||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
|
||||||
using Content.Shared.GameObjects.EntitySystems;
|
|
||||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
|
||||||
using Content.Shared.GameObjects.Verbs;
|
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Containers;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.Maths;
|
|
||||||
using Robust.Shared.Timing;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Medical
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
[ComponentReference(typeof(IActivate))]
|
|
||||||
[ComponentReference(typeof(SharedMedicalScannerComponent))]
|
|
||||||
public class MedicalScannerComponent : SharedMedicalScannerComponent, IActivate, IDestroyAct
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
||||||
|
|
||||||
private static readonly TimeSpan InternalOpenAttemptDelay = TimeSpan.FromSeconds(0.5);
|
|
||||||
private TimeSpan _lastInternalOpenAttempt;
|
|
||||||
|
|
||||||
private ContainerSlot _bodyContainer = default!;
|
|
||||||
private readonly Vector2 _ejectOffset = new(0f, 0f);
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
private bool Powered => !Owner.TryGetComponent(out PowerReceiverComponent? receiver) || receiver.Powered;
|
|
||||||
[ViewVariables]
|
|
||||||
private BoundUserInterface? UserInterface => Owner.GetUIOrNull(MedicalScannerUiKey.Key);
|
|
||||||
|
|
||||||
public bool IsOccupied => _bodyContainer.ContainedEntity != null;
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
if (UserInterface != null)
|
|
||||||
{
|
|
||||||
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
_bodyContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-bodyContainer");
|
|
||||||
|
|
||||||
// TODO: write this so that it checks for a change in power events and acts accordingly.
|
|
||||||
var newState = GetUserInterfaceState();
|
|
||||||
UserInterface?.SetState(newState);
|
|
||||||
|
|
||||||
UpdateUserInterface();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
|
||||||
{
|
|
||||||
base.HandleMessage(message, component);
|
|
||||||
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case RelayMovementEntityMessage msg:
|
|
||||||
{
|
|
||||||
if (ActionBlockerSystem.CanInteract(msg.Entity))
|
|
||||||
{
|
|
||||||
if (_gameTiming.CurTime <
|
|
||||||
_lastInternalOpenAttempt + InternalOpenAttemptDelay)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastInternalOpenAttempt = _gameTiming.CurTime;
|
|
||||||
EjectBody();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly MedicalScannerBoundUserInterfaceState EmptyUIState =
|
|
||||||
new(
|
|
||||||
null,
|
|
||||||
new Dictionary<DamageGroupPrototype, int>(),
|
|
||||||
new Dictionary<DamageTypePrototype, int>(),
|
|
||||||
false);
|
|
||||||
|
|
||||||
private MedicalScannerBoundUserInterfaceState GetUserInterfaceState()
|
|
||||||
{
|
|
||||||
var body = _bodyContainer.ContainedEntity;
|
|
||||||
if (body == null)
|
|
||||||
{
|
|
||||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
|
||||||
{
|
|
||||||
appearance?.SetData(MedicalScannerVisuals.Status, MedicalScannerStatus.Open);
|
|
||||||
}
|
|
||||||
|
|
||||||
return EmptyUIState;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!body.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
{
|
|
||||||
return EmptyUIState;
|
|
||||||
}
|
|
||||||
|
|
||||||
var classes = new Dictionary<DamageGroupPrototype, int>(damageable.DamageClasses);
|
|
||||||
var types = new Dictionary<DamageTypePrototype, int>(damageable.DamageTypes);
|
|
||||||
|
|
||||||
if (_bodyContainer.ContainedEntity?.Uid == null)
|
|
||||||
{
|
|
||||||
return new MedicalScannerBoundUserInterfaceState(body.Uid, classes, types, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
var cloningSystem = EntitySystem.Get<CloningSystem>();
|
|
||||||
var scanned = _bodyContainer.ContainedEntity.TryGetComponent(out MindComponent? mindComponent) &&
|
|
||||||
mindComponent.Mind != null &&
|
|
||||||
cloningSystem.HasDnaScan(mindComponent.Mind);
|
|
||||||
|
|
||||||
return new MedicalScannerBoundUserInterfaceState(body.Uid, classes, types, scanned);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateUserInterface()
|
|
||||||
{
|
|
||||||
if (!Powered)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newState = GetUserInterfaceState();
|
|
||||||
UserInterface?.SetState(newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
private MedicalScannerStatus GetStatusFromDamageState(IMobStateComponent state)
|
|
||||||
{
|
|
||||||
if (state.IsAlive())
|
|
||||||
{
|
|
||||||
return MedicalScannerStatus.Green;
|
|
||||||
}
|
|
||||||
else if (state.IsCritical())
|
|
||||||
{
|
|
||||||
return MedicalScannerStatus.Red;
|
|
||||||
}
|
|
||||||
else if (state.IsDead())
|
|
||||||
{
|
|
||||||
return MedicalScannerStatus.Death;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return MedicalScannerStatus.Yellow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private MedicalScannerStatus GetStatus()
|
|
||||||
{
|
|
||||||
if (Powered)
|
|
||||||
{
|
|
||||||
var body = _bodyContainer.ContainedEntity;
|
|
||||||
var state = body?.GetComponentOrNull<IMobStateComponent>();
|
|
||||||
|
|
||||||
return state == null
|
|
||||||
? MedicalScannerStatus.Open
|
|
||||||
: GetStatusFromDamageState(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return MedicalScannerStatus.Off;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAppearance()
|
|
||||||
{
|
|
||||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
|
||||||
{
|
|
||||||
appearance.SetData(MedicalScannerVisuals.Status, GetStatus());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void IActivate.Activate(ActivateEventArgs args)
|
|
||||||
{
|
|
||||||
if (!args.User.TryGetComponent(out IActorComponent? actor))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Powered)
|
|
||||||
return;
|
|
||||||
|
|
||||||
UserInterface?.Open(actor.playerSession);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Verb]
|
|
||||||
public sealed class EnterVerb : Verb<MedicalScannerComponent>
|
|
||||||
{
|
|
||||||
protected override void GetData(IEntity user, MedicalScannerComponent component, VerbData data)
|
|
||||||
{
|
|
||||||
if (!ActionBlockerSystem.CanInteract(user))
|
|
||||||
{
|
|
||||||
data.Visibility = VerbVisibility.Invisible;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.Text = Loc.GetString("Enter");
|
|
||||||
data.Visibility = component.IsOccupied ? VerbVisibility.Invisible : VerbVisibility.Visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Activate(IEntity user, MedicalScannerComponent component)
|
|
||||||
{
|
|
||||||
component.InsertBody(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Verb]
|
|
||||||
public sealed class EjectVerb : Verb<MedicalScannerComponent>
|
|
||||||
{
|
|
||||||
protected override void GetData(IEntity user, MedicalScannerComponent component, VerbData data)
|
|
||||||
{
|
|
||||||
if (!ActionBlockerSystem.CanInteract(user))
|
|
||||||
{
|
|
||||||
data.Visibility = VerbVisibility.Invisible;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.Text = Loc.GetString("Eject");
|
|
||||||
data.Visibility = component.IsOccupied ? VerbVisibility.Visible : VerbVisibility.Invisible;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Activate(IEntity user, MedicalScannerComponent component)
|
|
||||||
{
|
|
||||||
component.EjectBody();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void InsertBody(IEntity user)
|
|
||||||
{
|
|
||||||
_bodyContainer.Insert(user);
|
|
||||||
UpdateUserInterface();
|
|
||||||
UpdateAppearance();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EjectBody()
|
|
||||||
{
|
|
||||||
var containedEntity = _bodyContainer.ContainedEntity;
|
|
||||||
if (containedEntity == null) return;
|
|
||||||
_bodyContainer.Remove(containedEntity);
|
|
||||||
containedEntity.Transform.WorldPosition += _ejectOffset;
|
|
||||||
UpdateUserInterface();
|
|
||||||
UpdateAppearance();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(float frameTime)
|
|
||||||
{
|
|
||||||
UpdateUserInterface();
|
|
||||||
UpdateAppearance();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
|
||||||
{
|
|
||||||
if (obj.Message is not UiButtonPressedMessage message) return;
|
|
||||||
|
|
||||||
switch (message.Button)
|
|
||||||
{
|
|
||||||
case UiButton.ScanDNA:
|
|
||||||
if (_bodyContainer.ContainedEntity != null)
|
|
||||||
{
|
|
||||||
//TODO: Show a 'ERROR: Body is completely devoid of soul' if no Mind owns the entity.
|
|
||||||
var cloningSystem = EntitySystem.Get<CloningSystem>();
|
|
||||||
|
|
||||||
if (!_bodyContainer.ContainedEntity.TryGetComponent(out MindComponent? mind) || mind.Mind == null)
|
|
||||||
break;
|
|
||||||
|
|
||||||
cloningSystem.AddToDnaScans(mind.Mind);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool DragDropOn(DragDropEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
_bodyContainer.Insert(eventArgs.Dragged);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IDestroyAct.OnDestroy(DestructionEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
EjectBody();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,405 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Content.Server.Atmos;
|
|
||||||
using Content.Server.GameObjects.Components.Body.Behavior;
|
|
||||||
using Content.Server.GameObjects.Components.Body.Circulatory;
|
|
||||||
using Content.Server.GameObjects.Components.Mobs;
|
|
||||||
using Content.Server.GameObjects.Components.Temperature;
|
|
||||||
using Content.Shared.Alert;
|
|
||||||
using Content.Shared.Atmos;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Body;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
|
||||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
|
||||||
using Content.Shared.Interfaces;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Metabolism
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public class MetabolismComponent : Component
|
|
||||||
{
|
|
||||||
[ComponentDependency] private readonly IBody? _body = default!;
|
|
||||||
|
|
||||||
public override string Name => "Metabolism";
|
|
||||||
|
|
||||||
private float _accumulatedFrameTime;
|
|
||||||
|
|
||||||
private bool _isShivering;
|
|
||||||
private bool _isSweating;
|
|
||||||
|
|
||||||
[DataField("damageType", required: true)]
|
|
||||||
private readonly DamageTypePrototype _damageType = default!;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamage")] private int _suffocationDamage = 1;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamageRecovery")] private int _suffocationDamageRecovery = 1;
|
|
||||||
|
|
||||||
[ViewVariables] [DataField("needsGases")] public Dictionary<Gas, float> NeedsGases { get; set; } = new();
|
|
||||||
|
|
||||||
[ViewVariables] [DataField("producesGases")] public Dictionary<Gas, float> ProducesGases { get; set; } = new();
|
|
||||||
|
|
||||||
[ViewVariables] [DataField("deficitGases")] public Dictionary<Gas, float> DeficitGases { get; set; } = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Heat generated due to metabolism. It's generated via metabolism
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("metabolismHeat")]
|
|
||||||
public float MetabolismHeat { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Heat output via radiation.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("radiatedHeat")]
|
|
||||||
public float RadiatedHeat { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maximum heat regulated via sweat
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("sweatHeatRegulation")]
|
|
||||||
public float SweatHeatRegulation { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maximum heat regulated via shivering
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("shiveringHeatRegulation")]
|
|
||||||
public float ShiveringHeatRegulation { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Amount of heat regulation that represents thermal regulation processes not
|
|
||||||
/// explicitly coded.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("implicitHeatRegulation")]
|
|
||||||
public float ImplicitHeatRegulation { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Normal body temperature
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("normalBodyTemperature")]
|
|
||||||
public float NormalBodyTemperature { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Deviation from normal temperature for body to start thermal regulation
|
|
||||||
/// </summary>
|
|
||||||
[DataField("thermalRegulationTemperatureThreshold")]
|
|
||||||
public float ThermalRegulationTemperatureThreshold { get; private set; }
|
|
||||||
|
|
||||||
[ViewVariables] public bool Suffocating { get; private set; }
|
|
||||||
|
|
||||||
private Dictionary<Gas, float> NeedsAndDeficit(float frameTime)
|
|
||||||
{
|
|
||||||
var needs = new Dictionary<Gas, float>(NeedsGases);
|
|
||||||
foreach (var (gas, amount) in DeficitGases)
|
|
||||||
{
|
|
||||||
var newAmount = (needs.GetValueOrDefault(gas) + amount) * frameTime;
|
|
||||||
needs[gas] = newAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
return needs;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClampDeficit()
|
|
||||||
{
|
|
||||||
var deficitGases = new Dictionary<Gas, float>(DeficitGases);
|
|
||||||
|
|
||||||
foreach (var (gas, deficit) in deficitGases)
|
|
||||||
{
|
|
||||||
if (!NeedsGases.TryGetValue(gas, out var need))
|
|
||||||
{
|
|
||||||
DeficitGases.Remove(gas);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (deficit > need)
|
|
||||||
{
|
|
||||||
DeficitGases[gas] = need;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float SuffocatingPercentage()
|
|
||||||
{
|
|
||||||
var total = 0f;
|
|
||||||
|
|
||||||
foreach (var (gas, deficit) in DeficitGases)
|
|
||||||
{
|
|
||||||
var lack = 1f;
|
|
||||||
if (NeedsGases.TryGetValue(gas, out var needed))
|
|
||||||
{
|
|
||||||
lack = deficit / needed;
|
|
||||||
}
|
|
||||||
|
|
||||||
total += lack / Atmospherics.TotalNumberOfGases;
|
|
||||||
}
|
|
||||||
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float GasProducedMultiplier(Gas gas, float usedAverage)
|
|
||||||
{
|
|
||||||
if (!ProducesGases.TryGetValue(gas, out var produces))
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!NeedsGases.TryGetValue(gas, out var needs))
|
|
||||||
{
|
|
||||||
needs = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return needs * produces * usedAverage;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Dictionary<Gas, float> GasProduced(float usedAverage)
|
|
||||||
{
|
|
||||||
return ProducesGases.ToDictionary(pair => pair.Key, pair => GasProducedMultiplier(pair.Key, usedAverage));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProcessGases(float frameTime)
|
|
||||||
{
|
|
||||||
if (!Owner.TryGetComponent(out BloodstreamComponent? bloodstream))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_body == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var lungs = _body.GetMechanismBehaviors<LungBehavior>().ToArray();
|
|
||||||
|
|
||||||
var needs = NeedsAndDeficit(frameTime);
|
|
||||||
var used = 0f;
|
|
||||||
foreach (var (gas, amountNeeded) in needs)
|
|
||||||
{
|
|
||||||
var bloodstreamAmount = bloodstream.Air.GetMoles(gas);
|
|
||||||
var deficit = 0f;
|
|
||||||
|
|
||||||
if (bloodstreamAmount < amountNeeded)
|
|
||||||
{
|
|
||||||
if (!Owner.GetComponent<IMobStateComponent>().IsCritical())
|
|
||||||
{
|
|
||||||
// Panic inhale
|
|
||||||
foreach (var lung in lungs)
|
|
||||||
{
|
|
||||||
lung.Gasp();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bloodstreamAmount = bloodstream.Air.GetMoles(gas);
|
|
||||||
|
|
||||||
deficit = Math.Max(0, amountNeeded - bloodstreamAmount);
|
|
||||||
|
|
||||||
if (deficit > 0)
|
|
||||||
{
|
|
||||||
bloodstream.Air.SetMoles(gas, 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bloodstream.Air.AdjustMoles(gas, -amountNeeded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bloodstream.Air.AdjustMoles(gas, -amountNeeded);
|
|
||||||
}
|
|
||||||
|
|
||||||
DeficitGases[gas] = deficit;
|
|
||||||
|
|
||||||
|
|
||||||
used += (amountNeeded - deficit) / amountNeeded;
|
|
||||||
}
|
|
||||||
|
|
||||||
var produced = GasProduced(used / needs.Count);
|
|
||||||
|
|
||||||
foreach (var (gas, amountProduced) in produced)
|
|
||||||
{
|
|
||||||
bloodstream.Air.AdjustMoles(gas, amountProduced);
|
|
||||||
}
|
|
||||||
|
|
||||||
ClampDeficit();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Process thermal regulation
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="frameTime"></param>
|
|
||||||
private void ProcessThermalRegulation(float frameTime)
|
|
||||||
{
|
|
||||||
if (!Owner.TryGetComponent(out TemperatureComponent? temperatureComponent)) return;
|
|
||||||
temperatureComponent.ReceiveHeat(MetabolismHeat);
|
|
||||||
temperatureComponent.RemoveHeat(RadiatedHeat);
|
|
||||||
|
|
||||||
// implicit heat regulation
|
|
||||||
var tempDiff = Math.Abs(temperatureComponent.CurrentTemperature - NormalBodyTemperature);
|
|
||||||
var targetHeat = tempDiff * temperatureComponent.HeatCapacity;
|
|
||||||
if (temperatureComponent.CurrentTemperature > NormalBodyTemperature)
|
|
||||||
{
|
|
||||||
temperatureComponent.RemoveHeat(Math.Min(targetHeat, ImplicitHeatRegulation));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
temperatureComponent.ReceiveHeat(Math.Min(targetHeat, ImplicitHeatRegulation));
|
|
||||||
}
|
|
||||||
|
|
||||||
// recalc difference and target heat
|
|
||||||
tempDiff = Math.Abs(temperatureComponent.CurrentTemperature - NormalBodyTemperature);
|
|
||||||
targetHeat = tempDiff * temperatureComponent.HeatCapacity;
|
|
||||||
|
|
||||||
// if body temperature is not within comfortable, thermal regulation
|
|
||||||
// processes starts
|
|
||||||
if (tempDiff < ThermalRegulationTemperatureThreshold)
|
|
||||||
{
|
|
||||||
if (_isShivering || _isSweating)
|
|
||||||
{
|
|
||||||
Owner.PopupMessage(Loc.GetString("You feel comfortable"));
|
|
||||||
}
|
|
||||||
|
|
||||||
_isShivering = false;
|
|
||||||
_isSweating = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (temperatureComponent.CurrentTemperature > NormalBodyTemperature)
|
|
||||||
{
|
|
||||||
if (!ActionBlockerSystem.CanSweat(Owner)) return;
|
|
||||||
if (!_isSweating)
|
|
||||||
{
|
|
||||||
Owner.PopupMessage(Loc.GetString("You are sweating"));
|
|
||||||
_isSweating = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// creadth: sweating does not help in airless environment
|
|
||||||
if (Owner.Transform.Coordinates.TryGetTileAir(out _, Owner.EntityManager))
|
|
||||||
{
|
|
||||||
temperatureComponent.RemoveHeat(Math.Min(targetHeat, SweatHeatRegulation));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!ActionBlockerSystem.CanShiver(Owner)) return;
|
|
||||||
if (!_isShivering)
|
|
||||||
{
|
|
||||||
Owner.PopupMessage(Loc.GetString("You are shivering"));
|
|
||||||
_isShivering = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
temperatureComponent.ReceiveHeat(Math.Min(targetHeat, ShiveringHeatRegulation));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Processes gases in the bloodstream.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="frameTime">
|
|
||||||
/// The time since the last metabolism tick in seconds.
|
|
||||||
/// </param>
|
|
||||||
public void Update(float frameTime)
|
|
||||||
{
|
|
||||||
if (!Owner.TryGetComponent<IMobStateComponent>(out var state) ||
|
|
||||||
state.IsDead())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_accumulatedFrameTime += frameTime;
|
|
||||||
|
|
||||||
if (_accumulatedFrameTime < 1)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessGases(_accumulatedFrameTime);
|
|
||||||
ProcessThermalRegulation(_accumulatedFrameTime);
|
|
||||||
|
|
||||||
_accumulatedFrameTime -= 1;
|
|
||||||
|
|
||||||
if (SuffocatingPercentage() > 0)
|
|
||||||
{
|
|
||||||
TakeSuffocationDamage();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
StopSuffocation();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TakeSuffocationDamage()
|
|
||||||
{
|
|
||||||
Suffocating = true;
|
|
||||||
|
|
||||||
if (Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent))
|
|
||||||
{
|
|
||||||
alertsComponent.ShowAlert(AlertType.LowOxygen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
damageable.ChangeDamage(_damageType, _suffocationDamage, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void StopSuffocation()
|
|
||||||
{
|
|
||||||
Suffocating = false;
|
|
||||||
|
|
||||||
if (Owner.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
{
|
|
||||||
damageable.ChangeDamage(_damageType, -_suffocationDamageRecovery, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent))
|
|
||||||
{
|
|
||||||
alertsComponent.ClearAlert(AlertType.LowOxygen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public GasMixture Clean(BloodstreamComponent bloodstream)
|
|
||||||
{
|
|
||||||
var gasMixture = new GasMixture(bloodstream.Air.Volume)
|
|
||||||
{
|
|
||||||
Temperature = bloodstream.Air.Temperature
|
|
||||||
};
|
|
||||||
|
|
||||||
for (Gas gas = 0; gas < (Gas) Atmospherics.TotalNumberOfGases; gas++)
|
|
||||||
{
|
|
||||||
float amount;
|
|
||||||
var molesInBlood = bloodstream.Air.GetMoles(gas);
|
|
||||||
|
|
||||||
if (!NeedsGases.TryGetValue(gas, out var needed))
|
|
||||||
{
|
|
||||||
amount = molesInBlood;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var overflowThreshold = needed * 5f;
|
|
||||||
|
|
||||||
amount = molesInBlood > overflowThreshold
|
|
||||||
? molesInBlood - overflowThreshold
|
|
||||||
: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
gasMixture.AdjustMoles(gas, amount);
|
|
||||||
bloodstream.Air.AdjustMoles(gas, -amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
return gasMixture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Content.Server.GameObjects.Components.Weapon.Melee;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Mining;
|
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Random;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Mining
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public class AsteroidRockComponent : Component, IInteractUsing
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
|
||||||
|
|
||||||
public override string Name => "AsteroidRock";
|
|
||||||
private static readonly string[] SpriteStates = {"0", "1", "2", "3", "4"};
|
|
||||||
|
|
||||||
[DataField("damageType",required: true)]
|
|
||||||
private readonly DamageTypePrototype _damageType = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
|
||||||
{
|
|
||||||
appearance.SetData(AsteroidRockVisuals.State, _random.Pick(SpriteStates));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
var item = eventArgs.Using;
|
|
||||||
if (!item.TryGetComponent(out MeleeWeaponComponent? meleeWeaponComponent)) return false;
|
|
||||||
|
|
||||||
var DamageableComponent = Owner.GetComponent<IDamageableComponent>().ChangeDamage(
|
|
||||||
_damageType, meleeWeaponComponent.Damage, false, item);
|
|
||||||
|
|
||||||
if (!item.TryGetComponent(out PickaxeComponent? pickaxeComponent)) return true;
|
|
||||||
if (!string.IsNullOrWhiteSpace(pickaxeComponent.MiningSound))
|
|
||||||
{
|
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), pickaxeComponent.MiningSound, Owner, AudioParams.Default);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Content.Server.GameObjects.Components.Mobs;
|
|
||||||
using Content.Shared.Alert;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
|
||||||
using Content.Shared.GameObjects.Components.Movement;
|
|
||||||
using Content.Shared.GameObjects.Components.Nutrition;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Log;
|
|
||||||
using Robust.Shared.Players;
|
|
||||||
using Robust.Shared.Random;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Nutrition
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed class HungerComponent : SharedHungerComponent
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
|
||||||
|
|
||||||
[DataField("base_decay_rate")]
|
|
||||||
private float _baseDecayRate = 0.1f;
|
|
||||||
|
|
||||||
[DataField("damageType",required: true)]
|
|
||||||
private readonly DamageTypePrototype _damageType = default!;
|
|
||||||
|
|
||||||
private float _actualDecayRate;
|
|
||||||
private float _currentHunger;
|
|
||||||
private HungerThreshold _currentHungerThreshold;
|
|
||||||
private HungerThreshold _lastHungerThreshold;
|
|
||||||
private readonly Dictionary<HungerThreshold, float> _hungerThresholds = new()
|
|
||||||
{
|
|
||||||
{HungerThreshold.Overfed, 600.0f},
|
|
||||||
{HungerThreshold.Okay, 450.0f},
|
|
||||||
{HungerThreshold.Peckish, 300.0f},
|
|
||||||
{HungerThreshold.Starving, 150.0f},
|
|
||||||
{HungerThreshold.Dead, 0.0f},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Base stuff
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public float BaseDecayRate
|
|
||||||
{
|
|
||||||
get => _baseDecayRate;
|
|
||||||
set => _baseDecayRate = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public float ActualDecayRate
|
|
||||||
{
|
|
||||||
get => _actualDecayRate;
|
|
||||||
set => _actualDecayRate = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hunger
|
|
||||||
[ViewVariables(VVAccess.ReadOnly)]
|
|
||||||
public override HungerThreshold CurrentHungerThreshold => _currentHungerThreshold;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public float CurrentHunger
|
|
||||||
{
|
|
||||||
get => _currentHunger;
|
|
||||||
set => _currentHunger = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly)]
|
|
||||||
public Dictionary<HungerThreshold, float> HungerThresholds => _hungerThresholds;
|
|
||||||
|
|
||||||
public static readonly Dictionary<HungerThreshold, AlertType> HungerThresholdAlertTypes = new()
|
|
||||||
{
|
|
||||||
{ HungerThreshold.Overfed, AlertType.Overfed },
|
|
||||||
{ HungerThreshold.Peckish, AlertType.Peckish },
|
|
||||||
{ HungerThreshold.Starving, AlertType.Starving },
|
|
||||||
};
|
|
||||||
|
|
||||||
public void HungerThresholdEffect(bool force = false)
|
|
||||||
{
|
|
||||||
if (_currentHungerThreshold != _lastHungerThreshold || force)
|
|
||||||
{
|
|
||||||
// Revert slow speed if required
|
|
||||||
if (_lastHungerThreshold == HungerThreshold.Starving && _currentHungerThreshold != HungerThreshold.Dead &&
|
|
||||||
Owner.TryGetComponent(out MovementSpeedModifierComponent? movementSlowdownComponent))
|
|
||||||
{
|
|
||||||
movementSlowdownComponent.RefreshMovementSpeedModifiers();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update UI
|
|
||||||
Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent);
|
|
||||||
|
|
||||||
if (HungerThresholdAlertTypes.TryGetValue(_currentHungerThreshold, out var alertId))
|
|
||||||
{
|
|
||||||
alertsComponent?.ShowAlert(alertId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
alertsComponent?.ClearAlertCategory(AlertCategory.Hunger);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (_currentHungerThreshold)
|
|
||||||
{
|
|
||||||
case HungerThreshold.Overfed:
|
|
||||||
_lastHungerThreshold = _currentHungerThreshold;
|
|
||||||
_actualDecayRate = _baseDecayRate * 1.2f;
|
|
||||||
return;
|
|
||||||
|
|
||||||
case HungerThreshold.Okay:
|
|
||||||
_lastHungerThreshold = _currentHungerThreshold;
|
|
||||||
_actualDecayRate = _baseDecayRate;
|
|
||||||
return;
|
|
||||||
|
|
||||||
case HungerThreshold.Peckish:
|
|
||||||
// Same as okay except with UI icon saying eat soon.
|
|
||||||
_lastHungerThreshold = _currentHungerThreshold;
|
|
||||||
_actualDecayRate = _baseDecayRate * 0.8f;
|
|
||||||
return;
|
|
||||||
|
|
||||||
case HungerThreshold.Starving:
|
|
||||||
// TODO: If something else bumps this could cause mega-speed.
|
|
||||||
// If some form of speed update system if multiple things are touching it use that.
|
|
||||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent? movementSlowdownComponent1))
|
|
||||||
{
|
|
||||||
movementSlowdownComponent1.RefreshMovementSpeedModifiers();
|
|
||||||
}
|
|
||||||
_lastHungerThreshold = _currentHungerThreshold;
|
|
||||||
_actualDecayRate = _baseDecayRate * 0.6f;
|
|
||||||
return;
|
|
||||||
|
|
||||||
case HungerThreshold.Dead:
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
Logger.ErrorS("hunger", $"No hunger threshold found for {_currentHungerThreshold}");
|
|
||||||
throw new ArgumentOutOfRangeException($"No hunger threshold found for {_currentHungerThreshold}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Startup()
|
|
||||||
{
|
|
||||||
base.Startup();
|
|
||||||
// Similar functionality to SS13. Should also stagger people going to the chef.
|
|
||||||
_currentHunger = _random.Next(
|
|
||||||
(int)_hungerThresholds[HungerThreshold.Peckish] + 10,
|
|
||||||
(int)_hungerThresholds[HungerThreshold.Okay] - 1);
|
|
||||||
_currentHungerThreshold = GetHungerThreshold(_currentHunger);
|
|
||||||
_lastHungerThreshold = HungerThreshold.Okay; // TODO: Potentially change this -> Used Okay because no effects.
|
|
||||||
HungerThresholdEffect(true);
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public HungerThreshold GetHungerThreshold(float food)
|
|
||||||
{
|
|
||||||
HungerThreshold result = HungerThreshold.Dead;
|
|
||||||
var value = HungerThresholds[HungerThreshold.Overfed];
|
|
||||||
foreach (var threshold in _hungerThresholds)
|
|
||||||
{
|
|
||||||
if (threshold.Value <= value && threshold.Value >= food)
|
|
||||||
{
|
|
||||||
result = threshold.Key;
|
|
||||||
value = threshold.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateFood(float amount)
|
|
||||||
{
|
|
||||||
_currentHunger = Math.Min(_currentHunger + amount, HungerThresholds[HungerThreshold.Overfed]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: If mob is moving increase rate of consumption?
|
|
||||||
// Should use a multiplier as something like a disease would overwrite decay rate.
|
|
||||||
public void OnUpdate(float frametime)
|
|
||||||
{
|
|
||||||
_currentHunger -= frametime * ActualDecayRate;
|
|
||||||
UpdateCurrentThreshold();
|
|
||||||
|
|
||||||
if (_currentHungerThreshold != HungerThreshold.Dead)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!Owner.TryGetComponent(out IMobStateComponent? mobState))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!mobState.IsDead())
|
|
||||||
{
|
|
||||||
damageable.ChangeDamage(_damageType, 2, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateCurrentThreshold()
|
|
||||||
{
|
|
||||||
var calculatedHungerThreshold = GetHungerThreshold(_currentHunger);
|
|
||||||
// _trySound(calculatedThreshold);
|
|
||||||
if (calculatedHungerThreshold != _currentHungerThreshold)
|
|
||||||
{
|
|
||||||
_currentHungerThreshold = calculatedHungerThreshold;
|
|
||||||
HungerThresholdEffect();
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetFood()
|
|
||||||
{
|
|
||||||
_currentHunger = HungerThresholds[HungerThreshold.Okay];
|
|
||||||
UpdateCurrentThreshold();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override ComponentState GetComponentState(ICommonSession player)
|
|
||||||
{
|
|
||||||
return new HungerComponentState(_currentHungerThreshold);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Content.Server.GameObjects.Components.Mobs;
|
|
||||||
using Content.Shared.Alert;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
|
||||||
using Content.Shared.GameObjects.Components.Movement;
|
|
||||||
using Content.Shared.GameObjects.Components.Nutrition;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Log;
|
|
||||||
using Robust.Shared.Players;
|
|
||||||
using Robust.Shared.Random;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Nutrition
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed class ThirstComponent : SharedThirstComponent
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
|
||||||
|
|
||||||
[DataField("damageType", required: true)]
|
|
||||||
private readonly DamageTypePrototype _damageType = default!;
|
|
||||||
|
|
||||||
// Base stuff
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public float BaseDecayRate
|
|
||||||
{
|
|
||||||
get => _baseDecayRate;
|
|
||||||
set => _baseDecayRate = value;
|
|
||||||
}
|
|
||||||
[DataField("base_decay_rate")]
|
|
||||||
private float _baseDecayRate = 0.1f;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public float ActualDecayRate
|
|
||||||
{
|
|
||||||
get => _actualDecayRate;
|
|
||||||
set => _actualDecayRate = value;
|
|
||||||
}
|
|
||||||
private float _actualDecayRate;
|
|
||||||
|
|
||||||
// Thirst
|
|
||||||
[ViewVariables(VVAccess.ReadOnly)]
|
|
||||||
public override ThirstThreshold CurrentThirstThreshold => _currentThirstThreshold;
|
|
||||||
private ThirstThreshold _currentThirstThreshold;
|
|
||||||
|
|
||||||
private ThirstThreshold _lastThirstThreshold;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public float CurrentThirst
|
|
||||||
{
|
|
||||||
get => _currentThirst;
|
|
||||||
set => _currentThirst = value;
|
|
||||||
}
|
|
||||||
private float _currentThirst;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly)]
|
|
||||||
public Dictionary<ThirstThreshold, float> ThirstThresholds { get; } = new()
|
|
||||||
{
|
|
||||||
{ThirstThreshold.OverHydrated, 600.0f},
|
|
||||||
{ThirstThreshold.Okay, 450.0f},
|
|
||||||
{ThirstThreshold.Thirsty, 300.0f},
|
|
||||||
{ThirstThreshold.Parched, 150.0f},
|
|
||||||
{ThirstThreshold.Dead, 0.0f},
|
|
||||||
};
|
|
||||||
|
|
||||||
public static readonly Dictionary<ThirstThreshold, AlertType> ThirstThresholdAlertTypes = new()
|
|
||||||
{
|
|
||||||
{ThirstThreshold.OverHydrated, AlertType.Overhydrated},
|
|
||||||
{ThirstThreshold.Thirsty, AlertType.Thirsty},
|
|
||||||
{ThirstThreshold.Parched, AlertType.Parched},
|
|
||||||
};
|
|
||||||
|
|
||||||
public void ThirstThresholdEffect(bool force = false)
|
|
||||||
{
|
|
||||||
if (_currentThirstThreshold != _lastThirstThreshold || force)
|
|
||||||
{
|
|
||||||
// Revert slow speed if required
|
|
||||||
if (_lastThirstThreshold == ThirstThreshold.Parched && _currentThirstThreshold != ThirstThreshold.Dead &&
|
|
||||||
Owner.TryGetComponent(out MovementSpeedModifierComponent? movementSlowdownComponent))
|
|
||||||
{
|
|
||||||
movementSlowdownComponent.RefreshMovementSpeedModifiers();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update UI
|
|
||||||
Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent);
|
|
||||||
|
|
||||||
if (ThirstThresholdAlertTypes.TryGetValue(_currentThirstThreshold, out var alertId))
|
|
||||||
{
|
|
||||||
alertsComponent?.ShowAlert(alertId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
alertsComponent?.ClearAlertCategory(AlertCategory.Thirst);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (_currentThirstThreshold)
|
|
||||||
{
|
|
||||||
case ThirstThreshold.OverHydrated:
|
|
||||||
_lastThirstThreshold = _currentThirstThreshold;
|
|
||||||
_actualDecayRate = _baseDecayRate * 1.2f;
|
|
||||||
return;
|
|
||||||
|
|
||||||
case ThirstThreshold.Okay:
|
|
||||||
_lastThirstThreshold = _currentThirstThreshold;
|
|
||||||
_actualDecayRate = _baseDecayRate;
|
|
||||||
return;
|
|
||||||
|
|
||||||
case ThirstThreshold.Thirsty:
|
|
||||||
// Same as okay except with UI icon saying drink soon.
|
|
||||||
_lastThirstThreshold = _currentThirstThreshold;
|
|
||||||
_actualDecayRate = _baseDecayRate * 0.8f;
|
|
||||||
return;
|
|
||||||
|
|
||||||
case ThirstThreshold.Parched:
|
|
||||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent? movementSlowdownComponent1))
|
|
||||||
{
|
|
||||||
movementSlowdownComponent1.RefreshMovementSpeedModifiers();
|
|
||||||
}
|
|
||||||
_lastThirstThreshold = _currentThirstThreshold;
|
|
||||||
_actualDecayRate = _baseDecayRate * 0.6f;
|
|
||||||
return;
|
|
||||||
|
|
||||||
case ThirstThreshold.Dead:
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
Logger.ErrorS("thirst", $"No thirst threshold found for {_currentThirstThreshold}");
|
|
||||||
throw new ArgumentOutOfRangeException($"No thirst threshold found for {_currentThirstThreshold}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Startup()
|
|
||||||
{
|
|
||||||
base.Startup();
|
|
||||||
_currentThirst = _random.Next(
|
|
||||||
(int)ThirstThresholds[ThirstThreshold.Thirsty] + 10,
|
|
||||||
(int)ThirstThresholds[ThirstThreshold.Okay] - 1);
|
|
||||||
_currentThirstThreshold = GetThirstThreshold(_currentThirst);
|
|
||||||
_lastThirstThreshold = ThirstThreshold.Okay; // TODO: Potentially change this -> Used Okay because no effects.
|
|
||||||
// TODO: Check all thresholds make sense and throw if they don't.
|
|
||||||
ThirstThresholdEffect(true);
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ThirstThreshold GetThirstThreshold(float drink)
|
|
||||||
{
|
|
||||||
ThirstThreshold result = ThirstThreshold.Dead;
|
|
||||||
var value = ThirstThresholds[ThirstThreshold.OverHydrated];
|
|
||||||
foreach (var threshold in ThirstThresholds)
|
|
||||||
{
|
|
||||||
if (threshold.Value <= value && threshold.Value >= drink)
|
|
||||||
{
|
|
||||||
result = threshold.Key;
|
|
||||||
value = threshold.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateThirst(float amount)
|
|
||||||
{
|
|
||||||
_currentThirst = Math.Min(_currentThirst + amount, ThirstThresholds[ThirstThreshold.OverHydrated]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: If mob is moving increase rate of consumption.
|
|
||||||
// Should use a multiplier as something like a disease would overwrite decay rate.
|
|
||||||
public void OnUpdate(float frametime)
|
|
||||||
{
|
|
||||||
_currentThirst -= frametime * ActualDecayRate;
|
|
||||||
UpdateCurrentThreshold();
|
|
||||||
|
|
||||||
if (_currentThirstThreshold != ThirstThreshold.Dead)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!Owner.TryGetComponent(out IMobStateComponent? mobState))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!mobState.IsDead())
|
|
||||||
{
|
|
||||||
damageable.ChangeDamage(_damageType, 2, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateCurrentThreshold()
|
|
||||||
{
|
|
||||||
var calculatedThirstThreshold = GetThirstThreshold(_currentThirst);
|
|
||||||
// _trySound(calculatedThreshold);
|
|
||||||
if (calculatedThirstThreshold != _currentThirstThreshold)
|
|
||||||
{
|
|
||||||
_currentThirstThreshold = calculatedThirstThreshold;
|
|
||||||
ThirstThresholdEffect();
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetThirst()
|
|
||||||
{
|
|
||||||
_currentThirst = ThirstThresholds[ThirstThreshold.Okay];
|
|
||||||
UpdateCurrentThreshold();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override ComponentState GetComponentState(ICommonSession player)
|
|
||||||
{
|
|
||||||
return new ThirstComponentState(_currentThirstThreshold);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Content.Server.GameObjects.Components.GUI;
|
|
||||||
using Content.Server.GameObjects.Components.Items.Storage;
|
|
||||||
using Content.Server.GameObjects.Components.MachineLinking;
|
|
||||||
using Content.Server.GameObjects.Components.MachineLinking.Signals;
|
|
||||||
using Content.Server.GameObjects.Components.Mobs;
|
|
||||||
using Content.Server.GameObjects.Components.Observer;
|
|
||||||
using Content.Shared.Actions;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Power.ApcNetComponents.PowerReceiverUsers;
|
|
||||||
using Content.Shared.Interfaces;
|
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.Containers;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.Timing;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerReceiverUsers
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Component that represents a wall light. It has a light bulb that can be replaced when broken.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent]
|
|
||||||
public class PoweredLightComponent : Component, IInteractHand, IInteractUsing, IMapInit, ISignalReceiver<bool>, ISignalReceiver<ToggleSignal>, IGhostBooAffected
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
||||||
|
|
||||||
public override string Name => "PoweredLight";
|
|
||||||
|
|
||||||
private static readonly TimeSpan _thunkDelay = TimeSpan.FromSeconds(2);
|
|
||||||
// time to blink light when ghost made boo nearby
|
|
||||||
private static readonly TimeSpan ghostBlinkingTime = TimeSpan.FromSeconds(10);
|
|
||||||
private static readonly TimeSpan ghostBlinkingCooldown = TimeSpan.FromSeconds(60);
|
|
||||||
|
|
||||||
[ComponentDependency]
|
|
||||||
private readonly AppearanceComponent? _appearance;
|
|
||||||
|
|
||||||
private TimeSpan _lastThunk;
|
|
||||||
private TimeSpan? _lastGhostBlink;
|
|
||||||
|
|
||||||
[DataField("hasLampOnSpawn")]
|
|
||||||
private bool _hasLampOnSpawn = true;
|
|
||||||
|
|
||||||
[DataField("damageType", required: true)]
|
|
||||||
private readonly DamageTypePrototype _damageType = default!;
|
|
||||||
|
|
||||||
[ViewVariables] [DataField("on")]
|
|
||||||
private bool _on = true;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
private bool _currentLit;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
private bool _isBlinking;
|
|
||||||
|
|
||||||
[ViewVariables] [DataField("ignoreGhostsBoo")]
|
|
||||||
private bool _ignoreGhostsBoo;
|
|
||||||
|
|
||||||
[DataField("bulb")] private LightBulbType _bulbType = LightBulbType.Tube;
|
|
||||||
public LightBulbType BulbType => _bulbType;
|
|
||||||
|
|
||||||
[ViewVariables] private ContainerSlot _lightBulbContainer = default!;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public LightBulbComponent? LightBulb
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_lightBulbContainer.ContainedEntity == null) return null;
|
|
||||||
|
|
||||||
_lightBulbContainer.ContainedEntity.TryGetComponent(out LightBulbComponent? bulb);
|
|
||||||
|
|
||||||
return bulb;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO CONSTRUCTION make this use a construction graph
|
|
||||||
|
|
||||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
return InsertBulb(eventArgs.Using);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (!eventArgs.User.TryGetComponent(out IDamageableComponent? damageableComponent))
|
|
||||||
{
|
|
||||||
Eject();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if(eventArgs.User.TryGetComponent(out HeatResistanceComponent? heatResistanceComponent))
|
|
||||||
{
|
|
||||||
if(CanBurn(heatResistanceComponent.GetHeatResistance()))
|
|
||||||
{
|
|
||||||
Burn();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Eject();
|
|
||||||
return true;
|
|
||||||
|
|
||||||
bool CanBurn(int heatResistance)
|
|
||||||
{
|
|
||||||
if (LightBulb == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return _currentLit && heatResistance < LightBulb.BurningTemperature;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Burn()
|
|
||||||
{
|
|
||||||
Owner.PopupMessage(eventArgs.User, Loc.GetString("You burn your hand!"));
|
|
||||||
damageableComponent.ChangeDamage(_damageType, 20, false, Owner);
|
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), "/Audio/Effects/lightburn.ogg", Owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Eject()
|
|
||||||
{
|
|
||||||
EjectBulb(eventArgs.User);
|
|
||||||
UpdateLight();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Try to replace current bulb with a new one
|
|
||||||
/// </summary>
|
|
||||||
public bool ReplaceBulb(IEntity bulb)
|
|
||||||
{
|
|
||||||
EjectBulb();
|
|
||||||
return InsertBulb(bulb);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Inserts the bulb if possible.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if it could insert it, false if it couldn't.</returns>
|
|
||||||
private bool InsertBulb(IEntity bulb)
|
|
||||||
{
|
|
||||||
if (LightBulb != null) return false;
|
|
||||||
if (!bulb.TryGetComponent(out LightBulbComponent? lightBulb)) return false;
|
|
||||||
if (lightBulb.Type != _bulbType) return false;
|
|
||||||
|
|
||||||
var inserted = _lightBulbContainer.Insert(bulb);
|
|
||||||
|
|
||||||
lightBulb.OnLightBulbStateChange += UpdateLight;
|
|
||||||
lightBulb.OnLightColorChange += UpdateLight;
|
|
||||||
|
|
||||||
UpdateLight();
|
|
||||||
|
|
||||||
return inserted;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ejects the bulb to a mob's hand if possible.
|
|
||||||
/// </summary>
|
|
||||||
private void EjectBulb(IEntity? user = null)
|
|
||||||
{
|
|
||||||
if (LightBulb == null) return;
|
|
||||||
|
|
||||||
var bulb = LightBulb;
|
|
||||||
|
|
||||||
bulb.OnLightBulbStateChange -= UpdateLight;
|
|
||||||
bulb.OnLightColorChange -= UpdateLight;
|
|
||||||
|
|
||||||
if (!_lightBulbContainer.Remove(bulb.Owner)) return;
|
|
||||||
|
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
if (!user.TryGetComponent(out HandsComponent? hands)
|
|
||||||
|| !hands.PutInHand(bulb.Owner.GetComponent<ItemComponent>()))
|
|
||||||
bulb.Owner.Transform.Coordinates = user.Transform.Coordinates;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bulb.Owner.Transform.Coordinates = Owner.Transform.Coordinates;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// For attaching UpdateLight() to events.
|
|
||||||
/// </summary>
|
|
||||||
public void UpdateLight(object? sender, EventArgs? e)
|
|
||||||
{
|
|
||||||
UpdateLight();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the light's power drain, sprite and actual light state.
|
|
||||||
/// </summary>
|
|
||||||
public void UpdateLight()
|
|
||||||
{
|
|
||||||
var powerReceiver = Owner.GetComponent<PowerReceiverComponent>();
|
|
||||||
|
|
||||||
if (LightBulb == null) // No light bulb.
|
|
||||||
{
|
|
||||||
_currentLit = false;
|
|
||||||
powerReceiver.Load = 0;
|
|
||||||
_appearance?.SetData(PoweredLightVisuals.BulbState, PoweredLightState.Empty);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (LightBulb.State)
|
|
||||||
{
|
|
||||||
case LightBulbState.Normal:
|
|
||||||
if (powerReceiver.Powered && _on)
|
|
||||||
{
|
|
||||||
_currentLit = true;
|
|
||||||
powerReceiver.Load = LightBulb.PowerUse;
|
|
||||||
_appearance?.SetData(PoweredLightVisuals.BulbState, PoweredLightState.On);
|
|
||||||
_appearance?.SetData(PoweredLightVisuals.BulbColor, LightBulb.Color);
|
|
||||||
var time = _gameTiming.CurTime;
|
|
||||||
if (time > _lastThunk + _thunkDelay)
|
|
||||||
{
|
|
||||||
_lastThunk = time;
|
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), "/Audio/Machines/light_tube_on.ogg", Owner, AudioParams.Default.WithVolume(-10f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_currentLit = false;
|
|
||||||
_appearance?.SetData(PoweredLightVisuals.BulbState, PoweredLightState.Off);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LightBulbState.Broken:
|
|
||||||
_currentLit = false;
|
|
||||||
_appearance?.SetData(PoweredLightVisuals.BulbState, PoweredLightState.Broken);
|
|
||||||
break;
|
|
||||||
case LightBulbState.Burned:
|
|
||||||
_currentLit = false;
|
|
||||||
_appearance?.SetData(PoweredLightVisuals.BulbState, PoweredLightState.Burned);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
_lightBulbContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, "light_bulb");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
|
||||||
{
|
|
||||||
base.HandleMessage(message, component);
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case PowerChangedMessage:
|
|
||||||
UpdateLight();
|
|
||||||
break;
|
|
||||||
case DamageChangedMessage msg:
|
|
||||||
TryDestroyBulb(msg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TryDestroyBulb(DamageChangedMessage msg)
|
|
||||||
{
|
|
||||||
if (!msg.TookDamage)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (LightBulb == null || LightBulb.State == LightBulbState.Broken)
|
|
||||||
return;
|
|
||||||
|
|
||||||
LightBulb.State = LightBulbState.Broken;
|
|
||||||
LightBulb.PlayBreakSound();
|
|
||||||
UpdateLight();
|
|
||||||
}
|
|
||||||
|
|
||||||
void IMapInit.MapInit()
|
|
||||||
{
|
|
||||||
if (_hasLampOnSpawn)
|
|
||||||
{
|
|
||||||
var prototype = _bulbType switch
|
|
||||||
{
|
|
||||||
LightBulbType.Bulb => "LightBulb",
|
|
||||||
LightBulbType.Tube => "LightTube",
|
|
||||||
_ => throw new ArgumentOutOfRangeException()
|
|
||||||
};
|
|
||||||
|
|
||||||
var entity = Owner.EntityManager.SpawnEntity(prototype, Owner.Transform.Coordinates);
|
|
||||||
_lightBulbContainer.Insert(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
// need this to update visualizers
|
|
||||||
UpdateLight();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TriggerSignal(bool signal)
|
|
||||||
{
|
|
||||||
_on = signal;
|
|
||||||
UpdateLight();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TriggerSignal(ToggleSignal signal)
|
|
||||||
{
|
|
||||||
_on = !_on;
|
|
||||||
UpdateLight();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ToggleBlinkingLight(bool isNowBlinking)
|
|
||||||
{
|
|
||||||
if (_isBlinking == isNowBlinking)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_isBlinking = isNowBlinking;
|
|
||||||
_appearance?.SetData(PoweredLightVisuals.Blinking, _isBlinking);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool AffectedByGhostBoo(InstantActionEventArgs args)
|
|
||||||
{
|
|
||||||
if (_ignoreGhostsBoo)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// check cooldown first to prevent abuse
|
|
||||||
var time = _gameTiming.CurTime;
|
|
||||||
if (_lastGhostBlink != null)
|
|
||||||
{
|
|
||||||
if (time <= _lastGhostBlink + ghostBlinkingCooldown)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
_lastGhostBlink = time;
|
|
||||||
|
|
||||||
ToggleBlinkingLight(true);
|
|
||||||
Owner.SpawnTimer(ghostBlinkingTime, () => {
|
|
||||||
ToggleBlinkingLight(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.Physics;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Maths;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Timing;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Projectiles
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Lasers etc.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent]
|
|
||||||
public class HitscanComponent : Component
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
||||||
|
|
||||||
private TimeSpan _startTime;
|
|
||||||
private TimeSpan _deathTime;
|
|
||||||
|
|
||||||
[DataField("layers")] //todo WithFormat.Flags<CollisionLayer>()
|
|
||||||
private int _collisionMask = (int) CollisionGroup.Opaque;
|
|
||||||
[DataField("damage")]
|
|
||||||
private float _damage = 10f;
|
|
||||||
|
|
||||||
[DataField("damageType", required: true)]
|
|
||||||
private DamageTypePrototype _damageType { get; set; } = default!;
|
|
||||||
|
|
||||||
[DataField("muzzleFlash")]
|
|
||||||
private string? _muzzleFlash;
|
|
||||||
[DataField("impactFlash")]
|
|
||||||
private string? _impactFlash;
|
|
||||||
[DataField("soundHitWall")]
|
|
||||||
private string _soundHitWall = "/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg";
|
|
||||||
[DataField("spriteName")]
|
|
||||||
private string _spriteName = "Objects/Weapons/Guns/Projectiles/laser.png";
|
|
||||||
|
|
||||||
public override string Name => "Hitscan";
|
|
||||||
public DamageTypePrototype DamageType => _damageType;
|
|
||||||
public float MaxLength => 20.0f;
|
|
||||||
public CollisionGroup CollisionMask => (CollisionGroup) _collisionMask;
|
|
||||||
public float ColorModifier { get; set; } = 1.0f;
|
|
||||||
public float Damage
|
|
||||||
{
|
|
||||||
get => _damage;
|
|
||||||
set => _damage = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FireEffects(IEntity user, float distance, Angle angle, IEntity? hitEntity = null)
|
|
||||||
{
|
|
||||||
var effectSystem = EntitySystem.Get<EffectSystem>();
|
|
||||||
_startTime = _gameTiming.CurTime;
|
|
||||||
_deathTime = _startTime + TimeSpan.FromSeconds(1);
|
|
||||||
|
|
||||||
var afterEffect = AfterEffects(user.Transform.Coordinates, angle, distance, 1.0f);
|
|
||||||
if (afterEffect != null)
|
|
||||||
{
|
|
||||||
effectSystem.CreateParticle(afterEffect);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we're too close we'll stop the impact and muzzle / impact sprites from clipping
|
|
||||||
if (distance > 1.0f)
|
|
||||||
{
|
|
||||||
var impactEffect = ImpactFlash(distance, angle);
|
|
||||||
if (impactEffect != null)
|
|
||||||
{
|
|
||||||
effectSystem.CreateParticle(impactEffect);
|
|
||||||
}
|
|
||||||
|
|
||||||
var muzzleEffect = MuzzleFlash(user.Transform.Coordinates, angle);
|
|
||||||
if (muzzleEffect != null)
|
|
||||||
{
|
|
||||||
effectSystem.CreateParticle(muzzleEffect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hitEntity != null && _soundHitWall != null)
|
|
||||||
{
|
|
||||||
// TODO: No wall component so ?
|
|
||||||
var offset = angle.ToVec().Normalized / 2;
|
|
||||||
var coordinates = user.Transform.Coordinates.Offset(offset);
|
|
||||||
SoundSystem.Play(Filter.Pvs(coordinates), _soundHitWall, coordinates);
|
|
||||||
}
|
|
||||||
|
|
||||||
Owner.SpawnTimer((int) _deathTime.TotalMilliseconds, () =>
|
|
||||||
{
|
|
||||||
if (!Owner.Deleted)
|
|
||||||
{
|
|
||||||
Owner.Delete();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private EffectSystemMessage? MuzzleFlash(EntityCoordinates grid, Angle angle)
|
|
||||||
{
|
|
||||||
if (_muzzleFlash == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var offset = angle.ToVec().Normalized / 2;
|
|
||||||
|
|
||||||
var message = new EffectSystemMessage
|
|
||||||
{
|
|
||||||
EffectSprite = _muzzleFlash,
|
|
||||||
Born = _startTime,
|
|
||||||
DeathTime = _deathTime,
|
|
||||||
Coordinates = grid.Offset(offset),
|
|
||||||
//Rotated from east facing
|
|
||||||
Rotation = (float) angle.Theta,
|
|
||||||
Color = Vector4.Multiply(new Vector4(255, 255, 255, 750), ColorModifier),
|
|
||||||
ColorDelta = new Vector4(0, 0, 0, -1500f),
|
|
||||||
Shaded = false
|
|
||||||
};
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
private EffectSystemMessage AfterEffects(EntityCoordinates origin, Angle angle, float distance, float offset = 0.0f)
|
|
||||||
{
|
|
||||||
var midPointOffset = angle.ToVec() * distance / 2;
|
|
||||||
var message = new EffectSystemMessage
|
|
||||||
{
|
|
||||||
EffectSprite = _spriteName,
|
|
||||||
Born = _startTime,
|
|
||||||
DeathTime = _deathTime,
|
|
||||||
Size = new Vector2(distance - offset, 1f),
|
|
||||||
Coordinates = origin.Offset(midPointOffset),
|
|
||||||
//Rotated from east facing
|
|
||||||
Rotation = (float) angle.Theta,
|
|
||||||
Color = Vector4.Multiply(new Vector4(255, 255, 255, 750), ColorModifier),
|
|
||||||
ColorDelta = new Vector4(0, 0, 0, -1500f),
|
|
||||||
|
|
||||||
Shaded = false
|
|
||||||
};
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
private EffectSystemMessage? ImpactFlash(float distance, Angle angle)
|
|
||||||
{
|
|
||||||
if (_impactFlash == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var message = new EffectSystemMessage
|
|
||||||
{
|
|
||||||
EffectSprite = _impactFlash,
|
|
||||||
Born = _startTime,
|
|
||||||
DeathTime = _deathTime,
|
|
||||||
Coordinates = Owner.Transform.Coordinates.Offset(angle.ToVec() * distance),
|
|
||||||
//Rotated from east facing
|
|
||||||
Rotation = (float) angle.FlipPositive(),
|
|
||||||
Color = Vector4.Multiply(new Vector4(255, 255, 255, 750), ColorModifier),
|
|
||||||
ColorDelta = new Vector4(0, 0, 0, -1500f),
|
|
||||||
Shaded = false
|
|
||||||
};
|
|
||||||
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Content.Server.GameObjects.Components.Mobs;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Projectiles;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
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;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Projectiles
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public class ProjectileComponent : SharedProjectileComponent, IStartCollide
|
|
||||||
{
|
|
||||||
protected override EntityUid Shooter => _shooter;
|
|
||||||
|
|
||||||
private EntityUid _shooter = EntityUid.Invalid;
|
|
||||||
|
|
||||||
[DataField("damageTypes")]
|
|
||||||
private Dictionary<DamageTypePrototype, int> _damageTypes = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public Dictionary<DamageTypePrototype, int> Damages
|
|
||||||
{
|
|
||||||
get => _damageTypes;
|
|
||||||
set => _damageTypes = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[DataField("deleteOnCollide")]
|
|
||||||
public bool DeleteOnCollide { get; } = true;
|
|
||||||
|
|
||||||
// Get that juicy FPS hit sound
|
|
||||||
[DataField("soundHit")]
|
|
||||||
private string? _soundHit = default;
|
|
||||||
[DataField("soundHitSpecies")]
|
|
||||||
private string? _soundHitSpecies = default;
|
|
||||||
|
|
||||||
private bool _damagedEntity;
|
|
||||||
|
|
||||||
public float TimeLeft { get; set; } = 10;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Function that makes the collision of this object ignore a specific entity so we don't collide with ourselves
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="shooter"></param>
|
|
||||||
public void IgnoreEntity(IEntity shooter)
|
|
||||||
{
|
|
||||||
_shooter = shooter.Uid;
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies the damage when our projectile collides with its victim
|
|
||||||
/// </summary>
|
|
||||||
void IStartCollide.CollideWith(Fixture ourFixture, Fixture otherFixture, in Manifold manifold)
|
|
||||||
{
|
|
||||||
// This is so entities that shouldn't get a collision are ignored.
|
|
||||||
if (!otherFixture.Hard || _damagedEntity)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var coordinates = otherFixture.Body.Owner.Transform.Coordinates;
|
|
||||||
var playerFilter = Filter.Pvs(coordinates);
|
|
||||||
if (otherFixture.Body.Owner.TryGetComponent(out IDamageableComponent? damage) && _soundHitSpecies != null)
|
|
||||||
{
|
|
||||||
SoundSystem.Play(playerFilter, _soundHitSpecies, coordinates);
|
|
||||||
}
|
|
||||||
else if (_soundHit != null)
|
|
||||||
{
|
|
||||||
SoundSystem.Play(playerFilter, _soundHit, coordinates);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (damage != null)
|
|
||||||
{
|
|
||||||
Owner.EntityManager.TryGetEntity(_shooter, out var shooter);
|
|
||||||
|
|
||||||
foreach (var (damageType, amount) in _damageTypes)
|
|
||||||
{
|
|
||||||
damage.ChangeDamage(damageType, amount, false, shooter);
|
|
||||||
}
|
|
||||||
|
|
||||||
_damagedEntity = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Damaging it can delete it
|
|
||||||
if (!otherFixture.Body.Deleted && otherFixture.Body.Owner.TryGetComponent(out CameraRecoilComponent? recoilComponent))
|
|
||||||
{
|
|
||||||
var direction = ourFixture.Body.LinearVelocity.Normalized;
|
|
||||||
recoilComponent.Kick(direction);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(DeleteOnCollide)
|
|
||||||
Owner.Delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override ComponentState GetComponentState(ICommonSession player)
|
|
||||||
{
|
|
||||||
return new ProjectileComponentState(NetID!.Value, _shooter, IgnoreShooter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Content.Server.GameObjects.Components.Mobs;
|
|
||||||
using Content.Shared.Alert;
|
|
||||||
using Content.Shared.Atmos;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Physics;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Temperature
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Handles changing temperature,
|
|
||||||
/// informing others of the current temperature,
|
|
||||||
/// and taking fire damage from high temperature.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent]
|
|
||||||
public class TemperatureComponent : Component
|
|
||||||
{
|
|
||||||
|
|
||||||
[DataField("coldDamageType",required: true)]
|
|
||||||
private readonly DamageTypePrototype coldDamageType = default!;
|
|
||||||
|
|
||||||
[DataField("hotDamageType",required: true)]
|
|
||||||
private readonly DamageTypePrototype hotDamageType = default!;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override string Name => "Temperature";
|
|
||||||
[ViewVariables] public float CurrentTemperature { get => _currentTemperature; set => _currentTemperature = value; }
|
|
||||||
|
|
||||||
[ViewVariables] public float HeatDamageThreshold => _heatDamageThreshold;
|
|
||||||
[ViewVariables] public float ColdDamageThreshold => _coldDamageThreshold;
|
|
||||||
[ViewVariables] public float TempDamageCoefficient => _tempDamageCoefficient;
|
|
||||||
[ViewVariables] public float HeatCapacity {
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (Owner.TryGetComponent<IPhysBody>(out var physics))
|
|
||||||
{
|
|
||||||
return SpecificHeat * physics.Mass;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Atmospherics.MinimumHeatCapacity;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[ViewVariables] public float SpecificHeat => _specificHeat;
|
|
||||||
|
|
||||||
[DataField("heatDamageThreshold")]
|
|
||||||
private float _heatDamageThreshold = default;
|
|
||||||
[DataField("coldDamageThreshold")]
|
|
||||||
private float _coldDamageThreshold = default;
|
|
||||||
[DataField("tempDamageCoefficient")]
|
|
||||||
private float _tempDamageCoefficient = 1;
|
|
||||||
[DataField("currentTemperature")]
|
|
||||||
private float _currentTemperature = Atmospherics.T20C;
|
|
||||||
[DataField("specificHeat")]
|
|
||||||
private float _specificHeat = Atmospherics.MinimumHeatCapacity;
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
var tempDamage = 0;
|
|
||||||
DamageTypePrototype? damageType = null;
|
|
||||||
if (CurrentTemperature >= _heatDamageThreshold)
|
|
||||||
{
|
|
||||||
tempDamage = (int) Math.Floor((CurrentTemperature - _heatDamageThreshold) * _tempDamageCoefficient);
|
|
||||||
damageType = hotDamageType;
|
|
||||||
}
|
|
||||||
else if (CurrentTemperature <= _coldDamageThreshold)
|
|
||||||
{
|
|
||||||
tempDamage = (int) Math.Floor((_coldDamageThreshold - CurrentTemperature) * _tempDamageCoefficient);
|
|
||||||
damageType = coldDamageType;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Owner.TryGetComponent(out ServerAlertsComponent? status))
|
|
||||||
{
|
|
||||||
switch (CurrentTemperature)
|
|
||||||
{
|
|
||||||
// Cold strong.
|
|
||||||
case var t when t <= 260:
|
|
||||||
status.ShowAlert(AlertType.Cold, 3);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Cold mild.
|
|
||||||
case var t when t <= 280 && t > 260:
|
|
||||||
status.ShowAlert(AlertType.Cold, 2);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Cold weak.
|
|
||||||
case var t when t <= 292 && t > 280:
|
|
||||||
status.ShowAlert(AlertType.Cold, 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Safe.
|
|
||||||
case var t when t <= 327 && t > 292:
|
|
||||||
status.ClearAlertCategory(AlertCategory.Temperature);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Heat weak.
|
|
||||||
case var t when t <= 335 && t > 327:
|
|
||||||
status.ShowAlert(AlertType.Hot, 1);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Heat mild.
|
|
||||||
case var t when t <= 345 && t > 335:
|
|
||||||
status.ShowAlert(AlertType.Hot, 2);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Heat strong.
|
|
||||||
case var t when t > 345:
|
|
||||||
status.ShowAlert(AlertType.Hot, 3);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (damageType is null) return;
|
|
||||||
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? component)) return;
|
|
||||||
component.ChangeDamage(damageType, tempDamage, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Forcefully give heat to this component
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="heatAmount"></param>
|
|
||||||
public void ReceiveHeat(float heatAmount)
|
|
||||||
{
|
|
||||||
CurrentTemperature += heatAmount / HeatCapacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Forcefully remove heat from this component
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="heatAmount"></param>
|
|
||||||
public void RemoveHeat(float heatAmount)
|
|
||||||
{
|
|
||||||
CurrentTemperature -= heatAmount / HeatCapacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Content.Server.GameObjects.EntitySystems;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Items;
|
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
|
||||||
using Content.Shared.Physics;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Maths;
|
|
||||||
using Robust.Shared.Physics;
|
|
||||||
using Robust.Shared.Physics.Broadphase;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.Timing;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Weapon.Melee
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public class MeleeWeaponComponent : Component, IAttack, IHandSelected
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
||||||
|
|
||||||
public override string Name => "MeleeWeapon";
|
|
||||||
private TimeSpan _lastAttackTime;
|
|
||||||
private TimeSpan _cooldownEnd;
|
|
||||||
|
|
||||||
[DataField("hitSound")]
|
|
||||||
private string _hitSound = "/Audio/Weapons/genhit1.ogg";
|
|
||||||
|
|
||||||
[DataField("missSound")]
|
|
||||||
private string _missSound = "/Audio/Weapons/punchmiss.ogg";
|
|
||||||
|
|
||||||
[DataField("arcCooldownTime")]
|
|
||||||
public float ArcCooldownTime { get; private set; } = 1f;
|
|
||||||
|
|
||||||
[DataField("cooldownTime")]
|
|
||||||
public float CooldownTime { get; private set; } = 1f;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("clickArc")]
|
|
||||||
public string ClickArc { get; set; } = "punch";
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("arc")]
|
|
||||||
public string Arc { get; set; } = "default";
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("arcwidth")] public float ArcWidth { get; set; } = 90;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("range")]
|
|
||||||
public float Range { get; set; } = 1;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("damage")]
|
|
||||||
public int Damage { get; set; } = 5;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("damageType", required: true)]
|
|
||||||
public DamageTypePrototype DamageType { get; set; } = default!;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("clickAttackEffect")] public bool ClickAttackEffect { get; set; } = true;
|
|
||||||
|
|
||||||
protected virtual bool OnHitEntities(IReadOnlyList<IEntity> entities, AttackEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IAttack.WideAttack(AttackEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (!eventArgs.WideAttack) return true;
|
|
||||||
|
|
||||||
var curTime = _gameTiming.CurTime;
|
|
||||||
|
|
||||||
if (curTime < _cooldownEnd)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
var location = eventArgs.User.Transform.Coordinates;
|
|
||||||
var diff = eventArgs.ClickLocation.ToMapPos(Owner.EntityManager) - location.ToMapPos(Owner.EntityManager);
|
|
||||||
var angle = Angle.FromWorldVec(diff);
|
|
||||||
|
|
||||||
// This should really be improved. GetEntitiesInArc uses pos instead of bounding boxes.
|
|
||||||
var entities = ArcRayCast(eventArgs.User.Transform.WorldPosition, angle, eventArgs.User);
|
|
||||||
|
|
||||||
if (entities.Count != 0)
|
|
||||||
{
|
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), _hitSound, entities.First());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), _missSound, eventArgs.User);
|
|
||||||
}
|
|
||||||
|
|
||||||
var hitEntities = new List<IEntity>();
|
|
||||||
foreach (var entity in entities)
|
|
||||||
{
|
|
||||||
if (!entity.Transform.IsMapTransform || entity == eventArgs.User)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (entity.TryGetComponent(out IDamageableComponent? damageComponent))
|
|
||||||
{
|
|
||||||
damageComponent.ChangeDamage(DamageType, Damage, false, Owner);
|
|
||||||
hitEntities.Add(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SendMessage(new MeleeHitMessage(hitEntities));
|
|
||||||
|
|
||||||
if (!OnHitEntities(hitEntities, eventArgs)) return false;
|
|
||||||
|
|
||||||
if (Arc != null)
|
|
||||||
{
|
|
||||||
var sys = EntitySystem.Get<MeleeWeaponSystem>();
|
|
||||||
sys.SendAnimation(Arc, angle, eventArgs.User, Owner, hitEntities);
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastAttackTime = curTime;
|
|
||||||
_cooldownEnd = _lastAttackTime + TimeSpan.FromSeconds(ArcCooldownTime);
|
|
||||||
|
|
||||||
RefreshItemCooldown();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IAttack.ClickAttack(AttackEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (eventArgs.WideAttack) return false;
|
|
||||||
|
|
||||||
var curTime = _gameTiming.CurTime;
|
|
||||||
|
|
||||||
if (curTime < _cooldownEnd || !eventArgs.Target.IsValid())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
var target = eventArgs.TargetEntity;
|
|
||||||
|
|
||||||
var location = eventArgs.User.Transform.Coordinates;
|
|
||||||
var diff = eventArgs.ClickLocation.ToMapPos(Owner.EntityManager) - location.ToMapPos(Owner.EntityManager);
|
|
||||||
var angle = Angle.FromWorldVec(diff);
|
|
||||||
|
|
||||||
if (target != null)
|
|
||||||
{
|
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), _hitSound, target);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), _missSound, eventArgs.User);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target.TryGetComponent(out IDamageableComponent? damageComponent))
|
|
||||||
{
|
|
||||||
damageComponent.ChangeDamage(DamageType, Damage, false, Owner);
|
|
||||||
}
|
|
||||||
SendMessage(new MeleeHitMessage(new List<IEntity> { target }));
|
|
||||||
|
|
||||||
var targets = new[] { target };
|
|
||||||
|
|
||||||
if (!OnHitEntities(targets, eventArgs))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (ClickArc != null)
|
|
||||||
{
|
|
||||||
var sys = EntitySystem.Get<MeleeWeaponSystem>();
|
|
||||||
sys.SendAnimation(ClickArc, angle, eventArgs.User, Owner, targets, ClickAttackEffect, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastAttackTime = curTime;
|
|
||||||
_cooldownEnd = _lastAttackTime + TimeSpan.FromSeconds(CooldownTime);
|
|
||||||
|
|
||||||
RefreshItemCooldown();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private HashSet<IEntity> ArcRayCast(Vector2 position, Angle angle, IEntity ignore)
|
|
||||||
{
|
|
||||||
var widthRad = Angle.FromDegrees(ArcWidth);
|
|
||||||
var increments = 1 + 35 * (int) Math.Ceiling(widthRad / (2 * Math.PI));
|
|
||||||
var increment = widthRad / increments;
|
|
||||||
var baseAngle = angle - widthRad / 2;
|
|
||||||
|
|
||||||
var resSet = new HashSet<IEntity>();
|
|
||||||
|
|
||||||
var mapId = Owner.Transform.MapID;
|
|
||||||
for (var i = 0; i < increments; i++)
|
|
||||||
{
|
|
||||||
var castAngle = new Angle(baseAngle + increment * i);
|
|
||||||
var res = EntitySystem.Get<SharedBroadPhaseSystem>().IntersectRay(mapId,
|
|
||||||
new CollisionRay(position, castAngle.ToWorldVec(),
|
|
||||||
(int) (CollisionGroup.Impassable | CollisionGroup.MobImpassable)), Range, ignore).ToList();
|
|
||||||
|
|
||||||
if (res.Count != 0)
|
|
||||||
{
|
|
||||||
resSet.Add(res[0].HitEntity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return resSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IHandSelected.HandSelected(HandSelectedEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
var curTime = _gameTiming.CurTime;
|
|
||||||
var cool = TimeSpan.FromSeconds(CooldownTime * 0.5f);
|
|
||||||
|
|
||||||
if (curTime < _cooldownEnd)
|
|
||||||
{
|
|
||||||
if (_cooldownEnd - curTime < cool)
|
|
||||||
{
|
|
||||||
_lastAttackTime = curTime;
|
|
||||||
_cooldownEnd += cool;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_lastAttackTime = curTime;
|
|
||||||
_cooldownEnd = curTime + cool;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshItemCooldown();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RefreshItemCooldown()
|
|
||||||
{
|
|
||||||
if (Owner.TryGetComponent(out ItemCooldownComponent? cooldown))
|
|
||||||
{
|
|
||||||
cooldown.CooldownStart = _lastAttackTime;
|
|
||||||
cooldown.CooldownEnd = _cooldownEnd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MeleeHitMessage : ComponentMessage
|
|
||||||
{
|
|
||||||
public readonly List<IEntity> HitEntities;
|
|
||||||
|
|
||||||
public MeleeHitMessage(List<IEntity> hitEntities)
|
|
||||||
{
|
|
||||||
HitEntities = hitEntities;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,322 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Content.Server.GameObjects.Components.GUI;
|
|
||||||
using Content.Server.GameObjects.Components.Items.Storage;
|
|
||||||
using Content.Server.GameObjects.Components.Power;
|
|
||||||
using Content.Server.GameObjects.Components.Projectiles;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects;
|
|
||||||
using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels;
|
|
||||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
|
||||||
using Content.Shared.GameObjects.Verbs;
|
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.Containers;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Players;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed class ServerBatteryBarrelComponent : ServerRangedBarrelComponent
|
|
||||||
{
|
|
||||||
public override string Name => "BatteryBarrel";
|
|
||||||
public override uint? NetID => ContentNetIDs.BATTERY_BARREL;
|
|
||||||
|
|
||||||
// The minimum change we need before we can fire
|
|
||||||
[DataField("lowerChargeLimit")]
|
|
||||||
[ViewVariables] private float _lowerChargeLimit = 10;
|
|
||||||
[DataField("fireCost")]
|
|
||||||
[ViewVariables] private int _baseFireCost = 300;
|
|
||||||
// What gets fired
|
|
||||||
[DataField("ammoPrototype")]
|
|
||||||
[ViewVariables] private string? _ammoPrototype;
|
|
||||||
|
|
||||||
[ViewVariables] public IEntity? PowerCellEntity => _powerCellContainer.ContainedEntity;
|
|
||||||
public BatteryComponent? PowerCell => _powerCellContainer.ContainedEntity?.GetComponentOrNull<BatteryComponent>();
|
|
||||||
private ContainerSlot _powerCellContainer = default!;
|
|
||||||
private ContainerSlot _ammoContainer = default!;
|
|
||||||
[DataField("powerCellPrototype")]
|
|
||||||
private string? _powerCellPrototype = default;
|
|
||||||
[DataField("powerCellRemovable")]
|
|
||||||
[ViewVariables] private bool _powerCellRemovable = default;
|
|
||||||
|
|
||||||
public override int ShotsLeft
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var powerCell = _powerCellContainer.ContainedEntity;
|
|
||||||
|
|
||||||
if (powerCell == null)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int) Math.Ceiling(powerCell.GetComponent<BatteryComponent>().CurrentCharge / _baseFireCost);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int Capacity
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var powerCell = _powerCellContainer.ContainedEntity;
|
|
||||||
|
|
||||||
if (powerCell == null)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (int) Math.Ceiling((float) (powerCell.GetComponent<BatteryComponent>().MaxCharge / _baseFireCost));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private AppearanceComponent? _appearanceComponent;
|
|
||||||
|
|
||||||
// Sounds
|
|
||||||
[DataField("soundPowerCellInsert")]
|
|
||||||
private string? _soundPowerCellInsert = default;
|
|
||||||
[DataField("soundPowerCellEject")]
|
|
||||||
private string? _soundPowerCellEject = default;
|
|
||||||
|
|
||||||
public override ComponentState GetComponentState(ICommonSession player)
|
|
||||||
{
|
|
||||||
(int, int)? count = (ShotsLeft, Capacity);
|
|
||||||
|
|
||||||
return new BatteryBarrelComponentState(
|
|
||||||
FireRateSelector,
|
|
||||||
count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
_powerCellContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-powercell-container", out var existing);
|
|
||||||
if (!existing && _powerCellPrototype != null)
|
|
||||||
{
|
|
||||||
var powerCellEntity = Owner.EntityManager.SpawnEntity(_powerCellPrototype, Owner.Transform.Coordinates);
|
|
||||||
_powerCellContainer.Insert(powerCellEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_ammoPrototype != null)
|
|
||||||
{
|
|
||||||
_ammoContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-ammo-container");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Owner.TryGetComponent(out AppearanceComponent? appearanceComponent))
|
|
||||||
{
|
|
||||||
_appearanceComponent = appearanceComponent;
|
|
||||||
}
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Startup()
|
|
||||||
{
|
|
||||||
UpdateAppearance();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateAppearance()
|
|
||||||
{
|
|
||||||
_appearanceComponent?.SetData(MagazineBarrelVisuals.MagLoaded, _powerCellContainer.ContainedEntity != null);
|
|
||||||
_appearanceComponent?.SetData(AmmoVisuals.AmmoCount, ShotsLeft);
|
|
||||||
_appearanceComponent?.SetData(AmmoVisuals.AmmoMax, Capacity);
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IEntity PeekAmmo()
|
|
||||||
{
|
|
||||||
// Spawn a dummy entity because it's easier to work with I guess
|
|
||||||
// This will get re-used for the projectile
|
|
||||||
var ammo = _ammoContainer.ContainedEntity;
|
|
||||||
if (ammo == null)
|
|
||||||
{
|
|
||||||
ammo = Owner.EntityManager.SpawnEntity(_ammoPrototype, Owner.Transform.Coordinates);
|
|
||||||
_ammoContainer.Insert(ammo);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ammo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IEntity? TakeProjectile(EntityCoordinates spawnAt)
|
|
||||||
{
|
|
||||||
var powerCellEntity = _powerCellContainer.ContainedEntity;
|
|
||||||
|
|
||||||
if (powerCellEntity == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var capacitor = powerCellEntity.GetComponent<BatteryComponent>();
|
|
||||||
if (capacitor.CurrentCharge < _lowerChargeLimit)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can fire confirmed
|
|
||||||
// Multiply the entity's damage / whatever by the percentage of charge the shot has.
|
|
||||||
IEntity entity;
|
|
||||||
var chargeChange = Math.Min(capacitor.CurrentCharge, _baseFireCost);
|
|
||||||
if (capacitor.UseCharge(chargeChange) < _lowerChargeLimit)
|
|
||||||
{
|
|
||||||
// Handling of funny exploding cells.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
var energyRatio = chargeChange / _baseFireCost;
|
|
||||||
|
|
||||||
if (_ammoContainer.ContainedEntity != null)
|
|
||||||
{
|
|
||||||
entity = _ammoContainer.ContainedEntity;
|
|
||||||
_ammoContainer.Remove(entity);
|
|
||||||
entity.Transform.Coordinates = spawnAt;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
entity = Owner.EntityManager.SpawnEntity(_ammoPrototype, spawnAt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity.TryGetComponent(out ProjectileComponent? projectileComponent))
|
|
||||||
{
|
|
||||||
if (energyRatio < 1.0)
|
|
||||||
{
|
|
||||||
var newDamages = new Dictionary<DamageTypePrototype, int>(projectileComponent.Damages.Count);
|
|
||||||
foreach (var (damageType, damage) in projectileComponent.Damages)
|
|
||||||
{
|
|
||||||
newDamages.Add(damageType, (int) (damage * energyRatio));
|
|
||||||
}
|
|
||||||
|
|
||||||
projectileComponent.Damages = newDamages;
|
|
||||||
}
|
|
||||||
} else if (entity.TryGetComponent(out HitscanComponent? hitscanComponent))
|
|
||||||
{
|
|
||||||
hitscanComponent.Damage *= energyRatio;
|
|
||||||
hitscanComponent.ColorModifier = energyRatio;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Ammo doesn't have hitscan or projectile?");
|
|
||||||
}
|
|
||||||
|
|
||||||
Dirty();
|
|
||||||
UpdateAppearance();
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryInsertPowerCell(IEntity entity)
|
|
||||||
{
|
|
||||||
if (_powerCellContainer.ContainedEntity != null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!entity.HasComponent<BatteryComponent>())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_soundPowerCellInsert != null)
|
|
||||||
{
|
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), _soundPowerCellInsert, Owner.Transform.Coordinates, AudioParams.Default.WithVolume(-2));
|
|
||||||
}
|
|
||||||
|
|
||||||
_powerCellContainer.Insert(entity);
|
|
||||||
|
|
||||||
Dirty();
|
|
||||||
UpdateAppearance();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool UseEntity(UseEntityEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (!_powerCellRemovable)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PowerCellEntity == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TryEjectCell(eventArgs.User);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryEjectCell(IEntity user)
|
|
||||||
{
|
|
||||||
if (PowerCell == null || !_powerCellRemovable)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user.TryGetComponent(out HandsComponent? hands))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cell = PowerCell;
|
|
||||||
if (!_powerCellContainer.Remove(cell.Owner))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dirty();
|
|
||||||
UpdateAppearance();
|
|
||||||
|
|
||||||
if (!hands.PutInHand(cell.Owner.GetComponent<ItemComponent>()))
|
|
||||||
{
|
|
||||||
cell.Owner.Transform.Coordinates = user.Transform.Coordinates;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_soundPowerCellEject != null)
|
|
||||||
{
|
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), _soundPowerCellEject, Owner.Transform.Coordinates, AudioParams.Default.WithVolume(-2));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (!eventArgs.Using.HasComponent<BatteryComponent>())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TryInsertPowerCell(eventArgs.Using);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Verb]
|
|
||||||
public sealed class EjectCellVerb : Verb<ServerBatteryBarrelComponent>
|
|
||||||
{
|
|
||||||
protected override void GetData(IEntity user, ServerBatteryBarrelComponent component, VerbData data)
|
|
||||||
{
|
|
||||||
if (!ActionBlockerSystem.CanInteract(user) || !component._powerCellRemovable)
|
|
||||||
{
|
|
||||||
data.Visibility = VerbVisibility.Invisible;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component.PowerCell == null)
|
|
||||||
{
|
|
||||||
data.Text = Loc.GetString("No cell");
|
|
||||||
data.Visibility = VerbVisibility.Disabled;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data.Text = Loc.GetString("Eject cell");
|
|
||||||
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Activate(IEntity user, ServerBatteryBarrelComponent component)
|
|
||||||
{
|
|
||||||
component.TryEjectCell(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using Content.Server.GameObjects.Components.GUI;
|
|
||||||
using Content.Server.GameObjects.Components.Mobs;
|
|
||||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Barrels;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Weapons.Ranged;
|
|
||||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
|
||||||
using Content.Shared.Interfaces;
|
|
||||||
using Content.Shared.Interfaces.GameObjects.Components;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.Log;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Maths;
|
|
||||||
using Robust.Shared.Network;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Players;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.Timing;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
using Content.Server.Atmos;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.Components.Weapon.Ranged
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed class ServerRangedWeaponComponent : SharedRangedWeaponComponent, IHandSelected
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
||||||
|
|
||||||
private TimeSpan _lastFireTime;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("clumsyCheck")]
|
|
||||||
public bool ClumsyCheck { get; set; } = true;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("clumsyExplodeChance")]
|
|
||||||
public float ClumsyExplodeChance { get; set; } = 0.5f;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("canHotspot")]
|
|
||||||
private bool _canHotspot = true;
|
|
||||||
|
|
||||||
public Func<bool>? WeaponCanFireHandler;
|
|
||||||
public Func<IEntity, bool>? UserCanFireHandler;
|
|
||||||
public Action<IEntity, Vector2>? FireHandler;
|
|
||||||
|
|
||||||
public ServerRangedBarrelComponent? Barrel
|
|
||||||
{
|
|
||||||
get => _barrel;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_barrel != null && value != null)
|
|
||||||
{
|
|
||||||
Logger.Error("Tried setting Barrel on RangedWeapon that already has one");
|
|
||||||
throw new InvalidOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
_barrel = value;
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private ServerRangedBarrelComponent? _barrel;
|
|
||||||
|
|
||||||
private FireRateSelector FireRateSelector => _barrel?.FireRateSelector ?? FireRateSelector.Safety;
|
|
||||||
|
|
||||||
private bool WeaponCanFire()
|
|
||||||
{
|
|
||||||
return WeaponCanFireHandler == null || WeaponCanFireHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool UserCanFire(IEntity user)
|
|
||||||
{
|
|
||||||
return (UserCanFireHandler == null || UserCanFireHandler(user)) && ActionBlockerSystem.CanAttack(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession? session = null)
|
|
||||||
{
|
|
||||||
base.HandleNetworkMessage(message, channel, session);
|
|
||||||
|
|
||||||
if (session == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(session));
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case FirePosComponentMessage msg:
|
|
||||||
var user = session.AttachedEntity;
|
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg.TargetGrid != GridId.Invalid)
|
|
||||||
{
|
|
||||||
// grid pos
|
|
||||||
if (!_mapManager.TryGetGrid(msg.TargetGrid, out var grid))
|
|
||||||
{
|
|
||||||
// Client sent us a message with an invalid grid.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var targetPos = grid.LocalToWorld(msg.TargetPosition);
|
|
||||||
TryFire(user, targetPos);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// map pos
|
|
||||||
TryFire(user, msg.TargetPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override ComponentState GetComponentState(ICommonSession player)
|
|
||||||
{
|
|
||||||
return new RangedWeaponComponentState(FireRateSelector);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to fire a round of ammo out of the weapon.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="user">Entity that is operating the weapon, usually the player.</param>
|
|
||||||
/// <param name="targetPos">Target position on the map to shoot at.</param>
|
|
||||||
private void TryFire(IEntity user, Vector2 targetPos)
|
|
||||||
{
|
|
||||||
if (!user.TryGetComponent(out HandsComponent? hands) || hands.GetActiveHand?.Owner != Owner)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!user.TryGetComponent(out CombatModeComponent? combat) || !combat.IsInCombatMode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!UserCanFire(user) || !WeaponCanFire())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var curTime = _gameTiming.CurTime;
|
|
||||||
var span = curTime - _lastFireTime;
|
|
||||||
if (span.TotalSeconds < 1 / _barrel?.FireRate)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastFireTime = curTime;
|
|
||||||
|
|
||||||
if (ClumsyCheck && ClumsyComponent.TryRollClumsy(user, ClumsyExplodeChance))
|
|
||||||
{
|
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), "/Audio/Items/bikehorn.ogg",
|
|
||||||
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
|
||||||
|
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), "/Audio/Weapons/Guns/Gunshots/bang.ogg",
|
|
||||||
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
|
||||||
|
|
||||||
if (user.TryGetComponent(out IDamageableComponent? health))
|
|
||||||
{
|
|
||||||
health.ChangeDamage(health.GetDamageType("Blunt"), 10, false, user);
|
|
||||||
health.ChangeDamage(health.GetDamageType("Heat"), 5, false, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (user.TryGetComponent(out StunnableComponent? stun))
|
|
||||||
{
|
|
||||||
stun.Paralyze(3f);
|
|
||||||
}
|
|
||||||
|
|
||||||
user.PopupMessage(Loc.GetString("The gun blows up in your face!"));
|
|
||||||
|
|
||||||
Owner.Delete();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_canHotspot && user.Transform.Coordinates.TryGetTileAtmosphere(out var tile))
|
|
||||||
{
|
|
||||||
tile.HotspotExpose(700, 50);
|
|
||||||
}
|
|
||||||
FireHandler?.Invoke(user, targetPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Probably a better way to do this.
|
|
||||||
void IHandSelected.HandSelected(HandSelectedEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Content.Server.GameObjects.Components.Atmos;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameTicking;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
|
|
||||||
namespace Content.Server.GameObjects.EntitySystems
|
|
||||||
{
|
|
||||||
[UsedImplicitly]
|
|
||||||
public class GodmodeSystem : EntitySystem, IResettingEntitySystem
|
|
||||||
{
|
|
||||||
private readonly Dictionary<IEntity, OldEntityInformation> _entities = new();
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_entities.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool EnableGodmode(IEntity entity)
|
|
||||||
{
|
|
||||||
if (_entities.ContainsKey(entity))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_entities[entity] = new OldEntityInformation(entity);
|
|
||||||
|
|
||||||
if (entity.TryGetComponent(out MovedByPressureComponent? moved))
|
|
||||||
{
|
|
||||||
moved.Enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
{
|
|
||||||
//TODO use resistanceSet instead?
|
|
||||||
damageable.Godmode = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasGodmode(IEntity entity)
|
|
||||||
{
|
|
||||||
return _entities.ContainsKey(entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool DisableGodmode(IEntity entity)
|
|
||||||
{
|
|
||||||
if (!_entities.Remove(entity, out var old))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity.TryGetComponent(out MovedByPressureComponent? moved))
|
|
||||||
{
|
|
||||||
moved.Enabled = old.MovedByPressure;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
{
|
|
||||||
damageable.Godmode = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Toggles godmode for a given entity.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="entity">The entity to toggle godmode for.</param>
|
|
||||||
/// <returns>true if enabled, false if disabled.</returns>
|
|
||||||
public bool ToggleGodmode(IEntity entity)
|
|
||||||
{
|
|
||||||
if (HasGodmode(entity))
|
|
||||||
{
|
|
||||||
DisableGodmode(entity);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EnableGodmode(entity);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class OldEntityInformation
|
|
||||||
{
|
|
||||||
public OldEntityInformation(IEntity entity)
|
|
||||||
{
|
|
||||||
Entity = entity;
|
|
||||||
MovedByPressure = entity.IsMovedByPressure();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEntity Entity { get; }
|
|
||||||
public bool MovedByPressure { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
#nullable enable annotations
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Content.Server.GameObjects.Components.Observer;
|
|
||||||
using Content.Server.Interfaces.GameTicking;
|
|
||||||
using Content.Server.Mobs;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
|
||||||
using Content.Shared.Preferences;
|
|
||||||
using Robust.Server.Player;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Network;
|
|
||||||
|
|
||||||
namespace Content.Server.GameTicking
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A round-start setup preset, such as which antagonists to spawn.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class GamePreset
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
public abstract bool Start(IReadOnlyList<IPlayerSession> readyPlayers, bool force = false);
|
|
||||||
public virtual string ModeTitle => "Sandbox";
|
|
||||||
public virtual string Description => "Secret!";
|
|
||||||
public virtual bool DisallowLateJoin => false;
|
|
||||||
public Dictionary<NetUserId, HumanoidCharacterProfile> ReadyProfiles = new();
|
|
||||||
|
|
||||||
public virtual void OnGameStarted() { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when a player is spawned in (this includes, but is not limited to, before Start)
|
|
||||||
/// </summary>
|
|
||||||
public virtual void OnSpawnPlayerCompleted(IPlayerSession session, IEntity mob, bool lateJoin) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when a player attempts to ghost.
|
|
||||||
/// </summary>
|
|
||||||
public virtual bool OnGhostAttempt(Mind mind, bool canReturnGlobal)
|
|
||||||
{
|
|
||||||
var playerEntity = mind.OwnedEntity;
|
|
||||||
|
|
||||||
if (playerEntity != null && playerEntity.HasComponent<GhostComponent>())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (mind.VisitingEntity != null)
|
|
||||||
{
|
|
||||||
mind.UnVisit();
|
|
||||||
}
|
|
||||||
|
|
||||||
var position = playerEntity?.Transform.Coordinates ?? IoCManager.Resolve<IGameTicker>().GetObserverSpawnPoint();
|
|
||||||
var canReturn = false;
|
|
||||||
|
|
||||||
if (playerEntity != null && canReturnGlobal && playerEntity.TryGetComponent(out IMobStateComponent? mobState))
|
|
||||||
{
|
|
||||||
if (mobState.IsDead())
|
|
||||||
{
|
|
||||||
canReturn = true;
|
|
||||||
}
|
|
||||||
else if (mobState.IsCritical())
|
|
||||||
{
|
|
||||||
canReturn = true;
|
|
||||||
|
|
||||||
if (playerEntity.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
{
|
|
||||||
//todo: what if they dont breathe lol
|
|
||||||
//cry deeply
|
|
||||||
damageable.SetDamage(damageable.GetDamageType("Asphyxiation"), 200, playerEntity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
canReturn = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
|
||||||
var ghost = entityManager.SpawnEntity("MobObserver", position);
|
|
||||||
ghost.Name = mind.CharacterName ?? string.Empty;
|
|
||||||
|
|
||||||
var ghostComponent = ghost.GetComponent<GhostComponent>();
|
|
||||||
ghostComponent.CanReturnToBody = canReturn;
|
|
||||||
|
|
||||||
if (canReturn)
|
|
||||||
mind.Visit(ghost);
|
|
||||||
else
|
|
||||||
mind.TransferTo(ghost);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual string GetRoundEndDescription() => "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,233 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Content.Server.Atmos;
|
|
||||||
using Content.Server.GameObjects.Components.GUI;
|
|
||||||
using Content.Server.GameObjects.Components.Items.Storage;
|
|
||||||
using Content.Server.GameObjects.Components.Markers;
|
|
||||||
using Content.Server.GameObjects.Components.PDA;
|
|
||||||
using Content.Server.GameObjects.Components.TraitorDeathMatch;
|
|
||||||
using Content.Server.GameTicking.GameRules;
|
|
||||||
using Content.Server.Interfaces.Chat;
|
|
||||||
using Content.Server.Interfaces.GameTicking;
|
|
||||||
using Content.Server.Mobs;
|
|
||||||
using Content.Server.Mobs.Roles.Traitor;
|
|
||||||
using Content.Server.Players;
|
|
||||||
using Content.Shared;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Inventory;
|
|
||||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
|
||||||
using Content.Shared.GameObjects.Components.PDA;
|
|
||||||
using Robust.Server.Player;
|
|
||||||
using Robust.Shared.Configuration;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.Log;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Random;
|
|
||||||
|
|
||||||
namespace Content.Server.GameTicking.GamePresets
|
|
||||||
{
|
|
||||||
[GamePreset("traitordm", "traitordeathmatch")]
|
|
||||||
public sealed class PresetTraitorDeathMatch : GamePreset
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
|
||||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
|
||||||
[Dependency] private readonly IGameTicker _gameTicker = default!;
|
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
|
||||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
|
||||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
|
||||||
|
|
||||||
public string PDAPrototypeName => "CaptainPDA";
|
|
||||||
public string BeltPrototypeName => "ClothingBeltJanitorFilled";
|
|
||||||
public string BackpackPrototypeName => "ClothingBackpackFilled";
|
|
||||||
|
|
||||||
private RuleMaxTimeRestart _restarter = default!;
|
|
||||||
private bool _safeToEndRound = false;
|
|
||||||
|
|
||||||
private Dictionary<UplinkAccount, string> _allOriginalNames = new();
|
|
||||||
|
|
||||||
public override bool Start(IReadOnlyList<IPlayerSession> readyPlayers, bool force = false)
|
|
||||||
{
|
|
||||||
_gameTicker.AddGameRule<RuleTraitorDeathMatch>();
|
|
||||||
_restarter = _gameTicker.AddGameRule<RuleMaxTimeRestart>();
|
|
||||||
_restarter.RoundMaxTime = TimeSpan.FromMinutes(30);
|
|
||||||
_restarter.RestartTimer();
|
|
||||||
_safeToEndRound = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnSpawnPlayerCompleted(IPlayerSession session, IEntity mob, bool lateJoin)
|
|
||||||
{
|
|
||||||
var startingBalance = _cfg.GetCVar(CCVars.TraitorDeathMatchStartingBalance);
|
|
||||||
|
|
||||||
// Yup, they're a traitor
|
|
||||||
var mind = session.Data.ContentData()?.Mind;
|
|
||||||
if (mind == null)
|
|
||||||
{
|
|
||||||
Logger.ErrorS("preset", "Failed getting mind for TDM player.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var traitorRole = new TraitorRole(mind);
|
|
||||||
mind.AddRole(traitorRole);
|
|
||||||
|
|
||||||
// Delete anything that may contain "dangerous" role-specific items.
|
|
||||||
// (This includes the PDA, as everybody gets the captain PDA in this mode for true-all-access reasons.)
|
|
||||||
if (mind.OwnedEntity != null && mind.OwnedEntity.TryGetComponent(out InventoryComponent? inventory))
|
|
||||||
{
|
|
||||||
var victimSlots = new[] {EquipmentSlotDefines.Slots.IDCARD, EquipmentSlotDefines.Slots.BELT, EquipmentSlotDefines.Slots.BACKPACK};
|
|
||||||
foreach (var slot in victimSlots)
|
|
||||||
{
|
|
||||||
if (inventory.TryGetSlotItem(slot, out ItemComponent? vItem))
|
|
||||||
vItem.Owner.Delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace their items:
|
|
||||||
|
|
||||||
// pda
|
|
||||||
var newPDA = _entityManager.SpawnEntity(PDAPrototypeName, mind.OwnedEntity.Transform.Coordinates);
|
|
||||||
inventory.Equip(EquipmentSlotDefines.Slots.IDCARD, newPDA.GetComponent<ItemComponent>());
|
|
||||||
|
|
||||||
// belt
|
|
||||||
var newTmp = _entityManager.SpawnEntity(BeltPrototypeName, mind.OwnedEntity.Transform.Coordinates);
|
|
||||||
inventory.Equip(EquipmentSlotDefines.Slots.BELT, newTmp.GetComponent<ItemComponent>());
|
|
||||||
|
|
||||||
// backpack
|
|
||||||
newTmp = _entityManager.SpawnEntity(BackpackPrototypeName, mind.OwnedEntity.Transform.Coordinates);
|
|
||||||
inventory.Equip(EquipmentSlotDefines.Slots.BACKPACK, newTmp.GetComponent<ItemComponent>());
|
|
||||||
|
|
||||||
// Like normal traitors, they need access to a traitor account.
|
|
||||||
var uplinkAccount = new UplinkAccount(mind.OwnedEntity.Uid, startingBalance);
|
|
||||||
var pdaComponent = newPDA.GetComponent<PDAComponent>();
|
|
||||||
pdaComponent.InitUplinkAccount(uplinkAccount);
|
|
||||||
_allOriginalNames[uplinkAccount] = mind.OwnedEntity.Name;
|
|
||||||
|
|
||||||
// The PDA needs to be marked with the correct owner.
|
|
||||||
pdaComponent.SetPDAOwner(mind.OwnedEntity.Name);
|
|
||||||
newPDA.AddComponent<TraitorDeathMatchReliableOwnerTagComponent>().UserId = mind.UserId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, it would be preferrable if they spawned as far away from other players as reasonably possible.
|
|
||||||
if (mind.OwnedEntity != null && FindAnyIsolatedSpawnLocation(mind, out var bestTarget))
|
|
||||||
{
|
|
||||||
mind.OwnedEntity.Transform.Coordinates = bestTarget;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// The station is too drained of air to safely continue.
|
|
||||||
if (_safeToEndRound)
|
|
||||||
{
|
|
||||||
_chatManager.DispatchServerAnnouncement(Loc.GetString("The station is too unsafe to continue. You have one minute."));
|
|
||||||
_restarter.RoundMaxTime = TimeSpan.FromMinutes(1);
|
|
||||||
_restarter.RestartTimer();
|
|
||||||
_safeToEndRound = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// It would be nice if this function were moved to some generic helpers class.
|
|
||||||
private bool FindAnyIsolatedSpawnLocation(Mind ignoreMe, out EntityCoordinates bestTarget)
|
|
||||||
{
|
|
||||||
// Collate people to avoid...
|
|
||||||
var existingPlayerPoints = new List<EntityCoordinates>();
|
|
||||||
foreach (var player in _playerManager.GetAllPlayers())
|
|
||||||
{
|
|
||||||
var avoidMeMind = player.Data.ContentData()?.Mind;
|
|
||||||
if ((avoidMeMind == null) || (avoidMeMind == ignoreMe))
|
|
||||||
continue;
|
|
||||||
var avoidMeEntity = avoidMeMind.OwnedEntity;
|
|
||||||
if (avoidMeEntity == null)
|
|
||||||
continue;
|
|
||||||
if (avoidMeEntity.TryGetComponent(out IMobStateComponent? mobState))
|
|
||||||
{
|
|
||||||
// Does have mob state component; if critical or dead, they don't really matter for spawn checks
|
|
||||||
if (mobState.IsCritical() || mobState.IsDead())
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Doesn't have mob state component. Assume something interesting is going on and don't count this as someone to avoid.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
existingPlayerPoints.Add(avoidMeEntity.Transform.Coordinates);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate over each possible spawn point, comparing to the existing player points.
|
|
||||||
// On failure, the returned target is the location that we're already at.
|
|
||||||
var bestTargetDistanceFromNearest = -1.0f;
|
|
||||||
// Need the random shuffle or it stuffs the first person into Atmospherics pretty reliably
|
|
||||||
var ents = new List<IEntity>(_entityManager.GetEntities(new TypeEntityQuery(typeof(SpawnPointComponent))));
|
|
||||||
_robustRandom.Shuffle(ents);
|
|
||||||
var foundATarget = false;
|
|
||||||
bestTarget = EntityCoordinates.Invalid;
|
|
||||||
foreach (var entity in ents)
|
|
||||||
{
|
|
||||||
if (!entity.Transform.Coordinates.IsTileAirProbablySafe())
|
|
||||||
continue;
|
|
||||||
var distanceFromNearest = float.PositiveInfinity;
|
|
||||||
foreach (var existing in existingPlayerPoints)
|
|
||||||
{
|
|
||||||
if (entity.Transform.Coordinates.TryDistance(_entityManager, existing, out var dist))
|
|
||||||
distanceFromNearest = Math.Min(distanceFromNearest, dist);
|
|
||||||
}
|
|
||||||
if (bestTargetDistanceFromNearest < distanceFromNearest)
|
|
||||||
{
|
|
||||||
bestTarget = entity.Transform.Coordinates;
|
|
||||||
bestTargetDistanceFromNearest = distanceFromNearest;
|
|
||||||
foundATarget = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return foundATarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool OnGhostAttempt(Mind mind, bool canReturnGlobal)
|
|
||||||
{
|
|
||||||
var entity = mind.OwnedEntity;
|
|
||||||
if ((entity != null) && (entity.TryGetComponent(out IMobStateComponent? mobState)))
|
|
||||||
{
|
|
||||||
if (mobState.IsCritical())
|
|
||||||
{
|
|
||||||
// TODO: This is copy/pasted from ghost code. Really, IDamagableComponent needs a method to reliably kill the target.
|
|
||||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
{
|
|
||||||
//todo: what if they dont breathe lol
|
|
||||||
damageable.ChangeDamage(damageable.GetDamageType("Asphyxiation"), 100, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (!mobState.IsDead())
|
|
||||||
{
|
|
||||||
if (entity.HasComponent<HandsComponent>())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var session = mind.Session;
|
|
||||||
if (session == null)
|
|
||||||
return false;
|
|
||||||
_gameTicker.Respawn(session);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string GetRoundEndDescription()
|
|
||||||
{
|
|
||||||
var lines = new List<string>();
|
|
||||||
lines.Add("The PDAs recovered afterwards...");
|
|
||||||
foreach (var entity in _entityManager.GetEntities(new TypeEntityQuery(typeof(PDAComponent))))
|
|
||||||
{
|
|
||||||
var pda = entity.GetComponent<PDAComponent>();
|
|
||||||
var uplink = pda.SyndicateUplinkAccount;
|
|
||||||
if ((uplink != null) && _allOriginalNames.ContainsKey(uplink))
|
|
||||||
{
|
|
||||||
lines.Add(Loc.GetString("{0}'s PDA, with {1} TC", _allOriginalNames[uplink], uplink.Balance));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return string.Join('\n', lines);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string ModeTitle => "Traitor Deathmatch";
|
|
||||||
public override string Description => Loc.GetString("Everyone's a traitor. Everyone wants each other dead.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,6 @@ using Robust.Server.Player;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Server.GameTicking.Presets
|
namespace Content.Server.GameTicking.Presets
|
||||||
{
|
{
|
||||||
@@ -67,17 +66,7 @@ namespace Content.Server.GameTicking.Presets
|
|||||||
if (playerEntity.TryGetComponent(out IDamageableComponent? damageable))
|
if (playerEntity.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
{
|
{
|
||||||
//todo: what if they dont breathe lol
|
//todo: what if they dont breathe lol
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
damageable.SetDamage(DamageType.Asphyxiation, 200, playerEntity);
|
damageable.SetDamage(DamageType.Asphyxiation, 200, playerEntity);
|
||||||
=======
|
|
||||||
//cry deeply
|
|
||||||
damageable.TrySetDamage(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Asphyxiation"), 200);
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
//cry deeply
|
|
||||||
damageable.TrySetDamage(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Asphyxiation"), 200);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ using Robust.Server.Player;
|
|||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Log;
|
using Robust.Shared.Log;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
@@ -39,7 +38,6 @@ namespace Content.Server.GameTicking.Presets
|
|||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
|
|
||||||
public string PDAPrototypeName => "CaptainPDA";
|
public string PDAPrototypeName => "CaptainPDA";
|
||||||
public string BeltPrototypeName => "ClothingBeltJanitorFilled";
|
public string BeltPrototypeName => "ClothingBeltJanitorFilled";
|
||||||
@@ -194,19 +192,11 @@ namespace Content.Server.GameTicking.Presets
|
|||||||
{
|
{
|
||||||
if (mobState.IsCritical())
|
if (mobState.IsCritical())
|
||||||
{
|
{
|
||||||
// TODO: This is copy/pasted from ghost code. Really, IDamageableComponent needs a method to reliably kill the target.
|
// TODO: This is copy/pasted from ghost code. Really, IDamagableComponent needs a method to reliably kill the target.
|
||||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
{
|
{
|
||||||
//todo: what if they dont breathe lol
|
//todo: what if they dont breathe lol
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
damageable.ChangeDamage(DamageType.Asphyxiation, 100, true);
|
damageable.ChangeDamage(DamageType.Asphyxiation, 100, true);
|
||||||
=======
|
|
||||||
damageable.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>("Asphyxiation"), 100, true);
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
damageable.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>("Asphyxiation"), 100, true);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!mobState.IsDead())
|
else if (!mobState.IsDead())
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ using Robust.Shared.Audio;
|
|||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
@@ -59,22 +58,8 @@ namespace Content.Server.Light.Components
|
|||||||
[DataField("hasLampOnSpawn")]
|
[DataField("hasLampOnSpawn")]
|
||||||
private bool _hasLampOnSpawn = true;
|
private bool _hasLampOnSpawn = true;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
[DataField("on")]
|
[DataField("on")]
|
||||||
=======
|
|
||||||
[ViewVariables] [DataField("on")]
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("on")]
|
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
=======
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("on")]
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
private bool _on = true;
|
private bool _on = true;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
@@ -92,19 +77,6 @@ namespace Content.Server.Light.Components
|
|||||||
|
|
||||||
[ViewVariables] private ContainerSlot _lightBulbContainer = default!;
|
[ViewVariables] private ContainerSlot _lightBulbContainer = default!;
|
||||||
|
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
|
||||||
[DataField("damageType")]
|
|
||||||
private readonly string _damageTypeID = "Heat";
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public DamageTypePrototype DamageType = default!;
|
|
||||||
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
|
||||||
_lightBulbContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, "light_bulb");
|
|
||||||
}
|
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public LightBulbComponent? LightBulb
|
public LightBulbComponent? LightBulb
|
||||||
{
|
{
|
||||||
@@ -154,22 +126,8 @@ namespace Content.Server.Light.Components
|
|||||||
void Burn()
|
void Burn()
|
||||||
{
|
{
|
||||||
Owner.PopupMessage(eventArgs.User, Loc.GetString("powered-light-component-burn-hand"));
|
Owner.PopupMessage(eventArgs.User, Loc.GetString("powered-light-component-burn-hand"));
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
damageableComponent.ChangeDamage(DamageType.Heat, 20, false, Owner);
|
damageableComponent.ChangeDamage(DamageType.Heat, 20, false, Owner);
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), _burnHandSound.GetSound(), Owner);
|
SoundSystem.Play(Filter.Pvs(Owner), _burnHandSound.GetSound(), Owner);
|
||||||
=======
|
|
||||||
damageableComponent.TryChangeDamage(DamageType, 20);
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), "/Audio/Effects/lightburn.ogg", Owner);
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), _burnHandSound.GetSound(), Owner);
|
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
=======
|
|
||||||
damageableComponent.TryChangeDamage(DamageType, 20);
|
|
||||||
SoundSystem.Play(Filter.Pvs(Owner), _burnHandSound.GetSound(), Owner);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Eject()
|
void Eject()
|
||||||
@@ -291,6 +249,13 @@ namespace Content.Server.Light.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
_lightBulbContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, "light_bulb");
|
||||||
|
}
|
||||||
|
|
||||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||||
{
|
{
|
||||||
base.HandleMessage(message, component);
|
base.HandleMessage(message, component);
|
||||||
|
|||||||
@@ -20,13 +20,7 @@ namespace Content.Server.Storage.Components
|
|||||||
public override string Name => "Lock";
|
public override string Name => "Lock";
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("locked")] public bool Locked { get; set; } = true;
|
[ViewVariables(VVAccess.ReadWrite)] [DataField("locked")] public bool Locked { get; set; } = true;
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("lockOnClick")] public bool LockOnClick { get; set; } = false;
|
[ViewVariables(VVAccess.ReadWrite)] [DataField("lockOnClick")] public bool LockOnClick { get; set; } = false;
|
||||||
=======
|
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("unlockingSound")] public SoundSpecifier UnlockSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/door_lock_off.ogg");
|
[ViewVariables(VVAccess.ReadWrite)] [DataField("unlockingSound")] public SoundSpecifier UnlockSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/door_lock_off.ogg");
|
||||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("lockingSound")] public SoundSpecifier LockSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/door_lock_off.ogg");
|
[ViewVariables(VVAccess.ReadWrite)] [DataField("lockingSound")] public SoundSpecifier LockSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/door_lock_off.ogg");
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Stack;
|
using Content.Server.Stack;
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Interaction.Events;
|
||||||
using Content.Shared.Interaction.Helpers;
|
using Content.Shared.Interaction.Helpers;
|
||||||
using Content.Shared.Stacks;
|
using Content.Shared.Stacks;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.Medical.Components
|
namespace Content.Server.Medical.Components
|
||||||
{
|
{
|
||||||
@@ -20,27 +18,7 @@ namespace Content.Server.Medical.Components
|
|||||||
{
|
{
|
||||||
public override string Name => "Healing";
|
public override string Name => "Healing";
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
[DataField("heal")] public Dictionary<DamageType, int> Heal { get; private set; } = new();
|
[DataField("heal")] public Dictionary<DamageType, int> Heal { get; private set; } = new();
|
||||||
=======
|
|
||||||
[DataField("heal", required: true )]
|
|
||||||
public Dictionary<string, int> Heal { get; private set; } = new();
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
|
||||||
// This also requires changing the dictionary type, and removing a _prototypeManager.Index() call.
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
[DataField("heal", required: true )]
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public Dictionary<string, int> Heal = new();
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
@@ -70,9 +48,9 @@ namespace Content.Server.Medical.Components
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var (damageTypeID, amount) in Heal)
|
foreach (var (type, amount) in Heal)
|
||||||
{
|
{
|
||||||
damageable.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(damageTypeID), -amount, true);
|
damageable.ChangeDamage(type, -amount, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -99,18 +99,8 @@ namespace Content.Server.Medical.Components
|
|||||||
private static readonly MedicalScannerBoundUserInterfaceState EmptyUIState =
|
private static readonly MedicalScannerBoundUserInterfaceState EmptyUIState =
|
||||||
new(
|
new(
|
||||||
null,
|
null,
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
new Dictionary<DamageClass, int>(),
|
new Dictionary<DamageClass, int>(),
|
||||||
new Dictionary<DamageType, int>(),
|
new Dictionary<DamageType, int>(),
|
||||||
=======
|
|
||||||
new Dictionary<string, int>(),
|
|
||||||
new Dictionary<string, int>(),
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
new Dictionary<string, int>(),
|
|
||||||
new Dictionary<string, int>(),
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
false);
|
false);
|
||||||
|
|
||||||
private MedicalScannerBoundUserInterfaceState GetUserInterfaceState()
|
private MedicalScannerBoundUserInterfaceState GetUserInterfaceState()
|
||||||
@@ -131,24 +121,12 @@ namespace Content.Server.Medical.Components
|
|||||||
return EmptyUIState;
|
return EmptyUIState;
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
var classes = new Dictionary<DamageClass, int>(damageable.DamageClasses);
|
var classes = new Dictionary<DamageClass, int>(damageable.DamageClasses);
|
||||||
var types = new Dictionary<DamageType, int>(damageable.DamageTypes);
|
var types = new Dictionary<DamageType, int>(damageable.DamageTypes);
|
||||||
=======
|
|
||||||
// Get dictionaries of damage, by fully supported damage groups and types
|
|
||||||
var groups = new Dictionary<string, int>(damageable.GetDamagePerFullySupportedGroupIDs);
|
|
||||||
var types = new Dictionary<string, int>(damageable.GetDamagePerTypeIDs);
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
// 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);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
if (_bodyContainer.ContainedEntity?.Uid == null)
|
if (_bodyContainer.ContainedEntity?.Uid == null)
|
||||||
{
|
{
|
||||||
return new MedicalScannerBoundUserInterfaceState(body.Uid, groups, types, true);
|
return new MedicalScannerBoundUserInterfaceState(body.Uid, classes, types, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
var cloningSystem = EntitySystem.Get<CloningSystem>();
|
var cloningSystem = EntitySystem.Get<CloningSystem>();
|
||||||
@@ -156,7 +134,7 @@ namespace Content.Server.Medical.Components
|
|||||||
mindComponent.Mind != null &&
|
mindComponent.Mind != null &&
|
||||||
cloningSystem.HasDnaScan(mindComponent.Mind);
|
cloningSystem.HasDnaScan(mindComponent.Mind);
|
||||||
|
|
||||||
return new MedicalScannerBoundUserInterfaceState(body.Uid, groups, types, scanned);
|
return new MedicalScannerBoundUserInterfaceState(body.Uid, classes, types, scanned);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateUserInterface()
|
private void UpdateUserInterface()
|
||||||
|
|||||||
@@ -7,20 +7,9 @@ using Content.Shared.Mining;
|
|||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
=======
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
namespace Content.Server.Mining.Components
|
namespace Content.Server.Mining.Components
|
||||||
{
|
{
|
||||||
@@ -32,25 +21,10 @@ namespace Content.Server.Mining.Components
|
|||||||
public override string Name => "AsteroidRock";
|
public override string Name => "AsteroidRock";
|
||||||
private static readonly string[] SpriteStates = {"0", "1", "2", "3", "4"};
|
private static readonly string[] SpriteStates = {"0", "1", "2", "3", "4"};
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// 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!;
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
protected override void Initialize()
|
protected override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
|
||||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||||
{
|
{
|
||||||
appearance.SetData(AsteroidRockVisuals.State, _random.Pick(SpriteStates));
|
appearance.SetData(AsteroidRockVisuals.State, _random.Pick(SpriteStates));
|
||||||
@@ -63,15 +37,7 @@ namespace Content.Server.Mining.Components
|
|||||||
if (!item.TryGetComponent(out MeleeWeaponComponent? meleeWeaponComponent))
|
if (!item.TryGetComponent(out MeleeWeaponComponent? meleeWeaponComponent))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
Owner.GetComponent<IDamageableComponent>().ChangeDamage(DamageType.Blunt, meleeWeaponComponent.Damage, false, item);
|
Owner.GetComponent<IDamageableComponent>().ChangeDamage(DamageType.Blunt, meleeWeaponComponent.Damage, false, item);
|
||||||
=======
|
|
||||||
Owner.GetComponent<IDamageableComponent>().TryChangeDamage(DamageType, meleeWeaponComponent.Damage);
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
Owner.GetComponent<IDamageableComponent>().TryChangeDamage(DamageType, meleeWeaponComponent.Damage);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
if (!item.TryGetComponent(out PickaxeComponent? pickaxeComponent))
|
if (!item.TryGetComponent(out PickaxeComponent? pickaxeComponent))
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.Alert;
|
using Content.Server.Alert;
|
||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
@@ -14,7 +14,6 @@ using Robust.Shared.Players;
|
|||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Server.Nutrition.Components
|
namespace Content.Server.Nutrition.Components
|
||||||
{
|
{
|
||||||
@@ -23,19 +22,6 @@ namespace Content.Server.Nutrition.Components
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// TODO DAMAGE UNITS When damage units support decimals, get rid of this.
|
|
||||||
// See also _accumulatedDamage in ThirstComponent and HealthChange.
|
|
||||||
private float _accumulatedDamage;
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// Base stuff
|
// Base stuff
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public float BaseDecayRate
|
public float BaseDecayRate
|
||||||
@@ -43,15 +29,7 @@ namespace Content.Server.Nutrition.Components
|
|||||||
get => _baseDecayRate;
|
get => _baseDecayRate;
|
||||||
set => _baseDecayRate = value;
|
set => _baseDecayRate = value;
|
||||||
}
|
}
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
[DataField("base_decay_rate")]
|
[DataField("base_decay_rate")]
|
||||||
=======
|
|
||||||
[DataField("baseDecayRate")]
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
[DataField("baseDecayRate")]
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
private float _baseDecayRate = 0.1f;
|
private float _baseDecayRate = 0.1f;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
@@ -81,25 +59,11 @@ namespace Content.Server.Nutrition.Components
|
|||||||
public Dictionary<HungerThreshold, float> HungerThresholds => _hungerThresholds;
|
public Dictionary<HungerThreshold, float> HungerThresholds => _hungerThresholds;
|
||||||
private readonly Dictionary<HungerThreshold, float> _hungerThresholds = new()
|
private readonly Dictionary<HungerThreshold, float> _hungerThresholds = new()
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
{HungerThreshold.Overfed, 600.0f},
|
{HungerThreshold.Overfed, 600.0f},
|
||||||
{HungerThreshold.Okay, 450.0f},
|
{HungerThreshold.Okay, 450.0f},
|
||||||
{HungerThreshold.Peckish, 300.0f},
|
{HungerThreshold.Peckish, 300.0f},
|
||||||
{HungerThreshold.Starving, 150.0f},
|
{HungerThreshold.Starving, 150.0f},
|
||||||
{HungerThreshold.Dead, 0.0f},
|
{HungerThreshold.Dead, 0.0f},
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
{ HungerThreshold.Overfed, 600.0f },
|
|
||||||
{ HungerThreshold.Okay, 450.0f },
|
|
||||||
{ HungerThreshold.Peckish, 300.0f },
|
|
||||||
{ HungerThreshold.Starving, 150.0f },
|
|
||||||
{ HungerThreshold.Dead, 0.0f },
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public static readonly Dictionary<HungerThreshold, AlertType> HungerThresholdAlertTypes = new()
|
public static readonly Dictionary<HungerThreshold, AlertType> HungerThresholdAlertTypes = new()
|
||||||
@@ -109,18 +73,6 @@ namespace Content.Server.Nutrition.Components
|
|||||||
{ HungerThreshold.Starving, AlertType.Starving },
|
{ HungerThreshold.Starving, AlertType.Starving },
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
|
||||||
// Also remove Initialize override, if no longer needed.
|
|
||||||
[DataField("damageType")]
|
|
||||||
private readonly string _damageTypeID = "Blunt"!;
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public DamageTypePrototype DamageType = default!;
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void HungerThresholdEffect(bool force = false)
|
public void HungerThresholdEffect(bool force = false)
|
||||||
{
|
{
|
||||||
if (_currentHungerThreshold != _lastHungerThreshold || force)
|
if (_currentHungerThreshold != _lastHungerThreshold || force)
|
||||||
@@ -225,7 +177,6 @@ namespace Content.Server.Nutrition.Components
|
|||||||
|
|
||||||
if (_currentHungerThreshold != HungerThreshold.Dead)
|
if (_currentHungerThreshold != HungerThreshold.Dead)
|
||||||
return;
|
return;
|
||||||
// --> Current Hunger is below dead threshold
|
|
||||||
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
return;
|
return;
|
||||||
@@ -235,24 +186,7 @@ namespace Content.Server.Nutrition.Components
|
|||||||
|
|
||||||
if (!mobState.IsDead())
|
if (!mobState.IsDead())
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
damageable.ChangeDamage(DamageType.Blunt, 2, true);
|
damageable.ChangeDamage(DamageType.Blunt, 2, true);
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// --> 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);
|
|
||||||
}
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.Alert;
|
using Content.Server.Alert;
|
||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
@@ -14,7 +14,6 @@ using Robust.Shared.Players;
|
|||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Server.Nutrition.Components
|
namespace Content.Server.Nutrition.Components
|
||||||
{
|
{
|
||||||
@@ -23,19 +22,6 @@ namespace Content.Server.Nutrition.Components
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// TODO DAMAGE UNITS When damage units support decimals, get rid of this.
|
|
||||||
// See also _accumulatedDamage in HungerComponent and HealthChange.
|
|
||||||
private float _accumulatedDamage;
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// Base stuff
|
// Base stuff
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public float BaseDecayRate
|
public float BaseDecayRate
|
||||||
@@ -43,7 +29,7 @@ namespace Content.Server.Nutrition.Components
|
|||||||
get => _baseDecayRate;
|
get => _baseDecayRate;
|
||||||
set => _baseDecayRate = value;
|
set => _baseDecayRate = value;
|
||||||
}
|
}
|
||||||
[DataField("baseDecayRate")]
|
[DataField("base_decay_rate")]
|
||||||
private float _baseDecayRate = 0.1f;
|
private float _baseDecayRate = 0.1f;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
@@ -86,18 +72,6 @@ namespace Content.Server.Nutrition.Components
|
|||||||
{ThirstThreshold.Parched, AlertType.Parched},
|
{ThirstThreshold.Parched, AlertType.Parched},
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
|
||||||
// Also remove Initialize override, if no longer needed.
|
|
||||||
[DataField("damageType")]
|
|
||||||
private readonly string _damageTypeID = "Blunt";
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public DamageTypePrototype DamageType = default!;
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ThirstThresholdEffect(bool force = false)
|
public void ThirstThresholdEffect(bool force = false)
|
||||||
{
|
{
|
||||||
if (_currentThirstThreshold != _lastThirstThreshold || force)
|
if (_currentThirstThreshold != _lastThirstThreshold || force)
|
||||||
@@ -200,7 +174,6 @@ namespace Content.Server.Nutrition.Components
|
|||||||
|
|
||||||
if (_currentThirstThreshold != ThirstThreshold.Dead)
|
if (_currentThirstThreshold != ThirstThreshold.Dead)
|
||||||
return;
|
return;
|
||||||
// --> Current Hunger is below dead threshold
|
|
||||||
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
return;
|
return;
|
||||||
@@ -210,25 +183,7 @@ namespace Content.Server.Nutrition.Components
|
|||||||
|
|
||||||
if (!mobState.IsDead())
|
if (!mobState.IsDead())
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
damageable.ChangeDamage(DamageType.Blunt, 2, true);
|
damageable.ChangeDamage(DamageType.Blunt, 2, true);
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// --> 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);
|
|
||||||
}
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
{
|
{
|
||||||
comp.OnUpdate(_accumulatedFrameTime);
|
comp.OnUpdate(_accumulatedFrameTime);
|
||||||
}
|
}
|
||||||
_accumulatedFrameTime = 0;
|
_accumulatedFrameTime -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -354,19 +354,7 @@ namespace Content.Server.Physics
|
|||||||
// Box2D has this as 800 which is jesus christo.
|
// Box2D has this as 800 which is jesus christo.
|
||||||
// Wouldn't recommend higher than 100 in debug and higher than 300 on release unless
|
// Wouldn't recommend higher than 100 in debug and higher than 300 on release unless
|
||||||
// you really want a profile.
|
// you really want a profile.
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
var count = 200;
|
var count = 200;
|
||||||
=======
|
|
||||||
var count = 50;
|
|
||||||
|
|
||||||
EntitySystem.Get<SharedPhysicsSystem>().Maps[mapId].Gravity = new Vector2(0f, -9.8f);
|
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
=======
|
|
||||||
var count = 50;
|
|
||||||
|
|
||||||
EntitySystem.Get<SharedPhysicsSystem>().Maps[mapId].Gravity = new Vector2(0f, -9.8f);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||||
|
|
||||||
for (var i = 0; i < count; i++)
|
for (var i = 0; i < count; i++)
|
||||||
|
|||||||
@@ -9,10 +9,8 @@ using Robust.Shared.IoC;
|
|||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.Projectiles.Components
|
namespace Content.Server.Projectiles.Components
|
||||||
{
|
{
|
||||||
@@ -27,96 +25,33 @@ namespace Content.Server.Projectiles.Components
|
|||||||
public override string Name => "Hitscan";
|
public override string Name => "Hitscan";
|
||||||
public CollisionGroup CollisionMask => (CollisionGroup) _collisionMask;
|
public CollisionGroup CollisionMask => (CollisionGroup) _collisionMask;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
public override string Name => "Hitscan";
|
|
||||||
public CollisionGroup CollisionMask => (CollisionGroup) _collisionMask;
|
|
||||||
=======
|
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
[DataField("layers")] //todo WithFormat.Flags<CollisionLayer>()
|
[DataField("layers")] //todo WithFormat.Flags<CollisionLayer>()
|
||||||
private int _collisionMask = (int) CollisionGroup.Opaque;
|
private int _collisionMask = (int) CollisionGroup.Opaque;
|
||||||
|
|
||||||
|
public float Damage
|
||||||
|
{
|
||||||
|
get => _damage;
|
||||||
|
set => _damage = value;
|
||||||
|
}
|
||||||
[DataField("damage")]
|
[DataField("damage")]
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
private float _damage = 10f;
|
private float _damage = 10f;
|
||||||
public DamageType DamageType => _damageType;
|
public DamageType DamageType => _damageType;
|
||||||
[DataField("damageType")]
|
[DataField("damageType")]
|
||||||
private DamageType _damageType = DamageType.Heat;
|
private DamageType _damageType = DamageType.Heat;
|
||||||
public float MaxLength => 20.0f;
|
public float MaxLength => 20.0f;
|
||||||
=======
|
|
||||||
public float Damage { get; set; } = 10f;
|
|
||||||
public float MaxLength => 20.0f;
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
private TimeSpan _startTime;
|
|
||||||
private TimeSpan _deathTime;
|
|
||||||
=======
|
|
||||||
[DataField("damageType", required: true)]
|
|
||||||
private string _damageTypeID = default!;
|
|
||||||
|
|
||||||
private DamageTypePrototype _damageType => _prototypeManager.Index<DamageTypePrototype>(_damageTypeID);
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
public float Damage { get; set; } = 10f;
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
|
|
||||||
public float ColorModifier { get; set; } = 1.0f;
|
|
||||||
<<<<<<< HEAD
|
|
||||||
[DataField("spriteName")]
|
|
||||||
=======
|
|
||||||
public float MaxLength => 20.0f;
|
|
||||||
|
|
||||||
private TimeSpan _startTime;
|
private TimeSpan _startTime;
|
||||||
private TimeSpan _deathTime;
|
private TimeSpan _deathTime;
|
||||||
|
|
||||||
public float ColorModifier { get; set; } = 1.0f;
|
public float ColorModifier { get; set; } = 1.0f;
|
||||||
[DataField("spriteName")]
|
[DataField("spriteName")]
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
=======
|
|
||||||
[DataField("spriteName")]
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
private string _spriteName = "Objects/Weapons/Guns/Projectiles/laser.png";
|
private string _spriteName = "Objects/Weapons/Guns/Projectiles/laser.png";
|
||||||
[DataField("muzzleFlash")]
|
[DataField("muzzleFlash")]
|
||||||
private string? _muzzleFlash;
|
private string? _muzzleFlash;
|
||||||
[DataField("impactFlash")]
|
[DataField("impactFlash")]
|
||||||
private string? _impactFlash;
|
private string? _impactFlash;
|
||||||
[DataField("soundHitWall")]
|
[DataField("soundHitWall")]
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
private SoundSpecifier _soundHitWall = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg");
|
private SoundSpecifier _soundHitWall = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg");
|
||||||
=======
|
|
||||||
private string _soundHitWall = "/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg";
|
|
||||||
[DataField("spriteName")]
|
|
||||||
private string _spriteName = "Objects/Weapons/Guns/Projectiles/laser.png";
|
|
||||||
=======
|
|
||||||
private SoundSpecifier _soundHitWall = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg");
|
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
=======
|
|
||||||
private SoundSpecifier _soundHitWall = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg");
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
public void FireEffects(IEntity user, float distance, Angle angle, IEntity? hitEntity = null)
|
public void FireEffects(IEntity user, float distance, Angle angle, IEntity? hitEntity = null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ namespace Content.Server.Projectiles.Components
|
|||||||
[ComponentReference(typeof(SharedProjectileComponent))]
|
[ComponentReference(typeof(SharedProjectileComponent))]
|
||||||
public class ProjectileComponent : SharedProjectileComponent
|
public class ProjectileComponent : SharedProjectileComponent
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
[DataField("damages")] private Dictionary<DamageType, int> _damages = new();
|
[DataField("damages")] private Dictionary<DamageType, int> _damages = new();
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
@@ -23,33 +21,12 @@ namespace Content.Server.Projectiles.Components
|
|||||||
get => _damages;
|
get => _damages;
|
||||||
set => _damages = value;
|
set => _damages = value;
|
||||||
}
|
}
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
|
||||||
// This also requires changing the dictionary type and modifying ProjectileSystem.cs, which uses it.
|
|
||||||
// While thats being done, also replace "damages" -> "damageTypes" For consistency.
|
|
||||||
[DataField("damages")]
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public Dictionary<string, int> Damages { get; set; } = new();
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
[DataField("deleteOnCollide")]
|
[DataField("deleteOnCollide")]
|
||||||
public bool DeleteOnCollide { get; } = true;
|
public bool DeleteOnCollide { get; } = true;
|
||||||
|
|
||||||
// Get that juicy FPS hit sound
|
// Get that juicy FPS hit sound
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
[DataField("soundHit", required: true)] public SoundSpecifier? SoundHit = default!;
|
[DataField("soundHit", required: true)] public SoundSpecifier? SoundHit = default!;
|
||||||
=======
|
|
||||||
[DataField("soundHit", required: true)] public SoundSpecifier SoundHit = default!;
|
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
=======
|
|
||||||
[DataField("soundHit", required: true)] public SoundSpecifier SoundHit = default!;
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
[DataField("soundHitSpecies")] public SoundSpecifier? SoundHitSpecies = null;
|
[DataField("soundHitSpecies")] public SoundSpecifier? SoundHitSpecies = null;
|
||||||
|
|
||||||
public bool DamagedEntity;
|
public bool DamagedEntity;
|
||||||
|
|||||||
@@ -7,17 +7,12 @@ using Robust.Shared.Audio;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Physics.Dynamics;
|
using Robust.Shared.Physics.Dynamics;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
|
|
||||||
namespace Content.Server.Projectiles
|
namespace Content.Server.Projectiles
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
internal sealed class ProjectileSystem : EntitySystem
|
internal sealed class ProjectileSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -44,27 +39,19 @@ namespace Content.Server.Projectiles
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
var soundHit = component.SoundHit?.GetSound();
|
var soundHit = component.SoundHit?.GetSound();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(soundHit))
|
if (!string.IsNullOrEmpty(soundHit))
|
||||||
SoundSystem.Play(playerFilter, soundHit, coordinates);
|
SoundSystem.Play(playerFilter, soundHit, coordinates);
|
||||||
=======
|
|
||||||
SoundSystem.Play(playerFilter, component.SoundHit.GetSound(), coordinates);
|
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
=======
|
|
||||||
SoundSystem.Play(playerFilter, component.SoundHit.GetSound(), coordinates);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!otherEntity.Deleted && otherEntity.TryGetComponent(out IDamageableComponent? damage))
|
if (!otherEntity.Deleted && otherEntity.TryGetComponent(out IDamageableComponent? damage))
|
||||||
{
|
{
|
||||||
EntityManager.TryGetEntity(component.Shooter, out var shooter);
|
EntityManager.TryGetEntity(component.Shooter, out var shooter);
|
||||||
|
|
||||||
foreach (var (damageTypeID, amount) in component.Damages)
|
foreach (var (damageType, amount) in component.Damages)
|
||||||
{
|
{
|
||||||
damage.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(damageTypeID), amount);
|
damage.ChangeDamage(damageType, amount, false, shooter);
|
||||||
}
|
}
|
||||||
|
|
||||||
component.DamagedEntity = true;
|
component.DamagedEntity = true;
|
||||||
|
|||||||
@@ -13,35 +13,8 @@ namespace Content.Server.Radiation
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityLookup _lookup = default!;
|
[Dependency] private readonly IEntityLookup _lookup = default!;
|
||||||
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
private const float RadiationCooldown = 0.5f;
|
private const float RadiationCooldown = 0.5f;
|
||||||
private float _accumulator;
|
private float _accumulator;
|
||||||
=======
|
|
||||||
public IEntity RadiationPulse(
|
|
||||||
EntityCoordinates coordinates,
|
|
||||||
float range,
|
|
||||||
int dps,
|
|
||||||
bool decay = true,
|
|
||||||
float minPulseLifespan = 0.8f,
|
|
||||||
float maxPulseLifespan = 2.5f,
|
|
||||||
SoundSpecifier sound = default!)
|
|
||||||
{
|
|
||||||
var radiationEntity = EntityManager.SpawnEntity(RadiationPrototype, coordinates);
|
|
||||||
var radiation = radiationEntity.GetComponent<RadiationPulseComponent>();
|
|
||||||
|
|
||||||
radiation.Range = range;
|
|
||||||
radiation.RadsPerSecond = dps;
|
|
||||||
radiation.Draw = false;
|
|
||||||
radiation.Decay = decay;
|
|
||||||
radiation.MinPulseLifespan = minPulseLifespan;
|
|
||||||
radiation.MaxPulseLifespan = maxPulseLifespan;
|
|
||||||
radiation.Sound = sound;
|
|
||||||
|
|
||||||
radiation.DoPulse();
|
|
||||||
|
|
||||||
return radiationEntity;
|
|
||||||
}
|
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ namespace Content.Server.Repairable
|
|||||||
{
|
{
|
||||||
if (!await welder.UseTool(eventArgs.User, Owner, _doAfterDelay, ToolQuality.Welding, _fuelCost))
|
if (!await welder.UseTool(eventArgs.User, Owner, _doAfterDelay, ToolQuality.Welding, _fuelCost))
|
||||||
return false;
|
return false;
|
||||||
damageable.TrySetAllDamage(0);
|
damageable.Heal();
|
||||||
|
|
||||||
Owner.PopupMessage(eventArgs.User,
|
Owner.PopupMessage(eventArgs.User,
|
||||||
Loc.GetString("comp-repairable-repair",
|
Loc.GetString("comp-repairable-repair",
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace Content.Server.Spawners.Components
|
|||||||
[DataField("job_id")]
|
[DataField("job_id")]
|
||||||
private string? _jobId;
|
private string? _jobId;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[field: ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("spawn_type")]
|
[DataField("spawn_type")]
|
||||||
public SpawnPointType SpawnType { get; } = SpawnPointType.Unset;
|
public SpawnPointType SpawnType { get; } = SpawnPointType.Unset;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Content.Server.Alert;
|
using Content.Server.Alert;
|
||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
@@ -8,8 +8,6 @@ using Robust.Shared.GameObjects;
|
|||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Server.Temperature.Components
|
namespace Content.Server.Temperature.Components
|
||||||
{
|
{
|
||||||
@@ -24,38 +22,11 @@ namespace Content.Server.Temperature.Components
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string Name => "Temperature";
|
public override string Name => "Temperature";
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
=======
|
|
||||||
[DataField("coldDamageType",required: true)]
|
|
||||||
private readonly string coldDamageType = default!;
|
|
||||||
[DataField("hotDamageType",required: true)]
|
|
||||||
private readonly string hotDamageType = default!;
|
|
||||||
=======
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
[DataField("heatDamageThreshold")]
|
|
||||||
private float _heatDamageThreshold = default;
|
|
||||||
[DataField("coldDamageThreshold")]
|
|
||||||
private float _coldDamageThreshold = default;
|
|
||||||
[DataField("tempDamageCoefficient")]
|
|
||||||
private float _tempDamageCoefficient = 1;
|
|
||||||
[DataField("currentTemperature")]
|
|
||||||
public float CurrentTemperature { get; set; } = Atmospherics.T20C;
|
|
||||||
[DataField("specificHeat")]
|
|
||||||
private float _specificHeat = Atmospherics.MinimumHeatCapacity;
|
|
||||||
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
[ViewVariables] public float CurrentTemperature { get => _currentTemperature; set => _currentTemperature = value; }
|
[ViewVariables] public float CurrentTemperature { get => _currentTemperature; set => _currentTemperature = value; }
|
||||||
=======
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
[ViewVariables] public float HeatDamageThreshold => _heatDamageThreshold;
|
[ViewVariables] public float HeatDamageThreshold => _heatDamageThreshold;
|
||||||
[ViewVariables] public float ColdDamageThreshold => _coldDamageThreshold;
|
[ViewVariables] public float ColdDamageThreshold => _coldDamageThreshold;
|
||||||
[ViewVariables] public float TempDamageCoefficient => _tempDamageCoefficient;
|
[ViewVariables] public float TempDamageCoefficient => _tempDamageCoefficient;
|
||||||
[ViewVariables] public float SpecificHeat => _specificHeat;
|
|
||||||
[ViewVariables] public float HeatCapacity {
|
[ViewVariables] public float HeatCapacity {
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
@@ -68,29 +39,22 @@ namespace Content.Server.Temperature.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
[ViewVariables] public float SpecificHeat => _specificHeat;
|
||||||
// Also remove Initialize override, if no longer needed.
|
|
||||||
[DataField("coldDamageType")]
|
[DataField("heatDamageThreshold")]
|
||||||
private readonly string _coldDamageTypeID = "Cold";
|
private float _heatDamageThreshold = default;
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField("coldDamageThreshold")]
|
||||||
public DamageTypePrototype ColdDamageType = default!;
|
private float _coldDamageThreshold = default;
|
||||||
[DataField("hotDamageType")]
|
[DataField("tempDamageCoefficient")]
|
||||||
private readonly string _hotDamageTypeID = "Heat";
|
private float _tempDamageCoefficient = 1;
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField("currentTemperature")]
|
||||||
public DamageTypePrototype HotDamageType = default!;
|
private float _currentTemperature = Atmospherics.T20C;
|
||||||
protected override void Initialize()
|
[DataField("specificHeat")]
|
||||||
{
|
private float _specificHeat = Atmospherics.MinimumHeatCapacity;
|
||||||
base.Initialize();
|
|
||||||
ColdDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_coldDamageTypeID);
|
|
||||||
HotDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_hotDamageTypeID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
public void Update()
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
var tempDamage = 0;
|
var tempDamage = 0;
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
DamageType? damageType = null;
|
DamageType? damageType = null;
|
||||||
if (CurrentTemperature >= _heatDamageThreshold)
|
if (CurrentTemperature >= _heatDamageThreshold)
|
||||||
{
|
{
|
||||||
@@ -102,13 +66,6 @@ namespace Content.Server.Temperature.Components
|
|||||||
tempDamage = (int) Math.Floor((_coldDamageThreshold - CurrentTemperature) * _tempDamageCoefficient);
|
tempDamage = (int) Math.Floor((_coldDamageThreshold - CurrentTemperature) * _tempDamageCoefficient);
|
||||||
damageType = DamageType.Cold;
|
damageType = DamageType.Cold;
|
||||||
}
|
}
|
||||||
=======
|
|
||||||
DamageTypePrototype? damageType = null;
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
if (Owner.TryGetComponent(out ServerAlertsComponent? status))
|
if (Owner.TryGetComponent(out ServerAlertsComponent? status))
|
||||||
{
|
{
|
||||||
@@ -151,40 +108,10 @@ namespace Content.Server.Temperature.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
if (!damageType.HasValue) return;
|
if (!damageType.HasValue) return;
|
||||||
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? component)) return;
|
if (!Owner.TryGetComponent(out IDamageableComponent? component)) return;
|
||||||
component.ChangeDamage(damageType.Value, tempDamage, false);
|
component.ChangeDamage(damageType.Value, tempDamage, false);
|
||||||
=======
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? component)) return;
|
|
||||||
=======
|
|
||||||
if (!Owner.TryGetComponent(out IDamageableComponent? component)) return;
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
if (CurrentTemperature >= _heatDamageThreshold)
|
|
||||||
{
|
|
||||||
int tempDamage = (int) Math.Floor((CurrentTemperature - _heatDamageThreshold) * _tempDamageCoefficient);
|
|
||||||
component.TryChangeDamage(HotDamageType, tempDamage, false);
|
|
||||||
}
|
|
||||||
else if (CurrentTemperature <= _coldDamageThreshold)
|
|
||||||
{
|
|
||||||
int tempDamage = (int) Math.Floor((_coldDamageThreshold - CurrentTemperature) * _tempDamageCoefficient);
|
|
||||||
component.TryChangeDamage(ColdDamageType, tempDamage, false);
|
|
||||||
}
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
|
|
||||||
if (damageType is null) return;
|
|
||||||
component.ChangeDamage(damageType, tempDamage, false);
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ using Content.Shared.Sound;
|
|||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Server.Weapon.Melee.Components
|
namespace Content.Server.Weapon.Melee.Components
|
||||||
{
|
{
|
||||||
@@ -50,23 +48,15 @@ namespace Content.Server.Weapon.Melee.Components
|
|||||||
[DataField("damage")]
|
[DataField("damage")]
|
||||||
public int Damage { get; set; } = 5;
|
public int Damage { get; set; } = 5;
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
[DataField("damageType")]
|
||||||
|
public DamageType DamageType { get; set; } = DamageType.Blunt;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("clickAttackEffect")]
|
[DataField("clickAttackEffect")]
|
||||||
public bool ClickAttackEffect { get; set; } = true;
|
public bool ClickAttackEffect { get; set; } = true;
|
||||||
|
|
||||||
public TimeSpan LastAttackTime;
|
public TimeSpan LastAttackTime;
|
||||||
public TimeSpan CooldownEnd;
|
public TimeSpan CooldownEnd;
|
||||||
|
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
|
||||||
// Also remove Initialize override, if no longer needed.
|
|
||||||
[DataField("damageType")]
|
|
||||||
private readonly string _damageTypeID = "Blunt";
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public DamageTypePrototype DamageType = default!;
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,6 @@ namespace Content.Server.Weapon.Melee
|
|||||||
public sealed class MeleeWeaponSystem : EntitySystem
|
public sealed class MeleeWeaponSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private IGameTiming _gameTiming = default!;
|
[Dependency] private IGameTiming _gameTiming = default!;
|
||||||
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -90,7 +88,7 @@ namespace Content.Server.Weapon.Melee
|
|||||||
|
|
||||||
if (target.TryGetComponent(out IDamageableComponent? damageableComponent))
|
if (target.TryGetComponent(out IDamageableComponent? damageableComponent))
|
||||||
{
|
{
|
||||||
damageableComponent.TryChangeDamage(comp.DamageType, comp.Damage);
|
damageableComponent.ChangeDamage(comp.DamageType, comp.Damage, false, owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
SoundSystem.Play(Filter.Pvs(owner), comp.HitSound.GetSound(), target);
|
SoundSystem.Play(Filter.Pvs(owner), comp.HitSound.GetSound(), target);
|
||||||
@@ -159,7 +157,7 @@ namespace Content.Server.Weapon.Melee
|
|||||||
{
|
{
|
||||||
if (entity.TryGetComponent<IDamageableComponent>(out var damageComponent))
|
if (entity.TryGetComponent<IDamageableComponent>(out var damageComponent))
|
||||||
{
|
{
|
||||||
damageComponent.TryChangeDamage(comp.DamageType, comp.Damage);
|
damageComponent.ChangeDamage(comp.DamageType, comp.Damage, false, owner);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,15 +188,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
{
|
{
|
||||||
if (energyRatio < 1.0)
|
if (energyRatio < 1.0)
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
var newDamages = new Dictionary<DamageType, int>(projectileComponent.Damages.Count);
|
var newDamages = new Dictionary<DamageType, int>(projectileComponent.Damages.Count);
|
||||||
=======
|
|
||||||
var newDamages = new Dictionary<string, int>(projectileComponent.Damages.Count);
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
var newDamages = new Dictionary<string, int>(projectileComponent.Damages.Count);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
foreach (var (damageType, damage) in projectileComponent.Damages)
|
foreach (var (damageType, damage) in projectileComponent.Damages)
|
||||||
{
|
{
|
||||||
newDamages.Add(damageType, (int) (damage * energyRatio));
|
newDamages.Add(damageType, (int) (damage * energyRatio));
|
||||||
|
|||||||
@@ -399,7 +399,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
|||||||
if (!result.HitEntity.TryGetComponent(out IDamageableComponent? damageable))
|
if (!result.HitEntity.TryGetComponent(out IDamageableComponent? damageable))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
damageable.TryChangeDamage(hitscan.DamageType, (int)Math.Round(hitscan.Damage, MidpointRounding.AwayFromZero));
|
damageable.ChangeDamage(hitscan.DamageType, (int)Math.Round(hitscan.Damage, MidpointRounding.AwayFromZero), false, Owner);
|
||||||
//I used Math.Round over Convert.toInt32, as toInt32 always rounds to
|
//I used Math.Round over Convert.toInt32, as toInt32 always rounds to
|
||||||
//even numbers if halfway between two numbers, rather than rounding to nearest
|
//even numbers if halfway between two numbers, rather than rounding to nearest
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ using Robust.Shared.Players;
|
|||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Content.Server.Weapon.Ranged
|
namespace Content.Server.Weapon.Ranged
|
||||||
{
|
{
|
||||||
@@ -52,41 +50,11 @@ namespace Content.Server.Weapon.Ranged
|
|||||||
[DataField("canHotspot")]
|
[DataField("canHotspot")]
|
||||||
private bool _canHotspot = true;
|
private bool _canHotspot = true;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
=======
|
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
[DataField("clumsyWeaponHandlingSound")]
|
[DataField("clumsyWeaponHandlingSound")]
|
||||||
private SoundSpecifier _clumsyWeaponHandlingSound = new SoundPathSpecifier("/Audio/Items/bikehorn.ogg");
|
private SoundSpecifier _clumsyWeaponHandlingSound = new SoundPathSpecifier("/Audio/Items/bikehorn.ogg");
|
||||||
|
|
||||||
[DataField("clumsyWeaponShotSound")]
|
[DataField("clumsyWeaponShotSound")]
|
||||||
private SoundSpecifier _clumsyWeaponShotSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/bang.ogg");
|
private SoundSpecifier _clumsyWeaponShotSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/bang.ogg");
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
=======
|
|
||||||
=======
|
|
||||||
|
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
=======
|
|
||||||
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// 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 }
|
|
||||||
};
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
public Func<bool>? WeaponCanFireHandler;
|
public Func<bool>? WeaponCanFireHandler;
|
||||||
public Func<IEntity, bool>? UserCanFireHandler;
|
public Func<IEntity, bool>? UserCanFireHandler;
|
||||||
@@ -200,9 +168,6 @@ namespace Content.Server.Weapon.Ranged
|
|||||||
|
|
||||||
if (ClumsyCheck && ClumsyComponent.TryRollClumsy(user, ClumsyExplodeChance))
|
if (ClumsyCheck && ClumsyComponent.TryRollClumsy(user, ClumsyExplodeChance))
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
SoundSystem.Play(
|
SoundSystem.Play(
|
||||||
Filter.Pvs(Owner), _clumsyWeaponHandlingSound.GetSound(),
|
Filter.Pvs(Owner), _clumsyWeaponHandlingSound.GetSound(),
|
||||||
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
||||||
@@ -210,48 +175,18 @@ namespace Content.Server.Weapon.Ranged
|
|||||||
SoundSystem.Play(
|
SoundSystem.Play(
|
||||||
Filter.Pvs(Owner), _clumsyWeaponShotSound.GetSound(),
|
Filter.Pvs(Owner), _clumsyWeaponShotSound.GetSound(),
|
||||||
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
||||||
=======
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
|
|
||||||
=======
|
|
||||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
|
||||||
//Wound them
|
|
||||||
if (user.TryGetComponent(out IDamageableComponent? health))
|
if (user.TryGetComponent(out IDamageableComponent? health))
|
||||||
{
|
{
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
health.ChangeDamage(DamageType.Blunt, 10, false, user);
|
health.ChangeDamage(DamageType.Blunt, 10, false, user);
|
||||||
health.ChangeDamage(DamageType.Heat, 5, false, user);
|
health.ChangeDamage(DamageType.Heat, 5, false, user);
|
||||||
=======
|
|
||||||
=======
|
|
||||||
//Wound them
|
|
||||||
if (user.TryGetComponent(out IDamageableComponent? health))
|
|
||||||
{
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
foreach (KeyValuePair<string, int> damage in ClumsyDamage)
|
|
||||||
{
|
|
||||||
health.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(damage.Key), damage.Value);
|
|
||||||
}
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Knock them down
|
|
||||||
if (user.TryGetComponent(out StunnableComponent? stun))
|
if (user.TryGetComponent(out StunnableComponent? stun))
|
||||||
{
|
{
|
||||||
stun.Paralyze(3f);
|
stun.Paralyze(3f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply salt to the wound ("Honk!")
|
|
||||||
SoundSystem.Play(
|
|
||||||
Filter.Pvs(Owner), _clumsyWeaponHandlingSound.GetSound(),
|
|
||||||
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
|
||||||
|
|
||||||
SoundSystem.Play(
|
|
||||||
Filter.Pvs(Owner), _clumsyWeaponShotSound.GetSound(),
|
|
||||||
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
|
||||||
|
|
||||||
user.PopupMessage(Loc.GetString("server-ranged-weapon-component-try-fire-clumsy"));
|
user.PopupMessage(Loc.GetString("server-ranged-weapon-component-try-fire-clumsy"));
|
||||||
|
|
||||||
Owner.Delete();
|
Owner.Delete();
|
||||||
|
|||||||
@@ -73,23 +73,6 @@ namespace Content.Shared.Body.Components
|
|||||||
|
|
||||||
public SharedBodyPartComponent? CenterPart => CenterSlot?.Part;
|
public SharedBodyPartComponent? CenterPart => CenterSlot?.Part;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Amount of damage to deal when all vital organs are removed.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("vitalPartsRemovedDamage")]
|
|
||||||
public int VitalPartsRemovedDamage { get; set; } = 300!;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Damage type to deal when all vital organs are removed.
|
|
||||||
/// </summary>
|
|
||||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("vitalPartsRemovedDamageType")]
|
|
||||||
private string _vitalPartsRemovedDamageTypeID { get; set; } = "Bloodloss"!;
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public DamageTypePrototype VitalPartsRemovedDamageType = default!;
|
|
||||||
|
|
||||||
protected override void Initialize()
|
protected override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -98,7 +81,6 @@ namespace Content.Shared.Body.Components
|
|||||||
// TODO BODY Move to template or somewhere else
|
// TODO BODY Move to template or somewhere else
|
||||||
if (TemplateId != null)
|
if (TemplateId != null)
|
||||||
{
|
{
|
||||||
VitalPartsRemovedDamageType = _prototypeManager.Index<DamageTypePrototype>(_vitalPartsRemovedDamageTypeID);
|
|
||||||
var template = _prototypeManager.Index<BodyTemplatePrototype>(TemplateId);
|
var template = _prototypeManager.Index<BodyTemplatePrototype>(TemplateId);
|
||||||
|
|
||||||
foreach (var (id, partType) in template.Slots)
|
foreach (var (id, partType) in template.Slots)
|
||||||
@@ -212,15 +194,7 @@ namespace Content.Shared.Body.Components
|
|||||||
{
|
{
|
||||||
if (part.IsVital && SlotParts.Count(x => x.Value.PartType == part.PartType) == 0)
|
if (part.IsVital && SlotParts.Count(x => x.Value.PartType == part.PartType) == 0)
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
damageable.ChangeDamage(DamageType.Bloodloss, 300, true); // TODO BODY KILL
|
damageable.ChangeDamage(DamageType.Bloodloss, 300, true); // TODO BODY KILL
|
||||||
=======
|
|
||||||
damageable.TryChangeDamage(VitalPartsRemovedDamageType, VitalPartsRemovedDamage, true); // TODO BODY KILL
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
damageable.TryChangeDamage(VitalPartsRemovedDamageType, VitalPartsRemovedDamage, true); // TODO BODY KILL
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,242 +18,67 @@ namespace Content.Shared.Damage.Components
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Component that allows attached entities to take damage.
|
/// Component that allows attached entities to take damage.
|
||||||
|
/// This basic version never dies (thus can take an indefinite amount of damage).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
|
||||||
/// The supported damage types are specified using a <see cref="DamageContainerPrototype"/>s. DamageContainers
|
|
||||||
/// are effectively a dictionary of damage types and damage numbers, along with functions to modify them. Damage
|
|
||||||
/// groups are collections of damage types. A damage group is 'applicable' to a damageable component if it
|
|
||||||
/// supports at least one damage type in that group. A subset of these groups may be 'fully supported' when every
|
|
||||||
/// member of the group is supported by the container. This basic version never dies (thus can take an
|
|
||||||
/// indefinite amount of damage).
|
|
||||||
/// </remarks>
|
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(IDamageableComponent))]
|
[ComponentReference(typeof(IDamageableComponent))]
|
||||||
[NetworkedComponent()]
|
[NetworkedComponent()]
|
||||||
public class DamageableComponent : Component, IDamageableComponent, IRadiationAct, ISerializationHooks
|
public class DamageableComponent : Component, IDamageableComponent, IRadiationAct, ISerializationHooks
|
||||||
{
|
{
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
=======
|
|
||||||
|
|
||||||
=======
|
|
||||||
>>>>>>> fix a few bugs
|
|
||||||
public override string Name => "Damageable";
|
public override string Name => "Damageable";
|
||||||
public override uint? NetID => ContentNetIDs.DAMAGEABLE;
|
|
||||||
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
<<<<<<< HEAD
|
|
||||||
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
>>>>>>> Fix Merge issues
|
|
||||||
// TODO define these in yaml?
|
// TODO define these in yaml?
|
||||||
public const string DefaultResistanceSet = "defaultResistances";
|
public const string DefaultResistanceSet = "defaultResistances";
|
||||||
public const string DefaultDamageContainer = "metallicDamageContainer";
|
public const string DefaultDamageContainer = "metallicDamageContainer";
|
||||||
=======
|
|
||||||
/// <summary>
|
|
||||||
/// The main damage dictionary. All the damage information is stored in this dictionary with <see cref="DamageTypePrototype"/> keys.
|
|
||||||
/// </summary>
|
|
||||||
private Dictionary<DamageTypePrototype, int> _damageDict = new();
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
private readonly Dictionary<DamageType, int> _damageList = DamageTypeExtensions.ToNewDictionary();
|
private readonly Dictionary<DamageType, int> _damageList = DamageTypeExtensions.ToNewDictionary();
|
||||||
|
|
||||||
[DataField("resistances")] public string ResistanceSetId = DefaultResistanceSet;
|
[DataField("resistances")] public string ResistanceSetId = DefaultResistanceSet;
|
||||||
=======
|
|
||||||
[DataField("resistances")]
|
|
||||||
public string ResistanceSetId { get; set; } = "defaultResistances";
|
|
||||||
|
|
||||||
[ViewVariables] public ResistanceSet Resistances { get; set; } = new();
|
|
||||||
>>>>>>> Merge fixes
|
|
||||||
|
|
||||||
// TODO DAMAGE Use as default values, specify overrides in a separate property through yaml for better (de)serialization
|
// TODO DAMAGE Use as default values, specify overrides in a separate property through yaml for better (de)serialization
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
[ViewVariables] [DataField("damageContainer")] public string DamageContainerId { get; set; } = DefaultDamageContainer;
|
[ViewVariables] [DataField("damageContainer")] public string DamageContainerId { get; set; } = DefaultDamageContainer;
|
||||||
=======
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The main damage dictionary. All the damage information is stored in this dictionary with <see cref="DamageTypePrototype"/> keys.
|
|
||||||
/// </summary>
|
|
||||||
private Dictionary<DamageTypePrototype, int> _damageDict = new();
|
|
||||||
|
|
||||||
[DataField("resistances")]
|
|
||||||
public string ResistanceSetId { get; set; } = "defaultResistances";
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
[ViewVariables] public ResistanceSet Resistances { get; set; } = new();
|
[ViewVariables] public ResistanceSet Resistances { get; set; } = new();
|
||||||
=======
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("damageContainer")]
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
public string DamageContainerId { get; set; } = DefaultDamageContainer;
|
|
||||||
>>>>>>> fix a few bugs
|
|
||||||
|
|
||||||
// TODO DAMAGE Use as default values, specify overrides in a separate property through yaml for better (de)serialization
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("damageContainer")]
|
|
||||||
public string DamageContainerId { get; set; } = "metallicDamageContainer";
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
[ViewVariables] public IReadOnlyDictionary<DamageClass, int> DamageClasses => _damageList.ToClassDictionary();
|
|
||||||
=======
|
|
||||||
// TODO DAMAGE Cache this
|
// TODO DAMAGE Cache this
|
||||||
// When moving logic from damageableComponent --> Damage System, make damageSystem update these on damage change.
|
[ViewVariables] public int TotalDamage => _damageList.Values.Sum();
|
||||||
[ViewVariables] public int TotalDamage => _damageDict.Values.Sum();
|
|
||||||
[ViewVariables] public IReadOnlyDictionary<DamageTypePrototype, int> GetDamagePerType => _damageDict;
|
|
||||||
[ViewVariables] public IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerApplicableGroup => DamageTypeDictToDamageGroupDict(_damageDict, ApplicableDamageGroups);
|
|
||||||
[ViewVariables] public IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerFullySupportedGroup => DamageTypeDictToDamageGroupDict(_damageDict, FullySupportedDamageGroups);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
// Whenever sending over network, also need a <string, int> dictionary
|
[ViewVariables] public IReadOnlyDictionary<DamageClass, int> DamageClasses => _damageList.ToClassDictionary();
|
||||||
// TODO DAMAGE MAYBE Cache this?
|
|
||||||
public IReadOnlyDictionary<string, int> GetDamagePerApplicableGroupIDs => ConvertDictKeysToIDs(GetDamagePerApplicableGroup);
|
|
||||||
public IReadOnlyDictionary<string, int> GetDamagePerFullySupportedGroupIDs => ConvertDictKeysToIDs(GetDamagePerFullySupportedGroup);
|
|
||||||
public IReadOnlyDictionary<string, int> GetDamagePerTypeIDs => ConvertDictKeysToIDs(_damageDict);
|
|
||||||
|
|
||||||
// TODO PROTOTYPE Replace these datafield variables with prototype references, once they are supported.
|
[ViewVariables] public IReadOnlyDictionary<DamageType, int> DamageTypes => _damageList;
|
||||||
// Also requires appropriate changes in OnExplosion() and RadiationAct()
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("radiationDamageTypes")]
|
|
||||||
public List<string> RadiationDamageTypeIDs { get; set; } = new() {"Radiation"};
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("explosionDamageTypes")]
|
|
||||||
public List<string> ExplosionDamageTypeIDs { get; set; } = new() { "Piercing", "Heat" };
|
|
||||||
|
|
||||||
public HashSet<DamageGroupPrototype> ApplicableDamageGroups { get; } = new();
|
[ViewVariables] public HashSet<DamageType> SupportedTypes { get; } = new();
|
||||||
|
|
||||||
|
[ViewVariables] public HashSet<DamageClass> SupportedClasses { get; } = new();
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
public bool SupportsDamageClass(DamageClass @class)
|
public bool SupportsDamageClass(DamageClass @class)
|
||||||
{
|
{
|
||||||
return SupportedClasses.Contains(@class);
|
return SupportedClasses.Contains(@class);
|
||||||
=======
|
|
||||||
public HashSet<DamageGroupPrototype> SupportedGroups { get; } = new();
|
|
||||||
=======
|
|
||||||
public string DamageContainerId { get; set; } = "metallicDamageContainer";
|
|
||||||
|
|
||||||
// TODO DAMAGE Cache this
|
|
||||||
// When moving logic from damageableComponent --> Damage System, make damageSystem update these on damage change.
|
|
||||||
[ViewVariables] public int TotalDamage => _damageDict.Values.Sum();
|
|
||||||
[ViewVariables] public IReadOnlyDictionary<DamageTypePrototype, int> GetDamagePerType => _damageDict;
|
|
||||||
[ViewVariables] public IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerApplicableGroup => DamageTypeDictToDamageGroupDict(_damageDict, ApplicableDamageGroups);
|
|
||||||
[ViewVariables] public IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerFullySupportedGroup => DamageTypeDictToDamageGroupDict(_damageDict, FullySupportedDamageGroups);
|
|
||||||
|
|
||||||
// Whenever sending over network, also need a <string, int> dictionary
|
|
||||||
// TODO DAMAGE MAYBE Cache this?
|
|
||||||
public IReadOnlyDictionary<string, int> GetDamagePerApplicableGroupIDs => ConvertDictKeysToIDs(GetDamagePerApplicableGroup);
|
|
||||||
public IReadOnlyDictionary<string, int> GetDamagePerFullySupportedGroupIDs => ConvertDictKeysToIDs(GetDamagePerFullySupportedGroup);
|
|
||||||
public IReadOnlyDictionary<string, int> GetDamagePerTypeIDs => ConvertDictKeysToIDs(_damageDict);
|
|
||||||
|
|
||||||
// TODO PROTOTYPE Replace these datafield variables with prototype references, once they are supported.
|
|
||||||
// Also requires appropriate changes in OnExplosion() and RadiationAct()
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("radiationDamageTypes")]
|
|
||||||
public List<string> RadiationDamageTypeIDs { get; set; } = new() {"Radiation"};
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("explosionDamageTypes")]
|
|
||||||
public List<string> ExplosionDamageTypeIDs { get; set; } = new() { "Piercing", "Heat" };
|
|
||||||
|
|
||||||
public HashSet<DamageGroupPrototype> ApplicableDamageGroups { get; } = new();
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
|
|
||||||
public HashSet<DamageGroupPrototype> FullySupportedDamageGroups { get; } = new();
|
|
||||||
|
|
||||||
public HashSet<DamageTypePrototype> SupportedDamageTypes { get; } = new();
|
|
||||||
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
public bool SupportsDamageClass(DamageGroupPrototype damageGroup)
|
|
||||||
{
|
|
||||||
return SupportedGroups.Contains(damageGroup);
|
|
||||||
>>>>>>> Merge fixes
|
|
||||||
}
|
}
|
||||||
=======
|
|
||||||
public HashSet<DamageGroupPrototype> FullySupportedDamageGroups { get; } = new();
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
public HashSet<DamageTypePrototype> SupportedDamageTypes { get; } = new();
|
public bool SupportsDamageType(DamageType type)
|
||||||
|
{
|
||||||
|
return SupportedTypes.Contains(type);
|
||||||
|
}
|
||||||
|
|
||||||
=======
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
protected override void Initialize()
|
protected override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||||
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// TODO DAMAGE Serialize damage done and resistance changes
|
// TODO DAMAGE Serialize damage done and resistance changes
|
||||||
var damageContainerPrototype = _prototypeManager.Index<DamageContainerPrototype>(DamageContainerId);
|
var damagePrototype = prototypeManager.Index<DamageContainerPrototype>(DamageContainerId);
|
||||||
|
|
||||||
ApplicableDamageGroups.Clear();
|
|
||||||
FullySupportedDamageGroups.Clear();
|
|
||||||
SupportedDamageTypes.Clear();
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
SupportedClasses.Clear();
|
SupportedClasses.Clear();
|
||||||
SupportedTypes.Clear();
|
SupportedTypes.Clear();
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> fix a few bugs
|
|
||||||
_prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
|
||||||
=======
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
|
|
||||||
// TODO DAMAGE Serialize damage done and resistance changes
|
|
||||||
var damageContainerPrototype = _prototypeManager.Index<DamageContainerPrototype>(DamageContainerId);
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
DamageContainerId = damagePrototype.ID;
|
DamageContainerId = damagePrototype.ID;
|
||||||
SupportedClasses.UnionWith(damagePrototype.SupportedClasses);
|
SupportedClasses.UnionWith(damagePrototype.SupportedClasses);
|
||||||
SupportedTypes.UnionWith(damagePrototype.SupportedTypes);
|
SupportedTypes.UnionWith(damagePrototype.SupportedTypes);
|
||||||
=======
|
|
||||||
SupportedGroups.Clear();
|
|
||||||
SupportedTypes.Clear();
|
|
||||||
=======
|
|
||||||
ApplicableDamageGroups.Clear();
|
|
||||||
FullySupportedDamageGroups.Clear();
|
|
||||||
SupportedDamageTypes.Clear();
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
|
|
||||||
//Get Damage groups/types from the DamageContainerPrototype.
|
var resistancePrototype = prototypeManager.Index<ResistanceSetPrototype>(ResistanceSetId);
|
||||||
DamageContainerId = damageContainerPrototype.ID;
|
Resistances = new ResistanceSet(resistancePrototype);
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
SupportedGroups.UnionWith(damageContainerPrototype.SupportedDamageGroups);
|
|
||||||
SupportedTypes.UnionWith(damageContainerPrototype.SupportedDamageTypes);
|
|
||||||
>>>>>>> Merge fixes
|
|
||||||
=======
|
|
||||||
ApplicableDamageGroups.UnionWith(damageContainerPrototype.ApplicableDamageGroups);
|
|
||||||
FullySupportedDamageGroups.UnionWith(damageContainerPrototype.FullySupportedDamageGroups);
|
|
||||||
SupportedDamageTypes.UnionWith(damageContainerPrototype.SupportedDamageTypes);
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
|
|
||||||
=======
|
|
||||||
//Get Damage groups/types from the DamageContainerPrototype.
|
|
||||||
DamageContainerId = damageContainerPrototype.ID;
|
|
||||||
ApplicableDamageGroups.UnionWith(damageContainerPrototype.ApplicableDamageGroups);
|
|
||||||
FullySupportedDamageGroups.UnionWith(damageContainerPrototype.FullySupportedDamageGroups);
|
|
||||||
SupportedDamageTypes.UnionWith(damageContainerPrototype.SupportedDamageTypes);
|
|
||||||
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
//initialize damage dictionary 0 damage
|
|
||||||
_damageDict = new(SupportedDamageTypes.Count);
|
|
||||||
foreach (var type in SupportedDamageTypes)
|
|
||||||
{
|
|
||||||
_damageDict.Add(type, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Resistances = new ResistanceSet(_prototypeManager.Index<ResistanceSetPrototype>(ResistanceSetId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Startup()
|
protected override void Startup()
|
||||||
@@ -263,25 +88,9 @@ namespace Content.Shared.Damage.Components
|
|||||||
ForceHealthChangedEvent();
|
ForceHealthChangedEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
=======
|
|
||||||
public DamageTypePrototype GetDamageType(string ID)
|
|
||||||
{
|
|
||||||
return _prototypeManager.Index<DamageTypePrototype>(ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DamageGroupPrototype GetDamageGroup(string ID)
|
|
||||||
{
|
|
||||||
return _prototypeManager.Index<DamageGroupPrototype>(ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
public override ComponentState GetComponentState(ICommonSession player)
|
public override ComponentState GetComponentState(ICommonSession player)
|
||||||
{
|
{
|
||||||
return new DamageableComponentState(GetDamagePerTypeIDs);
|
return new DamageableComponentState(_damageList);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||||
@@ -293,104 +102,49 @@ namespace Content.Shared.Damage.Components
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_damageDict.Clear();
|
_damageList.Clear();
|
||||||
|
|
||||||
foreach (var (type, damage) in state.DamageDict)
|
foreach (var (type, damage) in state.DamageList)
|
||||||
{
|
{
|
||||||
_damageDict[_prototypeManager.Index<DamageTypePrototype>(type)] = damage;
|
_damageList[type] = damage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetDamage(DamageTypePrototype type)
|
public int GetDamage(DamageType type)
|
||||||
{
|
{
|
||||||
return GetDamagePerType.GetValueOrDefault(type);
|
return _damageList.GetValueOrDefault(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryGetDamage(DamageTypePrototype type, out int damage)
|
public bool TryGetDamage(DamageType type, out int damage)
|
||||||
{
|
{
|
||||||
return GetDamagePerType.TryGetValue(type, out damage);
|
return _damageList.TryGetValue(type, out damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
public int GetDamage(DamageClass @class)
|
public int GetDamage(DamageClass @class)
|
||||||
{
|
{
|
||||||
if (!SupportsDamageClass(@class))
|
if (!SupportsDamageClass(@class))
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
=======
|
|
||||||
public int GetDamage(DamageGroupPrototype group)
|
|
||||||
{
|
|
||||||
return GetDamagePerApplicableGroup.GetValueOrDefault(group);
|
|
||||||
}
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
public int GetDamage(DamageGroupPrototype group)
|
|
||||||
{
|
|
||||||
return GetDamagePerApplicableGroup.GetValueOrDefault(group);
|
|
||||||
}
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
public bool TryGetDamage(DamageGroupPrototype group, out int damage)
|
var damage = 0;
|
||||||
{
|
|
||||||
return GetDamagePerApplicableGroup.TryGetValue(group, out damage);
|
|
||||||
}
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
foreach (var type in @class.ToTypes())
|
foreach (var type in @class.ToTypes())
|
||||||
{
|
{
|
||||||
damage += GetDamage(type);
|
damage += GetDamage(type);
|
||||||
}
|
}
|
||||||
=======
|
|
||||||
=======
|
return damage;
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
public bool IsApplicableDamageGroup(DamageGroupPrototype group)
|
|
||||||
{
|
|
||||||
return ApplicableDamageGroups.Contains(group);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsFullySupportedDamageGroup(DamageGroupPrototype group)
|
|
||||||
{
|
|
||||||
return FullySupportedDamageGroups.Contains(group);
|
|
||||||
<<<<<<< HEAD
|
|
||||||
=======
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSupportedDamageType(DamageTypePrototype type)
|
|
||||||
{
|
|
||||||
return SupportedDamageTypes.Contains(type);
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
public bool IsSupportedDamageType(DamageTypePrototype type)
|
|
||||||
{
|
|
||||||
return SupportedDamageTypes.Contains(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
public bool TryGetDamage(DamageClass @class, out int damage)
|
public bool TryGetDamage(DamageClass @class, out int damage)
|
||||||
{
|
{
|
||||||
if (!SupportsDamageClass(@class))
|
if (!SupportsDamageClass(@class))
|
||||||
=======
|
|
||||||
public bool TrySetDamage(DamageGroupPrototype group, int newValue)
|
|
||||||
{
|
|
||||||
if (!ApplicableDamageGroups.Contains(group))
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
public bool TrySetDamage(DamageGroupPrototype group, int newValue)
|
|
||||||
{
|
|
||||||
if (!ApplicableDamageGroups.Contains(group))
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
{
|
{
|
||||||
|
damage = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
damage = GetDamage(@class);
|
damage = GetDamage(@class);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -402,90 +156,44 @@ namespace Content.Shared.Damage.Components
|
|||||||
/// True if successful, false if this container does not support that type.
|
/// True if successful, false if this container does not support that type.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
public bool TrySetDamage(DamageType type, int newValue)
|
public bool TrySetDamage(DamageType type, int newValue)
|
||||||
=======
|
|
||||||
if (newValue < 0)
|
|
||||||
{
|
|
||||||
// invalid value
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var type in group.DamageTypes)
|
|
||||||
{
|
|
||||||
TrySetDamage(type, newValue);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TrySetAllDamage(int newValue)
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
if (newValue < 0)
|
|
||||||
{
|
|
||||||
// invalid value
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var type in group.DamageTypes)
|
|
||||||
{
|
|
||||||
TrySetDamage(type, newValue);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TrySetAllDamage(int newValue)
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
{
|
{
|
||||||
if (newValue < 0)
|
if (newValue < 0)
|
||||||
{
|
{
|
||||||
// invalid value
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
var damageClass = type.ToClass();
|
var damageClass = type.ToClass();
|
||||||
|
|
||||||
if (SupportedClasses.Contains(damageClass))
|
if (SupportedClasses.Contains(damageClass))
|
||||||
=======
|
|
||||||
if (SupportedTypes.Contains(type))
|
|
||||||
>>>>>>> Merge fixes
|
|
||||||
=======
|
|
||||||
foreach (var type in SupportedDamageTypes)
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
foreach (var type in SupportedDamageTypes)
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
{
|
{
|
||||||
TrySetDamage(type, newValue);
|
var old = _damageList[type] = newValue;
|
||||||
|
_damageList[type] = newValue;
|
||||||
|
|
||||||
|
var delta = newValue - old;
|
||||||
|
var datum = new DamageChangeData(type, newValue, delta);
|
||||||
|
var data = new List<DamageChangeData> {datum};
|
||||||
|
|
||||||
|
OnHealthChanged(data);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Heal(DamageType type)
|
public void Heal(DamageType type)
|
||||||
{
|
{
|
||||||
SetDamage(type, 0);
|
SetDamage(type, 0);
|
||||||
=======
|
|
||||||
return true;
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
return true;
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryChangeDamage(DamageTypePrototype type, int amount, bool ignoreDamageResistances = false)
|
public void Heal()
|
||||||
{
|
{
|
||||||
// Check if damage type is supported, and get the current value if it is.
|
foreach (var type in SupportedTypes)
|
||||||
if (!GetDamagePerType.TryGetValue(type, out var current))
|
|
||||||
{
|
{
|
||||||
return false;
|
Heal(type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
public bool ChangeDamage(
|
public bool ChangeDamage(
|
||||||
DamageType type,
|
DamageType type,
|
||||||
int amount,
|
int amount,
|
||||||
@@ -494,19 +202,13 @@ namespace Content.Shared.Damage.Components
|
|||||||
DamageChangeParams? extraParams = null)
|
DamageChangeParams? extraParams = null)
|
||||||
{
|
{
|
||||||
if (!SupportsDamageType(type))
|
if (!SupportsDamageType(type))
|
||||||
=======
|
|
||||||
if (amount == 0)
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
if (amount == 0)
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply resistances (does nothing if amount<0)
|
|
||||||
var finalDamage = amount;
|
var finalDamage = amount;
|
||||||
if (!ignoreDamageResistances)
|
|
||||||
|
if (!ignoreResistances)
|
||||||
{
|
{
|
||||||
finalDamage = Resistances.CalculateDamage(type, amount);
|
finalDamage = Resistances.CalculateDamage(type, amount);
|
||||||
}
|
}
|
||||||
@@ -514,23 +216,24 @@ namespace Content.Shared.Damage.Components
|
|||||||
if (finalDamage == 0)
|
if (finalDamage == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Are we healing below zero?
|
if (!_damageList.TryGetValue(type, out var current))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (current + finalDamage < 0)
|
if (current + finalDamage < 0)
|
||||||
{
|
{
|
||||||
if (current == 0)
|
if (current == 0)
|
||||||
// Damage type is supported, but there is nothing to do
|
|
||||||
return false;
|
return false;
|
||||||
|
_damageList[type] = 0;
|
||||||
// Cap healing down to zero
|
|
||||||
_damageDict[type] = 0;
|
|
||||||
finalDamage = -current;
|
finalDamage = -current;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_damageDict[type] = current + finalDamage;
|
_damageList[type] = current + finalDamage;
|
||||||
}
|
}
|
||||||
|
|
||||||
current = _damageDict[type];
|
current = _damageList[type];
|
||||||
|
|
||||||
var datum = new DamageChangeData(type, current, finalDamage);
|
var datum = new DamageChangeData(type, current, finalDamage);
|
||||||
var data = new List<DamageChangeData> {datum};
|
var data = new List<DamageChangeData> {datum};
|
||||||
@@ -540,8 +243,6 @@ namespace Content.Shared.Damage.Components
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
public bool ChangeDamage(DamageClass @class, int amount, bool ignoreResistances,
|
public bool ChangeDamage(DamageClass @class, int amount, bool ignoreResistances,
|
||||||
IEntity? source = null,
|
IEntity? source = null,
|
||||||
DamageChangeParams? extraParams = null)
|
DamageChangeParams? extraParams = null)
|
||||||
@@ -551,149 +252,98 @@ namespace Content.Shared.Damage.Components
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
var types = @class.ToTypes();
|
var types = @class.ToTypes();
|
||||||
=======
|
|
||||||
var types = damageGroup.DamageTypes.ToArray();
|
|
||||||
>>>>>>> Merge fixes
|
|
||||||
=======
|
|
||||||
public bool TryChangeDamage(DamageGroupPrototype group, int amount, bool ignoreDamageResistances = false)
|
|
||||||
{
|
|
||||||
var types = group.DamageTypes.ToArray();
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
public bool TryChangeDamage(DamageGroupPrototype group, int amount, bool ignoreDamageResistances = false)
|
|
||||||
{
|
|
||||||
var types = group.DamageTypes.ToArray();
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
if (amount < 0)
|
if (amount < 0)
|
||||||
{
|
{
|
||||||
// We are Healing. Keep track of how much we can hand out (with a better var name for readability).
|
// Changing multiple types is a bit more complicated. Might be a better way (formula?) to do this,
|
||||||
var availableHealing = -amount;
|
// but essentially just loops between each damage category until all healing is used up.
|
||||||
|
var healingLeft = -amount;
|
||||||
|
var healThisCycle = 1;
|
||||||
|
|
||||||
// Get total group damage.
|
// While we have healing left...
|
||||||
var damageToHeal = GetDamagePerApplicableGroup[group];
|
while (healingLeft > 0 && healThisCycle != 0)
|
||||||
<<<<<<< HEAD
|
|
||||||
|
|
||||||
// Is there any damage to even heal?
|
|
||||||
if (damageToHeal == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
=======
|
|
||||||
|
|
||||||
// Is there any damage to even heal?
|
|
||||||
if (damageToHeal == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
// If total healing is more than there is damage, just set to 0 and return.
|
|
||||||
if (damageToHeal <= availableHealing)
|
|
||||||
{
|
{
|
||||||
TrySetDamage(group, 0);
|
// Infinite loop fallback, if no healing was done in a cycle
|
||||||
|
// then exit
|
||||||
|
healThisCycle = 0;
|
||||||
|
|
||||||
|
int healPerType;
|
||||||
|
if (healingLeft < types.Count)
|
||||||
|
{
|
||||||
|
// Say we were to distribute 2 healing between 3
|
||||||
|
// this will distribute 1 to each (and stop after 2 are given)
|
||||||
|
healPerType = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Say we were to distribute 62 healing between 3
|
||||||
|
// this will distribute 20 to each, leaving 2 for next loop
|
||||||
|
healPerType = healingLeft / types.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var type in types)
|
||||||
|
{
|
||||||
|
var damage = GetDamage(type);
|
||||||
|
var healAmount = Math.Min(healingLeft, damage);
|
||||||
|
healAmount = Math.Min(healAmount, healPerType);
|
||||||
|
|
||||||
|
ChangeDamage(type, -healAmount, true);
|
||||||
|
healThisCycle += healAmount;
|
||||||
|
healingLeft -= healAmount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Partially heal each damage group
|
var damageLeft = amount;
|
||||||
int healing, damage;
|
|
||||||
|
while (damageLeft > 0)
|
||||||
|
{
|
||||||
|
int damagePerType;
|
||||||
|
|
||||||
|
if (damageLeft < types.Count)
|
||||||
|
{
|
||||||
|
damagePerType = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
damagePerType = damageLeft / types.Count;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var type in types)
|
foreach (var type in types)
|
||||||
{
|
{
|
||||||
if (!_damageDict.TryGetValue(type, out damage))
|
var damageAmount = Math.Min(damagePerType, damageLeft);
|
||||||
{
|
ChangeDamage(type, damageAmount, true);
|
||||||
// Damage Type is not supported. Continue without reducing availableHealing
|
damageLeft -= damageAmount;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply healing to the damage type. The healing amount may be zero if either damage==0, or if
|
|
||||||
// integer rounding made it zero (i.e., damage is small)
|
|
||||||
healing = (availableHealing * damage) / damageToHeal;
|
|
||||||
TryChangeDamage(type, -healing, ignoreDamageResistances);
|
|
||||||
|
|
||||||
// remove this damage type from the damage we consider for future loops, regardless of how much we
|
|
||||||
// actually healed this type.
|
|
||||||
damageToHeal -= damage;
|
|
||||||
availableHealing -= healing;
|
|
||||||
|
|
||||||
// If we now healed all the damage, exit. otherwise 1/0 and universe explodes.
|
|
||||||
if (damageToHeal == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Damage type is supported, there was damage to heal, and resistances were ignored
|
|
||||||
// --> Damage must have changed
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (amount > 0)
|
|
||||||
{
|
|
||||||
// Resistances may result in no actual damage change. We need to keep track if any damage got through.
|
|
||||||
var damageChanged = false;
|
|
||||||
|
|
||||||
// We are adding damage. Keep track of how much we can dish out (with a better var name for readability).
|
|
||||||
var availableDamage = amount;
|
|
||||||
|
|
||||||
// How many damage types do we have to distribute over?.
|
|
||||||
var numberDamageTypes = types.Length;
|
|
||||||
|
|
||||||
// Apply damage to each damage group
|
|
||||||
int damage;
|
|
||||||
foreach (var type in types)
|
|
||||||
{
|
|
||||||
// Distribute the remaining damage over the remaining damage types.
|
|
||||||
damage = availableDamage / numberDamageTypes;
|
|
||||||
|
|
||||||
// Try apply the damage type. If damage type is not supported, this has no effect.
|
|
||||||
// We also use the return value to check whether any damage has changed
|
|
||||||
damageChanged = TryChangeDamage(type, damage, ignoreDamageResistances) || damageChanged;
|
|
||||||
|
|
||||||
// regardless of whether we dealt damage, reduce the amount to distribute.
|
|
||||||
availableDamage -= damage;
|
|
||||||
numberDamageTypes -= 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
return damageChanged;
|
|
||||||
}
|
|
||||||
|
|
||||||
// amount==0 no damage change.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
public bool SetDamage(DamageType type, int newValue, IEntity? source = null, DamageChangeParams? extraParams = null)
|
public bool SetDamage(DamageType type, int newValue, IEntity? source = null, DamageChangeParams? extraParams = null)
|
||||||
=======
|
|
||||||
public bool SetDamage(DamageTypePrototype type, int newValue, IEntity? source = null, DamageChangeParams? extraParams = null)
|
|
||||||
>>>>>>> Fix Merge issues
|
|
||||||
=======
|
|
||||||
public bool TrySetDamage(DamageTypePrototype type, int newValue)
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
public bool TrySetDamage(DamageTypePrototype type, int newValue)
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
{
|
{
|
||||||
if (!_damageDict.TryGetValue(type, out var oldValue))
|
if (newValue >= TotalDamage)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newValue < 0)
|
if (newValue < 0)
|
||||||
{
|
{
|
||||||
// invalid value
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oldValue == newValue)
|
if (!_damageList.ContainsKey(type))
|
||||||
{
|
{
|
||||||
// No health change.
|
return false;
|
||||||
// But we are trying to set, not trying to change.
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_damageDict[type] = newValue;
|
var old = _damageList[type];
|
||||||
|
_damageList[type] = newValue;
|
||||||
|
|
||||||
var delta = newValue - oldValue;
|
var delta = newValue - old;
|
||||||
var datum = new DamageChangeData(type, 0, delta);
|
var datum = new DamageChangeData(type, 0, delta);
|
||||||
var data = new List<DamageChangeData> {datum};
|
var data = new List<DamageChangeData> {datum};
|
||||||
|
|
||||||
@@ -706,7 +356,7 @@ namespace Content.Shared.Damage.Components
|
|||||||
{
|
{
|
||||||
var data = new List<DamageChangeData>();
|
var data = new List<DamageChangeData>();
|
||||||
|
|
||||||
foreach (var type in SupportedDamageTypes)
|
foreach (var type in SupportedTypes)
|
||||||
{
|
{
|
||||||
var damage = GetDamage(type);
|
var damage = GetDamage(type);
|
||||||
var datum = new DamageChangeData(type, damage, 0);
|
var datum = new DamageChangeData(type, damage, 0);
|
||||||
@@ -722,32 +372,6 @@ namespace Content.Shared.Damage.Components
|
|||||||
OnHealthChanged(args);
|
OnHealthChanged(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
=======
|
|
||||||
private IReadOnlyDictionary<DamageGroupPrototype, int> damageListToDamageGroup(IReadOnlyDictionary<DamageTypePrototype, int> damagelist)
|
|
||||||
{
|
|
||||||
var damageGroupDict = new Dictionary<DamageGroupPrototype, int>();
|
|
||||||
int damageGroupSumDamage = 0;
|
|
||||||
int damageTypeDamage = 0;
|
|
||||||
foreach (var damageGroup in SupportedGroups)
|
|
||||||
{
|
|
||||||
damageGroupSumDamage = 0;
|
|
||||||
foreach (var damageType in SupportedTypes)
|
|
||||||
{
|
|
||||||
damageTypeDamage = 0;
|
|
||||||
damagelist.TryGetValue(damageType, out damageTypeDamage);
|
|
||||||
damageGroupSumDamage += damageTypeDamage;
|
|
||||||
}
|
|
||||||
damageGroupDict.Add(damageGroup, damageGroupSumDamage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return damageGroupDict;
|
|
||||||
}
|
|
||||||
|
|
||||||
>>>>>>> Merge fixes
|
|
||||||
=======
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
protected virtual void OnHealthChanged(DamageChangedEventArgs e)
|
protected virtual void OnHealthChanged(DamageChangedEventArgs e)
|
||||||
{
|
{
|
||||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, e);
|
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, e);
|
||||||
@@ -758,25 +382,11 @@ namespace Content.Shared.Damage.Components
|
|||||||
Dirty();
|
Dirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RadiationAct(float frameTime, SharedRadiationPulseComponent radiation)
|
void IRadiationAct.RadiationAct(float frameTime, SharedRadiationPulseComponent radiation)
|
||||||
{
|
{
|
||||||
var totalDamage = Math.Max((int)(frameTime * radiation.RadsPerSecond), 1);
|
var totalDamage = Math.Max((int)(frameTime * radiation.RadsPerSecond), 1);
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
ChangeDamage(DamageType.Radiation, totalDamage, false, radiation.Owner);
|
ChangeDamage(DamageType.Radiation, totalDamage, false, radiation.Owner);
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
foreach (var typeID in RadiationDamageTypeIDs)
|
|
||||||
{
|
|
||||||
TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(typeID), totalDamage);
|
|
||||||
}
|
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnExplosion(ExplosionEventArgs eventArgs)
|
public void OnExplosion(ExplosionEventArgs eventArgs)
|
||||||
@@ -789,99 +399,19 @@ namespace Content.Shared.Damage.Components
|
|||||||
_ => throw new ArgumentOutOfRangeException()
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
};
|
};
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
ChangeDamage(DamageType.Piercing, damage, false);
|
ChangeDamage(DamageType.Piercing, damage, false);
|
||||||
ChangeDamage(DamageType.Heat, damage, false);
|
ChangeDamage(DamageType.Heat, damage, false);
|
||||||
=======
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
foreach (var typeID in ExplosionDamageTypeIDs)
|
|
||||||
{
|
|
||||||
TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(typeID), damage);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Take a dictionary with <see cref="IPrototype"/> keys and return a dictionary using <see cref="IPrototype.ID"/> as keys
|
|
||||||
/// instead.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Useful when sending damage type and group prototypes dictionaries over the network.
|
|
||||||
/// </remarks>
|
|
||||||
public static IReadOnlyDictionary<string, int>
|
|
||||||
ConvertDictKeysToIDs<TPrototype>(IReadOnlyDictionary<TPrototype, int> prototypeDict)
|
|
||||||
where TPrototype : IPrototype
|
|
||||||
{
|
|
||||||
Dictionary<string, int> idDict = new(prototypeDict.Count);
|
|
||||||
foreach (var entry in prototypeDict)
|
|
||||||
{
|
|
||||||
idDict.Add(entry.Key.ID, entry.Value);
|
|
||||||
}
|
|
||||||
return idDict;
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert a dictionary with damage type keys to a dictionary of damage groups keys.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Takes a dictionary with damage types as keys and integers as values, and an iterable list of damage
|
|
||||||
/// groups. Returns a dictionary with damage group keys, with values calculated by adding up the values for
|
|
||||||
/// each damage type in that group. If a damage type is associated with more than one supported damage
|
|
||||||
/// group, it will contribute to the total of each group. Conversely, some damage types may not contribute
|
|
||||||
/// to the new dictionary if their associated group(s) are not in given list of groups.
|
|
||||||
/// </remarks>
|
|
||||||
public static IReadOnlyDictionary<DamageGroupPrototype, int>
|
|
||||||
DamageTypeDictToDamageGroupDict(IReadOnlyDictionary<DamageTypePrototype, int> damageTypeDict, IEnumerable<DamageGroupPrototype> groupKeys)
|
|
||||||
{
|
|
||||||
var damageGroupDict = new Dictionary<DamageGroupPrototype, int>();
|
|
||||||
int damageGroupSumDamage, damageTypeDamage;
|
|
||||||
// iterate over the list of group keys for our new dictionary
|
|
||||||
foreach (var group in groupKeys)
|
|
||||||
{
|
|
||||||
// For each damage type in this group, add up the damage present in the given dictionary
|
|
||||||
damageGroupSumDamage = 0;
|
|
||||||
foreach (var type in group.DamageTypes)
|
|
||||||
{
|
|
||||||
// if the damage type is in the dictionary, add it's damage to the group total.
|
|
||||||
if (damageTypeDict.TryGetValue(type, out damageTypeDamage))
|
|
||||||
{
|
|
||||||
damageGroupSumDamage += damageTypeDamage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
damageGroupDict.Add(group, damageGroupSumDamage);
|
|
||||||
}
|
|
||||||
return damageGroupDict;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public class DamageableComponentState : ComponentState
|
public class DamageableComponentState : ComponentState
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
public readonly Dictionary<DamageType, int> DamageList;
|
public readonly Dictionary<DamageType, int> DamageList;
|
||||||
|
|
||||||
public DamageableComponentState(Dictionary<DamageType, int> damageList)
|
public DamageableComponentState(Dictionary<DamageType, int> damageList)
|
||||||
=======
|
|
||||||
public readonly IReadOnlyDictionary<string, int> DamageDict;
|
|
||||||
|
|
||||||
public DamageableComponentState(IReadOnlyDictionary<string, int> damageDict)
|
|
||||||
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
public readonly IReadOnlyDictionary<string, int> DamageDict;
|
|
||||||
|
|
||||||
public DamageableComponentState(IReadOnlyDictionary<string, int> damageDict)
|
|
||||||
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
{
|
{
|
||||||
DamageDict = damageDict;
|
DamageList = damageList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,212 +8,75 @@ namespace Content.Shared.Damage.Components
|
|||||||
public interface IDamageableComponent : IComponent, IExAct
|
public interface IDamageableComponent : IComponent, IExAct
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The sum of all damages types in the DamageableComponent.
|
/// Sum of all damages taken.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int TotalDamage { get; }
|
int TotalDamage { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a dictionary of the damage in the container, indexed by applicable <see cref="DamageGroupPrototype"/>.
|
/// The amount of damage mapped by <see cref="DamageClass"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
IReadOnlyDictionary<DamageClass, int> DamageClasses { get; }
|
||||||
/// The values represent the sum of all damage in each group. If a supported damage type is a member of more than one group, it will contribute to each one.
|
|
||||||
/// Therefore, the sum of the values may be greater than the sum of the values in the dictionary returned by <see cref="GetDamagePerType"/>
|
|
||||||
/// </remarks>
|
|
||||||
IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerApplicableGroup { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns a dictionary of the damage in the container, indexed by fully supported instances of <see
|
/// The amount of damage mapped by <see cref="DamageType"/>.
|
||||||
/// cref="DamageGroupPrototype"/>.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
IReadOnlyDictionary<DamageType, int> DamageTypes { get; }
|
||||||
/// The values represent the sum of all damage in each group. As the damage container may have some damage
|
|
||||||
/// types that are not part of a fully supported damage group, the sum of the values may be less of the values
|
|
||||||
/// in the dictionary returned by <see cref="GetDamagePerType"/>. On the other hand, if a supported damage type
|
|
||||||
/// is a member of more than one group, it will contribute to each one. Therefore, the sum may also be greater
|
|
||||||
/// instead.
|
|
||||||
/// </remarks>
|
|
||||||
IReadOnlyDictionary<DamageGroupPrototype, int> GetDamagePerFullySupportedGroup { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
HashSet<DamageType> SupportedTypes { get; }
|
||||||
/// Returns a dictionary of the damage in the container, indexed by <see cref="DamageTypePrototype"/>.
|
|
||||||
/// </summary>
|
|
||||||
IReadOnlyDictionary<DamageTypePrototype, int> GetDamagePerType { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
HashSet<DamageClass> SupportedClasses { get; }
|
||||||
/// Like <see cref="GetDamagePerApplicableGroup"/>, but indexed by <see cref="DamageGroupPrototype.ID"/>
|
|
||||||
/// </summary>
|
|
||||||
IReadOnlyDictionary<string, int> GetDamagePerApplicableGroupIDs { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Like <see cref="GetDamagePerFullySupportedGroup"/>, but indexed by <see cref="DamageGroupPrototype.ID"/>
|
|
||||||
/// </summary>
|
|
||||||
IReadOnlyDictionary<string, int> GetDamagePerFullySupportedGroupIDs { get; }
|
|
||||||
<<<<<<< HEAD
|
|
||||||
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
bool SupportsDamageClass(DamageClass @class);
|
|
||||||
|
|
||||||
bool SupportsDamageType(DamageType type);
|
|
||||||
=======
|
|
||||||
/// <summary>
|
|
||||||
/// Like <see cref="GetDamagePerType"/>, but indexed by <see cref="DamageTypePrototype.ID"/>
|
|
||||||
/// </summary>
|
|
||||||
IReadOnlyDictionary<string, int> GetDamagePerTypeIDs { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Collection of damage types supported by this DamageableComponent.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Each of these damage types is fully supported. If any of these damage types is a
|
|
||||||
/// member of a damage group, these groups are represented in <see cref="ApplicableDamageGroups"></see>
|
|
||||||
/// </remarks>
|
|
||||||
HashSet<DamageTypePrototype> SupportedDamageTypes { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Collection of damage groups that are fully supported by DamageableComponent.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This describes what damage groups this damage container explicitly supports. It supports every damage type
|
|
||||||
/// contained in these damage groups. It may also support other damage types not in these groups. To see all
|
|
||||||
/// damage types <see cref="SupportedDamageTypes"/>, and to see all applicable damage groups <see
|
|
||||||
/// cref="ApplicableDamageGroups"/>.
|
|
||||||
/// </remarks>
|
|
||||||
HashSet<DamageGroupPrototype> FullySupportedDamageGroups { get; }
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Collection of damage groups that could apply damage to this DamageableComponent.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This describes what damage groups could have an effect on this damage container. However not every damage
|
|
||||||
/// group has to be fully supported. For example, the container may support ONLY the piercing damage type. It should
|
|
||||||
/// therefore be affected by instances of brute damage, but does not necessarily support blunt or slash damage.
|
|
||||||
/// For a list of supported damage types, see <see cref="SupportedDamageTypes"/>.
|
|
||||||
/// </remarks>
|
|
||||||
HashSet<DamageGroupPrototype> ApplicableDamageGroups { get; }
|
|
||||||
|
|
||||||
=======
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Like <see cref="GetDamagePerType"/>, but indexed by <see cref="DamageTypePrototype.ID"/>
|
|
||||||
/// </summary>
|
|
||||||
IReadOnlyDictionary<string, int> GetDamagePerTypeIDs { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Collection of damage types supported by this DamageableComponent.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Each of these damage types is fully supported. If any of these damage types is a
|
|
||||||
/// member of a damage group, these groups are represented in <see cref="ApplicableDamageGroups"></see>
|
|
||||||
/// </remarks>
|
|
||||||
HashSet<DamageTypePrototype> SupportedDamageTypes { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Collection of damage groups that are fully supported by DamageableComponent.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This describes what damage groups this damage container explicitly supports. It supports every damage type
|
|
||||||
/// contained in these damage groups. It may also support other damage types not in these groups. To see all
|
|
||||||
/// damage types <see cref="SupportedDamageTypes"/>, and to see all applicable damage groups <see
|
|
||||||
/// cref="ApplicableDamageGroups"/>.
|
|
||||||
/// </remarks>
|
|
||||||
HashSet<DamageGroupPrototype> FullySupportedDamageGroups { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Collection of damage groups that could apply damage to this DamageableComponent.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This describes what damage groups could have an effect on this damage container. However not every damage
|
|
||||||
/// group has to be fully supported. For example, the container may support ONLY the piercing damage type. It should
|
|
||||||
/// therefore be affected by instances of brute damage, but does not necessarily support blunt or slash damage.
|
|
||||||
/// For a list of supported damage types, see <see cref="SupportedDamageTypes"/>.
|
|
||||||
/// </remarks>
|
|
||||||
HashSet<DamageGroupPrototype> ApplicableDamageGroups { get; }
|
|
||||||
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The resistances of this component.
|
/// The resistances of this component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ResistanceSet Resistances { get; }
|
ResistanceSet Resistances { get; }
|
||||||
|
|
||||||
|
bool SupportsDamageClass(DamageClass @class);
|
||||||
|
|
||||||
|
bool SupportsDamageType(DamageType type);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to get the amount of damage of a type.
|
/// Gets the amount of damage of a type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">The type to get the damage of.</param>
|
/// <param name="type">The type to get the damage of.</param>
|
||||||
/// <param name="damage">The amount of damage of that type.</param>
|
/// <param name="damage">The amount of damage of that type.</param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// True if the given <see cref="type"/> is supported, false otherwise.
|
/// True if the given <see cref="type"/> is supported, false otherwise.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
bool TryGetDamage(DamageTypePrototype type, out int damage);
|
bool TryGetDamage(DamageType type, out int damage);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the amount of damage of a given type, or zero if it is not supported.
|
/// Gets the amount of damage of a class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int GetDamage(DamageTypePrototype type);
|
/// <param name="class">The class to get the damage of.</param>
|
||||||
|
/// <param name="damage">The amount of damage of that class.</param>
|
||||||
/// <summary>
|
|
||||||
<<<<<<< HEAD
|
|
||||||
/// Returns the amount of damage of a given type, or zero if it is not supported.
|
|
||||||
/// </summary>
|
|
||||||
int GetDamage(DamageTypePrototype type);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to get the total amount of damage in a damage group.
|
|
||||||
/// </summary>
|
|
||||||
=======
|
|
||||||
/// Tries to get the total amount of damage in a damage group.
|
|
||||||
/// </summary>
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
/// <param name="group">The group to get the damage of.</param>
|
|
||||||
/// <param name="damage">The amount of damage in that group.</param>
|
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// True if the given group is applicable to this container, false otherwise.
|
/// True if the given <see cref="@class"/> is supported, false otherwise.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
bool TryGetDamage(DamageClass @class, out int damage);
|
bool TryGetDamage(DamageClass @class, out int damage);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Changes the specified <see cref="DamageType"/>, applying
|
/// Changes the specified <see cref="DamageType"/>, applying
|
||||||
/// resistance values only if it is damage.
|
/// resistance values only if it is damage.
|
||||||
=======
|
|
||||||
bool TryGetDamage(DamageGroupPrototype group, out int damage);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the amount of damage present in an applicable group, or zero if no members are supported.
|
|
||||||
/// </summary>
|
|
||||||
int GetDamage(DamageGroupPrototype group);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to change the specified <see cref="DamageTypePrototype"/>, applying
|
|
||||||
/// resistance values only if it is dealing damage.
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
bool TryGetDamage(DamageGroupPrototype group, out int damage);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the amount of damage present in an applicable group, or zero if no members are supported.
|
|
||||||
/// </summary>
|
|
||||||
int GetDamage(DamageGroupPrototype group);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to change the specified <see cref="DamageTypePrototype"/>, applying
|
|
||||||
/// resistance values only if it is dealing damage.
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">Type of damage being changed.</param>
|
/// <param name="type">Type of damage being changed.</param>
|
||||||
/// <param name="amount">
|
/// <param name="amount">
|
||||||
/// Amount of damage being received (positive for damage, negative for heals).
|
/// Amount of damage being received (positive for damage, negative for heals).
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="ignoreDamageResistances">
|
/// <param name="ignoreResistances">
|
||||||
/// Whether or not to ignore resistances when taking damage.
|
/// Whether or not to ignore resistances.
|
||||||
/// Healing always ignores resistances, regardless of this input.
|
/// Healing always ignores resistances, regardless of this input.
|
||||||
/// </param>
|
/// </param>
|
||||||
|
/// <param name="source">
|
||||||
|
/// The entity that dealt or healed the damage, if any.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="extraParams">
|
||||||
|
/// Extra parameters that some components may require, such as a specific limb to target.
|
||||||
|
/// </param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// False if the given type is not supported or no damage change occurred; true otherwise.
|
/// False if the given type is not supported or improper
|
||||||
|
/// <see cref="DamageChangeParams"/> were provided; true otherwise.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
bool ChangeDamage(
|
bool ChangeDamage(
|
||||||
DamageType type,
|
DamageType type,
|
||||||
int amount,
|
int amount,
|
||||||
@@ -226,47 +89,24 @@ namespace Content.Shared.Damage.Components
|
|||||||
/// resistance values only if it is damage.
|
/// resistance values only if it is damage.
|
||||||
/// Spreads amount evenly between the <see cref="DamageType"></see>s
|
/// Spreads amount evenly between the <see cref="DamageType"></see>s
|
||||||
/// represented by that class.
|
/// represented by that class.
|
||||||
=======
|
|
||||||
bool TryChangeDamage(DamageTypePrototype type, int amount, bool ignoreDamageResistances = false);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to change damage of the specified <see cref="DamageGroupPrototype"/>, applying resistance values
|
|
||||||
/// only if it is damage.
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
bool TryChangeDamage(DamageTypePrototype type, int amount, bool ignoreDamageResistances = false);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to change damage of the specified <see cref="DamageGroupPrototype"/>, applying resistance values
|
|
||||||
/// only if it is damage.
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <param name="class">Class of damage being changed.</param>
|
||||||
/// <para>
|
|
||||||
/// If dealing damage, this spreads the damage change amount evenly between the <see
|
|
||||||
/// cref="DamageTypePrototype"></see>s in this group (subject to integer rounding). If only a subset of the
|
|
||||||
/// damage types in the group are actually supported, then the total damage dealt may be less than expected
|
|
||||||
/// (unsupported damage is ignored).
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// If healing damage, this spreads the damage change proportional to the current damage value of each <see
|
|
||||||
/// cref="DamageTypePrototype"></see> (subject to integer rounding). If there is less damage than is being
|
|
||||||
/// healed, some healing is wasted. Unsupported damage types do not waste healing.
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="group">group of damage being changed.</param>
|
|
||||||
/// <param name="amount">
|
/// <param name="amount">
|
||||||
/// Amount of damage being received (positive for damage, negative for heals).
|
/// Amount of damage being received (positive for damage, negative for heals).
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="ignoreDamageResistances">
|
/// <param name="ignoreResistances">
|
||||||
/// Whether to ignore resistances when taking damage. Healing always ignores resistances, regardless of this
|
/// Whether to ignore resistances.
|
||||||
/// input.
|
/// Healing always ignores resistances, regardless of this input.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="source">Entity that dealt or healed the damage, if any.</param>
|
||||||
|
/// <param name="extraParams">
|
||||||
|
/// Extra parameters that some components may require,
|
||||||
|
/// such as a specific limb to target.
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// Returns false if the given group is not applicable or no damage change occurred; true otherwise.
|
/// Returns false if the given class is not supported or improper
|
||||||
|
/// <see cref="DamageChangeParams"/> were provided; true otherwise.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
bool ChangeDamage(
|
bool ChangeDamage(
|
||||||
DamageClass @class,
|
DamageClass @class,
|
||||||
int amount,
|
int amount,
|
||||||
@@ -277,110 +117,28 @@ namespace Content.Shared.Damage.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Forcefully sets the specified <see cref="DamageType"/> to the given
|
/// Forcefully sets the specified <see cref="DamageType"/> to the given
|
||||||
/// value, ignoring resistance values.
|
/// value, ignoring resistance values.
|
||||||
=======
|
|
||||||
bool TryChangeDamage(DamageGroupPrototype group, int amount, bool ignoreDamageResistances = false);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
=======
|
|
||||||
bool TryChangeDamage(DamageGroupPrototype group, int amount, bool ignoreDamageResistances = false);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
/// Forcefully sets the specified <see cref="DamageTypePrototype"/> to the given value, ignoring resistance
|
|
||||||
/// values.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">Type of damage being set.</param>
|
/// <param name="type">Type of damage being changed.</param>
|
||||||
<<<<<<< HEAD
|
|
||||||
/// <param name="newValue">New damage value to be set.</param>
|
/// <param name="newValue">New damage value to be set.</param>
|
||||||
|
/// <param name="source">Entity that set the new damage value.</param>
|
||||||
|
/// <param name="extraParams">
|
||||||
|
/// Extra parameters that some components may require,
|
||||||
|
/// such as a specific limb to target.
|
||||||
|
/// </param>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// Returns false if a given type is not supported or a negative value is provided; true otherwise.
|
/// Returns false if the given type is not supported or improper
|
||||||
|
/// <see cref="DamageChangeParams"/> were provided; true otherwise.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
bool TrySetDamage(DamageTypePrototype type, int newValue);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Forcefully sets all damage types in a specified damage group using <see cref="TrySetDamage"></see>.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Note that the actual damage of this group will be equal to the given value times the number damage group
|
|
||||||
/// members that this container supports.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="group">Group of damage being set.</param>
|
|
||||||
/// <param name="newValue">New damage value to be set.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// Returns false if the given group is not applicable or a negative value is provided; true otherwise.
|
|
||||||
/// </returns>
|
|
||||||
bool TrySetDamage(DamageGroupPrototype group, int newValue);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets all supported damage types to specified value using <see cref="TrySetDamage"></see>.
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="newValue">New damage value to be set.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// Returns false if a negative value is provided; true otherwise.
|
|
||||||
/// </returns>
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
bool SetDamage(
|
bool SetDamage(
|
||||||
DamageType type,
|
DamageType type,
|
||||||
int newValue,
|
int newValue,
|
||||||
IEntity? source = null,
|
IEntity? source = null,
|
||||||
DamageChangeParams? extraParams = null);
|
DamageChangeParams? extraParams = null);
|
||||||
=======
|
|
||||||
bool TrySetAllDamage(int newValue);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns true if the given damage group is applicable to this damage container.
|
/// Sets all damage values to zero.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
=======
|
void Heal();
|
||||||
/// <param name="newValue">New damage value to be set.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// Returns false if a given type is not supported or a negative value is provided; true otherwise.
|
|
||||||
/// </returns>
|
|
||||||
bool TrySetDamage(DamageTypePrototype type, int newValue);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Forcefully sets all damage types in a specified damage group using <see cref="TrySetDamage"></see>.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Note that the actual damage of this group will be equal to the given value times the number damage group
|
|
||||||
/// members that this container supports.
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="group">Group of damage being set.</param>
|
|
||||||
/// <param name="newValue">New damage value to be set.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// Returns false if the given group is not applicable or a negative value is provided; true otherwise.
|
|
||||||
/// </returns>
|
|
||||||
bool TrySetDamage(DamageGroupPrototype group, int newValue);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets all supported damage types to specified value using <see cref="TrySetDamage"></see>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="newValue">New damage value to be set.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// Returns false if a negative value is provided; true otherwise.
|
|
||||||
/// </returns>
|
|
||||||
bool TrySetAllDamage(int newValue);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if the given damage group is applicable to this damage container.
|
|
||||||
/// </summary>
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
public bool IsApplicableDamageGroup(DamageGroupPrototype group);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if the given damage group is fully supported by this damage container.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsFullySupportedDamageGroup(DamageGroupPrototype group);
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns true if the given damage type is supported by this damage container.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsSupportedDamageType(DamageTypePrototype type);
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invokes the HealthChangedEvent with the current values of health.
|
/// Invokes the HealthChangedEvent with the current values of health.
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
@@ -9,287 +8,46 @@ using Robust.Shared.ViewVariables;
|
|||||||
namespace Content.Shared.Damage.Container
|
namespace Content.Shared.Damage.Container
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A damage container which can be used to specify support for various damage types.
|
/// Prototype for the DamageContainer class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
|
||||||
/// This is effectively just a list of damage types that can be specified in YAML files using both damage types
|
|
||||||
/// and damage groups. Currently this is only used to specify what damage types a <see
|
|
||||||
/// cref="Components.DamageableComponent"/> should support.
|
|
||||||
/// </remarks>
|
|
||||||
[Prototype("damageContainer")]
|
[Prototype("damageContainer")]
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public class DamageContainerPrototype : IPrototype, ISerializationHooks
|
public class DamageContainerPrototype : IPrototype, ISerializationHooks
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
[DataField("supportAll")] private bool _supportAll;
|
||||||
<<<<<<< refs/remotes/origin/master
|
[DataField("supportedClasses")] private HashSet<DamageClass> _supportedClasses = new();
|
||||||
=======
|
[DataField("supportedTypes")] private HashSet<DamageType> _supportedTypes = new();
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
// TODO NET 5 IReadOnlySet
|
||||||
private IPrototypeManager _prototypeManager = default!;
|
[ViewVariables] public IReadOnlyCollection<DamageClass> SupportedClasses => _supportedClasses;
|
||||||
|
|
||||||
|
[ViewVariables] public IReadOnlyCollection<DamageType> SupportedTypes => _supportedTypes;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
[DataField("id", required: true)]
|
[DataField("id", required: true)]
|
||||||
public string ID { get; } = default!;
|
public string ID { get; } = default!;
|
||||||
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether this DamageContainerPrototype will support ALL damage types and groups. If true,
|
|
||||||
/// ignore all other options.
|
|
||||||
/// </summary>
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
[DataField("supportAll")] private bool _supportAll;
|
|
||||||
|
|
||||||
[DataField("supportedGroups")] private HashSet<string> _supportedDamageGroupIDs = new();
|
|
||||||
[DataField("supportedTypes")] private HashSet<string> _supportedDamageTypeIDs = new();
|
|
||||||
|
|
||||||
private HashSet<DamageGroupPrototype> _applicableDamageGroups = new();
|
|
||||||
private HashSet<DamageGroupPrototype> _fullySupportedDamageGroups = new();
|
|
||||||
private HashSet<DamageTypePrototype> _supportedDamageTypes = new();
|
|
||||||
|
|
||||||
// TODO NET 5 IReadOnlySet
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
[ViewVariables] public IReadOnlyCollection<DamageClass> SupportedClasses => _supportedClasses;
|
|
||||||
|
|
||||||
[ViewVariables] public IReadOnlyCollection<DamageType> SupportedTypes => _supportedTypes;
|
|
||||||
=======
|
|
||||||
[ViewVariables] public IReadOnlyCollection<DamageGroupPrototype> SupportedDamageGroups => _supportedDamageGroups;
|
|
||||||
=======
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether this DamageContainerPrototype will support ALL damage types and groups. If true,
|
|
||||||
/// ignore all other options.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("supportAll")] private bool _supportAll;
|
|
||||||
|
|
||||||
[DataField("supportedGroups")] private HashSet<string> _supportedDamageGroupIDs = new();
|
|
||||||
[DataField("supportedTypes")] private HashSet<string> _supportedDamageTypeIDs = new();
|
|
||||||
|
|
||||||
private HashSet<DamageGroupPrototype> _applicableDamageGroups = new();
|
|
||||||
private HashSet<DamageGroupPrototype> _fullySupportedDamageGroups = new();
|
|
||||||
private HashSet<DamageTypePrototype> _supportedDamageTypes = new();
|
|
||||||
|
|
||||||
// TODO NET 5 IReadOnlySet
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Collection of damage groups that can affect this container.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This describes what damage groups can have an effect on this damage container. However not every damage
|
|
||||||
/// group has to be fully supported. For example, the container may support ONLY the piercing damage type.
|
|
||||||
/// It should therefore be affected by instances of brute group damage, but does not necessarily support
|
|
||||||
/// blunt or slash damage. If damage containers are only specified by supported damage groups, and every
|
|
||||||
/// damage type is in only one damage group, then SupportedDamageTypes should be equal to
|
|
||||||
/// ApplicableDamageGroups. For a list of supported damage types, see <see cref="SupportedDamageTypes"/>.
|
|
||||||
/// </remarks>
|
|
||||||
[ViewVariables] public IReadOnlyCollection<DamageGroupPrototype> ApplicableDamageGroups => _applicableDamageGroups;
|
|
||||||
|
|
||||||
=======
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Collection of damage groups that can affect this container.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This describes what damage groups can have an effect on this damage container. However not every damage
|
|
||||||
/// group has to be fully supported. For example, the container may support ONLY the piercing damage type.
|
|
||||||
/// It should therefore be affected by instances of brute group damage, but does not necessarily support
|
|
||||||
/// blunt or slash damage. If damage containers are only specified by supported damage groups, and every
|
|
||||||
/// damage type is in only one damage group, then SupportedDamageTypes should be equal to
|
|
||||||
/// ApplicableDamageGroups. For a list of supported damage types, see <see cref="SupportedDamageTypes"/>.
|
|
||||||
/// </remarks>
|
|
||||||
[ViewVariables] public IReadOnlyCollection<DamageGroupPrototype> ApplicableDamageGroups => _applicableDamageGroups;
|
|
||||||
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
/// <summary>
|
|
||||||
/// Collection of damage groups that are fully supported by this container.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This describes what damage groups this damage container explicitly supports. It supports every damage
|
|
||||||
/// type contained in these damage groups. It may also support other damage types not in these groups. To
|
|
||||||
/// see all damage types <see cref="SupportedDamageTypes"/>, and to see all applicable damage groups <see
|
|
||||||
/// cref="ApplicableDamageGroups"/>.
|
|
||||||
/// </remarks>
|
|
||||||
[ViewVariables] public IReadOnlyCollection<DamageGroupPrototype> FullySupportedDamageGroups => _fullySupportedDamageGroups;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Collection of damage types supported by this container.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Each of these damage types is fully supported by the DamageContainer. If any of these damage types is a
|
|
||||||
/// member of a damage group, these groups are added to <see cref="ApplicableDamageGroups"></see>
|
|
||||||
/// </remarks>
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
[ViewVariables] public IReadOnlyCollection<DamageTypePrototype> SupportedDamageTypes => _supportedDamageTypes;
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
[ViewVariables] public IReadOnlyCollection<DamageTypePrototype> SupportedDamageTypes => _supportedDamageTypes;
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
void ISerializationHooks.AfterDeserialization()
|
||||||
{
|
{
|
||||||
_prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
|
||||||
|
|
||||||
if (_supportAll)
|
if (_supportAll)
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
_supportedClasses.UnionWith(Enum.GetValues<DamageClass>());
|
_supportedClasses.UnionWith(Enum.GetValues<DamageClass>());
|
||||||
_supportedTypes.UnionWith(Enum.GetValues<DamageType>());
|
_supportedTypes.UnionWith(Enum.GetValues<DamageType>());
|
||||||
=======
|
|
||||||
foreach (var DamageGroup in _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
|
||||||
=======
|
|
||||||
foreach (var group in _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
=======
|
|
||||||
foreach (var group in _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
{
|
|
||||||
_applicableDamageGroups.Add(group);
|
|
||||||
_fullySupportedDamageGroups.Add(group);
|
|
||||||
}
|
|
||||||
foreach (var type in _prototypeManager.EnumeratePrototypes<DamageTypePrototype>())
|
|
||||||
{
|
|
||||||
_supportedDamageTypes.Add(type);
|
|
||||||
}
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
|
|
||||||
>>>>>>> fix a few bugs
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add fully supported damage groups
|
foreach (var supportedClass in _supportedClasses)
|
||||||
foreach (var groupID in _supportedDamageGroupIDs)
|
|
||||||
{
|
{
|
||||||
var group = _prototypeManager.Index<DamageGroupPrototype>(groupID);
|
|
||||||
_fullySupportedDamageGroups.Add(group);
|
|
||||||
foreach (var type in group.DamageTypes)
|
|
||||||
{
|
|
||||||
_supportedDamageTypes.Add(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add individual damage types, that are either not part of a group, or whose groups are (possibly) not fully supported
|
|
||||||
foreach (var supportedTypeID in _supportedDamageTypeIDs)
|
|
||||||
{
|
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
foreach (var supportedType in supportedClass.ToTypes())
|
foreach (var supportedType in supportedClass.ToTypes())
|
||||||
=======
|
|
||||||
var type = _prototypeManager.Index<DamageTypePrototype>(supportedTypeID);
|
|
||||||
_supportedDamageTypes.Add(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For whatever reason, someone may have listed all members of a group as supported instead of just listing
|
|
||||||
// the group as supported. Check for this.
|
|
||||||
foreach (var group in _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
|
||||||
{
|
{
|
||||||
if (_fullySupportedDamageGroups.Contains(group))
|
_supportedTypes.Add(supportedType);
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// The group is not in the list of fully supported groups. Should it be?
|
|
||||||
var allMembersSupported = true;
|
|
||||||
foreach (var type in group.DamageTypes)
|
|
||||||
{
|
|
||||||
if (!_supportedDamageTypes.Contains(type))
|
|
||||||
{
|
|
||||||
// not all members are supported
|
|
||||||
allMembersSupported = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (allMembersSupported) {
|
|
||||||
// All members are supported. The silly goose should have just used a damage group.
|
|
||||||
_fullySupportedDamageGroups.Add(group);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For each supported damage type, check whether it is in any existing group, If it is add it to _applicableDamageGroups
|
foreach (var originalType in _supportedTypes)
|
||||||
foreach (var type in _supportedDamageTypes)
|
|
||||||
{
|
{
|
||||||
foreach (var group in _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
_supportedClasses.Add(originalType.ToClass());
|
||||||
{
|
|
||||||
if (group.DamageTypes.Contains(type))
|
|
||||||
{
|
|
||||||
_applicableDamageGroups.Add(group);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
=======
|
|
||||||
var DamageGroup= _prototypeManager.Index<DamageGroupPrototype>(supportedClassID);
|
|
||||||
_supportedDamageGroups.Add(DamageGroup);
|
|
||||||
foreach (var DamageType in DamageGroup.DamageTypes)
|
|
||||||
=======
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add fully supported damage groups
|
|
||||||
foreach (var groupID in _supportedDamageGroupIDs)
|
|
||||||
{
|
|
||||||
var group = _prototypeManager.Index<DamageGroupPrototype>(groupID);
|
|
||||||
_fullySupportedDamageGroups.Add(group);
|
|
||||||
foreach (var type in group.DamageTypes)
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
{
|
|
||||||
_supportedDamageTypes.Add(type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
>>>>>>> fix a few bugs
|
|
||||||
=======
|
|
||||||
// Add individual damage types, that are either not part of a group, or whose groups are (possibly) not fully supported
|
|
||||||
foreach (var supportedTypeID in _supportedDamageTypeIDs)
|
|
||||||
{
|
|
||||||
var type = _prototypeManager.Index<DamageTypePrototype>(supportedTypeID);
|
|
||||||
_supportedDamageTypes.Add(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For whatever reason, someone may have listed all members of a group as supported instead of just listing
|
|
||||||
// the group as supported. Check for this.
|
|
||||||
foreach (var group in _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
|
||||||
{
|
|
||||||
if (_fullySupportedDamageGroups.Contains(group))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// The group is not in the list of fully supported groups. Should it be?
|
|
||||||
var allMembersSupported = true;
|
|
||||||
foreach (var type in group.DamageTypes)
|
|
||||||
{
|
|
||||||
if (!_supportedDamageTypes.Contains(type))
|
|
||||||
{
|
|
||||||
// not all members are supported
|
|
||||||
allMembersSupported = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (allMembersSupported) {
|
|
||||||
// All members are supported. The silly goose should have just used a damage group.
|
|
||||||
_fullySupportedDamageGroups.Add(group);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each supported damage type, check whether it is in any existing group, If it is add it to _applicableDamageGroups
|
|
||||||
foreach (var type in _supportedDamageTypes)
|
|
||||||
{
|
|
||||||
foreach (var group in _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
|
||||||
{
|
|
||||||
if (group.DamageTypes.Contains(type))
|
|
||||||
{
|
|
||||||
_applicableDamageGroups.Add(group);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,14 @@
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data class with information on how the value of a
|
/// Data class with information on how the value of a
|
||||||
/// single <see cref="DamageTypePrototype"/> has changed.
|
/// single <see cref="DamageType"/> has changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public struct DamageChangeData
|
public struct DamageChangeData
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Type of damage that changed.
|
/// Type of damage that changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DamageTypePrototype Type;
|
public DamageType Type;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The new current value for that damage.
|
/// The new current value for that damage.
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int Delta;
|
public int Delta;
|
||||||
|
|
||||||
public DamageChangeData(DamageTypePrototype type, int newValue, int delta)
|
public DamageChangeData(DamageType type, int newValue, int delta)
|
||||||
{
|
{
|
||||||
Type = type;
|
Type = type;
|
||||||
NewValue = newValue;
|
NewValue = newValue;
|
||||||
|
|||||||
18
Content.Shared/Damage/DamageChangeParams.cs
Normal file
18
Content.Shared/Damage/DamageChangeParams.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Damage.Components;
|
||||||
|
|
||||||
|
namespace Content.Shared.Damage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Data class with information on how to damage a
|
||||||
|
/// <see cref="IDamageableComponent"/>.
|
||||||
|
/// While not necessary to damage for all instances, classes such as
|
||||||
|
/// <see cref="SharedBodyComponent"/> may require it for extra data
|
||||||
|
/// (such as selecting which limb to target).
|
||||||
|
/// </summary>
|
||||||
|
// TODO BODY: Remove and pretend it never existed
|
||||||
|
public class DamageChangeParams : EventArgs
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ namespace Content.Shared.Damage
|
|||||||
Data = data;
|
Data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DamageChangedEventArgs(IDamageableComponent damageable, DamageTypePrototype type, int newValue, int delta)
|
public DamageChangedEventArgs(IDamageableComponent damageable, DamageType type, int newValue, int delta)
|
||||||
{
|
{
|
||||||
Damageable = damageable;
|
Damageable = damageable;
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ namespace Content.Shared.Damage
|
|||||||
public IDamageableComponent Damageable { get; }
|
public IDamageableComponent Damageable { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List containing data on each <see cref="DamageTypePrototype"/> that was changed.
|
/// List containing data on each <see cref="DamageType"/> that was changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyList<DamageChangeData> Data { get; }
|
public IReadOnlyList<DamageChangeData> Data { get; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Content.Shared.Damage
|
|||||||
Data = data;
|
Data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DamageChangedMessage(IDamageableComponent damageable, DamageTypePrototype type, int newValue, int delta)
|
public DamageChangedMessage(IDamageableComponent damageable, DamageType type, int newValue, int delta)
|
||||||
{
|
{
|
||||||
Damageable = damageable;
|
Damageable = damageable;
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ namespace Content.Shared.Damage
|
|||||||
public IDamageableComponent Damageable { get; }
|
public IDamageableComponent Damageable { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// List containing data on each <see cref="DamageTypePrototype"/> that was changed.
|
/// List containing data on each <see cref="DamageType"/> that was changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyList<DamageChangeData> Data { get; }
|
public IReadOnlyList<DamageChangeData> Data { get; }
|
||||||
|
|
||||||
|
|||||||
38
Content.Shared/Damage/DamageClass.cs
Normal file
38
Content.Shared/Damage/DamageClass.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Damage
|
||||||
|
{
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum DamageClass
|
||||||
|
{
|
||||||
|
Brute,
|
||||||
|
Burn,
|
||||||
|
Toxin,
|
||||||
|
Airloss,
|
||||||
|
Genetic
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DamageClassExtensions
|
||||||
|
{
|
||||||
|
public static ImmutableList<DamageType> ToTypes(this DamageClass @class)
|
||||||
|
{
|
||||||
|
return DamageSystem.ClassToType[@class];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<DamageClass, T> ToNewDictionary<T>() where T : struct
|
||||||
|
{
|
||||||
|
return Enum.GetValues(typeof(DamageClass))
|
||||||
|
.Cast<DamageClass>()
|
||||||
|
.ToDictionary(@class => @class, _ => default(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<DamageClass, int> ToNewDictionary()
|
||||||
|
{
|
||||||
|
return ToNewDictionary<int>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Shared.Damage.DamageContainer
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Prototype for the DamageContainer class.
|
|
||||||
/// </summary>
|
|
||||||
[Prototype("damageContainer")]
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public class DamageContainerPrototype : IPrototype, ISerializationHooks
|
|
||||||
{
|
|
||||||
[Dependency]
|
|
||||||
private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
|
|
||||||
[DataField("supportAll")] private bool _supportAll;
|
|
||||||
[DataField("supportedClasses")] private HashSet<string> _supportedDamageGroupsButAsStrings = new();
|
|
||||||
[DataField("supportedTypes")] private HashSet<string> _supportedDamageTypesButAsStrings = new();
|
|
||||||
|
|
||||||
private HashSet<DamageGroupPrototype> _supportedDamageGroups = new();
|
|
||||||
private HashSet<DamageTypePrototype> _supportedDamageTypes = new();
|
|
||||||
|
|
||||||
// TODO NET 5 IReadOnlySet
|
|
||||||
[ViewVariables] public IReadOnlyCollection<DamageGroupPrototype> SupportedDamageGroups => _supportedDamageGroups;
|
|
||||||
|
|
||||||
[ViewVariables] public IReadOnlyCollection<DamageTypePrototype> SupportedDamageTypes => _supportedDamageTypes;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("id", required: true)]
|
|
||||||
public string ID { get; } = default!;
|
|
||||||
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
|
||||||
{
|
|
||||||
if (_supportAll)
|
|
||||||
{
|
|
||||||
// _supportedClasses.UnionWith(Enum.GetValues<DamageClass>());
|
|
||||||
//_supportedTypes.UnionWith(Enum.GetValues<DamageType>());
|
|
||||||
|
|
||||||
foreach (var DamageGroup in _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
|
||||||
{
|
|
||||||
_supportedDamageGroups.Add(DamageGroup);
|
|
||||||
foreach (var SupportedDamageType in DamageGroup.DamageTypes)
|
|
||||||
{
|
|
||||||
_supportedDamageTypes.Add(SupportedDamageType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var supportedClassID in _supportedDamageGroupsButAsStrings)
|
|
||||||
{
|
|
||||||
var resolvedDamageGroup= _prototypeManager.Index<DamageGroupPrototype>(supportedClassID);
|
|
||||||
foreach (var supportedType in resolvedDamageGroup.DamageTypes)
|
|
||||||
{
|
|
||||||
_supportedDamageTypes.Add(supportedType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//reverse link type to group because smug said so ask him
|
|
||||||
foreach (var originalType in _supportedDamageTypes)
|
|
||||||
{
|
|
||||||
_supportedDamageTypes.Add(originalType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
|
|
||||||
namespace Content.Shared.Damage
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A Group of <see cref="DamageTypePrototype"/>s.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// These groups can be used to specify supported damage types of a <see
|
|
||||||
/// cref="Container.DamageContainerPrototype"/>, or to change/get/set damage in a <see
|
|
||||||
/// cref="Components.DamageableComponent"/>.
|
|
||||||
/// </remarks>
|
|
||||||
[Prototype("damageGroup")]
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public class DamageGroupPrototype : IPrototype, ISerializationHooks
|
|
||||||
{
|
|
||||||
private IPrototypeManager _prototypeManager = default!;
|
|
||||||
|
|
||||||
[DataField("id", required: true)] public string ID { get; } = default!;
|
|
||||||
|
|
||||||
[DataField("damageTypes", required: true)]
|
|
||||||
public List<string> TypeIDs { get; } = default!;
|
|
||||||
|
|
||||||
public HashSet<DamageTypePrototype> DamageTypes { get; } = new();
|
|
||||||
|
|
||||||
// Create set of damage types
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
|
||||||
{
|
|
||||||
_prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
|
||||||
|
|
||||||
foreach (var typeID in TypeIDs)
|
|
||||||
{
|
|
||||||
DamageTypes.Add(_prototypeManager.Index<DamageTypePrototype>(typeID));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,6 +8,59 @@ namespace Content.Shared.Damage
|
|||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public class DamageSystem : EntitySystem
|
public class DamageSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
public static ImmutableDictionary<DamageClass, ImmutableList<DamageType>> ClassToType { get; } = DefaultClassToType();
|
||||||
|
|
||||||
|
public static ImmutableDictionary<DamageType, DamageClass> TypeToClass { get; } = DefaultTypeToClass();
|
||||||
|
|
||||||
|
private static ImmutableDictionary<DamageClass, ImmutableList<DamageType>> DefaultClassToType()
|
||||||
|
{
|
||||||
|
return new Dictionary<DamageClass, ImmutableList<DamageType>>
|
||||||
|
{
|
||||||
|
[DamageClass.Brute] = new List<DamageType>
|
||||||
|
{
|
||||||
|
DamageType.Blunt,
|
||||||
|
DamageType.Slash,
|
||||||
|
DamageType.Piercing
|
||||||
|
}.ToImmutableList(),
|
||||||
|
[DamageClass.Burn] = new List<DamageType>
|
||||||
|
{
|
||||||
|
DamageType.Heat,
|
||||||
|
DamageType.Shock,
|
||||||
|
DamageType.Cold
|
||||||
|
}.ToImmutableList(),
|
||||||
|
[DamageClass.Toxin] = new List<DamageType>
|
||||||
|
{
|
||||||
|
DamageType.Poison,
|
||||||
|
DamageType.Radiation
|
||||||
|
}.ToImmutableList(),
|
||||||
|
[DamageClass.Airloss] = new List<DamageType>
|
||||||
|
{
|
||||||
|
DamageType.Asphyxiation,
|
||||||
|
DamageType.Bloodloss
|
||||||
|
}.ToImmutableList(),
|
||||||
|
[DamageClass.Genetic] = new List<DamageType>
|
||||||
|
{
|
||||||
|
DamageType.Cellular
|
||||||
|
}.ToImmutableList()
|
||||||
|
}.ToImmutableDictionary();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ImmutableDictionary<DamageType, DamageClass> DefaultTypeToClass()
|
||||||
|
{
|
||||||
|
return new Dictionary<DamageType, DamageClass>
|
||||||
|
{
|
||||||
|
{DamageType.Blunt, DamageClass.Brute},
|
||||||
|
{DamageType.Slash, DamageClass.Brute},
|
||||||
|
{DamageType.Piercing, DamageClass.Brute},
|
||||||
|
{DamageType.Heat, DamageClass.Burn},
|
||||||
|
{DamageType.Shock, DamageClass.Burn},
|
||||||
|
{DamageType.Cold, DamageClass.Burn},
|
||||||
|
{DamageType.Poison, DamageClass.Toxin},
|
||||||
|
{DamageType.Radiation, DamageClass.Toxin},
|
||||||
|
{DamageType.Asphyxiation, DamageClass.Airloss},
|
||||||
|
{DamageType.Bloodloss, DamageClass.Airloss},
|
||||||
|
{DamageType.Cellular, DamageClass.Genetic}
|
||||||
|
}.ToImmutableDictionary();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
63
Content.Shared/Damage/DamageType.cs
Normal file
63
Content.Shared/Damage/DamageType.cs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Damage
|
||||||
|
{
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum DamageType
|
||||||
|
{
|
||||||
|
Blunt,
|
||||||
|
Slash,
|
||||||
|
Piercing,
|
||||||
|
Heat,
|
||||||
|
Shock,
|
||||||
|
Cold,
|
||||||
|
Poison,
|
||||||
|
Radiation,
|
||||||
|
Asphyxiation,
|
||||||
|
Bloodloss,
|
||||||
|
Cellular
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DamageTypeExtensions
|
||||||
|
{
|
||||||
|
public static DamageClass ToClass(this DamageType type)
|
||||||
|
{
|
||||||
|
return DamageSystem.TypeToClass[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<DamageType, T> ToNewDictionary<T>() where T : struct
|
||||||
|
{
|
||||||
|
return Enum.GetValues(typeof(DamageType))
|
||||||
|
.Cast<DamageType>()
|
||||||
|
.ToDictionary(type => type, _ => default(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<DamageType, int> ToNewDictionary()
|
||||||
|
{
|
||||||
|
return ToNewDictionary<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dictionary<DamageClass, int> ToClassDictionary(this IReadOnlyDictionary<DamageType, int> types)
|
||||||
|
{
|
||||||
|
var classes = DamageClassExtensions.ToNewDictionary();
|
||||||
|
|
||||||
|
foreach (var @class in classes.Keys.ToList())
|
||||||
|
{
|
||||||
|
foreach (var type in @class.ToTypes())
|
||||||
|
{
|
||||||
|
if (!types.TryGetValue(type, out var damage))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
classes[@class] += damage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
|
|
||||||
namespace Content.Shared.Damage
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A single damage type. These types are grouped together in <see cref="DamageGroupPrototype"/>s.
|
|
||||||
/// </summary>
|
|
||||||
[Prototype("damageType")]
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public class DamageTypePrototype : IPrototype
|
|
||||||
{
|
|
||||||
[DataField("id", required: true)]
|
|
||||||
public string ID { get; } = default!;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Shared.Damage.ResistanceSet
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Set of resistances used by damageable objects.
|
|
||||||
/// Each <see cref="DamageTypePrototype"/> has a multiplier and flat damage
|
|
||||||
/// reduction value.
|
|
||||||
/// </summary>
|
|
||||||
[NetSerializable]
|
|
||||||
[Serializable]
|
|
||||||
public class ResistanceSet : ISerializationHooks
|
|
||||||
{
|
|
||||||
[ViewVariables]
|
|
||||||
private Dictionary<DamageTypePrototype, ResistanceSetSettings> _resistances = new();
|
|
||||||
|
|
||||||
public ResistanceSet()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AfterDeserialization()
|
|
||||||
{
|
|
||||||
var _prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
|
||||||
foreach (var damageType in _prototypeManager.EnumeratePrototypes<DamageTypePrototype>())
|
|
||||||
{
|
|
||||||
_resistances.Add(damageType, new ResistanceSetSettings(1f, 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ResistanceSet(ResistanceSetPrototype data)
|
|
||||||
{
|
|
||||||
ID = data.ID;
|
|
||||||
_resistances = data.Resistances;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ID { get; } = string.Empty;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adjusts input damage with the resistance set values.
|
|
||||||
/// Only applies reduction if the amount is damage (positive), not
|
|
||||||
/// healing (negative).
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="damageType">Type of damage.</param>
|
|
||||||
/// <param name="amount">Incoming amount of damage.</param>
|
|
||||||
public int CalculateDamage(DamageTypePrototype damageType, int amount)
|
|
||||||
{
|
|
||||||
if (amount > 0) // Only apply reduction if it's healing, not damage.
|
|
||||||
{
|
|
||||||
amount -= _resistances[damageType].FlatReduction;
|
|
||||||
|
|
||||||
if (amount <= 0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
amount = (int) Math.Ceiling(amount * _resistances[damageType].Coefficient);
|
|
||||||
|
|
||||||
return amount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Settings for a specific damage type in a resistance set. Flat reduction is applied before the coefficient.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public struct ResistanceSetSettings
|
|
||||||
{
|
|
||||||
[ViewVariables] public float Coefficient { get; private set; }
|
|
||||||
|
|
||||||
[ViewVariables] public int FlatReduction { get; private set; }
|
|
||||||
|
|
||||||
public ResistanceSetSettings(float coefficient, int flatReduction)
|
|
||||||
{
|
|
||||||
Coefficient = coefficient;
|
|
||||||
FlatReduction = flatReduction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Shared.Damage.ResistanceSet
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Prototype for the BodyPart class.
|
|
||||||
/// </summary>
|
|
||||||
[Prototype("resistanceSet")]
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public class ResistanceSetPrototype : IPrototype, ISerializationHooks
|
|
||||||
{
|
|
||||||
[Dependency]
|
|
||||||
private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[field: DataField("coefficients")]
|
|
||||||
public Dictionary<string, float> Coefficients { get; } = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[field: DataField("flatReductions")]
|
|
||||||
public Dictionary<string, int> FlatReductions { get; } = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public Dictionary<DamageTypePrototype, float> Resistances { get; private set; } = new();
|
|
||||||
[ViewVariables]
|
|
||||||
public Dictionary<DamageTypePrototype, int> FlatResistances { get; private set; } = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("id", required: true)]
|
|
||||||
public string ID { get; } = default!;
|
|
||||||
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
|
||||||
{
|
|
||||||
Resistances = new Dictionary<DamageTypePrototype, float>();
|
|
||||||
FlatResistances = new Dictionary<DamageTypePrototype, int>();
|
|
||||||
|
|
||||||
foreach (var KeyValuePair in Coefficients)
|
|
||||||
{
|
|
||||||
var resolvedDamageType = _prototypeManager.Index<DamageTypePrototype>(KeyValuePair.Key);
|
|
||||||
Resistances.Add(resolvedDamageType,KeyValuePair.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var KeyValuePair in FlatReductions)
|
|
||||||
{
|
|
||||||
var resolvedDamageType = _prototypeManager.Index<DamageTypePrototype>(KeyValuePair.Key);
|
|
||||||
Resistances.Add(resolvedDamageType,KeyValuePair.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var damageType in _prototypeManager.EnumeratePrototypes<DamageTypePrototype>())
|
|
||||||
{
|
|
||||||
// Resistances.Add(damageType, new ResistanceSetSettings(Coefficients[damageType], FlatReductions[damageType]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
@@ -9,31 +7,18 @@ namespace Content.Shared.Damage.Resistances
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Set of resistances used by damageable objects.
|
/// Set of resistances used by damageable objects.
|
||||||
/// Each <see cref="DamageTypePrototype"/> has a multiplier and flat damage
|
/// Each <see cref="DamageType"/> has a multiplier and flat damage
|
||||||
/// reduction value.
|
/// reduction value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public class ResistanceSet
|
public class ResistanceSet
|
||||||
{
|
{
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public string? ID { get; } = string.Empty;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public Dictionary<DamageTypePrototype, ResistanceSetSettings> Resistances { get; } = new();
|
|
||||||
|
|
||||||
public ResistanceSet()
|
public ResistanceSet()
|
||||||
{
|
{
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
foreach (var damageType in (DamageType[]) Enum.GetValues(typeof(DamageType)))
|
foreach (var damageType in (DamageType[]) Enum.GetValues(typeof(DamageType)))
|
||||||
{
|
{
|
||||||
Resistances.Add(damageType, new ResistanceSetSettings(1f, 0));
|
Resistances.Add(damageType, new ResistanceSetSettings(1f, 0));
|
||||||
}
|
}
|
||||||
=======
|
|
||||||
>>>>>>> Merge fixes
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResistanceSet(ResistanceSetPrototype data)
|
public ResistanceSet(ResistanceSetPrototype data)
|
||||||
@@ -42,6 +27,12 @@ namespace Content.Shared.Damage.Resistances
|
|||||||
Resistances = data.Resistances;
|
Resistances = data.Resistances;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public string ID { get; } = string.Empty;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public Dictionary<DamageType, ResistanceSetSettings> Resistances { get; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adjusts input damage with the resistance set values.
|
/// Adjusts input damage with the resistance set values.
|
||||||
/// Only applies reduction if the amount is damage (positive), not
|
/// Only applies reduction if the amount is damage (positive), not
|
||||||
@@ -49,18 +40,11 @@ namespace Content.Shared.Damage.Resistances
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="damageType">Type of damage.</param>
|
/// <param name="damageType">Type of damage.</param>
|
||||||
/// <param name="amount">Incoming amount of damage.</param>
|
/// <param name="amount">Incoming amount of damage.</param>
|
||||||
public int CalculateDamage(DamageTypePrototype damageType, int amount)
|
public int CalculateDamage(DamageType damageType, int amount)
|
||||||
{
|
{
|
||||||
|
|
||||||
// Do nothing if the damage type is not specified in resistance set.
|
|
||||||
if (!Resistances.TryGetValue(damageType, out var resistance))
|
|
||||||
{
|
|
||||||
return amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (amount > 0) // Only apply reduction if it's healing, not damage.
|
if (amount > 0) // Only apply reduction if it's healing, not damage.
|
||||||
{
|
{
|
||||||
amount -= resistance.FlatReduction;
|
amount -= Resistances[damageType].FlatReduction;
|
||||||
|
|
||||||
if (amount <= 0)
|
if (amount <= 0)
|
||||||
{
|
{
|
||||||
@@ -68,9 +52,25 @@ namespace Content.Shared.Damage.Resistances
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
amount = (int) Math.Ceiling(amount * resistance.Coefficient);
|
amount = (int) Math.Ceiling(amount * Resistances[damageType].Coefficient);
|
||||||
|
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Settings for a specific damage type in a resistance set. Flat reduction is applied before the coefficient.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public readonly struct ResistanceSetSettings
|
||||||
|
{
|
||||||
|
[ViewVariables] public readonly float Coefficient;
|
||||||
|
[ViewVariables] public readonly int FlatReduction;
|
||||||
|
|
||||||
|
public ResistanceSetSettings(float coefficient, int flatReduction)
|
||||||
|
{
|
||||||
|
Coefficient = coefficient;
|
||||||
|
FlatReduction = flatReduction;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,5 @@
|
|||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
using System;
|
using System;
|
||||||
=======
|
|
||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using System.CodeDom;
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
using System;
|
|
||||||
using System.CodeDom;
|
|
||||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
=======
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
>>>>>>> Merge fixes
|
|
||||||
=======
|
|
||||||
using System;
|
|
||||||
using System.CodeDom;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
@@ -37,90 +15,28 @@ namespace Content.Shared.Damage.Resistances
|
|||||||
public class ResistanceSetPrototype : IPrototype, ISerializationHooks
|
public class ResistanceSetPrototype : IPrototype, ISerializationHooks
|
||||||
{
|
{
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
<<<<<<< HEAD
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
[DataField("coefficients")]
|
[DataField("coefficients")]
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
public Dictionary<DamageType, float> Coefficients { get; } = new();
|
public Dictionary<DamageType, float> Coefficients { get; } = new();
|
||||||
=======
|
|
||||||
[DataField("id", required: true)]
|
|
||||||
public string ID { get; } = default!;
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
[DataField("coefficients", required: true)]
|
[DataField("flatReductions")]
|
||||||
private Dictionary<string, float> coefficients { get; } = new();
|
public Dictionary<DamageType, int> FlatReductions { get; } = new();
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
<<<<<<< HEAD
|
|
||||||
public Dictionary<DamageType, ResistanceSetSettings> Resistances { get; private set; } = new();
|
public Dictionary<DamageType, ResistanceSetSettings> Resistances { get; private set; } = new();
|
||||||
=======
|
|
||||||
public Dictionary<string, float> Coefficients { get; } = new();
|
[ViewVariables]
|
||||||
=======
|
|
||||||
[DataField("id", required: true)]
|
[DataField("id", required: true)]
|
||||||
public string ID { get; } = default!;
|
public string ID { get; } = default!;
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("coefficients", required: true)]
|
|
||||||
private Dictionary<string, float> coefficients { get; } = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("flatReductions", required: true)]
|
|
||||||
private Dictionary<string, int> flatReductions { get; } = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
<<<<<<< refs/remotes/origin/master
|
|
||||||
public Dictionary<DamageTypePrototype, int> FlatResistances { get; private set; } = new();
|
|
||||||
>>>>>>> Merge fixes
|
|
||||||
=======
|
|
||||||
[DataField("flatReductions", required: true)]
|
|
||||||
private Dictionary<string, int> flatReductions { get; } = new();
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public Dictionary<DamageTypePrototype, ResistanceSetSettings> Resistances { get; private set; } = new();
|
|
||||||
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
void ISerializationHooks.AfterDeserialization()
|
||||||
{
|
{
|
||||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
Resistances = new Dictionary<DamageType, ResistanceSetSettings>();
|
||||||
foreach (var damageTypeID in coefficients.Keys)
|
foreach (var damageType in (DamageType[]) Enum.GetValues(typeof(DamageType)))
|
||||||
{
|
{
|
||||||
var resolvedDamageType = prototypeManager.Index<DamageTypePrototype>(damageTypeID);
|
Resistances.Add(damageType,
|
||||||
Resistances.Add(resolvedDamageType, new ResistanceSetSettings(coefficients[damageTypeID], flatReductions[damageTypeID]));
|
new ResistanceSetSettings(Coefficients[damageType], FlatReductions[damageType]));
|
||||||
}
|
|
||||||
=======
|
|
||||||
public Dictionary<DamageTypePrototype, ResistanceSetSettings> Resistances { get; private set; } = new();
|
|
||||||
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
|
||||||
{
|
|
||||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
|
||||||
foreach (var damageTypeID in coefficients.Keys)
|
|
||||||
{
|
|
||||||
var resolvedDamageType = prototypeManager.Index<DamageTypePrototype>(damageTypeID);
|
|
||||||
Resistances.Add(resolvedDamageType, new ResistanceSetSettings(coefficients[damageTypeID], flatReductions[damageTypeID]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resistance Settings for a specific DamageType. Flat reduction should always be applied before the coefficient.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public readonly struct ResistanceSetSettings
|
|
||||||
{
|
|
||||||
[ViewVariables] public readonly float Coefficient;
|
|
||||||
[ViewVariables] public readonly int FlatReduction;
|
|
||||||
|
|
||||||
public ResistanceSetSettings(float coefficient, int flatReduction)
|
|
||||||
{
|
|
||||||
Coefficient = coefficient;
|
|
||||||
FlatReduction = flatReduction;
|
|
||||||
<<<<<<< HEAD
|
|
||||||
>>>>>>> update damagecomponent across shared and server
|
|
||||||
=======
|
|
||||||
>>>>>>> refactor-damageablecomponent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,709 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Body.Part;
|
|
||||||
using Content.Shared.GameObjects.Components.Body.Part.Property;
|
|
||||||
using Content.Shared.GameObjects.Components.Body.Preset;
|
|
||||||
using Content.Shared.GameObjects.Components.Body.Slot;
|
|
||||||
using Content.Shared.GameObjects.Components.Body.Template;
|
|
||||||
using Content.Shared.GameObjects.Components.Damage;
|
|
||||||
using Content.Shared.GameObjects.Components.Movement;
|
|
||||||
using Content.Shared.GameObjects.EntitySystems;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Players;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.Utility;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Shared.GameObjects.Components.Body
|
|
||||||
{
|
|
||||||
// TODO BODY Damage methods for collections of IDamageableComponents
|
|
||||||
public abstract class SharedBodyComponent : Component, IBody, ISerializationHooks
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
|
|
||||||
public override string Name => "Body";
|
|
||||||
|
|
||||||
public override uint? NetID => ContentNetIDs.BODY;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("template", required: true)]
|
|
||||||
private string? TemplateId { get; } = default;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("preset", required: true)]
|
|
||||||
private string? PresetId { get; } = default;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public BodyTemplatePrototype? Template => TemplateId == null
|
|
||||||
? null
|
|
||||||
: _prototypeManager.Index<BodyTemplatePrototype>(TemplateId);
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public BodyPresetPrototype? Preset => PresetId == null
|
|
||||||
? null
|
|
||||||
: _prototypeManager.Index<BodyPresetPrototype>(PresetId);
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
private Dictionary<string, BodyPartSlot> SlotIds { get; } = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
private Dictionary<IBodyPart, BodyPartSlot> SlotParts { get; } = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public IEnumerable<BodyPartSlot> Slots => SlotIds.Values;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public IEnumerable<KeyValuePair<IBodyPart, BodyPartSlot>> Parts => SlotParts;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public IEnumerable<BodyPartSlot> EmptySlots => Slots.Where(slot => slot.Part == null);
|
|
||||||
|
|
||||||
public BodyPartSlot? CenterSlot =>
|
|
||||||
Template?.CenterSlot is { } centerSlot
|
|
||||||
? SlotIds.GetValueOrDefault(centerSlot)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
public IBodyPart? CenterPart => CenterSlot?.Part;
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
// TODO BODY BeforeDeserialization
|
|
||||||
// TODO BODY Move to template or somewhere else
|
|
||||||
if (TemplateId != null)
|
|
||||||
{
|
|
||||||
var template = _prototypeManager.Index<BodyTemplatePrototype>(TemplateId);
|
|
||||||
|
|
||||||
foreach (var (id, partType) in template.Slots)
|
|
||||||
{
|
|
||||||
SetSlot(id, partType);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var (slotId, connectionIds) in template.Connections)
|
|
||||||
{
|
|
||||||
var connections = connectionIds.Select(id => SlotIds[id]);
|
|
||||||
SlotIds[slotId].SetConnectionsInternal(connections);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CalculateSpeed();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnRemove()
|
|
||||||
{
|
|
||||||
foreach (var slot in SlotIds.Values)
|
|
||||||
{
|
|
||||||
slot.Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
base.OnRemove();
|
|
||||||
}
|
|
||||||
|
|
||||||
private BodyPartSlot SetSlot(string id, BodyPartType type)
|
|
||||||
{
|
|
||||||
var slot = new BodyPartSlot(id, type);
|
|
||||||
|
|
||||||
SlotIds[id] = slot;
|
|
||||||
slot.PartAdded += part => OnAddPart(slot, part);
|
|
||||||
slot.PartRemoved += part => OnRemovePart(slot, part);
|
|
||||||
|
|
||||||
return slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Dictionary<BodyPartSlot, IBodyPart> GetHangingParts(BodyPartSlot from)
|
|
||||||
{
|
|
||||||
var hanging = new Dictionary<BodyPartSlot, IBodyPart>();
|
|
||||||
|
|
||||||
foreach (var connection in from.Connections)
|
|
||||||
{
|
|
||||||
if (connection.Part != null &&
|
|
||||||
!ConnectedToCenter(connection.Part))
|
|
||||||
{
|
|
||||||
hanging.Add(connection, connection.Part);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hanging;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual bool CanAddPart(string slotId, IBodyPart part)
|
|
||||||
{
|
|
||||||
if (!SlotIds.TryGetValue(slotId, out var slot) ||
|
|
||||||
slot.CanAddPart(part))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnAddPart(BodyPartSlot slot, IBodyPart part)
|
|
||||||
{
|
|
||||||
SlotParts[part] = slot;
|
|
||||||
part.Body = this;
|
|
||||||
|
|
||||||
var argsAdded = new BodyPartAddedEventArgs(slot.Id, part);
|
|
||||||
|
|
||||||
foreach (var component in Owner.GetAllComponents<IBodyPartAdded>().ToArray())
|
|
||||||
{
|
|
||||||
component.BodyPartAdded(argsAdded);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO BODY Sort this duplicate out
|
|
||||||
OnBodyChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnRemovePart(BodyPartSlot slot, IBodyPart part)
|
|
||||||
{
|
|
||||||
SlotParts.Remove(part);
|
|
||||||
|
|
||||||
foreach (var connectedSlot in slot.Connections)
|
|
||||||
{
|
|
||||||
if (connectedSlot.Part != null &&
|
|
||||||
!ConnectedToCenter(connectedSlot.Part))
|
|
||||||
{
|
|
||||||
RemovePart(connectedSlot.Part);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
part.Body = null;
|
|
||||||
|
|
||||||
var args = new BodyPartRemovedEventArgs(slot.Id, part);
|
|
||||||
|
|
||||||
foreach (var component in Owner.GetAllComponents<IBodyPartRemoved>())
|
|
||||||
{
|
|
||||||
component.BodyPartRemoved(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
// creadth: fall down if no legs
|
|
||||||
if (part.PartType == BodyPartType.Leg &&
|
|
||||||
GetPartsOfType(BodyPartType.Leg).ToArray().Length == 0)
|
|
||||||
{
|
|
||||||
EntitySystem.Get<SharedStandingStateSystem>().Down(Owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
// creadth: immediately kill entity if last vital part removed
|
|
||||||
if (Owner.TryGetComponent(out IDamageableComponent? damageable))
|
|
||||||
{
|
|
||||||
if (part.IsVital && SlotParts.Count(x => x.Value.PartType == part.PartType) == 0)
|
|
||||||
{
|
|
||||||
damageable.ChangeDamage(damageable.GetDamageType("Bloodloss"), 300, true); // TODO BODY KILL
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
OnBodyChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryAddPart(string slotId, IBodyPart part)
|
|
||||||
{
|
|
||||||
DebugTools.AssertNotNull(part);
|
|
||||||
DebugTools.AssertNotNull(slotId);
|
|
||||||
|
|
||||||
if (!CanAddPart(slotId, part))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SlotIds.TryGetValue(slotId, out var slot) &&
|
|
||||||
slot.TryAddPart(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetPart(string slotId, IBodyPart part)
|
|
||||||
{
|
|
||||||
if (!SlotIds.TryGetValue(slotId, out var slot))
|
|
||||||
{
|
|
||||||
slot = SetSlot(slotId, part.PartType);
|
|
||||||
SlotIds[slotId] = slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
slot.SetPart(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasPart(string slotId)
|
|
||||||
{
|
|
||||||
DebugTools.AssertNotNull(slotId);
|
|
||||||
|
|
||||||
return SlotIds.TryGetValue(slotId, out var slot) &&
|
|
||||||
slot.Part != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasPart(IBodyPart part)
|
|
||||||
{
|
|
||||||
DebugTools.AssertNotNull(part);
|
|
||||||
|
|
||||||
return SlotParts.ContainsKey(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool RemovePart(IBodyPart part)
|
|
||||||
{
|
|
||||||
DebugTools.AssertNotNull(part);
|
|
||||||
|
|
||||||
return SlotParts.TryGetValue(part, out var slot) &&
|
|
||||||
slot.RemovePart();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool RemovePart(string slotId)
|
|
||||||
{
|
|
||||||
DebugTools.AssertNotNull(slotId);
|
|
||||||
|
|
||||||
return SlotIds.TryGetValue(slotId, out var slot) &&
|
|
||||||
slot.RemovePart();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool RemovePart(IBodyPart part, [NotNullWhen(true)] out BodyPartSlot? slotId)
|
|
||||||
{
|
|
||||||
DebugTools.AssertNotNull(part);
|
|
||||||
|
|
||||||
if (!SlotParts.TryGetValue(part, out var slot))
|
|
||||||
{
|
|
||||||
slotId = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!slot.RemovePart())
|
|
||||||
{
|
|
||||||
slotId = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
slotId = slot;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryDropPart(BodyPartSlot slot, [NotNullWhen(true)] out Dictionary<BodyPartSlot, IBodyPart>? dropped)
|
|
||||||
{
|
|
||||||
DebugTools.AssertNotNull(slot);
|
|
||||||
|
|
||||||
if (!SlotIds.TryGetValue(slot.Id, out var ownedSlot) ||
|
|
||||||
ownedSlot != slot ||
|
|
||||||
slot.Part == null)
|
|
||||||
{
|
|
||||||
dropped = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var oldPart = slot.Part;
|
|
||||||
dropped = GetHangingParts(slot);
|
|
||||||
|
|
||||||
if (!slot.RemovePart())
|
|
||||||
{
|
|
||||||
dropped = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dropped[slot] = oldPart;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ConnectedToCenter(IBodyPart part)
|
|
||||||
{
|
|
||||||
return TryGetSlot(part, out var result) &&
|
|
||||||
ConnectedToCenterPartRecursion(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ConnectedToCenterPartRecursion(BodyPartSlot slot, HashSet<BodyPartSlot>? searched = null)
|
|
||||||
{
|
|
||||||
searched ??= new HashSet<BodyPartSlot>();
|
|
||||||
|
|
||||||
if (Template?.CenterSlot == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slot.Part == CenterPart)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
searched.Add(slot);
|
|
||||||
|
|
||||||
foreach (var connection in slot.Connections)
|
|
||||||
{
|
|
||||||
if (!searched.Contains(connection) &&
|
|
||||||
ConnectedToCenterPartRecursion(connection, searched))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasSlot(string slot)
|
|
||||||
{
|
|
||||||
return SlotIds.ContainsKey(slot);
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<IBodyPart> GetParts()
|
|
||||||
{
|
|
||||||
foreach (var slot in SlotIds.Values)
|
|
||||||
{
|
|
||||||
if (slot.Part != null)
|
|
||||||
{
|
|
||||||
yield return slot.Part;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetPart(string slotId, [NotNullWhen(true)] out IBodyPart? result)
|
|
||||||
{
|
|
||||||
result = null;
|
|
||||||
|
|
||||||
return SlotIds.TryGetValue(slotId, out var slot) &&
|
|
||||||
(result = slot.Part) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BodyPartSlot? GetSlot(string id)
|
|
||||||
{
|
|
||||||
return SlotIds.GetValueOrDefault(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BodyPartSlot? GetSlot(IBodyPart part)
|
|
||||||
{
|
|
||||||
return SlotParts.GetValueOrDefault(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetSlot(string slotId, [NotNullWhen(true)] out BodyPartSlot? slot)
|
|
||||||
{
|
|
||||||
return (slot = GetSlot(slotId)) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetSlot(IBodyPart part, [NotNullWhen(true)] out BodyPartSlot? slot)
|
|
||||||
{
|
|
||||||
return (slot = GetSlot(part)) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetPartConnections(string slotId, [NotNullWhen(true)] out List<IBodyPart>? connections)
|
|
||||||
{
|
|
||||||
if (!SlotIds.TryGetValue(slotId, out var slot))
|
|
||||||
{
|
|
||||||
connections = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
connections = new List<IBodyPart>();
|
|
||||||
foreach (var connection in slot.Connections)
|
|
||||||
{
|
|
||||||
if (connection.Part != null)
|
|
||||||
{
|
|
||||||
connections.Add(connection.Part);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connections.Count <= 0)
|
|
||||||
{
|
|
||||||
connections = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasSlotOfType(BodyPartType type)
|
|
||||||
{
|
|
||||||
foreach (var _ in GetSlotsOfType(type))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<BodyPartSlot> GetSlotsOfType(BodyPartType type)
|
|
||||||
{
|
|
||||||
foreach (var slot in SlotIds.Values)
|
|
||||||
{
|
|
||||||
if (slot.PartType == type)
|
|
||||||
{
|
|
||||||
yield return slot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasPartOfType(BodyPartType type)
|
|
||||||
{
|
|
||||||
foreach (var _ in GetPartsOfType(type))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<IBodyPart> GetPartsOfType(BodyPartType type)
|
|
||||||
{
|
|
||||||
foreach (var slot in GetSlotsOfType(type))
|
|
||||||
{
|
|
||||||
if (slot.Part != null)
|
|
||||||
{
|
|
||||||
yield return slot.Part;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<(IBodyPart part, IBodyPartProperty property)> GetPartsWithProperty(Type type)
|
|
||||||
{
|
|
||||||
foreach (var slot in SlotIds.Values)
|
|
||||||
{
|
|
||||||
if (slot.Part != null && slot.Part.TryGetProperty(type, out var property))
|
|
||||||
{
|
|
||||||
yield return (slot.Part, property);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<(IBodyPart part, T property)> GetPartsWithProperty<T>() where T : class, IBodyPartProperty
|
|
||||||
{
|
|
||||||
foreach (var part in SlotParts.Keys)
|
|
||||||
{
|
|
||||||
if (part.TryGetProperty<T>(out var property))
|
|
||||||
{
|
|
||||||
yield return (part, property);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateSpeed()
|
|
||||||
{
|
|
||||||
if (!Owner.TryGetComponent(out MovementSpeedModifierComponent? playerMover))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var legs = GetPartsWithProperty<LegComponent>().ToArray();
|
|
||||||
float speedSum = 0;
|
|
||||||
|
|
||||||
foreach (var leg in legs)
|
|
||||||
{
|
|
||||||
var footDistance = DistanceToNearestFoot(leg.part);
|
|
||||||
|
|
||||||
if (Math.Abs(footDistance - float.MinValue) <= 0.001f)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
speedSum += leg.property.Speed * (1 + (float) Math.Log(footDistance, 1024.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (speedSum <= 0.001f)
|
|
||||||
{
|
|
||||||
playerMover.BaseWalkSpeed = 0.8f;
|
|
||||||
playerMover.BaseSprintSpeed = 2.0f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Extra legs stack diminishingly.
|
|
||||||
playerMover.BaseWalkSpeed =
|
|
||||||
speedSum / (legs.Length - (float) Math.Log(legs.Length, 4.0));
|
|
||||||
|
|
||||||
playerMover.BaseSprintSpeed = playerMover.BaseWalkSpeed * 1.75f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called when the layout of this body changes.
|
|
||||||
/// </summary>
|
|
||||||
private void OnBodyChanged()
|
|
||||||
{
|
|
||||||
// Calculate move speed based on this body.
|
|
||||||
if (Owner.HasComponent<MovementSpeedModifierComponent>())
|
|
||||||
{
|
|
||||||
CalculateSpeed();
|
|
||||||
}
|
|
||||||
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the combined length of the distance to the nearest
|
|
||||||
/// <see cref="IBodyPart"/> that is a foot.
|
|
||||||
/// If you consider a <see cref="IBody"/> a node map, then it will
|
|
||||||
/// look for a foot node from the given node. It can only search
|
|
||||||
/// through <see cref="IBodyPart"/>s with an
|
|
||||||
/// <see cref="ExtensionComponent"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// The distance to the foot if found, <see cref="float.MinValue"/>
|
|
||||||
/// otherwise.
|
|
||||||
/// </returns>
|
|
||||||
public float DistanceToNearestFoot(IBodyPart source)
|
|
||||||
{
|
|
||||||
if (source.PartType == BodyPartType.Foot &&
|
|
||||||
source.TryGetProperty<ExtensionComponent>(out var extension))
|
|
||||||
{
|
|
||||||
return extension.Distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
return LookForFootRecursion(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
private float LookForFootRecursion(IBodyPart current, HashSet<BodyPartSlot>? searched = null)
|
|
||||||
{
|
|
||||||
searched ??= new HashSet<BodyPartSlot>();
|
|
||||||
|
|
||||||
if (!current.TryGetProperty<ExtensionComponent>(out var extProperty))
|
|
||||||
{
|
|
||||||
return float.MinValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all connected parts if the current part has an extension property
|
|
||||||
if (!TryGetSlot(current, out var slot))
|
|
||||||
{
|
|
||||||
return float.MinValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a connected BodyPart is a foot, return this BodyPart's length.
|
|
||||||
foreach (var connection in slot.Connections)
|
|
||||||
{
|
|
||||||
if (connection.PartType == BodyPartType.Foot &&
|
|
||||||
!searched.Contains(connection))
|
|
||||||
{
|
|
||||||
return extProperty.Distance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, get the recursion values of all connected BodyParts and
|
|
||||||
// store them in a list.
|
|
||||||
var distances = new List<float>();
|
|
||||||
foreach (var connection in slot.Connections)
|
|
||||||
{
|
|
||||||
if (connection.Part == null || !searched.Contains(connection))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = LookForFootRecursion(connection.Part, searched);
|
|
||||||
|
|
||||||
if (Math.Abs(result - float.MinValue) > 0.001f)
|
|
||||||
{
|
|
||||||
distances.Add(result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If one or more of the searches found a foot, return the smallest one
|
|
||||||
// and add this ones length.
|
|
||||||
if (distances.Count > 0)
|
|
||||||
{
|
|
||||||
return distances.Min<float>() + extProperty.Distance;
|
|
||||||
}
|
|
||||||
|
|
||||||
return float.MinValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO BODY optimize this
|
|
||||||
public BodyPartSlot SlotAt(int index)
|
|
||||||
{
|
|
||||||
return SlotIds.Values.ElementAt(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeyValuePair<IBodyPart, BodyPartSlot> PartAt(int index)
|
|
||||||
{
|
|
||||||
return SlotParts.ElementAt(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override ComponentState GetComponentState(ICommonSession player)
|
|
||||||
{
|
|
||||||
var parts = new (string slot, EntityUid partId)[SlotParts.Count];
|
|
||||||
|
|
||||||
var i = 0;
|
|
||||||
foreach (var (part, slot) in SlotParts)
|
|
||||||
{
|
|
||||||
parts[i] = (slot.Id, part.Owner.Uid);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new BodyComponentState(parts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
|
||||||
{
|
|
||||||
base.HandleComponentState(curState, nextState);
|
|
||||||
|
|
||||||
if (curState is not BodyComponentState state)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newParts = state.Parts();
|
|
||||||
|
|
||||||
foreach (var (oldPart, slot) in SlotParts)
|
|
||||||
{
|
|
||||||
if (!newParts.TryGetValue(slot.Id, out var newPart) ||
|
|
||||||
newPart != oldPart)
|
|
||||||
{
|
|
||||||
RemovePart(oldPart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var (slotId, newPart) in newParts)
|
|
||||||
{
|
|
||||||
if (!SlotIds.TryGetValue(slotId, out var slot) ||
|
|
||||||
slot.Part != newPart)
|
|
||||||
{
|
|
||||||
SetPart(slotId, newPart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void Gib(bool gibParts = false)
|
|
||||||
{
|
|
||||||
foreach (var part in SlotParts.Keys)
|
|
||||||
{
|
|
||||||
RemovePart(part);
|
|
||||||
|
|
||||||
if (gibParts)
|
|
||||||
part.Gib();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public class BodyComponentState : ComponentState
|
|
||||||
{
|
|
||||||
private Dictionary<string, IBodyPart>? _parts;
|
|
||||||
|
|
||||||
public readonly (string slot, EntityUid partId)[] PartIds;
|
|
||||||
|
|
||||||
public BodyComponentState((string slot, EntityUid partId)[] partIds) : base(ContentNetIDs.BODY)
|
|
||||||
{
|
|
||||||
PartIds = partIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Dictionary<string, IBodyPart> Parts(IEntityManager? entityManager = null)
|
|
||||||
{
|
|
||||||
if (_parts != null)
|
|
||||||
{
|
|
||||||
return _parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
entityManager ??= IoCManager.Resolve<IEntityManager>();
|
|
||||||
|
|
||||||
var parts = new Dictionary<string, IBodyPart>(PartIds.Length);
|
|
||||||
|
|
||||||
foreach (var (slot, partId) in PartIds)
|
|
||||||
{
|
|
||||||
if (!entityManager.TryGetEntity(partId, out var entity))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!entity.TryGetComponent(out IBodyPart? part))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
parts[slot] = part;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _parts = parts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
|
|
||||||
namespace Content.Shared.GameObjects.Components.Damage
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Data class with information on how the value of a
|
|
||||||
/// single <see cref="DamageTypePrototype"/> has changed.
|
|
||||||
/// </summary>
|
|
||||||
public struct DamageChangeData
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Type of damage that changed.
|
|
||||||
/// </summary>
|
|
||||||
public DamageTypePrototype Type;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The new current value for that damage.
|
|
||||||
/// </summary>
|
|
||||||
public int NewValue;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// How much the health value changed from its last value (negative is heals, positive is damage).
|
|
||||||
/// </summary>
|
|
||||||
public int Delta;
|
|
||||||
|
|
||||||
public DamageChangeData(DamageTypePrototype type, int newValue, int delta)
|
|
||||||
{
|
|
||||||
Type = type;
|
|
||||||
NewValue = newValue;
|
|
||||||
Delta = delta;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
|
|
||||||
namespace Content.Shared.GameObjects.Components.Damage
|
|
||||||
{
|
|
||||||
public class DamageChangedEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public DamageChangedEventArgs(IDamageableComponent damageable, IReadOnlyList<DamageChangeData> data)
|
|
||||||
{
|
|
||||||
Damageable = damageable;
|
|
||||||
Data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DamageChangedEventArgs(IDamageableComponent damageable, DamageTypePrototype type, int newValue, int delta)
|
|
||||||
{
|
|
||||||
Damageable = damageable;
|
|
||||||
|
|
||||||
var datum = new DamageChangeData(type, newValue, delta);
|
|
||||||
var data = new List<DamageChangeData> {datum};
|
|
||||||
|
|
||||||
Data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reference to the <see cref="IDamageableComponent"/> that invoked the event.
|
|
||||||
/// </summary>
|
|
||||||
public IDamageableComponent Damageable { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// List containing data on each <see cref="DamageTypePrototype"/> that was changed.
|
|
||||||
/// </summary>
|
|
||||||
public IReadOnlyList<DamageChangeData> Data { get; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user