sonolus.py 0.1.3__py3-none-any.whl → 0.1.5__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.

Files changed (90) hide show
  1. sonolus/backend/blocks.py +756 -756
  2. sonolus/backend/excepthook.py +37 -37
  3. sonolus/backend/finalize.py +77 -69
  4. sonolus/backend/interpret.py +7 -7
  5. sonolus/backend/ir.py +29 -3
  6. sonolus/backend/mode.py +24 -24
  7. sonolus/backend/node.py +40 -40
  8. sonolus/backend/ops.py +197 -197
  9. sonolus/backend/optimize/__init__.py +0 -0
  10. sonolus/backend/optimize/allocate.py +126 -0
  11. sonolus/backend/optimize/constant_evaluation.py +374 -0
  12. sonolus/backend/optimize/copy_coalesce.py +85 -0
  13. sonolus/backend/optimize/dead_code.py +185 -0
  14. sonolus/backend/optimize/dominance.py +96 -0
  15. sonolus/backend/{flow.py → optimize/flow.py} +122 -92
  16. sonolus/backend/optimize/inlining.py +137 -0
  17. sonolus/backend/optimize/liveness.py +177 -0
  18. sonolus/backend/optimize/optimize.py +44 -0
  19. sonolus/backend/optimize/passes.py +52 -0
  20. sonolus/backend/optimize/simplify.py +191 -0
  21. sonolus/backend/optimize/ssa.py +200 -0
  22. sonolus/backend/place.py +17 -25
  23. sonolus/backend/utils.py +58 -48
  24. sonolus/backend/visitor.py +1151 -882
  25. sonolus/build/cli.py +7 -1
  26. sonolus/build/compile.py +88 -90
  27. sonolus/build/engine.py +10 -5
  28. sonolus/build/level.py +24 -23
  29. sonolus/build/node.py +43 -43
  30. sonolus/script/archetype.py +438 -139
  31. sonolus/script/array.py +27 -10
  32. sonolus/script/array_like.py +297 -0
  33. sonolus/script/bucket.py +253 -191
  34. sonolus/script/containers.py +257 -51
  35. sonolus/script/debug.py +26 -10
  36. sonolus/script/easing.py +365 -0
  37. sonolus/script/effect.py +191 -131
  38. sonolus/script/engine.py +71 -4
  39. sonolus/script/globals.py +303 -269
  40. sonolus/script/instruction.py +205 -151
  41. sonolus/script/internal/__init__.py +5 -5
  42. sonolus/script/internal/builtin_impls.py +255 -144
  43. sonolus/script/{callbacks.py → internal/callbacks.py} +127 -127
  44. sonolus/script/internal/constant.py +139 -0
  45. sonolus/script/internal/context.py +26 -9
  46. sonolus/script/internal/descriptor.py +17 -17
  47. sonolus/script/internal/dict_impl.py +65 -0
  48. sonolus/script/internal/generic.py +6 -9
  49. sonolus/script/internal/impl.py +38 -13
  50. sonolus/script/internal/introspection.py +17 -14
  51. sonolus/script/internal/math_impls.py +121 -0
  52. sonolus/script/internal/native.py +40 -38
  53. sonolus/script/internal/random.py +67 -0
  54. sonolus/script/internal/range.py +81 -0
  55. sonolus/script/internal/transient.py +51 -0
  56. sonolus/script/internal/tuple_impl.py +113 -0
  57. sonolus/script/internal/value.py +3 -3
  58. sonolus/script/interval.py +338 -112
  59. sonolus/script/iterator.py +167 -214
  60. sonolus/script/level.py +24 -0
  61. sonolus/script/num.py +80 -48
  62. sonolus/script/options.py +257 -191
  63. sonolus/script/particle.py +190 -157
  64. sonolus/script/pointer.py +30 -30
  65. sonolus/script/print.py +102 -81
  66. sonolus/script/project.py +8 -0
  67. sonolus/script/quad.py +263 -0
  68. sonolus/script/record.py +47 -16
  69. sonolus/script/runtime.py +52 -1
  70. sonolus/script/sprite.py +418 -333
  71. sonolus/script/text.py +409 -407
  72. sonolus/script/timing.py +114 -42
  73. sonolus/script/transform.py +332 -48
  74. sonolus/script/ui.py +216 -160
  75. sonolus/script/values.py +6 -13
  76. sonolus/script/vec.py +196 -78
  77. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/METADATA +1 -1
  78. sonolus_py-0.1.5.dist-info/RECORD +89 -0
  79. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/WHEEL +1 -1
  80. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/licenses/LICENSE +21 -21
  81. sonolus/backend/allocate.py +0 -51
  82. sonolus/backend/optimize.py +0 -9
  83. sonolus/backend/passes.py +0 -6
  84. sonolus/backend/simplify.py +0 -30
  85. sonolus/script/comptime.py +0 -160
  86. sonolus/script/graphics.py +0 -150
  87. sonolus/script/math.py +0 -92
  88. sonolus/script/range.py +0 -58
  89. sonolus_py-0.1.3.dist-info/RECORD +0 -75
  90. {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/entry_points.txt +0 -0
sonolus/script/sprite.py CHANGED
@@ -1,333 +1,418 @@
1
- from dataclasses import dataclass
2
- from typing import Annotated, Any, NewType, dataclass_transform, get_origin
3
-
4
- from sonolus.backend.ops import Op
5
- from sonolus.script.graphics import QuadLike, flatten_quad
6
- from sonolus.script.internal.introspection import get_field_specifiers
7
- from sonolus.script.internal.native import native_function
8
- from sonolus.script.record import Record
9
- from sonolus.script.vec import Vec2
10
-
11
-
12
- class Sprite(Record):
13
- id: int
14
-
15
- @property
16
- def is_available(self) -> bool:
17
- return _has_skin_sprite(self.id)
18
-
19
- def draw(self, quad: QuadLike, z: float = 0.0, a: float = 1.0):
20
- _draw(self.id, *flatten_quad(quad), z, a)
21
-
22
- def draw_curved_b(self, quad: QuadLike, cp: Vec2, n: float, z: float = 0.0, a: float = 1.0):
23
- _draw_curved_b(self.id, *flatten_quad(quad), z, a, n, *cp.tuple)
24
-
25
- def draw_curved_t(self, quad: QuadLike, cp: Vec2, n: float, z: float = 0.0, a: float = 1.0):
26
- _draw_curved_t(self.id, *flatten_quad(quad), z, a, n, *cp.tuple)
27
-
28
- def draw_curved_l(self, quad: QuadLike, cp: Vec2, n: float, z: float = 0.0, a: float = 1.0):
29
- _draw_curved_l(self.id, *flatten_quad(quad), z, a, n, *cp.tuple)
30
-
31
- def draw_curved_r(self, quad: QuadLike, cp: Vec2, n: float, z: float = 0.0, a: float = 1.0):
32
- _draw_curved_r(self.id, *flatten_quad(quad), z, a, n, *cp.tuple)
33
-
34
- def draw_curved_bt(self, quad: QuadLike, cp1: Vec2, cp2: Vec2, n: float, z: float = 0.0, a: float = 1.0):
35
- _draw_curved_bt(self.id, *flatten_quad(quad), z, a, n, *cp1.tuple, *cp2.tuple)
36
-
37
- def draw_curved_lr(self, quad: QuadLike, cp1: Vec2, cp2: Vec2, n: float, z: float = 0.0, a: float = 1.0):
38
- _draw_curved_lr(self.id, *flatten_quad(quad), z, a, n, *cp1.tuple, *cp2.tuple)
39
-
40
-
41
- @native_function(Op.HasSkinSprite)
42
- def _has_skin_sprite(sprite_id: int) -> bool:
43
- raise NotImplementedError
44
-
45
-
46
- @native_function(Op.Draw)
47
- def _draw(
48
- sprite_id: int,
49
- x1: float,
50
- y1: float,
51
- x2: float,
52
- y2: float,
53
- x3: float,
54
- y3: float,
55
- x4: float,
56
- y4: float,
57
- z: float,
58
- a: float,
59
- ):
60
- raise NotImplementedError
61
-
62
-
63
- @native_function(Op.DrawCurvedB)
64
- def _draw_curved_b(
65
- sprite_id: int,
66
- x1: float,
67
- y1: float,
68
- x2: float,
69
- y2: float,
70
- x3: float,
71
- y3: float,
72
- x4: float,
73
- y4: float,
74
- z: float,
75
- a: float,
76
- n: int,
77
- p: float,
78
- q: float,
79
- ):
80
- raise NotImplementedError
81
-
82
-
83
- @native_function(Op.DrawCurvedT)
84
- def _draw_curved_t(
85
- sprite_id: int,
86
- x1: float,
87
- y1: float,
88
- x2: float,
89
- y2: float,
90
- x3: float,
91
- y3: float,
92
- x4: float,
93
- y4: float,
94
- z: float,
95
- a: float,
96
- n: int,
97
- p: float,
98
- q: float,
99
- ):
100
- raise NotImplementedError
101
-
102
-
103
- @native_function(Op.DrawCurvedL)
104
- def _draw_curved_l(
105
- sprite_id: int,
106
- x1: float,
107
- y1: float,
108
- x2: float,
109
- y2: float,
110
- x3: float,
111
- y3: float,
112
- x4: float,
113
- y4: float,
114
- z: float,
115
- a: float,
116
- n: int,
117
- p: float,
118
- q: float,
119
- ):
120
- raise NotImplementedError
121
-
122
-
123
- @native_function(Op.DrawCurvedR)
124
- def _draw_curved_r(
125
- sprite_id: int,
126
- x1: float,
127
- y1: float,
128
- x2: float,
129
- y2: float,
130
- x3: float,
131
- y3: float,
132
- x4: float,
133
- y4: float,
134
- z: float,
135
- a: float,
136
- n: int,
137
- p: float,
138
- q: float,
139
- ):
140
- raise NotImplementedError
141
-
142
-
143
- @native_function(Op.DrawCurvedBT)
144
- def _draw_curved_bt(
145
- sprite_id: int,
146
- x1: float,
147
- y1: float,
148
- x2: float,
149
- y2: float,
150
- x3: float,
151
- y3: float,
152
- x4: float,
153
- y4: float,
154
- z: float,
155
- a: float,
156
- n: int,
157
- p1: float,
158
- q1: float,
159
- p2: float,
160
- q2: float,
161
- ):
162
- raise NotImplementedError
163
-
164
-
165
- @native_function(Op.DrawCurvedLR)
166
- def _draw_curved_lr(
167
- sprite_id: int,
168
- x1: float,
169
- y1: float,
170
- x2: float,
171
- y2: float,
172
- x3: float,
173
- y3: float,
174
- x4: float,
175
- y4: float,
176
- z: float,
177
- a: float,
178
- n: int,
179
- p1: float,
180
- q1: float,
181
- p2: float,
182
- q2: float,
183
- ):
184
- raise NotImplementedError
185
-
186
-
187
- @dataclass
188
- class SkinSprite:
189
- name: str
190
-
191
-
192
- def skin_sprite(name: str) -> Any:
193
- return SkinSprite(name)
194
-
195
-
196
- type Skin = NewType("Skin", Any)
197
-
198
-
199
- @dataclass_transform()
200
- def skin[T](cls: type[T]) -> T | Skin:
201
- if len(cls.__bases__) != 1:
202
- raise ValueError("Skin class must not inherit from any class (except object)")
203
- instance = cls()
204
- names = []
205
- for i, (name, annotation) in enumerate(get_field_specifiers(cls).items()):
206
- if get_origin(annotation) is not Annotated:
207
- raise TypeError(f"Invalid annotation for skin: {annotation}")
208
- annotation_type = annotation.__args__[0]
209
- annotation_values = annotation.__metadata__
210
- if annotation_type is not Sprite:
211
- raise TypeError(f"Invalid annotation for skin: {annotation}, expected annotation of type Sprite")
212
- if len(annotation_values) != 1 or not isinstance(annotation_values[0], SkinSprite):
213
- raise TypeError(f"Invalid annotation for skin: {annotation}, expected a single string annotation value")
214
- sprite_name = annotation_values[0].name
215
- names.append(sprite_name)
216
- setattr(instance, name, Sprite(i))
217
- instance._sprites_ = names
218
- instance._is_comptime_value_ = True
219
- return instance
220
-
221
-
222
- class StandardSprite:
223
- NOTE_HEAD_NEUTRAL = Annotated[Sprite, skin_sprite("#NOTE_HEAD_NEUTRAL")]
224
- NOTE_HEAD_RED = Annotated[Sprite, skin_sprite("#NOTE_HEAD_RED")]
225
- NOTE_HEAD_GREEN = Annotated[Sprite, skin_sprite("#NOTE_HEAD_GREEN")]
226
- NOTE_HEAD_BLUE = Annotated[Sprite, skin_sprite("#NOTE_HEAD_BLUE")]
227
- NOTE_HEAD_YELLOW = Annotated[Sprite, skin_sprite("#NOTE_HEAD_YELLOW")]
228
- NOTE_HEAD_PURPLE = Annotated[Sprite, skin_sprite("#NOTE_HEAD_PURPLE")]
229
- NOTE_HEAD_CYAN = Annotated[Sprite, skin_sprite("#NOTE_HEAD_CYAN")]
230
-
231
- NOTE_TICK_NEUTRAL = Annotated[Sprite, skin_sprite("#NOTE_TICK_NEUTRAL")]
232
- NOTE_TICK_RED = Annotated[Sprite, skin_sprite("#NOTE_TICK_RED")]
233
- NOTE_TICK_GREEN = Annotated[Sprite, skin_sprite("#NOTE_TICK_GREEN")]
234
- NOTE_TICK_BLUE = Annotated[Sprite, skin_sprite("#NOTE_TICK_BLUE")]
235
- NOTE_TICK_YELLOW = Annotated[Sprite, skin_sprite("#NOTE_TICK_YELLOW")]
236
- NOTE_TICK_PURPLE = Annotated[Sprite, skin_sprite("#NOTE_TICK_PURPLE")]
237
- NOTE_TICK_CYAN = Annotated[Sprite, skin_sprite("#NOTE_TICK_CYAN")]
238
-
239
- NOTE_TAIL_NEUTRAL = Annotated[Sprite, skin_sprite("#NOTE_TAIL_NEUTRAL")]
240
- NOTE_TAIL_RED = Annotated[Sprite, skin_sprite("#NOTE_TAIL_RED")]
241
- NOTE_TAIL_GREEN = Annotated[Sprite, skin_sprite("#NOTE_TAIL_GREEN")]
242
- NOTE_TAIL_BLUE = Annotated[Sprite, skin_sprite("#NOTE_TAIL_BLUE")]
243
- NOTE_TAIL_YELLOW = Annotated[Sprite, skin_sprite("#NOTE_TAIL_YELLOW")]
244
- NOTE_TAIL_PURPLE = Annotated[Sprite, skin_sprite("#NOTE_TAIL_PURPLE")]
245
- NOTE_TAIL_CYAN = Annotated[Sprite, skin_sprite("#NOTE_TAIL_CYAN")]
246
-
247
- NOTE_CONNECTION_NEUTRAL = Annotated[Sprite, skin_sprite("#NOTE_CONNECTION_NEUTRAL")]
248
- NOTE_CONNECTION_RED = Annotated[Sprite, skin_sprite("#NOTE_CONNECTION_RED")]
249
- NOTE_CONNECTION_GREEN = Annotated[Sprite, skin_sprite("#NOTE_CONNECTION_GREEN")]
250
- NOTE_CONNECTION_BLUE = Annotated[Sprite, skin_sprite("#NOTE_CONNECTION_BLUE")]
251
- NOTE_CONNECTION_YELLOW = Annotated[Sprite, skin_sprite("#NOTE_CONNECTION_YELLOW")]
252
- NOTE_CONNECTION_PURPLE = Annotated[Sprite, skin_sprite("#NOTE_CONNECTION_PURPLE")]
253
- NOTE_CONNECTION_CYAN = Annotated[Sprite, skin_sprite("#NOTE_CONNECTION_CYAN")]
254
-
255
- NOTE_CONNECTION_NEUTRAL_SEAMLESS = Annotated[Sprite, skin_sprite("#NOTE_CONNECTION_NEUTRAL_SEAMLESS")]
256
- NOTE_CONNECTION_RED_SEAMLESS = Annotated[Sprite, skin_sprite("#NOTE_CONNECTION_RED_SEAMLESS")]
257
- NOTE_CONNECTION_GREEN_SEAMLESS = Annotated[Sprite, skin_sprite("#NOTE_CONNECTION_GREEN_SEAMLESS")]
258
- NOTE_CONNECTION_BLUE_SEAMLESS = Annotated[Sprite, skin_sprite("#NOTE_CONNECTION_BLUE_SEAMLESS")]
259
- NOTE_CONNECTION_YELLOW_SEAMLESS = Annotated[Sprite, skin_sprite("#NOTE_CONNECTION_YELLOW_SEAMLESS")]
260
- NOTE_CONNECTION_PURPLE_SEAMLESS = Annotated[Sprite, skin_sprite("#NOTE_CONNECTION_PURPLE_SEAMLESS")]
261
- NOTE_CONNECTION_CYAN_SEAMLESS = Annotated[Sprite, skin_sprite("#NOTE_CONNECTION_CYAN_SEAMLESS")]
262
-
263
- SIMULTANEOUS_CONNECTION_NEUTRAL = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_CONNECTION_NEUTRAL")]
264
- SIMULTANEOUS_CONNECTION_RED = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_CONNECTION_RED")]
265
- SIMULTANEOUS_CONNECTION_GREEN = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_CONNECTION_GREEN")]
266
- SIMULTANEOUS_CONNECTION_BLUE = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_CONNECTION_BLUE")]
267
- SIMULTANEOUS_CONNECTION_YELLOW = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_CONNECTION_YELLOW")]
268
- SIMULTANEOUS_CONNECTION_PURPLE = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_CONNECTION_PURPLE")]
269
- SIMULTANEOUS_CONNECTION_CYAN = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_CONNECTION_CYAN")]
270
-
271
- SIMULTANEOUS_CONNECTION_NEUTRAL_SEAMLESS = Annotated[
272
- Sprite, skin_sprite("#SIMULTANEOUS_CONNECTION_NEUTRAL_SEAMLESS")
273
- ]
274
- SIMULTANEOUS_CONNECTION_RED_SEAMLESS = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_CONNECTION_RED_SEAMLESS")]
275
- SIMULTANEOUS_CONNECTION_GREEN_SEAMLESS = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_CONNECTION_GREEN_SEAMLESS")]
276
- SIMULTANEOUS_CONNECTION_BLUE_SEAMLESS = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_CONNECTION_BLUE_SEAMLESS")]
277
- SIMULTANEOUS_CONNECTION_YELLOW_SEAMLESS = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_CONNECTION_YELLOW_SEAMLESS")]
278
- SIMULTANEOUS_CONNECTION_PURPLE_SEAMLESS = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_CONNECTION_PURPLE_SEAMLESS")]
279
- SIMULTANEOUS_CONNECTION_CYAN_SEAMLESS = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_CONNECTION_CYAN_SEAMLESS")]
280
-
281
- DIRECTIONAL_MARKER_NEUTRAL = Annotated[Sprite, skin_sprite("#DIRECTIONAL_MARKER_NEUTRAL")]
282
- DIRECTIONAL_MARKER_RED = Annotated[Sprite, skin_sprite("#DIRECTIONAL_MARKER_RED")]
283
- DIRECTIONAL_MARKER_GREEN = Annotated[Sprite, skin_sprite("#DIRECTIONAL_MARKER_GREEN")]
284
- DIRECTIONAL_MARKER_BLUE = Annotated[Sprite, skin_sprite("#DIRECTIONAL_MARKER_BLUE")]
285
- DIRECTIONAL_MARKER_YELLOW = Annotated[Sprite, skin_sprite("#DIRECTIONAL_MARKER_YELLOW")]
286
- DIRECTIONAL_MARKER_PURPLE = Annotated[Sprite, skin_sprite("#DIRECTIONAL_MARKER_PURPLE")]
287
- DIRECTIONAL_MARKER_CYAN = Annotated[Sprite, skin_sprite("#DIRECTIONAL_MARKER_CYAN")]
288
-
289
- SIMULTANEOUS_MARKER_NEUTRAL = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_MARKER_NEUTRAL")]
290
- SIMULTANEOUS_MARKER_RED = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_MARKER_RED")]
291
- SIMULTANEOUS_MARKER_GREEN = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_MARKER_GREEN")]
292
- SIMULTANEOUS_MARKER_BLUE = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_MARKER_BLUE")]
293
- SIMULTANEOUS_MARKER_YELLOW = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_MARKER_YELLOW")]
294
- SIMULTANEOUS_MARKER_PURPLE = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_MARKER_PURPLE")]
295
- SIMULTANEOUS_MARKER_CYAN = Annotated[Sprite, skin_sprite("#SIMULTANEOUS_MARKER_CYAN")]
296
-
297
- STAGE_MIDDLE = Annotated[Sprite, skin_sprite("#STAGE_MIDDLE")]
298
- STAGE_LEFT_BORDER = Annotated[Sprite, skin_sprite("#STAGE_LEFT_BORDER")]
299
- STAGE_RIGHT_BORDER = Annotated[Sprite, skin_sprite("#STAGE_RIGHT_BORDER")]
300
- STAGE_TOP_BORDER = Annotated[Sprite, skin_sprite("#STAGE_TOP_BORDER")]
301
- STAGE_BOTTOM_BORDER = Annotated[Sprite, skin_sprite("#STAGE_BOTTOM_BORDER")]
302
-
303
- STAGE_LEFT_BORDER_SEAMLESS = Annotated[Sprite, skin_sprite("#STAGE_LEFT_BORDER_SEAMLESS")]
304
- STAGE_RIGHT_BORDER_SEAMLESS = Annotated[Sprite, skin_sprite("#STAGE_RIGHT_BORDER_SEAMLESS")]
305
- STAGE_TOP_BORDER_SEAMLESS = Annotated[Sprite, skin_sprite("#STAGE_TOP_BORDER_SEAMLESS")]
306
- STAGE_BOTTOM_BORDER_SEAMLESS = Annotated[Sprite, skin_sprite("#STAGE_BOTTOM_BORDER_SEAMLESS")]
307
-
308
- STAGE_TOP_LEFT_CORNER = Annotated[Sprite, skin_sprite("#STAGE_TOP_LEFT_CORNER")]
309
- STAGE_TOP_RIGHT_CORNER = Annotated[Sprite, skin_sprite("#STAGE_TOP_RIGHT_CORNER")]
310
- STAGE_BOTTOM_LEFT_CORNER = Annotated[Sprite, skin_sprite("#STAGE_BOTTOM_LEFT_CORNER")]
311
- STAGE_BOTTOM_RIGHT_CORNER = Annotated[Sprite, skin_sprite("#STAGE_BOTTOM_RIGHT_CORNER")]
312
-
313
- LANE = Annotated[Sprite, skin_sprite("#LANE")]
314
- LANE_SEAMLESS = Annotated[Sprite, skin_sprite("#LANE_SEAMLESS")]
315
- LANE_ALTERNATIVE = Annotated[Sprite, skin_sprite("#LANE_ALTERNATIVE")]
316
- LANE_ALTERNATIVE_SEAMLESS = Annotated[Sprite, skin_sprite("#LANE_ALTERNATIVE_SEAMLESS")]
317
-
318
- JUDGMENT_LINE = Annotated[Sprite, skin_sprite("#JUDGMENT_LINE")]
319
- NOTE_SLOT = Annotated[Sprite, skin_sprite("#NOTE_SLOT")]
320
- STAGE_COVER = Annotated[Sprite, skin_sprite("#STAGE_COVER")]
321
-
322
- GRID_NEUTRAL = Annotated[Sprite, skin_sprite("#GRID_NEUTRAL")]
323
- GRID_RED = Annotated[Sprite, skin_sprite("#GRID_RED")]
324
- GRID_GREEN = Annotated[Sprite, skin_sprite("#GRID_GREEN")]
325
- GRID_BLUE = Annotated[Sprite, skin_sprite("#GRID_BLUE")]
326
- GRID_YELLOW = Annotated[Sprite, skin_sprite("#GRID_YELLOW")]
327
- GRID_PURPLE = Annotated[Sprite, skin_sprite("#GRID_PURPLE")]
328
- GRID_CYAN = Annotated[Sprite, skin_sprite("#GRID_CYAN")]
329
-
330
-
331
- @skin
332
- class EmptySkin:
333
- pass
1
+ from dataclasses import dataclass
2
+ from typing import Annotated, Any, NewType, dataclass_transform, get_origin
3
+
4
+ from sonolus.backend.ops import Op
5
+ from sonolus.script.internal.introspection import get_field_specifiers
6
+ from sonolus.script.internal.native import native_function
7
+ from sonolus.script.quad import QuadLike, flatten_quad
8
+ from sonolus.script.record import Record
9
+ from sonolus.script.vec import Vec2
10
+
11
+
12
+ class Sprite(Record):
13
+ """Skin sprite.
14
+
15
+ Usage:
16
+ ```python
17
+ Sprite(id: int)
18
+ ```
19
+ """
20
+
21
+ id: int
22
+
23
+ @property
24
+ def is_available(self) -> bool:
25
+ """Check if the sprite is available."""
26
+ return _has_skin_sprite(self.id)
27
+
28
+ def draw(self, quad: QuadLike, z: float = 0.0, a: float = 1.0):
29
+ """Draw the sprite.
30
+
31
+ Arguments:
32
+ quad: The quad to draw the sprite on.
33
+ z: The z-index of the sprite.
34
+ a: The alpha of the sprite.
35
+ """
36
+ _draw(self.id, *flatten_quad(quad), z, a)
37
+
38
+ def draw_curved_b(self, quad: QuadLike, cp: Vec2, n: float, z: float = 0.0, a: float = 1.0):
39
+ """Draw the sprite with a curved bottom with a quadratic Bézier curve.
40
+
41
+ Arguments:
42
+ quad: The quad to draw the sprite on.
43
+ cp: The control point of the curve.
44
+ n: The number of segments to approximate the curve (higher is smoother but more expensive).
45
+ z: The z-index of the sprite.
46
+ a: The alpha of the sprite.
47
+ """
48
+ _draw_curved_b(self.id, *flatten_quad(quad), z, a, n, *cp.tuple)
49
+
50
+ def draw_curved_t(self, quad: QuadLike, cp: Vec2, n: float, z: float = 0.0, a: float = 1.0):
51
+ """Draw the sprite with a curved top with a quadratic Bézier curve.
52
+
53
+ Arguments:
54
+ quad: The quad to draw the sprite on.
55
+ cp: The control point of the curve.
56
+ n: The number of segments to approximate the curve (higher is smoother but more expensive).
57
+ z: The z-index of the sprite.
58
+ a: The alpha of the sprite.
59
+ """
60
+ _draw_curved_t(self.id, *flatten_quad(quad), z, a, n, *cp.tuple)
61
+
62
+ def draw_curved_l(self, quad: QuadLike, cp: Vec2, n: float, z: float = 0.0, a: float = 1.0):
63
+ """Draw the sprite with a curved left side with a quadratic Bézier curve.
64
+
65
+ Arguments:
66
+ quad: The quad to draw the sprite on.
67
+ cp: The control point of the curve.
68
+ n: The number of segments to approximate the curve (higher is smoother but more expensive).
69
+ z: The z-index of the sprite.
70
+ a: The alpha of the sprite.
71
+ """
72
+ _draw_curved_l(self.id, *flatten_quad(quad), z, a, n, *cp.tuple)
73
+
74
+ def draw_curved_r(self, quad: QuadLike, cp: Vec2, n: float, z: float = 0.0, a: float = 1.0):
75
+ """Draw the sprite with a curved right side with a quadratic Bézier curve.
76
+
77
+ Arguments:
78
+ quad: The quad to draw the sprite on.
79
+ cp: The control point of the curve.
80
+ n: The number of segments to approximate the curve (higher is smoother but more expensive).
81
+ z: The z-index of the sprite.
82
+ a: The alpha of the sprite.
83
+ """
84
+ _draw_curved_r(self.id, *flatten_quad(quad), z, a, n, *cp.tuple)
85
+
86
+ def draw_curved_bt(self, quad: QuadLike, cp1: Vec2, cp2: Vec2, n: float, z: float = 0.0, a: float = 1.0):
87
+ """Draw the sprite with a curved bottom and top with a cubic Bézier curve.
88
+
89
+ Arguments:
90
+ quad: The quad to draw the sprite on.
91
+ cp1: The control point of the bottom curve.
92
+ cp2: The control point of the top curve.
93
+ n: The number of segments to approximate the curve (higher is smoother but more expensive).
94
+ z: The z-index of the sprite.
95
+ a: The alpha of the sprite.
96
+ """
97
+ _draw_curved_bt(self.id, *flatten_quad(quad), z, a, n, *cp1.tuple, *cp2.tuple)
98
+
99
+ def draw_curved_lr(self, quad: QuadLike, cp1: Vec2, cp2: Vec2, n: float, z: float = 0.0, a: float = 1.0):
100
+ """Draw the sprite with a curved left and right side with a cubic Bézier curve.
101
+
102
+ Arguments:
103
+ quad: The quad to draw the sprite on.
104
+ cp1: The control point of the left curve.
105
+ cp2: The control point of the right curve.
106
+ n: The number of segments to approximate the curve (higher is smoother but more expensive).
107
+ z: The z-index of the sprite.
108
+ a: The alpha of the sprite.
109
+ """
110
+ _draw_curved_lr(self.id, *flatten_quad(quad), z, a, n, *cp1.tuple, *cp2.tuple)
111
+
112
+
113
+ @native_function(Op.HasSkinSprite)
114
+ def _has_skin_sprite(sprite_id: int) -> bool:
115
+ raise NotImplementedError
116
+
117
+
118
+ @native_function(Op.Draw)
119
+ def _draw(
120
+ sprite_id: int,
121
+ x1: float,
122
+ y1: float,
123
+ x2: float,
124
+ y2: float,
125
+ x3: float,
126
+ y3: float,
127
+ x4: float,
128
+ y4: float,
129
+ z: float,
130
+ a: float,
131
+ ) -> None:
132
+ raise NotImplementedError
133
+
134
+
135
+ @native_function(Op.DrawCurvedB)
136
+ def _draw_curved_b(
137
+ sprite_id: int,
138
+ x1: float,
139
+ y1: float,
140
+ x2: float,
141
+ y2: float,
142
+ x3: float,
143
+ y3: float,
144
+ x4: float,
145
+ y4: float,
146
+ z: float,
147
+ a: float,
148
+ n: int,
149
+ p: float,
150
+ q: float,
151
+ ) -> None:
152
+ raise NotImplementedError
153
+
154
+
155
+ @native_function(Op.DrawCurvedT)
156
+ def _draw_curved_t(
157
+ sprite_id: int,
158
+ x1: float,
159
+ y1: float,
160
+ x2: float,
161
+ y2: float,
162
+ x3: float,
163
+ y3: float,
164
+ x4: float,
165
+ y4: float,
166
+ z: float,
167
+ a: float,
168
+ n: int,
169
+ p: float,
170
+ q: float,
171
+ ) -> None:
172
+ raise NotImplementedError
173
+
174
+
175
+ @native_function(Op.DrawCurvedL)
176
+ def _draw_curved_l(
177
+ sprite_id: int,
178
+ x1: float,
179
+ y1: float,
180
+ x2: float,
181
+ y2: float,
182
+ x3: float,
183
+ y3: float,
184
+ x4: float,
185
+ y4: float,
186
+ z: float,
187
+ a: float,
188
+ n: int,
189
+ p: float,
190
+ q: float,
191
+ ) -> None:
192
+ raise NotImplementedError
193
+
194
+
195
+ @native_function(Op.DrawCurvedR)
196
+ def _draw_curved_r(
197
+ sprite_id: int,
198
+ x1: float,
199
+ y1: float,
200
+ x2: float,
201
+ y2: float,
202
+ x3: float,
203
+ y3: float,
204
+ x4: float,
205
+ y4: float,
206
+ z: float,
207
+ a: float,
208
+ n: int,
209
+ p: float,
210
+ q: float,
211
+ ) -> None:
212
+ raise NotImplementedError
213
+
214
+
215
+ @native_function(Op.DrawCurvedBT)
216
+ def _draw_curved_bt(
217
+ sprite_id: int,
218
+ x1: float,
219
+ y1: float,
220
+ x2: float,
221
+ y2: float,
222
+ x3: float,
223
+ y3: float,
224
+ x4: float,
225
+ y4: float,
226
+ z: float,
227
+ a: float,
228
+ n: int,
229
+ p1: float,
230
+ q1: float,
231
+ p2: float,
232
+ q2: float,
233
+ ) -> None:
234
+ raise NotImplementedError
235
+
236
+
237
+ @native_function(Op.DrawCurvedLR)
238
+ def _draw_curved_lr(
239
+ sprite_id: int,
240
+ x1: float,
241
+ y1: float,
242
+ x2: float,
243
+ y2: float,
244
+ x3: float,
245
+ y3: float,
246
+ x4: float,
247
+ y4: float,
248
+ z: float,
249
+ a: float,
250
+ n: int,
251
+ p1: float,
252
+ q1: float,
253
+ p2: float,
254
+ q2: float,
255
+ ) -> None:
256
+ raise NotImplementedError
257
+
258
+
259
+ @dataclass
260
+ class SkinSprite:
261
+ name: str
262
+
263
+
264
+ def sprite(name: str) -> Any:
265
+ """Define a sprite with the given name."""
266
+ return SkinSprite(name)
267
+
268
+
269
+ type Skin = NewType("Skin", Any)
270
+
271
+
272
+ @dataclass_transform()
273
+ def skin[T](cls: type[T]) -> T | Skin:
274
+ """Decorator to define a skin.
275
+
276
+ Usage:
277
+ ```python
278
+ @skin
279
+ class Skin:
280
+ note: StandardSprite.NOTE_HEAD_RED
281
+ other: Sprite = skin_sprite("other")
282
+ ```
283
+ """
284
+ if len(cls.__bases__) != 1:
285
+ raise ValueError("Skin class must not inherit from any class (except object)")
286
+ instance = cls()
287
+ names = []
288
+ for i, (name, annotation) in enumerate(get_field_specifiers(cls).items()):
289
+ if get_origin(annotation) is not Annotated:
290
+ raise TypeError(f"Invalid annotation for skin: {annotation}")
291
+ annotation_type = annotation.__args__[0]
292
+ annotation_values = annotation.__metadata__
293
+ if annotation_type is not Sprite:
294
+ raise TypeError(f"Invalid annotation for skin: {annotation}, expected annotation of type Sprite")
295
+ if len(annotation_values) != 1 or not isinstance(annotation_values[0], SkinSprite):
296
+ raise TypeError(f"Invalid annotation for skin: {annotation}, expected a single string annotation value")
297
+ sprite_name = annotation_values[0].name
298
+ names.append(sprite_name)
299
+ setattr(instance, name, Sprite(i))
300
+ instance._sprites_ = names
301
+ instance._is_comptime_value_ = True
302
+ return instance
303
+
304
+
305
+ class StandardSprite:
306
+ """Standard skin sprites."""
307
+
308
+ NOTE_HEAD_NEUTRAL = Annotated[Sprite, sprite("#NOTE_HEAD_NEUTRAL")]
309
+ NOTE_HEAD_RED = Annotated[Sprite, sprite("#NOTE_HEAD_RED")]
310
+ NOTE_HEAD_GREEN = Annotated[Sprite, sprite("#NOTE_HEAD_GREEN")]
311
+ NOTE_HEAD_BLUE = Annotated[Sprite, sprite("#NOTE_HEAD_BLUE")]
312
+ NOTE_HEAD_YELLOW = Annotated[Sprite, sprite("#NOTE_HEAD_YELLOW")]
313
+ NOTE_HEAD_PURPLE = Annotated[Sprite, sprite("#NOTE_HEAD_PURPLE")]
314
+ NOTE_HEAD_CYAN = Annotated[Sprite, sprite("#NOTE_HEAD_CYAN")]
315
+
316
+ NOTE_TICK_NEUTRAL = Annotated[Sprite, sprite("#NOTE_TICK_NEUTRAL")]
317
+ NOTE_TICK_RED = Annotated[Sprite, sprite("#NOTE_TICK_RED")]
318
+ NOTE_TICK_GREEN = Annotated[Sprite, sprite("#NOTE_TICK_GREEN")]
319
+ NOTE_TICK_BLUE = Annotated[Sprite, sprite("#NOTE_TICK_BLUE")]
320
+ NOTE_TICK_YELLOW = Annotated[Sprite, sprite("#NOTE_TICK_YELLOW")]
321
+ NOTE_TICK_PURPLE = Annotated[Sprite, sprite("#NOTE_TICK_PURPLE")]
322
+ NOTE_TICK_CYAN = Annotated[Sprite, sprite("#NOTE_TICK_CYAN")]
323
+
324
+ NOTE_TAIL_NEUTRAL = Annotated[Sprite, sprite("#NOTE_TAIL_NEUTRAL")]
325
+ NOTE_TAIL_RED = Annotated[Sprite, sprite("#NOTE_TAIL_RED")]
326
+ NOTE_TAIL_GREEN = Annotated[Sprite, sprite("#NOTE_TAIL_GREEN")]
327
+ NOTE_TAIL_BLUE = Annotated[Sprite, sprite("#NOTE_TAIL_BLUE")]
328
+ NOTE_TAIL_YELLOW = Annotated[Sprite, sprite("#NOTE_TAIL_YELLOW")]
329
+ NOTE_TAIL_PURPLE = Annotated[Sprite, sprite("#NOTE_TAIL_PURPLE")]
330
+ NOTE_TAIL_CYAN = Annotated[Sprite, sprite("#NOTE_TAIL_CYAN")]
331
+
332
+ NOTE_CONNECTION_NEUTRAL = Annotated[Sprite, sprite("#NOTE_CONNECTION_NEUTRAL")]
333
+ NOTE_CONNECTION_RED = Annotated[Sprite, sprite("#NOTE_CONNECTION_RED")]
334
+ NOTE_CONNECTION_GREEN = Annotated[Sprite, sprite("#NOTE_CONNECTION_GREEN")]
335
+ NOTE_CONNECTION_BLUE = Annotated[Sprite, sprite("#NOTE_CONNECTION_BLUE")]
336
+ NOTE_CONNECTION_YELLOW = Annotated[Sprite, sprite("#NOTE_CONNECTION_YELLOW")]
337
+ NOTE_CONNECTION_PURPLE = Annotated[Sprite, sprite("#NOTE_CONNECTION_PURPLE")]
338
+ NOTE_CONNECTION_CYAN = Annotated[Sprite, sprite("#NOTE_CONNECTION_CYAN")]
339
+
340
+ NOTE_CONNECTION_NEUTRAL_SEAMLESS = Annotated[Sprite, sprite("#NOTE_CONNECTION_NEUTRAL_SEAMLESS")]
341
+ NOTE_CONNECTION_RED_SEAMLESS = Annotated[Sprite, sprite("#NOTE_CONNECTION_RED_SEAMLESS")]
342
+ NOTE_CONNECTION_GREEN_SEAMLESS = Annotated[Sprite, sprite("#NOTE_CONNECTION_GREEN_SEAMLESS")]
343
+ NOTE_CONNECTION_BLUE_SEAMLESS = Annotated[Sprite, sprite("#NOTE_CONNECTION_BLUE_SEAMLESS")]
344
+ NOTE_CONNECTION_YELLOW_SEAMLESS = Annotated[Sprite, sprite("#NOTE_CONNECTION_YELLOW_SEAMLESS")]
345
+ NOTE_CONNECTION_PURPLE_SEAMLESS = Annotated[Sprite, sprite("#NOTE_CONNECTION_PURPLE_SEAMLESS")]
346
+ NOTE_CONNECTION_CYAN_SEAMLESS = Annotated[Sprite, sprite("#NOTE_CONNECTION_CYAN_SEAMLESS")]
347
+
348
+ SIMULTANEOUS_CONNECTION_NEUTRAL = Annotated[Sprite, sprite("#SIMULTANEOUS_CONNECTION_NEUTRAL")]
349
+ SIMULTANEOUS_CONNECTION_RED = Annotated[Sprite, sprite("#SIMULTANEOUS_CONNECTION_RED")]
350
+ SIMULTANEOUS_CONNECTION_GREEN = Annotated[Sprite, sprite("#SIMULTANEOUS_CONNECTION_GREEN")]
351
+ SIMULTANEOUS_CONNECTION_BLUE = Annotated[Sprite, sprite("#SIMULTANEOUS_CONNECTION_BLUE")]
352
+ SIMULTANEOUS_CONNECTION_YELLOW = Annotated[Sprite, sprite("#SIMULTANEOUS_CONNECTION_YELLOW")]
353
+ SIMULTANEOUS_CONNECTION_PURPLE = Annotated[Sprite, sprite("#SIMULTANEOUS_CONNECTION_PURPLE")]
354
+ SIMULTANEOUS_CONNECTION_CYAN = Annotated[Sprite, sprite("#SIMULTANEOUS_CONNECTION_CYAN")]
355
+
356
+ SIMULTANEOUS_CONNECTION_NEUTRAL_SEAMLESS = Annotated[
357
+ Sprite, sprite("#SIMULTANEOUS_CONNECTION_NEUTRAL_SEAMLESS")
358
+ ]
359
+ SIMULTANEOUS_CONNECTION_RED_SEAMLESS = Annotated[Sprite, sprite("#SIMULTANEOUS_CONNECTION_RED_SEAMLESS")]
360
+ SIMULTANEOUS_CONNECTION_GREEN_SEAMLESS = Annotated[Sprite, sprite("#SIMULTANEOUS_CONNECTION_GREEN_SEAMLESS")]
361
+ SIMULTANEOUS_CONNECTION_BLUE_SEAMLESS = Annotated[Sprite, sprite("#SIMULTANEOUS_CONNECTION_BLUE_SEAMLESS")]
362
+ SIMULTANEOUS_CONNECTION_YELLOW_SEAMLESS = Annotated[Sprite, sprite("#SIMULTANEOUS_CONNECTION_YELLOW_SEAMLESS")]
363
+ SIMULTANEOUS_CONNECTION_PURPLE_SEAMLESS = Annotated[Sprite, sprite("#SIMULTANEOUS_CONNECTION_PURPLE_SEAMLESS")]
364
+ SIMULTANEOUS_CONNECTION_CYAN_SEAMLESS = Annotated[Sprite, sprite("#SIMULTANEOUS_CONNECTION_CYAN_SEAMLESS")]
365
+
366
+ DIRECTIONAL_MARKER_NEUTRAL = Annotated[Sprite, sprite("#DIRECTIONAL_MARKER_NEUTRAL")]
367
+ DIRECTIONAL_MARKER_RED = Annotated[Sprite, sprite("#DIRECTIONAL_MARKER_RED")]
368
+ DIRECTIONAL_MARKER_GREEN = Annotated[Sprite, sprite("#DIRECTIONAL_MARKER_GREEN")]
369
+ DIRECTIONAL_MARKER_BLUE = Annotated[Sprite, sprite("#DIRECTIONAL_MARKER_BLUE")]
370
+ DIRECTIONAL_MARKER_YELLOW = Annotated[Sprite, sprite("#DIRECTIONAL_MARKER_YELLOW")]
371
+ DIRECTIONAL_MARKER_PURPLE = Annotated[Sprite, sprite("#DIRECTIONAL_MARKER_PURPLE")]
372
+ DIRECTIONAL_MARKER_CYAN = Annotated[Sprite, sprite("#DIRECTIONAL_MARKER_CYAN")]
373
+
374
+ SIMULTANEOUS_MARKER_NEUTRAL = Annotated[Sprite, sprite("#SIMULTANEOUS_MARKER_NEUTRAL")]
375
+ SIMULTANEOUS_MARKER_RED = Annotated[Sprite, sprite("#SIMULTANEOUS_MARKER_RED")]
376
+ SIMULTANEOUS_MARKER_GREEN = Annotated[Sprite, sprite("#SIMULTANEOUS_MARKER_GREEN")]
377
+ SIMULTANEOUS_MARKER_BLUE = Annotated[Sprite, sprite("#SIMULTANEOUS_MARKER_BLUE")]
378
+ SIMULTANEOUS_MARKER_YELLOW = Annotated[Sprite, sprite("#SIMULTANEOUS_MARKER_YELLOW")]
379
+ SIMULTANEOUS_MARKER_PURPLE = Annotated[Sprite, sprite("#SIMULTANEOUS_MARKER_PURPLE")]
380
+ SIMULTANEOUS_MARKER_CYAN = Annotated[Sprite, sprite("#SIMULTANEOUS_MARKER_CYAN")]
381
+
382
+ STAGE_MIDDLE = Annotated[Sprite, sprite("#STAGE_MIDDLE")]
383
+ STAGE_LEFT_BORDER = Annotated[Sprite, sprite("#STAGE_LEFT_BORDER")]
384
+ STAGE_RIGHT_BORDER = Annotated[Sprite, sprite("#STAGE_RIGHT_BORDER")]
385
+ STAGE_TOP_BORDER = Annotated[Sprite, sprite("#STAGE_TOP_BORDER")]
386
+ STAGE_BOTTOM_BORDER = Annotated[Sprite, sprite("#STAGE_BOTTOM_BORDER")]
387
+
388
+ STAGE_LEFT_BORDER_SEAMLESS = Annotated[Sprite, sprite("#STAGE_LEFT_BORDER_SEAMLESS")]
389
+ STAGE_RIGHT_BORDER_SEAMLESS = Annotated[Sprite, sprite("#STAGE_RIGHT_BORDER_SEAMLESS")]
390
+ STAGE_TOP_BORDER_SEAMLESS = Annotated[Sprite, sprite("#STAGE_TOP_BORDER_SEAMLESS")]
391
+ STAGE_BOTTOM_BORDER_SEAMLESS = Annotated[Sprite, sprite("#STAGE_BOTTOM_BORDER_SEAMLESS")]
392
+
393
+ STAGE_TOP_LEFT_CORNER = Annotated[Sprite, sprite("#STAGE_TOP_LEFT_CORNER")]
394
+ STAGE_TOP_RIGHT_CORNER = Annotated[Sprite, sprite("#STAGE_TOP_RIGHT_CORNER")]
395
+ STAGE_BOTTOM_LEFT_CORNER = Annotated[Sprite, sprite("#STAGE_BOTTOM_LEFT_CORNER")]
396
+ STAGE_BOTTOM_RIGHT_CORNER = Annotated[Sprite, sprite("#STAGE_BOTTOM_RIGHT_CORNER")]
397
+
398
+ LANE = Annotated[Sprite, sprite("#LANE")]
399
+ LANE_SEAMLESS = Annotated[Sprite, sprite("#LANE_SEAMLESS")]
400
+ LANE_ALTERNATIVE = Annotated[Sprite, sprite("#LANE_ALTERNATIVE")]
401
+ LANE_ALTERNATIVE_SEAMLESS = Annotated[Sprite, sprite("#LANE_ALTERNATIVE_SEAMLESS")]
402
+
403
+ JUDGMENT_LINE = Annotated[Sprite, sprite("#JUDGMENT_LINE")]
404
+ NOTE_SLOT = Annotated[Sprite, sprite("#NOTE_SLOT")]
405
+ STAGE_COVER = Annotated[Sprite, sprite("#STAGE_COVER")]
406
+
407
+ GRID_NEUTRAL = Annotated[Sprite, sprite("#GRID_NEUTRAL")]
408
+ GRID_RED = Annotated[Sprite, sprite("#GRID_RED")]
409
+ GRID_GREEN = Annotated[Sprite, sprite("#GRID_GREEN")]
410
+ GRID_BLUE = Annotated[Sprite, sprite("#GRID_BLUE")]
411
+ GRID_YELLOW = Annotated[Sprite, sprite("#GRID_YELLOW")]
412
+ GRID_PURPLE = Annotated[Sprite, sprite("#GRID_PURPLE")]
413
+ GRID_CYAN = Annotated[Sprite, sprite("#GRID_CYAN")]
414
+
415
+
416
+ @skin
417
+ class EmptySkin:
418
+ pass