Skip to content

Commit b884eb2

Browse files
authored
Merge branch 'master' into custom_sandwich_lava
2 parents 6ea753f + 9750e16 commit b884eb2

File tree

13 files changed

+704
-3
lines changed

13 files changed

+704
-3
lines changed

Ahorn/entities/sidewaysLava.jl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
module SpringCollab2020SidewaysLava
2+
3+
using ..Ahorn, Maple
4+
5+
@mapdef Entity "SpringCollab2020/SidewaysLava" SidewaysLava(x::Integer, y::Integer, intro::Bool=false, lavaMode::String="LeftToRight", speedMultiplier::Number=1)
6+
7+
const lavaModes = String["LeftToRight", "RightToLeft", "Sandwich"]
8+
9+
const placements = Ahorn.PlacementDict(
10+
"Sideways Lava (Spring Collab 2020)" => Ahorn.EntityPlacement(
11+
SidewaysLava
12+
)
13+
)
14+
15+
Ahorn.editingOptions(entity::SidewaysLava) = Dict{String, Any}(
16+
"lavaMode" => lavaModes
17+
)
18+
19+
function Ahorn.selection(entity::SidewaysLava)
20+
x, y = Ahorn.position(entity)
21+
22+
return Ahorn.Rectangle(x - 12, y - 12, 24, 24)
23+
end
24+
25+
function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::SidewaysLava, room::Maple.Room)
26+
Ahorn.Cairo.save(ctx)
27+
28+
lavaMode = get(entity.data, "lavaMode", "LeftToRight")
29+
30+
Ahorn.rotate(ctx, (lavaMode == "RightToLeft" ? -1 : 1) * pi / 2)
31+
Ahorn.drawImage(ctx, (lavaMode == "Sandwich" ? Ahorn.Assets.lavaSanwitch : Ahorn.Assets.risingLava), -12, -12)
32+
33+
Ahorn.Cairo.restore(ctx);
34+
end
35+
36+
end

Ahorn/lang/en_gb.lang

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,9 @@ placements.entities.SpringCollab2020/CustomSandwichLava.tooltips.sandwichGap=The
6363
# Custom Sandwich Lava Settings Trigger
6464
placements.triggers.SpringCollab2020/CustomSandwichLavaSettingsTrigger.tooltips.onlyOnce=If enabled, the settings will change only on the first time the player enters the trigger.
6565
placements.triggers.SpringCollab2020/CustomSandwichLavaSettingsTrigger.tooltips.direction=Changes the sandwich lava direction. CoreModeBased is the same behaviour as vanilla, depending on core mode.
66-
placements.triggers.SpringCollab2020/CustomSandwichLavaSettingsTrigger.tooltips.speed=Changes the speed the lava is moving at, in pixels per second. Vanilla value is 20.
66+
placements.triggers.SpringCollab2020/CustomSandwichLavaSettingsTrigger.tooltips.speed=Changes the speed the lava is moving at, in pixels per second. Vanilla value is 20.
67+
68+
# Sideways Lava
69+
placements.entities.SpringCollab2020/SidewaysLava.tooltips.intro=Determines whether the lava moves into the screen or starts already present on the screen. Not suitable for sandwich lava.
70+
placements.entities.SpringCollab2020/SidewaysLava.tooltips.lavaMode=Determines which side(s) of the screen the lava comes from.
71+
placements.entities.SpringCollab2020/SidewaysLava.tooltips.speedMultiplier=Modifies the lava speed. 1 is vanilla lava speed, 2 is twice as fast.

Entities/SidewaysLava.cs

Lines changed: 372 additions & 0 deletions
Large diffs are not rendered by default.

