def handle_lost_area(self, coords_list):
		"""Handle losing the potential land in the given coordinates list."""
		# remove planned fields that are now impossible
		lost_coords_list = []
		for coords in coords_list:
			if coords in self.plan:
				lost_coords_list.append(coords)
		self.register_change_list(lost_coords_list, BUILDING_PURPOSE.NONE, None)

		field_size = Entities.buildings[BUILDINGS.POTATO_FIELD].size
		removed_list = []
		for coords, (purpose, _) in self.plan.iteritems():
			if purpose in [BUILDING_PURPOSE.POTATO_FIELD, BUILDING_PURPOSE.PASTURE, BUILDING_PURPOSE.SUGARCANE_FIELD, BUILDING_PURPOSE.TOBACCO_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)
			self.register_change_list(list(rect.tuple_iter()), BUILDING_PURPOSE.NONE, None)
		self._refresh_unused_fields()
		super(ProductionBuilder, self).handle_lost_area(coords_list)
		self.road_connectivity_cache.modify_area(lost_coords_list)
Exemplo n.º 2
0
    def handle_lost_area(self, coords_list):
        """Handle losing the potential land in the given coordinates list."""
        # remove planned fields that are now impossible
        lost_coords_list = []
        for coords in coords_list:
            if coords in self.plan:
                lost_coords_list.append(coords)
        self.register_change_list(lost_coords_list, BUILDING_PURPOSE.NONE,
                                  None)

        field_size = Entities.buildings[BUILDINGS.POTATO_FIELD].size
        removed_list = []
        for coords, (purpose, _) in self.plan.items():
            if purpose in [
                    BUILDING_PURPOSE.POTATO_FIELD, BUILDING_PURPOSE.PASTURE,
                    BUILDING_PURPOSE.SUGARCANE_FIELD,
                    BUILDING_PURPOSE.TOBACCO_FIELD, BUILDING_PURPOSE.HERBARY
            ]:
                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)
            self.register_change_list(list(rect.tuple_iter()),
                                      BUILDING_PURPOSE.NONE, None)
        self._refresh_unused_fields()
        super(ProductionBuilder, self).handle_lost_area(coords_list)
        self.road_connectivity_cache.modify_area(lost_coords_list)
