From 4a22446fac51c29d2c165871102bde33a7cda6f9 Mon Sep 17 00:00:00 2001 From: Janet Blackquill Date: Thu, 2 Jul 2026 20:24:59 -0400 Subject: [PATCH 01/15] douners --- Content.Server/_DV/Diona/DVNymphNPCSystem.cs | 30 +++++++++++ .../_DV/Diona/DVNymphingOrganSystem.cs | 48 +++++++++++++++++ .../_DV/Diona/DVNymphFollowerComponent.cs | 14 +++++ .../_DV/Diona/DVNymphLeadComponent.cs | 11 ++++ .../_DV/Diona/DVNymphRelationsSystem.cs | 54 +++++++++++++++++++ .../_DV/Diona/DVNymphingBodyComponent.cs | 13 +++++ .../_DV/Diona/DVNymphingBodySystem.cs | 48 +++++++++++++++++ .../_DV/Diona/DVNymphingOrganComponent.cs | 23 ++++++++ Resources/Prototypes/Body/Species/diona.yml | 22 +++++--- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 14 +++++ Resources/Prototypes/_DV/NPC/nymph.yml | 27 ++++++++++ 11 files changed, 297 insertions(+), 7 deletions(-) create mode 100644 Content.Server/_DV/Diona/DVNymphNPCSystem.cs create mode 100644 Content.Server/_DV/Diona/DVNymphingOrganSystem.cs create mode 100644 Content.Shared/_DV/Diona/DVNymphFollowerComponent.cs create mode 100644 Content.Shared/_DV/Diona/DVNymphLeadComponent.cs create mode 100644 Content.Shared/_DV/Diona/DVNymphRelationsSystem.cs create mode 100644 Content.Shared/_DV/Diona/DVNymphingBodyComponent.cs create mode 100644 Content.Shared/_DV/Diona/DVNymphingBodySystem.cs create mode 100644 Content.Shared/_DV/Diona/DVNymphingOrganComponent.cs create mode 100644 Resources/Prototypes/_DV/NPC/nymph.yml diff --git a/Content.Server/_DV/Diona/DVNymphNPCSystem.cs b/Content.Server/_DV/Diona/DVNymphNPCSystem.cs new file mode 100644 index 00000000000..0c15e13183a --- /dev/null +++ b/Content.Server/_DV/Diona/DVNymphNPCSystem.cs @@ -0,0 +1,30 @@ +using System.Numerics; +using Content.Server.NPC; +using Content.Server.NPC.HTN; +using Content.Server.NPC.Systems; +using Content.Shared._DV.Diona; +using Robust.Shared.Map; + +namespace Content.Server._DV.Diona; + +public sealed class DVNymphNPCSystem : EntitySystem +{ + [Dependency] private readonly NPCSystem _npc = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnLeadGotChanged); + } + + private void OnLeadGotChanged(Entity ent, ref DVNymphFollowerLeadGotChangedEvent args) + { + if (ent.Comp.Lead is { } leader) + _npc.SetBlackboard(ent, NPCBlackboard.FollowTarget, new EntityCoordinates(leader, Vector2.Zero)); + else if (TryComp(ent, out var htn)) + { + htn.Blackboard.Remove(NPCBlackboard.FollowTarget); + } + } +} diff --git a/Content.Server/_DV/Diona/DVNymphingOrganSystem.cs b/Content.Server/_DV/Diona/DVNymphingOrganSystem.cs new file mode 100644 index 00000000000..757d03c2cc8 --- /dev/null +++ b/Content.Server/_DV/Diona/DVNymphingOrganSystem.cs @@ -0,0 +1,48 @@ +using Content.Server.Mind; +using Content.Server.Zombies; +using Content.Shared._DV.Diona; +using Content.Shared.Body; +using Content.Shared.Gibbing; +using Content.Shared.Species.Components; +using Content.Shared.Zombies; +using Robust.Shared.Prototypes; + +namespace Content.Server._DV.Diona; + +public sealed class NymphSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly MindSystem _mindSystem = default!; + [Dependency] private readonly ZombieSystem _zombie = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(OnBeingGibbed); + } + + private void OnBeingGibbed(Entity ent, ref BodyRelayedEvent args) + { + if (TerminatingOrDeleted(ent)) + return; + + if (!_protoManager.TryIndex(ent.Comp.EntityPrototype, out var entityProto)) + return; + + // Get the organs' position & spawn a nymph there + var coords = Transform(ent).Coordinates; + var nymph = SpawnAtPosition(entityProto.ID, coords); + + if (HasComp(args.Body)) // Zombify the new nymph if old one is a zombie + _zombie.ZombifyEntity(nymph); + + // Move the mind if there is one and it's supposed to be transferred + if (ent.Comp.TransferMind && _mindSystem.TryGetMind(args.Body, out var mindId, out var mind)) + _mindSystem.TransferTo(mindId, nymph, true, mind: mind); + + // Delete the old organ + QueueDel(ent); + args.Args.Giblets.Add(nymph); + } +} diff --git a/Content.Shared/_DV/Diona/DVNymphFollowerComponent.cs b/Content.Shared/_DV/Diona/DVNymphFollowerComponent.cs new file mode 100644 index 00000000000..1922cfb23a0 --- /dev/null +++ b/Content.Shared/_DV/Diona/DVNymphFollowerComponent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Diona; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(DVNymphRelationSystem))] +public sealed partial class DVNymphFollowerComponent : Component +{ + [DataField, AutoNetworkedField] + public EntityUid? Lead; +} + +[ByRefEvent] +public readonly record struct DVNymphFollowerLeadGotChangedEvent; diff --git a/Content.Shared/_DV/Diona/DVNymphLeadComponent.cs b/Content.Shared/_DV/Diona/DVNymphLeadComponent.cs new file mode 100644 index 00000000000..e0b217ea813 --- /dev/null +++ b/Content.Shared/_DV/Diona/DVNymphLeadComponent.cs @@ -0,0 +1,11 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Diona; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(DVNymphRelationSystem))] +public sealed partial class DVNymphLeadComponent : Component +{ + [DataField, AutoNetworkedField] + public HashSet Followers = new(); +} diff --git a/Content.Shared/_DV/Diona/DVNymphRelationsSystem.cs b/Content.Shared/_DV/Diona/DVNymphRelationsSystem.cs new file mode 100644 index 00000000000..4e0d40d89c4 --- /dev/null +++ b/Content.Shared/_DV/Diona/DVNymphRelationsSystem.cs @@ -0,0 +1,54 @@ +using JetBrains.Annotations; + +namespace Content.Shared._DV.Diona; + +public sealed class DVNymphRelationSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnLeadShutdown); + SubscribeLocalEvent(OnFollowerShutdown); + } + + private void OnFollowerShutdown(Entity ent, ref ComponentShutdown args) + { + if (ent.Comp.Lead is not { } nymph || !TryComp(nymph, out var lead)) + return; + + lead.Followers.Remove(ent); + Dirty(nymph, lead); + } + + private void OnLeadShutdown(Entity ent, ref ComponentShutdown args) + { + foreach (var nymph in ent.Comp.Followers) + { + if (!TryComp(nymph, out var follower)) + continue; + + follower.Lead = null; + Dirty(nymph, follower); + + var evt = new DVNymphFollowerLeadGotChangedEvent(); + RaiseLocalEvent(nymph, ref evt); + } + } + + [PublicAPI] + public void Follow(Entity leader, Entity follower) + { + if (!Resolve(leader, ref leader.Comp) || !Resolve(follower, ref follower.Comp)) + return; + + leader.Comp.Followers.Add(follower); + follower.Comp.Lead = leader; + + var evt = new DVNymphFollowerLeadGotChangedEvent(); + RaiseLocalEvent(follower, ref evt); + + Dirty(leader, leader.Comp); + Dirty(follower, follower.Comp); + } +} diff --git a/Content.Shared/_DV/Diona/DVNymphingBodyComponent.cs b/Content.Shared/_DV/Diona/DVNymphingBodyComponent.cs new file mode 100644 index 00000000000..e7020869917 --- /dev/null +++ b/Content.Shared/_DV/Diona/DVNymphingBodyComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Diona; + +[RegisterComponent, NetworkedComponent] +public sealed partial class DVNymphingBodyComponent : Component +{ + /// + /// The text that appears when attempting to split. + /// + [DataField] + public LocId PopupText = "diona-gib-action-use"; +} diff --git a/Content.Shared/_DV/Diona/DVNymphingBodySystem.cs b/Content.Shared/_DV/Diona/DVNymphingBodySystem.cs new file mode 100644 index 00000000000..bcdf92d67e6 --- /dev/null +++ b/Content.Shared/_DV/Diona/DVNymphingBodySystem.cs @@ -0,0 +1,48 @@ +using Content.Shared.Gibbing; +using Content.Shared.Mind; +using Content.Shared.Popups; +using Content.Shared.Species; + +namespace Content.Shared._DV.Diona; + +public sealed class DVNymphingBodySystem : EntitySystem +{ + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly DVNymphRelationSystem _nymph = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnNymphingBodyGib); + } + + private void OnNymphingBodyGib(Entity ent, ref GibActionSystem.GibActionEvent args) + { + _popup.PopupPredicted(Loc.GetString(ent.Comp.PopupText, ("name", ent)), ent, ent); + var giblets = _gibbing.Gib(ent, user: args.Performer); + EntityUid? leadGiblet = null; + + foreach (var giblet in giblets) + { + if (!_mind.TryGetMind(giblet, out var mindUid, out var mind)) + continue; + + leadGiblet = giblet; + break; + } + + if (leadGiblet is not { } leader || !TryComp(leader, out var leaderComp)) + return; + + foreach (var giblet in giblets) + { + if (!TryComp(giblet, out var follower)) + continue; + + _nymph.Follow((leader, leaderComp), (giblet, follower)); + } + } +} diff --git a/Content.Shared/_DV/Diona/DVNymphingOrganComponent.cs b/Content.Shared/_DV/Diona/DVNymphingOrganComponent.cs new file mode 100644 index 00000000000..5c5b5e3bb7b --- /dev/null +++ b/Content.Shared/_DV/Diona/DVNymphingOrganComponent.cs @@ -0,0 +1,23 @@ +using Robust.Shared.Prototypes; +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Diona; + +/// +/// Component that will cause an organ to turn into a nymph when removed from its body. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class DVNymphingOrganComponent : Component +{ + /// + /// The entity to replace the organ with. + /// + [DataField(required: true)] + public EntProtoId EntityPrototype; + + /// + /// Whether to transfer the mind to this new entity. + /// + [DataField] + public bool TransferMind; +} diff --git a/Resources/Prototypes/Body/Species/diona.yml b/Resources/Prototypes/Body/Species/diona.yml index fb7f7aaad73..9a3d56d8c35 100644 --- a/Resources/Prototypes/Body/Species/diona.yml +++ b/Resources/Prototypes/Body/Species/diona.yml @@ -165,10 +165,18 @@ - type: IgniteOnHeatDamage fireStacks: 1 threshold: 12 - - type: GibAction - actionPrototype: DionaGibAction - allowedStates: - - Dead + # Begin DeltaV Changes - gib at will + # - type: GibAction + # actionPrototype: DionaGibAction + # allowedStates: + # - Alive + # - Critical + # - Dead + - type: DVNymphingBody + - type: ActionGrant + actions: + - DionaGibAction + # End DeltaV Changes - gib at will - type: Rootable - type: MovementSpeedModifier # DeltaV baseWalkSpeed: 1.0 @@ -315,7 +323,7 @@ suffix: "Diona, Nymphing" id: OrganDionaBrainNymphing components: - - type: Nymph + - type: DVNymphingOrgan # DeltaV - new diona mechanics transferMind: true entityPrototype: OrganDionaNymphBrain @@ -324,7 +332,7 @@ suffix: "Diona, Nymphing" id: OrganDionaLungsNymphing components: - - type: Nymph + - type: DVNymphingOrgan # DeltaV - new diona mechanics entityPrototype: OrganDionaNymphLungs - type: entity @@ -332,7 +340,7 @@ suffix: "Diona, Nymphing" id: OrganDionaStomachNymphing components: - - type: Nymph + - type: DVNymphingOrgan # DeltaV - new diona mechanics entityPrototype: OrganDionaNymphStomach - type: entity diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 8e673a135ca..e3e03b7a81c 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -3848,6 +3848,20 @@ id: MobDionaNymph description: It's like a cat, only.... branch-ier. components: + # Begin DeltaV - expanded diona mechanics + - type: DVNymphLead + - type: DVNymphFollower + - type: HTN + rootTask: + task: DVNymphCompound + blackboard: + IdleRange: !type:Single + 2.5 + FollowCloseRange: !type:Single + 1.0 + FollowRange: !type:Single + 2.0 + # End DeltaV - expanded diona mechanics - type: Sprite drawdepth: Mobs layers: diff --git a/Resources/Prototypes/_DV/NPC/nymph.yml b/Resources/Prototypes/_DV/NPC/nymph.yml new file mode 100644 index 00000000000..b972a2302e9 --- /dev/null +++ b/Resources/Prototypes/_DV/NPC/nymph.yml @@ -0,0 +1,27 @@ +- type: htnCompound + id: DVNymphCompound + branches: + # - preconditions: + # - !type:HasOrdersPrecondition + # orders: enum.RatKingOrderType.Stay + # tasks: + # - !type:HTNCompoundTask + # task: IdleCompound + # - preconditions: + # - !type:HasOrdersPrecondition + # orders: enum.RatKingOrderType.Follow + - tasks: + - !type:HTNCompoundTask + task: FollowCompound + # - preconditions: + # - !type:HasOrdersPrecondition + # orders: enum.RatKingOrderType.CheeseEm + # tasks: + # - !type:HTNCompoundTask + # task: RatServantTargetAttackCompound + # - preconditions: + # - !type:HasOrdersPrecondition + # orders: enum.RatKingOrderType.Loose + # tasks: + # - !type:HTNCompoundTask + # task: SimpleHostileCompound From 0e1e8388270b12ef762d3fcca061ad966c03cc2b Mon Sep 17 00:00:00 2001 From: Janet Blackquill Date: Thu, 2 Jul 2026 20:51:10 -0400 Subject: [PATCH 02/15] steal sprites --- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 33 +- .../Diona/gestalt.rsi/equipped-HELMET.png | Bin 0 -> 943 bytes .../gestalt.rsi/equipped-OUTERCLOTHING.png | Bin 0 -> 791 bytes .../Diona/gestalt.rsi/eyes_gestalt.png | Bin 0 -> 868 bytes .../Species/Diona/gestalt.rsi/eyes_nymph.png | Bin 0 -> 815 bytes .../Mobs/Species/Diona/gestalt.rsi/floor.png | Bin 0 -> 1292 bytes .../Species/Diona/gestalt.rsi/flower_back.png | Bin 0 -> 811 bytes .../Species/Diona/gestalt.rsi/flower_fore.png | Bin 0 -> 915 bytes .../Species/Diona/gestalt.rsi/gestalt.png | Bin 0 -> 4854 bytes .../Mobs/Species/Diona/gestalt.rsi/hat.png | Bin 0 -> 618 bytes .../Species/Diona/gestalt.rsi/health0.png | Bin 0 -> 1106 bytes .../Species/Diona/gestalt.rsi/health1.png | Bin 0 -> 1110 bytes .../Species/Diona/gestalt.rsi/health2.png | Bin 0 -> 1092 bytes .../Species/Diona/gestalt.rsi/health3.png | Bin 0 -> 1068 bytes .../Species/Diona/gestalt.rsi/health4.png | Bin 0 -> 1042 bytes .../Species/Diona/gestalt.rsi/health5.png | Bin 0 -> 1020 bytes .../Species/Diona/gestalt.rsi/health6.png | Bin 0 -> 953 bytes .../Species/Diona/gestalt.rsi/health7.png | Bin 0 -> 944 bytes .../Mobs/Species/Diona/gestalt.rsi/held.png | Bin 0 -> 606 bytes .../Species/Diona/gestalt.rsi/inhand-left.png | Bin 0 -> 695 bytes .../Diona/gestalt.rsi/inhand-right.png | Bin 0 -> 700 bytes .../Diona/gestalt.rsi/intent_devour.png | Bin 0 -> 748 bytes .../Diona/gestalt.rsi/intent_expel.png | Bin 0 -> 786 bytes .../Mobs/Species/Diona/gestalt.rsi/meta.json | 304 ++++++++++++++++++ .../Mobs/Species/Diona/gestalt.rsi/nymph.png | Bin 0 -> 3887 bytes .../Species/Diona/gestalt.rsi/nymph_dead.png | Bin 0 -> 655 bytes .../Species/Diona/gestalt.rsi/nymph_sleep.png | Bin 0 -> 1305 bytes .../Mobs/Species/Diona/gestalt.rsi/wall.png | Bin 0 -> 1706 bytes 28 files changed, 327 insertions(+), 10 deletions(-) create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/equipped-OUTERCLOTHING.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/eyes_gestalt.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/eyes_nymph.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/floor.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/flower_back.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/flower_fore.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/gestalt.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/hat.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health0.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health1.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health2.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health3.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health4.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health5.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health6.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health7.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/held.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/inhand-left.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/inhand-right.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/intent_devour.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/intent_expel.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/meta.json create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/nymph.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/nymph_dead.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/nymph_sleep.png create mode 100644 Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/wall.png diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index e3e03b7a81c..7d50088f553 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -3848,7 +3848,15 @@ id: MobDionaNymph description: It's like a cat, only.... branch-ier. components: - # Begin DeltaV - expanded diona mechanics + # DeltaV changes begin + - type: Sprite + drawdepth: Mobs + sprite: _DV/Mobs/Species/Diona/gestalt.rsi + layers: + - map: ["enum.DamageStateVisualLayers.Base"] + state: nymph + - state: eyes_nymph + shader: unshaded - type: DVNymphLead - type: DVNymphFollower - type: HTN @@ -3861,15 +3869,20 @@ 1.0 FollowRange: !type:Single 2.0 - # End DeltaV - expanded diona mechanics - - type: Sprite - drawdepth: Mobs - layers: - - map: ["enum.DamageStateVisualLayers.Base"] - state: nymph - sprite: Mobs/Animals/nymph.rsi - # DeltaV changes begin - - type: Carriable + - type: Clothing + quickEquip: false + sprite: _DV/Mobs/Species/Diona/gestalt.rsi + slots: + - HEAD + - OUTERCLOTHING + - type: Item + size: Normal + sprite: _DV/Mobs/Species/Diona/gestalt.rsi + inhandVisuals: + left: + - state: inhand-left + right: + - state: inhand-right - type: GhostRole makeSentient: true allowSpeech: true diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/equipped-HELMET.png b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/equipped-HELMET.png new file mode 100644 index 0000000000000000000000000000000000000000..9040077725357d3b9aab445ab821c3facc551038 GIT binary patch literal 943 zcmV;g15o^lP)4duVu?hAAi^kE=*1U{UX%WS zh%kEVZ40s9dJ9FOKOpQis+WBU0(*%d5Gh2ok`~Q1bx8_y+i&#fbq%^R_wMapW_~Zj zy%*=4^UaxYcFr`waNb=R++Da-puPu|vfzoABWZQQApNL zxchr8kp7PyX~n6|qgY)2p+tB;@ul*1(#XE?8o-ff@N9tsqTDs zBe~bn2=Kk79^)ShnpgPq|QZES0rC*Q?V9RGy61fjhQ*NPzlbJU4JhxCX?kjNuaJsFJ zucGZ>qgntZ6Czy%|0fKR`wp%F44y4Bz;qfWP5?oI=YJz^!c6-~tN@Y$K}n z2U!3~A_Wkv%>W`<5M=(7#0n4=i54K}=#xYX5C%>FSdJ3_mg59~52l9B(WDPh%FBmXJ=RlrwDQeHg#f|7Ne+F595?v;`1Hcp#Z> z&TLj`GpbI{QuSKzROM^9BGBf!yA@|&Zrc&NY{e%Nn_2?UP_{d>qaq`1_H2g=o_7Y` zAdj$7cM*E?$S!y6vcEAHs!*yeymkhNaUMs@ku+PDs^*6qf&TD=C(|o=OtF9Lv>O!P z0?6opoSIXH>$~=$`1PkMm9vF7H5krWotKUNnC2TcSR56*Z0E)|v#{bHG_ycE0~kCV z7|rdxAK<+&Pz$_4RTo10`n${CYOxMl0?2g~s_rG~`vKMV=+Am_b;~~;v;>faN2Pp> zpg#v-Rw?o=fVUnu(XfO#!3^kZO&L{+wbuZuJTeq*%Kr;ai90th7~NCS7Jy~xI3gD% zbTP_+IAZFTF9E#!?IdJ?&hG{D+0>E;d0I(b<04&D|0LyU#z;dh?;13^e)+(ui RfA|0Z002ovPDHLkV1j5?u$lk> literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/equipped-OUTERCLOTHING.png new file mode 100644 index 0000000000000000000000000000000000000000..16a6612501e10c1d520095bed68aee66b0664c9c GIT binary patch literal 791 zcmV+y1L*vTP)(RCt{2n!#%mK@`TnNI?)aq-sg&MT6LbClSFzL6DpTucCi} z2>t;c>Pc^2E9ze$DR>e*6+vh{Sj3~Gl<1`tlGHV}2tCN^H#$qQre@#zW@$R}yUcVp zoq6-UnYY8vn}Fv80W>5)rj5Vsi4kcU)tiA-@5oGS#@N8guC>}YSDP{=+w#T+kpNcxVKg4EmhL>9 zix(HxO$kLNrw;?uRW#pj3^>4gn{5=Gzk4#j>@Eg$t3OcLD)0H+4lXrf6t+JK`(<<2 zOf+5Q9W%24{lUo9ad#Bj!V=L6up;09AwCD#SB$HpY0>092pxpUlkhzNo)ZLs=L7-Z zIY9t;P7nZ|69j&D85FpP#6_F26lpVPQaGehzv13?W!hdf8`;~ZHKa%Da zz~%$+p5W@e%c?m6MWPY}UE~B5MQ#D?Z7}Z(UF8H6K`sH(w?o=F0YxHCkYGvA6MCOl z(`ycM0wi`>oFz!uCBM>X|Gt@xcY!XPz$p4B|6t)wGWPwuS455w#ekwGIg0e;3ICRekV$}X%L{04 z?LajKko|Vd{g+dmyao0n8crE&0W=5#z;l8C@SGq3JSPYM&j|v+bAkZyoFD)^=O-L; Vm`vO^<}3gJ002ovPDHLkV1m6*Tp0iW literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/eyes_gestalt.png b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/eyes_gestalt.png new file mode 100644 index 0000000000000000000000000000000000000000..adf73f17201dcd52cc89584efcf60f520a029303 GIT binary patch literal 868 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9F5M?jcysy3fA0|T>| zr;B4q#hkZu5B6Sh5O8Jh)RruGaB3#U1do)(3T%sJxJ>9sI2U6%fss*XOPE;F@w{EJ z@78U-SG{~{_^em?%_CR_~ej1M#zWLO&(GZct0^f4U>WUxRHJj4{UzrLcT zqQ1JcvFO_$MaF%9|NQy!@$K{}AC`XWkB`@%8DE$7Z!rgFfZu`QQ`0y7$$xTEbN>H# zXP4X8-~DG9x9&tyO`A;i&*{Hfc`U0wx3k>P+VJhD%-P_#x9@&b{WD7@`C7sG`@cTF zJiFh!;nqI$$4{&w9psfGTCBC^E+on&j_nOSGb>4T$y9a>e(;psL#{8Q;yJzlW&iT6M{0`P@ zRrk_kYopbztm~(&(_N7E`^4V@xAXPhf2D5z{C+K8Xu;EF_c%^|^%<5E=GN!e$}>O2 z8>o2Q2Mzu?8yLdVS>{;Pdo-N6_amtKnD(Rp+uMxKvuU)jM7$1L`}LCi$*|pDPQ?A% zYHG9G%sY{7@6W@+PS^U*XFRT3ReN9Ce$({2THfzDXM<)q=KqUj+OlWAUAN?BmZ)!k z*ZkW1vz)Ww$?wNbt{Bghb-B18amL2%?m@FFC(O0?`YfBU`g!d!-3`}1+r55u+v?)YpNmU!8I|wF z-w!s={^sC)_syT`*XEoCpZ+uwJvBeWNoZ5TXV L{an^LB{Ts5GcRzO literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/eyes_nymph.png b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/eyes_nymph.png new file mode 100644 index 0000000000000000000000000000000000000000..8752226f4e88d884ebe7c33a22b32e08544ebd6f GIT binary patch literal 815 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/hat.png b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/hat.png new file mode 100644 index 0000000000000000000000000000000000000000..5f0fb2fbe574a090d0d6fde193dddffeb7ee2910 GIT binary patch literal 618 zcmV-w0+s!VP)&l#io>&0kdz z?jJsv*e=m|ty(V1|HL0fl<55IbYJy;2I}Rtr8X+7s@>}=*Bz^J+0m?otxsC=?e5f6 zWo<>(Zyy5@!6$(8M)Q5r?syLqgV9j461F~R$=7K1)$!q$_Iw}!BL~Lo%hQo~*6j{8 zdvST9Zm#Y^{b}w4j%Vp7BmiNeQam{Feva3c_WHa&y>IlVxzB+~KbnBH-wDNdsnu~+ zUOv$ub3~+3l4<5X3_=%kgZ4(E5vIRAaekYNrVc9N=YmG*e0SX zs^ql_FlPaQ8ZIr?4%Bp7IAuEualD}#drU>M@D@u$2_i9v{_c?*QiF-l-8^}fMicLS702~^yZ+HBM^WGJkenJAU%Au|o zaWc{(fa4L4NpcxoM&K^*=Gk)>3MuC9iML`|_U5Xl}LJVba^hz`-c zKex^uJ9`Z8UIIZ6T|zo|DAx$VQ1M^bLA2Fv(G(TCZ10=*zS)_#^WGZv!7}^ao0;$X z{hhfWW$@@tZ^89`{PkRg{0GDeAaPC8<;3sj72*_t%}JcPSLt4vDb78Qh!O!LCvj`n zOw3YG3&EPL$*g*uncJ%HdaoDKscLKX#D_JfNK1f89#1S0VP4>kp|n86z~2&#?l^c_4`pYjc@i z{e1lU)cQU>yDmcydsHZ51tfW(9I#fTNNVN*01qG+qOb(yGT5Lff=PBZavP=x;P&a+ z!{j7Xjl{3zpu+_s{)HUkm)`zR|6BT-1-$^s=8n|(;BI?~`z-;ggNYPsUfi%Yc7?=c zG00T~tYww;5P=b)z=*y8L_4Gv0({*6d7`|)R_r1(0n4!lYe7!y8GnwZS7HSKb|~Tx zqaa^Ik@Fk?7<+hw=)Rejckkz&`;-WPC0YcKajpwI6$-{0#F|zz6a@a`9lBy-83N2y zZPid(T;O+-qw|^M-F_~a-kW~9A`6mD&H(@=gPKL-u!KawjZDjFEy=9}9`j`7)uyhB zSOFx*xU}jf$2Y_j#C7$ToR&v>iFzLKBCmoE|_(5W5}UGZ;}$pL4s~<-qeN zo~(b*DHlh{O%X1X2!Ped+M%`{`Sf)|g# zvDf6(w|EaUh9%eDmQtAwwJ0x$!Nxn`bt;HugCbuEEk*T!0P9@mKHp&DF6Tf07*qoM6N<$g5-Y%*#H0l literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health1.png b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health1.png new file mode 100644 index 0000000000000000000000000000000000000000..9f2473a11290d219bd9f20273ee7dfe760909084 GIT binary patch literal 1110 zcmV-c1gZOpP)6x&Si+HC-_I(FAtvgmp13N__^zOa9Bz%At4XMk>C3B#Co2;Fy_zA$o}N9-0V!r zf=8a{PRRRD>ncRahD$Qo-(Fed2{A=z0kA$jQeQPW-)Rveo+qxIh^&d!Ua66yKc~qP zuy=4IiyPZYOc0g_s&#uDOfTe@iyrBr4uuPr0K_v&1T1xhAPV$CVee3uRz-#e(kd75 z94X*555y5-Z6VjEpO1c>Sl?&n)+KwdSA`-{K%56o1J;UENXIYp@pNw4U+jXnG}5 z0APorBE&@ufLyKd!PvtaM9=kqC+$SUemS_<`&bhAaDPJ&8PFl%O5cnVO&=q6L z5MZWitFkF^f!|4v&S#SMc)4VHZ|2d8EJ!{+2LO}|Y8H*d5)uG6GA*aIB)1ZHJR>VF zHbZxdkN}cnTv~OL;~QcM;zZ*`r{%$3qMk=HlJ`6jD*(V*P7fd?h~19o8H^~WkA+?B za^UI1jI4hvC>ICGO%X1H1i)%!ZMLIVK78I#A;BM92Lh8AR^uDyVNDP`z~UO3B>*I_ z7^fEg;8GBxnZVT;LC%W?(G+O0=9xlr-kf3u_?OEPEG3HGnQk@}lC#X<1$EijD~mv) zb&a-KB1m4G*o_xJL|w%Jk`Ik{sb`cIL!ocdh6unsCt}MGw^AE@(C&py6_@wuJLRPVZn1 z|7ZU&_hfT3x$Fyx|AUhggEv@u#&mJgal&C8=gb#EkRLeG4?1T zA_7cuvo+!NnPGw$u3_T&s}-+EOMq`836o%Y>(;}8^7_oCTx@TZrsm38-?oFk5h?qG z)ZBz5IEYzY~DMAal`*g_K?J1|N)rbFo@MlG& z{!E>e!uKtKfSuianP1yfVq9ne-@pEn8;?Ip94`O9=Y2nWVobGbi&-h=4=gVf7E1x? zo(_c%RtT_2d(U(%kt>4aKF|xrojqAt78xGwRC@u>u>#`A4?~E##eBbh-v4o6^-s;L zO741}3Pr4dBoEXA=89BF&3ypC1K0~LECGmNkc?UcT%KgLO>V>V0Ng$`vzNRHH(nG( zav71lXc7NH4)F_5zNr6ggRO#E05D~*UFnRt-x8oIm`M5TgY(wHzL5AV2D!?Bm4dP! zBG4ifXi*mcx29MjKqt}>UPxc9izPDw%P|LYK~C!#f3~JqVg&$pD5^qy!~n?EoDhsX zyg~F{$jGyov)*+g0$_<20pz{&DxL}j^DLm13Ct-RAd3hfIogF~KRNmk zQxK<`PI@hOYl(Uu%}OEgM4|uyXE{B9kRWzjfoISno!%C=wabC~x3jYPxu{(1lA9uY zhzNky*tNNiK6&$gO@#zMxDEs+F|0-(#$k>N9$-lY%@P0-Sd3E(Ke!ZxC?;?1I(OInxYYP?wFJiU@49?$I_&1j$Pi+sOimxT`oo z^5OAr^^EjlDD>UX5CNFyL}D7^Qi?+Z8x1YN4TzTHuug5V@rsD*_)RUsXBaXlfW8-M z0XIjw%IUCDVc=G{`EZG7+HFu-f$Ttdcx+40SfvesYZu385e_-ldNKJi-UBUR%5`?6 z)y{@mq!+|s;jQpGRf%P@B3}wEMfQLI^L*z%-(elI99sfNFh5xtfJ+3y3dxj|KtySx z`jLY7xbg7L$NIr_+3!LNFkQwfXci!#@pQemgyTU@?_du9vwoOsvbdRC_Jt(;#Nt5r z220PFE>3$+ILzZ18i6%QY<^4n>Oka^+}%*-b6g)=v!?hyV*di7!LbL5tzLZq0000< KMNUMnLSTZnTl)b3 literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health3.png b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health3.png new file mode 100644 index 0000000000000000000000000000000000000000..4065fb50eb27be368a9e42f7275ce6e56dd8173c GIT binary patch literal 1068 zcmV+{1k?M8P)K+r{-5Em{=jR*`Czrq%xjZTB5q||1*&$;)x^WJmM+|l%b;mp0~<9XhX z^PY1rN)6=hUN5`e&wk#%Am@Op0!W-mHzf9-Rftmn76);m_}e_QB`69l#-2q)On@fW zt%lvF!vry0Lu2CIx>ICCfNLTNlc2rz_+@{2Yko&=b+k!yi@(;7J+E&_$}S-_laK@_ z5p=sNBa0q+swXAWA2-arbkh}i(BDxR zvb4El#Mn5I2Wo{tLwljHQu0U-!%(}=HY_8;_I9DOnc>urz$ORjb z5cmt^LJ@cVK9hw@qbDM_p*;Y%&&(etC*k&_m%;gr$ee^CPD2jy%dfwg|Lp^9f?5DD zUmo7e#@rtfU@DkMxiQEEF6;`4%VLn59pG(6SPv0s5el@Z3xJ)_kr1F0xd1Pu@7Gl& z(}3le!#Y6g8Gp9gD^&#m(NI)`xQGFen>hg(dw7HBy_u0WZx@{F#00>SPy{gNoacKg z5X=fnD;Wv`|M3o0vFbJ?n5m&v`HZ;0ev+f|X>!yR?Y)_&tFkDCGcXthc#Y6rbQa{H`s3J5NjWmrm-yfa-F z6_RtC!3*ZHvG0pOqIHk7M?{dkG`^QCfJnL#k$iBh+dN}?F%;q5P>BHaIgz*xaV^84 zfkZ<~Fagn$9M+j68}mg}#_xn8T!vu>c?j=?p@89`u5vo4R2aA$Bp)sj%?Ax;S73J_ zJUq6gXVh;!;M&D8p$Ll{YfVmlsqTT+V9RAY(s{M|EwQ+O%^xJ6}^xIpI97d-(cw(?c%iKgu^_Jp%GY<#QIy(RR?09 mo=W2aybxm=7#Qp>59;wp%B3V8F0000Om*U<{An2k^NDCLGMg)e6UttT;MyJ72GHf%Q=iK{T-+S(v83&~YhBN1$kLP(m z&U?>2FIAAeeYNC!KJ$6&oSXs53LtSN-B5A-v_iZBusMkHh2OT%mY^uKn0p!#2?3g1 zw;Jx44im(14~>bpYhIBt0ltMKtOV_?$1nR!n{(T8qrFv{nuEQ5?3%G5Dfxs{O+pf! zM9|Hyj4T-PR8LCYeO$Ne(v6qoUVr=XBqKzNhy=KPQv!wXnX?<|#luHx+AC!R09%u$nB+261fad)5(6N&YeF#g@CMO) zJtME*%zO7q2!JJ#2w>K`F7Q++m=%;(G86><;~A=A)nmv^jjYOL#0QR(9FUdu^- z>+ds}zc3o~hqID5o~S4Qz`L9tKu8d~9pf3aI8Gl5yOGO*=TEY-{B{J|qOd zYGiG$qfg#{+OQ$P53U1&CWh4*!#u2U0n$q32>~F1#dvGs2bY2n#RRTK3vymGh!&^B zqIS>=B*!xz-LeA0%Vi0c5=HY)*F}ZoJZA8My=?3SB9Lg^!)-AUBrlHdRu({1x)G6l zaID+*S%MLY=x(S)03cs7?+M@DiAyP78b~y>1QQS~$zh#MvavwK@%-&bgwHUXAcN>$ z7zubd)KyA{l?oGg!sNpxqG`Xuo(h}}geQolv94z{Xbo`f;+#l?Lyo;Br@oZ;KudV! zIy=%fv!NEp3u3VGPI#RPV%coTS3*m1dO(16zV|+#VIN(NV**I9I2BC5C4yjuovu!jFxKlGj~Zkj80A>k($ zC%QLSdPciA?Rmpt9p}&p>`7w%E$OQRiBEDjp>)687+YOad>^s@0ET#!ogX3~m;e9( M07*qoM6N<$g5wU;_5c6? literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health5.png b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health5.png new file mode 100644 index 0000000000000000000000000000000000000000..2b6c297d7c24c680f41cd79e3641a8b3fbc62a95 GIT binary patch literal 1020 zcmV4V|Ud-vY+ zednBe&%Lif)j;-IXW8|B_T%OWbquH~fW(<}b7KEdg*XMkIEcOCPxH){peVE$dlV5d z0g_y{645@HCWxU8i5IWdoFYR4ToXx{1R1S+Pln4Ib6e_icZX_i^KE_G(S2j8>=IIQ z5t86Ag0A#s)Pg2Y4W!iT_XRUA-Ev0V9PX|R(n6#NDDORGwaj`W(ffakge)Dr>X z;K%RK^UPd9Mh|CVq0^mJFphYUsqAs|cX>b{;H|Mer(0k^et&B=l0YvI9Qe&nizxB2fT^7Fz#!S4afOOOrdv0*It?O!Couzj?;?Vk+$2P>29Pj@sg~ z9lMm`(7-}NOK<_AB{{q^i!ASpsEpsTB3zDP2Wi-Qp%rjvtgoC7DisE92a6Avh}OMk zvn#MWkRCsm+Pi@XztzCCi({;a2svy`O@FE$f%ag__4cIA%7$8OFNlHR?ch4)=d#U^ zuLPcA_kaNNT<1RD!Hz7)Aps;LqBL1K3Gp5`9^Uy_KWLZzQCI;o zWUPX+00Av$o2(}s4{}BabNHY2L)v6`-lZ-5GRBH0000kEoXO3bHiG%CIgVadAZ~IzVrF#IXMGF5}@JP%w%l;s6?Cuus94?8-L6@TY{v}V(cg+LIkvN z-D=o<+D#zCH8ftmJ#>=nBjB1ygK^MmJ$ky*Iohqr_3WIar@L$Ys(XEF(s2I6J1g1VAdiS95fHGYPXf*<99GTy#HDd*`h7DVf!6Vf z?3F8qjE$ib7~}#CtwMFb=^1SYu5jT@U7$e~8m+bz4@B;+=1eT$Ju!hK3J@p1Py61t zc1yB;f7!SqF@aGOAfNzZ!Nz}40J3lGw*Nf{lPG{pm|vX})B-*B-mP3n{yqXx6>M07 zFk~{oBY@q51X{QPE$V`6Gh?oWlKNcN0q&muXRB3-BoMd5L4^2?zK0sbwJWpo^350L z+93k2gUEFny2fyqbOnKDd_z?nn1+m0Z8Rz_v~g5EZQP!NZw0Bw#{htoLC&IZ*hFj< z*suLd<571A`+JB0jiW6dRK*4K!KW~sN}mtyJnjx*!x@A)%c%fd0^ijwj1?8 za^T71yp+B-jKtdDFbEOAt`W8C3(NBUQ`xu#KP(6X+AwyFK8(X0B1tXjBLE}VF-|S~ zASn!@m_TZ@FwTnxF{7SS52s@dR$u&xCg5K#J6g)7cc$y2LgP#`s9-J|tuBe?MqREx zpEu9hr5)3j_w&;DaBBKu6(Hgyq;Za6j>8o}^glZ?Bw!eT@uI);*fhjhfI|a;hL&Ie zqP5eECAc@f5|D6h3>oAhxEBTl9;_{PGJd7Pz?vU?xJ0CnCrwr$J4_&Yeom={;a6O{ zI3^%r8OK_$H@`+JFyp6OZXshL8*GtYAcKW#{&fnz1c}V;arx%&6xjmvn za@^a>2VC^P!}Lz>f%J`tblQ< zgE@R={m^T=%6sTS(%pF}*tG_g(Gq7ICmiN+3^#!_X;}Z4bk%{-UvfL3^m|kvTU}FJ be`5avdJ=ix*`BeH00000NkvXXu0mjfUKzBE literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health7.png b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/health7.png new file mode 100644 index 0000000000000000000000000000000000000000..6eec9c14fafe617f7d8068dec14661bd1cdef01e GIT binary patch literal 944 zcmV;h15f;kP)XQ|G_c|WC6S5vl3AWVDWlTG2!4m)!#ckH;c7O@{T!}I|~VqfD7(| zN-~lJ`$2|lNPGS6SS6VvpnX`zB;jW-*C#((U2}JN$&)rkui@p8a5X2o=E^Q z?j811K(hT8A{W1Yr`~s49kczUX_vx;vg(bCeM%ww>FjxY5|Oa{rEw{lRrg3t6*gVj_GSNAR>U>g9KW*0xjwS=c)wc zLbKEX?wA5MYZ5ogAivq6@W|NyI!OWTBOs*K|hfk_Tu@b>3knpi6y9ekZI-- zz^)Os+bd1;;me7234V|c01_Cx#u(<|8);<=mvk!5Rj6w3d#Zmo4-EiofQD5I{1dqtRHes zt-N~|lIhMn$*!eQ87Xm~oN)M#bGQktNnrV3QmX^rKVvtbrz9p2Fl-1(p?i`&%bl5hh-Ouwo2=&)$J+YAE{!|)2u{Vo3!LR*srM4 z#+s_#KSv^>R{-br#{0P34h9qb;XtzzX>HPyuiogXvy(k-d?Wxf2S%H#lbHn8?+i3s zy*^iWHxE|(H2r`VSo*O9AWT$>fy3bE1!ZY(uWFNiqkWowPE7jI1hm3ND8@t0wy#_P zIY!==aIOhpGWnrmKDa125%QWz8wo(RB4MbMCmq{pjQw7Kz0I+glg_M?%?Q%T((s&aEm|p?bZqs5d zH~VEB{n%UNIFr{Bz~nHMVvoSAU}c@NWV;+MEkWMQB#cuU7y4O2-pnL4BhdNwEql1T zJubg|d?nVMiV8Q-pn&0fCJ<*PQ|7lO#m(pIJbM@VArQ&(~l(pyBOB{JZ?r>1aLjVHA!xr s%e)x@Nbcxvh(C%@Il;Oen~izm4^RVoJ{lffF#rGn07*qoM6N<$f>H?#jQ{`u literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/inhand-left.png b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..c4f19d7a4a04497d4ffb0f5057878eaa148f73ae GIT binary patch literal 695 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|Qg0 zr;B4q#hkaZe7&UuMcVFXwmNNWiO>yxwa81MTW7bXhR4CsyW+pOT?+2|iikUD-W7HI z&8_0JJ+|}i7VVct9SW}&xt?q{)ac;e`-~^X<2e7ECvz5AKEHV;Wny{J{GG+>hxfz= z^l>&6`7lgLW?CR4%2uiZ|5b|Yc3-{Xs_`y8Mt^Q zspoB>4vC4jGv-K_X@8EsIL&ys(7c5c;`jf~K2Z5l<(A&FdbT;i*Pa(0)>{y^KDjFE z$i+oh-#vKw`yZdfPN9xFtQKtVGp8vq?q=w;w-fqa-*3z7WU0n*%8(J8e;puFK?j3a zIo}DjbugSP<9Pb(&$GW_pKq}K=`ZuJnwnG{d`8bE4%muDU-zAMCiL81C4te$wGa*|ZZ<8@^0jE?X4*FCbo`lF41*0?ZsA`t)7#0wwjHOucfN50)ewofvCc&xNMPaWviL&%4vl|Iq6=FeZIke+ zYji1CsV&G=sv0{jh$GW{(To-etsR-x=OlN&4e@%$H*9X{J7Y~Z7B)vKJX*YEJ|Mr(7urW+Kx0HX{`i4ihlb;k%+g#qb z!oX>R*h*=h+3%B3H*}dw+;!?A*xnX1(tfA^rl>EA4S@&)knb z-=tZ-x_N!W!gl5xnju2o`8vDzl-=6wK3_xJZ*HaC=3@IV^_RmKbQjLu%Aa>}zujF0 zt{tCc{Il!#uVr{8Kgr?mm!(y08W(c5&UrfjQ0ikT+4(G-s*Hk8+bf)2r>ziK$h}~} z^jAgZ94MYn=;L50c40sXj}j{f^B@(qW=UDj7hx^OQmj;`h`l)0EFA1PwU6b(l6$%P zw#?VFDRdS&J8h>}fTyUt!;0kgO-w6(?3Ord=i~VJyxpx|`*lC8w=0={c)G4g_HhGF z)li=Wx(>FgY+C#82lM;AzUFW4lK4v{Bk7HUcEcp`*V@M4UcddGw@_FY9HN752;LOc;jj>HSfCCq)ppn3%46| zn6DCkwxf|HV12q&{c-=07PnmA#8>rOSSubI6`tZ@k75k$IsHTMhtmp?&3iW7;cEuP c`cBrReWFk9@A`fNm>d~AUHx3vIVCg!0GlW$+W-In literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/intent_devour.png b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/intent_devour.png new file mode 100644 index 0000000000000000000000000000000000000000..2c1178372d642b357fbfd953f438af13c29aa2f2 GIT binary patch literal 748 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/nymph_sleep.png b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/nymph_sleep.png new file mode 100644 index 0000000000000000000000000000000000000000..fe49b15fba8e5570d3aab9eca30c3f8859e3a106 GIT binary patch literal 1305 zcmV+!1?KvRP)?->Q|GtBsH-`R%oaOEGV+nE5W^3Sn$R> zLD>5R@h|9w{smdsdqME7H-g~B3hKoc6@}_1Thc{WnxrPNwy9__$2^m~r{hUK&bU4C zV&;J`=gdszec$K2Gw0)(0ay-404#?i0G7iM0L$SBfaP!mz;ZYOU^yHCrhoYD;*Z{; z)DW)y{%1_;u3q}dt29>d$F0XPM2y|GZ$to%(+JC7&*wd<7vs{7h@ji&uer90#9;D& z*$+hkgS|OZh|wUyGIw~~Z!f)B^y~Hh*d=T>9&P<{_5|+Uf38T_ssC08MF0nrL0Qt^ z(a8)R%$9IwI;XCH-oBf`i+#D`9Dgc5hQFT9`|UhuJ=;yQAoLW_gJ;k>cuq2+2wosr zcQBj4+iFwQHQukOlIN#SO{!9|F)Tt8AakC<-hNWRso}J$|7U&)Pv6&ZVLGqUF-j5` z{LjG{xtC7W5{HaZ34{33{AjFa~(s8Ju2Xlm5;#iMjOvr&puItSGd~f ztTS@;rL5Lg5o?+AZgdx53Uan>rSeb+NsI&N^J!vj~&6Y;wwZ2FYX~bn#4le zCNqIS6Q9;UW1?N}!GkG`2w?D>i^X&t>FEsWiyx65--qJj8uqm7D~J{hxY;FKM?QBL z3$M#PcY`npO~6-pF}Vd1UzRJp!-~4 zg3tu;)^h&moVRap9JNPfq((-*DhaLuH)o=G5Tbo9unbg)AQ-%U9?64{VCG&acH!Kl znrKYL+jr(;`V%4B5g~YY(e2BvS#LKT)A(J0K?j{eAF;Rt}` za0I||I09ff909N#jsRE=M*u9xHxob&PL4&T?T0)rwwr-3z~5@yhyanjM1bVEQi)ST zN|U2buj}?vfHr7L)M*|EeKPq@XhZ-9=BM%UQUfBCQm;!54=T~*?|=V8)SwN1il;hF zS*+4XDqrXmeKY+?YD54H=)wngYRnbNN}d*lIBGC>+G6muO&_R8B?dQqH2FN=LjVV0 z85~4{7e(e+E~^AlLgeaxfx+uGNeGo<-Pg_hXBn&?2zH_ZEU7M?gTZGGCY2LRe}d`H zpRUkE1=^S^bdGuT=>vVCPZUhW$Dsc_=2Qui;}f6WCi>3Bk^d1TNhhIa3g}y1^xz3HEF|z)N(N7d`UTpe zO+D=e;+Oa=3wBUO6ZiyB^whS$sE*7CU4>=vWI8jNz*4QLO3~=Q2Sg@AMdx{9&p1Es;yGlq9V+&5zucm;IAd4AyTyTEJrw*v&^!WaseF3fdNM9De zAY|IbH`%Mus(--Cfn~I2%YNVb@;g69eVm>(K<{wn@>!2NFDA;JZ7Exo{aE}q^OI@4 zlW)xC?Xq**2chdX?sxy#z3*&Uejjw6`gZbddmr7~AS{O?0G7iM0L$?OuTn^~G($M+ P00000NkvXXu0mjf)%|N5 literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/wall.png b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/wall.png new file mode 100644 index 0000000000000000000000000000000000000000..a2c5c18d8515f21874ba3a084290de684c19c2d4 GIT binary patch literal 1706 zcmV;b237fqP)ZCVj^K{2kvreHzL^tx?L&`17^0 zBKwO~JIEy${KIc|B8RoOe{1{cnm^;9Un^r-mnHixl9$iAjZ6+kYvrAy=u3u1jg2+Q zN7X2{UjlqST(|EO-JZw1`gP+%FOd{XDhGCX{RT$p@-ug&SWNRBqfjw zL^~If?xAean=0um&)1|;$x1^2+pSfj@(sI-mrpx=+vj&v&t?3>Wwi8z{N{s@=Hhq; zx<_P1eF>R4U)rm~$S3Q_PH*;dXy;{qN8& z@Lk7EtpI@KqL#OI8gg=eC4)xQz#q^0vW@#IA(Y#fV*|bJ+(@CaH=kE6slRWm7^oX^ zq34%750Zr}%{6`@wmbyD*ZAZ?4zwDMPcCIwzk^G+s&DwR(-o1$hOw}rpEK4B{JxQ>L5`)S5Sg`jTT8fp0VCMO%lF@ z;Z}m+zu$jXA;C&Mqv%q%oDTqd3NSgWC-GdDyG~a`y>ON&OtrkF!g`_fyaZWbTM{~p zK_rkx_syC#RonHoW%=|agaSblvz{y$1xKmXHw^)gyFH&*+3_~m8HfBQ=q zpio$>kg>(}Q9XnLzKAm+!YabjQ(?rL#h!zcGvB%cdC^>z;!6?o;Bc$-#*P}c8=H|S z;Ejicd+-DVAzF3_Vxcv4>-ljK!6{9YU5zD21-xiB?H(UIfd`$q>R>*fDICeXqd8?_ zDFl^#;}b|fht=@mh4O6zWPX0C{xjz#Rd3P4#Q zNYs)grBO>Tym9%U0`FxRi&ZG4xGtd_2YPNYs#uOZQayS;k7bU$ZY$*0G;wQA#Z!&w z0XG^3+=}}Uz~$+sx?JD(OETgaNm|$VPrILw6vW=2@=X(UR!1#J1 zJ@o=38Oa#NfLMmU+&yU9{&;0^ZIvxJIQvWG(UNj+xS%;wSp1KFrwWPy9J?b*Iu}r) z@1mT~hXD)vhk>mb0i2eiHzllgT6NcMYeE&8c;}kY zJ*1a4BL+tYXK~<6M=y7EI-vNVAs>fUihu{g3En6kg8PdEJf-J@jWC14xM0r^ktYaa zl|&c9112=G-I$&qms|efqH7@J3KmjD^Wb;=@A7OVE3wKduu{`(XEh^F<1V*~l}*w8gH92_!!C)>&Yp;eE?<_&3F~_lCaq613+;n|EC#z^j>VNg3V4Fk>B(g;4Uwb}NebdCybVpKULU_DuSS Date: Thu, 2 Jul 2026 20:59:46 -0400 Subject: [PATCH 03/15] just fully do our own diona thing --- Resources/Prototypes/Body/Species/diona.yml | 6 +- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 76 +--------- .../_DV/Entities/Mobs/Player/diona.yml | 131 ++++++++++++++++++ 3 files changed, 140 insertions(+), 73 deletions(-) create mode 100644 Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml diff --git a/Resources/Prototypes/Body/Species/diona.yml b/Resources/Prototypes/Body/Species/diona.yml index 9a3d56d8c35..57de5df9118 100644 --- a/Resources/Prototypes/Body/Species/diona.yml +++ b/Resources/Prototypes/Body/Species/diona.yml @@ -344,7 +344,7 @@ entityPrototype: OrganDionaNymphStomach - type: entity - parent: MobDionaNymph + parent: DVMobDionaNymph # DeltaV - new diona mechanics id: OrganDionaNymphBrain suffix: brain components: @@ -361,7 +361,7 @@ - id: OrganAnimalKidneys - type: entity - parent: MobDionaNymph + parent: DVMobDionaNymph # DeltaV - new diona mechanics id: OrganDionaNymphStomach suffix: stomach components: @@ -377,7 +377,7 @@ - id: OrganAnimalKidneys - type: entity - parent: MobDionaNymph + parent: DVMobDionaNymph # DeltaV - new diona mechanics id: OrganDionaNymphLungs suffix: lungs components: diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 7d50088f553..20c23775e97 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -3844,63 +3844,16 @@ - type: entity name: diona nymph - parent: [BaseMobAnimal, SimpleMobBase, StripableInventoryBase, DVNodeCrawler] - id: MobDionaNymph + parent: [BaseMobAnimal, SimpleMobBase, StripableInventoryBase] + id: MobDionaNymphUnused # DeltaV - we don't use this prototype description: It's like a cat, only.... branch-ier. components: - # DeltaV changes begin - type: Sprite drawdepth: Mobs - sprite: _DV/Mobs/Species/Diona/gestalt.rsi layers: - map: ["enum.DamageStateVisualLayers.Base"] state: nymph - - state: eyes_nymph - shader: unshaded - - type: DVNymphLead - - type: DVNymphFollower - - type: HTN - rootTask: - task: DVNymphCompound - blackboard: - IdleRange: !type:Single - 2.5 - FollowCloseRange: !type:Single - 1.0 - FollowRange: !type:Single - 2.0 - - type: Clothing - quickEquip: false - sprite: _DV/Mobs/Species/Diona/gestalt.rsi - slots: - - HEAD - - OUTERCLOTHING - - type: Item - size: Normal - sprite: _DV/Mobs/Species/Diona/gestalt.rsi - inhandVisuals: - left: - - state: inhand-left - right: - - state: inhand-right - - type: GhostRole - makeSentient: true - allowSpeech: true - allowMovement: true - name: ghost-role-information-nymph-name - description: ghost-role-information-nymph-description - rules: ghost-role-information-nonantagonist-rules - - type: GhostTakeoverAvailable - - type: IntrinsicRadioReceiver - - type: IntrinsicRadioTransmitter - channels: - - Rootsong - - type: ActiveRadio - channels: - - Rootsong - - type: TypingIndicator - proto: diona - # DeltaV changes end + sprite: Mobs/Animals/nymph.rsi - type: Physics - type: Fixtures fixtures: @@ -3929,8 +3882,8 @@ Base: nymph_sleep Dead: Base: nymph_dead - - type: Butcherable - spawned: + - type: ToolRefinable + refineResult: - id: MaterialWoodPlank1 amount: 2 - type: InteractionPopup @@ -3971,27 +3924,10 @@ reformTime: 10 popupText: diona-reform-attempt reformPrototype: MobDionaReformed - - type: LightReactive # DeltaV - manual: true - - type: LightLevelHealth # DeltaV - darkThreshold: 0.4 - lightThreshold: 1.2 - darkDamage: - types: - Heat: 1.0 - lightDamage: - types: - Blunt: -0.1 - Piercing: -0.1 - Slash: -0.1 - Heat: -0.1 - Poison: -0.1 - Asphyxiation: -0.1 - darkMovementSpeedMultiplier: 0.7 - type: entity parent: MobDionaNymph - id: MobDionaNymphAccent # No talky. For non-brain & wild nymphs + id: MobDionaNymphAccentUnused # No talky. For non-brain & wild nymphs # DeltaV - we don't use this prototype suffix: Accent components: - type: ReplacementAccent diff --git a/Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml b/Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml new file mode 100644 index 00000000000..950c6604648 --- /dev/null +++ b/Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml @@ -0,0 +1,131 @@ +- type: entity + name: diona nymph + parent: [BaseMobAnimal, SimpleMobBase, StripableInventoryBase, DVNodeCrawler] + id: DVMobDionaNymph + description: It's a little skittery critter. Chirp. + components: + - type: Sprite + drawdepth: Mobs + sprite: _DV/Mobs/Species/Diona/gestalt.rsi + layers: + - map: ["enum.DamageStateVisualLayers.Base"] + state: nymph + - state: eyes_nymph + shader: unshaded + - type: DVNymphLead + - type: DVNymphFollower + - type: HTN + rootTask: + task: DVNymphCompound + blackboard: + IdleRange: !type:Single + 2.5 + FollowCloseRange: !type:Single + 1.0 + FollowRange: !type:Single + 2.0 + - type: Clothing + quickEquip: false + sprite: _DV/Mobs/Species/Diona/gestalt.rsi + slots: + - HEAD + - OUTERCLOTHING + - type: Item + size: Normal + sprite: _DV/Mobs/Species/Diona/gestalt.rsi + inhandVisuals: + left: + - state: inhand-left + right: + - state: inhand-right + - type: IntrinsicRadioReceiver + - type: IntrinsicRadioTransmitter + channels: + - Rootsong + - type: ActiveRadio + channels: + - Rootsong + - type: TypingIndicator + proto: diona + - type: Physics + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.35 + density: 100 # High, because wood is heavy. + mask: + - MobMask + layer: + - MobLayer + - type: Inventory + speciesId: cat + templateId: pet + - type: Bloodstream + bloodReferenceSolution: + reagents: + - ReagentId: Sap + Quantity: 60 + - type: DamageStateVisuals + states: + Alive: + Base: nymph + Critical: + Base: nymph_sleep + Dead: + Base: nymph_dead + - type: Butcherable + spawned: + - id: MaterialWoodPlank1 + amount: 2 + - type: InteractionPopup + successChance: 0.7 + interactSuccessString: petting-success-nymph + interactFailureString: petting-failure-nymph + interactSuccessSound: + path: /Audio/Animals/nymph_chirp.ogg + - type: MobThresholds + thresholds: + 0: Alive + 30: Critical + 60: Dead + - type: MovementSpeedModifier + baseWalkSpeed : 2.5 + baseSprintSpeed : 4.5 + - type: Grammar + attributes: + gender: epicene + - type: Speech + speechVerb: Plant + speechSounds: Alto + allowedEmotes: ['Chirp'] + - type: Vocal + sounds: + Male: UnisexDiona + Female: UnisexDiona + Unsexed: UnisexDiona + - type: Tag + tags: + - DoorBumpOpener + - VimPilot + - type: Emoting + - type: BodyEmotes + soundsId: Nymph + - type: LightReactive # DeltaV + manual: true + - type: LightLevelHealth # DeltaV + darkThreshold: 0.4 + lightThreshold: 1.2 + darkDamage: + types: + Heat: 1.0 + lightDamage: + types: + Blunt: -0.1 + Piercing: -0.1 + Slash: -0.1 + Heat: -0.1 + Poison: -0.1 + Asphyxiation: -0.1 + darkMovementSpeedMultiplier: 0.7 From f569ead6710d414e6c3bd5ed9d1c76db35b0634f Mon Sep 17 00:00:00 2001 From: Janet Blackquill Date: Fri, 3 Jul 2026 01:48:12 -0400 Subject: [PATCH 04/15] cheep --- Resources/Prototypes/Entities/Mobs/NPCs/animals.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 20c23775e97..6d4682b2b63 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -3843,6 +3843,7 @@ task: RuminantCompound - type: entity + abstract: true # DeltaV - we don't use this prototype name: diona nymph parent: [BaseMobAnimal, SimpleMobBase, StripableInventoryBase] id: MobDionaNymphUnused # DeltaV - we don't use this prototype @@ -3926,7 +3927,8 @@ reformPrototype: MobDionaReformed - type: entity - parent: MobDionaNymph + abstract: true # DeltaV - we don't use this prototype + parent: MobDionaNymphUnused # DeltaV - we don't use this prototype id: MobDionaNymphAccentUnused # No talky. For non-brain & wild nymphs # DeltaV - we don't use this prototype suffix: Accent components: From 2424d0b87f6c4461c7b7cedcb32955634011aea8 Mon Sep 17 00:00:00 2001 From: Janet Blackquill Date: Fri, 3 Jul 2026 15:00:23 -0400 Subject: [PATCH 05/15] churrr --- .../_DV/Diona/DVNymphingOrganSystem.cs | 37 ++- Content.Shared/Body/GibbableOrganComponent.cs | 9 +- Content.Shared/Body/GibbableOrganSystem.cs | 5 +- .../_DV/Diona/DVAssimilateNymphActionEvent.cs | 5 + .../_DV/Diona/DVGestaltComponent.cs | 26 +++ .../_DV/Diona/DVGestaltMemberComponent.cs | 10 + Content.Shared/_DV/Diona/DVGestaltSystem.cs | 216 ++++++++++++++++++ .../_DV/Diona/DVNymphMindMemoryComponent.cs | 10 + .../_DV/Diona/DVNymphProfileComponent.cs | 37 +++ .../_DV/Diona/DVNymphingBodySystem.cs | 22 +- .../Locale/en-US/_DV/armor/diona/names.ftl | 1 + Resources/Prototypes/Body/Species/diona.yml | 108 ++++++--- Resources/Prototypes/_DV/Actions/diona.yml | 39 ++++ .../_DV/Entities/Mobs/Player/diona.yml | 139 +++++++---- 14 files changed, 579 insertions(+), 85 deletions(-) create mode 100644 Content.Shared/_DV/Diona/DVAssimilateNymphActionEvent.cs create mode 100644 Content.Shared/_DV/Diona/DVGestaltComponent.cs create mode 100644 Content.Shared/_DV/Diona/DVGestaltMemberComponent.cs create mode 100644 Content.Shared/_DV/Diona/DVGestaltSystem.cs create mode 100644 Content.Shared/_DV/Diona/DVNymphMindMemoryComponent.cs create mode 100644 Content.Shared/_DV/Diona/DVNymphProfileComponent.cs create mode 100644 Resources/Locale/en-US/_DV/armor/diona/names.ftl create mode 100644 Resources/Prototypes/_DV/Actions/diona.yml diff --git a/Content.Server/_DV/Diona/DVNymphingOrganSystem.cs b/Content.Server/_DV/Diona/DVNymphingOrganSystem.cs index 757d03c2cc8..ce62b4b5f58 100644 --- a/Content.Server/_DV/Diona/DVNymphingOrganSystem.cs +++ b/Content.Server/_DV/Diona/DVNymphingOrganSystem.cs @@ -3,23 +3,29 @@ using Content.Server.Zombies; using Content.Shared._DV.Diona; using Content.Shared.Body; using Content.Shared.Gibbing; +using Content.Shared.Humanoid; +using Content.Shared.NameIdentifier; +using Content.Shared.NameModifier.EntitySystems; using Content.Shared.Species.Components; using Content.Shared.Zombies; using Robust.Shared.Prototypes; namespace Content.Server._DV.Diona; -public sealed class NymphSystem : EntitySystem +public sealed class NymphingOrganSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _protoManager = default!; [Dependency] private readonly MindSystem _mindSystem = default!; [Dependency] private readonly ZombieSystem _zombie = default!; + [Dependency] private readonly SharedVisualBodySystem _visualBody = default!; + [Dependency] private readonly NameModifierSystem _nameModifier = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent>(OnBeingGibbed); + SubscribeLocalEvent(OnRefreshNameModifiers); } private void OnBeingGibbed(Entity ent, ref BodyRelayedEvent args) @@ -41,8 +47,37 @@ public sealed class NymphSystem : EntitySystem if (ent.Comp.TransferMind && _mindSystem.TryGetMind(args.Body, out var mindId, out var mind)) _mindSystem.TransferTo(mindId, nymph, true, mind: mind); + if (TryComp(nymph, out var nymphProfile)) + { + nymphProfile.Name = Name(args.Body); + + if (TryComp(args.Body, out var bodyProfile)) + { + nymphProfile.Species = bodyProfile.Species; + nymphProfile.Gender = bodyProfile.Gender; + nymphProfile.Sex = bodyProfile.Sex; + nymphProfile.Age = bodyProfile.Age; + nymphProfile.Height = bodyProfile.Height; + } + + if (_visualBody.TryGatherMarkingsData(args.Body.Owner, null, out var profiles, out _, out var applied)) + { + nymphProfile.OrganMarkings = applied; + nymphProfile.OrganProfiles = profiles; + } + + Dirty(nymph, nymphProfile); + _nameModifier.RefreshNameModifiers(nymph); + } + // Delete the old organ QueueDel(ent); args.Args.Giblets.Add(nymph); } + + private void OnRefreshNameModifiers(Entity ent, ref RefreshNameModifiersEvent args) + { + if (ent.Comp.Name is { } name) + args.AddModifier("nymph-name-prefix", 0, ("identityName", name)); + } } diff --git a/Content.Shared/Body/GibbableOrganComponent.cs b/Content.Shared/Body/GibbableOrganComponent.cs index 0ac2025facb..d5d12f12ef4 100644 --- a/Content.Shared/Body/GibbableOrganComponent.cs +++ b/Content.Shared/Body/GibbableOrganComponent.cs @@ -9,4 +9,11 @@ namespace Content.Shared.Body; /// [RegisterComponent, NetworkedComponent] [Access(typeof(GibbableOrganSystem))] -public sealed partial class GibbableOrganComponent : Component; +public sealed partial class GibbableOrganComponent : Component +{ + /// + /// DeltaV - whether the organ will become a giblet + /// + [DataField] + public bool Active = true; +} diff --git a/Content.Shared/Body/GibbableOrganSystem.cs b/Content.Shared/Body/GibbableOrganSystem.cs index 56b0af11c19..eafd16658db 100644 --- a/Content.Shared/Body/GibbableOrganSystem.cs +++ b/Content.Shared/Body/GibbableOrganSystem.cs @@ -13,6 +13,9 @@ public sealed class GibbableOrganSystem : EntitySystem private void OnBeingGibbed(Entity ent, ref BodyRelayedEvent args) { - args.Args.Giblets.Add(ent); + // Begin DeltaV - gibbable activation + if (ent.Comp.Active) + args.Args.Giblets.Add(ent); + // End DeltaV - gibbable activation } } diff --git a/Content.Shared/_DV/Diona/DVAssimilateNymphActionEvent.cs b/Content.Shared/_DV/Diona/DVAssimilateNymphActionEvent.cs new file mode 100644 index 00000000000..b8cc279cd7c --- /dev/null +++ b/Content.Shared/_DV/Diona/DVAssimilateNymphActionEvent.cs @@ -0,0 +1,5 @@ +using Content.Shared.Actions; + +namespace Content.Shared._DV.Diona; + +public sealed partial class DVAssimilateNymphActionEvent : EntityTargetActionEvent; diff --git a/Content.Shared/_DV/Diona/DVGestaltComponent.cs b/Content.Shared/_DV/Diona/DVGestaltComponent.cs new file mode 100644 index 00000000000..adae5cbee1b --- /dev/null +++ b/Content.Shared/_DV/Diona/DVGestaltComponent.cs @@ -0,0 +1,26 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Diona; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(DVGestaltSystem))] +public sealed partial class DVGestaltComponent : Component +{ + [DataField(serverOnly: true)] + public EntityUid NymphStorageMap; + + [DataField(serverOnly: true)] + public HashSet StoredNymphs = new(); + + [DataField, AutoNetworkedField] + public int NymphCount; + + [DataField] + public int RequiredNymphs = 3; + + /// + /// The text that appears when attempting to split. + /// + [DataField] + public LocId PopupText = "diona-gib-action-use"; +} diff --git a/Content.Shared/_DV/Diona/DVGestaltMemberComponent.cs b/Content.Shared/_DV/Diona/DVGestaltMemberComponent.cs new file mode 100644 index 00000000000..12a9162a5d7 --- /dev/null +++ b/Content.Shared/_DV/Diona/DVGestaltMemberComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Diona; + +[RegisterComponent, NetworkedComponent] +public sealed partial class DVGestaltMemberComponent : Component +{ + [DataField] + public EntityUid? StoredInGestalt; +} diff --git a/Content.Shared/_DV/Diona/DVGestaltSystem.cs b/Content.Shared/_DV/Diona/DVGestaltSystem.cs new file mode 100644 index 00000000000..6b6b9f0485d --- /dev/null +++ b/Content.Shared/_DV/Diona/DVGestaltSystem.cs @@ -0,0 +1,216 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Numerics; +using Content.Shared.Body; +using Content.Shared.Gibbing; +using Content.Shared.Humanoid; +using Content.Shared.Mind; +using Content.Shared.Popups; +using Content.Shared.Preferences; +using Content.Shared.Species; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Network; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Diona; + +public sealed class DVGestaltSystem : EntitySystem +{ + [Dependency] private readonly SharedMapSystem _map = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly MetaDataSystem _metadata = default!; + [Dependency] private readonly HumanoidProfileSystem _profile = default!; + [Dependency] private readonly SharedVisualBodySystem _visualBody = default!; + + private static readonly EntProtoId GestaltPrototype = "DVMobDionaGestalt"; + private static readonly EntProtoId ReformedPrototype = "MobDionaReformed"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnNymphAssimilate); + + SubscribeLocalEvent(OnGestaltInit); + SubscribeLocalEvent(OnGestaltShutdown); + SubscribeLocalEvent(OnGestaltAssimilate); + SubscribeLocalEvent(OnGestaltGib); + SubscribeLocalEvent(OnGestaltBeingGibbed); + SubscribeLocalEvent(OnGestaltGibbedBeforeDeletion); + SubscribeLocalEvent(OnGestaltReforming); + } + + private void OnGestaltInit(Entity ent, ref MapInitEvent args) + { + var map = _map.CreateMap(runMapInit: false); + ent.Comp.NymphStorageMap = map; + _metadata.SetEntityName(map, $"Diona Gestalt Storage {ToPrettyString(ent)}"); + Dirty(ent); + } + + private void OnGestaltShutdown(Entity ent, ref ComponentShutdown args) + { + if (TryComp(ent.Comp.NymphStorageMap, out var map)) + _map.QueueDeleteMap(map.MapId); + } + + private void OnGestaltAssimilate(Entity ent, ref DVAssimilateNymphActionEvent args) + { + if (!_net.IsServer) + return; + + if (_mind.TryGetMind(args.Target, out _, out _)) + return; + + Assimilate(ent, args.Target); + args.Handled = true; + } + + private void OnNymphAssimilate(Entity ent, ref DVAssimilateNymphActionEvent args) + { + if (!_net.IsServer) + return; + + if (_mind.TryGetMind(args.Target, out _, out _)) + return; + + var gestalt = SpawnAtPosition(GestaltPrototype, Transform(ent).Coordinates); + var gestaltComp = Comp(gestalt); + Assimilate((gestalt, gestaltComp), ent.Owner); + Assimilate((gestalt, gestaltComp), args.Target); + + if (_mind.TryGetMind(ent, out var mindId, out var mind)) + _mind.TransferTo(mindId, gestalt, true, mind: mind); + + args.Handled = true; + } + + private void Assimilate(Entity gestalt, Entity nymph) + { + if (!Resolve(nymph, ref nymph.Comp) || !TryComp(gestalt.Comp.NymphStorageMap, out var map)) + return; + + _transform.SetMapCoordinates(nymph, new MapCoordinates(Vector2.Zero, map.MapId)); + gestalt.Comp.StoredNymphs.Add(nymph); + gestalt.Comp.NymphCount++; + nymph.Comp.StoredInGestalt = gestalt; + Dirty(nymph, nymph.Comp); + Dirty(gestalt); + } + + private void OnGestaltGib(Entity ent, ref GibActionSystem.GibActionEvent args) + { + _popup.PopupPredicted(Loc.GetString(ent.Comp.PopupText, ("name", ent)), ent, ent); + _gibbing.Gib(ent, user: args.Performer); + } + + private void OnGestaltBeingGibbed(Entity ent, ref BeingGibbedEvent args) + { + args.Giblets.UnionWith(ent.Comp.StoredNymphs); + } + + private void OnGestaltGibbedBeforeDeletion(Entity ent, ref GibbedBeforeDeletionEvent args) + { + foreach (var giblet in args.Giblets) + { + if (!TryComp(giblet, out var memory)) + continue; + + if (!Exists(memory.Mind) || !TryComp(memory.Mind, out var mindComponent)) + continue; + + _mind.TransferTo(memory.Mind.Value, giblet, true, mind: mindComponent); + } + } + + private bool DetermineProfile( + Entity ent, + [NotNullWhen(true)] out DVNymphProfileComponent? profile) + { + profile = null; + var nymphs = ent.Comp.StoredNymphs.ToList(); + if (nymphs.Count == 0) + return false; + + if (nymphs.Count == 1) + { + profile = Comp(nymphs[0]); + return true; + } + + var headProfile = Comp(nymphs[0]); + foreach (var nymph in nymphs.Skip(1)) + { + var nymphProfile = Comp(nymph); + + if (nymphProfile.Name != headProfile.Name + || nymphProfile.Species != headProfile.Species + || nymphProfile.Gender != headProfile.Gender + || nymphProfile.Sex != headProfile.Sex + || nymphProfile.Age != headProfile.Age + // ReSharper disable once CompareOfFloatsByEqualityOperator + || nymphProfile.Height != headProfile.Height) + { + return false; + } + } + + profile = headProfile; + return true; + } + + private void OnGestaltReforming(Entity ent, ref ReformSystem.ReformEvent args) + { + if (ent.Comp.NymphCount < ent.Comp.RequiredNymphs) + return; + + args.Handled = true; + if (!_net.IsServer) + return; + + var child = SpawnNextToOrDrop(ReformedPrototype, ent); + if (_mind.TryGetMind(ent, out var mindId, out var mind)) + _mind.TransferTo(mindId, child, mind: mind); + + if (DetermineProfile(ent, out var profile)) + { + if (profile.Name is { } name) + _metadata.SetEntityName(child, name); + + _profile.ApplyProfileTo(child, + new HumanoidCharacterProfile() + .WithSpecies(profile.Species) + .WithAge(profile.Age) + .WithSex(profile.Sex) + .WithHeight(profile.Height) + .WithGender(profile.Gender) + .WithHeight(profile.Height)); + + if (profile.OrganProfiles is { } profiles) + _visualBody.ApplyProfiles(child, profiles); + + if (profile.OrganMarkings is { } markings) + _visualBody.ApplyMarkings(child, markings); + } + + var newComp = CopyComp(ent, child, ent.Comp); + newComp.StoredNymphs.Clear(); + newComp.StoredNymphs.UnionWith(ent.Comp.StoredNymphs); + var newMap = Comp(newComp.NymphStorageMap); + foreach (var nymph in ent.Comp.StoredNymphs) + { + var gestaltMember = Comp(nymph); + gestaltMember.StoredInGestalt = child; + _transform.SetMapCoordinates(nymph, new MapCoordinates(Vector2.Zero, newMap.MapId)); + } + ent.Comp.StoredNymphs.Clear(); + _metadata.SetEntityName(ent.Comp.NymphStorageMap, $"Diona Gestalt Storage {ToPrettyString(ent)}"); + + QueueDel(ent); + } +} diff --git a/Content.Shared/_DV/Diona/DVNymphMindMemoryComponent.cs b/Content.Shared/_DV/Diona/DVNymphMindMemoryComponent.cs new file mode 100644 index 00000000000..3633bdf942d --- /dev/null +++ b/Content.Shared/_DV/Diona/DVNymphMindMemoryComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Diona; + +[RegisterComponent, NetworkedComponent] +public sealed partial class DVNymphMindMemoryComponent : Component +{ + [DataField(serverOnly: true)] + public EntityUid? Mind; +} diff --git a/Content.Shared/_DV/Diona/DVNymphProfileComponent.cs b/Content.Shared/_DV/Diona/DVNymphProfileComponent.cs new file mode 100644 index 00000000000..ed5fd72e840 --- /dev/null +++ b/Content.Shared/_DV/Diona/DVNymphProfileComponent.cs @@ -0,0 +1,37 @@ +using Content.Shared.Body; +using Content.Shared.Humanoid; +using Content.Shared.Humanoid.Markings; +using Content.Shared.Humanoid.Prototypes; +using Robust.Shared.Enums; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Diona; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class DVNymphProfileComponent : Component +{ + [DataField, AutoNetworkedField] + public Dictionary, OrganProfileData>? OrganProfiles; + + [DataField, AutoNetworkedField] + public Dictionary, Dictionary>>? OrganMarkings; + + [DataField, AutoNetworkedField] + public string? Name; + + [DataField, AutoNetworkedField] + public ProtoId Species; + + [DataField, AutoNetworkedField] + public Gender Gender; + + [DataField, AutoNetworkedField] + public Sex Sex; + + [DataField, AutoNetworkedField] + public int Age = 18; + + [DataField, AutoNetworkedField] + public float Height = 1f; +} diff --git a/Content.Shared/_DV/Diona/DVNymphingBodySystem.cs b/Content.Shared/_DV/Diona/DVNymphingBodySystem.cs index bcdf92d67e6..8d7dcbc5ffd 100644 --- a/Content.Shared/_DV/Diona/DVNymphingBodySystem.cs +++ b/Content.Shared/_DV/Diona/DVNymphingBodySystem.cs @@ -17,17 +17,23 @@ public sealed class DVNymphingBodySystem : EntitySystem base.Initialize(); SubscribeLocalEvent(OnNymphingBodyGib); + SubscribeLocalEvent(OnGibbedBeforeDeletion); } private void OnNymphingBodyGib(Entity ent, ref GibActionSystem.GibActionEvent args) { _popup.PopupPredicted(Loc.GetString(ent.Comp.PopupText, ("name", ent)), ent, ent); - var giblets = _gibbing.Gib(ent, user: args.Performer); - EntityUid? leadGiblet = null; + _gibbing.Gib(ent, user: args.Performer); + } - foreach (var giblet in giblets) + private void OnGibbedBeforeDeletion(Entity ent, ref GibbedBeforeDeletionEvent args) + { + EntityUid? leadGiblet = null; + var leadMind = EntityUid.Invalid; + + foreach (var giblet in args.Giblets) { - if (!_mind.TryGetMind(giblet, out var mindUid, out var mind)) + if (!_mind.TryGetMind(giblet, out leadMind, out _)) continue; leadGiblet = giblet; @@ -37,7 +43,12 @@ public sealed class DVNymphingBodySystem : EntitySystem if (leadGiblet is not { } leader || !TryComp(leader, out var leaderComp)) return; - foreach (var giblet in giblets) + if (TryComp(leader, out var memory) && Exists(leadMind)) + { + memory.Mind = leadMind; + } + + foreach (var giblet in args.Giblets) { if (!TryComp(giblet, out var follower)) continue; @@ -45,4 +56,5 @@ public sealed class DVNymphingBodySystem : EntitySystem _nymph.Follow((leader, leaderComp), (giblet, follower)); } } + } diff --git a/Resources/Locale/en-US/_DV/armor/diona/names.ftl b/Resources/Locale/en-US/_DV/armor/diona/names.ftl new file mode 100644 index 00000000000..d2afd694568 --- /dev/null +++ b/Resources/Locale/en-US/_DV/armor/diona/names.ftl @@ -0,0 +1 @@ +nymph-name-prefix = {$baseName} ({$identityName}) diff --git a/Resources/Prototypes/Body/Species/diona.yml b/Resources/Prototypes/Body/Species/diona.yml index 57de5df9118..5373171ee94 100644 --- a/Resources/Prototypes/Body/Species/diona.yml +++ b/Resources/Prototypes/Body/Species/diona.yml @@ -175,7 +175,7 @@ - type: DVNymphingBody - type: ActionGrant actions: - - DionaGibAction + - DVDionaGibAction # End DeltaV Changes - gib at will - type: Rootable - type: MovementSpeedModifier # DeltaV @@ -292,11 +292,20 @@ - type: entity parent: [ OrganBaseBrain, OrganDionaInternal ] id: OrganDionaBrain + # Begin DeltaV - diona nymphing overhaul + components: + - type: GibbableOrgan + active: false + # End DeltaV - diona nymphing overhaul - type: entity parent: [ OrganDionaVisual, OrganBaseEyes, OrganDionaInternal ] id: OrganDionaEyes components: + # Begin DeltaV - diona nymphing overhaul + - type: GibbableOrgan + active: false + # End DeltaV - diona nymphing overhaul - type: VisualOrgan data: sprite: Mobs/Customization/eyes.rsi @@ -306,6 +315,10 @@ parent: [ OrganBaseLungs, OrganDionaInternal, OrganDionaMetabolizer ] id: OrganDionaLungs components: + # Begin DeltaV - diona nymphing overhaul + - type: GibbableOrgan + active: false + # End DeltaV - diona nymphing overhaul - type: Sprite layers: - state: lungs @@ -314,6 +327,10 @@ parent: [ OrganBaseStomach, OrganDionaInternal, OrganDionaMetabolizer ] id: OrganDionaStomach components: + # Begin DeltaV - diona nymphing overhaul + - type: GibbableOrgan + active: false + # End DeltaV - diona nymphing overhaul - type: Metabolizer maxReagents: 6 stages: [ Digestion, Bloodstream, Metabolites ] @@ -323,6 +340,10 @@ suffix: "Diona, Nymphing" id: OrganDionaBrainNymphing components: + # Begin DeltaV - diona nymphing overhaul + - type: GibbableOrgan + active: true + # End DeltaV - diona nymphing overhaul - type: DVNymphingOrgan # DeltaV - new diona mechanics transferMind: true entityPrototype: OrganDionaNymphBrain @@ -332,6 +353,10 @@ suffix: "Diona, Nymphing" id: OrganDionaLungsNymphing components: + # Begin DeltaV - diona nymphing overhaul + - type: GibbableOrgan + active: true + # End DeltaV - diona nymphing overhaul - type: DVNymphingOrgan # DeltaV - new diona mechanics entityPrototype: OrganDionaNymphLungs @@ -340,6 +365,10 @@ suffix: "Diona, Nymphing" id: OrganDionaStomachNymphing components: + # Begin DeltaV - diona nymphing overhaul + - type: GibbableOrgan + active: true + # End DeltaV - diona nymphing overhaul - type: DVNymphingOrgan # DeltaV - new diona mechanics entityPrototype: OrganDionaNymphStomach @@ -349,16 +378,17 @@ suffix: brain components: - type: IsDeadIC - - type: EntityTableContainerFill - containers: - body_organs: !type:AllSelector - children: - - id: OrganDionaBrain - - id: OrganAnimalLungs - - id: OrganAnimalStomach - - id: OrganAnimalLiver - - id: OrganAnimalHeart - - id: OrganAnimalKidneys + # - type: EntityTableContainerFill + # containers: + # body_organs: !type:AllSelector + # children: + # - id: OrganDionaBrain + # - id: OrganAnimalLungs + # - id: OrganAnimalStomach + # - id: OrganAnimalLiver + # - id: OrganAnimalHeart + # - id: OrganAnimalKidneys + # End DeltaV - we have a uniform organ configuration - type: entity parent: DVMobDionaNymph # DeltaV - new diona mechanics @@ -366,15 +396,16 @@ suffix: stomach components: - type: IsDeadIC - - type: EntityTableContainerFill - containers: - body_organs: !type:AllSelector - children: - - id: OrganDionaLungs - - id: OrganAnimalStomach - - id: OrganAnimalLiver - - id: OrganAnimalHeart - - id: OrganAnimalKidneys + # - type: EntityTableContainerFill + # containers: + # body_organs: !type:AllSelector + # children: + # - id: OrganDionaLungs + # - id: OrganAnimalStomach + # - id: OrganAnimalLiver + # - id: OrganAnimalHeart + # - id: OrganAnimalKidneys + # End DeltaV - we have a uniform organ configuration - type: entity parent: DVMobDionaNymph # DeltaV - new diona mechanics @@ -382,15 +413,16 @@ suffix: lungs components: - type: IsDeadIC - - type: EntityTableContainerFill - containers: - body_organs: !type:AllSelector - children: - - id: OrganAnimalLungs - - id: OrganDionaStomach - - id: OrganAnimalLiver - - id: OrganAnimalHeart - - id: OrganAnimalKidneys + # - type: EntityTableContainerFill + # containers: + # body_organs: !type:AllSelector + # children: + # - id: OrganAnimalLungs + # - id: OrganDionaStomach + # - id: OrganAnimalLiver + # - id: OrganAnimalHeart + # - id: OrganAnimalKidneys + # End DeltaV - we have a uniform organ configuration - type: entity parent: MobDiona @@ -399,3 +431,21 @@ components: - type: IsDeadIC - type: RandomHumanoidAppearance + # Begin DeltaV - diona nymph overhaul + - type: InitialBody + organs: + Torso: OrganDionaTorso + Head: OrganDionaHead + ArmLeft: OrganDionaArmLeft + ArmRight: OrganDionaArmRight + HandRight: OrganDionaHandRight + HandLeft: OrganDionaHandLeft + LegLeft: OrganDionaLegLeft + LegRight: OrganDionaLegRight + FootLeft: OrganDionaFootLeft + FootRight: OrganDionaFootRight + Brain: OrganDionaBrain + Eyes: OrganDionaEyes + Lungs: OrganDionaLungs + Stomach: OrganDionaStomach + # End DeltaV - diona nymph overhaul \ No newline at end of file diff --git a/Resources/Prototypes/_DV/Actions/diona.yml b/Resources/Prototypes/_DV/Actions/diona.yml new file mode 100644 index 00000000000..d9c656979c2 --- /dev/null +++ b/Resources/Prototypes/_DV/Actions/diona.yml @@ -0,0 +1,39 @@ +- type: entity + parent: BaseAction + id: DVDionaAssimilateAction + name: Assimilate + description: Assimilate with another nymph. + components: + - type: Action + icon: + sprite: _DV/Mobs/Species/Diona/gestalt.rsi + state: gestalt + - type: TargetAction + - type: EntityTargetAction + event: !type:DVAssimilateNymphActionEvent + +- type: entity + parent: BaseSuicideAction + id: DVDionaGibAction + name: Split up + description: Dissolve your gestalt and split apart. + components: + - type: Action + icon: + sprite: _DV/Mobs/Species/Diona/gestalt.rsi + state: nymph + - type: InstantAction + event: !type:GibActionEvent + +- type: entity + parent: BaseAction + id: DVDionaReformAction + name: Assume Humanoid Form + description: Assume a humanoid form. Requires three nymphs. + components: + - type: Action + icon: + sprite: Mobs/Species/Diona/parts.rsi + state: full + - type: InstantAction + event: !type:ReformEvent diff --git a/Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml b/Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml index 950c6604648..e0802e27c00 100644 --- a/Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml +++ b/Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml @@ -1,7 +1,65 @@ - type: entity - name: diona nymph - parent: [BaseMobAnimal, SimpleMobBase, StripableInventoryBase, DVNodeCrawler] + abstract: true + id: DVDionaMixin + components: + - type: IntrinsicRadioReceiver + - type: IntrinsicRadioTransmitter + channels: + - Rootsong + - type: ActiveRadio + channels: + - Rootsong + - type: TypingIndicator + proto: diona + - type: Grammar + attributes: + gender: epicene + - type: Speech + speechVerb: Plant + speechSounds: Alto + allowedEmotes: ['Chirp'] + - type: Vocal + sounds: + Male: UnisexDiona + Female: UnisexDiona + Unsexed: UnisexDiona + - type: Emoting + - type: BodyEmotes + soundsId: Nymph + - type: LightReactive # DeltaV + manual: true + - type: LightLevelHealth # DeltaV + darkThreshold: 0.4 + lightThreshold: 1.2 + darkDamage: + types: + Heat: 1.0 + lightDamage: + types: + Blunt: -0.1 + Piercing: -0.1 + Slash: -0.1 + Heat: -0.1 + Poison: -0.1 + Asphyxiation: -0.1 + darkMovementSpeedMultiplier: 0.7 + - type: EntityTableContainerFill + containers: + body_organs: !type:AllSelector + children: + - id: OrganDionaBrain + - id: OrganDionaLungs + - id: OrganDionaStomach + - type: Bloodstream + bloodReferenceSolution: + reagents: + - ReagentId: Sap + Quantity: 60 + +- type: entity + parent: [DVDionaMixin, BaseMobAnimal, SimpleMobBase, StripableInventoryBase, DVNodeCrawler] id: DVMobDionaNymph + name: diona nymph description: It's a little skittery critter. Chirp. components: - type: Sprite @@ -14,6 +72,9 @@ shader: unshaded - type: DVNymphLead - type: DVNymphFollower + - type: DVNymphProfile + - type: DVGestaltMember + - type: DVNymphMindMemory - type: HTN rootTask: task: DVNymphCompound @@ -38,15 +99,6 @@ - state: inhand-left right: - state: inhand-right - - type: IntrinsicRadioReceiver - - type: IntrinsicRadioTransmitter - channels: - - Rootsong - - type: ActiveRadio - channels: - - Rootsong - - type: TypingIndicator - proto: diona - type: Physics - type: Fixtures fixtures: @@ -62,11 +114,6 @@ - type: Inventory speciesId: cat templateId: pet - - type: Bloodstream - bloodReferenceSolution: - reagents: - - ReagentId: Sap - Quantity: 60 - type: DamageStateVisuals states: Alive: @@ -93,39 +140,35 @@ - type: MovementSpeedModifier baseWalkSpeed : 2.5 baseSprintSpeed : 4.5 - - type: Grammar - attributes: - gender: epicene - - type: Speech - speechVerb: Plant - speechSounds: Alto - allowedEmotes: ['Chirp'] - - type: Vocal - sounds: - Male: UnisexDiona - Female: UnisexDiona - Unsexed: UnisexDiona - type: Tag tags: - DoorBumpOpener - VimPilot - - type: Emoting - - type: BodyEmotes - soundsId: Nymph - - type: LightReactive # DeltaV - manual: true - - type: LightLevelHealth # DeltaV - darkThreshold: 0.4 - lightThreshold: 1.2 - darkDamage: - types: - Heat: 1.0 - lightDamage: - types: - Blunt: -0.1 - Piercing: -0.1 - Slash: -0.1 - Heat: -0.1 - Poison: -0.1 - Asphyxiation: -0.1 - darkMovementSpeedMultiplier: 0.7 + - type: ActionGrant + actions: + - DVDionaAssimilateAction + +- type: entity + parent: [DVDionaMixin, BaseMobAnimal, SimpleMobBase] + id: DVMobDionaGestalt + name: nascent diona gestalt + description: A heaving mass of nymphs. Chirp? + components: + - type: DVGestalt + - type: ComplexInteraction + - type: MovementSpeedModifier + baseWalkSpeed : 1.0 + baseSprintSpeed : 1.0 + - type: Sprite + drawdepth: Mobs + sprite: _DV/Mobs/Species/Diona/gestalt.rsi + layers: + - map: ["enum.DamageStateVisualLayers.Base"] + state: gestalt + - state: eyes_gestalt + shader: unshaded + - type: ActionGrant + actions: + - DVDionaAssimilateAction + - DVDionaGibAction + - DVDionaReformAction From d451640188f31dcc81d0402be6fad7f7c1f60bc5 Mon Sep 17 00:00:00 2001 From: Janet Blackquill Date: Fri, 3 Jul 2026 15:01:43 -0400 Subject: [PATCH 06/15] robustness --- Content.Shared/_DV/Diona/DVGestaltSystem.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Content.Shared/_DV/Diona/DVGestaltSystem.cs b/Content.Shared/_DV/Diona/DVGestaltSystem.cs index 6b6b9f0485d..ba4a30cbaf7 100644 --- a/Content.Shared/_DV/Diona/DVGestaltSystem.cs +++ b/Content.Shared/_DV/Diona/DVGestaltSystem.cs @@ -38,6 +38,7 @@ public sealed class DVGestaltSystem : EntitySystem SubscribeLocalEvent(OnGestaltInit); SubscribeLocalEvent(OnGestaltShutdown); + SubscribeLocalEvent(OnGestaltMemberShutdown); SubscribeLocalEvent(OnGestaltAssimilate); SubscribeLocalEvent(OnGestaltGib); SubscribeLocalEvent(OnGestaltBeingGibbed); @@ -53,6 +54,16 @@ public sealed class DVGestaltSystem : EntitySystem Dirty(ent); } + private void OnGestaltMemberShutdown(Entity ent, ref ComponentShutdown args) + { + if (!TryComp(ent.Comp.StoredInGestalt, out var gestalt)) + return; + + gestalt.StoredNymphs.Remove(ent); + gestalt.NymphCount--; + Dirty(ent.Comp.StoredInGestalt.Value, gestalt); + } + private void OnGestaltShutdown(Entity ent, ref ComponentShutdown args) { if (TryComp(ent.Comp.NymphStorageMap, out var map)) From daf83541caeb016bcbd47edb87c4232fe81c240f Mon Sep 17 00:00:00 2001 From: Janet Blackquill Date: Fri, 3 Jul 2026 16:23:00 -0400 Subject: [PATCH 07/15] teeeests --- .../Tests/_DV/DVDionaTest.cs | 364 ++++++++++++++++++ 1 file changed, 364 insertions(+) create mode 100644 Content.IntegrationTests/Tests/_DV/DVDionaTest.cs diff --git a/Content.IntegrationTests/Tests/_DV/DVDionaTest.cs b/Content.IntegrationTests/Tests/_DV/DVDionaTest.cs new file mode 100644 index 00000000000..9398a6bb6ce --- /dev/null +++ b/Content.IntegrationTests/Tests/_DV/DVDionaTest.cs @@ -0,0 +1,364 @@ +#nullable enable +using System.Collections.Generic; +using System.Linq; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Content.Server.NPC; +using Content.Server.NPC.HTN; +using Content.Shared._DV.Diona; +using Content.Shared.Gibbing; +using Content.Shared.Humanoid; +using Content.Shared.Humanoid.Prototypes; +using Content.Shared.Mind; +using Content.Shared.Species; +using Robust.Shared.Enums; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Map.Components; +using Robust.Shared.Prototypes; + +namespace Content.IntegrationTests.Tests._DV; + +[TestFixture] +[TestOf(typeof(DVGestaltSystem))] +[TestOf(typeof(DVNymphingBodySystem))] +[TestOf(typeof(DVNymphRelationSystem))] +public sealed class DVDionaTest : GameTest +{ + private static readonly EntProtoId NymphPrototype = "DVMobDionaNymph"; + private static readonly EntProtoId ReformedPrototype = "MobDionaReformed"; + + private static readonly ProtoId Diona = "Diona"; + private static readonly Gender Gender = Gender.Epicene; + public static readonly Sex Sex = Sex.Unsexed; + public static readonly int Age = 72; + public static readonly float Height = 1f; + + [SidedDependency(Side.Server)] private readonly SharedMindSystem _mind = null!; + [SidedDependency(Side.Server)] private readonly DVNymphRelationSystem _relations = null!; + [SidedDependency(Side.Server)] private readonly GibbingSystem _gibbing = null!; + [SidedDependency(Side.Server)] private readonly MetaDataSystem _metadata = null!; + + [Test] + [RunOnSide(Side.Server)] + public void Assimilation() + { + var actor = SSpawn(NymphPrototype); + var mindlessTarget = SSpawn(NymphPrototype); + var mindedTarget = SSpawn(NymphPrototype); + + var actorMind = _mind.CreateMind(null); + _mind.TransferTo(actorMind, actor); + + var targetMind = _mind.CreateMind(null); + _mind.TransferTo(targetMind, mindedTarget); + + var assimilate = new DVAssimilateNymphActionEvent { Target = mindlessTarget }; + SEntMan.EventBus.RaiseLocalEvent(actor, assimilate); + Assert.That(assimilate.Handled, Is.True); + + var gestalt = GetSingleGestalt(); + var mapComp = SComp(gestalt.Comp.NymphStorageMap); + + using (Assert.EnterMultipleScope()) + { + Assert.That(gestalt.Comp.NymphCount, Is.EqualTo(2)); + Assert.That(gestalt.Comp.StoredNymphs, Is.EquivalentTo([actor, mindlessTarget])); + Assert.That(SComp(actor).StoredInGestalt, Is.EqualTo(gestalt.Owner)); + Assert.That(SComp(mindlessTarget).StoredInGestalt, Is.EqualTo(gestalt.Owner)); + Assert.That(SComp(actor).MapID, Is.EqualTo(mapComp.MapId)); + Assert.That(SComp(mindlessTarget).MapID, Is.EqualTo(mapComp.MapId)); + Assert.That(_mind.GetMind(gestalt), Is.EqualTo(actorMind.Owner)); + } + + var beforeCount = gestalt.Comp.NymphCount; + var beforeStored = gestalt.Comp.StoredNymphs.Count; + assimilate = new DVAssimilateNymphActionEvent { Target = mindedTarget }; + + SEntMan.EventBus.RaiseLocalEvent(gestalt, assimilate); + + using (Assert.EnterMultipleScope()) + { + Assert.That(assimilate.Handled, Is.False); + Assert.That(gestalt.Comp.NymphCount, Is.EqualTo(beforeCount)); + Assert.That(gestalt.Comp.StoredNymphs.Count, Is.EqualTo(beforeStored)); + Assert.That(gestalt.Comp.StoredNymphs, Is.EquivalentTo([actor, mindlessTarget])); + } + } + + [Test] + [RunOnSide(Side.Server)] + public void GestaltMembership() + { + var actor = SSpawn(NymphPrototype); + var target = SSpawn(NymphPrototype); + + var assimilate = new DVAssimilateNymphActionEvent { Target = target }; + SEntMan.EventBus.RaiseLocalEvent(actor, assimilate); + Assert.That(assimilate.Handled, Is.True); + + var gestalt = GetSingleGestalt(); + Assert.That(gestalt.Comp.NymphCount, Is.EqualTo(2)); + + SEntMan.RemoveComponent(target); + + using (Assert.EnterMultipleScope()) + { + Assert.That(gestalt.Comp.NymphCount, Is.EqualTo(1)); + Assert.That(gestalt.Comp.StoredNymphs, Is.EquivalentTo([actor])); + } + } + + [Test] + [RunOnSide(Side.Server)] + public void GestaltReformWithDifferentIdentities() + { + var first = SpawnProfiledNymph("Mismatched Rings"); + var second = SpawnProfiledNymph("Different Rings"); + var third = SpawnProfiledNymph("Mismatched Rings"); + + var assimilate = new DVAssimilateNymphActionEvent { Target = second }; + SEntMan.EventBus.RaiseLocalEvent(first, assimilate); + Assert.That(assimilate.Handled, Is.True); + + var gestalt = GetSingleGestalt(); + Assert.That(gestalt.Comp.NymphCount, Is.EqualTo(2)); + + assimilate = new DVAssimilateNymphActionEvent { Target = third }; + SEntMan.EventBus.RaiseLocalEvent(gestalt, assimilate); + Assert.That(assimilate.Handled, Is.True); + + var reform = new ReformSystem.ReformEvent(); + SEntMan.EventBus.RaiseLocalEvent(gestalt, reform); + Assert.That(reform.Handled, Is.True); + + var reformed = FindEntityByPrototype(ReformedPrototype); + Assert.That(reformed, Is.Not.Null); + + Assert.That(SComp(reformed.Value).EntityName, Is.Not.EqualTo("Mismatched Rings")); + Assert.That(SComp(reformed.Value).EntityName, Is.Not.EqualTo("Different Rings")); + } + + [Test] + [RunOnSide(Side.Server)] + public void GestaltReformWithSameIdentities() + { + var first = SpawnProfiledNymph("Identical Rings"); + var second = SpawnProfiledNymph("Identical Rings"); + var third = SpawnProfiledNymph("Identical Rings"); + + var firstMind = _mind.CreateMind(null); + _mind.TransferTo(firstMind, first); + + var assimilate = new DVAssimilateNymphActionEvent { Target = second }; + SEntMan.EventBus.RaiseLocalEvent(first, assimilate); + Assert.That(assimilate.Handled, Is.True); + + var gestalt = GetSingleGestalt(); + Assert.That(gestalt.Comp.NymphCount, Is.EqualTo(2)); + + assimilate = new DVAssimilateNymphActionEvent { Target = third }; + SEntMan.EventBus.RaiseLocalEvent(gestalt, assimilate); + Assert.That(assimilate.Handled, Is.True); + + var reform = new ReformSystem.ReformEvent(); + SEntMan.EventBus.RaiseLocalEvent(gestalt, reform); + Assert.That(reform.Handled, Is.True); + + var reformed = FindEntityByPrototype(ReformedPrototype); + Assert.That(reformed, Is.Not.Null); + + var profile = SComp(reformed.Value); + + Assert.Multiple(() => + { + Assert.That(_mind.GetMind(reformed.Value), Is.EqualTo(firstMind.Owner)); + Assert.That(SComp(reformed.Value).EntityName, Is.EqualTo("Identical Rings")); + Assert.That(profile.Species, Is.EqualTo(Diona)); + Assert.That(profile.Gender, Is.EqualTo(Gender)); + Assert.That(profile.Sex, Is.EqualTo(Sex)); + Assert.That(profile.Age, Is.EqualTo(Age)); + Assert.That(profile.Height, Is.EqualTo(Height)); + }); + } + + [Test] + [RunOnSide(Side.Server)] + public void GestaltMapPreservation([Range(1, 2)] int additionalNymphCount) + { + var first = SpawnProfiledNymph("Identical Rings"); + var others = new List(); + for (var i = 0; i < additionalNymphCount; i++) + { + others.Add(SpawnProfiledNymph("Identical Rings")); + } + + var second = SpawnProfiledNymph("Identical Rings"); + + var assimilate = new DVAssimilateNymphActionEvent { Target = second }; + SEntMan.EventBus.RaiseLocalEvent(first, assimilate); + Assert.That(assimilate.Handled, Is.True); + + var gestalt = GetSingleGestalt(); + + foreach (var other in others) + { + assimilate = new DVAssimilateNymphActionEvent { Target = other }; + SEntMan.EventBus.RaiseLocalEvent(gestalt, assimilate); + Assert.That(assimilate.Handled, Is.True); + } + + Assert.That(gestalt.Comp.NymphCount, Is.EqualTo(2 + additionalNymphCount)); + + var gestaltMap = SComp(gestalt.Comp.NymphStorageMap); + + foreach (var other in others) + { + Assert.That(SComp(other).MapID, Is.EqualTo(gestaltMap.MapId)); + } + + var reform = new ReformSystem.ReformEvent(); + SEntMan.EventBus.RaiseLocalEvent(gestalt, reform); + Assert.That(reform.Handled, Is.True); + + var reformed = FindEntityByPrototype(ReformedPrototype); + Assert.That(reformed, Is.Not.Null); + + var reformedGestalt = SComp(reformed.Value); + Assert.That(reformedGestalt.NymphCount, Is.EqualTo(2 + additionalNymphCount)); + + var reformedGestaltMap = SComp(reformedGestalt.NymphStorageMap); + + foreach (var other in others) + { + Assert.That(SComp(other).MapID, Is.EqualTo(reformedGestaltMap.MapId)); + } + } + + + [Test] + [RunOnSide(Side.Server)] + public void Relations() + { + var leader = SSpawn(NymphPrototype); + var follower = SSpawn(NymphPrototype); + + _relations.Follow( + (leader, SComp(leader)), + (follower, SComp(follower))); + + var leaderComp = SComp(leader); + var followerComp = SComp(follower); + var htn = SComp(follower); + + using (Assert.EnterMultipleScope()) + { + Assert.That(leaderComp.Followers, Does.Contain(follower)); + Assert.That(followerComp.Lead, Is.EqualTo(leader)); + Assert.That(htn.Blackboard.TryGetValue(NPCBlackboard.FollowTarget, out var coords, SEntMan), Is.True); + Assert.That(coords!.EntityId, Is.EqualTo(leader)); + } + + SEntMan.RemoveComponent(leader); + + using (Assert.EnterMultipleScope()) + { + Assert.That(followerComp.Lead, Is.Null); + Assert.That(htn.Blackboard.TryGetValue(NPCBlackboard.FollowTarget, out _, SEntMan), Is.False); + } + } + + [Test] + [RunOnSide(Side.Server)] + public void GibRelations() + { + var body = SSpawn("MobDiona"); + _metadata.SetEntityName(body, "Remembered Rings"); + + var bodyMind = _mind.CreateMind(null); + _mind.TransferTo(bodyMind, body); + + var giblets = _gibbing.Gib(body); + Assert.That(giblets.Count, Is.GreaterThanOrEqualTo(3)); + + var nymphs = giblets + .Where(SEntMan.EntityExists) + .Where(SEntMan.HasComponent) + .ToList(); + + Assert.That(nymphs.Count, Is.EqualTo(3)); + + var lead = nymphs.Single(uid => _mind.GetMind(uid) == bodyMind); + var leadComp = SComp(lead); + var memory = SComp(lead); + + using (Assert.EnterMultipleScope()) + { + Assert.That(memory.Mind, Is.EqualTo(bodyMind.Owner)); + Assert.That(leadComp.Followers, Is.EquivalentTo(nymphs)); + } + + foreach (var nymph in nymphs) + { + var profile = SComp(nymph); + var follower = SComp(nymph); + + using (Assert.EnterMultipleScope()) + { + Assert.That(profile.Name, Is.EqualTo("Remembered Rings")); + Assert.That(follower.Lead, Is.EqualTo(lead)); + } + } + } + + private EntityUid SpawnProfiledNymph(string name) + { + var uid = SSpawn(NymphPrototype); + var profile = SComp(uid); + + profile.Name = name; + profile.Species = Diona; + profile.Gender = Gender; + profile.Sex = Sex; + profile.Age = Age; + profile.Height = Height; + + return uid; + } + + private Entity GetSingleGestalt() + { + EntityUid foundUid = default; + DVGestaltComponent? foundComp = null; + var count = 0; + var query = SEntMan.EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out var comp)) + { + if (SEntMan.Deleted(uid)) + continue; + + foundUid = uid; + foundComp = comp; + count++; + } + + Assert.That(count, Is.EqualTo(1)); + return (foundUid, foundComp!); + } + + private EntityUid? FindEntityByPrototype(EntProtoId prototype) + { + var query = SEntMan.EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var meta)) + { + if (SEntMan.Deleted(uid)) + continue; + + if (meta.EntityPrototype?.ID == prototype.Id) + return uid; + } + + return null; + } +} From c1160522f431262cf8de44c7636040545cb8fa37 Mon Sep 17 00:00:00 2001 From: Janet Blackquill Date: Fri, 3 Jul 2026 18:31:09 -0400 Subject: [PATCH 08/15] cooled down --- .../_DV/Diona/DVNymphingOrganSystem.cs | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/Content.Server/_DV/Diona/DVNymphingOrganSystem.cs b/Content.Server/_DV/Diona/DVNymphingOrganSystem.cs index ce62b4b5f58..a4912515a45 100644 --- a/Content.Server/_DV/Diona/DVNymphingOrganSystem.cs +++ b/Content.Server/_DV/Diona/DVNymphingOrganSystem.cs @@ -1,9 +1,13 @@ using Content.Server.Mind; using Content.Server.Zombies; using Content.Shared._DV.Diona; +using Content.Shared.Actions; +using Content.Shared.Actions.Components; using Content.Shared.Body; using Content.Shared.Gibbing; using Content.Shared.Humanoid; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; using Content.Shared.NameIdentifier; using Content.Shared.NameModifier.EntitySystems; using Content.Shared.Species.Components; @@ -12,9 +16,14 @@ using Robust.Shared.Prototypes; namespace Content.Server._DV.Diona; -public sealed class NymphingOrganSystem : EntitySystem +public sealed class DVNymphingOrganSystem : EntitySystem { + private static readonly EntProtoId AssimilateAction = "DVDionaAssimilateAction"; + private static readonly TimeSpan AliveGibAssimilateCooldown = TimeSpan.FromSeconds(30); + private static readonly TimeSpan DeadGibAssimilateCooldown = TimeSpan.FromMinutes(10); + [Dependency] private readonly IPrototypeManager _protoManager = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly MindSystem _mindSystem = default!; [Dependency] private readonly ZombieSystem _zombie = default!; [Dependency] private readonly SharedVisualBodySystem _visualBody = default!; @@ -39,6 +48,7 @@ public sealed class NymphingOrganSystem : EntitySystem // Get the organs' position & spawn a nymph there var coords = Transform(ent).Coordinates; var nymph = SpawnAtPosition(entityProto.ID, coords); + SetAssimilateCooldown(nymph, GetAssimilateCooldown(args.Body)); if (HasComp(args.Body)) // Zombify the new nymph if old one is a zombie _zombie.ZombifyEntity(nymph); @@ -75,6 +85,25 @@ public sealed class NymphingOrganSystem : EntitySystem args.Args.Giblets.Add(nymph); } + private TimeSpan GetAssimilateCooldown(EntityUid body) + { + return TryComp(body, out var mobState) && mobState.CurrentState == MobState.Alive + ? AliveGibAssimilateCooldown + : DeadGibAssimilateCooldown; + } + + private void SetAssimilateCooldown(EntityUid nymph, TimeSpan cooldown) + { + foreach (var action in _actions.GetActions(nymph)) + { + if (Prototype(action.Owner)?.ID != AssimilateAction.Id) + continue; + + _actions.SetCooldown((action.Owner, action.Comp), cooldown); + return; + } + } + private void OnRefreshNameModifiers(Entity ent, ref RefreshNameModifiersEvent args) { if (ent.Comp.Name is { } name) From cc0ca75c101567c68dadf0628c25283b6f105efc Mon Sep 17 00:00:00 2001 From: Janet Blackquill Date: Fri, 3 Jul 2026 18:48:25 -0400 Subject: [PATCH 09/15] neck --- .../Prototypes/_DV/Entities/Mobs/Player/diona.yml | 2 +- ...equipped-OUTERCLOTHING.png => equipped-NECK.png} | Bin .../_DV/Mobs/Species/Diona/gestalt.rsi/meta.json | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/{equipped-OUTERCLOTHING.png => equipped-NECK.png} (100%) diff --git a/Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml b/Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml index e0802e27c00..b3b84d562b0 100644 --- a/Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml +++ b/Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml @@ -90,7 +90,7 @@ sprite: _DV/Mobs/Species/Diona/gestalt.rsi slots: - HEAD - - OUTERCLOTHING + - NECK - type: Item size: Normal sprite: _DV/Mobs/Species/Diona/gestalt.rsi diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/equipped-NECK.png similarity index 100% rename from Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/equipped-OUTERCLOTHING.png rename to Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/equipped-NECK.png diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/meta.json b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/meta.json index 821d80a06a3..f504002965a 100644 --- a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/meta.json +++ b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/meta.json @@ -289,7 +289,7 @@ "directions": 4 }, { - "name": "equipped-OUTERCLOTHING", + "name": "equipped-NECK", "directions": 4 }, { From 2d8c773719eefd26517fb5de11f0a1936fc005a9 Mon Sep 17 00:00:00 2001 From: Janet Blackquill Date: Sun, 5 Jul 2026 16:46:25 -0400 Subject: [PATCH 10/15] robust harvest spawns more nymphs now --- Resources/Prototypes/Reagents/botany.yml | 33 +++++++++++++++---- .../_DV/Entities/Mobs/Player/diona.yml | 14 ++++++++ 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/Resources/Prototypes/Reagents/botany.yml b/Resources/Prototypes/Reagents/botany.yml index 5330dfa528b..ce40448beef 100644 --- a/Resources/Prototypes/Reagents/botany.yml +++ b/Resources/Prototypes/Reagents/botany.yml @@ -146,14 +146,33 @@ Asphyxiation: 1 Heat: 2 Poison: 1 - - !type:Polymorph - prototype: TreeMorph + # Begin DeltaV changes - robust harvest OD makes more nymphs, not treemorph + - !type:SpawnEntity + entity: DVDionaNymphDeferredSpawn # deferred spawn to prevent crash from spawning a new metabolizer during metabolism conditions: - - !type:MetabolizerTypeCondition - type: [Plant] - - !type:ReagentCondition - reagent: RobustHarvest - min: 80 + - !type:MetabolizerTypeCondition + type: [Plant] + - !type:ReagentCondition + reagent: RobustHarvest + min: 40 + - !type:AdjustReagent + reagent: RobustHarvest + amount: -20 + conditions: + - !type:MetabolizerTypeCondition + type: [Plant] + - !type:ReagentCondition + reagent: RobustHarvest + min: 40 + # - !type:Polymorph + # prototype: TreeMorph + # conditions: + # - !type:MetabolizerTypeCondition + # type: [Plant] + # - !type:ReagentCondition + # reagent: RobustHarvest + # min: 80 + # End DeltaV changes - robust harvest OD makes more nymphs, not treemorph - type: reagent id: Sedin diff --git a/Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml b/Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml index b3b84d562b0..6b748db71ac 100644 --- a/Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml +++ b/Resources/Prototypes/_DV/Entities/Mobs/Player/diona.yml @@ -172,3 +172,17 @@ - DVDionaAssimilateAction - DVDionaGibAction - DVDionaReformAction + + +- type: entity + id: DVDionaNymphDeferredSpawn + categories: [ HideSpawnMenu ] + components: + - type: TimedSpawner + prototypes: + - DVMobDionaNymph + intervalSeconds: 1 + minimumEntitiesSpawned: 1 + maximumEntitiesSpawned: 1 + - type: TimedDespawn + lifetime: 2 From aab0fadd9d4293667652df84a4d5db91d1646fa8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 22:52:00 +0000 Subject: [PATCH 11/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../Mobs/Species/Diona/gestalt.rsi/meta.json | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/meta.json b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/meta.json index f504002965a..530af1b84dc 100644 --- a/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/meta.json +++ b/Resources/Textures/_DV/Mobs/Species/Diona/gestalt.rsi/meta.json @@ -285,20 +285,20 @@ "name": "hat" }, { - "name": "equipped-HELMET", - "directions": 4 + "name": "equipped-HELMET", + "directions": 4 }, { - "name": "equipped-NECK", - "directions": 4 + "name": "equipped-NECK", + "directions": 4 }, { - "name": "inhand-left", - "directions": 4 + "name": "inhand-left", + "directions": 4 }, { - "name": "inhand-right", - "directions": 4 + "name": "inhand-right", + "directions": 4 } ] -} \ No newline at end of file +} From c7e942c362ba2470dac7d6ebc3b093fb43f54807 Mon Sep 17 00:00:00 2001 From: Janet Blackquill Date: Sun, 5 Jul 2026 16:47:16 -0400 Subject: [PATCH 12/15] tests --- Content.IntegrationTests/Tests/_DV/DVDionaTest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Content.IntegrationTests/Tests/_DV/DVDionaTest.cs b/Content.IntegrationTests/Tests/_DV/DVDionaTest.cs index 9398a6bb6ce..35ef92d9935 100644 --- a/Content.IntegrationTests/Tests/_DV/DVDionaTest.cs +++ b/Content.IntegrationTests/Tests/_DV/DVDionaTest.cs @@ -184,7 +184,8 @@ public sealed class DVDionaTest : GameTest [Test] [RunOnSide(Side.Server)] - public void GestaltMapPreservation([Range(1, 2)] int additionalNymphCount) + [NonParallelizable] + public void GestaltMapPreservation([Range(1, 4)] int additionalNymphCount) { var first = SpawnProfiledNymph("Identical Rings"); var others = new List(); From a1a22a76ae31700c728c6f1c3117929d6389cc77 Mon Sep 17 00:00:00 2001 From: Janet Blackquill Date: Mon, 6 Jul 2026 10:07:49 -0400 Subject: [PATCH 13/15] eep --- Resources/Prototypes/_DV/Roles/GhostRoles/vent_critter.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/_DV/Roles/GhostRoles/vent_critter.yml b/Resources/Prototypes/_DV/Roles/GhostRoles/vent_critter.yml index 19d3f8b070c..4050e75b23f 100644 --- a/Resources/Prototypes/_DV/Roles/GhostRoles/vent_critter.yml +++ b/Resources/Prototypes/_DV/Roles/GhostRoles/vent_critter.yml @@ -34,6 +34,6 @@ - MindRoleGhostRoleFreeAgentHarmless - type: dvSpawnableGhostRole - id: MobDionaNymph - entity: MobDionaNymph + id: DVMobDionaNymph + entity: DVMobDionaNymph rules: ghost-role-information-nonantagonist-rules From 0649ef57aa7e65568ddcbdc0578cec5db1271d99 Mon Sep 17 00:00:00 2001 From: Janet Blackquill Date: Wed, 22 Jul 2026 13:18:08 -0400 Subject: [PATCH 14/15] more explicit cleanup --- .../Tests/_DV/DVDionaTest.cs | 49 ++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/Content.IntegrationTests/Tests/_DV/DVDionaTest.cs b/Content.IntegrationTests/Tests/_DV/DVDionaTest.cs index 35ef92d9935..989f9cfc310 100644 --- a/Content.IntegrationTests/Tests/_DV/DVDionaTest.cs +++ b/Content.IntegrationTests/Tests/_DV/DVDionaTest.cs @@ -43,6 +43,8 @@ public sealed class DVDionaTest : GameTest [RunOnSide(Side.Server)] public void Assimilation() { + using var cleanup = Cleanup(); + var actor = SSpawn(NymphPrototype); var mindlessTarget = SSpawn(NymphPrototype); var mindedTarget = SSpawn(NymphPrototype); @@ -90,6 +92,8 @@ public sealed class DVDionaTest : GameTest [RunOnSide(Side.Server)] public void GestaltMembership() { + using var cleanup = Cleanup(); + var actor = SSpawn(NymphPrototype); var target = SSpawn(NymphPrototype); @@ -113,6 +117,8 @@ public sealed class DVDionaTest : GameTest [RunOnSide(Side.Server)] public void GestaltReformWithDifferentIdentities() { + using var cleanup = Cleanup(); + var first = SpawnProfiledNymph("Mismatched Rings"); var second = SpawnProfiledNymph("Different Rings"); var third = SpawnProfiledNymph("Mismatched Rings"); @@ -143,6 +149,8 @@ public sealed class DVDionaTest : GameTest [RunOnSide(Side.Server)] public void GestaltReformWithSameIdentities() { + using var cleanup = Cleanup(); + var first = SpawnProfiledNymph("Identical Rings"); var second = SpawnProfiledNymph("Identical Rings"); var third = SpawnProfiledNymph("Identical Rings"); @@ -184,9 +192,10 @@ public sealed class DVDionaTest : GameTest [Test] [RunOnSide(Side.Server)] - [NonParallelizable] public void GestaltMapPreservation([Range(1, 4)] int additionalNymphCount) { + using var cleanup = Cleanup(); + var first = SpawnProfiledNymph("Identical Rings"); var others = new List(); for (var i = 0; i < additionalNymphCount; i++) @@ -241,6 +250,8 @@ public sealed class DVDionaTest : GameTest [RunOnSide(Side.Server)] public void Relations() { + using var cleanup = Cleanup(); + var leader = SSpawn(NymphPrototype); var follower = SSpawn(NymphPrototype); @@ -273,6 +284,8 @@ public sealed class DVDionaTest : GameTest [RunOnSide(Side.Server)] public void GibRelations() { + using var cleanup = Cleanup(); + var body = SSpawn("MobDiona"); _metadata.SetEntityName(body, "Remembered Rings"); @@ -327,6 +340,40 @@ public sealed class DVDionaTest : GameTest return uid; } + private sealed class ActionDisposable(Action action) : IDisposable + { + public void Dispose() + { + action.Invoke(); + } + } + + private ActionDisposable Cleanup() + { + return new ActionDisposable(() => + { + var gestalts = SEntMan.EntityQueryEnumerator(); + + while (gestalts.MoveNext(out var uid, out _)) + { + if (SEntMan.Deleted(uid)) + continue; + + SEntMan.QueueDeleteEntity(uid); + } + + var nymphs = SEntMan.EntityQueryEnumerator(); + + while (nymphs.MoveNext(out var uid, out _)) + { + if (SEntMan.Deleted(uid)) + continue; + + SEntMan.QueueDeleteEntity(uid); + } + }); + } + private Entity GetSingleGestalt() { EntityUid foundUid = default; From efdf22412be3f9e96b14fdcbdec31f82636acec9 Mon Sep 17 00:00:00 2001 From: Janet Blackquill Date: Wed, 22 Jul 2026 22:22:34 -0400 Subject: [PATCH 15/15] fix serialization --- Content.Shared/_DV/Diona/DVNymphProfileComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/_DV/Diona/DVNymphProfileComponent.cs b/Content.Shared/_DV/Diona/DVNymphProfileComponent.cs index ed5fd72e840..092e00a8685 100644 --- a/Content.Shared/_DV/Diona/DVNymphProfileComponent.cs +++ b/Content.Shared/_DV/Diona/DVNymphProfileComponent.cs @@ -21,7 +21,7 @@ public sealed partial class DVNymphProfileComponent : Component public string? Name; [DataField, AutoNetworkedField] - public ProtoId Species; + public ProtoId Species = "Diona"; [DataField, AutoNetworkedField] public Gender Gender;