Allow moving hero using mouse
This commit is contained in:
parent
92a41b3cff
commit
9dff378b57
11
README.rst
11
README.rst
|
@ -1,8 +1,8 @@
|
||||||
Brutal Maze
|
Brutal Maze
|
||||||
===========
|
===========
|
||||||
|
|
||||||
Brutal Maze is a hack and slash game with fast-paced action and a minimalist
|
Brutal Maze is a third-person shooter game with fast-paced action and a
|
||||||
art style.
|
minimalist art style.
|
||||||
|
|
||||||
.. image:: https://raw.githubusercontent.com/McSinyx/brutalmaze/master/screenshot.png
|
.. image:: https://raw.githubusercontent.com/McSinyx/brutalmaze/master/screenshot.png
|
||||||
|
|
||||||
|
@ -39,7 +39,8 @@ For more information, see
|
||||||
page from Brutal Maze wiki.
|
page from Brutal Maze wiki.
|
||||||
|
|
||||||
After installation, you can launch the game by running the command
|
After installation, you can launch the game by running the command
|
||||||
``brutalmaze``. Below are default bindings:
|
``brutalmaze``. Below are the default bindings, which can be configured as
|
||||||
|
shown in the next section:
|
||||||
|
|
||||||
F2
|
F2
|
||||||
New game.
|
New game.
|
||||||
|
@ -55,9 +56,11 @@ Up
|
||||||
Move up.
|
Move up.
|
||||||
Down
|
Down
|
||||||
Move down.
|
Move down.
|
||||||
|
Right Mouse
|
||||||
|
Move the hero using mouse
|
||||||
Left Mouse
|
Left Mouse
|
||||||
Long-range attack.
|
Long-range attack.
|
||||||
Right Mouse
|
Space
|
||||||
Close-range attack, also dodge from bullets.
|
Close-range attack, also dodge from bullets.
|
||||||
|
|
||||||
Configuration
|
Configuration
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
"""Brutal Maze is a minimalist hack and slash game with fast-paced action"""
|
"""Brutal Maze is a minimalist third-person shooter with fast-paced action"""
|
||||||
|
|
|
@ -26,7 +26,7 @@ from sys import modules
|
||||||
from .constants import (
|
from .constants import (
|
||||||
TANGO, HERO_HP, SFX_HEART, HEAL_SPEED, MIN_BEAT, ATTACK_SPEED, ENEMY,
|
TANGO, HERO_HP, SFX_HEART, HEAL_SPEED, MIN_BEAT, ATTACK_SPEED, ENEMY,
|
||||||
ENEMY_SPEED, ENEMY_HP, SFX_SLASH_HERO, MIDDLE, WALL, FIRANGE, AROUND_HERO,
|
ENEMY_SPEED, ENEMY_HP, SFX_SLASH_HERO, MIDDLE, WALL, FIRANGE, AROUND_HERO,
|
||||||
ADJACENT_GRIDS, EMPTY, FG_COLOR, SQRT2, MINW)
|
ADJACENTS, EMPTY, FG_COLOR, SQRT2, MINW)
|
||||||
from .misc import sign, cosin, randsign, regpoly, fill_aapolygon, choices, play
|
from .misc import sign, cosin, randsign, regpoly, fill_aapolygon, choices, play
|
||||||
from .weapons import Bullet
|
from .weapons import Bullet
|
||||||
|
|
||||||
|
@ -232,8 +232,8 @@ class Enemy:
|
||||||
self.move_speed = self.maze.fps / speed
|
self.move_speed = self.maze.fps / speed
|
||||||
directions = [(sign(MIDDLE - self.x), 0), (0, sign(MIDDLE - self.y))]
|
directions = [(sign(MIDDLE - self.x), 0), (0, sign(MIDDLE - self.y))]
|
||||||
shuffle(directions)
|
shuffle(directions)
|
||||||
directions.append(choice(ADJACENT_GRIDS))
|
directions.append(choice(ADJACENTS))
|
||||||
if self.maze.hero.dead: directions = choice(ADJACENT_GRIDS),
|
if self.maze.hero.dead: directions = choice(ADJACENTS),
|
||||||
for x, y in directions:
|
for x, y in directions:
|
||||||
if (x or y) and self.maze.map[self.x + x][self.y + y] == EMPTY:
|
if (x or y) and self.maze.map[self.x + x][self.y + y] == EMPTY:
|
||||||
self.offsetx = round(x * (1 - self.move_speed))
|
self.offsetx = round(x * (1 - self.move_speed))
|
||||||
|
|
|
@ -56,9 +56,9 @@ ATTACK_SPEED = 333.333 # ms/strike
|
||||||
FIRANGE = 6 # grids
|
FIRANGE = 6 # grids
|
||||||
BULLET_LIFETIME = 1000.0 * FIRANGE / (BULLET_SPEED-HERO_SPEED) # ms
|
BULLET_LIFETIME = 1000.0 * FIRANGE / (BULLET_SPEED-HERO_SPEED) # ms
|
||||||
EMPTY, WALL, HERO, ENEMY = range(4)
|
EMPTY, WALL, HERO, ENEMY = range(4)
|
||||||
ADJACENT_GRIDS = (1, 0), (0, 1), (-1, 0), (0, -1)
|
ADJACENTS = (1, 0), (0, 1), (-1, 0), (0, -1)
|
||||||
AROUND_HERO = set((MIDDLE + x, MIDDLE + y) for x, y in
|
CORNERS = (1, 1), (-1, 1), (-1, -1), (1, -1)
|
||||||
ADJACENT_GRIDS + ((1, 1), (-1, 1), (-1, -1), (1, -1)))
|
AROUND_HERO = set((MIDDLE + x, MIDDLE + y) for x, y in ADJACENTS + CORNERS)
|
||||||
|
|
||||||
TANGO = {'Butter': ((252, 233, 79), (237, 212, 0), (196, 160, 0)),
|
TANGO = {'Butter': ((252, 233, 79), (237, 212, 0), (196, 160, 0)),
|
||||||
'Orange': ((252, 175, 62), (245, 121, 0), (206, 92, 0)),
|
'Orange': ((252, 175, 62), (245, 121, 0), (206, 92, 0)),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# main.py - main module, starts game and main loop
|
# game.py - main module, starts game and main loop
|
||||||
# Copyright (C) 2017, 2018 Nguyễn Gia Phong
|
# Copyright (C) 2017, 2018 Nguyễn Gia Phong
|
||||||
#
|
#
|
||||||
# This file is part of Brutal Maze.
|
# This file is part of Brutal Maze.
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with Brutal Maze. If not, see <https://www.gnu.org/licenses/>.
|
# along with Brutal Maze. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
__version__ = '0.6.5'
|
__version__ = '0.7.0'
|
||||||
|
|
||||||
import re
|
import re
|
||||||
from argparse import ArgumentParser, FileType, RawTextHelpFormatter
|
from argparse import ArgumentParser, FileType, RawTextHelpFormatter
|
||||||
|
@ -37,7 +37,7 @@ from pygame import KEYDOWN, QUIT, VIDEORESIZE
|
||||||
from pygame.time import Clock, get_ticks
|
from pygame.time import Clock, get_ticks
|
||||||
from appdirs import AppDirs
|
from appdirs import AppDirs
|
||||||
|
|
||||||
from .constants import SETTINGS, ICON, MUSIC, HERO_SPEED, COLORS, WALL
|
from .constants import SETTINGS, ICON, MUSIC, HERO_SPEED, COLORS, MIDDLE, WALL
|
||||||
from .maze import Maze
|
from .maze import Maze
|
||||||
from .misc import deg, round2, sign
|
from .misc import deg, round2, sign
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ class ConfigReader:
|
||||||
('Toggle mute', 'mute'),
|
('Toggle mute', 'mute'),
|
||||||
('Move left', 'left'), ('Move right', 'right'),
|
('Move left', 'left'), ('Move right', 'right'),
|
||||||
('Move up', 'up'), ('Move down', 'down'),
|
('Move up', 'up'), ('Move down', 'down'),
|
||||||
|
('Auto move', 'autove'),
|
||||||
('Long-range attack', 'shot'),
|
('Long-range attack', 'shot'),
|
||||||
('Close-range attack', 'slash'))
|
('Close-range attack', 'slash'))
|
||||||
WEIRD_MOUSE_ERR = '{}: Mouse is not a suitable control'
|
WEIRD_MOUSE_ERR = '{}: Mouse is not a suitable control'
|
||||||
|
@ -81,7 +82,7 @@ class ConfigReader:
|
||||||
for cmd, alias in self.CONTROL_ALIASES:
|
for cmd, alias in self.CONTROL_ALIASES:
|
||||||
i = self.config.get('Control', cmd)
|
i = self.config.get('Control', cmd)
|
||||||
if re.match('mouse[1-3]$', i.lower()):
|
if re.match('mouse[1-3]$', i.lower()):
|
||||||
if alias not in ('shot', 'slash'):
|
if alias not in ('autove', 'shot', 'slash'):
|
||||||
raise ValueError(self.WEIRD_MOUSE_ERR.format(cmd))
|
raise ValueError(self.WEIRD_MOUSE_ERR.format(cmd))
|
||||||
self.mouse[alias] = int(i[-1]) - 1
|
self.mouse[alias] = int(i[-1]) - 1
|
||||||
continue
|
continue
|
||||||
|
@ -219,27 +220,33 @@ class Game:
|
||||||
|
|
||||||
def move(self, x, y):
|
def move(self, x, y):
|
||||||
"""Command the hero to move faster in the given direction."""
|
"""Command the hero to move faster in the given direction."""
|
||||||
x, y = -x, -y # or move the maze in the reverse direction
|
maze = self.maze
|
||||||
velocity = self.maze.distance * HERO_SPEED / self.fps
|
velocity = maze.distance * HERO_SPEED / self.fps
|
||||||
accel = velocity * HERO_SPEED / self.fps
|
accel = velocity * HERO_SPEED / self.fps
|
||||||
|
|
||||||
if self.maze.next_move > 0 or not x:
|
if x == y == 0:
|
||||||
self.maze.vx -= sign(self.maze.vx) * accel
|
maze.set_step()
|
||||||
if abs(self.maze.vx) < accel * 2: self.maze.vx = 0.0
|
x, y = maze.stepx, maze.stepy
|
||||||
elif x * self.maze.vx < 0:
|
|
||||||
self.maze.vx += x * 2 * accel
|
|
||||||
else:
|
else:
|
||||||
self.maze.vx += x * accel
|
x, y = -x, -y # or move the maze in the reverse direction
|
||||||
if abs(self.maze.vx) > velocity: self.maze.vx = x * velocity
|
|
||||||
|
|
||||||
if self.maze.next_move > 0 or not y:
|
if maze.next_move > 0 or not x:
|
||||||
self.maze.vy -= sign(self.maze.vy) * accel
|
maze.vx -= sign(maze.vx) * accel
|
||||||
if abs(self.maze.vy) < accel * 2: self.maze.vy = 0.0
|
if abs(maze.vx) < accel * 2: maze.vx = 0.0
|
||||||
elif y * self.maze.vy < 0:
|
elif x * maze.vx < 0:
|
||||||
self.maze.vy += y * 2 * accel
|
maze.vx += x * 2 * accel
|
||||||
else:
|
else:
|
||||||
self.maze.vy += y * accel
|
maze.vx += x * accel
|
||||||
if abs(self.maze.vy) > velocity: self.maze.vy = y * velocity
|
if abs(maze.vx) > velocity: maze.vx = x * velocity
|
||||||
|
|
||||||
|
if maze.next_move > 0 or not y:
|
||||||
|
maze.vy -= sign(maze.vy) * accel
|
||||||
|
if abs(maze.vy) < accel * 2: maze.vy = 0.0
|
||||||
|
elif y * maze.vy < 0:
|
||||||
|
maze.vy += y * 2 * accel
|
||||||
|
else:
|
||||||
|
maze.vy += y * accel
|
||||||
|
if abs(maze.vy) > velocity: maze.vy = y * velocity
|
||||||
|
|
||||||
def control(self, x, y, angle, firing, slashing):
|
def control(self, x, y, angle, firing, slashing):
|
||||||
"""Control how the hero move and attack."""
|
"""Control how the hero move and attack."""
|
||||||
|
@ -293,11 +300,11 @@ class Game:
|
||||||
right = keys[self.key['right']] - keys[self.key['left']]
|
right = keys[self.key['right']] - keys[self.key['left']]
|
||||||
down = keys[self.key['down']] - keys[self.key['up']]
|
down = keys[self.key['down']] - keys[self.key['up']]
|
||||||
|
|
||||||
# Follow the mouse cursor
|
|
||||||
x, y = pygame.mouse.get_pos()
|
|
||||||
angle = atan2(y - self.hero.y, x - self.hero.x)
|
|
||||||
|
|
||||||
buttons = pygame.mouse.get_pressed()
|
buttons = pygame.mouse.get_pressed()
|
||||||
|
try:
|
||||||
|
autove = keys[self.key['autove']]
|
||||||
|
except KeyError:
|
||||||
|
autove = buttons[self.mouse['autove']]
|
||||||
try:
|
try:
|
||||||
firing = keys[self.key['shot']]
|
firing = keys[self.key['shot']]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
|
@ -307,6 +314,21 @@ class Game:
|
||||||
except KeyError:
|
except KeyError:
|
||||||
slashing = buttons[self.mouse['slash']]
|
slashing = buttons[self.mouse['slash']]
|
||||||
|
|
||||||
|
# Follow the mouse cursor
|
||||||
|
x, y = pygame.mouse.get_pos()
|
||||||
|
maze = self.maze
|
||||||
|
if right or down:
|
||||||
|
maze.destx = maze.desty = MIDDLE
|
||||||
|
maze.stepx = maze.stepy = 0
|
||||||
|
elif autove:
|
||||||
|
maze.destx = MIDDLE + round2((x-maze.centerx) / maze.distance)
|
||||||
|
maze.desty = MIDDLE + round2((y-maze.centery) / maze.distance)
|
||||||
|
maze.set_step(lambda x: maze.rangex[0] <= x <= maze.rangex[-1],
|
||||||
|
lambda y: maze.rangey[0] <= y <= maze.rangey[-1])
|
||||||
|
if maze.stepx == maze.stepy == 0:
|
||||||
|
maze.destx = maze.desty = MIDDLE
|
||||||
|
|
||||||
|
angle = atan2(y - self.hero.y, x - self.hero.x)
|
||||||
self.control(right, down, angle, firing, slashing)
|
self.control(right, down, angle, firing, slashing)
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, traceback):
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
__doc__ = 'Brutal Maze module for the maze class'
|
__doc__ = 'Brutal Maze module for the maze class'
|
||||||
|
|
||||||
from collections import deque
|
from collections import deque, defaultdict
|
||||||
from math import pi, log
|
from math import pi, log
|
||||||
from random import choice, getrandbits, uniform
|
from random import choice, getrandbits, uniform
|
||||||
|
|
||||||
|
@ -28,10 +28,10 @@ import pygame
|
||||||
from .characters import Hero, new_enemy
|
from .characters import Hero, new_enemy
|
||||||
from .constants import (
|
from .constants import (
|
||||||
EMPTY, WALL, HERO, ROAD_WIDTH, MAZE_SIZE, MIDDLE, INIT_SCORE, ENEMIES,
|
EMPTY, WALL, HERO, ROAD_WIDTH, MAZE_SIZE, MIDDLE, INIT_SCORE, ENEMIES,
|
||||||
MINW, MAXW, SQRT2, SFX_SPAWN, SFX_SLASH_ENEMY, SFX_LOSE, ADJACENT_GRIDS,
|
MINW, MAXW, SQRT2, SFX_SPAWN, SFX_SLASH_ENEMY, SFX_LOSE, ADJACENTS,
|
||||||
BG_COLOR, FG_COLOR, CELL_WIDTH, LAST_ROW, HERO_HP, ENEMY_HP, ATTACK_SPEED,
|
BG_COLOR, FG_COLOR, CELL_WIDTH, LAST_ROW, HERO_HP, ENEMY_HP, ATTACK_SPEED,
|
||||||
HERO_SPEED, BULLET_LIFETIME)
|
HERO_SPEED, BULLET_LIFETIME)
|
||||||
from .misc import round2, sign, regpoly, fill_aapolygon, play
|
from .misc import round2, sign, around, regpoly, fill_aapolygon, play
|
||||||
from .weapons import Bullet
|
from .weapons import Bullet
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,6 +74,8 @@ class Maze:
|
||||||
enemy_weights (dict): probabilities of enemies to be created
|
enemy_weights (dict): probabilities of enemies to be created
|
||||||
enemies (list of Enemy): alive enemies
|
enemies (list of Enemy): alive enemies
|
||||||
hero (Hero): the hero
|
hero (Hero): the hero
|
||||||
|
destx, desty (int): the grid the hero is moving to
|
||||||
|
stepx, stepy (int): direction the maze is moving
|
||||||
next_move (float): time until the hero gets mobilized (in ms)
|
next_move (float): time until the hero gets mobilized (in ms)
|
||||||
next_slashfx (float): time until next slash effect of the hero (in ms)
|
next_slashfx (float): time until next slash effect of the hero (in ms)
|
||||||
slashd (float): minimum distance for slashes to be effective
|
slashd (float): minimum distance for slashes to be effective
|
||||||
|
@ -105,6 +107,8 @@ class Maze:
|
||||||
self.add_enemy()
|
self.add_enemy()
|
||||||
self.hero = Hero(self.surface, fps, size)
|
self.hero = Hero(self.surface, fps, size)
|
||||||
self.map[MIDDLE][MIDDLE] = HERO
|
self.map[MIDDLE][MIDDLE] = HERO
|
||||||
|
self.destx = self.desty = MIDDLE
|
||||||
|
self.stepx = self.stepy = 0
|
||||||
self.next_move = self.next_slashfx = 0.0
|
self.next_move = self.next_slashfx = 0.0
|
||||||
self.slashd = self.hero.R + self.distance/SQRT2
|
self.slashd = self.hero.R + self.distance/SQRT2
|
||||||
|
|
||||||
|
@ -121,7 +125,7 @@ class Maze:
|
||||||
num = log(self.score, INIT_SCORE)
|
num = log(self.score, INIT_SCORE)
|
||||||
while walls and len(self.enemies) < num:
|
while walls and len(self.enemies) < num:
|
||||||
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 ADJACENTS):
|
||||||
continue
|
continue
|
||||||
enemy = new_enemy(self, x, y)
|
enemy = new_enemy(self, x, y)
|
||||||
self.enemies.append(enemy)
|
self.enemies.append(enemy)
|
||||||
|
@ -164,22 +168,26 @@ class Maze:
|
||||||
y = int((self.centery-self.y) * 2 / self.distance)
|
y = int((self.centery-self.y) * 2 / self.distance)
|
||||||
if x == y == 0: return
|
if x == y == 0: return
|
||||||
for enemy in self.enemies: self.map[enemy.x][enemy.y] = EMPTY
|
for enemy in self.enemies: self.map[enemy.x][enemy.y] = EMPTY
|
||||||
|
|
||||||
self.map[MIDDLE][MIDDLE] = EMPTY
|
self.map[MIDDLE][MIDDLE] = EMPTY
|
||||||
if x:
|
self.centerx -= x * self.distance
|
||||||
self.centerx -= x * self.distance
|
self.map.rotate(x)
|
||||||
self.map.rotate(x)
|
self.rotatex += x
|
||||||
self.rotatex += x
|
self.centery -= y * self.distance
|
||||||
if y:
|
for d in self.map: d.rotate(y)
|
||||||
self.centery -= y * self.distance
|
self.rotatey += y
|
||||||
for d in self.map: d.rotate(y)
|
|
||||||
self.rotatey += y
|
|
||||||
self.map[MIDDLE][MIDDLE] = HERO
|
self.map[MIDDLE][MIDDLE] = HERO
|
||||||
|
if self.map[self.destx][self.desty] != HERO:
|
||||||
|
self.destx += x
|
||||||
|
self.desty += y
|
||||||
|
self.stepx = self.stepy = 0
|
||||||
|
|
||||||
# Respawn the enemies that fall off the display
|
# Respawn the enemies that fall off the display
|
||||||
killist = []
|
killist = []
|
||||||
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 not (self.rangex[0] <= enemy.x <= self.rangex[-1]
|
||||||
|
and self.rangey[0] <= enemy.y <= self.rangey[-1]):
|
||||||
self.score += enemy.wound
|
self.score += enemy.wound
|
||||||
enemy.die()
|
enemy.die()
|
||||||
killist.append(i)
|
killist.append(i)
|
||||||
|
@ -307,26 +315,25 @@ class Maze:
|
||||||
return 0.0
|
return 0.0
|
||||||
for enemy in self.enemies:
|
for enemy in self.enemies:
|
||||||
x, y = self.get_pos(enemy.x, enemy.y)
|
x, y = self.get_pos(enemy.x, enemy.y)
|
||||||
if (max(abs(herox - x), abs(heroy - y)) * 2 < self.distance
|
if max(abs(herox - x), abs(heroy - y)) * 2 < self.distance:
|
||||||
and enemy.awake):
|
|
||||||
return 0.0
|
return 0.0
|
||||||
return vx or vy
|
return vx or vy
|
||||||
|
|
||||||
def update(self, fps):
|
def update(self, fps):
|
||||||
"""Update the maze."""
|
"""Update the maze."""
|
||||||
self.fps = fps
|
self.fps = fps
|
||||||
dx = self.is_valid_move(vx=self.vx)
|
self.vx = self.is_valid_move(vx=self.vx)
|
||||||
self.centerx += dx
|
self.centerx += self.vx
|
||||||
dy = self.is_valid_move(vy=self.vy)
|
self.vy = self.is_valid_move(vy=self.vy)
|
||||||
self.centery += dy
|
self.centery += self.vy
|
||||||
|
|
||||||
self.next_move -= 1000.0 / self.fps
|
self.next_move -= 1000.0 / self.fps
|
||||||
self.next_slashfx -= 1000.0 / self.fps
|
self.next_slashfx -= 1000.0 / self.fps
|
||||||
|
|
||||||
self.rotate()
|
self.rotate()
|
||||||
if dx or dy:
|
if self.vx or self.vy:
|
||||||
for enemy in self.enemies: enemy.wake()
|
for enemy in self.enemies: enemy.wake()
|
||||||
for bullet in self.bullets: bullet.place(dx, dy)
|
for bullet in self.bullets: bullet.place(self.vx, self.vy)
|
||||||
|
|
||||||
for enemy in self.enemies: enemy.update()
|
for enemy in self.enemies: enemy.update()
|
||||||
if not self.hero.dead:
|
if not self.hero.dead:
|
||||||
|
@ -351,6 +358,44 @@ class Maze:
|
||||||
self.rangey = list(range(MIDDLE - h, MIDDLE + h + 1))
|
self.rangey = list(range(MIDDLE - h, MIDDLE + h + 1))
|
||||||
self.slashd = self.hero.R + self.distance/SQRT2
|
self.slashd = self.hero.R + self.distance/SQRT2
|
||||||
|
|
||||||
|
def set_step(self, xcheck=(lambda _: True), ycheck=(lambda _: True)):
|
||||||
|
"""Return direction on the shortest path to the destination."""
|
||||||
|
if self.stepx or self.stepy and self.vx == self.vy == 0.0:
|
||||||
|
x, y = MIDDLE - self.stepx, MIDDLE - self.stepy
|
||||||
|
if self.stepx and not self.stepy:
|
||||||
|
nextx = x - self.stepx
|
||||||
|
n = self.map[x][y - 1] == EMPTY == self.map[nextx][y - 1]
|
||||||
|
s = self.map[x][y + 1] == EMPTY == self.map[nextx][y + 1]
|
||||||
|
self.stepy = n - s
|
||||||
|
elif not self.stepx and self.stepy:
|
||||||
|
nexty = y - self.stepy
|
||||||
|
w = self.map[x - 1][y] == EMPTY == self.map[x - 1][nexty]
|
||||||
|
e = self.map[x + 1][y] == EMPTY == self.map[x + 1][nexty]
|
||||||
|
self.stepx = w - e
|
||||||
|
return
|
||||||
|
|
||||||
|
queue = defaultdict(list, {0: [(self.destx, self.desty)]})
|
||||||
|
visited, count, distance = set(), 1, 0
|
||||||
|
while count:
|
||||||
|
# Hashes of small intergers are themselves so queue is sorted
|
||||||
|
if not queue[distance]: distance += 1
|
||||||
|
x, y = queue[distance].pop()
|
||||||
|
count -= 1
|
||||||
|
if (x, y) not in visited:
|
||||||
|
visited.add((x, y))
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
dx, dy = MIDDLE - x, MIDDLE - y
|
||||||
|
if dx**2 + dy**2 <= 2:
|
||||||
|
self.stepx, self.stepy = dx, dy
|
||||||
|
return
|
||||||
|
for i, j in around(x, y):
|
||||||
|
if self.map[i][j] == EMPTY and xcheck(i) and ycheck(j):
|
||||||
|
queue[distance + 1].append((i, j))
|
||||||
|
count += 1
|
||||||
|
self.stepx, self.stepy = 0, 0
|
||||||
|
|
||||||
def isfast(self):
|
def isfast(self):
|
||||||
"""Return if the hero is moving faster than HERO_SPEED."""
|
"""Return if the hero is moving faster than HERO_SPEED."""
|
||||||
return (self.vx**2+self.vy**2)**0.5*self.fps > HERO_SPEED*self.distance
|
return (self.vx**2+self.vy**2)**0.5*self.fps > HERO_SPEED*self.distance
|
||||||
|
@ -359,6 +404,8 @@ class Maze:
|
||||||
"""Handle loses."""
|
"""Handle loses."""
|
||||||
self.hero.dead = True
|
self.hero.dead = True
|
||||||
self.hero.slashing = self.hero.firing = False
|
self.hero.slashing = self.hero.firing = False
|
||||||
|
self.destx = self.desty = MIDDLE
|
||||||
|
self.stepx = self.stepy = 0
|
||||||
self.vx = self.vy = 0.0
|
self.vx = self.vy = 0.0
|
||||||
play(self.sfx_lose)
|
play(self.sfx_lose)
|
||||||
|
|
||||||
|
@ -368,6 +415,8 @@ class Maze:
|
||||||
self.score = INIT_SCORE
|
self.score = 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.destx = self.desty = MIDDLE
|
||||||
|
self.stepx = self.stepy = 0
|
||||||
self.vx = self.vy = 0.0
|
self.vx = self.vy = 0.0
|
||||||
self.rotatex = self.rotatey = 0
|
self.rotatex = self.rotatey = 0
|
||||||
self.bullets, self.enemies = [], []
|
self.bullets, self.enemies = [], []
|
||||||
|
|
|
@ -19,12 +19,15 @@
|
||||||
|
|
||||||
__doc__ = 'Brutal Maze module for miscellaneous functions'
|
__doc__ = 'Brutal Maze module for miscellaneous functions'
|
||||||
|
|
||||||
|
from itertools import chain
|
||||||
from math import degrees, cos, sin, pi
|
from math import degrees, cos, sin, pi
|
||||||
from random import uniform
|
from random import shuffle, uniform
|
||||||
|
|
||||||
import pygame
|
import pygame
|
||||||
from pygame.gfxdraw import filled_polygon, aapolygon
|
from pygame.gfxdraw import filled_polygon, aapolygon
|
||||||
|
|
||||||
|
from .constants import ADJACENTS, CORNERS
|
||||||
|
|
||||||
|
|
||||||
def round2(number):
|
def round2(number):
|
||||||
"""Round a number to an int."""
|
"""Round a number to an int."""
|
||||||
|
@ -69,6 +72,15 @@ def cosin(x):
|
||||||
return cos(x) + sin(x)
|
return cos(x) + sin(x)
|
||||||
|
|
||||||
|
|
||||||
|
def around(x, y):
|
||||||
|
"""Return grids around the given one in random order."""
|
||||||
|
a = [(x + i, y + j) for i, j in ADJACENTS]
|
||||||
|
shuffle(a)
|
||||||
|
c = [(x + i, y + j) for i, j in CORNERS]
|
||||||
|
shuffle(c)
|
||||||
|
return chain(a, c)
|
||||||
|
|
||||||
|
|
||||||
def choices(d):
|
def choices(d):
|
||||||
"""Choose a random key from a dict which has values being relative
|
"""Choose a random key from a dict which has values being relative
|
||||||
weights of the coresponding keys.
|
weights of the coresponding keys.
|
||||||
|
|
|
@ -22,8 +22,10 @@ Move left: Left
|
||||||
Move right: Right
|
Move right: Right
|
||||||
Move up: Up
|
Move up: Up
|
||||||
Move down: Down
|
Move down: Down
|
||||||
|
# Move hero using mouse
|
||||||
|
Auto move: Mouse3
|
||||||
Long-range attack: Mouse1
|
Long-range attack: Mouse1
|
||||||
Close-range attack: Mouse3
|
Close-range attack: Space
|
||||||
|
|
||||||
[Server]
|
[Server]
|
||||||
# Enabling remote control will disable control via keyboard and mouse.
|
# Enabling remote control will disable control via keyboard and mouse.
|
||||||
|
|
6
setup.py
6
setup.py
|
@ -7,8 +7,8 @@ with open('README.rst') as f:
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='brutalmaze',
|
name='brutalmaze',
|
||||||
version='0.6.5',
|
version='0.7.0',
|
||||||
description='A minimalist hack and slash game with fast-paced action',
|
description='A minimalist TPS game with fast-paced action',
|
||||||
long_description=long_description,
|
long_description=long_description,
|
||||||
url='https://github.com/McSinyx/brutalmaze',
|
url='https://github.com/McSinyx/brutalmaze',
|
||||||
author='Nguyễn Gia Phong',
|
author='Nguyễn Gia Phong',
|
||||||
|
@ -25,7 +25,7 @@ setup(
|
||||||
'Operating System :: OS Independent',
|
'Operating System :: OS Independent',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
'Topic :: Games/Entertainment :: Arcade'],
|
'Topic :: Games/Entertainment :: Arcade'],
|
||||||
keywords='pygame action-game arcade-game maze socket-server ai-challenges',
|
keywords='pygame third-person-shooter arcade-game maze ai-challenges',
|
||||||
packages=['brutalmaze'],
|
packages=['brutalmaze'],
|
||||||
install_requires=['appdirs', 'pygame>=1.9'],
|
install_requires=['appdirs', 'pygame>=1.9'],
|
||||||
package_data={'brutalmaze': ['icon.png', 'soundfx/*.ogg', 'settings.ini']},
|
package_data={'brutalmaze': ['icon.png', 'soundfx/*.ogg', 'settings.ini']},
|
||||||
|
|
2
wiki
2
wiki
|
@ -1 +1 @@
|
||||||
Subproject commit 8f40eb7b3d368076bb2b9fc4d268472af62e2886
|
Subproject commit b4169d8f16a5f99b11f41e4823ca67065788cbac
|
Loading…
Reference in New Issue