summary refs log tree commit diff homepage
diff options
context:
space:
mode:
-rw-r--r--brutalmaze/characters.py37
-rw-r--r--brutalmaze/constants.py3
-rw-r--r--brutalmaze/main.py2
-rw-r--r--brutalmaze/maze.py53
-rw-r--r--brutalmaze/misc.py (renamed from brutalmaze/utils.py)6
-rw-r--r--brutalmaze/soundfx/lose.oggbin18159 -> 23696 bytes
-rw-r--r--brutalmaze/soundfx/missed.oggbin0 -> 6159 bytes
-rw-r--r--brutalmaze/soundfx/music.oggbin1409097 -> 1422997 bytes
-rw-r--r--brutalmaze/soundfx/shot-enemy.oggbin7703 -> 15077 bytes
-rw-r--r--brutalmaze/soundfx/shot-hero.oggbin7066 -> 6450 bytes
-rw-r--r--brutalmaze/soundfx/slash-enemy.oggbin0 -> 8040 bytes
-rw-r--r--brutalmaze/soundfx/slash-hero.oggbin0 -> 9326 bytes
-rw-r--r--brutalmaze/weapons.py12
-rwxr-xr-xsetup.py2
14 files changed, 75 insertions, 40 deletions
diff --git a/brutalmaze/characters.py b/brutalmaze/characters.py
index 8a7ff8d..395514a 100644
--- a/brutalmaze/characters.py
+++ b/brutalmaze/characters.py
@@ -27,7 +27,7 @@ from sys import modules
 import pygame
 
 from .constants import *
-from .utils import sign, cosin, randsign, regpoly, fill_aapolygon, choices
+from .misc import sign, cosin, randsign, regpoly, fill_aapolygon, choices, play
 from .weapons import Bullet
 
 
@@ -61,12 +61,10 @@ class Hero:
         self.spin_speed = fps / HERO_HP
         self.spin_queue = self.wound = 0.0
 
-        self.sfx_shot = pygame.mixer.Sound(SFX_SHOT_HERO)
-
     def update(self, fps):
         """Update the hero."""
         if self.dead:
-            self.spin_queue = 0
+            self.spin_queue = 0.0
             return
         old_speed, time = self.spin_speed, pygame.time.get_ticks()
         self.spin_speed = fps / (HERO_HP-self.wound**0.5)
@@ -111,6 +109,7 @@ class Enemy:
         spin_speed (float): speed of spinning (in frames per slash)
         spin_queue (float): frames left to finish spinning
         wound (float): amount of wound
+        sfx_slash (Sound): sound effect indicating close-range attack damage
     """
     def __init__(self, maze, x, y, color):
         self.maze = maze
@@ -125,7 +124,7 @@ class Enemy:
         self.spin_speed = self.maze.fps / ENEMY_HP
         self.spin_queue = self.wound = 0.0
 
-        self.sfx_shot = pygame.mixer.Sound(SFX_SHOT_ENEMY)
+        self.sfx_slash = pygame.mixer.Sound(SFX_SLASH_HERO)
 
     def get_pos(self):
         """Return coordinate of the center of the enemy."""
@@ -200,15 +199,17 @@ class Enemy:
                 return True
         return False
 
-    def slash(self):
+    def get_slash(self):
         """Return the enemy's close-range damage."""
-        if self.spin_queue:
-            d = self.maze.slashd - self.maze.get_distance(*self.get_pos())
-            wound = d / self.maze.hero.R / self.spin_speed
-            if wound >= 0:
-                self.maze.hit(wound, self.color, per_frame=True)
-                return wound
-        return 0.0
+        d = self.maze.slashd - self.maze.get_distance(*self.get_pos())
+        wound = d / self.maze.hero.R
+        return wound if wound > 0 else 0.0
+
+    def slash(self):
+        """Return the enemy's close-range damage per frame."""
+        wound = self.get_slash() / self.spin_speed
+        if self.spin_queue: self.maze.hit_hero(wound, self.color)
+        return wound
 
     def draw(self):
         """Draw the enemy."""
@@ -224,6 +225,7 @@ class Enemy:
             self.spin_queue *= self.spin_speed / tmp
             if not self.spin_queue and not self.fire() and not self.move():
                 self.spin_queue = randsign() * self.spin_speed
+                play(self.sfx_slash, self.get_slash())
             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)
@@ -231,12 +233,9 @@ class Enemy:
                 self.angle, self.spin_queue = pi / 4, 0.0
         self.draw()
 
