summary refs log tree commit diff homepage
diff options
context:
space:
mode:
-rw-r--r--README.rst7
-rw-r--r--brutalmaze/characters.py70
-rw-r--r--brutalmaze/constants.py4
-rw-r--r--brutalmaze/main.py8
-rw-r--r--brutalmaze/maze.py26
-rw-r--r--brutalmaze/weapons.py4
6 files changed, 64 insertions, 55 deletions
diff --git a/README.rst b/README.rst
index eab00b8..557069f 100644
--- a/README.rst
+++ b/README.rst
@@ -47,5 +47,8 @@ Left, ``a``
 Right, ``d``
    Move right.
 
-Return, Left Mouse
-   Close-range attack.
+Left Mouse
+   Long-ranged attack.
+
+Return, Right Mouse
+   Close-ranged attack.
diff --git a/brutalmaze/characters.py b/brutalmaze/characters.py
index 029b78d..dd69d69 100644
--- a/brutalmaze/characters.py
+++ b/brutalmaze/characters.py
@@ -26,7 +26,7 @@ from random import shuffle
 import pygame
 
 from .constants import *
-from .utils import round2, randsign, regpoly, fill_aapolygon, pos, sign
+from .utils import randsign, regpoly, fill_aapolygon, pos, sign
 
 
 class Hero:
@@ -38,42 +38,32 @@ class Hero:
         self.angle, self.color = pi / 4, TANGO['Aluminium']
         self.R = int((w * h / sin(pi*2/3) / 624) ** 0.5)
 
-        self.spin_speed = round2(fps / len(self.color))
-        self.spin_queue, self.slashing = deque(), False
-        self.wound = 0.0
-
-    def get_color(self):
-        """Return the color of the hero based on the amount of wounds."""
-        return self.color[int(self.wound)]
-
-    def slash(self, hold=False):
-        """Spin the hero. If the button is hold, delay before continue
-        each spin.
-        """
-        if self.slashing and not self.spin_queue:
-            if hold: self.spin_queue.extend([0] * (self.spin_speed >> 1))
-            self.spin_queue.extend([randsign()] * self.spin_speed)
-
-    def draw(self):
-        """Draw the hero."""
-        trigon = regpoly(3, self.R, self.angle, self.x, self.y)
-        fill_aapolygon(self.surface, trigon, self.get_color())
+        self.next_strike = 0
+        self.slashing = self.firing = False
+        self.spin_speed = fps / len(self.color)
+        self.spin_queue = self.wound = 0.0
 
     def update(self, fps):
         """Update the hero."""
-        self.spin_speed = round2(fps / (len(self.color)-self.wound))
+        old_speed, time = self.spin_speed, pygame.time.get_ticks()
+        self.spin_speed = fps / (len(self.color)-self.wound**0.5)
+        self.spin_queue *= self.spin_speed / old_speed
         self.wound -= HEAL_SPEED / len(self.color) / self.spin_speed
         if self.wound < 0: self.wound = 0.0
 
-        self.slash(hold=True)
-        direction = self.spin_queue.popleft() if self.spin_queue else 0
-        if direction:
-            self.angle += direction * pi * 2 / 3 / self.spin_speed
+        if self.slashing and time >= self.next_strike:
+            self.next_strike = time + ATTACK_SPEED
+            self.spin_queue = randsign() * self.spin_speed
+        if abs(self.spin_queue) > 0.5:
+            self.angle += sign(self.spin_queue) * pi / 2 / self.spin_speed
+            self.spin_queue -= sign(self.spin_queue)
         else:
             # Follow the mouse cursor
             x, y = pygame.mouse.get_pos()
             self.angle = atan2(y - self.y, x - self.x)
-        self.draw()
+            self.spin_queue = 0.0
+        trigon = regpoly(3, self.R, self.angle, self.x, self.y)
+        fill_aapolygon(self.surface, trigon, self.color[int(self.wound)])
 
     def resize(self):
         """Resize the hero."""
@@ -91,25 +81,28 @@ class Enemy:
         self.maze[x][y] = ENEMY
 
         self.awake = False
