ThePythonGameBook

learn Python. Create Games

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
en:pygame:step019 [2020/05/03 21:35]
Horst JENS ↷ Links adapted because of a move operation
en:pygame:step019 [2020/05/15 22:52] (current)
Horst JENS
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~~ 
  
/var/www/horst/thepythongamebook.com/data/pages/en/pygame/step019.txt · Last modified: 2020/05/15 22:52 by Horst JENS