Partially implement long-ranged attacks

This commit is contained in:
Nguyễn Gia Phong 2017-10-21 17:40:10 +07:00
parent 81d0b68811
commit daae5de9b7
6 changed files with 118 additions and 22 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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

48
brutalmaze/weapons.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
#
# 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