Skip to content

Commit ff6efd2

Browse files
committed
Merge branch 'dev' into dream-tunnel-dash-state-check-overhaul
2 parents 70b6df0 + aa5f232 commit ff6efd2

File tree

8 files changed

+139
-13
lines changed

8 files changed

+139
-13
lines changed

GameBananaPage.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@
2121
<br>
2222
<pre>
2323
- Name: CommunalHelper
24-
Version: 1.22.0
24+
Version: 1.23.0
2525
</pre>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
local triggers = require("triggers")
2+
local depths = require("consts.object_depths")
3+
local drawableRectangle = require("structs.drawable_rectangle")
4+
local drawableText = require("structs.drawable_text")
5+
local colors = require("consts.colors")
6+
7+
local dreamTunnelBlocker = {}
8+
9+
dreamTunnelBlocker.name = "CommunalHelper/DreamTunnelBlocker"
10+
dreamTunnelBlocker.depth = depths.fakeWalls + 2000;
11+
12+
dreamTunnelBlocker.placements = {
13+
name = "normal",
14+
data = {
15+
width = 16,
16+
height = 16,
17+
blockDreamTunnelDashes = true,
18+
blockDreamDashes = false
19+
}
20+
}
21+
22+
-- adapted from triggers.getDrawable
23+
function dreamTunnelBlocker.sprite(room, entity)
24+
local displayName = triggers.triggerText(room, entity)
25+
26+
local x = entity.x or 0
27+
local y = entity.y or 0
28+
29+
local width = entity.width or 16
30+
local height = entity.height or 16
31+
32+
local borderedRectangle = drawableRectangle.fromRectangle("bordered", x, y, width, height, colors.triggerColor, colors.triggerBorderColor)
33+
local textDrawable = drawableText.fromText(displayName, x, y, width, height, nil, triggers.triggerFontSize, colors.triggerTextColor)
34+
35+
local drawables = borderedRectangle:getDrawableSprite()
36+
table.insert(drawables, textDrawable)
37+
38+
textDrawable.depth = dreamTunnelBlocker.depth - 1
39+
40+
return drawables
41+
end
42+
43+
return dreamTunnelBlocker

Loenn/entities/misc/glow_controller.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ glowController.placements = {
5555
bloomRadius = 8.0,
5656
bloomOffsetX = 0,
5757
bloomOffsetY = 0,
58+
deathAnimationIds = "death",
59+
respawnAnimationIds = "respawn",
5860
},
5961
},
6062
}

Loenn/lang/en_gb.lang

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,8 @@ entities.CommunalHelper/GlowController.attributes.description.bloomAlpha=The opa
821821
entities.CommunalHelper/GlowController.attributes.description.bloomRadius=The radius of the bloom in pixels. Defaults to 8.
822822
entities.CommunalHelper/GlowController.attributes.description.bloomOffsetX=The X offset from the entity's position at which to place the BloomPoint. Defaults to 0.
823823
entities.CommunalHelper/GlowController.attributes.description.bloomOffsetY=The Y offset from the entity's position at which to place the BloomPoint. Defaults to 0.
824+
entities.CommunalHelper/GlowController.attributes.description.deathAnimationIds=A list of animation IDs to treat as "death" animations, that is, when the targeted entities' Sprites start playing one of these animations, their light/bloom will fade out.
825+
entities.CommunalHelper/GlowController.attributes.description.respawnAnimationIds=A list of animation IDs to treat as "respawn" animations, that is, when the targeted entities' Sprites start playing one of these animations, their light/bloom will fade back in.
824826

825827
# Poison Gas
826828
entities.CommunalHelper/PoisonGas.placements.name.main=Poison Gas
@@ -979,6 +981,10 @@ entities.CommunalHelper/SJ/PelletEmitter.attributes.description.wiggleFrequency=
979981
entities.CommunalHelper/SJ/PelletEmitter.attributes.description.wiggleAmount=The amplitude of the wiggle. Defaults to 2. Set to 0 to disable wiggling, causing pellets to travel in a straight line.
980982
entities.CommunalHelper/SJ/PelletEmitter.attributes.description.wiggleHitbox=Whether or not the pellet hitbox should move following the wiggle.
981983

984+
entities.CommunalHelper/DreamTunnelBlocker.placements.name.normal=Dream Tunnel Blocker
985+
entities.CommunalHelper/DreamTunnelBlocker.attributes.description.blockDreamTunnelDashes=Whether to prevent dream tunneling when dashing into a solid covered by this entity.
986+
entities.CommunalHelper/DreamTunnelBlocker.attributes.description.blockDreamDashes=Whether to prevent dream dashing when dashing into a dream block covered by this entity.
987+
982988
# ---------------------- TRIGGERS --------------------------
983989

984990
# [Strawberry Jam] Show Hitbox Trigger

Release Log.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
--1.23.0 (13/05/2025)--
2+
* Added Dream Tunnel Blockers. @SnipUndercover
3+
* Added death/respawn animation IDs options to Glow Controllers. @aonkeeper4
4+
15
--1.22.0 (27/04/2025)--
26
* Implemented zoom-out support for PCTN-MRT rendering and Dream Sprite rendering. @bigkahuna443 & @earthwise01
37
* Added 'noHole' option to Loop Blocks. @Spo0o0ky

src/DashStates/DreamTunnelDash/DreamTunnelDash.cs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using Celeste.Mod.CommunalHelper.Components;
2+
using Celeste.Mod.CommunalHelper.Entities;
23
using Celeste.Mod.CommunalHelper.States;
34
using Mono.Cecil.Cil;
45
using MonoMod.Cil;
56
using MonoMod.RuntimeDetour;
67
using MonoMod.Utils;
8+
using System.Collections.Generic;
79
using System.Linq;
810
using System.Reflection;
911

@@ -50,6 +52,8 @@ public static bool NextDashFeather
5052
public static Color[] DreamTrailColors;
5153
public static int DreamTrailColorIndex = 0;
5254

55+
// Keep a List<Entity> around to not allocate a new one every CollideAll<T>() call
56+
private static List<Entity> dreamTunnelBlockers = new();
5357

5458
private static IDetour hook_Player_DashCoroutine;
5559
private static IDetour hook_Player_orig_Update;
@@ -348,7 +352,14 @@ private static void Player_OnCollideV(ILContext il) {
348352

349353
private static bool Player_DreamDashCheck(On.Celeste.Player.orig_DreamDashCheck orig, Player self, Vector2 dir)
350354
{
351-
return overrideDreamDashCheck ? (overrideDreamDashCheck = false) : orig(self, dir);
355+
if (overrideDreamDashCheck)
356+
return overrideDreamDashCheck = false;
357+
358+
// Don't enter StDreamDash if there's a blocker
359+
if (self.IsDreamDashBlocked(self.Position + dir))
360+
return false;
361+
362+
return orig(self, dir);
352363
}
353364

354365
// Fixes bug with dreamSfx soundsource not being stopped
@@ -698,6 +709,10 @@ private static bool DreamTunnelDashCheck(this Player player, Vector2 dir)
698709
if (player.Left + dir.X < bounds.Left || player.Right + dir.X > bounds.Right || player.Top + dir.Y < bounds.Top || player.Bottom + dir.Y > bounds.Bottom)
699710
return false;
700711

712+
// Check if we're colliding with a DreamTunnelBlocker
713+
if (player.IsDreamTunnelDashBlocked(player.Position + dir))
714+
return false;
715+
701716
Solid solid = null;
702717

703718
// Check for dream blocks first, then for solids
@@ -779,6 +794,37 @@ private static bool DreamTunnelDashCheck(this Player player, Vector2 dir)
779794
return false;
780795
}
781796

797+
private static (bool blockDreamTunnelDashes, bool blockDreamDashes) GetBlockerConfiguration(this Player player, Vector2 position)
798+
{
799+
bool blockDreamTunnelDashes = false;
800+
bool blockDreamDashes = false;
801+
802+
foreach (Entity e in player.CollideAll<DreamTunnelBlocker>(position, dreamTunnelBlockers))
803+
{
804+
if (e is DreamTunnelBlocker { BlockDreamTunnelDashes: true })
805+
blockDreamTunnelDashes = true;
806+
if (e is DreamTunnelBlocker { BlockDreamDashes: true })
807+
blockDreamDashes = true;
808+
809+
if (blockDreamTunnelDashes && blockDreamDashes)
810+
break;
811+
}
812+
813+
return (blockDreamTunnelDashes, blockDreamDashes);
814+
}
815+
816+
private static bool IsDreamTunnelDashBlocked(this Player player, Vector2 position)
817+
{
818+
return player.CollideAll<DreamTunnelBlocker>(position, dreamTunnelBlockers)
819+
.Any(e => e is DreamTunnelBlocker { BlockDreamTunnelDashes: true });
820+
}
821+
822+
private static bool IsDreamDashBlocked(this Player player, Vector2 position)
823+
{
824+
return player.CollideAll<DreamTunnelBlocker>(position, dreamTunnelBlockers)
825+
.Any(e => e is DreamTunnelBlocker { BlockDreamDashes: true });
826+
}
827+
782828
#endregion
783829

784830
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace Celeste.Mod.CommunalHelper.Entities;
2+
3+
[Tracked]
4+
[CustomEntity("CommunalHelper/DreamTunnelBlocker")]
5+
public class DreamTunnelBlocker : Entity
6+
{
7+
public bool BlockDreamTunnelDashes;
8+
public bool BlockDreamDashes;
9+
10+
/// Blocking behavior handled in <see cref="DashStates.DreamTunnelDash.DreamTunnelDashCheck" />
11+
/// and <see cref="DashStates.DreamTunnelDash.Player_DreamDashCheck"/>
12+
public DreamTunnelBlocker(EntityData data, Vector2 offset) : base(data.Position + offset)
13+
{
14+
Collider = new Hitbox(data.Width, data.Height);
15+
Depth = Depths.FakeWalls + 2000;
16+
17+
BlockDreamTunnelDashes = data.Bool("blockDreamTunnelDashes", true);
18+
BlockDreamDashes = data.Bool("blockDreamDashes", false);
19+
}
20+
}

src/Entities/Misc/GlowController.cs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ private static void IL_LightingRenderer_BeforeRender(ILContext il)
5555
private readonly float bloomRadius;
5656
private readonly Vector2 bloomOffset;
5757

58-
private const string DeathAnimationId = "death";
59-
private const string RespawnAnimationId = "respawn";
58+
private readonly string[] deathAnimationIds;
59+
private readonly string[] respawnAnimationIds;
6060

6161
public GlowController(EntityData data, Vector2 offset)
6262
: base(data.Position + offset)
@@ -74,6 +74,9 @@ public GlowController(EntityData data, Vector2 offset)
7474
bloomAlpha = data.Float("bloomAlpha", 1f);
7575
bloomRadius = data.Float("bloomRadius", 8f);
7676
bloomOffset = new Vector2(data.Int("bloomOffsetX"), data.Int("bloomOffsetY", -10));
77+
78+
deathAnimationIds = data.Attr("deathAnimationIds", "death").Split(',');
79+
respawnAnimationIds = data.Attr("respawnAnimationIds", "respawn").Split(',');
7780
}
7881

7982
public override void Awake(Scene scene)
@@ -108,9 +111,9 @@ public override void Awake(Scene scene)
108111
}
109112

110113
// some entities get a special coroutine that hides lights and blooms
111-
// if it's a glider or otherwise has a sprite with a "death" animation
114+
// if it's a glider or otherwise has a sprite with an animation id contained in `deathAnimationIds`
112115
if (requiresRemovalRoutine &&
113-
entity.Components.GetAll<Sprite>().FirstOrDefault(s => s.Has(DeathAnimationId)) is { } sprite)
116+
entity.Components.GetAll<Sprite>().FirstOrDefault(s => deathAnimationIds.Any(s.Has)) is { } sprite)
114117
{
115118
entity.Add(new Coroutine(DeathRemovalRoutine(entity, sprite)));
116119
}
@@ -132,15 +135,15 @@ void SetAlpha(float alpha)
132135
}
133136
}
134137

135-
if (!sprite.Animations.TryGetValue(DeathAnimationId, out var deathAnimation))
138+
if (sprite.Animations.FirstOrDefault(kvp => deathAnimationIds.Contains(kvp.Key)).Value is not {} deathAnimation)
136139
{
137140
yield break;
138141
}
139142

140143
while (entity.Scene != null)
141144
{
142145
// wait until the sprite plays the death animation
143-
while (entity.Scene != null && sprite.CurrentAnimationID != DeathAnimationId)
146+
while (entity.Scene != null && !deathAnimationIds.Contains(sprite.CurrentAnimationID))
144147
{
145148
yield return null;
146149
}
@@ -149,16 +152,17 @@ void SetAlpha(float alpha)
149152
var fadeTime = deathAnimation.Frames.Length * deathAnimation.Delay;
150153
var fadeRemaining = fadeTime;
151154

152-
while (entity.Scene != null && sprite.CurrentAnimationID == DeathAnimationId && fadeRemaining > 0)
155+
while (entity.Scene != null && deathAnimationIds.Contains(sprite.CurrentAnimationID) && fadeRemaining > 0)
153156
{
154157
fadeRemaining -= Engine.DeltaTime;
155158
SetAlpha(Math.Max(fadeRemaining / fadeTime, 0f));
156159
yield return null;
157160
}
161+
SetAlpha(0f);
158162

159-
// if it's a respawning jelly, wait until the sprite is playing the respawn animation
160-
if (!sprite.Animations.TryGetValue(RespawnAnimationId, out var respawnAnimation)) break;
161-
while (entity.Scene != null && sprite.CurrentAnimationID != RespawnAnimationId)
163+
// if the sprite has a respawn animation, wait until it's playing it
164+
if (sprite.Animations.FirstOrDefault(kvp => respawnAnimationIds.Contains(kvp.Key)).Value is not {} respawnAnimation) break;
165+
while (entity.Scene != null && !respawnAnimationIds.Contains(sprite.CurrentAnimationID))
162166
{
163167
yield return null;
164168
}
@@ -167,12 +171,13 @@ void SetAlpha(float alpha)
167171
fadeTime = respawnAnimation.Frames.Length * respawnAnimation.Delay;
168172
fadeRemaining = fadeTime;
169173

170-
while (entity.Scene != null && sprite.CurrentAnimationID == RespawnAnimationId && fadeRemaining > 0)
174+
while (entity.Scene != null && respawnAnimationIds.Contains(sprite.CurrentAnimationID) && fadeRemaining > 0)
171175
{
172176
fadeRemaining -= Engine.DeltaTime;
173177
SetAlpha(1f - Math.Max(fadeRemaining / fadeTime, 0f));
174178
yield return null;
175179
}
180+
SetAlpha(1f);
176181
}
177182
}
178183
}

0 commit comments

Comments
 (0)