#!/usr/bin/env python

from pygame import transform, font, Rect
from pygame.locals import *
import var, gfx, snd
from math import *
from random import randint, random

# *** TODO: make calculations use arena.left and arena.top 
#       instead of assuming 0,0
if __name__ != '__main__':
    # We were imported normally from gameplay.py
    arena = var.arena
else:
    # We are running objs.py standalone
    arena = Rect(0, 0, 800, 600)

fps_list = [30] * var.frames_per_sec

# Object lists:
#   - vapors for smoke and other "vapors"
#   - low for hard objects: ship and sun
#   - high for the fire and explosions
#   - virtual for help text, and non-colliding objects
#   - pend for non-rendered objects still being tracked
#   - list: rendered lists in rendering order
vapors, low, high, virtual, pend = [], [], [], [], []
list = [vapors, low, high, virtual]

images = {}

# Load all the images
def load_game_resources():
    global images

    # Ship images are 3D arrays [shipnum][phase][direction]
    for name,files in (
        ('ship',   ((0, "ship1-up.png"), (1, "ship2-up.png"),
                    (2, "ship3-up.png"), (3, "ship4-up.png"))),
        ('reverse',((0, "ship1-up-boost1.png"), (1, "ship2-up-boost1.png"),
                    (2, "ship3-up-boost1.png"), (3, "ship4-up-boost1.png"))),
        ('turbo',  ((0, "ship1-up-boost2.png"), (1, "ship2-up-boost2.png"),
                    (2, "ship3-up-boost2.png"), (3, "ship4-up-boost2.png")))):
        images[name]   = [[],[],[],[]]
        for ship, file in files:
            img = gfx.load(file)
            #img=transform.scale(img,(img.get_width()*2/3,img.get_height()*2/3))
            img_anim = gfx.animstrip(img)
            phases = len(img_anim)
            for p in range(phases):
                target = images[name]
                target[ship].append([])
                for i in range(0, 361, 360/var.compass_dirs):
                    target[ship][p].append(transform.rotate(img_anim[p],i))

    images['shield']  = []
    img = gfx.load('bonus-shield.png')
    #img = transform.scale(img, (img.get_width()*2/3, img.get_height()*2/3))
    img_anim = gfx.animstrip(img)
    alphas = [160,145,130,115,100,85,70,55,40,55,70,85,100,115,130,145]
    for p in range(len(alphas)):
        images['shield'].append([])
        for img in img_anim:
            newimg = img.convert()
            newimg.set_alpha(alphas[p], RLEACCEL)
            images['shield'][p].append(newimg)

    images['bullet']  = []
    img = gfx.load('bonus-bullet.png')
    #img = transform.scale(img, (img.get_width()*2/3, img.get_height()*2/3))
    img_anim = gfx.animstrip(img)
    alphas = [255,239,223,207,191,175,159,143,127,111,95,79,63]
    for p in range(len(img_anim)):
    #for p in range(len(alphas)):
        images['bullet'].append([])
        for alpha in alphas:
            newimg = img_anim[p].convert()
            newimg.set_alpha(alpha, RLEACCEL)
            images['bullet'][p].append(newimg)

    images['smoke'] = []
    img = gfx.load('smoke.png')
    #img = transform.scale(img, (img.get_width()*2/3, img.get_height()*2/3))
    img_anim = gfx.animstrip(img)
    if gfx.surface.get_bytesize()>1: #16 or 32bit
        i = 1
        for img in img_anim:
            img.set_alpha((1.8-log(i))*40, RLEACCEL)
            i += 1
    phases = len(img_anim)
    for p in range(phases):
        images['smoke'].append([])
        images['smoke'][p].append(img_anim[p])

    images['pop'] = []
    img = gfx.load('popshot.png')
    #img = transform.scale(img, (img.get_width()*2/3, img.get_height()*2/3))
    img_anim = gfx.animstrip(img, 18)
    phases = len(img_anim)
    for p in range(phases):
        images['pop'].append([])
        images['pop'][p].append(img_anim[p])

    # Images with no special needs
    for name,file in (('explosion', 'explosion.png'),
                      ('tele', 'ship-teleport.png'),
                      ('fire', 'fire.png'),
                      ('fire2', 'fire2.png'),
                      ('box', 'boxes.png'),
                      ('spike', 'spikeball.png'),
                      ('asteroid', 'asteroid.png'),
                      ('bobble', 'powerup.png')):
        images[name] = []
        img = gfx.load(file)
        #img = transform.scale(img, (img.get_width()*2/3, img.get_height()*2/3))
        img_anim = gfx.animstrip(img)
        phases = len(img_anim)
        for p in range(phases):
            images[name].append([])
            images[name][p].append(img_anim[p])


