changeset 196:1e501e3b6645

Add a subclass for each character, and implement player attacks.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Sat, 29 Oct 2011 14:21:42 -0700
parents 52c0b9399413
children e1bc8c4cbb1a
files eclviewer.py pytouhou/game/character.py pytouhou/game/game.py pytouhou/game/games.py pytouhou/game/player.py pytouhou/games/__init__.py pytouhou/games/eosd.py
diffstat 7 files changed, 282 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/eclviewer.py
+++ b/eclviewer.py
@@ -22,7 +22,7 @@ pyximport.install()
 from pytouhou.resource.loader import Loader
 from pytouhou.game.background import Background
 from pytouhou.opengl.gamerunner import GameRunner
-from pytouhou.game.games import EoSDGame
+from pytouhou.games.eosd import EoSDGame
 from pytouhou.game.player import PlayerState
 from pytouhou.formats.t6rp import T6RP
 
deleted file mode 100644
--- a/pytouhou/game/character.py
+++ /dev/null
@@ -1,7 +0,0 @@
-class Character(object):
-    def __init__(self, anm_wrapper, speed, focused_speed, hitbox_size, graze_hitbox_size):
-        self.anm_wrapper = anm_wrapper
-        self.speed = speed
-        self.focused_speed = focused_speed
-        self.hitbox_size = hitbox_size
-        self.graze_hitbox_size = graze_hitbox_size
--- a/pytouhou/game/game.py
+++ b/pytouhou/game/game.py
@@ -26,16 +26,15 @@ from pytouhou.game.effect import Particl
 
 
 class Game(object):
-    def __init__(self, resource_loader, player_states, stage, rank, difficulty,
-                 bullet_types, item_types, characters, prng=None, nb_bullets_max=None):
+    def __init__(self, resource_loader, players, stage, rank, difficulty,
+                 bullet_types, item_types, prng=None, nb_bullets_max=None):
         self.resource_loader = resource_loader
 
         self.nb_bullets_max = nb_bullets_max
         self.bullet_types = bullet_types
         self.item_types = item_types
-        self.characters = characters
 
-        self.players = [Player(player_state, characters[player_state.character], self) for player_state in player_states]
+        self.players = players
         self.enemies = []
         self.effects = []
         self.bullets = []
@@ -144,7 +143,8 @@ class Game(object):
                         or by2 < ey1 or by1 > ey2):
                     bullet.collide()
                     enemy.on_attack(bullet)
-                    player.state.score += 90 # found experimentally
+                    #TODO: place that at the right place.
+                    #player.state.score += 90 # found experimentally
 
 
     def update_players(self, keystate):
deleted file mode 100644
--- a/pytouhou/game/games.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# -*- encoding: utf-8 -*-
-##
-## Copyright (C) 2011 Thibaut Girka <thib@sitedethib.com>
-##
-## 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.game import Game
-from pytouhou.game.character import Character
-from pytouhou.game.bullettype import BulletType
-from pytouhou.game.itemtype import ItemType
-
-class EoSDGame(Game):
-    def __init__(self, resource_loader, players, stage, rank, difficulty, **kwargs):
-        etama3 = resource_loader.get_anm_wrapper(('etama3.anm',))
-        etama4 = resource_loader.get_anm_wrapper(('etama4.anm',))
-        bullet_types = [BulletType(etama3, 0, 11, 14, 15, 16, hitbox_size=4),
-                        BulletType(etama3, 1, 12, 17, 18, 19, hitbox_size=6),
-                        BulletType(etama3, 2, 12, 17, 18, 19, hitbox_size=4),
-                        BulletType(etama3, 3, 12, 17, 18, 19, hitbox_size=6),
-                        BulletType(etama3, 4, 12, 17, 18, 19, hitbox_size=5),
-                        BulletType(etama3, 5, 12, 17, 18, 19, hitbox_size=4),
-                        BulletType(etama3, 6, 13, 20, 20, 20, hitbox_size=16),
-                        BulletType(etama3, 7, 13, 20, 20, 20, hitbox_size=11),
-                        BulletType(etama3, 8, 13, 20, 20, 20, hitbox_size=9),
-                        BulletType(etama4, 0, 1, 2, 2, 2, hitbox_size=32)]
-        #TODO: hitbox
-        item_types = [ItemType(etama3, 0, 7,  hitbox_size=42, score=10), #Power
-                      ItemType(etama3, 1, 8,  hitbox_size=42, score=100000), #Point, TODO: changes for hard, lunatic and poc
-                      ItemType(etama3, 2, 9,  hitbox_size=42, score=10), #Big power
-                      ItemType(etama3, 3, 10, hitbox_size=42), #Bomb
-                      ItemType(etama3, 4, 11, hitbox_size=42, score=1000), #Full power
-                      ItemType(etama3, 5, 12, hitbox_size=42), #1up
-                      ItemType(etama3, 6, 13, hitbox_size=42, score=500)] #Star
-
-        player00 = resource_loader.get_anm_wrapper(('player00.anm',))
-        player01 = resource_loader.get_anm_wrapper(('player01.anm',))
-        characters = [Character(player00, 4., 2., 2.5, 42.),
-                      Character(player00, 4., 2., 2.5, 42.),
-                      Character(player01, 5., 2.5, 2.5, 42.),
-                      Character(player01, 5., 2.5, 2.5, 42.)]
-
-        Game.__init__(self, resource_loader, players, stage, rank, difficulty,
-                      bullet_types, item_types, characters, nb_bullets_max=640, **kwargs)
-
--- a/pytouhou/game/player.py
+++ b/pytouhou/game/player.py
@@ -15,6 +15,9 @@
 
 from pytouhou.game.sprite import Sprite
 from pytouhou.vm.anmrunner import ANMRunner
