Ghost roles create new minds, better tracking of roles at round end screen (#5175)

* Ghost roles now get new Minds

* Some round start/end button stuff

* Mind tracking for better round end reports

* Make traitor kill objectives use mind CharacterName rather than actual occupied entity ("kill brain" prevention)

* Transition over to EntityUid for mind stuff because that's the only way to do it

* BrainSystem fix for PR rebase
This commit is contained in:
20kdc
2021-11-15 18:14:34 +00:00
committed by GitHub
parent c3a7548545
commit 4cce40bd9f
26 changed files with 229 additions and 68 deletions

View File

@@ -92,7 +92,7 @@ namespace Content.Server.Mind.Components
EntitySystem.Get<SharedGhostSystem>().SetCanReturnToBody(ghost, false);
}
Mind!.TransferTo(visiting);
Mind!.TransferTo(visiting.Uid);
}
else if (GhostOnShutdown)
{
@@ -116,7 +116,7 @@ namespace Content.Server.Mind.Components
if (Mind != null)
{
ghost.Name = Mind.CharacterName ?? string.Empty;
Mind.TransferTo(ghost);
Mind.TransferTo(ghost.Uid);
}
});
}

View File

@@ -37,12 +37,14 @@ namespace Content.Server.Mind
private readonly List<Objective> _objectives = new();
/// <summary>
/// Creates the new mind attached to a specific player session.
/// Creates the new mind.
/// Note: the Mind is NOT initially attached!
/// The provided UserId is solely for tracking of intended owner.
/// </summary>
/// <param name="userId">The session ID of the owning player.</param>
/// <param name="userId">The session ID of the original owner (may get credited).</param>
public Mind(NetUserId userId)
{
UserId = userId;
OriginalOwnerUserId = userId;
}
// TODO: This session should be able to be changed, probably.
@@ -52,6 +54,13 @@ namespace Content.Server.Mind
[ViewVariables]
public NetUserId? UserId { get; private set; }
/// <summary>
/// The session ID of the original owner, if any.
/// May end up used for round-end information (as the owner may have abandoned Mind since)
/// </summary>
[ViewVariables]
public NetUserId OriginalOwnerUserId { get; }
[ViewVariables]
public bool IsVisitingEntity => VisitingEntity != null;
@@ -234,12 +243,10 @@ namespace Content.Server.Mind
return true;
}
/// <summary>
/// Transfer this mind's control over to a new entity.
/// </summary>
/// <param name="entity">
/// <param name="entityUid">
/// The entity to control.
/// Can be null, in which case it will simply detach the mind from any entity.
/// </param>
@@ -249,28 +256,31 @@ namespace Content.Server.Mind
/// <exception cref="ArgumentException">
/// Thrown if <paramref name="entity"/> is already owned by another mind.
/// </exception>
public void TransferTo(IEntity? entity, bool ghostCheckOverride = false)
public void TransferTo(EntityUid? entityUid, bool ghostCheckOverride = false)
{
var entMan = IoCManager.Resolve<IEntityManager>();
IEntity? entity = (entityUid != null) ? entMan.GetEntity(entityUid.Value) : null;
MindComponent? component = null;
var alreadyAttached = false;
if (entity != null)
if (entityUid != null)
{
if (!entity.TryGetComponent(out component))
if (!entMan.TryGetComponent<MindComponent>(entityUid.Value, out component))
{
component = entity.AddComponent<MindComponent>();
component = entMan.AddComponent<MindComponent>(entityUid.Value);
}
else if (component.HasMind)
else if (component!.HasMind)
{
EntitySystem.Get<GameTicker>().OnGhostAttempt(component.Mind!, false);
}
if (entity.TryGetComponent(out ActorComponent? actor))
if (entMan.TryGetComponent<ActorComponent>(entityUid.Value, out var actor))
{
// Happens when transferring to your currently visited entity.
if (actor.PlayerSession != Session)
{
throw new ArgumentException("Visit target already has a session.", nameof(entity));
throw new ArgumentException("Visit target already has a session.", nameof(entityUid));
}
alreadyAttached = true;
@@ -298,11 +308,6 @@ namespace Content.Server.Mind
}
}
public void RemoveOwningPlayer()
{
UserId = null;
}
public void ChangeOwningPlayer(NetUserId? newOwner)
{
var playerMgr = IoCManager.Resolve<IPlayerManager>();
@@ -329,7 +334,7 @@ namespace Content.Server.Mind
{
var data = playerMgr.GetPlayerData(UserId.Value).ContentData();
DebugTools.AssertNotNull(data);
data!.Mind = null;
data!.UpdateMindFromMindChangeOwningPlayer(null);
}
UserId = newOwner;
@@ -342,7 +347,7 @@ namespace Content.Server.Mind
// Can I mention how much I love the word yank?
DebugTools.AssertNotNull(newOwnerData);
newOwnerData!.Mind?.ChangeOwningPlayer(null);
newOwnerData.Mind = this;
newOwnerData.UpdateMindFromMindChangeOwningPlayer(this);
}
public void Visit(IEntity entity)

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using Content.Server.GameTicking;
using Content.Server.Mind.Components;
using Content.Shared.GameTicking;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.ViewVariables;
using Robust.Shared.Player;
namespace Content.Server.Mind
{
/// <summary>
/// This is absolutely evil.
/// It tracks all mind changes and logs all the Mind objects.
/// This is so that when round end comes around, there's a coherent list of all Minds that were in play during the round.
/// The Minds themselves contain metadata about their owners.
/// Anyway, this is because disconnected people and ghost roles have been breaking round end statistics for way too long.
/// </summary>
public class MindTrackerSystem : EntitySystem
{
[ViewVariables]
public readonly HashSet<Mind> AllMinds = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
SubscribeLocalEvent<MindComponent, MindAddedMessage>(OnMindAdded);
}
void Reset(RoundRestartCleanupEvent ev)
{
AllMinds.Clear();
}
void OnMindAdded(EntityUid uid, MindComponent mc, MindAddedMessage args)
{
var mind = mc.Mind;
if (mind != null)
AllMinds.Add(mind);
}
}
}