# All game play objects are a subclass of Mass
#   - Mass itself cannot be instantiated because
#     image_name must be defined
class Mass:
    phase_rate = 0.0
    phase_death = 0
    radius = 0
    taxonomy = ()

    def __init__(self, x, y, vx=0.0, vy=0.0, mass=1.0, dir=0, imgs=''):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy
        self.mass = mass
        self.dir = dir*var.compass_dirs/360

        if imgs:
            self.imgs = imgs
        else:
            self.imgs = images[self.image_name]
        self.phases = len(self.imgs)
        self.phase = 0.0
        self.ticks_to_live = -1
        self.width = self.imgs[0][self.dir].get_width()
        self.height = self.imgs[0][self.dir].get_height()
        self.rect = Rect(int(self.x)-self.width/2, int(self.y)-self.height/2,
                     self.width, self.height)
        self.lastrect = self.rect
        self.dead = 0

    def tick(self, speedadjust):
        self.x = (self.x + self.vx) % arena.right
        self.y = (self.y + self.vy) % arena.bottom

        self.rect = Rect(int(self.x)-self.width/2, int(self.y)-self.height/2,
                     self.width, self.height)
        if self.phase_death:
            self.phase = self.phase + self.phase_rate
            if not self.dead and self.phase >= self.phases:
                self.dead = 1
        else:
            self.phase = (self.phase+self.phase_rate)%self.phases
        if self.ticks_to_live > 0:
            self.ticks_to_live = self.ticks_to_live - 1
            if not self.dead and self.ticks_to_live == 0:
                self.dead = 1

    def draw(self):
        phase = int(self.phase)
        dir = self.dir
        gfx.surface.blit(self.imgs[phase][dir], self.rect)
        gfx.dirty2(self.rect, self.lastrect)
        self.lastrect = self.rect

    def erase(self):
        gfx.surface.fill(0, self.rect)
        if self.dead:
            gfx.dirty2(self.rect, self.lastrect)

    def distance(self, other_mass):
        xdist = self.x - other_mass.x
        ydist = self.y - other_mass.y
        return sqrt(xdist*xdist + ydist*ydist)

    # Return radian direction to other mass
    def direction(self, other_mass):
        xdist = self.x - other_mass.x
        ydist = self.y - other_mass.y
        if ydist == 0:
            ydist = 0.00000000000001
        rads = atan(xdist/ydist)
        # Adjust atan depending on direction quadrant
        # upper left stays the same
        # upper right
        if xdist < 0 and ydist > 0:
            rads = rads + 2.0*pi
        # lower left and lower right
        if ydist < 0:
            rads = rads + pi
        return rads

    # Return relative radian direction to other mass
    def rel_direction(self, other_mass):
        abs_rads = Mass.direction(self, other_mass)
        self_rads = self.dir*2*pi/var.compass_dirs
        rel_rads = (abs_rads-self_rads) % (2*pi)
        if rel_rads > pi:
            rel_rads -= 2*pi
        elif rel_rads < -pi:
            rel_rads += 2*pi
        return rel_rads

    def gravitate(self, other_mass):
        dist = self.distance(other_mass)
        if dist < 4.0: dist = 4.0
        force = other_mass.mass/(dist*dist)

        rads = self.direction(other_mass)
        self.vx = self.vx - sin(rads)*force*var.gravity_const
        self.vy = self.vy - cos(rads)*force*var.gravity_const

    def clearance(self, other_mass):
        # *** TODO: this needs to account for motion
        return self.distance(other_mass) - (self.radius + other_mass.radius)

    def check_spot(self):
        for object in low + high:
            if object is not self:
                if self.clearance(object) < var.start_clearance:
                    return 0
        return 1

    def find_spot(self):
        new_dir = randint(0, var.compass_dirs-1)
        while 1:
            self.x = randint(50, arena.right-50)
            self.y = randint(50, arena.bottom-50)
            if self.check_spot(): return
            ###found_spot = 1
            ###for object in low + high:
            ###    if object is not self:
            ###        if self.clearance(object) < var.start_clearance:
            ###            found_spot = 0

    def hit_by(self, other_mass):
        pass