-    def hit(self, wound, per_frame=False):
+    def hit(self, wound):
         """Handle the enemy when it's attacked."""
         self.wound += wound
-        if not per_frame:
-            self.sfx_shot.set_volume(wound)
-            self.sfx_shot.play()
 
     def die(self):
         """Handle the enemy's death."""
@@ -268,10 +267,10 @@ class Chameleon(Enemy):
         if not self.awake or pygame.time.get_ticks() <= self.visible:
             Enemy.draw(self)
 
-    def hit(self, wound, per_frame=False):
+    def hit(self, wound):
         """Handle the Chameleon when it's attacked."""
         self.visible = pygame.time.get_ticks() + 1000//ENEMY_SPEED
-        Enemy.hit(self, wound, per_frame)
+        Enemy.hit(self, wound)
 
 
 class Plum(Enemy):
diff --git a/brutalmaze/constants.py b/brutalmaze/constants.py
index 6c896e1..2f756e6 100644
--- a/brutalmaze/constants.py
+++ b/brutalmaze/constants.py
@@ -25,8 +25,11 @@ from pkg_resources import resource_filename
 
 ICON = image.load(resource_filename('brutalmaze', 'icon.png'))
 MUSIC = resource_filename('brutalmaze', 'soundfx/music.ogg')
+SFX_SLASH_ENEMY = resource_filename('brutalmaze', 'soundfx/slash-enemy.ogg')
+SFX_SLASH_HERO = resource_filename('brutalmaze', 'soundfx/slash-hero.ogg')
 SFX_SHOT_ENEMY = resource_filename('brutalmaze', 'soundfx/shot-enemy.ogg')
 SFX_SHOT_HERO = resource_filename('brutalmaze', 'soundfx/shot-hero.ogg')
+SFX_MISSED = resource_filename('brutalmaze', 'soundfx/missed.ogg')
 SFX_LOSE = resource_filename('brutalmaze', 'soundfx/lose.ogg')
 
 UP = (K_UP, K_w)
diff --git a/brutalmaze/main.py b/brutalmaze/main.py
index 291981a..c3db5e9 100644
--- a/brutalmaze/main.py
+++ b/brutalmaze/main.py
@@ -24,7 +24,7 @@ from pygame.locals import *
 
 from .constants import *
 from .maze import Maze
-from .utils import some
+from .misc import some
 
 
 def main():
diff --git a/brutalmaze/maze.py b/brutalmaze/maze.py
index 8597055..0ca75ae 100644
--- a/brutalmaze/maze.py
+++ b/brutalmaze/maze.py
@@ -25,10 +25,12 @@ from random import choice, getrandbits, uniform
 
 import pygame
 from pygame import RESIZABLE
+from pygame.mixer import Sound, set_num_channels
+from pygame.time import get_ticks
 
 from .characters import Hero, new_enemy
 from .constants import *
-from .utils import round2, sign, regpoly, fill_aapolygon
+from .misc import round2, sign, regpoly, fill_aapolygon, play
 from .weapons import Bullet
 
 
@@ -73,7 +75,11 @@ class Maze:
         enemies (list of Enemy): alive enemies
         hero (Hero): the hero
         next_move (int): the tick that the hero gets mobilized
+        next_slashfx (int): the tick to play next slash effect of the hero
         slashd (float): minimum distance for slashes to be effective
+        sfx_slash (Sound): sound effect indicating an enemy get slashed
+        sfx_shot (Sound): sound effect indicating an enemy get shot
+        sfx_lose (Sound): sound effect to be played when you lose
     """
     def __init__(self, size, fps):
         self.w, self.h = size
@@ -96,7 +102,12 @@ class Maze:
         self.add_enemy()
         self.hero = Hero(self.surface, fps)
         self.map[MIDDLE][MIDDLE] = HERO
-        self.next_move, self.slashd = 0, self.hero.R + self.distance/SQRT2
+        self.next_move = self.next_slashfx = 0
+        self.slashd = self.hero.R + self.distance/SQRT2
+
+        self.sfx_slash = Sound(SFX_SLASH_ENEMY)
+        self.sfx_shot = Sound(SFX_SHOT_ENEMY)
+        self.sfx_lose = Sound(SFX_LOSE)
 
     def add_enemy(self):
         """Add enough enemies."""
@@ -104,7 +115,8 @@ class Maze:
                  if self.map[i][j] == WALL]
         plums = [e for e in self.enemies if e.color == 'Plum' and e.awake]
         plum = choice(plums) if plums else None
-        while walls and len(self.enemies) < log(self.score, INIT_SCORE):
+        num = log(self.score, INIT_SCORE)
+        while walls and len(self.enemies) < num:
             x, y = choice(walls)
             if all(self.map[x + a][y + b] == WALL for a, b in ADJACENT_GRIDS):
                 continue
@@ -114,6 +126,7 @@ class Maze:
                 walls.remove((x, y))
             else:
                 self.map[x][y] = WALL
+        set_num_channels(int(num * 3))
 
     def get_pos(self, x, y):
         """Return coordinate of the center of the grid (x, y)."""
@@ -183,14 +196,11 @@ class Maze:
         """
         return ((self.x-x)**2 + (self.y-y)**2)**0.5
 
