Ghost types (#37949)
* Empty commit * yeah thingi * added a GetHighestDamageTypes thingi to the DamageableSystem * no idea why those files names are different only in github so just in case readding them * yeah doing that * first steps of moving the logic somewhere nicer * still plenty to do * gosh such a mess but getting progress done * small fixie push * big mess of bunch of stuff * dealing with a conflict and fixing the random numbers * testing if github will update now * dealing with the other conflict * github please update i beg you * dealing with more conflicts * hopefully this fixes it * fixing conflicts again * cleaning up stuffies * sprite fixie * general cleanup * doing the small fixies first * getting rid of the new event, gotta handle ashing next * adding spaces to comments before i forget * handling ashing * think that did it? * small fixies * more small fixies * last batch of quickie fixies before i gotta handle the bigger stuff * last bunch of fixies i do understand * small bit of progress yknow may as well yeah * renaming and moving stuff to shared * comment fixiees * saving damage in a new component instead of in MindComponent * protoid's and dict usage instead of the previously ickier methods * small fixie before biggie fixie * more fixies im slepy gosh * thinkie that should fixie it * smoothed the damage storage systeem so its less repetitive and icki and now itss cooler and i can go eepy * lots of stuffies x3 * first step of getting git to detect my file name changes * thinkie that should fixie it * fixies * just getting rid of the merge conflict, will check damageable later * small thingies first * more small stuffiees * now all of the sprites have at leeast a 0 * dirtying the lastbody comp * more fixies * small thingi first * another small fixie and a minor sprite fixie * rng fixie * moving the damage storage system to shared * smoothing out code thats likely to be replaced soon but its good to do for now * just showing progress bcus yis * general progress stuffies mhm * pushie * small cleanup * general progress :3 * in progress push for helpie * proper pushie with progress and workies * removed unnecessary usage of the storedamage component * minor fixiees * extra comments * replaced a couple strings for ProtoId's * gibbing related fixies :3
@@ -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<GhostComponent> _ghostQuery;
|
||||
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||
@@ -481,6 +483,11 @@ namespace Content.Server.Ghost
|
||||
var ghost = SpawnAtPosition(GameTicker.ObserverPrototypeName, spawnPosition.Value);
|
||||
var ghostComponent = Comp<GhostComponent>(ghost);
|
||||
|
||||
if (TryComp<GhostSpriteStateComponent>(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.
|
||||
|
||||
@@ -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<DamageableComponent> ent, ref ComponentGetState args)
|
||||
{
|
||||
if (_netMan.IsServer)
|
||||
@@ -94,4 +95,26 @@ public sealed partial class DamageableSystem : EntitySystem
|
||||
ent.Comp.HealthBarThreshold
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public Dictionary<ProtoId<DamageTypePrototype>, FixedPoint2> GetDamages(Dictionary<ProtoId<DamageGroupPrototype>, FixedPoint2> damagePerGroup, DamageSpecifier damage)
|
||||
{
|
||||
var damageTypes = new Dictionary<ProtoId<DamageTypePrototype>, FixedPoint2>();
|
||||
|
||||
foreach (var (damageGroupId, _) in damagePerGroup) //go through each group
|
||||
{
|
||||
var group = _prototypeManager.Index<DamageGroupPrototype>(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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ghost sprites dependent on damage by the player body
|
||||
/// </summary>
|
||||
/// <remarks>Used to change a ghost sprite to better visually represent their cause of death</remarks>
|
||||
[Serializable, NetSerializable]
|
||||
public enum GhostVisuals : byte
|
||||
{
|
||||
Damage
|
||||
}
|
||||
|
||||
public sealed partial class ToggleFoVActionEvent : InstantActionEvent { }
|
||||
|
||||
public sealed partial class ToggleGhostsActionEvent : InstantActionEvent { }
|
||||
|
||||
28
Content.Shared/GhostTypes/GhostSpriteStateComponent.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.GhostTypes;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class GhostSpriteStateComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string Prefix;
|
||||
|
||||
/// <summary>
|
||||
/// 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)
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Dictionary<ProtoId<DamageTypePrototype>, int> DamageMap = new();
|
||||
}
|
||||
70
Content.Shared/GhostTypes/GhostSpriteStateSystem.cs
Normal file
@@ -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!;
|
||||
|
||||
/// <summary>
|
||||
/// It goes through an entity damage and assigns them a sprite according to the highest damage type/s
|
||||
/// </summary>
|
||||
public void SetGhostSprite(Entity<GhostSpriteStateComponent?> ent, EntityUid mind)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp))
|
||||
return;
|
||||
|
||||
if (!TryComp<AppearanceComponent>(ent, out var appearance) || !HasComp<MindComponent>(mind))
|
||||
return;
|
||||
|
||||
var damageTypes = new Dictionary<ProtoId<DamageTypePrototype>, FixedPoint2>();
|
||||
ProtoId<SpecialCauseOfDeathPrototype>? specialCase = null;
|
||||
|
||||
if (!TryComp<LastBodyDamageComponent>(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<DamageTypePrototype>? 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);
|
||||
}
|
||||
}
|
||||
48
Content.Shared/GhostTypes/LastBodyDamageComponent.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Added to the Mind of an entity by the StoreDamageTakenOnMindSystem, allowing storage of the damage values their body had.
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class LastBodyDamageComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionary DamageGroupPrototype proto ids to how much damage was received from that damage type.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public Dictionary<ProtoId<DamageGroupPrototype>, FixedPoint2>? DamagePerGroup;
|
||||
|
||||
/// <summary>
|
||||
/// Collection of possible damage types, stored by the StoreDamageTakenOnMind.
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public DamageSpecifier? Damage;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public ProtoId<SpecialCauseOfDeathPrototype>? SpecialCauseOfDeath = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prototype for special causes of death (such as "Explosion")
|
||||
/// </summary>
|
||||
[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;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.GhostTypes;
|
||||
|
||||
/// <summary>
|
||||
/// Stores the damage an entity took before their body is destroyed inside it's mind LastBodyDamageComponent
|
||||
/// </summary>
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class StoreDamageTakenOnMindComponent : Component;
|
||||
101
Content.Shared/GhostTypes/StoreDamageTakenOnMindSystem.cs
Normal file
@@ -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<StoreDamageTakenOnMindComponent, DestructionEventArgs>(SaveBodyOnGib);
|
||||
SubscribeLocalEvent<StoreDamageTakenOnMindComponent, MobStateChangedEvent>(SaveBodyOnThreshold);
|
||||
SubscribeLocalEvent<StoreDamageTakenOnMindComponent, BeforeExplodeEvent>(DeathByExplosion);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the damage of a player body inside their MindComponent after a DestructionEventArgs
|
||||
/// </summary>
|
||||
private void SaveBodyOnGib(Entity<StoreDamageTakenOnMindComponent> ent, ref DestructionEventArgs args)
|
||||
{
|
||||
SaveBody(ent.Owner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves the damage of a player body inside their MindComponent after a damage threshold event
|
||||
/// </summary>
|
||||
private void SaveBodyOnThreshold(Entity<StoreDamageTakenOnMindComponent> ent, ref MobStateChangedEvent args)
|
||||
{
|
||||
if (args.NewMobState != MobState.Dead)
|
||||
ClearSpecialCause(ent);
|
||||
|
||||
SaveBody(ent.Owner);
|
||||
}
|
||||
|
||||
private void DeathByExplosion(Entity<StoreDamageTakenOnMindComponent> ent, ref BeforeExplodeEvent args)
|
||||
{
|
||||
SaveSpecialCauseOfDeath(ent, "Explosion");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an entity Mind and stores it's current body damages inside of it's LastBodyDamageComponent
|
||||
/// </summary>
|
||||
private void SaveBody(EntityUid ent)
|
||||
{
|
||||
if (!TryComp<DamageableComponent>(ent, out var damageable)
|
||||
|| !TryComp<MindContainerComponent>(ent, out var mindContainer)
|
||||
|| !HasComp<MindComponent>(mindContainer.Mind))
|
||||
return;
|
||||
|
||||
EnsureComp<LastBodyDamageComponent>(mindContainer.Mind.Value, out var storedDamage);
|
||||
|
||||
var protoDict = new Dictionary<ProtoId<DamageGroupPrototype>, 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves an specific cause of death inside of an entity LastBodyDamageComponent
|
||||
/// </summary>
|
||||
private void SaveSpecialCauseOfDeath(EntityUid ent, ProtoId<SpecialCauseOfDeathPrototype> cause)
|
||||
{
|
||||
if (!TryComp<MindContainerComponent>(ent, out var mindContainer)
|
||||
|| !HasComp<MindComponent>(mindContainer.Mind))
|
||||
return;
|
||||
|
||||
EnsureComp<LastBodyDamageComponent>(mindContainer.Mind.Value, out var storedDamage);
|
||||
|
||||
storedDamage.SpecialCauseOfDeath = cause;
|
||||
Dirty(mindContainer.Mind.Value, storedDamage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the specific cause of death of an entity LastBodyDamageComponent
|
||||
/// </summary>
|
||||
private void ClearSpecialCause(EntityUid ent)
|
||||
{
|
||||
if (!TryComp<MindContainerComponent>(ent, out var mindContainer)
|
||||
|| !HasComp<MindComponent>(mindContainer.Mind))
|
||||
return;
|
||||
|
||||
EnsureComp<LastBodyDamageComponent>(mindContainer.Mind.Value, out var storedDamage);
|
||||
|
||||
storedDamage.SpecialCauseOfDeath = null;
|
||||
Dirty(mindContainer.Mind.Value, storedDamage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
- type: specialCauseOfDeath
|
||||
id: Explosion
|
||||
numOfStates: 3
|
||||
@@ -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
|
||||
|
||||
@@ -123,6 +123,7 @@
|
||||
- ActionCritSuccumb
|
||||
- ActionCritFakeDeath
|
||||
- ActionCritLastWords
|
||||
- type: StoreDamageTakenOnMind
|
||||
- type: Deathgasp
|
||||
- type: HealthExaminable
|
||||
examinableTypes:
|
||||
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
BIN
Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Blunt0.png
Normal file
|
After Width: | Height: | Size: 946 B |
BIN
Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Blunt1.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Blunt2.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 821 B |
BIN
Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Cold0.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 870 B |
|
After Width: | Height: | Size: 876 B |
|
After Width: | Height: | Size: 914 B |
BIN
Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Heat0.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 733 B |
|
After Width: | Height: | Size: 740 B |
|
After Width: | Height: | Size: 844 B |
BIN
Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Poison0.png
Normal file
|
After Width: | Height: | Size: 972 B |
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Shock0.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Slash0.png
Normal file
|
After Width: | Height: | Size: 696 B |
BIN
Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Slash1.png
Normal file
|
After Width: | Height: | Size: 831 B |
BIN
Resources/Textures/Mobs/Ghosts/ghost_human.rsi/ghost_Slash2.png
Normal file
|
After Width: | Height: | Size: 725 B |
@@ -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
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||