Skip to content

Commit

Permalink
Respawning Bounce Jellyfish: better support for jelly dashing
Browse files Browse the repository at this point in the history
  • Loading branch information
maddie480 committed Dec 19, 2023
1 parent 7e0afaa commit d7c622b
Show file tree
Hide file tree
Showing 136 changed files with 58 additions and 33 deletions.
6 changes: 3 additions & 3 deletions Ahorn/entities/maxHelpingHandRespawningBounceJellyfish.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ 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")
respawnTime::Number=2.0, spriteDirectory::String="objects/MaxHelpingHand/bounceJellyfish")

const placements = Ahorn.PlacementDict(
"Respawning Bounce Jellyfish (Bounce Helper + Maddie's Helping Hand)" => Ahorn.EntityPlacement(
Expand All @@ -13,13 +13,13 @@ const placements = Ahorn.PlacementDict(

function Ahorn.selection(entity::RespawningBounceJellyfish)
x, y = Ahorn.position(entity)
sprite = get(entity, "spriteDirectory", "objects/MaxHelpingHand/glider") * "/idle0"
sprite = get(entity, "spriteDirectory", "objects/MaxHelpingHand/glider") * "/blue/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"
sprite = get(entity, "spriteDirectory", "objects/MaxHelpingHand/glider") * "/blue/idle0"
Ahorn.drawSprite(ctx, sprite, 0, -4)

if get(entity, "platform", false)
Expand Down
2 changes: 1 addition & 1 deletion Ahorn/lang/en_gb.lang
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ placements.entities.MaxHelpingHand/RespawningBounceJellyfish.tooltips.platform=W
placements.entities.MaxHelpingHand/RespawningBounceJellyfish.tooltips.soulBound=Whether the jelly kills the player on death and can't be left behind.
placements.entities.MaxHelpingHand/RespawningBounceJellyfish.tooltips.baseDashCount=How many dashes the jelly will start with and refill to.
placements.entities.MaxHelpingHand/RespawningBounceJellyfish.tooltips.respawnTime=The jellyfish will respawn at its starting location after this amount of time (in seconds) after being destroyed.
placements.entities.MaxHelpingHand/RespawningBounceJellyfish.tooltips.spriteDirectory=The directory containing all the sprites for the respawning jellyfish.\nTo make your own, copy Graphics/Atlases/Gameplay/objects/MaxHelpingHand/glider from the mod zip to Mods/yourmod/Graphics/Atlases/Gameplay/MyMap/myglider and type MyMap/myglider in this field.
placements.entities.MaxHelpingHand/RespawningBounceJellyfish.tooltips.spriteDirectory=The directory containing all the sprites for the respawning jellyfish.\nTo make your own, copy Graphics/Atlases/Gameplay/objects/MaxHelpingHand/respawningJellyfish from the mod zip to Mods/yourmod/Graphics/Atlases/Gameplay/MyMap/myglider and type MyMap/myglider in this field.

# Grouped Trigger Spikes
placements.entities.MaxHelpingHand/GroupedTriggerSpikesUp.tooltips.type=Changes the visual appearance of the spikes.
Expand Down
15 changes: 14 additions & 1 deletion Entities/RespawningBounceJellyfish.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,20 @@ private static void onTrackerInitialize(On.Monocle.Tracker.orig_Initialize orig)
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);
manager = new RespawningJellyfishGeneric<RespawningBounceJellyfish, BounceJellyfish>(this, data, sprite => {
foreach (string variant in new string[] { "blue", "red", "pink", "flash" }) {
string suffix = variant.Substring(0, 1).ToUpperInvariant();
sprite.AddLoop("idle" + suffix, variant + "/idle", 0.1f);
sprite.AddLoop("held" + suffix, variant + "/held", 0.1f);
sprite.Add("fall" + suffix, variant + "/fall", 0.06f, "fallLoop" + suffix);
sprite.AddLoop("fallLoop" + suffix, variant + "/fallLoop", 0.06f);
sprite.Add("death" + suffix, variant + "/death", 0.06f);
sprite.Add("respawn" + suffix, variant + "/respawn", 0.03f, "idle" + suffix);
}
sprite.Play("idleB");
}, () => Speed, speed => Speed = speed);
}

public override void Update() {
Expand Down
15 changes: 14 additions & 1 deletion Entities/RespawningJellyfish.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,16 @@ public class RespawningJellyfish : Glider {
private RespawningJellyfishGeneric<RespawningJellyfish, Glider> manager;

public RespawningJellyfish(EntityData data, Vector2 offset) : base(data, offset) {
manager = new RespawningJellyfishGeneric<RespawningJellyfish, Glider>(this, data, () => Speed, speed => Speed = speed);
manager = new RespawningJellyfishGeneric<RespawningJellyfish, Glider>(this, data, sprite => {
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");
}, () => Speed, speed => Speed = speed);
}

public override void Update() {
Expand All @@ -22,5 +31,9 @@ protected override void OnSquish(CollisionData data) {
private IEnumerator destroyThenRespawnRoutine() {
return manager.destroyThenRespawnRoutine();
}

private void spritePlay(string name) {
manager.sprite.Play(name);
}
}
}
39 changes: 19 additions & 20 deletions Entities/RespawningJellyfishGeneric.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ namespace Celeste.Mod.MaxHelpingHand.Entities {
internal static class RespawningJellyfishCache {
internal static MethodInfo trySquishWiggle = typeof(Actor).GetMethod("TrySquishWiggle", BindingFlags.NonPublic | BindingFlags.Instance);
}
public class RespawningJellyfishGeneric<T, U> where T : Actor, U where U : Actor {
public class RespawningJellyfishGeneric<RespawningType, BaseType> where RespawningType : Actor, BaseType where BaseType : Actor {
private static ILHook hookGliderUpdate = null;
private static MethodInfo jellyfishSpritePlay = typeof(BaseType).GetMethod("spritePlay", BindingFlags.NonPublic | BindingFlags.Instance)
?? typeof(RespawningType).GetMethod("spritePlay", BindingFlags.NonPublic | BindingFlags.Instance);
private static MethodInfo jellyDashRefill = typeof(BaseType).GetMethod("refillDash", BindingFlags.Public | BindingFlags.Instance);

public static void Load() {
hookGliderUpdate = new ILHook(typeof(U).GetMethod("Update"), modGliderUpdate);
hookGliderUpdate = new ILHook(typeof(BaseType).GetMethod("Update"), modGliderUpdate);
}

public static void Unload() {
Expand All @@ -26,13 +29,13 @@ public static void Unload() {

private static ParticleType P_NotGlow;

private T self;
private DynData<U> selfData;
private RespawningType self;
private DynData<BaseType> selfData;

private float respawnTime;
private bool bubble;

private Sprite sprite;
internal Sprite sprite;

private Vector2 initialPosition;
private bool respawning;
Expand All @@ -42,7 +45,7 @@ public static void Unload() {
private Func<Vector2> getSpeed;
private Action<Vector2> setSpeed;

public RespawningJellyfishGeneric(T self, EntityData data, Func<Vector2> getSpeed, Action<Vector2> setSpeed) {
public RespawningJellyfishGeneric(RespawningType self, EntityData data, Action<Sprite> setupSpriteCallback, Func<Vector2> getSpeed, Action<Vector2> setSpeed) {
this.self = self;
this.getSpeed = getSpeed;
this.setSpeed = setSpeed;
Expand All @@ -61,21 +64,13 @@ public RespawningJellyfishGeneric(T self, EntityData data, Func<Vector2> getSpee
respawning = false;

// get the sprite, and replace it depending on the path in entity properties.
selfData = new DynData<U>(self);
selfData = new DynData<BaseType>(self);
sprite = selfData.Get<Sprite>("sprite");
new DynData<Sprite>(sprite)["atlas"] = GFX.Game;
sprite.Path = data.Attr("spriteDirectory", defaultValue: "objects/MaxHelpingHand/glider") + "/";
sprite.Stop();
sprite.ClearAnimations();
foreach (string suffix in new string[] { "", "B", "R", "RH", "P", "PH", "F" }) {
sprite.AddLoop("idle" + suffix, "idle", 0.1f);
sprite.AddLoop("held" + suffix, "held", 0.1f);
sprite.Add("fall" + suffix, "fall", 0.06f, "fallLoop" + suffix);
sprite.AddLoop("fallLoop" + suffix, "fallLoop", 0.06f);
sprite.Add("death" + suffix, "death", 0.06f);
sprite.Add("respawn" + suffix, "respawn", 0.03f, "idle" + suffix);
}
sprite.Play("idle");
setupSpriteCallback(sprite);

// make the jelly go invisible when the death animation is done.
sprite.OnFinish += anim => {
Expand Down Expand Up @@ -109,12 +104,12 @@ private static void modGliderUpdate(ILContext il) {
ILCursor cursor = new ILCursor(il);

if (cursor.TryGotoNext(MoveType.After, instr => instr.MatchNewobj<Coroutine>())) {
MethodInfo method = typeof(T).GetMethod("destroyThenRespawnRoutine", BindingFlags.NonPublic | BindingFlags.Instance);
MethodInfo method = typeof(RespawningType).GetMethod("destroyThenRespawnRoutine", BindingFlags.NonPublic | BindingFlags.Instance);
Logger.Log("MaxHelpingHand/RespawningJellyfish", $"Replacing coroutine to make jellyfish respawn at {cursor.Index} in IL for {cursor.Method.FullName}, calling {method.DeclaringType.FullName}.{method.Name}");

cursor.Emit(OpCodes.Ldarg_0);
cursor.EmitDelegate<Func<Coroutine, Glider, Coroutine>>((orig, self) => {
if (self is T) {
if (self is RespawningType) {
return new Coroutine((IEnumerator) method.Invoke(self, new object[0]));
}
Expand Down Expand Up @@ -145,7 +140,7 @@ private void removeAndRespawn() {
internal 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", self.Position);
sprite.Play("death");
jellyfishSpritePlay.Invoke(self, new object[] { "death" });

return respawnRoutine();
}
Expand All @@ -160,7 +155,11 @@ private IEnumerator respawnRoutine() {
self.Visible = true;
self.Position = initialPosition;
setSpeed(Vector2.Zero);
sprite.Play("respawn");
jellyfishSpritePlay.Invoke(self, new object[] { "respawn" });

// refill dashes and cancel ongoing dashes (bounce jellies only)
jellyDashRefill?.Invoke(self, new object[] { -1 });
selfData["dashBufferTimer"] = 0f;

yield return 0.24f;

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
10 changes: 5 additions & 5 deletions Loenn/entities/respawningBounceJellyfish.lua
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ respawningBounceJellyfish.placements = {
soulBound = false,
baseDashCount = 0,
respawnTime = 2.0,
spriteDirectory = "objects/MaxHelpingHand/glider"
spriteDirectory = "objects/MaxHelpingHand/bounceJellyfish"
}
},
{
Expand All @@ -35,7 +35,7 @@ respawningBounceJellyfish.placements = {
soulBound = false,
baseDashCount = 1,
respawnTime = 2.0,
spriteDirectory = "objects/MaxHelpingHand/glider"
spriteDirectory = "objects/MaxHelpingHand/bounceJellyfish"
}
},
{
Expand All @@ -45,14 +45,14 @@ respawningBounceJellyfish.placements = {
soulBound = false,
baseDashCount = 2,
respawnTime = 2.0,
spriteDirectory = "objects/MaxHelpingHand/glider"
spriteDirectory = "objects/MaxHelpingHand/bounceJellyfish"
}
}
}

function respawningBounceJellyfish.sprite(room, entity)
local bubble = entity.platform
local texture = entity.spriteDirectory .. "/idle0"
local texture = entity.spriteDirectory .. "/blue/idle0"

if entity.bubble then
local x, y = entity.x or 0, entity.y or 0
Expand All @@ -71,7 +71,7 @@ function respawningBounceJellyfish.sprite(room, entity)
end

function respawningBounceJellyfish.rectangle(room, entity)
local texture = entity.spriteDirectory .. "/idle0"
local texture = entity.spriteDirectory .. "/blue/idle0"
local sprite = drawableSprite.fromTexture(texture, entity)

return sprite:getRectangle()
Expand Down
2 changes: 1 addition & 1 deletion Loenn/lang/en_gb.lang
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ entities.MaxHelpingHand/RespawningBounceJellyfish.attributes.description.platfor
entities.MaxHelpingHand/RespawningBounceJellyfish.attributes.description.soulBound=Whether the jelly kills the player on death and can't be left behind.
entities.MaxHelpingHand/RespawningBounceJellyfish.attributes.description.baseDashCount=How many dashes the jelly will start with and refill to.
entities.MaxHelpingHand/RespawningBounceJellyfish.attributes.description.respawnTime=The jellyfish will respawn at its starting location after this amount of time (in seconds) after being destroyed.
entities.MaxHelpingHand/RespawningBounceJellyfish.attributes.description.spriteDirectory=The directory containing all the sprites for the respawning jellyfish.\nTo make your own, copy Graphics/Atlases/Gameplay/objects/MaxHelpingHand/glider from the mod zip to Mods/yourmod/Graphics/Atlases/Gameplay/MyMap/myglider and type MyMap/myglider in this field.
entities.MaxHelpingHand/RespawningBounceJellyfish.attributes.description.spriteDirectory=The directory containing all the sprites for the respawning jellyfish.\nTo make your own, copy Graphics/Atlases/Gameplay/objects/MaxHelpingHand/respawningJellyfish from the mod zip to Mods/yourmod/Graphics/Atlases/Gameplay/MyMap/myglider and type MyMap/myglider in this field.

# Rotating Bumper
entities.MaxHelpingHand/RotatingBumper.placements.name.bumper=Bumper (Rotating)
Expand Down
2 changes: 1 addition & 1 deletion everest.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# The mod used to be known as "max480's Helping Hand", and wasn't renamed for compatibility reasons
- Name: MaxHelpingHand
Version: 1.28.8
Version: 1.28.9
DLL: bin/Release/net452/MaxHelpingHand.dll
Dependencies:
- Name: Everest
Expand Down

0 comments on commit d7c622b

Please sign in to comment.