+        self.next_move = 0
         self.move_speed = fps / MOVE_SPEED
         self.offsetx = self.offsety = 0
         self.spin_speed = fps / len(self.color)
         self.spin_queue = self.wound = 0.0
 
+    def firable(self):
+        """Return True if the enemies should shoot the hero,
+        False otherwise.
+        """
+        if not self.awake or self.spin_queue or self.offsetx or self.offsety:
+            return False
+        else:
+            self.next_move = pygame.time.get_ticks() + ATTACK_SPEED
+            return True
+
     def pos(self, distance, middlex, middley):
         """Return coordinate of the center of the enemy."""
         x, y = pos(self.x, self.y, distance, middlex, middley)
         step = distance / self.move_speed
         return x + self.offsetx*step, y + self.offsety*step
 
-    def draw(self, distance, middlex, middley):
-        """Draw the enemy, given distance between grids and the middle grid."""
-        radious = distance/SQRT2 - self.awake*2
-        square = regpoly(4, radious, self.angle,
-                         *self.pos(distance, middlex, middley))
-        color = self.color[int(self.wound)] if self.awake else FG_COLOR
-        fill_aapolygon(self.surface, square, color)
-
     def place(self, x=0, y=0):
         """Move the enemy by (x, y) (in grids)."""
         self.x += x
