diff options
-rw-r--r-- | README.rst | 21 | ||||
-rw-r--r-- | brutalmaze/characters.py | 63 | ||||
-rw-r--r-- | brutalmaze/maze.py | 76 | ||||
-rw-r--r-- | brutalmaze/weapons.py | 10 | ||||
-rwxr-xr-x | setup.py | 4 |
5 files changed, 116 insertions, 58 deletions
diff --git a/README.rst b/README.rst index 720d270..705f722 100644 --- a/README.rst +++ b/README.rst @@ -12,13 +12,14 @@ job is to help the trigon fight against those evil squares and find a way out (if there is any). Be aware that the more get killed, the more will show up and our hero will get weaker when wounded. -As a research game, Brutal Maze has a few primary goals: +Being a research game, Brutal Maze has a few primary goals: * Highly portable. * Auto-generated and infinite maze. -* No binary art data. -* Enemies with randomized attributes: stun, poison, etc. -* Resizable in realtime. +* No binary data for drawing. +* Enemies with randomized attributes: stun, poison, camo, etc. +* Somewhat a realistic physic and logic system. +* Resizable game window in-game. Installation ------------ @@ -27,10 +28,10 @@ Brutal Maze is written in Python and is compatible with both version 2 and 3. The installation procedure should be as simply as follow: * Install Python and `pip <https://pip.pypa.io/en/latest/>`_. Make sure the - directory for Python executables is your ``PATH``. -* Clone the Github repository or download the Zip achieve and unpack. -* Open Terminal in the directory containing the repo's folder and run - ``pip install --user brutalmaze``. + directory for `Python scripts <https://docs.python.org/2/install/index.html#alternate-installation-the-user-scheme>`_ + is your ``PATH``. +* Open Terminal or Command Prompt and run ``pip install --user brutalmaze``. + Now you can lauch the game by running the command ``brutalmaze``. Control ------- @@ -54,7 +55,7 @@ Right, ``d`` Move right. Left Mouse - Long-ranged attack. + Long-range attack. Return, Right Mouse - Close-ranged attack. + Close-range attack, also dodge from bullets. diff --git a/brutalmaze/characters.py b/brutalmaze/characters.py index 7a68f37..2e33379 100644 --- a/brutalmaze/characters.py +++ b/brutalmaze/characters.py @@ -20,18 +20,33 @@ __doc__ = 'brutalmaze module for hero and enemy classes' from collections import deque -from math import atan2, sin, pi +from math import atan, atan2, sin, pi from random import choice, shuffle, uniform import pygame from .constants import * -from .utils import randsign, regpoly, fill_aapolygon, sign +from .utils import sign, cosin, randsign, regpoly, fill_aapolygon from .weapons import Bullet class Hero: - """Object representing the hero.""" + """Object representing the hero. + + Attributes: + surface (pygame.Surface): the display to draw on + x, y (int): coordinates of the center of the hero (in pixels) + angle (float): angle of the direction the hero pointing (in radians) + color (tuple of pygame.Color): colors of the hero on different HPs + R (int): circumradius of the regular triangle representing the hero + next_strike (int): the tick that the hero can do the next attack + slashing (bool): flag indicates if the hero is doing close-range attack + firing (bool): flag indicates if the hero is doing long-range attack + dead (bool): flag indicates if the hero is dead + spin_speed (float): speed of spinning (in frames per slash) + spin_queue (float): frames left to finish spinning + wound (float): amount of wound + """ def __init__(self, surface, fps): self.surface = surface w, h = self.surface.get_width(), self.surface.get_height() @@ -82,15 +97,29 @@ class Hero: class Enemy: - """Object representing an enemy.""" + """Object representing an enemy. + + Attributes: + maze (Maze): the maze + x, y (int): coordinates of the center of the enemy (in grids) + angle (float): angle of the direction the enemy pointing (in radians) + color (tuple of pygame.Color): colors of the enemy on different HPs + awake (bool): flag indicates if the enemy is active + next_strike (int): the tick that the enemy can do the next attack + move_speed (float): speed of movement (in frames per grid) + offsetx, offsety (integer): steps moved from the center of the grid + spin_speed (float): speed of spinning (in frames per slash) + spin_queue (float): frames left to finish spinning + wound (float): amount of wound + """ def __init__(self, maze, x, y): self.maze = maze - self.angle, self.color = pi / 4, TANGO[choice(ENEMIES)] self.x, self.y = x, y self.maze.map[x][y] = ENEMY + self.angle, self.color = pi / 4, TANGO[choice(ENEMIES)] self.awake = False - self.next_move = 0 + self.next_strike = 0 self.move_speed = self.maze.fps / ENEMY_SPEED self.offsetx = self.offsety = 0 self.spin_speed = self.maze.fps / ENEMY_HP @@ -108,15 +137,33 @@ class Enemy: self.y += y self.maze.map[self.x][self.y] = ENEMY + def wake(self): + """Wake the enemy up if it can see the hero.""" + if self.awake: return + startx = starty = MIDDLE + stopx, stopy, distance = self.x, self.y, self.maze.distance + if startx > stopx: startx, stopx = stopx, startx + if starty > stopy: starty, stopy = stopy, starty + dx = (self.x-MIDDLE)*distance + self.maze.centerx - self.maze.x + dy = (self.y-MIDDLE)*distance + self.maze.centery - self.maze.y + mind = cosin(abs(atan(dy / dx)) if dx else 0) * distance + def length(x, y): return abs(dy*x - dx*y) / (dy**2 + dx**2)**0.5 + for i in range(startx, stopx + 1): + for j in range(starty, stopy + 1): + if self.maze.map[i][j] != WALL: continue + x, y = self.maze.pos(i, j) + if length(x - self.maze.x, y - self.maze.y) <= mind: return + self.awake = True + def fire(self): """Return True if the enemy shot the hero, False otherwise.""" x, y = self.pos() if (self.maze.length(x, y) > FIRANGE*self.maze.distance - or self.next_move > pygame.time.get_ticks() + or self.next_strike > 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): return False - self.next_move = pygame.time.get_ticks() + ATTACK_SPEED + self.next_strike = pygame.time.get_ticks() + ATTACK_SPEED self.maze.bullets.append(Bullet( self.maze.surface, x, y, atan2(self.maze.y - y, self.maze.x - x), self.color[0])) diff --git a/brutalmaze/maze.py b/brutalmaze/maze.py index ae12ded..8fd8645 100644 --- a/brutalmaze/maze.py +++ b/brutalmaze/maze.py @@ -20,7 +20,7 @@ __doc__ = 'brutalmaze module for the maze class' from collections import deque -from math import pi, atan, atan2, log +from math import pi, atan2, log from random import choice, getrandbits import pygame @@ -28,7 +28,7 @@ from pygame import RESIZABLE from .characters import Hero, Enemy from .constants import * -from .utils import round2, sign, cosin, regpoly, fill_aapolygon +from .utils import round2, sign, regpoly, fill_aapolygon from .weapons import Bullet @@ -53,13 +53,33 @@ def new_column(): class Maze: - """Object representing the maze, including the characters.""" + """Object representing the maze, including the characters. + + Attributes: + w, h: width and height of the display + fps: current frame rate + surface (pygame.Surface): the display to draw on + distance (float): distance between centers of grids (in px) + x, y (int): coordinates of the center of the hero (in px) + centerx, centery (float): center grid's center's coordinates (in px) + rangex, rangey: range of the index of the grids on display + paused (bool): flag indicates if the game is paused + score (float): current score + map (deque of deque): map of grids representing objects on the maze + down, right (int): direction the maze moving + rotatex, rotatey: grids rotated + bullets (list of Bullet): bullets flying + enemies (list of Enemy): alive enemies + hero (Hero): the hero + slashd (float): minimum distance for slashes to be effective + """ def __init__(self, size, fps): self.w, self.h = size self.fps = fps self.surface = pygame.display.set_mode(size, RESIZABLE) self.distance = (self.w * self.h / 416) ** 0.5 - self.middlex, self.middley = self.x, self.y = self.w >> 1, self.h >> 1 + self.x, self.y = self.w // 2, self.h // 2 + self.centerx, self.centery = self.w / 2.0, self.h / 2.0 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) @@ -90,8 +110,8 @@ class Maze: 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) + return (self.centerx + (x - MIDDLE)*self.distance, + self.centery + (y - MIDDLE)*self.distance) def draw(self): """Draw the maze.""" @@ -103,36 +123,19 @@ class Maze: 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.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 - if startx > stopx : startx, stopx = stopx, startx - if starty > stopy : starty, stopy = stopy, starty - for i in range(startx, stopx + 1): - for j in range(starty, stopy + 1): - if self.map[i][j] != WALL: continue - 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): """Rotate the maze if needed.""" - x = int((self.middlex-self.x) * 2 / self.distance) - y = int((self.middley-self.y) * 2 / self.distance) + x = int((self.centerx-self.x) * 2 / self.distance) + y = int((self.centery-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.middlex -= x * self.distance + self.centerx -= x * self.distance self.map.rotate(x) self.rotatex += x if y: - self.middley -= y * self.distance + self.centery -= y * self.distance for d in self.map: d.rotate(y) self.rotatey += y self.map[MIDDLE][MIDDLE] = HERO @@ -174,7 +177,7 @@ class Maze: return ((self.x-x)**2 + (self.y-y)**2)**0.5 def slash(self): - """Handle close-ranged attacks.""" + """Handle close-range attacks.""" for enemy in self.enemies: if not enemy.spin_queue: continue x, y = enemy.pos() @@ -249,14 +252,13 @@ class Maze: if self.paused: return self.fps, step = fps, self.distance * HERO_SPEED / fps dx = step * self.right * self.isvalid(step, dx=self.right) - self.middlex += dx + self.centerx += dx dy = step * self.down * self.isvalid(step, dy=self.down) - self.middley += dy + self.centery += dy if dx or dy: self.rotate() - for enemy in self.enemies: - if not enemy.awake: self.wake(enemy) + for enemy in self.enemies: enemy.wake() for bullet in self.bullets: bullet.place(dx, dy) self.draw() @@ -275,12 +277,12 @@ 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 + offsetx = (self.centerx-self.x) / self.distance + offsety = (self.centery-self.y) / self.distance self.distance = (w * h / 416) ** 0.5 - self.x, self.y = w >> 1, h >> 1 - self.middlex = self.x + offsetx*self.distance - self.middley = self.y + offsety*self.distance + self.x, self.y = w // 2, h // 2 + self.centerx = self.x + offsetx*self.distance + self.centery = 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) diff --git a/brutalmaze/weapons.py b/brutalmaze/weapons.py index 062ba68..7504457 100644 --- a/brutalmaze/weapons.py +++ b/brutalmaze/weapons.py @@ -28,7 +28,15 @@ from .utils import regpoly, fill_aapolygon class Bullet: - """Object representing a bullet.""" + """Object representing a bullet. + + Attributes: + surface (pygame.Surface): the display to draw on + x, y (int): coordinates of the center of the bullet (in pixels) + angle (float): angle of the direction the bullet pointing (in radians) + color (pygame.Color): color of the bullet + fall_time (int): the tick that the bullet will fall down + """ def __init__(self, surface, x, y, angle, color): self.surface = surface self.x, self.y, self.angle, self.color = x, y, angle, color diff --git a/setup.py b/setup.py index b1f9853..cc739a2 100755 --- a/setup.py +++ b/setup.py @@ -7,8 +7,8 @@ with open('README.rst') as f: setup( name='brutalmaze', - version='0.0.1', - description='Brutal Maze', + version='0.0.2', + description='A research hash and slash game with fast-paced action and a minimalist art style', long_description=long_description, url='https://github.com/McSinyx/brutalmaze', author='Nguyễn Gia Phong', |