class Smoke(Mass):
    image_name = 'smoke'
    taxonomy = ()
    radius = 5.0

    def __init__(self, x, y, vx, vy):
        Mass.__init__(self, x, y, vx, vy, mass=0.0)
        self.ticks_to_live = randint(12, 20)

    def tick(self, speedadjust):
        Mass.tick(self, speedadjust)
        self.phase = int(self.phases * (self.ticks_to_live / 20.0))

class Fire(Mass):
    image_name = 'fire'
    taxonomy = ('fire', 'damage', 'significant')
    radius = 8.0
    phase_rate = 0.20
    damage = var.fire_damage

    def __init__(self, x, y, vx, vy, owner):
        Mass.__init__(self, x, y, vx, vy, mass=0.0)
        self.ticks_to_live = var.fire_life
        self.owner = owner
        self.dead_by_hit = 0

    def hit_by(self, other_mass):
        if not self.dead:
            # If dead by another method, don't over-ride
            self.dead_by_hit = 1
            self.dead = 1

class SuperFire(Fire):
    image_name = 'fire2'
    taxonomy = ('fire', 'damage', 'significant')
    radius = 8.0
    phase_rate = 0.3
    damage = int(var.fire_damage * 1.5)

    def __init__(self, x, y, vx, vy, owner):
        Mass.__init__(self, x, y, vx, vy, mass=0.0)
        self.ticks_to_live = int(var.fire_life * 1.5)
        self.owner = owner
        self.dead_by_hit = 0

    def hit_by(self, other_mass):
        if not self.dead:
            # If dead by another method, don't over-ride
            self.dead_by_hit = 1
            self.dead = 1

class Pop(Mass):
    image_name = 'pop'
    radius = 8.0
    phase_rate = 0.2
    phase_death = 1

    def __init__(self, x, y, vx, vy):
        Mass.__init__(self, x, y, vx, vy, mass=0.0)

    def gravitate(self, other_mass):
        # No effect from gravity
        pass

class Explosion(Mass):
    image_name = 'explosion'
    taxonomy = ('explosion', 'damage', 'significant')
    radius = 18.0
    phase_rate = 0.5
    phase_death = 1

    def __init__(self, x, y, vx, vy):
        Mass.__init__(self, x, y, vx, vy, mass=0.0)
        snd.play('explode', 1.0, self.rect.centerx)

    def gravitate(self, other_mass):
        # No effect from gravity
        pass

class Sun(Mass):
    image_name = 'box'
    taxonomy = ('sun', 'hard', 'significant')
    radius = 10.0
    phase_rate = 0.4

    def __init__(self, x=arena.centerx, y=arena.centery):
        Mass.__init__(self, x, y, vx=0, vy=0, mass=10.0)

    def draw(self):
        if var.sun != 3:
            Mass.draw(self)

    def gravitate(self, other_mass):
        if var.sun >= 2:
            Mass.gravitate(self)

# Spike and Asteroid superclass. Don't instantiate
class Hard(Mass):
    def __init__(self, x, y, vx, vy):
        Mass.__init__(self, x, y, vx, vy, mass=4.0)

    def tick(self, speedadjust):
        Mass.tick(self, speedadjust)

        if self.pending_frames > 0:
            if self.pending_frames == var.frames_per_sec / 2:
                if self.snd1: snd.play('flop', 1.0, self.rect.centerx)
            self.pending_frames = self.pending_frames -1
            # Make the spike appear
            if self.pending_frames <= 0:
                self.dead = 0
                self.find_spot()
                self.vx = (random()-0.5) * 4
                self.vy = (random()-0.5) * 4
                if self.snd2: snd.play('klank2', 1.0, self.rect.centerx)

    def hit_by(self, other_mass):
        if not self.dead:
            # If dead by another method, don't over-ride
            if other_mass.__class__ in (Sun, Spike, Asteroid):
                self.dead = 1
                explosion = Explosion(self.x, self.y, self.vx, self.vy)
                explosion.mass = self.mass
                high[0:0] = [explosion]
                # gameplay.runobjects() will remove spike


