From 951b2a4e3d57738f3c0c2c0c369be8ceef07b383 Mon Sep 17 00:00:00 2001 From: Pok <113675512+Pok27@users.noreply.github.com> Date: Wed, 8 Jul 2026 03:53:42 +0300 Subject: [PATCH] Changeling last resort (#43820) * Changeling-LastResort-Stasis * Update changeling_catalog.yml * fix * Merge fix * fix * Update ChangelingLastResortSystem.cs * ConfirmableAction * fix 2 * Merge * Update ChangelingLastResortSystem.cs * review * sprite * Update icon.png * fix * NightVision * review * Interaction * review review * misc cleaning - Moved some components around - Lowered price of biodegrade and voice mimic - Updated store locale to make the warning more clear - Unpredicted "Take Over Body" - Made cloning copy over entity descriptions * abstract * fix * stuff * move to API * fix MobLing * review * docs --------- Co-authored-by: ScarKy0 --- .../Systems/ChangelingLastResortSystem.cs | 5 + .../AntagSelectionSystem.API.Assignment.cs | 22 ++++ Content.Server/Antag/AntagSelectionSystem.cs | 3 +- .../Systems/ChangelingLastResortSystem.cs | 109 ++++++++++++++++++ Content.Server/Cloning/CloningSystem.cs | 1 + .../ChangelingLastResortAbilityComponent.cs | 30 +++++ .../Components/ChangelingSlugComponent.cs | 51 ++++++++ .../Systems/ChangelingTransformSystem.cs | 1 + .../SharedChangelingLastResortSystem.cs | 49 ++++++++ .../Locale/en-US/changeling/changeling.ftl | 6 + .../Locale/en-US/store/changeling-catalog.ftl | 5 + Resources/Prototypes/Actions/changeling.yml | 36 ++++++ .../Prototypes/Catalog/changeling_catalog.yml | 18 ++- .../Entities/Mobs/Player/changeling.yml | 47 ++++++++ Resources/Prototypes/GameRules/roundstart.yml | 1 + .../Prototypes/Roles/Antags/changeling.yml | 2 +- .../Actions/changeling2.rsi/last_resort.png | Bin 0 -> 1536 bytes .../Actions/changeling2.rsi/meta.json | 8 +- .../changeling2.rsi/take_over_corpse.png | Bin 0 -> 1278 bytes .../Mobs/Animals/headslug.rsi/icon.png | Bin 0 -> 445 bytes .../Mobs/Animals/headslug.rsi/meta.json | 21 ++++ .../Mobs/Animals/headslug.rsi/slug.png | Bin 0 -> 1185 bytes .../Mobs/Animals/headslug.rsi/slug_dead.png | Bin 0 -> 530 bytes 23 files changed, 410 insertions(+), 5 deletions(-) create mode 100644 Content.Client/Changeling/Systems/ChangelingLastResortSystem.cs create mode 100644 Content.Server/Changeling/Systems/ChangelingLastResortSystem.cs create mode 100644 Content.Shared/Changeling/Components/ChangelingLastResortAbilityComponent.cs create mode 100644 Content.Shared/Changeling/Components/ChangelingSlugComponent.cs create mode 100644 Content.Shared/Changeling/Systems/SharedChangelingLastResortSystem.cs create mode 100644 Resources/Textures/Interface/Actions/changeling2.rsi/last_resort.png create mode 100644 Resources/Textures/Interface/Actions/changeling2.rsi/take_over_corpse.png create mode 100644 Resources/Textures/Mobs/Animals/headslug.rsi/icon.png create mode 100644 Resources/Textures/Mobs/Animals/headslug.rsi/meta.json create mode 100644 Resources/Textures/Mobs/Animals/headslug.rsi/slug.png create mode 100644 Resources/Textures/Mobs/Animals/headslug.rsi/slug_dead.png 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 0000000000000000000000000000000000000000..5df2d4b692c838d821da2004341cf04e67c479ae GIT binary patch literal 1536 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 nGvj!KQQF00000NkvXXu0mjf?9IVQ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..b2f347232851e11a6dcaf371ad5ad93de8aa74df GIT binary patch literal 1185 zcmV;S1YY}zP)Px(TS-JgRCt{2n%_%XXBfvn85(Z_kzl$+%aLAmvS0=S2P?sLuv$Yv5vmLqupqq= z*1tgPMIjfxD+K1~d@7iF5bX`AT|M1s$3^rlXU@3zra*v0Sq9+c)O1}c!KiPfT763upo!I7LW7r=1oQ?CRm6(BDuPbySR(<=$Nq2uTC#5)^tC3wYEmP=`Opx z68Ws6-~H@t-158?1sUxPscnB8uu&?J+TNkFrTHYN^>sdNYa_M2!~2rNrlRQA~=OvC9ZaU$no-P0Om4Vx~|7=XC{}gSi7P@Ba(EgtOl-KV=lAB z{^0>{9ZmvH2dV8H3XOjkRIL|3lgk5~(sCXhbG!8lbD1pwU0)ktNemV30aJ=%n^F`T z<;8#h<_-IY2WaO!b~}Z}2Bw!5ZQ606Rhuv_#;8?jY+x?4#qHKBr}VXC)tnJYQq9#s z`7V8T>y~<%kHLS_2>bl%*Eeodt!-%&)b6;M>7_**m;4_9Qzg_}TObe!1OkCTAP@)y z0)apv5d1%=$?NNSdIf5&b)gBgV6=91Ix)}h%0|tCJ4>rw^3d_w=TL5mUsTJ|YjJZh z=03f<<2oOWKP3D7DdDxXn)L_6KEK-4(~WmRpGa)BCav3+TH=sJplkofXE{N zUjCTCT^?Ht#zTF4;B5ua%@{`AO+|skFS=ST0Kj#9iFAzRGh5I2};jW!d56nf&X?xt3;9+dFJ3itZJlY|ME3)6W$@ z5h~sUwF%Ar`9fm@9=jb3rU;MSUNeWt^wOeDF6^JFq3LU}Xk)BA2`mSw?d-$Sl6L^;a=a#mb*CG7KGszIXOIs$<}@J{d-{fbhnn@LE^00000NkvXXu0mjfa0@vV literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..55e6a49a9e4e1026f840bc5426706e3bec092cae GIT binary patch literal 530 zcmV+t0`2{YP)Px$%t=H+R9J=WlTAw-Q51%ssZk3FObGEK6{hz2H`0LtvoRuNp#Px1pwO*9 zU_kH(WY>0Cskm@6kX=E8LJKZt8mF-`WONjXmfMAGh~ns^Ur@Nu>R!&_zVA8b9*{^R z5{X12`Co7?Tey}jemCbPxvb((pF3A)5HH@pQ+_4qHXaP)HVSgS$rEA$@VQrNt z^oc^BW)Nh4GqZsx^eI&|wwG!&g8)Z21c0L(q8S8iFV!ejv|;K=H!wI$*RsW9Z5n`a z)5JUJF_OeA=M@~?5X*UmktD`D=`n7aY^-l#s)fG6`?z&Zu4Rjv^HbUn^UR!|vUzYw zwOjtG$ziDX|~(_+`2W-hyAO2$M*r4e>9oNKxUJE&u=k literal 0 HcmV?d00001