Start slug stasis after taking a corpse (#46138)

yay
This commit is contained in:
ScarKy0 2026-09-23 13:19:14 +00:00 • committed by GitHub
parent 42f0073a71
commit 040d28fb9d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 50 additions and 9 deletions

View file

@ -35,10 +35,13 @@ public sealed partial class ServerChangelingLastResortSystem : ChangelingLastRes
args.Handled = true;
Audio.PlayPvs(ent.Comp.Sound, ent.Owner);
_popup.PopupEntity(Loc.GetString("changeling-takeover-start-others", ("user", Identity.Entity(ent.Owner, EntityManager))),
_popup.PopupEntity(Loc.GetString("changeling-takeover-start-others",
("user", Identity.Entity(ent.Owner, EntityManager))),
ent.Owner,
PopupType.MediumCaution);
var examine = Loc.GetString("changeling-takeover-doafter", ("user", Identity.Entity(ent, EntityManager)), ("target", Identity.Entity(args.Target, EntityManager)));
var doAfter = new DoAfterArgs(EntityManager,
ent.Owner,
ent.Comp.TakeOverDuration,
@ -50,6 +53,7 @@ public sealed partial class ServerChangelingLastResortSystem : ChangelingLastRes
BreakOnMove = true,
DuplicateCondition = DuplicateConditions.None,
RequireCanInteract = false,
ExamineText = examine
};
_doAfter.TryStartDoAfter(doAfter);
@ -92,17 +96,21 @@ public sealed partial class ServerChangelingLastResortSystem : ChangelingLastRes
if (!_mind.TryGetMind(args.User, out var mindId, out var mind))
return;
TakeOverCorpse(args.User, target, mindId, mind);
_popup.PopupEntity(Loc.GetString("changeling-takeover-success-self"), target, target, PopupType.Large);
TakeOverCorpse(ent, target, mindId, mind);
}
private void TakeOverCorpse(EntityUid user, EntityUid target, EntityUid mindId, MindComponent mind)
private void TakeOverCorpse(Entity<ChangelingSlugComponent> ent,
EntityUid target,
EntityUid mindId,
MindComponent mind)
{
_mind.TransferTo(mindId, target, mind: mind);
_antag.AssignAntagComponents(target, ChangelingAntag);
QueueDel(user);
var ev = new BodyTakenByHeadslugEvent(target, ent.Comp.AutoStasis, ent.Comp.AutoStasisDurationMultiplier);
RaiseLocalEvent(target, ref ev);
QueueDel(ent);
}
}

View file

@ -1,4 +1,5 @@
using Content.Shared.Actions.Components;
using Content.Shared.Changeling.Systems;
using Content.Shared.Ghost;
using Content.Shared.Ghost.Systems;
using Content.Shared.Mobs;
@ -9,6 +10,7 @@ public abstract partial class SharedActionsSystem
{
private void InitializeRelay()
{
SubscribeLocalEvent<ActionsComponent, BodyTakenByHeadslugEvent>(RefRelayActionEvent);
SubscribeLocalEvent<ActionsComponent, MobStateChangedEvent>(RefRelayActionEvent);
SubscribeLocalEvent<ActionsComponent, GhostAttemptEvent>(RefRelayActionEvent);
}

View file

@ -31,6 +31,18 @@ public sealed partial class ChangelingSlugComponent : Component
[DataField]
public TimeSpan TakeOverDuration = TimeSpan.FromSeconds(5);
/// <summary>
/// Whether the body should automatically enter regen stasis after being taken over.
/// </summary>
[DataField]
public bool AutoStasis = true;
/// <summary>
/// The multiplier for the duration of the automatic stasis.
/// </summary>
[DataField]
public float AutoStasisDurationMultiplier = 1f;
/// <summary>
/// The sound to play when starting the takeover.
/// </summary>

View file

@ -52,3 +52,11 @@ public abstract partial class ChangelingLastResortSystem : EntitySystem
_gibbing.Gib(args.Performer);
}
}
/// <summary>
/// Raised on a body after a headslug takes it over.
/// </summary>
/// <param name="StartStasis">Whether the body should instantly start regen stasis after being taken over.</param>
/// <param name="StasisDurationMultiplier">Time multiplier for the regen stasis, if one is started.</param>
[ByRefEvent]
public record struct BodyTakenByHeadslugEvent(EntityUid Target, bool StartStasis = true, float StasisDurationMultiplier = 1f);

