def apply_state(self, state, remaining_ticks=None): """Takes actions to set collector to a state. Useful after loading. @param state: EnumValue from states @param remaining_ticks: ticks after which current state is finished """ if state == self.states.idle: # we do nothing, so schedule a new search for a job Scheduler().add_new_object(self.search_job, self, remaining_ticks) elif state == self.states.moving_to_target: # we are on the way to target, so save the job self.setup_new_job() # and notify us, when we're at target self.add_move_callback(self.begin_working) self.add_blocked_callback(self.handle_path_to_job_blocked) self.show() elif state == self.states.working: # we are at the target and work # register the new job self.setup_new_job() # job finishes in remaining_ticks ticks Scheduler().add_new_object(self.finish_working, self, remaining_ticks)
def test_sell(self): self.inventory.alter(1, 1) self.assertFalse(self.tradepost.sell(1, 1, 1, 100)) self.tradepost.set_slot(0, 1, True, 0) # sell until 0 self.assertTrue(self.tradepost.sell(1, 1, 1, 100)) self.assertEqual(self.tradepost.sell_income, 1) Scheduler().cur_tick += 1 # ran out of res self.assertFalse(self.tradepost.sell(1, 1, 1, 100)) Scheduler().cur_tick += 1 self.inventory.alter(1, 1) self.assertTrue(self.tradepost.sell(1, 1, 1, 100)) self.tradepost.clear_slot(0, True) # not selling any more self.assertFalse(self.tradepost.sell(1, 1, 1, 100)) self.assertEqual(self.tradepost.sell_income, 2) self.assertEqual(self.tradepost.total_earnings, 2)
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 _load(self, db, worldid): super(Production, self).load(db, worldid) db_data = db.get_production_row(worldid) self.__init(WorldObject.get_object_by_id(db_data[1]).inventory, db_data[2], \ PRODUCTION.STATES[db_data[0]], None if db_data[4] is None else PRODUCTION.STATES[db_data[4]]) if self._state == PRODUCTION.STATES.paused: self._pause_remaining_ticks = db_data[3] elif self._state == PRODUCTION.STATES.producing: Scheduler().add_new_object(self._finished_producing, self, db_data[3]) elif self._state == PRODUCTION.STATES.waiting_for_res or \ self._state == PRODUCTION.STATES.inventory_full: self.inventory.add_change_listener(self._check_inventory)
def save(self, db): remaining_ticks = None if self._state == PRODUCTION.STATES.paused: remaining_ticks = self._pause_remaining_ticks elif self._state == PRODUCTION.STATES.producing: remaining_ticks = Scheduler().get_remaining_ticks(self, self._finished_producing) # use a number > 0 for ticks if remaining_ticks < 1: remaining_ticks = 1 db('INSERT INTO production(rowid, state, prod_line_id, remaining_ticks, \ _pause_old_state) VALUES(?, ?, ?, ?, ?)', self.worldid, self._state.index, \ self._prod_line.id, remaining_ticks, \ None if self._pause_old_state is None else self._pause_old_state.index)
def _ship_found_signal_fire(self, ship): signal_fire = self._check_for_signal_fire_in_ship_range(ship) self.log.debug("Trader %s ship %s found signal fire %s", self.worldid, ship.worldid, signal_fire) # search a branch office in the range of the signal fire and move to it branch_offices = self.session.world.get_branch_offices() for bo in branch_offices: if bo.position.distance(signal_fire.position) <= signal_fire.radius and \ bo.owner == signal_fire.owner: self.log.debug("Trader %s moving to bo %s", self.worldid, bo) self.allured_by_signal_fire[ship] = True # HACK: remove allured flag in a few ticks def rem_allured(self, ship): self.allured_by_signal_fire[ship] = False Scheduler().add_new_object(Callback(rem_allured, self, ship), self, Scheduler().get_ticks(60)) self.send_ship_random_branch(ship, bo) return self.log.debug("Trader can't find bo in range of the signal fire")
def run(self, seconds=0): """Provide a nice way to run the game for some time. Despite its name, this method will run the *game simulation* for X seconds. When the game is paused, the timer continues once the game unpauses. """ if not seconds: cooperative.schedule() else: # little hack because we don't have Python3's nonlocal class Flag(object): running = True def stop(): Flag.running = False ticks = Scheduler().get_ticks(seconds) Scheduler().add_new_object(stop, None, run_in=ticks) while Flag.running: cooperative.schedule()
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 wreak_havoc(self, building): """Some inhabitants have to die.""" super(BlackDeathDisaster, self) if building.inhabitants > 1: inhabitants_that_will_die = self._manager.session.random.randint( 1, building.inhabitants) building.inhabitants -= inhabitants_that_will_die self.log.debug("%s inhabitants dying", inhabitants_that_will_die) Scheduler().add_new_object(Callback(self.wreak_havoc, building), self, run_in=self.TIME_BEFORE_HAVOC) else: self.recover(building)
def __init__(self, session, id, name, color, **kwargs): super(Pirate, self).__init__(session, id, name, color, **kwargs) # choose a random water tile on the coast and call it home self.home_point = self.session.world.get_random_possible_coastal_ship_position( ) # random sea tile if costal tile not found. Empty map? if not self.home_point: self.home_point = self.session.world.get_random_possible_ship_position( ) self.log.debug("Pirate: home at (%d, %d), radius %d", self.home_point.x, self.home_point.y, self.home_radius) self.__init() # create a ship and place it randomly (temporary hack) for i in range(self.ship_count): self.create_ship_at_random_position() Scheduler().add_new_object(Callback(self.tick), self, 1, -1, self.tick_interval) Scheduler().add_new_object(Callback(self.tick_long), self, 1, -1, self.tick_long_interval)
def add(self, string_id, point=None, msg_type=None, message_dict=None, play_sound=True, check_duplicate=False): """Adds a message to the MessageWidget. @param point: point where the action took place. Clicks on the message will then focus that spot. @param id: message id string, needed to retrieve the message text from the content database. @param msg_type: message type; determines what happens on click @param message_dict: dict with strings to replace in the message, e.g. {'player': 'Arthus'} @param play_sound: whether to play the default message speech for string_id @param check_duplicate: check for pseudo-duplicates (similar messages recently nearby) """ if check_duplicate: if string_id in self._last_message: when, where = self._last_message[string_id] if when > Scheduler().cur_tick - Scheduler().get_ticks(self.__class__._DUPLICATE_TIME_THRESHOLD) and \ where.distance(point) < self.__class__._DUPLICATE_SPACE_THRESHOLD: # there has been a message nearby recently, abort return self._last_message[string_id] = (Scheduler().cur_tick, point) sound = get_speech_file(string_id) if play_sound else None return self._add_message(_IngameMessage(point=point, id=string_id, msg_type=msg_type, created=self.msgcount.next(), message_dict=message_dict), sound=sound)
def _load(self, db, worldid): super(Pirate, self)._load(db, worldid) self.__init() remaining_ticks, = db( "SELECT remaining_ticks FROM ai_pirate WHERE rowid = ?", worldid)[0] Scheduler().add_new_object(Callback(self.tick), self, remaining_ticks, -1, self.tick_interval) remaining_ticks_long, = db( "SELECT remaining_ticks_long FROM ai_pirate WHERE rowid = ?", worldid)[0] Scheduler().add_new_object(Callback(self.tick_long), self, remaining_ticks_long, -1, self.tick_interval) home = db("SELECT x, y FROM pirate_home_point")[0] self.home_point = Point(home[0], home[1]) self.log.debug("Pirate: home at (%d, %d), radius %d", self.home_point.x, self.home_point.y, self.home_radius)
def save_attacks(cls, db): """ Saves ongoing attacks """ calls = Scheduler().get_classinst_calls(Weapon) for call in calls: callback = call.callback weapon_id = callback.args[1] damage = callback.args[2] dest_x = callback.args[3].x dest_y = callback.args[3].y ticks = calls[call] db("INSERT INTO attacks(remaining_ticks, weapon_id, damage, dest_x, dest_y) VALUES (?, ?, ?, ?, ?)", ticks, weapon_id, damage, dest_x, dest_y)
def remove_building(self, building): """Called when a building is removed from the area (the building still exists during the call).""" if building.id in self.field_building_classes: # this can't be handled right now because the building still exists Scheduler().add_new_object(Callback(self._refresh_unused_fields), self, run_in=0) Scheduler().add_new_object(Callback(partial(super(ProductionBuilder, self).remove_building, building)), self, run_in=0) elif building.buildable_upon or building.id == BUILDINGS.TRAIL: pass # don't react to road, trees and ruined tents being destroyed else: self.register_change_list(list(building.position.tuple_iter()), BUILDING_PURPOSE.NONE, None) if building.id in self.collector_building_classes: self.collector_buildings.remove(building) self.simple_collector_area_cache.remove_building(building) elif building.id in self.production_building_classes: self.production_buildings.remove(building) if building.id == BUILDINGS.LUMBERJACK: self._handle_lumberjack_removal(building) elif building.id == BUILDINGS.FARM: self._handle_farm_removal(building) super(ProductionBuilder, self).remove_building(building)
def _changed(self): super()._changed() if not self._prod_line.save_statistics: return state = self._state.index current_tick = Scheduler().cur_tick if self._state_history and self._state_history[-1][0] == current_tick: self._state_history.pop() # make sure no two events are on the same tick if not self._state_history or self._state_history[-1][1] != state: self._state_history.append((current_tick, state)) self._clean_state_history()
def begin_working(self): """Pretends that the collector works by waiting some time. finish_working is called after that time.""" self.log.debug("%s begins working", self) assert self.job is not None, '%s job is None in begin_working' % self Scheduler().add_new_object(self.finish_working, self, self.work_duration) # play working sound if self.has_component(AmbientSoundComponent): am_comp = self.get_component(AmbientSoundComponent) if am_comp.soundfiles: am_comp.play_ambient(am_comp.soundfiles[0], position=self.position) self.state = self.states.working
def __init__(self, x, y, slots=1, size=4, start_hidden=True, **kwargs): super(Collector, self).__init__(slots = slots, \ size = size, \ x = x, \ y = y, \ **kwargs) self.inventory.limit = size # TODO: use different storage to support multiple slots. see StorageHolder self.__init(self.states.idle, start_hidden) # start searching jobs just when construction (of subclass) is completed Scheduler().add_new_object(self.search_job, self, 1)
def load(self, db, worldid): super().load(db, worldid) runtime, action_set_id = db.get_concrete_object_data(worldid) # action_set_id should never be None in regular games, # but this information was lacking in savegames before rev 59. if action_set_id is None: action_set_id = self.__class__.get_random_action_set(level=self.level if hasattr(self, "level") else 0) self.__init(action_set_id) # delay setting of runtime until load of sub/super-class has set the action def set_action_runtime(self, runtime): # workaround to delay resolution of self._instance, which doesn't exist yet self._instance.setActionRuntime(runtime) Scheduler().add_new_object(Callback(set_action_runtime, self, runtime), self, run_in=0)
def do_win(session): """The player wins the current scenario.""" PauseCommand().execute(session) show_db_message(session, 'YOU_HAVE_WON') horizons.globals.fife.play_sound('effects', "content/audio/sounds/events/scenario/win.ogg") continue_playing = session.gui.show_popup(_("You have won!"), _("You have completed this scenario.") + u" " + _("Do you want to continue playing?"), show_cancel_button=True) if not continue_playing: Scheduler().add_new_object(Callback(session.gui.quit_session, force=True), session, run_in=0) else: UnPauseCommand().execute(session)
def infect(self, building, load=None): """Infect a building with fire. @load: (db, disaster_worldid), set on restoring infected state of savegame""" super(FireDisaster, self).infect(building, load=load) # keep in sync with load() AddStatusIcon.broadcast(building, FireStatusIcon(building)) NewDisaster.broadcast(building.owner, building, FireDisaster) self._affected_buildings.append(building) havoc_time = self.TIME_BEFORE_HAVOC if load: db, worldid = load havoc_time = db("SELECT remaining_ticks_havoc FROM fire_disaster WHERE disaster = ? AND building = ?", worldid, building.worldid)[0][0] Scheduler().add_new_object(Callback(self.wreak_havoc, building), self, run_in=havoc_time)