def add_slots(self, num): """Adds num amount of slots to the buysellmenu. @param num: amount of slots that are to be added.""" content = self.widget.findChild(name="content") assert(content is not None) for num in range(0, num): slot = load_xml_translated('trade_single_slot.xml') self.slots[num] = slot slot.id = num slot.action = 'buy' slot.res = None slot.findChild(name='button').capture(Callback(self.show_resource_menu, num)) slot.findChild(name='button').up_image = self.dummy_icon_path slot.findChild(name='button').down_image = self.dummy_icon_path slot.findChild(name='button').hover_image = self.dummy_icon_path slot.findChild(name='amount').stylize('menu_black') slider = slot.findChild(name="slider") slider.setScaleStart(0.0) slider.setScaleEnd(float(self.settlement.inventory.limit))# Set scale according to the settlements inventory size slot.findChild(name="buysell").capture(Callback(self.toggle_buysell, num)) fillbar = slot.findChild(name="fillbar") # hide fillbar by setting position icon = slot.findChild(name="icon") fillbar.position = (icon.width - fillbar.width - 1, icon.height) content.addChild(slot) self.widget.adaptLayout()
def preload_game_data(lock): """Preloads game data. Keeps releasing and acquiring lock, runs until lock can't be acquired.""" try: import logging from horizons.entities import Entities from horizons.util import Callback log = logging.getLogger("preload") mydb = _create_db( ) # create own db reader instance, since it's not thread-safe preload_functions = [ ActionSetLoader.load, \ TileSetLoader.load, Callback(Entities.load_grounds, mydb), \ Callback(Entities.load_buildings, mydb), \ Callback(Entities.load_units, mydb) ] for f in preload_functions: if not lock.acquire(False): break log.debug("Preload: %s", f) f() log.debug("Preload: %s is done", f) lock.release() log.debug("Preloading done.") except Exception, e: log.warning("Exception occured in preloading thread: %s", e)
def draw_widget(self): """Updates the widget.""" button_space = self.widget.findChild(name="button_space") button_space.removeAllChildren() # Remove old buttons for index, message in enumerate(self.active_messages): if (self.position + index) < len(self.active_messages): button = pychan.widgets.ImageButton() button.name = str(index) button.up_image = message.up_image button.hover_image = message.hover_image button.down_image = message.down_image # show text on hover events = { button.name + "/mouseEntered": Callback(self.show_text, index), button.name + "/mouseExited": self.hide_text } if message.x is not None and message.y is not None: # center source of event on click, if there is a source events[button.name] = Callback( \ self.session.view.center, message.x, message.y) button.mapEvents(events) button_space.addChild(button) button_space.resizeToContent() self.widget.size = button_space.size
def draw_widget(self): self.partners = self.find_partner() if len(self.partners) > 0: dropdown = self.widget.findChild(name='partners') #dropdown.setInitialData([item.settlement.name for item in self.partners]) #dropdown.capture(Callback(self.set_partner, dropdown.getData())) nearest_partner = self.get_nearest_partner(self.partners) #dropdown.setData(nearest_partner) dropdown.text = unicode(self.partners[nearest_partner].settlement. name) # label fix for release use only old_partner = self.partner self.partner = self.partners[nearest_partner] # If we changed partners, update changelisteners if old_partner and old_partner is not self.partner: old_partner.inventory.remove_change_listener(self.draw_widget) self.partner.inventory.add_change_listener(self.draw_widget) inv_partner = self.widget.findChild(name='inventory_partner') inv_partner.init(self.instance.session.db, self.partner.inventory) for button in self.get_widgets_by_class(inv_partner, ImageFillStatusButton): button.button.capture( Callback(self.transfer, button.res_id, self.partner, self.instance)) inv = self.widget.findChild(name='inventory_ship') inv.init(self.instance.session.db, self.instance.inventory) for button in self.get_widgets_by_class(inv, ImageFillStatusButton): button.button.capture( Callback(self.transfer, button.res_id, self.instance, self.partner)) self.widget.adaptLayout() else: self.widget.hide()
def refresh(self): # show rename when you click on name events = { 'name': Callback(self.instance.session.ingame_gui.show_change_name_dialog, self.instance) } # check if an island is in range and it doesn't contain a player's settlement island_without_player_settlement_found = False for island in self.instance.session.world.get_islands_in_radius(self.instance.position, \ self.instance.radius): player_settlements = [ settlement for settlement in island.settlements if \ settlement.owner is self.instance.session.world.player ] if len(player_settlements) == 0: island_without_player_settlement_found = True if island_without_player_settlement_found: events['foundSettlement'] = Callback(self.instance.session.ingame_gui._build, \ BUILDINGS.BRANCH_OFFICE_CLASS, \ weakref.ref(self.instance) ) self.widget.child_finder('bg_button').set_active() self.widget.child_finder('foundSettlement').set_active() else: events['foundSettlement'] = None self.widget.child_finder('bg_button').set_inactive() self.widget.child_finder('foundSettlement').set_inactive() self.widget.mapEvents(events) super(ShipOverviewTab, self).refresh()
def ship_idle(self, ship): """Called if a ship is idle. Sends ship to a random place or branch office (which target to use is decided by chance, probability for branch office (BUSINESS_S.) is 2/3 by default) @param ship: ship instance""" if self.session.random.randint(0, 100) < TRADER.BUSINESS_SENSE: # delay one tick, to allow old movement calls to completely finish self.log.debug("Trader %s ship %s: idle, moving to random bo", self.worldid, ship.worldid) Scheduler().add_new_object(Callback(self.send_ship_random_branch, ship), self, run_in=0) else: self.log.debug("Trader %s ship %s: idle, moving to random location", self.worldid, ship.worldid) Scheduler().add_new_object(Callback(self.send_ship_random, ship), self, run_in=0)
def _init_gui(self): """Initial init of gui.""" self._gui = load_xml_translated("choose_next_scenario.xml") self._gui.mapEvents({ 'choose_scenario': Callback(self.validate_choice), 'cancelButton': Callback(self.hide), }) self._gui.position_technique = "automatic" # "center:center" #SavegameManager.get_campaign_info(cls, name = "", file = "") self.choose_scenario = self._gui.findChild(name="choose_scenario")
def __init__(self, session, id, name, color, **kwargs): super(Pirate, self).__init__(session, id, name, color, **kwargs) # create a ship and place it randomly (temporary hack) point = self.session.world.get_random_possible_ship_position() ship = CreateUnit(self.worldid, UNITS.PIRATE_SHIP_CLASS, point.x, point.y)(issuer=self.session.world.player) self.ships[ship] = self.shipStates.idle for ship in self.ships.keys(): Scheduler().add_new_object(Callback(self.send_ship, ship), self) Scheduler().add_new_object(Callback(self.lookout, ship), self, 8, -1)
def _init_gui(self): """Initial init of gui.""" self._gui = load_xml_translated("captains_log.xml") self._gui.mapEvents({ 'backwardButton' : Callback(self._scroll, -2), 'forwardButton' : Callback(self._scroll, 2), 'cancelButton' : self.hide }) self._gui.position_technique = "automatic" # "center:center" self.backward_button = self._gui.findChild(name="backwardButton") self.forward_button = self._gui.findChild(name="forwardButton")
def _init_gui(self): """Initial init of gui.""" self._gui = load_xml_translated("captains_log.xml") self._gui.mapEvents({ 'backwardButton': Callback(self._scroll, -2), 'forwardButton': Callback(self._scroll, 2), 'cancelButton': self.hide }) center_widget(self._gui) self.backward_button = self._gui.findChild(name="backwardButton") self.forward_button = self._gui.findChild(name="forwardButton")
def load_ship_states(self, db): # load ships one by one from db (ship instances themselves are loaded already, but # we have to use them here) for ship_id, state_id, remaining_ticks in \ db("SELECT rowid, state, remaining_ticks FROM pirate_ships"): state = self.shipStates[state_id] ship = WorldObject.get_object_by_id(ship_id) self.ships[ship] = state assert remaining_ticks is not None Scheduler().add_new_object(Callback(self.lookout, ship), self, remaining_ticks, -1, 8) ship.add_move_callback(Callback(self.ship_idle, ship)) self.calculate_visibility_points(ship)
def show_resource_menu(self, slot_id): self.resources = load_xml_translated('select_trade_resource.xml') self.resources.position = self.widget.position button_width = 50 vbox = self.resources.findChild(name="resources") current_hbox = pychan.widgets.HBox(padding = 2) index = 1 resources = horizons.main.db.get_res_id_and_icon(True) # Add the zero element to the beginning that allows to remove the currently sold/bought resource for (res_id, icon) in [(0, self.dummy_icon_path)] + list(resources): if res_id in self.settlement.buy_list or res_id in self.settlement.sell_list: continue # don't show resources that are already in the list button = TooltipButton(size=(50, 50)) button.up_image, button.down_image, button.hover_image = icon, icon, icon if res_id == 0: button.tooltip = u"" else: button.tooltip = horizons.main.db.get_res_name(res_id) button.capture(Callback(self.add_resource, res_id, slot_id)) current_hbox.addChild(button) if index % (vbox.width/(button_width)) == 0 and index is not 0: vbox.addChild(current_hbox) current_hbox = pychan.widgets.HBox(padding=0) index += 1 vbox.addChild(current_hbox) vbox.adaptLayout() self.resources.addChild(vbox) self.resources.stylize('headline') self.hide() self.resources.show() self.settlement.session.ingame_gui.minimap_to_front()
def add_resource(self, res_id, slot_id, value=None, dont_use_commands=False): """Adds a resource to the specified slot @param res_id: int - resource id @param slot: int - slot number of the slot that is to be set""" self.log.debug("BuySellTab add_resource() resid: %s; slot_id %s; value: %s", \ res_id, slot_id, value) if self.resources is not None: # Hide resource menu self.resources.hide() self.show() slot = self.slots[slot_id] slider = slot.findChild(name="slider") if value is None: value = int(slider.getValue()) # If no value is provided, take current slider value else: slider.setValue(float(value)) # set slider correctly if slot.action is "sell": if slot.res is not None: # slot has been in use before, delete old value if dont_use_commands: # dont_use_commands is true if called by __init__ self.settlement.remove_from_sell_list(slot.res) else: RemoveFromSellList(self.settlement, slot.res).execute(self.settlement.session) if res_id != 0: self.add_sell_to_settlement(res_id, value, slot.id, dont_use_commands) else: if slot.action is "buy" and slot.res is not None: if dont_use_commands: # dont_use_commands is true if called by __init__ self.settlement.remove_from_buy_list(slot.res) else: RemoveFromBuyList(self.settlement, slot.res).execute(self.settlement.session) if res_id != 0: self.add_buy_to_settlement(res_id, value, slot.id, dont_use_commands) button = slot.findChild(name="button") fillbar = slot.findChild(name="fillbar") if res_id == 0: button.up_image, button.down_image, button.hover_image = [ self.dummy_icon_path ] * 3 button.tooltip = u"" slot.findChild(name="amount").text = u"" slot.res = None slider.capture(None) # hide fillbar by setting position icon = slot.findChild(name="icon") fillbar.position = (icon.width - fillbar.width - 1, icon.height) else: icons = horizons.main.db.get_res_icon(res_id) button.up_image = icons[0] button.down_image = icons[0] button.hover_image = icons[1] # disabled icon button.tooltip = horizons.main.db.get_res_name(res_id) slot.res = res_id # use some python magic to assign a res attribute to the slot to save which res_id he stores slider.capture(Callback(self.slider_adjust, res_id, slot.id)) slot.findChild(name="amount").text = unicode(value)+"t" icon = slot.findChild(name="icon") inventory = self.settlement.inventory filled = float(inventory[res_id]) / inventory.get_limit(res_id) fillbar.position = (icon.width - fillbar.width - 1, icon.height - int(icon.height*filled)) slot.adaptLayout()
def _draw(self): """Draws the icon + bar.""" # hash buttons by creation function call # NOTE: there may be problems with multiple buttons with the same # images and tooltip at the same time create_btn = Callback(TooltipButton, up_image=self.up_image, down_image=self.down_image, hover_image=self.hover_image, tooltip=self.tooltip) self.button = None if self.uncached: self.button = create_btn() else: self.button = self.__widget_cache.get(create_btn, None) if self.button is None: # create button self.__widget_cache[create_btn] = self.button = create_btn() else: # disconnect button from earlier layout if self.button.parent: self.button.parent.removeChild(self.button) # can't cache the other instances, because we need multiple instances # with the same data active at the same time self.label = pychan.widgets.Label(text=self.text) self.label.position = self.text_position self.fill_bar = pychan.widgets.Icon( "content/gui/images/tabwidget/green_line.png") self.fill_bar.position = (self.button.width-self.fill_bar.width-1, \ self.button.height-int(self.button.height/100.0*self.filled)) self.addChildren(self.button, self.fill_bar, self.label)
def _chase_closest_ship(self, pirate_ship): ship = self.get_nearest_player_ship(pirate_ship) if ship: # The player is in sight here # Note down all the point's visibility the player belongs to for point in self.visibility_points: if ship.position.distance_to_point( point[0]) <= self.sight_radius: point[ 1] += 1 # point[1] represents the chance of seeing a player if ship.position.distance_to_point( pirate_ship.position) <= self.caught_ship_radius: self.ships[pirate_ship] = self.shipStates.chasing_ship return False # already caught it # move ship there: for point in self.session.world.get_points_in_radius( ship.position, self.caught_ship_radius - 1): try: pirate_ship.move(point, Callback(self.send_ship, pirate_ship)) except MoveNotPossible: continue self.log.debug( 'Pirate %s: chasing %s (next point %d, %d)' % (self.worldid, pirate_ship.name, point.x, point.y)) self.ships[pirate_ship] = self.shipStates.chasing_ship return True return False
def save(self, db): super(Trader, self).save(db) # mark self as a trader db("UPDATE player SET is_trader = 1 WHERE rowid = ?", self.worldid) for ship in self.ships: # prepare values ship_state = self.ships[ship] remaining_ticks = None # get current callback in scheduler, according to ship state, to retrieve # the number of ticks, when the call will actually be done current_callback = None if ship_state == self.shipStates.reached_branch: current_callback = Callback(self.ship_idle, ship) if current_callback is not None: # current state has a callback calls = Scheduler().get_classinst_calls(self, current_callback) assert len(calls) == 1, "got %s calls for saving %s: %s" %(len(calls), current_callback, calls) remaining_ticks = max(calls.values()[0], 1) targeted_branch = None if ship.worldid not in self.office else self.office[ship.worldid].worldid # put them in the database db("INSERT INTO trader_ships(rowid, state, remaining_ticks, targeted_branch) \ VALUES(?, ?, ?, ?)", ship.worldid, ship_state.index, remaining_ticks, targeted_branch)
def schedule_action(self, action): if self.sleep_ticks_remaining > 0: Scheduler().add_new_object(Callback(action, self.session), self, run_in=self.sleep_ticks_remaining) else: action(self.session)
def setup_setting_extras(self): slider_initial_data = {} slider_event_map = {} self.OptionsDlg = self._setting.loadSettingsDialog() self.OptionsDlg.position_technique = "automatic" # "center:center" slider_dict = { 'AutosaveInterval': 'autosaveinterval', 'AutosaveMaxCount': 'autosavemaxcount', 'QuicksaveMaxCount': 'quicksavemaxcount' } for x in slider_dict.keys(): slider_initial_data[slider_dict[x] + '_value'] = unicode( int(self._setting.get(UH_MODULE, x))) slider_initial_data['volume_music_value'] = unicode( int(self._setting.get(UH_MODULE, "VolumeMusic") * 500)) + '%' slider_initial_data['volume_effects_value'] = unicode( int(self._setting.get(UH_MODULE, "VolumeEffects") * 200)) + '%' self.OptionsDlg.distributeInitialData(slider_initial_data) for x in slider_dict.values(): slider_event_map[x] = Callback(self.update_slider_values, x) slider_event_map['volume_music'] = self.set_volume_music slider_event_map['volume_effects'] = self.set_volume_effects self.OptionsDlg.mapEvents(slider_event_map)
def reached_branch(self, ship): """Actions that need to be taken when reaching a branch office @param ship: ship instance""" self.log.debug("Trader %s ship %s: reached bo", self.worldid, ship.worldid) settlement = self.office[ship.worldid].settlement # NOTE: must be sorted for mp games (same order everywhere) for res in sorted(settlement.buy_list.iterkeys()): # check for resources that the settlement wants to buy amount = self.session.random.randint(*TRADER.SELL_AMOUNT) # select a random amount to sell if amount == 0: continue price = int(self.session.db.get_res_value(res) * TRADER.PRICE_MODIFIER_SELL * amount) settlement.buy(res, amount, price) # don't care if he bought it. the trader just offers. self.log.debug("Trader %s: offered sell %s tons of res %s", self.worldid, amount, res) # NOTE: must be sorted for mp games (same order everywhere) for res in sorted(settlement.sell_list.iterkeys()): # select a random amount to buy from the settlement amount = self.session.random.randint(*TRADER.BUY_AMOUNT) if amount == 0: continue price = int(self.session.db.get_res_value(res) * TRADER.PRICE_MODIFIER_BUY * amount) settlement.sell(res, amount, price) self.log.debug("Trader %s: offered buy %s tons of res %s", self.worldid, amount, res) del self.office[ship.worldid] # wait 2 seconds before going on to the next island Scheduler().add_new_object(Callback(self.ship_idle, ship), self, \ Scheduler().get_ticks(TRADER.TRADING_DURATION)) self.ships[ship] = self.shipStates.reached_branch
def send_ship_random_branch(self, ship, branch_office=None): """Sends a ship to a random branch office on the map @param ship: Ship instance that is to be used @param branch_office: Branch Office instance to move to. Random one is selected on None.""" self.log.debug("Trader %s ship %s moving to bo (random=%s)", self.worldid, ship.worldid, \ (branch_office is None)) # maybe this kind of list should be saved somewhere, as this is pretty performance intense branchoffices = self.session.world.get_branch_offices() if len(branchoffices) == 0: # there aren't any branch offices, so move randomly self.send_ship_random(ship) else: # select a branch office if branch_office is None: rand = self.session.random.randint(0, len(branchoffices) - 1) self.office[ship.worldid] = branchoffices[rand] else: self.office[ship.worldid] = branch_office found_path_to_bo = False # try to find a possible position near the bo for point in Circle(self.office[ship.worldid].position.center(), ship.radius): try: ship.move(point, Callback(self.reached_branch, ship)) except MoveNotPossible: continue found_path_to_bo = True self.ships[ship] = self.shipStates.moving_to_branch break if not found_path_to_bo: self.send_ship_random(ship)
def show_resource_menu(self, slot_id): self.resources = load_xml_translated('select_trade_resource.xml') self.resources.position = self.widget.position button_width = 50 vbox = self.resources.findChild(name="resources") amount_per_line = vbox.width / button_width current_hbox = pychan.widgets.HBox(name="hbox_0") index = 1 resources = horizons.main.db.get_res_id_and_icon(True) # Add the zero element to the beginning that allows to remove the currently sold/bought resource for (res_id, icon) in [(0, self.dummy_icon_path)] + list(resources): if res_id in self.settlement.buy_list or res_id in self.settlement.sell_list: continue # don't show resources that are already in the list button = TooltipButton( size=(button_width, button_width), \ name="resource_icon_%02d" % res_id ) button.up_image, button.down_image, button.hover_image = icon, icon, icon if res_id == 0: button.tooltip = u"" else: button.tooltip = horizons.main.db.get_res_name(res_id) button.capture(Callback(self.add_resource, res_id, slot_id)) current_hbox.addChild(button) if index % amount_per_line == 0 and index is not 0: vbox.addChild(current_hbox) current_hbox = pychan.widgets.HBox(name="hbox_%s" % (index / amount_per_line) ) index += 1 # current_hbox.addSpacer(pychan.widgets.layout.Spacer) #TODO: proper alignment vbox.addChild(current_hbox) vbox.adaptLayout() self.hide() # hides tab that invoked the selection widget self.resources.show() # show selection widget, still display old tab icons self.settlement.session.ingame_gui.minimap_to_front()
def schedule_check(self, condition): """Let check_events run in one tick for condition. Useful for lag prevetion if time is a critical factor, e.g. when the user has to wait for a function to return..""" if self.session.world.inited: # don't check while loading Scheduler().add_new_object(Callback(self.check_events, condition), self, run_in=self.sleep_ticks_remaining)
def create_ship(self): """Create a ship and place it randomly""" self.log.debug("Trader %s: creating new ship", self.worldid) point = self.session.world.get_random_possible_ship_position() ship = CreateUnit(self.worldid, UNITS.TRADER_SHIP_CLASS, point.x, point.y)(issuer=self) self.ships[ship] = self.shipStates.reached_branch Scheduler().add_new_object(Callback(self.ship_idle, ship), self, run_in=0)
def refresh(self): """This function is called by the TabWidget to redraw the widget.""" # remove old field data parent_container = self.widget.child_finder('related_fields') while len(parent_container.children) > 0: parent_container.removeChild(parent_container.children[0]) # Load all related Fields of this Farm building_ids = self.instance.session.db.cached_query( "SELECT related_building FROM related_buildings where building = ?", self.instance.id) build_buttons = list() gui = load_xml_translated(self.relatedfields_gui_xml) container = gui.findChild(name="fields_container") for x, building_id in enumerate(sorted(building_ids)): retVal = self._create_build_buttons(building_id[0], container) if retVal != None: build_buttons.append(retVal) if x % 3 == 0: container.stylize('menu_black') parent_container.addChild(container) else: container = gui.findChild(name="fields_container") for name, cls in build_buttons: self.widget.mapEvents({name: Callback(self.buildField, cls)}) super(BuildRelatedTab, self).refresh()
def _init_tabs(self): """Add enough tabbuttons for all widgets.""" # Load buttons for index, tab in enumerate(self._tabs): container = pychan.Container() background = pychan.Icon() background.image = tab.button_background_image button = TooltipButton() button.name = index if self.current_tab is tab: button.up_image = tab.button_active_image else: button.up_image = tab.button_up_image button.down_image = tab.button_down_image button.hover_image = tab.button_hover_image button.is_focusable = False button.size = (50, 50) button.capture(Callback(self._show_tab, index)) if hasattr(tab, 'tooltip') and tab.tooltip is not None: button.tooltip = unicode(tab.tooltip) container.size = background.size container.addChild(background) container.addChild(button) self.content.addChild(container) self.widget.size = (50, 200 * len(self._tabs)) self.widget.adaptLayout()
def validate_choice(self, *args, **kwargs): if not self.selected_scenario: return ExtScheduler().add_new_object(Callback(SavegameManager.load_scenario, self.session.campaign, self.selected_scenario), SavegameManager, run_in=1)
def cancel(self, continue_action=None): """Cancels current job and moves back home""" self.log.debug("%s cancel", self) if continue_action is None: continue_action = Callback(self.move_home, callback=self.search_job, action='move') super(BuildingCollector, self).cancel(continue_action=continue_action)
def show_logbook_entry_delayed(session, head, message, delay=MESSAGES.LOGBOOK_DEFAULT_DELAY): """Show a logbook entry delayed by delay seconds""" callback = Callback(show_logbook_entry, session, head, message) Scheduler().add_new_object(callback, session.scenario_eventhandler, run_in=Scheduler().get_ticks(delay))
def load_ship_states(self, db): # load ships one by one from db (ship instances themselves are loaded already, but # we have to use them here) for ship_id, state_id, remaining_ticks, targeted_branch in \ db("SELECT rowid, state, remaining_ticks, targeted_branch FROM trader_ships"): state = self.shipStates[state_id] ship = WorldObject.get_object_by_id(ship_id) self.ships[ship] = state if state == self.shipStates.moving_random: ship.add_move_callback(Callback(self.ship_idle, ship)) elif state == self.shipStates.moving_to_branch: ship.add_move_callback(Callback(self.reached_branch, ship)) assert targeted_branch is not None self.office[ship.worldid] = WorldObject.get_object_by_id(targeted_branch) elif state == self.shipStates.reached_branch: assert remaining_ticks is not None Scheduler().add_new_object( \ Callback(self.ship_idle, ship), self, remaining_ticks)
def show_message(session, *message): """Shows a custom message in the messagewidget. If you pass more than one message, they will be shown after each other after a delay""" delay_ticks = Scheduler().get_ticks(MESSAGES.CUSTOM_MSG_SHOW_DELAY) visible_ticks = Scheduler().get_ticks(MESSAGES.CUSTOM_MSG_VISIBLE_FOR) delay_iter = 1 for msg in message: Scheduler().add_new_object(Callback(session.ingame_gui.message_widget.add_custom, None, None, \ msg + '\n'* 30 + 'UHtutorial', visible_for=visible_ticks), \ None, run_in=delay_iter) delay_iter += delay_ticks