The Python Game Book

code games. learn Python.

User Tools

Site Tools


en:pygame:step019

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
en:pygame:step019 [2020/05/03 21:35]
horst ↷ Links adapted because of a move operation
en:pygame:step019 [2020/05/15 22:52] (current)
horst
Line 74: Line 74:
 View/Edit/Download the file directly in Github: https://github.com/horstjens/ThePythonGameBook/blob/master/pygame/019_homing_missiles.py View/Edit/Download the file directly in Github: https://github.com/horstjens/ThePythonGameBook/blob/master/pygame/019_homing_missiles.py
  
-//click reload in your browser if you see no code here:// +<code python> 
-<html> +#!/usr/bin/env python 
- <script src="http://gist-it.appspot.com/github/horstjens/ThePythonGameBook/blob/master/pygame/019_homing_missiles.py"> +# -*- coding: utf-8 -*- 
- </script></html>+""" 
 +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()
 +
 +</code>
 ^ [[:en:pygame:step018| ← previous]] ^ [[en:pygame:start| ↑ Overview]] ^ [[:en:pygame:step020| → next ]] ^ ^ [[:en:pygame:step018| ← previous]] ^ [[en:pygame:start| ↑ Overview]] ^ [[:en:pygame:step020| → next ]] ^
  
-====== comment this page ====== 
-~~DISQUS~~ 
  
en/pygame/step019.1588534520.txt.gz · Last modified: 2020/05/03 21:35 by horst