Skip to content

Commit 08822e3

Browse files
VMSolidusThereDrD0BurningRashTimemaster99blueDev2
authored
Cherry-Pick Antag Refactor (Simple-Station#734)
This cherry-pick's Wizden's Antag Refactor, which is needed for all future antag updates, as well as for me to cherry-pick over Cultists(Who are going to need some editing to fit the antag refactor), Changelings, Heretics, and Wizards. I actually selected the White-Dream-Public version of the Antag Refactor, due to it having commits made tailored to our repository, so it comes prepackaged with all the changes necessary for our repo-specific content. https://github.com/frosty-dev/ss14-wwdp/pull/10 Signed-off-by: Timemaster99 <[email protected]> Co-authored-by: ThereDrD <[email protected]> Co-authored-by: Jeff <[email protected]> Co-authored-by: Timemaster99 <[email protected]> Co-authored-by: Timemaster99 <[email protected]> Co-authored-by: [email protected] <[email protected]> Co-authored-by: Danger Revolution! <[email protected]> Co-authored-by: Azzy <[email protected]>
1 parent f93b8dc commit 08822e3

121 files changed

Lines changed: 4544 additions & 4777 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Content.Benchmarks/SpawnEquipDeleteBenchmark.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ await _pair.Server.WaitPost(() =>
5858
for (var i = 0; i < N; i++)
5959
{
6060
_entity = server.EntMan.SpawnAttachedTo(Mob, _coords);
61-
_spawnSys.EquipStartingGear(_entity, _gear, null);
61+
_spawnSys.EquipStartingGear(_entity, _gear);
6262
server.EntMan.DeleteEntity(_entity);
6363
}
6464
});

