示例#1
0
def main():
    try:
        sequencer.begin_sequence(scene_1)
    except Exception:
        msg = exception_message(header='RUNTIME ERROR', log_handler=logger)
        msg += '\n\nSee the log for more details.'

        # Print message in red
        print_color('{RA}{FLred}', end='')
        print_color(msg, do_not_format=True)
示例#2
0
    def do_list(self, arg):
        """List your current moves.
Usage: list [future commands]"""
        # Should be the same as in the Move shell
        print_color(self.fighter.string_moves(
            ignore_skills=True,
            ignore_items=True)
        )
        print()

        self.updateCmdqueue(arg)
示例#3
0
    def do_stats(self, arg):
        """View your current stats.
Usage: stats [future commands]"""
        print_color(
            self.fighter.battle_env.fightChartOne(
                self.fighter, color=cfg_engine.GAME_DISPLAY_STATS_COLOR_MODE
            )[0]
        )
        print()

        self.updateCmdqueue(arg)
示例#4
0
    def do_display(self, arg):
        """Display the current battle.
Usage: display [future commands]"""
        print_color(self.fighter.battle_env.fightChartTwo(
            self.fighter, self.opponent,
            topMessage='<--', tabs=cfg_engine.GAME_DISPLAY_USE_TABS,
            color_mode=cfg_engine.GAME_DISPLAY_STATS_COLOR_MODE)
        )
        print()

        self.updateCmdqueue(arg)
示例#5
0
        def a_turn():
            nonlocal move_result
            nonlocal statLogA
            nonlocal statLogB

            effects_messages_durations = a.update_status_effect_durations()
            effects_messages_values = a.update_status_effect_values()
            if effects_messages_values:
                autoplay_pause()
                a.print_status_effect_messages(
                    effects_messages_values, get_status_effects_print_delay())
                if autoplay:
                    print()
            elif turn > 1:
                # Only triggers after first turn so battle starts immediately
                autoplay_pause()

            if is_dead(a) or is_dead(b):
                return False

            # Print a chart of both fighters' stats
            print(fight_chart(topMessage='<--'))

            autoplay_pause()
            if effects_messages_durations:
                a.print_status_effect_messages(
                    effects_messages_durations,
                    get_status_effects_print_delay())
                print()

            if cfg_engine.GAME_DISPLAY_SHOW_STAT_DIFFERENCE:
                # Show damage difference
                statLogA = self.battle_stats_log(a)
                statLogB = self.battle_stats_log(b)

            # Print user moves if enabled
            if cfg_engine.GAME_DISPLAY_PRINT_MOVES:
                print_color(a.string_moves())

            # Fighter attacks
            move_result = a.move(b, do_not_send=stop_after_move)

            if not cfg_engine.GAME_DISPLAY_SHOW_STAT_DIFFERENCE:
                # Show regeneration
                statLogA = self.battle_stats_log(a)
            if not is_dead(a) and not is_dead(b):
                a.update_stats()
                return True
            return False
示例#6
0
def input_gamemode(gamemodes):
    print_color(f"Gamemodes: {', '.join(gamemodes[1:])}")
    gamemodes = [gm.casefold() for gm in gamemodes]

    gamemode = input_color(
        'Type a gamemode (case-insensitive) to play it,\n'
        f'or nothing to skip. {cfg_engine.GAME_SETUP_INPUT_COLOR}').casefold()

    while gamemode not in gamemodes:
        gamemode = input_color(
            'Unknown gamemode; check spelling: '
            f'{cfg_engine.GAME_SETUP_INPUT_COLOR}').casefold()

    logger.debug(f'Got gamemode from user: {gamemode!r}')
    return gamemode
示例#7
0
 def print_end_message(end_message):
     speed = cfg_engine.GAME_DISPLAY_SPEED
     time.sleep(0.75 / speed)
     print_color(end_message[0])
     time.sleep(0.5 / speed)
     print_color(end_message[1])
     time.sleep(0.25 / speed)
     print_color(end_message[2])
     time.sleep(0.8 / speed)
     print_color(end_message[3])
