Enable pausing, fix sticky move bug and clean up

This commit is contained in:
Nguyễn Gia Phong 2017-10-22 17:07:05 +07:00
parent c5a67b0e59
commit aafa943a53
9 changed files with 83 additions and 76 deletions

View File

@ -4,7 +4,7 @@ Brutal Maze
Brutal Maze is a research hash and slash game with fast-paced action and a Brutal Maze is a research hash and slash game with fast-paced action and a
minimalist art style. 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 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 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. 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:
* Make sure you have Python and `pip <https://pip.pypa.io/en/latest/>`_ * Install Python and `pip <https://pip.pypa.io/en/latest/>`_. Make sure the
installed on your system. directory for Python executables is your ``PATH``.
* Clone the Github repository or download the Zip achieve and unpack. * Clone the Github repository or download the Zip achieve and unpack.
* Open Terminal in the directory containing the repo's folder and run * Open Terminal in the directory containing the repo's folder and run
``pip install --user brutalmaze``. ``pip install --user brutalmaze``.

View File

@ -17,17 +17,17 @@
# #
# Copyright (C) 2017 Nguyễn Gia Phong # Copyright (C) 2017 Nguyễn Gia Phong
__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 atan2, sin, pi
from random import shuffle from random import choice, shuffle
import pygame import pygame
from .constants import * from .constants import *
from .utils import randsign, regpoly, fill_aapolygon, pos, sign from .utils import randsign, regpoly, fill_aapolygon, pos, sign
__doc__ = 'brutalmaze module for hero and enemy classes'
class Hero: class Hero:
"""Object representing the hero.""" """Object representing the hero."""
@ -40,15 +40,15 @@ class Hero:
self.next_strike = 0 self.next_strike = 0
self.slashing = self.firing = False 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 self.spin_queue = self.wound = 0.0
def update(self, fps): def update(self, fps):
"""Update the hero.""" """Update the hero."""
old_speed, time = self.spin_speed, pygame.time.get_ticks() 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.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.wound < 0: self.wound = 0.0
if self.slashing and time >= self.next_strike: if self.slashing and time >= self.next_strike:
@ -74,9 +74,9 @@ class Hero:
class Enemy: class Enemy:
"""Object representing an 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.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.x, self.y = x, y
self.maze[x][y] = ENEMY self.maze[x][y] = ENEMY
@ -84,7 +84,7 @@ class Enemy:
self.next_move = 0 self.next_move = 0
self.move_speed = fps / MOVE_SPEED self.move_speed = fps / MOVE_SPEED
self.offsetx = self.offsety = 0 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 self.spin_queue = self.wound = 0.0
def firable(self): def firable(self):
@ -137,7 +137,7 @@ class Enemy:
def update(self, fps, distance, middlex, middley): def update(self, fps, distance, middlex, middley):
"""Update the enemy.""" """Update the enemy."""
if self.awake: 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 self.spin_queue *= self.spin_speed / old_speed
if not self.spin_queue and not self.move(fps): if not self.spin_queue and not self.move(fps):
self.spin_queue = randsign() * self.spin_speed self.spin_queue = randsign() * self.spin_speed

View File

@ -18,7 +18,6 @@
# Copyright (C) 2017 Nguyễn Gia Phong # Copyright (C) 2017 Nguyễn Gia Phong
from pygame import image from pygame import image
from pygame.locals import *
from pkg_resources import resource_filename from pkg_resources import resource_filename
__doc__ = 'brutalmaze module for shared constants' __doc__ = 'brutalmaze module for shared constants'
@ -29,6 +28,7 @@ SQRT2 = 2 ** 0.5
GOLDEN_MEAN = 5**0.5/2 + 0.5 GOLDEN_MEAN = 5**0.5/2 + 0.5
INIT_FPS = 30.0 INIT_FPS = 30.0
MAX_FPS = 144.0
SIZE = 640, 480 SIZE = 640, 480
MAZE_SIZE = 10 MAZE_SIZE = 10
ROAD_WIDTH = 5 # grids ROAD_WIDTH = 5 # grids
@ -38,7 +38,7 @@ LAST_ROW = (MAZE_SIZE-1) * ROAD_WIDTH * 2
INIT_SCORE = 208.2016 INIT_SCORE = 208.2016
MOVE_SPEED = 5 # grid/s MOVE_SPEED = 5 # grid/s
BULLET_SPEED = 10 # grid/s BULLET_SPEED = 10 # grid/s
HEAL_SPEED = 1.0 # HP/s HEAL_SPEED = 1 # HP/s
ATTACK_SPEED = 333 # ms/strike ATTACK_SPEED = 333 # ms/strike
BULLET_LIFETIME = 1000 # ms 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))} (136, 138, 133), (85, 87, 83), (46, 52, 54))}
ENEMIES = ('Butter', 'Orange', 'Chocolate', 'Chameleon', ENEMIES = ('Butter', 'Orange', 'Chocolate', 'Chameleon',
'Sky Blue', 'Plum', 'Scarlet Red') 'Sky Blue', 'Plum', 'Scarlet Red')
ENEMY_HP = 3
HERO_HP = 6
BG_COLOR = TANGO['Aluminium'][-1] BG_COLOR = TANGO['Aluminium'][-1]
FG_COLOR = TANGO['Aluminium'][0] FG_COLOR = TANGO['Aluminium'][0]

View File

@ -20,8 +20,9 @@
from collections import deque from collections import deque
import pygame import pygame
from pygame.locals import *
from .constants import * from .constants import ICON, SIZE, INIT_FPS, MAX_FPS
from .maze import Maze from .maze import Maze
@ -38,25 +39,27 @@ def main():
if event.type == QUIT: if event.type == QUIT:
going = False going = False
elif event.type == KEYDOWN: elif event.type == KEYDOWN:
if event.key in (K_UP, K_w): if event.key in (K_ESCAPE, K_p):
maze.move(0, 1) maze.paused ^= True
elif event.key in (K_UP, K_w):
maze.move(up=-1)
elif event.key in (K_LEFT, K_a): elif event.key in (K_LEFT, K_a):
maze.move(1, 0) maze.move(left=-1)
elif event.key in (K_DOWN, K_s): elif event.key in (K_DOWN, K_s):
maze.move(0, -1) maze.move(down=-1)
elif event.key in (K_RIGHT, K_d): elif event.key in (K_RIGHT, K_d):
maze.move(-1, 0) maze.move(right=-1)
elif event.key == K_RETURN: elif event.key == K_RETURN:
maze.hero.slashing = True maze.hero.slashing = True
elif event.type == KEYUP: elif event.type == KEYUP:
if event.key in (K_UP, K_w): if event.key in (K_UP, K_w):
maze.move(0, -1) maze.move(up=1)
elif event.key in (K_LEFT, K_a): elif event.key in (K_LEFT, K_a):
maze.move(-1, 0) maze.move(left=1)
elif event.key in (K_DOWN, K_s): elif event.key in (K_DOWN, K_s):
maze.move(0, 1) maze.move(down=1)
elif event.key in (K_RIGHT, K_d): elif event.key in (K_RIGHT, K_d):
maze.move(1, 0) maze.move(right=1)
elif event.key == K_RETURN: elif event.key == K_RETURN:
maze.hero.slashing = False maze.hero.slashing = False
elif event.type == MOUSEBUTTONDOWN: elif event.type == MOUSEBUTTONDOWN:
@ -73,8 +76,11 @@ def main():
maze.resize(event.w, event.h) maze.resize(event.w, event.h)
if len(flash_time) > 5: if len(flash_time) > 5:
new_fps = 5000.0 / (flash_time[-1] - flash_time[0]) new_fps = 5000.0 / (flash_time[-1] - flash_time[0])
fps += -1 if new_fps < fps else 5
flash_time.popleft() flash_time.popleft()
if new_fps < fps:
fps -= 1
elif fps < MAX_FPS and not maze.paused:
fps += 5
maze.update(fps) maze.update(fps)
flash_time.append(pygame.time.get_ticks()) flash_time.append(pygame.time.get_ticks())
clock.tick(fps) clock.tick(fps)

View File

@ -22,6 +22,7 @@ from math import pi, atan, atan2, log
from random import choice, getrandbits, uniform from random import choice, getrandbits, uniform
import pygame import pygame
from pygame import RESIZABLE
from .characters import Hero, Enemy from .characters import Hero, Enemy
from .constants import * from .constants import *
@ -64,11 +65,12 @@ class Maze:
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)
self.offsetx = self.offsety = 0.0 self.offsetx = self.offsety = 0.0
self.score = INIT_SCORE self.paused, self.score = False, INIT_SCORE
self.map = deque() self.map = deque()
for _ in range(MAZE_SIZE): self.map.extend(new_column()) 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.bullets, self.enemies = [], []
self.add_enemy() self.add_enemy()
self.hero = Hero(self.surface, fps) self.hero = Hero(self.surface, fps)
@ -85,8 +87,7 @@ class Maze:
x, y = choice(walls) x, y = choice(walls)
if all(self.map[x + a][y + b] == WALL for a, b in ADJACENT_GRIDS): if all(self.map[x + a][y + b] == WALL for a, b in ADJACENT_GRIDS):
continue continue
self.enemies.append( self.enemies.append(Enemy(self.surface, self.fps, self.map, x, y))
Enemy(self.surface, self.fps, self.map, choice(ENEMIES), x, y))
walls.remove((x, y)) walls.remove((x, y))
def draw(self): def draw(self):
@ -135,6 +136,7 @@ class Maze:
for i, enemy in enumerate(self.enemies): for i, enemy in enumerate(self.enemies):
enemy.place(x, y) enemy.place(x, y)
if enemy.x not in self.rangex or enemy.y not in self.rangey: if enemy.x not in self.rangex or enemy.y not in self.rangey:
self.score += enemy.wound
enemy.die() enemy.die()
killist.append(i) killist.append(i)
for i in reversed(killist): self.enemies.pop(i) for i in reversed(killist): self.enemies.pop(i)
@ -175,7 +177,7 @@ class Maze:
d = length(x, y, self.x, self.y) d = length(x, y, self.x, self.y)
if d <= self.slashd: if d <= self.slashd:
enemy.wound += (self.slashd-d) / unit enemy.wound += (self.slashd-d) / unit
if enemy.wound >= len(enemy.color): if enemy.wound >= ENEMY_HP:
self.score += enemy.wound self.score += enemy.wound
enemy.die() enemy.die()
killist.append(i) killist.append(i)
@ -186,11 +188,11 @@ class Maze:
"""Handle the bullets.""" """Handle the bullets."""
fallen, time = [], pygame.time.get_ticks() fallen, time = [], pygame.time.get_ticks()
for enemy in self.enemies: 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(): if uniform(-2, 2) > (INIT_SCORE/self.score)**2 and enemy.firable():
x, y = enemy.pos(self.distance, self.middlex, self.middley) x, y = enemy.pos(self.distance, self.middlex, self.middley)
self.bullets.append( angle, color = atan2(self.y - y, self.x - x), enemy.color[0]
Bullet(self.surface, x, y, atan2(self.y - y, self.x - x), self.bullets.append(Bullet(self.surface, x, y, angle, color))
enemy.color[0]))
if (self.hero.firing and not self.hero.slashing if (self.hero.firing and not self.hero.slashing
and time >= self.hero.next_strike): and time >= self.hero.next_strike):
self.hero.next_strike = time + ATTACK_SPEED self.hero.next_strike = time + ATTACK_SPEED
@ -211,7 +213,7 @@ class Maze:
x, y = enemy.pos(self.distance, self.middlex, self.middley) x, y = enemy.pos(self.distance, self.middlex, self.middley)
if length(bullet.x, bullet.y, x, y) < self.distance: if length(bullet.x, bullet.y, x, y) < self.distance:
enemy.wound += wound enemy.wound += wound
if enemy.wound >= len(enemy.color): if enemy.wound >= ENEMY_HP:
self.score += enemy.wound self.score += enemy.wound
enemy.die() enemy.die()
self.enemies.pop(j) self.enemies.pop(j)
@ -224,32 +226,29 @@ class Maze:
def update(self, fps): def update(self, fps):
"""Update the maze.""" """Update the maze."""
if self.paused: return
self.offsetx *= fps / self.fps self.offsetx *= fps / self.fps
self.offsety *= fps / self.fps self.offsety *= fps / self.fps
self.fps, self.speed = fps, fps / MOVE_SPEED self.fps, self.speed = fps, fps / MOVE_SPEED
self.step = self.distance / self.speed self.step = self.distance / self.speed
dx, dy, d = 0, 0, self.distance*1.5 - self.hero.R d = self.distance*1.5 - self.hero.R
if self.right: dx, dy = sign(self.right) - sign(self.left), sign(self.down) - sign(self.up)
self.offsetx += self.right self.offsetx += dx
s = sign(self.offsetx) * 2 self.offsety += dy
if ((self.map[MIDDLE - s][MIDDLE - 1] x, y = MIDDLE - sign(self.offsetx)*2, MIDDLE - sign(self.offsety)*2
or self.map[MIDDLE - s][MIDDLE] if ((self.map[x][MIDDLE - 1] != EMPTY
or self.map[MIDDLE - s][MIDDLE + 1]) or self.map[x][MIDDLE] != EMPTY
and abs(self.offsetx*self.step) > d): or self.map[x][MIDDLE + 1] != EMPTY)
self.offsetx -= self.right and abs(self.offsetx*self.step) > d):
else: self.offsetx -= dx
dx = self.right dx = 0
if self.down: if ((self.map[MIDDLE - 1][y] != EMPTY
self.offsety += self.down or self.map[MIDDLE][y] != EMPTY
s = sign(self.offsety) * 2 or self.map[MIDDLE + 1][y] != EMPTY)
if ((self.map[MIDDLE - 1][MIDDLE - s] and abs(self.offsety*self.step) > d):
or self.map[MIDDLE][MIDDLE - s] self.offsety -= dy
or self.map[MIDDLE + 1][MIDDLE - s]) dy = 0
and abs(self.offsety*self.step) > d):
self.offsety -= self.down
else:
dy = self.down
if dx or dy: if dx or dy:
self.map[MIDDLE][MIDDLE] = EMPTY self.map[MIDDLE][MIDDLE] = EMPTY
@ -263,15 +262,15 @@ class Maze:
for bullet in self.bullets: bullet.place(dx, dy, self.step) for bullet in self.bullets: bullet.place(dx, dy, self.step)
self.draw() self.draw()
self.slash()
self.track_bullets()
for enemy in self.enemies: for enemy in self.enemies:
enemy.update(fps, self.distance, self.middlex, self.middley) enemy.update(fps, self.distance, self.middlex, self.middley)
self.hero.update(fps) self.hero.update(fps)
self.slash()
self.track_bullets()
pygame.display.flip() pygame.display.flip()
pygame.display.set_caption('Brutal Maze - Score: {}'.format( pygame.display.set_caption('Brutal Maze - Score: {}'.format(
int(self.score - INIT_SCORE))) 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): def resize(self, w, h):
"""Resize the maze.""" """Resize the maze."""
@ -289,14 +288,14 @@ class Maze:
self.rangey = range(MIDDLE - h, MIDDLE + h + 1) self.rangey = range(MIDDLE - h, MIDDLE + h + 1)
self.slashd = self.hero.R + self.distance/SQRT2 self.slashd = self.hero.R + self.distance/SQRT2
def move(self, x, y): def move(self, up=0, left=0, down=0, right=0):
"""Command the maze to move x step/frame faster to the left and """Make the maze to move in the given directions by moving the
y step/frame faster upward so the hero will move in the reverse maze in the reverse way.
direction.
""" """
self.right += x self.up += up
self.down += y self.left += left
self.right, self.down = sign(self.right), sign(self.down) self.down += down
self.right += right
def lose(self): def lose(self):
"""Handle loses.""" """Handle loses."""

View File

@ -17,14 +17,14 @@
# #
# Copyright (C) 2017 Nguyễn Gia Phong # Copyright (C) 2017 Nguyễn Gia Phong
__doc__ = 'brutalmaze module for hero and enemy classes'
from math import cos, sin, pi from math import cos, sin, pi
import pygame import pygame
from pygame.gfxdraw import filled_polygon, aapolygon 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): def round2(number):

View File

@ -17,14 +17,14 @@
# #
# Copyright (C) 2017 Nguyễn Gia Phong # Copyright (C) 2017 Nguyễn Gia Phong
__doc__ = 'brutalmaze module for weapon classes' from math import cos, sin
from math import pi, cos, sin
from pygame.time import get_ticks from pygame.time import get_ticks
from .constants import * from .constants import BULLET_LIFETIME, BULLET_SPEED
from .utils import randsign, regpoly, fill_aapolygon, pos, sign from .utils import regpoly, fill_aapolygon
__doc__ = 'brutalmaze module for weapon classes'
class Bullet: class Bullet:

BIN
screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@ -24,9 +24,9 @@ setup(
'Natural Language :: English', 'Natural Language :: English',
'Operating System :: OS Independent', 'Operating System :: OS Independent',
'Programming Language :: Python', 'Programming Language :: Python',
'Topic :: Games/Entertainment :: Role-Playing'], 'Topic :: Games/Entertainment :: Arcade'],
keywords='', keywords='',
packages=['brutalmaze'], packages=['brutalmaze'],
install_requires=['pygame>=1.9'], install_requires=['pygame>=1.9'],
package_data={'brutalmaze': ['*.xm']}, package_data={'brutalmaze': ['icon.png']},
entry_points={'gui_scripts': ['brutalmaze = brutalmaze:main']}) entry_points={'gui_scripts': ['brutalmaze = brutalmaze:main']})