Skip to content

Commit df16dd8

Browse files
authored
Merge pull request #128 from EverestAPI/custom_gradient_spinners
Rainbow Spinner Color Controller
2 parents eaf7057 + 48c1897 commit df16dd8

File tree

3 files changed

+155
-0
lines changed

3 files changed

+155
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module SpringCollab2020RainbowSpinnerColorController
2+
3+
using ..Ahorn, Maple
4+
5+
@mapdef Entity "SpringCollab2020/RainbowSpinnerColorController" RainbowSpinnerColorController(x::Integer, y::Integer,
6+
colors::String="89E5AE,88E0E0,87A9DD,9887DB,D088E2", gradientSize::Number=280.0)
7+
8+
const placements = Ahorn.PlacementDict(
9+
"Rainbow Spinner Colour Controller (Spring Collab 2020)" => Ahorn.EntityPlacement(
10+
RainbowSpinnerColorController
11+
)
12+
)
13+
14+
function Ahorn.selection(entity::RainbowSpinnerColorController)
15+
x, y = Ahorn.position(entity)
16+
17+
return Ahorn.Rectangle(x - 12, y - 12, 24, 24)
18+
end
19+
20+
Ahorn.render(ctx::Ahorn.Cairo.CairoContext, entity::RainbowSpinnerColorController, room::Maple.Room) = Ahorn.drawImage(ctx, Ahorn.Assets.northernLights, -12, -12)
21+
22+
end

Ahorn/lang/en_gb.lang

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,3 +179,7 @@ placements.entities.SpringCollab2020/MultiRoomStrawberry.tooltips.moon=Makes the
179179

