Damage visualizer for simple mobs (#1332)
Co-authored-by: Metal Gear Sloth <metalgearsloth@gmail.com> Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
This commit is contained in:
@@ -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<DamageStateVisualData, string> _stateMap = new Dictionary<DamageStateVisualData,string>();
|
||||||
|
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<ISpriteComponent>();
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -116,7 +116,7 @@ namespace Content.Server.AI.Utility.AiLogic
|
|||||||
_planner = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AiActionSystem>();
|
_planner = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AiActionSystem>();
|
||||||
if (SelfEntity.TryGetComponent(out DamageableComponent damageableComponent))
|
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?
|
// TODO: If DamageableComponent removed still need to unsubscribe?
|
||||||
if (SelfEntity.TryGetComponent(out DamageableComponent damageableComponent))
|
if (SelfEntity.TryGetComponent(out DamageableComponent damageableComponent))
|
||||||
{
|
{
|
||||||
damageableComponent.DamageThresholdPassed -= DeathHandle;
|
damageableComponent.DamageThresholdPassed -= DamageThresholdHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentOp = CurrentAction?.ActionOperators.Peek();
|
var currentOp = CurrentAction?.ActionOperators.Peek();
|
||||||
currentOp?.Shutdown(Outcome.Failed);
|
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;
|
_isDead = true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// TODO: If we get healed - double-check what it should be
|
|
||||||
if (eventArgs.DamageThreshold.ThresholdType == ThresholdType.None)
|
|
||||||
{
|
{
|
||||||
_isDead = false;
|
_isDead = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using Content.Server.GameObjects.Components.Mobs;
|
using Content.Server.GameObjects.Components.Mobs;
|
||||||
using Content.Server.Mobs;
|
using Content.Server.Mobs;
|
||||||
|
using Content.Shared.GameObjects.Components.Mobs;
|
||||||
using Content.Shared.GameObjects.EntitySystems;
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.GameObjects.Components;
|
using Robust.Shared.GameObjects.Components;
|
||||||
using Robust.Shared.Interfaces.GameObjects;
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
|
||||||
@@ -25,6 +27,8 @@ namespace Content.Server.GameObjects
|
|||||||
{
|
{
|
||||||
public void EnterState(IEntity entity)
|
public void EnterState(IEntity entity)
|
||||||
{
|
{
|
||||||
|
entity.TryGetComponent(out AppearanceComponent appearanceComponent);
|
||||||
|
appearanceComponent?.SetData(DamageStateVisuals.State, DamageStateVisualData.Normal);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ExitState(IEntity entity)
|
public void ExitState(IEntity entity)
|
||||||
@@ -104,6 +108,8 @@ namespace Content.Server.GameObjects
|
|||||||
if(entity.TryGetComponent(out StunnableComponent stun))
|
if(entity.TryGetComponent(out StunnableComponent stun))
|
||||||
stun.CancelAll();
|
stun.CancelAll();
|
||||||
|
|
||||||
|
entity.TryGetComponent(out AppearanceComponent appearanceComponent);
|
||||||
|
appearanceComponent?.SetData(DamageStateVisuals.State, DamageStateVisualData.Crit);
|
||||||
StandingStateHelper.Down(entity);
|
StandingStateHelper.Down(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,6 +192,8 @@ namespace Content.Server.GameObjects
|
|||||||
stun.CancelAll();
|
stun.CancelAll();
|
||||||
|
|
||||||
StandingStateHelper.Down(entity);
|
StandingStateHelper.Down(entity);
|
||||||
|
entity.TryGetComponent(out AppearanceComponent appearanceComponent);
|
||||||
|
appearanceComponent?.SetData(DamageStateVisuals.State, DamageStateVisualData.Dead);
|
||||||
|
|
||||||
if (entity.TryGetComponent(out ICollidableComponent collidable))
|
if (entity.TryGetComponent(out ICollidableComponent collidable))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -634,6 +634,10 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering
|
|||||||
// God there's so many ways to do this
|
// 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
|
// 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);
|
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
|
// 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
|
// 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
|
// i.e. towards the right
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
# Hacky for the stress test so don't even consider adding to this
|
# Hacky for the stress test so don't even consider adding to this
|
||||||
- type: entity
|
- type: entity
|
||||||
save: false
|
save: false
|
||||||
name: Xeno
|
name: Xeno hunter
|
||||||
id: XenoMob_Content
|
id: XenoMob_Content
|
||||||
description: They mostly come at night. Mostly.
|
description: They mostly come at night. Mostly.
|
||||||
drawdepth: Mobs
|
drawdepth: Mobs
|
||||||
@@ -14,15 +14,16 @@
|
|||||||
- left
|
- left
|
||||||
- right
|
- right
|
||||||
- type: MovementSpeedModifier
|
- type: MovementSpeedModifier
|
||||||
# Organs
|
|
||||||
- type: InteractionOutline
|
- type: InteractionOutline
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
drawdepth: Mobs
|
drawdepth: Mobs
|
||||||
sprite: Mobs/Species/xeno.rsi
|
layers:
|
||||||
state: running
|
- map: ["enum.DamageStateVisualLayers.Base"]
|
||||||
|
state: running
|
||||||
|
sprite: Mobs/Species/xeno_hunter.rsi
|
||||||
- type: Icon
|
- type: Icon
|
||||||
sprite: Mobs/Species/xeno.rsi
|
sprite: Mobs/Species/xeno_hunter.rsi
|
||||||
state: running
|
state: standing
|
||||||
- type: Clickable
|
- type: Clickable
|
||||||
- type: Physics
|
- type: Physics
|
||||||
mass: 85
|
mass: 85
|
||||||
@@ -48,13 +49,16 @@
|
|||||||
- type: Damageable
|
- type: Damageable
|
||||||
- type: CombatMode
|
- type: CombatMode
|
||||||
- type: Teleportable
|
- type: Teleportable
|
||||||
- type: CharacterInfo
|
|
||||||
- type: FootstepSound
|
- type: FootstepSound
|
||||||
- type: HumanoidAppearance
|
|
||||||
- type: Stunnable
|
- type: Stunnable
|
||||||
- type: AnimationPlayer
|
|
||||||
- type: UnarmedCombat
|
- type: UnarmedCombat
|
||||||
range: 1.5
|
range: 1.5
|
||||||
arcwidth: 0
|
arcwidth: 0
|
||||||
arc: claw
|
arc: claw
|
||||||
damage: 90
|
damage: 90
|
||||||
|
- type: Appearance
|
||||||
|
visuals:
|
||||||
|
- type: DamageStateVisualizer2D
|
||||||
|
normal: running
|
||||||
|
dead: dead
|
||||||
|
|
||||||
BIN
Resources/Textures/Mobs/Species/xeno_hunter.rsi/dead.png
Normal file
BIN
Resources/Textures/Mobs/Species/xeno_hunter.rsi/dead.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 KiB |
@@ -14,6 +14,20 @@
|
|||||||
{
|
{
|
||||||
"name": "standing",
|
"name": "standing",
|
||||||
"directions": 4
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "dead",
|
||||||
|
"directions": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sleeping",
|
||||||
|
"directions": 1,
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
1.0,
|
||||||
|
1.0
|
||||||
|
]
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
BIN
Resources/Textures/Mobs/Species/xeno_hunter.rsi/sleeping.png
Normal file
BIN
Resources/Textures/Mobs/Species/xeno_hunter.rsi/sleeping.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Reference in New Issue
Block a user