Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions Celeste.Mod.mm/Mod/Backdrops/CustomBackdropAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ namespace Celeste.Mod.Backdrops {
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class CustomBackdropAttribute : Attribute {
/// <summary>
/// A list of unique identifiers for this Backdrop.
/// A list of unique identifiers for this Backdrop.<br/>
/// Follows the pattern "ID [= LoadMethodName]".
/// </summary>
public string[] IDs { get; }

/// <summary>
/// Marks this backdrop as a Custom <see cref="Backdrop"/>.
/// </summary>
/// <param name="ids">A list of unique identifiers for this Backdrop.</param>
/// <param name="ids">
/// A list of unique identifiers for this Backdrop.<br/>
/// Follows the pattern "ID [= LoadMethodName]".
/// </param>
public CustomBackdropAttribute(params string[] ids) {
IDs = ids;
}
Expand Down
8 changes: 6 additions & 2 deletions Celeste.Mod.mm/Mod/Entities/CustomEntityAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ namespace Celeste.Mod.Entities {
public class CustomEntityAttribute : Attribute {

/// <summary>
/// A list of unique identifiers for this Entity.
/// A list of unique identifiers for this Entity.<br/>
/// Follows the pattern "ID [= LoadMethodName]".
/// </summary>
public string[] IDs;

/// <summary>
/// Mark this entity as a Custom <see cref="Entity"/> or <see cref="Trigger"/>.
/// </summary>
/// <param name="ids">A list of unique identifiers for this Entity.</param>
/// <param name="ids">
/// A list of unique identifiers for this Entity.<br/>
/// Follows the pattern "ID [= LoadMethodName]".
/// </param>
public CustomEntityAttribute(params string[] ids) {
IDs = ids;
}
Expand Down
8 changes: 6 additions & 2 deletions Celeste.Mod.mm/Mod/Entities/CustomEventAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,18 @@ namespace Celeste.Mod.Entities {
public class CustomEventAttribute : Attribute {

/// <summary>
/// A list of unique identifiers for this Event.
/// A list of unique identifiers for this Event.<br/>
/// Follows the pattern "ID [= LoadMethodName]".
/// </summary>
public string[] IDs;

/// <summary>
/// Mark this entity as a Custom <see cref="CutsceneEntity"/> or other Event <see cref="Entity"/>.
/// </summary>
/// <param name="ids">A list of unique identifiers for this Event.</param>
/// <param name="ids">
/// A list of unique identifiers for this Event.<br/>
/// Follows the pattern "ID [= LoadMethodName]".
/// </param>
public CustomEventAttribute(params string[] ids) {
IDs = ids;
}
Expand Down
32 changes: 32 additions & 0 deletions Celeste.Mod.mm/Mod/Entities/CustomWipeAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;

namespace Celeste.Mod.Entities;

/// <summary>
/// Mark this renderer as a custom <see cref="ScreenWipe"/> with an identifier.
/// <br/>
/// This Screen Wipe will be applied if the map's Wipe metadata has a matching value.
/// <br/>
/// If there is no match, then the full type name of the Screen Wipe is checked for.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
public class CustomWipeAttribute : Attribute {

/// <summary>
/// A list of unique identifiers for this Screen Wipe.<br/>
/// Follows the pattern "ID [= LoadMethodName]".
/// </summary>
public string[] IDs;

/// <summary>
/// Mark this renderer as a custom <see cref="ScreenWipe"/> with an identifier.<br/>
/// If there is no match, then the full type name of the Screen Wipe is checked for.
/// </summary>
/// <param name="ids">
/// A list of unique identifiers for this Screen Wipe.<br/>
/// Follows the pattern "ID [= LoadMethodName]".
/// </param>
public CustomWipeAttribute(params string[] ids) {
IDs = ids;
}
}
16 changes: 16 additions & 0 deletions Celeste.Mod.mm/Mod/Everest/Everest.Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,22 @@ public static class SubHudRenderer {
internal static void BeforeRender(_SubHudRenderer renderer, Scene scene)
=> OnBeforeRender?.Invoke(renderer, scene);
}

public static class MapMeta {
public delegate Action<Scene, bool, Action> ParseWipeHandler(string wipe);
public static event ParseWipeHandler OnParseWipe;
internal static Action<Scene, bool, Action> ParseWipe(string wipe) {
if (OnParseWipe is null)
return null;

foreach (ParseWipeHandler handler in OnParseWipe.GetInvocationList()) {
if (handler(wipe) is { } wipeLoader)
return wipeLoader;
}

return null;
Comment thread
SilverDorian46 marked this conversation as resolved.
}
}
}
}
}
47 changes: 47 additions & 0 deletions Celeste.Mod.mm/Mod/Everest/Everest.Loader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,53 @@ internal static void ProcessAssembly(EverestModuleMetadata meta, Assembly asm, T
}
}

// Search for all Screen Wipes marked with the CustomWipeAttribute.
foreach (CustomWipeAttribute attrib in type.GetCustomAttributes<CustomWipeAttribute>()) {
foreach (string idFull in attrib.IDs) {
string id;
string genName;
string[] split = idFull.Split('=');

if (split.Length == 1) {
id = split[0];
genName = "Load";
} else if (split.Length == 2) {
id = split[0];
genName = split[1];
} else {
Logger.Warn("core", $"Invalid number of custom wipe ID elements: {idFull} ({type.FullName})");
continue;
}

id = id.Trim();
genName = genName.Trim();

Action<Scene, bool, Action> loader = null;

ConstructorInfo ctor;
MethodInfo gen;

gen = type.GetMethod(genName, new Type[] { typeof(Scene), typeof(bool), typeof(Action) });
if (gen != null && gen.IsStatic && gen.ReturnType.IsCompatible(typeof(ScreenWipe))) {
loader = (scene, wipeIn, onComplete) => gen.Invoke(null, new object[] { scene, wipeIn, onComplete });
goto RegisterWipeLoader;
}

ctor = type.GetConstructor(new Type[] { typeof(Scene), typeof(bool), typeof(Action) });
if (ctor != null) {
loader = (scene, wipeIn, onComplete) => ctor.Invoke(new object[] { scene, wipeIn, onComplete });
goto RegisterWipeLoader;
}

RegisterWipeLoader:
if (loader == null) {
Logger.Warn("core", $"Found custom wipe without suitable constructor / {genName}(Scene, bool, Action): {id} ({type.FullName})");
continue;
}
patch_AreaData.WipeLoaders[id] = loader;
}
}

// we already are in the overworld. Register new Ouis real quick!
if (Engine.Instance != null && Engine.Scene is Overworld overworld && typeof(Oui).IsAssignableFrom(type) && !type.IsAbstract) {
Logger.Verbose("core", $"Instantiating UI from {meta}: {type.FullName}");
Expand Down
15 changes: 11 additions & 4 deletions Celeste.Mod.mm/Mod/Meta/MapMeta.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,10 +161,17 @@ public void ApplyTo(patch_AreaData area) {
area.ColorGrade = ColorGrade;

if (!string.IsNullOrEmpty(Wipe)) {
Type type = Assembly.GetEntryAssembly().GetType(Wipe);
ConstructorInfo ctor = type?.GetConstructor(new Type[] { typeof(Scene), typeof(bool), typeof(Action) });
if (type != null && ctor != null) {
area.Wipe = (scene, wipeIn, onComplete) => ctor.Invoke(new object[] { scene, wipeIn, onComplete });
string wipeStr = Wipe;
if (Everest.Events.MapMeta.ParseWipe(wipeStr) is { } wipeLoader)
area.Wipe = wipeLoader;
else if (patch_AreaData.WipeLoaders.TryGetValue(wipeStr, out wipeLoader) && wipeLoader is not null)
area.Wipe = wipeLoader;
else {
Type type = Assembly.GetEntryAssembly().GetType(wipeStr);
ConstructorInfo ctor = type?.GetConstructor(new Type[] { typeof(Scene), typeof(bool), typeof(Action) });
if (type != null && ctor != null) {
area.Wipe = (scene, wipeIn, onComplete) => ctor.Invoke(new object[] { scene, wipeIn, onComplete });
}
}
}

Expand Down
2 changes: 2 additions & 0 deletions Celeste.Mod.mm/Patches/AreaData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
namespace Celeste {
public class patch_AreaData : AreaData {

public static readonly Dictionary<string, Action<Scene, bool, Action>> WipeLoaders = new();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: can the Action<Scene, bool, Action> be a named delegate type instead?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may just be my experience, but getting an Action and a delegate to work together like this is kind of tricky.
so I thought just using an Action would be easier


#pragma warning disable CS0108 // Hides inherited member

// Required to reference this class in other files
Expand Down