180180
# Madeline Silhouette Trigger
181181
placements.triggers.SpringCollab2020/MadelineSilhouetteTrigger.tooltips.enable=If checked, the trigger will turn Madeline into a silhouette. If unchecked, it will turn Madeline back to normal.
182+
183+
# Rainbow Spinner Color Controller
184+
placements.entities.SpringCollab2020/RainbowSpinnerColorController.tooltips.colors=The colours the rainbow spinners in the room will take, separated by commas. For example, if 3 colours A, B and C are specified, the colours will cycle in the following order: A > B > C > B > A.
185+
placements.entities.SpringCollab2020/RainbowSpinnerColorController.tooltips.gradientSize=The distance required to achieve a complete loop across all colours.
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
using Celeste.Mod.Entities;
2+
using Microsoft.Xna.Framework;
3+
using Monocle;
4+
5+
namespace Celeste.Mod.SpringCollab2020.Entities {
6+
/// <summary>
7+
/// A controller allowing for customization of rainbow spinner colors.
8+
/// </summary>
9+
[CustomEntity("SpringCollab2020/RainbowSpinnerColorController")]
10+
class RainbowSpinnerColorController : Entity {
11+
private static bool rainbowSpinnerHueHooked = false;
12+
13+
// the spinner controller on the current screen.
14+
private static RainbowSpinnerColorController spinnerControllerOnScreen;
15+
16+
// during transitions: the spinner controller on the next screen, and the progress between both screens.
17+
// transitionProgress = -1 means no transition is ongoing.
18+
private static RainbowSpinnerColorController nextSpinnerController;
19+
private static float transitionProgress = -1f;
20+
21+
// the parameters for this spinner controller.
22+
private Color[] colors;
23+
private float gradientSize;
24+
25+
public RainbowSpinnerColorController(EntityData data, Vector2 offset) : base(data.Position + offset) {
26+
// convert the color list to Color objects
27+
string[] colorsAsStrings = data.Attr("colors", "89E5AE,88E0E0,87A9DD,9887DB,D088E2").Split(',');
28+
colors = new Color[colorsAsStrings.Length];
29+
for (int i = 0; i < colors.Length; i++) {
30+
colors[i] = Calc.HexToColor(colorsAsStrings[i]);
31+
}
32+
33+
gradientSize = data.Float("gradientSize", 280);
34+
35+
Add(new TransitionListener {
36+
OnIn = progress => transitionProgress = progress,
37+
OnOut = progress => transitionProgress = progress,
38+
OnInBegin = () => transitionProgress = 0f,
39+
OnInEnd = () => transitionProgress = -1f
40+
});
41+
}
42+
43+
public override void Awake(Scene scene) {
44+
base.Awake(scene);
45+
46+
// this is the controller for the next screen.
47+
nextSpinnerController = this;
48+
49+
// enable the hook on rainbow spinner hue.
50+
if (!rainbowSpinnerHueHooked) {
51+
On.Celeste.CrystalStaticSpinner.GetHue += getRainbowSpinnerHue;
52+
rainbowSpinnerHueHooked = true;
53+
}
54+
}
55+
56+
public override void Removed(Scene scene) {
57+
base.Removed(scene);
58+
59+
// the "current" spinner controller is now the one from the next screen.
60+
spinnerControllerOnScreen = nextSpinnerController;
61+
nextSpinnerController = null;
62+
63+
// the transition (if any) is over.
64+
transitionProgress = -1f;
65+
66+
// if there is none, clean up the hook on the spinner hue.
67+
if (spinnerControllerOnScreen == null && rainbowSpinnerHueHooked) {
68+
On.Celeste.CrystalStaticSpinner.GetHue -= getRainbowSpinnerHue;
69+
rainbowSpinnerHueHooked = false;
70+
}
71+
}
72+
73+
public override void SceneEnd(Scene scene) {
74+
base.SceneEnd(scene);
75+
76+
// leaving level: forget about all controllers and clean up the hook if present.
77+
spinnerControllerOnScreen = null;
78+
nextSpinnerController = null;
79+
if (rainbowSpinnerHueHooked) {
80+
On.Celeste.CrystalStaticSpinner.GetHue -= getRainbowSpinnerHue;
81+
rainbowSpinnerHueHooked = false;
82+
};
83+
}
84+
85+
private static Color getRainbowSpinnerHue(On.Celeste.CrystalStaticSpinner.orig_GetHue orig, CrystalStaticSpinner self, Vector2 position) {
86+
if (transitionProgress == -1f) {
87+
// no transition is ongoing.
88+
// if only nextSpinnerController is defined, move it into spinnerControllerOnScreen.
89+
if (spinnerControllerOnScreen == null) {
90+
spinnerControllerOnScreen = nextSpinnerController;
91+
nextSpinnerController = null;
92+
}
93+
94+
return getModHue(spinnerControllerOnScreen.colors, spinnerControllerOnScreen.gradientSize, self.Scene, self.Position);
95+
} else {
96+
// get the spinner color in the room we're coming from.
97+
Color fromRoomColor;
98+
if (spinnerControllerOnScreen != null) {
99+
fromRoomColor = getModHue(spinnerControllerOnScreen.colors, spinnerControllerOnScreen.gradientSize, self.Scene, self.Position);
100+
} else {
101+
fromRoomColor = orig(self, position);
102+
}
103+
104+
// get the spinner color in the room we're going to.
105+
Color toRoomColor;
106+
if (nextSpinnerController != null) {
107+
toRoomColor = getModHue(nextSpinnerController.colors, nextSpinnerController.gradientSize, self.Scene, self.Position);
108+
} else {
109+
toRoomColor = orig(self, position);
110+
}
111+
112+
// transition smoothly between both.
113+
return Color.Lerp(fromRoomColor, toRoomColor, transitionProgress);
114+
}
115+
}
116+
117+
private static Color getModHue(Color[] colors, float gradientSize, Scene scene, Vector2 position) {
118+
float progress = Calc.YoYo((position.Length() + scene.TimeActive * 50f) % gradientSize / gradientSize);
119+
if (progress == 1) {
120+
return colors[colors.Length - 1];
121+
}
122+
123+
float globalProgress = (colors.Length - 1) * progress;
124+
int colorIndex = (int) globalProgress;
125+
float progressInIndex = globalProgress - colorIndex;
126+
return Color.Lerp(colors[colorIndex], colors[colorIndex + 1], progressInIndex);
127+
}
128+
}
129+
}

0 commit comments

Comments
 (0)