-
Notifications
You must be signed in to change notification settings - Fork 10
/
scene.py
179 lines (137 loc) · 5.62 KB
/
scene.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0
# Author (©): Alvaro del Castillo
# TODO: at some point this must be a real Singleton
import pickle
from mcpi.vec3 import Vec3
from mcthings.blocks_memory import BlocksMemory
from mcthings.utils import build_schematic_nbt
from mcthings.world import World
class Scene:
"""
A scene is a container for all the things built using McThings.
A scene can be built, unbuilt and moved. There is only one scene
in a program using McThings. Things built are added automatically to
the Scene. A Scene can also be loaded from a file, and
it can be saved to a file.
Before adding Things to the Scene, it must be connected to a
Minecraft server (fill the Scene.server attribute)
"""
def __init__(self):
self.things = []
""" map with the things in the scene """
self._decorators = []
""" decorators for the scene """
self._position = None
""" position in the world of the scene """
self._end_position = None
""" end position in the world of the scene """
World.add_scene(self)
@property
def end_position(self):
""" end position of the thing """
return self._end_position
@property
def position(self):
""" initial position of the thing """
return self._position
def add(self, thing):
""" Add a new thing to the scene """
if not self.things:
# The initial position of the scene is the position
# of its first thing added
self._position = thing.position
self.things.append(thing)
def add_decorator(self, decorator):
""" Add a new decorator to the scene """
self._decorators.append(decorator)
def decorate(self):
"""
Call all decorators for the current Scene
:return:
"""
for decorator in self._decorators:
decorator(self).decorate()
def build(self):
""" Build all the things inside the Scene """
for thing in self.things:
thing.build()
(min_pos, max_pos) = self.find_bounding_box()
self._end_position = max_pos
def unbuild(self):
""" Unbuild all the things inside the Scene """
for thing in self.things:
thing.unbuild()
def create(self):
""" Create all the things inside the Scene """
for thing in self.things:
thing.create()
def reposition(self, position):
"""
Move all the things in the scene to a new relative position
:param position: new position for the Scene
:return:
"""
# All the things inside the scene must be moved
diff_x = position.x - self._position.x
diff_y = position.y - self._position.y
diff_z = position.z - self._position.z
for thing in self.things:
repos_x = thing.position.x + diff_x
repos_y = thing.position.y + diff_y
repos_z = thing.position.z + diff_z
thing._position = (Vec3(repos_x, repos_y, repos_z))
def move(self, position):
"""
Move the scene to a new position
:param position: new position
:return:
"""
self.unbuild()
self.reposition(position)
self.build()
def load(self, file_path):
""" Load a scene from a file (but no build it yet) """
self.things = pickle.load(open(file_path, "rb"))
if self.things:
self._position = self.things[0].position
def save(self, file_path):
""" Save a scene to a file """
def clean_memory(thing):
for child in thing._children:
clean_memory(child)
thing._blocks_memory = BlocksMemory()
# Clean the blocks_memory: it is not needed to recreate the scene
for thing in self.things:
clean_memory(thing)
pickle.dump(self.things, open(file_path, "wb"))
# Reload the memory
self.create()
def find_bounding_box(self):
""" Compute the bounding box of the Scene """
def update_box(box_pos_min, box_pos_max, pos):
# Update box_pos_min and box_pos_max checking pos
box_pos_min.x = pos.x if pos.x < box_pos_min.x else box_pos_min.x
box_pos_min.y = pos.y if pos.y < box_pos_min.y else box_pos_min.y
box_pos_min.z = pos.z if pos.z < box_pos_min.z else box_pos_min.z
box_pos_max.x = pos.x if pos.x > box_pos_max.x else box_pos_max.x
box_pos_max.y = pos.y if pos.y > box_pos_max.y else box_pos_max.y
box_pos_max.z = pos.z if pos.z > box_pos_max.z else box_pos_max.z
return box_pos_min, box_pos_max
# Default init values
min_pos = Vec3(self._position.x, self._position.y, self._position.z)
max_pos = Vec3(self._position.x, self._position.y, self._position.z)
# Find the bounding box for the scene
for thing in self.things:
min_pos, max_pos = update_box(min_pos, max_pos, thing.position)
if thing.end_position:
min_pos, max_pos = update_box(min_pos, max_pos, thing.end_position)
return min_pos, max_pos
def to_schematic(self, file_path, block_data=False):
"""
Save the Scene into a Schematic file
:param file_path: file in which to export the Scene in Schematic format
:param block_data: extract blocks ids and data (much slower)
:return: the Schematic object
"""
(min_pos, max_pos) = self.find_bounding_box()
build_schematic_nbt(min_pos, max_pos, block_data).write_file(file_path)