@@ -121,6 +114,7 @@ class Enemy:
 
         Return True if it moved, False otherwise.
         """
+        if self.next_move > pygame.time.get_ticks(): return False
         if self.offsetx:
             self.offsetx -= sign(self.offsetx)
             return True
@@ -152,7 +146,11 @@ class Enemy:
                 self.spin_queue -= sign(self.spin_queue)
             else:
                 self.angle, self.spin_queue = pi / 4, 0.0
-        self.draw(distance, middlex, middley)
+        radious = distance/SQRT2 - self.awake*2
+        square = regpoly(4, radious, self.angle,
+                         *self.pos(distance, middlex, middley))
+        color = self.color[int(self.wound)] if self.awake else FG_COLOR
+        fill_aapolygon(self.surface, square, color)
 
     def die(self):
         """Kill the enemy."""
diff --git a/brutalmaze/constants.py b/brutalmaze/constants.py
index 4d750ee..e85c1cb 100644
--- a/brutalmaze/constants.py
+++ b/brutalmaze/constants.py
@@ -37,8 +37,10 @@ MIDDLE = (MAZE_SIZE + MAZE_SIZE%2 - 1)*ROAD_WIDTH + ROAD_WIDTH//2
 LAST_ROW = (MAZE_SIZE-1) * ROAD_WIDTH * 2
 INIT_SCORE = 208.2016
 MOVE_SPEED = 5  # grid/s
+BULLET_SPEED = 10   # grid/s
 HEAL_SPEED = 1.0    # HP/s
-BULLET_LIFETIME = 1000  # milliseconds
+ATTACK_SPEED = 333  # ms/strike
+BULLET_LIFETIME = 1000  # ms
 
 EMPTY, WALL, HERO, ENEMY = range(4)
 ADJACENT_GRIDS = (1, 0), (0, 1), (-1, 0), (0, -1)
diff --git a/brutalmaze/main.py b/brutalmaze/main.py
index 1874ed6..eb17954 100644
--- a/brutalmaze/main.py
+++ b/brutalmaze/main.py
@@ -48,7 +48,6 @@ def main():
                     maze.move(-1, 0)
                 elif event.key == K_RETURN:
                     maze.hero.slashing = True
-                    maze.hero.slash()
             elif event.type == KEYUP:
                 if event.key in (K_UP, K_w):
                     maze.move(0, -1)
@@ -62,12 +61,13 @@ def main():
                     maze.hero.slashing = False
             elif event.type == MOUSEBUTTONDOWN:
                 if event.button == 1:
+                    maze.hero.firing = True
+                elif event.button == 3:
                     maze.hero.slashing = True
-                    maze.hero.slash()
-                if event.button == 3:
-                    maze.fire()
             elif event.type == MOUSEBUTTONUP:
                 if event.button == 1:
+                    maze.hero.firing = False
+                elif event.button == 3:
                     maze.hero.slashing = False
             elif event.type == VIDEORESIZE:
                 maze.resize(event.w, event.h)
diff --git a/brutalmaze/maze.py b/brutalmaze/maze.py
index f78c1c5..f9877a4 100644
--- a/brutalmaze/maze.py
+++ b/brutalmaze/maze.py
@@ -18,8 +18,8 @@
 # Copyright (C) 2017 Nguyễn Gia Phong
 
 from collections import deque
-from math import pi, atan, log
-from random import choice, getrandbits
+from math import pi, atan, atan2, log
+from random import choice, getrandbits, uniform
 
 import pygame
 
@@ -168,7 +168,7 @@ class Maze:
             if d >= 0:
                 self.hero.wound += d / self.hero.R / enemy.spin_speed
 
-        if not self.hero.slashing: return
+        if not self.hero.spin_queue: return
         unit, killist = self.distance/SQRT2 * self.hero.spin_speed, []
         for i, enemy in enumerate(self.enemies):
             x, y = enemy.pos(self.distance, self.middlex, self.middley)
@@ -185,6 +185,17 @@ class Maze:
     def track_bullets(self):
         """Handle the bullets."""
         fallen, time = [], pygame.time.get_ticks()
+        for enemy in self.enemies:
+            if uniform(-2, 2) > (INIT_SCORE/self.score)**2 and enemy.firable():
+                x, y = enemy.pos(self.distance, self.middlex, self.middley)
+                self.bullets.append(
+                    Bullet(self.surface, x, y, atan2(self.y - y, self.x - x),
+                           enemy.color[0]))
+        if (self.hero.firing and not self.hero.slashing
+            and time >= self.hero.next_strike):
+            self.hero.next_strike = time + ATTACK_SPEED
+            self.bullets.append(Bullet(self.surface, self.x, self.y,
+                                       self.hero.angle, FG_COLOR))
         for i, bullet in enumerate(self.bullets):
             wound = float(bullet.fall_time-time) / BULLET_LIFETIME
             bullet.update(self.fps, self.distance)
@@ -207,7 +218,7 @@ class Maze:
                         fallen.append(i)
                         break
             elif length(bullet.x, bullet.y, self.x, self.y) < self.distance:
-                self.hero.wound += wound
+                if not self.hero.spin_queue: self.hero.wound += wound
                 fallen.append(i)
         for i in reversed(fallen): self.bullets.pop(i)
 
@@ -277,7 +288,6 @@ class Maze:
         self.rangex = range(MIDDLE - w, MIDDLE + w + 1)
         self.rangey = range(MIDDLE - h, MIDDLE + h + 1)
         self.slashd = self.hero.R + self.distance/SQRT2
-        self.draw()
 
     def move(self, x, y):
         """Command the maze to move x step/frame faster to the left and
@@ -288,11 +298,7 @@ class Maze:
         self.down += y
         self.right, self.down = sign(self.right), sign(self.down)
 
-    def fire(self):
-        """Create a bullet shot from the hero."""
-        self.bullets.append(
-            Bullet(self.surface, self.x, self.y, self.hero.angle, FG_COLOR))
-
     def lose(self):
         """Handle loses."""
+        print('Your score is: {}'.format(int(self.score - INIT_SCORE)))
         quit()
diff --git a/brutalmaze/weapons.py b/brutalmaze/weapons.py
index 108b042..0d2fb8c 100644
--- a/brutalmaze/weapons.py
+++ b/brutalmaze/weapons.py
@@ -36,10 +36,10 @@ class Bullet:
 
     def update(self, fps, distance):
         """Update the bullet."""
-        s = distance * 8 / fps
+        s = distance * BULLET_SPEED / fps
         self.x += s * cos(self.angle)
         self.y += s * sin(self.angle)
-        hexagon = regpoly(6, distance // 4, self.angle, self.x, self.y)
+        hexagon = regpoly(5, distance // 4, self.angle, self.x, self.y)
         fill_aapolygon(self.surface, hexagon, self.color)
 
     def place(self, x, y, step):