# HG changeset patch # User Emmanuel Gil Peyrot # Date 1319923302 25200 # Node ID 1e501e3b6645349ce516d5d5f22af1ad59a201c9 # Parent 52c0b9399413c346b0bad804af1646c2c9f57033 Add a subclass for each character, and implement player attacks. diff --git a/eclviewer.py b/eclviewer.py --- 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 diff --git a/pytouhou/game/character.py b/pytouhou/game/character.py 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 diff --git a/pytouhou/game/game.py b/pytouhou/game/game.py --- 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): diff --git a/pytouhou/game/games.py b/pytouhou/game/games.py deleted file mode 100644 --- a/pytouhou/game/games.py +++ /dev/null @@ -1,52 +0,0 @@ -# -*- encoding: utf-8 -*- -## -## Copyright (C) 2011 Thibaut Girka -## -## 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) - diff --git a/pytouhou/game/player.py b/pytouhou/game/player.py --- 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 :( diff --git a/pytouhou/games/__init__.py b/pytouhou/games/__init__.py new file mode 100644 diff --git a/pytouhou/games/eosd.py b/pytouhou/games/eosd.py new file mode 100644 --- /dev/null +++ b/pytouhou/games/eosd.py @@ -0,0 +1,251 @@ +# -*- encoding: utf-8 -*- +## +## Copyright (C) 2011 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.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