Bodysystem and damagesystem rework (#1544)

* Things and stuff with grids, unfinished w/ code debug changes.

* Updated submodule and also lost some progress cause I fucked it up xd

* First unfinished draft of the BodySystem. Doesn't compile.

* More changes to make it compile, but still just a framework. Doesn't do anything at the moment.

* Many cleanup changes.

* Revert "Merge branch 'master' of https://github.com/GlassEclipse/space-station-14 into body_system"

This reverts commit ddd4aebbc76cf2a0b7b102f72b93d55a0816c88c, reversing
changes made to 12d0dd752706bdda8879393bd8191a1199a0c978.

* Commit human.yml

* Updated a lot of things to be more classy, more progress overall, etc. etc.

* Latest update with many changes

* Minor changes

* Fixed Travis build bug

* Adds first draft of Body Scanner console, apparently I also forgot to tie Mechanisms into body parts so now a heart just sits in the Torso like a good boy :)

* Commit rest of stuff

* Latest changes

* Latest changes again

* 14 naked cowboys

* Yay!

* Latest changes (probably doesnt compile)

* Surgery!!!!!!!!!~1116y

* Cleaned some stuff up

* More cleanup

* Refactoring of code. Basic surgery path now done.

* Removed readme, has been added to HackMD

* Fixes typo (and thus test errors)

* WIP changes, committing so I can pull latest master changes

* Still working on that god awful merge

* Latest changes

* Latest changes!!

* Beginning of refactor to BoundUserInterface

* Surgery!

* Latest changes - fixes pr change requests and random fixes

* oops

* Fixes bodypart recursion

* Beginning of work on revamping the damage system.

* More latest changes

* Latest changes

* Finished merge

* Commit before removing old healthcode

* Almost done with removing speciescomponent...

* It compiles!!!

* yahoo more work

* Fixes to make it work

* Merge conflict fixes

* Deleting species visualizer was a mistake

* IDE warnings are VERBOTEN

