Package horizons :: Package gui :: Module ingamegui
[hide private]
[frames] | no frames]

Source Code for Module horizons.gui.ingamegui

  1  # ################################################### 
  2  # Copyright (C) 2008-2017 The Unknown Horizons Team 
  3  # team@unknown-horizons.org 
  4  # This file is part of Unknown Horizons. 
  5  # 
  6  # Unknown Horizons is free software; you can redistribute it and/or modify 
  7  # it under the terms of the GNU General Public License as published by 
  8  # the Free Software Foundation; either version 2 of the License, or 
  9  # (at your option) any later version. 
 10  # 
 11  # This program is distributed in the hope that it will be useful, 
 12  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 13  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 14  # GNU General Public License for more details. 
 15  # 
 16  # You should have received a copy of the GNU General Public License 
 17  # along with this program; if not, write to the 
 18  # Free Software Foundation, Inc., 
 19  # 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA 
 20  # ################################################### 
 21   
 22  from fife import fife 
 23   
 24  import horizons.globals 
 25  from horizons.command.game import SpeedDownCommand, SpeedUpCommand, TogglePauseCommand 
 26  from horizons.component.ambientsoundcomponent import AmbientSoundComponent 
 27  from horizons.component.selectablecomponent import SelectableComponent 
 28  from horizons.constants import BUILDINGS, GAME_SPEED, HOTKEYS, LAYERS, VERSION, VIEW 
 29  from horizons.entities import Entities 
 30  from horizons.gui import mousetools 
 31  from horizons.gui.keylisteners import IngameKeyListener, KeyConfig 
 32  from horizons.gui.modules import HelpDialog, PauseMenu, SelectSavegameDialog 
 33  from horizons.gui.modules.ingame import ChangeNameDialog, ChatDialog, CityInfo 
 34  from horizons.gui.tabs import BuildTab, DiplomacyTab, SelectMultiTab, TabWidget, resolve_tab 
 35  from horizons.gui.tabs.tabinterface import TabInterface 
 36  from horizons.gui.util import load_uh_widget 
 37  from horizons.gui.widgets.logbook import LogBook 
 38  from horizons.gui.widgets.messagewidget import MessageWidget 
 39  from horizons.gui.widgets.minimap import Minimap 
 40  from horizons.gui.widgets.playersoverview import PlayersOverview 
 41  from horizons.gui.widgets.playerssettlements import PlayersSettlements 
 42  from horizons.gui.widgets.playersships import PlayersShips 
 43  from horizons.gui.widgets.resourceoverviewbar import ResourceOverviewBar 
 44  from horizons.gui.windows import WindowManager 
 45  from horizons.i18n import gettext as T 
 46  from horizons.messaging import ( 
 47          GuiAction, GuiCancelAction, GuiHover, LanguageChanged, MineEmpty, NewDisaster, NewSettlement, 
 48          PlayerLevelUpgrade, SpeedChanged, TabWidgetChanged, ZoomChanged) 
 49  from horizons.util.lastactiveplayersettlementmanager import LastActivePlayerSettlementManager 
 50  from horizons.util.living import LivingObject, livingProperty 
 51  from horizons.util.python.callback import Callback 
 52  from horizons.util.worldobject import WorldObject 
 53  from horizons.world.managers.productionfinishediconmanager import ProductionFinishedIconManager 
 54  from horizons.world.managers.statusiconmanager import StatusIconManager 
 55   
 56   
