about summary refs log tree commit diff homepage
path: root/brutalmaze
diff options
context:
space:
mode:
authorNguyễn Gia Phong <vn.mcsinyx@gmail.com>2018-03-06 21:01:27 +0700
committerNguyễn Gia Phong <vn.mcsinyx@gmail.com>2018-03-06 21:01:27 +0700
commitb5039285d5a95d1921d1d4428032c16fe94da897 (patch)
tree3c9329c870629970e44ba7c4d22a19662e0daa4b /brutalmaze
parentf7c600934e1406fe0d18ce69a948c3622af84d36 (diff)
downloadbrutalmaze-b5039285d5a95d1921d1d4428032c16fe94da897.tar.gz
Retain game state after pauses 0.6.0
Diffstat (limited to 'brutalmaze')
-rw-r--r--brutalmaze/characters.py59
-rw-r--r--brutalmaze/constants.py2
-rw-r--r--brutalmaze/game.py23
-rw-r--r--brutalmaze/maze.py39
-rw-r--r--brutalmaze/weapons.py9
5 files changed, 69 insertions, 63 deletions
diff --git a/brutalmaze/characters.py b/brutalmaze/characters.py
index 2f17b42..daa97bc 100644
--- a/brutalmaze/characters.py
+++ b/brutalmaze/characters.py
@@ -23,8 +23,6 @@ from math import atan, atan2, sin, pi
 from random import choice, randrange, shuffle
 from sys import modules
 
