Update documentations

This commit is contained in:
Nguyễn Gia Phong 2017-11-04 21:43:05 +07:00
parent 13e0ea6f98
commit b1f0c052f9
5 changed files with 116 additions and 58 deletions

View File

@ -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 (if there is any). Be aware that the more get killed, the more will show up and
our hero will get weaker when wounded. 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. * Highly portable.
* Auto-generated and infinite maze. * Auto-generated and infinite maze.
* No binary art data. * No binary data for drawing.
* Enemies with randomized attributes: stun, poison, etc. * Enemies with randomized attributes: stun, poison, camo, etc.
* Resizable in realtime. * Somewhat a realistic physic and logic system.
* Resizable game window in-game.
Installation 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: The installation procedure should be as simply as follow:
* Install Python and `pip <https://pip.pypa.io/en/latest/>`_. Make sure the * Install Python and `pip <https://pip.pypa.io/en/latest/>`_. Make sure the
directory for Python executables is your ``PATH``. directory for `Python scripts <https://docs.python.org/2/install/index.html#alternate-installation-the-user-scheme>`_
* Clone the Github repository or download the Zip achieve and unpack. is your ``PATH``.
* Open Terminal in the directory containing the repo's folder and run * Open Terminal or Command Prompt and run ``pip install --user brutalmaze``.
``pip install --user brutalmaze``. Now you can lauch the game by running the command ``brutalmaze``.
Control Control
------- -------
@ -54,7 +55,7 @@ Right, ``d``
Move right. Move right.
Left Mouse Left Mouse
Long-ranged attack. Long-range attack.
Return, Right Mouse Return, Right Mouse
Close-ranged attack. Close-range attack, also dodge from bullets.

View File

@ -20,18 +20,33 @@
__doc__ = 'brutalmaze module for hero and enemy classes' __doc__ = 'brutalmaze module for hero and enemy classes'
from collections import deque from collections import deque
from math import atan2, sin, pi from math import atan, atan2, sin, pi
from random import choice, shuffle, uniform from random import choice, shuffle, uniform
import pygame import pygame
from .constants import * from .constants import *
from .utils import randsign, regpoly, fill_aapolygon, sign from .utils import sign, cosin, randsign, regpoly, fill_aapolygon
from .weapons import Bullet from .weapons import Bullet
class Hero: 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): def __init__(self, surface, fps):
self.surface = surface self.surface = surface
w, h = self.surface.get_width(), self.surface.get_height() w, h = self.surface.get_width(), self.surface.get_height()
@ -82,15 +97,29 @@ class Hero:
class Enemy: 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): def __init__(self, maze, x, y):
self.maze = maze self.maze = maze
self.angle, self.color = pi / 4, TANGO[choice(ENEMIES)]
self.x, self.y = x, y self.x, self.y = x, y
self.maze.map[x][y] = ENEMY self.maze.map[x][y] = ENEMY
self.angle, self.color = pi / 4, TANGO[choice(ENEMIES)]
self.awake = False self.awake = False
self.next_move = 0 self.next_strike = 0
self.move_speed = self.maze.fps / ENEMY_SPEED self.move_speed = self.maze.fps / ENEMY_SPEED
self.offsetx = self.offsety = 0 self.offsetx = self.offsety = 0
self.spin_speed = self.maze.fps / ENEMY_HP self.spin_speed = self.maze.fps / ENEMY_HP
@ -108,15 +137,33 @@ class Enemy:
self.y += y self.y += y
self.maze.map[self.x][self.y] = ENEMY 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): def fire(self):
"""Return True if the enemy shot the hero, False otherwise.""" """Return True if the enemy shot the hero, False otherwise."""
x, y = self.pos() x, y = self.pos()
if (self.maze.length(x, 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.next_strike > pygame.time.get_ticks()
or (self.x, self.y) in AROUND_HERO or self.offsetx or self.offsety or (self.x, self.y) in AROUND_HERO or self.offsetx or self.offsety
or uniform(-2, 2) < (INIT_SCORE/self.maze.score) ** 2): or uniform(-2, 2) < (INIT_SCORE/self.maze.score) ** 2):
return False 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.bullets.append(Bullet(
self.maze.surface, x, y, self.maze.surface, x, y,
atan2(self.maze.y - y, self.maze.x - x), self.color[0])) atan2(self.maze.y - y, self.maze.x - x), self.color[0]))

View File

