diff --git a/Content.Client/Changeling/Systems/ChangelingLastResortSystem.cs b/Content.Client/Changeling/Systems/ChangelingLastResortSystem.cs new file mode 100644 index 0000000000..62560c4310 --- /dev/null +++ b/Content.Client/Changeling/Systems/ChangelingLastResortSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Changeling.Systems; + +namespace Content.Client.Changeling.Systems; + +public sealed partial class ChangelingLastResortSystem : SharedChangelingLastResortSystem; diff --git a/Content.Server/Antag/AntagSelectionSystem.API.Assignment.cs b/Content.Server/Antag/AntagSelectionSystem.API.Assignment.cs index 46741d1978..4259916c77 100644 --- a/Content.Server/Antag/AntagSelectionSystem.API.Assignment.cs +++ b/Content.Server/Antag/AntagSelectionSystem.API.Assignment.cs @@ -497,4 +497,26 @@ public sealed partial class AntagSelectionSystem GameTicker.StartGameRule(ruleEnt); return (ruleEnt, antag); } + + /// + /// Assigns components to an entity based on a + /// + /// The entity to give the components. + /// The prototype to apply the components from. + [PublicAPI] + public void AssignAntagComponents(EntityUid entity, ProtoId antag) + { + // The following is where we apply components, equipment, and other changes to our antagonist entity. + if (!ProtoMan.Resolve(antag, out var antagPrototype)) + return; + + AssignAntagComponents(entity, antagPrototype); + } + + /// + [PublicAPI] + public void AssignAntagComponents(EntityUid entity, AntagSpecifierPrototype antag) + { + EntityManager.AddComponents(entity, antag.Components); + } } diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index 0ec6e2603c..8ca6c70e6e 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -26,6 +26,7 @@ using Content.Shared.GameTicking.Components; using Content.Shared.Random.Helpers; using Content.Shared.Roles; using Content.Shared.Whitelist; +using JetBrains.Annotations; using Robust.Server.Audio; using Robust.Server.GameObjects; using Robust.Server.Player; @@ -780,7 +781,7 @@ public sealed partial class AntagSelectionSystem : GameRuleSystem> gear = new(); diff --git a/Content.Server/Changeling/Systems/ChangelingLastResortSystem.cs b/Content.Server/Changeling/Systems/ChangelingLastResortSystem.cs new file mode 100644 index 0000000000..dbfbf6df57 --- /dev/null +++ b/Content.Server/Changeling/Systems/ChangelingLastResortSystem.cs @@ -0,0 +1,109 @@ +using Content.Server.Antag; +using Content.Shared.Administration.Systems; +using Content.Shared.Antag; +using Content.Shared.Changeling.Components; +using Content.Shared.Changeling.Systems; +using Content.Shared.DoAfter; +using Content.Shared.Humanoid; +using Content.Shared.Mind; +using Content.Shared.Mobs.Systems; +using Content.Shared.Popups; +using Robust.Shared.Prototypes; + +namespace Content.Server.Changeling.Systems; + +public sealed partial class ChangelingLastResortSystem : SharedChangelingLastResortSystem +{ + private static readonly ProtoId ChangelingAntag = "Changeling"; + + [Dependency] private RejuvenateSystem _rejuvenate = default!; + [Dependency] private SharedMindSystem _mind = default!; + [Dependency] private MobStateSystem _mobState = default!; + [Dependency] private SharedPopupSystem _popup = default!; + [Dependency] private SharedDoAfterSystem _doAfter = default!; + [Dependency] private AntagSelectionSystem _antag = default!; + + [SubscribeLocalEvent] + private void OnTakeOverCorpseAction(Entity ent, + ref ChangelingTakeOverCorpseActionEvent args) + { + // CanTakeOver checks for the existence of ChangelingIdentityComponent on the target. + // As such it cannot be predicted as that component is not networked to sessions other than the owner + // Unless we do fucky networking magic + if (args.Handled || !CanTakeOver(ent.Owner, args.Target)) + return; + + args.Handled = true; + + Audio.PlayPvs(ent.Comp.Sound, ent.Owner); + _popup.PopupEntity(Loc.GetString("changeling-takeover-start-others", ("user", ent.Owner)), + ent.Owner, + PopupType.MediumCaution); + + var doAfter = new DoAfterArgs(EntityManager, + ent.Owner, + ent.Comp.TakeOverDuration, + new ChangelingTakeOverCorpseDoAfterEvent(), + ent, + target: args.Target) + { + BreakOnDamage = true, + BreakOnMove = true, + DuplicateCondition = DuplicateConditions.None, + RequireCanInteract = false, + }; + + _doAfter.TryStartDoAfter(doAfter); + } + + /// + /// Checks whether a changeling slug can take over the body. + /// + private bool CanTakeOver(EntityUid user, EntityUid target, bool showPopups = true) + { + if (!HasComp(target)) + return false; + + if (HasComp(target)) + { + if (showPopups) + _popup.PopupEntity(Loc.GetString("changeling-takeover-is-changeling"), user); + return false; + } + + if (_mobState.IsDead(target)) + return true; + + if (showPopups) + _popup.PopupEntity(Loc.GetString("changeling-takeover-not-dead"), user); + return false; + } + + [SubscribeLocalEvent] + private void OnTakeOverCorpseDoAfter(Entity ent, + ref ChangelingTakeOverCorpseDoAfterEvent args) + { + args.Handled = true; + + if (args.Cancelled || args.Target is not { } target || !CanTakeOver(args.User, target, showPopups: false)) + return; + + if (!_mind.TryGetMind(args.User, out var mindId, out var mind)) + return; + + TakeOverCorpse(args.User, target, mindId, mind); + + _popup.PopupEntity(Loc.GetString("changeling-takeover-success-self"), target, target, PopupType.Large); + } + + private void TakeOverCorpse(EntityUid user, EntityUid target, EntityUid mindId, MindComponent mind) + { + // TODO: delete this after adding the stasis. + _rejuvenate.PerformRejuvenate(target); + _mind.TransferTo(mindId, target, mind: mind); + + _antag.AssignAntagComponents(target, ChangelingAntag); + + QueueDel(user); + } +} diff --git a/Content.Server/Cloning/CloningSystem.cs b/Content.Server/Cloning/CloningSystem.cs index eef5ac02a7..2abf6f7e25 100644 --- a/Content.Server/Cloning/CloningSystem.cs +++ b/Content.Server/Cloning/CloningSystem.cs @@ -88,6 +88,7 @@ public sealed partial class CloningSystem : SharedCloningSystem // Set the clone's name. The raised events will also adjust their PDA and ID card names. _metaData.SetEntityName(clone.Value, originalName, raiseEvents: settings.RaiseEntityRenamedEvent); + _metaData.SetEntityDescription(clone.Value, Description(original)); _identity.QueueIdentityUpdate(clone.Value); // We have to manually refresh the identity in case we did not raise events. _adminLogger.Add(LogType.Chat, LogImpact.Medium, $"The body of {original:player} was cloned as {clone.Value:player}"); diff --git a/Content.Shared/Changeling/Components/ChangelingLastResortAbilityComponent.cs b/Content.Shared/Changeling/Components/ChangelingLastResortAbilityComponent.cs new file mode 100644 index 0000000000..73ed0dcf13 --- /dev/null +++ b/Content.Shared/Changeling/Components/ChangelingLastResortAbilityComponent.cs @@ -0,0 +1,30 @@ +using Content.Shared.Actions; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Changeling.Components; + +/// +/// Allows a changeling to gib their current body and escape as a head slug. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class ChangelingLastResortAbilityComponent : Component +{ + /// + /// The prototype ID of the slug to spawn. + /// + [DataField] + public EntProtoId SlugPrototype = "MobHeadSlug"; + + /// + /// The sound to play when the changeling gibbs. + /// + [DataField] + public SoundSpecifier Sound = new SoundCollectionSpecifier("gib", AudioParams.Default.WithVariation(0.025f)); +} + +/// +/// Action event for Last Resort. +/// +public sealed partial class ChangelingLastResortActionEvent : InstantActionEvent; diff --git a/Content.Shared/Changeling/Components/ChangelingSlugComponent.cs b/Content.Shared/Changeling/Components/ChangelingSlugComponent.cs new file mode 100644 index 0000000000..96dfdb3f7b --- /dev/null +++ b/Content.Shared/Changeling/Components/ChangelingSlugComponent.cs @@ -0,0 +1,51 @@ +using Content.Shared.Actions; +using Content.Shared.DoAfter; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.Changeling.Components; + +/// +/// Allows a changeling slug to take over a corpse and become a full changeling again. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ChangelingSlugComponent : Component +{ + /// + /// The action granted for taking over a corpse. + /// + [DataField] + public EntProtoId? Action = "ActionChangelingTakeOverCorpse"; + + /// + /// The action entity associated with taking over a corpse. + /// + [DataField, AutoNetworkedField] + public EntityUid? ActionEntity; + + /// + /// How long it takes to take over a corpse. + /// + [DataField] + public TimeSpan TakeOverDuration = TimeSpan.FromSeconds(5); + + /// + /// The sound to play when starting the takeover. + /// + [DataField] + public SoundSpecifier? Sound = new SoundPathSpecifier("/Audio/Effects/Fluids/splat.ogg"); +} + +/// +/// Action event for taking over a corpse. +/// +[ByRefEvent] +public sealed partial class ChangelingTakeOverCorpseActionEvent : EntityTargetActionEvent; + +/// +/// DoAfter event for the takeover process. +/// +[Serializable, NetSerializable] +public sealed partial class ChangelingTakeOverCorpseDoAfterEvent : SimpleDoAfterEvent; diff --git a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs index cb78adc77f..4124c524a8 100644 --- a/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs +++ b/Content.Shared/Changeling/Systems/ChangelingTransformSystem.cs @@ -195,6 +195,7 @@ public sealed partial class ChangelingTransformSystem : EntitySystem _adminLogger.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(ent.Owner):player} successfully transformed into \"{Name(targetIdentity)}\""); _metaData.SetEntityName(ent, Name(targetIdentity), raiseEvents: false); // Don't raise events because we don't want to rename the ID card. + _metaData.SetEntityDescription(ent, Description(targetIdentity)); _identity.QueueIdentityUpdate(ent); // We have to manually refresh the identity because we did not raise events. Dirty(ent); diff --git a/Content.Shared/Changeling/Systems/SharedChangelingLastResortSystem.cs b/Content.Shared/Changeling/Systems/SharedChangelingLastResortSystem.cs new file mode 100644 index 0000000000..acfce30cf8 --- /dev/null +++ b/Content.Shared/Changeling/Systems/SharedChangelingLastResortSystem.cs @@ -0,0 +1,49 @@ +using Content.Shared.Mind; +using Content.Shared.Actions; +using Content.Shared.Changeling.Components; +using Content.Shared.Gibbing; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Network; + +namespace Content.Shared.Changeling.Systems; + +public abstract partial class SharedChangelingLastResortSystem : EntitySystem +{ + [Dependency] private INetManager _net = default!; + [Dependency] private GibbingSystem _gibbing = default!; + [Dependency] private SharedMindSystem _mind = default!; + [Dependency] private SharedActionsSystem _actions = default!; + [Dependency] protected SharedAudioSystem Audio = default!; + + [SubscribeLocalEvent] + private void OnTakeOverMapInit(Entity ent, ref MapInitEvent args) + { + _actions.AddAction(ent, ref ent.Comp.ActionEntity, ent.Comp.Action); + } + + [SubscribeLocalEvent] + private void OnTakeOverShutdown(Entity ent, ref ComponentShutdown args) + { + if (ent.Comp.ActionEntity != null) + _actions.RemoveAction(ent.Owner, ent.Comp.ActionEntity); + } + + [SubscribeLocalEvent] + private void OnLastResortAction(Entity ent, + ref ChangelingLastResortActionEvent args) + { + if (args.Handled || !_mind.TryGetMind(args.Performer, out var mindId, out var mind)) + return; + + args.Handled = true; + + Audio.PlayPredicted(ent.Comp.Sound, args.Performer, args.Performer); + + if (!_net.IsServer) + return; // Transfer Mind is unpredictable. + + var slug = PredictedSpawnAtPosition(ent.Comp.SlugPrototype, Transform(args.Performer).Coordinates); + _mind.TransferTo(mindId, slug, mind: mind); + _gibbing.Gib(args.Performer); + } +} diff --git a/Resources/Locale/en-US/changeling/changeling.ftl b/Resources/Locale/en-US/changeling/changeling.ftl index 52daca4783..02636a33e5 100644 --- a/Resources/Locale/en-US/changeling/changeling.ftl +++ b/Resources/Locale/en-US/changeling/changeling.ftl @@ -42,6 +42,12 @@ changeling-flesh-clothing-alert-desc = Whether clothing transformation is enable changeling-biodegrade-used-popup = {CAPITALIZE(THE($user))} vomits acid over {POSS-ADJ($user)} {$restraint}! changeling-biodegrade-used-popup-self = We vomit acid over our {$restraint}! +# last resort +changeling-takeover-not-dead = This body yet lives! We cannot infest it alive. +changeling-takeover-is-changeling = This body rejects our attempts to take over! +changeling-takeover-start-others = {CAPITALIZE(THE($user))} begins to crawl inside the corpse! +changeling-takeover-success-self = We have successfully infested the body and assumed its form. + # stings changeling-sting-success = We silently sting {THE($target)}! diff --git a/Resources/Locale/en-US/store/changeling-catalog.ftl b/Resources/Locale/en-US/store/changeling-catalog.ftl index d42415b1fd..25133ba334 100644 --- a/Resources/Locale/en-US/store/changeling-catalog.ftl +++ b/Resources/Locale/en-US/store/changeling-catalog.ftl @@ -24,3 +24,8 @@ changeling-catalog-lethal-sting-desc = Causes very slow damage to the target. Us changeling-catalog-hallucinogenic-sting-name = Hallucinogenic Sting changeling-catalog-hallucinogenic-sting-desc = Causes the target to get high and have difficulty speaking. Causes confusion among the crew. Can be used on self. + +changeling-catalog-last-resort-name = Last Resort +changeling-catalog-last-resort-desc = Gib your current body and escape as a weak head slug. If you infest a humanoid corpse, you can return to a full changeling body. + + Warning! This resets your progress as a changeling, removing your store purchases and absorbed identities! diff --git a/Resources/Prototypes/Actions/changeling.yml b/Resources/Prototypes/Actions/changeling.yml index 0ebb7bdbed..b68dc9fc36 100644 --- a/Resources/Prototypes/Actions/changeling.yml +++ b/Resources/Prototypes/Actions/changeling.yml @@ -202,3 +202,39 @@ - !type:AddReagentToBloodstream reagent: Cryptobiolin quantity: 10 # About 20s of your speech being scrambled + +- type: entity + parent: BaseAction + id: ActionChangelingLastResort + name: "[color=red]Last Resort[/color]" + description: We sacrifice our current body in a moment of need, violently expanding to break through obstacles and reforming as a headslug. + components: + - type: ConfirmableAction + popup: suicide-action-popup + - type: Action + raiseOnAction: true + checkCanInteract: false # You can transform if you are killed + checkConsciousness: false + itemIconStyle: BigAction + icon: + sprite: Interface/Actions/changeling2.rsi + state: last_resort + - type: InstantAction + event: !type:ChangelingLastResortActionEvent + - type: ChangelingLastResortAbility + +- type: entity + parent: BaseAction + id: ActionChangelingTakeOverCorpse + name: Take Over Corpse + description: Infest a corpse to return to your true form. + components: + - type: Action + useDelay: 10 + itemIconStyle: BigAction + icon: + sprite: Interface/Actions/changeling2.rsi + state: take_over_corpse + - type: EntityTargetAction + event: !type:ChangelingTakeOverCorpseActionEvent + - type: TargetAction diff --git a/Resources/Prototypes/Catalog/changeling_catalog.yml b/Resources/Prototypes/Catalog/changeling_catalog.yml index 1477569e8b..c83f07fb36 100644 --- a/Resources/Prototypes/Catalog/changeling_catalog.yml +++ b/Resources/Prototypes/Catalog/changeling_catalog.yml @@ -34,7 +34,7 @@ description: changeling-catalog-voice-mimic-desc icon: { sprite: /Textures/Interface/Actions/changeling2.rsi, state: mimicry} cost: - ChangelingDNA: 20 + ChangelingDNA: 10 categories: - ChangelingStoreUtility conditions: @@ -49,7 +49,7 @@ icon: { sprite: /Textures/Interface/Actions/changeling2.rsi, state: biodegrade } applyToMob: true cost: - ChangelingDNA: 20 + ChangelingDNA: 10 categories: - ChangelingStoreUtility conditions: @@ -130,3 +130,17 @@ - !type:ListingLimitedStockCondition stock: 1 productAction: ActionChangelingStingHallucinogenic + +- type: listing + id: ChangelingLastResort + name: changeling-catalog-last-resort-name + description: changeling-catalog-last-resort-desc + applyToMob: true + cost: + ChangelingDNA: 20 + categories: + - ChangelingStoreUtility + conditions: + - !type:ListingLimitedStockCondition + stock: 1 + productAction: ActionChangelingLastResort diff --git a/Resources/Prototypes/Entities/Mobs/Player/changeling.yml b/Resources/Prototypes/Entities/Mobs/Player/changeling.yml index 8ece41546e..ffa7f94b8c 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/changeling.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/changeling.yml @@ -14,6 +14,53 @@ balance: ChangelingDNA: 60 +- type: entity + parent: [ SimpleSpaceMobBase, MobNightVision ] + id: MobHeadSlug + name: headslug + description: A small, slug-like creature with a large, gaping maw. It's covered in a thick, slimy mucus. + components: + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.2 + density: 15 + mask: + - SmallMobMask + layer: + - SmallMobLayer + - type: Sprite + drawdepth: SmallMobs + layers: + - map: ["enum.DamageStateVisualLayers.Base"] + state: slug + sprite: Mobs/Animals/headslug.rsi + - type: DamageStateVisuals + states: + Alive: + Base: slug + Dead: + Base: slug_dead + - type: MobThresholds + thresholds: + 0: Alive + 50: Dead + - type: MovementSpeedModifier # Slightly faster than regular mobs + baseWalkSpeed: 3 + baseSprintSpeed: 5 + - type: MeleeWeapon + altDisarm: false + damage: + types: + Blunt: 5 + Structural: 15 # to destroy lockers in which bodies could be hidden + - type: ChangelingSlug + - type: Tag # Removes DoorBumpOpener + tags: + [] + - type: entity # dummy prototype for the store listing id: ChangelingFleshClothingAbilityStoreDummy categories: [ HideSpawnMenu ] diff --git a/Resources/Prototypes/GameRules/roundstart.yml b/Resources/Prototypes/GameRules/roundstart.yml index ef4bb40b76..6320d33417 100644 --- a/Resources/Prototypes/GameRules/roundstart.yml +++ b/Resources/Prototypes/GameRules/roundstart.yml @@ -209,6 +209,7 @@ id: Changeling parent: [BaseGameRule, BaseRoundstartAntagRule] components: + - type: ChangelingRule - type: GameRule minPlayers: 25 - type: AntagSelection diff --git a/Resources/Prototypes/Roles/Antags/changeling.yml b/Resources/Prototypes/Roles/Antags/changeling.yml index 76df0c6069..c16e5be244 100644 --- a/Resources/Prototypes/Roles/Antags/changeling.yml +++ b/Resources/Prototypes/Roles/Antags/changeling.yml @@ -13,7 +13,7 @@ text: changeling-role-greeting color: Red sound: /Audio/Ambience/Antag/changeling_start.ogg - components: + components: # When updating components, remember to copy the changes over to MobLing !!!!! - type: ChangelingDevour - type: ChangelingIdentity - type: ChangelingTransform diff --git a/Resources/Textures/Interface/Actions/changeling2.rsi/last_resort.png b/Resources/Textures/Interface/Actions/changeling2.rsi/last_resort.png new file mode 100644 index 0000000000..5df2d4b692 Binary files /dev/null and b/Resources/Textures/Interface/Actions/changeling2.rsi/last_resort.png differ diff --git a/Resources/Textures/Interface/Actions/changeling2.rsi/meta.json b/Resources/Textures/Interface/Actions/changeling2.rsi/meta.json index c8544d759c..691fc0fd9e 100644 --- a/Resources/Textures/Interface/Actions/changeling2.rsi/meta.json +++ b/Resources/Textures/Interface/Actions/changeling2.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Made by ketufaispikinut from a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (scientist suit), a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (hydro suit) and the changeling ability border/background sprites created by TiniestShark. Biodegrade, Mimicry and Extract Dna On/Off, Blind, Mute, Neurodepressant, Cryogenic, Lethal and Hallucination sting by ketufaispikinut, based on TiniestShark's textures. Shop icon by ketufaispikinut, based on TiniestShark's textures.", + "copyright": "Made by ketufaispikinut from a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (scientist suit), a sprite taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c838ba21dae97db345e0113f99596decd1d66039 (hydro suit) and the changeling ability border/background sprites created by TiniestShark. Biodegrade, Mimicry and Extract Dna On/Off, Blind, Mute, Neurodepressant, Cryogenic, Lethal and Hallucination sting by ketufaispikinut, based on TiniestShark's textures. Shop icon by ketufaispikinut, based on TiniestShark's textures. last_resort taken from https://github.com/tgstation/tgstation/blob/5c90546a27a1e5ea59b9f127ae274d7341acc70b/icons/mob/actions/actions_changeling.dmi and modified by Pok27 (GitHub). take_over_corpse by Pok27 (GitHub).", "size": { "x": 32, "y": 32 @@ -45,6 +45,12 @@ }, { "name": "shop" + }, + { + "name": "last_resort" + }, + { + "name": "take_over_corpse" } ] } diff --git a/Resources/Textures/Interface/Actions/changeling2.rsi/take_over_corpse.png b/Resources/Textures/Interface/Actions/changeling2.rsi/take_over_corpse.png new file mode 100644 index 0000000000..82027a9c0e Binary files /dev/null and b/Resources/Textures/Interface/Actions/changeling2.rsi/take_over_corpse.png differ diff --git a/Resources/Textures/Mobs/Animals/headslug.rsi/icon.png b/Resources/Textures/Mobs/Animals/headslug.rsi/icon.png new file mode 100644 index 0000000000..6620e232e4 Binary files /dev/null and b/Resources/Textures/Mobs/Animals/headslug.rsi/icon.png differ diff --git a/Resources/Textures/Mobs/Animals/headslug.rsi/meta.json b/Resources/Textures/Mobs/Animals/headslug.rsi/meta.json new file mode 100644 index 0000000000..64666f2c04 --- /dev/null +++ b/Resources/Textures/Mobs/Animals/headslug.rsi/meta.json @@ -0,0 +1,21 @@ +{ + "version": 1, + "license": "CC-BY-4.0", + "copyright": "Made by ketufaispikinut", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "slug", + "directions": 4 + }, + { + "name": "slug_dead" + }, + { + "name": "icon" + } + ] +} diff --git a/Resources/Textures/Mobs/Animals/headslug.rsi/slug.png b/Resources/Textures/Mobs/Animals/headslug.rsi/slug.png new file mode 100644 index 0000000000..b2f3472328 Binary files /dev/null and b/Resources/Textures/Mobs/Animals/headslug.rsi/slug.png differ diff --git a/Resources/Textures/Mobs/Animals/headslug.rsi/slug_dead.png b/Resources/Textures/Mobs/Animals/headslug.rsi/slug_dead.png new file mode 100644 index 0000000000..55e6a49a9e Binary files /dev/null and b/Resources/Textures/Mobs/Animals/headslug.rsi/slug_dead.png differ