Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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.
Outdated
}
}
}
}
}
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