示例#8
0
        def b_turn():
            nonlocal move_result
            nonlocal statLogA
            nonlocal statLogB

            effects_messages_durations = b.update_status_effect_durations()
            effects_messages_values = b.update_status_effect_values()
            autoplay_pause()
            if effects_messages_values:
                b.print_status_effect_messages(
                    effects_messages_values, get_status_effects_print_delay())
                if autoplay:
                    print()

            if is_dead(a) or is_dead(b):
                return False

            print(fight_chart(topMessage='-->'))

            autoplay_pause()
            if effects_messages_durations:
                b.print_status_effect_messages(
                    effects_messages_durations,
                    get_status_effects_print_delay())
                print()

            if cfg_engine.GAME_DISPLAY_SHOW_STAT_DIFFERENCE:
                statLogA = self.battle_stats_log(a)
                statLogB = self.battle_stats_log(b)

            if cfg_engine.GAME_DISPLAY_PRINT_MOVES:
                print_color(b.string_moves())

            move_result = b.move(a, do_not_send=stop_after_move)

            if not cfg_engine.GAME_DISPLAY_SHOW_STAT_DIFFERENCE:
                statLogB = self.battle_stats_log(b)
            if not is_dead(a) and not is_dead(b):
                b.update_stats()
                return True
            return False
示例#9
0
    def default(self, line):
        """Searches for the move."""
        inputMove = line.lower().title()

        moveFind = self.fighter.find_move(
            {'name': inputMove},
            ignore_movetypes=True,
            ignore_skills=True,
            ignore_items=True,
            exactSearch=cfg_interface.MOVES_REQUIRE_EXACT_SEARCH,
            detailedFail=True)

        # If search worked, print info about the move
        if not isinstance(moveFind, (type(None), BoolDetailed)):
            namespace = fighter_stats.ALL_STAT_INFOS.copy()
            namespace['move'] = moveFind
            print_color(moveFind['description'], namespace=namespace)
            print()
            return

        # Else request for a move again
        elif moveFind is None:
            # Search failed (detailedFail=False)
            message = 'Did not find move'
            if 'returnTo' in self.namespace:
                print_color(message)
                return
            else:
                self.prompt = message + ', type again: '
        elif isinstance(moveFind, BoolDetailed):
            # Search failed (detailedFail=True)
            if moveFind.name == 'TooManyResults':
                moveCount = int(moveFind.description.split()[1])
                message = f'Found {moveCount:,} different moves'
                if 'returnTo' in self.namespace:
                    print_color(message)
                    return
                else:
                    self.prompt = message + ', type again: '
            elif moveFind.name == 'NoResults':
                message = 'Did not find move'
                if 'returnTo' in self.namespace:
                    print_color(message)
                    return
                else:
                    self.prompt = message + ', type again: '
        else:
            raise RuntimeError('MoveInfoShell from moveFind in player_move '
                               f'returned unknown object {moveFind!r}')
示例#10
0
 def default(self, line):
     """Called when a command prefix is not recognized."""
     print_color('{Fyello}Unknown command')
