sonolus.py 0.1.2__tar.gz → 0.1.3__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 (97) hide show
  1. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/PKG-INFO +1 -2
  2. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/pyproject.toml +1 -1
  3. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/visitor.py +2 -0
  4. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/build/engine.py +55 -5
  5. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/callbacks.py +12 -0
  6. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/engine.py +37 -1
  7. sonolus_py-0.1.3/sonolus/script/instruction.py +151 -0
  8. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/iterator.py +3 -0
  9. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/runtime.py +43 -6
  10. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/vec.py +7 -1
  11. sonolus_py-0.1.2/sonolus/build/defaults.py +0 -32
  12. sonolus_py-0.1.2/sonolus/script/icon.py +0 -73
  13. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/.gitignore +0 -0
  14. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/.python-version +0 -0
  15. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/LICENSE +0 -0
  16. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/README.md +0 -0
  17. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/scripts/generate.py +0 -0
  18. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/scripts/runtimes/Engine/Tutorial/Blocks.json +0 -0
  19. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/scripts/runtimes/Functions.json +0 -0
  20. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/scripts/runtimes/Level/Play/Blocks.json +0 -0
  21. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/scripts/runtimes/Level/Preview/Blocks.json +0 -0
  22. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/scripts/runtimes/Level/Watch/Blocks.json +0 -0
  23. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/__init__.py +0 -0
  24. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/__init__.py +0 -0
  25. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/allocate.py +0 -0
  26. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/blocks.py +0 -0
  27. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/excepthook.py +0 -0
  28. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/finalize.py +0 -0
  29. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/flow.py +0 -0
  30. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/interpret.py +0 -0
  31. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/ir.py +0 -0
  32. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/mode.py +0 -0
  33. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/node.py +0 -0
  34. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/ops.py +0 -0
  35. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/optimize.py +0 -0
  36. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/passes.py +0 -0
  37. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/place.py +0 -0
  38. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/simplify.py +0 -0
  39. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/backend/utils.py +0 -0
  40. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/build/__init__.py +0 -0
  41. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/build/cli.py +0 -0
  42. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/build/collection.py +0 -0
  43. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/build/compile.py +0 -0
  44. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/build/level.py +0 -0
  45. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/build/node.py +0 -0
  46. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/build/project.py +0 -0
  47. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/py.typed +0 -0
  48. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/__init__.py +0 -0
  49. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/archetype.py +0 -0
  50. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/array.py +0 -0
  51. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/bucket.py +0 -0
  52. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/comptime.py +0 -0
  53. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/containers.py +0 -0
  54. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/debug.py +0 -0
  55. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/effect.py +0 -0
  56. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/globals.py +0 -0
  57. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/graphics.py +0 -0
  58. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/internal/__init__.py +0 -0
  59. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/internal/builtin_impls.py +0 -0
  60. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/internal/context.py +0 -0
  61. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/internal/descriptor.py +0 -0
  62. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/internal/error.py +0 -0
  63. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/internal/generic.py +0 -0
  64. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/internal/impl.py +0 -0
  65. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/internal/introspection.py +0 -0
  66. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/internal/native.py +0 -0
  67. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/internal/value.py +0 -0
  68. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/interval.py +0 -0
  69. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/level.py +0 -0
  70. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/math.py +0 -0
  71. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/num.py +0 -0
  72. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/options.py +0 -0
  73. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/particle.py +0 -0
  74. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/pointer.py +0 -0
  75. /sonolus_py-0.1.2/sonolus/script/preview.py → /sonolus_py-0.1.3/sonolus/script/print.py +0 -0
  76. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/project.py +0 -0
  77. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/range.py +0 -0
  78. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/record.py +0 -0
  79. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/sprite.py +0 -0
  80. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/text.py +0 -0
  81. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/timing.py +0 -0
  82. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/transform.py +0 -0
  83. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/ui.py +0 -0
  84. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/sonolus/script/values.py +0 -0
  85. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/tests/__init__.py +0 -0
  86. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/tests/script/__init__.py +0 -0
  87. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/tests/script/conftest.py +0 -0
  88. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/tests/script/test_array.py +0 -0
  89. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/tests/script/test_array_map.py +0 -0
  90. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/tests/script/test_assert.py +0 -0
  91. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/tests/script/test_helpers.py +0 -0
  92. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/tests/script/test_interval.py +0 -0
  93. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/tests/script/test_num.py +0 -0
  94. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/tests/script/test_range.py +0 -0
  95. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/tests/script/test_record.py +0 -0
  96. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/tests/script/test_var_array.py +0 -0
  97. {sonolus_py-0.1.2 → sonolus_py-0.1.3}/uv.lock +0 -0
@@ -1,8 +1,7 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sonolus.py
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Sonolus engine development in Python
5
- License-File: LICENSE
6
5
  Requires-Python: >=3.13
7
6
  Description-Content-Type: text/markdown
8
7
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sonolus.py"
3
- version = "0.1.2"
3
+ version = "0.1.3"
4
4
  description = "Sonolus engine development in Python"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -622,6 +622,8 @@ class Visitor(ast.NodeVisitor):
622
622
 
623
623
  def visit_Call(self, node):
624
624
  fn = self.visit(node.func)
625
+ if fn is Num:
626
+ raise ValueError("Calling int/bool/float is not supported")
625
627
  args = []
626
628
  kwargs = {}
627
629
  for arg in node.args:
@@ -7,12 +7,15 @@ 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 EMPTY_ENGINE_TUTORIAL_DATA
11
10
  from sonolus.script.archetype import BaseArchetype
12
11
  from sonolus.script.bucket import Buckets
13
- from sonolus.script.callbacks import update_spawn_callback
12
+ from sonolus.script.callbacks import navigate_callback, preprocess_callback, update_callback, update_spawn_callback
14
13
  from sonolus.script.effect import Effects
15
14
  from sonolus.script.engine import EngineData
15
+ from sonolus.script.instruction import (
16
+ TutorialInstructionIcons,
17
+ TutorialInstructions,
18
+ )
16
19
  from sonolus.script.internal.context import ReadOnlyMemory
17
20
  from sonolus.script.options import Options
18
21
  from sonolus.script.particle import Particles
@@ -61,17 +64,28 @@ def package_engine(engine: EngineData):
61
64
  rom=rom,
62
65
  update_spawn=engine.watch.update_spawn,
63
66
  )
