mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-16 21:17:39 +03:00
Cocoon Cleanup & Minor Bloodsucker Tweaks (#1058)
# Description - Generalizes cocooning - Allows any mob to be cocooned - Cocoon bloodsucking moved to vampirism system - Any blood sucker can drink from cocoons - Vampirism no longer fails if bloodstream isn't normal blood, but gives a pop up - Vampirism `WebRequired` actually works in a way that makes sense - Adds cocooning and bloodsucker to all spider mobs + Arachnids resolves #978 --- # Changelog 🆑 - tweak: All spiders, arachne, and arachnids can cocoon mobs, and drink their blood. --------- Co-authored-by: VMSolidus <evilexecutive@gmail.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.Arachne;
|
||||
using Content.Shared.Cocoon;
|
||||
using Content.Shared.Humanoid;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
@@ -1,7 +1,6 @@
|
||||
using Content.Shared.Arachne;
|
||||
using Content.Shared.Cocoon;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Verbs;
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Eye.Blinding.Systems;
|
||||
@@ -10,29 +9,21 @@ using Content.Shared.Damage;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Server.Buckle.Systems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Vampiric;
|
||||
using Content.Server.Speech.Components;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Server.Console;
|
||||
using Content.Shared.Mobs.Components;
|
||||
|
||||
namespace Content.Server.Arachne
|
||||
namespace Content.Server.Cocoon
|
||||
{
|
||||
public sealed class ArachneSystem : EntitySystem
|
||||
public sealed class CocooningSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly BuckleSystem _buckleSystem = default!;
|
||||
[Dependency] private readonly ItemSlotsSystem _itemSlots = default!;
|
||||
[Dependency] private readonly BlindableSystem _blindableSystem = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||
|
||||
[Dependency] private readonly IServerConsoleHost _host = default!;
|
||||
[Dependency] private readonly BloodSuckerSystem _bloodSuckerSystem = default!;
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
|
||||
private const string BodySlot = "body_slot";
|
||||
@@ -40,27 +31,16 @@ namespace Content.Server.Arachne
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<ArachneComponent, GetVerbsEvent<InnateVerb>>(AddCocoonVerb);
|
||||
|
||||
SubscribeLocalEvent<CocoonerComponent, GetVerbsEvent<InnateVerb>>(AddCocoonVerb);
|
||||
SubscribeLocalEvent<CocoonComponent, EntInsertedIntoContainerMessage>(OnCocEntInserted);
|
||||
SubscribeLocalEvent<CocoonComponent, EntRemovedFromContainerMessage>(OnCocEntRemoved);
|
||||
SubscribeLocalEvent<CocoonComponent, DamageChangedEvent>(OnDamageChanged);
|
||||
SubscribeLocalEvent<CocoonComponent, GetVerbsEvent<AlternativeVerb>>(AddSuccVerb);
|
||||
SubscribeLocalEvent<ArachneComponent, ArachneCocoonDoAfterEvent>(OnCocoonDoAfter);
|
||||
SubscribeLocalEvent<CocoonerComponent, CocoonDoAfterEvent>(OnCocoonDoAfter);
|
||||
}
|
||||
|
||||
private void AddCocoonVerb(EntityUid uid, ArachneComponent component, GetVerbsEvent<InnateVerb> args)
|
||||
private void AddCocoonVerb(EntityUid uid, CocoonerComponent component, GetVerbsEvent<InnateVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
if (args.Target == uid)
|
||||
return;
|
||||
|
||||
if (!TryComp<BloodstreamComponent>(args.Target, out var bloodstream))
|
||||
return;
|
||||
|
||||
if (bloodstream.BloodReagent != component.WebBloodReagent)
|
||||
if (!args.CanAccess || !args.CanInteract || !HasComp<MobStateComponent>(args.Target))
|
||||
return;
|
||||
|
||||
InnateVerb verb = new()
|
||||
@@ -77,31 +57,23 @@ namespace Content.Server.Arachne
|
||||
|
||||
private void OnCocEntInserted(EntityUid uid, CocoonComponent component, EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
_blindableSystem.UpdateIsBlind(args.Entity);
|
||||
EnsureComp<StunnedComponent>(args.Entity);
|
||||
component.Victim = args.Entity;
|
||||
|
||||
if (TryComp<ReplacementAccentComponent>(args.Entity, out var currentAccent))
|
||||
{
|
||||
component.WasReplacementAccent = true;
|
||||
component.OldAccent = currentAccent.Accent;
|
||||
currentAccent.Accent = "mumble";
|
||||
} else
|
||||
{
|
||||
component.WasReplacementAccent = false;
|
||||
var replacement = EnsureComp<ReplacementAccentComponent>(args.Entity);
|
||||
replacement.Accent = "mumble";
|
||||
}
|
||||
|
||||
EnsureComp<ReplacementAccentComponent>(args.Entity).Accent = "mumble";
|
||||
EnsureComp<StunnedComponent>(args.Entity);
|
||||
|
||||
_blindableSystem.UpdateIsBlind(args.Entity);
|
||||
}
|
||||
|
||||
private void OnCocEntRemoved(EntityUid uid, CocoonComponent component, EntRemovedFromContainerMessage args)
|
||||
{
|
||||
if (component.WasReplacementAccent && TryComp<ReplacementAccentComponent>(args.Entity, out var replacement))
|
||||
{
|
||||
replacement.Accent = component.OldAccent;
|
||||
} else
|
||||
{
|
||||
if (TryComp<ReplacementAccentComponent>(args.Entity, out var replacement))
|
||||
replacement.Accent = component.OldAccent ?? replacement.Accent;
|
||||
else
|
||||
RemComp<ReplacementAccentComponent>(args.Entity);
|
||||
}
|
||||
|
||||
RemComp<StunnedComponent>(args.Entity);
|
||||
_blindableSystem.UpdateIsBlind(args.Entity);
|
||||
@@ -109,79 +81,24 @@ namespace Content.Server.Arachne
|
||||
|
||||
private void OnDamageChanged(EntityUid uid, CocoonComponent component, DamageChangedEvent args)
|
||||
{
|
||||
if (!args.DamageIncreased)
|
||||
return;
|
||||
|
||||
if (args.DamageDelta == null)
|
||||
return;
|
||||
|
||||
var body = _itemSlots.GetItemOrNull(uid, BodySlot);
|
||||
|
||||
if (body == null)
|
||||
if (!args.DamageIncreased || args.DamageDelta == null || component.Victim == null)
|
||||
return;
|
||||
|
||||
var damage = args.DamageDelta * component.DamagePassthrough;
|
||||
_damageableSystem.TryChangeDamage(body, damage);
|
||||
_damageableSystem.TryChangeDamage(component.Victim, damage);
|
||||
}
|
||||
|
||||
private void AddSuccVerb(EntityUid uid, CocoonComponent component, GetVerbsEvent<AlternativeVerb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract)
|
||||
return;
|
||||
|
||||
if (!TryComp<BloodSuckerComponent>(args.User, out var sucker))
|
||||
return;
|
||||
|
||||
if (!sucker.WebRequired)
|
||||
return;
|
||||
|
||||
var victim = _itemSlots.GetItemOrNull(uid, BodySlot);
|
||||
|
||||
if (victim == null)
|
||||
return;
|
||||
|
||||
if (!TryComp<BloodstreamComponent>(victim, out var stream))
|
||||
return;
|
||||
|
||||
AlternativeVerb verb = new()
|
||||
{
|
||||
Act = () =>
|
||||
{
|
||||
_bloodSuckerSystem.StartSuccDoAfter(args.User, victim.Value, sucker, stream, false); // start doafter
|
||||
},
|
||||
Text = Loc.GetString("action-name-suck-blood"),
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Nyanotrasen/Icons/verbiconfangs.png")),
|
||||
Priority = 2
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
private void OnEntRemoved(EntityUid uid, WebComponent web, EntRemovedFromContainerMessage args)
|
||||
{
|
||||
if (!TryComp<StrapComponent>(uid, out var strap))
|
||||
return;
|
||||
|
||||
if (HasComp<ArachneComponent>(args.Entity))
|
||||
_buckleSystem.StrapSetEnabled(uid, false, strap);
|
||||
}
|
||||
|
||||
private void StartCocooning(EntityUid uid, ArachneComponent component, EntityUid target)
|
||||
private void StartCocooning(EntityUid uid, CocoonerComponent component, EntityUid target)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("cocoon-start-third-person", ("target", Identity.Entity(target, EntityManager)), ("spider", Identity.Entity(uid, EntityManager))), uid,
|
||||
Shared.Popups.PopupType.MediumCaution);
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("cocoon-start-second-person", ("target", Identity.Entity(target, EntityManager))), uid, uid, Shared.Popups.PopupType.Medium);
|
||||
|
||||
var delay = component.CocoonDelay;
|
||||
|
||||
if (HasComp<KnockedDownComponent>(target))
|
||||
delay *= component.CocoonKnockdownMultiplier;
|
||||
|
||||
// Is it good practice to use empty data just to disambiguate doafters
|
||||
// Who knows, there's no docs!
|
||||
var ev = new ArachneCocoonDoAfterEvent();
|
||||
|
||||
var args = new DoAfterArgs(EntityManager, uid, delay, ev, uid, target: target)
|
||||
var args = new DoAfterArgs(EntityManager, uid, delay, new CocoonDoAfterEvent(), uid, target: target)
|
||||
{
|
||||
BreakOnUserMove = true,
|
||||
BreakOnTargetMove = true,
|
||||
@@ -190,7 +107,7 @@ namespace Content.Server.Arachne
|
||||
_doAfter.TryStartDoAfter(args);
|
||||
}
|
||||
|
||||
private void OnCocoonDoAfter(EntityUid uid, ArachneComponent component, ArachneCocoonDoAfterEvent args)
|
||||
private void OnCocoonDoAfter(EntityUid uid, CocoonerComponent component, CocoonDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled || args.Cancelled || args.Args.Target == null)
|
||||
return;
|
||||
@@ -6,6 +6,7 @@ using Content.Shared.Interaction;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Vampiric;
|
||||
using Content.Shared.Cocoon;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Body.Systems;
|
||||
@@ -45,23 +46,28 @@ namespace Content.Server.Vampiric
|
||||
|
||||
private void AddSuccVerb(EntityUid uid, BloodSuckerComponent component, GetVerbsEvent<InnateVerb> args)
|
||||
{
|
||||
if (args.User == args.Target)
|
||||
|
||||
var victim = args.Target;
|
||||
var ignoreClothes = false;
|
||||
|
||||
if (TryComp<CocoonComponent>(args.Target, out var cocoon))
|
||||
{
|
||||
victim = cocoon.Victim ?? args.Target;
|
||||
ignoreClothes = cocoon.Victim != null;
|
||||
} else if (component.WebRequired)
|
||||
return;
|
||||
if (component.WebRequired)
|
||||
return; // handled elsewhere
|
||||
if (!TryComp<BloodstreamComponent>(args.Target, out var bloodstream))
|
||||
return;
|
||||
if (!args.CanAccess)
|
||||
|
||||
if (!TryComp<BloodstreamComponent>(victim, out var bloodstream) || args.User == victim || !args.CanAccess)
|
||||
return;
|
||||
|
||||
InnateVerb verb = new()
|
||||
{
|
||||
Act = () =>
|
||||
{
|
||||
StartSuccDoAfter(uid, args.Target, component, bloodstream); // start doafter
|
||||
StartSuccDoAfter(uid, victim, component, bloodstream, !ignoreClothes); // start doafter
|
||||
},
|
||||
Text = Loc.GetString("action-name-suck-blood"),
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Nyanotrasen/Icons/verbiconfangs.png")),
|
||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Nyanotrasen/Icons/verbiconfangs.png")),
|
||||
Priority = 2
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
@@ -80,10 +86,8 @@ namespace Content.Server.Vampiric
|
||||
|
||||
if (_prototypeManager.TryIndex<DamageGroupPrototype>("Brute", out var brute) && args.Damageable.Damage.TryGetDamageInGroup(brute, out var bruteTotal)
|
||||
&& _prototypeManager.TryIndex<DamageGroupPrototype>("Airloss", out var airloss) && args.Damageable.Damage.TryGetDamageInGroup(airloss, out var airlossTotal))
|
||||
{
|
||||
if (bruteTotal == 0 && airlossTotal == 0)
|
||||
RemComp<BloodSuckedComponent>(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDoAfter(EntityUid uid, BloodSuckerComponent component, BloodSuckDoAfterEvent args)
|
||||
@@ -96,18 +100,13 @@ namespace Content.Server.Vampiric
|
||||
|
||||
public void StartSuccDoAfter(EntityUid bloodsucker, EntityUid victim, BloodSuckerComponent? bloodSuckerComponent = null, BloodstreamComponent? stream = null, bool doChecks = true)
|
||||
{
|
||||
if (!Resolve(bloodsucker, ref bloodSuckerComponent))
|
||||
return;
|
||||
|
||||
if (!Resolve(victim, ref stream))
|
||||
if (!Resolve(bloodsucker, ref bloodSuckerComponent) || !Resolve(victim, ref stream))
|
||||
return;
|
||||
|
||||
if (doChecks)
|
||||
{
|
||||
if (!_interactionSystem.InRangeUnobstructed(bloodsucker, victim))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_inventorySystem.TryGetSlotEntity(victim, "head", out var headUid) && HasComp<PressureProtectionComponent>(headUid))
|
||||
{
|
||||
@@ -125,19 +124,15 @@ namespace Content.Server.Vampiric
|
||||
}
|
||||
|
||||
if (stream.BloodReagent != "Blood")
|
||||
{
|
||||
_popups.PopupEntity(Loc.GetString("bloodsucker-fail-not-blood", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_solutionSystem.PercentFull(stream.Owner) != 0)
|
||||
_popups.PopupEntity(Loc.GetString("bloodsucker-not-blood", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium);
|
||||
else if (_solutionSystem.PercentFull(victim) != 0)
|
||||
_popups.PopupEntity(Loc.GetString("bloodsucker-fail-no-blood", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium);
|
||||
else
|
||||
_popups.PopupEntity(Loc.GetString("bloodsucker-doafter-start", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium);
|
||||
|
||||
_popups.PopupEntity(Loc.GetString("bloodsucker-doafter-start-victim", ("sucker", bloodsucker)), victim, victim, Shared.Popups.PopupType.LargeCaution);
|
||||
_popups.PopupEntity(Loc.GetString("bloodsucker-doafter-start", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium);
|
||||
|
||||
var ev = new BloodSuckDoAfterEvent();
|
||||
var args = new DoAfterArgs(EntityManager, bloodsucker, bloodSuckerComponent.Delay, ev, bloodsucker, target: victim)
|
||||
var args = new DoAfterArgs(EntityManager, bloodsucker, bloodSuckerComponent.Delay, new BloodSuckDoAfterEvent(), bloodsucker, target: victim)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = false,
|
||||
@@ -206,8 +201,5 @@ namespace Content.Server.Vampiric
|
||||
//}
|
||||
return true;
|
||||
}
|
||||
|
||||
private record struct BloodSuckData()
|
||||
{}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Arachne
|
||||
{
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class ArachneComponent : Component
|
||||
{
|
||||
[DataField("cocoonDelay")]
|
||||
public float CocoonDelay = 12f;
|
||||
|
||||
[DataField("cocoonKnockdownMultiplier")]
|
||||
public float CocoonKnockdownMultiplier = 0.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Blood reagent required to web up a mob.
|
||||
/// </summary>
|
||||
|
||||
[DataField("webBloodReagent")]
|
||||
public string WebBloodReagent = "Blood";
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization;
|
||||
using Content.Shared.DoAfter;
|
||||
|
||||
namespace Content.Shared.Arachne
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class ArachneCocoonDoAfterEvent : SimpleDoAfterEvent
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Arachne
|
||||
{
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class WebComponent : Component
|
||||
{}
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
namespace Content.Shared.Arachne
|
||||
namespace Content.Shared.Cocoon
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed partial class CocoonComponent : Component
|
||||
{
|
||||
public bool WasReplacementAccent = false;
|
||||
public string? OldAccent;
|
||||
|
||||
public string OldAccent = "";
|
||||
public EntityUid? Victim;
|
||||
|
||||
[DataField("damagePassthrough")]
|
||||
public float DamagePassthrough = 0.5f;
|
||||
|
||||
}
|
||||
}
|
||||
10
Content.Shared/Cocoon/CocoonDoAfterEvent.cs
Normal file
10
Content.Shared/Cocoon/CocoonDoAfterEvent.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Robust.Shared.Serialization;
|
||||
using Content.Shared.DoAfter;
|
||||
|
||||
namespace Content.Shared.Cocoon
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class CocoonDoAfterEvent : SimpleDoAfterEvent
|
||||
{
|
||||
}
|
||||
}
|
||||
14
Content.Shared/Cocoon/CocoonerComponent.cs
Normal file
14
Content.Shared/Cocoon/CocoonerComponent.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Robust.Shared.GameStates;
|
||||
|
||||
namespace Content.Shared.Cocoon
|
||||
{
|
||||
[RegisterComponent, NetworkedComponent]
|
||||
public sealed partial class CocoonerComponent : Component
|
||||
{
|
||||
[DataField("cocoonDelay")]
|
||||
public float CocoonDelay = 12f;
|
||||
|
||||
[DataField("cocoonKnockdownMultiplier")]
|
||||
public float CocoonKnockdownMultiplier = 0.5f;
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@ action-description-suck-blood = Suck the blood of the victim in your hand.
|
||||
bloodsucker-fail-helmet = You'd need to remove {THE($helmet)}.
|
||||
bloodsucker-fail-mask = You'd need to remove your mask!
|
||||
|
||||
bloodsucker-fail-not-blood = { CAPITALIZE(SUBJECT($target)) } doesn't have delicious, nourishing mortal blood.
|
||||
bloodsucker-fail-no-blood = { CAPITALIZE(SUBJECT($target)) } has no blood in { POSS-ADJ($target) } body.
|
||||
bloodsucker-fail-no-blood-bloodsucked = { CAPITALIZE(SUBJECT($target)) } has been sucked dry.
|
||||
bloodsucker-not-blood = {$target} doesn't have delicious, nourishing blood.
|
||||
bloodsucker-fail-no-blood = {$target} has no blood in { POSS-ADJ($target) } body.
|
||||
bloodsucker-fail-no-blood-bloodsucked = {$target} has been sucked dry.
|
||||
|
||||
bloodsucker-blood-sucked = You suck some blood from {$target}.
|
||||
bloodsucker-doafter-start = You try to suck blood from {$target}.
|
||||
|
||||
@@ -2357,6 +2357,9 @@
|
||||
- type: RandomBark
|
||||
barkType: hissing
|
||||
barkMultiplier: 0.3
|
||||
- type: BloodSucker
|
||||
webRequired: true
|
||||
- type: Cocooner
|
||||
|
||||
- type: entity
|
||||
name: tarantula
|
||||
|
||||
@@ -277,6 +277,9 @@
|
||||
Unsexed: UnisexArachnid
|
||||
- type: TypingIndicator
|
||||
proto: spider
|
||||
- type: BloodSucker
|
||||
webRequired: true
|
||||
- type: Cocooner
|
||||
|
||||
- type: entity
|
||||
id: MobSpiderSpaceSalvage
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
bloodRegenerationThirst: 4 # 1 unit of demon's blood satiates 4 thirst
|
||||
- type: BloodSucker
|
||||
webRequired: true
|
||||
- type: Arachne
|
||||
- type: Cocooner
|
||||
- type: DamageVisuals
|
||||
targetLayers:
|
||||
- "enum.HumanoidVisualLayers.Chest"
|
||||
|
||||
@@ -44,6 +44,9 @@
|
||||
methods: [Touch]
|
||||
effects:
|
||||
- !type:WashCreamPieReaction
|
||||
- type: BloodSucker
|
||||
webRequired: true
|
||||
- type: Cocooner
|
||||
# Damage (Self)
|
||||
- type: Bloodstream
|
||||
bloodReagent: CopperBlood
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
# path: /Audio/Animals/snake_hiss.ogg
|
||||
# - type: Puller
|
||||
# needsHands: false
|
||||
# - type: Arachne
|
||||
# - type: Cocooner
|
||||
# cocoonDelay: 8
|
||||
# - type: SolutionContainerManager
|
||||
# solutions:
|
||||
|
||||
Reference in New Issue
Block a user