Skip to content

Commit beed768

Browse files
committed
fixup cassette aero blocks and clean up hooks
1 parent cc88bae commit beed768

File tree

2 files changed

+74
-128
lines changed

2 files changed

+74
-128
lines changed

Loenn/entities/aero/aero_block_charged.lua

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ local cassetteIndexOptions = {
1010
["2 - Bright Sun"] = 2,
1111
["3 - Malachite"] = 3
1212
}
13-
local colors = communalHelper.cassetteBlockHexColors
13+
local cassetteIndexColors = communalHelper.cassetteBlockHexColors
1414

1515
local aeroBlockCharged = {}
1616

@@ -55,20 +55,22 @@ aeroBlockCharged.placements = {
5555
}
5656
}
5757
}
58-
for i = 1, 4 do
58+
for i = 0, 3 do
5959
table.insert(aeroBlockCharged.placements, {
60-
name = string.format("aero_block_charged_%s", i - 1),
60+
name = string.format("aero_block_charged_%s", i),
6161
data = {
6262
width = 16,
6363
height = 16,
6464
buttonSequence = "left -> right+top",
6565
hover = true,
6666
loop = false,
67-
activeColor = colors[i],
67+
activeColor = cassetteIndexColors[i + 1],
6868
inactiveColor = "FF6347",
6969
easing = "CubeIn",
70-
SpirialisBug = false,
71-
cassetteIndex = i - 1,
70+
-- SpirialisBug = false,
71+
SpirialisBugV2 = false,
72+
wallbounceLeniency = true,
73+
cassetteIndex = i,
7274
moveOnCassetteTick = true,
7375
}
7476
})
@@ -96,10 +98,8 @@ function aeroBlockCharged.sprite(room, entity)
9698
table.insert(sprites, rectangle:getDrawableSprite())
9799

98100
if entity.cassetteIndex and entity.cassetteIndex ~= -1 then
99-
local cassetteIndicatorColor = entity.activeColor ~= "" and communalHelper.hexToColor(entity.activeColor) or
100-
communalHelper.cassetteBlockColors[entity.cassetteIndex + 1]
101-
local cassetteIndicator = drawableRectangle.fromRectangle("fill", x + 4, y + 4, width - 8, height - 8,
102-
cassetteIndicatorColor)
101+
local cassetteIndicatorColor = entity.activeColor ~= "" and communalHelper.hexToColor(entity.activeColor) or cassetteIndexColors[entity.cassetteIndex + 1]
102+
local cassetteIndicator = drawableRectangle.fromRectangle("fill", x + 4, y + 4, width - 8, height - 8, cassetteIndicatorColor)
103103
table.insert(sprites, cassetteIndicator:getDrawableSprite())
104104
end
105105

src/Entities/Aero/AeroBlockCharged.cs

Lines changed: 64 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using MonoMod.RuntimeDetour;
22
using System.Linq;
3-
using System.Reflection;
43

54
namespace Celeste.Mod.CommunalHelper.Entities;
65

@@ -42,11 +41,12 @@ public bool Visible
4241
if (visible != value)
4342
for (int i = 0; i < buttonImages.Length; i++)
4443
buttonImages[i].Visible = buttonOutlineImages[i].Visible = value;
44+
4545
visible = value;
4646
}
4747
}
4848

49-
private bool preventVisualUpdate = false;
49+
private bool preventVisualUpdate;
5050

5151
private bool pressed;
5252
public bool Pressed
@@ -63,6 +63,7 @@ private set
6363
buttonOutlineImages[i].Position = imagePositions[i] + offset;
6464
}
6565
}
66+
6667
pressed = value;
6768
}
6869
}
@@ -122,8 +123,8 @@ public void Update(AeroBlockCharged self, bool preventVisualUpdate)
122123
: Calc.Approach(lerp, 0.0f, Engine.DeltaTime * 4f);
123124

124125
Color color = Color.Lerp(Color.White, self.alive ? self.activeColor : self.inactiveColor, lerp);
125-
for (int i = 0; i < buttonImages.Length; i++)
126-
buttonImages[i].Color = color;
126+
foreach (Image image in buttonImages)
127+
image.Color = color;
127128
}
128129

129130
public void ChangeProperties(int pressOffset, Color color)
@@ -154,14 +155,21 @@ public static Button TopButton(AeroBlockCharged entity, bool visible)
154155
private readonly bool loop;
155156

156157
private readonly int cassetteIndex = -1;
157-
private CassetteListener listener;
158+
private readonly CassetteListener listener;
158159
private readonly bool moveOnCassetteTick;
159160

160161
private bool alive = true;
161162
private readonly bool SpirialisBug = false;
162163
private readonly bool wallbounceLeniency;
163164