64
- preview_mode = build_preview_mode(
67
+ preview_data = build_preview_mode(
65
68
  archetypes=engine.preview.archetypes,
66
69
  skin=engine.preview.skin,
67
70
  rom=rom,
68
71
  )
72
+ tutorial_data = build_tutorial_mode(
73
+ skin=engine.tutorial.skin,
74
+ effects=engine.tutorial.effects,
75
+ particles=engine.tutorial.particles,
76
+ instructions=engine.tutorial.instructions,
77
+ instruction_icons=engine.tutorial.instruction_icons,
78
+ preprocess=engine.tutorial.preprocess,
79
+ navigate=engine.tutorial.navigate,
80
+ update=engine.tutorial.update,
81
+ rom=rom,
82
+ )
69
83
  return PackagedEngine(
70
84
  configuration=package_output(configuration),
71
85
  play_data=package_output(play_data),
72
86
  watch_data=package_output(watch_data),
73
- preview_data=package_output(preview_mode),
74
- tutorial_data=package_output(EMPTY_ENGINE_TUTORIAL_DATA),
87
+ preview_data=package_output(preview_data),
88
+ tutorial_data=package_output(tutorial_data),
75
89
  rom=package_rom(rom),
76
90
  )
77
91
 
@@ -134,6 +148,35 @@ def build_preview_mode(
134
148
  }
135
149
 
136
150
 
151
+ def build_tutorial_mode(
152
+ skin: Skin,
153
+ effects: Effects,
154
+ particles: Particles,
155
+ instructions: TutorialInstructions,
156
+ instruction_icons: TutorialInstructionIcons,
157
+ preprocess: Callable[[], None],
158
+ navigate: Callable[[int], None],
159
+ update: Callable[[], None],
160
+ rom: ReadOnlyMemory,
161
+ ):
162
+ return {
163
+ **compile_mode(
164
+ mode=Mode.TUTORIAL,
165
+ rom=rom,
166
+ archetypes=[],
167
+ global_callbacks=[
168
+ (preprocess_callback, preprocess),
169
+ (navigate_callback, navigate),
170
+ (update_callback, update),
171
+ ],
172
+ ),
173
+ "skin": build_skin(skin),
174
+ "effect": build_effects(effects),
175
+ "particle": build_particles(particles),
176
+ "instruction": build_instructions(instructions, instruction_icons),
177
+ }
178
+
179
+
137
180
  def build_skin(skin: Skin) -> JsonValue:
138
181
  return {"sprites": [{"name": name, "id": i} for i, name in enumerate(skin._sprites_)]}
139
182
 
@@ -150,6 +193,13 @@ def build_buckets(buckets: Buckets) -> JsonValue:
150
193
  return [bucket.to_dict() for bucket in buckets._buckets_]
151
194
 
152
195
 
196
+ def build_instructions(instructions: TutorialInstructions, instruction_icons: TutorialInstructionIcons) -> JsonValue:
197
+ return {
198
+ "texts": [{"name": name, "id": i} for i, name in enumerate(instructions._instructions_)],
199
+ "icons": [{"name": name, "id": i} for i, name in enumerate(instruction_icons._instruction_icons_)],
200
+ }
201
+
202
+
153
203
  def package_rom(rom: ReadOnlyMemory) -> bytes:
154
204
  values = rom.values or [0]
155
205
  output = bytearray()
@@ -81,6 +81,18 @@ render_callback = CallbackInfo(
81
81
  supports_order=False,
82
82
  returns_value=False,
83
83
  )
84
+ navigate_callback = CallbackInfo(
85
+ name="navigate",
86
+ py_name="navigate",
87
+ supports_order=False,
88
+ returns_value=False,
89
+ )
90
+ update_callback = CallbackInfo(
91
+ name="update",
92
+ py_name="update",
93
+ supports_order=False,
94
+ returns_value=False,
95
+ )
84
96
 
85
97
 
86
98
  def _by_name(*callbacks: CallbackInfo) -> dict[str, CallbackInfo]:
@@ -1,11 +1,18 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Callable
4
+ from typing import Any
4
5
 
5
6
  from sonolus.build.collection import Asset
6
7
  from sonolus.script.archetype import BaseArchetype, PlayArchetype, PreviewArchetype, WatchArchetype
7
8
  from sonolus.script.bucket import Buckets, EmptyBuckets
8
9
  from sonolus.script.effect import Effects, EmptyEffects
10
+ from sonolus.script.instruction import (
11
+ EmptyInstructionIcons,
12
+ EmptyInstructions,
13
+ TutorialInstructionIcons,
14
+ TutorialInstructions,
15
+ )
9
16
  from sonolus.script.options import EmptyOptions, Options
10
17
  from sonolus.script.particle import EmptyParticles, Particles
11
18
  from sonolus.script.sprite import EmptySkin, Skin
@@ -41,7 +48,7 @@ class Engine:
41
48
  self.data = data
42
49
 
43
50
 
44
- def default_callback() -> float:
51
+ def default_callback() -> Any:
45
52
  return 0.0
46
53
 
47
54
 
@@ -104,6 +111,29 @@ class PreviewMode:
104
111
  raise ValueError(f"archetype {archetype} is not a BaseArchetype")
105
112
 
106
113
 
114
+ class TutorialMode:
115
+ def __init__(
116
+ self,
117
+ *,
118
+ skin: Skin = EmptySkin,
119
+ effects: Effects = EmptyEffects,
120
+ particles: Particles = EmptyParticles,
121
+ instructions: TutorialInstructions = EmptyInstructions,
122
+ instruction_icons: TutorialInstructionIcons = EmptyInstructionIcons,
123
+ preprocess: Callable[[], None],
124
+ navigate: Callable[[], None],
125
+ update: Callable[[], None],
126
+ ) -> None:
127
+ self.skin = skin
128
+ self.effects = effects
129
+ self.particles = particles
130
+ self.instructions = instructions
131
+ self.instruction_icons = instruction_icons
132
+ self.preprocess = preprocess
133
+ self.navigate = navigate
134
+ self.update = update
135
+
136
+
107
137
  class EngineData:
108
138
  def __init__(
109
139
  self,
@@ -113,9 +143,15 @@ class EngineData:
113
143
  play: PlayMode | None = None,
114
144
  watch: WatchMode | None = None,
115
145
  preview: PreviewMode | None = None,
146
+ tutorial: TutorialMode | None = None,
116
147
  ) -> None:
117
148
  self.ui = ui or UiConfig()
118
149
  self.options = options
119
150
  self.play = play or PlayMode()
120
151
  self.watch = watch or WatchMode(update_spawn=default_callback)
121
152
  self.preview = preview or PreviewMode()
153
+ self.tutorial = tutorial or TutorialMode(
154
+ preprocess=default_callback,
155
+ navigate=default_callback,
156
+ update=default_callback,
157
+ )
@@ -0,0 +1,151 @@
1
+ from dataclasses import dataclass
2
+ from typing import Annotated, Any, NewType, dataclass_transform, get_origin
3
+
4
+ from sonolus.backend.ops import Op
5
+ from sonolus.script.internal.introspection import get_field_specifiers
6
+ from sonolus.script.internal.native import native_function
7
+ from sonolus.script.record import Record
8
+ from sonolus.script.runtime import _TutorialInstruction
9
+ from sonolus.script.text import StandardText
10
+ from sonolus.script.vec import Vec2
11
+
12
+
13
+ class InstructionText(Record):
14
+ id: int
15
+
16
+ def show(self):
17
+ show_instruction(self)
18
+
19
+
20
+ class InstructionIcon(Record):
21
+ id: int
22
+
23
+ def paint(self, position: Vec2, size: float, rotation: float, z: float, a: float):
24
+ _paint(self.id, position.x, position.y, size, rotation, z, a)
25
+
26
+
27
+ @dataclass
28
+ class InstructionTextInfo:
29
+ name: str
30
+
31
+
32
+ @dataclass
33
+ class InstructionIconInfo:
34
+ name: str
35
+
36
+
37
+ def instruction(name: str) -> Any:
38
+ return InstructionTextInfo(name=name)
39
+
40
+
41
+ def instruction_icon(name: str) -> Any:
42
+ return InstructionIconInfo(name=name)
43
+
44
+
45
+ type TutorialInstructions = NewType("TutorialInstructions", Any)
46
+ type TutorialInstructionIcons = NewType("TutorialInstructionIcons", Any)
47
+
48
+
49
+ @dataclass_transform()
50
+ def instructions[T](cls: type[T]) -> T | TutorialInstructions:
51
+ if len(cls.__bases__) != 1:
52
+ raise ValueError("Instructions class must not inherit from any class (except object)")
53
+ instance = cls()
54
+ names = []
55
+ for i, (name, annotation) in enumerate(get_field_specifiers(cls).items()):
56
+ if get_origin(annotation) is not Annotated:
57
+ raise TypeError(f"Invalid annotation for instruction: {annotation}")
58
+ annotation_type = annotation.__args__[0]
59
+ annotation_values = annotation.__metadata__
60
+ if annotation_type is not InstructionText:
61
+ raise TypeError(
62
+ f"Invalid annotation for instruction: {annotation}, expected annotation of type InstructionText"
63
+ )
64
+ if len(annotation_values) != 1 or not isinstance(annotation_values[0], InstructionTextInfo):
65
+ raise TypeError(f"Invalid annotation for instruction: {annotation}, expected a single annotation value")
66
+ instruction_name = annotation_values[0].name
67
+ names.append(instruction_name)
68
+ setattr(instance, name, InstructionText(i))
69
+ instance._instructions_ = names
70
+ instance._is_comptime_value_ = True
71
+ return instance
72
+
73
+
74
+ @dataclass_transform()
75
+ def instruction_icons[T](cls: type[T]) -> T | TutorialInstructionIcons:
76
+ if len(cls.__bases__) != 1:
77
+ raise ValueError("Instruction icons class must not inherit from any class (except object)")
78
+ instance = cls()
79
+ names = []
80
+ for i, (name, annotation) in enumerate(get_field_specifiers(cls).items()):
81
+ if get_origin(annotation) is not Annotated:
82
+ raise TypeError(f"Invalid annotation for instruction icon: {annotation}")
83
+ annotation_type = annotation.__args__[0]
84
+ annotation_values = annotation.__metadata__
85
+ if annotation_type is not InstructionIcon:
86
+ raise TypeError(
87
+ f"Invalid annotation for instruction icon: {annotation}, expected annotation of type InstructionIcon"
88
+ )
89
+ if len(annotation_values) != 1 or not isinstance(annotation_values[0], InstructionIconInfo):
90
+ raise TypeError(
91
+ f"Invalid annotation for instruction icon: {annotation}, expected a single annotation value"
92
+ )
93
+ icon_name = annotation_values[0].name
94
+ names.append(icon_name)
95
+ setattr(instance, name, InstructionIcon(i))
96
+ instance._instruction_icons_ = names
97
+ instance._is_comptime_value_ = True
98
+ return instance
99
+
100
+
101
+ class StandardInstruction:
102
+ TAP = Annotated[InstructionText, instruction(StandardText.TAP)]
103
+ TAP_HOLD = Annotated[InstructionText, instruction(StandardText.TAP_HOLD)]
104
+ TAP_RELEASE = Annotated[InstructionText, instruction(StandardText.TAP_RELEASE)]
105
+ TAP_FLICK = Annotated[InstructionText, instruction(StandardText.TAP_FLICK)]
106
+ TAP_SLIDE = Annotated[InstructionText, instruction(StandardText.TAP_SLIDE)]
107
+ HOLD = Annotated[InstructionText, instruction(StandardText.HOLD)]
108
+ HOLD_SLIDE = Annotated[InstructionText, instruction(StandardText.HOLD_SLIDE)]
109
+ HOLD_FOLLOW = Annotated[InstructionText, instruction(StandardText.HOLD_FOLLOW)]
110
+ RELEASE = Annotated[InstructionText, instruction(StandardText.RELEASE)]
111
+ FLICK = Annotated[InstructionText, instruction(StandardText.FLICK)]
112
+ SLIDE = Annotated[InstructionText, instruction(StandardText.SLIDE)]
113
+ SLIDE_FLICK = Annotated[InstructionText, instruction(StandardText.SLIDE_FLICK)]
114
+ AVOID = Annotated[InstructionText, instruction(StandardText.AVOID)]
115
+ JIGGLE = Annotated[InstructionText, instruction(StandardText.JIGGLE)]
116
+
117
+
118
+ class StandardInstructionIcon:
119
+ HAND = Annotated[InstructionIcon, instruction_icon("#HAND")]
120
+ ARROW = Annotated[InstructionIcon, instruction_icon("#ARROW")]
121
+
122
+
123
+ @instructions
124
+ class EmptyInstructions:
125
+ pass
126
+
127
+
128
+ @instruction_icons
129
+ class EmptyInstructionIcons:
130
+ pass
131
+
132
+
133
+ @native_function(Op.Paint)
134
+ def _paint(
135
+ icon_id: int,
136
+ x: float,
137
+ y: float,
138
+ size: float,
139
+ rotation: float,
140
+ z: float,
141
+ a: float,
142
+ ):
143
+ raise NotImplementedError()
144
+
145
+
146
+ def show_instruction(inst: InstructionText, /):
147
+ _TutorialInstruction.text_id = inst.id
148
+
149
+
150
+ def clear_instruction():
151
+ _TutorialInstruction.text_id = -1
@@ -180,6 +180,9 @@ class ArrayReverser[V: ArrayLike](Record, ArrayLike):
180
180
  def __setitem__(self, index: Num, value: V):
181
181
  self.array[self.size() - 1 - index] = value
182
182
 
183
+ def reversed(self) -> ArrayLike[V]:
184
+ return self.array
185
+
183
186
 
184
187
  class Enumerator[V: SonolusIterator](Record, SonolusIterator):
185
188
  i: int
@@ -18,6 +18,7 @@ from sonolus.script.globals import (
18
18
  _runtime_particle_transform,
19
19
  _runtime_skin_transform,
20
20
  _runtime_touch_array,
21
+ _tutorial_instruction,
21
22
  _tutorial_runtime_environment,
22
23
  _tutorial_runtime_ui,
23
24
  _tutorial_runtime_ui_configuration,
@@ -181,6 +182,37 @@ class RuntimeUiLayout(Record):
181
182
  self.background = background
182
183
 
183
184
 
185
+ class BasicRuntimeUiLayout(Record):
186
+ anchor: Vec2
187
+ pivot: Vec2
188
+ dimensions: Vec2
189
+ rotation: float
190
+ alpha: float
191
+ background: bool
192
+
193
+ def update(
194
+ self,
195
+ anchor: Vec2 | None = None,
196
+ pivot: Vec2 | None = None,
197
+ dimensions: Vec2 | None = None,
198
+ rotation: float | None = None,
199
+ alpha: float | None = None,
200
+ background: bool | None = None,
201
+ ):
202
+ if anchor is not None:
203
+ self.anchor = anchor
204
+ if pivot is not None:
205
+ self.pivot = pivot
206
+ if dimensions is not None:
207
+ self.dimensions = dimensions
208
+ if rotation is not None:
209
+ self.rotation = rotation
210
+ if alpha is not None:
211
+ self.alpha = alpha
212
+ if background is not None:
213
+ self.background = background
214
+
215
+
184
216
  @_play_runtime_ui
185
217
  class _PlayRuntimeUi:
186
218
  menu: RuntimeUiLayout
@@ -208,16 +240,16 @@ class _WatchRuntimeUi:
208
240
 
209
241
  @_preview_runtime_ui
210
242
  class _PreviewRuntimeUi:
211
- menu: RuntimeUiLayout
212
- progress: RuntimeUiLayout
243
+ menu: BasicRuntimeUiLayout
244
+ progress: BasicRuntimeUiLayout
213
245
 
214
246
 
215
247
  @_tutorial_runtime_ui
216
248
  class _TutorialRuntimeUi:
217
- menu: RuntimeUiLayout
218
- previous: RuntimeUiLayout
219
- next: RuntimeUiLayout
220
- instruction: RuntimeUiLayout
249
+ menu: BasicRuntimeUiLayout
250
+ previous: BasicRuntimeUiLayout
251
+ next: BasicRuntimeUiLayout
252
+ instruction: BasicRuntimeUiLayout
221
253
 
222
254
 
223
255
  class Touch(Record):
@@ -392,6 +424,11 @@ class _LevelLife:
392
424
  self.consecutive_good_step = consecutive_good_step
393
425
 
394
426
 
427
+ @_tutorial_instruction
428
+ class _TutorialInstruction:
429
+ text_id: int
430
+
431
+
395
432
  @meta_fn
396
433
  def is_debug() -> bool:
397
434
  if not ctx():
@@ -1,6 +1,6 @@
1
1
  from typing import Self
2
2
 
3
- from sonolus.script.math import atan2
3
+ from sonolus.script.math import atan2, cos, sin
4
4
  from sonolus.script.num import Num
5
5
  from sonolus.script.record import Record
6
6
 
@@ -44,6 +44,12 @@ class Vec2(Record):
44
44
  def dot(self, other: Self) -> Num:
45
45
  return self.x * other.x + self.y * other.y
46
46
 
47
+ def rotate(self, angle: Num) -> Self:
48
+ return Vec2(
49
+ x=self.x * cos(angle) - self.y * sin(angle),
50
+ y=self.x * sin(angle) + self.y * cos(angle),
51
+ )
52
+
47
53
  @property
48
54
  def tuple(self) -> tuple[float, float]:
49
55
  return self.x, self.y
@@ -1,32 +0,0 @@
1
- EMPTY_ENGINE_PLAY_DATA = {
2
- "skin": {"sprites": []},
3
- "effect": {"clips": []},
4
- "particle": {"effects": []},
5
- "buckets": [],
6
- "archetypes": [],
7
- "nodes": [{"value": 0}],
8
- }
9
-
10
- EMPTY_ENGINE_WATCH_DATA = {
11
- "skin": {"sprites": []},
12
- "effect": {"clips": []},
13
- "particle": {"effects": []},
14
- "buckets": [],
15
- "archetypes": [],
16
- "updateSpawn": 0,
17
- "nodes": [{"value": 0}],
18
- }
19
-
20
- EMPTY_ENGINE_PREVIEW_DATA = {
21
- "skin": {"sprites": []},
22
- "archetypes": [],
23
- "nodes": [{"value": 0}],
24
- }
25
-
26
- EMPTY_ENGINE_TUTORIAL_DATA = {
27
- "skin": {"sprites": []},
28
- "effect": {"clips": []},
29
- "particle": {"effects": []},
30
- "instruction": {"texts": [], "icons": []},
31
- "nodes": [{"value": 0}],
32
- }
@@ -1,73 +0,0 @@
1
- from enum import StrEnum
2
-
3
-
4
- class StandardIcon(StrEnum):
5
- ADVANCED = "advanced"
6
- ANGLE_DOWN = "angleDown"
7
- ANGLE_LEFT = "angleLeft"
8
- ANGLE_RIGHT = "angleRight"
9
- ANGLES_DOWN = "anglesDown"
10
- ANGLES_LEFT = "anglesLeft"
11
- ANGLES_RIGHT = "anglesRight"
12
- ANGLES_UP = "anglesUp"
13
- ANGLE_UP = "angleUp"
14
- ARROW_DOWN = "arrowDown"
15
- ARROW_LEFT = "arrowLeft"
16
- ARROW_RIGHT = "arrowRight"
17
- ARROW_UP = "arrowUp"
18
- AWARD = "award"
19
- BACKGROUND = "background"
20
- BELL = "bell"
21
- BELL_SLASH = "bellSlash"
22
- BOOKMARK = "bookmark"
23
- BOOKMARK_HOLLOW = "bookmarkHollow"
24
- CHECK = "check"
25
- CLOCK = "clock"
26
- COMMENT = "comment"
27
- CROWN = "crown"
28
- DELETE = "delete"
29
- EDIT = "edit"
30
- EFFECT = "effect"
31
- ENGINE = "engine"
32
- ENVELOPE = "envelope"
33
- ENVELOPE_OPEN = "envelopeOpen"
34
- GLOBE = "globe"
35
- HEART = "heart"
36
- HEART_HOLLOW = "heartHollow"
37
- HIDE = "hide"
38
- INFORMATION = "information"
39
- LEVEL = "level"
40
- LOCK = "lock"
41
- MEDAL = "medal"
42
- MESSAGE = "message"
43
- MINUS = "minus"
44
- OPTIONS = "options"
45
- PARTICLE = "particle"
46
- PIN = "pin"
47
- PLAYER = "player"
48
- PLAYLIST = "playlist"
49
- PLUS = "plus"
50
- POST = "post"
51
- QUESTION = "question"
52
- RANKING = "ranking"
53
- REPLAY = "replay"
54
- REPLY = "reply"
55
- RESTORE = "restore"
56
- ROOM = "room"
57
- SEARCH = "search"
58
- SETTINGS = "settings"
59
- SHOW = "show"
60
- SHUFFLE = "shuffle"
61
- SKIN = "skin"
62
- STAR = "star"
63
- STAR_HALF = "starHalf"
64
- STAR_HOLLOW = "starHollow"
65
- STOPWATCH = "stopwatch"
66
- TAG = "tag"
67
- THUMBS_DOWN = "thumbsDown"
68
- THUMBS_DOWN_HOLLOW = "thumbsDownHollow"
69
- THUMBS_UP = "thumbsUp"
70
- THUMBS_UP_HOLLOW = "thumbsUpHollow"
71
- TROPHY = "trophy"
72
- UNLOCK = "unlock"
73
- X_MARK = "xMark"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes