about summary refs log tree commit diff homepage
diff options
context:
space:
mode:
authorNguyễn Gia Phong <vn.mcsinyx@gmail.com>2017-10-21 17:40:10 +0700
committerNguyễn Gia Phong <vn.mcsinyx@gmail.com>2017-10-21 17:40:10 +0700
commitdaae5de9b7531a5401c88b1c3e5c0e6109b9e40b (patch)
tree1cd960c5cb5c1c6467f009f05507edd0c64eac82
parent81d0b68811283f35645d16bdec3aa7eae621cd02 (diff)
downloadbrutalmaze-daae5de9b7531a5401c88b1c3e5c0e6109b9e40b.tar.gz
Partially implement long-ranged attacks
-rw-r--r--brutalmaze/characters.py8
-rw-r--r--brutalmaze/constants.py5
-rw-r--r--brutalmaze/main.py2
-rw-r--r--brutalmaze/maze.py72
-rw-r--r--brutalmaze/utils.py5
-rw-r--r--brutalmaze/weapons.py48
6 files changed, 118 insertions, 22 deletions
diff --git a/brutalmaze/characters.py b/brutalmaze/characters.py
index c91da56..029b78d 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 randsign, regpoly, fill_aapolygon, pos, sign
+from .utils import round2, randsign, regpoly, fill_aapolygon, pos, sign
 
 
 class Hero:
@@ -38,7 +38,7 @@ 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 = int(round(fps / len(self.color)))
+        self.spin_speed = round2(fps / len(self.color))
         self.spin_queue, self.slashing = deque(), False
         self.wound = 0.0
 
@@ -61,7 +61,7 @@ class Hero:
 
     def update(self, fps):
         """Update the hero."""
-        self.spin_speed = int(round(fps / (len(self.color)-self.wound)))
+        self.spin_speed = round2(fps / (len(self.color)-self.wound))
         self.wound -= HEAL_SPEED / len(self.color) / self.spin_speed
         if self.wound < 0: self.wound = 0.0
 
@@ -111,7 +111,7 @@ class Enemy:
         fill_aapolygon(self.surface, square, color)
 
     def place(self, x=0, y=0):
-        """Move the enemy by (x, y)."""
+        """Move the enemy by (x, y) (in grids)."""
         self.x += x
         self.y += y
         self.maze[self.x][self.y] = ENEMY
diff --git a/brutalmaze/constants.py b/brutalmaze/constants.py
index fb10e28..4d750ee 100644
--- a/brutalmaze/constants.py
+++ b/brutalmaze/constants.py
@@ -31,13 +31,14 @@ GOLDEN_MEAN = 5**0.5/2 + 0.5
 INIT_FPS = 30.0
 SIZE = 640, 480
 MAZE_SIZE = 10
-ROAD_WIDTH = 5
-CELL_WIDTH = ROAD_WIDTH * 2
+ROAD_WIDTH = 5  # grids
+CELL_WIDTH = ROAD_WIDTH * 2     # grids
 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
 HEAL_SPEED = 1.0    # HP/s
+BULLET_LIFETIME = 1000  # milliseconds
 
 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 7a1dbf0..1874ed6 100644
--- a/brutalmaze/main.py
+++ b/brutalmaze/main.py
@@ -64,6 +64,8 @@ def main():
                 if event.button == 1:
                     maze.hero.slashing = True
                     maze.hero.slash()
+                if event.button == 3:
+                    maze.fire()
             elif event.type == MOUSEBUTTONUP:
                 if event.button == 1:
                     maze.hero.slashing = False
diff --git a/brutalmaze/maze.py b/brutalmaze/maze.py
index 85187a7..f78c1c5 100644
--- a/brutalmaze/maze.py
+++ b/brutalmaze/maze.py
@@ -25,7 +25,8 @@ import pygame
 
 from .characters import Hero, Enemy
 from .constants import *
-from .utils import pos, sign, cosin, length, regpoly, fill_aapolygon
+from .utils import round2, pos, sign, cosin, length, regpoly, fill_aapolygon
+from .weapons import Bullet
 
 __doc__ = 'brutalmaze module for the maze class'
 
@@ -68,12 +69,11 @@ class Maze:
         self.map = deque()
         for _ in range(MAZE_SIZE): self.map.extend(new_column())
         self.right = self.down = self.rotatex = self.rotatey = 0
-        self.enemies = []
+        self.bullets, self.enemies = [], []
         self.add_enemy()
         self.hero = Hero(self.surface, fps)
         self.map[MIDDLE][MIDDLE] = HERO
         self.slashd = self.hero.R + self.distance/SQRT2
-        self.draw()
 
     def add_enemy(self):
         """Add enough enemies."""
@@ -118,7 +118,9 @@ class Maze:
 
     def rotate(self, x, y):
         """Rotate the maze by (x, y)."""
+        if not x and not y: return
         for enemy in self.enemies: self.map[enemy.x][enemy.y] = EMPTY
+
         if x:
             self.offsetx = 0.0
             self.map.rotate(x)
@@ -158,7 +160,15 @@ class Maze:
                         self.map[c + k][LAST_ROW + j] = grid
 
     def slash(self):
-        """Slash the enemies."""
+        """Handle close-ranged attacks."""
+        for enemy in self.enemies:
+            if not enemy.spin_queue: continue
+            x, y = enemy.pos(self.distance, self.middlex, self.middley)
+            d = self.slashd - length(x, y, self.x, self.y)
+            if d >= 0:
+                self.hero.wound += d / self.hero.R / enemy.spin_speed
+
+        if not self.hero.slashing: 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)
@@ -172,6 +182,35 @@ class Maze:
         for i in reversed(killist): self.enemies.pop(i)
         self.add_enemy()
 
