Skip to content

Commit 501b2ff

Browse files
authored
Merge pull request #79 from EverestAPI/custom_sandwich_lava
Custom Sandwich Lava (custom speed/width/direction)
2 parents 9750e16 + b884eb2 commit 501b2ff

File tree

7 files changed

+389
-0
lines changed

7 files changed

+389
-0
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module SpringCollab2020CustomSandwichLava
2+
3+
using ..Ahorn, Maple
4+
5+
@mapdef Entity "SpringCollab2020/CustomSandwichLava" CustomSandwichLava(x::Integer, y::Integer,
6+
direction::String="CoreModeBased", speed::Number=20, sandwichGap::Number=160)
7+
8+
const directions = String["AlwaysUp", "AlwaysDown", "CoreModeBased"]
9+
10+
const placements = Ahorn.PlacementDict(
11+
"Custom Sandwich Lava (Spring Collab 2020)" => Ahorn.EntityPlacement(
12+
CustomSandwichLava
13+
)
14+
)
15+
16+
Ahorn.editingOptions(entity::CustomSandwichLava) = Dict{String, Any}(
17+
"direction" => directions
18+
)
19+
20+
function Ahorn.selection(entity::CustomSandwichLava)
21+
x, y = Ahorn.position(entity)
22+
23+
return Ahorn.Rectangle(x - 12, y - 12, 24, 24)
24+
end
25+
26+
function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::CustomSandwichLava, room::Maple.Room)
27+
direction = get(entity.data, "direction", "CoreModeBased")
28+
29+
if direction == "AlwaysUp"
30+
Ahorn.drawSprite(ctx, "ahorn/SpringCollab2020/lava_sandwich_up", 0, 0)
31+
elseif direction == "AlwaysDown"
32+
Ahorn.drawSprite(ctx, "ahorn/SpringCollab2020/lava_sandwich_down", 0, 0)
33+
else
34+
Ahorn.drawImage(ctx, Ahorn.Assets.lavaSanwitch, -12, -12)
35+
end
36+
end
37+
38+
end

Ahorn/lang/en_gb.lang

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ 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+
# Custom Sandwich Lava
59+
placements.entities.SpringCollab2020/CustomSandwichLava.tooltips.direction=Changes the sandwich lava direction. CoreModeBased is the same behaviour as vanilla, depending on core mode.
60+
placements.entities.SpringCollab2020/CustomSandwichLava.tooltips.speed=Changes the speed the lava is moving at, in pixels per second. Vanilla value is 20.
61+
placements.entities.SpringCollab2020/CustomSandwichLava.tooltips.sandwichGap=The gap in pixels between both sides of the lava. Vanilla value is 160.
62+
63+
# Custom Sandwich Lava Settings Trigger
64+
placements.triggers.SpringCollab2020/CustomSandwichLavaSettingsTrigger.tooltips.onlyOnce=If enabled, the settings will change only on the first time the player enters the trigger.
65+
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.
67+
5868
# Sideways Lava
5969
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.
6070
placements.entities.SpringCollab2020/SidewaysLava.tooltips.lavaMode=Determines which side(s) of the screen the lava comes from.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
module SpringCollab2020CustomSandwichLavaSettingsTrigger
2+
3+
using ..Ahorn, Maple
4+
5+
@mapdef Trigger "SpringCollab2020/CustomSandwichLavaSettingsTrigger" CustomSandwichLavaSettingsTrigger(x::Integer, y::Integer, width::Integer=Maple.defaultTriggerWidth, height::Integer=Maple.defaultTriggerHeight,
6+
onlyOnce::Bool=false, direction::String="CoreModeBased", speed::Number=20)
7+
8+
const directions = String["AlwaysUp", "AlwaysDown", "CoreModeBased"]
9+
10+
const placements = Ahorn.PlacementDict(
11+
"Custom Sandwich Lava Settings (Spring Collab 2020)" => Ahorn.EntityPlacement(
12+
CustomSandwichLavaSettingsTrigger,
13+
"rectangle",
14+
),
15+
)
16+
17+
Ahorn.editingOptions(entity::CustomSandwichLavaSettingsTrigger) = Dict{String, Any}(
18+
"direction" => directions
19+
)
20+
21+
end

