sonolus.py 0.2.1__tar.gz → 0.3.0__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.

Files changed (175) hide show
  1. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/PKG-INFO +1 -1
  2. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/concepts/builtins.md +2 -1
  3. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/concepts/constructs.md +4 -4
  4. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/concepts/project.md +34 -2
  5. sonolus_py-0.3.0/docs/concepts/resources.md +304 -0
  6. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/concepts/types.md +45 -28
  7. sonolus_py-0.3.0/docs/reference/sonolus.script.stream.md +3 -0
  8. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/mkdocs.yml +2 -0
  9. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/pyproject.toml +1 -1
  10. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/ops.py +6 -0
  11. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/archetype.py +1 -1
  12. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/array.py +56 -27
  13. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/array_like.py +1 -0
  14. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/bucket.py +5 -3
  15. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/constant.py +3 -1
  16. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/generic.py +7 -0
  17. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/impl.py +10 -9
  18. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/value.py +13 -0
  19. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/num.py +5 -3
  20. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/particle.py +1 -0
  21. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/quad.py +3 -3
  22. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/record.py +13 -2
  23. sonolus_py-0.3.0/sonolus/script/stream.py +529 -0
  24. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/transform.py +3 -3
  25. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/values.py +11 -0
  26. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/uv.lock +1 -1
  27. sonolus_py-0.2.1/docs/concepts/resources.md +0 -244
  28. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/.github/workflows/publish.yaml +0 -0
  29. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/.gitignore +0 -0
  30. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/.python-version +0 -0
  31. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/.run/Python tests in tests.run.xml +0 -0
  32. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/LICENSE +0 -0
  33. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/README.md +0 -0
  34. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/doc_stubs/__init__.py +0 -0
  35. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/doc_stubs/builtins.pyi +0 -0
  36. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/doc_stubs/math.pyi +0 -0
  37. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/doc_stubs/num.pyi +0 -0
  38. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/doc_stubs/random.pyi +0 -0
  39. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/CNAME +0 -0
  40. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/concepts/cli.md +0 -0
  41. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/concepts/index.md +0 -0
  42. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/index.md +0 -0
  43. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/builtins.md +0 -0
  44. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/index.md +0 -0
  45. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/math.md +0 -0
  46. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/random.md +0 -0
  47. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.archetype.md +0 -0
  48. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.array.md +0 -0
  49. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.array_like.md +0 -0
  50. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.bucket.md +0 -0
  51. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.containers.md +0 -0
  52. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.debug.md +0 -0
  53. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.easing.md +0 -0
  54. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.effect.md +0 -0
  55. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.engine.md +0 -0
  56. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.globals.md +0 -0
  57. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.instruction.md +0 -0
  58. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.interval.md +0 -0
  59. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.iterator.md +0 -0
  60. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.level.md +0 -0
  61. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.metadata.md +0 -0
  62. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.num.md +0 -0
  63. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.options.md +0 -0
  64. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.particle.md +0 -0
  65. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.printing.md +0 -0
  66. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.project.md +0 -0
  67. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.quad.md +0 -0
  68. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.record.md +0 -0
  69. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.runtime.md +0 -0
  70. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.sprite.md +0 -0
  71. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.text.md +0 -0
  72. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.timing.md +0 -0
  73. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.transform.md +0 -0
  74. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.ui.md +0 -0
  75. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.values.md +0 -0
  76. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/docs/reference/sonolus.script.vec.md +0 -0
  77. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/scripts/generate.py +0 -0
  78. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/scripts/runtimes/Engine/Tutorial/Blocks.json +0 -0
  79. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/scripts/runtimes/Functions.json +0 -0
  80. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/scripts/runtimes/Level/Play/Blocks.json +0 -0
  81. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/scripts/runtimes/Level/Preview/Blocks.json +0 -0
  82. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/scripts/runtimes/Level/Watch/Blocks.json +0 -0
  83. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/__init__.py +0 -0
  84. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/__init__.py +0 -0
  85. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/blocks.py +0 -0
  86. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/excepthook.py +0 -0
  87. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/finalize.py +0 -0
  88. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/interpret.py +0 -0
  89. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/ir.py +0 -0
  90. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/mode.py +0 -0
  91. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/node.py +0 -0
  92. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/optimize/__init__.py +0 -0
  93. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/optimize/allocate.py +0 -0
  94. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/optimize/constant_evaluation.py +0 -0
  95. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/optimize/copy_coalesce.py +0 -0
  96. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/optimize/dead_code.py +0 -0
  97. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/optimize/dominance.py +0 -0
  98. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/optimize/flow.py +0 -0
  99. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/optimize/inlining.py +0 -0
  100. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/optimize/liveness.py +0 -0
  101. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/optimize/optimize.py +0 -0
  102. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/optimize/passes.py +0 -0
  103. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/optimize/simplify.py +0 -0
  104. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/optimize/ssa.py +0 -0
  105. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/place.py +0 -0
  106. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/utils.py +0 -0
  107. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/backend/visitor.py +0 -0
  108. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/build/__init__.py +0 -0
  109. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/build/cli.py +0 -0
  110. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/build/collection.py +0 -0
  111. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/build/compile.py +0 -0
  112. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/build/engine.py +0 -0
  113. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/build/level.py +0 -0
  114. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/build/node.py +0 -0
  115. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/build/project.py +0 -0
  116. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/py.typed +0 -0
  117. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/__init__.py +0 -0
  118. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/containers.py +0 -0
  119. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/debug.py +0 -0
  120. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/easing.py +0 -0
  121. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/effect.py +0 -0
  122. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/engine.py +0 -0
  123. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/globals.py +0 -0
  124. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/instruction.py +0 -0
  125. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/__init__.py +0 -0
  126. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/builtin_impls.py +0 -0
  127. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/callbacks.py +0 -0
  128. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/context.py +0 -0
  129. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/descriptor.py +0 -0
  130. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/dict_impl.py +0 -0
  131. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/error.py +0 -0
  132. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/introspection.py +0 -0
  133. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/math_impls.py +0 -0
  134. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/native.py +0 -0
  135. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/random.py +0 -0
  136. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/range.py +0 -0
  137. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/transient.py +0 -0
  138. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/internal/tuple_impl.py +0 -0
  139. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/interval.py +0 -0
  140. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/iterator.py +0 -0
  141. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/level.py +0 -0
  142. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/metadata.py +0 -0
  143. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/options.py +0 -0
  144. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/pointer.py +0 -0
  145. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/printing.py +0 -0
  146. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/project.py +0 -0
  147. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/runtime.py +0 -0
  148. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/sprite.py +0 -0
  149. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/text.py +0 -0
  150. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/timing.py +0 -0
  151. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/ui.py +0 -0
  152. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/sonolus/script/vec.py +0 -0
  153. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/__init__.py +0 -0
  154. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/__init__.py +0 -0
  155. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/conftest.py +0 -0
  156. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_array.py +0 -0
  157. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_array_map.py +0 -0
  158. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_array_set.py +0 -0
  159. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_assert.py +0 -0
  160. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_dict.py +0 -0
  161. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_flow.py +0 -0
  162. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_functions.py +0 -0
  163. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_helpers.py +0 -0
  164. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_interval.py +0 -0
  165. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_match.py +0 -0
  166. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_num.py +0 -0
  167. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_operator.py +0 -0
  168. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_quad.py +0 -0
  169. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_random.py +0 -0
  170. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_range.py +0 -0
  171. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_record.py +0 -0
  172. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_transform.py +0 -0
  173. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_tuple.py +0 -0
  174. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_var_array.py +0 -0
  175. {sonolus_py-0.2.1 → sonolus_py-0.3.0}/tests/script/test_vec.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sonolus.py