+    def track_bullets(self):
+        """Handle the bullets."""
+        fallen, time = [], pygame.time.get_ticks()
+        for i, bullet in enumerate(self.bullets):
+            wound = float(bullet.fall_time-time) / BULLET_LIFETIME
+            bullet.update(self.fps, self.distance)
+            if wound < 0:
+                fallen.append(i)
+            elif bullet.color == FG_COLOR:
+                x = MIDDLE + round2((bullet.x-self.x) / self.distance)
+                y = MIDDLE + round2((bullet.y-self.y) / self.distance)
+                if self.map[x][y] == WALL:
+                    fallen.append(i)
+                    continue
+                for j, enemy in enumerate(self.enemies):
+                    x, y = enemy.pos(self.distance, self.middlex, self.middley)
+                    if length(bullet.x, bullet.y, x, y) < self.distance:
+                        enemy.wound += wound
+                        if enemy.wound >= len(enemy.color):
+                            self.score += enemy.wound
+                            enemy.die()
+                            self.enemies.pop(j)
+                        fallen.append(i)
+                        break
+            elif length(bullet.x, bullet.y, self.x, self.y) < self.distance:
+                self.hero.wound += wound
+                fallen.append(i)
+        for i in reversed(fallen): self.bullets.pop(i)
+
     def update(self, fps):
         """Update the maze."""
         self.offsetx *= fps / self.fps
@@ -179,7 +218,7 @@ class Maze:
         self.fps, self.speed = fps, fps / MOVE_SPEED
         self.step = self.distance / self.speed
 
-        modified, d = False, self.distance*1.5 - self.hero.R
+        dx, dy, d = 0, 0, self.distance*1.5 - self.hero.R
         if self.right:
             self.offsetx += self.right
             s = sign(self.offsetx) * 2
@@ -189,7 +228,7 @@ class Maze:
                 and abs(self.offsetx*self.step) > d):
                 self.offsetx -= self.right
             else:
-                modified = True
+                dx = self.right
         if self.down:
             self.offsety += self.down
             s = sign(self.offsety) * 2
@@ -199,9 +238,9 @@ class Maze:
                 and abs(self.offsety*self.step) > d):
                 self.offsety -= self.down
             else:
-                modified = True
+                dy = self.down
 
-        if modified:
+        if dx or dy:
             self.map[MIDDLE][MIDDLE] = EMPTY
             self.rotate(sign(self.offsetx) * (abs(self.offsetx)>=self.speed),
                         sign(self.offsety) * (abs(self.offsety)>=self.speed))
@@ -210,18 +249,14 @@ class Maze:
             self.middley = self.y + self.offsety*self.step
             for enemy in self.enemies:
                 if not enemy.awake: self.wake(enemy)
+            for bullet in self.bullets: bullet.place(dx, dy, self.step)
 
         self.draw()
         for enemy in self.enemies:
             enemy.update(fps, self.distance, self.middlex, self.middley)
         self.hero.update(fps)
-        if self.hero.slashing: self.slash()
-        for enemy in self.enemies:
-            if not enemy.spin_queue: continue
-            x, y = enemy.pos(self.distance, self.middlex, self.middley)
-            d = self.slashd - length(x, y, self.x, self.y)
-            if d >= 0:
-                self.hero.wound += d / self.hero.R / enemy.spin_speed
+        self.slash()
+        self.track_bullets()
         pygame.display.flip()
         pygame.display.set_caption('Brutal Maze - Score: {}'.format(
             int(self.score - INIT_SCORE)))
@@ -253,6 +288,11 @@ 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."""
-        pygame.quit()
+        quit()
diff --git a/brutalmaze/utils.py b/brutalmaze/utils.py
index d198ef5..f972fc8 100644
--- a/brutalmaze/utils.py
+++ b/brutalmaze/utils.py
@@ -27,6 +27,11 @@ from pygame.gfxdraw import filled_polygon, aapolygon
 from .constants import *
 
 
+def round2(number):
+    """Round a number to an int."""
+    return int(round(number))
+
+
 def randsign():
     """Return either -1 or 1 (kind of) randomly."""
     return (pygame.time.get_ticks() & 1)*2 - 1
diff --git a/brutalmaze/weapons.py b/brutalmaze/weapons.py
new file mode 100644
index 0000000..108b042
--- /dev/null
+++ b/brutalmaze/weapons.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8 -*-
+# characters.py - module for weapon classes
+# This file is part of brutalmaze
+#
+# brutalmaze is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# brutalmaze is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with brutalmaze.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Copyright (C) 2017 Nguyễn Gia Phong
+
+__doc__ = 'brutalmaze module for weapon classes'
+
+from math import pi, cos, sin
+
+from pygame.time import get_ticks
+
+from .constants import *
+from .utils import randsign, regpoly, fill_aapolygon, pos, sign
+
+
+class Bullet:
+    """Object representing a bullet."""
+    def __init__(self, surface, x, y, angle, color):
+        self.surface = surface
+        self.x, self.y, self.angle, self.color = x, y, angle, color
+        self.fall_time = get_ticks() + BULLET_LIFETIME
+
+    def update(self, fps, distance):
+        """Update the bullet."""
+        s = distance * 8 / fps
+        self.x += s * cos(self.angle)
+        self.y += s * sin(self.angle)
+        hexagon = regpoly(6, distance // 4, self.angle, self.x, self.y)
+        fill_aapolygon(self.surface, hexagon, self.color)
+
+    def place(self, x, y, step):
+        """Move the bullet by (x, y) (in steps)."""
+        self.x += x * step
+        self.y += y * step