class ChunkController: def __init__(self, width, height, seed, changes=None, min_height=128, max_height=-128): self.width = width self.height = height self.ground_height = [0 for g in range(self.width) ] # небольшая оптимизация self.tree_map = [[0 for g in range(self.width)] for i in range(self.height)] self.sub_tree_map = [0 for g in range(self.width)] self.max_ground = 25 self.chunk = [[0 for g in range(self.width)] for i in range(self.height)] self.seed = seed self.cave_generator = TwoDisNoise(self.seed) self.generator = Noise(self.seed) self.min_height = min_height self.max_height = max_height self.changes = changes if changes is not None else {} def get_player_start_pos(self, x): return self.max_ground - int( self.generator.noise1d(x) * self.max_ground) - 2 def place_block(self, x, y, block, game): if x not in self.changes: self.changes[x] = {} self.changes[x][y] = block if not game.is_server: lx, ly = x - game.pos_center[0] + ( game.block_width + game.additional) // 2, y - game.pos_center[ 1] + (game.block_height + game.additional) // 2 if 0 <= lx < (game.block_width + game.additional) and 0 <= ly < ( game.block_height + game.additional): was = self.chunk[ly][lx] self.chunk[ly][lx] = block block.x = lx block.y = ly game.update_changes(was, block) if not block.transparent: game.light_controller.max_y[lx] = min( game.light_controller.max_y[lx], y) elif not was.transparent: game.light_controller.calc_max_y_for_x( lx, game.pos_center, self, game.objects) game.light_controller.calculate_light(self.chunk, self.ground_height) block.on_place(game) def generate_trees(self, x, y): self.tree_map = [[0 for g in range(self.height)] for i in range(self.width)] self.sub_map = [0 for g in range(self.width)] for i in range(-self.width // 2, math.ceil(self.width / 2)): val = self.max_ground - int( self.generator.noise1d((int(x) + i)) * self.max_ground) self.ground_height[i + self.width // 2] = val subval = abs( self.cave_generator.noise2d(int(x) + i, val, octaves=3, amp=0.02, zoom=0.05, fr=3)) if subval > 0.5: continue if self.generator.noise1d( (int(x) + i), amp=15, fr=0.001, zoom=10, octaves=1) > 0.85: self.sub_map[i + self.width // 2] = 1 if int(y) - self.height // 2 > max(self.ground_height): return offsets_tree = [(0, -1), (0, -2), (0, -3)] offsets = [(0, -4), (0, -5), (1, -4), (1, -5), (2, -4), (2, -5), (-1, -4), (-1, -5), (-2, -4), (-2, -5), (-1, -6), (1, -6), (0, -6)] for i in range(-self.width // 2, math.ceil(self.width / 2)): for g in range(-self.height // 2, math.ceil(self.height / 2)): was = False if self.tree_map[i + self.width // 2][g + self.height // 2] != 0: continue for k in offsets_tree: if not (0 <= i + self.width // 2 + k[0] < self.width): continue if self.sub_map[i + self.width // 2 + k[0]] and int(y) + g - \ self.ground_height[i + self.width // 2 + k[0]] == k[1]: self.tree_map[i + self.width // 2][g + self.height // 2] = 2 was = True break if was: continue for k in offsets: if not (0 <= i + self.width // 2 + k[0] < self.width): continue if self.sub_map[i + self.width // 2 + k[0]] and int(y) + g - \ self.ground_height[i + self.width // 2 + k[0]] == k[1]: self.tree_map[i + self.width // 2][g + self.height // 2] = 1 break def get(self, x, y, blocks, game): # получить блок в любой точке мира if not game.is_server: lx, ly = x - game.pos_center[0] + ( game.block_width + game.additional) // 2, y - game.pos_center[ 1] + (game.block_height + game.additional) // 2 if 0 <= lx < (game.block_width + game.additional) and 0 <= ly < ( game.block_height + game.additional): return self.chunk[ly][lx] buff = type(self)(1, 1, self.seed, changes=self.changes) buff.update_chunk(x, y, blocks) return buff.chunk[0][0] def update_chunk(self, x, y, blocks): self.generate_trees(x, y) for i in range(-self.width // 2, math.ceil(self.width / 2)): val = self.ground_height[i + self.width // 2] for g in range(-self.height // 2, math.ceil(self.height / 2)): lx, ly = i + self.width // 2, g + self.height // 2 # локальные wx, wy = int(x) + i, int(y) + g # глобальные block_type = EMPTY if self.min_height > wy > self.max_height and wy >= val: if wy == val: block_type = GRASS elif wy > val + 10 + val % 2: block_type = STONE else: block_type = DIRT original = block_type if wy >= val and not (wx in self.changes and wy in self.changes[wx]): subval = abs( self.cave_generator.noise2d(wx, wy, octaves=3, amp=0.02, zoom=0.05, fr=3)) if subval > 0.5: block_type = EMPTY else: subval = self.cave_generator.noise2d( wx, wy) # у руд другие настройки генератора if self.min_height > wy > val + 10 + val % 2 and subval < -0.15: if -0.9 < subval < -0.85: block_type = 'gold_ore' elif -0.77 < subval < -0.7: block_type = "iron_ore" elif -0.42 < subval < -0.3: block_type = 'coal_ore' elif -0.7 < subval < -0.67: block_type = 'diamond_ore' elif -0.23 < subval < -0.15: block_type = 'redstone_ore' if self.tree_map[i + self.width // 2][g + self.height // 2] != 0: block_type = 'orig_wood' if self.tree_map[ i + self.width // 2][g + self.height // 2] == 2 else 'leaves' if self.min_height == wy: block_type = 'bedrock' if wx in self.changes and wy in self.changes[ wx]: # обновление измененного блока self.chunk[ly][lx] = self.changes[wx][wy] self.chunk[ly][lx].x = lx self.chunk[ly][lx].y = ly continue self.chunk[ly][lx] = blocks[block_type].generate_block( lx, ly, (wx, wy)) self.chunk[ly][lx].x = lx self.chunk[ly][lx].y = ly def save(self): changes = {} for i in self.changes.keys(): changes[i] = {} for g in self.changes[i].keys(): changes[i][g] = self.changes[i][g].save() return {'seed': self.seed, 'changes': changes} def load_block(self, data, blocks): return blocks[data['type']].generate_block(**data) def load(self, data, blocks): self.seed = data['seed'] changes = data['changes'] for i in changes.keys(): self.changes[int(i)] = {} for g in changes[i].keys(): changes[i][g]['x'] = 0 changes[i][g]['y'] = 0 if 'drop' in changes[i][g]: changes[i][g]['drop'] = BlockDrop(*changes[i][g]['drop']) self.changes[int(i)][int(g)] = self.load_block( changes[i][g], blocks)