Update documentations
This commit is contained in:
parent
13e0ea6f98
commit
b1f0c052f9
21
README.rst
21
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.
|
||||
|
|
|
@ -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]))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
4
setup.py
4
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',
|
||||
|
|
Loading…
Reference in New Issue