Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions Ahorn/entities/bubblePushField.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
module SpringCollab2020BubblePushField

using ..Ahorn, Maple

@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)

const placements = Ahorn.PlacementDict(
"Bubble Column (Spring Collab 2020)" => Ahorn.EntityPlacement(
BubblePushField,
"rectangle"
)
)

Ahorn.editingOptions(entity::BubblePushField) = Dict{String,Any}(
"direction" => ["Up", "Down", "Left", "Right"]
)

Ahorn.minimumSize(entity::BubblePushField) = 8, 8
Ahorn.resizable(entity::BubblePushField) = true, true

Ahorn.selection(entity::BubblePushField) = Ahorn.getEntityRectangle(entity)

function Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::BubblePushField, room::Maple.Room)
x = Int(get(entity.data, "x", 0))
y = Int(get(entity.data, "y", 0))

width = Int(get(entity.data, "width", 32))
height = Int(get(entity.data, "height", 32))

Ahorn.drawRectangle(ctx, 0, 0, width, height, (0.7, 0.28, 0.0, 0.34), (1.0, 1.0, 1.0, 0.5))
end

end
8 changes: 7 additions & 1 deletion Ahorn/lang/en_gb.lang
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,15 @@ placements.entities.SpringCollab2020/dashSpring.tooltips.playerCanUse=Determines
placements.entities.SpringCollab2020/CustomizableGlassBlockController.tooltips.starColors=A comma-separated list of all star colours visible in glass blocks in the room.
placements.entities.SpringCollab2020/CustomizableGlassBlockController.tooltips.bgColor=The background colour for glass blocks in the room.

# Bubble Push Field
placements.entities.SpringCollab2020/bubblePushField.tooltips.strength=The strength with which the bubbles push objects within.
placements.entities.SpringCollab2020/bubblePushField.tooltips.upwardStrength=How much the bubbles push the player up to compensate for gravity.
placements.entities.SpringCollab2020/bubblePushField.tooltips.direction=The direction the bubbles push the player.
placements.entities.SpringCollab2020/bubblePushField.tooltips.water=If the bubble field will create a Water entity to make its presence visible.

# Upside Down Jump Thru
placements.entities.SpringCollab2020/UpsideDownJumpThru.tooltips.texture=Changes the appearance of the platform.

# Sideways Jump Thru
placements.entities.SpringCollab2020/SidewaysJumpThru.tooltips.texture=Changes the appearance of the platform.
placements.entities.SpringCollab2020/SidewaysJumpThru.tooltips.left=Whether the solid side of the jumpthru is the left side.
placements.entities.SpringCollab2020/SidewaysJumpThru.tooltips.left=Whether the solid side of the jumpthru is the left side.
225 changes: 225 additions & 0 deletions Entities/BubblePushField.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
using Microsoft.Xna.Framework;
using Celeste.Mod.Entities;
using System;
using System.Collections.Generic;
using Monocle;

