The Python Game Book

code games. learn Python.

User Tools

Site Tools


Sidebar

Github:

en:pygame:step019

Step 019 - Homing missiles

In this example game, two players can fire bullets and rockets at each other or at big nasty monsters.

Bullets have a short range, but steal hitpoints if hitting a player or a monster.

There are 2 different kind of rockets:

  • a “light”, weak damage, fast flying variant. Short lifetime. Can be shot down.
  • a “heavy”, heavy damage, slow flying “sliding” variant. Longer lifetime. Can not be shot down, but it's flightpath can be disturbed if hit.

Both types of rockets aim automatically toward the target. While the “light” rockets move in the direction of their heading (boring), the “heavy” rockets “slide” like in open space, making more interesting flight patterns.

A player can store a maximum ammo of 32 rockets (see class Player, self.rocketsmax). The first 16 rockets are light rockets, the others are heavy rockets.

Not shooting increase the stock of missiles of each player (see the blue rocketbar above each player)

If no more monsters are present, the players can fight each other (duel mode). New monsters can be created my pressing <key>m</key>.

If both players are shot down by monsters, the monsters start fighting themselves (Overtime). You can press <key>o</key> to get more overtime and watch the monsters fighting each other.

Monster states

Each 15 seconds, (see class Monster, self.hunttime) the monster has a chance to select a new target.

Each monster has a primitive State_machine, cycling between the three states “do nothing”, “shoot bullets at target” and “shoot rockets at target”. Each “state” is exactly 3 seconds long. This is the code line to see in what “state” or “phase” a monster currently is (inside class Monster, def update):

self.phase = self.phases[int(self.alivetime) % 3 ]
Self.alivetime is a variable counting the time (in seconds) of existence of a monster. The %-sign is the modulo operator. It doesn't return the result of a division, but instead the remainder.

As you can see in this example, the remainder cycles between 0 , 1 and 2 if the divisor is 3:

for x in range(31): # python2.x
   print x, "divided by 3 =", x/3, "remainder:", x%3,"float result:", x/3.0
output:

0 divided by 3 = 0 remainder: 0 float result: 0.0 
1 divided by 3 = 0 remainder: 1 float result: 0.333333333333 
2 divided by 3 = 0 remainder: 2 float result: 0.666666666667 
3 divided by 3 = 1 remainder: 0 float result: 1.0 
4 divided by 3 = 1 remainder: 1 float result: 1.33333333333 
5 divided by 3 = 1 remainder: 2 float result: 1.66666666667 
...

classes

All classes in this game derive from pygame's Sprite class.

additional resources

source code on github

To run this example you need:

file in folder download
019_homing_missiles.py pygame Download the whole Archive with all files from Github:
https://github.com/horstjens/ThePythonGameBook/archives/master
player_red2.png pygame/data
player_blue2.png pygame/data
xmonster_s.png pygame/data
xmonster_fire_s.png pygame/data
xmonster_left_s.png pygame/data
xmonster_right_s.png pygame/data
claws.ogg pygame/data
wormhole.ogg pygame/data
bomb.ogg pygame/data
shoot.ogg pygame/data
beep.ogg pygame/data
explode.ogg pygame/data

View/Edit/Download the file directly in Github: https://github.com/horstjens/ThePythonGameBook/blob/master/pygame/019_homing_missiles.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
019_homing_missiles.py
2-player game with homing missiles
url: http://thepythongamebook.com/en:part2:pygame:step019
author: horst.jens@spielend-programmieren.at
physic by Leonard Michlmayr
licence: gpl, see http://www.gnu.org/licenses/gpl.html

2 player can shoot at each other and/or at monster(s).
2 types of homing missiles (can also be shot down)
create new monsters with key m

