From 858b485de9da371c7bf0979f20962e8b690e16fe Mon Sep 17 00:00:00 2001 From: SilverDorian46 <86711559+SilverDorian46@users.noreply.github.com> Date: Wed, 8 Oct 2025 22:31:29 +0400 Subject: [PATCH] Implement Cancel StLaunch On Ground as a variant --- Ahorn/libraries/extendedVariantDictionary.jl | 1 + Dialog/English.txt | 4 ++ .../booleanExtendedVariantTrigger.lua | 1 + Module/ExtendedVariantsModule.cs | 2 + UI/ModOptionsEntries.cs | 4 +- Variants/CancelStLaunchOnGround.cs | 69 +++++++++++++++++++ 6 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 Variants/CancelStLaunchOnGround.cs diff --git a/Ahorn/libraries/extendedVariantDictionary.jl b/Ahorn/libraries/extendedVariantDictionary.jl index 9f21d2d..9055ea8 100755 --- a/Ahorn/libraries/extendedVariantDictionary.jl +++ b/Ahorn/libraries/extendedVariantDictionary.jl @@ -92,6 +92,7 @@ const BooleanVariants = [ "BadelineChasersEverywhere", "BounceEverywhere", "BufferableGrab", + "CancelStLaunchOnGround", "ChangePatternsOfExistingBosses", "ConsistentThrowing", "CornerboostProtection", diff --git a/Dialog/English.txt b/Dialog/English.txt index d35f180..5963b4a 100755 --- a/Dialog/English.txt +++ b/Dialog/English.txt @@ -414,3 +414,7 @@ MODOPTIONS_EXTENDEDVARIANTS_CLIMBDOWNSPEED= Climb Down Speed MODOPTIONS_EXTENDEDVARIANTS_CLIMBJUMPSTAMINACOST= Climb Jump Stamina Cost MODOPTIONS_EXTENDEDVARIANTS_CLIMBUPSTAMINADRAINRATE= Climb Up Stamina Drain Rate MODOPTIONS_EXTENDEDVARIANTS_CLIMBHOLDSTAMINADRAINRATE= Climb Hold Stamina Drain Rate + +MODOPTIONS_EXTENDEDVARIANTS_CANCELSTLAUNCHONGROUND= Cancel StLaunch On Ground +MODOPTIONS_EXTENDEDVARIANTS_CANCELSTLAUNCHONGROUND_HINT_1= Allows the player to exit StLaunch immediately when touching the ground. +MODOPTIONS_EXTENDEDVARIANTS_CANCELSTLAUNCHONGROUND_HINT_2= This allows jumping earlier in situations where the player is launched towards the ground. diff --git a/Loenn/triggers/booleanExtendedVariantTrigger.lua b/Loenn/triggers/booleanExtendedVariantTrigger.lua index 03b9cdc..c52ea7f 100755 --- a/Loenn/triggers/booleanExtendedVariantTrigger.lua +++ b/Loenn/triggers/booleanExtendedVariantTrigger.lua @@ -34,6 +34,7 @@ trigger.fieldInformation = { "BadelineChasersEverywhere", "BounceEverywhere", "BufferableGrab", + "CancelStLaunchOnGround", "ChangePatternsOfExistingBosses", "ConsistentThrowing", "CornerboostProtection", diff --git a/Module/ExtendedVariantsModule.cs b/Module/ExtendedVariantsModule.cs index 502a406..e81cc72 100755 --- a/Module/ExtendedVariantsModule.cs +++ b/Module/ExtendedVariantsModule.cs @@ -68,6 +68,7 @@ public enum Variant { WaterSurfaceSpeedX, WaterSurfaceSpeedY, LiftboostCapX, LiftboostCapUp, LiftboostCapDown, AutoJump, SlowfallGravityMultiplier, SlowfallSpeedThreshold, AutoDash, ConsistentThrowing, JumpBoost, ClimbUpSpeed, ClimbDownSpeed, ClimbJumpStaminaCost, ClimbUpStaminaDrainRate, ClimbHoldStaminaDrainRate, + CancelStLaunchOnGround, // vanilla variants AirDashes, DashAssist, VanillaGameSpeed, Hiccups, InfiniteStamina, Invincible, InvisibleMotion, LowFriction, MirrorMode, NoGrabbing, PlayAsBadeline, @@ -242,6 +243,7 @@ public ExtendedVariantsModule() { VariantHandlers[Variant.ClimbJumpStaminaCost] = new ClimbJumpStaminaCost(); VariantHandlers[Variant.ClimbUpStaminaDrainRate] = new ClimbUpStaminaDrainRate(); VariantHandlers[Variant.ClimbHoldStaminaDrainRate] = new ClimbHoldStaminaDrainRate(); + VariantHandlers[Variant.CancelStLaunchOnGround] = new CancelStLaunchOnGround(); // vanilla variants VariantHandlers[Variant.AirDashes] = new AirDashes(); VariantHandlers[Variant.DashAssist] = new DashAssist(); diff --git a/UI/ModOptionsEntries.cs b/UI/ModOptionsEntries.cs index 48fed09..086fd18 100644 --- a/UI/ModOptionsEntries.cs +++ b/UI/ModOptionsEntries.cs @@ -343,7 +343,7 @@ public void CreateAllOptions(VariantCategory category, bool includeMasterSwitch, qualityOfLifeSubmenu.GetHighlightColor = () => getColorForVariantSubmenu(new List { Variant.UltraProtection, Variant.LiftboostProtection, Variant.CornerboostProtection, Variant.CrouchDashFix, Variant.AlternativeBuffering, Variant.MultiBuffering, - Variant.SaferDiagonalSmuggle, Variant.DashBeforePickup, Variant.ThrowIgnoresForcedMove, Variant.ConsistentThrowing + Variant.SaferDiagonalSmuggle, Variant.DashBeforePickup, Variant.ThrowIgnoresForcedMove, Variant.ConsistentThrowing, Variant.CancelStLaunchOnGround }); elementsToHideOnToggle = new List() { resetVanillaVariants, resetExtendedVariants, title, movementSubmenu, gameElementsSubmenu, visualSubmenu, gameplayTweaksSubmenu, qualityOfLifeSubmenu }; @@ -808,7 +808,7 @@ public void CreateAllOptions(VariantCategory category, bool includeMasterSwitch, if (category == VariantCategory.QualityOfLife) { menu.Add(buildHeading(menu, "QUALITYOFLIFE")); - foreach (Variant variant in new Variant[] { Variant.UltraProtection, Variant.LiftboostProtection, Variant.CornerboostProtection, Variant.CrouchDashFix, Variant.AlternativeBuffering, Variant.ConsistentThrowing }) { + foreach (Variant variant in new Variant[] { Variant.UltraProtection, Variant.LiftboostProtection, Variant.CornerboostProtection, Variant.CrouchDashFix, Variant.AlternativeBuffering, Variant.ConsistentThrowing, Variant.CancelStLaunchOnGround }) { TextMenuExt.OnOff option = getToggleOption(variant); menu.Add(option); option.AddDescription(menu, Dialog.Clean($"MODOPTIONS_EXTENDEDVARIANTS_{variant}_HINT_2")); diff --git a/Variants/CancelStLaunchOnGround.cs b/Variants/CancelStLaunchOnGround.cs new file mode 100644 index 0000000..cecd57c --- /dev/null +++ b/Variants/CancelStLaunchOnGround.cs @@ -0,0 +1,69 @@ +using System; +using System.Reflection; +using Celeste; +using Celeste.Mod; +using Microsoft.Xna.Framework; +using Mono.Cecil.Cil; +using MonoMod.Cil; + +using static ExtendedVariants.Module.ExtendedVariantsModule; + +namespace ExtendedVariants.Variants; + +public class CancelStLaunchOnGround : AbstractExtendedVariant +{ + private readonly FieldInfo f_Player_onGround = typeof(Player) + .GetField("onGround", BindingFlags.NonPublic | BindingFlags.Instance); + + public CancelStLaunchOnGround() : base(variantType: typeof(bool), defaultVariantValue: false) { } + + public override object ConvertLegacyVariantValue(int value) => value != 0; + + public override void Load() => IL.Celeste.Player.LaunchUpdate += ILPlayerLaunchUpdate; + + public override void Unload() => IL.Celeste.Player.LaunchUpdate -= ILPlayerLaunchUpdate; + + private void ILPlayerLaunchUpdate(ILContext il) + { + ILCursor cur = new(il); + + if (f_Player_onGround is null) + throw new Exception("Private field player.onGround hasn't been found!"); + + Logger.Log(LogLevel.Info, $"ExtendedVariantMode/{nameof(CancelStLaunchOnGround)}", + "Patching IL for Player.LaunchUpdate to add grounded condition" + ); + + /* + GroundedCheck(this.onGround) || + vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + if ( Speed.Length() < 220f) + return 0; // StNormal + + return 7; // StLaunch + */ + ILLabel returnStNormalLabel = cur.DefineLabel(); + + cur.GotoNext(MoveType.AfterLabel, + instr => instr.MatchLdarg(0), + instr => instr.MatchLdflda(nameof(Player.Speed)), + instr => instr.MatchCall(nameof(Vector2.Length)) + ); // search forward for this.Speed.Length() + + // Clone the cursor + ILCursor clone = cur.Clone(); + clone.GotoNext(MoveType.AfterLabel, instr => instr.MatchLdcI4(0)); // search forward for 0 (StNormal) + clone.MarkLabel(returnStNormalLabel); // mark label here + + // Back to the original cursor + cur.Emit(OpCodes.Ldarg_0); // this + cur.Emit(OpCodes.Ldfld, f_Player_onGround); // this.onGround + cur.EmitDelegate(GroundedCheck); // GroundedCheck(this.onGround) + cur.Emit(OpCodes.Brtrue_S, returnStNormalLabel); // go to that label if the check returns true + + // Since we can't access private members of base game classes with this mod, + // we have to pass player.onGround as an argument. + bool GroundedCheck(bool player_onGround) + => GetVariantValue(Variant.CancelStLaunchOnGround) && player_onGround; + } +}