Skip to content

Commit 5dc9683

Browse files
committed
Added support for reskinning B-side, C-side and AltSidesHelper hearts through XML
1 parent df4f408 commit 5dc9683

File tree

3 files changed

+57
-12
lines changed

3 files changed

+57
-12
lines changed

CollabModule.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public override void Initialize() {
7777
base.Initialize();
7878

7979
LobbyHelper.OnInitialize();
80+
InGameOverworldHelper.Initialize();
8081
}
8182

8283
private void onModAssetUpdate(ModAsset oldAsset, ModAsset newAsset) {

DOCUMENTATION.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ If you use one of the vanilla crystal heart sprites (`collectables/heartgem/1/`,
161161

162162
If you want a custom text color on the poem screen, include `poemtextcolor_[hexcode]` somewhere in the texture path: for example `path="MyMod/customcrystalheart_poemtextcolor_ff0000/"`.
163163

164+
For B-side and C-side hearts, add `_B` or `_C` at the end of the sprite name. For example, for the B-side of 2021SeasonCollab_0_Lobbies_2_Summer: `crystalHeart_2021SeasonCollab_0_Lobbies_2_Summer_B`
165+
164166
### Fake Mini Hearts
165167

166168
Those look exactly like Mini Hearts, but disappear for 3 seconds when dashed into, like vanilla fake hearts.

UI/InGameOverworldHelper.cs

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Mono.Cecil.Cil;
55
using Monocle;
66
using MonoMod.Cil;
7+
using MonoMod.RuntimeDetour;
78
using MonoMod.Utils;
89
using System;
910
using System.Collections;
@@ -26,6 +27,8 @@ public static class InGameOverworldHelper {
2627
private static MethodInfo m_PlayExpandSfx = typeof(OuiChapterPanel)
2728
.GetMethod("PlayExpandSfx", BindingFlags.NonPublic | BindingFlags.Instance);
2829

30+
private static List<Hook> altSidesHelperHooks = new List<Hook>();
31+
2932
public static void Load() {
3033
Everest.Events.Level.OnPause += OnPause;
3134
On.Celeste.Audio.SetMusic += OnSetMusic;
@@ -46,6 +49,20 @@ public static void Load() {
4649
On.Celeste.Mod.AssetReloadHelper.ReloadLevel += OnReloadLevel;
4750
}
4851

52+
public static void Initialize() {
53+
if (Everest.Loader.DependencyLoaded(new EverestModuleMetadata() { Name = "AltSidesHelper", Version = new Version(1, 2, 0) })) {
54+
Type altSidesHelperModule = Everest.Modules.Where(m => m.GetType().FullName == "AltSidesHelper.AltSidesHelperModule").First().GetType();
55+
56+
altSidesHelperHooks.Add(new Hook(
57+
altSidesHelperModule.GetMethod("ResetCrystalHeart", BindingFlags.NonPublic | BindingFlags.Static),
58+
typeof(InGameOverworldHelper).GetMethod("resetCrystalHeartAfterAltSidesHelper", BindingFlags.NonPublic | BindingFlags.Static)));
59+
60+
altSidesHelperHooks.Add(new Hook(
61+
altSidesHelperModule.GetMethod("CustomizeCrystalHeart", BindingFlags.NonPublic | BindingFlags.Static),
62+
typeof(InGameOverworldHelper).GetMethod("customizeCrystalHeartAfterAltSidesHelper", BindingFlags.NonPublic | BindingFlags.Static)));
63+
}
64+
}
65+
4966
public static void Unload() {
5067
Everest.Events.Level.OnPause -= OnPause;
5168
On.Celeste.Audio.SetMusic -= OnSetMusic;
@@ -64,6 +81,11 @@ public static void Unload() {
6481
On.Celeste.OuiChapterPanel.Start -= OnOuiChapterPanelStart;
6582
On.Celeste.Player.Die -= OnPlayerDie;
6683
On.Celeste.Mod.AssetReloadHelper.ReloadLevel -= OnReloadLevel;
84+
85+
foreach (Hook hook in altSidesHelperHooks) {
86+
hook.Dispose();
87+
}
88+
altSidesHelperHooks.Clear();
6789
}
6890

6991
private static void OnOuiChapterPanelStart(On.Celeste.OuiChapterPanel.orig_Start orig, OuiChapterPanel self, string checkpoint) {
@@ -198,22 +220,42 @@ private static void customizeCrystalHeart(OuiChapterPanel panel) {
198220
string mapName = sid.DialogKeyify();
199221
string mapLevelSet = AreaData.Get(sid)?.GetLevelSet().DialogKeyify();
200222

201-
if (HeartSpriteBank.Has("crystalHeart_" + mapName)) {
202-
// this map has a custom heart registered: use it.
203-
animId = "crystalHeart_" + mapName;
204-
} else if (HeartSpriteBank.Has("crystalHeart_" + mapLevelSet)) {
205-
// this level set has a custom heart registered: use it.
206-
animId = "crystalHeart_" + mapLevelSet;
207-
}
223+
for (int side = 0; side < 3; side++) {
224+
string sideName = mapName;
225+
if (side == 1) {
226+
sideName += "_B";
227+
} else if (side == 2) {
228+
sideName += "_C";
229+
}
230+
231+
if (HeartSpriteBank.Has("crystalHeart_" + sideName)) {
232+
// this map has a custom heart registered: use it.
233+
animId = "crystalHeart_" + sideName;
234+
} else if (HeartSpriteBank.Has("crystalHeart_" + mapLevelSet)) {
235+
// this level set has a custom heart registered: use it.
236+
animId = "crystalHeart_" + mapLevelSet;
237+
}
208238

209-
if (animId != null) {
210-
Sprite heartSprite = HeartSpriteBank.Create(animId);
211-
new DynData<OuiChapterPanel>(panel).Get<HeartGemDisplay>("heart").Sprites[0] = heartSprite;
212-
heartSprite.Play("spin");
213-
new DynData<OuiChapterPanel>(panel)["heartDirty"] = true;
239+
if (animId != null) {
240+
Sprite heartSprite = HeartSpriteBank.Create(animId);
241+
new DynData<OuiChapterPanel>(panel).Get<HeartGemDisplay>("heart").Sprites[side] = heartSprite;
242+
heartSprite.Play("spin");
243+
new DynData<OuiChapterPanel>(panel)["heartDirty"] = true;
244+
}
214245
}
215246
}
216247

248+
// AltSidesHelper does very similar stuff to us, and we want to override what it does if the XMLs are asking for it.
249+
private static void resetCrystalHeartAfterAltSidesHelper(Action<OuiChapterPanel> orig, OuiChapterPanel panel) {
250+
orig(panel);
251+
resetCrystalHeart(panel);
252+
}
253+
254+
private static void customizeCrystalHeartAfterAltSidesHelper(Action<OuiChapterPanel> orig, OuiChapterPanel panel) {
255+
orig(panel);
256+
customizeCrystalHeart(panel);
257+
}
258+
217259
private static bool OnSaveDataFoundAnyCheckpoints(On.Celeste.SaveData.orig_FoundAnyCheckpoints orig, SaveData self, AreaKey area) {
218260
// if this is a collab chapter panel, display the second page (containing the credits) if they are defined in English.txt.
219261
// otherwise, if there is a saved state, also display the chapter panel.

0 commit comments

Comments
 (0)