+from pytouhou.game.bullettype import BulletType
+
+from math import pi
 
 
 SQ2 = 2. ** 0.5 / 2.
@@ -40,17 +43,27 @@ class PlayerState(object):
 
 
 class Player(object):
-    def __init__(self, state, character, game):
+    def __init__(self, state, game, anm_wrapper, speed=4., hitbox_size=2.5, graze_hitbox_size=42.):
         self._sprite = None
         self._anmrunner = None
         self._game = game
+        self.anm_wrapper = anm_wrapper
 
-        self.hitbox_half_size = character.hitbox_size / 2.
-        self.graze_hitbox_half_size = character.graze_hitbox_size / 2.
+        self.speed = speed
+        self.focused_speed = speed/2.
+
+        self.hitbox_size = hitbox_size
+        self.hitbox_half_size = self.hitbox_size / 2.
+        self.graze_hitbox_size = graze_hitbox_size
+        self.graze_hitbox_half_size = self.graze_hitbox_size / 2.
+
+        self.bullet_type = BulletType(anm_wrapper, 64, 96, 0, 0, 0, hitbox_size=4, damage=48)
+        self.bullet_launch_interval = 5
+        self.bullet_speed = 12.
+        self.bullet_launch_angle = -pi/2
+        self.fire_time = 0
 
         self.state = state
-        self.character = character
-        self.anm_wrapper = character.anm_wrapper
         self.direction = None
 
         self.set_anim(0)
@@ -98,7 +111,7 @@ class Player(object):
                 speed = 0.0
                 dx, dy = 0.0, 0.0
             else:
-                speed = self.character.focused_speed if keystate & 4 else self.character.speed
+                speed = self.focused_speed if keystate & 4 else self.speed
                 dx, dy = dx * speed, dy * speed
 
             if dx < 0 and self.direction != -1:
@@ -125,6 +138,12 @@ class Player(object):
                     self._sprite.color = (64, 64, 64)
                     self._sprite._changed = True
 
