1+ using Microsoft . Xna . Framework ;
2+ using Mono . Cecil . Cil ;
3+ using Monocle ;
4+ using MonoMod . Cil ;
5+ using MonoMod . RuntimeDetour ;
6+
7+ namespace Celeste . Mod . TASHelper . Gameplay ;
8+ internal static class BetterSpeedrunTimerDisplay {
9+
10+ [ Load ]
11+
12+ private static void Load ( ) {
13+ using ( new DetourContext { Before = new List < string > { "*" } , ID = "TAS Helper BetterSpeedrunTimerDisplay" } ) {
14+ IL . Celeste . SpeedrunTimerDisplay . Render += ILSpeedrunTimerDisplayRender ;
15+ }
16+ }
17+
18+ private static void Unload ( ) {
19+ IL . Celeste . SpeedrunTimerDisplay . Render -= ILSpeedrunTimerDisplayRender ;
20+ }
21+
22+ private static void ILSpeedrunTimerDisplayRender ( ILContext il ) {
23+ ILCursor cursor = new ILCursor ( il ) ;
24+
25+ if ( ! cursor . TryGotoNext ( MoveType . After , ins => ins . OpCode == OpCodes . Ret ) || cursor . Next is null ) {
26+ return ;
27+ }
28+ VariableDefinition alpha = new VariableDefinition ( il . Import ( typeof ( float ) ) ) ;
29+ il . Body . Variables . Add ( alpha ) ;
30+
31+ cursor . MoveAfterLabels ( ) ;
32+ cursor . Emit ( OpCodes . Ldarg_0 ) ;
33+ cursor . Emit ( OpCodes . Ldloc , alpha ) ;
34+ cursor . EmitDelegate ( UpdateAlpha ) ;
35+ cursor . Emit ( OpCodes . Stloc , alpha ) ;
36+
37+ Instruction head = cursor . Next ;
38+
39+ while ( cursor . TryGotoNext ( MoveType . After , ins => ins . MatchCall ( typeof ( Color ) , "get_Black" ) ) ) {
40+ ColorMulWithAlpha ( ) ;
41+ }
42+
43+ cursor . Goto ( head ) ;
44+ while ( cursor . TryGotoNext ( MoveType . Before ,
45+ ins => ins . MatchCallvirt (
46+ typeof ( MTexture ) . GetMethod ( "Draw" , new Type [ ] { typeof ( Vector2 ) } ) ) )
47+ ) {
48+ cursor . Remove ( ) ;
49+ cursor . Emit ( OpCodes . Call , typeof ( Vector2 ) . GetMethod ( "get_Zero" ) ) ;
50+ cursor . Emit ( OpCodes . Call , typeof ( Color ) . GetMethod ( "get_White" ) ) ;
51+ cursor . Emit ( OpCodes . Callvirt , typeof ( MTexture ) . GetMethod ( "Draw" , new Type [ ] { typeof ( Vector2 ) , typeof ( Vector2 ) , typeof ( Color ) } ) ) ;
52+ cursor . Index ++ ;
53+ }
54+
55+ cursor . Goto ( head ) ;
56+ while ( cursor . TryGotoNext ( MoveType . After , ins => ins . MatchCall ( typeof ( Color ) , "get_White" ) ) ) {
57+ ColorMulWithAlpha ( ) ;
58+ }
59+
60+ cursor . Goto ( head ) ;
61+ while ( cursor . TryGotoNext ( MoveType . Before , ins => ins . MatchCall ( typeof ( SpeedrunTimerDisplay ) , nameof ( SpeedrunTimerDisplay . DrawTime ) ) ) ) {
62+ FloatMulWithAlpha ( ) ;
63+ cursor . Index ++ ;
64+ }
65+
66+ void FloatMulWithAlpha ( ) {
67+ cursor . Emit ( OpCodes . Ldloc , alpha ) ;
68+ cursor . Emit ( OpCodes . Mul ) ;
69+ }
70+
71+ void ColorMulWithAlpha ( ) {
72+ cursor . Emit ( OpCodes . Ldloc , alpha ) ;
73+ cursor . Emit ( OpCodes . Call , typeof ( Color ) . GetMethod ( nameof ( Color . Multiply ) ) ) ;
74+ }
75+ }
76+
77+ private static float UpdateAlpha ( SpeedrunTimerDisplay display , float alpha ) {
78+ if ( Settings . Instance . SpeedrunClock == SpeedrunType . Off || ! FrameStep ) {
79+ return 1f ;
80+ }
81+ return TasHelperSettings . SpeedrunTimerDisplayOpacityToFloat ;
82+ }
83+
84+ }
0 commit comments