-from pygame.time import get_ticks
-
 from .constants import (
     TANGO, HERO_HP, SFX_HEART, HEAL_SPEED, MIN_BEAT, ATTACK_SPEED, ENEMY,
     ENEMY_SPEED, ENEMY_HP, SFX_SLASH_HERO, MIDDLE, WALL, FIRANGE, AROUND_HERO,
@@ -42,9 +40,9 @@ class Hero:
         angle (float): angle of the direction the hero pointing (in radians)
         color (tuple of pygame.Color): colors of the hero on different HPs
         R (int): circumradius of the regular triangle representing the hero
-        next_heal (int): the tick that the hero gains back healing ability
-        next_beat (int): the tick to play next heart beat
-        next_strike (int): the tick that the hero can do the next attack
+        next_heal (float): ms until the hero gains back healing ability
+        next_beat (float): time until next heart beat (in ms)
+        next_strike (float): time until the hero can do the next attack (in ms)
         slashing (bool): flag indicates if the hero is doing close-range attack
         firing (bool): flag indicates if the hero is doing long-range attack
         dead (bool): flag indicates if the hero is dead
@@ -60,7 +58,7 @@ class Hero:
         self.angle, self.color = -pi * 3 / 4, TANGO['Aluminium']
         self.R = (w * h / sin(pi*2/3) / 624) ** 0.5
 
-        self.next_heal = self.next_beat = self.next_strike = 0
+        self.next_heal = self.next_beat = self.next_strike = 0.0
         self.slashing = self.firing = self.dead = False
         self.spin_speed = fps / HERO_HP
         self.spin_queue = self.wound = 0.0
@@ -72,19 +70,24 @@ class Hero:
         if self.dead:
             self.spin_queue = 0.0
             return
-        old_speed, time = self.spin_speed, get_ticks()
+        old_speed = self.spin_speed
         self.spin_speed = fps / (HERO_HP-self.wound**0.5)
         self.spin_queue *= self.spin_speed / old_speed
-        if time >= self.next_heal:
+        if self.next_heal <= 0:
             self.wound -= HEAL_SPEED / self.spin_speed / HERO_HP
             if self.wound < 0: self.wound = 0.0
-        if time >= self.next_beat:
+        else:
+            self.next_heal -= 1000.0 / fps
+        if self.next_beat <= 0:
             play(self.sfx_heart)
-            self.next_beat = time + MIN_BEAT*(2 - self.wound/HERO_HP)
+            self.next_beat = MIN_BEAT*(2 - self.wound/HERO_HP)
+        else:
+            self.next_beat -= 1000.0 / fps
+        self.next_strike -= 1000.0 / fps
 
         full_spin = pi * 2 / self.get_sides()
-        if self.slashing and time >= self.next_strike:
-            self.next_strike = time + ATTACK_SPEED
+        if self.slashing and self.next_strike <= 0:
+            self.next_strike = ATTACK_SPEED
             self.spin_queue = randsign() * self.spin_speed
             self.angle -= sign(self.spin_queue) * full_spin
         if abs(self.spin_queue) > 0.5:
@@ -97,7 +100,7 @@ class Hero:
         """Return the number of sides the hero has. While the hero is
         generally a trigon, Agent Orange may turn him into a square.
         """
-        return 3 if get_ticks() >= self.next_heal else 4
+        return 3 if self.next_heal <= 0 else 4
 
     def update_angle(self, angle):
         """Turn to the given angle if the hero is not busy slashing."""
@@ -106,7 +109,7 @@ class Hero:
         unit = pi * 2 / self.get_sides() / self.spin_speed
         if abs(delta) < unit:
             self.angle, self.spin_queue = angle, 0.0
-        elif get_ticks() >= self.next_strike:
+        elif self.next_strike <= 0:
             self.spin_queue = delta / unit
 
     def get_color(self):
@@ -134,7 +137,7 @@ class Enemy:
         angle (float): angle of the direction the enemy pointing (in radians)
         color (str): enemy's color name
         awake (bool): flag indicates if the enemy is active
-        next_strike (int): the tick that the enemy can do the next attack
+        next_strike (float): time until the enemy's next action (in ms)
         move_speed (float): speed of movement (in frames per grid)
         offsetx, offsety (integer): steps moved from the center of the grid
         spin_speed (float): speed of spinning (in frames per slash)
@@ -149,7 +152,7 @@ class Enemy:
         self.angle, self.color = pi / 4, color
 
         self.awake = False
-        self.next_strike = 0
+        self.next_strike = 0.0
         self.move_speed = self.maze.fps / ENEMY_SPEED
         self.offsetx = self.offsety = 0
         self.spin_speed = self.maze.fps / ENEMY_HP
@@ -207,11 +210,11 @@ class Enemy:
         if self.maze.hero.dead: return False
         x, y = self.get_pos()
         if (self.maze.get_distance(x, y) > FIRANGE*self.maze.distance
-            or get_ticks() < self.next_strike
+            or self.next_strike > 0
             or (self.x, self.y) in AROUND_HERO or self.offsetx or self.offsety
             or randrange((self.maze.hero.slashing+self.maze.isfast()+1) * 3)):
             return False
-        self.next_strike = get_ticks() + ATTACK_SPEED
+        self.next_strike = ATTACK_SPEED
         self.maze.bullets.append(
             Bullet(self.maze.surface, x, y, self.get_angle() + pi, self.color))
         return True
@@ -224,7 +227,7 @@ class Enemy:
         if self.offsety:
             self.offsety -= sign(self.offsety)
             return True
-        if get_ticks() < self.next_strike: return False
+        if self.next_strike > 0: return False
 
         self.move_speed = self.maze.fps / speed
         directions = [(sign(MIDDLE - self.x), 0), (0, sign(MIDDLE - self.y))]
@@ -267,7 +270,7 @@ class Enemy:
 
     def draw(self):
         """Draw the enemy."""
-        if get_ticks() < self.maze.next_move and not self.awake: return
+        if self.maze.next_move > 0 and not self.awake: return
         radius = self.maze.distance/SQRT2 - self.awake*2
         square = regpoly(4, radius, self.angle, *self.get_pos())
         fill_aapolygon(self.maze.surface, square, self.get_color())
@@ -277,6 +280,7 @@ class Enemy:
         if self.awake:
             self.spin_speed, tmp = self.maze.fps / ENEMY_HP, self.spin_speed
             self.spin_queue *= self.spin_speed / tmp
+            self.next_strike -= 1000.0 / self.maze.fps
             if not self.spin_queue and not self.fire() and not self.move():
                 self.spin_queue = randsign() * self.spin_speed
                 if not self.maze.hero.dead:
@@ -305,25 +309,30 @@ class Chameleon(Enemy):
     """Object representing an enemy of Chameleon.
 
     Additional attributes:
-        visible (int): the tick until which the Chameleon is visible
+        visible (float): time until the Chameleon is visible (in ms)
     """
     def __init__(self, maze, x, y):
         Enemy.__init__(self, maze, x, y, 'Chameleon')
-        self.visible = 0
+        self.visible = 0.0
 
     def wake(self):
         """Wake the Chameleon up if it can see the hero."""
         if Enemy.wake(self) is True:
-            self.visible = get_ticks() + 1000//ENEMY_SPEED
+            self.visible = 1000.0 / ENEMY_SPEED
 
     def draw(self):
         """Draw the Chameleon."""
-        if not self.awake or get_ticks() < self.visible or self.spin_queue:
+        if not self.awake or self.visible > 0 or self.spin_queue:
             Enemy.draw(self)
 
+    def update(self):
+        """Update the Chameleon."""
+        Enemy.update(self)
+        if self.awake: self.visible -= 1000.0 / self.maze.fps
+
     def hit(self, wound):
         """Handle the Chameleon when it's attacked."""
-        self.visible = get_ticks() + 1000//ENEMY_SPEED
+        self.visible = 1000.0 / ENEMY_SPEED
         Enemy.hit(self, wound)
 
 
diff --git a/brutalmaze/constants.py b/brutalmaze/constants.py
index 8b0acf8..cb65d88 100644
--- a/brutalmaze/constants.py
+++ b/brutalmaze/constants.py
@@ -52,7 +52,7 @@ HEAL_SPEED = 1  # HP/s
 HERO_SPEED = 5  # grid/s
 ENEMY_SPEED = 6 # grid/s
 BULLET_SPEED = 15   # grid/s
-ATTACK_SPEED = 333  # ms/strike
+ATTACK_SPEED = 333.333  # ms/strike
 FIRANGE = 6     # grids
 BULLET_LIFETIME = 1000.0 * FIRANGE / (BULLET_SPEED-HERO_SPEED)  # ms
 EMPTY, WALL, HERO, ENEMY = range(4)
diff --git a/brutalmaze/game.py b/brutalmaze/game.py
index 963b55c..f6acb99 100644
--- a/brutalmaze/game.py
+++ b/brutalmaze/game.py
@@ -17,7 +17,7 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with Brutal Maze.  If not, see <https://www.gnu.org/licenses/>.
 
-__version__ = '0.5.6'
+__version__ = '0.6.0'
 
 import re
 from argparse import ArgumentParser, FileType, RawTextHelpFormatter
@@ -26,13 +26,12 @@ try:                    # Python 3
     from configparser import ConfigParser
 except ImportError:     # Python 2
     from ConfigParser import ConfigParser
-from math import atan2, degrees, radians, pi
+from math import atan2, radians, pi
 from os.path import join, pathsep
 from socket import socket, SOL_SOCKET, SO_REUSEADDR
 from sys import stdout
 from threading import Thread
 
-
 import pygame
 from pygame import DOUBLEBUF, KEYDOWN, OPENGL, QUIT, RESIZABLE, VIDEORESIZE
 from pygame.time import Clock, get_ticks
@@ -147,21 +146,21 @@ class Game:
 
     def export(self):
         """Export maze data to a bytes object."""
-        maze, hero, time = self.maze, self.hero, get_ticks()
+        maze, hero, = self.maze, self.hero
         lines = deque(['{0} {4} {5} {1} {2:d} {3:d}'.format(
             COLORS[hero.get_color()], deg(self.hero.angle),
-            hero.next_strike <= time, hero.next_heal <= time,
+            hero.next_strike <= 0, hero.next_heal <= 0,
             *self.expos(maze.x, maze.y))])
 
         walls = [[1 if maze.map[x][y] == WALL else 0 for x in maze.rangex]
-                 for y in maze.rangey] if maze.next_move <= time else []
+                 for y in maze.rangey] if maze.next_move <= 0 else []
         ne = nb = 0
 
         for enemy in maze.enemies:
             if not enemy.awake and walls:
                 walls[enemy.y-maze.rangey[0]][enemy.x-maze.rangex[0]] = WALL
                 continue
