From daae5de9b7531a5401c88b1c3e5c0e6109b9e40b Mon Sep 17 00:00:00 2001 From: Nguyễn Gia Phong Date: Sat, 21 Oct 2017 17:40:10 +0700 Subject: Partially implement long-ranged attacks --- brutalmaze/characters.py | 8 +++--- brutalmaze/constants.py | 5 ++-- brutalmaze/main.py | 2 ++ brutalmaze/maze.py | 72 +++++++++++++++++++++++++++++++++++++----------- brutalmaze/utils.py | 5 ++++ brutalmaze/weapons.py | 48 ++++++++++++++++++++++++++++++++ 6 files changed, 118 insertions(+), 22 deletions(-) create mode 100644 brutalmaze/weapons.py diff --git a/brutalmaze/characters.py b/brutalmaze/characters.py index c91da56..029b78d 100644 --- a/brutalmaze/characters.py +++ b/brutalmaze/characters.py @@ -26,7 +26,7 @@ from random import shuffle import pygame from .constants import * -from .utils import randsign, regpoly, fill_aapolygon, pos, sign +from .utils import round2, randsign, regpoly, fill_aapolygon, pos, sign class Hero: @@ -38,7 +38,7 @@ class Hero: self.angle, self.color = pi / 4, TANGO['Aluminium'] self.R = int((w * h / sin(pi*2/3) / 624) ** 0.5) - self.spin_speed = int(round(fps / len(self.color))) + self.spin_speed = round2(fps / len(self.color)) self.spin_queue, self.slashing = deque(), False self.wound = 0.0 @@ -61,7 +61,7 @@ class Hero: def update(self, fps): """Update the hero.""" - self.spin_speed = int(round(fps / (len(self.color)-self.wound))) + self.spin_speed = round2(fps / (len(self.color)-self.wound)) self.wound -= HEAL_SPEED / len(self.color) / self.spin_speed if self.wound < 0: self.wound = 0.0 @@ -111,7 +111,7 @@ class Enemy: fill_aapolygon(self.surface, square, color) def place(self, x=0, y=0): - """Move the enemy by (x, y).""" + """Move the enemy by (x, y) (in grids).""" self.x += x self.y += y self.maze[self.x][self.y] = ENEMY diff --git a/brutalmaze/constants.py b/brutalmaze/constants.py index fb10e28..4d750ee 100644 --- a/brutalmaze/constants.py +++ b/brutalmaze/constants.py @@ -31,13 +31,14 @@ GOLDEN_MEAN = 5**0.5/2 + 0.5 INIT_FPS = 30.0 SIZE = 640, 480 MAZE_SIZE = 10 -ROAD_WIDTH = 5 -CELL_WIDTH = ROAD_WIDTH * 2 +ROAD_WIDTH = 5 # grids +CELL_WIDTH = ROAD_WIDTH * 2 # grids MIDDLE = (MAZE_SIZE + MAZE_SIZE%2 - 1)*ROAD_WIDTH + ROAD_WIDTH//2 LAST_ROW = (MAZE_SIZE-1) * ROAD_WIDTH * 2 INIT_SCORE = 208.2016 MOVE_SPEED = 5 # grid/s HEAL_SPEED = 1.0 # HP/s +BULLET_LIFETIME = 1000 # milliseconds EMPTY, WALL, HERO, ENEMY = range(4) ADJACENT_GRIDS = (1, 0), (0, 1), (-1, 0), (0, -1) diff --git a/brutalmaze/main.py b/brutalmaze/main.py index 7a1dbf0..1874ed6 100644 --- a/brutalmaze/main.py +++ b/brutalmaze/main.py @@ -64,6 +64,8 @@ def main(): if event.button == 1: maze.hero.slashing = True maze.hero.slash() + if event.button == 3: + maze.fire() elif event.type == MOUSEBUTTONUP: if event.button == 1: maze.hero.slashing = False diff --git a/brutalmaze/maze.py b/brutalmaze/maze.py index 85187a7..f78c1c5 100644 --- a/brutalmaze/maze.py +++ b/brutalmaze/maze.py @@ -25,7 +25,8 @@ import pygame from .characters import Hero, Enemy from .constants import * -from .utils import pos, sign, cosin, length, regpoly, fill_aapolygon +from .utils import round2, pos, sign, cosin, length, regpoly, fill_aapolygon +from .weapons import Bullet __doc__ = 'brutalmaze module for the maze class' @@ -68,12 +69,11 @@ class Maze: self.map = deque() for _ in range(MAZE_SIZE): self.map.extend(new_column()) self.right = self.down = self.rotatex = self.rotatey = 0 - self.enemies = [] + self.bullets, self.enemies = [], [] self.add_enemy() self.hero = Hero(self.surface, fps) self.map[MIDDLE][MIDDLE] = HERO self.slashd = self.hero.R + self.distance/SQRT2 - self.draw() def add_enemy(self): """Add enough enemies.""" @@ -118,7 +118,9 @@ class Maze: def rotate(self, x, y): """Rotate the maze by (x, y).""" + if not x and not y: return for enemy in self.enemies: self.map[enemy.x][enemy.y] = EMPTY + if x: self.offsetx = 0.0 self.map.rotate(x) @@ -158,7 +160,15 @@ class Maze: self.map[c + k][LAST_ROW + j] = grid def slash(self): - """Slash the enemies.""" + """Handle close-ranged attacks.""" + for enemy in self.enemies: + if not enemy.spin_queue: continue + x, y = enemy.pos(self.distance, self.middlex, self.middley) + d = self.slashd - length(x, y, self.x, self.y) + if d >= 0: + self.hero.wound += d / self.hero.R / enemy.spin_speed + + if not self.hero.slashing: return unit, killist = self.distance/SQRT2 * self.hero.spin_speed, [] for i, enemy in enumerate(self.enemies): x, y = enemy.pos(self.distance, self.middlex, self.middley) @@ -172,6 +182,35 @@ class Maze: for i in reversed(killist): self.enemies.pop(i) self.add_enemy() + def track_bullets(self): + """Handle the bullets.""" + fallen, time = [], pygame.time.get_ticks() + for i, bullet in enumerate(self.bullets): + wound = float(bullet.fall_time-time) / BULLET_LIFETIME + bullet.update(self.fps, self.distance) + if wound < 0: + fallen.append(i) + elif bullet.color == FG_COLOR: + x = MIDDLE + round2((bullet.x-self.x) / self.distance) + y = MIDDLE + round2((bullet.y-self.y) / self.distance) + if self.map[x][y] == WALL: + fallen.append(i) + continue + for j, enemy in enumerate(self.enemies): + x, y = enemy.pos(self.distance, self.middlex, self.middley) + if length(bullet.x, bullet.y, x, y) < self.distance: + enemy.wound += wound + if enemy.wound >= len(enemy.color): + self.score += enemy.wound + enemy.die() + self.enemies.pop(j) + fallen.append(i) + break + elif length(bullet.x, bullet.y, self.x, self.y) < self.distance: + self.hero.wound += wound + fallen.append(i) + for i in reversed(fallen): self.bullets.pop(i) + def update(self, fps): """Update the maze.""" self.offsetx *= fps / self.fps @@ -179,7 +218,7 @@ class Maze: self.fps, self.speed = fps, fps / MOVE_SPEED self.step = self.distance / self.speed - modified, d = False, self.distance*1.5 - self.hero.R + dx, dy, d = 0, 0, self.distance*1.5 - self.hero.R if self.right: self.offsetx += self.right s = sign(self.offsetx) * 2 @@ -189,7 +228,7 @@ class Maze: and abs(self.offsetx*self.step) > d): self.offsetx -= self.right else: - modified = True + dx = self.right if self.down: self.offsety += self.down s = sign(self.offsety) * 2 @@ -199,9 +238,9 @@ class Maze: and abs(self.offsety*self.step) > d): self.offsety -= self.down else: - modified = True + dy = self.down - if modified: + if dx or dy: self.map[MIDDLE][MIDDLE] = EMPTY self.rotate(sign(self.offsetx) * (abs(self.offsetx)>=self.speed), sign(self.offsety) * (abs(self.offsety)>=self.speed)) @@ -210,18 +249,14 @@ class Maze: self.middley = self.y + self.offsety*self.step for enemy in self.enemies: if not enemy.awake: self.wake(enemy) + for bullet in self.bullets: bullet.place(dx, dy, self.step) self.draw() for enemy in self.enemies: enemy.update(fps, self.distance, self.middlex, self.middley) self.hero.update(fps) - if self.hero.slashing: self.slash() - for enemy in self.enemies: - if not enemy.spin_queue: continue - x, y = enemy.pos(self.distance, self.middlex, self.middley) - d = self.slashd - length(x, y, self.x, self.y) - if d >= 0: - self.hero.wound += d / self.hero.R / enemy.spin_speed + self.slash() + self.track_bullets() pygame.display.flip() pygame.display.set_caption('Brutal Maze - Score: {}'.format( int(self.score - INIT_SCORE))) @@ -253,6 +288,11 @@ class Maze: self.down += y self.right, self.down = sign(self.right), sign(self.down) + def fire(self): + """Create a bullet shot from the hero.""" + self.bullets.append( + Bullet(self.surface, self.x, self.y, self.hero.angle, FG_COLOR)) + def lose(self): """Handle loses.""" - pygame.quit() + quit() diff --git a/brutalmaze/utils.py b/brutalmaze/utils.py index d198ef5..f972fc8 100644 --- a/brutalmaze/utils.py +++ b/brutalmaze/utils.py @@ -27,6 +27,11 @@ from pygame.gfxdraw import filled_polygon, aapolygon from .constants import * +def round2(number): + """Round a number to an int.""" + return int(round(number)) + + def randsign(): """Return either -1 or 1 (kind of) randomly.""" return (pygame.time.get_ticks() & 1)*2 - 1 diff --git a/brutalmaze/weapons.py b/brutalmaze/weapons.py new file mode 100644 index 0000000..108b042 --- /dev/null +++ b/brutalmaze/weapons.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# characters.py - module for weapon classes +# This file is part of brutalmaze +# +# brutalmaze is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# brutalmaze is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with brutalmaze. If not, see . +# +# Copyright (C) 2017 Nguyễn Gia Phong + +__doc__ = 'brutalmaze module for weapon classes' + +from math import pi, cos, sin + +from pygame.time import get_ticks + +from .constants import * +from .utils import randsign, regpoly, fill_aapolygon, pos, sign + + +class Bullet: + """Object representing a bullet.""" + 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 + + def update(self, fps, distance): + """Update the bullet.""" + s = distance * 8 / fps + self.x += s * cos(self.angle) + self.y += s * sin(self.angle) + hexagon = regpoly(6, distance // 4, self.angle, self.x, self.y) + fill_aapolygon(self.surface, hexagon, self.color) + + def place(self, x, y, step): + """Move the bullet by (x, y) (in steps).""" + self.x += x * step + self.y += y * step -- cgit 1.4.1