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

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

View File

@@ -1,5 +1,4 @@
using Content.Server.Roles;
using Content.Shared.Preferences;
using Content.Shared.Preferences;
using Content.Shared.Roles;
using Robust.Server.Player;
using Robust.Shared.Audio;
@@ -10,12 +9,12 @@ namespace Content.Server.GameTicking.Rules.Components;
[RegisterComponent, Access(typeof(TraitorRuleSystem))]
public sealed partial class TraitorRuleComponent : Component
{
public List<TraitorRole> Traitors = new();
public readonly List<EntityUid> TraitorMinds = new();
[DataField("traitorPrototypeId", customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
public string TraitorPrototypeId = "Traitor";
public int TotalTraitors => Traitors.Count;
public int TotalTraitors => TraitorMinds.Count;
public string[] Codewords = new string[3];
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.Events;
using Content.Server.Humanoid;
using Content.Server.Humanoid.Systems;
using Content.Server.Mind;
using Content.Server.Mind.Components;
using Content.Server.NPC.Components;
@@ -20,7 +19,6 @@ using Content.Server.Shuttles.Systems;
using Content.Server.Spawners.Components;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Server.Traitor;
using Content.Shared.Dataset;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes;
@@ -58,8 +56,12 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
[Dependency] private readonly MapLoaderSystem _map = default!;
[Dependency] private readonly ShuttleSystem _shuttle = default!;
[Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly RoleSystem _roles = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
[ValidatePrototypeId<AntagPrototype>]
public const string NukeopsId = "Nukeops";
public override void Initialize()
{
base.Initialize();
@@ -87,10 +89,10 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
continue;
// 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;
var session = mindComponent.Mind?.Session;
var session = mind?.Session;
var name = MetaData(uid).EntityName;
if (session != null)
nukeops.OperativePlayers.Add(name, session);
@@ -592,22 +594,21 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
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;
var mind = mindContainerComponent.Mind;
foreach (var nukeops in EntityQuery<NukeopsRuleComponent>())
{
if (nukeops.OperativeMindPendingData.TryGetValue(uid, out var role) || !nukeops.SpawnOutpost || !nukeops.EndsRound)
{
role ??= nukeops.OperativeRoleProto;
_mindSystem.AddRole(mind, new NukeopsRole(mind, _prototypeManager.Index<AntagPrototype>(role)));
_roles.MindAddRole(mindId, new NukeopsRoleComponent { PrototypeId = role });
nukeops.OperativeMindPendingData.Remove(uid);
}
if (!_mindSystem.TryGetSession(mind, out var playerSession))
if (mind.Session is not { } playerSession)
return;
if (nukeops.OperativePlayers.ContainsValue(playerSession))
return;
@@ -778,7 +779,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
SetupOperativeEntity(mob, spawnDetails.Name, spawnDetails.Gear, profile, component);
var newMind = _mindSystem.CreateMind(session.UserId, spawnDetails.Name);
_mindSystem.SetUserId(newMind, session.UserId);
_mindSystem.AddRole(newMind, new NukeopsRole(newMind, nukeOpsAntag));
_roles.MindAddRole(newMind, new NukeopsRoleComponent { PrototypeId = spawnDetails.Role });
_mindSystem.TransferTo(newMind, mob);
}
@@ -820,13 +821,13 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
}
//For admins forcing someone to nukeOps.
public void MakeLoneNukie(Mind.Mind mind)
public void MakeLoneNukie(EntityUid mindId, MindComponent mind)
{
if (!mind.OwnedEntity.HasValue)
return;
//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);
}
@@ -878,7 +879,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
var query = EntityQuery<NukeOperativeComponent, MindContainerComponent, MetaDataComponent>(true);
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;
component.OperativePlayers.Add(metaData.EntityName, session);
}

View File

@@ -16,14 +16,12 @@ using Content.Shared.Roles;
using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Server.Player;
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
using Robust.Shared.Enums;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Utility;
using Robust.Shared.Enums;
using Robust.Shared.Player;
namespace Content.Server.GameTicking.Rules;
@@ -68,15 +66,14 @@ public sealed class PiratesRuleSystem : GameRuleSystem<PiratesRuleComponent>
}
else
{
List<(double, EntityUid)> mostValuableThefts = new();
var comp1 = pirates;
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.
}
@@ -92,9 +89,9 @@ public sealed class PiratesRuleSystem : GameRuleSystem<PiratesRuleComponent>
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);
}
@@ -119,7 +116,10 @@ public sealed class PiratesRuleSystem : GameRuleSystem<PiratesRuleComponent>
ev.AddLine(Loc.GetString("pirates-list-start"));
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.
public void MakePirate(Mind.Mind mind)
public void MakePirate(EntityUid mindId, MindComponent mind)
{
if (!mind.OwnedEntity.HasValue)
return;

View File

@@ -1,19 +1,19 @@
using System.Linq;
using Content.Server.Chat.Managers;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.NPC.Systems;
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.Players;
using Content.Server.Roles;
using Content.Server.Roles.Jobs;
using Content.Server.Shuttles.Components;
using Content.Server.Traitor.Uplink;
using Content.Shared.CCVar;
using Content.Shared.Dataset;
using Content.Shared.Preferences;
using Content.Shared.Mobs.Systems;
using Content.Shared.PDA;
using Content.Shared.Preferences;
using Content.Shared.Roles;
using Robust.Server.Player;
using Robust.Shared.Configuration;
@@ -28,16 +28,17 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IObjectivesManager _objectivesManager = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IComponentFactory _component = default!;
[Dependency] private readonly NpcFactionSystem _npcFaction = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly UplinkSystem _uplink = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly MindSystem _mindSystem = default!;
private ISawmill _sawmill = default!;
[Dependency] private readonly RoleSystem _roleSystem = default!;
[Dependency] private readonly JobSystem _jobs = default!;
[Dependency] private readonly ObjectivesSystem _objectives = default!;
private int PlayersPerTraitor => _cfg.GetCVar(CCVars.TraitorPlayersPerTraitor);
private int MaxTraitors => _cfg.GetCVar(CCVars.TraitorMaxTraitors);
@@ -46,8 +47,6 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
{
base.Initialize();
_sawmill = Logger.GetSawmill("preset");
SubscribeLocalEvent<RoundStartAttemptEvent>(OnStartAttempt);
SubscribeLocalEvent<RulePlayerJobsAssignedEvent>(OnPlayersSpawned);
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(HandleLatejoin);
@@ -107,7 +106,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
{
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;
}
@@ -156,7 +155,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
foreach (var player in candidates.Keys)
{
// Role prevents antag.
if (!(player.Data.ContentData()?.Mind?.AllRoles.All(role => role is not Job { CanBeAntag: false }) ?? false))
if (!_jobs.CanBeAntag(player))
{
continue;
}
@@ -180,7 +179,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
}
if (prefList.Count == 0)
{
_sawmill.Info("Insufficient preferred traitors, picking at random.");
Log.Info("Insufficient preferred traitors, picking at random.");
prefList = list;
}
return prefList;
@@ -191,14 +190,14 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
var results = new List<IPlayerSession>(traitorCount);
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;
}
for (var i = 0; i < traitorCount; i++)
{
results.Add(_random.PickAndTake(prefList));
_sawmill.Info("Selected a preferred traitor.");
Log.Info("Selected a preferred traitor.");
}
return results;
}
@@ -215,22 +214,28 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
MakeCodewords(traitorRule);
}
var mind = traitor.Data.ContentData()?.Mind;
if (mind == null)
if (!_mindSystem.TryGetMind(traitor, out var mindId, out var mind))
{
_sawmill.Info("Failed getting mind for picked traitor.");
Log.Info("Failed getting mind for picked traitor.");
return false;
}
if (HasComp<TraitorRoleComponent>(mindId))
{
Log.Error($"Player {traitor.Name} is already a traitor.");
return false;
}
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;
}
// Calculate the amount of currency on the uplink.
var startingBalance = _cfg.GetCVar(CCVars.TraitorStartingBalance);
if (mind.CurrentJob != null)
startingBalance = Math.Max(startingBalance - mind.CurrentJob.Prototype.AntagAdvantage, 0);
if (_jobs.MindTryGetJob(mindId, out _, out var prototype))
startingBalance = Math.Max(startingBalance - prototype.AntagAdvantage, 0);
// creadth: we need to create uplink for the antag.
// 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
var code = EnsureComp<RingerUplinkComponent>(pda.Value).Code;
// Prepare antagonist role
var antagPrototype = _prototypeManager.Index<AntagPrototype>(traitorRule.TraitorPrototypeId);
var traitorRole = new TraitorRole(mind, antagPrototype);
// Prepare traitor role
// Give traitors their codewords and uplink code to keep in their character info menu
traitorRole.Mind.Briefing = string.Format(
"{0}\n{1}",
Loc.GetString("traitor-role-codewords-short", ("codewords", string.Join(", ", traitorRule.Codewords))),
Loc.GetString("traitor-role-uplink-code-short", ("code", string.Join("-", code).Replace("sharp","#"))));
var briefing =
$"{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", "#")))}";
var traitorRole = new TraitorRoleComponent
{
PrototypeId = traitorRule.TraitorPrototypeId,
Briefing = briefing
};
// Assign traitor roles
_mindSystem.AddRole(mind, traitorRole);
SendTraitorBriefing(mind, traitorRule.Codewords, code);
traitorRule.Traitors.Add(traitorRole);
_roleSystem.MindAddRole(mindId, traitorRole);
SendTraitorBriefing(mindId, traitorRule.Codewords, code);
traitorRule.TraitorMinds.Add(mindId);
if (_mindSystem.TryGetSession(mind, out var session))
if (_mindSystem.TryGetSession(mindId, out var session))
{
// Notificate player about new role assignment
_audioSystem.PlayGlobal(traitorRule.GreetSoundNotification, session);
@@ -272,11 +277,11 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
var difficulty = 0f;
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)
continue;
if (_mindSystem.TryAddObjective(traitorRole.Mind, objective))
if (_mindSystem.TryAddObjective(mindId, mind, objective))
difficulty += objective.Difficulty;
}
@@ -289,7 +294,7 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
/// <param name="mind">A mind (player)</param>
/// <param name="codewords">Codewords</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))
{
@@ -362,18 +367,21 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
if (!GameTicker.IsGameRuleAdded(uid, gameRule))
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))) +
"\n";
foreach (var t in traitor.Traitors)
foreach (var mindId in traitor.TraitorMinds)
{
var name = t.Mind.CharacterName;
_mindSystem.TryGetSession(t.Mind, out var session);
if (!TryComp(mindId, out MindComponent? mind))
continue;
var name = mind.CharacterName;
_mindSystem.TryGetSession(mindId, out var session);
var username = session?.Name;
var objectives = t.Mind.AllObjectives.ToArray();
var objectives = mind.AllObjectives.ToArray();
if (objectives.Length == 0)
{
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 role in GetOtherTraitorsAliveAndConnected(ourMind, traitor))
foreach (var role in GetOtherTraitorMindsAliveAndConnected(ourMind, traitor))
{
if (!allTraitors.Contains(role))
allTraitors.Add(role);
@@ -452,13 +460,22 @@ public sealed class TraitorRuleSystem : GameRuleSystem<TraitorRuleComponent>
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
.Where(t => t.Mind.OwnedEntity is not null) // no entity
.Where(t => t.Mind.Session is not null) // player disconnected
.Where(t => t.Mind != ourMind) // ourselves
.Where(t => _mobStateSystem.IsAlive((EntityUid) t.Mind.OwnedEntity!)) // dead
.Where(t => t.Mind.CurrentEntity == t.Mind.OwnedEntity).ToList(); // not in original body
var traitors = new List<(EntityUid Id, MindComponent Mind)>();
foreach (var traitor in component.TraitorMinds)
{
if (TryComp(traitor, out MindComponent? mind) &&
mind.OwnedEntity != null &&
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.GameTicking.Rules.Components;
using Content.Server.Mind;
using Content.Server.Mind.Components;
using Content.Server.Players;
using Content.Server.Popups;
using Content.Server.Preferences.Managers;
using Content.Server.Roles;
@@ -21,7 +19,6 @@ using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Preferences;
using Content.Shared.Roles;
using Content.Shared.Zombies;
using Robust.Server.GameObjects;
using Robust.Server.Player;
@@ -48,6 +45,7 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly ZombieSystem _zombie = default!;
[Dependency] private readonly MindSystem _mindSystem = default!;
[Dependency] private readonly RoleSystem _roles = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
@@ -97,10 +95,9 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
{
var meta = MetaData(survivor);
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 = mindcomp.Mind.Session.Name;
username = mind.Session.Name;
}
ev.AddLine(Loc.GetString("zombie-round-end-user-was-survivor",
@@ -312,12 +309,15 @@ public sealed class ZombieRuleSystem : GameRuleSystem<ZombieRuleComponent>
prefList.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;
}
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);
pending.GracePeriod = _random.Next(component.MinInitialInfectedGrace, component.MaxInitialInfectedGrace);