Exemplo n.º 1
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)
Exemplo n.º 2
0
    def check_build(cls, session, point, rotation=45, check_settlement=True, ship=None, issuer=None):
        """Check if a building is buildable here.
		All tiles, that the building occupies are checked.
		@param point: Point instance, coords
		@param rotation: prefered rotation of building
		@param check_settlement: whether to check for a settlement (for settlementless buildings)
		@param ship: ship instance if building from ship
		@return instance of _BuildPosition"""
        # for non-quadratic buildings, we have to switch width and height depending on the rotation
        if rotation == 45 or rotation == 225:
            position = Rect.init_from_topleft_and_size(point.x, point.y, cls.size[0], cls.size[1])
        else:
            position = Rect.init_from_topleft_and_size(point.x, point.y, cls.size[1], cls.size[0])

        buildable = True
        problem = None
        tearset = []
        try:
            island = cls._check_island(session, position)
            # TODO: if the rotation changes here for non-quadratic buildings, wrong results will be returned
            rotation = cls._check_rotation(session, position, rotation)
            tearset = cls._check_buildings(session, position, island=island)
            cls._check_units(session, position)
            if check_settlement:
                cls._check_settlement(session, position, ship=ship, issuer=issuer)
        except _NotBuildableError as e:
            buildable = False
            problem = (e.errortype, _(BuildableErrorTypes.text[e.errortype]))

        return _BuildPosition(position, rotation, tearset, buildable, problem=problem)
	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.º 4
0
	def check_build(cls, session, point, rotation=45, check_settlement=True, ship=None, issuer=None):
		"""Check if a building is buildable here.
		All tiles, that the building occupies are checked.
		@param point: Point instance, coords
		@param rotation: preferred rotation of building
		@param check_settlement: whether to check for a settlement (for settlementless buildings)
		@param ship: ship instance if building from ship
		@return instance of _BuildPosition"""
		# for non-quadratic buildings, we have to switch width and height depending on the rotation
		if rotation in [45, 225]:
			position = Rect.init_from_topleft_and_size(point.x, point.y, cls.size[0], cls.size[1])
		else:
			position = Rect.init_from_topleft_and_size(point.x, point.y, cls.size[1], cls.size[0])

		buildable = True
		problem = None
		tearset = []
		try:
			island = cls._check_island(session, position)
			# TODO: if the rotation changes here for non-quadratic buildings, wrong results will be returned
			rotation = cls._check_rotation(session, position, rotation)
			tearset = cls._check_buildings(session, position, island=island)
			cls._check_units(session, position)
			if check_settlement:
				cls._check_settlement(session, position, ship=ship, issuer=issuer)
		except _NotBuildableError as e:
			buildable = False
			problem = (e.errortype, BuildableErrorTypes.text[e.errortype])

		return _BuildPosition(position, rotation, tearset, buildable, problem=problem)
Exemplo n.º 5
0
	def __init__(self, position, session, view, targetrenderer, imagemanager, renderer=None, world=None,
	             cam_border=True, use_rotation=True, on_click=None, preview=False, tooltip=None):
		"""
		@param position: a Rect or a Pychan Icon, where we will draw to
		@param world: World object or fake thereof
		@param view: View object for cam control. Can be None to disable this
		@param renderer: renderer to be used if position isn't an icon
		@param targetrenderer: fife target renderer for drawing on icons
		@param imagemanager: fife imagemanager for drawing on icons
		@param cam_border: boolean, whether to draw the cam border
		@param use_rotation: boolean, whether to use rotation (it must also be enabled in the settings)
		@param on_click: function taking 1 argument or None for scrolling
		@param preview: flag, whether to only show the map as preview
		@param tooltip: always show this tooltip when cursor hovers over minimap

		NOTE: Preview generation in a different process overwrites this method.
		"""
		if isinstance(position, Rect):
			self.location = position
			self.renderer = renderer
		else: # assume icon
			self.location = Rect.init_from_topleft_and_size(0, 0, position.width, position.height)
			self.icon = position
			self.use_overlay_icon(self.icon)

		# FIXME PY3 width / height of icon is sometimes zero. Why?
		if self.location.height == 0 or self.location.width == 0:
			self.location = Rect.init_from_topleft_and_size(0, 0, 128, 128)

		self.session = session
		self.world = world
		if self.world:
			self._update_world_to_minimap_ratio()
		self.view = view
		self.rotation = 0
		self.fixed_tooltip = tooltip

		self.click_handler = on_click if on_click is not None else self.default_on_click

		self.cam_border = cam_border
		self.use_rotation = use_rotation
		self.preview = preview

		self.location_center = self.location.center

		self._id = str(next(self.__class__.__minimap_id_counter)) # internal identifier, used for allocating resources

		self._image_size_cache = {} # internal detail

		self.imagemanager = imagemanager

		self.minimap_image = _MinimapImage(self, targetrenderer)

		self._rotation_setting = horizons.globals.fife.get_uh_setting("MinimapRotation")
		if self.use_rotation:
			SettingChanged.subscribe(self._on_setting_changed)
Exemplo n.º 6
0
	def check_build(cls, session, point, rotation=45, check_settlement=True, ship=None, issuer=None):
		# for non-quadratic buildings, we have to switch width and height depending on the rotation
		if rotation in [45, 225]:
			position = Rect.init_from_topleft_and_size(point.x, point.y, cls.size[0], cls.size[1])
		else:
			position = Rect.init_from_topleft_and_size(point.x, point.y, cls.size[1], cls.size[0])

		buildable = True
		tearset = []
		return _BuildPosition(position, rotation, tearset, buildable)
Exemplo n.º 7
0
    def check_build(cls, session, point, rotation=45, check_settlement=True, ship=None, issuer=None):
        # for non-quadratic buildings, we have to switch width and height depending on the rotation
        if rotation == 45 or rotation == 225:
            position = Rect.init_from_topleft_and_size(point.x, point.y, cls.size[0], cls.size[1])
        else:
            position = Rect.init_from_topleft_and_size(point.x, point.y, cls.size[1], cls.size[0])

        buildable = True
        tearset = []
        return _BuildPosition(position, rotation, tearset, buildable)
