def generate_minimap(cls, size, parameters): """Called as subprocess, calculates minimap data and passes it via string via stdout""" # called as standalone basically, so init everything we need from horizons.main import _create_main_db from horizons.entities import Entities from horizons.ext.dummy import Dummy db = _create_main_db() Entities.load_grounds(db, load_now=False) # create all references map_file = SingleplayerMenu._generate_random_map(parameters) world = cls._load_raw_world(map_file) location = Rect.init_from_topleft_and_size_tuples((0, 0), size) minimap = Minimap( location, session=None, view=None, world=world, targetrenderer=Dummy(), imagemanager=Dummy(), cam_border=False, use_rotation=False, preview=True, ) # communicate via stdout print minimap.dump_data()
def handle_lost_area(self, coords_list): """Handle losing the potential land in the given coordinates list.""" # remove planned fields that are now impossible field_size = Entities.buildings[BUILDINGS.POTATO_FIELD_CLASS].size removed_list = [] for coords, (purpose, _) in self.plan.iteritems(): if purpose in [BUILDING_PURPOSE.POTATO_FIELD, BUILDING_PURPOSE.PASTURE, BUILDING_PURPOSE.SUGARCANE_FIELD]: rect = Rect.init_from_topleft_and_size_tuples(coords, field_size) for field_coords in rect.tuple_iter(): if field_coords not in self.land_manager.production: removed_list.append(coords) break for coords in removed_list: rect = Rect.init_from_topleft_and_size_tuples(coords, field_size) for field_coords in rect.tuple_iter(): self.plan[field_coords] = (BUILDING_PURPOSE.NONE, None) self._refresh_unused_fields() super(ProductionBuilder, self).handle_lost_area(coords_list)
def __init_outline(cls): """Save a template outline that surrounds a lumberjack.""" position = Rect.init_from_topleft_and_size_tuples((0, 0), Entities.buildings[BUILDINGS.LUMBERJACK_CLASS].size) moves = [(-1, 0), (0, -1), (0, 1), (1, 0)] coords_list = set(position.get_radius_coordinates(Entities.buildings[BUILDINGS.LUMBERJACK_CLASS].radius, True)) result = set() for x, y in coords_list: for dx, dy in moves: coords = (x + dx, y + dy) if coords not in coords_list: result.add(coords) cls.__template_outline = list(result)
def _get_possible_building_positions(self, section_coords_set, size): """Return {(x, y): Rect, ...} that contains every size x size potential building location where only the provided coordinates are legal.""" result = {} for (x, y) in sorted(section_coords_set): ok = True for dx in xrange(size[0]): for dy in xrange(size[1]): coords = (x + dx, y + dy) if coords not in section_coords_set or not self.land_manager.coords_usable(coords): ok = False break if not ok: break if ok: result[(x, y)] = Rect.init_from_topleft_and_size_tuples((x, y), size) return result
def _handle_farm_removal(self, building): """Handle farm removal by removing planned fields and tearing existing ones that can't be serviced by another farm.""" unused_fields = set() farms = self.settlement.get_buildings_by_id(BUILDINGS.FARM_CLASS) for coords in building.position.get_radius_coordinates(building.radius): if not coords in self.plan: continue object = self.island.ground_map[coords].object if object is None or object.id not in self.field_building_classes: continue used_by_another_farm = False for farm in farms: if farm.worldid != building.worldid and object.position.distance(farm.position) <= farm.radius: used_by_another_farm = True break if not used_by_another_farm: unused_fields.add(object) # tear the finished but no longer used fields down for unused_field in unused_fields: for x, y in unused_field.position.tuple_iter(): self.register_change(x, y, BUILDING_PURPOSE.NONE, None) Tear(unused_field).execute(self.session) # remove the planned but never built fields from the plan self._refresh_unused_fields() for unused_fields_list in self.unused_fields.itervalues(): for coords in unused_fields_list: position = Rect.init_from_topleft_and_size_tuples( coords, Entities.buildings[BUILDINGS.POTATO_FIELD_CLASS].size ) if building.position.distance(position) > building.radius: continue # it never belonged to the removed building used_by_another_farm = False for farm in farms: if farm.worldid != building.worldid and position.distance(farm.position) <= farm.radius: used_by_another_farm = True break if not used_by_another_farm: for x, y in position.tuple_iter(): self.register_change(x, y, BUILDING_PURPOSE.NONE, None) self._refresh_unused_fields()
def _get_position(cls, coords, building_id): """Return the position Rect of a building of the given type at the given position.""" return Rect.init_from_topleft_and_size_tuples(coords, Entities.buildings[building_id].size)
def _enlarge_collector_area(self): if not self.production_builder.have_resources(BUILDINGS.STORAGE_CLASS): return BUILD_RESULT.NEED_RESOURCES moves = [(-1, 0), (0, -1), (0, 1), (1, 0)] # valid moves for collectors collector_area = self.production_builder.get_collector_area() # area_label contains free tiles in the production area and all road tiles area_label = dict.fromkeys(self.land_manager.roads) # {(x, y): area_number, ...} for coords, (purpose, _) in self.production_builder.plan.iteritems(): if purpose == BUILDING_PURPOSE.NONE: area_label[coords] = None areas = 0 for coords in collector_area: if coords in area_label and area_label[coords] is not None: continue queue = deque([coords]) while queue: x, y = queue[0] queue.popleft() for dx, dy in moves: coords2 = (x + dx, y + dy) if coords2 in area_label and area_label[coords2] is None: area_label[coords2] = areas queue.append(coords2) areas += 1 coords_set_by_area = defaultdict(lambda: set()) for coords, area_number in area_label.iteritems(): if coords in self.production_builder.plan and self.production_builder.plan[coords][0] == BUILDING_PURPOSE.NONE and coords not in collector_area: coords_set_by_area[area_number].add(coords) options = [] for (x, y), area_number in area_label.iteritems(): builder = self.production_builder.make_builder(BUILDINGS.STORAGE_CLASS, x, y, False) if not builder: continue coords_set = set(builder.position.get_radius_coordinates(Entities.buildings[BUILDINGS.STORAGE_CLASS].radius)) useful_area = len(coords_set_by_area[area_number].intersection(coords_set)) if not useful_area: continue alignment = 1 for tile in self.production_builder.iter_neighbour_tiles(builder.position): coords = (tile.x, tile.y) if coords not in self.production_builder.plan or self.production_builder.plan[coords][0] != BUILDING_PURPOSE.NONE: alignment += 1 value = useful_area + alignment * self.personality.alignment_coefficient options.append((value, builder)) if options: return self.production_builder.build_best_option(options, BUILDING_PURPOSE.STORAGE) # enlarge the settlement area instead since just enlarging the collector area is impossible if self.village_builder.tent_queue: tent_size = Entities.buildings[BUILDINGS.RESIDENTIAL_CLASS].size tent_radius = Entities.buildings[BUILDINGS.RESIDENTIAL_CLASS].radius best_coords = None best_area = 0 for x, y in self.village_builder.tent_queue: new_area = 0 for coords in Rect.init_from_topleft_and_size(x, y, tent_size[0], tent_size[1]).get_radius_coordinates(tent_radius): if coords in area_label and coords not in self.land_manager.roads and coords not in collector_area: new_area += 1 if new_area > best_area: best_coords = (x, y) best_area = new_area if best_coords is not None: return self.production_builder.extend_settlement_with_tent(Rect.init_from_topleft_and_size_tuples(best_coords, tent_size)) return BUILD_RESULT.IMPOSSIBLE