def perform_character_creation_questions( self, existing_names: List[str]) -> Union[str, bool, list, dict]: questions = [ { "type": "input", "message": emojis.encode(':man: Please enter your character name '), "validate": DuplicatedNamesValidator(existing_names), "invalid_message": "minimum of 1 letters, max of 20 letters", "name": "nickname" }, { "type": "list", "message": emojis.encode(':skull: Please enter your character race? '), "choices": get_configuration(RACES_SECTION).keys(), "name": "race" }, { "type": "list", "message": emojis.encode( ':name_badge: Please enter your character job? '), "choices": get_configuration(JOBS_SECTION).keys(), "name": "job" }, ] return prompt(questions)
def attack(self, player: IPlayer) -> Optional[bool]: players = self.game.get_remaining_players(player) attack_range = player.get_ranged_attack_area() possible_foes = self.get_attack_possibilities(attack_range, player, players) if len(possible_foes) == 0: self.communicator.informer.no_foes_attack(player) return False enemy_to_attack = self.communicator.questioner.ask_enemy_to_attack(possible_foes) if enemy_to_attack is None: return False self.communicator.informer.force_loading(2) self.communicator.informer.event('attack') dice_result = self.game.roll_the_dice() self.communicator.informer.dice_result(player.name, dice_result, 'attack', self.game.dice_sides) damage = self.calculate_damage(player, enemy_to_attack, dice_result) self.check_player_level_up(player) if damage > 0: enemy_to_attack.suffer_damage(damage) self.communicator.informer.suffer_damage(player, enemy_to_attack, damage) experience = get_configuration(EXPERIENCE_EARNED_ACTION).get('attack', 0) player.earn_xp(experience) self.communicator.informer.player_earned_xp(player_name=player.name, xp=experience) if not enemy_to_attack.is_alive(): experience = get_configuration(EXPERIENCE_EARNED_ACTION).get('kill', 0) player.earn_xp(experience) self.communicator.informer.player_killed_enemy_earned_xp(player_name=player.name, xp=experience) else: self.communicator.informer.missed(player, enemy_to_attack) return
def check_experience(self, player: IPlayer, successful_skill: bool, killed: bool) -> None: if successful_skill: experience = get_configuration(EXPERIENCE_EARNED_ACTION).get( 'attack', 0) player.earn_xp(experience) self.communicator.informer.player_earned_xp( player_name=player.name, xp=experience) if killed: experience = get_configuration(EXPERIENCE_EARNED_ACTION).get( 'kill', 0) player.earn_xp(experience) self.communicator.informer.player_earned_xp( player_name=player.name, xp=experience)
def test_conf(self): # this test is self explanatory, as the game configuration class itself, already does a lot of # validations on all the required files, params and environment variables, the only test to be done it's # to check this class gets instantiated correctly, because as a Singleton, the class has all validators inside # its constructor configuration_object = get_configuration('') self.assertIsNotNone(configuration_object)
def distribute_random_items(self) -> None: """ Function to coordinate distribution of random items in the map The items will be placed in the half of the quantity of the walkable nodes in the map, and with the probabilities for each tier of item configured in the conf file, the quantity for each tier will be determined, and finally a random items will be picked for the respective quantities of each tier, and placed in some random places. :rtype: None """ walkable_nodes = self.graph.get_walkable_nodes() number_of_walkable_nodes = len(walkable_nodes) number_of_items = floor(number_of_walkable_nodes / 2) probabilities = get_configuration('item_probabilities') common_items_number = round(number_of_items * probabilities.get('common', 0.6)) uncommon_items_number = round(number_of_items * probabilities.get('uncommon', 0.2)) rare_items_number = round(number_of_items * probabilities.get('rare', 0.15)) legendary_items_number = round(number_of_items * probabilities.get('legendary', 0.05)) item_type_distribution = { 'common': ['healing'] * 45 + ['equipment'] * 30 + ['recovery'] * 25, 'uncommon': ['healing'] * 50 + ['equipment'] * 50, 'rare': ['healing'] * 20 + ['equipment'] * 80, 'legendary': ['equipment'] * 80 } for i in range(common_items_number + uncommon_items_number + rare_items_number + legendary_items_number): key = random.choice(list(walkable_nodes.keys())) walkable_nodes.pop(key) tier = '' if common_items_number > 0: tier = 'common' common_items_number = common_items_number - 1 elif uncommon_items_number > 0: tier = 'uncommon' uncommon_items_number = uncommon_items_number - 1 elif rare_items_number > 0: tier = 'rare' rare_items_number = rare_items_number - 1 elif legendary_items_number > 0: tier = 'legendary' legendary_items_number = legendary_items_number - 1 item_type = random.choice(item_type_distribution.get(tier)) item = get_random_item(tier, item_type) self.add_item_to_map(key, item)
def bot_factory(number_of_bots: int) -> List[IBotPlayer]: """ Function that will generated all the bots, depending of the number that was informed as the argument, each bot race and job, will be picked randomly. :param int number_of_bots: Number of bots to create. :rtype: List[IBotPlayer]. """ bots = [] jobs = list(get_configuration(JOBS_SECTION).keys()) races = list(get_configuration(RACES_SECTION).keys()) for n in range(int(number_of_bots)): name = generate_name() chosen_job = jobs[randrange(len(jobs))] chosen_race = races[randrange(len(races))] job = dynamic_jobs_classes[chosen_job]() race = dynamic_races_classes[chosen_race]() bag = Bag() equipment = Equipment() bots.append(BotPlayer(name, job, race, bag, equipment)) return bots
def get_random_item(tier: str, item_type: str) -> Item: """ Function to get a random item along all the items from the items.yaml file, considering a specific tier and type. :param str tier: The tier of the item. :param str item_type: The type of the item(healing, recovery, equipment) :rtype: Item """ items_dicts = get_configuration(ITEMS_SECTION) item_key = random.choice( list({ k: v for (k, v) in items_dicts.items() if v.get('tier') == tier and v.get('type') == item_type }.keys())) item_dict = items_dicts.get(item_key) if item_dict.get('type') == 'healing': return HealingItem(name=item_dict.get('name'), tier=item_dict.get('tier'), description=item_dict.get('description'), weight=item_dict.get('weight'), attribute=item_dict.get('attribute'), base=item_dict.get('base')) elif item_dict.get('type') == 'recovery': return RecoveryItem(name=item_dict.get('name'), tier=item_dict.get('tier'), description=item_dict.get('description'), weight=item_dict.get('weight'), status=item_dict.get('status')) elif item_dict.get('type') == 'equipment': ''' Side-effects are instantiated here, because when the user equips the item, the side-effect it's already instantiated and it will be appended into the user's side-effect list, passing the same instance, which makes the changes to duration attribute, each time a new turn comes, reflects both in the list of side effects and also in the bag. ''' side_effects = instantiate_side_effects(item_dict.get('side_effects')) return EquipmentItem(name=item_dict.get('name'), tier=item_dict.get('tier'), description=item_dict.get('description'), weight=item_dict.get('weight'), attribute=item_dict.get('attribute'), base=item_dict.get('base'), side_effects=side_effects, category=item_dict.get('category'), usage=item_dict.get('usage'), wielding=item_dict.get('wielding', 0))
def improve_attributes_automatically(job: IJob, race: IRace) -> Dict: """ This function can be used by both bots and humans to upgrade their attributes, this method Takes the result of the combination of the attributes from job and race, and select the greater ones to improve. :param Job job: base job of the player. :param Race race: base race of the player. :rtype: dict. """ unsorted_attributes_dict = {} chosen_attributes = {} job_attribute_points = get_configuration(JOBS_SECTION).get(job) race_attribute_points = get_configuration(RACES_SECTION).get(race) level_up_increment_attributes = get_configuration(LEVEL_UP_INCREMENT) for key, value in level_up_increment_attributes.items(): unsorted_attributes_dict[ key] = job_attribute_points[key] + race_attribute_points[key] sorted_list = iter( sorted(unsorted_attributes_dict, key=unsorted_attributes_dict.get, reverse=True)) first_attribute = next(sorted_list) second_attribute = next(sorted_list) not_allowed_together_list = ['magic_points', 'health_points'] # As HP and MP, always have a higher distribution percentage, most probably they will always be the first two # elements of the sorted list, so to prevent all bots to always improve only these attributes, this IF is necessary. if first_attribute in not_allowed_together_list and second_attribute in not_allowed_together_list: second_attribute = next(sorted_list) chosen_attributes[first_attribute] = level_up_increment_attributes.get( first_attribute, 0) chosen_attributes[second_attribute] = level_up_increment_attributes.get( second_attribute, 0) return chosen_attributes
def improve_attributes_randomly() -> Dict: """ This function can be used by bots and humans, and it will randomly pick 2 attributes to upgrade. :rtype: dict. """ level_up_increment_attributes = copy(get_configuration(LEVEL_UP_INCREMENT)) first_key, first_val = random.choice( list(level_up_increment_attributes.items())) level_up_increment_attributes.pop(first_key, None) second_key, second_val = random.choice( list(level_up_increment_attributes.items())) return {first_key: first_val, second_key: second_val}
def __init__(self, clsname, superclasses, attributedict): if clsname == 'Race': return super_class = superclasses[0] race_attributes = get_configuration('races').get(clsname, {}) super_class.__init__(self, race_attributes.get('health_points', 0), race_attributes.get('magic_points', 0), race_attributes.get('move_speed', 0), race_attributes.get('strength', 0), race_attributes.get('intelligence', 0), race_attributes.get('accuracy', 0), race_attributes.get('armour', 0), race_attributes.get('magic_resist', 0), race_attributes.get('will', 0) )
def create_dynamic_races(): races_dynamic_classes = {} race_from_config = get_configuration(RACES_SECTION) for race in race_from_config: custom_race = type(race, (Race,), { # constructor "__init__": constructor, "name": race, # member functions "func_arg": display_method, "class_func": class_method, "get_name": get_name }) races_dynamic_classes[race] = custom_race return races_dynamic_classes
def __init__(self, clsname, superclasses, attributedict): if clsname == 'Job': return super_class = superclasses[0] jobs_attributes = get_configuration('jobs').get(clsname, {}) super_class.__init__( self, jobs_attributes.get('health_points', 0), jobs_attributes.get('magic_points', 0), jobs_attributes.get('move_speed', 0), jobs_attributes.get('strength', 0), jobs_attributes.get('intelligence', 0), jobs_attributes.get('accuracy', 0), jobs_attributes.get('armour', 0), jobs_attributes.get('magic_resist', 0), jobs_attributes.get('will', 0), jobs_attributes.get('attack_type', 'melee'), jobs_attributes.get('damage_vector', 'strength'), )
def get_player_available_skills(player: IPlayer) -> List[ISkill]: """ Skills can be unlocked/revealed for players depending their levels and jobs, this method compare the current attributes of a player, will all the skills, to check which ones this current player has eligibility to use. :param IPlayer player: The current player to discover new skills. :rtype: List[ISkill]. """ skill_dicts = get_configuration(SKILLS_SECTION) available_skills: List[ISkill] = [] for key, value in skill_dicts.items(): if player.job.get_name() == value.get( 'job') and player.level >= value.get( 'level_requirement') and player.mana > value.get('cost'): available_skills.append(get_instantiated_skill({key: value})) return available_skills
def create_dynamic_jobs(): jobs_dynamic_classes = {} job_from_config = get_configuration(JOBS_SECTION) for job in job_from_config: custom_job = type( job, (Job, ), { # constructor "__init__": constructor, "name": job, # member functions "func_arg": display_method, "class_func": class_method, "get_name": get_name }) jobs_dynamic_classes[job] = custom_job return jobs_dynamic_classes
def validate(self, document: Document): """ This function is used for validating the number of bots inserted, when creating a new game. :param Document document: The document to be validated. :rtype: None. """ max_bot_number = get_configuration(GAME_SECTION).get('max_number_bots') if not document.text.isnumeric(): raise ValidationError( message="Input should be a number", cursor_position=document.cursor_position, ) else: if int(document.text) > max_bot_number or int(document.text) <= 0: raise ValidationError( message= "The number of boots needs to be minimum 1 and maximum {maximum}" .format(maximum=max_bot_number), cursor_position=document.cursor_position, )
def instantiate_side_effects( side_effects_strings: List[str]) -> List[ISideEffect]: """ This class will receive a list of strings that represents the names of each side-effects, those names of side-effects will be queried from the configuration, and instantiated to be ready to use. :param List[str] side_effects_strings: List with the name os the effects. :rtype: List[ISideEffect]. """ side_effects_library_dict = get_configuration('side_effects') side_effects: List[SideEffect] = [] for side_effect_string in side_effects_strings: side_effect_dict = side_effects_library_dict.get(side_effect_string) side_effect = SideEffect(name=side_effect_string, effect_type=side_effect_dict.get('type'), attribute=side_effect_dict.get('attribute'), base=side_effect_dict.get('base'), duration=side_effect_dict.get('duration'), occurrence=side_effect_dict.get('occurrence')) side_effects.append(side_effect) return side_effects
def wrapper(func): section_result = get_configuration(section) setattr(func, section, section_result) return func
def ask_attributes_to_improve(self) -> Union[str, bool, list, List]: level_up_increment_attributes = get_configuration(LEVEL_UP_INCREMENT) health_points = level_up_increment_attributes.get('health_points', 5) magic_points = level_up_increment_attributes.get('magic_points', 5) move_speed = level_up_increment_attributes.get('move_speed', 1) strength = level_up_increment_attributes.get('strength', 3) intelligence = level_up_increment_attributes.get('intelligence', 3) accuracy = level_up_increment_attributes.get('accuracy', 1) armour = level_up_increment_attributes.get('armour', 3) magic_resist = level_up_increment_attributes.get('magic_resist', 3) will = level_up_increment_attributes.get('will', 3) level_up_questions = [ { "type": "list", "message": "Select an action:", "choices": [ { "name": emojis.encode( "+{points} Health Points :green_heart:".format( points=health_points)), "value": { "attribute": "health_points", "value": health_points } }, { "name": emojis.encode( "+{points} Magic Points :blue_heart:".format( points=magic_points)), "value": { "attribute": "magic_points", "value": magic_points } }, { "name": emojis.encode("+{points} Move Speed :runner:".format( points=move_speed)), "value": { "attribute": "move_speed", "value": move_speed } }, { "name": emojis.encode("+{points} Strength :punch:".format( points=strength)), "value": { "attribute": "strength", "value": strength } }, { "name": emojis.encode("+{points} Intelligence :books:".format( points=intelligence)), "value": { "attribute": "intelligence", "value": intelligence } }, { "name": emojis.encode("+{points} Accuracy :dart:".format( points=accuracy)), "value": { "attribute": "accuracy", "value": accuracy } }, { "name": emojis.encode( "+{points} Armour :anger:".format(points=armour)), "value": { "attribute": "armour", "value": armour } }, { "name": emojis.encode( "+{points} Magic Resist :cyclone:".format( points=magic_resist)), "value": { "attribute": "magic_resist", "value": magic_resist } }, { "name": emojis.encode( "+{points} Will :pray:".format(points=will)), "value": { "attribute": "will", "value": will } }, ], "default": None, "multiselect": True, "validate": lambda selected: len(selected) == 2, "invalid_message": "You need to select 2 attributes to improve!", "show_cursor": True, "max_height": "100" }, ] result = prompt(questions=level_up_questions) return result[0]
def attack(self) -> None: if self.possible_foe is None: self.current_play_style = IPlayingMode.DEFENSIVE return # This dictionary represents the possibilities of attack/skills that a bot can perform, where the keys # are the attack/skills and the values the possible damage that this one may inflict attack_possibilities_dict = {} attack_range = self.current_bot.get_ranged_attack_area() attack_range_possibilities = self.game.game_map.graph.get_available_nodes_in_range( self.current_bot.position, attack_range) attack_range_possibilities.append(self.current_bot.position) if (self.current_bot.job.attack_type == 'melee' and self.possible_foe.position == self.current_bot.position) \ or ( self.current_bot.job.attack_type == 'ranged' and self.possible_foe.position in attack_range_possibilities): attack_possibilities_dict['attack'] = self.current_bot.get_attribute_real_value( self.current_bot.job.damage_vector, self.current_bot.job.attack_type ) for skill in self.current_bot.skills: if skill.cost < self.current_bot.mana and skill.kind == 'inflict' \ and self.game.game_map.graph.is_target_in_range(self.current_bot.position, skill.ranged, self.possible_foe.position): attack_possibilities_dict[skill] = self.current_bot.get_attribute_real_value( skill.base_attribute ) + skill.base if len(attack_possibilities_dict) < 1: self.current_play_style = IPlayingMode.DEFENSIVE return sorted_attack_possibilities_tuple = sorted(attack_possibilities_dict.items(), key=lambda x: x[1], reverse=True) best_attack = next(iter(sorted_attack_possibilities_tuple))[0] dice_result = self.game.roll_the_dice() if best_attack == 'attack': self.communicator.informer.event('attack') self.communicator.informer.dice_result(self.current_bot.name, dice_result, 'attack', self.game.dice_sides) targeted_defense = 'armour' if self.current_bot.job.damage_vector == 'strength' else 'magic_resist' damage = 0 if self.current_bot.job.damage_vector == 'intelligence': damage = (self.current_bot.get_attribute_real_value(self.current_bot.job.damage_vector, self.current_bot.job.attack_type) / 2) + ( dice_result / self.game.dice_sides) * 5 elif self.current_bot.job.damage_vector == 'strength' and self.current_bot.job.attack_type == 'ranged': damage = self.current_bot.get_attribute_real_value(self.current_bot.job.damage_vector, self.current_bot.job.attack_type) \ + self.current_bot.get_attribute_real_value( 'accuracy') + ( dice_result / self.game.dice_sides) * 5 else: damage = self.current_bot.get_attribute_real_value(self.current_bot.job.damage_vector, self.current_bot.job.attack_type) + ( dice_result / self.game.dice_sides) * 5 damage = math.ceil(damage - self.possible_foe.get_attribute_real_value(targeted_defense)) if damage > 0: self.possible_foe.suffer_damage(damage) self.communicator.informer.suffer_damage(self.current_bot, self.possible_foe, damage) experience = get_configuration(EXPERIENCE_EARNED_ACTION).get('attack', 0) self.current_bot.earn_xp(experience) self.communicator.informer.player_earned_xp(player_name=self.current_bot.name, xp=experience) if not self.possible_foe.is_alive(): experience = get_configuration(EXPERIENCE_EARNED_ACTION).get('kill', 0) self.current_bot.earn_xp(experience) self.communicator.informer.player_earned_xp(player_name=self.current_bot.name, xp=experience) else: self.communicator.informer.missed(self.current_bot, self.possible_foe) return elif isinstance(best_attack, ISkill): self.prepare_execute_skill(best_attack) else: self.current_play_style = IPlayingMode.DEFENSIVE