View file

@ -62,10 +62,17 @@ public sealed partial class RegenerativeStasisSystem : EntitySystem
EnterStasis((ent, ent.Comp), args.Performer);
}
[SubscribeLocalEvent]
private void OnHeadslugTookBody(Entity<RegenerativeStasisActionComponent> ent, ref ActionRelayedEvent<BodyTakenByHeadslugEvent> args)
{
if (args.Args.StartStasis)
EnterStasis(ent.AsNullable(), args.Args.Target, args.Args.StasisDurationMultiplier);
}
/// <summary>
/// Enter the stasis and set the action cooldown depending on the damage you have taken.
/// </summary>
public void EnterStasis(Entity<RegenerativeStasisActionComponent?> ent, EntityUid target)
public void EnterStasis(Entity<RegenerativeStasisActionComponent?> ent, EntityUid target, float durationMultiplier = 1f)
{
if (!Resolve(ent.Owner, ref ent.Comp))
return;
@ -92,7 +99,8 @@ public sealed partial class RegenerativeStasisSystem : EntitySystem
var stasisDuration = ent.Comp.MinStasisCooldown;
stasisDuration += ent.Comp.BonusCooldownPerDamage * (double)_damage.GetTotalDamage(target);
stasisDuration = new TimeSpan(Math.Clamp(stasisDuration.Ticks, ent.Comp.MinStasisCooldown.Ticks, ent.Comp.MaxStasisCooldown.Ticks)); // No clamp method for TimeSpans
var calculatedDuration = (long)(Math.Clamp(stasisDuration.Ticks, ent.Comp.MinStasisCooldown.Ticks, ent.Comp.MaxStasisCooldown.Ticks) * durationMultiplier); // No clamp method for TimeSpans
stasisDuration = new TimeSpan(calculatedDuration);
_metaData.SetEntityName(ent, Loc.GetString("changeling-stasis-active-name"));
_metaData.SetEntityDescription(ent, Loc.GetString("changeling-stasis-active-desc"));
@ -160,6 +168,7 @@ public sealed partial class RegenerativeStasisSystem : EntitySystem
_metaData.SetEntityDescription(ent, ent.Comp.InitialDescription);
_actions.SetToggled(ent.Owner, ent.Comp.IsInStasis);
_actions.SetCooldown(ent.Owner, ent.Comp.MinStasisCooldown); // Don't allow entering stasis right after cancelling it.
}
}

View file

@ -51,7 +51,7 @@ changeling-biodegrade-used-popup-self = We vomit acid over our {$restraint}!
changeling-takeover-not-dead = This body yet lives! We cannot infest it alive.
changeling-takeover-is-changeling = This body rejects our attempts to take over!
changeling-takeover-start-others = {CAPITALIZE(THE($user))} begins to crawl inside the corpse!
changeling-takeover-success-self = We have successfully infested the body and assumed its form.
changeling-takeover-doafter = [color=red]{ CAPITALIZE(SUBJECT($user)) } { CONJUGATE-BE($user) } crawling inside of {$target}![/color]
# stings
changeling-sting-success = We silently sting {THE($target)}!

View file

@ -171,6 +171,8 @@
interactSuccessSpawn: EffectHearts
interactSuccessSound:
path: /Audio/Animals/cat_meow.ogg # meow
- type: ChangelingSlug
autoStasisDurationMultiplier: 0.5 # Mercy upon headslugs who already took forever to find a body.
- type: entity # dummy prototype for the store listing
id: ChangelingFleshClothingAbilityStoreDummy