-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathParticles.cs
137 lines (117 loc) · 6.19 KB
/
Particles.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
using OpenTK;
using OpenTK.Graphics;
using StorybrewCommon.Mapset;
using StorybrewCommon.Scripting;
using StorybrewCommon.Storyboarding;
using System;
using System.Drawing;
using System.Linq;
namespace StorybrewScripts
{
public class Particles : StoryboardObjectGenerator
{
[Group("Timing")]
[Configurable] public int StartTime;
[Configurable] public int EndTime;
[Group("Sprite")]
[Configurable] public string Path = "sb/particle.png";
[Configurable] public OsbOrigin Origin = OsbOrigin.Centre;
[Configurable] public Vector2 Scale = new Vector2(1, 1);
[Description("Rotation of the sprite; does not influences particle motion direction.")]
[Configurable] public float Rotation = 0;
[Configurable] public Color4 Color = Color4.White;
[Description("Varies the saturation and brightness of the selected Color for each particle.")]
[Configurable] public float ColorVariance = 0.6f;
[Configurable] public bool Additive = false;
[Group("Spawn")]
[Configurable] public int ParticleCount = 32;
[Configurable] public float Lifetime = 1000;
[Description("The point around which particles will be created.")]
[Configurable] public Vector2 SpawnOrigin = new Vector2(420, 0);
[Description("The distance around the Spawn Origin point where particles will be created.")]
[Configurable] public float SpawnSpread = 360;
[Group("Motion")]
[Description("The angle in degrees at which particles will be moving.\n0 is to the right, positive values rotate counterclockwise.")]
[Configurable] public float Angle = 110;
[Description("The spread in degrees around Angle.")]
[Configurable] public float AngleSpread = 60;
[Description("The speed at which particles move, in osupixels.")]
[Configurable] public float Speed = 480;
[Description("Eases the motion of particles.")]
[Configurable] public OsbEasing Easing = OsbEasing.None;
public override void Generate()
{
if (StartTime == EndTime && Beatmap.HitObjects.FirstOrDefault() != null)
{
StartTime = (int)Beatmap.HitObjects.First().StartTime;
EndTime = (int)Beatmap.HitObjects.Last().EndTime;
}
EndTime = Math.Min(EndTime, (int)AudioDuration);
StartTime = Math.Min(StartTime, EndTime);
var bitmap = GetMapsetBitmap(Path);
var duration = (double)(EndTime - StartTime);
var loopCount = Math.Max(1, (int)Math.Floor(duration / Lifetime));
var layer = GetLayer("");
for (var i = 0; i < ParticleCount; i++)
{
var spawnAngle = Random(Math.PI * 2);
var spawnDistance = (float)(SpawnSpread * Math.Sqrt(Random(1f)));
var moveAngle = MathHelper.DegreesToRadians(Angle + Random(-AngleSpread, AngleSpread) * 0.5f);
var moveDistance = Speed * Lifetime * 0.001f;
var spriteRotation = moveAngle + MathHelper.DegreesToRadians(Rotation);
var startPosition = SpawnOrigin + new Vector2((float)Math.Cos(spawnAngle), (float)Math.Sin(spawnAngle)) * spawnDistance;
var endPosition = startPosition + new Vector2((float)Math.Cos(moveAngle), (float)Math.Sin(moveAngle)) * moveDistance;
var loopDuration = duration / loopCount;
var startTime = StartTime + (i * loopDuration) / ParticleCount;
var endTime = startTime + loopDuration * loopCount;
if (!isVisible(bitmap, startPosition, endPosition, (float)spriteRotation, (float)loopDuration))
continue;
var color = Color;
if (ColorVariance > 0)
{
ColorVariance = MathHelper.Clamp(ColorVariance, 0, 1);
var hsba = Color4.ToHsl(color);
var sMin = Math.Max(0, hsba.Y - ColorVariance * 0.5f);
var sMax = Math.Min(sMin + ColorVariance, 1);
var vMin = Math.Max(0, hsba.Z - ColorVariance * 0.5f);
var vMax = Math.Min(vMin + ColorVariance, 1);
color = Color4.FromHsl(new Vector4(
hsba.X,
(float)Random(sMin, sMax),
(float)Random(vMin, vMax),
hsba.W));
}
var particle = layer.CreateSprite(Path, Origin);
if (spriteRotation != 0)
particle.Rotate(startTime, spriteRotation);
if (color.R != 1 || color.G != 1 || color.B != 1)
particle.Color(startTime, color);
if (Scale.X != 1 || Scale.Y != 1)
{
if (Scale.X != Scale.Y)
particle.ScaleVec(startTime, Scale.X, Scale.Y);
else particle.Scale(startTime, Scale.X);
}
if (Additive)
particle.Additive(startTime, endTime);
particle.StartLoopGroup(startTime, loopCount);
particle.Fade(OsbEasing.Out, 0, loopDuration * 0.2, 0, color.A);
particle.Fade(OsbEasing.In, loopDuration * 0.8, loopDuration, color.A, 0);
particle.Move(Easing, 0, loopDuration, startPosition, endPosition);
particle.EndGroup();
}
}
private bool isVisible(Bitmap bitmap, Vector2 startPosition, Vector2 endPosition, float rotation, float duration)
{
var spriteSize = new Vector2(bitmap.Width * Scale.X, bitmap.Height * Scale.Y);
var originVector = OsbSprite.GetOriginVector(Origin, spriteSize.X, spriteSize.Y);
for (var t = 0; t < duration; t += 200)
{
var position = Vector2.Lerp(startPosition, endPosition, t / duration);
if (OsbSprite.InScreenBounds(position, spriteSize, rotation, originVector))
return true;
}
return false;
}
}
}