示例#11
0
    def help_move(self):
        print_color("""
Type the move you want to use.
Use "list" to display your available moves.
""")
示例#12
0
    def default(self, line):
        """Searches for the move."""
        inputMove = line.lower().title()

        moveFind, unsatisfactories = self.fighter.find_move(
            {'name': inputMove},
            ignore_skills=True,
            ignore_items=True,
            exactSearch=cfg_interface.MOVES_REQUIRE_EXACT_SEARCH,
            detailedFail=True,
            showUnsatisfactories=True)

        # If search worked, check for unsatisfactories
        if not isinstance(moveFind, (type(None), BoolDetailed)):
            if unsatisfactories:
                # Missing dependencies; explain then request another move
                reasonMessage = f'Could not use {moveFind}; ' \
                                + moveFind.parse_unsatisfactories(
                                    unsatisfactories)
                if 'returnTo' in self.namespace:
                    print_color(reasonMessage)
                    self.namespace['shell_result'] = False
                    return self.exit()
                else:
                    self.prompt = reasonMessage + '\nPick another move: '
                    return

            else:
                # All dependencies satisfied; use the move
                self.namespace['shell_result'] = moveFind
                if 'returnTo' in self.namespace:
                    self.namespace['newMoveCMD'] = ''
                else:
                    self.namespace['newMoveCMD'] = 'move'
                return self.exit()

        # Else request for a move again
        elif moveFind is None:
            # Search failed (detailedFail=False)
            if 'returnTo' in self.namespace:
                print_color('Did not find move')
                self.namespace['shell_result'] = False
                return self.exit()
            else:
                self.prompt = 'Did not find move, type again: '
        elif isinstance(moveFind, BoolDetailed):
            # Search failed (detailedFail=True)
            if moveFind.name == 'TooManyResults':
                moveCount = int(moveFind.description.split()[1])
                message = f'Found {moveCount:,} different moves'
                if 'returnTo' in self.namespace:
                    print_color(message)
                    self.namespace['shell_result'] = False
                    return self.exit()
                else:
                    self.prompt = message + ', type again: '
            elif moveFind.name == 'NoResults':
                message = 'Did not find move'
                if 'returnTo' in self.namespace:
                    print_color(message)
                    self.namespace['shell_result'] = False
                    return self.exit()
                else:
                    self.prompt = message + ', type again: '
        else:
            raise RuntimeError('moveFind in player_move returned '
                               f'unknown object {moveFind!r}')
