def testPoint(self): p1 = Point(0,0) p2 = Point(0,2) self.assertEqual(p1.distance(p2), 2) self.assertEqual(p1.distance((0,1)), 1) self.assertEqual(p1.get_coordinates(), [(0,0)]) self.assertEqual(p1, p1.copy())
def test_point(): p1 = Point(0, 0) p2 = Point(0, 2) assert p1.distance(p2) == 2 assert p1.distance((0, 1)) == 1 assert p1.get_coordinates() == [(0, 0)] assert p1 == p1.copy()
def calculate_ship_dispersion(cls, ship_group): """ There are many solutions to solve the problem of caculating ship_group dispersion efficiently. We generally care about computing that in linear time, rather than having accurate numbers in O(n^2). We settle for a diagonal of a bounding box for the whole group. @return: dispersion factor @rtype: float """ positions = [ship.position for ship in ship_group] bottom_left = Point(min(positions, key=lambda position: position.x).x, min(positions, key=lambda position: position.y).y) top_right = Point(max(positions, key=lambda position: position.x).x, max(positions, key=lambda position: position.y).y) diagonal = bottom_left.distance(top_right) return diagonal
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:
def _get_event_coord(self, event): """Returns position of event as uh map coordinate tuple or None""" mouse_position = Point(event.getX(), event.getY()) if not hasattr(self, "icon"): icon_pos = Point(*self.overlay_icon.getAbsolutePos()) abs_mouse_position = icon_pos + mouse_position if not self.location.contains(abs_mouse_position): # mouse click was on icon but not actually on minimap return abs_mouse_position = abs_mouse_position.to_tuple() else: abs_mouse_position = mouse_position.to_tuple() if self._get_rotation_setting(): abs_mouse_position = self._get_from_rotated_coords(abs_mouse_position) return self._minimap_coord_to_world_coord(abs_mouse_position)
def find_warehouse_location(cls, ship, land_manager): """ Finds a location for the warehouse on the given island @param LandManager: the LandManager of the island @return _BuildPosition: a possible build location """ moves = [(-1, 0), (0, -1), (0, 1), (1, 0)] island = land_manager.island world = island.session.world personality = land_manager.owner.personality_manager.get('FoundSettlement') options = [] for (x, y), tile in sorted(island.ground_map.iteritems()): ok = False for x_offset, y_offset in moves: for d in xrange(2, 6): coords = (x + d * x_offset, y + d * y_offset) if coords in world.water_body and world.water_body[coords] == world.water_body[ship.position.to_tuple()]: # the planned warehouse should be reachable from the ship's water body ok = True if not ok: continue build_info = None point = Point(x, y) warehouse = Builder(BUILDINGS.WAREHOUSE, land_manager, point, ship = ship) if not warehouse: continue cost = 0 for coords in land_manager.village: distance = point.distance(coords) if distance < personality.too_close_penalty_threshold: cost += personality.too_close_constant_penalty + personality.too_close_linear_penalty / (distance + 1.0) else: cost += distance for settlement_manager in land_manager.owner.settlement_managers: cost += warehouse.position.distance(settlement_manager.settlement.warehouse.position) * personality.linear_warehouse_penalty options.append((cost, warehouse)) for _, build_info in sorted(options): (x, y) = build_info.position.get_coordinates()[4] if ship.check_move(Circle(Point(x, y), BUILDINGS.BUILD.MAX_BUILDING_SHIP_DISTANCE)): return build_info return None
def _make_surrounding_transparent(self, building): """Makes the surrounding of building_position transparent""" world_contains = self.session.world.map_dimensions.contains_without_border for coord in building.position.get_radius_coordinates( self.nearby_objects_radius, include_self=True): p = Point(*coord) if not world_contains(p): continue tile = self.session.world.get_tile(p) if tile.object is not None and tile.object.buildable_upon: inst = tile.object.fife_instance inst.get2dGfxVisual().setTransparency( BUILDINGS.TRANSPARENCY_VALUE) self._transparent_instances.add(weakref.ref(inst))
def _check_island(cls, session, position, island=None): # this might raise, just let it through super(BuildableSingleOnOcean, cls)._check_island(session, position, island) if island is None: island = session.world.get_island(position.center) if island is None: raise _NotBuildableError(BuildableErrorTypes.NO_ISLAND) posis = position.get_coordinates() for tile in posis: for rad in Circle(Point(*tile), 3): if island.get_tile(rad) is None: # Tile not on island -> deep water return island raise _NotBuildableError(BuildableErrorTypes.NO_OCEAN_NEARBY)
def begin_current_job(self): # we can only move on 1 building; simulate this by choosing a random location with # the building coords = self._get_random_positions_on_object(self.job.object) # move to first walkable target coord we find for coord in coords: # job target is walkable, so at least one coord of it has to be # so we can safely assume, that we will find a walkable coord target_location = Point(*coord) if self.check_move(target_location): super(FarmAnimal, self).begin_current_job(job_location=target_location) return assert False
def __make_new_builder(self, building_id, x, y, needs_collector, orientation): """Return a Builder object if it is allowed to be built at the location, otherwise return None (not cached).""" coords = (x, y) # quick check to see whether the origin square is allowed to be in the requested place if building_id == BUILDINGS.CLAY_PIT or building_id == BUILDINGS.IRON_MINE: # clay deposits and mountains are outside the production plan until they are constructed if coords in self.plan or coords not in self.settlement.ground_map: return None elif building_id in self.coastal_building_classes: # coastal buildings can use coastal tiles if coords not in self.land_manager.coastline and coords in self.plan and self.plan[ coords][0] != BUILDING_PURPOSE.NONE: return None else: if coords not in self.plan or self.plan[coords][ 0] != BUILDING_PURPOSE.NONE or coords not in self.settlement.ground_map: return None # create the builder, make sure that it is allowed according to the game logic builder = Builder.create(building_id, self.land_manager, Point(x, y), orientation=orientation) if not builder or not self.land_manager.legal_for_production( builder.position): return None # make sure that the position of the building is allowed according to the plan if building_id in self.coastal_building_classes: # coastal buildings can use coastal tiles for coords in builder.position.tuple_iter(): if coords not in self.land_manager.coastline and coords in self.plan and self.plan[ coords][0] != BUILDING_PURPOSE.NONE: return None elif building_id in [BUILDINGS.CLAY_PIT, BUILDINGS.IRON_MINE]: # clay deposits and mountains can't be in areas restricted by the plan pass else: for coords in builder.position.tuple_iter(): if coords not in self.plan or self.plan[coords][ 0] != BUILDING_PURPOSE.NONE: return None # make sure the building is close enough to a collector if it produces any resources that have to be collected if needs_collector and not any( True for building in self.collector_buildings if building. position.distance(builder.position) <= building.radius): return None return builder
def _check_island(cls, session, position, island=None): # this might raise, just let it through super(BuildableSingleOnOcean, cls)._check_island(session, position, island) if island is None: island = session.world.get_island(position.center) if island is None: raise _NotBuildableError(BuildableErrorTypes.NO_ISLAND) posis = position.get_coordinates() for tile in posis: for rad in Circle(Point(*tile), 3): if rad in session.world.water_body and session.world.water_body[rad] == session.world.sea_number: # Found legit see tile return island raise _NotBuildableError(BuildableErrorTypes.NO_OCEAN_NEARBY)
def go(self, x, y): from horizons.world.units.movingobject import MoveNotPossible """Moves the unit. This is called when a unit is selected and the right mouse button is pressed outside the unit""" x = int(round(x)) y = int(round(y)) move_target = Point(x, y) try: self.instance.move(move_target) except MoveNotPossible: # find a near tile to move to surrounding = Circle(move_target, radius=1) move_target = None # try with smaller circles, increase radius if smaller circle isn't reachable while surrounding.radius < 5: try: self.instance.move(surrounding) except MoveNotPossible: surrounding.radius += 1 continue # update actual target coord move_target = self.instance.get_move_target() break if self.instance.owner.is_local_player: self.instance.session.ingame_gui.minimap.show_unit_path( self.instance) if move_target is None: # can't move if not self.instance.owner.is_local_player: return if self.session.world.get_tile(Point( x, y)) is None: # not even in world string_id = "MOVE_OUTSIDE_OF_WORLD" else: # in world, but still unreachable string_id = "MOVE_INVALID_LOCATION" self.session.ingame_gui.message_widget.add(point=Point(x, y), string_id=string_id)
def execute(self): (result, building) = super(LumberjackEvaluator, self).execute() if result != BUILD_RESULT.OK: return (result, None) for coords in building.position.get_radius_coordinates( Entities.buildings[BUILDINGS.LUMBERJACK].radius): if coords in self.area_builder.plan and self.area_builder.plan[ coords][0] == BUILDING_PURPOSE.NONE: self.area_builder.register_change(coords[0], coords[1], BUILDING_PURPOSE.TREE, None) # TODO: don't ignore the return value Builder.create(BUILDINGS.TREE, self.area_builder.land_manager, Point(coords[0], coords[1])).execute() return (BUILD_RESULT.OK, building)
def get_random_possible_ground_unit_position(world): """Returns a position in water, that is not at the border of the world""" offset = 2 while True: x = world.session.random.randint(world.min_x + offset, world.max_x - offset) y = world.session.random.randint(world.min_y + offset, world.max_y - offset) if (x, y) in world.ground_unit_map: continue for island in world.islands: if (x, y) in island.path_nodes.nodes: return Point(x, y)
def get_random_possible_ground_unit_position(world): """Returns a random position upon an island""" offset = 2 while True: x = world.session.random.randint(world.min_x + offset, world.max_x - offset) y = world.session.random.randint(world.min_y + offset, world.max_y - offset) if (x, y) in world.ground_unit_map: continue for island in world.islands: if (x, y) in island.path_nodes.nodes: return Point(x, y)
def find_warehouse_location(cls, ship, land_manager): """Return the coordinates of a location for the warehouse on the given island.""" warehouse_class = Entities.buildings[BUILDINGS.WAREHOUSE] pos_offsets = [] for dx in xrange(warehouse_class.width): for dy in xrange(warehouse_class.height): pos_offsets.append((dx, dy)) island = land_manager.island personality = land_manager.owner.personality_manager.get( 'FoundSettlement') available_spots_list = list( sorted(island.terrain_cache.cache[warehouse_class.terrain_type][ warehouse_class.size].intersection( island.available_land_cache.cache[warehouse_class.size]))) if not available_spots_list: return None options = [] limited_spots = island.session.random.sample( available_spots_list, min(len(available_spots_list), personality.max_options)) for (x, y) in limited_spots: cost = 0 for (x2, y2) in land_manager.village: dx = x2 - x dy = y2 - y distance = (dx * dx + dy * dy)**0.5 if distance < personality.too_close_penalty_threshold: cost += personality.too_close_constant_penalty + personality.too_close_linear_penalty / ( distance + 1.0) else: cost += distance for settlement_manager in land_manager.owner.settlement_managers: cost += settlement_manager.settlement.warehouse.position.distance( (x, y)) * personality.linear_warehouse_penalty options.append((cost, x, y)) for _, x, y in sorted(options): if ship.check_move( Circle( Point(x + warehouse_class.width // 2, y + warehouse_class.height // 2), BUILDINGS.BUILD.MAX_BUILDING_SHIP_DISTANCE)): return (x, y) return None
def __init__(self, x, y, rotation, owner, island, level=None, **kwargs): self.__pre_init(owner, rotation, Point(x, y), level=level) super().__init__(x=x, y=y, rotation=rotation, owner=owner, island=island, **kwargs) self.__init() self.island = island settlements = self.island.get_settlements(self.position, owner) self.settlement = None if settlements: self.settlement = settlements[0] elif owner: # create one if we have an owner self.settlement = self.island.add_settlement(self.position, self.radius, owner) assert self.settlement is None or isinstance(self.settlement, Settlement)
def patched_world_location_from_event(self, evt): """Typically we expect a Mock MouseEvent, genereated by `_make_mouse_event`. However NavigationTool keeps track of the last event position, which is an instance of fife.ScreenPoint. """ try: # fife.MouseEvent x = evt.getX() y = evt.getY() except AttributeError: # fife.ScreenPoint x = evt.x y = evt.y return Point(x, y)
def build_roads(self): """Try to build all roads in the village area, record the result in the field roads_built.""" all_built = True for coords, (purpose, (section, _)) in self.plan.iteritems(): if section > self.current_section: all_built = False continue if purpose != BUILDING_PURPOSE.ROAD: continue object = self.island.ground_map[coords].object if object is not None and object.id == BUILDINGS.TRAIL: continue if Builder.create(BUILDINGS.TRAIL, self.land_manager, Point(coords[0], coords[1])).execute() is None: all_built = False self.roads_built = all_built
def test_get_tile_alignment_action(expected, tiles): """Tests for the road/wall orientation code. Basically `get_tile_alignment_action` returns the sorted list of fields that the tile in position . should connect. Tile names: h a e d . b g c f """ tiles, accessor = make_tiles(tiles) aligned = get_tile_alignment_action(Point(0, 0), accessor) assert aligned == expected
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:
def new_settlement(session, pos=Point(30, 20)): """ Creates a settlement at the given position. It returns the settlement and the island where it was created on, to avoid making function-baed tests too verbose. """ island = session.world.get_island(pos) assert island, "No island found at %s" % pos player = session.world.player ship = CreateUnit(player.worldid, UNITS.PLAYER_SHIP, pos.x, pos.y)(player) for res, amount in session.db("SELECT resource, amount FROM start_resources"): ship.get_component(StorageComponent).inventory.alter(res, amount) building = Build(BUILDINGS.WAREHOUSE, pos.x, pos.y, island, ship=ship)(player) assert building, "Could not build warehouse at %s" % pos return (building.settlement, island)
def _round_map_coords(self, map_x, map_y): """Returns the coordinates of an event at the map. Why roundhalfplus? a b a-b round(a)-round(b) roundplus(a)-roundplus(b) 1.50 0.50 1.00 1.00 1.0 0.50 -0.49 0.99 1.00 1.0 -0.49 -1.49 1.00 1.00 1.0 Error: 0.50 -0.50 1.00 2.00 1.0 This error would result in fields at position 0 to be smaller than the others, because both sides (-0.5 and 0.5) would be wrongly assigned to the other fields. @return Point with int coordinates""" return Point(roundhalfplus(map_x), roundhalfplus(map_y))
def build(self, settlement_manager, resource_id): production_builder = settlement_manager.production_builder purpose = self.get_purpose(resource_id) if not production_builder.unused_fields[purpose]: return (BUILD_RESULT.NEED_PARENT_FIRST, None) if not self.have_resources(settlement_manager): return (BUILD_RESULT.NEED_RESOURCES, None) assert production_builder.unused_fields[purpose], 'expected field spot to be available' coords = production_builder.unused_fields[purpose][0] builder = Builder.create(self.id, settlement_manager.land_manager, Point(coords[0], coords[1])) building = builder.execute() if not building: return (BUILD_RESULT.UNKNOWN_ERROR, None) production_builder.unused_fields[purpose].popleft() production_builder.register_change(coords[0], coords[1], purpose, None) return (BUILD_RESULT.OK, building)
def execute(self, land_manager, ship=None): """Build the building.""" building_class = Entities.buildings[self.building_id] building_level = building_class.get_initial_level(land_manager.owner) action_set_id = building_class.get_random_action_set(level = building_level) build_position = Entities.buildings[self.building_id].check_build(land_manager.session, Point(*self.coords), rotation=self.rotations[self.orientation], check_settlement=(ship is None), ship=ship, issuer=land_manager.owner) assert build_position.buildable cmd = Build(self.building_id, self.coords[0], self.coords[1], land_manager.island, self._get_rotation(land_manager.session, build_position.rotation), settlement=land_manager.settlement, ship=ship, tearset=build_position.tearset, action_set_id=action_set_id) result = cmd(land_manager.owner) assert result return result
def high(i=0): i += 1 render_name = self._get_render_name("highlight")+str(tup) self.minimap_image.set_drawing_enabled() self.minimap_image.rendertarget.removeAll(render_name) if i > STEPS: if finish_callback: finish_callback() return part = i # grow bigger if i > STEPS // 2: # after the first half part = STEPS-i # become smaller radius = MIN_RAD + int(( float(part) / (STEPS // 2) ) * (MAX_RAD - MIN_RAD) ) for x, y in Circle( Point(*tup), radius=radius ).get_border_coordinates(): self.minimap_image.rendertarget.addPoint(render_name, fife.Point(x, y), *color) ExtScheduler().add_new_object(lambda : high(i), self, INTERVAL, loops=1)
def _make_surrounding_transparent(self, building): """Makes the surrounding of building_position transparent and hide buildings that are built upon (tearset)""" world_contains = self.session.world.map_dimensions.contains_without_border get_tile = self.session.world.get_tile for coord in building.position.get_radius_coordinates(self.nearby_objects_radius, include_self=True): p = Point(*coord) if not world_contains(p): continue tile = get_tile(p) if tile.object is not None and tile.object.buildable_upon: inst = tile.object.fife_instance inst.get2dGfxVisual().setTransparency(BUILDINGS.TRANSPARENCY_VALUE) self._transparencified_instances.add(weakref.ref(inst)) for to_tear_worldid in building.tearset: inst = WorldObject.get_object_by_id(to_tear_worldid).fife_instance inst.get2dGfxVisual().setTransparency(255) # full transparency = hidden self._transparencified_instances.add(weakref.ref(inst))
def fire_all_weapons(self, dest, rotated=False, bullet_delay=0): """ Fires all weapons in storage at a given position @param dest: Point with the given position @param rotated: If True weapons will be fired at different locations, rotated around dest override to True for units that need to fire at rotated coords """ self.log.debug("%s fire all weapons", self) self._fired_weapons_number = 0 if not self.can_attack_position(dest): self.log.debug("%s can't attack this position", self) return if not rotated: for weapon in self._fireable: weapon.fire(dest, self.position.center, bullet_delay) else: angle = (math.pi / 60) * (-len(self._fireable) / 2) cos = math.cos(angle) sin = math.sin(angle) x = self.position.center.x y = self.position.center.y dest_x = dest.x dest_y = dest.y dest_x = (dest_x - x) * cos - (dest_y - y) * sin + x dest_y = (dest_x - x) * sin + (dest_y - y) * cos + y angle = math.pi / 60 cos = math.cos(angle) sin = math.sin(angle) for weapon in self._fireable: destination = Point(dest_x, dest_y) weapon.fire(destination, self.position.center, bullet_delay) dest_x = (dest_x - x) * cos - (dest_y - y) * sin + x dest_y = (dest_x - x) * sin + (dest_y - y) * cos + y if self._fired_weapons_number != 0: self.act_attack(dest)
def __place_unit(self, unit): radius = 1 found_tile = False # search for free water tile, and increase search radius if none is found while not found_tile: for coord in Circle(self.instance.position.center, radius).tuple_iter(): point = Point(coord[0], coord[1]) if self.instance.island.get_tile(point) is not None: continue tile = self.session.world.get_tile(point) if tile is not None and tile.is_water and coord not in self.session.world.ship_map: # execute bypassing the manager, it's simulated on every machine u = CreateUnit(self.instance.owner.worldid, unit, point.x, point.y)(issuer=self.instance.owner) # Fire a message indicating that the ship has been created name = u.get_component(NamedComponent).name self.session.ingame_gui.message_widget.add(string_id='NEW_UNIT', point=point, message_dict={'name' : name}) found_tile = True break radius += 1
def check_build_line(cls, session, point1, point2, rotation=45, ship=None): # Pathfinding currently only supports buildingsize 1x1, so don't use it in this case if cls.size != (1, 1): return [ cls.check_build_fuzzy(session, point2, rotation=rotation, ship=ship) ] # use pathfinding to get a path, then try to build along it island = session.world.get_island(point1) if island is None: return [] if cls.id == BUILDINGS.TRAIL: nodes = island.path_nodes.nodes elif cls.id == BUILDINGS.BARRIER: # Allow nodes that can be walked upon and existing barriers when finding a # build path nodes = ChainedContainer(island.path_nodes.nodes, island.barrier_nodes.nodes) else: raise Exception('BuildableLine does not support building id {0}'.format(cls.id)) path = a_star_find_path(point1.to_tuple(), point2.to_tuple(), nodes, rotation in (45, 225)) if path is None: # can't find a path between these points return [] # TODO: maybe implement alternative strategy possible_builds = [] #TODO duplicates recalculation code in world.building.path for x, y in path: action = '' for action_char, (xoff, yoff) in \ sorted(BUILDINGS.ACTION.action_offset_dict.items()): # order is important here if action_char in 'abcd' and (xoff + x, yoff + y) in path: action += action_char if action == '': action = 'single' # single trail piece with no neighbors build = cls.check_build(session, Point(x, y)) build.action = action possible_builds.append(build) return possible_builds
def check_build_line(cls, session, point1, point2, rotation=45, ship=None): # Pathfinding currently only supports buildingsize 1x1, so don't use it in this case if cls.size != (1, 1): return [ cls.check_build_fuzzy(session, point2, rotation=rotation, ship=ship) ] # use pathfinding to get a path, then try to build along it island = session.world.get_island(point1) if island is None: return [] path = RoadPathFinder()(island.path_nodes.nodes, point1.to_tuple(), point2.to_tuple(), rotation == 45 or rotation == 225) if path is None: # can't find a path between these points return [] # TODO: maybe implement alternative strategy possible_builds = [] #TODO duplicates recalculation code in world.building.path for x, y in path: action = '' for action_char, (xoff, yoff) in \ sorted(BUILDINGS.ACTION.action_offset_dict.iteritems()): # order is important here if action_char in 'abcd' and (xoff + x, yoff + y) in path: action += action_char if action == '': action = 'single' # single trail piece with no neighbours build = cls.check_build(session, Point(x, y)) build.action = action possible_builds.append(build) return possible_builds
def _load(self, db, worldid): super(Pirate, self)._load(db, worldid) self.__init() remaining_ticks, = db( "SELECT remaining_ticks FROM ai_pirate WHERE rowid = ?", worldid)[0] Scheduler().add_new_object(Callback(self.tick), self, remaining_ticks, -1, self.tick_interval) remaining_ticks_long, = db( "SELECT remaining_ticks_long FROM ai_pirate WHERE rowid = ?", worldid)[0] Scheduler().add_new_object(Callback(self.tick_long), self, remaining_ticks_long, -1, self.tick_interval) home = db("SELECT x, y FROM pirate_home_point")[0] self.home_point = Point(home[0], home[1]) self.log.debug("Pirate: home at (%d, %d), radius %d", self.home_point.x, self.home_point.y, self.home_radius)
def _place_unit(self, unit): radius = 1 found_tile = False while not found_tile: # search for a free tile around the building for tile in self.instance.island.get_surrounding_tiles( self.instance.position.center, radius): point = Point(tile.x, tile.y) if not (tile.is_water or tile.blocked) and ( tile.x, tile.y) not in self.session.world.ground_unit_map: u = CreateUnit(self.instance.owner.worldid, unit, tile.x, tile.y)(issuer=self.instance.owner) # Fire a message indicating that the ship has been created name = u.get_component(NamedComponent).name self.session.ingame_gui.message_widget.add( string_id='NEW_SOLDIER', point=point, message_dict={'name': name}) found_tile = True break radius += 1
def __init__(self, *args): if len(args) == 2 and isinstance(args[0], Point) and isinstance(args[1], Point): #args: edge1, edge2 self.top = min(args[0].y, args[1].y) self.left = min(args[0].x, args[1].x) self.right = max(args[0].x, args[1].x) self.bottom = max(args[0].y, args[1].y) elif len(args) == 3 and isinstance(args[0], Point) and isinstance(args[1], int) and isinstance(args[2], int): #args: position, width, height self.top = args[0].y self.left = args[0].x self.right = self.left + args[1] self.bottom = self.top + args[2] elif len(args) == 4 and isinstance(args[0], int) and isinstance(args[1], int) and isinstance(args[2], int) and isinstance(args[3], int): self.top = min(args[1], args[3]) self.left = min(args[0], args[2]) self.right = max(args[0], args[2]) self.bottom = max(args[1], args[3]) else: assert False # Convenience attributes (can be used to make code more easy to read/understand) self.origin = Point(self.left, self.top)
def create(cls, area_builder, x, y, new_field_purpose): building_id = None if new_field_purpose == BUILDING_PURPOSE.POTATO_FIELD: building_id = BUILDINGS.POTATO_FIELD elif new_field_purpose == BUILDING_PURPOSE.PASTURE: building_id = BUILDINGS.PASTURE elif new_field_purpose == BUILDING_PURPOSE.SUGARCANE_FIELD: building_id = BUILDINGS.SUGARCANE_FIELD elif new_field_purpose == BUILDING_PURPOSE.TOBACCO_FIELD: building_id = BUILDINGS.TOBACCO_FIELD builder = Builder.create(building_id, area_builder.land_manager, Point(x, y)) if not builder: return None value = 0 personality = area_builder.owner.personality_manager.get( 'ModifiedFieldEvaluator') if new_field_purpose == BUILDING_PURPOSE.POTATO_FIELD: value += personality.add_potato_field_value elif new_field_purpose == BUILDING_PURPOSE.PASTURE: value += personality.add_pasture_value elif new_field_purpose == BUILDING_PURPOSE.SUGARCANE_FIELD: value += personality.add_sugarcane_field_value elif new_field_purpose == BUILDING_PURPOSE.TOBACCO_FIELD: value += personality.add_tobacco_field_value old_field_purpose = area_builder.plan[(x, y)][0] if old_field_purpose == BUILDING_PURPOSE.POTATO_FIELD: value -= personality.remove_unused_potato_field_penalty elif old_field_purpose == BUILDING_PURPOSE.PASTURE: value -= personality.remove_unused_pasture_penalty elif old_field_purpose == BUILDING_PURPOSE.SUGARCANE_FIELD: value -= personality.remove_unused_sugarcane_field_penalty elif old_field_purpose == BUILDING_PURPOSE.TOBACCO_FIELD: value -= personality.remove_unused_tobacco_field_penalty return ModifiedFieldEvaluator(area_builder, builder, value, old_field_purpose)
def spawn_ships(session, owner_id, ship_id, number, *position): """ Creates a number of ships controlled by a certain player around a position on the map. @param owner_id: the owner worldid @param ship_id: the ship id @param number: number of ships to be spawned @param position: position around the ships to be spawned """ center = Point(*position) player = WorldObject.get_object_by_id(owner_id) # calculate a radius that should fit all the ships # if it doesn't fit them all increase the radius radius = int(math.sqrt(number)) while number != 0: for point in Circle(center, radius): if (point.x, point.y) in session.world.ship_map \ or session.world.get_island(point) is not None: continue CreateUnit(owner_id, ship_id, point.x, point.y)(issuer=player) number -= 1 if number == 0: break radius += 1
def _recalculate(self, where=None, dump_data=False): """Calculate which pixel of the minimap should display what and draw it @param where: Rect of minimap coords. Defaults to self.location @param dump_data: Don't draw but return calculated data""" self.minimap_image.set_drawing_enabled() rt = self.minimap_image.rendertarget render_name = self._get_render_name("base") if where is None: where = self.location rt.removeAll(render_name) # calculate which area of the real map is mapped to which pixel on the minimap pixel_per_coord_x, pixel_per_coord_y = self._world_to_minimap_ratio # calculate values here so we don't have to do it in the loop pixel_per_coord_x_half_as_int = int(pixel_per_coord_x/2) pixel_per_coord_y_half_as_int = int(pixel_per_coord_y/2) real_map_point = Point(0, 0) world_min_x = self.world.min_x world_min_y = self.world.min_y get_island_tuple = self.world.get_island_tuple island_col = self.COLORS["island"] location_left = self.location.left location_top = self.location.top if dump_data: data = [] drawPoint = lambda name, fife_point, r, g, b : data.append( (fife_point.x, fife_point.y, r, g, b) ) else: drawPoint = rt.addPoint fife_point = fife.Point(0,0) use_rotation = self._get_rotation_setting() # loop through map coordinates, assuming (0, 0) is the origin of the minimap # this faciliates calculating the real world coords for x in xrange(where.left-self.location.left, where.left+where.width-self.location.left): # Optimisation: remember last island last_island = None island = None for y in xrange(where.top-self.location.top, where.top+where.height-self.location.top): """ This code should be here, but since python can't do inlining, we have to inline ourselves for performance reasons covered_area = Rect.init_from_topleft_and_size( int(x * pixel_per_coord_x)+world_min_x, \ int(y * pixel_per_coord_y)+world_min_y), \ int(pixel_per_coord_x), int(pixel_per_coord_y)) real_map_point = covered_area.center """ # use center of the rect that the pixel covers real_map_point.x = int(x*pixel_per_coord_x)+world_min_x + \ pixel_per_coord_x_half_as_int real_map_point.y = int(y*pixel_per_coord_y)+world_min_y + \ pixel_per_coord_y_half_as_int real_map_point_tuple = (real_map_point.x, real_map_point.y) # check what's at the covered_area if last_island is not None and real_map_point_tuple in last_island.ground_map: island = last_island else: island = get_island_tuple(real_map_point_tuple) if island is not None: last_island = island # this pixel is an island settlement = island.get_settlement(real_map_point) if settlement is None: # island without settlement color = island_col else: # pixel belongs to a player color = settlement.owner.color.to_tuple() else: continue if use_rotation: # inlined _get_rotated_coords rot_x, rot_y = self._rotate( (location_left + x, location_top + y), self._rotations) fife_point.set(rot_x - location_left, rot_y - location_top) else: fife_point.set(x, y) drawPoint(render_name, fife_point, *color) if dump_data: return json.dumps( data )