77using System . Collections . Generic ;
88using System . IO ;
99using System . Linq ;
10+ using System . Reflection . Emit ;
1011using UnityEngine ;
1112using UnityEngine . UI ;
1213
@@ -21,6 +22,17 @@ public class Main : BaseUnityPlugin
2122
2223 internal static ManualLogSource StaticLogger ;
2324
25+ private static bool isSpawning = false ;
26+ private static MiCharacter . CharacterType optionUsableByCharType ;
27+ private static MiCharacter . CharacterType [ ] playerChars =
28+ {
29+ MiCharacter . CharacterType . Cooper ,
30+ MiCharacter . CharacterType . McCoy ,
31+ MiCharacter . CharacterType . Trapper ,
32+ MiCharacter . CharacterType . Kate ,
33+ MiCharacter . CharacterType . Voodoo ,
34+ } ;
35+
2436 public ConfigEntry < KeyboardShortcut > ShowHotkey { get ; private set ; }
2537 public ConfigEntry < KeyboardShortcut > SpawnHotkey { get ; private set ; }
2638 private ConfigEntry < Favorites > FavoritesConfig { get ; set ; }
@@ -43,11 +55,14 @@ public class Main : BaseUnityPlugin
4355 public Main ( )
4456 {
4557 _windowId = GetHashCode ( ) ;
58+ optionUsableByCharType = playerChars . Aggregate ( ( a , b ) => a | b ) ;
4659 }
4760
4861 private bool _show = false ;
49- public bool Show {
50- get => _show ; set {
62+ public bool Show
63+ {
64+ get => _show ; set
65+ {
5166 if ( Show != value )
5267 {
5368 _show = value ;
@@ -64,7 +79,7 @@ public bool Show {
6479 panel . transform . SetParent ( _clickBlockerCanvas . transform ) ;
6580 _clickBlockerRect = panel . GetComponent < RectTransform > ( ) ;
6681 UpdateClickBlockerSize ( ) ;
67- panel . GetComponent < Image > ( ) . color = new Color ( 0 , 0 , 0 , 0.3f ) ;
82+ panel . GetComponent < Image > ( ) . color = new Color ( 0 , 0 , 0 , 0.8f ) ;
6883 }
6984 else
7085 {
@@ -113,19 +128,25 @@ private void Awake()
113128 error = "Failed to load 'spawn_codes.txt': " + e ;
114129 Logger . LogError ( error ) ;
115130 }
131+
132+ Harmony . CreateAndPatchAll ( typeof ( Patch ) ) ;
116133 }
117134
118135 private void Update ( )
119136 {
120137 if ( Input . GetKeyDown ( ShowHotkey . Value . MainKey ) )
138+ {
121139 Show = ! Show ;
140+ }
122141
123142 if ( Input . GetKeyDown ( SpawnHotkey . Value . MainKey ) && selectedCode != - 1 && MiGameInput . instance . iPlayerCharacterCount > 0 )
124143 {
125144 var inputs = AccessTools . Field ( typeof ( UIManager ) , "m_dicPlayerInputs" ) . GetValue ( UIManager . instance ) as Dictionary < GameUser , MiPlayerInput > ;
126145 foreach ( var input in inputs . Values )
127146 {
147+ isSpawning = true ;
128148 input . devSpawn ( selectedCode / 100 , selectedCode % 100 ) ;
149+ isSpawning = false ;
129150 break ;
130151 }
131152 }
@@ -180,17 +201,34 @@ private void DisplayWindow(int id)
180201 GUILayout . EndHorizontal ( ) ;
181202 }
182203 GUILayout . EndScrollView ( ) ;
204+ DrawOptions ( ) ;
183205 GUILayout . EndVertical ( ) ;
184206
185207 GUI . DragWindow ( ) ;
186208 UpdateClickBlockerSize ( ) ;
187209 }
188210
211+ private void DrawOptions ( )
212+ {
213+ // TODO: Enemy health?
214+ if ( selectedCode == 6118 )
215+ {
216+ GUILayout . BeginVertical ( "box" ) ;
217+ foreach ( var c in playerChars )
218+ {
219+ var isActive = ( optionUsableByCharType & c ) != 0 ;
220+ var shouldActive = GUILayout . Toggle ( isActive , c . ToNiceString ( ) ) ;
221+ if ( shouldActive != isActive ) optionUsableByCharType ^= c ;
222+ }
223+ GUILayout . EndVertical ( ) ;
224+ }
225+ }
226+
189227 private void UpdateClickBlockerSize ( )
190228 {
191229 if ( _clickBlockerRect == null ) return ;
192230 _clickBlockerRect . anchorMin = new Vector2 ( WindowRect . x / Screen . width , 1 - WindowRect . y / Screen . height ) ;
193- _clickBlockerRect . anchorMax = new Vector2 ( WindowRect . xMax / Screen . width , 1 - WindowRect . yMax / Screen . height ) ;
231+ _clickBlockerRect . anchorMax = new Vector2 ( WindowRect . xMax / Screen . width , 1 - WindowRect . yMax / Screen . height ) ;
194232 _clickBlockerRect . offsetMin = Vector2 . zero ;
195233 _clickBlockerRect . offsetMax = Vector2 . zero ;
196234 _clickBlockerRect . offsetMax = Vector2 . zero ;
@@ -215,6 +253,34 @@ private bool IsFavorite(int code)
215253 {
216254 return favorites . Set . Contains ( code ) ;
217255 }
256+
257+ public static void PostSpawnUsable ( GameObject go )
258+ {
259+ if ( ! isSpawning ) return ;
260+ MiUsable usable = go . GetComponent < MiUsable > ( ) ;
261+ usable . m_eCanUsedBy = optionUsableByCharType ;
262+ }
263+ }
264+
265+ [ HarmonyPatch ]
266+ class Patch
267+ {
268+ [ HarmonyTranspiler ]
269+ [ HarmonyPatch ( typeof ( MiGameInput ) , nameof ( MiGameInput . devSpawnUsable ) ) ]
270+ public static IEnumerable < CodeInstruction > MiGameInput_devSpawnUsable_Transpiler ( IEnumerable < CodeInstruction > instructions )
271+ {
272+ return new CodeMatcher ( instructions )
273+ . MatchForward ( false ,
274+ new CodeMatch ( OpCodes . Ldc_I4_1 ) ,
275+ new CodeMatch ( OpCodes . Ret ) )
276+ . Repeat ( matcher => matcher
277+ . InsertAndAdvance (
278+ new CodeInstruction ( OpCodes . Ldloc_1 ) ,
279+ new CodeInstruction ( OpCodes . Call , AccessTools . Method ( typeof ( Main ) , nameof ( Main . PostSpawnUsable ) ) ) )
280+ . Advance ( 2 )
281+ )
282+ . InstructionEnumeration ( ) ;
283+ }
218284 }
219285
220286 public struct SpawnCode
@@ -252,4 +318,20 @@ public Favorites(List<int> favorites)
252318 Set = new HashSet < int > ( List ) ;
253319 }
254320 }
321+
322+ static class Extensions
323+ {
324+ public static String ToNiceString ( this MiCharacter . CharacterType c )
325+ {
326+ switch ( c )
327+ {
328+ case MiCharacter . CharacterType . Cooper : return "Cooper" ;
329+ case MiCharacter . CharacterType . McCoy : return "McCoy" ;
330+ case MiCharacter . CharacterType . Trapper : return "Hector" ;
331+ case MiCharacter . CharacterType . Kate : return "Kate" ;
332+ case MiCharacter . CharacterType . Voodoo : return "Isabelle" ;
333+ default : return "Non-player character" ;
334+ }
335+ }
336+ }
255337}
0 commit comments