Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Entity Collider Components to Entities #858

Open
wants to merge 44 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b442c72
Added AddEntityToTracker and AddComponentToTracker methods
TheDavSmasher Nov 26, 2024
fec4721
Simplified Lambda, accounted for multiple Components in the same entity
TheDavSmasher Nov 26, 2024
036e3d3
Simplified new statement
TheDavSmasher Nov 26, 2024
a6d3b62
Added EntityCollider and EntityColliderByComponent
TheDavSmasher Nov 26, 2024
48db15a
Simplified AddComponentToTracker by using AddRange instead of foreach…
TheDavSmasher Nov 30, 2024
5c0e16a
Made method instance instead of static so it can access its own dicti…
TheDavSmasher Nov 30, 2024
68d4be0
Changed Added for EntityAdded to access scene for Tracker
TheDavSmasher Nov 30, 2024
04ead2d
Added default null collider
TheDavSmasher Nov 30, 2024
795db3c
Added Summary documentation to Entity Collider Components
TheDavSmasher Nov 30, 2024
c20217c
Fixed boolean logic of EntityAdded
TheDavSmasher Nov 30, 2024
aa7cd7b
Changed to general AddTypeToTracker
TheDavSmasher Nov 30, 2024
3cc657a
Added static RefreshTracker which will make sure the tracker is up to…
TheDavSmasher Nov 30, 2024
780d16a
Changed Entity and Component methods to AddTypeToTracker in the same …
TheDavSmasher Nov 30, 2024
6bc5a2e
Added RefreshTracker and RefreshTrackerLists to update the tracker ma…
TheDavSmasher Dec 1, 2024
6a2f3cf
TrackedTypes Dictionaries access changed, added RefreshTracker call t…
TheDavSmasher Dec 1, 2024
d6c68cf
Added TrackedAs parameter to AddTypeToTracker
TheDavSmasher Dec 1, 2024
4dd4556
Changed type field to be Type instead of string
TheDavSmasher Dec 1, 2024
e6a0da1
Made PascalCase and changed to property
TheDavSmasher Dec 1, 2024
d4cdbf7
Changed exception message
TheDavSmasher Dec 1, 2024
5b6b2e0
Changed Initialize to use AddTypeToTracker to avoid duplicate code
TheDavSmasher Dec 1, 2024
8d65602
Removed parameterized RefreshTracker
TheDavSmasher Dec 1, 2024
610ffb3
Made refresh instance method
TheDavSmasher Dec 1, 2024
a35664d
Made non-static and added check if already inside tracker to prevent …
TheDavSmasher Dec 1, 2024
024a821
Added instance field Unrefreshed to prevent Refresh being executed ag…
TheDavSmasher Dec 1, 2024
64d8f9c
Added note to summary for Refresh
TheDavSmasher Dec 1, 2024
56b0549
Overrode Added and EntityAwake methods to Refresh the Tracker: Added …
TheDavSmasher Dec 1, 2024
e9312ac
Do Dictionary check before List indexing
TheDavSmasher Dec 2, 2024
3391348
Fixed summary error
TheDavSmasher Dec 2, 2024
4b1e9ac
Added AddTypeToTracker to Added before refresh, both only if Entity i…
TheDavSmasher Dec 2, 2024
df2f875
Removed unneeded using statement
TheDavSmasher Dec 2, 2024
1bd3b0b
Attempt to reduce duplicate code between adding an Entity Type and Co…
TheDavSmasher Dec 2, 2024
df175d3
Changed Exception Message to account for Tracked vs TrackedAs additions
TheDavSmasher Dec 2, 2024
7d6ad9f
Small refactors
TheDavSmasher Dec 6, 2024
c1a7050
Made untracked and removed Type property
TheDavSmasher Dec 6, 2024
42fac47
use version to determine if tracker is outdated
wuke32767 Dec 3, 2024
4edfb38
Merge pull request #2 from wuke32767-s-clone/addColliderComponents
TheDavSmasher Dec 6, 2024
5c4d285
Simplified assignment and boolean expressions
TheDavSmasher Dec 6, 2024
2170b46
Added Summary and set Tracker Version to default after initialization…
TheDavSmasher Dec 6, 2024
4da146f
Made TrackedTypeVersion private and removed Summary
TheDavSmasher Dec 6, 2024
d21dcfd
Made static and requires a scene parameter, which defaults to Engine.…
TheDavSmasher Dec 9, 2024
79ad2ff
Changed parameter name and null check
TheDavSmasher Dec 9, 2024
797ca53
Added summary note of default value of scene parameter
TheDavSmasher Dec 9, 2024
5385f3e
Changed method call to static call
TheDavSmasher Dec 9, 2024
9ef4ab5
Called Refresh with the scene of the Entity
TheDavSmasher Dec 9, 2024
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
62 changes: 62 additions & 0 deletions Celeste.Mod.mm/Mod/Entities/EntityCollider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
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>
[Tracked(false)]
public class EntityCollider<T> : Component where T : Entity {
JaThePlayer marked this conversation as resolved.
Show resolved Hide resolved
/// <summary>
/// Provides a simple way to know the Entity type of the specific Collider without Reflection
/// </summary>
public readonly Type entityType = typeof(T);
TheDavSmasher marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// The Action invoked on Collision, with the Component 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 EntityAdded(Scene scene) {
if (!scene.Tracker.IsEntityTracked<T>()) {
patch_Tracker.AddTypeToTracker(typeof(T));
}
base.EntityAdded(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;
JaThePlayer marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
63 changes: 63 additions & 0 deletions Celeste.Mod.mm/Mod/Entities/EntityColliderByComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
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>
[Tracked(false)]
public class EntityColliderByComponent<T> : Component where T : Component {
/// <summary>
/// Provides a simple way to know the Component type of the specific Collider without Reflection
/// </summary>
public readonly Type componentType = typeof(T);
TheDavSmasher marked this conversation as resolved.
Show resolved Hide resolved

/// <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 EntityAdded(Scene scene) {
if (!scene.Tracker.IsComponentTracked<T>()) {
patch_Tracker.AddTypeToTracker(typeof(T));
}
base.EntityAdded(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;
}
}
}
}
104 changes: 104 additions & 0 deletions Celeste.Mod.mm/Patches/Monocle/Tracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,109 @@ private static List<Type> GetSubclasses(Type type) {
// don't hold references to all the types anymore
_temporaryAllTypes = null;
}

