Mercurial > touhou
changeset 187:46793ccfedca
Implement replays.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Wed, 26 Oct 2011 17:54:03 -0700 |
parents | 84da28ae7ee4 |
children | 008f90ebfdc0 |
files | eclviewer.py pytouhou/formats/t6rp.py pytouhou/opengl/gamerenderer.pyx pytouhou/opengl/gamerunner.py |
diffstat | 4 files changed, 129 insertions(+), 29 deletions(-) [+] |
line wrap: on
line diff
--- a/eclviewer.py +++ b/eclviewer.py @@ -24,9 +24,18 @@ from pytouhou.game.background import Bac from pytouhou.opengl.gamerunner import GameRunner from pytouhou.game.games import EoSDGame from pytouhou.game.player import PlayerState +from pytouhou.formats.t6rp import T6RP -def main(path, stage_num, rank, character): +def main(path, stage_num, rank, character, replay): + 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 + resource_loader = Loader() resource_loader.scan_archives(os.path.join(path, name) for name in ('CM.DAT', 'ST.DAT')) @@ -42,7 +51,7 @@ def main(path, stage_num, rank, characte print(stage.name) # Main loop - runner = GameRunner(resource_loader, game, background) + runner = GameRunner(resource_loader, game, background, replay=replay) runner.start() @@ -52,7 +61,8 @@ parser.add_argument('-p', '--path', meta parser.add_argument('-s', '--stage', metavar='STAGE', type=int, required=True, 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) +main(args.path, args.stage, args.rank, args.character, args.replay)
new file mode 100644 --- /dev/null +++ b/pytouhou/formats/t6rp.py @@ -0,0 +1,70 @@ +# -*- encoding: utf-8 -*- +## +## Copyright (C) 2011 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 3 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## + +from struct import unpack +from pytouhou.utils.helpers import read_string + +from pytouhou.utils.helpers import get_logger + +logger = get_logger(__name__) + + +class Level(object): + def __init__(self): + self.keys = [] + + +class T6RP(object): + def __init__(self): + self.levels = [] + + + @classmethod + def read(cls, file): + if file.read(4) != b'T6RP': + raise Exception + if file.read(2) != b'\x02\x01': + raise Exception + + replay = cls() + + replay.character, replay.rank, checksum, unknown, key, unknown = unpack('<BBHIBB', file.read(10)) + replay.date = read_string(file, 9, 'ascii') + replay.name = read_string(file, 9, 'ascii').rstrip() + unknown, replay.score, unknown, replay.slowdown, unknown = unpack('<HIIfI', file.read(18)) + + stages_offsets = unpack('<7I', file.read(28)) + + replay.levels = [] + + for offset in stages_offsets: + replay.levels.append(None) + + if offset == 0: + continue + + level = Level() + replay.levels[-1] = level + + file.seek(offset) + level.score, level.random_seed, unknown, level.power, level.lives, level.bombs, level.difficulty, unknown = unpack('<IHHBbbBI', file.read(16)) + + while True: + time, keys, unknown = unpack('<IHH', file.read(8)) + + if time == 9999999: + break + + level.keys.append((time, keys)) +
--- a/pytouhou/opengl/gamerenderer.pyx +++ b/pytouhou/opengl/gamerenderer.pyx @@ -42,7 +42,7 @@ cdef class GameRenderer: cdef Vertex *vertex_buffer - def __cinit__(self, resource_loader, game=None, background=None): + def __cinit__(self): # Allocate buffers self.vertex_buffer = <Vertex*> malloc(MAX_ELEMENTS * sizeof(Vertex))
--- a/pytouhou/opengl/gamerunner.py +++ b/pytouhou/opengl/gamerunner.py @@ -21,11 +21,17 @@ from pytouhou.opengl.gamerenderer import class GameRunner(pyglet.window.Window, GameRenderer): - def __init__(self, resource_loader, game=None, background=None): + def __init__(self, resource_loader, game=None, background=None, replay=None): GameRenderer.__init__(self, resource_loader, game, background) pyglet.window.Window.__init__(self, caption='PyTouhou', resizable=False) - self.keys = pyglet.window.key.KeyStateHandler() - self.push_handlers(self.keys) + self.replay_level = None + if not replay or not replay.levels[game.stage-1]: + self.keys = pyglet.window.key.KeyStateHandler() + self.push_handlers(self.keys) + else: + self.keys = 0 + self.instruction_pointer = 0 + self.replay_level = replay.levels[game.stage-1] self.fps_display = pyglet.clock.ClockDisplay() @@ -85,28 +91,42 @@ class GameRunner(pyglet.window.Window, G if self.background: self.background.update(self.game.frame) if self.game: - #TODO: allow user settings - keystate = 0 - if self.keys[pyglet.window.key.W]: - keystate |= 1 - if self.keys[pyglet.window.key.X]: - keystate |= 2 - #TODO: on some configurations, LSHIFT is Shift_L when pressed - # and ISO_Prev_Group when released, confusing the hell out of pyglet - # and leading to a always-on LSHIFT... - if self.keys[pyglet.window.key.LSHIFT]: - keystate |= 4 - if self.keys[pyglet.window.key.UP]: - keystate |= 16 - if self.keys[pyglet.window.key.DOWN]: - keystate |= 32 - if self.keys[pyglet.window.key.LEFT]: - keystate |= 64 - if self.keys[pyglet.window.key.RIGHT]: - keystate |= 128 - if self.keys[pyglet.window.key.LCTRL]: - keystate |= 256 - self.game.run_iter(keystate) #TODO: self.keys... + if not self.replay_level: + #TODO: allow user settings + keystate = 0 + if self.keys[pyglet.window.key.W]: + keystate |= 1 + if self.keys[pyglet.window.key.X]: + keystate |= 2 + #TODO: on some configurations, LSHIFT is Shift_L when pressed + # and ISO_Prev_Group when released, confusing the hell out of pyglet + # and leading to a always-on LSHIFT... + if self.keys[pyglet.window.key.LSHIFT]: + keystate |= 4 + if self.keys[pyglet.window.key.UP]: + keystate |= 16 + if self.keys[pyglet.window.key.DOWN]: + keystate |= 32 + if self.keys[pyglet.window.key.LEFT]: + keystate |= 64 + if self.keys[pyglet.window.key.RIGHT]: + keystate |= 128 + if self.keys[pyglet.window.key.LCTRL]: + keystate |= 256 + self.game.run_iter(keystate) #TODO: self.keys... + else: + frame, keys = self.replay_level.keys[self.instruction_pointer] + + if frame > self.game.frame: + self.game.run_iter(self.keys) + return + + self.instruction_pointer += 1 + + if frame == self.game.frame: + self.keys = keys + + self.game.run_iter(self.keys) def on_draw(self):