changeset 130:11ab06f4c4c6

Introduce characters!
author Thibaut Girka <thib@sitedethib.com>
date Sat, 10 Sep 2011 22:48:56 +0200
parents 284ac8f97a96
children fab7ad2f0d8b
files eclviewer.py pytouhou/game/character.py pytouhou/game/enemy.py pytouhou/game/game.py pytouhou/game/games.py pytouhou/game/player.py pytouhou/opengl/gamerenderer.py
diffstat 7 files changed, 161 insertions(+), 32 deletions(-) [+]
line wrap: on
line diff
--- a/eclviewer.py
+++ b/eclviewer.py
@@ -20,14 +20,14 @@ from pytouhou.resource.loader import Loa
 from pytouhou.game.background import Background
 from pytouhou.opengl.gamerenderer import GameRenderer
 from pytouhou.game.games import EoSDGame
-from pytouhou.game.player import Player
+from pytouhou.game.player import PlayerState
 
 
 def main(path, stage_num):
     resource_loader = Loader()
     resource_loader.scan_archives(os.path.join(path, name)
                                     for name in ('CM.DAT', 'ST.DAT'))
-    game = EoSDGame(resource_loader, [Player()], stage_num, 3, 16)
+    game = EoSDGame(resource_loader, [PlayerState()], stage_num, 3, 16)
 
     # Load stage data
     stage = resource_loader.get_stage('stage%d.std' % stage_num)
new file mode 100644
--- /dev/null
+++ b/pytouhou/game/character.py
@@ -0,0 +1,6 @@
+class Character(object):
+    def __init__(self, anm_wrapper, speed, focused_speed, hitbox_size):
+        self.anm_wrapper = anm_wrapper
+        self.speed = speed
+        self.focused_speed = focused_speed
+        self.hitbox_size = hitbox_size
--- a/pytouhou/game/enemy.py
+++ b/pytouhou/game/enemy.py
@@ -13,10 +13,6 @@
 ##
 
 
-from itertools import chain
-from io import BytesIO
-import os
-from struct import unpack, pack
 from pytouhou.utils.interpolator import Interpolator
 from pytouhou.vm.eclrunner import ECLRunner
 from pytouhou.vm.anmrunner import ANMRunner
--- a/pytouhou/game/game.py
+++ b/pytouhou/game/game.py
@@ -17,16 +17,19 @@ from pytouhou.utils.random import Random
 
 from pytouhou.vm.eclrunner import ECLMainRunner
 
+from pytouhou.game.player import Player
 from pytouhou.game.enemy import Enemy
 
 
 class GameState(object):
     __slots__ = ('resource_loader', 'bullets', 'players', 'rank', 'difficulty', 'frame',
-                 'stage', 'boss', 'prng', 'bullet_types')
-    def __init__(self, resource_loader, players, stage, rank, difficulty, bullet_types):
+                 'stage', 'boss', 'prng', 'bullet_types', 'characters')
+    def __init__(self, resource_loader, players, stage, rank, difficulty,
+                 bullet_types, characters):
         self.resource_loader = resource_loader
 
         self.bullet_types = bullet_types
+        self.characters = characters
 
         self.bullets = []
 
@@ -41,9 +44,13 @@ class GameState(object):
 
 
 class Game(object):
-    def __init__(self, resource_loader, players, stage, rank, difficulty, bullet_types):
-        self.game_state = GameState(resource_loader, players, stage, rank, difficulty, bullet_types)
+    def __init__(self, resource_loader, player_states, stage, rank, difficulty,
+                 bullet_types, characters):
+        self.game_state = GameState(resource_loader, player_states, stage,
+                                    rank, difficulty,
+                                    bullet_types, characters)
 
+        self.players = [Player(player_state, characters[player_state.character]) for player_state in player_states]
         self.enemies = []
 
         self.bonuses = []
@@ -68,6 +75,17 @@ class Game(object):
         self.enemies = [enemy for enemy in self.enemies if not enemy._removed]
 
         # 3. Let's play!
+        for player in self.players:
+            player.update(keystate) #TODO: differentiate keystates
+            if player.state.x < 8.:
+                player.state.x = 8.
+            if player.state.x > 384.-8: #TODO
+                player.state.x = 384.-8
+            if player.state.y < 16.:
+                player.state.y = 16.
+            if player.state.y > 448.-16: #TODO
+                player.state.y = 448.-16
+
         for enemy in self.enemies:
             enemy.update()
 
--- a/pytouhou/game/games.py
+++ b/pytouhou/game/games.py
@@ -13,6 +13,7 @@
 ##
 
 from pytouhou.game.game import Game
+from pytouhou.game.character import Character
 from pytouhou.game.bullettype import BulletType
 
 class EoSDGame(Game):
@@ -30,4 +31,13 @@ class EoSDGame(Game):
                         BulletType(etama3, 8, 13, 20, 20, 20),
                         BulletType(etama4, 0, 1, 2, 2, 2)]
 
-        Game.__init__(self, resource_loader, players, stage, rank, difficulty, bullet_types)
+        player00 = resource_loader.get_anm_wrapper(('player00.anm',))
+        player01 = resource_loader.get_anm_wrapper(('player01.anm',))
+        characters = [Character(player00, 4., 2., 4.),
+                      Character(player00, 4., 2., 4.),
+                      Character(player01, 5., 2.5, 5.),
+                      Character(player01, 5., 2.5, 5.)]
+
+        Game.__init__(self, resource_loader, players, stage, rank, difficulty,
+                      bullet_types, characters)
+
--- a/pytouhou/game/player.py
+++ b/pytouhou/game/player.py
@@ -13,13 +13,84 @@
 ##
 
 