public static void AddTypeToTracker(Type type, Type trackedAs = null, bool inheritAll = false) {
AddTypeToTracker(type, trackedAs, inheritAll ? GetSubclasses(type).ToArray() : Array.Empty<Type>());
}

public static void AddTypeToTracker(Type type, Type trackedAs = null, params Type[] subtypes) {
TheDavSmasher marked this conversation as resolved.
Show resolved Hide resolved
if (typeof(Entity).IsAssignableFrom(type)) {
StoredEntityTypes.Add(type);
if (!type.IsAbstract) {
if (!TrackedEntityTypes.TryGetValue(type, out List<Type> value)) {
value = new List<Type>();
TrackedEntityTypes.Add(type, value);
}
value.AddRange(TrackedEntityTypes.TryGetValue(trackedAs != null && trackedAs.IsAssignableFrom(type)
? trackedAs : type, out List<Type> list) ? list : new List<Type>());
TrackedEntityTypes[type] = value.Distinct().ToList();
}
foreach (Type subtype in subtypes) {
if (!subtype.IsAbstract) {
if (!TrackedEntityTypes.TryGetValue(subtype, out List<Type> value)) {
value = new List<Type>();
TrackedEntityTypes.Add(subtype, value);
}
value.AddRange(TrackedEntityTypes.TryGetValue(trackedAs != null && trackedAs.IsAssignableFrom(type)
? trackedAs : type, out List<Type> list) ? list : new List<Type>());
TrackedEntityTypes[subtype] = value.Distinct().ToList();
}
}
}
else if (typeof(Component).IsAssignableFrom(type)) {
StoredComponentTypes.Add(type);
if (!type.IsAbstract) {
if (!TrackedComponentTypes.TryGetValue(type, out List<Type> value)) {
value = new List<Type>();
TrackedComponentTypes.Add(type, value);
}
value.AddRange(TrackedComponentTypes.TryGetValue(trackedAs != null && trackedAs.IsAssignableFrom(type)
? trackedAs : type, out List<Type> list) ? list : new List<Type>());
TrackedComponentTypes[type] = value.Distinct().ToList();
}
foreach(Type subtype in subtypes) {
if (!subtype.IsAbstract) {
if (!TrackedComponentTypes.TryGetValue(subtype, out List<Type> value)) {
value = new List<Type>();
TrackedComponentTypes.Add(subtype, value);
}
value.AddRange(TrackedComponentTypes.TryGetValue(trackedAs != null && trackedAs.IsAssignableFrom(type)
? trackedAs : type, out List<Type> list) ? list : new List<Type>());
TrackedComponentTypes[subtype] = value.Distinct().ToList();
}
}
}
else {
throw new Exception("Type '" + type.Name + "' cannot be TrackedAs because it does not derive from Entity or Component");
}
RefreshTracker(type);
}

public static void RefreshTracker(Type type) {
if (typeof(Entity).IsAssignableFrom(type) && !Engine.Scene.Tracker.Entities.ContainsKey(type)) {
Engine.Scene.Tracker.Entities.Add(type, new List<Entity>());
TheDavSmasher marked this conversation as resolved.
Show resolved Hide resolved
}
else if (typeof(Component).IsAssignableFrom(type) && !Engine.Scene.Tracker.Components.ContainsKey(type)) {
Engine.Scene.Tracker.Components.Add(type, new List<Component>());
} else {
throw new Exception("Type '" + type.Name + "' cannot be TrackedAs because it does not derive from Entity or Component");
}
RefreshTrackerLists();
}
TheDavSmasher marked this conversation as resolved.
Show resolved Hide resolved

public static void RefreshTracker() {
TheDavSmasher marked this conversation as resolved.
Show resolved Hide resolved
foreach (Type entityType in StoredEntityTypes) {
if (!Engine.Scene.Tracker.Entities.ContainsKey(entityType)) {
Engine.Scene.Tracker.Entities.Add(entityType, new List<Entity>());
}
}
foreach (Type componentType in StoredComponentTypes) {
if (!Engine.Scene.Tracker.Components.ContainsKey(componentType)) {
Engine.Scene.Tracker.Components.Add(componentType, new List<Component>());
}
}
RefreshTrackerLists();
}

private static void RefreshTrackerLists() {
foreach (Entity entity in Engine.Scene.Entities) {
TheDavSmasher marked this conversation as resolved.
Show resolved Hide resolved
foreach (Component component in entity.Components) {
Type componentType = component.GetType();
if (!TrackedComponentTypes.TryGetValue(componentType, out List<Type> componentTypes)) {
continue;
}
foreach (Type trackedType in componentTypes) {
Engine.Scene.Tracker.Components[trackedType].Add(component);
TheDavSmasher marked this conversation as resolved.
Show resolved Hide resolved
}
}
Type entityType = entity.GetType();
if (!TrackedEntityTypes.TryGetValue(entityType, out List<Type> entityTypes)) {
continue;
}
foreach (Type trackedType in entityTypes) {
Engine.Scene.Tracker.Entities[trackedType].Add(entity);
}
}
}
}
}