Skip to content
Merged
2 changes: 1 addition & 1 deletion CollabMapDataProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.Collections.Generic;

namespace Celeste.Mod.CollabUtils2 {
class CollabMapDataProcessor : EverestMapDataProcessor {
public class CollabMapDataProcessor : EverestMapDataProcessor {
public struct SpeedBerryInfo {
public EntityID ID;
public float Gold;
Expand Down
4 changes: 2 additions & 2 deletions CollabModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public override void Load() {
LobbyHelper.Load();
SpeedBerryTimerDisplay.Load();
SpeedBerryPBInChapterPanel.Load();
JournalTrigger.Load();
JournalHelper.Load();
CustomCrystalHeartHelper.Load();
GoldenBerryPlayerRespawnPoint.Load();
SpeedBerry.Load();
Expand All @@ -63,7 +63,7 @@ public override void Unload() {
LobbyHelper.Unload();
SpeedBerryTimerDisplay.Unload();
SpeedBerryPBInChapterPanel.Unload();
JournalTrigger.Unload();
JournalHelper.Unload();
CustomCrystalHeartHelper.Unload();
GoldenBerryPlayerRespawnPoint.Unload();
SpeedBerry.Unload();
Expand Down
45 changes: 3 additions & 42 deletions Triggers/JournalTrigger.cs
Original file line number Diff line number Diff line change
@@ -1,49 +1,11 @@
using Celeste.Mod.CollabUtils2.UI;
using Celeste.Mod.Entities;
using Microsoft.Xna.Framework;
using MonoMod.Utils;

namespace Celeste.Mod.CollabUtils2.Triggers {
[CustomEntity("CollabUtils2/JournalTrigger")]
public class JournalTrigger : Trigger {
private static bool showOnlyDiscovered;
private static bool vanillaJournal;

internal static void Load() {
Everest.Events.Journal.OnEnter += onJournalEnter;
}

internal static void Unload() {
Everest.Events.Journal.OnEnter -= onJournalEnter;
}

private static void onJournalEnter(OuiJournal journal, Oui from) {
// if using the vanilla journal, we just don't have anything to do, since vanilla already did everything for us!
if (vanillaJournal)
return;

AreaData forceArea = journal.Overworld == null ? null : new DynData<Overworld>(journal.Overworld).Get<AreaData>("collabInGameForcedArea");
if (forceArea != null) {
// custom journal: throw away all pages.
journal.Pages.Clear();

// add the cover with stickers.
journal.Pages.Add(new OuiJournalCoverWithStickers(journal));

// then, fill in the journal with our custom pages.
journal.Pages.AddRange(OuiJournalCollabProgressInLobby.GeneratePages(journal, forceArea.LevelSet, showOnlyDiscovered));

// and add the map if we have it as well.
if (MTN.Journal.Has("collabLobbyMaps/" + forceArea.LevelSet)) {
journal.Pages.Add(new OuiJournalLobbyMap(journal, MTN.Journal["collabLobbyMaps/" + forceArea.LevelSet]));
}

// redraw the first page to include the stickers
journal.Pages[0].Redraw(journal.CurrentPageBuffer);
}
}

public string levelset;
private string levelset;

private readonly TalkComponent talkComponent;

Expand All @@ -55,8 +17,8 @@ public JournalTrigger(EntityData data, Vector2 offset)
new Rectangle(0, 0, data.Width, data.Height),
data.Nodes.Length != 0 ? (data.Nodes[0] - data.Position) : new Vector2(data.Width / 2f, data.Height / 2f),
player => {
showOnlyDiscovered = data.Bool("showOnlyDiscovered", defaultValue: false);
vanillaJournal = data.Bool("vanillaJournal", defaultValue: false);
JournalHelper.VanillaJournal = data.Bool("vanillaJournal", defaultValue: false);
JournalHelper.ShowOnlyDiscovered = data.Bool("showOnlyDiscovered", defaultValue: false);
InGameOverworldHelper.OpenJournal(player, levelset);
}
) { PlayerMustBeFacing = false });
Expand All @@ -66,6 +28,5 @@ public override void Update() {
base.Update();
talkComponent.Enabled = !InGameOverworldHelper.IsOpen;
}

}
}
59 changes: 51 additions & 8 deletions UI/InGameOverworldHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public static List<CreditsTag> Parse(string dialog) {
private static SceneWrappingEntity<Overworld> overworldWrapper;

public static SpriteBank HeartSpriteBank;
private static Dictionary<string, string> OverrideHeartSpriteIDs = new Dictionary<string, string>();

private static AreaKey? lastArea;

Expand Down Expand Up @@ -458,6 +459,17 @@ private static void customizeCrystalHeart(OuiChapterPanel panel) {
}
}

private static string mapSideName(string mapSID, AreaMode side) {
string sideName = mapSID.DialogKeyify();
if (side == AreaMode.BSide) {
sideName += "_B";
} else if (side == AreaMode.CSide) {
sideName += "_C";
}

return sideName;
}

/// <summary>
/// Returns the GUI heart sprite ID (for display in the chapter panel) matching the given map and side, to read from the HeartSpriteBank.
/// </summary>
Expand All @@ -466,15 +478,12 @@ private static void customizeCrystalHeart(OuiChapterPanel panel) {
/// <returns>The sprite ID to pass to HeartSpriteBank.Create to get the custom heart sprite, or null if none was found</returns>
public static string GetGuiHeartSpriteId(string mapSID, AreaMode side) {
string mapLevelSet = AreaData.Get(mapSID)?.LevelSet.DialogKeyify();
string sideName = mapSideName(mapSID, side);

string sideName = mapSID.DialogKeyify();
if (side == AreaMode.BSide) {
sideName += "_B";
} else if (side == AreaMode.CSide) {
sideName += "_C";
}

if (HeartSpriteBank.Has("crystalHeart_" + sideName)) {
if (OverrideHeartSpriteIDs.TryGetValue(sideName, out string spriteID) && HeartSpriteBank.Has(spriteID)) {
// this map has an override custom heart registered: use it.
return spriteID;
} else if (HeartSpriteBank.Has("crystalHeart_" + sideName)) {
// this map has a custom heart registered: use it.
return "crystalHeart_" + sideName;
} else if (HeartSpriteBank.Has("crystalHeart_" + mapLevelSet)) {
Expand All @@ -485,6 +494,34 @@ public static string GetGuiHeartSpriteId(string mapSID, AreaMode side) {
return null;
}

/// <summary>
/// Adds an override heart sprite ID to use for a given map.
/// Useful when lots of heart sprites need to be overridden and replacing all of those manually in the sprite swap XML is too tedious.
/// </summary>
/// <param name="mapSID">The map SID to override the heart sprite for</param>
/// <param name="side">The side to override the heart sprite for</param>
/// <param name="spriteID">The sprite ID to override the map's heart with</param>
public static void AddOverrideHeartSpriteID(string mapSID, AreaMode side, string spriteID) {
string sideName = mapSideName(mapSID, side);

if (OverrideHeartSpriteIDs.TryGetValue(sideName, out _))
OverrideHeartSpriteIDs[sideName] = spriteID;
else
OverrideHeartSpriteIDs.Add(sideName, spriteID);
}

/// <summary>
/// Removes the override heart sprite ID for a given map.
/// </summary>
/// <param name="mapSID">The map SID to remove the override for</param>
/// <param name="side">The side to remove the override for</param>
public static void RemoveOverrideHeartSpriteID(string mapSID, AreaMode side) {
string sideName = mapSideName(mapSID, side);

if (OverrideHeartSpriteIDs.TryGetValue(sideName, out _))
OverrideHeartSpriteIDs.Remove(sideName);
}

// AltSidesHelper does very similar stuff to us, and we want to override what it does if the XMLs are asking for it.
private static void resetCrystalHeartAfterAltSidesHelper(Action<OuiChapterPanel> orig, OuiChapterPanel panel) {
orig(panel);
Expand Down Expand Up @@ -1268,6 +1305,12 @@ private static class ModExports {
public static SpriteBank GetHeartSpriteBank() {
return HeartSpriteBank;
}
public static void AddOverrideHeartSpriteID(string mapSID, AreaMode side, string spriteID) {
InGameOverworldHelper.AddOverrideHeartSpriteID(mapSID, side, spriteID);
}
public static void RemoveOverrideHeartSpriteID(string mapSID, AreaMode side, string spriteID) {
InGameOverworldHelper.RemoveOverrideHeartSpriteID(mapSID, side);
}
public static string GetGuiHeartSpriteId(string mapSID, AreaMode side) {
return InGameOverworldHelper.GetGuiHeartSpriteId(mapSID, side);
}
Expand Down
83 changes: 83 additions & 0 deletions UI/JournalHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using MonoMod.ModInterop;
using MonoMod.Utils;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Celeste.Mod.CollabUtils2.UI {
public static class JournalHelper {
private static readonly Dictionary<string, Action<OuiJournal, string, bool>> JournalEditors = new Dictionary<string, Action<OuiJournal, string, bool>>();

internal static bool VanillaJournal = true; // default to vanilla journal
internal static bool ShowOnlyDiscovered = false;

public static void AddJournalEditor(string collabID, Action<OuiJournal, string, bool> editor) {
if (JournalEditors.TryGetValue(collabID, out _))
JournalEditors[collabID] = editor;
else
JournalEditors.Add(collabID, editor);
}

public static void RemoveJournalEditor(string collabID) {
if (JournalEditors.TryGetValue(collabID, out _))
JournalEditors.Remove(collabID);
}

internal static void Load() {
JournalEditors.Clear();

Everest.Events.Journal.OnEnter += OnJournalEnter;
}

internal static void Unload() {
Everest.Events.Journal.OnEnter -= OnJournalEnter;
}

private static void OnJournalEnter(OuiJournal journal, Oui from) {
// if using the vanilla journal, we just don't have anything to do, since vanilla already did everything for us!
if (VanillaJournal)
return;

// get current area
AreaData forceArea = new DynData<Overworld>(journal.Overworld).Get<AreaData>("collabInGameForcedArea");
if (forceArea == null)
return;

// custom journal: throw away all pages.
journal.Pages.Clear();

// add the cover with stickers.
journal.Pages.Add(new OuiJournalCoverWithStickers(journal));

// then, fill in the journal with our custom pages.
journal.Pages.AddRange(OuiJournalCollabProgressInLobby.GeneratePages(journal, forceArea.LevelSet, ShowOnlyDiscovered));

// and add the map if we have it as well.
if (MTN.Journal.Has("collabLobbyMaps/" + forceArea.LevelSet))
journal.Pages.Add(new OuiJournalLobbyMap(journal, MTN.Journal["collabLobbyMaps/" + forceArea.LevelSet]));

// apply custom page editing if in a lobby with a journal page editor set
if (LobbyHelper.IsCollabLevelSet(forceArea.LevelSet) && JournalEditors.TryGetValue(LobbyHelper.GetCollabNameForSID(forceArea.SID), out Action<OuiJournal, string, bool> collabJournalPageEditor))
collabJournalPageEditor(journal, forceArea.LevelSet, ShowOnlyDiscovered);

// if necessary, redraw the first page to include the stickers
if (journal.Pages.ElementAtOrDefault(0) is OuiJournalCoverWithStickers coverWithStickers)
coverWithStickers.Redraw(journal.CurrentPageBuffer);

// reset journal entry data
VanillaJournal = true;
ShowOnlyDiscovered = false;
}

// ModInterop exports
[ModExportName("CollabUtils2.JournalHelper")]
private static class ModExports {
public static void AddJournalEditor(string collabID, Action<OuiJournal, string, bool> editor) {
JournalHelper.AddJournalEditor(collabID, editor);
}
public static void RemoveJournalEditor(string collabID) {
JournalHelper.RemoveJournalEditor(collabID);
}
}
}
}
38 changes: 36 additions & 2 deletions UI/LobbyMapUI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Monocle;
using MonoMod.ModInterop;
using System;
using System.Collections;
using System.Collections.Generic;
Expand Down Expand Up @@ -81,6 +82,19 @@ public class LobbyMapUI : Entity {
private int lastSelectedWarpIndex = -1;
private float scaleMultiplier = 1f;
private float finalScale => actualScale * scaleMultiplier;

// mod interop
private static readonly Dictionary<string, Action<Entity, List<Component>>> CustomRenderActions = new Dictionary<string, Action<Entity, List<Component>>>();
public static void AddCustomRenderAction(string collabID, Action<Entity, List<Component>> action) {
if (CustomRenderActions.TryGetValue(collabID, out _))
CustomRenderActions[collabID] = action;
else
CustomRenderActions.Add(collabID, action);
}
public static void RemoveCustomRenderAction(string collabID) {
if (CustomRenderActions.TryGetValue(collabID, out _))
CustomRenderActions.Remove(collabID);
}

private Rectangle windowBounds;
private Rectangle mapBounds;
Expand Down Expand Up @@ -595,7 +609,8 @@ public bool updateSelectedLobby(bool first = false) {

if (lobbyMapInfo.ShowHeartCount) {
// try to get a custom id
var id = InGameOverworldHelper.GetGuiHeartSpriteId(selection.SID, AreaMode.Normal);
var id = InGameOverworldHelper.GetGuiHeartSpriteId(selection.SID + "_lobbyMap", AreaMode.Normal)
?? InGameOverworldHelper.GetGuiHeartSpriteId(selection.SID, AreaMode.Normal);

if (id == null) {
heartSprite = GFX.GuiSpriteBank.Create("heartgem0");
Expand Down Expand Up @@ -701,6 +716,10 @@ public override void Render() {
drawVisitedPoints();
}

string currentCollabName = LobbyHelper.GetCollabNameForSID(SceneAs<Level>().Session.Area.SID);
if (CustomRenderActions.TryGetValue(currentCollabName, out Action<Entity, List<Component>> customRenderAction))
customRenderAction(this, markerComponents);

drawForeground();
}

Expand Down Expand Up @@ -1161,7 +1180,7 @@ public bool CheckLocked() {
/// <summary>
/// Represents a map marker as an image.
/// </summary>
private class MarkerImage : Image {
public class MarkerImage : Image {
public readonly LobbyMapController.MarkerInfo Info;

public MarkerImage(LobbyMapController.MarkerInfo info) : base(null) {
Expand Down Expand Up @@ -1203,5 +1222,20 @@ public LobbySelection(EntityData data, MapData map) {
}

#endregion

#region ModInterop

// ModInterop exports
[ModExportName("CollabUtils2.LobbyMapUI")]
private static class ModExports {
public static void AddCustomRenderAction(string collabID, Action<Entity, List<Component>> editor) {
LobbyMapUI.AddCustomRenderAction(collabID, editor);
}
public static void RemoveCustomRenderAction(string collabID) {
LobbyMapUI.RemoveCustomRenderAction(collabID);
}
}

#endregion
}
}
8 changes: 4 additions & 4 deletions UI/OuiJournalCollabProgressDashCountMod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,21 @@ namespace Celeste.Mod.CollabUtils2.UI {
/// in order to enable dash count in the Collab Utils' journals.
/// (It is simpler to implement them in Collab Utils directly, rather than hooking both journals with IL hooks.)
/// </summary>
static class OuiJournalCollabProgressDashCountMod {
public static class OuiJournalCollabProgressDashCountMod {
[MethodImpl(MethodImplOptions.NoInlining)]
internal static bool IsDashCountEnabled() {
public static bool IsDashCountEnabled() {
return false;
}

// those depend on Dash Count Mod settings / save data, and will be implemented by Dash Count Mod itself.

[MethodImpl(MethodImplOptions.NoInlining)]
internal static bool DisplaysTotalDashes() {
public static bool DisplaysTotalDashes() {
return false;
}

[MethodImpl(MethodImplOptions.NoInlining)]
internal static int GetLevelDashesForJournalProgress(AreaStats stats) {
public static int GetLevelDashesForJournalProgress(AreaStats stats) {
return stats.BestTotalDashes;
}
}
Expand Down
2 changes: 1 addition & 1 deletion UI/OuiJournalCollabProgressInLobby.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using System.Text.RegularExpressions;

namespace Celeste.Mod.CollabUtils2.UI {
class OuiJournalCollabProgressInLobby : OuiJournalPage {
public class OuiJournalCollabProgressInLobby : OuiJournalPage {

private Table table;

Expand Down
2 changes: 1 addition & 1 deletion UI/OuiJournalCollabProgressInOverworld.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
using System.Linq;

namespace Celeste.Mod.CollabUtils2.UI {
class OuiJournalCollabProgressInOverworld : OuiJournalPage {
public class OuiJournalCollabProgressInOverworld : OuiJournalPage {

private Table table;

Expand Down
Loading