diff options
-rw-r--r-- | README.rst | 1 | ||||
-rw-r--r-- | brutalmaze/characters.py | 30 | ||||
-rw-r--r-- | brutalmaze/constants.py | 2 | ||||
-rw-r--r-- | brutalmaze/maze.py | 90 |
4 files changed, 75 insertions, 48 deletions
diff --git a/README.rst b/README.rst index 760068e..9b4e9a4 100644 --- a/README.rst +++ b/README.rst @@ -4,6 +4,7 @@ Brutal Maze **Brutal Maze** is a research hash and slash game with *polyart* graphic and Tango color palette. Its primary goals are: +* Highly portable. * Auto-generated and infinite maze. * No binary data. * Enemies with randomized attributes: stun, poison, etc. diff --git a/brutalmaze/characters.py b/brutalmaze/characters.py index be18fe9..47b910d 100644 --- a/brutalmaze/characters.py +++ b/brutalmaze/characters.py @@ -47,6 +47,11 @@ def fill_aapolygon(surface, points, color): filled_polygon(surface, points, color) +def pos(x, y, distance, middlex, middley): + """Return coordinate of the center of the grid (x, y).""" + return middlex + (x - MIDDLE)*distance, middley + (y - MIDDLE)*distance + + class Hero: """Object representing the hero.""" def __init__(self, surface): @@ -60,12 +65,6 @@ class Hero: self.speed = FPS // len(self.color) self.spin_queue, self.slashing = deque(), False - def resize(self): - """Resize the hero.""" - w, h = self.surface.get_width(), self.surface.get_height() - self.x, self.y = w >> 1, h >> 1 - self.R = int((w * h / sin(pi*2/3) / 624) ** 0.5) - def slash(self, hold=False): """Spin the hero. If the button is hold, delay before continue each spin. @@ -92,6 +91,12 @@ class Hero: self.angle = atan2(y - self.y, x - self.x) self.draw() + def resize(self): + """Resize the hero.""" + w, h = self.surface.get_width(), self.surface.get_height() + self.x, self.y = w >> 1, h >> 1 + self.R = int((w * h / sin(pi*2/3) / 624) ** 0.5) + class Enemy: """Object representing an enemy.""" @@ -100,25 +105,24 @@ class Enemy: self.x, self.y = x, y self.angle, self.color = pi / 4, TANGO[TANGO_KEYS[n]] - self.moving = False + self.awake, self.moving = False, False self.wound = 0 self.speed = FPS // len(self.color) self.spin_queue, self.slashing = deque(), False def draw(self, distance, middlex, middley, color=None): - """Draw the enemy, given distance between blocks and the middle - block. + """Draw the enemy, given distance between grids and the middle + grid. """ - x = middlex + (self.x - MIDDLE)*distance - y = middley + (self.y - MIDDLE)*distance + x, y = pos(self.x, self.y, distance, middlex, middley) square = regpoly(4, int(distance / SQRT2), x, y, self.angle) fill_aapolygon(self.surface, square, color or self.color[self.wound]) def update(self, distance, middlex, middley): """Update the enemy.""" - if (self.angle - pi/4) < FLOATING_ZERO: self.angle = pi / 4 + if (self.angle - pi/4) < EPSILON: self.angle = pi / 4 self.draw(distance, middlex, middley, color=BG_COLOR) - if not self.spin_queue: + if self.awake and not self.spin_queue: self.spin_queue.extend([randsign()] * self.speed) if self.spin_queue and not self.moving: self.angle += self.spin_queue.popleft() * pi / 2 / self.speed diff --git a/brutalmaze/constants.py b/brutalmaze/constants.py index d4e01b9..2e476be 100644 --- a/brutalmaze/constants.py +++ b/brutalmaze/constants.py @@ -20,7 +20,7 @@ from pygame.locals import * from pygame.math import Vector2 -FLOATING_ZERO = 8 ** -8 +EPSILON = 8 ** -8 SQRT2 = 2 ** 0.5 FPS = 30 diff --git a/brutalmaze/maze.py b/brutalmaze/maze.py index b35708b..4ac57cb 100644 --- a/brutalmaze/maze.py +++ b/brutalmaze/maze.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- # maze.py - module containing the maze object # This file is part of brutalmaze @@ -19,12 +18,12 @@ # Copyright (C) 2017 Nguyễn Gia Phong from collections import deque -from math import pi +from math import atan, cos, sin, pi from random import getrandbits import pygame -from .characters import regpoly, fill_aapolygon, Hero, Enemy +from .characters import pos, regpoly, fill_aapolygon, Hero, Enemy from .constants import * @@ -33,6 +32,11 @@ def sign(n): return -1 if n < 0 else 1 if n else 0 +def cosin(x): + """Return the sum of cosine and sine of x (measured in radians).""" + return cos(x) + sin(x) + + class Maze: """Object representing the maze, including the characters.""" def __init__(self, size): @@ -40,13 +44,13 @@ class Maze: self.surface = pygame.display.set_mode(size, RESIZABLE) self.distance = int((self.w * self.h / 416) ** 0.5) self.step = self.distance // 5 - self.x, self.y = self.w >> 1, self.h >> 1 + self.middlex, self.middley = self.x, self.y = self.w >> 1, self.h >> 1 w, h = self.w//self.distance+2 >> 1, self.h//self.distance+2 >> 1 self.rangex = range(MIDDLE - w, MIDDLE + w + 1) self.rangey = range(MIDDLE - h, MIDDLE + h + 1) self.hero = Hero(self.surface) - self.enemies = [Enemy(self.surface, 4, 35, 35)] + self.enemies = [Enemy(self.surface, 4, 34, 34)] self.right = self.down = self.offsetx = self.offsety = 0 def wall(bit, upper=True): @@ -68,38 +72,29 @@ class Maze: def draw(self): """Draw the maze.""" self.surface.fill(BG_COLOR) - middlex = self.x + self.offsetx*self.step - middley = self.y + self.offsety*self.step for i in self.rangex: for j in self.rangey: if not self.map[i][j]: continue - x = middlex + (i - MIDDLE)*self.distance - y = middley + (j - MIDDLE)*self.distance + x, y = pos(i, j, self.distance, self.middlex, self.middley) square = regpoly(4, int(self.distance / SQRT2), x, y, pi / 4) fill_aapolygon(self.surface, square, FG_COLOR) - def resize(self, w, h): - """Resize the maze.""" - size = self.w, self.h = w, h - self.surface = pygame.display.set_mode(size, RESIZABLE) - self.hero.resize() - - self.distance = int((w * h / 416) ** 0.5) - self.step = self.distance // 5 - self.x, self.y = w >> 1, h >> 1 - w, h = self.w//self.distance+2 >> 1, self.h//self.distance+2 >> 1 - self.rangex = range(MIDDLE - w, MIDDLE + w + 1) - self.rangey = range(MIDDLE - h, MIDDLE + h + 1) - self.draw() - - 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. - """ - self.right += x - self.down += y - self.right, self.down = sign(self.right), sign(self.down) + def wake(self, enemy): + """Wake the enemy up if it can see the hero.""" + dx = (enemy.x - MIDDLE)*self.distance + self.offsetx*self.step + dy = (enemy.y - MIDDLE)*self.distance + self.offsety*self.step + mind = cosin(abs(atan(dy / dx)) if dx else 0) * self.distance + startx = starty = MIDDLE + stopx, stopy = enemy.x, enemy.y + if startx > stopx : startx, stopx = stopx, startx + if starty > stopy : starty, stopy = stopy, starty + for i in range(startx, stopx + 1): + for j in range(starty, stopy + 1): + if not self.map[i][j] or (i, j) == (enemy.x, enemy.y): continue + x, y = pos(i, j, self.distance, self.middlex, self.middley) + d = abs(dy*(x-self.x) - dx*(y-self.y)) / (dy**2 + dx**2)**0.5 + if d <= mind: return + enemy.awake = True def update(self): """Update the maze.""" @@ -136,11 +131,38 @@ class Maze: for d in self.map: d.rotate(s) for enemy in self.enemies: enemy.place(0, s) + self.middlex = self.x + self.offsetx*self.step + self.middley = self.y + self.offsety*self.step self.draw() + for enemy in self.enemies: + if not enemy.awake: self.wake(enemy) - middlex = self.x + self.offsetx*self.step - middley = self.y + self.offsety*self.step for enemy in self.enemies: - enemy.update(self.distance, middlex, middley) + enemy.update(self.distance, self.middlex, self.middley) self.hero.update() pygame.display.flip() + + def resize(self, w, h): + """Resize the maze.""" + size = self.w, self.h = w, h + self.surface = pygame.display.set_mode(size, RESIZABLE) + self.hero.resize() + + self.distance = int((w * h / 416) ** 0.5) + self.step = self.distance // 5 + self.middlex = self.x + self.offsetx*self.step + self.middley = self.y + self.offsety*self.step + self.x, self.y = w >> 1, h >> 1 + w, h = self.w//self.distance+2 >> 1, self.h//self.distance+2 >> 1 + self.rangex = range(MIDDLE - w, MIDDLE + w + 1) + self.rangey = range(MIDDLE - h, MIDDLE + h + 1) + self.draw() + + 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. + """ + self.right += x + self.down += y + self.right, self.down = sign(self.right), sign(self.down) |