class Spike(Hard):
    image_name = 'spike'
    taxonomy = ('spike', 'hard', 'significant')
    radius = 10.0
    phase_rate = 0.4
    snd1 = 'flop'
    snd2 = 'klank2'

class Asteroid(Hard):
    image_name = 'asteroid'
    taxonomy = ('asteroid', 'hard', 'significant')
    radius = 15.0
    phase_rate = 0.2
    snd1 = ''
    snd2 = ''

    def find_spot(self):
        new_dir = randint(0, var.compass_dirs-1)
        edge = randint(0, 1) # what edge
        while 1:
            if edge == 0:
                if edge == 0: self.x = 0
                self.y = randint(0, arena.bottom-1)
            else:
                if edge == 2: self.y = 0
                self.x = randint(0, arena.right-1)
            if self.check_spot(): return


# Superclass for "Bobbles". Don't instantiate
class Bobble(Mass):
    def __init__(self, x, y, vx, vy):
        Mass.__init__(self, x, y, vx, vy, mass=0.5)

    def tick(self, speedadjust):
        Mass.tick(self, speedadjust)

        if self.pending_frames > 0:
            self.pending_frames = self.pending_frames -1
            # Make the bobble appear
            if self.pending_frames <= 0:
                self.dead = 0
                self.find_spot()
                self.vx = (random()-0.5) * 6
                self.vy = (random()-0.5) * 6
                snd.play('chimeout', 1.0, self.rect.centerx)

    def hit_by(self, other_mass):
        if not self.dead:
            # If dead by another method, don't over-ride
            self.dead = 1
            vx = self.vx / 5
            vy = self.vy / 5
            if not other_mass.__class__ == Ship:
                snd.play('boxhit', 1.0, other_mass.rect.centerx)
                vapors.append(Pop(other_mass.x, other_mass.y, vx, vy))

class ShieldBobble(Bobble):
    image_name = 'bobble'
    taxonomy = ('shield', 'powerup', 'significant')
    radius = 10.0
    phase_rate = 0.2

class BulletBobble(Bobble):
    image_name = 'bobble'
    taxonomy = ('bullet', 'powerup', 'significant')
    radius = 10.0
    phase_rate = 0.1

class Tele(Mass):
    image_name = 'tele'
    radius = 11.0
    phase_rate = 0.6
    phase_death = 1