* makes the server not kill itself on startup, some cleanup (#1)

* Namespaces, comments and exception fixes

* Fix conveyor and conveyor switch serialization

SS14 in reactive when

* Move damage, acts and body to shared

Damage cleanup
Comment cleanup

* Rename SpeciesComponent to RotationComponent and cleanup

Damage cleanup
Comment cleanup

* Fix nullable warnings

* Address old reviews

Fix off welder suicide damage type, deathmatch and suspicion

* Fix new test fail with units being able to accept items when unpowered

* Remove RotationComponent, change references to IBodyManagerComponent

* Add a bloodstream to humans

* More cleanups

* Add body conduits, connections, connectors substances and valves

* Revert "Add body conduits, connections, connectors substances and valves"

This reverts commit 9ab0b50e6b15fe98852d7b0836c0cdbf4bd76d20.

* Implement the heart mechanism behavior with the circulatory network

* Added network property to mechanism behaviors

* Changed human organ sprites and added missing ones

* Fix tests

* Add individual body part sprite rendering

* Fix error where dropped mechanisms are not initialized

* Implement client/server body damage

* Make DamageContainer take care of raising events

* Reimplement medical scanner with the new body system

* Improve the medical scanner ui

* Merge conflict fixes

* Fix crash when colliding with something

* Fix microwave suicides and eyes sprite rendering

* Fix nullable reference error

* Fix up surgery client side

* Fix missing using from merge conflict

* Add breathing

*inhale

* Merge conflict fixes

* Fix accumulatedframetime being reset to 0 instead of decreased by the threshold

https://github.com/space-wizards/space-station-14/pull/1617

* Use and add to the new AtmosHelpers

* Fix feet

* Add proper coloring to dropped body parts

* Fix Urist's lungs being too strong

* Merge conflict fixes

* Merge conflict fixes

* Merge conflict fixes

Co-authored-by: GlassEclipse <tsymall5@gmail.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
Co-authored-by: AJCM-git <60196617+AJCM-git@users.noreply.github.com>
This commit is contained in:
DrSmugleaf
2020-08-17 01:42:42 +02:00
committed by GitHub
parent c17dd97383
commit b051261485
276 changed files with 7853 additions and 4737 deletions

View File

@@ -1,275 +0,0 @@
using Content.Server.Mobs;
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystems;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Server.GameObjects.Components.Mobs
{
/// <summary>
/// Defines the blocking effect of each damage state, and what effects to apply upon entering or exiting the state
/// </summary>
public interface IDamageState : IActionBlocker
{
void EnterState(IEntity entity);
void ExitState(IEntity entity);
bool IsConscious { get; }
}
/// <summary>
/// Standard state that a species is at with no damage or negative effect
/// </summary>
public struct NormalState : IDamageState
{
public void EnterState(IEntity entity)
{
entity.TryGetComponent(out AppearanceComponent appearanceComponent);
appearanceComponent?.SetData(DamageStateVisuals.State, DamageStateVisualData.Normal);
}
public void ExitState(IEntity entity)
{
}
public bool IsConscious => true;
bool IActionBlocker.CanInteract()
{
return true;
}
bool IActionBlocker.CanMove()
{
return true;
}
bool IActionBlocker.CanUse()
{
return true;
}
bool IActionBlocker.CanThrow()
{
return true;
}
bool IActionBlocker.CanSpeak()
{
return true;
}
bool IActionBlocker.CanDrop()
{
return true;
}
bool IActionBlocker.CanPickup()
{
return true;
}
bool IActionBlocker.CanEmote()
{
return true;
}
bool IActionBlocker.CanAttack()
{
return true;
}
bool IActionBlocker.CanEquip()
{
return true;
}
bool IActionBlocker.CanUnequip()
{
return true;
}
bool IActionBlocker.CanChangeDirection()
{
return true;
}
}
/// <summary>
/// A state in which you are disabled from acting due to damage
/// </summary>
public struct CriticalState : IDamageState
{
public void EnterState(IEntity entity)
{
if(entity.TryGetComponent(out StunnableComponent stun))
stun.CancelAll();
entity.TryGetComponent(out AppearanceComponent appearanceComponent);
appearanceComponent?.SetData(DamageStateVisuals.State, DamageStateVisualData.Crit);
StandingStateHelper.Down(entity);
}
public void ExitState(IEntity entity)
{
StandingStateHelper.Standing(entity);
}
public bool IsConscious => false;
bool IActionBlocker.CanInteract()
{
return false;
}
bool IActionBlocker.CanMove()
{
return false;
}
bool IActionBlocker.CanUse()
{
return false;
}
bool IActionBlocker.CanThrow()
{
return false;
}
bool IActionBlocker.CanSpeak()
{
return false;
}
bool IActionBlocker.CanDrop()
{
return false;
}
bool IActionBlocker.CanPickup()
{
return false;
}
bool IActionBlocker.CanEmote()
{
return false;
}
bool IActionBlocker.CanAttack()
{
return false;
}
bool IActionBlocker.CanEquip()
{
return false;
}
bool IActionBlocker.CanUnequip()
{
return false;
}
bool IActionBlocker.CanChangeDirection()
{
return true;
}
}
/// <summary>
/// A damage state which will allow ghosting out of mobs
/// </summary>
public struct DeadState : IDamageState
{
public void EnterState(IEntity entity)
{
if(entity.TryGetComponent(out StunnableComponent stun))
stun.CancelAll();
StandingStateHelper.Down(entity);
entity.TryGetComponent(out AppearanceComponent appearanceComponent);
appearanceComponent?.SetData(DamageStateVisuals.State, DamageStateVisualData.Dead);
if (entity.TryGetComponent(out ICollidableComponent collidable))
{
collidable.CanCollide = false;
}
}
public void ExitState(IEntity entity)
{
StandingStateHelper.Standing(entity);
if (entity.TryGetComponent(out ICollidableComponent collidable))
{
collidable.CanCollide = true;
}
}
public bool IsConscious => false;
bool IActionBlocker.CanInteract()
{
return false;
}
bool IActionBlocker.CanMove()
{
return false;
}
bool IActionBlocker.CanUse()
{
return false;
}
bool IActionBlocker.CanThrow()
{
return false;
}
bool IActionBlocker.CanSpeak()
{
return false;
}
bool IActionBlocker.CanDrop()
{
return false;
}
bool IActionBlocker.CanPickup()
{
return false;
}
bool IActionBlocker.CanEmote()
{
return false;
}
bool IActionBlocker.CanAttack()
{
return false;
}
bool IActionBlocker.CanEquip()
{
return false;
}
bool IActionBlocker.CanUnequip()
{
return false;
}
bool IActionBlocker.CanChangeDirection()
{
return false;
}
}
}

View File

@@ -1,65 +0,0 @@
using System.Collections.Generic;
using Content.Server.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Damage;
namespace Content.Server.GameObjects.Components.Mobs.DamageThresholdTemplates
{
/// <summary>
/// Defines the threshold values for each damage state for any kind of species
/// </summary>
public abstract class DamageTemplates
{
public abstract List<DamageThreshold> HealthHudThresholds { get; }
/// <summary>
/// Changes the hud state when a threshold is reached
/// </summary>
/// <param name="damage"></param>
/// <returns></returns>
public abstract void ChangeHudState(DamageableComponent damage);
//public abstract ResistanceSet resistanceset { get; }
/// <summary>
/// Shows allowed states, ordered by priority, closest to last value to have threshold reached is preferred
/// </summary>
public abstract List<(DamageType, int, ThresholdType)> AllowedStates { get; }
/// <summary>
/// Map of ALL POSSIBLE damage states to the threshold enum value that will trigger them, normal state wont be triggered by this value but is a default that is fell back onto
/// </summary>
public static Dictionary<ThresholdType, IDamageState> StateThresholdMap = new Dictionary<ThresholdType, IDamageState>()
{
{ ThresholdType.None, new NormalState() },
{ ThresholdType.Critical, new CriticalState() },
{ ThresholdType.Death, new DeadState() }
};
public List<DamageThreshold> DamageThresholds
{
get
{
List<DamageThreshold> thresholds = new List<DamageThreshold>();
foreach (var element in AllowedStates)
{
thresholds.Add(new DamageThreshold(element.Item1, element.Item2, element.Item3));
}
return thresholds;
}
}
public ThresholdType CalculateDamageState(DamageableComponent damage)
{
ThresholdType healthstate = ThresholdType.None;
foreach(var element in AllowedStates)
{
if(damage.CurrentDamage[element.Item1] >= element.Item2)
{
healthstate = element.Item3;
}
}
return healthstate;
}
}
}

View File

@@ -1,97 +0,0 @@
using System;
using System.Collections.Generic;
using Content.Server.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Mobs;
using JetBrains.Annotations;
namespace Content.Server.GameObjects.Components.Mobs.DamageThresholdTemplates
{
[UsedImplicitly]
public class Human : DamageTemplates
{
int critvalue = 200;
int normalstates = 6;
//string startsprite = "human0";
public override List<(DamageType, int, ThresholdType)> AllowedStates => new List<(DamageType, int, ThresholdType)>()
{
(DamageType.Total, critvalue-1, ThresholdType.None),
(DamageType.Total, critvalue, ThresholdType.Critical),
(DamageType.Total, 300, ThresholdType.Death),
};
public override List<DamageThreshold> HealthHudThresholds
{
get
{
List<DamageThreshold> thresholds = new List<DamageThreshold>();
thresholds.Add(new DamageThreshold(DamageType.Total, 1, ThresholdType.HUDUpdate));
for (var i = 1; i <= normalstates; i++)
{
thresholds.Add(new DamageThreshold(DamageType.Total, i * critvalue / normalstates, ThresholdType.HUDUpdate));
}
return thresholds; //we don't need to respecify the state damage thresholds since we'll update hud on damage state changes as well
}
}
// for shared string dict, since we don't define these anywhere in content
[UsedImplicitly]
public static readonly string[] _humanStatusImages =
{
"/Textures/Interface/StatusEffects/Human/human0.png",
"/Textures/Interface/StatusEffects/Human/human1.png",
"/Textures/Interface/StatusEffects/Human/human2.png",
"/Textures/Interface/StatusEffects/Human/human3.png",
"/Textures/Interface/StatusEffects/Human/human4.png",
"/Textures/Interface/StatusEffects/Human/human5.png",
"/Textures/Interface/StatusEffects/Human/human6-0.png",
"/Textures/Interface/StatusEffects/Human/human6-1.png",
"/Textures/Interface/StatusEffects/Human/humancrit-0.png",
"/Textures/Interface/StatusEffects/Human/humancrit-1.png",
"/Textures/Interface/StatusEffects/Human/humandead.png",
};
public override void ChangeHudState(DamageableComponent damage)
{
ThresholdType healthstate = CalculateDamageState(damage);
damage.Owner.TryGetComponent(out ServerStatusEffectsComponent statusEffectsComponent);
damage.Owner.TryGetComponent(out ServerOverlayEffectsComponent overlayComponent);
switch (healthstate)
{
case ThresholdType.None:
var totaldamage = damage.CurrentDamage[DamageType.Total];
if (totaldamage > critvalue)
{
throw new InvalidOperationException(); //these should all be below the crit value, possibly going over multiple thresholds at once?
}
var modifier = totaldamage / (critvalue / normalstates); //integer division floors towards zero
statusEffectsComponent?.ChangeStatusEffectIcon(StatusEffect.Health,
"/Textures/Interface/StatusEffects/Human/human" + modifier + ".png");
overlayComponent?.RemoveOverlay(SharedOverlayID.GradientCircleMaskOverlay);
overlayComponent?.RemoveOverlay(SharedOverlayID.CircleMaskOverlay);
return;
case ThresholdType.Critical:
statusEffectsComponent?.ChangeStatusEffectIcon(
StatusEffect.Health,
"/Textures/Interface/StatusEffects/Human/humancrit-0.png");
overlayComponent?.AddOverlay(SharedOverlayID.GradientCircleMaskOverlay);
overlayComponent?.RemoveOverlay(SharedOverlayID.CircleMaskOverlay);
return;
case ThresholdType.Death:
statusEffectsComponent?.ChangeStatusEffectIcon(
StatusEffect.Health,
"/Textures/Interface/StatusEffects/Human/humandead.png");
overlayComponent?.RemoveOverlay(SharedOverlayID.GradientCircleMaskOverlay);
overlayComponent?.AddOverlay(SharedOverlayID.CircleMaskOverlay);
return;
default:
throw new InvalidOperationException();
}
}
}
}

View File

@@ -13,10 +13,9 @@ namespace Content.Server.GameObjects.Components.Mobs
public int GetHeatResistance()
{
if (Owner.GetComponent<InventoryComponent>().TryGetSlotItem(EquipmentSlotDefines.Slots.GLOVES, itemComponent: out ClothingComponent gloves)
| Owner.TryGetComponent(out SpeciesComponent speciesComponent))
if (Owner.GetComponent<InventoryComponent>().TryGetSlotItem(EquipmentSlotDefines.Slots.GLOVES, itemComponent: out ClothingComponent gloves))
{
return Math.Max(gloves?.HeatResistance ?? int.MinValue, speciesComponent?.HeatResistance ?? int.MinValue);
return gloves?.HeatResistance ?? int.MinValue;
}
return int.MinValue;
}

View File

@@ -2,6 +2,7 @@
using Content.Server.GameObjects.Components.Observer;
using Content.Server.Interfaces.GameTicking;
using Content.Server.Mobs;
using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
@@ -126,8 +127,8 @@ namespace Content.Server.GameObjects.Components.Mobs
}
var dead =
Owner.TryGetComponent<SpeciesComponent>(out var species) &&
species.CurrentDamageState is DeadState;
Owner.TryGetComponent<IDamageableComponent>(out var damageable) &&
damageable.CurrentDamageState == DamageState.Dead;
if (!HasMind)
{

View File

@@ -0,0 +1,488 @@
using System.Collections.Generic;
using Content.Server.GameObjects.Components.Body;
using Content.Server.GameObjects.Components.Damage;
using Content.Server.Mobs;
using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Server.GameObjects.Components.Mobs
{
/// <summary>
/// When attacked to an <see cref="IDamageableComponent"/>, this component will handle critical and death behaviors
/// for mobs.
/// Additionally, it handles sending effects to clients (such as blur effect for unconsciousness) and managing the
/// health HUD.
/// </summary>
[RegisterComponent]
internal class MobStateManagerComponent : Component, IOnHealthChangedBehavior, IActionBlocker
{
private readonly Dictionary<DamageState, IMobState> _behavior = new Dictionary<DamageState, IMobState>
{
{DamageState.Alive, new NormalState()},
{DamageState.Critical, new CriticalState()},
{DamageState.Dead, new DeadState()}
};
public override string Name => "MobStateManager";
private DamageState _currentDamageState;
public IMobState CurrentMobState { get; private set; } = new NormalState();
bool IActionBlocker.CanInteract()
{
return CurrentMobState.CanInteract();
}
bool IActionBlocker.CanMove()
{
return CurrentMobState.CanMove();
}
bool IActionBlocker.CanUse()
{
return CurrentMobState.CanUse();
}
bool IActionBlocker.CanThrow()
{
return CurrentMobState.CanThrow();
}
bool IActionBlocker.CanSpeak()
{
return CurrentMobState.CanSpeak();
}
bool IActionBlocker.CanDrop()
{
return CurrentMobState.CanDrop();
}
bool IActionBlocker.CanPickup()
{
return CurrentMobState.CanPickup();
}
bool IActionBlocker.CanEmote()
{
return CurrentMobState.CanEmote();
}
bool IActionBlocker.CanAttack()
{
return CurrentMobState.CanAttack();
}
bool IActionBlocker.CanEquip()
{
return CurrentMobState.CanEquip();
}
bool IActionBlocker.CanUnequip()
{
return CurrentMobState.CanUnequip();
}
bool IActionBlocker.CanChangeDirection()
{
return CurrentMobState.CanChangeDirection();
}
public void OnHealthChanged(HealthChangedEventArgs e)
{
if (e.Damageable.CurrentDamageState != _currentDamageState)
{
_currentDamageState = e.Damageable.CurrentDamageState;
CurrentMobState.ExitState(Owner);
CurrentMobState = _behavior[_currentDamageState];
CurrentMobState.EnterState(Owner);
}
CurrentMobState.UpdateState(Owner);
}
public override void Initialize()
{
base.Initialize();
_currentDamageState = DamageState.Alive;
CurrentMobState = _behavior[_currentDamageState];
CurrentMobState.EnterState(Owner);
CurrentMobState.UpdateState(Owner);
}
public override void OnRemove()
{
// TODO: Might want to add an OnRemove() to IMobState since those are where these components are being used
base.OnRemove();
if (Owner.TryGetComponent(out ServerStatusEffectsComponent status))
{
status.RemoveStatusEffect(StatusEffect.Health);
}
if (Owner.TryGetComponent(out ServerOverlayEffectsComponent overlay))
{
overlay.ClearOverlays();
}
}
}
/// <summary>
/// Defines the blocking effects of an associated <see cref="DamageState"/>
/// (i.e. Normal, Critical, Dead) and what effects to apply upon entering or
/// exiting the state.
/// </summary>
public interface IMobState : IActionBlocker
{
/// <summary>
/// Called when this state is entered.
/// </summary>
void EnterState(IEntity entity);
/// <summary>
/// Called when this state is left for a different state.
/// </summary>
void ExitState(IEntity entity);
/// <summary>
/// Called when this state is updated.
/// </summary>
void UpdateState(IEntity entity);
}
/// <summary>
/// The standard state an entity is in; no negative effects.
/// </summary>
public struct NormalState : IMobState
{
public void EnterState(IEntity entity)
{
UpdateState(entity);
}
public void ExitState(IEntity entity) { }
public void UpdateState(IEntity entity)
{
if (!entity.TryGetComponent(out ServerStatusEffectsComponent status))
{
return;
}
if (!entity.TryGetComponent(out IDamageableComponent damageable))
{
status.ChangeStatusEffectIcon(StatusEffect.Health,
"/Textures/Interface/StatusEffects/Human/human0.png");
return;
}
// TODO
switch (damageable)
{
case RuinableComponent ruinable:
{
var modifier = (int) (ruinable.TotalDamage / (ruinable.MaxHp / 7f));
status.ChangeStatusEffectIcon(StatusEffect.Health,
"/Textures/Interface/StatusEffects/Human/human" + modifier + ".png");
break;
}
case BodyManagerComponent body:
{
// TODO: Declare body max normal damage (currently 100)
var modifier = (int) (body.TotalDamage / (100f / 7f));
status.ChangeStatusEffectIcon(StatusEffect.Health,
"/Textures/Interface/StatusEffects/Human/human" + modifier + ".png");
break;
}
default:
{
status.ChangeStatusEffectIcon(StatusEffect.Health,
"/Textures/Interface/StatusEffects/Human/human0.png");
break;
}
}
}
bool IActionBlocker.CanInteract()
{
return true;
}
bool IActionBlocker.CanMove()
{
return true;
}
bool IActionBlocker.CanUse()
{
return true;
}
bool IActionBlocker.CanThrow()
{
return true;
}
bool IActionBlocker.CanSpeak()
{
return true;
}
bool IActionBlocker.CanDrop()
{
return true;
}
bool IActionBlocker.CanPickup()
{
return true;
}
bool IActionBlocker.CanEmote()
{
return true;
}
bool IActionBlocker.CanAttack()
{
return true;
}
bool IActionBlocker.CanEquip()
{
return true;
}
bool IActionBlocker.CanUnequip()
{
return true;
}
bool IActionBlocker.CanChangeDirection()
{
return true;
}
}
/// <summary>
/// A state in which an entity is disabled from acting due to sufficient damage (considered unconscious).
/// </summary>
public struct CriticalState : IMobState
{
public void EnterState(IEntity entity)
{
if (entity.TryGetComponent(out ServerStatusEffectsComponent status))
{
status.ChangeStatusEffectIcon(StatusEffect.Health,
"/Textures/Interface/StatusEffects/Human/humancrit-0.png"); //Todo: combine humancrit-0 and humancrit-1 into a gif and display it
}
if (entity.TryGetComponent(out ServerOverlayEffectsComponent overlay))
{
overlay.AddOverlay(SharedOverlayID.GradientCircleMaskOverlay);
}
if (entity.TryGetComponent(out StunnableComponent stun))
{
stun.CancelAll();
}
StandingStateHelper.Down(entity);
}
public void ExitState(IEntity entity)
{
StandingStateHelper.Standing(entity);
if (entity.TryGetComponent(out ServerOverlayEffectsComponent overlay))
{
overlay.ClearOverlays();
}
}
public void UpdateState(IEntity entity)
{
}
bool IActionBlocker.CanInteract()
{
return false;
}
bool IActionBlocker.CanMove()
{
return false;
}
bool IActionBlocker.CanUse()
{
return false;
}
bool IActionBlocker.CanThrow()
{
return false;
}
bool IActionBlocker.CanSpeak()
{
return false;
}
bool IActionBlocker.CanDrop()
{
return false;
}
bool IActionBlocker.CanPickup()
{
return false;
}
bool IActionBlocker.CanEmote()
{
return false;
}
bool IActionBlocker.CanAttack()
{
return false;
}
bool IActionBlocker.CanEquip()
{
return false;
}
bool IActionBlocker.CanUnequip()
{
return false;
}
bool IActionBlocker.CanChangeDirection()
{
return false;
}
}
/// <summary>
/// The state representing a dead entity; allows for ghosting.
/// </summary>
public struct DeadState : IMobState
{
public void EnterState(IEntity entity)
{
if (entity.TryGetComponent(out ServerStatusEffectsComponent status))
{
status.ChangeStatusEffectIcon(StatusEffect.Health,
"/Textures/Interface/StatusEffects/Human/humandead.png");
}
if (entity.TryGetComponent(out ServerOverlayEffectsComponent overlayComponent))
{
overlayComponent.AddOverlay(SharedOverlayID.CircleMaskOverlay);
}
if (entity.TryGetComponent(out StunnableComponent stun))
{
stun.CancelAll();
}
StandingStateHelper.Down(entity);
if (entity.TryGetComponent(out CollidableComponent collidable))
{
collidable.CanCollide = false;
}
}
public void ExitState(IEntity entity)
{
StandingStateHelper.Standing(entity);
if (entity.TryGetComponent(out CollidableComponent collidable))
{
collidable.CanCollide = true;
}
if (entity.TryGetComponent(out ServerOverlayEffectsComponent overlay))
{
overlay.ClearOverlays();
}
}
public void UpdateState(IEntity entity)
{
}
bool IActionBlocker.CanInteract()
{
return false;
}
bool IActionBlocker.CanMove()
{
return false;
}
bool IActionBlocker.CanUse()
{
return false;
}
bool IActionBlocker.CanThrow()
{
return false;
}
bool IActionBlocker.CanSpeak()
{
return false;
}
bool IActionBlocker.CanDrop()
{
return false;
}
bool IActionBlocker.CanPickup()
{
return false;
}
bool IActionBlocker.CanEmote()
{
return false;
}
bool IActionBlocker.CanAttack()
{
return false;
}
bool IActionBlocker.CanEquip()
{
return false;
}
bool IActionBlocker.CanUnequip()
{
return false;
}
bool IActionBlocker.CanChangeDirection()
{
return false;
}
}
}

View File

@@ -1,247 +0,0 @@
using System;
using System.Collections.Generic;
using Content.Server.GameObjects.Components.Damage;
using Content.Server.GameObjects.Components.Mobs.DamageThresholdTemplates;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces.GameObjects;
using Content.Server.Observer;
using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.GameObjects.Components.Movement;
using Content.Shared.GameObjects.EntitySystems;
using Robust.Server.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.Components.Mobs
{
[RegisterComponent]
[ComponentReference(typeof(SharedSpeciesComponent))]
public class SpeciesComponent : SharedSpeciesComponent, IActionBlocker, IOnDamageBehavior, IExAct, IRelayMoveInput
{
/// <summary>
/// Damagestates are reached by reaching a certain damage threshold, they will block actions after being reached
/// </summary>
public IDamageState CurrentDamageState { get; private set; } = new NormalState();
/// <summary>
/// Damage state enum for current health, set only via change damage state //TODO: SETTER
/// </summary>
private ThresholdType currentstate = ThresholdType.None;
/// <summary>
/// Holds the damage template which controls the threshold and resistance settings for this species type
/// </summary>
public DamageTemplates DamageTemplate { get; private set; }
/// <summary>
/// Variable for serialization
/// </summary>
private string templatename;
private int _heatResistance;
public int HeatResistance => _heatResistance;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref templatename, "Template", "Human");
var type = typeof(SpeciesComponent).Assembly.GetType("Content.Server.GameObjects.Components.Mobs.DamageThresholdTemplates." + templatename);
DamageTemplate = (DamageTemplates) Activator.CreateInstance(type);
serializer.DataFieldCached(ref _heatResistance, "HeatResistance", 323);
}
public override void HandleMessage(ComponentMessage message, IComponent component)
{
base.HandleMessage(message, component);
switch (message)
{
case PlayerAttachedMsg _:
if (CanReceiveStatusEffect(Owner)) {
DamageableComponent damage = Owner.GetComponent<DamageableComponent>();
DamageTemplate.ChangeHudState(damage);
}
break;
case PlayerDetachedMsg _:
break;
}
}
public override void OnRemove()
{
base.OnRemove();
Owner.TryGetComponent(out ServerStatusEffectsComponent statusEffectsComponent);
statusEffectsComponent?.RemoveStatusEffect(StatusEffect.Health);
Owner.TryGetComponent(out ServerOverlayEffectsComponent overlayEffectsComponent);
overlayEffectsComponent?.ClearOverlays();
}
bool IActionBlocker.CanMove()
{
return CurrentDamageState.CanMove();
}
bool IActionBlocker.CanInteract()
{
return CurrentDamageState.CanInteract();
}
bool IActionBlocker.CanUse()
{
return CurrentDamageState.CanUse();
}
bool IActionBlocker.CanThrow()
{
return CurrentDamageState.CanThrow();
}
bool IActionBlocker.CanSpeak()
{
return CurrentDamageState.CanSpeak();
}
bool IActionBlocker.CanDrop()
{
return CurrentDamageState.CanDrop();
}
bool IActionBlocker.CanPickup()
{
return CurrentDamageState.CanPickup();
}
bool IActionBlocker.CanEmote()
{
return CurrentDamageState.CanEmote();
}
bool IActionBlocker.CanAttack()
{
return CurrentDamageState.CanAttack();
}
bool IActionBlocker.CanEquip()
{
return CurrentDamageState.CanEquip();
}
bool IActionBlocker.CanUnequip()
{
return CurrentDamageState.CanUnequip();
}
bool IActionBlocker.CanChangeDirection()
{
return CurrentDamageState.CanChangeDirection();
}
List<DamageThreshold> IOnDamageBehavior.GetAllDamageThresholds()
{
var thresholdlist = DamageTemplate.DamageThresholds;
thresholdlist.AddRange(DamageTemplate.HealthHudThresholds);
return thresholdlist;
}
void IOnDamageBehavior.OnDamageThresholdPassed(object damageable, DamageThresholdPassedEventArgs e)
{
DamageableComponent damage = (DamageableComponent) damageable;
if (e.DamageThreshold.ThresholdType != ThresholdType.HUDUpdate)
{
ChangeDamageState(DamageTemplate.CalculateDamageState(damage));
}
//specifies if we have a client to update the hud for
if (CanReceiveStatusEffect(Owner))
{
DamageTemplate.ChangeHudState(damage);
}
}
private bool CanReceiveStatusEffect(IEntity user)
{
if (!user.HasComponent<ServerStatusEffectsComponent>() &&
!user.HasComponent<ServerOverlayEffectsComponent>())
{
return false;
}
if (user.HasComponent<DamageableComponent>())
{
return true;
}
return false;
}
private void ChangeDamageState(ThresholdType threshold)
{
if (threshold == currentstate)
{
return;
}
CurrentDamageState.ExitState(Owner);
CurrentDamageState = DamageTemplates.StateThresholdMap[threshold];
CurrentDamageState.EnterState(Owner);
currentstate = threshold;
var toRaise = new MobDamageStateChangedMessage(this);
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, toRaise);
}
void IExAct.OnExplosion(ExplosionEventArgs eventArgs)
{
var burnDamage = 0;
var bruteDamage = 0;
switch(eventArgs.Severity)
{
case ExplosionSeverity.Destruction:
bruteDamage += 250;
burnDamage += 250;
break;
case ExplosionSeverity.Heavy:
bruteDamage += 60;
burnDamage += 60;
break;
case ExplosionSeverity.Light:
bruteDamage += 30;
break;
}
Owner.GetComponent<DamageableComponent>().TakeDamage(DamageType.Brute, bruteDamage, null);
Owner.GetComponent<DamageableComponent>().TakeDamage(DamageType.Heat, burnDamage, null);
}
void IRelayMoveInput.MoveInputPressed(ICommonSession session)
{
if (CurrentDamageState is DeadState)
{
new Ghost().Execute(null, (IPlayerSession) session, null);
}
}
}
/// <summary>
/// Fired when <see cref="SpeciesComponent.CurrentDamageState"/> changes.
/// </summary>
public sealed class MobDamageStateChangedMessage : EntitySystemMessage
{
public MobDamageStateChangedMessage(SpeciesComponent species)
{
Species = species;
}
/// <summary>
/// The species component that was changed.
/// </summary>
public SpeciesComponent Species { get; }
}
}