Entities/SidewaysLavaRect.cs

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
using Microsoft.Xna.Framework;
2+
using Microsoft.Xna.Framework.Graphics;
3+
using Monocle;
4+
using System;
5+
using System.Collections.Generic;
6+
7+
namespace Celeste.Mod.SpringCollab2020.Entities {
8+
// Strongly based on vanilla LavaRect, except their upper side is left or right, and bubbles go sideways.
9+
class SidewaysLavaRect : Component {
10+
public enum OnlyModes {
11+
OnlyLeft,
12+
OnlyRight
13+
}
14+
15+
private struct Bubble {
16+
public Vector2 Position;
17+
public float Speed;
18+
public float Alpha;
19+
}
20+
21+
private struct SurfaceBubble {
22+
public float Y;
23+
public float Frame;
24+
public byte Animation;
25+
}
26+
27+
public Vector2 Position;
28+
29+
public float Fade = 16f;
30+
public float Spikey = 0f;
31+
32+
public OnlyModes OnlyMode;
33+
34+
public float SmallWaveAmplitude = 1f;
35+
public float BigWaveAmplitude = 4f;
36+
public float CurveAmplitude = 7f;
37+
public float UpdateMultiplier = 1f;
38+
39+
public Color SurfaceColor = Color.White;
40+
public Color EdgeColor = Color.LightGray;
41+
public Color CenterColor = Color.DarkGray;
42+
43+
private float timer = Calc.Random.NextFloat(100f);
44+
45+
private VertexPositionColor[] verts;
46+
private int vertCount;
47+
48+
private bool dirty;
49+
50+
private Bubble[] bubbles;
51+
private SurfaceBubble[] surfaceBubbles;
52+
private int surfaceBubbleIndex;
53+
private List<List<MTexture>> surfaceBubbleAnimations;
54+
55+
public int SurfaceStep {
56+
get;
57+
private set;
58+
}
59+
60+
public float Width {
61+
get;
62+
private set;
63+
}
64+
65+
public float Height {
66+
get;
67+
private set;
68+
}
69+
70+
public SidewaysLavaRect(float width, float height, int step, OnlyModes onlyMode)
71+
: base(active: true, visible: true) {
72+
73+
OnlyMode = onlyMode;
74+
75+
Resize(width, height, step);
76+
}
77+
78+
public void Resize(float width, float height, int step) {
79+
Width = width;
80+
Height = height;
81+
SurfaceStep = step;
82+
dirty = true;
83+
84+
int surroundingSize = (int) (width / SurfaceStep * 2f + height / SurfaceStep * 2f + 4f);
85+
verts = new VertexPositionColor[surroundingSize * 3 * 6 + 6];
86+
87+
// spread bubbles in the LavaRect.
88+
bubbles = new Bubble[(int) (width * height * 0.005f)];
89+
surfaceBubbles = new SurfaceBubble[(int) Math.Max(4f, bubbles.Length * 0.25f)];
90+
for (int i = 0; i < bubbles.Length; i++) {
91+
bubbles[i].Position = new Vector2(1f + Calc.Random.NextFloat(Width - 2f), Calc.Random.NextFloat(Height));
92+
bubbles[i].Speed = Calc.Random.Range(4, 12);
93+
bubbles[i].Alpha = Calc.Random.Range(0.4f, 0.8f);
94+
}
95+
// initialize empty structures for surface bubbles.
96+
for (int j = 0; j < surfaceBubbles.Length; j++) {
97+
surfaceBubbles[j].Y = -1f;
98+
}
99+
surfaceBubbleAnimations = new List<List<MTexture>>();
100+
surfaceBubbleAnimations.Add(GFX.Game.GetAtlasSubtextures(OnlyMode == OnlyModes.OnlyLeft ?
101+
"danger/SpringCollab2020/sidewayslava/bubble_right_a" : "danger/SpringCollab2020/sidewayslava/bubble_left_a"));
102+
}
103+
104+
public override void Update() {
105+
timer += UpdateMultiplier * Engine.DeltaTime;
106+
if (UpdateMultiplier != 0f) {
107+
dirty = true;
108+
}
109+
110+
// make bubbles bubble "up" (actually right).
111+
for (int i = 0; i < bubbles.Length; i++) {
112+
bool limitReached = false;
113+
114+
if (OnlyMode == OnlyModes.OnlyLeft) {
115+
bubbles[i].Position.X += UpdateMultiplier * bubbles[i].Speed * Engine.DeltaTime;
116+
if (bubbles[i].Position.X > Width - 2f + Wave((int) (bubbles[i].Position.Y / SurfaceStep), Height)) {
117+
// bubble reached the surface. reset it
118+
bubbles[i].Position.X = 1f;
119+
limitReached = true;
120+
}
121+
} else {
122+
bubbles[i].Position.X -= UpdateMultiplier * bubbles[i].Speed * Engine.DeltaTime;
123+
if (bubbles[i].Position.X < 2f - Wave((int) (bubbles[i].Position.Y / SurfaceStep), Height)) {
124+
// bubble reached the surface. reset it
125+
bubbles[i].Position.X = Width - 1f;
126+
limitReached = true;
127+
}
128+
}
129+
130+
if (limitReached) {
131+
if (Calc.Random.Chance(0.75f)) {
132+
// we want the bubble to explode at the surface.
133+
surfaceBubbles[surfaceBubbleIndex].Y = bubbles[i].Position.Y;
134+
surfaceBubbles[surfaceBubbleIndex].Frame = 0f;
135+
surfaceBubbles[surfaceBubbleIndex].Animation = (byte) Calc.Random.Next(surfaceBubbleAnimations.Count);
136+
surfaceBubbleIndex = (surfaceBubbleIndex + 1) % surfaceBubbles.Length;
137+
}
138+
}
139+
}
140+
141+
// make surface bubbles animations advance.
142+
for (int j = 0; j < surfaceBubbles.Length; j++) {
143+
if (surfaceBubbles[j].Y >= 0f) {
144+
surfaceBubbles[j].Frame += Engine.DeltaTime * 6f;
145+
if (surfaceBubbles[j].Frame >= surfaceBubbleAnimations[surfaceBubbles[j].Animation].Count) {
146+
// animation is over: clean up!
147+
surfaceBubbles[j].Y = -1f;
148+
}
149+
}
150+
}
151+
152+
base.Update();
153+
}
154+
155+
private float Sin(float value) {
156+
return (1f + (float) Math.Sin(value)) / 2f;
157+
}
158+
159+
private float Wave(int step, float length) {
160+
int stepOffset = step * SurfaceStep;
161+
float waveOffset = Sin(stepOffset * 0.25f + timer * 4f) * SmallWaveAmplitude;
162+
waveOffset += Sin(stepOffset * 0.05f + timer * 0.5f) * BigWaveAmplitude;
163+
if (step % 2 == 0) {
164+
waveOffset += Spikey;
165+
}
166+
waveOffset += (1f - Calc.YoYo(stepOffset / length)) * CurveAmplitude;
167+
return waveOffset;
168+
}
169+
170+
private void Quad(ref int vert, Vector2 va, Vector2 vb, Vector2 vc, Vector2 vd, Color color) {
171+
Quad(ref vert, va, color, vb, color, vc, color, vd, color);
172+
}
173+
174+
private void Quad(ref int vert, Vector2 va, Color ca, Vector2 vb, Color cb, Vector2 vc, Color cc, Vector2 vd, Color cd) {
175+
verts[vert].Position.X = va.X;
176+
verts[vert].Position.Y = va.Y;
177+
verts[vert++].Color = ca;
178+
verts[vert].Position.X = vb.X;
179+
verts[vert].Position.Y = vb.Y;
180+
verts[vert++].Color = cb;
181+
verts[vert].Position.X = vc.X;
182+
verts[vert].Position.Y = vc.Y;
183+
verts[vert++].Color = cc;
184+
verts[vert].Position.X = va.X;
185+
verts[vert].Position.Y = va.Y;
186+
verts[vert++].Color = ca;
187+
verts[vert].Position.X = vc.X;
188+
verts[vert].Position.Y = vc.Y;
189+
verts[vert++].Color = cc;
190+
verts[vert].Position.X = vd.X;
191+
verts[vert].Position.Y = vd.Y;
192+
verts[vert++].Color = cd;
193+
}
194+
195+
private void Edge(ref int vert, Vector2 a, Vector2 b, float fade) {
196+
float edgeSize = (a - b).Length();
197+
float stepCount = edgeSize / SurfaceStep;
198+
Vector2 direction = (b - a).SafeNormalize();
199+
Vector2 perpendicular = direction.Perpendicular();
200+
for (int i = 1; i <= stepCount; i++) {
201+
// values for the previous position
202+
Vector2 positionBefore = Vector2.Lerp(a, b, (i - 1) / stepCount);
203+
float waveOffsetBefore = Wave(i - 1, edgeSize);
204+
Vector2 wavePositionBefore = positionBefore - perpendicular * waveOffsetBefore;
205+
206+
// values for the current position
207+
Vector2 position = Vector2.Lerp(a, b, i / stepCount);
208+
float waveOffset = Wave(i, edgeSize);
209+
Vector2 wavePosition = position - perpendicular * waveOffset;
210+
211+
Quad(ref vert, wavePositionBefore + perpendicular, EdgeColor,
212+
wavePosition + perpendicular, EdgeColor,
213+
position + perpendicular * (fade - waveOffset), CenterColor,
214+
positionBefore + perpendicular * (fade - waveOffsetBefore), CenterColor);
215+
216+
Quad(ref vert, positionBefore + perpendicular * (fade - waveOffsetBefore),
217+
position + perpendicular * (fade - waveOffset),
218+
position + perpendicular * fade,
219+
positionBefore + perpendicular * fade,
220+
CenterColor);
221+
222+
Quad(ref vert, wavePositionBefore,
223+
wavePosition,
224+
wavePosition + perpendicular,
225+
wavePositionBefore + perpendicular * 1f,
226+
SurfaceColor);
227+
}
228+
}
229+
230+
public override void Render() {
231+
GameplayRenderer.End();
232+
233+
// render the edges of the lava rect.
234+
if (dirty) {
235+
Vector2 zero = Vector2.Zero;
236+
237+
Vector2 topLeft = zero;
238+
Vector2 topRight = new Vector2(zero.X + Width, zero.Y);
239+
Vector2 bottomLeft = new Vector2(zero.X, zero.Y + Height);
240+
Vector2 bottomRight = zero + new Vector2(Width, Height);
241+
242+
Vector2 fadeOffset = new Vector2(Math.Min(Fade, Width / 2f), Math.Min(Fade, Height / 2f));
243+
vertCount = 0;
244+
if (OnlyMode == OnlyModes.OnlyLeft) {
245+
Edge(ref vertCount, topRight, bottomRight, fadeOffset.X);
246+
Quad(ref vertCount, topLeft, topRight - new Vector2(fadeOffset.X, 0f), bottomRight - new Vector2(fadeOffset.X, 0f), bottomLeft, CenterColor);
247+
} else if (OnlyMode == OnlyModes.OnlyRight) {
248+
Edge(ref vertCount, bottomLeft, topLeft, fadeOffset.X);
249+
Quad(ref vertCount, topLeft + new Vector2(fadeOffset.X, 0f), topRight, bottomRight, bottomLeft + new Vector2(fadeOffset.X, 0f), CenterColor);
250+
}
251+
252+
dirty = false;
253+
}
254+
255+
// render the vertices
256+
Camera camera = (Scene as Level).Camera;
257+
GFX.DrawVertices(Matrix.CreateTranslation(new Vector3(Entity.Position + Position, 0f)) * camera.Matrix, verts, vertCount);
258+
259+
GameplayRenderer.Begin();
260+
261+
Vector2 rectPosition = Entity.Position + Position;
262+
263+
// render bubbles
264+
MTexture bubbleTexture = GFX.Game["particles/bubble"];
265+
for (int i = 0; i < bubbles.Length; i++) {
266+
bubbleTexture.DrawCentered(rectPosition + bubbles[i].Position, SurfaceColor * bubbles[i].Alpha);
267+
}
268+
269+
// render surface bubbles
270+
for (int j = 0; j < surfaceBubbles.Length; j++) {
271+
if (surfaceBubbles[j].Y >= 0f) {
272+
MTexture surfaceBubbleTexture = surfaceBubbleAnimations[surfaceBubbles[j].Animation][(int) surfaceBubbles[j].Frame];
273+
int bubbleOffset = (int) (surfaceBubbles[j].Y / SurfaceStep);
274+
275+
float x;
276+
if (OnlyMode == OnlyModes.OnlyLeft)
277+
x = Width + 7f + Wave(bubbleOffset, Height);
278+
else
279+
x = 1f - Wave(bubbleOffset, Height);
280+
281+
surfaceBubbleTexture.DrawJustified(rectPosition +
282+
new Vector2(x, OnlyMode == OnlyModes.OnlyRight ? Height - bubbleOffset * SurfaceStep : bubbleOffset * SurfaceStep),
283+
new Vector2(1f, 0.5f), SurfaceColor);
284+
}
285+
}
286+
}
287+
}
288+
}
143 Bytes
Loading
145 Bytes
Loading
154 Bytes
Loading
138 Bytes
Loading
141 Bytes
Loading
146 Bytes
Loading

0 commit comments

Comments
 (0)