Mercurial > touhou
changeset 445:b0abb05811f7
Make pytouhou.game.player an extension type, and move the GameOver exception there since it makes more sense.
| author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
|---|---|
| date | Sat, 17 Aug 2013 04:44:28 +0200 |
| parents | f26c8ab57257 |
| children | 3a33ed7f3b85 |
| files | eosd pytouhou/game/game.py pytouhou/game/item.py pytouhou/game/player.pxd pytouhou/game/player.py pytouhou/game/player.pyx pytouhou/vm/eclrunner.py |
| diffstat | 7 files changed, 335 insertions(+), 314 deletions(-) [+] |
line wrap: on
line diff
--- a/eosd Fri Aug 30 14:16:08 2013 +0200 +++ b/eosd Sat Aug 17 04:44:28 2013 +0200 @@ -56,8 +56,7 @@ from pytouhou.resource.loader import Loader from pytouhou.ui.gamerunner import GameRunner from pytouhou.games.eosd import EoSDCommon, EoSDGame -from pytouhou.game.game import GameOver -from pytouhou.game.player import PlayerState +from pytouhou.game.player import PlayerState, GameOver from pytouhou.formats.t6rp import T6RP, Level from pytouhou.utils.random import Random from pytouhou.vm.msgrunner import NextStage @@ -161,7 +160,7 @@ previous_level = replay.levels[stage_num - 1] states[0].score = previous_level.score states[0].effective_score = previous_level.score - states[0].point_items = level.point_items + states[0].points = level.point_items states[0].power = level.power states[0].lives = level.lives states[0].bombs = level.bombs
--- a/pytouhou/game/game.py Fri Aug 30 14:16:08 2013 +0200 +++ b/pytouhou/game/game.py Sat Aug 17 04:44:28 2013 +0200 @@ -24,11 +24,6 @@ from pytouhou.game.face import Face - -class GameOver(Exception): - pass - - class Game(object): def __init__(self, players, stage, rank, difficulty, bullet_types, laser_types, item_types, nb_bullets_max=None, width=384, @@ -342,7 +337,7 @@ if not player.state.touchable: continue - px, py = player.x, player.y + px, py = player.state.x, player.state.y phalf_size = player.sht.hitbox px1, px2 = px - phalf_size, px + phalf_size py1, py2 = py - phalf_size, py + phalf_size
--- a/pytouhou/game/item.py Fri Aug 30 14:16:08 2013 +0200 +++ b/pytouhou/game/item.py Sat Aug 17 04:44:28 2013 +0200 @@ -168,7 +168,8 @@ (3.,), 180) if self.player is not None: - self.angle = atan2(self.player.y - self.y, self.player.x - self.x) + player_state = self.player.state + self.angle = atan2(player_state.y - self.y, player_state.x - self.x) self.x += cos(self.angle) * self.speed self.y += sin(self.angle) * self.speed elif self.speed_interpolator is None:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/game/player.pxd Sat Aug 17 04:44:28 2013 +0200 @@ -0,0 +1,22 @@ +from pytouhou.game.element cimport Element + +cdef class PlayerState: + cdef public double x, y + cdef public bint touchable, focused + cdef public long character, score, effective_score, lives, bombs, power, graze, points, invulnerable_time, power_bonus + + +cdef class Player(Element): + cdef public PlayerState state + cdef public object _game + cdef public long death_time + + cdef object anm + cdef tuple speeds + cdef long fire_time, direction + + cdef void set_anim(self, index) + cpdef play_sound(self, str name) + cpdef collide(self) + cdef void fire(self) + cpdef update(self, long keystate)
--- a/pytouhou/game/player.py Fri Aug 30 14:16:08 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,302 +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.element import Element -from pytouhou.game.sprite import Sprite -from pytouhou.vm.anmrunner import ANMRunner -from pytouhou.game.bullettype import BulletType -from pytouhou.game.bullet import Bullet -from pytouhou.game.lasertype import LaserType -from pytouhou.game.laser import PlayerLaser -from pytouhou.game.game import GameOver - -from math import pi - - -class PlayerState(object): - def __init__(self, character=0, score=0, power=0, lives=2, bombs=3): - self.character = character # ReimuA/ReimuB/MarisaA/MarisaB/... - - self.score = score - self.effective_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.invulnerable_time = 240 - self.touchable = True - self.focused = False - - self.power_bonus = 0 # Never goes over 30. - - - def copy(self): - return PlayerState(self.character, self.score, - self.power, self.lives, self.bombs) - - -class Player(Element): - def __init__(self, state, game, anm): - Element.__init__(self) - - self._game = game - self.anm = anm - - self.speeds = (self.sht.horizontal_vertical_speed, - self.sht.diagonal_speed, - self.sht.horizontal_vertical_focused_speed, - self.sht.diagonal_focused_speed) - - self.fire_time = 0 - - self.state = state - self.direction = None - - self.set_anim(0) - - self.death_time = 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, index, self.sprite) - - - def play_sound(self, name): - self._game.sfx_player.play('%s.wav' % name) - - - def collide(self): - if not self.state.invulnerable_time and not self.death_time and self.state.touchable: # Border Between Life and Death - self.death_time = self._game.frame - self._game.new_effect((self.state.x, self.state.y), 17) - self._game.modify_difficulty(-1600) - self.play_sound('pldead00') - for i in range(16): - self._game.new_particle((self.state.x, self.state.y), 11, 256) #TODO: find the real size and range. - - - def start_focusing(self): - self.state.focused = True - - - def stop_focusing(self): - self.state.focused = False - - - def fire(self): - sht = self.focused_sht if self.state.focused else self.sht - - # Don’t use min() since sht.shots could be an empty dict. - power = 999 - for shot_power in sht.shots: - if self.state.power < shot_power: - power = power if power < shot_power else shot_power - - bullets = self._game.players_bullets - lasers = self._game.players_lasers - nb_bullets_max = self._game.nb_bullets_max - - if self.fire_time % 5 == 0: - self.play_sound('plst00') - - for shot in sht.shots[power]: - origin = self.orbs[shot.orb - 1] if shot.orb else self.state - - if shot.type == 3: - if self.fire_time != 30: - continue - - number = shot.delay #TODO: number can do very surprising things, like removing any bullet creation from enemies with 3. For now, crash when not 0 or 1. - if lasers[number]: - continue - - laser_type = LaserType(self.anm, shot.sprite % 256, 68) - lasers[number] = PlayerLaser(laser_type, 0, shot.hitbox, shot.damage, shot.angle, shot.speed, shot.interval, origin) - continue - - if (self.fire_time + shot.delay) % shot.interval != 0: - continue - - if nb_bullets_max is not None and len(bullets) == nb_bullets_max: - break - - x = origin.x + shot.pos[0] - y = origin.y + shot.pos[1] - - #TODO: find a better way to do that. - bullet_type = BulletType(self.anm, shot.sprite % 256, - shot.sprite % 256 + 32, #TODO: find the real cancel anim - 0, 0, 0, 0.) - #TODO: Type 1 (homing bullets) - if shot.type == 2: - #TODO: triple-check acceleration! - bullets.append(Bullet((x, y), bullet_type, 0, - shot.angle, shot.speed, - (-1, 0, 0, 0, 0.15, -pi/2., 0., 0.), - 16, self, self._game, player_bullet=True, - damage=shot.damage, hitbox=shot.hitbox)) - else: - bullets.append(Bullet((x, y), bullet_type, 0, - shot.angle, shot.speed, - (0, 0, 0, 0, 0., 0., 0., 0.), - 0, self, self._game, player_bullet=True, - damage=shot.damage, hitbox=shot.hitbox)) - - - def update(self, keystate): - if self.death_time == 0 or self._game.frame - self.death_time > 60: - speed, diag_speed = self.speeds[2:] if self.state.focused else self.speeds[:2] - try: - dx, dy = {16: (0.0, -speed), 32: (0.0, speed), 64: (-speed, 0.0), 128: (speed, 0.0), - 16|64: (-diag_speed, -diag_speed), 16|128: (diag_speed, -diag_speed), - 32|64: (-diag_speed, diag_speed), 32|128: (diag_speed, diag_speed)}[keystate & (16|32|64|128)] - except KeyError: - dx, dy = 0.0, 0.0 - - 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 - - if self.state.x < 8.: - self.state.x = 8. - if self.state.x > self._game.width - 8: - self.state.x = self._game.width - 8. - if self.state.y < 16.: - self.state.y = 16. - if self.state.y > self._game.height - 16: - self.state.y = self._game.height -16. - - if not self.state.focused and keystate & 4: - self.start_focusing() - elif self.state.focused and not keystate & 4: - self.stop_focusing() - - if self.state.invulnerable_time > 0: - self.state.invulnerable_time -= 1 - - m = self.state.invulnerable_time % 8 - if m == 7 or self.state.invulnerable_time == 0: - self.sprite.color = (255, 255, 255) - self.sprite.changed = True - elif m == 1: - 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 :( - self.state.touchable = False - if self.state.power > 16: - self.state.power -= 16 - else: - self.state.power = 0 - for laser in self._game.players_lasers: - if laser: - laser.cancel() - - self.state.lives -= 1 - if self.state.lives < 0: - #TODO: display a menu to ask the players if they want to continue. - self._game.continues -= 1 - if self._game.continues < 0: - raise GameOver - - for i in range(5): - self._game.drop_bonus(self.state.x, self.state.y, 4, - end_pos=(self._game.prng.rand_double() * 288 + 48, - self._game.prng.rand_double() * 192 - 64)) - self.state.score = 0 - self.state.effective_score = 0 - self.state.lives = 2 #TODO: use the right default. - self.state.bombs = 3 #TODO: use the right default. - self.state.power = 0 - - self.state.graze = 0 - self.state.points = 0 - else: - self._game.drop_bonus(self.state.x, self.state.y, 2, - end_pos=(self._game.prng.rand_double() * 288 + 48, # 102h.exe@0x41f3dc - self._game.prng.rand_double() * 192 - 64)) # @0x41f3 - for i in range(5): - self._game.drop_bonus(self.state.x, self.state.y, 0, - end_pos=(self._game.prng.rand_double() * 288 + 48, - self._game.prng.rand_double() * 192 - 64)) - - elif time == 7: - self.sprite.mirrored = False - self.sprite.blendfunc = 0 - self.sprite.rescale = 0.75, 1.5 - self.sprite.fade(26, 96, lambda x: x) - self.sprite.scale_in(26, 0.00, 2.5, lambda x: x) - - elif time == 32: - self.state.x = float(self._game.width) / 2. #TODO - self.state.y = float(self._game.width) #TODO - self.direction = None - - self.sprite = Sprite() - self.anmrunner = ANMRunner(self.anm, 0, self.sprite) - self.sprite.alpha = 128 - self.sprite.rescale = 0.0, 2.5 - self.sprite.fade(30, 255, lambda x: x) - self.sprite.blendfunc = 1 - self.sprite.scale_in(30, 1., 1., lambda x: x) - - elif time == 61: # respawned - self.state.touchable = True - self.state.invulnerable_time = 240 - self.sprite.blendfunc = 0 - self.sprite.changed = True - - if time > 30: - self._game.cancel_bullets() - - if time > 90: # start the bullet hell again - self.death_time = 0 - - self.anmrunner.run_frame() -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pytouhou/game/player.pyx Sat Aug 17 04:44:28 2013 +0200 @@ -0,0 +1,306 @@ +# -*- 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 libc.math cimport M_PI as pi + +from pytouhou.game.sprite cimport Sprite +from pytouhou.vm.anmrunner import ANMRunner +from pytouhou.game.bullettype import BulletType +from pytouhou.game.bullet cimport Bullet +from pytouhou.game.lasertype import LaserType +from pytouhou.game.laser import PlayerLaser + + +class GameOver(Exception): + pass + + +cdef class PlayerState: + def __init__(self, long character=0, long score=0, long power=0, long lives=2, long bombs=3): + self.character = character # ReimuA/ReimuB/MarisaA/MarisaB/... + + self.score = score + self.effective_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.invulnerable_time = 240 + self.touchable = True + self.focused = False + + self.power_bonus = 0 # Never goes over 30. + + + def copy(self): + return PlayerState(self.character, self.score, + self.power, self.lives, self.bombs) + + +cdef class Player(Element): + def __init__(self, PlayerState state, game, anm): + Element.__init__(self) + + self._game = game + self.anm = anm + + self.speeds = (self.sht.horizontal_vertical_speed, + self.sht.diagonal_speed, + self.sht.horizontal_vertical_focused_speed, + self.sht.diagonal_focused_speed) + + self.fire_time = 0 + + self.state = state + self.direction = 0 + + self.set_anim(0) + + self.death_time = 0 + + + cdef void set_anim(self, index): + self.sprite = Sprite() + self.anmrunner = ANMRunner(self.anm, index, self.sprite) + + + cpdef play_sound(self, str name): + self._game.sfx_player.play('%s.wav' % name) + + + cpdef collide(self): + if not self.state.invulnerable_time and not self.death_time and self.state.touchable: # Border Between Life and Death + self.death_time = self._game.frame + self._game.new_effect((self.state.x, self.state.y), 17) + self._game.modify_difficulty(-1600) + self.play_sound('pldead00') + for i in xrange(16): + self._game.new_particle((self.state.x, self.state.y), 11, 256) #TODO: find the real size and range. + + + def start_focusing(self): + self.state.focused = True + + + def stop_focusing(self): + self.state.focused = False + + + cdef void fire(self): + cdef double x, y + cdef long shot_power + + sht = self.focused_sht if self.state.focused else self.sht + + # Don’t use min() since sht.shots could be an empty dict. + power = 999 + for shot_power in sht.shots: + if self.state.power < shot_power: + power = power if power < shot_power else shot_power + + bullets = self._game.players_bullets + lasers = self._game.players_lasers + nb_bullets_max = <long>self._game.nb_bullets_max + + if self.fire_time % 5 == 0: + self.play_sound('plst00') + + for shot in sht.shots[power]: + origin = self.orbs[shot.orb - 1] if shot.orb else self.state + shot_type = <unsigned char>shot.type + + if shot_type == 3: + if self.fire_time != 30: + continue + + #TODO: number can do very surprising things, like removing any + # bullet creation from enemies with 3. For now, crash when not + # an actual laser number. + number = <long>shot.delay + if lasers[number] is not None: + continue + + laser_type = LaserType(self.anm, shot.sprite % 256, 68) + lasers[number] = PlayerLaser(laser_type, 0, shot.hitbox, shot.damage, shot.angle, shot.speed, shot.interval, origin) + continue + + if (self.fire_time + shot.delay) % shot.interval != 0: + continue + + if nb_bullets_max != 0 and len(bullets) == nb_bullets_max: + break + + x = origin.x + shot.pos[0] + y = origin.y + shot.pos[1] + + #TODO: find a better way to do that. + bullet_type = BulletType(self.anm, shot.sprite % 256, + shot.sprite % 256 + 32, #TODO: find the real cancel anim + 0, 0, 0, 0.) + #TODO: Type 1 (homing bullets) + if shot_type == 2: + #TODO: triple-check acceleration! + bullets.append(Bullet((x, y), bullet_type, 0, + shot.angle, shot.speed, + (-1, 0, 0, 0, 0.15, -pi/2., 0., 0.), + 16, self, self._game, player_bullet=True, + damage=shot.damage, hitbox=shot.hitbox)) + else: + bullets.append(Bullet((x, y), bullet_type, 0, + shot.angle, shot.speed, + (0, 0, 0, 0, 0., 0., 0., 0.), + 0, self, self._game, player_bullet=True, + damage=shot.damage, hitbox=shot.hitbox)) + + + cpdef update(self, long keystate): + cdef double dx, dy + + if self.death_time == 0 or self._game.frame - self.death_time > 60: + speed, diag_speed = self.speeds[2:] if self.state.focused else self.speeds[:2] + try: + dx, dy = {16: (0., -speed), 32: (0., speed), 64: (-speed, 0.), 128: (speed, 0.), + 16|64: (-diag_speed, -diag_speed), 16|128: (diag_speed, -diag_speed), + 32|64: (-diag_speed, diag_speed), 32|128: (diag_speed, diag_speed)}[keystate & (16|32|64|128)] + except KeyError: + dx, dy = 0., 0. + + 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 != 0: + self.set_anim({-1: 2, +1: 4}[self.direction]) + self.direction = 0 + + self.state.x += dx + self.state.y += dy + + #XXX + self.x = self.state.x + self.y = self.state.y + + if self.state.x < 8.: + self.state.x = 8. + if self.state.x > self._game.width - 8: + self.state.x = self._game.width - 8. + if self.state.y < 16.: + self.state.y = 16. + if self.state.y > self._game.height - 16: + self.state.y = self._game.height -16. + + if not self.state.focused and keystate & 4: + self.start_focusing() + elif self.state.focused and not keystate & 4: + self.stop_focusing() + + if self.state.invulnerable_time > 0: + self.state.invulnerable_time -= 1 + + m = self.state.invulnerable_time % 8 + if m == 7 or self.state.invulnerable_time == 0: + self.sprite.color = (255, 255, 255) + self.sprite.changed = True + elif m == 1: + 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 = <long>self._game.frame - self.death_time + if time == 6: # too late, you are dead :( + self.state.touchable = False + if self.state.power > 16: + self.state.power -= 16 + else: + self.state.power = 0 + for laser in self._game.players_lasers: + if laser is not None: + laser.cancel() + + self.state.lives -= 1 + if self.state.lives < 0: + #TODO: display a menu to ask the players if they want to continue. + self._game.continues -= 1 + if self._game.continues < 0: + raise GameOver + + for i in xrange(5): + self._game.drop_bonus(self.state.x, self.state.y, 4, + end_pos=(self._game.prng.rand_double() * 288 + 48, + self._game.prng.rand_double() * 192 - 64)) + self.state.score = 0 + self.state.effective_score = 0 + self.state.lives = 2 #TODO: use the right default. + self.state.bombs = 3 #TODO: use the right default. + self.state.power = 0 + + self.state.graze = 0 + self.state.points = 0 + else: + self._game.drop_bonus(self.state.x, self.state.y, 2, + end_pos=(self._game.prng.rand_double() * 288 + 48, # 102h.exe@0x41f3dc + self._game.prng.rand_double() * 192 - 64)) # @0x41f3 + for i in xrange(5): + self._game.drop_bonus(self.state.x, self.state.y, 0, + end_pos=(self._game.prng.rand_double() * 288 + 48, + self._game.prng.rand_double() * 192 - 64)) + + elif time == 7: + self.sprite.mirrored = False + self.sprite.blendfunc = 0 + self.sprite.rescale = 0.75, 1.5 + self.sprite.fade(26, 96) + self.sprite.scale_in(26, 0., 2.5) + + elif time == 32: + self.state.x = float(self._game.width) / 2. #TODO + self.state.y = float(self._game.width) #TODO + self.direction = 0 + + self.sprite = Sprite() + self.anmrunner = ANMRunner(self.anm, 0, self.sprite) + self.sprite.alpha = 128 + self.sprite.rescale = 0., 2.5 + self.sprite.fade(30, 255) + self.sprite.blendfunc = 1 + self.sprite.scale_in(30, 1., 1.) + + elif time == 61: # respawned + self.state.touchable = True + self.state.invulnerable_time = 240 + self.sprite.blendfunc = 0 + self.sprite.changed = True + + if time > 30: + self._game.cancel_bullets() + + if time > 90: # start the bullet hell again + self.death_time = 0 + + self.anmrunner.run_frame() +
--- a/pytouhou/vm/eclrunner.py Fri Aug 30 14:16:08 2013 +0200 +++ b/pytouhou/vm/eclrunner.py Sat Aug 17 04:44:28 2013 +0200 @@ -203,10 +203,10 @@ return self._enemy.z elif value == -10018: player = self._enemy.select_player() - return player.x + return player.state.x elif value == -10019: player = self._enemy.select_player() - return player.y + return player.state.y elif value == -10021: return self._enemy.get_player_angle() elif value == -10022:
