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']})