about summary refs log tree commit diff homepage
diff options
context:
space:
mode:
authorNguyễn Gia Phong <vn.mcsinyx@gmail.com>2018-02-16 20:25:32 +0700
committerNguyễn Gia Phong <vn.mcsinyx@gmail.com>2018-02-16 20:25:32 +0700
commit77bb9a53d646d52bdb8bf39616bf4fae383ef6fa (patch)
tree81f5b99c66e344dde568aca0c372aeb71aa213f4
parente1db0393f9ccd3d45af2c0025f05d6fc67f87547 (diff)
downloadbrutalmaze-77bb9a53d646d52bdb8bf39616bf4fae383ef6fa.tar.gz
Partially refactor to prepare for alternate control from socket
-rw-r--r--brutalmaze/main.py155
-rw-r--r--brutalmaze/maze.py51
2 files changed, 113 insertions, 93 deletions
diff --git a/brutalmaze/main.py b/brutalmaze/main.py
index 79e84c3..765c7e7 100644
--- a/brutalmaze/main.py
+++ b/brutalmaze/main.py
@@ -23,30 +23,32 @@ import re
 from argparse import ArgumentParser, FileType, RawTextHelpFormatter
 from collections import deque
 try:                    # Python 3
-    from configparser import ConfigParser, NoOptionError, NoSectionError
+    from configparser import ConfigParser
 except ImportError:     # Python 2
-    from ConfigParser import ConfigParser, NoOptionError, NoSectionError
+    from ConfigParser import ConfigParser
 from os.path import join, pathsep
 from sys import stdout
 
 
 import pygame
 from pygame import DOUBLEBUF, KEYDOWN, OPENGL, QUIT, RESIZABLE, VIDEORESIZE
+from pygame.time import Clock, get_ticks
 from appdirs import AppDirs
 
 from .constants import *
 from .maze import Maze
+from .misc import sign
 
 
 class ConfigReader:
     """Object reading and processing INI configuration file for
     Brutal Maze.
     """
-    DEFAULT_BINDINGS = (('New game', 'new'), ('Pause', 'pause'),
-                        ('Move left', 'left'), ('Move right', 'right'),
-                        ('Move up', 'up'), ('Move down', 'down'),
-                        ('Long-range attack', 'shot'),
-                        ('Close-range attack', 'slash'))
+    CONTROL_ALIASES = (('New game', 'new'), ('Pause', 'pause'),
+                       ('Move left', 'left'), ('Move right', 'right'),
+                       ('Move up', 'up'), ('Move down', 'down'),
+                       ('Long-range attack', 'shot'),
+                       ('Close-range attack', 'slash'))
     WEIRD_MOUSE_ERR = '{}: Mouse is not a suitable control'
     INVALID_CONTROL_ERR = '{}: {} is not recognized as a valid control key'
 
@@ -87,6 +89,91 @@ class ConfigReader:
         if arguments.max_fps is not None: self.max_fps = arguments.max_fps
 
 
