Skip to content

Commit

Permalink
Merge branch 'dev' into fillAnimForPlayer
Browse files Browse the repository at this point in the history
  • Loading branch information
maddie480 authored Feb 2, 2025
2 parents 549196e + 2daa170 commit 13ad2a4
Show file tree
Hide file tree
Showing 10 changed files with 776 additions and 143 deletions.
71 changes: 71 additions & 0 deletions Celeste.Mod.mm/Mod/Entities/EntityCollider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using Monocle;
using System;
using Microsoft.Xna.Framework;

namespace Celeste.Mod.Entities {
/// <summary>
/// Allows for Collision with any type of entity in the game, similar to a PlayerCollider or PufferCollider.
/// Performs the Action provided on collision.
/// </summary>
/// <typeparam name="T">The specific type of Entity this component should try to collide with</typeparam>
public class EntityCollider<T> : Component where T : Entity {
/// <summary>
/// The Action invoked on Collision, with the Entity collided with passed as a parameter
/// </summary>
public Action<T> OnEntityAction;

public Collider Collider;

public EntityCollider(Action<T> onEntityAction, Collider collider = null)
: base(active: true, visible: true) {
OnEntityAction = onEntityAction;
Collider = collider;
}

public override void Added(Entity entity) {
base.Added(entity);
//Only called if Component is added post Scene Begin and Entity Adding and Awake time.
if (Scene != null) {
if (!Scene.Tracker.IsEntityTracked<T>()) {
patch_Tracker.AddTypeToTracker(typeof(T));
}
patch_Tracker.Refresh(Scene);
}
}

public override void EntityAdded(Scene scene) {
if (!scene.Tracker.IsEntityTracked<T>()) {
patch_Tracker.AddTypeToTracker(typeof(T));
}
base.EntityAdded(scene);
}

public override void EntityAwake() {
patch_Tracker.Refresh(Scene);
}

public override void Update() {
if (OnEntityAction == null) {
return;
}

Collider collider = Entity.Collider;
if (Collider != null) {
Entity.Collider = Collider;
}

Entity.CollideDo(OnEntityAction);

Entity.Collider = collider;
}

public override void DebugRender(Camera camera) {
if (Collider != null) {
Collider collider = Entity.Collider;
Entity.Collider = Collider;
Collider.Render(camera, Color.HotPink);
Entity.Collider = collider;
}
}
}
}
72 changes: 72 additions & 0 deletions Celeste.Mod.mm/Mod/Entities/EntityColliderByComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using Monocle;
using System;
using Microsoft.Xna.Framework;

namespace Celeste.Mod.Entities {
/// <summary>
/// Allows for Collision with any type of entity in the game, similar to a PlayerCollider or PufferCollider.
/// Collision is done by component, as in, it will get all the components of the type and try to collide with their entities.
/// Performs the Action provided on collision.
/// </summary>
/// <typeparam name="T">The specific type of Component this component should try to collide with</typeparam>
public class EntityColliderByComponent<T> : Component where T : Component {
/// <summary>
/// The Action invoked on Collision, with the Component collided with passed as a parameter
/// </summary>
public Action<T> OnComponentAction;

public Collider Collider;

public EntityColliderByComponent(Action<T> onComponentAction, Collider collider = null)
: base(active: true, visible: true) {
OnComponentAction = onComponentAction;
Collider = collider;
}

public override void Added(Entity entity) {
base.Added(entity);
//Only called if Component is added post Scene Begin and Entity Adding and Awake time.
if (Scene != null) {
if (!Scene.Tracker.IsComponentTracked<T>()) {
patch_Tracker.AddTypeToTracker(typeof(T));
}
patch_Tracker.Refresh(Scene);
}
}

public override void EntityAdded(Scene scene) {
if (!scene.Tracker.IsComponentTracked<T>()) {
patch_Tracker.AddTypeToTracker(typeof(T));
}
base.EntityAdded(scene);
}

public override void EntityAwake() {
patch_Tracker.Refresh(Scene);
}

public override void Update() {
if (OnComponentAction == null) {
return;
}

Collider collider = Entity.Collider;
if (Collider != null) {
Entity.Collider = Collider;
}

Entity.CollideDoByComponent(OnComponentAction);

Entity.Collider = collider;
}

public override void DebugRender(Camera camera) {
if (Collider != null) {
Collider collider = Entity.Collider;
Entity.Collider = Collider;
Collider.Render(camera, Color.HotPink);
Entity.Collider = collider;
}
}
}
}
2 changes: 1 addition & 1 deletion Celeste.Mod.mm/Mod/Everest/Everest.Content.cs
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,7 @@ public static string GuessType(string file, out Type type, out string format) {
return fileSpan[..^4].ToString();
}

