summary refs log tree commit diff homepage
diff options
context:
space:
mode:
-rw-r--r--README.rst21
-rw-r--r--brutalmaze/characters.py63
-rw-r--r--brutalmaze/maze.py76
-rw-r--r--brutalmaze/weapons.py10
-rwxr-xr-xsetup.py4
5 files changed, 116 insertions, 58 deletions
diff --git a/README.rst b/README.rst
index 720d270..705f722 100644
--- a/README.rst
+++ b/README.rst
@@ -12,13 +12,14 @@ job is to help the trigon fight against those evil squares and find a way out
 (if there is any). Be aware that the more get killed, the more will show up and
 our hero will get weaker when wounded.
 
-As a research game, Brutal Maze has a few primary goals:
+Being a research game, Brutal Maze has a few primary goals:
 
 * Highly portable.
 * Auto-generated and infinite maze.
-* No binary art data.
-* Enemies with randomized attributes: stun, poison, etc.
-* Resizable in realtime.
+* No binary data for drawing.
+* Enemies with randomized attributes: stun, poison, camo, etc.
+* Somewhat a realistic physic and logic system.
+* Resizable game window in-game.
 
 Installation
 ------------
@@ -27,10 +28,10 @@ Brutal Maze is written in Python and is compatible with both version 2 and 3.
 The installation procedure should be as simply as follow:
 
 * Install Python and `pip <https://pip.pypa.io/en/latest/>`_. 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``.
+  directory for `Python scripts <https://docs.python.org/2/install/index.html#alternate-installation-the-user-scheme>`_
+  is your ``PATH``.
+* Open Terminal or Command Prompt and run ``pip install --user brutalmaze``.
+  Now you can lauch the game by running the command ``brutalmaze``.
 
 Control
 -------
@@ -54,7 +55,7 @@ Right, ``d``
    Move right.
 
 Left Mouse
-   Long-ranged attack.
+   Long-range attack.
 
 Return, Right Mouse
-   Close-ranged attack.
+   Close-range attack, also dodge from bullets.
diff --git a/brutalmaze/characters.py b/brutalmaze/characters.py
index 7a68f37..2e33379 100644
--- a/brutalmaze/characters.py
+++ b/brutalmaze/characters.py
@@ -20,18 +20,33 @@
 __doc__ = 'brutalmaze module for hero and enemy classes'
 
 from collections import deque
-from math import atan2, sin, pi
+from math import atan, atan2, sin, pi
 from random import choice, shuffle, uniform
 
 import pygame
 
 from .constants import *
-from .utils import randsign, regpoly, fill_aapolygon, sign
+from .utils import sign, cosin, randsign, regpoly, fill_aapolygon
 from .weapons import Bullet
 
 
 class Hero:
-    """Object representing the hero."""
+    """Object representing the hero.
+
+    Attributes:
+        surface (pygame.Surface): the display to draw on
+        x, y (int): coordinates of the center of the hero (in pixels)
+        angle (float): angle of the direction the hero pointing (in radians)
+        color (tuple of pygame.Color): colors of the hero on different HPs
+        R (int): circumradius of the regular triangle representing the hero
+        next_strike (int): the tick that the hero can do the next attack
+        slashing (bool): flag indicates if the hero is doing close-range attack
+        firing (bool): flag indicates if the hero is doing long-range attack
+        dead (bool): flag indicates if the hero is dead
+        spin_speed (float): speed of spinning (in frames per slash)
+        spin_queue (float): frames left to finish spinning
+        wound (float): amount of wound
+    """
     def __init__(self, surface, fps):
         self.surface = surface
         w, h = self.surface.get_width(), self.surface.get_height()
@@ -82,15 +97,29 @@ class Hero:
 
 
 class Enemy:
-    """Object representing an enemy."""
+    """Object representing an enemy.
+
+    Attributes:
+        maze (Maze): the maze
+        x, y (int): coordinates of the center of the enemy (in grids)
+        angle (float): angle of the direction the enemy pointing (in radians)
+        color (tuple of pygame.Color): colors of the enemy on different HPs
+        awake (bool): flag indicates if the enemy is active
+        next_strike (int): the tick that the enemy can do the next attack
+        move_speed (float): speed of movement (in frames per grid)
+        offsetx, offsety (integer): steps moved from the center of the grid
+        spin_speed (float): speed of spinning (in frames per slash)
+        spin_queue (float): frames left to finish spinning
+        wound (float): amount of wound
+    """
     def __init__(self, maze, x, y):
         self.maze = maze
-        self.angle, self.color = pi / 4, TANGO[choice(ENEMIES)]
         self.x, self.y = x, y
         self.maze.map[x][y] = ENEMY
+        self.angle, self.color = pi / 4, TANGO[choice(ENEMIES)]
 
         self.awake = False
-        self.next_move = 0
+        self.next_strike = 0
         self.move_speed = self.maze.fps / ENEMY_SPEED
         self.offsetx = self.offsety = 0
         self.spin_speed = self.maze.fps / ENEMY_HP