Exemplo n.º 8
0
	def __init__(self, position, session, view, targetrenderer, imagemanager, renderer=None, world=None,
	             cam_border=True, use_rotation=True, on_click=None, preview=False, tooltip=None, mousearea=None):
		"""
		@param position: a Rect or a Pychan Icon, where we will draw to
		@param world: World object or fake thereof
		@param view: View object for cam control. Can be None to disable this
		@param renderer: renderer to be used if position isn't an icon
		@param targetrenderer: fife target renderer for drawing on icons
		@param imagemanager: fife imagemanager for drawing on icons
		@param cam_border: boolean, whether to draw the cam border
		@param use_rotation: boolean, whether to use rotation
		@param on_click: function taking 1 argument or None for scrolling
		@param preview: flag, whether to only show the map as preview
		@param tooltip: always show this tooltip when cursor hovers over minimap

		NOTE: Preview generation in a different process overwrites this method.
		"""
		if isinstance(position, Rect):
			self.location = position
			self.renderer = renderer
		else: # assume icon
			self.location = Rect.init_from_topleft_and_size(0, 0, position.width, position.height)
			self.icon = position
			if mousearea is None:
				mousearea = self.icon
			self.use_overlay_icon(mousearea)

		# FIXME PY3 width / height of icon is sometimes zero. Why?
		if self.location.height == 0 or self.location.width == 0:
			self.location = Rect.init_from_topleft_and_size(0, 0, 128, 128)

		self.session = session
		self.world = world
		self.view = view
		self.fixed_tooltip = tooltip

		self.click_handler = on_click if on_click is not None else self.default_on_click

		self.cam_border = cam_border
		self.use_rotation = use_rotation
		self.preview = preview

		self.location_center = self.location.center

		self._id = str(next(self.__class__.__minimap_id_counter)) # internal identifier, used for allocating resources

		self._image_size_cache = {} # internal detail

		self.imagemanager = imagemanager

		self.minimap_image = _MinimapImage(self, targetrenderer)

		self.transform = None
Exemplo n.º 9
0
	def get_loading_area(cls, building_id, rotation, pos):
		if building_id == BUILDINGS.MOUNTAIN or building_id == BUILDINGS.MINE:
			if rotation == 45:
				return Rect.init_from_topleft_and_size(pos.origin.x, pos.origin.y + 1, 1, 3)
			elif rotation == 135:
				return Rect.init_from_topleft_and_size(pos.origin.x + 1, pos.origin.y + pos.height - 1, 3, 1)
			elif rotation == 225:
				return Rect.init_from_topleft_and_size(pos.origin.x + pos.width -1, pos.origin.y + 1, 1, 3)
			elif rotation == 315:
				return Rect.init_from_topleft_and_size(pos.origin.x + 1, pos.origin.y, 3, 1)
			assert False
		else:
			return pos
Exemplo n.º 10
0
	def get_loading_area(cls, building_id, rotation, pos):
		if building_id in [BUILDINGS.MOUNTAIN, BUILDINGS.MINE]:
			if rotation == 45:
				return Rect.init_from_topleft_and_size(pos.origin.x, pos.origin.y + 1, 1, 3)
			elif rotation == 135:
				return Rect.init_from_topleft_and_size(pos.origin.x + 1, pos.origin.y + pos.height - 1, 3, 1)
			elif rotation == 225:
				return Rect.init_from_topleft_and_size(pos.origin.x + pos.width - 1, pos.origin.y + 1, 1, 3)
			elif rotation == 315:
				return Rect.init_from_topleft_and_size(pos.origin.x + 1, pos.origin.y, 3, 1)
			assert False
		else:
			return pos
Exemplo n.º 11
0
def test_rect():
    r1 = Rect(Point(0, 0), 1, 1)
    r2 = Rect(0, 0, 1, 1)
    r3 = Rect(Point(2, 2), 1, 1)
    assert r1 == r2
    assert not r1.contains(Point(-1, -1))
    assert r2.contains(Point(0, 0))
    assert r2.contains(Point(1, 1))
    assert r1.intersects(r2)
    assert not r1.intersects(r3)
Exemplo n.º 12
0
 def testRect(self):
     r1 = Rect(Point(0, 0), 1, 1)
     r2 = Rect(0, 0, 1, 1)
     r3 = Rect(Point(2, 2), 1, 1)
     self.assertEqual(r1, r2)
     self.assertTrue(r1 == r2)
     self.assertFalse(r1.contains(Point(-1, -1)))
     self.assertTrue(r2.contains(Point(0, 0)))
     self.assertTrue(r2.contains(Point(1, 1)))
     self.assertTrue(r1.intersects(r2))
     self.assertFalse(r1.intersects(r3))
    def _build_extra_road_connection(self, building, collector_building):
        collector_coords = set(
            coords
            for coords in self.production_builder.iter_possible_road_coords(
                collector_building.position, collector_building.position
            )
        )
        destination_coords = set(
            coords
            for coords in self.production_builder.iter_possible_road_coords(building.loading_area, building.position)
        )
        pos = building.loading_area
        beacon = Rect.init_from_borders(pos.left - 1, pos.top - 1, pos.right + 1, pos.bottom + 1)

        path = RoadPlanner()(
            self.owner.personality_manager.get("RoadPlanner"),
            collector_coords,
            destination_coords,
            beacon,
            self.production_builder.get_path_nodes(),
        )
        if path is None:
            return BUILD_RESULT.IMPOSSIBLE

        cost = self.production_builder.get_road_cost(path)
        for resource_id, amount in cost.iteritems():
            if resource_id == RES.GOLD:
                if self.owner.get_component(StorageComponent).inventory[resource_id] < amount:
                    return BUILD_RESULT.NEED_RESOURCES
            elif self.settlement.get_component(StorageComponent).inventory[resource_id] < amount:
                return BUILD_RESULT.NEED_RESOURCES
        return BUILD_RESULT.OK if self.production_builder.build_road(path) else BUILD_RESULT.UNKNOWN_ERROR
Exemplo n.º 14
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.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')
Exemplo n.º 15
0
    def extend_settlement_with_storage(self, target_position):
        """Build a storage to extend the settlement towards the given position. Return a BUILD_RESULT constant."""
        if not self.have_resources(BUILDINGS.STORAGE):
            return BUILD_RESULT.NEED_RESOURCES

        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.buildability_cache)
        storage_surrounding_offsets = Rect.get_surrounding_offsets(
            storage_class.size)
        coastline = self.land_manager.coastline

        options = []
        for (x, y) in sorted(storage_spots):
            builder = BasicBuilder.create(BUILDINGS.STORAGE, (x, y), 0)

            alignment = 1
            for (dx, dy) in storage_surrounding_offsets:
                coords = (x + dx, y + dy)
                if coords in coastline or coords not in self.plan or self.plan[
                        coords][0] != BUILDING_PURPOSE.NONE:
                    alignment += 1

            distance = distances.distance_rect_rect(target_position,
                                                    builder.position)
            value = distance - alignment * 0.7
            options.append((-value, builder))
        return self.build_best_option(options, BUILDING_PURPOSE.STORAGE)
Exemplo n.º 16
0
	def check_build_line(cls, session, point1, point2, rotation=45, ship=None):
		if point1 == point2:
			# this is actually a masked single build
			return [cls.check_build_fuzzy(session, point1, rotation=rotation, ship=ship)]
		possible_builds = []
		area = Rect.init_from_corners(point1, point2)
		# correct placement for large buildings (mouse should be at center of building)
		area.left -= (cls.size[0] - 1) // 2
		area.right -= (cls.size[0] - 1) // 2
		area.top -= (cls.size[1] - 1) // 2
		area.bottom -= (cls.size[1] - 1) // 2

		xstart, xend = area.left, area.right + 1
		xstep = cls.size[0]
		if point1.x > point2.x:
			xstart, xend = area.right, area.left - 1
			xstep *= -1

		ystart, yend = area.top, area.bottom + 1
		ystep = cls.size[1]
		if point1.y > point2.y:
			ystart, yend = area.bottom, area.top - 1
			ystep *= -1

		for x in range(xstart, xend, xstep):
			for y in range(ystart, yend, ystep):
				possible_builds.append(
				  cls.check_build(session, Point(x, y), rotation=rotation, ship=ship)
				)
		return possible_builds
