sonolus.py 0.1.0__py3-none-any.whl → 0.1.2__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/mode.py +4 -4
- sonolus/backend/visitor.py +2 -2
- sonolus/build/compile.py +3 -3
- sonolus/build/engine.py +20 -4
- sonolus/script/archetype.py +42 -11
- sonolus/script/bucket.py +4 -5
- sonolus/script/callbacks.py +10 -0
- sonolus/script/comptime.py +14 -0
- sonolus/script/debug.py +1 -1
- sonolus/script/effect.py +13 -14
- sonolus/script/engine.py +26 -6
- sonolus/script/globals.py +79 -44
- sonolus/script/graphics.py +37 -28
- sonolus/script/internal/builtin_impls.py +3 -3
- sonolus/script/internal/native.py +2 -2
- sonolus/script/interval.py +14 -0
- sonolus/script/level.py +7 -7
- sonolus/script/num.py +30 -4
- sonolus/script/options.py +6 -9
- sonolus/script/particle.py +50 -51
- sonolus/script/pointer.py +3 -3
- sonolus/script/preview.py +81 -0
- sonolus/script/runtime.py +113 -35
- sonolus/script/sprite.py +108 -107
- sonolus/script/text.py +407 -404
- sonolus/script/transform.py +13 -17
- sonolus/script/vec.py +24 -0
- {sonolus_py-0.1.0.dist-info → sonolus_py-0.1.2.dist-info}/METADATA +1 -1
- {sonolus_py-0.1.0.dist-info → sonolus_py-0.1.2.dist-info}/RECORD +32 -31
- {sonolus_py-0.1.0.dist-info → sonolus_py-0.1.2.dist-info}/WHEEL +0 -0
- {sonolus_py-0.1.0.dist-info → sonolus_py-0.1.2.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.1.0.dist-info → sonolus_py-0.1.2.dist-info}/licenses/LICENSE +0 -0
sonolus/script/graphics.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import Protocol
|
|
3
|
+
from typing import Protocol, Self
|
|
4
4
|
|
|
5
5
|
from sonolus.script.record import Record
|
|
6
6
|
from sonolus.script.vec import Vec2
|
|
@@ -20,21 +20,12 @@ class Rect(Record):
|
|
|
20
20
|
l: float # noqa: E741
|
|
21
21
|
|
|
22
22
|
@classmethod
|
|
23
|
-
def
|
|
23
|
+
def from_center(cls, center: Vec2, dimensions: Vec2) -> Rect:
|
|
24
24
|
return cls(
|
|
25
|
-
t=y +
|
|
26
|
-
r=x +
|
|
27
|
-
b=y,
|
|
28
|
-
l=x,
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
@classmethod
|
|
32
|
-
def from_center(cls, x: float, y: float, w: float, h: float) -> Rect:
|
|
33
|
-
return cls(
|
|
34
|
-
t=y + h / 2,
|
|
35
|
-
r=x + w / 2,
|
|
36
|
-
b=y - h / 2,
|
|
37
|
-
l=x - w / 2,
|
|
25
|
+
t=center.y + dimensions.y / 2,
|
|
26
|
+
r=center.x + dimensions.x / 2,
|
|
27
|
+
b=center.y - dimensions.y / 2,
|
|
28
|
+
l=center.x - dimensions.x / 2,
|
|
38
29
|
)
|
|
39
30
|
|
|
40
31
|
@property
|
|
@@ -99,19 +90,37 @@ class Rect(Record):
|
|
|
99
90
|
br=self.br,
|
|
100
91
|
)
|
|
101
92
|
|
|
102
|
-
def scale(self,
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
def
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
93
|
+
def scale(self, factor: Vec2, /) -> Self:
|
|
94
|
+
return Rect(
|
|
95
|
+
t=self.t * factor.y,
|
|
96
|
+
r=self.r * factor.x,
|
|
97
|
+
b=self.b * factor.y,
|
|
98
|
+
l=self.l * factor.x,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
def expand(self, expansion: Vec2, /) -> Self:
|
|
102
|
+
return Rect(
|
|
103
|
+
t=self.t + expansion.y,
|
|
104
|
+
r=self.r + expansion.x,
|
|
105
|
+
b=self.b - expansion.y,
|
|
106
|
+
l=self.l - expansion.x,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def shrink(self, shrinkage: Vec2, /) -> Self:
|
|
110
|
+
return Rect(
|
|
111
|
+
t=self.t - shrinkage.y,
|
|
112
|
+
r=self.r - shrinkage.x,
|
|
113
|
+
b=self.b + shrinkage.y,
|
|
114
|
+
l=self.l + shrinkage.x,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def translate(self, translation: Vec2, /) -> Self:
|
|
118
|
+
return Rect(
|
|
119
|
+
t=self.t + translation.y,
|
|
120
|
+
r=self.r + translation.x,
|
|
121
|
+
b=self.b + translation.y,
|
|
122
|
+
l=self.l + translation.x,
|
|
123
|
+
)
|
|
115
124
|
|
|
116
125
|
|
|
117
126
|
class QuadLike(Protocol):
|
|
@@ -5,7 +5,7 @@ from sonolus.script.internal.context import ctx
|
|
|
5
5
|
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
6
6
|
from sonolus.script.iterator import ArrayLike, Enumerator, SonolusIterator
|
|
7
7
|
from sonolus.script.math import MATH_BUILTIN_IMPLS
|
|
8
|
-
from sonolus.script.num import
|
|
8
|
+
from sonolus.script.num import is_num
|
|
9
9
|
from sonolus.script.range import Range
|
|
10
10
|
|
|
11
11
|
|
|
@@ -74,7 +74,7 @@ def _max(*args):
|
|
|
74
74
|
else:
|
|
75
75
|
raise TypeError(f"Unsupported type: {type(iterable)} for max")
|
|
76
76
|
else:
|
|
77
|
-
if not all(
|
|
77
|
+
if not all(is_num(arg) for arg in args):
|
|
78
78
|
raise TypeError("Arguments to max must be numbers")
|
|
79
79
|
if ctx():
|
|
80
80
|
result = compile_and_call(_max2, args[0], args[1])
|
|
@@ -114,7 +114,7 @@ def _min(*args):
|
|
|
114
114
|
else:
|
|
115
115
|
raise TypeError(f"Unsupported type: {type(iterable)} for min")
|
|
116
116
|
else:
|
|
117
|
-
if not all(
|
|
117
|
+
if not all(is_num(arg) for arg in args):
|
|
118
118
|
raise TypeError("Arguments to min must be numbers")
|
|
119
119
|
if ctx():
|
|
120
120
|
result = compile_and_call(_min2, args[0], args[1])
|
|
@@ -6,14 +6,14 @@ from sonolus.backend.ir import IRInstr, IRPureInstr, IRSet
|
|
|
6
6
|
from sonolus.backend.ops import Op
|
|
7
7
|
from sonolus.script.internal.context import ctx
|
|
8
8
|
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
9
|
-
from sonolus.script.num import Num
|
|
9
|
+
from sonolus.script.num import Num, is_num
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def native_call(op: Op, *args: Num) -> Num:
|
|
13
13
|
if not ctx():
|
|
14
14
|
raise RuntimeError("Unexpected native call")
|
|
15
15
|
args = tuple(validate_value(arg) for arg in args)
|
|
16
|
-
if not all(
|
|
16
|
+
if not all(is_num(arg) for arg in args):
|
|
17
17
|
raise RuntimeError("All arguments must be of type Num")
|
|
18
18
|
result = ctx().alloc(size=1)
|
|
19
19
|
ctx().add_statements(IRSet(result, (IRPureInstr if op.pure else IRInstr)(op, [arg.ir() for arg in args])))
|
sonolus/script/interval.py
CHANGED
|
@@ -55,6 +55,12 @@ class Interval(Record):
|
|
|
55
55
|
def __and__(self, other: Self) -> Self:
|
|
56
56
|
return Interval(max(self.start, other.start), min(self.end, other.end))
|
|
57
57
|
|
|
58
|
+
def shrink(self, value: float | int) -> Self:
|
|
59
|
+
return Interval(self.start + value, self.end - value)
|
|
60
|
+
|
|
61
|
+
def expand(self, value: float | int) -> Self:
|
|
62
|
+
return Interval(self.start - value, self.end + value)
|
|
63
|
+
|
|
58
64
|
def lerp(self, x: float, /) -> float:
|
|
59
65
|
return lerp(self.start, self.end, x)
|
|
60
66
|
|
|
@@ -67,6 +73,9 @@ class Interval(Record):
|
|
|
67
73
|
def unlerp_clamped(self, x: float, /) -> float:
|
|
68
74
|
return unlerp_clamped(self.start, self.end, x)
|
|
69
75
|
|
|
76
|
+
def clamp(self, x: float, /) -> float:
|
|
77
|
+
return clamp(x, self.start, self.end)
|
|
78
|
+
|
|
70
79
|
|
|
71
80
|
@native_function(Op.Lerp)
|
|
72
81
|
def lerp(a, b, x, /):
|
|
@@ -96,3 +105,8 @@ def remap(a, b, c, d, x, /):
|
|
|
96
105
|
@native_function(Op.RemapClamped)
|
|
97
106
|
def remap_clamped(a, b, c, d, x, /):
|
|
98
107
|
return c + (d - c) * max(0, min(1, (x - a) / (b - a)))
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@native_function(Op.Clamp)
|
|
111
|
+
def clamp(x, a, b, /):
|
|
112
|
+
return max(a, min(b, x))
|
sonolus/script/level.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from sonolus.build.collection import Asset
|
|
4
|
-
from sonolus.script.archetype import PlayArchetype, StandardImport
|
|
4
|
+
from sonolus.script.archetype import PlayArchetype, StandardArchetypeName, StandardImport
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class Level:
|
|
@@ -39,14 +39,14 @@ class LevelData:
|
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
class BpmChange(PlayArchetype):
|
|
42
|
-
name =
|
|
42
|
+
name = StandardArchetypeName.BPM_CHANGE
|
|
43
43
|
|
|
44
|
-
beat: StandardImport.
|
|
45
|
-
bpm: StandardImport.
|
|
44
|
+
beat: StandardImport.BEAT
|
|
45
|
+
bpm: StandardImport.BPM
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
class TimescaleChange(PlayArchetype):
|
|
49
|
-
name =
|
|
49
|
+
name = StandardArchetypeName.TIMESCALE_CHANGE
|
|
50
50
|
|
|
51
|
-
beat: StandardImport.
|
|
52
|
-
timescale: StandardImport.
|
|
51
|
+
beat: StandardImport.BEAT
|
|
52
|
+
timescale: StandardImport.TIMESCALE
|
sonolus/script/num.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# ruff: noqa: N801
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
2
4
|
import operator
|
|
3
5
|
from collections.abc import Callable, Iterable
|
|
4
|
-
from typing import TYPE_CHECKING, Any, Self, final
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Self, TypeIs, final
|
|
5
7
|
|
|
6
8
|
from sonolus.backend.ir import IRConst, IRGet, IRPureInstr, IRSet
|
|
7
9
|
from sonolus.backend.ops import Op
|
|
@@ -12,14 +14,30 @@ from sonolus.script.internal.impl import meta_fn
|
|
|
12
14
|
from sonolus.script.internal.value import Value
|
|
13
15
|
|
|
14
16
|
|
|
17
|
+
class NumMeta(type):
|
|
18
|
+
def __instancecheck__(cls, instance):
|
|
19
|
+
return isinstance(instance, float | int | bool) or is_num(instance)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def is_num(value: Any) -> TypeIs[Num]:
|
|
23
|
+
"""Check if a value is a precisely Num instance."""
|
|
24
|
+
return type.__instancecheck__(Num, value) # type: ignore # noqa: PLC2801
|
|
25
|
+
|
|
26
|
+
|
|
15
27
|
@final
|
|
16
|
-
class _Num(Value):
|
|
28
|
+
class _Num(Value, metaclass=NumMeta):
|
|
29
|
+
# This works for ints, floats, and bools
|
|
30
|
+
# Since we don't support complex numbers, real is equal to the original number
|
|
31
|
+
__match_args__ = ("real",)
|
|
32
|
+
|
|
17
33
|
data: BlockPlace | float | int | bool
|
|
18
34
|
|
|
19
35
|
def __init__(self, data: Place | float | int | bool):
|
|
36
|
+
if isinstance(data, complex):
|
|
37
|
+
raise TypeError("Cannot create a Num from a complex number")
|
|
20
38
|
if isinstance(data, int):
|
|
21
39
|
data = float(data)
|
|
22
|
-
if
|
|
40
|
+
if is_num(data):
|
|
23
41
|
raise InternalError("Cannot create a Num from a Num")
|
|
24
42
|
self.data = data
|
|
25
43
|
|
|
@@ -55,7 +73,7 @@ class _Num(Value):
|
|
|
55
73
|
def _accept_(cls, value: Any) -> Self:
|
|
56
74
|
if not cls._accepts_(value):
|
|
57
75
|
raise TypeError(f"Cannot accept {value}")
|
|
58
|
-
if
|
|
76
|
+
if is_num(value):
|
|
59
77
|
return value
|
|
60
78
|
return cls(value)
|
|
61
79
|
|
|
@@ -368,6 +386,14 @@ class _Num(Value):
|
|
|
368
386
|
def not_(self) -> Self:
|
|
369
387
|
return self._unary_op(operator.not_, Op.Not)
|
|
370
388
|
|
|
389
|
+
@property
|
|
390
|
+
def real(self) -> Self:
|
|
391
|
+
return self
|
|
392
|
+
|
|
393
|
+
@property
|
|
394
|
+
def imag(self) -> Self:
|
|
395
|
+
return 0
|
|
396
|
+
|
|
371
397
|
|
|
372
398
|
if TYPE_CHECKING:
|
|
373
399
|
|
sonolus/script/options.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ruff: noqa: A002
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from typing import Annotated, Any,
|
|
3
|
+
from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
4
4
|
|
|
5
5
|
from sonolus.backend.mode import Mode
|
|
6
6
|
from sonolus.backend.place import BlockPlace
|
|
@@ -124,10 +124,7 @@ def select_option(
|
|
|
124
124
|
return SelectOption(name, standard, advanced, scope, default, values)
|
|
125
125
|
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
_options_: ClassVar[list[SliderOption | ToggleOption | SelectOption]]
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
type Options = NewType("Options", Any)
|
|
131
128
|
type OptionInfo = SliderOption | ToggleOption | SelectOption
|
|
132
129
|
|
|
133
130
|
|
|
@@ -142,13 +139,13 @@ class OptionField(SonolusDescriptor):
|
|
|
142
139
|
def __get__(self, instance, owner):
|
|
143
140
|
if ctx():
|
|
144
141
|
match ctx().global_state.mode:
|
|
145
|
-
case Mode.
|
|
142
|
+
case Mode.PLAY:
|
|
146
143
|
block = ctx().blocks.LevelOption
|
|
147
|
-
case Mode.
|
|
144
|
+
case Mode.WATCH:
|
|
148
145
|
block = ctx().blocks.LevelOption
|
|
149
|
-
case Mode.
|
|
146
|
+
case Mode.PREVIEW:
|
|
150
147
|
block = ctx().blocks.PreviewOption
|
|
151
|
-
case Mode.
|
|
148
|
+
case Mode.TUTORIAL:
|
|
152
149
|
block = None
|
|
153
150
|
case _:
|
|
154
151
|
assert_unreachable()
|
sonolus/script/particle.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import Annotated, Any,
|
|
4
|
+
from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
5
5
|
|
|
6
6
|
from sonolus.backend.ops import Op
|
|
7
7
|
from sonolus.script.graphics import QuadLike, flatten_quad
|
|
@@ -73,8 +73,7 @@ def particle(name: str) -> Any:
|
|
|
73
73
|
return ParticleInfo(name)
|
|
74
74
|
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
_particles_: list[str]
|
|
76
|
+
type Particles = NewType("Particles", Any)
|
|
78
77
|
|
|
79
78
|
|
|
80
79
|
@dataclass_transform()
|
|
@@ -103,54 +102,54 @@ def particles[T](cls: type[T]) -> T | Particles:
|
|
|
103
102
|
|
|
104
103
|
|
|
105
104
|
class StandardParticle:
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
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")]
|
|
154
153
|
|
|
155
154
|
|
|
156
155
|
@particles
|
sonolus/script/pointer.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from sonolus.backend.place import BlockPlace
|
|
2
2
|
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
3
3
|
from sonolus.script.internal.value import Value
|
|
4
|
-
from sonolus.script.num import Num
|
|
4
|
+
from sonolus.script.num import Num, is_num
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
@meta_fn
|
|
@@ -14,7 +14,7 @@ def deref[T: Value](block: Num, offset: Num, type_: type[T]) -> T:
|
|
|
14
14
|
if not isinstance(block, int):
|
|
15
15
|
raise TypeError("block must be an integer")
|
|
16
16
|
else:
|
|
17
|
-
if not
|
|
17
|
+
if not is_num(block):
|
|
18
18
|
raise TypeError("block must be a Num")
|
|
19
19
|
block = block.index()
|
|
20
20
|
if offset._is_py_():
|
|
@@ -22,7 +22,7 @@ def deref[T: Value](block: Num, offset: Num, type_: type[T]) -> T:
|
|
|
22
22
|
if not isinstance(offset, int):
|
|
23
23
|
raise TypeError("offset must be an integer")
|
|
24
24
|
else:
|
|
25
|
-
if not
|
|
25
|
+
if not is_num(offset):
|
|
26
26
|
raise TypeError("offset must be a Num")
|
|
27
27
|
offset = offset.index()
|
|
28
28
|
if not (isinstance(type_, type) and issubclass(type_, Value)):
|
|
@@ -0,0 +1,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
|
+
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
|
+
)
|