-class Player(object):
-    def __init__(self):
+from math import cos, sin, atan2, pi
+
+from pytouhou.game.sprite import Sprite
+from pytouhou.vm.anmrunner import ANMRunner
+
+
+SQ2 = 2. ** 0.5 / 2.
+
+
+class PlayerState(object):
+    def __init__(self, character=0, score=0, power=0, lives=0, bombs=0):
+        self.character = character # ReimuA/ReimuB/MarisaA/MarisaB/...
+
+        self.score = score
+        self.lives = lives
+        self.bombs = bombs
+        self.power = power
+
+        self.graze = 0
+        self.points = 0
+
         self.x = 192.0
         self.y = 384.0
-        self.score = 0
-        self.graze = 0
-        self.power = 0
-        self.lives = 0
-        self.bombs = 0
-        self.character = 0 # ReimuA/ReimuB/MarisaA/MarisaB/...
+
+
+class Player(object):
+    def __init__(self, state, character):
+        self._sprite = None
+        self._anmrunner = None
+
+        self.state = state
+        self.character = character
+        self.anm_wrapper = character.anm_wrapper
+        self.direction = None
+
+        self.set_anim(0)
+
+
+    @property
+    def x(self):
+        return self.state.x
+
+
+    @property
+    def y(self):
+        return self.state.y
+
+
+    def set_anim(self, index):
+        self._sprite = Sprite()
+        self._anmrunner = ANMRunner(self.anm_wrapper, index, self._sprite)
+        self._anmrunner.run_frame()
+
+
+    def update(self, keystate):
+        try:
+            dx, dy = {16: (0.0, -1.0), 32: (0.0, 1.0), 64: (-1.0, 0.0), 128: (1.0, 0.0),
+                      16|64: (-SQ2, -SQ2), 16|128: (SQ2, -SQ2),
+                      32|64: (-SQ2, SQ2), 32|128:  (SQ2, SQ2)}[keystate & (16|32|64|128)]
+        except KeyError:
+            speed = 0.0
+            dx, dy = 0.0, 0.0
+        else:
+            speed = self.character.focused_speed if keystate & 4 else self.character.speed
+            dx, dy = dx * speed, dy * speed
+
+        if dx < 0 and self.direction != -1:
+            self.set_anim(1)
+            self.direction = -1
+        elif dx > 0 and self.direction != +1:
+            self.set_anim(3)
+            self.direction = +1
+        elif dx == 0 and self.direction is not None:
+            self.set_anim({-1: 2, +1: 4}[self.direction])
+            self.direction = None
+
+        self.state.x += dx
+        self.state.y += dy
+
+        self._anmrunner.run_frame()
+
--- a/pytouhou/opengl/gamerenderer.py
+++ b/pytouhou/opengl/gamerenderer.py
@@ -35,7 +35,7 @@ class GameRenderer(pyglet.window.Window)
 
         self.texture_manager = TextureManager(resource_loader)
 
-        self.fps_display =         pyglet.clock.ClockDisplay()
+        self.fps_display = pyglet.clock.ClockDisplay()
 
         self.game = game
         self.background = background
@@ -65,27 +65,54 @@ class GameRenderer(pyglet.window.Window)
                         ctypes.byref(buff, 3 * 4),
                         ctypes.byref(buff, 3 * 4 + 2 * 4))
 
-        pyglet.clock.schedule_interval(self.update, 1./120.)
-        pyglet.app.run()
+        # Use our own loop to ensure 60 (for now, 120) fps
+        pyglet.clock.set_fps_limit(120)
+        while not self.has_exit:
+            pyglet.clock.tick()
+            self.dispatch_events()
+            self.update()
+            self.on_draw()
+            self.flip()
 
 
     def on_resize(self, width, height):
         glViewport(0, 0, width, height)
 
 
-    def update(self, dt):
+    def on_key_press(self, symbol, modifiers):
+        if symbol == pyglet.window.key.ESCAPE:
+            self.has_exit = True
+        # XXX: Fullscreen will be enabled the day pyglet stops sucking
+        elif symbol == pyglet.window.key.F11:
+            self.set_fullscreen(not self.fullscreen)
+
+
+    def update(self):
         if self.background:
             self.background.update(self.game.game_state.frame)
         if self.game:
-            self.game.run_iter(0) #TODO: self.keys...
-
-
-    def on_key_press(self, symbol, modifiers):
-        if symbol == pyglet.window.key.ESCAPE:
-            pyglet.app.exit()
-        # XXX: Fullscreen will be enabled the day pyglet stops sucking
-        elif symbol == pyglet.window.key.F11:
-            self.set_fullscreen(not self.fullscreen)
+            #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...
 
 
     def render_elements(self, elements):
@@ -197,6 +224,7 @@ class GameRenderer(pyglet.window.Window)
             glDisable(GL_FOG)
             self.render_elements(game.enemies)
             self.render_elements(game.game_state.bullets)
+            self.render_elements(game.players)
             glEnable(GL_FOG)
 
         #TODO