@@ -17,11 +17,12 @@ internal class SpectateEnemies : MonoBehaviour
1717 private static readonly Dictionary < string , bool > settings = [ ] ;
1818
1919 private bool WindowOpen = false ;
20- private Rect window = new ( 10 , 10 , 500 , 300 ) ;
20+ private Rect window = new ( 10 , 10 , 500 , 400 ) ;
2121
2222 public int SpectatedEnemyIndex = - 1 ;
2323 public bool SpectatingEnemies = false ;
2424 public Spectatable [ ] SpectatorList ;
25+ public float zoomLevel = 1f ;
2526
2627 private void Awake ( )
2728 {
@@ -39,6 +40,8 @@ private void SetupKeybinds()
3940 Plugin . Inputs . SwapKey . performed += OnSwapKeyPressed ;
4041 Plugin . Inputs . MenuKey . performed += OnMenuKeyPressed ;
4142 Plugin . Inputs . FlashlightKey . performed += OnFlashlightKeyPressed ;
43+ Plugin . Inputs . ZoomOutKey . performed += OnZoomOutPressed ;
44+ Plugin . Inputs . ZoomInKey . performed += OnZoomInPressed ;
4245 }
4346
4447 private void OnSwapKeyPressed ( InputAction . CallbackContext context )
@@ -85,6 +88,24 @@ private void OnFlashlightKeyPressed(InputAction.CallbackContext context)
8588 }
8689 }
8790
91+ private void OnZoomOutPressed ( InputAction . CallbackContext context )
92+ {
93+ if ( ! context . performed ) return ;
94+ if ( ! SpectatingEnemies ) return ;
95+ zoomLevel += 0.1f ;
96+ if ( zoomLevel > 10f )
97+ zoomLevel = 10f ;
98+ }
99+
100+ private void OnZoomInPressed ( InputAction . CallbackContext context )
101+ {
102+ if ( ! context . performed ) return ;
103+ if ( ! SpectatingEnemies ) return ;
104+ zoomLevel -= 0.1f ;
105+ if ( zoomLevel < 1f )
106+ zoomLevel = 1f ;
107+ }
108+
88109 private void LateUpdate ( )
89110 {
90111 if ( SpectatingEnemies )
@@ -94,17 +115,20 @@ private void LateUpdate()
94115 SpectatingEnemies = false ;
95116 return ;
96117 }
97- if ( SpectatedEnemyIndex >= SpectatorList . Length )
98- {
99- GetNextValidSpectatable ( ) ;
100- return ;
101- }
102118 Spectatable currentEnemy = SpectatorList . ElementAtOrDefault ( SpectatedEnemyIndex ) ;
103119 if ( currentEnemy == null )
104120 {
105121 GetNextValidSpectatable ( ) ;
106122 return ;
107123 }
124+ if ( currentEnemy . type == SpectatableType . Enemy || currentEnemy . type == SpectatableType . Masked )
125+ {
126+ if ( currentEnemy . enemyInstance != null && currentEnemy . enemyInstance . isEnemyDead )
127+ {
128+ GetNextValidSpectatable ( ) ;
129+ return ;
130+ }
131+ }
108132 Vector3 ? position = GetSpectatePosition ( currentEnemy ) ;
109133 if ( ! position . HasValue )
110134 {
@@ -115,20 +139,20 @@ private void LateUpdate()
115139 {
116140 TryFixName ( ref currentEnemy ) ;
117141 }
118- HUDManager . Instance . localPlayer . spectateCameraPivot . position = position . Value + GetZoomDistance ( currentEnemy ) ;
142+ GameNetworkManager . Instance . localPlayerController . spectateCameraPivot . position = position . Value + Vector3 . up * zoomLevel ;
119143 if ( currentEnemy . type == SpectatableType . Masked && currentEnemy . maskedName != string . Empty )
120- HUDManager . Instance . spectatingPlayerText . text = "(Spectating: " + currentEnemy . maskedName + ")" ;
144+ HUDManager . Instance . spectatingPlayerText . text = string . Format ( "(Spectating: {0}) [{1:F1}x]" , currentEnemy . maskedName , zoomLevel ) ;
121145 else
122- HUDManager . Instance . spectatingPlayerText . text = "(Spectating: " + currentEnemy . enemyName + ")" ;
123- Plugin . raycastSpectate . Invoke ( HUDManager . Instance . localPlayer , [ ] ) ;
146+ HUDManager . Instance . spectatingPlayerText . text = string . Format ( "(Spectating: {0}) [{1:F1}x]" , currentEnemy . enemyName , zoomLevel ) ;
147+ Plugin . raycastSpectate . Invoke ( GameNetworkManager . Instance . localPlayerController , [ ] ) ;
124148 }
125149 }
126150
127151 private Vector3 ? GetSpectatePosition ( Spectatable obj )
128152 {
129153 if ( obj . type == SpectatableType . Enemy || obj . type == SpectatableType . Masked )
130154 {
131- EnemyAI enemy = obj . GetComponent < EnemyAI > ( ) ;
155+ EnemyAI enemy = obj . enemyInstance ;
132156 if ( enemy != null )
133157 {
134158 return enemy . eye == null ? enemy . transform . position : enemy . eye . position ;
@@ -153,16 +177,6 @@ private void LateUpdate()
153177 return null ;
154178 }
155179
156- private Vector3 GetZoomDistance ( Spectatable obj )
157- {
158- if ( obj . enemyName == "ForestGiant" )
159- return Vector3 . up * 3 ;
160- if ( obj . enemyName == "MouthDog" || obj . enemyName == "Jester" )
161- return Vector3 . up * 2 ;
162- else
163- return Vector3 . up ;
164- }
165-
166180 private void TryFixName ( ref Spectatable obj )
167181 {
168182 if ( obj . gameObject . TryGetComponent ( out MaskedPlayerEnemy masked ) )
@@ -195,7 +209,7 @@ public void ToggleSpectatingMode(PlayerControllerB __instance)
195209 SpectatingEnemies = ! SpectatingEnemies ;
196210 if ( SpectatingEnemies )
197211 {
198- SpectatorList = FindObjectsByType < Spectatable > ( FindObjectsSortMode . None ) ;
212+ SpectatorList = FindObjectsByType < Spectatable > ( FindObjectsSortMode . None ) . Where ( x => settings [ x . enemyName ] ) . ToArray ( ) ;
199213 if ( SpectatorList . Length == 0 )
200214 {
201215 SpectatingEnemies = false ;
@@ -210,17 +224,11 @@ public void ToggleSpectatingMode(PlayerControllerB __instance)
210224 }
211225 else
212226 {
213- List < Spectatable > matches = SpectatorList . Where ( x => settings [ x . enemyName ] ) . ToList ( ) ;
214- if ( matches . Count == 0 )
215- {
216- Plugin . displaySpectatorTip . Invoke ( HUDManager . Instance , [ "No enemies to spectate" ] ) ;
217- return ;
218- }
219227 float closest = 999999f ;
220228 int index = 0 ;
221- for ( int i = 0 ; i < matches . Count ; i ++ )
229+ for ( int i = 0 ; i < SpectatorList . Length ; i ++ )
222230 {
223- float dist = ( matches [ i ] . transform . position - __instance . spectatedPlayerScript . transform . position ) . sqrMagnitude ;
231+ float dist = ( SpectatorList [ i ] . transform . position - __instance . spectatedPlayerScript . transform . position ) . sqrMagnitude ;
224232 if ( dist < closest * closest )
225233 {
226234 closest = dist ;
@@ -230,36 +238,25 @@ public void ToggleSpectatingMode(PlayerControllerB __instance)
230238 SpectatedEnemyIndex = index ;
231239 }
232240 }
233- else
234- {
235- if ( ! settings [ SpectatorList [ SpectatedEnemyIndex ] . enemyName ] )
236- {
237- GetNextValidSpectatable ( ) ;
238- }
239- }
240241 __instance . spectatedPlayerScript = null ;
241242 }
242243 else
243244 {
244245 __instance . spectatedPlayerScript = __instance . playersManager . allPlayerScripts . FirstOrDefault ( x => ! x . isPlayerDead && x . isPlayerControlled ) ;
245- HUDManager . Instance . spectatingPlayerText . text = "(Spectating: " + __instance . spectatedPlayerScript . playerUsername + " )";
246+ HUDManager . Instance . spectatingPlayerText . text = $ "(Spectating: { __instance . spectatedPlayerScript . playerUsername } )";
246247 }
247248 }
248249
249- public void PopulateSettings ( SelectableLevel level )
250+ public void PopulateSettings ( )
250251 {
251- foreach ( SpawnableEnemyWithRarity t in level . Enemies )
252- {
253- settings . TryAdd ( t . enemyType . enemyName , true ) ;
254- }
255- foreach ( SpawnableEnemyWithRarity t in level . OutsideEnemies )
252+ EnemyType [ ] allEnemies = Resources . FindObjectsOfTypeAll < EnemyType > ( ) ;
253+ foreach ( EnemyType type in allEnemies )
256254 {
257- settings . TryAdd ( t . enemyType . enemyName , true ) ;
258- }
259- foreach ( SpawnableEnemyWithRarity t in level . DaytimeEnemies )
260- {
261- settings . TryAdd ( t . enemyType . enemyName , false ) ;
255+ if ( type . enemyName == "Red pill" || type . enemyName == "Lasso" )
256+ continue ;
257+ settings . TryAdd ( type . enemyName , ! type . isDaytimeEnemy ) ;
262258 }
259+
263260 settings . TryAdd ( "Landmine" , false ) ;
264261 settings . TryAdd ( "Turret" , false ) ;
265262
@@ -278,9 +275,10 @@ public void PopulateSettings(SelectableLevel level)
278275 {
279276 string [ ] c = s . Split ( ':' ) ;
280277 if ( c . Length != 2 ) continue ;
281- if ( settings . ContainsKey ( c [ 0 ] ) )
278+ if ( settings . ContainsKey ( c [ 0 ] ) ) {
282279 if ( bool . TryParse ( c [ 1 ] , out bool value ) )
283280 settings [ c [ 0 ] ] = value ;
281+ }
284282 }
285283 Debug . LogWarning ( "[SpectateEnemies]: Config loaded" ) ;
286284 }
@@ -292,9 +290,9 @@ public void PopulateSettings(SelectableLevel level)
292290 catch ( Exception )
293291 {
294292 Debug . LogWarning ( "[SpectateEnemies]: Config failed to load, using default values!" ) ;
295- }
293+ }
296294
297- // AssertSettings();
295+ AssertSettings ( ) ;
298296 }
299297
300298 private void OnApplicationQuit ( )
@@ -324,6 +322,7 @@ public bool SpectateNextEnemy()
324322
325323 private void GetNextValidSpectatable ( )
326324 {
325+ SpectatorList = FindObjectsByType < Spectatable > ( FindObjectsSortMode . None ) . Where ( x => settings [ x . enemyName ] ) . ToArray ( ) ;
327326 int enemiesChecked = 0 ;
328327 int current = SpectatedEnemyIndex ;
329328 while ( enemiesChecked < SpectatorList . Length )
@@ -333,10 +332,21 @@ private void GetNextValidSpectatable()
333332 {
334333 current = 0 ;
335334 }
336- if ( settings [ SpectatorList [ current ] . enemyName ] )
335+ Spectatable enemy = SpectatorList . ElementAtOrDefault ( current ) ;
336+ if ( enemy != null )
337337 {
338- SpectatedEnemyIndex = current ;
339- return ;
338+ if ( enemy . type == SpectatableType . Enemy || enemy . type == SpectatableType . Masked )
339+ {
340+ if ( enemy . enemyInstance != null && enemy . enemyInstance . isEnemyDead )
341+ {
342+ continue ;
343+ }
344+ }
345+ if ( settings . ContainsKey ( enemy . enemyName ) )
346+ {
347+ SpectatedEnemyIndex = current ;
348+ return ;
349+ }
340350 }
341351 enemiesChecked ++ ;
342352 }
0 commit comments