-            elif enemy.color == 'Chameleon' and maze.next_move <= time:
+            elif enemy.color == 'Chameleon' and maze.next_move <= 0:
                 continue
             lines.append('{0} {2} {3} {1:.0f}'.format(
                 COLORS[enemy.get_color()], deg(enemy.angle),
@@ -212,20 +211,18 @@ class Game:
             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)
-            if not self.headless: self.maze.draw()
+        if not self.paused: self.maze.update(self.fps)
+        if not self.headless: self.maze.draw()
         self.clock.tick(self.fps)
         return True
 
     def move(self, x, y):
         """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
         velocity = self.maze.distance * HERO_SPEED / self.fps
         accel = velocity * HERO_SPEED / self.fps
 
-        if stunned or not x:
+        if self.maze.next_move > 0 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:
@@ -234,7 +231,7 @@ class Game:
             self.maze.vx += x * accel
             if abs(self.maze.vx) > velocity: self.maze.vx = x * velocity
 
-        if stunned or not y:
+        if self.maze.next_move > 0 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:
diff --git a/brutalmaze/maze.py b/brutalmaze/maze.py
index b25662e..c3d1ff7 100644
--- a/brutalmaze/maze.py
+++ b/brutalmaze/maze.py
@@ -24,7 +24,6 @@ from math import pi, log
 from random import choice, getrandbits, uniform
 
 import pygame
-from pygame.time import get_ticks
 
 from .characters import Hero, new_enemy
 from .constants import (
@@ -75,8 +74,8 @@ class Maze:
         enemy_weights (dict): probabilities of enemies to be created
         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
+        next_move (float): time until the hero gets mobilized (in ms)
+        next_slashfx (float): time until next slash effect of the hero (in ms)
         slashd (float): minimum distance for slashes to be effective
         sfx_slash (pygame.mixer.Sound): sound effect of slashed enemy
         sfx_lose (pygame.mixer.Sound): sound effect to be played when you lose
@@ -107,7 +106,7 @@ class Maze:
         self.add_enemy()
         self.hero = Hero(self.surface, fps, size)
         self.map[MIDDLE][MIDDLE] = HERO
-        self.next_move = self.next_slashfx = 0
+        self.next_move = self.next_slashfx = 0.0
         self.slashd = self.hero.R + self.distance/SQRT2
 
         self.sfx_spawn = SFX_SPAWN
@@ -144,7 +143,7 @@ class Maze:
     def draw(self):
         """Draw the maze."""
         self.surface.fill(BG_COLOR)
-        if get_ticks() >= self.next_move:
+        if self.next_move <= 0:
             for i in self.rangex:
                 for j in self.rangey:
                     if self.map[i][j] != WALL: continue
@@ -217,13 +216,12 @@ class Maze:
         """Handle the hero when he loses HP."""
         fx = (uniform(0, sum(self.enemy_weights.values()))
               < self.enemy_weights[color])
-        time = get_ticks()
         if (color == 'Butter' or color == 'ScarletRed') and fx:
             self.hero.wound += wound * 2.5
         elif color == 'Orange' and fx:
-            self.hero.next_heal = max(self.hero.next_heal, time) + wound*1000
+            self.hero.next_heal = max(self.hero.next_heal, 0) + wound*1000
         elif color == 'SkyBlue' and fx:
-            self.next_move = max(self.next_move, time) + wound*1000
+            self.next_move = max(self.next_move, 0) + wound*1000
         else:
             self.hero.wound += wound
         if self.enemy_weights[color] + wound < MAXW:
@@ -238,10 +236,10 @@ class Maze:
         for i, enemy in enumerate(self.enemies):
             d = self.slashd - enemy.get_distance()
             if d > 0:
-                wound, time = d * SQRT2 / self.distance, get_ticks()
-                if time >= self.next_slashfx:
+                wound = d * SQRT2 / self.distance
+                if self.next_slashfx <= 0:
                     play(self.sfx_slash, wound, enemy.get_angle())
-                    self.next_slashfx = time + ATTACK_SPEED
+                    self.next_slashfx = ATTACK_SPEED
                 enemy.hit(wound / self.hero.spin_speed)
                 if enemy.wound >= ENEMY_HP:
                     self.score += enemy.wound
@@ -252,21 +250,21 @@ class Maze:
 
     def track_bullets(self):
         """Handle the bullets."""
-        fallen, time = [], get_ticks()
+        fallen = []
         if (self.hero.firing and not self.hero.slashing
-            and time >= self.hero.next_strike):
-            self.hero.next_strike = time + ATTACK_SPEED
+            and self.hero.next_strike <= 0):
+            self.hero.next_strike = ATTACK_SPEED
             self.bullets.append(Bullet(self.surface, self.x, self.y,
                                        self.hero.angle, 'Aluminium'))
         for i, bullet in enumerate(self.bullets):
-            wound = float(bullet.fall_time-time) / BULLET_LIFETIME
+            wound = bullet.fall_time / BULLET_LIFETIME
             bullet.update(self.fps, self.distance)
             if wound < 0:
                 fallen.append(i)
             elif bullet.color == 'Aluminium':
                 x = MIDDLE + round2((bullet.x-self.x) / self.distance)
                 y = MIDDLE + round2((bullet.y-self.y) / self.distance)
-                if self.map[x][y] == WALL and time >= self.next_move:
+                if self.map[x][y] == WALL and self.next_move <= 0:
                     fallen.append(i)
                     continue
                 for j, enemy in enumerate(self.enemies):
@@ -282,9 +280,9 @@ class Maze:
                         fallen.append(i)
                         break
             elif bullet.get_distance(self.x, self.y) < self.distance:
-                if self.hero.spin_queue and time >= self.hero.next_heal:
+                if self.hero.spin_queue and self.hero.next_heal <= 0:
                     self.hero.next_strike = (abs(self.hero.spin_queue*self.fps)
-                                             + time + ATTACK_SPEED)
+                                             + ATTACK_SPEED)
                     play(bullet.sfx_missed, wound, bullet.angle + pi)
                 else:
                     self.hit_hero(wound, bullet.color)
@@ -319,6 +317,9 @@ class Maze:
         dy = self.is_valid_move(vy=self.vy)
         self.centery += dy
 
+        self.next_move -= 1000.0 / self.fps
+        self.next_slashfx -= 1000.0 / self.fps
+
         if dx or dy:
             self.rotate()
             for enemy in self.enemies: enemy.wake()
@@ -370,7 +371,7 @@ class Maze:
         self.enemy_weights = {color: MINW for color in ENEMIES}
         self.add_enemy()
 
-        self.next_move = self.next_slashfx = 0
+        self.next_move = self.next_slashfx = 0.0
         self.hero.next_heal = self.hero.next_strike = 0
         self.hero.slashing = self.hero.firing = self.hero.dead = False
         self.hero.spin_queue = self.hero.wound = 0.0
diff --git a/brutalmaze/weapons.py b/brutalmaze/weapons.py
index f99d694..7eae843 100644
--- a/brutalmaze/weapons.py
+++ b/brutalmaze/weapons.py
@@ -21,8 +21,6 @@ __doc__ = 'Brutal Maze module for weapon classes'
 
 from math import cos, sin
 
-from pygame.time import get_ticks
-
 from .constants import (BULLET_LIFETIME, SFX_SHOT_ENEMY, SFX_SHOT_HERO,
                         SFX_MISSED, BULLET_SPEED, ENEMY_HP, TANGO, BG_COLOR)
 from .misc import regpoly, fill_aapolygon
@@ -36,14 +34,14 @@ class Bullet:
         x, y (int): coordinates of the center of the bullet (in pixels)
         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
+        fall_time (int): time until the bullet fall down
         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
+        self.fall_time = BULLET_LIFETIME
         if color == 'Aluminium':
             self.sfx_hit = SFX_SHOT_ENEMY
         else:
@@ -55,10 +53,11 @@ class Bullet:
         s = distance * BULLET_SPEED / fps
         self.x += s * cos(self.angle)
         self.y += s * sin(self.angle)
+        self.fall_time -= 1000.0 / fps
 
     def get_color(self):
         """Return current color of the enemy."""
-        value = int((1-(self.fall_time-get_ticks())/BULLET_LIFETIME)*ENEMY_HP)
+        value = int((1 - self.fall_time/BULLET_LIFETIME) * ENEMY_HP)
         try:
             return TANGO[self.color][value]
         except IndexError: