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.

Files changed (75) hide show
  1. sonolus/__init__.py +0 -0
  2. sonolus/backend/__init__.py +0 -0
  3. sonolus/backend/allocate.py +51 -0
  4. sonolus/backend/blocks.py +756 -0
  5. sonolus/backend/excepthook.py +37 -0
  6. sonolus/backend/finalize.py +69 -0
  7. sonolus/backend/flow.py +92 -0
  8. sonolus/backend/interpret.py +333 -0
  9. sonolus/backend/ir.py +89 -0
  10. sonolus/backend/mode.py +24 -0
  11. sonolus/backend/node.py +40 -0
  12. sonolus/backend/ops.py +197 -0
  13. sonolus/backend/optimize.py +9 -0
  14. sonolus/backend/passes.py +6 -0
  15. sonolus/backend/place.py +90 -0
  16. sonolus/backend/simplify.py +30 -0
  17. sonolus/backend/utils.py +48 -0
  18. sonolus/backend/visitor.py +880 -0
  19. sonolus/build/__init__.py +0 -0
  20. sonolus/build/cli.py +170 -0
  21. sonolus/build/collection.py +293 -0
  22. sonolus/build/compile.py +90 -0
  23. sonolus/build/defaults.py +32 -0
  24. sonolus/build/engine.py +149 -0
  25. sonolus/build/level.py +23 -0
  26. sonolus/build/node.py +43 -0
  27. sonolus/build/project.py +94 -0
  28. sonolus/py.typed +0 -0
  29. sonolus/script/__init__.py +0 -0
  30. sonolus/script/archetype.py +651 -0
  31. sonolus/script/array.py +241 -0
  32. sonolus/script/bucket.py +192 -0
  33. sonolus/script/callbacks.py +105 -0
  34. sonolus/script/comptime.py +146 -0
  35. sonolus/script/containers.py +247 -0
  36. sonolus/script/debug.py +70 -0
  37. sonolus/script/effect.py +132 -0
  38. sonolus/script/engine.py +101 -0
  39. sonolus/script/globals.py +234 -0
  40. sonolus/script/graphics.py +141 -0
  41. sonolus/script/icon.py +73 -0
  42. sonolus/script/internal/__init__.py +5 -0
  43. sonolus/script/internal/builtin_impls.py +144 -0
  44. sonolus/script/internal/context.py +365 -0
  45. sonolus/script/internal/descriptor.py +17 -0
  46. sonolus/script/internal/error.py +15 -0
  47. sonolus/script/internal/generic.py +197 -0
  48. sonolus/script/internal/impl.py +69 -0
  49. sonolus/script/internal/introspection.py +14 -0
  50. sonolus/script/internal/native.py +38 -0
  51. sonolus/script/internal/value.py +144 -0
  52. sonolus/script/interval.py +98 -0
  53. sonolus/script/iterator.py +211 -0
  54. sonolus/script/level.py +52 -0
  55. sonolus/script/math.py +92 -0
  56. sonolus/script/num.py +382 -0
  57. sonolus/script/options.py +194 -0
  58. sonolus/script/particle.py +158 -0
  59. sonolus/script/pointer.py +30 -0
  60. sonolus/script/project.py +17 -0
  61. sonolus/script/range.py +58 -0
  62. sonolus/script/record.py +293 -0
  63. sonolus/script/runtime.py +526 -0
  64. sonolus/script/sprite.py +332 -0
  65. sonolus/script/text.py +404 -0
  66. sonolus/script/timing.py +42 -0
  67. sonolus/script/transform.py +118 -0
  68. sonolus/script/ui.py +160 -0
  69. sonolus/script/values.py +43 -0
  70. sonolus/script/vec.py +48 -0
  71. sonolus_py-0.1.0.dist-info/METADATA +10 -0
  72. sonolus_py-0.1.0.dist-info/RECORD +75 -0
  73. sonolus_py-0.1.0.dist-info/WHEEL +4 -0
  74. sonolus_py-0.1.0.dist-info/entry_points.txt +2 -0
  75. 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))