sonolus.py 0.3.2__tar.gz → 0.3.4__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.3.2 → sonolus_py-0.3.4}/PKG-INFO +1 -1
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/doc_stubs/math.pyi +33 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/concepts/builtins.md +8 -1
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/concepts/cli.md +1 -1
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/concepts/constructs.md +3 -1
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/concepts/types.md +10 -4
- sonolus_py-0.3.4/docs/index.md +27 -0
- sonolus_py-0.3.4/docs/overview.md +174 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/mkdocs.yml +1 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/pyproject.toml +1 -1
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/finalize.py +16 -4
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/visitor.py +25 -9
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/archetype.py +40 -22
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/array.py +8 -5
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/array_like.py +44 -19
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/containers.py +24 -4
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/debug.py +4 -4
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/engine.py +2 -2
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/instruction.py +1 -1
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/builtin_impls.py +10 -5
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/context.py +5 -1
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/math_impls.py +17 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/native.py +3 -3
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/range.py +30 -7
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/tuple_impl.py +3 -1
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/value.py +1 -1
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/interval.py +61 -3
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/num.py +13 -13
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/pointer.py +1 -1
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/quad.py +39 -3
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/record.py +7 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/runtime.py +28 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/stream.py +25 -16
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/transform.py +4 -4
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/vec.py +19 -7
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_array.py +56 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_array_map.py +96 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_array_set.py +17 -0
- sonolus_py-0.3.4/tests/script/test_interval.py +572 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_operator.py +104 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_range.py +73 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_tuple.py +70 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_var_array.py +98 -7
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_vec.py +20 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/uv.lock +1 -1
- sonolus_py-0.3.2/docs/index.md +0 -22
- sonolus_py-0.3.2/tests/script/test_interval.py +0 -223
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/.github/workflows/publish.yaml +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/.gitignore +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/.python-version +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/.run/Python tests in tests.run.xml +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/LICENSE +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/README.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/doc_stubs/__init__.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/doc_stubs/builtins.pyi +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/doc_stubs/num.pyi +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/doc_stubs/random.pyi +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/CNAME +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/concepts/index.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/concepts/project.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/concepts/resources.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/builtins.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/index.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/math.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/random.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.archetype.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.array.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.array_like.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.bucket.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.containers.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.debug.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.easing.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.effect.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.engine.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.globals.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.instruction.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.interval.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.iterator.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.level.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.metadata.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.num.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.options.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.particle.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.printing.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.project.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.quad.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.record.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.runtime.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.sprite.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.stream.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.text.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.timing.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.transform.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.ui.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.values.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/docs/reference/sonolus.script.vec.md +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/scripts/generate.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/scripts/runtimes/Engine/Tutorial/Blocks.json +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/scripts/runtimes/Functions.json +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/scripts/runtimes/Level/Play/Blocks.json +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/scripts/runtimes/Level/Preview/Blocks.json +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/scripts/runtimes/Level/Watch/Blocks.json +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/__init__.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/__init__.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/blocks.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/excepthook.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/interpret.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/ir.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/mode.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/node.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/ops.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/optimize/__init__.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/optimize/allocate.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/optimize/constant_evaluation.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/optimize/copy_coalesce.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/optimize/dead_code.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/optimize/dominance.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/optimize/flow.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/optimize/inlining.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/optimize/liveness.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/optimize/optimize.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/optimize/passes.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/optimize/simplify.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/optimize/ssa.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/place.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/backend/utils.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/build/__init__.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/build/cli.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/build/collection.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/build/compile.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/build/engine.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/build/level.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/build/node.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/build/project.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/py.typed +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/__init__.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/bucket.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/easing.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/effect.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/globals.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/__init__.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/callbacks.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/constant.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/descriptor.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/dict_impl.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/error.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/generic.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/impl.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/introspection.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/random.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/simulation_context.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/internal/transient.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/iterator.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/level.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/metadata.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/options.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/particle.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/printing.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/project.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/sprite.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/text.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/timing.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/ui.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/sonolus/script/values.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/__init__.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/__init__.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/conftest.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_assert.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_dict.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_flow.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_functions.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_helpers.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_match.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_num.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_quad.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_random.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_record.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_transform.py +0 -0
- {sonolus_py-0.3.2 → sonolus_py-0.3.4}/tests/script/test_values.py +0 -0
|
@@ -154,3 +154,36 @@ def log(x: float, base: float = ...) -> float:
|
|
|
154
154
|
The logarithm of x to the specified base.
|
|
155
155
|
"""
|
|
156
156
|
...
|
|
157
|
+
|
|
158
|
+
def sqrt(x: float) -> float:
|
|
159
|
+
"""Compute the square root of x.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
x: A non-negative numeric value.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
The square root of x.
|
|
166
|
+
"""
|
|
167
|
+
...
|
|
168
|
+
|
|
169
|
+
def degrees(x: float) -> float:
|
|
170
|
+
"""Convert radians to degrees.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
x: An angle in radians.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
The angle in degrees.
|
|
177
|
+
"""
|
|
178
|
+
...
|
|
179
|
+
|
|
180
|
+
def radians(x: float) -> float:
|
|
181
|
+
"""Convert degrees to radians.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
x: An angle in degrees.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
The angle in radians.
|
|
188
|
+
"""
|
|
189
|
+
...
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Sonolus.py comes with support for a number of built-in functions.
|
|
3
3
|
|
|
4
4
|
- `abs(x)`
|
|
5
|
-
- `bool(object)`
|
|
5
|
+
- `bool(object)`
|
|
6
6
|
- `callable(object)`
|
|
7
7
|
- `enumerate(iterable, start=0)`
|
|
8
8
|
- `filter(function, iterable)`
|
|
@@ -38,6 +38,13 @@ Sonolus.py also comes with support for some standard library modules.
|
|
|
38
38
|
- `ceil(x)`
|
|
39
39
|
- `trunc(x)`
|
|
40
40
|
- `log(x[, base])`
|
|
41
|
+
- `sqrt(x)`
|
|
42
|
+
- `degrees(x)`
|
|
43
|
+
- `radians(x)`
|
|
44
|
+
- `pi`
|
|
45
|
+
- `e`
|
|
46
|
+
- `tau`
|
|
47
|
+
- `inf`
|
|
41
48
|
|
|
42
49
|
### random
|
|
43
50
|
- `randrange(stop)`, `random.randrange(start, stop[, step])`
|
|
@@ -25,4 +25,4 @@ sonolus-py schema
|
|
|
25
25
|
## Programmatic usage
|
|
26
26
|
The same functionality can be accessed programmatically as methods of a project.
|
|
27
27
|
|
|
28
|
-
See [Project]
|
|
28
|
+
See [Project](../reference/sonolus.script.project.md) for more information.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Constructs
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Sonolus.py functions as a compiler from Python to Sonolus nodes. While most standard Python constructs are supported,
|
|
4
|
+
there are some limitations compared to standard Python. The following sections outline what Sonolus.py supports and
|
|
5
|
+
how it differs from standard Python.
|
|
4
6
|
|
|
5
7
|
## Key Differences
|
|
6
8
|
|
|
@@ -11,10 +11,14 @@ the constants `None`, `Ellipsis`, and `NotImplemented`.
|
|
|
11
11
|
`Num` is the numeric and boolean type in Sonolus.py. It is interchangeable with `int`, `float`, and `bool`.
|
|
12
12
|
Sonolus.py will treat any of these types as `Num`, but it's recommended to use what's appropriate for clarity.
|
|
13
13
|
|
|
14
|
+
Typically, `Num` isn't directly used in code since `int`, `float`, and `bool` are more specific and easier to read.
|
|
15
|
+
The main exception is for instance checking (`isinstance` or `match` patterns), where `Num` is the only supported
|
|
16
|
+
way to check for numeric or boolean values.
|
|
17
|
+
|
|
14
18
|
The Sonolus app uses 32-bit floating-point numbers for all numeric values, so precision may be lower compared to Python
|
|
15
|
-
when running
|
|
19
|
+
when running within Sonolus.
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
NaN and values outside the range of 32-bit floating-point numbers are not supported.
|
|
18
22
|
|
|
19
23
|
You can import `Num` from `sonolus.script.num`:
|
|
20
24
|
|
|
@@ -42,8 +46,10 @@ Nums support most of the standard Python operations:
|
|
|
42
46
|
Floating point precision may be lower when running on Sonolus compared to Python.
|
|
43
47
|
Care should be taken when performing precision-sensitive operations.
|
|
44
48
|
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
As in regular Python, `0` is considered `False`, while any non-zero value is considered `True`.
|
|
50
|
+
|
|
51
|
+
Objects with an explicit `__bool__` method may also be used in `if`, `while`, `case ... if` expressions as well as with
|
|
52
|
+
the `not` operator. However, the operands of the `and` and `or` operators must be of type `Num`.
|
|
47
53
|
|
|
48
54
|
- Logical operators: `and`, `or`, `not`
|
|
49
55
|
- Ternary expressions: `... if <condition> else ...`
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Sonolus.py
|
|
2
|
+
Sonolus.py is a Python library for creating Sonolus engines.
|
|
3
|
+
|
|
4
|
+
## Installation
|
|
5
|
+
Sonolus.py is available on PyPI and can be installed using a package manager like pip.
|
|
6
|
+
|
|
7
|
+
=== "pip"
|
|
8
|
+
```bash
|
|
9
|
+
pip install sonolus.py
|
|
10
|
+
```
|
|
11
|
+
=== "uv"
|
|
12
|
+
```bash
|
|
13
|
+
uv add sonolus.py
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Getting Started
|
|
17
|
+
Example Projects:
|
|
18
|
+
|
|
19
|
+
- [pydori](https://github.com/qwewqa/pydori): A Bandori-like engine designed to be an example project for Sonolus.py.
|
|
20
|
+
|
|
21
|
+
New Project Template: [sonolus.py-template-project](https://github.com/qwewqa/sonolus.py-template-project)
|
|
22
|
+
|
|
23
|
+
## Documentation
|
|
24
|
+
|
|
25
|
+
- [Overview](overview.md): High-level overview of Sonolus.py.
|
|
26
|
+
- [Concepts](concepts/index.md): Detailed information on concepts and usage.
|
|
27
|
+
- [Reference](reference/index.md): Detailed information on included classes and functions.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# Overview
|
|
2
|
+
|
|
3
|
+
Sonolus.py is a Python library for creating Sonolus engines. This page provides an overview of the key functionality
|
|
4
|
+
available in the library. For more detailed information, see the [Concepts](concepts/index.md) and
|
|
5
|
+
[Reference](reference/index.md) sections.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
Sonolus.py functions by compiling Python code into Sonolus nodes. As such, it supports a subset of Python including
|
|
9
|
+
most syntax and a portion of the standard library. Additionally, Sonolus.py provides its own library of types and
|
|
10
|
+
functions that are specifically designed for use in Sonolus engines.
|
|
11
|
+
|
|
12
|
+
### Language
|
|
13
|
+
|
|
14
|
+
#### Syntax
|
|
15
|
+
Most Python syntax is supported, but there are a few limitations:
|
|
16
|
+
|
|
17
|
+
- Destructuring assignment with the `*` operator is unsupported.
|
|
18
|
+
- Sequence (list and array) `match` patterns with the `*` operator are unsupported.
|
|
19
|
+
- Mapping (dict) `match` patterns are unsupported.
|
|
20
|
+
- Within functions, `import` statements are unsupported.
|
|
21
|
+
- The `global` and `nonlocal` keywords are unsupported.
|
|
22
|
+
- Exception related statements (`try`, `except`, `finally`, `raise`) are unsupported.
|
|
23
|
+
|
|
24
|
+
#### Compile Time Evaluation
|
|
25
|
+
Sonolus.py will evaluate some expressions at compile time such as basic arithmetic operations on constants,
|
|
26
|
+
boolean logical operations (`and`, `or`, `not`) on constants, and type checks (`isinstance`, `issubclass`).
|
|
27
|
+
|
|
28
|
+
In control flow constructs like `if` and `match`, Sonolus.py may determine some branches to be unreachable at compile
|
|
29
|
+
and eliminate them without evaluating them. This allows code like the following to compile successfully:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
a = 1
|
|
33
|
+
if isinstance(a, Vec2):
|
|
34
|
+
# This branch is eliminated at compile time.
|
|
35
|
+
# If it were not, compilation would fail because `a` has no attribute `x`.
|
|
36
|
+
debug_log(a.x)
|
|
37
|
+
else:
|
|
38
|
+
debug_log(a)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
#### Variables
|
|
42
|
+
Numeric (`int`, `float`, `bool`) variables are fully supported and can be freely assigned and modified.
|
|
43
|
+
|
|
44
|
+
All other variables have the restriction that if the compiler finds multiple possible values for a variable, it may
|
|
45
|
+
not be accessed. For example, the following code will not compile:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
if random() < 0.5:
|
|
49
|
+
a = Vec2(1, 2)
|
|
50
|
+
else:
|
|
51
|
+
a = Vec2(3, 4)
|
|
52
|
+
# This will not compile because `a` could have been defined in either branch.
|
|
53
|
+
debug_log(a.x)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### Function Returns
|
|
57
|
+
Similar to variables, functions returning `int`, `float`, or `bool` can have any number of return statements. Functions
|
|
58
|
+
returning `None` may also have any number of `return` or `return None` statements.
|
|
59
|
+
|
|
60
|
+
Functions returning any other type must have exactly one `return` statement, and it must be the only exit point of the
|
|
61
|
+
function. It is ok, however, for a function to have other `return` statements that are eliminated at compile time.
|
|
62
|
+
For example, the following code will compile successfully:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
def fn(a: int | Vec2):
|
|
66
|
+
if isinstance(a, Vec2):
|
|
67
|
+
return a.x + a.y
|
|
68
|
+
else:
|
|
69
|
+
return a * 2
|
|
70
|
+
|
|
71
|
+
fn(123)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Types
|
|
75
|
+
|
|
76
|
+
#### Numbers
|
|
77
|
+
Sonolus.py supports `int`, `float`, and `bool` types and most of the standard operations such as mathematical operations
|
|
78
|
+
(`+`, `-`, `*`, `/`, `//`, `%`), comparisons (`<`, `<=`, `>`, `>=`, `==`, `!=`), and boolean operations
|
|
79
|
+
(`and`, `or`, `not`).
|
|
80
|
+
|
|
81
|
+
#### Record
|
|
82
|
+
[`Record`](reference/sonolus.script.record.md) is the main way to define custom types in Sonolus.py. It functions similarly to a data class and
|
|
83
|
+
provides a way to define a type with named fields:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
class MyRecord(Record):
|
|
87
|
+
a: int
|
|
88
|
+
b: float
|
|
89
|
+
|
|
90
|
+
my_record = MyRecord(1, b=2.5)
|
|
91
|
+
my_zero_record = +MyRecord # Short for MyRecord(0, 0.0)
|
|
92
|
+
my_record_copy = +my_record # Create a copy of my_record with the same values
|
|
93
|
+
my_record.a = 123 # Modify a field of the record
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Records may also be generic:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
class MyGenericRecord[T](Record):
|
|
100
|
+
value: T
|
|
101
|
+
|
|
102
|
+
my_generic_record = MyGenericRecord[int](42)
|
|
103
|
+
my_generic_record_2 = MyGenericRecord(MyRecord(1, 3.5)) # Type arguments are inferred
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### Array
|
|
107
|
+
[`Array`](reference/sonolus.script.array.md) is a type that represents a fixed-size array of elements of a specific type.
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
my_array = Array[int, 3](1, 2, 3)
|
|
111
|
+
my_array_2 = Array(4, 5, 6) # Type arguments are inferred
|
|
112
|
+
my_zero_array = +Array[int, 3] # Short for Array[int, 3](0, 0, 0)
|
|
113
|
+
my_array_copy = +my_array # Create a copy of my_array with the same values
|
|
114
|
+
my_array[2] = 10 # Modify an element of the array
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### Other Types
|
|
118
|
+
Sonolus.py has limited support for other types of values such as strings, tuples, and functions. These have restrictions
|
|
119
|
+
such as not being valid as Record field types or Array element types.
|
|
120
|
+
|
|
121
|
+
### Modules
|
|
122
|
+
Sonolus.py provides a number of built-in modules that can be used in Sonolus engines. These include:
|
|
123
|
+
|
|
124
|
+
- Project
|
|
125
|
+
- [Project](reference/sonolus.script.project.md): Configuration for a Sonolus.py project.
|
|
126
|
+
- [Engine](reference/sonolus.script.engine.md): Configuration for a Sonolus.py engine.
|
|
127
|
+
- [Level](reference/sonolus.script.level.md): Configuration for a Sonolus.py level.
|
|
128
|
+
- [Archetype](reference/sonolus.script.archetype.md): Engine archetypes and their configuration.
|
|
129
|
+
- Core Types
|
|
130
|
+
- [Array](reference/sonolus.script.array.md): Fixed-size arrays.
|
|
131
|
+
- [Num](reference/sonolus.script.num.md): Numeric values (int, float, bool).
|
|
132
|
+
- [Record](reference/sonolus.script.record.md): User-defined types with named fields.
|
|
133
|
+
- Engine Resources
|
|
134
|
+
- [Bucket](reference/sonolus.script.bucket.md): Judgment buckets.
|
|
135
|
+
- [Effect](reference/sonolus.script.effect.md): Sound effects.
|
|
136
|
+
- [Instruction](reference/sonolus.script.instruction.md): Tutorial instructions.
|
|
137
|
+
- [Options](reference/sonolus.script.options.md): Engine options.
|
|
138
|
+
- [Particle](reference/sonolus.script.particle.md): Particle effects.
|
|
139
|
+
- [Sprite](reference/sonolus.script.sprite.md): Sprites and skins.
|
|
140
|
+
- [UI](reference/sonolus.script.ui.md): Engine ui configuration.
|
|
141
|
+
- Sonolus Runtime
|
|
142
|
+
- [Globals](reference/sonolus.script.globals.md): Level data and level memory definition.
|
|
143
|
+
- [Runtime](reference/sonolus.script.runtime.md): Runtime functions like time and ui configuration.
|
|
144
|
+
- [Stream](reference/sonolus.script.stream.md): Data streams recorded in play mode and used in watch mode.
|
|
145
|
+
- [Text](reference/sonolus.script.text.md): Standard Sonolus text constants.
|
|
146
|
+
- [Timing](reference/sonolus.script.timing.md): Beat and timescale related functions.
|
|
147
|
+
- Python Builtins
|
|
148
|
+
- [builtins](reference/builtins.md): Supported Python builtins.
|
|
149
|
+
- [math](reference/math.md): Supported math functions.
|
|
150
|
+
- [random](reference/random.md): Supported random functions.
|
|
151
|
+
- Utilities
|
|
152
|
+
- [ArrayLike](reference/sonolus.script.array_like.md): Mixin for array functionality.
|
|
153
|
+
- [Containers](reference/sonolus.script.containers.md): Additional container types like `VarArray` and `ArrayMap`.
|
|
154
|
+
- [Debug](reference/sonolus.script.debug.md): Debugging utilities.
|
|
155
|
+
- [Easing](reference/sonolus.script.easing.md): Easing functions for animations.
|
|
156
|
+
- [Interval](reference/sonolus.script.interval.md): Mathematical intervals.
|
|
157
|
+
- [Iterator](reference/sonolus.script.iterator.md): Iterators over collections.
|
|
158
|
+
- [Printing](reference/sonolus.script.printing.md): Preview mode number printing.
|
|
159
|
+
- [Quad](reference/sonolus.script.quad.md): Quadrilaterals.
|
|
160
|
+
- [Transform](reference/sonolus.script.transform.md): Transformations like translation, rotation, and scaling.
|
|
161
|
+
- [Values](reference/sonolus.script.values.md): Generic utilities for working with values.
|
|
162
|
+
- [Vec](reference/sonolus.script.vec.md): The Vec2 type and related functions.
|
|
163
|
+
|
|
164
|
+
For more details, see the [Reference](reference/index.md) section.
|
|
165
|
+
|
|
166
|
+
## Getting Started
|
|
167
|
+
|
|
168
|
+
Before making a new Sonolus.py engine, it may be helpful to look at some existing projects to see how they use
|
|
169
|
+
the library:
|
|
170
|
+
|
|
171
|
+
- [pydori](https://github.com/qwewqa/pydori): A Bandori-like engine designed to be an example project for Sonolus.py.
|
|
172
|
+
|
|
173
|
+
If you're starting a new project, you'll probably want to use the
|
|
174
|
+
[new project template](https://github.com/qwewqa/sonolus.py-template-project).
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from math import isfinite, isinf, isnan
|
|
2
|
+
|
|
1
3
|
from sonolus.backend.ir import IRConst, IRGet, IRInstr, IRPureInstr, IRSet
|
|
2
4
|
from sonolus.backend.node import ConstantNode, EngineNode, FunctionNode
|
|
3
5
|
from sonolus.backend.ops import Op
|
|
@@ -54,10 +56,20 @@ def cfg_to_engine_node(entry: BasicBlock):
|
|
|
54
56
|
|
|
55
57
|
def ir_to_engine_node(stmt) -> EngineNode:
|
|
56
58
|
match stmt:
|
|
57
|
-
case int() | float():
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
case int(value) | float(value) | IRConst(value=int(value) | float(value)):
|
|
60
|
+
value = float(value)
|
|
61
|
+
if value.is_integer():
|
|
62
|
+
return ConstantNode(value=int(value))
|
|
63
|
+
elif isfinite(value):
|
|
64
|
+
return ConstantNode(value=value)
|
|
65
|
+
elif isinf(value):
|
|
66
|
+
# Read values from ROM
|
|
67
|
+
return FunctionNode(Op.Get, args=[ConstantNode(value=3000), ConstantNode(value=1 if value > 0 else 2)])
|
|
68
|
+
elif isnan(value):
|
|
69
|
+
# Read value from ROM
|
|
70
|
+
return FunctionNode(Op.Get, args=[ConstantNode(value=3000), ConstantNode(value=0)])
|
|
71
|
+
else:
|
|
72
|
+
raise ValueError(f"Invalid constant value: {value}")
|
|
61
73
|
case IRPureInstr(op=op, args=args) | IRInstr(op=op, args=args):
|
|
62
74
|
return FunctionNode(func=op, args=[ir_to_engine_node(arg) for arg in args])
|
|
63
75
|
case IRGet(place=place):
|
|
@@ -359,7 +359,7 @@ class Visitor(ast.NodeVisitor):
|
|
|
359
359
|
self.loop_head_ctxs.append(header_ctx)
|
|
360
360
|
self.break_ctxs.append([])
|
|
361
361
|
set_ctx(header_ctx)
|
|
362
|
-
has_next = self.
|
|
362
|
+
has_next = self.convert_to_boolean_num(node, self.handle_call(node, iterator.has_next))
|
|
363
363
|
if has_next._is_py_() and not has_next._as_py_():
|
|
364
364
|
# The loop will never run, continue after evaluating the condition
|
|
365
365
|
self.loop_head_ctxs.pop()
|
|
@@ -400,7 +400,7 @@ class Visitor(ast.NodeVisitor):
|
|
|
400
400
|
self.loop_head_ctxs.append(header_ctx)
|
|
401
401
|
self.break_ctxs.append([])
|
|
402
402
|
set_ctx(header_ctx)
|
|
403
|
-
test = self.
|
|
403
|
+
test = self.convert_to_boolean_num(node.test, self.visit(node.test))
|
|
404
404
|
if test._is_py_():
|
|
405
405
|
if test._as_py_():
|
|
406
406
|
# The loop will run until a break / return
|
|
@@ -454,7 +454,7 @@ class Visitor(ast.NodeVisitor):
|
|
|
454
454
|
set_ctx(after_ctx)
|
|
455
455
|
|
|
456
456
|
def visit_If(self, node):
|
|
457
|
-
test = self.
|
|
457
|
+
test = self.convert_to_boolean_num(node.test, self.visit(node.test))
|
|
458
458
|
|
|
459
459
|
if test._is_py_():
|
|
460
460
|
if test._as_py_():
|
|
@@ -507,7 +507,9 @@ class Visitor(ast.NodeVisitor):
|
|
|
507
507
|
set_ctx(false_ctx)
|
|
508
508
|
continue
|
|
509
509
|
set_ctx(true_ctx)
|
|
510
|
-
guard =
|
|
510
|
+
guard = (
|
|
511
|
+
self.convert_to_boolean_num(case.guard, self.visit(case.guard)) if case.guard else validate_value(True)
|
|
512
|
+
)
|
|
511
513
|
if guard._is_py_():
|
|
512
514
|
if guard._as_py_():
|
|
513
515
|
for stmt in case.body:
|
|
@@ -544,7 +546,7 @@ class Visitor(ast.NodeVisitor):
|
|
|
544
546
|
match pattern:
|
|
545
547
|
case ast.MatchValue(value=value):
|
|
546
548
|
value = self.visit(value)
|
|
547
|
-
test = self.
|
|
549
|
+
test = self.convert_to_boolean_num(pattern, validate_value(subject == value))
|
|
548
550
|
if test._is_py_():
|
|
549
551
|
if test._as_py_():
|
|
550
552
|
return ctx(), ctx().into_dead()
|
|
@@ -574,7 +576,7 @@ class Visitor(ast.NodeVisitor):
|
|
|
574
576
|
target_len = len(patterns)
|
|
575
577
|
if not (isinstance(subject, Sequence | TupleImpl)):
|
|
576
578
|
return ctx().into_dead(), ctx()
|
|
577
|
-
length_test = self.
|
|
579
|
+
length_test = self.convert_to_boolean_num(pattern, validate_value(_len(subject) == target_len))
|
|
578
580
|
ctx_init = ctx()
|
|
579
581
|
if not length_test._is_py_():
|
|
580
582
|
ctx_init.test = length_test.ir()
|
|
@@ -739,7 +741,7 @@ class Visitor(ast.NodeVisitor):
|
|
|
739
741
|
def visit_UnaryOp(self, node):
|
|
740
742
|
operand = self.visit(node.operand)
|
|
741
743
|
if isinstance(node.op, ast.Not):
|
|
742
|
-
return self.
|
|
744
|
+
return self.convert_to_boolean_num(node, operand).not_()
|
|
743
745
|
op = unary_ops[type(node.op)]
|
|
744
746
|
if operand._is_py_():
|
|
745
747
|
operand_py = operand._as_py_()
|
|
@@ -768,7 +770,7 @@ class Visitor(ast.NodeVisitor):
|
|
|
768
770
|
return validate_value(fn)
|
|
769
771
|
|
|
770
772
|
def visit_IfExp(self, node):
|
|
771
|
-
test = self.
|
|
773
|
+
test = self.convert_to_boolean_num(node.test, self.visit(node.test))
|
|
772
774
|
|
|
773
775
|
if test._is_py_():
|
|
774
776
|
if test._as_py_():
|
|
@@ -1076,7 +1078,7 @@ class Visitor(ast.NodeVisitor):
|
|
|
1076
1078
|
|
|
1077
1079
|
def handle_call[**P, R](
|
|
1078
1080
|
self, node: ast.stmt | ast.expr, fn: Callable[P, R], /, *args: P.args, **kwargs: P.kwargs
|
|
1079
|
-
) -> R:
|
|
1081
|
+
) -> R | Value:
|
|
1080
1082
|
"""Handles a call to the given callable."""
|
|
1081
1083
|
self.active_ctx = ctx()
|
|
1082
1084
|
if (
|
|
@@ -1127,6 +1129,20 @@ class Visitor(ast.NodeVisitor):
|
|
|
1127
1129
|
raise TypeError(f"Invalid type where a bool (Num) was expected: {type(value).__name__}")
|
|
1128
1130
|
return value
|
|
1129
1131
|
|
|
1132
|
+
def convert_to_boolean_num(self, node, value: Value) -> Num:
|
|
1133
|
+
if _is_num(value):
|
|
1134
|
+
return value
|
|
1135
|
+
if hasattr(type(value), "__bool__"):
|
|
1136
|
+
return self.ensure_boolean_num(self.handle_call(node, type(value).__bool__, validate_value(value)))
|
|
1137
|
+
if hasattr(type(value), "__len__"):
|
|
1138
|
+
length = self.handle_call(node, type(value).__len__, validate_value(value))
|
|
1139
|
+
if not _is_num(length):
|
|
1140
|
+
raise TypeError(f"Invalid type for __len__: {type(length).__name__}")
|
|
1141
|
+
if length._is_py_():
|
|
1142
|
+
return Num._accept_(length._as_py_() > 0)
|
|
1143
|
+
return length > Num._accept_(0)
|
|
1144
|
+
raise TypeError(f"Converting {type(value).__name__} to bool is not supported")
|
|
1145
|
+
|
|
1130
1146
|
def arguments_to_signature(self, arguments: ast.arguments) -> inspect.Signature:
|
|
1131
1147
|
parameters: list[inspect.Parameter] = []
|
|
1132
1148
|
pos_only_count = len(arguments.posonlyargs)
|
|
@@ -312,7 +312,7 @@ class StandardImport:
|
|
|
312
312
|
def callback[T: Callable](*, order: int = 0) -> Callable[[T], T]:
|
|
313
313
|
"""Annotate a callback with its order.
|
|
314
314
|
|
|
315
|
-
Callbacks are
|
|
315
|
+
Callbacks are executed from lowest to highest order. By default, callbacks have an order of 0.
|
|
316
316
|
|
|
317
317
|
Usage:
|
|
318
318
|
```python
|
|
@@ -338,9 +338,9 @@ class _ArchetypeSelfData:
|
|
|
338
338
|
|
|
339
339
|
|
|
340
340
|
class _ArchetypeReferenceData:
|
|
341
|
-
index:
|
|
341
|
+
index: int
|
|
342
342
|
|
|
343
|
-
def __init__(self, index:
|
|
343
|
+
def __init__(self, index: int):
|
|
344
344
|
self.index = index
|
|
345
345
|
|
|
346
346
|
|
|
@@ -416,21 +416,21 @@ class _BaseArchetype:
|
|
|
416
416
|
|
|
417
417
|
@classmethod
|
|
418
418
|
@meta_fn
|
|
419
|
-
def at(cls, index:
|
|
419
|
+
def at(cls, index: int) -> Self:
|
|
420
420
|
result = cls._new()
|
|
421
421
|
result._data_ = _ArchetypeReferenceData(index=Num._accept_(index))
|
|
422
422
|
return result
|
|
423
423
|
|
|
424
424
|
@classmethod
|
|
425
425
|
@meta_fn
|
|
426
|
-
def is_at(cls, index:
|
|
426
|
+
def is_at(cls, index: int) -> bool:
|
|
427
427
|
if not ctx():
|
|
428
428
|
raise RuntimeError("is_at is only available during compilation")
|
|
429
429
|
return entity_info_at(index).archetype_id == cls.id()
|
|
430
430
|
|
|
431
431
|
@classmethod
|
|
432
432
|
@meta_fn
|
|
433
|
-
def id(cls):
|
|
433
|
+
def id(cls) -> int:
|
|
434
434
|
if not ctx():
|
|
435
435
|
raise RuntimeError("Archetype id is only available during compilation")
|
|
436
436
|
result = ctx().global_state.archetypes.get(cls)
|
|
@@ -549,11 +549,19 @@ class _BaseArchetype:
|
|
|
549
549
|
metadata = _annotation_defaults.get(metadata, metadata)
|
|
550
550
|
if isinstance(metadata, _ArchetypeFieldInfo):
|
|
551
551
|
if field_info is not None:
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
552
|
+
if field_info.storage == metadata.storage and field_info.name is None:
|
|
553
|
+
field_info = metadata
|
|
554
|
+
elif field_info.storage == metadata.storage and (
|
|
555
|
+
metadata.name is None or field_info.name == metadata.name
|
|
556
|
+
):
|
|
557
|
+
pass
|
|
558
|
+
else:
|
|
559
|
+
raise TypeError(
|
|
560
|
+
f"Unexpected multiple field annotations for '{name}', "
|
|
561
|
+
f"expected exactly one of imported, exported, entity_memory, or shared_memory"
|
|
562
|
+
)
|
|
563
|
+
else:
|
|
564
|
+
field_info = metadata
|
|
557
565
|
if field_info is None:
|
|
558
566
|
raise TypeError(
|
|
559
567
|
f"Missing field annotation for '{name}', "
|
|
@@ -880,7 +888,7 @@ class WatchArchetype(_BaseArchetype):
|
|
|
880
888
|
case _ArchetypeSelfData():
|
|
881
889
|
return _deref(ctx().blocks.EntityInfo, 0, WatchEntityInfo)
|
|
882
890
|
case _ArchetypeReferenceData(index=index):
|
|
883
|
-
return _deref(ctx().blocks.EntityInfoArray, index * WatchEntityInfo._size_(),
|
|
891
|
+
return _deref(ctx().blocks.EntityInfoArray, index * WatchEntityInfo._size_(), WatchEntityInfo)
|
|
884
892
|
case _:
|
|
885
893
|
raise RuntimeError("Info is only accessible from the entity itself")
|
|
886
894
|
|
|
@@ -984,7 +992,7 @@ class PreviewArchetype(_BaseArchetype):
|
|
|
984
992
|
|
|
985
993
|
|
|
986
994
|
@meta_fn
|
|
987
|
-
def entity_info_at(index:
|
|
995
|
+
def entity_info_at(index: int) -> PlayEntityInfo | WatchEntityInfo | PreviewEntityInfo:
|
|
988
996
|
"""Retrieve entity info of the entity at the given index.
|
|
989
997
|
|
|
990
998
|
Available in play, watch, and preview mode.
|
|
@@ -1039,24 +1047,24 @@ class PreviewEntityInfo(Record):
|
|
|
1039
1047
|
class ArchetypeLife(Record):
|
|
1040
1048
|
"""How an entity contributes to life."""
|
|
1041
1049
|
|
|
1042
|
-
perfect_increment:
|
|
1050
|
+
perfect_increment: int
|
|
1043
1051
|
"""Life increment for a perfect judgment."""
|
|
1044
1052
|
|
|
1045
|
-
great_increment:
|
|
1053
|
+
great_increment: int
|
|
1046
1054
|
"""Life increment for a great judgment."""
|
|
1047
1055
|
|
|
1048
|
-
good_increment:
|
|
1056
|
+
good_increment: int
|
|
1049
1057
|
"""Life increment for a good judgment."""
|
|
1050
1058
|
|
|
1051
|
-
miss_increment:
|
|
1059
|
+
miss_increment: int
|
|
1052
1060
|
"""Life increment for a miss judgment."""
|
|
1053
1061
|
|
|
1054
1062
|
def update(
|
|
1055
1063
|
self,
|
|
1056
|
-
perfect_increment:
|
|
1057
|
-
great_increment:
|
|
1058
|
-
good_increment:
|
|
1059
|
-
miss_increment:
|
|
1064
|
+
perfect_increment: int | None = None,
|
|
1065
|
+
great_increment: int | None = None,
|
|
1066
|
+
good_increment: int | None = None,
|
|
1067
|
+
miss_increment: int | None = None,
|
|
1060
1068
|
):
|
|
1061
1069
|
"""Update the life increments."""
|
|
1062
1070
|
if perfect_increment is not None:
|
|
@@ -1106,10 +1114,20 @@ class EntityRef[A: _BaseArchetype](Record):
|
|
|
1106
1114
|
"""Return a new reference with the given archetype type."""
|
|
1107
1115
|
return EntityRef[archetype](index=self.index)
|
|
1108
1116
|
|
|
1117
|
+
@meta_fn
|
|
1109
1118
|
def get(self) -> A:
|
|
1110
1119
|
"""Get the entity."""
|
|
1120
|
+
if ref := getattr(self, "_ref_", None):
|
|
1121
|
+
return ref
|
|
1111
1122
|
return self.archetype().at(self.index)
|
|
1112
1123
|
|
|
1124
|
+
@meta_fn
|
|
1125
|
+
def get_as(self, archetype: type[_BaseArchetype]) -> _BaseArchetype:
|
|
1126
|
+
"""Get the entity as the given archetype type."""
|
|
1127
|
+
if getattr(archetype, "_ref_", None):
|
|
1128
|
+
raise TypeError("Using get_as in level data is not supported.")
|
|
1129
|
+
return self.with_archetype(archetype).get()
|
|
1130
|
+
|
|
1113
1131
|
def archetype_matches(self) -> bool:
|
|
1114
1132
|
"""Check if entity at the index is precisely of the archetype."""
|
|
1115
1133
|
return self.index >= 0 and self.archetype().is_at(self.index)
|
|
@@ -1117,7 +1135,7 @@ class EntityRef[A: _BaseArchetype](Record):
|
|
|
1117
1135
|
def _to_list_(self, level_refs: dict[Any, str] | None = None) -> list[DataValue | str]:
|
|
1118
1136
|
ref = getattr(self, "_ref_", None)
|
|
1119
1137
|
if ref is None:
|
|
1120
|
-
return
|
|
1138
|
+
return Num._accept_(self.index)._to_list_()
|
|
1121
1139
|
else:
|
|
1122
1140
|
if ref not in level_refs:
|
|
1123
1141
|
raise KeyError("Reference to entity not in level data")
|