From aafa943a5313721cb2dee408a0b60c8b11ff5ad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Gia=20Phong?= Date: Sun, 22 Oct 2017 17:07:05 +0700 Subject: [PATCH] Enable pausing, fix sticky move bug and clean up --- README.rst | 6 +-- brutalmaze/characters.py | 20 +++++----- brutalmaze/constants.py | 6 ++- brutalmaze/main.py | 28 ++++++++------ brutalmaze/maze.py | 79 +++++++++++++++++++-------------------- brutalmaze/utils.py | 6 +-- brutalmaze/weapons.py | 10 ++--- screenshot.png | Bin 0 -> 10078 bytes setup.py | 4 +- 9 files changed, 83 insertions(+), 76 deletions(-) create mode 100644 screenshot.png 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 0000000000000000000000000000000000000000..9c2a89331840f2e66075563d602da9687a585eca GIT binary patch literal 10078 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.9'], - package_data={'brutalmaze': ['*.xm']}, + package_data={'brutalmaze': ['icon.png']}, entry_points={'gui_scripts': ['brutalmaze = brutalmaze:main']})