''' Instantiate this to create a Combat 'scene' containing a player and enemies both must be given, run the Combat.cmdloop() to start the scene ''' from monster import Monster from dicts.utils import * import colorama as C import cmd, platform, os from time import sleep from random import randint class Combat(cmd.Cmd): STRINGS = { 'intro' : 'You choose to fight. Enemies are staring viciously at you!\nPress Enter to start . . .', 'win' : 'VICTORY, You defeated all enemies!\nPress Enter to Exit . . .', 'lose' : 'DEFEAT, You got beaten by enemies!\nPress Enter to Exit . . .', 'syntax_error' : 'Oops! I dont understand', 'unknown_enemy': "I can't see that enemy!", 'enemy_death' : "You eliminated", 'user_death' : "You are bleeding too much.. Argh!", 'full_hp' : "You are perfectly healthy!", 'prompt' : 'Type <atk> to attack:\n> ', 'prompt_skl' : 'Type <atk> or <skl>:\n> ', 'atk_choice' : 'Attack .. Choose enemy number:\n', 'skl_choice' : 'Skill .. Choose enemy number:\n', 'no_skl' : "Oops! It seems like you haven't acquired a skill yet!", 'no_pwr' : 'Argh! not enough power to use your skill!', } # Global constants LIST_SYMBOL = ' *' PROMPT_SIGN = '# ' def __init__(self, user, enemies): # cmd.Cmd initialization super().__init__() # Color settings if platform.system() == 'Windows': C.init() self.reset_color() self.intro = input(center_screen(self.STRINGS['intro'])) self.prompt = '{}{}'.format(self.PROMPT_SIGN, self.STRINGS['prompt']) # user/enemies variables self.user = user self.STRINGS['player_attack'] = 'You ' + self.user.weapon_verb self.user_attack_msg = '' self.enemies = enemies self.no_of_enemies = len(enemies) self.enemies_dict = self.create_dictionary() self.enemies_attack_msg = '' # cmd.Cmd method overriding # Avoids repitition of last command def emptyline(self): self.display() pass # Error message for unknown commands def default(self, line): self.display() print(C.Fore.RED + '{}{} <{}>{}'.format(self.PROMPT_SIGN, self.STRINGS['syntax_error'], line, C.Back.BLACK)) print(C.Fore.WHITE, end='') # Removes the help method def do_help(self, arg): self.display() pass # Controls termination of Combat, win/lose msg def postcmd(self, stop, line): # Checks win condition if not self.enemies_alive(): print(C.Back.GREEN + C.Fore.WHITE + self.PROMPT_SIGN + self.STRINGS['win'] + C.Back.BLACK, end='') input() return True elif self.enemies_alive() and not self.user.alive: print(C.Back.RED + C.Fore.WHITE + self.PROMPT_SIGN + self.STRINGS['lose'] + C.Back.BLACK, end='') input() return True # Changes prompt if Skill is available to use if self.user.skill == self.user.max_skill: self.prompt = self.PROMPT_SIGN + self.STRINGS['prompt_skl'] else: self.prompt = self.PROMPT_SIGN + self.STRINGS['prompt'] # Pre/Post Loop functions def preloop(self): self.display() def postloop(self): pass @staticmethod def reset_color(): print(C.Style.RESET_ALL + C.Back.BLACK + C.Fore.WHITE + C.Style.BRIGHT, end='') # ENEMIES, PLAYER, COMBAT STUFF # Creates a dictionary that store Enemies and their corresponding names def create_dictionary(self): dict = {} counter = 1 for enemy in self.enemies: if enemy.alive: dict[str(counter)] = enemy counter += 1 return dict # Returns a string of alive enemy names def alive_enemy_names(self): names = '' counter = 1 for enemy in self.enemies: if enemy.alive: names += ' {}| {}\n'.format(counter, enemy.name) counter += 1 return names # Are any enemy alive? True/False def enemies_alive(self): self.no_of_enemies = len(self.enemies) for enemy in self.enemies: if not enemy.alive: self.no_of_enemies -= 1 if self.no_of_enemies > 0: return True else: return False # Checks if enemy will die from a specific blow and returns a string accordingly def enemy_death_msg(self, enemy, dmg_taken): if (enemy.hp - dmg_taken) <= 0: # Message if enemy is dead outcome = "\n{}{}{} {}{}".format(C.Style.BRIGHT + C.Back.BLACK + C.Fore.RED, self.PROMPT_SIGN, self.STRINGS['enemy_death'], enemy.name, C.Back.BLACK) else: outcome = '' return outcome # Attacks a chosen enemy def user_attack(self, enemy): self.user_attack_msg = "{}{}{} {} (-{}HP)".format(C.Style.BRIGHT + C.Back.BLACK + C.Fore.CYAN, self.PROMPT_SIGN, self.STRINGS['player_attack'], enemy.name, self.user.dmg) self.user_attack_msg += self.enemy_death_msg(enemy, self.user.dmg) self.user.attack(enemy) # Attacks enemy using the player's current skill def user_skill(self, enemies): my_skill = self.user.skill_type # Executes skill depending multi-target/single-target skill if my_skill['ismulti']: self.user_attack_msg = (C.Style.BRIGHT + C.Back.BLACK + C.Fore.MAGENTA + "{}SKILL: {} >>>\n{} (-{}HP to all)".format(self.PROMPT_SIGN, my_skill['name'].upper(), my_skill['message'], my_skill['dmg'])) for enemy in enemies: self.user_attack_msg += self.enemy_death_msg(enemy, my_skill['dmg']) my_skill['function'](self.user, enemies) else: self.user_attack_msg = (C.Style.BRIGHT + C.Back.BLACK + C.Fore.MAGENTA + "{}SKILL: {} >>>\n{} {} {} (-{}HP)".format(self.PROMPT_SIGN, my_skill['name'].upper(), self.STRINGS['player_attack'], enemies.name, my_skill['message'], my_skill['dmg'])) self.user_attack_msg += self.enemy_death_msg(enemies, my_skill['dmg']) my_skill['function'](self.user, enemies) self.user.skill = 0 # All alive enemies attacks the user and returns a hit string def enemies_attack(self): messages = C.Style.BRIGHT + C.Back.BLACK + C.Fore.WHITE for enemy in self.enemies: if enemy.alive: if enemy.dmg == 0: hit_string = '' else: enemy.attack(self.user) hit_string = "!! {} {} you (-{}HP)\n".format(enemy.name, enemy.action, str(enemy.dmg)) messages += hit_string messages += C.Style.BRIGHT + C.Back.LIGHTCYAN_EX + C.Fore.BLUE if self.user.hp <= 0: messages += self.PROMPT_SIGN + self.STRINGS['user_death'] + C.Back.BLACK elif self.user.hp == self.user.max_hp: messages += self.PROMPT_SIGN + self.STRINGS['full_hp'] + C.Back.BLACK else: messages += '{}You survived enemy attacks with {}/{} HP left{}'.format(self.PROMPT_SIGN, self.user.hp, self.user.max_hp, C.Back.BLACK) self.enemies_attack_msg = messages # UTILITY FUNCTIONS # Displays the interface: All Enemies and user status def display(self, clr = True): self.reset_color() if clr: clear() self.user.show() for enemy in self.enemies: if enemy.alive: print(C.Style.BRIGHT + C.Back.BLACK + C.Fore.RED + enemy.show()) else: print(C.Style.DIM + C.Back.BLACK + C.Fore.RED + enemy.show()) self.reset_color() print() # Reveals events slowly if self.user_attack_msg: for line in self.user_attack_msg.split('\n'): sleep(0.5) print(line) if self.enemies_attack_msg: for line in self.enemies_attack_msg.split('\n'): sleep(0.75) print(line) self.user_attack_msg = '' self.enemies_attack_msg = '' # print(self.enemies_attack_msg) print(C.Style.BRIGHT + C.Back.BLACK + C.Fore.WHITE) # A wrapper for printing a RED error message def error_msg(self, text): self.display() print(C.Fore.RED + self.PROMPT_SIGN + text + C.Fore.WHITE) # A loop that executes any given function on an enemy # whenever a valid input is entered def demand_and_execute(self, function, _prompt=STRINGS['atk_choice']): self.display() while True: choice = input(self.PROMPT_SIGN + _prompt + self.alive_enemy_names() + '> ') self.enemies_dict = self.create_dictionary() try: target_enemy = self.enemies_dict[choice.lower()] function(target_enemy) self.enemies_attack() self.display() return True except KeyError: print(C.Fore.RED, end='') input(self.PROMPT_SIGN + self.STRINGS['unknown_enemy']) self.display() # USER INPUT AND COMMANDS # Cmd commands def do_atk(self, arg): """Attacks a specific enemy, type <atk>""" self.demand_and_execute(self.user_attack) def do_skl(self, arg): """Unleashs special power using up all power points, type <skl>""" if self.user.skill_type == None: self.error_msg(self.STRINGS['no_skl']) elif self.user.skill == self.user.max_skill: # Multi and single target skill execution if not self.user.skill_type['ismulti']: self.demand_and_execute(self.user_skill, self.STRINGS['skl_choice']) else: enemies_list = [] for enemy in self.enemies: if enemy.alive: enemies_list.append(enemy) self.user_skill(enemies_list) self.enemies_attack() self.display() else: self.error_msg(self.STRINGS['no_pwr'])