Skip to content

Commit 972e4d4

Browse files
committed
Add bubble columns
1 parent 9dcda4a commit 972e4d4

File tree

5 files changed

+269
-1
lines changed

5 files changed

+269
-1
lines changed

Ahorn/entities/bubblePushField.jl

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
module SpringCollab2020BubblePushField
2+
3+
using ..Ahorn, Maple
4+
5+
@mapdef Entity "SpringCollab2020/bubblePushField" BubblePushField(x::Integer, y::Integer, width::Integer=Maple.defaultBlockWidth, height::Integer=Maple.defaultBlockHeight, strength::Number=1, upwardStrength::Number=1, direction::String="Right", water::Bool=true)
6+
7+
const placements = Ahorn.PlacementDict(
8+
"Bubble Column (Spring Collab 2020)" => Ahorn.EntityPlacement(
9+
BubblePushField,
10+
"rectangle"
11+
)
12+
)
13+
14+
Ahorn.editingOptions(entity::BubblePushField) = Dict{String,Any}(
15+
"direction" => ["Up", "Down", "Left", "Right"]
16+
)
17+
18+
Ahorn.minimumSize(entity::BubblePushField) = 8, 8
19+
Ahorn.resizable(entity::BubblePushField) = true, true
20+
21+
Ahorn.selection(entity::BubblePushField) = Ahorn.getEntityRectangle(entity)
22+
23+
function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::BubblePushField, room::Maple.Room)
24+
x = Int(get(entity.data, "x", 0))
25+
y = Int(get(entity.data, "y", 0))
26+
27+
width = Int(get(entity.data, "width", 32))
28+
height = Int(get(entity.data, "height", 32))
29+
30+
Ahorn.drawRectangle(ctx, 0, 0, width, height, (0.7, 0.28, 0.0, 0.34), (1.0, 1.0, 1.0, 0.5))
31+
end
32+
33+
end

Ahorn/lang/en_gb.lang

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,10 @@ placements.entities.SpringCollab2020/dashSpring.tooltips.playerCanUse=Determines
4040

4141
# Customizable Glass Block Controller
4242
placements.entities.SpringCollab2020/CustomizableGlassBlockController.tooltips.starColors=A comma-separated list of all star colours visible in glass blocks in the room.
43-
placements.entities.SpringCollab2020/CustomizableGlassBlockController.tooltips.bgColor=The background colour for glass blocks in the room.
43+
placements.entities.SpringCollab2020/CustomizableGlassBlockController.tooltips.bgColor=The background colour for glass blocks in the room.
44+
45+
# Bubble Push Field
46+
placements.entities.SpringCollab2020/bubblePushField.tooltips.strength=The strength with which the bubbles push objects within.
47+
placements.entities.SpringCollab2020/bubblePushField.tooltips.upwardStrength=How much the bubbles push the player up to compensate for gravity.
48+
placements.entities.SpringCollab2020/bubblePushField.tooltips.direction=The direction the bubbles push the player.
49+
placements.entities.SpringCollab2020/bubblePushField.tooltips.water=If the bubble field will create a Water entity to make its presence visible.