164165
private Button leftButton, rightButton, topButton;
166+
private Button ButtonFromDir(int dir) => dir switch
167+
{
168+
-1 => leftButton,
169+
0 => topButton,
170+
1 => rightButton,
171+
_ => null
172+
};
165173

166174
private bool buttonSfxOn = false;
167175
private float buttonSfxLerp;
@@ -175,8 +183,7 @@ public static Button TopButton(AeroBlockCharged entity, bool visible)
175183

176184
private readonly Color activeColor, inactiveColor;
177185

178-
private bool activatedThisTick = false;
179-
private bool activatedAlready = false;
186+
private bool activatedPreviously, activatedThisTick;
180187

181188
public AeroBlockCharged(EntityData data, Vector2 offset)
182189
: this(data.NodesWithPosition(offset), data.Width, data.Height, data.Bool("loop"), data.HexColor("activeColor", defaultOnColor), data.HexColor("inactiveColor", defaultEndColor), data.Bool("hover", true), data.Attr("buttonSequence", DEFAULT_BUTTON_SEQUENCE), data.Bool("wallbounceLeniency", false), data.Int("cassetteIndex", -1), data.Bool("moveOnCassetteTick"))
@@ -247,7 +254,7 @@ private void OnActivated()
247254
private void OnDeactivated()
248255
{
249256
ChangeButtonProperties(2, inactiveColor, inactiveColor);
250-
if (moveOnCassetteTick && !activatedThisTick && activatedAlready && alive)
257+
if (moveOnCassetteTick && !activatedThisTick && activatedPreviously && alive)
251258
IncrementPosition();
252259
}
253260

@@ -330,6 +337,7 @@ private void EndSequence()
330337
Hold = 0.5f,
331338
FadeOut = 0.5f,
332339
});
340+
333341
Alarm.Set(this, 0.5f, () =>
334342
{
335343
blinker.Complete = true;
@@ -349,34 +357,35 @@ private void Smash(Player player, Vector2 speed)
349357

350358
player.Speed = speed;
351359
player.StateMachine.State = Player.StLaunch;
360+
352361
Celeste.Freeze(0.05f);
353362
Audio.Play(CustomSFX.game_aero_block_smash, Center, "magic", 1.0f);
354363
Input.Rumble(RumbleStrength.Strong, RumbleLength.Short);
355-
(Scene as Level).DirectionalShake(Vector2.UnitY);
364+
SceneAs<Level>().DirectionalShake(Vector2.UnitY);
356365
windLayer.MulitplyVelocities(-0.5f);
357366

358367
IncrementPosition();
359368
}
360369

361370
private void IncrementPosition()
362371
{
363-
activatedAlready = true;
372+
activatedPreviously = true;
364373
if (IsCassette)
365374
activatedThisTick = true;
366375

367376
positionIndex++;
368377
if (loop)
369378
positionIndex %= positions.Length;
379+
370380
combinationIndex = (combinationIndex + 1) % sequence.Length;
381+
371382
if (positionIndex < positions.Length)
372383
{
373384
Home = positions[positionIndex];
374385
ChangeCombination(sequence[combinationIndex]);
375386
}
376387
else
377-
{
378388
EndSequence();
379-
}
380389
}
381390

382391
public override void Added(Scene scene)
@@ -389,7 +398,7 @@ public override void Awake(Scene scene)
389398
{
390399
base.Awake(scene);
391400

392-
// update visuals if deactivated on start
401+
// update visuals if we start deactivated
393402
if (IsCassette && (!listener?.Activated ?? false))
394403
ForceButtonDeactivate();
395404
}
@@ -407,22 +416,15 @@ public override void Update()
407416
else
408417
ForceButtonDeactivate();
409418

410-
bool check = CheckAnyButton();
411-
if (check)
419+
if (CheckAnyButton() && !buttonSfxOn)
412420
{
413-
if (!buttonSfxOn)
414-
{
415-
Audio.Play(CustomSFX.game_aero_block_button_press, Center);
416-
buttonSfxOn = true;
417-
}
421+
Audio.Play(CustomSFX.game_aero_block_button_press, Center);
422+
buttonSfxOn = true;
418423
}
419-
else
424+
else if (buttonSfxOn)
420425
{
421-
if (buttonSfxOn)
422-
{
423-
Audio.Play(CustomSFX.game_aero_block_button_let_go, Center);
424-
buttonSfxOn = false;
425-
}
426+
Audio.Play(CustomSFX.game_aero_block_button_let_go, Center);
427+
buttonSfxOn = false;
426428
}
427429

428430
float chargeSoundRate = buttonSfxOn ? 2.0f : (alive ? 3.0f : 4.0f);
@@ -453,8 +455,8 @@ internal static void Load()
453455
On.Celeste.Player.Jump += Player_Jump;
454456
On.Celeste.Player.WallJump += Player_WallJump;
455457
On.Celeste.Player.ClimbJump += Player_ClimbJump;
456-
On.Celeste.Player.SuperWallJump += Player_SuperWallJump;
457458
On.Celeste.Player.SuperJump += Player_SuperJump;
459+
On.Celeste.Player.SuperWallJump += Player_SuperWallJump;
458460
}
459461
}
460462

