diff --git a/README.rst b/README.rst
index 557069f..7676d22 100644
--- a/README.rst
+++ b/README.rst
@@ -4,7 +4,7 @@ Brutal Maze
Brutal Maze is a research hash and slash game with fast-paced action and a
minimalist art style.
-.. image:: https://raw.githubusercontent.com/McSinyx/brutalmaze/master/brutalmaze/icon.png
+.. image:: https://raw.githubusercontent.com/McSinyx/brutalmaze/master/screenshot.png
The game features a trigon trapped in an infinite maze. As our hero tries to
escape, the maze's border turn into aggressive squares trying to stop him. Your
@@ -26,8 +26,8 @@ Installation
Brutal Maze is written in Python and is compatible with both version 2 and 3.
The installation procedure should be as simply as follow:
-* Make sure you have Python and `pip `_
- installed on your system.
+* Install Python and `pip `_. 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``.
diff --git a/brutalmaze/characters.py b/brutalmaze/characters.py
index dd69d69..0490a55 100644
--- a/brutalmaze/characters.py
+++ b/brutalmaze/characters.py
@@ -17,17 +17,17 @@
#
# 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 shuffle
+from random import choice, shuffle
import pygame
from .constants import *
from .utils import randsign, regpoly, fill_aapolygon, pos, sign
+__doc__ = 'brutalmaze module for hero and enemy classes'
+
class Hero:
"""Object representing the hero."""
@@ -40,15 +40,15 @@ class Hero:
self.next_strike = 0
self.slashing = self.firing = False
- self.spin_speed = fps / len(self.color)
+ self.spin_speed = fps / HERO_HP
self.spin_queue = self.wound = 0.0
def update(self, fps):
"""Update the hero."""
old_speed, time = self.spin_speed, pygame.time.get_ticks()
- self.spin_speed = fps / (len(self.color)-self.wound**0.5)
+ self.spin_speed = fps / (HERO_HP-self.wound**0.5)
self.spin_queue *= self.spin_speed / old_speed
- self.wound -= HEAL_SPEED / len(self.color) / self.spin_speed
+ self.wound -= HEAL_SPEED / self.spin_speed / HERO_HP
if self.wound < 0: self.wound = 0.0
if self.slashing and time >= self.next_strike:
@@ -74,9 +74,9 @@ class Hero:
class Enemy:
"""Object representing an enemy."""
- def __init__(self, surface, fps, maze, kind, x, y):
+ def __init__(self, surface, fps, maze, x, y):
self.surface, self.maze = surface, maze
- self.angle, self.color = pi / 4, TANGO[kind]
+ self.angle, self.color = pi / 4, TANGO[choice(ENEMIES)]
self.x, self.y = x, y
self.maze[x][y] = ENEMY
@@ -84,7 +84,7 @@ class Enemy:
self.next_move = 0
self.move_speed = fps / MOVE_SPEED
self.offsetx = self.offsety = 0
- self.spin_speed = fps / len(self.color)
+ self.spin_speed = fps / ENEMY_HP
self.spin_queue = self.wound = 0.0
def firable(self):
@@ -137,7 +137,7 @@ class Enemy:
def update(self, fps, distance, middlex, middley):
"""Update the enemy."""
if self.awake:
- self.spin_speed, old_speed = fps / len(self.color), self.spin_speed
+ self.spin_speed, old_speed = fps / ENEMY_HP, self.spin_speed
self.spin_queue *= self.spin_speed / old_speed
if not self.spin_queue and not self.move(fps):
self.spin_queue = randsign() * self.spin_speed
diff --git a/brutalmaze/constants.py b/brutalmaze/constants.py
index e85c1cb..d3d97b5 100644
--- a/brutalmaze/constants.py
+++ b/brutalmaze/constants.py
@@ -18,7 +18,6 @@
# Copyright (C) 2017 Nguyễn Gia Phong
from pygame import image
-from pygame.locals import *
from pkg_resources import resource_filename
__doc__ = 'brutalmaze module for shared constants'
@@ -29,6 +28,7 @@ SQRT2 = 2 ** 0.5
GOLDEN_MEAN = 5**0.5/2 + 0.5
INIT_FPS = 30.0
+MAX_FPS = 144.0
SIZE = 640, 480
MAZE_SIZE = 10
ROAD_WIDTH = 5 # grids
@@ -38,7 +38,7 @@ LAST_ROW = (MAZE_SIZE-1) * ROAD_WIDTH * 2
INIT_SCORE = 208.2016
MOVE_SPEED = 5 # grid/s
BULLET_SPEED = 10 # grid/s
-HEAL_SPEED = 1.0 # HP/s
+HEAL_SPEED = 1 # HP/s
ATTACK_SPEED = 333 # ms/strike
BULLET_LIFETIME = 1000 # ms
@@ -56,5 +56,7 @@ TANGO = {'Butter': ((252, 233, 79), (237, 212, 0), (196, 160, 0)),
(136, 138, 133), (85, 87, 83), (46, 52, 54))}
ENEMIES = ('Butter', 'Orange', 'Chocolate', 'Chameleon',
'Sky Blue', 'Plum', 'Scarlet Red')
+ENEMY_HP = 3
+HERO_HP = 6
BG_COLOR = TANGO['Aluminium'][-1]
FG_COLOR = TANGO['Aluminium'][0]
diff --git a/brutalmaze/main.py b/brutalmaze/main.py
index eb17954..c58e9cb 100644
--- a/brutalmaze/main.py
+++ b/brutalmaze/main.py
@@ -20,8 +20,9 @@
from collections import deque
import pygame
+from pygame.locals import *
-from .constants import *
+from .constants import ICON, SIZE, INIT_FPS, MAX_FPS
from .maze import Maze
@@ -38,25 +39,27 @@ def main():
if event.type == QUIT:
going = False
elif event.type == KEYDOWN:
- if event.key in (K_UP, K_w):
- maze.move(0, 1)
+ if 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(1, 0)
+ maze.move(left=-1)
elif event.key in (K_DOWN, K_s):
- maze.move(0, -1)
+ maze.move(down=-1)
elif event.key in (K_RIGHT, K_d):
- maze.move(-1, 0)
+ maze.move(right=-1)
elif event.key == K_RETURN:
maze.hero.slashing = True
elif event.type == KEYUP:
if event.key in (K_UP, K_w):
- maze.move(0, -1)
+ maze.move(up=1)
elif event.key in (K_LEFT, K_a):
- maze.move(-1, 0)
+ maze.move(left=1)
elif event.key in (K_DOWN, K_s):
- maze.move(0, 1)
+ maze.move(down=1)
elif event.key in (K_RIGHT, K_d):
- maze.move(1, 0)
+ maze.move(right=1)
elif event.key == K_RETURN:
maze.hero.slashing = False
elif event.type == MOUSEBUTTONDOWN:
@@ -73,8 +76,11 @@ def main():
maze.resize(event.w, event.h)
if len(flash_time) > 5:
new_fps = 5000.0 / (flash_time[-1] - flash_time[0])
- fps += -1 if new_fps < fps else 5
flash_time.popleft()
+ if new_fps < fps:
+ fps -= 1
+ elif fps < MAX_FPS and not maze.paused:
+ fps += 5
maze.update(fps)
flash_time.append(pygame.time.get_ticks())
clock.tick(fps)
diff --git a/brutalmaze/maze.py b/brutalmaze/maze.py
index f9877a4..2e1e44c 100644
--- a/brutalmaze/maze.py
+++ b/brutalmaze/maze.py
@@ -22,6 +22,7 @@ from math import pi, atan, atan2, log
from random import choice, getrandbits, uniform
import pygame
+from pygame import RESIZABLE
from .characters import Hero, Enemy
from .constants import *
@@ -64,11 +65,12 @@ class Maze:
self.rangex = range(MIDDLE - w, MIDDLE + w + 1)
self.rangey = range(MIDDLE - h, MIDDLE + h + 1)
self.offsetx = self.offsety = 0.0
- self.score = INIT_SCORE
+ self.paused, self.score = False, INIT_SCORE
self.map = deque()
for _ in range(MAZE_SIZE): self.map.extend(new_column())
- self.right = self.down = self.rotatex = self.rotatey = 0
+ self.up = self.left = self.down = self.right = 0
+ self.rotatex = self.rotatey = 0
self.bullets, self.enemies = [], []
self.add_enemy()
self.hero = Hero(self.surface, fps)
@@ -85,8 +87,7 @@ class Maze:
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.surface, self.fps, self.map, choice(ENEMIES), x, y))
+ self.enemies.append(Enemy(self.surface, self.fps, self.map, x, y))
walls.remove((x, y))
def draw(self):
@@ -135,6 +136,7 @@ class Maze:
for i, enemy in enumerate(self.enemies):
enemy.place(x, y)
if enemy.x not in self.rangex or enemy.y not in self.rangey:
+ self.score += enemy.wound
enemy.die()
killist.append(i)
for i in reversed(killist): self.enemies.pop(i)
@@ -175,7 +177,7 @@ class Maze:
d = length(x, y, self.x, self.y)
if d <= self.slashd:
enemy.wound += (self.slashd-d) / unit
- if enemy.wound >= len(enemy.color):
+ if enemy.wound >= ENEMY_HP:
self.score += enemy.wound
enemy.die()
killist.append(i)
@@ -186,11 +188,11 @@ class Maze:
"""Handle the bullets."""
fallen, time = [], pygame.time.get_ticks()
for enemy in self.enemies:
+ # Chance that an enemy fire increase from 25% to 50%
if uniform(-2, 2) > (INIT_SCORE/self.score)**2 and enemy.firable():
x, y = enemy.pos(self.distance, self.middlex, self.middley)
- self.bullets.append(
- Bullet(self.surface, x, y, atan2(self.y - y, self.x - x),
- enemy.color[0]))
+ angle, color = atan2(self.y - y, self.x - x), enemy.color[0]
+ self.bullets.append(Bullet(self.surface, x, y, angle, color))
if (self.hero.firing and not self.hero.slashing
and time >= self.hero.next_strike):
self.hero.next_strike = time + ATTACK_SPEED
@@ -211,7 +213,7 @@ class Maze:
x, y = enemy.pos(self.distance, self.middlex, self.middley)
if length(bullet.x, bullet.y, x, y) < self.distance:
enemy.wound += wound
- if enemy.wound >= len(enemy.color):
+ if enemy.wound >= ENEMY_HP:
self.score += enemy.wound
enemy.die()
self.enemies.pop(j)
@@ -224,32 +226,29 @@ class Maze:
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 / MOVE_SPEED
self.step = self.distance / self.speed
- dx, dy, d = 0, 0, self.distance*1.5 - self.hero.R
- if self.right:
- self.offsetx += self.right
- s = sign(self.offsetx) * 2
- if ((self.map[MIDDLE - s][MIDDLE - 1]
- or self.map[MIDDLE - s][MIDDLE]
- or self.map[MIDDLE - s][MIDDLE + 1])
- and abs(self.offsetx*self.step) > d):
- self.offsetx -= self.right
- else:
- dx = self.right
- if self.down:
- self.offsety += self.down
- s = sign(self.offsety) * 2
- if ((self.map[MIDDLE - 1][MIDDLE - s]
- or self.map[MIDDLE][MIDDLE - s]
- or self.map[MIDDLE + 1][MIDDLE - s])
- and abs(self.offsety*self.step) > d):
- self.offsety -= self.down
- else:
- dy = self.down
+ d = self.distance*1.5 - self.hero.R
+ dx, dy = sign(self.right) - sign(self.left), sign(self.down) - sign(self.up)
+ self.offsetx += dx
+ 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
if dx or dy:
self.map[MIDDLE][MIDDLE] = EMPTY
@@ -263,15 +262,15 @@ class Maze:
for bullet in self.bullets: bullet.place(dx, dy, self.step)
self.draw()
+ self.slash()
+ self.track_bullets()
for enemy in self.enemies:
enemy.update(fps, self.distance, self.middlex, self.middley)
self.hero.update(fps)
- self.slash()
- self.track_bullets()
pygame.display.flip()
pygame.display.set_caption('Brutal Maze - Score: {}'.format(
int(self.score - INIT_SCORE)))
- if self.hero.wound + 1 > len(self.hero.color): self.lose()
+ if self.hero.wound + 1 > HERO_HP: self.lose()
def resize(self, w, h):
"""Resize the maze."""
@@ -289,14 +288,14 @@ class Maze:
self.rangey = range(MIDDLE - h, MIDDLE + h + 1)
self.slashd = self.hero.R + self.distance/SQRT2
- def move(self, x, y):
- """Command the maze to move x step/frame faster to the left and
- y step/frame faster upward so the hero will move in the reverse
- direction.
+ 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.right += x
- self.down += y
- self.right, self.down = sign(self.right), sign(self.down)
+ self.up += up
+ self.left += left
+ self.down += down
+ self.right += right
def lose(self):
"""Handle loses."""
diff --git a/brutalmaze/utils.py b/brutalmaze/utils.py
index f972fc8..ac47d2d 100644
--- a/brutalmaze/utils.py
+++ b/brutalmaze/utils.py
@@ -17,14 +17,14 @@
#
# Copyright (C) 2017 Nguyễn Gia Phong
-__doc__ = 'brutalmaze module for hero and enemy classes'
-
from math import cos, sin, pi
import pygame
from pygame.gfxdraw import filled_polygon, aapolygon
-from .constants import *
+from .constants import MIDDLE
+
+__doc__ = 'brutalmaze module for hero and enemy classes'
def round2(number):
diff --git a/brutalmaze/weapons.py b/brutalmaze/weapons.py
index 0d2fb8c..110fc10 100644
--- a/brutalmaze/weapons.py
+++ b/brutalmaze/weapons.py
@@ -17,14 +17,14 @@
#
# Copyright (C) 2017 Nguyễn Gia Phong
-__doc__ = 'brutalmaze module for weapon classes'
-
-from math import pi, cos, sin
+from math import cos, sin
from pygame.time import get_ticks
-from .constants import *
-from .utils import randsign, regpoly, fill_aapolygon, pos, sign
+from .constants import BULLET_LIFETIME, BULLET_SPEED
+from .utils import regpoly, fill_aapolygon
+
+__doc__ = 'brutalmaze module for weapon classes'
class Bullet:
diff --git a/screenshot.png b/screenshot.png
new file mode 100644
index 0000000..9c2a893
Binary files /dev/null and b/screenshot.png differ
diff --git a/setup.py b/setup.py
index d295fe9..083b11b 100755
--- a/setup.py
+++ b/setup.py
@@ -24,9 +24,9 @@ setup(
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python',
- 'Topic :: Games/Entertainment :: Role-Playing'],
+ 'Topic :: Games/Entertainment :: Arcade'],
keywords='',
packages=['brutalmaze'],
install_requires=['pygame>=1.9'],
- package_data={'brutalmaze': ['*.xm']},
+ package_data={'brutalmaze': ['icon.png']},
entry_points={'gui_scripts': ['brutalmaze = brutalmaze:main']})