Refactor minds to be entities with components, make roles components (#19591)

Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com>
Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
This commit is contained in:
DrSmugleaf
2023-08-28 16:53:24 -07:00
committed by GitHub
parent e0ee397af7
commit 15c0211fb2
119 changed files with 1445 additions and 1289 deletions

View File

@@ -2,6 +2,7 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Mind;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
@@ -211,8 +212,9 @@ public sealed partial class TestPair : IAsyncDisposable
Assert.That(entMan.HasComponent<MindContainerComponent>(session.AttachedEntity)); Assert.That(entMan.HasComponent<MindContainerComponent>(session.AttachedEntity));
var mindCont = entMan.GetComponent<MindContainerComponent>(session.AttachedEntity!.Value); var mindCont = entMan.GetComponent<MindContainerComponent>(session.AttachedEntity!.Value);
Assert.NotNull(mindCont.Mind); Assert.NotNull(mindCont.Mind);
Assert.Null(mindCont.Mind?.VisitingEntity); Assert.True(entMan.TryGetComponent(mindCont.Mind, out MindComponent? mind));
Assert.That(mindCont.Mind!.OwnedEntity, Is.EqualTo(session.AttachedEntity!.Value)); Assert.Null(mind!.VisitingEntity);
Assert.That(mindCont.Mind.UserId, Is.EqualTo(session.UserId)); Assert.That(mind.OwnedEntity, Is.EqualTo(session.AttachedEntity!.Value));
Assert.That(mind.UserId, Is.EqualTo(session.UserId));
} }
} }

View File

@@ -44,19 +44,20 @@ public sealed class GhostRoleTests
var conHost = client.ResolveDependency<IConsoleHost>(); var conHost = client.ResolveDependency<IConsoleHost>();
var mindSystem = entMan.System<MindSystem>(); var mindSystem = entMan.System<MindSystem>();
var session = sPlayerMan.ServerSessions.Single(); var session = sPlayerMan.ServerSessions.Single();
var originalMind = session.ContentData()!.Mind!; var originalMindId = session.ContentData()!.Mind!.Value;
// Spawn player entity & attach // Spawn player entity & attach
EntityUid originalMob = default; EntityUid originalMob = default;
await server.WaitPost(() => await server.WaitPost(() =>
{ {
originalMob = entMan.SpawnEntity(null, MapCoordinates.Nullspace); originalMob = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
mindSystem.TransferTo(originalMind, originalMob, true); mindSystem.TransferTo(originalMindId, originalMob, true);
}); });
// Check player got attached. // Check player got attached.
await pair.RunTicksSync(10); await pair.RunTicksSync(10);
Assert.That(session.AttachedEntity, Is.EqualTo(originalMob)); Assert.That(session.AttachedEntity, Is.EqualTo(originalMob));
var originalMind = entMan.GetComponent<MindComponent>(originalMindId);
Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob)); Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob));
Assert.Null(originalMind.VisitingEntity); Assert.Null(originalMind.VisitingEntity);
@@ -66,7 +67,7 @@ public sealed class GhostRoleTests
var ghost = session.AttachedEntity; var ghost = session.AttachedEntity;
Assert.That(entMan.HasComponent<GhostComponent>(ghost)); Assert.That(entMan.HasComponent<GhostComponent>(ghost));
Assert.That(ghost, Is.Not.EqualTo(originalMob)); Assert.That(ghost, Is.Not.EqualTo(originalMob));
Assert.That(session.ContentData()?.Mind, Is.EqualTo(originalMind)); Assert.That(session.ContentData()?.Mind, Is.EqualTo(originalMindId));
Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob)); Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob));
Assert.That(originalMind.VisitingEntity, Is.EqualTo(ghost)); Assert.That(originalMind.VisitingEntity, Is.EqualTo(ghost));
@@ -83,8 +84,9 @@ public sealed class GhostRoleTests
// Check player got attached to ghost role. // Check player got attached to ghost role.
await pair.RunTicksSync(10); await pair.RunTicksSync(10);
var newMind = session.ContentData()!.Mind!; var newMindId = session.ContentData()!.Mind!.Value;
Assert.That(newMind, Is.Not.EqualTo(originalMind)); var newMind = entMan.GetComponent<MindComponent>(newMindId);
Assert.That(newMindId, Is.Not.EqualTo(originalMindId));
Assert.That(session.AttachedEntity, Is.EqualTo(ghostRole)); Assert.That(session.AttachedEntity, Is.EqualTo(ghostRole));
Assert.That(newMind.OwnedEntity, Is.EqualTo(ghostRole)); Assert.That(newMind.OwnedEntity, Is.EqualTo(ghostRole));
Assert.Null(newMind.VisitingEntity); Assert.Null(newMind.VisitingEntity);
@@ -101,12 +103,12 @@ public sealed class GhostRoleTests
Assert.That(entMan.HasComponent<GhostComponent>(otherGhost)); Assert.That(entMan.HasComponent<GhostComponent>(otherGhost));
Assert.That(otherGhost, Is.Not.EqualTo(originalMob)); Assert.That(otherGhost, Is.Not.EqualTo(originalMob));
Assert.That(otherGhost, Is.Not.EqualTo(ghostRole)); Assert.That(otherGhost, Is.Not.EqualTo(ghostRole));
Assert.That(session.ContentData()?.Mind, Is.EqualTo(newMind)); Assert.That(session.ContentData()?.Mind, Is.EqualTo(newMindId));
Assert.That(newMind.OwnedEntity, Is.EqualTo(ghostRole)); Assert.That(newMind.OwnedEntity, Is.EqualTo(ghostRole));
Assert.That(newMind.VisitingEntity, Is.EqualTo(session.AttachedEntity)); Assert.That(newMind.VisitingEntity, Is.EqualTo(session.AttachedEntity));
// Next, control the original entity again: // Next, control the original entity again:
await server.WaitPost(() => mindSystem.SetUserId(originalMind, session.UserId)); await server.WaitPost(() => mindSystem.SetUserId(originalMindId, session.UserId));
await pair.RunTicksSync(10); await pair.RunTicksSync(10);
Assert.That(session.AttachedEntity, Is.EqualTo(originalMob)); Assert.That(session.AttachedEntity, Is.EqualTo(originalMob));
Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob)); Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob));

View File

@@ -31,7 +31,8 @@ public sealed partial class MindTests
EntityUid playerEnt = default; EntityUid playerEnt = default;
EntityUid visitEnt = default; EntityUid visitEnt = default;
Mind mind = default!; EntityUid mindId = default!;
MindComponent mind = default!;
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
var player = playerMan.ServerSessions.Single(); var player = playerMan.ServerSessions.Single();
@@ -39,9 +40,10 @@ public sealed partial class MindTests
playerEnt = entMan.SpawnEntity(null, MapCoordinates.Nullspace); playerEnt = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
visitEnt = entMan.SpawnEntity(null, MapCoordinates.Nullspace); visitEnt = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
mind = mindSystem.CreateMind(player.UserId); mindId = mindSystem.CreateMind(player.UserId);
mindSystem.TransferTo(mind, playerEnt); mind = entMan.GetComponent<MindComponent>(mindId);
mindSystem.Visit(mind, visitEnt); mindSystem.TransferTo(mindId, playerEnt);
mindSystem.Visit(mindId, visitEnt);
Assert.Multiple(() => Assert.Multiple(() =>
{ {
@@ -84,12 +86,14 @@ public sealed partial class MindTests
var mindSystem = entMan.EntitySysManager.GetEntitySystem<MindSystem>(); var mindSystem = entMan.EntitySysManager.GetEntitySystem<MindSystem>();
EntityUid playerEnt = default; EntityUid playerEnt = default;
Mind mind = default!; EntityUid mindId = default!;
MindComponent mind = default!;
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
playerEnt = entMan.SpawnEntity(null, coordinates); playerEnt = entMan.SpawnEntity(null, coordinates);
mind = player.ContentData()!.Mind!; mindId = player.ContentData()!.Mind!.Value;
mindSystem.TransferTo(mind, playerEnt); mind = entMan.GetComponent<MindComponent>(mindId);
mindSystem.TransferTo(mindId, playerEnt);
Assert.That(mind.CurrentEntity, Is.EqualTo(playerEnt)); Assert.That(mind.CurrentEntity, Is.EqualTo(playerEnt));
}); });
@@ -169,15 +173,15 @@ public sealed partial class MindTests
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
ghost = entMan.SpawnEntity("MobObserver", MapCoordinates.Nullspace); ghost = entMan.SpawnEntity("MobObserver", MapCoordinates.Nullspace);
mindSystem.Visit(mind, ghost); mindSystem.Visit(mind.Id, ghost);
}); });
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(player.AttachedEntity, Is.EqualTo(ghost)); Assert.That(player.AttachedEntity, Is.EqualTo(ghost));
Assert.That(entMan.HasComponent<GhostComponent>(player.AttachedEntity), "player is not a ghost"); Assert.That(entMan.HasComponent<GhostComponent>(player.AttachedEntity), "player is not a ghost");
Assert.That(mind.VisitingEntity, Is.EqualTo(player.AttachedEntity)); Assert.That(mind.Comp.VisitingEntity, Is.EqualTo(player.AttachedEntity));
Assert.That(mind.OwnedEntity, Is.EqualTo(originalEntity)); Assert.That(mind.Comp.OwnedEntity, Is.EqualTo(originalEntity));
}); });
await pair.RunTicksSync(5); await pair.RunTicksSync(5);
@@ -192,8 +196,8 @@ public sealed partial class MindTests
{ {
Assert.That(player.AttachedEntity, Is.EqualTo(ghost)); Assert.That(player.AttachedEntity, Is.EqualTo(ghost));
Assert.That(entMan.HasComponent<GhostComponent>(player.AttachedEntity)); Assert.That(entMan.HasComponent<GhostComponent>(player.AttachedEntity));
Assert.That(mind.VisitingEntity, Is.Null); Assert.That(mind.Comp.VisitingEntity, Is.Null);
Assert.That(mind.OwnedEntity, Is.EqualTo(ghost)); Assert.That(mind.Comp.OwnedEntity, Is.EqualTo(ghost));
}); });
await pair.CleanReturnAsync(); await pair.CleanReturnAsync();
@@ -233,8 +237,10 @@ public sealed partial class MindTests
Assert.That(entMan.GetComponent<MetaDataComponent>(player.AttachedEntity!.Value).EntityPrototype?.ID, Is.EqualTo("AdminObserver")); Assert.That(entMan.GetComponent<MetaDataComponent>(player.AttachedEntity!.Value).EntityPrototype?.ID, Is.EqualTo("AdminObserver"));
}); });
var mind = player.ContentData()?.Mind; var mindId = player.ContentData()?.Mind;
Assert.That(mind, Is.Not.Null); Assert.That(mindId, Is.Not.Null);
var mind = entMan.GetComponent<MindComponent>(mindId.Value);
Assert.That(mind.VisitingEntity, Is.Null); Assert.That(mind.VisitingEntity, Is.Null);
await pair.CleanReturnAsync(); await pair.CleanReturnAsync();

View File

@@ -9,7 +9,6 @@ using Robust.Shared.Enums;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Network; using Robust.Shared.Network;
using IPlayerManager = Robust.Server.Player.IPlayerManager;
namespace Content.IntegrationTests.Tests.Minds; namespace Content.IntegrationTests.Tests.Minds;
@@ -40,19 +39,21 @@ public sealed partial class MindTests
var player = playerMan.ServerSessions.Single(); var player = playerMan.ServerSessions.Single();
EntityUid entity = default; EntityUid entity = default;
Mind mind = default!; EntityUid mindId = default!;
MindComponent mind = default!;
await pair.Server.WaitPost(() => await pair.Server.WaitPost(() =>
{ {
entity = entMan.SpawnEntity(null, MapCoordinates.Nullspace); entity = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
mind = mindSys.CreateMind(player.UserId); mindId = mindSys.CreateMind(player.UserId);
mindSys.TransferTo(mind, entity); mind = entMan.GetComponent<MindComponent>(mindId);
mindSys.TransferTo(mindId, entity);
}); });
await pair.RunTicksSync(5); await pair.RunTicksSync(5);
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(player.ContentData()?.Mind, Is.EqualTo(mind)); Assert.That(player.ContentData()?.Mind, Is.EqualTo(mindId));
Assert.That(player.AttachedEntity, Is.EqualTo(entity)); Assert.That(player.AttachedEntity, Is.EqualTo(entity));
Assert.That(player.AttachedEntity, Is.EqualTo(mind.CurrentEntity), "Player is not attached to the mind's current entity."); Assert.That(player.AttachedEntity, Is.EqualTo(mind.CurrentEntity), "Player is not attached to the mind's current entity.");
Assert.That(entMan.EntityExists(mind.OwnedEntity), "The mind's current entity does not exist"); Assert.That(entMan.EntityExists(mind.OwnedEntity), "The mind's current entity does not exist");
@@ -67,23 +68,25 @@ public sealed partial class MindTests
var playerMan = pair.Server.ResolveDependency<IPlayerManager>(); var playerMan = pair.Server.ResolveDependency<IPlayerManager>();
var mindSys = entMan.System<MindSystem>(); var mindSys = entMan.System<MindSystem>();
EntityUid ghostUid = default; EntityUid ghostUid = default;
Mind mind = default!; EntityUid mindId = default!;
MindComponent mind = default!;
var player = playerMan.ServerSessions.Single(); var player = playerMan.ServerSessions.Single();
await pair.Server.WaitAssertion(() => await pair.Server.WaitAssertion(() =>
{ {
var oldUid = player.AttachedEntity; var oldUid = player.AttachedEntity;
ghostUid = entMan.SpawnEntity("MobObserver", MapCoordinates.Nullspace); ghostUid = entMan.SpawnEntity("MobObserver", MapCoordinates.Nullspace);
mind = mindSys.GetMind(player.UserId); mindId = mindSys.GetMind(player.UserId)!.Value;
Assert.That(mind, Is.Not.Null); Assert.That(mindId, Is.Not.EqualTo(default(EntityUid)));
mind = entMan.GetComponent<MindComponent>(mindId);
if (visit) if (visit)
{ {
mindSys.Visit(mind, ghostUid); mindSys.Visit(mindId, ghostUid);
return; return;
} }
mindSys.TransferTo(mind, ghostUid); mindSys.TransferTo(mindId, ghostUid);
if (oldUid != null) if (oldUid != null)
entMan.DeleteEntity(oldUid.Value); entMan.DeleteEntity(oldUid.Value);
@@ -111,15 +114,16 @@ public sealed partial class MindTests
/// <summary> /// <summary>
/// Get the player's current mind and check that the entities exists. /// Get the player's current mind and check that the entities exists.
/// </summary> /// </summary>
private static Mind GetMind(Pair.TestPair pair) private static (EntityUid Id, MindComponent Comp) GetMind(Pair.TestPair pair)
{ {
var playerMan = pair.Server.ResolveDependency<IPlayerManager>(); var playerMan = pair.Server.ResolveDependency<IPlayerManager>();
var entMan = pair.Server.ResolveDependency<IEntityManager>(); var entMan = pair.Server.ResolveDependency<IEntityManager>();
var player = playerMan.ServerSessions.SingleOrDefault(); var player = playerMan.ServerSessions.SingleOrDefault();
Assert.That(player, Is.Not.Null); Assert.That(player, Is.Not.Null);
var mind = player.ContentData()!.Mind; var mindId = player.ContentData()!.Mind!.Value;
Assert.That(mind, Is.Not.Null); Assert.That(mindId, Is.Not.EqualTo(default(EntityUid)));
var mind = entMan.GetComponent<MindComponent>(mindId);
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(player.AttachedEntity, Is.EqualTo(mind.CurrentEntity), "Player is not attached to the mind's current entity."); Assert.That(player.AttachedEntity, Is.EqualTo(mind.CurrentEntity), "Player is not attached to the mind's current entity.");
@@ -127,15 +131,17 @@ public sealed partial class MindTests
Assert.That(mind.VisitingEntity == null || entMan.EntityExists(mind.VisitingEntity), "The minds visited entity does not exist."); Assert.That(mind.VisitingEntity == null || entMan.EntityExists(mind.VisitingEntity), "The minds visited entity does not exist.");
}); });
return mind; return (mindId, mind);
} }
private static async Task Disconnect(Pair.TestPair pair) private static async Task Disconnect(Pair.TestPair pair)
{ {
var netManager = pair.Client.ResolveDependency<IClientNetManager>(); var netManager = pair.Client.ResolveDependency<IClientNetManager>();
var playerMan = pair.Server.ResolveDependency<IPlayerManager>(); var playerMan = pair.Server.ResolveDependency<IPlayerManager>();
var entMan = pair.Server.ResolveDependency<IEntityManager>();
var player = playerMan.ServerSessions.Single(); var player = playerMan.ServerSessions.Single();
var mind = player.ContentData()!.Mind; var mindId = player.ContentData()!.Mind!.Value;
var mind = entMan.GetComponent<MindComponent>(mindId);
await pair.Client.WaitAssertion(() => await pair.Client.WaitAssertion(() =>
{ {

View File

@@ -29,8 +29,8 @@ public sealed partial class MindTests
{ {
Assert.That(GetMind(pair), Is.EqualTo(mind)); Assert.That(GetMind(pair), Is.EqualTo(mind));
Assert.That(entMan.Deleted(ghost)); Assert.That(entMan.Deleted(ghost));
Assert.That(entMan.HasComponent<GhostComponent>(mind.OwnedEntity)); Assert.That(entMan.HasComponent<GhostComponent>(mind.Comp.OwnedEntity));
Assert.That(mind.VisitingEntity, Is.Null); Assert.That(mind.Comp.VisitingEntity, Is.Null);
}); });
await pair.CleanReturnAsync(); await pair.CleanReturnAsync();
@@ -52,11 +52,11 @@ public sealed partial class MindTests
var player = playerMan.ServerSessions.Single(); var player = playerMan.ServerSessions.Single();
var name = player.Name; var name = player.Name;
var user = player.UserId; var user = player.UserId;
Assert.That(mind.OwnedEntity, Is.Not.Null); Assert.That(mind.Comp.OwnedEntity, Is.Not.Null);
var entity = mind.OwnedEntity.Value; var entity = mind.Comp.OwnedEntity.Value;
// Player is not a ghost // Player is not a ghost
Assert.That(!entMan.HasComponent<GhostComponent>(mind.CurrentEntity)); Assert.That(!entMan.HasComponent<GhostComponent>(mind.Comp.CurrentEntity));
// Disconnect // Disconnect
await Disconnect(pair); await Disconnect(pair);
@@ -67,7 +67,7 @@ public sealed partial class MindTests
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(entMan.Deleted(entity)); Assert.That(entMan.Deleted(entity));
Assert.That(mind.OwnedEntity, Is.Null); Assert.That(mind.Comp.OwnedEntity, Is.Null);
}); });
// Reconnect // Reconnect
@@ -79,8 +79,8 @@ public sealed partial class MindTests
// Player is now a new ghost entity // Player is now a new ghost entity
Assert.That(GetMind(pair), Is.EqualTo(mind)); Assert.That(GetMind(pair), Is.EqualTo(mind));
Assert.That(mind.OwnedEntity, Is.Not.EqualTo(entity)); Assert.That(mind.Comp.OwnedEntity, Is.Not.EqualTo(entity));
Assert.That(entMan.HasComponent<GhostComponent>(mind.OwnedEntity)); Assert.That(entMan.HasComponent<GhostComponent>(mind.Comp.OwnedEntity));
}); });
await pair.CleanReturnAsync(); await pair.CleanReturnAsync();
@@ -98,7 +98,7 @@ public sealed partial class MindTests
var entMan = pair.Server.ResolveDependency<IEntityManager>(); var entMan = pair.Server.ResolveDependency<IEntityManager>();
var mind = GetMind(pair); var mind = GetMind(pair);
var original = mind.CurrentEntity; var original = mind.Comp.CurrentEntity;
var ghost = await VisitGhost(pair); var ghost = await VisitGhost(pair);
await DisconnectReconnect(pair); await DisconnectReconnect(pair);
@@ -106,7 +106,7 @@ public sealed partial class MindTests
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(mind, Is.EqualTo(GetMind(pair))); Assert.That(mind, Is.EqualTo(GetMind(pair)));
Assert.That(mind.CurrentEntity, Is.EqualTo(original)); Assert.That(mind.Comp.CurrentEntity, Is.EqualTo(original));
Assert.That(entMan.Deleted(original), Is.False); Assert.That(entMan.Deleted(original), Is.False);
Assert.That(entMan.Deleted(ghost)); Assert.That(entMan.Deleted(ghost));
}); });
@@ -128,12 +128,12 @@ public sealed partial class MindTests
var mind = GetMind(pair); var mind = GetMind(pair);
// Make player visit a new mob // Make player visit a new mob
var original = mind.CurrentEntity; var original = mind.Comp.CurrentEntity;
EntityUid visiting = default; EntityUid visiting = default;
await pair.Server.WaitAssertion(() => await pair.Server.WaitAssertion(() =>
{ {
visiting = entMan.SpawnEntity(null, MapCoordinates.Nullspace); visiting = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
mindSys.Visit(mind, visiting); mindSys.Visit(mind.Id, visiting);
}); });
await pair.RunTicksSync(5); await pair.RunTicksSync(5);
@@ -145,7 +145,7 @@ public sealed partial class MindTests
Assert.That(GetMind(pair), Is.EqualTo(mind)); Assert.That(GetMind(pair), Is.EqualTo(mind));
Assert.That(entMan.Deleted(original), Is.False); Assert.That(entMan.Deleted(original), Is.False);
Assert.That(entMan.Deleted(visiting), Is.False); Assert.That(entMan.Deleted(visiting), Is.False);
Assert.That(mind.CurrentEntity, Is.EqualTo(visiting)); Assert.That(mind.Comp.CurrentEntity, Is.EqualTo(visiting));
}); });
await pair.CleanReturnAsync(); await pair.CleanReturnAsync();

View File

