sonolus.py 0.1.1__tar.gz → 0.1.2__tar.gz
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_py-0.1.1 → sonolus_py-0.1.2}/PKG-INFO +1 -1
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/pyproject.toml +1 -1
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/mode.py +4 -4
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/visitor.py +2 -2
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/build/compile.py +3 -3
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/build/engine.py +20 -4
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/archetype.py +42 -11
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/bucket.py +2 -2
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/callbacks.py +10 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/comptime.py +14 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/debug.py +1 -1
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/effect.py +11 -11
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/engine.py +26 -6
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/globals.py +79 -44
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/graphics.py +37 -28
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/internal/builtin_impls.py +3 -3
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/internal/native.py +2 -2
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/interval.py +14 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/level.py +7 -7
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/num.py +30 -4
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/options.py +4 -4
- sonolus_py-0.1.2/sonolus/script/particle.py +157 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/pointer.py +3 -3
- sonolus_py-0.1.2/sonolus/script/preview.py +81 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/runtime.py +113 -35
- sonolus_py-0.1.2/sonolus/script/sprite.py +333 -0
- sonolus_py-0.1.2/sonolus/script/text.py +407 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/transform.py +13 -17
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/vec.py +24 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/tests/script/conftest.py +1 -1
- sonolus_py-0.1.1/sonolus/script/particle.py +0 -157
- sonolus_py-0.1.1/sonolus/script/sprite.py +0 -331
- sonolus_py-0.1.1/sonolus/script/text.py +0 -404
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/.gitignore +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/.python-version +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/LICENSE +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/README.md +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/scripts/generate.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/scripts/runtimes/Engine/Tutorial/Blocks.json +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/scripts/runtimes/Functions.json +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/scripts/runtimes/Level/Play/Blocks.json +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/scripts/runtimes/Level/Preview/Blocks.json +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/scripts/runtimes/Level/Watch/Blocks.json +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/__init__.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/__init__.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/allocate.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/blocks.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/excepthook.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/finalize.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/flow.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/interpret.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/ir.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/node.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/ops.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/optimize.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/passes.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/place.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/simplify.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/backend/utils.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/build/__init__.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/build/cli.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/build/collection.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/build/defaults.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/build/level.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/build/node.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/build/project.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/py.typed +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/__init__.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/array.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/containers.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/icon.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/internal/__init__.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/internal/context.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/internal/descriptor.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/internal/error.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/internal/generic.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/internal/impl.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/internal/introspection.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/internal/value.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/iterator.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/math.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/project.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/range.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/record.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/timing.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/ui.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/sonolus/script/values.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/tests/__init__.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/tests/script/__init__.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/tests/script/test_array.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/tests/script/test_array_map.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/tests/script/test_assert.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/tests/script/test_helpers.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/tests/script/test_interval.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/tests/script/test_num.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/tests/script/test_range.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/tests/script/test_record.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/tests/script/test_var_array.py +0 -0
- {sonolus_py-0.1.1 → sonolus_py-0.1.2}/uv.lock +0 -0
|
@@ -7,10 +7,10 @@ from sonolus.backend.blocks import Block, PlayBlock, PreviewBlock, TutorialBlock
|
|
|
7
7
|
class Mode(Enum):
|
|
8
8
|
blocks: type[Block]
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
PLAY = (PlayBlock,)
|
|
11
|
+
WATCH = (WatchBlock,)
|
|
12
|
+
PREVIEW = (PreviewBlock,)
|
|
13
|
+
TUTORIAL = (TutorialBlock,)
|
|
14
14
|
|
|
15
15
|
def __init__(self, blocks: type[Block]):
|
|
16
16
|
self.blocks = blocks
|
|
@@ -17,7 +17,7 @@ from sonolus.script.internal.error import CompilationError
|
|
|
17
17
|
from sonolus.script.internal.impl import try_validate_value, validate_value
|
|
18
18
|
from sonolus.script.internal.value import Value
|
|
19
19
|
from sonolus.script.iterator import SonolusIterator
|
|
20
|
-
from sonolus.script.num import Num
|
|
20
|
+
from sonolus.script.num import Num, is_num
|
|
21
21
|
|
|
22
22
|
_compiler_internal_ = True
|
|
23
23
|
|
|
@@ -833,7 +833,7 @@ class Visitor(ast.NodeVisitor):
|
|
|
833
833
|
|
|
834
834
|
def ensure_boolean_num(self, value) -> Num:
|
|
835
835
|
# This just checks the type for now, although we could support custom __bool__ implementations in the future
|
|
836
|
-
if not
|
|
836
|
+
if not is_num(value):
|
|
837
837
|
raise TypeError(f"Invalid type where a bool (Num) was expected: {type(value).__name__}")
|
|
838
838
|
return value
|
|
839
839
|
|
|
@@ -19,7 +19,7 @@ from sonolus.script.internal.context import (
|
|
|
19
19
|
ctx,
|
|
20
20
|
using_ctx,
|
|
21
21
|
)
|
|
22
|
-
from sonolus.script.num import
|
|
22
|
+
from sonolus.script.num import is_num
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def compile_mode(
|
|
@@ -43,7 +43,7 @@ def compile_mode(
|
|
|
43
43
|
archetype_data["imports"] = [
|
|
44
44
|
{"name": name, "index": index} for name, index in archetype._imported_keys_.items()
|
|
45
45
|
]
|
|
46
|
-
if mode == Mode.
|
|
46
|
+
if mode == Mode.PLAY:
|
|
47
47
|
archetype_data["exports"] = [
|
|
48
48
|
{"name": name, "index": index} for name, index in archetype._exported_keys_.items()
|
|
49
49
|
]
|
|
@@ -85,6 +85,6 @@ def callback_to_cfg(
|
|
|
85
85
|
result = compile_and_call(callback, archetype._for_compilation())
|
|
86
86
|
else:
|
|
87
87
|
result = compile_and_call(callback)
|
|
88
|
-
if
|
|
88
|
+
if is_num(result):
|
|
89
89
|
ctx().add_statements(IRInstr(Op.Break, [IRConst(1), result.ir()]))
|
|
90
90
|
return context_to_cfg(context)
|
|
@@ -7,7 +7,7 @@ from pathlib import Path
|
|
|
7
7
|
|
|
8
8
|
from sonolus.backend.mode import Mode
|
|
9
9
|
from sonolus.build.compile import compile_mode
|
|
10
|
-
from sonolus.build.defaults import
|
|
10
|
+
from sonolus.build.defaults import EMPTY_ENGINE_TUTORIAL_DATA
|
|
11
11
|
from sonolus.script.archetype import BaseArchetype
|
|
12
12
|
from sonolus.script.bucket import Buckets
|
|
13
13
|
from sonolus.script.callbacks import update_spawn_callback
|
|
@@ -61,11 +61,16 @@ def package_engine(engine: EngineData):
|
|
|
61
61
|
rom=rom,
|
|
62
62
|
update_spawn=engine.watch.update_spawn,
|
|
63
63
|
)
|
|
64
|
+
preview_mode = build_preview_mode(
|
|
65
|
+
archetypes=engine.preview.archetypes,
|
|
66
|
+
skin=engine.preview.skin,
|
|
67
|
+
rom=rom,
|
|
68
|
+
)
|
|
64
69
|
return PackagedEngine(
|
|
65
70
|
configuration=package_output(configuration),
|
|
66
71
|
play_data=package_output(play_data),
|
|
67
72
|
watch_data=package_output(watch_data),
|
|
68
|
-
preview_data=package_output(
|
|
73
|
+
preview_data=package_output(preview_mode),
|
|
69
74
|
tutorial_data=package_output(EMPTY_ENGINE_TUTORIAL_DATA),
|
|
70
75
|
rom=package_rom(rom),
|
|
71
76
|
)
|
|
@@ -90,7 +95,7 @@ def build_play_mode(
|
|
|
90
95
|
rom: ReadOnlyMemory,
|
|
91
96
|
):
|
|
92
97
|
return {
|
|
93
|
-
**compile_mode(mode=Mode.
|
|
98
|
+
**compile_mode(mode=Mode.PLAY, rom=rom, archetypes=archetypes, global_callbacks=None),
|
|
94
99
|
"skin": build_skin(skin),
|
|
95
100
|
"effect": build_effects(effects),
|
|
96
101
|
"particle": build_particles(particles),
|
|
@@ -109,7 +114,7 @@ def build_watch_mode(
|
|
|
109
114
|
):
|
|
110
115
|
return {
|
|
111
116
|
**compile_mode(
|
|
112
|
-
mode=Mode.
|
|
117
|
+
mode=Mode.WATCH, rom=rom, archetypes=archetypes, global_callbacks=[(update_spawn_callback, update_spawn)]
|
|
113
118
|
),
|
|
114
119
|
"skin": build_skin(skin),
|
|
115
120
|
"effect": build_effects(effects),
|
|
@@ -118,6 +123,17 @@ def build_watch_mode(
|
|
|
118
123
|
}
|
|
119
124
|
|
|
120
125
|
|
|
126
|
+
def build_preview_mode(
|
|
127
|
+
archetypes: list[type[BaseArchetype]],
|
|
128
|
+
skin: Skin,
|
|
129
|
+
rom: ReadOnlyMemory,
|
|
130
|
+
):
|
|
131
|
+
return {
|
|
132
|
+
**compile_mode(mode=Mode.PREVIEW, rom=rom, archetypes=archetypes, global_callbacks=None),
|
|
133
|
+
"skin": build_skin(skin),
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
121
137
|
def build_skin(skin: Skin) -> JsonValue:
|
|
122
138
|
return {"sprites": [{"name": name, "id": i} for i, name in enumerate(skin._sprites_)]}
|
|
123
139
|
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import inspect
|
|
4
4
|
from collections.abc import Callable
|
|
5
5
|
from dataclasses import dataclass
|
|
6
|
-
from enum import Enum
|
|
6
|
+
from enum import Enum, StrEnum
|
|
7
7
|
from types import FunctionType
|
|
8
8
|
from typing import Annotated, Any, ClassVar, Self, get_origin
|
|
9
9
|
|
|
@@ -11,7 +11,7 @@ from sonolus.backend.ir import IRConst, IRInstr
|
|
|
11
11
|
from sonolus.backend.mode import Mode
|
|
12
12
|
from sonolus.backend.ops import Op
|
|
13
13
|
from sonolus.script.bucket import Bucket, Judgment
|
|
14
|
-
from sonolus.script.callbacks import PLAY_CALLBACKS, WATCH_ARCHETYPE_CALLBACKS, CallbackInfo
|
|
14
|
+
from sonolus.script.callbacks import PLAY_CALLBACKS, PREVIEW_CALLBACKS, WATCH_ARCHETYPE_CALLBACKS, CallbackInfo
|
|
15
15
|
from sonolus.script.internal.context import ctx
|
|
16
16
|
from sonolus.script.internal.descriptor import SonolusDescriptor
|
|
17
17
|
from sonolus.script.internal.generic import validate_concrete_type
|
|
@@ -174,11 +174,11 @@ _annotation_defaults: dict[Callable, ArchetypeFieldInfo] = {
|
|
|
174
174
|
|
|
175
175
|
|
|
176
176
|
class StandardImport:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
177
|
+
BEAT = Annotated[float, imported(name="#BEAT")]
|
|
178
|
+
BPM = Annotated[float, imported(name="#BPM")]
|
|
179
|
+
TIMESCALE = Annotated[float, imported(name="#TIMESCALE")]
|
|
180
|
+
JUDGMENT = Annotated[int, imported(name="#JUDGMENT")]
|
|
181
|
+
ACCURACY = Annotated[float, imported(name="#ACCURACY")]
|
|
182
182
|
|
|
183
183
|
|
|
184
184
|
def callback[T: Callable](order: int) -> Callable[[T], T]:
|
|
@@ -559,16 +559,42 @@ class WatchArchetype(BaseArchetype):
|
|
|
559
559
|
self.result.target_time = value
|
|
560
560
|
|
|
561
561
|
|
|
562
|
+
class PreviewArchetype(BaseArchetype):
|
|
563
|
+
_supported_callbacks_ = PREVIEW_CALLBACKS
|
|
564
|
+
|
|
565
|
+
def preprocess(self):
|
|
566
|
+
pass
|
|
567
|
+
|
|
568
|
+
def render(self):
|
|
569
|
+
pass
|
|
570
|
+
|
|
571
|
+
@property
|
|
572
|
+
def _info(self) -> PreviewEntityInfo:
|
|
573
|
+
if not ctx():
|
|
574
|
+
raise RuntimeError("Calling info is only allowed within a callback")
|
|
575
|
+
match self._data_:
|
|
576
|
+
case ArchetypeSelfData():
|
|
577
|
+
return deref(ctx().blocks.EntityInfo, 0, PreviewEntityInfo)
|
|
578
|
+
case ArchetypeReferenceData(index=index):
|
|
579
|
+
return deref(ctx().blocks.EntityInfoArray, index * PreviewEntityInfo._size_(), PreviewEntityInfo)
|
|
580
|
+
case _:
|
|
581
|
+
raise RuntimeError("Info is only accessible from the entity itself")
|
|
582
|
+
|
|
583
|
+
@property
|
|
584
|
+
def index(self) -> int:
|
|
585
|
+
return self._info.index
|
|
586
|
+
|
|
587
|
+
|
|
562
588
|
@meta_fn
|
|
563
589
|
def entity_info_at(index: Num) -> PlayEntityInfo | WatchEntityInfo | PreviewEntityInfo:
|
|
564
590
|
if not ctx():
|
|
565
591
|
raise RuntimeError("Calling entity_info_at is only allowed within a callback")
|
|
566
592
|
match ctx().global_state.mode:
|
|
567
|
-
case Mode.
|
|
593
|
+
case Mode.PLAY:
|
|
568
594
|
return deref(ctx().blocks.EntityInfoArray, index * PlayEntityInfo._size_(), PlayEntityInfo)
|
|
569
|
-
case Mode.
|
|
595
|
+
case Mode.WATCH:
|
|
570
596
|
return deref(ctx().blocks.EntityInfoArray, index * WatchEntityInfo._size_(), WatchEntityInfo)
|
|
571
|
-
case Mode.
|
|
597
|
+
case Mode.PREVIEW:
|
|
572
598
|
return deref(ctx().blocks.EntityInfoArray, index * PreviewEntityInfo._size_(), PreviewEntityInfo)
|
|
573
599
|
case _:
|
|
574
600
|
raise RuntimeError(f"Entity info is not available in mode '{ctx().global_state.mode}'")
|
|
@@ -581,7 +607,7 @@ def archetype_life_of(archetype: type[BaseArchetype] | BaseArchetype) -> Archety
|
|
|
581
607
|
if not ctx():
|
|
582
608
|
raise RuntimeError("Calling archetype_life_of is only allowed within a callback")
|
|
583
609
|
match ctx().global_state.mode:
|
|
584
|
-
case Mode.
|
|
610
|
+
case Mode.PLAY | Mode.WATCH:
|
|
585
611
|
return deref(ctx().blocks.ArchetypeLife, archetype.id() * ArchetypeLife._size_(), ArchetypeLife)
|
|
586
612
|
case _:
|
|
587
613
|
raise RuntimeError(f"Archetype life is not available in mode '{ctx().global_state.mode}'")
|
|
@@ -649,3 +675,8 @@ class EntityRef[A: BaseArchetype](Record):
|
|
|
649
675
|
|
|
650
676
|
def get(self) -> A:
|
|
651
677
|
return self.archetype().at(Num(self.index))
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
class StandardArchetypeName(StrEnum):
|
|
681
|
+
BPM_CHANGE = "#BPM_CHANGE"
|
|
682
|
+
TIMESCALE_CHANGE = "#TIMESCALE_CHANGE"
|
|
@@ -88,9 +88,9 @@ class Bucket(Record):
|
|
|
88
88
|
if not ctx():
|
|
89
89
|
raise RuntimeError("Bucket window access outside of compilation")
|
|
90
90
|
match ctx().global_state.mode:
|
|
91
|
-
case Mode.
|
|
91
|
+
case Mode.PLAY:
|
|
92
92
|
return deref(ctx().blocks.LevelBucket, self.id * JudgmentWindow._size_(), JudgmentWindow)
|
|
93
|
-
case Mode.
|
|
93
|
+
case Mode.WATCH:
|
|
94
94
|
return deref(ctx().blocks.LevelBucket, self.id * JudgmentWindow._size_(), JudgmentWindow)
|
|
95
95
|
case _:
|
|
96
96
|
raise RuntimeError("Invalid mode for bucket window access")
|
|
@@ -75,6 +75,12 @@ update_spawn_callback = CallbackInfo(
|
|
|
75
75
|
supports_order=False,
|
|
76
76
|
returns_value=True,
|
|
77
77
|
)
|
|
78
|
+
render_callback = CallbackInfo(
|
|
79
|
+
name="render",
|
|
80
|
+
py_name="render",
|
|
81
|
+
supports_order=False,
|
|
82
|
+
returns_value=False,
|
|
83
|
+
)
|
|
78
84
|
|
|
79
85
|
|
|
80
86
|
def _by_name(*callbacks: CallbackInfo) -> dict[str, CallbackInfo]:
|
|
@@ -103,3 +109,7 @@ WATCH_ARCHETYPE_CALLBACKS = _by_name(
|
|
|
103
109
|
WATCH_GLOBAL_CALLBACKS = _by_name(
|
|
104
110
|
update_spawn_callback,
|
|
105
111
|
)
|
|
112
|
+
PREVIEW_CALLBACKS = _by_name(
|
|
113
|
+
preprocess_callback,
|
|
114
|
+
render_callback,
|
|
115
|
+
)
|
|
@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, Any, Self, TypeVar, final
|
|
|
3
3
|
|
|
4
4
|
from sonolus.backend.place import BlockPlace
|
|
5
5
|
from sonolus.script.internal.generic import GenericValue
|
|
6
|
+
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
@final
|
|
@@ -92,6 +93,19 @@ class _Comptime[T, V](GenericValue):
|
|
|
92
93
|
def _copy_(self) -> Self:
|
|
93
94
|
return self
|
|
94
95
|
|
|
96
|
+
@meta_fn
|
|
97
|
+
def __eq__(self, other):
|
|
98
|
+
other = validate_value(other)
|
|
99
|
+
match self.value():
|
|
100
|
+
case str():
|
|
101
|
+
return other._is_py_() and other._as_py_() == self.value()
|
|
102
|
+
case _:
|
|
103
|
+
raise TypeError("Unsupported comparison with comptime value")
|
|
104
|
+
|
|
105
|
+
@meta_fn
|
|
106
|
+
def __hash__(self):
|
|
107
|
+
return hash(self.value())
|
|
108
|
+
|
|
95
109
|
@classmethod
|
|
96
110
|
def _alloc_(cls) -> Self:
|
|
97
111
|
return cls._instance
|
|
@@ -65,6 +65,6 @@ def terminate():
|
|
|
65
65
|
def visualize_cfg(fn: Callable[[], Any]) -> str:
|
|
66
66
|
from sonolus.build.compile import callback_to_cfg
|
|
67
67
|
|
|
68
|
-
cfg = callback_to_cfg(GlobalContextState(Mode.
|
|
68
|
+
cfg = callback_to_cfg(GlobalContextState(Mode.PLAY), fn, "")
|
|
69
69
|
cfg = CoalesceFlow().run(cfg)
|
|
70
70
|
return cfg_to_mermaid(cfg)
|
|
@@ -113,17 +113,17 @@ def effects[T](cls: type[T]) -> T | Effects:
|
|
|
113
113
|
|
|
114
114
|
|
|
115
115
|
class StandardEffect:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
116
|
+
MISS = Annotated[Effect, effect("#MISS")]
|
|
117
|
+
PERFECT = Annotated[Effect, effect("#PERFECT")]
|
|
118
|
+
GREAT = Annotated[Effect, effect("#GREAT")]
|
|
119
|
+
GOOD = Annotated[Effect, effect("#GOOD")]
|
|
120
|
+
HOLD = Annotated[Effect, effect("#HOLD")]
|
|
121
|
+
MISS_ALTERNATIVE = Annotated[Effect, effect("#MISS_ALTERNATIVE")]
|
|
122
|
+
PERFECT_ALTERNATIVE = Annotated[Effect, effect("#PERFECT_ALTERNATIVE")]
|
|
123
|
+
GREAT_ALTERNATIVE = Annotated[Effect, effect("#GREAT_ALTERNATIVE")]
|
|
124
|
+
GOOD_ALTERNATIVE = Annotated[Effect, effect("#GOOD_ALTERNATIVE")]
|
|
125
|
+
HOLD_ALTERNATIVE = Annotated[Effect, effect("#HOLD_ALTERNATIVE")]
|
|
126
|
+
STAGE = Annotated[Effect, effect("#STAGE")]
|
|
127
127
|
|
|
128
128
|
|
|
129
129
|
@effects
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
from collections.abc import Callable
|
|
4
4
|
|
|
5
5
|
from sonolus.build.collection import Asset
|
|
6
|
-
from sonolus.script.archetype import BaseArchetype
|
|
6
|
+
from sonolus.script.archetype import BaseArchetype, PlayArchetype, PreviewArchetype, WatchArchetype
|
|
7
7
|
from sonolus.script.bucket import Buckets, EmptyBuckets
|
|
8
8
|
from sonolus.script.effect import Effects, EmptyEffects
|
|
9
9
|
from sonolus.script.options import EmptyOptions, Options
|
|
@@ -61,6 +61,10 @@ class PlayMode:
|
|
|
61
61
|
self.particles = particles
|
|
62
62
|
self.buckets = buckets
|
|
63
63
|
|
|
64
|
+
for archetype in self.archetypes:
|
|
65
|
+
if not issubclass(archetype, PlayArchetype):
|
|
66
|
+
raise ValueError(f"archetype {archetype} is not a PlayArchetype")
|
|
67
|
+
|
|
64
68
|
|
|
65
69
|
class WatchMode:
|
|
66
70
|
def __init__(
|
|
@@ -80,13 +84,27 @@ class WatchMode:
|
|
|
80
84
|
self.buckets = buckets
|
|
81
85
|
self.update_spawn = update_spawn
|
|
82
86
|
|
|
87
|
+
for archetype in self.archetypes:
|
|
88
|
+
if not issubclass(archetype, WatchArchetype):
|
|
89
|
+
raise ValueError(f"archetype {archetype} is not a PlayArchetype")
|
|
83
90
|
|
|
84
|
-
class EngineData:
|
|
85
|
-
ui: UiConfig
|
|
86
|
-
options: Options
|
|
87
|
-
play: PlayMode
|
|
88
|
-
watch: WatchMode
|
|
89
91
|
|
|
92
|
+
class PreviewMode:
|
|
93
|
+
def __init__(
|
|
94
|
+
self,
|
|
95
|
+
*,
|
|
96
|
+
archetypes: list[type[BaseArchetype]] | None = None,
|
|
97
|
+
skin: Skin = EmptySkin,
|
|
98
|
+
) -> None:
|
|
99
|
+
self.archetypes = archetypes or []
|
|
100
|
+
self.skin = skin
|
|
101
|
+
|
|
102
|
+
for archetype in self.archetypes:
|
|
103
|
+
if not issubclass(archetype, PreviewArchetype):
|
|
104
|
+
raise ValueError(f"archetype {archetype} is not a BaseArchetype")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
class EngineData:
|
|
90
108
|
def __init__(
|
|
91
109
|
self,
|
|
92
110
|
*,
|
|
@@ -94,8 +112,10 @@ class EngineData:
|
|
|
94
112
|
options: Options = EmptyOptions,
|
|
95
113
|
play: PlayMode | None = None,
|
|
96
114
|
watch: WatchMode | None = None,
|
|
115
|
+
preview: PreviewMode | None = None,
|
|
97
116
|
) -> None:
|
|
98
117
|
self.ui = ui or UiConfig()
|
|
99
118
|
self.options = options
|
|
100
119
|
self.play = play or PlayMode()
|
|
101
120
|
self.watch = watch or WatchMode(update_spawn=default_callback)
|
|
121
|
+
self.preview = preview or PreviewMode()
|
|
@@ -86,42 +86,47 @@ def create_global(cls: type, blocks: dict[Mode, Block], offset: int | None):
|
|
|
86
86
|
|
|
87
87
|
@dataclass_transform()
|
|
88
88
|
def _play_runtime_environment[T](cls: type[T]) -> T:
|
|
89
|
-
return create_global(cls, {Mode.
|
|
89
|
+
return create_global(cls, {Mode.PLAY: PlayBlock.RuntimeEnvironment}, 0)
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
@dataclass_transform()
|
|
93
93
|
def _watch_runtime_environment[T](cls: type[T]) -> T:
|
|
94
|
-
return create_global(cls, {Mode.
|
|
94
|
+
return create_global(cls, {Mode.WATCH: WatchBlock.RuntimeEnvironment}, 0)
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
@dataclass_transform()
|
|
98
98
|
def _tutorial_runtime_environment[T](cls: type[T]) -> T:
|
|
99
|
-
return create_global(cls, {Mode.
|
|
99
|
+
return create_global(cls, {Mode.TUTORIAL: TutorialBlock.RuntimeEnvironment}, 0)
|
|
100
100
|
|
|
101
101
|
|
|
102
102
|
@dataclass_transform()
|
|
103
103
|
def _preview_runtime_environment[T](cls: type[T]) -> T:
|
|
104
|
-
return create_global(cls, {Mode.
|
|
104
|
+
return create_global(cls, {Mode.PREVIEW: PreviewBlock.RuntimeEnvironment}, 0)
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
@dataclass_transform()
|
|
108
108
|
def _play_runtime_update[T](cls: type[T]) -> T:
|
|
109
|
-
return create_global(cls, {Mode.
|
|
109
|
+
return create_global(cls, {Mode.PLAY: PlayBlock.RuntimeUpdate}, 0)
|
|
110
110
|
|
|
111
111
|
|
|
112
112
|
@dataclass_transform()
|
|
113
113
|
def _watch_runtime_update[T](cls: type[T]) -> T:
|
|
114
|
-
return create_global(cls, {Mode.
|
|
114
|
+
return create_global(cls, {Mode.WATCH: WatchBlock.RuntimeUpdate}, 0)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@dataclass_transform()
|
|
118
|
+
def _preview_runtime_canvas[T](cls: type[T]) -> T:
|
|
119
|
+
return create_global(cls, {Mode.PREVIEW: PreviewBlock.RuntimeCanvas}, 0)
|
|
115
120
|
|
|
116
121
|
|
|
117
122
|
@dataclass_transform()
|
|
118
123
|
def _tutorial_runtime_update[T](cls: type[T]) -> T:
|
|
119
|
-
return create_global(cls, {Mode.
|
|
124
|
+
return create_global(cls, {Mode.TUTORIAL: TutorialBlock.RuntimeUpdate}, 0)
|
|
120
125
|
|
|
121
126
|
|
|
122
127
|
@dataclass_transform()
|
|
123
128
|
def _runtime_touch_array[T](cls: type[T]) -> T:
|
|
124
|
-
return create_global(cls, {Mode.
|
|
129
|
+
return create_global(cls, {Mode.PLAY: PlayBlock.RuntimeTouchArray}, 0)
|
|
125
130
|
|
|
126
131
|
|
|
127
132
|
@dataclass_transform()
|
|
@@ -129,9 +134,10 @@ def _runtime_skin_transform[T](cls: type[T]) -> T:
|
|
|
129
134
|
return create_global(
|
|
130
135
|
cls,
|
|
131
136
|
{
|
|
132
|
-
Mode.
|
|
133
|
-
Mode.
|
|
134
|
-
Mode.
|
|
137
|
+
Mode.PLAY: PlayBlock.RuntimeSkinTransform,
|
|
138
|
+
Mode.WATCH: WatchBlock.RuntimeSkinTransform,
|
|
139
|
+
Mode.PREVIEW: PreviewBlock.RuntimeSkinTransform,
|
|
140
|
+
Mode.TUTORIAL: TutorialBlock.RuntimeSkinTransform,
|
|
135
141
|
},
|
|
136
142
|
0,
|
|
137
143
|
)
|
|
@@ -142,9 +148,9 @@ def _runtime_particle_transform[T](cls: type[T]) -> T:
|
|
|
142
148
|
return create_global(
|
|
143
149
|
cls,
|
|
144
150
|
{
|
|
145
|
-
Mode.
|
|
146
|
-
Mode.
|
|
147
|
-
Mode.
|
|
151
|
+
Mode.PLAY: PlayBlock.RuntimeParticleTransform,
|
|
152
|
+
Mode.WATCH: WatchBlock.RuntimeParticleTransform,
|
|
153
|
+
Mode.TUTORIAL: TutorialBlock.RuntimeParticleTransform,
|
|
148
154
|
},
|
|
149
155
|
0,
|
|
150
156
|
)
|
|
@@ -155,55 +161,84 @@ def _runtime_background[T](cls: type[T]) -> T:
|
|
|
155
161
|
return create_global(
|
|
156
162
|
cls,
|
|
157
163
|
{
|
|
158
|
-
Mode.
|
|
159
|
-
Mode.
|
|
160
|
-
Mode.
|
|
164
|
+
Mode.PLAY: PlayBlock.RuntimeBackground,
|
|
165
|
+
Mode.WATCH: WatchBlock.RuntimeBackground,
|
|
166
|
+
Mode.TUTORIAL: TutorialBlock.RuntimeBackground,
|
|
161
167
|
},
|
|
162
168
|
0,
|
|
163
169
|
)
|
|
164
170
|
|
|
165
171
|
|
|
166
172
|
@dataclass_transform()
|
|
167
|
-
def
|
|
168
|
-
return create_global(
|
|
169
|
-
cls,
|
|
170
|
-
{
|
|
171
|
-
Mode.Play: PlayBlock.RuntimeUI,
|
|
172
|
-
Mode.Watch: WatchBlock.RuntimeUI,
|
|
173
|
-
Mode.Tutorial: TutorialBlock.RuntimeUI,
|
|
174
|
-
Mode.Preview: PreviewBlock.RuntimeUI,
|
|
175
|
-
},
|
|
176
|
-
0,
|
|
177
|
-
)
|
|
173
|
+
def _play_runtime_ui[T](cls: type[T]) -> T:
|
|
174
|
+
return create_global(cls, {Mode.PLAY: PlayBlock.RuntimeUI}, 0)
|
|
178
175
|
|
|
179
176
|
|
|
180
177
|
@dataclass_transform()
|
|
181
|
-
def
|
|
182
|
-
return create_global(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
178
|
+
def _watch_runtime_ui[T](cls: type[T]) -> T:
|
|
179
|
+
return create_global(cls, {Mode.WATCH: WatchBlock.RuntimeUI}, 0)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
@dataclass_transform()
|
|
183
|
+
def _tutorial_runtime_ui[T](cls: type[T]) -> T:
|
|
184
|
+
return create_global(cls, {Mode.TUTORIAL: TutorialBlock.RuntimeUI}, 0)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@dataclass_transform()
|
|
188
|
+
def _preview_runtime_ui[T](cls: type[T]) -> T:
|
|
189
|
+
return create_global(cls, {Mode.PREVIEW: PreviewBlock.RuntimeUI}, 0)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
@dataclass_transform()
|
|
193
|
+
def _play_runtime_ui_configuration[T](cls: type[T]) -> T:
|
|
194
|
+
return create_global(cls, {Mode.PLAY: PlayBlock.RuntimeUIConfiguration}, 0)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@dataclass_transform()
|
|
198
|
+
def _watch_runtime_ui_configuration[T](cls: type[T]) -> T:
|
|
199
|
+
return create_global(cls, {Mode.WATCH: WatchBlock.RuntimeUIConfiguration}, 0)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
@dataclass_transform()
|
|
203
|
+
def _tutorial_runtime_ui_configuration[T](cls: type[T]) -> T:
|
|
204
|
+
return create_global(cls, {Mode.TUTORIAL: TutorialBlock.RuntimeUIConfiguration}, 0)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@dataclass_transform()
|
|
208
|
+
def _preview_runtime_ui_configuration[T](cls: type[T]) -> T:
|
|
209
|
+
return create_global(cls, {Mode.PREVIEW: PreviewBlock.RuntimeUIConfiguration}, 0)
|
|
192
210
|
|
|
193
211
|
|
|
194
212
|
@dataclass_transform()
|
|
195
213
|
def _tutorial_instruction[T](cls: type[T]) -> T:
|
|
196
|
-
return create_global(cls, {Mode.
|
|
214
|
+
return create_global(cls, {Mode.TUTORIAL: TutorialBlock.TutorialInstruction}, 0)
|
|
197
215
|
|
|
198
216
|
|
|
199
217
|
@dataclass_transform()
|
|
200
218
|
def level_memory[T](cls: type[T]) -> T:
|
|
201
|
-
return create_global(
|
|
219
|
+
return create_global(
|
|
220
|
+
cls,
|
|
221
|
+
{
|
|
222
|
+
Mode.PLAY: PlayBlock.LevelMemory,
|
|
223
|
+
Mode.WATCH: WatchBlock.LevelMemory,
|
|
224
|
+
Mode.TUTORIAL: TutorialBlock.TutorialMemory,
|
|
225
|
+
},
|
|
226
|
+
None,
|
|
227
|
+
)
|
|
202
228
|
|
|
203
229
|
|
|
204
230
|
@dataclass_transform()
|
|
205
231
|
def level_data[T](cls: type[T]) -> T:
|
|
206
|
-
return create_global(
|
|
232
|
+
return create_global(
|
|
233
|
+
cls,
|
|
234
|
+
{
|
|
235
|
+
Mode.PLAY: PlayBlock.LevelData,
|
|
236
|
+
Mode.WATCH: WatchBlock.LevelData,
|
|
237
|
+
Mode.PREVIEW: PreviewBlock.PreviewData,
|
|
238
|
+
Mode.TUTORIAL: TutorialBlock.TutorialData,
|
|
239
|
+
},
|
|
240
|
+
None,
|
|
241
|
+
)
|
|
207
242
|
|
|
208
243
|
|
|
209
244
|
# level_option is handled by the options decorator
|
|
@@ -212,12 +247,12 @@ def level_data[T](cls: type[T]) -> T:
|
|
|
212
247
|
|
|
213
248
|
@dataclass_transform()
|
|
214
249
|
def _level_score[T](cls: type[T]) -> T:
|
|
215
|
-
return create_global(cls, {Mode.
|
|
250
|
+
return create_global(cls, {Mode.PLAY: PlayBlock.LevelScore, Mode.WATCH: WatchBlock.LevelScore}, 0)
|
|
216
251
|
|
|
217
252
|
|
|
218
253
|
@dataclass_transform()
|
|
219
254
|
def _level_life[T](cls: type[T]) -> T:
|
|
220
|
-
return create_global(cls, {Mode.
|
|
255
|
+
return create_global(cls, {Mode.PLAY: PlayBlock.LevelLife, Mode.WATCH: WatchBlock.LevelLife}, 0)
|
|
221
256
|
|
|
222
257
|
|
|
223
258
|
# engine_rom is handled by the compiler
|