changeset 331:1b4f04b08729

Add the story mode.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Sat, 30 Jun 2012 19:37:21 +0200
parents 16ed1ab1e14b
children bdcf2077e368
files TODO eosd pytouhou/game/player.py pytouhou/ui/gamerunner.py pytouhou/vm/msgrunner.py
diffstat 5 files changed, 73 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- 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?
--- 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)
--- 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
--- 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):
--- 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)