@@ -8,17 +8,17 @@ using Content.Server.Mind.Commands;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Server.Players; using Content.Server.Players;
using Content.Server.Roles; using Content.Server.Roles;
using Content.Server.Roles.Jobs;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Roles;
using Robust.Server.Console; using Robust.Server.Console;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using IPlayerManager = Robust.Server.Player.IPlayerManager;
namespace Content.IntegrationTests.Tests.Minds; namespace Content.IntegrationTests.Tests.Minds;
@@ -66,12 +66,13 @@ public sealed partial class MindTests
var entity = entMan.SpawnEntity(null, new MapCoordinates()); var entity = entMan.SpawnEntity(null, new MapCoordinates());
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity); var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
var mind = mindSystem.CreateMind(null); var mindId = mindSystem.CreateMind(null);
var mind = entMan.GetComponent<MindComponent>(mindId);
Assert.That(mind.UserId, Is.EqualTo(null)); Assert.That(mind.UserId, Is.EqualTo(null));
mindSystem.TransferTo(mind, entity); mindSystem.TransferTo(mindId, entity, mind: mind);
Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind)); Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mindId));
}); });
await pair.CleanReturnAsync(); await pair.CleanReturnAsync();
@@ -92,15 +93,16 @@ public sealed partial class MindTests
var entity = entMan.SpawnEntity(null, new MapCoordinates()); var entity = entMan.SpawnEntity(null, new MapCoordinates());
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity); var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
var mind = mindSystem.CreateMind(null); var mindId = mindSystem.CreateMind(null);
mindSystem.TransferTo(mind, entity); mindSystem.TransferTo(mindId, entity);
Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind)); Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mindId));
var mind2 = mindSystem.CreateMind(null); var mind2 = mindSystem.CreateMind(null);
mindSystem.TransferTo(mind2, entity); mindSystem.TransferTo(mind2, entity);
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind2)); Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind2));
var mind = entMan.GetComponent<MindComponent>(mindId);
Assert.That(mind.OwnedEntity, Is.Not.EqualTo(entity)); Assert.That(mind.OwnedEntity, Is.Not.EqualTo(entity));
}); });
}); });
@@ -119,7 +121,7 @@ public sealed partial class MindTests
EntityUid entity = default!; EntityUid entity = default!;
MindContainerComponent mindContainerComp = default!; MindContainerComponent mindContainerComp = default!;
Mind mind = default!; EntityUid mindId = default!;
var mindSystem = entMan.EntitySysManager.GetEntitySystem<MindSystem>(); var mindSystem = entMan.EntitySysManager.GetEntitySystem<MindSystem>();
var damageableSystem = entMan.EntitySysManager.GetEntitySystem<DamageableSystem>(); var damageableSystem = entMan.EntitySysManager.GetEntitySystem<DamageableSystem>();
@@ -128,12 +130,13 @@ public sealed partial class MindTests
entity = entMan.SpawnEntity("MindTestEntityDamageable", new MapCoordinates()); entity = entMan.SpawnEntity("MindTestEntityDamageable", new MapCoordinates());
mindContainerComp = entMan.EnsureComponent<MindContainerComponent>(entity); mindContainerComp = entMan.EnsureComponent<MindContainerComponent>(entity);
mind = mindSystem.CreateMind(null); mindId = mindSystem.CreateMind(null);
mindSystem.TransferTo(mind, entity); mindSystem.TransferTo(mindId, entity);
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(mindSystem.GetMind(entity, mindContainerComp), Is.EqualTo(mind)); Assert.That(mindSystem.GetMind(entity, mindContainerComp), Is.EqualTo(mindId));
var mind = entMan.GetComponent<MindComponent>(mindId);
Assert.That(!mindSystem.IsCharacterDeadPhysically(mind)); Assert.That(!mindSystem.IsCharacterDeadPhysically(mind));
}); });
}); });
@@ -149,13 +152,14 @@ public sealed partial class MindTests
} }
damageableSystem.SetDamage(entity, damageable, new DamageSpecifier(prototype, FixedPoint2.New(401))); damageableSystem.SetDamage(entity, damageable, new DamageSpecifier(prototype, FixedPoint2.New(401)));
Assert.That(mindSystem.GetMind(entity, mindContainerComp), Is.EqualTo(mind)); Assert.That(mindSystem.GetMind(entity, mindContainerComp), Is.EqualTo(mindId));
}); });
await pair.RunTicksSync(5); await pair.RunTicksSync(5);
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
var mind = entMan.GetComponent<MindComponent>(mindId);
Assert.That(mindSystem.IsCharacterDeadPhysically(mind)); Assert.That(mindSystem.IsCharacterDeadPhysically(mind));
}); });
@@ -211,20 +215,22 @@ public sealed partial class MindTests
await pair.RunTicksSync(5); await pair.RunTicksSync(5);
var mindSystem = entMan.EntitySysManager.GetEntitySystem<MindSystem>(); var mindSystem = entMan.EntitySysManager.GetEntitySystem<MindSystem>();
var originalMind = GetMind(pair); var originalMind = GetMind(pair);
var userId = originalMind.UserId; var userId = originalMind.Comp.UserId;
Mind mind = default!; EntityUid mindId = default!;
MindComponent mind = default!;
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
var entity = entMan.SpawnEntity(null, new MapCoordinates()); var entity = entMan.SpawnEntity(null, new MapCoordinates());
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity); var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
entMan.DirtyEntity(entity); entMan.DirtyEntity(entity);
mind = mindSystem.CreateMind(null); mindId = mindSystem.CreateMind(null);
mindSystem.TransferTo(mind, entity); mind = entMan.GetComponent<MindComponent>(mindId);
mindSystem.TransferTo(mindId, entity);
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind)); Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mindId));
Assert.That(mindComp.HasMind); Assert.That(mindComp.HasMind);
}); });
}); });
@@ -233,18 +239,18 @@ public sealed partial class MindTests
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
mindSystem.SetUserId(mind, userId); mindSystem.SetUserId(mindId, userId);
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(mind.UserId, Is.EqualTo(userId)); Assert.That(mind.UserId, Is.EqualTo(userId));
Assert.That(originalMind.UserId, Is.EqualTo(null)); Assert.That(originalMind.Comp.UserId, Is.EqualTo(null));
}); });
mindSystem.SetUserId(originalMind, userId); mindSystem.SetUserId(originalMind.Id, userId);
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(mind.UserId, Is.EqualTo(null)); Assert.That(mind.UserId, Is.EqualTo(null));
Assert.That(originalMind.UserId, Is.EqualTo(userId)); Assert.That(originalMind.Comp.UserId, Is.EqualTo(userId));
}); });
}); });
@@ -264,57 +270,59 @@ public sealed partial class MindTests
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
var mindSystem = entMan.EntitySysManager.GetEntitySystem<MindSystem>(); var mindSystem = entMan.EntitySysManager.GetEntitySystem<MindSystem>();
var roleSystem = entMan.EntitySysManager.GetEntitySystem<RoleSystem>();
var entity = entMan.SpawnEntity(null, new MapCoordinates()); var entity = entMan.SpawnEntity(null, new MapCoordinates());
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity); var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
var mind = mindSystem.CreateMind(null); var mindId = mindSystem.CreateMind(null);
var mind = entMan.EnsureComponent<MindComponent>(mindId);
Assert.That(mind.UserId, Is.EqualTo(null)); Assert.That(mind.UserId, Is.EqualTo(null));
mindSystem.TransferTo(mind, entity); mindSystem.TransferTo(mindId, entity);
Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind)); Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mindId));
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(mindSystem.HasRole<TraitorRole>(mind), Is.False); Assert.That(roleSystem.MindHasRole<TraitorRoleComponent>(mindId), Is.False);
Assert.That(mindSystem.HasRole<Job>(mind), Is.False); Assert.That(roleSystem.MindHasRole<JobComponent>(mindId), Is.False);
}); });
var traitorRole = new TraitorRole(mind, new AntagPrototype()); var traitorRole = new TraitorRoleComponent();
mindSystem.AddRole(mind, traitorRole); roleSystem.MindAddRole(mindId, traitorRole);
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(mindSystem.HasRole<TraitorRole>(mind)); Assert.That(roleSystem.MindHasRole<TraitorRoleComponent>(mindId));
Assert.That(mindSystem.HasRole<Job>(mind), Is.False); Assert.That(roleSystem.MindHasRole<JobComponent>(mindId), Is.False);
}); });
var jobRole = new Job(mind, new JobPrototype()); var jobRole = new JobComponent();
mindSystem.AddRole(mind, jobRole); roleSystem.MindAddRole(mindId, jobRole);
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(mindSystem.HasRole<TraitorRole>(mind)); Assert.That(roleSystem.MindHasRole<TraitorRoleComponent>(mindId));
Assert.That(mindSystem.HasRole<Job>(mind)); Assert.That(roleSystem.MindHasRole<JobComponent>(mindId));
}); });
mindSystem.RemoveRole(mind, traitorRole); roleSystem.MindRemoveRole<TraitorRoleComponent>(mindId);
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(mindSystem.HasRole<TraitorRole>(mind), Is.False); Assert.That(roleSystem.MindHasRole<TraitorRoleComponent>(mindId), Is.False);
Assert.That(mindSystem.HasRole<Job>(mind)); Assert.That(roleSystem.MindHasRole<JobComponent>(mindId));
}); });
mindSystem.RemoveRole(mind, jobRole); roleSystem.MindRemoveRole<JobComponent>(mindId);
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(mindSystem.HasRole<TraitorRole>(mind), Is.False); Assert.That(roleSystem.MindHasRole<TraitorRoleComponent>(mindId), Is.False);
Assert.That(mindSystem.HasRole<Job>(mind), Is.False); Assert.That(roleSystem.MindHasRole<JobComponent>(mindId), Is.False);
}); });
}); });
@@ -335,7 +343,8 @@ public sealed partial class MindTests
var ghostSystem = entMan.EntitySysManager.GetEntitySystem<GhostSystem>(); var ghostSystem = entMan.EntitySysManager.GetEntitySystem<GhostSystem>();
EntityUid entity = default!; EntityUid entity = default!;
Mind mind = default!; EntityUid mindId = default!;
MindComponent mind = default!;
var player = playerMan.ServerSessions.Single(); var player = playerMan.ServerSessions.Single();
await server.WaitAssertion(() => await server.WaitAssertion(() =>
@@ -343,12 +352,13 @@ public sealed partial class MindTests
entity = entMan.SpawnEntity(null, new MapCoordinates()); entity = entMan.SpawnEntity(null, new MapCoordinates());
var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity); var mindComp = entMan.EnsureComponent<MindContainerComponent>(entity);
mind = mindSystem.CreateMind(player.UserId, "Mindy McThinker"); mindId = mindSystem.CreateMind(player.UserId, "Mindy McThinker");
mind = entMan.GetComponent<MindComponent>(mindId);
Assert.That(mind.UserId, Is.EqualTo(player.UserId)); Assert.That(mind.UserId, Is.EqualTo(player.UserId));
mindSystem.TransferTo(mind, entity); mindSystem.TransferTo(mindId, entity);
Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mind)); Assert.That(mindSystem.GetMind(entity, mindComp), Is.EqualTo(mindId));
}); });
await pair.RunTicksSync(5); await pair.RunTicksSync(5);
@@ -361,7 +371,8 @@ public sealed partial class MindTests
await pair.RunTicksSync(5); await pair.RunTicksSync(5);
EntityUid mob = default!; EntityUid mob = default!;
Mind mobMind = default!; EntityUid mobMindId = default!;
MindComponent mobMind = default!;
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
@@ -370,22 +381,25 @@ public sealed partial class MindTests
mob = entMan.SpawnEntity(null, new MapCoordinates()); mob = entMan.SpawnEntity(null, new MapCoordinates());
MakeSentientCommand.MakeSentient(mob, IoCManager.Resolve<IEntityManager>()); MakeSentientCommand.MakeSentient(mob, IoCManager.Resolve<IEntityManager>());
mobMind = mindSystem.CreateMind(player.UserId, "Mindy McThinker the Second"); mobMindId = mindSystem.CreateMind(player.UserId, "Mindy McThinker the Second");
mobMind = entMan.GetComponent<MindComponent>(mobMindId);
mindSystem.SetUserId(mobMind, player.UserId); mindSystem.SetUserId(mobMindId, player.UserId);
mindSystem.TransferTo(mobMind, mob); mindSystem.TransferTo(mobMindId, mob);
}); });
await pair.RunTicksSync(5); await pair.RunTicksSync(5);
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
var m = player.ContentData()?.Mind; var mId = player.ContentData()?.Mind!.Value;
Assert.That(m, Is.Not.Null); Assert.That(mId, Is.Not.Null);
Assert.That(mId, Is.Not.EqualTo(default(EntityUid)));
var m = entMan.GetComponent<MindComponent>(mId!.Value);
Assert.Multiple(() => Assert.Multiple(() =>
{ {
Assert.That(m!.OwnedEntity, Is.EqualTo(mob)); Assert.That(m!.OwnedEntity, Is.EqualTo(mob));
Assert.That(m, Is.Not.EqualTo(mind)); Assert.That(mId, Is.Not.EqualTo(mindId));
}); });
}); });
@@ -416,7 +430,8 @@ public sealed partial class MindTests
//EntityUid entity = default!; //EntityUid entity = default!;
EntityUid ghostRole = default!; EntityUid ghostRole = default!;
EntityUid ghost = default!; EntityUid ghost = default!;
Mind mind = default!; EntityUid mindId = default!;
MindComponent mind = default!;
var player = playerMan.ServerSessions.Single(); var player = playerMan.ServerSessions.Single();
await server.WaitAssertion(() => await server.WaitAssertion(() =>
@@ -434,7 +449,8 @@ public sealed partial class MindTests
var data = player.ContentData(); var data = player.ContentData();
Assert.That(data?.Mind, Is.Not.EqualTo(null)); Assert.That(data?.Mind, Is.Not.EqualTo(null));
mind = data!.Mind!; mindId = data!.Mind!.Value;
mind = entMan.GetComponent<MindComponent>(mindId);
Assert.That(mind.OwnedEntity, Is.Not.Null); Assert.That(mind.OwnedEntity, Is.Not.Null);
@@ -460,8 +476,8 @@ public sealed partial class MindTests
await server.WaitAssertion(() => await server.WaitAssertion(() =>
{ {
var data = player.ContentData()!; var data = entMan.GetComponent<MindComponent>(player.ContentData()!.Mind!.Value);
Assert.That(data.Mind!.OwnedEntity, Is.EqualTo(ghostRole)); Assert.That(data.OwnedEntity, Is.EqualTo(ghostRole));
serverConsole.ExecuteCommand(player, "aghost"); serverConsole.ExecuteCommand(player, "aghost");
Assert.That(player.AttachedEntity, Is.Not.Null); Assert.That(player.AttachedEntity, Is.Not.Null);

View File

@@ -1,6 +1,5 @@
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Players;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Ghost; using Content.Shared.Ghost;
using Robust.Server.Player; using Robust.Server.Player;
@@ -26,20 +25,18 @@ namespace Content.Server.Administration.Commands
return; return;
} }
var mind = player.ContentData()?.Mind; var mindSystem = _entities.System<MindSystem>();
if (!mindSystem.TryGetMind(player, out var mindId, out var mind))
if (mind == null)
{ {
shell.WriteLine("You can't ghost here!"); shell.WriteLine("You can't ghost here!");
return; return;
} }
var mindSystem = _entities.System<MindSystem>();
var metaDataSystem = _entities.System<MetaDataSystem>(); var metaDataSystem = _entities.System<MetaDataSystem>();
if (mind.VisitingEntity != default && _entities.TryGetComponent<GhostComponent>(mind.VisitingEntity, out var oldGhostComponent)) if (mind.VisitingEntity != default && _entities.TryGetComponent<GhostComponent>(mind.VisitingEntity, out var oldGhostComponent))
{ {
mindSystem.UnVisit(mind); mindSystem.UnVisit(mindId, mind);
// If already an admin ghost, then return to body. // If already an admin ghost, then return to body.
if (oldGhostComponent.CanGhostInteract) if (oldGhostComponent.CanGhostInteract)
return; return;
@@ -61,12 +58,12 @@ namespace Content.Server.Administration.Commands
else if (!string.IsNullOrWhiteSpace(mind.Session?.Name)) else if (!string.IsNullOrWhiteSpace(mind.Session?.Name))
metaDataSystem.SetEntityName(ghost, mind.Session.Name); metaDataSystem.SetEntityName(ghost, mind.Session.Name);
mindSystem.Visit(mind, ghost); mindSystem.Visit(mindId, ghost, mind);
} }
else else
{ {
metaDataSystem.SetEntityName(ghost, player.Name); metaDataSystem.SetEntityName(ghost, player.Name);
mindSystem.TransferTo(mind, ghost); mindSystem.TransferTo(mindId, ghost, mind: mind);
} }
var comp = _entities.GetComponent<GhostComponent>(ghost); var comp = _entities.GetComponent<GhostComponent>(ghost);

View File

@@ -1,10 +1,7 @@
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Mind.Components;
using Content.Server.Players;
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.Utility;
namespace Content.Server.Administration.Commands namespace Content.Server.Administration.Commands
{ {
@@ -45,18 +42,14 @@ namespace Content.Server.Administration.Commands
return; return;
} }
if (!_entities.HasComponent<MindContainerComponent>(target)) var mindSystem = _entities.System<MindSystem>();
if (!mindSystem.TryGetMind(target, out var mindId, out var mind))
{ {
shell.WriteLine(Loc.GetString("shell-entity-is-not-mob")); shell.WriteLine(Loc.GetString("shell-entity-is-not-mob"));
return; return;
} }
var mind = player.ContentData()?.Mind; mindSystem.TransferTo(mindId, target, mind: mind);
DebugTools.AssertNotNull(mind);
var mindSystem = _entities.System<MindSystem>();
mindSystem.TransferTo(mind!, target);
} }
} }
} }

View File

@@ -68,13 +68,9 @@ namespace Content.Server.Administration.Commands
} }
var mindSystem = entityManager.System<MindSystem>(); var mindSystem = entityManager.System<MindSystem>();
var metadata = entityManager.GetComponent<MetaDataComponent>(eUid);
var mind = playerCData.Mind; var mind = playerCData.Mind ?? mindSystem.CreateMind(session.UserId, metadata.EntityName);
if (mind == null)
{
mind = mindSystem.CreateMind(session.UserId);
mind.CharacterName = entityManager.GetComponent<MetaDataComponent>(eUid).EntityName;
}
mindSystem.TransferTo(mind, eUid, ghostOverride); mindSystem.TransferTo(mind, eUid, ghostOverride);
} }

View File

@@ -1,10 +1,9 @@
using System.Globalization;
using System.Linq; using System.Linq;
using Content.Server.Administration.Managers; using Content.Server.Administration.Managers;
using Content.Server.GameTicking.Events;
using Content.Server.IdentityManagement; using Content.Server.IdentityManagement;
using Content.Server.Players; using Content.Server.Mind;
using Content.Server.Roles; using Content.Server.Roles;
using Content.Server.Roles.Jobs;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Administration.Events; using Content.Shared.Administration.Events;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
@@ -20,6 +19,9 @@ namespace Content.Server.Administration.Systems
{ {
[Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly JobSystem _jobs = default!;
[Dependency] private readonly MindSystem _minds = default!;
[Dependency] private readonly RoleSystem _role = default!;
private readonly Dictionary<NetUserId, PlayerInfo> _playerList = new(); private readonly Dictionary<NetUserId, PlayerInfo> _playerList = new();
@@ -102,10 +104,10 @@ namespace Content.Server.Administration.Systems
private void OnRoleEvent(RoleEvent ev) private void OnRoleEvent(RoleEvent ev)
{ {
if (!ev.Role.Antagonist || ev.Role.Mind.Session == null) if (!ev.Antagonist || ev.Mind.Session == null)
return; return;
UpdatePlayerList(ev.Role.Mind.Session); UpdatePlayerList(ev.Mind.Session);
} }
private void OnAdminPermsChanged(AdminPermsChangedEventArgs obj) private void OnAdminPermsChanged(AdminPermsChangedEventArgs obj)
@@ -169,12 +171,13 @@ namespace Content.Server.Administration.Systems
identityName = Identity.Name(session.AttachedEntity.Value, EntityManager); identityName = Identity.Name(session.AttachedEntity.Value, EntityManager);
} }
var mind = data.ContentData()?.Mind; var antag = false;
var startingRole = string.Empty;
var job = mind?.AllRoles.FirstOrDefault(role => role is Job); if (_minds.TryGetMind(session, out var mindId, out _))
var startingRole = job != null ? CultureInfo.CurrentCulture.TextInfo.ToTitleCase(job.Name) : string.Empty; {
antag = _role.MindIsAntagonist(mindId);
var antag = mind?.AllRoles.Any(r => r.Antagonist) ?? false; startingRole = _jobs.MindTryGetJobName(mindId);
}
var connected = session != null && session.Status is SessionStatus.Connected or SessionStatus.InGame; var connected = session != null && session.Status is SessionStatus.Connected or SessionStatus.InGame;

View File

@@ -1,4 +1,5 @@
using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules;
using Content.Server.Mind;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Server.Zombies; using Content.Server.Zombies;
using Content.Shared.Administration; using Content.Shared.Administration;
@@ -15,6 +16,7 @@ public sealed partial class AdminVerbSystem
[Dependency] private readonly TraitorRuleSystem _traitorRule = default!; [Dependency] private readonly TraitorRuleSystem _traitorRule = default!;
[Dependency] private readonly NukeopsRuleSystem _nukeopsRule = default!; [Dependency] private readonly NukeopsRuleSystem _nukeopsRule = default!;
[Dependency] private readonly PiratesRuleSystem _piratesRule = default!; [Dependency] private readonly PiratesRuleSystem _piratesRule = default!;
[Dependency] private readonly MindSystem _minds = default!;
// All antag verbs have names so invokeverb works. // All antag verbs have names so invokeverb works.
private void AddAntagVerbs(GetVerbsEvent<Verb> args) private void AddAntagVerbs(GetVerbsEvent<Verb> args)
@@ -38,10 +40,10 @@ public sealed partial class AdminVerbSystem
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Structures/Wallmounts/posters.rsi"), "poster5_contraband"), Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Structures/Wallmounts/posters.rsi"), "poster5_contraband"),
Act = () => Act = () =>
{ {
if (targetMindComp.Mind == null || targetMindComp.Mind.Session == null) if (!_minds.TryGetSession(targetMindComp.Mind, out var session))
return; return;
_traitorRule.MakeTraitor(targetMindComp.Mind.Session); _traitorRule.MakeTraitor(session);
}, },
Impact = LogImpact.High, Impact = LogImpact.High,
Message = Loc.GetString("admin-verb-make-traitor"), Message = Loc.GetString("admin-verb-make-traitor"),
@@ -70,10 +72,10 @@ public sealed partial class AdminVerbSystem
Icon = new SpriteSpecifier.Rsi(new("/Textures/Structures/Wallmounts/signs.rsi"), "radiation"), Icon = new SpriteSpecifier.Rsi(new("/Textures/Structures/Wallmounts/signs.rsi"), "radiation"),
Act = () => Act = () =>
{ {
if (targetMindComp.Mind == null || targetMindComp.Mind.Session == null) if (!_minds.TryGetMind(args.Target, out var mindId, out var mind))
return; return;
_nukeopsRule.MakeLoneNukie(targetMindComp.Mind); _nukeopsRule.MakeLoneNukie(mindId, mind);
}, },
Impact = LogImpact.High, Impact = LogImpact.High,
Message = Loc.GetString("admin-verb-make-nuclear-operative"), Message = Loc.GetString("admin-verb-make-nuclear-operative"),
@@ -87,10 +89,10 @@ public sealed partial class AdminVerbSystem
Icon = new SpriteSpecifier.Rsi(new("/Textures/Clothing/Head/Hats/pirate.rsi"), "icon"), Icon = new SpriteSpecifier.Rsi(new("/Textures/Clothing/Head/Hats/pirate.rsi"), "icon"),
Act = () => Act = () =>
{ {
if (targetMindComp.Mind == null || targetMindComp.Mind.Session == null) if (!_minds.TryGetMind(args.Target, out var mindId, out var mind))
return; return;
_piratesRule.MakePirate(targetMindComp.Mind); _piratesRule.MakePirate(mindId, mind);
}, },
Impact = LogImpact.High, Impact = LogImpact.High,
Message = Loc.GetString("admin-verb-make-pirate"), Message = Loc.GetString("admin-verb-make-pirate"),

View File

@@ -11,7 +11,6 @@ using Content.Server.Ghost.Roles;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Mind.Commands; using Content.Server.Mind.Commands;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Server.Players;
using Content.Server.Prayer; using Content.Server.Prayer;
using Content.Server.Xenoarchaeology.XenoArtifacts; using Content.Server.Xenoarchaeology.XenoArtifacts;
using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
@@ -262,11 +261,10 @@ namespace Content.Server.Administration.Systems
{ {
MakeSentientCommand.MakeSentient(args.Target, EntityManager); MakeSentientCommand.MakeSentient(args.Target, EntityManager);
var mind = player.ContentData()?.Mind; if (!_minds.TryGetMind(player, out var mindId, out var mind))
if (mind == null)
return; return;
_mindSystem.TransferTo(mind, args.Target, ghostCheckOverride: true); _mindSystem.TransferTo(mindId, args.Target, ghostCheckOverride: true, mind: mind);
}, },
Impact = LogImpact.High, Impact = LogImpact.High,
ConfirmationPopup = true ConfirmationPopup = true

View File

@@ -8,7 +8,7 @@ using System.Threading.Tasks;
using Content.Server.Administration.Managers; using Content.Server.Administration.Managers;
using Content.Server.Discord; using Content.Server.Discord;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Players; using Content.Server.Mind;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using JetBrains.Annotations; using JetBrains.Annotations;
@@ -31,6 +31,7 @@ namespace Content.Server.Administration.Systems
[Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPlayerLocator _playerLocator = default!; [Dependency] private readonly IPlayerLocator _playerLocator = default!;
[Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly GameTicker _gameTicker = default!;
[Dependency] private readonly MindSystem _minds = default!;
private ISawmill _sawmill = default!; private ISawmill _sawmill = default!;
private readonly HttpClient _httpClient = new(); private readonly HttpClient _httpClient = new();
@@ -221,8 +222,6 @@ namespace Content.Server.Administration.Systems
return; return;
} }
var characterName = _playerManager.GetPlayerData(userId).ContentData()?.Mind?.CharacterName;
var linkToPrevious = string.Empty; var linkToPrevious = string.Empty;
// If we have all the data required, we can link to the embed of the previous round or embed that was too long // If we have all the data required, we can link to the embed of the previous round or embed that was too long
@@ -238,6 +237,7 @@ namespace Content.Server.Administration.Systems
} }
} }
var characterName = _minds.GetCharacterName(userId);
existingEmbed = (null, lookup.Username, linkToPrevious, characterName, _gameTicker.RunLevel); existingEmbed = (null, lookup.Username, linkToPrevious, characterName, _gameTicker.RunLevel);
} }

View File

@@ -1,3 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Ame.Components; using Content.Server.Ame.Components;
using Content.Server.Chat.Managers; using Content.Server.Chat.Managers;
@@ -15,8 +17,6 @@ using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Content.Server.Ame.EntitySystems; namespace Content.Server.Ame.EntitySystems;
@@ -201,7 +201,7 @@ public sealed class AmeControllerSystem : EntitySystem
safeLimit = group.CoreCount * 2; safeLimit = group.CoreCount * 2;
if (oldValue <= safeLimit && value > safeLimit) if (oldValue <= safeLimit && value > safeLimit)
_chatManager.SendAdminAlert(user.Value, $"increased AME over safe limit to {controller.InjectionAmount}", mindContainer); _chatManager.SendAdminAlert(user.Value, $"increased AME over safe limit to {controller.InjectionAmount}");
} }
public void AdjustInjectionAmount(EntityUid uid, int delta, int min = 0, int max = int.MaxValue, EntityUid? user = null, AmeControllerComponent? controller = null) public void AdjustInjectionAmount(EntityUid uid, int delta, int min = 0, int max = int.MaxValue, EntityUid? user = null, AmeControllerComponent? controller = null)

View File

@@ -8,9 +8,7 @@ using Content.Server.Mind;
using Content.Shared.Body.Components; using Content.Shared.Body.Components;
using Content.Shared.Body.Organ; using Content.Shared.Body.Organ;
using Content.Shared.Body.Part; using Content.Shared.Body.Part;
using Content.Shared.Body.Prototypes;
using Content.Shared.Body.Systems; using Content.Shared.Body.Systems;
using Content.Shared.Coordinates;
using Content.Shared.Humanoid; using Content.Shared.Humanoid;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.Movement.Events; using Content.Shared.Movement.Events;
@@ -106,14 +104,10 @@ public sealed class BodySystem : SharedBodySystem
private void OnRelayMoveInput(EntityUid uid, BodyComponent component, ref MoveInputEvent args) private void OnRelayMoveInput(EntityUid uid, BodyComponent component, ref MoveInputEvent args)
{ {
if (_mobState.IsDead(uid) && _mindSystem.TryGetMind(uid, out var mind)) if (_mobState.IsDead(uid) && _mindSystem.TryGetMind(uid, out var mindId, out var mind))
{ {
if (!mind.TimeOfDeath.HasValue) mind.TimeOfDeath ??= _gameTiming.RealTime;
{ _ticker.OnGhostAttempt(mindId, true, mind: mind);
mind.TimeOfDeath = _gameTiming.RealTime;
}
_ticker.OnGhostAttempt(mind, true);
} }
} }

View File

@@ -5,7 +5,6 @@ using Content.Server.Mind.Components;
using Content.Shared.Body.Components; using Content.Shared.Body.Components;
using Content.Shared.Body.Events; using Content.Shared.Body.Events;
using Content.Shared.Body.Organ; using Content.Shared.Body.Organ;
using Content.Shared.Body.Part;
using Content.Shared.Movement.Components; using Content.Shared.Movement.Components;
using Content.Shared.Movement.Systems; using Content.Shared.Movement.Systems;
@@ -57,10 +56,10 @@ namespace Content.Server.Body.Systems
_movementSpeed.ChangeBaseSpeed(newEntity, 0, 0 , 0, move); _movementSpeed.ChangeBaseSpeed(newEntity, 0, 0 , 0, move);
} }
if (!_mindSystem.TryGetMind(oldEntity, out var mind, oldMind)) if (!_mindSystem.TryGetMind(oldEntity, out var mindId, out var mind))
return; return;
_mindSystem.TransferTo(mind, newEntity); _mindSystem.TransferTo(mindId, newEntity, mind: mind);
} }
} }
} }

View File

@@ -1,5 +1,6 @@
using Content.Server.Mind.Components; using Content.Server.Mind;
using Content.Server.Roles; using Content.Server.Roles;
using Content.Server.Roles.Jobs;
using Content.Shared.CharacterInfo; using Content.Shared.CharacterInfo;
using Content.Shared.Objectives; using Content.Shared.Objectives;
@@ -7,6 +8,10 @@ namespace Content.Server.CharacterInfo;
public sealed class CharacterInfoSystem : EntitySystem public sealed class CharacterInfoSystem : EntitySystem
{ {
[Dependency] private readonly JobSystem _jobs = default!;
[Dependency] private readonly MindSystem _minds = default!;
[Dependency] private readonly RoleSystem _roles = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -25,10 +30,8 @@ public sealed class CharacterInfoSystem : EntitySystem
var conditions = new Dictionary<string, List<ConditionInfo>>(); var conditions = new Dictionary<string, List<ConditionInfo>>();
var jobTitle = "No Profession"; var jobTitle = "No Profession";
var briefing = "!!ERROR: No Briefing!!"; //should never show on the UI unless there's an issue var briefing = "!!ERROR: No Briefing!!"; //should never show on the UI unless there's an issue
if (TryComp<MindContainerComponent>(entity, out var mindContainerComponent) && mindContainerComponent.Mind != null) if (_minds.TryGetMind(entity, out var mindId, out var mind))
{ {
var mind = mindContainerComponent.Mind;
// Get objectives // Get objectives
foreach (var objective in mind.AllObjectives) foreach (var objective in mind.AllObjectives)
{ {
@@ -41,17 +44,11 @@ public sealed class CharacterInfoSystem : EntitySystem
} }
} }
// Get job title if (_jobs.MindTryGetJobName(mindId, out var jobName))
foreach (var role in mind.AllRoles) jobTitle = jobName;
{
if (role.GetType() != typeof(Job)) continue;
jobTitle = role.Name;
break;
}
// Get briefing // Get briefing
briefing = mind.Briefing; briefing = _roles.MindGetBriefing(mindId) ?? string.Empty;
} }
RaiseNetworkEvent(new CharacterInfoEvent(entity, jobTitle, conditions, briefing), args.SenderSession); RaiseNetworkEvent(new CharacterInfoEvent(entity, jobTitle, conditions, briefing), args.SenderSession);

View File

@@ -1,5 +1,5 @@
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Players; using Content.Server.Mind;
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Console; using Robust.Shared.Console;
@@ -26,25 +26,27 @@ namespace Content.Server.Chat.Commands
if (player.Status != SessionStatus.InGame || player.AttachedEntity == null) if (player.Status != SessionStatus.InGame || player.AttachedEntity == null)
return; return;
var mind = player.ContentData()?.Mind;
var minds = IoCManager.Resolve<IEntityManager>().System<MindSystem>();
// This check also proves mind not-null for at the end when the mob is ghosted. // This check also proves mind not-null for at the end when the mob is ghosted.
if (mind?.OwnedEntity is not { Valid: true } victim) if (!minds.TryGetMind(player, out var mindId, out var mind) ||
mind.OwnedEntity is not { Valid: true } victim)
{ {
shell.WriteLine("You don't have a mind!"); shell.WriteLine("You don't have a mind!");
return; return;
} }
var gameTicker = EntitySystem.Get<GameTicker>(); var gameTicker = EntitySystem.Get<GameTicker>();
var suicideSystem = EntitySystem.Get<SuicideSystem>(); var suicideSystem = EntitySystem.Get<SuicideSystem>();
if (suicideSystem.Suicide(victim)) if (suicideSystem.Suicide(victim))
{ {
// Prevent the player from returning to the body. // Prevent the player from returning to the body.
// Note that mind cannot be null because otherwise victim would be null. // Note that mind cannot be null because otherwise victim would be null.
gameTicker.OnGhostAttempt(mind, false); gameTicker.OnGhostAttempt(mindId, false, mind: mind);
return; return;
} }
if (gameTicker.OnGhostAttempt(mind, true)) if (gameTicker.OnGhostAttempt(mindId, true, mind: mind))
return; return;
shell.WriteLine("You can't ghost right now."); shell.WriteLine("You can't ghost right now.");

View File

