Refactor antag rule code (#23445)
* Initial Pass, Rev, Thief * Zombie initial pass * Rebase, Traitor * Nukeops, More overloads * Revert RevolutionaryRuleComponent * Use TryRoundStartAttempt, Rewrite nukie spawning * Comments, Add task scheduler to GameRuleSystem * Zombie initial testing done * Sort methods, rework GameRuleTask * Add CCVar, Initial testing continues * Might as well get rid of the obsolete logging * Oops, i dont know how to log apparently * Suggested formatting fixes * Suggested changes * Fix merge issues * Minor optimisation * Allowed thief to choose other antags * Review changes * Spawn items on floor first, then inserting * minor tweaks * Shift as much as possible to ProtoId<> * Remove unneeded * Add exclusive antag attribute * Fix merge issues * Minor formatting fix * Convert to struct * Cleanup * Review cleanup (need to test a lot) * Some fixes, (mostly) tested * oop * Pass tests (for real) --------- Co-authored-by: Rainfall <rainfey0+git@gmail.com> Co-authored-by: AJCM <AJCM@tutanota.com>
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Antag;
|
||||
using Content.Server.Chat.Managers;
|
||||
using Content.Server.EUI;
|
||||
using Content.Server.Flash;
|
||||
using Content.Server.GameTicking.Rules.Components;
|
||||
@@ -13,10 +11,12 @@ using Content.Server.Revolutionary;
|
||||
using Content.Server.Revolutionary.Components;
|
||||
using Content.Server.Roles;
|
||||
using Content.Server.RoundEnd;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Server.Shuttles.Systems;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Mindshield.Components;
|
||||
@@ -27,9 +27,9 @@ using Content.Shared.Revolutionary.Components;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Zombies;
|
||||
using Robust.Server.Audio;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.GameTicking.Rules;
|
||||
|
||||
@@ -39,7 +39,6 @@ namespace Content.Server.GameTicking.Rules;
|
||||
public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleComponent>
|
||||
{
|
||||
[Dependency] private readonly IAdminLogManager _adminLogManager = default!;
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly AntagSelectionSystem _antagSelection = default!;
|
||||
[Dependency] private readonly EuiManager _euiMan = default!;
|
||||
@@ -50,12 +49,13 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
|
||||
[Dependency] private readonly RoleSystem _role = default!;
|
||||
[Dependency] private readonly SharedStunSystem _stun = default!;
|
||||
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
|
||||
[Dependency] private readonly AudioSystem _audioSystem = default!;
|
||||
[Dependency] private readonly StationSystem _stationSystem = default!;
|
||||
[Dependency] private readonly EmergencyShuttleSystem _emergencyShuttle = default!;
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
|
||||
[ValidatePrototypeId<NpcFactionPrototype>]
|
||||
public const string RevolutionaryNpcFaction = "Revolutionary";
|
||||
[ValidatePrototypeId<AntagPrototype>]
|
||||
public const string RevolutionaryAntagRole = "Rev";
|
||||
//Used in OnPostFlash, no reference to the rule component is available
|
||||
public readonly ProtoId<NpcFactionPrototype> RevolutionaryNpcFaction = "Revolutionary";
|
||||
public readonly ProtoId<NpcFactionPrototype> RevPrototypeId = "Rev";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -69,15 +69,20 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
|
||||
SubscribeLocalEvent<HeadRevolutionaryComponent, AfterFlashedEvent>(OnPostFlash);
|
||||
}
|
||||
|
||||
//Set miniumum players
|
||||
protected override void Added(EntityUid uid, RevolutionaryRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args)
|
||||
{
|
||||
base.Added(uid, component, gameRule, args);
|
||||
|
||||
gameRule.MinPlayers = component.MinPlayers;
|
||||
}
|
||||
|
||||
protected override void Started(EntityUid uid, RevolutionaryRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
|
||||
{
|
||||
base.Started(uid, component, gameRule, args);
|
||||
component.CommandCheck = _timing.CurTime + component.TimerWait;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the round should end and also checks who has a mindshield.
|
||||
/// </summary>
|
||||
protected override void ActiveTick(EntityUid uid, RevolutionaryRuleComponent component, GameRuleComponent gameRule, float frameTime)
|
||||
{
|
||||
base.ActiveTick(uid, component, gameRule, frameTime);
|
||||
@@ -139,63 +144,63 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
|
||||
args.Append(Loc.GetString(head ? "head-rev-briefing" : "rev-briefing"));
|
||||
}
|
||||
|
||||
//Check for enough players to start rule
|
||||
private void OnStartAttempt(RoundStartAttemptEvent ev)
|
||||
{
|
||||
var query = AllEntityQuery<RevolutionaryRuleComponent, GameRuleComponent>();
|
||||
while (query.MoveNext(out var uid, out var comp, out var gameRule))
|
||||
{
|
||||
_antagSelection.AttemptStartGameRule(ev, uid, comp.MinPlayers, gameRule);
|
||||
}
|
||||
TryRoundStartAttempt(ev, Loc.GetString("roles-antag-rev-name"));
|
||||
}
|
||||
|
||||
private void OnPlayerJobAssigned(RulePlayerJobsAssignedEvent ev)
|
||||
{
|
||||
var query = QueryActiveRules();
|
||||
while (query.MoveNext(out _, out var comp, out _))
|
||||
while (query.MoveNext(out var uid, out var activeGameRule, out var comp, out var gameRule))
|
||||
{
|
||||
_antagSelection.EligiblePlayers(comp.HeadRevPrototypeId, comp.MaxHeadRevs, comp.PlayersPerHeadRev, comp.HeadRevStartSound,
|
||||
"head-rev-role-greeting", "#5e9cff", out var chosen);
|
||||
if (chosen.Any())
|
||||
GiveHeadRev(chosen, comp.HeadRevPrototypeId, comp);
|
||||
else
|
||||
{
|
||||
_chatManager.SendAdminAnnouncement(Loc.GetString("rev-no-heads"));
|
||||
}
|
||||
var eligiblePlayers = _antagSelection.GetEligiblePlayers(ev.Players, comp.HeadRevPrototypeId);
|
||||
|
||||
if (eligiblePlayers.Count == 0)
|
||||
continue;
|
||||
|
||||
var headRevCount = _antagSelection.CalculateAntagCount(ev.Players.Length, comp.PlayersPerHeadRev, comp.MaxHeadRevs);
|
||||
|
||||
var headRevs = _antagSelection.ChooseAntags(headRevCount, eligiblePlayers);
|
||||
|
||||
GiveHeadRev(headRevs, comp.HeadRevPrototypeId, comp);
|
||||
}
|
||||
}
|
||||
|
||||
private void GiveHeadRev(List<EntityUid> chosen, string antagProto, RevolutionaryRuleComponent comp)
|
||||
private void GiveHeadRev(IEnumerable<EntityUid> chosen, ProtoId<AntagPrototype> antagProto, RevolutionaryRuleComponent comp)
|
||||
{
|
||||
foreach (var headRev in chosen)
|
||||
GiveHeadRev(headRev, antagProto, comp);
|
||||
}
|
||||
private void GiveHeadRev(EntityUid chosen, ProtoId<AntagPrototype> antagProto, RevolutionaryRuleComponent comp)
|
||||
{
|
||||
RemComp<CommandStaffComponent>(chosen);
|
||||
|
||||
var inCharacterName = MetaData(chosen).EntityName;
|
||||
|
||||
if (!_mind.TryGetMind(chosen, out var mind, out _))
|
||||
return;
|
||||
|
||||
if (!_role.MindHasRole<RevolutionaryRoleComponent>(mind))
|
||||
{
|
||||
RemComp<CommandStaffComponent>(headRev);
|
||||
|
||||
var inCharacterName = MetaData(headRev).EntityName;
|
||||
if (_mind.TryGetMind(headRev, out var mindId, out var mind))
|
||||
{
|
||||
if (!_role.MindHasRole<RevolutionaryRoleComponent>(mindId))
|
||||
{
|
||||
_role.MindAddRole(mindId, new RevolutionaryRoleComponent { PrototypeId = antagProto });
|
||||
}
|
||||
if (mind.Session != null)
|
||||
{
|
||||
comp.HeadRevs.Add(inCharacterName, mindId);
|
||||
}
|
||||
}
|
||||
|
||||
_antagSelection.GiveAntagBagGear(headRev, comp.StartingGear);
|
||||
EnsureComp<RevolutionaryComponent>(headRev);
|
||||
EnsureComp<HeadRevolutionaryComponent>(headRev);
|
||||
_role.MindAddRole(mind, new RevolutionaryRoleComponent { PrototypeId = antagProto }, silent: true);
|
||||
}
|
||||
|
||||
comp.HeadRevs.Add(inCharacterName, mind);
|
||||
_inventory.SpawnItemsOnEntity(chosen, comp.StartingGear);
|
||||
var revComp = EnsureComp<RevolutionaryComponent>(chosen);
|
||||
EnsureComp<HeadRevolutionaryComponent>(chosen);
|
||||
|
||||
_antagSelection.SendBriefing(chosen, Loc.GetString("head-rev-role-greeting"), Color.CornflowerBlue, revComp.RevStartSound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a Head Rev uses a flash in melee to convert somebody else.
|
||||
/// </summary>
|
||||
public void OnPostFlash(EntityUid uid, HeadRevolutionaryComponent comp, ref AfterFlashedEvent ev)
|
||||
private void OnPostFlash(EntityUid uid, HeadRevolutionaryComponent comp, ref AfterFlashedEvent ev)
|
||||
{
|
||||
TryComp<AlwaysRevolutionaryConvertibleComponent>(ev.Target, out var alwaysConvertibleComp);
|
||||
var alwaysConvertible = alwaysConvertibleComp != null;
|
||||
var alwaysConvertible = HasComp<AlwaysRevolutionaryConvertibleComponent>(ev.Target);
|
||||
|
||||
if (!_mind.TryGetMind(ev.Target, out var mindId, out var mind) && !alwaysConvertible)
|
||||
return;
|
||||
@@ -211,8 +216,9 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
|
||||
}
|
||||
|
||||
_npcFaction.AddFaction(ev.Target, RevolutionaryNpcFaction);
|
||||
EnsureComp<RevolutionaryComponent>(ev.Target);
|
||||
var revComp = EnsureComp<RevolutionaryComponent>(ev.Target);
|
||||
_stun.TryParalyze(ev.Target, comp.StunTime, true);
|
||||
|
||||
if (ev.User != null)
|
||||
{
|
||||
_adminLogManager.Add(LogType.Mind, LogImpact.Medium, $"{ToPrettyString(ev.User.Value)} converted {ToPrettyString(ev.Target)} into a Revolutionary");
|
||||
@@ -223,20 +229,16 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
|
||||
|
||||
if (mindId == default || !_role.MindHasRole<RevolutionaryRoleComponent>(mindId))
|
||||
{
|
||||
_role.MindAddRole(mindId, new RevolutionaryRoleComponent { PrototypeId = RevolutionaryAntagRole });
|
||||
_role.MindAddRole(mindId, new RevolutionaryRoleComponent { PrototypeId = RevPrototypeId });
|
||||
}
|
||||
|
||||
if (mind?.Session != null)
|
||||
{
|
||||
var message = Loc.GetString("rev-role-greeting");
|
||||
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
|
||||
_chatManager.ChatMessageToOne(ChatChannel.Server, message, wrappedMessage, default, false, mind.Session.Channel, Color.Red);
|
||||
_audioSystem.PlayGlobal("/Audio/Ambience/Antag/headrev_start.ogg", ev.Target);
|
||||
}
|
||||
_antagSelection.SendBriefing(mind.Session, Loc.GetString("rev-role-greeting"), Color.Red, revComp.RevStartSound);
|
||||
}
|
||||
|
||||
public void OnHeadRevAdmin(EntityUid mindId, MindComponent? mind = null)
|
||||
public void OnHeadRevAdmin(EntityUid entity)
|
||||
{
|
||||
if (!Resolve(mindId, ref mind))
|
||||
if (HasComp<HeadRevolutionaryComponent>(entity))
|
||||
return;
|
||||
|
||||
var revRule = EntityQuery<RevolutionaryRuleComponent>().FirstOrDefault();
|
||||
@@ -246,24 +248,10 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
|
||||
revRule = Comp<RevolutionaryRuleComponent>(ruleEnt);
|
||||
}
|
||||
|
||||
if (!HasComp<HeadRevolutionaryComponent>(mind.OwnedEntity))
|
||||
{
|
||||
if (mind.OwnedEntity != null)
|
||||
{
|
||||
var player = new List<EntityUid>
|
||||
{
|
||||
mind.OwnedEntity.Value
|
||||
};
|
||||
GiveHeadRev(player, RevolutionaryAntagRole, revRule);
|
||||
}
|
||||
if (mind.Session != null)
|
||||
{
|
||||
var message = Loc.GetString("head-rev-role-greeting");
|
||||
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
|
||||
_chatManager.ChatMessageToOne(ChatChannel.Server, message, wrappedMessage, default, false, mind.Session.Channel, Color.FromHex("#5e9cff"));
|
||||
}
|
||||
}
|
||||
GiveHeadRev(entity, revRule.HeadRevPrototypeId, revRule);
|
||||
}
|
||||
|
||||
//TODO: Enemies of the revolution
|
||||
private void OnCommandMobStateChanged(EntityUid uid, CommandStaffComponent comp, MobStateChangedEvent ev)
|
||||
{
|
||||
if (ev.NewMobState == MobState.Dead || ev.NewMobState == MobState.Invalid)
|
||||
@@ -283,7 +271,7 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
|
||||
commandList.Add(id);
|
||||
}
|
||||
|
||||
return _antagSelection.IsGroupDead(commandList, true);
|
||||
return IsGroupDead(commandList, true);
|
||||
}
|
||||
|
||||
private void OnHeadRevMobStateChanged(EntityUid uid, HeadRevolutionaryComponent comp, MobStateChangedEvent ev)
|
||||
@@ -307,7 +295,7 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
|
||||
}
|
||||
|
||||
// If no Head Revs are alive all normal Revs will lose their Rev status and rejoin Nanotrasen
|
||||
if (_antagSelection.IsGroupDead(headRevList, false))
|
||||
if (IsGroupDead(headRevList, false))
|
||||
{
|
||||
var rev = AllEntityQuery<RevolutionaryComponent, MindContainerComponent>();
|
||||
while (rev.MoveNext(out var uid, out _, out var mc))
|
||||
@@ -338,6 +326,38 @@ public sealed class RevolutionaryRuleSystem : GameRuleSystem<RevolutionaryRuleCo
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will take a group of entities and check if they are all alive or dead
|
||||
/// </summary>
|
||||
/// <param name="list">The list of the entities</param>
|
||||
/// <param name="checkOffStation">Bool for if you want to check if someone is in space and consider them dead. (Won't check when emergency shuttle arrives just in case)</param>
|
||||
/// <returns></returns>
|
||||
private bool IsGroupDead(List<EntityUid> list, bool checkOffStation)
|
||||
{
|
||||
var dead = 0;
|
||||
foreach (var entity in list)
|
||||
{
|
||||
if (TryComp<MobStateComponent>(entity, out var state))
|
||||
{
|
||||
if (state.CurrentState == MobState.Dead || state.CurrentState == MobState.Invalid)
|
||||
{
|
||||
dead++;
|
||||
}
|
||||
else if (checkOffStation && _stationSystem.GetOwningStation(entity) == null && !_emergencyShuttle.EmergencyShuttleArrived)
|
||||
{
|
||||
dead++;
|
||||
}
|
||||
}
|
||||
//If they don't have the MobStateComponent they might as well be dead.
|
||||
else
|
||||
{
|
||||
dead++;
|
||||
}
|
||||
}
|
||||
|
||||
return dead == list.Count || list.Count == 0;
|
||||
}
|
||||
|
||||
private static readonly string[] Outcomes =
|
||||
{
|
||||
// revs survived and heads survived... how
|
||||
|
||||
Reference in New Issue
Block a user