Skip to content

Commit

Permalink
v1.9.15 release
Browse files Browse the repository at this point in the history
  • Loading branch information
LozenChen committed Aug 5, 2024
1 parent 42987f2 commit aab693b
Show file tree
Hide file tree
Showing 14 changed files with 234 additions and 5 deletions.
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.
12 changes: 9 additions & 3 deletions Source/Gameplay/ActualPosition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ internal static class ActualPosition {
internal static Vector2 CameraTowards = Vector2.Zero;
internal static Vector2 PlayerPosition = Vector2.Zero;
// Player's position when Hazards update
internal static Vector2 PlayerPositionBeforeSelfUpdate = Vector2.Zero;
internal static Vector2 PreviousPlayerPosition = Vector2.Zero;
internal static Vector2 PlayerPositionBeforeCameraUpdate = Vector2.Zero;
internal static int PlayerPositionChangedCount = 0;
Expand Down Expand Up @@ -144,6 +145,8 @@ private static void PatchAfterUpdate(Scene self) {

private static void PlayerPositionBeforeCameraUpdateIL(ILContext il) {
ILCursor cursor = new ILCursor(il);
cursor.Emit(OpCodes.Ldarg_0);
cursor.EmitDelegate(GetPositionBeforeSelfUpdate);
if (cursor.TryGotoNext(MoveType.After,
ins => ins.OpCode == OpCodes.Stfld && ins.Operand.ToString() == "System.Boolean Celeste.Player::StrawberriesBlocked"
// stfld bool Celeste.Player::StrawberriesBlocked
Expand All @@ -153,10 +156,13 @@ private static void PlayerPositionBeforeCameraUpdateIL(ILContext il) {
}
}


private static void GetPositionBeforeSelfUpdate(Player player) {
PlayerPositionBeforeSelfUpdate = player.Position;
}

private static void GetCameraPosition(Player player) {
if (TasHelperSettings.Enabled) {
PlayerPositionBeforeCameraUpdate = player.Position;
}
PlayerPositionBeforeCameraUpdate = player.Position;
}

//private static void PatchCrysSpinnerUpdate(On.Celeste.CrystalStaticSpinner.orig_Update orig, CrystalStaticSpinner self) {
Expand Down
2 changes: 1 addition & 1 deletion Source/Gameplay/AutoWatchEntity/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace Celeste.Mod.TASHelper.Gameplay.AutoWatchEntity;

internal static class Config {
public static bool MainEnabled = true;
public static bool MainEnabled = false;

public static Mode SwapBlock = Mode.WhenWatched;

Expand Down
2 changes: 1 addition & 1 deletion Source/Gameplay/AutoWatchEntity/CoreLogic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ private static void Initialize() {
typeof(InfoWatchEntity).GetMethodInfo("ClearWatchEntities").HookAfter(OnRemoveAllWatchEntity);
}


public static bool IsWatched(Entity entity) {
return InfoWatchEntity.WatchingEntities.Contains(entity) || (entity.GetEntityData() is EntityData entityData && InfoWatchEntity.RequireWatchUniqueEntityIds.Contains(new UniqueEntityId(entity, entityData)));
}
Expand Down Expand Up @@ -61,7 +62,6 @@ public static void AddRenderersToLevel(Level level) {
}
}


[LoadLevel]
public static void OnLoadLevel(Level level) {
if (Config.MainEnabled) {
Expand Down
82 changes: 82 additions & 0 deletions Source/Gameplay/HKNailDebugRender.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using Celeste.Mod.TASHelper.Utils;
using Microsoft.Xna.Framework;
using Monocle;
using System.Reflection;

namespace Celeste.Mod.TASHelper.Gameplay;
internal static class HKNailDebugRender {

[Initialize]
private static void Initialize() {
prepared = true;
if (ModUtils.GetType("FlaglinesAndSuch", "FlaglinesAndSuch.HKnail") is { } hkNail) {
nailTimerGetter = hkNail.GetFieldInfo("nailTimer");
rechargeTimerGetter = hkNail.GetFieldInfo("nailRechargeTimer");
nailDir = hkNail.GetFieldInfo("nailDir");
if (nailTimerGetter is null || rechargeTimerGetter is null || nailDir is null) {
prepared = false;
}
}
else {
prepared = false;
}
if (prepared && ModUtils.GetType("FlaglinesAndSuch", "FlaglinesAndSuch.Class1") is { } moduleType && ModUtils.GetType("FlaglinesAndSuch", "FlaglinesAndSuch.FlaglinesAndSuchModuleSettings") is { } settingType && settingType.GetPropertyInfo("PlayerAlwaysHasNail") is { } alwaysHasNail && moduleType.GetPropertyValue<EverestModuleSettings>("Settings") is { } settings) {
flaglinesAndSuchSettings = settings;
alwaysHasNailGetter = alwaysHasNail;
}
else {
prepared = false;
}
}

private static FieldInfo nailTimerGetter;

private static FieldInfo rechargeTimerGetter;

private static FieldInfo nailDir;

private static EverestModuleSettings flaglinesAndSuchSettings;

private static PropertyInfo alwaysHasNailGetter;

private static bool PlayerAlwaysHasNail => (bool)alwaysHasNailGetter.GetValue(flaglinesAndSuchSettings);

private static bool prepared = false;

[AddDebugRender]
private static void PatchEntityListDebugRender(EntityList self, Camera camera) {
if (!prepared || Engine.Scene is not Level level) {
return;
}
if (!PlayerAlwaysHasNail && !level.Session.GetFlag("flaglinesandsuch_nail_enabled")) {
return;
}
if (playerInstance is not Player player) {
return;
}
if ((float)nailTimerGetter.GetValue(null) > 0f || (float)rechargeTimerGetter.GetValue(null) == 0.1f) {
Vector2 orig_Position = player.Position;
Collider collider = player.Collider;
int dir = (int)nailDir.GetValue(null);
player.Position = ActualPosition.PlayerPositionBeforeSelfUpdate;
player.Collider = dir switch {
0 => nailhitboxUp,
1 => nailhitboxDown,
2 => nailhitboxLeft,
3 => nailhitboxRight,
_ => nailhitboxRight
};
player.Collider.Render(camera);
player.Collider = collider;
player.Position = orig_Position;
}
}

private static readonly Hitbox nailhitboxDown = new Hitbox(16f, 14f, -8f);

private static readonly Hitbox nailhitboxUp = new Hitbox(16f, 14f, -8f, -25f);

private static readonly Hitbox nailhitboxRight = new Hitbox(14f, 16f, 4f, -12f);

private static readonly Hitbox nailhitboxLeft = new Hitbox(14f, 16f, -18f, -12f);
}
46 changes: 46 additions & 0 deletions Source/Gameplay/Spinner/SimplifiedSpinner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ internal static class SimplifiedSpinner {

private static List<FieldInfo> VivSpinnerExtraComponentGetter;

private static List<FieldInfo> XaphanSpinnerExtraComponentGetter;

private static List<FieldInfo> ChroniaSpinnerExtraComponentGetter;

private static List<FieldInfo> ChronoSpinnerExtraComponentGetter;

private static bool wasSpritesCleared = !SpritesCleared;
Expand Down Expand Up @@ -80,6 +84,48 @@ public static void Initialize() {
OnCreateSprites(vivAnimSpinnerType);
}

if (ModUtils.GetType("XaphanHelper", "Celeste.Mod.XaphanHelper.Entities.CustomSpinner") is { } xaphanSpinnerType) {
XaphanSpinnerExtraComponentGetter = new() {
xaphanSpinnerType.GetField("border", BindingFlags.NonPublic | BindingFlags.Instance),
xaphanSpinnerType.GetField("filler", BindingFlags.NonPublic | BindingFlags.Instance)
};
LevelExtensions.AddToTracker(xaphanSpinnerType);
ClearSpritesAction.Add(self => {
if (!self.Tracker.Entities.ContainsKey(xaphanSpinnerType)) {
self.Tracker.Entities.Add(xaphanSpinnerType, new List<Entity>());
}
foreach (Entity spinner in self.Tracker.Entities[xaphanSpinnerType]) {
spinner.UpdateComponentVisiblity();
foreach (FieldInfo getter in XaphanSpinnerExtraComponentGetter) {
object obj = getter.GetValue(spinner);
if (obj != null) {
obj.SetFieldValue("Visible", !SpritesCleared);
}
}
}
});
OnCreateSprites(xaphanSpinnerType);
}

if (ModUtils.GetType("ChroniaHelper", "ChroniaHelper.Entities.SeamlessSpinner") is { } chroniaSpinnerType) {
ChroniaSpinnerExtraComponentGetter = new() {
chroniaSpinnerType.GetField("border", BindingFlags.NonPublic | BindingFlags.Instance),
chroniaSpinnerType.GetField("filler", BindingFlags.NonPublic | BindingFlags.Instance)
};
ClearSpritesAction.Add(self => {
foreach (Entity spinner in self.Tracker.Entities[chroniaSpinnerType]) {
spinner.UpdateComponentVisiblity();
foreach (FieldInfo getter in ChroniaSpinnerExtraComponentGetter) {
object obj = getter.GetValue(spinner);
if (obj != null) {
obj.SetFieldValue("Visible", !SpritesCleared);
}
}
}
});
OnCreateSprites(chroniaSpinnerType);
}

if (ModUtils.GetType("ChronoHelper", "Celeste.Mod.ChronoHelper.Entities.ShatterSpinner") is { } chronoSpinnerType) {
ChronoSpinnerExtraComponentGetter = new() {
chronoSpinnerType.GetField("border", BindingFlags.NonPublic | BindingFlags.Instance),
Expand Down
57 changes: 57 additions & 0 deletions Source/Gameplay/Spinner/SpinnerCalculateHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,15 @@ public static void Initialize() {
});
}

if (ModUtils.GetType("XaphanHelper", "Celeste.Mod.XaphanHelper.Entities.CustomSpinner") is { } xaphanSpinnerType) {
DictionaryAdderNormal(xaphanSpinnerType, "offset", spinner);
}

if (ModUtils.GetType("ChroniaHelper", "ChroniaHelper.Entities.SeamlessSpinner") is { } chroniaSpinnerType) {
ChroniaSpinnerType = chroniaSpinnerType;
DictionaryAdderNormal(chroniaSpinnerType, "offset", spinner);
}

if (ModUtils.GetType("BrokemiaHelper", "BrokemiaHelper.CassetteSpinner") is { } cassetteSpinnerType) {
HazardTypesTreatNormal.Add(cassetteSpinnerType, spinner);
OffsetGetters.Add(cassetteSpinnerType, OffsetGetters[vanillaCrysSpinnerType]);
Expand Down Expand Up @@ -229,6 +238,8 @@ public static void Initialize() {

private static Type VivSpinnerType;

private static Type ChroniaSpinnerType;

public static bool NoCycle(Entity self) {
if (NoCycleTypes.TryGetValue(self.GetType(), out Func<Entity, bool> func)) {
return func(self);
Expand All @@ -240,6 +251,10 @@ public static bool IsVivSpinner(Entity self) {
return self.GetType().IsSameOrSubclassOf(VivSpinnerType);
}

public static bool IsChroniaSpinner(Entity self) {
return self.GetType().IsSameOrSubclassOf(ChroniaSpinnerType);
}

public static bool GetCollidable(Entity self) {
if (LightningCollidable.TryGetValue(self.GetType(), out Func<Entity, bool> func)) {
return func(self);
Expand Down Expand Up @@ -432,4 +447,46 @@ public static int CalculateSpinnerGroup(float offset) {
}
return 3;
}

public static string[] GetChroniaHitboxString(Entity spinner) {
if (spinner.Collider is ColliderList list) {
switch (list.colliders.Count()) {
case 1: {
Collider c = list.colliders.First();
if (IsCircle(c, out float radius)) {
if (radius == 6f) {
return new string[] { "C:6;0,0" };
}
else if (radius == 8f) {
return new string[] { "C:8;0,0" };
}
}
else if (c is Hitbox hb && hb.width == 16f && hb.height == 16f && hb.Position.X == -8f && hb.Position.Y == -8f) {
return new string[] { "R:16,16;-8,-8" };
}
break;
}
case 2: {
if (IsCircle(list.colliders.First(), out float radius) && radius == 6f && list.colliders[1] is Hitbox hb && hb.width == 16f && hb.height == 4f && hb.Position.X == -8f && hb.Position.Y == -3f) {
return new string[] { "C:6;0,0", "R:16,4;-8,-3" };
}
break;
}
default: {
break;
}
}
}
return new string[] { "UNEXPECTED" };

}

private static bool IsCircle(Collider collider, out float radius) {
if (collider is Circle circle) {
radius = circle.Radius;
return true;
}
radius = 0f;
return false;
}
}
1 change: 1 addition & 0 deletions Source/Gameplay/Spinner/SpinnerColliderHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public static void Initialize() {
Add("C:6;0,0", 1f, new SpinnerColliderValue("C600"));
Add("C:6;0,0|R:16,4;-8,-1", 1f, new SpinnerColliderValue("reverted"));
Add("C:8;0,0", 1f, new SpinnerColliderValue("C800"));
Add("R:16,16;-8,-8", 1f, new SpinnerColliderValue("S16"));

void Add(string hitboxS, float scale, SpinnerColliderValue value) {
string[] hitboxString = hitboxS.Split('|');
Expand Down
30 changes: 30 additions & 0 deletions Source/Gameplay/Spinner/SpinnerRenderHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ public static void Initialize() {
cursor.Emit(OpCodes.Ret);
});
}

if (ModUtils.ChroniaHelperInstalled) {
typeof(SpinnerRenderHelper).GetMethod(nameof(DrawSpinnerCollider)).IlHook((cursor, _) => {
Instruction skipChronia = cursor.Next;
cursor.Emit(OpCodes.Ldarg_0);
cursor.Emit(OpCodes.Ldarg_1);
cursor.Emit(OpCodes.Ldarg_2);
cursor.Emit(OpCodes.Ldarg_3);
cursor.EmitDelegate(DrawChroniaCollider);
cursor.Emit(OpCodes.Brfalse, skipChronia);
cursor.Emit(OpCodes.Ret);
});
}
}

internal static Color DefaultColor => TasSettings.EntityHitboxColor;
Expand Down Expand Up @@ -149,6 +162,23 @@ public static bool DrawVivCollider(Entity self, Camera camera, Color color, bool
}
return false;
}

public static bool DrawChroniaCollider(Entity self, Camera camera, Color color, bool collidable) {
if (SpinnerCalculateHelper.IsChroniaSpinner(self)) {
if (OnGrid(self)) {
#pragma warning disable CS8600, CS8604
string[] hitboxString = SpinnerCalculateHelper.GetChroniaHitboxString(self);
if (SpinnerColliderHelper.TryGetValue(hitboxString, 1f, out SpinnerColliderHelper.SpinnerColliderValue value)) {
value.DrawOutlineAndInside(self.Position, color, collidable);
return true;
}
#pragma warning restore CS8600, CS8604
}
DrawComplexSpinnerCollider(self, camera, color, collidable);
return true;
}
return false;
}
public static bool OnGrid(Entity self) {
return self.Position.X == Math.Floor(self.Position.X) && self.Position.Y == Math.Floor(self.Position.Y);
}
Expand Down
1 change: 1 addition & 0 deletions Source/Module/WhatsNew.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public static void CreateUpdateLog() {
AddLog("1.9.12", "Bugfix: InView Range does not work properly if you are doing a bino control storage. Now InView Range will handle OoO issues properly. (Thanks @atpx8)");
AddLog("1.9.13", "Bugfix: Game crashes in wavedash.ppt. (Thanks @trans_alexa)");
AddLog("1.9.14", "Feature: Make SpeedrunTimer become transparent when TAS pauses. Default is ON. Edit it in \"More Options\".", "Divide \"More Options\" into three pages.", "Feature: Allow PageUp/PageDown in subMenus.");
AddLog("1.9.15", "Feature: Show the hitbox of Hollow Knight Nail from FlaglinesAndSuch.", "Feature: Spinner related features now support ChroniaHelper's SeamlessSpinner.");
UpdateLogs.Sort((x, y) => new Version(y.Item1).CompareTo(new Version(x.Item1)));
}

Expand Down
6 changes: 6 additions & 0 deletions Source/Utils/ModUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ public static Assembly GetAssembly(string modName) {
public static bool SpeedrunToolInstalled = false;

public static bool GravityHelperInstalled = false;

public static bool ChroniaHelperInstalled = false;

public static bool FlaglinesAndSuchInstalled = false;
public static bool UpsideDown => ExtendedVariantsUtils.UpsideDown;
public static void InitializeAtFirst() {
FrostHelperInstalled = IsInstalled("FrostHelper");
Expand All @@ -65,6 +69,8 @@ public static void InitializeAtFirst() {
IsaGrabBagInstalled = IsInstalled("IsaGrabBag");
SpeedrunToolInstalled = IsInstalled("SpeedrunTool");
GravityHelperInstalled = IsInstalled("GravityHelper");
ChroniaHelperInstalled = IsInstalled("ChroniaHelper");
FlaglinesAndSuchInstalled = IsInstalled("FlaglinesAndSuch");
// we actually also assume they are in enough late version
// so all entities mentioned in corresponding hooks do exist
}
Expand Down

0 comments on commit aab693b

Please sign in to comment.