CTF spawn protection + KOTZ summary fix for player points (#537)

* Add spawn protection setting

* Fix summary to show individual player points

* Fix
This commit is contained in:
ZypherusKhan authored and GitHub committed 2026-04-01 20:46:18 +02:00
1 parent 5683d2566d
commit a9be3ff652
3 files changed
+59 -21

No files matched your search

+29 -1
View File
@@ -22,6 +22,7 @@ public static class CaptureTheFlag
private static OptionItem WhenFlagCarrierGetsTagged;
private static OptionItem SpeedReductionForFlagCarrier;
private static OptionItem TagCooldown;
private static OptionItem SpawnProtectionTime;
private static OptionItem GameEndCriteria;
private static OptionItem RoundsToPlay;
private static OptionItem PointsToWin;
@@ -55,6 +56,7 @@ public static class CaptureTheFlag
private static Dictionary<byte, CTFPlayerData> PlayerData = [];
private static Dictionary<byte, NetworkedPlayerInfo.PlayerOutfit> DefaultOutfits = [];
private static Dictionary<byte, long> TemporarilyOutPlayers = [];
private static Dictionary<byte, long> SpawnProtectionTimes = [];
private static bool ValidTag;
private static long GameStartTS;
private static int RoundsPlayed => TeamData.Values.Sum(x => x.RoundsWon);
@@ -172,6 +174,11 @@ public static class CaptureTheFlag
.SetGameMode(CustomGameMode.CaptureTheFlag)
.SetValueFormat(OptionFormat.Multiplier)
.SetColor(color);
SpawnProtectionTime = new IntegerOptionItem(id + 13, "CTF_SpawnProtectionTime", new(0, 30, 1), 5, TabGroup.GameSettings)
.SetGameMode(CustomGameMode.CaptureTheFlag)
.SetValueFormat(OptionFormat.Seconds)
.SetColor(color);
}
public static bool KnowTargetRoleColor(PlayerControl target, ref string color)
@@ -240,6 +247,11 @@ public static class CaptureTheFlag
sb.Append(string.Format(Translator.GetString("CTF_BackIn"), backTS - Utils.TimeStamp));
sb.Append('\n');
}
else if (SpawnProtectionTimes.TryGetValue(id, out long protectionEndTS))
{
sb.Append(string.Format(Translator.GetString("CTF_Suffix.SpawnProtectionTime"), protectionEndTS - Utils.TimeStamp));
sb.Append('\n');
}
sb.Append(Utils.ColorString(Color.blue, TeamData[CTFTeam.Blue].RoundsWon.ToString()));
sb.Append("<#ffffff> | </color>");
@@ -326,6 +338,7 @@ public static class CaptureTheFlag
PlayerData = Main.PlayerStates.Keys.ToDictionary(x => x, _ => new CTFPlayerData());
DefaultOutfits = Main.EnumeratePlayerControls().ToDictionary(x => x.PlayerId, x => x.Data.DefaultOutfit);
TemporarilyOutPlayers = [];
SpawnProtectionTimes = [];
ValidTag = false;
SendRPC();
}
@@ -472,7 +485,7 @@ public static class CaptureTheFlag
public static void OnCheckMurder(PlayerControl killer, PlayerControl target)
{
if (!ValidTag || TemporarilyOutPlayers.ContainsKey(killer.PlayerId) || !PlayerTeams.TryGetValue(target.PlayerId, out CTFTeam targetTeam) || !PlayerTeams.TryGetValue(killer.PlayerId, out CTFTeam killerTeam) || killerTeam == targetTeam || TeamData.Values.Any(x => x.FlagCarrier == killer.PlayerId)) return;
if (!ValidTag || TemporarilyOutPlayers.ContainsKey(killer.PlayerId) || SpawnProtectionTimes.ContainsKey(target.PlayerId) || !PlayerTeams.TryGetValue(target.PlayerId, out CTFTeam targetTeam) || !PlayerTeams.TryGetValue(killer.PlayerId, out CTFTeam killerTeam) || killerTeam == targetTeam || TeamData.Values.Any(x => x.FlagCarrier == killer.PlayerId)) return;
new[] { killer, target }.Do(x => x.SetKillCooldownNonSync(KCD));
@@ -488,6 +501,7 @@ public static class CaptureTheFlag
target.TP(targetTeam.GetFlagBase().Position);
Main.AllPlayerSpeed[target.PlayerId] = Main.RealOptionsData.GetFloat(FloatOptionNames.PlayerSpeedMod);
target.MarkDirtySettings();
SpawnProtectionTimes[target.PlayerId] = Utils.TimeStamp + SpawnProtectionTime.GetInt();
break;
case 1:
target.Suicide(PlayerState.DeathReason.Kill);
@@ -811,10 +825,24 @@ public static class CaptureTheFlag
__instance.ReviveFromTemporaryExile();
__instance.TP(team.GetFlagBase().Position);
RPC.PlaySoundRPC(__instance.PlayerId, Sounds.SpawnSound);
SpawnProtectionTimes[__instance.PlayerId] = Utils.TimeStamp + SpawnProtectionTime.GetInt();
}
if (!notified) Utils.NotifyRoles(SpecifySeer: __instance, SpecifyTarget: __instance, SendOption: SendOption.None);
}
if (SpawnProtectionTimes.TryGetValue(__instance.PlayerId, out long protectionEndTS))
{
if (now >= protectionEndTS)
{
SpawnProtectionTimes.Remove(__instance.PlayerId);
Utils.NotifyRoles(SpecifySeer: __instance, SpecifyTarget: __instance, SendOption: SendOption.None);
}
else if (!notified)
{
Utils.NotifyRoles(SpecifySeer: __instance, SpecifyTarget: __instance, SendOption: SendOption.None);
}
}
}
}
}
+28 -20
View File
@@ -82,6 +82,7 @@ public static class KingOfTheZones
private static Dictionary<byte, KOTZTeam> PlayerTeams = [];
public static Dictionary<byte, long> RespawnTimes = [];
private static Dictionary<byte, long> SpawnProtectionTimes = [];
private static Dictionary<byte, int> PlayerPoints = [];
private static Dictionary<KOTZTeam, int> Points = [];
private static Dictionary<SystemTypes, KOTZTeam> ZoneDomination = [];
private static Dictionary<SystemTypes, long> ZoneMoveSchedules = [];
@@ -249,6 +250,7 @@ public static class KingOfTheZones
KOTZTeam[] teams = Enum.GetValues<KOTZTeam>();
Points = teams.ToDictionary(x => x, _ => 0);
PlayerPoints = Main.PlayerStates.Keys.ToDictionary(x => x, _ => 0);
ZoneDomination = Zones.ToDictionary(x => x, _ => KOTZTeam.None);
ZoneMoveSchedules = [];
ZoneDowntimeExpire = [];
@@ -591,13 +593,13 @@ public static class KingOfTheZones
public static string GetStatistics(byte id)
{
return string.Format(GetString("KOTZ.EndScreen.Statistics"), GetZoneTime(id));
return string.Format(GetString("KOTZ.EndScreen.Statistics"), PlayerPoints.TryGetValue(id, out int points) ? points : 0);
}
public static int GetZoneTime(byte id)
{
try { return Points[PlayerTeams[id]]; }
catch { return 0; }
if (!PlayerPoints.TryGetValue(id, out int points)) return 0;
return points;
}
public static bool CheckForGameEnd(out GameOverReason reason)
@@ -839,23 +841,22 @@ public static class KingOfTheZones
try
{
Dictionary<SystemTypes, int[]> zoneTeamCounts = new(Zones.Count);
foreach (SystemTypes zone in Zones)
zoneTeamCounts[zone] = new int[TeamsEnumSize];
Dictionary<SystemTypes, int[]> zoneTeamCounts = Zones.ToDictionary(x => x, _ => new int[TeamsEnumSize]);
Dictionary<SystemTypes, List<byte>> playersInZone = Zones.ToDictionary(x => x, _ => new List<byte>());
foreach (PlayerControl player in Main.EnumerateAlivePlayerControls())
{
if (RespawnTimes.ContainsKey(player.PlayerId))
continue;
if (RespawnTimes.ContainsKey(player.PlayerId)) continue;
PlainShipRoom room = player.GetPlainShipRoom();
if (!room) continue;
SystemTypes playerZone = room.RoomId;
if (!playersInZone.TryGetValue(playerZone, out List<byte> occupants)) continue;
int team = (int)PlayerTeams[player.PlayerId];
foreach (SystemTypes zone in Zones)
{
if (player.IsInRoom(zone))
zoneTeamCounts[zone][team]++;
}
zoneTeamCounts[playerZone][team]++;
occupants.Add(player.PlayerId);
}
foreach (SystemTypes zone in Zones)
@@ -894,13 +895,20 @@ public static class KingOfTheZones
: (KOTZTeam)maxTeam;
}
foreach (KOTZTeam team in ZoneDomination.Values)
foreach ((SystemTypes zone, KOTZTeam winningTeam) in ZoneDomination)
{
if (team != KOTZTeam.None)
Points[team]++;
}
if (winningTeam == KOTZTeam.None) continue;
Logger.Info($"Zone domination: {string.Join(", ", ZoneDomination.Select(x => $"{x.Key} = {x.Value}"))}", "KOTZ");
Points[winningTeam]++;
foreach (byte playerId in playersInZone[zone])
{
if (PlayerTeams[playerId] != winningTeam) continue;
if (!Main.PlayerStates.TryGetValue(playerId, out PlayerState state) || state.IsDead) continue;
PlayerPoints[playerId]++;
}
}
}
catch (Exception e) { Utils.ThrowException(e); }
+2
View File
@@ -4260,7 +4260,9 @@
"CTF_PlayerStats_Tags": "<#00a5ff>Tags: <#ffffff>{0}</color></color>",
"CTF_PointsToWin": "Points to Win",
"CTF_RoundsToPlay": "Rounds to Play",
"CTF_SpawnProtectionTime": "Spawn Protection Time",
"CTF_SpeedReductionForFlagCarrier": "Speed Reduction for Flag Carriers",
"CTF_Suffix.SpawnProtectionTime": "<#00ffa5>Under Spawn Protection</color> <#888888>-</color> <#ffff44>{0}</color>",
"CTF_TagCooldown": "Tag Cooldown",
"CTF_TaggedPlayersGet": "Tagged Players get",
"CTF_TaggedPlayersGet.Killed": "Killed",