From 79e1110073bb0e71af59668f89edf56eb643cfe7 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Mon, 10 Jun 2024 16:00:21 -0400 Subject: [PATCH] Oneirophage complete --- Content.Server/Arachne/ArachneSystem.cs | 95 +------ Content.Server/Arachne/CocoonComponent.cs | 2 +- .../Vampire/BloodSuckedComponent.cs | 2 +- .../Vampire/BloodSuckerComponent.cs | 6 +- Content.Server/Vampire/BloodSuckerSystem.cs | 42 ++- .../BloodSuckerGlandInjectorComponent.cs | 2 +- Content.Shared/Arachne/ArachneComponent.cs | 14 +- Content.Shared/Arachne/Events.cs | 18 -- Content.Shared/Arachne/WebComponent.cs | 5 +- Resources/Locale/en-US/abilities/arachne.ftl | 13 + .../Locale/en-US/abilities/bloodsucker.ftl | 19 ++ Resources/Prototypes/GameRules/events.yml | 16 ++ .../Entities/Body/Mechanisms/vampiric.yml | 22 ++ .../Entities/Body/Parts/spider.yml | 24 +- .../Entities/Body/Prototypes/arachne.yml | 63 +++++ .../Body/Prototypes/vampiricanimal.yml | 43 +++ .../Entities/Markers/Spawners/ghost_roles.yml | 40 +-- .../Entities/Mobs/NPCs/mutants.yml | 259 +++++++++--------- .../Entities/Structures/Webbing/webs.yml | 93 +++++++ Resources/Prototypes/tags.yml | 6 + .../Structures/cocoon.rsi/cocoon1.png | Bin 0 -> 669 bytes .../Structures/cocoon.rsi/cocoon2.png | Bin 0 -> 636 bytes .../Structures/cocoon.rsi/cocoon3.png | Bin 0 -> 578 bytes .../Structures/cocoon.rsi/cocoon_large1.png | Bin 0 -> 762 bytes .../Structures/cocoon.rsi/cocoon_large2.png | Bin 0 -> 683 bytes .../Structures/cocoon.rsi/cocoon_large3.png | Bin 0 -> 876 bytes .../Structures/cocoon.rsi/meta.json | 35 +++ 27 files changed, 507 insertions(+), 312 deletions(-) create mode 100644 Resources/Locale/en-US/abilities/arachne.ftl create mode 100644 Resources/Locale/en-US/abilities/bloodsucker.ftl create mode 100644 Resources/Prototypes/Nyanotrasen/Entities/Body/Mechanisms/vampiric.yml create mode 100644 Resources/Prototypes/Nyanotrasen/Entities/Body/Prototypes/arachne.yml create mode 100644 Resources/Prototypes/Nyanotrasen/Entities/Body/Prototypes/vampiricanimal.yml create mode 100644 Resources/Prototypes/Nyanotrasen/Entities/Structures/Webbing/webs.yml create mode 100644 Resources/Textures/Nyanotrasen/Structures/cocoon.rsi/cocoon1.png create mode 100644 Resources/Textures/Nyanotrasen/Structures/cocoon.rsi/cocoon2.png create mode 100644 Resources/Textures/Nyanotrasen/Structures/cocoon.rsi/cocoon3.png create mode 100644 Resources/Textures/Nyanotrasen/Structures/cocoon.rsi/cocoon_large1.png create mode 100644 Resources/Textures/Nyanotrasen/Structures/cocoon.rsi/cocoon_large2.png create mode 100644 Resources/Textures/Nyanotrasen/Structures/cocoon.rsi/cocoon_large3.png create mode 100644 Resources/Textures/Nyanotrasen/Structures/cocoon.rsi/meta.json diff --git a/Content.Server/Arachne/ArachneSystem.cs b/Content.Server/Arachne/ArachneSystem.cs index cf79a4dd8bf..338ecc21a1a 100644 --- a/Content.Server/Arachne/ArachneSystem.cs +++ b/Content.Server/Arachne/ArachneSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Arachne; using Content.Shared.Actions; +using Content.Shared.Actions.Events; using Content.Shared.Coordinates.Helpers; using Content.Shared.IdentityManagement; using Content.Shared.Verbs; @@ -15,32 +16,26 @@ using Content.Shared.Inventory; using Content.Shared.Administration.Logs; using Content.Shared.Database; -using Content.Shared.Examine; using Content.Shared.Humanoid; using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.EntitySystems; using Content.Server.Buckle.Systems; -using Content.Server.Nutrition.EntitySystems; -using Content.Server.Nutrition.Components; using Content.Server.Popups; using Content.Server.DoAfter; using Content.Server.Body.Components; using Content.Server.Vampiric; using Content.Server.Speech.Components; using Robust.Shared.Prototypes; -using Robust.Shared.Player; using Robust.Shared.Physics.Components; using Robust.Shared.Containers; using Robust.Shared.Map; using Robust.Shared.Utility; using Robust.Server.Console; -using static Content.Shared.Examine.ExamineSystemShared; namespace Content.Server.Arachne { public sealed class ArachneSystem : EntitySystem { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly HungerSystem _hungerSystem = default!; [Dependency] private readonly ThirstSystem _thirstSystem = default!; @@ -63,25 +58,15 @@ public sealed class ArachneSystem : EntitySystem public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnInit); SubscribeLocalEvent>(AddCocoonVerb); SubscribeLocalEvent(OnCocEntInserted); SubscribeLocalEvent(OnCocEntRemoved); SubscribeLocalEvent(OnDamageChanged); SubscribeLocalEvent>(AddSuccVerb); - - SubscribeLocalEvent(OnSpinWeb); - - SubscribeLocalEvent(OnWebDoAfter); SubscribeLocalEvent(OnCocoonDoAfter); } - private void OnInit(EntityUid uid, ArachneComponent component, ComponentInit args) - { - _actions.AddAction(uid, ref component.WebActionEntity, component.WebActionId); - } - private void AddCocoonVerb(EntityUid uid, ArachneComponent component, GetVerbsEvent args) { if (!args.CanAccess || !args.CanInteract) @@ -198,63 +183,6 @@ private void OnEntRemoved(EntityUid uid, WebComponent web, EntRemovedFromContain _buckleSystem.StrapSetEnabled(uid, false, strap); } - private void OnSpinWeb(SpinWebActionEvent args) - { - if (!TryComp(args.Performer, out var arachne)) - return; - - if (_containerSystem.IsEntityInContainer(args.Performer)) - return; - - TryComp(args.Performer, out var hunger); - TryComp(args.Performer, out var thirst); - - if (hunger != null && thirst != null) - { - if (hunger.CurrentThreshold <= Shared.Nutrition.Components.HungerThreshold.Peckish) - { - _popupSystem.PopupEntity(Loc.GetString("spin-web-action-hungry"), args.Performer, args.Performer, Shared.Popups.PopupType.MediumCaution); - return; - } - if (thirst.CurrentThirstThreshold <= ThirstThreshold.Thirsty) - { - _popupSystem.PopupEntity(Loc.GetString("spin-web-action-thirsty"), args.Performer, args.Performer, Shared.Popups.PopupType.MediumCaution); - return; - } - } - - var coords = args.Target; - if (!_mapManager.TryGetGrid(coords.GetGridUid(EntityManager), out var grid)) - { - _popupSystem.PopupEntity(Loc.GetString("action-name-spin-web-space"), args.Performer, args.Performer, Shared.Popups.PopupType.MediumCaution); - return; - } - - foreach (var entity in coords.GetEntitiesInTile()) - { - PhysicsComponent? physics = null; // We use this to check if it's impassable - if ((HasComp(entity)) || // Is there already a web there? - ((Resolve(entity, ref physics, false) && (physics.CollisionLayer & (int) CollisionGroup.Impassable) != 0) // Is it impassable? - && !(TryComp(entity, out var door) && door.State != DoorState.Closed))) // Is it a door that's open and so not actually impassable? - { - _popupSystem.PopupEntity(Loc.GetString("action-name-spin-web-blocked"), args.Performer, args.Performer, Shared.Popups.PopupType.MediumCaution); - return; - } - } - - _popupSystem.PopupEntity(Loc.GetString("spin-web-start-third-person", ("spider", Identity.Entity(args.Performer, EntityManager))), args.Performer, - Shared.Popups.PopupType.MediumCaution); - _popupSystem.PopupEntity(Loc.GetString("spin-web-start-second-person"), args.Performer, args.Performer, Shared.Popups.PopupType.Medium); - - var ev = new ArachneWebDoAfterEvent(coords); - var doAfterArgs = new DoAfterArgs(EntityManager, args.Performer, arachne.WebDelay, ev, args.Performer) - { - BreakOnUserMove = true, - }; - - _doAfter.TryStartDoAfter(doAfterArgs); - } - private void StartCocooning(EntityUid uid, ArachneComponent component, EntityUid target) { _popupSystem.PopupEntity(Loc.GetString("cocoon-start-third-person", ("target", Identity.Entity(target, EntityManager)), ("spider", Identity.Entity(uid, EntityManager))), uid, @@ -280,22 +208,6 @@ private void StartCocooning(EntityUid uid, ArachneComponent component, EntityUid _doAfter.TryStartDoAfter(args); } - private void OnWebDoAfter(EntityUid uid, ArachneComponent component, ArachneWebDoAfterEvent args) - { - if (args.Handled || args.Cancelled) - return; - - _hungerSystem.ModifyHunger(uid, -8); - if (TryComp(uid, out var thirst)) - _thirstSystem.ModifyThirst(uid, thirst, -20); - - Spawn("ArachneWeb", args.Coords.SnapToGrid()); - _popupSystem.PopupEntity(Loc.GetString("spun-web-third-person", ("spider", Identity.Entity(uid, EntityManager))), uid, - Shared.Popups.PopupType.MediumCaution); - _popupSystem.PopupEntity(Loc.GetString("spun-web-second-person"), uid, uid, Shared.Popups.PopupType.Medium); - args.Handled = true; - } - private void OnCocoonDoAfter(EntityUid uid, ArachneComponent component, ArachneCocoonDoAfterEvent args) { if (args.Handled || args.Cancelled || args.Args.Target == null) @@ -319,9 +231,6 @@ private void OnCocoonDoAfter(EntityUid uid, ArachneComponent component, ArachneC var scale = Math.Clamp(1 / (35 / physics.FixturesMass), 0.35, 2.5); _host.ExecuteCommand(null, "scale " + cocoon + " " + scale); } - - _inventorySystem.TryUnequip(args.Args.Target.Value, "ears", true, true); - _itemSlots.SetLock(cocoon, BodySlot, false, slots); _itemSlots.TryInsert(cocoon, BodySlot, args.Args.Target.Value, args.Args.User); _itemSlots.SetLock(cocoon, BodySlot, true, slots); @@ -332,6 +241,4 @@ private void OnCocoonDoAfter(EntityUid uid, ArachneComponent component, ArachneC args.Handled = true; } } - - public sealed partial class SpinWebActionEvent : WorldTargetActionEvent {} } diff --git a/Content.Server/Arachne/CocoonComponent.cs b/Content.Server/Arachne/CocoonComponent.cs index d7c8aea1296..42ecf27971a 100644 --- a/Content.Server/Arachne/CocoonComponent.cs +++ b/Content.Server/Arachne/CocoonComponent.cs @@ -1,7 +1,7 @@ namespace Content.Server.Arachne { [RegisterComponent] - public sealed class CocoonComponent : Component + public sealed partial class CocoonComponent : Component { public bool WasReplacementAccent = false; diff --git a/Content.Server/Vampire/BloodSuckedComponent.cs b/Content.Server/Vampire/BloodSuckedComponent.cs index 2a0ced3cdfd..d7e402cd98a 100644 --- a/Content.Server/Vampire/BloodSuckedComponent.cs +++ b/Content.Server/Vampire/BloodSuckedComponent.cs @@ -4,6 +4,6 @@ namespace Content.Server.Vampiric /// For entities who have been succed. /// [RegisterComponent] - public sealed class BloodSuckedComponent : Component + public sealed partial class BloodSuckedComponent : Component {} } diff --git a/Content.Server/Vampire/BloodSuckerComponent.cs b/Content.Server/Vampire/BloodSuckerComponent.cs index 53908e2fa60..f5619d1cb49 100644 --- a/Content.Server/Vampire/BloodSuckerComponent.cs +++ b/Content.Server/Vampire/BloodSuckerComponent.cs @@ -1,7 +1,7 @@ namespace Content.Server.Vampiric { [RegisterComponent] - public sealed class BloodSuckerComponent : Component + public sealed partial class BloodSuckerComponent : Component { /// /// How much to succ each time we succ. @@ -12,8 +12,8 @@ public sealed class BloodSuckerComponent : Component /// /// The time (in seconds) that it takes to succ an entity. /// - [DataField("succDelay")] - public long SuccDelay = 4; + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan Delay = TimeSpan.FromSeconds(4); // ***INJECT WHEN SUCC*** diff --git a/Content.Server/Vampire/BloodSuckerSystem.cs b/Content.Server/Vampire/BloodSuckerSystem.cs index d987bbe911a..35f4743e645 100644 --- a/Content.Server/Vampire/BloodSuckerSystem.cs +++ b/Content.Server/Vampire/BloodSuckerSystem.cs @@ -17,6 +17,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Player; using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; using Robust.Shared.Utility; namespace Content.Server.Vampiric @@ -34,6 +35,7 @@ public sealed class BloodSuckerSystem : EntitySystem [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; public override void Initialize() { base.Initialize(); @@ -130,21 +132,14 @@ public void StartSuccDoAfter(EntityUid bloodsucker, EntityUid victim, BloodSucke return; } - if (_bloodstreamSystem.GetBloodLevelPercentage(victim, stream) <= 1) - { - if (HasComp(victim)) - _popups.PopupEntity(Loc.GetString("bloodsucker-fail-no-blood-bloodsucked", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium); - else - _popups.PopupEntity(Loc.GetString("bloodsucker-fail-no-blood", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium); - - return; - } + if (_solutionSystem.PercentFull(stream.Owner) != 0) + _popups.PopupEntity(Loc.GetString("bloodsucker-fail-no-blood", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium); _popups.PopupEntity(Loc.GetString("bloodsucker-doafter-start-victim", ("sucker", bloodsucker)), victim, victim, Shared.Popups.PopupType.LargeCaution); _popups.PopupEntity(Loc.GetString("bloodsucker-doafter-start", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium); var ev = new BloodSuckDoAfterEvent(); - var args = new DoAfterArgs(EntityManager, bloodsucker, new TimeSpan(bloodSuckerComponent.SuccDelay), ev, bloodsucker, target: victim) + var args = new DoAfterArgs(EntityManager, bloodsucker, bloodSuckerComponent.Delay, ev, bloodsucker, target: victim) { BreakOnTargetMove = true, BreakOnUserMove = false, @@ -155,14 +150,14 @@ public void StartSuccDoAfter(EntityUid bloodsucker, EntityUid victim, BloodSucke _doAfter.TryStartDoAfter(args); } - public bool TrySucc(EntityUid bloodsucker, EntityUid victim, BloodSuckerComponent? bloodsuckerComp = null, BloodstreamComponent? bloodstream = null) + public bool TrySucc(EntityUid bloodsucker, EntityUid victim, BloodSuckerComponent? bloodsuckerComp = null) { // Is bloodsucker a bloodsucker? if (!Resolve(bloodsucker, ref bloodsuckerComp)) return false; // Does victim have a bloodstream? - if (!Resolve(victim, ref bloodstream)) + if (!TryComp(victim, out var bloodstream)) return false; // No blood left, yikes. @@ -178,12 +173,8 @@ public bool TrySucc(EntityUid bloodsucker, EntityUid victim, BloodSuckerComponen return false; // Are we too full? - var unitsToDrain = bloodsuckerComp.UnitsToSucc; - - if (_solutionSystem.TryGetDrainableSolution(stomachList) < unitsToDrain) - unitsToDrain = (float) stomachSolution.AvailableVolume; - if (unitsToDrain <= 2) + if (_solutionSystem.PercentFull(bloodsucker) >= 1) { _popups.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough"), bloodsucker, bloodsucker, Shared.Popups.PopupType.MediumCaution); return false; @@ -192,14 +183,16 @@ public bool TrySucc(EntityUid bloodsucker, EntityUid victim, BloodSuckerComponen _adminLogger.Add(Shared.Database.LogType.MeleeHit, Shared.Database.LogImpact.Medium, $"{ToPrettyString(bloodsucker):player} sucked blood from {ToPrettyString(victim):target}"); // All good, succ time. - SoundSystem.Play("/Audio/Items/drink.ogg", Filter.Pvs(bloodsucker), bloodsucker); + _audio.PlayPvs("/Audio/Items/drink.ogg", bloodsucker); _popups.PopupEntity(Loc.GetString("bloodsucker-blood-sucked-victim", ("sucker", bloodsucker)), victim, victim, Shared.Popups.PopupType.LargeCaution); _popups.PopupEntity(Loc.GetString("bloodsucker-blood-sucked", ("target", victim)), bloodsucker, bloodsucker, Shared.Popups.PopupType.Medium); EnsureComp(victim); // Make everything actually ingest. - var temp = _solutionSystem.SplitSolution(victim, bloodstream.BloodSolution, unitsToDrain); - temp.DoEntityReaction(bloodsucker, Shared.Chemistry.Reagent.ReactionMethod.Ingestion); + if (bloodstream.BloodSolution == null) + return false; + + var temp = _solutionSystem.SplitSolution(bloodstream.BloodSolution.Value, bloodsuckerComp.UnitsToSucc); _stomachSystem.TryTransferSolution(stomachList[0].Comp.Owner, temp, stomachList[0].Comp); // Add a little pierce @@ -208,10 +201,11 @@ public bool TrySucc(EntityUid bloodsucker, EntityUid victim, BloodSuckerComponen _damageableSystem.TryChangeDamage(victim, damage, true, true); - if (bloodsuckerComp.InjectWhenSucc && _solutionSystem.TryGetInjectableSolution(victim, out var injectable)) - { - _solutionSystem.TryAddReagent(victim, injectable, bloodsuckerComp.InjectReagent, bloodsuckerComp.UnitsToInject, out var acceptedQuantity); - } + //I'm not porting the nocturine gland, this code is deprecated, and will be reworked at a later date. + //if (bloodsuckerComp.InjectWhenSucc && _solutionSystem.TryGetInjectableSolution(victim, out var injectable)) + //{ + // _solutionSystem.TryAddReagent(victim, injectable, bloodsuckerComp.InjectReagent, bloodsuckerComp.UnitsToInject, out var acceptedQuantity); + //} return true; } diff --git a/Content.Server/Vampire/Injector/BloodSuckerGlandInjectorComponent.cs b/Content.Server/Vampire/Injector/BloodSuckerGlandInjectorComponent.cs index 6e97daf6a84..1a3c9b1588a 100644 --- a/Content.Server/Vampire/Injector/BloodSuckerGlandInjectorComponent.cs +++ b/Content.Server/Vampire/Injector/BloodSuckerGlandInjectorComponent.cs @@ -4,7 +4,7 @@ namespace Content.Server.Vampiric /// /// Item that gives a bloodsucker injection glands (for poison, usually) /// - public sealed class BloodSuckerGlandInjectorComponent : Component + public sealed partial class BloodSuckerGlandInjectorComponent : Component { public bool Used = false; diff --git a/Content.Shared/Arachne/ArachneComponent.cs b/Content.Shared/Arachne/ArachneComponent.cs index 1b19a3e1de6..8e1dd03b200 100644 --- a/Content.Shared/Arachne/ArachneComponent.cs +++ b/Content.Shared/Arachne/ArachneComponent.cs @@ -1,14 +1,13 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Arachne { - [RegisterComponent] + [RegisterComponent, NetworkedComponent] public sealed partial class ArachneComponent : Component { - [DataField("webDelay")] - public float WebDelay = 5f; - [DataField("cocoonDelay")] public float CocoonDelay = 12f; @@ -21,12 +20,5 @@ public sealed partial class ArachneComponent : Component [DataField("webBloodReagent")] public string WebBloodReagent = "Blood"; - - [DataField("webActionId", - customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? WebActionId = "SpinWeb"; - - [DataField("dispelActionEntity")] - public EntityUid? WebActionEntity; } } diff --git a/Content.Shared/Arachne/Events.cs b/Content.Shared/Arachne/Events.cs index 1adc08eda5c..02001286ac6 100644 --- a/Content.Shared/Arachne/Events.cs +++ b/Content.Shared/Arachne/Events.cs @@ -4,24 +4,6 @@ namespace Content.Shared.Arachne { - [Serializable, NetSerializable] - public sealed partial class ArachneWebDoAfterEvent : DoAfterEvent - { - [DataField("coords", required: true)] - public EntityCoordinates Coords = default!; - - private ArachneWebDoAfterEvent() - { - } - - public ArachneWebDoAfterEvent(EntityCoordinates coords) - { - Coords = coords; - } - - public override DoAfterEvent Clone() => this; - } - [Serializable, NetSerializable] public sealed partial class ArachneCocoonDoAfterEvent : SimpleDoAfterEvent { diff --git a/Content.Shared/Arachne/WebComponent.cs b/Content.Shared/Arachne/WebComponent.cs index b35e911bfde..95c03e5a4a2 100644 --- a/Content.Shared/Arachne/WebComponent.cs +++ b/Content.Shared/Arachne/WebComponent.cs @@ -1,6 +1,9 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + namespace Content.Shared.Arachne { - [RegisterComponent] + [RegisterComponent, NetworkedComponent] public sealed partial class WebComponent : Component {} } diff --git a/Resources/Locale/en-US/abilities/arachne.ftl b/Resources/Locale/en-US/abilities/arachne.ftl new file mode 100644 index 00000000000..6f6348d7212 --- /dev/null +++ b/Resources/Locale/en-US/abilities/arachne.ftl @@ -0,0 +1,13 @@ +action-name-spin-web = Spin Web +action-desc-spin-web = Use your spinnerets to make a spider web in the current tile. Makes you hungrier and thirstier. +action-name-spin-web-space = You can't spin a web in space! +action-name-spin-web-blocked = There's no room for a web here. +spin-web-action-hungry = You're too hungry to spin a web! +spin-web-action-thirsty = You're too thirsty to spin a web! +spin-web-start-second-person = You start spinning a web. +spin-web-start-third-person = {CAPITALIZE(THE($spider))} starts spinning a web! +cocoon-start-second-person = You start cocooning {THE($target)}. +cocoon-start-third-person = {CAPITALIZE(THE($spider))} starts cocooning {THE($target)}. +spun-web-second-person = You spin up a web. +spun-web-third-person = {CAPITALIZE(THE($spider))} spins up a web! +cocoon = Cocoon diff --git a/Resources/Locale/en-US/abilities/bloodsucker.ftl b/Resources/Locale/en-US/abilities/bloodsucker.ftl new file mode 100644 index 00000000000..d956eaff84e --- /dev/null +++ b/Resources/Locale/en-US/abilities/bloodsucker.ftl @@ -0,0 +1,19 @@ +action-name-suck-blood = Suck Blood +action-description-suck-blood = Suck the blood of the victim in your hand. + +bloodsucker-fail-helmet = You'd need to remove {THE($helmet)}. +bloodsucker-fail-mask = You'd need to remove your mask! + +bloodsucker-fail-not-blood = { CAPITALIZE(SUBJECT($target)) } doesn't have delicious, nourishing mortal blood. +bloodsucker-fail-no-blood = { CAPITALIZE(SUBJECT($target)) } has no blood in { POSS-ADJ($target) } body. +bloodsucker-fail-no-blood-bloodsucked = { CAPITALIZE(SUBJECT($target)) } has been sucked dry. + +bloodsucker-blood-sucked = You suck some blood from {$target}. +bloodsucker-doafter-start = You try to suck blood from {$target}. + +bloodsucker-doafter-start-victim = {CAPITALIZE(THE($sucker))} is trying to bite your neck! +bloodsucker-blood-sucked-victim = {CAPITALIZE(THE($sucker))} sucks some of your blood! + +bloodsucked-health-examine = [color=red]{ CAPITALIZE(SUBJECT($target)) } { CONJUGATE-HAVE($target) } bite marks on { POSS-ADJ($target) } neck.[/color] + +bloodsucker-glands-throb = The glands behind your fangs feel a bit sore. diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 6812ca14f70..44f10bd9307 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -395,6 +395,22 @@ # - id: MobClownSpider # prob: 0.05 +- type: entity + id: OneirophageSpawn + parent: BaseGameRule + noSpawn: true + components: + - type: StationEvent + id: VentCritters + earliestStart: 15 + minimumPlayers: 15 + weight: 4 + duration: 60 + - type: VentCrittersRule + entries: + - id: MobGiantSpiderVampireAngry + prob: 0.01 + - type: entity id: ZombieOutbreak parent: BaseGameRule diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Body/Mechanisms/vampiric.yml b/Resources/Prototypes/Nyanotrasen/Entities/Body/Mechanisms/vampiric.yml new file mode 100644 index 00000000000..23934b3ebcc --- /dev/null +++ b/Resources/Prototypes/Nyanotrasen/Entities/Body/Mechanisms/vampiric.yml @@ -0,0 +1,22 @@ +- type: entity + id: OrganVampiricHumanoidStomach + parent: OrganHumanStomach + components: + - type: Metabolizer + # mm yummy + maxReagents: 3 + metabolizerTypes: [Vampiric] + groups: + - id: Food + - id: Drink + +- type: entity + id: OrganVampiricStomach + parent: OrganAnimalStomach + components: + - type: Metabolizer + maxReagents: 3 + metabolizerTypes: [Vampiric] + groups: + - id: Food + - id: Drink diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Body/Parts/spider.yml b/Resources/Prototypes/Nyanotrasen/Entities/Body/Parts/spider.yml index a900f7524e7..7e71227dbcb 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Body/Parts/spider.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Body/Parts/spider.yml @@ -24,13 +24,33 @@ - ReagentId: DemonsBlood Quantity: 10 +- type: entity + id: ThoraxSpider + name: "spider thorax" #for arachne, actual spiders should get a cephalothorax that combines with head. + parent: PartSpider + components: + - type: Sprite + sprite: Mobs/Species/Moth/parts.rsi # placeholder sprite + state: "torso_m" + - type: Icon + sprite: Mobs/Species/Moth/parts.rsi + state: "torso_m" + - type: BodyPart #"Other" type + - type: Extractable + juiceSolution: + reagents: + - ReagentId: Fat + Quantity: 10 + - ReagentId: DemonsBlood + Quantity: 20 + - type: entity id: RightLegSpider name: "right spider leg" parent: PartSpider components: - type: Sprite - sprite: Objects/Consumable/Food/meat.rsi + sprite: Objects/Consumable/Food/meat.rsi # placeholder sprite state: spiderleg - type: Icon sprite: Objects/Consumable/Food/meat.rsi @@ -48,7 +68,7 @@ parent: PartSpider components: - type: Sprite - sprite: Objects/Consumable/Food/meat.rsi + sprite: Objects/Consumable/Food/meat.rsi # placeholder sprite state: spiderleg - type: Icon sprite: Objects/Consumable/Food/meat.rsi diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Body/Prototypes/arachne.yml b/Resources/Prototypes/Nyanotrasen/Entities/Body/Prototypes/arachne.yml new file mode 100644 index 00000000000..553391484e2 --- /dev/null +++ b/Resources/Prototypes/Nyanotrasen/Entities/Body/Prototypes/arachne.yml @@ -0,0 +1,63 @@ +- type: body + id: Arachne + name: "arachne" + root: torso + slots: + head: + part: HeadHuman + connections: + - torso + organs: + brain: OrganHumanBrain + eyes: OrganHumanEyes + torso: + part: TorsoHuman + connections: + - left arm + - right arm + - thorax + organs: + heart: OrganHumanHeart + lungs: OrganHumanLungs + stomach: OrganVampiricHumanoidStomach + liver: OrganHumanLiver + kidneys: OrganHumanKidneys + right arm: + part: RightArmHuman + connections: + - right hand + left arm: + part: LeftArmHuman + connections: + - left hand + right hand: + part: RightHandHuman + left hand: + part: LeftHandHuman + thorax: + part: ThoraxSpider + connections: + - left foreleg + - left second leg + - left third leg + - left hind leg + - right foreleg + - right second leg + - right third leg + - right hind leg + left foreleg: + part: LeftLegSpider + left second leg: + part: LeftLegSpider + left third leg: + part: LeftLegSpider + left hind leg: + part: LeftLegSpider + right foreleg: + part: RightLegSpider + right second leg: + part: RightLegSpider + right third leg: + part: RightLegSpider + right hind leg: + part: RightLegSpider diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Body/Prototypes/vampiricanimal.yml b/Resources/Prototypes/Nyanotrasen/Entities/Body/Prototypes/vampiricanimal.yml new file mode 100644 index 00000000000..3f4cdb06de1 --- /dev/null +++ b/Resources/Prototypes/Nyanotrasen/Entities/Body/Prototypes/vampiricanimal.yml @@ -0,0 +1,43 @@ +- type: body + id: VampiricAnimal + name: "vampiric animal" + root: torso + slots: + torso: + part: TorsoAnimal + connections: + - legs + organs: + lungs: OrganAnimalLungs + stomach: OrganVampiricStomach + liver: OrganAnimalLiver + heart: OrganAnimalHeart + kidneys: OrganAnimalKidneys + legs: + part: LegsAnimal + connections: + - feet + feet: + part: FeetAnimal + +- type: body + id: VampiricAnimalLarge + name: "large vampiric animal" + root: torso + slots: + torso: + part: TorsoAnimal + connections: + - legs + organs: + lungs: OrganAnimalLungs + stomach: OrganVampiricHumanoidStomach + liver: OrganAnimalLiver + heart: OrganAnimalHeart + kidneys: OrganAnimalKidneys + legs: + part: LegsAnimal + connections: + - feet + feet: + part: FeetAnimal diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Markers/Spawners/ghost_roles.yml b/Resources/Prototypes/Nyanotrasen/Entities/Markers/Spawners/ghost_roles.yml index 2652a89127e..046a324e6f6 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Markers/Spawners/ghost_roles.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Markers/Spawners/ghost_roles.yml @@ -51,23 +51,23 @@ - state: prisoner # - type: MidRoundAntagSpawnLocation # When MidRoundAntag? -# - type: entity -# id: SpawnPointGhostVampSpider -# name: ghost role spawn point -# suffix: Vampire spider -# parent: MarkerBase -# noSpawn: true -# components: -# - type: GhostRoleMobSpawner -# prototype: MobGiantSpiderVampireAngry -# - type: GhostRole -# makeSentient: true -# name: ghost-role-information-giant-spider-vampire-name -# description: ghost-role-information-giant-spider-vampire-description -# rules: No antagonist restrictions. Just don't talk in emote; you have telepathic chat. -# - type: Sprite -# sprite: Markers/jobs.rsi -# layers: -# - state: green -# - sprite: Mobs/Animals/bat.rsi -# state: bat +- type: entity + id: SpawnPointGhostVampSpider + name: ghost role spawn point + suffix: Vampire spider + parent: MarkerBase + noSpawn: true + components: + - type: GhostRoleMobSpawner + prototype: MobGiantSpiderVampireAngry + - type: GhostRole + makeSentient: true + name: ghost-role-information-giant-spider-vampire-name + description: ghost-role-information-giant-spider-vampire-description + rules: No antagonist restrictions. Just don't talk in emote; you have telepathic chat. + - type: Sprite + sprite: Markers/jobs.rsi + layers: + - state: green + - sprite: Mobs/Animals/bat.rsi + state: bat diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Mobs/NPCs/mutants.yml b/Resources/Prototypes/Nyanotrasen/Entities/Mobs/NPCs/mutants.yml index 5daf2e15e56..462b3254f1e 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Mobs/NPCs/mutants.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Mobs/NPCs/mutants.yml @@ -70,143 +70,130 @@ - type: Produce - type: NoSlip -# - type: entity -# name: oneirophage -# parent: SimpleMobBase -# id: MobGiantSpiderVampire -# description: The 'dream-eater' spider, rumored to be one of the potential genetic sources for arachne. -# components: -# - type: Sprite -# drawdepth: Mobs -# layers: -# - map: ["enum.DamageStateVisualLayers.Base"] -# state: viper -# sprite: Mobs/Animals/spider.rsi -# - type: Physics -# - type: Fixtures -# fixtures: -# fix1: -# shape: -# !type:PhysShapeCircle -# radius: 0.35 -# density: 130 -# mask: -# - SmallMobMask -# layer: -# - SmallMobLayer -# - type: Appearance -# - type: DamageStateVisuals -# states: -# Alive: -# Base: viper -# Critical: -# Base: viper_dead -# Dead: -# Base: viper_dead -# - type: Butcherable -# spawned: -# - id: FoodMeatSpider -# amount: 2 -# - type: CombatMode -# - type: ReplacementAccent -# accent: xeno -# - type: InteractionPopup -# successChance: 0.5 -# interactSuccessString: petting-success-tarantula -# interactFailureString: petting-failure-generic -# - type: Puller -# needsHands: false -# - type: Arachne -# cocoonDelay: 8 -# - type: SolutionContainerManager -# solutions: -# melee: -# reagents: -# - ReagentId: Nocturine -# Quantity: 20 -# - type: MeleeChemicalInjector -# solution: melee -# transferAmount: 3.5 -# - type: SolutionRegeneration -# solution: melee -# generated: -# reagents: -# - ReagentId: Nocturine -# Quantity: 0.15 -# - type: BloodSucker -# unitsToSucc: 35 -# injectWhenSucc: true -# injectReagent: Cryptobiolin -# unitsToInject: 10 -# webRequired: true -# - type: Bloodstream -# bloodReagent: DemonsBlood -# - type: Body -# prototype: VampiricAnimalLarge -# - type: PotentialPsionic -# - type: Psionic -# removable: false -# - type: MetapsionicPower -# - type: MeleeWeapon -# hidden: true -# angle: 0 -# animation: WeaponArcBite -# damage: -# types: -# Piercing: 8 -# - type: AntiPsionicWeapon -# punish: false -# modifiers: -# coefficients: -# Piercing: 2.25 -# - type: Damageable -# damageContainer: HalfSpirit -# damageModifierSet: HalfSpirit -# - type: StatusEffects -# allowed: -# - Stun -# - KnockedDown -# - SlowedDown -# - Stutter -# - SeeingRainbows -# - Electrocution -# - Drunk -# - SlurredSpeech -# - PressureImmunity -# - Muted -# - ForcedSleep -# - TemporaryBlindness -# - Pacified -# - PsionicsDisabled -# - PsionicallyInsulated -# - type: Tag -# tags: -# - Oneirophage -# - type: MovementAlwaysTouching -# - type: PsionicInvisibleContacts -# whitelist: -# tags: -# - ArachneWeb +- type: entity + name: oneirophage + parent: MobGiantSpider + id: MobGiantSpiderVampire + description: The 'dream-eater' spider, rumored to be one of the potential genetic sources for arachne. + components: + - type: Sprite + drawdepth: Mobs + layers: + - map: ["enum.DamageStateVisualLayers.Base", "movement"] + state: viper + sprite: Mobs/Animals/spider.rsi + - type: SpriteMovement + movementLayers: + movement: + state: viper-moving + noMovementLayers: + movement: + state: viper + - type: Appearance + - type: DamageStateVisuals + states: + Alive: + Base: viper + Critical: + Base: viper_dead + Dead: + Base: viper_dead + - type: ReplacementAccent + accent: xeno + - type: InteractionPopup + successChance: 0.5 + interactSuccessString: petting-success-tarantula + interactFailureString: petting-failure-generic + interactSuccessSpawn: EffectHearts + interactSuccessSound: + path: /Audio/Animals/snake_hiss.ogg + - type: Puller + needsHands: false + - type: Arachne + cocoonDelay: 8 + - type: SolutionContainerManager + solutions: + melee: + reagents: + - ReagentId: Nocturine + Quantity: 20 + - type: MeleeChemicalInjector + solution: melee + transferAmount: 3.5 + - type: SolutionRegeneration + solution: melee + generated: + reagents: + - ReagentId: Nocturine + Quantity: 0.15 + - type: BloodSucker + unitsToSucc: 35 + injectWhenSucc: true + injectReagent: Cryptobiolin + unitsToInject: 10 + webRequired: true + - type: Bloodstream + bloodReagent: DemonsBlood + - type: Body + prototype: VampiricAnimalLarge + - type: PotentialPsionic + - type: Psionic + removable: false + - type: MetapsionicPower + - type: AntiPsionicWeapon + punish: false + modifiers: + coefficients: + Piercing: 2.25 + - type: Damageable + damageContainer: HalfSpirit + damageModifierSet: HalfSpirit + - type: StatusEffects + allowed: + - Stun + - KnockedDown + - SlowedDown + - Stutter + - SeeingRainbows + - Electrocution + - Drunk + - SlurredSpeech + - PressureImmunity + - Muted + - ForcedSleep + - TemporaryBlindness + - Pacified + - PsionicsDisabled + - PsionicallyInsulated + - type: Tag + tags: + - Oneirophage + - type: MovementAlwaysTouching + - type: PsionicInvisibleContacts + whitelist: + tags: + - ArachneWeb -# - type: entity -# name: oneirophage -# parent: MobGiantSpiderVampire -# id: MobGiantSpiderVampireAngry -# suffix: Angry -# components: -# - type: NpcFactionMember -# factions: -# - SimpleHostile -# - type: InputMover -# - type: MobMover -# - type: HTN -# rootTask: SimpleHostileCompound -# - type: GhostRole -# makeSentient: true -# name: ghost-role-information-giant-spider-vampire-name -# description: ghost-role-information-giant-spider-vampire-description -# rules: No antagonist restrictions. Just don't talk in emote; you have telepathic chat. -# - type: GhostTakeoverAvailable +- type: entity + name: oneirophage + parent: MobGiantSpiderVampire + id: MobGiantSpiderVampireAngry + suffix: Angry + components: + - type: NpcFactionMember + factions: + - SimpleHostile + - type: InputMover + - type: MobMover + - type: HTN + rootTask: + task: SimpleHostileCompound + - type: GhostRole + makeSentient: true + name: ghost-role-information-giant-spider-vampire-name + description: ghost-role-information-giant-spider-vampire-description + rules: No antagonist restrictions. Just don't talk in emote; you have telepathic chat. + - type: GhostTakeoverAvailable - type: entity parent: SimpleMobBase diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Webbing/webs.yml b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Webbing/webs.yml new file mode 100644 index 00000000000..e483ea5da71 --- /dev/null +++ b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Webbing/webs.yml @@ -0,0 +1,93 @@ +- type: entity + id: CocoonedHumanoid + name: cocooned humanoid + description: Unlucky. + placement: + mode: SnapgridCenter + snap: + - Wall + components: + - type: Sprite + layers: + - sprite: Nyanotrasen/Structures/cocoon.rsi + state: cocoon_large1 + map: [ "enum.DamageStateVisualLayers.Base" ] + - type: RandomSprite + available: + - enum.DamageStateVisualLayers.Base: + cocoon_large1: "" + - enum.DamageStateVisualLayers.Base: #your guess for why randomsprite requires an arbitrary layer is as good as mine friend + cocoon_large2: "" + - enum.DamageStateVisualLayers.Base: + cocoon_large3: "" + - type: Cocoon + - type: Clickable + - type: InteractionOutline + - type: Transform + noRot: true + - type: Damageable + damageModifierSet: Web + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 40 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeAabb + bounds: "-0.25,-0.4,0.25,0.1" + density: 20 + mask: + - SmallMobMask + layer: + - SmallMobLayer + - type: Physics + bodyType: Dynamic + - type: Pullable + - type: AntiRottingContainer + - type: ItemSlots + slots: + body_slot: + name: Body + locked: true + ejectOnBreak: true + - type: Butcherable + butcheringType: Knife + butcherDelay: 12 + spawned: + - id: MaterialCloth1 + amount: 1 + prob: 0.5 #This doesn't cost hunger so should at least make it not worth it time-wise + - type: Appearance + - type: ContainerContainer + containers: + body_slot: !type:ContainerSlot + +- type: entity + id: CocoonSmall + parent: CocoonedHumanoid + name: cocoon + description: What could be inside...? + placement: + mode: SnapgridCenter + snap: + - Wall + components: + - type: Sprite + layers: + - sprite: Nyanotrasen/Structures/cocoon.rsi + state: cocoon1 + map: [ "enum.DamageStateVisualLayers.Base" ] + - type: RandomSprite + available: + - enum.DamageStateVisualLayers.Base: + cocoon1: "" + - enum.DamageStateVisualLayers.Base: #your guess for why randomsprite requires an arbitrary layer is as good as mine friend + cocoon2: "" + - enum.DamageStateVisualLayers.Base: + cocoon3: "" diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index c6a0ab3f8fd..1bf002d254e 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -16,6 +16,9 @@ - type: Tag id: AppraisalTool +- type: Tag + id: ArachneWeb + - type: Tag id: ArtifactFragment @@ -889,6 +892,9 @@ - type: Tag id: Ointment +- type: Tag + id: Oneirophage + - type: Tag id: Ore diff --git a/Resources/Textures/Nyanotrasen/Structures/cocoon.rsi/cocoon1.png b/Resources/Textures/Nyanotrasen/Structures/cocoon.rsi/cocoon1.png new file mode 100644 index 0000000000000000000000000000000000000000..27741fdf314bc38be7153151d603479000af1891 GIT binary patch literal 669 zcmV;O0%HA%P)$5Dx(m zFBXfsFs^ZhFvIKhs_Q@?a2Eu|Znw+%d={V2CxgL2(&@BRt5s77QvgGJv)QP)!{Lxf zBqH5zS3;qX0suChPIdoDARG?Ma5&VIZq5}t7z~=4e3$?qrcx<&lF#R5GMOk0#n$Wf zbgvNx%x1I6<#LhTZYQJB=)MNvY>Qb6p90{%P$;MnB8HP#EGF%CTX=>j_YedE$8-R| z2pY9PD*b+6(|S1pqs>SHF&0TClhWyQ6o&J|;UM$*{N4f71zSRDY#+4%AZuXAG5}6) zJ*{FSF&>X~3xL8MAVy2#m^3I#?&oqjg$5#%$!M*-lmLq%x>zjq8dr$n%mCybVgTLx zUR_f8)C2?Ph&LJyX|-C`y5LqQR|<#N5hY3%&StYJXt<{%0O37mT984J0?BUyP@joE zOaO?F549B$#R;MmAA^~AnE_;<*&=3=1DK@06q*z%=`4(1e*jw4~ z0qlJMWovU6H!CGJl-MY-xwmdtq%iY)ns=JT%y?aMo6Y-_-gBJS`Tw5(^PJ}DNTn6v)$^dz}l z?&{uG0e*DwrC2OVzu#ADv6;^I`F#40Q3}HmU~mHGDA`>A1U8Z_U5_|(ZouJi__HaQ zb4ntSh&q9ItyYt6w|oCZ)ai881#Y-_Jg%Y$7#{#)v6!9+#DQ$JTE_40cff%7Zs&#L z)@(La1XswMBT4plyIm@kirRiH^P2&PUpCc0o8Q&ftnh~bj4)&Ervm)TumCIo3*Z}` WA1sKIggnat0000>)WwzxBKr~01&TMs}c%@WV_wUYPFI?BEi)4t02Yzh&P)}+3)w-zg#X- zDwPz3@mqWv#0bC=C*w>eBcstsa=Dy%JRWspHk*mt?Us7It}P&@5XKH5PW1czd*+CH zy=I<#amf@8*aR#6tkGuT&~3hV6jRB0QN)BpQv%Znx93ryd@e$n4R$MNZwy{|3K8G+z z!Wb@C4gm4&)`v%71mO4kWiS|M;ZcBOY1xfC6eHr)fzRi=uS=y;5BGiuKv5kI2Xz3D zbULj^5ex=jy*HoFRS*~g7kH2Lwg7s)9`;=ujfVCSW>Y>rs(-WDs2hPmKqp|B26zX=cKp8o0}g-#@aF*j10gEyIQQql Q8~^|S07*qoM6N<$f*=3`Hvj+t literal 0 HcmV?d00001 diff --git a/Resources/Textures/Nyanotrasen/Structures/cocoon.rsi/cocoon_large1.png b/Resources/Textures/Nyanotrasen/Structures/cocoon.rsi/cocoon_large1.png new file mode 100644 index 0000000000000000000000000000000000000000..f9431f6428586fe569344878961bd645cbc04fc6 GIT binary patch literal 762 zcmVgc z)HGVt5GrUW(MCfaUs*`R6?PUu8&3| zNvG3tb#*1>a@o1Oyxf|EOMu{L0LW@nhDS$7QYw{P@YQN1KA%r^cX!o}?e_MT;npOS z0IYMloFtP;d3$?vgMWN{xLH0wKdZ(L0YI@wA`ywlP)iTC$+wedl~_5y~WPfkvBj3y>__7w^Rr`>L^ z5zz`*DNQVxW%cy*B;jyavm+Di%jHt5z#cw`quGZf5((9|PBO$gJv}9kQ>j#T)_MSj znM}ql8BVj=Oq~EE3y3gB=JUBr#MYRT2ni7n!3I&PNX3w1u}F_;pC|#GC@tUs6Lhgy zXaidIcHUNUn{!$Y^}?1i+)CY#N=3DoO#J%UXS&qe~DNc>iKw1&f>5L*PT;*8~QGffs!} zpU=Pki~!)>Zr7AbC3$_n-??}$BvKPl@Or&&PN$QpR;%WCJR0t~kVr)!dh+Mb%Vi;e0@UO25G7E>wo((o??RzquGg!nR4V3jxrFbX&*%3O z0E81G`|WlU5)_17DiwjiTW>rbi}%=(&*ww%*=%N(%jNq**lxEagrpwNU$2*_ffB3L z${IWs0l-J2k@(Mp0$hEk3tY;H0N}k|Po8sfE##*a31Y2Q6cTGIE(O#1Pj;9&toXiW$VheODDlgT8zK?E?T*O80l5Ya3g z4hJEF1z2KRP8L9d0(`w*ixt?Qz^Fn+hf<~lIGK9w2T~;8?RMdb)e4s!I1vDV(_;Z@ zaAJsPK?p0bYO~p7OCV?~nE~2@bb^MDJU|djc%Q%;i;ZYaz@K~`sb}CY@i-+99Y(s8 zAhX?W!`+Zni39-V>bIfvJK>}Bq&oirjJ71z@AtXHst`+{)9H8sBQF4W&yONKK#(DT zyoWmEg6Am1XCX43PGd<}SpWz>hyvPj6oG^YkErDOS?<#jTvI6!Y&B08jIW6P@f20a z0#hxX%$l3vJF)B8X%5)#dD~4${QyPKZkme+ R_X7X`002ovPDHLkV1mDMGcW)E literal 0 HcmV?d00001 diff --git a/Resources/Textures/Nyanotrasen/Structures/cocoon.rsi/cocoon_large3.png b/Resources/Textures/Nyanotrasen/Structures/cocoon.rsi/cocoon_large3.png new file mode 100644 index 0000000000000000000000000000000000000000..9a033961ffa27e1796d98c10f6fc9d563f13066a GIT binary patch literal 876 zcmV-y1C#uTP)P6p9*9R5b`t4M~gH+uwv|vr7wIu9v%9!kbKXc6WH6-}`4~`Dy;KodM1O zXMq0~pwVbJn!mrlo5#n;AAbb^VFc^-x=E+g=JoZ}j(k4v%;$5{ZnsYYISByqTrOv7 zwVJuOxG?MW+Hj9uUtgQaWWv-r1tK&6;zS*dMolahvjAM5pPy|e7K??sy1FuzN`)+h z4k{FY#EEjXTG^3ICM^VmdolqKFsu)og-`&AM8Z1e${_WsJ@>Nhc5BBe_9ejW?XBZB zm`y#x5`1}ivC#uTHvG^27y#ncYSqq_b|M4;| za|~gkCEcuJOs7*jPO%RF_xJZssZ{bB3Nbl}=HBV1Z zwiJ4&5-<=Js4E;0Z#J9Wx8hhy0swb+ced-*Ob8%O#Hm!ut2qXwj8m{&E;Dt2K-~3Z z-|aHx9D)b{5EUJdai!im;UbZUd3$@a5Tu<>M;N1I7(2#YZ_f>Sf#zjBr>G0;d{&+S z01k;V?-3skhjz|8t_T9m z+}|fW$GGwZB@4Y?FOY=*0ClCCHQ-*^ct8vPUUAI}K#JNiLu*@g_>hbjTa zp9$BgLl%fe;>v~^j7|qqe@X^I;+0IbvLu-NH#yC###c+Y{tIbhlW zM6IJ*<