+class Game:
+    """Object handling main loop and IO."""
+    def __init__(self, size, scrtype, max_fps, key, mouse):
+        pygame.mixer.pre_init(frequency=44100)
+        pygame.init()
+        pygame.mixer.music.load(MUSIC)
+        pygame.mixer.music.play(-1)
+        pygame.display.set_icon(ICON)
+        pygame.fastevent.init()
+        self.clock, self.flashes, self.fps = Clock(), deque(), max_fps
+        self.max_fps, self.key, self.mouse = max_fps, key, mouse
+        self.maze = Maze(max_fps, size, scrtype)
+        self.hero = self.maze.hero
+        self.paused = False
+
+    def __enter__(self): return self
+
+    def move(self, x, y):
+        """Command the hero to move faster in the given direction."""
+        stunned = pygame.time.get_ticks() < self.maze.next_move
+        velocity = self.maze.distance * HERO_SPEED / self.fps
+        accel = velocity * HERO_SPEED / self.fps
+
+        if stunned or not x:
+            self.maze.vx -= sign(self.maze.vx) * accel
+            if abs(self.maze.vx) < accel * 2: self.maze.vx = 0.0
+        elif x * self.maze.vx < 0:
+            self.maze.vx += x * 2 * accel
+        else:
+            self.maze.vx += x * accel
+            if abs(self.maze.vx) > velocity: self.maze.vx = x * velocity
+
+        if stunned or not y:
+            self.maze.vy -= sign(self.maze.vy) * accel
+            if abs(self.maze.vy) < accel * 2: self.maze.vy = 0.0
+        elif y * self.maze.vy < 0:
+            self.maze.vy += y * 2 * accel
+        else:
+            self.maze.vy += y * accel
+            if abs(self.maze.vy) > velocity: self.maze.vy = y * velocity
+
+    def loop(self):
+        """Start and handle main loop."""
+        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:
+                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
+
+        if not self.hero.dead:
+            keys = pygame.key.get_pressed()
+            self.move(keys[self.key['left']] - keys[self.key['right']],
+                      keys[self.key['up']] - keys[self.key['down']])
+            buttons = pygame.mouse.get_pressed()
+            try:
+                self.hero.firing = keys[self.key['shot']]
+            except KeyError:
+                self.hero.firing = buttons[self.mouse['shot']]
+            try:
+                self.hero.slashing = keys[self.key['slash']]
+            except KeyError:
+                self.hero.slashing = buttons[self.mouse['slash']]
+
+        # Compare current FPS with the average of the last 5 seconds
+        if len(self.flashes) > 5:
+            new_fps = 5000.0 / (self.flashes[-1] - self.flashes[0])
+            self.flashes.popleft()
+            if new_fps < self.fps:
+                self.fps -= 1
+            elif self.fps < self.max_fps and not self.paused:
+                self.fps += 5
+        if not self.paused: self.maze.update(self.fps)
+        self.flashes.append(pygame.time.get_ticks())
+        self.clock.tick(self.fps)
+        return True
+
+    def __exit__(self, exc_type, exc_value, traceback): pygame.quit()
+
+
 def main():
     """Start game and main loop."""
     # Read configuration file
@@ -132,55 +219,7 @@ def main():
     config.parse_graphics()
     config.parse_control()
 
-    # Initialization
-    pygame.mixer.pre_init(frequency=44100)
-    pygame.init()
-    pygame.mixer.music.load(MUSIC)
-    pygame.mixer.music.play(-1)
-    pygame.display.set_icon(ICON)
-    pygame.fastevent.init()
-    clock, flash_time, fps = pygame.time.Clock(), deque(), config.max_fps
-    scrtype = (config.opengl and DOUBLEBUF|OPENGL) | RESIZABLE
-    maze = Maze(config.size, scrtype, fps)
-
     # Main loop