@@ -2,17 +2,15 @@ using System.Linq;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Administration.Managers; using Content.Server.Administration.Managers;
using Content.Server.Administration.Systems; using Content.Server.Administration.Systems;
using Content.Server.Mind.Components; using Content.Server.Mind;
using Content.Server.MoMMI; using Content.Server.MoMMI;
using Content.Server.Preferences.Managers; using Content.Server.Preferences.Managers;
using Content.Server.Station.Systems;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Chat; using Content.Shared.Chat;
using Content.Shared.Database; using Content.Shared.Database;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Map;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Replays; using Robust.Shared.Replays;
@@ -116,19 +114,19 @@ namespace Content.Server.Chat.Managers
ChatMessageToMany(ChatChannel.AdminAlert, message, wrappedMessage, default, false, true, clients); ChatMessageToMany(ChatChannel.AdminAlert, message, wrappedMessage, default, false, true, clients);
} }
public void SendAdminAlert(EntityUid player, string message, MindContainerComponent? mindContainerComponent = null) public void SendAdminAlert(EntityUid player, string message)
{ {
if ((mindContainerComponent == null && !_entityManager.TryGetComponent(player, out mindContainerComponent)) || !mindContainerComponent.HasMind) var mindSystem = _entityManager.System<MindSystem>();
if (!mindSystem.TryGetMind(player, out var mindId, out var mind))
{ {
SendAdminAlert(message); SendAdminAlert(message);
return; return;
} }
var adminSystem = _entityManager.System<AdminSystem>(); var adminSystem = _entityManager.System<AdminSystem>();
var antag = mindContainerComponent.Mind!.UserId != null var antag = mind.UserId != null && (adminSystem.GetCachedPlayerInfo(mind.UserId.Value)?.Antag ?? false);
&& (adminSystem.GetCachedPlayerInfo(mindContainerComponent.Mind!.UserId.Value)?.Antag ?? false);
SendAdminAlert($"{mindContainerComponent.Mind!.Session?.Name}{(antag ? " (ANTAG)" : "")} {message}"); SendAdminAlert($"{mind.Session?.Name}{(antag ? " (ANTAG)" : "")} {message}");
} }
public void SendHookOOC(string sender, string message) public void SendHookOOC(string sender, string message)

View File

@@ -1,4 +1,3 @@
using Content.Server.Mind.Components;
using Content.Shared.Chat; using Content.Shared.Chat;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Network; using Robust.Shared.Network;
@@ -24,7 +23,7 @@ namespace Content.Server.Chat.Managers
void SendHookOOC(string sender, string message); void SendHookOOC(string sender, string message);
void SendAdminAnnouncement(string message); void SendAdminAnnouncement(string message);
void SendAdminAlert(string message); void SendAdminAlert(string message);
void SendAdminAlert(EntityUid player, string message, MindContainerComponent? mindContainerComponent = null); void SendAdminAlert(EntityUid player, string message);
void ChatMessageToOne(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, void ChatMessageToOne(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat,
INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0); INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0);

View File

@@ -1,4 +1,5 @@
using Content.Server.EUI; using Content.Server.EUI;
using Content.Server.Mind;
using Content.Shared.Cloning; using Content.Shared.Cloning;
using Content.Shared.Eui; using Content.Shared.Eui;
@@ -6,11 +7,13 @@ namespace Content.Server.Cloning
{ {
public sealed class AcceptCloningEui : BaseEui public sealed class AcceptCloningEui : BaseEui
{ {
private readonly EntityUid _mindId;
private readonly MindComponent _mind;
private readonly CloningSystem _cloningSystem; private readonly CloningSystem _cloningSystem;
private readonly Mind.Mind _mind;
public AcceptCloningEui(Mind.Mind mind, CloningSystem cloningSys) public AcceptCloningEui(EntityUid mindId, MindComponent mind, CloningSystem cloningSys)
{ {
_mindId = mindId;
_mind = mind; _mind = mind;
_cloningSystem = cloningSys; _cloningSystem = cloningSys;
} }
@@ -26,7 +29,7 @@ namespace Content.Server.Cloning
return; return;
} }
_cloningSystem.TransferMindToClone(_mind); _cloningSystem.TransferMindToClone(_mindId, _mind);
Close(); Close();
} }
} }

View File

