summary refs log tree commit diff homepage
diff options
context:
space:
mode:
authorRaphael McSinyx <vn.mcsinyx@gmail.com>2017-10-13 15:10:16 +0700
committerRaphael McSinyx <vn.mcsinyx@gmail.com>2017-10-13 15:20:09 +0700
commit05bfead6e967d1728ba944451a561e13647ac01b (patch)
treef649799147cd06500168d0cad0e9036df262e828
parent613732b7c6e579b2e70101015da3835cabff285b (diff)
downloadbrutalmaze-05bfead6e967d1728ba944451a561e13647ac01b.tar.gz
Give enemies eyes
-rw-r--r--README.rst1
-rw-r--r--brutalmaze/characters.py30
-rw-r--r--brutalmaze/constants.py2
-rw-r--r--brutalmaze/maze.py90
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)