@@ -463,111 +465,55 @@ internal static void Unload()
463465
On.Celeste.Player.Jump -= Player_Jump;
464466
On.Celeste.Player.WallJump -= Player_WallJump;
465467
On.Celeste.Player.ClimbJump -= Player_ClimbJump;
466-
On.Celeste.Player.SuperWallJump -= Player_SuperWallJump;
467468
On.Celeste.Player.SuperJump -= Player_SuperJump;
469+
On.Celeste.Player.SuperWallJump -= Player_SuperWallJump;
468470
}
469471

470472
private static void Player_Jump(On.Celeste.Player.orig_Jump orig, Player self, bool particles, bool playSfx)
471-
{
472-
if (!(self.Scene.Tracker.Entities.TryGetValue(typeof(AeroBlockCharged), out var q) && Collide.First(self, q, self.Position + Vector2.UnitY) is AeroBlockCharged block)) { orig(self, particles, playSfx); return; }
473-
if (block.SpirialisBug)
474-
{
475-
orig(self, particles, playSfx);
476-
if (self.OnGround() && block is not null && block.CheckTopButton())
477-
block.Smash(self, Vector2.UnitY * -350);
478-
self.varJumpSpeed = self.Speed.Y;
479-
}
480-
else
481-
{
482-
orig(self, particles, playSfx);
483-
484-
if (!self.OnGround())
485-
return;
486-
487-
// jump
488-
if (block is not null && block.CheckTopButton())
489-
block.Smash(self, Vector2.UnitY * -350);
490-
}
491-
}
492-
473+
=> SmashFirstTouchingAeroBlock(() => orig(self, particles, playSfx), self, Vector2.UnitY, block => self.OnGround() && block.CheckTopButton(), Vector2.UnitY * -350f);
493474
private static void Player_WallJump(On.Celeste.Player.orig_WallJump orig, Player self, int dir)
494-
{
495-
if (!(self.Scene.Tracker.Entities.TryGetValue(typeof(AeroBlockCharged), out var q) && Collide.First(self, q, self.Position - Vector2.UnitX * dir * 3) is AeroBlockCharged block)) { orig(self, dir); return; }
496-
if (block.SpirialisBug)
497-
{
498-
orig(self, dir);
499-
// wallbounce
500-
if (block is not null && (dir < 0 ? block.CheckLeftButton() : block.CheckRightButton()))
501-
block.Smash(self, new Vector2(300 * dir, -300));
502-
self.varJumpSpeed = self.Speed.Y;
503-
}
504-
else
505-
{
506-
orig(self, dir);
507-
508-
// walljump
509-
if (block is not null && (dir < 0 ? block.CheckLeftButton() : block.CheckRightButton()))
510-
block.Smash(self, new Vector2(dir * 300, -300));
511-
}
512-
}
513-
475+
=> SmashFirstTouchingAeroBlock(() => orig(self, dir), self, -Vector2.UnitX * dir * 3f, block => dir < 0 ? block.CheckLeftButton() : block.CheckRightButton(), new Vector2(dir * 300f, -300f));
514476
private static void Player_ClimbJump(On.Celeste.Player.orig_ClimbJump orig, Player self)
515-
{
516-
orig(self);
517-
518-
// climbjump
519-
if (!(self.Scene.Tracker.Entities.TryGetValue(typeof(AeroBlockCharged), out var q) && Collide.First(self, q, self.Position + Vector2.UnitX * (int) self.Facing * 3) is AeroBlockCharged block)) { return; }
520-
if (block is not null && (self.Facing == Facings.Right ? block.CheckLeftButton() : block.CheckRightButton()))
521-
{
522-
float speed = ((int) self.Facing == Math.Sign(Input.MoveX.Value)) ? 300 : -300;
523-
block.Smash(self, new Vector2((int) self.Facing * speed, -300));
524-
}
525-
}
526-
477+
=> SmashFirstTouchingAeroBlock(() => orig(self), self, Vector2.UnitX * (int) self.Facing * 3f, block => self.Facing == Facings.Right ? block.CheckLeftButton() : block.CheckRightButton(), new Vector2((int) self.Facing * (int) self.Facing == Math.Sign(Input.MoveX.Value) ? 300f : -300f, -300f));
478+
private static void Player_SuperJump(On.Celeste.Player.orig_SuperJump orig, Player self)
479+
=> SmashFirstTouchingAeroBlock(() => orig(self), self, Vector2.UnitY, block => self.OnGround() && block.CheckTopButton(), new Vector2(self.Speed.X * 1.2f, -350f));
527480
private static void Player_SuperWallJump(On.Celeste.Player.orig_SuperWallJump orig, Player self, int dir)
528-
{
529-
// check for blocks up to 5px away instead of 3px, since wallbounces have 2 more pixels of leniency than regular wall jumps
530-
if (!(self.Scene.Tracker.Entities.TryGetValue(typeof(AeroBlockCharged), out var q) && Collide.First(self, q, self.Position - Vector2.UnitX * dir * 5) is AeroBlockCharged block)) { orig(self, dir); return; }
531-
532-
orig(self, dir);
533-
534-
// wallbounce
535-
var button = dir < 0 ? block.leftButton : block.rightButton;
536-
// button.Pressed is only true if madeline is within 3 pixels of the aero block, but wallbounces have 5 pixels of leniency
537-
// this means that, if wallbounceLeniency is enabled, we want to check button.Visible as well to prevent there being a 2px window where it is possible to wallbounce but not recieve the boost
538-
// (the 5px window is already enforced by the line which gets the reference to the aero block, so this doesn't mean you can receive a boost from an aero block you didn't wallbounce on)
539-
if (button is not null && (button.Pressed || (block.wallbounceLeniency && button.Visible)))
481+
=> SmashFirstTouchingAeroBlock(() => orig(self, dir), self, -Vector2.UnitX * dir * 5f, block =>
482+
{
483+
Button button = block.ButtonFromDir(dir);
484+
// button.Pressed is only true if madeline is within 3 pixels of the aero block, but wallbounces have 5 pixels of leniency
485+
// this means that, if wallbounceLeniency is enabled, we want to check button.Visible as well to prevent there being a 2px window where it is possible to wallbounce but not receive the boost
486+
// (the 5px window is already enforced by the time we get the aero block, so this doesn't mean you can receive a boost from an aero block you didn't wallbounce on)
487+
return button is not null && (button.Pressed || (block.wallbounceLeniency && button.Visible));
488+
}, new Vector2(dir * 300f, -400f), block =>
540489
{
541-
block.Smash(self, new Vector2(300 * dir, -400));
542-
543490
// give the button a quick (visual) press if it isn't already pressed, due to wallbounce leniency
544-
if (!button.Pressed && block.wallbounceLeniency)
491+
Button button = block.ButtonFromDir(dir);
492+
if (button is not null && !button.Pressed && block.wallbounceLeniency)
545493
button.QuickForcePress();
546-
547-
if (block.SpirialisBug)
548-
self.varJumpSpeed = self.Speed.Y;
549-
}
550-
}
551-
552-
private static void Player_SuperJump(On.Celeste.Player.orig_SuperJump orig, Player self)
494+
});
495+
496+
private static void SmashFirstTouchingAeroBlock(Action callOrig, Player player, Vector2 checkOffset, Func<AeroBlockCharged, bool> smashCondition, Vector2 smashSpeed, Action<AeroBlockCharged> smashCallback = null)
553497
{
554-
if (!(self.Scene.Tracker.Entities.TryGetValue(typeof(AeroBlockCharged), out var q) && Collide.First(self, q, self.Position + Vector2.UnitY) is AeroBlockCharged block)) { orig(self); return; }
555-
if (block.SpirialisBug)
498+
if (player.Scene.Tracker
499+
.GetEntities<AeroBlockCharged>()
500+
.FirstOrDefault(b => player.CollideCheck(b, player.Position + checkOffset))
501+
is not AeroBlockCharged block)
556502
{
557-
orig(self);
558-
if (self.OnGround() && block is not null && block.CheckTopButton())
559-
block.Smash(self, new Vector2(self.Speed.X * 1.2f, -350));
560-
self.varJumpSpeed = self.Speed.Y;
503+
callOrig();
504+
return;
561505
}
562-
else
563-
{
564-
orig(self);
565-
566-
if (!self.OnGround())
567-
return;
506+
507+
callOrig();
568508

569-
if (block is not null && block.CheckTopButton())
570-
block.Smash(self, new Vector2(self.Speed.X * 1.2f, -350));
509+
if (smashCondition(block))
510+
{
511+
block.Smash(player, smashSpeed);
512+
513+
if (block.SpirialisBug)
514+
player.varJumpSpeed = player.Speed.Y;
515+
516+
smashCallback?.Invoke(block);
571517
}
572518
}
573519

0 commit comments

Comments
 (0)