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.
- sonolus/backend/blocks.py +756 -756
- sonolus/backend/excepthook.py +37 -37
- sonolus/backend/finalize.py +77 -69
- sonolus/backend/interpret.py +7 -7
- sonolus/backend/ir.py +29 -3
- sonolus/backend/mode.py +24 -24
- sonolus/backend/node.py +40 -40
- sonolus/backend/ops.py +197 -197
- sonolus/backend/optimize/__init__.py +0 -0
- sonolus/backend/optimize/allocate.py +126 -0
- sonolus/backend/optimize/constant_evaluation.py +374 -0
- sonolus/backend/optimize/copy_coalesce.py +85 -0
- sonolus/backend/optimize/dead_code.py +185 -0
- sonolus/backend/optimize/dominance.py +96 -0
- sonolus/backend/{flow.py → optimize/flow.py} +122 -92
- sonolus/backend/optimize/inlining.py +137 -0
- sonolus/backend/optimize/liveness.py +177 -0
- sonolus/backend/optimize/optimize.py +44 -0
- sonolus/backend/optimize/passes.py +52 -0
- sonolus/backend/optimize/simplify.py +191 -0
- sonolus/backend/optimize/ssa.py +200 -0
- sonolus/backend/place.py +17 -25
- sonolus/backend/utils.py +58 -48
- sonolus/backend/visitor.py +1151 -882
- sonolus/build/cli.py +7 -1
- sonolus/build/compile.py +88 -90
- sonolus/build/engine.py +10 -5
- sonolus/build/level.py +24 -23
- sonolus/build/node.py +43 -43
- sonolus/script/archetype.py +438 -139
- sonolus/script/array.py +27 -10
- sonolus/script/array_like.py +297 -0
- sonolus/script/bucket.py +253 -191
- sonolus/script/containers.py +257 -51
- sonolus/script/debug.py +26 -10
- sonolus/script/easing.py +365 -0
- sonolus/script/effect.py +191 -131
- sonolus/script/engine.py +71 -4
- sonolus/script/globals.py +303 -269
- sonolus/script/instruction.py +205 -151
- sonolus/script/internal/__init__.py +5 -5
- sonolus/script/internal/builtin_impls.py +255 -144
- sonolus/script/{callbacks.py → internal/callbacks.py} +127 -127
- sonolus/script/internal/constant.py +139 -0
- sonolus/script/internal/context.py +26 -9
- sonolus/script/internal/descriptor.py +17 -17
- sonolus/script/internal/dict_impl.py +65 -0
- sonolus/script/internal/generic.py +6 -9
- sonolus/script/internal/impl.py +38 -13
- sonolus/script/internal/introspection.py +17 -14
- sonolus/script/internal/math_impls.py +121 -0
- sonolus/script/internal/native.py +40 -38
- sonolus/script/internal/random.py +67 -0
- sonolus/script/internal/range.py +81 -0
- sonolus/script/internal/transient.py +51 -0
- sonolus/script/internal/tuple_impl.py +113 -0
- sonolus/script/internal/value.py +3 -3
- sonolus/script/interval.py +338 -112
- sonolus/script/iterator.py +167 -214
- sonolus/script/level.py +24 -0
- sonolus/script/num.py +80 -48
- sonolus/script/options.py +257 -191
- sonolus/script/particle.py +190 -157
- sonolus/script/pointer.py +30 -30
- sonolus/script/print.py +102 -81
- sonolus/script/project.py +8 -0
- sonolus/script/quad.py +263 -0
- sonolus/script/record.py +47 -16
- sonolus/script/runtime.py +52 -1
- sonolus/script/sprite.py +418 -333
- sonolus/script/text.py +409 -407
- sonolus/script/timing.py +114 -42
- sonolus/script/transform.py +332 -48
- sonolus/script/ui.py +216 -160
- sonolus/script/values.py +6 -13
- sonolus/script/vec.py +196 -78
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/METADATA +1 -1
- sonolus_py-0.1.5.dist-info/RECORD +89 -0
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/WHEEL +1 -1
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/licenses/LICENSE +21 -21
- sonolus/backend/allocate.py +0 -51
- sonolus/backend/optimize.py +0 -9
- sonolus/backend/passes.py +0 -6
- sonolus/backend/simplify.py +0 -30
- sonolus/script/comptime.py +0 -160
- sonolus/script/graphics.py +0 -150
- sonolus/script/math.py +0 -92
- sonolus/script/range.py +0 -58
- sonolus_py-0.1.3.dist-info/RECORD +0 -75
- {sonolus_py-0.1.3.dist-info → sonolus_py-0.1.5.dist-info}/entry_points.txt +0 -0
sonolus/script/quad.py
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Protocol, Self
|
|
4
|
+
|
|
5
|
+
from sonolus.script.record import Record
|
|
6
|
+
from sonolus.script.vec import Vec2
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Quad(Record):
|
|
10
|
+
"""A quad defined by its four corners.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
```
|
|
14
|
+
Quad(bl: Vec2, tl: Vec2, tr: Vec2, br: Vec2)
|
|
15
|
+
```
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
bl: Vec2
|
|
19
|
+
"""The bottom-left corner of the quad."""
|
|
20
|
+
|
|
21
|
+
tl: Vec2
|
|
22
|
+
"""The top-left corner of the quad."""
|
|
23
|
+
|
|
24
|
+
tr: Vec2
|
|
25
|
+
"""The top-right corner of the quad."""
|
|
26
|
+
|
|
27
|
+
br: Vec2
|
|
28
|
+
"""The bottom-right corner of the quad."""
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def center(self) -> Vec2:
|
|
32
|
+
"""The center of the quad."""
|
|
33
|
+
return (self.bl + self.tr + self.tl + self.br) / 4
|
|
34
|
+
|
|
35
|
+
def translate(self, translation: Vec2, /) -> Self:
|
|
36
|
+
"""Translate the quad by the given translation and return a new quad."""
|
|
37
|
+
return Quad(
|
|
38
|
+
bl=self.bl + translation,
|
|
39
|
+
tl=self.tl + translation,
|
|
40
|
+
tr=self.tr + translation,
|
|
41
|
+
br=self.br + translation,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def scale(self, factor: Vec2, /) -> Self:
|
|
45
|
+
"""Scale the quad by the given factor about the origin and return a new quad."""
|
|
46
|
+
return Quad(
|
|
47
|
+
bl=self.bl * factor,
|
|
48
|
+
tl=self.tl * factor,
|
|
49
|
+
tr=self.tr * factor,
|
|
50
|
+
br=self.br * factor,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def scale_about(self, factor: Vec2, /, pivot: Vec2) -> Self:
|
|
54
|
+
"""Scale the quad by the given factor about the given pivot and return a new quad."""
|
|
55
|
+
return Quad(
|
|
56
|
+
bl=(self.bl - pivot) * factor + pivot,
|
|
57
|
+
tl=(self.tl - pivot) * factor + pivot,
|
|
58
|
+
tr=(self.tr - pivot) * factor + pivot,
|
|
59
|
+
br=(self.br - pivot) * factor + pivot,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def scale_centered(self, factor: Vec2, /) -> Self:
|
|
63
|
+
"""Scale the quad by the given factor about its center and return a new quad."""
|
|
64
|
+
return Quad(
|
|
65
|
+
bl=self.bl * factor,
|
|
66
|
+
tl=self.tl * factor,
|
|
67
|
+
tr=self.tr * factor,
|
|
68
|
+
br=self.br * factor,
|
|
69
|
+
).translate(self.center * (Vec2(1, 1) - factor))
|
|
70
|
+
|
|
71
|
+
def rotate(self, angle: float, /) -> Self:
|
|
72
|
+
"""Rotate the quad by the given angle about the origin and return a new quad."""
|
|
73
|
+
return Quad(
|
|
74
|
+
bl=self.bl.rotate(angle),
|
|
75
|
+
tl=self.tl.rotate(angle),
|
|
76
|
+
tr=self.tr.rotate(angle),
|
|
77
|
+
br=self.br.rotate(angle),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def rotate_about(
|
|
81
|
+
self,
|
|
82
|
+
angle: float,
|
|
83
|
+
/,
|
|
84
|
+
pivot: Vec2,
|
|
85
|
+
) -> Self:
|
|
86
|
+
"""Rotate the quad by the given angle about the given pivot and return a new quad."""
|
|
87
|
+
return Quad(
|
|
88
|
+
bl=self.bl.rotate_about(angle, pivot),
|
|
89
|
+
tl=self.tl.rotate_about(angle, pivot),
|
|
90
|
+
tr=self.tr.rotate_about(angle, pivot),
|
|
91
|
+
br=self.br.rotate_about(angle, pivot),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def rotate_centered(self, angle: float, /) -> Self:
|
|
95
|
+
"""Rotate the quad by the given angle about its center and return a new quad."""
|
|
96
|
+
return self.rotate_about(angle, self.center)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class Rect(Record):
|
|
100
|
+
"""A rectangle defined by its top, right, bottom, and left edges.
|
|
101
|
+
|
|
102
|
+
Usage:
|
|
103
|
+
```
|
|
104
|
+
Rect(t: float, r: float, b: float, l: float)
|
|
105
|
+
```
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
t: float
|
|
109
|
+
"""The top edge of the rectangle."""
|
|
110
|
+
|
|
111
|
+
r: float
|
|
112
|
+
"""The right edge of the rectangle."""
|
|
113
|
+
|
|
114
|
+
b: float
|
|
115
|
+
"""The bottom edge of the rectangle."""
|
|
116
|
+
|
|
117
|
+
l: float # noqa: E741
|
|
118
|
+
"""The left edge of the rectangle."""
|
|
119
|
+
|
|
120
|
+
@classmethod
|
|
121
|
+
def from_center(cls, center: Vec2, dimensions: Vec2) -> Rect:
|
|
122
|
+
"""Create a rectangle from its center and dimensions."""
|
|
123
|
+
return cls(
|
|
124
|
+
t=center.y + dimensions.y / 2,
|
|
125
|
+
r=center.x + dimensions.x / 2,
|
|
126
|
+
b=center.y - dimensions.y / 2,
|
|
127
|
+
l=center.x - dimensions.x / 2,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def w(self) -> float:
|
|
132
|
+
"""The width of the rectangle."""
|
|
133
|
+
return self.r - self.l
|
|
134
|
+
|
|
135
|
+
@property
|
|
136
|
+
def h(self) -> float:
|
|
137
|
+
"""The height of the rectangle."""
|
|
138
|
+
return self.t - self.b
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def bl(self) -> Vec2:
|
|
142
|
+
"""The bottom-left corner of the rectangle."""
|
|
143
|
+
return Vec2(self.l, self.b)
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def tl(self) -> Vec2:
|
|
147
|
+
"""The top-left corner of the rectangle."""
|
|
148
|
+
return Vec2(self.l, self.t)
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def tr(self) -> Vec2:
|
|
152
|
+
"""The top-right corner of the rectangle."""
|
|
153
|
+
return Vec2(self.r, self.t)
|
|
154
|
+
|
|
155
|
+
@property
|
|
156
|
+
def br(self) -> Vec2:
|
|
157
|
+
"""The bottom-right corner of the rectangle."""
|
|
158
|
+
return Vec2(self.r, self.b)
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def center(self) -> Vec2:
|
|
162
|
+
"""The center of the rectangle."""
|
|
163
|
+
return Vec2((self.l + self.r) / 2, (self.t + self.b) / 2)
|
|
164
|
+
|
|
165
|
+
def as_quad(self) -> Quad:
|
|
166
|
+
"""Convert the rectangle to a quad."""
|
|
167
|
+
return Quad(
|
|
168
|
+
bl=self.bl,
|
|
169
|
+
tl=self.tl,
|
|
170
|
+
tr=self.tr,
|
|
171
|
+
br=self.br,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
def translate(self, translation: Vec2, /) -> Self:
|
|
175
|
+
"""Translate the rectangle by the given translation and return a new rectangle."""
|
|
176
|
+
return Rect(
|
|
177
|
+
t=self.t + translation.y,
|
|
178
|
+
r=self.r + translation.x,
|
|
179
|
+
b=self.b + translation.y,
|
|
180
|
+
l=self.l + translation.x,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
def scale(self, factor: Vec2, /) -> Self:
|
|
184
|
+
"""Scale the rectangle by the given factor about the origin and return a new rectangle."""
|
|
185
|
+
return Rect(
|
|
186
|
+
t=self.t * factor.y,
|
|
187
|
+
r=self.r * factor.x,
|
|
188
|
+
b=self.b * factor.y,
|
|
189
|
+
l=self.l * factor.x,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
def scale_about(self, factor: Vec2, /, pivot: Vec2) -> Self:
|
|
193
|
+
"""Scale the rectangle by the given factor about the given pivot and return a new rectangle."""
|
|
194
|
+
return Rect(
|
|
195
|
+
t=(self.t - pivot.y) * factor.y + pivot.y,
|
|
196
|
+
r=(self.r - pivot.x) * factor.x + pivot.x,
|
|
197
|
+
b=(self.b - pivot.y) * factor.y + pivot.y,
|
|
198
|
+
l=(self.l - pivot.x) * factor.x + pivot.x,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
def scale_centered(self, factor: Vec2, /) -> Self:
|
|
202
|
+
"""Scale the rectangle by the given factor about its center and return a new rectangle."""
|
|
203
|
+
return Rect(
|
|
204
|
+
t=self.t * factor.y,
|
|
205
|
+
r=self.r * factor.x,
|
|
206
|
+
b=self.b * factor.y,
|
|
207
|
+
l=self.l * factor.x,
|
|
208
|
+
).translate(self.center * (Vec2(1, 1) - factor))
|
|
209
|
+
|
|
210
|
+
def expand(self, expansion: Vec2, /) -> Self:
|
|
211
|
+
"""Expand the rectangle by the given amount and return a new rectangle."""
|
|
212
|
+
return Rect(
|
|
213
|
+
t=self.t + expansion.y,
|
|
214
|
+
r=self.r + expansion.x,
|
|
215
|
+
b=self.b - expansion.y,
|
|
216
|
+
l=self.l - expansion.x,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
def shrink(self, shrinkage: Vec2, /) -> Self:
|
|
220
|
+
"""Shrink the rectangle by the given amount and return a new rectangle."""
|
|
221
|
+
return Rect(
|
|
222
|
+
t=self.t - shrinkage.y,
|
|
223
|
+
r=self.r - shrinkage.x,
|
|
224
|
+
b=self.b + shrinkage.y,
|
|
225
|
+
l=self.l + shrinkage.x,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class QuadLike(Protocol):
|
|
230
|
+
"""A protocol for types that can be used as quads."""
|
|
231
|
+
|
|
232
|
+
@property
|
|
233
|
+
def bl(self) -> Vec2:
|
|
234
|
+
"""The bottom-left corner of the quad."""
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def tl(self) -> Vec2:
|
|
238
|
+
"""The top-left corner of the quad."""
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def tr(self) -> Vec2:
|
|
242
|
+
"""The top-right corner of the quad."""
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def br(self) -> Vec2:
|
|
246
|
+
"""The bottom-right corner of the quad."""
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def flatten_quad(quad: QuadLike) -> tuple[float, float, float, float, float, float, float, float]:
|
|
250
|
+
bl = quad.bl
|
|
251
|
+
tl = quad.tl
|
|
252
|
+
tr = quad.tr
|
|
253
|
+
br = quad.br
|
|
254
|
+
return (
|
|
255
|
+
bl.x,
|
|
256
|
+
bl.y,
|
|
257
|
+
tl.x,
|
|
258
|
+
tl.y,
|
|
259
|
+
tr.x,
|
|
260
|
+
tr.y,
|
|
261
|
+
br.x,
|
|
262
|
+
br.y,
|
|
263
|
+
)
|
sonolus/script/record.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import inspect
|
|
4
4
|
from collections.abc import Iterable
|
|
5
5
|
from inspect import getmro
|
|
6
|
-
from typing import Any, ClassVar, Self, dataclass_transform, get_origin
|
|
6
|
+
from typing import Any, ClassVar, Self, TypeVar, dataclass_transform, get_origin
|
|
7
7
|
|
|
8
8
|
from sonolus.backend.place import BlockPlace
|
|
9
9
|
from sonolus.script.internal.context import ctx
|
|
@@ -22,15 +22,33 @@ from sonolus.script.num import Num
|
|
|
22
22
|
|
|
23
23
|
@dataclass_transform(eq_default=True)
|
|
24
24
|
class Record(GenericValue):
|
|
25
|
+
"""Base class for user-defined data structures.
|
|
26
|
+
|
|
27
|
+
Usage:
|
|
28
|
+
A regular record:
|
|
29
|
+
```python
|
|
30
|
+
class MyRecord(Record):
|
|
31
|
+
field1: int
|
|
32
|
+
field2: bool
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
A generic record:
|
|
36
|
+
```python
|
|
37
|
+
class MyGenericRecord[T, U](Record):
|
|
38
|
+
field1: T
|
|
39
|
+
field2: U
|
|
40
|
+
```
|
|
41
|
+
"""
|
|
42
|
+
|
|
25
43
|
_value: dict[str, Value]
|
|
26
|
-
_fields: ClassVar[list[
|
|
44
|
+
_fields: ClassVar[list[_RecordField] | None] = None
|
|
27
45
|
_constructor_signature: ClassVar[inspect.Signature]
|
|
28
46
|
|
|
29
47
|
@classmethod
|
|
30
|
-
def
|
|
48
|
+
def _validate_type_args_(cls, args: tuple[Any, ...]) -> tuple[Any, ...]:
|
|
31
49
|
if cls._fields is None:
|
|
32
50
|
raise TypeError("Base Record class cannot have type arguments")
|
|
33
|
-
return super().
|
|
51
|
+
return super()._validate_type_args_(args)
|
|
34
52
|
|
|
35
53
|
def __init_subclass__(cls, **kwargs):
|
|
36
54
|
super().__init_subclass__()
|
|
@@ -43,7 +61,7 @@ class Record(GenericValue):
|
|
|
43
61
|
for generic_field in cls._fields:
|
|
44
62
|
resolved_type = validate_and_resolve_type(generic_field.type, cls._type_vars_to_args_)
|
|
45
63
|
resolved_type = validate_concrete_type(resolved_type)
|
|
46
|
-
field =
|
|
64
|
+
field = _RecordField(generic_field.name, resolved_type, generic_field.index, offset)
|
|
47
65
|
fields.append(field)
|
|
48
66
|
setattr(cls, field.name, field)
|
|
49
67
|
offset += resolved_type._size_()
|
|
@@ -70,7 +88,7 @@ class Record(GenericValue):
|
|
|
70
88
|
if hasattr(cls, name):
|
|
71
89
|
raise TypeError("Default values are not supported for Record fields")
|
|
72
90
|
type_ = validate_type_spec(hint)
|
|
73
|
-
fields.append(
|
|
91
|
+
fields.append(_RecordField(name, type_, index, offset))
|
|
74
92
|
if isinstance(type_, type) and issubclass(type_, Value) and type_._is_concrete_():
|
|
75
93
|
offset += type_._size_()
|
|
76
94
|
setattr(cls, name, fields[-1])
|
|
@@ -111,7 +129,7 @@ class Record(GenericValue):
|
|
|
111
129
|
values[field.name] = value._get_()
|
|
112
130
|
for type_param in cls.__type_params__:
|
|
113
131
|
if type_param not in type_vars:
|
|
114
|
-
raise TypeError(f"Type parameter {type_param}
|
|
132
|
+
raise TypeError(f"Type parameter {type_param} cannot be inferred and must be provided explicitly")
|
|
115
133
|
type_args = tuple(type_vars[type_param] for type_param in cls.__type_params__)
|
|
116
134
|
if cls._type_args_ is not None:
|
|
117
135
|
parameterized = cls
|
|
@@ -168,10 +186,10 @@ class Record(GenericValue):
|
|
|
168
186
|
iterator = iter(values)
|
|
169
187
|
return cls(**{field.name: field.type._from_list_(iterator) for field in cls._fields})
|
|
170
188
|
|
|
171
|
-
def _to_list_(self) -> list[float | BlockPlace]:
|
|
189
|
+
def _to_list_(self, level_refs: dict[Any, int] | None = None) -> list[float | BlockPlace]:
|
|
172
190
|
result = []
|
|
173
191
|
for field in self._fields:
|
|
174
|
-
result.extend(self._value[field.name]._to_list_())
|
|
192
|
+
result.extend(self._value[field.name]._to_list_(level_refs))
|
|
175
193
|
return result
|
|
176
194
|
|
|
177
195
|
@classmethod
|
|
@@ -194,7 +212,7 @@ class Record(GenericValue):
|
|
|
194
212
|
field.__set__(self, field.__get__(value))
|
|
195
213
|
|
|
196
214
|
def _copy_(self) -> Self:
|
|
197
|
-
return type(self)(**{field.name: self._value[field.name]._copy_() for field in self._fields})
|
|
215
|
+
return type(self)._raw(**{field.name: self._value[field.name]._copy_() for field in self._fields})
|
|
198
216
|
|
|
199
217
|
@classmethod
|
|
200
218
|
def _alloc_(cls) -> Self:
|
|
@@ -214,7 +232,7 @@ class Record(GenericValue):
|
|
|
214
232
|
)
|
|
215
233
|
|
|
216
234
|
@meta_fn
|
|
217
|
-
def __eq__(self, other):
|
|
235
|
+
def __eq__(self, other: Any) -> bool:
|
|
218
236
|
if not isinstance(other, type(self)):
|
|
219
237
|
return False
|
|
220
238
|
result: Num = Num._accept_(True)
|
|
@@ -223,7 +241,7 @@ class Record(GenericValue):
|
|
|
223
241
|
return result
|
|
224
242
|
|
|
225
243
|
@meta_fn
|
|
226
|
-
def __ne__(self, other):
|
|
244
|
+
def __ne__(self, other: Any) -> bool:
|
|
227
245
|
if not isinstance(other, type(self)):
|
|
228
246
|
return True
|
|
229
247
|
result: Num = Num._accept_(False)
|
|
@@ -232,10 +250,23 @@ class Record(GenericValue):
|
|
|
232
250
|
return result
|
|
233
251
|
|
|
234
252
|
def __hash__(self):
|
|
235
|
-
|
|
253
|
+
return hash(tuple(field.__get__(self) for field in self._fields))
|
|
254
|
+
|
|
255
|
+
@classmethod
|
|
256
|
+
@meta_fn
|
|
257
|
+
def type_var_value(cls, var: TypeVar, /) -> Any:
|
|
258
|
+
"""Return the value of a type variable.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
var: The type variable to get the value of.
|
|
262
|
+
|
|
263
|
+
Returns:
|
|
264
|
+
The value of the type variable.
|
|
265
|
+
"""
|
|
266
|
+
return super().type_var_value(var)
|
|
236
267
|
|
|
237
268
|
|
|
238
|
-
class
|
|
269
|
+
class _RecordField(SonolusDescriptor):
|
|
239
270
|
def __init__(self, name: str, type_: type[Value], index: int, offset: int):
|
|
240
271
|
self.name = name
|
|
241
272
|
self.type = type_
|
|
@@ -259,7 +290,7 @@ class RecordField(SonolusDescriptor):
|
|
|
259
290
|
instance._value[self.name]._copy_from_(value)
|
|
260
291
|
|
|
261
292
|
|
|
262
|
-
|
|
293
|
+
_ops_to_inplace_ops = {
|
|
263
294
|
"__add__": "__iadd__",
|
|
264
295
|
"__sub__": "__isub__",
|
|
265
296
|
"__mul__": "__imul__",
|
|
@@ -277,7 +308,7 @@ ops_to_inplace_ops = {
|
|
|
277
308
|
|
|
278
309
|
|
|
279
310
|
def _add_inplace_ops(cls):
|
|
280
|
-
for op, inplace_op in
|
|
311
|
+
for op, inplace_op in _ops_to_inplace_ops.items():
|
|
281
312
|
if hasattr(cls, op) and not hasattr(cls, inplace_op):
|
|
282
313
|
setattr(cls, inplace_op, _make_inplace_op(op))
|
|
283
314
|
return cls
|
sonolus/script/runtime.py
CHANGED
|
@@ -28,10 +28,10 @@ from sonolus.script.globals import (
|
|
|
28
28
|
_watch_runtime_ui_configuration,
|
|
29
29
|
_watch_runtime_update,
|
|
30
30
|
)
|
|
31
|
-
from sonolus.script.graphics import Quad, Rect
|
|
32
31
|
from sonolus.script.internal.context import ctx
|
|
33
32
|
from sonolus.script.internal.impl import meta_fn
|
|
34
33
|
from sonolus.script.num import Num
|
|
34
|
+
from sonolus.script.quad import Quad, Rect
|
|
35
35
|
from sonolus.script.record import Record
|
|
36
36
|
from sonolus.script.transform import Transform2d
|
|
37
37
|
from sonolus.script.vec import Vec2
|
|
@@ -308,6 +308,7 @@ class _SkinTransform:
|
|
|
308
308
|
a12=Num(values[1 * 4 + 3]),
|
|
309
309
|
a20=Num(values[3 * 4 + 0]),
|
|
310
310
|
a21=Num(values[3 * 4 + 1]),
|
|
311
|
+
a22=Num(values[3 * 4 + 3]),
|
|
311
312
|
)
|
|
312
313
|
|
|
313
314
|
|
|
@@ -328,6 +329,7 @@ class _ParticleTransform:
|
|
|
328
329
|
a12=Num(values[1 * 4 + 3]),
|
|
329
330
|
a20=Num(values[3 * 4 + 0]),
|
|
330
331
|
a21=Num(values[3 * 4 + 1]),
|
|
332
|
+
a22=Num(values[3 * 4 + 3]),
|
|
331
333
|
)
|
|
332
334
|
|
|
333
335
|
|
|
@@ -431,6 +433,7 @@ class _TutorialInstruction:
|
|
|
431
433
|
|
|
432
434
|
@meta_fn
|
|
433
435
|
def is_debug() -> bool:
|
|
436
|
+
"""Check if the game is running in debug mode."""
|
|
434
437
|
if not ctx():
|
|
435
438
|
return False
|
|
436
439
|
match ctx().global_state.mode:
|
|
@@ -448,6 +451,7 @@ def is_debug() -> bool:
|
|
|
448
451
|
|
|
449
452
|
@meta_fn
|
|
450
453
|
def aspect_ratio() -> float:
|
|
454
|
+
"""Get the aspect ratio of the game."""
|
|
451
455
|
if not ctx():
|
|
452
456
|
return 16 / 9
|
|
453
457
|
match ctx().global_state.mode:
|
|
@@ -463,6 +467,10 @@ def aspect_ratio() -> float:
|
|
|
463
467
|
|
|
464
468
|
@meta_fn
|
|
465
469
|
def audio_offset() -> float:
|
|
470
|
+
"""Get the audio offset of the game.
|
|
471
|
+
|
|
472
|
+
Returns 0 in preview mode.
|
|
473
|
+
"""
|
|
466
474
|
if not ctx():
|
|
467
475
|
return 0
|
|
468
476
|
match ctx().global_state.mode:
|
|
@@ -478,6 +486,10 @@ def audio_offset() -> float:
|
|
|
478
486
|
|
|
479
487
|
@meta_fn
|
|
480
488
|
def input_offset() -> float:
|
|
489
|
+
"""Get the input offset of the game.
|
|
490
|
+
|
|
491
|
+
Returns 0 in preview mode and tutorial mode.
|
|
492
|
+
"""
|
|
481
493
|
if not ctx():
|
|
482
494
|
return 0
|
|
483
495
|
match ctx().global_state.mode:
|
|
@@ -491,6 +503,10 @@ def input_offset() -> float:
|
|
|
491
503
|
|
|
492
504
|
@meta_fn
|
|
493
505
|
def is_multiplayer() -> bool:
|
|
506
|
+
"""Check if the game is running in multiplayer mode.
|
|
507
|
+
|
|
508
|
+
Returns False if not in play mode.
|
|
509
|
+
"""
|
|
494
510
|
if not ctx():
|
|
495
511
|
return False
|
|
496
512
|
match ctx().global_state.mode:
|
|
@@ -502,6 +518,10 @@ def is_multiplayer() -> bool:
|
|
|
502
518
|
|
|
503
519
|
@meta_fn
|
|
504
520
|
def is_replay() -> bool:
|
|
521
|
+
"""Check if the game is running in replay mode.
|
|
522
|
+
|
|
523
|
+
Returns False if not in watch mode.
|
|
524
|
+
"""
|
|
505
525
|
if not ctx():
|
|
506
526
|
return False
|
|
507
527
|
match ctx().global_state.mode:
|
|
@@ -513,6 +533,10 @@ def is_replay() -> bool:
|
|
|
513
533
|
|
|
514
534
|
@meta_fn
|
|
515
535
|
def time() -> float:
|
|
536
|
+
"""Get the current time of the game.
|
|
537
|
+
|
|
538
|
+
Returns 0 in preview mode.
|
|
539
|
+
"""
|
|
516
540
|
if not ctx():
|
|
517
541
|
return 0
|
|
518
542
|
match ctx().global_state.mode:
|
|
@@ -528,6 +552,10 @@ def time() -> float:
|
|
|
528
552
|
|
|
529
553
|
@meta_fn
|
|
530
554
|
def delta_time() -> float:
|
|
555
|
+
"""Get the time elapsed since the last frame.
|
|
556
|
+
|
|
557
|
+
Returns 0 in preview mode.
|
|
558
|
+
"""
|
|
531
559
|
if not ctx():
|
|
532
560
|
return 0
|
|
533
561
|
match ctx().global_state.mode:
|
|
@@ -543,6 +571,10 @@ def delta_time() -> float:
|
|
|
543
571
|
|
|
544
572
|
@meta_fn
|
|
545
573
|
def scaled_time() -> float:
|
|
574
|
+
"""Get the current time of the game affected by the time scale.
|
|
575
|
+
|
|
576
|
+
Returns the unscaled time in tutorial mode and 0 in preview mode.
|
|
577
|
+
"""
|
|
546
578
|
if not ctx():
|
|
547
579
|
return 0
|
|
548
580
|
match ctx().global_state.mode:
|
|
@@ -558,6 +590,7 @@ def scaled_time() -> float:
|
|
|
558
590
|
|
|
559
591
|
@meta_fn
|
|
560
592
|
def touches() -> VarArray[Touch, 999]:
|
|
593
|
+
"""Get the current touches of the game."""
|
|
561
594
|
if not ctx():
|
|
562
595
|
return VarArray(0, Array[Touch, 0]())
|
|
563
596
|
match ctx().global_state.mode:
|
|
@@ -569,6 +602,10 @@ def touches() -> VarArray[Touch, 999]:
|
|
|
569
602
|
|
|
570
603
|
@meta_fn
|
|
571
604
|
def is_skip() -> bool:
|
|
605
|
+
"""Check if there was a time skip this frame.
|
|
606
|
+
|
|
607
|
+
Returns False if not in watch mode.
|
|
608
|
+
"""
|
|
572
609
|
if not ctx():
|
|
573
610
|
return False
|
|
574
611
|
match ctx().global_state.mode:
|
|
@@ -580,6 +617,10 @@ def is_skip() -> bool:
|
|
|
580
617
|
|
|
581
618
|
@meta_fn
|
|
582
619
|
def navigation_direction() -> int:
|
|
620
|
+
"""Get the navigation direction of the tutorial.
|
|
621
|
+
|
|
622
|
+
Returns 0 if not in tutorial mode.
|
|
623
|
+
"""
|
|
583
624
|
if not ctx():
|
|
584
625
|
return 0
|
|
585
626
|
match ctx().global_state.mode:
|
|
@@ -590,28 +631,34 @@ def navigation_direction() -> int:
|
|
|
590
631
|
|
|
591
632
|
|
|
592
633
|
def skin_transform() -> Transform2d:
|
|
634
|
+
"""Get the global skin transform."""
|
|
593
635
|
return _SkinTransform.transform
|
|
594
636
|
|
|
595
637
|
|
|
596
638
|
@meta_fn
|
|
597
639
|
def set_skin_transform(value: Transform2d):
|
|
640
|
+
"""Set the global skin transform."""
|
|
598
641
|
_SkinTransform.transform._copy_from_(value)
|
|
599
642
|
|
|
600
643
|
|
|
601
644
|
def particle_transform() -> Transform2d:
|
|
645
|
+
"""Get the global particle transform."""
|
|
602
646
|
return _ParticleTransform.transform
|
|
603
647
|
|
|
604
648
|
|
|
605
649
|
@meta_fn
|
|
606
650
|
def set_particle_transform(value: Transform2d):
|
|
651
|
+
"""Set the global particle transform."""
|
|
607
652
|
_ParticleTransform.transform._copy_from_(value)
|
|
608
653
|
|
|
609
654
|
|
|
610
655
|
def background() -> Quad:
|
|
656
|
+
"""Get the background quad."""
|
|
611
657
|
return _Background.value
|
|
612
658
|
|
|
613
659
|
|
|
614
660
|
def set_background(value: Quad):
|
|
661
|
+
"""Set the background quad."""
|
|
615
662
|
_Background.value = value
|
|
616
663
|
|
|
617
664
|
|
|
@@ -626,16 +673,20 @@ tutorial_ui_configs = _TutorialRuntimeUiConfigs
|
|
|
626
673
|
|
|
627
674
|
|
|
628
675
|
def canvas() -> _PreviewRuntimeCanvas:
|
|
676
|
+
"""Get the preview canvas."""
|
|
629
677
|
return _PreviewRuntimeCanvas
|
|
630
678
|
|
|
631
679
|
|
|
632
680
|
def screen() -> Rect:
|
|
681
|
+
"""Get the screen boundaries as a rectangle."""
|
|
633
682
|
return Rect(t=1, r=aspect_ratio(), b=-1, l=-aspect_ratio())
|
|
634
683
|
|
|
635
684
|
|
|
636
685
|
def level_score() -> _LevelScore:
|
|
686
|
+
"""Get the level score configuration."""
|
|
637
687
|
return _LevelScore
|
|
638
688
|
|
|
639
689
|
|
|
640
690
|
def level_life() -> _LevelLife:
|
|
691
|
+
"""Get the level life configuration."""
|
|
641
692
|
return _LevelLife
|