diff --git a/Content.Client/GameObjects/Components/Mobs/DamageStateVisualizer.cs b/Content.Client/GameObjects/Components/Mobs/DamageStateVisualizer.cs new file mode 100644 index 0000000000..556e066e1f --- /dev/null +++ b/Content.Client/GameObjects/Components/Mobs/DamageStateVisualizer.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using Content.Shared.GameObjects.Components.Mobs; +using JetBrains.Annotations; +using Robust.Client.GameObjects; +using Robust.Client.Interfaces.GameObjects.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.Utility; +using YamlDotNet.RepresentationModel; +using DrawDepth = Content.Shared.GameObjects.DrawDepth; + +namespace Content.Client.GameObjects.Components.Mobs +{ + [UsedImplicitly] + public sealed class DamageStateVisualizer : AppearanceVisualizer + { + private DamageStateVisualData _data = DamageStateVisualData.Normal; + private Dictionary _stateMap = new Dictionary(); + private int? _originalDrawDepth = null; + + public override void LoadData(YamlMappingNode node) + { + base.LoadData(node); + if (node.TryGetNode("normal", out var normal)) + { + _stateMap.Add(DamageStateVisualData.Normal, normal.AsString()); + } + + if (node.TryGetNode("crit", out var crit)) + { + _stateMap.Add(DamageStateVisualData.Crit, crit.AsString()); + } + + if (node.TryGetNode("dead", out var dead)) + { + _stateMap.Add(DamageStateVisualData.Dead, dead.AsString()); + } + } + + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + var sprite = component.Owner.GetComponent(); + if (!component.TryGetData(DamageStateVisuals.State, out DamageStateVisualData data)) + { + return; + } + + if (_data == data) + { + return; + } + + _data = data; + + if (_stateMap.TryGetValue(_data, out var state)) + { + sprite.LayerSetState(DamageStateVisualLayers.Base, state); + } + + // So they don't draw over mobs anymore + if (_data == DamageStateVisualData.Dead) + { + _originalDrawDepth = sprite.DrawDepth; + sprite.DrawDepth = (int) DrawDepth.FloorObjects; + } + else if (_originalDrawDepth != null) + { + sprite.DrawDepth = _originalDrawDepth.Value; + _originalDrawDepth = null; + } + } + } + + public enum DamageStateVisualLayers + { + Base + } +} diff --git a/Content.Server/AI/Utility/AiLogic/UtilityAI.cs b/Content.Server/AI/Utility/AiLogic/UtilityAI.cs index 5847deb850..791727b637 100644 --- a/Content.Server/AI/Utility/AiLogic/UtilityAI.cs +++ b/Content.Server/AI/Utility/AiLogic/UtilityAI.cs @@ -116,7 +116,7 @@ namespace Content.Server.AI.Utility.AiLogic _planner = IoCManager.Resolve().GetEntitySystem(); if (SelfEntity.TryGetComponent(out DamageableComponent damageableComponent)) { - damageableComponent.DamageThresholdPassed += DeathHandle; + damageableComponent.DamageThresholdPassed += DamageThresholdHandle; } } @@ -125,22 +125,25 @@ namespace Content.Server.AI.Utility.AiLogic // TODO: If DamageableComponent removed still need to unsubscribe? if (SelfEntity.TryGetComponent(out DamageableComponent damageableComponent)) { - damageableComponent.DamageThresholdPassed -= DeathHandle; + damageableComponent.DamageThresholdPassed -= DamageThresholdHandle; } var currentOp = CurrentAction?.ActionOperators.Peek(); currentOp?.Shutdown(Outcome.Failed); } - private void DeathHandle(object sender, DamageThresholdPassedEventArgs eventArgs) + private void DamageThresholdHandle(object sender, DamageThresholdPassedEventArgs eventArgs) { - if (eventArgs.DamageThreshold.ThresholdType == ThresholdType.Death) + if (!SelfEntity.TryGetComponent(out SpeciesComponent speciesComponent)) + { + return; + } + + if (speciesComponent.CurrentDamageState is DeadState) { _isDead = true; } - - // TODO: If we get healed - double-check what it should be - if (eventArgs.DamageThreshold.ThresholdType == ThresholdType.None) + else { _isDead = false; } diff --git a/Content.Server/GameObjects/Components/Mobs/DamageStates.cs b/Content.Server/GameObjects/Components/Mobs/DamageStates.cs index 6ee267da41..69e354e7bc 100644 --- a/Content.Server/GameObjects/Components/Mobs/DamageStates.cs +++ b/Content.Server/GameObjects/Components/Mobs/DamageStates.cs @@ -1,6 +1,8 @@ using Content.Server.GameObjects.Components.Mobs; 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; @@ -25,6 +27,8 @@ namespace Content.Server.GameObjects { public void EnterState(IEntity entity) { + entity.TryGetComponent(out AppearanceComponent appearanceComponent); + appearanceComponent?.SetData(DamageStateVisuals.State, DamageStateVisualData.Normal); } public void ExitState(IEntity entity) @@ -104,6 +108,8 @@ namespace Content.Server.GameObjects if(entity.TryGetComponent(out StunnableComponent stun)) stun.CancelAll(); + entity.TryGetComponent(out AppearanceComponent appearanceComponent); + appearanceComponent?.SetData(DamageStateVisuals.State, DamageStateVisualData.Crit); StandingStateHelper.Down(entity); } @@ -186,6 +192,8 @@ namespace Content.Server.GameObjects stun.CancelAll(); StandingStateHelper.Down(entity); + entity.TryGetComponent(out AppearanceComponent appearanceComponent); + appearanceComponent?.SetData(DamageStateVisuals.State, DamageStateVisualData.Dead); if (entity.TryGetComponent(out ICollidableComponent collidable)) { diff --git a/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs b/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs index ff51d9af76..ba92f7122d 100644 --- a/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs @@ -634,6 +634,10 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering // God there's so many ways to do this // err for now we'll just assume the first entity is the center and just add a vector for it var collisionEntity = _entityManager.GetEntity(uid); + + //Pathfinding updates are deferred so this may not be done yet. + if (collisionEntity.Deleted) continue; + // if we're moving in the same direction then ignore // So if 2 entities are moving towards each other and both detect a collision they'll both move in the same direction // i.e. towards the right diff --git a/Content.Shared/GameObjects/Components/Mobs/SharedDamageState.cs b/Content.Shared/GameObjects/Components/Mobs/SharedDamageState.cs new file mode 100644 index 0000000000..b6cc81cef2 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Mobs/SharedDamageState.cs @@ -0,0 +1,19 @@ +using System; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Mobs +{ + [Serializable, NetSerializable] + public enum DamageStateVisuals + { + State + } + + [Serializable, NetSerializable] + public enum DamageStateVisualData + { + Normal, + Crit, + Dead + } +} \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml index ed6b0bdc1c..d2873913a9 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -1,7 +1,7 @@ # Hacky for the stress test so don't even consider adding to this - type: entity save: false - name: Xeno + name: Xeno hunter id: XenoMob_Content description: They mostly come at night. Mostly. drawdepth: Mobs @@ -14,15 +14,16 @@ - left - right - type: MovementSpeedModifier - # Organs - type: InteractionOutline - type: Sprite drawdepth: Mobs - sprite: Mobs/Species/xeno.rsi - state: running + layers: + - map: ["enum.DamageStateVisualLayers.Base"] + state: running + sprite: Mobs/Species/xeno_hunter.rsi - type: Icon - sprite: Mobs/Species/xeno.rsi - state: running + sprite: Mobs/Species/xeno_hunter.rsi + state: standing - type: Clickable - type: Physics mass: 85 @@ -48,13 +49,16 @@ - type: Damageable - type: CombatMode - type: Teleportable - - type: CharacterInfo - type: FootstepSound - - type: HumanoidAppearance - type: Stunnable - - type: AnimationPlayer - type: UnarmedCombat range: 1.5 arcwidth: 0 arc: claw damage: 90 + - type: Appearance + visuals: + - type: DamageStateVisualizer2D + normal: running + dead: dead + \ No newline at end of file diff --git a/Resources/Textures/Mobs/Species/xeno_hunter.rsi/dead.png b/Resources/Textures/Mobs/Species/xeno_hunter.rsi/dead.png new file mode 100644 index 0000000000..6768e30ca1 Binary files /dev/null and b/Resources/Textures/Mobs/Species/xeno_hunter.rsi/dead.png differ diff --git a/Resources/Textures/Mobs/Species/xeno.rsi/meta.json b/Resources/Textures/Mobs/Species/xeno_hunter.rsi/meta.json similarity index 59% rename from Resources/Textures/Mobs/Species/xeno.rsi/meta.json rename to Resources/Textures/Mobs/Species/xeno_hunter.rsi/meta.json index b6ea7da0c2..c8115c140c 100644 --- a/Resources/Textures/Mobs/Species/xeno.rsi/meta.json +++ b/Resources/Textures/Mobs/Species/xeno_hunter.rsi/meta.json @@ -14,6 +14,20 @@ { "name": "standing", "directions": 4 + }, + { + "name": "dead", + "directions": 1 + }, + { + "name": "sleeping", + "directions": 1, + "delays": [ + [ + 1.0, + 1.0 + ] + ] } ] } \ No newline at end of file diff --git a/Resources/Textures/Mobs/Species/xeno.rsi/running.png b/Resources/Textures/Mobs/Species/xeno_hunter.rsi/running.png similarity index 100% rename from Resources/Textures/Mobs/Species/xeno.rsi/running.png rename to Resources/Textures/Mobs/Species/xeno_hunter.rsi/running.png diff --git a/Resources/Textures/Mobs/Species/xeno_hunter.rsi/sleeping.png b/Resources/Textures/Mobs/Species/xeno_hunter.rsi/sleeping.png new file mode 100644 index 0000000000..1368da006b Binary files /dev/null and b/Resources/Textures/Mobs/Species/xeno_hunter.rsi/sleeping.png differ diff --git a/Resources/Textures/Mobs/Species/xeno.rsi/standing.png b/Resources/Textures/Mobs/Species/xeno_hunter.rsi/standing.png similarity index 100% rename from Resources/Textures/Mobs/Species/xeno.rsi/standing.png rename to Resources/Textures/Mobs/Species/xeno_hunter.rsi/standing.png