diff --git a/Code/EeveeHelper.csproj b/Code/EeveeHelper.csproj index d7f47eb..d7efb1d 100644 --- a/Code/EeveeHelper.csproj +++ b/Code/EeveeHelper.csproj @@ -1,7 +1,7 @@  - net7.0 + net8.0 EeveeHelper Celeste.Mod.EeveeHelper latest diff --git a/Code/EeveeHelperModule.cs b/Code/EeveeHelperModule.cs index e7a7439..892a32f 100644 --- a/Code/EeveeHelperModule.cs +++ b/Code/EeveeHelperModule.cs @@ -37,7 +37,7 @@ public override void Load() PatientBooster.Load(); CoreZone.Load(); - Everest.Events.Level.OnLoadBackdrop += this.OnLoadBackdrop; + Everest.Events.Level.OnLoadBackdrop += OnLoadBackdrop; EntityHandler.RegisterInherited((entity, container) => new WaterHandler(entity)); EntityHandler.RegisterInherited((entity, container) => new TrackSpinnerHandler(entity)); @@ -64,6 +64,8 @@ public override void Unload() HoldableTiles.Unload(); PatientBooster.Unload(); CoreZone.Unload(); + + Everest.Events.Level.OnLoadBackdrop -= OnLoadBackdrop; } public override void Initialize() @@ -105,12 +107,11 @@ public override void Initialize() } } - private Backdrop OnLoadBackdrop(MapData map, BinaryPacker.Element child, BinaryPacker.Element above) + private static Backdrop OnLoadBackdrop(MapData map, BinaryPacker.Element child, BinaryPacker.Element above) { if (child.Name.Equals("EeveeHelper/SeededStarfield", StringComparison.OrdinalIgnoreCase)) - { - return new SeededStarfield(Calc.HexToColor(child.Attr("color")), child.AttrFloat("speed", 1f), child.AttrInt("seed")); - } + return new SeededStarfield(Calc.HexToColor(child.Attr("color", "ffffff")) * child.AttrFloat("alpha", 1f), child.AttrFloat("speed", 1f), child.AttrInt("seed", 0), child.Attr("textureDir", "particles/starfield")); + return null; } diff --git a/Code/Effects/SeededStarfield.cs b/Code/Effects/SeededStarfield.cs index 8baa0f5..a67e639 100644 --- a/Code/Effects/SeededStarfield.cs +++ b/Code/Effects/SeededStarfield.cs @@ -4,9 +4,10 @@ using System.Collections.Generic; namespace Celeste.Mod.EeveeHelper.Effects; + public class SeededStarfield : Backdrop { - public struct Star + private struct Star { public MTexture Texture; public Vector2 Position; @@ -17,15 +18,16 @@ public struct Star public float Sine; } - public const int StepSize = 32; - public const int Steps = 15; - public const float MinDist = 4f; - public const float MaxDist = 24f; - public float FlowSpeed; - public List YNodes = new(); - public Star[] Stars = new Star[128]; + private const int NodeDistance = 32; + private const int NodeCount = 15; + private const float MinDist = 4f; + private const float MaxDist = 24f; + + private readonly float FlowSpeed; + private readonly List YNodes = []; + private readonly Star[] Stars = new Star[128]; - public SeededStarfield(Color color, float speed = 1f, int seed = 0) + public SeededStarfield(Color color, float speed = 1f, int seed = 0, string textureDir = "particles/starfield") { Color = color; FlowSpeed = speed; @@ -35,31 +37,30 @@ public SeededStarfield(Color color, float speed = 1f, int seed = 0) Calc.PushRandom(seed); } - var num = Calc.Random.NextFloat(180f); - var num2 = 0; - while (num2 < 15) + float starY = Calc.Random.NextFloat(180f); + for (int i = 0; i < NodeCount; i++) { - YNodes.Add(num); - num2++; - num += Calc.Random.Choose(-1, 1) * (16f + Calc.Random.NextFloat(24f)); + YNodes.Add(starY); + starY += Calc.Random.Choose(-1, 1) * (16f + Calc.Random.NextFloat(24f)); } - for (var i = 0; i < 4; i++) + + for (int i = 0; i < 4; i++) { YNodes[YNodes.Count - 1 - i] = Calc.LerpClamp(YNodes[YNodes.Count - 1 - i], YNodes[0], 1f - i / 4f); } - var atlasSubtextures = GFX.Game.GetAtlasSubtextures("particles/starfield/"); - for (var j = 0; j < Stars.Length; j++) + var atlasSubtextures = GFX.Game.GetAtlasSubtextures(textureDir + "/"); + for (int i = 0; i < Stars.Length; i++) { - var num3 = Calc.Random.NextFloat(1f); - Stars[j].NodeIndex = Calc.Random.Next(YNodes.Count - 1); - Stars[j].NodePercent = Calc.Random.NextFloat(1f); - Stars[j].Distance = 4f + num3 * 20f; - Stars[j].Sine = Calc.Random.NextFloat((float)Math.PI * 2f); - Stars[j].Position = GetTargetOfStar(ref Stars[j]); - Stars[j].Color = Color.Lerp(Color, Color.Transparent, num3 * 0.5f); - var index = (int)Calc.Clamp(Ease.CubeIn(1f - num3) * atlasSubtextures.Count, 0f, atlasSubtextures.Count - 1); - Stars[j].Texture = atlasSubtextures[index]; + float smallness = Calc.Random.NextFloat(1f); + Stars[i].NodeIndex = Calc.Random.Next(YNodes.Count - 1); + Stars[i].NodePercent = Calc.Random.NextFloat(1f); + Stars[i].Distance = MinDist + smallness * (MaxDist - MinDist); + Stars[i].Sine = Calc.Random.NextFloat((float)Math.PI * 2f); + Stars[i].Position = GetTargetOfStar(ref Stars[i]); + Stars[i].Color = Color.Lerp(Color, Color.Transparent, smallness * 0.5f); + int textureIndex = (int)Calc.Clamp(Ease.CubeIn(1f - smallness) * atlasSubtextures.Count, 0f, atlasSubtextures.Count - 1); + Stars[i].Texture = atlasSubtextures[textureIndex]; } if (seed != 0) @@ -71,7 +72,7 @@ public SeededStarfield(Color color, float speed = 1f, int seed = 0) public override void Update(Scene scene) { base.Update(scene); - for (var i = 0; i < Stars.Length; i++) + for (int i = 0; i < Stars.Length; i++) { UpdateStar(ref Stars[i]); } @@ -88,7 +89,7 @@ private void UpdateStar(ref Star star) if (star.NodeIndex >= YNodes.Count - 1) { star.NodeIndex = 0; - star.Position.X -= 448f; + star.Position.X -= (NodeCount - 1) * NodeDistance; } } star.Position += (GetTargetOfStar(ref star) - star.Position) / 50f; @@ -96,29 +97,28 @@ private void UpdateStar(ref Star star) private Vector2 GetTargetOfStar(ref Star star) { - var vector = new Vector2(star.NodeIndex * 32, YNodes[star.NodeIndex]); - var value = new Vector2((star.NodeIndex + 1) * 32, YNodes[star.NodeIndex + 1]); - var value2 = vector + (value - vector) * star.NodePercent; - var vector2 = (value - vector).SafeNormalize(); - var value3 = new Vector2(0f - vector2.Y, vector2.X); - return value2 + value3 * star.Distance * (float)Math.Sin(star.Sine); + var startNode = new Vector2(star.NodeIndex * NodeDistance, YNodes[star.NodeIndex]); + var endNode = new Vector2((star.NodeIndex + 1) * NodeDistance, YNodes[star.NodeIndex + 1]); + var lerpedNode = startNode + (endNode - startNode) * star.NodePercent; + var direction = (endNode - startNode).SafeNormalize(); + var perpendicular = new Vector2(0f - direction.Y, direction.X); + return lerpedNode + perpendicular * star.Distance * (float)Math.Sin(star.Sine); } public override void Render(Scene scene) { var position = (scene as Level).Camera.Position; - for (var i = 0; i < Stars.Length; i++) + for (int i = 0; i < Stars.Length; i++) { - var vector = new Vector2(); - vector.X = -64f + Mod(Stars[i].Position.X - position.X * Scroll.X, 448f); - vector.Y = -16f + Mod(Stars[i].Position.Y - position.Y * Scroll.Y, 212f); - var position2 = vector; - Stars[i].Texture.DrawCentered(position2, Stars[i].Color * FadeAlphaMultiplier); + var renderPosition = new Vector2() + { + X = -64f + Mod(Stars[i].Position.X - position.X * Scroll.X, (NodeCount - 1) * NodeDistance), + Y = -16f + Mod(Stars[i].Position.Y - position.Y * Scroll.Y, 180f + 32f) + }; + + Stars[i].Texture.DrawCentered(renderPosition, Stars[i].Color * FadeAlphaMultiplier); } } - private float Mod(float x, float m) - { - return (x % m + m) % m; - } + private static float Mod(float x, float m) => (x % m + m) % m; } diff --git a/Loenn/effects/seededStarfield.lua b/Loenn/effects/seededStarfield.lua new file mode 100644 index 0000000..f96b670 --- /dev/null +++ b/Loenn/effects/seededStarfield.lua @@ -0,0 +1,35 @@ +local seededStarfield = {} + +seededStarfield.name = "EeveeHelper/SeededStarfield" + +seededStarfield.defaultData = { + textureDir = "particles/starfield", + color = "ffffff", + alpha = 1.0, + scrollx = 1.0, + scrolly = 1.0, + speed = 1.0, + seed = 0 +} + +seededStarfield.fieldOrder = { + "only", "exclude", "tag", "flag", + "textureDir", "color", "alpha", "notflag", + "seed", "speed", "scrollx", "scrolly" +} + +seededStarfield.fieldInformation = { + color = { + fieldType = "color", + allowEmpty = true + }, + alpha = { + minimumValue = 0.0, + maximumValue = 1.0 + }, + seed = { + fieldType = "integer" + } +} + +return seededStarfield \ No newline at end of file diff --git a/Loenn/lang/en_gb.lang b/Loenn/lang/en_gb.lang index c349acd..3cdfc23 100644 --- a/Loenn/lang/en_gb.lang +++ b/Loenn/lang/en_gb.lang @@ -287,3 +287,15 @@ entities.EeveeHelper/RefillShard.attributes.description.spawnRefill=Whether the entities.EeveeHelper/RefillShard.attributes.description.oneUse=Whether the shards respawn after collection / Whether the spawned refill is single-use. entities.EeveeHelper/RefillShard.attributes.description.resetOnGround=Whether touching the ground causes the shards to reset (normal seed behavior). entities.EeveeHelper/RefillShard.attributes.description.twoDashes=Whether the refill gives you two dashes. + +# Seeded Starfield +style.effects.EeveeHelper/SeededStarfield.name=Seeded Starfield +style.effects.EeveeHelper/SeededStarfield.attribute.scrollx=Scroll X +style.effects.EeveeHelper/SeededStarfield.attribute.scrolly=Scroll Y +style.effects.EeveeHelper/SeededStarfield.description.color=The color of the stars. +style.effects.EeveeHelper/SeededStarfield.description.alpha=The alpha of the stars. +style.effects.EeveeHelper/SeededStarfield.description.textureDir=The path to the folder containing the star textures, relative to Graphics/Atlases/Gameplay/. +style.effects.EeveeHelper/SeededStarfield.description.scrollx=Determines the parallax scrolling of the stars along the X axis. +style.effects.EeveeHelper/SeededStarfield.description.scrolly=Determines the parallax scrolling of the stars along the Y axis. +style.effects.EeveeHelper/SeededStarfield.description.speed=A multiplier to apply to the speed of the stars. +style.effects.EeveeHelper/SeededStarfield.description.seed=The random seed used to determine the initial appearance of the starfield.\nSetting this to 0 will cause the starfield to be different every time the map is loaded. \ No newline at end of file