From 5930cccfe500705776ca36f4511ff9737eb6a1c0 Mon Sep 17 00:00:00 2001 From: Bartosz Gradzik Date: Fri, 10 Oct 2025 21:52:09 +0200 Subject: [PATCH] Poprawki w rolach zwyciezcow --- Export/DataTransformer.cs | 139 +++++++++++++++++++++++--- Export/StatsExporter.cs | 5 + Reflection/TouMiraReflectionBridge.cs | 2 +- 3 files changed, 131 insertions(+), 15 deletions(-) diff --git a/Export/DataTransformer.cs b/Export/DataTransformer.cs index 466dcb5..709c7a7 100644 --- a/Export/DataTransformer.cs +++ b/Export/DataTransformer.cs @@ -195,28 +195,139 @@ public static class DataTransformer private static string DetermineWinningTeam(string winningFaction, List playerRecords) { - // Use WinningFaction from GameHistory if available - if (!string.IsNullOrEmpty(winningFaction)) + TownOfUsStatsPlugin.Logger.LogInfo($"DetermineWinningTeam called with winningFaction='{winningFaction}'"); + + // ALWAYS use PlayerRecords to determine the winning team + // WinningFaction field can be stale (contains data from previous game) + var winners = playerRecords.Where(r => r.Winner).ToList(); + + if (winners.Count == 0) { - // Clean HTML tags from winning faction - return StripColorTags(winningFaction); + TownOfUsStatsPlugin.Logger.LogWarning("No winners found in PlayerRecords!"); + return "Unknown"; } - // Fallback: Check first winner's team - var winner = playerRecords.FirstOrDefault(r => r.Winner); - if (winner == null) + TownOfUsStatsPlugin.Logger.LogInfo($"Found {winners.Count} winner(s)"); + + // Group winners by team + var winnersByTeam = winners.GroupBy(w => w.TeamString).ToList(); + + // Log all winner teams + foreach (var group in winnersByTeam) + { + var roles = string.Join(", ", group.Select(w => ParseFirstRoleFromRoleString(w.RoleString))); + TownOfUsStatsPlugin.Logger.LogInfo($" Team '{group.Key}': {group.Count()} winner(s) - Roles: {roles}"); + } + + // Case 1: All winners are from the same team + if (winnersByTeam.Count == 1) + { + var teamString = winnersByTeam[0].Key; + TownOfUsStatsPlugin.Logger.LogInfo($"All winners from same team: '{teamString}'"); + + // For Custom team (solo neutrals, Lovers, etc.), use the role name + if (teamString == "Custom") + { + // If there's only one winner, it's a solo neutral (Werewolf, Glitch, etc.) + if (winners.Count == 1) + { + var roleName = ParseFirstRoleFromRoleString(winners[0].RoleString); + TownOfUsStatsPlugin.Logger.LogInfo($"Solo neutral win: '{roleName}'"); + return roleName; + } + // If multiple winners with Custom team, it's Lovers + else + { + TownOfUsStatsPlugin.Logger.LogInfo("Multiple Custom team winners: Lovers"); + return "Lovers"; + } + } + + // Standard team conversions + var result = teamString switch + { + "Crewmate" => "Crewmates", + "Impostor" => "Impostors", + "Neutral" => "Neutrals", + _ => "Unknown", + }; + + TownOfUsStatsPlugin.Logger.LogInfo($"Determined winning team: '{result}'"); + return result; + } + + // Case 2: Mixed teams (e.g., Mercenary winning with Impostors, Jester with Crewmates) + // Find the main team (non-Neutral with most winners) + TownOfUsStatsPlugin.Logger.LogInfo("Mixed teams detected"); + + var mainTeam = winnersByTeam + .Where(g => g.Key is "Crewmate" or "Impostor") // Only Crewmate or Impostor + .OrderByDescending(g => g.Count()) + .FirstOrDefault(); + + if (mainTeam != null) + { + var result = mainTeam.Key switch + { + "Crewmate" => "Crewmates", + "Impostor" => "Impostors", + _ => "Unknown", + }; + + TownOfUsStatsPlugin.Logger.LogInfo($"Main winning team (mixed): '{result}'"); + return result; + } + + // Case 3: Only Neutrals won (shouldn't happen, but fallback) + TownOfUsStatsPlugin.Logger.LogWarning("Only Neutral/Custom winners, no main team found"); + return "Neutrals"; + } + + /// + /// Parses the first role name from RoleString (e.g., "Werewolf (Flash) (2/4) | Alive" → "Werewolf") + /// + private static string ParseFirstRoleFromRoleString(string roleString) + { + if (string.IsNullOrEmpty(roleString)) { return "Unknown"; } - return winner.TeamString switch + // Strip color tags first + var cleanString = StripColorTags(roleString); + + // Remove everything after " |" (status info) + var pipeIndex = cleanString.IndexOf(" |"); + if (pipeIndex > 0) { - "Crewmate" => "Crewmates", - "Impostor" => "Impostors", - "Neutral" => "Neutrals", - "Custom" => "Custom", - _ => "Unknown", - }; + cleanString = cleanString.Substring(0, pipeIndex).Trim(); + } + + // Remove task info like "(0/4)" at the end + cleanString = Regex.Replace(cleanString, @"\s*\(\d+/\d+\)\s*$", "").Trim(); + + // Remove modifiers in parentheses (e.g., "(Flash)") + var parenIndex = cleanString.IndexOf('('); + if (parenIndex > 0) + { + cleanString = cleanString.Substring(0, parenIndex).Trim(); + } + + // If there's role history (separated by " > "), get the LAST role + if (cleanString.Contains(" > ")) + { + var roles = cleanString.Split(new[] { " > " }, StringSplitOptions.RemoveEmptyEntries); + cleanString = roles.Last().Trim(); + + // Remove modifiers again (after extracting last role) + parenIndex = cleanString.IndexOf('('); + if (parenIndex > 0) + { + cleanString = cleanString.Substring(0, parenIndex).Trim(); + } + } + + return string.IsNullOrEmpty(cleanString) ? "Unknown" : cleanString; } private static string GetMapName(byte mapId) diff --git a/Export/StatsExporter.cs b/Export/StatsExporter.cs index 46c4a0b..115a344 100644 --- a/Export/StatsExporter.cs +++ b/Export/StatsExporter.cs @@ -38,6 +38,10 @@ public static class StatsExporter return; } + // Wait a short delay to ensure TOU Mira has finished updating all static fields + // (including WinningFaction which is set after PlayerRecords) + await Task.Delay(100); + // Get data from TOU Mira via reflection var bridge = ReflectionBridgeProvider.GetBridge(); @@ -54,6 +58,7 @@ public static class StatsExporter var winningFaction = bridge.GetWinningFaction(); TownOfUsStatsPlugin.Logger.LogInfo($"Collected data: {playerRecords.Count} players, {playerStats.Count} stats entries"); + TownOfUsStatsPlugin.Logger.LogInfo($"WinningFaction from GameHistory: '{winningFaction}'"); // Transform to export format var gameData = DataTransformer.TransformToExportFormat( diff --git a/Reflection/TouMiraReflectionBridge.cs b/Reflection/TouMiraReflectionBridge.cs index 356f705..3677a43 100644 --- a/Reflection/TouMiraReflectionBridge.cs +++ b/Reflection/TouMiraReflectionBridge.cs @@ -338,7 +338,7 @@ public class TouMiraReflectionBridge } // Remove "Tou" suffix if present (e.g., "EngineerTou" -> "Engineer", "TrackerTou" -> "Tracker") - if (roleName.EndsWith("Tou")) + if (roleName != null && roleName.EndsWith("Tou")) { roleName = roleName.Substring(0, roleName.Length - 3); }