def generate_random_minimap(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.entities import Entities
	from horizons.main import _create_main_db

	if not VERSION.IS_DEV_VERSION:
		# Hack enable atlases.
		# Usually the minimap generator uses single tile files, but in release
		# mode these are not available. Therefor we have to hackenable atlases
		# for the minimap generation in this case. This forces the game to use
		# the correct imageloader
		# In normal dev mode + enabled atlases we ignore this and just continue
		# to use single tile files instead of atlases for the minimap generation.
		# These are always available in dev checkouts
		PATHS.DB_FILES = PATHS.DB_FILES + (PATHS.ATLAS_DB_PATH, )

	db = _create_main_db()
	horizons.globals.db = db
	horizons.globals.fife.init_animation_loader(not VERSION.IS_DEV_VERSION)
	Entities.load_grounds(db, load_now=False) # create all references

	map_file = generate_random_map(*parameters)
	world = load_raw_world(map_file)
	location = Rect.init_from_topleft_and_size_tuples((0, 0), size)

	# communicate via stdout. Sometimes the process seems to print more information, therefore
	# we add markers around our data so it's easier for the caller to get to the data.
	args = (location, world, Minimap.COLORS['island'], Minimap.COLORS['water'])
	data = [(x, y, r, g, b) for (x, y), (r, g, b) in iter_minimap_points_colors(*args)]
	print('DATA', json.dumps(data), 'ENDDATA')
	def build(self, settlement_manager, resource_id):
		village_builder = settlement_manager.village_builder
		building_purpose = self.get_purpose(resource_id)
		building_id = BUILDING_PURPOSE.get_building(building_purpose)
		building_class = Entities.buildings[building_id]

		for coords, (purpose, (section, _)) in village_builder.plan.iteritems():
			if section > village_builder.current_section or purpose != building_purpose:
				continue

			object = village_builder.land_manager.island.ground_map[coords].object
			if object is not None and object.id == self.id:
				continue

			if building_purpose != BUILDING_PURPOSE.MAIN_SQUARE:
				if not self._need_producer(settlement_manager, coords, resource_id):
					continue

			if not village_builder.have_resources(building_id):
				return (BUILD_RESULT.NEED_RESOURCES, None)
			if coords not in village_builder.settlement.buildability_cache.cache[building_class.size]:
				position = Rect.init_from_topleft_and_size_tuples(coords, building_class.size)
				return (BUILD_RESULT.OUT_OF_SETTLEMENT, position)

			building = BasicBuilder(building_id, coords, 0).execute(settlement_manager.land_manager)
			assert building
			if self.get_purpose(resource_id) == BUILDING_PURPOSE.MAIN_SQUARE and not village_builder.roads_built:
				village_builder.build_roads()
			return (BUILD_RESULT.OK, building)
		return (BUILD_RESULT.SKIP, None)
def generate_random_minimap(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.entities import Entities
    from horizons.main import _create_main_db

    if not VERSION.IS_DEV_VERSION:
        # Hack enable atlases.
        # Usually the minimap generator uses single tile files, but in release
        # mode these are not available. Therefor we have to hackenable atlases
        # for the minimap generation in this case. This forces the game to use
        # the correct imageloader
        # In normal dev mode + enabled atlases we ignore this and just continue
        # to use single tile files instead of atlases for the minimap generation.
        # These are always available in dev checkouts
        PATHS.DB_FILES = PATHS.DB_FILES + (PATHS.ATLAS_DB_PATH, )

    db = _create_main_db()
    horizons.globals.db = db
    horizons.globals.fife.init_animation_loader(not VERSION.IS_DEV_VERSION)
    Entities.load_grounds(db, load_now=False)  # create all references

    map_file = generate_random_map(*parameters)
    world = load_raw_world(map_file)
    location = Rect.init_from_topleft_and_size_tuples((0, 0), size)

    # communicate via stdout. Sometimes the process seems to print more information, therefore
    # we add markers around our data so it's easier for the caller to get to the data.
    args = (location, world, Minimap.COLORS['island'], Minimap.COLORS['water'])
    data = [(x, y, r, g, b)
            for (x, y), (r, g, b) in iter_minimap_points(*args)]
    print('DATA', json.dumps(data), 'ENDDATA')
	def __init__(self, building_id, coords, orientation):
		self.building_id = building_id
		self.coords = coords
		self.orientation = orientation

		size = Entities.buildings[building_id].size
		if orientation % 2 != 0:
			size = (size[1], size[0])
		self.position = Rect.init_from_topleft_and_size_tuples(coords, size)
Exemplo n.º 7
0
	def __init__(self, building_id, coords, orientation):
		self.building_id = building_id
		self.coords = coords
		self.orientation = orientation

		size = Entities.buildings[building_id].size
		if orientation % 2 != 0:
			size = (size[1], size[0])
		self.position = Rect.init_from_topleft_and_size_tuples(coords, size)
Exemplo n.º 8
0
	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].size)
		moves = [(-1, 0), (0, -1), (0, 1), (1, 0)]
		coords_list = set(position.get_radius_coordinates(Entities.buildings[BUILDINGS.LUMBERJACK].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)
Exemplo n.º 9
0
	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].size)
		moves = [(-1, 0), (0, -1), (0, 1), (1, 0)]
		coords_list = set(position.get_radius_coordinates(Entities.buildings[BUILDINGS.LUMBERJACK].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 = sorted(list(result))
		cls.__radius_offsets = sorted(position.get_radius_coordinates(Entities.buildings[BUILDINGS.LUMBERJACK].radius))
	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
Exemplo n.º 11
0
	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 range(size[0]):
				for dy in range(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
Exemplo n.º 12
0
    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.buildings_by_id.get(BUILDINGS.FARM, [])
        for coords in building.position.get_radius_coordinates(
                building.radius):
            if coords not 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:
            self.register_change_list(list(unused_field.position.tuple_iter()),
                                      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.values():
            for coords in unused_fields_list:
                position = Rect.init_from_topleft_and_size_tuples(
                    coords, Entities.buildings[BUILDINGS.POTATO_FIELD].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:
                    self.register_change_list(list(position.tuple_iter()),
                                              BUILDING_PURPOSE.NONE, None)
        self._refresh_unused_fields()
Exemplo n.º 13
0
    def _upgrade_to_rev69(self, db):
        settlement_map = {}
        for data in db("SELECT rowid, data FROM settlement_tiles"):
            settlement_id = int(data[0])
            coords_list = [
                tuple(raw_coords) for raw_coords in json.loads(data[1])
            ]  # json saves tuples as list
            for coords in coords_list:
                settlement_map[coords] = settlement_id
        db("DELETE FROM settlement_tiles")

        deposits = []
        for (worldid, building_id, x, y, location_id) in db(
                "SELECT rowid, type, x, y, location FROM building WHERE type = ? OR type = ?",
                BUILDINGS.CLAY_DEPOSIT, BUILDINGS.MOUNTAIN):
            worldid = int(worldid)
            building_id = int(building_id)
            origin_coords = (int(x), int(y))
            location_id = int(location_id)

            settlement_ids = set()
            position = Rect.init_from_topleft_and_size_tuples(
                origin_coords, Entities.buildings[building_id].size)
            for coords in position.tuple_iter():
                if coords in settlement_map:
                    settlement_ids.add(settlement_map[coords])
            if not settlement_ids:
                continue  # no settlement covers any of the deposit
            else:
                # assign all of it to the earlier settlement
                settlement_id = sorted(settlement_ids)[0]
                for coords in position.tuple_iter():
                    settlement_map[coords] = settlement_id
                if location_id != settlement_id:
                    db("UPDATE building SET location = ? WHERE rowid = ?",
                       settlement_id, worldid)

        # save the new settlement tiles data
        ground_map = defaultdict(lambda: [])
        for (coords, settlement_id) in settlement_map.iteritems():
            ground_map[settlement_id].append(coords)

        for (settlement_id, coords_list) in ground_map.iteritems():
            data = json.dumps(coords_list)
            db("INSERT INTO settlement_tiles(rowid, data) VALUES(?, ?)",
               settlement_id, data)
Exemplo n.º 14
0
	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.buildings_by_id.get(BUILDINGS.FARM, [])
		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].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 _upgrade_to_rev69(self, db):
        settlement_map = {}
        for data in db("SELECT rowid, data FROM settlement_tiles"):
            settlement_id = int(data[0])
            coords_list = [tuple(raw_coords) for raw_coords in json.loads(data[1])]  # json saves tuples as list
            for coords in coords_list:
                settlement_map[coords] = settlement_id
        db("DELETE FROM settlement_tiles")

        for (worldid, building_id, x, y, location_id) in db(
            "SELECT rowid, type, x, y, location FROM building WHERE type = ? OR type = ?",
            BUILDINGS.CLAY_DEPOSIT,
            BUILDINGS.MOUNTAIN,
        ):
            worldid = int(worldid)
            building_id = int(building_id)
            origin_coords = (int(x), int(y))
            location_id = int(location_id)

            settlement_ids = set()
            position = Rect.init_from_topleft_and_size_tuples(origin_coords, Entities.buildings[building_id].size)
            for coords in position.tuple_iter():
                if coords in settlement_map:
                    settlement_ids.add(settlement_map[coords])
            if not settlement_ids:
                continue  # no settlement covers any of the deposit
            else:
                # assign all of it to the earlier settlement
                settlement_id = sorted(settlement_ids)[0]
                for coords in position.tuple_iter():
                    settlement_map[coords] = settlement_id
                if location_id != settlement_id:
                    db("UPDATE building SET location = ? WHERE rowid = ?", settlement_id, worldid)

                    # save the new settlement tiles data
        ground_map = defaultdict(list)
        for (coords, settlement_id) in settlement_map.iteritems():
            ground_map[settlement_id].append(coords)

        for (settlement_id, coords_list) in ground_map.iteritems():
            data = json.dumps(coords_list)
            db("INSERT INTO settlement_tiles(rowid, data) VALUES(?, ?)", settlement_id, data)
 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()
Exemplo n.º 17
0
def generate_random_minimap(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.entities import Entities
	from horizons.ext.dummy import Dummy
	from horizons.main import _create_main_db

	if not VERSION.IS_DEV_VERSION:
		# Hack enable atlases.
		# Usually the minimap generator uses single tile files, but in release
		# mode these are not available. Therefor we have to hackenable atlases
		# for the minimap generation in this case. This forces the game to use
		# the correct imageloader
		# In normal dev mode + enabled atlases we ignore this and just continue
		# to use single tile files instead of atlases for the minimap generation.
		# These are always available in dev checkouts
		PATHS.DB_FILES = PATHS.DB_FILES + (PATHS.ATLAS_DB_PATH, )

	db = _create_main_db()
	horizons.globals.db = db
	horizons.globals.fife.init_animation_loader(not VERSION.IS_DEV_VERSION)
	Entities.load_grounds(db, load_now=False) # create all references

	map_file = generate_random_map(*parameters)
	world = 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()
Exemplo n.º 18
0
def generate_random_minimap(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.entities import Entities
	from horizons.ext.dummy import Dummy
	from horizons.main import _create_main_db

	if not VERSION.IS_DEV_VERSION:
		# Hack enable atlases.
		# Usually the minimap generator uses single tile files, but in release
		# mode these are not available. Therefor we have to hackenable atlases
		# for the minimap generation in this case. This forces the game to use
		# the correct imageloader
		# In normal dev mode + enabled atlases we ignore this and just continue
		# to use single tile files instead of atlases for the minimap generation.
		# These are always available in dev checkouts
		PATHS.DB_FILES = PATHS.DB_FILES + (PATHS.ATLAS_DB_PATH, )

	db = _create_main_db()
	horizons.globals.db = db
	horizons.globals.fife.init_animation_loader(not VERSION.IS_DEV_VERSION)
	Entities.load_grounds(db, load_now=False) # create all references

	map_file = generate_random_map(*parameters)
	world = 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()
Exemplo n.º 19
0
	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 build(self, settlement_manager, resource_id):
        village_builder = settlement_manager.village_builder
        building_purpose = self.get_purpose(resource_id)
        building_id = BUILDING_PURPOSE.get_building(building_purpose)
        building_class = Entities.buildings[building_id]

        for coords, (purpose, (section,
                               _)) in village_builder.plan.iteritems():
            if section > village_builder.current_section or purpose != building_purpose:
                continue

            object = village_builder.land_manager.island.ground_map[
                coords].object
            if object is not None and object.id == self.id:
                continue

            if building_purpose != BUILDING_PURPOSE.MAIN_SQUARE:
                if not self._need_producer(settlement_manager, coords,
                                           resource_id):
                    continue

            if not village_builder.have_resources(building_id):
                return (BUILD_RESULT.NEED_RESOURCES, None)
            if coords not in village_builder.settlement.buildability_cache.cache[
                    building_class.size]:
                position = Rect.init_from_topleft_and_size_tuples(
                    coords, building_class.size)
                return (BUILD_RESULT.OUT_OF_SETTLEMENT, position)

            building = BasicBuilder(building_id, coords,
                                    0).execute(settlement_manager.land_manager)
            assert building
            if self.get_purpose(
                    resource_id
            ) == BUILDING_PURPOSE.MAIN_SQUARE and not village_builder.roads_built:
                village_builder.build_roads()
            return (BUILD_RESULT.OK, building)
        return (BUILD_RESULT.SKIP, None)
    def _enlarge_collector_area(self):
        if not self.production_builder.have_resources(BUILDINGS.STORAGE):
            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, x, y, False)
            if not builder:
                continue

            coords_set = set(
                builder.position.get_radius_coordinates(
                    Entities.buildings[BUILDINGS.STORAGE].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].size
            tent_radius = Entities.buildings[BUILDINGS.RESIDENTIAL].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
Exemplo n.º 22
0
    def _enlarge_collector_area(self):
        if not self.production_builder.have_resources(BUILDINGS.STORAGE):
            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()
        coastline = self.land_manager.coastline

        # 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 coords not in coastline and purpose == BUILDING_PURPOSE.NONE:
                area_label[coords] = None

        areas = 0
        for coords in collector_area:
            assert coords not in coastline
            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)

        storage_class = Entities.buildings[BUILDINGS.STORAGE]
        storage_spots = self.island.terrain_cache.get_buildability_intersection(
            storage_class.terrain_type, storage_class.size,
            self.settlement.buildability_cache,
            self.production_builder.buildability_cache)
        storage_surrounding_offsets = Rect.get_surrounding_offsets(
            storage_class.size)

        options = []
        num_offsets = int(
            len(self._radius_offsets) * self.personality.overlap_precision)
        radius_offsets = self.session.random.sample(self._radius_offsets,
                                                    num_offsets)
        for coords in sorted(storage_spots):
            if coords not in area_label:
                continue
            x, y = coords

            area_number = area_label[coords]
            area_coords_set = coords_set_by_area[area_number]
            useful_area = 0
            for dx, dy in radius_offsets:
                coords = (x + dx, y + dy)
                if coords in area_coords_set:
                    useful_area += 1
            if not useful_area:
                continue

            alignment = 1
            builder = BasicBuilder.create(BUILDINGS.STORAGE, (x, y), 0)
            for (dx, dy) in storage_surrounding_offsets:
                coords = (x + dx, y + dy)
                if coords in coastline or 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].size
            tent_radius = Entities.buildings[BUILDINGS.RESIDENTIAL].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.village_builder.extend_settlement_with_tent(
                    Rect.init_from_topleft_and_size_tuples(
                        best_coords, tent_size))
        return BUILD_RESULT.IMPOSSIBLE
	def _enlarge_collector_area(self):
		if not self.production_builder.have_resources(BUILDINGS.STORAGE):
			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()
		coastline = self.land_manager.coastline

		# 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 coords not in coastline and purpose == BUILDING_PURPOSE.NONE:
				area_label[coords] = None

		areas = 0
		for coords in collector_area:
			assert coords not in coastline
			if coords in area_label and area_label[coords] is not None:
				continue

			queue = deque([coords])
			while queue:
				x, y = 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(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)

		storage_class = Entities.buildings[BUILDINGS.STORAGE]
		storage_spots = self.island.terrain_cache.get_buildability_intersection(storage_class.terrain_type,
			storage_class.size, self.settlement.buildability_cache, self.production_builder.buildability_cache)
		storage_surrounding_offsets = Rect.get_surrounding_offsets(storage_class.size)

		options = []
		num_offsets = int(len(self._radius_offsets) * self.personality.overlap_precision)
		radius_offsets = self.session.random.sample(self._radius_offsets, num_offsets)
		for coords in sorted(storage_spots):
			if coords not in area_label:
				continue
			x, y = coords

			area_number = area_label[coords]
			area_coords_set = coords_set_by_area[area_number]
			useful_area = 0
			for dx, dy in radius_offsets:
				coords = (x + dx, y + dy)
				if coords in area_coords_set:
					useful_area += 1
			if not useful_area:
				continue

			alignment = 1
			builder = BasicBuilder.create(BUILDINGS.STORAGE, (x, y), 0)
			for (dx, dy) in storage_surrounding_offsets:
				coords = (x + dx, y + dy)
				if coords in coastline or 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].size
			tent_radius = Entities.buildings[BUILDINGS.RESIDENTIAL].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.village_builder.extend_settlement_with_tent(Rect.init_from_topleft_and_size_tuples(best_coords, tent_size))
		return BUILD_RESULT.IMPOSSIBLE
	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):
			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, x, y, False)
			if not builder:
				continue

			coords_set = set(builder.position.get_radius_coordinates(Entities.buildings[BUILDINGS.STORAGE].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].size
			tent_radius = Entities.buildings[BUILDINGS.RESIDENTIAL].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
Exemplo n.º 26
0
 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)