-    def hit(self, wound, color, per_frame=False):
+    def hit_hero(self, wound, color):
         """Handle the hero when he loses HP."""
-        if not per_frame:
-            self.hero.sfx_shot.set_volume(wound)
-            self.hero.sfx_shot.play()
         fx = (uniform(0, sum(self.enemy_weights.values()))
               < self.enemy_weights[color])
-        time = pygame.time.get_ticks()
+        time = get_ticks()
         if (color == 'Butter' or color == 'ScarletRed') and fx:
             self.hero.wound += wound * 2.5
         elif color == 'Orange' and fx:
@@ -207,12 +217,15 @@ class Maze:
         """Handle close-range attacks."""
         for enemy in self.enemies: enemy.slash()
         if not self.hero.spin_queue: return
-        unit, killist = self.distance/SQRT2 * self.hero.spin_speed, []
+        killist = []
         for i, enemy in enumerate(self.enemies):
-            x, y = enemy.get_pos()
-            d = self.get_distance(x, y)
-            if d <= self.slashd:
-                enemy.hit((self.slashd-d) / unit, per_frame=True)
+            d = self.slashd - self.get_distance(*enemy.get_pos())
+            if d > 0:
+                wound, time = d * SQRT2 / self.distance, get_ticks()
+                if time >= self.next_slashfx:
+                    play(self.sfx_slash, wound)
+                    self.next_slashfx = time + ATTACK_SPEED
+                enemy.hit(wound / self.hero.spin_speed)
                 if enemy.wound >= ENEMY_HP:
                     self.score += enemy.wound
                     enemy.die()
@@ -222,7 +235,7 @@ class Maze:
 
     def track_bullets(self):
         """Handle the bullets."""
-        fallen, time = [], pygame.time.get_ticks()
+        fallen, time = [], get_ticks()
         if (self.hero.firing and not self.hero.slashing
             and time >= self.hero.next_strike):
             self.hero.next_strike = time + ATTACK_SPEED
@@ -240,6 +253,7 @@ class Maze:
                     fallen.append(i)
                     continue
                 for j, enemy in enumerate(self.enemies):
+                    if not enemy.awake: continue
                     x, y = enemy.get_pos()
                     if bullet.get_distance(x, y) < self.distance:
                         enemy.hit(wound)
@@ -247,10 +261,15 @@ class Maze:
                             self.score += enemy.wound
                             enemy.die()
                             self.enemies.pop(j)
+                        play(self.sfx_shot, wound)
                         fallen.append(i)
                         break
             elif bullet.get_distance(self.x, self.y) < self.distance:
-                if not self.hero.spin_queue: self.hit(wound, bullet.color)
+                if self.hero.spin_queue:
+                    play(bullet.sfx_missed, wound)
+                else:
+                    self.hit_hero(wound, bullet.color)
+                    play(bullet.sfx_hit, wound)
                 fallen.append(i)
         for i in reversed(fallen): self.bullets.pop(i)
 
@@ -298,7 +317,7 @@ class Maze:
 
     def move(self, x, y, fps):
         """Command the hero to move faster in the given direction."""
-        stunned = pygame.time.get_ticks() < self.next_move
+        stunned = get_ticks() < self.next_move
         velocity = self.distance * HERO_SPEED / fps
         accel = velocity * HERO_SPEED / fps
         if stunned or not x:
@@ -344,4 +363,4 @@ class Maze:
         self.hero.dead = True
         self.hero.slashing = self.hero.firing = False
         self.vx = self.vy = 0.0
