using HarmonyLib; using System.Reflection; using UnityEngine; using static ManifoldGardenArchipelago.Requirement; namespace ManifoldGardenArchipelago { [HarmonyPatch(typeof(LineDoorController), "SetDoorOpenCloseStateAnimated")] static class LineDoorControllerSetDoorOpenCloseStateAnimatedPatch { static bool Prefix(LineDoorController __instance, bool open) { SceneItemReference sir = GameState.GetChainListenerSceneReference(__instance); if (GameData.scenes.TryGetValue(sir.scene, out var sceneDescription)) { if (sceneDescription.doors.TryGetValue(sir.index, out var requirement)) { Requirement.Decision decision = requirement.Check(); if (decision != Requirement.Decision.Maybe) { bool shouldOpen = (decision == Requirement.Decision.Yes); if (open != shouldOpen) { return false; } } } } return true; } } [HarmonyPatch(typeof(ButtonLineActivator), "ToggleButtonPress")] static class ButtonLineActivatorToggleButtonPressPatch { static void Prefix(ButtonLineActivator __instance, bool setPressed) { SceneItemReference sir = GameState.GetButtonSceneReference(__instance); Plugin.Logger.LogInfo($"Button {sir} state {setPressed}"); if (!GameData.listenersByButton.TryGetValue(sir, out GameStateListeners listeners)) { return; } Plugin.slotSave.ActivatedButtons[sir] = setPressed; GameState.EvaluateGameStateListeners(listeners); } } [HarmonyPatch(typeof(ButtonLineActivator), "ForceButtonPressedImmediately")] static class ButtonLineActivatorForceButtonPressedImmediatelyPatch { static void Prefix(ButtonLineActivator __instance, bool _pressed) { SceneItemReference sir = GameState.GetButtonSceneReference(__instance); Plugin.Logger.LogInfo($"Button {sir} forced state {_pressed}"); if (!GameData.listenersByButton.TryGetValue(sir, out GameStateListeners listeners)) { return; } Plugin.slotSave.ActivatedButtons[sir] = _pressed; GameState.EvaluateGameStateListeners(listeners); } } [HarmonyPatch(typeof(CubeReceiverController), "SetActivatorActive")] static class CubeReceiverControllerSetActivatorActivePatch { static void Prefix(CubeReceiverController __instance, bool active) { SceneItemReference sir = GameState.GetSocketSceneReference(__instance); Plugin.Logger.LogInfo($"Socket {sir} state {active}"); if (!GameData.listenersBySocket.TryGetValue(sir, out GameStateListeners listeners)) { return; } Plugin.slotSave.ActivatedSockets[sir] = active; GameState.EvaluateGameStateListeners(listeners); } } [HarmonyPatch(typeof(CubeLineActivator), "SetActivatorActive")] static class CubeLineActivatorSetActivatorActivePatch { static void Prefix(CubeLineActivator __instance, bool setActive) { SceneItemReference sir = GameState.GetPadSceneReference(__instance); Plugin.Logger.LogInfo($"Pad {sir} state {setActive}"); if (!GameData.listenersByPad.TryGetValue(sir, out GameStateListeners listeners)) { return; } Plugin.slotSave.ActivatedPads[sir] = setActive; GameState.EvaluateGameStateListeners(listeners); } } [HarmonyPatch(typeof(CubeLineActivator), "ForceActivatorToTurnOnAsCorrectCubeOnSwitch")] static class CubeLineActivatorForceActivatorToTurnOnAsCorrectCubeOnSwitchPatch { static void Prefix(CubeLineActivator __instance) { SceneItemReference sir = GameState.GetPadSceneReference(__instance); Plugin.Logger.LogInfo($"Pad {sir} forced on"); if (!GameData.listenersByPad.TryGetValue(sir, out GameStateListeners listeners)) { return; } Plugin.slotSave.ActivatedPads[sir] = true; GameState.EvaluateGameStateListeners(listeners); } } [HarmonyPatch(typeof(CubeLineActivator), "ForceActivatorToTurnOffAsCorrectCubeNotOnSwitch")] static class CubeLineActivatorForceActivatorToTurnOffAsCorrectCubeNotOnSwitchPatch { static void Prefix(CubeLineActivator __instance) { SceneItemReference sir = GameState.GetPadSceneReference(__instance); Plugin.Logger.LogInfo($"Pad {sir} forced off"); if (!GameData.listenersByPad.TryGetValue(sir, out GameStateListeners listeners)) { return; } Plugin.slotSave.ActivatedPads[sir] = false; GameState.EvaluateGameStateListeners(listeners); } } [HarmonyPatch(typeof(WaterDetector), "SetActivatorActive")] static class WaterDetectorSetActivatorActivePatch { static void Prefix(WaterDetector __instance, bool setActive) { SceneItemReference sir = GameState.GetWaterwheelSceneReference(__instance); //Plugin.Logger.LogInfo($"Waterwheel {sir} state {setActive}"); if (!GameData.listenersByWaterwheel.TryGetValue(sir, out GameStateListeners listeners)) { return; } Plugin.slotSave.ActivatedWaterwheels[sir] = setActive; GameState.EvaluateGameStateListeners(listeners); } } [HarmonyPatch(typeof(SphereController), nameof(SphereController.SetState))] static class SphereControllerSetStatePatch { static void Prefix(SphereController __instance, SphereController.State newState) { SceneItemReference sir = GameState.GetSphereSceneReference(__instance); Plugin.Logger.LogInfo($"Sphere {sir} state {newState}"); if (!GameData.listenersBySphere.TryGetValue(sir, out GameStateListeners listeners)) { return; } Plugin.slotSave.ActivatedSpheres[sir] = (newState == SphereController.State.AtDestination); GameState.EvaluateGameStateListeners(listeners); } } [HarmonyPatch(typeof(LineDoorController), nameof(LineDoorController.OnLevelEnable))] static class LineDoorControllerOnLevelEnablePatch { static void Prefix(LineDoorController __instance) { SceneItemReference sir = GameState.GetChainListenerSceneReference(__instance); if (GameData.scenes.TryGetValue(sir.scene, out var sceneDescription)) { if (sceneDescription.doors.TryGetValue(sir.index, out var requirement)) { Requirement.Decision decision = requirement.Check(); if (decision != Requirement.Decision.Maybe) { bool shouldOpen = (decision == Requirement.Decision.Yes); FieldInfo fieldInfo = typeof(LineDoorController).GetField("doorShouldOpenImmediatelyOnEnable", BindingFlags.Instance | BindingFlags.NonPublic); fieldInfo.SetValue(__instance, shouldOpen); FieldInfo animatorInfo = typeof(LineDoorController).GetField("m_animator", BindingFlags.Instance | BindingFlags.NonPublic); Animator animator = (Animator)animatorInfo.GetValue(__instance); AnimatorExt.PlayDoorStateThenDisable(animator, __instance.DoorOpenAnimationString, shouldOpen ? 1f : -1f, true); __instance.chains.Clear(); } } } } } [HarmonyPatch(typeof(LineDoorController), nameof(LineDoorController.OnLoad))] static class LineDoorControllerOnLoadPatch { static void Prefix(LineDoorController __instance) { //Plugin.Logger.LogInfo("LineDoorControllerOnLoadPatch entered"); SceneItemReference sir = GameState.GetChainListenerSceneReference(__instance); //Plugin.Logger.LogInfo($"Got sir {sir}"); if (GameData.scenes.TryGetValue(sir.scene, out var sceneDescription)) { //Plugin.Logger.LogInfo($"{sceneDescription.doors.Count} doors in scene"); if (sceneDescription.doors.TryGetValue(sir.index, out var requirement)) { //Plugin.Logger.LogInfo($"Found req {requirement}"); Requirement.Decision decision = requirement.Check(); if (decision != Requirement.Decision.Maybe) { bool shouldOpen = (decision == Requirement.Decision.Yes); FieldInfo fieldInfo = typeof(LineDoorController).GetField("doorShouldOpenImmediatelyOnEnable", BindingFlags.Instance | BindingFlags.NonPublic); fieldInfo.SetValue(__instance, shouldOpen); FieldInfo animatorInfo = typeof(LineDoorController).GetField("m_animator", BindingFlags.Instance | BindingFlags.NonPublic); Animator animator = (Animator)animatorInfo.GetValue(__instance); AnimatorExt.PlayDoorStateThenDisable(animator, __instance.DoorOpenAnimationString, shouldOpen ? 1f : -1f, true); __instance.chains.Clear(); } } } } } [HarmonyPatch(typeof(LevelLoader), nameof(LevelLoader.MovePlayerIntoNewScene))] static class LevelLoaderMovePlayerIntoNewScenePatch { static void Postfix(LevelSystems newLevel) { if (newLevel.levelName == "World_905_EndingCollapseCutscene_Optimized") { Plugin.archipelagoManager.SendGoal(); return; } Plugin.slotSave.VisitedScenes.Add(newLevel.levelName); if (GameData.listenersByScene.TryGetValue(newLevel.levelName, out var listeners)) { Plugin.Logger.LogInfo($"Evaluating for scene {newLevel.levelName}"); GameState.EvaluateGameStateListeners(listeners); } } } [HarmonyPatch(typeof(DarkModeCollapsedCubeWorldGrow), nameof(DarkModeCollapsedCubeWorldGrow.OnChainFillComplete))] static class DarkModeCollapsedCubeWorldGrowOnChainFillCompletePatch { static bool Prefix(DarkModeCollapsedCubeWorldGrow __instance) { SceneItemReference sir = GameState.GetChainListenerSceneReference(__instance); if (GameData.scenes.TryGetValue(sir.scene, out var sceneDescription) && sceneDescription.worldGrows.TryGetValue(sir.index, out var requirement) && requirement.Check() != Requirement.Decision.Yes) { return false; } return true; } } [HarmonyPatch(typeof(DarkModeCollapsedCubeWorldGrow), nameof(DarkModeCollapsedCubeWorldGrow.OnLevelEnable))] static class DarkModeCollapsedCubeWorldGrowOnLevelEnablePatch { static void Prefix(DarkModeCollapsedCubeWorldGrow __instance) { SceneItemReference sir = GameState.GetChainListenerSceneReference(__instance); if (GameData.scenes.TryGetValue(sir.scene, out var sceneDescription) && sceneDescription.worldGrows.TryGetValue(sir.index, out var requirement) && requirement.Check() == Requirement.Decision.Yes) { FieldInfo fieldInfo = typeof(DarkModeCollapsedCubeWorldGrow).GetField("m_grown", BindingFlags.Instance | BindingFlags.NonPublic); fieldInfo.SetValue(__instance, true); } } } [HarmonyPatch(typeof(SolidStateController), nameof(SolidStateController.OnChainFillComplete))] static class SolidStateControllerOnChainFillCompletePatch { static bool Prefix(SolidStateController __instance) { SceneItemReference sir = GameState.GetChainListenerSceneReference(__instance); if (GameData.scenes.TryGetValue(sir.scene, out var sceneDescription)) { if (sceneDescription.smokeWalls.TryGetValue(sir.index, out var requirement)) { Requirement.Decision decision = requirement.Check(); if (decision == Requirement.Decision.No) { return false; } } } return true; } } [HarmonyPatch(typeof(SolidStateController), nameof(SolidStateController.OnUnfilled))] static class SolidStateControllerOnUnfilledPatch { static bool Prefix(SolidStateController __instance) { SceneItemReference sir = GameState.GetChainListenerSceneReference(__instance); if (GameData.scenes.TryGetValue(sir.scene, out var sceneDescription)) { if (sceneDescription.smokeWalls.TryGetValue(sir.index, out var requirement)) { Requirement.Decision decision = requirement.Check(); if (decision == Requirement.Decision.Yes) { return false; } } } return true; } } [HarmonyPatch(typeof(DarkModeCollider), nameof(DarkModeCollider.OnLevelUpdate))] static class DarkModeColliderOnLevelUpdatePatch { static void Postfix(DarkModeCollider __instance) { if (__instance.gameObject.scene.name == "World_000_Optimized") { __instance.gameObject.SetActive(false); } } } [HarmonyPatch(typeof(MiniatureWorldButton), "IInteractable.OnLookAt")] static class MiniatureWorldButtonOnLookAtPatch { static bool Prefix(MiniatureWorldButton __instance, ref bool __result) { __result = !GameManager.DarkModeController.IsDarkMode && GameManager.MandalaManager.LevelsActiveInMandala.Contains(__instance.LevelToGoIntoName) && __instance.myMandalaController.myGravityDirection == GameManager.PlayerController.gravityDirection; return false; } } [HarmonyPatch(typeof(MiniatureWorldButton), "IInteractable.CanInteract")] static class MiniatureWorldButtonCanInteractPatch { static bool Prefix(MiniatureWorldButton __instance, ref bool __result) { __result = !GameManager.DarkModeController.IsDarkMode && GameManager.MandalaManager.LevelsActiveInMandala.Contains(__instance.LevelToGoIntoName) && __instance.myMandalaController.myGravityDirection == GameManager.PlayerController.gravityDirection; return false; } } [HarmonyPatch(typeof(MiniatureWorldButton), "IInteractable.OnInteract")] static class MiniatureWorldButtonOnInteractPatch { static bool Prefix(MiniatureWorldButton __instance) { __instance.myMandalaController.ShrinkIntoLevel(__instance); return false; } } [HarmonyPatch(typeof(DarkModeCageController), "StartFilling")] static class DarkModeCageControllerStartFillingPatch { static bool Prefix(DarkModeCageController __instance) { SceneItemReference sir = GameState.GetGameplayComponentSceneReference(__instance); if (GameData.scenes.TryGetValue(sir.scene, out var sceneDescription)) { if (sceneDescription.lasers.TryGetValue(sir.index, out var requirement)) { Requirement.Decision decision = requirement.Check(); if (decision == Requirement.Decision.No) { return false; } } } return true; } } [HarmonyPatch(typeof(DarkModeCageController), "StartUnfilling")] static class DarkModeCageControllerStartUnfillingPatch { static bool Prefix(DarkModeCageController __instance) { SceneItemReference sir = GameState.GetGameplayComponentSceneReference(__instance); if (GameData.scenes.TryGetValue(sir.scene, out var sceneDescription)) { if (sceneDescription.lasers.TryGetValue(sir.index, out var requirement)) { Requirement.Decision decision = requirement.Check(); if (decision == Requirement.Decision.Yes) { return false; } } } return true; } } [HarmonyPatch(typeof(LevelSystems), nameof(LevelSystems.InitializeAndLoadLevel))] static class LevelSystemsInitializeAndLoadLevelPatch { static void Prefix(LevelSystems __instance) { if (Plugin.archipelagoManager.RoomShuffle) { if (GameData.scenes.TryGetValue(__instance.levelName, out SceneDescription sceneDescription)) { foreach (var portal in __instance.LevelInfo.portalList) { if (sceneDescription.portals.TryGetValue(portal.portalName, out EntranceIdentifier exitId) && Plugin.archipelagoManager.EntranceMapping.TryGetValue(exitId, out EntranceIdentifier enterId) && GameData.portal_by_entrance.TryGetValue(enterId, out PortalDescription pairedPortalDescription)) { SceneLink secondLink = new() { portalName = pairedPortalDescription.name, sceneName = pairedPortalDescription.scene.Substring(0, pairedPortalDescription.scene.Length - 10), gravity = GravityDirection.DownBlue, targetScene = portal, }; portal.targetScene = secondLink; } } } } /*if (__instance.gameObject.scene.name == "World_000_Optimized") { // 0: Portal_W001 SceneLink sceneLink = __instance.LevelInfo.portalList[0]; SceneLink secondLink = new() { portalName = "Portal_W063", sceneName = "Hallway_W045_W073", gravity = GravityDirection.DownBlue, targetScene = sceneLink }; sceneLink.targetScene = secondLink; } else if (__instance.gameObject.scene.name == "Hallway_W045_W073_Optimized") { SceneLink sceneLink = __instance.LevelInfo.portalList[3]; SceneLink secondLink = new() { portalName = "Portal_W001", sceneName = "World_000", gravity = GravityDirection.DownBlue, targetScene = sceneLink }; sceneLink.targetScene = secondLink; }*/ } } }