@@ -1,23 +1,23 @@
using System.Linq; using System.Linq;
using JetBrains.Annotations;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Medical.Components;
using Content.Server.Cloning.Components; using Content.Server.Cloning.Components;
using Content.Server.DeviceLinking.Systems; using Content.Server.DeviceLinking.Systems;
using Content.Server.Medical.Components;
using Content.Server.Mind;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Mind.Components;
using Content.Server.UserInterface;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Robust.Server.GameObjects; using Content.Server.UserInterface;
using Robust.Server.Player;
using Content.Shared.Cloning.CloningConsole;
using Content.Shared.Cloning; using Content.Shared.Cloning;
using Content.Shared.Cloning.CloningConsole;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.DeviceLinking; using Content.Shared.DeviceLinking;
using Content.Shared.DeviceLinking.Events; using Content.Shared.DeviceLinking.Events;
using Content.Shared.IdentityManagement; using Content.Shared.IdentityManagement;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Server.Player;
namespace Content.Server.Cloning namespace Content.Server.Cloning
{ {
@@ -31,6 +31,8 @@ namespace Content.Server.Cloning
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly PowerReceiverSystem _powerReceiverSystem = default!; [Dependency] private readonly PowerReceiverSystem _powerReceiverSystem = default!;
[Dependency] private readonly MindSystem _mindSystem = default!;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -162,12 +164,10 @@ namespace Content.Server.Cloning
if (body is null) if (body is null)
return; return;
if (!TryComp<MindContainerComponent>(body, out var mindComp)) if (!_mindSystem.TryGetMind(body.Value, out var mindId, out var mind))
return; return;
var mind = mindComp.Mind; if (mind.UserId.HasValue == false || mind.Session == null)
if (mind == null || mind.UserId.HasValue == false || mind.Session == null)
return; return;
if (_cloningSystem.TryCloning(cloningPodUid, body.Value, mind, cloningPod, scannerComp.CloningFailChanceMultiplier)) if (_cloningSystem.TryCloning(cloningPodUid, body.Value, mind, cloningPod, scannerComp.CloningFailChanceMultiplier))
@@ -212,15 +212,15 @@ namespace Content.Server.Cloning
{ {
scanBodyInfo = MetaData(scanBody.Value).EntityName; scanBodyInfo = MetaData(scanBody.Value).EntityName;
TryComp<MindContainerComponent>(scanBody, out var mindComp);
if (!_mobStateSystem.IsDead(scanBody.Value)) if (!_mobStateSystem.IsDead(scanBody.Value))
{ {
clonerStatus = ClonerStatus.ScannerOccupantAlive; clonerStatus = ClonerStatus.ScannerOccupantAlive;
} }
else else
{ {
if (mindComp == null || mindComp.Mind == null || mindComp.Mind.UserId == null || !_playerManager.TryGetSessionById(mindComp.Mind.UserId.Value, out _)) if (!_mindSystem.TryGetMind(scanBody.Value, out _, out var mind) ||
mind.UserId == null ||
!_playerManager.TryGetSessionById(mind.UserId.Value, out _))
{ {
clonerStatus = ClonerStatus.NoMindDetected; clonerStatus = ClonerStatus.NoMindDetected;
} }

View File

@@ -1,41 +1,40 @@
using Content.Shared.GameTicking; using Content.Server.Atmos.EntitySystems;
using Content.Shared.Damage; using Content.Server.Chat.Systems;
using Content.Shared.Examine; using Content.Server.Cloning.Components;
using Content.Shared.Cloning; using Content.Server.Construction;
using Content.Server.DeviceLinking.Systems;
using Content.Server.EUI;
using Content.Server.Fluids.EntitySystems;
using Content.Server.Humanoid;
using Content.Server.Jobs;
using Content.Server.Materials;
using Content.Server.Mind;
using Content.Server.Mind.Components;
using Content.Server.Popups;
using Content.Server.Power.EntitySystems;
using Content.Server.Roles.Jobs;
using Content.Server.Traits.Assorted;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Server.Cloning.Components;
using Content.Server.Mind.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Atmos.EntitySystems;
using Content.Server.EUI;
using Content.Server.Humanoid;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Server.Fluids.EntitySystems; using Content.Shared.Cloning;
using Content.Server.Chat.Systems; using Content.Shared.Damage;
using Content.Server.Construction;
using Content.Server.DeviceLinking.Events;
using Content.Server.DeviceLinking.Systems;
using Content.Server.Materials;
using Content.Server.Jobs;
using Content.Shared.DeviceLinking.Events; using Content.Shared.DeviceLinking.Events;
using Content.Shared.Emag.Components; using Content.Shared.Emag.Components;
using Content.Server.Mind; using Content.Shared.Emag.Systems;
using Content.Shared.Examine;
using Content.Shared.GameTicking;
using Content.Shared.Humanoid; using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes; using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Zombies;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Robust.Server.GameObjects;
using Robust.Server.Containers; using Robust.Server.Containers;
using Robust.Server.GameObjects;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
using Content.Shared.Emag.Systems; using Robust.Shared.Prototypes;
using Content.Server.Popups; using Robust.Shared.Random;
using Content.Server.Traits.Assorted;
namespace Content.Server.Cloning namespace Content.Server.Cloning
{ {
@@ -62,8 +61,9 @@ namespace Content.Server.Cloning
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly MindSystem _mindSystem = default!; [Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly MetaDataSystem _metaSystem = default!; [Dependency] private readonly MetaDataSystem _metaSystem = default!;
[Dependency] private readonly JobSystem _jobs = default!;
public readonly Dictionary<Mind.Mind, EntityUid> ClonesWaitingForMind = new(); public readonly Dictionary<MindComponent, EntityUid> ClonesWaitingForMind = new();
public const float EasyModeCloningCost = 0.7f; public const float EasyModeCloningCost = 0.7f;
public override void Initialize() public override void Initialize()
@@ -102,7 +102,7 @@ namespace Content.Server.Cloning
args.AddPercentageUpgrade("cloning-pod-component-upgrade-biomass-requirement", component.BiomassRequirementMultiplier); args.AddPercentageUpgrade("cloning-pod-component-upgrade-biomass-requirement", component.BiomassRequirementMultiplier);
} }
internal void TransferMindToClone(Mind.Mind mind) internal void TransferMindToClone(EntityUid mindId, MindComponent mind)
{ {
if (!ClonesWaitingForMind.TryGetValue(mind, out var entity) || if (!ClonesWaitingForMind.TryGetValue(mind, out var entity) ||
!EntityManager.EntityExists(entity) || !EntityManager.EntityExists(entity) ||
@@ -110,8 +110,8 @@ namespace Content.Server.Cloning
mindComp.Mind != null) mindComp.Mind != null)
return; return;
_mindSystem.TransferTo(mind, entity, ghostCheckOverride: true); _mindSystem.TransferTo(mindId, entity, ghostCheckOverride: true, mind: mind);
_mindSystem.UnVisit(mind); _mindSystem.UnVisit(mindId, mind);
ClonesWaitingForMind.Remove(mind); ClonesWaitingForMind.Remove(mind);
} }
@@ -154,7 +154,7 @@ namespace Content.Server.Cloning
args.PushMarkup(Loc.GetString("cloning-pod-biomass", ("number", _material.GetMaterialAmount(uid, component.RequiredMaterial)))); args.PushMarkup(Loc.GetString("cloning-pod-biomass", ("number", _material.GetMaterialAmount(uid, component.RequiredMaterial))));
} }
public bool TryCloning(EntityUid uid, EntityUid bodyToClone, Mind.Mind mind, CloningPodComponent? clonePod, float failChanceModifier = 1) public bool TryCloning(EntityUid uid, EntityUid bodyToClone, MindComponent mind, CloningPodComponent? clonePod, float failChanceModifier = 1)
{ {
if (!Resolve(uid, ref clonePod)) if (!Resolve(uid, ref clonePod))
return false; return false;
@@ -167,7 +167,7 @@ namespace Content.Server.Cloning
if (EntityManager.EntityExists(clone) && if (EntityManager.EntityExists(clone) &&
!_mobStateSystem.IsDead(clone) && !_mobStateSystem.IsDead(clone) &&
TryComp<MindContainerComponent>(clone, out var cloneMindComp) && TryComp<MindContainerComponent>(clone, out var cloneMindComp) &&
(cloneMindComp.Mind == null || cloneMindComp.Mind == mind)) (cloneMindComp.Mind == null || cloneMindComp.Mind == mind.Owner))
return false; // Mind already has clone return false; // Mind already has clone
ClonesWaitingForMind.Remove(mind); ClonesWaitingForMind.Remove(mind);
@@ -253,16 +253,16 @@ namespace Content.Server.Cloning
clonePod.BodyContainer.Insert(mob); clonePod.BodyContainer.Insert(mob);
ClonesWaitingForMind.Add(mind, mob); ClonesWaitingForMind.Add(mind, mob);
UpdateStatus(uid, CloningPodStatus.NoMind, clonePod); UpdateStatus(uid, CloningPodStatus.NoMind, clonePod);
_euiManager.OpenEui(new AcceptCloningEui(mind, this), client); var mindId = mind.Owner;
_euiManager.OpenEui(new AcceptCloningEui(mindId, mind, this), client);
AddComp<ActiveCloningPodComponent>(uid); AddComp<ActiveCloningPodComponent>(uid);
// TODO: Ideally, components like this should be on a mind entity so this isn't neccesary. // TODO: Ideally, components like this should be components on the mind entity so this isn't necessary.
// Remove this when 'mind entities' are added.
// Add on special job components to the mob. // Add on special job components to the mob.
if (mind.CurrentJob != null) if (_jobs.MindTryGetJob(mindId, out _, out var prototype))
{ {
foreach (var special in mind.CurrentJob.Prototype.Special) foreach (var special in prototype.Special)
{ {
if (special is AddComponentSpecial) if (special is AddComponentSpecial)
special.AfterEquip(mob); special.AfterEquip(mob);

View File

@@ -1,10 +1,12 @@
using Content.Server.Mind;
namespace Content.Server.Cloning.Components namespace Content.Server.Cloning.Components
{ {
[RegisterComponent] [RegisterComponent]
public sealed partial class BeingClonedComponent : Component public sealed partial class BeingClonedComponent : Component
{ {
[ViewVariables] [ViewVariables]
public Mind.Mind? Mind = default; public MindComponent? Mind = default;
[ViewVariables] [ViewVariables]
public EntityUid Parent; public EntityUid Parent;

View File

@@ -3,6 +3,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Content.Server.GameTicking.Presets; using Content.Server.GameTicking.Presets;
using Content.Server.Maps; using Content.Server.Maps;
using Content.Server.Mind;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
@@ -192,8 +193,11 @@ namespace Content.Server.GameTicking
} }
} }
public bool OnGhostAttempt(Mind.Mind mind, bool canReturnGlobal, bool viaCommand = false) public bool OnGhostAttempt(EntityUid mindId, bool canReturnGlobal, bool viaCommand = false, MindComponent? mind = null)
{ {
if (!Resolve(mindId, ref mind))
return false;
var playerEntity = mind.CurrentEntity; var playerEntity = mind.CurrentEntity;
if (playerEntity != null && viaCommand) if (playerEntity != null && viaCommand)
@@ -222,7 +226,7 @@ namespace Content.Server.GameTicking
if (mind.VisitingEntity != default) if (mind.VisitingEntity != default)
{ {
_mind.UnVisit(mind); _mind.UnVisit(mindId, mind: mind);
} }
var position = Exists(playerEntity) var position = Exists(playerEntity)
@@ -281,9 +285,9 @@ namespace Content.Server.GameTicking
_ghost.SetCanReturnToBody(ghostComponent, canReturn); _ghost.SetCanReturnToBody(ghostComponent, canReturn);
if (canReturn) if (canReturn)
_mind.Visit(mind, ghost); _mind.Visit(mindId, ghost, mind);
else else
_mind.TransferTo(mind, ghost); _mind.TransferTo(mindId, ghost, mind: mind);
return true; return true;
} }
@@ -308,11 +312,11 @@ namespace Content.Server.GameTicking
public sealed class GhostAttemptHandleEvent : HandledEntityEventArgs public sealed class GhostAttemptHandleEvent : HandledEntityEventArgs
{ {
public Mind.Mind Mind { get; } public MindComponent Mind { get; }
public bool CanReturnGlobal { get; } public bool CanReturnGlobal { get; }
public bool Result { get; set; } public bool Result { get; set; }
public GhostAttemptHandleEvent(Mind.Mind mind, bool canReturnGlobal) public GhostAttemptHandleEvent(MindComponent mind, bool canReturnGlobal)
{ {
Mind = mind; Mind = mind;
CanReturnGlobal = canReturnGlobal; CanReturnGlobal = canReturnGlobal;

View File

@@ -8,7 +8,6 @@ using Robust.Server.Player;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using PlayerData = Content.Server.Players.PlayerData;
namespace Content.Server.GameTicking namespace Content.Server.GameTicking
{ {
@@ -27,7 +26,7 @@ namespace Content.Server.GameTicking
{ {
var session = args.Session; var session = args.Session;
if (_mind.TryGetMind(session.UserId, out var mind)) if (_mind.TryGetMind(session.UserId, out var mindId, out var mind))
{ {
if (args.OldStatus == SessionStatus.Connecting && args.NewStatus == SessionStatus.Connected) if (args.OldStatus == SessionStatus.Connecting && args.NewStatus == SessionStatus.Connected)
mind.Session = session; mind.Session = session;
@@ -35,7 +34,7 @@ namespace Content.Server.GameTicking
DebugTools.Assert(mind.Session == session); DebugTools.Assert(mind.Session == session);
} }
DebugTools.Assert(session.GetMind() == mind); DebugTools.Assert(session.GetMind() == mindId);
switch (args.NewStatus) switch (args.NewStatus)
{ {
@@ -47,7 +46,7 @@ namespace Content.Server.GameTicking
if (session.Data.ContentDataUncast == null) if (session.Data.ContentDataUncast == null)
{ {
var data = new PlayerData(session.UserId, args.Session.Name); var data = new PlayerData(session.UserId, args.Session.Name);
data.Mind = mind; data.Mind = mindId;
session.Data.ContentDataUncast = data; session.Data.ContentDataUncast = data;
} }

View File

@@ -1,25 +1,25 @@
using System.Linq;
using Content.Server.Announcements; using Content.Server.Announcements;
using Content.Server.Discord;
using Content.Server.GameTicking.Events; using Content.Server.GameTicking.Events;
using Content.Server.Ghost; using Content.Server.Ghost;
using Content.Server.Maps; using Content.Server.Maps;
using Content.Server.Mind;
using Content.Server.Players; using Content.Server.Players;
using Content.Shared.Database;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Preferences; using Content.Shared.Preferences;
using JetBrains.Annotations; using JetBrains.Annotations;
using Prometheus; using Prometheus;
using Robust.Server.Maps; using Robust.Server.Maps;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Asynchronous;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using System.Linq;
using Content.Server.Discord;
using Content.Shared.Database;
using Robust.Shared.Asynchronous;
using PlayerData = Content.Server.Players.PlayerData;
namespace Content.Server.GameTicking namespace Content.Server.GameTicking
{ {
@@ -329,8 +329,8 @@ namespace Content.Server.GameTicking
//Generate a list of basic player info to display in the end round summary. //Generate a list of basic player info to display in the end round summary.
var listOfPlayerInfo = new List<RoundEndMessageEvent.RoundEndPlayerInfo>(); var listOfPlayerInfo = new List<RoundEndMessageEvent.RoundEndPlayerInfo>();
// Grab the great big book of all the Minds, we'll need them for this. // Grab the great big book of all the Minds, we'll need them for this.
var allMinds = _mindTracker.AllMinds; var allMinds = EntityQueryEnumerator<MindComponent>();
foreach (var mind in allMinds) while (allMinds.MoveNext(out var mindId, out var mind))
{ {
// TODO don't list redundant observer roles? // TODO don't list redundant observer roles?
// I.e., if a player was an observer ghost, then a hamster ghost role, maybe just list hamster and not // I.e., if a player was an observer ghost, then a hamster ghost role, maybe just list hamster and not
@@ -338,7 +338,7 @@ namespace Content.Server.GameTicking
var userId = mind.UserId ?? mind.OriginalOwnerUserId; var userId = mind.UserId ?? mind.OriginalOwnerUserId;
var connected = false; var connected = false;
var observer = mind.AllRoles.Any(role => role is ObserverRole); var observer = HasComp<ObserverRoleComponent>(mindId);
// Continuing // Continuing
if (userId != null && _playerManager.ValidSessionId(userId.Value)) if (userId != null && _playerManager.ValidSessionId(userId.Value))
{ {
@@ -350,7 +350,8 @@ namespace Content.Server.GameTicking
contentPlayerData = playerData.ContentData(); contentPlayerData = playerData.ContentData();
} }
// Finish // Finish
var antag = mind.AllRoles.Any(role => role.Antagonist);
var antag = _roles.MindIsAntagonist(mindId);
var playerIcName = "Unknown"; var playerIcName = "Unknown";
@@ -363,6 +364,8 @@ namespace Content.Server.GameTicking
if (Exists(entity)) if (Exists(entity))
_pvsOverride.AddGlobalOverride(entity.Value, recursive: true); _pvsOverride.AddGlobalOverride(entity.Value, recursive: true);
var roles = _roles.MindGetAllRoles(mindId);
var playerEndRoundInfo = new RoundEndMessageEvent.RoundEndPlayerInfo() var playerEndRoundInfo = new RoundEndMessageEvent.RoundEndPlayerInfo()
{ {
// Note that contentPlayerData?.Name sticks around after the player is disconnected. // Note that contentPlayerData?.Name sticks around after the player is disconnected.
@@ -372,8 +375,8 @@ namespace Content.Server.GameTicking
PlayerICName = playerIcName, PlayerICName = playerIcName,
PlayerEntityUid = entity, PlayerEntityUid = entity,
Role = antag Role = antag
? mind.AllRoles.First(role => role.Antagonist).Name ? roles.First(role => role.Antagonist).Name
: mind.AllRoles.FirstOrDefault()?.Name ?? Loc.GetString("game-ticker-unknown-role"), : roles.FirstOrDefault().Name ?? Loc.GetString("game-ticker-unknown-role"),
Antag = antag, Antag = antag,
Observer = observer, Observer = observer,
Connected = connected Connected = connected
@@ -496,6 +499,13 @@ namespace Content.Server.GameTicking
PlayerJoinLobby(player); PlayerJoinLobby(player);
} }
// Round restart cleanup event, so entity systems can reset.
var ev = new RoundRestartCleanupEvent();
RaiseLocalEvent(ev);
// So clients' entity systems can clean up too...
RaiseNetworkEvent(ev, Filter.Broadcast());
// Delete all entities. // Delete all entities.
foreach (var entity in EntityManager.GetEntities().ToArray()) foreach (var entity in EntityManager.GetEntities().ToArray())
{ {
@@ -530,13 +540,6 @@ namespace Content.Server.GameTicking
_allPreviousGameRules.Clear(); _allPreviousGameRules.Clear();
// Round restart cleanup event, so entity systems can reset.
var ev = new RoundRestartCleanupEvent();
RaiseLocalEvent(ev);
// So clients' entity systems can clean up too...
RaiseNetworkEvent(ev, Filter.Broadcast());
DisallowLateJoin = false; DisallowLateJoin = false;
_playerGameStatuses.Clear(); _playerGameStatuses.Clear();
foreach (var session in _playerManager.ServerSessions) foreach (var session in _playerManager.ServerSessions)

View File

@@ -4,6 +4,7 @@ using System.Numerics;
using Content.Server.Administration.Managers; using Content.Server.Administration.Managers;
using Content.Server.Ghost; using Content.Server.Ghost;
using Content.Server.Players; using Content.Server.Players;
using Content.Server.Roles.Jobs;
using Content.Server.Spawners.Components; using Content.Server.Spawners.Components;
using Content.Server.Speech.Components; using Content.Server.Speech.Components;
using Content.Server.Station.Components; using Content.Server.Station.Components;
@@ -18,13 +19,13 @@ using Robust.Shared.Network;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Job = Content.Server.Roles.Job;
namespace Content.Server.GameTicking namespace Content.Server.GameTicking
{ {
public sealed partial class GameTicker public sealed partial class GameTicker
{ {
[Dependency] private readonly IAdminManager _adminManager = default!; [Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly JobSystem _jobs = default!;
[ValidatePrototypeId<EntityPrototype>] [ValidatePrototypeId<EntityPrototype>]
private const string ObserverPrototypeName = "MobObserver"; private const string ObserverPrototypeName = "MobObserver";
@@ -186,12 +187,12 @@ namespace Content.Server.GameTicking
_mind.SetUserId(newMind, data.UserId); _mind.SetUserId(newMind, data.UserId);
var jobPrototype = _prototypeManager.Index<JobPrototype>(jobId); var jobPrototype = _prototypeManager.Index<JobPrototype>(jobId);
var job = new Job(newMind, jobPrototype); var job = new JobComponent { PrototypeId = jobId };
_mind.AddRole(newMind, job); _roles.MindAddRole(newMind, job);
var jobName = _jobs.MindTryGetJobName(newMind);
_playTimeTrackings.PlayerRolesChanged(player); _playTimeTrackings.PlayerRolesChanged(player);
var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(station, job, character); var mobMaybe = _stationSpawning.SpawnPlayerCharacterOnStation(station, job, character);
DebugTools.AssertNotNull(mobMaybe); DebugTools.AssertNotNull(mobMaybe);
var mob = mobMaybe!.Value; var mob = mobMaybe!.Value;
@@ -204,7 +205,7 @@ namespace Content.Server.GameTicking
Loc.GetString( Loc.GetString(
"latejoin-arrival-announcement", "latejoin-arrival-announcement",
("character", MetaData(mob).EntityName), ("character", MetaData(mob).EntityName),
("job", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(job.Name)) ("job", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(jobName))
), Loc.GetString("latejoin-arrival-sender"), ), Loc.GetString("latejoin-arrival-sender"),
playDefaultSound: false); playDefaultSound: false);
} }
@@ -217,9 +218,9 @@ namespace Content.Server.GameTicking
_stationJobs.TryAssignJob(station, jobPrototype); _stationJobs.TryAssignJob(station, jobPrototype);
if (lateJoin) if (lateJoin)
_adminLogger.Add(LogType.LateJoin, LogImpact.Medium, $"Player {player.Name} late joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {job.Name:jobName}."); _adminLogger.Add(LogType.LateJoin, LogImpact.Medium, $"Player {player.Name} late joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}.");
else else
_adminLogger.Add(LogType.RoundStartJoin, LogImpact.Medium, $"Player {player.Name} joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {job.Name:jobName}."); _adminLogger.Add(LogType.RoundStartJoin, LogImpact.Medium, $"Player {player.Name} joined as {character.Name:characterName} on station {Name(station):stationName} with {ToPrettyString(mob):entity} as a {jobName:jobName}.");
// Make sure they're aware of extended access. // Make sure they're aware of extended access.
if (Comp<StationJobsComponent>(station).ExtendedAccess if (Comp<StationJobsComponent>(station).ExtendedAccess
@@ -305,15 +306,15 @@ namespace Content.Server.GameTicking
if (mind == null) if (mind == null)
{ {
mind = _mind.CreateMind(player.UserId); mind = _mind.CreateMind(player.UserId);
_mind.SetUserId(mind, player.UserId); _mind.SetUserId(mind.Value, player.UserId);
_mind.AddRole(mind, new ObserverRole(mind)); _roles.MindAddRole(mind.Value, new ObserverRoleComponent());
} }
var name = GetPlayerProfile(player).Name; var name = GetPlayerProfile(player).Name;
var ghost = SpawnObserverMob(); var ghost = SpawnObserverMob();
_metaData.SetEntityName(ghost, name); _metaData.SetEntityName(ghost, name);
_ghost.SetCanReturnToBody(ghost, false); _ghost.SetCanReturnToBody(ghost, false);
_mind.TransferTo(mind, ghost); _mind.TransferTo(mind.Value, ghost);
} }
#region Mob Spawning Helpers #region Mob Spawning Helpers

View File

@@ -8,6 +8,7 @@ using Content.Server.Maps;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Players.PlayTimeTracking; using Content.Server.Players.PlayTimeTracking;
using Content.Server.Preferences.Managers; using Content.Server.Preferences.Managers;
using Content.Server.Roles;
using Content.Server.ServerUpdates; using Content.Server.ServerUpdates;
using Content.Server.Shuttles.Systems; using Content.Server.Shuttles.Systems;
using Content.Server.Station.Systems; using Content.Server.Station.Systems;
@@ -21,14 +22,14 @@ using Robust.Server.GameObjects;
using Robust.Server.GameStates; using Robust.Server.GameStates;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Console; using Robust.Shared.Console;
#if EXCEPTION_TOLERANCE
using Robust.Shared.Exceptions;
#endif
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
#if EXCEPTION_TOLERANCE
using Robust.Shared.Exceptions;
#endif
namespace Content.Server.GameTicking namespace Content.Server.GameTicking
{ {
@@ -57,7 +58,6 @@ namespace Content.Server.GameTicking
[Dependency] private readonly MapLoaderSystem _map = default!; [Dependency] private readonly MapLoaderSystem _map = default!;
[Dependency] private readonly GhostSystem _ghost = default!; [Dependency] private readonly GhostSystem _ghost = default!;
[Dependency] private readonly MindSystem _mind = default!; [Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly MindTrackerSystem _mindTracker = default!;
[Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly PlayTimeTrackingSystem _playTimeTrackings = default!; [Dependency] private readonly PlayTimeTrackingSystem _playTimeTrackings = default!;
[Dependency] private readonly PvsOverrideSystem _pvsOverride = default!; [Dependency] private readonly PvsOverrideSystem _pvsOverride = default!;
@@ -67,6 +67,7 @@ namespace Content.Server.GameTicking
[Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly UserDbDataManager _userDb = default!; [Dependency] private readonly UserDbDataManager _userDb = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly RoleSystem _roles = default!;
[ViewVariables] private bool _initialized; [ViewVariables] private bool _initialized;
[ViewVariables] private bool _postInitialized; [ViewVariables] private bool _postInitialized;

View File

@@ -6,7 +6,7 @@ namespace Content.Server.GameTicking.Rules.Components;
public sealed partial class PiratesRuleComponent : Component public sealed partial class PiratesRuleComponent : Component
{ {
[ViewVariables] [ViewVariables]
public List<Mind.Mind> Pirates = new(); public List<EntityUid> Pirates = new();
[ViewVariables] [ViewVariables]
public EntityUid PirateShip = EntityUid.Invalid; public EntityUid PirateShip = EntityUid.Invalid;
[ViewVariables] [ViewVariables]

View File

@@ -1,5 +1,4 @@
using Content.Server.Roles; using Content.Shared.Preferences;
using Content.Shared.Preferences;
using Content.Shared.Roles; using Content.Shared.Roles;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Audio; using Robust.Shared.Audio;
@@ -10,12 +9,12 @@ namespace Content.Server.GameTicking.Rules.Components;
[RegisterComponent, Access(typeof(TraitorRuleSystem))] [RegisterComponent, Access(typeof(TraitorRuleSystem))]
public sealed partial class TraitorRuleComponent : Component public sealed partial class TraitorRuleComponent : Component
{ {
public List<TraitorRole> Traitors = new(); public readonly List<EntityUid> TraitorMinds = new();
[DataField("traitorPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))] [DataField("traitorPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
public string TraitorPrototypeId = "Traitor"; public string TraitorPrototypeId = "Traitor";
public int TotalTraitors => Traitors.Count; public int TotalTraitors => TraitorMinds.Count;
public string[] Codewords = new string[3]; public string[] Codewords = new string[3];
public enum SelectionState public enum SelectionState

View File

@@ -6,7 +6,6 @@ using Content.Server.GameTicking.Rules.Components;
using Content.Server.Ghost.Roles.Components; using Content.Server.Ghost.Roles.Components;
using Content.Server.Ghost.Roles.Events; using Content.Server.Ghost.Roles.Events;
using Content.Server.Humanoid; using Content.Server.Humanoid;
using Content.Server.Humanoid.Systems;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Server.NPC.Components; using Content.Server.NPC.Components;
@@ -20,7 +19,6 @@ using Content.Server.Shuttles.Systems;
using Content.Server.Spawners.Components; using Content.Server.Spawners.Components;
using Content.Server.Station.Components; using Content.Server.Station.Components;
using Content.Server.Station.Systems; using Content.Server.Station.Systems;
using Content.Server.Traitor;
using Content.Shared.Dataset; using Content.Shared.Dataset;
using Content.Shared.Humanoid; using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes; using Content.Shared.Humanoid.Prototypes;
@@ -58,8 +56,12 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
[Dependency] private readonly MapLoaderSystem _map = default!; [Dependency] private readonly MapLoaderSystem _map = default!;
[Dependency] private readonly ShuttleSystem _shuttle = default!; [Dependency] private readonly ShuttleSystem _shuttle = default!;
[Dependency] private readonly MindSystem _mindSystem = default!; [Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly RoleSystem _roles = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly MetaDataSystem _metaData = default!;
[ValidatePrototypeId<AntagPrototype>]
public const string NukeopsId = "Nukeops";
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -87,10 +89,10 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
continue; continue;
// If entity has a prior mind attached, add them to the players list. // If entity has a prior mind attached, add them to the players list.
if (!TryComp<MindContainerComponent>(uid, out var mindComponent)) if (!_mindSystem.TryGetMind(uid, out _, out var mind))
continue; continue;
var session = mindComponent.Mind?.Session; var session = mind?.Session;
var name = MetaData(uid).EntityName; var name = MetaData(uid).EntityName;
if (session != null) if (session != null)
nukeops.OperativePlayers.Add(name, session); nukeops.OperativePlayers.Add(name, session);
@@ -592,22 +594,21 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
private void OnMindAdded(EntityUid uid, NukeOperativeComponent component, MindAddedMessage args) private void OnMindAdded(EntityUid uid, NukeOperativeComponent component, MindAddedMessage args)
{ {
if (!TryComp<MindContainerComponent>(uid, out var mindContainerComponent) || mindContainerComponent.Mind == null) if (!_mindSystem.TryGetMind(uid, out var mindId, out var mind))
return; return;
var mind = mindContainerComponent.Mind;
foreach (var nukeops in EntityQuery<NukeopsRuleComponent>()) foreach (var nukeops in EntityQuery<NukeopsRuleComponent>())
{ {
if (nukeops.OperativeMindPendingData.TryGetValue(uid, out var role) || !nukeops.SpawnOutpost || !nukeops.EndsRound) if (nukeops.OperativeMindPendingData.TryGetValue(uid, out var role) || !nukeops.SpawnOutpost || !nukeops.EndsRound)
{ {
role ??= nukeops.OperativeRoleProto; role ??= nukeops.OperativeRoleProto;
_mindSystem.AddRole(mind, new NukeopsRole(mind, _prototypeManager.Index<AntagPrototype>(role))); _roles.MindAddRole(mindId, new NukeopsRoleComponent { PrototypeId = role });
nukeops.OperativeMindPendingData.Remove(uid); nukeops.OperativeMindPendingData.Remove(uid);
} }
if (!_mindSystem.TryGetSession(mind, out var playerSession)) if (mind.Session is not { } playerSession)
return; return;
if (nukeops.OperativePlayers.ContainsValue(playerSession)) if (nukeops.OperativePlayers.ContainsValue(playerSession))
return; return;
@@ -778,7 +779,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
SetupOperativeEntity(mob, spawnDetails.Name, spawnDetails.Gear, profile, component); SetupOperativeEntity(mob, spawnDetails.Name, spawnDetails.Gear, profile, component);
var newMind = _mindSystem.CreateMind(session.UserId, spawnDetails.Name); var newMind = _mindSystem.CreateMind(session.UserId, spawnDetails.Name);
_mindSystem.SetUserId(newMind, session.UserId); _mindSystem.SetUserId(newMind, session.UserId);
_mindSystem.AddRole(newMind, new NukeopsRole(newMind, nukeOpsAntag)); _roles.MindAddRole(newMind, new NukeopsRoleComponent { PrototypeId = spawnDetails.Role });
_mindSystem.TransferTo(newMind, mob); _mindSystem.TransferTo(newMind, mob);
} }
@@ -820,13 +821,13 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
} }
//For admins forcing someone to nukeOps. //For admins forcing someone to nukeOps.
public void MakeLoneNukie(Mind.Mind mind) public void MakeLoneNukie(EntityUid mindId, MindComponent mind)
{ {
if (!mind.OwnedEntity.HasValue) if (!mind.OwnedEntity.HasValue)
return; return;
//ok hardcoded value bad but so is everything else here //ok hardcoded value bad but so is everything else here
_mindSystem.AddRole(mind, new NukeopsRole(mind, _prototypeManager.Index<AntagPrototype>("Nukeops"))); _roles.MindAddRole(mindId, new NukeopsRoleComponent { PrototypeId = NukeopsId }, mind);
SetOutfitCommand.SetOutfit(mind.OwnedEntity.Value, "SyndicateOperativeGearFull", EntityManager); SetOutfitCommand.SetOutfit(mind.OwnedEntity.Value, "SyndicateOperativeGearFull", EntityManager);
} }
@@ -878,7 +879,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
var query = EntityQuery<NukeOperativeComponent, MindContainerComponent, MetaDataComponent>(true); var query = EntityQuery<NukeOperativeComponent, MindContainerComponent, MetaDataComponent>(true);
foreach (var (_, mindComp, metaData) in query) foreach (var (_, mindComp, metaData) in query)
{ {
if (!mindComp.HasMind || !_mindSystem.TryGetSession(mindComp.Mind, out var session)) if (!mindComp.HasMind || !_mindSystem.TryGetSession(mindComp.Mind.Value, out var session))
continue; continue;
component.OperativePlayers.Add(metaData.EntityName, session); component.OperativePlayers.Add(metaData.EntityName, session);
} }

View File

@@ -16,14 +16,12 @@ using Content.Shared.Roles;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Maps; using Robust.Server.Maps;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Audio;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Enums;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Robust.Shared.Enums;
using Robust.Shared.Player;
namespace Content.Server.GameTicking.Rules; namespace Content.Server.GameTicking.Rules;
@@ -68,15 +66,14 @@ public sealed class PiratesRuleSystem : GameRuleSystem<PiratesRuleComponent>
} }
else else
{ {
List<(double, EntityUid)> mostValuableThefts = new(); List<(double, EntityUid)> mostValuableThefts = new();
var comp1 = pirates; var comp1 = pirates;
var finalValue = _pricingSystem.AppraiseGrid(pirates.PirateShip, uid => var finalValue = _pricingSystem.AppraiseGrid(pirates.PirateShip, uid =>
{ {
foreach (var mind in comp1.Pirates) foreach (var mindId in comp1.Pirates)
{ {
if (mind.CurrentEntity == uid) if (TryComp(mindId, out MindComponent? mind) && mind.CurrentEntity == uid)
return false; // Don't appraise the pirates twice, we count them in separately. return false; // Don't appraise the pirates twice, we count them in separately.
} }
@@ -92,9 +89,9 @@ public sealed class PiratesRuleSystem : GameRuleSystem<PiratesRuleComponent>
mostValuableThefts.Pop(); mostValuableThefts.Pop();
}); });
foreach (var mind in pirates.Pirates) foreach (var mindId in pirates.Pirates)
{ {
if (mind.CurrentEntity is not null) if (TryComp(mindId, out MindComponent? mind) && mind.CurrentEntity is not null)
finalValue += _pricingSystem.GetPrice(mind.CurrentEntity.Value); finalValue += _pricingSystem.GetPrice(mind.CurrentEntity.Value);
} }
@@ -119,7 +116,10 @@ public sealed class PiratesRuleSystem : GameRuleSystem<PiratesRuleComponent>
ev.AddLine(Loc.GetString("pirates-list-start")); ev.AddLine(Loc.GetString("pirates-list-start"));
foreach (var pirate in pirates.Pirates) foreach (var pirate in pirates.Pirates)
{ {
ev.AddLine($"- {pirate.CharacterName} ({pirate.Session?.Name})"); if (TryComp(pirate, out MindComponent? mind))
{
ev.AddLine($"- {mind.CharacterName} ({mind.Session?.Name})");
}
} }
} }
} }
@@ -239,7 +239,7 @@ public sealed class PiratesRuleSystem : GameRuleSystem<PiratesRuleComponent>
} }
//Forcing one player to be a pirate. //Forcing one player to be a pirate.
public void MakePirate(Mind.Mind mind) public void MakePirate(EntityUid mindId, MindComponent mind)
{ {
if (!mind.OwnedEntity.HasValue) if (!mind.OwnedEntity.HasValue)
return; return;

View File

@@ -1,19 +1,19 @@
using System.Linq; using System.Linq;
using Content.Server.Chat.Managers; using Content.Server.Chat.Managers;
using Content.Server.GameTicking.Rules.Components; using Content.Server.GameTicking.Rules.Components;
using Content.Server.NPC.Systems;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Objectives.Interfaces; using Content.Server.NPC.Systems;
using Content.Server.Objectives;
using Content.Server.PDA.Ringer; using Content.Server.PDA.Ringer;
using Content.Server.Players;
using Content.Server.Roles; using Content.Server.Roles;
using Content.Server.Roles.Jobs;
using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Components;
using Content.Server.Traitor.Uplink; using Content.Server.Traitor.Uplink;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Dataset; using Content.Shared.Dataset;
using Content.Shared.Preferences;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.PDA; using Content.Shared.PDA;
using Content.Shared.Preferences;
using Content.Shared.Roles; using Content.Shared.Roles;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
@@ -28,16 +28,17 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IObjectivesManager _objectivesManager = default!;
[Dependency] private readonly IChatManager _chatManager = default!; [Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IComponentFactory _component = default!;
[Dependency] private readonly NpcFactionSystem _npcFaction = default!; [Dependency] private readonly NpcFactionSystem _npcFaction = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly UplinkSystem _uplink = default!; [Dependency] private readonly UplinkSystem _uplink = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly MindSystem _mindSystem = default!; [Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly RoleSystem _roleSystem = default!;
private ISawmill _sawmill = default!; [Dependency] private readonly JobSystem _jobs = default!;
[Dependency] private readonly ObjectivesSystem _objectives = default!;
private int PlayersPerTraitor => _cfg.GetCVar(CCVars.TraitorPlayersPerTraitor); private int PlayersPerTraitor => _cfg.GetCVar(CCVars.TraitorPlayersPerTraitor);
private int MaxTraitors => _cfg.GetCVar(CCVars.TraitorMaxTraitors); private int MaxTraitors => _cfg.GetCVar(CCVars.TraitorMaxTraitors);
@@ -46,8 +47,6 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
{ {
base.Initialize(); base.Initialize();
_sawmill = Logger.GetSawmill("preset");
SubscribeLocalEvent<RoundStartAttemptEvent>(OnStartAttempt); SubscribeLocalEvent<RoundStartAttemptEvent>(OnStartAttempt);
SubscribeLocalEvent<RulePlayerJobsAssignedEvent>(OnPlayersSpawned); SubscribeLocalEvent<RulePlayerJobsAssignedEvent>(OnPlayersSpawned);
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(HandleLatejoin); SubscribeLocalEvent<PlayerSpawnCompleteEvent>(HandleLatejoin);
@@ -107,7 +106,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
{ {
if (!component.StartCandidates.Any()) if (!component.StartCandidates.Any())
{ {
_sawmill.Error("Tried to start Traitor mode without any candidates."); Log.Error("Tried to start Traitor mode without any candidates.");
return; return;
} }
@@ -156,7 +155,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
foreach (var player in candidates.Keys) foreach (var player in candidates.Keys)
{ {
// Role prevents antag. // Role prevents antag.
if (!(player.Data.ContentData()?.Mind?.AllRoles.All(role => role is not Job { CanBeAntag: false }) ?? false)) if (!_jobs.CanBeAntag(player))
{ {
continue; continue;
} }
@@ -180,7 +179,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
} }
if (prefList.Count == 0) if (prefList.Count == 0)
{ {
_sawmill.Info("Insufficient preferred traitors, picking at random."); Log.Info("Insufficient preferred traitors, picking at random.");
prefList = list; prefList = list;
} }
return prefList; return prefList;
@@ -191,14 +190,14 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
var results = new List<IPlayerSession>(traitorCount); var results = new List<IPlayerSession>(traitorCount);
if (prefList.Count == 0) if (prefList.Count == 0)
{ {
_sawmill.Info("Insufficient ready players to fill up with traitors, stopping the selection."); Log.Info("Insufficient ready players to fill up with traitors, stopping the selection.");
return results; return results;
} }
for (var i = 0; i < traitorCount; i++) for (var i = 0; i < traitorCount; i++)
{ {
results.Add(_random.PickAndTake(prefList)); results.Add(_random.PickAndTake(prefList));
_sawmill.Info("Selected a preferred traitor."); Log.Info("Selected a preferred traitor.");
} }
return results; return results;
} }
@@ -215,22 +214,28 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
MakeCodewords(traitorRule); MakeCodewords(traitorRule);
} }
var mind = traitor.Data.ContentData()?.Mind; if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind))
if (mind == null)
{ {
_sawmill.Info("Failed getting mind for picked traitor."); Log.Info("Failed getting mind for picked traitor.");
return false; return false;
} }
if (HasComp<TraitorRoleComponent>(mindId))
{
Log.Error($"Player {traitor.Name} is already a traitor.");
return false;
}
if (mind.OwnedEntity is not { } entity) if (mind.OwnedEntity is not { } entity)
{ {
Logger.ErrorS("preset", "Mind picked for traitor did not have an attached entity."); Log.Error("Mind picked for traitor did not have an attached entity.");
return false; return false;
} }
// Calculate the amount of currency on the uplink. // Calculate the amount of currency on the uplink.
var startingBalance = _cfg.GetCVar(CCVars.TraitorStartingBalance); var startingBalance = _cfg.GetCVar(CCVars.TraitorStartingBalance);
if (mind.CurrentJob != null) if (_jobs.MindTryGetJob(mindId, out _, out var prototype))
startingBalance = Math.Max(startingBalance - mind.CurrentJob.Prototype.AntagAdvantage, 0); startingBalance = Math.Max(startingBalance - prototype.AntagAdvantage, 0);
// creadth: we need to create uplink for the antag. // creadth: we need to create uplink for the antag.
// PDA should be in place already // PDA should be in place already
@@ -241,22 +246,22 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
// Add the ringtone uplink and get its code for greeting // Add the ringtone uplink and get its code for greeting
var code = EnsureComp<RingerUplinkComponent>(pda.Value).Code; var code = EnsureComp<RingerUplinkComponent>(pda.Value).Code;
// Prepare antagonist role // Prepare traitor role
var antagPrototype = _prototypeManager.Index<AntagPrototype>(traitorRule.TraitorPrototypeId);
var traitorRole = new TraitorRole(mind, antagPrototype);
// Give traitors their codewords and uplink code to keep in their character info menu // Give traitors their codewords and uplink code to keep in their character info menu
traitorRole.Mind.Briefing = string.Format( var briefing =
"{0}\n{1}", $"{Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", traitorRule.Codewords)))}\n{Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", code).Replace("sharp", "#")))}";
Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", traitorRule.Codewords))), var traitorRole = new TraitorRoleComponent
Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", code).Replace("sharp","#")))); {
PrototypeId = traitorRule.TraitorPrototypeId,
Briefing = briefing
};
// Assign traitor roles // Assign traitor roles
_mindSystem.AddRole(mind, traitorRole); _roleSystem.MindAddRole(mindId, traitorRole);
SendTraitorBriefing(mind, traitorRule.Codewords, code); SendTraitorBriefing(mindId, traitorRule.Codewords, code);
traitorRule.Traitors.Add(traitorRole); traitorRule.TraitorMinds.Add(mindId);
if (_mindSystem.TryGetSession(mind, out var session)) if (_mindSystem.TryGetSession(mindId, out var session))
{ {
// Notificate player about new role assignment // Notificate player about new role assignment
_audioSystem.PlayGlobal(traitorRule.GreetSoundNotification, session); _audioSystem.PlayGlobal(traitorRule.GreetSoundNotification, session);
@@ -272,11 +277,11 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
var difficulty = 0f; var difficulty = 0f;
for (var pick = 0; pick < maxPicks && maxDifficulty > difficulty; pick++) for (var pick = 0; pick < maxPicks && maxDifficulty > difficulty; pick++)
{ {
var objective = _objectivesManager.GetRandomObjective(traitorRole.Mind, "TraitorObjectiveGroups"); var objective = _objectives.GetRandomObjective(mindId, mind, "TraitorObjectiveGroups");
if (objective == null) if (objective == null)
continue; continue;
if (_mindSystem.TryAddObjective(traitorRole.Mind, objective)) if (_mindSystem.TryAddObjective(mindId, mind, objective))
difficulty += objective.Difficulty; difficulty += objective.Difficulty;
} }
@@ -289,7 +294,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
/// <param name="mind">A mind (player)</param> /// <param name="mind">A mind (player)</param>
/// <param name="codewords">Codewords</param> /// <param name="codewords">Codewords</param>
/// <param name="code">Uplink codes</param> /// <param name="code">Uplink codes</param>
private void SendTraitorBriefing(Mind.Mind mind, string[] codewords, Note[] code) private void SendTraitorBriefing(EntityUid mind, string[] codewords, Note[] code)
{ {
if (_mindSystem.TryGetSession(mind, out var session)) if (_mindSystem.TryGetSession(mind, out var session))
{ {
@@ -362,18 +367,21 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
if (!GameTicker.IsGameRuleAdded(uid, gameRule)) if (!GameTicker.IsGameRuleAdded(uid, gameRule))
continue; continue;
var result = Loc.GetString("traitor-round-end-result", ("traitorCount", traitor.Traitors.Count)); var result = Loc.GetString("traitor-round-end-result", ("traitorCount", traitor.TraitorMinds.Count));
result += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", traitor.Codewords))) + result += "\n" + Loc.GetString("traitor-round-end-codewords", ("codewords", string.Join(", ", traitor.Codewords))) +
"\n"; "\n";
foreach (var t in traitor.Traitors) foreach (var mindId in traitor.TraitorMinds)
{ {
var name = t.Mind.CharacterName; if (!TryComp(mindId, out MindComponent? mind))
_mindSystem.TryGetSession(t.Mind, out var session); continue;
var name = mind.CharacterName;
_mindSystem.TryGetSession(mindId, out var session);
var username = session?.Name; var username = session?.Name;
var objectives = t.Mind.AllObjectives.ToArray(); var objectives = mind.AllObjectives.ToArray();
if (objectives.Length == 0) if (objectives.Length == 0)
{ {
if (username != null) if (username != null)
@@ -437,12 +445,12 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
} }
} }
public List<TraitorRole> GetOtherTraitorsAliveAndConnected(Mind.Mind ourMind) public List<(EntityUid Id, MindComponent Mind)> GetOtherTraitorMindsAliveAndConnected(MindComponent ourMind)
{ {
List<TraitorRole> allTraitors = new(); List<(EntityUid Id, MindComponent Mind)> allTraitors = new();
foreach (var traitor in EntityQuery<TraitorRuleComponent>()) foreach (var traitor in EntityQuery<TraitorRuleComponent>())
{ {
foreach (var role in GetOtherTraitorsAliveAndConnected(ourMind, traitor)) foreach (var role in GetOtherTraitorMindsAliveAndConnected(ourMind, traitor))
{ {
if (!allTraitors.Contains(role)) if (!allTraitors.Contains(role))
allTraitors.Add(role); allTraitors.Add(role);
@@ -452,13 +460,22 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
return allTraitors; return allTraitors;
} }
private List<TraitorRole> GetOtherTraitorsAliveAndConnected(Mind.Mind ourMind, TraitorRuleComponent component) private List<(EntityUid Id, MindComponent Mind)> GetOtherTraitorMindsAliveAndConnected(MindComponent ourMind, TraitorRuleComponent component)
{ {
return component.Traitors // don't want var traitors = new List<(EntityUid Id, MindComponent Mind)>();
.Where(t => t.Mind.OwnedEntity is not null) // no entity foreach (var traitor in component.TraitorMinds)
.Where(t => t.Mind.Session is not null) // player disconnected {
.Where(t => t.Mind != ourMind) // ourselves if (TryComp(traitor, out MindComponent? mind) &&
.Where(t => _mobStateSystem.IsAlive((EntityUid) t.Mind.OwnedEntity!)) // dead mind.OwnedEntity != null &&
.Where(t => t.Mind.CurrentEntity == t.Mind.OwnedEntity).ToList(); // not in original body mind.Session != null &&
mind != ourMind &&
_mobStateSystem.IsAlive(mind.OwnedEntity.Value) &&
mind.CurrentEntity == mind.OwnedEntity)
{
traitors.Add((traitor, mind));
}
}
return traitors;
} }
} }

View File

@@ -5,8 +5,6 @@ using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems; using Content.Server.Chat.Systems;
using Content.Server.GameTicking.Rules.Components; using Content.Server.GameTicking.Rules.Components;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Mind.Components;
using Content.Server.Players;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Preferences.Managers; using Content.Server.Preferences.Managers;
using Content.Server.Roles; using Content.Server.Roles;
@@ -21,7 +19,6 @@ using Content.Shared.Mobs;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.Preferences; using Content.Shared.Preferences;
using Content.Shared.Roles;
using Content.Shared.Zombies; using Content.Shared.Zombies;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Player; using Robust.Server.Player;
@@ -48,6 +45,7 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
[Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly ZombieSystem _zombie = default!; [Dependency] private readonly ZombieSystem _zombie = default!;
[Dependency] private readonly MindSystem _mindSystem = default!; [Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly RoleSystem _roles = default!;
[Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
@@ -97,10 +95,9 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
{ {
var meta = MetaData(survivor); var meta = MetaData(survivor);
var username = string.Empty; var username = string.Empty;
if (TryComp<MindContainerComponent>(survivor, out var mindcomp)) if (_mindSystem.TryGetMind(survivor, out _, out var mind) && mind.Session != null)
{ {
if (mindcomp.Mind != null && mindcomp.Mind.Session != null) username = mind.Session.Name;
username = mindcomp.Mind.Session.Name;
} }
ev.AddLine(Loc.GetString("zombie-round-end-user-was-survivor", ev.AddLine(Loc.GetString("zombie-round-end-user-was-survivor",
@@ -312,12 +309,15 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
prefList.Remove(zombie); prefList.Remove(zombie);
playerList.Remove(zombie); playerList.Remove(zombie);
if (zombie.Data.ContentData()?.Mind is not { } mind || mind.OwnedEntity is not { } ownedEntity) if (!_mindSystem.TryGetMind(zombie, out var mindId, out var mind) ||
mind.OwnedEntity is not { } ownedEntity)
{
continue; continue;
}
totalInfected++; totalInfected++;
_mindSystem.AddRole(mind, new ZombieRole(mind, _prototypeManager.Index<AntagPrototype>(component.PatientZeroPrototypeId))); _roles.MindAddRole(mindId, new ZombieRoleComponent { PrototypeId = component.PatientZeroPrototypeId });
var pending = EnsureComp<PendingZombieComponent>(ownedEntity); var pending = EnsureComp<PendingZombieComponent>(ownedEntity);
pending.GracePeriod = _random.Next(component.MinInitialInfectedGrace, component.MaxInitialInfectedGrace); pending.GracePeriod = _random.Next(component.MinInitialInfectedGrace, component.MaxInitialInfectedGrace);

View File

@@ -1,5 +1,5 @@
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Players; using Content.Server.Mind;
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Console; using Robust.Shared.Console;
@@ -9,6 +9,8 @@ namespace Content.Server.Ghost
[AnyCommand] [AnyCommand]
public sealed class Ghost : IConsoleCommand public sealed class Ghost : IConsoleCommand
{ {
[Dependency] private readonly IEntityManager _entities = default!;
public string Command => "ghost"; public string Command => "ghost";
public string Description => "Give up on life and become a ghost."; public string Description => "Give up on life and become a ghost.";
public string Help => "ghost"; public string Help => "ghost";
@@ -22,14 +24,14 @@ namespace Content.Server.Ghost
return; return;
} }
var mind = player.ContentData()?.Mind; var minds = _entities.System<MindSystem>();
if (mind == null) if (!minds.TryGetMind(player, out var mindId, out var mind))
{ {
shell.WriteLine("You have no Mind, you can't ghost."); shell.WriteLine("You have no Mind, you can't ghost.");
return; return;
} }
if (!EntitySystem.Get<GameTicker>().OnGhostAttempt(mind, true, viaCommand:true)) if (!EntitySystem.Get<GameTicker>().OnGhostAttempt(mindId, true, true, mind))
{ {
shell.WriteLine("You can't ghost right now."); shell.WriteLine("You can't ghost right now.");
} }

View File

@@ -4,7 +4,7 @@ using Content.Server.GameTicking;
using Content.Server.Ghost.Components; using Content.Server.Ghost.Components;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Server.Players; using Content.Server.Roles.Jobs;
using Content.Server.Visible; using Content.Server.Visible;
using Content.Server.Warps; using Content.Server.Warps;
using Content.Shared.Actions; using Content.Shared.Actions;
@@ -16,7 +16,6 @@ using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.Movement.Events; using Content.Shared.Movement.Events;
using Content.Shared.Storage.Components; using Content.Shared.Storage.Components;
using JetBrains.Annotations;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Console; using Robust.Shared.Console;
@@ -38,6 +37,8 @@ namespace Content.Server.Ghost
[Dependency] private readonly FollowerSystem _followerSystem = default!; [Dependency] private readonly FollowerSystem _followerSystem = default!;
[Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!;
[Dependency] private readonly MindSystem _minds = default!;
[Dependency] private readonly JobSystem _jobs = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -92,13 +93,13 @@ namespace Content.Server.Ghost
if (EntityManager.HasComponent<VisitingMindComponent>(uid)) if (EntityManager.HasComponent<VisitingMindComponent>(uid))
return; return;
if (!EntityManager.TryGetComponent<MindContainerComponent>(uid, out var mind) || !mind.HasMind || mind.Mind.IsVisitingEntity) if (!_minds.TryGetMind(uid, out var mindId, out var mind) || mind.IsVisitingEntity)
return; return;
if (component.MustBeDead && (_mobState.IsAlive(uid) || _mobState.IsCritical(uid))) if (component.MustBeDead && (_mobState.IsAlive(uid) || _mobState.IsCritical(uid)))
return; return;
_ticker.OnGhostAttempt(mind.Mind, component.CanReturn); _ticker.OnGhostAttempt(mindId, component.CanReturn, mind: mind);
} }
private void OnGhostStartup(EntityUid uid, GhostComponent component, ComponentStartup args) private void OnGhostStartup(EntityUid uid, GhostComponent component, ComponentStartup args)
@@ -199,7 +200,7 @@ namespace Content.Server.Ghost
return; return;
} }
_mindSystem.UnVisit(actor.PlayerSession.ContentData()!.Mind); _mindSystem.UnVisit(actor.PlayerSession);
} }
private void OnGhostWarpToTargetRequest(GhostWarpToTargetRequestEvent msg, EntitySessionEventArgs args) private void OnGhostWarpToTargetRequest(GhostWarpToTargetRequestEvent msg, EntitySessionEventArgs args)
@@ -260,7 +261,8 @@ namespace Content.Server.Ghost
TryComp<MindContainerComponent>(attached, out var mind); TryComp<MindContainerComponent>(attached, out var mind);
string playerInfo = $"{EntityManager.GetComponent<MetaDataComponent>(attached).EntityName} ({mind?.Mind?.CurrentJob?.Name ?? "Unknown"})"; var jobName = _jobs.MindTryGetJobName(mind?.Mind);
var playerInfo = $"{EntityManager.GetComponent<MetaDataComponent>(attached).EntityName} ({jobName})";
if (_mobState.IsAlive(attached) || _mobState.IsCritical(attached)) if (_mobState.IsAlive(attached) || _mobState.IsCritical(attached))
yield return new GhostWarp(attached, playerInfo, false); yield return new GhostWarp(attached, playerInfo, false);

View File

@@ -1,17 +0,0 @@
using Content.Server.Roles;
namespace Content.Server.Ghost
{
/// <summary>
/// This is used to mark Observers properly, as they get Minds
/// </summary>
public sealed class ObserverRole : Role
{
public override string Name => Loc.GetString("observer-role-name");
public override bool Antagonist => false;
public ObserverRole(Mind.Mind mind) : base(mind)
{
}
}
}

View File

@@ -0,0 +1,11 @@
namespace Content.Server.Ghost
{
/// <summary>
/// This is used to mark Observers properly, as they get Minds
/// </summary>
[RegisterComponent]
public sealed partial class ObserverRoleComponent : Component
{
public string Name => Loc.GetString("observer-role-name");
}
}

View File

@@ -1,6 +1,5 @@
using Content.Server.EUI; using Content.Server.EUI;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Players;
using Content.Shared.Eui; using Content.Shared.Eui;
using Content.Shared.Ghost; using Content.Shared.Ghost;
@@ -10,9 +9,9 @@ public sealed class ReturnToBodyEui : BaseEui
{ {
private readonly MindSystem _mindSystem; private readonly MindSystem _mindSystem;
private readonly Mind.Mind _mind; private readonly MindComponent _mind;
public ReturnToBodyEui(Mind.Mind mind, MindSystem mindSystem) public ReturnToBodyEui(MindComponent mind, MindSystem mindSystem)
{ {
_mind = mind; _mind = mind;
_mindSystem = mindSystem; _mindSystem = mindSystem;
@@ -29,8 +28,8 @@ public sealed class ReturnToBodyEui : BaseEui
return; return;
} }
if (_mindSystem.TryGetSession(_mind, out var session)) _mindSystem.UnVisit(_mind.Session);
_mindSystem.UnVisit(session.ContentData()!.Mind);
Close(); Close();
} }
} }

View File

@@ -1,20 +0,0 @@
using Content.Server.Roles;
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 sealed 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;
}
}
}

View File

@@ -0,0 +1,11 @@
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>
[RegisterComponent]
public sealed partial class GhostRoleMarkerRoleComponent : Component
{
[DataField("name")] public string? Name;
}

View File

@@ -3,10 +3,11 @@ using Content.Server.EUI;
using Content.Server.Ghost.Roles.Components; using Content.Server.Ghost.Roles.Components;
using Content.Server.Ghost.Roles.Events; using Content.Server.Ghost.Roles.Events;
using Content.Server.Ghost.Roles.UI; using Content.Server.Ghost.Roles.UI;
using Content.Server.Mind.Commands;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Mind.Commands;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Server.Players; using Content.Server.Players;
using Content.Server.Roles;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Follower; using Content.Shared.Follower;
@@ -34,6 +35,7 @@ namespace Content.Server.Ghost.Roles
[Dependency] private readonly FollowerSystem _followerSystem = default!; [Dependency] private readonly FollowerSystem _followerSystem = default!;
[Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly MindSystem _mindSystem = default!; [Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly RoleSystem _roleSystem = default!;
private uint _nextRoleIdentifier; private uint _nextRoleIdentifier;
private bool _needsUpdateGhostRoleCount = true; private bool _needsUpdateGhostRoleCount = true;
@@ -219,7 +221,7 @@ namespace Content.Server.Ghost.Roles
var newMind = _mindSystem.CreateMind(player.UserId, var newMind = _mindSystem.CreateMind(player.UserId,
EntityManager.GetComponent<MetaDataComponent>(mob).EntityName); EntityManager.GetComponent<MetaDataComponent>(mob).EntityName);
_mindSystem.AddRole(newMind, new GhostRoleMarkerRole(newMind, role.RoleName)); _roleSystem.MindAddRole(newMind, new GhostRoleMarkerRoleComponent { Name = role.RoleName });
_mindSystem.SetUserId(newMind, player.UserId); _mindSystem.SetUserId(newMind, player.UserId);
_mindSystem.TransferTo(newMind, mob); _mindSystem.TransferTo(newMind, mob);

View File

@@ -107,12 +107,12 @@ public sealed class ToggleableGhostRoleSystem : EntitySystem
Text = Loc.GetString(component.WipeVerbText), Text = Loc.GetString(component.WipeVerbText),
Act = () => Act = () =>
{ {
if (!TryComp<MindContainerComponent>(uid, out var mindComp) || mindComp.Mind == null) if (!_mind.TryGetMind(uid, out var mindId, out var mind))
return; return;
// Wiping device :( // Wiping device :(
// The shutdown of the Mind should cause automatic reset of the pAI during OnMindRemoved // The shutdown of the Mind should cause automatic reset of the pAI during OnMindRemoved
// EDIT: But it doesn't!!!! Wtf? Do stuff manually // EDIT: But it doesn't!!!! Wtf? Do stuff manually
_mind.TransferTo(mindComp.Mind, null); _mind.TransferTo(mindId, null, mind: mind);
_popup.PopupEntity(Loc.GetString(component.WipeVerbPopup), uid, args.User, PopupType.Large); _popup.PopupEntity(Loc.GetString(component.WipeVerbPopup), uid, args.User, PopupType.Large);
UpdateAppearance(uid, ToggleableGhostRoleStatus.Off); UpdateAppearance(uid, ToggleableGhostRoleStatus.Off);
_pai.PAITurningOff(uid); _pai.PAITurningOff(uid);

View File

@@ -13,8 +13,6 @@ using Content.Server.Info;
using Content.Server.Maps; using Content.Server.Maps;
using Content.Server.MoMMI; using Content.Server.MoMMI;
using Content.Server.NodeContainer.NodeGroups; using Content.Server.NodeContainer.NodeGroups;
using Content.Server.Objectives;
using Content.Server.Objectives.Interfaces;
using Content.Server.Players.PlayTimeTracking; using Content.Server.Players.PlayTimeTracking;
using Content.Server.Preferences.Managers; using Content.Server.Preferences.Managers;
using Content.Server.ServerInfo; using Content.Server.ServerInfo;
@@ -40,7 +38,6 @@ namespace Content.Server.IoC
IoCManager.Register<INodeGroupFactory, NodeGroupFactory>(); IoCManager.Register<INodeGroupFactory, NodeGroupFactory>();
IoCManager.Register<IConnectionManager, ConnectionManager>(); IoCManager.Register<IConnectionManager, ConnectionManager>();
IoCManager.Register<ServerUpdateManager>(); IoCManager.Register<ServerUpdateManager>();
IoCManager.Register<IObjectivesManager, ObjectivesManager>();
IoCManager.Register<IAdminManager, AdminManager>(); IoCManager.Register<IAdminManager, AdminManager>();
IoCManager.Register<ISharedAdminManager, AdminManager>(); IoCManager.Register<ISharedAdminManager, AdminManager>();
IoCManager.Register<EuiManager, EuiManager>(); IoCManager.Register<EuiManager, EuiManager>();

View File

@@ -4,8 +4,8 @@ using Content.Server.Chemistry.EntitySystems;
using Content.Server.Construction; using Content.Server.Construction;
using Content.Server.Fluids.EntitySystems; using Content.Server.Fluids.EntitySystems;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Mind;
using Content.Server.Nutrition.Components; using Content.Server.Nutrition.Components;
using Content.Server.Players;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Stack; using Content.Server.Stack;
@@ -34,6 +34,7 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
[Dependency] private readonly SharedBodySystem _body = default!; //bobby [Dependency] private readonly SharedBodySystem _body = default!; //bobby
[Dependency] private readonly PuddleSystem _puddle = default!; [Dependency] private readonly PuddleSystem _puddle = default!;
[Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly StackSystem _stack = default!;
[Dependency] private readonly MindSystem _mind = default!;
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
@@ -105,9 +106,9 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
args.SetHandled(SuicideKind.Bloodloss); args.SetHandled(SuicideKind.Bloodloss);
var victim = args.Victim; var victim = args.Victim;
if (TryComp(victim, out ActorComponent? actor) && if (TryComp(victim, out ActorComponent? actor) &&
actor.PlayerSession.ContentData()?.Mind is { } mind) _mind.TryGetMind(actor.PlayerSession, out var mindId, out var mind))
{ {
_ticker.OnGhostAttempt(mind, false); _ticker.OnGhostAttempt(mindId, false, mind: mind);
if (mind.OwnedEntity is { Valid: true } entity) if (mind.OwnedEntity is { Valid: true } entity)
{ {
_popup.PopupEntity(Loc.GetString("recycler-component-suicide-message"), entity); _popup.PopupEntity(Loc.GetString("recycler-component-suicide-message"), entity);

View File

@@ -1,32 +1,32 @@
using System.Numerics; using System.Numerics;
using Content.Shared.Interaction;
using Content.Shared.Audio;
using Content.Shared.Jittering;
using Content.Shared.Chemistry.Components;
using Content.Shared.Throwing;
using Content.Shared.Construction.Components;
using Content.Shared.Nutrition.Components;
using Content.Shared.Administration.Logs;
using Content.Shared.CCVar;
using Content.Shared.Database;
using Content.Server.Power.Components;
using Content.Server.Fluids.EntitySystems;
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Server.Climbing; using Content.Server.Climbing;
using Content.Server.Construction; using Content.Server.Construction;
using Content.Server.Fluids.EntitySystems;
using Content.Server.Materials; using Content.Server.Materials;
using Content.Server.Mind.Components; using Content.Server.Mind;
using Content.Server.Power.Components;
using Content.Shared.Administration.Logs;
using Content.Shared.Audio;
using Content.Shared.CCVar;
using Content.Shared.Chemistry.Components;
using Content.Shared.Construction.Components;
using Content.Shared.Database;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Humanoid; using Content.Shared.Humanoid;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
using Content.Shared.Jittering;
using Content.Shared.Medical;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.Nutrition.Components;
using Content.Shared.Popups; using Content.Shared.Popups;
using Robust.Shared.Random; using Content.Shared.Throwing;
using Robust.Shared.Configuration;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Configuration;
using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Components;
using Content.Shared.Medical; using Robust.Shared.Random;
namespace Content.Server.Medical.BiomassReclaimer namespace Content.Server.Medical.BiomassReclaimer
{ {
@@ -45,6 +45,7 @@ namespace Content.Server.Medical.BiomassReclaimer
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly MaterialStorageSystem _material = default!; [Dependency] private readonly MaterialStorageSystem _material = default!;
[Dependency] private readonly MindSystem _minds = default!;
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
@@ -244,9 +245,9 @@ namespace Content.Server.Medical.BiomassReclaimer
// Reject souled bodies in easy mode. // Reject souled bodies in easy mode.
if (_configManager.GetCVar(CCVars.BiomassEasyMode) && if (_configManager.GetCVar(CCVars.BiomassEasyMode) &&
HasComp<HumanoidAppearanceComponent>(dragged) && HasComp<HumanoidAppearanceComponent>(dragged) &&
TryComp<MindContainerComponent>(dragged, out var mindComp)) _minds.TryGetMind(dragged, out _, out var mind))
{ {
if (mindComp.Mind?.UserId != null && _playerManager.TryGetSessionById(mindComp.Mind.UserId.Value, out _)) if (mind.UserId != null && _playerManager.TryGetSessionById(mind.UserId.Value, out _))
return false; return false;
} }

View File

@@ -5,7 +5,6 @@ using Content.Server.Electrocution;
using Content.Server.EUI; using Content.Server.EUI;
using Content.Server.Ghost; using Content.Server.Ghost;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Mind.Components;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Server.PowerCell; using Content.Server.PowerCell;
using Content.Shared.Damage; using Content.Shared.Damage;
@@ -224,16 +223,16 @@ public sealed class DefibrillatorSystem : EntitySystem
_mobState.ChangeMobState(target, MobState.Critical, mob, uid); _mobState.ChangeMobState(target, MobState.Critical, mob, uid);
_mobThreshold.SetAllowRevives(target, false, thresholds); _mobThreshold.SetAllowRevives(target, false, thresholds);
if (TryComp<MindContainerComponent>(target, out var mindComp) && if (_mind.TryGetMind(target, out var mindId, out var mind) &&
mindComp.Mind?.Session is { } playerSession) mind.Session is { } playerSession)
{ {
session = playerSession; session = playerSession;
// notify them they're being revived. // notify them they're being revived.
if (mindComp.Mind.CurrentEntity != target) if (mind.CurrentEntity != target)
{ {
_chatManager.TrySendInGameICMessage(uid, Loc.GetString("defibrillator-ghosted"), _chatManager.TrySendInGameICMessage(uid, Loc.GetString("defibrillator-ghosted"),
InGameICChatType.Speak, true); InGameICChatType.Speak, true);
_euiManager.OpenEui(new ReturnToBodyEui(mindComp.Mind, _mind), session); _euiManager.OpenEui(new ReturnToBodyEui(mind, _mind), session);
} }
} }
else else

View File

@@ -1,6 +1,6 @@
using System.Text; using System.Text;
using Content.Server.Administration; using Content.Server.Administration;
using Content.Server.Players; using Content.Server.Roles;
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Console; using Robust.Shared.Console;
@@ -10,10 +10,10 @@ namespace Content.Server.Mind.Commands
[AdminCommand(AdminFlags.Admin)] [AdminCommand(AdminFlags.Admin)]
public sealed class MindInfoCommand : IConsoleCommand public sealed class MindInfoCommand : IConsoleCommand
{ {
[Dependency] private readonly IEntityManager _entities = default!;
public string Command => "mindinfo"; public string Command => "mindinfo";
public string Description => "Lists info for the mind of a specific player."; public string Description => "Lists info for the mind of a specific player.";
public string Help => "mindinfo <session ID>"; public string Help => "mindinfo <session ID>";
public void Execute(IConsoleShell shell, string argStr, string[] args) public void Execute(IConsoleShell shell, string argStr, string[] args)
@@ -25,15 +25,14 @@ namespace Content.Server.Mind.Commands
} }
var mgr = IoCManager.Resolve<IPlayerManager>(); var mgr = IoCManager.Resolve<IPlayerManager>();
if (!mgr.TryGetSessionByUsername(args[0], out var data)) if (!mgr.TryGetSessionByUsername(args[0], out var session))
{ {
shell.WriteLine("Can't find that mind"); shell.WriteLine("Can't find that mind");
return; return;
} }
var mind = data.ContentData()?.Mind; var minds = _entities.System<MindSystem>();
if (!minds.TryGetMind(session, out var mindId, out var mind))
if (mind == null)
{ {
shell.WriteLine("Can't find that mind"); shell.WriteLine("Can't find that mind");
return; return;
@@ -41,7 +40,9 @@ namespace Content.Server.Mind.Commands
var builder = new StringBuilder(); var builder = new StringBuilder();
builder.AppendFormat("player: {0}, mob: {1}\nroles: ", mind.UserId, mind.OwnedEntity); builder.AppendFormat("player: {0}, mob: {1}\nroles: ", mind.UserId, mind.OwnedEntity);
foreach (var role in mind.AllRoles)
var roles = _entities.System<RoleSystem>();
foreach (var role in roles.MindGetAllRoles(mindId))
{ {
builder.AppendFormat("{0} ", role.Name); builder.AppendFormat("{0} ", role.Name);
} }

View File

@@ -1,7 +1,6 @@
using Content.Server.Access.Systems; using Content.Server.Access.Systems;
using Content.Server.Administration; using Content.Server.Administration;
using Content.Server.Administration.Systems; using Content.Server.Administration.Systems;
using Content.Server.Mind.Components;
using Content.Server.PDA; using Content.Server.PDA;
using Content.Server.StationRecords.Systems; using Content.Server.StationRecords.Systems;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
@@ -46,16 +45,16 @@ public sealed class RenameCommand : IConsoleCommand
var oldName = metadata.EntityName; var oldName = metadata.EntityName;
entMan.System<MetaDataSystem>().SetEntityName(entityUid, name, metadata); entMan.System<MetaDataSystem>().SetEntityName(entityUid, name, metadata);
var entSysMan = IoCManager.Resolve<IEntitySystemManager>(); var minds = entMan.System<MindSystem>();
if (entMan.TryGetComponent(entityUid, out MindContainerComponent? mind) && mind.Mind != null) if (minds.TryGetMind(entityUid, out var mindId, out var mind))
{ {
// Mind // Mind
mind.Mind.CharacterName = name; mind.CharacterName = name;
} }
// Id Cards // Id Cards
if (entSysMan.TryGetEntitySystem<IdCardSystem>(out var idCardSystem)) if (entMan.TrySystem<IdCardSystem>(out var idCardSystem))
{ {
if (idCardSystem.TryFindIdCard(entityUid, out var idCard)) if (idCardSystem.TryFindIdCard(entityUid, out var idCard))
{ {
@@ -63,7 +62,7 @@ public sealed class RenameCommand : IConsoleCommand
// Records // Records
// This is done here because ID cards are linked to station records // This is done here because ID cards are linked to station records
if (entSysMan.TryGetEntitySystem<StationRecordsSystem>(out var recordsSystem) if (entMan.TrySystem<StationRecordsSystem>(out var recordsSystem)
&& entMan.TryGetComponent(idCard.Owner, out StationRecordKeyStorageComponent? keyStorage) && entMan.TryGetComponent(idCard.Owner, out StationRecordKeyStorageComponent? keyStorage)
&& keyStorage.Key != null) && keyStorage.Key != null)
{ {
@@ -80,7 +79,7 @@ public sealed class RenameCommand : IConsoleCommand
} }
// PDAs // PDAs
if (entSysMan.TryGetEntitySystem<PdaSystem>(out var pdaSystem)) if (entMan.TrySystem<PdaSystem>(out var pdaSystem))
{ {
var query = entMan.EntityQueryEnumerator<PdaComponent>(); var query = entMan.EntityQueryEnumerator<PdaComponent>();
while (query.MoveNext(out var uid, out var pda)) while (query.MoveNext(out var uid, out var pda))
@@ -93,7 +92,7 @@ public sealed class RenameCommand : IConsoleCommand
} }
// Admin Overlay // Admin Overlay
if (entSysMan.TryGetEntitySystem<AdminSystem>(out var adminSystem) if (entMan.TrySystem<AdminSystem>(out var adminSystem)
&& entMan.TryGetComponent<ActorComponent>(entityUid, out var actorComp)) && entMan.TryGetComponent<ActorComponent>(entityUid, out var actorComp))
{ {
adminSystem.UpdatePlayerList(actorComp.PlayerSession); adminSystem.UpdatePlayerList(actorComp.PlayerSession);

View File

@@ -3,7 +3,7 @@ using System.Diagnostics.CodeAnalysis;
namespace Content.Server.Mind.Components namespace Content.Server.Mind.Components
{ {
/// <summary> /// <summary>
/// Stores a <see cref="Server.Mind.Mind"/> on a mob. /// Stores a <see cref="MindComponent"/> on a mob.
/// </summary> /// </summary>
[RegisterComponent, Access(typeof(MindSystem))] [RegisterComponent, Access(typeof(MindSystem))]
public sealed partial class MindContainerComponent : Component public sealed partial class MindContainerComponent : Component
@@ -13,7 +13,7 @@ namespace Content.Server.Mind.Components
/// </summary> /// </summary>
[ViewVariables] [ViewVariables]
[Access(typeof(MindSystem), Other = AccessPermissions.ReadWriteExecute)] // FIXME Friends [Access(typeof(MindSystem), Other = AccessPermissions.ReadWriteExecute)] // FIXME Friends
public Mind? Mind { get; set; } public EntityUid? Mind { get; set; }
/// <summary> /// <summary>
/// True if we have a mind, false otherwise. /// True if we have a mind, false otherwise.
@@ -40,10 +40,12 @@ namespace Content.Server.Mind.Components
public sealed class MindRemovedMessage : EntityEventArgs public sealed class MindRemovedMessage : EntityEventArgs
{ {
public Mind OldMind; public EntityUid OldMindId;
public MindComponent OldMind;
public MindRemovedMessage(Mind oldMind) public MindRemovedMessage(EntityUid oldMindId, MindComponent oldMind)
{ {
OldMindId = oldMindId;
OldMind = oldMind; OldMind = oldMind;
} }
} }

View File

@@ -4,7 +4,10 @@ namespace Content.Server.Mind.Components
public sealed partial class VisitingMindComponent : Component public sealed partial class VisitingMindComponent : Component
{ {
[ViewVariables] [ViewVariables]
public Mind? Mind; public EntityUid? MindId;
[ViewVariables]
public MindComponent? Mind;
} }
public sealed class MindUnvisitedMessage : EntityEventArgs public sealed class MindUnvisitedMessage : EntityEventArgs

View File

@@ -1,15 +1,14 @@
using System.Linq; using Content.Server.GameTicking;
using Content.Server.GameTicking;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Server.Objectives; using Content.Server.Objectives;
using Content.Server.Roles;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Network; using Robust.Shared.Network;
namespace Content.Server.Mind namespace Content.Server.Mind
{ {
/// <summary> /// <summary>
/// A mind represents the IC "mind" of a player. Stores roles currently. /// A mind represents the IC "mind" of a player.
/// Roles are attached as components to its owning entity.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Think of it like this: if a player is supposed to have their memories, /// Think of it like this: if a player is supposed to have their memories,
@@ -18,23 +17,11 @@ namespace Content.Server.Mind
/// Things such as respawning do not follow, because you're a new character. /// Things such as respawning do not follow, because you're a new character.
/// Getting borged, cloned, turned into a catbeast, etc... will keep it following you. /// Getting borged, cloned, turned into a catbeast, etc... will keep it following you.
/// </remarks> /// </remarks>
public sealed class Mind [RegisterComponent]
public sealed partial class MindComponent : Component
{ {
internal readonly ISet<Role> Roles = new HashSet<Role>();
internal readonly List<Objective> Objectives = new(); internal readonly List<Objective> Objectives = new();
public string Briefing = String.Empty;
/// <summary>
/// Creates the new mind.
/// Note: the Mind is NOT initially attached!
/// The provided UserId is solely for tracking of intended owner.
/// </summary>
public Mind()
{
}
/// <summary> /// <summary>
/// The session ID of the player owning this mind. /// The session ID of the player owning this mind.
/// </summary> /// </summary>
@@ -87,12 +74,7 @@ namespace Content.Server.Mind
[ViewVariables, Access(typeof(MindSystem))] [ViewVariables, Access(typeof(MindSystem))]
public EntityUid? OwnedEntity { get; set; } public EntityUid? OwnedEntity { get; set; }
/// <summary> // TODO move objectives out of mind component
/// An enumerable over all the roles this mind has.
/// </summary>
[ViewVariables]
public IEnumerable<Role> AllRoles => Roles;
/// <summary> /// <summary>
/// An enumerable over all the objectives this mind has. /// An enumerable over all the objectives this mind has.
/// </summary> /// </summary>
@@ -119,10 +101,5 @@ namespace Content.Server.Mind
/// </summary> /// </summary>
[ViewVariables, Access(typeof(MindSystem), typeof(GameTicker))] [ViewVariables, Access(typeof(MindSystem), typeof(GameTicker))]
public IPlayerSession? Session { get; internal set; } public IPlayerSession? Session { get; internal set; }
/// <summary>
/// Gets the current job
/// </summary>
public Job? CurrentJob => Roles.OfType<Job>().SingleOrDefault();
} }
} }

View File

@@ -1,19 +1,17 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Server.Ghost; using Content.Server.Ghost;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Server.Objectives; using Content.Server.Objectives;
using Content.Server.Players; using Content.Server.Players;
using Content.Server.Roles;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Ghost; using Content.Shared.Ghost;
using Content.Shared.Mobs.Systems;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Map; using Robust.Shared.Map;
@@ -36,7 +34,7 @@ public sealed class MindSystem : EntitySystem
[Dependency] private readonly MetaDataSystem _metaData = default!; [Dependency] private readonly MetaDataSystem _metaData = default!;
// This is dictionary is required to track the minds of disconnected players that may have had their entity deleted. // This is dictionary is required to track the minds of disconnected players that may have had their entity deleted.
private readonly Dictionary<NetUserId, Mind> _userMinds = new(); private readonly Dictionary<NetUserId, EntityUid> _userMinds = new();
public override void Initialize() public override void Initialize()
{ {
@@ -55,14 +53,6 @@ public sealed class MindSystem : EntitySystem
WipeAllMinds(); WipeAllMinds();
} }
public void SetGhostOnShutdown(EntityUid uid, bool value, MindContainerComponent? mind = null)
{
if (!Resolve(uid, ref mind))
return;
mind.GhostOnShutdown = value;
}
private void OnReset(RoundRestartCleanupEvent ev) private void OnReset(RoundRestartCleanupEvent ev)
{ {
WipeAllMinds(); WipeAllMinds();
@@ -86,59 +76,35 @@ public sealed class MindSystem : EntitySystem
} }
} }
public Mind? GetMind(NetUserId user) public EntityUid? GetMind(NetUserId user)
{ {
TryGetMind(user, out var mind); TryGetMind(user, out var mind, out _);
return mind; return mind;
} }
public bool TryGetMind(NetUserId user, [NotNullWhen(true)] out Mind? mind) public bool TryGetMind(NetUserId user, [NotNullWhen(true)] out EntityUid? mindId, [NotNullWhen(true)] out MindComponent? mind)
{ {
if (_userMinds.TryGetValue(user, out mind)) if (_userMinds.TryGetValue(user, out var mindIdValue) &&
TryComp(mindIdValue, out mind))
{ {
DebugTools.Assert(mind.UserId == user); DebugTools.Assert(mind.UserId == user);
DebugTools.Assert(_playerManager.GetPlayerData(user).ContentData() is not {} data DebugTools.Assert(_playerManager.GetPlayerData(user).ContentData() is not {} data
|| data.Mind == mind); || data.Mind == mindIdValue);
mindId = mindIdValue;
return true; return true;
} }
DebugTools.Assert(_playerManager.GetPlayerData(user).ContentData()?.Mind == null); DebugTools.Assert(_playerManager.GetPlayerData(user).ContentData()?.Mind == null);
mindId = null;
mind = null;
return false; return false;
} }
/// <summary>
/// Don't call this unless you know what the hell you're doing.
/// Use <see cref="MindSystem.TransferTo(Mind,System.Nullable{Robust.Shared.GameObjects.EntityUid},bool)"/> instead.
/// If that doesn't cover it, make something to cover it.
/// </summary>
private void InternalAssignMind(EntityUid uid, Mind value, MindContainerComponent? mind = null)
{
if (!Resolve(uid, ref mind))
return;
mind.Mind = value;
RaiseLocalEvent(uid, new MindAddedMessage(), true);
}
/// <summary>
/// Don't call this unless you know what the hell you're doing.
/// Use <see cref="MindSystem.TransferTo(Mind,System.Nullable{Robust.Shared.GameObjects.EntityUid},bool)"/> instead.
/// If that doesn't cover it, make something to cover it.
/// </summary>
private void InternalEjectMind(EntityUid uid, MindContainerComponent? mind = null)
{
if (!Resolve(uid, ref mind, false) || mind.Mind == null)
return;
var oldMind = mind.Mind;
mind.Mind = null;
RaiseLocalEvent(uid, new MindRemovedMessage(oldMind), true);
}
private void OnVisitingTerminating(EntityUid uid, VisitingMindComponent component, ref EntityTerminatingEvent args) private void OnVisitingTerminating(EntityUid uid, VisitingMindComponent component, ref EntityTerminatingEvent args)
{ {
if (component.Mind != null) if (component.MindId != null)
UnVisit(component.Mind); UnVisit(component.MindId.Value, component.Mind);
} }
private void OnMindContainerTerminating(EntityUid uid, MindContainerComponent component, ref EntityTerminatingEvent args) private void OnMindContainerTerminating(EntityUid uid, MindContainerComponent component, ref EntityTerminatingEvent args)
@@ -147,7 +113,7 @@ public sealed class MindSystem : EntitySystem
if (_gameTicker.RunLevel == GameRunLevel.PreRoundLobby) if (_gameTicker.RunLevel == GameRunLevel.PreRoundLobby)
return; return;
if (component.Mind is not { } mind) if (!TryGetMind(uid, out var mindId, out var mind, component))
return; return;
// If the player is currently visiting some other entity, simply attach to that entity. // If the player is currently visiting some other entity, simply attach to that entity.
@@ -156,13 +122,13 @@ public sealed class MindSystem : EntitySystem
&& !Deleted(visiting) && !Deleted(visiting)
&& !Terminating(visiting)) && !Terminating(visiting))
{ {
TransferTo(mind, visiting); TransferTo(mindId, visiting, mind: mind);
if (TryComp(visiting, out GhostComponent? ghost)) if (TryComp(visiting, out GhostComponent? ghost))
_ghostSystem.SetCanReturnToBody(ghost, false); _ghostSystem.SetCanReturnToBody(ghost, false);
return; return;
} }
TransferTo(mind, null, createGhost: false); TransferTo(mindId, null, createGhost: false, mind: mind);
if (component.GhostOnShutdown && mind.Session != null) if (component.GhostOnShutdown && mind.Session != null)
{ {
@@ -187,7 +153,7 @@ public sealed class MindSystem : EntitySystem
{ {
// This should be an error, if it didn't cause tests to start erroring when they delete a player. // This should be an error, if it didn't cause tests to start erroring when they delete a player.
Log.Warning($"Entity \"{ToPrettyString(uid)}\" for {mind.CharacterName} was deleted, and no applicable spawn location is available."); Log.Warning($"Entity \"{ToPrettyString(uid)}\" for {mind.CharacterName} was deleted, and no applicable spawn location is available.");
TransferTo(mind, null, createGhost: false); TransferTo(mindId, null, createGhost: false, mind: mind);
return; return;
} }
@@ -200,7 +166,7 @@ public sealed class MindSystem : EntitySystem
var val = mind.CharacterName ?? string.Empty; var val = mind.CharacterName ?? string.Empty;
_metaData.SetEntityName(ghost, val); _metaData.SetEntityName(ghost, val);
TransferTo(mind, ghost); TransferTo(mindId, ghost, mind: mind);
}); });
} }
} }
@@ -211,7 +177,7 @@ public sealed class MindSystem : EntitySystem
return; return;
var dead = _mobStateSystem.IsDead(uid); var dead = _mobStateSystem.IsDead(uid);
var hasSession = mindContainer.Mind?.Session; var hasSession = CompOrNull<MindComponent>(mindContainer.Mind)?.Session;
if (dead && !mindContainer.HasMind) if (dead && !mindContainer.HasMind)
args.PushMarkup($"[color=mediumpurple]{Loc.GetString("comp-mind-examined-dead-and-irrecoverable", ("ent", uid))}[/color]"); args.PushMarkup($"[color=mediumpurple]{Loc.GetString("comp-mind-examined-dead-and-irrecoverable", ("ent", uid))}[/color]");
@@ -230,36 +196,40 @@ public sealed class MindSystem : EntitySystem
if (args.Handled) if (args.Handled)
return; return;
if (component.HasMind && component.Mind.PreventSuicide) if (TryComp(component.Mind, out MindComponent? mind) && mind.PreventSuicide)
{ {
args.BlockSuicideAttempt(true); args.BlockSuicideAttempt(true);
} }
} }
public Mind? GetMind(EntityUid uid, MindContainerComponent? mind = null) public EntityUid? GetMind(EntityUid uid, MindContainerComponent? mind = null)
{ {
if (!Resolve(uid, ref mind)) if (!Resolve(uid, ref mind))
return null; return null;
if (mind.HasMind) if (mind.HasMind)
return mind.Mind; return mind.Mind;
return null; return null;
} }
public Mind CreateMind(NetUserId? userId, string? name = null) public EntityUid CreateMind(NetUserId? userId, string? name = null)
{ {
var mind = new Mind(); var mindId = Spawn(null, MapCoordinates.Nullspace);
var mind = EnsureComp<MindComponent>(mindId);
mind.CharacterName = name; mind.CharacterName = name;
SetUserId(mind, userId); SetUserId(mindId, userId, mind);
return mind; Dirty(mindId, MetaData(mindId));
return mindId;
} }
/// <summary> /// <summary>
/// True if the OwnedEntity of this mind is physically dead. /// True if the OwnedEntity of this mind is physically dead.
/// This specific definition, as opposed to CharacterDeadIC, is used to determine if ghosting should allow return. /// This specific definition, as opposed to CharacterDeadIC, is used to determine if ghosting should allow return.
/// </summary> /// </summary>
public bool IsCharacterDeadPhysically(Mind mind) public bool IsCharacterDeadPhysically(MindComponent mind)
{ {
// This is written explicitly so that the logic can be understood. // This is written explicitly so that the logic can be understood.
// But it's also weird and potentially situational. // But it's also weird and potentially situational.
@@ -285,8 +255,11 @@ public sealed class MindSystem : EntitySystem
return _mobStateSystem.IsDead(mind.OwnedEntity.Value, targetMobState); return _mobStateSystem.IsDead(mind.OwnedEntity.Value, targetMobState);
} }
public void Visit(Mind mind, EntityUid entity) public void Visit(EntityUid mindId, EntityUid entity, MindComponent? mind = null)
{ {
if (!Resolve(mindId, ref mind))
return;
if (mind.VisitingEntity != null) if (mind.VisitingEntity != null)
{ {
Log.Error($"Attempted to visit an entity ({ToPrettyString(entity)}) while already visiting another ({ToPrettyString(mind.VisitingEntity.Value)})."); Log.Error($"Attempted to visit an entity ({ToPrettyString(entity)}) while already visiting another ({ToPrettyString(mind.VisitingEntity.Value)}).");
@@ -304,6 +277,7 @@ public sealed class MindSystem : EntitySystem
// EnsureComp instead of AddComp to deal with deferred deletions. // EnsureComp instead of AddComp to deal with deferred deletions.
var comp = EnsureComp<VisitingMindComponent>(entity); var comp = EnsureComp<VisitingMindComponent>(entity);
comp.MindId = mindId;
comp.Mind = mind; comp.Mind = mind;
Log.Info($"Session {mind.Session?.Name} visiting entity {entity}."); Log.Info($"Session {mind.Session?.Name} visiting entity {entity}.");
} }
@@ -311,9 +285,12 @@ public sealed class MindSystem : EntitySystem
/// <summary> /// <summary>
/// Returns the mind to its original entity. /// Returns the mind to its original entity.
/// </summary> /// </summary>
public void UnVisit(Mind? mind) public void UnVisit(EntityUid mindId, MindComponent? mind = null)
{ {
if (mind == null || mind.VisitingEntity == null) if (!Resolve(mindId, ref mind))
return;
if (mind.VisitingEntity == null)
return; return;
RemoveVisitingEntity(mind); RemoveVisitingEntity(mind);
@@ -331,11 +308,22 @@ public sealed class MindSystem : EntitySystem
} }
} }
/// <summary>
/// Returns the mind to its original entity.
/// </summary>
public void UnVisit(IPlayerSession? player)
{
if (player == null || !TryGetMind(player, out var mindId, out var mind))
return;
UnVisit(mindId, mind);
}
/// <summary> /// <summary>
/// Cleans up the VisitingEntity. /// Cleans up the VisitingEntity.
/// </summary> /// </summary>
/// <param name="mind"></param> /// <param name="mind"></param>
private void RemoveVisitingEntity(Mind mind) private void RemoveVisitingEntity(MindComponent mind)
{ {
if (mind.VisitingEntity == null) if (mind.VisitingEntity == null)
return; return;
@@ -363,19 +351,19 @@ public sealed class MindSystem : EntitySystem
/// <summary> /// <summary>
/// Detaches a mind from all entities and clears the user ID. /// Detaches a mind from all entities and clears the user ID.
/// </summary> /// </summary>
public void WipeMind(Mind? mind) public void WipeMind(EntityUid? mindId, MindComponent? mind = null)
{ {
if (mind == null) if (mindId == null || !Resolve(mindId.Value, ref mind, false))
return; return;
TransferTo(mind, null); TransferTo(mindId.Value, null, mind: mind);
SetUserId(mind, null); SetUserId(mindId.Value, null, mind: mind);
} }
/// <summary> /// <summary>
/// Transfer this mind's control over to a new entity. /// Transfer this mind's control over to a new entity.
/// </summary> /// </summary>
/// <param name="mind">The mind to transfer</param> /// <param name="mindId">The mind to transfer</param>
/// <param name="entity"> /// <param name="entity">
/// The entity to control. /// The entity to control.
/// Can be null, in which case it will simply detach the mind from any entity. /// Can be null, in which case it will simply detach the mind from any entity.
@@ -384,10 +372,13 @@ public sealed class MindSystem : EntitySystem
/// If true, skips ghost check for Visiting Entity /// If true, skips ghost check for Visiting Entity
/// </param> /// </param>
/// <exception cref="ArgumentException"> /// <exception cref="ArgumentException">
/// Thrown if <paramref name="entity"/> is already owned by another mind. /// Thrown if <paramref name="entity"/> is already controlled by another player.
/// </exception> /// </exception>
public void TransferTo(Mind mind, EntityUid? entity, bool ghostCheckOverride = false, bool createGhost = true) public void TransferTo(EntityUid mindId, EntityUid? entity, bool ghostCheckOverride = false, bool createGhost = true, MindComponent? mind = null)
{ {
if (!Resolve(mindId, ref mind))
return;
if (entity == mind.OwnedEntity) if (entity == mind.OwnedEntity)
return; return;
@@ -399,7 +390,7 @@ public sealed class MindSystem : EntitySystem
component = EnsureComp<MindContainerComponent>(entity.Value); component = EnsureComp<MindContainerComponent>(entity.Value);
if (component.HasMind) if (component.HasMind)
_gameTicker.OnGhostAttempt(component.Mind, false); _gameTicker.OnGhostAttempt(component.Mind.Value, false);
if (TryComp<ActorComponent>(entity.Value, out var actor)) if (TryComp<ActorComponent>(entity.Value, out var actor))
{ {
@@ -426,7 +417,10 @@ public sealed class MindSystem : EntitySystem
var oldComp = mind.OwnedComponent; var oldComp = mind.OwnedComponent;
var oldEntity = mind.OwnedEntity; var oldEntity = mind.OwnedEntity;
if (oldComp != null && oldEntity != null) if (oldComp != null && oldEntity != null)
InternalEjectMind(oldEntity.Value, oldComp); {
oldComp.Mind = null;
RaiseLocalEvent(oldEntity.Value, new MindRemovedMessage(oldEntity.Value, mind), true);
}
SetOwnedEntity(mind, entity, component); SetOwnedEntity(mind, entity, component);
@@ -455,7 +449,8 @@ public sealed class MindSystem : EntitySystem
if (mind.OwnedComponent != null) if (mind.OwnedComponent != null)
{ {
InternalAssignMind(mind.OwnedEntity!.Value, mind, mind.OwnedComponent); mind.OwnedComponent.Mind = mindId;
RaiseLocalEvent(mind.OwnedEntity!.Value, new MindAddedMessage(), true);
mind.OriginalOwnedEntity ??= mind.OwnedEntity; mind.OriginalOwnedEntity ??= mind.OwnedEntity;
} }
} }
@@ -463,11 +458,11 @@ public sealed class MindSystem : EntitySystem
/// <summary> /// <summary>
/// Adds an objective to this mind. /// Adds an objective to this mind.
/// </summary> /// </summary>
public bool TryAddObjective(Mind mind, ObjectivePrototype objectivePrototype) public bool TryAddObjective(EntityUid mindId, MindComponent mind, ObjectivePrototype objectivePrototype)
{ {
if (!objectivePrototype.CanBeAssigned(mind)) if (!objectivePrototype.CanBeAssigned(mindId, mind))
return false; return false;
var objective = objectivePrototype.GetObjective(mind); var objective = objectivePrototype.GetObjective(mindId, mind);
if (mind.Objectives.Contains(objective)) if (mind.Objectives.Contains(objective))
return false; return false;
@@ -476,7 +471,6 @@ public sealed class MindSystem : EntitySystem
_adminLogger.Add(LogType.Mind, LogImpact.Low, $"'{condition.Title}' added to mind of {MindOwnerLoggingString(mind)}"); _adminLogger.Add(LogType.Mind, LogImpact.Low, $"'{condition.Title}' added to mind of {MindOwnerLoggingString(mind)}");
} }
mind.Objectives.Add(objective); mind.Objectives.Add(objective);
return true; return true;
} }
@@ -485,9 +479,10 @@ public sealed class MindSystem : EntitySystem
/// Removes an objective to this mind. /// Removes an objective to this mind.
/// </summary> /// </summary>
/// <returns>Returns true if the removal succeeded.</returns> /// <returns>Returns true if the removal succeeded.</returns>
public bool TryRemoveObjective(Mind mind, int index) public bool TryRemoveObjective(MindComponent mind, int index)
{ {
if (index < 0 || index >= mind.Objectives.Count) return false; if (index < 0 || index >= mind.Objectives.Count)
return false;
var objective = mind.Objectives[index]; var objective = mind.Objectives[index];
@@ -500,90 +495,56 @@ public sealed class MindSystem : EntitySystem
return true; return true;
} }
/// <summary> public bool TryGetSession(EntityUid? mindId, [NotNullWhen(true)] out IPlayerSession? session)
/// Gives this mind a new role.
/// </summary>
/// <param name="mind">The mind to add the role to.</param>
/// <param name="role">The type of the role to give.</param>
/// <returns>The instance of the role.</returns>
/// <exception cref="ArgumentException">
/// Thrown if we already have a role with this type.
/// </exception>
public void AddRole(Mind mind, Role role)
{ {
if (mind.Roles.Contains(role)) session = null;
{ return TryComp(mindId, out MindComponent? mind) && (session = mind.Session) != null;
throw new ArgumentException($"We already have this role: {role}");
}
mind.Roles.Add(role);
role.Greet();
var message = new RoleAddedEvent(mind, role);
if (mind.OwnedEntity != null)
{
RaiseLocalEvent(mind.OwnedEntity.Value, message, true);
}
_adminLogger.Add(LogType.Mind, LogImpact.Low,
$"'{role.Name}' added to mind of {MindOwnerLoggingString(mind)}");
}
/// <summary>
/// Removes a role from this mind.
/// </summary>
/// <param name="mind">The mind to remove the role from.</param>
/// <param name="role">The type of the role to remove.</param>
/// <exception cref="ArgumentException">
/// Thrown if we do not have this role.
/// </exception>
public void RemoveRole(Mind mind, Role role)
{
if (!mind.Roles.Contains(role))
{
throw new ArgumentException($"We do not have this role: {role}");
}
mind.Roles.Remove(role);
var message = new RoleRemovedEvent(mind, role);
if (mind.OwnedEntity != null)
{
RaiseLocalEvent(mind.OwnedEntity.Value, message, true);
}
_adminLogger.Add(LogType.Mind, LogImpact.Low,
$"'{role.Name}' removed from mind of {MindOwnerLoggingString(mind)}");
}
public bool HasRole<T>(Mind mind) where T : Role
{
return mind.Roles.Any(role => role is T);
}
public bool TryGetSession(Mind mind, [NotNullWhen(true)] out IPlayerSession? session)
{
return (session = mind.Session) != null;
} }
/// <summary> /// <summary>
/// Gets a mind from uid and/or MindContainerComponent. Used for null checks. /// Gets a mind from uid and/or MindContainerComponent. Used for null checks.
/// </summary> /// </summary>
/// <param name="uid">Entity UID that owns the mind.</param> /// <param name="uid">Entity UID that owns the mind.</param>
/// <param name="mindId">The mind id.</param>
/// <param name="mind">The returned mind.</param> /// <param name="mind">The returned mind.</param>
/// <param name="mindContainerComponent">Mind component on <paramref name="uid"/> to get the mind from.</param> /// <param name="container">Mind component on <paramref name="uid"/> to get the mind from.</param>
/// <returns>True if mind found. False if not.</returns> /// <returns>True if mind found. False if not.</returns>
public bool TryGetMind(EntityUid uid, [NotNullWhen(true)] out Mind? mind, MindContainerComponent? mindContainerComponent = null) public bool TryGetMind(
EntityUid uid,
out EntityUid mindId,
[NotNullWhen(true)] out MindComponent? mind,
MindContainerComponent? container = null)
{ {
mindId = default;
mind = null; mind = null;
if (!Resolve(uid, ref mindContainerComponent, false))
if (!Resolve(uid, ref container, false))
return false; return false;
if (!mindContainerComponent.HasMind) if (!container.HasMind)
return false; return false;
mind = mindContainerComponent.Mind; mindId = container.Mind ?? default;
return true; return TryComp(mindId, out mind);
}
public bool TryGetMind(
PlayerData player,
out EntityUid mindId,
[NotNullWhen(true)] out MindComponent? mind)
{
mindId = player.Mind ?? default;
return TryComp(mindId, out mind);
}
public bool TryGetMind(
IPlayerSession? player,
out EntityUid mindId,
[NotNullWhen(true)] out MindComponent? mind)
{
mindId = default;
mind = null;
return player?.ContentData() is { } data && TryGetMind(data, out mindId, out mind);
} }
/// <summary> /// <summary>
@@ -592,7 +553,7 @@ public sealed class MindSystem : EntitySystem
/// <param name="mind">Mind to set OwnedComponent and OwnedEntity on</param> /// <param name="mind">Mind to set OwnedComponent and OwnedEntity on</param>
/// <param name="uid">Entity owned by <paramref name="mind"/></param> /// <param name="uid">Entity owned by <paramref name="mind"/></param>
/// <param name="mindContainerComponent">MindContainerComponent owned by <paramref name="mind"/></param> /// <param name="mindContainerComponent">MindContainerComponent owned by <paramref name="mind"/></param>
private void SetOwnedEntity(Mind mind, EntityUid? uid, MindContainerComponent? mindContainerComponent) private void SetOwnedEntity(MindComponent mind, EntityUid? uid, MindContainerComponent? mindContainerComponent)
{ {
if (uid != null) if (uid != null)
Resolve(uid.Value, ref mindContainerComponent); Resolve(uid.Value, ref mindContainerComponent);
@@ -606,8 +567,11 @@ public sealed class MindSystem : EntitySystem
/// entity that any mind is connected to, except as a side effect of the fact that it may change a player's /// entity that any mind is connected to, except as a side effect of the fact that it may change a player's
/// attached entity. E.g., ghosts get deleted. /// attached entity. E.g., ghosts get deleted.
/// </summary> /// </summary>
public void SetUserId(Mind mind, NetUserId? userId) public void SetUserId(EntityUid mindId, NetUserId? userId, MindComponent? mind = null)
{ {
if (!Resolve(mindId, ref mind))
return;
if (mind.UserId == userId) if (mind.UserId == userId)
return; return;
@@ -637,12 +601,15 @@ public sealed class MindSystem : EntitySystem
return; return;
} }
if (_userMinds.TryGetValue(userId.Value, out var oldMind)) if (_userMinds.TryGetValue(userId.Value, out var oldMindId) &&
SetUserId(oldMind, null); TryComp(oldMindId, out MindComponent? oldMind))
{
SetUserId(oldMindId, null, oldMind);
}
DebugTools.AssertNull(_playerManager.GetPlayerData(userId.Value).ContentData()?.Mind); DebugTools.AssertNull(_playerManager.GetPlayerData(userId.Value).ContentData()?.Mind);
_userMinds[userId.Value] = mind; _userMinds[userId.Value] = mindId;
mind.UserId = userId; mind.UserId = userId;
mind.OriginalOwnerUserId ??= userId; mind.OriginalOwnerUserId ??= userId;
@@ -654,7 +621,7 @@ public sealed class MindSystem : EntitySystem
// session may be null, but user data may still exist for disconnected players. // session may be null, but user data may still exist for disconnected players.
if (_playerManager.GetPlayerData(userId.Value).ContentData() is { } data) if (_playerManager.GetPlayerData(userId.Value).ContentData() is { } data)
data.Mind = mind; data.Mind = mindId;
} }
/// <summary> /// <summary>
@@ -663,7 +630,7 @@ public sealed class MindSystem : EntitySystem
/// "If administrators decide that zombies are dead, this returns true for zombies." /// "If administrators decide that zombies are dead, this returns true for zombies."
/// (Maybe you were looking for the action blocker system?) /// (Maybe you were looking for the action blocker system?)
/// </summary> /// </summary>
public bool IsCharacterDeadIc(Mind mind) public bool IsCharacterDeadIc(MindComponent mind)
{ {
if (mind.OwnedEntity is { } owned) if (mind.OwnedEntity is { } owned)
{ {
@@ -680,7 +647,7 @@ public sealed class MindSystem : EntitySystem
/// <summary> /// <summary>
/// A string to represent the mind for logging /// A string to represent the mind for logging
/// </summary> /// </summary>
private string MindOwnerLoggingString(Mind mind) public string MindOwnerLoggingString(MindComponent mind)
{ {
if (mind.OwnedEntity != null) if (mind.OwnedEntity != null)
return ToPrettyString(mind.OwnedEntity.Value); return ToPrettyString(mind.OwnedEntity.Value);
@@ -688,6 +655,11 @@ public sealed class MindSystem : EntitySystem
return mind.UserId.Value.ToString(); return mind.UserId.Value.ToString();
return "(originally " + mind.OriginalOwnerUserId + ")"; return "(originally " + mind.OriginalOwnerUserId + ")";
} }
public string? GetCharacterName(NetUserId userId)
{
return TryGetMind(userId, out _, out var mind) ? mind.CharacterName : null;
}
} }
/// <summary> /// <summary>

View File

@@ -1,39 +0,0 @@
using Content.Server.Mind.Components;
using Content.Shared.GameTicking;
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 sealed class MindTrackerSystem : EntitySystem
{
[ViewVariables]
public readonly HashSet<Mind> AllMinds = new();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
SubscribeLocalEvent<MindContainerComponent, MindAddedMessage>(OnMindAdded);
}
void Reset(RoundRestartCleanupEvent ev)
{
AllMinds.Clear();
}
void OnMindAdded(EntityUid uid, MindContainerComponent mc, MindAddedMessage args)
{
var mind = mc.Mind;
if (mind != null)
AllMinds.Add(mind);
}
}
}

View File

@@ -1,7 +1,4 @@
using Content.Server.Mind.Components; using Robust.Server.Player;
using Content.Server.Players;
using Robust.Server.Player;
using Robust.Shared.Players;
using Robust.Shared.Toolshed; using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Errors; using Robust.Shared.Toolshed.Errors;
using Robust.Shared.Toolshed.Syntax; using Robust.Shared.Toolshed.Syntax;
@@ -17,20 +14,17 @@ public sealed class MindCommand : ToolshedCommand
private MindSystem? _mind; private MindSystem? _mind;
[CommandImplementation("get")] [CommandImplementation("get")]
public Mind? Get([PipedArgument] IPlayerSession session) public MindComponent? Get([PipedArgument] IPlayerSession session)
{ {
return session.ContentData()?.Mind; _mind ??= GetSys<MindSystem>();
return _mind.TryGetMind(session, out _, out var mind) ? mind : null;
} }
[CommandImplementation("get")] [CommandImplementation("get")]
public Mind? Get([PipedArgument] EntityUid ent) public MindComponent? Get([PipedArgument] EntityUid ent)
{ {
if (!TryComp<MindContainerComponent>(ent, out var container)) _mind ??= GetSys<MindSystem>();
{ return _mind.TryGetMind(ent, out _, out var mind) ? mind : null;
return null;
}
return container.Mind;
} }
[CommandImplementation("control")] [CommandImplementation("control")]
@@ -48,15 +42,13 @@ public sealed class MindCommand : ToolshedCommand
return target; return target;
} }
var mind = player.ContentData()?.Mind; if (!_mind.TryGetMind(player, out var mindId, out var mind))
if (mind is null)
{ {
ctx.ReportError(new SessionHasNoEntityError(player)); ctx.ReportError(new SessionHasNoEntityError(player));
return target; return target;
} }
_mind.TransferTo(mind, target); _mind.TransferTo(mindId, target, mind: mind);
return target; return target;
} }
} }

View File

@@ -25,7 +25,7 @@ public sealed class TransferMindOnGibSystem : EntitySystem
private void OnGib(EntityUid uid, TransferMindOnGibComponent component, BeingGibbedEvent args) private void OnGib(EntityUid uid, TransferMindOnGibComponent component, BeingGibbedEvent args)
{ {
if (!TryComp<MindContainerComponent>(uid, out var mindcomp) || mindcomp.Mind == null) if (!_mindSystem.TryGetMind(uid, out var mindId, out var mind))
return; return;
var validParts = args.GibbedParts.Where(p => _tag.HasTag(p, component.TargetTag)).ToHashSet(); var validParts = args.GibbedParts.Where(p => _tag.HasTag(p, component.TargetTag)).ToHashSet();
@@ -33,6 +33,6 @@ public sealed class TransferMindOnGibSystem : EntitySystem
return; return;
var ent = _random.Pick(validParts); var ent = _random.Pick(validParts);
_mindSystem.TransferTo(mindcomp.Mind, ent); _mindSystem.TransferTo(mindId, ent, mind: mind);
} }
} }

View File

@@ -1,20 +1,20 @@
using Content.Server.Morgue.Components;
using Content.Shared.Morgue;
using Robust.Server.GameObjects;
using Robust.Shared.Player;
using Content.Server.Storage.Components;
using Content.Shared.Verbs;
using Content.Shared.Database;
using Content.Shared.Interaction.Events;
using Content.Server.Players;
using Content.Server.GameTicking; using Content.Server.GameTicking;
using Content.Shared.Popups; using Content.Server.Mind;
using Content.Server.Morgue.Components;
using Content.Server.Storage.Components;
using Content.Server.Storage.EntitySystems; using Content.Server.Storage.EntitySystems;
using Content.Shared.Database;
using Content.Shared.Examine; using Content.Shared.Examine;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction.Events;
using Content.Shared.Morgue;
using Content.Shared.Popups;
using Content.Shared.Standing; using Content.Shared.Standing;
using Content.Shared.Storage; using Content.Shared.Storage;
using Content.Shared.IdentityManagement;
using Content.Shared.Storage.Components; using Content.Shared.Storage.Components;
using Content.Shared.Verbs;
using Robust.Server.GameObjects;
using Robust.Shared.Player;
namespace Content.Server.Morgue; namespace Content.Server.Morgue;
@@ -26,6 +26,7 @@ public sealed class CrematoriumSystem : EntitySystem
[Dependency] private readonly EntityStorageSystem _entityStorage = default!; [Dependency] private readonly EntityStorageSystem _entityStorage = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly StandingStateSystem _standing = default!; [Dependency] private readonly StandingStateSystem _standing = default!;
[Dependency] private readonly MindSystem _minds = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -141,9 +142,9 @@ public sealed class CrematoriumSystem : EntitySystem
args.SetHandled(SuicideKind.Heat); args.SetHandled(SuicideKind.Heat);
var victim = args.Victim; var victim = args.Victim;
if (TryComp(victim, out ActorComponent? actor) && actor.PlayerSession.ContentData()?.Mind is { } mind) if (TryComp(victim, out ActorComponent? actor) && _minds.TryGetMind(victim, out var mindId, out var mind))
{ {
_ticker.OnGhostAttempt(mind, false); _ticker.OnGhostAttempt(mindId, false, mind: mind);
if (mind.OwnedEntity is { Valid: true } entity) if (mind.OwnedEntity is { Valid: true } entity)
{ {

View File

@@ -1,6 +1,5 @@
using Content.Server.Administration; using Content.Server.Administration;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Players;
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Console; using Robust.Shared.Console;
@@ -25,15 +24,14 @@ namespace Content.Server.Objectives.Commands
} }
var mgr = IoCManager.Resolve<IPlayerManager>(); var mgr = IoCManager.Resolve<IPlayerManager>();
if (!mgr.TryGetPlayerDataByUsername(args[0], out var data)) if (!mgr.TryGetSessionByUsername(args[0], out var data))
{ {
shell.WriteLine("Can't find the playerdata."); shell.WriteLine("Can't find the playerdata.");
return; return;
} }
var minds = _entityManager.System<MindSystem>();
var mind = data.ContentData()?.Mind; if (!minds.TryGetMind(data, out var mindId, out var mind))
if (mind == null)
{ {
shell.WriteLine("Can't find the mind."); shell.WriteLine("Can't find the mind.");
return; return;
@@ -47,12 +45,10 @@ namespace Content.Server.Objectives.Commands
} }
var mindSystem = _entityManager.System<MindSystem>(); var mindSystem = _entityManager.System<MindSystem>();
if (!mindSystem.TryAddObjective(mindId, mind, objectivePrototype))
if (!mindSystem.TryAddObjective(mind, objectivePrototype))
{ {
shell.WriteLine("Objective requirements dont allow that objective to be added."); shell.WriteLine("Objective requirements dont allow that objective to be added.");
} }
} }
} }
} }

View File

@@ -1,6 +1,6 @@
using System.Linq; using System.Linq;
using Content.Server.Administration; using Content.Server.Administration;
using Content.Server.Players; using Content.Server.Mind;
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Console; using Robust.Shared.Console;
@@ -10,39 +10,38 @@ namespace Content.Server.Objectives.Commands
[AdminCommand(AdminFlags.Logs)] [AdminCommand(AdminFlags.Logs)]
public sealed class ListObjectivesCommand : LocalizedCommands public sealed class ListObjectivesCommand : LocalizedCommands
{ {
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IPlayerManager _players = default!;
public override string Command => "lsobjectives"; public override string Command => "lsobjectives";
public override void Execute(IConsoleShell shell, string argStr, string[] args) public override void Execute(IConsoleShell shell, string argStr, string[] args)
{ {
var player = shell.Player as IPlayerSession; var player = shell.Player as IPlayerSession;
IPlayerData? data; if (player == null || !_players.TryGetSessionByUsername(args[0], out player))
if (args.Length == 0 && player != null)
{
data = player.Data;
}
else if (player == null || !IoCManager.Resolve<IPlayerManager>().TryGetPlayerDataByUsername(args[0], out data))
{ {
shell.WriteError(LocalizationManager.GetString("shell-target-player-does-not-exist")); shell.WriteError(LocalizationManager.GetString("shell-target-player-does-not-exist"));
return; return;
} }
var mind = data.ContentData()?.Mind; var minds = _entities.System<MindSystem>();
if (mind == null) if (!minds.TryGetMind(player, out _, out var mind))
{ {
shell.WriteError(LocalizationManager.GetString("shell-target-entity-does-not-have-message", ("missing", "mind"))); shell.WriteError(LocalizationManager.GetString("shell-target-entity-does-not-have-message", ("missing", "mind")));
return; return;
} }
shell.WriteLine($"Objectives for player {data.UserId}:"); shell.WriteLine($"Objectives for player {player.UserId}:");
var objectives = mind.AllObjectives.ToList(); var objectives = mind.AllObjectives.ToList();
if (objectives.Count == 0) if (objectives.Count == 0)
{ {
shell.WriteLine("None."); shell.WriteLine("None.");
} }
for (var i = 0; i < objectives.Count; i++) for (var i = 0; i < objectives.Count; i++)
{ {
shell.WriteLine($"- [{i}] {objectives[i].Conditions[0].Title}"); shell.WriteLine($"- [{i}] {objectives[i].Conditions[0].Title}");
} }
} }
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)

View File

@@ -1,6 +1,5 @@
using Content.Server.Administration; using Content.Server.Administration;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Players;
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Console; using Robust.Shared.Console;
@@ -24,10 +23,14 @@ namespace Content.Server.Objectives.Commands
} }
var mgr = IoCManager.Resolve<IPlayerManager>(); var mgr = IoCManager.Resolve<IPlayerManager>();
if (mgr.TryGetPlayerDataByUsername(args[0], out var data)) var minds = _entityManager.System<MindSystem>();
if (!mgr.TryGetSessionByUsername(args[0], out var session))
{ {
var mind = data.ContentData()?.Mind; shell.WriteLine("Can't find the playerdata.");
if (mind == null) return;
}
if (!minds.TryGetMind(session, out _, out var mind))
{ {
shell.WriteLine("Can't find the mind."); shell.WriteLine("Can't find the mind.");
return; return;
@@ -45,10 +48,5 @@ namespace Content.Server.Objectives.Commands
shell.WriteLine($"Invalid index {args[1]}!"); shell.WriteLine($"Invalid index {args[1]}!");
} }
} }
else
{
shell.WriteLine("Can't find the playerdata.");
}
}
} }
} }

View File

@@ -9,9 +9,9 @@ namespace Content.Server.Objectives.Conditions
[DataDefinition] [DataDefinition]
public sealed partial class DieCondition : IObjectiveCondition public sealed partial class DieCondition : IObjectiveCondition
{ {
private Mind.Mind? _mind; private MindComponent? _mind;
public IObjectiveCondition GetAssigned(Mind.Mind mind) public IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind)
{ {
return new DieCondition { _mind = mind }; return new DieCondition { _mind = mind };
} }

View File

@@ -11,11 +11,13 @@ namespace Content.Server.Objectives.Conditions
[DataDefinition] [DataDefinition]
public sealed partial class EscapeShuttleCondition : IObjectiveCondition public sealed partial class EscapeShuttleCondition : IObjectiveCondition
{ {
private Mind.Mind? _mind; // TODO refactor all of this to be ecs
private MindComponent? _mind;
public IObjectiveCondition GetAssigned(Mind.Mind mind) public IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind)
{
return new EscapeShuttleCondition
{ {
return new EscapeShuttleCondition {
_mind = mind, _mind = mind,
}; };
} }

View File

@@ -1,5 +1,6 @@
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Objectives.Interfaces; using Content.Server.Objectives.Interfaces;
using Content.Server.Roles.Jobs;
using Content.Server.Shuttles.Systems; using Content.Server.Shuttles.Systems;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
@@ -10,10 +11,14 @@ namespace Content.Server.Objectives.Conditions
{ {
public abstract class KillPersonCondition : IObjectiveCondition public abstract class KillPersonCondition : IObjectiveCondition
{ {
// TODO refactor all of this to be ecs
protected IEntityManager EntityManager => IoCManager.Resolve<IEntityManager>(); protected IEntityManager EntityManager => IoCManager.Resolve<IEntityManager>();
protected MobStateSystem MobStateSystem => EntityManager.EntitySysManager.GetEntitySystem<MobStateSystem>(); protected MindSystem Minds => EntityManager.System<MindSystem>();
protected Mind.Mind? Target; protected JobSystem Jobs => EntityManager.System<JobSystem>();
public abstract IObjectiveCondition GetAssigned(Mind.Mind mind); protected MobStateSystem MobStateSystem => EntityManager.System<MobStateSystem>();
protected EntityUid? TargetMindId;
protected MindComponent? TargetMind => EntityManager.GetComponentOrNull<MindComponent>(TargetMindId);
public abstract IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind);
/// <summary> /// <summary>
/// Whether the target must be truly dead, ignores missing evac. /// Whether the target must be truly dead, ignores missing evac.
@@ -24,10 +29,11 @@ namespace Content.Server.Objectives.Conditions
{ {
get get
{ {
var targetName = Target?.CharacterName ?? "Unknown"; var mind = TargetMind;
var jobName = Target?.CurrentJob?.Name ?? "Unknown"; var targetName = mind?.CharacterName ?? "Unknown";
var jobName = Jobs.MindTryGetJobName(TargetMindId);
if (Target == null) if (TargetMind == null)
return Loc.GetString("objective-condition-kill-person-title", ("targetName", targetName), ("job", jobName)); return Loc.GetString("objective-condition-kill-person-title", ("targetName", targetName), ("job", jobName));
return Loc.GetString("objective-condition-kill-person-title", ("targetName", targetName), ("job", jobName)); return Loc.GetString("objective-condition-kill-person-title", ("targetName", targetName), ("job", jobName));
@@ -42,12 +48,12 @@ namespace Content.Server.Objectives.Conditions
{ {
get get
{ {
if (Target == null || Target.OwnedEntity == null) if (TargetMindId == null || TargetMind?.OwnedEntity == null)
return 1f; return 1f;
var entMan = IoCManager.Resolve<EntityManager>(); var entMan = IoCManager.Resolve<EntityManager>();
var mindSystem = entMan.System<MindSystem>(); var mindSystem = entMan.System<MindSystem>();
if (mindSystem.IsCharacterDeadIc(Target)) if (mindSystem.IsCharacterDeadIc(TargetMind))
return 1f; return 1f;
if (RequireDead) if (RequireDead)
@@ -60,7 +66,7 @@ namespace Content.Server.Objectives.Conditions
// target is escaping so you fail // target is escaping so you fail
var emergencyShuttle = entMan.System<EmergencyShuttleSystem>(); var emergencyShuttle = entMan.System<EmergencyShuttleSystem>();
if (emergencyShuttle.IsTargetEscaping(Target.OwnedEntity.Value)) if (emergencyShuttle.IsTargetEscaping(TargetMind.OwnedEntity.Value))
return 0f; return 0f;
// evac has left without the target, greentext since the target is afk in space with a full oxygen tank and coordinates off. // evac has left without the target, greentext since the target is afk in space with a full oxygen tank and coordinates off.
@@ -76,7 +82,7 @@ namespace Content.Server.Objectives.Conditions
public bool Equals(IObjectiveCondition? other) public bool Equals(IObjectiveCondition? other)
{ {
return other is KillPersonCondition kpc && Equals(Target, kpc.Target); return other is KillPersonCondition kpc && Equals(TargetMindId, kpc.TargetMindId);
} }
public override bool Equals(object? obj) public override bool Equals(object? obj)
@@ -89,7 +95,7 @@ namespace Content.Server.Objectives.Conditions
public override int GetHashCode() public override int GetHashCode()
{ {
return Target?.GetHashCode() ?? 0; return TargetMindId?.GetHashCode() ?? 0;
} }
} }
} }

View File

@@ -1,46 +1,43 @@
using System.Linq;
using Content.Server.Mind;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Server.Objectives.Interfaces; using Content.Server.Objectives.Interfaces;
using Content.Server.Roles;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Robust.Shared.Random; using Robust.Shared.Random;
using System.Linq;
namespace Content.Server.Objectives.Conditions; namespace Content.Server.Objectives.Conditions;
[DataDefinition] [DataDefinition]
public sealed partial class KillRandomHeadCondition : KillPersonCondition public sealed partial class KillRandomHeadCondition : KillPersonCondition
{ {
public override IObjectiveCondition GetAssigned(Mind.Mind mind) // TODO refactor all of this to be ecs
public override IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind)
{ {
RequireDead = true; RequireDead = true;
var allHumans = EntityManager.EntityQuery<MindContainerComponent>(true).Where(mc => var allHumans = EntityManager.EntityQuery<MindContainerComponent>(true).Where(mc =>
{ {
var entity = mc.Mind?.OwnedEntity; var entity = EntityManager.GetComponentOrNull<MindComponent>(mc.Mind)?.OwnedEntity;
if (entity == default) if (entity == default)
return false; return false;
return EntityManager.TryGetComponent(entity, out MobStateComponent? mobState) && return EntityManager.TryGetComponent(entity, out MobStateComponent? mobState) &&
MobStateSystem.IsAlive(entity.Value, mobState) && MobStateSystem.IsAlive(entity.Value, mobState) &&
mc.Mind != mind; mc.Mind != mindId;
}).Select(mc => mc.Mind).ToList(); }).Select(mc => mc.Mind).ToList();
if (allHumans.Count == 0) if (allHumans.Count == 0)
return new DieCondition(); // I guess I'll die return new DieCondition(); // I guess I'll die
var allHeads = allHumans.Where(mind => mind?.AllRoles.Any(role => { var allHeads = allHumans
if (role is not Job job) .Where(mind => Jobs.MindTryGetJob(mind, out _, out var prototype) && prototype.RequireAdminNotify)
return false; .ToList();
// basically a command department check, pretty sussy but whatever
return job.Prototype.RequireAdminNotify;
}) ?? false).ToList();
if (allHeads.Count == 0) if (allHeads.Count == 0)
allHeads = allHumans; // fallback to non-head target allHeads = allHumans; // fallback to non-head target
return new KillRandomHeadCondition {Target = IoCManager.Resolve<IRobustRandom>().Pick(allHeads)}; return new KillRandomHeadCondition { TargetMindId = IoCManager.Resolve<IRobustRandom>().Pick(allHeads) };
} }
public string Description => Loc.GetString("objective-condition-kill-head-description"); public string Description => Loc.GetString("objective-condition-kill-head-description");

View File

@@ -1,3 +1,4 @@
using Content.Server.Mind;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Server.Objectives.Interfaces; using Content.Server.Objectives.Interfaces;
using Content.Shared.Humanoid; using Content.Shared.Humanoid;
@@ -9,27 +10,27 @@ namespace Content.Server.Objectives.Conditions;
[DataDefinition] [DataDefinition]
public sealed partial class KillRandomPersonCondition : KillPersonCondition public sealed partial class KillRandomPersonCondition : KillPersonCondition
{ {
public override IObjectiveCondition GetAssigned(Mind.Mind mind) public override IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind)
{ {
var allHumans = new List<Mind.Mind>(); var allHumans = new List<EntityUid>();
var query = EntityManager.EntityQuery<MindContainerComponent, HumanoidAppearanceComponent>(true); var query = EntityManager.EntityQuery<MindContainerComponent, HumanoidAppearanceComponent>(true);
foreach (var (mc, _) in query) foreach (var (mc, _) in query)
{ {
var entity = mc.Mind?.OwnedEntity; var entity = EntityManager.GetComponentOrNull<MindComponent>(mc.Mind)?.OwnedEntity;
if (entity == default) if (entity == default)
continue; continue;
if (EntityManager.TryGetComponent(entity, out MobStateComponent? mobState) && if (EntityManager.TryGetComponent(entity, out MobStateComponent? mobState) &&
MobStateSystem.IsAlive(entity.Value, mobState) && MobStateSystem.IsAlive(entity.Value, mobState) &&
mc.Mind != mind && mc.Mind != null) mc.Mind != mindId && mc.Mind != null)
{ {
allHumans.Add(mc.Mind); allHumans.Add(mc.Mind.Value);
} }
} }
if (allHumans.Count == 0) if (allHumans.Count == 0)
return new DieCondition(); // I guess I'll die return new DieCondition(); // I guess I'll die
return new KillRandomPersonCondition {Target = IoCManager.Resolve<IRobustRandom>().Pick(allHumans)}; return new KillRandomPersonCondition {TargetMindId = IoCManager.Resolve<IRobustRandom>().Pick(allHumans)};
} }
} }

View File

@@ -1,25 +1,27 @@
using System.Linq; using System.Linq;
using Content.Server.Objectives.Interfaces;
using Robust.Shared.Random;
using Robust.Shared.Utility;
using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Objectives.Interfaces;
using Content.Server.Roles.Jobs;
using Robust.Shared.Random;
using Robust.Shared.Utility;
namespace Content.Server.Objectives.Conditions namespace Content.Server.Objectives.Conditions
{ {
[DataDefinition] [DataDefinition]
public sealed partial class RandomTraitorAliveCondition : IObjectiveCondition public sealed partial class RandomTraitorAliveCondition : IObjectiveCondition
{ {
private Mind.Mind? _target; private EntityUid? _target;
public IObjectiveCondition GetAssigned(Mind.Mind mind) public IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind)
{ {
var entityMgr = IoCManager.Resolve<IEntityManager>(); var entityMgr = IoCManager.Resolve<IEntityManager>();
var traitors = entityMgr.EntitySysManager.GetEntitySystem<TraitorRuleSystem>().GetOtherTraitorsAliveAndConnected(mind).ToList(); var traitors = entityMgr.System<TraitorRuleSystem>().GetOtherTraitorMindsAliveAndConnected(mind).ToList();
if (traitors.Count == 0) if (traitors.Count == 0)
return new EscapeShuttleCondition(); //You were made a traitor by admins, and are the first/only. return new EscapeShuttleCondition(); //You were made a traitor by admins, and are the first/only.
return new RandomTraitorAliveCondition { _target = IoCManager.Resolve<IRobustRandom>().Pick(traitors).Mind }; return new RandomTraitorAliveCondition { _target = IoCManager.Resolve<IRobustRandom>().Pick(traitors).Id };
} }
public string Title public string Title
@@ -27,13 +29,19 @@ namespace Content.Server.Objectives.Conditions
get get
{ {
var targetName = string.Empty; var targetName = string.Empty;
var jobName = _target?.CurrentJob?.Name ?? "Unknown"; var ents = IoCManager.Resolve<IEntityManager>();
var jobs = ents.System<JobSystem>();
var jobName = jobs.MindTryGetJobName(_target);
if (_target == null) if (_target == null)
return Loc.GetString("objective-condition-other-traitor-alive-title", ("targetName", targetName), ("job", jobName)); return Loc.GetString("objective-condition-other-traitor-alive-title", ("targetName", targetName), ("job", jobName));
if (_target.OwnedEntity is {Valid: true} owned) var minds = ents.System<MindSystem>();
targetName = IoCManager.Resolve<IEntityManager>().GetComponent<MetaDataComponent>(owned).EntityName; if (minds.TryGetMind(_target.Value, out _, out var mind) &&
mind.OwnedEntity is { Valid: true } owned)
{
targetName = ents.GetComponent<MetaDataComponent>(owned).EntityName;
}
return Loc.GetString("objective-condition-other-traitor-alive-title", ("targetName", targetName), ("job", jobName)); return Loc.GetString("objective-condition-other-traitor-alive-title", ("targetName", targetName), ("job", jobName));
} }
@@ -49,7 +57,11 @@ namespace Content.Server.Objectives.Conditions
{ {
var entityManager = IoCManager.Resolve<EntityManager>(); var entityManager = IoCManager.Resolve<EntityManager>();
var mindSystem = entityManager.System<MindSystem>(); var mindSystem = entityManager.System<MindSystem>();
return _target == null || !mindSystem.IsCharacterDeadIc(_target) ? 1f : 0f; return _target == null ||
!mindSystem.TryGetMind(_target.Value, out _, out var mind) ||
!mindSystem.IsCharacterDeadIc(mind)
? 1f
: 0f;
} }
} }

View File

@@ -1,24 +1,26 @@
using System.Linq; using System.Linq;
using Content.Server.GameTicking.Rules;
using Content.Server.Mind;
using Content.Server.Objectives.Interfaces; using Content.Server.Objectives.Interfaces;
using Content.Server.Roles.Jobs;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Content.Server.GameTicking.Rules;
using Content.Server.Roles;
namespace Content.Server.Objectives.Conditions namespace Content.Server.Objectives.Conditions
{ {
[DataDefinition] [DataDefinition]
public sealed partial class RandomTraitorProgressCondition : IObjectiveCondition public sealed partial class RandomTraitorProgressCondition : IObjectiveCondition
{ {
private Mind.Mind? _target; // TODO ecs all of this
private EntityUid? _target;
public IObjectiveCondition GetAssigned(Mind.Mind mind) public IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind)
{ {
//todo shit of a fuck //todo shit of a fuck
var entityMgr = IoCManager.Resolve<IEntityManager>(); var entityMgr = IoCManager.Resolve<IEntityManager>();
var traitors = entityMgr.EntitySysManager.GetEntitySystem<TraitorRuleSystem>().GetOtherTraitorsAliveAndConnected(mind).ToList(); var traitors = entityMgr.System<TraitorRuleSystem>().GetOtherTraitorMindsAliveAndConnected(mind).ToList();
List<TraitorRole> removeList = new(); List<EntityUid> removeList = new();
foreach (var traitor in traitors) foreach (var traitor in traitors)
{ {
@@ -28,7 +30,7 @@ namespace Content.Server.Objectives.Conditions
{ {
if (condition is RandomTraitorProgressCondition) if (condition is RandomTraitorProgressCondition)
{ {
removeList.Add(traitor); removeList.Add(traitor.Id);
} }
} }
} }
@@ -36,11 +38,11 @@ namespace Content.Server.Objectives.Conditions
foreach (var traitor in removeList) foreach (var traitor in removeList)
{ {
traitors.Remove(traitor); traitors.RemoveAll(t => t.Id == traitor);
} }
if (traitors.Count == 0) return new EscapeShuttleCondition{}; //You were made a traitor by admins, and are the first/only. if (traitors.Count == 0) return new EscapeShuttleCondition{}; //You were made a traitor by admins, and are the first/only.
return new RandomTraitorProgressCondition { _target = IoCManager.Resolve<IRobustRandom>().Pick(traitors).Mind }; return new RandomTraitorProgressCondition { _target = IoCManager.Resolve<IRobustRandom>().Pick(traitors).Id };
} }
public string Title public string Title
@@ -48,13 +50,18 @@ namespace Content.Server.Objectives.Conditions
get get
{ {
var targetName = string.Empty; var targetName = string.Empty;
var jobName = _target?.CurrentJob?.Name ?? "Unknown"; var entities = IoCManager.Resolve<IEntityManager>();
var jobs = entities.System<JobSystem>();
var jobName = jobs.MindTryGetJobName(_target);
if (_target == null) if (_target == null)
return Loc.GetString("objective-condition-other-traitor-progress-title", ("targetName", targetName), ("job", jobName)); return Loc.GetString("objective-condition-other-traitor-progress-title", ("targetName", targetName), ("job", jobName));
if (_target.OwnedEntity is {Valid: true} owned) if (entities.TryGetComponent(_target, out MindComponent? mind) &&
targetName = IoCManager.Resolve<IEntityManager>().GetComponent<MetaDataComponent>(owned).EntityName; mind.OwnedEntity is {Valid: true} owned)
{
targetName = entities.GetComponent<MetaDataComponent>(owned).EntityName;
}
return Loc.GetString("objective-condition-other-traitor-progress-title", ("targetName", targetName), ("job", jobName)); return Loc.GetString("objective-condition-other-traitor-progress-title", ("targetName", targetName), ("job", jobName));
} }
@@ -66,9 +73,8 @@ namespace Content.Server.Objectives.Conditions
public float Progress public float Progress
{ {
get { get
var entMan = IoCManager.Resolve<IEntityManager>(); {
float total = 0f; // how much progress they have float total = 0f; // how much progress they have
float max = 0f; // how much progress is needed for 100% float max = 0f; // how much progress is needed for 100%
@@ -78,7 +84,10 @@ namespace Content.Server.Objectives.Conditions
return 1f; return 1f;
} }
foreach (var objective in _target.AllObjectives) var entities = IoCManager.Resolve<IEntityManager>();
if (entities.TryGetComponent(_target, out MindComponent? mind))
{
foreach (var objective in mind.AllObjectives)
{ {
foreach (var condition in objective.Conditions) foreach (var condition in objective.Conditions)
{ {
@@ -86,6 +95,7 @@ namespace Content.Server.Objectives.Conditions
total += condition.Progress; total += condition.Progress;
} }
} }
}
if (max == 0f) if (max == 0f)
{ {

View File

@@ -1,3 +1,4 @@
using Content.Server.Mind;
using Content.Server.Objectives.Interfaces; using Content.Server.Objectives.Interfaces;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Containers; using Robust.Shared.Containers;
@@ -12,7 +13,7 @@ namespace Content.Server.Objectives.Conditions
[DataDefinition] [DataDefinition]
public sealed partial class StealCondition : IObjectiveCondition, ISerializationHooks public sealed partial class StealCondition : IObjectiveCondition, ISerializationHooks
{ {
private Mind.Mind? _mind; private EntityUid? _mind;
[DataField("prototype")] private string _prototypeId = string.Empty; [DataField("prototype")] private string _prototypeId = string.Empty;
/// <summary> /// <summary>
@@ -21,11 +22,11 @@ namespace Content.Server.Objectives.Conditions
/// </summary> /// </summary>
[DataField("owner")] private string? _owner = null; [DataField("owner")] private string? _owner = null;
public IObjectiveCondition GetAssigned(Mind.Mind mind) public IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind)
{ {
return new StealCondition return new StealCondition
{ {
_mind = mind, _mind = mindId,
_prototypeId = _prototypeId, _prototypeId = _prototypeId,
_owner = _owner _owner = _owner
}; };
@@ -49,7 +50,6 @@ namespace Content.Server.Objectives.Conditions
{ {
get get
{ {
var uid = _mind?.OwnedEntity;
var entMan = IoCManager.Resolve<IEntityManager>(); var entMan = IoCManager.Resolve<IEntityManager>();
// TODO make this a container system function // TODO make this a container system function
@@ -59,13 +59,16 @@ namespace Content.Server.Objectives.Conditions
var managerQuery = entMan.GetEntityQuery<ContainerManagerComponent>(); var managerQuery = entMan.GetEntityQuery<ContainerManagerComponent>();
var stack = new Stack<ContainerManagerComponent>(); var stack = new Stack<ContainerManagerComponent>();
if (!metaQuery.TryGetComponent(_mind?.OwnedEntity, out var meta)) if (!entMan.TryGetComponent(_mind, out MindComponent? mind))
return 0;
if (!metaQuery.TryGetComponent(mind.OwnedEntity, out var meta))
return 0; return 0;
if (meta.EntityPrototype?.ID == _prototypeId) if (meta.EntityPrototype?.ID == _prototypeId)
return 1; return 1;
if (!managerQuery.TryGetComponent(uid, out var currentManager)) if (!managerQuery.TryGetComponent(mind.OwnedEntity, out var currentManager))
return 0; return 0;
do do

View File

@@ -1,15 +1,18 @@
using Robust.Shared.Utility; using Content.Server.Mind;
using Robust.Shared.Utility;
namespace Content.Server.Objectives.Interfaces namespace Content.Server.Objectives.Interfaces
{ {
// TODO refactor all of this to be ecs
public interface IObjectiveCondition : IEquatable<IObjectiveCondition> public interface IObjectiveCondition : IEquatable<IObjectiveCondition>
{ {
/// <summary> /// <summary>
/// Returns a copy of the IObjectiveCondition which is assigned to the mind. /// Returns a copy of the IObjectiveCondition which is assigned to the mind.
/// </summary> /// </summary>
/// <param name="mindId">Mind id to assign to.</param>
/// <param name="mind">Mind to assign to.</param> /// <param name="mind">Mind to assign to.</param>
/// <returns>The new IObjectiveCondition.</returns> /// <returns>The new IObjectiveCondition.</returns>
IObjectiveCondition GetAssigned(Mind.Mind mind); IObjectiveCondition GetAssigned(EntityUid mindId, MindComponent mind);
/// <summary> /// <summary>
/// Returns the title of the condition. /// Returns the title of the condition.

View File

@@ -1,11 +1,14 @@
namespace Content.Server.Objectives.Interfaces using Content.Server.Mind;
namespace Content.Server.Objectives.Interfaces
{ {
// TODO refactor all of this to be ecs
public interface IObjectiveRequirement public interface IObjectiveRequirement
{ {
/// <summary> /// <summary>
/// Checks whether or not the entity & its surroundings are valid to be given the objective. /// Checks whether or not the entity & its surroundings are valid to be given the objective.
/// </summary> /// </summary>
/// <returns>Returns true if objective can be given.</returns> /// <returns>Returns true if objective can be given.</returns>
bool CanBeAssigned(Mind.Mind mind); bool CanBeAssigned(EntityUid mindId, MindComponent mind);
} }
} }

View File

@@ -1,10 +0,0 @@
namespace Content.Server.Objectives.Interfaces
{
public interface IObjectivesManager
{
/// <summary>
/// Returns a randomly picked objective the provided mind is valid for.
/// </summary>
ObjectivePrototype? GetRandomObjective(Mind.Mind mind, string objectiveGroupProto);
}
}

View File

@@ -1,24 +1,28 @@
using Content.Server.Objectives.Interfaces; using Content.Server.Mind;
using Content.Server.Objectives.Interfaces;
namespace Content.Server.Objectives namespace Content.Server.Objectives
{ {
public sealed class Objective : IEquatable<Objective> public sealed class Objective : IEquatable<Objective>
{ {
[ViewVariables] [ViewVariables]
public readonly Mind.Mind Mind; public readonly EntityUid MindId;
[ViewVariables]
public readonly MindComponent Mind;
[ViewVariables] [ViewVariables]
public readonly ObjectivePrototype Prototype; public readonly ObjectivePrototype Prototype;
private readonly List<IObjectiveCondition> _conditions = new(); private readonly List<IObjectiveCondition> _conditions = new();
[ViewVariables] [ViewVariables]
public IReadOnlyList<IObjectiveCondition> Conditions => _conditions; public IReadOnlyList<IObjectiveCondition> Conditions => _conditions;
public Objective(ObjectivePrototype prototype, Mind.Mind mind) public Objective(ObjectivePrototype prototype, EntityUid mindId, MindComponent mind)
{ {
Prototype = prototype; Prototype = prototype;
MindId = mindId;
Mind = mind; Mind = mind;
foreach (var condition in prototype.Conditions) foreach (var condition in prototype.Conditions)
{ {
_conditions.Add(condition.GetAssigned(mind)); _conditions.Add(condition.GetAssigned(mindId, mind));
} }
} }

View File

@@ -1,4 +1,5 @@
using System.Linq; using System.Linq;
using Content.Server.Mind;
using Content.Server.Objectives.Interfaces; using Content.Server.Objectives.Interfaces;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -34,27 +35,29 @@ namespace Content.Server.Objectives
[DataField("difficultyOverride")] [DataField("difficultyOverride")]
private float? _difficultyOverride = null; private float? _difficultyOverride = null;
public bool CanBeAssigned(Mind.Mind mind) public bool CanBeAssigned(EntityUid mindId, MindComponent mind)
{ {
foreach (var requirement in _requirements) foreach (var requirement in _requirements)
{ {
if (!requirement.CanBeAssigned(mind)) return false; if (!requirement.CanBeAssigned(mindId, mind))
return false;
} }
if (!CanBeDuplicateAssignment) if (!CanBeDuplicateAssignment)
{ {
foreach (var objective in mind.AllObjectives) foreach (var objective in mind.AllObjectives)
{ {
if (objective.Prototype.ID == ID) return false; if (objective.Prototype.ID == ID)
return false;
} }
} }
return true; return true;
} }
public Objective GetObjective(Mind.Mind mind) public Objective GetObjective(EntityUid mindId, MindComponent mind)
{ {
return new(this, mind); return new Objective(this, mindId, mind);
} }
} }
} }

View File

@@ -1,44 +0,0 @@
using Content.Server.Objectives.Interfaces;
using Content.Shared.Random.Helpers;
using Content.Shared.Random;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.Objectives
{
public sealed class ObjectivesManager : IObjectivesManager
{
[Dependency] private IPrototypeManager _prototypeManager = default!;
[Dependency] private IRobustRandom _random = default!;
public ObjectivePrototype? GetRandomObjective(Mind.Mind mind, string objectiveGroupProto)
{
if (!_prototypeManager.TryIndex<WeightedRandomPrototype>(objectiveGroupProto, out var groups))
{
Logger.Error("Tried to get a random objective, but can't index WeightedRandomPrototype " + objectiveGroupProto);
return null;
}
// yeah the old 'preventing infinite loops' thing wasn't super elegant either and it mislead people on what exactly it did
var tries = 0;
while (tries < 20)
{
var groupName = groups.Pick(_random);
if (!_prototypeManager.TryIndex<WeightedRandomPrototype>(groupName, out var group))
{
Logger.Error("Couldn't index objective group prototype" + groupName);
return null;
}
if (_prototypeManager.TryIndex<ObjectivePrototype>(group.Pick(_random), out var objective)
&& objective.CanBeAssigned(mind))
return objective;
else
tries++;
}
return null;
}
}
}

View File

@@ -0,0 +1,44 @@
using Content.Server.Mind;
using Content.Shared.Random;
using Content.Shared.Random.Helpers;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server.Objectives;
public sealed class ObjectivesSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
public ObjectivePrototype? GetRandomObjective(EntityUid mindId, MindComponent mind, string objectiveGroupProto)
{
if (!_prototypeManager.TryIndex<WeightedRandomPrototype>(objectiveGroupProto, out var groups))
{
Log.Error("Tried to get a random objective, but can't index WeightedRandomPrototype " + objectiveGroupProto);
return null;
}
// TODO replace whatever the fuck this is with a proper objective selection system
// yeah the old 'preventing infinite loops' thing wasn't super elegant either and it mislead people on what exactly it did
var tries = 0;
while (tries < 20)
{
var groupName = groups.Pick(_random);
if (!_prototypeManager.TryIndex<WeightedRandomPrototype>(groupName, out var group))
{
Log.Error("Couldn't index objective group prototype" + groupName);
return null;
}
if (_prototypeManager.TryIndex<ObjectivePrototype>(group.Pick(_random), out var objective)
&& objective.CanBeAssigned(mindId, mind))
return objective;
else
tries++;
}
return null;
}
}

View File

@@ -1,4 +1,5 @@
using Content.Server.Objectives.Interfaces; using Content.Server.Mind;
using Content.Server.Objectives.Interfaces;
namespace Content.Server.Objectives.Requirements namespace Content.Server.Objectives.Requirements
{ {
@@ -8,7 +9,7 @@ namespace Content.Server.Objectives.Requirements
[DataField("conditions")] [DataField("conditions")]
private List<string> _incompatibleConditions = new(); private List<string> _incompatibleConditions = new();
public bool CanBeAssigned(Mind.Mind mind) public bool CanBeAssigned(EntityUid mindId, MindComponent mind)
{ {
foreach (var objective in mind.AllObjectives) foreach (var objective in mind.AllObjectives)
{ {

View File

@@ -1,4 +1,5 @@
using Content.Server.Objectives.Interfaces; using Content.Server.Mind;
using Content.Server.Objectives.Interfaces;
namespace Content.Server.Objectives.Requirements namespace Content.Server.Objectives.Requirements
{ {
@@ -8,7 +9,7 @@ namespace Content.Server.Objectives.Requirements
[DataField("objectives")] [DataField("objectives")]
private List<string> _incompatibleObjectives = new(); private List<string> _incompatibleObjectives = new();
public bool CanBeAssigned(Mind.Mind mind) public bool CanBeAssigned(EntityUid mindId, MindComponent mind)
{ {
foreach (var objective in mind.AllObjectives) foreach (var objective in mind.AllObjectives)
{ {

View File

@@ -1,5 +1,6 @@
using Content.Server.Objectives.Interfaces;
using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules;
using Content.Server.Mind;
using Content.Server.Objectives.Interfaces;
namespace Content.Server.Objectives.Requirements namespace Content.Server.Objectives.Requirements
{ {
@@ -9,9 +10,9 @@ namespace Content.Server.Objectives.Requirements
[DataField("traitors")] [DataField("traitors")]
private int _requiredTraitors = 2; private int _requiredTraitors = 2;
public bool CanBeAssigned(Mind.Mind mind) public bool CanBeAssigned(EntityUid mindId, MindComponent mind)
{ {
return EntitySystem.Get<TraitorRuleSystem>().GetOtherTraitorsAliveAndConnected(mind).Count >= _requiredTraitors; return EntitySystem.Get<TraitorRuleSystem>().GetOtherTraitorMindsAliveAndConnected(mind).Count >= _requiredTraitors;
} }
} }
} }

View File

@@ -1,10 +1,11 @@
using Content.Shared.Roles; using Content.Server.Mind;
using Content.Server.Objectives.Interfaces; using Content.Server.Objectives.Interfaces;
using Content.Server.Roles.Jobs;
using Content.Shared.Roles;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Objectives.Requirements namespace Content.Server.Objectives.Requirements
{ {
[DataDefinition] [DataDefinition]
public sealed partial class NotRoleRequirement : IObjectiveRequirement public sealed partial class NotRoleRequirement : IObjectiveRequirement
{ {
@@ -14,12 +15,14 @@ namespace Content.Server.Objectives.Requirements
/// <summary> /// <summary>
/// This requirement is met if the traitor is NOT the roleId, and fails if they are. /// This requirement is met if the traitor is NOT the roleId, and fails if they are.
/// </summary> /// </summary>
public bool CanBeAssigned(Mind.Mind mind) public bool CanBeAssigned(EntityUid mindId, MindComponent mind)
{ {
if (mind.CurrentJob == null) // no job no problems // TODO ECS this shit i keep seeing shitcode everywhere
var entities = IoCManager.Resolve<IEntityManager>();
if (!entities.TryGetComponent(mindId, out JobComponent? job))
return true; return true;
return (mind.CurrentJob.Prototype.ID != _roleId); return job.PrototypeId != _roleId;
} }
} }
} }

View File

@@ -1,7 +1,7 @@
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Objectives.Interfaces; using Content.Server.Objectives.Interfaces;
using Content.Server.Roles;
using JetBrains.Annotations; using JetBrains.Annotations;
using TraitorRole = Content.Server.Roles.TraitorRole;
namespace Content.Server.Objectives.Requirements namespace Content.Server.Objectives.Requirements
{ {
@@ -9,11 +9,10 @@ namespace Content.Server.Objectives.Requirements
[DataDefinition] [DataDefinition]
public sealed partial class TraitorRequirement : IObjectiveRequirement public sealed partial class TraitorRequirement : IObjectiveRequirement
{ {
public bool CanBeAssigned(Mind.Mind mind) public bool CanBeAssigned(EntityUid mindId, MindComponent mind)
{ {
var entityManager = IoCManager.Resolve<IEntityManager>(); var roleSystem = IoCManager.Resolve<IEntityManager>().System<RoleSystem>();
var mindSystem = entityManager.System<MindSystem>(); return roleSystem.MindHasRole<TraitorRoleComponent>(mindId);
return mindSystem.HasRole<TraitorRole>(mind);
} }
} }
} }

View File

@@ -77,14 +77,17 @@ public sealed class PlayTimeTrackingSystem : EntitySystem
return state.CurrentState is MobState.Alive or MobState.Critical; return state.CurrentState is MobState.Alive or MobState.Critical;
} }
public IEnumerable<string> GetTimedRoles(Mind.Mind mind) public IEnumerable<string> GetTimedRoles(EntityUid mindId)
{ {
foreach (var role in mind.AllRoles) var ev = new MindGetAllRolesEvent(new List<RoleInfo>());
RaiseLocalEvent(ref ev);
foreach (var role in ev.Roles)
{ {
if (role is not IRoleTimer timer) if (string.IsNullOrWhiteSpace(role.PlayTimeTrackerId))
continue; continue;
yield return _prototypes.Index<PlayTimeTrackerPrototype>(timer.Timer).ID; yield return _prototypes.Index<PlayTimeTrackerPrototype>(role.PlayTimeTrackerId).ID;
} }
} }
@@ -95,7 +98,7 @@ public sealed class PlayTimeTrackingSystem : EntitySystem
if (contentData?.Mind == null) if (contentData?.Mind == null)
return Enumerable.Empty<string>(); return Enumerable.Empty<string>();
return GetTimedRoles(contentData.Mind); return GetTimedRoles(contentData.Mind.Value);
} }
private void OnRoleRemove(RoleRemovedEvent ev) private void OnRoleRemove(RoleRemovedEvent ev)

View File

@@ -29,7 +29,7 @@ namespace Content.Server.Players
/// DO NOT DIRECTLY SET THIS UNLESS YOU KNOW WHAT YOU'RE DOING. /// DO NOT DIRECTLY SET THIS UNLESS YOU KNOW WHAT YOU'RE DOING.
/// </summary> /// </summary>
[ViewVariables, Access(typeof(MindSystem), typeof(GameTicker))] [ViewVariables, Access(typeof(MindSystem), typeof(GameTicker))]
public Mind.Mind? Mind { get; set; } public EntityUid? Mind { get; set; }
/// <summary> /// <summary>
/// If true, the player is an admin and they explicitly de-adminned mid-game, /// If true, the player is an admin and they explicitly de-adminned mid-game,
@@ -65,7 +65,7 @@ namespace Content.Server.Players
/// <summary> /// <summary>
/// Gets the mind that is associated with this player. /// Gets the mind that is associated with this player.
/// </summary> /// </summary>
public static Mind.Mind? GetMind(this IPlayerSession session) public static EntityUid? GetMind(this IPlayerSession session)
{ {
return session.Data.ContentData()?.Mind; return session.Data.ContentData()?.Mind;
} }

View File

@@ -1,6 +1,6 @@
using System.Linq; using System.Linq;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Players; using Content.Server.Mind;
using Content.Server.Pointing.Components; using Content.Server.Pointing.Components;
using Content.Server.Visible; using Content.Server.Visible;
using Content.Shared.Bed.Sleep; using Content.Shared.Bed.Sleep;
@@ -38,6 +38,7 @@ namespace Content.Server.Pointing.EntitySystems
[Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly VisibilitySystem _visibilitySystem = default!; [Dependency] private readonly VisibilitySystem _visibilitySystem = default!;
[Dependency] private readonly MindSystem _minds = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!;
private static readonly TimeSpan PointDelay = TimeSpan.FromSeconds(0.5f); private static readonly TimeSpan PointDelay = TimeSpan.FromSeconds(0.5f);
@@ -169,7 +170,8 @@ namespace Content.Server.Pointing.EntitySystems
// Get players that are in range and whose visibility layer matches the arrow's. // Get players that are in range and whose visibility layer matches the arrow's.
bool ViewerPredicate(IPlayerSession playerSession) bool ViewerPredicate(IPlayerSession playerSession)
{ {
if (playerSession.ContentData()?.Mind?.CurrentEntity is not {Valid: true} ent || if (!_minds.TryGetMind(playerSession, out _, out var mind) ||
mind.CurrentEntity is not { Valid: true } ent ||
!TryComp(ent, out EyeComponent? eyeComp) || !TryComp(ent, out EyeComponent? eyeComp) ||
(eyeComp.VisibilityMask & layer) == 0) (eyeComp.VisibilityMask & layer) == 0)
return false; return false;

View File

@@ -3,7 +3,6 @@ using Content.Server.Humanoid;
using Content.Server.Inventory; using Content.Server.Inventory;
using Content.Server.Mind; using Content.Server.Mind;
using Content.Server.Mind.Commands; using Content.Server.Mind.Commands;
using Content.Server.Mind.Components;
using Content.Server.Nutrition; using Content.Server.Nutrition;
using Content.Server.Polymorph.Components; using Content.Server.Polymorph.Components;
using Content.Shared.Actions; using Content.Shared.Actions;
@@ -229,8 +228,8 @@ namespace Content.Server.Polymorph.Systems
_humanoid.CloneAppearance(uid, child); _humanoid.CloneAppearance(uid, child);
} }
if (_mindSystem.TryGetMind(uid, out var mind)) if (_mindSystem.TryGetMind(uid, out var mindId, out var mind))
_mindSystem.TransferTo(mind, child); _mindSystem.TransferTo(mindId, child, mind: mind);
//Ensures a map to banish the entity to //Ensures a map to banish the entity to
EnsurePausesdMap(); EnsurePausesdMap();
@@ -303,8 +302,8 @@ namespace Content.Server.Polymorph.Systems
} }
} }
if (_mindSystem.TryGetMind(uid, out var mind)) if (_mindSystem.TryGetMind(uid, out var mindId, out var mind))
_mindSystem.TransferTo(mind, parent); _mindSystem.TransferTo(mindId, parent, mind: mind);
// if an item polymorph was picked up, put it back down after reverting // if an item polymorph was picked up, put it back down after reverting
Transform(parent).AttachToGridOrMap(); Transform(parent).AttachToGridOrMap();

View File

@@ -1,12 +1,11 @@
using Content.Server.Administration; using Content.Server.Administration;
using Content.Server.Players; using Content.Server.Players;
using Content.Server.Roles.Jobs;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Roles; using Content.Shared.Roles;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using System.Linq;
using Content.Server.Mind;
namespace Content.Server.Roles namespace Content.Server.Roles
{ {
@@ -50,15 +49,14 @@ namespace Content.Server.Roles
return; return;
} }
if (mind.AllRoles.Any(r => r.Name == jobPrototype.Name)) var jobs = _entityManager.System<JobSystem>();
if (jobs.MindHasJobWithId(mind, jobPrototype.Name))
{ {
shell.WriteLine("Mind already has that role"); shell.WriteLine("Mind already has that role");
return; return;
} }
var role = new Job(mind, jobPrototype); jobs.MindAddJob(mind.Value, args[1]);
var mindSystem = _entityManager.System<MindSystem>();
mindSystem.AddRole(mind, role);
} }
} }
} }

View File

@@ -1,24 +0,0 @@
using Content.Shared.Roles;
namespace Content.Server.Roles;
public abstract class AntagonistRole : Role
{
public AntagPrototype Prototype { get; }
public override string Name { get; }
public override bool Antagonist { get; }
/// <summary>
/// .ctor
/// </summary>
/// <param name="mind">A mind (player)</param>
/// <param name="antagPrototype">Antagonist prototype</param>
protected AntagonistRole(Mind.Mind mind, AntagPrototype antagPrototype) : base(mind)
{
Prototype = antagPrototype;
Name = Loc.GetString(antagPrototype.Name);
Antagonist = antagPrototype.Antagonist;
}
}

View File

@@ -0,0 +1,10 @@
using Content.Shared.Roles;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Roles;
public abstract partial class AntagonistRoleComponent : Component
{
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
public string? PrototypeId;
}

View File

@@ -1,56 +0,0 @@
using Content.Server.Chat.Managers;
using Content.Server.Chat.Systems;
using Content.Shared.Roles;
using System.Globalization;
using Content.Server.Mind;
namespace Content.Server.Roles
{
public sealed class Job : Role, IRoleTimer
{
[ViewVariables] public string Timer => Prototype.PlayTimeTracker;
[ViewVariables]
public JobPrototype Prototype { get; }
public override string Name { get; }
public override bool Antagonist => false;
[ViewVariables]
public string? StartingGear => Prototype.StartingGear;
[ViewVariables]
public string? JobEntity => Prototype.JobEntity;
[ViewVariables]
public bool CanBeAntag;
public Job(Mind.Mind mind, JobPrototype jobPrototype) : base(mind)
{
Prototype = jobPrototype;
Name = jobPrototype.LocalizedName;
CanBeAntag = jobPrototype.CanBeAntag;
}
public override void Greet()
{
base.Greet();
var entityManager = IoCManager.Resolve<EntityManager>();
var mindSystem = entityManager.System<MindSystem>();
if (mindSystem.TryGetSession(Mind, out var session))
{
var chatMgr = IoCManager.Resolve<IChatManager>();
chatMgr.DispatchServerMessage(session, Loc.GetString("job-greet-introduce-job-name",
("jobName", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(Name))));
if(Prototype.RequireAdminNotify)
chatMgr.DispatchServerMessage(session, Loc.GetString("job-greet-important-disconnect-admin-notify"));
chatMgr.DispatchServerMessage(session, Loc.GetString("job-greet-supervisors-warning", ("jobName", Name), ("supervisors", Loc.GetString(Prototype.Supervisors))));
}
}
}
}

View File

@@ -0,0 +1,14 @@
using Content.Shared.Roles;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Roles.Jobs;
/// <summary>
/// Added to mind entities to hold the data for the player's current job.
/// </summary>
[RegisterComponent]
public sealed partial class JobComponent : Component
{
[DataField("prototype", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<JobPrototype>))]
public string? PrototypeId;
}

View File

@@ -0,0 +1,108 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using Content.Server.Chat.Managers;
using Content.Server.Mind;
using Content.Server.Players;
using Content.Shared.Roles;
using Robust.Server.Player;
using Robust.Shared.Prototypes;
namespace Content.Server.Roles.Jobs;
/// <summary>
/// Handles the job data on mind entities.
/// </summary>
public sealed class JobSystem : EntitySystem
{
[Dependency] private readonly IChatManager _chat = default!;
[Dependency] private readonly MindSystem _mind = default!;
[Dependency] private readonly RoleSystem _roles = default!;
[Dependency] private readonly IPrototypeManager _prototypes = default!;
public override void Initialize()
{
SubscribeLocalEvent<JobComponent, RoleAddedEvent>(MindOnDoGreeting);
}
private void MindOnDoGreeting(EntityUid mindId, JobComponent component, RoleAddedEvent args)
{
if (!_mind.TryGetSession(mindId, out var session))
return;
if (!MindTryGetJob(mindId, out _, out var prototype))
return;
_chat.DispatchServerMessage(session, Loc.GetString("job-greet-introduce-job-name",
("jobName", CultureInfo.CurrentCulture.TextInfo.ToTitleCase(prototype.LocalizedName))));
if (prototype.RequireAdminNotify)
_chat.DispatchServerMessage(session, Loc.GetString("job-greet-important-disconnect-admin-notify"));
_chat.DispatchServerMessage(session, Loc.GetString("job-greet-supervisors-warning",
("jobName", Name),
("supervisors", Loc.GetString(prototype.Supervisors))));
}
public void MindAddJob(EntityUid mindId, string jobPrototypeId)
{
if (MindHasJobWithId(mindId, jobPrototypeId))
return;
_roles.MindAddRole(mindId, new JobComponent { PrototypeId = jobPrototypeId });
}
public bool MindHasJobWithId(EntityUid? mindId, string prototypeId)
{
return CompOrNull<JobComponent>(mindId)?.PrototypeId == prototypeId;
}
public bool MindTryGetJob(
[NotNullWhen(true)] EntityUid? mindId,
[NotNullWhen(true)] out JobComponent? comp,
[NotNullWhen(true)] out JobPrototype? prototype)
{
comp = null;
prototype = null;
return TryComp(mindId, out comp) &&
comp.PrototypeId != null &&
_prototypes.TryIndex(comp.PrototypeId, out prototype);
}
/// <summary>
/// Tries to get the job name for this mind.
/// Returns unknown if not found.
/// </summary>
public bool MindTryGetJobName([NotNullWhen(true)] EntityUid? mindId, out string name)
{
if (MindTryGetJob(mindId, out _, out var prototype))
{
name = prototype.LocalizedName;
return true;
}
name = Loc.GetString("generic-unknown-title");
return false;
}
/// <summary>
/// Tries to get the job name for this mind.
/// Returns unknown if not found.
/// </summary>
public string MindTryGetJobName([NotNullWhen(true)] EntityUid? mindId)
{
MindTryGetJobName(mindId, out var name);
return name;
}
public bool CanBeAntag(IPlayerSession player)
{
if (player.ContentData() is not { Mind: { } mindId })
return false;
if (!MindTryGetJob(mindId, out _, out var prototype))
return true;
return prototype.CanBeAntag;
}
}

View File

@@ -0,0 +1,19 @@
using Content.Shared.Players.PlayTimeTracking;
namespace Content.Server.Roles;
/// <summary>
/// Event raised on a mind entity to get all roles that a player has.
/// </summary>
/// <param name="Roles">The list of roles on the player.</param>
[ByRefEvent]
public readonly record struct MindGetAllRolesEvent(List<RoleInfo> Roles);
/// <summary>
/// Returned by <see cref="MindGetAllRolesEvent"/> to give some information about a player's role.
/// </summary>
/// <param name="Component">Role component associated with the mind entity id.</param>
/// <param name="Name">Name of the role.</param>
/// <param name="Antagonist">Whether or not this role makes this player an antagonist.</param>
/// <param name="PlayTimeTrackerId">The <see cref="PlayTimeTrackerPrototype"/> id associated with the role.</param>
public readonly record struct RoleInfo(Component Component, string Name, bool Antagonist, string? PlayTimeTrackerId = null);

View File

@@ -0,0 +1,9 @@
namespace Content.Server.Roles;
/// <summary>
/// Event raised on a mind entity id to get whether or not the player is considered an antagonist,
/// depending on their roles.
/// </summary>
/// <param name="IsAntagonist">Whether or not the player is an antagonist.</param>
[ByRefEvent]
public record struct MindIsAntagonistEvent(bool IsAntagonist);

View File

@@ -1,8 +0,0 @@
using Content.Shared.Roles;
namespace Content.Server.Roles;
public sealed class NukeopsRole : AntagonistRole
{
public NukeopsRole(Mind.Mind mind, AntagPrototype antagPrototype) : base(mind, antagPrototype) { }
}

View File

@@ -0,0 +1,9 @@
namespace Content.Server.Roles;
/// <summary>
/// Added to mind entities to tag that they are a nuke operative.
/// </summary>
[RegisterComponent]
public sealed partial class NukeopsRoleComponent : AntagonistRoleComponent
{
}

View File

@@ -1,8 +1,7 @@
using Content.Server.Administration; using Content.Server.Administration;
using Content.Server.Mind;
using Content.Server.Players; using Content.Server.Players;
using Content.Server.Roles.Jobs;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Roles;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -44,9 +43,10 @@ namespace Content.Server.Roles
return; return;
} }
var role = new Job(mind, _prototypeManager.Index<JobPrototype>(args[1])); var roles = _entityManager.System<RoleSystem>();
var mindSystem = _entityManager.System<MindSystem>(); var jobs = _entityManager.System<JobSystem>();
mindSystem.RemoveRole(mind, role); if (jobs.MindHasJobWithId(mind, args[1]))
roles.MindRemoveRole<JobComponent>(mind.Value);
} }
} }
} }

View File

@@ -1,44 +0,0 @@
// Hey look,
// Antag Datums.
namespace Content.Server.Roles
{
/// <summary>
/// The Role is a basic building block for,
/// well, IC roles.
/// This can be anything and is not necessarily limited to antagonists.
/// </summary>
public abstract class Role
{
/// <summary>
/// The mind owning this role instance.
/// </summary>
[ViewVariables]
public Mind.Mind Mind { get; }
/// <summary>
/// A friendly name for this role type.
/// </summary>
[ViewVariables]
public abstract string Name { get; }
/// <summary>
/// Whether this role should be considered antagonistic or not.
/// </summary>
[ViewVariables]
public abstract bool Antagonist { get; }
protected Role(Mind.Mind mind)
{
Mind = mind;
}
/// <summary>
/// Called when a mind (player) first gets this role, to greet them.
/// </summary>
public virtual void Greet()
{
}
}
}

View File

@@ -1,7 +1,11 @@
namespace Content.Server.Roles using Content.Server.Mind;
{
public sealed class RoleAddedEvent : RoleEvent namespace Content.Server.Roles;
{
public RoleAddedEvent(Mind.Mind mind, Role role) : base(mind, role) { } /// <summary>
} /// Event raised on player entities to indicate that a role was added to their mind.
} /// </summary>
/// <param name="MindId">The mind id associated with the player.</param>
/// <param name="Mind">The mind component associated with the mind id.</param>
/// <param name="Antagonist">Whether or not the role makes the player an antagonist.</param>
public sealed record RoleAddedEvent(EntityUid MindId, MindComponent Mind, bool Antagonist) : RoleEvent(MindId, Mind, Antagonist);

View File

@@ -1,14 +1,11 @@
namespace Content.Server.Roles using Content.Server.Mind;
{
public abstract class RoleEvent : EntityEventArgs
{
public readonly Mind.Mind Mind;
public readonly Role Role;
public RoleEvent(Mind.Mind mind, Role role) namespace Content.Server.Roles;
{
Mind = mind; /// <summary>
Role = role; /// Base event raised on player entities to indicate that something changed about one of their roles.
} /// </summary>
} /// <param name="MindId">The mind id associated with the player.</param>
} /// <param name="Mind">The mind component associated with the mind id.</param>
/// <param name="Antagonist">Whether or not the role makes the player an antagonist.</param>
public abstract record RoleEvent(EntityUid MindId, MindComponent Mind, bool Antagonist);

View File

@@ -1,7 +1,14 @@
namespace Content.Server.Roles using Content.Server.Mind;
{
public sealed class RoleRemovedEvent : RoleEvent namespace Content.Server.Roles;
{
public RoleRemovedEvent(Mind.Mind mind, Role role) : base(mind, role) { } /// <summary>
} /// Event raised on player entities to indicate that a role was removed from their mind.
} /// </summary>
/// <param name="MindId">The mind id associated with the player.</param>
/// <param name="Mind">The mind component associated with the mind id.</param>
/// <param name="Antagonist">
/// Whether or not the role made the player an antagonist.
/// They may still be one due to one of their other roles.
/// </param>
public sealed record RoleRemovedEvent(EntityUid MindId, MindComponent Mind, bool Antagonist) : RoleEvent(MindId, Mind, Antagonist);

Some files were not shown because too many files have changed in this diff Show More