def get_ground_units(self, position=None, radius=None): """@see get_ships""" if position is not None and radius is not None: circle = Circle(position, radius) return [unit for unit in self.ground_units if circle.contains(unit.position)] else: return self.ground_units
def test_circle(): c1 = Circle(Point(0, 0), 1) c2 = Circle(Point(0, 0), 2) c3 = Circle(Point(0, 0), 0) assert not (c1 == c2) assert c1 != c2 assert c1.get_coordinates() == [(-1, 0), (0, -1), (0, 0), (0, 1), (1, 0)] assert c3.get_coordinates() == [(0, 0)]
def testCircle(self): c1 = Circle(Point(0,0), 1) c2 = Circle(Point(0,0), 2) c3 = Circle(Point(0,0), 0) self.assertFalse(c1 == c2) self.assertTrue(c1 != c2) self.assertNotEqual(c1, c2) self.assertEqual(c1.get_coordinates(), [(-1, 0), (0, -1), (0, 0), (0, 1), (1, 0)]) self.assertEqual(c3.get_coordinates(), [(0,0)])
def _add_coloring(self, pos): brush = Circle(Point(*pos), self.session.world_editor.brush_size - 1) for p in brush.tuple_iter(): if p not in self.session.world.full_map: continue tile = self.session.world.full_map[p] if hasattr(tile, '_instance'): self.renderer.addColored(tile._instance, *self.HIGHLIGHT_COLOR)
def get_ships(self, position=None, radius=None): """Returns all ships on the map, optionally only those in range around the specified position. @param position: Point or Rect instance. @param radius: int radius to use. @return: List of ships. """ if position is not None and radius is not None: circle = Circle(position, radius) return [ship for ship in self.ships if circle.contains(ship.position)] else: return self.ships
def get_buildings(self, position=None, radius=None): """@see get_ships""" buildings = [] if position is not None and radius is not None: circle = Circle(position, radius) for island in self.islands: for building in island.buildings: if circle.contains(building.position.center): buildings.append(building) return buildings else: return [b for b in island.buildings for island in self.islands]
def act_auto_attack(self): """ Check if target still exists or if unit exited the hold ground area """ if not Circle(self.return_position, self.move_range).contains(self.instance.position.center) or \ not self.instance.is_attacking(): try: self.instance.move(self.return_position) except MoveNotPossible: self.instance.move( Circle(self.return_position, self.stance_radius)) self.state = 'move_back'
def move(self, destination, callback=None, ratio=1.0): """ Move fleet to a destination. @param ratio: what percentage of ships has to reach destination in order for the move to be considered done: 0.0 - None (not really useful, executes the callback right away) 0.0001 - effectively ANY ship 1.0 - ALL of the ships 0.5 - at least half of the ships etc. """ assert self.size() > 0, "ordered to move a fleet consisting of 0 ships" # it's ok to specify single point for a destination only when there's only one ship in a fleet if isinstance(destination, Point) and self.size() > 1: destination = Circle(destination, self._get_circle_size()) self.destination = destination self.state = self.fleetStates.moving self.ratio = ratio self.callback = callback # This is a good place to do something fancier later like preserving ship formation instead sailing to the same point for ship in self._ships.keys(): self._move_ship(ship, destination, Callback(self._ship_reached, ship))
def _load(self, worldid, owner, db, destroy_callback): super(Fleet, self).load(db, worldid) self.owner = owner state_id, dest_x, dest_y, radius, ratio = db("SELECT state_id, dest_x, dest_y, radius, ratio FROM fleet WHERE fleet_id = ?", worldid)[0] if radius: # Circle self.destination = Circle(Point(dest_x, dest_y), radius) elif dest_x and dest_y: # Point self.destination = Point(dest_x, dest_y) else: # No destination pass if ratio: self.ratio = ratio ships_states = [(WorldObject.get_object_by_id(ship_id), self.shipStates[ship_state_id]) for ship_id, ship_state_id in db("SELECT ship_id, state_id FROM fleet_ship WHERE fleet_id = ?", worldid)] ships = [item[0] for item in ships_states] self.__init(ships, destroy_callback) self.state = self.fleetStates[state_id] for ship, state in ships_states: self._ships[ship] = state if self.state == self.fleetStates.moving: for ship in self.get_ships(): if self._ships[ship] == self.shipStates.moving: ship.add_move_callback(Callback(self._ship_reached, ship)) if destroy_callback: self.destroy_callback = destroy_callback
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)) draw_point = self.minimap_image.rendertarget.addPoint for x, y in Circle(Point(*tup), radius=radius).get_border_coordinates(): draw_point(render_name, fife.Point(x, y), *color) ExtScheduler().add_new_object(lambda: high(i), self, INTERVAL, loops=1)
def send_ship_random_warehouse(self, ship, warehouse=None): """Sends a ship to a random warehouse on the map @param ship: Ship instance that is to be used @param warehouse: warehouse instance to move to. Random one is selected on None.""" self.log.debug("Trader %s ship %s moving to warehouse (random=%s)", self.worldid, ship.worldid, (warehouse is None)) #TODO maybe this kind of list should be saved somewhere, as this is pretty performance intense warehouses = self.session.world.get_warehouses() # Remove all warehouses that are not safe to visit warehouses = list(filter(self.is_warehouse_safe, warehouses)) if not warehouses: # there aren't any warehouses, move randomly self.send_ship_random(ship) else: # select a warehouse if warehouse is None: self.warehouse[ship.worldid] = self.session.random.choice( warehouses) else: self.warehouse[ship.worldid] = warehouse try: # try to find a possible position near the warehouse ship.move( Circle(self.warehouse[ship.worldid].position.center, ship.radius), Callback(self.reached_warehouse, ship)) self.ships[ship] = self.shipStates.moving_to_warehouse except MoveNotPossible: self.send_ship_random(ship)
def _chase_closest_ship(self, pirate_ship): owner = pirate_ship.owner ship = owner.get_nearest_player_ship(pirate_ship) if ship: owner.ships[pirate_ship] = owner.shipStates.chasing_ship # if ship was caught if ship.position.distance( pirate_ship.position) <= self.pirate_caught_ship_radius: self.log.debug('Pirate %s: Ship %s(%s) caught %s' % (owner.worldid, pirate_ship.get_component(NamedComponent).name, owner.ships[pirate_ship], ship)) self._sail_home(pirate_ship) else: try: pirate_ship.move( Circle(ship.position, self.pirate_caught_ship_radius - 1), Callback(self._sail_home, pirate_ship)) owner.ships[pirate_ship] = owner.shipStates.chasing_ship self.log.debug( 'Pirate %s: Ship %s(%s) chasing %s' % (owner.worldid, pirate_ship.get_component(NamedComponent).name, owner.ships[pirate_ship], ship.get_component(NamedComponent).name)) except MoveNotPossible: self.log.debug( 'Pirate %s: Ship %s(%s) unable to chase the closest ship %s' % (owner.worldid, pirate_ship.get_component(NamedComponent).name, owner.ships[pirate_ship], ship.get_component(NamedComponent).name)) owner.ships[pirate_ship] = owner.shipStates.idle
def sail_to_target(self): self.log.debug("Player %s, Mission %s, 1/2 set off to ship %s at %s", self.owner.name, self.__class__.__name__, self.target_ship.get_component(NamedComponent).name, self.target_ship.position) try: self.fleet.move(Circle(self.target_ship.position, self.target_range), self._state_fleet_callbacks[self.missionStates.sailing_to_target]) self.state = self.missionStates.sailing_to_target except MoveNotPossible: self.report_failure("Move was not possible when moving to target")
def _move_to_warehouse_area(self, warehouse_position, success_callback, blocked_callback, failure_msg): (x, y) = warehouse_position.get_coordinates()[4] area = Circle(Point(x, y), BUILDINGS.BUILD.MAX_BUILDING_SHIP_DISTANCE) try: self.ship.move(area, success_callback, blocked_callback=blocked_callback) except MoveNotPossible: self.report_failure(failure_msg)
def _load(self, worldid, owner, db, success_callback, failure_callback): super(SurpriseAttack, self)._load(db, worldid, success_callback, failure_callback, owner) db_result = db("SELECT enemy_player_id, target_point_x, target_point_y, target_point_radius, return_point_x, return_point_y " "FROM ai_mission_surprise_attack WHERE rowid = ?", worldid)[0] enemy_player_id, target_point_x, target_point_y, target_point_radius, return_point_x, return_point_y = db_result target_point = Circle(Point(target_point_x, target_point_y), target_point_radius) return_point = Point(return_point_x, return_point_y) enemy_player = WorldObject.get_object_by_id(enemy_player_id) self.__init(target_point, return_point, enemy_player)
def _move_to_warehouse_area(self, position, success_callback, blocked_callback, failure_msg): area = Circle(position.center, BUILDINGS.BUILD.MAX_BUILDING_SHIP_DISTANCE) try: self.ship.move(area, success_callback, blocked_callback=blocked_callback) except MoveNotPossible: self.report_failure(failure_msg)
def _sail_home(self, pirate_ship): owner = pirate_ship.owner try: pirate_ship.move(Circle(owner.home_point, self.pirate_home_radius), Callback(self._arrived, pirate_ship)) owner.ships[pirate_ship] = owner.shipStates.going_home self.log.debug('Pirate %s: Ship %s(%s): sailing home at %s' % (owner.worldid, pirate_ship.get_component(NamedComponent).name, owner.ships[pirate_ship], owner.home_point)) except MoveNotPossible: owner.ships[pirate_ship] = owner.shipStates.idle self.log.debug('Pirate %s: Ship %s: unable to move home at %s' % (owner.worldid, pirate_ship.get_component(NamedComponent).name, owner.home_point))
def get_surrounding_tiles(self, where, radius=1, include_corners=True): """Returns tiles around point with specified radius. @param point: instance of Point, or object with get_surrounding()""" if hasattr(where, "get_surrounding"): coords = where.get_surrounding(include_corners=include_corners) else: # assume Point coords = Circle(where, radius).tuple_iter() for position in coords: tile = self.get_tile_tuple(position) if tile is not None: yield tile
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 range(warehouse_class.width): for dy in range(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]))) available_spots_list = [ x for x in available_spots_list if warehouse_class.check_build( land_manager.session, Point(*x), check_settlement=False) ] 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 _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 _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 get_points_in_radius(self, position, radius, shuffle=False): """Returns all points in the radius around the point. This is a generator; make sure you use it appropriately. @param position: Point instance @return List of points in radius. """ assert isinstance(position, Point) points = Circle(position, radius) if shuffle: points = list(points) self.session.random.shuffle(points) for point in points: if self.map_dimensions.contains_without_border(point): # don't yield if point is not in map, those points don't exist yield point
def move_to_next_route_warehouse(self, advance_waypoint=True): next_destination = self.get_next_destination(advance_waypoint) if next_destination is None: return warehouse = next_destination['warehouse'] if self.ship.position.distance(warehouse.position.center) <= self.ship.radius: self.on_route_warehouse_reached() return try: self.ship.move(Circle(warehouse.position.center, self.ship.radius), self.on_route_warehouse_reached, blocked_callback = self.on_ship_blocked) except MoveNotPossible: # retry in 5 seconds Scheduler().add_new_object(self.on_ship_blocked, self, GAME_SPEED.TICKS_PER_SECOND * 5)
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 __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 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 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 create_random_island(map_db, island_id, id_string): """Creates a random island as sqlite db. It is rather primitive; it places shapes on the dict. The coordinates of tiles will be 0 <= x < width and 0 <= y < height @param id_string: random island id string """ match_obj = re.match(_random_island_id_regexp, id_string) assert match_obj creation_method, width, height, seed, island_x, island_y = [ long(i) for i in match_obj.groups() ] assert creation_method == 2, 'The only supported island creation method is 2.' rand = random.Random(seed) map_set = set() # place this number of shapes for i in xrange(15 + width * height // 45): # place shape determined by shape_id on (x, y) add = True shape_id = rand.randint(2, 8) rect_chance = 29 if rand.randint(0, 4) == 0: rect_chance = 13 add = False shape = None if rand.randint(1, rect_chance) == 1: # use a rect if add: x = rand.randint(8, width - 7) y = rand.randint(8, height - 7) else: x = rand.randint(0, width) y = rand.randint(0, height) shape = Rect.init_from_topleft_and_size(x - 5, y - 5, rand.randint(2, 8), rand.randint(2, 8)) else: # use a circle such that the radius is determined by shape_id radius = shape_id if not add and rand.randint(0, 6) < 5: x = rand.randint(-radius * 3 // 2, width + radius * 3 // 2) y = rand.randint(-radius * 3 // 2, height + radius * 3 // 2) shape = Circle(Point(x, y), shape_id) elif width - radius - 4 >= radius + 3 and height - radius - 4 >= radius + 3: x = rand.randint(radius + 3, width - radius - 4) y = rand.randint(radius + 3, height - radius - 4) shape = Circle(Point(x, y), shape_id) if shape: for shape_coord in shape.tuple_iter(): if add: map_set.add(shape_coord) elif shape_coord in map_set: map_set.discard(shape_coord) # write values to db map_db("BEGIN TRANSACTION") # add grass tiles for x, y in map_set: map_db("INSERT INTO ground VALUES(?, ?, ?, ?, ?, ?)", island_id, island_x + x, island_y + y, *GROUND.DEFAULT_LAND) def fill_tiny_spaces(tile): """Fills 1 tile gulfs and straits with the specified tile @param tile: ground tile to fill with """ all_neighbours = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)] neighbours = [(-1, 0), (0, -1), (0, 1), (1, 0)] corners = [(-1, -1), (-1, 1)] knight_moves = [(-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (1, 2), (2, -1), (2, 1)] bad_configs = set([ 0, 1 << 0, 1 << 1, 1 << 2, 1 << 3, (1 << 0) | (1 << 3), (1 << 1) | (1 << 2) ]) edge_set = copy.copy(map_set) reduce_edge_set = True while True: to_fill = set() to_ignore = set() for x, y in edge_set: # ignore the tiles with no empty neighbours if reduce_edge_set: is_edge = False for x_offset, y_offset in all_neighbours: if (x + x_offset, y + y_offset) not in map_set: is_edge = True break if not is_edge: to_ignore.add((x, y)) continue for x_offset, y_offset in neighbours: x2 = x + x_offset y2 = y + y_offset if (x2, y2) in map_set: continue # (x2, y2) is now a point just off the island neighbours_dirs = 0 for i in xrange(len(neighbours)): x3 = x2 + neighbours[i][0] y3 = y2 + neighbours[i][1] if (x3, y3) not in map_set: neighbours_dirs |= (1 << i) if neighbours_dirs in bad_configs: # part of a straight 1 tile gulf to_fill.add((x2, y2)) else: for x_offset, y_offset in corners: x3 = x2 + x_offset y3 = y2 + y_offset x4 = x2 - x_offset y4 = y2 - y_offset if (x3, y3) in map_set and (x4, y4) in map_set: # part of a diagonal 1 tile gulf to_fill.add((x2, y2)) break # block 1 tile straits for x_offset, y_offset in knight_moves: x2 = x + x_offset y2 = y + y_offset if (x2, y2) not in map_set: continue if abs(x_offset) == 1: y2 = y + y_offset // 2 if (x2, y2) in map_set or (x, y2) in map_set: continue else: x2 = x + x_offset // 2 if (x2, y2) in map_set or (x2, y) in map_set: continue to_fill.add((x2, y2)) # block diagonal 1 tile straits for x_offset, y_offset in corners: x2 = x + x_offset y2 = y + y_offset x3 = x + 2 * x_offset y3 = y + 2 * y_offset if (x2, y2) not in map_set and (x3, y3) in map_set: to_fill.add((x2, y2)) elif (x2, y2) in map_set and (x2, y) not in map_set and ( x, y2) not in map_set: to_fill.add((x2, y)) if to_fill: for x, y in to_fill: map_set.add((x, y)) map_db("INSERT INTO ground VALUES(?, ?, ?, ?, ?, ?)", island_id, island_x + x, island_y + y, *tile) old_size = len(edge_set) edge_set = edge_set.difference(to_ignore).union(to_fill) reduce_edge_set = old_size - len(edge_set) > 50 else: break # possible movement directions all_moves = { 'sw': (-1, -1), 'w': (-1, 0), 'nw': (-1, 1), 's': (0, -1), 'n': (0, 1), 'se': (1, -1), 'e': (1, 0), 'ne': (1, 1) } def get_island_outline(): """ @return: the points just off the island as a dict """ result = set() for x, y in map_set: for offset_x, offset_y in all_moves.itervalues(): coords = (x + offset_x, y + offset_y) if coords not in map_set: result.add(coords) return result # add grass to sand tiles fill_tiny_spaces(GROUND.DEFAULT_LAND) outline = get_island_outline() for x, y in outline: filled = [] for dir in sorted(all_moves): coords = (x + all_moves[dir][1], y + all_moves[dir][0]) if coords in map_set: filled.append(dir) tile = None # straight coast or 1 tile U-shaped gulfs if filled == ['s', 'se', 'sw'] or filled == ['s']: tile = GROUND.SAND_NORTH elif filled == ['e', 'ne', 'se'] or filled == ['e']: tile = GROUND.SAND_WEST elif filled == ['n', 'ne', 'nw'] or filled == ['n']: tile = GROUND.SAND_SOUTH elif filled == ['nw', 'sw', 'w'] or filled == ['w']: tile = GROUND.SAND_EAST # slight turn (looks best with straight coast) elif filled == ['e', 'se'] or filled == ['e', 'ne']: tile = GROUND.SAND_WEST elif filled == ['n', 'ne'] or filled == ['n', 'nw']: tile = GROUND.SAND_SOUTH elif filled == ['nw', 'w'] or filled == ['sw', 'w']: tile = GROUND.SAND_EAST elif filled == ['s', 'sw'] or filled == ['s', 'se']: tile = GROUND.SAND_NORTH # sandy corner elif filled == ['se']: tile = GROUND.SAND_NORTHWEST1 elif filled == ['ne']: tile = GROUND.SAND_SOUTHWEST1 elif filled == ['nw']: tile = GROUND.SAND_SOUTHEAST1 elif filled == ['sw']: tile = GROUND.SAND_NORTHEAST1 # grassy corner elif 3 <= len(filled) <= 5: coast_set = set(filled) if 'e' in coast_set and 'se' in coast_set and 's' in coast_set: tile = GROUND.SAND_NORTHEAST3 elif 's' in coast_set and 'sw' in coast_set and 'w' in coast_set: tile = GROUND.SAND_NORTHWEST3 elif 'w' in coast_set and 'nw' in coast_set and 'n' in coast_set: tile = GROUND.SAND_SOUTHWEST3 elif 'n' in coast_set and 'ne' in coast_set and 'e' in coast_set: tile = GROUND.SAND_SOUTHEAST3 assert tile map_db("INSERT INTO ground VALUES(?, ?, ?, ?, ?, ?)", island_id, island_x + x, island_y + y, *tile) map_set = map_set.union(outline) # add sand to shallow water tiles fill_tiny_spaces(GROUND.SAND) outline = get_island_outline() for x, y in outline: filled = [] for dir in sorted(all_moves): coords = (x + all_moves[dir][1], y + all_moves[dir][0]) if coords in map_set: filled.append(dir) tile = None # straight coast or 1 tile U-shaped gulfs if filled == ['s', 'se', 'sw'] or filled == ['s']: tile = GROUND.COAST_NORTH elif filled == ['e', 'ne', 'se'] or filled == ['e']: tile = GROUND.COAST_WEST elif filled == ['n', 'ne', 'nw'] or filled == ['n']: tile = GROUND.COAST_SOUTH elif filled == ['nw', 'sw', 'w'] or filled == ['w']: tile = GROUND.COAST_EAST # slight turn (looks best with straight coast) elif filled == ['e', 'se'] or filled == ['e', 'ne']: tile = GROUND.COAST_WEST elif filled == ['n', 'ne'] or filled == ['n', 'nw']: tile = GROUND.COAST_SOUTH elif filled == ['nw', 'w'] or filled == ['sw', 'w']: tile = GROUND.COAST_EAST elif filled == ['s', 'sw'] or filled == ['s', 'se']: tile = GROUND.COAST_NORTH # mostly wet corner elif filled == ['se']: tile = GROUND.COAST_NORTHWEST1 elif filled == ['ne']: tile = GROUND.COAST_SOUTHWEST1 elif filled == ['nw']: tile = GROUND.COAST_SOUTHEAST1 elif filled == ['sw']: tile = GROUND.COAST_NORTHEAST1 # mostly dry corner elif 3 <= len(filled) <= 5: coast_set = set(filled) if 'e' in coast_set and 'se' in coast_set and 's' in coast_set: tile = GROUND.COAST_NORTHEAST3 elif 's' in coast_set and 'sw' in coast_set and 'w' in coast_set: tile = GROUND.COAST_NORTHWEST3 elif 'w' in coast_set and 'nw' in coast_set and 'n' in coast_set: tile = GROUND.COAST_SOUTHWEST3 elif 'n' in coast_set and 'ne' in coast_set and 'e' in coast_set: tile = GROUND.COAST_SOUTHEAST3 assert tile map_db("INSERT INTO ground VALUES(?, ?, ?, ?, ?, ?)", island_id, island_x + x, island_y + y, *tile) map_set = map_set.union(outline) # add shallow water to deep water tiles fill_tiny_spaces(GROUND.SHALLOW_WATER) outline = get_island_outline() for x, y in outline: filled = [] for dir in sorted(all_moves): coords = (x + all_moves[dir][1], y + all_moves[dir][0]) if coords in map_set: filled.append(dir) tile = None # straight coast or 1 tile U-shaped gulfs if filled == ['s', 'se', 'sw'] or filled == ['s']: tile = GROUND.DEEP_WATER_NORTH elif filled == ['e', 'ne', 'se'] or filled == ['e']: tile = GROUND.DEEP_WATER_WEST elif filled == ['n', 'ne', 'nw'] or filled == ['n']: tile = GROUND.DEEP_WATER_SOUTH elif filled == ['nw', 'sw', 'w'] or filled == ['w']: tile = GROUND.DEEP_WATER_EAST # slight turn (looks best with straight coast) elif filled == ['e', 'se'] or filled == ['e', 'ne']: tile = GROUND.DEEP_WATER_WEST elif filled == ['n', 'ne'] or filled == ['n', 'nw']: tile = GROUND.DEEP_WATER_SOUTH elif filled == ['nw', 'w'] or filled == ['sw', 'w']: tile = GROUND.DEEP_WATER_EAST elif filled == ['s', 'sw'] or filled == ['s', 'se']: tile = GROUND.DEEP_WATER_NORTH # mostly deep corner elif filled == ['se']: tile = GROUND.DEEP_WATER_NORTHWEST1 elif filled == ['ne']: tile = GROUND.DEEP_WATER_SOUTHWEST1 elif filled == ['nw']: tile = GROUND.DEEP_WATER_SOUTHEAST1 elif filled == ['sw']: tile = GROUND.DEEP_WATER_NORTHEAST1 # mostly shallow corner elif 3 <= len(filled) <= 5: coast_set = set(filled) if 'e' in coast_set and 'se' in coast_set and 's' in coast_set: tile = GROUND.DEEP_WATER_NORTHEAST3 elif 's' in coast_set and 'sw' in coast_set and 'w' in coast_set: tile = GROUND.DEEP_WATER_NORTHWEST3 elif 'w' in coast_set and 'nw' in coast_set and 'n' in coast_set: tile = GROUND.DEEP_WATER_SOUTHWEST3 elif 'n' in coast_set and 'ne' in coast_set and 'e' in coast_set: tile = GROUND.DEEP_WATER_SOUTHEAST3 assert tile map_db("INSERT INTO ground VALUES(?, ?, ?, ?, ?, ?)", island_id, island_x + x, island_y + y, *tile) map_db("COMMIT")
def _place_tile(self, coords): brush = Circle(Point(*coords), self.session.world_editor.brush_size - 1) self.session.world_editor.intermediate_map.set_south_east_corner(brush.tuple_iter(), self._tile_details)
def create_random_island(map_db, island_id, id_string): """Creates a random island as sqlite db. It is rather primitive; it places shapes on the dict. The coordinates of tiles will be 0 <= x < width and 0 <= y < height @param id_string: random island id string """ match_obj = re.match(_random_island_id_regexp, id_string) assert match_obj creation_method, width, height, seed, island_x, island_y = [long(i) for i in match_obj.groups()] assert creation_method == 2, 'The only supported island creation method is 2.' rand = random.Random(seed) map_set = set() # place this number of shapes for i in xrange(15 + width * height // 45): # place shape determined by shape_id on (x, y) add = True shape_id = rand.randint(2, 8) rect_chance = 29 if rand.randint(0, 4) == 0: rect_chance = 13 add = False shape = None if rand.randint(1, rect_chance) == 1: # use a rect if add: x = rand.randint(8, width - 7) y = rand.randint(8, height - 7) else: x = rand.randint(0, width) y = rand.randint(0, height) shape = Rect.init_from_topleft_and_size(x - 5, y - 5, rand.randint(2, 8), rand.randint(2, 8)) else: # use a circle such that the radius is determined by shape_id radius = shape_id if not add and rand.randint(0, 6) < 5: x = rand.randint(-radius * 3 // 2, width + radius * 3 // 2) y = rand.randint(-radius * 3 // 2, height + radius * 3 // 2) shape = Circle(Point(x, y), shape_id) elif width - radius - 4 >= radius + 3 and height - radius - 4 >= radius + 3: x = rand.randint(radius + 3, width - radius - 4) y = rand.randint(radius + 3, height - radius - 4) shape = Circle(Point(x, y), shape_id) if shape: for shape_coord in shape.tuple_iter(): if add: map_set.add(shape_coord) elif shape_coord in map_set: map_set.discard(shape_coord) # write values to db map_db("BEGIN TRANSACTION") # add grass tiles for x, y in map_set: map_db("INSERT INTO ground VALUES(?, ?, ?, ?, ?, ?)", island_id, island_x + x, island_y + y, *GROUND.DEFAULT_LAND) def fill_tiny_spaces(tile): """Fills 1 tile gulfs and straits with the specified tile @param tile: ground tile to fill with """ all_neighbours = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)] neighbours = [(-1, 0), (0, -1), (0, 1), (1, 0)] corners = [(-1, -1), (-1, 1)] knight_moves = [(-2, -1), (-2, 1), (-1, -2), (-1, 2), (1, -2), (1, 2), (2, -1), (2, 1)] bad_configs = set([0, 1 << 0, 1 << 1, 1 << 2, 1 << 3, (1 << 0) | (1 << 3), (1 << 1) | (1 << 2)]) edge_set = copy.copy(map_set) reduce_edge_set = True while True: to_fill = set() to_ignore = set() for x, y in edge_set: # ignore the tiles with no empty neighbours if reduce_edge_set: is_edge = False for x_offset, y_offset in all_neighbours: if (x + x_offset, y + y_offset) not in map_set: is_edge = True break if not is_edge: to_ignore.add((x, y)) continue for x_offset, y_offset in neighbours: x2 = x + x_offset y2 = y + y_offset if (x2, y2) in map_set: continue # (x2, y2) is now a point just off the island neighbours_dirs = 0 for i in xrange(len(neighbours)): x3 = x2 + neighbours[i][0] y3 = y2 + neighbours[i][1] if (x3, y3) not in map_set: neighbours_dirs |= (1 << i) if neighbours_dirs in bad_configs: # part of a straight 1 tile gulf to_fill.add((x2, y2)) else: for x_offset, y_offset in corners: x3 = x2 + x_offset y3 = y2 + y_offset x4 = x2 - x_offset y4 = y2 - y_offset if (x3, y3) in map_set and (x4, y4) in map_set: # part of a diagonal 1 tile gulf to_fill.add((x2, y2)) break # block 1 tile straits for x_offset, y_offset in knight_moves: x2 = x + x_offset y2 = y + y_offset if (x2, y2) not in map_set: continue if abs(x_offset) == 1: y2 = y + y_offset // 2 if (x2, y2) in map_set or (x, y2) in map_set: continue else: x2 = x + x_offset // 2 if (x2, y2) in map_set or (x2, y) in map_set: continue to_fill.add((x2, y2)) # block diagonal 1 tile straits for x_offset, y_offset in corners: x2 = x + x_offset y2 = y + y_offset x3 = x + 2 * x_offset y3 = y + 2 * y_offset if (x2, y2) not in map_set and (x3, y3) in map_set: to_fill.add((x2, y2)) elif (x2, y2) in map_set and (x2, y) not in map_set and (x, y2) not in map_set: to_fill.add((x2, y)) if to_fill: for x, y in to_fill: map_set.add((x, y)) map_db("INSERT INTO ground VALUES(?, ?, ?, ?, ?, ?)", island_id, island_x + x, island_y + y, *tile) old_size = len(edge_set) edge_set = edge_set.difference(to_ignore).union(to_fill) reduce_edge_set = old_size - len(edge_set) > 50 else: break # possible movement directions all_moves = { 'sw' : (-1, -1), 'w' : (-1, 0), 'nw' : (-1, 1), 's' : (0, -1), 'n' : (0, 1), 'se' : (1, -1), 'e' : (1, 0), 'ne' : (1, 1) } def get_island_outline(): """ @return: the points just off the island as a dict """ result = set() for x, y in map_set: for offset_x, offset_y in all_moves.itervalues(): coords = (x + offset_x, y + offset_y) if coords not in map_set: result.add(coords) return result # add grass to sand tiles fill_tiny_spaces(GROUND.DEFAULT_LAND) outline = get_island_outline() for x, y in outline: filled = [] for dir in sorted(all_moves): coords = (x + all_moves[dir][1], y + all_moves[dir][0]) if coords in map_set: filled.append(dir) tile = None # straight coast or 1 tile U-shaped gulfs if filled == ['s', 'se', 'sw'] or filled == ['s']: tile = GROUND.SAND_NORTH elif filled == ['e', 'ne', 'se'] or filled == ['e']: tile = GROUND.SAND_WEST elif filled == ['n', 'ne', 'nw'] or filled == ['n']: tile = GROUND.SAND_SOUTH elif filled == ['nw', 'sw', 'w'] or filled == ['w']: tile = GROUND.SAND_EAST # slight turn (looks best with straight coast) elif filled == ['e', 'se'] or filled == ['e', 'ne']: tile = GROUND.SAND_WEST elif filled == ['n', 'ne'] or filled == ['n', 'nw']: tile = GROUND.SAND_SOUTH elif filled == ['nw', 'w'] or filled == ['sw', 'w']: tile = GROUND.SAND_EAST elif filled == ['s', 'sw'] or filled == ['s', 'se']: tile = GROUND.SAND_NORTH # sandy corner elif filled == ['se']: tile = GROUND.SAND_NORTHWEST1 elif filled == ['ne']: tile = GROUND.SAND_SOUTHWEST1 elif filled == ['nw']: tile = GROUND.SAND_SOUTHEAST1 elif filled == ['sw']: tile = GROUND.SAND_NORTHEAST1 # grassy corner elif 3 <= len(filled) <= 5: coast_set = set(filled) if 'e' in coast_set and 'se' in coast_set and 's' in coast_set: tile = GROUND.SAND_NORTHEAST3 elif 's' in coast_set and 'sw' in coast_set and 'w' in coast_set: tile = GROUND.SAND_NORTHWEST3 elif 'w' in coast_set and 'nw' in coast_set and 'n' in coast_set: tile = GROUND.SAND_SOUTHWEST3 elif 'n' in coast_set and 'ne' in coast_set and 'e' in coast_set: tile = GROUND.SAND_SOUTHEAST3 assert tile map_db("INSERT INTO ground VALUES(?, ?, ?, ?, ?, ?)", island_id, island_x + x, island_y + y, *tile) map_set = map_set.union(outline) # add sand to shallow water tiles fill_tiny_spaces(GROUND.SAND) outline = get_island_outline() for x, y in outline: filled = [] for dir in sorted(all_moves): coords = (x + all_moves[dir][1], y + all_moves[dir][0]) if coords in map_set: filled.append(dir) tile = None # straight coast or 1 tile U-shaped gulfs if filled == ['s', 'se', 'sw'] or filled == ['s']: tile = GROUND.COAST_NORTH elif filled == ['e', 'ne', 'se'] or filled == ['e']: tile = GROUND.COAST_WEST elif filled == ['n', 'ne', 'nw'] or filled == ['n']: tile = GROUND.COAST_SOUTH elif filled == ['nw', 'sw', 'w'] or filled == ['w']: tile = GROUND.COAST_EAST # slight turn (looks best with straight coast) elif filled == ['e', 'se'] or filled == ['e', 'ne']: tile = GROUND.COAST_WEST elif filled == ['n', 'ne'] or filled == ['n', 'nw']: tile = GROUND.COAST_SOUTH elif filled == ['nw', 'w'] or filled == ['sw', 'w']: tile = GROUND.COAST_EAST elif filled == ['s', 'sw'] or filled == ['s', 'se']: tile = GROUND.COAST_NORTH # mostly wet corner elif filled == ['se']: tile = GROUND.COAST_NORTHWEST1 elif filled == ['ne']: tile = GROUND.COAST_SOUTHWEST1 elif filled == ['nw']: tile = GROUND.COAST_SOUTHEAST1 elif filled == ['sw']: tile = GROUND.COAST_NORTHEAST1 # mostly dry corner elif 3 <= len(filled) <= 5: coast_set = set(filled) if 'e' in coast_set and 'se' in coast_set and 's' in coast_set: tile = GROUND.COAST_NORTHEAST3 elif 's' in coast_set and 'sw' in coast_set and 'w' in coast_set: tile = GROUND.COAST_NORTHWEST3 elif 'w' in coast_set and 'nw' in coast_set and 'n' in coast_set: tile = GROUND.COAST_SOUTHWEST3 elif 'n' in coast_set and 'ne' in coast_set and 'e' in coast_set: tile = GROUND.COAST_SOUTHEAST3 assert tile map_db("INSERT INTO ground VALUES(?, ?, ?, ?, ?, ?)", island_id, island_x + x, island_y + y, *tile) map_set = map_set.union(outline) # add shallow water to deep water tiles fill_tiny_spaces(GROUND.SHALLOW_WATER) outline = get_island_outline() for x, y in outline: filled = [] for dir in sorted(all_moves): coords = (x + all_moves[dir][1], y + all_moves[dir][0]) if coords in map_set: filled.append(dir) tile = None # straight coast or 1 tile U-shaped gulfs if filled == ['s', 'se', 'sw'] or filled == ['s']: tile = GROUND.DEEP_WATER_NORTH elif filled == ['e', 'ne', 'se'] or filled == ['e']: tile = GROUND.DEEP_WATER_WEST elif filled == ['n', 'ne', 'nw'] or filled == ['n']: tile = GROUND.DEEP_WATER_SOUTH elif filled == ['nw', 'sw', 'w'] or filled == ['w']: tile = GROUND.DEEP_WATER_EAST # slight turn (looks best with straight coast) elif filled == ['e', 'se'] or filled == ['e', 'ne']: tile = GROUND.DEEP_WATER_WEST elif filled == ['n', 'ne'] or filled == ['n', 'nw']: tile = GROUND.DEEP_WATER_SOUTH elif filled == ['nw', 'w'] or filled == ['sw', 'w']: tile = GROUND.DEEP_WATER_EAST elif filled == ['s', 'sw'] or filled == ['s', 'se']: tile = GROUND.DEEP_WATER_NORTH # mostly deep corner elif filled == ['se']: tile = GROUND.DEEP_WATER_NORTHWEST1 elif filled == ['ne']: tile = GROUND.DEEP_WATER_SOUTHWEST1 elif filled == ['nw']: tile = GROUND.DEEP_WATER_SOUTHEAST1 elif filled == ['sw']: tile = GROUND.DEEP_WATER_NORTHEAST1 # mostly shallow corner elif 3 <= len(filled) <= 5: coast_set = set(filled) if 'e' in coast_set and 'se' in coast_set and 's' in coast_set: tile = GROUND.DEEP_WATER_NORTHEAST3 elif 's' in coast_set and 'sw' in coast_set and 'w' in coast_set: tile = GROUND.DEEP_WATER_NORTHWEST3 elif 'w' in coast_set and 'nw' in coast_set and 'n' in coast_set: tile = GROUND.DEEP_WATER_SOUTHWEST3 elif 'n' in coast_set and 'ne' in coast_set and 'e' in coast_set: tile = GROUND.DEEP_WATER_SOUTHEAST3 assert tile map_db("INSERT INTO ground VALUES(?, ?, ?, ?, ?, ?)", island_id, island_x + x, island_y + y, *tile) map_db("COMMIT")
def get_warehouse_area(self, settlement, range=10): return Circle(self.get_warehouse_point(settlement), range)
def get_positions(): iters = (iter(Circle(point, radius)) for radius in xrange( cls.CHECK_NEARBY_LOCATIONS_UP_TO_DISTANCE)) return itertools.chain.from_iterable(iters)
def _place_tile(self, coords): brush = Circle(Point(*coords), self.session.world_editor.brush_size - 1) self.session.world_editor.intermediate_map.set_south_east_corner( brush.tuple_iter(), self._tile_details)