+            if keystate & 1 and self.fire_time == 0:
+                self.fire_time = 30
+            if self.fire_time > 0:
+                self.fire()
+                self.fire_time -= 1
+
         if self.death_time:
             time = self._game.frame - self.death_time
             if time == 6: # too late, you are dead :(
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/pytouhou/games/eosd.py
@@ -0,0 +1,251 @@
+# -*- 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.game.game import Game
+from pytouhou.game.bullettype import BulletType
+from pytouhou.game.itemtype import ItemType
+from pytouhou.game.player import Player
+from pytouhou.game.bullet import Bullet
+
+from math import pi
+
+
+class EoSDGame(Game):
+    def __init__(self, resource_loader, player_states, stage, rank, difficulty, **kwargs):
+        etama3 = resource_loader.get_anm_wrapper(('etama3.anm',))
+        etama4 = resource_loader.get_anm_wrapper(('etama4.anm',))
+        bullet_types = [BulletType(etama3, 0, 11, 14, 15, 16, hitbox_size=4),
+                        BulletType(etama3, 1, 12, 17, 18, 19, hitbox_size=6),
+                        BulletType(etama3, 2, 12, 17, 18, 19, hitbox_size=4),
+                        BulletType(etama3, 3, 12, 17, 18, 19, hitbox_size=6),
+                        BulletType(etama3, 4, 12, 17, 18, 19, hitbox_size=5),
+                        BulletType(etama3, 5, 12, 17, 18, 19, hitbox_size=4),
+                        BulletType(etama3, 6, 13, 20, 20, 20, hitbox_size=16),
+                        BulletType(etama3, 7, 13, 20, 20, 20, hitbox_size=11),
+                        BulletType(etama3, 8, 13, 20, 20, 20, hitbox_size=9),
+                        BulletType(etama4, 0, 1, 2, 2, 2, hitbox_size=32)]
+        #TODO: hitbox
+        item_types = [ItemType(etama3, 0, 7,  hitbox_size=42, score=10), #Power
+                      ItemType(etama3, 1, 8,  hitbox_size=42, score=100000), #Point, TODO: changes for hard, lunatic and poc
+                      ItemType(etama3, 2, 9,  hitbox_size=42, score=10), #Big power
+                      ItemType(etama3, 3, 10, hitbox_size=42), #Bomb
+                      ItemType(etama3, 4, 11, hitbox_size=42, score=1000), #Full power
+                      ItemType(etama3, 5, 12, hitbox_size=42), #1up
+                      ItemType(etama3, 6, 13, hitbox_size=42, score=500)] #Star
+
+        eosd_characters = [ReimuA, ReimuB, MarisaA, MarisaB]
+        players = []
+        for player in player_states:
+            players.append(eosd_characters[player.character](player, self, resource_loader))
+
+        Game.__init__(self, resource_loader, players, stage, rank, difficulty,
+                      bullet_types, item_types, nb_bullets_max=640, **kwargs)
+
+
+class Reimu(Player):
+    def __init__(self, state, game, resource_loader):
+        anm_wrapper = resource_loader.get_anm_wrapper(('player00.anm',))
+        self.bullet_angle = pi/30 #TODO: check
+
+        Player.__init__(self, state, game, anm_wrapper, speed=4.)
+
+
+    def fire(self):
+        if self.fire_time % self.bullet_launch_interval == 0:
+            if self.state.power < 16:
+                bullets_per_shot = 1
+            elif self.state.power < 48:
+                bullets_per_shot = 2
+            elif self.state.power < 96:
+                bullets_per_shot = 3
+            elif self.state.power < 128:
+                bullets_per_shot = 4
+            elif self.state.power >= 128:
+                bullets_per_shot = 5
+
+            bullets = self._game.players_bullets
+            nb_bullets_max = self._game.nb_bullets_max
+
+            bullet_angle = self.bullet_launch_angle - self.bullet_angle * (bullets_per_shot - 1) / 2.
+            for bullet_nb in range(bullets_per_shot):
+                if nb_bullets_max is not None and len(bullets) == nb_bullets_max:
+                    break
+
+                bullets.append(Bullet((self.x, self.y), self.bullet_type, 0,
+                                      bullet_angle, self.bullet_speed,
+                                      (0, 0, 0, 0, 0., 0., 0., 0.),
+                                      0, self, self._game, player_bullet=True))
+                bullet_angle += self.bullet_angle
+
+
+class ReimuA(Reimu):
+    def __init__(self, state, game, resource_loader):
+        Reimu.__init__(self, state, game, resource_loader)
+
+        self.bulletA_type = BulletType(self.anm_wrapper, 65, 97, 0, 0, 0, hitbox_size=4, damage=48) #TODO: verify the hitbox and damages.
+        self.bulletA_speed = 12.
+
+
+    def fire(self):
+        Reimu.fire(self)
+
+        if self.state.power < 8:
+            return
+
+        else:
+            pass #TODO
+
+
+class ReimuB(Reimu):
+    def __init__(self, state, game, resource_loader):
+        Reimu.__init__(self, state, game, resource_loader)
+
+        self.bulletB_type = BulletType(self.anm_wrapper, 66, 98, 0, 0, 0, hitbox_size=4, damage=48) #TODO: verify the hitbox and damages.
+        self.bulletB_speed = 22.
+
+
+    def fire_spine(self, offset_x):
+        bullets = self._game.players_bullets
+        nb_bullets_max = self._game.nb_bullets_max
+
+        if nb_bullets_max is not None and len(bullets) == nb_bullets_max:
+            return
+
+        bullets.append(Bullet((self.x + offset_x, self.y), self.bulletB_type, 0,
+                              self.bullet_launch_angle, self.bulletB_speed,
+                              (0, 0, 0, 0, 0., 0., 0., 0.),
+                              0, self, self._game, player_bullet=True))
+
+
+    def fire(self):
+        Reimu.fire(self)
+
+        if self.state.power < 8:
+            return
+
+        elif self.state.power < 16:
+            if self.fire_time % 15 == 0:
+                for offset in (-24, 24):
+                    self.fire_spine(offset)
+
+        elif self.state.power < 32:
+            if self.fire_time % 10 == 0:
+                for offset in (-24, 24):
+                    self.fire_spine(offset)
+
+        elif self.state.power < 48:
+            if self.fire_time % 8 == 0:
+                for offset in (-24, 24):
+                    self.fire_spine(offset)
+
+        elif self.state.power < 96:
+            if self.fire_time % 5 == 0:
+                for offset in (-16, 32):
+                    self.fire_spine(offset)
+            if self.fire_time % 8 == 0:
+                for offset in (-32, 16):
+                    self.fire_spine(offset)
+
+        elif self.state.power < 128:
+            if self.fire_time % 3 == 0:
+                for offset in (-12, 36):
+                    self.fire_spine(offset)
+            if self.fire_time % 5 == 0:
+                for offset in (-36, 12):
+                    self.fire_spine(offset)
+            if self.fire_time % 10 == 0:
+                for offset in (-24, 24):
+                    self.fire_spine(offset)
+
+        else:
+            if self.fire_time % 3 == 0:
+                for offset in (-36, -12, 12, 36):
+                    self.fire_spine(offset)
+            if self.fire_time % 5 == 0:
+                for offset in (-24, 24):
+                    self.fire_spine(offset)
+
+
+class Marisa(Player):
+    def __init__(self, state, game, resource_loader):
+        anm_wrapper = resource_loader.get_anm_wrapper(('player01.anm',))
+        self.bullet_angle = pi/40 #TODO: check
+
+        Player.__init__(self, state, game, anm_wrapper, speed=5.)
+
+
+    def fire(self):
+        if self.fire_time % self.bullet_launch_interval == 0:
+            if self.state.power < 32:
+                bullets_per_shot = 1
+            elif self.state.power < 96:
+                bullets_per_shot = 2
+            elif self.state.power < 128:
+                bullets_per_shot = 3
+            elif self.state.power >= 128:
+                bullets_per_shot = 5
+
+            bullets = self._game.players_bullets
+            nb_bullets_max = self._game.nb_bullets_max
+
+            bullet_angle = self.bullet_launch_angle - self.bullet_angle * (bullets_per_shot - 1) / 2.
+            for bullet_nb in range(bullets_per_shot):
+                if nb_bullets_max is not None and len(bullets) == nb_bullets_max:
+                    break
+
+                bullets.append(Bullet((self.x, self.y), self.bullet_type, 0,
+                                      bullet_angle, self.bullet_speed,
+                                      (0, 0, 0, 0, 0., 0., 0., 0.),
+                                      0, self, self._game, player_bullet=True))
+                bullet_angle += self.bullet_angle
+
+
+class MarisaA(Marisa):
+    def __init__(self, state, game, resource_loader):
+        Marisa.__init__(self, state, game, resource_loader)
+
+        #TODO: verify the hitbox and damages.
+        self.bulletA_types = [BulletType(self.anm_wrapper, 65, 0, 0, 0, 0, hitbox_size=4, damage=48),
+                              BulletType(self.anm_wrapper, 66, 0, 0, 0, 0, hitbox_size=4, damage=48),
+                              BulletType(self.anm_wrapper, 67, 0, 0, 0, 0, hitbox_size=4, damage=48),
+                              BulletType(self.anm_wrapper, 68, 0, 0, 0, 0, hitbox_size=4, damage=48)]
+        self.bulletA_speed_interpolator = None
+
+
+    def fire(self):
+        Marisa.fire(self)
+
+        if self.state.power < 8:
+            return
+
+        else:
+            pass #TODO
+
+
+class MarisaB(Marisa):
+    def __init__(self, state, game, resource_loader):
+        Marisa.__init__(self, state, game, resource_loader)
+
+        self.laser_type = None
+
+
+    def fire(self):
+        Marisa.fire(self)
+
+        if self.state.power < 8:
+            return
+
+        else:
+            pass #TODO