Entities/CustomSandwichLava.cs

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
using Celeste.Mod.Entities;
2+
using Microsoft.Xna.Framework;
3+
using Monocle;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Reflection;
7+
8+
namespace Celeste.Mod.SpringCollab2020.Entities {
9+
/// <summary>
10+
/// Almost a copypaste of the vanilla SandwichLava class, but:
11+
/// - plays nicely with vertical rooms
12+
/// - can be made one-way
13+
/// - has customizable speed and gap.
14+
/// </summary>
15+
[CustomEntity("SpringCollab2020/CustomSandwichLava")]
16+
[Tracked]
17+
class CustomSandwichLava : Entity {
18+
private static FieldInfo lavaBlockerTriggerEnabled = typeof(LavaBlockerTrigger).GetField("enabled", BindingFlags.NonPublic | BindingFlags.Instance);
19+
20+
// if the player collides with one of those, lava should be forced into waiting.
21+
private List<LavaBlockerTrigger> lavaBlockerTriggers;
22+
23+
public enum DirectionMode {
24+
AlwaysUp, AlwaysDown, CoreModeBased
25+
};
26+
27+
private const float TopOffset = -160f;
28+
29+
// parameters
30+
private float startX;
31+
public DirectionMode Direction;
32+
public float Speed;
33+
// the extra pixels each side has to be shifted towards each other compared to vanilla
34+
// to comply with the "sandwichGap" setting.
35+
private float sandwichDisplacement;
36+
37+
// state
38+
private bool iceMode;
39+
private float lerp;
40+
public bool Waiting;
41+
private bool leaving = false;
42+
private float delay = 0f;
43+
private bool persistent;
44+
private bool entering = true;
45+
46+
// during a transition, those hold the Y positions of lava parts from the beginning of the transition.
47+
private float transitionStartY;
48+
private float transitionStartTopRectY;
49+
private float transitionStartBottomRectY;
50+
51+
private LavaRect bottomRect;
52+
private LavaRect topRect;
53+
54+
private SoundSource loopSfx;
55+
56+
// in vanilla, this is SceneAs<Level>().Bounds.Bottom. This is bad with vertical rooms.
57+
private float centerY => SceneAs<Level>().Camera.Bottom - 10f;
58+
59+
public CustomSandwichLava(EntityData data, Vector2 offset) {
60+
startX = data.Position.X + offset.X;
61+
Direction = data.Enum("direction", DirectionMode.CoreModeBased);
62+
Speed = data.Float("speed", 20f);
63+
64+
// vanilla is 160. so, setting sandwichGap to 120 requires each side to be shifted by 20 pixels towards the other ((160 - 120) / 2).
65+
sandwichDisplacement = (160f - data.Float("sandwichGap", 160f)) / 2;
66+
67+
Depth = -1000000;
68+
Collider = new ColliderList(new Hitbox(340f, 120f, 0f, -sandwichDisplacement), new Hitbox(340f, 120f, 0f, -280f + sandwichDisplacement));
69+
Visible = false;
70+
71+
Add(loopSfx = new SoundSource());
72+
Add(new PlayerCollider(OnPlayer));
73+
Add(new CoreModeListener(OnChangeMode));
74+
75+
Add(bottomRect = new LavaRect(400f, 200f, 4));
76+
bottomRect.Position = new Vector2(-40f, 0f);
77+
bottomRect.OnlyMode = LavaRect.OnlyModes.OnlyTop;
78+
bottomRect.SmallWaveAmplitude = 2f;
79+
80+
Add(topRect = new LavaRect(400f, 200f, 4));
81+
topRect.Position = new Vector2(-40f, -360f);
82+
topRect.OnlyMode = LavaRect.OnlyModes.OnlyBottom;
83+
topRect.SmallWaveAmplitude = 2f;
84+
topRect.BigWaveAmplitude = (bottomRect.BigWaveAmplitude = 2f);
85+
topRect.CurveAmplitude = (bottomRect.CurveAmplitude = 4f);
86+
87+
Add(new TransitionListener {
88+
OnOutBegin = () => {
89+
// save the Y positions
90+
transitionStartY = Y;
91+
transitionStartTopRectY = topRect.Position.Y;
92+
transitionStartBottomRectY = bottomRect.Position.Y;
93+
94+
if (persistent && Scene != null && Scene.Entities.FindAll<CustomSandwichLava>().Count <= 1) {
95+
// no lava in the next room: leave
96+
Leave();
97+
} else {
98+
// look up for all lava blocker triggers in the next room.
99+
lavaBlockerTriggers = Scene.Entities.OfType<LavaBlockerTrigger>().ToList();
100+
}
101+
},
102+
OnOut = progress => {
103+
if (Scene != null) {
104+
X = (Scene as Level).Camera.X;
105+
if (!leaving) {
106+
// make the lava elements transition smoothly to their expected positions.
107+
Y = MathHelper.Lerp(transitionStartY, centerY, progress);
108+
topRect.Position.Y = MathHelper.Lerp(transitionStartTopRectY, TopOffset - topRect.Height + sandwichDisplacement, progress);
109+
bottomRect.Position.Y = MathHelper.Lerp(transitionStartBottomRectY, -sandwichDisplacement, progress);
110+
}
111+
}
112+
113+
if ((progress > 0.95f) && leaving) {
114+
// lava is leaving, transition is over soon => remove it
115+
RemoveSelf();
116+
}
117+
},
118+
OnInEnd = () => {
119+
if (entering) {
120+
// transition is over. grab the camera position now since it's done moving.
121+
Y = centerY;
122+
entering = false;
123+
}
124+
}
125+
});
126+
}
127+
128+
public override void Added(Scene scene) {
129+
base.Added(scene);
130+
X = SceneAs<Level>().Bounds.Left - 10;
131+
Y = centerY;
132+
iceMode = (SceneAs<Level>().Session.CoreMode == Session.CoreModes.Cold);
133+
}
134+
135+
public override void Awake(Scene scene) {
136+
base.Awake(scene);
137+
138+
Player entity = Scene.Tracker.GetEntity<Player>();
139+
if (entity != null && (entity.JustRespawned || entity.X < startX)) {
140+
Waiting = true;
141+
}
142+
143+
List<CustomSandwichLava> existingLavas = Scene.Entities.FindAll<CustomSandwichLava>();
144+
145+
bool removedSelf = false;
146+
if (!persistent && existingLavas.Count >= 2) {
147+
// another lava entity already exists.
148+
CustomSandwichLava sandwichLava = (existingLavas[0] == this) ? existingLavas[1] : existingLavas[0];
149+
if (!sandwichLava.leaving) {
150+
// transfer new settings to the existing lava.
151+
sandwichLava.startX = startX;
152+
sandwichLava.Waiting = true;
153+
sandwichLava.Direction = Direction;
154+
sandwichLava.Speed = Speed;
155+
sandwichLava.sandwichDisplacement = sandwichDisplacement;
156+
sandwichLava.Collider = Collider;
157+
entering = false;
158+
RemoveSelf();
159+
removedSelf = true;
160+
}
161+
}
162+
163+
if (!removedSelf) {
164+
// turn the lava persistent, so that it animates during room transitions
165+
// and deals smoothly with successive rooms with sandwich lava.
166+
persistent = true;
167+
Tag = Tags.Persistent;
168+
169+
if ((scene as Level).LastIntroType != Player.IntroTypes.Respawn) {
170+
// throw the two lava parts off-screen.
171+
topRect.Position.Y -= 60f + sandwichDisplacement;
172+
bottomRect.Position.Y += 60f + sandwichDisplacement;
173+
} else {
174+
Visible = true;
175+
}
176+
177+
loopSfx.Play("event:/game/09_core/rising_threat", "room_state", iceMode ? 1 : 0);
178+
loopSfx.Position = new Vector2(Width / 2f, 0f);
179+
180+
// look up for all lava blocker triggers in the room.
181+
lavaBlockerTriggers = scene.Entities.OfType<LavaBlockerTrigger>().ToList();
182+
}
183+
}
184+
185+
private void OnChangeMode(Session.CoreModes mode) {
186+
iceMode = (mode == Session.CoreModes.Cold);
187+
loopSfx.Param("room_state", iceMode ? 1 : 0);
188+
}
189+
190+
private void OnPlayer(Player player) {
191+
if (Waiting) {
192+
return;
193+
}
194+
if (SaveData.Instance.Assists.Invincible) {
195+
if (delay <= 0f) {
196+
int direction = (player.Y > Y + bottomRect.Position.Y - 32f) ? 1 : -1;
197+
198+
float from = Y;
199+
float to = Y + (direction * 48);
200+
player.Speed.Y = -direction * 200;
201+
202+
if (direction > 0) {
203+
player.RefillDash();
204+
}
205+
206+
Tween.Set(this, Tween.TweenMode.Oneshot, 0.4f, Ease.CubeOut, tween => {
207+
Y = MathHelper.Lerp(from, to, tween.Eased);
208+
});
209+
210+
delay = 0.5f;
211+
loopSfx.Param("rising", 0f);
212+
Audio.Play("event:/game/general/assist_screenbottom", player.Position);
213+
}
214+
} else {
215+
player.Die(-Vector2.UnitY);
216+
}
217+
}
218+
219+
public void Leave() {
220+
AddTag(Tags.TransitionUpdate);
221+
leaving = true;
222+
Collidable = false;
223+
Alarm.Set(this, 2f, delegate {
224+
RemoveSelf();
225+
});
226+
}
227+
228+
public override void Update() {
229+
if (entering) {
230+
// set the Y position again on the first Update call, since the camera is finished being set up.
231+
Y = centerY;
232+
entering = false;
233+
}
234+
235+
Level level = Scene as Level;
236+
X = level.Camera.X;
237+
delay -= Engine.DeltaTime;
238+
base.Update();
239+
Visible = true;
240+
241+
Player player = Scene.Tracker.GetEntity<Player>();
242+
if (player != null) {
243+
LavaBlockerTrigger collidedTrigger = lavaBlockerTriggers.Find(trigger => player.CollideCheck(trigger));
244+
245+
if (collidedTrigger != null && (bool) lavaBlockerTriggerEnabled.GetValue(collidedTrigger)) {
246+
// player is in a lava blocker trigger and it is enabled; block the lava.
247+
Waiting = true;
248+
}
249+
}
250+
251+
if (Waiting) {
252+
Y = Calc.Approach(Y, centerY, 128f * Engine.DeltaTime);
253+
254+
loopSfx.Param("rising", 0f);
255+
if (player != null && player.X >= startX && !player.JustRespawned && player.StateMachine.State != 11) {
256+
Waiting = false;
257+
}
258+
} else if (!leaving && delay <= 0f) {
259+
loopSfx.Param("rising", 1f);
260+
if (Direction == DirectionMode.AlwaysDown || (Direction == DirectionMode.CoreModeBased && iceMode)) {
261+
Y += Speed * Engine.DeltaTime;
262+
} else {
263+
Y -= Speed * Engine.DeltaTime;
264+
}
265+
}
266+
267+
topRect.Position.Y = Calc.Approach(topRect.Position.Y, TopOffset - topRect.Height + (leaving ? (-512) : sandwichDisplacement), (leaving ? 256 : 64) * Engine.DeltaTime);
268+
bottomRect.Position.Y = Calc.Approach(bottomRect.Position.Y, leaving ? 512 : -sandwichDisplacement, (leaving ? 256 : 64) * Engine.DeltaTime);
269+
270+
lerp = Calc.Approach(lerp, iceMode ? 1 : 0, Engine.DeltaTime * 4f);
271+
272+
bottomRect.SurfaceColor = Color.Lerp(RisingLava.Hot[0], RisingLava.Cold[0], lerp);
273+
bottomRect.EdgeColor = Color.Lerp(RisingLava.Hot[1], RisingLava.Cold[1], lerp);
274+
bottomRect.CenterColor = Color.Lerp(RisingLava.Hot[2], RisingLava.Cold[2], lerp);
275+
bottomRect.Spikey = lerp * 5f;
276+
bottomRect.UpdateMultiplier = (1f - lerp) * 2f;
277+
bottomRect.Fade = (iceMode ? 128 : 32);
278+
279+
topRect.SurfaceColor = bottomRect.SurfaceColor;
280+
topRect.EdgeColor = bottomRect.EdgeColor;
281+
topRect.CenterColor = bottomRect.CenterColor;
282+
topRect.Spikey = bottomRect.Spikey;
283+
topRect.UpdateMultiplier = bottomRect.UpdateMultiplier;
284+
topRect.Fade = bottomRect.Fade;
285+
}
286+
}
287+
}
564 Bytes
Loading
561 Bytes
Loading

0 commit comments

Comments
 (0)