Content.Client/Humanoid/HumanoidAppearanceSystem.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,11 @@ private void SetLayerData(
118118
/// This should not be used if the entity is owned by the server. The server will otherwise
119119
/// override this with the appearance data it sends over.
120120
/// </remarks>
121-
public override void LoadProfile(EntityUid uid, HumanoidCharacterProfile profile, HumanoidAppearanceComponent? humanoid = null)
121+
public override void LoadProfile(EntityUid uid, HumanoidCharacterProfile? profile, HumanoidAppearanceComponent? humanoid = null)
122122
{
123+
if (profile == null)
124+
return;
125+
123126
if (!Resolve(uid, ref humanoid))
124127
{
125128
return;
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/* WD edit
2+
3+
#nullable enable
4+
using System.Linq;
5+
using Content.Server.Body.Components;
6+
using Content.Server.GameTicking;
7+
using Content.Server.GameTicking.Presets;
8+
using Content.Server.GameTicking.Rules.Components;
9+
using Content.Server.Mind;
10+
using Content.Server.NPC.Systems;
11+
using Content.Server.Pinpointer;
12+
using Content.Server.Roles;
13+
using Content.Server.Shuttles.Components;
14+
using Content.Server.Station.Components;
15+
using Content.Shared.CCVar;
16+
using Content.Shared.Damage;
17+
using Content.Shared.FixedPoint;
18+
using Content.Shared.GameTicking;
19+
using Content.Shared.Hands.Components;
20+
using Content.Shared.Inventory;
21+
using Content.Shared.NukeOps;
22+
using Robust.Server.GameObjects;
23+
using Robust.Shared.GameObjects;
24+
using Robust.Shared.Map.Components;
25+
26+
namespace Content.IntegrationTests.Tests.GameRules;
27+
28+
[TestFixture]
29+
public sealed class NukeOpsTest
30+
{
31+
/// <summary>
32+
/// Check that a nuke ops game mode can start without issue. I.e., that the nuke station and such all get loaded.
33+
/// </summary>
34+
[Test]
35+
public async Task TryStopNukeOpsFromConstantlyFailing()
36+
{
37+
await using var pair = await PoolManager.GetServerClient(new PoolSettings
38+
{
39+
Dirty = true,
40+
DummyTicker = false,
41+
Connected = true,
42+
InLobby = true
43+
});
44+
45+
var server = pair.Server;
46+
var client = pair.Client;
47+
var entMan = server.EntMan;
48+
var mapSys = server.System<MapSystem>();
49+
var ticker = server.System<GameTicker>();
50+
var mindSys = server.System<MindSystem>();
51+
var roleSys = server.System<RoleSystem>();
52+
var invSys = server.System<InventorySystem>();
53+
var factionSys = server.System<NpcFactionSystem>();
54+
55+
Assert.That(server.CfgMan.GetCVar(CCVars.GridFill), Is.False);
56+
server.CfgMan.SetCVar(CCVars.GridFill, true);
57+
58+
// Initially in the lobby
59+
Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby));
60+
Assert.That(client.AttachedEntity, Is.Null);
61+
Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.NotReadyToPlay));
62+
63+
// There are no grids or maps
64+
Assert.That(entMan.Count<MapComponent>(), Is.Zero);
65+
Assert.That(entMan.Count<MapGridComponent>(), Is.Zero);
66+
Assert.That(entMan.Count<StationMapComponent>(), Is.Zero);
67+
Assert.That(entMan.Count<StationMemberComponent>(), Is.Zero);
68+
Assert.That(entMan.Count<StationCentcommComponent>(), Is.Zero);
69+
70+
// And no nukie related components
71+
Assert.That(entMan.Count<NukeopsRuleComponent>(), Is.Zero);
72+
Assert.That(entMan.Count<NukeopsRoleComponent>(), Is.Zero);
73+
Assert.That(entMan.Count<NukeOperativeComponent>(), Is.Zero);
74+
Assert.That(entMan.Count<NukeOpsShuttleComponent>(), Is.Zero);
75+
Assert.That(entMan.Count<NukeOperativeSpawnerComponent>(), Is.Zero);
76+
77+
// Ready up and start nukeops
78+
await pair.WaitClientCommand("toggleready True");
79+
Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.ReadyToPlay));
80+
await pair.WaitCommand("forcepreset Nukeops");
81+
await pair.RunTicksSync(10);
82+
83+
// Game should have started
84+
Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.InRound));
85+
Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.JoinedGame));
86+
Assert.That(client.EntMan.EntityExists(client.AttachedEntity));
87+
var player = pair.Player!.AttachedEntity!.Value;
88+
Assert.That(entMan.EntityExists(player));
89+
90+
// Maps now exist
91+
Assert.That(entMan.Count<MapComponent>(), Is.GreaterThan(0));
92+
Assert.That(entMan.Count<MapGridComponent>(), Is.GreaterThan(0));
93+
Assert.That(entMan.Count<StationDataComponent>(), Is.EqualTo(2)); // The main station & nukie station
94+
Assert.That(entMan.Count<StationMemberComponent>(), Is.GreaterThan(3)); // Each station has at least 1 grid, plus some shuttles
95+
Assert.That(entMan.Count<StationCentcommComponent>(), Is.EqualTo(1));
96+
97+
// And we now have nukie related components
98+
Assert.That(entMan.Count<NukeopsRuleComponent>(), Is.EqualTo(1));
99+
Assert.That(entMan.Count<NukeopsRoleComponent>(), Is.EqualTo(1));
100+
Assert.That(entMan.Count<NukeOperativeComponent>(), Is.EqualTo(1));
101+
Assert.That(entMan.Count<NukeOpsShuttleComponent>(), Is.EqualTo(1));
102+
103+
// The player entity should be the nukie commander
104+
var mind = mindSys.GetMind(player)!.Value;
105+
Assert.That(entMan.HasComponent<NukeOperativeComponent>(player));
106+
Assert.That(roleSys.MindIsAntagonist(mind));
107+
Assert.That(roleSys.MindHasRole<NukeopsRoleComponent>(mind));
108+
109+
Assert.That(factionSys.IsMember(player, "Syndicate"), Is.True);
110+
Assert.That(factionSys.IsMember(player, "NanoTrasen"), Is.False);
111+
112+
var roles = roleSys.MindGetAllRoles(mind);
113+
var cmdRoles = roles.Where(x => x.Prototype == "NukeopsCommander" && x.Component is NukeopsRoleComponent);
114+
Assert.That(cmdRoles.Count(), Is.EqualTo(1));
115+
116+
// The game rule exists, and all the stations/shuttles/maps are properly initialized
117+
var rule = entMan.AllComponents<NukeopsRuleComponent>().Single().Component;
118+
var mapRule = entMan.AllComponents<LoadMapRuleComponent>().Single().Component;
119+
foreach (var grid in mapRule.MapGrids)
120+
{
121+
Assert.That(entMan.EntityExists(grid));
122+
Assert.That(entMan.HasComponent<MapGridComponent>(grid));
123+
Assert.That(entMan.HasComponent<StationMemberComponent>(grid));
124+
}
125+
Assert.That(entMan.EntityExists(rule.TargetStation));
126+
127+
Assert.That(entMan.HasComponent<StationDataComponent>(rule.TargetStation));
128+
129+
var nukieShuttlEnt = entMan.AllComponents<NukeOpsShuttleComponent>().FirstOrDefault().Uid;
130+
Assert.That(entMan.EntityExists(nukieShuttlEnt));
131+
132+
EntityUid? nukieStationEnt = null;
133+
foreach (var grid in mapRule.MapGrids)
134+
{
135+
if (entMan.HasComponent<StationMemberComponent>(grid))
136+
{
137+
nukieStationEnt = grid;
138+
break;
139+
}
140+
}
141+
142+
Assert.That(entMan.EntityExists(nukieStationEnt));
143+
var nukieStation = entMan.GetComponent<StationMemberComponent>(nukieStationEnt!.Value);
144+
145+
Assert.That(entMan.EntityExists(nukieStation.Station));
146+
Assert.That(nukieStation.Station, Is.Not.EqualTo(rule.TargetStation));
147+
148+
Assert.That(server.MapMan.MapExists(mapRule.Map));
149+
var nukieMap = mapSys.GetMap(mapRule.Map!.Value);
150+
151+
var targetStation = entMan.GetComponent<StationDataComponent>(rule.TargetStation!.Value);
152+
var targetGrid = targetStation.Grids.First();
153+
var targetMap = entMan.GetComponent<TransformComponent>(targetGrid).MapUid!.Value;
154+
Assert.That(targetMap, Is.Not.EqualTo(nukieMap));
155+
156+
Assert.That(entMan.GetComponent<TransformComponent>(player).MapUid, Is.EqualTo(nukieMap));
157+
Assert.That(entMan.GetComponent<TransformComponent>(nukieStationEnt.Value).MapUid, Is.EqualTo(nukieMap));
158+
Assert.That(entMan.GetComponent<TransformComponent>(nukieShuttlEnt).MapUid, Is.EqualTo(nukieMap));
159+
160+
// The maps are all map-initialized, including the player
161+
// Yes, this is necessary as this has repeatedly been broken somehow.
162+
Assert.That(mapSys.IsInitialized(nukieMap));
163+
Assert.That(mapSys.IsInitialized(targetMap));
164+
Assert.That(mapSys.IsPaused(nukieMap), Is.False);
165+
Assert.That(mapSys.IsPaused(targetMap), Is.False);
166+
167+
EntityLifeStage LifeStage(EntityUid? uid) => entMan.GetComponent<MetaDataComponent>(uid!.Value).EntityLifeStage;
168+
Assert.That(LifeStage(player), Is.GreaterThan(EntityLifeStage.Initialized));
169+
Assert.That(LifeStage(nukieMap), Is.GreaterThan(EntityLifeStage.Initialized));
170+
Assert.That(LifeStage(targetMap), Is.GreaterThan(EntityLifeStage.Initialized));
171+
Assert.That(LifeStage(nukieStationEnt.Value), Is.GreaterThan(EntityLifeStage.Initialized));
172+
Assert.That(LifeStage(nukieShuttlEnt), Is.GreaterThan(EntityLifeStage.Initialized));
173+
Assert.That(LifeStage(rule.TargetStation), Is.GreaterThan(EntityLifeStage.Initialized));
174+
175+
// Make sure the player has hands. We've had fucking disarmed nukies before.
176+
Assert.That(entMan.HasComponent<HandsComponent>(player));
177+
Assert.That(entMan.GetComponent<HandsComponent>(player).Hands.Count, Is.GreaterThan(0));
178+
179+
// While we're at it, lets make sure they aren't naked. I don't know how many inventory slots all mobs will be
180+
// likely to have in the future. But nukies should probably have at least 3 slots with something in them.
181+
var enumerator = invSys.GetSlotEnumerator(player);
182+
int total = 0;
183+
while (enumerator.NextItem(out _))
184+
{
185+
total++;
186+
}
187+
Assert.That(total, Is.GreaterThan(3));
188+
189+
// Finally lets check the nukie commander passed basic training and figured out how to breathe.
190+
var totalSeconds = 30;
191+
var totalTicks = (int) Math.Ceiling(totalSeconds / server.Timing.TickPeriod.TotalSeconds);
192+
int increment = 5;
193+
var resp = entMan.GetComponent<RespiratorComponent>(player);
194+
var damage = entMan.GetComponent<DamageableComponent>(player);
195+
for (var tick = 0; tick < totalTicks; tick += increment)
196+
{
197+
await pair.RunTicksSync(increment);
198+
Assert.That(resp.SuffocationCycles, Is.LessThanOrEqualTo(resp.SuffocationCycleThreshold));
199+
Assert.That(damage.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
200+
}
201+
202+
//ticker.SetGamePreset((GamePresetPrototype?)null); WD edit
203+
server.CfgMan.SetCVar(CCVars.GridFill, false);
204+
await pair.CleanReturnAsync();
205+
}
206+
}
207+
208+
*/

Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Content.Server.GameTicking;
22
using Content.Server.GameTicking.Commands;
3+
using Content.Server.GameTicking.Components;
34
using Content.Server.GameTicking.Rules;
45
using Content.Server.GameTicking.Rules.Components;
56
using Content.Shared.CCVar;

Content.IntegrationTests/Tests/GameRules/SecretStartsTest.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public async Task TestSecretStarts()
1717

1818
var server = pair.Server;
1919
await server.WaitIdleAsync();
20+
var entMan = server.ResolveDependency<IEntityManager>();
2021
var gameTicker = server.ResolveDependency<IEntitySystemManager>().GetEntitySystem<GameTicker>();
2122

2223
await server.WaitAssertion(() =>
@@ -32,10 +33,7 @@ await server.WaitAssertion(() =>
3233

3334
await server.WaitAssertion(() =>
3435
{
35-
foreach (var rule in gameTicker.GetAddedGameRules())
36-
{
37-
Assert.That(gameTicker.GetActiveGameRules(), Does.Contain(rule));
38-
}
36+
Assert.That(gameTicker.GetAddedGameRules().Count(), Is.GreaterThan(1), $"No additional rules started by secret rule.");
3937

4038
// End all rules
4139
gameTicker.ClearGameRules();

Content.Server/Administration/ServerApi.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System.Threading.Tasks;
99
using Content.Server.Administration.Systems;
1010
using Content.Server.GameTicking;
11+
using Content.Server.GameTicking.Components;
1112
using Content.Server.GameTicking.Presets;
1213
using Content.Server.GameTicking.Rules.Components;
1314
using Content.Server.Maps;

Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,37 @@
1-
using Content.Server.GameTicking.Rules;
1+
using Content.Server.Administration.Commands;
2+
using Content.Server.Antag;
3+
using Content.Server.GameTicking.Rules.Components;
24
using Content.Server.Zombies;
35
using Content.Shared.Administration;
46
using Content.Shared.Database;
5-
using Content.Shared.Humanoid;
67
using Content.Shared.Mind.Components;
8+
using Content.Shared.Roles;
79
using Content.Shared.Verbs;
810
using Robust.Shared.Player;
11+
using Robust.Shared.Prototypes;
912
using Robust.Shared.Utility;
1013

1114
namespace Content.Server.Administration.Systems;
1215

1316
public sealed partial class AdminVerbSystem
1417
{
18+
[Dependency] private readonly AntagSelectionSystem _antag = default!;
1519
[Dependency] private readonly ZombieSystem _zombie = default!;
16-
[Dependency] private readonly ThiefRuleSystem _thief = default!;
17-
[Dependency] private readonly TraitorRuleSystem _traitorRule = default!;
18-
[Dependency] private readonly NukeopsRuleSystem _nukeopsRule = default!;
19-
[Dependency] private readonly PiratesRuleSystem _piratesRule = default!;
20-
[Dependency] private readonly RevolutionaryRuleSystem _revolutionaryRule = default!;
20+
21+
[ValidatePrototypeId<EntityPrototype>]
22+
private const string DefaultTraitorRule = "Traitor";
23+
24+
[ValidatePrototypeId<EntityPrototype>]
25+
private const string DefaultNukeOpRule = "LoneOpsSpawn";
26+
27+
[ValidatePrototypeId<EntityPrototype>]
28+
private const string DefaultRevsRule = "Revolutionary";
29+
30+
[ValidatePrototypeId<EntityPrototype>]
31+
private const string DefaultThiefRule = "Thief";
32+
33+
[ValidatePrototypeId<StartingGearPrototype>]
34+
private const string PirateGearId = "PirateGear";
2135

2236
// All antag verbs have names so invokeverb works.
2337
private void AddAntagVerbs(GetVerbsEvent<Verb> args)
@@ -40,9 +54,7 @@ private void AddAntagVerbs(GetVerbsEvent<Verb> args)
4054
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Structures/Wallmounts/posters.rsi"), "poster5_contraband"),
4155
Act = () =>
4256
{
43-
// if its a monkey or mouse or something dont give uplink or objectives
44-
var isHuman = HasComp<HumanoidAppearanceComponent>(args.Target);
45-
_traitorRule.MakeTraitorAdmin(args.Target, giveUplink: isHuman, giveObjectives: isHuman);
57+
_antag.ForceMakeAntag<TraitorRuleComponent>(player, DefaultTraitorRule);
4658
},
4759
Impact = LogImpact.High,
4860
Message = Loc.GetString("admin-verb-make-traitor"),
@@ -71,7 +83,7 @@ private void AddAntagVerbs(GetVerbsEvent<Verb> args)
7183
Icon = new SpriteSpecifier.Rsi(new("/Textures/Structures/Wallmounts/signs.rsi"), "radiation"),
7284
Act = () =>
7385
{
74-
_nukeopsRule.MakeLoneNukie(args.Target);
86+
_antag.ForceMakeAntag<NukeopsRuleComponent>(player, DefaultNukeOpRule);
7587
},
7688
Impact = LogImpact.High,
7789
Message = Loc.GetString("admin-verb-make-nuclear-operative"),
@@ -85,22 +97,22 @@ private void AddAntagVerbs(GetVerbsEvent<Verb> args)
8597
Icon = new SpriteSpecifier.Rsi(new("/Textures/Clothing/Head/Hats/pirate.rsi"), "icon"),
8698
Act = () =>
8799
{
88-
_piratesRule.MakePirate(args.Target);
100+
// pirates just get an outfit because they don't really have logic associated with them
101+
SetOutfitCommand.SetOutfit(args.Target, PirateGearId, EntityManager);
89102
},
90103
Impact = LogImpact.High,
91104
Message = Loc.GetString("admin-verb-make-pirate"),
92105
};
93106
args.Verbs.Add(pirate);
94107

95-
//todo come here at some point dear lort.
96108
Verb headRev = new()
97109
{
98110
Text = Loc.GetString("admin-verb-text-make-head-rev"),
99111
Category = VerbCategory.Antag,
100112
Icon = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/job_icons.rsi"), "HeadRevolutionary"),
101113
Act = () =>
102114
{
103-
_revolutionaryRule.OnHeadRevAdmin(args.Target);
115+
_antag.ForceMakeAntag<RevolutionaryRuleComponent>(player, DefaultRevsRule);
104116
},
105117
Impact = LogImpact.High,
106118
Message = Loc.GetString("admin-verb-make-head-rev"),
@@ -114,7 +126,7 @@ private void AddAntagVerbs(GetVerbsEvent<Verb> args)
114126
Icon = new SpriteSpecifier.Rsi(new ResPath("/Textures/Clothing/Hands/Gloves/ihscombat.rsi"), "icon"),
115127
Act = () =>
116128
{
117-
_thief.AdminMakeThief(args.Target, false); //Midround add pacified is bad
129+
_antag.ForceMakeAntag<ThiefRuleComponent>(player, DefaultThiefRule);
118130
},
119131
Impact = LogImpact.High,
120132
Message = Loc.GetString("admin-verb-make-thief"),
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
using System.Linq;
3+
using Robust.Shared.Player;
4+
using Robust.Shared.Random;
5+
6+
namespace Content.Server.Antag;
7+
8+
public sealed class AntagSelectionPlayerPool (List<List<ICommonSession>> orderedPools)
9+
{
10+
public bool TryPickAndTake(IRobustRandom random, [NotNullWhen(true)] out ICommonSession? session)
11+
{
12+
session = null;
13+
14+
foreach (var pool in orderedPools)
15+
{
16+
if (pool.Count == 0)
17+
continue;
18+
19+
session = random.PickAndTake(pool);
20+
break;
21+
}
22+
23+
return session != null;
24+
}
25+
26+
public int Count => orderedPools.Sum(p => p.Count);
27+
}

0 commit comments

Comments
 (0)