@@ -108,15 +137,33 @@ class Enemy:
         self.y += y
         self.maze.map[self.x][self.y] = ENEMY
 
+    def wake(self):
+        """Wake the enemy up if it can see the hero."""
+        if self.awake: return
+        startx = starty = MIDDLE
+        stopx, stopy, distance = self.x, self.y, self.maze.distance
+        if startx > stopx: startx, stopx = stopx, startx
+        if starty > stopy: starty, stopy = stopy, starty
+        dx = (self.x-MIDDLE)*distance + self.maze.centerx - self.maze.x
+        dy = (self.y-MIDDLE)*distance + self.maze.centery - self.maze.y
+        mind = cosin(abs(atan(dy / dx)) if dx else 0) * distance
+        def length(x, y): return abs(dy*x - dx*y) / (dy**2 + dx**2)**0.5
+        for i in range(startx, stopx + 1):
+            for j in range(starty, stopy + 1):
+                if self.maze.map[i][j] != WALL: continue
+                x, y = self.maze.pos(i, j)
+                if length(x - self.maze.x, y - self.maze.y) <= mind: return
+        self.awake = True
+
     def fire(self):
         """Return True if the enemy shot the hero, False otherwise."""
         x, y = self.pos()
         if (self.maze.length(x, y) > FIRANGE*self.maze.distance
-            or self.next_move > pygame.time.get_ticks()
+            or self.next_strike > pygame.time.get_ticks()
             or (self.x, self.y) in AROUND_HERO or self.offsetx or self.offsety
             or uniform(-2, 2) < (INIT_SCORE/self.maze.score) ** 2):
             return False
-        self.next_move = pygame.time.get_ticks() + ATTACK_SPEED
+        self.next_strike = pygame.time.get_ticks() + ATTACK_SPEED
         self.maze.bullets.append(Bullet(
             self.maze.surface, x, y,
             atan2(self.maze.y - y, self.maze.x - x), self.color[0]))
diff --git a/brutalmaze/maze.py b/brutalmaze/maze.py
index ae12ded..8fd8645 100644
--- a/brutalmaze/maze.py
+++ b/brutalmaze/maze.py
@@ -20,7 +20,7 @@
 __doc__ = 'brutalmaze module for the maze class'
 
 from collections import deque
-from math import pi, atan, atan2, log
+from math import pi, atan2, log
 from random import choice, getrandbits
 
 import pygame
@@ -28,7 +28,7 @@ from pygame import RESIZABLE
 
 from .characters import Hero, Enemy
 from .constants import *
-from .utils import round2, sign, cosin, regpoly, fill_aapolygon
+from .utils import round2, sign, regpoly, fill_aapolygon
 from .weapons import Bullet
 
 
@@ -53,13 +53,33 @@ def new_column():
 
 
 class Maze:
-    """Object representing the maze, including the characters."""
+    """Object representing the maze, including the characters.
+
+    Attributes:
+        w, h: width and height of the display
+        fps: current frame rate
+        surface (pygame.Surface): the display to draw on
+        distance (float): distance between centers of grids (in px)
+        x, y (int): coordinates of the center of the hero (in px)
+        centerx, centery (float): center grid's center's coordinates (in px)
+        rangex, rangey: range of the index of the grids on display
+        paused (bool): flag indicates if the game is paused
+        score (float): current score
+        map (deque of deque): map of grids representing objects on the maze
+        down, right (int): direction the maze moving
+        rotatex, rotatey: grids rotated
+        bullets (list of Bullet): bullets flying
+        enemies (list of Enemy): alive enemies
+        hero (Hero): the hero
+        slashd (float): minimum distance for slashes to be effective
+    """
     def __init__(self, size, fps):
         self.w, self.h = size
         self.fps = fps
         self.surface = pygame.display.set_mode(size, RESIZABLE)
         self.distance = (self.w * self.h / 416) ** 0.5
-        self.middlex, self.middley = self.x, self.y = self.w >> 1, self.h >> 1
+        self.x, self.y = self.w // 2, self.h // 2
+        self.centerx, self.centery = self.w / 2.0, self.h / 2.0
         w, h = (int(i/self.distance/2 + 2) for i in size)
         self.rangex = range(MIDDLE - w, MIDDLE + w + 1)
         self.rangey = range(MIDDLE - h, MIDDLE + h + 1)
@@ -90,8 +110,8 @@ class Maze:
 
     def pos(self, x, y):
         """Return coordinate of the center of the grid (x, y)."""
-        return (self.middlex + (x - MIDDLE)*self.distance,
-                self.middley + (y - MIDDLE)*self.distance)
+        return (self.centerx + (x - MIDDLE)*self.distance,
+                self.centery + (y - MIDDLE)*self.distance)
 
     def draw(self):
         """Draw the maze."""