namespace Celeste.Mod.SpringCollab2020.Entities {
[CustomEntity("SpringCollab2020/bubblePushField")]
class BubblePushField : Entity {
public float Strength;

public float UpwardStrength;

private Dictionary<WindMover, float> WindMovers = new Dictionary<WindMover, float>();

private HitboxlessWater Water;

private bool _water;

// This is public so that bubble particles can use it so that their positions actually change.
public Random Rand;

private int FramesSinceSpawn = 0;

private int SpawnFrame = 30;

public PushDirection Direction;

public BubblePushField(EntityData data, Vector2 offset) : this(
data.Position + offset,
data.Width,
data.Height,
data.Float("strength", 1f),
data.Float("upwardStrength", 1f),
data.Attr("direction", "right"),
data.Bool("water", true)
) { }

public BubblePushField(Vector2 position, int width, int height, float strength, float upwardStrength, string direction, bool water) {
Position = position;
Strength = strength;
UpwardStrength = upwardStrength;
_water = water;

Rand = new Random();

Enum.TryParse(direction, out Direction);

Collider = new Hitbox(width, height);
}

public override void Added(Scene scene) {
base.Added(scene);

if (_water)
scene.Add(Water = new HitboxlessWater(Position, true, true, Width, Height));
}

public override void Removed(Scene scene) {
base.Removed(scene);

if (_water)
scene.Remove(Water);
}

public override void Render() {
base.Render();
}

public override void Update() {
base.Update();

FramesSinceSpawn++;
if (FramesSinceSpawn == SpawnFrame) {
FramesSinceSpawn = 0;
SpawnFrame = Rand.Next(2, 10);
Add(new BubbleParticle(true, true));
}

foreach (WindMover mover in Scene.Tracker.GetComponents<WindMover>()) {
if(mover.Entity.CollideCheck(this)) {
if (WindMovers.ContainsKey(mover))
WindMovers[mover] = Calc.Approach(WindMovers[mover], Strength, Engine.DeltaTime / .6f);
else
WindMovers.Add(mover, 0f);
} else {
if (WindMovers.ContainsKey(mover)) {
WindMovers[mover] = Calc.Approach(WindMovers[mover], 0f, Engine.DeltaTime / 0.3f);
if (WindMovers[mover] == 0f)
WindMovers.Remove(mover);
}
}
}

foreach (WindMover mover in WindMovers.Keys) {
float windSpeed = Strength * 2f * Ease.CubeInOut(WindMovers[mover]);

if (mover != null && mover.Entity != null && mover.Entity.Scene != null)
switch (Direction) {
case PushDirection.Up:
mover.Move(new Vector2(0, -windSpeed));
break;

case PushDirection.Down:
mover.Move(new Vector2(0, windSpeed));
break;

case PushDirection.Left:
mover.Move(new Vector2(-windSpeed, 0));
break;

case PushDirection.Right:
mover.Move(new Vector2(windSpeed, 0));
break;
}

if (mover.Entity is Player && mover.Entity.CollideCheck(this) && Strength > 0 && Direction != PushDirection.Down) {
Player tempPlayer = (Player) mover.Entity;
if (tempPlayer.Holding != null)
return;

mover.Move(new Vector2(0f, -UpwardStrength));
}

if (mover.Entity is Glider && Strength > 0 && Direction != PushDirection.Down) {
mover.Move(new Vector2(0f, -UpwardStrength / 20));
}
}
}
}

class HitboxlessWater : Water {
public HitboxlessWater(Vector2 position, bool topSurface, bool bottomSurface, float width, float height)
: base(position, topSurface, bottomSurface, width, height) {
Collidable = false;
}
}

class BubbleParticle : Component {
public Vector2 Position = Vector2.Zero;

public BubblePushField BubbleField;

public MTexture Texture;

private Random Rand;

private int FramesAlive = 0;

private int FramesMaxAlive;

private Vector2 Origin, End;

private static readonly string[] TextureNames = new string[] { "a", "b" };

public BubbleParticle(bool active, bool visible) : base(active, visible) { }

public override void Added(Entity entity) {
base.Added(entity);

BubbleField = (BubblePushField) entity;
Position = BubbleField.Position;

Rand = BubbleField.Rand;
Texture = GFX.Game["particles/SpringCollab2020/bubble_" + TextureNames[Rand.Next(0, 1)]];

// Determine bubble spawn point
switch (BubbleField.Direction) {
case PushDirection.Up:
Origin = new Vector2(Rand.Range(BubbleField.BottomLeft.X, BubbleField.BottomRight.X), BubbleField.BottomCenter.Y);
End = new Vector2(Rand.Range(BubbleField.TopLeft.X, BubbleField.TopRight.X), BubbleField.TopCenter.Y);
FramesMaxAlive = (int) Rand.Range(20, BubbleField.Height / BubbleField.Strength * .5f);
break;

case PushDirection.Down:
Origin = new Vector2(Rand.Range(BubbleField.TopLeft.X, BubbleField.TopRight.X), BubbleField.TopCenter.Y);
End = new Vector2(Rand.Range(BubbleField.BottomLeft.X, BubbleField.BottomRight.X), BubbleField.BottomCenter.Y);
FramesMaxAlive = (int) Rand.Range(20, BubbleField.Height / BubbleField.Strength * .5f);
break;

case PushDirection.Right:
Origin = new Vector2(BubbleField.CenterLeft.X, Rand.Range(BubbleField.BottomLeft.Y, BubbleField.TopLeft.Y));
End = new Vector2(BubbleField.CenterRight.X, Rand.Range(BubbleField.BottomRight.Y, BubbleField.TopRight.Y));
FramesMaxAlive = (int) Rand.Range(20, BubbleField.Width / BubbleField.Strength * .5f);
break;

case PushDirection.Left:
Origin = new Vector2(BubbleField.CenterRight.X, Rand.Range(BubbleField.BottomRight.Y, BubbleField.TopRight.Y));
End = new Vector2(BubbleField.CenterLeft.X, Rand.Range(BubbleField.BottomLeft.Y, BubbleField.TopLeft.Y));
FramesMaxAlive = (int) Rand.Range(20, BubbleField.Height / BubbleField.Strength * .5f);
break;
}

Position = Origin;
}

public override void Update() {
base.Update();

if (FramesAlive == FramesMaxAlive)
RemoveSelf();

FramesAlive++;

if (BubbleField.Direction == PushDirection.Up || BubbleField.Direction == PushDirection.Down) {
Position.X = Calc.Approach(Position.X, End.X, BubbleField.Strength / 5);
Position.Y = Calc.Approach(Position.Y, End.Y, BubbleField.Strength * 2);
} else {
Position.X = Calc.Approach(Position.X, End.X, BubbleField.Strength * 2);
Position.Y = Calc.Approach(Position.Y, End.Y, BubbleField.Strength / 5);
}
}

public override void Render() {
Texture.DrawCentered(Position);
}
}

public enum PushDirection {
Left,
Right,
Up,
Down
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.