class Ship(Mass):
    BASE_COLOR = ((75, 0, 0), (0, 75, 0), (0, 0, 75), (75, 75, 0))

    image_name = 'ship'
    taxonomy = ('ship', 'damage', 'significant')
    radius = 11.0
    phase_rate = 1.0

    def __init__(self, shipnum, x=50.0, y=50.0, dir=0):
        self.shipnum=shipnum
        self.player = None
        self.mycolor = self.BASE_COLOR[shipnum]
        self.max_health = 100.0
        self.max_shield = 100.0
        self.max_bullet = var.bbobble_charge
        self.start(x, y, dir=dir)

    def start(self, x=50.0, y=50.0, vx=0, vy=0, mass=1.0, dir=0):
        imgs = images['ship'][self.shipnum]
        Mass.__init__(self, x, y, vx, vy, mass, dir, imgs)

        self.health = 100.0
        self.shield = 0.0
        self.shield_phase = 0
        self.shield_phases = len(images['shield'])
        self.bullet = 0.0
        self.bullet_phase = 0
        self.bullet_phases = len(images['bullet'])
        self.thrust = 0
        self.turn = 0
        self.fire_delay = 0
        self.smoke_rate = 0.0
        self.pending_frames = 0

    def tick(self, speedadjust):
        # Ship is dead, waiting to re-appear 
        if self.pending_frames > 0:
            if self.pending_frames == var.frames_per_sec / 2:
                snd.play('startlife', 1.0, self.rect.centerx)
            self.pending_frames = self.pending_frames -1
            self.health = (self.max_health *
                (var.death_time * var.frames_per_sec - self.pending_frames) /
                (var.death_time * var.frames_per_sec))
            # Make the ship appear again
            if self.pending_frames <= 0:
                self.dead = 0
                dir = randint(0, 359)
                self.start(dir=dir)
                self.find_spot()
            # Since we're dead, no more to do
            return

        if self.thrust:
            # Calculate thrust acceleration
            rads = radians(self.dir*(360/var.compass_dirs))
            self.vx = self.vx - sin(rads)*self.thrust
            self.vy = self.vy - cos(rads)*self.thrust
            # Smoke trails, don't overload frame rate
            if (not var.ai_train and
                    var.graphics > 0 and
                    gfx.surface.get_bytesize()>1):
                if self.thrust > 0: rads = (rads+pi) % (2.0*pi)
                fps = min(fps_list)
                if fps > 40.0:
                    self.smoke_rate += 2.0
                if fps > 35.0:
                    self.smoke_rate += 1.0
                elif fps > 30.0:
                    self.smoke_rate += 0.4
                elif fps > 25.0:
                    self.smoke_rate += 0.2
                elif fps > 20.0:
                    self.smoke_rate += 0.1
                else:
                    self.smoke_rate += 0.05
                for i in range(int(self.smoke_rate)):
                    self.smoke_rate -= 1.0
                    x = (self.x - sin(rads)*(self.radius+5) + randint(-7,7))
                    y = (self.y - cos(rads)*(self.radius+5) + randint(-7,7))
                    vx = (self.vx - sin(rads)*var.smoke_speed + random()/3)
                    vy = (self.vy - cos(rads)*var.smoke_speed + random()/3)
                    vapors.append(Smoke(x, y, vx, vy))

        # Perform turns
        if self.turn:
            self.dir = (self.dir+self.turn)%var.compass_dirs
            self.width = self.imgs[0][self.dir].get_width()
            self.height = self.imgs[0][self.dir].get_height()

        # Perform standard movement
        Mass.tick(self, speedadjust)

        # Shield and health housekeeping
        self.shield_phase = (self.shield_phase+0.4)%self.shield_phases
        self.bullet_phase = (self.bullet_phase+0.4)%self.bullet_phases
        if self.bullet > 0:
            self.bullet -= 1
        if self.health < self.max_health:
            self.health = min (self.max_health,
                               self.health + var.heal_rate/100.0)

        # Slow down firing rate
        if self.fire_delay > 0:
            self.fire_delay = self.fire_delay -1

        # Keep track of ticks alive
        if var.ai_train:
            self.player.dna.time += 1

    def draw(self):
        Mass.draw(self)
        if self.bullet:
            levels = len(images['bullet'][0])-1
            level = levels - int((levels+0.99) * self.bullet / self.max_bullet)
            phase = int(self.bullet_phase)
            img = images['bullet'][phase][level]
            cx = int(self.x)-(img.get_rect()[2] /2)
            cy = int(self.y)-(img.get_rect()[3] /2)
            gfx.surface.blit(img, (cx, cy))
        if self.shield:
            levels = len(images['shield'][0])-1
            level = levels - int((levels+0.99) * self.shield / self.max_shield)
            phase = int(self.shield_phase)
            img = images['shield'][phase][level]
            cx = int(self.x)-(img.get_rect()[2] /2)
            cy = int(self.y)-(img.get_rect()[3] /2)
            gfx.surface.blit(img, (cx, cy))

    def cmd_left(self):
        self.turn = 1

    def cmd_right(self):
        self.turn = -1

    def cmd_turn_off(self):
        self.turn = 0

    def cmd_reverse(self):
        if not self.thrust == var.reverse_power:
            self.thrust = var.reverse_power
            self.imgs = images['reverse'][self.shipnum]
            self.phases = len(self.imgs)
            self.phase = 0.0

    def cmd_turbo(self):
        if not self.thrust == var.thrust_power:
            self.thrust = var.thrust_power
            self.imgs = images['turbo'][self.shipnum]
            self.phases = len(self.imgs)
            self.phase = 0.0

    def cmd_thrust_off(self):
        if self.thrust:
            self.thrust = 0
            self.imgs = images['ship'][self.shipnum]
            self.phases = len(self.imgs)
            self.phase = 0.0

    def cmd_fire(self):
        if not self.fire_delay and not self.pending_frames:
            rads = radians(self.dir*(360/var.compass_dirs))
            vx = self.vx - sin(rads)*var.fire_speed
            vy = self.vy - cos(rads)*var.fire_speed
            x = self.x - sin(rads)*(self.radius+Fire.radius+1)
            y = self.y - cos(rads)*(self.radius+Fire.radius+1)
            if self.bullet:
                self.fire_delay = int(var.fire_delay_frames * 0.75)
                vx *= 1.2
                vy *= 1.2
                fire = SuperFire(x, y, vx, vy, self)
            else:
                self.fire_delay = var.fire_delay_frames
                fire = Fire(x, y, vx, vy, self)
            high.append(fire)
            snd.play('select_choose', 1.0, self.rect.centerx)

    def damage(self, damage):
        self.shield -= damage
        if self.shield < 0.0:
            self.health += self.shield
            self.shield = 0.0
        if self.health <= 0.0:
            self.health = 0.0
            self.dead = 1
            self.player.deaths += 1
            self.pending_frames = var.death_time * var.frames_per_sec
            explosion = Explosion(self.x, self.y, self.vx, self.vy)
            explosion.mass = self.mass
            high[0:0] = [explosion]
            # gameplay.runobjects() will move ship to pending
            return 1  # Ship is dead
        return 0  # Ship still alive


    def hit_by(self, other_mass):
        global high
        if self.dead: return
        if other_mass.__class__ in (Fire, SuperFire):
            damage = ((other_mass.damage-10) *
                      other_mass.ticks_to_live/var.fire_life + 10)
            dead = self.damage(damage)
            if dead and other_mass.owner is not self:
                other_mass.owner.player.score += 1
                other_mass.owner.player.complement = 1
            if var.ai_train:
                other_mass.owner.player.dna.damage += damage
        elif other_mass.__class__ == Explosion:
            if not self.shield:
                self.damage(var.explosion_damage)
        elif other_mass.__class__ == Ship:
            my_damage = other_mass.health + other_mass.shield
            other_damage = self.health + self.shield
            self.damage(my_damage)
            other_mass.damage(other_damage)
        elif other_mass.__class__ == ShieldBobble:
            self.shield = min(self.max_shield, self.shield + var.sbobble_power)
            snd.play('chimein', 1.0, other_mass.rect.centerx)
        elif other_mass.__class__ == BulletBobble:
            self.bullet = min(self.max_bullet, self.bullet+var.bbobble_charge)
            snd.play('chimein', 1.0, other_mass.rect.centerx)
        else:
            dead = self.damage(1000)
            if dead and self.player.score > 0:
                if var.scoring == 0:
                    self.player.score -= 1
                self.player.insult = 1


