# HG changeset patch # User Emmanuel Gil Peyrot # Date 1331398023 -3600 # Node ID da53bc29b94aa69ebd05980ce2d0583af6c29350 # Parent e04e402e6380c3afb5a77af3cdc8390cff991ef3 Add the game interface. diff --git a/eosd b/eosd --- a/eosd +++ b/eosd @@ -61,6 +61,7 @@ def main(path, stage_num, rank, characte pathsep = os.path.pathsep default_data = (pathsep.join(('CM.DAT', 'th06*_CM.DAT', '*CM.DAT', '*cm.dat')), pathsep.join(('ST.DAT', 'th6*ST.DAT', '*ST.DAT', '*st.dat')), + pathsep.join(('IN.DAT', 'th6*IN.DAT', '*IN.DAT', '*in.dat')), pathsep.join(('102h.exe', '102*.exe', '東方紅魔郷.exe', '*.exe'))) diff --git a/pytouhou/formats/anm0.py b/pytouhou/formats/anm0.py --- a/pytouhou/formats/anm0.py +++ b/pytouhou/formats/anm0.py @@ -80,12 +80,12 @@ class ANM0(object): @classmethod def read(cls, file): nb_sprites, nb_scripts, zero1 = unpack(' +## +## 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 copy import copy + +from pytouhou.game.sprite import Sprite +from pytouhou.vm.anmrunner import ANMRunner + + +class Glyph(object): + def __init__(self, sprite, pos): + self._sprite = sprite + self._removed = False + + self.x, self.y = pos + + +class Text(object): + def __init__(self, pos, text, front_wrapper, ascii_wrapper): + self._sprite = Sprite() + self._anmrunner = ANMRunner(front_wrapper, 22, self._sprite) + self._anmrunner.run_frame() + self._removed = False + self._changed = True + + self.text = '' + self.glyphes = [] + + self.front_wrapper = front_wrapper + self.ascii_wrapper = ascii_wrapper + + self.x, self.y = pos + self.set_text(text) + + + def objects(self): + return self.glyphes + [self] + + + def set_text(self, text): + if text == self.text: + return + + if len(text) > len(self.glyphes): + ref_sprite = Sprite() + anm_runner = ANMRunner(self.ascii_wrapper, 0, ref_sprite) + anm_runner.run_frame() + ref_sprite.corner_relative_placement = True #TODO: perhaps not right, investigate. + self.glyphes.extend(Glyph(copy(ref_sprite), (self.x + 14*i, self.y)) + for i in range(len(self.glyphes), len(text))) + elif len(text) < len(self.glyphes): + self.glyphes[:] = self.glyphes[:len(text)] + + for glyph, character in zip(self.glyphes, text): + glyph._sprite.anm, glyph._sprite.texcoords = self.ascii_wrapper.get_sprite(ord(character) - 21) + glyph._sprite._changed = True + + self.text = text + self._changed = True + + + def update(self): + if self._changed: + if self._anmrunner and not self._anmrunner.run_frame(): + self._anmrunner = None + self._changed = False + diff --git a/pytouhou/games/eosd.py b/pytouhou/games/eosd.py --- a/pytouhou/games/eosd.py +++ b/pytouhou/games/eosd.py @@ -20,6 +20,8 @@ from pytouhou.game.lasertype import Lase from pytouhou.game.itemtype import ItemType from pytouhou.game.player import Player from pytouhou.game.orb import Orb +from pytouhou.game.effect import Effect +from pytouhou.game.text import Text from math import pi @@ -81,9 +83,56 @@ class EoSDGame(Game): characters = resource_loader.get_eosd_characters() players = [EoSDPlayer(state, self, resource_loader, characters[state.character]) for state in player_states] + interface = EoSDInterface(player_states, resource_loader) + Game.__init__(self, resource_loader, players, stage, rank, difficulty, bullet_types, laser_types, item_types, nb_bullets_max, - width, height, prng) + width, height, prng, interface) + + + +class EoSDInterface(Game): + def __init__(self, states, resource_loader): + self.states = states + front = resource_loader.get_anm_wrapper(('front.anm',)) + ascii_wrapper = resource_loader.get_anm_wrapper(('ascii.anm',)) + + self.width = 640 + self.height = 480 + self.game_pos = (32, 16) + + self.items = ([Effect((0, 32 * i), 6, front) for i in range(15)] + + [Effect((416 + 32 * i, 32 * j), 6, front) for i in range(7) for j in range(15)] + + [Effect((32 + 32 * i, 0), 7, front) for i in range(12)] + + [Effect((32 + 32 * i, 464), 8, front) for i in range(12)] + + [Effect((0, 0), 5, front)] + + [Effect((0, 0), i, front) for i in range(5) + range(9, 16)]) + for item in self.items: + item._sprite.allow_dest_offset = True #XXX + + self.labels = { + 'highscore': Text((500, 58), '0', front, ascii_wrapper), + 'score': Text((500, 82), '0', front, ascii_wrapper), + 'player': Text((500, 122), 'star star', front, ascii_wrapper), + 'bombs': Text((500, 146), 'star star', front, ascii_wrapper), + 'power': Text((500, 186), '0', front, ascii_wrapper), + 'graze': Text((500, 206), '0', front, ascii_wrapper), + 'points': Text((500, 226), '0', front, ascii_wrapper), + 'framerate': Text((512, 464), '', front, ascii_wrapper), + 'debug?': Text((0, 464), '', front, ascii_wrapper), + } + + + def update(self): + for elem in self.items: + elem.update() + + player_state = self.states[0] + + self.labels['score'].set_text('%09d' % player_state.score) + self.labels['power'].set_text('%d' % player_state.power) + self.labels['graze'].set_text('%d' % player_state.graze) + self.labels['points'].set_text('%d' % player_state.points) diff --git a/pytouhou/ui/gamerunner.py b/pytouhou/ui/gamerunner.py --- a/pytouhou/ui/gamerunner.py +++ b/pytouhou/ui/gamerunner.py @@ -14,14 +14,16 @@ import pyglet import traceback +from itertools import chain -from pyglet.gl import (glMatrixMode, glLoadIdentity, glEnable, - glHint, glEnableClientState, glViewport, +from pyglet.gl import (glMatrixMode, glLoadIdentity, glEnable, glDisable, + glHint, glEnableClientState, glViewport, glScissor, gluPerspective, gluOrtho2D, GL_MODELVIEW, GL_PROJECTION, GL_TEXTURE_2D, GL_BLEND, GL_FOG, GL_PERSPECTIVE_CORRECTION_HINT, GL_FOG_HINT, GL_NICEST, - GL_COLOR_ARRAY, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY) + GL_COLOR_ARRAY, GL_VERTEX_ARRAY, GL_TEXTURE_COORD_ARRAY, + GL_SCISSOR_TEST) from pytouhou.utils.helpers import get_logger @@ -35,7 +37,7 @@ class GameRunner(pyglet.window.Window, G def __init__(self, resource_loader, game=None, background=None, replay=None): GameRenderer.__init__(self, resource_loader, game, background) - width, height = (game.width, game.height) if game else (None, None) + width, height = (game.interface.width, game.interface.height) if game else (None, None) pyglet.window.Window.__init__(self, width=width, height=height, caption='PyTouhou', resizable=False) @@ -51,13 +53,12 @@ class GameRunner(pyglet.window.Window, G self.game.players[0].state.bombs = self.replay_level.bombs self.game.difficulty = self.replay_level.difficulty - self.fps_display = pyglet.clock.ClockDisplay() + self.clock = pyglet.clock.get_default() def start(self, width=None, height=None): - width = width or (self.game.width if self.game else 640) - height = height or (self.game.height if self.game else 480) - + width = width or (self.game.interface.width if self.game else 640) + height = height or (self.game.interface.height if self.game else 480) if (width, height) != (self.width, self.height): self.set_size(width, height) @@ -77,14 +78,11 @@ class GameRunner(pyglet.window.Window, G pyglet.clock.tick() self.dispatch_events() self.update() - self.on_draw() + self.render_game() + self.render_interface() self.flip() - def on_resize(self, width, height): - glViewport(0, 0, width, height) - - def _event_text_symbol(self, ev): # XXX: Ugly workaround to a pyglet bug on X11 #TODO: fix that bug in pyglet @@ -141,23 +139,46 @@ class GameRunner(pyglet.window.Window, G self.game.run_iter(keystate) - def on_draw(self): + def render_game(self): # Switch to game projection #TODO: move that to GameRenderer? + x, y = self.game.interface.game_pos + glViewport(x, y, self.game.width, self.game.height) + glScissor(x, y, self.game.width, self.game.height) + glEnable(GL_SCISSOR_TEST) glMatrixMode(GL_PROJECTION) glLoadIdentity() - gluPerspective(30, float(self.width) / float(self.height), + gluPerspective(30, float(self.game.width) / float(self.game.height), 101010101./2010101., 101010101./10101.) GameRenderer.render(self) - # Get back to standard orthographic projection + glDisable(GL_SCISSOR_TEST) + + + def render_interface(self): + # Interface + interface = self.game.interface + interface.labels['framerate'].set_text('%.2ffps' % self.clock.get_fps()) + glMatrixMode(GL_PROJECTION) glLoadIdentity() glMatrixMode(GL_MODELVIEW) glLoadIdentity() + gluOrtho2D(0., float(self.width), float(self.height), 0.) + glViewport(0, 0, self.width, self.height) - #TODO: draw interface - gluOrtho2D(0., float(self.game.width), 0., float(self.game.height)) - self.fps_display.draw() + items = [item for item in interface.items if item._anmrunner and item._anmrunner._running] + labels = interface.labels + if items: + # Force rendering of labels + self.render_elements(items) + self.render_elements(chain(*(label.objects() + for label in labels.itervalues()))) + else: + self.render_elements(chain(*(label.objects() + for label in labels.itervalues() + if label._changed))) + for label in interface.labels.itervalues(): + label._changed = False