From 7fe731137107b72704bc661d60a4da1f4674c9d9 Mon Sep 17 00:00:00 2001 From: Nguyễn Gia Phong Date: Sun, 21 Jan 2018 20:38:51 +0700 Subject: Finish adding sound effects (#2) --- brutalmaze/characters.py | 37 ++++++++------- brutalmaze/constants.py | 3 ++ brutalmaze/main.py | 2 +- brutalmaze/maze.py | 53 +++++++++++++++------- brutalmaze/misc.py | 89 +++++++++++++++++++++++++++++++++++++ brutalmaze/soundfx/lose.ogg | Bin 18159 -> 23696 bytes brutalmaze/soundfx/missed.ogg | Bin 0 -> 6159 bytes brutalmaze/soundfx/music.ogg | Bin 1409097 -> 1422997 bytes brutalmaze/soundfx/shot-enemy.ogg | Bin 7703 -> 15077 bytes brutalmaze/soundfx/shot-hero.ogg | Bin 7066 -> 6450 bytes brutalmaze/soundfx/slash-enemy.ogg | Bin 0 -> 8040 bytes brutalmaze/soundfx/slash-hero.ogg | Bin 0 -> 9326 bytes brutalmaze/utils.py | 83 ---------------------------------- brutalmaze/weapons.py | 12 ++++- setup.py | 2 +- 15 files changed, 158 insertions(+), 123 deletions(-) create mode 100644 brutalmaze/misc.py create mode 100644 brutalmaze/soundfx/missed.ogg create mode 100644 brutalmaze/soundfx/slash-enemy.ogg create mode 100644 brutalmaze/soundfx/slash-hero.ogg delete mode 100644 brutalmaze/utils.py 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/misc.py b/brutalmaze/misc.py new file mode 100644 index 0000000..431b5e6 --- /dev/null +++ b/brutalmaze/misc.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# characters.py - module for shared functions and macros +# 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 . +# +# Copyright (C) 2017 Nguyễn Gia Phong + +__doc__ = 'brutalmaze module for hero and enemy classes' + +from functools import reduce +from math import cos, sin, pi +from operator import or_ +from random import uniform + +import pygame +from pygame.gfxdraw import filled_polygon, aapolygon + +from .constants import MIDDLE + + +def some(a, keys): + """Return True if there is a key k in keys that bool(a[k]) is True.""" + return bool(reduce(or_, (a[k] for k in keys))) + + +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 + + +def regpoly(n, R, r, x, y): + """Return the pointlist of the regular polygon with n sides, + circumradius of R, the center point I(x, y) and one point A make the + vector IA with angle r (in radians). + """ + r %= pi * 2 + angles = [r + pi*2*side/n for side in range(n)] + return [(x + R*cos(angle), y + R*sin(angle)) for angle in angles] + + +def fill_aapolygon(surface, points, color): + """Draw a filled polygon with anti aliased edges onto a surface.""" + aapolygon(surface, points, color) + filled_polygon(surface, points, color) + + +def sign(n): + """Return the sign of number 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) + + +def choices(d): + """Choose a random key from a dict which has values being relative + weights of the coresponding keys. + """ + population, weights = tuple(d.keys()), tuple(d.values()) + cum_weights = [weights[0]] + for weight in weights[1:]: cum_weights.append(cum_weights[-1] + weight) + 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 Binary files a/brutalmaze/soundfx/lose.ogg and b/brutalmaze/soundfx/lose.ogg differ diff --git a/brutalmaze/soundfx/missed.ogg b/brutalmaze/soundfx/missed.ogg new file mode 100644 index 0000000..844abc0 Binary files /dev/null and b/brutalmaze/soundfx/missed.ogg differ diff --git a/brutalmaze/soundfx/music.ogg b/brutalmaze/soundfx/music.ogg index 9e764bf..73e2157 100644 Binary files a/brutalmaze/soundfx/music.ogg and b/brutalmaze/soundfx/music.ogg differ diff --git a/brutalmaze/soundfx/shot-enemy.ogg b/brutalmaze/soundfx/shot-enemy.ogg index adc80cf..3c5811a 100644 Binary files a/brutalmaze/soundfx/shot-enemy.ogg and b/brutalmaze/soundfx/shot-enemy.ogg differ diff --git a/brutalmaze/soundfx/shot-hero.ogg b/brutalmaze/soundfx/shot-hero.ogg index 94bf4a1..2c2256c 100644 Binary files a/brutalmaze/soundfx/shot-hero.ogg and b/brutalmaze/soundfx/shot-hero.ogg differ diff --git a/brutalmaze/soundfx/slash-enemy.ogg b/brutalmaze/soundfx/slash-enemy.ogg new file mode 100644 index 0000000..73848d9 Binary files /dev/null and b/brutalmaze/soundfx/slash-enemy.ogg differ diff --git a/brutalmaze/soundfx/slash-hero.ogg b/brutalmaze/soundfx/slash-hero.ogg new file mode 100644 index 0000000..aa3a3ce Binary files /dev/null and b/brutalmaze/soundfx/slash-hero.ogg differ diff --git a/brutalmaze/utils.py b/brutalmaze/utils.py deleted file mode 100644 index 387fea3..0000000 --- a/brutalmaze/utils.py +++ /dev/null @@ -1,83 +0,0 @@ -# -*- coding: utf-8 -*- -# characters.py - module for shared functions and macros -# 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 . -# -# Copyright (C) 2017 Nguyễn Gia Phong - -__doc__ = 'brutalmaze module for hero and enemy classes' - -from functools import reduce -from math import cos, sin, pi -from operator import or_ -from random import uniform - -import pygame -from pygame.gfxdraw import filled_polygon, aapolygon - -from .constants import MIDDLE - - -def some(a, keys): - """Return True if there is a key k in keys that bool(a[k]) is True.""" - return bool(reduce(or_, (a[k] for k in keys))) - - -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 - - -def regpoly(n, R, r, x, y): - """Return the pointlist of the regular polygon with n sides, - circumradius of R, the center point I(x, y) and one point A make the - vector IA with angle r (in radians). - """ - r %= pi * 2 - angles = [r + pi*2*side/n for side in range(n)] - return [(x + R*cos(angle), y + R*sin(angle)) for angle in angles] - - -def fill_aapolygon(surface, points, color): - """Draw a filled polygon with anti aliased edges onto a surface.""" - aapolygon(surface, points, color) - filled_polygon(surface, points, color) - - -def sign(n): - """Return the sign of number 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) - - -def choices(d): - """Choose a random key from a dict which has values being relative - weights of the coresponding keys. - """ - population, weights = tuple(d.keys()), tuple(d.values()) - cum_weights = [weights[0]] - for weight in weights[1:]: cum_weights.append(cum_weights[-1] + weight) - num = uniform(0, cum_weights[-1]) - for i, w in enumerate(cum_weights): - if num <= w: return population[i] diff --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', -- cgit 1.4.1