-    going = True
-    while going:
-        events = pygame.fastevent.get()
-        for event in events:
-            if event.type == QUIT:
-                going = False
-            elif event.type == VIDEORESIZE:
-                maze.resize((event.w, event.h), scrtype)
-            elif event.type == KEYDOWN:
-                if event.key == config.key['new']:
-                    maze.__init__((maze.w, maze.h), scrtype, fps)
-                elif event.key == config.key['pause'] and not maze.hero.dead:
-                    maze.paused ^= True
-
-        if not maze.hero.dead:
-            keys = pygame.key.get_pressed()
-            maze.move(keys[config.key['left']] - keys[config.key['right']],
-                      keys[config.key['up']] - keys[config.key['down']], fps)
-            buttons = pygame.mouse.get_pressed()
-            try:
-                maze.hero.firing = keys[config.key['shot']]
-            except KeyError:
-                maze.hero.firing = buttons[config.mouse['shot']]
-            try:
-                maze.hero.slashing = keys[config.key['slash']]
-            except KeyError:
-                maze.hero.slashing = buttons[config.mouse['slash']]
-
-        # 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()
-            if new_fps < fps:
-                fps -= 1
-            elif fps < config.max_fps and not maze.paused:
-                fps += 5
-        maze.update(fps)
-        flash_time.append(pygame.time.get_ticks())
-        clock.tick(fps)
-    pygame.quit()
+    scrtype = (config.opengl and DOUBLEBUF|OPENGL) | RESIZABLE
+    with Game(config.size, scrtype, config.max_fps, config.key, config.mouse) as game:
+        while game.loop(): pass
diff --git a/brutalmaze/maze.py b/brutalmaze/maze.py
index 197fb5e..0f1f5de 100644
--- a/brutalmaze/maze.py
+++ b/brutalmaze/maze.py
@@ -58,19 +58,18 @@ class Maze:
     """Object representing the maze, including the characters.
 
     Attributes:
-        w, h: width and height of the display
-        fps: current frame rate
+        w, h (int): width and height of the display (in px)
+        fps (float): current frame rate
         surface (pygame.Surface): the display to draw on
         distance (float): distance between centers of grids (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)
-        rangex, rangey: range of the index of the grids on display
-        paused (bool): flag indicates if the game is paused
+        rangex, rangey (range): range of the index of the grids on display
         score (float): current score
         map (deque of deque): map of grids representing objects on the maze
         vx, vy (float): velocity of the maze movement (in pixels per frame)
-        rotatex, rotatey: grids rotated
-        bullets (list of Bullet): bullets flying
+        rotatex, rotatey (int): grids rotated
+        bullets (list of Bullet): flying bullets
         enemy_weights (dict): probabilities of enemies to be created
         enemies (list of Enemy): alive enemies
         hero (Hero): the hero
@@ -81,17 +80,22 @@ class Maze:
         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, scrtype, fps):
-        self.w, self.h = size
+    def __init__(self, fps, size=None, scrtype=None):
         self.fps = fps
-        self.surface = pygame.display.set_mode(size, scrtype)
+        if size is not None:
+            self.w, self.h = size
+        else:
+            size = self.w, self.h
+        if scrtype is not None: self.scrtype = scrtype
+
+        self.surface = pygame.display.set_mode(size, self.scrtype)
         self.distance = (self.w * self.h / 416) ** 0.5
         self.x, self.y = self.w // 2, self.h // 2
         self.centerx, self.centery = self.w / 2.0, self.h / 2.0
         w, h = (int(i/self.distance/2 + 2) for i in size)
         self.rangex = range(MIDDLE - w, MIDDLE + w + 1)
         self.rangey = range(MIDDLE - h, MIDDLE + h + 1)
-        self.paused, self.score = False, INIT_SCORE
+        self.score = INIT_SCORE
 
         self.map = deque()
         for _ in range(MAZE_SIZE): self.map.extend(new_column())
@@ -295,7 +299,6 @@ class Maze:
 
     def update(self, fps):
         """Update the maze."""
-        if self.paused: return
         self.fps = fps
         dx = self.is_valid_move(vx=self.vx)
         self.centerx += dx
@@ -317,32 +320,10 @@ class Maze:
         pygame.display.set_caption('Brutal Maze - Score: {}'.format(
             int(self.score - INIT_SCORE)))
 
-    def move(self, x, y, fps):
-        """Command the hero to move faster in the given direction."""
-        stunned = get_ticks() < self.next_move
-        velocity = self.distance * HERO_SPEED / fps
-        accel = velocity * HERO_SPEED / fps
-        if stunned or not x:
-            self.vx -= sign(self.vx) * accel
-            if abs(self.vx) < accel * 2: self.vx = 0.0
-        elif x * self.vx < 0:
-            self.vx += x * 2 * accel
-        else:
-            self.vx += x * accel
-            if abs(self.vx) > velocity: self.vx = x * velocity
-        if stunned or not y:
-            self.vy -= sign(self.vy) * accel
-            if abs(self.vy) < accel * 2: self.vy = 0.0
-        elif y * self.vy < 0:
-            self.vy += y * 2 * accel
-        else:
-            self.vy += y * accel
-            if abs(self.vy) > velocity: self.vy = y * velocity
-
-    def resize(self, size, scrtype):
+    def resize(self, size):
         """Resize the maze."""
         self.w, self.h = size
-        self.surface = pygame.display.set_mode(size, scrtype)
+        self.surface = pygame.display.set_mode(size, self.scrtype)
         self.hero.resize()
 
         offsetx = (self.centerx-self.x) / self.distance