about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--AnodyneArchipelago/Patches/GameplayPatches.cs228
-rw-r--r--AnodyneArchipelago/Patches/StatePatches.cs39
-rw-r--r--AnodyneArchipelago/Plugin.cs258
3 files changed, 268 insertions, 257 deletions
diff --git a/AnodyneArchipelago/Patches/GameplayPatches.cs b/AnodyneArchipelago/Patches/GameplayPatches.cs new file mode 100644 index 0000000..ada4159 --- /dev/null +++ b/AnodyneArchipelago/Patches/GameplayPatches.cs
@@ -0,0 +1,228 @@
1using AnodyneSharp.Entities.Events;
2using AnodyneSharp.Entities.Gadget.Treasures;
3using AnodyneSharp.Entities.Gadget;
4using AnodyneSharp.Entities.Interactive;
5using AnodyneSharp.Entities;
6using AnodyneSharp.Registry;
7using AnodyneSharp.Utilities;
8using HarmonyLib;
9using System;
10using System.Collections.Generic;
11using System.Reflection;
12using Microsoft.Xna.Framework;
13
14namespace AnodyneArchipelago.Patches
15{
16 [HarmonyPatch(typeof(TreasureChest), nameof(TreasureChest.PlayerInteraction))]
17 class ChestInteractPatch
18 {
19 static void Prefix(TreasureChest __instance)
20 {
21 string entityId = PatchHelper.GetEntityPreset(typeof(TreasureChest), __instance).EntityID.ToString();
22 Plugin.Instance.Log.LogInfo($"Touched chest: {entityId}");
23 }
24 }
25
26 [HarmonyPatch(typeof(TreasureChest), "SetTreasure")]
27 class ChestSetTreasurePatch
28 {
29 static bool Prefix(TreasureChest __instance)
30 {
31 Guid entityId = PatchHelper.GetEntityPreset(typeof(TreasureChest), __instance).EntityID;
32 if (Locations.LocationsByGuid.ContainsKey(entityId))
33 {
34 BaseTreasure treasure = new ArchipelagoTreasure(Locations.LocationsByGuid[entityId], __instance.Position);
35
36 FieldInfo treasureField = typeof(TreasureChest).GetField("_treasure", BindingFlags.NonPublic | BindingFlags.Instance);
37 treasureField.SetValue(__instance, treasure);
38
39 return false;
40 }
41
42 return true;
43 }
44 }
45
46 [HarmonyPatch(typeof(Big_Key), nameof(Big_Key.PlayerInteraction))]
47 class BigKeyTouchPatch
48 {
49 // We basically just rewrite this method, because we need to get rid of the part that adds the key to the inventory.
50 static bool Prefix(Big_Key __instance, ref bool __result)
51 {
52 MethodInfo statesMethod = typeof(Big_Key).GetMethod("States", BindingFlags.NonPublic | BindingFlags.Instance);
53
54 EntityPreset preset = PatchHelper.GetEntityPreset(typeof(Big_Key), __instance);
55 preset.Alive = false;
56 __instance.Solid = false;
57 GlobalState.StartCutscene = (IEnumerator<AnodyneSharp.States.CutsceneState.CutsceneEvent>)statesMethod.Invoke(__instance, new object[] { });
58 __result = true;
59 return false;
60 }
61 }
62
63 [HarmonyPatch(typeof(Big_Key), "States")]
64 class BigKeyStatesPatch
65 {
66 static void Postfix(Big_Key __instance)
67 {
68 EntityPreset preset = PatchHelper.GetEntityPreset(typeof(Big_Key), __instance);
69
70 if (preset.Frame == 0)
71 {
72 ArchipelagoManager.SendLocation("Temple of the Seeing One - Green Key");
73 }
74 else if (preset.Frame == 1)
75 {
76 ArchipelagoManager.SendLocation("Red Grotto - Red Key");
77 }
78 else if (preset.Frame == 2)
79 {
80 ArchipelagoManager.SendLocation("Mountain Cavern - Blue Key");
81 }
82 }
83 }
84
85 [HarmonyPatch(typeof(HealthCicadaSentinel), nameof(HealthCicadaSentinel.Collided))]
86 class HealthCicadaInteractPatch
87 {
88 static void Prefix(TreasureChest __instance)
89 {
90 Type hcsType = typeof(HealthCicadaSentinel);
91 FieldInfo childField = hcsType.GetField("_child", BindingFlags.NonPublic | BindingFlags.Instance);
92 HealthCicada healthCicada = (HealthCicada)childField.GetValue(__instance);
93
94 EntityPreset preset = PatchHelper.GetEntityPreset(typeof(HealthCicada), healthCicada);
95 Plugin.Instance.Log.LogInfo($"Touched cicada: {preset.EntityID.ToString()}");
96 }
97 }
98
99 [HarmonyPatch(typeof(HealthCicada), nameof(HealthCicada.Update))]
100 class HealthCicadaUpdatePatch
101 {
102 static void Postfix(HealthCicada __instance)
103 {
104 Type cicadaType = typeof(HealthCicada);
105 FieldInfo chirpField = cicadaType.GetField("_chirp", BindingFlags.NonPublic | BindingFlags.Instance);
106 FieldInfo sentinelField = cicadaType.GetField("_sentinel", BindingFlags.NonPublic | BindingFlags.Instance);
107
108 HealthCicadaSentinel sentinel = (HealthCicadaSentinel)sentinelField.GetValue(__instance);
109 Type hcsType = typeof(HealthCicadaSentinel);
110 FieldInfo flyDistanceField = hcsType.GetField("_flyDistance", BindingFlags.NonPublic | BindingFlags.Instance);
111 float flyDistance = (float)flyDistanceField.GetValue(sentinel);
112
113 if (__instance.visible && !(bool)chirpField.GetValue(__instance) && flyDistance > 0)
114 {
115 flyDistanceField.SetValue(sentinel, 0f);
116
117 FieldInfo stateField = cicadaType.GetField("_state", BindingFlags.NonPublic | BindingFlags.Instance);
118 stateField.SetValue(__instance, StateLogic(__instance));
119 }
120 }
121
122 static IEnumerator<string> StateLogic(HealthCicada healthCicada)
123 {
124 EntityPreset preset = PatchHelper.GetEntityPreset(typeof(HealthCicada), healthCicada);
125
126 while (!MathUtilities.MoveTo(ref healthCicada.opacity, 0.0f, 0.6f))
127 yield return "FadingOut";
128 preset.Alive = false;
129
130 FieldInfo sentinelField = typeof(HealthCicada).GetField("_sentinel", BindingFlags.NonPublic | BindingFlags.Instance);
131 HealthCicadaSentinel sentinel = (HealthCicadaSentinel)sentinelField.GetValue(healthCicada);
132
133 sentinel.exists = false;
134
135 if (Locations.LocationsByGuid.ContainsKey(preset.EntityID))
136 {
137 ArchipelagoManager.SendLocation(Locations.LocationsByGuid[preset.EntityID]);
138 }
139 }
140 }
141
142 [HarmonyPatch(typeof(EntityPreset), nameof(EntityPreset.Create))]
143 class EntityPresetCreatePatch
144 {
145 static void Postfix(EntityPreset __instance, Entity __result)
146 {
147 if (__instance.Type.FullName == "AnodyneSharp.Entities.Interactive.DungeonStatue")
148 {
149 __result.Position = __instance.Position + new Vector2(1f, 32f);
150
151 string eventName = "StatueMoved_";
152 Facing moveDir = Facing.RIGHT;
153 if (__instance.Frame == 0)
154 {
155 eventName += "Temple";
156 moveDir = Facing.UP;
157 }
158 else if (__instance.Frame == 1)
159 {
160 eventName += "Grotto";
161 }
162 else if (__instance.Frame == 2)
163 {
164 eventName += "Mountain";
165 }
166
167 if (GlobalState.events.GetEvent(eventName) > 0)
168 {
169 __result.Position += Entity.FacingDirection(moveDir) * 32f;
170 }
171 }
172 else if (__instance.Type.FullName.StartsWith("AnodyneSharp.Entities.Decorations.RedCave"))
173 {
174 string side = __instance.Type.FullName.Substring(41);
175 int requiredGrottos = 0;
176 if (side == "Left")
177 {
178 requiredGrottos = 1;
179 }
180 else if (side == "Right")
181 {
182 requiredGrottos = 2;
183 }
184 else if (side == "North")
185 {
186 requiredGrottos = 3;
187 }
188
189 if (GlobalState.events.GetEvent("ProgressiveRedGrotto") < requiredGrottos)
190 {
191 __result.exists = false;
192 GlobalState.SpawnEntity((Entity)new DoorToggle(__result.Position, __result.width, __result.height));
193 }
194 }
195 else if (__instance.Type.FullName == "AnodyneSharp.Entities.Interactive.Npc.MitraCliff")
196 {
197 // We want to get rid of this scene entirely.
198 __instance.Alive = false;
199 __result.exists = false;
200 }
201 }
202 }
203
204 [HarmonyPatch]
205 class BreakChainPatch
206 {
207 static MethodInfo TargetMethod()
208 {
209 return typeof(Red_Pillar).GetNestedType("Chain", BindingFlags.NonPublic).GetMethod("Collided");
210 }
211
212 static void Postfix(object __instance)
213 {
214 Type chainType = typeof(Red_Pillar).GetNestedType("Chain", BindingFlags.NonPublic);
215 FieldInfo parentField = chainType.GetField("_parent", BindingFlags.NonPublic | BindingFlags.Instance);
216
217 Red_Pillar redPillar = (Red_Pillar)parentField.GetValue(__instance);
218 EntityPreset preset = PatchHelper.GetEntityPreset(typeof(Red_Pillar), redPillar);
219
220 Plugin.Instance.Log.LogInfo($"Broke red chain: {preset.EntityID.ToString()}");
221
222 if (Locations.LocationsByGuid.ContainsKey(preset.EntityID))
223 {
224 ArchipelagoManager.SendLocation(Locations.LocationsByGuid[preset.EntityID]);
225 }
226 }
227 }
228}
diff --git a/AnodyneArchipelago/Patches/StatePatches.cs b/AnodyneArchipelago/Patches/StatePatches.cs new file mode 100644 index 0000000..f6acc62 --- /dev/null +++ b/AnodyneArchipelago/Patches/StatePatches.cs
@@ -0,0 +1,39 @@
1using AnodyneSharp.Entities;
2using AnodyneSharp.Registry;
3using AnodyneSharp.States;
4using AnodyneSharp;
5using HarmonyLib;
6using System.Reflection;
7
8namespace AnodyneArchipelago.Patches
9{
10 [HarmonyPatch(typeof(AnodyneGame), "Update")]
11 class GameUpdatePatch
12 {
13 static void Postfix()
14 {
15 ArchipelagoManager.Update();
16 }
17 }
18
19 [HarmonyPatch(typeof(PlayState), nameof(PlayState.Create))]
20 class PlayStateCreatePatch
21 {
22 static void Prefix(PlayState __instance)
23 {
24 // Get player for later access.
25 FieldInfo playerField = typeof(PlayState).GetField("_player", BindingFlags.NonPublic | BindingFlags.Instance);
26 Plugin.Player = (Player)playerField.GetValue(__instance);
27
28 // Handle Red Grotto stuff.
29 GlobalState.events.SetEvent("red_cave_l_ss", 999);
30 GlobalState.events.SetEvent("red_cave_n_ss", 999);
31 GlobalState.events.SetEvent("red_cave_r_ss", 999);
32
33 // Connect to archipelago.
34 Plugin.Instance.Log.LogInfo("Connecting to Archipelago!");
35
36 ArchipelagoManager.Connect("localhost:38281", "Anodyne", "");
37 }
38 }
39}
diff --git a/AnodyneArchipelago/Plugin.cs b/AnodyneArchipelago/Plugin.cs index 26f1274..1f6e7ea 100644 --- a/AnodyneArchipelago/Plugin.cs +++ b/AnodyneArchipelago/Plugin.cs
@@ -1,21 +1,8 @@
1using AnodyneArchipelago.Patches; 1using AnodyneSharp.Entities;
2using AnodyneSharp;
3using AnodyneSharp.Entities;
4using AnodyneSharp.Entities.Events;
5using AnodyneSharp.Entities.Gadget;
6using AnodyneSharp.Entities.Gadget.Treasures;
7using AnodyneSharp.Entities.Interactive;
8using AnodyneSharp.Registry;
9using AnodyneSharp.States;
10using AnodyneSharp.Utilities;
11using BepInEx; 2using BepInEx;
12using BepInEx.NET.Common; 3using BepInEx.NET.Common;
13using HarmonyLib; 4using HarmonyLib;
14using HarmonyLib.Tools; 5using HarmonyLib.Tools;
15using Microsoft.Xna.Framework;
16using System;
17using System.Collections;
18using System.Collections.Generic;
19using System.Reflection; 6using System.Reflection;
20 7
21namespace AnodyneArchipelago 8namespace AnodyneArchipelago
@@ -40,247 +27,4 @@ namespace AnodyneArchipelago
40 Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly()); 27 Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly());
41 } 28 }
42 } 29 }
43
44 [HarmonyPatch(typeof(AnodyneGame), "Update")]
45 class GameUpdatePatch
46 {
47 static void Postfix()
48 {
49 ArchipelagoManager.Update();
50 }
51 }
52
53 [HarmonyPatch(typeof(PlayState), nameof(PlayState.Create))]
54 class PlayStateCreatePatch
55 {
56 static void Prefix(PlayState __instance)
57 {
58 // Get player for later access.
59 FieldInfo playerField = typeof(PlayState).GetField("_player", BindingFlags.NonPublic | BindingFlags.Instance);
60 Plugin.Player = (Player)playerField.GetValue(__instance);
61
62 // Handle Red Grotto stuff.
63 GlobalState.events.SetEvent("red_cave_l_ss", 999);
64 GlobalState.events.SetEvent("red_cave_n_ss", 999);
65 GlobalState.events.SetEvent("red_cave_r_ss", 999);
66
67 // Connect to archipelago.
68 Plugin.Instance.Log.LogInfo("Connecting to Archipelago!");
69
70 ArchipelagoManager.Connect("localhost:38281", "Anodyne", "");
71 }
72 }
73
74 [HarmonyPatch(typeof(TreasureChest), nameof(TreasureChest.PlayerInteraction))]
75 class ChestInteractPatch
76 {
77 static void Prefix(TreasureChest __instance)
78 {
79 string entityId = PatchHelper.GetEntityPreset(typeof(TreasureChest), __instance).EntityID.ToString();
80 Plugin.Instance.Log.LogInfo($"Touched chest: {entityId}");
81 }
82 }
83
84 [HarmonyPatch(typeof(TreasureChest), "SetTreasure")]
85 class ChestSetTreasurePatch
86 {
87 static bool Prefix(TreasureChest __instance)
88 {
89 Guid entityId = PatchHelper.GetEntityPreset(typeof(TreasureChest), __instance).EntityID;
90 if (Locations.LocationsByGuid.ContainsKey(entityId))
91 {
92 BaseTreasure treasure = new ArchipelagoTreasure(Locations.LocationsByGuid[entityId], __instance.Position);
93
94 FieldInfo treasureField = typeof(TreasureChest).GetField("_treasure", BindingFlags.NonPublic | BindingFlags.Instance);
95 treasureField.SetValue(__instance, treasure);
96
97 return false;
98 }
99
100 return true;
101 }
102 }
103
104 [HarmonyPatch(typeof(Big_Key), nameof(Big_Key.PlayerInteraction))]
105 class BigKeyTouchPatch
106 {
107 // We basically just rewrite this method, because we need to get rid of the part that adds the key to the inventory.
108 static bool Prefix(Big_Key __instance, ref bool __result)
109 {
110 MethodInfo statesMethod = typeof(Big_Key).GetMethod("States", BindingFlags.NonPublic | BindingFlags.Instance);
111
112 EntityPreset preset = PatchHelper.GetEntityPreset(typeof(Big_Key), __instance);
113 preset.Alive = false;
114 __instance.Solid = false;
115 GlobalState.StartCutscene = (IEnumerator<AnodyneSharp.States.CutsceneState.CutsceneEvent>)statesMethod.Invoke(__instance, new object[] { });
116 __result = true;
117 return false;
118 }
119 }
120
121 [HarmonyPatch(typeof(Big_Key), "States")]
122 class BigKeyStatesPatch
123 {
124 static void Postfix(Big_Key __instance)
125 {
126 EntityPreset preset = PatchHelper.GetEntityPreset(typeof(Big_Key), __instance);
127
128 if (preset.Frame == 0)
129 {
130 ArchipelagoManager.SendLocation("Temple of the Seeing One - Green Key");
131 }
132 else if (preset.Frame == 1)
133 {
134 ArchipelagoManager.SendLocation("Red Grotto - Red Key");
135 }
136 else if (preset.Frame == 2)
137 {
138 ArchipelagoManager.SendLocation("Mountain Cavern - Blue Key");
139 }
140 }
141 }
142
143 [HarmonyPatch(typeof(HealthCicadaSentinel), nameof(HealthCicadaSentinel.Collided))]
144 class HealthCicadaInteractPatch
145 {
146 static void Prefix(TreasureChest __instance)
147 {
148 Type hcsType = typeof(HealthCicadaSentinel);
149 FieldInfo childField = hcsType.GetField("_child", BindingFlags.NonPublic | BindingFlags.Instance);
150 HealthCicada healthCicada = (HealthCicada)childField.GetValue(__instance);
151
152 EntityPreset preset = PatchHelper.GetEntityPreset(typeof(HealthCicada), healthCicada);
153 Plugin.Instance.Log.LogInfo($"Touched cicada: {preset.EntityID.ToString()}");
154 }
155 }
156
157 [HarmonyPatch(typeof(HealthCicada), nameof(HealthCicada.Update))]
158 class HealthCicadaUpdatePatch
159 {
160 static void Postfix(HealthCicada __instance)
161 {
162 Type cicadaType = typeof(HealthCicada);
163 FieldInfo chirpField = cicadaType.GetField("_chirp", BindingFlags.NonPublic | BindingFlags.Instance);
164 FieldInfo sentinelField = cicadaType.GetField("_sentinel", BindingFlags.NonPublic | BindingFlags.Instance);
165
166 HealthCicadaSentinel sentinel = (HealthCicadaSentinel)sentinelField.GetValue(__instance);
167 Type hcsType = typeof(HealthCicadaSentinel);
168 FieldInfo flyDistanceField = hcsType.GetField("_flyDistance", BindingFlags.NonPublic | BindingFlags.Instance);
169 float flyDistance = (float)flyDistanceField.GetValue(sentinel);
170
171 if (__instance.visible && !(bool)chirpField.GetValue(__instance) && flyDistance > 0)
172 {
173 flyDistanceField.SetValue(sentinel, 0f);
174
175 FieldInfo stateField = cicadaType.GetField("_state", BindingFlags.NonPublic | BindingFlags.Instance);
176 stateField.SetValue(__instance, StateLogic(__instance));
177 }
178 }
179
180 static IEnumerator<string> StateLogic(HealthCicada healthCicada)
181 {
182 EntityPreset preset = PatchHelper.GetEntityPreset(typeof(HealthCicada), healthCicada);
183
184 while (!MathUtilities.MoveTo(ref healthCicada.opacity, 0.0f, 0.6f))
185 yield return "FadingOut";
186 preset.Alive = false;
187
188 FieldInfo sentinelField = typeof(HealthCicada).GetField("_sentinel", BindingFlags.NonPublic | BindingFlags.Instance);
189 HealthCicadaSentinel sentinel = (HealthCicadaSentinel)sentinelField.GetValue(healthCicada);
190
191 sentinel.exists = false;
192
193 if (Locations.LocationsByGuid.ContainsKey(preset.EntityID))
194 {
195 ArchipelagoManager.SendLocation(Locations.LocationsByGuid[preset.EntityID]);
196 }
197 }
198 }
199
200 [HarmonyPatch(typeof(EntityPreset), nameof(EntityPreset.Create))]
201 class EntityPresetCreatePatch
202 {
203 static void Postfix(EntityPreset __instance, Entity __result)
204 {
205 if (__instance.Type.FullName == "AnodyneSharp.Entities.Interactive.DungeonStatue")
206 {
207 __result.Position = __instance.Position + new Vector2(1f, 32f);
208
209 string eventName = "StatueMoved_";
210 Facing moveDir = Facing.RIGHT;
211 if (__instance.Frame == 0)
212 {
213 eventName += "Temple";
214 moveDir = Facing.UP;
215 }
216 else if (__instance.Frame == 1)
217 {
218 eventName += "Grotto";
219 }
220 else if (__instance.Frame == 2)
221 {
222 eventName += "Mountain";
223 }
224
225 if (GlobalState.events.GetEvent(eventName) > 0)
226 {
227 __result.Position += Entity.FacingDirection(moveDir) * 32f;
228 }
229 }
230 else if (__instance.Type.FullName.StartsWith("AnodyneSharp.Entities.Decorations.RedCave"))
231 {
232 string side = __instance.Type.FullName.Substring(41);
233 int requiredGrottos = 0;
234 if (side == "Left")
235 {
236 requiredGrottos = 1;
237 }
238 else if (side == "Right")
239 {
240 requiredGrottos = 2;
241 }
242 else if (side == "North")
243 {
244 requiredGrottos = 3;
245 }
246
247 if (GlobalState.events.GetEvent("ProgressiveRedGrotto") < requiredGrottos)
248 {
249 __result.exists = false;
250 GlobalState.SpawnEntity((Entity)new DoorToggle(__result.Position, __result.width, __result.height));
251 }
252 }
253 else if (__instance.Type.FullName == "AnodyneSharp.Entities.Interactive.Npc.MitraCliff")
254 {
255 // We want to get rid of this scene entirely.
256 __instance.Alive = false;
257 __result.exists = false;
258 }
259 }
260 }
261
262 [HarmonyPatch]
263 class BreakChainPatch
264 {
265 static MethodInfo TargetMethod()
266 {
267 return typeof(Red_Pillar).GetNestedType("Chain", BindingFlags.NonPublic).GetMethod("Collided");
268 }
269
270 static void Postfix(object __instance)
271 {
272 Type chainType = typeof(Red_Pillar).GetNestedType("Chain", BindingFlags.NonPublic);
273 FieldInfo parentField = chainType.GetField("_parent", BindingFlags.NonPublic | BindingFlags.Instance);
274
275 Red_Pillar redPillar = (Red_Pillar)parentField.GetValue(__instance);
276 EntityPreset preset = PatchHelper.GetEntityPreset(typeof(Red_Pillar), redPillar);
277
278 Plugin.Instance.Log.LogInfo($"Broke red chain: {preset.EntityID.ToString()}");
279
280 if (Locations.LocationsByGuid.ContainsKey(preset.EntityID))
281 {
282 ArchipelagoManager.SendLocation(Locations.LocationsByGuid[preset.EntityID]);
283 }
284 }
285 }
286} 30}