Mercurial > touhou
changeset 193:9f58e2a6e950
Fix particles, fix "random" item popping, change update order to match the original game's more closely.
author | Thibaut Girka <thib@sitedethib.com> |
---|---|
date | Fri, 28 Oct 2011 12:38:26 +0200 |
parents | 5e84dfd153ab |
children | efa847ee8b3c |
files | pytouhou/formats/t6rp.py pytouhou/game/effect.py pytouhou/game/enemy.py pytouhou/game/game.py pytouhou/game/player.py pytouhou/vm/eclrunner.py |
diffstat | 6 files changed, 91 insertions(+), 62 deletions(-) [+] |
line wrap: on
line diff
--- a/pytouhou/formats/t6rp.py +++ b/pytouhou/formats/t6rp.py @@ -71,8 +71,8 @@ class T6RP(object): raise Exception #TODO replay.unknown3 = unpack('<B', file.read(1)) - replay.date = read_string(file, 9, 'ascii') - replay.name = read_string(file, 9, 'ascii').rstrip() + replay.date = file.read(9) #read_string(file, 9, 'ascii') + replay.name = file.read(9) #read_string(file, 9, 'ascii').rstrip() replay.unknown4, replay.score, replay.unknown5, replay.slowdown, replay.unknown6 = unpack('<HIIfI', file.read(18)) stages_offsets = unpack('<7I', file.read(28))
--- a/pytouhou/game/effect.py +++ b/pytouhou/game/effect.py @@ -40,7 +40,7 @@ class Effect(object): class Particle(object): - def __init__(self, start_pos, index, anm_wrapper, size, amp, delay, game): + def __init__(self, start_pos, index, anm_wrapper, size, amp, game): self._sprite = Sprite() self._sprite.anm, self._sprite.texcoords = anm_wrapper.get_sprite(index) self._game = game @@ -56,7 +56,6 @@ class Particle(object): self.scale_interpolator = None self.rotations_interpolator = None - self.delay = delay self.amp = amp @@ -73,7 +72,7 @@ class Particle(object): def update(self): - if (self.frame == 0 and not self.delay) or (self.frame == 1 and self.delay): + if self.frame == 0: self.set_end_pos(self.amp) if self.pos_interpolator:
--- a/pytouhou/game/enemy.py +++ b/pytouhou/game/enemy.py @@ -187,7 +187,7 @@ class Enemy(object): if self._game.stage in [1, 2, 7]: color = 3 for i in range(number): - self._game.new_particle((self.x, self.y), color, 4., 256, delay=False) #TODO: find the real size. + self._game.new_particle((self.x, self.y), color, 4., 256) #TODO: find the real size. def set_pos(self, x, y, z):
--- a/pytouhou/game/game.py +++ b/pytouhou/game/game.py @@ -48,8 +48,6 @@ class Game(object): self.difficulty = difficulty self.boss = None self.spellcard = None - self.deaths_count = 0 - self.next_bonus = 0 self.bonus_list = [0,0,1,0,1,0,0,1,1,1,0,0,0,1,1,0,1,0,1,0,1,0,1,0,1,0,0,1,1,1,0,2] self.prng = prng or Random() self.frame = 0 @@ -60,10 +58,9 @@ class Game(object): ecl = resource_loader.get_ecl('ecldata%d.ecl' % stage) self.ecl_runner = ECLMainRunner(ecl, self) - #TODO: The game calls it two times. What for? # See 102h.exe@0x413220 if you think you're brave enough. - self.prng.rand_uint16() - self.prng.rand_uint16() + self.deaths_count = self.prng.rand_uint16() % 3 + self.next_bonus = self.prng.rand_uint16() % 8 def drop_bonus(self, x, y, _type, end_pos=None): @@ -87,8 +84,8 @@ class Game(object): self.effects.append(Effect(pos, anim, self.etama4)) - def new_particle(self, pos, color, size, amp, delay=False): - self.effects.append(Particle(pos, 7 + 4 * color + self.prng.rand_uint16() % 4, self.etama4, size, amp, delay, self)) + def new_particle(self, pos, color, size, amp): + self.effects.append(Particle(pos, 7 + 4 * color + self.prng.rand_uint16() % 4, self.etama4, size, amp, self)) def new_enemy(self, pos, life, instr_type, bonus_dropped, die_score): @@ -108,8 +105,49 @@ class Game(object): self.cancelled_bullets = [bullet for bullet in self.cancelled_bullets if not bullet._removed] self.items = [item for item in self.items if not item._removed] + # 3. Let's play! - #TODO: check update orders + # In the original game, updates are done in prioritized functions called "chains" + # We have to mimic this functionnality to be replay-compatible with the official game. + + # Pri 6 is background + self.update_players(keystate) # Pri 7 + self.update_enemies() # Pri 9 + self.update_effects() # Pri 10 + self.update_bullets() # Pri 11 + # Pri 12 is HUD + + # 4. Cleaning + self.cleanup() + + self.frame += 1 + + + def update_enemies(self): + for enemy in self.enemies: + enemy.update() + + # Check for collisions + for enemy in self.enemies: + ex, ey = enemy.x, enemy.y + ehalf_size_x, ehalf_size_y = enemy.hitbox_half_size + ex1, ex2 = ex - ehalf_size_x, ex + ehalf_size_x + ey1, ey2 = ey - ehalf_size_y, ey + ehalf_size_y + + for bullet in self.players_bullets: + half_size = bullet.hitbox_half_size + bx, by = bullet.x, bullet.y + bx1, bx2 = bx - half_size, bx + half_size + by1, by2 = by - half_size, by + half_size + + if not (bx2 < ex1 or bx1 > ex2 + or by2 < ey1 or by1 > ey2): + bullet.collide() + enemy.on_attack(bullet) + player.state.score += 90 # found experimentally + + + def update_players(self, keystate): for player in self.players: player.update(keystate) #TODO: differentiate keystates (multiplayer mode) if player.state.x < 8.: @@ -121,26 +159,53 @@ class Game(object): if player.state.y > 448.-16: #TODO player.state.y = 448.-16 - for enemy in self.enemies: - enemy.update() - - for enemy in self.effects: - enemy.update() - - for bullet in self.bullets: + for bullet in self.players_bullets: bullet.update() + # Check for collisions + for player in self.players: + if not player.state.touchable: + continue + + px, py = player.x, player.y + phalf_size = player.hitbox_half_size + px1, px2 = px - phalf_size, px + phalf_size + py1, py2 = py - phalf_size, py + phalf_size + + ghalf_size = player.graze_hitbox_half_size + gx1, gx2 = px - ghalf_size, px + ghalf_size + gy1, gy2 = py - ghalf_size, py + ghalf_size + + #TODO: Should that be done here or in update_enemies? + for enemy in self.enemies: + half_size_x, half_size_y = enemy.hitbox_half_size + bx, by = enemy.x, enemy.y + bx1, bx2 = bx - half_size_x, bx + half_size_x + by1, by2 = by - half_size_y, by + half_size_y + + #TODO: box-box or point-in-box? + if enemy.touchable and not (bx2 < px1 or bx1 > px2 + or by2 < py1 or by1 > py2): + enemy.on_collide() + if player.state.invulnerable_time == 0: + player.collide() + + + def update_effects(self): + for effect in self.effects: + effect.update() + + + def update_bullets(self): for bullet in self.cancelled_bullets: bullet.update() - for bullet in self.players_bullets: + for bullet in self.bullets: bullet.update() for item in self.items: item.update() - # 4. Check for collisions! - #TODO for player in self.players: if not player.state.touchable: continue @@ -171,22 +236,10 @@ class Game(object): bullet.grazed = True player.state.graze += 1 player.state.score += 500 # found experimentally - self.new_particle((px, py), 0, .8, 192, delay=True) #TODO: find the real size and range. + self.new_particle((px, py), 0, .8, 192) #TODO: find the real size and range. #TODO: display a static particle during one frame at # 12 pixels of the player, in the axis of the “collision”. - for enemy in self.enemies: - half_size_x, half_size_y = enemy.hitbox_half_size - bx, by = enemy.x, enemy.y - bx1, bx2 = bx - half_size_x, bx + half_size_x - by1, by2 = by - half_size_y, by + half_size_y - - if enemy.touchable and not (bx2 < px1 or bx1 > px2 - or by2 < py1 or by1 > py2): - enemy.on_collide() - if player.state.invulnerable_time == 0: - player.collide() - for item in self.items: half_size = item.hitbox_half_size bx, by = item.x, item.y @@ -197,29 +250,6 @@ class Game(object): or by2 < py1 or by1 > py2): player.collect(item) - for enemy in self.enemies: - ex, ey = enemy.x, enemy.y - ehalf_size_x, ehalf_size_y = enemy.hitbox_half_size - ex1, ex2 = ex - ehalf_size_x, ex + ehalf_size_x - ey1, ey2 = ey - ehalf_size_y, ey + ehalf_size_y - - for bullet in self.players_bullets: - half_size = bullet.hitbox_half_size - bx, by = bullet.x, bullet.y - bx1, bx2 = bx - half_size, bx + half_size - by1, by2 = by - half_size, by + half_size - - if not (bx2 < ex1 or bx1 > ex2 - or by2 < ey1 or by1 > ey2): - bullet.collide() - enemy.on_attack(bullet) - player.state.score += 90 # found experimentally - - # 5. Cleaning - self.cleanup() - - self.frame += 1 - def cleanup(self): # Filter out non-visible enemies
--- a/pytouhou/game/player.py +++ b/pytouhou/game/player.py @@ -79,7 +79,7 @@ class Player(object): self.death_time = self._game.frame self._game.new_death((self.state.x, self.state.y), 2) for i in range(16): - self._game.new_particle((self.state.x, self.state.y), 2, 4., 256, delay=True) #TODO: find the real size and range. + self._game.new_particle((self.state.x, self.state.y), 2, 4., 256) #TODO: find the real size and range. def collect(self, item):
--- a/pytouhou/vm/eclrunner.py +++ b/pytouhou/vm/eclrunner.py @@ -132,7 +132,7 @@ class ECLRunner(object): enm.drop_particles(7, 0) self._game.drop_bonus(enm.x, enm.y, enm._bonus_dropped) elif enm._bonus_dropped == -1: - if self._game.deaths_count % 3: + if self._game.deaths_count % 3 == 0: enm.drop_particles(10, 0) self._game.drop_bonus(enm.x, enm.y, self._game.bonus_list[self._game.next_bonus]) self._game.next_bonus = (self._game.next_bonus + 1) % 32