77using HarmonyLib ;
88using Multiplayer . API ;
99using RimWorld ;
10+ using UnityEngine ;
1011using Verse ;
1112
1213namespace Multiplayer . Compat
@@ -27,6 +28,7 @@ public VanillaPsycastsExpanded(ModContentPack mod)
2728 ( PatchPsysets , "Psysets" , false ) ,
2829 ( PatchPsyringDialog , "Psyring" , false ) ,
2930 ( RegisterSyncGizmos , "General gizmos" , true ) ,
31+ ( PatchAbilityToggle , "Ability toggle gizmo" , true ) ,
3032 ( PatchPsychicStatusGizmo , "Psychic status gizmo" , true ) ,
3133 ( PatchPsycasterHediff , "Skill point usage" , true ) ,
3234 ( PatchMotesAndFlecks , "Motes and flecks" , true ) ,
@@ -139,7 +141,7 @@ private static void PatchPsysets()
139141 MP . RegisterSyncMethod ( typeof ( VanillaPsycastsExpanded ) , nameof ( SyncEnsurePsysetExists ) ) ;
140142 MP . RegisterSyncWorker < PsysetRenameHolder > ( SyncPsysetRenameHolder ) ;
141143
142- var abilityDefType = AccessTools . TypeByName ( "VFECore .Abilities.AbilityDef" ) ;
144+ var abilityDefType = AccessTools . TypeByName ( "VEF .Abilities.AbilityDef" ) ;
143145 abilityDefHashSetType = typeof ( HashSet < > ) . MakeGenericType ( abilityDefType ) ;
144146 abilityDefHashSetAddMethod = MethodInvoker . GetHandler ( AccessTools . Method ( abilityDefHashSetType , "Add" ) ) ;
145147
@@ -422,6 +424,72 @@ private static void PatchPsycasterHediff()
422424
423425 #endregion
424426
427+ #region Ability toggle
428+
429+ // Command_AbilityToggle (Hurricane gizmo)
430+ private static AccessTools . FieldRef < object , object > commandAbilityFieldRef ;
431+ private static Type iAbilityToggleType ;
432+ private static PropertyInfo toggleToggleProperty ;
433+ private static AccessTools . FieldRef < object , Pawn > abilityPawnFieldRef ;
434+ private static AccessTools . FieldRef < object , object > abilityDefFieldRef ;
435+ private static Type compAbilitiesLocalType ;
436+ private static AccessTools . FieldRef < object , IEnumerable > learnedAbilitiesLocalField ;
437+
438+ private static void PatchAbilityToggle ( )
439+ {
440+ var commandAbilityToggleType = AccessTools . TypeByName ( "VanillaPsycastsExpanded.Staticlord.Command_AbilityToggle" ) ;
441+ var commandAbilityType = AccessTools . TypeByName ( "VEF.Abilities.Command_Ability" ) ;
442+
443+ commandAbilityFieldRef = AccessTools . FieldRefAccess < object > ( commandAbilityType , "ability" ) ;
444+
445+ iAbilityToggleType = AccessTools . TypeByName ( "VanillaPsycastsExpanded.Staticlord.IAbilityToggle" ) ;
446+ toggleToggleProperty = AccessTools . Property ( iAbilityToggleType , "Toggle" ) ;
447+
448+ var abilityType = AccessTools . TypeByName ( "VEF.Abilities.Ability" ) ;
449+ abilityPawnFieldRef = AccessTools . FieldRefAccess < Pawn > ( abilityType , "pawn" ) ;
450+ abilityDefFieldRef = AccessTools . FieldRefAccess < object > ( abilityType , "def" ) ;
451+
452+ compAbilitiesLocalType = AccessTools . TypeByName ( "VEF.Abilities.CompAbilities" ) ;
453+ learnedAbilitiesLocalField = AccessTools . FieldRefAccess < IEnumerable > ( compAbilitiesLocalType , "learnedAbilities" ) ;
454+
455+ MP . RegisterSyncMethod ( typeof ( VanillaPsycastsExpanded ) , nameof ( SyncedAbilityToggle ) ) ;
456+
457+ MpCompat . harmony . Patch ( AccessTools . Method ( commandAbilityToggleType , "ProcessInput" ) ,
458+ prefix : new HarmonyMethod ( typeof ( VanillaPsycastsExpanded ) , nameof ( PreAbilityToggleProcessInput ) ) ) ;
459+ }
460+
461+ private static bool PreAbilityToggleProcessInput ( object __instance )
462+ {
463+ if ( ! MP . IsInMultiplayer )
464+ return true ;
465+
466+ var ability = commandAbilityFieldRef ( __instance ) ;
467+ var pawn = abilityPawnFieldRef ( ability ) ;
468+ var def = ( Def ) abilityDefFieldRef ( ability ) ;
469+
470+ SyncedAbilityToggle ( pawn , def ) ;
471+ return false ;
472+ }
473+
474+ private static void SyncedAbilityToggle ( Pawn pawn , Def abilityDef )
475+ {
476+ var comp = pawn . AllComps . FirstOrDefault ( c => compAbilitiesLocalType . IsInstanceOfType ( c ) ) ;
477+ if ( comp == null )
478+ return ;
479+
480+ foreach ( var ability in learnedAbilitiesLocalField ( comp ) )
481+ {
482+ if ( ( Def ) abilityDefFieldRef ( ability ) == abilityDef && iAbilityToggleType . IsInstanceOfType ( ability ) )
483+ {
484+ var current = ( bool ) toggleToggleProperty . GetValue ( ability ) ;
485+ toggleToggleProperty . SetValue ( ability , ! current ) ;
486+ break ;
487+ }
488+ }
489+ }
490+
491+ #endregion
492+
425493 #region Other
426494
427495 private static void RegisterSyncGizmos ( )
@@ -442,6 +510,36 @@ private static void PatchMotesAndFlecks()
442510 "VanillaPsycastsExpanded.FixedTemperatureZone:ThrowFleck" ,
443511 "VanillaPsycastsExpanded.Conflagrator.FireTornado:ThrowPuff" ,
444512 } ) ;
513+
514+ // FleckMaker.ThrowSmoke in tick methods uses RNG after visibility check
515+ var transpiler = new HarmonyMethod ( typeof ( VanillaPsycastsExpanded ) , nameof ( TranspileThrowSmokeRandFix ) ) ;
516+ MpCompat . harmony . Patch (
517+ AccessTools . Method ( "VanillaPsycastsExpanded.Staticlord.Vortex:Tick" ) ,
518+ transpiler : transpiler ) ;
519+ MpCompat . harmony . Patch (
520+ AccessTools . Method ( "VanillaPsycastsExpanded.Staticlord.GameCondition_PsychicFlashstorm:GameConditionTick" ) ,
521+ transpiler : transpiler ) ;
522+ }
523+
524+ private static IEnumerable < CodeInstruction > TranspileThrowSmokeRandFix ( IEnumerable < CodeInstruction > instr )
525+ {
526+ var target = AccessTools . Method ( typeof ( FleckMaker ) , nameof ( FleckMaker . ThrowSmoke ) ,
527+ [ typeof ( Vector3 ) , typeof ( Map ) , typeof ( float ) ] ) ;
528+ var replacement = AccessTools . Method ( typeof ( VanillaPsycastsExpanded ) , nameof ( ThrowSmokeRandSafe ) ) ;
529+
530+ foreach ( var ci in instr )
531+ {
532+ if ( ci . operand is MethodInfo method && method == target )
533+ ci . operand = replacement ;
534+ yield return ci ;
535+ }
536+ }
537+
538+ private static void ThrowSmokeRandSafe ( Vector3 loc , Map map , float size )
539+ {
540+ Rand . PushState ( ) ;
541+ FleckMaker . ThrowSmoke ( loc , map , size ) ;
542+ Rand . PopState ( ) ;
445543 }
446544
447545 private static void PatchITab ( )
0 commit comments