# Standalone execution only
class Score:
    def __init__(self, players):
        self.font = font.SysFont("sans", 25)
        self.players = players
        self.render()
        self.dead = 0

    def render(self):
        self.imgs = []
        self.scores = []
        for player in self.players:
            self.scores.append(player.score)
            self.imgs.append(self.font.render(
                "Player %d: %d" % (player.playernum+1, player.score), 1, (0,128,255)))

    def tick(self, speedadjust):
        realscores = [x.score for x in self.players]
        if self.scores != realscores:
            self.render()
        pass

    def erase(self):
        self.draw(erase=1)

    def draw(self, erase=0):
        for i in range(len(self.scores)):
            width = self.imgs[i].get_width()
            height = self.imgs[i].get_height()
            cx = arena.right - width - 20
            cy = 20 + i*30
            if not erase:
                r = gfx.surface.blit(self.imgs[i], (cx, cy))
                gfx.dirty(r)
            else:
                gfx.surface.fill(0, (cx, cy, width+15, height))


def fps_update():
    global fps_list
    fps_list.append(var.clock.get_fps())
    fps_list = fps_list[1:]


def runobjects(speedadjust):
    # Miscellaneous housekeeping
    fps_update()

    # Make spikes appear
    if randint(0,var.spike_rate * var.frames_per_sec) == 1:
        spike = Spike(0,0,0,0)
        spike.dead = 1
        spike.pending_frames = var.frames_per_sec
        pend.append(spike)

    # Make asteroids appear
    if randint(0,var.spike_rate * var.frames_per_sec) == 1:
        asteroid = Asteroid(0,0,0,0)
        asteroid.dead = 1
        asteroid.pending_frames = var.frames_per_sec
        pend.append(asteroid)

    # Make shield powerups appear
    if randint(0,var.shield_powerup_rate * var.frames_per_sec) == 1:
        sbobble = ShieldBobble(0,0,0,0)
        sbobble.dead = 1
        sbobble.pending_frames = var.frames_per_sec
        pend.append(sbobble)

    # Make bullet powerups appear
    if randint(0,var.bullet_powerup_rate * var.frames_per_sec) == 1:
        bbobble = BulletBobble(0,0,0,0)
        bbobble.dead = 1
        bbobble.pending_frames = var.frames_per_sec
        pend.append(bbobble)

    # Gravitate
    for o1 in vapors + low + high:
        for o2 in low + high:
            if o1 is not o2:
                o1.gravitate(o2)

    # Tick pending objects
    for o in pend:
        o.tick(speedadjust)
        if not o.dead:
            if o.__class__ in [Ship, Spike, ShieldBobble, BulletBobble]:
                pend.remove(o)
                low.append(o)

    # Erase and tick visible objects
    for l in list:
        for o in l[:]:
            o.erase()
            o.tick(speedadjust)

    # Check for collisions, skip virtual and pending objects
    for o1 in low + high:
        for o2 in low + high:
            if o1 is not o2 and not o1.dead:
                if o1.clearance(o2) < 0:
                    o1.hit_by(o2)

    # Check all objects for death
    for l in list:
        for o in l[:]:
            if o.dead:
                o.erase()
                l.remove(o)
                # Put ships on pending list
                if o.__class__ == Ship:
                    pend.append(o)
                # Replace fires with pops
                if o.__class__ in (Fire, SuperFire) and o.dead != 2:
                    vx = o.vx
                    vy = o.vy
                    if o.dead_by_hit:
                        vx = vx / 5
                        vy = vy / 5
                        snd.play('shoot', 1.0, o.rect.centerx)
                    vapors.append(Pop(o.x, o.y, vx, vy))

    for l in list:
        for o in l[:]:
            o.draw()



