diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index 3486e2024b3..cccee48ba5c 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -17,6 +17,7 @@ using Content.Shared.Eye; using Content.Shared.FixedPoint; using Content.Shared.Follower; using Content.Shared.Ghost; +using Content.Shared.GhostTypes; using Content.Shared.Mind; using Content.Shared.Mind.Components; using Content.Shared.Mobs; @@ -68,6 +69,7 @@ namespace Content.Server.Ghost [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly NameModifierSystem _nameMod = default!; + [Dependency] private readonly GhostSpriteStateSystem _ghostState = default!; private EntityQuery _ghostQuery; private EntityQuery _physicsQuery; @@ -481,6 +483,11 @@ namespace Content.Server.Ghost var ghost = SpawnAtPosition(GameTicker.ObserverPrototypeName, spawnPosition.Value); var ghostComponent = Comp(ghost); + if (TryComp(ghost, out var state)) // If more TryComps are added this should be turned into an event + { + _ghostState.SetGhostSprite((ghost, state), mind); + } + // Try setting the ghost entity name to either the character name or the player name. // If all else fails, it'll default to the default entity prototype name, "observer". // However, that should rarely happen. diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index 2b4c5ad3609..17d14679fbd 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -1,7 +1,9 @@ using System.Linq; using Content.Shared.Chemistry; using Content.Shared.Damage.Components; +using Content.Shared.Damage.Prototypes; using Content.Shared.Explosion.EntitySystems; +using Content.Shared.FixedPoint; using Content.Shared.Mobs.Systems; using Robust.Shared.Configuration; using Robust.Shared.GameStates; @@ -68,7 +70,6 @@ public sealed partial class DamageableSystem : EntitySystem // byref struct event. RaiseLocalEvent(ent, new DamageChangedEvent(ent.Comp, damageDelta, interruptsDoAfters, origin)); } - private void DamageableGetState(Entity ent, ref ComponentGetState args) { if (_netMan.IsServer) @@ -94,4 +95,26 @@ public sealed partial class DamageableSystem : EntitySystem ent.Comp.HealthBarThreshold ); } + + /// + /// Goes through an entity damage's and saves them inside a dictionary if the value is higher than 0 + /// The dictionary is structured with a string for the name of the damage type, and a FixedPoint2 for the numeric damage value + /// + public Dictionary, FixedPoint2> GetDamages(Dictionary, FixedPoint2> damagePerGroup, DamageSpecifier damage) + { + var damageTypes = new Dictionary, FixedPoint2>(); + + foreach (var (damageGroupId, _) in damagePerGroup) //go through each group + { + var group = _prototypeManager.Index(damageGroupId); //get group + foreach (var type in group.DamageTypes) //go through each type inside that group + { + if (!damage.DamageDict.TryGetValue(type, out var damageValue) || damageValue == 0) //get value and make sure it isn't 0 + continue; + + damageTypes.Add(type, damageValue); + } + } + return damageTypes; + } } diff --git a/Content.Shared/Ghost/GhostComponent.cs b/Content.Shared/Ghost/GhostComponent.cs index 84416f8abc5..3fc9c081cb8 100644 --- a/Content.Shared/Ghost/GhostComponent.cs +++ b/Content.Shared/Ghost/GhostComponent.cs @@ -1,6 +1,7 @@ using Content.Shared.Actions; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; namespace Content.Shared.Ghost; @@ -95,6 +96,16 @@ public sealed partial class GhostComponent : Component public Color Color = Color.White; } +/// +/// Ghost sprites dependent on damage by the player body +/// +/// Used to change a ghost sprite to better visually represent their cause of death +[Serializable, NetSerializable] +public enum GhostVisuals : byte +{ + Damage +} + public sealed partial class ToggleFoVActionEvent : InstantActionEvent { } public sealed partial class ToggleGhostsActionEvent : InstantActionEvent { } diff --git a/Content.Shared/GhostTypes/GhostSpriteStateComponent.cs b/Content.Shared/GhostTypes/GhostSpriteStateComponent.cs new file mode 100644 index 00000000000..7620117e5a6 --- /dev/null +++ b/Content.Shared/GhostTypes/GhostSpriteStateComponent.cs @@ -0,0 +1,28 @@ +using Content.Shared.Damage.Prototypes; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.GhostTypes; + +/// +/// Changes the entity sprite according to damage taken +/// Slash may be shown by cuts and slashes on the ghost, Heat as flames, Cold as frostbite and ice, Radiation as a green glow, etc. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class GhostSpriteStateComponent : Component +{ + /// + /// Prefix the GhostSpriteStateSystem will add to the name of the damage type it chooses. + /// It should be identical to the prefix of the entity optional damage sprites. + /// (Example) Ghosts sprites currently use a "ghost_" prefix for their optional damage states. + /// + [DataField] + public string Prefix; + + /// + /// Should link damage types names to an int, according to the amount of possible sprites for that specific type. + /// (The GhostSpriteStateSystem will randomly choose between them) + /// + [DataField] + public Dictionary, int> DamageMap = new(); +} diff --git a/Content.Shared/GhostTypes/GhostSpriteStateSystem.cs b/Content.Shared/GhostTypes/GhostSpriteStateSystem.cs new file mode 100644 index 00000000000..ef65387d5e9 --- /dev/null +++ b/Content.Shared/GhostTypes/GhostSpriteStateSystem.cs @@ -0,0 +1,70 @@ +using System.Linq; +using Content.Shared.Damage.Prototypes; +using Content.Shared.Damage.Systems; +using Content.Shared.FixedPoint; +using Content.Shared.Ghost; +using Content.Shared.Mind; +using Content.Shared.Random.Helpers; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; + +namespace Content.Shared.GhostTypes; + +public sealed class GhostSpriteStateSystem : EntitySystem +{ + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + + /// + /// It goes through an entity damage and assigns them a sprite according to the highest damage type/s + /// + public void SetGhostSprite(Entity ent, EntityUid mind) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + if (!TryComp(ent, out var appearance) || !HasComp(mind)) + return; + + var damageTypes = new Dictionary, FixedPoint2>(); + ProtoId? specialCase = null; + + if (!TryComp(mind, out var storedDamage)) + return; + + if (storedDamage.DamagePerGroup != null && storedDamage.Damage != null) + { + damageTypes = _damageable.GetDamages(storedDamage.DamagePerGroup, storedDamage.Damage); + } + specialCase = storedDamage.SpecialCauseOfDeath; + + Dirty(mind, storedDamage); + + var damageTypesSorted = damageTypes.OrderByDescending(x => x.Value).ToDictionary(); + if (damageTypesSorted.Count == 0) + return; + + var highestType = damageTypesSorted.First().Key; // We only need 1 of the values + + // TODO: Replace with RandomPredicted once the engine PR is merged + var seed = SharedRandomExtensions.HashCodeCombine((int)_timing.CurTick.Value, GetNetEntity(ent).Id); + var rand = new System.Random(seed); + + ProtoId? spriteState = null; + + if (specialCase != null) // Possible special cases like death by an explosion + { + var prototype = _proto.Index(specialCase); + spriteState = specialCase + rand.Next(prototype.NumOfStates); + } + else if (ent.Comp.DamageMap.TryGetValue(highestType, out var spriteAmount)) + { + spriteState = highestType + rand.Next(spriteAmount); + } + + if (spriteState != null) + _appearance.SetData(ent, GhostVisuals.Damage, ent.Comp.Prefix + spriteState, appearance); + } +} diff --git a/Content.Shared/GhostTypes/LastBodyDamageComponent.cs b/Content.Shared/GhostTypes/LastBodyDamageComponent.cs new file mode 100644 index 00000000000..773a41b44d1 --- /dev/null +++ b/Content.Shared/GhostTypes/LastBodyDamageComponent.cs @@ -0,0 +1,48 @@ +using Content.Shared.Damage; +using Content.Shared.Damage.Prototypes; +using Content.Shared.FixedPoint; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.GhostTypes; + +/// +/// Added to the Mind of an entity by the StoreDamageTakenOnMindSystem, allowing storage of the damage values their body had. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class LastBodyDamageComponent : Component +{ + /// + /// Dictionary DamageGroupPrototype proto ids to how much damage was received from that damage type. + /// + [DataField, AutoNetworkedField] + public Dictionary, FixedPoint2>? DamagePerGroup; + + /// + /// Collection of possible damage types, stored by the StoreDamageTakenOnMind. + /// + [DataField, AutoNetworkedField] + public DamageSpecifier? Damage; + + /// + /// Special death cause that's saved after an event related to it is triggered + /// For example, a BeforeExplodeEvent will save "Explosion" as the special cause of death + /// + [DataField, AutoNetworkedField] + public ProtoId? SpecialCauseOfDeath = null; +} + +/// +/// Prototype for special causes of death (such as "Explosion") +/// +[Prototype] +public sealed partial class SpecialCauseOfDeathPrototype : IPrototype +{ + [ViewVariables, IdDataField] + public string ID { get; private set; } = string.Empty; + + // Specifies the amount of possible sprites for a special cause of death + // These values are set up in the special_cause_of_death_types.yml file + [DataField] + public int NumOfStates; +} diff --git a/Content.Shared/GhostTypes/StoreDamageTakenOnMindComponent.cs b/Content.Shared/GhostTypes/StoreDamageTakenOnMindComponent.cs new file mode 100644 index 00000000000..f4402cc09b7 --- /dev/null +++ b/Content.Shared/GhostTypes/StoreDamageTakenOnMindComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.GhostTypes; + +/// +/// Stores the damage an entity took before their body is destroyed inside it's mind LastBodyDamageComponent +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class StoreDamageTakenOnMindComponent : Component; diff --git a/Content.Shared/GhostTypes/StoreDamageTakenOnMindSystem.cs b/Content.Shared/GhostTypes/StoreDamageTakenOnMindSystem.cs new file mode 100644 index 00000000000..0e985b0b2d8 --- /dev/null +++ b/Content.Shared/GhostTypes/StoreDamageTakenOnMindSystem.cs @@ -0,0 +1,101 @@ +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Prototypes; +using Content.Shared.Destructible; +using Content.Shared.Explosion; +using Content.Shared.FixedPoint; +using Content.Shared.Mind; +using Content.Shared.Mind.Components; +using Content.Shared.Mobs; +using Robust.Shared.Prototypes; + +namespace Content.Shared.GhostTypes; + +public sealed class StoreDamageTakenOnMindSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _proto = default!; + public override void Initialize() + { + SubscribeLocalEvent(SaveBodyOnGib); + SubscribeLocalEvent(SaveBodyOnThreshold); + SubscribeLocalEvent(DeathByExplosion); + } + + /// + /// Saves the damage of a player body inside their MindComponent after a DestructionEventArgs + /// + private void SaveBodyOnGib(Entity ent, ref DestructionEventArgs args) + { + SaveBody(ent.Owner); + } + + /// + /// Saves the damage of a player body inside their MindComponent after a damage threshold event + /// + private void SaveBodyOnThreshold(Entity ent, ref MobStateChangedEvent args) + { + if (args.NewMobState != MobState.Dead) + ClearSpecialCause(ent); + + SaveBody(ent.Owner); + } + + private void DeathByExplosion(Entity ent, ref BeforeExplodeEvent args) + { + SaveSpecialCauseOfDeath(ent, "Explosion"); + } + + /// + /// Gets an entity Mind and stores it's current body damages inside of it's LastBodyDamageComponent + /// + private void SaveBody(EntityUid ent) + { + if (!TryComp(ent, out var damageable) + || !TryComp(ent, out var mindContainer) + || !HasComp(mindContainer.Mind)) + return; + + EnsureComp(mindContainer.Mind.Value, out var storedDamage); + + var protoDict = new Dictionary, FixedPoint2>(); + foreach (var stringDict in damageable.DamagePerGroup) // Translates the strings into ProtoId's before saving the Dictionary + { + if (!_proto.TryIndex(stringDict.Key, out DamageGroupPrototype? proto)) + continue; + protoDict.TryAdd(proto, stringDict.Value); + } + + storedDamage.DamagePerGroup = protoDict; + storedDamage.Damage = damageable.Damage; + Dirty(mindContainer.Mind.Value, storedDamage); + } + + /// + /// Saves an specific cause of death inside of an entity LastBodyDamageComponent + /// + private void SaveSpecialCauseOfDeath(EntityUid ent, ProtoId cause) + { + if (!TryComp(ent, out var mindContainer) + || !HasComp(mindContainer.Mind)) + return; + + EnsureComp(mindContainer.Mind.Value, out var storedDamage); + + storedDamage.SpecialCauseOfDeath = cause; + Dirty(mindContainer.Mind.Value, storedDamage); + } + + /// + /// Clears the specific cause of death of an entity LastBodyDamageComponent + /// + private void ClearSpecialCause(EntityUid ent) + { + if (!TryComp(ent, out var mindContainer) + || !HasComp(mindContainer.Mind)) + return; + + EnsureComp(mindContainer.Mind.Value, out var storedDamage); + + storedDamage.SpecialCauseOfDeath = null; + Dirty(mindContainer.Mind.Value, storedDamage); + } +} diff --git a/Resources/Prototypes/Damage/special_cause_of_death_types.yml b/Resources/Prototypes/Damage/special_cause_of_death_types.yml new file mode 100644 index 00000000000..625008b6e37 --- /dev/null +++ b/Resources/Prototypes/Damage/special_cause_of_death_types.yml @@ -0,0 +1,3 @@ +- type: specialCauseOfDeath + id: Explosion + numOfStates: 3 diff --git a/Resources/Prototypes/Entities/Mobs/Player/observer.yml b/Resources/Prototypes/Entities/Mobs/Player/observer.yml index 08bdbe8ebbb..d79aa3c9d21 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/observer.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/observer.yml @@ -44,7 +44,32 @@ color: "#fff8" layers: - state: animated + map: [ "ghostVariant" ] shader: unshaded + - type: Appearance + - type: GenericVisualizer + visuals: + enum.GhostVisuals.Damage: + ghostVariant: + ghost_Blunt0: { state: ghost_Blunt0 } # this is an icki way of doing this u.u -Thinbug + ghost_Blunt1: { state: ghost_Blunt1 } + ghost_Blunt2: { state: ghost_Blunt2 } + ghost_Slash0: { state: ghost_Slash0 } + ghost_Slash1: { state: ghost_Slash1 } + ghost_Slash2: { state: ghost_Slash2 } + ghost_Piercing0: { state: ghost_Piercing0 } + ghost_Piercing1: { state: ghost_Piercing1 } + ghost_Piercing2: { state: ghost_Piercing2 } + ghost_Heat0: { state: ghost_Heat0 } + ghost_Cold0: { state: ghost_Cold0 } + ghost_Shock0: { state: ghost_Shock0 } + ghost_Radiation0: { state: ghost_Radiation0 } + ghost_Cellular0: { state: ghost_Cellular0 } + ghost_Poison0: { state: ghost_Poison0 } + ghost_Asphyxiation0: { state: ghost_Asphyxiation0 } + ghost_Explosion0: { state: ghost_Explosion0 } + ghost_Explosion1: { state: ghost_Explosion1 } + ghost_Explosion2: { state: ghost_Explosion2 } - type: ContentEye maxZoom: 1.44,1.44 - type: Eye @@ -54,6 +79,19 @@ - type: Examiner skipChecks: true - type: Ghost + - type: GhostSpriteState + damageMap: + Blunt: 3 + Slash: 3 + Piercing: 3 + Heat: 1 + Cold: 1 + Shock: 1 + Radiation: 1 + Cellular: 1 + Poison: 1 + Asphyxiation: 1 + prefix: "ghost_" - type: GhostHearing - type: ShowElectrocutionHUD - type: IntrinsicRadioReceiver diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index 376d8e35b3e..2e672af0cca 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -123,6 +123,7 @@ - ActionCritSuccumb - ActionCritFakeDeath - ActionCritLastWords + - type: StoreDamageTakenOnMind - type: Deathgasp - type: HealthExaminable examinableTypes: diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Asphyxiation0.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Asphyxiation0.png new file mode 100644 index 00000000000..928b19672cc Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Asphyxiation0.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Blunt0.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Blunt0.png new file mode 100644 index 00000000000..fac4a13d20f Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Blunt0.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Blunt1.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Blunt1.png new file mode 100644 index 00000000000..a9b274699ab Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Blunt1.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Blunt2.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Blunt2.png new file mode 100644 index 00000000000..001f7f1382b Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Blunt2.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Cellular0.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Cellular0.png new file mode 100644 index 00000000000..29383b6f3a4 Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Cellular0.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Cold0.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Cold0.png new file mode 100644 index 00000000000..000751088d5 Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Cold0.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Explosion0.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Explosion0.png new file mode 100644 index 00000000000..efd2a1036d9 Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Explosion0.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Explosion1.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Explosion1.png new file mode 100644 index 00000000000..f619859e959 Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Explosion1.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Explosion2.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Explosion2.png new file mode 100644 index 00000000000..31586d70462 Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Explosion2.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Heat0.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Heat0.png new file mode 100644 index 00000000000..bacaf3e3890 Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Heat0.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Piercing0.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Piercing0.png new file mode 100644 index 00000000000..fd38128a9be Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Piercing0.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Piercing1.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Piercing1.png new file mode 100644 index 00000000000..52456d9ccbd Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Piercing1.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Piercing2.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Piercing2.png new file mode 100644 index 00000000000..e6fb28a66d0 Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Piercing2.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Poison0.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Poison0.png new file mode 100644 index 00000000000..0b746bb1fc5 Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Poison0.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Radiation0.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Radiation0.png new file mode 100644 index 00000000000..2d80fd7baad Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Radiation0.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Shock0.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Shock0.png new file mode 100644 index 00000000000..a5b5e4b5f24 Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Shock0.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Slash0.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Slash0.png new file mode 100644 index 00000000000..60ea51d3b36 Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Slash0.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Slash1.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Slash1.png new file mode 100644 index 00000000000..261f6b5aa22 Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Slash1.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Slash2.png b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Slash2.png new file mode 100644 index 00000000000..414518dfc39 Binary files /dev/null and b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Slash2.png differ diff --git a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/meta.json b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/meta.json index a465e90b6cf..bead5cea3d5 100644 --- a/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/meta.json +++ b/Resources/Textures/Mobs/Ghosts/ghost_human.rsi/meta.json @@ -1,26 +1,188 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "https://github.com/tgstation/tgstation/blob/f80e7ba62d27c77cfeac709dd71033744d0015c4/icons/mob/mob.dmi, inhand sprites by TiniestShark (github)", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "animated", - "directions": 4 - }, - { - "name": "icon" - }, - { - "name": "inhand-left", - "directions": 4 - }, - { - "name": "inhand-right", - "directions": 4 - } - ] -} + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/tgstation/tgstation/blob/f80e7ba62d27c77cfeac709dd71033744d0015c4/icons/mob/mob.dmi, inhand sprites by TiniestShark (github), damage sprites by Thinbug (github)", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "animated", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "ghost_Blunt0", + "directions": 4 + }, + { + "name": "ghost_Blunt1", + "directions": 4 + }, + { + "name": "ghost_Blunt2", + "directions": 4 + }, + { + "name": "ghost_Slash0", + "directions": 4 + }, + { + "name": "ghost_Slash1", + "directions": 4 + }, + { + "name": "ghost_Slash2", + "directions": 4 + }, + { + "name": "ghost_Piercing0", + "directions": 4 + }, + { + "name": "ghost_Piercing1", + "directions": 4 + }, + { + "name": "ghost_Piercing2", + "directions": 4 + }, + { + "name": "ghost_Heat0", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "ghost_Cold0", + "directions": 4 + }, + { + "name": "ghost_Shock0", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "ghost_Radiation0", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "ghost_Cellular0", + "directions": 4 + }, + { + "name": "ghost_Poison0", + "directions": 4 + }, + { + "name": "ghost_Asphyxiation0", + "directions": 4 + }, + { + "name": "ghost_Explosion0", + "directions": 4 + }, + { + "name": "ghost_Explosion1", + "directions": 4 + }, + { + "name": "ghost_Explosion2", + "directions": 4 + } + ] + }