Finnish first socket server protype
This commit is contained in:
parent
bc47fb3f30
commit
3e85f0c3a1
6 changed files with 213 additions and 88 deletions
|
@ -86,11 +86,12 @@ class Hero:
|
||||||
if abs(self.spin_queue) > 0.5:
|
if abs(self.spin_queue) > 0.5:
|
||||||
self.angle += sign(self.spin_queue) * pi / 2 / self.spin_speed
|
self.angle += sign(self.spin_queue) * pi / 2 / self.spin_speed
|
||||||
self.spin_queue -= sign(self.spin_queue)
|
self.spin_queue -= sign(self.spin_queue)
|
||||||
else:
|
|
||||||
# Follow the mouse cursor
|
def update_angle(self, angle):
|
||||||
x, y = pygame.mouse.get_pos()
|
"""Turn to the given angle if the hero is not busy slashing."""
|
||||||
self.angle = atan2(y - self.y, x - self.x)
|
if abs(self.spin_queue) <= 0.5:
|
||||||
self.spin_queue = 0.0
|
self.spin_queue = 0.0
|
||||||
|
self.angle = angle
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
"""Draw the hero."""
|
"""Draw the hero."""
|
||||||
|
@ -238,13 +239,18 @@ class Enemy:
|
||||||
x, y = self.get_pos()
|
x, y = self.get_pos()
|
||||||
return atan2(y - self.maze.y, x - self.maze.x)
|
return atan2(y - self.maze.y, x - self.maze.x)
|
||||||
|
|
||||||
|
|
||||||
|
def get_color(self):
|
||||||
|
"""Return current color of the enemy."""
|
||||||
|
return TANGO[self.color][int(self.wound)] if self.awake else FG_COLOR
|
||||||
|
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
"""Draw the enemy."""
|
"""Draw the enemy."""
|
||||||
if get_ticks() < self.maze.next_move and not self.awake: return
|
if get_ticks() < self.maze.next_move and not self.awake: return
|
||||||
radious = self.maze.distance/SQRT2 - self.awake*2
|
radious = self.maze.distance/SQRT2 - self.awake*2
|
||||||
square = regpoly(4, radious, self.angle, *self.get_pos())
|
square = regpoly(4, radious, self.angle, *self.get_pos())
|
||||||
color = TANGO[self.color][int(self.wound)] if self.awake else FG_COLOR
|
fill_aapolygon(self.maze.surface, square, self.get_color())
|
||||||
fill_aapolygon(self.maze.surface, square, color)
|
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Update the enemy."""
|
"""Update the enemy."""
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
__doc__ = 'brutalmaze module for shared constants'
|
__doc__ = 'brutalmaze module for shared constants'
|
||||||
|
|
||||||
|
from string import ascii_lowercase
|
||||||
|
|
||||||
from pkg_resources import resource_filename as pkg_file
|
from pkg_resources import resource_filename as pkg_file
|
||||||
import pygame
|
import pygame
|
||||||
from pygame.mixer import Sound
|
from pygame.mixer import Sound
|
||||||
|
@ -69,6 +71,9 @@ TANGO = {'Butter': ((252, 233, 79), (237, 212, 0), (196, 160, 0)),
|
||||||
(136, 138, 133), (85, 87, 83), (46, 52, 54))}
|
(136, 138, 133), (85, 87, 83), (46, 52, 54))}
|
||||||
ENEMIES = ['Butter', 'Orange', 'Chocolate', 'Chameleon',
|
ENEMIES = ['Butter', 'Orange', 'Chocolate', 'Chameleon',
|
||||||
'SkyBlue', 'Plum', 'ScarletRed']
|
'SkyBlue', 'Plum', 'ScarletRed']
|
||||||
|
COLOR_CODE = ascii_lowercase + '0'
|
||||||
|
COLORS = {c: COLOR_CODE[i] for i, c in enumerate(
|
||||||
|
color for code in ENEMIES + ['Aluminium'] for color in TANGO[code])}
|
||||||
MINW, MAXW = 24, 36
|
MINW, MAXW = 24, 36
|
||||||
ENEMY_HP = 3
|
ENEMY_HP = 3
|
||||||
HERO_HP = 5
|
HERO_HP = 5
|
||||||
|
|
|
@ -26,7 +26,10 @@ try: # Python 3
|
||||||
from configparser import ConfigParser
|
from configparser import ConfigParser
|
||||||
except ImportError: # Python 2
|
except ImportError: # Python 2
|
||||||
from ConfigParser import ConfigParser
|
from ConfigParser import ConfigParser
|
||||||
|
from itertools import repeat
|
||||||
|
from math import atan2, degrees
|
||||||
from os.path import join, pathsep
|
from os.path import join, pathsep
|
||||||
|
from socket import socket
|
||||||
from sys import stdout
|
from sys import stdout
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,9 +38,9 @@ from pygame import DOUBLEBUF, KEYDOWN, OPENGL, QUIT, RESIZABLE, VIDEORESIZE
|
||||||
from pygame.time import Clock, get_ticks
|
from pygame.time import Clock, get_ticks
|
||||||
from appdirs import AppDirs
|
from appdirs import AppDirs
|
||||||
|
|
||||||
from .constants import SETTINGS, ICON, MUSIC, HERO_SPEED
|
from .constants import SETTINGS, ICON, MUSIC, HERO_SPEED, COLORS, WALL
|
||||||
from .maze import Maze
|
from .maze import Maze
|
||||||
from .misc import sign
|
from .misc import round2, sign
|
||||||
|
|
||||||
|
|
||||||
class ConfigReader:
|
class ConfigReader:
|
||||||
|
@ -58,8 +61,33 @@ class ConfigReader:
|
||||||
self.config.read(SETTINGS) # default configuration
|
self.config.read(SETTINGS) # default configuration
|
||||||
self.config.read(filenames)
|
self.config.read(filenames)
|
||||||
|
|
||||||
def parse_output(self):
|
# Fallback to None when attribute is missing
|
||||||
"""Parse graphics and sound configurations."""
|
def __getattr__(self, name): return None
|
||||||
|
|
||||||
|
def parse(self):
|
||||||
|
"""Parse configurations."""
|
||||||
|
self.server = self.config.getboolean('Server', 'Enable')
|
||||||
|
if self.server:
|
||||||
|
self.host = self.config.get('Server', 'Host')
|
||||||
|
self.port = self.config.getint('Server', 'Port')
|
||||||
|
self.headless = self.config.getboolean('Server', 'Headless')
|
||||||
|
else:
|
||||||
|
self.key, self.mouse = {}, {}
|
||||||
|
for cmd, alias in self.CONTROL_ALIASES:
|
||||||
|
i = self.config.get('Control', cmd)
|
||||||
|
if re.match('mouse[1-3]$', i.lower()):
|
||||||
|
if alias not in ('shot', 'slash'):
|
||||||
|
raise ValueError(self.WEIRD_MOUSE_ERR.format(cmd))
|
||||||
|
self.mouse[alias] = int(i[-1]) - 1
|
||||||
|
continue
|
||||||
|
if len(i) == 1:
|
||||||
|
self.key[alias] = ord(i.lower())
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
self.key[alias] = getattr(pygame, 'K_{}'.format(i.upper()))
|
||||||
|
except AttributeError:
|
||||||
|
raise ValueError(self.INVALID_CONTROL_ERR.format(cmd, i))
|
||||||
|
|
||||||
self.size = (self.config.getint('Graphics', 'Screen width'),
|
self.size = (self.config.getint('Graphics', 'Screen width'),
|
||||||
self.config.getint('Graphics', 'Screen height'))
|
self.config.getint('Graphics', 'Screen height'))
|
||||||
self.opengl = self.config.getboolean('Graphics', 'OpenGL')
|
self.opengl = self.config.getboolean('Graphics', 'OpenGL')
|
||||||
|
@ -67,24 +95,6 @@ class ConfigReader:
|
||||||
self.muted = self.config.getboolean('Sound', 'Muted')
|
self.muted = self.config.getboolean('Sound', 'Muted')
|
||||||
self.musicvol = self.config.getfloat('Sound', 'Music volume')
|
self.musicvol = self.config.getfloat('Sound', 'Music volume')
|
||||||
|
|
||||||
def parse_control(self):
|
|
||||||
"""Parse control configurations."""
|
|
||||||
self.key, self.mouse = {}, {}
|
|
||||||
for cmd, alias in self.CONTROL_ALIASES:
|
|
||||||
i = self.config.get('Control', cmd)
|
|
||||||
if re.match('mouse[1-3]$', i.lower()):
|
|
||||||
if alias not in ('shot', 'slash'):
|
|
||||||
raise ValueError(self.WEIRD_MOUSE_ERR.format(cmd))
|
|
||||||
self.mouse[alias] = int(i[-1]) - 1
|
|
||||||
continue
|
|
||||||
if len(i) == 1:
|
|
||||||
self.key[alias] = ord(i.lower())
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
self.key[alias] = getattr(pygame, 'K_{}'.format(i.upper()))
|
|
||||||
except AttributeError:
|
|
||||||
raise ValueError(self.INVALID_CONTROL_ERR.format(cmd, i))
|
|
||||||
|
|
||||||
def read_args(self, arguments):
|
def read_args(self, arguments):
|
||||||
"""Read and parse a ArgumentParser.Namespace."""
|
"""Read and parse a ArgumentParser.Namespace."""
|
||||||
for option in 'size', 'opengl', 'max_fps', 'muted', 'musicvol':
|
for option in 'size', 'opengl', 'max_fps', 'muted', 'musicvol':
|
||||||
|
@ -94,29 +104,108 @@ class ConfigReader:
|
||||||
|
|
||||||
class Game:
|
class Game:
|
||||||
"""Object handling main loop and IO."""
|
"""Object handling main loop and IO."""
|
||||||
def __init__(self, size, scrtype, max_fps, muted, musicvol, key, mouse):
|
def __init__(self, config):
|
||||||
pygame.mixer.pre_init(frequency=44100)
|
pygame.mixer.pre_init(frequency=44100)
|
||||||
pygame.init()
|
pygame.init()
|
||||||
if muted:
|
if config.muted or config.headless:
|
||||||
pygame.mixer.quit()
|
pygame.mixer.quit()
|
||||||
else:
|
else:
|
||||||
pygame.mixer.music.load(MUSIC)
|
pygame.mixer.music.load(MUSIC)
|
||||||
pygame.mixer.music.set_volume(musicvol)
|
pygame.mixer.music.set_volume(config.musicvol)
|
||||||
pygame.mixer.music.play(-1)
|
pygame.mixer.music.play(-1)
|
||||||
pygame.display.set_icon(ICON)
|
pygame.display.set_icon(ICON)
|
||||||
pygame.fastevent.init()
|
|
||||||
|
if config.server:
|
||||||
|
self.socket = socket()
|
||||||
|
self.socket.bind((config.host, config.port))
|
||||||
|
self.socket.listen()
|
||||||
|
else:
|
||||||
|
pygame.fastevent.init()
|
||||||
|
|
||||||
|
self.server, self.headless = config.server, config.headless
|
||||||
# self.fps is a float to make sure floordiv won't be used in Python 2
|
# 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.max_fps, self.fps = config.max_fps, float(config.max_fps)
|
||||||
self.musicvol = musicvol
|
self.musicvol = config.musicvol
|
||||||
self.key, self.mouse = key, mouse
|
self.key, self.mouse = config.key, config.mouse
|
||||||
self.maze = Maze(max_fps, size, scrtype)
|
scrtype = (config.opengl and DOUBLEBUF|OPENGL) | RESIZABLE
|
||||||
|
self.maze = Maze(config.max_fps, config.size, scrtype, config.headless)
|
||||||
self.hero = self.maze.hero
|
self.hero = self.maze.hero
|
||||||
self.clock, self.paused = Clock(), False
|
self.clock, self.paused = Clock(), False
|
||||||
|
|
||||||
def __enter__(self): return self
|
def __enter__(self): return self
|
||||||
|
|
||||||
|
def expos(self, x, y):
|
||||||
|
"""Return position of the given coordinates in rounded percent."""
|
||||||
|
cx = (x+self.maze.x-self.maze.centerx) / self.maze.distance * 100
|
||||||
|
cy = (y+self.maze.y-self.maze.centery) / self.maze.distance * 100
|
||||||
|
return round2(cx), round2(cy)
|
||||||
|
|
||||||
|
def export(self):
|
||||||
|
"""Export maze data to a bytes object."""
|
||||||
|
maze, hero, tick, ne = self.maze, self.hero, get_ticks(), 0
|
||||||
|
walls = [[1 if maze.map[x][y] == WALL else 0 for x in maze.rangex]
|
||||||
|
for y in maze.rangey]
|
||||||
|
|
||||||
|
x, y = self.expos(maze.x, maze.y)
|
||||||
|
lines = deque(['{} {} {:.0f} {:d} {:d} {:d}'.format(
|
||||||
|
x, y, hero.wound * 100, hero.next_strike <= tick,
|
||||||
|
hero.next_heal <= tick, maze.next_move <= tick)])
|
||||||
|
|
||||||
|
for enemy in maze.enemies:
|
||||||
|
if not enemy.awake:
|
||||||
|
walls[enemy.y-maze.rangey[0]][enemy.x-maze.rangex[0]] = WALL
|
||||||
|
continue
|
||||||
|
elif enemy.color == 'Chameleon' and maze.next_move <= tick:
|
||||||
|
continue
|
||||||
|
x, y = self.expos(*enemy.get_pos())
|
||||||
|
lines.append('{} {} {} {:.0f}'.format(COLORS[enemy.get_color()],
|
||||||
|
x, y, degrees(enemy.angle)))
|
||||||
|
ne += 1
|
||||||
|
|
||||||
|
if maze.next_move <= tick:
|
||||||
|
rows = (''.join(str(cell) for cell in row) for row in walls)
|
||||||
|
else:
|
||||||
|
rows = repeat('0' * len(maze.rangex), len(maze.rangey))
|
||||||
|
lines.appendleft('\n'.join(rows))
|
||||||
|
|
||||||
|
for bullet in maze.bullets:
|
||||||
|
x, y = self.expos(bullet.x, bullet.y)
|
||||||
|
lines.append('{} {} {} {:.0f}'.format(COLORS[bullet.get_color()],
|
||||||
|
x, y, degrees(bullet.angle)))
|
||||||
|
|
||||||
|
lines.appendleft('{} {} {}'.format(len(walls), ne, len(maze.bullets)))
|
||||||
|
return '\n'.join(lines).encode()
|
||||||
|
|
||||||
|
def meta(self):
|
||||||
|
"""Handle meta events on Pygame window.
|
||||||
|
|
||||||
|
Return False if QUIT event is captured, True otherwise.
|
||||||
|
"""
|
||||||
|
events = pygame.fastevent.get()
|
||||||
|
for event in events:
|
||||||
|
if event.type == QUIT:
|
||||||
|
return False
|
||||||
|
elif event.type == VIDEORESIZE:
|
||||||
|
self.maze.resize((event.w, event.h))
|
||||||
|
elif event.type == KEYDOWN and not self.server:
|
||||||
|
if event.key == self.key['new']:
|
||||||
|
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.headless: self.maze.draw()
|
||||||
|
return True
|
||||||
|
|
||||||
def move(self, x, y):
|
def move(self, x, y):
|
||||||
"""Command the hero to move faster in the given direction."""
|
"""Command the hero to move faster in the given direction."""
|
||||||
|
x, y = -x, -y # or move the maze in the reverse direction
|
||||||
stunned = pygame.time.get_ticks() < self.maze.next_move
|
stunned = pygame.time.get_ticks() < self.maze.next_move
|
||||||
velocity = self.maze.distance * HERO_SPEED / self.fps
|
velocity = self.maze.distance * HERO_SPEED / self.fps
|
||||||
accel = velocity * HERO_SPEED / self.fps
|
accel = velocity * HERO_SPEED / self.fps
|
||||||
|
@ -139,42 +228,51 @@ class Game:
|
||||||
self.maze.vy += y * accel
|
self.maze.vy += y * accel
|
||||||
if abs(self.maze.vy) > velocity: self.maze.vy = y * velocity
|
if abs(self.maze.vy) > velocity: self.maze.vy = y * velocity
|
||||||
|
|
||||||
def loop(self):
|
def control(self, x, y, angle, firing, slashing):
|
||||||
"""Start and handle main loop."""
|
"""Control how the hero move and attack."""
|
||||||
events = pygame.fastevent.get()
|
self.move(x, y)
|
||||||
for event in events:
|
self.hero.update_angle(angle)
|
||||||
if event.type == QUIT:
|
self.hero.firing = firing
|
||||||
return False
|
self.hero.slashing = slashing
|
||||||
elif event.type == VIDEORESIZE:
|
|
||||||
self.maze.resize((event.w, event.h))
|
|
||||||
elif event.type == KEYDOWN:
|
|
||||||
if event.key == self.key['new']:
|
|
||||||
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()
|
|
||||||
|
|
||||||
|
def remote_control(self, connection):
|
||||||
|
"""Handle remote control though socket server.
|
||||||
|
|
||||||
|
Return False if client disconnect, True otherwise.
|
||||||
|
"""
|
||||||
|
data = self.export()
|
||||||
|
connection.send('{:06}'.format(len(data)))
|
||||||
|
connection.send(data)
|
||||||
|
buf = connection.recv(8)
|
||||||
|
if not buf: return False
|
||||||
|
x, y, angle, attack = (int(i) for i in buf.decode().split())
|
||||||
|
self.control(x, y, angle, attack & 1, attack >> 1)
|
||||||
|
|
||||||
|
def user_control(self):
|
||||||
|
"""Handle direct control from user's mouse and keyboard."""
|
||||||
if not self.hero.dead:
|
if not self.hero.dead:
|
||||||
keys = pygame.key.get_pressed()
|
keys = pygame.key.get_pressed()
|
||||||
self.move(keys[self.key['left']] - keys[self.key['right']],
|
right = keys[self.key['right']] - keys[self.key['left']]
|
||||||
keys[self.key['up']] - keys[self.key['down']])
|
down = keys[self.key['down']] - keys[self.key['up']]
|
||||||
|
|
||||||
|
# Follow the mouse cursor
|
||||||
|
x, y = pygame.mouse.get_pos()
|
||||||
|
angle = atan2(y - self.hero.y, x - self.hero.x)
|
||||||
|
|
||||||
buttons = pygame.mouse.get_pressed()
|
buttons = pygame.mouse.get_pressed()
|
||||||
try:
|
try:
|
||||||
self.hero.firing = keys[self.key['shot']]
|
firing = keys[self.key['shot']]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.hero.firing = buttons[self.mouse['shot']]
|
firing = buttons[self.mouse['shot']]
|
||||||
try:
|
try:
|
||||||
self.hero.slashing = keys[self.key['slash']]
|
slashing = keys[self.key['slash']]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.hero.slashing = buttons[self.mouse['slash']]
|
slashing = buttons[self.mouse['slash']]
|
||||||
|
|
||||||
|
self.control(right, down, angle, firing, slashing)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
"""Update fps and the maze."""
|
||||||
# Compare current FPS with the average of the last 10 frames
|
# Compare current FPS with the average of the last 10 frames
|
||||||
new_fps = self.clock.get_fps()
|
new_fps = self.clock.get_fps()
|
||||||
if new_fps < self.fps:
|
if new_fps < self.fps:
|
||||||
|
@ -183,7 +281,6 @@ class Game:
|
||||||
self.fps += 5
|
self.fps += 5
|
||||||
if not self.paused: self.maze.update(self.fps)
|
if not self.paused: self.maze.update(self.fps)
|
||||||
self.clock.tick(self.fps)
|
self.clock.tick(self.fps)
|
||||||
return True
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, traceback): pygame.quit()
|
def __exit__(self, exc_type, exc_value, traceback): pygame.quit()
|
||||||
|
|
||||||
|
@ -196,7 +293,7 @@ def main():
|
||||||
parents.append(dirs.user_config_dir)
|
parents.append(dirs.user_config_dir)
|
||||||
filenames = [join(parent, 'settings.ini') for parent in parents]
|
filenames = [join(parent, 'settings.ini') for parent in parents]
|
||||||
config = ConfigReader(filenames)
|
config = ConfigReader(filenames)
|
||||||
config.parse_output()
|
config.parse()
|
||||||
|
|
||||||
# Parse command-line arguments
|
# Parse command-line arguments
|
||||||
parser = ArgumentParser(formatter_class=RawTextHelpFormatter)
|
parser = ArgumentParser(formatter_class=RawTextHelpFormatter)
|
||||||
|
@ -238,11 +335,15 @@ def main():
|
||||||
# Manipulate config
|
# Manipulate config
|
||||||
if args.config: config.config.read(args.config)
|
if args.config: config.config.read(args.config)
|
||||||
config.read_args(args)
|
config.read_args(args)
|
||||||
config.parse_output()
|
config.parse()
|
||||||
config.parse_control()
|
|
||||||
|
|
||||||
# Main loop
|
# Main loop
|
||||||
scrtype = (config.opengl and DOUBLEBUF|OPENGL) | RESIZABLE
|
with Game(config) as game:
|
||||||
with Game(config.size, scrtype, config.max_fps, config.muted,
|
if config.server:
|
||||||
config.musicvol, config.key, config.mouse) as game:
|
while game.meta():
|
||||||
while game.loop(): pass
|
game.remote_control()
|
||||||
|
game.update()
|
||||||
|
else:
|
||||||
|
while game.meta():
|
||||||
|
game.user_control()
|
||||||
|
game.update()
|
||||||
|
|
|
@ -63,7 +63,7 @@ class Maze:
|
||||||
distance (float): distance between centers of grids (in px)
|
distance (float): distance between centers of grids (in px)
|
||||||
x, y (int): coordinates of the center of the hero (in px)
|
x, y (int): coordinates of the center of the hero (in px)
|
||||||
centerx, centery (float): center grid's center's coordinates (in px)
|
centerx, centery (float): center grid's center's coordinates (in px)
|
||||||
rangex, rangey (range): range of the index of the grids on display
|
rangex, rangey (list): range of the index of the grids on display
|
||||||
score (float): current score
|
score (float): current score
|
||||||
map (deque of deque): map of grids representing objects on the maze
|
map (deque of deque): map of grids representing objects on the maze
|
||||||
vx, vy (float): velocity of the maze movement (in pixels per frame)
|
vx, vy (float): velocity of the maze movement (in pixels per frame)
|
||||||
|
@ -78,21 +78,22 @@ class Maze:
|
||||||
sfx_slash (pygame.mixer.Sound): sound effect of slashed enemy
|
sfx_slash (pygame.mixer.Sound): sound effect of slashed enemy
|
||||||
sfx_lose (pygame.mixer.Sound): sound effect to be played when you lose
|
sfx_lose (pygame.mixer.Sound): sound effect to be played when you lose
|
||||||
"""
|
"""
|
||||||
def __init__(self, fps, size=None, scrtype=None):
|
def __init__(self, fps, size=None, scrtype=None, headless=False):
|
||||||
self.fps = fps
|
self.fps = fps
|
||||||
if size is not None:
|
if not headless:
|
||||||
self.w, self.h = size
|
if size is not None:
|
||||||
else:
|
self.w, self.h = size
|
||||||
size = self.w, self.h
|
else:
|
||||||
if scrtype is not None: self.scrtype = scrtype
|
size = self.w, self.h
|
||||||
|
if scrtype is not None: self.scrtype = scrtype
|
||||||
|
self.surface = pygame.display.set_mode(size, self.scrtype)
|
||||||
|
|
||||||
self.surface = pygame.display.set_mode(size, self.scrtype)
|
|
||||||
self.distance = (self.w * self.h / 416) ** 0.5
|
self.distance = (self.w * self.h / 416) ** 0.5
|
||||||
self.x, self.y = self.w // 2, self.h // 2
|
self.x, self.y = self.w // 2, self.h // 2
|
||||||
self.centerx, self.centery = self.w / 2.0, self.h / 2.0
|
self.centerx, self.centery = self.w / 2.0, self.h / 2.0
|
||||||
w, h = (int(i/self.distance/2 + 2) for i in size)
|
w, h = (int(i/self.distance/2 + 2) for i in size)
|
||||||
self.rangex = range(MIDDLE - w, MIDDLE + w + 1)
|
self.rangex = list(range(MIDDLE - w, MIDDLE + w + 1))
|
||||||
self.rangey = range(MIDDLE - h, MIDDLE + h + 1)
|
self.rangey = list(range(MIDDLE - h, MIDDLE + h + 1))
|
||||||
self.score = INIT_SCORE
|
self.score = INIT_SCORE
|
||||||
|
|
||||||
self.map = deque()
|
self.map = deque()
|
||||||
|
@ -146,7 +147,7 @@ class Maze:
|
||||||
fill_aapolygon(self.surface, square, FG_COLOR)
|
fill_aapolygon(self.surface, square, FG_COLOR)
|
||||||
|
|
||||||
for enemy in self.enemies: enemy.draw()
|
for enemy in self.enemies: enemy.draw()
|
||||||
self.hero.draw()
|
if not self.hero.dead: self.hero.draw()
|
||||||
bullet_radius = self.distance / 4
|
bullet_radius = self.distance / 4
|
||||||
for bullet in self.bullets: bullet.draw(bullet_radius)
|
for bullet in self.bullets: bullet.draw(bullet_radius)
|
||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
|
@ -320,7 +321,6 @@ class Maze:
|
||||||
self.hero.update(fps)
|
self.hero.update(fps)
|
||||||
self.slash()
|
self.slash()
|
||||||
self.track_bullets()
|
self.track_bullets()
|
||||||
self.draw()
|
|
||||||
|
|
||||||
def resize(self, size):
|
def resize(self, size):
|
||||||
"""Resize the maze."""
|
"""Resize the maze."""
|
||||||
|
@ -349,3 +349,4 @@ class Maze:
|
||||||
self.hero.slashing = self.hero.firing = False
|
self.hero.slashing = self.hero.firing = False
|
||||||
self.vx = self.vy = 0.0
|
self.vx = self.vy = 0.0
|
||||||
play(self.sfx_lose)
|
play(self.sfx_lose)
|
||||||
|
print('Game over. Your score: {}'.format(int(self.score - INIT_SCORE)))
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
[Server]
|
||||||
|
# Enabling remote control will disable control via keyboard and mouse.
|
||||||
|
Enable: no
|
||||||
|
Host: localhost
|
||||||
|
Port: 8089
|
||||||
|
# Disable graphics and sounds (only if socket server is enabled).
|
||||||
|
Headless: no
|
||||||
|
|
||||||
[Graphics]
|
[Graphics]
|
||||||
Screen width: 640
|
Screen width: 640
|
||||||
Screen height: 480
|
Screen height: 480
|
||||||
|
|
|
@ -55,14 +55,18 @@ class Bullet:
|
||||||
self.x += s * cos(self.angle)
|
self.x += s * cos(self.angle)
|
||||||
self.y += s * sin(self.angle)
|
self.y += s * sin(self.angle)
|
||||||
|
|
||||||
|
def get_color(self):
|
||||||
|
"""Return current color of the enemy."""
|
||||||
|
value = int((1-(self.fall_time-get_ticks())/BULLET_LIFETIME)*ENEMY_HP)
|
||||||
|
try:
|
||||||
|
return TANGO[self.color][value]
|
||||||
|
except IndexError:
|
||||||
|
return BG_COLOR
|
||||||
|
|
||||||
def draw(self, radius):
|
def draw(self, radius):
|
||||||
"""Draw the bullet."""
|
"""Draw the bullet."""
|
||||||
pentagon = regpoly(5, radius, self.angle, self.x, self.y)
|
pentagon = regpoly(5, radius, self.angle, self.x, self.y)
|
||||||
value = int((1-(self.fall_time-get_ticks())/BULLET_LIFETIME)*ENEMY_HP)
|
fill_aapolygon(self.surface, pentagon, self.get_color())
|
||||||
try:
|
|
||||||
fill_aapolygon(self.surface, pentagon, TANGO[self.color][value])
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def place(self, x, y):
|
def place(self, x, y):
|
||||||
"""Move the bullet by (x, y) (in pixels)."""
|
"""Move the bullet by (x, y) (in pixels)."""
|
||||||
|
|
Loading…
Reference in a new issue