def main():

    import pygame, sys, ai, agents

    pygame.init()
    var.clock = pygame.time.Clock()

    full=1
    if '-window' in sys.argv:
        full = 0

    gfx.initialize((800,600), full)
    pygame.display.set_caption('Spacewar')

    load_game_resources()

    if len(ai.dna_pool) == 0:
        ai.load_dna_pool()

    if not '-nosound' in sys.argv:
        snd.initialize()

    # Load the main game objects
    players = []
    ship = Ship(shipnum=0, x=100, y=100)
    ship.shield = 100.0
    ship.bullet = var.bbobble_charge
    low.append(ship)
    players.append(agents.HumanAgent(playernum=0, ship=ship))

    #ship = Ship(shipnum=1, x=540, y=380)
    #low.append(ship)
    ####players.append(agents.AIAgent_Docile(playernum=1, ship=ship))
    #dna = random.choice(ai.dna_pool)
    #players.append(agents.DNAAgent(1,ship,dna,[low,high]))

    ship = Ship(shipnum=2, x=540, y=100)
    low.append(ship)
    #dna = random.choice(ai.dna_pool)
    dna = ai.DNA()
    dna.random()
    print dna
    players.append(agents.DNAAgent(2,ship,dna,[low,high]))

    sun = Sun()
    low.append(sun)
    virtual.append(Score(players))

    # Main game event loop
    while 1:
        for player in players: player.do_action()

        for event in pygame.event.get():
            if event.type == QUIT:
                sys.exit()
            elif event.type == KEYDOWN and event.key == K_ESCAPE:
                sys.exit()

        runobjects(1.0)

        gfx.update()

        var.clock.tick(standalone_frame_rate)  # max frame rate

if __name__ == '__main__':
    standalone_frame_rate = var.frames_per_sec
    main()