def __init__(self, windows): Window.__init__(self, windows) PickBeltWidget.__init__(self) self._settings = horizons.globals.fife._setting self.widget.mapEvents({ 'okButton': self.apply_settings, 'defaultButton': self.set_defaults, 'cancelButton': self._windows.close, }) languages = find_available_languages().keys() language_names = [LANGUAGENAMES[x] for x in sorted(languages)] fps = {0: _lazy("Disabled"), 30: 30, 45: 45, 60: 60, 90: 90, 120: 120} FIFE = SETTINGS.FIFE_MODULE UH = SETTINGS.UH_MODULE def get_resolutions(): return get_screen_resolutions(self._settings.get(FIFE, 'ScreenResolution')) self._options = [ # Graphics/Sound/Input Setting(FIFE, 'ScreenResolution', 'screen_resolution', get_resolutions, restart=True), Setting(FIFE, 'FullScreen', 'enable_fullscreen', restart=True), Setting(FIFE, 'FrameLimit', 'fps_rate', fps, restart=True, callback=self._on_FrameLimit_changed), Setting(UH, 'VolumeMusic', 'volume_music', callback=self._on_VolumeMusic_changed), Setting(UH, 'VolumeEffects', 'volume_effects', callback=self._on_VolumeEffects_changed), Setting(FIFE, 'PlaySounds', 'enable_sound', callback=self._on_PlaySounds_changed), Setting(UH, 'EdgeScrolling', 'edgescrolling'), Setting(UH, 'CursorCenteredZoom', 'cursor_centered_zoom'), Setting(UH, 'MiddleMousePan', 'middle_mouse_pan'), Setting(FIFE, 'MouseSensitivity', 'mousesensitivity', restart=True), # Game Setting(UH, 'AutosaveInterval', 'autosaveinterval'), Setting(UH, 'AutosaveMaxCount', 'autosavemaxcount'), Setting(UH, 'QuicksaveMaxCount', 'quicksavemaxcount'), Setting(UH, 'Language', 'uni_language', language_names, callback=self._on_Language_changed), Setting(UH, 'MinimapRotation', 'minimaprotation'), Setting(UH, 'UninterruptedBuilding', 'uninterrupted_building'), Setting(UH, 'AutoUnload', 'auto_unload'), Setting(UH, 'DebugLog', 'debug_log', callback=self._on_DebugLog_changed), Setting(UH, 'ShowResourceIcons', 'show_resource_icons'), Setting(UH, 'ScrollSpeed', 'scrollspeed'), Setting(UH, 'QuotesType', 'quotestype', QUOTES_SETTINGS), Setting(UH, 'NetworkPort', 'network_port', callback=self._on_NetworkPort_changed), ] self._fill_widgets() # key configuration hk = HotkeyConfiguration() number = self.sections.index(('hotkeys_settings', _('Hotkeys'))) self.page_widgets[number].addChild(hk.widget) self.hotkey_interface = hk
def __init__(self, windows): Window.__init__(self, windows) PickBeltWidget.__init__(self) self._settings = horizons.globals.fife._setting self.widget.mapEvents( {"okButton": self.apply_settings, "defaultButton": self.set_defaults, "cancelButton": self._windows.close} ) languages = find_available_languages().keys() language_names = [LANGUAGENAMES[x] for x in sorted(languages)] fps = {0: _lazy("Disabled"), 30: 30, 45: 45, 60: 60, 90: 90, 120: 120} FIFE = SETTINGS.FIFE_MODULE UH = SETTINGS.UH_MODULE def get_resolutions(): return get_screen_resolutions(self._settings.get(FIFE, "ScreenResolution")) self._options = [ # Graphics/Sound/Input Setting(FIFE, "ScreenResolution", "screen_resolution", get_resolutions, restart=True), Setting(FIFE, "FullScreen", "enable_fullscreen", restart=True), Setting(FIFE, "FrameLimit", "fps_rate", fps, restart=True, callback=self._on_FrameLimit_changed), Setting(UH, "VolumeMusic", "volume_music", callback=self._on_VolumeMusic_changed), Setting(UH, "VolumeEffects", "volume_effects", callback=self._on_VolumeEffects_changed), Setting(FIFE, "PlaySounds", "enable_sound", callback=self._on_PlaySounds_changed), Setting(UH, "EdgeScrolling", "edgescrolling"), Setting(UH, "CursorCenteredZoom", "cursor_centered_zoom"), Setting(UH, "MiddleMousePan", "middle_mouse_pan"), Setting(FIFE, "MouseSensitivity", "mousesensitivity", restart=True), # Game Setting(UH, "AutosaveInterval", "autosaveinterval"), Setting(UH, "AutosaveMaxCount", "autosavemaxcount"), Setting(UH, "QuicksaveMaxCount", "quicksavemaxcount"), Setting(UH, "Language", "uni_language", language_names, callback=self._on_Language_changed), Setting(UH, "MinimapRotation", "minimaprotation"), Setting(UH, "UninterruptedBuilding", "uninterrupted_building"), Setting(UH, "AutoUnload", "auto_unload"), Setting(UH, "DebugLog", "debug_log", callback=self._on_DebugLog_changed), Setting(UH, "ShowResourceIcons", "show_resource_icons"), Setting(UH, "ScrollSpeed", "scrollspeed"), Setting(UH, "QuotesType", "quotestype", QUOTES_SETTINGS), Setting(UH, "NetworkPort", "network_port", callback=self._on_NetworkPort_changed), ] self._fill_widgets() # key configuration hk = HotkeyConfiguration() number = self.sections.index(("hotkeys_settings", _("Hotkeys"))) self.page_widgets[number].addChild(hk.widget) self.hotkey_interface = hk
def add_settings(self): #self.createAndAddEntry(module, name_in_settings_xml, widgetname, # applyfunction=None, initialdata=None, requiresrestart=False) uh_add = partial(self._setting.createAndAddEntry, UH_MODULE) fife_add = partial(self._setting.createAndAddEntry, FIFE_MODULE) uh_add("AutosaveInterval", "autosaveinterval") uh_add("AutosaveMaxCount", "autosavemaxcount") uh_add("QuicksaveMaxCount", "quicksavemaxcount") uh_add("EdgeScrolling", "edgescrolling") uh_add("CursorCenteredZoom", "cursor_centered_zoom") uh_add("ScrollSpeed", "scrollspeed") uh_add("MiddleMousePan", "middle_mouse_pan") uh_add("UninterruptedBuilding", "uninterrupted_building") uh_add("AutoUnload", "auto_unload") uh_add("MinimapRotation", "minimaprotation") uh_add("QuotesType", "quotestype", initialdata=QUOTES_SETTINGS) uh_add("ShowResourceIcons", "show_resource_icons") bpp = {0: _lazy("Default"), 16: _lazy("16 bit"), 32: _lazy("32 bit")} fife_add("BitsPerPixel", "screen_bpp", initialdata=bpp, requiresrestart=True) languages = find_available_languages().keys() uh_add("Language", "uni_language", applyfunction=self.update_languages, initialdata=[LANGUAGENAMES[x] for x in sorted(languages)]) uh_add("VolumeMusic", "volume_music", applyfunction=self.set_volume_music) uh_add("VolumeEffects", "volume_effects", applyfunction=self.set_volume_effects) uh_add("NetworkPort", "network_port", applyfunction=self.set_network_port) uh_add("DebugLog", "debug_log", applyfunction=self.set_debug_log) play_sounds = self._setting.entries[FIFE_MODULE]['PlaySounds'] play_sounds.applyfunction = lambda x: self.engine.sound.setup_sound() play_sounds.requiresrestart = False # This is set to True in FIFE render_backend = self._setting.entries[FIFE_MODULE]['RenderBackend'] render_backend.applyfunction = lambda x: self._show_renderbackend_warning() fps = [30, 45, 60, 90, 120] fife_add("FrameLimit", "fps_rate", initialdata=fps, requiresrestart=True) fife_add("MouseSensitivity", "mousesensitivity", requiresrestart=True)
def _strip_translation_marks(self, string): """Converts object translation `string` to translated object for in-game display. Object properties supposed to be translated are recognized by the (subsequently stripped) leading `_ `. If `string` is supposed to be translated, returns lazy translation object of `string`. If `string` is not supposed to be translated, returns `string` unchanged. If `string` is None (not defined or empty in yaml), returns empty unicode. """ if not string: return u'' if string.startswith("_ "): return _lazy(string[2:]) else: return string
class SmallProductionOverviewTab(ProductionOverviewTab): """Only display productions for which we have a related 'field' in range. Requires the building class using this tab to implement get_providers(). """ widget = 'overview_farm.xml' helptext = _lazy("Production overview") production_line_gui_xml = "overview_farmproductionline.xml" # the farm uses small buttons ACTIVE_PRODUCTION_ANIM_DIR = "content/gui/images/animations/cogs/small" BUTTON_BACKGROUND = "content/gui/images/buttons/msg_button_small.png" def get_displayed_productions(self): possible_res = set(res for field in self.instance.get_providers() for res in field.provided_resources) all_farm_productions = self.instance.get_component( Producer).get_productions() productions = set([ p for p in all_farm_productions for res in p.get_consumed_resources().keys() if res in possible_res ]) return sorted(productions, key=operator.methodcaller('get_production_line_id'))
class ShipOverviewTab(OverviewTab): widget = 'overview_trade_ship.xml' icon_path = 'icons/tabwidget/ship/ship_inv' helptext = _lazy("Ship overview") def init_widget(self): super(ShipOverviewTab, self).init_widget() ship_inv = self.instance.get_component(StorageComponent).inventory self.widget.child_finder('inventory').init(self.instance.session.db, ship_inv) # FIXME having to access the WindowManager this way is pretty ugly self._windows = self.instance.session.ingame_gui.windows self.route_menu = RouteConfig(self._windows, self.instance) def _configure_route(self): self._windows.toggle(self.route_menu) def _refresh_found_settlement_button(self, events): island_without_player_settlement_found = False helptext = _( "The ship needs to be close to an island to found a settlement.") for island in self.instance.session.world.get_islands_in_radius( self.instance.position, self.instance.radius): if not any(settlement.owner.is_local_player for settlement in island.settlements): island_without_player_settlement_found = True else: helptext = _("You already have a settlement on this island.") if island_without_player_settlement_found: events['found_settlement'] = Callback( self.instance.session.ingame_gui._build, BUILDINGS.WAREHOUSE, weakref.ref(self.instance)) self.widget.child_finder('found_settlement_bg').set_active() self.widget.child_finder('found_settlement').set_active() self.widget.child_finder('found_settlement').helptext = _( "Build settlement") else: events['found_settlement'] = None self.widget.child_finder('found_settlement_bg').set_inactive() self.widget.child_finder('found_settlement').set_inactive() self.widget.child_finder('found_settlement').helptext = helptext cb = Callback( self.instance.session.ingame_gui.resource_overview. set_construction_mode, self.instance, Entities.buildings[BUILDINGS.WAREHOUSE].costs) events['found_settlement/mouseEntered'] = cb cb1 = Callback(self.instance.session.ingame_gui.resource_overview. close_construction_mode) cb2 = Callback( self.widget.child_finder('found_settlement').hide_tooltip) #TODO the tooltip should actually hide on its own. Ticket #1096 cb = Callback.ChainedCallbacks(cb1, cb2) events['found_settlement/mouseExited'] = cb def _refresh_trade_button(self, events): warehouses = self.instance.get_tradeable_warehouses() if warehouses: if warehouses[0].owner is self.instance.owner: helptext = _('Load/Unload') else: helptext = _('Buy/Sell') events['trade'] = Callback( self.instance.get_component(SelectableComponent).show_menu, TradeTab) self.widget.findChild(name='trade_bg').set_active() self.widget.findChild(name='trade').set_active() self.widget.findChild(name='trade').helptext = helptext else: events['trade'] = None self.widget.findChild(name='trade_bg').set_inactive() self.widget.findChild(name='trade').set_inactive() self.widget.findChild(name='trade').helptext = _( 'Too far from the nearest tradeable warehouse') def _refresh_combat(self): # no combat def click_on_cannons(button): button.button.capture( Callback( self.instance.session.ingame_gui.show_popup, _("Cannot equip trade ship with weapons"), _("It is not possible to equip a trade ship with weapons.") )) self.widget.findChild(name='inventory').apply_to_buttons( click_on_cannons, lambda b: b.res_id == WEAPONS.CANNON) def refresh(self): # show rename when you click on name events = { 'name': Callback(self.instance.session.ingame_gui.show_change_name_dialog, self.instance), 'configure_route/mouseClicked': Callback(self._configure_route) } self._refresh_found_settlement_button(events) self._refresh_trade_button(events) self.widget.mapEvents(events) self.widget.child_finder('inventory').update() self._refresh_combat() super(ShipOverviewTab, self).refresh()
class TraderShipOverviewTab(OverviewTab): widget = 'overview_tradership.xml' icon_path = 'icons/tabwidget/ship/ship_inv' helptext = _lazy("Ship overview")
class BoatbuilderTab(UnitbuilderTabBase): widget = 'boatbuilder.xml' helptext = _lazy("Boat builder overview") UNIT_THUMBNAIL = "content/gui/icons/thumbnails/{type_id}.png" UNIT_PREVIEW_IMAGE = "content/gui/images/objects/ships/116/{type_id}.png"
import random import horizons.globals from horizons.constants import TIER from horizons.i18n import _lazy from horizons.gui.util import load_uh_widget from horizons.gui.windows import Window from horizons.messaging import LoadingProgress # list of quotes and gameplay tips that are displayed while loading a game # NOTE: Try to use not more than 4 lines in a quote/gameplay tip ! FUN_QUOTES = { 'name': _lazy("Quotes"), # Fun Quotes should not be translated... 'items': [ "beer, the cause and solution to all problems of humanity", "trying is the first step t'wards failing. ", "# nobody actually knows how the code below works. ", "here be dragons", "procrastination is the first step towards getting stuff done", "patience is a virtue \n(barra)", "you must really, really love to test \n(portal 2)", "here be bugs", "strength is the capacity to break a chocolate bar into four pieces with your bare hands - and then eat just one of the pieces", "If one does not know to which port one is sailing, no wind is favorable", "The pessimist complains about the wind; \nthe optimist expects it to change; \nthe realist adjusts the sails", "Travel beyond the horizon and discover unknown worlds!", u"War… war never changes",
class BuildRelatedTab(OverviewTab): """ Adds a special tab to each production building with at least one entry in the table related_buildings. This tab acts as modified build menu tab and only displays those buildings actually related to the selected building. Examples: tree for lumberjack; pavilion, school, etc. for inhabitants. """ widget = 'related_buildings.xml' icon_path = 'icons/tabwidget/production/related' helptext = _lazy("Build related buildings") template_gui_xml = 'related_buildings_container.xml' def refresh(self): """ This function is called by the TabWidget to redraw the widget. """ # remove old data parent_container = self.widget.child_finder('related_buildings') while parent_container.children: parent_container.removeChild(parent_container.children[0]) # load all related buildings from DB building_ids = self.instance.session.db.get_related_building_ids_for_menu( self.instance.id) sorted_ids = sorted([(b, Entities.buildings[b].settler_level) for b in building_ids], key=lambda x: x[1]) container = self.__get_new_container() self.current_row = min(building[1] for building in sorted_ids) for building_id, level in sorted_ids: if level <= self.instance.owner.settler_level: # available in build menu? button = self._create_build_buttons(building_id, container) # check whether to start new line (for new tier row) if level > self.current_row: self.current_row = level parent_container.addChild(container) container = self.__get_new_container() container.findChild( name="build_button_container").addChild(button) button_bg = Icon( image="content/gui/images/buttons/buildmenu_button_bg.png") container.findChild( name="build_button_bg_container").addChild(button_bg) # Still need to add last container parent_container.addChild(container) super(BuildRelatedTab, self).refresh() def __get_new_container(self): """ Loads a background container xml file. Returns the loaded widget. """ gui = load_uh_widget(self.template_gui_xml) return gui.findChild(name="buildings_container") def _create_build_buttons(self, building_id, container): # {{mode}} in double braces because it is replaced as a second step building_type = Entities.buildings[building_id] build_button = ImageButton(name="build{id}".format(id=building_id), helptext=building_type.get_tooltip()) build_button.path = "icons/buildmenu/{id:03d}".format(id=building_id) build_button.capture(Callback(self.build_related, building_id)) return build_button def build_related(self, building_id): self.hide() # deselect all for instance in self.instance.session.selected_instances: instance.get_component(SelectableComponent).deselect() self.instance.session.selected_instances.clear() self.instance.session.ingame_gui.set_cursor( 'building', Entities.buildings[building_id], ship=None, build_related=self.instance)
class ProductivityLowStatus(StatusIcon): """Terminology: productivity = capacity utilization""" threshold = 0.25 # display when productivity lower than this priority = 400 icon = 'as_attention_please+idle+45' helptext = _lazy("This building has a very low productivity.")
class DecommissionedStatus(StatusIcon): priority = 800 icon = 'as_decommissioned+idle+45' helptext = _lazy("This building is decomissioned.")
class SettingsDialog(PickBeltWidget, Window): """Widget for Options dialog with pickbelt style pages""" widget_xml = 'settings.xml' sections = (('graphics_settings', _lazy('Graphics')), ('hotkeys_settings', _lazy('Hotkeys')), ('game_settings', _lazy('Game'))) def __init__(self, windows): Window.__init__(self, windows) PickBeltWidget.__init__(self) self._settings = horizons.globals.fife._setting self.widget.mapEvents({ 'okButton': self.apply_settings, 'defaultButton': self.set_defaults, 'cancelButton': self._windows.close, }) languages = find_available_languages().keys() language_names = [LANGUAGENAMES[x] for x in sorted(languages)] fps = {0: _lazy("Disabled"), 30: 30, 45: 45, 60: 60, 90: 90, 120: 120} FIFE = SETTINGS.FIFE_MODULE UH = SETTINGS.UH_MODULE def get_resolutions(): return get_screen_resolutions( self._settings.get(FIFE, 'ScreenResolution')) self._options = [ # Graphics/Sound/Input Setting(FIFE, 'ScreenResolution', 'screen_resolution', get_resolutions, restart=True), Setting(FIFE, 'FullScreen', 'enable_fullscreen', restart=True), Setting(FIFE, 'FrameLimit', 'fps_rate', fps, restart=True, callback=self._on_FrameLimit_changed), Setting(UH, 'VolumeMusic', 'volume_music', callback=self._on_VolumeMusic_changed), Setting(UH, 'VolumeEffects', 'volume_effects', callback=self._on_VolumeEffects_changed), Setting(FIFE, 'PlaySounds', 'enable_sound', callback=self._on_PlaySounds_changed), Setting(UH, 'EdgeScrolling', 'edgescrolling'), Setting(UH, 'CursorCenteredZoom', 'cursor_centered_zoom'), Setting(UH, 'MiddleMousePan', 'middle_mouse_pan'), Setting(FIFE, 'MouseSensitivity', 'mousesensitivity', restart=True), # Game Setting(UH, 'AutosaveInterval', 'autosaveinterval'), Setting(UH, 'AutosaveMaxCount', 'autosavemaxcount'), Setting(UH, 'QuicksaveMaxCount', 'quicksavemaxcount'), Setting(UH, 'Language', 'uni_language', language_names, callback=self._on_Language_changed), Setting(UH, 'MinimapRotation', 'minimaprotation'), Setting(UH, 'UninterruptedBuilding', 'uninterrupted_building'), Setting(UH, 'AutoUnload', 'auto_unload'), Setting(UH, 'DebugLog', 'debug_log', callback=self._on_DebugLog_changed), Setting(UH, 'ShowResourceIcons', 'show_resource_icons'), Setting(UH, 'ScrollSpeed', 'scrollspeed'), Setting(UH, 'QuotesType', 'quotestype', QUOTES_SETTINGS), Setting(UH, 'NetworkPort', 'network_port', callback=self._on_NetworkPort_changed), ] self._fill_widgets() # key configuration hk = HotkeyConfiguration() number = self.sections.index(('hotkeys_settings', _('Hotkeys'))) self.page_widgets[number].addChild(hk.widget) self.hotkey_interface = hk def show(self): self.widget.show() def hide(self): self.widget.hide() def show_restart_popup(self): headline = _("Restart required") message = _( "Some of your changes require a restart of Unknown Horizons.") self._windows.open_popup(headline, message) def set_defaults(self): title = _("Restore default settings") msg = _("Restoring the default settings will delete all changes to the settings you made so far.") + \ u" " + _("Do you want to continue?") if self._windows.open_popup(title, msg, show_cancel_button=True): self.hotkey_interface.reset_to_default() self._settings.set_defaults() self.show_restart_popup() self._windows.close() def apply_settings(self): restart_required = False for entry in self._options: widget = self.widget.findChild(name=entry.widget_name) new_value = widget.getData() if isinstance(entry.initial_data, collections.Callable): initial_data = entry.initial_data() else: initial_data = entry.initial_data if isinstance(initial_data, list): new_value = initial_data[new_value] elif isinstance(initial_data, dict): new_value = initial_data.keys()[new_value] old_value = self._settings.get(entry.module, entry.name) # Store new setting self._settings.set(entry.module, entry.name, new_value) # If setting changed, allow applying of new value and sanity checks if new_value != old_value: if entry.restart: restart_required = True if entry.callback: entry.callback(old_value, new_value) if restart_required: self.show_restart_popup() self.hotkey_interface.save_settings() self._settings.apply() self._settings.save() self._windows.close() def _fill_widgets(self): for entry in self._options: value = self._settings.get(entry.module, entry.name) widget = self.widget.findChild(name=entry.widget_name) if entry.initial_data: if isinstance(entry.initial_data, collections.Callable): initial_data = entry.initial_data() else: initial_data = entry.initial_data if isinstance(initial_data, dict): widget.setInitialData(initial_data.values()) value = initial_data.keys().index(value) elif isinstance(initial_data, list): widget.setInitialData(initial_data) value = initial_data.index(value) else: widget.setInitialData(initial_data) widget.setData(value) # For sliders, there also is a label showing the current value if isinstance(widget, horizons.globals.fife.pychan.widgets.Slider): cb = Callback(self.slider_change, widget) cb() widget.capture(cb) def slider_change(self, widget): """Callback for updating value label of a slider after dragging it. As the numeric values under the hood often do not represent mental models of what the setting achieves very well, depending on the setting in question we display a modified value, sometimes with a '%' suffix.""" value_label = self.widget.findChild(name=widget.name + '_value') value = { 'volume_music': lambda x: u'%s%%' % int(500 * x), 'volume_effects': lambda x: u'%s%%' % int(200 * x), 'mousesensitivity': lambda x: u'%+.1f%%' % (200 * x), 'autosaveinterval': lambda x: u'%d' % x, 'autosavemaxcount': lambda x: u'%d' % x, 'quicksavemaxcount': lambda x: u'%d' % x, 'scrollspeed': lambda x: u'%.1f' % x, }[widget.name](widget.value) value_label.text = value # callbacks for changes of settings def _on_PlaySounds_changed(self, old, new): horizons.globals.fife.sound.setup_sound() def _on_VolumeMusic_changed(self, old, new): horizons.globals.fife.sound.set_volume_bgmusic(new) def _on_VolumeEffects_changed(self, old, new): horizons.globals.fife.sound.set_volume_effects(new) def _on_FrameLimit_changed(self, old, new): # handling value 0 for framelimit to disable limiter if new == 0: self._settings.set(SETTINGS.FIFE_MODULE, 'FrameLimitEnabled', False) else: self._settings.set(SETTINGS.FIFE_MODULE, 'FrameLimitEnabled', True) def _on_NetworkPort_changed(self, old, new): """Sets a new value for client network port""" # port is saved as string due to pychan limitations try: # 0 is not a valid port, but a valid value here (used for default) parse_port(new) except ValueError: headline = _("Invalid network port") descr = _( "The port you specified is not valid. It must be a number between 1 and 65535." ) advice = _( "Please check the port you entered and make sure it is in the specified range." ) self._windows.open_error_popup(headline, descr, advice) # reset value and reshow settings dlg self._settings.set(SETTINGS.UH_MODULE, 'NetworkPort', u"0") else: # port is valid try: if NetworkInterface() is None: NetworkInterface.create_instance() NetworkInterface().network_data_changed() except Exception as e: headline = _("Failed to apply new network settings.") descr = _( "Network features could not be initialized with the current configuration." ) advice = _( "Check the settings you specified in the network section.") if 0 < parse_port(new) < 1024: #i18n This is advice for players seeing a network error with the current config advice += u" " + \ _("Low port numbers sometimes require special access privileges, try 0 or a number greater than 1024.") details = unicode(e) self._windows.open_error_popup(headline, descr, advice, details) def _on_Language_changed(self, old, new): language = LANGUAGENAMES.get_by_value(new) change_language(language) def _on_DebugLog_changed(self, old, new): horizons.main.set_debug_log(new)
def __init__(self, windows): Window.__init__(self, windows) PickBeltWidget.__init__(self) self._settings = horizons.globals.fife._setting self.widget.mapEvents({ 'okButton': self.apply_settings, 'defaultButton': self.set_defaults, 'cancelButton': self._windows.close, }) languages = find_available_languages().keys() language_names = [LANGUAGENAMES[x] for x in sorted(languages)] fps = {0: _lazy("Disabled"), 30: 30, 45: 45, 60: 60, 90: 90, 120: 120} FIFE = SETTINGS.FIFE_MODULE UH = SETTINGS.UH_MODULE def get_resolutions(): return get_screen_resolutions( self._settings.get(FIFE, 'ScreenResolution')) self._options = [ # Graphics/Sound/Input Setting(FIFE, 'ScreenResolution', 'screen_resolution', get_resolutions, restart=True), Setting(FIFE, 'FullScreen', 'enable_fullscreen', restart=True), Setting(FIFE, 'FrameLimit', 'fps_rate', fps, restart=True, callback=self._on_FrameLimit_changed), Setting(UH, 'VolumeMusic', 'volume_music', callback=self._on_VolumeMusic_changed), Setting(UH, 'VolumeEffects', 'volume_effects', callback=self._on_VolumeEffects_changed), Setting(FIFE, 'PlaySounds', 'enable_sound', callback=self._on_PlaySounds_changed), Setting(UH, 'EdgeScrolling', 'edgescrolling'), Setting(UH, 'CursorCenteredZoom', 'cursor_centered_zoom'), Setting(UH, 'MiddleMousePan', 'middle_mouse_pan'), Setting(FIFE, 'MouseSensitivity', 'mousesensitivity', restart=True), # Game Setting(UH, 'AutosaveInterval', 'autosaveinterval'), Setting(UH, 'AutosaveMaxCount', 'autosavemaxcount'), Setting(UH, 'QuicksaveMaxCount', 'quicksavemaxcount'), Setting(UH, 'Language', 'uni_language', language_names, callback=self._on_Language_changed), Setting(UH, 'MinimapRotation', 'minimaprotation'), Setting(UH, 'UninterruptedBuilding', 'uninterrupted_building'), Setting(UH, 'AutoUnload', 'auto_unload'), Setting(UH, 'DebugLog', 'debug_log', callback=self._on_DebugLog_changed), Setting(UH, 'ShowResourceIcons', 'show_resource_icons'), Setting(UH, 'ScrollSpeed', 'scrollspeed'), Setting(UH, 'QuotesType', 'quotestype', QUOTES_SETTINGS), Setting(UH, 'NetworkPort', 'network_port', callback=self._on_NetworkPort_changed), ] self._fill_widgets() # key configuration hk = HotkeyConfiguration() number = self.sections.index(('hotkeys_settings', _('Hotkeys'))) self.page_widgets[number].addChild(hk.widget) self.hotkey_interface = hk
class AccountTab(MainSquareTab): """Display basic income and expenses of a settlement""" widget = 'tab_account.xml' icon_path = 'icons/tabwidget/warehouse/account' helptext = _lazy("Account") def init_widget(self): super(AccountTab, self).init_widget() self.widget.mapEvents({ 'show_production_overview/mouseClicked': self.show_production_overview, }) # FIXME having to access the WindowManager this way is pretty ugly self._windows = self.instance.session.ingame_gui.windows self.prod_overview = ProductionOverview(self._windows, self.settlement) self.widget.child_finder( 'headline').text = self.settlement.get_component( NamedComponent).name self.widget.child_finder('headline').helptext = _( 'Click to change the name of your settlement') path = 'icons/widgets/cityinfo/settlement_%s' % self.settlement.owner.color.name self.widget.child_finder('show_production_overview').path = path def show_production_overview(self): self._windows.toggle(self.prod_overview) def refresh(self): super(AccountTab, self).refresh() self.refresh_collector_utilization() taxes = self.settlement.cumulative_taxes running_costs = self.settlement.cumulative_running_costs buy_expenses = self.settlement.get_component( TradePostComponent).buy_expenses sell_income = self.settlement.get_component( TradePostComponent).sell_income balance = self.settlement.balance sign = '+' if balance >= 0 else '-' self.widget.child_finder('taxes').text = unicode(taxes) self.widget.child_finder('running_costs').text = unicode(running_costs) self.widget.child_finder('buying').text = unicode(buy_expenses) self.widget.child_finder('sale').text = unicode(sell_income) self.widget.child_finder('balance').text = unicode(sign + ' ' + str(abs(balance))) self.widget.child_finder( 'headline').text = self.settlement.get_component( NamedComponent).name rename = Callback( self.instance.session.ingame_gui.show_change_name_dialog, self.settlement) self.widget.mapEvents({'headline': rename}) def refresh_collector_utilization(self): if self.instance.has_component(CollectingComponent): utilization = int( round(self.instance.get_collector_utilization() * 100)) utilization = unicode(utilization) + u'%' else: utilization = u'---' self.widget.findChild(name="collector_utilization").text = utilization
class TradeTab(TabInterface): """Ship to trade post's trade tab. International as well as national trade.""" log = logging.getLogger("gui.tabs.tradetab") widget = 'tradetab.xml' icon_path = 'icons/tabwidget/warehouse/buysell' helptext = _lazy('Trade') scheduled_update_delay = 0.3 # map the size buttons in the gui to an amount exchange_size_buttons = { 1: 'size_1', 5: 'size_2', 10: 'size_3', 20: 'size_4', 50: 'size_5', } images = { 'box_highlighted': 'content/gui/icons/ship/smallbutton_a.png', 'box': 'content/gui/icons/ship/smallbutton.png', } def __init__(self, instance): """ @param instance: ship instance used for trading """ self.instance = instance super(TradeTab, self).__init__() def init_widget(self): events = {} for k, v in self.exchange_size_buttons.iteritems(): events[v] = Callback(self.set_exchange, k) self.widget.mapEvents(events) self.partner = None self.exchange = None self.set_exchange(GUI.DEFAULT_EXCHANGE_AMOUNT, initial=True) def refresh(self): super(TradeTab, self).refresh() self.draw_widget() def draw_widget(self): self.widget.findChild( name='ship_name').text = self.instance.get_component( NamedComponent).name self.partners = self.instance.get_tradeable_warehouses() # set up gui dynamically according to partners # NOTE: init on inventories will be optimized away internally if it's only an update if self.partners: partner_label = self.widget.findChild(name='partners') nearest_partner = self.get_nearest_partner(self.partners) partner_label.text = self.partners[ nearest_partner].settlement.get_component(NamedComponent).name new_partner = self.partners[nearest_partner] different_partner = new_partner is not self.partner if self.partner is not None and different_partner: self.__remove_changelisteners() self.partner = new_partner if different_partner: self.__add_changelisteners() is_own = self.partner.owner is self.instance.owner if not is_own: # foreign warehouse => disable exchange widget, enable trade interface self.widget.findChild(name='domestic').hide() selling_inventory = self.widget.findChild( name='selling_inventory') selling_inventory.init( self.instance.session.db, self.partner.get_component(StorageComponent).inventory, self.partner.settlement.get_component( TradePostComponent).sell_list, selling=True) for button in self.get_widgets_by_class( selling_inventory, ImageFillStatusButton): button.button.capture( Callback(self.transfer, button.res_id, self.partner.settlement, True)) buying_inventory = self.widget.findChild( name='buying_inventory') buying_inventory.init( self.instance.session.db, self.partner.get_component(StorageComponent).inventory, self.partner.settlement.get_component( TradePostComponent).buy_list, selling=False) for button in self.get_widgets_by_class( buying_inventory, ImageFillStatusButton): button.button.capture( Callback(self.transfer, button.res_id, self.partner.settlement, False)) self.widget.findChild(name='international').show() else: # own warehouse => enable exchange widget, disable trade interface self.widget.findChild(name='international').hide() inv_partner = self.widget.findChild( name='inventory_partner') # This is no BuySellInventory! inv_partner.init( self.instance.session.db, self.partner.get_component(StorageComponent).inventory) for button in self.get_widgets_by_class( inv_partner, ImageFillStatusButton): button.button.capture( Callback(self.transfer, button.res_id, self.partner.settlement, self.instance)) self.widget.findChild(name='domestic').show() inv = self.widget.findChild(name='inventory_ship') inv.init(self.instance.session.db, self.instance.get_component(StorageComponent).inventory) for button in self.get_widgets_by_class(inv, ImageFillStatusButton): button.button.capture( Callback(self.transfer, button.res_id, self.partner.settlement, False)) self.widget.adaptLayout() else: # no partner in range any more pass def __remove_changelisteners(self): # never redraw on clicks immediately because of # http://github.com/fifengine/fifengine/issues/387 # This way, there is a chance of clicks being noticed by pychan. # The cost is to delay all updates, which in this case is 0.3 sec, therefore deemed bearable. # need to be idempotent, show/hide calls it in arbitrary order if self.instance: self.instance.discard_change_listener(self._schedule_refresh) inv = self.instance.get_component(StorageComponent).inventory inv.discard_change_listener(self._schedule_refresh) if self.partner: inv = self.partner.get_component(StorageComponent).inventory inv.discard_change_listener(self._schedule_refresh) tradepost = self.partner.settlement.get_component( TradePostComponent) tradepost.discard_change_listener(self._schedule_refresh) def __add_changelisteners(self): # need to be idempotent, show/hide calls it in arbitrary order if self.instance: self.instance.add_change_listener(self._schedule_refresh, no_duplicates=True) inv = self.instance.get_component(StorageComponent).inventory inv.add_change_listener(self._schedule_refresh, no_duplicates=True) if self.partner: inv = self.partner.get_component(StorageComponent).inventory inv.add_change_listener(self._schedule_refresh, no_duplicates=True) tradepost = self.partner.settlement.get_component( TradePostComponent) tradepost.add_change_listener(self._schedule_refresh, no_duplicates=True) def hide(self): self.widget.hide() self.__remove_changelisteners() def show(self): self.widget.show() self.__add_changelisteners() self.refresh() def set_exchange(self, size, initial=False): """ Highlight radio button with selected amount and deselect old highlighted. @param initial: bool, use it to set exchange size when initing the widget """ self.log.debug("Tradewidget: exchange size now: %s", size) if not initial: old_name = self.exchange_size_buttons[self.exchange] old_box = self.widget.findChild(name=old_name) old_box.up_image = self.images['box'] self.exchange = size new_name = self.exchange_size_buttons[self.exchange] box_h = self.widget.findChild(name=new_name) box_h.up_image = self.images['box_highlighted'] if not initial: self.draw_widget() def transfer(self, res_id, settlement, selling): """Buy or sell the resources""" if self.instance.position.distance( settlement.warehouse.position) <= self.instance.radius: is_own = settlement.owner is self.instance.owner if selling and not is_own: # ship sells resources to settlement self.log.debug( 'InternationalTrade: %s/%s is selling %d of res %d to %s/%s', self.instance.get_component(NamedComponent).name, self.instance.owner.name, self.exchange, res_id, settlement.get_component(NamedComponent).name, settlement.owner.name) # international trading has own error handling, no signal_error SellResource(settlement.get_component(TradePostComponent), self.instance, res_id, self.exchange).execute(self.instance.session) elif selling and is_own: # transfer from settlement to ship self.log.debug( 'Trade: Transferring %s of res %s from %s/%s to %s/%s', self.exchange, res_id, settlement.get_component(NamedComponent).name, settlement.owner.name, self.instance.get_component(NamedComponent).name, self.instance.owner.name) TransferResource(self.exchange, res_id, settlement, self.instance, signal_errors=True).execute( self.instance.session) elif not selling and not is_own: # ship buys resources from settlement self.log.debug( 'InternationalTrade: %s/%s is buying %d of res %d from %s/%s', self.instance.get_component(NamedComponent).name, self.instance.owner.name, self.exchange, res_id, settlement.get_component(NamedComponent).name, settlement.owner.name) # international trading has own error handling, no signal_error BuyResource(settlement.get_component(TradePostComponent), self.instance, res_id, self.exchange).execute(self.instance.session) elif not selling and is_own: # transfer from ship to settlement self.log.debug( 'Trade: Transferring %s of res %s from %s/%s to %s/%s', self.exchange, res_id, self.instance.get_component(NamedComponent).name, self.instance.owner.name, settlement.get_component(NamedComponent).name, settlement.owner.name) TransferResource(self.exchange, res_id, self.instance, settlement, signal_errors=True).execute( self.instance.session) # let gui update be handled by changelisteners (mp-safe) def get_widgets_by_class(self, parent_widget, widget_class): """Gets all widget of a certain widget class from the tab. (e.g. pychan.widgets.Label for all labels)""" children = [] def _find_widget(widget): if isinstance(widget, widget_class): children.append(widget) parent_widget.deepApply(_find_widget) return children def get_nearest_partner(self, partners): nearest = None nearest_dist = None for partner in partners: dist = partner.position.distance(self.instance.position) if nearest_dist is None or dist < nearest_dist: nearest_dist = dist nearest = partners.index(partner) return nearest
import random import horizons.globals from horizons.constants import TIER from horizons.i18n import _lazy from horizons.gui.util import load_uh_widget from horizons.gui.windows import Window from horizons.messaging import LoadingProgress # list of quotes and gameplay tips that are displayed while loading a game # NOTE: Try to use not more than 4 lines in a quote/gameplay tip ! FUN_QUOTES = { "name": _lazy("Quotes"), # Fun Quotes should not be translated... "items": [ "beer, the cause and solution to all problems of humanity", "trying is the first step t'wards failing. ", "# nobody actually knows how the code below works. ", "here be dragons", "procrastination is the first step towards getting stuff done", "patience is a virtue \n(barra)", "you must really, really love to test \n(portal 2)", "here be bugs", "strength is the capacity to break a chocolate bar into four pieces with your bare hands - and then eat just one of the pieces", "If one does not know to which port one is sailing, no wind is favorable", "The pessimist complains about the wind; \nthe optimist expects it to change; \nthe realist adjusts the sails", "Travel beyond the horizon and discover unknown worlds!", u"War… war never changes",
import random import horizons.globals from horizons.constants import TIER from horizons.i18n import _lazy from horizons.gui.util import load_uh_widget from horizons.gui.windows import Window from horizons.messaging import LoadingProgress # list of quotes and gameplay tips that are displayed while loading a game # NOTE: Try to use not more than 4 lines in a quote/gameplay tip ! FUN_QUOTES = { 'name': _lazy("Quotes"), # Fun Quotes should not be translated... 'items': [ "Beer, the cause and solution to all problems of humanity.", "Trying is the first step t'wards failing.", "# nobody actually knows how the code below works.", "Here are dragons.", "Procrastination is the first step towards getting stuff done.", "Patience is a virtue. \n(barra)", "You must really, really love to test. \n(portal 2)", "Here are bugs.", "Strength is the capacity to break a chocolate bar into four pieces with your bare hands - and then eat just one of the pieces.", "If one does not know to which port one is sailing, no wind is favorable.", "The pessimist complains about the wind; \nthe optimist expects it to change; \nthe realist adjusts the sails.", "Travel beyond the horizon and discover unknown worlds!", u"War… war never changes.",
class PestilenceStatus(StatusIcon): priority = 2000 icon = 'as_pestilence+idle+45' helptext = _lazy("The inhabitants of this building have the plague.")
class SelectMultiTab(TabInterface): """ Tab shown when multiple units are selected """ widget = 'overview_select_multi.xml' icon_path = 'icons/tabwidget/common/inventory' helptext = _lazy("Selected Units") max_row_entry_number = 3 max_column_entry_number = 4 def __init__(self, selected_instances=None): self.selected_instances = selected_instances or [] # keep track of units that have stance self.stance_unit_number = 0 # keep local track of selected instances self.instances = [] # keep track of number of instances per type self.type_number = defaultdict(int) for i in self.selected_instances: if hasattr(i, 'stance'): self.stance_unit_number += 1 self.instances.append(i) if not i.has_remove_listener(Callback(self.on_instance_removed, i)): i.add_remove_listener(Callback(self.on_instance_removed, i)) self.type_number[i.id] += 1 self._scheduled_refresh = False super(SelectMultiTab, self).__init__() def init_widget(self): if self.stance_unit_number != 0: self.show_stance_widget() self.draw_selected_units_widget() def add_entry(self, entry): if self.column_number > self.max_column_entry_number - 1: self.column_number = 0 self.row_number += 1 if self.row_number >= 3: # TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO # This crashes when more than 2 rows are needed. # There just aren't any hboxes in the xml. # TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO self.row_number = 2 return self.column_number += 1 self.widget.findChild(name="hbox_%s" % self.row_number).addChild( entry.widget) self.entries.append(entry) def draw_selected_units_widget(self): self.entries = [] self.row_number = 0 self.column_number = 0 # if only one type of units is selected draw individual widgets for selected units if len(self.type_number) == 1: for instance in self.instances: self.add_entry(UnitEntry([instance], False)) else: entry_instances = defaultdict(list) for instance in self.instances: entry_instances[instance.id].append(instance) for instances in entry_instances.values(): self.add_entry(UnitEntry(instances)) def hide_selected_units_widget(self): for entry in self.entries: entry.remove() for i in xrange(0, self.max_row_entry_number): self.widget.findChild(name="hbox_%s" % i).removeAllChildren() def schedule_unit_widget_refresh(self): if not self._scheduled_refresh: self._scheduled_refresh = True Scheduler().add_new_object(self.refresh_unit_widget, self, run_in=0) def refresh_unit_widget(self): if self.instances: self._scheduled_refresh = False self.hide_selected_units_widget() self.draw_selected_units_widget() self.toggle_stance() self.widget.adaptLayout() else: # all units were destroyed self.hide_selected_units_widget() def on_instance_removed(self, instance): if hasattr(instance, 'stance'): self.stance_unit_number -= 1 self.instances.remove(instance) instance.discard_remove_listener( Callback(self.on_instance_removed, instance)) if self.widget.isVisible(): if len(self.instances) < 2: # hide the multi-selection tab instance.session.ingame_gui.hide_menu() # if one unit remains, show its menu if len(self.instances) == 1: self.instances[0].get_component( SelectableComponent).show_menu() return self.type_number[instance.id] -= 1 if self.type_number[instance.id] == 0: del self.type_number[instance.id] # if one type of units dies, schedule refresh self.schedule_unit_widget_refresh() # if one type of units is left, any removal would mean refresh if len(self.type_number) == 1: self.schedule_unit_widget_refresh() if self.stance_unit_number == 0: self.hide_stance_widget() def show_stance_widget(self): stance_widget = load_uh_widget('stancewidget.xml') self.widget.findChild(name='stance').addChild(stance_widget) self.toggle_stance() events = dict( (i.NAME, Callback(self.set_stance, i)) for i in DEFAULT_STANCES) self.widget.mapEvents(events) def hide_stance_widget(self): Scheduler().rem_all_classinst_calls(self) self.widget.findChild(name='stance').removeAllChildren() def set_stance(self, stance): for i in self.instances: if hasattr(i, 'stance'): SetStance(i, stance).execute(i.session) self.toggle_stance() def toggle_stance(self): """ Toggles the stance. Assumes at least one stance unit is selected. """ for stance in DEFAULT_STANCES: self.widget.findChild(name=stance.NAME).set_inactive() # get first unit stance stance_units = [u for u in self.instances if hasattr(u, "stance")] stance = stance_units[0].stance for unit in stance_units[1:]: if unit.stance != stance: # not all have the same stance, toggle none return self.widget.findChild(name=stance.NAME).set_active()
class BoatbuilderTab(_BoatbuilderOverviewTab): widget = 'boatbuilder.xml' helptext = _lazy("Boat builder overview") SHIP_THUMBNAIL = "content/gui/icons/thumbnails/{type_id}.png" SHIP_PREVIEW_IMG = "content/gui/images/objects/ships/116/{type_id}.png" def show(self): super(BoatbuilderTab, self).show() Scheduler().add_new_object(Callback(self.refresh), self, run_in=GAME_SPEED.TICKS_PER_SECOND, loops=-1) def hide(self): super(BoatbuilderTab, self).hide() Scheduler().rem_all_classinst_calls(self) def refresh(self): """This function is called by the TabWidget to redraw the widget.""" super(BoatbuilderTab, self).refresh() main_container = self.widget.findChild(name="BB_main_tab") container_active = main_container.findChild(name="container_active") container_inactive = main_container.findChild( name="container_inactive") progress_container = main_container.findChild( name="BB_progress_container") cancel_container = main_container.findChild(name="BB_cancel_container") needed_res_container = self.widget.findChild( name="BB_needed_resources_container") # a boatbuilder is considered active here if it builds sth, no matter if it's paused production_lines = self.producer.get_production_lines() if production_lines: cancel_container.parent.showChild(cancel_container) # Set progress progress_container.parent.showChild(progress_container) progress = math.floor(self.producer.get_production_progress() * 100) self.widget.findChild(name='progress').progress = progress progress_perc = self.widget.findChild(name='BB_progress_perc') progress_perc.text = u'{progress}%'.format(progress=progress) container_active.parent.showChild(container_active) if (Fife.getVersion() >= (0, 4, 0)): container_inactive.parent.hideChild(container_inactive) else: if not container_inactive in container_inactive.parent.hidden_children: container_inactive.parent.hideChild(container_inactive) # Update boatbuilder queue queue = self.producer.get_unit_production_queue() queue_container = container_active.findChild( name="queue_container") queue_container.removeAllChildren() for place_in_queue, unit_type in enumerate(queue): image = self.__class__.SHIP_THUMBNAIL.format(type_id=unit_type) helptext = _("{ship} (place in queue: {place})").format( ship=self.instance.session.db.get_unit_type_name( unit_type), place=place_in_queue + 1) # people don't count properly, always starting at 1.. icon_name = "queue_elem_" + str(place_in_queue) icon = Icon(name=icon_name, image=image, helptext=helptext) rm_from_queue_cb = Callback( RemoveFromQueue(self.producer, place_in_queue).execute, self.instance.session) icon.capture(rm_from_queue_cb, event_name="mouseClicked") queue_container.addChild(icon) # Set built ship info production_line = self.producer._get_production( production_lines[0]) produced_unit_id = production_line.get_produced_units().keys()[0] name = self.instance.session.db.get_unit_type_name( produced_unit_id) container_active.findChild( name="headline_BB_builtship_label").text = _(name) ship_icon = container_active.findChild(name="BB_cur_ship_icon") ship_icon.helptext = self.instance.session.db.get_ship_tooltip( produced_unit_id) ship_icon.image = self.__class__.SHIP_PREVIEW_IMG.format( type_id=produced_unit_id) button_active = container_active.findChild( name="toggle_active_active") button_inactive = container_active.findChild( name="toggle_active_inactive") to_active = not self.producer.is_active() if not to_active: # swap what we want to show and hide button_active, button_inactive = button_inactive, button_active if (Fife.getVersion() >= (0, 4, 0)): button_active.parent.hideChild(button_active) else: if not button_active in button_active.parent.hidden_children: button_active.parent.hideChild(button_active) button_inactive.parent.showChild(button_inactive) set_active_cb = Callback(self.producer.set_active, active=to_active) button_inactive.capture(set_active_cb, event_name="mouseClicked") upgrades_box = container_active.findChild(name="BB_upgrades_box") upgrades_box.removeAllChildren() # Update needed resources production = self.producer.get_productions()[0] needed_res = production.get_consumed_resources() # Now sort! -amount is the positive value, drop unnecessary res (amount 0) needed_res = dict((res, -amount) for res, amount in needed_res.iteritems() if amount < 0) needed_res = sorted(needed_res.iteritems(), key=itemgetter(1), reverse=True) needed_res_container.removeAllChildren() for i, (res, amount) in enumerate(needed_res): icon = create_resource_icon(res, self.instance.session.db) icon.max_size = icon.min_size = icon.size = (16, 16) label = Label(name="needed_res_lbl_%s" % i) label.text = u'{amount}t'.format(amount=amount) new_hbox = HBox(name="needed_res_box_%s" % i) new_hbox.addChildren(icon, label) needed_res_container.addChild(new_hbox) cancel_button = self.widget.findChild(name="BB_cancel_button") cancel_cb = Callback( CancelCurrentProduction(self.producer).execute, self.instance.session) cancel_button.capture(cancel_cb, event_name="mouseClicked") else: # display sth when nothing is produced container_inactive.parent.showChild(container_inactive) for w in (container_active, progress_container, cancel_container): if (Fife.getVersion() >= (0, 4, 0)): w.parent.hideChild(w) else: if not w in w.parent.hidden_children: w.parent.hideChild(w) self.widget.adaptLayout()
class GroundUnitOverviewTab(OverviewTab): widget = 'overview_war_groundunit.xml' helptext = _lazy("Groundunit overview") def init_widget(self): super(GroundUnitOverviewTab, self).init_widget()
class ProductionOverviewTab(OverviewTab): widget = 'overview_productionbuilding.xml' helptext = _lazy("Production overview") production_line_gui_xml = 'overview_productionline.xml' ACTIVE_PRODUCTION_ANIM_DIR = "content/gui/images/animations/cogs/large" BUTTON_BACKGROUND = "content/gui/images/buttons/msg_button.png" ARROW_TOP = "content/gui/icons/templates/production/production_arrow_top.png" ARROW_MID = "content/gui/icons/templates/production/production_arrow_start.png" ARROW_BOTTOM = "content/gui/icons/templates/production/production_arrow_bottom.png" ARROW_CONNECT_UP = "content/gui/icons/templates/production/production_arrow_connect_up.png" ARROW_CONNECT_DOWN = "content/gui/icons/templates/production/production_arrow_connect_down.png" def __init__(self, instance): self._animations = [] super(ProductionOverviewTab, self).__init__(instance=instance) def get_displayed_productions(self): """List all possible productions of a buildings sorted by production line id. Overwritten in some child classes (e.g. farm tab). """ productions = self.instance.get_component(Producer).get_productions() return sorted(productions, key=operator.methodcaller('get_production_line_id')) def refresh(self): """This function is called by the TabWidget to redraw the widget.""" self._refresh_utilization() # remove old production line data parent_container = self.widget.child_finder('production_lines') while parent_container.children: child = parent_container.children[-1] if hasattr(child, "anim"): child.anim.stop() del child.anim parent_container.removeChild(child) # create a container for each production # sort by production line id to have a consistent (basically arbitrary) order for production in self.get_displayed_productions(): # we need to be notified of small production changes # that aren't passed through the instance production.add_change_listener(self._schedule_refresh, no_duplicates=True) gui = load_uh_widget(self.production_line_gui_xml) # fill in values to gui reflecting the current game state container = gui.findChild(name="production_line_container") self._set_resource_amounts(container, production) centered_container = container.findChild( name='centered_production_icons') self._connect_input_res(centered_container, container, production) if production.is_paused(): centered_container.removeChild( centered_container.findChild(name="toggle_active_active")) toggle_icon = centered_container.findChild( name="toggle_active_inactive") toggle_icon.name = "toggle_active" else: centered_container.removeChild( centered_container.findChild( name="toggle_active_inactive")) toggle_icon = centered_container.findChild( name="toggle_active_active") toggle_icon.name = "toggle_active" if production.get_state() == PRODUCTION.STATES.producing: bg = Icon(image=self.__class__.BUTTON_BACKGROUND) bg.position = toggle_icon.position centered_container.addChild(bg) centered_container.removeChild( toggle_icon) # fix z-ordering centered_container.addChild(toggle_icon) anim = PychanAnimation( toggle_icon, self.__class__.ACTIVE_PRODUCTION_ANIM_DIR) centered_container.anim = anim anim.start(1.0 / 12, -1) # always start anew, people won't notice self._animations.append(weakref.ref(anim)) # fill it with input and output resources in_res_container = container.findChild(name="input_res") self._add_resource_icons(in_res_container, production.get_consumed_resources(), marker=True) out_res_container = container.findChild(name="output_res") self._add_resource_icons(out_res_container, production.get_produced_resources()) # active toggle_active button toggle_active = ToggleActive(self.instance.get_component(Producer), production) centered_container.mapEvents({ 'toggle_active': Callback(toggle_active.execute, self.instance.session) }) # NOTE: this command causes a refresh, so we needn't change the toggle_active-button-image parent_container.addChild(container) super(ProductionOverviewTab, self).refresh() def _connect_input_res(self, centered_container, container, production): """Draws incoming arrows for production line container.""" input_amount = len(production.get_consumed_resources()) if input_amount == 0: # Do not draw input arrows if there is no input return # center the production line icon_height = ImageFillStatusButton.CELL_SIZE[ 1] + ImageFillStatusButton.PADDING center_y = (icon_height // 2) * (input_amount - 1) centered_container.position = (0, center_y) if input_amount % 2: # Add center arrow for 1, 3, 5, ... but not 2, 4, ... mid_arrow = Icon(image=self.__class__.ARROW_MID) mid_arrow.position = (58, 17 + center_y) container.insertChild(mid_arrow, 0) for res in xrange(input_amount // 2): # --\ <= placed for res = 1 # --\| <= place connector <= placed for res = 0 # ---O--> <= placed above (mid_arrow) # --/| <= place connector <= placed for res = 0 # --/ <= placed for res = 1 offset = -17 + (icon_height // 2) * (2 * res + (input_amount % 2) + 1) top_arrow = Icon(image=self.__class__.ARROW_TOP) top_arrow.position = (58, center_y - offset) container.insertChild(top_arrow, 0) bottom_arrow = Icon(image=self.__class__.ARROW_BOTTOM) bottom_arrow.position = (58, center_y + offset) container.insertChild(bottom_arrow, 0) # Place a connector image (the | in above sketch) that vertically connects # the input resource arrows. We need those if the production line has more # than three input resources. Connectors are placed in the inner loop parts. place_connectors = (1 + 2 * res) < (input_amount // 2) if place_connectors: # the connector downwards connects top_arrows down_connector = Icon(image=self.__class__.ARROW_CONNECT_DOWN) down_connector.position = (98, center_y - offset) container.insertChild(down_connector, 0) # the connector upwards connects up_arrows up_connector = Icon(image=self.__class__.ARROW_CONNECT_UP) up_connector.position = (98, center_y + offset) container.insertChild(up_connector, 0) def _set_resource_amounts(self, container, production): for res, amount in production.get_consumed_resources().iteritems(): # consumed resources are negative! label = Label(text=unicode(-amount), margins=(0, 15)) container.findChild(name='input_box').addChild(label) for res, amount in production.get_produced_resources().iteritems(): label = Label(text=unicode(amount).rjust(2), margins=(0, 15)) container.findChild(name='output_box').addChild(label) def destruct_building(self): self.instance.session.ingame_gui.hide_menu() Tear(self.instance).execute(self.instance.session) def _refresh_utilization(self): utilization = 0 if self.instance.has_component(Producer): utilization = int( round( self.instance.get_component(Producer).capacity_utilization * 100)) self.widget.child_finder( 'capacity_utilization').text = unicode(utilization) + u'%' def _add_resource_icons(self, container, resources, marker=False): calculate_position = lambda amount: (amount * 100 ) // inventory.get_limit(res) for res in resources: inventory = self.instance.get_component(StorageComponent).inventory filled = calculate_position(inventory[res]) marker_level = calculate_position(-resources[res]) if marker else 0 image_button = ImageFillStatusButton.init_for_res( self.instance.session.db, res, inventory[res], filled, marker=marker_level, use_inactive_icon=False, uncached=True) container.addChild(image_button) def show(self): super(ProductionOverviewTab, self).show() Scheduler().add_new_object(Callback(self._refresh_utilization), self, run_in=GAME_SPEED.TICKS_PER_SECOND, loops=-1) def hide(self): super(ProductionOverviewTab, self).hide() self._cleanup() def on_instance_removed(self): self._cleanup() super(ProductionOverviewTab, self).on_instance_removed() def _cleanup(self): Scheduler().rem_all_classinst_calls(self) for production in self.get_displayed_productions(): production.discard_change_listener(self._schedule_refresh) for anim in self._animations: if anim(): anim().stop() self._animations = []
class SettlerOverviewTab(OverviewTab): widget = 'overview_settler.xml' helptext = _lazy("Settler overview") def init_widget(self): super(SettlerOverviewTab, self).init_widget() name = self.instance.settlement.get_component(NamedComponent).name self.widget.findChild(name="headline").text = name setup_tax_slider(self.widget.child_finder('tax_slider'), self.widget.child_finder('tax_val_label'), self.instance.settlement, self.instance.level) taxes = self.instance.settlement.tax_settings[self.instance.level] self.widget.child_finder('tax_val_label').text = unicode(taxes) action_set = ActionSetLoader.get_sets()[self.instance._action_set_id] action_gfx = action_set.items()[0][1] image = action_gfx[45].keys()[0] self.widget.findChild(name="building_image").image = image def on_settler_level_change(self, message): assert isinstance(message, SettlerUpdate) setup_tax_slider(self.widget.child_finder('tax_slider'), self.widget.child_finder('tax_val_label'), self.instance.settlement, message.level) taxes = self.instance.settlement.tax_settings[self.instance.level] self.widget.child_finder('tax_val_label').text = unicode(taxes) imgs = ActionSetLoader.get_sets()[ self.instance._action_set_id].items()[0][1] self.widget.findChild(name="building_image").image = imgs[45].keys()[0] def show(self): super(SettlerOverviewTab, self).show() SettlerUpdate.subscribe(self.on_settler_level_change, sender=self.instance) def hide(self): SettlerUpdate.discard(self.on_settler_level_change, sender=self.instance) super(SettlerOverviewTab, self).hide() def refresh(self): image, helptext = get_happiness_icon_and_helptext( self.instance.happiness, self.instance.session) self.widget.child_finder('happiness_label').image = image self.widget.child_finder('happiness_label').helptext = helptext self.widget.child_finder( 'happiness').progress = self.instance.happiness self.widget.child_finder('inhabitants').text = u"%s/%s" % ( self.instance.inhabitants, self.instance.inhabitants_max) self.widget.child_finder('taxes').text = unicode( self.instance.last_tax_payed) self.update_consumed_res() name = self.instance.settlement.get_component(NamedComponent).name self.widget.findChild(name="headline").text = name events = { 'headline': Callback(self.instance.session.ingame_gui.show_change_name_dialog, self.instance.settlement) } self.widget.mapEvents(events) super(SettlerOverviewTab, self).refresh() def update_consumed_res(self): """Updates the container that displays the needed resources of the settler""" container = self.widget.findChild(name="needed_res") # remove icons from the container container.removeAllChildren() # create new ones resources = self.instance.get_currently_not_consumed_resources() for res in resources: icon = create_resource_icon(res, self.instance.session.db) icon.max_size = icon.min_size = icon.size = (32, 32) container.addChild(icon) container.adaptLayout()
class FireStatusIcon(StatusIcon): """ Fire disaster """ priority = 3000 icon = 'as_on_fire+idle+45' helptext = _lazy("This building is on fire!")
class SettlerNotConnectedStatus(StatusIcon): # threshold is the inhabitants decrease level priority = 1700 icon = 'as_mainsquare_access+idle+45' helptext = _lazy("These residents don't have access to a main square.")
class OverviewTab(TabInterface): widget = 'overviewtab.xml' icon_path = 'icons/tabwidget/common/building_overview' helptext = _lazy("Overview") has_stance = False def __init__(self, instance, widget=None, icon_path=None): self.instance = instance super(OverviewTab, self).__init__(widget=widget, icon_path=icon_path) def init_widget(self): # set player emblem if self.widget.child_finder('player_emblem'): if self.instance.owner is not None: player_color = self.instance.owner.color.name else: player_color = 'no_player' emblem = 'content/gui/images/tabwidget/emblems/emblem_%s.png' self.widget.child_finder( 'player_emblem').image = emblem % player_color if self.__class__.has_stance: self.init_stance_widget() def refresh(self): if (hasattr(self.instance, 'name') or self.instance.has_component(NamedComponent) ) and self.widget.child_finder('name'): name_widget = self.widget.child_finder('name') # Named objects can't be translated. if self.instance.has_component(NamedComponent): name_widget.text = self.instance.get_component( NamedComponent).name else: name_widget.text = _(self.instance.name) if hasattr(self.instance, 'running_costs') and \ self.widget.child_finder('running_costs'): self.widget.child_finder('running_costs').text = \ unicode(self.instance.running_costs) self.widget.adaptLayout() def show(self): super(OverviewTab, self).show() if not self.instance.has_change_listener(self.refresh): self.instance.add_change_listener(self.refresh) if not self.instance.has_remove_listener(self.on_instance_removed): self.instance.add_remove_listener(self.on_instance_removed) if hasattr(self.instance, 'settlement') and \ self.instance.settlement is not None and \ not self.instance.settlement.has_change_listener(self._schedule_refresh): # listen for settlement name changes displayed as tab headlines self.instance.settlement.add_change_listener( self._schedule_refresh) def hide(self): super(OverviewTab, self).hide() if self.instance is not None: self.instance.discard_change_listener(self.refresh) self.instance.discard_remove_listener(self.on_instance_removed) if hasattr(self.instance, 'settlement') and self.instance.settlement is not None: self.instance.settlement.discard_change_listener( self._schedule_refresh) def on_instance_removed(self): self.on_remove() self.instance = None def init_stance_widget(self): """Call this for tabs with stances.""" stance_widget = self.widget.findChild(name='stance') stance_widget.init(self.instance) self.add_remove_listener(stance_widget.remove)
class SettlerUnhappyStatus(StatusIcon): # threshold is the inhabitants decrease level priority = 1700 icon = 'as_attention_please+idle+45' helptext = _lazy("These residents are unhappy.")