-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
397 additions
and
141 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
module MaxHelpingHandRespawningBounceJellyfish | ||
|
||
using ..Ahorn, Maple | ||
|
||
@mapdef Entity "MaxHelpingHand/RespawningBounceJellyfish" RespawningBounceJellyfish(x::Integer, y::Integer, platform::Bool=true, soulBound::Bool=true, baseDashCount::Integer=1, | ||
respawnTime::Number=2.0, spriteDirectory::String="objects/MaxHelpingHand/glider") | ||
|
||
const placements = Ahorn.PlacementDict( | ||
"Respawning Bounce Jellyfish (Bounce Helper + Maddie's Helping Hand)" => Ahorn.EntityPlacement( | ||
RespawningBounceJellyfish | ||
) | ||
) | ||
|
||
function Ahorn.selection(entity::RespawningBounceJellyfish) | ||
x, y = Ahorn.position(entity) | ||
sprite = get(entity, "spriteDirectory", "objects/MaxHelpingHand/glider") * "/idle0" | ||
|
||
return Ahorn.getSpriteRectangle(sprite, x, y - 4) | ||
end | ||
|
||
function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::RespawningBounceJellyfish, room::Maple.Room) | ||
sprite = get(entity, "spriteDirectory", "objects/MaxHelpingHand/glider") * "/idle0" | ||
Ahorn.drawSprite(ctx, sprite, 0, -4) | ||
|
||
if get(entity, "platform", false) | ||
curve = Ahorn.SimpleCurve((-7, -1), (7, -1), (0, -6)) | ||
Ahorn.drawSimpleCurve(ctx, curve, (1.0, 1.0, 1.0, 1.0), thickness=1) | ||
end | ||
end | ||
|
||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
using Celeste.Mod.BounceHelper; | ||
using Celeste.Mod.Entities; | ||
using Microsoft.Xna.Framework; | ||
using System.Collections; | ||
using Monocle; | ||
|
||
namespace Celeste.Mod.MaxHelpingHand.Entities { | ||
[CustomEntity("MaxHelpingHand/RespawningJellyfish")] | ||
public class RespawningBounceJellyfish : BounceJellyfish { | ||
public static void LoadBounceHelper() { | ||
RespawningJellyfishGeneric<RespawningBounceJellyfish, BounceJellyfish>.Load(); | ||
Everest.Events.Level.OnLoadEntity += onLoadEntity; | ||
On.Monocle.Tracker.Initialize += onTrackerInitialize; | ||
} | ||
|
||
public static void UnloadBounceHelper() { | ||
RespawningJellyfishGeneric<RespawningBounceJellyfish, BounceJellyfish>.Unload(); | ||
Everest.Events.Level.OnLoadEntity -= onLoadEntity; | ||
On.Monocle.Tracker.Initialize -= onTrackerInitialize; | ||
} | ||
|
||
private static bool onLoadEntity(Level level, LevelData levelData, Vector2 offset, EntityData entityData) { | ||
if (entityData.Name == "MaxHelpingHand/RespawningBounceJellyfish") { | ||
level.Add(new RespawningBounceJellyfish(entityData, offset)); | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private static void onTrackerInitialize(On.Monocle.Tracker.orig_Initialize orig) { | ||
orig(); | ||
Tracker.TrackedEntityTypes[typeof(RespawningBounceJellyfish)] = [typeof(BounceJellyfish)]; | ||
} | ||
|
||
|
||
private RespawningJellyfishGeneric<RespawningBounceJellyfish, BounceJellyfish> manager; | ||
|
||
public RespawningBounceJellyfish(EntityData data, Vector2 offset) : base(data, offset) { | ||
manager = new RespawningJellyfishGeneric<RespawningBounceJellyfish, BounceJellyfish>(this, data, () => Speed, speed => Speed = speed); | ||
} | ||
|
||
public override void Update() { | ||
manager.Update(base.Update); | ||
} | ||
|
||
protected override void OnSquish(CollisionData data) { | ||
manager.OnSquish(base.OnSquish, data); | ||
} | ||
|
||
private IEnumerator destroyThenRespawnRoutine() { | ||
return manager.destroyThenRespawnRoutine(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,156 +1,26 @@ | ||
using Celeste.Mod.Entities; | ||
using Microsoft.Xna.Framework; | ||
using Mono.Cecil.Cil; | ||
using Monocle; | ||
using MonoMod.Cil; | ||
using MonoMod.Utils; | ||
using System; | ||
using System.Collections; | ||
|
||
namespace Celeste.Mod.MaxHelpingHand.Entities { | ||
[CustomEntity("MaxHelpingHand/RespawningJellyfish")] | ||
public class RespawningJellyfish : Glider { | ||
public static void Load() { | ||
IL.Celeste.Glider.Update += modGliderUpdate; | ||
} | ||
|
||
public static void Unload() { | ||
IL.Celeste.Glider.Update -= modGliderUpdate; | ||
} | ||
|
||
private static ParticleType P_NotGlow; | ||
|
||
private DynData<Glider> self; | ||
|
||
private float respawnTime; | ||
private bool bubble; | ||
|
||
private Sprite sprite; | ||
|
||
private Vector2 initialPosition; | ||
private bool respawning; | ||
|
||
private bool shouldRespawn = true; | ||
private RespawningJellyfishGeneric<RespawningJellyfish, Glider> manager; | ||
|
||
public RespawningJellyfish(EntityData data, Vector2 offset) : base(data, offset) { | ||
if (P_NotGlow == null) { | ||
// P_NotGlow is a transparent particle. | ||
P_NotGlow = new ParticleType(P_Glow) { | ||
Color = Color.Transparent, | ||
Color2 = Color.Transparent | ||
}; | ||
} | ||
|
||
respawnTime = data.Float("respawnTime"); | ||
bubble = data.Bool("bubble"); | ||
initialPosition = Position; | ||
respawning = false; | ||
|
||
// get the sprite, and replace it depending on the path in entity properties. | ||
self = new DynData<Glider>(this); | ||
sprite = self.Get<Sprite>("sprite"); | ||
new DynData<Sprite>(sprite)["atlas"] = GFX.Game; | ||
sprite.Path = data.Attr("spriteDirectory", defaultValue: "objects/MaxHelpingHand/glider") + "/"; | ||
sprite.Stop(); | ||
sprite.ClearAnimations(); | ||
sprite.AddLoop("idle", "idle", 0.1f); | ||
sprite.AddLoop("held", "held", 0.1f); | ||
sprite.Add("fall", "fall", 0.06f, "fallLoop"); | ||
sprite.AddLoop("fallLoop", "fallLoop", 0.06f); | ||
sprite.Add("death", "death", 0.06f); | ||
sprite.Add("respawn", "respawn", 0.03f, "idle"); | ||
sprite.Play("idle"); | ||
|
||
// make the jelly go invisible when the death animation is done. | ||
sprite.OnFinish += anim => { | ||
if (anim == "death") { | ||
Visible = false; | ||
} | ||
}; | ||
|
||
// listen for transitions: if the jelly is carried to another screen, it should not respawn anymore. | ||
Add(new TransitionListener() { | ||
OnOutBegin = () => shouldRespawn = false | ||
}); | ||
manager = new RespawningJellyfishGeneric<RespawningJellyfish, Glider>(this, data, () => Speed, speed => Speed = speed); | ||
} | ||
|
||
public override void Update() { | ||
if (shouldRespawn && !respawning && Top + Speed.Y * Engine.DeltaTime > (SceneAs<Level>().Bounds.Bottom + 16)) { | ||
// the jellyfish glided off-screen. | ||
removeAndRespawn(); | ||
} | ||
|
||
// if the jelly is invisible, "disable" the particles (actually make them invisible). | ||
ParticleType vanillaGlow = P_Glow; | ||
if (!Visible) P_Glow = P_NotGlow; | ||
|
||
base.Update(); | ||
|
||
P_Glow = vanillaGlow; | ||
} | ||
|
||
private static void modGliderUpdate(ILContext il) { | ||
ILCursor cursor = new ILCursor(il); | ||
|
||
while (cursor.TryGotoNext(MoveType.After, instr => instr.MatchNewobj<Coroutine>())) { | ||
Logger.Log("MaxHelpingHand/RespawningJellyfish", $"Replacing coroutine to make jellyfish respawn at {cursor.Index} in IL for Glider.Update"); | ||
|
||
cursor.Emit(OpCodes.Ldarg_0); | ||
cursor.EmitDelegate<Func<Coroutine, Glider, Coroutine>>((orig, self) => { | ||
if (self is RespawningJellyfish jelly) { | ||
return new Coroutine(jelly.destroyThenRespawnRoutine()); | ||
} | ||
return orig; | ||
}); | ||
} | ||
manager.Update(base.Update); | ||
} | ||
|
||
protected override void OnSquish(CollisionData data) { | ||
if (shouldRespawn) { | ||
if (!TrySquishWiggle(data)) { | ||
// the jellyfish was squished. | ||
removeAndRespawn(); | ||
} | ||
} else { | ||
// vanilla behavior | ||
base.OnSquish(data); | ||
} | ||
} | ||
|
||
private void removeAndRespawn() { | ||
Collidable = false; | ||
Visible = false; | ||
self["destroyed"] = true; | ||
Add(new Coroutine(respawnRoutine())); | ||
manager.OnSquish(base.OnSquish, data); | ||
} | ||
|
||
private IEnumerator destroyThenRespawnRoutine() { | ||
// do like vanilla, but instead of removing the jelly, wait then have it respawn. | ||
Audio.Play("event:/new_content/game/10_farewell/glider_emancipate", Position); | ||
sprite.Play("death"); | ||
|
||
return respawnRoutine(); | ||
} | ||
|
||
private IEnumerator respawnRoutine() { | ||
respawning = true; | ||
|
||
// wait for the respawn time | ||
yield return respawnTime; | ||
|
||
// then respawn at the initial position | ||
Visible = true; | ||
Position = initialPosition; | ||
Speed = Vector2.Zero; | ||
sprite.Play("respawn"); | ||
|
||
yield return 0.24f; | ||
|
||
respawning = false; | ||
self["destroyed"] = false; | ||
self["bubble"] = bubble; | ||
Collidable = true; | ||
return manager.destroyThenRespawnRoutine(); | ||
} | ||
} | ||
} |
Oops, something went wrong.