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

Slingshot rewrite #34

Merged
merged 10 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 6 additions & 2 deletions Ahorn/entities/purpleBooster.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ using ..Ahorn, Maple
const placements = Ahorn.PlacementDict(
"Booster (Purple) (Vortex Helper)" => Ahorn.EntityPlacement(
PurpleBooster,
"point"
"point",
Dict{String,Any} (
"QoL" => true,
)
),
"Booster (Lavender) (Vortex Helper)" => Ahorn.EntityPlacement(
PurpleBooster,
"point",
Dict{String, Any}(
"lavender" => true
"lavender" => true,
"QoL" => true
)
)
)
Expand Down
3 changes: 3 additions & 0 deletions Ahorn/lang/en_gb.lang
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ placements.entities.VortexHelper/Lilly.tooltips.maxLength=The maximum distance a

# Purple Booster
placements.entities.VortexHelper/PurpleBooster.tooltips.lavender=Whether the booster should instead be lavender in color, and launch the player forward instead of arcing back.
placements.entities.VortexHelper/PurpleBooster.names.QoL=Version 2
placements.entities.VortexHelper/PurpleBooster.tooltips.QoL=Enables Wallbouncing during the windup animation.\nFixes speedboost not applying when dashing early from booster.\nReduces Freeze Timer upon launch exit and ensures a shorter dashCooldownTimer.