示例#13
0
    def begin_battle(self,
                     a,
                     b,
                     *,
                     statLogA=None,
                     statLogB=None,
                     starting_turn=2,
                     autoplay=2,
                     return_end_message=True,
                     stop_after_move=False):
        """
        Args:
            a (Fighter)
            b (Fighter): The two fighters battling each other.
            statLogA (Optional[List[dict]])
            statLogB (Optional[List[dict]]): A stat log generated by
                self.battle_stats_log() for their respective fighters.
                This info is used to calculate what stats have changed.
            starting_turn (int): The starting turn.
                If uneven, fighter B will move first.
                By default the starting turn is 2 so if one wins
                and `return_end_message` is True, it will show
                that the battle took one turn (`turn // 2`).
            autoplay (Autoplay):
                Autoplay.INSTANT: Disables pauses.
                Autoplay.SLEEP: Pauses using time.sleep.
                Autoplay.INPUT: Pauses using input().
            return_end_message (bool): If True, a list of color-formatted
                end messages is returned and can be printed out with
                `self.print_end_message`. Otherwise, returns None.
                If you know you will not print the default end message,
                set this to False so the messages do not get generated.
            stop_after_move (bool): If True, the next Fighter to move
                will not trigger `target.move_receive` but instead this
                method returns the results of `sender.move(do_not_send=True)`.
                `statLogA`, `statLogB`, and `turn` values are included.
                Note that `turn` is assigned to the "starting_turn" key to
                simplify using the return value as a starred expression
                for this method.
                Creating a custom code loop:
                    def step_battle(results=None):
                        results = {} if results is None else results
                        return begin_battle(a, b, stop_after_move=True, **results)
                    results = step_battle()
                    while isinstance(results, dict):
                        move_result = results.pop('move_result')
                        if move_result is None:
                            # Move failed due to unsatisfied requirements
                            results = step_battle(results)
                            continue
                        # <Your code here>
                        results = step_battle(results)
        Returns:
            dict: Returned when `stop_after_move` and a Fighter moves.
                See Args for more info.
            list: When `return_end_message`, this list contains 4 lines:
                "Number of Turns: {turn}"
                Fighter A's health: {hp}
                Fighter B's health: {hp}
                "The winner is {winner}!"
            None: Returned when return_end_message is False.

        """
        def fight_chart(*, topMessage, color_mode=None):
            if color_mode is None:
                color_mode = cfg_engine.GAME_DISPLAY_STATS_COLOR_MODE
            return self.fightChartTwo(a,
                                      b,
                                      statLogA,
                                      statLogB,
                                      statsToShow=self.stats_to_show,
                                      topMessage=topMessage,
                                      tabs=cfg_engine.GAME_DISPLAY_USE_TABS,
                                      color_mode=color_mode)

        def autoplay_pause():
            if autoplay == Autoplay.INSTANT:
                print()
            elif autoplay == Autoplay.SLEEP:
                if a.is_player or b.is_player:
                    # If there is one/two players, pause AUTOPLAY seconds
                    util.pause(cfg_engine.GAME_AUTOPLAY_NORMAL_DELAY /
                               cfg_engine.GAME_DISPLAY_SPEED)
                else:
                    # If two AIs are fighting, pause FIGHT_AI seconds
                    util.pause(cfg_engine.GAME_AUTOPLAY_AI_DELAY /
                               cfg_engine.GAME_DISPLAY_SPEED)
                print()
            elif autoplay == Autoplay.INPUT:
                input()
            else:
                raise TypeError(
                    'expected autoplay argument to be an Autoplay enum '
                    f'but received {autoplay!r}')

        def cannotMove(fighter):
            for effect in fighter.status_effects:
                if 'noMove' in effect:
                    return True
            return False

        def get_status_effects_print_delay():
            if autoplay == Autoplay.INSTANT:
                return 0
            elif autoplay == Autoplay.SLEEP:
                if a.is_player or b.is_player:
                    return (cfg_engine.GAME_AUTOPLAY_NORMAL_DELAY /
                            cfg_engine.GAME_DISPLAY_SPEED)
                else:
                    return (cfg_engine.GAME_AUTOPLAY_AI_DELAY /
                            cfg_engine.GAME_DISPLAY_SPEED)
            elif autoplay == Autoplay.INPUT:
                return None
            else:
                raise TypeError(
                    'expected autoplay argument to be an Autoplay enum '
                    f'but received {autoplay!r}')

        def is_dead(fighter):
            return hasattr(fighter, 'hp') and fighter.hp <= 0

        logger.info('Starting fight against '
                    f'{a.name_decolored} and {b.name_decolored}')
        turn = starting_turn
        move_result = None
        statLogA = self.battle_stats_log(a) if statLogA is None else statLogA
        statLogB = self.battle_stats_log(b) if statLogB is None else statLogB

        def a_turn():
            nonlocal move_result
            nonlocal statLogA
            nonlocal statLogB

            effects_messages_durations = a.update_status_effect_durations()
            effects_messages_values = a.update_status_effect_values()
            if effects_messages_values:
                autoplay_pause()
                a.print_status_effect_messages(
                    effects_messages_values, get_status_effects_print_delay())
                if autoplay:
                    print()
            elif turn > 1:
                # Only triggers after first turn so battle starts immediately
                autoplay_pause()

            if is_dead(a) or is_dead(b):
                return False

            # Print a chart of both fighters' stats
            print(fight_chart(topMessage='<--'))

            autoplay_pause()
            if effects_messages_durations:
                a.print_status_effect_messages(
                    effects_messages_durations,
                    get_status_effects_print_delay())
                print()

            if cfg_engine.GAME_DISPLAY_SHOW_STAT_DIFFERENCE:
                # Show damage difference
                statLogA = self.battle_stats_log(a)
                statLogB = self.battle_stats_log(b)

            # Print user moves if enabled
            if cfg_engine.GAME_DISPLAY_PRINT_MOVES:
                print_color(a.string_moves())

            # Fighter attacks
            move_result = a.move(b, do_not_send=stop_after_move)

            if not cfg_engine.GAME_DISPLAY_SHOW_STAT_DIFFERENCE:
                # Show regeneration
                statLogA = self.battle_stats_log(a)
            if not is_dead(a) and not is_dead(b):
                a.update_stats()
                return True
            return False

        def b_turn():
            nonlocal move_result
            nonlocal statLogA
            nonlocal statLogB

            effects_messages_durations = b.update_status_effect_durations()
            effects_messages_values = b.update_status_effect_values()
            autoplay_pause()
            if effects_messages_values:
                b.print_status_effect_messages(
                    effects_messages_values, get_status_effects_print_delay())
                if autoplay:
                    print()

            if is_dead(a) or is_dead(b):
                return False

            print(fight_chart(topMessage='-->'))

            autoplay_pause()
            if effects_messages_durations:
                b.print_status_effect_messages(
                    effects_messages_durations,
                    get_status_effects_print_delay())
                print()

            if cfg_engine.GAME_DISPLAY_SHOW_STAT_DIFFERENCE:
                statLogA = self.battle_stats_log(a)
                statLogB = self.battle_stats_log(b)

            if cfg_engine.GAME_DISPLAY_PRINT_MOVES:
                print_color(b.string_moves())

            move_result = b.move(a, do_not_send=stop_after_move)

            if not cfg_engine.GAME_DISPLAY_SHOW_STAT_DIFFERENCE:
                statLogB = self.battle_stats_log(b)
            if not is_dead(a) and not is_dead(b):
                b.update_stats()
                return True
            return False

        def game_loop():
            nonlocal turn

            someone_has_moved = False

            while True:
                if turn % 2 == 0:
                    continue_turn = a_turn()
                    someone_has_moved = True
                    turn += 1
                else:
                    # Skip to fighter B's turn
                    continue_turn = True

                if not continue_turn or stop_after_move and someone_has_moved:
                    # Either a_turn() says a fighter has no health
                    # or stop_after_move is True and fighter A has moved
                    break

                # Repeat for B
                if turn % 2 != 0:
                    continue_turn = b_turn()
                    someone_has_moved = True
                    turn += 1
                else:
                    # Skip to fighter A's turn
                    continue_turn = True

                if not continue_turn or stop_after_move and someone_has_moved:
                    # Either b_turn() says a fighter has no health
                    # or stop_after_move is True and fighter B has moved
                    break

        print()
        if not is_dead(a) and not is_dead(b):
            game_loop()

        if not is_dead(a) and not is_dead(b) and stop_after_move:
            # Return results of move since `stop_after_move` is True
            # along with the info required to continue `begin_battle`
            return {
                'move_result': move_result,
                'starting_turn': turn,
                'statLogA': statLogA,
                'statLogB': statLogB
            }

        # Semantically, one turn is where both fighters move
        turn //= 2

        # Print results
        winner = a if not is_dead(a) and is_dead(b) \
            else b if is_dead(a) and not is_dead(b) \
            else None

        # Show the stat change only for the loser
        if winner is a:
            statLogA = None
        elif winner is b:
            statLogB = None
        else:
            # No winner; don't show any stat change
            statLogA = statLogB = None

        fightChart = fight_chart(topMessage='END')
        fightChart_nocolor = fight_chart(topMessage='END', color_mode=0)

        logFightChart = '\n'.join(fightChart_nocolor.split('\n')[1:])
        logger.info(f'Fight ended in {turn} turn{plural(turn)}:\n'
                    f'{logFightChart}\n')

        # add green color to the top message and print it
        autoplay_pause()
        print_color(fightChart.replace('END', '{FLgree}END{RA}'))
        print()

        if return_end_message:
            end_message = []

            end_message.append(f"Number of Turns: {turn}")

            a_win_color = (ColoramaCodes.FLgree
                           if a.hp > 0 else ColoramaCodes.FLred)
            b_win_color = (ColoramaCodes.FLgree
                           if b.hp > 0 else ColoramaCodes.FLred)
            end_message.append(
                format_color(
                    "{a}'s {a.stats['hp'].ext_full.title()}: "
                    '{a_win_color}{a.hp}',
                    namespace=locals()))
            end_message.append(
                format_color(
                    "{b}'s {b.stats['hp'].ext_full.title()}: "
                    '{b_win_color}{b.hp}',
                    namespace=locals()))

            winner_str = str(winner) if winner is not None else 'nobody'
            end_message.append(format_color(f'The winner is {winner_str}!'))

            return end_message