Exemplo n.º 17
0
	def extend_settlement_with_storage(self, target_position):
		"""Build a storage to extend the settlement towards the given position. Return a BUILD_RESULT constant."""
		if not self.have_resources(BUILDINGS.STORAGE):
			return BUILD_RESULT.NEED_RESOURCES

		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.buildability_cache)
		storage_surrounding_offsets = Rect.get_surrounding_offsets(storage_class.size)
		coastline = self.land_manager.coastline

		options = []
		for (x, y) in sorted(storage_spots):
			builder = BasicBuilder.create(BUILDINGS.STORAGE, (x, y), 0)

			alignment = 1
			for (dx, dy) in storage_surrounding_offsets:
				coords = (x + dx, y + dy)
				if coords in coastline or coords not in self.plan or self.plan[coords][0] != BUILDING_PURPOSE.NONE:
					alignment += 1

			distance = distances.distance_rect_rect(target_position, builder.position)
			value = distance - alignment * 0.7
			options.append((-value, builder))
		return self.build_best_option(options, BUILDING_PURPOSE.STORAGE)
	def extend_settlement_with_tent(self, position):
		"""Build a tent to extend the settlement towards the given position. Return a BUILD_RESULT constant."""
		distance_rect_rect = distances.distance_rect_rect
		size = Entities.buildings[BUILDINGS.RESIDENTIAL].size
		min_distance = None
		best_coords = None

		for (x, y) in self.tent_queue:
			ok = True
			for dx in xrange(size[0]):
				for dy in xrange(size[1]):
					if (x + dx, y + dy) not in self.settlement.ground_map:
						ok = False
						break
			if not ok:
				continue

			distance = distance_rect_rect(Rect.init_from_topleft_and_size(x, y, size[0], size[1]), position)
			if min_distance is None or distance < min_distance:
				min_distance = distance
				best_coords = (x, y)

		if min_distance is None:
			return BUILD_RESULT.IMPOSSIBLE
		return self.build_tent(best_coords)
Exemplo n.º 19
0
    def _get_road_to_builder(self, builder):
        """Return a path from the builder to a building with general collectors (None if impossible)."""
        loading_area = builder.get_loading_area()
        collector_coords = set()
        for building in self.collector_buildings:
            if loading_area.distance(building.position) == 1:
                return []
            if loading_area.distance(building.position) > building.radius:
                continue  # the collector building is too far to be useful
            for coords in self.iter_possible_road_coords(
                    building.position, building.position):
                collector_coords.add(coords)

        destination_coords = set(
            self.iter_possible_road_coords(loading_area, builder.position))
        if self is self.settlement_manager.production_builder:
            if not self.settlement_manager.production_builder.road_connectivity_cache.is_connection_possible(
                    collector_coords, destination_coords):
                return None

        blocked_coords = set([
            coords for coords in builder.position.tuple_iter()
        ]).union(self.land_manager.coastline)
        beacon = Rect.init_from_borders(loading_area.left - 1,
                                        loading_area.top - 1,
                                        loading_area.right + 1,
                                        loading_area.bottom + 1)

        return RoadPlanner()(self.owner.personality_manager.get('RoadPlanner'),
                             collector_coords,
                             destination_coords,
                             beacon,
                             self.get_path_nodes(),
                             blocked_coords=blocked_coords)
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')
Exemplo n.º 21
0
def create_map():
	"""
	Create a map with a square island (20x20) at position (20, 20) and return the path
	to the database file.
	"""

	tiles = []
	for x, y in Rect.init_from_topleft_and_size(0, 0, 20, 20).tuple_iter():
		if (0 < x < 20) and (0 < y < 20):
			ground = GROUND.DEFAULT_LAND
		else:
			# Add coastline at the borders.
			ground = GROUND.SHALLOW_WATER
		tiles.append([0, 20 + x, 20 + y] + list(ground))

	fd, map_file = tempfile.mkstemp()
	os.close(fd)

	db = DbReader(map_file)
	with open('content/map-template.sql') as map_template:
		db.execute_script(map_template.read())

	db('BEGIN')
	db.execute_many("INSERT INTO ground VALUES(?, ?, ?, ?, ?, ?)", tiles)
	db('COMMIT')
	db.close()
	return map_file
Exemplo n.º 22
0
    def _build_extra_road_connection(self, building, collector_building):
        collector_coords = set(
            coords
            for coords in self.production_builder.iter_possible_road_coords(
                collector_building.position, collector_building.position))
        destination_coords = set(
            coords
            for coords in self.production_builder.iter_possible_road_coords(
                building.loading_area, building.position))
        pos = building.loading_area
        beacon = Rect.init_from_borders(pos.left - 1, pos.top - 1,
                                        pos.right + 1, pos.bottom + 1)

        path = RoadPlanner()(self.owner.personality_manager.get('RoadPlanner'),
                             collector_coords, destination_coords, beacon,
                             self.production_builder.get_path_nodes())
        if path is None:
            return BUILD_RESULT.IMPOSSIBLE

        cost = self.production_builder.get_road_cost(path)
        for resource_id, amount in cost.items():
            if resource_id == RES.GOLD:
                if self.owner.get_component(
                        StorageComponent).inventory[resource_id] < amount:
                    return BUILD_RESULT.NEED_RESOURCES
            elif self.settlement.get_component(
                    StorageComponent).inventory[resource_id] < amount:
                return BUILD_RESULT.NEED_RESOURCES
        return BUILD_RESULT.OK if self.production_builder.build_road(
            path) else BUILD_RESULT.UNKNOWN_ERROR
Exemplo n.º 23
0
def create_map():
    """
	Create a map with a square island (20x20) at position (20, 20) and return the path
	to the database file.
	"""

    tiles = []
    for x, y in Rect.init_from_topleft_and_size(0, 0, 20, 20).tuple_iter():
        if (0 < x < 20) and (0 < y < 20):
            ground = GROUND.DEFAULT_LAND
        else:
            # Add coastline at the borders.
            ground = GROUND.SHALLOW_WATER
        tiles.append([0, 20 + x, 20 + y] + list(ground))

    fd, map_file = tempfile.mkstemp()
    os.close(fd)

    db = DbReader(map_file)
    with open('content/map-template.sql') as map_template:
        db.execute_script(map_template.read())

    db('BEGIN')
    db.execute_many("INSERT INTO ground VALUES(?, ?, ?, ?, ?, ?)", tiles)
    db('COMMIT')
    db.close()
    return map_file
