koro 1.1.5__py3-none-any.whl → 2.0.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.
- koro/__init__.py +8 -8
- koro/slot/__init__.py +21 -0
- koro/{file → slot}/bin.py +13 -34
- koro/slot/file.py +70 -0
- koro/slot/save.py +137 -0
- koro/slot/xml.py +845 -0
- koro/stage/__init__.py +99 -0
- koro/stage/model.py +288 -0
- koro/stage/part.py +1754 -0
- {koro-1.1.5.dist-info → koro-2.0.0.dist-info}/METADATA +8 -2
- koro-2.0.0.dist-info/RECORD +14 -0
- {koro-1.1.5.dist-info → koro-2.0.0.dist-info}/WHEEL +1 -1
- koro/file/__init__.py +0 -40
- koro/file/dir.py +0 -212
- koro/file/lvl.py +0 -40
- koro/file/zip.py +0 -223
- koro/item/__init__.py +0 -0
- koro/item/group.py +0 -48
- koro/item/level.py +0 -108
- koro/item/save.py +0 -29
- koro-1.1.5.dist-info/RECORD +0 -15
- {koro-1.1.5.dist-info → koro-2.0.0.dist-info}/LICENSE +0 -0
- {koro-1.1.5.dist-info → koro-2.0.0.dist-info}/top_level.txt +0 -0
koro/stage/part.py
ADDED
@@ -0,0 +1,1754 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from abc import ABC, abstractmethod
|
4
|
+
from collections.abc import Iterable, MutableSequence
|
5
|
+
from enum import Enum, unique, Flag
|
6
|
+
from operator import index
|
7
|
+
from sys import maxsize
|
8
|
+
from typing import Any, Final, Iterator, Literal, Self, SupportsIndex, overload
|
9
|
+
|
10
|
+
from .model import DecorationModel, DeviceModel, PartModel
|
11
|
+
|
12
|
+
|
13
|
+
__all__ = [
|
14
|
+
"Ant",
|
15
|
+
"BasePart",
|
16
|
+
"BlinkingTile",
|
17
|
+
"Bumper",
|
18
|
+
"Cannon",
|
19
|
+
"ConveyorBelt",
|
20
|
+
"DashTunnel",
|
21
|
+
"Drawbridge",
|
22
|
+
"Fan",
|
23
|
+
"FixedSpeedDevice",
|
24
|
+
"Gear",
|
25
|
+
"Goal",
|
26
|
+
"GreenCrystal",
|
27
|
+
"KororinCapsule",
|
28
|
+
"Magnet",
|
29
|
+
"MagnetSegment",
|
30
|
+
"MagnifyingGlass",
|
31
|
+
"MelodyTile",
|
32
|
+
"MovingCurve",
|
33
|
+
"MovingTile",
|
34
|
+
"Part",
|
35
|
+
"Press",
|
36
|
+
"ProgressMarker",
|
37
|
+
"Punch",
|
38
|
+
"Scissors",
|
39
|
+
"SeesawBlock",
|
40
|
+
"SizeTunnel",
|
41
|
+
"SlidingTile",
|
42
|
+
"Spring",
|
43
|
+
"Start",
|
44
|
+
"Thorn",
|
45
|
+
"TimedDevice",
|
46
|
+
"ToyTrain",
|
47
|
+
"TrainTrack",
|
48
|
+
"Turntable",
|
49
|
+
"UpsideDownBall",
|
50
|
+
"UpsideDownStageDevice",
|
51
|
+
"Walls",
|
52
|
+
"Warp",
|
53
|
+
]
|
54
|
+
|
55
|
+
|
56
|
+
class BasePart(ABC):
|
57
|
+
"""Base class for all stage elements"""
|
58
|
+
|
59
|
+
__match_args__ = ("x_pos", "y_pos", "z_pos", "x_rot", "y_rot", "z_rot")
|
60
|
+
__slots__ = ("_x_pos", "_x_rot", "_y_pos", "_y_rot", "_z_pos", "_z_rot")
|
61
|
+
|
62
|
+
_x_pos: float
|
63
|
+
_x_rot: float
|
64
|
+
_y_pos: float
|
65
|
+
_y_rot: float
|
66
|
+
_z_pos: float
|
67
|
+
_z_rot: float
|
68
|
+
|
69
|
+
def __init__(
|
70
|
+
self,
|
71
|
+
x_pos: float,
|
72
|
+
y_pos: float,
|
73
|
+
z_pos: float,
|
74
|
+
x_rot: float,
|
75
|
+
y_rot: float,
|
76
|
+
z_rot: float,
|
77
|
+
) -> None:
|
78
|
+
self.x_pos = x_pos
|
79
|
+
self.x_rot = x_rot
|
80
|
+
self.y_pos = y_pos
|
81
|
+
self.y_rot = y_rot
|
82
|
+
self.z_pos = z_pos
|
83
|
+
self.z_rot = z_rot
|
84
|
+
|
85
|
+
@property
|
86
|
+
@abstractmethod
|
87
|
+
def cost(self) -> int:
|
88
|
+
"""The number of kororin points that this part costs to place."""
|
89
|
+
|
90
|
+
def __repr__(self) -> str:
|
91
|
+
return f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r})"
|
92
|
+
|
93
|
+
@property
|
94
|
+
def x_pos(self) -> float:
|
95
|
+
"""Positive is right, negative is left"""
|
96
|
+
return self._x_pos
|
97
|
+
|
98
|
+
@x_pos.setter
|
99
|
+
def x_pos(self, value: float, /) -> None:
|
100
|
+
self._x_pos = value
|
101
|
+
|
102
|
+
@property
|
103
|
+
def x_rot(self) -> float:
|
104
|
+
"""Represented in degrees from 0 to 360
|
105
|
+
Positive turns top face front, negative turns top face back
|
106
|
+
"""
|
107
|
+
return self._x_rot
|
108
|
+
|
109
|
+
@x_rot.setter
|
110
|
+
def x_rot(self, value: float, /) -> None:
|
111
|
+
self._x_rot = value % 360
|
112
|
+
|
113
|
+
@property
|
114
|
+
def y_pos(self) -> float:
|
115
|
+
"""Positive is up, negative is down"""
|
116
|
+
return self._y_pos
|
117
|
+
|
118
|
+
@y_pos.setter
|
119
|
+
def y_pos(self, value: float, /) -> None:
|
120
|
+
self._y_pos = value
|
121
|
+
|
122
|
+
@property
|
123
|
+
def y_rot(self) -> float:
|
124
|
+
"""Represented in degrees from 0 to 360
|
125
|
+
Positive turns front face right, negative turns front face left
|
126
|
+
"""
|
127
|
+
return self._y_rot
|
128
|
+
|
129
|
+
@y_rot.setter
|
130
|
+
def y_rot(self, value: float, /) -> None:
|
131
|
+
self._y_rot = value % 360
|
132
|
+
|
133
|
+
@property
|
134
|
+
def z_pos(self) -> float:
|
135
|
+
"""Positive is front, negative is back"""
|
136
|
+
return self._z_pos
|
137
|
+
|
138
|
+
@z_pos.setter
|
139
|
+
def z_pos(self, value: float, /) -> None:
|
140
|
+
self._z_pos = value
|
141
|
+
|
142
|
+
@property
|
143
|
+
def z_rot(self) -> float:
|
144
|
+
"""Represented in degrees from 0 to 360
|
145
|
+
Positive turns top face left, negative turns top face right
|
146
|
+
"""
|
147
|
+
return self._z_rot
|
148
|
+
|
149
|
+
@z_rot.setter
|
150
|
+
def z_rot(self, value: float, /) -> None:
|
151
|
+
self._z_rot = value % 360
|
152
|
+
|
153
|
+
|
154
|
+
class Part(BasePart):
|
155
|
+
"""(Usually) static model that has no behavior other than being solid."""
|
156
|
+
|
157
|
+
__slots__ = ("_shape",)
|
158
|
+
|
159
|
+
_shape: PartModel | DecorationModel
|
160
|
+
|
161
|
+
def __init__(
|
162
|
+
self,
|
163
|
+
x_pos: float,
|
164
|
+
y_pos: float,
|
165
|
+
z_pos: float,
|
166
|
+
x_rot: float,
|
167
|
+
y_rot: float,
|
168
|
+
z_rot: float,
|
169
|
+
*,
|
170
|
+
shape: PartModel | DecorationModel,
|
171
|
+
) -> None:
|
172
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
173
|
+
self.shape = shape
|
174
|
+
|
175
|
+
@property
|
176
|
+
def cost(self) -> Literal[10, 15, 20]:
|
177
|
+
if isinstance(self.shape, DecorationModel):
|
178
|
+
return 20
|
179
|
+
elif self.shape in frozenset(
|
180
|
+
{
|
181
|
+
PartModel.MagmaTile,
|
182
|
+
PartModel.SlipperyTile,
|
183
|
+
PartModel.StickyTile,
|
184
|
+
PartModel.InvisibleTile,
|
185
|
+
}
|
186
|
+
):
|
187
|
+
return 15
|
188
|
+
else:
|
189
|
+
return 10
|
190
|
+
|
191
|
+
@property
|
192
|
+
def shape(self) -> PartModel | DecorationModel:
|
193
|
+
return self._shape
|
194
|
+
|
195
|
+
@shape.setter
|
196
|
+
def shape(self, value: PartModel | DecorationModel, /) -> None:
|
197
|
+
self._shape = value
|
198
|
+
|
199
|
+
def __repr__(self) -> str:
|
200
|
+
return f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, shape={self.shape!r})"
|
201
|
+
|
202
|
+
|
203
|
+
class Start(BasePart):
|
204
|
+
__slots__ = ()
|
205
|
+
|
206
|
+
@property
|
207
|
+
def cost(self) -> Literal[0]:
|
208
|
+
return 0
|
209
|
+
|
210
|
+
|
211
|
+
class Goal(BasePart):
|
212
|
+
__slots__ = ()
|
213
|
+
|
214
|
+
@property
|
215
|
+
def cost(self) -> Literal[0]:
|
216
|
+
return 0
|
217
|
+
|
218
|
+
|
219
|
+
class ProgressMarker(BasePart):
|
220
|
+
"""Either a crystal (when progress is odd) or a respawn (when progress is even)"""
|
221
|
+
|
222
|
+
__slots__ = ("_progress",)
|
223
|
+
|
224
|
+
_progress: Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
225
|
+
|
226
|
+
def __init__(
|
227
|
+
self,
|
228
|
+
x_pos: float,
|
229
|
+
y_pos: float,
|
230
|
+
z_pos: float,
|
231
|
+
x_rot: float,
|
232
|
+
y_rot: float,
|
233
|
+
z_rot: float,
|
234
|
+
*,
|
235
|
+
progress: Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
|
236
|
+
) -> None:
|
237
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
238
|
+
self.progress = progress
|
239
|
+
|
240
|
+
@property
|
241
|
+
def cost(self) -> Literal[15, 0]:
|
242
|
+
return 15 if self.progress & 1 else 0
|
243
|
+
|
244
|
+
@property
|
245
|
+
def progress(self) -> Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]:
|
246
|
+
"""The number displayed above the object. Controls which crystals are required to enable a respawn."""
|
247
|
+
return self._progress
|
248
|
+
|
249
|
+
@progress.setter
|
250
|
+
def progress(self, value: Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], /):
|
251
|
+
self._progress = value
|
252
|
+
|
253
|
+
def __repr__(self) -> str:
|
254
|
+
return f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, progress={self.progress!r})"
|
255
|
+
|
256
|
+
|
257
|
+
@unique
|
258
|
+
class Speed(Enum):
|
259
|
+
SLOW = 55
|
260
|
+
NORMAL = 39
|
261
|
+
FAST = 23
|
262
|
+
|
263
|
+
|
264
|
+
@unique
|
265
|
+
class Walls(Flag):
|
266
|
+
BACK = 8
|
267
|
+
FRONT = 2
|
268
|
+
LEFT = 4
|
269
|
+
RIGHT = 1
|
270
|
+
|
271
|
+
|
272
|
+
class MovingTile(BasePart):
|
273
|
+
"""A part that moves in a straight line."""
|
274
|
+
|
275
|
+
__slots__ = (
|
276
|
+
"_dest_x",
|
277
|
+
"_dest_y",
|
278
|
+
"_dest_z",
|
279
|
+
"_shape",
|
280
|
+
"_speed",
|
281
|
+
"_switch",
|
282
|
+
"_walls",
|
283
|
+
)
|
284
|
+
|
285
|
+
_dest_x: float
|
286
|
+
_dest_y: float
|
287
|
+
_dest_z: float
|
288
|
+
_shape: Literal[
|
289
|
+
PartModel.Tile10x10,
|
290
|
+
PartModel.Tile20x20,
|
291
|
+
PartModel.TileA30x30,
|
292
|
+
PartModel.TileA30x90,
|
293
|
+
PartModel.Tile90x90,
|
294
|
+
PartModel.HoleB90x90,
|
295
|
+
PartModel.FunnelPipe,
|
296
|
+
PartModel.StraightPipe,
|
297
|
+
]
|
298
|
+
_speed: float
|
299
|
+
_switch: bool
|
300
|
+
_walls: Walls
|
301
|
+
|
302
|
+
def __init__(
|
303
|
+
self,
|
304
|
+
x_pos: float,
|
305
|
+
y_pos: float,
|
306
|
+
z_pos: float,
|
307
|
+
x_rot: float,
|
308
|
+
y_rot: float,
|
309
|
+
z_rot: float,
|
310
|
+
*,
|
311
|
+
dest_x: float,
|
312
|
+
dest_y: float,
|
313
|
+
dest_z: float,
|
314
|
+
shape: Literal[
|
315
|
+
PartModel.Tile10x10,
|
316
|
+
PartModel.Tile20x20,
|
317
|
+
PartModel.TileA30x30,
|
318
|
+
PartModel.TileA30x90,
|
319
|
+
PartModel.Tile90x90,
|
320
|
+
PartModel.HoleB90x90,
|
321
|
+
PartModel.FunnelPipe,
|
322
|
+
PartModel.StraightPipe,
|
323
|
+
],
|
324
|
+
speed: Speed | float = Speed.NORMAL,
|
325
|
+
switch: bool = False,
|
326
|
+
walls: Walls = Walls(0),
|
327
|
+
) -> None:
|
328
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
329
|
+
self.dest_x = dest_x
|
330
|
+
self.dest_y = dest_y
|
331
|
+
self.dest_z = dest_z
|
332
|
+
self._shape = shape
|
333
|
+
self.speed = speed # type: ignore[assignment]
|
334
|
+
self.switch = switch
|
335
|
+
self.walls = walls
|
336
|
+
|
337
|
+
@property
|
338
|
+
def cost(self) -> Literal[20, 70, 35, 40, 50, 65]:
|
339
|
+
match self.shape:
|
340
|
+
case PartModel.Tile10x10:
|
341
|
+
return 20
|
342
|
+
case PartModel.Tile20x20 | PartModel.TileA30x30:
|
343
|
+
return 70
|
344
|
+
case PartModel.TileA30x90:
|
345
|
+
return 35
|
346
|
+
case PartModel.Tile90x90 | PartModel.HoleB90x90:
|
347
|
+
return 40
|
348
|
+
case PartModel.FunnelPipe:
|
349
|
+
return 50
|
350
|
+
case PartModel.StraightPipe:
|
351
|
+
return 65
|
352
|
+
|
353
|
+
@property
|
354
|
+
def dest_x(self) -> float:
|
355
|
+
"""Positive is right, negative is left"""
|
356
|
+
return self._dest_x
|
357
|
+
|
358
|
+
@dest_x.setter
|
359
|
+
def dest_x(self, value: float, /) -> None:
|
360
|
+
self._dest_x = value
|
361
|
+
|
362
|
+
@property
|
363
|
+
def dest_y(self) -> float:
|
364
|
+
return self._dest_y
|
365
|
+
|
366
|
+
@dest_y.setter
|
367
|
+
def dest_y(self, value: float, /) -> None:
|
368
|
+
"""Positive is up, negative is down"""
|
369
|
+
self._dest_y = value
|
370
|
+
|
371
|
+
@property
|
372
|
+
def dest_z(self) -> float:
|
373
|
+
"""Positive is front, negative is back"""
|
374
|
+
return self._dest_z
|
375
|
+
|
376
|
+
@dest_z.setter
|
377
|
+
def dest_z(self, value: float, /) -> None:
|
378
|
+
self._dest_z = value
|
379
|
+
|
380
|
+
def __repr__(self) -> str:
|
381
|
+
output: Final[list[str]] = [
|
382
|
+
f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, dest_x={self.dest_x!r}, dest_y={self.dest_y!r}, dest_z={self.dest_z!r}, shape={self.shape!r}"
|
383
|
+
]
|
384
|
+
if self.speed != 1.0:
|
385
|
+
output.append(f", speed={self.speed!r}")
|
386
|
+
if self.switch:
|
387
|
+
output.append(f", switch={self.switch!r}")
|
388
|
+
if self.walls:
|
389
|
+
output.append(f", walls={self.walls!r}")
|
390
|
+
output.append(")")
|
391
|
+
return "".join(output)
|
392
|
+
|
393
|
+
@property
|
394
|
+
def shape(
|
395
|
+
self,
|
396
|
+
) -> Literal[
|
397
|
+
PartModel.Tile10x10,
|
398
|
+
PartModel.Tile20x20,
|
399
|
+
PartModel.TileA30x30,
|
400
|
+
PartModel.TileA30x90,
|
401
|
+
PartModel.Tile90x90,
|
402
|
+
PartModel.HoleB90x90,
|
403
|
+
PartModel.FunnelPipe,
|
404
|
+
PartModel.StraightPipe,
|
405
|
+
]:
|
406
|
+
"""The appearance of this moving tile."""
|
407
|
+
return self._shape
|
408
|
+
|
409
|
+
@shape.setter
|
410
|
+
def shape(
|
411
|
+
self,
|
412
|
+
value: Literal[
|
413
|
+
PartModel.Tile10x10,
|
414
|
+
PartModel.Tile20x20,
|
415
|
+
PartModel.TileA30x30,
|
416
|
+
PartModel.TileA30x90,
|
417
|
+
PartModel.Tile90x90,
|
418
|
+
PartModel.HoleB90x90,
|
419
|
+
PartModel.FunnelPipe,
|
420
|
+
PartModel.StraightPipe,
|
421
|
+
],
|
422
|
+
/,
|
423
|
+
) -> None:
|
424
|
+
if (
|
425
|
+
value in frozenset({PartModel.FunnelPipe, PartModel.StraightPipe})
|
426
|
+
and self.switch
|
427
|
+
):
|
428
|
+
raise ValueError("Moving pipes cannot be switches")
|
429
|
+
elif (
|
430
|
+
value not in frozenset({PartModel.Tile20x20, PartModel.TileA30x30})
|
431
|
+
and self.walls
|
432
|
+
):
|
433
|
+
raise ValueError("Invalid shape for wall attachment")
|
434
|
+
else:
|
435
|
+
self._shape = value
|
436
|
+
|
437
|
+
@property
|
438
|
+
def speed(self) -> float:
|
439
|
+
return self._speed
|
440
|
+
|
441
|
+
@speed.setter
|
442
|
+
def speed(self, value: Speed | float, /) -> None:
|
443
|
+
match value:
|
444
|
+
case Speed.SLOW:
|
445
|
+
self.speed = 0.5
|
446
|
+
case Speed.NORMAL:
|
447
|
+
self.speed = 1.0
|
448
|
+
case Speed.FAST:
|
449
|
+
self.speed = 1.5
|
450
|
+
case _:
|
451
|
+
self._speed = value
|
452
|
+
|
453
|
+
@property
|
454
|
+
def switch(self) -> bool:
|
455
|
+
"""Whether this moving tile must be touched in order to move."""
|
456
|
+
return self._switch
|
457
|
+
|
458
|
+
@switch.setter
|
459
|
+
def switch(self, value: bool, /) -> None:
|
460
|
+
if value and self.shape in frozenset(
|
461
|
+
{PartModel.FunnelPipe, PartModel.StraightPipe}
|
462
|
+
):
|
463
|
+
raise ValueError("Moving pipes cannot be switches")
|
464
|
+
else:
|
465
|
+
self._switch = value
|
466
|
+
|
467
|
+
@property
|
468
|
+
def walls(self) -> Walls:
|
469
|
+
return self._walls
|
470
|
+
|
471
|
+
@walls.setter
|
472
|
+
def walls(self, value: Walls, /) -> None:
|
473
|
+
if not value or self.shape in frozenset(
|
474
|
+
{PartModel.Tile20x20, PartModel.TileA30x30}
|
475
|
+
):
|
476
|
+
self._walls = value
|
477
|
+
else:
|
478
|
+
raise ValueError("Invalid shape for wall attachment")
|
479
|
+
|
480
|
+
|
481
|
+
class FixedSpeedDevice(BasePart, ABC):
|
482
|
+
"""Device template for devices with speeds that can be set to Slow, Normal, or Fast"""
|
483
|
+
|
484
|
+
__slots__ = ("_speed",)
|
485
|
+
|
486
|
+
_speed: Speed
|
487
|
+
|
488
|
+
def __init__(
|
489
|
+
self,
|
490
|
+
x_pos: float,
|
491
|
+
y_pos: float,
|
492
|
+
z_pos: float,
|
493
|
+
x_rot: float,
|
494
|
+
y_rot: float,
|
495
|
+
z_rot: float,
|
496
|
+
*,
|
497
|
+
speed: Speed = Speed.NORMAL,
|
498
|
+
) -> None:
|
499
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
500
|
+
self.speed = speed
|
501
|
+
|
502
|
+
def __repr__(self) -> str:
|
503
|
+
return (
|
504
|
+
f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, speed={self.speed!r})"
|
505
|
+
if self.speed != Speed.NORMAL
|
506
|
+
else f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r})"
|
507
|
+
)
|
508
|
+
|
509
|
+
@property
|
510
|
+
def speed(self) -> Speed:
|
511
|
+
return self._speed
|
512
|
+
|
513
|
+
@speed.setter
|
514
|
+
def speed(self, value: Speed, /) -> None:
|
515
|
+
self._speed = value
|
516
|
+
|
517
|
+
|
518
|
+
class MovingCurve(FixedSpeedDevice):
|
519
|
+
__slots__ = ("_shape",)
|
520
|
+
|
521
|
+
_shape: Literal[PartModel.CurveS, PartModel.CurveM, PartModel.CurveL]
|
522
|
+
|
523
|
+
def __init__(
|
524
|
+
self,
|
525
|
+
x_pos: float,
|
526
|
+
y_pos: float,
|
527
|
+
z_pos: float,
|
528
|
+
x_rot: float,
|
529
|
+
y_rot: float,
|
530
|
+
z_rot: float,
|
531
|
+
*,
|
532
|
+
shape: Literal[PartModel.CurveS, PartModel.CurveM, PartModel.CurveL],
|
533
|
+
speed: Speed = Speed.NORMAL,
|
534
|
+
) -> None:
|
535
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot, speed=speed)
|
536
|
+
self.shape = shape
|
537
|
+
|
538
|
+
@property
|
539
|
+
def cost(self) -> Literal[25]:
|
540
|
+
return 25
|
541
|
+
|
542
|
+
def __repr__(self) -> str:
|
543
|
+
return (
|
544
|
+
f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, shape={self.shape!r}, speed={self.speed!r})"
|
545
|
+
if self.speed != Speed.NORMAL
|
546
|
+
else f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, shape={self.shape!r})"
|
547
|
+
)
|
548
|
+
|
549
|
+
@property
|
550
|
+
def shape(self) -> Literal[PartModel.CurveS, PartModel.CurveM, PartModel.CurveL]:
|
551
|
+
return self._shape
|
552
|
+
|
553
|
+
@shape.setter
|
554
|
+
def shape(
|
555
|
+
self, value: Literal[PartModel.CurveS, PartModel.CurveM, PartModel.CurveL], /
|
556
|
+
) -> None:
|
557
|
+
self._shape = value
|
558
|
+
|
559
|
+
|
560
|
+
class SlidingTile(BasePart):
|
561
|
+
__slots__ = ()
|
562
|
+
|
563
|
+
@property
|
564
|
+
def cost(self) -> Literal[100]:
|
565
|
+
return 100
|
566
|
+
|
567
|
+
|
568
|
+
class ConveyorBelt(BasePart):
|
569
|
+
__slots__ = ("_reversing",)
|
570
|
+
|
571
|
+
_reversing: bool
|
572
|
+
|
573
|
+
def __init__(
|
574
|
+
self,
|
575
|
+
x_pos: float,
|
576
|
+
y_pos: float,
|
577
|
+
z_pos: float,
|
578
|
+
x_rot: float,
|
579
|
+
y_rot: float,
|
580
|
+
z_rot: float,
|
581
|
+
*,
|
582
|
+
reversing: bool = False,
|
583
|
+
) -> None:
|
584
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
585
|
+
self.reversing = reversing
|
586
|
+
|
587
|
+
@property
|
588
|
+
def cost(self) -> Literal[60]:
|
589
|
+
return 60
|
590
|
+
|
591
|
+
def __repr__(self) -> str:
|
592
|
+
return (
|
593
|
+
f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, reversing={self.reversing!r})"
|
594
|
+
if self.reversing
|
595
|
+
else super().__repr__()
|
596
|
+
)
|
597
|
+
|
598
|
+
@property
|
599
|
+
def reversing(self) -> bool:
|
600
|
+
return self._reversing
|
601
|
+
|
602
|
+
@reversing.setter
|
603
|
+
def reversing(self, value: bool, /) -> None:
|
604
|
+
self._reversing = value
|
605
|
+
|
606
|
+
|
607
|
+
class MagnetSegment(BasePart):
|
608
|
+
__slots__ = ("_shape",)
|
609
|
+
|
610
|
+
_shape: Literal[
|
611
|
+
DeviceModel.EndMagnet,
|
612
|
+
DeviceModel.StraightMagnet,
|
613
|
+
DeviceModel.CurveMagnetL,
|
614
|
+
DeviceModel.CurveMagnetS,
|
615
|
+
]
|
616
|
+
|
617
|
+
def __init__(
|
618
|
+
self,
|
619
|
+
x_pos: float,
|
620
|
+
y_pos: float,
|
621
|
+
z_pos: float,
|
622
|
+
x_rot: float,
|
623
|
+
y_rot: float,
|
624
|
+
z_rot: float,
|
625
|
+
*,
|
626
|
+
shape: Literal[
|
627
|
+
DeviceModel.EndMagnet,
|
628
|
+
DeviceModel.StraightMagnet,
|
629
|
+
DeviceModel.CurveMagnetL,
|
630
|
+
DeviceModel.CurveMagnetS,
|
631
|
+
],
|
632
|
+
) -> None:
|
633
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
634
|
+
self.shape = shape
|
635
|
+
|
636
|
+
@property
|
637
|
+
def cost(self) -> Literal[15]:
|
638
|
+
return 15
|
639
|
+
|
640
|
+
def __repr__(self) -> str:
|
641
|
+
return f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, shape={self.shape!r})"
|
642
|
+
|
643
|
+
@property
|
644
|
+
def shape(self) -> Literal[
|
645
|
+
DeviceModel.EndMagnet,
|
646
|
+
DeviceModel.StraightMagnet,
|
647
|
+
DeviceModel.CurveMagnetL,
|
648
|
+
DeviceModel.CurveMagnetS,
|
649
|
+
]:
|
650
|
+
return self._shape
|
651
|
+
|
652
|
+
@shape.setter
|
653
|
+
def shape(
|
654
|
+
self,
|
655
|
+
value: Literal[
|
656
|
+
DeviceModel.EndMagnet,
|
657
|
+
DeviceModel.StraightMagnet,
|
658
|
+
DeviceModel.CurveMagnetL,
|
659
|
+
DeviceModel.CurveMagnetS,
|
660
|
+
],
|
661
|
+
/,
|
662
|
+
) -> None:
|
663
|
+
self._shape = value
|
664
|
+
|
665
|
+
|
666
|
+
class Magnet(BasePart, MutableSequence[MagnetSegment]):
|
667
|
+
__slots__ = ("_segments",)
|
668
|
+
|
669
|
+
_segments: list[MagnetSegment]
|
670
|
+
|
671
|
+
def __init__(self, iterable: Iterable[MagnetSegment] = (), /) -> None:
|
672
|
+
self._segments = list(iterable)
|
673
|
+
|
674
|
+
def append(self, value: MagnetSegment) -> None:
|
675
|
+
return self._segments.append(value)
|
676
|
+
|
677
|
+
def clear(self) -> None:
|
678
|
+
self._segments.clear()
|
679
|
+
|
680
|
+
def __contains__(self, value: Any, /) -> bool:
|
681
|
+
return value in self._segments
|
682
|
+
|
683
|
+
@property
|
684
|
+
def cost(self) -> int:
|
685
|
+
return sum(segment.cost for segment in self)
|
686
|
+
|
687
|
+
def count(self, value: Any) -> int:
|
688
|
+
return self._segments.count(value)
|
689
|
+
|
690
|
+
def __delitem__(self, index: SupportsIndex | slice, /) -> None:
|
691
|
+
del self._segments[index]
|
692
|
+
|
693
|
+
def extend(self, iterable: Iterable[MagnetSegment], /) -> None:
|
694
|
+
self._segments.extend(iterable)
|
695
|
+
|
696
|
+
@overload
|
697
|
+
def __getitem__(self, index: SupportsIndex, /) -> MagnetSegment:
|
698
|
+
pass
|
699
|
+
|
700
|
+
@overload
|
701
|
+
def __getitem__(self, index: slice, /) -> MutableSequence[MagnetSegment]:
|
702
|
+
pass
|
703
|
+
|
704
|
+
def __getitem__(
|
705
|
+
self, index: SupportsIndex | slice, /
|
706
|
+
) -> MagnetSegment | MutableSequence[MagnetSegment]:
|
707
|
+
return self._segments[index]
|
708
|
+
|
709
|
+
def __iadd__(self, value: Iterable[MagnetSegment], /) -> Self:
|
710
|
+
self._segments += value
|
711
|
+
return self
|
712
|
+
|
713
|
+
def index(
|
714
|
+
self, value: Any, start: SupportsIndex = 0, stop: SupportsIndex = maxsize, /
|
715
|
+
) -> int:
|
716
|
+
return self._segments.index(value, start, stop)
|
717
|
+
|
718
|
+
def insert(self, index: SupportsIndex, value: MagnetSegment, /) -> None:
|
719
|
+
return self._segments.insert(index, value)
|
720
|
+
|
721
|
+
def __iter__(self) -> Iterator[MagnetSegment]:
|
722
|
+
return iter(self._segments)
|
723
|
+
|
724
|
+
def __len__(self) -> int:
|
725
|
+
return len(self._segments)
|
726
|
+
|
727
|
+
def pop(self, index: SupportsIndex = -1, /) -> MagnetSegment:
|
728
|
+
return self._segments.pop(index)
|
729
|
+
|
730
|
+
def remove(self, value: MagnetSegment, /) -> None:
|
731
|
+
self._segments.remove(value)
|
732
|
+
|
733
|
+
def __repr__(self) -> str:
|
734
|
+
return f"{type(self).__name__}({self._segments!r})"
|
735
|
+
|
736
|
+
def reverse(self) -> None:
|
737
|
+
self._segments.reverse()
|
738
|
+
|
739
|
+
def __reversed__(self) -> Iterator[MagnetSegment]:
|
740
|
+
return reversed(self._segments)
|
741
|
+
|
742
|
+
@overload
|
743
|
+
def __setitem__(self, index: SupportsIndex, value: MagnetSegment, /) -> None:
|
744
|
+
pass
|
745
|
+
|
746
|
+
@overload
|
747
|
+
def __setitem__(self, slice: slice, value: Iterable[MagnetSegment], /) -> None:
|
748
|
+
pass
|
749
|
+
|
750
|
+
def __setitem__(
|
751
|
+
self,
|
752
|
+
index: SupportsIndex | slice,
|
753
|
+
value: MagnetSegment | Iterable[MagnetSegment],
|
754
|
+
/,
|
755
|
+
) -> None:
|
756
|
+
self._segments[index] = value # type: ignore
|
757
|
+
|
758
|
+
@property
|
759
|
+
def x_pos(self) -> float:
|
760
|
+
"""Positive is right, negative is left"""
|
761
|
+
return self[-1].x_pos
|
762
|
+
|
763
|
+
@x_pos.setter
|
764
|
+
def x_pos(self, value: float, /) -> None:
|
765
|
+
offset: float = value - self.x_pos
|
766
|
+
for segment in self:
|
767
|
+
segment.x_pos += offset
|
768
|
+
|
769
|
+
@property
|
770
|
+
def x_rot(self) -> float:
|
771
|
+
"""Represented in degrees from 0 to 360
|
772
|
+
Positive turns top face front, negative turns top face back
|
773
|
+
"""
|
774
|
+
return self[-1].x_rot
|
775
|
+
|
776
|
+
@x_rot.setter
|
777
|
+
def x_rot(self, value: float, /) -> None:
|
778
|
+
"""Positions of segments are not updated."""
|
779
|
+
offset: float = value - self.x_rot
|
780
|
+
for segment in self:
|
781
|
+
segment.x_rot += offset
|
782
|
+
|
783
|
+
@property
|
784
|
+
def y_pos(self) -> float:
|
785
|
+
"""Positive is up, negative is down"""
|
786
|
+
return self[-1].y_pos
|
787
|
+
|
788
|
+
@y_pos.setter
|
789
|
+
def y_pos(self, value: float, /) -> None:
|
790
|
+
offset: float = value - self.y_pos
|
791
|
+
for segment in self:
|
792
|
+
segment.y_pos += offset
|
793
|
+
|
794
|
+
@property
|
795
|
+
def y_rot(self) -> float:
|
796
|
+
"""Represented in degrees from 0 to 360
|
797
|
+
Positive turns front face right, negative turns front face left
|
798
|
+
"""
|
799
|
+
return self[-1].y_rot
|
800
|
+
|
801
|
+
@y_rot.setter
|
802
|
+
def y_rot(self, value: float, /) -> None:
|
803
|
+
"""Positions of segments are not updated."""
|
804
|
+
offset: float = value - self.y_rot
|
805
|
+
for segment in self:
|
806
|
+
segment.y_rot += offset
|
807
|
+
|
808
|
+
@property
|
809
|
+
def z_pos(self) -> float:
|
810
|
+
"""Positive is front, negative is back"""
|
811
|
+
return self[-1].z_pos
|
812
|
+
|
813
|
+
@z_pos.setter
|
814
|
+
def z_pos(self, value: float, /) -> None:
|
815
|
+
offset: float = value - self.z_pos
|
816
|
+
for segment in self:
|
817
|
+
segment.z_pos += offset
|
818
|
+
|
819
|
+
@property
|
820
|
+
def z_rot(self) -> float:
|
821
|
+
"""Represented in degrees from 0 to 360
|
822
|
+
Positive turns top face left, negative turns top face right
|
823
|
+
"""
|
824
|
+
return self[-1].z_rot
|
825
|
+
|
826
|
+
@z_rot.setter
|
827
|
+
def z_rot(self, value: float, /) -> None:
|
828
|
+
"""Positions of segments are not updated."""
|
829
|
+
offset: float = value - self.z_rot
|
830
|
+
for segment in self:
|
831
|
+
segment.z_rot += offset
|
832
|
+
|
833
|
+
|
834
|
+
class DashTunnel(BasePart):
|
835
|
+
__slots__ = ("_shape",)
|
836
|
+
|
837
|
+
_shape: Literal[DeviceModel.DashTunnelA, DeviceModel.DashTunnelB]
|
838
|
+
|
839
|
+
def __init__(
|
840
|
+
self,
|
841
|
+
x_pos: float,
|
842
|
+
y_pos: float,
|
843
|
+
z_pos: float,
|
844
|
+
x_rot: float,
|
845
|
+
y_rot: float,
|
846
|
+
z_rot: float,
|
847
|
+
*,
|
848
|
+
shape: Literal[DeviceModel.DashTunnelA, DeviceModel.DashTunnelB],
|
849
|
+
) -> None:
|
850
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
851
|
+
self.shape = shape
|
852
|
+
|
853
|
+
@property
|
854
|
+
def cost(self) -> Literal[35, 100]:
|
855
|
+
match self.shape:
|
856
|
+
case DeviceModel.DashTunnelA:
|
857
|
+
return 35
|
858
|
+
case DeviceModel.DashTunnelB:
|
859
|
+
return 100
|
860
|
+
|
861
|
+
def __repr__(self) -> str:
|
862
|
+
return f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, shape={self.shape!r})"
|
863
|
+
|
864
|
+
@property
|
865
|
+
def shape(self) -> Literal[DeviceModel.DashTunnelA, DeviceModel.DashTunnelB]:
|
866
|
+
return self._shape
|
867
|
+
|
868
|
+
@shape.setter
|
869
|
+
def shape(
|
870
|
+
self, value: Literal[DeviceModel.DashTunnelA, DeviceModel.DashTunnelB], /
|
871
|
+
) -> None:
|
872
|
+
self._shape = value
|
873
|
+
|
874
|
+
|
875
|
+
class SeesawBlock(BasePart):
|
876
|
+
__slots__ = ("_auto", "_shape")
|
877
|
+
|
878
|
+
_auto: bool
|
879
|
+
_shape: Literal[DeviceModel.SeesawLBlock, DeviceModel.SeesawIBlock]
|
880
|
+
|
881
|
+
def __init__(
|
882
|
+
self,
|
883
|
+
x_pos: float,
|
884
|
+
y_pos: float,
|
885
|
+
z_pos: float,
|
886
|
+
x_rot: float,
|
887
|
+
y_rot: float,
|
888
|
+
z_rot: float,
|
889
|
+
*,
|
890
|
+
auto: bool = False,
|
891
|
+
shape: Literal[DeviceModel.SeesawLBlock, DeviceModel.SeesawIBlock],
|
892
|
+
) -> None:
|
893
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
894
|
+
self.auto = auto
|
895
|
+
self.shape = shape
|
896
|
+
|
897
|
+
@property
|
898
|
+
def auto(self) -> bool:
|
899
|
+
return self._auto
|
900
|
+
|
901
|
+
@auto.setter
|
902
|
+
def auto(self, value: bool, /) -> None:
|
903
|
+
self._auto = value
|
904
|
+
|
905
|
+
@property
|
906
|
+
def cost(self) -> Literal[100]:
|
907
|
+
return 100
|
908
|
+
|
909
|
+
def __repr__(self) -> str:
|
910
|
+
return (
|
911
|
+
f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, auto={self.auto!r}, shape={self.shape!r})"
|
912
|
+
if self.auto
|
913
|
+
else f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, shape={self.shape!r})"
|
914
|
+
)
|
915
|
+
|
916
|
+
@property
|
917
|
+
def shape(self) -> Literal[DeviceModel.SeesawLBlock, DeviceModel.SeesawIBlock]:
|
918
|
+
return self._shape
|
919
|
+
|
920
|
+
@shape.setter
|
921
|
+
def shape(
|
922
|
+
self, value: Literal[DeviceModel.SeesawLBlock, DeviceModel.SeesawIBlock], /
|
923
|
+
) -> None:
|
924
|
+
self._shape = value
|
925
|
+
|
926
|
+
|
927
|
+
class Cannon(BasePart):
|
928
|
+
__slots__ = ()
|
929
|
+
|
930
|
+
@property
|
931
|
+
def cost(self) -> Literal[30]:
|
932
|
+
return 30
|
933
|
+
|
934
|
+
|
935
|
+
class Drawbridge(BasePart):
|
936
|
+
__slots__ = ()
|
937
|
+
|
938
|
+
@property
|
939
|
+
def cost(self) -> Literal[50]:
|
940
|
+
return 50
|
941
|
+
|
942
|
+
|
943
|
+
class Turntable(FixedSpeedDevice):
|
944
|
+
__slots__ = ()
|
945
|
+
|
946
|
+
@property
|
947
|
+
def cost(self) -> Literal[50]:
|
948
|
+
return 50
|
949
|
+
|
950
|
+
|
951
|
+
class Bumper(BasePart):
|
952
|
+
__slots__ = ("_powerful",)
|
953
|
+
|
954
|
+
_powerful: bool
|
955
|
+
|
956
|
+
def __init__(
|
957
|
+
self,
|
958
|
+
x_pos: float,
|
959
|
+
y_pos: float,
|
960
|
+
z_pos: float,
|
961
|
+
x_rot: float,
|
962
|
+
y_rot: float,
|
963
|
+
z_rot: float,
|
964
|
+
*,
|
965
|
+
powerful: bool = False,
|
966
|
+
) -> None:
|
967
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
968
|
+
self.powerful = powerful
|
969
|
+
|
970
|
+
@property
|
971
|
+
def cost(self) -> Literal[20]:
|
972
|
+
return 20
|
973
|
+
|
974
|
+
@property
|
975
|
+
def powerful(self) -> bool:
|
976
|
+
return self._powerful
|
977
|
+
|
978
|
+
@powerful.setter
|
979
|
+
def powerful(self, value: bool, /) -> None:
|
980
|
+
self._powerful = value
|
981
|
+
|
982
|
+
def __repr__(self) -> str:
|
983
|
+
return (
|
984
|
+
f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, powerful={self.powerful!r})"
|
985
|
+
if self.powerful
|
986
|
+
else f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r})"
|
987
|
+
)
|
988
|
+
|
989
|
+
|
990
|
+
class Thorn(BasePart):
|
991
|
+
__slots__ = ()
|
992
|
+
|
993
|
+
@property
|
994
|
+
def cost(self) -> Literal[35]:
|
995
|
+
return 35
|
996
|
+
|
997
|
+
|
998
|
+
class Gear(FixedSpeedDevice):
|
999
|
+
__slots__ = ()
|
1000
|
+
|
1001
|
+
@property
|
1002
|
+
def cost(self) -> Literal[50]:
|
1003
|
+
return 50
|
1004
|
+
|
1005
|
+
|
1006
|
+
class Fan(BasePart):
|
1007
|
+
__slots__ = ("_wind_pattern",)
|
1008
|
+
|
1009
|
+
_wind_pattern: Literal[
|
1010
|
+
DeviceModel.Fan, DeviceModel.PowerfulFan, DeviceModel.TimerFan
|
1011
|
+
]
|
1012
|
+
|
1013
|
+
def __init__(
|
1014
|
+
self,
|
1015
|
+
x_pos: float,
|
1016
|
+
y_pos: float,
|
1017
|
+
z_pos: float,
|
1018
|
+
x_rot: float,
|
1019
|
+
y_rot: float,
|
1020
|
+
z_rot: float,
|
1021
|
+
*,
|
1022
|
+
wind_pattern: Literal[
|
1023
|
+
DeviceModel.Fan, DeviceModel.PowerfulFan, DeviceModel.TimerFan
|
1024
|
+
] = DeviceModel.Fan,
|
1025
|
+
) -> None:
|
1026
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
1027
|
+
self.wind_pattern = wind_pattern
|
1028
|
+
|
1029
|
+
@property
|
1030
|
+
def cost(self) -> Literal[50]:
|
1031
|
+
return 50
|
1032
|
+
|
1033
|
+
def __repr__(self) -> str:
|
1034
|
+
return (
|
1035
|
+
f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, wind_pattern={self.wind_pattern!r})"
|
1036
|
+
if self.wind_pattern != DeviceModel.Fan
|
1037
|
+
else f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r})"
|
1038
|
+
)
|
1039
|
+
|
1040
|
+
@property
|
1041
|
+
def wind_pattern(
|
1042
|
+
self,
|
1043
|
+
) -> Literal[DeviceModel.Fan, DeviceModel.PowerfulFan, DeviceModel.TimerFan]:
|
1044
|
+
return self._wind_pattern
|
1045
|
+
|
1046
|
+
@wind_pattern.setter
|
1047
|
+
def wind_pattern(
|
1048
|
+
self,
|
1049
|
+
value: Literal[DeviceModel.Fan, DeviceModel.PowerfulFan, DeviceModel.TimerFan],
|
1050
|
+
/,
|
1051
|
+
) -> None:
|
1052
|
+
self._wind_pattern = value
|
1053
|
+
|
1054
|
+
|
1055
|
+
class Spring(BasePart):
|
1056
|
+
__slots__ = ()
|
1057
|
+
|
1058
|
+
@property
|
1059
|
+
def cost(self) -> Literal[25]:
|
1060
|
+
return 25
|
1061
|
+
|
1062
|
+
|
1063
|
+
@unique
|
1064
|
+
class MovementTiming(Enum):
|
1065
|
+
A = 23
|
1066
|
+
B = 39
|
1067
|
+
C = 55
|
1068
|
+
|
1069
|
+
|
1070
|
+
class TimedDevice(BasePart, ABC):
|
1071
|
+
"""Device Template for devices which can have one of three timings for their movements"""
|
1072
|
+
|
1073
|
+
__slots__ = ("_timing",)
|
1074
|
+
|
1075
|
+
_timing: MovementTiming
|
1076
|
+
|
1077
|
+
def __init__(
|
1078
|
+
self,
|
1079
|
+
x_pos: float,
|
1080
|
+
y_pos: float,
|
1081
|
+
z_pos: float,
|
1082
|
+
x_rot: float,
|
1083
|
+
y_rot: float,
|
1084
|
+
z_rot: float,
|
1085
|
+
*,
|
1086
|
+
timing: MovementTiming,
|
1087
|
+
) -> None:
|
1088
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
1089
|
+
self.timing = timing
|
1090
|
+
|
1091
|
+
def __repr__(self) -> str:
|
1092
|
+
return f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, timing={self.timing!r})"
|
1093
|
+
|
1094
|
+
@property
|
1095
|
+
def timing(self) -> MovementTiming:
|
1096
|
+
return self._timing
|
1097
|
+
|
1098
|
+
@timing.setter
|
1099
|
+
def timing(self, value: MovementTiming, /) -> None:
|
1100
|
+
self._timing = value
|
1101
|
+
|
1102
|
+
|
1103
|
+
class Punch(TimedDevice):
|
1104
|
+
__slots__ = ()
|
1105
|
+
|
1106
|
+
@property
|
1107
|
+
def cost(self) -> Literal[30]:
|
1108
|
+
return 30
|
1109
|
+
|
1110
|
+
|
1111
|
+
class Press(TimedDevice):
|
1112
|
+
__slots__ = ()
|
1113
|
+
|
1114
|
+
@property
|
1115
|
+
def cost(self) -> Literal[50]:
|
1116
|
+
return 50
|
1117
|
+
|
1118
|
+
|
1119
|
+
class Scissors(TimedDevice):
|
1120
|
+
__slots__ = ()
|
1121
|
+
|
1122
|
+
@property
|
1123
|
+
def cost(self) -> Literal[70]:
|
1124
|
+
return 70
|
1125
|
+
|
1126
|
+
|
1127
|
+
class MagnifyingGlass(BasePart):
|
1128
|
+
__slots__ = ()
|
1129
|
+
|
1130
|
+
@property
|
1131
|
+
def cost(self) -> Literal[200]:
|
1132
|
+
return 200
|
1133
|
+
|
1134
|
+
|
1135
|
+
class UpsideDownStageDevice(BasePart):
|
1136
|
+
__slots__ = ()
|
1137
|
+
|
1138
|
+
@property
|
1139
|
+
def cost(self) -> Literal[50]:
|
1140
|
+
return 50
|
1141
|
+
|
1142
|
+
|
1143
|
+
class UpsideDownBall(BasePart):
|
1144
|
+
__slots__ = ()
|
1145
|
+
|
1146
|
+
@property
|
1147
|
+
def cost(self) -> Literal[25]:
|
1148
|
+
return 25
|
1149
|
+
|
1150
|
+
|
1151
|
+
class SizeTunnel(BasePart):
|
1152
|
+
__slots__ = ("_size",)
|
1153
|
+
|
1154
|
+
_size: Literal[DeviceModel.SmallTunnel, DeviceModel.BigTunnel]
|
1155
|
+
|
1156
|
+
def __init__(
|
1157
|
+
self,
|
1158
|
+
x_pos: float,
|
1159
|
+
y_pos: float,
|
1160
|
+
z_pos: float,
|
1161
|
+
x_rot: float,
|
1162
|
+
y_rot: float,
|
1163
|
+
z_rot: float,
|
1164
|
+
*,
|
1165
|
+
size: Literal[DeviceModel.SmallTunnel, DeviceModel.BigTunnel],
|
1166
|
+
) -> None:
|
1167
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
1168
|
+
self.size = size
|
1169
|
+
|
1170
|
+
@property
|
1171
|
+
def cost(self) -> Literal[50]:
|
1172
|
+
return 50
|
1173
|
+
|
1174
|
+
def __repr__(self) -> str:
|
1175
|
+
return f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, size={self.size!r})"
|
1176
|
+
|
1177
|
+
@property
|
1178
|
+
def size(self) -> Literal[DeviceModel.SmallTunnel, DeviceModel.BigTunnel]:
|
1179
|
+
return self._size
|
1180
|
+
|
1181
|
+
@size.setter
|
1182
|
+
def size(
|
1183
|
+
self, value: Literal[DeviceModel.SmallTunnel, DeviceModel.BigTunnel], /
|
1184
|
+
) -> None:
|
1185
|
+
self._size = value
|
1186
|
+
|
1187
|
+
|
1188
|
+
class TrainTrack(BasePart):
|
1189
|
+
__slots__ = ("_shape",)
|
1190
|
+
|
1191
|
+
_shape: Literal[
|
1192
|
+
DeviceModel.EndTracks,
|
1193
|
+
DeviceModel.LeftTracks,
|
1194
|
+
DeviceModel.RightTracks,
|
1195
|
+
DeviceModel.StraightTracks,
|
1196
|
+
]
|
1197
|
+
|
1198
|
+
def __init__(
|
1199
|
+
self,
|
1200
|
+
x_pos: float,
|
1201
|
+
y_pos: float,
|
1202
|
+
z_pos: float,
|
1203
|
+
x_rot: float,
|
1204
|
+
y_rot: float,
|
1205
|
+
z_rot: float,
|
1206
|
+
*,
|
1207
|
+
shape: Literal[
|
1208
|
+
DeviceModel.EndTracks,
|
1209
|
+
DeviceModel.LeftTracks,
|
1210
|
+
DeviceModel.RightTracks,
|
1211
|
+
DeviceModel.StraightTracks,
|
1212
|
+
],
|
1213
|
+
) -> None:
|
1214
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
1215
|
+
self.shape = shape
|
1216
|
+
|
1217
|
+
@property
|
1218
|
+
def cost(self) -> Literal[0, 20]:
|
1219
|
+
return 0 if self.shape is DeviceModel.EndTracks else 20
|
1220
|
+
|
1221
|
+
def __repr__(self) -> str:
|
1222
|
+
return f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, shape={self.shape!r})"
|
1223
|
+
|
1224
|
+
@property
|
1225
|
+
def shape(
|
1226
|
+
self,
|
1227
|
+
) -> Literal[
|
1228
|
+
DeviceModel.EndTracks,
|
1229
|
+
DeviceModel.LeftTracks,
|
1230
|
+
DeviceModel.RightTracks,
|
1231
|
+
DeviceModel.StraightTracks,
|
1232
|
+
]:
|
1233
|
+
return self._shape
|
1234
|
+
|
1235
|
+
@shape.setter
|
1236
|
+
def shape(
|
1237
|
+
self,
|
1238
|
+
value: Literal[
|
1239
|
+
DeviceModel.EndTracks,
|
1240
|
+
DeviceModel.LeftTracks,
|
1241
|
+
DeviceModel.RightTracks,
|
1242
|
+
DeviceModel.StraightTracks,
|
1243
|
+
],
|
1244
|
+
/,
|
1245
|
+
) -> None:
|
1246
|
+
self._shape = value
|
1247
|
+
|
1248
|
+
|
1249
|
+
class ToyTrain(BasePart, MutableSequence[TrainTrack]):
|
1250
|
+
__slots__ = ("_tracks",)
|
1251
|
+
|
1252
|
+
_tracks: list[TrainTrack]
|
1253
|
+
|
1254
|
+
def __init__(
|
1255
|
+
self,
|
1256
|
+
x_pos: float,
|
1257
|
+
y_pos: float,
|
1258
|
+
z_pos: float,
|
1259
|
+
x_rot: float,
|
1260
|
+
y_rot: float,
|
1261
|
+
z_rot: float,
|
1262
|
+
*,
|
1263
|
+
tracks: Iterable[TrainTrack] = (),
|
1264
|
+
) -> None:
|
1265
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
1266
|
+
self._tracks = list(tracks)
|
1267
|
+
|
1268
|
+
def append(self, value: TrainTrack) -> None:
|
1269
|
+
return self._tracks.append(value)
|
1270
|
+
|
1271
|
+
def clear(self) -> None:
|
1272
|
+
self._tracks.clear()
|
1273
|
+
|
1274
|
+
def __contains__(self, value: Any, /) -> bool:
|
1275
|
+
return value in self._tracks
|
1276
|
+
|
1277
|
+
@property
|
1278
|
+
def cost(self) -> int:
|
1279
|
+
return 100 + sum(track.cost for track in self)
|
1280
|
+
|
1281
|
+
def count(self, value: Any) -> int:
|
1282
|
+
return self._tracks.count(value)
|
1283
|
+
|
1284
|
+
def __delitem__(self, index: SupportsIndex | slice, /) -> None:
|
1285
|
+
del self._tracks[index]
|
1286
|
+
|
1287
|
+
def extend(self, iterable: Iterable[TrainTrack], /) -> None:
|
1288
|
+
self._tracks.extend(iterable)
|
1289
|
+
|
1290
|
+
@overload
|
1291
|
+
def __getitem__(self, index: SupportsIndex, /) -> TrainTrack:
|
1292
|
+
pass
|
1293
|
+
|
1294
|
+
@overload
|
1295
|
+
def __getitem__(self, index: slice, /) -> MutableSequence[TrainTrack]:
|
1296
|
+
pass
|
1297
|
+
|
1298
|
+
def __getitem__(
|
1299
|
+
self, index: SupportsIndex | slice, /
|
1300
|
+
) -> TrainTrack | MutableSequence[TrainTrack]:
|
1301
|
+
return self._tracks[index]
|
1302
|
+
|
1303
|
+
def __iadd__(self, value: Iterable[TrainTrack], /) -> Self:
|
1304
|
+
self._tracks += value
|
1305
|
+
return self
|
1306
|
+
|
1307
|
+
def index(
|
1308
|
+
self, value: Any, start: SupportsIndex = 0, stop: SupportsIndex = maxsize, /
|
1309
|
+
) -> int:
|
1310
|
+
return self._tracks.index(value, start, stop)
|
1311
|
+
|
1312
|
+
def insert(self, index: SupportsIndex, value: TrainTrack, /) -> None:
|
1313
|
+
return self._tracks.insert(index, value)
|
1314
|
+
|
1315
|
+
def __iter__(self) -> Iterator[TrainTrack]:
|
1316
|
+
return iter(self._tracks)
|
1317
|
+
|
1318
|
+
def __len__(self) -> int:
|
1319
|
+
return len(self._tracks)
|
1320
|
+
|
1321
|
+
def pop(self, index: SupportsIndex = -1, /) -> TrainTrack:
|
1322
|
+
return self._tracks.pop(index)
|
1323
|
+
|
1324
|
+
def remove(self, value: TrainTrack, /) -> None:
|
1325
|
+
self._tracks.remove(value)
|
1326
|
+
|
1327
|
+
def __repr__(self) -> str:
|
1328
|
+
return f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, tracks={self._tracks!r})"
|
1329
|
+
|
1330
|
+
def reverse(self) -> None:
|
1331
|
+
self._tracks.reverse()
|
1332
|
+
|
1333
|
+
def __reversed__(self) -> Iterator[TrainTrack]:
|
1334
|
+
return reversed(self._tracks)
|
1335
|
+
|
1336
|
+
@overload
|
1337
|
+
def __setitem__(self, index: SupportsIndex, value: TrainTrack, /) -> None:
|
1338
|
+
pass
|
1339
|
+
|
1340
|
+
@overload
|
1341
|
+
def __setitem__(self, slice: slice, value: Iterable[TrainTrack], /) -> None:
|
1342
|
+
pass
|
1343
|
+
|
1344
|
+
def __setitem__(
|
1345
|
+
self, index: SupportsIndex | slice, value: TrainTrack | Iterable[TrainTrack], /
|
1346
|
+
) -> None:
|
1347
|
+
self._tracks[index] = value # type: ignore
|
1348
|
+
|
1349
|
+
|
1350
|
+
class Warp(BasePart):
|
1351
|
+
__slots__ = (
|
1352
|
+
"_dest_x",
|
1353
|
+
"_dest_y",
|
1354
|
+
"_dest_z",
|
1355
|
+
"_return_dest_x",
|
1356
|
+
"_return_dest_y",
|
1357
|
+
"_return_dest_z",
|
1358
|
+
"_return_x_pos",
|
1359
|
+
"_return_x_rot",
|
1360
|
+
"_return_y_pos",
|
1361
|
+
"_return_y_rot",
|
1362
|
+
"_return_z_pos",
|
1363
|
+
"_return_z_rot",
|
1364
|
+
)
|
1365
|
+
|
1366
|
+
_dest_x: float
|
1367
|
+
_dest_y: float
|
1368
|
+
_dest_z: float
|
1369
|
+
_return_dest_x: float
|
1370
|
+
_return_dest_y: float
|
1371
|
+
_return_dest_z: float
|
1372
|
+
_return_x_pos: float
|
1373
|
+
_return_x_rot: float
|
1374
|
+
_return_y_pos: float
|
1375
|
+
_return_y_rot: float
|
1376
|
+
_return_z_pos: float
|
1377
|
+
_return_z_rot: float
|
1378
|
+
|
1379
|
+
def __init__(
|
1380
|
+
self,
|
1381
|
+
x_pos: float,
|
1382
|
+
y_pos: float,
|
1383
|
+
z_pos: float,
|
1384
|
+
x_rot: float,
|
1385
|
+
y_rot: float,
|
1386
|
+
z_rot: float,
|
1387
|
+
*,
|
1388
|
+
dest_x: float,
|
1389
|
+
dest_y: float,
|
1390
|
+
dest_z: float,
|
1391
|
+
return_x_pos: float,
|
1392
|
+
return_y_pos: float,
|
1393
|
+
return_z_pos: float,
|
1394
|
+
return_x_rot: float,
|
1395
|
+
return_y_rot: float,
|
1396
|
+
return_z_rot: float,
|
1397
|
+
return_dest_x: float,
|
1398
|
+
return_dest_y: float,
|
1399
|
+
return_dest_z: float,
|
1400
|
+
) -> None:
|
1401
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
1402
|
+
self.dest_x = dest_x
|
1403
|
+
self.dest_y = dest_y
|
1404
|
+
self.dest_z = dest_z
|
1405
|
+
self.return_dest_x = return_dest_x
|
1406
|
+
self.return_dest_y = return_dest_y
|
1407
|
+
self.return_dest_z = return_dest_z
|
1408
|
+
self.return_x_pos = return_x_pos
|
1409
|
+
self.return_x_rot = return_x_rot
|
1410
|
+
self.return_y_pos = return_y_pos
|
1411
|
+
self.return_y_rot = return_y_rot
|
1412
|
+
self.return_z_pos = return_z_pos
|
1413
|
+
self.return_z_rot = return_z_rot
|
1414
|
+
|
1415
|
+
@property
|
1416
|
+
def cost(self) -> Literal[25]:
|
1417
|
+
return 25
|
1418
|
+
|
1419
|
+
@property
|
1420
|
+
def dest_x(self) -> float:
|
1421
|
+
"""Positive is right, negative is left"""
|
1422
|
+
return self._dest_x
|
1423
|
+
|
1424
|
+
@dest_x.setter
|
1425
|
+
def dest_x(self, value: float, /) -> None:
|
1426
|
+
self._dest_x = value
|
1427
|
+
|
1428
|
+
@property
|
1429
|
+
def dest_y(self) -> float:
|
1430
|
+
return self._dest_y
|
1431
|
+
|
1432
|
+
@dest_y.setter
|
1433
|
+
def dest_y(self, value: float, /) -> None:
|
1434
|
+
"""Positive is up, negative is down"""
|
1435
|
+
self._dest_y = value
|
1436
|
+
|
1437
|
+
@property
|
1438
|
+
def dest_z(self) -> float:
|
1439
|
+
"""Positive is front, negative is back"""
|
1440
|
+
return self._dest_z
|
1441
|
+
|
1442
|
+
@dest_z.setter
|
1443
|
+
def dest_z(self, value: float, /) -> None:
|
1444
|
+
self._dest_z = value
|
1445
|
+
|
1446
|
+
def __repr__(self) -> str:
|
1447
|
+
return f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, dest_x={self.dest_x!r}, dest_y={self.dest_y!r}, dest_z={self.dest_z!r}, return_x_pos={self.return_x_pos}, return_y_pos={self.return_y_pos}, return_z_pos={self.return_z_pos}, return_x_rot={self.return_x_rot}, return_y_rot={self.return_y_rot}, return_z_rot={self.return_z_rot}, return_dest_x={self.return_dest_x}, return_dest_y={self.return_dest_y}, return_dest_z={self.return_dest_z})"
|
1448
|
+
|
1449
|
+
@property
|
1450
|
+
def return_dest_x(self) -> float:
|
1451
|
+
"""Positive is right, negative is left"""
|
1452
|
+
return self._return_dest_x
|
1453
|
+
|
1454
|
+
@return_dest_x.setter
|
1455
|
+
def return_dest_x(self, value: float, /) -> None:
|
1456
|
+
self._return_dest_x = value
|
1457
|
+
|
1458
|
+
@property
|
1459
|
+
def return_dest_y(self) -> float:
|
1460
|
+
return self._return_dest_y
|
1461
|
+
|
1462
|
+
@return_dest_y.setter
|
1463
|
+
def return_dest_y(self, value: float, /) -> None:
|
1464
|
+
"""Positive is up, negative is down"""
|
1465
|
+
self._return_dest_y = value
|
1466
|
+
|
1467
|
+
@property
|
1468
|
+
def return_dest_z(self) -> float:
|
1469
|
+
"""Positive is front, negative is back"""
|
1470
|
+
return self._return_dest_z
|
1471
|
+
|
1472
|
+
@return_dest_z.setter
|
1473
|
+
def return_dest_z(self, value: float, /) -> None:
|
1474
|
+
self._return_dest_z = value
|
1475
|
+
|
1476
|
+
@property
|
1477
|
+
def return_x_pos(self) -> float:
|
1478
|
+
"""Positive is right, negative is left"""
|
1479
|
+
return self._return_x_pos
|
1480
|
+
|
1481
|
+
@return_x_pos.setter
|
1482
|
+
def return_x_pos(self, value: float, /) -> None:
|
1483
|
+
self._return_x_pos = value
|
1484
|
+
|
1485
|
+
@property
|
1486
|
+
def return_x_rot(self) -> float:
|
1487
|
+
"""Represented in degrees from 0 to 360
|
1488
|
+
Positive turns top face front, negative turns top face back
|
1489
|
+
"""
|
1490
|
+
return self._return_x_rot
|
1491
|
+
|
1492
|
+
@return_x_rot.setter
|
1493
|
+
def return_x_rot(self, value: float, /) -> None:
|
1494
|
+
self._return_x_rot = value % 360
|
1495
|
+
|
1496
|
+
@property
|
1497
|
+
def return_y_pos(self) -> float:
|
1498
|
+
"""Positive is up, negative is down"""
|
1499
|
+
return self._return_y_pos
|
1500
|
+
|
1501
|
+
@return_y_pos.setter
|
1502
|
+
def return_y_pos(self, value: float, /) -> None:
|
1503
|
+
self._return_y_pos = value
|
1504
|
+
|
1505
|
+
@property
|
1506
|
+
def return_y_rot(self) -> float:
|
1507
|
+
"""Represented in degrees from 0 to 360
|
1508
|
+
Positive turns front face right, negative turns front face left
|
1509
|
+
"""
|
1510
|
+
return self._return_y_rot
|
1511
|
+
|
1512
|
+
@return_y_rot.setter
|
1513
|
+
def return_y_rot(self, value: float, /) -> None:
|
1514
|
+
self._return_y_rot = value % 360
|
1515
|
+
|
1516
|
+
@property
|
1517
|
+
def return_z_pos(self) -> float:
|
1518
|
+
"""Positive is front, negative is back"""
|
1519
|
+
return self._return_z_pos
|
1520
|
+
|
1521
|
+
@return_z_pos.setter
|
1522
|
+
def return_z_pos(self, value: float, /) -> None:
|
1523
|
+
self._return_z_pos = value
|
1524
|
+
|
1525
|
+
@property
|
1526
|
+
def return_z_rot(self) -> float:
|
1527
|
+
"""Represented in degrees from 0 to 360
|
1528
|
+
Positive turns top face left, negative turns top face right
|
1529
|
+
"""
|
1530
|
+
return self._return_z_rot
|
1531
|
+
|
1532
|
+
@return_z_rot.setter
|
1533
|
+
def return_z_rot(self, value: float, /) -> None:
|
1534
|
+
self._return_z_rot = value % 360
|
1535
|
+
|
1536
|
+
|
1537
|
+
class BlinkingTile(TimedDevice):
|
1538
|
+
__slots__ = ()
|
1539
|
+
|
1540
|
+
@property
|
1541
|
+
def cost(self) -> Literal[20]:
|
1542
|
+
return 20
|
1543
|
+
|
1544
|
+
|
1545
|
+
class MelodyTile(BasePart):
|
1546
|
+
__slots__ = ("_note",)
|
1547
|
+
|
1548
|
+
_note: Literal[
|
1549
|
+
DeviceModel.MelodyTileLowG,
|
1550
|
+
DeviceModel.MelodyTileLowGSharp,
|
1551
|
+
DeviceModel.MelodyTileLowA,
|
1552
|
+
DeviceModel.MelodyTileLowASharp,
|
1553
|
+
DeviceModel.MelodyTileLowB,
|
1554
|
+
DeviceModel.MelodyTileC,
|
1555
|
+
DeviceModel.MelodyTileCSharp,
|
1556
|
+
DeviceModel.MelodyTileD,
|
1557
|
+
DeviceModel.MelodyTileDSharp,
|
1558
|
+
DeviceModel.MelodyTileE,
|
1559
|
+
DeviceModel.MelodyTileF,
|
1560
|
+
DeviceModel.MelodyTileFSharp,
|
1561
|
+
DeviceModel.MelodyTileG,
|
1562
|
+
DeviceModel.MelodyTileGSharp,
|
1563
|
+
DeviceModel.MelodyTileA,
|
1564
|
+
DeviceModel.MelodyTileASharp,
|
1565
|
+
DeviceModel.MelodyTileB,
|
1566
|
+
DeviceModel.MelodyTileHighC,
|
1567
|
+
DeviceModel.MelodyTileHighCSharp,
|
1568
|
+
DeviceModel.MelodyTileHighD,
|
1569
|
+
DeviceModel.MelodyTileHighDSharp,
|
1570
|
+
DeviceModel.MelodyTileHighE,
|
1571
|
+
DeviceModel.MelodyTileHighF,
|
1572
|
+
DeviceModel.MelodyTileHighFSharp,
|
1573
|
+
DeviceModel.MelodyTileHighG,
|
1574
|
+
]
|
1575
|
+
|
1576
|
+
def __init__(
|
1577
|
+
self,
|
1578
|
+
x_pos: float,
|
1579
|
+
y_pos: float,
|
1580
|
+
z_pos: float,
|
1581
|
+
x_rot: float,
|
1582
|
+
y_rot: float,
|
1583
|
+
z_rot: float,
|
1584
|
+
*,
|
1585
|
+
note: Literal[
|
1586
|
+
DeviceModel.MelodyTileLowG,
|
1587
|
+
DeviceModel.MelodyTileLowGSharp,
|
1588
|
+
DeviceModel.MelodyTileLowA,
|
1589
|
+
DeviceModel.MelodyTileLowASharp,
|
1590
|
+
DeviceModel.MelodyTileLowB,
|
1591
|
+
DeviceModel.MelodyTileC,
|
1592
|
+
DeviceModel.MelodyTileCSharp,
|
1593
|
+
DeviceModel.MelodyTileD,
|
1594
|
+
DeviceModel.MelodyTileDSharp,
|
1595
|
+
DeviceModel.MelodyTileE,
|
1596
|
+
DeviceModel.MelodyTileF,
|
1597
|
+
DeviceModel.MelodyTileFSharp,
|
1598
|
+
DeviceModel.MelodyTileG,
|
1599
|
+
DeviceModel.MelodyTileGSharp,
|
1600
|
+
DeviceModel.MelodyTileA,
|
1601
|
+
DeviceModel.MelodyTileASharp,
|
1602
|
+
DeviceModel.MelodyTileB,
|
1603
|
+
DeviceModel.MelodyTileHighC,
|
1604
|
+
DeviceModel.MelodyTileHighCSharp,
|
1605
|
+
DeviceModel.MelodyTileHighD,
|
1606
|
+
DeviceModel.MelodyTileHighDSharp,
|
1607
|
+
DeviceModel.MelodyTileHighE,
|
1608
|
+
DeviceModel.MelodyTileHighF,
|
1609
|
+
DeviceModel.MelodyTileHighFSharp,
|
1610
|
+
DeviceModel.MelodyTileHighG,
|
1611
|
+
],
|
1612
|
+
) -> None:
|
1613
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
1614
|
+
self.note = note
|
1615
|
+
|
1616
|
+
@property
|
1617
|
+
def cost(self) -> Literal[20]:
|
1618
|
+
return 20
|
1619
|
+
|
1620
|
+
@property
|
1621
|
+
def note(self) -> Literal[
|
1622
|
+
DeviceModel.MelodyTileLowG,
|
1623
|
+
DeviceModel.MelodyTileLowGSharp,
|
1624
|
+
DeviceModel.MelodyTileLowA,
|
1625
|
+
DeviceModel.MelodyTileLowASharp,
|
1626
|
+
DeviceModel.MelodyTileLowB,
|
1627
|
+
DeviceModel.MelodyTileC,
|
1628
|
+
DeviceModel.MelodyTileCSharp,
|
1629
|
+
DeviceModel.MelodyTileD,
|
1630
|
+
DeviceModel.MelodyTileDSharp,
|
1631
|
+
DeviceModel.MelodyTileE,
|
1632
|
+
DeviceModel.MelodyTileF,
|
1633
|
+
DeviceModel.MelodyTileFSharp,
|
1634
|
+
DeviceModel.MelodyTileG,
|
1635
|
+
DeviceModel.MelodyTileGSharp,
|
1636
|
+
DeviceModel.MelodyTileA,
|
1637
|
+
DeviceModel.MelodyTileASharp,
|
1638
|
+
DeviceModel.MelodyTileB,
|
1639
|
+
DeviceModel.MelodyTileHighC,
|
1640
|
+
DeviceModel.MelodyTileHighCSharp,
|
1641
|
+
DeviceModel.MelodyTileHighD,
|
1642
|
+
DeviceModel.MelodyTileHighDSharp,
|
1643
|
+
DeviceModel.MelodyTileHighE,
|
1644
|
+
DeviceModel.MelodyTileHighF,
|
1645
|
+
DeviceModel.MelodyTileHighFSharp,
|
1646
|
+
DeviceModel.MelodyTileHighG,
|
1647
|
+
]:
|
1648
|
+
return self._note
|
1649
|
+
|
1650
|
+
@note.setter
|
1651
|
+
def note(
|
1652
|
+
self,
|
1653
|
+
value: Literal[
|
1654
|
+
DeviceModel.MelodyTileLowG,
|
1655
|
+
DeviceModel.MelodyTileLowGSharp,
|
1656
|
+
DeviceModel.MelodyTileLowA,
|
1657
|
+
DeviceModel.MelodyTileLowASharp,
|
1658
|
+
DeviceModel.MelodyTileLowB,
|
1659
|
+
DeviceModel.MelodyTileC,
|
1660
|
+
DeviceModel.MelodyTileCSharp,
|
1661
|
+
DeviceModel.MelodyTileD,
|
1662
|
+
DeviceModel.MelodyTileDSharp,
|
1663
|
+
DeviceModel.MelodyTileE,
|
1664
|
+
DeviceModel.MelodyTileF,
|
1665
|
+
DeviceModel.MelodyTileFSharp,
|
1666
|
+
DeviceModel.MelodyTileG,
|
1667
|
+
DeviceModel.MelodyTileGSharp,
|
1668
|
+
DeviceModel.MelodyTileA,
|
1669
|
+
DeviceModel.MelodyTileASharp,
|
1670
|
+
DeviceModel.MelodyTileB,
|
1671
|
+
DeviceModel.MelodyTileHighC,
|
1672
|
+
DeviceModel.MelodyTileHighCSharp,
|
1673
|
+
DeviceModel.MelodyTileHighD,
|
1674
|
+
DeviceModel.MelodyTileHighDSharp,
|
1675
|
+
DeviceModel.MelodyTileHighE,
|
1676
|
+
DeviceModel.MelodyTileHighF,
|
1677
|
+
DeviceModel.MelodyTileHighFSharp,
|
1678
|
+
DeviceModel.MelodyTileHighG,
|
1679
|
+
],
|
1680
|
+
/,
|
1681
|
+
) -> None:
|
1682
|
+
self._note = value
|
1683
|
+
|
1684
|
+
def __repr__(self) -> str:
|
1685
|
+
return f"{type(self).__name__}({self.x_pos!r}, {self.y_pos!r}, {self.z_pos!r}, {self.x_rot!r}, {self.y_rot!r}, {self.z_rot!r}, note={self.note!r})"
|
1686
|
+
|
1687
|
+
|
1688
|
+
class TextBox(BasePart):
|
1689
|
+
__slots__ = ("_shape", "_text_id")
|
1690
|
+
|
1691
|
+
_shape: Literal[DeviceModel.CubicTextBox, DeviceModel.WallTextBox]
|
1692
|
+
_text_id: int
|
1693
|
+
|
1694
|
+
def __init__(
|
1695
|
+
self,
|
1696
|
+
x_pos: float,
|
1697
|
+
y_pos: float,
|
1698
|
+
z_pos: float,
|
1699
|
+
x_rot: float,
|
1700
|
+
y_rot: float,
|
1701
|
+
z_rot: float,
|
1702
|
+
*,
|
1703
|
+
shape: Literal[DeviceModel.CubicTextBox, DeviceModel.WallTextBox],
|
1704
|
+
text_id: SupportsIndex,
|
1705
|
+
) -> None:
|
1706
|
+
super().__init__(x_pos, y_pos, z_pos, x_rot, y_rot, z_rot)
|
1707
|
+
self.shape = shape
|
1708
|
+
self.text_id = text_id # type: ignore[assignment]
|
1709
|
+
|
1710
|
+
@property
|
1711
|
+
def cost(self) -> Literal[0]:
|
1712
|
+
return 0
|
1713
|
+
|
1714
|
+
@property
|
1715
|
+
def shape(self) -> Literal[DeviceModel.CubicTextBox, DeviceModel.WallTextBox]:
|
1716
|
+
return self._shape
|
1717
|
+
|
1718
|
+
@shape.setter
|
1719
|
+
def shape(
|
1720
|
+
self, value: Literal[DeviceModel.CubicTextBox, DeviceModel.WallTextBox], /
|
1721
|
+
) -> None:
|
1722
|
+
self._shape = value
|
1723
|
+
|
1724
|
+
@property
|
1725
|
+
def text_id(self) -> int:
|
1726
|
+
return self._text_id
|
1727
|
+
|
1728
|
+
@text_id.setter
|
1729
|
+
def text_id(self, value: SupportsIndex, /) -> None:
|
1730
|
+
self._text_id = index(value)
|
1731
|
+
|
1732
|
+
|
1733
|
+
class KororinCapsule(BasePart):
|
1734
|
+
__slots__ = ()
|
1735
|
+
|
1736
|
+
@property
|
1737
|
+
def cost(self) -> Literal[0]:
|
1738
|
+
return 0
|
1739
|
+
|
1740
|
+
|
1741
|
+
class GreenCrystal(BasePart):
|
1742
|
+
__slots__ = ()
|
1743
|
+
|
1744
|
+
@property
|
1745
|
+
def cost(self) -> Literal[0]:
|
1746
|
+
return 0
|
1747
|
+
|
1748
|
+
|
1749
|
+
class Ant(BasePart):
|
1750
|
+
__slots__ = ()
|
1751
|
+
|
1752
|
+
@property
|
1753
|
+
def cost(self) -> Literal[0]:
|
1754
|
+
return 0
|