Fix data export formatting and implement robust modifier detection
Major improvements to data cleaning, modifier extraction, and role name handling: ## Data Cleaning & Normalization - Enhanced StripColorTags() to remove ALL HTML/Unity tags using universal regex - Added removal of modifier symbols (♥, #, ★, etc.) from player names - Fixed winningTeam containing HTML tags by applying StripColorTags() - Result: Clean player names without tags or symbols in exported JSON ## Modifier Detection System - Implemented three-tier modifier detection: 1. Symbol extraction from <size=60%>SYMBOL</size> tags (primary) 2. Reflection-based GetModifiers<GameModifier>() (secondary) 3. RoleString parsing fallback (tertiary) - Fixed false positive Underdog detection from friend codes (username#1234) - Symbols now extracted ONLY from size tags, not entire player name - Fixed Dictionary duplicate key error (removed \u2665 duplicate of ♥) - Supported symbols: ♥❤♡ (Lovers), # (Underdog), ★☆ (VIP), @ (Sleuth), etc. ## Role Name Handling - Added fallback for roles without GetRoleName() method - Extracts role names from type names: "GrenadierRole" → "Grenadier" - Removes "Tou" suffix: "EngineerTou" → "Engineer" - Skips generic "RoleBehaviour" type (not a real role) - Result: Full role history now populated correctly ## Code Quality - Added comprehensive logging for debugging modifier/role extraction - Enhanced error handling with per-player try-catch blocks - Improved code documentation with inline comments ## Documentation - Added CLAUDE.md with complete architecture guide - Documented modifier detection system - Added data cleaning & normalization section - Included troubleshooting guide and development workflows Fixes: #1 (HTML tags in export), #2 (missing role history), #3 (false modifier detection) 🎮 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -317,18 +317,48 @@ public class TouMiraReflectionBridge
|
||||
|
||||
TownOfUsStatsPlugin.Logger.LogInfo($"Player {playerId}: RoleBehaviour type = {roleBehaviour.GetType().Name}");
|
||||
|
||||
// Get role name from RoleBehaviour.GetRoleName()
|
||||
// Get role name from RoleBehaviour.GetRoleName() or fallback to type name
|
||||
string? roleName = null;
|
||||
|
||||
var getRoleNameMethod = roleBehaviour.GetType().GetMethod("GetRoleName");
|
||||
if (getRoleNameMethod == null)
|
||||
if (getRoleNameMethod != null)
|
||||
{
|
||||
TownOfUsStatsPlugin.Logger.LogWarning($"GetRoleName method not found on {roleBehaviour.GetType().Name}");
|
||||
roleName = getRoleNameMethod.Invoke(roleBehaviour, null) as string;
|
||||
}
|
||||
|
||||
// Fallback: Extract role name from type name (e.g., "GrenadierRole" -> "Grenadier")
|
||||
if (string.IsNullOrEmpty(roleName))
|
||||
{
|
||||
var typeName = roleBehaviour.GetType().Name;
|
||||
|
||||
// Remove "Role" suffix if present
|
||||
if (typeName.EndsWith("Role"))
|
||||
{
|
||||
roleName = typeName.Substring(0, typeName.Length - 4);
|
||||
}
|
||||
// Remove "Tou" suffix (e.g., "EngineerTou" -> "Engineer")
|
||||
else if (typeName.EndsWith("Tou"))
|
||||
{
|
||||
roleName = typeName.Substring(0, typeName.Length - 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
roleName = typeName;
|
||||
}
|
||||
|
||||
TownOfUsStatsPlugin.Logger.LogInfo($"Player {playerId}: Using type name as role: {roleName}");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(roleName))
|
||||
{
|
||||
TownOfUsStatsPlugin.Logger.LogWarning($"Could not determine role name for player {playerId}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var roleName = getRoleNameMethod.Invoke(roleBehaviour, null) as string;
|
||||
if (string.IsNullOrEmpty(roleName))
|
||||
// Skip generic RoleBehaviour (not a real role)
|
||||
if (roleName == "RoleBehaviour")
|
||||
{
|
||||
TownOfUsStatsPlugin.Logger.LogWarning($"GetRoleName returned null/empty for player {playerId}");
|
||||
TownOfUsStatsPlugin.Logger.LogInfo($"Skipping generic RoleBehaviour for player {playerId}");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -549,10 +579,9 @@ public class TouMiraReflectionBridge
|
||||
return text;
|
||||
}
|
||||
|
||||
text = Regex.Replace(text, @"<color=#[A-Fa-f0-9]+>", string.Empty);
|
||||
text = text.Replace("</color>", string.Empty);
|
||||
text = text.Replace("<b>", string.Empty).Replace("</b>", string.Empty);
|
||||
text = text.Replace("<i>", string.Empty).Replace("</i>", string.Empty);
|
||||
// Remove all Unity/HTML tags
|
||||
// Pattern matches: <tag>, <tag=value>, <tag=#value>, </tag>
|
||||
text = Regex.Replace(text, @"<[^>]+>", string.Empty);
|
||||
|
||||
return text.Trim();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user