@@ -17,69 +17,102 @@ public class EscapeManager
1717 {
1818 // Determine which role-specific configuration applies to this player
1919 string playerRoleKey = player . Role . ToString ( ) ;
20- LogManager . Debug ( playerRoleKey ) ;
21- if ( string . IsNullOrWhiteSpace ( playerRoleKey ) )
20+ string playerTeamKey = player . Team . ToString ( ) ;
21+
22+ LogManager . Debug ( $ "Player Role: { playerRoleKey } ") ;
23+ LogManager . Debug ( $ "Player Team: { playerTeamKey } ") ;
24+
25+ if ( string . IsNullOrWhiteSpace ( playerRoleKey ) || string . IsNullOrWhiteSpace ( playerTeamKey ) )
2226 {
2327 LogManager . Warn (
24- $ "Unable to determine player's role for escape evaluation (PlayerId={ player . PlayerId } ). Allowing natural escape.") ;
28+ $ "Unable to determine player's role or team for escape evaluation (PlayerId={ player . PlayerId } ). Allowing natural escape.") ;
2529 return new KeyValuePair < bool , object ? > ( false , null ) ;
2630 }
2731
28- // Try exact match first, then case-insensitive match as fallback
29- if ( ! roleAfterEscape . TryGetValue ( playerRoleKey , out List < Dictionary < string , string > > ? entries ) )
30- foreach ( string ? key in roleAfterEscape . Keys . Where ( key =>
31- string . Equals ( key , playerRoleKey , StringComparison . OrdinalIgnoreCase ) ) )
32+ List < Dictionary < string , string > > ? entries = ResolveEntries (
33+ roleAfterEscape ,
34+ $ "InternalTeam { playerTeamKey } ",
35+ $ "IT { playerRoleKey } ",
36+ playerRoleKey ) ;
37+
38+ if ( UCR . TryGetSummonedCustomRole ( player , out object summonedPlayer ) )
39+ {
40+ int ? customRoleId = UCR . GetSummonedCustomRoleId ( summonedPlayer ) ;
41+ if ( customRoleId is not null )
3242 {
33- entries = roleAfterEscape [ key ] ;
34- break ;
43+ LogManager . Debug ( $ "Player { player . PlayerId } has custom role { customRoleId } , checking for specific escape config...") ;
44+ List < Dictionary < string , string > > ? customEntries = ResolveEntries (
45+ roleAfterEscape ,
46+ $ "CustomRole { customRoleId } ",
47+ $ "CR { customRoleId } ") ;
48+ if ( customEntries is not null )
49+ {
50+ LogManager . Debug ( $ "Found { customEntries . Count } RoleAfterEscape entries for custom role '{ customRoleId } '.") ;
51+ entries = customEntries ;
52+ }
3553 }
54+ }
3655
3756 if ( entries is null )
3857 {
3958 LogManager . Debug ( $ "No RoleAfterEscape entries found for role '{ playerRoleKey } '. Allowing natural escape.") ;
4059 return new KeyValuePair < bool , object ? > ( false , null ) ;
4160 }
61+
62+ LogManager . Debug ( $ "Found { entries . Count } RoleAfterEscape entries for role '{ playerRoleKey } '.") ;
4263
4364 Dictionary < Team , KeyValuePair < bool , object ? > ? > asCuffedByInternalTeam = new ( ) ;
65+ Dictionary < RoleTypeId , KeyValuePair < bool , object ? > ? > asCuffedByInternalRole = new ( ) ;
4466 // Dictionary<uint, KeyValuePair<bool, object?>?> asCuffedByCustomTeam = new(); we will add the support to UCT and UIU-RS
4567 Dictionary < int , KeyValuePair < bool , object ? > ? > asCuffedByCustomRole = new ( ) ;
4668
4769 KeyValuePair < bool , object ? > ? defaultValue = new KeyValuePair < bool , object ? > ( false , null ) ;
4870
4971 // Flatten and parse all condition/value pairs for this role
5072 foreach ( Dictionary < string , string > dict in entries )
51- foreach ( KeyValuePair < string , string > kvp in dict )
5273 {
53- KeyValuePair < bool , object ? > ? data = ParseEscapeString ( kvp . Value ) ;
54- if ( kvp . Key is "default" )
74+ LogManager . Debug ( $ "Parsing RoleAfterEscape entry with { dict . Count } conditions." ) ;
75+ foreach ( KeyValuePair < string , string > kvp in dict )
5576 {
56- defaultValue = data ;
57- }
58- else
59- {
60- List < string > elements = kvp . Key . Split ( ' ' ) . ToList ( ) ;
61-
62- if ( elements . Count != 4 || elements [ 0 ] is not "cuffed" || elements [ 1 ] is not "by" )
77+ KeyValuePair < bool , object ? > ? data = ParseEscapeString ( kvp . Value ) ;
78+ if ( kvp . Key is "default" )
6379 {
64- LogManager . Warn (
65- $ "Failed to parse an EscapeRole[key]: syntax should be cuffed by <source> <id>, found { elements . Count } args! \n Source: { kvp . Key } " ) ;
66- return new KeyValuePair < bool , object ? > ( false , RoleTypeId . Spectator ) ;
80+ defaultValue = data ;
81+ LogManager . Debug (
82+ $ "Set default escape outcome for role ' { playerRoleKey } ' to: { ( data is null ? "Deny" : data . Value . Key ? $ "CustomRole { data . Value . Value } " : $ "InternalRole { data . Value . Value } " ) } " ) ;
6783 }
68-
69- switch ( elements [ 2 ] )
84+ else
7085 {
71- case "InternalTeam" when Enum . TryParse ( elements [ 3 ] , out Team team ) :
72- asCuffedByInternalTeam . TryAdd ( team , data ) ;
73- break ;
74- case "CustomRole" when int . TryParse ( elements [ 3 ] , out int id ) && UCR . TryGetCustomRole ( id , out _ ) :
75- asCuffedByCustomRole . TryAdd ( id , data ) ;
76- break ;
77- default :
86+ List < string > elements = kvp . Key . Split ( ' ' ) . ToList ( ) ;
87+
88+ if ( elements . Count != 4 || elements [ 0 ] is not "cuffed" || elements [ 1 ] is not "by" )
7889 {
79- bool okInt = int . TryParse ( elements [ 3 ] , out _ ) ;
8090 LogManager . Warn (
81- $ "Function SpawnManager::ParseEscapeRole[2](<...>) failed!\n Possible causes can be:\n - The source is not valid. Allowed: InternalTeam / IT / CustomRole / CR. Found: { elements [ 2 ] } \n - The target is not a CustomRole / InternalRole. Found: { elements [ 3 ] } (int32 parsable: { okInt } )") ;
82- break ;
91+ $ "Failed to parse an EscapeRole[key]: syntax should be cuffed by <source> <id>, found { elements . Count } args!\n Source: { kvp . Key } ") ;
92+ return new KeyValuePair < bool , object ? > ( false , RoleTypeId . Spectator ) ;
93+ }
94+
95+ LogManager . Debug ( $ "Parsing escape condition: { kvp . Key } -> { kvp . Value } ") ;
96+
97+ switch ( elements [ 2 ] )
98+ {
99+ case "InternalTeam" or "IT" when Enum . TryParse ( elements [ 3 ] , out Team team ) :
100+ asCuffedByInternalTeam . TryAdd ( team , data ) ;
101+ break ;
102+ case "InternalRole" or "IR" when Enum . TryParse ( elements [ 3 ] , out RoleTypeId id ) :
103+ asCuffedByInternalRole . TryAdd ( id , data ) ;
104+ break ;
105+ case "CustomRole" or "CR"
106+ when int . TryParse ( elements [ 3 ] , out int id ) && UCR . TryGetCustomRole ( id , out _ ) :
107+ asCuffedByCustomRole . TryAdd ( id , data ) ;
108+ break ;
109+ default :
110+ {
111+ bool okInt = int . TryParse ( elements [ 3 ] , out _ ) ;
112+ LogManager . Warn (
113+ $ "Function SpawnManager::ParseEscapeRole[2](<...>) failed!\n Possible causes can be:\n - The source is not valid. Allowed: InternalTeam / IT / CustomRole / CR. Found: { elements [ 2 ] } \n - The target is not a CustomRole / InternalRole. Found: { elements [ 3 ] } (int32 parsable: { okInt } )") ;
114+ break ;
115+ }
83116 }
84117 }
85118 }
@@ -88,19 +121,54 @@ public class EscapeManager
88121 // Now let's assign
89122 if ( ! player . IsDisarmed )
90123 return defaultValue ;
91- if ( player . IsDisarmed && player . DisarmedBy is not null )
92- //if (player.DisarmedBy.TryGetSummonedInstance(out SummonedCustomRole role) && asCuffedByCustomRole.ContainsKey(role.Role.Id))
93- // return asCuffedByCustomRole[role.Role.Id];
94- //else
95- if ( asCuffedByInternalTeam . ContainsKey ( player . DisarmedBy . Team ) )
96- return asCuffedByInternalTeam [ player . DisarmedBy . Team ] ;
124+ LogManager . Debug ( $ "Player { player . PlayerId } is disarmed by { player . DisarmedBy ? . Team } - { player . DisarmedBy ? . Role } ") ;
125+ if ( player is { IsDisarmed : true , DisarmedBy : not null } )
126+ {
127+ // Try custom role via reflection first
128+ if ( UCR . TryGetSummonedCustomRole ( player . DisarmedBy , out object summoned ) )
129+ {
130+ int ? customRoleId = UCR . GetSummonedCustomRoleId ( summoned ) ;
131+ if ( customRoleId is not null && asCuffedByCustomRole . TryGetValue ( customRoleId . Value , out KeyValuePair < bool , object ? > ? escapeRole ) && escapeRole is not null )
132+ {
133+ LogManager . Debug ( $ "Player { player . PlayerId } disarmed by custom role { customRoleId } , applying mapped escape outcome.") ;
134+ return escapeRole ;
135+ }
136+ }
137+
138+ // Then try internal role
139+ if ( asCuffedByInternalRole . TryGetValue ( player . DisarmedBy . Role , out KeyValuePair < bool , object ? > ? roleValue ) && roleValue is not null )
140+ return roleValue ;
141+
142+ if ( asCuffedByInternalTeam . TryGetValue ( player . DisarmedBy . Team , out KeyValuePair < bool , object ? > ? teamValue ) && teamValue is not null )
143+ return teamValue ;
144+ }
97145
98146 LogManager . Debug (
99147 $ "Returing default type for escaping evaluation of player { player . PlayerId } who's cuffed by { player . DisarmedBy ? . Team } ") ;
100148 return defaultValue ;
149+
150+ // Local function to resolve entries with case-insensitive keys
151+ List < Dictionary < string , string > > ? ResolveEntries (
152+ Dictionary < string , List < Dictionary < string , string > > > source ,
153+ params string [ ] keys )
154+ {
155+ foreach ( string key in keys )
156+ {
157+ LogManager . Debug ( $ "Attempting to resolve entries for key: { key } ") ;
158+ if ( source . TryGetValue ( key , out List < Dictionary < string , string > > ? value ) )
159+ return value ;
160+
161+ string ? ciMatch = source . Keys . FirstOrDefault ( k =>
162+ string . Equals ( k , key , StringComparison . OrdinalIgnoreCase ) ) ;
163+
164+ if ( ciMatch is not null )
165+ return source [ ciMatch ] ;
166+ }
167+ return null ;
168+ }
101169 }
102170
103- public static KeyValuePair < bool , object ? > ? ParseEscapeString ( string escape )
171+ private static KeyValuePair < bool , object ? > ? ParseEscapeString ( string escape )
104172 {
105173 if ( escape is "Deny" or "deny" or "DENY" )
106174 return null ;
@@ -113,10 +181,15 @@ public class EscapeManager
113181 return new KeyValuePair < bool , object ? > ( false , RoleTypeId . Spectator ) ;
114182 }
115183
116- if ( elements [ 0 ] is "CustomRole" || elements [ 0 ] is "CR" )
117- return new KeyValuePair < bool , object ? > ( true , int . Parse ( elements [ 1 ] ) ) ;
118- if ( ( elements [ 0 ] is "InternalRole" || elements [ 0 ] is "IR" ) && Enum . TryParse ( elements [ 1 ] , out RoleTypeId role ) )
119- return new KeyValuePair < bool , object ? > ( false , role ) ;
184+ switch ( elements [ 0 ] )
185+ {
186+ case "CustomRole" :
187+ case "CR" :
188+ return new KeyValuePair < bool , object ? > ( true , int . Parse ( elements [ 1 ] ) ) ;
189+ case "InternalRole" or "IR" when Enum . TryParse ( elements [ 1 ] , out RoleTypeId role ) :
190+ return new KeyValuePair < bool , object ? > ( false , role ) ;
191+ }
192+
120193 bool okInt = int . TryParse ( elements [ 1 ] , out _ ) ;
121194 LogManager . Warn (
122195 $ "Function SpawnManager::ParseEscapeString(string escape) failed!\n Possible causes can be:\n - The source is not valid. Allowed: InternalRole / IR / CustomRole / CR. Found: { elements [ 0 ] } \n - The target is not a CustomRole / InternalRole. Found: { elements [ 1 ] } (int32 parsable: { okInt } )") ;
0 commit comments