@@ -103,36 +123,19 @@ class Maze:
                 square = regpoly(4, self.distance / SQRT2, pi / 4, x, y)
                 fill_aapolygon(self.surface, square, FG_COLOR)
 
-    def wake(self, enemy):
-        """Wake the enemy up if it can see the hero."""
-        dx = (enemy.x-MIDDLE)*self.distance + self.middlex - self.x
-        dy = (enemy.y-MIDDLE)*self.distance + self.middley - self.y
-        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 self.map[i][j] != WALL: continue
-                x, y = self.pos(i, j)
-                d = abs(dy*(x-self.x) - dx*(y-self.y)) / (dy**2 + dx**2)**0.5
-                if d <= mind: return
-        enemy.awake = True
-
     def rotate(self):
         """Rotate the maze if needed."""
-        x = int((self.middlex-self.x) * 2 / self.distance)
-        y = int((self.middley-self.y) * 2 / self.distance)
+        x = int((self.centerx-self.x) * 2 / self.distance)
+        y = int((self.centery-self.y) * 2 / self.distance)
         if x == y == 0: return
         for enemy in self.enemies: self.map[enemy.x][enemy.y] = EMPTY
         self.map[MIDDLE][MIDDLE] = EMPTY
         if x:
-            self.middlex -= x * self.distance
+            self.centerx -= x * self.distance
             self.map.rotate(x)
             self.rotatex += x
         if y:
-            self.middley -= y * self.distance
+            self.centery -= y * self.distance
             for d in self.map: d.rotate(y)
             self.rotatey += y
         self.map[MIDDLE][MIDDLE] = HERO
@@ -174,7 +177,7 @@ class Maze:
         return ((self.x-x)**2 + (self.y-y)**2)**0.5
 
     def slash(self):
-        """Handle close-ranged attacks."""
+        """Handle close-range attacks."""
         for enemy in self.enemies:
             if not enemy.spin_queue: continue
             x, y = enemy.pos()
@@ -249,14 +252,13 @@ class Maze:
         if self.paused: return
         self.fps, step = fps, self.distance * HERO_SPEED / fps
         dx = step * self.right * self.isvalid(step, dx=self.right)
-        self.middlex += dx
+        self.centerx += dx
         dy = step * self.down * self.isvalid(step, dy=self.down)
-        self.middley += dy
+        self.centery += dy
 
         if dx or dy:
             self.rotate()
-            for enemy in self.enemies:
-                if not enemy.awake: self.wake(enemy)
+            for enemy in self.enemies: enemy.wake()
             for bullet in self.bullets: bullet.place(dx, dy)
 
         self.draw()
@@ -275,12 +277,12 @@ class Maze:
         self.surface = pygame.display.set_mode(size, RESIZABLE)
         self.hero.resize()
 
-        offsetx = (self.middlex-self.x) / self.distance
-        offsety = (self.middley-self.y) / self.distance
+        offsetx = (self.centerx-self.x) / self.distance
+        offsety = (self.centery-self.y) / self.distance
         self.distance = (w * h / 416) ** 0.5
-        self.x, self.y = w >> 1, h >> 1
-        self.middlex = self.x + offsetx*self.distance
-        self.middley = self.y + offsety*self.distance
+        self.x, self.y = w // 2, h // 2
+        self.centerx = self.x + offsetx*self.distance
+        self.centery = self.y + offsety*self.distance
         w, h = int(w/self.distance/2 + 2), int(h/self.distance/2 + 2)
         self.rangex = range(MIDDLE - w, MIDDLE + w + 1)
         self.rangey = range(MIDDLE - h, MIDDLE + h + 1)
diff --git a/brutalmaze/weapons.py b/brutalmaze/weapons.py
index 062ba68..7504457 100644
--- a/brutalmaze/weapons.py
+++ b/brutalmaze/weapons.py
@@ -28,7 +28,15 @@ from .utils import regpoly, fill_aapolygon
 
 
 class Bullet:
-    """Object representing a bullet."""
+    """Object representing a bullet.
+
+    Attributes:
+        surface (pygame.Surface): the display to draw on
+        x, y (int): coordinates of the center of the bullet (in pixels)
+        angle (float): angle of the direction the bullet pointing (in radians)
+        color (pygame.Color): color of the bullet
+        fall_time (int): the tick that the bullet will fall down
+    """
     def __init__(self, surface, x, y, angle, color):
         self.surface = surface
         self.x, self.y, self.angle, self.color = x, y, angle, color
diff --git a/setup.py b/setup.py
index b1f9853..cc739a2 100755
--- a/setup.py
+++ b/setup.py
@@ -7,8 +7,8 @@ with open('README.rst') as f:
 
 setup(
     name='brutalmaze',
-    version='0.0.1',
-    description='Brutal Maze',
+    version='0.0.2',
+    description='A research hash and slash game with fast-paced action and a minimalist art style',
     long_description=long_description,
     url='https://github.com/McSinyx/brutalmaze',
     author='Nguyễn Gia Phong',