diff options
-rw-r--r-- | brutalmaze/characters.py | 17 | ||||
-rw-r--r-- | brutalmaze/constants.py | 30 | ||||
-rw-r--r-- | brutalmaze/main.py | 54 | ||||
-rw-r--r-- | brutalmaze/maze.py | 42 | ||||
-rw-r--r-- | brutalmaze/misc.py | 1 | ||||
-rw-r--r-- | brutalmaze/settings.ini | 8 | ||||
-rw-r--r-- | brutalmaze/weapons.py | 20 | ||||
m--------- | wiki | 0 |
8 files changed, 106 insertions, 66 deletions
diff --git a/brutalmaze/characters.py b/brutalmaze/characters.py index faa67c4..6201569 100644 --- a/brutalmaze/characters.py +++ b/brutalmaze/characters.py @@ -24,7 +24,6 @@ from random import choice, randrange, shuffle from sys import modules import pygame -from pygame.mixer import Sound from pygame.time import get_ticks from .constants import * @@ -50,7 +49,7 @@ class Hero: spin_speed (float): speed of spinning (in frames per slash) spin_queue (float): frames left to finish spinning wound (float): amount of wound - sfx_heart (Sound): heart beat sound effect + sfx_heart (pygame.mixer.Sound): heart beat sound effect """ def __init__(self, surface, fps): self.surface = surface @@ -64,7 +63,7 @@ class Hero: self.spin_speed = fps / HERO_HP self.spin_queue = self.wound = 0.0 - self.sfx_heart = Sound(SFX_HEART) + self.sfx_heart = SFX_HEART def update(self, fps): """Update the hero.""" @@ -92,6 +91,9 @@ class Hero: x, y = pygame.mouse.get_pos() self.angle = atan2(y - self.y, x - self.x) self.spin_queue = 0.0 + + def draw(self): + """Draw the hero.""" trigon = regpoly(3, self.R, self.angle, self.x, self.y) fill_aapolygon(self.surface, trigon, self.color[int(self.wound)]) @@ -117,7 +119,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 + sfx_slash (pygame.mixer.Sound): sound effect of slashed hero """ def __init__(self, maze, x, y, color): self.maze = maze @@ -132,7 +134,7 @@ class Enemy: self.spin_speed = self.maze.fps / ENEMY_HP self.spin_queue = self.wound = 0.0 - self.sfx_slash = Sound(SFX_SLASH_HERO) + self.sfx_slash = SFX_SLASH_HERO def get_pos(self): """Return coordinate of the center of the enemy.""" @@ -238,6 +240,7 @@ class Enemy: def draw(self): """Draw the enemy.""" + if get_ticks() < self.maze.next_move and not self.awake: return radious = self.maze.distance/SQRT2 - self.awake*2 square = regpoly(4, radious, self.angle, *self.get_pos()) color = TANGO[self.color][int(self.wound)] if self.awake else FG_COLOR @@ -257,7 +260,6 @@ class Enemy: self.spin_queue -= sign(self.spin_queue) else: self.angle, self.spin_queue = pi / 4, 0.0 - if self.awake or get_ticks() >= self.maze.next_move: self.draw() def hit(self, wound): """Handle the enemy when it's attacked.""" @@ -290,8 +292,7 @@ class Chameleon(Enemy): def draw(self): """Draw the Chameleon.""" - if (not self.awake or self.spin_queue - or get_ticks() < max(self.visible, self.maze.next_move)): + if not self.awake or get_ticks() < self.visible or self.spin_queue: Enemy.draw(self) def hit(self, wound): diff --git a/brutalmaze/constants.py b/brutalmaze/constants.py index 0792808..3bd34b1 100644 --- a/brutalmaze/constants.py +++ b/brutalmaze/constants.py @@ -19,21 +19,25 @@ __doc__ = 'brutalmaze module for shared constants' -from pkg_resources import resource_filename -from pygame import image +from pkg_resources import resource_filename as pkg_file +import pygame from pygame.mixer import Sound -SETTINGS = resource_filename('brutalmaze', 'settings.ini') -ICON = image.load(resource_filename('brutalmaze', 'icon.png')) -MUSIC = resource_filename('brutalmaze', 'soundfx/music.ogg') -SFX_SPAWN = resource_filename('brutalmaze', 'soundfx/spawn.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_HEART = resource_filename('brutalmaze', 'soundfx/heart.ogg') -SFX_LOSE = resource_filename('brutalmaze', 'soundfx/lose.ogg') +SETTINGS = pkg_file('brutalmaze', 'settings.ini') +ICON = pygame.image.load(pkg_file('brutalmaze', 'icon.png')) +MUSIC = pkg_file('brutalmaze', 'soundfx/music.ogg') + +mixer = pygame.mixer.get_init() +if mixer is None: pygame.mixer.init(frequency=44100) +SFX_SPAWN = Sound(pkg_file('brutalmaze', 'soundfx/spawn.ogg')) +SFX_SLASH_ENEMY = Sound(pkg_file('brutalmaze', 'soundfx/slash-enemy.ogg')) +SFX_SLASH_HERO = Sound(pkg_file('brutalmaze', 'soundfx/slash-hero.ogg')) +SFX_SHOT_ENEMY = Sound(pkg_file('brutalmaze', 'soundfx/shot-enemy.ogg')) +SFX_SHOT_HERO = Sound(pkg_file('brutalmaze', 'soundfx/shot-hero.ogg')) +SFX_MISSED = Sound(pkg_file('brutalmaze', 'soundfx/missed.ogg')) +SFX_HEART = Sound(pkg_file('brutalmaze', 'soundfx/heart.ogg')) +SFX_LOSE = Sound(pkg_file('brutalmaze', 'soundfx/lose.ogg')) +if mixer is None: pygame.mixer.quit() SQRT2 = 2 ** 0.5 INIT_SCORE = 5**0.5/2 + 0.5 # golden mean diff --git a/brutalmaze/main.py b/brutalmaze/main.py index c84b92f..39dca3b 100644 --- a/brutalmaze/main.py +++ b/brutalmaze/main.py @@ -35,7 +35,7 @@ from pygame import DOUBLEBUF, KEYDOWN, OPENGL, QUIT, RESIZABLE, VIDEORESIZE from pygame.time import Clock, get_ticks from appdirs import AppDirs -from .constants import * +from .constants import SETTINGS, ICON, MUSIC, HERO_SPEED from .maze import Maze from .misc import sign @@ -44,7 +44,8 @@ class ConfigReader: """Object reading and processing INI configuration file for Brutal Maze. """ - CONTROL_ALIASES = (('New game', 'new'), ('Pause', 'pause'), + CONTROL_ALIASES = (('New game', 'new'), ('Toggle pause', 'pause'), + ('Toggle mute', 'mute'), ('Move left', 'left'), ('Move right', 'right'), ('Move up', 'up'), ('Move down', 'down'), ('Long-range attack', 'shot'), @@ -57,12 +58,14 @@ class ConfigReader: self.config.read(SETTINGS) # default configuration self.config.read(filenames) - def parse_graphics(self): - """Parse graphics configurations.""" + def parse_output(self): + """Parse graphics and sound configurations.""" self.size = (self.config.getint('Graphics', 'Screen width'), self.config.getint('Graphics', 'Screen height')) self.opengl = self.config.getboolean('Graphics', 'OpenGL') self.max_fps = self.config.getint('Graphics', 'Maximum FPS') + self.muted = self.config.getboolean('Sound', 'Muted') + self.musicvol = self.config.getfloat('Sound', 'Music volume') def parse_control(self): """Parse control configurations.""" @@ -84,27 +87,31 @@ class ConfigReader: def read_args(self, arguments): """Read and parse a ArgumentParser.Namespace.""" - if arguments.size is not None: self.size = arguments.size - if arguments.opengl is not None: self.opengl = arguments.opengl - if arguments.max_fps is not None: self.max_fps = arguments.max_fps + for option in 'size', 'opengl', 'max_fps', 'muted', 'musicvol': + value = getattr(arguments, option) + if value is not None: setattr(self, option, value) class Game: """Object handling main loop and IO.""" - def __init__(self, size, scrtype, max_fps, key, mouse): + def __init__(self, size, scrtype, max_fps, muted, musicvol, key, mouse): pygame.mixer.pre_init(frequency=44100) pygame.init() - pygame.mixer.music.load(MUSIC) - pygame.mixer.music.play(-1) + if muted: + pygame.mixer.quit() + else: + pygame.mixer.music.load(MUSIC) + pygame.mixer.music.set_volume(musicvol) + pygame.mixer.music.play(-1) pygame.display.set_icon(ICON) pygame.fastevent.init() - self.clock = Clock() # self.fps is a float to make sure floordiv won't be used in Python 2 self.max_fps, self.fps = max_fps, float(max_fps) + self.musicvol = musicvol self.key, self.mouse = key, mouse self.maze = Maze(max_fps, size, scrtype) self.hero = self.maze.hero - self.paused = False + self.clock, self.paused = Clock(), False def __enter__(self): return self @@ -145,6 +152,14 @@ class Game: self.maze.__init__(self.fps) elif event.key == self.key['pause'] and not self.hero.dead: self.paused ^= True + elif event.key == self.key['mute']: + if pygame.mixer.get_init() is None: + pygame.mixer.init(frequency=44100) + pygame.mixer.music.load(MUSIC) + pygame.mixer.music.set_volume(self.musicvol) + pygame.mixer.music.play(-1) + else: + pygame.mixer.quit() if not self.hero.dead: keys = pygame.key.get_pressed() @@ -181,7 +196,7 @@ def main(): parents.append(dirs.user_config_dir) filenames = [join(parent, 'settings.ini') for parent in parents] config = ConfigReader(filenames) - config.parse_graphics() + config.parse_output() # Parse command-line arguments parser = ArgumentParser(formatter_class=RawTextHelpFormatter) @@ -206,6 +221,14 @@ def main(): parser.add_argument( '-f', '--max-fps', type=int, metavar='FPS', help='the desired maximum FPS (fallback: {})'.format(config.max_fps)) + parser.add_argument( + '--mute', '-m', action='store_true', default=None, + help='mute all sounds (fallback: {})'.format(config.muted)) + parser.add_argument('--unmute', action='store_false', dest='muted', + help='unmute sound') + parser.add_argument( + '--music-volume', type=float, metavar='VOL', dest='musicvol', + help='between 0.0 and 1.0 (fallback: {})'.format(config.musicvol)) args = parser.parse_args() if args.defaultcfg is not None: with open(SETTINGS) as settings: args.defaultcfg.write(settings.read()) @@ -215,10 +238,11 @@ def main(): # Manipulate config if args.config: config.config.read(args.config) config.read_args(args) - config.parse_graphics() + config.parse_output() config.parse_control() # Main loop scrtype = (config.opengl and DOUBLEBUF|OPENGL) | RESIZABLE - with Game(config.size, scrtype, config.max_fps, config.key, config.mouse) as game: + with Game(config.size, scrtype, config.max_fps, config.muted, + config.musicvol, config.key, config.mouse) as game: while game.loop(): pass diff --git a/brutalmaze/maze.py b/brutalmaze/maze.py index 0f1f5de..66c4c81 100644 --- a/brutalmaze/maze.py +++ b/brutalmaze/maze.py @@ -25,7 +25,6 @@ from random import choice, getrandbits, uniform import pygame from pygame import RESIZABLE -from pygame.mixer import Sound from pygame.time import get_ticks from .characters import Hero, new_enemy @@ -76,9 +75,8 @@ class Maze: 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 + sfx_slash (pygame.mixer.Sound): sound effect of slashed enemy + sfx_lose (pygame.mixer.Sound): sound effect to be played when you lose """ def __init__(self, fps, size=None, scrtype=None): self.fps = fps @@ -109,10 +107,9 @@ class Maze: self.next_move = self.next_slashfx = 0 self.slashd = self.hero.R + self.distance/SQRT2 - self.sfx_spawn = Sound(SFX_SPAWN) - self.sfx_slash = Sound(SFX_SLASH_ENEMY) - self.sfx_shot = Sound(SFX_SHOT_ENEMY) - self.sfx_lose = Sound(SFX_LOSE) + self.sfx_spawn = SFX_SPAWN + self.sfx_slash = SFX_SLASH_ENEMY + self.sfx_lose = SFX_LOSE def add_enemy(self): """Add enough enemies.""" @@ -140,13 +137,21 @@ class Maze: def draw(self): """Draw the maze.""" self.surface.fill(BG_COLOR) - if get_ticks() < self.next_move: return - for i in self.rangex: - for j in self.rangey: - if self.map[i][j] != WALL: continue - x, y = self.get_pos(i, j) - square = regpoly(4, self.distance / SQRT2, pi / 4, x, y) - fill_aapolygon(self.surface, square, FG_COLOR) + if get_ticks() >= self.next_move: + for i in self.rangex: + for j in self.rangey: + if self.map[i][j] != WALL: continue + x, y = self.get_pos(i, j) + square = regpoly(4, self.distance / SQRT2, pi / 4, x, y) + fill_aapolygon(self.surface, square, FG_COLOR) + + for enemy in self.enemies: enemy.draw() + self.hero.draw() + bullet_radius = self.distance / 4 + for bullet in self.bullets: bullet.draw(bullet_radius) + pygame.display.flip() + pygame.display.set_caption('Brutal Maze - Score: {}'.format( + int(self.score - INIT_SCORE))) def rotate(self): """Rotate the maze if needed.""" @@ -266,7 +271,7 @@ class Maze: self.score += enemy.wound enemy.die() self.enemies.pop(j) - play(self.sfx_shot, wound, bullet.angle) + play(bullet.sfx_hit, wound, bullet.angle) fallen.append(i) break elif bullet.get_distance(self.x, self.y) < self.distance: @@ -310,15 +315,12 @@ class Maze: for enemy in self.enemies: enemy.wake() for bullet in self.bullets: bullet.place(dx, dy) - self.draw() for enemy in self.enemies: enemy.update() if not self.hero.dead: self.hero.update(fps) self.slash() self.track_bullets() - pygame.display.flip() - pygame.display.set_caption('Brutal Maze - Score: {}'.format( - int(self.score - INIT_SCORE))) + self.draw() def resize(self, size): """Resize the maze.""" diff --git a/brutalmaze/misc.py b/brutalmaze/misc.py index ef04502..af5a736 100644 --- a/brutalmaze/misc.py +++ b/brutalmaze/misc.py @@ -76,6 +76,7 @@ def choices(d): def play(sound, volume=1.0, angle=None): """Play a pygame.mixer.Sound at the given volume.""" + if pygame.mixer.get_init() is None: return if pygame.mixer.find_channel() is None: pygame.mixer.set_num_channels(pygame.mixer.get_num_channels() + 1) diff --git a/brutalmaze/settings.ini b/brutalmaze/settings.ini index 00a071f..2baeedd 100644 --- a/brutalmaze/settings.ini +++ b/brutalmaze/settings.ini @@ -6,6 +6,11 @@ OpenGL: no # FPS should not be greater than refresh rate. Maximum FPS: 60 +[Sound] +Muted: no +# Volume must be between 0.0 and 1.0 +Music volume: 1.0 + [Control] # Input values should be either from Mouse1 to Mouse3 or a keyboard key # and they are case-insensitively read. @@ -13,7 +18,8 @@ Maximum FPS: 60 # http://www.pygame.org/docs/ref/key.html # Key combinations are not supported. New game: F2 -Pause: p +Toggle pause: p +Toggle mute: m Move left: Left Move right: Right Move up: Up diff --git a/brutalmaze/weapons.py b/brutalmaze/weapons.py index c0c860a..3979d30 100644 --- a/brutalmaze/weapons.py +++ b/brutalmaze/weapons.py @@ -22,7 +22,6 @@ __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 * from .misc import regpoly, fill_aapolygon @@ -37,25 +36,28 @@ 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 + sfx_hit (pygame.mixer.Sound): sound effect indicating target was hit + sfx_missed (pygame.mixer.Sound): sound effect indicating a miss shot """ 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) + if color == 'Aluminium': + self.sfx_hit = SFX_SHOT_ENEMY + else: + self.sfx_hit = SFX_SHOT_HERO + self.sfx_missed = SFX_MISSED def update(self, fps, distance): """Update the bullet.""" s = distance * BULLET_SPEED / fps self.x += s * cos(self.angle) self.y += s * sin(self.angle) - pentagon = regpoly(5, distance // 4, self.angle, self.x, self.y) + + def draw(self, radius): + """Draw the bullet.""" + pentagon = regpoly(5, radius, self.angle, self.x, self.y) value = int((1-(self.fall_time-get_ticks())/BULLET_LIFETIME)*ENEMY_HP) try: fill_aapolygon(self.surface, pentagon, TANGO[self.color][value]) diff --git a/wiki b/wiki -Subproject 3b014564eb185bb525a59378fd2d9ede65bf0d3 +Subproject 34af1cf8b3e3ea8272d6793a794484a239794d5 |