def __init__(self, db, islandid, session): """ @param db: db instance with island table @param islandid: id of island in that table @param session: reference to Session instance """ super(Island, self).__init__(worldid=islandid) self.session = session x, y, filename = db( "SELECT x, y, file FROM island WHERE rowid = ? - 1000", islandid)[0] self.__init(Point(x, y), filename) # create building indexers self.building_indexers = {} self.building_indexers[BUILDINGS.TREE_CLASS] = BuildingIndexer( WildAnimal.walking_range, self, self.session.random) # load settlements for (settlement_id, ) in db( "SELECT rowid FROM settlement WHERE island = ?", islandid): Settlement.load(db, settlement_id, self.session) # load buildings from horizons.world import load_building for (building_worldid, building_typeid) in \ db("SELECT rowid, type FROM building WHERE location = ?", islandid): load_building(self.session, db, building_typeid, building_worldid)
class World(BuildingOwner, LivingObject, WorldObject): """The World class represents an Unknown Horizons map with all its units, grounds, buildings, etc. * players - a list of all the session's players - Player instances * islands - a list of all the map's islands - Island instances * grounds - a list of all the map's groundtiles * ground_map - a dictionary that binds tuples of coordinates with a reference to the tile: { (x, y): tileref, ...} This is important for pathfinding and quick tile fetching.z * full_map - a dictionary that binds tuples of coordinates with a reference to the tile (includes water and ground) * island_map - a dictionary that binds tuples of coordinates with a reference to the island * ships - a list of all the ships ingame - horizons.world.units.ship.Ship instances * ship_map - same as ground_map, but for ships * fish_indexer - a BuildingIndexer for all fish on the map * session - reference to horizons.session.Session instance of the current game * water - Dictionary of coordinates that are water * water_body - Dictionary of water bodies {coords: area_number, ...} * sea_number - The water_body number of the sea * trader - The world's ingame free trader player instance * pirate - The world's ingame pirate player instance TUTORIAL: You should now check out the _init() function. """ log = logging.getLogger("world") def __init__(self, session): """ @param session: instance of session the world belongs to. """ self.inited = False self.session = session super(World, self).__init__(worldid=GAME.WORLD_WORLDID) def end(self): self.session = None self.properties = None self.players = None self.player = None self.ground_map = None self.full_map = None self.island_map = None self.water = None self.ships = None self.ship_map = None self.fish_indexer = None self.ground_units = None self.trader = None self.pirate = None self.islands = None self.diplomacy = None self.bullets = None super(World, self).end() @decorators.make_constants() def _init(self, savegame_db): """ @param savegame_db: Dbreader with loaded savegame database """ #load properties self.properties = {} for (name, value) in savegame_db("SELECT name, value FROM map_properties"): self.properties[name] = value # create playerlist self.players = [] self.player = None # player sitting in front of this machine self.trader = None self.pirate = None # load player human_players = [] for player_worldid, client_id in savegame_db("SELECT rowid, client_id FROM player WHERE is_trader = 0 and is_pirate = 0 ORDER BY rowid"): player = None # check if player is an ai ai_data = self.session.db("SELECT class_package, class_name FROM ai WHERE client_id = ?", client_id) if len(ai_data) > 0: class_package, class_name = ai_data[0] # import ai class and call load on it module = __import__('horizons.ai.'+class_package, fromlist=[class_name]) ai_class = getattr(module, class_name) player = ai_class.load(self.session, savegame_db, player_worldid) else: # no ai player = HumanPlayer.load(self.session, savegame_db, player_worldid) self.players.append(player) if client_id == horizons.main.fife.get_uh_setting("ClientID"): self.player = player elif client_id is not None and len(ai_data) == 0: # possible human player candidate with different client id human_players.append(player) if self.player is None: # we have no human player. # check if there is only one player with an id (i.e. human player) # this would be the case if the savegame originates from a different installation. # if there's more than one of this kind, we can't be sure what to select. # TODO: create interface for selecting player, if we want this if(len(human_players) == 1): # exactly one player, we can quite safely use this one self.player = human_players[0] elif not human_players and self.players: # the first player should be the human-ai hybrid self.player = self.players[0] if self.player is not None: self.player.inventory.add_change_listener(self.session.ingame_gui.update_gold, \ call_listener_now=True) if self.player is None and self.session.is_game_loaded(): self.log.warning('WARNING: Cannot autoselect a player because there are no \ or multiple candidates.') # load islands self.islands = [] for (islandid,) in savegame_db("SELECT rowid + 1000 FROM island"): island = Island(savegame_db, islandid, self.session) 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 = i.rect.left if self.min_x is None or i.rect.left < self.min_x else self.min_x self.min_y = i.rect.top if self.min_y is None or i.rect.top < self.min_y else self.min_y self.max_x = i.rect.right if self.max_x is None or i.rect.right > self.max_x else self.max_x self.max_y = i.rect.bottom if self.max_y is None or i.rect.bottom > self.max_y else 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 = {} default_grounds = Entities.grounds[int(self.properties.get('default_ground', GROUND.WATER))] # extra world size that is added so that he player can't see the "black void" border = 30 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): ground = 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)] = ground # 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 # load world buildings (e.g. fish) for (building_worldid, building_typeid) in \ savegame_db("SELECT rowid, type FROM building WHERE location = ?", self.worldid): load_building(self.session, savegame_db, building_typeid, building_worldid) # use a dict because it's directly supported by the pathfinding algo self.water = dict.fromkeys(list(self.ground_map), 1.0) self._init_water_bodies() self.sea_number = self.water_body[(self.min_x, self.min_y)] # assemble list of water and coastline for ship, that can drive through shallow water # NOTE: this is rather a temporary fix to make the fisher be able to move # since there are tile between coastline and deep sea, all non-constructible tiles # are added to this list as well, which will contain a few too many self.water_and_coastline = copy.copy(self.water) for island in self.islands: for coord, tile in island.ground_map.iteritems(): if 'coastline' in tile.classes or 'constructible' not in tile.classes: self.water_and_coastline[coord] = 1.0 # create ship position list. entries: ship_map[(x, y)] = ship self.ship_map = {} self.ground_unit_map = {} # create shiplist, which is currently used for saving ships # and having at least one reference to them self.ships = [] self.ground_units = [] # create bullets list, used for saving bullets in ongoing attacks self.bullets = [] if self.session.is_game_loaded(): # there are 0 or 1 trader AIs so this is safe trader_data = savegame_db("SELECT rowid FROM player WHERE is_trader = 1") if trader_data: self.trader = Trader.load(self.session, savegame_db, trader_data[0][0]) # there are 0 or 1 pirate AIs so this is safe pirate_data = savegame_db("SELECT rowid FROM player WHERE is_pirate = 1") if pirate_data: self.pirate = Pirate.load(self.session, savegame_db, pirate_data[0][0]) # load all units (we do it here cause all buildings are loaded by now) for (worldid, typeid) in savegame_db("SELECT rowid, type FROM unit ORDER BY rowid"): Entities.units[typeid].load(self.session, savegame_db, worldid) if self.session.is_game_loaded(): # let trader command it's ships. we have to do this here cause ships have to be # initialised for this, and trader has to exist before ships are loaded. if self.trader: self.trader.load_ship_states(savegame_db) # let pirate command it's ships. we have to do this here cause ships have to be # initialised for this, and pirate has to exist before ships are loaded. if self.pirate: self.pirate.load_ship_states(savegame_db) # load the AI players # this has to be done here because otherwise the ships and other objects won't exist AIPlayer.load_abstract_buildings(self.session.db) # TODO: find a better place for this for player in self.players: if not isinstance(player, HumanPlayer): player.finish_loading(savegame_db) # load bullets if self.session.is_game_loaded(): for (worldid, sx, sy, dx, dy, speed, img) in savegame_db("SELECT worldid, startx, starty, destx, desty, speed, image FROM bullet"): Bullet(img, Point(sx, sy), Point(dx, dy), speed, self.session, False, worldid) # load ongoing attacks if self.session.is_game_loaded(): Weapon.load_attacks(self.session, savegame_db) # load diplomacy self.diplomacy = Diplomacy() if self.session.is_game_loaded(): self.diplomacy.load(self, savegame_db) # add diplomacy notification listeners def notify_change(caller, change_type, a, b): player1 = a.name player2 = b.name #check if status really changed, if so update status string if change_type == 'friend': status = 'friends' elif change_type == 'enemy': status = 'enemies' else: status = 'neutral' self.session.ingame_gui.message_widget.add(self.max_x/2, self.max_y/2, 'DIPLOMACY_STATUS_CHANGED', {'player1' : player1, 'player2' : player2, 'status' : status}) self.diplomacy.add_diplomacy_status_changed_listener(notify_change) self.inited = True """TUTORIAL: To dig deeper, you should now continue to horizons/world/island.py, to check out how buildings and settlements are added to the map""" def _init_water_bodies(self): """ This function runs the flood fill algorithm on the water to make it easy to recognise different water bodies """ moves = [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)] n = 0 self.water_body = dict.fromkeys(self.water) for coords, num in self.water_body.iteritems(): if num is not None: continue self.water_body[coords] = n queue = deque([coords]) while queue: x, y = queue[0] queue.popleft() for dx, dy in moves: coords2 = (x + dx, y + dy) if coords2 in self.water_body and self.water_body[coords2] is None: self.water_body[coords2] = n queue.append(coords2) n += 1 def init_fish_indexer(self): self.fish_indexer = BuildingIndexer(16, self.full_map) for tile in self.ground_map.itervalues(): if tile.object is not None and tile.object.id == BUILDINGS.FISH_DEPOSIT_CLASS: self.fish_indexer.add(tile.object) self.fish_indexer._update() @decorators.make_constants() def init_new_world(self, trader_enabled, pirate_enabled, natural_resource_multiplier): """ This should be called if a new map is loaded (not a savegame, a fresh map). In other words when it is loaded for the first time. NOTE: commands for creating the world objects are executed directly, bypassing the manager This is necessary because else the commands would be transmitted over the wire in network games. @return: the coordinates of the players first ship """ # workaround: the creation of all the objects causes a lot of logging output, we don't need # therefore, reset the levels for now loggers_to_silence = { 'world.production' : None } for logger_name in loggers_to_silence: logger = logging.getLogger(logger_name) loggers_to_silence[logger_name] = logger.getEffectiveLevel() logger.setLevel( logging.WARN ) # add a random number of environmental objects self._add_nature_objects(natural_resource_multiplier) # reset loggers, see above for logger_name, level in loggers_to_silence.iteritems(): logging.getLogger(logger_name).setLevel(level) # add free trader if trader_enabled: self.trader = Trader(self.session, 99999, u"Free Trader", Color()) ret_coords = None for player in self.players: # Adding ships for the players # hack to place the ship on the development map point = self.get_random_possible_ship_position() # Execute command directly, not via manager, because else it would be transmitted over the # network to other players. Those however will do the same thing anyways. ship = CreateUnit(player.worldid, UNITS.PLAYER_SHIP_CLASS, point.x, point.y)(issuer=self.session.world.player) # give ship basic resources for res, amount in self.session.db("SELECT resource, amount FROM start_resources"): ship.inventory.alter(res, amount) if player is self.player: ret_coords = point.to_tuple() AIPlayer.load_abstract_buildings(self.session.db) # TODO: find a better place for this # add a pirate ship if pirate_enabled: self.pirate = Pirate(self.session, 99998, "Captain Blackbeard", Color()) # Fire a message for new world creation self.session.ingame_gui.message_widget.add(self.max_x/2, self.max_y/2, 'NEW_WORLD') assert ret_coords is not None, "Return coords are None. No players loaded?" return ret_coords def _add_resource_deposits(self, resource_multiplier): """ Place clay deposits and mountains. The algorithm: 1. calculate the manhattan distance from each island tile to the sea 2. calculate the value of a tile 3. calculate the value of an object's location as min(covered tile values) 4. for each island place a number of clay deposits and mountains 5. place a number of extra clay deposits and mountains without caring about the island * the probability of choosing a resource deposit location is proportional to its value @param natural_resource_multiplier: multiply the amount of clay deposits and mountains by this. """ moves = [(-1, 0), (0, -1), (0, 1), (1, 0)] ClayDeposit = Entities.buildings[BUILDINGS.CLAY_DEPOSIT_CLASS] Mountain = Entities.buildings[BUILDINGS.MOUNTAIN_CLASS] clay_deposit_locations = [] mountain_locations = [] def get_valid_locations(usable_part, island, width, height): """Return a list of all valid locations for a width times height object in the format [(value, (x, y), island), ...].""" locations = [] offsets = list(itertools.product(xrange(width), xrange(height))) for x, y in sorted(usable_part): min_value = None for dx, dy in offsets: coords = (x + dx, y + dy) if coords in usable_part: value = usable_part[coords] min_value = value if min_value is None or min_value > value else min_value else: min_value = None break if min_value: locations.append((1.0 / min_value, (x, y), island)) return locations def place_objects(locations, max_objects, object_class): """Place at most max_objects objects of the given class.""" if not locations: return total_sum = [0] last_sum = 0 for value in zip(*locations)[0]: last_sum += value total_sum.append(last_sum) for _ in xrange(max_objects): for _ in xrange(7): # try to place the object 7 times object_sum = self.session.random.random() * last_sum pos = bisect.bisect_left(total_sum, object_sum, 0, len(total_sum) - 2) x, y = locations[pos][1] if object_class.check_build(self.session, Point(x, y), check_settlement = False): Build(object_class, x, y, ownerless = True, island = locations[pos][2])(issuer = None) break for island in self.islands: # mark island tiles that are next to the sea queue = deque() distance = {} for (x, y), tile in island.ground_map.iteritems(): if len(tile.classes) == 1: # could be a shallow to deep water tile for dx, dy in moves: coords = (x + dx, y + dy) if coords in self.water_body and self.water_body[coords] == self.sea_number: distance[(x, y)] = 1 queue.append((x, y, 1)) break # calculate the manhattan distance to the sea while queue: x, y, dist = queue[0] queue.popleft() for dx, dy in moves: coords = (x + dx, y + dy) if coords in distance: continue if coords in self.water_body and self.water_body[coords] == self.sea_number: continue distance[coords] = dist + 1 queue.append((coords[0], coords[1], dist + 1)) # calculate tiles' values usable_part = {} for coords, dist in distance.iteritems(): if coords in island.ground_map and 'constructible' in island.ground_map[coords].classes: usable_part[coords] = (dist + 5) ** 2 # place the local clay deposits local_clay_deposit_locations = get_valid_locations(usable_part, island, *ClayDeposit.size) clay_deposit_locations.extend(local_clay_deposit_locations) local_clay_deposits_base = 0.3 + len(local_clay_deposit_locations) ** 0.7 / 60.0 num_local_clay_deposits = int(max(0, resource_multiplier * min(3, local_clay_deposits_base + abs(self.session.random.gauss(0, 0.7))))) place_objects(local_clay_deposit_locations, num_local_clay_deposits, ClayDeposit) # place the local mountains local_mountain_locations = get_valid_locations(usable_part, island, *Mountain.size) mountain_locations.extend(local_mountain_locations) local_mountains_base = 0.1 + len(local_mountain_locations) ** 0.5 / 120.0 num_local_mountains = int(max(0, resource_multiplier * min(2, local_mountains_base + abs(self.session.random.gauss(0, 0.8))))) place_objects(local_mountain_locations, num_local_mountains, Mountain) # place some extra clay deposits extra_clay_base = len(clay_deposit_locations) ** 0.8 / 400.0 num_extra_clay_deposits = int(round(max(1, resource_multiplier * min(7, len(self.islands) * 1.0 + 2, extra_clay_base + abs(self.session.random.gauss(0, 1)))))) place_objects(clay_deposit_locations, num_extra_clay_deposits, ClayDeposit) # place some extra mountains extra_mountains_base = len(mountain_locations) ** 0.8 / 700.0 num_extra_mountains = int(round(max(1, resource_multiplier * min(4, len(self.islands) * 0.5 + 2, extra_mountains_base + abs(self.session.random.gauss(0, 0.7)))))) place_objects(mountain_locations, num_extra_mountains, Mountain) def _add_nature_objects(self, natural_resource_multiplier): """ Place trees, wild animals, fish deposits, clay deposits, and mountains. @param natural_resource_multiplier: multiply the amount of fish deposits, clay deposits, and mountains by this. """ if not int(self.properties.get('RandomTrees', 1)): return self._add_resource_deposits(natural_resource_multiplier) Tree = Entities.buildings[BUILDINGS.TREE_CLASS] FishDeposit = Entities.buildings[BUILDINGS.FISH_DEPOSIT_CLASS] fish_directions = [(i, j) for i in xrange(-1, 2) for j in xrange(-1, 2)] # add trees, wild animals, and fish for island in self.islands: for (x, y), tile in sorted(island.ground_map.iteritems()): # add tree to every nth tile and an animal to one in every M trees if self.session.random.randint(0, 2) == 0 and \ Tree.check_build(self.session, tile, check_settlement = False): building = Build(Tree, x, y, ownerless = True, island = island)(issuer = None) building.finish_production_now() # make trees big and fill their inventory if self.session.random.randint(0, WILD_ANIMAL.POPUlATION_INIT_RATIO) == 0: # add animal to every nth tree CreateUnit(island.worldid, UNITS.WILD_ANIMAL_CLASS, x, y)(issuer = None) if self.session.random.random() > WILD_ANIMAL.FOOD_AVAILABLE_ON_START: building.inventory.alter(RES.WILDANIMALFOOD_ID, -1) if 'coastline' in tile.classes and self.session.random.random() < natural_resource_multiplier / 4.0: # try to place fish: from the current position go to a random directions twice for (x_dir, y_dir) in self.session.random.sample(fish_directions, 2): # move a random amount in both directions fish_x = x + x_dir * self.session.random.randint(3, 9) fish_y = y + y_dir * self.session.random.randint(3, 9) # now we have the location, check if we can build here if (fish_x, fish_y) in self.ground_map: Build(FishDeposit, fish_x, fish_y, ownerless = True, island = self)(issuer = None) @decorators.make_constants() def get_random_possible_ground_unit_position(self): """Returns a position in water, that is not at the border of the world""" offset = 2 while True: x = self.session.random.randint(self.min_x + offset, self.max_x - offset) y = self.session.random.randint(self.min_y + offset, self.max_y - offset) if (x, y) in self.ground_unit_map: continue for island in self.islands: if (x, y) in island.path_nodes.nodes: return Point(x, y) @decorators.make_constants() def get_random_possible_ship_position(self): """Returns a position in water, that is not at the border of the world""" offset = 2 while True: x = self.session.random.randint(self.min_x + offset, self.max_x - offset) y = self.session.random.randint(self.min_y + offset, self.max_y - offset) if (x, y) in self.ship_map: continue # don't place ship where there is already a ship # check if there is an island nearby (check only important coords) position_possible = True for first_sign in (-1, 0, 1): for second_sign in (-1, 0, 1): point_to_check = Point( x + offset*first_sign, y + offset*second_sign ) if self.get_island(point_to_check) is not None: position_possible = False break if not position_possible: # propagate break continue # try another coord break # all checks successful return Point(x, y) @decorators.make_constants() def get_random_possible_coastal_ship_position(self): """Returns a position in water, that is not at the border of the world but on the coast of an island""" offset = 2 while True: x = self.session.random.randint(self.min_x + offset, self.max_x - offset) y = self.session.random.randint(self.min_y + offset, self.max_y - offset) if (x, y) in self.ship_map: continue # don't place ship where there is already a ship result = Point(x, y) if self.get_island(result) is not None: continue # don't choose a point on an island # check if there is an island nearby (check only important coords) for first_sign in (-1, 0, 1): for second_sign in (-1, 0, 1): point_to_check = Point( x + first_sign, y + second_sign ) if self.get_island(point_to_check) is not None: return result #---------------------------------------------------------------------- def get_tiles_in_radius(self, position, radius, shuffle=False): """Returns a all tiles in the radius around the point. This is a generator, make sure you use it appropriately. @param position: Point instance @return List of tiles in radius. """ for point in self.get_points_in_radius(position, radius, shuffle): yield self.get_tile(point) 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 setup_player(self, id, name, color, local, is_ai, difficulty_level): """Sets up a new Player instance and adds him to the active world. Only used for new games. Loading old players is done in _init(). @param local: bool, whether the player is the one sitting on front of this machine.""" inv = self.session.db.get_player_start_res() player = None if local: if is_ai: # a human controlled AI player player = AIPlayer(self.session, id, name, color, difficulty_level, inventory=inv) else: player = HumanPlayer(self.session, id, name, color, difficulty_level, inventory=inv) self.player = player self.player.inventory.add_change_listener(self.session.ingame_gui.update_gold, \ call_listener_now=True) elif is_ai: player = AIPlayer(self.session, id, name, color, difficulty_level, inventory=inv) else: player = Player(self.session, id, name, color, difficulty_level, inventory=inv) self.players.append(player) def get_tile(self, point): """Returns the ground at x, y. @param point: coords as Point @return: instance of Ground at x, y """ return self.full_map[(point.x, point.y)] def get_settlement(self, point): """Returns settlement on point. Very fast (O(1)). Returns None if point isn't on world. @param point: instance of Point @return: instance of Settlement or None""" try: return self.get_tile(point).settlement except KeyError: return None @property def settlements(self): """Returns all settlements on world""" settlements = [] for i in self.islands: settlements.extend(i.settlements) return settlements def get_building(self, point): """Returns the building at the position x, y. @param point: Point instance @return: Building class instance if a building is found, else None.""" i = self.get_island(point) return None if i is None else i.get_building(point) def get_island(self, point): """Returns the island for that coordinate, if none is found, returns None. @param point: instance of Point""" tup = point.to_tuple() if tup not in self.island_map: return None return self.island_map[tup] def get_islands_in_radius(self, point, radius): """Returns all islands in a certain radius around a point. @return set of islands in radius""" islands = set() for island in self.islands: for tile in island.get_surrounding_tiles(point, radius): islands.add(island) break return islands @decorators.make_constants() def get_branch_offices(self, position=None, radius=None, owner=None, include_friendly=False): """Returns all branch offices on the map. Optionally only those in range around the specified position. @param position: Point or Rect instance. @param radius: int radius to use. @param owner: Player instance, list only branch offices belonging to this player. @param include_friendly also list the branch offices belonging to friends @return: List of branch offices. """ branchoffices = [] islands = [] if radius is not None and position is not None: islands = self.get_islands_in_radius(position, radius) else: islands = self.islands for island in self.islands: for settlement in island.settlements: bo = settlement.branch_office if (radius is None or position is None or \ bo.position.distance(position) <= radius) and \ (owner is None or bo.owner == owner or include_friendly): branchoffices.append(bo) return branchoffices @decorators.make_constants() 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) ships = [] for ship in self.ships: if circle.contains(ship.position): ships.append(ship) return ships else: return self.ships @decorators.make_constants() 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) units = [] for unit in self.ground_units: if circle.contains(unit.position): units.append(unit) return units else: return self.ground_units @decorators.make_constants() 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) else: for island in self.islands: for building in island.buildings: buildings.append(building) return buildings @decorators.make_constants() def get_health_instances(self, position=None, radius=None): """Returns all instances that have health""" instances = [] for instance in self.get_ships(position, radius)+\ self.get_ground_units(position, radius): if instance.has_component('health'): instances.append(instance) return instances def save(self, db): """Saves the current game to the specified db. @param db: DbReader object of the db the game is saved to.""" super(World, self).save(db) for name, value in self.properties.iteritems(): db("INSERT INTO map_properties (name, value) VALUES (?, ?)", name, value) for island in self.islands: island.save(db) for player in self.players: player.save(db) if self.trader is not None: self.trader.save(db) if self.pirate is not None: self.pirate.save(db) for ship in self.ships: ship.save(db) for ground_unit in self.ground_units: ground_unit.save(db) for bullet in self.bullets: bullet.save(db) self.diplomacy.save(db) Weapon.save_attacks(db) def get_checkup_hash(self): dict = { 'rngvalue': self.session.random.random(), 'settlements': [], 'ships': [], } for island in self.islands: for settlement in island.settlements: entry = { 'owner': str(settlement.owner.worldid), 'tax_settings': str(settlement.tax_settings), 'inhabitants': str(settlement.inhabitants), 'cumulative_running_costs': str(settlement.cumulative_running_costs), 'cumulative_taxes': str(settlement.cumulative_taxes), 'inventory': str(settlement.inventory._storage), } dict['settlements'].append(entry) for ship in self.ships: entry = { 'owner': str(ship.owner.worldid), 'position': ship.position.to_tuple(), } dict['ships'].append(entry) return dict def notify_new_settlement(self): """Called when a new settlement is created""" # make sure there's a trader ship for 2 settlements if self.trader and len(self.settlements) > self.trader.get_ship_count() * 2: self.trader.create_ship() @decorators.make_constants() def toggle_translucency(self): """Make certain building types translucent""" if not hasattr(self, "_translucent_buildings"): self._translucent_buildings = set() if not self._translucent_buildings: # no translucent buildings saved => enable building_types = self.session.db.get_translucent_buildings() add = self._translucent_buildings.add from weakref import ref as create_weakref def get_all_buildings(world): for island in world.islands: for b in island.buildings: yield b for s in island.settlements: for b in s.buildings: yield b for b in get_all_buildings(self): if b.id in building_types: fife_instance = b._instance add( create_weakref(fife_instance) ) fife_instance.keep_translucency = True fife_instance.get2dGfxVisual().setTransparency( BUILDINGS.TRANSPARENCY_VALUE ) else: # undo translucency for inst in self._translucent_buildings: try: inst().get2dGfxVisual().setTransparency( 0 ) inst().keep_translucency = False except AttributeError: pass # obj has been deleted, inst() returned None self._translucent_buildings.clear()
def init_fish_indexer(self): self.fish_indexer = BuildingIndexer(16, self.full_map) for tile in self.ground_map.itervalues(): if tile.object is not None and tile.object.id == BUILDINGS.FISH_DEPOSIT_CLASS: self.fish_indexer.add(tile.object) self.fish_indexer._update()