Add control configuration

This commit is contained in:
Nguyễn Gia Phong 2018-02-08 20:41:08 +07:00
parent dbe0ae4c01
commit 50a839826d
5 changed files with 108 additions and 41 deletions

View File

@ -17,35 +17,88 @@
#
# Copyright (C) 2017 Nguyễn Gia Phong
import re
from collections import deque
try:
from configparser import ConfigParser # Python 3
except ImportError:
from ConfigParser import ConfigParser # Python 2
try: # Python 3
from configparser import ConfigParser, NoOptionError, NoSectionError
except ImportError: # Python 2
from ConfigParser import ConfigParser, NoOptionError, NoSectionError
from os.path import join
from appdirs import AppDirs
from appdirs import user_config_dir, site_config_dir
from pkg_resources import resource_filename
import pygame
from pygame.locals import *
from pygame import DOUBLEBUF, KEYDOWN, OPENGL, QUIT, RESIZABLE, VIDEORESIZE
from .constants import *
from .maze import Maze
from .misc import some
USER_CONFIG = join(user_config_dir('brutalmaze'), 'settings.ini')
SITE_CONFIG = join(site_config_dir('brutalmaze'), 'settings.ini')
DEFAULT_BINDINGS = {'New game': 'F2', 'Pause': 'p',
'Move left': 'Left', 'Move right': 'Right',
'Move up': 'Up', 'Move down': 'Down',
'Long-range attack': 'Mouse1',
'Close-range attack': 'Mouse3'}
def getconf(config, section, option, valtype=str, fallback=None):
"""Return an option value for a given section from a ConfigParser
object.
If the key is not found, return the fallback value.
"""
if fallback is None: fallback = valtype()
try:
if valtype == str:
return config.get(section, option)
elif valtype == bool:
return config.getboolean(section, option)
elif valtype == float:
return config.getfloat(section, option)
elif valtype == int:
return config.getint(section, option)
except NoSectionError, NoOptionError:
return fallback
def main():
"""Start game and main loop."""
# Read configuration file
dirs = AppDirs(appname='brutalmaze')
config = ConfigParser()
if not config.read(join(dirs.user_config_dir, 'settings.ini')):
if not config.read(join(dirs.site_config_dir, 'settings.ini')):
config.read(resource_filename('brutalmaze', 'settings.ini'))
conf = config.read(USER_CONFIG)
if not conf: conf = config.read(SITE_CONFIG)
conf = conf[0] if conf else ''
# Read graphics configurations
width = getconf(config, 'Graphics', 'Screen width', int, 640)
height = getconf(config, 'Graphics', 'Screen height', int, 480)
scrtype = RESIZABLE
if config.getboolean('Graphics', 'OpenGL'):
surftype |= OPENGL | DOUBLEBUF
fps = config.getfloat('Graphics', 'Maximum FPS')
if getconf(config, 'Graphics', 'OpenGL', bool):
scrtype |= OPENGL | DOUBLEBUF
fps = getconf(config, 'Graphics', 'Maximum FPS', float, 60.0)
# Read control configurations
key, mouse = {}, {}
for cmd, bind in DEFAULT_BINDINGS.items():
i = getconf(config, 'Control', cmd, fallback=bind).lower()
if re.match('mouse[1-3]$', i):
if cmd not in ('Long-range attack', 'Close-range attack'):
print('File "{}", section Control'.format(conf))
print('\tOne does not simply {} using a mouse'.format(cmd))
quit()
mouse[cmd] = int(i[-1]) - 1
continue
if len(i) == 1:
key[cmd] = ord(i)
continue
try:
key[cmd] = getattr(pygame, 'K_{}'.format(i.upper()))
except AttributeError:
print('File "{}", section Control, option {}'.format(conf, cmd))
print('\t"{}" is not recognized as a valid input'.format(i))
quit()
# Initialization
pygame.mixer.pre_init(frequency=44100)
@ -54,8 +107,7 @@ def main():
pygame.mixer.music.play(-1)
pygame.display.set_icon(ICON)
pygame.fastevent.init()
maze = Maze((config.getint('Graphics', 'Screen width'),
config.getint('Graphics', 'Screen height')), scrtype, fps)
maze = Maze((width, height), scrtype, fps)
clock, flash_time, going = pygame.time.Clock(), deque(), True
# Main loop
@ -67,19 +119,26 @@ def main():
elif event.type == VIDEORESIZE:
maze.resize((event.w, event.h), scrtype)
elif event.type == KEYDOWN:
if event.key == K_F2: # new game
maze.__init__((maze.w, maze.h), fps)
elif event.key in (K_ESCAPE, K_p) and not maze.hero.dead:
if event.key == key['New game']:
maze.__init__((maze.w, maze.h), scrtype, fps)
elif event.key == key['Pause'] and not maze.hero.dead:
maze.paused ^= True
if not maze.hero.dead:
keys = pygame.key.get_pressed()
maze.move(keys[key['Move left']] - keys[key['Move right']],
keys[key['Move up']] - keys[key['Move down']], fps)
buttons = pygame.mouse.get_pressed()
maze.move(some(keys, LEFT) - some(keys, RIGHT),
some(keys, UP) - some(keys, DOWN), fps)
maze.hero.slashing = keys[K_RETURN] or buttons[2]
maze.hero.firing = buttons[0]
try:
maze.hero.firing = keys[key['Long-range attack']]
except KeyError:
maze.hero.firing = buttons[mouse['Long-range attack']]
try:
maze.hero.slashing = keys[key['Close-range attack']]
except KeyError:
maze.hero.slashing = buttons[mouse['Close-range attack']]
# Compare current FPS with the average of the last 5 seconds
if len(flash_time) > 5:
new_fps = 5000.0 / (flash_time[-1] - flash_time[0])
flash_time.popleft()

