diff options
author | Star Rauchenberger <fefferburbia@gmail.com> | 2025-02-23 14:00:17 -0500 |
---|---|---|
committer | Star Rauchenberger <fefferburbia@gmail.com> | 2025-02-23 14:00:17 -0500 |
commit | c0ad40a3d43e83f35432d12e8b55d90c1865c264 (patch) | |
tree | b1a91e9d697cb548ba048e8badbaccaf2800b065 | |
parent | 22fd4098d048b6833f1993604f17144eadf3dea3 (diff) | |
download | manifold-garden-archipelago-c0ad40a3d43e83f35432d12e8b55d90c1865c264.tar.gz manifold-garden-archipelago-c0ad40a3d43e83f35432d12e8b55d90c1865c264.tar.bz2 manifold-garden-archipelago-c0ad40a3d43e83f35432d12e8b55d90c1865c264.zip |
A bunch of things
- Requirement decisions now have three states: yes, no, and maybe. Maybe means that the state of the game object in question hasn't been detected yet, so acting on the event that checked the requirement should be deferred. This was intended to fix the inverted pad checks in Pyramid, but it does not seem to have worked. Questioning reverting this part. - Fixed the doors in W018. - Smoke walls now work, sort of. If the cube is still on the pad, loading the game in that room will cause the wall to close (leaving and re-entering the room dismisses it thought). Putting the cube back on the pad will also close it again. Need to fix later. - More patches that record gameplay state without interaction from the player and call listeners for those objects. e.g. buttons getting pressed by the game when loaded in, as opposed to when pressed by the player. Pads have been tweaked like this too. - When opening a door, the mod now also invokes the animator. This fixes the problem where doors would appear to be closed when re-entering a room, even though you could still walk through them.
-rw-r--r-- | ArchipelagoManager.cs | 1 | ||||
-rw-r--r-- | GameData.cs | 2 | ||||
-rw-r--r-- | GameState.cs | 37 | ||||
-rw-r--r-- | GameplayPatches.cs | 195 | ||||
-rw-r--r-- | Requirements.cs | 117 | ||||
-rw-r--r-- | SlotSave.cs | 16 | ||||
-rw-r--r-- | game_data.yaml | 38 |
7 files changed, 309 insertions, 97 deletions
diff --git a/ArchipelagoManager.cs b/ArchipelagoManager.cs index 611ef30..998e2d9 100644 --- a/ArchipelagoManager.cs +++ b/ArchipelagoManager.cs | |||
@@ -87,6 +87,7 @@ namespace ManifoldGardenArchipelago | |||
87 | 87 | ||
88 | if (GameData.listenersByItem.TryGetValue(itemName, out var listeners)) | 88 | if (GameData.listenersByItem.TryGetValue(itemName, out var listeners)) |
89 | { | 89 | { |
90 | //Plugin.Logger.LogInfo($"Evaluating for {itemName}"); | ||
90 | GameState.EvaluateGameStateListeners(listeners); | 91 | GameState.EvaluateGameStateListeners(listeners); |
91 | } | 92 | } |
92 | } | 93 | } |
diff --git a/GameData.cs b/GameData.cs index 9ec511e..6b5eddc 100644 --- a/GameData.cs +++ b/GameData.cs | |||
@@ -372,6 +372,8 @@ namespace ManifoldGardenArchipelago | |||
372 | Requirement req = ParseRequirement(scenePair.Key, (Dictionary<object, object>)smokePair.Value); | 372 | Requirement req = ParseRequirement(scenePair.Key, (Dictionary<object, object>)smokePair.Value); |
373 | sceneDescription.smokeWalls[sir.index] = req; | 373 | sceneDescription.smokeWalls[sir.index] = req; |
374 | 374 | ||
375 | Plugin.Logger.LogInfo($"Smoke {sir} requirements: {req}"); | ||
376 | |||
375 | foreach (var reqScene in req.references.scenes) | 377 | foreach (var reqScene in req.references.scenes) |
376 | { | 378 | { |
377 | GetOrAddListeners(listenersByScene, reqScene).smokeWalls[sir] = req; | 379 | GetOrAddListeners(listenersByScene, reqScene).smokeWalls[sir] = req; |
diff --git a/GameState.cs b/GameState.cs index d3bea06..bdf5760 100644 --- a/GameState.cs +++ b/GameState.cs | |||
@@ -112,7 +112,7 @@ namespace ManifoldGardenArchipelago | |||
112 | { | 112 | { |
113 | foreach (var location in listeners.locations) | 113 | foreach (var location in listeners.locations) |
114 | { | 114 | { |
115 | if (location.Value.Check()) | 115 | if (location.Value.Check() == Requirement.Decision.Yes) |
116 | { | 116 | { |
117 | Plugin.archipelagoManager.CheckLocation(location.Key); | 117 | Plugin.archipelagoManager.CheckLocation(location.Key); |
118 | } | 118 | } |
@@ -120,8 +120,14 @@ namespace ManifoldGardenArchipelago | |||
120 | 120 | ||
121 | foreach (var door in listeners.doors) | 121 | foreach (var door in listeners.doors) |
122 | { | 122 | { |
123 | bool shouldOpen = door.Value.Check(); | 123 | Requirement.Decision decision = door.Value.Check(); |
124 | Plugin.Logger.LogInfo($"{door.Key}: {door.Value} -> {shouldOpen}"); | 124 | if (decision == Requirement.Decision.Maybe) |
125 | { | ||
126 | continue; | ||
127 | } | ||
128 | |||
129 | bool shouldOpen = (decision == Requirement.Decision.Yes); | ||
130 | //Plugin.Logger.LogInfo($"{door.Key}: {door.Value} -> {shouldOpen}"); | ||
125 | 131 | ||
126 | if (SceneManager.GetSceneByName(door.Key.scene) is Scene doorScene && doorScene.isLoaded) | 132 | if (SceneManager.GetSceneByName(door.Key.scene) is Scene doorScene && doorScene.isLoaded) |
127 | { | 133 | { |
@@ -142,11 +148,34 @@ namespace ManifoldGardenArchipelago | |||
142 | } | 148 | } |
143 | } | 149 | } |
144 | 150 | ||
151 | foreach (var smokeWall in listeners.smokeWalls) | ||
152 | { | ||
153 | Requirement.Decision decision = smokeWall.Value.Check(); | ||
154 | if (decision == Requirement.Decision.Maybe) | ||
155 | { | ||
156 | continue; | ||
157 | } | ||
158 | |||
159 | bool shouldOpen = (decision == Requirement.Decision.Yes); | ||
160 | //Plugin.Logger.LogInfo($"{smokeWall.Key}: {smokeWall.Value} -> {shouldOpen}"); | ||
145 | 161 | ||
162 | if (SceneManager.GetSceneByName(smokeWall.Key.scene) is Scene smokeScene && smokeScene.isLoaded) | ||
163 | { | ||
164 | LevelSystems levelSystems = GetLevelSystems(smokeScene); | ||
165 | if (levelSystems.isActiveAndEnabled) | ||
166 | { | ||
167 | SolidStateController ldc = levelSystems.chainListeners[smokeWall.Key.index].GetComponent<SolidStateController>(); | ||
168 | ldc.chains.Clear(); | ||
169 | |||
170 | FieldInfo fieldInfo = typeof(SolidStateController).GetField("m_isSolid", BindingFlags.Instance | BindingFlags.NonPublic); | ||
171 | fieldInfo.SetValue(ldc, !shouldOpen); | ||
172 | } | ||
173 | } | ||
174 | } | ||
146 | 175 | ||
147 | foreach (var worldGrow in listeners.worldGrows) | 176 | foreach (var worldGrow in listeners.worldGrows) |
148 | { | 177 | { |
149 | if (worldGrow.Value.Check()) | 178 | if (worldGrow.Value.Check() == Requirement.Decision.Yes) |
150 | { | 179 | { |
151 | if (SceneManager.GetSceneByName(worldGrow.Key.scene) is Scene gardenScene && gardenScene.isLoaded) | 180 | if (SceneManager.GetSceneByName(worldGrow.Key.scene) is Scene gardenScene && gardenScene.isLoaded) |
152 | { | 181 | { |
diff --git a/GameplayPatches.cs b/GameplayPatches.cs index c21bf97..3090a98 100644 --- a/GameplayPatches.cs +++ b/GameplayPatches.cs | |||
@@ -1,5 +1,6 @@ | |||
1 | using HarmonyLib; | 1 | using HarmonyLib; |
2 | using System.Reflection; | 2 | using System.Reflection; |
3 | using UnityEngine; | ||
3 | 4 | ||
4 | namespace ManifoldGardenArchipelago | 5 | namespace ManifoldGardenArchipelago |
5 | { | 6 | { |
@@ -14,9 +15,16 @@ namespace ManifoldGardenArchipelago | |||
14 | { | 15 | { |
15 | if (sceneDescription.doors.TryGetValue(sir.index, out var requirement)) | 16 | if (sceneDescription.doors.TryGetValue(sir.index, out var requirement)) |
16 | { | 17 | { |
17 | if (open != requirement.Check()) | 18 | Requirement.Decision decision = requirement.Check(); |
19 | |||
20 | if (decision != Requirement.Decision.Maybe) | ||
18 | { | 21 | { |
19 | return false; | 22 | bool shouldOpen = (decision == Requirement.Decision.Yes); |
23 | |||
24 | if (open != shouldOpen) | ||
25 | { | ||
26 | return false; | ||
27 | } | ||
20 | } | 28 | } |
21 | } | 29 | } |
22 | } | 30 | } |
@@ -38,15 +46,27 @@ namespace ManifoldGardenArchipelago | |||
38 | return; | 46 | return; |
39 | } | 47 | } |
40 | 48 | ||
41 | if (setPressed) | 49 | Plugin.slotSave.ActivatedButtons[sir] = setPressed; |
42 | { | 50 | |
43 | Plugin.slotSave.ActivatedButtons.Add(sir); | 51 | GameState.EvaluateGameStateListeners(listeners); |
44 | } | 52 | } |
45 | else | 53 | } |
54 | |||
55 | [HarmonyPatch(typeof(ButtonLineActivator), "ForceButtonPressedImmediately")] | ||
56 | static class ButtonLineActivatorForceButtonPressedImmediatelyPatch | ||
57 | { | ||
58 | static void Prefix(ButtonLineActivator __instance, bool _pressed) | ||
59 | { | ||
60 | SceneItemReference sir = GameState.GetButtonSceneReference(__instance); | ||
61 | Plugin.Logger.LogInfo($"Button {sir} forced state {_pressed}"); | ||
62 | |||
63 | if (!GameData.listenersByButton.TryGetValue(sir, out GameStateListeners listeners)) | ||
46 | { | 64 | { |
47 | Plugin.slotSave.ActivatedButtons.Remove(sir); | 65 | return; |
48 | } | 66 | } |
49 | 67 | ||
68 | Plugin.slotSave.ActivatedButtons[sir] = _pressed; | ||
69 | |||
50 | GameState.EvaluateGameStateListeners(listeners); | 70 | GameState.EvaluateGameStateListeners(listeners); |
51 | } | 71 | } |
52 | } | 72 | } |
@@ -64,14 +84,7 @@ namespace ManifoldGardenArchipelago | |||
64 | return; | 84 | return; |
65 | } | 85 | } |
66 | 86 | ||
67 | if (active) | 87 | Plugin.slotSave.ActivatedSockets[sir] = active; |
68 | { | ||
69 | Plugin.slotSave.ActivatedSockets.Add(sir); | ||
70 | } | ||
71 | else | ||
72 | { | ||
73 | Plugin.slotSave.ActivatedSockets.Remove(sir); | ||
74 | } | ||
75 | 88 | ||
76 | GameState.EvaluateGameStateListeners(listeners); | 89 | GameState.EvaluateGameStateListeners(listeners); |
77 | } | 90 | } |
@@ -90,15 +103,46 @@ namespace ManifoldGardenArchipelago | |||
90 | return; | 103 | return; |
91 | } | 104 | } |
92 | 105 | ||
93 | if (setActive) | 106 | Plugin.slotSave.ActivatedPads[sir] = setActive; |
107 | |||
108 | GameState.EvaluateGameStateListeners(listeners); | ||
109 | } | ||
110 | } | ||
111 | |||
112 | [HarmonyPatch(typeof(CubeLineActivator), "ForceActivatorToTurnOnAsCorrectCubeOnSwitch")] | ||
113 | static class CubeLineActivatorForceActivatorToTurnOnAsCorrectCubeOnSwitchPatch | ||
114 | { | ||
115 | static void Prefix(CubeLineActivator __instance) | ||
116 | { | ||
117 | SceneItemReference sir = GameState.GetPadSceneReference(__instance); | ||
118 | Plugin.Logger.LogInfo($"Pad {sir} forced on"); | ||
119 | |||
120 | if (!GameData.listenersByPad.TryGetValue(sir, out GameStateListeners listeners)) | ||
94 | { | 121 | { |
95 | Plugin.slotSave.ActivatedPads.Add(sir); | 122 | return; |
96 | } | 123 | } |
97 | else | 124 | |
125 | Plugin.slotSave.ActivatedPads[sir] = true; | ||
126 | |||
127 | GameState.EvaluateGameStateListeners(listeners); | ||
128 | } | ||
129 | } | ||
130 | |||
131 | [HarmonyPatch(typeof(CubeLineActivator), "ForceActivatorToTurnOffAsCorrectCubeNotOnSwitch")] | ||
132 | static class CubeLineActivatorForceActivatorToTurnOffAsCorrectCubeNotOnSwitchPatch | ||
133 | { | ||
134 | static void Prefix(CubeLineActivator __instance) | ||
135 | { | ||
136 | SceneItemReference sir = GameState.GetPadSceneReference(__instance); | ||
137 | Plugin.Logger.LogInfo($"Pad {sir} forced off"); | ||
138 | |||
139 | if (!GameData.listenersByPad.TryGetValue(sir, out GameStateListeners listeners)) | ||
98 | { | 140 | { |
99 | Plugin.slotSave.ActivatedPads.Remove(sir); | 141 | return; |
100 | } | 142 | } |
101 | 143 | ||
144 | Plugin.slotSave.ActivatedPads[sir] = false; | ||
145 | |||
102 | GameState.EvaluateGameStateListeners(listeners); | 146 | GameState.EvaluateGameStateListeners(listeners); |
103 | } | 147 | } |
104 | } | 148 | } |
@@ -109,21 +153,14 @@ namespace ManifoldGardenArchipelago | |||
109 | static void Prefix(WaterDetector __instance, bool setActive) | 153 | static void Prefix(WaterDetector __instance, bool setActive) |
110 | { | 154 | { |
111 | SceneItemReference sir = GameState.GetWaterwheelSceneReference(__instance); | 155 | SceneItemReference sir = GameState.GetWaterwheelSceneReference(__instance); |
112 | Plugin.Logger.LogInfo($"Waterwheel {sir} state {setActive}"); | 156 | //Plugin.Logger.LogInfo($"Waterwheel {sir} state {setActive}"); |
113 | 157 | ||
114 | if (!GameData.listenersByWaterwheel.TryGetValue(sir, out GameStateListeners listeners)) | 158 | if (!GameData.listenersByWaterwheel.TryGetValue(sir, out GameStateListeners listeners)) |
115 | { | 159 | { |
116 | return; | 160 | return; |
117 | } | 161 | } |
118 | 162 | ||
119 | if (setActive) | 163 | Plugin.slotSave.ActivatedWaterwheels[sir] = setActive; |
120 | { | ||
121 | Plugin.slotSave.ActivatedWaterwheels.Add(sir); | ||
122 | } | ||
123 | else | ||
124 | { | ||
125 | Plugin.slotSave.ActivatedWaterwheels.Remove(sir); | ||
126 | } | ||
127 | 164 | ||
128 | GameState.EvaluateGameStateListeners(listeners); | 165 | GameState.EvaluateGameStateListeners(listeners); |
129 | } | 166 | } |
@@ -142,14 +179,7 @@ namespace ManifoldGardenArchipelago | |||
142 | return; | 179 | return; |
143 | } | 180 | } |
144 | 181 | ||
145 | if (newState == SphereController.State.AtDestination) | 182 | Plugin.slotSave.ActivatedSpheres[sir] = (newState == SphereController.State.AtDestination); |
146 | { | ||
147 | Plugin.slotSave.ActivatedSpheres.Add(sir); | ||
148 | } | ||
149 | else | ||
150 | { | ||
151 | Plugin.slotSave.ActivatedSpheres.Remove(sir); | ||
152 | } | ||
153 | 183 | ||
154 | GameState.EvaluateGameStateListeners(listeners); | 184 | GameState.EvaluateGameStateListeners(listeners); |
155 | } | 185 | } |
@@ -166,10 +196,22 @@ namespace ManifoldGardenArchipelago | |||
166 | { | 196 | { |
167 | if (sceneDescription.doors.TryGetValue(sir.index, out var requirement)) | 197 | if (sceneDescription.doors.TryGetValue(sir.index, out var requirement)) |
168 | { | 198 | { |
169 | FieldInfo fieldInfo = typeof(LineDoorController).GetField("doorShouldOpenImmediatelyOnEnable", BindingFlags.Instance | BindingFlags.NonPublic); | 199 | Requirement.Decision decision = requirement.Check(); |
170 | fieldInfo.SetValue(__instance, requirement.Check()); | 200 | |
201 | if (decision != Requirement.Decision.Maybe) | ||
202 | { | ||
203 | bool shouldOpen = (decision == Requirement.Decision.Yes); | ||
204 | |||
205 | FieldInfo fieldInfo = typeof(LineDoorController).GetField("doorShouldOpenImmediatelyOnEnable", BindingFlags.Instance | BindingFlags.NonPublic); | ||
206 | fieldInfo.SetValue(__instance, shouldOpen); | ||
207 | |||
208 | FieldInfo animatorInfo = typeof(LineDoorController).GetField("m_animator", BindingFlags.Instance | BindingFlags.NonPublic); | ||
209 | Animator animator = (Animator)animatorInfo.GetValue(__instance); | ||
210 | |||
211 | AnimatorExt.PlayDoorStateThenDisable(animator, __instance.DoorOpenAnimationString, shouldOpen ? 1f : -1f, true); | ||
171 | 212 | ||
172 | __instance.chains.Clear(); | 213 | __instance.chains.Clear(); |
214 | } | ||
173 | } | 215 | } |
174 | } | 216 | } |
175 | } | 217 | } |
@@ -180,16 +222,32 @@ namespace ManifoldGardenArchipelago | |||
180 | { | 222 | { |
181 | static void Prefix(LineDoorController __instance) | 223 | static void Prefix(LineDoorController __instance) |
182 | { | 224 | { |
225 | //Plugin.Logger.LogInfo("LineDoorControllerOnLoadPatch entered"); | ||
183 | SceneItemReference sir = GameState.GetChainListenerSceneReference(__instance); | 226 | SceneItemReference sir = GameState.GetChainListenerSceneReference(__instance); |
227 | //Plugin.Logger.LogInfo($"Got sir {sir}"); | ||
184 | 228 | ||
185 | if (GameData.scenes.TryGetValue(sir.scene, out var sceneDescription)) | 229 | if (GameData.scenes.TryGetValue(sir.scene, out var sceneDescription)) |
186 | { | 230 | { |
231 | //Plugin.Logger.LogInfo($"{sceneDescription.doors.Count} doors in scene"); | ||
187 | if (sceneDescription.doors.TryGetValue(sir.index, out var requirement)) | 232 | if (sceneDescription.doors.TryGetValue(sir.index, out var requirement)) |
188 | { | 233 | { |
189 | FieldInfo fieldInfo = typeof(LineDoorController).GetField("doorShouldOpenImmediatelyOnEnable", BindingFlags.Instance | BindingFlags.NonPublic); | 234 | //Plugin.Logger.LogInfo($"Found req {requirement}"); |
190 | fieldInfo.SetValue(__instance, requirement.Check()); | 235 | Requirement.Decision decision = requirement.Check(); |
236 | |||
237 | if (decision != Requirement.Decision.Maybe) | ||
238 | { | ||
239 | bool shouldOpen = (decision == Requirement.Decision.Yes); | ||
240 | |||
241 | FieldInfo fieldInfo = typeof(LineDoorController).GetField("doorShouldOpenImmediatelyOnEnable", BindingFlags.Instance | BindingFlags.NonPublic); | ||
242 | fieldInfo.SetValue(__instance, shouldOpen); | ||
243 | |||
244 | FieldInfo animatorInfo = typeof(LineDoorController).GetField("m_animator", BindingFlags.Instance | BindingFlags.NonPublic); | ||
245 | Animator animator = (Animator)animatorInfo.GetValue(__instance); | ||
246 | |||
247 | AnimatorExt.PlayDoorStateThenDisable(animator, __instance.DoorOpenAnimationString, shouldOpen ? 1f : -1f, true); | ||
191 | 248 | ||
192 | __instance.chains.Clear(); | 249 | __instance.chains.Clear(); |
250 | } | ||
193 | } | 251 | } |
194 | } | 252 | } |
195 | } | 253 | } |
@@ -204,6 +262,7 @@ namespace ManifoldGardenArchipelago | |||
204 | 262 | ||
205 | if (GameData.listenersByScene.TryGetValue(newLevel.levelName, out var listeners)) | 263 | if (GameData.listenersByScene.TryGetValue(newLevel.levelName, out var listeners)) |
206 | { | 264 | { |
265 | Plugin.Logger.LogInfo($"Evaluating for scene {newLevel.levelName}"); | ||
207 | GameState.EvaluateGameStateListeners(listeners); | 266 | GameState.EvaluateGameStateListeners(listeners); |
208 | } | 267 | } |
209 | } | 268 | } |
@@ -227,7 +286,7 @@ namespace ManifoldGardenArchipelago | |||
227 | 286 | ||
228 | if (GameData.scenes.TryGetValue(sir.scene, out var sceneDescription) && | 287 | if (GameData.scenes.TryGetValue(sir.scene, out var sceneDescription) && |
229 | sceneDescription.worldGrows.TryGetValue(sir.index, out var requirement) && | 288 | sceneDescription.worldGrows.TryGetValue(sir.index, out var requirement) && |
230 | requirement.Check()) | 289 | requirement.Check() == Requirement.Decision.Yes) |
231 | { | 290 | { |
232 | FieldInfo fieldInfo = typeof(DarkModeCollapsedCubeWorldGrow).GetField("m_grown", BindingFlags.Instance | BindingFlags.NonPublic); | 291 | FieldInfo fieldInfo = typeof(DarkModeCollapsedCubeWorldGrow).GetField("m_grown", BindingFlags.Instance | BindingFlags.NonPublic); |
233 | fieldInfo.SetValue(__instance, true); | 292 | fieldInfo.SetValue(__instance, true); |
@@ -235,6 +294,54 @@ namespace ManifoldGardenArchipelago | |||
235 | } | 294 | } |
236 | } | 295 | } |
237 | 296 | ||
297 | [HarmonyPatch(typeof(SolidStateController), nameof(SolidStateController.OnChainFillComplete))] | ||
298 | static class SolidStateControllerOnChainFillCompletePatch | ||
299 | { | ||
300 | static bool Prefix(SolidStateController __instance) | ||
301 | { | ||
302 | SceneItemReference sir = GameState.GetChainListenerSceneReference(__instance); | ||
303 | |||
304 | if (GameData.scenes.TryGetValue(sir.scene, out var sceneDescription)) | ||
305 | { | ||
306 | if (sceneDescription.smokeWalls.TryGetValue(sir.index, out var requirement)) | ||
307 | { | ||
308 | Requirement.Decision decision = requirement.Check(); | ||
309 | |||
310 | if (decision == Requirement.Decision.No) | ||
311 | { | ||
312 | return false; | ||
313 | } | ||
314 | } | ||
315 | } | ||
316 | |||
317 | return true; | ||
318 | } | ||
319 | } | ||
320 | |||
321 | [HarmonyPatch(typeof(SolidStateController), nameof(SolidStateController.OnUnfilled))] | ||
322 | static class SolidStateControllerOnUnfilledPatch | ||
323 | { | ||
324 | static bool Prefix(SolidStateController __instance) | ||
325 | { | ||
326 | SceneItemReference sir = GameState.GetChainListenerSceneReference(__instance); | ||
327 | |||
328 | if (GameData.scenes.TryGetValue(sir.scene, out var sceneDescription)) | ||
329 | { | ||
330 | if (sceneDescription.smokeWalls.TryGetValue(sir.index, out var requirement)) | ||
331 | { | ||
332 | Requirement.Decision decision = requirement.Check(); | ||
333 | |||
334 | if (decision == Requirement.Decision.Yes) | ||
335 | { | ||
336 | return false; | ||
337 | } | ||
338 | } | ||
339 | } | ||
340 | |||
341 | return true; | ||
342 | } | ||
343 | } | ||
344 | |||
238 | [HarmonyPatch(typeof(DarkModeCollider), nameof(DarkModeCollider.OnLevelUpdate))] | 345 | [HarmonyPatch(typeof(DarkModeCollider), nameof(DarkModeCollider.OnLevelUpdate))] |
239 | static class DarkModeColliderOnLevelUpdatePatch | 346 | static class DarkModeColliderOnLevelUpdatePatch |
240 | { | 347 | { |
diff --git a/Requirements.cs b/Requirements.cs index 987a299..81ecf36 100644 --- a/Requirements.cs +++ b/Requirements.cs | |||
@@ -50,9 +50,16 @@ namespace ManifoldGardenArchipelago | |||
50 | 50 | ||
51 | public abstract class Requirement | 51 | public abstract class Requirement |
52 | { | 52 | { |
53 | public enum Decision | ||
54 | { | ||
55 | Maybe, | ||
56 | No, | ||
57 | Yes, | ||
58 | } | ||
59 | |||
53 | public RequirementReferences references = new(); | 60 | public RequirementReferences references = new(); |
54 | 61 | ||
55 | public abstract bool Check(); | 62 | public abstract Decision Check(); |
56 | } | 63 | } |
57 | 64 | ||
58 | public class AndRequirement : Requirement | 65 | public class AndRequirement : Requirement |
@@ -69,9 +76,19 @@ namespace ManifoldGardenArchipelago | |||
69 | } | 76 | } |
70 | } | 77 | } |
71 | 78 | ||
72 | public override bool Check() | 79 | public override Decision Check() |
73 | { | 80 | { |
74 | return _requirements.All(x => x.Check()); | 81 | foreach (var requirement in _requirements) |
82 | { | ||
83 | Decision decision = requirement.Check(); | ||
84 | |||
85 | if (decision != Decision.Yes) | ||
86 | { | ||
87 | return decision; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | return Decision.Yes; | ||
75 | } | 92 | } |
76 | 93 | ||
77 | public override string ToString() | 94 | public override string ToString() |
@@ -94,9 +111,19 @@ namespace ManifoldGardenArchipelago | |||
94 | } | 111 | } |
95 | } | 112 | } |
96 | 113 | ||
97 | public override bool Check() | 114 | public override Decision Check() |
98 | { | 115 | { |
99 | return _requirements.Any(x => x.Check()); | 116 | foreach (var requirement in _requirements) |
117 | { | ||
118 | Decision decision = requirement.Check(); | ||
119 | |||
120 | if (decision != Decision.No) | ||
121 | { | ||
122 | return decision; | ||
123 | } | ||
124 | } | ||
125 | |||
126 | return Decision.No; | ||
100 | } | 127 | } |
101 | 128 | ||
102 | public override string ToString() | 129 | public override string ToString() |
@@ -116,9 +143,9 @@ namespace ManifoldGardenArchipelago | |||
116 | references.items.Add(itemName); | 143 | references.items.Add(itemName); |
117 | } | 144 | } |
118 | 145 | ||
119 | public override bool Check() | 146 | public override Decision Check() |
120 | { | 147 | { |
121 | return Plugin.archipelagoManager.HasItem(_itemName); | 148 | return Plugin.archipelagoManager.HasItem(_itemName) ? Decision.Yes : Decision.No; |
122 | } | 149 | } |
123 | 150 | ||
124 | public override string ToString() | 151 | public override string ToString() |
@@ -138,9 +165,9 @@ namespace ManifoldGardenArchipelago | |||
138 | references.scenes.Add(sceneName); | 165 | references.scenes.Add(sceneName); |
139 | } | 166 | } |
140 | 167 | ||
141 | public override bool Check() | 168 | public override Decision Check() |
142 | { | 169 | { |
143 | return Plugin.slotSave.VisitedScenes.Contains(_sceneName); | 170 | return Plugin.slotSave.VisitedScenes.Contains(_sceneName) ? Decision.Yes : Decision.No; |
144 | } | 171 | } |
145 | 172 | ||
146 | public override string ToString() | 173 | public override string ToString() |
@@ -161,9 +188,16 @@ namespace ManifoldGardenArchipelago | |||
161 | references.buttons.Add(button); | 188 | references.buttons.Add(button); |
162 | } | 189 | } |
163 | 190 | ||
164 | public override bool Check() | 191 | public override Decision Check() |
165 | { | 192 | { |
166 | return Plugin.slotSave.ActivatedButtons.Contains(_button); | 193 | if (Plugin.slotSave.ActivatedButtons.TryGetValue(_button, out bool value)) |
194 | { | ||
195 | return value ? Decision.Yes : Decision.No; | ||
196 | } | ||
197 | else | ||
198 | { | ||
199 | return Decision.Maybe; | ||
200 | } | ||
167 | } | 201 | } |
168 | 202 | ||
169 | public override string ToString() | 203 | public override string ToString() |
@@ -184,9 +218,16 @@ namespace ManifoldGardenArchipelago | |||
184 | references.sockets.Add(socket); | 218 | references.sockets.Add(socket); |
185 | } | 219 | } |
186 | 220 | ||
187 | public override bool Check() | 221 | public override Decision Check() |
188 | { | 222 | { |
189 | return Plugin.slotSave.ActivatedSockets.Contains(_socket); | 223 | if (Plugin.slotSave.ActivatedSockets.TryGetValue(_socket, out bool value)) |
224 | { | ||
225 | return value ? Decision.Yes : Decision.No; | ||
226 | } | ||
227 | else | ||
228 | { | ||
229 | return Decision.Maybe; | ||
230 | } | ||
190 | } | 231 | } |
191 | 232 | ||
192 | public override string ToString() | 233 | public override string ToString() |
@@ -207,9 +248,16 @@ namespace ManifoldGardenArchipelago | |||
207 | references.pads.Add(pad); | 248 | references.pads.Add(pad); |
208 | } | 249 | } |
209 | 250 | ||
210 | public override bool Check() | 251 | public override Decision Check() |
211 | { | 252 | { |
212 | return Plugin.slotSave.ActivatedPads.Contains(_pad); | 253 | if (Plugin.slotSave.ActivatedPads.TryGetValue(_pad, out bool value)) |
254 | { | ||
255 | return value ? Decision.Yes : Decision.No; | ||
256 | } | ||
257 | else | ||
258 | { | ||
259 | return Decision.Maybe; | ||
260 | } | ||
213 | } | 261 | } |
214 | 262 | ||
215 | public override string ToString() | 263 | public override string ToString() |
@@ -230,9 +278,16 @@ namespace ManifoldGardenArchipelago | |||
230 | references.waterwheels.Add(waterwheel); | 278 | references.waterwheels.Add(waterwheel); |
231 | } | 279 | } |
232 | 280 | ||
233 | public override bool Check() | 281 | public override Decision Check() |
234 | { | 282 | { |
235 | return Plugin.slotSave.ActivatedWaterwheels.Contains(_waterwheel); | 283 | if (Plugin.slotSave.ActivatedWaterwheels.TryGetValue(_waterwheel, out bool value)) |
284 | { | ||
285 | return value ? Decision.Yes : Decision.No; | ||
286 | } | ||
287 | else | ||
288 | { | ||
289 | return Decision.Maybe; | ||
290 | } | ||
236 | } | 291 | } |
237 | 292 | ||
238 | public override string ToString() | 293 | public override string ToString() |
@@ -253,9 +308,16 @@ namespace ManifoldGardenArchipelago | |||
253 | references.spheres.Add(sphere); | 308 | references.spheres.Add(sphere); |
254 | } | 309 | } |
255 | 310 | ||
256 | public override bool Check() | 311 | public override Decision Check() |
257 | { | 312 | { |
258 | return Plugin.slotSave.ActivatedSpheres.Contains(_sphere); | 313 | if (Plugin.slotSave.ActivatedSpheres.TryGetValue(_sphere, out bool value)) |
314 | { | ||
315 | return value ? Decision.Yes : Decision.No; | ||
316 | } | ||
317 | else | ||
318 | { | ||
319 | return Decision.Maybe; | ||
320 | } | ||
259 | } | 321 | } |
260 | 322 | ||
261 | public override string ToString() | 323 | public override string ToString() |
@@ -275,9 +337,22 @@ namespace ManifoldGardenArchipelago | |||
275 | references = requirement.references; | 337 | references = requirement.references; |
276 | } | 338 | } |
277 | 339 | ||
278 | public override bool Check() | 340 | public override Decision Check() |
279 | { | 341 | { |
280 | return !_requirement.Check(); | 342 | Decision subdec = _requirement.Check(); |
343 | |||
344 | if (subdec == Decision.Yes) | ||
345 | { | ||
346 | return Decision.No; | ||
347 | } | ||
348 | else if (subdec == Decision.No) | ||
349 | { | ||
350 | return Decision.Yes; | ||
351 | } | ||
352 | else | ||
353 | { | ||
354 | return Decision.Maybe; | ||
355 | } | ||
281 | } | 356 | } |
282 | 357 | ||
283 | public override string ToString() | 358 | public override string ToString() |
diff --git a/SlotSave.cs b/SlotSave.cs index 6261890..dfa18bc 100644 --- a/SlotSave.cs +++ b/SlotSave.cs | |||
@@ -1,18 +1,14 @@ | |||
1 | using System; | 1 | using System.Collections.Generic; |
2 | using System.Collections.Generic; | ||
3 | using System.Linq; | ||
4 | using System.Text; | ||
5 | using System.Threading.Tasks; | ||
6 | 2 | ||
7 | namespace ManifoldGardenArchipelago | 3 | namespace ManifoldGardenArchipelago |
8 | { | 4 | { |
9 | public class SlotSave | 5 | public class SlotSave |
10 | { | 6 | { |
11 | public readonly HashSet<string> VisitedScenes = []; | 7 | public readonly HashSet<string> VisitedScenes = []; |
12 | public readonly HashSet<SceneItemReference> ActivatedButtons = []; | 8 | public readonly Dictionary<SceneItemReference, bool> ActivatedButtons = []; |
13 | public readonly HashSet<SceneItemReference> ActivatedSockets = []; | 9 | public readonly Dictionary<SceneItemReference, bool> ActivatedSockets = []; |
14 | public readonly HashSet<SceneItemReference> ActivatedPads = []; | 10 | public readonly Dictionary<SceneItemReference, bool> ActivatedPads = []; |
15 | public readonly HashSet<SceneItemReference> ActivatedWaterwheels = []; | 11 | public readonly Dictionary<SceneItemReference, bool> ActivatedWaterwheels = []; |
16 | public readonly HashSet<SceneItemReference> ActivatedSpheres = []; | 12 | public readonly Dictionary<SceneItemReference, bool> ActivatedSpheres = []; |
17 | } | 13 | } |
18 | } | 14 | } |
diff --git a/game_data.yaml b/game_data.yaml index 43ccf22..a261be0 100644 --- a/game_data.yaml +++ b/game_data.yaml | |||
@@ -274,24 +274,25 @@ World_612_BlindSpherePuzzle_Optimized: | |||
274 | scene: Hallway_W612_W057_Optimized | 274 | scene: Hallway_W612_W057_Optimized |
275 | index: 0 | 275 | index: 0 |
276 | World_018_PastaTile_Optimized: | 276 | World_018_PastaTile_Optimized: |
277 | 0: | 277 | doors: |
278 | or: | 278 | 0: |
279 | - button: 0 | 279 | or: |
280 | - pad: 0 | 280 | - button: 0 |
281 | 3: | 281 | - pad: 0 |
282 | or: | 282 | 3: |
283 | - pad: 0 | 283 | or: |
284 | - sphere: | 284 | - pad: 0 |
285 | scene: Hallway_W018_W003B_Optimized | 285 | - sphere: |
286 | index: 0 | 286 | scene: Hallway_W018_W003B_Optimized |
287 | 5: | 287 | index: 0 |
288 | or: | 288 | 5: |
289 | - button: 1 | 289 | or: |
290 | - button: | 290 | - button: 1 |
291 | - scene: Hallway_W018_W063_Optimized | 291 | - button: |
292 | index: 0 | 292 | - scene: Hallway_W018_W063_Optimized |
293 | - scene: Hallway_W018_W063_Optimized | 293 | index: 0 |
294 | index: 1 | 294 | - scene: Hallway_W018_W063_Optimized |
295 | index: 1 | ||
295 | Hallway_W018_W003B_Optimized: | 296 | Hallway_W018_W003B_Optimized: |
296 | locations: | 297 | locations: |
297 | Mini Sphere Puzzle Solved: | 298 | Mini Sphere Puzzle Solved: |
@@ -559,6 +560,7 @@ World_026_Library_Optimized: | |||
559 | smoke: | 560 | smoke: |
560 | 3: | 561 | 3: |
561 | item: Library - Yellow Smoke Wall | 562 | item: Library - Yellow Smoke Wall |
563 | entry: World_026_Library_Optimized | ||
562 | Hallway_W026_W002_Optimized: | 564 | Hallway_W026_W002_Optimized: |
563 | locations: | 565 | locations: |
564 | Yellow Cube Fetch Quest Completed: | 566 | Yellow Cube Fetch Quest Completed: |