1+ using System . Reflection ;
2+ using System . Reflection . Emit ;
3+ using HarmonyLib ;
14using MegaCrit . Sts2 . Core . Models ;
25using MegaCrit . Sts2 . Core . Nodes . Screens . RunHistoryScreen ;
36using MegaCrit . Sts2 . Core . Rooms ;
710
811namespace STS2RitsuLib . Lifecycle . Patches
912{
10- internal static class RunHistoryMissingModelScope
11- {
12- [ ThreadStatic ] private static int _depth ;
13-
14- internal static bool IsActive => _depth > 0 ;
15-
16- internal static void Enter ( )
17- {
18- _depth ++ ;
19- }
20-
21- internal static void Exit ( )
22- {
23- if ( _depth > 0 )
24- _depth -- ;
25- }
26- }
27-
2813 /// <summary>
29- /// Creates an execution scope for run-history UI methods that may read missing mod models.
14+ /// Replaces <c>ModelDb.GetById<CharacterModel></c> and <c>GetById<ActModel></c> in run-history UI with
15+ /// <see cref="RunHistoryMissingModelSupport" /> so missing mod content does not throw.
3016 /// </summary>
3117 public class RunHistoryMissingModelDbGetByIdTranspilerPatch : IPatchMethod
3218 {
19+ private static readonly MethodInfo CharacterFallback =
20+ AccessTools . DeclaredMethod ( typeof ( RunHistoryMissingModelSupport ) ,
21+ nameof ( RunHistoryMissingModelSupport . CharacterForRunHistory ) ) ;
22+
23+ private static readonly MethodInfo ActFallback =
24+ AccessTools . DeclaredMethod ( typeof ( RunHistoryMissingModelSupport ) ,
25+ nameof ( RunHistoryMissingModelSupport . ActForRunHistory ) ) ;
26+
3327 /// <inheritdoc />
3428 public static string PatchId => "run_history_missing_model_db_getbyid_transpile" ;
3529
3630 /// <inheritdoc />
3731 public static string Description =>
38- "Create run-history scope for missing-model fallbacks" ;
32+ "Transpile run-history methods to use Character/Act fallbacks when ModelDb has no entry " ;
3933
4034 /// <inheritdoc />
4135 public static bool IsCritical => false ;
@@ -58,61 +52,41 @@ public static ModPatchTarget[] GetTargets()
5852 }
5953
6054 /// <summary>
61- /// Enters run-history missing-model support scope.
62- /// </summary>
63- public static void Prefix ( )
64- {
65- RunHistoryMissingModelScope . Enter ( ) ;
66- }
67-
68- /// <summary>
69- /// Exits run-history scope even if the target method throws.
55+ /// Harmony transpiler: redirect ModelDb lookups to RitsuLib fallbacks.
7056 /// </summary>
71- public static void Finalizer ( )
57+ public static IEnumerable < CodeInstruction > Transpiler ( IEnumerable < CodeInstruction > instructions )
7258 {
73- RunHistoryMissingModelScope . Exit ( ) ;
74- }
75- }
76-
77- /// <summary>
78- /// Uses run-history-specific fallbacks for missing character/act lookups.
79- /// </summary>
80- public class RunHistoryMissingModelDbGetByIdPatch : IPatchMethod
81- {
82- /// <inheritdoc />
83- public static string PatchId => "run_history_missing_model_db_getbyid" ;
84-
85- /// <inheritdoc />
86- public static string Description =>
87- "Use Character/Act fallbacks in run-history scope when ModelDb.GetById has no entry" ;
88-
89- /// <inheritdoc />
90- public static bool IsCritical => false ;
91-
92- /// <inheritdoc />
93- public static ModPatchTarget [ ] GetTargets ( )
94- {
95- return [ new ( typeof ( ModelDb ) , nameof ( ModelDb . GetById ) , [ typeof ( ModelId ) ] ) ] ;
59+ foreach ( var code in instructions )
60+ {
61+ if ( code . operand is MethodInfo called )
62+ {
63+ if ( IsModelDbGetByIdFor ( called , typeof ( CharacterModel ) ) )
64+ {
65+ code . opcode = OpCodes . Call ;
66+ code . operand = CharacterFallback ;
67+ }
68+ else if ( IsModelDbGetByIdFor ( called , typeof ( ActModel ) ) )
69+ {
70+ code . opcode = OpCodes . Call ;
71+ code . operand = ActFallback ;
72+ }
73+ }
74+
75+ yield return code ;
76+ }
9677 }
9778
98- /// <summary>
99- /// Replaces vanilla GetById throws with run-history fallback models.
100- /// </summary>
101- // ReSharper disable once InconsistentNaming
102- public static bool Prefix < T > ( ModelId id , ref T __result ) where T : AbstractModel
79+ private static bool IsModelDbGetByIdFor ( MethodInfo mi , Type typeArg )
10380 {
104- if ( ! RunHistoryMissingModelScope . IsActive )
105- return true ;
81+ if ( ! mi . IsGenericMethod || mi . DeclaringType != typeof ( ModelDb ) )
82+ return false ;
10683
107- if ( typeof ( T ) == typeof ( CharacterModel ) )
108- {
109- __result = ( T ) ( AbstractModel ) RunHistoryMissingModelSupport . CharacterForRunHistory ( id ) ;
84+ var def = mi . GetGenericMethodDefinition ( ) ;
85+ if ( def . Name != nameof ( ModelDb . GetById ) )
11086 return false ;
111- }
11287
113- if ( typeof ( T ) != typeof ( ActModel ) ) return true ;
114- __result = ( T ) ( AbstractModel ) RunHistoryMissingModelSupport . ActForRunHistory ( id ) ;
115- return false ;
88+ var args = mi . GetGenericArguments ( ) ;
89+ return args . Length == 1 && args [ 0 ] == typeArg ;
11690 }
11791 }
11892}
0 commit comments