diff --git a/APRandomizerMain.cs b/APRandomizerMain.cs index a48d448..5e1e21e 100644 --- a/APRandomizerMain.cs +++ b/APRandomizerMain.cs @@ -13,15 +13,15 @@ using Mod.Courier; using Mod.Courier.Module; using Mod.Courier.UI; -using static Mod.Courier.UI.TextEntryButtonInfo; using MonoMod.Cil; using TMPro; using UnityEngine; using UnityEngine.SceneManagement; using WebSocketSharp; +using static Mod.Courier.UI.TextEntryButtonInfo; using Object = UnityEngine.Object; -namespace MessengerRando +namespace MessengerRando { /// /// Where it all begins! This class defines and injects all the necessary for the mod. @@ -53,7 +53,7 @@ public override void Load() //Initialize the randomizer state manager randoStateManager = new RandomizerStateManager(); - + //Plug in my code :3 On.InventoryManager.AddItem += InventoryManager_AddItem; On.ProgressionManager.SetChallengeRoomAsCompleted += ProgressionManager_SetChallengeRoomAsCompleted; @@ -102,6 +102,8 @@ public override void Load() On.ElementalSkylandsLevelInitializer.OnBeforeInitDone += RandoLevelManager.ElementalSkylandsInit; // On.PortalOpeningCutscene.OnOpenPortalEvent += RandoPortalManager.OpenPortalEvent; On.TotHQ.LeaveToLevel += RandoPortalManager.LeaveHQ; + // generator deactivation management + Hooks.ElementalSkylandGenerator.ApplyHooks(); //These functions let us override and manage power seals ourselves with 'fake' items On.ProgressionManager.TotalPowerSealCollected += ProgressionManager_TotalPowerSealCollected; On.ShopChestOpenCutscene.OnChestOpened += (orig, self) => @@ -121,12 +123,12 @@ public override void Load() On.Cutscene.Play += Cutscene_Play; On.PlayerController.Awake += OnPlayerController_Awake; //temp add - #if DEBUG +#if DEBUG On.PhantomIntroCutscene.OnEnterRoom += PhantomIntro_OnEnterRoom; //this lets us skip the phantom fight On.UIManager.ShowView += UIManager_ShowView; On.MusicBox.SetNotesState += MusicBox_SetNotesState; On.PowerSeal.OnEnterRoom += PowerSeal_OnEnterRoom; - #endif +#endif Console.WriteLine("Randomizer finished loading!"); } @@ -171,17 +173,17 @@ private void OnOptionScreenEnable(On.OptionScreen.orig_OnEnable orig, OptionScre public override void Initialize() { - #if DEBUG +#if DEBUG SceneManager.sceneLoaded += OnSceneLoadedRando; - #endif - +#endif + //load config Debug.Log("Loading config from APConfig.toml"); try { UserConfig.ReadConfig(ModPath); } - catch (Exception e) {Console.Write(e);} + catch (Exception e) { Console.Write(e); } ArchipelagoMenu.BuildArchipelagoMenu(); RandoMenu.BuildRandoMenu(); HintMenu.BuildHintMenu(); @@ -245,7 +247,7 @@ void InventoryManager_AddItem(On.InventoryManager.orig_AddItem orig, InventoryMa } //Call original add with items orig(self, itemId, quantity); - + } void ProgressionManager_SetChallengeRoomAsCompleted(On.ProgressionManager.orig_SetChallengeRoomAsCompleted orig, ProgressionManager self, string roomKey) @@ -277,7 +279,7 @@ bool HasItem_IsTrue(On.HasItem.orig_IsTrue orig, HasItem self) //OLD WAY //Don't actually check for the item i have, check to see if I have the item that was at it's location. //int itemQuantity = Manager.Instance.GetItemQuantity(randoStateManager.CurrentLocationToItemMapping[check].Item); - + //NEW WAY //Don't actually check for the item I have, check to see if I have done this check before. We'll do this by seeing if the item at its location has been collected yet or not int itemQuantity = RandomizerStateManager.HasCompletedCheck(check) ? 1 : 0; @@ -290,7 +292,7 @@ bool HasItem_IsTrue(On.HasItem.orig_IsTrue orig, HasItem self) ? 1 : 0; } - + switch (self.conditionOperator) { case EConditionOperator.LESS_THAN: @@ -314,9 +316,9 @@ bool HasItem_IsTrue(On.HasItem.orig_IsTrue orig, HasItem self) Console.WriteLine("HasItem check was not randomized. Doing vanilla checks."); Debug.Log($"Is randomized file : '{ArchipelagoClient.HasConnected}' | Is location '{self.item}' randomized: '{randoStateManager.IsLocationRandomized(self.item, out check)}' | Not in the special triggers list: '{!RandomizerConstants.GetSpecialTriggerNames().Contains(self.Owner.name)}'|"); return orig(self); - + } - + bool AwardNoteCutscene_ShouldPlay(On.AwardNoteCutscene.orig_ShouldPlay orig, AwardNoteCutscene self) { //Need to handle note cutscene triggers so they will play as long as I dont have the actual item it grants @@ -338,7 +340,7 @@ bool CutsceneHasPlayed_IsTrue(On.CutsceneHasPlayed.orig_IsTrue orig, CutsceneHas } return orig(self); } - + void SaveGameSelectionScreen_OnLoadGame(On.SaveGameSelectionScreen.orig_OnLoadGame orig, SaveGameSelectionScreen self, int slotIndex) { //slotIndex is 0-based, going to increment it locally to keep things simple. @@ -496,7 +498,7 @@ private void SaveGameSelectionScreen_OnDelete(On.SaveGameSelectionScreen.orig_On } orig(self, delete); } - + void PauseScreen_OnQuitToTitle(On.BackToTitleScreen.orig_GoBackToTitleScreen orig) { if (ArchipelagoClient.HasConnected) @@ -524,7 +526,7 @@ void PauseScreen_OnQuitToTitle(On.BackToTitleScreen.orig_GoBackToTitleScreen ori void CatacombLevelInitializer_OnBeforeInitDone(On.CatacombLevelInitializer.orig_OnBeforeInitDone orig, CatacombLevelInitializer self) { //check to see if we already have the item at Necro check - if(ArchipelagoClient.HasConnected) + if (ArchipelagoClient.HasConnected) { if (!RandomizerStateManager.HasCompletedCheck( ItemsAndLocationsHandler.LocationsLookup[new LocationRO("Necro")])) @@ -538,7 +540,7 @@ ItemsAndLocationsHandler.LocationsLookup[new LocationRO("Necro")])) //we are not rando here, call orig method orig(self); } - + } // Breaking into Necro cutscene to fix things @@ -554,11 +556,11 @@ void RuxxtinNoteAndAwardAmuletCutscene_Play(ILContext il) { ILCursor cursor = new ILCursor(il); - while(cursor.TryGotoNext(MoveType.After, instr => instr.MatchLdcI4(55))) + while (cursor.TryGotoNext(MoveType.After, instr => instr.MatchLdcI4(55))) { cursor.EmitDelegate>(GetRandoItemByItem); } - + } void MegaTimeShard_OnBreakDone(On.MegaTimeShard.orig_OnBreakDone orig, MegaTimeShard self) @@ -652,7 +654,7 @@ void MusicBox_SetNotesState(On.MusicBox.orig_SetNotesState orig, MusicBox self) // this determines which notes should be shown present in the music box orig(self); } - + public static void OnToggleWindmillShuriken() { Manager.Instance.useWindmillShuriken = !Manager.Instance.useWindmillShuriken; @@ -663,7 +665,7 @@ public static void OnToggleWindmillShuriken() public static void OnSelectTeleportToHq() { - Console.WriteLine("Teleporting to HQ!"); + Console.WriteLine("Teleporting to HQ!"); #if DEBUG var position = Manager.Instance.Player.transform.position; Console.WriteLine($"{position.x} {position.y} {position.z}"); @@ -753,7 +755,7 @@ public static void OnSelectArchipelagoConnect() { return; } - + ArchipelagoClient.ConnectAsync(ArchipelagoMenu.ArchipelagoConnectButton); } @@ -916,7 +918,7 @@ private void SaveManager_DoActualSave(On.SaveManager.orig_DoActualSaving orig, S // just keep trying to solve it until it eventually works lmao LostWoodsManager.SolveLostWoods(); } - + // The game calls the save method after the ending cutscene before rolling credits if (ArchipelagoClient.Authenticated && Manager.Instance.GetCurrentLevelEnum().Equals(ELevel.Level_Ending)) diff --git a/Archipelago/ArchipelagoData.cs b/Archipelago/ArchipelagoData.cs index 0ec0050..dff21ae 100644 --- a/Archipelago/ArchipelagoData.cs +++ b/Archipelago/ArchipelagoData.cs @@ -1,12 +1,9 @@ using System; using System.Collections.Generic; -using System.IO; using System.Threading; -using Archipelago.MultiClient.Net.Models; using MessengerRando.GameOverrideManagers; using MessengerRando.Utils; using Newtonsoft.Json; -using UnityEngine; namespace MessengerRando.Archipelago { @@ -37,7 +34,7 @@ public void StartNewSeed() ReceivedItems = new Dictionary(); AvailableTeleports = [false, false]; } - + public override string ToString() { return JsonConvert.SerializeObject(this); @@ -70,7 +67,7 @@ private bool loadData(int slot) Index = tempServerData.Index; PowerSealsCollected = tempServerData.PowerSealsCollected; CheckedLocations = tempServerData.CheckedLocations ?? []; - RandoBossManager.DefeatedBosses = DefeatedBosses = + RandoBossManager.DefeatedBosses = DefeatedBosses = tempServerData.DefeatedBosses ?? []; ReceivedItems = tempServerData.ReceivedItems ?? []; AvailableTeleports = tempServerData.AvailableTeleports ?? [false, false]; @@ -131,10 +128,10 @@ private bool loadData(int slot) } return ArchipelagoClient.HasConnected = true; } - catch (Exception ex) + catch (Exception ex) { Console.WriteLine(ex.ToString()); - return false; + return false; } } } diff --git a/Archipelago/ItemsAndLocationsHandler.cs b/Archipelago/ItemsAndLocationsHandler.cs index 6ec1305..845946c 100644 --- a/Archipelago/ItemsAndLocationsHandler.cs +++ b/Archipelago/ItemsAndLocationsHandler.cs @@ -13,7 +13,7 @@ public static class ItemsAndLocationsHandler public static Dictionary ItemsLookup; public static Dictionary LocationsLookup; private static Dictionary EItemsLocationsLookup; - private static Dictionary IDtoLocationsLookup; + public static Dictionary IDtoLocationsLookup; public static RandomizerStateManager RandoStateManager; @@ -39,13 +39,14 @@ public static void Initialize() new RandoItemRO("Money Wrench", EItems.MONEY_WRENCH), new RandoItemRO("Teleport Trap", EItems.NONE), new RandoItemRO("Prophecy Trap", EItems.NONE), + new RandoItemRO("Progressive Generator Shutdown", EItems.NONE), new RandoItemRO("Darkness Trap", EItems.NONE), new RandoItemRO("Health", EItems.POTION), new RandoItemRO("Mana", EItems.MANA), new RandoItemRO("Feather", EItems.FEATHER), new RandoItemRO("Mask Fragment", EItems.MASK_PIECE), }); - + foreach (var item in ArchipelagoItems) { ItemsLookup.Add(offset, item); @@ -58,7 +59,7 @@ public static void Initialize() LocationsLookup = new Dictionary(); EItemsLocationsLookup = new Dictionary(); IDtoLocationsLookup = new Dictionary(); - + var megaShards = RandoTimeShardManager.MegaShardLookup.Select(item => item.Loc).ToList(); ArchipelagoLocations.AddRange(megaShards); ArchipelagoLocations.AddRange(BossLocations); @@ -66,7 +67,12 @@ public static void Initialize() foreach (var figurine in Enum.GetValues(typeof(EFigurine))) ArchipelagoLocations.Add(new LocationRO(figurine.ToString())); ArchipelagoLocations.Add(new LocationRO("Money Wrench", EItems.MONEY_WRENCH)); - + + ArchipelagoLocations.Add(new LocationRO("Elemental Skylands - Shutdown Air Generator")); + ArchipelagoLocations.Add(new LocationRO("Elemental Skylands - Shutdown Earth Generator")); + ArchipelagoLocations.Add(new LocationRO("Elemental Skylands - Shutdown Water Generator")); + ArchipelagoLocations.Add(new LocationRO("Elemental Skylands - Shutdown Fire Generator")); + foreach (var progLocation in ArchipelagoLocations) { LocationsLookup.Add(progLocation, offset); @@ -228,7 +234,7 @@ public static void Initialize() //Elemental Skylands new LocationRO("-52-20420436", "Elemental Skylands Seal - Air Seal"), new LocationRO("18361868372388", "Elemental Skylands Seal - Water Seal"), - new LocationRO("28602892356388", "Elemental Skylands Seal - Fire Seal") + new LocationRO("28602892356388", "Elemental Skylands Seal - Fire Seal"), ]; private static readonly List BossLocations = @@ -354,15 +360,20 @@ public static void Unlock(long itemToUnlock, int quantity = APQuantity) case "Timeshard": quantity = ArchipelagoClient.ServerData.SlotData.ContainsKey("shop") ? 1 : 100; break; - case "Timeshard (10)": quantity = 10; + case "Timeshard (10)": + quantity = 10; break; - case "Timeshard (50)": quantity = 50; + case "Timeshard (50)": + quantity = 50; break; - case "Timeshard (100)": quantity = 100; + case "Timeshard (100)": + quantity = 100; break; - case "Timeshard (300)": quantity = 300; + case "Timeshard (300)": + quantity = 300; break; - case "Timeshard (500)": quantity = 500; + case "Timeshard (500)": + quantity = 500; break; } Console.WriteLine($"Unlocking time shards... {quantity}"); @@ -379,6 +390,11 @@ public static void Unlock(long itemToUnlock, int quantity = APQuantity) } catch { + if (randoItem.Name.EndsWith("Generator Shutdown")) + { + SkylandsGeneratorStateManager.ReceiveGeneratorShutdown(randoItem.Name); + break; + } switch (randoItem.Name) { case "Darkness Trap": @@ -430,6 +446,12 @@ public static void Unlock(long itemToUnlock, int quantity = APQuantity) ArchipelagoClient.ServerData.ReceivedItems.Add(itemToUnlock, 1); } + public static bool IsLocationChecked(LocationRO location) + { + LocationsLookup.TryGetValue(location, out var locationID); + return RandomizerStateManager.HasCompletedCheck(locationID); + } + public static void SendLocationCheck(LocationRO checkedLocation) { LocationsLookup.TryGetValue(checkedLocation, out var locationID); @@ -460,7 +482,7 @@ public static void SendLocationCheck(long locationID) else return; } ArchipelagoClient.ServerData.CheckedLocations.Add(locationID); - + Console.WriteLine("Sending location checks"); if (ArchipelagoClient.Authenticated) { @@ -493,7 +515,7 @@ public static void SendLocationCheck(long locationID) Unlock(itemToUnlock); else ArchipelagoClient.ItemQueue.Enqueue(itemToUnlock); - + if (!HasDialog(locationID)) { var dialog = SeedGenerator.GetOfflineDialog(locationID); @@ -518,7 +540,7 @@ public static void UnlockItems() Synced = false; } - + public static void ReSync() { Synced = true; diff --git a/GameOverrideManagers/RandoShopManager.cs b/GameOverrideManagers/RandoShopManager.cs index eb22a57..33c1784 100644 --- a/GameOverrideManagers/RandoShopManager.cs +++ b/GameOverrideManagers/RandoShopManager.cs @@ -139,8 +139,6 @@ public static void UpgradeButton_Refresh(On.UpgradeButton.orig_Refresh orig, Upg public static string GetText(On.LocalizationManager.orig_GetText orig, LocalizationManager self, string locid) { - // if (!InShop()) return orig(self, locid); - Console.WriteLine($"Requesting text for {locid}"); if (!ArchipelagoClient.HasConnected) return orig(self, locid); var locType = TextType.None; var lookupName = string.Empty; @@ -190,7 +188,7 @@ public static string GetText(On.LocalizationManager.orig_GetText orig, Localizat var itemOnLocation = RandomizerStateManager.Instance.ScoutedLocations[locationID]; if (locType.Equals(TextType.Name) && ArchipelagoClient.Authenticated) { - if(!ArchipelagoClient.ServerData.CheckedLocations.Contains(locationID)) + if (!ArchipelagoClient.ServerData.CheckedLocations.Contains(locationID)) { ThreadPool.QueueUserWorkItem(_ => ArchipelagoClient.Session.Locations.ScoutLocationsAsync( diff --git a/GameOverrideManagers/SkylandsGeneratorStateManager.cs b/GameOverrideManagers/SkylandsGeneratorStateManager.cs new file mode 100644 index 0000000..6886589 --- /dev/null +++ b/GameOverrideManagers/SkylandsGeneratorStateManager.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using MessengerRando.Archipelago; +using MessengerRando.RO; + +namespace MessengerRando.GameOverrideManagers +{ + public static class SkylandsGeneratorStateManager + { + + private static readonly Dictionary LoadedGenerators = []; + private static readonly Dictionary FlagsByGenerator = new() + { + { GeneratorType.AIR, Flags.AirGeneratorDeactivated }, + { GeneratorType.EARTH, Flags.EarthGeneratorDeactivated }, + { GeneratorType.WATER, Flags.WaterGeneratorDeactivated }, + { GeneratorType.FIRE, Flags.FireGeneratorDeactivated }, + }; + private static readonly Dictionary LocationsByGenerator = new() + { + { GeneratorType.AIR, new LocationRO("Elemental Skylands - Shutdown Air Generator") }, + { GeneratorType.EARTH, new LocationRO("Elemental Skylands - Shutdown Earth Generator") }, + { GeneratorType.WATER, new LocationRO("Elemental Skylands - Shutdown Water Generator") }, + { GeneratorType.FIRE, new LocationRO("Elemental Skylands - Shutdown Fire Generator") }, + }; + + public static bool AreGeneratorsShuffled = false; + + public static void RegisterGenerator(ElementalSkylandGenerator generator) + { + var generatorType = ToGeneratorType(generator.name); + LoadedGenerators[generatorType] = generator; + Console.WriteLine($"Registered {generatorType} generator"); + } + + public static void CleanupGenerator(ElementalSkylandGenerator generator) + { + var generatorType = ToGeneratorType(generator.name); + LoadedGenerators[generatorType] = null; + Console.WriteLine($"Cleaned up {generatorType} generator"); + } + + public static void SendLocation(ElementalSkylandGenerator generator) + { + var generatorType = ToGeneratorType(generator.name); + var location = LocationsByGenerator[generatorType]; + Console.WriteLine($"Sending location {location.LocationName}"); + ItemsAndLocationsHandler.SendLocationCheck(location); + } + + public static bool IsLocationSent(GeneratorType generatorType) + { + return ItemsAndLocationsHandler.IsLocationChecked(LocationsByGenerator[generatorType]); + } + + public static void ReceiveGeneratorShutdown(string generatorShutdownItem) + { + Console.WriteLine($"Received {generatorShutdownItem}"); + + if (AreAllGeneratorsShutdownReceived()) + { + Console.WriteLine($"All generators already deactivated, so ignoring received shutdown"); + return; + } + + var generatorType = FindNextGeneratorToShutdown(); + var generatorFlag = FlagsByGenerator[generatorType]; + Manager.Instance.SetFlag(generatorFlag, false); + Console.WriteLine($"Current flags are [{string.Join(", ", [.. Manager.Instance.flags])}]"); + + if (LoadedGenerators.TryGetValue(generatorType, out var generator) && generator != null) + { + Console.WriteLine($"Generator loaded, so playing animation"); + Manager.Instance.PlaySoundEffect(generator.shutdownSFX); + generator.animator.SetTrigger("Deactivate"); + } + } + + + public static bool AreAllGeneratorsShutdownReceived() + { + return Manager.Instance.IsFlagSet(Flags.AirGeneratorDeactivated) + && Manager.Instance.IsFlagSet(Flags.EarthGeneratorDeactivated) + && Manager.Instance.IsFlagSet(Flags.WaterGeneratorDeactivated) + && Manager.Instance.IsFlagSet(Flags.FireGeneratorDeactivated); + } + + public static void OpenFireGeneratorDoor() + { + var fireGenerator = LoadedGenerators[GeneratorType.FIRE]; + Manager.Instance.PlaySoundEffect(fireGenerator.wallDisappearSFX); + fireGenerator.wall.SetActive(value: false); + } + + public static GeneratorType ToGeneratorType(string generator) + { + return generator switch + { + var g when g.Contains("Air") => GeneratorType.AIR, + var g when g.Contains("Water") => GeneratorType.WATER, + var g when g.Contains("Earth") => GeneratorType.EARTH, + var g when g.Contains("Fire") => GeneratorType.FIRE, + _ => throw new Exception($"Unknown generator type for generator with name {generator}") + }; + } + + private static GeneratorType FindNextGeneratorToShutdown() + { + if (Manager.Instance.IsFlagSet(Flags.WaterGeneratorDeactivated)) + { + Console.WriteLine($"Water generator already deactivated, so shutting down Fire generator"); + return GeneratorType.FIRE; + } + if (Manager.Instance.IsFlagSet(Flags.EarthGeneratorDeactivated)) + { + Console.WriteLine($"Earth generator already deactivated, so shutting down Water generator"); + return GeneratorType.WATER; + } + if (Manager.Instance.IsFlagSet(Flags.AirGeneratorDeactivated)) + { + Console.WriteLine($"Air generator already deactivated, so shutting down Earth generator"); + return GeneratorType.EARTH; + } + + Console.WriteLine($"No generators already deactivated, so shutting down Air generator"); + return GeneratorType.AIR; + } + } + + public enum GeneratorType + { + AIR, EARTH, WATER, FIRE + } + + +} diff --git a/Hooks/ElementalSkylandGenerator.cs b/Hooks/ElementalSkylandGenerator.cs new file mode 100644 index 0000000..2ea307e --- /dev/null +++ b/Hooks/ElementalSkylandGenerator.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections; +using MessengerRando.Archipelago; +using MessengerRando.GameOverrideManagers; +using MessengerRando.Utils; + + +namespace MessengerRando.Hooks +{ + public class ElementalSkylandGenerator + { + + public static void ApplyHooks() + { + On.ElementalSkylandGenerator.Start += ElementalSkylandGenerator_Start; + On.ElementalSkylandGenerator.SetState += ElementalSkylandGenerator_SetState; + On.ElementalSkylandGenerator.OnLanternHit += ElementalSkylandGenerator_OnLanternHit; + On.ElementalSkylandGenerator.Shutdown += ElementalSkylandGenerator_Shutdown; + On.ElementalSkylandGenerator.OnDeactivateDone += ElementalSkylandGenerator_OnDeactivateDone; + On.ElementalSkylandGenerator.OnDisable += ElementalSkylandGenerator_OnDisable; + } + + public static void ElementalSkylandGenerator_Start(On.ElementalSkylandGenerator.orig_Start orig, global::ElementalSkylandGenerator self) + { + if (!SkylandsGeneratorStateManager.AreGeneratorsShuffled) { orig(self); return; } + + SkylandsGeneratorStateManager.RegisterGenerator(self); + orig(self); + } + + public static void ElementalSkylandGenerator_SetState(On.ElementalSkylandGenerator.orig_SetState orig, global::ElementalSkylandGenerator self) + { + if (!SkylandsGeneratorStateManager.AreGeneratorsShuffled) { orig(self); return; } + if (!ArchipelagoClient.HasConnected) { orig(self); return; } + + var generatorType = SkylandsGeneratorStateManager.ToGeneratorType(self.name); + if (Manager.Instance.IsFlagSet(self.deactivatedFlag)) + { + Console.WriteLine($"Deactivating {self.name}"); + self.animator.SetTrigger("DeactivateInstant"); + } + + bool isLocationSent = SkylandsGeneratorStateManager.IsLocationSent(generatorType); + if ((generatorType == GeneratorType.FIRE && SkylandsGeneratorStateManager.AreAllGeneratorsShutdownReceived()) + || (generatorType != GeneratorType.FIRE && (isLocationSent || Manager.Instance.IsFlagSet(self.deactivatedFlag)))) + { + Console.WriteLine($"Opening door for {self.name}"); + self.wall.gameObject.SetActive(value: false); + } + + for (int i = 0; i < self.powerLanterns.Count; i++) + { + self.powerLanterns[i].SetFull(full: !isLocationSent); + } + } + + public static void ElementalSkylandGenerator_OnLanternHit(On.ElementalSkylandGenerator.orig_OnLanternHit orig, global::ElementalSkylandGenerator self, global::Hittable lantern, global::HitData hitData) + { + if (!SkylandsGeneratorStateManager.AreGeneratorsShuffled) { orig(self, lantern, hitData); return; } + + if (!(lantern as Lantern).Full) + { + return; + } + + Manager.Instance.PlaySoundEffect(self.powerSourceHitSFX); + bool flag = true; + for (int i = 0; i < self.powerLanterns.Count; i++) + { + if (self.powerLanterns[i].Full && self.powerLanterns[i] != lantern) + { + flag = false; + break; + } + } + + if (!Manager.Instance.IsFlagSet(self.deactivatedFlag)) + { + self.animator.SetTrigger("ReceiveHit"); + } + if (flag) + { + ReflectionHelpers.InvokeMethod(self, "Shutdown"); + } + } + + public static void ElementalSkylandGenerator_Shutdown(On.ElementalSkylandGenerator.orig_Shutdown orig, global::ElementalSkylandGenerator self) + { + if (!SkylandsGeneratorStateManager.AreGeneratorsShuffled) { orig(self); return; } + if (!ArchipelagoClient.HasConnected) { orig(self); return; } + + SkylandsGeneratorStateManager.SendLocation(self); + + if (SkylandsGeneratorStateManager.ToGeneratorType(self.name) != GeneratorType.FIRE && self.wall.activeSelf) + { + Manager.Instance.PlaySoundEffect(self.wallDisappearSFX); + self.wall.SetActive(value: false); + } + } + + public static void ElementalSkylandGenerator_OnDeactivateDone(On.ElementalSkylandGenerator.orig_OnDeactivateDone orig, global::ElementalSkylandGenerator self) + { + if (!SkylandsGeneratorStateManager.AreGeneratorsShuffled) { orig(self); return; } + if (SkylandsGeneratorStateManager.AreAllGeneratorsShutdownReceived()) + { + SkylandsGeneratorStateManager.OpenFireGeneratorDoor(); + } + + if (SkylandsGeneratorStateManager.ToGeneratorType(self.name) != GeneratorType.FIRE && self.wall.activeSelf) + { + Manager.Instance.PlaySoundEffect(self.wallDisappearSFX); + self.wall.SetActive(value: false); + } + + self.StartCoroutine((IEnumerator)ReflectionHelpers.InvokeMethodWithReturn(self, "ShakeCamCoroutine")); + } + + public static void ElementalSkylandGenerator_OnDisable(On.ElementalSkylandGenerator.orig_OnDisable orig, global::ElementalSkylandGenerator self) + { + if (!SkylandsGeneratorStateManager.AreGeneratorsShuffled) { orig(self); return; } + + SkylandsGeneratorStateManager.CleanupGenerator(self); + orig(self); + } + } +} diff --git a/MessengerRando.csproj b/MessengerRando.csproj index 5b19cf5..0a760f3 100644 --- a/MessengerRando.csproj +++ b/MessengerRando.csproj @@ -95,6 +95,7 @@ + @@ -104,6 +105,7 @@ + diff --git a/Utils/RandomizerStateManager.cs b/Utils/RandomizerStateManager.cs index c36181b..df1a19b 100644 --- a/Utils/RandomizerStateManager.cs +++ b/Utils/RandomizerStateManager.cs @@ -4,7 +4,6 @@ using System.Linq; using Archipelago.MultiClient.Net.Enums; using Archipelago.MultiClient.Net.Models; -using Archipelago.MultiClient.Net.Packets; using MessengerRando.Archipelago; using MessengerRando.GameOverrideManagers; using MessengerRando.Utils.Constants; @@ -43,9 +42,9 @@ public class RandomizerStateManager public RandomizerStateManager() { - #if DEBUG +#if DEBUG // SkipPhantom = true; - #endif +#endif try { //Create initial values for the state machine @@ -56,8 +55,9 @@ public RandomizerStateManager() { 2, new ArchipelagoData() }, { 3, new ArchipelagoData() }, }; - if (ArchipelagoClient.Authenticated) InitializeSeed(); - } catch (Exception e) {Console.WriteLine(e);} + if (ArchipelagoClient.Authenticated) InitializeSeed(); + } + catch (Exception e) { Console.WriteLine(e); } } public static void InitializeSeed() @@ -143,6 +143,19 @@ public static void InitializeSeed() "GlacialPeakPortal" ]; } + + if (ArchipelagoClient.Session.Locations.AllLocations.Any(location => + ItemsAndLocationsHandler.IDtoLocationsLookup.TryGetValue(location, out var loc) + && loc.LocationName.StartsWith("Elemental Skylands - Shutdown"))) + { + Console.WriteLine("Found at least one location for skylands generator shutdown, meaning generators are shuffled"); + SkylandsGeneratorStateManager.AreGeneratorsShuffled = true; + + } + else + { + Console.WriteLine("No locations found for skylands generator shutdown, meaning generators are not shuffled"); + } } private static void SetupScoutedLocations(Dictionary scoutedLocationInfo) @@ -240,13 +253,13 @@ public static void InitializeNewSecondQuest(SaveGameSelectionScreen saveScreen, invManager.ItemQuantityByItemId = new ItemQuantityByItemID(); invManager.shopUpgradeUnlocked = []; invManager.figurinesCollected = []; - + invManager.ItemQuantityByItemId.Add(EItems.SCROLL_UPGRADE, 1); invManager.ItemQuantityByItemId.Add(EItems.TIME_SHARD, 0); invManager.ItemQuantityByItemId.Add(EItems.MAP, 1); invManager.ItemQuantityByItemId.Add(EItems.CLIMBING_CLAWS, 1); invManager.AllTimeItemQuantityByItemId = invManager.ItemQuantityByItemId; - + progManager.secondQuest = true; // var discoveredLevels = new List // { @@ -269,7 +282,7 @@ public static void InitializeNewSecondQuest(SaveGameSelectionScreen saveScreen, // progManager.levelsDiscovered.AddRange(discoveredLevels); // progManager.allTimeDiscoveredLevels.AddRange(discoveredLevels); progManager.isLevelDiscoveredAwarded = true; - + var skipCutscenes = new List { "MessengerCutScene", diff --git a/Utils/ReflectionHelpers.cs b/Utils/ReflectionHelpers.cs index effcf0c..b67b31a 100644 --- a/Utils/ReflectionHelpers.cs +++ b/Utils/ReflectionHelpers.cs @@ -23,5 +23,10 @@ public static void InvokeMethod(this object o, string methodName, object[] param { o.GetType().GetMethod(methodName, Flags)?.Invoke(o, parameters ?? []); } + + public static object InvokeMethodWithReturn(this object o, string methodName, object[] parameters = null) + { + return o.GetType().GetMethod(methodName, Flags)?.Invoke(o, parameters ?? []); + } } -} \ No newline at end of file +}