Skip to content

Commit 8aa3070

Browse files
authored
Assorted thundadome fixes (Goob-Station#6212)
1 parent 3967ec3 commit 8aa3070

16 files changed

Lines changed: 744 additions & 187 deletions

File tree

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
namespace Content.Goobstation.Common.Mind;
22

33
/// <summary>
4-
/// Used to see if the target is valid for an antagonist's target objective.
4+
/// Raised on an entity to check if it should be excluded from objective target selection.
55
/// </summary>
6-
/// <param name="IsSilicon"> Set to true if the target is a silicon. </param>
7-
/// <param name="IsChangeling"> Set to true if the target is a changeling. </param>
86
[ByRefEvent]
9-
public record struct GetAntagSelectionBlockerEvent(
10-
bool IsSilicon = false,
11-
bool IsChangeling = false);
7+
public record struct GetAntagSelectionBlockerEvent(bool Blocked = false);

Content.Goobstation.Server/Changeling/ChangelingSystem.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ private void OnLimbAmputation(Entity<ChangelingComponent> ent, ref BeforeAmputat
214214

215215
private void OnGetAntagBlocker(Entity<ChangelingComponent> ent, ref GetAntagSelectionBlockerEvent args)
216216
{
217-
args.IsChangeling = true;
217+
args.Blocked = true;
218218
}
219219

220220
private void OnMindswapAttempt(Entity<ChangelingComponent> ent, ref BeforeMindSwappedEvent args)
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
using Content.Goobstation.Shared.MisandryBox.Mind;
2+
using Content.Server.Ghost;
3+
using Content.Server.Mind;
4+
using Content.Shared.Ghost;
5+
using Content.Shared.Mind;
6+
using Content.Shared.Mind.Components;
7+
using Robust.Server.Player;
8+
using Robust.Shared.Enums;
9+
using Robust.Shared.Player;
10+
using Robust.Shared.Utility;
11+
12+
namespace Content.Goobstation.Server.MisandryBox.Mind;
13+
14+
/// <summary>
15+
/// Manages temporary mind swaps: shelves a player's real mind and gives them a fresh
16+
/// disposable one for a temporary body. On cleanup the disposable mind is deleted
17+
/// and the player reconnects to their original mind, preserving all roles and objectives.
18+
/// </summary>
19+
public sealed class TemporaryMindSystem : EntitySystem
20+
{
21+
[Dependency] private readonly MindSystem _mind = default!;
22+
[Dependency] private readonly GhostSystem _ghost = default!;
23+
[Dependency] private readonly MetaDataSystem _meta = default!;
24+
[Dependency] private readonly SharedTransformSystem _transform = default!;
25+
[Dependency] private readonly IPlayerManager _playerManager = default!;
26+
27+
public override void Initialize()
28+
{
29+
base.Initialize();
30+
31+
SubscribeLocalEvent<TemporaryMindComponent, PlayerDetachedEvent>(OnPlayerDetached);
32+
}
33+
34+
private void OnPlayerDetached(Entity<TemporaryMindComponent> ent, ref PlayerDetachedEvent args)
35+
{
36+
if (args.Player.Status != SessionStatus.Disconnected)
37+
return;
38+
39+
CleanupDisposableMind(ent.Comp);
40+
RemComp<TemporaryMindComponent>(ent);
41+
}
42+
43+
/// <summary>
44+
/// Shelves the player's current mind and creates a fresh disposable one for the new body.
45+
/// </summary>
46+
public bool TrySwapTempMind(ICommonSession session, EntityUid newBody)
47+
{
48+
if (session.AttachedEntity is not { Valid: true } currentEntity)
49+
return false;
50+
51+
if (!_mind.TryGetMind(currentEntity, out var origMindId, out var origMind))
52+
return false;
53+
54+
var userId = origMind.UserId;
55+
_mind.UnVisit(origMindId, origMind);
56+
57+
// Clear userId from original mind so the disposable mind can claim it in UserMinds
58+
_mind.SetUserId(origMindId, null, origMind);
59+
60+
// Suppress catatonic examine on the original body while mind is swapped
61+
if (origMind.OwnedEntity is { } originalBody
62+
&& TryComp<MindContainerComponent>(originalBody, out var mindContainer))
63+
_mind.SetShowExamineInfo((originalBody, mindContainer), false);
64+
65+
var newMind = _mind.CreateMind(userId, origMind.CharacterName);
66+
_mind.TransferTo(newMind, newBody);
67+
_playerManager.SetAttachedEntity(session, newBody);
68+
69+
var comp = EnsureComp<TemporaryMindComponent>(newBody);
70+
comp.OriginalMind = origMindId;
71+
comp.DisposableMind = newMind;
72+
73+
return true;
74+
}
75+
76+
/// <summary>
77+
/// Discards the disposable mind and restores the original mind as a ghost at the body's location.
78+
/// </summary>
79+
public EntityUid? TryRestoreAsGhost(EntityUid temporaryBody)
80+
{
81+
if (!TryComp<TemporaryMindComponent>(temporaryBody, out var temp))
82+
return null;
83+
84+
if (!TryComp<MindComponent>(temp.OriginalMind, out var origMind))
85+
return null;
86+
87+
var coords = _transform.GetMapCoordinates(temporaryBody);
88+
89+
CleanupDisposableMind(temp);
90+
91+
var ghost = Spawn("MobObserver", coords);
92+
_mind.Visit(temp.OriginalMind, ghost, origMind);
93+
94+
var canReturn = origMind.OwnedEntity is { } ownedEntity && Exists(ownedEntity);
95+
96+
if (TryComp<GhostComponent>(ghost, out var ghostComp))
97+
_ghost.SetCanReturnToBody((ghost, ghostComp), canReturn);
98+
99+
if (!string.IsNullOrWhiteSpace(origMind.CharacterName))
100+
_meta.SetEntityName(ghost, FormattedMessage.EscapeText(origMind.CharacterName));
101+
else if (origMind.UserId is { } userId && _playerManager.TryGetSessionById(userId, out var session))
102+
_meta.SetEntityName(ghost, FormattedMessage.EscapeText(session.Name));
103+
104+
RemComp<TemporaryMindComponent>(temporaryBody);
105+
return ghost;
106+
}
107+
108+
/// <summary>
109+
/// Discards the disposable mind and restores the original mind to its owned body.
110+
/// </summary>
111+
public bool TryRestoreToOriginalBody(EntityUid temporaryBody)
112+
{
113+
if (!TryComp<TemporaryMindComponent>(temporaryBody, out var temp))
114+
return false;
115+
116+
if (!TryComp<MindComponent>(temp.OriginalMind, out var origMind))
117+
return false;
118+
119+
var originalBody = origMind.OwnedEntity;
120+
if (originalBody == null || !Exists(originalBody))
121+
return false;
122+
123+
CleanupDisposableMind(temp);
124+
125+
if (origMind.UserId is { } userId && _playerManager.TryGetSessionById(userId, out var session))
126+
_playerManager.SetAttachedEntity(session, originalBody.Value);
127+
128+
RemComp<TemporaryMindComponent>(temporaryBody);
129+
return true;
130+
}
131+
132+
private void CleanupDisposableMind(TemporaryMindComponent temp)
133+
{
134+
if (Exists(temp.DisposableMind))
135+
{
136+
_mind.WipeMind(temp.DisposableMind);
137+
QueueDel(temp.DisposableMind);
138+
}
139+
140+
if (TryComp<MindComponent>(temp.OriginalMind, out var origMind)
141+
&& origMind.OriginalOwnerUserId is { } userId)
142+
{
143+
_mind.SetUserId(temp.OriginalMind, userId);
144+
145+
if (origMind.OwnedEntity is { } originalBody
146+
&& TryComp<MindContainerComponent>(originalBody, out var mindContainer))
147+
_mind.SetShowExamineInfo((originalBody, mindContainer), true);
148+
}
149+
}
150+
}

0 commit comments

Comments
 (0)