Skip to content

Commit 9eab6d9

Browse files
committed
Add option in Configure Dream Tunnel Dash Trigger to allow screen transitions while dream tunnel dashing
1 parent 4315998 commit 9eab6d9

File tree

5 files changed

+93
-14
lines changed

5 files changed

+93
-14
lines changed

Loenn/lang/en_gb.lang

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,7 @@ triggers.CommunalHelper/ConfigureDreamTunnelDashTrigger.attributes.description.s
10671067
triggers.CommunalHelper/ConfigureDreamTunnelDashTrigger.attributes.description.customSpeed=If the "Speed Configuration" field is set to "Use Custom Speed", this specifies the speed of dream tunnel dashes (measured in pixels per second). Does nothing otherwise.
10681068
triggers.CommunalHelper/ConfigureDreamTunnelDashTrigger.attributes.description.allowDashCancels=Allows the dream tunnel dash to be cancelled before tunnelling through solids. Replicates vanilla behavior of being able to instant hyper, for example, on dream blocks.
10691069
triggers.CommunalHelper/ConfigureDreamTunnelDashTrigger.attributes.description.redirectConsumesNormalDash=Whether dream tunnel dash redirects should consume the player's normal dashes instead of their dream tunnel dashes.
1070+
triggers.CommunalHelper/ConfigureDreamTunnelDashTrigger.attributes.description.allowTransitions=Whether to allow screen transitions while dream tunnel dashing.
10701071

10711072
# Follow Shapeshifter Path Trigger
10721073
triggers.CommunalHelper/FollowShapeshifterPathTrigger.placements.name.follow_path=Follow Shapeshifter Path

Loenn/triggers/configure_dream_tunnel_dash.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ return {
1212
customSpeed = 0,
1313
allowDashCancels = false,
1414
redirectConsumesNormalDash = false,
15+
allowTransitions = false,
1516
}
1617
}
1718
},

src/DashStates/DreamTunnelDash/DreamTunnelDash.cs

Lines changed: 89 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public struct DreamTunnelDashConfiguration
7373
public float CustomSpeed;
7474
public bool AllowDashCancels;
7575
public bool RedirectConsumesNormalDash;
76+
public bool AllowTransitions;
7677
}
7778

7879
public static readonly DreamTunnelDashConfiguration DefaultDreamTunnelDashConfiguration = new()
@@ -85,6 +86,7 @@ public struct DreamTunnelDashConfiguration
8586
CustomSpeed = 0,
8687
AllowDashCancels = false,
8788
RedirectConsumesNormalDash = false,
89+
AllowTransitions = false,
8890
};
8991

9092

@@ -106,6 +108,9 @@ public static void Load()
106108
IL.Celeste.Player.IsRiding_JumpThru += Player_IsRiding_JumpThru;
107109
IL.Celeste.Player.OnCollideH += State_DreamDashEqual;
108110
IL.Celeste.Player.OnCollideV += State_DreamDashEqual;
111+
IL.Celeste.Player.BeforeUpTransition += Player_BeforeUpTransition;
112+
IL.Celeste.Player.BeforeDownTransition += Player_BeforeDownTransition;
113+
IL.Celeste.Player.TransitionTo += Player_TransitionTo;
109114
hook_Player_orig_Update = new ILHook(
110115
typeof(Player).GetMethod("orig_Update"),
111116
Player_orig_Update);
@@ -140,6 +145,9 @@ public static void Unload()
140145
IL.Celeste.Player.IsRiding_JumpThru -= Player_IsRiding_JumpThru;
141146
IL.Celeste.Player.OnCollideH -= State_DreamDashEqual;
142147
IL.Celeste.Player.OnCollideV -= State_DreamDashEqual;
148+
IL.Celeste.Player.BeforeUpTransition -= Player_BeforeUpTransition;
149+
IL.Celeste.Player.BeforeDownTransition -= Player_BeforeDownTransition;
150+
IL.Celeste.Player.TransitionTo -= Player_TransitionTo;
143151
hook_Player_orig_Update.Dispose();
144152
hook_Player_orig_UpdateSprite.Dispose();
145153

@@ -360,33 +368,100 @@ private static void Player_IsRiding_JumpThru(ILContext il)
360368
State_DreamDashNotEqual_And(il);
361369
}
362370

371+
private static void Player_BeforeUpTransition(ILContext il)
372+
{
373+
ILCursor cursor = new(il);
374+
375+
CheckState(cursor, Player.StRedDash, false);
376+
CheckState(cursor, Player.StRedDash, false, true);
377+
}
378+
379+
private static void Player_BeforeDownTransition(ILContext il)
380+
{
381+
ILCursor cursor = new(il);
382+
383+
CheckState(cursor, Player.StRedDash, false, true);
384+
}
385+
386+
private static void Player_TransitionTo(ILContext il)
387+
{
388+
ILCursor cursor = new(il);
389+
390+
if (cursor.TryGotoNext(MoveType.Before, instr => instr.MatchCall<Actor>("MoveTowardsX")))
391+
{
392+
UseInsteadIfDreamTunnelDashing(cursor, NaiveMoveTowardsX);
393+
}
394+
if (cursor.TryGotoNext(MoveType.Before, instr => instr.MatchCall<Actor>("MoveTowardsY")))
395+
{
396+
UseInsteadIfDreamTunnelDashing(cursor, NaiveMoveTowardsY);
397+
}
398+
return;
399+
400+
// utility to replace one method call with another of the same signature if the player is dream dashing
401+
static void UseInsteadIfDreamTunnelDashing<T>(ILCursor cursor, T cb) where T : Delegate
402+
{
403+
ILLabel normalCall = cursor.DefineLabel();
404+
ILLabel afterNormalCall = cursor.DefineLabel();
405+
406+
cursor.Emit(OpCodes.Ldarg_0);
407+
cursor.EmitDelegate<Func<Player, bool>>(player => player.StateMachine.State == St.DreamTunnelDash);
408+
cursor.Emit(OpCodes.Brfalse_S, normalCall);
409+
cursor.EmitDelegate(cb);
410+
cursor.Emit(OpCodes.Br_S, afterNormalCall);
411+
cursor.MarkLabel(normalCall);
412+
// normal method call would be here
413+
cursor.Index++;
414+
cursor.MarkLabel(afterNormalCall);
415+
}
416+
}
417+
418+
// hopefully the tasers don't kill me for this
419+
private static void NaiveMoveTowardsX(Player player, float targetX, float maxAmount, Collision _)
420+
{
421+
float toX = Calc.Approach(player.ExactPosition.X, targetX, maxAmount);
422+
player.movementCounter.X += (float) ((double) toX - player.Position.X - player.movementCounter.X);
423+
int x = (int) Math.Round(player.movementCounter.X);
424+
player.Position.X += x;
425+
player.movementCounter.X -= x;
426+
}
427+
428+
private static void NaiveMoveTowardsY(Player player, float targetY, float maxAmount, Collision _)
429+
{
430+
float toY = Calc.Approach(player.ExactPosition.Y, targetY, maxAmount);
431+
player.movementCounter.Y += (float) ((double) toY - player.Position.Y - player.movementCounter.Y);
432+
int y = (int) Math.Round(player.movementCounter.Y);
433+
player.Position.Y += y;
434+
player.movementCounter.Y -= y;
435+
}
436+
363437
// Patch any method that checks the player's State
364438
/// <summary>
365439
/// Use if decompilation says <c>State==9</c> and NOT followed by <c>&amp;&amp;</c>.
366440
/// </summary>
367-
private static readonly ILContext.Manipulator State_DreamDashEqual = il => Check_State_DreamDash(new ILCursor(il), true);
441+
private static readonly ILContext.Manipulator State_DreamDashEqual = il => CheckState(new ILCursor(il), Player.StDreamDash, true);
368442
/// <summary>
369443
/// Use if decompilation says <c>State!=9</c> and NOT followed by <c>&amp;&amp;</c>.
370444
/// </summary>
371-
private static readonly ILContext.Manipulator State_DreamDashNotEqual = il => Check_State_DreamDash(new ILCursor(il), false);
445+
private static readonly ILContext.Manipulator State_DreamDashNotEqual = il => CheckState(new ILCursor(il), Player.StDreamDash, false);
372446
/// <summary>
373447
/// Use if decompilation says <c>State==9</c> and IS followed by <c>&amp;&amp;</c>.
374448
/// </summary>
375-
private static readonly ILContext.Manipulator State_DreamDashEqual_And = il => Check_State_DreamDash(new ILCursor(il), true, true);
449+
private static readonly ILContext.Manipulator State_DreamDashEqual_And = il => CheckState(new ILCursor(il), Player.StDreamDash, true, true);
376450
/// <summary>
377451
/// Use if decompilation says <c>State!=9</c> and IS followed by <c>&amp;&amp;</c>.
378452
/// </summary>
379-
private static readonly ILContext.Manipulator State_DreamDashNotEqual_And = il => Check_State_DreamDash(new ILCursor(il), false, true);
453+
private static readonly ILContext.Manipulator State_DreamDashNotEqual_And = il => CheckState(new ILCursor(il), Player.StDreamDash, false, true);
380454
/// <summary>
381455
/// Patch any method that checks the player's state.
382456
/// </summary>
383-
/// <remarks>Checks for <c>ldc.i4.s 9</c></remarks>
384-
/// <param name="cursor"></param>
385-
/// <param name="equal">Whether the decompilation says State == 9</param>
457+
/// <remarks>Checks for <c>ldc.i4.s &lt;state&gt;</c></remarks>
458+
/// <param name="cursor">The ILCursor to use</param>
459+
/// <param name="state">The state to check for</param>
460+
/// <param name="equal">Whether the decompilation says <c>State == &lt;state&gt;</c></param>
386461
/// <param name="and">Whether the check is followed by <c>&amp;&amp;</c></param>
387-
private static void Check_State_DreamDash(ILCursor cursor, bool equal, bool and = false)
462+
private static void CheckState(ILCursor cursor, int state, bool equal, bool and = false)
388463
{
389-
if (cursor.TryGotoNext(instr => instr.MatchLdcI4(Player.StDreamDash) &&
464+
if (cursor.TryGotoNext(instr => instr.MatchLdcI4(state) &&
390465
instr.Previous != null && instr.Previous.MatchCallvirt<StateMachine>("get_State")))
391466
{
392467
Instruction idx = cursor.Next;
@@ -424,20 +499,20 @@ private static void Check_State_DreamDash(ILCursor cursor, bool equal, bool and
424499
private static void Player_orig_Update(ILContext il)
425500
{
426501
ILCursor cursor = new(il);
427-
Check_State_DreamDash(cursor, true);
428-
Check_State_DreamDash(cursor, false, true);
429-
Check_State_DreamDash(cursor, false, true);
502+
CheckState(cursor, Player.StDreamDash, true);
503+
CheckState(cursor, Player.StDreamDash, false, true);
504+
CheckState(cursor, Player.StDreamDash, false, true);
430505
// Not used because we DO want to enforce Level bounds.
431506
//Check_State_DreamDash(cursor, false, true);
432507
}
433508

434-
// Kill the player if they attempt to DreamTunnel out of the level
509+
// Kill the player if they attempt to DreamTunnel out of the level and transitions are not enabled
435510
private static void Level_EnforceBounds(On.Celeste.Level.orig_EnforceBounds orig, Level self, Player player)
436511
{
437512
if (DynamicData.For(self).Get<Coroutine>("transition") is not null)
438513
return;
439514

440-
if (player.StateMachine.State == St.DreamTunnelDash)
515+
if (player.StateMachine.State == St.DreamTunnelDash && !CommunalHelperModule.Session.CurrentDreamTunnelDashConfiguration.AllowTransitions)
441516
{
442517
Rectangle bounds = self.Bounds;
443518
if (player.Right > bounds.Right || player.Left < bounds.Left || player.Top < bounds.Top || player.Bottom > bounds.Bottom)

src/States/DreamTunnelDash.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public static void DreamTunnelDashBegin(this Player player)
1818
if (player.dreamSfxLoop == null)
1919
{
2020
player.dreamSfxLoop = new SoundSource();
21+
player.dreamSfxLoop.DisposeOnTransition = !CommunalHelperModule.Session.CurrentDreamTunnelDashConfiguration.AllowTransitions;
2122
player.Add(player.dreamSfxLoop);
2223
}
2324

src/Triggers/ConfigureDreamTunnelDashTrigger.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public ConfigureDreamTunnelDashTrigger(EntityData data, Vector2 offset)
2020
CustomSpeed = data.Float("customSpeed", 0f),
2121
AllowDashCancels = data.Bool("allowDashCancels", false),
2222
RedirectConsumesNormalDash = data.Bool("redirectConsumesNormalDash", false),
23+
AllowTransitions = data.Bool("allowTransitions", false),
2324
};
2425
}
2526

0 commit comments

Comments
 (0)