示例#14
0
def missile_evasion(evader, missile, side='left'):
    """Start a missile evasion.

    Args:
        evader (Fighter): The fighter evading the missile.
        missile (str): The move that the missile is being used from.
        side (Union["left", "right"]): The fighter's side
            to display on. The missile moves first regardless.

    """
    missile_type = missile['missile_type']
    speed = missile['missile_speed']
    field_of_regard = missile['field_of_regard']
    track_rate = missile['track_rate']
    base_damage = missile['base_damage']
    blast_radius = missile['blast_radius']

    missile_fighter = engine.Fighter(name='{FLmage}Missile{RA}',
                                     stats=stats.get_defaults(
                                         stats.missile_stats),
                                     moves=moves.missile_moves,
                                     counters={},
                                     AI=ai.MissileAI())
    with engine.BattleEnvironment(fighters=(evader,
                                            missile_fighter)) as battle:

        def step_battle(results=None):
            results = {} if results is None else results
            return battle.begin_battle(a,
                                       b,
                                       stop_after_move=True,
                                       autoplay=Autoplay.SLEEP,
                                       **results)

        results = {'starting_turn': 2 + int(side == 'left')}

        if side == 'left':
            a, b = evader, missile_fighter
        elif side == 'right':
            a, b = missile_fighter, evader
        else:
            raise ValueError(f'Unknown side {side!r}')

        results = step_battle(results)
        while isinstance(results, dict):
            move_result = results.pop('move_result')
            if move_result is None:
                results = step_battle(results)
                continue

            sender, move = move_result['sender'], move_result['move']

            if sender == evader:
                effect = move['effect']
                if isinstance(effect, dict):
                    actual_effect = effect.get(missile_type, 0)
                else:
                    actual_effect = effect

                missile_fighter.er += actual_effect
            elif sender == missile_fighter:
                missile_fighter.di -= speed
                if missile_fighter.er < field_of_regard:
                    # Missile can still see evader
                    weight = max(
                        0,
                        min(
                            1 - missile_fighter.er / (1.5 * field_of_regard),
                            1 - (missile_fighter.er / field_of_regard)**10,
                        ))
                    error_correction = int(track_rate * weight)

                    missile_fighter.er -= error_correction

            if missile_fighter.di == 0:
                # Detonation
                sleep = (cfg_engine.GAME_AUTOPLAY_AI_DELAY /
                         cfg_engine.GAME_DISPLAY_SPEED)

                print_color('\nThe missile makes its last adjustments!',
                            end='\n\n\n')

                time.sleep(sleep)

                print(battle.fightChartTwo(
                    a,
                    b,
                    statLogA=results['statLogA'],
                    statLogB=results['statLogB'],
                    tabs=cfg_engine.GAME_DISPLAY_USE_TABS,
                    color_mode=cfg_engine.GAME_DISPLAY_STATS_COLOR_MODE),
                      end='\n\n\n')

                value = -int(base_damage *
                             (1 - max(0, missile_fighter.er / blast_radius)))
                if value < 0:
                    print_typewriter('... Hit!',
                                     sleep_char=SLEEP_CHAR_DELAY_NORMAL,
                                     sleep_char_specifics={'.': 0.5})
                else:
                    print_typewriter('... Miss!',
                                     sleep_char=SLEEP_CHAR_DELAY_NORMAL,
                                     sleep_char_specifics={'.': 0.5})
                return value
            else:
                target = b if sender == a else a
                target.print_move(sender, move, None, None, 'fastMessage')

            results = step_battle(results)