#!/usr/bin/python
# coding=utf-8
import os, pygame, time, random, uuid, sys
class myRect(pygame.Rect):
""" Add type property """
def __init__(self, left, top, width, height, type):
pygame.Rect.__init__(self, left, top, width, height)
self.type = type
class Timer(object):
def __init__(self):
self.timers = []
def add(self, interval, f, repeat = -1):
options = {
"interval" : interval,
"callback" : f,
"repeat" : repeat,
"times" : 0,
"time" : 0,
"uuid" : uuid.uuid4()
}
self.timers.append(options)
return options["uuid"]
def destroy(self, uuid_nr):
for timer in self.timers:
if timer["uuid"] == uuid_nr:
self.timers.remove(timer)
return
def update(self, time_passed):
for timer in self.timers:
timer["time"] += time_passed
if timer["time"] > timer["interval"]:
timer["time"] -= timer["interval"]
timer["times"] += 1
if timer["repeat"] > -1 and timer["times"] == timer["repeat"]:
self.timers.remove(timer)
try:
timer["callback"]()
except:
try:
self.timers.remove(timer)
except:
pass
class Castle():
""" Player's castle/fortress """
(STATE_STANDING, STATE_DESTROYED, STATE_EXPLODING) = range(3)
def __init__(self):
global sprites
# images
self.img_undamaged = sprites.subsurface(0, 15*2, 16*2, 16*2)
self.img_destroyed = sprites.subsurface(16*2, 15*2, 16*2, 16*2)
# init position
self.rect = pygame.Rect(12*16, 24*16, 32, 32)
# start w/ undamaged and shiny castle
self.rebuild()
def draw(self):
""" Draw castle """
global screen
screen.blit(self.image, self.rect.topleft)
if self.state == self.STATE_EXPLODING:
if not self.explosion.active:
self.state = self.STATE_DESTROYED
del self.explosion
else:
self.explosion.draw()
def rebuild(self):
""" Reset castle """
self.state = self.STATE_STANDING
self.image = self.img_undamaged
self.active = True
def destroy(self):
""" Destroy castle """
self.state = self.STATE_EXPLODING
self.explosion = Explosion(self.rect.topleft)
self.image = self.img_destroyed
self.active = False
class Bonus():
""" Various power-ups
When bonus is spawned, it begins flashing and after some time dissapears
Available bonusses:
grenade : Picking up the grenade power up instantly wipes out ever enemy presently on the screen, including Armor Tanks regardless of how many times you've hit them. You do not, however, get credit for destroying them during the end-stage bonus points.
helmet : The helmet power up grants you a temporary force field that makes you invulnerable to enemy shots, just like the one you begin every stage with.
shovel : The shovel power up turns the walls around your fortress from brick to stone. This makes it impossible for the enemy to penetrate the wall and destroy your fortress, ending the game prematurely. The effect, however, is only temporary, and will wear off eventually.
star : The star power up grants your tank with new offensive power each time you pick one up, up to three times. The first star allows you to fire your bullets as fast as the power tanks can. The second star allows you to fire up to two bullets on the screen at one time. And the third star allows your bullets to destroy the otherwise unbreakable steel walls. You carry this power with you to each new stage until you lose a life.
tank : The tank power up grants you one extra life. The only other way to get an extra life is to score 20000 points.
timer : The timer power up temporarily freezes time, allowing you to harmlessly approach every tank and destroy them until the time freeze wears off.
"""
# bonus types
(BONUS_GRENADE, BONUS_HELMET, BONUS_SHOVEL, BONUS_STAR, BONUS_TANK, BONUS_TIMER) = range(6)
def __init__(self, level):
global sprites
# to know where to place
self.level = level
# bonus lives only for a limited period of time
self.active = True
# blinking state
self.visible = True
self.rect = pygame.Rect(random.randint(0, 416-32), random.randint(0, 416-32), 32, 32)
self.bonus = random.choice([
self.BONUS_GRENADE,
self.BONUS_HELMET,
self.BONUS_SHOVEL,
self.BONUS_STAR,
self.BONUS_TANK,
self.BONUS_TIMER
])
self.image = sprites.subsurface(16*2*self.bonus, 32*2, 16*2, 15*2)
def draw(self):
""" draw bonus """
global screen
if self.visible:
screen.blit(self.image, self.rect.topleft)
def toggleVisibility(self):
""" Toggle bonus visibility """
self.visible = not self.visible
class Bullet():
# direction constants
(DIR_UP, DIR_RIGHT, DIR_DOWN, DIR_LEFT) = range(4)
# bullet's stated
(STATE_REMOVED, STATE_ACTIVE, STATE_EXPLODING) = range(3)
(OWNER_PLAYER, OWNER_ENEMY) = range(2)
def __init__(self, level, position, direction, damage = 100, speed = 5):
global sprites
self.level = level
self.direction = direction
self.damage = damage
self.owner = None
self.owner_class = None
# 1-regular everyday normal bullet
# 2-can destroy steel
self.power = 1
self.image = sprites.subsurface(75*2, 74*2, 3*2, 4*2)
# position is player's top left corner, so we'll need to
# recalculate a bit. also rotate image itself.
if direction == self.DIR_UP:
self.rect = pygame.Rect(position[0] + 11, position[1] - 8, 6, 8)
elif direction == self.DIR_RIGHT:
self.image = pygame.transform.rotate(self.image, 270)
self.rect = pygame.Rect(position[0] + 26, position[1] + 11, 8, 6)
elif direction == self.DIR_DOWN:
self.image = pygame.transform.rotate(self.image, 180)
self.rect = pygame.Rect(position[0] + 11, position[1] + 26, 6, 8)
elif direction == self.DIR_LEFT:
self.image = pygame.transform.rotate(self.image, 90)
self.rect = pygame.Rect(position[0] - 8 , position[1] + 11, 8, 6)
self.explosion_images = [
sprites.subsurface(0, 80*2, 32*2, 32*2),
sprites.subsurface(32*2, 80*2, 32*2, 32*2),
]
self.speed = speed
self.state = self.STATE_ACTIVE
def draw(self):
""" draw bullet """
global screen
if self.state == self.STATE_ACTIVE:
screen.blit(self.image, self.rect.topleft)
elif self.state == self.STATE_EXPLODING:
self.explosion.draw()
def update(self):
global castle, players, enemies, bullets
if self.state == self.STATE_EXPLODING:
if not self.explosion.active:
self.destroy()
del self.explosion
if self.state != self.STATE_ACTIVE:
return
""" move bullet """
if self.direction == self.DIR_UP:
self.rect.topleft = [self.rect.left, self.rect.top - self.speed]
if self.rect.top < 0:
if play_sounds and self.owner == self.OWNER_PLAYER:
sounds["steel"].play()
self.explode()
return
elif self.direction == self.DIR_RIGHT:
self.rect.topleft = [self.rect.left + self.speed, self.rect.top]
if self.rect.left > (416 - self.rect.width):
if play_sounds and self.owner == self.OWNER_PLAYER:
sounds["steel"].play()
self.explode()
return
elif self.direction == self.DIR_DOWN:
self.rect.topleft = [self.rect.left, self.rect.top + self.speed]
if self.rect.top > (416 - self.rect.height):
if play_sounds and self.owner == self.OWNER_PLAYER:
sounds["steel"].play()
self.explode()
return
elif self.direction == self.DIR_LEFT:
self.rect.topleft = [self.rect.left - self.speed, self.rect.top]
if self.rect.left < 0:
if play_sounds and self.owner == self.OWNER_PLAYER:
sounds["steel"].play()
self.explode()
return
has_collided = False
# check for collisions with walls. one bullet can destroy several (1 or 2)
# tiles but explosion remains 1
rects = self.level.obstacle_rects
collisions = self.rect.collidelistall(rects)
if collisions != []:
for i in collisions:
if self.level.hitTile(rects[i].topleft, self.power, self.owner == self.OWNER_PLAYER):
has_collided = True
if has_collided:
self.explode()
return
# check for collisions with other bullets
for bullet in bullets:
if self.state == self.STATE_ACTIVE and bullet.owner != self