changeset 404:6c0cb3eee33e

Add MoF’s hints support, and fix the Text timeout interface.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Sun, 24 Mar 2013 10:29:37 +0100
parents 9589a01e6edf
children 402e96a0baeb
files eosd pytouhou/formats/hint.py pytouhou/game/game.py pytouhou/game/text.py pytouhou/games/eosd.py
diffstat 5 files changed, 80 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/eosd
+++ b/eosd
@@ -30,6 +30,7 @@ from pytouhou.game.player import PlayerS
 from pytouhou.formats.t6rp import T6RP, Level
 from pytouhou.utils.random import Random
 from pytouhou.vm.msgrunner import NextStage
+from pytouhou.formats.hint import Hint
 
 
 class EoSDGameBossRush(EoSDGame):
@@ -64,7 +65,7 @@ class EoSDGameBossRush(EoSDGame):
 
 def main(path, data, stage_num, rank, character, replay, save_filename,
          skip_replay, boss_rush, fps_limit, single_buffer, debug,
-         fixed_pipeline, display_background):
+         fixed_pipeline, display_background, hints):
 
     resource_loader = Loader(path)
 
@@ -98,6 +99,10 @@ def main(path, data, stage_num, rank, ch
         save_replay.rank = rank
         save_replay.character = character
 
+    if hints:
+        with open(hints, 'rb') as file:
+            hints = Hint.read(file)
+
     difficulty = 16
     default_power = [0, 64, 128, 128, 128, 128, 0][stage_num - 1]
     states = [PlayerState(character=character, power=default_power)]
@@ -139,10 +144,12 @@ def main(path, data, stage_num, rank, ch
                 level.difficulty = difficulty
             save_keystates = []
 
+        hints_stage = hints.stages[stage_num - 1] if hints else None
+
         # Load stage data
         stage = resource_loader.get_stage('stage%d.std' % stage_num)
 
-        game = game_class(resource_loader, states, stage_num, rank, difficulty, prng=prng, continues=continues)
+        game = game_class(resource_loader, states, stage_num, rank, difficulty, prng=prng, continues=continues, hints=hints_stage)
 
         if display_background:
             background_anm_wrapper = resource_loader.get_anm_wrapper(('stg%dbg.anm' % stage_num,))
@@ -201,10 +208,11 @@ parser.add_argument('--fps-limit', metav
 parser.add_argument('--debug', action='store_true', help='Set unlimited continues, and perhaps other debug features.')
 parser.add_argument('--fixed-pipeline', action='store_true', help='Use the fixed pipeline instead of the new programmable one.')
 parser.add_argument('--no-background', action='store_false', help='Disable background display (huge performance boost on slow systems).')
+parser.add_argument('--hints', metavar='HINTS', default=None, help='Hints file, to display text while playing.')
 
 args = parser.parse_args()
 
 main(args.path, tuple(args.data), args.stage, args.rank, args.character,
      args.replay, args.save_replay, args.skip_replay, args.boss_rush,
      args.fps_limit, args.single_buffer, args.debug, args.fixed_pipeline,
-     args.no_background)
+     args.no_background, args.hints)
--- a/pytouhou/formats/hint.py
+++ b/pytouhou/formats/hint.py
@@ -76,6 +76,7 @@ class Hint(object):
         stage_mode = False
         tip_mode = False
         stage = None
+        tip = None
         hints = cls()
         stages = hints.stages
 
--- a/pytouhou/game/game.py
+++ b/pytouhou/game/game.py
@@ -34,7 +34,7 @@ class Game(object):
     def __init__(self, resource_loader, players, stage, rank, difficulty,
                  bullet_types, laser_types, item_types,
                  nb_bullets_max=None, width=384, height=448, prng=None,
-                 interface=None, continues=0):
+                 interface=None, continues=0, hints=None):
         self.resource_loader = resource_loader
 
         self.width, self.height = width, height
@@ -56,6 +56,7 @@ class Game(object):
         self.labels = []
         self.faces = [None, None]
         self.interface = interface
+        self.hints = hints
 
         self.continues = continues
         self.stage = stage
@@ -166,7 +167,7 @@ class Game(object):
         score = 0
         bonus = 2000
         for bullet in self.bullets:
-            label = self.new_label((bullet.x, bullet.y), str(bonus))
+            self.new_label((bullet.x, bullet.y), str(bonus))
             score += bonus
             bonus += 10
         self.bullets = []
@@ -211,7 +212,20 @@ class Game(object):
 
     def new_label(self, pos, text):
         label = Text(pos, self.ascii_wrapper, text=text, xspacing=8, shift=48)
-        label.set_timeout(60)
+        label.set_timeout(60, effect='move')
+        self.labels.append(label)
+        return label
+
+
+    def new_hint(self, hint):
+        pos = hint['Pos']
+        #TODO: Scale
+
+        pos = pos[0] + 192, pos[1]
+        label = Text(pos, self.ascii_wrapper, text=hint['Text'], align=hint['Align'])
+        label.set_timeout(hint['Time'])
+        label.set_alpha(hint['Alpha'])
+        label.set_color(hint['Color'], text=False)
         self.labels.append(label)
         return label
 
@@ -255,6 +269,8 @@ class Game(object):
         for laser in self.lasers: #TODO: what priority is it?
             laser.update()
         self.interface.update() # Pri 12
+        if self.hints:
+            self.update_hints() # Not from this game, so unknown.
         for label in self.labels: #TODO: what priority is it?
             label.update()
         self.update_faces() # Pri XXX
@@ -307,6 +323,12 @@ class Game(object):
             effect.update()
 
 
+    def update_hints(self):
+        for hint in self.hints:
+            if hint['Count'] == self.frame and hint['Base'] == 'start':
+                self.new_hint(hint)
+
+
     def update_faces(self):
         for face in self.faces:
             if face:
--- a/pytouhou/game/text.py
+++ b/pytouhou/game/text.py
@@ -86,15 +86,39 @@ class GlyphCollection(Widget):
             glyph.sprite.changed = True
 
 
+    def set_color(self, color, text=True):
+        if text:
+            colors = {'white': (255, 255, 255), 'yellow': (255, 255, 0),
+                      'blue': (192, 192, 255), 'darkblue': (160, 128, 255),
+                      'purple': (224, 128, 255), 'red': (255, 64, 0)}
+            color = colors[color]
+        self.ref_sprite.color = color
+        for glyph in self.glyphes:
+            glyph.sprite.color = color
+
+
+    def set_alpha(self, alpha):
+        self.ref_sprite.alpha = alpha
+        for glyph in self.glyphes:
+            glyph.sprite.alpha = alpha
+
+
 
 class Text(GlyphCollection):
     def __init__(self, pos, ascii_wrapper, back_wrapper=None, text='',
-                 xspacing=14, shift=21, back_script=22):
+                 xspacing=14, shift=21, back_script=22, align='left'):
         GlyphCollection.__init__(self, pos, ascii_wrapper, back_wrapper,
                                  xspacing=xspacing, back_script=back_script)
         self.text = ''
         self.shift = shift
 
