sonolus.py 0.7.0__py3-none-any.whl → 0.8.0__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/visitor.py +32 -14
- sonolus/build/collection.py +3 -1
- sonolus/build/compile.py +7 -1
- sonolus/build/engine.py +2 -2
- sonolus/build/project.py +3 -1
- sonolus/script/archetype.py +5 -0
- sonolus/script/debug.py +32 -0
- sonolus/script/effect.py +66 -9
- sonolus/script/level.py +16 -1
- sonolus/script/num.py +2 -0
- sonolus/script/particle.py +68 -9
- sonolus/script/project.py +1 -1
- sonolus/script/quad.py +93 -2
- sonolus/script/runtime.py +1 -1
- sonolus/script/sprite.py +65 -10
- {sonolus_py-0.7.0.dist-info → sonolus_py-0.8.0.dist-info}/METADATA +1 -1
- {sonolus_py-0.7.0.dist-info → sonolus_py-0.8.0.dist-info}/RECORD +20 -20
- {sonolus_py-0.7.0.dist-info → sonolus_py-0.8.0.dist-info}/WHEEL +0 -0
- {sonolus_py-0.7.0.dist-info → sonolus_py-0.8.0.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.7.0.dist-info → sonolus_py-0.8.0.dist-info}/licenses/LICENSE +0 -0
sonolus/backend/visitor.py
CHANGED
|
@@ -14,7 +14,16 @@ from sonolus.backend.utils import get_function, scan_writes
|
|
|
14
14
|
from sonolus.script.debug import assert_true
|
|
15
15
|
from sonolus.script.internal.builtin_impls import BUILTIN_IMPLS, _bool, _float, _int, _len, _super
|
|
16
16
|
from sonolus.script.internal.constant import ConstantValue
|
|
17
|
-
from sonolus.script.internal.context import
|
|
17
|
+
from sonolus.script.internal.context import (
|
|
18
|
+
ConflictBinding,
|
|
19
|
+
Context,
|
|
20
|
+
EmptyBinding,
|
|
21
|
+
Scope,
|
|
22
|
+
ValueBinding,
|
|
23
|
+
ctx,
|
|
24
|
+
set_ctx,
|
|
25
|
+
using_ctx,
|
|
26
|
+
)
|
|
18
27
|
from sonolus.script.internal.descriptor import SonolusDescriptor
|
|
19
28
|
from sonolus.script.internal.error import CompilationError
|
|
20
29
|
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
@@ -22,7 +31,7 @@ from sonolus.script.internal.transient import TransientValue
|
|
|
22
31
|
from sonolus.script.internal.tuple_impl import TupleImpl
|
|
23
32
|
from sonolus.script.internal.value import Value
|
|
24
33
|
from sonolus.script.iterator import SonolusIterator
|
|
25
|
-
from sonolus.script.maybe import Maybe
|
|
34
|
+
from sonolus.script.maybe import Maybe, Nothing
|
|
26
35
|
from sonolus.script.num import Num, _is_num
|
|
27
36
|
from sonolus.script.record import Record
|
|
28
37
|
|
|
@@ -312,13 +321,16 @@ class Visitor(ast.NodeVisitor):
|
|
|
312
321
|
yield_merge_ctx = Context.meet(yield_between_ctxs)
|
|
313
322
|
else:
|
|
314
323
|
yield_merge_ctx = before_ctx.new_empty_disconnected()
|
|
315
|
-
# Making it default to a number for convenience when used with stuff like min, etc.
|
|
316
|
-
yield_merge_ctx.scope.set_value("$yield", validate_value(0))
|
|
317
324
|
yield_binding = yield_merge_ctx.scope.get_binding("$yield")
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
325
|
+
match yield_binding:
|
|
326
|
+
case ValueBinding():
|
|
327
|
+
with using_ctx(yield_merge_ctx):
|
|
328
|
+
is_present_var._set_(1)
|
|
329
|
+
yield_value = Maybe(present=is_present_var, value=yield_binding.value)
|
|
330
|
+
case EmptyBinding():
|
|
331
|
+
yield_value = Nothing
|
|
332
|
+
case ConflictBinding():
|
|
333
|
+
raise ValueError("Function has conflicting yield values")
|
|
322
334
|
next_result_ctx = Context.meet([yield_merge_ctx, return_ctx])
|
|
323
335
|
set_ctx(before_ctx)
|
|
324
336
|
return_test = Num._alloc_()
|
|
@@ -327,7 +339,7 @@ class Visitor(ast.NodeVisitor):
|
|
|
327
339
|
return_test,
|
|
328
340
|
entry,
|
|
329
341
|
next_result_ctx,
|
|
330
|
-
|
|
342
|
+
yield_value,
|
|
331
343
|
self.used_parent_binding_values,
|
|
332
344
|
self,
|
|
333
345
|
)
|
|
@@ -739,11 +751,17 @@ class Visitor(ast.NodeVisitor):
|
|
|
739
751
|
test = validate_value(subject._is_py_() and subject._as_py_() is None)
|
|
740
752
|
case _:
|
|
741
753
|
raise NotImplementedError("Unsupported match singleton")
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
754
|
+
if test._is_py_():
|
|
755
|
+
if test._as_py_():
|
|
756
|
+
return ctx(), ctx().into_dead()
|
|
757
|
+
else:
|
|
758
|
+
return ctx().into_dead(), ctx()
|
|
759
|
+
else:
|
|
760
|
+
ctx_init = ctx()
|
|
761
|
+
ctx_init.test = test.ir()
|
|
762
|
+
true_ctx = ctx_init.branch(None)
|
|
763
|
+
false_ctx = ctx_init.branch(0)
|
|
764
|
+
return true_ctx, false_ctx
|
|
747
765
|
case ast.MatchSequence(patterns=patterns):
|
|
748
766
|
target_len = len(patterns)
|
|
749
767
|
if not (isinstance(subject, Sequence | TupleImpl)):
|
sonolus/build/collection.py
CHANGED
|
@@ -4,6 +4,7 @@ import gzip
|
|
|
4
4
|
import hashlib
|
|
5
5
|
import json
|
|
6
6
|
import urllib.request
|
|
7
|
+
import warnings
|
|
7
8
|
import zipfile
|
|
8
9
|
from io import BytesIO
|
|
9
10
|
from os import PathLike
|
|
@@ -116,6 +117,7 @@ class Collection:
|
|
|
116
117
|
try:
|
|
117
118
|
item_data = json.loads(item_json_path.read_text(encoding="utf-8"))
|
|
118
119
|
except json.JSONDecodeError:
|
|
120
|
+
warnings.warn(f"Invalid JSON in {item_json_path}, skipping item.", stacklevel=2)
|
|
119
121
|
continue
|
|
120
122
|
|
|
121
123
|
item_data = self._localize_item(item_data)
|
|
@@ -129,7 +131,7 @@ class Collection:
|
|
|
129
131
|
resource_data = resource_path.read_bytes()
|
|
130
132
|
|
|
131
133
|
if resource_path.suffix.lower() in {".json", ".bin"}:
|
|
132
|
-
resource_data = gzip.compress(resource_data)
|
|
134
|
+
resource_data = gzip.compress(resource_data, mtime=0)
|
|
133
135
|
|
|
134
136
|
srl = self.add_asset(resource_data)
|
|
135
137
|
item_data[resource_path.stem] = srl
|
sonolus/build/compile.py
CHANGED
|
@@ -77,7 +77,13 @@ def compile_mode(
|
|
|
77
77
|
base_archetype_entries = {}
|
|
78
78
|
|
|
79
79
|
if archetypes is not None:
|
|
80
|
-
base_archetypes =
|
|
80
|
+
base_archetypes = []
|
|
81
|
+
seen_base_archetypes = set()
|
|
82
|
+
for a in archetypes:
|
|
83
|
+
base = getattr(a, "_derived_base_", a)
|
|
84
|
+
if base not in seen_base_archetypes:
|
|
85
|
+
seen_base_archetypes.add(base)
|
|
86
|
+
base_archetypes.append(base)
|
|
81
87
|
|
|
82
88
|
for archetype in base_archetypes:
|
|
83
89
|
archetype._init_fields()
|
sonolus/build/engine.py
CHANGED
|
@@ -407,12 +407,12 @@ def package_rom(rom: ReadOnlyMemory) -> bytes:
|
|
|
407
407
|
for value in values:
|
|
408
408
|
output.extend(struct.pack("<f", value))
|
|
409
409
|
|
|
410
|
-
return gzip.compress(bytes(output))
|
|
410
|
+
return gzip.compress(bytes(output), mtime=0)
|
|
411
411
|
|
|
412
412
|
|
|
413
413
|
def package_data(value: JsonValue) -> bytes:
|
|
414
414
|
json_data = json.dumps(value, separators=(",", ":")).encode("utf-8")
|
|
415
|
-
return gzip.compress(json_data)
|
|
415
|
+
return gzip.compress(json_data, mtime=0)
|
|
416
416
|
|
|
417
417
|
|
|
418
418
|
def unpackage_data(data: bytes) -> JsonValue:
|
sonolus/build/project.py
CHANGED
|
@@ -39,7 +39,7 @@ def build_project_to_collection(project: Project, config: BuildConfig | None):
|
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
def apply_converter_to_collection(
|
|
42
|
-
self, src_engine: str | None, converter: Callable[[ExternalLevelData], LevelData]
|
|
42
|
+
self, src_engine: str | None, converter: Callable[[ExternalLevelData], LevelData | None]
|
|
43
43
|
) -> None:
|
|
44
44
|
for level_details in self.categories.get("levels", {}).values():
|
|
45
45
|
level = level_details["item"]
|
|
@@ -56,6 +56,8 @@ def apply_converter_to_collection(
|
|
|
56
56
|
raise ValueError(f"Level data for level '{level['name']}' is not valid")
|
|
57
57
|
parsed_data = parse_external_level_data(cast(ExternalLevelDataDict, data))
|
|
58
58
|
new_data = converter(parsed_data)
|
|
59
|
+
if new_data is None:
|
|
60
|
+
continue
|
|
59
61
|
packaged_new_data = package_level_data(new_data)
|
|
60
62
|
new_data_srl = self.add_asset(packaged_new_data)
|
|
61
63
|
level["data"] = new_data_srl
|
sonolus/script/archetype.py
CHANGED
|
@@ -1233,6 +1233,11 @@ class EntityRef[A: _BaseArchetype](Record):
|
|
|
1233
1233
|
return self._ref_ is other._ref_
|
|
1234
1234
|
return super().__eq__(other)
|
|
1235
1235
|
|
|
1236
|
+
def __hash__(self) -> int:
|
|
1237
|
+
if not ctx() and hasattr(self, "_ref_"):
|
|
1238
|
+
return hash(id(self._ref_))
|
|
1239
|
+
return super().__hash__()
|
|
1240
|
+
|
|
1236
1241
|
@meta_fn
|
|
1237
1242
|
def get(self) -> A:
|
|
1238
1243
|
"""Get the entity."""
|
sonolus/script/debug.py
CHANGED
|
@@ -80,6 +80,20 @@ def assert_false(value: int | float | bool, message: str | None = None):
|
|
|
80
80
|
error(message)
|
|
81
81
|
|
|
82
82
|
|
|
83
|
+
def static_assert(value: int | float | bool, message: str | None = None):
|
|
84
|
+
message = message if message is not None else "Static assertion failed"
|
|
85
|
+
if not _is_static_true(value):
|
|
86
|
+
static_error(message)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def try_static_assert(value: int | float | bool, message: str | None = None):
|
|
90
|
+
message = message if message is not None else "Static assertion failed"
|
|
91
|
+
if _is_static_false(value):
|
|
92
|
+
static_error(message)
|
|
93
|
+
if not value:
|
|
94
|
+
error(message)
|
|
95
|
+
|
|
96
|
+
|
|
83
97
|
@meta_fn
|
|
84
98
|
def assert_unreachable(message: str | None = None) -> Never:
|
|
85
99
|
# This works a bit differently from assert_never from typing in that it throws an error if the Sonolus.py
|
|
@@ -140,3 +154,21 @@ def visualize_cfg(
|
|
|
140
154
|
|
|
141
155
|
def simulation_context() -> SimulationContext:
|
|
142
156
|
return SimulationContext()
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@meta_fn
|
|
160
|
+
def _is_static_true(value: int | float | bool) -> bool:
|
|
161
|
+
if ctx() is None:
|
|
162
|
+
return bool(value)
|
|
163
|
+
else:
|
|
164
|
+
value = validate_value(value)
|
|
165
|
+
return value._is_py_() and value._as_py_()
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@meta_fn
|
|
169
|
+
def _is_static_false(value: int | float | bool) -> bool:
|
|
170
|
+
if ctx() is None:
|
|
171
|
+
return not bool(value)
|
|
172
|
+
else:
|
|
173
|
+
value = validate_value(value)
|
|
174
|
+
return value._is_py_() and not value._as_py_()
|
sonolus/script/effect.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from collections.abc import Iterable
|
|
3
4
|
from dataclasses import dataclass
|
|
4
5
|
from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
5
6
|
|
|
6
7
|
from sonolus.backend.ops import Op
|
|
8
|
+
from sonolus.script.array_like import ArrayLike
|
|
9
|
+
from sonolus.script.debug import static_error
|
|
7
10
|
from sonolus.script.internal.introspection import get_field_specifiers
|
|
8
11
|
from sonolus.script.internal.native import native_function
|
|
9
12
|
from sonolus.script.record import Record
|
|
@@ -92,6 +95,29 @@ class ScheduledLoopedEffectHandle(Record):
|
|
|
92
95
|
_stop_looped_scheduled(self.id, end_time)
|
|
93
96
|
|
|
94
97
|
|
|
98
|
+
class EffectGroup(Record, ArrayLike[Effect]):
|
|
99
|
+
"""A group of effect clips.
|
|
100
|
+
|
|
101
|
+
Usage:
|
|
102
|
+
```python
|
|
103
|
+
EffectGroup(start_id: int, size: int)
|
|
104
|
+
```
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
start_id: int
|
|
108
|
+
size: int
|
|
109
|
+
|
|
110
|
+
def __len__(self) -> int:
|
|
111
|
+
return self.size
|
|
112
|
+
|
|
113
|
+
def __getitem__(self, index: int) -> Effect:
|
|
114
|
+
assert 0 <= index < self.size
|
|
115
|
+
return Effect(self.start_id + index)
|
|
116
|
+
|
|
117
|
+
def __setitem__(self, index: int, value: Effect) -> None:
|
|
118
|
+
static_error("EffectGroup is read-only")
|
|
119
|
+
|
|
120
|
+
|
|
95
121
|
@native_function(Op.HasEffectClip)
|
|
96
122
|
def _has_effect_clip(effect_id: int) -> bool:
|
|
97
123
|
raise NotImplementedError
|
|
@@ -132,11 +158,21 @@ class EffectInfo:
|
|
|
132
158
|
name: str
|
|
133
159
|
|
|
134
160
|
|
|
161
|
+
@dataclass
|
|
162
|
+
class EffectGroupInfo:
|
|
163
|
+
names: list[str]
|
|
164
|
+
|
|
165
|
+
|
|
135
166
|
def effect(name: str) -> Any:
|
|
136
167
|
"""Define a sound effect clip with the given name."""
|
|
137
168
|
return EffectInfo(name)
|
|
138
169
|
|
|
139
170
|
|
|
171
|
+
def effect_group(names: Iterable[str]) -> Any:
|
|
172
|
+
"""Define an effect group with the given names."""
|
|
173
|
+
return EffectGroupInfo(list(names))
|
|
174
|
+
|
|
175
|
+
|
|
140
176
|
type Effects = NewType("Effects", Any) # type: ignore
|
|
141
177
|
|
|
142
178
|
|
|
@@ -150,24 +186,45 @@ def effects[T](cls: type[T]) -> T | Effects:
|
|
|
150
186
|
class Effects:
|
|
151
187
|
miss: StandardEffect.MISS
|
|
152
188
|
other: Effect = effect("other")
|
|
189
|
+
group_1: EffectGroup = effect_group(["one", "two", "three"])
|
|
190
|
+
group_2: EffectGroup = effect_group(f"name_{i}" for i in range(10))
|
|
153
191
|
```
|
|
154
192
|
"""
|
|
155
193
|
if len(cls.__bases__) != 1:
|
|
156
194
|
raise ValueError("Effects class must not inherit from any class (except object)")
|
|
157
195
|
instance = cls()
|
|
158
196
|
names = []
|
|
159
|
-
|
|
197
|
+
i = 0
|
|
198
|
+
for name, annotation in get_field_specifiers(cls).items():
|
|
160
199
|
if get_origin(annotation) is not Annotated:
|
|
161
|
-
raise TypeError(f"Invalid annotation for effects: {annotation}")
|
|
200
|
+
raise TypeError(f"Invalid annotation for effects: {annotation} on field {name}")
|
|
162
201
|
annotation_type = annotation.__args__[0]
|
|
163
202
|
annotation_values = annotation.__metadata__
|
|
164
|
-
if
|
|
165
|
-
raise TypeError(f"Invalid annotation for effects: {annotation},
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
203
|
+
if len(annotation_values) != 1:
|
|
204
|
+
raise TypeError(f"Invalid annotation for effects: {annotation} on field {name}, too many annotation values")
|
|
205
|
+
effect_info = annotation_values[0]
|
|
206
|
+
match effect_info:
|
|
207
|
+
case EffectInfo(name=effect_name):
|
|
208
|
+
if annotation_type is not Effect:
|
|
209
|
+
raise TypeError(f"Invalid annotation for effects: {annotation} on field {name}, expected Effect")
|
|
210
|
+
names.append(effect_name)
|
|
211
|
+
setattr(instance, name, Effect(i))
|
|
212
|
+
i += 1
|
|
213
|
+
case EffectGroupInfo(names=effect_names):
|
|
214
|
+
if annotation_type is not EffectGroup:
|
|
215
|
+
raise TypeError(
|
|
216
|
+
f"Invalid annotation for effects: {annotation} on field {name}, expected EffectGroup"
|
|
217
|
+
)
|
|
218
|
+
start_id = i
|
|
219
|
+
count = len(effect_names)
|
|
220
|
+
names.extend(effect_names)
|
|
221
|
+
setattr(instance, name, EffectGroup(start_id, count))
|
|
222
|
+
i += count
|
|
223
|
+
case _:
|
|
224
|
+
raise TypeError(
|
|
225
|
+
f"Invalid annotation for effects: {annotation} on field {name}, unknown effect info, "
|
|
226
|
+
f"expected an effect() or effect_group() specifier"
|
|
227
|
+
)
|
|
171
228
|
instance._effects_ = names
|
|
172
229
|
instance._is_comptime_value_ = True
|
|
173
230
|
return instance
|
sonolus/script/level.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import gzip
|
|
3
4
|
import json
|
|
4
5
|
from collections.abc import Iterator
|
|
5
6
|
from os import PathLike
|
|
@@ -229,7 +230,21 @@ class ExternalEntityData(NamedTuple):
|
|
|
229
230
|
data: dict[str, Any]
|
|
230
231
|
|
|
231
232
|
|
|
232
|
-
def parse_external_level_data(raw_data: ExternalLevelDataDict) -> ExternalLevelData:
|
|
233
|
+
def parse_external_level_data(raw_data: ExternalLevelDataDict | str | bytes, /) -> ExternalLevelData:
|
|
234
|
+
"""Parse level data from an external source.
|
|
235
|
+
|
|
236
|
+
If given a string, it is parsed as JSON. If given bytes, it is un-gzipped and then parsed as JSON.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
raw_data: The raw level data to parse.
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
The parsed level data.
|
|
243
|
+
"""
|
|
244
|
+
if isinstance(raw_data, bytes):
|
|
245
|
+
raw_data = gzip.decompress(raw_data).decode("utf-8")
|
|
246
|
+
if isinstance(raw_data, str):
|
|
247
|
+
raw_data = json.loads(raw_data)
|
|
233
248
|
bgm_offset = raw_data["bgmOffset"]
|
|
234
249
|
raw_entities = raw_data["entities"]
|
|
235
250
|
entity_name_to_index = {e["name"]: i for i, e in enumerate(raw_entities) if "name" in e}
|
sonolus/script/num.py
CHANGED
|
@@ -131,6 +131,8 @@ class _Num(Value, metaclass=_NumMeta):
|
|
|
131
131
|
if isinstance(self.data.block, BlockData) and not ctx().is_writable(self.data):
|
|
132
132
|
# This block is immutable in the current callback, so no need to copy it in case it changes.
|
|
133
133
|
return Num(self.data)
|
|
134
|
+
if isinstance(self.data, int | float | bool):
|
|
135
|
+
return Num(self.data)
|
|
134
136
|
place = ctx().alloc(size=1)
|
|
135
137
|
ctx().add_statements(IRSet(place, self.ir()))
|
|
136
138
|
return Num(place)
|
sonolus/script/particle.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from collections.abc import Iterable
|
|
3
4
|
from dataclasses import dataclass
|
|
4
5
|
from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
5
6
|
|
|
6
7
|
from sonolus.backend.ops import Op
|
|
8
|
+
from sonolus.script.array_like import ArrayLike
|
|
9
|
+
from sonolus.script.debug import static_error
|
|
7
10
|
from sonolus.script.internal.introspection import get_field_specifiers
|
|
8
11
|
from sonolus.script.internal.native import native_function
|
|
9
12
|
from sonolus.script.quad import QuadLike, flatten_quad
|
|
@@ -52,6 +55,29 @@ class ParticleHandle(Record):
|
|
|
52
55
|
_destroy_particle_effect(self.id)
|
|
53
56
|
|
|
54
57
|
|
|
58
|
+
class ParticleGroup(Record, ArrayLike[Particle]):
|
|
59
|
+
"""A group of particle effects.
|
|
60
|
+
|
|
61
|
+
Usage:
|
|
62
|
+
```python
|
|
63
|
+
ParticleGroup(start_id: int, size: int)
|
|
64
|
+
```
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
start_id: int
|
|
68
|
+
size: int
|
|
69
|
+
|
|
70
|
+
def __len__(self) -> int:
|
|
71
|
+
return self.size
|
|
72
|
+
|
|
73
|
+
def __getitem__(self, index: int) -> Particle:
|
|
74
|
+
assert 0 <= index < self.size
|
|
75
|
+
return Particle(self.start_id + index)
|
|
76
|
+
|
|
77
|
+
def __setitem__(self, index: int, value: Particle) -> None:
|
|
78
|
+
static_error("ParticleGroup is read-only")
|
|
79
|
+
|
|
80
|
+
|
|
55
81
|
@native_function(Op.HasParticleEffect)
|
|
56
82
|
def _has_particle_effect(particle_id: int) -> bool:
|
|
57
83
|
raise NotImplementedError
|
|
@@ -91,11 +117,21 @@ class _ParticleInfo:
|
|
|
91
117
|
name: str
|
|
92
118
|
|
|
93
119
|
|
|
120
|
+
@dataclass
|
|
121
|
+
class _ParticleGroupInfo:
|
|
122
|
+
names: list[str]
|
|
123
|
+
|
|
124
|
+
|
|
94
125
|
def particle(name: str) -> Any:
|
|
95
126
|
"""Define a particle with the given name."""
|
|
96
127
|
return _ParticleInfo(name)
|
|
97
128
|
|
|
98
129
|
|
|
130
|
+
def particle_group(names: Iterable[str]) -> Any:
|
|
131
|
+
"""Define a particle group with the given names."""
|
|
132
|
+
return _ParticleGroupInfo(list(names))
|
|
133
|
+
|
|
134
|
+
|
|
99
135
|
type Particles = NewType("Particles", Any) # type: ignore
|
|
100
136
|
|
|
101
137
|
|
|
@@ -109,26 +145,49 @@ def particles[T](cls: type[T]) -> T | Particles:
|
|
|
109
145
|
class Particles:
|
|
110
146
|
tap: StandardParticle.NOTE_CIRCULAR_TAP_RED
|
|
111
147
|
other: Particle = particle("other")
|
|
148
|
+
group_1: ParticleGroup = particle_group(["one", "two", "three"])
|
|
149
|
+
group_2: ParticleGroup = particle_group(f"name_{i}" for i in range(10))
|
|
112
150
|
```
|
|
113
151
|
"""
|
|
114
152
|
if len(cls.__bases__) != 1:
|
|
115
153
|
raise ValueError("Particles class must not inherit from any class (except object)")
|
|
116
154
|
instance = cls()
|
|
117
155
|
names = []
|
|
118
|
-
|
|
156
|
+
i = 0
|
|
157
|
+
for name, annotation in get_field_specifiers(cls).items():
|
|
119
158
|
if get_origin(annotation) is not Annotated:
|
|
120
|
-
raise TypeError(f"Invalid annotation for particles: {annotation}")
|
|
159
|
+
raise TypeError(f"Invalid annotation for particles: {annotation} on field {name}")
|
|
121
160
|
annotation_type = annotation.__args__[0]
|
|
122
161
|
annotation_values = annotation.__metadata__
|
|
123
|
-
if
|
|
124
|
-
raise TypeError(f"Invalid annotation for particles: {annotation}, expected annotation of type Particle")
|
|
125
|
-
if len(annotation_values) != 1 or not isinstance(annotation_values[0], _ParticleInfo):
|
|
162
|
+
if len(annotation_values) != 1:
|
|
126
163
|
raise TypeError(
|
|
127
|
-
f"Invalid annotation for particles: {annotation}
|
|
164
|
+
f"Invalid annotation for particles: {annotation} on field {name}, too many annotation values"
|
|
128
165
|
)
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
166
|
+
particle_info = annotation_values[0]
|
|
167
|
+
match particle_info:
|
|
168
|
+
case _ParticleInfo(name=particle_name):
|
|
169
|
+
if annotation_type is not Particle:
|
|
170
|
+
raise TypeError(
|
|
171
|
+
f"Invalid annotation for particles: {annotation} on field {name}, expected Particle"
|
|
172
|
+
)
|
|
173
|
+
names.append(particle_name)
|
|
174
|
+
setattr(instance, name, Particle(i))
|
|
175
|
+
i += 1
|
|
176
|
+
case _ParticleGroupInfo(names=particle_names):
|
|
177
|
+
if annotation_type is not ParticleGroup:
|
|
178
|
+
raise TypeError(
|
|
179
|
+
f"Invalid annotation for particles: {annotation} on field {name}, expected ParticleGroup"
|
|
180
|
+
)
|
|
181
|
+
start_id = i
|
|
182
|
+
count = len(particle_names)
|
|
183
|
+
names.extend(particle_names)
|
|
184
|
+
setattr(instance, name, ParticleGroup(start_id, count))
|
|
185
|
+
i += count
|
|
186
|
+
case _:
|
|
187
|
+
raise TypeError(
|
|
188
|
+
f"Invalid annotation for particles: {annotation} on field {name}, unknown particle info, "
|
|
189
|
+
f"expected a particle() or particle_group() specifier"
|
|
190
|
+
)
|
|
132
191
|
instance._particles_ = names
|
|
133
192
|
instance._is_comptime_value_ = True
|
|
134
193
|
return instance
|
sonolus/script/project.py
CHANGED
|
@@ -28,7 +28,7 @@ class Project:
|
|
|
28
28
|
engine: Engine,
|
|
29
29
|
levels: Iterable[Level] | Callable[[], Iterable[Level]] | None = None,
|
|
30
30
|
resources: PathLike | None = None,
|
|
31
|
-
converters: dict[str | None, Callable[[ExternalLevelData], LevelData]] | None = None,
|
|
31
|
+
converters: dict[str | None, Callable[[ExternalLevelData], LevelData | None]] | None = None,
|
|
32
32
|
):
|
|
33
33
|
self.engine = engine
|
|
34
34
|
match levels:
|
sonolus/script/quad.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, assert_never, overload
|
|
4
4
|
|
|
5
5
|
from sonolus.script.record import Record
|
|
6
6
|
from sonolus.script.values import zeros
|
|
@@ -57,6 +57,26 @@ class Quad(Record):
|
|
|
57
57
|
"""The center of the quad."""
|
|
58
58
|
return (self.bl + self.tr + self.tl + self.br) / 4
|
|
59
59
|
|
|
60
|
+
@property
|
|
61
|
+
def mt(self) -> Vec2:
|
|
62
|
+
"""The midpoint of the top edge of the quad."""
|
|
63
|
+
return (self.tl + self.tr) / 2
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def mr(self) -> Vec2:
|
|
67
|
+
"""The midpoint of the right edge of the quad."""
|
|
68
|
+
return (self.tr + self.br) / 2
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def mb(self) -> Vec2:
|
|
72
|
+
"""The midpoint of the bottom edge of the quad."""
|
|
73
|
+
return (self.bl + self.br) / 2
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def ml(self) -> Vec2:
|
|
77
|
+
"""The midpoint of the left edge of the quad."""
|
|
78
|
+
return (self.bl + self.tl) / 2
|
|
79
|
+
|
|
60
80
|
def translate(self, translation: Vec2, /) -> Quad:
|
|
61
81
|
"""Translate the quad by the given translation and return a new quad."""
|
|
62
82
|
return Quad(
|
|
@@ -212,7 +232,7 @@ class Rect(Record):
|
|
|
212
232
|
b: float
|
|
213
233
|
"""The bottom edge of the rectangle."""
|
|
214
234
|
|
|
215
|
-
l: float
|
|
235
|
+
l: float
|
|
216
236
|
"""The left edge of the rectangle."""
|
|
217
237
|
|
|
218
238
|
@classmethod
|
|
@@ -225,6 +245,57 @@ class Rect(Record):
|
|
|
225
245
|
l=center.x - dimensions.x / 2,
|
|
226
246
|
)
|
|
227
247
|
|
|
248
|
+
@overload
|
|
249
|
+
@classmethod
|
|
250
|
+
def from_margin(cls, trbl: float, /) -> Rect: ...
|
|
251
|
+
|
|
252
|
+
@overload
|
|
253
|
+
@classmethod
|
|
254
|
+
def from_margin(cls, tb: float, lr: float, /) -> Rect: ...
|
|
255
|
+
|
|
256
|
+
@overload
|
|
257
|
+
@classmethod
|
|
258
|
+
def from_margin(cls, t: float, lr: float, b: float, /) -> Rect: ...
|
|
259
|
+
|
|
260
|
+
@overload
|
|
261
|
+
@classmethod
|
|
262
|
+
def from_margin(cls, t: float, r: float, b: float, l: float, /) -> Rect: ...
|
|
263
|
+
|
|
264
|
+
@classmethod
|
|
265
|
+
def from_margin(cls, a: float, b: float | None = None, c: float | None = None, d: float | None = None, /) -> Self:
|
|
266
|
+
"""Create a rectangle based on margins (edge distances) from the origin.
|
|
267
|
+
|
|
268
|
+
Compared to the regular [`Rect`][sonolus.script.quad.Rect] constructor, this method negates the bottom and
|
|
269
|
+
left values, and supports shorthands when fewer than four arguments are provided.
|
|
270
|
+
|
|
271
|
+
The following signatures are supported:
|
|
272
|
+
|
|
273
|
+
- `from_margin(trbl)`: All margins set to `trbl`.
|
|
274
|
+
- `from_margin(tb, lr)`: Top and bottom margins set to `tb`, left and right margins set to `lr`.
|
|
275
|
+
- `from_margin(t, lr, b)`: Top margin set to `t`, left and right margins set to `lr`, bottom margin set to `b`.
|
|
276
|
+
- `from_margin(t, r, b, l)`: Top, right, bottom, and left margins set to `t`, `r`, `b`, and `l` respectively.
|
|
277
|
+
|
|
278
|
+
Usage:
|
|
279
|
+
```python
|
|
280
|
+
Rect.from_margin(1) # Rect(t=1, r=1, b=-1, l=-1)
|
|
281
|
+
Rect.from_margin(1, 2) # Rect(t=1, r=2, b=-1, l=-2)
|
|
282
|
+
Rect.from_margin(1, 2, 3) # Rect(t=1, r=2, b=-3, l=-2)
|
|
283
|
+
Rect.from_margin(1, 2, 3, 4) # Rect(t=1, r=2, b=-3, l=-4)
|
|
284
|
+
```
|
|
285
|
+
"""
|
|
286
|
+
args = (a, b, c, d)
|
|
287
|
+
match args:
|
|
288
|
+
case (a, None, None, None):
|
|
289
|
+
return cls(t=a, r=a, b=-a, l=-a)
|
|
290
|
+
case (a, b, None, None):
|
|
291
|
+
return cls(t=a, r=b, b=-a, l=-b)
|
|
292
|
+
case (a, b, c, None):
|
|
293
|
+
return cls(t=a, r=b, b=-c, l=-b)
|
|
294
|
+
case (a, b, c, d):
|
|
295
|
+
return cls(t=a, r=b, b=-c, l=-d)
|
|
296
|
+
case _:
|
|
297
|
+
assert_never(args)
|
|
298
|
+
|
|
228
299
|
@property
|
|
229
300
|
def w(self) -> float:
|
|
230
301
|
"""The width of the rectangle."""
|
|
@@ -260,6 +331,26 @@ class Rect(Record):
|
|
|
260
331
|
"""The center of the rectangle."""
|
|
261
332
|
return Vec2((self.l + self.r) / 2, (self.t + self.b) / 2)
|
|
262
333
|
|
|
334
|
+
@property
|
|
335
|
+
def mt(self) -> Vec2:
|
|
336
|
+
"""The middle-top point of the rectangle."""
|
|
337
|
+
return Vec2((self.l + self.r) / 2, self.t)
|
|
338
|
+
|
|
339
|
+
@property
|
|
340
|
+
def mr(self) -> Vec2:
|
|
341
|
+
"""The middle-right point of the rectangle."""
|
|
342
|
+
return Vec2(self.r, (self.t + self.b) / 2)
|
|
343
|
+
|
|
344
|
+
@property
|
|
345
|
+
def mb(self) -> Vec2:
|
|
346
|
+
"""The middle-bottom point of the rectangle."""
|
|
347
|
+
return Vec2((self.l + self.r) / 2, self.b)
|
|
348
|
+
|
|
349
|
+
@property
|
|
350
|
+
def ml(self) -> Vec2:
|
|
351
|
+
"""The middle-left point of the rectangle."""
|
|
352
|
+
return Vec2(self.l, (self.t + self.b) / 2)
|
|
353
|
+
|
|
263
354
|
def as_quad(self) -> Quad:
|
|
264
355
|
"""Convert the rectangle to a [`Quad`][sonolus.script.quad.Quad]."""
|
|
265
356
|
return Quad(
|
sonolus/script/runtime.py
CHANGED
|
@@ -992,7 +992,7 @@ def time() -> float:
|
|
|
992
992
|
def offset_adjusted_time() -> float:
|
|
993
993
|
"""Get the current time of the game adjusted by the input offset.
|
|
994
994
|
|
|
995
|
-
Returns 0 in preview mode and tutorial mode.
|
|
995
|
+
Returns 0 in preview mode and the current time without adjustment in tutorial mode.
|
|
996
996
|
"""
|
|
997
997
|
if not ctx():
|
|
998
998
|
return 0
|
sonolus/script/sprite.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
from collections.abc import Iterable
|
|
1
2
|
from dataclasses import dataclass
|
|
2
3
|
from enum import StrEnum
|
|
3
4
|
from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
4
5
|
|
|
5
6
|
from sonolus.backend.ops import Op
|
|
7
|
+
from sonolus.script.array_like import ArrayLike
|
|
8
|
+
from sonolus.script.debug import static_error
|
|
6
9
|
from sonolus.script.internal.introspection import get_field_specifiers
|
|
7
10
|
from sonolus.script.internal.native import native_function
|
|
8
11
|
from sonolus.script.quad import QuadLike, flatten_quad
|
|
@@ -111,6 +114,29 @@ class Sprite(Record):
|
|
|
111
114
|
_draw_curved_lr(self.id, *flatten_quad(quad), z, a, n, *cp1.tuple, *cp2.tuple)
|
|
112
115
|
|
|
113
116
|
|
|
117
|
+
class SpriteGroup(Record, ArrayLike[Sprite]):
|
|
118
|
+
"""A group of sprites.
|
|
119
|
+
|
|
120
|
+
Usage:
|
|
121
|
+
```python
|
|
122
|
+
SpriteGroup(start_id: int, size: int)
|
|
123
|
+
```
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
start_id: int
|
|
127
|
+
size: int
|
|
128
|
+
|
|
129
|
+
def __len__(self) -> int:
|
|
130
|
+
return self.size
|
|
131
|
+
|
|
132
|
+
def __getitem__(self, index: int) -> Sprite:
|
|
133
|
+
assert 0 <= index < self.size
|
|
134
|
+
return Sprite(self.start_id + index)
|
|
135
|
+
|
|
136
|
+
def __setitem__(self, index: int, value: Sprite) -> None:
|
|
137
|
+
static_error("SpriteGroup is read-only")
|
|
138
|
+
|
|
139
|
+
|
|
114
140
|
@native_function(Op.HasSkinSprite)
|
|
115
141
|
def _has_skin_sprite(sprite_id: int) -> bool:
|
|
116
142
|
raise NotImplementedError
|
|
@@ -262,11 +288,21 @@ class SkinSprite:
|
|
|
262
288
|
name: str
|
|
263
289
|
|
|
264
290
|
|
|
291
|
+
@dataclass
|
|
292
|
+
class SkinSpriteGroup:
|
|
293
|
+
names: list[str]
|
|
294
|
+
|
|
295
|
+
|
|
265
296
|
def sprite(name: str) -> Any:
|
|
266
297
|
"""Define a sprite with the given name."""
|
|
267
298
|
return SkinSprite(name)
|
|
268
299
|
|
|
269
300
|
|
|
301
|
+
def sprite_group(names: Iterable[str]) -> Any:
|
|
302
|
+
"""Define a sprite group with the given names."""
|
|
303
|
+
return SkinSpriteGroup(list(names))
|
|
304
|
+
|
|
305
|
+
|
|
270
306
|
type Skin = NewType("Skin", Any) # type: ignore
|
|
271
307
|
|
|
272
308
|
|
|
@@ -294,25 +330,44 @@ def skin[T](cls: type[T]) -> T | Skin:
|
|
|
294
330
|
render_mode: RenderMode = RenderMode.LIGHTWEIGHT
|
|
295
331
|
|
|
296
332
|
note: StandardSprite.NOTE_HEAD_RED
|
|
297
|
-
other: Sprite =
|
|
333
|
+
other: Sprite = sprite("other")
|
|
334
|
+
group_1: SpriteGroup = sprite_group(["one", "two", "three"])
|
|
335
|
+
group_2: SpriteGroup = sprite_group(f"name_{i}" for i in range(10))
|
|
298
336
|
```
|
|
299
337
|
"""
|
|
300
338
|
if len(cls.__bases__) != 1:
|
|
301
339
|
raise ValueError("Skin class must not inherit from any class (except object)")
|
|
302
340
|
instance = cls()
|
|
303
341
|
names = []
|
|
304
|
-
|
|
342
|
+
i = 0
|
|
343
|
+
for name, annotation in get_field_specifiers(cls, skip={"render_mode"}).items():
|
|
305
344
|
if get_origin(annotation) is not Annotated:
|
|
306
|
-
raise TypeError(f"Invalid annotation for skin: {annotation}")
|
|
345
|
+
raise TypeError(f"Invalid annotation for skin: {annotation} on field {name}")
|
|
307
346
|
annotation_type = annotation.__args__[0]
|
|
308
347
|
annotation_values = annotation.__metadata__
|
|
309
|
-
if
|
|
310
|
-
raise TypeError(f"Invalid annotation for skin: {annotation},
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
348
|
+
if len(annotation_values) != 1:
|
|
349
|
+
raise TypeError(f"Invalid annotation for skin: {annotation} on field {name}, too many annotation values")
|
|
350
|
+
sprite_info = annotation_values[0]
|
|
351
|
+
match sprite_info:
|
|
352
|
+
case SkinSprite(name=sprite_name):
|
|
353
|
+
if annotation_type is not Sprite:
|
|
354
|
+
raise TypeError(f"Invalid annotation for skin: {annotation} on field {name}, expected Sprite")
|
|
355
|
+
names.append(sprite_name)
|
|
356
|
+
setattr(instance, name, Sprite(i))
|
|
357
|
+
i += 1
|
|
358
|
+
case SkinSpriteGroup(names=sprite_names):
|
|
359
|
+
if annotation_type is not SpriteGroup:
|
|
360
|
+
raise TypeError(f"Invalid annotation for skin: {annotation} on field {name}, expected SpriteGroup")
|
|
361
|
+
start_id = i
|
|
362
|
+
count = len(sprite_names)
|
|
363
|
+
names.extend(sprite_names)
|
|
364
|
+
setattr(instance, name, SpriteGroup(start_id, count))
|
|
365
|
+
i += count
|
|
366
|
+
case _:
|
|
367
|
+
raise TypeError(
|
|
368
|
+
f"Invalid annotation for skin: {annotation} on field {name}, unknown sprite info, "
|
|
369
|
+
f"expected a skin() or sprite_group() specifier"
|
|
370
|
+
)
|
|
316
371
|
instance._sprites_ = names
|
|
317
372
|
instance.render_mode = RenderMode(getattr(instance, "render_mode", RenderMode.DEFAULT))
|
|
318
373
|
instance._is_comptime_value_ = True
|
|
@@ -11,7 +11,7 @@ sonolus/backend/node.py,sha256=eEzPP14jzWJp2xrZCAaPlNtokxdoqg0bSM7xQiwx1j8,1254
|
|
|
11
11
|
sonolus/backend/ops.py,sha256=5weB_vIxbkwCSJuzYZyKUk7vVXsSIEDJYRlvE-2ke8A,10572
|
|
12
12
|
sonolus/backend/place.py,sha256=7qwV732hZ4WP-9GNN8FQSEKssPJZELip1wLXTWfop7Y,4717
|
|
13
13
|
sonolus/backend/utils.py,sha256=OwD1EPh8j-hsfkLzeKNzPQojT_3kklpJou0WTJNoCbc,2337
|
|
14
|
-
sonolus/backend/visitor.py,sha256=
|
|
14
|
+
sonolus/backend/visitor.py,sha256=SIkrr7JOOQ4is9ov2siBpczpb_vWZZyckBQQr2BojrQ,63437
|
|
15
15
|
sonolus/backend/optimize/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
sonolus/backend/optimize/allocate.py,sha256=CuumoMphkpQlGRNeKLHT4FBGE0XVj5pwhfNdrqiLFSs,7535
|
|
17
17
|
sonolus/backend/optimize/constant_evaluation.py,sha256=U--9moGsXFzrgweWWwHIiEuuMzwetd1IOjjtrCscoNM,21450
|
|
@@ -27,39 +27,39 @@ sonolus/backend/optimize/simplify.py,sha256=RDNVTKfC7ByRyxY5z30_ShimOAKth_pKlVFV
|
|
|
27
27
|
sonolus/backend/optimize/ssa.py,sha256=raQO0furQQRPYb8iIBKfNrJlj-_5wqtI4EWNfLZ8QFo,10834
|
|
28
28
|
sonolus/build/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
29
|
sonolus/build/cli.py,sha256=-_lTN8zT7nQB2lySM8itAEPVutcEQI-TJ13BcPIfGb4,10113
|
|
30
|
-
sonolus/build/collection.py,sha256=
|
|
31
|
-
sonolus/build/compile.py,sha256=
|
|
32
|
-
sonolus/build/engine.py,sha256=
|
|
30
|
+
sonolus/build/collection.py,sha256=KgMfdLsCA7bheT-E2wmdB2OBEWolbsECm8vrwAhGC1c,12415
|
|
31
|
+
sonolus/build/compile.py,sha256=mrbKTlhgu1x0YWijd1pIGHcOg0UTHPx4rEECPZuCRSM,6968
|
|
32
|
+
sonolus/build/engine.py,sha256=YQ2Sc2zC56N2Y5S5zE3QCoUzwKcVHSPl14iQE0tkm6Q,13463
|
|
33
33
|
sonolus/build/level.py,sha256=KLqUAtxIuIqrzeFURJA97rdqjA5pcvYSmwNZQhElaMQ,702
|
|
34
34
|
sonolus/build/node.py,sha256=gnX71RYDUOK_gYMpinQi-bLWO4csqcfiG5gFmhxzSec,1330
|
|
35
|
-
sonolus/build/project.py,sha256=
|
|
35
|
+
sonolus/build/project.py,sha256=__sOJas3RTwn4aizq16rYCvZgG8aX-JbQaxmfVhRnc4,8154
|
|
36
36
|
sonolus/script/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
|
-
sonolus/script/archetype.py,sha256=
|
|
37
|
+
sonolus/script/archetype.py,sha256=xhdm1jO_p32V1NtGjgV0SGPm7X9GnJ6Z7hXi2zT69Ww,49647
|
|
38
38
|
sonolus/script/array.py,sha256=9uOUHZIDMyMT9q3APcXJWXWt97yG-AZoRlxwrvSY6SU,12367
|
|
39
39
|
sonolus/script/array_like.py,sha256=hUDdDaP306kflVv9YomdHIMXogrVjxsBXCrLvB9QpuE,9681
|
|
40
40
|
sonolus/script/bucket.py,sha256=SNqnfLGpnO_u-3LFFazdgzNk332OdDUIlmZHsuoVZuE,7674
|
|
41
41
|
sonolus/script/containers.py,sha256=L3rzPfN1cDb2m02k9qRyjnGxJx07X3q3MccCaL5tNJ8,18773
|
|
42
|
-
sonolus/script/debug.py,sha256=
|
|
42
|
+
sonolus/script/debug.py,sha256=KNc2zhzLzQr_YZV9BkZCX4GP00Sev2O23jb6PDpHC4w,5472
|
|
43
43
|
sonolus/script/easing.py,sha256=2FUJI_nfp990P_armCcRqHm2329O985glJAhSC6tnxs,11379
|
|
44
|
-
sonolus/script/effect.py,sha256=
|
|
44
|
+
sonolus/script/effect.py,sha256=pYihzdVOS3ekiiTwPVg6czUW1UNv0CJNIk-xBsYRdq8,7792
|
|
45
45
|
sonolus/script/engine.py,sha256=etI9dJsQ7V9YZICVNZg54WqpLijPxG8eTPHiV-_EiG8,10687
|
|
46
46
|
sonolus/script/globals.py,sha256=nlXSNS4NRXsgQU2AJImVIs752h1WqsMnShSKgU011c4,10270
|
|
47
47
|
sonolus/script/instruction.py,sha256=Dd-14D5Amo8nhPBr6DNyg2lpYw_rqZkT8Kix3HkfE7k,6793
|
|
48
48
|
sonolus/script/interval.py,sha256=dj6F2wn5uP6I6_mcZn-wIREgRUQbsLzhvhzB0oEyAdU,11290
|
|
49
49
|
sonolus/script/iterator.py,sha256=_ICY_yX7FG0Zbgs3NhVnaIBdVDpAeXjxJ_CQtq30l7Y,3774
|
|
50
|
-
sonolus/script/level.py,sha256=
|
|
50
|
+
sonolus/script/level.py,sha256=X3-V99ihruYYCcPdch66dHi_ydCWXXn7epviLLjxW8w,8288
|
|
51
51
|
sonolus/script/maybe.py,sha256=VYvTWgEfPzoXqI3i3zXhc4dz0pWBVoHmW8FtWH0GQvM,8194
|
|
52
52
|
sonolus/script/metadata.py,sha256=ttRK27eojHf3So50KQJ-8yj3udZoN1bli5iD-knaeLw,753
|
|
53
|
-
sonolus/script/num.py,sha256=
|
|
53
|
+
sonolus/script/num.py,sha256=s3Sl4nxZAjkOg-UwzJiWkUsV0lyJQPTOyjhAfzWFTf8,16137
|
|
54
54
|
sonolus/script/options.py,sha256=XVN-mL7Rwhd2Tu9YysYq9YDGpH_LazdmhqzSYE6nR3Q,9455
|
|
55
|
-
sonolus/script/particle.py,sha256=
|
|
55
|
+
sonolus/script/particle.py,sha256=rCEJGT7frqJqZLi4EBCqDs4QBvLW6Ys640nD1FxiVec,10326
|
|
56
56
|
sonolus/script/pointer.py,sha256=FoOfyD93r0G5d_2BaKfeOT9SqkOP3hq6sqtOs_Rb0c8,1511
|
|
57
57
|
sonolus/script/printing.py,sha256=mNYu9QWiacBBGZrnePZQMVwbbguoelUps9GiOK_aVRU,2096
|
|
58
|
-
sonolus/script/project.py,sha256=
|
|
59
|
-
sonolus/script/quad.py,sha256=
|
|
58
|
+
sonolus/script/project.py,sha256=gG6mYsGlIqznY_RODyFMiXVBh0tVbXLAWZ5LHCiBIuU,4439
|
|
59
|
+
sonolus/script/quad.py,sha256=9qUCOVirSShSE_hP9G0ZIkewNX990sw476PNwwJlWdU,14377
|
|
60
60
|
sonolus/script/record.py,sha256=igmawc0hqb98YVcZnPTThHvnIP88Qelhoge4cNJx45Q,12770
|
|
61
|
-
sonolus/script/runtime.py,sha256=
|
|
62
|
-
sonolus/script/sprite.py,sha256=
|
|
61
|
+
sonolus/script/runtime.py,sha256=VwmW-8fy-uKyfUNewtXQhphmLZP7ugQeMWBFA8zi28w,33360
|
|
62
|
+
sonolus/script/sprite.py,sha256=J4rA558MSNjNkI3KPnDp3R5RyJRmpi5P5cDfg6dKlIU,18065
|
|
63
63
|
sonolus/script/stream.py,sha256=qVljaCxJJtQs7aBwfUG15pYvztb4jFYzSLGDh5t4DTA,24713
|
|
64
64
|
sonolus/script/text.py,sha256=wxujIgKYcCfl2AD2_Im8g3vh0lDEHYwTSRZg9wsBPEU,13402
|
|
65
65
|
sonolus/script/timing.py,sha256=DklMvuxcFg3MzXsecUo6Yhdk7pScOJ7STwXvAiTvLKM,3067
|
|
@@ -86,8 +86,8 @@ sonolus/script/internal/simulation_context.py,sha256=LGxLTvxbqBIhoe1R-SfwGajNIDw
|
|
|
86
86
|
sonolus/script/internal/transient.py,sha256=y2AWABqF1aoaP6H4_2u4MMpNioC4OsZQCtPyNI0txqo,1634
|
|
87
87
|
sonolus/script/internal/tuple_impl.py,sha256=DPNdmmRmupU8Ah4_XKq6-PdT336l4nt15_uCJKQGkkk,3587
|
|
88
88
|
sonolus/script/internal/value.py,sha256=OngrCdmY_h6mV2Zgwqhuo4eYFad0kTk6263UAxctZcY,6963
|
|
89
|
-
sonolus_py-0.
|
|
90
|
-
sonolus_py-0.
|
|
91
|
-
sonolus_py-0.
|
|
92
|
-
sonolus_py-0.
|
|
93
|
-
sonolus_py-0.
|
|
89
|
+
sonolus_py-0.8.0.dist-info/METADATA,sha256=Q-mtT3V7JhxLurDNDQyLmz5Hre95CGj-T-NHbh0hTMk,302
|
|
90
|
+
sonolus_py-0.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
91
|
+
sonolus_py-0.8.0.dist-info/entry_points.txt,sha256=oTYspY_b7SA8TptEMTDxh4-Aj-ZVPnYC9f1lqH6s9G4,54
|
|
92
|
+
sonolus_py-0.8.0.dist-info/licenses/LICENSE,sha256=JEKpqVhQYfEc7zg3Mj462sKbKYmO1K7WmvX1qvg9IJk,1067
|
|
93
|
+
sonolus_py-0.8.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|