if (MatchExtension(fileSpan, fileNameSpan, "obj.export", ref warningAlreadySent)) {
if (MatchMultipartExtension(fileSpan, fileNameSpan, "obj.export", ref warningAlreadySent)) {
type = typeof(AssetTypeObjModelExport);
return fileSpan[..^7].ToString();
}
Expand Down
231 changes: 210 additions & 21 deletions Celeste.Mod.mm/Mod/Everest/Everest.Events.cs
Original file line number Diff line number Diff line change
Expand Up @@ -322,17 +322,17 @@ internal static void BeforeRender(_SubHudRenderer renderer, Scene scene)
public static class PlayerSprite {
public delegate void GetIdsUsedFillAnimForIdHandler(List<string> ids, string id);

/// <summary>
/// Called during <see cref="patch_PlayerSprite.CreateFramesMetadata(string)"/>, unless <see cref="DoNotFillAnimFor"/> contains its given string.
/// <br/> <br/>
/// Mods using this should always add an id to <see cref="List{T}"/> ids whether what the <see cref="string"/> id was given, to avoid certain <see cref="string"/> id missing their animations
/// <br/>e.g: <code>
/// Everest.Event.PlayerSprite.OnGetIdsUsedFillAnimForId += ((ids, id) => {
/// if (id == "player_no_backpack")
/// ids.Add("MyHelper_Anims_NB")
/// else
/// ids.Add("MyHelper_Anims")
/// });
/// <summary>
/// Called during <see cref="patch_PlayerSprite.CreateFramesMetadata(string)"/>, unless <see cref="DoNotFillAnimFor"/> contains its given string.
/// <br/> <br/>
/// Mods using this should always add an id to <see cref="List{T}"/> ids whether what the <see cref="string"/> id was given, to avoid certain <see cref="string"/> id missing their animations
/// <br/>e.g: <code>
/// Everest.Event.PlayerSprite.OnGetIdsUsedFillAnimForId += ((ids, id) => {
/// if (id == "player_no_backpack")
/// ids.Add("MyHelper_Anims_NB")
/// else
/// ids.Add("MyHelper_Anims")
/// });
/// </code></summary>
public static event GetIdsUsedFillAnimForIdHandler OnGetIdsUsedFillAnimForId;

Expand All @@ -342,13 +342,202 @@ internal static List<string> GetIdsUsedFillAnimFor(string id) {
return ids;
}

// Put this field here make it so visible
/// <summary>
/// Filter IDs that should not be fill anim by other IDs during <see cref="patch_PlayerSprite.CreateFramesMetadata(string)"/>,
/// <br/>prevent e.g "MyHelper_Anims", ""MyHelper_Anims_NB", "OthersHelper_Anims" ids from senseless filling each other's animations
/// </summary>
public static HashSet<string> DoNotFillAnimFor = new(StringComparer.CurrentCultureIgnoreCase);
}
}
}
}
/// <summary>
/// Filter IDs that should not be fill anim by other IDs during <see cref="patch_PlayerSprite.CreateFramesMetadata(string)"/>,
/// <br/>prevent e.g "MyHelper_Anims", ""MyHelper_Anims_NB", "OthersHelper_Anims" ids from senseless filling each other's animations
/// </summary>
public static HashSet<string> DoNotFillAnimFor = new(StringComparer.CurrentCultureIgnoreCase);

