class IngameGui(LivingObject): """Class handling all the ingame gui events. Assumes that only 1 instance is used (class variables)""" message_widget = livingProperty() minimap = livingProperty() keylistener = livingProperty() def __init__(self, session): super(IngameGui, self).__init__() self.session = session assert isinstance(self.session, horizons.session.Session) self.settlement = None self._old_menu = None self.cursor = None self.coordinates_tooltip = None self.keylistener = IngameKeyListener(self.session) self.cityinfo = CityInfo(self) LastActivePlayerSettlementManager.create_instance(self.session) self.message_widget = MessageWidget(self.session) # Windows self.windows = WindowManager() self.show_popup = self.windows.show_popup self.show_error_popup = self.windows.show_error_popup self.logbook = LogBook(self.session, self.windows) self.players_overview = PlayersOverview(self.session) self.players_settlements = PlayersSettlements(self.session) self.players_ships = PlayersShips(self.session) self.chat_dialog = ChatDialog(self.windows, self.session) self.change_name_dialog = ChangeNameDialog(self.windows, self.session) self.pausemenu = PauseMenu(self.session, self, self.windows, in_editor_mode=False) self.help_dialog = HelpDialog(self.windows, session=self.session) # Icon manager self.status_icon_manager = StatusIconManager( renderer=self.session.view.renderer['GenericRenderer'], layer=self.session.view.layers[LAYERS.OBJECTS]) self.production_finished_icon_manager = ProductionFinishedIconManager( renderer=self.session.view.renderer['GenericRenderer'], layer=self.session.view.layers[LAYERS.OBJECTS]) # 'minimap' is the guichan gui around the actual minimap, which is saved # in self.minimap self.mainhud = load_uh_widget('minimap.xml') self.mainhud.position_technique = "right:top" icon = self.mainhud.findChild(name="minimap") self.minimap = Minimap( icon, targetrenderer=horizons.globals.fife.targetrenderer, imagemanager=horizons.globals.fife.imagemanager, session=self.session, view=self.session.view) def speed_up(): SpeedUpCommand().execute(self.session) def speed_down(): SpeedDownCommand().execute(self.session) self.mainhud.mapEvents({ 'zoomIn': self.session.view.zoom_in, 'zoomOut': self.session.view.zoom_out, 'rotateRight': Callback.ChainedCallbacks(self.session.view.rotate_right, self.minimap.rotate_right), 'rotateLeft': Callback.ChainedCallbacks(self.session.view.rotate_left, self.minimap.rotate_left), 'speedUp': speed_up, 'speedDown': speed_down, 'destroy_tool': self.toggle_destroy_tool, 'build': self.show_build_menu, 'diplomacyButton': self.show_diplomacy_menu, 'gameMenuButton': self.toggle_pause, 'logbook': lambda: self.windows.toggle(self.logbook) }) self.mainhud.show() self.resource_overview = ResourceOverviewBar(self.session) # Register for messages SpeedChanged.subscribe(self._on_speed_changed) NewDisaster.subscribe(self._on_new_disaster) NewSettlement.subscribe(self._on_new_settlement) PlayerLevelUpgrade.subscribe(self._on_player_level_upgrade) MineEmpty.subscribe(self._on_mine_empty) self.session.view.add_change_listener(self._update_zoom) self._display_speed(self.session.timer.ticks_per_second) def end(self): # unsubscribe early, to avoid messages coming in while we're shutting down SpeedChanged.unsubscribe(self._on_speed_changed) NewDisaster.unsubscribe(self._on_new_disaster) NewSettlement.unsubscribe(self._on_new_settlement) PlayerLevelUpgrade.unsubscribe(self._on_player_level_upgrade) MineEmpty.unsubscribe(self._on_mine_empty) self.session.view.remove_change_listener(self._update_zoom) self.mainhud.mapEvents({ 'zoomIn': None, 'zoomOut': None, 'rotateRight': None, 'rotateLeft': None, 'destroy_tool': None, 'build': None, 'diplomacyButton': None, 'gameMenuButton': None }) self.mainhud.hide() self.mainhud = None self.windows.close_all() self.message_widget = None self.minimap = None self.resource_overview.end() self.resource_overview = None self.keylistener = None self.cityinfo.end() self.cityinfo = None self.hide_menu() if self.cursor: self.cursor.remove() self.cursor.end() self.cursor = None LastActivePlayerSettlementManager().remove() LastActivePlayerSettlementManager.destroy_instance() self.production_finished_icon_manager.end() self.production_finished_icon_manager = None self.status_icon_manager.end() self.status_icon_manager = None super(IngameGui, self).end() def show_select_savegame(self, mode): window = SelectSavegameDialog(mode, self.windows) return self.windows.show(window) def toggle_pause(self): self.windows.toggle(self.pausemenu) def toggle_help(self): self.windows.toggle(self.help_dialog) def minimap_to_front(self): """Make sure the full right top gui is visible and not covered by some dialog""" self.mainhud.hide() self.mainhud.show() def show_diplomacy_menu(self): # check if the menu is already shown if getattr(self.get_cur_menu(), 'name', None) == "diplomacy_widget": self.hide_menu() return if not DiplomacyTab.is_useable(self.session.world): self.windows.show_popup( _("No diplomacy possible"), _("Cannot do diplomacy as there are no other players.")) return tab = DiplomacyTab(self, self.session.world) self.show_menu(tab) def show_multi_select_tab(self, instances): tab = TabWidget(self, tabs=[SelectMultiTab(instances)], name='select_multi') self.show_menu(tab) def show_build_menu(self, update=False): """ @param update: set when build possibilities change (e.g. after inhabitant tier upgrade) """ # check if build menu is already shown if hasattr(self.get_cur_menu(), 'name') and self.get_cur_menu( ).name == "build_menu_tab_widget": self.hide_menu() if not update: # this was only a toggle call, don't reshow return self.set_cursor() # set default cursor for build menu self.deselect_all() if not any(settlement.owner.is_local_player for settlement in self.session.world.settlements): # player has not built any settlements yet. Accessing the build menu at such a point # indicates a mistake in the mental model of the user. Display a hint. tab = TabWidget( self, tabs=[TabInterface(widget="buildtab_no_settlement.xml")]) else: btabs = BuildTab.create_tabs(self.session, self._build) tab = TabWidget(self, tabs=btabs, name="build_menu_tab_widget", active_tab=BuildTab.last_active_build_tab) self.show_menu(tab) def deselect_all(self): for instance in self.session.selected_instances: instance.get_component(SelectableComponent).deselect() self.session.selected_instances.clear() def _build(self, building_id, unit=None): """Calls the games buildingtool class for the building_id. @param building_id: int with the building id that is to be built. @param unit: weakref to the unit, that builds (e.g. ship for warehouse)""" self.hide_menu() self.deselect_all() cls = Entities.buildings[building_id] if hasattr(cls, 'show_build_menu'): cls.show_build_menu() self.set_cursor('building', cls, None if unit is None else unit()) def toggle_road_tool(self): if not isinstance(self.cursor, mousetools.BuildingTool ) or self.cursor._class.id != BUILDINGS.TRAIL: self._build(BUILDINGS.TRAIL) else: self.set_cursor() def get_cur_menu(self): """Returns menu that is currently displayed""" return self._old_menu def show_menu(self, menu): """Shows a menu @param menu: str with the guiname or pychan object. """ if self._old_menu is not None: if hasattr(self._old_menu, "remove_remove_listener"): self._old_menu.remove_remove_listener( Callback(self.show_menu, None)) self._old_menu.hide() self._old_menu = menu if self._old_menu is not None: if hasattr(self._old_menu, "add_remove_listener"): self._old_menu.add_remove_listener( Callback(self.show_menu, None)) self._old_menu.show() self.minimap_to_front() TabWidgetChanged.broadcast(self) def hide_menu(self): self.show_menu(None) def save(self, db): self.message_widget.save(db) self.logbook.save(db) self.resource_overview.save(db) LastActivePlayerSettlementManager().save(db) def load(self, db): self.message_widget.load(db) self.logbook.load(db) self.resource_overview.load(db) if self.session.is_game_loaded(): LastActivePlayerSettlementManager().load(db) cur_settlement = LastActivePlayerSettlementManager( ).get_current_settlement() self.cityinfo.set_settlement(cur_settlement) self.minimap.draw() # update minimap to new world self.current_cursor = 'default' self.cursor = mousetools.SelectionTool(self.session) # Set cursor correctly, menus might need to be opened. # Open menus later; they may need unit data not yet inited self.cursor.apply_select() if not self.session.is_game_loaded(): # Fire a message for new world creation self.session.ingame_gui.message_widget.add('NEW_WORLD') # Show message when the relationship between players changed 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} string_id = 'DIPLOMACY_STATUS_{old}_{new}'.format( old=old_state.upper(), new=new_state.upper()) self.message_widget.add(string_id=string_id, message_dict=data) self.session.world.diplomacy.add_diplomacy_status_changed_listener( notify_change) def show_change_name_dialog(self, instance): """Shows a dialog where the user can change the name of an object.""" self.windows.show(self.change_name_dialog, instance=instance) def on_escape(self): if self.windows.visible: self.windows.on_escape() elif hasattr(self.cursor, 'on_escape'): self.cursor.on_escape() else: self.toggle_pause() return True def on_return(self): if self.windows.visible: self.windows.on_return() return True def _on_speed_changed(self, message): self._display_speed(message.new) def _display_speed(self, tps): text = u'' up_icon = self.mainhud.findChild(name='speedUp') down_icon = self.mainhud.findChild(name='speedDown') if tps == 0: # pause text = u'0x' up_icon.set_inactive() down_icon.set_inactive() else: if tps != GAME_SPEED.TICKS_PER_SECOND: text = unicode("%1gx" % (tps * 1.0 / GAME_SPEED.TICKS_PER_SECOND)) #%1g: displays 0.5x, but 2x instead of 2.0x index = GAME_SPEED.TICK_RATES.index(tps) if index + 1 >= len(GAME_SPEED.TICK_RATES): up_icon.set_inactive() else: up_icon.set_active() if index > 0: down_icon.set_active() else: down_icon.set_inactive() wdg = self.mainhud.findChild(name="speed_text") wdg.text = text wdg.resizeToContent() self.mainhud.show() def on_key_press(self, action, evt): """Handle a key press in-game. Returns True if the key was acted upon. """ _Actions = KeyConfig._Actions keyval = evt.getKey().getValue() if action == _Actions.ESCAPE: return self.on_escape() elif keyval == fife.Key.ENTER: return self.on_return() if action == _Actions.GRID: gridrenderer = self.session.view.renderer['GridRenderer'] gridrenderer.setEnabled(not gridrenderer.isEnabled()) elif action == _Actions.COORD_TOOLTIP: self.coordinates_tooltip.toggle() elif action == _Actions.DESTROY_TOOL: self.toggle_destroy_tool() elif action == _Actions.REMOVE_SELECTED: self.session.remove_selected() elif action == _Actions.ROAD_TOOL: self.toggle_road_tool() elif action == _Actions.SPEED_UP: SpeedUpCommand().execute(self.session) elif action == _Actions.SPEED_DOWN: SpeedDownCommand().execute(self.session) elif action == _Actions.PAUSE: TogglePauseCommand().execute(self.session) elif action == _Actions.PLAYERS_OVERVIEW: self.logbook.toggle_stats_visibility(widget='players') elif action == _Actions.SETTLEMENTS_OVERVIEW: self.logbook.toggle_stats_visibility(widget='settlements') elif action == _Actions.SHIPS_OVERVIEW: self.logbook.toggle_stats_visibility(widget='ships') elif action == _Actions.LOGBOOK: self.windows.toggle(self.logbook) elif action == _Actions.DEBUG and VERSION.IS_DEV_VERSION: import pdb pdb.set_trace() elif action == _Actions.BUILD_TOOL: self.show_build_menu() elif action == _Actions.ROTATE_RIGHT: if hasattr(self.cursor, "rotate_right"): # used in e.g. build preview to rotate building instead of map self.cursor.rotate_right() else: self.session.view.rotate_right() self.minimap.rotate_right() elif action == _Actions.ROTATE_LEFT: if hasattr(self.cursor, "rotate_left"): self.cursor.rotate_left() else: self.session.view.rotate_left() self.minimap.rotate_left() elif action == _Actions.CHAT: self.windows.show(self.chat_dialog) elif action == _Actions.TRANSLUCENCY: self.session.world.toggle_translucency() elif action == _Actions.TILE_OWNER_HIGHLIGHT: self.session.world.toggle_owner_highlight() elif keyval in (fife.Key.NUM_0, fife.Key.NUM_1, fife.Key.NUM_2, fife.Key.NUM_3, fife.Key.NUM_4, fife.Key.NUM_5, fife.Key.NUM_6, fife.Key.NUM_7, fife.Key.NUM_8, fife.Key.NUM_9): num = int(keyval - fife.Key.NUM_0) if evt.isControlPressed(): # create new group (only consider units owned by the player) self.session.selection_groups[num] = \ set(filter(lambda unit : unit.owner.is_local_player, self.session.selected_instances)) # drop units of the new group from all other groups for group in self.session.selection_groups: if group is not self.session.selection_groups[num]: group -= self.session.selection_groups[num] else: # deselect # we need to make sure to have a cursor capable of selection (for apply_select()) # this handles deselection implicitly in the destructor self.set_cursor('selection') # apply new selection for instance in self.session.selection_groups[num]: instance.get_component(SelectableComponent).select( reset_cam=True) # assign copy since it will be randomly changed, the unit should only be changed on ctrl-events self.session.selected_instances = self.session.selection_groups[ num].copy() # show menu depending on the entities selected if self.session.selected_instances: self.cursor.apply_select() else: # nothing is selected here, we need to hide the menu since apply_select doesn't handle that case self.show_menu(None) elif action == _Actions.QUICKSAVE: self.session.quicksave( ) # load is only handled by the MainListener elif action == _Actions.PIPETTE: # copy mode: pipette tool self.toggle_cursor('pipette') elif action == _Actions.HEALTH_BAR: # shows health bar of every instance with an health component self.session.world.toggle_health_for_all_health_instances() elif action == _Actions.SHOW_SELECTED: if self.session.selected_instances: # scroll to first one, we can never guarantee to display all selected units instance = iter(self.session.selected_instances).next() self.session.view.center(*instance.position.center.to_tuple()) for instance in self.session.selected_instances: if hasattr(instance, "path") and instance.owner.is_local_player: self.minimap.show_unit_path(instance) elif action == _Actions.HELP: self.toggle_help() else: return False return True def toggle_cursor(self, which, *args, **kwargs): """Alternate between the cursor which and default. args and kwargs are used to construct which.""" if self.current_cursor == which: self.set_cursor() else: self.set_cursor(which, *args, **kwargs) def set_cursor(self, which='default', *args, **kwargs): """Sets the mousetool (i.e. cursor). This is done here for encapsulation and control over destructors. Further arguments are passed to the mouse tool constructor.""" self.cursor.remove() self.current_cursor = which klass = { 'default': mousetools.SelectionTool, 'selection': mousetools.SelectionTool, 'tearing': mousetools.TearingTool, 'pipette': mousetools.PipetteTool, 'attacking': mousetools.AttackingTool, 'building': mousetools.BuildingTool, }[which] self.cursor = klass(self.session, *args, **kwargs) def toggle_destroy_tool(self): """Initiate the destroy tool""" self.toggle_cursor('tearing') def _update_zoom(self): """Enable/disable zoom buttons""" zoom = self.session.view.get_zoom() in_icon = self.mainhud.findChild(name='zoomIn') out_icon = self.mainhud.findChild(name='zoomOut') if zoom == VIEW.ZOOM_MIN: out_icon.set_inactive() else: out_icon.set_active() if zoom == VIEW.ZOOM_MAX: in_icon.set_inactive() else: in_icon.set_active() def _on_new_disaster(self, message): """Called when a building is 'infected' with a disaster.""" if message.building.owner.is_local_player and len( message.disaster._affected_buildings) == 1: pos = message.building.position.center self.message_widget.add( point=pos, string_id=message.disaster_class.NOTIFICATION_TYPE) def _on_new_settlement(self, message): player = message.settlement.owner self.message_widget.add(string_id='NEW_SETTLEMENT', point=message.warehouse_position, message_dict={'player': player.name}, play_sound=player.is_local_player) def _on_player_level_upgrade(self, message): """Called when a player's population reaches a new level.""" if not message.sender.is_local_player: return # show notification self.message_widget.add(point=message.building.position.center, string_id='SETTLER_LEVEL_UP', message_dict={'level': message.level + 1}) # update build menu to show new buildings menu = self.get_cur_menu() if hasattr(menu, "name") and menu.name == "build_menu_tab_widget": self.show_build_menu(update=True) # TODO: Use a better measure then first tab # Quite fragile, makes sure the tablist in the mainsquare menu is updated if hasattr(menu, '_tabs') and isinstance(menu._tabs[0], MainSquareOverviewTab): instance = list(self.session.selected_instances)[0] instance.get_component(SelectableComponent).show_menu( jump_to_tabclass=type(menu.current_tab)) def _on_mine_empty(self, message): self.message_widget.add(point=message.mine.position.center, string_id='MINE_EMPTY')
class IngameGui(LivingObject): minimap = livingProperty() keylistener = livingProperty() message_widget = livingProperty() def __init__(self, session): self.session = session self.cursor = None self.coordinates_tooltip = None self.keylistener = IngameKeyListener(self.session) # used by NavigationTool LastActivePlayerSettlementManager.create_instance(self.session) # Mocks needed to act like the real IngameGui self.show_menu = lambda x: 0 self.hide_menu = lambda: 0 # a logbook Dummy is necessary for message_widget to work self.logbook = DummyLogbook() self.mainhud = load_uh_widget('minimap.xml') self.mainhud.position_technique = "right+0:top+0" icon = self.mainhud.findChild(name="minimap") self.minimap = Minimap(icon, targetrenderer=horizons.globals.fife.targetrenderer, imagemanager=horizons.globals.fife.imagemanager, session=self.session, view=self.session.view) self.mainhud.mapEvents({ 'zoomIn': self.session.view.zoom_in, 'zoomOut': self.session.view.zoom_out, 'rotateRight': Callback.ChainedCallbacks(self.session.view.rotate_right, self.minimap.rotate_right), 'rotateLeft': Callback.ChainedCallbacks(self.session.view.rotate_left, self.minimap.rotate_left), 'gameMenuButton': self.toggle_pause, }) self.mainhud.show() ZoomChanged.subscribe(self._update_zoom) # Hide unnecessary buttons in hud for widget in ("build", "speedUp", "speedDown", "destroy_tool", "diplomacyButton", "logbook"): self.mainhud.findChild(name=widget).hide() self.windows = WindowManager() self.message_widget = MessageWidget(self.session) self.pausemenu = PauseMenu(self.session, self, self.windows, in_editor_mode=True) self.help_dialog = HelpDialog(self.windows) def end(self): self.mainhud.mapEvents({ 'zoomIn': None, 'zoomOut': None, 'rotateRight': None, 'rotateLeft': None, 'gameMenuButton': None }) self.mainhud.hide() self.mainhud = None self._settings_tab.hide() self._settings_tab = None self.windows.close_all() self.minimap = None self.keylistener = None LastActivePlayerSettlementManager().remove() LastActivePlayerSettlementManager.destroy_instance() ZoomChanged.unsubscribe(self._update_zoom) if self.cursor: self.cursor.remove() self.cursor.end() self.cursor = None super(IngameGui, self).end() def handle_selection_group(self, num, ctrl_pressed): # Someday, maybe cool stuff will be possible here. # That day is not today, I'm afraid. pass def toggle_pause(self): self.windows.toggle(self.pausemenu) def toggle_help(self): self.windows.toggle(self.help_dialog) def load(self, savegame): self.minimap.draw() self.cursor = SelectionTool(self.session) def setup(self): """Called after the world editor was initialized.""" self._settings_tab = TabWidget(self, tabs=[SettingsTab(self.session.world_editor, self)]) self._settings_tab.show() def minimap_to_front(self): """Make sure the full right top gui is visible and not covered by some dialog""" self.mainhud.hide() self.mainhud.show() def show_save_map_dialog(self): """Shows a dialog where the user can set the name of the saved map.""" window = SelectSavegameDialog('editor-save', self.windows) savegamename = self.windows.open(window) if savegamename is None: return False # user aborted dialog success = self.session.save(savegamename) if success: self.message_widget.add('SAVED_GAME') def on_escape(self): pass def on_key_press(self, action, evt): _Actions = KeyConfig._Actions if action == _Actions.QUICKSAVE: self.session.quicksave() if action == _Actions.ESCAPE: if self.windows.visible: self.windows.on_escape() elif hasattr(self.cursor, 'on_escape'): self.cursor.on_escape() else: self.toggle_pause() elif action == _Actions.HELP: self.toggle_help() else: return False return True def set_cursor(self, which='default', *args, **kwargs): """Sets the mousetool (i.e. cursor). This is done here for encapsulation and control over destructors. Further arguments are passed to the mouse tool constructor. """ self.cursor.remove() klass = { 'default': SelectionTool, 'tile_layer': TileLayingTool }[which] self.cursor = klass(self.session, *args, **kwargs) def _update_zoom(self, message): """Enable/disable zoom buttons""" in_icon = self.mainhud.findChild(name='zoomIn') out_icon = self.mainhud.findChild(name='zoomOut') if message.zoom == VIEW.ZOOM_MIN: out_icon.set_inactive() else: out_icon.set_active() if message.zoom == VIEW.ZOOM_MAX: in_icon.set_inactive() else: in_icon.set_active()
class IngameGui(LivingObject): minimap = livingProperty() keylistener = livingProperty() message_widget = livingProperty() def __init__(self, session): self.session = session self.cursor = None self.coordinates_tooltip = None self.keylistener = IngameKeyListener(self.session) # used by NavigationTool LastActivePlayerSettlementManager.create_instance(self.session) # Mocks needed to act like the real IngameGui self.show_menu = Dummy self.hide_menu = Dummy # a logbook Dummy is necessary for message_widget to work self.logbook = Dummy() self.mainhud = load_uh_widget('minimap.xml') self.mainhud.position_technique = "right+0:top+0" icon = self.mainhud.findChild(name="minimap") self.minimap = Minimap( icon, targetrenderer=horizons.globals.fife.targetrenderer, imagemanager=horizons.globals.fife.imagemanager, session=self.session, view=self.session.view) self.mainhud.mapEvents({ 'zoomIn': self.session.view.zoom_in, 'zoomOut': self.session.view.zoom_out, 'rotateRight': Callback.ChainedCallbacks(self.session.view.rotate_right, self.minimap.rotate_right), 'rotateLeft': Callback.ChainedCallbacks(self.session.view.rotate_left, self.minimap.rotate_left), 'gameMenuButton': self.toggle_pause, }) self.mainhud.show() ZoomChanged.subscribe(self._update_zoom) # Hide unnecessary buttons in hud for widget in ("build", "speedUp", "speedDown", "destroy_tool", "diplomacyButton", "logbook"): self.mainhud.findChild(name=widget).hide() self.windows = WindowManager() self.message_widget = MessageWidget(self.session) self.pausemenu = PauseMenu(self.session, self, self.windows, in_editor_mode=True) self.help_dialog = HelpDialog(self.windows) def end(self): self.mainhud.mapEvents({ 'zoomIn': None, 'zoomOut': None, 'rotateRight': None, 'rotateLeft': None, 'gameMenuButton': None }) self.mainhud.hide() self.mainhud = None self._settings_tab.hide() self._settings_tab = None self.windows.close_all() self.minimap = None self.keylistener = None LastActivePlayerSettlementManager().remove() LastActivePlayerSettlementManager.destroy_instance() ZoomChanged.unsubscribe(self._update_zoom) if self.cursor: self.cursor.remove() self.cursor.end() self.cursor = None super(IngameGui, self).end() def handle_selection_group(self, num, ctrl_pressed): # Someday, maybe cool stuff will be possible here. # That day is not today, I'm afraid. pass def toggle_pause(self): self.windows.toggle(self.pausemenu) def toggle_help(self): self.windows.toggle(self.help_dialog) def load(self, savegame): self.minimap.draw() self.cursor = SelectionTool(self.session) def setup(self): """Called after the world editor was initialized.""" self._settings_tab = TabWidget( self, tabs=[SettingsTab(self.session.world_editor, self)]) self._settings_tab.show() def minimap_to_front(self): """Make sure the full right top gui is visible and not covered by some dialog""" self.mainhud.hide() self.mainhud.show() def show_save_map_dialog(self): """Shows a dialog where the user can set the name of the saved map.""" window = SelectSavegameDialog('editor-save', self.windows) savegamename = self.windows.open(window) if savegamename is None: return False # user aborted dialog success = self.session.save(savegamename) if success: self.message_widget.add('SAVED_GAME') def on_escape(self): pass def on_key_press(self, action, evt): _Actions = KeyConfig._Actions if action == _Actions.QUICKSAVE: self.session.quicksave() if action == _Actions.ESCAPE: if self.windows.visible: self.windows.on_escape() elif hasattr(self.cursor, 'on_escape'): self.cursor.on_escape() else: self.toggle_pause() elif action == _Actions.HELP: self.toggle_help() else: return False return True def set_cursor(self, which='default', *args, **kwargs): """Sets the mousetool (i.e. cursor). This is done here for encapsulation and control over destructors. Further arguments are passed to the mouse tool constructor. """ self.cursor.remove() klass = {'default': SelectionTool, 'tile_layer': TileLayingTool}[which] self.cursor = klass(self.session, *args, **kwargs) def _update_zoom(self, message): """Enable/disable zoom buttons""" in_icon = self.mainhud.findChild(name='zoomIn') out_icon = self.mainhud.findChild(name='zoomOut') if message.zoom == VIEW.ZOOM_MIN: out_icon.set_inactive() else: out_icon.set_active() if message.zoom == VIEW.ZOOM_MAX: in_icon.set_inactive() else: in_icon.set_active()
class IngameGui(LivingObject): """Class handling all the ingame gui events. Assumes that only 1 instance is used (class variables)""" message_widget = livingProperty() minimap = livingProperty() keylistener = livingProperty() def __init__(self, session): super(IngameGui, self).__init__() self.session = session assert isinstance(self.session, horizons.session.Session) self.settlement = None self._old_menu = None self.cursor = None self.coordinates_tooltip = None self.keylistener = IngameKeyListener(self.session) self.cityinfo = CityInfo(self) LastActivePlayerSettlementManager.create_instance(self.session) self.message_widget = MessageWidget(self.session) # Windows self.windows = WindowManager() self.show_popup = self.windows.show_popup self.show_error_popup = self.windows.show_error_popup self.logbook = LogBook(self.session, self.windows) self.players_overview = PlayersOverview(self.session) self.players_settlements = PlayersSettlements(self.session) self.players_ships = PlayersShips(self.session) self.chat_dialog = ChatDialog(self.windows, self.session) self.change_name_dialog = ChangeNameDialog(self.windows, self.session) self.pausemenu = PauseMenu(self.session, self, self.windows, in_editor_mode=False) self.help_dialog = HelpDialog(self.windows, session=self.session) # Icon manager self.status_icon_manager = StatusIconManager( renderer=self.session.view.renderer['GenericRenderer'], layer=self.session.view.layers[LAYERS.OBJECTS] ) self.production_finished_icon_manager = ProductionFinishedIconManager( renderer=self.session.view.renderer['GenericRenderer'], layer=self.session.view.layers[LAYERS.OBJECTS] ) # 'minimap' is the guichan gui around the actual minimap, which is saved # in self.minimap self.mainhud = load_uh_widget('minimap.xml') self.mainhud.position_technique = "right:top" icon = self.mainhud.findChild(name="minimap") self.minimap = Minimap(icon, targetrenderer=horizons.globals.fife.targetrenderer, imagemanager=horizons.globals.fife.imagemanager, session=self.session, view=self.session.view) def speed_up(): SpeedUpCommand().execute(self.session) def speed_down(): SpeedDownCommand().execute(self.session) self.mainhud.mapEvents({ 'zoomIn' : self.session.view.zoom_in, 'zoomOut' : self.session.view.zoom_out, 'rotateRight' : Callback.ChainedCallbacks(self.session.view.rotate_right, self.minimap.rotate_right), 'rotateLeft' : Callback.ChainedCallbacks(self.session.view.rotate_left, self.minimap.rotate_left), 'speedUp' : speed_up, 'speedDown' : speed_down, 'destroy_tool' : self.toggle_destroy_tool, 'build' : self.show_build_menu, 'diplomacyButton' : self.show_diplomacy_menu, 'gameMenuButton' : self.toggle_pause, 'logbook' : lambda: self.windows.toggle(self.logbook) }) self.mainhud.show() hotkey_replacements = { 'rotateRight': 'ROTATE_RIGHT', 'rotateLeft': 'ROTATE_LEFT', 'speedUp': 'SPEED_UP', 'speedDown': 'SPEED_DOWN', 'destroy_tool': 'DESTROY_TOOL', 'build': 'BUILD_TOOL', 'gameMenuButton': 'ESCAPE', 'logbook': 'LOGBOOK', } for (widgetname, action) in hotkey_replacements.iteritems(): widget = self.mainhud.findChild(name=widgetname) keys = horizons.globals.fife.get_keys_for_action(action) # No `.upper()` here: "Pause" looks better than "PAUSE". keyname = HOTKEYS.DISPLAY_KEY.get(keys[0], keys[0].capitalize()) widget.helptext = widget.helptext.format(key=keyname) self.resource_overview = ResourceOverviewBar(self.session) # Register for messages SpeedChanged.subscribe(self._on_speed_changed) NewDisaster.subscribe(self._on_new_disaster) NewSettlement.subscribe(self._on_new_settlement) PlayerLevelUpgrade.subscribe(self._on_player_level_upgrade) MineEmpty.subscribe(self._on_mine_empty) ZoomChanged.subscribe(self._update_zoom) self._display_speed(self.session.timer.ticks_per_second) def end(self): # unsubscribe early, to avoid messages coming in while we're shutting down SpeedChanged.unsubscribe(self._on_speed_changed) NewDisaster.unsubscribe(self._on_new_disaster) NewSettlement.unsubscribe(self._on_new_settlement) PlayerLevelUpgrade.unsubscribe(self._on_player_level_upgrade) MineEmpty.unsubscribe(self._on_mine_empty) ZoomChanged.unsubscribe(self._update_zoom) self.mainhud.mapEvents({ 'zoomIn' : None, 'zoomOut' : None, 'rotateRight' : None, 'rotateLeft': None, 'destroy_tool' : None, 'build' : None, 'diplomacyButton' : None, 'gameMenuButton' : None }) self.mainhud.hide() self.mainhud = None self.windows.close_all() self.message_widget = None self.minimap = None self.resource_overview.end() self.resource_overview = None self.keylistener = None self.cityinfo.end() self.cityinfo = None self.hide_menu() if self.cursor: self.cursor.remove() self.cursor.end() self.cursor = None LastActivePlayerSettlementManager().remove() LastActivePlayerSettlementManager.destroy_instance() self.production_finished_icon_manager.end() self.production_finished_icon_manager = None self.status_icon_manager.end() self.status_icon_manager = None super(IngameGui, self).end() def show_select_savegame(self, mode): window = SelectSavegameDialog(mode, self.windows) return self.windows.show(window) def toggle_pause(self): self.windows.toggle(self.pausemenu) def toggle_help(self): self.windows.toggle(self.help_dialog) def minimap_to_front(self): """Make sure the full right top gui is visible and not covered by some dialog""" self.mainhud.hide() self.mainhud.show() def show_diplomacy_menu(self): # check if the menu is already shown if getattr(self.get_cur_menu(), 'name', None) == "diplomacy_widget": self.hide_menu() return if not DiplomacyTab.is_useable(self.session.world): self.windows.show_popup(_("No diplomacy possible"), _("Cannot do diplomacy as there are no other players.")) return tab = DiplomacyTab(self, self.session.world) self.show_menu(tab) def show_multi_select_tab(self, instances): tab = TabWidget(self, tabs=[SelectMultiTab(instances)], name='select_multi') self.show_menu(tab) def show_build_menu(self, update=False): """ @param update: set when build possibilities change (e.g. after inhabitant tier upgrade) """ # check if build menu is already shown if hasattr(self.get_cur_menu(), 'name') and self.get_cur_menu().name == "build_menu_tab_widget": self.hide_menu() if not update: # this was only a toggle call, don't reshow return self.set_cursor() # set default cursor for build menu self.deselect_all() if not any( settlement.owner.is_local_player for settlement in self.session.world.settlements): # player has not built any settlements yet. Accessing the build menu at such a point # indicates a mistake in the mental model of the user. Display a hint. tab = TabWidget(self, tabs=[ TabInterface(widget="buildtab_no_settlement.xml") ]) else: btabs = BuildTab.create_tabs(self.session, self._build) tab = TabWidget(self, tabs=btabs, name="build_menu_tab_widget", active_tab=BuildTab.last_active_build_tab) self.show_menu(tab) def deselect_all(self): for instance in self.session.selected_instances: instance.get_component(SelectableComponent).deselect() self.session.selected_instances.clear() def _build(self, building_id, unit=None): """Calls the games buildingtool class for the building_id. @param building_id: int with the building id that is to be built. @param unit: weakref to the unit, that builds (e.g. ship for warehouse)""" self.hide_menu() self.deselect_all() cls = Entities.buildings[building_id] if hasattr(cls, 'show_build_menu'): cls.show_build_menu() self.set_cursor('building', cls, None if unit is None else unit()) def toggle_road_tool(self): if not isinstance(self.cursor, mousetools.BuildingTool) or self.cursor._class.id != BUILDINGS.TRAIL: self._build(BUILDINGS.TRAIL) else: self.set_cursor() def get_cur_menu(self): """Returns menu that is currently displayed""" return self._old_menu def show_menu(self, menu): """Shows a menu @param menu: str with the guiname or pychan object. """ if self._old_menu is not None: if hasattr(self._old_menu, "remove_remove_listener"): self._old_menu.remove_remove_listener( Callback(self.show_menu, None) ) self._old_menu.hide() self._old_menu = menu if self._old_menu is not None: if hasattr(self._old_menu, "add_remove_listener"): self._old_menu.add_remove_listener( Callback(self.show_menu, None) ) self._old_menu.show() self.minimap_to_front() TabWidgetChanged.broadcast(self) def hide_menu(self): self.show_menu(None) def save(self, db): self.message_widget.save(db) self.logbook.save(db) self.resource_overview.save(db) LastActivePlayerSettlementManager().save(db) self.save_selection(db) def save_selection(self, db): # Store instances that are selected right now. for instance in self.session.selected_instances: db("INSERT INTO selected (`group`, id) VALUES (NULL, ?)", instance.worldid) # If a single instance is selected, also store the currently displayed tab. # (Else, upon restoring, we display a multi-selection tab.) tabname = None if len(self.session.selected_instances) == 1: tabclass = self.get_cur_menu().current_tab tabname = tabclass.__class__.__name__ db("INSERT INTO metadata (name, value) VALUES (?, ?)", 'selected_tab', tabname) # Store user defined unit selection groups (Ctrl+number) for (number, group) in enumerate(self.session.selection_groups): for instance in group: db("INSERT INTO selected (`group`, id) VALUES (?, ?)", number, instance.worldid) def load(self, db): self.message_widget.load(db) self.logbook.load(db) self.resource_overview.load(db) if self.session.is_game_loaded(): LastActivePlayerSettlementManager().load(db) cur_settlement = LastActivePlayerSettlementManager().get_current_settlement() self.cityinfo.set_settlement(cur_settlement) self.minimap.draw() # update minimap to new world self.current_cursor = 'default' self.cursor = mousetools.SelectionTool(self.session) # Set cursor correctly, menus might need to be opened. # Open menus later; they may need unit data not yet inited self.cursor.apply_select() self.load_selection(db) if not self.session.is_game_loaded(): # Fire a message for new world creation self.message_widget.add('NEW_WORLD') # Show message when the relationship between players changed 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} string_id = 'DIPLOMACY_STATUS_{old}_{new}'.format(old=old_state.upper(), new=new_state.upper()) self.message_widget.add(string_id=string_id, message_dict=data) self.session.world.diplomacy.add_diplomacy_status_changed_listener(notify_change) def load_selection(self, db): # Re-select old selected instance for (instance_id, ) in db("SELECT id FROM selected WHERE `group` IS NULL"): obj = WorldObject.get_object_by_id(instance_id) self.session.selected_instances.add(obj) obj.get_component(SelectableComponent).select() # Re-show old tab (if there was one) or multiselection if len(self.session.selected_instances) == 1: tabname = db("SELECT value FROM metadata WHERE name = ?", 'selected_tab')[0][0] # This can still be None due to old savegames not storing the information tabclass = None if tabname is None else resolve_tab(tabname) obj.get_component(SelectableComponent).show_menu(jump_to_tabclass=tabclass) elif self.session.selected_instances: self.show_multi_select_tab(self.session.selected_instances) # Load user defined unit selection groups (Ctrl+number) for (num, group) in enumerate(self.session.selection_groups): for (instance_id, ) in db("SELECT id FROM selected WHERE `group` = ?", num): obj = WorldObject.get_object_by_id(instance_id) group.add(obj) def show_change_name_dialog(self, instance): """Shows a dialog where the user can change the name of an object.""" self.windows.show(self.change_name_dialog, instance=instance) def on_escape(self): if self.windows.visible: self.windows.on_escape() elif hasattr(self.cursor, 'on_escape'): self.cursor.on_escape() else: self.toggle_pause() return True def on_return(self): if self.windows.visible: self.windows.on_return() return True def _on_speed_changed(self, message): self._display_speed(message.new) def _display_speed(self, tps): text = u'' up_icon = self.mainhud.findChild(name='speedUp') down_icon = self.mainhud.findChild(name='speedDown') if tps == 0: # pause text = u'0x' up_icon.set_inactive() down_icon.set_inactive() else: if tps != GAME_SPEED.TICKS_PER_SECOND: text = u"%1gx" % (tps * 1.0/GAME_SPEED.TICKS_PER_SECOND) #%1g: displays 0.5x, but 2x instead of 2.0x index = GAME_SPEED.TICK_RATES.index(tps) if index + 1 >= len(GAME_SPEED.TICK_RATES): up_icon.set_inactive() else: up_icon.set_active() if index > 0: down_icon.set_active() else: down_icon.set_inactive() wdg = self.mainhud.findChild(name="speed_text") wdg.text = text wdg.resizeToContent() self.mainhud.show() def on_key_press(self, action, evt): """Handle a key press in-game. Returns True if the key was acted upon. """ _Actions = KeyConfig._Actions keyval = evt.getKey().getValue() if action == _Actions.ESCAPE: return self.on_escape() elif keyval == fife.Key.ENTER: return self.on_return() if action == _Actions.GRID: gridrenderer = self.session.view.renderer['GridRenderer'] gridrenderer.setEnabled( not gridrenderer.isEnabled() ) elif action == _Actions.COORD_TOOLTIP: self.coordinates_tooltip.toggle() elif action == _Actions.DESTROY_TOOL: self.toggle_destroy_tool() elif action == _Actions.REMOVE_SELECTED: message = _(u"Are you sure you want to delete these objects?") if self.windows.show_popup(_(u"Delete"), message, show_cancel_button=True): self.session.remove_selected() else: self.deselect_all() elif action == _Actions.ROAD_TOOL: self.toggle_road_tool() elif action == _Actions.SPEED_UP: SpeedUpCommand().execute(self.session) elif action == _Actions.SPEED_DOWN: SpeedDownCommand().execute(self.session) elif action == _Actions.PAUSE: TogglePauseCommand().execute(self.session) elif action == _Actions.PLAYERS_OVERVIEW: self.logbook.toggle_stats_visibility(widget='players') elif action == _Actions.SETTLEMENTS_OVERVIEW: self.logbook.toggle_stats_visibility(widget='settlements') elif action == _Actions.SHIPS_OVERVIEW: self.logbook.toggle_stats_visibility(widget='ships') elif action == _Actions.LOGBOOK: self.windows.toggle(self.logbook) elif action == _Actions.DEBUG and VERSION.IS_DEV_VERSION: import pdb; pdb.set_trace() elif action == _Actions.BUILD_TOOL: self.show_build_menu() elif action == _Actions.ROTATE_RIGHT: if hasattr(self.cursor, "rotate_right"): # used in e.g. build preview to rotate building instead of map self.cursor.rotate_right() else: self.session.view.rotate_right() self.minimap.rotate_right() elif action == _Actions.ROTATE_LEFT: if hasattr(self.cursor, "rotate_left"): self.cursor.rotate_left() else: self.session.view.rotate_left() self.minimap.rotate_left() elif action == _Actions.CHAT: self.windows.show(self.chat_dialog) elif action == _Actions.TRANSLUCENCY: self.session.world.toggle_translucency() elif action == _Actions.TILE_OWNER_HIGHLIGHT: self.session.world.toggle_owner_highlight() elif fife.Key.NUM_0 <= keyval <= fife.Key.NUM_9: num = int(keyval - fife.Key.NUM_0) self.handle_selection_group(num, evt.isControlPressed()) elif action == _Actions.QUICKSAVE: self.session.quicksave() # Quickload is only handled by the MainListener. elif action == _Actions.PIPETTE: # Mode that allows copying buildings. self.toggle_cursor('pipette') elif action == _Actions.HEALTH_BAR: # Show health bar of every instance with a health component. self.session.world.toggle_health_for_all_health_instances() elif action == _Actions.SHOW_SELECTED: if self.session.selected_instances: # Scroll to first one, we can never guarantee to display all selected units. instance = iter(self.session.selected_instances).next() self.session.view.center( * instance.position.center.to_tuple()) for instance in self.session.selected_instances: if hasattr(instance, "path") and instance.owner.is_local_player: self.minimap.show_unit_path(instance) elif action == _Actions.HELP: self.toggle_help() else: return False return True def handle_selection_group(self, num, ctrl_pressed): """Select existing or assign new unit selection group. Ctrl+number creates or overwrites the group of number `num` with the currently selected units. Pressing the associated key selects a group and centers the camera around these units. """ if ctrl_pressed: # Only consider units owned by the player. units = set(u for u in self.session.selected_instances if u.owner.is_local_player) self.session.selection_groups[num] = units # Drop units of the new group from all other groups. for group in self.session.selection_groups: current_group = self.session.selection_groups[num] if group != current_group: group -= current_group else: # We need to make sure to have a cursor capable of selection # for apply_select() to work. # This handles deselection implicitly in the destructor. self.set_cursor('selection') # Apply new selection. for instance in self.session.selection_groups[num]: instance.get_component(SelectableComponent).select(reset_cam=True) # Assign copy since it will be randomly changed in selection code. # The unit group itself should only be changed on Ctrl events. self.session.selected_instances = self.session.selection_groups[num].copy() # Show correct tabs depending on what's selected. if self.session.selected_instances: self.cursor.apply_select() else: # Nothing is selected here. Hide the menu since apply_select # doesn't handle that case. self.show_menu(None) def toggle_cursor(self, which): """Alternate between the cursor *which* and the default cursor.""" if which == self.current_cursor: self.set_cursor() else: self.set_cursor(which) def set_cursor(self, which='default', *args, **kwargs): """Sets the mousetool (i.e. cursor). This is done here for encapsulation and control over destructors. Further arguments are passed to the mouse tool constructor.""" self.cursor.remove() self.current_cursor = which klass = { 'default' : mousetools.SelectionTool, 'selection' : mousetools.SelectionTool, 'tearing' : mousetools.TearingTool, 'pipette' : mousetools.PipetteTool, 'attacking' : mousetools.AttackingTool, 'building' : mousetools.BuildingTool, }[which] self.cursor = klass(self.session, *args, **kwargs) def toggle_destroy_tool(self): """Initiate the destroy tool""" self.toggle_cursor('tearing') def _update_zoom(self, message): """Enable/disable zoom buttons""" in_icon = self.mainhud.findChild(name='zoomIn') out_icon = self.mainhud.findChild(name='zoomOut') if message.zoom == VIEW.ZOOM_MIN: out_icon.set_inactive() else: out_icon.set_active() if message.zoom == VIEW.ZOOM_MAX: in_icon.set_inactive() else: in_icon.set_active() def _on_new_disaster(self, message): """Called when a building is 'infected' with a disaster.""" if message.building.owner.is_local_player and len(message.disaster._affected_buildings) == 1: pos = message.building.position.center self.message_widget.add(point=pos, string_id=message.disaster_class.NOTIFICATION_TYPE) def _on_new_settlement(self, message): player = message.settlement.owner self.message_widget.add( string_id='NEW_SETTLEMENT', point=message.warehouse_position, message_dict={'player': player.name}, play_sound=player.is_local_player ) def _on_player_level_upgrade(self, message): """Called when a player's population reaches a new level.""" if not message.sender.is_local_player: return # show notification self.message_widget.add( point=message.building.position.center, string_id='SETTLER_LEVEL_UP', message_dict={'level': message.level + 1} ) # update build menu to show new buildings menu = self.get_cur_menu() if hasattr(menu, "name") and menu.name == "build_menu_tab_widget": self.show_build_menu(update=True) # TODO: Use a better measure then first tab # Quite fragile, makes sure the tablist in the mainsquare menu is updated if hasattr(menu, '_tabs') and isinstance(menu._tabs[0], MainSquareOverviewTab): instance = list(self.session.selected_instances)[0] instance.get_component(SelectableComponent).show_menu(jump_to_tabclass=type(menu.current_tab)) def _on_mine_empty(self, message): self.message_widget.add(point=message.mine.position.center, string_id='MINE_EMPTY')
class IngameGui(LivingObject): """Class handling all the ingame gui events. Assumes that only 1 instance is used (class variables) @type session: horizons.session.Session @param session: instance of session the world belongs to. """ message_widget = livingProperty() minimap = livingProperty() keylistener = livingProperty() def __init__(self, session): super().__init__() self.session = session assert isinstance(self.session, horizons.session.Session) self.settlement = None self._old_menu = None self.cursor = None self.coordinates_tooltip = None self.keylistener = IngameKeyListener(self.session) self.cityinfo = CityInfo(self) LastActivePlayerSettlementManager.create_instance(self.session) self.message_widget = MessageWidget(self.session) # Windows self.windows = WindowManager() self.open_popup = self.windows.open_popup self.open_error_popup = self.windows.open_error_popup self.logbook = LogBook(self.session, self.windows) self.players_overview = PlayersOverview(self.session) self.players_settlements = PlayersSettlements(self.session) self.players_ships = PlayersShips(self.session) self.chat_dialog = ChatDialog(self.windows, self.session) self.change_name_dialog = ChangeNameDialog(self.windows, self.session) self.pausemenu = PauseMenu(self.session, self, self.windows, in_editor_mode=False) self.help_dialog = HelpDialog(self.windows) # Icon manager self.status_icon_manager = StatusIconManager( renderer=self.session.view.renderer['GenericRenderer'], layer=self.session.view.layers[LAYERS.OBJECTS]) self.production_finished_icon_manager = ProductionFinishedIconManager( renderer=self.session.view.renderer['GenericRenderer'], layer=self.session.view.layers[LAYERS.OBJECTS]) # 'minimap' is the guichan gui around the actual minimap, which is saved # in self.minimap self.mainhud = load_uh_widget('minimap.xml') self.mainhud.position_technique = "right:top" icon = self.mainhud.findChild(name="minimap") self.minimap = Minimap( icon, targetrenderer=horizons.globals.fife.targetrenderer, imagemanager=horizons.globals.fife.imagemanager, session=self.session, view=self.session.view) def speed_up(): SpeedUpCommand().execute(self.session) def speed_down(): SpeedDownCommand().execute(self.session) self.mainhud.mapEvents({ 'zoomIn': self.session.view.zoom_in, 'zoomOut': self.session.view.zoom_out, 'rotateRight': Callback.ChainedCallbacks(self.session.view.rotate_right, self.minimap.update_rotation), 'rotateLeft': Callback.ChainedCallbacks(self.session.view.rotate_left, self.minimap.update_rotation), 'speedUp': speed_up, 'speedDown': speed_down, 'destroy_tool': self.toggle_destroy_tool, 'build': self.show_build_menu, 'diplomacyButton': self.show_diplomacy_menu, 'gameMenuButton': self.toggle_pause, 'logbook': lambda: self.windows.toggle(self.logbook) }) self.mainhud.show() self._replace_hotkeys_in_widgets() self.resource_overview = ResourceOverviewBar(self.session) # Register for messages SpeedChanged.subscribe(self._on_speed_changed) NewDisaster.subscribe(self._on_new_disaster) NewSettlement.subscribe(self._on_new_settlement) PlayerLevelUpgrade.subscribe(self._on_player_level_upgrade) MineEmpty.subscribe(self._on_mine_empty) ZoomChanged.subscribe(self._update_zoom) GuiAction.subscribe(self._on_gui_click_action) GuiHover.subscribe(self._on_gui_hover_action) GuiCancelAction.subscribe(self._on_gui_cancel_action) # NOTE: This has to be called after the text is replaced! LanguageChanged.subscribe(self._on_language_changed) self._display_speed(self.session.timer.ticks_per_second) def end(self): # unsubscribe early, to avoid messages coming in while we're shutting down SpeedChanged.unsubscribe(self._on_speed_changed) NewDisaster.unsubscribe(self._on_new_disaster) NewSettlement.unsubscribe(self._on_new_settlement) PlayerLevelUpgrade.unsubscribe(self._on_player_level_upgrade) MineEmpty.unsubscribe(self._on_mine_empty) ZoomChanged.unsubscribe(self._update_zoom) GuiAction.unsubscribe(self._on_gui_click_action) GuiHover.unsubscribe(self._on_gui_hover_action) GuiCancelAction.unsubscribe(self._on_gui_cancel_action) self.mainhud.mapEvents({ 'zoomIn': None, 'zoomOut': None, 'rotateRight': None, 'rotateLeft': None, 'destroy_tool': None, 'build': None, 'diplomacyButton': None, 'gameMenuButton': None }) self.mainhud.hide() self.mainhud = None self.windows.close_all() self.message_widget = None self.minimap = None self.resource_overview.end() self.resource_overview = None self.keylistener = None self.cityinfo.end() self.cityinfo = None self.hide_menu() if self.cursor: self.cursor.remove() self.cursor.end() self.cursor = None LastActivePlayerSettlementManager().remove() LastActivePlayerSettlementManager.destroy_instance() self.production_finished_icon_manager.end() self.production_finished_icon_manager = None self.status_icon_manager.end() self.status_icon_manager = None super().end() def show_select_savegame(self, mode): window = SelectSavegameDialog(mode, self.windows) return self.windows.open(window) def toggle_pause(self): self.set_cursor('default') self.windows.toggle(self.pausemenu) def toggle_help(self): self.windows.toggle(self.help_dialog) def minimap_to_front(self): """Make sure the full right top gui is visible and not covered by some dialog""" self.mainhud.hide() self.mainhud.show() def show_diplomacy_menu(self): # check if the menu is already shown if getattr(self.get_cur_menu(), 'name', None) == "diplomacy_widget": self.hide_menu() return if not DiplomacyTab.is_useable(self.session.world): self.windows.open_popup( T("No diplomacy possible"), T("Cannot do diplomacy as there are no other players.")) return tab = DiplomacyTab(self, self.session.world) self.show_menu(tab) def show_multi_select_tab(self, instances): tab = TabWidget(self, tabs=[SelectMultiTab(instances)], name='select_multi') self.show_menu(tab) def show_build_menu(self, update=False): """ @param update: set when build possibilities change (e.g. after inhabitant tier upgrade) """ # check if build menu is already shown if hasattr(self.get_cur_menu(), 'name') and self.get_cur_menu( ).name == "build_menu_tab_widget": self.hide_menu() if not update: # this was only a toggle call, don't reshow return self.set_cursor() # set default cursor for build menu self.deselect_all() if not any(settlement.owner.is_local_player for settlement in self.session.world.settlements): # player has not built any settlements yet. Accessing the build menu at such a point # indicates a mistake in the mental model of the user. Display a hint. tab = TabWidget( self, tabs=[TabInterface(widget="buildtab_no_settlement.xml")]) else: btabs = BuildTab.create_tabs(self.session, self._build) tab = TabWidget(self, tabs=btabs, name="build_menu_tab_widget", active_tab=BuildTab.last_active_build_tab) self.show_menu(tab) def deselect_all(self): for instance in self.session.selected_instances: instance.get_component(SelectableComponent).deselect() self.session.selected_instances.clear() def _build(self, building_id, unit=None): """Calls the games buildingtool class for the building_id. @param building_id: int with the building id that is to be built. @param unit: weakref to the unit, that builds (e.g. ship for warehouse)""" self.hide_menu() self.deselect_all() cls = Entities.buildings[building_id] if hasattr(cls, 'show_build_menu'): cls.show_build_menu() self.set_cursor('building', cls, None if unit is None else unit()) def toggle_road_tool(self): if not isinstance(self.cursor, mousetools.BuildingTool ) or self.cursor._class.id != BUILDINGS.TRAIL: self._build(BUILDINGS.TRAIL) else: self.set_cursor() def get_cur_menu(self): """Returns menu that is currently displayed""" return self._old_menu def show_menu(self, menu): """Shows a menu @param menu: str with the guiname or pychan object. """ if self._old_menu is not None: if hasattr(self._old_menu, "remove_remove_listener"): self._old_menu.remove_remove_listener( Callback(self.show_menu, None)) self._old_menu.hide() self._old_menu = menu if self._old_menu is not None: if hasattr(self._old_menu, "add_remove_listener"): self._old_menu.add_remove_listener( Callback(self.show_menu, None)) self._old_menu.show() self.minimap_to_front() TabWidgetChanged.broadcast(self) def hide_menu(self): self.show_menu(None) def save(self, db): self.message_widget.save(db) self.logbook.save(db) self.resource_overview.save(db) LastActivePlayerSettlementManager().save(db) self.save_selection(db) def save_selection(self, db): # Store instances that are selected right now. for instance in self.session.selected_instances: db("INSERT INTO selected (`group`, id) VALUES (NULL, ?)", instance.worldid) # If a single instance is selected, also store the currently displayed tab. # (Else, upon restoring, we display a multi-selection tab.) tabname = None if len(self.session.selected_instances) == 1: tabclass = self.get_cur_menu().current_tab tabname = tabclass.__class__.__name__ db("INSERT INTO metadata (name, value) VALUES (?, ?)", 'selected_tab', tabname) # Store user defined unit selection groups (Ctrl+number) for (number, group) in enumerate(self.session.selection_groups): for instance in group: db("INSERT INTO selected (`group`, id) VALUES (?, ?)", number, instance.worldid) def load(self, db): self.message_widget.load(db) self.logbook.load(db) self.resource_overview.load(db) if self.session.is_game_loaded(): LastActivePlayerSettlementManager().load(db) cur_settlement = LastActivePlayerSettlementManager( ).get_current_settlement() self.cityinfo.set_settlement(cur_settlement) self.minimap.draw() # update minimap to new world self.current_cursor = 'default' self.cursor = mousetools.SelectionTool(self.session) # Set cursor correctly, menus might need to be opened. # Open menus later; they may need unit data not yet inited self.cursor.apply_select() self.load_selection(db) if not self.session.is_game_loaded(): # Fire a message for new world creation self.message_widget.add('NEW_WORLD') # Show message when the relationship between players changed def notify_change(caller, old_state, new_state, a, b): player1 = "{0!s}".format(a.name) player2 = "{0!s}".format(b.name) data = {'player1': player1, 'player2': player2} string_id = 'DIPLOMACY_STATUS_{old}_{new}'.format( old=old_state.upper(), new=new_state.upper()) self.message_widget.add(string_id=string_id, message_dict=data) self.session.world.diplomacy.add_diplomacy_status_changed_listener( notify_change) def load_selection(self, db): # Re-select old selected instance for (instance_id, ) in db("SELECT id FROM selected WHERE `group` IS NULL"): obj = WorldObject.get_object_by_id(instance_id) self.session.selected_instances.add(obj) obj.get_component(SelectableComponent).select() # Re-show old tab (if there was one) or multiselection if len(self.session.selected_instances) == 1: tabname = db("SELECT value FROM metadata WHERE name = ?", 'selected_tab')[0][0] # This can still be None due to old savegames not storing the information tabclass = None if tabname is None else resolve_tab(tabname) obj.get_component(SelectableComponent).show_menu( jump_to_tabclass=tabclass) elif self.session.selected_instances: self.show_multi_select_tab(self.session.selected_instances) # Load user defined unit selection groups (Ctrl+number) for (num, group) in enumerate(self.session.selection_groups): for (instance_id, ) in db( "SELECT id FROM selected WHERE `group` = ?", num): obj = WorldObject.get_object_by_id(instance_id) group.add(obj) def show_change_name_dialog(self, instance): """Shows a dialog where the user can change the name of an object.""" self.windows.open(self.change_name_dialog, instance=instance) def on_escape(self): if self.windows.visible: self.windows.on_escape() elif hasattr(self.cursor, 'on_escape'): self.cursor.on_escape() else: self.toggle_pause() return True def on_return(self): if self.windows.visible: self.windows.on_return() return True def _on_speed_changed(self, message): self._display_speed(message.new) def _display_speed(self, tps): text = '' up_icon = self.mainhud.findChild(name='speedUp') down_icon = self.mainhud.findChild(name='speedDown') if tps == 0: # pause text = '0x' up_icon.set_inactive() down_icon.set_inactive() else: if tps != GAME_SPEED.TICKS_PER_SECOND: text = "{0:1g}x".format(tps * 1.0 / GAME_SPEED.TICKS_PER_SECOND) #%1g: displays 0.5x, but 2x instead of 2.0x index = GAME_SPEED.TICK_RATES.index(tps) if index + 1 >= len(GAME_SPEED.TICK_RATES): up_icon.set_inactive() else: up_icon.set_active() if index > 0: down_icon.set_active() else: down_icon.set_inactive() wdg = self.mainhud.findChild(name="speed_text") wdg.text = text wdg.resizeToContent() self.mainhud.show() def on_key_press(self, action, evt): """Handle a key press in-game. Returns True if the key was acted upon. """ _Actions = KeyConfig._Actions keyval = evt.getKey().getValue() if action == _Actions.ESCAPE: return self.on_escape() elif keyval == fife.Key.ENTER: return self.on_return() if action == _Actions.GRID: gridrenderer = self.session.view.renderer['GridRenderer'] gridrenderer.setEnabled(not gridrenderer.isEnabled()) elif action == _Actions.COORD_TOOLTIP: self.coordinates_tooltip.toggle() elif action == _Actions.DESTROY_TOOL: self.toggle_destroy_tool() elif action == _Actions.REMOVE_SELECTED: message = T("Are you sure you want to delete these objects?") if self.windows.open_popup(T("Delete"), message, show_cancel_button=True): self.session.remove_selected() else: self.deselect_all() elif action == _Actions.ROAD_TOOL: self.toggle_road_tool() elif action == _Actions.SPEED_UP: SpeedUpCommand().execute(self.session) elif action == _Actions.SPEED_DOWN: SpeedDownCommand().execute(self.session) elif action == _Actions.ZOOM_IN: self.session.view.zoom_in() elif action == _Actions.ZOOM_OUT: self.session.view.zoom_out() elif action == _Actions.PAUSE: TogglePauseCommand().execute(self.session) elif action == _Actions.PLAYERS_OVERVIEW: self.logbook.toggle_stats_visibility(widget='players') elif action == _Actions.SETTLEMENTS_OVERVIEW: self.logbook.toggle_stats_visibility(widget='settlements') elif action == _Actions.SHIPS_OVERVIEW: self.logbook.toggle_stats_visibility(widget='ships') elif action == _Actions.LOGBOOK: self.windows.toggle(self.logbook) elif action == _Actions.DEBUG and VERSION.IS_DEV_VERSION: import pdb pdb.set_trace() elif action == _Actions.BUILD_TOOL: self.show_build_menu() elif action == _Actions.ROTATE_RIGHT: if hasattr(self.cursor, "rotate_right"): # used in e.g. build preview to rotate building instead of map self.cursor.rotate_right() else: self.session.view.rotate_right() self.minimap.update_rotation() elif action == _Actions.ROTATE_LEFT: if hasattr(self.cursor, "rotate_left"): self.cursor.rotate_left() else: self.session.view.rotate_left() self.minimap.update_rotation() elif action == _Actions.CHAT: self.windows.open(self.chat_dialog) elif action == _Actions.TRANSLUCENCY: self.session.world.toggle_translucency() elif action == _Actions.TILE_OWNER_HIGHLIGHT: self.session.world.toggle_owner_highlight() elif fife.Key.NUM_0 <= keyval <= fife.Key.NUM_9: num = int(keyval - fife.Key.NUM_0) self.handle_selection_group(num, evt.isControlPressed()) elif action == _Actions.QUICKSAVE: self.session.quicksave() # Quickload is only handled by the MainListener. elif action == _Actions.PIPETTE: # Mode that allows copying buildings. self.toggle_cursor('pipette') elif action == _Actions.HEALTH_BAR: # Show health bar of every instance with a health component. self.session.world.toggle_health_for_all_health_instances() elif action == _Actions.SHOW_SELECTED: if self.session.selected_instances: # Scroll to first one, we can never guarantee to display all selected units. instance = next(iter(self.session.selected_instances)) self.session.view.center(*instance.position.center.to_tuple()) for instance in self.session.selected_instances: if hasattr(instance, "path") and instance.owner.is_local_player: self.minimap.show_unit_path(instance) elif action == _Actions.HELP: self.toggle_help() else: return False return True def handle_selection_group(self, num, ctrl_pressed): """Select existing or assign new unit selection group. Ctrl+number creates or overwrites the group of number `num` with the currently selected units. Pressing the associated key selects a group and centers the camera around these units. """ if ctrl_pressed: # Only consider units owned by the player. units = { u for u in self.session.selected_instances if u.owner.is_local_player } self.session.selection_groups[num] = units # Drop units of the new group from all other groups. for group in self.session.selection_groups: current_group = self.session.selection_groups[num] if group != current_group: group -= current_group else: # We need to make sure to have a cursor capable of selection # for apply_select() to work. # This handles deselection implicitly in the destructor. self.set_cursor('selection') # Apply new selection. for instance in self.session.selection_groups[num]: instance.get_component(SelectableComponent).select( reset_cam=True) # Assign copy since it will be randomly changed in selection code. # The unit group itself should only be changed on Ctrl events. self.session.selected_instances = self.session.selection_groups[ num].copy() # Show correct tabs depending on what's selected. if self.session.selected_instances: self.cursor.apply_select() else: # Nothing is selected here. Hide the menu since apply_select # doesn't handle that case. self.show_menu(None) def toggle_cursor(self, which): """Alternate between the cursor *which* and the default cursor.""" if which == self.current_cursor: self.set_cursor() else: self.set_cursor(which) def set_cursor(self, which='default', *args, **kwargs): """Sets the mousetool (i.e. cursor). This is done here for encapsulation and control over destructors. Further arguments are passed to the mouse tool constructor.""" self.cursor.remove() self.current_cursor = which klass = { 'default': mousetools.SelectionTool, 'selection': mousetools.SelectionTool, 'tearing': mousetools.TearingTool, 'pipette': mousetools.PipetteTool, 'attacking': mousetools.AttackingTool, 'building': mousetools.BuildingTool, }[which] self.cursor = klass(self.session, *args, **kwargs) def toggle_destroy_tool(self): """Initiate the destroy tool""" self.toggle_cursor('tearing') def _update_zoom(self, message): """Enable/disable zoom buttons""" in_icon = self.mainhud.findChild(name='zoomIn') out_icon = self.mainhud.findChild(name='zoomOut') if message.zoom == VIEW.ZOOM_MIN: out_icon.set_inactive() else: out_icon.set_active() if message.zoom == VIEW.ZOOM_MAX: in_icon.set_inactive() else: in_icon.set_active() def _on_new_disaster(self, message): """Called when a building is 'infected' with a disaster.""" if message.building.owner.is_local_player and len( message.disaster._affected_buildings) == 1: pos = message.building.position.center self.message_widget.add( point=pos, string_id=message.disaster_class.NOTIFICATION_TYPE) def _on_new_settlement(self, message): player = message.settlement.owner self.message_widget.add(string_id='NEW_SETTLEMENT', point=message.warehouse_position, message_dict={'player': player.name}, play_sound=player.is_local_player) def _on_player_level_upgrade(self, message): """Called when a player's population reaches a new level.""" if not message.sender.is_local_player: return # show notification self.message_widget.add(point=message.building.position.center, string_id='SETTLER_LEVEL_UP', message_dict={'level': message.level + 1}) # update build menu to show new buildings menu = self.get_cur_menu() if hasattr(menu, "name") and menu.name == "build_menu_tab_widget": self.show_build_menu(update=True) def _on_mine_empty(self, message): self.message_widget.add(point=message.mine.position.center, string_id='MINE_EMPTY') def _on_gui_click_action(self, msg): """Make a sound when a button is clicked""" AmbientSoundComponent.play_special('click', gain=10) def _on_gui_cancel_action(self, msg): """Make a sound when a cancelButton is clicked""" AmbientSoundComponent.play_special('success', gain=10) def _on_gui_hover_action(self, msg): """Make a sound when the mouse hovers over a button""" AmbientSoundComponent.play_special('refresh', position=None, gain=1) def _replace_hotkeys_in_widgets(self): """Replaces the `{key}` in the (translated) widget helptext with the actual hotkey""" hotkey_replacements = { 'rotateRight': 'ROTATE_RIGHT', 'rotateLeft': 'ROTATE_LEFT', 'speedUp': 'SPEED_UP', 'speedDown': 'SPEED_DOWN', 'destroy_tool': 'DESTROY_TOOL', 'build': 'BUILD_TOOL', 'gameMenuButton': 'ESCAPE', 'logbook': 'LOGBOOK', } for (widgetname, action) in hotkey_replacements.items(): widget = self.mainhud.findChild(name=widgetname) keys = horizons.globals.fife.get_keys_for_action(action) # No `.upper()` here: "Pause" looks better than "PAUSE". keyname = HOTKEYS.DISPLAY_KEY.get(keys[0], keys[0].capitalize()) widget.helptext = widget.helptext.format(key=keyname) def _on_language_changed(self, msg): """Replace the hotkeys after translation. NOTE: This should be called _after_ the texts are replaced. This currently relies on import order with `horizons.gui`. """ self._replace_hotkeys_in_widgets()
class Gui: """This class handles all the out of game menu, like the main and pause menu, etc. """ log = logging.getLogger("gui") def __init__(self): self.mainlistener = MainListener(self) self.windows = WindowManager() # temporary aliases for compatibility with rest of the code self.open_popup = self.windows.open_popup self.open_error_popup = self.windows.open_error_popup # Main menu background image setup. available_images = glob.glob('content/gui/images/background/mainmenu/bg_*.png') self.bg_images = deque(available_images) latest_bg = horizons.globals.fife.get_uh_setting("LatestBackground") try: # If we know the current background from an earlier session, # show all other available ones before picking that one again. self.bg_images.remove(latest_bg) self.bg_images.append(latest_bg) except ValueError: pass self._background = Icon(position_technique='center:center') self.rotate_background() self._background.show() # Initialize menu dialogs and widgets that are accessed from `gui`. self.singleplayermenu = SingleplayerMenu(self.windows) self.multiplayermenu = MultiplayerMenu(self, self.windows) self.help_dialog = HelpDialog(self.windows) self.loadingscreen = LoadingScreen() self.settings_dialog = SettingsDialog(self.windows) self.mainmenu = MainMenu(self, self.windows) self.fps_display = FPSDisplay() def show_main(self): """Shows the main menu""" GuiAction.subscribe(self._on_gui_click_action) GuiHover.subscribe(self._on_gui_hover_action) GuiCancelAction.subscribe(self._on_gui_cancel_action) if not self._background.isVisible(): self._background.show() self.windows.open(self.mainmenu) def show_select_savegame(self, mode): window = SelectSavegameDialog(mode, self.windows) return self.windows.open(window) def load_game(self): saved_game = self.show_select_savegame(mode='load') if saved_game is None: return False # user aborted dialog options = StartGameOptions(saved_game) horizons.main.start_singleplayer(options) return True def on_help(self): self.windows.toggle(self.help_dialog) def show_credits(self): """Shows the credits dialog. """ window = CreditsPickbeltWidget(self.windows) self.windows.open(window) def on_escape(self): self.windows.on_escape() def on_return(self): self.windows.on_return() def close_all(self): GuiAction.discard(self._on_gui_click_action) GuiHover.discard(self._on_gui_hover_action) GuiCancelAction.discard(self._on_gui_cancel_action) self.windows.close_all() self._background.hide() def show_loading_screen(self): if not self._background.isVisible(): self._background.show() self.windows.open(self.loadingscreen) def rotate_background(self): """Select next background image to use in the game menu. Triggered by the "Change background" main menu button. """ # Note: bg_images is a deque. self.bg_images.rotate(1) self._background.image = self.bg_images[0] # Save current background choice to settings. # This keeps the background image consistent between sessions. horizons.globals.fife.set_uh_setting("LatestBackground", self.bg_images[0]) horizons.globals.fife.save_settings() def _on_gui_click_action(self, msg): """Make a sound when a button is clicked""" AmbientSoundComponent.play_special('click', gain=10) def _on_gui_cancel_action(self, msg): """Make a sound when a cancelButton is clicked""" AmbientSoundComponent.play_special('success', gain=10) def _on_gui_hover_action(self, msg): """Make a sound when the mouse hovers over a button""" AmbientSoundComponent.play_special('refresh', position=None, gain=1) def show_editor_start_menu(self): editor_start_menu = EditorStartMenu(self.windows) self.windows.open(editor_start_menu)
class Gui(object): """This class handles all the out of game menu, like the main and pause menu, etc. """ log = logging.getLogger("gui") def __init__(self): self.mainlistener = MainListener(self) self.windows = WindowManager() # temporary aliases for compatibility with rest of the code self.show_popup = self.windows.show_popup self.show_error_popup = self.windows.show_error_popup self._background = Icon(image=self._get_random_background(), position_technique='center:center') self._background.show() self.subscribe() self.singleplayermenu = SingleplayerMenu(self.windows) self.multiplayermenu = MultiplayerMenu(self, self.windows) self.help_dialog = HelpDialog(self.windows) self.loadingscreen = LoadingScreen() self.settings_dialog = SettingsDialog(self.windows) self.mainmenu = MainMenu(self, self.windows) self.fps_display = FPSDisplay() def subscribe(self): """Subscribe to the necessary messages.""" GuiAction.subscribe(self._on_gui_action) def unsubscribe(self): GuiAction.unsubscribe(self._on_gui_action) def show_main(self): """Shows the main menu """ if not self._background.isVisible(): self._background.show() self.windows.show(self.mainmenu) def show_select_savegame(self, mode): window = SelectSavegameDialog(mode, self.windows) return self.windows.show(window) def load_game(self): saved_game = self.show_select_savegame(mode='load') if saved_game is None: return False # user aborted dialog options = StartGameOptions(saved_game) horizons.main.start_singleplayer(options) return True def on_help(self): self.windows.toggle(self.help_dialog) def show_credits(self): """Shows the credits dialog. """ window = CreditsPickbeltWidget(self.windows) self.windows.show(window) def on_escape(self): self.windows.on_escape() def close_all(self): self.windows.close_all() self._background.hide() def show_loading_screen(self): if not self._background.isVisible(): self._background.show() self.windows.show(self.loadingscreen) def randomize_background(self): """Randomly select a background image to use. This function is triggered by change background button from main menu.""" self._background.image = self._get_random_background() def _get_random_background(self): """Randomly select a background image to use through out the game menu.""" available_images = glob.glob('content/gui/images/background/mainmenu/bg_*.png') #get latest background latest_background = horizons.globals.fife.get_uh_setting("LatestBackground") #if there is a latest background then remove it from available list if latest_background is not None: available_images.remove(latest_background) background_choice = random.choice(available_images) #save current background choice horizons.globals.fife.set_uh_setting("LatestBackground", background_choice) horizons.globals.fife.save_settings() return background_choice def _on_gui_action(self, msg): AmbientSoundComponent.play_special('click') def show_editor_start_menu(self): editor_start_menu = EditorStartMenu(self.windows) self.windows.show(editor_start_menu)
class Gui(object): """This class handles all the out of game menu, like the main and pause menu, etc. """ log = logging.getLogger("gui") def __init__(self): self.mainlistener = MainListener(self) self.windows = WindowManager() # temporary aliases for compatibility with rest of the code self.show_popup = self.windows.show_popup self.show_error_popup = self.windows.show_error_popup self._background = Icon(image=self._get_random_background(), position_technique='center:center') self._background.show() self.singleplayermenu = SingleplayerMenu(self.windows) self.multiplayermenu = MultiplayerMenu(self, self.windows) self.help_dialog = HelpDialog(self.windows) self.loadingscreen = LoadingScreen() self.settings_dialog = SettingsDialog(self.windows) self.mainmenu = MainMenu(self, self.windows) self.fps_display = FPSDisplay() def show_main(self): """Shows the main menu """ GuiAction.subscribe(self._on_gui_action) if not self._background.isVisible(): self._background.show() self.windows.show(self.mainmenu) def show_select_savegame(self, mode): window = SelectSavegameDialog(mode, self.windows) return self.windows.show(window) def load_game(self): saved_game = self.show_select_savegame(mode='load') if saved_game is None: return False # user aborted dialog options = StartGameOptions(saved_game) horizons.main.start_singleplayer(options) return True def on_help(self): self.windows.toggle(self.help_dialog) def show_credits(self): """Shows the credits dialog. """ window = CreditsPickbeltWidget(self.windows) self.windows.show(window) def on_escape(self): self.windows.on_escape() def on_return(self): self.windows.on_return() def close_all(self): GuiAction.discard(self._on_gui_action) self.windows.close_all() self._background.hide() def show_loading_screen(self): if not self._background.isVisible(): self._background.show() self.windows.show(self.loadingscreen) def randomize_background(self): """Randomly select a background image to use. This function is triggered by change background button from main menu.""" self._background.image = self._get_random_background() def _get_random_background(self): """Randomly select a background image to use through out the game menu.""" available_images = glob.glob('content/gui/images/background/mainmenu/bg_*.png') #get latest background latest_background = horizons.globals.fife.get_uh_setting("LatestBackground") #if there is a latest background then remove it from available list if latest_background is not None: available_images.remove(latest_background) background_choice = random.choice(available_images) #save current background choice horizons.globals.fife.set_uh_setting("LatestBackground", background_choice) horizons.globals.fife.save_settings() return background_choice def _on_gui_action(self, msg): AmbientSoundComponent.play_special('click') def show_editor_start_menu(self): editor_start_menu = EditorStartMenu(self.windows) self.windows.show(editor_start_menu)
class Gui(object): """This class handles all the out of game menu, like the main and pause menu, etc. """ log = logging.getLogger("gui") def __init__(self): self.mainlistener = MainListener(self) self.windows = WindowManager() # temporary aliases for compatibility with rest of the code self.show_popup = self.windows.show_popup self.show_error_popup = self.windows.show_error_popup # Main menu background image setup. available_images = glob.glob('content/gui/images/background/mainmenu/bg_*.png') self.bg_images = deque(available_images) latest_bg = horizons.globals.fife.get_uh_setting("LatestBackground") try: # If we know the current background from an earlier session, # show all other available ones before picking that one again. self.bg_images.remove(latest_bg) self.bg_images.append(latest_bg) except ValueError: pass self._background = Icon(position_technique='center:center') self.rotate_background() self._background.show() self.singleplayermenu = SingleplayerMenu(self.windows) self.multiplayermenu = MultiplayerMenu(self, self.windows) self.help_dialog = HelpDialog(self.windows) self.loadingscreen = LoadingScreen() self.settings_dialog = SettingsDialog(self.windows) self.mainmenu = MainMenu(self, self.windows) self.fps_display = FPSDisplay() def show_main(self): """Shows the main menu """ GuiAction.subscribe(self._on_gui_action) if not self._background.isVisible(): self._background.show() self.windows.show(self.mainmenu) def show_select_savegame(self, mode): window = SelectSavegameDialog(mode, self.windows) return self.windows.show(window) def load_game(self): saved_game = self.show_select_savegame(mode='load') if saved_game is None: return False # user aborted dialog options = StartGameOptions(saved_game) horizons.main.start_singleplayer(options) return True def on_help(self): self.windows.toggle(self.help_dialog) def show_credits(self): """Shows the credits dialog. """ window = CreditsPickbeltWidget(self.windows) self.windows.show(window) def on_escape(self): self.windows.on_escape() def on_return(self): self.windows.on_return() def close_all(self): GuiAction.discard(self._on_gui_action) self.windows.close_all() self._background.hide() def show_loading_screen(self): if not self._background.isVisible(): self._background.show() self.windows.show(self.loadingscreen) def rotate_background(self): """Select next background image to use in the game menu. Triggered by the "Change background" main menu button. """ # Note: bg_images is a deque. self.bg_images.rotate() self._background.image = self.bg_images[0] # Save current background choice to settings. # This keeps the background image consistent between sessions. horizons.globals.fife.set_uh_setting("LatestBackground", self.bg_images[0]) horizons.globals.fife.save_settings() def _on_gui_action(self, msg): AmbientSoundComponent.play_special('click') def show_editor_start_menu(self): editor_start_menu = EditorStartMenu(self.windows) self.windows.show(editor_start_menu)
class Gui: """This class handles all the out of game menu, like the main and pause menu, etc. """ log = logging.getLogger("gui") def __init__(self): self.mainlistener = MainListener(self) self.windows = WindowManager() # temporary aliases for compatibility with rest of the code self.open_popup = self.windows.open_popup self.open_error_popup = self.windows.open_error_popup self._background = Background() self._background.show() # Initialize menu dialogs and widgets that are accessed from `gui`. self.singleplayermenu = SingleplayerMenu(self.windows) self.multiplayermenu = MultiplayerMenu(self, self.windows) self.help_dialog = HelpDialog(self.windows) self.loadingscreen = LoadingScreen() self.settings_dialog = SettingsDialog(self.windows) self.mainmenu = MainMenu(self, self.windows) self.fps_display = FPSDisplay() def show_main(self): """Shows the main menu""" GuiAction.subscribe(self._on_gui_click_action) GuiHover.subscribe(self._on_gui_hover_action) GuiCancelAction.subscribe(self._on_gui_cancel_action) if not self._background.visible: self._background.show() self.windows.open(self.mainmenu) def show_select_savegame(self, mode): window = SelectSavegameDialog(mode, self.windows) return self.windows.open(window) def load_game(self): saved_game = self.show_select_savegame(mode='load') if saved_game is None: return False # user aborted dialog options = StartGameOptions(saved_game) horizons.main.start_singleplayer(options) return True def on_help(self): self.windows.toggle(self.help_dialog) def show_credits(self): """Shows the credits dialog. """ window = CreditsPickbeltWidget(self.windows) self.windows.open(window) def on_escape(self): self.windows.on_escape() def on_return(self): self.windows.on_return() def close_all(self): GuiAction.discard(self._on_gui_click_action) GuiHover.discard(self._on_gui_hover_action) GuiCancelAction.discard(self._on_gui_cancel_action) self.windows.close_all() self._background.hide() def show_loading_screen(self): if not self._background.visible: self._background.show() self.windows.open(self.loadingscreen) def _on_gui_click_action(self, msg): """Make a sound when a button is clicked""" AmbientSoundComponent.play_special('click', gain=10) def _on_gui_cancel_action(self, msg): """Make a sound when a cancelButton is clicked""" AmbientSoundComponent.play_special('success', gain=10) def _on_gui_hover_action(self, msg): """Make a sound when the mouse hovers over a button""" AmbientSoundComponent.play_special('refresh', position=None, gain=1) def show_editor_start_menu(self): editor_start_menu = EditorStartMenu(self.windows) self.windows.open(editor_start_menu)