Exemplo n.º 24
0
    def extend_settlement_with_tent(self, position):
        """Build a tent to extend the settlement towards the given position. Return a BUILD_RESULT constant."""
        distance_rect_rect = distances.distance_rect_rect
        size = Entities.buildings[BUILDINGS.RESIDENTIAL].size
        min_distance = None
        best_coords = None

        for (x, y) in self.tent_queue:
            ok = True
            for dx in xrange(size[0]):
                for dy in xrange(size[1]):
                    if (x + dx, y + dy) not in self.settlement.ground_map:
                        ok = False
                        break
            if not ok:
                continue

            distance = distance_rect_rect(
                Rect.init_from_topleft_and_size(x, y, size[0], size[1]),
                position)
            if min_distance is None or distance < min_distance:
                min_distance = distance
                best_coords = (x, y)

        if min_distance is None:
            return BUILD_RESULT.IMPOSSIBLE
        return self.build_tent(best_coords)
	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)
Exemplo n.º 26
0
    def check_build_line(cls, session, point1, point2, rotation=45, ship=None):
        if point1 == point2:
            # this is actually a masked single build
            return [cls.check_build_fuzzy(session, point1, rotation=rotation, ship=ship)]
        possible_builds = []
        area = Rect.init_from_corners(point1, point2)
        # correct placement for large buildings (mouse should be at center of building)
        area.left -= (cls.size[0] - 1) / 2
        area.right -= (cls.size[0] - 1) / 2
        area.top -= (cls.size[1] - 1) / 2
        area.bottom -= (cls.size[1] - 1) / 2

        xstart, xend = area.left, area.right + 1
        xstep = cls.size[0]
        if point1.x > point2.x:
            xstart, xend = area.right, area.left - 1
            xstep *= -1

        ystart, yend = area.top, area.bottom + 1
        ystep = cls.size[1]
        if point1.y > point2.y:
            ystart, yend = area.bottom, area.top - 1
            ystep *= -1

        for x in xrange(xstart, xend, xstep):
            for y in xrange(ystart, yend, ystep):
                possible_builds.append(cls.check_build(session, Point(x, y), rotation=rotation, ship=ship))
        return possible_builds