public static event LoadEntityHandler OnLoadEntity;
internal static bool LoadEntity(_Level level, LevelData levelData, Vector2 offset, EntityData entityData) {
LoadEntityHandler onLoadEntity = OnLoadEntity;

if (onLoadEntity == null)
return false;

// replicates the InvokeWhileFalse extension method, but hardcoding the type to avoid dynamic dispatch
foreach (LoadEntityHandler handler in onLoadEntity.GetInvocationList()) {
if (handler(level, levelData, offset, entityData))
return true;
}

return false;
}

public delegate Backdrop LoadBackdropHandler(MapData map, BinaryPacker.Element child, BinaryPacker.Element above);
public static event LoadBackdropHandler OnLoadBackdrop;
internal static Backdrop LoadBackdrop(MapData map, BinaryPacker.Element child, BinaryPacker.Element above)
=> OnLoadBackdrop?.InvokeWhileNull<Backdrop>(map, child, above);

public delegate void LoadLevelHandler(_Level level, _Player.IntroTypes playerIntro, bool isFromLoader);
/// <summary>
/// Called after <see cref="_Level.LoadLevel"/>.<br/>
/// This event is invoked <b>every time</b> a room is entered - transition, respawn, teleport, etc.
/// </summary>
/// <seealso cref="LevelLoader.OnLoadingThread"/>
public static event LoadLevelHandler OnLoadLevel;
internal static void LoadLevel(_Level level, _Player.IntroTypes playerIntro, bool isFromLoader)
=> OnLoadLevel?.Invoke(level, playerIntro, isFromLoader);

public delegate void EnterHandler(_Session session, bool fromSaveData);
public static event EnterHandler OnEnter;
internal static void Enter(_Session session, bool fromSaveData)
=> OnEnter?.Invoke(session, fromSaveData);

public delegate void ExitHandler(_Level level, LevelExit exit, LevelExit.Mode mode, _Session session, HiresSnow snow);
public static event ExitHandler OnExit;
internal static void Exit(_Level level, LevelExit exit, LevelExit.Mode mode, _Session session, HiresSnow snow)
=> OnExit?.Invoke(level, exit, mode, session, snow);

public delegate void CompleteHandler(_Level level);
public static event CompleteHandler OnComplete;
internal static void Complete(_Level level)
=> OnComplete?.Invoke(level);
}

public static class Session {
public static event Action<patch_Session, patch_Session.Slider, float?> OnSliderChanged;
internal static void SliderChanged(patch_Session session, patch_Session.Slider slider, float? previous)
=> OnSliderChanged?.Invoke(session, slider, previous);
}

public static class Player {
public static event Action<_Player> OnSpawn;
internal static void Spawn(_Player player)
=> OnSpawn?.Invoke(player);

public static event Action<_Player> OnDie;
internal static void Die(_Player player)
=> OnDie?.Invoke(player);

public static event Action<_Player> OnRegisterStates;
internal static void RegisterStates(_Player player)
=> OnRegisterStates?.Invoke(player);
}

public static class Seeker {
public static event Action<_Seeker> OnRegisterStates;
internal static void RegisterStates(_Seeker seeker)
=> OnRegisterStates?.Invoke(seeker);
}

public static class AngryOshiro {
public static event Action<_AngryOshiro> OnRegisterStates;
internal static void RegisterStates(_AngryOshiro oshiro)
=> OnRegisterStates?.Invoke(oshiro);
}

public static class Input {
public static event Action OnInitialize;
internal static void Initialize()
=> OnInitialize?.Invoke();

public static event Action OnDeregister;
internal static void Deregister()
=> OnDeregister?.Invoke();
}

