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:
@@ -4,9 +4,10 @@
|
||||
Margin="4"
|
||||
MinSize="50 50">
|
||||
<GridContainer
|
||||
Columns="4">
|
||||
Columns="3">
|
||||
<cc:CommandButton Command="startround" Text="{Loc Start Round}" />
|
||||
<cc:CommandButton Command="endround" Text="{Loc End Round}" />
|
||||
<cc:CommandButton Command="restartround" Text="{Loc Restart Round}" />
|
||||
<cc:CommandButton Command="restartroundnow" Text="{Loc administration-ui-round-tab-restart-round-now}" />
|
||||
</GridContainer>
|
||||
</Control>
|
||||
|
||||
@@ -37,9 +37,9 @@ namespace Content.IntegrationTests.Tests
|
||||
visitEnt = entMgr.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
|
||||
mind = new Mind(player.UserId);
|
||||
player.ContentData().Mind = mind;
|
||||
mind.ChangeOwningPlayer(player.UserId);
|
||||
|
||||
mind.TransferTo(playerEnt);
|
||||
mind.TransferTo(playerEnt.Uid);
|
||||
mind.Visit(visitEnt);
|
||||
|
||||
Assert.That(player.AttachedEntity, Is.EqualTo(visitEnt));
|
||||
@@ -81,9 +81,9 @@ namespace Content.IntegrationTests.Tests
|
||||
playerEnt = entMgr.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
|
||||
mind = new Mind(player.UserId);
|
||||
player.ContentData().Mind = mind;
|
||||
mind.ChangeOwningPlayer(player.UserId);
|
||||
|
||||
mind.TransferTo(playerEnt);
|
||||
mind.TransferTo(playerEnt.Uid);
|
||||
|
||||
Assert.That(mind.CurrentEntity, Is.EqualTo(playerEnt));
|
||||
});
|
||||
@@ -130,9 +130,9 @@ namespace Content.IntegrationTests.Tests
|
||||
playerEnt = entMgr.SpawnEntity(null, grid.ToCoordinates());
|
||||
|
||||
mind = new Mind(player.UserId);
|
||||
player.ContentData().Mind = mind;
|
||||
mind.ChangeOwningPlayer(player.UserId);
|
||||
|
||||
mind.TransferTo(playerEnt);
|
||||
mind.TransferTo(playerEnt.Uid);
|
||||
|
||||
Assert.That(mind.CurrentEntity, Is.EqualTo(playerEnt));
|
||||
});
|
||||
|
||||
@@ -93,7 +93,7 @@ namespace Content.Server.Administration
|
||||
// TODO VERB ICON control mob icon
|
||||
verb.Act = () =>
|
||||
{
|
||||
player.ContentData()?.Mind?.TransferTo(args.Target, ghostCheckOverride: true);
|
||||
player.ContentData()?.Mind?.TransferTo(args.Target.Uid, ghostCheckOverride: true);
|
||||
};
|
||||
args.Verbs.Add(verb);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Content.Server.Administration.Commands
|
||||
else
|
||||
{
|
||||
ghost.Name = player.Name;
|
||||
mind.TransferTo(ghost);
|
||||
mind.TransferTo(ghost.Uid);
|
||||
}
|
||||
|
||||
var comp = ghost.GetComponent<GhostComponent>();
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Content.Server.Administration.Commands
|
||||
|
||||
DebugTools.AssertNotNull(mind);
|
||||
|
||||
mind!.TransferTo(target);
|
||||
mind!.TransferTo(target.Uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,9 +71,9 @@ namespace Content.Server.Administration.Commands
|
||||
{
|
||||
CharacterName = target.Name
|
||||
};
|
||||
playerCData.Mind = mind;
|
||||
mind.ChangeOwningPlayer(session.UserId);
|
||||
}
|
||||
mind.TransferTo(target);
|
||||
mind.TransferTo(target.Uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Content.Server.Body.Systems
|
||||
if (!EntityManager.HasComponent<IMoverComponent>(newEntity))
|
||||
EntityManager.AddComponent<SharedDummyInputMoverComponent>(newEntity);
|
||||
|
||||
oldMind.Mind?.TransferTo(EntityManager.GetEntity(newEntity));
|
||||
oldMind.Mind?.TransferTo(newEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Content.Server.Cloning
|
||||
mindComp.Mind != null)
|
||||
return;
|
||||
|
||||
mind.TransferTo(entity, ghostCheckOverride: true);
|
||||
mind.TransferTo(entity.Uid, ghostCheckOverride: true);
|
||||
mind.UnVisit();
|
||||
ClonesWaitingForMind.Remove(mind);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,14 @@ namespace Content.Server.GameTicking.Commands
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var ticker = EntitySystem.Get<GameTicker>();
|
||||
|
||||
if (ticker.RunLevel != GameRunLevel.InRound)
|
||||
{
|
||||
shell.WriteLine("This can only be executed while the game is in a round - try restartroundnow");
|
||||
return;
|
||||
}
|
||||
|
||||
EntitySystem.Get<RoundEndSystem>().EndRound();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Content.Server.GameTicking
|
||||
{
|
||||
// Always make sure the client has player data. Mind gets assigned on spawn.
|
||||
if (session.Data.ContentDataUncast == null)
|
||||
session.Data.ContentDataUncast = new PlayerData(session.UserId);
|
||||
session.Data.ContentDataUncast = new PlayerData(session.UserId, args.Session.Name);
|
||||
|
||||
// Make the player actually join the game.
|
||||
// timer time must be > tick length
|
||||
|
||||
@@ -2,6 +2,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.Players;
|
||||
using Content.Server.Mind;
|
||||
using Content.Server.Ghost;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Coordinates;
|
||||
using Content.Shared.GameTicking;
|
||||
@@ -210,29 +212,50 @@ namespace Content.Server.GameTicking
|
||||
|
||||
//Generate a list of basic player info to display in the end round summary.
|
||||
var listOfPlayerInfo = new List<RoundEndMessageEvent.RoundEndPlayerInfo>();
|
||||
foreach (var ply in _playerManager.GetAllPlayers().OrderBy(p => p.Name))
|
||||
// Grab the great big book of all the Minds, we'll need them for this.
|
||||
var allMinds = EntitySystem.Get<MindTrackerSystem>().AllMinds;
|
||||
foreach (var mind in allMinds)
|
||||
{
|
||||
var mind = ply.ContentData()?.Mind;
|
||||
|
||||
if (mind != null)
|
||||
{
|
||||
_playersInLobby.TryGetValue(ply, out var status);
|
||||
// Some basics assuming things fail
|
||||
var userId = mind.OriginalOwnerUserId;
|
||||
var playerOOCName = userId.ToString();
|
||||
var connected = false;
|
||||
var observer = mind.AllRoles.Any(role => role is ObserverRole);
|
||||
// Continuing
|
||||
if (_playerManager.TryGetSessionById(userId, out var ply))
|
||||
{
|
||||
connected = true;
|
||||
}
|
||||
PlayerData? contentPlayerData = null;
|
||||
if (_playerManager.TryGetPlayerData(userId, out var playerData))
|
||||
{
|
||||
contentPlayerData = playerData.ContentData();
|
||||
}
|
||||
// Finish
|
||||
var antag = mind.AllRoles.Any(role => role.Antagonist);
|
||||
var playerEndRoundInfo = new RoundEndMessageEvent.RoundEndPlayerInfo()
|
||||
{
|
||||
PlayerOOCName = ply.Name,
|
||||
PlayerICName = mind.CurrentEntity?.Name,
|
||||
// Note that contentPlayerData?.Name sticks around after the player is disconnected.
|
||||
// This is as opposed to ply?.Name which doesn't.
|
||||
PlayerOOCName = contentPlayerData?.Name ?? "(IMPOSSIBLE: REGISTERED MIND WITH NO OWNER)",
|
||||
// Character name takes precedence over current entity name
|
||||
PlayerICName = mind.CharacterName ?? mind.CurrentEntity?.Name,
|
||||
Role = antag
|
||||
? mind.AllRoles.First(role => role.Antagonist).Name
|
||||
: mind.AllRoles.FirstOrDefault()?.Name ?? Loc.GetString("game-ticker-unknown-role"),
|
||||
Antag = antag,
|
||||
Observer = status == LobbyPlayerStatus.Observer,
|
||||
Observer = observer,
|
||||
Connected = connected
|
||||
};
|
||||
listOfPlayerInfo.Add(playerEndRoundInfo);
|
||||
}
|
||||
}
|
||||
// This ordering mechanism isn't great (no ordering of minds) but functions
|
||||
var listOfPlayerInfoFinal = listOfPlayerInfo.OrderBy(pi => pi.PlayerOOCName).ToArray();
|
||||
|
||||
RaiseNetworkEvent(new RoundEndMessageEvent(gamemodeTitle, roundEndText, roundDuration, listOfPlayerInfo.Count, listOfPlayerInfo.ToArray()));
|
||||
RaiseNetworkEvent(new RoundEndMessageEvent(gamemodeTitle, roundEndText, roundDuration, listOfPlayerInfoFinal.Length, listOfPlayerInfoFinal));
|
||||
}
|
||||
|
||||
public void RestartRound()
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Globalization;
|
||||
using Content.Server.Access.Components;
|
||||
using Content.Server.Access.Systems;
|
||||
using Content.Server.CharacterAppearance.Components;
|
||||
using Content.Server.Ghost;
|
||||
using Content.Server.Ghost.Components;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Inventory.Components;
|
||||
@@ -70,17 +71,18 @@ namespace Content.Server.GameTicking
|
||||
DebugTools.AssertNotNull(data);
|
||||
|
||||
data!.WipeMind();
|
||||
data.Mind = new Mind.Mind(player.UserId)
|
||||
var newMind = new Mind.Mind(data.UserId)
|
||||
{
|
||||
CharacterName = character.Name
|
||||
};
|
||||
newMind.ChangeOwningPlayer(data.UserId);
|
||||
|
||||
// Pick best job best on prefs.
|
||||
jobId ??= PickBestAvailableJob(character);
|
||||
|
||||
var jobPrototype = _prototypeManager.Index<JobPrototype>(jobId);
|
||||
var job = new Job(data.Mind, jobPrototype);
|
||||
data.Mind.AddRole(job);
|
||||
var job = new Job(newMind, jobPrototype);
|
||||
newMind.AddRole(job);
|
||||
|
||||
if (lateJoin)
|
||||
{
|
||||
@@ -92,7 +94,7 @@ namespace Content.Server.GameTicking
|
||||
}
|
||||
|
||||
var mob = SpawnPlayerMob(job, character, lateJoin);
|
||||
data.Mind.TransferTo(mob);
|
||||
newMind.TransferTo(mob.Uid);
|
||||
|
||||
if (player.UserId == new Guid("{e887eb93-f503-4b65-95b6-2f282c014192}"))
|
||||
{
|
||||
@@ -150,13 +152,15 @@ namespace Content.Server.GameTicking
|
||||
DebugTools.AssertNotNull(data);
|
||||
|
||||
data!.WipeMind();
|
||||
data.Mind = new Mind.Mind(player.UserId);
|
||||
var newMind = new Mind.Mind(data.UserId);
|
||||
newMind.ChangeOwningPlayer(data.UserId);
|
||||
newMind.AddRole(new ObserverRole(newMind));
|
||||
|
||||
var mob = SpawnObserverMob();
|
||||
mob.Name = name;
|
||||
var ghost = mob.GetComponent<GhostComponent>();
|
||||
EntitySystem.Get<SharedGhostSystem>().SetCanReturnToBody(ghost, false);
|
||||
data.Mind.TransferTo(mob);
|
||||
newMind.TransferTo(mob.Uid);
|
||||
|
||||
_playersInLobby[player] = LobbyPlayerStatus.Observer;
|
||||
RaiseNetworkEvent(GetStatusSingle(player, LobbyPlayerStatus.Observer));
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace Content.Server.GameTicking.Presets
|
||||
if (canReturn)
|
||||
mind.Visit(ghost);
|
||||
else
|
||||
mind.TransferTo(ghost);
|
||||
mind.TransferTo(ghost.Uid);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
18
Content.Server/Ghost/ObserverRole.cs
Normal file
18
Content.Server/Ghost/ObserverRole.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using Content.Server.Roles;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Ghost
|
||||
{
|
||||
/// <summary>
|
||||
/// This is used to mark Observers properly, as they get Minds
|
||||
/// </summary>
|
||||
public class ObserverRole : Role
|
||||
{
|
||||
public override string Name => Loc.GetString("observer-role-name");
|
||||
public override bool Antagonist => false;
|
||||
|
||||
public ObserverRole(Mind.Mind mind) : base(mind)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,11 +51,8 @@ namespace Content.Server.Ghost.Roles.Components
|
||||
|
||||
mob.EnsureComponent<MindComponent>();
|
||||
|
||||
var mind = session.ContentData()?.Mind;
|
||||
|
||||
DebugTools.AssertNotNull(mind);
|
||||
|
||||
mind!.TransferTo(mob);
|
||||
var ghostRoleSystem = EntitySystem.Get<GhostRoleSystem>();
|
||||
ghostRoleSystem.GhostRoleInternalCreateMindAndTransfer(session, OwnerUid, mob.Uid, this);
|
||||
|
||||
if (++_currentTakeovers < _availableTakeovers)
|
||||
return true;
|
||||
|
||||
@@ -27,13 +27,10 @@ namespace Content.Server.Ghost.Roles.Components
|
||||
if (mind.HasMind)
|
||||
return false;
|
||||
|
||||
var sessionMind = session.ContentData()?.Mind;
|
||||
var ghostRoleSystem = EntitySystem.Get<GhostRoleSystem>();
|
||||
ghostRoleSystem.GhostRoleInternalCreateMindAndTransfer(session, OwnerUid, OwnerUid, this);
|
||||
|
||||
DebugTools.AssertNotNull(sessionMind);
|
||||
|
||||
sessionMind!.TransferTo(Owner);
|
||||
|
||||
EntitySystem.Get<GhostRoleSystem>().UnregisterGhostRole(this);
|
||||
ghostRoleSystem.UnregisterGhostRole(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
21
Content.Server/Ghost/Roles/GhostRoleMarkerRole.cs
Normal file
21
Content.Server/Ghost/Roles/GhostRoleMarkerRole.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using Content.Server.Roles;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Ghost.Roles
|
||||
{
|
||||
/// <summary>
|
||||
/// This is used for round end display of ghost roles.
|
||||
/// It may also be used to ensure some ghost roles count as antagonists in future.
|
||||
/// </summary>
|
||||
public class GhostRoleMarkerRole : Role
|
||||
{
|
||||
private readonly string _name;
|
||||
public override string Name => _name;
|
||||
public override bool Antagonist => false;
|
||||
|
||||
public GhostRoleMarkerRole(Mind.Mind mind, string name) : base(mind)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using Content.Server.EUI;
|
||||
using Content.Server.Ghost.Components;
|
||||
using Content.Server.Ghost.Roles.Components;
|
||||
using Content.Server.Ghost.Roles.UI;
|
||||
using Content.Server.Players;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Ghost.Roles;
|
||||
using Content.Shared.Ghost;
|
||||
@@ -14,6 +15,7 @@ using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Enums;
|
||||
|
||||
namespace Content.Server.Ghost.Roles
|
||||
@@ -153,6 +155,24 @@ namespace Content.Server.Ghost.Roles
|
||||
CloseEui(player);
|
||||
}
|
||||
|
||||
public void GhostRoleInternalCreateMindAndTransfer(IPlayerSession player, EntityUid roleUid, EntityUid mob, GhostRoleComponent? role = null)
|
||||
{
|
||||
if (!Resolve(roleUid, ref role)) return;
|
||||
|
||||
var contentData = player.ContentData();
|
||||
|
||||
DebugTools.AssertNotNull(contentData);
|
||||
|
||||
var newMind = new Mind.Mind(player.UserId)
|
||||
{
|
||||
CharacterName = EntityManager.GetComponent<MetaDataComponent>(mob).EntityName
|
||||
};
|
||||
newMind.AddRole(new GhostRoleMarkerRole(newMind, role.RoleName));
|
||||
|
||||
newMind.ChangeOwningPlayer(player.UserId);
|
||||
newMind.TransferTo(mob);
|
||||
}
|
||||
|
||||
public GhostRoleInfo[] GetGhostRolesInfo()
|
||||
{
|
||||
var roles = new GhostRoleInfo[_ghostRoles.Count];
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
46
Content.Server/Mind/MindTrackerSystem.cs
Normal file
46
Content.Server/Mind/MindTrackerSystem.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Content.Server.Objectives.Conditions
|
||||
protected Mind.Mind? Target;
|
||||
public abstract IObjectiveCondition GetAssigned(Mind.Mind mind);
|
||||
|
||||
public string Title => Loc.GetString("objective-condition-kill-person-title", ("targetName", Target?.OwnedEntity?.Name ?? string.Empty));
|
||||
public string Title => Loc.GetString("objective-condition-kill-person-title", ("targetName", Target?.CharacterName ?? Target?.OwnedEntity?.Name ?? string.Empty));
|
||||
|
||||
public string Description => Loc.GetString("objective-condition-kill-person-description");
|
||||
|
||||
|
||||
@@ -16,12 +16,19 @@ namespace Content.Server.Players
|
||||
[ViewVariables]
|
||||
public NetUserId UserId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// This is a backup copy of the player name stored on connection.
|
||||
/// This is useful in the event the player disconnects.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The currently occupied mind of the player owning this data.
|
||||
/// DO NOT DIRECTLY SET THIS UNLESS YOU KNOW WHAT YOU'RE DOING.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public Mind.Mind? Mind { get; set; }
|
||||
public Mind.Mind? Mind { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// If true, the player is an admin and they explicitly de-adminned mid-game,
|
||||
@@ -32,13 +39,22 @@ namespace Content.Server.Players
|
||||
public void WipeMind()
|
||||
{
|
||||
Mind?.TransferTo(null);
|
||||
Mind?.RemoveOwningPlayer();
|
||||
Mind = null;
|
||||
// This will ensure Mind == null
|
||||
Mind?.ChangeOwningPlayer(null);
|
||||
}
|
||||
|
||||
public PlayerData(NetUserId userId)
|
||||
/// <summary>
|
||||
/// Called from Mind.ChangeOwningPlayer *and nowhere else.*
|
||||
/// </summary>
|
||||
public void UpdateMindFromMindChangeOwningPlayer(Mind.Mind? mind)
|
||||
{
|
||||
Mind = mind;
|
||||
}
|
||||
|
||||
public PlayerData(NetUserId userId, string name)
|
||||
{
|
||||
UserId = userId;
|
||||
Name = name;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,6 +128,7 @@ namespace Content.Shared.GameTicking
|
||||
public string Role;
|
||||
public bool Antag;
|
||||
public bool Observer;
|
||||
public bool Connected;
|
||||
}
|
||||
|
||||
public string GamemodeTitle { get; }
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
administration-ui-round-tab-restart-round-now = Restart NOW
|
||||
|
||||
2
Resources/Locale/en-US/ghost/observer-role.ftl
Normal file
2
Resources/Locale/en-US/ghost/observer-role.ftl
Normal file
@@ -0,0 +1,2 @@
|
||||
observer-role-name = Observer
|
||||
|
||||
Reference in New Issue
Block a user