+        if align == 'center':
+            self.x -= xspacing * len(text) // 2
+        elif align == 'right':
+            self.x -= xspacing * len(text)
+        else:
+            assert align == 'left'
+
         self.set_text(text)
 
 
@@ -112,13 +136,10 @@ class Text(GlyphCollection):
         self.changed = True
 
 
-    def set_color(self, color):
-        colors = {'white': (255, 255, 255), 'yellow': (255, 255, 0),
-                  'blue': (192, 192, 255), 'darkblue': (160, 128, 255),
-                  'purple': (224, 128, 255), 'red': (255, 64, 0)}
-        self.ref_sprite.color = colors[color]
-        for glyph in self.glyphes:
-            glyph.sprite.color = colors[color]
+    def timeout_update(self):
+        GlyphCollection.update(self)
+        if self.frame == self.timeout:
+            self.removed = True
 
 
     def move_timeout_update(self):
@@ -134,9 +155,9 @@ class Text(GlyphCollection):
         GlyphCollection.update(self)
         if self.frame >= self.start:
             if self.frame == self.start:
-                self.fade(self.effect, 255, lambda x: x)
-            elif self.frame == self.timeout - self.effect:
-                self.fade(self.effect, 0, lambda x: x)
+                self.fade(self.duration, 255, lambda x: x)
+            elif self.frame == self.timeout - self.duration:
+                self.fade(self.duration, 0, lambda x: x)
             if self.fade_interpolator:
                 self.fade_interpolator.update(self.frame)
                 self.alpha = int(self.fade_interpolator.values[0])
@@ -153,18 +174,21 @@ class Text(GlyphCollection):
                                               formula)
 
 
-    def set_timeout(self, timeout, effect=None, start=0):
-        if effect == None: #XXX
+    def set_timeout(self, timeout, effect=None, duration=0, start=0):
+        if effect == 'move':
             self.update = self.move_timeout_update
             self.timeout = timeout + start
-        else:
+        elif effect == 'fadeout':
             self.alpha = 0
             for glyph in self.glyphes:
                 glyph.sprite.alpha = 0
             self.update = self.fadeout_timeout_update
-            self.effect = effect
+            self.duration = duration
             self.start = start
             self.timeout = timeout + start
+        else:
+            self.update = self.timeout_update
+            self.timeout = timeout + start
 
 
 
--- a/pytouhou/games/eosd.py
+++ b/pytouhou/games/eosd.py
@@ -30,7 +30,8 @@ SQ2 = 2. ** 0.5 / 2.
 class EoSDGame(Game):
     def __init__(self, resource_loader, player_states, stage, rank, difficulty,
                  bullet_types=None, laser_types=None, item_types=None,
-                 nb_bullets_max=640, width=384, height=448, prng=None, continues=0):
+                 nb_bullets_max=640, width=384, height=448, prng=None,
+                 continues=0, hints=None):
 
         if not bullet_types:
             etama3 = resource_loader.get_anm_wrapper(('etama3.anm',))
@@ -96,7 +97,7 @@ class EoSDGame(Game):
 
         Game.__init__(self, resource_loader, players, stage, rank, difficulty,
                       bullet_types, laser_types, item_types, nb_bullets_max,
-                      width, height, prng, interface, continues)
+                      width, height, prng, interface, continues, hints)
 
 
 
@@ -121,7 +122,7 @@ class EoSDInterface(object):
             item.sprite.allow_dest_offset = True #XXX
 
         self.level_start = [Text((176, 200), ascii_wrapper, text='STAGE %d' % game.stage)] #TODO: find the exact location.
-        self.level_start[0].set_timeout(240, effect=60, start=120)
+        self.level_start[0].set_timeout(240, effect='fadeout', duration=60, start=120)
         self.level_start[0].set_color('yellow')
         #TODO: use the system text for the stage name, and the song name.