Mercurial > touhou
changeset 597:244c99c568c8
Don’t hardcode the available games and interfaces, and relocation them.
| author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
|---|---|
| date | Sat, 25 Oct 2014 18:52:16 +0200 |
| parents | ab131d04987d |
| children | a7286a0facf9 |
| files | pytouhou/games/eosd.py pytouhou/games/eosd/__init__.py pytouhou/games/eosd/game.py pytouhou/games/eosd/interface.py pytouhou/games/sample/__init__.py pytouhou/games/sample/interface.py pytouhou/interfaces/__init__.py pytouhou/interfaces/eosd.py pytouhou/interfaces/sample.py pytouhou/options.py scripts/pytouhou |
| diffstat | 8 files changed, 431 insertions(+), 426 deletions(-) [+] |
line wrap: on
line diff
--- a/pytouhou/games/eosd.py Sat Oct 25 18:49:41 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,210 +0,0 @@ -# -*- 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 pytouhou.utils.interpolator import Interpolator - -from pytouhou.game.game import Game -from pytouhou.game.bullettype import BulletType -from pytouhou.game.lasertype import LaserType -from pytouhou.game.itemtype import ItemType -from pytouhou.game.player import Player -from pytouhou.game.orb import Orb -from pytouhou.game.background import Background - -from pytouhou.vm import ECLMainRunner - - -class EoSDCommon(object): - def __init__(self, resource_loader, player_characters, continues, stage, - width=384, height=448): - self.width, self.height = width, height - - self.etama = resource_loader.get_multi_anm(('etama3.anm', 'etama4.anm')) - self.bullet_types = [BulletType(self.etama[0], 0, 11, 14, 15, 16, hitbox_size=2, - type_id=0), - BulletType(self.etama[0], 1, 12, 17, 18, 19, hitbox_size=3, - type_id=1), - BulletType(self.etama[0], 2, 12, 17, 18, 19, hitbox_size=2, - type_id=2), - BulletType(self.etama[0], 3, 12, 17, 18, 19, hitbox_size=3, - type_id=3), - BulletType(self.etama[0], 4, 12, 17, 18, 19, hitbox_size=2.5, - type_id=4), - BulletType(self.etama[0], 5, 12, 17, 18, 19, hitbox_size=2, - type_id=5), - BulletType(self.etama[0], 6, 13, 20, 20, 20, hitbox_size=8, - launch_anim_offsets=(0, 1, 1, 2, 2, 3, 4, 0), - type_id=6), - BulletType(self.etama[0], 7, 13, 20, 20, 20, hitbox_size=5.5, - launch_anim_offsets=(1, 1, 1, 1), - type_id=7), - BulletType(self.etama[0], 8, 13, 20, 20, 20, hitbox_size=4.5, - launch_anim_offsets=(0, 1, 1, 2, 2, 3, 4, 0), - type_id=8), - BulletType(self.etama[1], 0, 1, 2, 2, 2, hitbox_size=16, - launch_anim_offsets=(0, 1, 2, 3), - type_id=9)] - - self.laser_types = [LaserType(self.etama[0], 9), - LaserType(self.etama[0], 10)] - - self.item_types = [ItemType(self.etama[0], 0, 7), #Power - ItemType(self.etama[0], 1, 8), #Point - ItemType(self.etama[0], 2, 9), #Big power - ItemType(self.etama[0], 3, 10), #Bomb - ItemType(self.etama[0], 4, 11), #Full power - ItemType(self.etama[0], 5, 12), #1up - ItemType(self.etama[0], 6, 13)] #Star - - self.enemy_face = [('face03a.anm', 'face03b.anm'), - ('face05a.anm',), - ('face06a.anm', 'face06b.anm'), - ('face08a.anm', 'face08b.anm'), - ('face09a.anm', 'face09b.anm'), - ('face09b.anm', 'face10a.anm', 'face10b.anm'), - ('face08a.anm', 'face12a.anm', 'face12b.anm', 'face12c.anm')] - - default_power = [0, 64, 128, 128, 128, 128, 0][stage] - - eosd_characters = resource_loader.get_eosd_characters() - self.first_character = player_characters[0] // 2 - self.player_anms = {} - self.players = [None] * len(player_characters) - for i, player_character in enumerate(player_characters): - character = player_character // 2 - if character not in self.player_anms: - face = resource_loader.get_multi_anm(('face0%da.anm' % character, - 'face0%db.anm' % character, - 'face0%dc.anm' % character)) - anm = resource_loader.get_single_anm('player0%d.anm' % character) - self.player_anms[character] = (anm, face) - - self.players[i] = EoSDPlayer(i, self.player_anms[character][0], - eosd_characters[player_character], - character, default_power, continues) - - - -class EoSDGame(Game): - def __init__(self, resource_loader, stage, rank, difficulty, - common, prng, hints=None, friendly_fire=True, - nb_bullets_max=640): - - self.etama = common.etama #XXX - try: - self.enm_anm = resource_loader.get_multi_anm(('stg%denm.anm' % stage, - 'stg%denm2.anm' % stage)) - except KeyError: - self.enm_anm = resource_loader.get_anm('stg%denm.anm' % stage) - ecl = resource_loader.get_ecl('ecldata%d.ecl' % stage) - self.ecl_runners = [ECLMainRunner(main, ecl.subs, self) for main in ecl.mains] - - self.spellcard_effect_anm = resource_loader.get_single_anm('eff0%d.anm' % stage) - - self.msg = resource_loader.get_msg('msg%d.dat' % stage) - msg_anm = [common.player_anms[common.first_character][1], #TODO: does it break bomb face of non-first player? - resource_loader.get_multi_anm(common.enemy_face[stage - 1])] - - self.msg_anm = [[], []] - for i, anms in enumerate(msg_anm): - for anm in anms: - for sprite in anm.sprites.values(): - self.msg_anm[i].append((anm, sprite)) - - for player in common.players: - player._game = self - - # Load stage data - self.std = resource_loader.get_stage('stage%d.std' % stage) - - background_anm = resource_loader.get_single_anm('stg%dbg.anm' % stage) - self.background = Background(self.std, background_anm) - - common.interface.start_stage(self, stage) - - Game.__init__(self, common.players, stage, rank, difficulty, - common.bullet_types, common.laser_types, - common.item_types, nb_bullets_max, common.width, - common.height, prng, common.interface, hints, - friendly_fire) - - try: - self.texts['stage_name'] = common.interface.stage_name - except AttributeError: - pass - - try: - self.texts['song_name'] = common.interface.song_name - except AttributeError: - pass - - - -class EoSDPlayer(Player): - def __init__(self, number, anm, shts, character, power, continues): - self.sht = shts[0] - self.focused_sht = shts[1] - - Player.__init__(self, number, anm, character, power, continues) - - self.orbs = [Orb(anm, 128, self), - Orb(anm, 129, self)] - - self.orbs[0].offset_x = -24 - self.orbs[1].offset_x = 24 - - self.orb_dx_interpolator = None - self.orb_dy_interpolator = None - - - def start_focusing(self): - self.orb_dx_interpolator = Interpolator((24,), self._game.frame, - (8,), self._game.frame + 8, - lambda x: x ** 2) - self.orb_dy_interpolator = Interpolator((0,), self._game.frame, - (-32,), self._game.frame + 8) - self.focused = True - - - def stop_focusing(self): - self.orb_dx_interpolator = Interpolator((8,), self._game.frame, - (24,), self._game.frame + 8, - lambda x: x ** 2) - self.orb_dy_interpolator = Interpolator((-32,), self._game.frame, - (0,), self._game.frame + 8) - self.focused = False - - - @property - def objects(self): - return [self] + (self.orbs if self.power >= 8 else []) - - - def update(self, keystate): - Player.update(self, keystate) - - if self.death_time == 0 or self._game.frame - self.death_time > 60: - if self.orb_dx_interpolator: - self.orb_dx_interpolator.update(self._game.frame) - dx, = self.orb_dx_interpolator.values - self.orbs[0].offset_x = -dx - self.orbs[1].offset_x = dx - if self.orb_dy_interpolator: - self.orb_dy_interpolator.update(self._game.frame) - dy, = self.orb_dy_interpolator.values - self.orbs[0].offset_y = dy - self.orbs[1].offset_y = dy - - for orb in self.orbs: - orb.update()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/games/eosd/game.py Sat Oct 25 18:52:16 2014 +0200 @@ -0,0 +1,210 @@ +# -*- 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 pytouhou.utils.interpolator import Interpolator + +from pytouhou.game.game import Game as GameBase +from pytouhou.game.bullettype import BulletType +from pytouhou.game.lasertype import LaserType +from pytouhou.game.itemtype import ItemType +from pytouhou.game.player import Player as PlayerBase +from pytouhou.game.orb import Orb +from pytouhou.game.background import Background + +from pytouhou.vm import ECLMainRunner + + +class Common(object): + def __init__(self, resource_loader, player_characters, continues, stage, + width=384, height=448): + self.width, self.height = width, height + + self.etama = resource_loader.get_multi_anm(('etama3.anm', 'etama4.anm')) + self.bullet_types = [BulletType(self.etama[0], 0, 11, 14, 15, 16, hitbox_size=2, + type_id=0), + BulletType(self.etama[0], 1, 12, 17, 18, 19, hitbox_size=3, + type_id=1), + BulletType(self.etama[0], 2, 12, 17, 18, 19, hitbox_size=2, + type_id=2), + BulletType(self.etama[0], 3, 12, 17, 18, 19, hitbox_size=3, + type_id=3), + BulletType(self.etama[0], 4, 12, 17, 18, 19, hitbox_size=2.5, + type_id=4), + BulletType(self.etama[0], 5, 12, 17, 18, 19, hitbox_size=2, + type_id=5), + BulletType(self.etama[0], 6, 13, 20, 20, 20, hitbox_size=8, + launch_anim_offsets=(0, 1, 1, 2, 2, 3, 4, 0), + type_id=6), + BulletType(self.etama[0], 7, 13, 20, 20, 20, hitbox_size=5.5, + launch_anim_offsets=(1, 1, 1, 1), + type_id=7), + BulletType(self.etama[0], 8, 13, 20, 20, 20, hitbox_size=4.5, + launch_anim_offsets=(0, 1, 1, 2, 2, 3, 4, 0), + type_id=8), + BulletType(self.etama[1], 0, 1, 2, 2, 2, hitbox_size=16, + launch_anim_offsets=(0, 1, 2, 3), + type_id=9)] + + self.laser_types = [LaserType(self.etama[0], 9), + LaserType(self.etama[0], 10)] + + self.item_types = [ItemType(self.etama[0], 0, 7), #Power + ItemType(self.etama[0], 1, 8), #Point + ItemType(self.etama[0], 2, 9), #Big power + ItemType(self.etama[0], 3, 10), #Bomb + ItemType(self.etama[0], 4, 11), #Full power + ItemType(self.etama[0], 5, 12), #1up + ItemType(self.etama[0], 6, 13)] #Star + + self.enemy_face = [('face03a.anm', 'face03b.anm'), + ('face05a.anm',), + ('face06a.anm', 'face06b.anm'), + ('face08a.anm', 'face08b.anm'), + ('face09a.anm', 'face09b.anm'), + ('face09b.anm', 'face10a.anm', 'face10b.anm'), + ('face08a.anm', 'face12a.anm', 'face12b.anm', 'face12c.anm')] + + default_power = [0, 64, 128, 128, 128, 128, 0][stage] + + eosd_characters = resource_loader.get_eosd_characters() + self.first_character = player_characters[0] // 2 + self.player_anms = {} + self.players = [None] * len(player_characters) + for i, player_character in enumerate(player_characters): + character = player_character // 2 + if character not in self.player_anms: + face = resource_loader.get_multi_anm(('face0%da.anm' % character, + 'face0%db.anm' % character, + 'face0%dc.anm' % character)) + anm = resource_loader.get_single_anm('player0%d.anm' % character) + self.player_anms[character] = (anm, face) + + self.players[i] = Player(i, self.player_anms[character][0], + eosd_characters[player_character], + character, default_power, continues) + + + +class Game(GameBase): + def __init__(self, resource_loader, stage, rank, difficulty, + common, prng, hints=None, friendly_fire=True, + nb_bullets_max=640): + + self.etama = common.etama #XXX + try: + self.enm_anm = resource_loader.get_multi_anm(('stg%denm.anm' % stage, + 'stg%denm2.anm' % stage)) + except KeyError: + self.enm_anm = resource_loader.get_anm('stg%denm.anm' % stage) + ecl = resource_loader.get_ecl('ecldata%d.ecl' % stage) + self.ecl_runners = [ECLMainRunner(main, ecl.subs, self) for main in ecl.mains] + + self.spellcard_effect_anm = resource_loader.get_single_anm('eff0%d.anm' % stage) + + self.msg = resource_loader.get_msg('msg%d.dat' % stage) + msg_anm = [common.player_anms[common.first_character][1], #TODO: does it break bomb face of non-first player? + resource_loader.get_multi_anm(common.enemy_face[stage - 1])] + + self.msg_anm = [[], []] + for i, anms in enumerate(msg_anm): + for anm in anms: + for sprite in anm.sprites.values(): + self.msg_anm[i].append((anm, sprite)) + + for player in common.players: + player._game = self + + # Load stage data + self.std = resource_loader.get_stage('stage%d.std' % stage) + + background_anm = resource_loader.get_single_anm('stg%dbg.anm' % stage) + self.background = Background(self.std, background_anm) + + common.interface.start_stage(self, stage) + + GameBase.__init__(self, common.players, stage, rank, difficulty, + common.bullet_types, common.laser_types, + common.item_types, nb_bullets_max, common.width, + common.height, prng, common.interface, hints, + friendly_fire) + + try: + self.texts['stage_name'] = common.interface.stage_name + except AttributeError: + pass + + try: + self.texts['song_name'] = common.interface.song_name + except AttributeError: + pass + + + +class Player(PlayerBase): + def __init__(self, number, anm, shts, character, power, continues): + self.sht = shts[0] + self.focused_sht = shts[1] + + PlayerBase.__init__(self, number, anm, character, power, continues) + + self.orbs = [Orb(anm, 128, self), + Orb(anm, 129, self)] + + self.orbs[0].offset_x = -24 + self.orbs[1].offset_x = 24 + + self.orb_dx_interpolator = None + self.orb_dy_interpolator = None + + + def start_focusing(self): + self.orb_dx_interpolator = Interpolator((24,), self._game.frame, + (8,), self._game.frame + 8, + lambda x: x ** 2) + self.orb_dy_interpolator = Interpolator((0,), self._game.frame, + (-32,), self._game.frame + 8) + self.focused = True + + + def stop_focusing(self): + self.orb_dx_interpolator = Interpolator((8,), self._game.frame, + (24,), self._game.frame + 8, + lambda x: x ** 2) + self.orb_dy_interpolator = Interpolator((-32,), self._game.frame, + (0,), self._game.frame + 8) + self.focused = False + + + @property + def objects(self): + return [self] + (self.orbs if self.power >= 8 else []) + + + def update(self, keystate): + PlayerBase.update(self, keystate) + + if self.death_time == 0 or self._game.frame - self.death_time > 60: + if self.orb_dx_interpolator: + self.orb_dx_interpolator.update(self._game.frame) + dx, = self.orb_dx_interpolator.values + self.orbs[0].offset_x = -dx + self.orbs[1].offset_x = dx + if self.orb_dy_interpolator: + self.orb_dy_interpolator.update(self._game.frame) + dy, = self.orb_dy_interpolator.values + self.orbs[0].offset_y = dy + self.orbs[1].offset_y = dy + + for orb in self.orbs: + orb.update()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/games/eosd/interface.py Sat Oct 25 18:52:16 2014 +0200 @@ -0,0 +1,156 @@ +# -*- encoding: utf-8 -*- +## +## Copyright (C) 2014 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 pytouhou.game.effect import Effect +from pytouhou.game.text import Text, Counter, Gauge, NativeText + + +class Interface(object): + width = 640 + height = 480 + game_pos = (32, 16) + + 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.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=b'0'), + 'score': Text((500, 82), self.ascii_anm, front, text=b'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=b'0'), + 'graze': Text((500, 206), self.ascii_anm, front, text=b'0'), + 'points': Text((500, 226), self.ascii_anm, front, text=b'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).encode() + elif stage == 6: + text = b'FINAL STAGE' + elif stage == 7: + text = b'EXTRA STAGE' + + self.stage_name = NativeText((192, 200), 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), '♪ ' + 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/games/sample/interface.py Sat Oct 25 18:52:16 2014 +0200 @@ -0,0 +1,48 @@ +# -*- encoding: utf-8 -*- +## +## Copyright (C) 2014 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. +## + +class Interface(object): + width = 384 + height = 448 + game_pos = (0, 0) + + def __init__(self, resource_loader, player_state): + self.game = None + self.player_state = player_state + self.ascii_anm = resource_loader.get_single_anm('ascii.anm') #XXX + + self.items = [] + self.level_start = [] + self.labels = {} + self.boss_items = [] + + + def start_stage(self, game, stage): + self.game = game + + + def set_song_name(self, name): + pass + + + def set_boss_life(self): + pass + + + def set_spell_life(self): + pass + + + def update(self): + pass
--- a/pytouhou/interfaces/eosd.py Sat Oct 25 18:49:41 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,156 +0,0 @@ -# -*- encoding: utf-8 -*- -## -## Copyright (C) 2014 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 pytouhou.game.effect import Effect -from pytouhou.game.text import Text, Counter, Gauge, NativeText - - -class EoSDInterface(object): - width = 640 - height = 480 - game_pos = (32, 16) - - 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.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=b'0'), - 'score': Text((500, 82), self.ascii_anm, front, text=b'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=b'0'), - 'graze': Text((500, 206), self.ascii_anm, front, text=b'0'), - 'points': Text((500, 226), self.ascii_anm, front, text=b'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).encode() - elif stage == 6: - text = b'FINAL STAGE' - elif stage == 7: - text = b'EXTRA STAGE' - - self.stage_name = NativeText((192, 200), 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), '♪ ' + 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
--- a/pytouhou/interfaces/sample.py Sat Oct 25 18:49:41 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -# -*- encoding: utf-8 -*- -## -## Copyright (C) 2014 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. -## - -class SampleInterface(object): - width = 384 - height = 448 - game_pos = (0, 0) - - def __init__(self, resource_loader, player_state): - self.game = None - self.player_state = player_state - self.ascii_anm = resource_loader.get_single_anm('ascii.anm') #XXX - - self.items = [] - self.level_start = [] - self.labels = {} - self.boss_items = [] - - - def start_stage(self, game, stage): - self.game = game - - - def set_song_name(self, name): - pass - - - def set_boss_life(self): - pass - - - def set_spell_life(self): - pass - - - def update(self): - pass
--- a/pytouhou/options.py Sat Oct 25 18:49:41 2014 +0200 +++ b/pytouhou/options.py Sat Oct 25 18:52:16 2014 +0200 @@ -125,8 +125,8 @@ game_group.add_argument('-r', '--rank', metavar='RANK', type=int, help='Rank, from 0 (Easy, default) to 3 (Lunatic).') game_group.add_argument('-c', '--character', metavar='CHARACTER', type=int, 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'], help='Select the game engine to use.') - game_group.add_argument('--interface', metavar='INTERFACE', choices=['EoSD', 'Sample'], help='Select the interface to use.') + game_group.add_argument('--game', metavar='GAME', help='Select the game engine to use.') + game_group.add_argument('--interface', metavar='INTERFACE', help='Select the interface to use.') game_group.add_argument('--hints', metavar='HINTS', help='Hints file, to display text while playing.') replay_group = parser.add_argument_group('Replay options')
--- a/scripts/pytouhou Sat Oct 25 18:49:41 2014 +0200 +++ b/scripts/pytouhou Sat Oct 25 18:52:16 2014 +0200 @@ -24,8 +24,8 @@ 'path': '.', 'rank': 0, 'character': 0, - 'game': 'EoSD', - 'interface': 'EoSD', + 'game': 'eosd', + 'interface': 'eosd', 'port': 0, 'backend': ['opengl', 'sdl'], 'gl-flavor': 'compatibility', @@ -57,14 +57,21 @@ else: menu(options, args) -if args.game == 'EoSD': - from pytouhou.games.eosd import EoSDCommon as Common, EoSDGame as Game +import sys +from importlib import import_module -if args.interface == 'EoSD': - from pytouhou.interfaces.eosd import EoSDInterface as Interface -elif args.interface == 'Sample': - from pytouhou.interfaces.sample import SampleInterface as Interface +def load_module(type_, name, items=None): + try: + module = import_module('pytouhou.games.%s.%s' % (name, type_)) + except ImportError: + logger.critical('Module “%s” doesn’t contain %s data, aborting.', name, type_) + sys.exit(1) + if items is None: + return module + return (getattr(module, item) for item in items) +Game, Common = load_module('game', args.game, ['Game', 'Common']) +Interface = load_module('interface', args.interface).Interface from pytouhou.lib.sdl import SDL, show_simple_message_box from pytouhou.ui.window import Window @@ -77,7 +84,6 @@ from pytouhou.network import Network -from importlib import import_module for backend_name in args.backend: if backend_name == 'opengl': options = { @@ -146,7 +152,6 @@ resource_loader.scan_archives(data) except IOError: show_simple_message_box(u'Some data files were not found, did you forget the -p option?') - import sys sys.exit(1) if stage_num is None:
