def A_move_region(self, adjacent_region): """Move to an adjacent region of space adjacent_region -- (x,y) coordinates of an adjacent region""" # check to make sure not on celestial if self.celestial: message = [ f'{self} is currently on the celestial {self.celestial}. Use the take_off ability before trying to move regions.' ] return Payload(self.get_LID(), message) # get the two region objects Regions = get_file('Regions.pickle') r1 = Regions[self.xy] # this is a user-facing ability, so adjacent_region is a string adjacent_region_tup = region_string_to_int(adjacent_region) r2 = Regions[adjacent_region_tup] # calculate the distance between the two regions distance = distance_between(r1.xy[0], r2.xy[0], r1.xy[1], r2.xy[1]) # calculate the time it would take in hours duration = distance / self.speed_space messages = [ f'{self} is now moving towards {r2}.', f'It will arrive in {duration} hours' ] return Payload(self.get_LID(), messages, isTaskMaker=True, taskDuration=duration, onCompleteFunc=self.set_new_region, onCompleteArgs=[adjacent_region_tup])
def resource_harvested(self, resource_name, harvester_ID): # need to do this so we can save Territories = get_file('Territories.pickle') self_territory = Territories[self.id] harvester = self_territory.content[harvester_ID] try: # decrease resource by 1 self_territory.resources[resource_name] -= 1 except KeyError as e: # double-check that the resource is actually here print( f'{e} not found in {self} when harvest was attempted by {self.content[harvester_ID]}.' ) messages = [ f'{resource_name} in {self} was depleted before {self.content[harvester_ID]} could complete harvest.' ] return Payload(None, messages) # add the resource to the harvester's inventory if resource_name in self.content[harvester_ID].inventory: # increment if already exists self_territory.content[harvester_ID].inventory[resource_name] += 1 else: # set if it doesn't exist self_territory.content[harvester_ID].inventory[resource_name] = 1 # save inventory and resource changes save_file(Territories, 'Territories.pickle') messages = [f'{harvester} has harvested 1 {resource_name}.'] return Payload(None, messages)
def A_harvest_resource(self, resource_name): """Harvest a resource in the same territory as this Harvester resource_name -- The name of the resource to harvest You can see the resources in a territory with ~scan. """ # get the territory of self TID = self.celestial.upper() + self.territory.lower() Territories = get_file('Territories.pickle') terr_obj = Territories[TID] try: if terr_obj.resources[resource_name] != 0: # harvest the resource duration = self.harvest_time messages = [f'{self} is now harvesting 1 {resource_name} from {self.territory} {self.celestial}.', f'It will be completed in {duration} hours.'] return Payload(self.get_LID(), messages, isTaskMaker=True, taskDuration=duration, onCompleteFunc=terr_obj.resource_harvested, onCompleteArgs=[resource_name, self.eid]) elif terr_obj.resource[resource_name] == 0: messages = [f'The {resource_name} in {self.territory} is depleted.', f'Wait for Evan to implement the regeneration mechanic.'] return Payload(self.get_LID(), messages) except KeyError: # if the resource doesn't exist at all in the territory messages = [f'There is no {resource_name} in {self.territory}'] return Payload(self.get_LID(), messages)
def set_new_region(self, new_region_xy): """Trigger function used to move the entity into a new region First, deletes the entity from its old region or territory Then places the entity in the new region new_region_xy -- The tuple (x,y) of the new region """ # get the region storage file Regions = get_file('Regions.pickle') if self.eid in Regions[self.xy].content: # remove self from old region del Regions[self.xy].content[self.eid] else: # triggered if the entity is on a celestial # therefore: Territories = get_file('Territories.pickle') # get the Territory ID from the celestial + territory name TID = self.celestial.upper() + self.territory.lower() # remove self from the territory and celestial self.territory = None self.celestial = None del Territories[TID].content[self.eid] save_file(Territories, 'Territories.pickle') # add self to new region self.xy = new_region_xy new_region = Regions[new_region_xy] new_region.content[self.get_eid()] = self save_file(Regions, 'Regions.pickle') # managing output (what the bot should send) messages = [f'{self} has arrived in {new_region_xy}'] return Payload(self.get_LID(), messages)
def set_new_territory(self, new_territory_ID): """Trigger function used to place the entity in a new territory First, deletes from the old region or territory Then, places in the new territory new_territory_ID -- The territory ID string, e.g. EARTHnorth """ if self.xy: # delete self, if in a region Regions = get_file('Regions.pickle') old_region = Regions[self.xy] del old_region.content[self.eid] save_file(Regions, 'Regions.pickle') if self.territory: # delete self, if in a territory Territories = get_file('Territories.pickle') TID = self.celestial.upper() + self.territory.lower() old_territory = Territories[TID] del old_territory.content[self.eid] save_file(Territories, 'Territories.pickle') # now we add to new territory Territories = get_file('Territories.pickle') new_territory = Territories[new_territory_ID] self.celestial = new_territory.parent self.territory = new_territory.label new_territory.content[self.get_eid()] = self # change self.territory save_file(Territories, 'Territories.pickle') # bot output messages = [f'{self} has arrived in {new_territory}'] return Payload(self.get_LID(), messages)
def landed_on(self, entity_id, target_territory): '''Function that is called when this celestial is landed on entity_id -- The ID of the entity that is lending on this celestial target_territory -- The territory this entity will land''' # * Note that this "sucks in" the landing_entity * # # * Vs. the entity actually landing * # # * The celestial is doing all the work * # Regions = get_file('Regions.pickle') # get the region object Region = Regions[self.xy] # get the entity object from the region dict entity_obj = Region.content[entity_id] # get the territory object from Territories.pickle Territories = get_file('Territories.pickle') territory_obj = Territories[self.name.upper() + target_territory.lower()] # add the entity to the territory territory_obj.content[entity_id] = entity_obj # set the attributes of the vehicle entity_obj.celestial = self.name entity_obj.territory = target_territory save_file(Territories, 'Territories.pickle') # delete the entity from the region del Region.content[entity_id] save_file(Regions, 'Regions.pickle') # return the payload for the landing messages = [ f'{entity_obj} has landed on the {target_territory} region of {self}.' ] return Payload(self, messages)
async def on_command_error(ctx, error): '''Manages error messages''' # This prevents any commands with local handlers being handled here in on_command_error. if hasattr(ctx.command, 'on_error'): return if isinstance(error, commands.errors.CommandInvokeError): err_string = str(error) if 'KeyError' in err_string: missing_ID = err_string.split()[-1] messages = [f'A lookup failed when looking under the ID {missing_ID}.', 'You may have mistyped the entity name, or are searching in the wrong region/territory.', 'Try ~scanning the region or territory to make sure the entity is in it.'] output = payload_manage(Payload(None, messages)) await ctx.send(output) traceback.print_exception(type(error), error, error.__traceback__) return if isinstance(error, commands.errors.MissingRequiredArgument): command = ctx.command signature = command.signature.replace('<', '"').replace('>', '"') messages = [f'Improper usage or missing arguments for {command}.', f'{command.name} {signature}'] output = payload_manage(Payload(None, messages)) await ctx.send(output) return if isinstance(error, commands.errors.CommandNotFound): attempt = ctx.message.content[1:] all_commands = bot.all_commands possible_commands = [c for c in all_commands if attempt in c] messages = [f'The command "{attempt}" does not exist'] if possible_commands: messages.append(f'Did you mean: {possible_commands}?') else: messages.append(f'No commands contain "{attempt}".') output = payload_manage(Payload(None, messages)) await ctx.send(output) return if isinstance(error, KeyError): messages = f'No result was found when looking up {error}' output = payload_manage(Payload(None, messages)) await ctx.send(output) return print('Ignoring exception in command {}:'.format(ctx.command)) traceback.print_exception(type(error), error, error.__traceback__)
def scan(self): messages = [ f'This is the region of space denoted by the coordinates {self.xy}', 'This region contains the following entities:' ] for obj in self.content.values(): try: messages.append(f' {str(obj)} | {obj.owner}') except AttributeError: messages.append(f' {str(obj)} | {type(obj).__name__}') return Payload(None, messages)
def on_landing(self, landing_obj): # get the distance between each celestial (in millions of miles) distance = distance_between(self.xy[0], self.linkRegion.xy[0], self.xy[1], self.linkRegion.xy[1]) # divide that distance by the slingshot travel rate travel_time = distance / 25 return Payload(self.get_LID(), ['Hello'], isTaskMaker=True, taskDuration=travel_time, onCompleteFunc=landing_obj.change_region, onCompleteArgs=self.linkRegion)
def inspect(self): """Returns details describing the current state of this entity""" messages = [f'A {type(self).__name__} belonging to {self.owner}.'] if self.xy: messages.append(f'It is currently in the region {self.xy}') if self.celestial: messages.append( f'It is currently on the celestial {self.celestial}, in the {self.territory} territory.' ) messages.append(f'It has the following abilities: {self.abilities}') return Payload(self.get_LID(), messages)
def A_take_off(self): """Takes off from the current celestial into the surrounding region""" duration = self.speed_landing messages = [ f'{self} is now preparing to take off from the {self.territory} territory of {self.celestial}', f'It will arrive in the {self.xy} region in {duration} hours.' ] return Payload(self.get_LID(), messages, isTaskMaker=True, taskDuration=duration, onCompleteFunc=self.change_region, onCompleteArgs=[self.xy])
def A_move_territory(self, new_territory): """Move to another territory on the same celestial EXAMPLE move_territory South new_territory -- The territory on the same celestial to move to Territories can be found by ~scanning the celestial """ # check to make sure in territory, not region if self.xy: message = [f'{self} is currently in the space region {self.xy}, not a territory.'] return Payload(self.get_LID(), message) # this is a user-facing ability, so convert to TID new_TID = self.celestial.upper() + new_territory.lower() # calculate the time it would take in hours duration = self.speed_land messages = [f'{self} is now moving towards the {new_territory} territory of {self.celestial}.', f'They will arrive in {duration} hours'] return Payload(self.get_LID(), messages, isTaskMaker=True, taskDuration=duration, onCompleteFunc=self.set_new_territory, onCompleteArgs=[new_TID])
def A_construct_building(self, plan_name): """Help construct a building plan in the same territory as this Builder plan_name -- The name of the building plan to construct You can see plans in the territory with ~scan """ # get the territory of self TID = self.celestial.upper() + self.territory.lower() Territories = get_file('Territories.pickle') terr_obj = Territories[TID] try: # get the building plan obj bpan = terr_obj.content[plan_name] messages = [f'{bpan} is now being worked on by {self}.', f"The {bpan}'s progress will be advanced in {self.build_time} hours."] return Payload(self.get_LID(), messages, isTaskMaker=True, taskDuration=self.build_time, onCompleteFunc=bpan.worked_on, onCompleteArgs=[1]) except KeyError: # if the name doesn't exist in the territory messages = [f'There is no plan named "{plan_name}" in {self.territory}.'] return Payload(self.get_LID(), messages)
def scan(self): messages = [ f'The {self.label} territory of the celestial {self.parent}.', f'It is a {self.description} biome.', f'It currently hosts the following resources: {self.resources}' ] if self.content: messages.append(f'It currently contains the following entities:') for obj in self.content.values(): try: messages.append(f' {str(obj)} | {obj.owner}') except AttributeError: messages.append(f' {str(obj)} | {type(obj).__name__}') return Payload(self, messages)
def worked_on(self, UoC): "Trigger func when this building plan is Built by an Actor" # need to save self LID = self.get_LID() Storage = get_file(LID['LocFile']) self.construction_remaining -= UoC Storage[LID['LocKey']].content[self.eid] = self save_file(Storage, LID['LocFile']) if self.construction_remaining > 0: # if there is still construction to be done messages = [f'The construction progress of {self.name} has advanced.', f'It now requires {self.construction_remaining} units of Construction to be completed.'] return Payload(self.get_LID(), messages) elif self.construction_remaining <= 0: # if construction_remaining is 0 or less return self.complete()
def A_calculate_slingshot(self, celest_1, celest_2): # * bot-facing function * # # calculates a fast path between two celestials # first, we have to check that both celestials exist Celestials = get_file('Celestials.pickle') try: c1 = Celestials[celest_1] c2 = Celestials[celest_2] except KeyError as e: return f"The indicated celestial {e} does not exist" # now we send out a Payload object to the bot return Payload(self.get_LID(), ['Hello'], isTaskMaker=True, taskDuration=1, onCompleteFunc=self.create_slingshot, onCompleteArgs=[c1, c2])
async def ability_help(ctx, entity_display, target_xy, ability): """Sends a help display for an ability of a given entity EXAMPLE: ~ability_help "Evan's Halcyon" (0,0) move_region entity_display -- The display name of the target, e.g. "Breq's Halcyon" \n target_xy -- The (x,y) coordinates of the target \n ability -- The ability name to display help for (shown by ~inspect) """ target = get_entity_obj(entity_display, target_xy=target_xy) ability_method = getattr(target, 'A_' + ability) arg_count = ability_method.__code__.co_argcount arguments = str(ability_method.__code__.co_varnames[1:arg_count]) messages = [str(ability_method.__name__)[2:] + '- Arguments: ' + arguments, str(ability_method.__doc__)] output = payload_manage(Payload(None, messages)) await ctx.send(output)
def A_land_on(self, target_celestial, target_territory): """Land on any celestial body capable of hosting a ship target_celestial -- The celestial to land on, must be in same region target_territory -- The territory the ship should land in (Territories can be viewed by ~inspect_entity <landing_target>""" duration = self.speed_landing # Get the landing_target object Regions = get_file('Regions.pickle') landing_target = Regions[self.xy].content[target_celestial] messages = [ f'{self} is now preparing to land on {landing_target}.', f'It will arrive in {duration} hours.' ] return Payload(self.get_LID(), messages, isTaskMaker=True, taskDuration=duration, onCompleteFunc=landing_target.landed_on, onCompleteArgs=[self.eid, target_territory])
def inspect(self): messages = [ f'The planet {self.name}, located in {self.xy}.', f'Contains the territories {list(self.territories.keys())}.' ] return Payload(self, messages)