3
- Version: 0.2.1
3
+ Version: 0.3.0
4
4
  Summary: Sonolus engine development in Python
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -11,7 +11,8 @@ Sonolus.py comes with support for a number of built-in functions.
11
11
  - `isinstance(object, classinfo)`
12
12
  - `issubclass(class, classinfo)`
13
13
  - `len(s)`
14
- - `map(function, iterable)`
14
+ - `map(function, iterable)` (note: may differ from standard Python behavior, see
15
+ [`map`](../reference/builtins.md#doc_stubs.builtins.map))
15
16
  - `max(iterable, *, key=None)`, `max(arg1, arg2, *args, key=None)`
16
17
  - `min(iterable, *, key=None)`, `min(arg1, arg2, *args, key=None)`
17
18
  - `range(stop)`, `range(start, stop[, step])`
@@ -30,8 +30,8 @@ The following constructs are supported in Sonolus.py:
30
30
  - Unary: `+`, `-`, `not`, `~`
31
31
  - Binary: `+`, `-`, `*`, `/`, `//`, `%`, `**`, `&`, `|`, `^`, `<<`, `>>`
32
32
  - Comparison: `==`, `!=`, `>`, `<`, `>=`, `<=`, `is`, `is not`, `in`, `not in`
33
- - Logical: `and`, `or` (for `Num` arguments only)
34
- - Ternary: `a if <condition> else b` (for `Num` conditions only)
33
+ - Logical: `and`, `or` (for [`Num`](types.md#num) arguments only)
34
+ - Ternary: `a if <condition> else b` (for [`Num`](types.md#num) conditions only)
35
35
  - Attribute: `a.b`
36
36
  - Indexing: `a[b]`
37
37
  - Call: `f(a, b, c)`
@@ -185,7 +185,7 @@ h = g[0] + g[1] + g[2]
185
185
  (i := 1)
186
186
  ```
187
187
 
188
- The ternary operator is supported for, but the condition must be a `Num`. If the operands are not nums,
188
+ The ternary operator is supported for, but the condition must be a [`Num`](types.md#num). If the operands are not nums,
189
189
  the condition must be a compile-time constant or this will be considered an error:
190
190
 
191
191
  ```python
@@ -448,7 +448,7 @@ Outside of functions returning `None` or a num, most functions should have a sin
448
448
 
449
449
  ### Classes
450
450
 
451
- Classes are supported at the module level. User defined classes should subclass `Record` or have a supported
451
+ Classes are supported at the module level. User defined classes should subclass [`Record`][sonolus.script.record.Record] or have a supported
452
452
  Sonolus.py decorator such as `@level_memory`.
453
453
 
454
454
  Methods may have the `@staticmethod`, `@classmethod`, or `@property` decorators.
@@ -54,7 +54,9 @@ resources/
54
54
  ```
55
55
 
56
56
  ## Modes
57
- Modes are defined using the `PlayMode`, `WatchMode`, `PreviewMode`, and `TutorialMode` classes:
57
+ Modes are defined using the [`PlayMode`][sonolus.script.engine.PlayMode], [`WatchMode`][sonolus.script.engine.WatchMode], [`PreviewMode`][sonolus.script.engine.PreviewMode], and [`TutorialMode`][sonolus.script.engine.TutorialMode] classes.
58
+
59
+ ### Play Mode
58
60
 
59
61
  ```python
60
62
  from sonolus.script.engine import PlayMode
@@ -78,6 +80,14 @@ play_mode = PlayMode(
78
80
 
79
81
  ```
80
82
 
83
+ Play mode archetypes subclass [`PlayArchetype`][sonolus.script.archetype.PlayArchetype] and should implement the [`should_spawn`][sonolus.script.archetype.PlayArchetype.should_spawn] callback. They may also implement
84
+ the [`preprocess`][sonolus.script.archetype.PlayArchetype.preprocess], [`spawn_order`][sonolus.script.archetype.PlayArchetype.spawn_order], [`initialize`][sonolus.script.archetype.PlayArchetype.initialize], [`update_sequential`][sonolus.script.archetype.PlayArchetype.update_sequential], [`update_parallel`][sonolus.script.archetype.PlayArchetype.update_parallel], [`touch`][sonolus.script.archetype.PlayArchetype.touch], and
85
+ [`terminate`][sonolus.script.archetype.PlayArchetype.terminate] callbacks.
86
+
87
+ Archetypes for scored notes should have the [`is_scored`][sonolus.script.archetype.PlayArchetype.is_scored] class variable set to `True`.
88
+
89
+ ### Watch Mode
90
+
81
91
  ```python
82
92
  from sonolus.script.engine import WatchMode
83
93
 
@@ -100,6 +110,16 @@ watch_mode = WatchMode(
100
110
  )
101
111
  ```
102
112
 
113
+ Watch mode archetypes subclass [`WatchArchetype`][sonolus.script.archetype.WatchArchetype] and should implement the [`spawn_time`][sonolus.script.archetype.WatchArchetype.spawn_time] and [`despawn_time`][sonolus.script.archetype.WatchArchetype.despawn_time] callbacks.
114
+ They may also implement the [`preprocess`][sonolus.script.archetype.WatchArchetype.preprocess], [`initialize`][sonolus.script.archetype.WatchArchetype.initialize], [`update_sequential`][sonolus.script.archetype.WatchArchetype.update_sequential], [`update_parallel`][sonolus.script.archetype.WatchArchetype.update_parallel], and
115
+ [`terminate`][sonolus.script.archetype.WatchArchetype.terminate] callbacks.
116
+
117
+ Watch mode also has the `update_spawn` global callback, which is invoked every frame and should return the reference
118
+ time to compare against spawn and despawn times of archetypes. Typically, this can be either the current time or the
119
+ current scaled time.
120
+
121
+ ### Preview Mode
122
+
103
123
  ```python
104
124
  from sonolus.script.engine import PreviewMode
105
125
 
@@ -115,6 +135,10 @@ preview_mode = PreviewMode(
115
135
  )
116
136
  ```
117
137
 
138
+ Preview mode archetypes subclass [`PreviewArchetype`][sonolus.script.archetype.PreviewArchetype] and may implement the [`preprocess`][sonolus.script.archetype.PreviewArchetype.preprocess] and [`render`][sonolus.script.archetype.PreviewArchetype.render] callbacks.
139
+
140
+ ### Tutorial Mode
141
+
118
142
  ```python
119
143
  from sonolus.script.engine import TutorialMode
120
144
 
@@ -138,8 +162,16 @@ tutorial_mode = TutorialMode(
138
162
  )
139
163
  ```
140
164
 
165
+ Tutorial mode does not have archetypes, but it has `preprocess`, `navigate`, and `update` global callbacks.
166
+
167
+ `preprocess` is invoked once before the tutorial starts.
168
+
169
+ `navigate` is invoked when the player navigates forward or backward in the tutorial.
170
+
171
+ `update` is invoked every frame and should handle most of the drawing logic.
172
+
141
173
  ## Levels
142
- Levels are defined using the `Level` class:
174
+ Levels are defined using the [`Level`][sonolus.script.level.Level] class:
143
175
 
144
176
  ```python
145
177
  from sonolus.script.level import LevelData, BpmChange, Level, TimescaleChange
@@ -0,0 +1,304 @@
1
+ # Resources & Declarations
2
+
3
+ ## Level Memory & Level Data
4
+ Level memory and level data are defined with the [`@level_memory`][sonolus.script.globals.level_memory] and [`@level_data`][sonolus.script.globals.level_data] class decorators, respectively:
5
+
6
+ ```python
7
+ from sonolus.script.globals import level_memory, level_data
8
+
9
+
10
+ @level_memory
11
+ class LevelMemory:
12
+ value: int
13
+
14
+
15
+ @level_data
16
+ class LevelData:
17
+ value: int
18
+ ```
19
+
20
+ Alternatively, they may be called as functions as well by passing the type as an argument:
21
+ ```python
22
+ from sonolus.script.globals import level_memory, level_data
23
+ from sonolus.script.vec import Vec2
24
+
25
+
26
+ level_memory_value = level_memory(Vec2)
27
+ level_data_value = level_data(Vec2)
28
+ ```
29
+
30
+ ## Streams
31
+ Streams are defined with the [`@streams`][sonolus.script.stream.streams] decorator:
32
+
33
+ ```python
34
+ from sonolus.script.stream import streams, Stream, StreamGroup
35
+ from sonolus.script.num import Num
36
+ from sonolus.script.vec import Vec2
37
+
38
+ @streams
39
+ class Streams:
40
+ stream_1: Stream[Num] # A stream of Num values
41
+ stream_2: Stream[Vec2] # A stream of Vec2 values
42
+ group_1: StreamGroup[Num, 10] # A group of 10 Num streams
43
+ group_2: StreamGroup[Vec2, 5] # A group of 5 Vec2 streams
44
+
45
+ data_field_1: Num # A data field of type Num
46
+ data_field_2: Vec2 # A data field of type Vec2
47
+ ```
48
+
49
+ Streams and stream groups are declared by annotating class attributes with [`Stream`][sonolus.script.stream.Stream] or [`StreamGroup`][sonolus.script.stream.StreamGroup].
50
+
51
+ Other types are also supported in the form of data fields. They may be used to store additional data to export from
52
+ Play to Watch mode.
53
+
54
+ In either case, data is write-only in Play mode and read-only in Watch mode.
55
+
56
+ This should only be used once in most projects, as multiple decorated classes will overlap with each other and
57
+ interfere when both are used at the same time.
58
+
59
+ For backwards compatibility, new streams and stream groups should be added to the end of existing ones, and
60
+ lengths and element types of existing streams and stream groups should not be changed. Otherwise, old replays may
61
+ not work on new versions of the engine.
62
+
63
+ ## Skins
64
+ Skins are defined with the [`@skin`][sonolus.script.sprite.skin] decorator:
65
+
66
+ ```python
67
+ from sonolus.script.sprite import skin, StandardSprite, sprite, Sprite, RenderMode
68
+
69
+
70
+ @skin
71
+ class Skin:
72
+ render_mode: RenderMode = RenderMode.DEFAULT
73
+
74
+ note: StandardSprite.NOTE_HEAD_RED
75
+ other: Sprite = sprite("other")
76
+ ```
77
+
78
+ Standard sprites are defined by annotating the field with the corresponding value from [`StandardSprite`][sonolus.script.sprite.StandardSprite].
79
+
80
+ Custom sprites are defined by annotating the field with [`Sprite`][sonolus.script.sprite.Sprite] and calling [`sprite`][sonolus.script.sprite.sprite] with the sprite name.
81
+
82
+ To set the render mode for the skin, set the `render_mode` field to the desired value from [`RenderMode`][sonolus.script.sprite.RenderMode].
83
+
84
+ ## Sound Effects
85
+ Sound effects are defined with the [`@effects`][sonolus.script.effect.effects] decorator:
86
+
87
+ ```python
88
+ from sonolus.script.effect import effects, StandardEffect, Effect, effect
89
+
90
+
91
+ @effects
92
+ class Effects:
93
+ tap_perfect: StandardEffect.PERFECT
94
+ other: Effect = effect("other")
95
+ ```
96
+
97
+ Standard sound effects are defined by annotating the field with the corresponding value from [`StandardEffect`][sonolus.script.effect.StandardEffect].
98
+
99
+ Custom sound effects are defined by annotating the field with [`Effect`][sonolus.script.effect.Effect] and calling [`effect`][sonolus.script.effect.effect] with the effect name.
100
+
101
+ ## Particles
102
+ Particles are defined with the [`@particles`][sonolus.script.particle.particles] decorator:
103
+
104
+ ```python
105
+ from sonolus.script.particle import particles, StandardParticle, Particle, particle
106
+
107
+
108
+ @particles
109
+ class Particles:
110
+ tap: StandardParticle.NOTE_CIRCULAR_TAP_RED
111
+ other: Particle = particle("other")
112
+ ```
113
+
114
+ Standard particles are defined by annotating the field with the corresponding value from [`StandardParticle`][sonolus.script.particle.StandardParticle].
115
+
116
+ Custom particles are defined by annotating the field with [`Particle`][sonolus.script.particle.Particle] and calling [`particle`][sonolus.script.particle.particle] with the particle name.
117
+
118
+ ## Buckets
119
+ Buckets are defined with the [`@buckets`][sonolus.script.bucket.buckets] decorator:
120
+
121
+ ```python
122
+ from sonolus.script.bucket import buckets, bucket_sprite, bucket, Bucket
123
+ from sonolus.script.text import StandardText
124
+ from my_engine.common.skin import Skin
125
+
126
+ @buckets
127
+ class Buckets:
128
+ note: Bucket = bucket(
129
+ sprites=[
130
+ bucket_sprite(
131
+ sprite=Skin.note,
132
+ x=0,
133
+ y=0,
134
+ w=2,
135
+ h=2,
136
+ )
137
+ ],
138
+ unit=StandardText.MILLISECOND_UNIT,
139
+ )
140
+ ```
141
+
142
+ Buckets are defined by annotating the field with [`Bucket`][sonolus.script.bucket.Bucket] and calling [`bucket`][sonolus.script.bucket.bucket] with the bucket name.
143
+
144
+ ## Tutorial Instructions
145
+ Tutorial instructions are defined with the [`@instructions`][sonolus.script.instruction.instructions] decorator:
146
+
147
+ ```python
148
+ from sonolus.script.instruction import instructions, StandardInstruction, Instruction, instruction
149
+
150
+
151
+ @instructions
152
+ class Instructions:
153
+ tap: StandardInstruction.TAP
154
+ other: Instruction = instruction("other")
155
+ ```
156
+
157
+ Standard instructions are defined by annotating the field with the corresponding value from [`StandardInstruction`][sonolus.script.instruction.StandardInstruction].
158
+
159
+ Custom instructions are defined by annotating the field with [`Instruction`][sonolus.script.instruction.Instruction] and calling [`instruction`][sonolus.script.instruction.instruction] with the instruction name.
160
+
161
+ ## Tutorial Instruction Icons
162
+ Tutorial instruction icons are defined with the [`@instruction_icons`][sonolus.script.instruction.instruction_icons] decorator:
163
+
164
+ ```python
165
+ from sonolus.script.instruction import instruction_icons, StandardInstructionIcon, InstructionIcon, instruction_icon
166
+
167
+
168
+ @instruction_icons
169
+ class InstructionIcons:
170
+ hand: StandardInstructionIcon.HAND
171
+ other: InstructionIcon = instruction_icon("other")
172
+ ```
173
+
174
+ Standard instruction icons are defined by annotating the field with the corresponding value from [`StandardInstructionIcon`][sonolus.script.instruction.StandardInstructionIcon].
175
+
176
+ Custom instruction icons are defined by annotating the field with [`InstructionIcon`][sonolus.script.instruction.InstructionIcon] and calling [`instruction_icon`][sonolus.script.instruction.instruction_icon] with the icon name.
177
+
178
+ ## Options
179
+ Engine options are defined with the [`@options`][sonolus.script.options.options] decorator:
180
+
181
+ ```python
182
+ from sonolus.script.options import options, select_option, slider_option, toggle_option
183
+
184
+
185
+ @options
186
+ class Options:
187
+ slider_option: float = slider_option(
188
+ name="Slider Option",
189
+ standard=True,
190
+ advanced=False,
191
+ default=0.5,
192
+ min=0,
193
+ max=1,
194
+ step=0.1,
195
+ unit="unit",
196
+ scope="scope",
197
+ )
198
+ toggle_option: bool = toggle_option(
199
+ name="Toggle Option",
200
+ standard=True,
201
+ advanced=False,
202
+ default=True,
203
+ scope="scope",
204
+ )
205
+ select_option: int = select_option(
206
+ name="Select Option",
207
+ standard=True,
208
+ advanced=False,
209
+ default="value",
210
+ values=["value"],
211
+ scope="scope",
212
+ )
213
+ ```
214
+
215
+ There are three types of options available:
216
+
217
+ 1. [`slider_option`][sonolus.script.options.slider_option]: A slider control for numeric values
218
+ 2. [`toggle_option`][sonolus.script.options.toggle_option]: A toggle switch for boolean values
219
+ 3. [`select_option`][sonolus.script.options.select_option]: A dropdown menu for selecting from predefined values
220
+
221
+ ## UI
222
+ Ui configuration is defined with the [`UiConfig`][sonolus.script.ui.UiConfig] class:
223
+
224
+ ```python
225
+ from sonolus.script.ui import (
226
+ EaseType,
227
+ UiAnimation,
228
+ UiAnimationTween,
229
+ UiConfig,
230
+ UiJudgmentErrorPlacement,
231
+ UiJudgmentErrorStyle,
232
+ UiMetric,
233
+ UiVisibility,
234
+ )
235
+
236
+ ui_config = UiConfig(
237
+ scope="my_engine",
238
+ primary_metric=UiMetric.ARCADE,
239
+ secondary_metric=UiMetric.LIFE,
240
+ menu_visibility=UiVisibility(
241
+ scale=1.0,
242
+ alpha=1.0,
243
+ ),
244
+ judgment_visibility=UiVisibility(
245
+ scale=1.0,
246
+ alpha=1.0,
247
+ ),
248
+ combo_visibility=UiVisibility(
249
+ scale=1.0,
250
+ alpha=1.0,
251
+ ),
252
+ primary_metric_visibility=UiVisibility(
253
+ scale=1.0,
254
+ alpha=1.0,
255
+ ),
256
+ secondary_metric_visibility=UiVisibility(
257
+ scale=1.0,
258
+ alpha=1.0,
259
+ ),
260
+ progress_visibility=UiVisibility(
261
+ scale=1.0,
262
+ alpha=1.0,
263
+ ),
264
+ tutorial_navigation_visibility=UiVisibility(
265
+ scale=1.0,
266
+ alpha=1.0,
267
+ ),
268
+ tutorial_instruction_visibility=UiVisibility(
269
+ scale=1.0,
270
+ alpha=1.0,
271
+ ),
272
+ judgment_animation=UiAnimation(
273
+ scale=UiAnimationTween(
274
+ start=1.0,
275
+ end=1.0,
276
+ duration=0.0,
277
+ ease=EaseType.NONE,
278
+ ),
279
+ alpha=UiAnimationTween(
280
+ start=1.0,
281
+ end=1.0,
282
+ duration=0.0,
283
+ ease=EaseType.NONE,
284
+ ),
285
+ ),
286
+ combo_animation=UiAnimation(
287
+ scale=UiAnimationTween(
288
+ start=1.2,
289
+ end=1.0,
290
+ duration=0.2,
291
+ ease=EaseType.IN_CUBIC,
292
+ ),
293
+ alpha=UiAnimationTween(
294
+ start=1.0,
295
+ end=1.0,
296
+ duration=0.0,
297
+ ease=EaseType.NONE,
298
+ ),
299
+ ),
300
+ judgment_error_style=UiJudgmentErrorStyle.LATE,
301
+ judgment_error_placement=UiJudgmentErrorPlacement.TOP,
302
+ judgment_error_min=0.0,
303
+ )
304
+ ```
@@ -1,5 +1,7 @@
1
+ from sonolus.script.archetype import PlayArchetype
2
+
1
3
  # Types
2
- Sonolus.py has 3 core types: `Num`, `Array`, and `Record`. representing numeric values, fixed-size arrays,
4
+ Sonolus.py has 3 core types: [`Num`](#num), [`Array`](#array), and [`Record`](#record). representing numeric values, fixed-size arrays,
3
5
  and custom data structures, respectively. Arrays and records can be nested within each other to create complex data
4
6
  structures.
5
7
 
@@ -89,13 +91,14 @@ Details:
89
91
 
90
92
  ## Array
91
93
 
92
- `Array[T, Size]` stores a fixed number of elements of the same type.
94
+ [`Array[T, Size]`][sonolus.script.array.Array] stores a fixed number of elements of the same type.
93
95
 
94
96
  It has two type parameters:
97
+
95
98
  - `T`: The type of the elements.
96
99
  - `Size`: The number of elements.
97
100
 
98
- You can import `Array` from `sonolus.script.array`:
101
+ You can import [`Array`][sonolus.script.array.Array] from `sonolus.script.array`:
99
102
 
100
103
  ```python
101
104
  from sonolus.script.array import Array
@@ -116,7 +119,7 @@ If at least one element is provided, the element type and size can be inferred:
116
119
  a3 = Array(1, 2, 3)
117
120
  ```
118
121
 
119
- Since `Array` takes type parameters, it is considered a generic type. A version of `Array` with type parameters provided
122
+ Since [`Array`][sonolus.script.array.Array] takes type parameters, it is considered a generic type. A version of [`Array`][sonolus.script.array.Array] with type parameters provided
120
123
  is considered a concrete type.
121
124
 
122
125
  ```python
@@ -184,7 +187,7 @@ assert a == Array(4, 2, 3)
184
187
  ```
185
188
 
186
189
  !!! warning
187
- If a value in an array is not a `Num`, updating it will copy the given value into the corresponding element
190
+ If a value in an array is not a [`Num`](#num), updating it will copy the given value into the corresponding element
188
191
  of the array. However, that element remains independent of the original value, which may lead to unexpected
189
192
  results when updating either value.
190
193
 
@@ -200,7 +203,7 @@ assert a == Array(4, 2, 3)
200
203
  ```
201
204
 
202
205
  For clarity, it's recommended to use the copy from operator (`@=`) when updating elements that are known to be
203
- an array or record (see the next section).
206
+ an array or record.
204
207
 
205
208
  ```python
206
209
  a[0] @= pair
@@ -223,18 +226,18 @@ for element in a:
223
226
 
224
227
  Other functionality:
225
228
 
226
- `Array` inherits from [ArrayLike][sonolus.script.array_like.ArrayLike] and supports all of its methods.
229
+ [`Array`][sonolus.script.array.Array] inherits from [`ArrayLike`][sonolus.script.array_like.ArrayLike] and supports all of its methods.
227
230
 
228
231
  ### Instance Checks
229
232
 
230
- Any array is considered an instance of the generic `Array` type.
233
+ Any array is considered an instance of the generic [`Array`][sonolus.script.array.Array] type.
231
234
 
232
235
  ```python
233
236
  a = Array(1, 2, 3)
234
237
  assert isinstance(a, Array)
235
238
  ```
236
239
 
237
- Only an array with the exact element type and size is considered an instance of a concrete `Array[T, Size]` type.
240
+ Only an array with the exact element type and size is considered an instance of a concrete [`Array[T, Size]`][sonolus.script.array.Array] type.
238
241
 
239
242
  ```python
240
243
  a = Array(1, 2, 3)
@@ -245,8 +248,8 @@ assert not isinstance(a, Array[Pair, 3])
245
248
 
246
249
  ### Enums
247
250
 
248
- There is limited support for enums containing `Num` values. Methods on enums are not supported.
249
- When used as a type, any enum class is treated as `Num` and no enforcement is done on the values.
251
+ There is limited support for enums containing [`Num`](#num) values. Methods on enums are not supported.
252
+ When used as a type, any enum class is treated as [`Num`](#num) and no enforcement is done on the values.
250
253
 
251
254
  ```python
252
255
  class MyEnum(IntEnum):
@@ -259,9 +262,9 @@ b = Array[MyEnum, 2](1, 2)
259
262
 
260
263
  ## Record
261
264
 
262
- `Record` is the base class for user-defined types in Sonolus.py. It functions similarly to dataclasses.
265
+ [`Record`][sonolus.script.record.Record] is the base class for user-defined types in Sonolus.py. It functions similarly to dataclasses.
263
266
 
264
- You can import `Record` from `sonolus.script.record`:
267
+ You can import [`Record`][sonolus.script.record.Record] from `sonolus.script.record`:
265
268
 
266
269
  ```python
267
270
  from sonolus.script.record import Record
@@ -269,7 +272,7 @@ from sonolus.script.record import Record
269
272
 
270
273
  ### Declaration
271
274
 
272
- A record can be defined by inheriting from `Record` and defining zero or more fields as class attributes:
275
+ A record can be defined by inheriting from [`Record`][sonolus.script.record.Record] and defining zero or more fields as class attributes:
273
276
 
274
277
  ```python
275
278
  class MyPair(Record):
@@ -277,7 +280,7 @@ class MyPair(Record):
277
280
  second: int
278
281
  ```
279
282
 
280
- Fields must be annotated by `Num` (or equivalently `int`, `float`, or `bool`),
283
+ Fields must be annotated by [`Num`](#num) (or equivalently `int`, `float`, or `bool`),
281
284
  a concrete array type, or a concrete record type.
282
285
 
283
286
  ```python
@@ -286,7 +289,7 @@ class MyRecord(Record):
286
289
  array: Array # Array is not concrete since it has unspecified type parameters
287
290
  ```
288
291
 
289
- A `Record` subclass cannot be further subclassed.
292
+ A [`Record`][sonolus.script.record.Record] subclass cannot be further subclassed.
290
293
 
291
294
  ```python
292
295
  # Not ok:
@@ -296,7 +299,7 @@ class MyPairSubclass(MyPair):
296
299
 
297
300
  ### Instantiation
298
301
 
299
- A constructor is automatically generated for the `Record` class:
302
+ A constructor is automatically generated for the [`Record`][sonolus.script.record.Record] class:
300
303
 
301
304
  ```python
302
305
  pair_1 = MyPair(1, 2)
@@ -305,7 +308,7 @@ pair_2 = MyPair(first=1, second=2)
305
308
 
306
309
  ### Generics
307
310
 
308
- `Record` supports generics. If at least one type parameter is provided in the class definition, a generic
311
+ [`Record`][sonolus.script.record.Record] supports generics. If at least one type parameter is provided in the class definition, a generic
309
312
  record type is created.
310
313
 
311
314
  ```python
@@ -324,7 +327,7 @@ pair_1 = MyGenericPair[int, int](1, 2)
324
327
  pair_2 = MyGenericPair(1, 2)
325
328
  ```
326
329
 
327
- The value of a type parameter can be accessed via the `type_var_value()` classmethod.
330
+ The value of a type parameter can be accessed via the [`type_var_value()`][sonolus.script.record.Record.type_var_value] classmethod.
328
331
 
329
332
  ```python
330
333
  class MyGenericRecord[T](Record):
@@ -368,7 +371,7 @@ class MyAddablePair(Record):
368
371
  ```
369
372
 
370
373
  If a dunder method has an in-place variant and the in-place method is not explicitly implemented
371
- (e.g. `__iadd__` is the in-place variant of `__add__`), `Record` will automatically generate one that
374
+ (e.g. `__iadd__` is the in-place variant of `__add__`), [`Record`][sonolus.script.record.Record] will automatically generate one that
372
375
  modifies the instance in place:
373
376
 
374
377
  ```python
@@ -378,7 +381,7 @@ pair += MyAddablePair(3, 4)
378
381
  assert pair == reference == MyAddablePair(4, 6) # The instance is modified in place
379
382
  ```
380
383
 
381
- Regular methods, properties, classmethods, and staticmethods can also be defined in a `Record` subclass.
384
+ Regular methods, properties, classmethods, and staticmethods can also be defined in a [`Record`][sonolus.script.record.Record] subclass.
382
385
 
383
386
  ```python
384
387
  class MyRecord(Record):
@@ -414,7 +417,7 @@ assert pair == MyPair(3, 2)
414
417
  ```
415
418
 
416
419
  !!! warning
417
- If a value in a record is not a `Num`, updating it will copy the given value into the corresponding field
420
+ If a value in a record is not a [`Num`](#num), updating it will copy the given value into the corresponding field
418
421
  of the record. However, that field remains independent of the original value.
419
422
 
420
423
  ```python
@@ -437,7 +440,7 @@ assert pair == MyPair(3, 2)
437
440
 
438
441
  ### Instance Checks
439
442
 
440
- Any record is considered an instance of the generic `Record` type:
443
+ Any record is considered an instance of the generic [`Record`][sonolus.script.record.Record] type:
441
444
 
442
445
  ```python
443
446
  pair = MyPair(1, 2)
@@ -451,7 +454,7 @@ pair = MyGenericPair[int, int](1, 2)
451
454
  assert isinstance(pair, MyGenericPair)
452
455
  ```
453
456
 
454
- Only an instance of a record with the exact field types is considered an instance of a concrete `Record` type:
457
+ Only an instance of a record with the exact field types is considered an instance of a concrete [`Record`][sonolus.script.record.Record] type:
455
458
 
456
459
  ```python
457
460
  pair = MyPair(1, 2)
@@ -460,10 +463,24 @@ assert not isinstance(pair, MyPair[int, Array[int, 2]])
460
463
  ```
461
464
 
462
465
  ## Transient Types
463
- In addition to the standard types, the following transient types are available.
464
- Compared to the standard types, these types come with the restriction that they cannot be used as type parameters
465
- or as a Record field's type. Otherwise, they can be used like any other type, including passing instances of them as
466
- arguments to functions.
466
+ In addition to the core types, the following transient types are available.
467
+ There are some restrictions on how they can be used:
468
+
469
+ - They cannot be used as type arguments:
470
+ ```python
471
+ # Not ok:
472
+ Array[str, 3]
473
+ ```
474
+ - They cannot be used as a field types:
475
+ ```python
476
+ # Not ok:
477
+ class MyRecord(Record):
478
+ field: str
479
+
480
+ # Not ok:
481
+ class MyArchetype(PlayArchetype):
482
+ field: str = imported()
483
+ ```
467
484
 
468
485
  ### tuple
469
486
 
@@ -0,0 +1,3 @@
1
+ # sonolus.script.stream
2
+
3
+ ::: sonolus.script.stream
@@ -9,6 +9,7 @@ theme:
9
9
  - navigation.expand
10
10
  - navigation.path
11
11
  - navigation.indexes
12
+ - navigation.footer
12
13
  - toc.follow
13
14
  - navigation.top
14
15
  palette:
@@ -106,6 +107,7 @@ nav:
106
107
  - reference/sonolus.script.record.md
107
108
  - reference/sonolus.script.runtime.md
108
109
  - reference/sonolus.script.sprite.md
110
+ - reference/sonolus.script.stream.md
109
111
  - reference/sonolus.script.text.md
110
112
  - reference/sonolus.script.timing.md
111
113
  - reference/sonolus.script.transform.md
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sonolus.py"
3
- version = "0.2.1"
3
+ version = "0.3.0"
4
4
  description = "Sonolus engine development in Python"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -180,6 +180,12 @@ class Op(StrEnum):
180
180
  StackSetPointer = ("StackSetPointer", True, False, False)
181
181
  StopLooped = ("StopLooped", True, False, False)
182
182
  StopLoopedScheduled = ("StopLoopedScheduled", True, False, False)
183
+ # Streams are immutable when they're readable, so we can treat read operations as pure.
184
+ StreamGetNextKey = ("StreamGetNextKey", False, True, False)
185
+ StreamGetPreviousKey = ("StreamGetPreviousKey", False, True, False)
186
+ StreamGetValue = ("StreamGetValue", False, True, False)
187
+ StreamHas = ("StreamHas", False, True, False)
188
+ StreamSet = ("StreamSet", True, False, False)
183
189
  Subtract = ("Subtract", False, True, False)
184
190
  Switch = ("Switch", False, True, True)
185
191
  SwitchInteger = ("SwitchInteger", False, True, True)