# HG changeset patch # User Emmanuel Gil Peyrot # Date 1400698613 -7200 # Node ID 1e9ea6519f3c2510667b563f6b81ecaa6c0df4ba # Parent e35bef07290d05eef6340b4501d31fc833377774 Make EoSDInterface separate from EoSD game. diff --git a/pytouhou/games/eosd.py b/pytouhou/games/eosd.py --- a/pytouhou/games/eosd.py +++ b/pytouhou/games/eosd.py @@ -20,8 +20,6 @@ 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, Counter, Gauge, NativeText from pytouhou.game.background import Background from pytouhou.vm import ECLMainRunner @@ -96,8 +94,6 @@ class EoSDCommon(object): eosd_characters[player_character], character, default_power, continues) - self.interface = EoSDInterface(resource_loader, self.players[0]) #XXX - class EoSDGame(Game): @@ -143,149 +139,15 @@ class EoSDGame(Game): common.height, prng, common.interface, hints, friendly_fire) - self.texts['stage_name'] = common.interface.stage_name - self.texts['song_name'] = common.interface.song_name - - - -class EoSDInterface(object): - def __init__(self, resource_loader, player_state): - self.game = None - self.player_state = player_state - front = resource_loader.get_single_anm('front.anm') - self.ascii_anm = resource_loader.get_single_anm('ascii.anm') - - self.width = 640 - self.height = 480 - self.game_pos = (32, 16) - - self.highscore = 1000000 #TODO: read score.dat - 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), i, front) for i in reversed(range(6))] + - [Effect((0, 0), i, front) for i in range(9, 16)]) - for item in self.items: - item.sprite.allow_dest_offset = True #XXX - - self.level_start = [] - - self.labels = { - 'highscore': Text((500, 58), self.ascii_anm, front, text='0'), - 'score': Text((500, 82), self.ascii_anm, front, text='0'), - 'player': Counter((500, 122), front, front, script=16, value=0), - 'bombs': Counter((500, 146), front, front, script=17, value=0), - 'power': Text((500, 186), self.ascii_anm, front, text='0'), - 'graze': Text((500, 206), self.ascii_anm, front, text='0'), - 'points': Text((500, 226), self.ascii_anm, front, text='0'), - 'framerate': Text((512, 464), self.ascii_anm, front), - 'debug?': Text((0, 464), self.ascii_anm, front), - - # Only when there is a boss. - 'boss_lives': Text((80, 16), self.ascii_anm), - 'timeout': Text((384, 16), self.ascii_anm), - } - self.labels['boss_lives'].set_color('yellow') - - self.boss_items = [ - Effect((0, 0), 19, front), # Enemy - Gauge((100, 24), front), # Gauge - Gauge((100, 24), front), # Spellcard gauge - ] - for item in self.boss_items: - item.sprite.allow_dest_offset = True #XXX - - - def start_stage(self, game, stage): - self.game = game - if stage < 6: - text = 'STAGE %d' % stage - elif stage == 6: - text = 'FINAL STAGE' - elif stage == 7: - text = 'EXTRA STAGE' - - self.stage_name = NativeText((192, 200), unicode(game.std.name), shadow=True, align='center') - self.stage_name.set_timeout(240, effect='fadeout', duration=60, start=120) - - self.set_song_name(game.std.bgms[0][0]) - - self.level_start = [Text((16+384/2, 200), self.ascii_anm, text=text, align='center')] #TODO: find the exact location. - self.level_start[0].set_timeout(240, effect='fadeout', duration=60, start=120) - self.level_start[0].set_color('yellow') + try: + self.texts['stage_name'] = common.interface.stage_name + except AttributeError: + pass - - def set_song_name(self, name): - #TODO: use the correct animation. - self.song_name = NativeText((384, 432), u'♪ ' + name, shadow=True, align='right') - self.song_name.set_timeout(240, effect='fadeout', duration=60, start=120) - - - def set_boss_life(self): - if not self.game.boss: - return - self.boss_items[1].maximum = self.game.boss.life or 1 - self.boss_items[2].maximum = self.game.boss.life or 1 - - - def set_spell_life(self): - self.boss_items[2].set_value(self.game.boss.low_life_trigger if self.game.boss else 0) - - - def update(self): - for elem in self.items: - elem.update() - - for elem in self.level_start: - elem.update() - if elem.removed: #XXX - self.level_start = [] - - player_state = self.player_state - - self.highscore = max(self.highscore, player_state.effective_score) - self.labels['highscore'].set_text('%09d' % self.highscore) - self.labels['score'].set_text('%09d' % player_state.effective_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) - self.labels['player'].set_value(player_state.lives) - self.labels['bombs'].set_value(player_state.bombs) - - if self.game.boss: - boss = self.game.boss - - life_gauge = self.boss_items[1] - life_gauge.set_value(boss.life) - - spell_gauge = self.boss_items[2] - spell_gauge.sprite.color = (255, 192, 192) - if boss.life < spell_gauge.value: - spell_gauge.set_value(boss.life) - - for item in self.boss_items: - item.update() - - self.labels['boss_lives'].set_text('%d' % boss.remaining_lives) - self.labels['boss_lives'].changed = True - - timeout = min((boss.timeout - boss.frame) // 60, 99) - timeout_label = self.labels['timeout'] - if timeout >= 20: - timeout_label.set_color('blue') - elif timeout >= 10: - timeout_label.set_color('darkblue') - else: - if timeout >= 5: - timeout_label.set_color('purple') - else: - timeout_label.set_color('red') - if (boss.timeout - boss.frame) % 60 == 0 and boss.timeout != 0: - self.game.sfx_player.set_volume('timeout.wav', 1.) - self.game.sfx_player.play('timeout.wav') - timeout_label.set_text('%02d' % (timeout if timeout >= 0 else 0)) - timeout_label.changed = True + try: + self.texts['song_name'] = common.interface.song_name + except AttributeError: + pass diff --git a/pytouhou/interfaces/__init__.py b/pytouhou/interfaces/__init__.py new file mode 100644 diff --git a/pytouhou/interfaces/eosd.py b/pytouhou/interfaces/eosd.py new file mode 100644 --- /dev/null +++ b/pytouhou/interfaces/eosd.py @@ -0,0 +1,156 @@ +# -*- encoding: utf-8 -*- +## +## Copyright (C) 2014 Emmanuel Gil Peyrot +## +## 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 pytouhou.game.effect import Effect +from pytouhou.game.text import Text, Counter, Gauge, NativeText + + +class EoSDInterface(object): + def __init__(self, resource_loader, player_state): + self.game = None + self.player_state = player_state + front = resource_loader.get_single_anm('front.anm') + self.ascii_anm = resource_loader.get_single_anm('ascii.anm') + + self.width = 640 + self.height = 480 + self.game_pos = (32, 16) + + self.highscore = 1000000 #TODO: read score.dat + 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), i, front) for i in reversed(range(6))] + + [Effect((0, 0), i, front) for i in range(9, 16)]) + for item in self.items: + item.sprite.allow_dest_offset = True #XXX + + self.level_start = [] + + self.labels = { + 'highscore': Text((500, 58), self.ascii_anm, front, text='0'), + 'score': Text((500, 82), self.ascii_anm, front, text='0'), + 'player': Counter((500, 122), front, front, script=16, value=0), + 'bombs': Counter((500, 146), front, front, script=17, value=0), + 'power': Text((500, 186), self.ascii_anm, front, text='0'), + 'graze': Text((500, 206), self.ascii_anm, front, text='0'), + 'points': Text((500, 226), self.ascii_anm, front, text='0'), + 'framerate': Text((512, 464), self.ascii_anm, front), + 'debug?': Text((0, 464), self.ascii_anm, front), + + # Only when there is a boss. + 'boss_lives': Text((80, 16), self.ascii_anm), + 'timeout': Text((384, 16), self.ascii_anm), + } + self.labels['boss_lives'].set_color('yellow') + + self.boss_items = [ + Effect((0, 0), 19, front), # Enemy + Gauge((100, 24), front), # Gauge + Gauge((100, 24), front), # Spellcard gauge + ] + for item in self.boss_items: + item.sprite.allow_dest_offset = True #XXX + + + def start_stage(self, game, stage): + self.game = game + if stage < 6: + text = 'STAGE %d' % stage + elif stage == 6: + text = 'FINAL STAGE' + elif stage == 7: + text = 'EXTRA STAGE' + + self.stage_name = NativeText((192, 200), unicode(game.std.name), shadow=True, align='center') + self.stage_name.set_timeout(240, effect='fadeout', duration=60, start=120) + + self.set_song_name(game.std.bgms[0][0]) + + self.level_start = [Text((16+384/2, 200), self.ascii_anm, text=text, align='center')] #TODO: find the exact location. + self.level_start[0].set_timeout(240, effect='fadeout', duration=60, start=120) + self.level_start[0].set_color('yellow') + + + def set_song_name(self, name): + #TODO: use the correct animation. + self.song_name = NativeText((384, 432), u'♪ ' + name, shadow=True, align='right') + self.song_name.set_timeout(240, effect='fadeout', duration=60, start=120) + + + def set_boss_life(self): + if not self.game.boss: + return + self.boss_items[1].maximum = self.game.boss.life or 1 + self.boss_items[2].maximum = self.game.boss.life or 1 + + + def set_spell_life(self): + self.boss_items[2].set_value(self.game.boss.low_life_trigger if self.game.boss else 0) + + + def update(self): + for elem in self.items: + elem.update() + + for elem in self.level_start: + elem.update() + if elem.removed: #XXX + self.level_start = [] + + player_state = self.player_state + + self.highscore = max(self.highscore, player_state.effective_score) + self.labels['highscore'].set_text('%09d' % self.highscore) + self.labels['score'].set_text('%09d' % player_state.effective_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) + self.labels['player'].set_value(player_state.lives) + self.labels['bombs'].set_value(player_state.bombs) + + if self.game.boss: + boss = self.game.boss + + life_gauge = self.boss_items[1] + life_gauge.set_value(boss.life) + + spell_gauge = self.boss_items[2] + spell_gauge.sprite.color = (255, 192, 192) + if boss.life < spell_gauge.value: + spell_gauge.set_value(boss.life) + + for item in self.boss_items: + item.update() + + self.labels['boss_lives'].set_text('%d' % boss.remaining_lives) + self.labels['boss_lives'].changed = True + + timeout = min((boss.timeout - boss.frame) // 60, 99) + timeout_label = self.labels['timeout'] + if timeout >= 20: + timeout_label.set_color('blue') + elif timeout >= 10: + timeout_label.set_color('darkblue') + else: + if timeout >= 5: + timeout_label.set_color('purple') + else: + timeout_label.set_color('red') + if (boss.timeout - boss.frame) % 60 == 0 and boss.timeout != 0: + self.game.sfx_player.set_volume('timeout.wav', 1.) + self.game.sfx_player.play('timeout.wav') + timeout_label.set_text('%02d' % (timeout if timeout >= 0 else 0)) + timeout_label.changed = True diff --git a/scripts/pytouhou b/scripts/pytouhou --- a/scripts/pytouhou +++ b/scripts/pytouhou @@ -38,6 +38,7 @@ game_group.add_argument('-r', '--rank', game_group.add_argument('-c', '--character', metavar='CHARACTER', type=int, default=0, help='Select the character to use, from 0 (ReimuA, default) to 3 (MarisaB).') game_group.add_argument('-b', '--boss-rush', action='store_true', help='Fight only bosses.') game_group.add_argument('--game', metavar='GAME', choices=['EoSD'], default='EoSD', help='Select the game engine to use.') +game_group.add_argument('--interface', metavar='INTERFACE', choices=['EoSD'], default='EoSD', help='Select the interface to use.') game_group.add_argument('--hints', metavar='HINTS', default=None, help='Hints file, to display text while playing.') replay_group = parser.add_argument_group('Replay options') @@ -69,6 +70,9 @@ import logging if args.game == 'EoSD': from pytouhou.games.eosd import EoSDCommon as Common, EoSDGame as Game +if args.interface == 'EoSD': + from pytouhou.interfaces.eosd import EoSDInterface as Interface + from pytouhou.lib.sdl import SDL, show_simple_message_box from pytouhou.ui.window import Window @@ -188,6 +192,8 @@ def main(window, path, data, stage_num, game_class = GameBossRush if boss_rush else Game common = Common(resource_loader, characters, continues, stage_num - 1) + interface = Interface(resource_loader, common.players[0]) #XXX + common.interface = interface #XXX renderer = GameRenderer(resource_loader, window) runner = GameRunner(window, renderer, common, resource_loader, skip_replay, con) window.set_runner(runner)