View File

@ -347,11 +347,11 @@ class Maze:
offsetx = (self.centerx-self.x) / self.distance
offsety = (self.centery-self.y) / self.distance
self.distance = (w * h / 416) ** 0.5
self.x, self.y = w // 2, h // 2
self.distance = (self.w * self.h / 416) ** 0.5
self.x, self.y = self.w // 2, self.h // 2
self.centerx = self.x + offsetx*self.distance
self.centery = self.y + offsety*self.distance
w, h = int(w/self.distance/2 + 2), int(h/self.distance/2 + 2)
w, h = int(self.w/self.distance/2 + 2), int(self.h/self.distance/2 + 2)
self.rangex = range(MIDDLE - w, MIDDLE + w + 1)
self.rangey = range(MIDDLE - h, MIDDLE + h + 1)
self.slashd = self.hero.R + self.distance/SQRT2

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# characters.py - module for shared functions and macros
# misc.py - module for miscellaneous functions
# This file is part of brutalmaze
#
# brutalmaze is free software: you can redistribute it and/or modify
@ -19,9 +19,7 @@
__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
@ -30,11 +28,6 @@ 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))

View File

@ -1,7 +0,0 @@
[Graphics]
Screen width: 640
Screen height: 480
# OpenGL should be supported on all machines with hardware acceleration
OpenGL: no
# FPS should not be greater than refresh rate
Maximum FPS: 60

22
settings.ini Normal file
View File

@ -0,0 +1,22 @@
[Graphics]
Screen width: 640
Screen height: 480
# OpenGL should be supported on all machines with hardware acceleration.
OpenGL: no
# FPS should not be greater than refresh rate.
Maximum FPS: 60
[Control]
# Input values should be either from Mouse1 to Mouse3 or a keyboard key
# and they are case-insensitively read.
# Aliases for special keys are listed here (without the K_ part):
# http://www.pygame.org/docs/ref/key.html
# Key combinations are not supported.
New game: F2
Pause: p
Move left: Left
Move right: Right
Move up: Up
Move down: Down
Long-range attack: Mouse1
Close-range attack: Mouse3