def __init__(self, gui, db, rng_seed=None): super(Session, self).__init__() self.log.debug("Initing session") self.gui = gui # main gui, not ingame gui self.db = db # main db for game data (game.sqlite) # this saves how often the current game has been saved self.savecounter = 0 self.is_alive = True WorldObject.reset() NamedObject.reset() AIPlayer.clear_caches() #game self.random = self.create_rng(rng_seed) self.timer = self.create_timer() Scheduler.create_instance(self.timer) self.manager = self.create_manager() self.view = View(self, (15, 15)) Entities.load(self.db) self.scenario_eventhandler = ScenarioEventHandler(self) # dummy handler with no events self.campaign = {} #GUI self.gui.session = self self.ingame_gui = IngameGui(self, self.gui) self.keylistener = IngameKeyListener(self) self.coordinates_tooltip = None self.display_speed() self.selected_instances = set() self.selection_groups = [set()] * 10 # List of sets that holds the player assigned unit groups.
def new_session(mapgen=create_map, rng_seed=RANDOM_SEED, human_player = True, ai_players = 0): """ Create a new session with a map, add one human player and a trader (it will crash otherwise). It returns both session and player to avoid making the function-baed tests too verbose. """ session = SPTestSession(horizons.main.db, rng_seed=rng_seed) human_difficulty = DifficultySettings.DEFAULT_LEVEL ai_difficulty = DifficultySettings.EASY_LEVEL players = [] if human_player: players.append({'id': 1, 'name': 'foobar', 'color': Color[1], 'local': True, 'is_ai': False, 'difficulty': human_difficulty}) for i in xrange(ai_players): id = i + human_player + 1 players.append({'id': id, 'name': ('AI' + str(i)), 'color': Color[id], 'local': id == 1, 'is_ai': True, 'difficulty': ai_difficulty}) session.load(mapgen(), players) session.world.init_fish_indexer() # use different trader id here, so that init_new_world can be called # (else there would be a worldid conflict) session.world.trader = Trader(session, 99999 + 42, 'Free Trader', Color()) if ai_players > 0: # currently only ai tests use the ships for player in session.world.players: point = session.world.get_random_possible_ship_position() ship = CreateUnit(player.worldid, UNITS.PLAYER_SHIP_CLASS, point.x, point.y)(issuer=player) # give ship basic resources for res, amount in session.db("SELECT resource, amount FROM start_resources"): ship.inventory.alter(res, amount) AIPlayer.load_abstract_buildings(session.db) return session, session.world.player
def __init__(self, db, rng_seed=None): """ Unfortunately, right now there is no other way to setup Dummy versions of the GUI, View etc., unless we want to patch the references in the session module. """ super(LivingObject, self).__init__() self.gui = Dummy() self.db = db self.savecounter = 0 # this is a new game. self.is_alive = True WorldObject.reset() NamedObject.reset() AIPlayer.clear_caches() # Game self.current_tick = 0 self.random = self.create_rng(rng_seed) self.timer = self.create_timer() Scheduler.create_instance(self.timer) ExtScheduler.create_instance(Dummy) self.manager = self.create_manager() self.view = Dummy() self.view.renderer = Dummy() Entities.load(self.db) self.scenario_eventhandler = Dummy() self.campaign = {} self.selected_instances = [] # GUI self.gui.session = self self.ingame_gui = Dummy() GAME_SPEED.TICKS_PER_SECOND = 16
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 if natural_resource_multiplier != 0: 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, 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.get_component(StorageComponent).inventory.alter(res, amount) if player is self.player: ret_coords = point.to_tuple() # load the AI stuff only when we have AI players if any(isinstance(player, AIPlayer) for player in self.players): 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(None, None, 'NEW_WORLD') assert ret_coords is not None, "Return coords are None. No players loaded?" return ret_coords
def setup_player(self, id, name, color, clientid, local, is_ai, difficulty_level): """Sets up a new Player instance and adds her 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 is_ai: # a human controlled AI player player = AIPlayer(self.session, id, name, color, clientid, difficulty_level) else: player = HumanPlayer(self.session, id, name, color, clientid, difficulty_level) player.initialize(inv) # Componentholder init if local: self.player = player self.players.append(player)
def __init__(self, gui, db, rng_seed=None): super(Session, self).__init__() assert isinstance(gui, Gui) assert isinstance(db, horizons.util.uhdbaccessor.UhDbAccessor) self.log.debug("Initing session") self.gui = gui # main gui, not ingame gui self.db = db # main db for game data (game.sql) # this saves how often the current game has been saved self.savecounter = 0 self.is_alive = True self.message_bus = MessageBus() # misc WorldObject.reset() NamedComponent.reset() AIPlayer.clear_caches() #game self.random = self.create_rng(rng_seed) assert isinstance(self.random, Random) self.timer = self.create_timer() Scheduler.create_instance(self.timer) self.manager = self.create_manager() self.view = View(self) Entities.load(self.db) self.scenario_eventhandler = ScenarioEventHandler(self) # dummy handler with no events self.campaign = {} #GUI self.gui.session = self self.ingame_gui = IngameGui(self, self.gui) self.keylistener = IngameKeyListener(self) self.coordinates_tooltip = None self.display_speed() LastActivePlayerSettlementManager.create_instance(self) self.status_icon_manager = StatusIconManager(self) self.selected_instances = set() self.selection_groups = [set()] * 10 # List of sets that holds the player assigned unit groups.
def __init__(self, db, rng_seed=None): """ Unfortunately, right now there is no other way to setup Dummy versions of the GUI, View etc., unless we want to patch the references in the session module. """ super(LivingObject, self).__init__() self.gui = Dummy() self.db = db self.savecounter = 0 # this is a new game. self.is_alive = True WorldObject.reset() NamedComponent.reset() AIPlayer.clear_caches() # Game self.random = self.create_rng(rng_seed) self.timer = self.create_timer() Scheduler.create_instance(self.timer) ExtScheduler.create_instance(Dummy) self.manager = self.create_manager() self.view = Dummy() self.view.renderer = Dummy() Entities.load(self.db) self.scenario_eventhandler = Dummy() self.campaign = {} self.message_bus = MessageBus() self.status_icon_manager = StatusIconManager(self) # GUI self.gui.session = self self.ingame_gui = Dummy() LastActivePlayerSettlementManager.create_instance(self) self.selected_instances = set() self.selection_groups = [set()] * 10 # List of sets that holds the player assigned unit groups. GAME_SPEED.TICKS_PER_SECOND = 16
def _init(self, savegame_db, force_player_id=None, disasters_enabled=True): """ @param savegame_db: Dbreader with loaded savegame database @param force_player_id: the worldid of the selected human player or default if None (debug option) """ """ All essential and non-essential parts of the world are set up here, you don't need to know everything that happens. """ # load properties self.properties = {} for (name, value) in savegame_db("SELECT name, value FROM map_properties"): self.properties[name] = json.loads(value) if not 'disasters_enabled' in self.properties: # set on first init self.properties['disasters_enabled'] = disasters_enabled # create playerlist self.players = [] self.player = None # player sitting in front of this machine self.trader = None self.pirate = None self._load_players(savegame_db, force_player_id) # all static data LoadingProgress.broadcast(self, 'world_load_map') self.load_raw_map(savegame_db) # load world buildings (e.g. fish) LoadingProgress.broadcast(self, 'world_load_buildings') 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 LoadingProgress.broadcast(self, 'world_init_water') self.water = dict((tile, 1.0) for tile in self.ground_map) self._init_water_bodies() self.sea_number = self.water_body[(self.min_x, self.min_y)] for island in self.islands: island.terrain_cache.create_sea_cache() # 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 self._init_shallow_water_bodies() self.shallow_sea_number = self.shallow_water_body[(self.min_x, self.min_y)] # 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) LoadingProgress.broadcast(self, 'world_load_units') 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 and pirate command their ships. we have to do this here # because ships have to be initialized for this, and they have # to exist before ships are loaded. if self.trader: self.trader.load_ship_states(savegame_db) if self.pirate: self.pirate.finish_loading(savegame_db) # load the AI stuff only when we have AI players LoadingProgress.broadcast(self, 'world_setup_ai') if any(isinstance(player, AIPlayer) for player in self.players): AIPlayer.load_abstract_buildings(self.session.db) # TODO: find a better place for this # load the AI players # this has to be done here because otherwise the ships and other objects won't exist for player in self.players: if not isinstance(player, HumanPlayer): player.finish_loading(savegame_db) LoadingProgress.broadcast(self, 'world_load_stuff') self._load_combat(savegame_db) self._load_diplomacy(savegame_db) self._load_disasters(savegame_db) self.inited = True """TUTORIAL:
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 if natural_resource_multiplier != 0: self._add_nature_objects(natural_resource_multiplier) # reset loggers, see above for logger_name, level in loggers_to_silence.items(): logging.getLogger(logger_name).setLevel(level) # add free trader if trader_enabled: self.trader = Trader(self.session, 99999, "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, 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.get_component(StorageComponent).inventory.alter( res, amount) if player is self.player: ret_coords = point.to_tuple() # HACK: Store starting ship as first unit group, and select it def _preselect_player_ship(player_ship): sel_comp = player_ship.get_component(SelectableComponent) sel_comp.select(reset_cam=True) self.session.selected_instances = {player_ship} self.session.ingame_gui.handle_selection_group(1, True) sel_comp.show_menu() select_ship = partial(_preselect_player_ship, ship) Scheduler().add_new_object(select_ship, ship, run_in=0) # load the AI stuff only when we have AI players if any(isinstance(player, AIPlayer) for player in self.players): 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()) assert ret_coords is not None, "Return coords are None. No players loaded?" return ret_coords
def _init(self, savegame_db, force_player_id=None, disasters_enabled=True): """ @param savegame_db: Dbreader with loaded savegame database @param force_player_id: the worldid of the selected human player or default if None (debug option) """ """ All essential and non-essential parts of the world are set up here, you don't need to know everything that happens. """ # load properties self.properties = {} for (name, value) in savegame_db("SELECT name, value FROM map_properties"): self.properties[name] = json.loads(value) if 'disasters_enabled' not in self.properties: # set on first init self.properties['disasters_enabled'] = disasters_enabled self._load_players(savegame_db, force_player_id) # all static data LoadingProgress.broadcast(self, 'world_load_map') self.load_raw_map(savegame_db) # load world buildings (e.g. fish) LoadingProgress.broadcast(self, 'world_load_buildings') buildings = savegame_db( "SELECT rowid, type FROM building WHERE location = ?", self.worldid) for (building_worldid, building_typeid) in buildings: load_building(self.session, savegame_db, building_typeid, building_worldid) # use a dict because it's directly supported by the pathfinding algo LoadingProgress.broadcast(self, 'world_init_water') self.water = {tile: 1.0 for tile in self.ground_map} self._init_water_bodies() self.sea_number = self.water_body[(self.min_x, self.min_y)] for island in self.islands: island.terrain_cache.create_sea_cache() # 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.items(): if 'coastline' in tile.classes or 'constructible' not in tile.classes: self.water_and_coastline[coord] = 1.0 self._init_shallow_water_bodies() self.shallow_sea_number = self.shallow_water_body[(self.min_x, self.min_y)] # create ship position list. entries: ship_map[(x, y)] = ship self.ship_map = {} self.ground_unit_map = {} 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) LoadingProgress.broadcast(self, 'world_load_units') 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 and pirate command their ships. we have to do this here # because ships have to be initialized for this, and they have # to exist before ships are loaded. if self.trader: self.trader.load_ship_states(savegame_db) if self.pirate: self.pirate.finish_loading(savegame_db) # load the AI stuff only when we have AI players LoadingProgress.broadcast(self, 'world_setup_ai') if any(isinstance(player, AIPlayer) for player in self.players): AIPlayer.load_abstract_buildings( self.session.db) # TODO: find a better place for this # load the AI players # this has to be done here because otherwise the ships and other objects won't exist for player in self.players: if not isinstance(player, HumanPlayer): player.finish_loading(savegame_db) LoadingProgress.broadcast(self, 'world_load_stuff') self._load_combat(savegame_db) self._load_diplomacy(savegame_db) self._load_disasters(savegame_db) self.inited = True """TUTORIAL:
def _clear_caches(cls): """Clear all data caches in global namespace related to a session""" WorldObject.reset() NamedComponent.reset() AIPlayer.clear_caches() SelectableBuildingComponent.reset()
def _init(self, savegame_db, force_player_id=None, disasters_enabled=True): """ @param savegame_db: Dbreader with loaded savegame database @param force_player_id: the worldid of the selected human player or default if None (debug option) """ """ All essential and non-essential parts of the world are set up here, you don't need to know everything that happens. """ #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) self.owner_highlight_active = False self.health_visible_for_all_health_instances = False 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] # set the human player to the forced value (debug option) self.set_forced_player(force_player_id) 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.') # all static data self.load_raw_map(savegame_db) # 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 stuff only when we have AI players if any(isinstance(player, AIPlayer) for player in self.players): AIPlayer.load_abstract_buildings(self.session.db) # TODO: find a better place for this # load the AI players # this has to be done here because otherwise the ships and other objects won't exist 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, old_state, new_state, a, b): player1 = u"%s" % a.name player2 = u"%s" % b.name data = {'player1' : player1, 'player2' : player2} self.session.ingame_gui.message_widget.add( None, None, 'DIPLOMACY_STATUS_'+old_state.upper()+"_"+new_state.upper(), data) self.diplomacy.add_diplomacy_status_changed_listener(notify_change) disasters_disabled_by_properties = 'disasters_enabled' in self.properties and not self.properties['disasters_enabled'] # if savegame or parameter disables disasters, it's disabled (both have to be set to enable to actually enable) disasters_disabled = not disasters_enabled or disasters_disabled_by_properties self.disaster_manager = DisasterManager(self.session, disabled=disasters_disabled) if self.session.is_game_loaded(): self.disaster_manager.load(savegame_db) self.inited = True """TUTORIAL:
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:
def _clear_caches(self): WorldObject.reset() NamedComponent.reset() AIPlayer.clear_caches()
def init_new_world(self, minclay = 2, maxclay = 3, minmountains = 1, maxmountains = 3): """ 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: Returs 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 ) from horizons.command.building import Build from horizons.command.unit import CreateUnit # add a random number of environmental objects to the gameworld if int(self.properties.get('RandomTrees', 1)) == 1: Tree = Entities.buildings[BUILDINGS.TREE_CLASS] Clay = Entities.buildings[BUILDINGS.CLAY_DEPOSIT_CLASS] Fish = Entities.buildings[BUILDINGS.FISH_DEPOSIT_CLASS] Mountain = Entities.buildings[BUILDINGS.MOUNTAIN_CLASS] for island in self.islands: if maxclay <= minclay: minclay = maxclay-1 if maxmountains <= minmountains: minmountains = maxmountains-1 max_clay_deposits = self.session.random.randint(minclay, maxclay) max_mountains = self.session.random.randint(minmountains, maxmountains) num_clay_deposits = 0 num_mountains = 0 # TODO: fix this sorted()-call. its slow but orderness of dict-loop isn't guaranteed for coords, tile in sorted(island.ground_map.iteritems()): # add tree to every nth tile if self.session.random.randint(0, 2) == 0 and \ Tree.check_build(self.session, tile, check_settlement=False): building = Build(Tree, coords[0], coords[1], 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, *coords)(issuer=None) if self.session.random.random() > WILD_ANIMAL.FOOD_AVAILABLE_ON_START: building.inventory.alter(RES.WILDANIMALFOOD_ID, -1) elif num_clay_deposits < max_clay_deposits and \ self.session.random.randint(0, 40) == 0 and \ Clay.check_build(self.session, tile, check_settlement=False): num_clay_deposits += 1 Build(Clay, coords[0], coords[1], ownerless=True, island=island)(issuer=None) elif num_mountains < max_mountains and \ self.session.random.randint(0, 40) == 0 and \ Mountain.check_build(self.session, tile, check_settlement=False): num_mountains += 1 Build(Mountain, coords[0], coords[1], ownerless=True, island=island)(issuer=None) if 'coastline' in tile.classes and self.session.random.randint(0, 4) == 0: # try to place fish # from the current position, go to random directions 2 times directions = [ (i, j) for i in xrange(-1, 2) for j in xrange(-1, 2) ] for (x_dir, y_dir) in self.session.random.sample(directions, 2): # move a random amount in both directions coord_to_check = ( coords[0] + x_dir * self.session.random.randint(3, 9), coords[1] + y_dir * self.session.random.randint(3, 9), ) # now we have the location, check if we can build here if coord_to_check in self.ground_map: Build(Fish, coord_to_check[0], coord_to_check[1], ownerless=True, \ island=self)(issuer=None) # reset loggers, see above for logger_name, level in loggers_to_silence.iteritems(): logging.getLogger(logger_name).setLevel(level) # add free trader 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 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