sonolus.py 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sonolus.py might be problematic. Click here for more details.
- sonolus/__init__.py +0 -0
- sonolus/backend/__init__.py +0 -0
- sonolus/backend/allocate.py +51 -0
- sonolus/backend/blocks.py +756 -0
- sonolus/backend/excepthook.py +37 -0
- sonolus/backend/finalize.py +69 -0
- sonolus/backend/flow.py +92 -0
- sonolus/backend/interpret.py +333 -0
- sonolus/backend/ir.py +89 -0
- sonolus/backend/mode.py +24 -0
- sonolus/backend/node.py +40 -0
- sonolus/backend/ops.py +197 -0
- sonolus/backend/optimize.py +9 -0
- sonolus/backend/passes.py +6 -0
- sonolus/backend/place.py +90 -0
- sonolus/backend/simplify.py +30 -0
- sonolus/backend/utils.py +48 -0
- sonolus/backend/visitor.py +880 -0
- sonolus/build/__init__.py +0 -0
- sonolus/build/cli.py +170 -0
- sonolus/build/collection.py +293 -0
- sonolus/build/compile.py +90 -0
- sonolus/build/defaults.py +32 -0
- sonolus/build/engine.py +149 -0
- sonolus/build/level.py +23 -0
- sonolus/build/node.py +43 -0
- sonolus/build/project.py +94 -0
- sonolus/py.typed +0 -0
- sonolus/script/__init__.py +0 -0
- sonolus/script/archetype.py +651 -0
- sonolus/script/array.py +241 -0
- sonolus/script/bucket.py +192 -0
- sonolus/script/callbacks.py +105 -0
- sonolus/script/comptime.py +146 -0
- sonolus/script/containers.py +247 -0
- sonolus/script/debug.py +70 -0
- sonolus/script/effect.py +132 -0
- sonolus/script/engine.py +101 -0
- sonolus/script/globals.py +234 -0
- sonolus/script/graphics.py +141 -0
- sonolus/script/icon.py +73 -0
- sonolus/script/internal/__init__.py +5 -0
- sonolus/script/internal/builtin_impls.py +144 -0
- sonolus/script/internal/context.py +365 -0
- sonolus/script/internal/descriptor.py +17 -0
- sonolus/script/internal/error.py +15 -0
- sonolus/script/internal/generic.py +197 -0
- sonolus/script/internal/impl.py +69 -0
- sonolus/script/internal/introspection.py +14 -0
- sonolus/script/internal/native.py +38 -0
- sonolus/script/internal/value.py +144 -0
- sonolus/script/interval.py +98 -0
- sonolus/script/iterator.py +211 -0
- sonolus/script/level.py +52 -0
- sonolus/script/math.py +92 -0
- sonolus/script/num.py +382 -0
- sonolus/script/options.py +194 -0
- sonolus/script/particle.py +158 -0
- sonolus/script/pointer.py +30 -0
- sonolus/script/project.py +17 -0
- sonolus/script/range.py +58 -0
- sonolus/script/record.py +293 -0
- sonolus/script/runtime.py +526 -0
- sonolus/script/sprite.py +332 -0
- sonolus/script/text.py +404 -0
- sonolus/script/timing.py +42 -0
- sonolus/script/transform.py +118 -0
- sonolus/script/ui.py +160 -0
- sonolus/script/values.py +43 -0
- sonolus/script/vec.py +48 -0
- sonolus_py-0.1.0.dist-info/METADATA +10 -0
- sonolus_py-0.1.0.dist-info/RECORD +75 -0
- sonolus_py-0.1.0.dist-info/WHEEL +4 -0
- sonolus_py-0.1.0.dist-info/entry_points.txt +2 -0
- sonolus_py-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,651 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import inspect
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from types import FunctionType
|
|
8
|
+
from typing import Annotated, Any, ClassVar, Self, get_origin
|
|
9
|
+
|
|
10
|
+
from sonolus.backend.ir import IRConst, IRInstr
|
|
11
|
+
from sonolus.backend.mode import Mode
|
|
12
|
+
from sonolus.backend.ops import Op
|
|
13
|
+
from sonolus.script.bucket import Bucket, Judgment
|
|
14
|
+
from sonolus.script.callbacks import PLAY_CALLBACKS, WATCH_ARCHETYPE_CALLBACKS, CallbackInfo
|
|
15
|
+
from sonolus.script.internal.context import ctx
|
|
16
|
+
from sonolus.script.internal.descriptor import SonolusDescriptor
|
|
17
|
+
from sonolus.script.internal.generic import validate_concrete_type
|
|
18
|
+
from sonolus.script.internal.impl import meta_fn, validate_value
|
|
19
|
+
from sonolus.script.internal.introspection import get_field_specifiers
|
|
20
|
+
from sonolus.script.internal.native import native_call
|
|
21
|
+
from sonolus.script.internal.value import Value
|
|
22
|
+
from sonolus.script.num import Num
|
|
23
|
+
from sonolus.script.pointer import deref
|
|
24
|
+
from sonolus.script.record import Record
|
|
25
|
+
from sonolus.script.values import zeros
|
|
26
|
+
|
|
27
|
+
ENTITY_MEMORY_SIZE = 64
|
|
28
|
+
ENTITY_DATA_SIZE = 32
|
|
29
|
+
ENTITY_SHARED_MEMORY_SIZE = 32
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class StorageType(Enum):
|
|
33
|
+
IMPORTED = "imported"
|
|
34
|
+
EXPORTED = "exported"
|
|
35
|
+
MEMORY = "memory"
|
|
36
|
+
SHARED = "shared_memory"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class ArchetypeFieldInfo:
|
|
41
|
+
name: str | None
|
|
42
|
+
storage: StorageType
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class ArchetypeField(SonolusDescriptor):
|
|
46
|
+
def __init__(self, name: str, data_name: str, storage: StorageType, offset: int, type_: type[Value]):
|
|
47
|
+
self.name = name
|
|
48
|
+
self.data_name = data_name # name used in level data
|
|
49
|
+
self.storage = storage
|
|
50
|
+
self.offset = offset
|
|
51
|
+
self.type = type_
|
|
52
|
+
|
|
53
|
+
def __get__(self, instance: BaseArchetype, owner):
|
|
54
|
+
if instance is None:
|
|
55
|
+
return self
|
|
56
|
+
result = None
|
|
57
|
+
match self.storage:
|
|
58
|
+
case StorageType.IMPORTED:
|
|
59
|
+
match instance._data_:
|
|
60
|
+
case ArchetypeSelfData():
|
|
61
|
+
result = deref(ctx().blocks.EntityData, self.offset, self.type)
|
|
62
|
+
case ArchetypeReferenceData(index=index):
|
|
63
|
+
result = deref(ctx().blocks.EntityDataArray, self.offset + index * ENTITY_DATA_SIZE, self.type)
|
|
64
|
+
case ArchetypeLevelData(values=values):
|
|
65
|
+
result = values[self.name]
|
|
66
|
+
case StorageType.EXPORTED:
|
|
67
|
+
raise RuntimeError("Exported fields are write-only")
|
|
68
|
+
case StorageType.MEMORY:
|
|
69
|
+
match instance._data_:
|
|
70
|
+
case ArchetypeSelfData():
|
|
71
|
+
result = deref(ctx().blocks.EntityMemory, self.offset, self.type)
|
|
72
|
+
case ArchetypeReferenceData():
|
|
73
|
+
raise RuntimeError("Entity memory of other entities is not accessible")
|
|
74
|
+
case ArchetypeLevelData():
|
|
75
|
+
raise RuntimeError("Entity memory is not available in level data")
|
|
76
|
+
case StorageType.SHARED:
|
|
77
|
+
match instance._data_:
|
|
78
|
+
case ArchetypeSelfData():
|
|
79
|
+
result = deref(ctx().blocks.EntitySharedMemory, self.offset, self.type)
|
|
80
|
+
case ArchetypeReferenceData(index=index):
|
|
81
|
+
result = deref(
|
|
82
|
+
ctx().blocks.EntitySharedMemoryArray,
|
|
83
|
+
self.offset + index * ENTITY_SHARED_MEMORY_SIZE,
|
|
84
|
+
self.type,
|
|
85
|
+
)
|
|
86
|
+
case ArchetypeLevelData():
|
|
87
|
+
raise RuntimeError("Entity shared memory is not available in level data")
|
|
88
|
+
if result is None:
|
|
89
|
+
raise RuntimeError("Invalid storage type")
|
|
90
|
+
if ctx():
|
|
91
|
+
return result._get_()
|
|
92
|
+
else:
|
|
93
|
+
return result._as_py_()
|
|
94
|
+
|
|
95
|
+
def __set__(self, instance: BaseArchetype, value):
|
|
96
|
+
if instance is None:
|
|
97
|
+
raise RuntimeError("Cannot set field on class")
|
|
98
|
+
if not self.type._accepts_(value):
|
|
99
|
+
raise TypeError(f"Expected {self.type}, got {type(value)}")
|
|
100
|
+
target = None
|
|
101
|
+
match self.storage:
|
|
102
|
+
case StorageType.IMPORTED:
|
|
103
|
+
match instance._data_:
|
|
104
|
+
case ArchetypeSelfData():
|
|
105
|
+
target = deref(ctx().blocks.EntityData, self.offset, self.type)
|
|
106
|
+
case ArchetypeReferenceData(index=index):
|
|
107
|
+
target = deref(ctx().blocks.EntityDataArray, self.offset + index * ENTITY_DATA_SIZE, self.type)
|
|
108
|
+
case ArchetypeLevelData(values=values):
|
|
109
|
+
target = values[self.name]
|
|
110
|
+
case StorageType.EXPORTED:
|
|
111
|
+
match instance._data_:
|
|
112
|
+
case ArchetypeSelfData():
|
|
113
|
+
if not isinstance(value, self.type):
|
|
114
|
+
raise TypeError(f"Expected {self.type}, got {type(value)}")
|
|
115
|
+
for k, v in value._to_flat_dict_(self.data_name).items():
|
|
116
|
+
index = instance._exported_keys_[k]
|
|
117
|
+
ctx().add_statements(IRInstr(Op.ExportValue, [IRConst(index), Num._accept_(v).ir()]))
|
|
118
|
+
return
|
|
119
|
+
case ArchetypeReferenceData():
|
|
120
|
+
raise RuntimeError("Exported fields of other entities are not accessible")
|
|
121
|
+
case ArchetypeLevelData():
|
|
122
|
+
raise RuntimeError("Exported fields are not available in level data")
|
|
123
|
+
case StorageType.MEMORY:
|
|
124
|
+
match instance._data_:
|
|
125
|
+
case ArchetypeSelfData():
|
|
126
|
+
target = deref(ctx().blocks.EntityMemory, self.offset, self.type)
|
|
127
|
+
case ArchetypeReferenceData():
|
|
128
|
+
raise RuntimeError("Entity memory of other entities is not accessible")
|
|
129
|
+
case ArchetypeLevelData():
|
|
130
|
+
raise RuntimeError("Entity memory is not available in level data")
|
|
131
|
+
case StorageType.SHARED:
|
|
132
|
+
match instance._data_:
|
|
133
|
+
case ArchetypeSelfData():
|
|
134
|
+
target = deref(ctx().blocks.EntitySharedMemory, self.offset, self.type)
|
|
135
|
+
case ArchetypeReferenceData(index=index):
|
|
136
|
+
target = deref(
|
|
137
|
+
ctx().blocks.EntitySharedMemoryArray,
|
|
138
|
+
self.offset + index * ENTITY_SHARED_MEMORY_SIZE,
|
|
139
|
+
self.type,
|
|
140
|
+
)
|
|
141
|
+
case ArchetypeLevelData():
|
|
142
|
+
raise RuntimeError("Entity shared memory is not available in level data")
|
|
143
|
+
if target is None:
|
|
144
|
+
raise RuntimeError("Invalid storage type")
|
|
145
|
+
value = self.type._accept_(value)
|
|
146
|
+
if self.type._is_value_type_():
|
|
147
|
+
target._set_(value)
|
|
148
|
+
else:
|
|
149
|
+
target._copy_from_(value)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def imported(*, name: str | None = None) -> Any:
|
|
153
|
+
return ArchetypeFieldInfo(name, StorageType.IMPORTED)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def exported(*, name: str | None = None) -> Any:
|
|
157
|
+
return ArchetypeFieldInfo(name, StorageType.EXPORTED)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def entity_memory() -> Any:
|
|
161
|
+
return ArchetypeFieldInfo(None, StorageType.MEMORY)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def shared_memory() -> Any:
|
|
165
|
+
return ArchetypeFieldInfo(None, StorageType.SHARED)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
_annotation_defaults: dict[Callable, ArchetypeFieldInfo] = {
|
|
169
|
+
imported: imported(),
|
|
170
|
+
exported: exported(),
|
|
171
|
+
entity_memory: entity_memory(),
|
|
172
|
+
shared_memory: shared_memory(),
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class StandardImport:
|
|
177
|
+
Beat = Annotated[float, imported(name="#BEAT")]
|
|
178
|
+
Bpm = Annotated[float, imported(name="#BPM")]
|
|
179
|
+
Timescale = Annotated[float, imported(name="#TIMESCALE")]
|
|
180
|
+
Judgment = Annotated[int, imported(name="#JUDGMENT")]
|
|
181
|
+
Accuracy = Annotated[float, imported(name="#ACCURACY")]
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def callback[T: Callable](order: int) -> Callable[[T], T]:
|
|
185
|
+
def decorator(func: T) -> T:
|
|
186
|
+
func._callback_order_ = order
|
|
187
|
+
return func
|
|
188
|
+
|
|
189
|
+
return decorator
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class ArchetypeSelfData:
|
|
193
|
+
pass
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
class ArchetypeReferenceData:
|
|
197
|
+
index: Num
|
|
198
|
+
|
|
199
|
+
def __init__(self, index: Num):
|
|
200
|
+
self.index = index
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class ArchetypeLevelData:
|
|
204
|
+
values: dict[str, Value]
|
|
205
|
+
|
|
206
|
+
def __init__(self, values: dict[str, Value]):
|
|
207
|
+
self.values = values
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
type ArchetypeData = ArchetypeSelfData | ArchetypeReferenceData | ArchetypeLevelData
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class BaseArchetype:
|
|
214
|
+
_is_comptime_value_ = True
|
|
215
|
+
|
|
216
|
+
_supported_callbacks_: ClassVar[dict[str, CallbackInfo]]
|
|
217
|
+
_default_callbacks_: ClassVar[set[Callable]]
|
|
218
|
+
|
|
219
|
+
_imported_fields_: ClassVar[dict[str, ArchetypeField]]
|
|
220
|
+
_exported_fields_: ClassVar[dict[str, ArchetypeField]]
|
|
221
|
+
_memory_fields_: ClassVar[dict[str, ArchetypeField]]
|
|
222
|
+
_shared_memory_fields_: ClassVar[dict[str, ArchetypeField]]
|
|
223
|
+
|
|
224
|
+
_imported_keys_: ClassVar[dict[str, int]]
|
|
225
|
+
_exported_keys_: ClassVar[dict[str, int]]
|
|
226
|
+
_callbacks_: ClassVar[list[Callable]]
|
|
227
|
+
_data_constructor_signature_: ClassVar[inspect.Signature]
|
|
228
|
+
_spawn_signature_: ClassVar[inspect.Signature]
|
|
229
|
+
|
|
230
|
+
_data_: ArchetypeData
|
|
231
|
+
|
|
232
|
+
name: ClassVar[str | None] = None
|
|
233
|
+
is_scored: ClassVar[bool] = False
|
|
234
|
+
|
|
235
|
+
def __init__(self, *args, **kwargs):
|
|
236
|
+
if ctx():
|
|
237
|
+
raise RuntimeError("The Archetype constructor is only for defining level data")
|
|
238
|
+
bound = self._data_constructor_signature_.bind_partial(*args, **kwargs)
|
|
239
|
+
bound.apply_defaults()
|
|
240
|
+
values = {
|
|
241
|
+
field.name: field.type._accept_(bound.arguments.get(field.name) or zeros(field.type))._get_()
|
|
242
|
+
for field in self._imported_fields_.values()
|
|
243
|
+
}
|
|
244
|
+
self._data_ = ArchetypeLevelData(values=values)
|
|
245
|
+
|
|
246
|
+
@classmethod
|
|
247
|
+
def _new(cls):
|
|
248
|
+
return object.__new__(cls)
|
|
249
|
+
|
|
250
|
+
@classmethod
|
|
251
|
+
def _for_compilation(cls):
|
|
252
|
+
result = cls._new()
|
|
253
|
+
result._data_ = ArchetypeSelfData()
|
|
254
|
+
return result
|
|
255
|
+
|
|
256
|
+
@classmethod
|
|
257
|
+
@meta_fn
|
|
258
|
+
def at(cls, index: Num) -> Self:
|
|
259
|
+
result = cls._new()
|
|
260
|
+
result._data_ = ArchetypeReferenceData(index=index)
|
|
261
|
+
return result
|
|
262
|
+
|
|
263
|
+
@classmethod
|
|
264
|
+
@meta_fn
|
|
265
|
+
def id(cls):
|
|
266
|
+
if not ctx():
|
|
267
|
+
raise RuntimeError("Archetype id is only available during compilation")
|
|
268
|
+
result = ctx().global_state.archetypes.get(cls)
|
|
269
|
+
if result is None:
|
|
270
|
+
raise RuntimeError("Archetype is not registered")
|
|
271
|
+
return result
|
|
272
|
+
|
|
273
|
+
@classmethod
|
|
274
|
+
@meta_fn
|
|
275
|
+
def spawn(cls, **kwargs):
|
|
276
|
+
if not ctx():
|
|
277
|
+
raise RuntimeError("Spawn is only allowed within a callback")
|
|
278
|
+
archetype_id = cls.id()
|
|
279
|
+
bound = cls._spawn_signature_.bind_partial(**kwargs)
|
|
280
|
+
bound.apply_defaults()
|
|
281
|
+
data = []
|
|
282
|
+
for field in cls._memory_fields_.values():
|
|
283
|
+
data.extend(field.type._accept_(bound.arguments[field.name] or zeros(field.type))._to_list_())
|
|
284
|
+
native_call(Op.Spawn, archetype_id, *(Num(x) for x in data))
|
|
285
|
+
|
|
286
|
+
def _level_data_entries(self):
|
|
287
|
+
if not isinstance(self._data_, ArchetypeLevelData):
|
|
288
|
+
raise RuntimeError("Entity is not level data")
|
|
289
|
+
entries = []
|
|
290
|
+
for name, value in self._data_.values.items():
|
|
291
|
+
field_info = self._imported_fields_.get(name)
|
|
292
|
+
for k, v in value._to_flat_dict_(field_info.data_name).items():
|
|
293
|
+
entries.append({"name": k, "value": v})
|
|
294
|
+
return entries
|
|
295
|
+
|
|
296
|
+
def __init_subclass__(cls, **kwargs):
|
|
297
|
+
if cls.__module__ == BaseArchetype.__module__:
|
|
298
|
+
if cls._supported_callbacks_ is None:
|
|
299
|
+
raise TypeError("Cannot directly subclass Archetype, use the Archetype subclass for your mode")
|
|
300
|
+
cls._default_callbacks_ = {getattr(cls, cb_info.py_name) for cb_info in cls._supported_callbacks_.values()}
|
|
301
|
+
return
|
|
302
|
+
if getattr(cls, "_callbacks_", None) is not None:
|
|
303
|
+
raise TypeError("Cannot subclass Archetypes")
|
|
304
|
+
if cls.name is None:
|
|
305
|
+
cls.name = cls.__name__
|
|
306
|
+
cls._imported_fields_ = {}
|
|
307
|
+
cls._exported_fields_ = {}
|
|
308
|
+
cls._memory_fields_ = {}
|
|
309
|
+
cls._shared_memory_fields_ = {}
|
|
310
|
+
imported_offset = 0
|
|
311
|
+
exported_offset = 0
|
|
312
|
+
memory_offset = 0
|
|
313
|
+
shared_memory_offset = 0
|
|
314
|
+
for name, value in get_field_specifiers(cls).items():
|
|
315
|
+
if value is ClassVar or get_origin(value) is ClassVar:
|
|
316
|
+
continue
|
|
317
|
+
if get_origin(value) is not Annotated:
|
|
318
|
+
raise TypeError(
|
|
319
|
+
"Archetype fields must be annotated using imported, exported, entity_memory, or shared_memory"
|
|
320
|
+
)
|
|
321
|
+
field_info = None
|
|
322
|
+
for metadata in value.__metadata__:
|
|
323
|
+
if isinstance(metadata, FunctionType):
|
|
324
|
+
metadata = _annotation_defaults.get(metadata, metadata)
|
|
325
|
+
if isinstance(metadata, ArchetypeFieldInfo):
|
|
326
|
+
if field_info is not None:
|
|
327
|
+
raise TypeError(
|
|
328
|
+
f"Unexpected multiple field annotations for '{name}', "
|
|
329
|
+
f"expected exactly one of imported, exported, entity_memory, or shared_memory"
|
|
330
|
+
)
|
|
331
|
+
field_info = metadata
|
|
332
|
+
if field_info is None:
|
|
333
|
+
raise TypeError(
|
|
334
|
+
f"Missing field annotation for '{name}', "
|
|
335
|
+
f"expected exactly one of imported, exported, entity_memory, or shared_memory"
|
|
336
|
+
)
|
|
337
|
+
field_type = validate_concrete_type(value.__args__[0])
|
|
338
|
+
match field_info.storage:
|
|
339
|
+
case StorageType.IMPORTED:
|
|
340
|
+
cls._imported_fields_[name] = ArchetypeField(
|
|
341
|
+
name, field_info.name or name, field_info.storage, imported_offset, field_type
|
|
342
|
+
)
|
|
343
|
+
imported_offset += field_type._size_()
|
|
344
|
+
setattr(cls, name, cls._imported_fields_[name])
|
|
345
|
+
case StorageType.EXPORTED:
|
|
346
|
+
cls._exported_fields_[name] = ArchetypeField(
|
|
347
|
+
name, field_info.name or name, field_info.storage, exported_offset, field_type
|
|
348
|
+
)
|
|
349
|
+
exported_offset += field_type._size_()
|
|
350
|
+
setattr(cls, name, cls._exported_fields_[name])
|
|
351
|
+
case StorageType.MEMORY:
|
|
352
|
+
cls._memory_fields_[name] = ArchetypeField(
|
|
353
|
+
name, field_info.name or name, field_info.storage, memory_offset, field_type
|
|
354
|
+
)
|
|
355
|
+
memory_offset += field_type._size_()
|
|
356
|
+
setattr(cls, name, cls._memory_fields_[name])
|
|
357
|
+
case StorageType.SHARED:
|
|
358
|
+
cls._shared_memory_fields_[name] = ArchetypeField(
|
|
359
|
+
name, field_info.name or name, field_info.storage, shared_memory_offset, field_type
|
|
360
|
+
)
|
|
361
|
+
shared_memory_offset += field_type._size_()
|
|
362
|
+
setattr(cls, name, cls._shared_memory_fields_[name])
|
|
363
|
+
cls._imported_keys_ = {
|
|
364
|
+
name: i
|
|
365
|
+
for i, name in enumerate(
|
|
366
|
+
key for field in cls._imported_fields_.values() for key in field.type._flat_keys_(field.data_name)
|
|
367
|
+
)
|
|
368
|
+
}
|
|
369
|
+
cls._exported_keys_ = {
|
|
370
|
+
name: i
|
|
371
|
+
for i, name in enumerate(
|
|
372
|
+
key for field in cls._exported_fields_.values() for key in field.type._flat_keys_(field.data_name)
|
|
373
|
+
)
|
|
374
|
+
}
|
|
375
|
+
cls._data_constructor_signature_ = inspect.Signature(
|
|
376
|
+
[inspect.Parameter(name, inspect.Parameter.POSITIONAL_OR_KEYWORD) for name in cls._imported_fields_]
|
|
377
|
+
)
|
|
378
|
+
cls._spawn_signature_ = inspect.Signature(
|
|
379
|
+
[inspect.Parameter(name, inspect.Parameter.POSITIONAL_OR_KEYWORD) for name in cls._memory_fields_]
|
|
380
|
+
)
|
|
381
|
+
cls._callbacks_ = []
|
|
382
|
+
for name in cls._supported_callbacks_:
|
|
383
|
+
cb = getattr(cls, name)
|
|
384
|
+
if cb in cls._default_callbacks_:
|
|
385
|
+
continue
|
|
386
|
+
cls._callbacks_.append(cb)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class PlayArchetype(BaseArchetype):
|
|
390
|
+
_supported_callbacks_ = PLAY_CALLBACKS
|
|
391
|
+
|
|
392
|
+
def preprocess(self):
|
|
393
|
+
pass
|
|
394
|
+
|
|
395
|
+
def spawn_order(self) -> float:
|
|
396
|
+
pass
|
|
397
|
+
|
|
398
|
+
def should_spawn(self) -> bool:
|
|
399
|
+
pass
|
|
400
|
+
|
|
401
|
+
def initialize(self):
|
|
402
|
+
pass
|
|
403
|
+
|
|
404
|
+
def update_sequential(self):
|
|
405
|
+
pass
|
|
406
|
+
|
|
407
|
+
def update_parallel(self):
|
|
408
|
+
pass
|
|
409
|
+
|
|
410
|
+
def touch(self):
|
|
411
|
+
pass
|
|
412
|
+
|
|
413
|
+
def terminate(self):
|
|
414
|
+
pass
|
|
415
|
+
|
|
416
|
+
@property
|
|
417
|
+
@meta_fn
|
|
418
|
+
def despawn(self):
|
|
419
|
+
if not ctx():
|
|
420
|
+
raise RuntimeError("Calling despawn is only allowed within a callback")
|
|
421
|
+
match self._data_:
|
|
422
|
+
case ArchetypeSelfData():
|
|
423
|
+
return deref(ctx().blocks.EntityDespawn, 0, Num)
|
|
424
|
+
case _:
|
|
425
|
+
raise RuntimeError("Despawn is only accessible from the entity itself")
|
|
426
|
+
|
|
427
|
+
@despawn.setter
|
|
428
|
+
@meta_fn
|
|
429
|
+
def despawn(self, value: bool):
|
|
430
|
+
if not ctx():
|
|
431
|
+
raise RuntimeError("Calling despawn is only allowed within a callback")
|
|
432
|
+
match self._data_:
|
|
433
|
+
case ArchetypeSelfData():
|
|
434
|
+
deref(ctx().blocks.EntityDespawn, 0, Num)._set_(value)
|
|
435
|
+
case _:
|
|
436
|
+
raise RuntimeError("Despawn is only accessible from the entity itself")
|
|
437
|
+
|
|
438
|
+
@property
|
|
439
|
+
@meta_fn
|
|
440
|
+
def _info(self):
|
|
441
|
+
if not ctx():
|
|
442
|
+
raise RuntimeError("Calling info is only allowed within a callback")
|
|
443
|
+
match self._data_:
|
|
444
|
+
case ArchetypeSelfData():
|
|
445
|
+
return deref(ctx().blocks.EntityInfo, 0, PlayEntityInfo)
|
|
446
|
+
case ArchetypeReferenceData(index=index):
|
|
447
|
+
return deref(ctx().blocks.EntityInfoArray, index * PlayEntityInfo._size_(), PlayEntityInfo)
|
|
448
|
+
case _:
|
|
449
|
+
raise RuntimeError("Info is only accessible from the entity itself")
|
|
450
|
+
|
|
451
|
+
@property
|
|
452
|
+
def index(self) -> int:
|
|
453
|
+
return self._info.index
|
|
454
|
+
|
|
455
|
+
@property
|
|
456
|
+
def is_waiting(self) -> bool:
|
|
457
|
+
return self._info.state == 0
|
|
458
|
+
|
|
459
|
+
@property
|
|
460
|
+
def is_active(self) -> bool:
|
|
461
|
+
return self._info.state == 1
|
|
462
|
+
|
|
463
|
+
@property
|
|
464
|
+
def is_despawned(self) -> bool:
|
|
465
|
+
return self._info.state == 2
|
|
466
|
+
|
|
467
|
+
@property
|
|
468
|
+
def life(self) -> ArchetypeLife:
|
|
469
|
+
if not ctx():
|
|
470
|
+
raise RuntimeError("Calling life is only allowed within a callback")
|
|
471
|
+
match self._data_:
|
|
472
|
+
case ArchetypeSelfData() | ArchetypeReferenceData():
|
|
473
|
+
return deref(ctx().blocks.ArchetypeLife, self.id() * ArchetypeLife._size_(), ArchetypeLife)
|
|
474
|
+
case _:
|
|
475
|
+
raise RuntimeError("Life is not available in level data")
|
|
476
|
+
|
|
477
|
+
@property
|
|
478
|
+
def result(self) -> PlayEntityInput:
|
|
479
|
+
if not ctx():
|
|
480
|
+
raise RuntimeError("Calling result is only allowed within a callback")
|
|
481
|
+
match self._data_:
|
|
482
|
+
case ArchetypeSelfData():
|
|
483
|
+
return deref(ctx().blocks.EntityInput, 0, PlayEntityInput)
|
|
484
|
+
case _:
|
|
485
|
+
raise RuntimeError("Result is only accessible from the entity itself")
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
class WatchArchetype(BaseArchetype):
|
|
489
|
+
_supported_callbacks_ = WATCH_ARCHETYPE_CALLBACKS
|
|
490
|
+
|
|
491
|
+
def preprocess(self):
|
|
492
|
+
pass
|
|
493
|
+
|
|
494
|
+
def spawn_time(self) -> float:
|
|
495
|
+
pass
|
|
496
|
+
|
|
497
|
+
def despawn_time(self) -> float:
|
|
498
|
+
pass
|
|
499
|
+
|
|
500
|
+
def initialize(self):
|
|
501
|
+
pass
|
|
502
|
+
|
|
503
|
+
def update_sequential(self):
|
|
504
|
+
pass
|
|
505
|
+
|
|
506
|
+
def update_parallel(self):
|
|
507
|
+
pass
|
|
508
|
+
|
|
509
|
+
def terminate(self):
|
|
510
|
+
pass
|
|
511
|
+
|
|
512
|
+
@property
|
|
513
|
+
@meta_fn
|
|
514
|
+
def _info(self):
|
|
515
|
+
if not ctx():
|
|
516
|
+
raise RuntimeError("Calling info is only allowed within a callback")
|
|
517
|
+
match self._data_:
|
|
518
|
+
case ArchetypeSelfData():
|
|
519
|
+
return deref(ctx().blocks.EntityInfo, 0, WatchEntityInfo)
|
|
520
|
+
case ArchetypeReferenceData(index=index):
|
|
521
|
+
return deref(ctx().blocks.EntityInfoArray, index * WatchEntityInfo._size_(), PlayEntityInfo)
|
|
522
|
+
case _:
|
|
523
|
+
raise RuntimeError("Info is only accessible from the entity itself")
|
|
524
|
+
|
|
525
|
+
@property
|
|
526
|
+
def index(self) -> int:
|
|
527
|
+
return self._info.index
|
|
528
|
+
|
|
529
|
+
@property
|
|
530
|
+
def is_active(self) -> bool:
|
|
531
|
+
return self._info.state == 1
|
|
532
|
+
|
|
533
|
+
@property
|
|
534
|
+
def life(self) -> ArchetypeLife:
|
|
535
|
+
if not ctx():
|
|
536
|
+
raise RuntimeError("Calling life is only allowed within a callback")
|
|
537
|
+
match self._data_:
|
|
538
|
+
case ArchetypeSelfData() | ArchetypeReferenceData():
|
|
539
|
+
return deref(ctx().blocks.ArchetypeLife, self.id() * ArchetypeLife._size_(), ArchetypeLife)
|
|
540
|
+
case _:
|
|
541
|
+
raise RuntimeError("Life is not available in level data")
|
|
542
|
+
|
|
543
|
+
@property
|
|
544
|
+
def result(self) -> WatchEntityInput:
|
|
545
|
+
if not ctx():
|
|
546
|
+
raise RuntimeError("Calling result is only allowed within a callback")
|
|
547
|
+
match self._data_:
|
|
548
|
+
case ArchetypeSelfData():
|
|
549
|
+
return deref(ctx().blocks.EntityInput, 0, WatchEntityInput)
|
|
550
|
+
case _:
|
|
551
|
+
raise RuntimeError("Result is only accessible from the entity itself")
|
|
552
|
+
|
|
553
|
+
@property
|
|
554
|
+
def target_time(self) -> float:
|
|
555
|
+
return self.result.target_time
|
|
556
|
+
|
|
557
|
+
@target_time.setter
|
|
558
|
+
def target_time(self, value: float):
|
|
559
|
+
self.result.target_time = value
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
@meta_fn
|
|
563
|
+
def entity_info_at(index: Num) -> PlayEntityInfo | WatchEntityInfo | PreviewEntityInfo:
|
|
564
|
+
if not ctx():
|
|
565
|
+
raise RuntimeError("Calling entity_info_at is only allowed within a callback")
|
|
566
|
+
match ctx().global_state.mode:
|
|
567
|
+
case Mode.Play:
|
|
568
|
+
return deref(ctx().blocks.EntityInfoArray, index * PlayEntityInfo._size_(), PlayEntityInfo)
|
|
569
|
+
case Mode.Watch:
|
|
570
|
+
return deref(ctx().blocks.EntityInfoArray, index * WatchEntityInfo._size_(), WatchEntityInfo)
|
|
571
|
+
case Mode.Preview:
|
|
572
|
+
return deref(ctx().blocks.EntityInfoArray, index * PreviewEntityInfo._size_(), PreviewEntityInfo)
|
|
573
|
+
case _:
|
|
574
|
+
raise RuntimeError(f"Entity info is not available in mode '{ctx().global_state.mode}'")
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
@meta_fn
|
|
578
|
+
def archetype_life_of(archetype: type[BaseArchetype] | BaseArchetype) -> ArchetypeLife:
|
|
579
|
+
archetype = validate_value(archetype)
|
|
580
|
+
archetype = archetype._as_py_()
|
|
581
|
+
if not ctx():
|
|
582
|
+
raise RuntimeError("Calling archetype_life_of is only allowed within a callback")
|
|
583
|
+
match ctx().global_state.mode:
|
|
584
|
+
case Mode.Play | Mode.Watch:
|
|
585
|
+
return deref(ctx().blocks.ArchetypeLife, archetype.id() * ArchetypeLife._size_(), ArchetypeLife)
|
|
586
|
+
case _:
|
|
587
|
+
raise RuntimeError(f"Archetype life is not available in mode '{ctx().global_state.mode}'")
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
class PlayEntityInfo(Record):
|
|
591
|
+
index: int
|
|
592
|
+
archetype_id: int
|
|
593
|
+
state: int
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
class WatchEntityInfo(Record):
|
|
597
|
+
index: int
|
|
598
|
+
archetype_id: int
|
|
599
|
+
state: int
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
class PreviewEntityInfo(Record):
|
|
603
|
+
index: int
|
|
604
|
+
archetype_id: int
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
class ArchetypeLife(Record):
|
|
608
|
+
perfect_increment: Num
|
|
609
|
+
great_increment: Num
|
|
610
|
+
good_increment: Num
|
|
611
|
+
miss_increment: Num
|
|
612
|
+
|
|
613
|
+
def update(
|
|
614
|
+
self,
|
|
615
|
+
perfect_increment: Num | None = None,
|
|
616
|
+
great_increment: Num | None = None,
|
|
617
|
+
good_increment: Num | None = None,
|
|
618
|
+
miss_increment: Num | None = None,
|
|
619
|
+
):
|
|
620
|
+
if perfect_increment is not None:
|
|
621
|
+
self.perfect_increment = perfect_increment
|
|
622
|
+
if great_increment is not None:
|
|
623
|
+
self.great_increment = great_increment
|
|
624
|
+
if good_increment is not None:
|
|
625
|
+
self.good_increment = good_increment
|
|
626
|
+
if miss_increment is not None:
|
|
627
|
+
self.miss_increment = miss_increment
|
|
628
|
+
|
|
629
|
+
|
|
630
|
+
class PlayEntityInput(Record):
|
|
631
|
+
judgment: Judgment
|
|
632
|
+
accuracy: float
|
|
633
|
+
bucket: Bucket
|
|
634
|
+
bucket_value: float
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
class WatchEntityInput(Record):
|
|
638
|
+
target_time: float
|
|
639
|
+
bucket: Bucket
|
|
640
|
+
bucket_value: float
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
class EntityRef[A: BaseArchetype](Record):
|
|
644
|
+
index: int
|
|
645
|
+
|
|
646
|
+
@classmethod
|
|
647
|
+
def archetype(cls) -> type[A]:
|
|
648
|
+
return cls._get_type_arg_(A)
|
|
649
|
+
|
|
650
|
+
def get(self) -> A:
|
|
651
|
+
return self.archetype().at(Num(self.index))
|