Skip to content

Commit db2eabd

Browse files
authored
Added Non-Badeline Moving Blocks (#78)
Added Non-Badeline Moving Blocks
2 parents 501b2ff + 158e1d2 commit db2eabd

File tree

3 files changed

+282
-0
lines changed

3 files changed

+282
-0
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
module SpringCollab2020NonBadelineMovingBlock
2+
3+
using ..Ahorn, Maple
4+
5+
@pardef NonBadelineMovingBlock(x1::Integer, y1::Integer, x2::Integer=x1+16, y2::Integer=y1, width::Integer=Maple.defaultBlockWidth, height::Integer=Maple.defaultBlockHeight, tiletype::String="g", highlightTiletype::String="G") = Entity("SpringCollab2020/nonBadelineMovingBlock", x=x1, y=y1, nodes=Tuple{Int, Int}[(x2, y2)], width=width, height=height, tiletype=tiletype, highlightTiletype=highlightTiletype)
6+
7+
const placements = Ahorn.PlacementDict(
8+
"Non-Badeline Boss Moving Block (Spring Collab 2020)" => Ahorn.EntityPlacement(
9+
NonBadelineMovingBlock,
10+
"rectangle",
11+
Dict{String, Any}(),
12+
function(entity)
13+
entity.data["nodes"] = [(Int(entity.data["x"]) + Int(entity.data["width"]) + 8, Int(entity.data["y"]))]
14+
end
15+
)
16+
)
17+
18+
Ahorn.editingOptions(entity::NonBadelineMovingBlock) = Dict{String, Any}(
19+
"tiletype" => Ahorn.tiletypeEditingOptions(),
20+
"highlightTiletype" => Ahorn.tiletypeEditingOptions()
21+
)
22+
23+
Ahorn.nodeLimits(entity::NonBadelineMovingBlock) = 1, 1
24+
Ahorn.minimumSize(entity::NonBadelineMovingBlock) = 8, 8
25+
Ahorn.resizable(entity::NonBadelineMovingBlock) = true, true
26+
27+
function Ahorn.selection(entity::NonBadelineMovingBlock)
28+
if entity.name == "SpringCollab2020/nonBadelineMovingBlock"
29+
x, y = Ahorn.position(entity)
30+
nx, ny = Int.(entity.data["nodes"][1])
31+
32+
width = Int(get(entity.data, "width", 8))
33+
height = Int(get(entity.data, "height", 8))
34+
35+
return [Ahorn.Rectangle(x, y, width, height), Ahorn.Rectangle(nx, ny, width, height)]
36+
end
37+
end
38+
39+
function Ahorn.renderAbs(ctx::Ahorn.Cairo.CairoContext, entity::NonBadelineMovingBlock, room::Maple.Room)
40+
Ahorn.drawTileEntity(ctx, room, entity, material=get(entity.data, "tiletype", "g")[1], blendIn=false)
41+
end
42+
43+
function Ahorn.renderSelectedAbs(ctx::Ahorn.Cairo.CairoContext, entity::NonBadelineMovingBlock, room::Maple.Room)
44+
x, y = Ahorn.position(entity)
45+
nodes = get(entity.data, "nodes", ())
46+
47+
width = Int(get(entity.data, "width", 8))
48+
height = Int(get(entity.data, "height", 8))
49+
50+
if !isempty(nodes)
51+
nx, ny = Int.(nodes[1])
52+
cox, coy = floor(Int, width / 2), floor(Int, height / 2)
53+
54+
# Use 'G' instead of 'g', as that is the highlight color of the block (the active color)
55+
fakeTiles = Ahorn.createFakeTiles(room, nx, ny, width, height, get(entity.data, "highlightTiletype", "G")[1], blendIn=false)
56+
Ahorn.drawFakeTiles(ctx, room, fakeTiles, room.objTiles, true, nx, ny, clipEdges=true)
57+
Ahorn.drawArrow(ctx, x + cox, y + coy, nx + cox, ny + coy, Ahorn.colors.selection_selected_fc, headLength=6)
58+
end
59+
end
60+
61+
end

Ahorn/lang/en_gb.lang

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ placements.entities.SpringCollab2020/UpsideDownJumpThru.tooltips.texture=Changes
5555
placements.entities.SpringCollab2020/SidewaysJumpThru.tooltips.texture=Changes the appearance of the platform.
5656
placements.entities.SpringCollab2020/SidewaysJumpThru.tooltips.left=Whether the solid side of the jumpthru is the left side.
5757

58+
# Non-Badeline Moving Blocks
59+
placements.entities.SpringCollab2020/nonBadelineMovingBlock.tooltips.tiletype=Changes the visual appearance of the block before it starts moving.
60+
placements.entities.SpringCollab2020/nonBadelineMovingBlock.tooltips.highlightTiletype=Changes the visual appearance of the block once it starts moving.
61+
5862
# Custom Sandwich Lava
5963
placements.entities.SpringCollab2020/CustomSandwichLava.tooltips.direction=Changes the sandwich lava direction. CoreModeBased is the same behaviour as vanilla, depending on core mode.
6064
placements.entities.SpringCollab2020/CustomSandwichLava.tooltips.speed=Changes the speed the lava is moving at, in pixels per second. Vanilla value is 20.

Entities/NonBadelineMovingBlock.cs

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
using Celeste.Mod.Entities;
2+
using Microsoft.Xna.Framework;
3+
using Monocle;
4+
using System;
5+
using System.Collections;
6+
7+
namespace Celeste.Mod.SpringCollab2020.Entities {
8+
[CustomEntity("SpringCollab2020/nonBadelineMovingBlock")]
9+
[Tracked(false)]
10+
public class NonBadelineMovingBlock : Solid {
11+
12+
private float startDelay;
13+
14+
private int nodeIndex;
15+
16+
private Vector2[] nodes;
17+
18+
private TileGrid sprite;
19+
20+
private TileGrid highlight;
21+
22+
private Coroutine moveCoroutine;
23+
24+
private bool isHighlighted;
25+
26+
public NonBadelineMovingBlock(Vector2[] nodes, float width, float height, char tiletype, char highlightTiletype)
27+
: base(nodes[0], width, height, safe: false) {
28+
this.nodes = nodes;
29+
int newSeed = Calc.Random.Next();
30+
Calc.PushRandom(newSeed);
31+
sprite = GFX.FGAutotiler.GenerateBox(tiletype, (int) base.Width / 8, (int) base.Height / 8).TileGrid;
32+
Add(sprite);
33+
Calc.PopRandom();
34+
Calc.PushRandom(newSeed);
35+
highlight = GFX.FGAutotiler.GenerateBox(highlightTiletype, (int) (base.Width / 8f), (int) base.Height / 8).TileGrid;
36+
highlight.Alpha = 0f;
37+
Add(highlight);
38+
Calc.PopRandom();
39+
Add(new TileInterceptor(sprite, highPriority: false));
40+
Add(new LightOcclude());
41+
}
42+
43+
public NonBadelineMovingBlock(EntityData data, Vector2 offset)
44+
: this(data.NodesWithPosition(offset), data.Width, data.Height, data.Char("tiletype", 'g'), data.Char("highlightTiletype", 'G')) {
45+
}
46+
47+
public override void Added(Scene scene) {
48+
base.Added(scene);
49+
StartMoving(0);
50+
}
51+
52+
public override void OnShake(Vector2 amount) {
53+
base.OnShake(amount);
54+
sprite.Position = amount;
55+
}
56+
57+
public void StartMoving(float delay) {
58+
startDelay = delay;
59+
Add(moveCoroutine = new Coroutine(MoveSequence()));
60+
}
61+
62+
private IEnumerator MoveSequence() {
63+
while (true) {
64+
StartShaking(0.2f + startDelay);
65+
if (!isHighlighted) {
66+
for (float p = 0f; p < 1f; p += Engine.DeltaTime / (0.2f + startDelay + 0.2f)) {
67+
highlight.Alpha = Ease.CubeIn(p);
68+
sprite.Alpha = 1f - highlight.Alpha;
69+
yield return null;
70+
}
71+
highlight.Alpha = 1f;
72+
sprite.Alpha = 0f;
73+
isHighlighted = true;
74+
} else {
75+
yield return 0.2f + startDelay + 0.2f;
76+
}
77+
startDelay = 0f;
78+
nodeIndex++;
79+
nodeIndex %= nodes.Length;
80+
Vector2 from = Position;
81+
Vector2 to = nodes[nodeIndex];
82+
Tween tween = Tween.Create(Tween.TweenMode.Oneshot, Ease.CubeIn, 0.8f, start: true);
83+
tween.OnUpdate = delegate (Tween t) {
84+
MoveTo(Vector2.Lerp(from, to, t.Eased));
85+
};
86+
tween.OnComplete = delegate {
87+
if (CollideCheck<SolidTiles>(Position + (to - from).SafeNormalize() * 2f)) {
88+
Audio.Play("event:/game/06_reflection/fallblock_boss_impact", Center);
89+
ImpactParticles(to - from);
90+
} else {
91+
StopParticles(to - from);
92+
}
93+
};
94+
Add(tween);
95+
yield return 0.8f;
96+
}
97+
}
98+
99+
private void StopParticles(Vector2 moved) {
100+
Level level = SceneAs<Level>();
101+
float direction = moved.Angle();
102+
if (moved.X > 0f) {
103+
Vector2 value = new Vector2(Right - 1f, Top);
104+
for (int i = 0; i < Height; i += 4) {
105+
level.Particles.Emit(FinalBossMovingBlock.P_Stop, value + Vector2.UnitY * (2 + i + Calc.Random.Range(-1, 1)), direction);
106+
}
107+
} else if (moved.X < 0f) {
108+
Vector2 value2 = new Vector2(Left, Top);
109+
for (int j = 0; j < Height; j += 4) {
110+
level.Particles.Emit(FinalBossMovingBlock.P_Stop, value2 + Vector2.UnitY * (2 + j + Calc.Random.Range(-1, 1)), direction);
111+
}
112+
}
113+
if (moved.Y > 0f) {
114+
Vector2 value3 = new Vector2(Left, Bottom - 1f);
115+
for (int k = 0; k < Width; k += 4) {
116+
level.Particles.Emit(FinalBossMovingBlock.P_Stop, value3 + Vector2.UnitX * (2 + k + Calc.Random.Range(-1, 1)), direction);
117+
}
118+
} else if (moved.Y < 0f) {
119+
Vector2 value4 = new Vector2(Left, Top);
120+
for (int l = 0; l < Width; l += 4) {
121+
level.Particles.Emit(FinalBossMovingBlock.P_Stop, value4 + Vector2.UnitX * (2 + l + Calc.Random.Range(-1, 1)), direction);
122+
}
123+
}
124+
}
125+
126+
private void BreakParticles() {
127+
for (int i = 0; i < Width; i += 4) {
128+
for (int j = 0; j < Height; j += 4) {
129+
SceneAs<Level>().Particles.Emit(FinalBossMovingBlock.P_Break, 1, Position + new Vector2(2 + i, 2 + j), Vector2.One * 2f, (Position + new Vector2(2 + i, 2 + j) - Center).Angle());
130+
}
131+
}
132+
}
133+
134+
private void ImpactParticles(Vector2 moved) {
135+
if (moved.X < 0f) {
136+
Vector2 offset = new Vector2(0f, 2f);
137+
for (int i = 0; i < Height / 8f; i++) {
138+
Vector2 collideCheckPos = new Vector2(Left - 1f, Top + 4f + (i * 8));
139+
if (!Scene.CollideCheck<Water>(collideCheckPos) && Scene.CollideCheck<Solid>(collideCheckPos)) {
140+
SceneAs<Level>().ParticlesFG.Emit(CrushBlock.P_Impact, collideCheckPos + offset, 0f);
141+
SceneAs<Level>().ParticlesFG.Emit(CrushBlock.P_Impact, collideCheckPos - offset, 0f);
142+
}
143+
}
144+
} else if (moved.X > 0f) {
145+
Vector2 offset = new Vector2(0f, 2f);
146+
for (int j = 0; j < Height / 8f; j++) {
147+
Vector2 collideCheckPos = new Vector2(Right + 1f, Top + 4f + (j * 8));
148+
if (!Scene.CollideCheck<Water>(collideCheckPos) && Scene.CollideCheck<Solid>(collideCheckPos)) {
149+
SceneAs<Level>().ParticlesFG.Emit(CrushBlock.P_Impact, collideCheckPos + offset, (float) Math.PI);
150+
SceneAs<Level>().ParticlesFG.Emit(CrushBlock.P_Impact, collideCheckPos - offset, (float) Math.PI);
151+
}
152+
}
153+
}
154+
if (moved.Y < 0f) {
155+
Vector2 offset = new Vector2(2f, 0f);
156+
for (int k = 0; k < Width / 8f; k++) {
157+
Vector2 collideCheckPos = new Vector2(Left + 4f + (k * 8), Top - 1f);
158+
if (!Scene.CollideCheck<Water>(collideCheckPos) && Scene.CollideCheck<Solid>(collideCheckPos)) {
159+
SceneAs<Level>().ParticlesFG.Emit(CrushBlock.P_Impact, collideCheckPos + offset, (float) Math.PI / 2f);
160+
SceneAs<Level>().ParticlesFG.Emit(CrushBlock.P_Impact, collideCheckPos - offset, (float) Math.PI / 2f);
161+
}
162+
}
163+
} else {
164+
if (!(moved.Y > 0f)) {
165+
return;
166+
}
167+
Vector2 offset = new Vector2(2f, 0f);
168+
for (int l = 0; l < Width / 8f; l++) {
169+
Vector2 collideCheckPos = new Vector2(Left + 4f + (l * 8), Bottom + 1f);
170+
if (!Scene.CollideCheck<Water>(collideCheckPos) && Scene.CollideCheck<Solid>(collideCheckPos)) {
171+
SceneAs<Level>().ParticlesFG.Emit(CrushBlock.P_Impact, collideCheckPos + offset, -(float) Math.PI / 2f);
172+
SceneAs<Level>().ParticlesFG.Emit(CrushBlock.P_Impact, collideCheckPos - offset, -(float) Math.PI / 2f);
173+
}
174+
}
175+
}
176+
}
177+
178+
public override void Render() {
179+
Vector2 position = Position;
180+
Position += Shake;
181+
base.Render();
182+
if (highlight.Alpha > 0f && highlight.Alpha < 1f) {
183+
int num = (int) ((1f - highlight.Alpha) * 16f);
184+
Rectangle rect = new Rectangle((int) X, (int) Y, (int) Width, (int) Height);
185+
rect.Inflate(num, num);
186+
Draw.HollowRect(rect, Color.Lerp(Color.Purple, Color.Pink, 0.7f));
187+
}
188+
Position = position;
189+
}
190+
191+
private void Finish() {
192+
Vector2 from = CenterRight + Vector2.UnitX * 10f;
193+
for (int i = 0; i < Width / 8f; i++) {
194+
for (int j = 0; j < Height / 8f; j++) {
195+
Scene.Add(Engine.Pooler.Create<Debris>().Init(Position + new Vector2(4 + i * 8, 4 + j * 8), 'f', playSound: true).BlastFrom(from));
196+
}
197+
}
198+
BreakParticles();
199+
DestroyStaticMovers();
200+
RemoveSelf();
201+
}
202+
203+
public void Destroy(float delay) {
204+
if (Scene != null) {
205+
if (moveCoroutine != null) {
206+
Remove(moveCoroutine);
207+
}
208+
if (delay <= 0f) {
209+
Finish();
210+
return;
211+
}
212+
StartShaking(delay);
213+
Alarm.Set(this, delay, Finish);
214+
}
215+
}
216+
}
217+
}

0 commit comments

Comments
 (0)