@ -20,7 +20,7 @@
__doc__ = 'brutalmaze module for the maze class' __doc__ = 'brutalmaze module for the maze class'
from collections import deque from collections import deque
from math import pi, atan, atan2, log from math import pi, atan2, log
from random import choice, getrandbits from random import choice, getrandbits
import pygame import pygame
@ -28,7 +28,7 @@ from pygame import RESIZABLE
from .characters import Hero, Enemy from .characters import Hero, Enemy
from .constants import * from .constants import *
from .utils import round2, sign, cosin, regpoly, fill_aapolygon from .utils import round2, sign, regpoly, fill_aapolygon
from .weapons import Bullet from .weapons import Bullet
@ -53,13 +53,33 @@ def new_column():
class Maze: 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): def __init__(self, size, fps):
self.w, self.h = size self.w, self.h = size
self.fps = fps self.fps = fps
self.surface = pygame.display.set_mode(size, RESIZABLE) self.surface = pygame.display.set_mode(size, RESIZABLE)
self.distance = (self.w * self.h / 416) ** 0.5 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) w, h = (int(i/self.distance/2 + 2) for i in size)
self.rangex = range(MIDDLE - w, MIDDLE + w + 1) self.rangex = range(MIDDLE - w, MIDDLE + w + 1)
self.rangey = range(MIDDLE - h, MIDDLE + h + 1) self.rangey = range(MIDDLE - h, MIDDLE + h + 1)
@ -90,8 +110,8 @@ class Maze:
def pos(self, x, y): def pos(self, x, y):
"""Return coordinate of the center of the grid (x, y).""" """Return coordinate of the center of the grid (x, y)."""
return (self.middlex + (x - MIDDLE)*self.distance, return (self.centerx + (x - MIDDLE)*self.distance,
self.middley + (y - MIDDLE)*self.distance) self.centery + (y - MIDDLE)*self.distance)
def draw(self): def draw(self):
"""Draw the maze.""" """Draw the maze."""
@ -103,36 +123,19 @@ class Maze:
square = regpoly(4, self.distance / SQRT2, pi / 4, x, y) square = regpoly(4, self.distance / SQRT2, pi / 4, x, y)
fill_aapolygon(self.surface, square, FG_COLOR) 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): def rotate(self):
"""Rotate the maze if needed.""" """Rotate the maze if needed."""
x = int((self.middlex-self.x) * 2 / self.distance) x = int((self.centerx-self.x) * 2 / self.distance)
y = int((self.middley-self.y) * 2 / self.distance) y = int((self.centery-self.y) * 2 / self.distance)
if x == y == 0: return if x == y == 0: return
for enemy in self.enemies: self.map[enemy.x][enemy.y] = EMPTY for enemy in self.enemies: self.map[enemy.x][enemy.y] = EMPTY
self.map[MIDDLE][MIDDLE] = EMPTY self.map[MIDDLE][MIDDLE] = EMPTY
if x: if x:
self.middlex -= x * self.distance self.centerx -= x * self.distance
self.map.rotate(x) self.map.rotate(x)
self.rotatex += x self.rotatex += x
if y: if y:
self.middley -= y * self.distance self.centery -= y * self.distance
for d in self.map: d.rotate(y) for d in self.map: d.rotate(y)
self.rotatey += y self.rotatey += y
self.map[MIDDLE][MIDDLE] = HERO self.map[MIDDLE][MIDDLE] = HERO
@ -174,7 +177,7 @@ class Maze:
return ((self.x-x)**2 + (self.y-y)**2)**0.5 return ((self.x-x)**2 + (self.y-y)**2)**0.5
def slash(self): def slash(self):
"""Handle close-ranged attacks.""" """Handle close-range attacks."""
for enemy in self.enemies: for enemy in self.enemies:
if not enemy.spin_queue: continue if not enemy.spin_queue: continue
x, y = enemy.pos() x, y = enemy.pos()
@ -249,14 +252,13 @@ class Maze:
if self.paused: return if self.paused: return
self.fps, step = fps, self.distance * HERO_SPEED / fps self.fps, step = fps, self.distance * HERO_SPEED / fps
dx = step * self.right * self.isvalid(step, dx=self.right) 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) dy = step * self.down * self.isvalid(step, dy=self.down)
self.middley += dy self.centery += dy
if dx or dy: if dx or dy:
self.rotate() self.rotate()
for enemy in self.enemies: for enemy in self.enemies: enemy.wake()
if not enemy.awake: self.wake(enemy)
for bullet in self.bullets: bullet.place(dx, dy) for bullet in self.bullets: bullet.place(dx, dy)
self.draw() self.draw()
@ -275,12 +277,12 @@ class Maze:
self.surface = pygame.display.set_mode(size, RESIZABLE) self.surface = pygame.display.set_mode(size, RESIZABLE)
self.hero.resize() self.hero.resize()
offsetx = (self.middlex-self.x) / self.distance offsetx = (self.centerx-self.x) / self.distance
offsety = (self.middley-self.y) / self.distance offsety = (self.centery-self.y) / self.distance
self.distance = (w * h / 416) ** 0.5 self.distance = (w * h / 416) ** 0.5
self.x, self.y = w >> 1, h >> 1 self.x, self.y = w // 2, h // 2
self.middlex = self.x + offsetx*self.distance self.centerx = self.x + offsetx*self.distance
self.middley = self.y + offsety*self.distance self.centery = self.y + offsety*self.distance
w, h = int(w/self.distance/2 + 2), int(h/self.distance/2 + 2) w, h = int(w/self.distance/2 + 2), int(h/self.distance/2 + 2)
self.rangex = range(MIDDLE - w, MIDDLE + w + 1) self.rangex = range(MIDDLE - w, MIDDLE + w + 1)
self.rangey = range(MIDDLE - h, MIDDLE + h + 1) self.rangey = range(MIDDLE - h, MIDDLE + h + 1)

View File

@ -28,7 +28,15 @@ from .utils import regpoly, fill_aapolygon
class Bullet: 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): def __init__(self, surface, x, y, angle, color):
self.surface = surface self.surface = surface
self.x, self.y, self.angle, self.color = x, y, angle, color self.x, self.y, self.angle, self.color = x, y, angle, color

View File

@ -7,8 +7,8 @@ with open('README.rst') as f:
setup( setup(
name='brutalmaze', name='brutalmaze',
version='0.0.1', version='0.0.2',
description='Brutal Maze', description='A research hash and slash game with fast-paced action and a minimalist art style',
long_description=long_description, long_description=long_description,
url='https://github.com/McSinyx/brutalmaze', url='https://github.com/McSinyx/brutalmaze',
author='Nguyễn Gia Phong', author='Nguyễn Gia Phong',