# Switch Block
placements.entities.VortexHelper/SwitchBlock.tooltips.index=Changes the index and color of this Switch Block.
Expand Down
14 changes: 7 additions & 7 deletions Code/Entities/FloorBooster.cs
Original file line number Diff line number Diff line change
Expand Up @@ -275,18 +275,18 @@ private static bool Player_RefillDash(On.Celeste.Player.orig_RefillDash orig, Pl
private static void Player_NormalBegin(On.Celeste.Player.orig_NormalBegin orig, Player self)
{
orig(self);
DynData<Player> playerData = self.GetData();
DynamicData playerData = DynamicData.For(self);
playerData.Set("floorBoosterSpeed", 0f);
playerData.Set<FloorBooster>("lastFloorBooster", null);
playerData.Set("lastFloorBooster", null);
}

private static int Player_NormalUpdate(On.Celeste.Player.orig_NormalUpdate orig, Player self)
{
DynData<Player> playerData = self.GetData();
DynamicData playerData = DynamicData.For(self);

// thanks max480 for the bug report.
if (!playerData.Data.ContainsKey("lastFloorBooster"))
playerData.Set<FloorBooster>("lastFloorBooster", null);
playerData.Set("lastFloorBooster", null);

FloorBooster lastFloorBooster = playerData.Get<FloorBooster>("lastFloorBooster");

Expand All @@ -300,7 +300,7 @@ private static int Player_NormalUpdate(On.Celeste.Player.orig_NormalUpdate orig,
self.LiftSpeed += vec / 1.6f;
self.Speed += vec / 1.6f;

playerData.Set<FloorBooster>("lastFloorBooster", null);
playerData.Set("lastFloorBooster", null);
}

bool touchedFloorBooster = false;
Expand All @@ -324,8 +324,8 @@ private static int Player_NormalUpdate(On.Celeste.Player.orig_NormalUpdate orig,
}
}

if (!touchedFloorBooster)
floorBoosterSpeed = Calc.Approach(playerData.Get<float>("floorBoosterSpeed"), 0f, 4f * Engine.DeltaTime);
if (!touchedFloorBooster && playerData?.Get("floorBoosterSpeed") is float f)
floorBoosterSpeed = Calc.Approach(f, 0f, 4f * Engine.DeltaTime);
playerData.Set("floorBoosterSpeed", floorBoosterSpeed);

return orig(self);
Expand Down
9 changes: 6 additions & 3 deletions Code/Entities/LavenderBooster.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ public class LavenderBooster : Booster
public static readonly ParticleType P_BurstExplodeLavender = new(P_Burst);

private readonly DynData<Booster> boosterData;

private readonly bool QoL;
public LavenderBooster(EntityData data, Vector2 offset)
: base(data.Position + offset, red: false)
{
this.boosterData = new DynData<Booster>(this);
QoL = data.Bool("QoL", false);

Sprite oldSprite = this.boosterData.Get<Sprite>("sprite");
Remove(oldSprite);
Expand Down Expand Up @@ -56,11 +57,13 @@ public static void Unhook()
private static void Booster_PlayerReleased(On.Celeste.Booster.orig_PlayerReleased orig, Booster self)
{
orig(self);
if (Util.TryGetPlayer(out Player player) && player.LastBooster is LavenderBooster)
if (Util.TryGetPlayer(out Player player) && player.LastBooster is LavenderBooster l)
{
Audio.Play(SFX.game_05_redbooster_end, player.Center);
PurpleBooster.LaunchPlayerParticles(player, player.DashDir, P_BurstExplodeLavender);
PurpleBooster.PurpleBoosterExplodeLaunch(player, player.GetData(), self.Center - player.DashDir, null, -1f);
DynamicData dyn = DynamicData.For(player);
dyn.Set(PurpleBooster.QUALITYOFLIFEUPDATE, l.QoL);
PurpleBooster.PurpleBoosterExplodeLaunch(player, dyn, self.Center - player.DashDir, null, -1f);
}
}

Expand Down
128 changes: 107 additions & 21 deletions Code/Entities/PurpleBooster.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
using Celeste.Mod.VortexHelper.Misc;
using Celeste.Mod.VortexHelper.Misc.Extensions;
using Microsoft.Xna.Framework;
using Mono.Cecil.Cil;
using Monocle;
using MonoMod.Cil;
using MonoMod.RuntimeDetour;
using MonoMod.Utils;
using System;
using System.Collections;
Expand All @@ -15,6 +18,7 @@
public class PurpleBooster : Entity
{
internal const string POSSIBLE_EARLY_DASHSPEED = "purpleBoostPossibleEarlyDashSpeed";
internal const string QUALITYOFLIFEUPDATE = "purpleBoostQoL";

private readonly Sprite sprite;
private readonly Wiggler wiggler;
Expand Down Expand Up @@ -43,8 +47,11 @@
public static readonly ParticleType P_BurstExplode = new(Booster.P_Burst);

private readonly SoundSource loopingSfx;
public readonly bool QoL;
public PurpleBooster(EntityData data, Vector2 offset)
: this(data.Position + offset) { }
: this(data.Position + offset) {
QoL = data.Bool("QoL");
}

public PurpleBooster(Vector2 position)
: base(position)
Expand Down Expand Up @@ -124,8 +131,9 @@
{
player.StateMachine.State = VortexHelperModule.PurpleBoosterState;
player.Speed = Vector2.Zero;
DynData<Player> playerData = player.GetData();
DynamicData playerData = DynamicData.For(player);
playerData.Set("boostTarget", booster.Center);
playerData.Set(QUALITYOFLIFEUPDATE, booster.QoL);
booster.StartedBoosting = true;
}

Expand Down Expand Up @@ -335,7 +343,7 @@
flag = null;
else
{
MapMetaModeProperties meta = level.Session.MapData.GetMeta();

Check warning on line 346 in Code/Entities/PurpleBooster.cs

View workflow job for this annotation

GitHub Actions / build

'MapDataExt.GetMeta(MapData)' is obsolete: 'Use MapData.Meta instead.'

Check warning on line 346 in Code/Entities/PurpleBooster.cs

View workflow job for this annotation

GitHub Actions / build

'MapDataExt.GetMeta(MapData)' is obsolete: 'Use MapData.Meta instead.'
flag = meta?.TheoInBubble;
}

Expand All @@ -352,7 +360,7 @@
public static int PurpleBoostUpdate()
{
Util.TryGetPlayer(out Player player);
DynData<Player> playerData = player.GetData();
DynamicData playerData = DynamicData.For(player);

Vector2 boostTarget = playerData.Get<Vector2>("boostTarget");
Vector2 value = Input.Aim.Value * 3f;
Expand Down Expand Up @@ -385,10 +393,9 @@
public static void PurpleBoostEnd()
{
Util.TryGetPlayer(out Player player);

Vector2 boostTarget = player.GetData().Get<Vector2>("boostTarget");
DynamicData playerData = DynamicData.For(player);
Vector2 boostTarget = playerData.Get<Vector2>("boostTarget");
Vector2 vector = (boostTarget - player.Collider.Center).Floor();

player.MoveToX(vector.X, null);
player.MoveToY(vector.Y, null);
}
Expand All @@ -407,7 +414,7 @@
Celeste.Freeze(0.05f); // this freeze makes fastbubbling much more lenient

Util.TryGetPlayer(out Player player);
DynData<Player> playerData = player.GetData();
DynamicData playerData = DynamicData.For(player);
player.DashDir = Input.GetAimVector(player.Facing);
playerData.Set(POSSIBLE_EARLY_DASHSPEED, Vector2.Zero);

Expand All @@ -426,33 +433,70 @@

public static int PurpleDashingUpdate()
{
Util.TryGetPlayer(out Player player);
DynamicData playerData = DynamicData.For(player);
bool QoL = playerData.Get(QUALITYOFLIFEUPDATE) is bool b && b;
if (Input.DashPressed || Input.CrouchDashPressed)
{
Util.TryGetPlayer(out Player player);
DynData<Player> playerData = player.GetData();

player.LiftSpeed += playerData.Get<Vector2>(POSSIBLE_EARLY_DASHSPEED);

if (QoL) player.Speed += playerData.Get<Vector2>(POSSIBLE_EARLY_DASHSPEED);
else player.LiftSpeed += playerData.Get<Vector2>(POSSIBLE_EARLY_DASHSPEED);
return player.StartDash();
}

if (QoL && Math.Abs(player.DashDir.X) <= 0.02 &&
Input.Jump.Pressed && player.CanUnDuck &&
(player.DashDir.Y < 0 ? playerData.Get<Vector2>(POSSIBLE_EARLY_DASHSPEED).Y == 0 : playerData.Get<Vector2>(POSSIBLE_EARLY_DASHSPEED).Y < 0))
{
if ((bool)Util.player_WallJumpCheck.Invoke(player, new object[1]{1}))
{
Util.player_SuperWallJump.Invoke(player,new object[1]{-1});
return 0;
}else if ((bool) Util.player_WallJumpCheck.Invoke(player, new object[1]{-1}))
{
Util.player_SuperWallJump.Invoke(player, new object[1]{1});
return 0;
}
}
return VortexHelperModule.PurpleBoosterDashState;
}

public static IEnumerator PurpleDashingCoroutine()
{
float t = 0f;
Util.TryGetPlayer(out Player player);
DynData<Player> playerData = player.GetData();
DynamicData playerData = DynamicData.For(player);
Vector2 origin = playerData.Get<Vector2>("boostTarget");
if(playerData.Get(QUALITYOFLIFEUPDATE) is bool a && a) {
yield return null;
player.DashDir = playerData.Get<Vector2>("lastAim");
}

Vector2 earlyExitBoost;
Vector2 earlyExitBoost = Vector2.Zero;
while (t < 1f)
{
t = Calc.Approach(t, 1.0f, Engine.DeltaTime * 1.5f);
Vector2 vec = origin + Vector2.UnitY * 6f + player.DashDir * 60f * (float) Math.Sin(t * Math.PI);

playerData.Set(POSSIBLE_EARLY_DASHSPEED, earlyExitBoost = (t > .6f) ? (t - .5f) * 200f * -player.DashDir : Vector2.Zero);

if(playerData.Get(QUALITYOFLIFEUPDATE) is bool b && b)
{
if(t == 1f)
{
// frame 0: mimics speed at launch exit exactly, Input.MoveX.Value == -Math.Sign(player.DashDir) ? 300 : 250
earlyExitBoost = 250f * -player.DashDir;
Vector2 aim = Input.GetAimVector(player.Facing).EightWayNormal().Sign();
if (aim.X == Math.Sign(earlyExitBoost.X)) earlyExitBoost.X *= 1.2f;
if (aim.Y == Math.Sign(earlyExitBoost.Y)) earlyExitBoost.Y *= 1.2f;
} else if(t > 0.93f)
{
// frame -2 : 200 speed
// frame -1 : 205 speed
earlyExitBoost = (float)Math.Round(210f * t) * -player.DashDir;
}
}
else if (t > 0.6f)
{
earlyExitBoost = (t - .5f) * 200f * -player.DashDir;
}
playerData.Set(POSSIBLE_EARLY_DASHSPEED, earlyExitBoost);

if (player.CollideCheck<Solid>(vec))
{
Expand All @@ -467,11 +511,18 @@
PurpleBoosterExplodeLaunch(player, playerData, player.Center - player.DashDir, origin);
}

public static void PurpleBoosterExplodeLaunch(Player player, DynData<Player> playerData, Vector2 from, Vector2? origin, float factor = 1f)
public static void PurpleDashingEnd()
{
Util.TryGetPlayer(out Player player);
DynamicData playerData = DynamicData.For(player);
playerData.Set(QUALITYOFLIFEUPDATE, false);
}
public static void PurpleBoosterExplodeLaunch(Player player, DynamicData playerData, Vector2 from, Vector2? origin, float factor = 1f)
{
bool QoL = playerData?.Get(QUALITYOFLIFEUPDATE) is bool b && b;
Input.Rumble(RumbleStrength.Strong, RumbleLength.Medium);
Celeste.Freeze(0.1f);
playerData.Set<float?>("launchApproachX", null);
Celeste.Freeze(QoL ? 0.05f : 0.1f);
playerData.Set("launchApproachX", null);
Level level = player.SceneAs<Level>();

if (origin is not null)
Expand All @@ -493,6 +544,8 @@
if (!player.Inventory.NoRefills)
player.RefillDash();
player.RefillStamina();
if (QoL && playerData?.Get("dashCooldownTimer") is float f)
playerData.Set("dashCooldownTimer", f > 0.06f ? 0.06f : f);
player.StateMachine.State = Player.StLaunch;
player.Speed *= factor;
}
Expand All @@ -501,14 +554,18 @@

internal static class Hooks
{

public static void Hook()
{
On.Celeste.Player.ctor += Player_ctor;
IL.Celeste.Player.WallJumpCheck += Player_WallJumpCheck;
}


public static void Unhook()
{
On.Celeste.Player.ctor -= Player_ctor;
IL.Celeste.Player.WallJumpCheck -= Player_WallJumpCheck;
}

private static void Player_ctor(On.Celeste.Player.orig_ctor orig, Player self, Vector2 position, PlayerSpriteMode spriteMode)
Expand All @@ -519,7 +576,36 @@
VortexHelperModule.PurpleBoosterState = self.StateMachine.AddState(PurpleBoostUpdate, PurpleBoostCoroutine, PurpleBoostBegin, PurpleBoostEnd);

// Custom Purple Booster State (Arc Motion)
VortexHelperModule.PurpleBoosterDashState = self.StateMachine.AddState(PurpleDashingUpdate, PurpleDashingCoroutine, PurpleDashingBegin);
VortexHelperModule.PurpleBoosterDashState = self.StateMachine.AddState(PurpleDashingUpdate, PurpleDashingCoroutine, PurpleDashingBegin, PurpleDashingEnd);
}

private static void Player_WallJumpCheck(ILContext il)
{
ILCursor cursor = new ILCursor(il);
if (cursor.TryGotoNext(MoveType.After, i => i.MatchCallvirt<Player>("get_DashAttacking")))
{
cursor.Emit(OpCodes.Ldarg_0);
cursor.EmitDelegate<Func<bool, Player, bool>>((b, p) =>
{
if (b) return true;
try { if (DynamicData.For(p).TryGet<bool>(QUALITYOFLIFEUPDATE, out bool c) && c) return true; }
catch (NullReferenceException) { return false; }
return false;
});
}
if(cursor.TryGotoNext(MoveType.After, i => i.MatchLdcR4(-1) && i.Next.MatchCeq()))
{
cursor.Emit(OpCodes.Ldarg_0);
cursor.EmitDelegate<Func<float, Player, float>>((f, p) =>
{
try { if (DynamicData.For(p).TryGet<bool>(QUALITYOFLIFEUPDATE, out bool c) && c) return p.DashDir.Y; }
catch (NullReferenceException) { return f; }
return f;
});
}


}

}
}
13 changes: 0 additions & 13 deletions Code/Misc/Extensions/PlayerExt.cs

This file was deleted.

10 changes: 10 additions & 0 deletions Code/Misc/Util.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Monocle;
using MonoMod.Utils;
using System.Reflection;

namespace Celeste.Mod.VortexHelper.Misc;

Expand All @@ -10,4 +12,12 @@ public static bool TryGetPlayer(out Player player)
player = Engine.Scene?.Tracker?.GetEntity<Player>();
return player is not null;
}

public static void LoadDelegates()
{
player_WallJumpCheck = typeof(Player).GetMethod("WallJumpCheck", BindingFlags.NonPublic | BindingFlags.Instance);
player_SuperWallJump = typeof(Player).GetMethod("SuperWallJump", BindingFlags.NonPublic | BindingFlags.Instance);
}
public static MethodInfo player_WallJumpCheck;
public static MethodInfo player_SuperWallJump;
}
2 changes: 2 additions & 0 deletions Code/VortexHelperModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ public override void Load()
PufferBarrierRenderer.Hooks.Hook();
StaticMoverWithLiftSpeed.Hooks.Hook();
MiscHooks.Hook();

Util.LoadDelegates();
}

public override void Unload()
Expand Down
Loading
Loading