sonolus.py 0.1.8__py3-none-any.whl → 0.2.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/finalize.py +2 -1
- sonolus/backend/optimize/constant_evaluation.py +2 -2
- sonolus/backend/optimize/copy_coalesce.py +16 -7
- sonolus/backend/optimize/optimize.py +12 -4
- sonolus/backend/optimize/passes.py +2 -1
- sonolus/backend/place.py +95 -14
- sonolus/backend/visitor.py +83 -12
- sonolus/build/cli.py +60 -9
- sonolus/build/collection.py +68 -26
- sonolus/build/compile.py +87 -30
- sonolus/build/engine.py +166 -40
- sonolus/build/level.py +2 -1
- sonolus/build/node.py +8 -1
- sonolus/build/project.py +30 -11
- sonolus/script/archetype.py +169 -51
- sonolus/script/array.py +12 -1
- sonolus/script/bucket.py +26 -8
- sonolus/script/debug.py +2 -2
- sonolus/script/effect.py +2 -2
- sonolus/script/engine.py +123 -15
- sonolus/script/internal/builtin_impls.py +21 -2
- sonolus/script/internal/constant.py +6 -2
- sonolus/script/internal/context.py +30 -25
- sonolus/script/internal/introspection.py +8 -1
- sonolus/script/internal/math_impls.py +2 -1
- sonolus/script/internal/transient.py +5 -1
- sonolus/script/internal/value.py +10 -2
- sonolus/script/interval.py +16 -0
- sonolus/script/iterator.py +17 -0
- sonolus/script/level.py +130 -8
- sonolus/script/metadata.py +32 -0
- sonolus/script/num.py +11 -2
- sonolus/script/options.py +5 -3
- sonolus/script/pointer.py +2 -0
- sonolus/script/project.py +41 -5
- sonolus/script/record.py +8 -3
- sonolus/script/runtime.py +61 -10
- sonolus/script/sprite.py +18 -1
- sonolus/script/ui.py +7 -3
- sonolus/script/values.py +8 -5
- sonolus/script/vec.py +28 -0
- {sonolus_py-0.1.8.dist-info → sonolus_py-0.2.0.dist-info}/METADATA +3 -2
- sonolus_py-0.2.0.dist-info/RECORD +90 -0
- {sonolus_py-0.1.8.dist-info → sonolus_py-0.2.0.dist-info}/WHEEL +1 -1
- sonolus_py-0.1.8.dist-info/RECORD +0 -89
- /sonolus/script/{print.py → printing.py} +0 -0
- {sonolus_py-0.1.8.dist-info → sonolus_py-0.2.0.dist-info}/entry_points.txt +0 -0
- {sonolus_py-0.1.8.dist-info → sonolus_py-0.2.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
type LocalizationText = dict[str, str]
|
|
4
|
+
type AnyText = str | LocalizationText
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def as_localization_text(text: AnyText) -> LocalizationText:
|
|
8
|
+
if isinstance(text, str):
|
|
9
|
+
return {"en": text}
|
|
10
|
+
return text
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Tag:
|
|
14
|
+
"""A tag for an engine or level.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
title: The title of the tag.
|
|
18
|
+
icon: The icon of the tag.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
title: LocalizationText
|
|
22
|
+
icon: str | None
|
|
23
|
+
|
|
24
|
+
def __init__(self, title: AnyText, icon: str | None = None) -> None:
|
|
25
|
+
self.title = as_localization_text(title)
|
|
26
|
+
self.icon = icon
|
|
27
|
+
|
|
28
|
+
def as_dict(self) -> dict[str, Any]:
|
|
29
|
+
result = {"title": self.title}
|
|
30
|
+
if self.icon is not None:
|
|
31
|
+
result["icon"] = self.icon
|
|
32
|
+
return result
|
sonolus/script/num.py
CHANGED
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
|
|
4
4
|
import operator
|
|
5
5
|
from collections.abc import Callable, Iterable
|
|
6
|
-
from typing import TYPE_CHECKING, Any, Self, final, runtime_checkable
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Self, TypeGuard, final, runtime_checkable
|
|
7
7
|
|
|
8
8
|
from sonolus.backend.ir import IRConst, IRGet, IRPureInstr, IRSet
|
|
9
9
|
from sonolus.backend.ops import Op
|
|
@@ -92,7 +92,7 @@ class _Num(Value, metaclass=_NumMeta):
|
|
|
92
92
|
value = next(iter(values))
|
|
93
93
|
return Num(value)
|
|
94
94
|
|
|
95
|
-
def _to_list_(self, level_refs: dict[Any,
|
|
95
|
+
def _to_list_(self, level_refs: dict[Any, str] | None = None) -> list[float | str | BlockPlace]:
|
|
96
96
|
return [self.data]
|
|
97
97
|
|
|
98
98
|
@classmethod
|
|
@@ -134,6 +134,15 @@ class _Num(Value, metaclass=_NumMeta):
|
|
|
134
134
|
else:
|
|
135
135
|
return Num(-1)
|
|
136
136
|
|
|
137
|
+
@classmethod
|
|
138
|
+
def _zero_(cls) -> Self:
|
|
139
|
+
if ctx():
|
|
140
|
+
result_place = ctx().alloc(size=1)
|
|
141
|
+
ctx().add_statements(IRSet(result_place, IRConst(0)))
|
|
142
|
+
return cls(result_place)
|
|
143
|
+
else:
|
|
144
|
+
return cls(0)
|
|
145
|
+
|
|
137
146
|
def ir(self):
|
|
138
147
|
if isinstance(self.data, BlockPlace):
|
|
139
148
|
return IRGet(self.data)
|
sonolus/script/options.py
CHANGED
|
@@ -69,7 +69,7 @@ class _SelectOption:
|
|
|
69
69
|
standard: bool
|
|
70
70
|
advanced: bool
|
|
71
71
|
scope: str | None
|
|
72
|
-
default:
|
|
72
|
+
default: int
|
|
73
73
|
values: list[str]
|
|
74
74
|
|
|
75
75
|
def to_dict(self):
|
|
@@ -78,7 +78,7 @@ class _SelectOption:
|
|
|
78
78
|
"name": self.name,
|
|
79
79
|
"standard": self.standard,
|
|
80
80
|
"advanced": self.advanced,
|
|
81
|
-
"def": self.
|
|
81
|
+
"def": self.default,
|
|
82
82
|
"values": self.values,
|
|
83
83
|
}
|
|
84
84
|
if self.scope is not None:
|
|
@@ -139,7 +139,7 @@ def select_option(
|
|
|
139
139
|
name: str | None = None,
|
|
140
140
|
standard: bool = False,
|
|
141
141
|
advanced: bool = False,
|
|
142
|
-
default: str,
|
|
142
|
+
default: str | int,
|
|
143
143
|
values: list[str],
|
|
144
144
|
scope: str | None = None,
|
|
145
145
|
) -> Any:
|
|
@@ -153,6 +153,8 @@ def select_option(
|
|
|
153
153
|
values: The values of the option.
|
|
154
154
|
scope: The scope of the option.
|
|
155
155
|
"""
|
|
156
|
+
if isinstance(default, str):
|
|
157
|
+
default = values.index(default)
|
|
156
158
|
return _SelectOption(name, standard, advanced, scope, default, values)
|
|
157
159
|
|
|
158
160
|
|
sonolus/script/pointer.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from sonolus.backend.place import BlockPlace
|
|
2
|
+
from sonolus.script.internal.context import ctx
|
|
2
3
|
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
3
4
|
from sonolus.script.internal.value import Value
|
|
4
5
|
from sonolus.script.num import Num, _is_num
|
|
@@ -13,6 +14,7 @@ def _deref[T: Value](block: Num, offset: Num, type_: type[T]) -> T:
|
|
|
13
14
|
block = block._as_py_()
|
|
14
15
|
if not isinstance(block, int):
|
|
15
16
|
raise TypeError("block must be an integer")
|
|
17
|
+
block = ctx().blocks(block)
|
|
16
18
|
else:
|
|
17
19
|
if not _is_num(block):
|
|
18
20
|
raise TypeError("block must be a Num")
|
sonolus/script/project.py
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from dataclasses import dataclass
|
|
3
5
|
from os import PathLike
|
|
4
6
|
from pathlib import Path
|
|
5
|
-
from typing import Self, TypedDict
|
|
7
|
+
from typing import ClassVar, Self, TypedDict
|
|
6
8
|
|
|
9
|
+
from sonolus.backend.optimize import optimize
|
|
10
|
+
from sonolus.backend.optimize.passes import CompilerPass
|
|
7
11
|
from sonolus.script.archetype import ArchetypeSchema
|
|
8
12
|
from sonolus.script.engine import Engine
|
|
9
13
|
from sonolus.script.level import Level
|
|
@@ -39,27 +43,30 @@ class Project:
|
|
|
39
43
|
"""
|
|
40
44
|
return Project(self.engine, levels, self.resources)
|
|
41
45
|
|
|
42
|
-
def dev(self, build_dir: PathLike, port: int = 8080):
|
|
46
|
+
def dev(self, build_dir: PathLike, port: int = 8080, config: BuildConfig | None = None):
|
|
43
47
|
"""Start a development server for the project.
|
|
44
48
|
|
|
45
49
|
Args:
|
|
46
50
|
build_dir: The path to the build directory.
|
|
47
51
|
port: The port of the development server.
|
|
52
|
+
config: The build configuration.
|
|
48
53
|
"""
|
|
49
54
|
from sonolus.build.cli import build_collection, run_server
|
|
50
55
|
|
|
51
|
-
build_collection(self, Path(build_dir))
|
|
56
|
+
build_collection(self, Path(build_dir), config)
|
|
52
57
|
run_server(Path(build_dir) / "site", port=port)
|
|
53
58
|
|
|
54
|
-
def build(self, build_dir: PathLike):
|
|
59
|
+
def build(self, build_dir: PathLike, config: BuildConfig | None = None):
|
|
55
60
|
"""Build the project.
|
|
56
61
|
|
|
57
62
|
Args:
|
|
58
63
|
build_dir: The path to the build directory.
|
|
64
|
+
config: The build configuration.
|
|
59
65
|
"""
|
|
60
66
|
from sonolus.build.cli import build_project
|
|
61
67
|
|
|
62
|
-
|
|
68
|
+
config = config or BuildConfig()
|
|
69
|
+
build_project(self, Path(build_dir), config)
|
|
63
70
|
|
|
64
71
|
def schema(self) -> ProjectSchema:
|
|
65
72
|
"""Generate the schema of the project.
|
|
@@ -74,3 +81,32 @@ class Project:
|
|
|
74
81
|
|
|
75
82
|
class ProjectSchema(TypedDict):
|
|
76
83
|
archetypes: list[ArchetypeSchema]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass
|
|
87
|
+
class BuildConfig:
|
|
88
|
+
"""A configuration for building an engine package."""
|
|
89
|
+
|
|
90
|
+
MINIMAL_PASSES: ClassVar[Sequence[CompilerPass]] = optimize.MINIMAL_PASSES
|
|
91
|
+
"""The minimal list of compiler passes."""
|
|
92
|
+
|
|
93
|
+
FAST_PASSES: ClassVar[Sequence[CompilerPass]] = optimize.FAST_PASSES
|
|
94
|
+
"""The list of compiler passes for faster builds."""
|
|
95
|
+
|
|
96
|
+
STANDARD_PASSES: ClassVar[Sequence[CompilerPass]] = optimize.STANDARD_PASSES
|
|
97
|
+
"""The standard list of compiler passes."""
|
|
98
|
+
|
|
99
|
+
passes: Sequence[CompilerPass] = optimize.STANDARD_PASSES
|
|
100
|
+
"""The list of compiler passes to use."""
|
|
101
|
+
|
|
102
|
+
build_play: bool = True
|
|
103
|
+
"""Whether to build the play package."""
|
|
104
|
+
|
|
105
|
+
build_watch: bool = True
|
|
106
|
+
"""Whether to build the watch package."""
|
|
107
|
+
|
|
108
|
+
build_preview: bool = True
|
|
109
|
+
"""Whether to build the preview package."""
|
|
110
|
+
|
|
111
|
+
build_tutorial: bool = True
|
|
112
|
+
"""Whether to build the tutorial package."""
|
sonolus/script/record.py
CHANGED
|
@@ -186,7 +186,7 @@ class Record(GenericValue):
|
|
|
186
186
|
iterator = iter(values)
|
|
187
187
|
return cls(**{field.name: field.type._from_list_(iterator) for field in cls._fields})
|
|
188
188
|
|
|
189
|
-
def _to_list_(self, level_refs: dict[Any,
|
|
189
|
+
def _to_list_(self, level_refs: dict[Any, str] | None = None) -> list[float | str | BlockPlace]:
|
|
190
190
|
result = []
|
|
191
191
|
for field in self._fields:
|
|
192
192
|
result.extend(self._value[field.name]._to_list_(level_refs))
|
|
@@ -206,8 +206,7 @@ class Record(GenericValue):
|
|
|
206
206
|
raise TypeError("Record does not support set_")
|
|
207
207
|
|
|
208
208
|
def _copy_from_(self, value: Self):
|
|
209
|
-
|
|
210
|
-
raise TypeError("Cannot copy from different type")
|
|
209
|
+
value = self._accept_(value)
|
|
211
210
|
for field in self._fields:
|
|
212
211
|
field.__set__(self, field.__get__(value))
|
|
213
212
|
|
|
@@ -221,6 +220,12 @@ class Record(GenericValue):
|
|
|
221
220
|
result._value = {field.name: field.type._alloc_() for field in cls._fields}
|
|
222
221
|
return result
|
|
223
222
|
|
|
223
|
+
@classmethod
|
|
224
|
+
def _zero_(cls) -> Self:
|
|
225
|
+
result = object.__new__(cls)
|
|
226
|
+
result._value = {field.name: field.type._zero_() for field in cls._fields}
|
|
227
|
+
return result
|
|
228
|
+
|
|
224
229
|
def __str__(self):
|
|
225
230
|
return (
|
|
226
231
|
f"{self.__class__.__name__}({", ".join(f"{field.name}={field.__get__(self)}" for field in self._fields)})"
|
sonolus/script/runtime.py
CHANGED
|
@@ -103,6 +103,12 @@ class _PreviewRuntimeCanvas:
|
|
|
103
103
|
scroll_direction: ScrollDirection
|
|
104
104
|
size: float
|
|
105
105
|
|
|
106
|
+
def update(self, scroll_direction: ScrollDirection | None = None, size: float | None = None):
|
|
107
|
+
if scroll_direction is not None:
|
|
108
|
+
self.scroll_direction = scroll_direction
|
|
109
|
+
if size is not None:
|
|
110
|
+
self.size = size
|
|
111
|
+
|
|
106
112
|
|
|
107
113
|
class RuntimeUiConfig(Record):
|
|
108
114
|
scale: float
|
|
@@ -253,36 +259,57 @@ class _TutorialRuntimeUi:
|
|
|
253
259
|
|
|
254
260
|
|
|
255
261
|
class Touch(Record):
|
|
262
|
+
"""Data of a touch event."""
|
|
263
|
+
|
|
256
264
|
id: int
|
|
265
|
+
"""The unique identifier of the touch."""
|
|
266
|
+
|
|
257
267
|
started: bool
|
|
268
|
+
"""Whether the touch has started this frame."""
|
|
269
|
+
|
|
258
270
|
ended: bool
|
|
271
|
+
"""Whether the touch has ended this frame."""
|
|
272
|
+
|
|
259
273
|
time: float
|
|
274
|
+
"""The time of the touch event.
|
|
275
|
+
|
|
276
|
+
May remain constant while there is no movement.
|
|
277
|
+
"""
|
|
278
|
+
|
|
260
279
|
start_time: float
|
|
280
|
+
"""The time the touch started."""
|
|
281
|
+
|
|
261
282
|
position: Vec2
|
|
283
|
+
"""The current position of the touch."""
|
|
284
|
+
|
|
262
285
|
start_position: Vec2
|
|
286
|
+
"""The position the touch started."""
|
|
287
|
+
|
|
263
288
|
delta: Vec2
|
|
289
|
+
"""The change in position of the touch."""
|
|
290
|
+
|
|
264
291
|
velocity: Vec2
|
|
292
|
+
"""The velocity of the touch."""
|
|
293
|
+
|
|
265
294
|
speed: float
|
|
295
|
+
"""The speed of the touch's movement."""
|
|
296
|
+
|
|
266
297
|
angle: float
|
|
298
|
+
"""The angle of the touch's movement."""
|
|
267
299
|
|
|
268
300
|
@property
|
|
269
|
-
def
|
|
270
|
-
|
|
301
|
+
def prev_position(self) -> Vec2:
|
|
302
|
+
"""The previous position of the touch."""
|
|
303
|
+
return self.position - self.delta
|
|
271
304
|
|
|
272
305
|
@property
|
|
273
306
|
def total_delta(self) -> Vec2:
|
|
307
|
+
"""The total change in position of the touch."""
|
|
274
308
|
return self.position - self.start_position
|
|
275
309
|
|
|
276
|
-
@property
|
|
277
|
-
def total_velocity(self) -> Vec2:
|
|
278
|
-
return self.total_delta / self.total_time if self.total_time > 0 else Vec2(0, 0)
|
|
279
|
-
|
|
280
|
-
@property
|
|
281
|
-
def total_speed(self) -> float:
|
|
282
|
-
return self.total_velocity.magnitude
|
|
283
|
-
|
|
284
310
|
@property
|
|
285
311
|
def total_angle(self) -> float:
|
|
312
|
+
"""The total angle of the touch's movement."""
|
|
286
313
|
return self.total_delta.angle
|
|
287
314
|
|
|
288
315
|
|
|
@@ -449,6 +476,30 @@ def is_debug() -> bool:
|
|
|
449
476
|
return False
|
|
450
477
|
|
|
451
478
|
|
|
479
|
+
@meta_fn
|
|
480
|
+
def is_play() -> bool:
|
|
481
|
+
"""Check if the game is running in play mode."""
|
|
482
|
+
return ctx() and ctx().global_state.mode == Mode.PLAY
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
@meta_fn
|
|
486
|
+
def is_preview() -> bool:
|
|
487
|
+
"""Check if the game is running in preview mode."""
|
|
488
|
+
return ctx() and ctx().global_state.mode == Mode.PREVIEW
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
@meta_fn
|
|
492
|
+
def is_watch() -> bool:
|
|
493
|
+
"""Check if the game is running in watch mode."""
|
|
494
|
+
return ctx() and ctx().global_state.mode == Mode.WATCH
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
@meta_fn
|
|
498
|
+
def is_tutorial() -> bool:
|
|
499
|
+
"""Check if the game is running in tutorial mode."""
|
|
500
|
+
return ctx() and ctx().global_state.mode == Mode.TUTORIAL
|
|
501
|
+
|
|
502
|
+
|
|
452
503
|
@meta_fn
|
|
453
504
|
def aspect_ratio() -> float:
|
|
454
505
|
"""Get the aspect ratio of the game."""
|
sonolus/script/sprite.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
+
from enum import StrEnum
|
|
2
3
|
from typing import Annotated, Any, NewType, dataclass_transform, get_origin
|
|
3
4
|
|
|
4
5
|
from sonolus.backend.ops import Op
|
|
@@ -269,6 +270,19 @@ def sprite(name: str) -> Any:
|
|
|
269
270
|
type Skin = NewType("Skin", Any)
|
|
270
271
|
|
|
271
272
|
|
|
273
|
+
class RenderMode(StrEnum):
|
|
274
|
+
"""Render mode for sprites."""
|
|
275
|
+
|
|
276
|
+
DEFAULT = "default"
|
|
277
|
+
"""Use the user's preferred render mode."""
|
|
278
|
+
|
|
279
|
+
STANDARD = "standard"
|
|
280
|
+
"""Use the standard render mode with bilinear interpolation of textures."""
|
|
281
|
+
|
|
282
|
+
LIGHTWEIGHT = "lightweight"
|
|
283
|
+
"""Use the lightweight render mode with projective interpolation of textures."""
|
|
284
|
+
|
|
285
|
+
|
|
272
286
|
@dataclass_transform()
|
|
273
287
|
def skin[T](cls: type[T]) -> T | Skin:
|
|
274
288
|
"""Decorator to define a skin.
|
|
@@ -277,6 +291,8 @@ def skin[T](cls: type[T]) -> T | Skin:
|
|
|
277
291
|
```python
|
|
278
292
|
@skin
|
|
279
293
|
class Skin:
|
|
294
|
+
render_mode: RenderMode
|
|
295
|
+
|
|
280
296
|
note: StandardSprite.NOTE_HEAD_RED
|
|
281
297
|
other: Sprite = skin_sprite("other")
|
|
282
298
|
```
|
|
@@ -285,7 +301,7 @@ def skin[T](cls: type[T]) -> T | Skin:
|
|
|
285
301
|
raise ValueError("Skin class must not inherit from any class (except object)")
|
|
286
302
|
instance = cls()
|
|
287
303
|
names = []
|
|
288
|
-
for i, (name, annotation) in enumerate(get_field_specifiers(cls).items()):
|
|
304
|
+
for i, (name, annotation) in enumerate(get_field_specifiers(cls, skip={"render_mode"}).items()):
|
|
289
305
|
if get_origin(annotation) is not Annotated:
|
|
290
306
|
raise TypeError(f"Invalid annotation for skin: {annotation}")
|
|
291
307
|
annotation_type = annotation.__args__[0]
|
|
@@ -298,6 +314,7 @@ def skin[T](cls: type[T]) -> T | Skin:
|
|
|
298
314
|
names.append(sprite_name)
|
|
299
315
|
setattr(instance, name, Sprite(i))
|
|
300
316
|
instance._sprites_ = names
|
|
317
|
+
instance.render_mode = RenderMode(getattr(instance, "render_mode", RenderMode.DEFAULT))
|
|
301
318
|
instance._is_comptime_value_ = True
|
|
302
319
|
return instance
|
|
303
320
|
|
sonolus/script/ui.py
CHANGED
|
@@ -120,8 +120,8 @@ class UiAnimation:
|
|
|
120
120
|
alpha: The animation applied to alpha.
|
|
121
121
|
"""
|
|
122
122
|
|
|
123
|
-
scale: UiAnimationTween
|
|
124
|
-
alpha: UiAnimationTween
|
|
123
|
+
scale: UiAnimationTween
|
|
124
|
+
alpha: UiAnimationTween
|
|
125
125
|
|
|
126
126
|
def to_dict(self):
|
|
127
127
|
return {
|
|
@@ -183,7 +183,11 @@ class UiConfig:
|
|
|
183
183
|
progress_visibility: UiVisibility = field(default_factory=UiVisibility)
|
|
184
184
|
tutorial_navigation_visibility: UiVisibility = field(default_factory=UiVisibility)
|
|
185
185
|
tutorial_instruction_visibility: UiVisibility = field(default_factory=UiVisibility)
|
|
186
|
-
judgment_animation: UiAnimation = field(
|
|
186
|
+
judgment_animation: UiAnimation = field(
|
|
187
|
+
default_factory=lambda: UiAnimation(
|
|
188
|
+
scale=UiAnimationTween(0, 1, 0.1, EaseType.OUT_CUBIC), alpha=UiAnimationTween(1, 0, 0.3, EaseType.NONE)
|
|
189
|
+
)
|
|
190
|
+
)
|
|
187
191
|
combo_animation: UiAnimation = field(
|
|
188
192
|
default_factory=lambda: UiAnimation(
|
|
189
193
|
scale=UiAnimationTween(1.2, 1, 0.2, EaseType.IN_CUBIC), alpha=UiAnimationTween(1, 1, 0, EaseType.NONE)
|
sonolus/script/values.py
CHANGED
|
@@ -19,11 +19,7 @@ def alloc[T](type_: type[T]) -> T:
|
|
|
19
19
|
@meta_fn
|
|
20
20
|
def zeros[T](type_: type[T]) -> T:
|
|
21
21
|
"""Make a new instance of the given type initialized with zeros."""
|
|
22
|
-
|
|
23
|
-
if ctx():
|
|
24
|
-
return copy(type_._from_list_([0] * type_._size_()))
|
|
25
|
-
else:
|
|
26
|
-
return type_._from_list_([0] * type_._size_())._as_py_()
|
|
22
|
+
return validate_concrete_type(type_)._zero_()
|
|
27
23
|
|
|
28
24
|
|
|
29
25
|
@meta_fn
|
|
@@ -34,3 +30,10 @@ def copy[T](value: T) -> T:
|
|
|
34
30
|
return value._copy_()
|
|
35
31
|
else:
|
|
36
32
|
return value._copy_()._as_py_()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def swap[T](a: T, b: T):
|
|
36
|
+
"""Swap the values of the given variables."""
|
|
37
|
+
temp = copy(a)
|
|
38
|
+
a @= b
|
|
39
|
+
b @= temp
|
sonolus/script/vec.py
CHANGED
|
@@ -128,6 +128,25 @@ class Vec2(Record):
|
|
|
128
128
|
"""
|
|
129
129
|
return (self - pivot).rotate(angle) + pivot
|
|
130
130
|
|
|
131
|
+
def normalize(self) -> Self:
|
|
132
|
+
"""Normalize the vector (set the magnitude to 1) and return a new vector.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
A new vector with magnitude 1.
|
|
136
|
+
"""
|
|
137
|
+
magnitude = self.magnitude
|
|
138
|
+
return Vec2(x=self.x / magnitude, y=self.y / magnitude)
|
|
139
|
+
|
|
140
|
+
def orthogonal(self) -> Self:
|
|
141
|
+
"""Return a vector orthogonal to this vector.
|
|
142
|
+
|
|
143
|
+
The orthogonal vector is rotated 90 degrees counter-clockwise from this vector.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
A new vector orthogonal to this vector.
|
|
147
|
+
"""
|
|
148
|
+
return Vec2(x=-self.y, y=self.x)
|
|
149
|
+
|
|
131
150
|
@property
|
|
132
151
|
def tuple(self) -> tuple[float, float]:
|
|
133
152
|
"""Return the vector as a tuple (x, y).
|
|
@@ -173,6 +192,15 @@ class Vec2(Record):
|
|
|
173
192
|
return Vec2(x=self.x * x, y=self.y * y)
|
|
174
193
|
case Num(factor):
|
|
175
194
|
return Vec2(x=self.x * factor, y=self.y * factor)
|
|
195
|
+
case _:
|
|
196
|
+
return NotImplemented
|
|
197
|
+
|
|
198
|
+
def __rmul__(self, other):
|
|
199
|
+
match other:
|
|
200
|
+
case Num(factor):
|
|
201
|
+
return Vec2(x=self.x * factor, y=self.y * factor)
|
|
202
|
+
case _:
|
|
203
|
+
return NotImplemented
|
|
176
204
|
|
|
177
205
|
def __truediv__(self, other: Self | float) -> Self:
|
|
178
206
|
"""Divide this vector by another vector or a scalar and return a new vector.
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
sonolus/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
sonolus/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
sonolus/backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
sonolus/backend/blocks.py,sha256=3peyb9eYBy0s53xNVJ1KmK4IgoyVkkwG-lqDQ_VZTHc,18531
|
|
5
|
+
sonolus/backend/excepthook.py,sha256=pqI9gtPBh0mlTgMNqul8bEVO1ARzKb8pNE9EN_CyDpk,994
|
|
6
|
+
sonolus/backend/finalize.py,sha256=vNUGLCUDxNqXGxweNRA1NpRvtij7Tks9to1V_JTs7Jo,4020
|
|
7
|
+
sonolus/backend/interpret.py,sha256=B0jqlLmEGoyO2mxpcvwRwV17Tq_gOE9wLNt26Q5QOfs,14306
|
|
8
|
+
sonolus/backend/ir.py,sha256=TCDLMvlX2S8emFDQwFVeD2OUC4fnhbrMObgYtoa_7PQ,2845
|
|
9
|
+
sonolus/backend/mode.py,sha256=NkcPZJm8dn83LX35uP24MtQOCnfRDFZ280dHeEEfauE,613
|
|
10
|
+
sonolus/backend/node.py,sha256=H8qgnNyIseR-DhfgtcbDX03SUmhAJSSrYAlUEJTkkUo,999
|
|
11
|
+
sonolus/backend/ops.py,sha256=ekkHSdgRubIYLSYFk0wTUuBvyf3TKdApM4AyR_koTQ8,10122
|
|
12
|
+
sonolus/backend/place.py,sha256=jABvLNNE-2pklTcb9WnyfHK8c-tYxn0ObsoLp5LYd5I,4703
|
|
13
|
+
sonolus/backend/utils.py,sha256=9-mmCUwGlNdjp51jKrNBN2dGxCAXVV2PdJn031kaXHM,1717
|
|
14
|
+
sonolus/backend/visitor.py,sha256=GvAEx8D3_YijTMUYBmir0IvDPAEpK1ObAuQOAcAMKm0,48157
|
|
15
|
+
sonolus/backend/optimize/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
sonolus/backend/optimize/allocate.py,sha256=LCqxvT8YsVGTaVAu58fb6lrrytVLWx1LNbcOfx8CqL8,5671
|
|
17
|
+
sonolus/backend/optimize/constant_evaluation.py,sha256=ozYB__haE2A_L2NBh2H3IT__Z65FBBtbjh6BJfQRC_A,15988
|
|
18
|
+
sonolus/backend/optimize/copy_coalesce.py,sha256=4vUe-y2w8sGyrguSlp8O_p0uy9rEUu5M-smI0F5UMQw,4404
|
|
19
|
+
sonolus/backend/optimize/dead_code.py,sha256=qYmUqBmAp-O5mUpNusOoMEJVkbc9YIHx6Ynrg6dyNGY,8077
|
|
20
|
+
sonolus/backend/optimize/dominance.py,sha256=oTFUqN8twrw6lTzqr1nlYs-Gk1NzIa-4qGdEQvDGzlM,3217
|
|
21
|
+
sonolus/backend/optimize/flow.py,sha256=mMf5Um2uxwF65nw13UiIv7dFMHZZHnyWv2WaOVnGw2c,4266
|
|
22
|
+
sonolus/backend/optimize/inlining.py,sha256=jwTiuHivSq9bB7FfVbD9Wd_Ztaz8ml_5CRnikr2d6yU,5719
|
|
23
|
+
sonolus/backend/optimize/liveness.py,sha256=c0ejVf1LNcYBlByf3rxkNs18IoXmiOdCFwEk3Afv8DE,7121
|
|
24
|
+
sonolus/backend/optimize/optimize.py,sha256=It_z8fAaTsG8bRNrvR5mQzVjmwZT5I1whBvy4F-Xoos,1473
|
|
25
|
+
sonolus/backend/optimize/passes.py,sha256=BQy-BovGu234Y12c-vpBmTaNNl9oeW4qZ8A3q5y5nLs,1684
|
|
26
|
+
sonolus/backend/optimize/simplify.py,sha256=Tz4RftjH5TpIxoIOwiZuLchsrwzI9GaS_exdjAW6HKk,7927
|
|
27
|
+
sonolus/backend/optimize/ssa.py,sha256=D3CQm3s3gcuU0_k_0pXVrwirm4xjAZsX6corrTDS1Co,9013
|
|
28
|
+
sonolus/build/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
|
+
sonolus/build/cli.py,sha256=zWkiY2frHHIk3nqP8XrzV1ez1zU67deNZ7WeFR5SfVo,8801
|
|
30
|
+
sonolus/build/collection.py,sha256=kOVnpQC_KHAsyTM4nAplSh6QE16CgO-H6PP1GItWm78,12187
|
|
31
|
+
sonolus/build/compile.py,sha256=nWtQRao_k-ChJhjmYFpPMRlPgxFI0t9wtMWSRHQcnbQ,5586
|
|
32
|
+
sonolus/build/engine.py,sha256=AYzJHRcYaR-PidR9eG6Tbr4Xz5-spHTOp4S9F4nm-UY,11178
|
|
33
|
+
sonolus/build/level.py,sha256=AjvK4725nqDcg7oGn5kWocBdG-AcirXpku74T7c2epA,673
|
|
34
|
+
sonolus/build/node.py,sha256=gnX71RYDUOK_gYMpinQi-bLWO4csqcfiG5gFmhxzSec,1330
|
|
35
|
+
sonolus/build/project.py,sha256=DhNqgHnm73qKUOhrg1JPlWEL0Vg7VxcGUbNokpMWzVE,6315
|
|
36
|
+
sonolus/script/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
|
+
sonolus/script/archetype.py,sha256=UbIAn6xmCOUpp6Aa8Y_Bd85LhWglinG6o-TE0sRdLPw,38716
|
|
38
|
+
sonolus/script/array.py,sha256=mUOY0cbjbSBssQNwmJ4EbySmyUGg2I-lnLmqDKu8Vh8,9798
|
|
39
|
+
sonolus/script/array_like.py,sha256=OVOLMadS-Jj941S-EgoxlFPdaQLT6ZrHzaMnKtB6PfU,8346
|
|
40
|
+
sonolus/script/bucket.py,sha256=oaaB7OXtBWzr8sLzPOpm8-GMhs7FzEhYrI1uVcM0DHM,7453
|
|
41
|
+
sonolus/script/containers.py,sha256=tf0-UUSq8NH6k9jIKq6qgXRxanFQ0f5olH6uHghTynk,13171
|
|
42
|
+
sonolus/script/debug.py,sha256=-rFPOgtmGLjfBsCPzPKObb4t-x5VN3QYb2BznZGDMgI,2457
|
|
43
|
+
sonolus/script/easing.py,sha256=7zaDKIfM_whUpb4FBz1DAF4NNG2vk_nDjl8kL2Y90aU,11396
|
|
44
|
+
sonolus/script/effect.py,sha256=V9bJvMzs1O4C1PjTOKgsAXov-l4AnDb2h38-DzmeWpI,5838
|
|
45
|
+
sonolus/script/engine.py,sha256=_5ZYPT4irTePjki16sFI7A8dzdm3kXAS7tvrp_ocG7Y,10661
|
|
46
|
+
sonolus/script/globals.py,sha256=Z8RfLkgDuXPIKiq-aOqblP0s__ALGmvcKdlyWZH21EM,9166
|
|
47
|
+
sonolus/script/instruction.py,sha256=PNfxC1dhT_hB0BxhDV3KXMn_kKxfI0t1iZmg8m6ddMU,6725
|
|
48
|
+
sonolus/script/interval.py,sha256=H-xedXbiLia-JU-Tafxq2rSoQ7pBOAeNOnyovrQyp14,9462
|
|
49
|
+
sonolus/script/iterator.py,sha256=OHnIOKRchzVCMaN33Tubo6EzqV61ZYUxDWIJ2S5G6iQ,4590
|
|
50
|
+
sonolus/script/level.py,sha256=wR23xk-NOcW_JMRb3R12sqIXCLSZL-7cM3y7IpMF1J0,6333
|
|
51
|
+
sonolus/script/metadata.py,sha256=ttRK27eojHf3So50KQJ-8yj3udZoN1bli5iD-knaeLw,753
|
|
52
|
+
sonolus/script/num.py,sha256=5PmfugfKtQ8Mobt9Ul_agv4XyqZr_EtNNPYMQcL14xQ,14178
|
|
53
|
+
sonolus/script/options.py,sha256=AljmjEyWH_Aquv_Jj-sx3GA3l5ZoOSl9acDv19FMHNA,7576
|
|
54
|
+
sonolus/script/particle.py,sha256=K06ArT9tstRNdbuGIviDmDDWcK3-ieA53LHg0Xvizow,8304
|
|
55
|
+
sonolus/script/pointer.py,sha256=Rlu3fW4odvJzZRFNwk6pGA8JLrp4J-PlONBOg3mSA8A,1203
|
|
56
|
+
sonolus/script/printing.py,sha256=mNYu9QWiacBBGZrnePZQMVwbbguoelUps9GiOK_aVRU,2096
|
|
57
|
+
sonolus/script/project.py,sha256=jLndgGJHdkqFYe-lDl_IzTjQ4gOSuy80en8WoSWXnB8,3340
|
|
58
|
+
sonolus/script/quad.py,sha256=e2kXKSp9K46Q9qstLsGLFPxaW7Z2kfVfignA8Ka2-Pw,8375
|
|
59
|
+
sonolus/script/record.py,sha256=jHt2RjDD7FZ_eWAukEOZePu9VJaH-rwRqHdoEJpxCNY,11339
|
|
60
|
+
sonolus/script/runtime.py,sha256=xmf4HLShrcAqQ8qK3N-hGyVzEolgp34j_JSJH9orwIY,19930
|
|
61
|
+
sonolus/script/sprite.py,sha256=CMcRAZ2hejXnaBmY2_n1_rj6hGOgPP5zEW-BpyaEVOY,16256
|
|
62
|
+
sonolus/script/text.py,sha256=IsoINZJXefjReYDjJFwVaFsUCdgeQvPBDeywljM2dWo,13025
|
|
63
|
+
sonolus/script/timing.py,sha256=ZR0ypV2PIoDCMHHGOMfCeezStCsBQdzomdqaz5VKex0,2981
|
|
64
|
+
sonolus/script/transform.py,sha256=hH6KSRQC8vV-Z10CRCrGewMYqQwUMH3mQIEmniuC2Zw,10760
|
|
65
|
+
sonolus/script/ui.py,sha256=R5J1QSvul-cFCUCjuEHbnzp4-CA2I8u608_wfJRr80s,7277
|
|
66
|
+
sonolus/script/values.py,sha256=GWl_gmNzwEBwm5fqj2LErfwOz8iWTJEAoFyOUnkBRS0,1028
|
|
67
|
+
sonolus/script/vec.py,sha256=4ntfJ96zxKR-nVZluUgHd-Ud4vNfButfiv7EsroZxfE,7002
|
|
68
|
+
sonolus/script/internal/__init__.py,sha256=T6rzLoiOUaiSQtaHMZ88SNO-ijSjSSv33TKtUwu-Ms8,136
|
|
69
|
+
sonolus/script/internal/builtin_impls.py,sha256=w9QqxJQ2YR5pVy820qWmAy0utbsW4hcSzMvA_yNgsvg,8186
|
|
70
|
+
sonolus/script/internal/callbacks.py,sha256=vWzJG8uiJoEtsNnbeZPqOHogCwoLpz2D1MnHY2wVV8s,2801
|
|
71
|
+
sonolus/script/internal/constant.py,sha256=4kJpLek7PcdB0aTL8ygV4GYDZSDuHoWHljaIZpTtRrY,3842
|
|
72
|
+
sonolus/script/internal/context.py,sha256=xW7yFWr13yf77Uk4C_F7v9Kp4ZP1o30uZuBkvK1KyLA,14157
|
|
73
|
+
sonolus/script/internal/descriptor.py,sha256=XRFey-EjiAm_--KsNl-8N0Mi_iyQwlPh68gDp0pKf3E,392
|
|
74
|
+
sonolus/script/internal/dict_impl.py,sha256=alu_wKGSk1kZajNf64qbe7t71shEzD4N5xNIATH8Swo,1885
|
|
75
|
+
sonolus/script/internal/error.py,sha256=ZNnsvQVQAnFKzcvsm6-sste2lo-tP5pPI8sD7XlAZWc,490
|
|
76
|
+
sonolus/script/internal/generic.py,sha256=YU1hUJoBcGc0OSrFStK5JI6CikOwSmd_IR20pCuT82k,7310
|
|
77
|
+
sonolus/script/internal/impl.py,sha256=HKQVoHknw5t43Wmj_G1vFjGSmnFpOj1FUYnhbUM3rHc,2969
|
|
78
|
+
sonolus/script/internal/introspection.py,sha256=SL2zaYjid0kkcj6ZbFLIwhgh7WKZBaAHhlbSJGr6PWs,974
|
|
79
|
+
sonolus/script/internal/math_impls.py,sha256=Xk7tLMnV2npzPJWtHlspONQHt09Gh2YLdHhAjx4jkdE,2320
|
|
80
|
+
sonolus/script/internal/native.py,sha256=XKlNnWSJ-lxbwVGWhGj_CSSoWsVN18imqT5sAsDJT1w,1551
|
|
81
|
+
sonolus/script/internal/random.py,sha256=6Ku5edRcDUh7rtqEEYCJz0BQavw69RALsVHS25z50pI,1695
|
|
82
|
+
sonolus/script/internal/range.py,sha256=lrTanQFHU7RuQxSSPwDdoC30Y8FnHGxcP1Ahditu3zU,2297
|
|
83
|
+
sonolus/script/internal/transient.py,sha256=c_Jg4yJJ_xvxMGNx9XECHCdiDlMhjfClf5ntzrPr6uc,1643
|
|
84
|
+
sonolus/script/internal/tuple_impl.py,sha256=vjXmScLVdeTkDn3t9fgIRqtW31iwngnaP2rmA6nlsLw,3431
|
|
85
|
+
sonolus/script/internal/value.py,sha256=hEq0YhXMj4uaGQA-r_W8PDOp_MY97nW30JKlpj-aZLM,4496
|
|
86
|
+
sonolus_py-0.2.0.dist-info/METADATA,sha256=LOjlu1hCu6r3VgzL7DbplSaBsjabZ2GWzsDsilNdSFc,238
|
|
87
|
+
sonolus_py-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
88
|
+
sonolus_py-0.2.0.dist-info/entry_points.txt,sha256=oTYspY_b7SA8TptEMTDxh4-Aj-ZVPnYC9f1lqH6s9G4,54
|
|
89
|
+
sonolus_py-0.2.0.dist-info/licenses/LICENSE,sha256=JEKpqVhQYfEc7zg3Mj462sKbKYmO1K7WmvX1qvg9IJk,1067
|
|
90
|
+
sonolus_py-0.2.0.dist-info/RECORD,,
|