-        pygame.mixer.Sound(SFX_LOSE).play()
+        self.sfx_lose.play()
diff --git a/brutalmaze/utils.py b/brutalmaze/misc.py
index 387fea3..431b5e6 100644
--- a/brutalmaze/utils.py
+++ b/brutalmaze/misc.py
@@ -81,3 +81,9 @@ def choices(d):
     num = uniform(0, cum_weights[-1])
     for i, w in enumerate(cum_weights):
         if num <= w: return population[i]
+
+
+def play(sound, volume):
+    """Play a pygame.mixer.Sound at the given volume."""
+    sound.set_volume(volume)
+    sound.play()
diff --git a/brutalmaze/soundfx/lose.ogg b/brutalmaze/soundfx/lose.ogg
index a71cc0f..4baa6e9 100644
--- a/brutalmaze/soundfx/lose.ogg
+++ b/brutalmaze/soundfx/lose.ogg
Binary files differdiff --git a/brutalmaze/soundfx/missed.ogg b/brutalmaze/soundfx/missed.ogg
new file mode 100644
index 0000000..844abc0
--- /dev/null
+++ b/brutalmaze/soundfx/missed.ogg
Binary files differdiff --git a/brutalmaze/soundfx/music.ogg b/brutalmaze/soundfx/music.ogg
index 9e764bf..73e2157 100644
--- a/brutalmaze/soundfx/music.ogg
+++ b/brutalmaze/soundfx/music.ogg
Binary files differdiff --git a/brutalmaze/soundfx/shot-enemy.ogg b/brutalmaze/soundfx/shot-enemy.ogg
index adc80cf..3c5811a 100644
--- a/brutalmaze/soundfx/shot-enemy.ogg
+++ b/brutalmaze/soundfx/shot-enemy.ogg
Binary files differdiff --git a/brutalmaze/soundfx/shot-hero.ogg b/brutalmaze/soundfx/shot-hero.ogg
index 94bf4a1..2c2256c 100644
--- a/brutalmaze/soundfx/shot-hero.ogg
+++ b/brutalmaze/soundfx/shot-hero.ogg
Binary files differdiff --git a/brutalmaze/soundfx/slash-enemy.ogg b/brutalmaze/soundfx/slash-enemy.ogg
new file mode 100644
index 0000000..73848d9
--- /dev/null
+++ b/brutalmaze/soundfx/slash-enemy.ogg
Binary files differdiff --git a/brutalmaze/soundfx/slash-hero.ogg b/brutalmaze/soundfx/slash-hero.ogg
new file mode 100644
index 0000000..aa3a3ce
--- /dev/null
+++ b/brutalmaze/soundfx/slash-hero.ogg
Binary files differdiff --git a/brutalmaze/weapons.py b/brutalmaze/weapons.py
index 8f1ee72..c0c860a 100644
--- a/brutalmaze/weapons.py
+++ b/brutalmaze/weapons.py
@@ -22,9 +22,10 @@ __doc__ = 'brutalmaze module for weapon classes'
 from math import cos, sin
 
 from pygame.time import get_ticks
+from pygame.mixer import Sound
 
-from .constants import BULLET_LIFETIME, BULLET_SPEED, ENEMY_HP, TANGO
-from .utils import regpoly, fill_aapolygon
+from .constants import *
+from .misc import regpoly, fill_aapolygon
 
 
 class Bullet:
@@ -36,11 +37,18 @@ class Bullet:
         angle (float): angle of the direction the bullet pointing (in radians)
         color (str): bullet's color name
         fall_time (int): the tick that the bullet will fall down
+        sfx_hit (Sound): sound effect indicating the bullet hits the target
+        sfx_missed (Sound): sound effect indicating the bullet hits the target
     """
     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
+        # Sound effects of bullets shot by hero are stored in Maze to avoid
+        # unnecessary duplication
+        if color != 'Aluminium':
+            self.sfx_hit = Sound(SFX_SHOT_HERO)
+            self.sfx_missed = Sound(SFX_MISSED)
 
     def update(self, fps, distance):
         """Update the bullet."""
diff --git a/setup.py b/setup.py
index 364a7c0..f7a480a 100755
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@ with open('README.rst') as f:
 
 setup(
     name='brutalmaze',
-    version='0.2.2',
+    version='0.3.0',
     description='A hash and slash game with fast-paced action and a minimalist art style',
     long_description=long_description,
     url='https://github.com/McSinyx/brutalmaze',