Exemplo n.º 27
0
    def __init(self, db, island_id, preview):
        """
		Load the actual island from a file
		@param preview: flag, map preview mode
		"""
        p_x, p_y, width, height = db(
            "SELECT MIN(x), MIN(y), (1 + MAX(x) - MIN(x)), (1 + MAX(y) - MIN(y)) FROM ground WHERE island_id = ?",
            island_id - 1001)[0]

        # rect for quick checking if a tile isn't on this island
        # NOTE: it contains tiles, that are not on the island!
        self.rect = Rect(Point(p_x, p_y), width, height)

        self.ground_map = {}
        for (x, y, ground_id, action_id, rotation) in db(
                "SELECT x, y, ground_id, action_id, rotation FROM ground WHERE island_id = ?",
                island_id - 1001):  # Load grounds
            if not preview:  # actual game, need actual tiles
                ground = Entities.grounds[str(
                    '%d-%s' % (ground_id, action_id))](self.session, x, y)
                ground.act(rotation)
            else:
                ground = MapPreviewTile(x, y, ground_id)
            # These are important for pathfinding and building to check if the ground tile
            # is blocked in any way.
            self.ground_map[(ground.x, ground.y)] = ground

        self._init_cache()

        self.settlements = []
        self.wild_animals = []
        self.num_trees = 0

        # define the rectangle with the smallest area that contains every island tile its position
        min_x = min(zip(*self.ground_map.keys())[0])
        max_x = max(zip(*self.ground_map.keys())[0])
        min_y = min(zip(*self.ground_map.keys())[1])
        max_y = max(zip(*self.ground_map.keys())[1])
        self.position = Rect.init_from_borders(min_x, min_y, max_x, max_y)

        if not preview:  # this isn't needed for previews, but it is in actual games
            self.path_nodes = IslandPathNodes(self)

            # repopulate wild animals every 2 mins if they die out.
            Scheduler().add_new_object(self.check_wild_animal_population, self,
                                       Scheduler().get_ticks(120), -1)
        """TUTORIAL:
Exemplo n.º 28
0
    def build_tent(self, coords=None):
        """Build the next tent (or the specified one if coords is not None)."""
        if not self.tent_queue:
            return BUILD_RESULT.IMPOSSIBLE

        # can_trigger_next_section is used to start building the next section when the old one is done
        # if a tent is built just to extend the area then that can't trigger the next section
        # TODO: handle extension tents nicer than just letting them die
        can_trigger_next_section = False
        if coords is None:
            coords = self.tent_queue[0]
            can_trigger_next_section = True

        ok = True
        x, y = coords
        owned_by_other = False
        size = Entities.buildings[BUILDINGS.RESIDENTIAL].size
        for dx in xrange(size[0]):
            for dy in xrange(size[1]):
                coords2 = (x + dx, y + dy)
                if coords2 not in self.settlement.ground_map:
                    ok = False
                    if self.island.ground_map[coords2].settlement is not None:
                        owned_by_other = True

        if ok and not owned_by_other:
            if not self.have_resources(BUILDINGS.RESIDENTIAL):
                return BUILD_RESULT.NEED_RESOURCES
            assert BasicBuilder(BUILDINGS.RESIDENTIAL, (x, y),
                                0).execute(self.land_manager)

        if ok or owned_by_other:
            if self.tent_queue[0] == coords:
                self.tent_queue.popleft()
            else:
                for i in xrange(len(self.tent_queue)):
                    if self.tent_queue[i] == coords:
                        del self.tent_queue[i]
                        break
            if owned_by_other:
                self.log.debug(
                    '%s tent position owned by other player at (%d, %d)', self,
                    x, y)
                return BUILD_RESULT.IMPOSSIBLE

        if not ok:
            # need to extends the area, it is not owned by another player
            self.log.debug(
                '%s tent position not owned by the player at (%d, %d), extending settlement area instead',
                self, x, y)
            return self.extend_settlement(
                Rect.init_from_topleft_and_size(x, y, size[0], size[1]))

        if not self.roads_built:
            self.build_roads()
        if can_trigger_next_section and self.plan[coords][1][
                0] > self.current_section:
            self.current_section = self.plan[coords][1][0]
        return BUILD_RESULT.OK
Exemplo n.º 29
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)
	def _init_radius_offsets(cls):
		building_class = Entities.buildings[BUILDINGS.STORAGE]
		size = building_class.size
		assert size[0] == size[1]
		rect = Rect.init_from_topleft_and_size(0, 0, size[0], size[1])

		cls._radius_offsets = []
		for coords in rect.get_radius_coordinates(building_class.radius):
			cls._radius_offsets.append(coords)
Exemplo n.º 31
0
    def _init_radius_offsets(cls):
        building_class = Entities.buildings[BUILDINGS.STORAGE]
        size = building_class.size
        assert size[0] == size[1]
        rect = Rect.init_from_topleft_and_size(0, 0, size[0], size[1])

        cls._radius_offsets = []
        for coords in rect.get_radius_coordinates(building_class.radius):
            cls._radius_offsets.append(coords)
	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.º 33
0
	def load_raw_map(self, savegame_db, preview=False):
		self.map_name = savegame_db.map_name

		# load islands
		self.islands = []
		for (islandid,) in savegame_db("SELECT DISTINCT island_id + 1001 FROM ground"):
			island = Island(savegame_db, islandid, self.session, preview=preview)
			self.islands.append(island)

		#calculate map dimensions
		self.min_x, self.min_y, self.max_x, self.max_y = 0, 0, 0, 0
		for island in self.islands:
			self.min_x = min(island.position.left, self.min_x)
			self.min_y = min(island.position.top, self.min_y)
			self.max_x = max(island.position.right, self.max_x)
			self.max_y = max(island.position.bottom, self.max_y)
		self.min_x -= savegame_db.map_padding
		self.min_y -= savegame_db.map_padding
		self.max_x += savegame_db.map_padding
		self.max_y += savegame_db.map_padding

		self.map_dimensions = Rect.init_from_borders(self.min_x, self.min_y, self.max_x, self.max_y)

		#add water
		self.log.debug("Filling world with water...")
		self.ground_map = {}

		# big sea water tile class
		if not preview:
			default_grounds = Entities.grounds[self.properties.get('default_ground', '%d-straight' % GROUND.WATER[0])]

		fake_tile_class = Entities.grounds['-1-special']
		fake_tile_size = 10
		for x in xrange(self.min_x-MAP.BORDER, self.max_x+MAP.BORDER, fake_tile_size):
			for y in xrange(self.min_y-MAP.BORDER, self.max_y+MAP.BORDER, fake_tile_size):
				fake_tile_x = x - 1
				fake_tile_y = y + fake_tile_size - 1
				if not preview:
					# we don't need no references, we don't need no mem control
					default_grounds(self.session, fake_tile_x, fake_tile_y)
				for x_offset in xrange(fake_tile_size):
					if self.min_x <= x + x_offset < self.max_x:
						for y_offset in xrange(fake_tile_size):
							if self.min_y <= y + y_offset < self.max_y:
								self.ground_map[(x+x_offset, y+y_offset)] = fake_tile_class(self.session, fake_tile_x, fake_tile_y)
		self.fake_tile_map = copy.copy(self.ground_map)

		# remove parts that are occupied by islands, create the island map and the full map
		self.island_map = {}
		self.full_map = copy.copy(self.ground_map)
		for island in self.islands:
			for coords in island.ground_map:
				if coords in self.ground_map:
					self.full_map[coords] = island.ground_map[coords]
					del self.ground_map[coords]
					self.island_map[coords] = island
Exemplo n.º 34
0
	def get_displayed_area(self):
		"""Returns the coords of what is displayed on the screen as Rect"""
		coords = self.cam.getLocation().getLayerCoordinates()
		cell_dim = self.cam.getCellImageDimensions()
		width_x = horizons.globals.fife.engine_settings.getScreenWidth() // cell_dim.x + 1
		width_y = horizons.globals.fife.engine_settings.getScreenHeight() // cell_dim.y + 1
		screen_width_as_coords = (width_x // self.zoom, width_y // self.zoom)
		return Rect.init_from_topleft_and_size(coords.x - (screen_width_as_coords[0] // 2),
		                                       coords.y - (screen_width_as_coords[1] // 2),
		                                       *screen_width_as_coords)
Exemplo n.º 35
0
	def get_displayed_area(self):
		"""Returns the coords of what is displayed on the screen as Rect"""
		coords = self.cam.getLocationRef().getLayerCoordinates()
		cell_dim = self.cam.getCellImageDimensions()
		width_x = horizons.globals.fife.engine_settings.getScreenWidth() // cell_dim.x + 1
		width_y = horizons.globals.fife.engine_settings.getScreenHeight() // cell_dim.y + 1
		screen_width_as_coords = (width_x // self.zoom, width_y // self.zoom)
		return Rect.init_from_topleft_and_size(coords.x - (screen_width_as_coords[0] // 2),
		                                       coords.y - (screen_width_as_coords[1] // 2),
		                                       *screen_width_as_coords)
Exemplo n.º 36
0
	def build_tent(self, coords=None):
		"""Build the next tent (or the specified one if coords is not None)."""
		if not self.tent_queue:
			return BUILD_RESULT.IMPOSSIBLE

		# can_trigger_next_section is used to start building the next section when the old one is done
		# if a tent is built just to extend the area then that can't trigger the next section
		# TODO: handle extension tents nicer than just letting them die
		can_trigger_next_section = False
		if coords is None:
			coords = self.tent_queue[0]
			can_trigger_next_section = True

		ok = True
		x, y = coords
		owned_by_other = False
		size = Entities.buildings[BUILDINGS.RESIDENTIAL].size
		for dx in xrange(size[0]):
			for dy in xrange(size[1]):
				coords2 = (x + dx, y + dy)
				if coords2 not in self.settlement.ground_map:
					ok = False
					if self.island.ground_map[coords2].settlement is not None:
						owned_by_other = True

		if ok and not owned_by_other:
			builder = self.make_builder(BUILDINGS.RESIDENTIAL, x, y, False)
			if not builder.have_resources():
				return BUILD_RESULT.NEED_RESOURCES
			if not builder.execute():
				self.log.debug('%s unable to build a tent at (%d, %d)', self, x, y)
				return BUILD_RESULT.UNKNOWN_ERROR

		if ok or owned_by_other:
			if self.tent_queue[0] == coords:
				self.tent_queue.popleft()
			else:
				for i in xrange(len(self.tent_queue)):
					if self.tent_queue[i] == coords:
						del self.tent_queue[i]
						break
			if owned_by_other:
				self.log.debug('%s tent position owned by other player at (%d, %d)', self, x, y)
				return BUILD_RESULT.IMPOSSIBLE

		if not ok:
			# need to extends the area, it is not owned by another player
			self.log.debug('%s tent position not owned by the player at (%d, %d), extending settlement area instead', self, x, y)
			return self.extend_settlement(Rect.init_from_topleft_and_size(x, y, size[0], size[1]))

		if not self.roads_built:
			self.build_roads()
		if can_trigger_next_section and self.plan[coords][1][0] > self.current_section:
			self.current_section = self.plan[coords][1][0]
		return BUILD_RESULT.OK
Exemplo n.º 37
0
	def load_raw_map(self, savegame_db, preview=False):
		self.map_name = savegame_db.map_name

		# Load islands.
		for (islandid,) in savegame_db("SELECT DISTINCT island_id + 1001 FROM ground"):
			island = Island(savegame_db, islandid, self.session, preview=preview)
			self.islands.append(island)

		# Calculate map dimensions.
		self.min_x, self.min_y, self.max_x, self.max_y = 0, 0, 0, 0
		for island in self.islands:
			self.min_x = min(island.position.left, self.min_x)
			self.min_y = min(island.position.top, self.min_y)
			self.max_x = max(island.position.right, self.max_x)
			self.max_y = max(island.position.bottom, self.max_y)
		self.min_x -= savegame_db.map_padding
		self.min_y -= savegame_db.map_padding
		self.max_x += savegame_db.map_padding
		self.max_y += savegame_db.map_padding

		self.map_dimensions = Rect.init_from_borders(self.min_x, self.min_y, self.max_x, self.max_y)

		# Add water.
		self.log.debug("Filling world with water...")
		self.ground_map = {}

		# big sea water tile class
		if not preview:
			default_grounds = Entities.grounds[self.properties.get('default_ground', '%d-straight' % GROUND.WATER[0])]

		fake_tile_class = Entities.grounds['-1-special']
		fake_tile_size = 10
		for x in xrange(self.min_x-MAP.BORDER, self.max_x+MAP.BORDER, fake_tile_size):
			for y in xrange(self.min_y-MAP.BORDER, self.max_y+MAP.BORDER, fake_tile_size):
				fake_tile_x = x - 1
				fake_tile_y = y + fake_tile_size - 1
				if not preview:
					# we don't need no references, we don't need no mem control
					default_grounds(self.session, fake_tile_x, fake_tile_y)
				for x_offset in xrange(fake_tile_size):
					if self.min_x <= x + x_offset < self.max_x:
						for y_offset in xrange(fake_tile_size):
							if self.min_y <= y + y_offset < self.max_y:
								self.ground_map[(x+x_offset, y+y_offset)] = fake_tile_class(self.session, fake_tile_x, fake_tile_y)
		self.fake_tile_map = copy.copy(self.ground_map)

		# Remove parts that are occupied by islands, create the island map and the full map.
		self.island_map = {}
		self.full_map = copy.copy(self.ground_map)
		for island in self.islands:
			for coords in island.ground_map:
				if coords in self.ground_map:
					self.full_map[coords] = island.ground_map[coords]
					del self.ground_map[coords]
					self.island_map[coords] = island
Exemplo n.º 38
0
	def __init__(self, position, session, view, targetrenderer, imagemanager, renderer=None, world=None,
	             cam_border=True, use_rotation=True, on_click=None, preview=False, tooltip=None):
		"""
		@param position: a Rect or a Pychan Icon, where we will draw to
		@param world: World object or fake thereof
		@param view: View object for cam control. Can be None to disable this
		@param renderer: renderer to be used if position isn't an icon
		@param targetrenderer: fife target rendererfor drawing on icons
		@param imagemanager: fife imagemanager for drawing on icons
		@param cam_border: boolean, whether to draw the cam border
		@param use_rotation: boolean, whether to use rotation (it must also be enabled in the settings)
		@param on_click: function taking 1 argument or None for scrolling
		@param preview: flag, whether to only show the map as preview
		@param tooltip: always show this tooltip when cursor hovers over minimap
		"""
		if isinstance(position, Rect):
			self.location = position
			self.renderer = renderer
		else: # assume icon
			self.location = Rect.init_from_topleft_and_size(0, 0, position.width, position.height)
			self.icon = position
			self.use_overlay_icon(self.icon)
		self.session = session
		self.world = world
		if self.world:
			self._update_world_to_minimap_ratio()
		self.view = view
		self.rotation = 0
		self.fixed_tooltip = tooltip

		if on_click is not None:
			self.on_click = on_click

		self.cam_border = cam_border
		self.use_rotation = use_rotation
		self.preview = preview

		self.location_center = self.location.center

		self._id = str(self.__class__.__minimap_id_counter.next()) # internal identifier, used for allocating resources

		self._image_size_cache = {} # internal detail

		self.imagemanager = imagemanager

		self.minimap_image = _MinimapImage(self, targetrenderer)

		#import random
		#ExtScheduler().add_new_object(lambda : self.highlight( (50+random.randint(-50,50), random.randint(-50,50) + 50 )), self, 2, loops=-1)

		self._rotation_setting = horizons.globals.fife.get_uh_setting("MinimapRotation")
		if self.use_rotation:
			MinimapRotationSettingChanged.subscribe(self._on_rotation_setting_change)
Exemplo n.º 39
0
	def __init__(self, position, session, view, targetrenderer, imagemanager, renderer=None, world=None,
	             cam_border=True, use_rotation=True, on_click=None, preview=False, tooltip=None):
		"""
		@param position: a Rect or a Pychan Icon, where we will draw to
		@param world: World object or fake thereof
		@param view: View object for cam control. Can be None to disable this
		@param renderer: renderer to be used if position isn't an icon
		@param targetrenderer: fife target rendererfor drawing on icons
		@param imagemanager: fife imagemanager for drawing on icons
		@param cam_border: boolean, whether to draw the cam border
		@param use_rotation: boolean, whether to use rotation (it must also be enabled in the settings)
		@param on_click: function taking 1 argument or None for scrolling
		@param preview: flag, whether to only show the map as preview
		@param tooltip: always show this tooltip when cursor hovers over minimap
		"""
		if isinstance(position, Rect):
			self.location = position
			self.renderer = renderer
		else: # assume icon
			self.location = Rect.init_from_topleft_and_size(0, 0, position.width, position.height)
			self.icon = position
			self.use_overlay_icon(self.icon)
		self.session = session
		self.world = world
		if self.world:
			self._update_world_to_minimap_ratio()
		self.view = view
		self.rotation = 0
		self.fixed_tooltip = tooltip

		if on_click is not None:
			self.on_click = on_click

		self.cam_border = cam_border
		self.use_rotation = use_rotation
		self.preview = preview

		self.location_center = self.location.center

		self._id = str(self.__class__.__minimap_id_counter.next()) # internal identifier, used for allocating resources

		self._image_size_cache = {} # internal detail

		self.imagemanager = imagemanager

		self.minimap_image = _MinimapImage(self, targetrenderer)

		#import random
		#ExtScheduler().add_new_object(lambda : self.highlight( (50+random.randint(-50,50), random.randint(-50,50) + 50 )), self, 2, loops=-1)

		self._rotation_setting = horizons.globals.fife.get_uh_setting("MinimapRotation")
		if self.use_rotation:
			MinimapRotationSettingChanged.subscribe(self._on_rotation_setting_change)
Exemplo n.º 40
0
	def load(cls, db, worldid, session, island):
		self = cls.__new__(cls)
		self.session = session
		super(Settlement, self).load(db, worldid)

		owner = db("SELECT owner FROM settlement WHERE rowid = ?", worldid)[0][0]
		upgrade_permissions = {}
		tax_settings = {}
		for level, allowed, tax in db("SELECT level, upgrading_allowed, tax_setting FROM settlement_level_properties WHERE settlement = ?", worldid):
			upgrade_permissions[level] = allowed
			tax_settings[level] = tax
		self.__init(session, WorldObject.get_object_by_id(owner), upgrade_permissions, tax_settings)

		try:
			# normal tile loading for new savegames
			tile_data = db("SELECT data FROM settlement_tiles WHERE rowid = ?", worldid)[0][0]
			tile_data = json.loads(tile_data)
			for (x, y) in tile_data: # NOTE: json saves tuples as list
				tup = (x, y)
				tile = island.ground_map[tup]
				self.ground_map[tup] = tile
				tile.settlement = self
		except sqlite3.OperationalError:
			print "Updating data of outdated savegame.."
			# old savegame, create settlement tiles provisionally (not correct, but useable)
			# TODO: remove when there aren't any savegames from before december 2011 any more
			for b_type, x, y in db("SELECT type, x, y FROM building WHERE location = ?", worldid):
				cls = Entities.buildings[b_type]
				position = Rect.init_from_topleft_and_size(x, y, cls.size[0], cls.size[1])
				for coord in position.get_radius_coordinates(cls.radius, include_self=True):
					tile = island.get_tile_tuple(coord)
					if tile is not None:
						if tile.settlement is None:
							self.ground_map[coord] = island.ground_map[coord]
							tile.settlement = self

		# load super here cause basic stuff is just set up now

		# load all buildings from this settlement
		# the buildings will expand the area of the settlement by adding everything,
		# that is in the radius of the building, to the settlement.
		from horizons.world import load_building
		for building_id, building_type in \
			  db("SELECT rowid, type FROM building WHERE location = ?", worldid):
			building = load_building(session, db, building_type, building_id)
			if building_type == BUILDINGS.WAREHOUSE:
				self.warehouse = building

		for res, amount in db("SELECT res, amount FROM settlement_produced_res WHERE settlement = ?", worldid):
			self.produced_res[res] = amount

		return self
Exemplo n.º 41
0
    def __init(self, db, island_id, preview):
        """
		Load the actual island from a file
		@param preview: flag, map preview mode
		"""
        p_x, p_y, width, height = db(
            "SELECT MIN(x), MIN(y), (1 + MAX(x) - MIN(x)), (1 + MAX(y) - MIN(y)) FROM ground WHERE island_id = ?",
            island_id - 1001)[0]

        self.ground_map = {}
        for (x, y, ground_id, action_id, rotation) in db(
                "SELECT x, y, ground_id, action_id, rotation FROM ground WHERE island_id = ?",
                island_id - 1001):  # Load grounds
            if not preview:  # actual game, need actual tiles
                ground = Entities.grounds[str('{:d}-{}'.format(
                    ground_id, action_id))](self.session, x, y)
                ground.act(rotation)
            else:
                ground = MapPreviewTile(x, y, ground_id)
            # These are important for pathfinding and building to check if the ground tile
            # is blocked in any way.
            self.ground_map[(ground.x, ground.y)] = ground

        self._init_cache()

        # Contains references to all resource deposits (but not mines)
        # on the island, regardless of the owner:
        # {building_id: {(x, y): building_instance, ...}, ...}
        self.deposits = defaultdict(dict)

        self.settlements = []
        self.wild_animals = []
        self.num_trees = 0

        # define the rectangle with the smallest area that contains every island tile its position
        min_x = min(list(zip(*self.ground_map.keys()))[0])
        max_x = max(list(zip(*self.ground_map.keys()))[0])
        min_y = min(list(zip(*self.ground_map.keys()))[1])
        max_y = max(list(zip(*self.ground_map.keys()))[1])
        self.position = Rect.init_from_borders(min_x, min_y, max_x, max_y)

        if not preview:
            # This isn't needed for map previews, but it is in actual games.
            self.path_nodes = IslandPathNodes(self)
            self.barrier_nodes = IslandBarrierNodes(self)

            # Repopulate wild animals every 2 mins if they die out.
            Scheduler().add_new_object(self.check_wild_animal_population,
                                       self,
                                       run_in=Scheduler().get_ticks(120),
                                       loops=-1)
        """TUTORIAL:
Exemplo n.º 42
0
	def load_raw_map(self, savegame_db, preview=False):
		# load islands
		self.islands = []
		for (islandid,) in savegame_db("SELECT rowid + 1000 FROM island"):
			island = Island(savegame_db, islandid, self.session, preview=preview)
			self.islands.append(island)

		#calculate map dimensions
		self.min_x, self.min_y, self.max_x, self.max_y = 0, 0, 0, 0
		for i in self.islands:
			self.min_x = min(i.rect.left, self.min_x)
			self.min_y = min(i.rect.top, self.min_y)
			self.max_x = max(i.rect.right, self.max_x)
			self.max_y = max(i.rect.bottom, self.max_y)
		self.min_x -= 10
		self.min_y -= 10
		self.max_x += 10
		self.max_y += 10

		self.map_dimensions = Rect.init_from_borders(self.min_x, self.min_y, self.max_x, self.max_y)

		#add water
		self.log.debug("Filling world with water...")
		self.ground_map = {}

		# big sea water tile class
		if not preview:
			default_grounds = Entities.grounds[int(self.properties.get('default_ground', GROUND.WATER[0]))]

		# extra world size that is added so that the player can't see the "black void"
		border = 30
		fake_tile_class = Entities.grounds[-1]
		for x in xrange(self.min_x-border, self.max_x+border, 10):
			for y in xrange(self.min_y-border, self.max_y+border, 10):
				if not preview:
					# we don't need no references, we don't need no mem control
					default_grounds(self.session, x, y)
				for x_offset in xrange(0, 10):
					if x+x_offset < self.max_x and x+x_offset >= self.min_x:
						for y_offset in xrange(0, 10):
							if y+y_offset < self.max_y and y+y_offset >= self.min_y:
								self.ground_map[(x+x_offset, y+y_offset)] = fake_tile_class(self.session, x, y)

		# remove parts that are occupied by islands, create the island map and the full map
		self.island_map = {}
		self.full_map = copy.copy(self.ground_map)
		for island in self.islands:
			for coords in island.ground_map:
				if coords in self.ground_map:
					self.full_map[coords] = island.ground_map[coords]
					del self.ground_map[coords]
					self.island_map[coords] = island
