# HG changeset patch # User Emmanuel Gil Peyrot # Date 1341077841 -7200 # Node ID 1b4f04b087299b340e39557389abfdbb708e2973 # Parent 16ed1ab1e14b3cf1c015c3a8bf39c20199c1b0d3 Add the story mode. diff --git a/TODO b/TODO --- a/TODO +++ b/TODO @@ -8,6 +8,7 @@ Engine - stage change in story mode - update score.dat and disallow the launch of locked stages +- Rumia’s Moonlight Ray isn’t directed towards the player - Cirno’s last spell, killed before even starting it - Meiling’s rainbow in normal, doesn’t look like it should - Meiling’s blue/red thing, should be directed toward the player? diff --git a/eosd b/eosd --- a/eosd +++ b/eosd @@ -25,38 +25,71 @@ from pytouhou.ui.gamerunner import GameR from pytouhou.games.eosd import EoSDGame from pytouhou.game.player import PlayerState from pytouhou.formats.t6rp import T6RP +from pytouhou.utils.random import Random +from pytouhou.vm.msgrunner import NextStage -def main(path, stage_num, rank, character, replay, data): +def main(path, data, stage_num, rank, character, replay): + resource_loader = Loader(path) + resource_loader.scan_archives(data) + + if stage_num is None: + story = True + stage_num = 1 + else: + story = False + if replay: with open(replay, 'rb') as file: replay = T6RP.read(file) rank = replay.rank character = replay.character - if not replay.levels[stage_num-1]: - raise Exception - from pytouhou.utils.random import Random - prng = Random(replay.levels[stage_num-1].random_seed) - else: - prng = None + + difficulty = 16 + default_power = [0, 64, 128, 128, 128, 128, 0][stage_num - 1] + states = [PlayerState(character=character, power=default_power)] - resource_loader = Loader(path) + runner = GameRunner(resource_loader) + while True: + if replay: + level = replay.levels[stage_num - 1] + if not level: + raise Exception - resource_loader.scan_archives(data) + prng = Random(level.random_seed) - # Load stage data - stage = resource_loader.get_stage('stage%d.std' % stage_num) + #TODO: apply the replay to the other players. + #TODO: see if the stored score is used or if it’s the one from the previous stage. + if stage_num != 1 and stage_num - 2 in replay.levels: + previous_level = replay.levels[stage_num - 1] + states[0].score = previous_level.score + states[0].effective_score = previous_level.score + states[0].power = level.power + states[0].lives = level.lives + states[0].bombs = level.bombs + difficulty = level.difficulty + else: + prng = None - default_power = [0, 64, 128, 128, 128, 128, 0][stage_num - 1] - game = EoSDGame(resource_loader, [PlayerState(character=character, power=default_power)], stage_num, rank, 16, - prng=prng, bgms=stage.bgms) + # Load stage data + stage = resource_loader.get_stage('stage%d.std' % stage_num) - background_anm_wrapper = resource_loader.get_anm_wrapper(('stg%dbg.anm' % stage_num,)) - background = Background(stage, background_anm_wrapper) + game = EoSDGame(resource_loader, states, stage_num, rank, difficulty, prng=prng, bgms=stage.bgms) + + background_anm_wrapper = resource_loader.get_anm_wrapper(('stg%dbg.anm' % stage_num,)) + background = Background(stage, background_anm_wrapper) - # Main loop - runner = GameRunner(resource_loader, game, background, replay=replay) - runner.start() + # Main loop + runner.load_game(game, background, replay) + try: + runner.start() + break + except NextStage: + game.music.pause() + if not story or stage_num == 6: + break + stage_num += 1 + states = [player.state.copy() for player in game.players] # if player.state.lives >= 0] pathsep = os.path.pathsep @@ -71,11 +104,11 @@ parser = argparse.ArgumentParser(descrip parser.add_argument('data', metavar='DAT', default=default_data, nargs='*', help='Game’s data files') parser.add_argument('-p', '--path', metavar='DIRECTORY', default='.', help='Game directory path.') -parser.add_argument('-s', '--stage', metavar='STAGE', type=int, required=True, help='Stage, 1 to 7 (Extra).') +parser.add_argument('-s', '--stage', metavar='STAGE', type=int, default=None, help='Stage, 1 to 7 (Extra).') parser.add_argument('-r', '--rank', metavar='RANK', type=int, default=0, help='Rank, from 0 (Easy, default) to 3 (Lunatic).') parser.add_argument('-c', '--character', metavar='CHARACTER', type=int, default=0, help='Select the character to use, from 0 (ReimuA, default) to 3 (MarisaB).') parser.add_argument('--replay', metavar='REPLAY', help='Select a replay') args = parser.parse_args() -main(args.path, args.stage, args.rank, args.character, args.replay, tuple(args.data)) +main(args.path, tuple(args.data), args.stage, args.rank, args.character, args.replay) diff --git a/pytouhou/game/player.py b/pytouhou/game/player.py --- a/pytouhou/game/player.py +++ b/pytouhou/game/player.py @@ -46,6 +46,11 @@ class PlayerState(object): self.power_bonus = 0 # Never goes over 30. + def copy(self): + return PlayerState(self.character, self.score, + self.power, self.lives, self.bombs) + + class Player(object): def __init__(self, state, game, anm_wrapper): self._game = game diff --git a/pytouhou/ui/gamerunner.py b/pytouhou/ui/gamerunner.py --- a/pytouhou/ui/gamerunner.py +++ b/pytouhou/ui/gamerunner.py @@ -44,7 +44,9 @@ class GameRunner(pyglet.window.Window, G caption='PyTouhou', resizable=False) self.replay_level = None - self.load_game(game, background, replay) + + if game: + self.load_game(game, background, replay) self.clock = pyglet.clock.get_default() @@ -142,7 +144,6 @@ class GameRunner(pyglet.window.Window, G keystate |= 128 if self.keys[pyglet.window.key.LCTRL]: keystate |= 256 - self.game.run_iter(keystate) else: keystate = 0 for frame, _keystate, unknown in self.replay_level.keys: @@ -151,7 +152,7 @@ class GameRunner(pyglet.window.Window, G else: keystate = _keystate - self.game.run_iter(keystate) + self.game.run_iter(keystate) def render_game(self): diff --git a/pytouhou/vm/msgrunner.py b/pytouhou/vm/msgrunner.py --- a/pytouhou/vm/msgrunner.py +++ b/pytouhou/vm/msgrunner.py @@ -21,6 +21,10 @@ from pytouhou.game.face import Face logger = get_logger(__name__) +class NextStage(Exception): + pass + + class MSGRunner(object): __metaclass__ = MetaRegistry __slots__ = ('_msg', '_game', 'frame', 'sleep_time', 'allow_skip', @@ -138,6 +142,11 @@ class MSGRunner(object): self.frozen = True + @instruction(11) + def next_stage(self): + raise NextStage + + @instruction(13) def set_allow_skip(self, boolean): self.allow_skip = bool(boolean)