Skip to content

Commit

Permalink
Merge branch 'dev' into core
Browse files Browse the repository at this point in the history
  • Loading branch information
maddie480 committed Nov 8, 2023
2 parents ae4ae9f + e3440dd commit 8f96c2c
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 5 deletions.
12 changes: 10 additions & 2 deletions Celeste.Mod.mm/Mod/UI/OuiModToggler.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Ionic.Zip;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Monocle;
using System;
using System.Collections;
Expand Down Expand Up @@ -379,7 +380,7 @@ private static bool WrappingLinearSearch<T>(List<T> items, Func<T, bool> predica
}

nextModIndex = startIndex;
return false;
return predicate(items[nextModIndex]);
}

private void AddSearchBox(TextMenu menu) {
Expand Down Expand Up @@ -437,7 +438,14 @@ void exitSearch(TextMenuExt.TextBox textBox) {

textBox.OnTextInputCharActions['\t'] = searchNextMod(false);
textBox.OnTextInputCharActions['\n'] = (_) => { };
textBox.OnTextInputCharActions['\r'] = searchNextMod(false);
textBox.OnTextInputCharActions['\r'] = (textBox) => {
if (MInput.Keyboard.CurrentState.IsKeyDown(Keys.LeftShift)
|| MInput.Keyboard.CurrentState.IsKeyDown(Keys.RightShift)) {
searchNextMod(true)(textBox);
} else {
searchNextMod(false)(textBox);
}
};
textBox.OnTextInputCharActions['\b'] = (textBox) => {
if (textBox.DeleteCharacter()) {
Audio.Play(SFX.ui_main_rename_entry_backspace);
Expand Down
39 changes: 39 additions & 0 deletions Celeste.Mod.mm/Patches/Level.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public static void RegisterLoadOverride(Level level, LoadOverride loadOverride)
[Obsolete("Use RegisterLoadOverride instead")] public static int SkipScreenWipes;
[Obsolete("Use RegisterLoadOverride instead")] public static bool ShouldAutoPause = false;

public Vector2 TransitionDirection { get; private set; } = new();

public delegate Entity EntityLoader(Level level, LevelData levelData, Vector2 offset, EntityData entityData);
public static readonly Dictionary<string, EntityLoader> EntityLoaders = new Dictionary<string, EntityLoader>();

Expand Down Expand Up @@ -94,6 +96,10 @@ public static void RegisterLoadOverride(Level level, LoadOverride loadOverride)
[PatchLevelUpdate] // ... except for manually manipulating the method via MonoModRules
public extern new void Update();

[MonoModIgnore] // We don't want to change anything about the method...
[PatchLevelEnforceBounds] // ... except for manually manipulating the method via MonoModRules
public extern new void EnforceBounds(Player player);

[MonoModReplace]
public new void RegisterAreaComplete() {
bool completed = Completed;
Expand Down Expand Up @@ -181,6 +187,7 @@ void Unpause() {

public extern void orig_TransitionTo(LevelData next, Vector2 direction);
public new void TransitionTo(LevelData next, Vector2 direction) {
TransitionDirection = direction;
orig_TransitionTo(next, direction);
Everest.Events.Level.TransitionTo(this, next, direction);
}
Expand Down Expand Up @@ -230,6 +237,7 @@ private IEnumerator TransitionRoutine(LevelData next, Vector2 direction) {

yield return orig.Current;
}
TransitionDirection = new();
}

[PatchLevelLoader] // Manually manipulate the method via MonoModRules
Expand Down Expand Up @@ -602,6 +610,14 @@ private bool CheckForErrors() {

return errorPresent;
}

private static void BlockUpTransitionsWithoutHoldable(TheoCrystal entity, Player player, Rectangle bounds) {
// we stay consistent with base game, so we let the player transition with any holdable item and not just TheoCrystal
if (entity != null && player.Top < (bounds.Top + 1) && (!player.Holding?.IsHeld ?? true)) {
player.Top = bounds.Top + 1;
player.OnBoundsV();
}
}
}

public static class LevelExt {
Expand Down Expand Up @@ -655,6 +671,12 @@ class PatchTransitionRoutineAttribute : Attribute { }
[MonoModCustomMethodAttribute(nameof(MonoModRules.PatchLevelCanPause))]
class PatchLevelCanPauseAttribute : Attribute { }

/// <summary>
/// A patch for the EnforceBounds method that checks if the player can transition up with Theo in the room.
/// </summary>
[MonoModCustomMethodAttribute(nameof(MonoModRules.PatchLevelEnforceBounds))]
class PatchLevelEnforceBoundsAttribute : Attribute { }

static partial class MonoModRules {

public static void PatchLevelLoader(ILContext context, CustomAttribute attrib) {
Expand Down Expand Up @@ -873,5 +895,22 @@ public static void PatchLevelCanPause(ILContext il, CustomAttribute attrib) {
c.Emit(OpCodes.Ldc_I4_0);
}


public static void PatchLevelEnforceBounds(MethodDefinition method, CustomAttribute attrib) {

MethodDefinition m_BlockUpTransitionsWithoutHoldable = method.DeclaringType.FindMethod("BlockUpTransitionsWithoutHoldable");

new ILContext(method).Invoke(il => {
ILCursor cursor = new(il);

cursor.GotoNext(MoveType.After,
instr => instr.MatchCallvirt("Monocle.Tracker", "GetEntity"),
instr => instr.MatchStloc(2));
cursor.Emit(OpCodes.Ldloc_2);
cursor.Emit(OpCodes.Ldarg_1);
cursor.Emit(OpCodes.Ldloc_0);
cursor.Emit(OpCodes.Call, m_BlockUpTransitionsWithoutHoldable);
});
}
}
}
44 changes: 44 additions & 0 deletions Celeste.Mod.mm/Patches/LevelLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,50 @@ public void ctor(Session session, Vector2? startPosition = default) {
SpriteData valueMod = kvpBank.Value;

if (bankOrig.SpriteData.TryGetValue(key, out SpriteData valueOrig)) {
// in order to allow map metadata Sprites.xml to override sprite origin and position, we
// need to manually copy the property from the map metadata sprites onto the main spritebank
// (done only if the overriding Sprites.xml specifies a value for that property)

bool foundOrigin = false; // Center, Justify, Origin are all ways to specify the origin
bool foundPosition = false;

// iterate through the sources list, starting from the end (the most recently added source
// setting a particular type of property is used)
for (int i = valueMod.Sources.Count - 1; i >= 0; i--) {
XmlElement xml = valueMod.Sources[i].XML;

if (xml != null) {
// based on SpriteData.Add()
if (!foundOrigin) {
if (xml.HasChild("Center")) {
valueOrig.Sprite.CenterOrigin();
valueOrig.Sprite.Justify = new Vector2(0.5f, 0.5f);
foundOrigin = true;
}
else if (xml.HasChild("Justify")) {
valueOrig.Sprite.JustifyOrigin(xml.ChildPosition("Justify"));
valueOrig.Sprite.Justify = xml.ChildPosition("Justify");
foundOrigin = true;
}
else if (xml.HasChild("Origin")) {
valueOrig.Sprite.Origin = xml.ChildPosition("Origin");
foundOrigin = true;
}
}

if (!foundPosition) {
if (xml.HasChild("Position")) {
valueOrig.Sprite.Position = xml.ChildPosition("Position");
foundPosition = true;
}
}

if (foundOrigin && foundPosition) {
break;
}
}
}

IDictionary animsOrig = ((patch_Sprite) valueOrig.Sprite).Animations;
IDictionary animsMod = ((patch_Sprite) valueMod.Sprite).Animations;
foreach (DictionaryEntry kvpAnim in animsMod) {
Expand Down
15 changes: 14 additions & 1 deletion Celeste.Mod.mm/Patches/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class patch_Player : Player {
private bool wasDashB;
private HashSet<Trigger> triggersInside;
private List<Entity> temp;
private readonly Hitbox normalHitbox;

private static int diedInGBJ = 0;
private int framesAlive;
Expand Down Expand Up @@ -285,6 +286,18 @@ public override void SceneEnd(Scene scene) {
[PatchPlayerApproachMaxMove]
private extern int NormalUpdate();

public extern bool orig_get_CanUnDuck();

public bool get_CanUnDuck() {
bool origCanUnDuck = orig_get_CanUnDuck();
bool theoBlockingUpTransition = false;

if (origCanUnDuck && level.Tracker.GetEntity<TheoCrystal>() != null && (!Holding?.IsHeld ?? true)) {
theoBlockingUpTransition = normalHitbox.Top + Position.Y < level.Bounds.Top + 1;
}

return origCanUnDuck && !theoBlockingUpTransition;
}
}

public static class PlayerExt {
Expand Down Expand Up @@ -347,7 +360,7 @@ class PatchPlayerOrigWallJumpAttribute : Attribute { }
/// </summary>
[MonoModCustomMethodAttribute(nameof(MonoModRules.PatchPlayerCtor))]
class PatchPlayerCtorAttribute : Attribute { }

/// <summary>
/// Patches the method to fix puffer boosts breaking on respawn.
/// </summary>
Expand Down
63 changes: 61 additions & 2 deletions Celeste.Mod.mm/Patches/TheoCrystal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,21 @@
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value

using Microsoft.Xna.Framework;
using Mono.Cecil;
using Mono.Cecil.Cil;
using MonoMod;
using MonoMod.Cil;
using MonoMod.Utils;
using System;

namespace Celeste {
class patch_TheoCrystal : TheoCrystal {
public patch_Holdable Hold; // avoids extra cast

public patch_TheoCrystal(EntityData data, Vector2 offset)
// we need to expose that vanilla private field to our patch class.
private patch_Level Level;

public patch_TheoCrystal(EntityData data, Vector2 offset)
: base(data, offset) {
}

Expand All @@ -20,5 +28,56 @@ public void ctor(Vector2 position) {
orig_ctor(position);
Hold.SpeedSetter = (speed) => { Speed = speed; };
}

[MonoModIgnore]
[PatchTheoCrystalUpdate]
public extern new void Update();

private static bool IsPlayerHoldingItemAndTransitioningUp(TheoCrystal theoCrystal) {
bool isPlayerHoldingItem = false;
bool isUpTransition = false;

patch_Level level = ((patch_TheoCrystal) theoCrystal).Level;
if (level.Tracker.GetEntity<Player>() is Player player) {
isPlayerHoldingItem = player.Holding?.IsHeld ?? false;
isUpTransition = level.Transitioning && level.TransitionDirection == -Vector2.UnitY;
}

return isPlayerHoldingItem && isUpTransition;
}
}
}

namespace MonoMod {

/// <summary>
/// A patch for the Update method that keeps the player alive on up transition and item held
/// </summary>
[MonoModCustomMethodAttribute(nameof(MonoModRules.PatchTheoCrystalUpdate))]
class PatchTheoCrystalUpdateAttribute : Attribute { }

static partial class MonoModRules {
public static void PatchTheoCrystalUpdate(MethodDefinition method, CustomAttribute attrib) {

MethodDefinition m_IsPlayerHoldingItemAndTransitioningUp = method.DeclaringType.FindMethod("IsPlayerHoldingItemAndTransitioningUp");
ILLabel afterDieLabel = null;

new ILContext(method).Invoke(il => {
ILCursor curser = new(il);
curser.GotoNext(MoveType.After,
instr => instr.MatchCall("Microsoft.Xna.Framework.Rectangle", "get_Bottom"),
instr => instr.MatchConvR4(),
instr => instr.MatchBleUn(out afterDieLabel),
instr => instr.MatchLdarg(0),
instr => instr.MatchCallvirt("Celeste.TheoCrystal", "Die"));

curser.Index -= 2;

curser.Emit(OpCodes.Ldarg_0);
curser.Emit(OpCodes.Call, m_IsPlayerHoldingItemAndTransitioningUp);
curser.Emit(OpCodes.Brtrue, afterDieLabel);
});
}
}
}
}

0 comments on commit 8f96c2c

Please sign in to comment.