Entities/BubblePushField.cs

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
using Microsoft.Xna.Framework;
2+
using Celeste.Mod.Entities;
3+
using System;
4+
using System.Collections.Generic;
5+
using Monocle;
6+
7+
namespace Celeste.Mod.SpringCollab2020.Entities {
8+
[CustomEntity("SpringCollab2020/bubblePushField")]
9+
class BubblePushField : Entity {
10+
public new int Width;
11+
12+
public new int Height;
13+
14+
public float Strength;
15+
16+
public float UpwardStrength;
17+
18+
private Dictionary<WindMover, float> WindMovers = new Dictionary<WindMover, float>();
19+
20+
private HitboxlessWater Water;
21+
22+
private bool _water;
23+
24+
// This is public so that bubble particles can use it so that their positions actually change.
25+
public Random Rand;
26+
27+
private int FramesSinceSpawn = 0;
28+
29+
private int SpawnFrame = 30;
30+
31+
public PushDirection Direction;
32+
33+
public BubblePushField(EntityData data, Vector2 offset) : this(
34+
data.Position + offset,
35+
data.Width,
36+
data.Height,
37+
data.Float("strength", 1f),
38+
data.Float("upwardStrength", 1f),
39+
data.Attr("direction", "right"),
40+
data.Bool("water", true)
41+
) { }
42+
43+
public BubblePushField(Vector2 position, int width, int height, float strength, float upwardStrength, string direction, bool water) {
44+
Position = position;
45+
Width = width;
46+
Height = height;
47+
Strength = strength;
48+
UpwardStrength = upwardStrength;
49+
_water = water;
50+
51+
Rand = new Random();
52+
53+
Enum.TryParse(direction, out Direction);
54+
55+
Collider = new Hitbox(width, height);
56+
}
57+
58+
public override void Added(Scene scene) {
59+
base.Added(scene);
60+
61+
if (_water)
62+
scene.Add(Water = new HitboxlessWater(Position, true, true, Width, Height));
63+
}
64+
65+
public override void Removed(Scene scene) {
66+
base.Removed(scene);
67+
68+
if (_water)
69+
scene.Remove(Water);
70+
}
71+
72+
public override void Render() {
73+
base.Render();
74+
}
75+
76+
public override void Update() {
77+
base.Update();
78+
79+
FramesSinceSpawn++;
80+
if (FramesSinceSpawn == SpawnFrame) {
81+
FramesSinceSpawn = 0;
82+
SpawnFrame = Rand.Next(2, 10);
83+
Add(new BubbleParticle(true, true));
84+
}
85+
86+
foreach (WindMover mover in Scene.Tracker.GetComponents<WindMover>()) {
87+
if(mover.Entity.CollideCheck(this)) {
88+
if (WindMovers.ContainsKey(mover))
89+
WindMovers[mover] = Calc.Approach(WindMovers[mover], Strength, Engine.DeltaTime / .6f);
90+
else
91+
WindMovers.Add(mover, 0f);
92+
} else {
93+
if(WindMovers.ContainsKey(mover)) {
94+
WindMovers[mover] = Calc.Approach(WindMovers[mover], 0f, Engine.DeltaTime / 0.3f);
95+
if (WindMovers[mover] == 0f)
96+
WindMovers.Remove(mover);
97+
}
98+
}
99+
}
100+
101+
foreach (WindMover mover in WindMovers.Keys) {
102+
float windSpeed = Strength * 2f * Ease.CubeInOut(WindMovers[mover]);
103+
104+
if (mover != null && mover.Entity != null && mover.Entity.Scene != null)
105+
switch (Direction) {
106+
case PushDirection.Up:
107+
mover.Move(new Vector2(0, -windSpeed));
108+
break;
109+
110+
case PushDirection.Down:
111+
mover.Move(new Vector2(0, windSpeed));
112+
break;
113+
114+
case PushDirection.Left:
115+
mover.Move(new Vector2(-windSpeed, 0));
116+
break;
117+
118+
case PushDirection.Right:
119+
mover.Move(new Vector2(windSpeed, 0));
120+
break;
121+
}
122+
123+
if (mover.Entity is Player && mover.Entity.CollideCheck(this) && Strength > 0 && Direction != PushDirection.Down) {
124+
Player tempPlayer = (Player) mover.Entity;
125+
if (tempPlayer.Holding != null)
126+
return;
127+
128+
mover.Move(new Vector2(0f, -UpwardStrength));
129+
}
130+
131+
if (mover.Entity is Glider && Strength > 0 && Direction != PushDirection.Down) {
132+
mover.Move(new Vector2(0f, -UpwardStrength / 20));
133+
}
134+
}
135+
}
136+
}
137+
138+
class HitboxlessWater : Water {
139+
public HitboxlessWater(Vector2 position, bool topSurface, bool bottomSurface, float width, float height)
140+
: base(position, topSurface, bottomSurface, width, height) {
141+
Collidable = false;
142+
}
143+
}
144+
145+
class BubbleParticle : Component {
146+
public Vector2 Position = Vector2.Zero;
147+
148+
public BubblePushField BubbleField;
149+
150+
public MTexture Texture;
151+
152+
private Random Rand;
153+
154+
private int FramesAlive = 0;
155+
156+
private int FramesMaxAlive;
157+
158+
private Vector2 Origin, End;
159+
160+
public BubbleParticle(bool active, bool visible) : base(active, visible) { }
161+
162+
public override void Added(Entity entity) {
163+
base.Added(entity);
164+
165+
BubbleField = (BubblePushField) entity;
166+
Position = BubbleField.Position;
167+
168+
Rand = BubbleField.Rand;
169+
Texture = GFX.Game["particles/SpringCollab2020/bubble_" + new string[] { "a", "b" }[Rand.Next(0, 1)]];
170+
171+
// Determine bubble spawn point
172+
switch (BubbleField.Direction) {
173+
case PushDirection.Up:
174+
Origin = new Vector2(Rand.Range(BubbleField.BottomLeft.X, BubbleField.BottomRight.X), BubbleField.BottomCenter.Y);
175+
End = new Vector2(Rand.Range(BubbleField.TopLeft.X, BubbleField.TopRight.X), BubbleField.TopCenter.Y);
176+
FramesMaxAlive = (int) Rand.Range(20, BubbleField.Height / BubbleField.Strength * .5f);
177+
break;
178+
179+
case PushDirection.Down:
180+
Origin = new Vector2(Rand.Range(BubbleField.TopLeft.X, BubbleField.TopRight.X), BubbleField.TopCenter.Y);
181+
End = new Vector2(Rand.Range(BubbleField.BottomLeft.X, BubbleField.BottomRight.X), BubbleField.BottomCenter.Y);
182+
FramesMaxAlive = (int) Rand.Range(20, BubbleField.Height / BubbleField.Strength * .5f);
183+
break;
184+
185+
case PushDirection.Right:
186+
Origin = new Vector2(BubbleField.CenterLeft.X, Rand.Range(BubbleField.BottomLeft.Y, BubbleField.TopLeft.Y));
187+
End = new Vector2(BubbleField.CenterRight.X, Rand.Range(BubbleField.BottomRight.Y, BubbleField.TopRight.Y));
188+
FramesMaxAlive = (int) Rand.Range(20, BubbleField.Width / BubbleField.Strength * .5f);
189+
break;
190+
191+
case PushDirection.Left:
192+
Origin = new Vector2(BubbleField.CenterRight.X, Rand.Range(BubbleField.BottomRight.Y, BubbleField.TopRight.Y));
193+
End = new Vector2(BubbleField.CenterLeft.X, Rand.Range(BubbleField.BottomLeft.Y, BubbleField.TopLeft.Y));
194+
FramesMaxAlive = (int) Rand.Range(20, BubbleField.Height / BubbleField.Strength * .5f);
195+
break;
196+
}
197+
198+
Position = Origin;
199+
}
200+
201+
public override void Update() {
202+
base.Update();
203+
204+
if (FramesAlive == FramesMaxAlive)
205+
RemoveSelf();
206+
207+
FramesAlive++;
208+
209+
if (BubbleField.Direction == PushDirection.Up || BubbleField.Direction == PushDirection.Down) {
210+
Position.X = Calc.Approach(Position.X, End.X, BubbleField.Strength / 5);
211+
Position.Y = Calc.Approach(Position.Y, End.Y, BubbleField.Strength * 2);
212+
} else {
213+
Position.X = Calc.Approach(Position.X, End.X, BubbleField.Strength * 2);
214+
Position.Y = Calc.Approach(Position.Y, End.Y, BubbleField.Strength / 5);
215+
}
216+
}
217+
218+
public override void Render() {
219+
Texture.DrawCentered(Position);
220+
}
221+
}
222+
223+
public enum PushDirection {
224+
Left,
225+
Right,
226+
Up,
227+
Down
228+
}
229+
}
132 Bytes
Loading
135 Bytes
Loading

0 commit comments

Comments
 (0)