[Obsolete("Use Journal instead.")]
public static class OuiJournal {
public delegate void EnterHandler(_OuiJournal journal, Oui from);
public static event EnterHandler OnCreateButtons {
add {
Journal.OnEnter += (Journal.EnterHandler) value.CastDelegate(typeof(Journal.EnterHandler));
}
remove {
Journal.OnEnter -= (Journal.EnterHandler) value.CastDelegate(typeof(Journal.EnterHandler));
}
}
}

public static class Journal {
public delegate void EnterHandler(_OuiJournal journal, Oui from);
public static event EnterHandler OnEnter;
internal static void Enter(_OuiJournal journal, Oui from)
=> OnEnter?.Invoke(journal, from);
}

public static class Decal {
public delegate void DecalRegistryHandler(_Decal decal, DecalRegistry.DecalInfo decalInfo);
public static event DecalRegistryHandler OnHandleDecalRegistry;
internal static void HandleDecalRegistry(_Decal decal, DecalRegistry.DecalInfo decalInfo)
=> OnHandleDecalRegistry?.Invoke(decal, decalInfo);
}

public static class FileSelectSlot {
public delegate void CreateButtonsHandler(List<patch_OuiFileSelectSlot.Button> buttons, OuiFileSelectSlot slot, EverestModuleSaveData modSaveData, bool fileExists);
public static event CreateButtonsHandler OnCreateButtons;
internal static void HandleCreateButtons(List<patch_OuiFileSelectSlot.Button> buttons, OuiFileSelectSlot slot, bool fileExists) {
if (OnCreateButtons == null) {
return;
}

foreach (Delegate del in OnCreateButtons.GetInvocationList()) {
// find the Everest module this delegate belongs to, and load the mod save data from it for the current slot.
EverestModule matchingModule = _Modules.Find(module => module.GetType().Assembly == del.Method.DeclaringType.Assembly);
EverestModuleSaveData modSaveData = null;
if (matchingModule != null) {
modSaveData = matchingModule._SaveData;
}

// call the delegate.
del.DynamicInvoke(new object[] { buttons, slot, modSaveData, fileExists });
}
}
}

public static class EventTrigger {
public delegate bool TriggerEventHandler(_EventTrigger trigger, _Player player, string eventID);
public static event TriggerEventHandler OnEventTrigger;
internal static bool TriggerEvent(_EventTrigger trigger, _Player player, string eventID)
=> OnEventTrigger?.InvokeWhileFalse(trigger, player, eventID) ?? false;
}

public static class CustomBirdTutorial {
public delegate object ParseCommandHandler(string command);
public static event ParseCommandHandler OnParseCommand;
internal static object ParseCommand(string command)
=> OnParseCommand?.InvokeWhileNull<object>(command);
}

public static class AssetReload {
public delegate void ReloadHandler(bool silent);
public static event ReloadHandler OnBeforeReload, OnAfterReload;

public static event ReloadHandler OnBeforeNextReload, OnAfterNextReload;

internal static void BeforeReload(bool silent) {
OnBeforeReload?.Invoke(silent);

var beforeNextReload = OnBeforeNextReload;
OnBeforeNextReload = null;
beforeNextReload?.Invoke(silent);
}

internal static void AfterReload(bool silent) {
OnAfterReload?.Invoke(silent);

var afterNextReload = OnAfterNextReload;
OnAfterNextReload = null;
afterNextReload?.Invoke(silent);
}

public delegate void ReloadLevelHandler(global::Celeste.Level level);
public static ReloadLevelHandler OnReloadLevel;
internal static void ReloadLevel(global::Celeste.Level level)
=> OnReloadLevel?.Invoke(level);

public static Action OnReloadAllMaps;
internal static void ReloadAllMaps()
=> OnReloadAllMaps?.Invoke();
}

public static class SubHudRenderer {
public delegate void BeforeRenderHandler(_SubHudRenderer renderer, Scene scene);
public static event BeforeRenderHandler OnBeforeRender;
internal static void BeforeRender(_SubHudRenderer renderer, Scene scene)
=> OnBeforeRender?.Invoke(renderer, scene);
}
}
}
}
Loading

0 comments on commit 13ad2a4

Please sign in to comment.