57 -class IngameGui(LivingObject):
58 """Class handling all the ingame gui events. 59 Assumes that only 1 instance is used (class variables) 60 61 @type session: horizons.session.Session 62 @param session: instance of session the world belongs to. 63 """ 64 65 message_widget = livingProperty() 66 minimap = livingProperty() 67 keylistener = livingProperty() 68
69 - def __init__(self, session):
70 super().__init__() 71 self.session = session 72 assert isinstance(self.session, horizons.session.Session) 73 self.settlement = None 74 self._old_menu = None 75 76 self.cursor = None 77 self.coordinates_tooltip = None 78 79 self.keylistener = IngameKeyListener(self.session) 80 81 self.cityinfo = CityInfo(self) 82 LastActivePlayerSettlementManager.create_instance(self.session) 83 84 self.message_widget = MessageWidget(self.session) 85 86 # Windows 87 self.windows = WindowManager() 88 self.open_popup = self.windows.open_popup 89 self.open_error_popup = self.windows.open_error_popup 90 91 self.logbook = LogBook(self.session, self.windows) 92 self.session.SetLogBook(self.logbook) 93 self.players_overview = PlayersOverview(self.session) 94 self.players_settlements = PlayersSettlements(self.session) 95 self.players_ships = PlayersShips(self.session) 96 97 self.chat_dialog = ChatDialog(self.windows, self.session) 98 self.change_name_dialog = ChangeNameDialog(self.windows, self.session) 99 self.pausemenu = PauseMenu(self.session, self, self.windows, in_editor_mode=False) 100 self.help_dialog = HelpDialog(self.windows) 101 102 # Icon manager 103 self.status_icon_manager = StatusIconManager( 104 renderer=self.session.view.renderer['GenericRenderer'], 105 layer=self.session.view.layers[LAYERS.OBJECTS] 106 ) 107 self.production_finished_icon_manager = ProductionFinishedIconManager( 108 renderer=self.session.view.renderer['GenericRenderer'], 109 layer=self.session.view.layers[LAYERS.OBJECTS] 110 ) 111 112 # 'minimap' is the guichan gui around the actual minimap, which is saved 113 # in self.minimap 114 self.mainhud = load_uh_widget('minimap.xml') 115 self.mainhud.position_technique = "right:top" 116 117 icon = self.mainhud.findChild(name="minimap") 118 self.minimap = Minimap(icon, 119 targetrenderer=horizons.globals.fife.targetrenderer, 120 imagemanager=horizons.globals.fife.imagemanager, 121 session=self.session, 122 view=self.session.view, 123 mousearea=self.mainhud.findChild(name="minimapMouse")) 124 125 def speed_up(): 126 SpeedUpCommand().execute(self.session)
127 128 def speed_down(): 129 SpeedDownCommand().execute(self.session)
130 131 self.mainhud.mapEvents({ 132 'zoomIn': self.session.view.zoom_in, 133 'zoomOut': self.session.view.zoom_out, 134 'rotateRight': Callback.ChainedCallbacks(self.session.view.rotate_right, self.minimap.update_rotation), 135 'rotateLeft': Callback.ChainedCallbacks(self.session.view.rotate_left, self.minimap.update_rotation), 136 'speedUp': speed_up, 137 'speedDown': speed_down, 138 'destroy_tool': self.toggle_destroy_tool, 139 'build': self.show_build_menu, 140 'diplomacyButton': self.show_diplomacy_menu, 141 'gameMenuButton': self.toggle_pause, 142 'logbook': lambda: self.windows.toggle(self.logbook) 143 }) 144 self.mainhud.show() 145 146 self._replace_hotkeys_in_widgets() 147 148 self.resource_overview = ResourceOverviewBar(self.session) 149 150 # Register for messages 151 SpeedChanged.subscribe(self._on_speed_changed) 152 NewDisaster.subscribe(self._on_new_disaster) 153 NewSettlement.subscribe(self._on_new_settlement) 154 PlayerLevelUpgrade.subscribe(self._on_player_level_upgrade) 155 MineEmpty.subscribe(self._on_mine_empty) 156 ZoomChanged.subscribe(self._update_zoom) 157 GuiAction.subscribe(self._on_gui_click_action) 158 GuiHover.subscribe(self._on_gui_hover_action) 159 GuiCancelAction.subscribe(self._on_gui_cancel_action) 160 # NOTE: This has to be called after the text is replaced! 161 LanguageChanged.subscribe(self._on_language_changed) 162 163 self._display_speed(self.session.timer.ticks_per_second) 164
165 - def end(self):
166 # unsubscribe early, to avoid messages coming in while we're shutting down 167 SpeedChanged.unsubscribe(self._on_speed_changed) 168 NewDisaster.unsubscribe(self._on_new_disaster) 169 NewSettlement.unsubscribe(self._on_new_settlement) 170 PlayerLevelUpgrade.unsubscribe(self._on_player_level_upgrade) 171 MineEmpty.unsubscribe(self._on_mine_empty) 172 ZoomChanged.unsubscribe(self._update_zoom) 173 GuiAction.unsubscribe(self._on_gui_click_action) 174 GuiHover.unsubscribe(self._on_gui_hover_action) 175 GuiCancelAction.unsubscribe(self._on_gui_cancel_action) 176 177 self.mainhud.mapEvents({ 178 'zoomIn': None, 179 'zoomOut': None, 180 'rotateRight': None, 181 'rotateLeft': None, 182 183 'destroy_tool': None, 184 'build': None, 185 'diplomacyButton': None, 186 'gameMenuButton': None 187 }) 188 self.mainhud.hide() 189 self.mainhud = None 190 191 self.windows.close_all() 192 self.message_widget = None 193 self.minimap = None 194 self.resource_overview.end() 195 self.resource_overview = None 196 self.keylistener = None 197 self.cityinfo.end() 198 self.cityinfo = None 199 self.hide_menu() 200 201 if self.cursor: 202 self.cursor.remove() 203 self.cursor.end() 204 self.cursor = None 205 206 LastActivePlayerSettlementManager().remove() 207 LastActivePlayerSettlementManager.destroy_instance() 208 209 self.production_finished_icon_manager.end() 210 self.production_finished_icon_manager = None 211 self.status_icon_manager.end() 212 self.status_icon_manager = None 213 214 super().end()
215
216 - def show_select_savegame(self, mode):
217 window = SelectSavegameDialog(mode, self.windows) 218 return self.windows.open(window)
219
220 - def toggle_pause(self):
221 self.set_cursor('default') 222 self.windows.toggle(self.pausemenu)
223
224 - def toggle_help(self):
225 self.windows.toggle(self.help_dialog)
226
227 - def minimap_to_front(self):
228 """Make sure the full right top gui is visible and not covered by some dialog""" 229 self.mainhud.hide() 230 self.mainhud.show()
231
232 - def show_diplomacy_menu(self):
233 # check if the menu is already shown 234 if getattr(self.get_cur_menu(), 'name', None) == "diplomacy_widget": 235 self.hide_menu() 236 return 237 238 if not DiplomacyTab.is_useable(self.session.world): 239 self.windows.open_popup(T("No diplomacy possible"), 240 T("Cannot do diplomacy as there are no other players.")) 241 return 242 243 tab = DiplomacyTab(self, self.session.world) 244 self.show_menu(tab)
245
246 - def show_multi_select_tab(self, instances):
247 tab = TabWidget(self, tabs=[SelectMultiTab(instances)], name='select_multi') 248 self.show_menu(tab)
249
250 - def show_build_menu(self, update=False):
251 """ 252 @param update: set when build possibilities change (e.g. after inhabitant tier upgrade) 253 """ 254 # check if build menu is already shown 255 if hasattr(self.get_cur_menu(), 'name') and self.get_cur_menu().name == "build_menu_tab_widget": 256 self.hide_menu() 257 258 if not update: # this was only a toggle call, don't reshow 259 return 260 261 self.set_cursor() # set default cursor for build menu 262 self.deselect_all() 263 264 if not any(settlement.owner.is_local_player for settlement in self.session.world.settlements): 265 # player has not built any settlements yet. Accessing the build menu at such a point 266 # indicates a mistake in the mental model of the user. Display a hint. 267 tab = TabWidget(self, tabs=[TabInterface(widget="buildtab_no_settlement.xml")]) 268 else: 269 btabs = BuildTab.create_tabs(self.session, self._build) 270 tab = TabWidget(self, tabs=btabs, name="build_menu_tab_widget", 271 active_tab=BuildTab.last_active_build_tab) 272 self.show_menu(tab)
273
274 - def deselect_all(self):
275 for instance in self.session.selected_instances: 276 instance.get_component(SelectableComponent).deselect() 277 self.session.selected_instances.clear()
278
279 - def _build(self, building_id, unit=None):
280 """Calls the games buildingtool class for the building_id. 281 @param building_id: int with the building id that is to be built. 282 @param unit: weakref to the unit, that builds (e.g. ship for warehouse)""" 283 self.hide_menu() 284 self.deselect_all() 285 cls = Entities.buildings[building_id] 286 if hasattr(cls, 'show_build_menu'): 287 cls.show_build_menu() 288 self.set_cursor('building', cls, None if unit is None else unit())
289
290 - def toggle_road_tool(self):
291 if not isinstance(self.cursor, mousetools.BuildingTool) or self.cursor._class.id != BUILDINGS.TRAIL: 292 self._build(BUILDINGS.TRAIL) 293 else: 294 self.set_cursor()
295
296 - def get_cur_menu(self):
297 """Returns menu that is currently displayed""" 298 return self._old_menu
299
300 - def show_menu(self, menu):
301 """Shows a menu 302 @param menu: str with the guiname or pychan object. 303 """ 304 if self._old_menu is not None: 305 if hasattr(self._old_menu, "remove_remove_listener"): 306 self._old_menu.remove_remove_listener(Callback(self.show_menu, None)) 307 self._old_menu.hide() 308 309 self._old_menu = menu 310 if self._old_menu is not None: 311 if hasattr(self._old_menu, "add_remove_listener"): 312 self._old_menu.add_remove_listener(Callback(self.show_menu, None)) 313 self._old_menu.show() 314 self.minimap_to_front() 315 316 TabWidgetChanged.broadcast(self)
317
318 - def hide_menu(self):
319 self.show_menu(None)
320
321 - def save(self, db):
322 self.message_widget.save(db) 323 self.logbook.save(db) 324 self.resource_overview.save(db) 325 LastActivePlayerSettlementManager().save(db) 326 self.save_selection(db)
327
328 - def save_selection(self, db):
329 # Store instances that are selected right now. 330 for instance in self.session.selected_instances: 331 db("INSERT INTO selected (`group`, id) VALUES (NULL, ?)", instance.worldid) 332 333 # If a single instance is selected, also store the currently displayed tab. 334 # (Else, upon restoring, we display a multi-selection tab.) 335 tabname = None 336 if len(self.session.selected_instances) == 1: 337 tabclass = self.get_cur_menu().current_tab 338 tabname = tabclass.__class__.__name__ 339 db("INSERT INTO metadata (name, value) VALUES (?, ?)", 'selected_tab', tabname) 340 341 # Store user defined unit selection groups (Ctrl+number) 342 for (number, group) in enumerate(self.session.selection_groups): 343 for instance in group: 344 db("INSERT INTO selected (`group`, id) VALUES (?, ?)", number, instance.worldid)
345
346 - def load(self, db):
347 self.message_widget.load(db) 348 self.logbook.load(db) 349 self.resource_overview.load(db) 350 351 if self.session.is_game_loaded(): 352 LastActivePlayerSettlementManager().load(db) 353 cur_settlement = LastActivePlayerSettlementManager().get_current_settlement() 354 self.cityinfo.set_settlement(cur_settlement) 355 356 self.minimap.draw() # update minimap to new world 357 358 self.current_cursor = 'default' 359 self.cursor = mousetools.SelectionTool(self.session) 360 # Set cursor correctly, menus might need to be opened. 361 # Open menus later; they may need unit data not yet inited 362 self.cursor.apply_select() 363 364 self.load_selection(db) 365 366 if not self.session.is_game_loaded(): 367 # Fire a message for new world creation 368 self.message_widget.add('NEW_WORLD') 369 370 # Show message when the relationship between players changed 371 def notify_change(caller, old_state, new_state, a, b): 372 player1 = "{0!s}".format(a.name) 373 player2 = "{0!s}".format(b.name) 374 375 data = {'player1': player1, 'player2': player2} 376 377 string_id = 'DIPLOMACY_STATUS_{old}_{new}'.format(old=old_state.upper(), 378 new=new_state.upper()) 379 self.message_widget.add(string_id=string_id, message_dict=data)
380 381 self.session.world.diplomacy.add_diplomacy_status_changed_listener(notify_change) 382
383 - def load_selection(self, db):
384 # Re-select old selected instance 385 for (instance_id, ) in db("SELECT id FROM selected WHERE `group` IS NULL"): 386 obj = WorldObject.get_object_by_id(instance_id) 387 self.session.selected_instances.add(obj) 388 obj.get_component(SelectableComponent).select() 389 390 # Re-show old tab (if there was one) or multiselection 391 if len(self.session.selected_instances) == 1: 392 tabname = db("SELECT value FROM metadata WHERE name = ?", 393 'selected_tab')[0][0] 394 # This can still be None due to old savegames not storing the information 395 tabclass = None if tabname is None else resolve_tab(tabname) 396 obj.get_component(SelectableComponent).show_menu(jump_to_tabclass=tabclass) 397 elif self.session.selected_instances: 398 self.show_multi_select_tab(self.session.selected_instances) 399 400 # Load user defined unit selection groups (Ctrl+number) 401 for (num, group) in enumerate(self.session.selection_groups): 402 for (instance_id, ) in db("SELECT id FROM selected WHERE `group` = ?", num): 403 obj = WorldObject.get_object_by_id(instance_id) 404 group.add(obj)
405
406 - def show_change_name_dialog(self, instance):
407 """Shows a dialog where the user can change the name of an object.""" 408 self.windows.open(self.change_name_dialog, instance=instance)
409
410 - def on_escape(self):
411 if self.windows.visible: 412 self.windows.on_escape() 413 elif hasattr(self.cursor, 'on_escape'): 414 self.cursor.on_escape() 415 else: 416 self.toggle_pause() 417 418 return True
419
420 - def on_return(self):
421 if self.windows.visible: 422 self.windows.on_return() 423 424 return True
425
426 - def _on_speed_changed(self, message):
427 self._display_speed(message.new)
428
429 - def _display_speed(self, tps):
430 text = '' 431 up_icon = self.mainhud.findChild(name='speedUp') 432 down_icon = self.mainhud.findChild(name='speedDown') 433 if tps == 0: # pause 434 text = '0x' 435 up_icon.set_inactive() 436 down_icon.set_inactive() 437 else: 438 if tps != GAME_SPEED.TICKS_PER_SECOND: 439 text = "{0:1g}x".format(tps * 1.0 / GAME_SPEED.TICKS_PER_SECOND) 440 #%1g: displays 0.5x, but 2x instead of 2.0x 441 index = GAME_SPEED.TICK_RATES.index(tps) 442 if index + 1 >= len(GAME_SPEED.TICK_RATES): 443 up_icon.set_inactive() 444 else: 445 up_icon.set_active() 446 if index > 0: 447 down_icon.set_active() 448 else: 449 down_icon.set_inactive() 450 451 wdg = self.mainhud.findChild(name="speed_text") 452 wdg.text = text 453 wdg.resizeToContent() 454 self.mainhud.show()
455
456 - def on_key_press(self, action, evt):
457 """Handle a key press in-game. 458 459 Returns True if the key was acted upon. 460 """ 461 _Actions = KeyConfig._Actions 462 keyval = evt.getKey().getValue() 463 464 if action == _Actions.ESCAPE: 465 return self.on_escape() 466 elif keyval == fife.Key.ENTER: 467 return self.on_return() 468 469 if action == _Actions.GRID: 470 gridrenderer = self.session.view.renderer['GridRenderer'] 471 gridrenderer.setEnabled(not gridrenderer.isEnabled()) 472 elif action == _Actions.COORD_TOOLTIP: 473 self.coordinates_tooltip.toggle() 474 elif action == _Actions.DESTROY_TOOL: 475 self.toggle_destroy_tool() 476 elif action == _Actions.REMOVE_SELECTED: 477 message = T("Are you sure you want to delete these objects?") 478 if self.windows.open_popup(T("Delete"), message, show_cancel_button=True): 479 self.session.remove_selected() 480 else: 481 self.deselect_all() 482 elif action == _Actions.ROAD_TOOL: 483 self.toggle_road_tool() 484 elif action == _Actions.SPEED_UP: 485 SpeedUpCommand().execute(self.session) 486 elif action == _Actions.SPEED_DOWN: 487 SpeedDownCommand().execute(self.session) 488 elif action == _Actions.ZOOM_IN: 489 self.session.view.zoom_in() 490 elif action == _Actions.ZOOM_OUT: 491 self.session.view.zoom_out() 492 elif action == _Actions.PAUSE: 493 TogglePauseCommand().execute(self.session) 494 elif action == _Actions.PLAYERS_OVERVIEW: 495 self.logbook.toggle_stats_visibility(widget='players') 496 elif action == _Actions.SETTLEMENTS_OVERVIEW: 497 self.logbook.toggle_stats_visibility(widget='settlements') 498 elif action == _Actions.SHIPS_OVERVIEW: 499 self.logbook.toggle_stats_visibility(widget='ships') 500 elif action == _Actions.LOGBOOK: 501 self.windows.toggle(self.logbook) 502 elif action == _Actions.DEBUG and VERSION.IS_DEV_VERSION: 503 import pdb 504 pdb.set_trace() 505 elif action == _Actions.BUILD_TOOL: 506 self.show_build_menu() 507 elif action == _Actions.ROTATE_RIGHT: 508 if hasattr(self.cursor, "rotate_right"): 509 # used in e.g. build preview to rotate building instead of map 510 self.cursor.rotate_right() 511 else: 512 self.session.view.rotate_right() 513 self.minimap.update_rotation() 514 elif action == _Actions.ROTATE_LEFT: 515 if hasattr(self.cursor, "rotate_left"): 516 self.cursor.rotate_left() 517 else: 518 self.session.view.rotate_left() 519 self.minimap.update_rotation() 520 elif action == _Actions.CHAT: 521 self.windows.open(self.chat_dialog) 522 elif action == _Actions.TRANSLUCENCY: 523 self.session.world.toggle_translucency() 524 elif action == _Actions.TILE_OWNER_HIGHLIGHT: 525 self.session.world.toggle_owner_highlight() 526 elif fife.Key.NUM_0 <= keyval <= fife.Key.NUM_9: 527 num = int(keyval - fife.Key.NUM_0) 528 self.handle_selection_group(num, evt.isControlPressed()) 529 elif action == _Actions.QUICKSAVE: 530 self.session.quicksave() 531 # Quickload is only handled by the MainListener. 532 elif action == _Actions.PIPETTE: 533 # Mode that allows copying buildings. 534 self.toggle_cursor('pipette') 535 elif action == _Actions.HEALTH_BAR: 536 # Show health bar of every instance with a health component. 537 self.session.world.toggle_health_for_all_health_instances() 538 elif action == _Actions.SHOW_SELECTED: 539 if self.session.selected_instances: 540 # Scroll to first one, we can never guarantee to display all selected units. 541 instance = next(iter(self.session.selected_instances)) 542 self.session.view.center(* instance.position.center.to_tuple()) 543 for instance in self.session.selected_instances: 544 if hasattr(instance, "path") and instance.owner.is_local_player: 545 self.minimap.show_unit_path(instance) 546 elif action == _Actions.HELP: 547 self.toggle_help() 548 else: 549 return False 550 551 return True
552
553 - def handle_selection_group(self, num, ctrl_pressed):
554 """Select existing or assign new unit selection group. 555 556 Ctrl+number creates or overwrites the group of number `num` 557 with the currently selected units. 558 Pressing the associated key selects a group and centers the 559 camera around these units. 560 """ 561 if ctrl_pressed: 562 # Only consider units owned by the player. 563 units = {u for u in self.session.selected_instances 564 if u.owner.is_local_player} 565 self.session.selection_groups[num] = units 566 # Drop units of the new group from all other groups. 567 for group in self.session.selection_groups: 568 current_group = self.session.selection_groups[num] 569 if group != current_group: 570 group -= current_group 571 else: 572 # We need to make sure to have a cursor capable of selection 573 # for apply_select() to work. 574 # This handles deselection implicitly in the destructor. 575 self.set_cursor('selection') 576 # Apply new selection. 577 for instance in self.session.selection_groups[num]: 578 instance.get_component(SelectableComponent).select(reset_cam=True) 579 # Assign copy since it will be randomly changed in selection code. 580 # The unit group itself should only be changed on Ctrl events. 581 self.session.selected_instances = self.session.selection_groups[num].copy() 582 # Show correct tabs depending on what's selected. 583 if self.session.selected_instances: 584 self.cursor.apply_select() 585 else: 586 # Nothing is selected here. Hide the menu since apply_select 587 # doesn't handle that case. 588 self.show_menu(None)
589
590 - def toggle_cursor(self, which):
591 """Alternate between the cursor *which* and the default cursor.""" 592 if which == self.current_cursor: 593 self.set_cursor() 594 else: 595 self.set_cursor(which)
596
597 - def set_cursor(self, which='default', *args, **kwargs):
598 """Sets the mousetool (i.e. cursor). 599 This is done here for encapsulation and control over destructors. 600 Further arguments are passed to the mouse tool constructor.""" 601 self.cursor.remove() 602 self.current_cursor = which 603 klass = { 604 'default': mousetools.SelectionTool, 605 'selection': mousetools.SelectionTool, 606 'tearing': mousetools.TearingTool, 607 'pipette': mousetools.PipetteTool, 608 'attacking': mousetools.AttackingTool, 609 'building': mousetools.BuildingTool, 610 }[which] 611 self.cursor = klass(self.session, *args, **kwargs)
612
613 - def toggle_destroy_tool(self):
614 """Initiate the destroy tool""" 615 self.toggle_cursor('tearing')
616
617 - def _update_zoom(self, message):
618 """Enable/disable zoom buttons""" 619 in_icon = self.mainhud.findChild(name='zoomIn') 620 out_icon = self.mainhud.findChild(name='zoomOut') 621 if message.zoom == VIEW.ZOOM_MIN: 622 out_icon.set_inactive() 623 else: 624 out_icon.set_active() 625 if message.zoom == VIEW.ZOOM_MAX: 626 in_icon.set_inactive() 627 else: 628 in_icon.set_active()
629
630 - def _on_new_disaster(self, message):
631 """Called when a building is 'infected' with a disaster.""" 632 if message.building.owner.is_local_player and len(message.disaster._affected_buildings) == 1: 633 pos = message.building.position.center 634 self.message_widget.add(point=pos, string_id=message.disaster_class.NOTIFICATION_TYPE)
635
636 - def _on_new_settlement(self, message):
637 player = message.settlement.owner 638 self.message_widget.add( 639 string_id='NEW_SETTLEMENT', 640 point=message.warehouse_position, 641 message_dict={'player': player.name}, 642 play_sound=player.is_local_player 643 )
644
645 - def _on_player_level_upgrade(self, message):
646 """Called when a player's population reaches a new level.""" 647 if not message.sender.is_local_player: 648 return 649 650 # show notification 651 self.message_widget.add( 652 point=message.building.position.center, 653 string_id='SETTLER_LEVEL_UP', 654 message_dict={'level': message.level + 1} 655 ) 656 657 # update build menu to show new buildings 658 menu = self.get_cur_menu() 659 if hasattr(menu, "name") and menu.name == "build_menu_tab_widget": 660 self.show_build_menu(update=True)
661
662 - def _on_mine_empty(self, message):
663 self.message_widget.add(point=message.mine.position.center, string_id='MINE_EMPTY')
664
665 - def _on_gui_click_action(self, msg):
666 """Make a sound when a button is clicked""" 667 AmbientSoundComponent.play_special('click', gain=10)
668
669 - def _on_gui_cancel_action(self, msg):
670 """Make a sound when a cancelButton is clicked""" 671 AmbientSoundComponent.play_special('success', gain=10)
672
673 - def _on_gui_hover_action(self, msg):
674 """Make a sound when the mouse hovers over a button""" 675 AmbientSoundComponent.play_special('refresh', position=None, gain=1)
676
677 - def _replace_hotkeys_in_widgets(self):
678 """Replaces the `{key}` in the (translated) widget helptext with the actual hotkey""" 679 hotkey_replacements = { 680 'rotateRight': 'ROTATE_RIGHT', 681 'rotateLeft': 'ROTATE_LEFT', 682 'speedUp': 'SPEED_UP', 683 'speedDown': 'SPEED_DOWN', 684 'destroy_tool': 'DESTROY_TOOL', 685 'build': 'BUILD_TOOL', 686 'gameMenuButton': 'ESCAPE', 687 'logbook': 'LOGBOOK', 688 } 689 for (widgetname, action) in hotkey_replacements.items(): 690 widget = self.mainhud.findChild(name=widgetname) 691 keys = horizons.globals.fife.get_keys_for_action(action) 692 # No `.upper()` here: "Pause" looks better than "PAUSE". 693 keyname = HOTKEYS.DISPLAY_KEY.get(keys[0], keys[0].capitalize()) 694 widget.helptext = widget.helptext.format(key=keyname)
695
696 - def _on_language_changed(self, msg):
697 """Replace the hotkeys after translation. 698 699 NOTE: This should be called _after_ the texts are replaced. This 700 currently relies on import order with `horizons.gui`. 701 """ 702 self._replace_hotkeys_in_widgets()
703