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
@@ -1,157 +1,190 @@
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.graphics import QuadLike, flatten_quad
8
- from sonolus.script.internal.introspection import get_field_specifiers
9
- from sonolus.script.internal.native import native_function
10
- from sonolus.script.record import Record
11
-
12
-
13
- class Particle(Record):
14
- id: int
15
-
16
- def is_available(self) -> bool:
17
- return _has_particle_effect(self.id)
18
-
19
- def spawn(self, quad: QuadLike, duration: float, loop: bool = False) -> ParticleHandle:
20
- return ParticleHandle(_spawn_particle_effect(self.id, *flatten_quad(quad), duration, loop))
21
-
22
-
23
- class ParticleHandle(Record):
24
- id: int
25
-
26
- def move(self, quad: QuadLike) -> None:
27
- _move_particle_effect(self.id, *flatten_quad(quad))
28
-
29
- def destroy(self) -> None:
30
- _destroy_particle_effect(self.id)
31
-
32
-
33
- @native_function(Op.HasParticleEffect)
34
- def _has_particle_effect(particle_id: int) -> bool:
35
- raise NotImplementedError
36
-
37
-
38
- @native_function(Op.SpawnParticleEffect)
39
- def _spawn_particle_effect(
40
- particle_id: int,
41
- x1: float,
42
- y1: float,
43
- x2: float,
44
- y2: float,
45
- x3: float,
46
- y3: float,
47
- x4: float,
48
- y4: float,
49
- duration: float,
50
- loop: bool,
51
- ) -> int:
52
- raise NotImplementedError
53
-
54
-
55
- @native_function(Op.MoveParticleEffect)
56
- def _move_particle_effect(
57
- handle: int, x1: float, y1: float, x2: float, y2: float, x3: float, y3: float, x4: float, y4: float
58
- ) -> None:
59
- raise NotImplementedError
60
-
61
-
62
- @native_function(Op.DestroyParticleEffect)
63
- def _destroy_particle_effect(handle: int) -> None:
64
- raise NotImplementedError
65
-
66
-
67
- @dataclass
68
- class ParticleInfo:
69
- name: str
70
-
71
-
72
- def particle(name: str) -> Any:
73
- return ParticleInfo(name)
74
-
75
-
76
- type Particles = NewType("Particles", Any)
77
-
78
-
79
- @dataclass_transform()
80
- def particles[T](cls: type[T]) -> T | Particles:
81
- if len(cls.__bases__) != 1:
82
- raise ValueError("Particles class must not inherit from any class (except object)")
83
- instance = cls()
84
- names = []
85
- for i, (name, annotation) in enumerate(get_field_specifiers(cls).items()):
86
- if get_origin(annotation) is not Annotated:
87
- raise TypeError(f"Invalid annotation for particles: {annotation}")
88
- annotation_type = annotation.__args__[0]
89
- annotation_values = annotation.__metadata__
90
- if annotation_type is not Particle:
91
- raise TypeError(f"Invalid annotation for particles: {annotation}, expected annotation of type Particle")
92
- if len(annotation_values) != 1 or not isinstance(annotation_values[0], ParticleInfo):
93
- raise TypeError(
94
- f"Invalid annotation for particles: {annotation}, expected a single string annotation value"
95
- )
96
- particle_name = annotation_values[0].name
97
- names.append(particle_name)
98
- setattr(instance, name, Particle(i))
99
- instance._particles_ = names
100
- instance._is_comptime_value_ = True
101
- return instance
102
-
103
-
104
- class StandardParticle:
105
- NOTE_CIRCULAR_TAP_NEUTRAL = Annotated[Particle, particle("#NOTE_CIRCULAR_TAP_NEUTRAL")]
106
- NOTE_CIRCULAR_TAP_RED = Annotated[Particle, particle("#NOTE_CIRCULAR_TAP_RED")]
107
- NOTE_CIRCULAR_TAP_GREEN = Annotated[Particle, particle("#NOTE_CIRCULAR_TAP_GREEN")]
108
- NOTE_CIRCULAR_TAP_BLUE = Annotated[Particle, particle("#NOTE_CIRCULAR_TAP_BLUE")]
109
- NOTE_CIRCULAR_TAP_YELLOW = Annotated[Particle, particle("#NOTE_CIRCULAR_TAP_YELLOW")]
110
- NOTE_CIRCULAR_TAP_PURPLE = Annotated[Particle, particle("#NOTE_CIRCULAR_TAP_PURPLE")]
111
- NOTE_CIRCULAR_TAP_CYAN = Annotated[Particle, particle("#NOTE_CIRCULAR_TAP_CYAN")]
112
- NOTE_CIRCULAR_ALTERNATIVE_NEUTRAL = Annotated[Particle, particle("#NOTE_CIRCULAR_ALTERNATIVE_NEUTRAL")]
113
- NOTE_CIRCULAR_ALTERNATIVE_RED = Annotated[Particle, particle("#NOTE_CIRCULAR_ALTERNATIVE_RED")]
114
- NOTE_CIRCULAR_ALTERNATIVE_GREEN = Annotated[Particle, particle("#NOTE_CIRCULAR_ALTERNATIVE_GREEN")]
115
- NOTE_CIRCULAR_ALTERNATIVE_BLUE = Annotated[Particle, particle("#NOTE_CIRCULAR_ALTERNATIVE_BLUE")]
116
- NOTE_CIRCULAR_ALTERNATIVE_YELLOW = Annotated[Particle, particle("#NOTE_CIRCULAR_ALTERNATIVE_YELLOW")]
117
- NOTE_CIRCULAR_ALTERNATIVE_PURPLE = Annotated[Particle, particle("#NOTE_CIRCULAR_ALTERNATIVE_PURPLE")]
118
- NOTE_CIRCULAR_ALTERNATIVE_CYAN = Annotated[Particle, particle("#NOTE_CIRCULAR_ALTERNATIVE_CYAN")]
119
- NOTE_CIRCULAR_HOLD_NEUTRAL = Annotated[Particle, particle("#NOTE_CIRCULAR_HOLD_NEUTRAL")]
120
- NOTE_CIRCULAR_HOLD_RED = Annotated[Particle, particle("#NOTE_CIRCULAR_HOLD_RED")]
121
- NOTE_CIRCULAR_HOLD_GREEN = Annotated[Particle, particle("#NOTE_CIRCULAR_HOLD_GREEN")]
122
- NOTE_CIRCULAR_HOLD_BLUE = Annotated[Particle, particle("#NOTE_CIRCULAR_HOLD_BLUE")]
123
- NOTE_CIRCULAR_HOLD_YELLOW = Annotated[Particle, particle("#NOTE_CIRCULAR_HOLD_YELLOW")]
124
- NOTE_CIRCULAR_HOLD_PURPLE = Annotated[Particle, particle("#NOTE_CIRCULAR_HOLD_PURPLE")]
125
- NOTE_CIRCULAR_HOLD_CYAN = Annotated[Particle, particle("#NOTE_CIRCULAR_HOLD_CYAN")]
126
- NOTE_LINEAR_TAP_NEUTRAL = Annotated[Particle, particle("#NOTE_LINEAR_TAP_NEUTRAL")]
127
- NOTE_LINEAR_TAP_RED = Annotated[Particle, particle("#NOTE_LINEAR_TAP_RED")]
128
- NOTE_LINEAR_TAP_GREEN = Annotated[Particle, particle("#NOTE_LINEAR_TAP_GREEN")]
129
- NOTE_LINEAR_TAP_BLUE = Annotated[Particle, particle("#NOTE_LINEAR_TAP_BLUE")]
130
- NOTE_LINEAR_TAP_YELLOW = Annotated[Particle, particle("#NOTE_LINEAR_TAP_YELLOW")]
131
- NOTE_LINEAR_TAP_PURPLE = Annotated[Particle, particle("#NOTE_LINEAR_TAP_PURPLE")]
132
- NOTE_LINEAR_TAP_CYAN = Annotated[Particle, particle("#NOTE_LINEAR_TAP_CYAN")]
133
- NOTE_LINEAR_ALTERNATIVE_NEUTRAL = Annotated[Particle, particle("#NOTE_LINEAR_ALTERNATIVE_NEUTRAL")]
134
- NOTE_LINEAR_ALTERNATIVE_RED = Annotated[Particle, particle("#NOTE_LINEAR_ALTERNATIVE_RED")]
135
- NOTE_LINEAR_ALTERNATIVE_GREEN = Annotated[Particle, particle("#NOTE_LINEAR_ALTERNATIVE_GREEN")]
136
- NOTE_LINEAR_ALTERNATIVE_BLUE = Annotated[Particle, particle("#NOTE_LINEAR_ALTERNATIVE_BLUE")]
137
- NOTE_LINEAR_ALTERNATIVE_YELLOW = Annotated[Particle, particle("#NOTE_LINEAR_ALTERNATIVE_YELLOW")]
138
- NOTE_LINEAR_ALTERNATIVE_PURPLE = Annotated[Particle, particle("#NOTE_LINEAR_ALTERNATIVE_PURPLE")]
139
- NOTE_LINEAR_ALTERNATIVE_CYAN = Annotated[Particle, particle("#NOTE_LINEAR_ALTERNATIVE_CYAN")]
140
- NOTE_LINEAR_HOLD_NEUTRAL = Annotated[Particle, particle("#NOTE_LINEAR_HOLD_NEUTRAL")]
141
- NOTE_LINEAR_HOLD_RED = Annotated[Particle, particle("#NOTE_LINEAR_HOLD_RED")]
142
- NOTE_LINEAR_HOLD_GREEN = Annotated[Particle, particle("#NOTE_LINEAR_HOLD_GREEN")]
143
- NOTE_LINEAR_HOLD_BLUE = Annotated[Particle, particle("#NOTE_LINEAR_HOLD_BLUE")]
144
- NOTE_LINEAR_HOLD_YELLOW = Annotated[Particle, particle("#NOTE_LINEAR_HOLD_YELLOW")]
145
- NOTE_LINEAR_HOLD_PURPLE = Annotated[Particle, particle("#NOTE_LINEAR_HOLD_PURPLE")]
146
- NOTE_LINEAR_HOLD_CYAN = Annotated[Particle, particle("#NOTE_LINEAR_HOLD_CYAN")]
147
- LANE_CIRCULAR = Annotated[Particle, particle("#LANE_CIRCULAR")]
148
- LANE_LINEAR = Annotated[Particle, particle("#LANE_LINEAR")]
149
- SLOT_CIRCULAR = Annotated[Particle, particle("#SLOT_CIRCULAR")]
150
- SLOT_LINEAR = Annotated[Particle, particle("#SLOT_LINEAR")]
151
- JUDGE_LINE_CIRCULAR = Annotated[Particle, particle("#JUDGE_LINE_CIRCULAR")]
152
- JUDGE_LINE_LINEAR = Annotated[Particle, particle("#JUDGE_LINE_LINEAR")]
153
-
154
-
155
- @particles
156
- class EmptyParticles:
157
- 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.quad import QuadLike, flatten_quad
10
+ from sonolus.script.record import Record
11
+
12
+
13
+ class Particle(Record):
14
+ """A particle effect."""
15
+
16
+ id: int
17
+
18
+ def is_available(self) -> bool:
19
+ """Check if the particle effect is available."""
20
+ return _has_particle_effect(self.id)
21
+
22
+ def spawn(self, quad: QuadLike, duration: float, loop: bool = False) -> ParticleHandle:
23
+ """Spawn the particle effect.
24
+
25
+ Args:
26
+ quad: The quad to spawn the particle effect on.
27
+ duration: The duration of the particle effect.
28
+ loop: Whether to loop the particle effect.
29
+
30
+ Returns:
31
+ ParticleHandle: A handle to the spawned particle effect.
32
+ """
33
+ return ParticleHandle(_spawn_particle_effect(self.id, *flatten_quad(quad), duration, loop))
34
+
35
+
36
+ class ParticleHandle(Record):
37
+ """A handle to a looping particle effect."""
38
+
39
+ id: int
40
+
41
+ def move(self, quad: QuadLike) -> None:
42
+ """Move the particle effect to a new location.
43
+
44
+ Args:
45
+ quad: The new quad to move the particle effect to.
46
+ """
47
+ _move_particle_effect(self.id, *flatten_quad(quad))
48
+
49
+ def destroy(self) -> None:
50
+ """Destroy the particle effect."""
51
+ _destroy_particle_effect(self.id)
52
+
53
+
54
+ @native_function(Op.HasParticleEffect)
55
+ def _has_particle_effect(particle_id: int) -> bool:
56
+ raise NotImplementedError
57
+
58
+
59
+ @native_function(Op.SpawnParticleEffect)
60
+ def _spawn_particle_effect(
61
+ particle_id: int,
62
+ x1: float,
63
+ y1: float,
64
+ x2: float,
65
+ y2: float,
66
+ x3: float,
67
+ y3: float,
68
+ x4: float,
69
+ y4: float,
70
+ duration: float,
71
+ loop: bool,
72
+ ) -> int:
73
+ raise NotImplementedError
74
+
75
+
76
+ @native_function(Op.MoveParticleEffect)
77
+ def _move_particle_effect(
78
+ handle: int, x1: float, y1: float, x2: float, y2: float, x3: float, y3: float, x4: float, y4: float
79
+ ) -> None:
80
+ raise NotImplementedError
81
+
82
+
83
+ @native_function(Op.DestroyParticleEffect)
84
+ def _destroy_particle_effect(handle: int) -> None:
85
+ raise NotImplementedError
86
+
87
+
88
+ @dataclass
89
+ class _ParticleInfo:
90
+ name: str
91
+
92
+
93
+ def particle(name: str) -> Any:
94
+ return _ParticleInfo(name)
95
+
96
+
97
+ type Particles = NewType("Particles", Any)
98
+
99
+
100
+ @dataclass_transform()
101
+ def particles[T](cls: type[T]) -> T | Particles:
102
+ """Decorator to define particles.
103
+
104
+ Usage:
105
+ ```python
106
+ @particles
107
+ class Particles:
108
+ tap: StandardParticle.NOTE_CIRCULAR_TAP_RED
109
+ other: Particle = particle("other")
110
+ ```
111
+ """
112
+ if len(cls.__bases__) != 1:
113
+ raise ValueError("Particles class must not inherit from any class (except object)")
114
+ instance = cls()
115
+ names = []
116
+ for i, (name, annotation) in enumerate(get_field_specifiers(cls).items()):
117
+ if get_origin(annotation) is not Annotated:
118
+ raise TypeError(f"Invalid annotation for particles: {annotation}")
119
+ annotation_type = annotation.__args__[0]
120
+ annotation_values = annotation.__metadata__
121
+ if annotation_type is not Particle:
122
+ raise TypeError(f"Invalid annotation for particles: {annotation}, expected annotation of type Particle")
123
+ if len(annotation_values) != 1 or not isinstance(annotation_values[0], _ParticleInfo):
124
+ raise TypeError(
125
+ f"Invalid annotation for particles: {annotation}, expected a single string annotation value"
126
+ )
127
+ particle_name = annotation_values[0].name
128
+ names.append(particle_name)
129
+ setattr(instance, name, Particle(i))
130
+ instance._particles_ = names
131
+ instance._is_comptime_value_ = True
132
+ return instance
133
+
134
+
135
+ class StandardParticle:
136
+ """Standard particles."""
137
+
138
+ NOTE_CIRCULAR_TAP_NEUTRAL = Annotated[Particle, particle("#NOTE_CIRCULAR_TAP_NEUTRAL")]
139
+ NOTE_CIRCULAR_TAP_RED = Annotated[Particle, particle("#NOTE_CIRCULAR_TAP_RED")]
140
+ NOTE_CIRCULAR_TAP_GREEN = Annotated[Particle, particle("#NOTE_CIRCULAR_TAP_GREEN")]
141
+ NOTE_CIRCULAR_TAP_BLUE = Annotated[Particle, particle("#NOTE_CIRCULAR_TAP_BLUE")]
142
+ NOTE_CIRCULAR_TAP_YELLOW = Annotated[Particle, particle("#NOTE_CIRCULAR_TAP_YELLOW")]
143
+ NOTE_CIRCULAR_TAP_PURPLE = Annotated[Particle, particle("#NOTE_CIRCULAR_TAP_PURPLE")]
144
+ NOTE_CIRCULAR_TAP_CYAN = Annotated[Particle, particle("#NOTE_CIRCULAR_TAP_CYAN")]
145
+ NOTE_CIRCULAR_ALTERNATIVE_NEUTRAL = Annotated[Particle, particle("#NOTE_CIRCULAR_ALTERNATIVE_NEUTRAL")]
146
+ NOTE_CIRCULAR_ALTERNATIVE_RED = Annotated[Particle, particle("#NOTE_CIRCULAR_ALTERNATIVE_RED")]
147
+ NOTE_CIRCULAR_ALTERNATIVE_GREEN = Annotated[Particle, particle("#NOTE_CIRCULAR_ALTERNATIVE_GREEN")]
148
+ NOTE_CIRCULAR_ALTERNATIVE_BLUE = Annotated[Particle, particle("#NOTE_CIRCULAR_ALTERNATIVE_BLUE")]
149
+ NOTE_CIRCULAR_ALTERNATIVE_YELLOW = Annotated[Particle, particle("#NOTE_CIRCULAR_ALTERNATIVE_YELLOW")]
150
+ NOTE_CIRCULAR_ALTERNATIVE_PURPLE = Annotated[Particle, particle("#NOTE_CIRCULAR_ALTERNATIVE_PURPLE")]
151
+ NOTE_CIRCULAR_ALTERNATIVE_CYAN = Annotated[Particle, particle("#NOTE_CIRCULAR_ALTERNATIVE_CYAN")]
152
+ NOTE_CIRCULAR_HOLD_NEUTRAL = Annotated[Particle, particle("#NOTE_CIRCULAR_HOLD_NEUTRAL")]
153
+ NOTE_CIRCULAR_HOLD_RED = Annotated[Particle, particle("#NOTE_CIRCULAR_HOLD_RED")]
154
+ NOTE_CIRCULAR_HOLD_GREEN = Annotated[Particle, particle("#NOTE_CIRCULAR_HOLD_GREEN")]
155
+ NOTE_CIRCULAR_HOLD_BLUE = Annotated[Particle, particle("#NOTE_CIRCULAR_HOLD_BLUE")]
156
+ NOTE_CIRCULAR_HOLD_YELLOW = Annotated[Particle, particle("#NOTE_CIRCULAR_HOLD_YELLOW")]
157
+ NOTE_CIRCULAR_HOLD_PURPLE = Annotated[Particle, particle("#NOTE_CIRCULAR_HOLD_PURPLE")]
158
+ NOTE_CIRCULAR_HOLD_CYAN = Annotated[Particle, particle("#NOTE_CIRCULAR_HOLD_CYAN")]
159
+ NOTE_LINEAR_TAP_NEUTRAL = Annotated[Particle, particle("#NOTE_LINEAR_TAP_NEUTRAL")]
160
+ NOTE_LINEAR_TAP_RED = Annotated[Particle, particle("#NOTE_LINEAR_TAP_RED")]
161
+ NOTE_LINEAR_TAP_GREEN = Annotated[Particle, particle("#NOTE_LINEAR_TAP_GREEN")]
162
+ NOTE_LINEAR_TAP_BLUE = Annotated[Particle, particle("#NOTE_LINEAR_TAP_BLUE")]
163
+ NOTE_LINEAR_TAP_YELLOW = Annotated[Particle, particle("#NOTE_LINEAR_TAP_YELLOW")]
164
+ NOTE_LINEAR_TAP_PURPLE = Annotated[Particle, particle("#NOTE_LINEAR_TAP_PURPLE")]
165
+ NOTE_LINEAR_TAP_CYAN = Annotated[Particle, particle("#NOTE_LINEAR_TAP_CYAN")]
166
+ NOTE_LINEAR_ALTERNATIVE_NEUTRAL = Annotated[Particle, particle("#NOTE_LINEAR_ALTERNATIVE_NEUTRAL")]
167
+ NOTE_LINEAR_ALTERNATIVE_RED = Annotated[Particle, particle("#NOTE_LINEAR_ALTERNATIVE_RED")]
168
+ NOTE_LINEAR_ALTERNATIVE_GREEN = Annotated[Particle, particle("#NOTE_LINEAR_ALTERNATIVE_GREEN")]
169
+ NOTE_LINEAR_ALTERNATIVE_BLUE = Annotated[Particle, particle("#NOTE_LINEAR_ALTERNATIVE_BLUE")]
170
+ NOTE_LINEAR_ALTERNATIVE_YELLOW = Annotated[Particle, particle("#NOTE_LINEAR_ALTERNATIVE_YELLOW")]
171
+ NOTE_LINEAR_ALTERNATIVE_PURPLE = Annotated[Particle, particle("#NOTE_LINEAR_ALTERNATIVE_PURPLE")]
172
+ NOTE_LINEAR_ALTERNATIVE_CYAN = Annotated[Particle, particle("#NOTE_LINEAR_ALTERNATIVE_CYAN")]
173
+ NOTE_LINEAR_HOLD_NEUTRAL = Annotated[Particle, particle("#NOTE_LINEAR_HOLD_NEUTRAL")]
174
+ NOTE_LINEAR_HOLD_RED = Annotated[Particle, particle("#NOTE_LINEAR_HOLD_RED")]
175
+ NOTE_LINEAR_HOLD_GREEN = Annotated[Particle, particle("#NOTE_LINEAR_HOLD_GREEN")]
176
+ NOTE_LINEAR_HOLD_BLUE = Annotated[Particle, particle("#NOTE_LINEAR_HOLD_BLUE")]
177
+ NOTE_LINEAR_HOLD_YELLOW = Annotated[Particle, particle("#NOTE_LINEAR_HOLD_YELLOW")]
178
+ NOTE_LINEAR_HOLD_PURPLE = Annotated[Particle, particle("#NOTE_LINEAR_HOLD_PURPLE")]
179
+ NOTE_LINEAR_HOLD_CYAN = Annotated[Particle, particle("#NOTE_LINEAR_HOLD_CYAN")]
180
+ LANE_CIRCULAR = Annotated[Particle, particle("#LANE_CIRCULAR")]
181
+ LANE_LINEAR = Annotated[Particle, particle("#LANE_LINEAR")]
182
+ SLOT_CIRCULAR = Annotated[Particle, particle("#SLOT_CIRCULAR")]
183
+ SLOT_LINEAR = Annotated[Particle, particle("#SLOT_LINEAR")]
184
+ JUDGE_LINE_CIRCULAR = Annotated[Particle, particle("#JUDGE_LINE_CIRCULAR")]
185
+ JUDGE_LINE_LINEAR = Annotated[Particle, particle("#JUDGE_LINE_LINEAR")]
186
+
187
+
188
+ @particles
189
+ class EmptyParticles:
190
+ pass
sonolus/script/pointer.py CHANGED
@@ -1,30 +1,30 @@
1
- from sonolus.backend.place import BlockPlace
2
- from sonolus.script.internal.impl import meta_fn, validate_value
3
- from sonolus.script.internal.value import Value
4
- from sonolus.script.num import Num, is_num
5
-
6
-
7
- @meta_fn
8
- def deref[T: Value](block: Num, offset: Num, type_: type[T]) -> T:
9
- block = Num._accept_(block)
10
- offset = Num._accept_(offset)
11
- type_ = validate_value(type_)._as_py_()
12
- if block._is_py_():
13
- block = block._as_py_()
14
- if not isinstance(block, int):
15
- raise TypeError("block must be an integer")
16
- else:
17
- if not is_num(block):
18
- raise TypeError("block must be a Num")
19
- block = block.index()
20
- if offset._is_py_():
21
- offset = offset._as_py_()
22
- if not isinstance(offset, int):
23
- raise TypeError("offset must be an integer")
24
- else:
25
- if not is_num(offset):
26
- raise TypeError("offset must be a Num")
27
- offset = offset.index()
28
- if not (isinstance(type_, type) and issubclass(type_, Value)):
29
- raise TypeError("type_ must be a Value")
30
- return type_._from_place_(BlockPlace(block, offset))
1
+ from sonolus.backend.place import BlockPlace
2
+ from sonolus.script.internal.impl import meta_fn, validate_value
3
+ from sonolus.script.internal.value import Value
4
+ from sonolus.script.num import Num, _is_num
5
+
6
+
7
+ @meta_fn
8
+ def _deref[T: Value](block: Num, offset: Num, type_: type[T]) -> T:
9
+ block = Num._accept_(block)
10
+ offset = Num._accept_(offset)
11
+ type_ = validate_value(type_)._as_py_()
12
+ if block._is_py_():
13
+ block = block._as_py_()
14
+ if not isinstance(block, int):
15
+ raise TypeError("block must be an integer")
16
+ else:
17
+ if not _is_num(block):
18
+ raise TypeError("block must be a Num")
19
+ block = block.index()
20
+ if offset._is_py_():
21
+ offset = offset._as_py_()
22
+ if not isinstance(offset, int):
23
+ raise TypeError("offset must be an integer")
24
+ else:
25
+ if not _is_num(offset):
26
+ raise TypeError("offset must be a Num")
27
+ offset = offset.index()
28
+ if not (isinstance(type_, type) and issubclass(type_, Value)):
29
+ raise TypeError("type_ must be a Value")
30
+ return type_._from_place_(BlockPlace(block, offset))
sonolus/script/print.py CHANGED
@@ -1,81 +1,102 @@
1
- from enum import IntEnum
2
-
3
- from sonolus.backend.ops import Op
4
- from sonolus.script.internal.native import native_function
5
- from sonolus.script.runtime import HorizontalAlign
6
- from sonolus.script.vec import Vec2
7
-
8
-
9
- class PrintFormat(IntEnum):
10
- NUMBER = 0
11
- PERCENTAGE = 1
12
- TIME = 10
13
- SCORE = 11
14
- BPM = 20
15
- TIMESCALE = 21
16
- BEAT_COUNT = 30
17
- MEASURE_COUNT = 31
18
- ENTITY_COUNT = 32
19
-
20
-
21
- class PrintColor(IntEnum):
22
- THEME = -1
23
- NEUTRAL = 0
24
- RED = 1
25
- GREEN = 2
26
- BLUE = 3
27
- YELLOW = 4
28
- PURPLE = 5
29
- CYAN = 6
30
-
31
-
32
- @native_function(Op.Print)
33
- def _print(
34
- value: int | float,
35
- format: PrintFormat, # noqa: A002
36
- decimal_places: int,
37
- anchor_x: float,
38
- anchor_y: float,
39
- pivot_x: float,
40
- pivot_y: float,
41
- width: float,
42
- height: float,
43
- rotation: float,
44
- color: PrintColor,
45
- alpha: float,
46
- horizontal_align: HorizontalAlign,
47
- background: bool,
48
- ):
49
- raise NotImplementedError
50
-
51
-
52
- def print_number(
53
- value: int | float,
54
- *,
55
- fmt: PrintFormat,
56
- decimal_places: int = 0,
57
- anchor: Vec2,
58
- pivot: Vec2,
59
- dimensions: Vec2,
60
- rotation: float = 0,
61
- color: PrintColor = PrintColor.THEME,
62
- alpha: float = 1,
63
- horizontal_align: HorizontalAlign = HorizontalAlign.LEFT,
64
- background: bool = False,
65
- ):
66
- _print(
67
- value,
68
- fmt,
69
- decimal_places,
70
- anchor.x,
71
- anchor.y,
72
- pivot.x,
73
- pivot.y,
74
- dimensions.x,
75
- dimensions.y,
76
- rotation,
77
- color,
78
- alpha,
79
- horizontal_align,
80
- background,
81
- )
1
+ from enum import IntEnum
2
+
3
+ from sonolus.backend.ops import Op
4
+ from sonolus.script.internal.native import native_function
5
+ from sonolus.script.runtime import HorizontalAlign
6
+ from sonolus.script.vec import Vec2
7
+
8
+
9
+ class PrintFormat(IntEnum):
10
+ """Print format."""
11
+
12
+ NUMBER = 0
13
+ PERCENTAGE = 1
14
+ TIME = 10
15
+ SCORE = 11
16
+ BPM = 20
17
+ TIMESCALE = 21
18
+ BEAT_COUNT = 30
19
+ MEASURE_COUNT = 31
20
+ ENTITY_COUNT = 32
21
+
22
+
23
+ class PrintColor(IntEnum):
24
+ """Print color."""
25
+
26
+ THEME = -1
27
+ NEUTRAL = 0
28
+ RED = 1
29
+ GREEN = 2
30
+ BLUE = 3
31
+ YELLOW = 4
32
+ PURPLE = 5
33
+ CYAN = 6
34
+
35
+
36
+ @native_function(Op.Print)
37
+ def _print(
38
+ value: int | float,
39
+ format: PrintFormat, # noqa: A002
40
+ decimal_places: int,
41
+ anchor_x: float,
42
+ anchor_y: float,
43
+ pivot_x: float,
44
+ pivot_y: float,
45
+ width: float,
46
+ height: float,
47
+ rotation: float,
48
+ color: PrintColor,
49
+ alpha: float,
50
+ horizontal_align: HorizontalAlign,
51
+ background: bool,
52
+ ) -> None:
53
+ raise NotImplementedError
54
+
55
+
56
+ def print_number(
57
+ value: int | float,
58
+ *,
59
+ fmt: PrintFormat,
60
+ decimal_places: int = 0,
61
+ anchor: Vec2,
62
+ pivot: Vec2,
63
+ dimensions: Vec2,
64
+ rotation: float = 0,
65
+ color: PrintColor = PrintColor.THEME,
66
+ alpha: float = 1,
67
+ horizontal_align: HorizontalAlign = HorizontalAlign.LEFT,
68
+ background: bool = False,
69
+ ):
70
+ """Print a number.
71
+
72
+ Only supported in preview mode.
73
+
74
+ Args:
75
+ value: The value to print.
76
+ fmt: The print format.
77
+ decimal_places: The number of decimal places.
78
+ anchor: The anchor.
79
+ pivot: The pivot.
80
+ dimensions: The dimensions.
81
+ rotation: The rotation.
82
+ color: The color.
83
+ alpha: The alpha.
84
+ horizontal_align: The horizontal alignment.
85
+ background: Whether to show a background.
86
+ """
87
+ _print(
88
+ value,
89
+ fmt,
90
+ decimal_places,
91
+ anchor.x,
92
+ anchor.y,
93
+ pivot.x,
94
+ pivot.y,
95
+ dimensions.x,
96
+ dimensions.y,
97
+ rotation,
98
+ color,
99
+ alpha,
100
+ horizontal_align,
101
+ background,
102
+ )
sonolus/script/project.py CHANGED
@@ -6,6 +6,14 @@ from sonolus.script.level import Level
6
6
 
7
7
 
8
8
  class Project:
9
+ """A Sonolus.py project.
10
+
11
+ Args:
12
+ engine: The engine of the project.
13
+ levels: The levels of the project.
14
+ resources: The path to the resources of the project.
15
+ """
16
+
9
17
  def __init__(
10
18
  self,
11
19
  engine: Engine,