diff options
author | Nguyễn Gia Phong <vn.mcsinyx@gmail.com> | 2017-11-02 21:39:06 +0700 |
---|---|---|
committer | Nguyễn Gia Phong <vn.mcsinyx@gmail.com> | 2017-11-02 21:39:06 +0700 |
commit | 13e0ea6f987273609cd6d199030bbf526ab4d726 (patch) | |
tree | 66f8b856524b9089d72b6f0c1cc917db8ec764e1 | |
parent | ba4a575ab52f792f46c2f4d49d44edd7ff8e203d (diff) | |
download | brutalmaze-0.0.1.tar.gz |
Fix various movement and display bugs 0.0.1
-rw-r--r-- | brutalmaze/characters.py | 18 | ||||
-rw-r--r-- | brutalmaze/constants.py | 14 | ||||
-rw-r--r-- | brutalmaze/main.py | 47 | ||||
-rw-r--r-- | brutalmaze/maze.py | 133 | ||||
-rw-r--r-- | brutalmaze/utils.py | 21 | ||||
-rw-r--r-- | brutalmaze/weapons.py | 18 | ||||
-rwxr-xr-x | setup.py | 4 |
7 files changed, 116 insertions, 139 deletions
diff --git a/brutalmaze/characters.py b/brutalmaze/characters.py index 2e2294d..7a68f37 100644 --- a/brutalmaze/characters.py +++ b/brutalmaze/characters.py @@ -17,6 +17,8 @@ # # Copyright (C) 2017 Nguyễn Gia Phong +__doc__ = 'brutalmaze module for hero and enemy classes' + from collections import deque from math import atan2, sin, pi from random import choice, shuffle, uniform @@ -24,11 +26,9 @@ from random import choice, shuffle, uniform import pygame from .constants import * -from .utils import randsign, regpoly, fill_aapolygon, pos, sign, length +from .utils import randsign, regpoly, fill_aapolygon, sign from .weapons import Bullet -__doc__ = 'brutalmaze module for hero and enemy classes' - class Hero: """Object representing the hero.""" @@ -98,9 +98,9 @@ class Enemy: def pos(self): """Return coordinate of the center of the enemy.""" - x, y = pos(self.x, self.y, self.maze.distance, - self.maze.middlex, self.maze.middley) - return x + self.offsetx*self.maze.step, y + self.offsety*self.maze.step + x, y = self.maze.pos(self.x, self.y) + step = self.maze.distance * HERO_SPEED / self.maze.fps + return x + self.offsetx*step, y + self.offsety*step def place(self, x=0, y=0): """Move the enemy by (x, y) (in grids).""" @@ -111,7 +111,7 @@ class Enemy: def fire(self): """Return True if the enemy shot the hero, False otherwise.""" x, y = self.pos() - if (length(x, y, self.maze.x, self.maze.y) > FIRANGE*self.maze.distance + if (self.maze.length(x, y) > FIRANGE*self.maze.distance or self.next_move > pygame.time.get_ticks() or (self.x, self.y) in AROUND_HERO or self.offsetx or self.offsety or uniform(-2, 2) < (INIT_SCORE/self.maze.score) ** 2): @@ -164,6 +164,10 @@ class Enemy: color = self.color[int(self.wound)] if self.awake else FG_COLOR fill_aapolygon(self.maze.surface, square, color) + def hit(self, wound): + """Handle the enemy when it's hit by a bullet.""" + self.wound += wound + def die(self): """Handle the enemy's death.""" self.maze.map[self.x][self.y] = EMPTY if self.awake else WALL diff --git a/brutalmaze/constants.py b/brutalmaze/constants.py index defdfb1..ecb797c 100644 --- a/brutalmaze/constants.py +++ b/brutalmaze/constants.py @@ -17,16 +17,19 @@ # # Copyright (C) 2017 Nguyễn Gia Phong -from pygame import image -from pkg_resources import resource_filename - __doc__ = 'brutalmaze module for shared constants' +from pygame import image, K_UP, K_w, K_LEFT, K_a, K_DOWN, K_s, K_RIGHT, K_d +from pkg_resources import resource_filename + ICON = image.load(resource_filename('brutalmaze', 'icon.png')) +UP = (K_UP, K_w) +LEFT = (K_LEFT, K_a) +DOWN = (K_DOWN, K_s) +RIGHT = (K_RIGHT, K_d) SQRT2 = 2 ** 0.5 -GOLDEN_MEAN = 5**0.5/2 + 0.5 - +INIT_SCORE = 5**0.5/2 + 0.5 # golden mean INIT_FPS = 30.0 MAX_FPS = 144.0 SIZE = 640, 480 @@ -35,7 +38,6 @@ ROAD_WIDTH = 5 # grids CELL_WIDTH = ROAD_WIDTH * 2 # grids MIDDLE = (MAZE_SIZE + MAZE_SIZE%2 - 1)*ROAD_WIDTH + ROAD_WIDTH//2 LAST_ROW = (MAZE_SIZE-1) * ROAD_WIDTH * 2 -INIT_SCORE = 208.2016 HEAL_SPEED = 1 # HP/s HERO_SPEED = 5 # grid/s ENEMY_SPEED = 6 # grid/s diff --git a/brutalmaze/main.py b/brutalmaze/main.py index cd3a026..6690b51 100644 --- a/brutalmaze/main.py +++ b/brutalmaze/main.py @@ -22,8 +22,9 @@ from collections import deque import pygame from pygame.locals import * -from .constants import ICON, SIZE, INIT_FPS, MAX_FPS +from .constants import ICON, SIZE, INIT_FPS, MAX_FPS, UP, LEFT, DOWN, RIGHT from .maze import Maze +from .utils import some def main(): @@ -43,43 +44,17 @@ def main(): elif event.type == KEYDOWN: if event.key == K_F2: # new game maze.__init__((maze.w, maze.h), fps) - elif maze.hero.dead: - continue elif event.key in (K_ESCAPE, K_p): maze.paused ^= True - elif event.key in (K_UP, K_w): - maze.move(up=-1) - elif event.key in (K_LEFT, K_a): - maze.move(left=-1) - elif event.key in (K_DOWN, K_s): - maze.move(down=-1) - elif event.key in (K_RIGHT, K_d): - maze.move(right=-1) - elif event.key == K_RETURN: - maze.hero.slashing = True - elif maze.hero.dead: - continue - elif event.type == KEYUP: - if event.key in (K_UP, K_w): - maze.move(up=1) - elif event.key in (K_LEFT, K_a): - maze.move(left=1) - elif event.key in (K_DOWN, K_s): - maze.move(down=1) - elif event.key in (K_RIGHT, K_d): - maze.move(right=1) - elif event.key == K_RETURN: - maze.hero.slashing = False - elif event.type == MOUSEBUTTONDOWN: - if event.button == 1: - maze.hero.firing = True - elif event.button == 3: - maze.hero.slashing = True - elif event.type == MOUSEBUTTONUP: - if event.button == 1: - maze.hero.firing = False - elif event.button == 3: - maze.hero.slashing = False + + if not maze.hero.dead: + keys = pygame.key.get_pressed() + buttons = pygame.mouse.get_pressed() + maze.right = some(keys, LEFT) - some(keys, RIGHT) + maze.down = some(keys, UP) - some(keys, DOWN) + maze.hero.slashing = keys[K_RETURN] or buttons[2] + maze.hero.firing = buttons[0] + if len(flash_time) > 5: new_fps = 5000.0 / (flash_time[-1] - flash_time[0]) flash_time.popleft() diff --git a/brutalmaze/maze.py b/brutalmaze/maze.py index 022c8aa..ae12ded 100644 --- a/brutalmaze/maze.py +++ b/brutalmaze/maze.py @@ -17,6 +17,8 @@ # # Copyright (C) 2017 Nguyễn Gia Phong +__doc__ = 'brutalmaze module for the maze class' + from collections import deque from math import pi, atan, atan2, log from random import choice, getrandbits @@ -26,11 +28,9 @@ from pygame import RESIZABLE from .characters import Hero, Enemy from .constants import * -from .utils import round2, pos, sign, cosin, length, regpoly, fill_aapolygon +from .utils import round2, sign, cosin, regpoly, fill_aapolygon from .weapons import Bullet -__doc__ = 'brutalmaze module for the maze class' - def cell(bit, upper=True): """Return a half of a cell of the maze based on the given bit.""" @@ -56,20 +56,18 @@ class Maze: """Object representing the maze, including the characters.""" def __init__(self, size, fps): self.w, self.h = size - self.fps, self.speed = fps, fps / HERO_SPEED + self.fps = fps self.surface = pygame.display.set_mode(size, RESIZABLE) self.distance = (self.w * self.h / 416) ** 0.5 - self.step = self.distance / self.speed self.middlex, self.middley = self.x, self.y = self.w >> 1, self.h >> 1 w, h = (int(i/self.distance/2 + 2) for i in size) self.rangex = range(MIDDLE - w, MIDDLE + w + 1) self.rangey = range(MIDDLE - h, MIDDLE + h + 1) - self.offsetx = self.offsety = 0.0 self.paused, self.score = False, INIT_SCORE self.map = deque() for _ in range(MAZE_SIZE): self.map.extend(new_column()) - self.up = self.left = self.down = self.right = 0 + self.down = self.right = 0 self.rotatex = self.rotatey = 0 self.bullets, self.enemies = [], [] self.add_enemy() @@ -79,31 +77,36 @@ class Maze: def add_enemy(self): """Add enough enemies.""" - walls, length = [], log(self.score, GOLDEN_MEAN) + walls = [] for i in self.rangex: for j in self.rangey: if self.map[i][j] == WALL: walls.append((i, j)) - while walls and len(self.enemies) < length: + while walls and len(self.enemies) < log(self.score, INIT_SCORE): x, y = choice(walls) if all(self.map[x + a][y + b] == WALL for a, b in ADJACENT_GRIDS): continue self.enemies.append(Enemy(self, x, y)) walls.remove((x, y)) + def pos(self, x, y): + """Return coordinate of the center of the grid (x, y).""" + return (self.middlex + (x - MIDDLE)*self.distance, + self.middley + (y - MIDDLE)*self.distance) + def draw(self): """Draw the maze.""" self.surface.fill(BG_COLOR) for i in self.rangex: for j in self.rangey: if self.map[i][j] != WALL: continue - x, y = pos(i, j, self.distance, self.middlex, self.middley) + x, y = self.pos(i, j) square = regpoly(4, self.distance / SQRT2, pi / 4, x, y) fill_aapolygon(self.surface, square, FG_COLOR) def wake(self, enemy): """Wake the enemy up if it can see the hero.""" - dx = (enemy.x - MIDDLE)*self.distance + self.offsetx*self.step - dy = (enemy.y - MIDDLE)*self.distance + self.offsety*self.step + dx = (enemy.x-MIDDLE)*self.distance + self.middlex - self.x + dy = (enemy.y-MIDDLE)*self.distance + self.middley - self.y mind = cosin(abs(atan(dy / dx)) if dx else 0) * self.distance startx = starty = MIDDLE stopx, stopy = enemy.x, enemy.y @@ -112,24 +115,27 @@ class Maze: for i in range(startx, stopx + 1): for j in range(starty, stopy + 1): if self.map[i][j] != WALL: continue - x, y = pos(i, j, self.distance, self.middlex, self.middley) + x, y = self.pos(i, j) d = abs(dy*(x-self.x) - dx*(y-self.y)) / (dy**2 + dx**2)**0.5 if d <= mind: return enemy.awake = True - def rotate(self, x, y): - """Rotate the maze by (x, y).""" - if not x and not y: return + def rotate(self): + """Rotate the maze if needed.""" + x = int((self.middlex-self.x) * 2 / self.distance) + y = int((self.middley-self.y) * 2 / self.distance) + if x == y == 0: return for enemy in self.enemies: self.map[enemy.x][enemy.y] = EMPTY - + self.map[MIDDLE][MIDDLE] = EMPTY if x: - self.offsetx = 0.0 + self.middlex -= x * self.distance self.map.rotate(x) self.rotatex += x if y: - self.offsety = 0.0 + self.middley -= y * self.distance for d in self.map: d.rotate(y) self.rotatey += y + self.map[MIDDLE][MIDDLE] = HERO # Respawn the enemies that fall off the display killist = [] @@ -161,12 +167,18 @@ class Maze: for k in range(ROAD_WIDTH): self.map[c + k][LAST_ROW + j] = grid + def length(self, x, y): + """Return the length of the line segment joining the center of + the maze and the point (x, y). + """ + return ((self.x-x)**2 + (self.y-y)**2)**0.5 + def slash(self): """Handle close-ranged attacks.""" for enemy in self.enemies: if not enemy.spin_queue: continue x, y = enemy.pos() - d = self.slashd - length(x, y, self.x, self.y) + d = self.slashd - self.length(x, y) if d >= 0: self.hero.wound += d / self.hero.R / enemy.spin_speed @@ -174,9 +186,9 @@ class Maze: unit, killist = self.distance/SQRT2 * self.hero.spin_speed, [] for i, enemy in enumerate(self.enemies): x, y = enemy.pos() - d = length(x, y, self.x, self.y) + d = self.length(x, y) if d <= self.slashd: - enemy.wound += (self.slashd-d) / unit + enemy.hit((self.slashd-d) / unit) if enemy.wound >= ENEMY_HP: self.score += enemy.wound enemy.die() @@ -205,56 +217,47 @@ class Maze: continue for j, enemy in enumerate(self.enemies): x, y = enemy.pos() - if length(bullet.x, bullet.y, x, y) < self.distance: - enemy.wound += FIRE_DAM + if bullet.length(x, y) < self.distance: + enemy.hit(wound) if enemy.wound >= ENEMY_HP: self.score += enemy.wound enemy.die() self.enemies.pop(j) fallen.append(i) break - elif length(bullet.x, bullet.y, self.x, self.y) < self.distance: - if not self.hero.spin_queue: self.hero.wound += FIRE_DAM + elif bullet.length(self.x, self.y) < self.distance: + if not self.hero.spin_queue: self.hero.wound += wound fallen.append(i) for i in reversed(fallen): self.bullets.pop(i) + def isvalid(self, step, dx=0, dy=0): + """Return True if it is valid to move by (dx, dy) (in steps), + False otherwise. + """ + d = self.distance/2 + self.hero.R + herox, heroy = self.x - step*dx, self.y - step*dy + for x in range(MIDDLE - dx - 1, MIDDLE - dx + 2): + for y in range(MIDDLE - dy - 1, MIDDLE - dy + 2): + gridx, gridy = self.pos(x, y) + if (max(abs(herox - gridx), abs(heroy - gridy)) < d + and self.map[x][y] == WALL): + return False + return True + def update(self, fps): """Update the maze.""" if self.paused: return - self.offsetx *= fps / self.fps - self.offsety *= fps / self.fps - self.fps, self.speed = fps, fps / HERO_SPEED - self.step = self.distance / self.speed - - d = self.distance*1.5 - self.hero.R - dx = sign(self.right) - sign(self.left) - self.offsetx += dx - dy = sign(self.down) - sign(self.up) - self.offsety += dy - x, y = MIDDLE - sign(self.offsetx)*2, MIDDLE - sign(self.offsety)*2 - if ((self.map[x][MIDDLE - 1] != EMPTY - or self.map[x][MIDDLE] != EMPTY - or self.map[x][MIDDLE + 1] != EMPTY) - and abs(self.offsetx*self.step) > d): - self.offsetx -= dx - dx = 0 - if ((self.map[MIDDLE - 1][y] != EMPTY - or self.map[MIDDLE][y] != EMPTY - or self.map[MIDDLE + 1][y] != EMPTY) - and abs(self.offsety*self.step) > d): - self.offsety -= dy - dy = 0 + self.fps, step = fps, self.distance * HERO_SPEED / fps + dx = step * self.right * self.isvalid(step, dx=self.right) + self.middlex += dx + dy = step * self.down * self.isvalid(step, dy=self.down) + self.middley += dy if dx or dy: - self.map[MIDDLE][MIDDLE] = EMPTY - self.rotate(sign(self.offsetx) * (abs(self.offsetx)>=self.speed), - sign(self.offsety) * (abs(self.offsety)>=self.speed)) - self.map[MIDDLE][MIDDLE] = HERO - self.middlex = self.x + self.offsetx*self.step - self.middley = self.y + self.offsety*self.step + self.rotate() for enemy in self.enemies: if not enemy.awake: self.wake(enemy) - for bullet in self.bullets: bullet.place(dx, dy, self.step) + for bullet in self.bullets: bullet.place(dx, dy) self.draw() for enemy in self.enemies: enemy.update() @@ -272,26 +275,18 @@ class Maze: self.surface = pygame.display.set_mode(size, RESIZABLE) self.hero.resize() + offsetx = (self.middlex-self.x) / self.distance + offsety = (self.middley-self.y) / self.distance self.distance = (w * h / 416) ** 0.5 - self.step = self.distance / self.speed - self.middlex = self.x + self.offsetx*self.step - self.middley = self.y + self.offsety*self.step self.x, self.y = w >> 1, h >> 1 + self.middlex = self.x + offsetx*self.distance + self.middley = self.y + offsety*self.distance w, h = int(w/self.distance/2 + 2), int(h/self.distance/2 + 2) self.rangex = range(MIDDLE - w, MIDDLE + w + 1) self.rangey = range(MIDDLE - h, MIDDLE + h + 1) self.slashd = self.hero.R + self.distance/SQRT2 - def move(self, up=0, left=0, down=0, right=0): - """Make the maze to move in the given directions by moving the - maze in the reverse way. - """ - self.up += up - self.left += left - self.down += down - self.right += right - def lose(self): """Handle loses.""" self.hero.die() - self.up = self.left = self.down = self.right = 0 + self.down = self.right = 0 diff --git a/brutalmaze/utils.py b/brutalmaze/utils.py index ac47d2d..fdf893f 100644 --- a/brutalmaze/utils.py +++ b/brutalmaze/utils.py @@ -17,14 +17,21 @@ # # Copyright (C) 2017 Nguyễn Gia Phong +__doc__ = 'brutalmaze module for hero and enemy classes' + +from functools import reduce from math import cos, sin, pi +from operator import or_ import pygame from pygame.gfxdraw import filled_polygon, aapolygon from .constants import MIDDLE -__doc__ = 'brutalmaze module for hero and enemy classes' + +def some(a, keys): + """Return True if there is a key k in keys that bool(a[k]) is True.""" + return bool(reduce(or_, (a[k] for k in keys))) def round2(number): @@ -53,11 +60,6 @@ def fill_aapolygon(surface, points, color): filled_polygon(surface, points, color) -def pos(x, y, distance, middlex, middley): - """Return coordinate of the center of the grid (x, y).""" - return middlex + (x - MIDDLE)*distance, middley + (y - MIDDLE)*distance - - def sign(n): """Return the sign of number n.""" return -1 if n < 0 else 1 if n else 0 @@ -66,10 +68,3 @@ def sign(n): def cosin(x): """Return the sum of cosine and sine of x (measured in radians).""" return cos(x) + sin(x) - - -def length(x0, y0, x1, y1): - """Return the length of the line segment joining the two points - (x0, y0) and (x1, y1). - """ - return ((x0-x1)**2 + (y0-y1)**2)**0.5 diff --git a/brutalmaze/weapons.py b/brutalmaze/weapons.py index 110fc10..062ba68 100644 --- a/brutalmaze/weapons.py +++ b/brutalmaze/weapons.py @@ -17,6 +17,8 @@ # # Copyright (C) 2017 Nguyễn Gia Phong +__doc__ = 'brutalmaze module for weapon classes' + from math import cos, sin from pygame.time import get_ticks @@ -24,8 +26,6 @@ from pygame.time import get_ticks from .constants import BULLET_LIFETIME, BULLET_SPEED from .utils import regpoly, fill_aapolygon -__doc__ = 'brutalmaze module for weapon classes' - class Bullet: """Object representing a bullet.""" @@ -42,7 +42,13 @@ class Bullet: hexagon = regpoly(5, distance // 4, self.angle, self.x, self.y) fill_aapolygon(self.surface, hexagon, self.color) - def place(self, x, y, step): - """Move the bullet by (x, y) (in steps).""" - self.x += x * step - self.y += y * step + def place(self, x, y): + """Move the bullet by (x, y) (in pixels).""" + self.x += x + self.y += y + + def length(self, x, y): + """Return the length of the line segment joining the center of + the bullet and the point (x, y). + """ + return ((self.x-x)**2 + (self.y-y)**2)**0.5 diff --git a/setup.py b/setup.py index 083b11b..b1f9853 100755 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( author_email='vn.mcsinyx@gmail.com', license='GPLv3+', classifiers=[ - 'Development Status :: 1 - Planning' + 'Development Status :: 2 - Pre-Alpha', 'Environment :: MacOS X', 'Environment :: Win32 (MS Windows)', 'Environment :: X11 Applications', @@ -25,7 +25,7 @@ setup( 'Operating System :: OS Independent', 'Programming Language :: Python', 'Topic :: Games/Entertainment :: Arcade'], - keywords='', + keywords='pygame action-game', packages=['brutalmaze'], install_requires=['pygame>=1.9'], package_data={'brutalmaze': ['icon.png']}, |