works with python3.4 and python2.7
"""

#the next line is only needed for python2.x and not necessary for python3.x
from __future__ import print_function, division

def game(folder = "data"):
    import pygame
    import os
    import random
    import math 
    #------ starting pygame -------------
    pygame.mixer.pre_init(44100, -16, 2, 2048) # setup mixer to avoid sound lag
    pygame.init()
    screen=pygame.display.set_mode((1800,1000)) # try out larger values and see what happens !
    screenrect = screen.get_rect()
    #winstyle = 0  # |FULLSCREEN # Set the display mode
    #print "pygame version", pygame.ver 
    # ------- game constants ----------------------
    GRAD = math.pi / 180 # 2 * pi / 360   # math module needs Radiant instead of Grad
    # ----------- functions -----------
    def write(msg="pygame is cool", color=(0,0,0)):
        """write text into pygame surfaces"""
        myfont = pygame.font.SysFont("None", 32)
        mytext = myfont.render(msg, True, color)
        mytext = mytext.convert_alpha()
        return mytext
    def getclassname(class_instance):
        """this function extract the class name of a class instance.
        For an instance of a XWing class, it will return 'XWing'."""
        text = str(class_instance.__class__) # like "<class '__main__.XWing'>"
        parts = text.split(".") # like ["<class '__main__","XWing'>"]
        return parts[-1][0:-2] # from the last (-1) part, take all but the last 2 chars
    
    def radians_to_degrees(radians):
        return (radians / math.pi) * 180.0
    
    def degrees_to_radians(degrees):
        return degrees * (math.pi / 180.0)
    
    def elastic_collision(sprite1, sprite2):
        """elasitc collision between 2 sprites (calculated as disc's).
           The function alters the dx and dy movement vectors of both sprites.
           The sprites need the property .mass, .radius, .pos[0], .pos[1], .dx, dy
           pos[0] is the x postion, pos[1] the y position"""
        # here we do some physics: the elastic
        # collision
        # first we get the direction of the push.
        # Let's assume that the sprites are disk
        # shaped, so the direction of the force is
        # the direction of the distance.
        dirx = sprite1.pos[0] - sprite2.pos[0]
        diry = sprite1.pos[1] - sprite2.pos[1]
        # the velocity of the centre of mass
        sumofmasses = sprite1.mass + sprite2.mass
        sx = (sprite1.dx * sprite1.mass + sprite2.dx * sprite2.mass) / sumofmasses
        sy = (sprite1.dy * sprite1.mass + sprite2.dy * sprite2.mass) / sumofmasses
        # if we sutract the velocity of the centre
        # of mass from the velocity of the sprite,
        # we get it's velocity relative to the
        # centre of mass. And relative to the
        # centre of mass, it looks just like the
        # sprite is hitting a mirror.
        bdxs = sprite2.dx - sx
        bdys = sprite2.dy - sy
        cbdxs = sprite1.dx - sx
        cbdys = sprite1.dy - sy
        # (dirx,diry) is perpendicular to the mirror
        # surface. We use the dot product to
        # project to that direction.
        distancesquare = dirx * dirx + diry * diry
        if distancesquare == 0:
            # no distance? this should not happen,
            # but just in case, we choose a random
            # direction
            dirx = random.randint(0,11) - 5.5
            diry = random.randint(0,11) - 5.5
            distancesquare = dirx * dirx + diry * diry
        dp = (bdxs * dirx + bdys * diry) # scalar product
        dp /= distancesquare # divide by distance * distance.
        cdp = (cbdxs * dirx + cbdys * diry)
        cdp /= distancesquare
        # We are done. (dirx * dp, diry * dp) is
        # the projection of the velocity
        # perpendicular to the virtual mirror
        # surface. Subtract it twice to get the
        # new direction.
        # Only collide if the sprites are moving
        # towards each other: dp > 0
        if dp > 0:
            sprite2.dx -= 2 * dirx * dp 
            sprite2.dy -= 2 * diry * dp
            sprite1.dx -= 2 * dirx * cdp 
            sprite1.dy -= 2 * diry * cdp
    
    # ----------- classes ------------------------

    class Text(pygame.sprite.Sprite):
        """a pygame Sprite displaying text"""
        def __init__(self, msg="The Python Game Book", color=(0,0,0), topleft=(0,0)):
            self.groups = allgroup
            self.topleft = topleft
            self._layer = 1
            pygame.sprite.Sprite.__init__(self, self.groups)
            self.newmsg(msg,color)
            
        def update(self, time):
            pass # allgroup sprites need update method that accept time
        
        def newmsg(self, msg, color=(0,0,0)):
            self.image =  write(msg,color)
            self.rect = self.image.get_rect()
            self.rect.topleft = self.topleft

    class Lifebar(pygame.sprite.Sprite):
        """shows a bar with the hitpoints of a GameObject sprite
           with a given bossnumber, the Lifebar class can 
           identify the boss (GameObject sprite) with this codeline:
           GameObject.gameobjects[self.bossnumber] """
        def __init__(self, boss):
            self.groups = allgroup
            self.boss = boss
            self._layer = self.boss._layer
            pygame.sprite.Sprite.__init__(self, self.groups)
            self.oldpercent = 0
            self.color = (0,255,0)
            self.distance = 10
            self.paint()
            self.oldangle = self.boss.angle # store angle of boss to redraw bar if boss is rotating
            
        def paint(self):
            self.image = pygame.Surface((self.boss.rect.width,7))
            self.image.set_colorkey((0,0,0)) # black transparent
            pygame.draw.rect(self.image, self.color, (0,0,self.boss.rect.width,7),1)
            self.rect = self.image.get_rect()
 
        def recalc(self):
            self.percent = self.boss.hitpoints / self.boss.hitpointsfull * 1.0
 
        def update(self, time):
            self.recalc()
            #self.paint()
            if (self.percent != self.oldpercent) or (self.oldangle != self.boss.angle):
                self.oldangle = self.boss.angle # store angle of boss
                self.paint() # important ! boss.rect.width may have changed (because rotating)
                pygame.draw.rect(self.image, (0,0,0), (1,1,self.boss.rect.width-2,5)) # fill black
                pygame.draw.rect(self.image, self.color, (1,1,
                                 int(self.boss.rect.width * self.percent),5),0) # fill green
            self.oldpercent = self.percent
            self.rect.centerx = self.boss.rect.centerx
            self.rect.centery = self.boss.rect.centery - self.boss.rect.height /2 - self.distance
            if GameObject.gameobjects[self.boss.number] == None:
                self.kill() # kill the hitbar
    
    class Rocketbar(Lifebar):
        """shows a bar to indicate the stock of rockets."""
        def __init__(self, boss):
            Lifebar.__init__(self,boss)
            self.color = (0,0,128)
            self.distance = 16
        
        def recalc(self):
            if self.boss.rockets > self.boss.rocketsmax / 2:
                self.color = (0,0,255)
            else:
                self.color = (0,0,128)
            self.percent = self.boss.rockets / self.boss.rocketsmax * 1.0
        
    class GameObject(pygame.sprite.Sprite):
        """generic Game Object Sprite class, to be called from every Sprite
           with a physic collision (Player, Rocket, Monster, Bullet)
           need self.image and self.image0 and self.groups to be set
           need self.rect and self.pos to be set
           self.hitpoints must be set to a float, also self.hitpointsfull"""
        image=[]  # list of all images
        gameobjects = {} # a dictionary of all GameObjects, each GameObject has its own number
        number = 0  
        #def __init__(self, pos, layer= 4, area=screenrect, areastop = False, areabounce = False, angle=0, speedmax = 500, friction = 0.95, lifetime = -1):
        def __init__(self, layer= 4, area=screenrect, areastop = False, areabounce = False, angle=0, speedmax = 500, friction = 0.95, lifetime = -1):
            #self.pos = pos
            self._layer = layer                   # assign level
            self.area = area
            self.areastop = areastop
            self.areabounce = areabounce
            self.angle = angle 
            self.oldangle = angle
            self.speedmax = speedmax
            self.friction = friction # between 0 and 1, 1 means no friction, 0 means no more movement is possible
            self.lifetime = lifetime # -1 means infinite lifetime
            pygame.sprite.Sprite.__init__(self,  self.groups  ) #---------------call parent class. NEVER FORGET !
            self.alivetime = 0.0 # how long does this GameObject exist ?
            self.bouncefriction = -0.5 # how much speed is lost by bouncing off a wall. 1 means no loss, 0 means full stop
            self.dx = 0   # wait at the beginning
            self.dy = 0            
            self.number = GameObject.number # get my personal GameObject number
            GameObject.number+= 1           # increase the number for next GameObject
            GameObject.gameobjects[self.number] = self # store myself into the GameObject dictionary
          
        def speedcheck(self):
            speed = (self.dx**2 + self.dy**2)**0.5 ## calculate total speed
            if speed > self.speedmax:
                factor = self.speedmax / speed * 1.0
                self.dx *= factor
                self.dy *= factor
            else:
                self.color = (0,0,128)
            self.percent = self.boss.rockets / self.boss.rocketsmax * 1.0
        
    class GameObject(pygame.sprite.Sprite):
        """generic Game Object Sprite class, to be called from every Sprite
           with a physic collision (Player, Rocket, Monster, Bullet)
           need self.image and self.image0 and self.groups to be set
           need self.rect and self.pos to be set
           self.hitpoints must be set to a float, also self.hitpointsfull"""
        image=[]  # list of all images
        gameobjects = {} # a dictionary of all GameObjects, each GameObject has its own number
        number = 0  
        #def __init__(self, pos, layer= 4, area=screenrect, areastop = False, areabounce = False, angle=0, speedmax = 500, friction = 0.95, lifetime = -1):
        def __init__(self, layer= 4, area=screenrect, areastop = False, areabounce = False, angle=0, speedmax = 500, friction = 0.95, lifetime = -1):
            #self.pos = pos
            self._layer = layer                   # assign level
            self.area = area
            self.areastop = areastop
            self.areabounce = areabounce
            self.angle = angle 
            self.oldangle = angle
            self.speedmax = speedmax
            self.friction = friction # between 0 and 1, 1 means no friction, 0 means no more movement is possible
            self.lifetime = lifetime # -1 means infinite lifetime
            pygame.sprite.Sprite.__init__(self,  self.groups  ) #---------------call parent class. NEVER FORGET !
            self.alivetime = 0.0 # how long does this GameObject exist ?
            self.bouncefriction = -0.5 # how much speed is lost by bouncing off a wall. 1 means no loss, 0 means full stop
            self.dx = 0   # wait at the beginning
            self.dy = 0            
            self.number = GameObject.number # get my personal GameObject number
            GameObject.number+= 1           # increase the number for next GameObject
            GameObject.gameobjects[self.number] = self # store myself into the GameObject dictionary
          
        def speedcheck(self):
            speed = (self.dx**2 + self.dy**2)**0.5 ## calculate total speed
            if speed > self.speedmax:
                factor = self.speedmax / speed * 1.0
                self.dx *= factor
                self.dy *= factor
            #----------- friction -------------            
            if abs(self.dx) > 0 : 
                self.dx *= self.friction  # make the Sprite slower over time
            if abs(self.dy) > 0 :
                self.dy *= self.friction

        def areacheck(self):
            """if GameObject leave self.arena, it is bounced (self.areabounce) or stopped (self.areastop)"""
            if (self.areastop or self.areabounce) and not self.area.contains(self.rect):
                # --- compare self.rect and area.rect
                if self.pos[0] + self.rect.width/2 > self.area.right:
                    self.pos[0] = self.area.right - self.rect.width/2
                    if self.areabounce:
                        self.dx *= self.bouncefriction # bouncing off but loosing speed
                    else:
                        self.dx = 0
                if self.pos[0] - self.rect.width/2 < self.area.left:
                    self.pos[0] = self.area.left + self.rect.width/2
                    if self.areabounce:
                        self.dx *= self.bouncefriction # bouncing off but loosing speed
                    else:
                        self.dx = 0
                if self.pos[1] + self.rect.height/2 > self.area.bottom:
                    self.pos[1] = self.area.bottom - self.rect.height/2
                    if self.areabounce:
                        self.dy *= self.bouncefriction # bouncing off but loosing speed
                    else:
                        self.dy = 0
                if self.pos[1] - self.rect.height/2 < self.area.top:
                    self.pos[1] = self.area.top + self.rect.height/2
                    if self.areabounce:
                        self.dy *= self.bouncefriction # bouncing off but loosing speed
                    else:
                        self.dy = 0
                        
        def rotate_toward_moving(self, dx= None, dy=None):
            if dx is None and dy is None:
                dx = self.dx
                dy = self.dy
            return  math.atan2(-dx, -dy)/math.pi*180.0 
        
        def kill(self):
            GameObject.gameobjects[self.number] =   None # delete sprite from dictionary
            pygame.sprite.Sprite.kill(self) # kill the sprite              
        
        def update(self, seconds):
            self.alivetime += seconds
            # ------- killing --------------
            if self.hitpoints <= 1:
                self.kill()
            if self.lifetime != -1:
                if self.alivetime > self.lifetime:
                    self.kill() # end of natural lifetime
            # --------- rotated ? -------------------
            if self.angle != self.oldangle:            
                self.oldcenter = self.rect.center
                self.image = pygame.transform.rotate(self.image0, self.angle)
                self.rect = self.image.get_rect()
                self.rect.center = self.oldcenter
                self.oldangle = self.angle

            #----------moving ----------------
            self.pos[0] += self.dx * seconds
            self.pos[1] += self.dy * seconds
            self.speedcheck()    # ------------- movement
            self.areacheck() # ------- check if Bird out of screen
            self.rect.centerx = round(self.pos[0],0)
            self.rect.centery = round(self.pos[1],0)
    
    class Player(GameObject):
        """a class to hold all players"""
        number = 0
        image = []
        duel = False # duel or cooperative play
        def __init__(self, playernumber = 0):
            self.playernumber = Player.number
            self.bullets_fired = 0
            self.rockets_fired = 0
            self.bullets_hit = 0
            self.rockets_hit = 0
            Player.number += 1 # prepare number for next player 
            self.hitpoints = 450.0
            self.hitpointsfull = 450.0
            self.image = Player.image[self.playernumber] # start with 0
            self.image0 = Player.image[self.playernumber] # start with 0 
            self.rect = self.image.get_rect()
            self.mask = pygame.mask.from_surface(self.image) # pixelmask ---- necessary ?
            if self.playernumber == 0:
                self.pos = [screen.get_width()/10*2,screen.get_height()-30]
                self.angle = 270
                self.bulletcolor = (200,0,200)
                self.rocket1color = (200,50,50)
                self.rocket2color = (250,100,0)
            elif self.playernumber ==1:
                self.pos = [screen.get_width()/10*8,screen.get_height()-30]
                self.angle = 90
                self.bulletcolor = (0,200,200)
                self.rocket1color = (50,50,200)
                self.rocket2color = (0,200,250)
            # ---------- both players ---------
            self.bulletlifetime = 1.4 # short lifetime, but steals hitpoints
            self.max_abberation = 5.5 # low value means more precise shooting
            self.groups = allgroup, playergroup, gravitygroup
            #  def __init__(self, pos, layer= 4, area=screenrect, areastop = False, areabounce = False, angle=0, speedmax = 500, friction = 0.8, lifetime = -1)
            GameObject.__init__(self, areastop = True, angle = self.angle, speedmax = 300) # ------------------- important ! ----------------------
            self.speed = 200.0 # base movement speed factor
            self.rotatespeed = 3.0 # rotating speed
            self.frags = 100
            Lifebar(self)
            self.cooldowntime = 0.08 #seconds
            self.cooldown = 0.0
            self.rocketcooldowntime = .005 #seconds
            self.rocketcooldown = 0.0
            self.rocketreloadtime = 1.6 # seconds
            self.peacetime = 0.0 # how long no shot was fired
            self.rockets = 0.0   # must be float or bar sprite will not work correctly
            self.rocketsmax = 32.0 # max amount of rockets in stock
                                 # all rockets above rocketsmax/2 are heavy rockets
            Rocketbar(self) # draw bar to indicate how many rockets are left
            self.mass = 400.0
            self.frags = 100
            self.oldangle = -5
            
        def kill(self):
            bombsound.play()
            for _ in range(self.frags):
                RedFragment(self.pos)
            GameObject.kill(self) # call parent method
        
        def get_target_nr(self):
            # select a random monster as target
            if Player.duel:
                if (GameObject.gameobjects[0] is not None) and (GameObject.gameobjects[1] is not None):
                    # both players alive and duel mode, select other player
                    if self.number == 0:
                        return 1
                    else:
                        return 0
                else:
                    Player.duel = False # switch to cooperative mode because only one player is alive
            if not Player.duel and len(Monster.monsters) > 0:
                    return random.choice(Monster.monsters)
            else:
                pass # ------
                
        
        def update(self, seconds):
              pressedkeys = pygame.key.get_pressed()
              self.ddx = 0.0
              self.ddy = 0.0
              self.targetnumber = self.get_target_nr()
              if self.playernumber == 0:
                    if pressedkeys[pygame.K_w]: # forward
                             self.ddx = -math.sin(self.angle*GRAD) 
                             self.ddy = -math.cos(self.angle*GRAD) 
                             Smoke(self.rect.center, -self.ddx , -self.ddy )
                    if pressedkeys[pygame.K_s]: # backward
                             self.ddx = +math.sin(self.angle*GRAD) 
                             self.ddy = +math.cos(self.angle*GRAD) 
                             Smoke(self.rect.center, -self.ddx, -self.ddy )
                    if pressedkeys[pygame.K_e]: # right side
                             self.ddx = +math.cos(self.angle*GRAD)
                             self.ddy = -math.sin(self.angle*GRAD)
                             Smoke(self.rect.center, -self.ddx , -self.ddy )
                    if pressedkeys[pygame.K_q]: # left side
                             self.ddx = -math.cos(self.angle*GRAD) 
                             self.ddy = +math.sin(self.angle*GRAD) 
                             Smoke(self.rect.center, -self.ddx , -self.ddy )
              elif self.playernumber == 1:
                    if pressedkeys[pygame.K_KP8]: # forward
                             self.ddx = -math.sin(self.angle*GRAD) 
                             self.ddy = -math.cos(self.angle*GRAD) 
                             Smoke(self.rect.center, -self.ddx , -self.ddy )
                    if pressedkeys[pygame.K_KP5] or pressedkeys[pygame.K_KP2]: # backward
                             self.ddx = +math.sin(self.angle*GRAD) 
                             self.ddy = +math.cos(self.angle*GRAD) 
                             Smoke(self.rect.center, -self.ddx, -self.ddy )
                    if pressedkeys[pygame.K_KP9]: # right side
                             self.ddx = +math.cos(self.angle*GRAD)
                             self.ddy = -math.sin(self.angle*GRAD)
                             Smoke(self.rect.center, -self.ddx , -self.ddy )
                    if pressedkeys[pygame.K_KP7]: # left side
                             self.ddx = -math.cos(self.angle*GRAD) 
                             self.ddy = +math.sin(self.angle*GRAD) 
                             Smoke(self.rect.center, -self.ddx , -self.ddy )                        
              # ------------shoot-----------------
              self.peacetime += seconds # increase peacetime if no shot was fired
              if self.cooldown > 0: # ------ can not shoot
                    self.cooldown -= seconds # pause between bullets
              else: # --------can shoot
                    if ((self.playernumber == 1 and pressedkeys[pygame.K_KP0]) or 
                        (self.playernumber == 0 and pressedkeys[pygame.K_SPACE])): # shoot forward
                            self.ddx = +math.sin(self.angle*GRAD)#recoil
                            self.ddy = +math.cos(self.angle*GRAD)
                            lasersound.play() # play sound
                            Bullet(self, None, self.max_abberation )
                            self.peacetime = 0 # reset peacetime
                            self.cooldown = self.cooldowntime 
                            self.bullets_fired += 1
                            if self.rocketcooldown > 0:
                                self.rocketcooldown -= seconds
                            else:
                                if self.rockets > self.rocketsmax / 2: # heavy sliding rocket
                                    if self.targetnumber is not None:
                                        crysound.play()
                                        Rocket(self,self.targetnumber,1,-30) #boss, target, type, launchangle
                                        Rocket(self,self.targetnumber,1, 30) #boss, target, type, launchangle
                                        self.rockets_fired +=2
                                        self.rockets -= 2
                                        self.rocketcooldown = self.rocketcooldowntime
                                elif self.rockets > 2: # weak seeking rocket
                                    if self.targetnumber is not None:
                                        crysound.play()
                                        Rocket(self,self.targetnumber, 2, -80 )#boss, target, type
                                        Rocket(self,self.targetnumber, 2, 80 )#boss, target, type
                                        self.rockets_fired += 2
                                        self.rockets -= 2
                                        self.rocketcooldown = self.rocketcooldowntime
              #----- add more rockets --------
              if self.peacetime > self.rocketreloadtime:
                  self.rockets += 2
                  self.peacetime = 0
              #-------------rotate----------------
              if self.playernumber == 0:
                    if pressedkeys[pygame.K_a]: # left turn , counterclockwise
                        self.angle += self.rotatespeed
                    if pressedkeys[pygame.K_d]: # right turn, clockwise
                        self.angle -= self.rotatespeed
              elif self.playernumber == 1:
                    if pressedkeys[pygame.K_KP4]: # left turn , counterclockwise
                        self.angle += self.rotatespeed
                    if pressedkeys[pygame.K_KP6]: # right turn, clockwise
                        self.angle -= self.rotatespeed
              # ------------move------------------
              self.dx += self.ddx * self.speed 
              self.dy += self.ddy * self.speed
              # ----- move, rotate etc. ------------  
              GameObject.update(self, seconds)# ------- calll parent function 
            
    class Monster(GameObject):
        """neutral Monster, hunt both players"""
        image = []
        monsters=[]
        def __init__(self, pos=screenrect.center):
            self.groups = allgroup, gravitygroup, playergroup, monstergroup
            self.image = Monster.image[0]
            self.bullets_fired = 0
            self.rockets_fired = 0
            self.bullets_hit = 0
            self.bulletcolor=(0,128,0)
            self.bulletlifetime = 2.8 # longer lifetime than player's bullet, but no lifestealing effect
            self.max_abberation = 6
            self.rocket1color = (20,random.randint(200,255),80)
            self.rocket2color = (20,random.randint(200,255),80)
            self.rockets_hit = 0
            self.rect = self.image.get_rect()
            self.pos = [0.0,0.0]
            self.pos[0] = pos[0]
            self.pos[1] = pos[1]
            self.rect.center = pos
            self.hitpoints = 500.0
            self.hitpointsfull = 1000.0
            self.mass = 1000
            self.radius = self.rect.width / 2
            GameObject.__init__(self, layer= 5, area=screenrect, areastop = True, areabounce = True, angle=0, speedmax = 300, friction = 0.95, lifetime = -1)
            Monster.monsters.append(self.number)
            self.frags = 1406 
            self.hunttime = 0.0
            self.targetnumber = self.choose_target_nr()
            self.target = GameObject.gameobjects[self.targetnumber]
            self.playernumber = Player.number
            Player.number += 1
            Lifebar(self)
            self.firetime = 0.0 # how long the fire image is visible
            self.phase =  "nothing" # do not shoot
            self.phases = ["nothing", "bullets", "heavy rockets", "small rockets"]
        
        def kill(self):
            bombsound.play()
            for  _ in range(self.frags):
                RedFragment(self.pos)
            Monster.monsters.remove(self.number)
            GameObject.kill(self)
            
        def choose_target_nr(self):
            # as long as one player exist, target him
            # else, target another monster
            if GameObject.gameobjects[0] is not None and GameObject.gameobjects[1] is not None: # both players alive
               return random.randint(0,1) # choose one of both
            elif GameObject.gameobjects[0] is not None:
                return 0 # choose the surviving player
            elif GameObject.gameobjects[1] is not None:
                return 1 # choose the surviving player
            elif len(monstergroup) > 0: # 1+ Monsters are alive
                mynumber = self.number
                while mynumber == self.number:
                   mynumber = random.choice(Monster.monsters)
                return mynumber
            else:
                return None
                
            
        def update(self, seconds):
            # each second, decide if to hunt player 0 or player 1
            self.hunttime += seconds
            if self.hunttime > 15:
                self.hunttime = 0
                self.targetnumber = self.choose_target_nr()
                self.target = GameObject.gameobjects[self.targetnumber]
            # hunting
            self.image = Monster.image[0] # "normal" xmonster
            if GameObject.gameobjects[self.targetnumber] is not None:
                self.targetdistancex = self.target.pos[0] - self.pos[0]
                self.targetdistancey = self.target.pos[1] - self.pos[1]
                if self.targetdistancex > 0:
                    self.dx += 1
                    if self.targetdistancex > 100:
                        self.image = Monster.image[3] # look right
                        #self.image0 = Monster.image[3] # look right
                elif self.targetdistancex <0:
                    self.dx -= 1
                    if self.targetdistancex < -100:
                        self.image = Monster.image[2] # look left
                        #self.image0 = Monster.image[2] # look left
                if self.targetdistancey > 0:
                    self.dy += 1
                elif self.targetdistancey < 0:
                    self.dy -= 1
                # ----- shoot rockets --------
                # ---- 4 different phases: bullets, heavy rockets, light rockets, pausing
                # ---- chance to change into another phase each full second
                self.phase = self.phases[int(self.alivetime) % 4 ] # fully cycle throug all phases each 3 seconds
                if self.phase == "nothing":
                    pass # do not shoot
                elif self.phase == "bullets":
                    if random.randint(1,2) == 1:
                        Bullet(self, GameObject.rotate_toward_moving(self, self.targetdistancex, self.targetdistancey), self.max_abberation)
                        self.bullets_fired += 1
                        self.firetime = 0.1
                elif self.phase == "heavy rockets":
                    #self.angle = math.atan2(-self.dx, -self.dy)/math.pi*180.0 
                    if random.randint(1,50) == 1:
                        Rocket(self, self.targetnumber, 1) # shoot a slow, heavy rocket
                        self.rockets_fired += 1
                        self.firetime = 0.25 # show fire image for this time
                elif self.phase == "small rockets":
                    if random.randint(1,25) == 1:
                        Rocket(self, self.targetnumber, 2) # shoot a small fast rocket
                        self.rockets_fired += 1
                        self.firetime = 0.25 # show fire image for this time
                    
                if self.firetime > 0:
                    self.image = Monster.image[1] # show fire image for monster
                    self.firetime -= seconds    
                else:
                    self.firetime = 0
            GameObject.update(self, seconds)
            
            
    class Fragment(pygame.sprite.Sprite):
        """generic Fragment class. """
        number = 0
        def __init__(self, pos, layer = 9):
            self._layer = layer
            pygame.sprite.Sprite.__init__(self, self.groups)
            self.pos = [0.0,0.0]
            self.fragmentmaxspeed = 200# try out other factors !
            self.number = Fragment.number
            Fragment.number += 1
            
        def init2(self):  # split the init method into 2 parts for better access from subclasses
            self.image = pygame.Surface((10,10))
            self.image.set_colorkey((0,0,0)) # black transparent
            self.fragmentradius = random.randint(2,5)
            pygame.draw.circle(self.image, self.color, (5,5), self.fragmentradius)
            self.image = self.image.convert_alpha()
            self.rect = self.image.get_rect()
            self.rect.center = self.pos #if you forget this line the sprite sit in the topleft corner
            self.time = 0.0
            
        def update(self, seconds):
            self.time += seconds
            if self.time > self.lifetime:
                self.kill() 
            self.pos[0] += self.dx * seconds
            self.pos[1] += self.dy * seconds
            self.rect.centerx = round(self.pos[0],0)
            self.rect.centery = round(self.pos[1],0)
    
    class RedFragment(Fragment):
        """explodes outward from (killed) sprite"""
        def __init__(self,pos, stay = False):
            self.groups = allgroup, fragmentgroup, gravitygroup
            Fragment.__init__(self,pos)
            self.stay = stay # if the Fragment stay still or moves
            self.color = (random.randint(25,255),0,0) # red            
            self.pos[0] = pos[0]
            self.pos[1] = pos[1]
            if self.stay:
                self.dx = 0
                self.dy = 0
            else:
                self.dx = random.randint(-self.fragmentmaxspeed,self.fragmentmaxspeed)
                self.dy = random.randint(-self.fragmentmaxspeed,self.fragmentmaxspeed)
            self.lifetime = 0.5 + random.random() # max 1.5 seconds
            self.init2() # continue with generic Fragment class
            self.mass = 48.0
            
    class Wound(Fragment):
        """yellow impact wound that shows the exact location of the hit"""
        def __init__(self, pos, greenmin = 200, greenmax = 255 ):
            self.greenmin = greenmin
            self.greenmax = greenmax
            self.color = ( random.randint(200,255), random.randint(self.greenmin,self.greenmax), random.randint(0,50))
            self.groups = allgroup
            Fragment.__init__(self, pos, 7) # layer
            self.pos[0] = pos[0]
            self.pos[1] = pos[1]
            self.lifetime = 1 + random.random()*2 # max 3 seconds
            Fragment.init2(self)
            self.dx = 0
            self.dy = 0
        
        def update(self,time):
            self.color = ( random.randint(200,255), random.randint(self.greenmin,self.greenmax), random.randint(0,50))
            pygame.draw.circle(self.image, self.color, (5,5), self.fragmentradius)
            self.image = self.image.convert_alpha()
            Fragment.update(self, time)
            
    class Smoke(Fragment):
        """black exhaust indicating that the sprite is moved.
           Exhaust direction is inverse of players movement direction"""
        def __init__(self, pos, dx, dy, colmin=1, colmax=50):
           self.color = ( random.randint(colmin,colmax), random.randint(colmin,colmax), random.randint(colmin,colmax) )
           self.groups = allgroup
           Fragment.__init__(self,pos, 3) # give startpos and layer 
           self.pos[0] = pos[0]
           self.pos[1] = pos[1]
           self.lifetime = 0.25 + random.random()*0.5 # 
           Fragment.init2(self)
           self.smokespeed = 120.0 # how fast the smoke leaves the Bird
           self.smokearc = .3 # 0 = thin smoke stream, 1 = 180 Degrees
           arc = self.smokespeed * self.smokearc
           self.dx = dx * self.smokespeed + random.random()*2*arc - arc
           self.dy = dy * self.smokespeed + random.random()*2*arc - arc
           
    class Bullet(GameObject):
        """a bullet flying in the direction of the boss sprite's facing.
           If shooting direction should be independent of boss sprite's facing,
           angle can be given as argument"""
        def __init__(self, boss, myangle = None, max_abberation = 5.5):
            self.boss = boss
            if myangle is None:
                myangle = self.boss.angle 
            self.abberation = random.uniform(-max_abberation,max_abberation) # no shot is perfect
            myangle += self.abberation # spoil the perfect shot
            dx =  -math.sin(myangle*GRAD) 
            dy =  -math.cos(myangle*GRAD)
            self.color = self.boss.bulletcolor
            self.groups = allgroup, bulletgroup, gravitygroup,projectilegroup
            self.lifetime = self.boss.bulletlifetime 
            self.image = pygame.Surface((4,20))
            self.image.set_colorkey((0,0,0)) # black transparent
            pygame.draw.rect(self.image, self.color, (0,0,4,20) )
            pygame.draw.rect(self.image, (10,0,0), (0,0,4,4)) # point
            self.image = self.image.convert_alpha()
            self.image0 = self.image.copy()
            self.rect = self.image.get_rect()
            self.pos = self.boss.pos[:]
            self.rect.centerx = round(self.pos[0],0)
            self.rect.centery = round(self.pos[1],0)
            #GameObject.__init__(self, self.boss.pos, layer= 4, area=screenrect, areastop = False, areabounce = False, angle=self.boss.angle, speedmax = 500, friction = 1.0, lifetime = 1.2)
            GameObject.__init__(self, layer= 4, area=screenrect, areastop = True, areabounce = True, angle=myangle + self.abberation, speedmax = 400, friction = 1.0, lifetime = self.lifetime)
            self.dx = dx * self.speedmax  
            self.dy = dy * self.speedmax
            self.damage = 10
            self.hitpoints = 3
            self.mass = 50
            self.radius = self.rect.width / 2.0
            
        def update(self, seconds):
            self.angle = GameObject.rotate_toward_moving(self)
            #GameObject.speedcheck(self)
            GameObject.update(self, seconds)
            
            
    class Rocket(GameObject):
        """a rocket flying and steering toward a target
           type 1 is a haevy damage, slow-flying, sliding rocket
           type 2 is a light damage, fast-flying, direct seeking rocket"""
        def __init__(self, boss, targetnr, type=1, launchangle = 0):
            self.boss = boss
            if type == 1:   # --------heavy sliding missile ----------
                self.size = 6
                self.color = self.boss.rocket1color
                self.damage = 15
                self.mass = 40
                self.hitpoints = 5 # more hitpoints because less hard to avoid
                self.lifetime = 38 # longer lifetime because less hard to avoid
                self.speed = 50
                self.starttime = 0.9 # missles are launched but are not homing in this time
                rocketfriction = 0.989            
            elif type==2:            # ---------light direct seeking missile ----------------
                self.size = 4
                self.color = self.boss.rocket2color
                self.damage = 5
                self.hitpoints = 2
                self.mass = 10
                self.lifetime = 14
                self.speed = 155 # pixel per second ?
                self.starttime = 0.9 # missles are launched but are not homing in this time
                rocketfriction = 0.99
            self.boss = boss
            self.targetnr = targetnr
            self.target = GameObject.gameobjects[targetnr] # player ?
            self.type = type
            #---------- image --------------
            self.image = pygame.Surface((self.size,20))
            self.image.set_colorkey((0,0,0)) # black transparent
            pygame.draw.rect(self.image, self.color, (0,0,self.size,20) )
            pygame.draw.rect(self.image, (10,0,0), (0,0,self.size,4)) # point
            self.image = self.image.convert_alpha()
            self.image0 = self.image.copy()
            self.rect = self.image.get_rect()
            self.angle = launchangle + self.boss.angle
            self.groups = allgroup, rocketgroup, projectilegroup
            #GameObject.__init__(self, self.boss.pos[:], layer= 8, area=screenrect, areastop = False, areabounce = False, angle=self.angle, speedmax = 800, friction = rocketfriction, lifetime = self.lifetime)
            self.pos = self.boss.pos[:]
            GameObject.__init__(self, layer= 8, area=screenrect, areastop = True, areabounce = False, angle=self.angle, speedmax = 800, friction = rocketfriction, lifetime = self.lifetime)
            self.ddx = -math.sin(self.angle*GRAD) 
            self.ddy = -math.cos(self.angle*GRAD) 
            self.dx = self.ddx * self.speed
            self.dy = self.ddy * self.speed
            self.rotspeed = 5 # grad per second
            self.radius = self.rect.width / 2.0
            self.frags = 5
            self.smokechance = 3 # probability of 1:5 for each frame to launch Smoke
            
        def kill(self):
            #for _ in range(self.frags):
            #    RedFragment(self.pos)
            Wound(self.pos, 0,50)
            GameObject.kill(self)
            
        def update(self, seconds):
            
            self.alivetime += seconds
            if self.alivetime > self.lifetime:
                self.kill() 
            if self.hitpoints < 1:
                self.kill()
            if GameObject.gameobjects[self.targetnr] is None:
                self.kill()
            if self.alivetime < self.starttime:
                #--------- rotate into direction of movement ------------
                self.angle = math.atan2(-self.dx, -self.dy)/math.pi*180.0 
            else:
                #-------- rotate toward target
                if self.target is not None:
                    ix = self.target.pos[0] - self.pos[0]
                    iy = self.target.pos[1] - self.pos[1]
                    self.angle = radians_to_degrees(math.atan2(iy,- ix))+90
                self.ddx = -math.sin(self.angle*GRAD)  
                self.ddy = -math.cos(self.angle*GRAD) 
            if self.type == 1: # sliding
                self.dx += self.ddx  #* self.speed
                self.dy += self.ddy  #* self.speed
                if random.randint(1,self.smokechance) ==1:
                    Smoke(self.pos, -self.ddx * 2, -self.ddy * 2)
            elif self.type == 2: #seeking
                self.dx = self.ddx * self.speed
                self.dy = self.ddy * self.speed
                if random.randint(1, self.smokechance) ==1:
                    Smoke(self.pos, -self.ddx, -self.ddy, 25, 75)
            #----------- both ------------
            GameObject.speedcheck(self)
            oldrect = self.rect.center
            self.image = pygame.transform.rotozoom(self.image0,self.angle,1.0) 
            self.rect = self.image.get_rect()   
            self.rect.center = oldrect
            self.pos[0] += self.dx * seconds
            self.pos[1] += self.dy * seconds
            self.rect.centerx = round(self.pos[0],0)
            self.rect.centery = round(self.pos[1],0)
    #------------- end of classes -----------------
    # ----------------- background artwork -------------  
    background = pygame.Surface((screen.get_width(), screen.get_height()))
    background.fill((255,255,255))     # fill white
    background.blit(write("red player:  w,a,s,d,q,e fire: SPACE", (130,130,130)),(50,110))
    background.blit(write("blue player: Numpad 8,4,5,6,7,9 fire: 0", (130,130,130)),(50,140))
    background.blit(write("icrease # of rockets by not firing", (130,130,130)), (50, 170))
    background.blit(write("ESC=quit, m=new monster o=more overtime", (130,130,130)), (50,200))
    background = background.convert()  # jpg can not have transparency
    screen.blit(background, (0,0))     # blit background on screen (overwriting all)
    #-----------------define sprite groups------------------------
    playergroup = pygame.sprite.Group() 
    monstergroup = pygame.sprite.Group()
    bulletgroup = pygame.sprite.Group()
    fragmentgroup = pygame.sprite.Group()
    rocketgroup = pygame.sprite.Group()
    gravitygroup = pygame.sprite.Group()
    projectilegroup = pygame.sprite.Group()
    # only the allgroup draws the sprite, so i use LayeredUpdates() instead Group()
    allgroup = pygame.sprite.LayeredUpdates() # more sophisticated, can draw sprites in layers 

    #-------------loading files from data subdirectory -------------------------------
    try:
        Player.image.append(pygame.image.load(os.path.join(folder,"player_red2.png")).convert_alpha())   #0
        Player.image.append(pygame.image.load(os.path.join(folder,"player_blue2.png")).convert_alpha())  #1
        Monster.image.append(pygame.image.load(os.path.join(folder, "xmonster_s.png")).convert_alpha())        #0
        Monster.image.append(pygame.image.load(os.path.join(folder, "xmonster_fire_s.png")).convert_alpha())   #1
        Monster.image.append(pygame.image.load(os.path.join(folder, "xmonster_left_s.png")).convert_alpha())   #2
        Monster.image.append(pygame.image.load(os.path.join(folder, "xmonster_right_s.png")).convert_alpha())  #3

        # ------- load sound -------
        crysound = pygame.mixer.Sound(os.path.join(folder,'claws.ogg'))  #load sound
        warpsound = pygame.mixer.Sound(os.path.join(folder,'wormhole.ogg'))
        bombsound = pygame.mixer.Sound(os.path.join(folder,'bomb.ogg'))
        lasersound = pygame.mixer.Sound(os.path.join(folder,'shoot.ogg'))
        hitsound = pygame.mixer.Sound(os.path.join(folder,'beep.ogg'))
        impactsound = pygame.mixer.Sound(os.path.join(folder,'explode.ogg'))
    except:
        raise(UserWarning, "Sadly i could not loading all graphic or sound files from %s" % folder)
    
    # ------------- before the main loop ----------------------
    screentext1 = Text("first line", (255,0,255),(0,0))
    screentext2 = Text("second line",(0,0,0),(0,25))
    screentext3 = Text("third line", (255,0,0),(0,50))
    screentext4 = Text("fourth line", (0,0,255),(0,75))
    
    clock = pygame.time.Clock()        # create pygame clock object 
    mainloop = True                    # if False, game ends
    FPS = 60                           # desired max. framerate in frames per second. 
    player1 = Player() # game object number 0
    player2 = Player() # game object number 1
    warpsound.play()   # play new monster sound
    Monster() # create a single Monster
    overtime = 15 # time in seconds to admire the explosion of player before the game ends
    gameOver = False # if True, game still continues until overtime runs out
    gametime = 360 # how long to play (seconds)
    playtime = 0  # how long the game was played
    gravity = False # gravity can be toggled
    
        
    while mainloop:
        milliseconds = clock.tick(FPS)  # milliseconds passed since last frame
        seconds = milliseconds / 1000.0 # seconds passed since last frame
        playtime += seconds # keep track of playtime
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                mainloop = False # pygame window closed by user
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    mainloop = False # user pressed ESC
                elif event.key == pygame.K_g:
                    gravity = not gravity # toggle gravity
                elif event.key == pygame.K_m:
                    warpsound.play()
                    Monster() # create a new monster
                    Player.duel = False
                elif event.key == pygame.K_o:
                    if gameOver:
                        overtime += 10 # more overtime to watch monsters fight each other
        #---- new Monster ?
        #if random.randint(1,1000) == 1:
        #    Monster()
        pygame.display.set_caption("Monster duel. FPS: %.2f"   % clock.get_fps())
        if len(monstergroup) == 0:
            Player.duel = True
        else:
            Player.duel = False
            
        for player in playergroup:  # test if a player crash into enemy bullet ... vamipr health stealing effect !
            crashgroup = pygame.sprite.spritecollide(player, bulletgroup, False, pygame.sprite.collide_mask)
            for bullet in crashgroup: # this include friendly fire
                if bullet.boss.playernumber != player.playernumber: # only care for unfriendly fire
                    if bullet.boss.number < 2:
                        hitsound.play() # only "crysound" if player shot at monster
                    player.hitpoints -= bullet.damage
                    bullet.boss.bullets_hit += 1
                    bullet.boss.hitpoints += 1 # shooter steals at least one hitpoint from victim. Vampire effect
                    Wound(bullet.pos[:]) # pos, victim, move_with_victim = False
                    elastic_collision(bullet, player) # impact on player
                    bullet.kill()
            # player vs player
            crashgroup = pygame.sprite.spritecollide(player, playergroup, False, pygame.sprite.collide_circle)
            for crashplayer in crashgroup:
                if player.number > crashplayer.number:
                    elastic_collision(crashplayer, player) # impact on player
                    # player.hitpoints -= crashplayer.damage
                # no damage ?
        
            # test if player crash into enemy rocket
            crashgroup = pygame.sprite.spritecollide(player, rocketgroup, False, pygame.sprite.collide_mask)
            for rocket in crashgroup:
                #if projectile.physicnumber > crashbody.physicnumber: #avoid checking twice
                if rocket.boss.playernumber != player.playernumber: # avoid friendly fire
                   impactsound.play()
                   player.hitpoints -= rocket.damage
                   rocket.boss.rockets_hit += 1
                   Wound(rocket.pos[:])
                   elastic_collision(rocket, player)
                   rocket.kill()
        
        for projectile in projectilegroup:
            # rocket vs rocket vs bullet vs bullet
            crashgroup = pygame.sprite.spritecollide(projectile, projectilegroup, False )
            for crashthing in crashgroup:
                if projectile.number > crashthing.number:
                    if crashthing.boss.playernumber != projectile.boss.playernumber:
                        projectile.hitpoints -= crashthing.damage
                        crashthing.hitpoints -= projectile.damage
                        elastic_collision(projectile, crashthing)
            
        if gravity: # ---- gravity check ---
            for thing in gravitygroup:  # gravity suck down bullets, players, monsters
                thing.dy += 2.81 # pixel per second square earth: 9.81 m/s
        # ------game Over ? -------------
        #if  (playtime > gametime) and not gameOver:
        #    gameOver = True # do those things once when the game ends
        if GameObject.gameobjects[0] is None and GameObject.gameobjects[1] is None:
            gameOver = True # both players death
            screentext1.newmsg("Game Over. Time played: %.2f seconds" % playtime)
            screentext2.newmsg("both players killed")
        elif GameObject.gameobjects[0] is None or GameObject.gameobjects[1] is None:
            if player1.hitpoints > 0:
                textname = "Red Player"
                textcolor = (255,0,0)
            else:
                textname = "Blue Player"
                textcolor = (0,0,255)
            if len(monstergroup) == 0: 
                gameOver = True # one player dead, all monsters dead
                screentext2.newmsg("%s, you win!" % textname, textcolor)
            elif len(monstergroup) == 1:
                screentext2.newmsg("%s, fight the monster !" % textname, textcolor)
            else:
                screentext2.newmsg("%s, fight the monsters !" % textname, textcolor)
        elif len(monstergroup) == 0:
            Player.duel = True # both players alive, no monsters alive
            screentext2.newmsg("Duel mode. Both Players, fight each other!", (255,0,255))
        elif len(monstergroup) == 1:
            Player.duel = False
            screentext2.newmsg("Both players, fight the monster", (255,0,255))
        elif len(monstergroup) > 1:
            Player.duel = False
            screentext2.newmsg("Both players, fight the monsters", (255,0,255))
        if gameOver: # overtime to watch score, explosion etc
            overtime -= seconds
            screentext1.newmsg("Game over. Overtime: %.2f" % overtime)
            if overtime < 0:
                mainloop = False
        else: # not yet gameOver
            screentext1.newmsg("Time left: %.2f" % (gametime - playtime),(255,0,255))
            #if player1.bullets_fired > 0:
            screentext3.newmsg("Red player: bullets: %i hit: %i quota: %.2f%% rockets: %i hits: %i quota: %.2f%%"
                               % (player1.bullets_fired, player1.bullets_hit, player1.bullets_hit *100.0 / max(1, player1.bullets_fired),
                               player1.rockets_fired, player1.rockets_hit, player1.rockets_hit * 100.0 / max(1,player1.rockets_fired))
                               ,(255,0,0))
            screentext4.newmsg("Blue player: bullets: %i hit: %i quota: %.2f%% rockets: %i hits: %i quota: %.2f%%"
                               % (player2.bullets_fired, player2.bullets_hit, player2.bullets_hit *100.0 / max(1, player2.bullets_fired),
                               player2.rockets_fired, player2.rockets_hit, player2.rockets_hit * 100.0 / max(1,player2.rockets_fired))
                               ,(0,0,255))
        # ----------- clear, draw , update, flip -----------------  
        allgroup.clear(screen, background)
        allgroup.update(seconds)
        allgroup.draw(screen)           
        pygame.display.flip()         

if __name__ == "__main__":
    game()

en/pygame/step019.txt · Last modified: 2020/05/15 22:52 by horst