Exemplo n.º 43
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.º 44
0
    def __init(self, db, island_id, preview):
        """
		Load the actual island from a file
		@param preview: flag, map preview mode
		"""
        p_x, p_y, width, height = db(
            "SELECT MIN(x), MIN(y), (1 + MAX(x) - MIN(x)), (1 + MAX(y) - MIN(y)) FROM ground WHERE island_id = ?",
            island_id - 1001,
        )[0]

        self.ground_map = {}
        for (x, y, ground_id, action_id, rotation) in db(
            "SELECT x, y, ground_id, action_id, rotation FROM ground WHERE island_id = ?", island_id - 1001
        ):  # Load grounds
            if not preview:  # actual game, need actual tiles
                ground = Entities.grounds[str("%d-%s" % (ground_id, action_id))](self.session, x, y)
                ground.act(rotation)
            else:
                ground = MapPreviewTile(x, y, ground_id)
                # These are important for pathfinding and building to check if the ground tile
                # is blocked in any way.
            self.ground_map[(ground.x, ground.y)] = ground

        self._init_cache()

        # Contains references to all resource deposits (but not mines)
        # on the island, regardless of the owner:
        # {building_id: {(x, y): building_instance, ...}, ...}
        self.deposits = defaultdict(dict)

        self.settlements = []
        self.wild_animals = []
        self.num_trees = 0

        # define the rectangle with the smallest area that contains every island tile its position
        min_x = min(zip(*self.ground_map.keys())[0])
        max_x = max(zip(*self.ground_map.keys())[0])
        min_y = min(zip(*self.ground_map.keys())[1])
        max_y = max(zip(*self.ground_map.keys())[1])
        self.position = Rect.init_from_borders(min_x, min_y, max_x, max_y)

        if not preview:
            # This isn't needed for map previews, but it is in actual games.
            self.path_nodes = IslandPathNodes(self)

            # Repopulate wild animals every 2 mins if they die out.
            Scheduler().add_new_object(
                self.check_wild_animal_population, self, run_in=Scheduler().get_ticks(120), loops=-1
            )

        """TUTORIAL:
Exemplo n.º 45
0
	def __init(self, origin, filename, preview=False):
		"""
		Load the actual island from a file
		@param origin: Point
		@param filename: String, filename of island db or random map id
		@param preview: flag, map preview mode
		"""
		self.file = filename
		self.origin = origin
		db = self._get_island_db()

		p_x, p_y, width, height = db("SELECT (MIN(x) + ?), (MIN(y) + ?), (1 + MAX(x) - MIN(x)), (1 + MAX(y) - MIN(y)) FROM ground", self.origin.x, self.origin.y)[0]

		# rect for quick checking if a tile isn't on this island
		# NOTE: it contains tiles, that are not on the island!
		self.rect = Rect(Point(p_x, p_y), width, height)

		self.ground_map = {}
		for (rel_x, rel_y, ground_id, action_id, rotation) in db("SELECT x, y, ground_id, action_id, rotation FROM ground"): # Load grounds
			if not preview: # actual game, need actual tiles
				ground = Entities.grounds[ground_id](self.session, self.origin.x + rel_x, self.origin.y + rel_y)
				ground.act(action_id, rotation)
			else:
				ground = Point(self.origin.x + rel_x, self.origin.y + rel_y)
				ground.classes = tuple()
				ground.settlement = None
			# These are important for pathfinding and building to check if the ground tile
			# is blocked in any way.
			self.ground_map[(ground.x, ground.y)] = ground

		self._init_cache()

		self.settlements = []
		self.wild_animals = []
		self.num_trees = 0

		# define the rectangle with the smallest area that contains every island tile its position
		min_x = min(zip(*self.ground_map.keys())[0])
		max_x = max(zip(*self.ground_map.keys())[0])
		min_y = min(zip(*self.ground_map.keys())[1])
		max_y = max(zip(*self.ground_map.keys())[1])
		self.position = Rect.init_from_borders(min_x, min_y, max_x, max_y)

		if not preview: # this isn't needed for previews, but it is in actual games
			self.path_nodes = IslandPathNodes(self)

			# repopulate wild animals every 2 mins if they die out.
			Scheduler().add_new_object(self.check_wild_animal_population, self, Scheduler().get_ticks(120), -1)

		"""TUTORIAL:
Exemplo n.º 46
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 test_rect():
	r1 = Rect(Point(0, 0), 1, 1)
	r2 = Rect(0, 0, 1, 1)
	r3 = Rect(Point(2, 2), 1, 1)
	assert r1 == r2
	assert not r1.contains(Point(-1, -1))
	assert r2.contains(Point(0, 0))
	assert r2.contains(Point(1, 1))
	assert r1.intersects(r2)
	assert not r1.intersects(r3)
Exemplo n.º 48
0
	def _update(self, tup):
		"""Recalculate and redraw minimap for real world coord tup
		@param tup: (x, y)"""
		if self.world is None or not self.world.inited:
			return # don't draw while loading
		minimap_point = self._world_to_minimap( tup, self._get_rotation_setting() )
		world_to_minimap = self._world_to_minimap_ratio
		# TODO: remove this remnant of the old implementation, perhaps by refactoring recalculate()
		minimap_point = (
		  minimap_point[0] + self.location.left,
		  minimap_point[1] + self.location.top,
		)
		rect = Rect.init_from_topleft_and_size(minimap_point[0], minimap_point[1],
								                           int(round(1/world_to_minimap[0])) + 1,
								                           int(round(1/world_to_minimap[1])) + 1)
		self._recalculate(rect)
	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.º 50
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.º 51
0
	def _update(self, tup):
		"""Recalculate and redraw minimap for real world coord tup
		@param tup: (x, y)"""
		if self.world is None or not self.world.inited:
			return # don't draw while loading
		minimap_point = self._world_to_minimap( tup, self._get_rotation_setting() )
		world_to_minimap = self._world_to_minimap_ratio
		# TODO: remove this remnant of the old implementation, perhaps by refactoring recalculate()
		minimap_point = (
		  minimap_point[0] + self.location.left,
		  minimap_point[1] + self.location.top,
		)
		rect = Rect.init_from_topleft_and_size(minimap_point[0], minimap_point[1],
								                           int(round(1/world_to_minimap[0])) + 1,
								                           int(round(1/world_to_minimap[1])) + 1)
		self._recalculate(rect)
Exemplo n.º 52
0
	def testRect(self):
		r1 = Rect(Point(0,0), 1, 1)
		r2 = Rect(0, 0, 1, 1)
		r3 = Rect(Point(2, 2), 1, 1)
		self.assertEqual(r1, r2)
		self.assertTrue(r1 == r2)
		self.assertFalse(r1.contains(Point(-1,-1)))
		self.assertTrue(r2.contains(Point(0,0)))
		self.assertTrue(r2.contains(Point(1,1)))
		self.assertTrue(r1.intersects(r2))
		self.assertFalse(r1.intersects(r3))
Exemplo n.º 53
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.º 54
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.º 55
0
	def _get_road_to_builder(self, builder):
		"""Return a path from the builder to a building with general collectors (None if impossible)."""
		loading_area = builder.get_loading_area()
		collector_coords = set()
		for building in self.collector_buildings:
			if loading_area.distance(building.position) == 1:
				return []
			if loading_area.distance(building.position) > building.radius:
				continue # the collector building is too far to be useful
			for coords in self.iter_possible_road_coords(building.position, building.position):
				collector_coords.add(coords)

		blocked_coords = set([coords for coords in builder.position.tuple_iter()])
		destination_coords = set(self.iter_possible_road_coords(loading_area, builder.position))
		beacon = Rect.init_from_borders(loading_area.left - 1, loading_area.top - 1,
		                                loading_area.right + 1, loading_area.bottom + 1)

		return RoadPlanner()(self.owner.personality_manager.get('RoadPlanner'), collector_coords,
			destination_coords, beacon, self.get_path_nodes(), blocked_coords=blocked_coords)
Exemplo n.º 56
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.º 59
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()
    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)