sonolus.py 0.1.3__py3-none-any.whl → 0.1.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sonolus.py might be problematic. Click here for more details.
- sonolus/backend/allocate.py +125 -51
- sonolus/backend/blocks.py +756 -756
- sonolus/backend/coalesce.py +85 -0
- sonolus/backend/constant_evaluation.py +374 -0
- sonolus/backend/dead_code.py +80 -0
- sonolus/backend/dominance.py +111 -0
- sonolus/backend/excepthook.py +37 -37
- sonolus/backend/finalize.py +69 -69
- sonolus/backend/flow.py +121 -92
- sonolus/backend/inlining.py +150 -0
- sonolus/backend/ir.py +5 -3
- sonolus/backend/liveness.py +173 -0
- sonolus/backend/mode.py +24 -24
- sonolus/backend/node.py +40 -40
- sonolus/backend/ops.py +197 -197
- sonolus/backend/optimize.py +37 -9
- sonolus/backend/passes.py +52 -6
- sonolus/backend/simplify.py +47 -30
- sonolus/backend/ssa.py +187 -0
- sonolus/backend/utils.py +48 -48
- sonolus/backend/visitor.py +892 -882
- sonolus/build/cli.py +7 -1
- sonolus/build/compile.py +88 -90
- sonolus/build/level.py +24 -23
- sonolus/build/node.py +43 -43
- sonolus/script/archetype.py +23 -6
- sonolus/script/array.py +2 -2
- sonolus/script/bucket.py +191 -191
- sonolus/script/callbacks.py +127 -127
- sonolus/script/comptime.py +1 -1
- sonolus/script/containers.py +23 -0
- sonolus/script/debug.py +19 -3
- sonolus/script/easing.py +323 -0
- sonolus/script/effect.py +131 -131
- sonolus/script/globals.py +269 -269
- sonolus/script/graphics.py +200 -150
- sonolus/script/instruction.py +151 -151
- sonolus/script/internal/__init__.py +5 -5
- sonolus/script/internal/builtin_impls.py +144 -144
- sonolus/script/internal/context.py +12 -4
- sonolus/script/internal/descriptor.py +17 -17
- sonolus/script/internal/introspection.py +14 -14
- sonolus/script/internal/native.py +40 -38
- sonolus/script/internal/value.py +3 -3
- sonolus/script/interval.py +120 -112
- sonolus/script/iterator.py +214 -214
- sonolus/script/math.py +30 -1
- sonolus/script/num.py +1 -1
- sonolus/script/options.py +191 -191
- sonolus/script/particle.py +157 -157
- sonolus/script/pointer.py +30 -30
- sonolus/script/print.py +81 -81
- sonolus/script/random.py +14 -0
- sonolus/script/range.py +58 -58
- sonolus/script/record.py +3 -3
- sonolus/script/runtime.py +2 -0
- sonolus/script/sprite.py +333 -333
- sonolus/script/text.py +407 -407
- sonolus/script/timing.py +42 -42
- sonolus/script/transform.py +77 -23
- sonolus/script/ui.py +160 -160
- sonolus/script/vec.py +81 -78
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.4.dist-info}/METADATA +1 -1
- sonolus_py-0.1.4.dist-info/RECORD +84 -0
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.4.dist-info}/WHEEL +1 -1
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.4.dist-info}/licenses/LICENSE +21 -21
- sonolus_py-0.1.3.dist-info/RECORD +0 -75
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.4.dist-info}/entry_points.txt +0 -0
sonolus/script/debug.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from collections.abc import Callable
|
|
2
|
+
from contextvars import ContextVar
|
|
2
3
|
from typing import Any, Never
|
|
3
4
|
|
|
4
5
|
from sonolus.backend.flow import cfg_to_mermaid
|
|
5
6
|
from sonolus.backend.mode import Mode
|
|
6
7
|
from sonolus.backend.ops import Op
|
|
8
|
+
from sonolus.backend.passes import CompilerPass, run_passes
|
|
7
9
|
from sonolus.backend.simplify import CoalesceFlow
|
|
8
10
|
from sonolus.script.comptime import Comptime
|
|
9
11
|
from sonolus.script.internal.context import GlobalContextState, ctx, set_ctx
|
|
@@ -12,6 +14,8 @@ from sonolus.script.internal.native import native_function
|
|
|
12
14
|
from sonolus.script.num import Num
|
|
13
15
|
from sonolus.script.values import with_default
|
|
14
16
|
|
|
17
|
+
debug_log_callback = ContextVar[Callable[[Num], None]]("debug_log_callback")
|
|
18
|
+
|
|
15
19
|
|
|
16
20
|
@meta_fn
|
|
17
21
|
def error(message: str | None = None) -> None:
|
|
@@ -26,9 +30,18 @@ def error(message: str | None = None) -> None:
|
|
|
26
30
|
raise RuntimeError(message)
|
|
27
31
|
|
|
28
32
|
|
|
29
|
-
@
|
|
33
|
+
@meta_fn
|
|
30
34
|
def debug_log(value: Num):
|
|
35
|
+
if debug_log_callback.get(None):
|
|
36
|
+
return debug_log_callback.get()(value)
|
|
37
|
+
else:
|
|
38
|
+
return _debug_log(value)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@native_function(Op.DebugLog)
|
|
42
|
+
def _debug_log(value: Num):
|
|
31
43
|
print(f"[DEBUG] {value}")
|
|
44
|
+
return 0
|
|
32
45
|
|
|
33
46
|
|
|
34
47
|
@native_function(Op.DebugPause)
|
|
@@ -62,9 +75,12 @@ def terminate():
|
|
|
62
75
|
raise RuntimeError("Terminated")
|
|
63
76
|
|
|
64
77
|
|
|
65
|
-
def visualize_cfg(fn: Callable[[], Any]) -> str:
|
|
78
|
+
def visualize_cfg(fn: Callable[[], Any], passes: list[CompilerPass] | None = None) -> str:
|
|
66
79
|
from sonolus.build.compile import callback_to_cfg
|
|
67
80
|
|
|
81
|
+
if passes is None:
|
|
82
|
+
passes = [CoalesceFlow()]
|
|
83
|
+
|
|
68
84
|
cfg = callback_to_cfg(GlobalContextState(Mode.PLAY), fn, "")
|
|
69
|
-
cfg =
|
|
85
|
+
cfg = run_passes(cfg, passes)
|
|
70
86
|
return cfg_to_mermaid(cfg)
|
sonolus/script/easing.py
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import math
|
|
2
|
+
|
|
3
|
+
from sonolus.backend.ops import Op
|
|
4
|
+
from sonolus.script.internal.native import native_function
|
|
5
|
+
from sonolus.script.interval import clamp
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@native_function(Op.EaseInBack)
|
|
9
|
+
def ease_in_back(x: float) -> float:
|
|
10
|
+
x = clamp(x, 0, 1)
|
|
11
|
+
c1 = 1.70158
|
|
12
|
+
c3 = c1 + 1
|
|
13
|
+
return c3 * x**3 - c1 * x**2
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@native_function(Op.EaseOutBack)
|
|
17
|
+
def ease_out_back(x: float) -> float:
|
|
18
|
+
x = clamp(x, 0, 1)
|
|
19
|
+
c1 = 1.70158
|
|
20
|
+
c3 = c1 + 1
|
|
21
|
+
return 1 + c3 * (x - 1) ** 3 + c1 * (x - 1) ** 2
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@native_function(Op.EaseInOutBack)
|
|
25
|
+
def ease_in_out_back(x: float) -> float:
|
|
26
|
+
x = clamp(x, 0, 1)
|
|
27
|
+
c1 = 1.70158
|
|
28
|
+
c2 = c1 * 1.525
|
|
29
|
+
if x < 0.5:
|
|
30
|
+
return ((2 * x) ** 2 * ((c2 + 1) * 2 * x - c2)) / 2
|
|
31
|
+
else:
|
|
32
|
+
return ((2 * x - 2) ** 2 * ((c2 + 1) * (2 * x - 2) + c2) + 2) / 2
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@native_function(Op.EaseInCirc)
|
|
36
|
+
def ease_in_circ(x: float) -> float:
|
|
37
|
+
x = clamp(x, 0, 1)
|
|
38
|
+
return 1 - math.sqrt(1 - x**2)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@native_function(Op.EaseOutCirc)
|
|
42
|
+
def ease_out_circ(x: float) -> float:
|
|
43
|
+
x = clamp(x, 0, 1)
|
|
44
|
+
return math.sqrt(1 - (x - 1) ** 2)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@native_function(Op.EaseInOutCirc)
|
|
48
|
+
def ease_in_out_circ(x: float) -> float:
|
|
49
|
+
x = clamp(x, 0, 1)
|
|
50
|
+
if x < 0.5:
|
|
51
|
+
return (1 - math.sqrt(1 - (2 * x) ** 2)) / 2
|
|
52
|
+
else:
|
|
53
|
+
return (math.sqrt(1 - (2 * x - 2) ** 2) + 1) / 2
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@native_function(Op.EaseInCubic)
|
|
57
|
+
def ease_in_cubic(x: float) -> float:
|
|
58
|
+
x = clamp(x, 0, 1)
|
|
59
|
+
return x**3
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@native_function(Op.EaseOutCubic)
|
|
63
|
+
def ease_out_cubic(x: float) -> float:
|
|
64
|
+
x = clamp(x, 0, 1)
|
|
65
|
+
return 1 - (1 - x) ** 3
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@native_function(Op.EaseInOutCubic)
|
|
69
|
+
def ease_in_out_cubic(x: float) -> float:
|
|
70
|
+
x = clamp(x, 0, 1)
|
|
71
|
+
if x < 0.5:
|
|
72
|
+
return 4 * x**3
|
|
73
|
+
else:
|
|
74
|
+
return 1 - (-2 * x + 2) ** 3 / 2
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@native_function(Op.EaseInElastic)
|
|
78
|
+
def ease_in_elastic(x: float) -> float:
|
|
79
|
+
x = clamp(x, 0, 1)
|
|
80
|
+
c4 = (2 * math.pi) / 3
|
|
81
|
+
if x in {0, 1}:
|
|
82
|
+
return x
|
|
83
|
+
else:
|
|
84
|
+
return -(2 ** (10 * x - 10)) * math.sin((x * 10 - 10.75) * c4)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@native_function(Op.EaseOutElastic)
|
|
88
|
+
def ease_out_elastic(x: float) -> float:
|
|
89
|
+
x = clamp(x, 0, 1)
|
|
90
|
+
c4 = (2 * math.pi) / 3
|
|
91
|
+
if x in {0, 1}:
|
|
92
|
+
return x
|
|
93
|
+
else:
|
|
94
|
+
return 2 ** (-10 * x) * math.sin((x * 10 - 0.75) * c4) + 1
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@native_function(Op.EaseInOutElastic)
|
|
98
|
+
def ease_in_out_elastic(x: float) -> float:
|
|
99
|
+
x = clamp(x, 0, 1)
|
|
100
|
+
c5 = (2 * math.pi) / 4.5
|
|
101
|
+
if x in {0, 1}:
|
|
102
|
+
return x
|
|
103
|
+
elif x < 0.5:
|
|
104
|
+
return -(2 ** (20 * x - 10) * math.sin((20 * x - 11.125) * c5)) / 2
|
|
105
|
+
else:
|
|
106
|
+
return (2 ** (-20 * x + 10) * math.sin((20 * x - 11.125) * c5)) / 2 + 1
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@native_function(Op.EaseInExpo)
|
|
110
|
+
def ease_in_expo(x: float) -> float:
|
|
111
|
+
x = clamp(x, 0, 1)
|
|
112
|
+
return 0 if x == 0 else 2 ** (10 * x - 10)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@native_function(Op.EaseOutExpo)
|
|
116
|
+
def ease_out_expo(x: float) -> float:
|
|
117
|
+
x = clamp(x, 0, 1)
|
|
118
|
+
return 1 if x == 1 else 1 - 2 ** (-10 * x)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@native_function(Op.EaseInOutExpo)
|
|
122
|
+
def ease_in_out_expo(x: float) -> float:
|
|
123
|
+
x = clamp(x, 0, 1)
|
|
124
|
+
if x in {0, 1}:
|
|
125
|
+
return x
|
|
126
|
+
elif x < 0.5:
|
|
127
|
+
return 2 ** (20 * x - 10) / 2
|
|
128
|
+
else:
|
|
129
|
+
return (2 - 2 ** (-20 * x + 10)) / 2
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@native_function(Op.EaseInOutQuad)
|
|
133
|
+
def ease_in_out_quad(x: float) -> float:
|
|
134
|
+
x = clamp(x, 0, 1)
|
|
135
|
+
if x < 0.5:
|
|
136
|
+
return 2 * x**2
|
|
137
|
+
else:
|
|
138
|
+
return 1 - (-2 * x + 2) ** 2 / 2
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@native_function(Op.EaseInOutQuart)
|
|
142
|
+
def ease_in_out_quart(x: float) -> float:
|
|
143
|
+
x = clamp(x, 0, 1)
|
|
144
|
+
if x < 0.5:
|
|
145
|
+
return 8 * x**4
|
|
146
|
+
else:
|
|
147
|
+
return 1 - (-2 * x + 2) ** 4 / 2
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@native_function(Op.EaseInOutQuint)
|
|
151
|
+
def ease_in_out_quint(x: float) -> float:
|
|
152
|
+
x = clamp(x, 0, 1)
|
|
153
|
+
if x < 0.5:
|
|
154
|
+
return 16 * x**5
|
|
155
|
+
else:
|
|
156
|
+
return 1 - (-2 * x + 2) ** 5 / 2
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@native_function(Op.EaseInOutSine)
|
|
160
|
+
def ease_in_out_sine(x: float) -> float:
|
|
161
|
+
x = clamp(x, 0, 1)
|
|
162
|
+
return -(math.cos(math.pi * x) - 1) / 2
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@native_function(Op.EaseInQuad)
|
|
166
|
+
def ease_in_quad(x: float) -> float:
|
|
167
|
+
x = clamp(x, 0, 1)
|
|
168
|
+
return x**2
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
@native_function(Op.EaseOutQuad)
|
|
172
|
+
def ease_out_quad(x: float) -> float:
|
|
173
|
+
x = clamp(x, 0, 1)
|
|
174
|
+
return 1 - (1 - x) ** 2
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@native_function(Op.EaseInQuart)
|
|
178
|
+
def ease_in_quart(x: float) -> float:
|
|
179
|
+
x = clamp(x, 0, 1)
|
|
180
|
+
return x**4
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@native_function(Op.EaseOutQuart)
|
|
184
|
+
def ease_out_quart(x: float) -> float:
|
|
185
|
+
x = clamp(x, 0, 1)
|
|
186
|
+
return 1 - (1 - x) ** 4
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@native_function(Op.EaseInQuint)
|
|
190
|
+
def ease_in_quint(x: float) -> float:
|
|
191
|
+
x = clamp(x, 0, 1)
|
|
192
|
+
return x**5
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@native_function(Op.EaseOutQuint)
|
|
196
|
+
def ease_out_quint(x: float) -> float:
|
|
197
|
+
x = clamp(x, 0, 1)
|
|
198
|
+
return 1 - (1 - x) ** 5
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@native_function(Op.EaseInSine)
|
|
202
|
+
def ease_in_sine(x: float) -> float:
|
|
203
|
+
x = clamp(x, 0, 1)
|
|
204
|
+
return 1 - math.cos((x * math.pi) / 2)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@native_function(Op.EaseOutSine)
|
|
208
|
+
def ease_out_sine(x: float) -> float:
|
|
209
|
+
x = clamp(x, 0, 1)
|
|
210
|
+
return math.sin((x * math.pi) / 2)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@native_function(Op.EaseOutInBack)
|
|
214
|
+
def ease_out_in_back(x: float) -> float:
|
|
215
|
+
x = clamp(x, 0, 1)
|
|
216
|
+
c1 = 1.70158
|
|
217
|
+
c3 = c1 + 1
|
|
218
|
+
if x < 0.5:
|
|
219
|
+
return (1 + c3 * (2 * x - 1) ** 3 + c1 * (2 * x - 1) ** 2) / 2
|
|
220
|
+
else:
|
|
221
|
+
return (c3 * (2 * x - 1) ** 3 - c1 * (2 * x - 1) ** 2) / 2 + 0.5
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@native_function(Op.EaseOutInCirc)
|
|
225
|
+
def ease_out_in_circ(x: float) -> float:
|
|
226
|
+
x = clamp(x, 0, 1)
|
|
227
|
+
if x < 0.5:
|
|
228
|
+
return (math.sqrt(1 - (2 * x - 1) ** 2)) / 2
|
|
229
|
+
else:
|
|
230
|
+
return (1 - math.sqrt(1 - (2 * x - 1) ** 2)) / 2 + 0.5
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@native_function(Op.EaseOutInCubic)
|
|
234
|
+
def ease_out_in_cubic(x: float) -> float:
|
|
235
|
+
x = clamp(x, 0, 1)
|
|
236
|
+
if x < 0.5:
|
|
237
|
+
return (1 - (1 - 2 * x) ** 3) / 2
|
|
238
|
+
else:
|
|
239
|
+
return ((2 * x - 1) ** 3) / 2 + 0.5
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
@native_function(Op.EaseOutInElastic)
|
|
243
|
+
def ease_out_in_elastic(x: float) -> float:
|
|
244
|
+
x = clamp(x, 0, 1)
|
|
245
|
+
c4 = (2 * math.pi) / 3
|
|
246
|
+
if x < 0.5:
|
|
247
|
+
if x == 0:
|
|
248
|
+
return 0
|
|
249
|
+
else:
|
|
250
|
+
return (2 ** (-20 * x + 10) * math.sin((20 * x - 0.75) * c4)) / 2 + 0.5
|
|
251
|
+
elif x == 1:
|
|
252
|
+
return 1
|
|
253
|
+
else:
|
|
254
|
+
return (-(2 ** (10 * (2 * x - 1) - 10)) * math.sin((20 * x - 10.75) * c4)) / 2 + 0.5
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
@native_function(Op.EaseOutInExpo)
|
|
258
|
+
def ease_out_in_expo(x: float) -> float:
|
|
259
|
+
x = clamp(x, 0, 1)
|
|
260
|
+
if x in {0, 1}:
|
|
261
|
+
return x
|
|
262
|
+
elif x < 0.5:
|
|
263
|
+
return (1 - 2 ** (-20 * x)) / 2
|
|
264
|
+
else:
|
|
265
|
+
return (2 ** (20 * x - 20)) / 2 + 0.5
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
@native_function(Op.EaseOutInQuad)
|
|
269
|
+
def ease_out_in_quad(x: float) -> float:
|
|
270
|
+
x = clamp(x, 0, 1)
|
|
271
|
+
if x < 0.5:
|
|
272
|
+
return (1 - (1 - 2 * x) ** 2) / 2
|
|
273
|
+
else:
|
|
274
|
+
return ((2 * x - 1) ** 2) / 2 + 0.5
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@native_function(Op.EaseOutInQuart)
|
|
278
|
+
def ease_out_in_quart(x: float) -> float:
|
|
279
|
+
x = clamp(x, 0, 1)
|
|
280
|
+
if x < 0.5:
|
|
281
|
+
return (1 - (1 - 2 * x) ** 4) / 2
|
|
282
|
+
else:
|
|
283
|
+
return ((2 * x - 1) ** 4) / 2 + 0.5
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
@native_function(Op.EaseOutInQuint)
|
|
287
|
+
def ease_out_in_quint(x: float) -> float:
|
|
288
|
+
x = clamp(x, 0, 1)
|
|
289
|
+
if x < 0.5:
|
|
290
|
+
return (1 - (1 - 2 * x) ** 5) / 2
|
|
291
|
+
else:
|
|
292
|
+
return ((2 * x - 1) ** 5) / 2 + 0.5
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
@native_function(Op.EaseOutInSine)
|
|
296
|
+
def ease_out_in_sine(x: float) -> float:
|
|
297
|
+
x = clamp(x, 0, 1)
|
|
298
|
+
if x < 0.5:
|
|
299
|
+
return (math.sin(math.pi * x)) / 2
|
|
300
|
+
else:
|
|
301
|
+
return (1 - math.cos(math.pi * x)) / 2
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def linstep(x: float) -> float:
|
|
305
|
+
return clamp(x, 0.0, 1.0)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def smoothstep(x: float) -> float:
|
|
309
|
+
x = clamp(x, 0.0, 1.0)
|
|
310
|
+
return x * x * (3 - 2 * x)
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def smootherstep(x: float) -> float:
|
|
314
|
+
x = clamp(x, 0.0, 1.0)
|
|
315
|
+
return x * x * x * (x * (x * 6 - 15) + 10)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def step_start(x: float) -> float:
|
|
319
|
+
return 1.0 if x >= 0 else 0.0
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def step_end(x: float) -> float:
|
|
323
|
+
return 1.0 if x >= 1 else 0.0
|
sonolus/script/effect.py
CHANGED
|
@@ -1,131 +1,131 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass
|
|
4
|
-
from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
5
|
-
|
|
6
|
-
from sonolus.backend.ops import Op
|
|
7
|
-
from sonolus.script.internal.introspection import get_field_specifiers
|
|
8
|
-
from sonolus.script.internal.native import native_function
|
|
9
|
-
from sonolus.script.record import Record
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class Effect(Record):
|
|
13
|
-
id: int
|
|
14
|
-
|
|
15
|
-
def is_available(self) -> bool:
|
|
16
|
-
return _has_effect_clip(self.id)
|
|
17
|
-
|
|
18
|
-
def play(self, distance: float) -> None:
|
|
19
|
-
_play(self.id, distance)
|
|
20
|
-
|
|
21
|
-
def schedule(self, time: float, distance: float) -> None:
|
|
22
|
-
_play_scheduled(self.id, time, distance)
|
|
23
|
-
|
|
24
|
-
def loop(self) -> LoopedEffectHandle:
|
|
25
|
-
return LoopedEffectHandle(_play_looped(self.id))
|
|
26
|
-
|
|
27
|
-
def schedule_loop(self, start_time: float) -> ScheduledLoopedEffectHandle:
|
|
28
|
-
return ScheduledLoopedEffectHandle(_play_looped_scheduled(self.id, start_time))
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class LoopedEffectHandle(Record):
|
|
32
|
-
id: int
|
|
33
|
-
|
|
34
|
-
def stop(self) -> None:
|
|
35
|
-
_stop_looped(self.id)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class ScheduledLoopedEffectHandle(Record):
|
|
39
|
-
id: int
|
|
40
|
-
|
|
41
|
-
def stop(self, end_time: float) -> None:
|
|
42
|
-
_stop_looped_scheduled(self.id, end_time)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
@native_function(Op.HasEffectClip)
|
|
46
|
-
def _has_effect_clip(effect_id: int) -> bool:
|
|
47
|
-
raise NotImplementedError
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
@native_function(Op.Play)
|
|
51
|
-
def _play(effect_id: int, distance: float) -> None:
|
|
52
|
-
raise NotImplementedError
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
@native_function(Op.PlayLooped)
|
|
56
|
-
def _play_looped(effect_id: int) -> int:
|
|
57
|
-
raise NotImplementedError
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
@native_function(Op.PlayLoopedScheduled)
|
|
61
|
-
def _play_looped_scheduled(effect_id: int, start_time: float) -> int:
|
|
62
|
-
raise NotImplementedError
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
@native_function(Op.PlayScheduled)
|
|
66
|
-
def _play_scheduled(effect_id: int, time: float, distance: float) -> None:
|
|
67
|
-
raise NotImplementedError
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
@native_function(Op.StopLooped)
|
|
71
|
-
def _stop_looped(handle: int) -> None:
|
|
72
|
-
raise NotImplementedError
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
@native_function(Op.StopLoopedScheduled)
|
|
76
|
-
def _stop_looped_scheduled(handle: int, end_time: float) -> None:
|
|
77
|
-
raise NotImplementedError
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
@dataclass
|
|
81
|
-
class EffectInfo:
|
|
82
|
-
name: str
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def effect(name: str) -> Any:
|
|
86
|
-
return EffectInfo(name)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
type Effects = NewType("Effects", Any)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
@dataclass_transform()
|
|
93
|
-
def effects[T](cls: type[T]) -> T | Effects:
|
|
94
|
-
if len(cls.__bases__) != 1:
|
|
95
|
-
raise ValueError("Effects class must not inherit from any class (except object)")
|
|
96
|
-
instance = cls()
|
|
97
|
-
names = []
|
|
98
|
-
for i, (name, annotation) in enumerate(get_field_specifiers(cls).items()):
|
|
99
|
-
if get_origin(annotation) is not Annotated:
|
|
100
|
-
raise TypeError(f"Invalid annotation for effects: {annotation}")
|
|
101
|
-
annotation_type = annotation.__args__[0]
|
|
102
|
-
annotation_values = annotation.__metadata__
|
|
103
|
-
if annotation_type is not Effect:
|
|
104
|
-
raise TypeError(f"Invalid annotation for effects: {annotation}, expected annotation of type Effect")
|
|
105
|
-
if len(annotation_values) != 1 or not isinstance(annotation_values[0], EffectInfo):
|
|
106
|
-
raise TypeError(f"Invalid annotation for effects: {annotation}, expected a single string annotation value")
|
|
107
|
-
effect_name = annotation_values[0].name
|
|
108
|
-
names.append(effect_name)
|
|
109
|
-
setattr(instance, name, Effect(i))
|
|
110
|
-
instance._effects_ = names
|
|
111
|
-
instance._is_comptime_value_ = True
|
|
112
|
-
return instance
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
class StandardEffect:
|
|
116
|
-
MISS = Annotated[Effect, effect("#MISS")]
|
|
117
|
-
PERFECT = Annotated[Effect, effect("#PERFECT")]
|
|
118
|
-
GREAT = Annotated[Effect, effect("#GREAT")]
|
|
119
|
-
GOOD = Annotated[Effect, effect("#GOOD")]
|
|
120
|
-
HOLD = Annotated[Effect, effect("#HOLD")]
|
|
121
|
-
MISS_ALTERNATIVE = Annotated[Effect, effect("#MISS_ALTERNATIVE")]
|
|
122
|
-
PERFECT_ALTERNATIVE = Annotated[Effect, effect("#PERFECT_ALTERNATIVE")]
|
|
123
|
-
GREAT_ALTERNATIVE = Annotated[Effect, effect("#GREAT_ALTERNATIVE")]
|
|
124
|
-
GOOD_ALTERNATIVE = Annotated[Effect, effect("#GOOD_ALTERNATIVE")]
|
|
125
|
-
HOLD_ALTERNATIVE = Annotated[Effect, effect("#HOLD_ALTERNATIVE")]
|
|
126
|
-
STAGE = Annotated[Effect, effect("#STAGE")]
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
@effects
|
|
130
|
-
class EmptyEffects:
|
|
131
|
-
pass
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
5
|
+
|
|
6
|
+
from sonolus.backend.ops import Op
|
|
7
|
+
from sonolus.script.internal.introspection import get_field_specifiers
|
|
8
|
+
from sonolus.script.internal.native import native_function
|
|
9
|
+
from sonolus.script.record import Record
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Effect(Record):
|
|
13
|
+
id: int
|
|
14
|
+
|
|
15
|
+
def is_available(self) -> bool:
|
|
16
|
+
return _has_effect_clip(self.id)
|
|
17
|
+
|
|
18
|
+
def play(self, distance: float) -> None:
|
|
19
|
+
_play(self.id, distance)
|
|
20
|
+
|
|
21
|
+
def schedule(self, time: float, distance: float) -> None:
|
|
22
|
+
_play_scheduled(self.id, time, distance)
|
|
23
|
+
|
|
24
|
+
def loop(self) -> LoopedEffectHandle:
|
|
25
|
+
return LoopedEffectHandle(_play_looped(self.id))
|
|
26
|
+
|
|
27
|
+
def schedule_loop(self, start_time: float) -> ScheduledLoopedEffectHandle:
|
|
28
|
+
return ScheduledLoopedEffectHandle(_play_looped_scheduled(self.id, start_time))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class LoopedEffectHandle(Record):
|
|
32
|
+
id: int
|
|
33
|
+
|
|
34
|
+
def stop(self) -> None:
|
|
35
|
+
_stop_looped(self.id)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ScheduledLoopedEffectHandle(Record):
|
|
39
|
+
id: int
|
|
40
|
+
|
|
41
|
+
def stop(self, end_time: float) -> None:
|
|
42
|
+
_stop_looped_scheduled(self.id, end_time)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@native_function(Op.HasEffectClip)
|
|
46
|
+
def _has_effect_clip(effect_id: int) -> bool:
|
|
47
|
+
raise NotImplementedError
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@native_function(Op.Play)
|
|
51
|
+
def _play(effect_id: int, distance: float) -> None:
|
|
52
|
+
raise NotImplementedError
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@native_function(Op.PlayLooped)
|
|
56
|
+
def _play_looped(effect_id: int) -> int:
|
|
57
|
+
raise NotImplementedError
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@native_function(Op.PlayLoopedScheduled)
|
|
61
|
+
def _play_looped_scheduled(effect_id: int, start_time: float) -> int:
|
|
62
|
+
raise NotImplementedError
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@native_function(Op.PlayScheduled)
|
|
66
|
+
def _play_scheduled(effect_id: int, time: float, distance: float) -> None:
|
|
67
|
+
raise NotImplementedError
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@native_function(Op.StopLooped)
|
|
71
|
+
def _stop_looped(handle: int) -> None:
|
|
72
|
+
raise NotImplementedError
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@native_function(Op.StopLoopedScheduled)
|
|
76
|
+
def _stop_looped_scheduled(handle: int, end_time: float) -> None:
|
|
77
|
+
raise NotImplementedError
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass
|
|
81
|
+
class EffectInfo:
|
|
82
|
+
name: str
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def effect(name: str) -> Any:
|
|
86
|
+
return EffectInfo(name)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
type Effects = NewType("Effects", Any)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass_transform()
|
|
93
|
+
def effects[T](cls: type[T]) -> T | Effects:
|
|
94
|
+
if len(cls.__bases__) != 1:
|
|
95
|
+
raise ValueError("Effects class must not inherit from any class (except object)")
|
|
96
|
+
instance = cls()
|
|
97
|
+
names = []
|
|
98
|
+
for i, (name, annotation) in enumerate(get_field_specifiers(cls).items()):
|
|
99
|
+
if get_origin(annotation) is not Annotated:
|
|
100
|
+
raise TypeError(f"Invalid annotation for effects: {annotation}")
|
|
101
|
+
annotation_type = annotation.__args__[0]
|
|
102
|
+
annotation_values = annotation.__metadata__
|
|
103
|
+
if annotation_type is not Effect:
|
|
104
|
+
raise TypeError(f"Invalid annotation for effects: {annotation}, expected annotation of type Effect")
|
|
105
|
+
if len(annotation_values) != 1 or not isinstance(annotation_values[0], EffectInfo):
|
|
106
|
+
raise TypeError(f"Invalid annotation for effects: {annotation}, expected a single string annotation value")
|
|
107
|
+
effect_name = annotation_values[0].name
|
|
108
|
+
names.append(effect_name)
|
|
109
|
+
setattr(instance, name, Effect(i))
|
|
110
|
+
instance._effects_ = names
|
|
111
|
+
instance._is_comptime_value_ = True
|
|
112
|
+
return instance
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class StandardEffect:
|
|
116
|
+
MISS = Annotated[Effect, effect("#MISS")]
|
|
117
|
+
PERFECT = Annotated[Effect, effect("#PERFECT")]
|
|
118
|
+
GREAT = Annotated[Effect, effect("#GREAT")]
|
|
119
|
+
GOOD = Annotated[Effect, effect("#GOOD")]
|
|
120
|
+
HOLD = Annotated[Effect, effect("#HOLD")]
|
|
121
|
+
MISS_ALTERNATIVE = Annotated[Effect, effect("#MISS_ALTERNATIVE")]
|
|
122
|
+
PERFECT_ALTERNATIVE = Annotated[Effect, effect("#PERFECT_ALTERNATIVE")]
|
|
123
|
+
GREAT_ALTERNATIVE = Annotated[Effect, effect("#GREAT_ALTERNATIVE")]
|
|
124
|
+
GOOD_ALTERNATIVE = Annotated[Effect, effect("#GOOD_ALTERNATIVE")]
|
|
125
|
+
HOLD_ALTERNATIVE = Annotated[Effect, effect("#HOLD_ALTERNATIVE")]
|
|
126
|
+
STAGE = Annotated[Effect, effect("#STAGE")]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@effects
|
|
130
|
+
class EmptyEffects:
|
|
131
|
+
pass
|