The Python Game Book

code games. learn Python.

User Tools

Site Tools


Sidebar

Navigation

indexmenu_n_9999 Start
msort nsort navbar

fr:ressources:jeux:schiff

Schiff

Schiff signifie vaisseau en allemand. Il aurait besoin de plus de travail, mais il est jouable.

captures d'écrans

capture d'écran du jeu Schiff


comment jouer

pilotez votre(vos) vaisseau(x) 1) au clavier pour les orienter ou les déplacer vers l'avant ou l'arrière. Essayez d'éviter les balles ennemies et les rectangles jaunes flottants. Le dernier vaisseau flottant gagne la partie.

fonctionnalités

aucun fichier externe nécessire en dehors de python et de pygame

joueurs

jusqu'à 4 joueurs, en se partageant un clavier.

Vous aurez besoin de Python (2.5.2 ou plus) et de Pygame (1.8.1 ou plus) pour lancer ce jeu.

Code source

#!/usr/bin/env python
# -*- coding: utf-8 -*-

#         - - - - - -
# example with vector class for my pygamebook
# LICENSE: Gnu General Public License (GPL)
# includes vec2d class from www.pygame.org
# AUTHOR: (from the game, not from the vec2dclass): Horst JENS
# email: horst.jens@gmail.com
# web: http://www.spielend-programmieren.at
#        - - - - - - - 

#       
#       This program is free software; you can redistribute it and/or modify
#       it under the terms of the GNU General Public License as published by
#       the Free Software Foundation; either version 2 of the License, or
#       (at your option) any later version.
#       
#       This program is distributed in the hope that it will be useful,
#       but WITHOUT ANY WARRANTY; without even the implied warranty of
#       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#       GNU General Public License for more details.
#       
#       You should have received a copy of the GNU General Public License
#       along with this program; if not, write to the Free Software
#       Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
#       MA 02110-1301, USA.



# idee Theo:
# schlachtschiff
# ölflecken, minen
# single mit mehr tasten
# peace-modus
# 4 Tasten für Schiffsteuerung, 2 Tasten für Geschützsturm-Steuerung
# Items
#  bonus wenn mehrere Items der selben ARt (Laser, Gescütz etc) aufgesammelt werden -> Upgrades
# Item in Fässern, Fässer muss man zerschiessen ( viele Hitpoints)


#FIXME: seit pygame.clock-reparatur alles zu schnell ?
#FIXME: tank.reload ist derzeit sinnlos
#FIXME: bar vom AI Schiff hinkt nach
#FIXME: rotation ordentlich machen, ohne -90° und *-1
#FIXME: self.rect.center - oldcenter in Ordnung bringen
#FIXME: Modus sinnvoll machen (derzeit nur space sinnvoll)
#FIXME: spacemove und newmoving vereinheitlichen
#FIXME: division by Zero kapieren
#FIXME: myfont eine globale Variable oder Konstante machen (im Sprite Text)


#Fixme: öltropfen spawnen zu sehr/dauernd nach Explosion... nicht-spawn-zeit einbauen ?

#TODO: AI-Schiff fährt immer in die Mitte der 4 Spieler...4 Viereck hat nicht unbedingt einen exakten Mittelpunkt
#TODO: AI-Schiff Masse (soll nicht so leicht aus der Bahn schiessbar sein)
#TODO: AI-Schiff drehen
#TODO: braunes AI Schiff ruckelt
#TODO: recalc raushaun
#TODO: tracer code entfernen oder Bugwellensystem / Blasen
#TODO: braunes Schiff: schönere Form, drehung, Antrieb, schüsse

#TODO: 4 Player / Bots / AI !
#TODO: verstehen: wird ein pos-Vektor als positionsargument übergeben wandert das ding mit (siehe victory)
#TODO: GUI oder config

#TODO: Reibung, 
#TODO: optional sich in die Kurve legen (x-schrumpfen) bei rotation-speed<>0
#TODO: Wind
#TODO: Feuer, Riffe oder gefährlicher Rand
#TODO: Segelschiffe, schiessen seitlich, eventuell Buggeschütz, schwaches Heckgeschütz(e)
#TODO: Rammen (Rammwinkel)
#TODO: mehr Segelschiff-sim
#TODO: Vektoren schöner machen, mit Pfeilspitzen oder Dreiecke

#TODO: wenn ölfleck getroffen, schneller / grösser / driften

#done: AI-Schiff hat 3 Kreise (Geschütztürme)
#done: schussweite (flytime) verringert.
#done: patrol oil rausgehaut. ölflecken nur noch dann wenn ein schiff abgeschossen wird (inkl. grosses AI schiff)
#done: patrol oil spawnt nur im eck und schrumpft nicht
#done: schwarzes AI Schiff verschwindet manchmal
#done: stossen 2 Ölflecken längere Zeit zusammen dann "kalben" Sie einen baby-Ölfleck der ins Spielfeld schwimmt und bei Kollision mit Player explodiert.
#done: AI-Schiffe kommen auch vom Zentrum. Sind selbst Feuerempfindlich-
#done: gelbe Vektoren für Feuer, schwarze Vektoren für AI Schiff
#done: schwarze statt braun, violett statt schwarz.
#done: Ölflecken patrollieren am Rand, spawnen von rand-mitte
#done: GhostTank dreht sich nicht ordentlich mit boss mit.
#done: regeneration (self.regen) von Tanks
#done: Schüsse werden kleiner, mehr damage wenn Schüsse gross sind, Tank hat flytime (für Schüsse)
#done: cooler Bug am Anfang, Schiffe schiessen am Start
#done: Spiel erkennt nicht Game-Over Situation
#done: hitratio - division by zero bug gelöst
#done: pygame <1.8 versionsabfrage, sollte jetzt auch mit pygame 1.7 laufen
#done: Tastaturkürzel sinnvoller (links/rechts), auf mac/netbook testen. Vorschlag: player1: wasd, player2: ijkl oder cursor
#done: 4 Player !
#done: feuervektorliste, gegnervektorliste
#done: Mausvecktor und Positionsvektor mit variabler Länge
#done: Vektorenkreise schön an screenrectseiten ausrichten (Blocksatz)
#done: Vektoren wachsen aus kreis raus (bei ship-coll)
#done: seltsamer zoom-fehler debugt (textsize erhöht anstatt rotozoom bei text verwendet)
#done: Text-classe die schön zoomt und davon abgeleitete hitpointtext mit hitpoint-update
#done: hitpointtext machen
#done: gelber Damagetext bei Ölflecken (danger)
#done: Vectorsprite holt sich koordinaten von bosssprite (Tank)
#done: Victory und Looser Text, vorbereitet für mehr als 2 Spieler
#done: Ölflecken "fressen" Bälle
#done: 4 Ölflecken, am Rand
#done: player2 startet rechts unten
#done: finale Explosion
#done: 2. Vektorspirte mehr nach rechts
#done: unglieche tick-zeit..kugeln fliegen unterschiedlich schnell (?)
#done: schiffe stoppen total bei zusammenstoss
#done: schüsse bremsen (gegenvektor), nicht nur verschieben der Position
#done: schwimmendes Feuer (sehr primitv)
#done:  hitpoints-balken, trefferanzeige
#done: Kugeln haben Farbe von Bosssprite
#done: keine Schmutzflecken mehr. Vorher: entstehen bei speed und schiessen.
#done: ball und schuss
#done: screen und screenrect
#done: move-vektor
#done: Forcevektor
#done: Mausvektor
#done: brems-steuerungslichter
#done: speedlimit
#done: modus umschalten mit m
#done: time-based movement
#done: 2 Tracer Linien von den Eckpunkten, wie Kondensstreifen

from __future__ import division
import operator # für vec2d
import math     # für vec2d
# für mich
import pygame   
import random
random.seed() # init random generator with time or other random value
#import vec2d

    



class vec2d(object):
    """2d vector class, supports vector and scalar operators,
       and also provides a bunch of high level functions
       """
    __slots__ = ['x', 'y']
 
    def __init__(self, x_or_pair, y = None):
        if y == None:
            self.x = x_or_pair[0]
            self.y = x_or_pair[1]
        else:
            self.x = x_or_pair
            self.y = y
 
    def __len__(self):
        return 2
 
    def __getitem__(self, key):
        if key == 0:
            return self.x
        elif key == 1:
            return self.y
        else:
            raise IndexError("Invalid subscript "+str(key)+" to vec2d")
 
    def __setitem__(self, key, value):
        if key == 0:
            self.x = value
        elif key == 1:
            self.y = value
        else:
            raise IndexError("Invalid subscript "+str(key)+" to vec2d")
 
    # String representaion (for debugging)
    def __repr__(self):
        return 'vec2d(%s, %s)' % (self.x, self.y)
    
    # Comparison
    def __eq__(self, other):
        if hasattr(other, "__getitem__") and len(other) == 2:
            return self.x == other[0] and self.y == other[1]
        else:
            return False
    
    def __ne__(self, other):
        if hasattr(other, "__getitem__") and len(other) == 2:
            return self.x != other[0] or self.y != other[1]
        else:
            return True
 
    def __nonzero__(self):
        return self.x or self.y
 
    # Generic operator handlers
    def _o2(self, other, f):
        "Any two-operator operation where the left operand is a vec2d"
        if isinstance(other, vec2d):
            return vec2d(f(self.x, other.x),
                         f(self.y, other.y))
        elif (hasattr(other, "__getitem__")):
            return vec2d(f(self.x, other[0]),
                         f(self.y, other[1]))
        else:
            return vec2d(f(self.x, other),
                         f(self.y, other))
 
    def _r_o2(self, other, f):
        "Any two-operator operation where the right operand is a vec2d"
        if (hasattr(other, "__getitem__")):
            return vec2d(f(other[0], self.x),
                         f(other[1], self.y))
        else:
            return vec2d(f(other, self.x),
                         f(other, self.y))
 
    def _io(self, other, f):
        "inplace operator"
        if (hasattr(other, "__getitem__")):
            self.x = f(self.x, other[0])
            self.y = f(self.y, other[1])
        else:
            self.x = f(self.x, other)
            self.y = f(self.y, other)
        return self
 
    # Addition
    def __add__(self, other):
        if isinstance(other, vec2d):
            return vec2d(self.x + other.x, self.y + other.y)
        elif hasattr(other, "__getitem__"):
            return vec2d(self.x + other[0], self.y + other[1])
        else:
            return vec2d(self.x + other, self.y + other)
    __radd__ = __add__
    
    def __iadd__(self, other):
        if isinstance(other, vec2d):
            self.x += other.x
            self.y += other.y
        elif hasattr(other, "__getitem__"):
            self.x += other[0]
            self.y += other[1]
        else:
            self.x += other
            self.y += other
        return self
 
    # Subtraction
    def __sub__(self, other):
        if isinstance(other, vec2d):
            return vec2d(self.x - other.x, self.y - other.y)
        elif (hasattr(other, "__getitem__")):
            return vec2d(self.x - other[0], self.y - other[1])
        else:
            return vec2d(self.x - other, self.y - other)
    def __rsub__(self, other):
        if isinstance(other, vec2d):
            return vec2d(other.x - self.x, other.y - self.y)
        if (hasattr(other, "__getitem__")):
            return vec2d(other[0] - self.x, other[1] - self.y)
        else:
            return vec2d(other - self.x, other - self.y)
    def __isub__(self, other):
        if isinstance(other, vec2d):
            self.x -= other.x
            self.y -= other.y
        elif (hasattr(other, "__getitem__")):
            self.x -= other[0]
            self.y -= other[1]
        else:
            self.x -= other
            self.y -= other
        return self
 
    # Multiplication
    def __mul__(self, other):
        if isinstance(other, vec2d):
            return vec2d(self.x*other.x, self.y*other.y)
        if (hasattr(other, "__getitem__")):
            return vec2d(self.x*other[0], self.y*other[1])
        else:
            return vec2d(self.x*other, self.y*other)
    __rmul__ = __mul__
    
    def __imul__(self, other):
        if isinstance(other, vec2d):
            self.x *= other.x
            self.y *= other.y
        elif (hasattr(other, "__getitem__")):
            self.x *= other[0]
            self.y *= other[1]
        else:
            self.x *= other
            self.y *= other
        return self
 
    # Division
    def __div__(self, other):
        return self._o2(other, operator.div)
    def __rdiv__(self, other):
        return self._r_o2(other, operator.div)
    def __idiv__(self, other):
        return self._io(other, operator.div)
 
    def __floordiv__(self, other):
        return self._o2(other, operator.floordiv)
    def __rfloordiv__(self, other):
        return self._r_o2(other, operator.floordiv)
    def __ifloordiv__(self, other):
        return self._io(other, operator.floordiv)
 
    def __truediv__(self, other):
        return self._o2(other, operator.truediv)
    def __rtruediv__(self, other):
        return self._r_o2(other, operator.truediv)
    def __itruediv__(self, other):
        return self._io(other, operator.floordiv)
 
    # Modulo
    def __mod__(self, other):
        return self._o2(other, operator.mod)
    def __rmod__(self, other):
        return self._r_o2(other, operator.mod)
 
    def __divmod__(self, other):
        return self._o2(other, operator.divmod)
    def __rdivmod__(self, other):
        return self._r_o2(other, operator.divmod)
 
    # Exponentation
    def __pow__(self, other):
        return self._o2(other, operator.pow)
    def __rpow__(self, other):
        return self._r_o2(other, operator.pow)
 
    # Bitwise operators
    def __lshift__(self, other):
        return self._o2(other, operator.lshift)
    def __rlshift__(self, other):
        return self._r_o2(other, operator.lshift)
 
    def __rshift__(self, other):
        return self._o2(other, operator.rshift)
    def __rrshift__(self, other):
        return self._r_o2(other, operator.rshift)
 
    def __and__(self, other):
        return self._o2(other, operator.and_)
    __rand__ = __and__
 
    def __or__(self, other):
        return self._o2(other, operator.or_)
    __ror__ = __or__
 
    def __xor__(self, other):
        return self._o2(other, operator.xor)
    __rxor__ = __xor__
 
    # Unary operations
    def __neg__(self):
        return vec2d(operator.neg(self.x), operator.neg(self.y))
 
    def __pos__(self):
        return vec2d(operator.pos(self.x), operator.pos(self.y))
 
    def __abs__(self):
        return vec2d(abs(self.x), abs(self.y))
 
    def __invert__(self):
        return vec2d(-self.x, -self.y)
 
    # vectory functions
    def get_length_sqrd(self): 
        return self.x**2 + self.y**2
 
    def get_length(self):
        return math.sqrt(self.x**2 + self.y**2)    
    def __setlength(self, value):
        length = self.get_length()
        if length == 0:
            pass # do nothing
        else:
            self.x *= value/length
            self.y *= value/length

    length = property(get_length, __setlength, None, "gets or sets the magnitude of the vector")
       
    def rotate(self, angle_degrees):
        radians = math.radians(angle_degrees)
        cos = math.cos(radians)
        sin = math.sin(radians)
        x = self.x*cos - self.y*sin
        y = self.x*sin + self.y*cos
        self.x = x
        self.y = y
 
    def rotated(self, angle_degrees):
        radians = math.radians(angle_degrees)
        cos = math.cos(radians)
        sin = math.sin(radians)
        x = self.x*cos - self.y*sin
        y = self.x*sin + self.y*cos
        return vec2d(x, y)
    
    def get_angle(self):
        if (self.get_length_sqrd() == 0):
            return 0
        return math.degrees(math.atan2(self.y, self.x))
    def __setangle(self, angle_degrees):
        self.x = self.length
        self.y = 0
        self.rotate(angle_degrees)
    angle = property(get_angle, __setangle, None, "gets or sets the angle of a vector")
 
    def get_angle_between(self, other):
        cross = self.x*other[1] - self.y*other[0]
        dot = self.x*other[0] + self.y*other[1]
        return math.degrees(math.atan2(cross, dot))
            
    def normalized(self):
        length = self.length
        if length != 0:
            return self/length
        return vec2d(self)
 
    def normalize_return_length(self):
        length = self.length
        if length != 0:
            self.x /= length
            self.y /= length
        return length
 
    def perpendicular(self):
        return vec2d(-self.y, self.x)
    
    def perpendicular_normal(self):
        length = self.length
        if length != 0:
            return vec2d(-self.y/length, self.x/length)
        return vec2d(self)
        
    def dot(self, other):
        return float(self.x*other[0] + self.y*other[1])
        
    def get_distance(self, other):
        return math.sqrt((self.x - other[0])**2 + (self.y - other[1])**2)
        
    def get_dist_sqrd(self, other):
        return (self.x - other[0])**2 + (self.y - other[1])**2
        
    def projection(self, other):
        other_length_sqrd = other[0]*other[0] + other[1]*other[1]
        projected_length_times_other_length = self.dot(other)
        return other*(projected_length_times_other_length/other_length_sqrd)
    
    def cross(self, other):
        return self.x*other[1] - self.y*other[0]
    
    def interpolate_to(self, other, range):
        return vec2d(self.x + (other[0] - self.x)*range, self.y + (other[1] - self.y)*range)
    
    def convert_to_basis(self, x_vector, y_vector):
        return vec2d(self.dot(x_vector)/x_vector.get_length_sqrd(), self.dot(y_vector)/y_vector.get_length_sqrd())
 
    def __getstate__(self):
        return [self.x, self.y]
        
    def __setstate__(self, dict):
        self.x, self.y = dict
        
# ---------------------------
# start


class Tank(pygame.sprite.Sprite):
    def __init__(self, screenrect, id="player1", mass=100.0):
        pygame.sprite.Sprite.__init__(self)
        #, color=(0,255,0) , pos=(100.0,100.0)
        self.screenrect = screenrect
        #self.mass = mass # how heavy the ship is
        self.mass = 100
        self.geist = False # i am not a ghost
        self.friction = 0.9 # 0.9 reduce speed for 10% each second
        if id == "player1":
            self.vc = (160,200)
            self.pos = vec2d(160,200)
            self.rotating=-30.0 # heading west
            self.color = (255,64,64)
            self.color2 = (255,0,128)
            self.color3 = (255,128,0)
            self.l = pygame.K_a
            self.r = pygame.K_d
            self.u = pygame.K_w
            self.d = pygame.K_s

        elif id == "player2":
            #self.rotating = 0.0 
            self.vc = (screenrect.width - 160, 200)
            self.pos = vec2d(screenrect.width - 160, 200)
            self.rotating=-150.0 # heading west
            self.color = (64,64,255)
            self.color2 = (0,128,255)
            self.color3 = (128,0,255)
            self.l = pygame.K_LEFT
            self.r = pygame.K_RIGHT
            self.u = pygame.K_UP
            self.d = pygame.K_DOWN
        elif id=="player3":
            self.vc = (160, screenrect.height - 200)
            self.pos = vec2d(160, screenrect.height - 200)
            self.rotating= 30.0 # heading west
            self.color = (64,255,64)
            self.color2 = (0,255,128)
            self.color3 = (128,255,0)
            self.l = pygame.K_j
            self.r = pygame.K_l
            self.u = pygame.K_i
            self.d = pygame.K_k
        elif id=="player4":
            self.vc = (screenrect.width - 160, screenrect.height - 200)
            self.pos = vec2d(screenrect.width - 160, screenrect.height - 200)
            self.rotating= 150.0 # heading west
            self.color = (255,64,255) #64x3
            self.color2 = (32,64,0)
            self.color3 = (10,64,32)
            self.l = pygame.K_KP4 
            self.r = pygame.K_KP6 
            self.u = pygame.K_KP8  # or KP8
            self.d = pygame.K_KP5
            
        self.shotby = {}
        self.shotby["player1"] = 0
        self.shotby["player2"] = 0
        self.shotby["player3"] = 0
        self.shotby["player4"] = 0
        self.shotby["danger"] = 0
        self.shotbysomebody = 0
        self.wrap = True # wrap-around world
        self.force = 0
        self.regen = 1 # regenarte hitpoints per full second
        #--- reloading and ammo
        self.flytime =  2.5 #1.0 # 5.0 seconds ... how long a shot fly
        self.reloading = 0.1 # tank can not fire while relaoding -------- BUG !!!! -----
        self.reloadingtime = 0.3 # seconds needed to reload, 0.0 means automatic fire
        self.ammowrap = 1 # how many time the ammo will wrap around world edge
        self.moving = vec2d(0.0,0.0)
        self.image1 = pygame.Surface((50,50))
        self.image1.fill((255,255,255))         # fill with white
        self.image1.set_colorkey((255,255,255)) # make white transparent
        pygame.draw.rect(self.image1 , (10,10,10), (0,0,50,8))
        pygame.draw.rect(self.image1, (10,10,10), (0,42,50,50))
        self.p1 = (-25.0,-25.0) # middle is 25,25
        self.p2 = (-25.0, 25.0) # middle is 25,25
        pygame.draw.polygon(self.image1, (64,64,64), [(5,10),(5,40),(50,25)],0)
        pygame.draw.circle(self.image1, self.color,(25,25),16,0) # 0 füllt den Kreis
        self.image1.convert_alpha()
        self.image = self.image1.copy() # copy to not destroy image1
        self.rect = self.image.get_rect()
        self.rect.center = (round(self.pos.x,0),round(self.pos.y,0)) 
        self.limit = 100 # the speed limit # --------------------- ausprobieren
        self.rotspeed = 0.0
        self.maxrotspeed =300 # maximum turnspeed
        self.rotdelta = 30   # um wieviel gedreht wird
        self.forcedelta = 5  # 
        self.oldcenter = (0,0)
        self.oldcenter2 = (0,0)
        self.oldrot = 0
        self.spacemove= vec2d(0,0)
        self.newmoving = vec2d(0,0)
        self.cm = 0.0 # angle to mouse
        self.cmd = 0.0 # distance to mouse
        self.showvector = True
        self.modus = "space"
        self.speedflag = False
        self.showtext = False
        self.drawTracer = False
        self.id= id
        self.hitpoints = 500.0  # float division ! # 500.0
        self.hitpointsfull = 500.0       # 500.0
        self.peaceful = False
        self.hitradius = 20.0
        #self.damagemax = 1 # how much damage a bullet in the center does (20)
        # damagemax can be higher by 5 because it is difficult to
        # teleport a bullet into the center
        #self.damagemin = 1  # how much damage a bullet on the edge does
        self.offset = 15 # if set to 0 tank can complete disappear behind
                        # screen edge bevor wrap-around on other site
                        # a offset > 0 spoil that, so that a bit of the tank
                        # should be always visible
        #self.shotby = None
        self.shots = 0 # how many balls this tank shoots in his lifetime
        self.hits = 0 # how many times this thank hits an enemy in his lifetime
        self.age = 0.0
        self.ageseconds = 0
        self.alive = True
        
        
    def fire(self):
        """check if firing is allowed, return True if yes and set the reloadingtime"""
        if self.peaceful:
            return False
        elif self.reloading > 0:
            return False # Tank is still reloading
        elif not self.screenrect.collidepoint(self.rect.center):
            # firing not allowed if Tank center out of screenrect
            return False
        else:
            self.reloading = self.reloadingtime # now the weapon must be reloaded for some time
            self.shots += 1
            return True
        
    def update(self, tick_seconds):
        """tick is the time passed since last frame in seconds"""
        #save old position
        self.age += tick_seconds # Tank get older
        if self.age - self.ageseconds > 1: 
            # a full second survived, time for regeneration
            self.ageseconds = int(self.age)
            self.hitpoints += self.regen # heals each full second
        self.rect = self.image.get_rect()
        self.oldcenter = self.rect.center # from the image, only for rotating
        
        # check reloading time,check if ready to fire again
        if self.reloading: # <>0..True, 0..False
            self.reloading -= tick_seconds
        if self.reloading < 0:
            self.reloading = 0
        
        # image selector / rotation / speed lights painting
        self.image = self.image1.copy()
        if self.rotspeed < 0:
            #self.image = self.image2
            pygame.draw.rect(self.image,  (255,0,0), (0,0,8,8))
            pygame.draw.rect(self.image,  (255,0,0), (42,42,50,50))
        elif self.rotspeed > 0:
            pygame.draw.rect(self.image, (255,0,0), (0,42,8,50))
            pygame.draw.rect(self.image, (255,0,0), (42,0,50,8))           
        if self.force > 0:
            pygame.draw.rect(self.image, (255,0,0), (0,10,5,30))
        elif self.force < 0:
            pygame.draw.rect(self.image, (255,0,0), (45,10,50,30))
        

        #--- rotate the sprite
        self.rot(self.image)
        
        #--- calculate new position
        self.rect.center = (round(self.pos.x,0), round(self.pos.y,0))
        #self.oldcenter2 = self.rect.center # save for drawing

        #--- render mousevector  # FIXME...ugly !
        c = vec2d(self.rect.center) # vector to center on screen
        m = vec2d(pygame.mouse.get_pos()) #vector to mousepos on screen
        cm =  m - c # vector from sprite-center to mousepos
        self.cm = cm.angle
        self.cmd = cm.length 
        cm.length = 16 # shrink length to radius of sprite circle
        #    calulate the new middle of the rotated sprite
        nm = vec2d(self.image.get_rect().width/2,self.image.get_rect().height/2)
        cm += nm  # correction because sprite is rotated, middle point moves
        pygame.draw.line(self.image, (0,0,0), (nm.x,nm.y), (int(cm.x), int(cm.y)),1)

        # more precise keyboard event handler
        pressed_keys = pygame.key.get_pressed()
        if pressed_keys[self.l]:
            self.rotspeed += self.rotdelta 
        if pressed_keys[self.r]:
            self.rotspeed -= self.rotdelta
        if pressed_keys[self.u]:
            self.force += self.forcedelta
        if pressed_keys[self.d]:
            self.force -= self.forcedelta
            
        # halt rotation when not-auto
        if self.modus != "auto":
            if (not pressed_keys[self.l] and 
                not pressed_keys[self.r]):
                    self.rotspeed = 0 # stop rotation
            if (not pressed_keys[self.u] and
                not pressed_keys[self.d]):
                    self.force = 0 # stop moving
            
        #limit to limit, maxrotspeed
        if self.rotspeed > self.maxrotspeed:
            self.rotspeed = self.maxrotspeed
        if self.rotspeed < -self.maxrotspeed:
            self.rotspeed = -self.maxrotspeed
        
        if self.force >= self.limit:
            self.force = self.limit
            self.speedflag = True
        elif self.force <= -self.limit:
            self.force = -self.limit
            self.speedflag = True
        else:
            self.speedflag = False
            
        #reset rotation on 360 Degree
        if self.rotating < -360:
            self.rotating += 360
        if self.rotating > 360:
            self.rotating -= 360
        
        # time-based rotation        
        self.rotating += self.rotspeed * tick_seconds 
        
        #FIXME: das geht sicher einfacher auch
        # simple forward movement
        if self.modus=="space":
            if self.force!=0:
                if self.rotating != 0:
                    dazu = vec2d(self.force, 0)
                    dazu.rotate(-self.rotating)
                    self.spacemove += dazu
                else:
                    self.spacemove += vec2d(self.force,0)
            # check speed limit
            if self.spacemove.length >= self.limit:
                self.spacemove.length = self.limit
                self.speedflag = True
            else:
                self.speedflag = False
            #self.pos += self.spacemove * tick_seconds
            self.newmoving = self.spacemove # komaptibel machen ?
            
        else:
            if self.force!= 0:
                self.newmoving = vec2d(self.force,0)
                if self.rotating != 0:
                    self.newmoving.rotate(-self.rotating)
                #self.pos += self.newmoving * tick_seconds
        self.newmoving *= self.friction ** tick_seconds # Reibung Leo
        self.pos += self.newmoving * tick_seconds 
        if self.wrap: 
            # wrap-around world
            #if self.pos.x + self.rect.width/2 < 0:
            #    #self.pos.x = self.screenrect.get_width()  + self.rect.width/2
            #    self.pos.x = self.screenrect.width  + self.rect.width/2 - self.offset
            #if self.pos.x - self.rect.width/2 > self.screenrect.width:
            #    self.pos.x = 0 - self.rect.width/2 + self.offset
            #if self.pos.y + self.rect.height/2 < 0:
            #    self.pos.y = self.screenrect.height + self.rect.height/2 - self.offset
            #if self.pos.y - self.rect.height/2 > self.screenrect.height:
            #    self.pos.y = 0 - self.rect.height/2 + self.offset
            #--------- wrap at self.rect.center, TankGhost will care about the Rest
            if self.pos.x < 0:
                self.pos.x = self.screenrect.width
            if self.pos.x > self.screenrect.width:
                self.pos.x = 0
            if self.pos.y < 0:
                self.pos.y = self.screenrect.height
            if self.pos.y > self.screenrect.height:
                self.pos.y = 0
            
            
        else:
             #no wrap-around  , stop on end of screen
            if self.pos.x < 0:
                self.pos.x = 0 
            if self.pos.x > self.screenrect.width:
                self.pos.x = self.screenrect.width
            if self.pos.y < 0:
                self.pos.y = 0
            if self.pos.y > self.screenrect.height:
                self.pos.y = self.screenrect.height
        
        #draw tracer ?
        if self.drawTracer:
            self.tracer()
        #else:
        #    self.dirtyrect=(0,0,0,0)
        
    def tracer(self):  
        #test if tracer is not too long (because world-wrap)
        nc = vec2d(self.rect.center)
        oc = vec2d(self.oldcenter2)
        if (nc - oc).length < self.rect.width:
            pygame.draw.line(background, self.color,self.oldcenter2, self.rect.center,2)
            nc1 = vec2d(self.p1)
            nc2 = vec2d(self.p2)
            oc1 = vec2d(self.p1)
            oc2 = vec2d(self.p2)
            # rotate the tracer-vectors
            vec2d.rotate(nc1,-self.rotating)
            vec2d.rotate(nc2,-self.rotating)
            vec2d.rotate(oc1,-self.rotating)
            vec2d.rotate(oc2,-self.rotating)
            #"----adding rect center----"
            nc1 += nc 
            nc2 += nc
            #"------ adding oldcenter ------"
            oc1 += oc
            oc2 += oc
            # draw the lines from old to new
            pygame.draw.line(background, self.color2, (oc1.x, oc1.y),(nc1.x,nc1.y),1)
            pygame.draw.line(background, self.color3, (oc2.x, oc2.y),(nc2.x,nc2.y),1)
            #self.dirtyrect =(int(min(nc1.x, nc2.x, oc1.x, oc2.x, oc.x, nc.x))-1,
            #                 int(min(nc1.y, nc2.y, oc1.y, oc2.y, oc.y, nc.y))-1,
            #                 int(max(nc1.x, nc2.x, oc1.x, oc2.x, oc.x, nc.x))+1,
            #                 int(max(nc1.y, nc2.y, oc1.y, oc2.y, oc.y, nc.y))+1)
        self.oldcenter2 = self.rect.center # save old tracerposition        
                                        
    def rot(self, image):
        self.image = pygame.transform.rotate(image, self.rotating)
        self.rect.center = self.oldcenter 
    def kill(self):
        self.alive = False
        pygame.sprite.Sprite.kill(self)
    
class TankGhost(Tank):
    def __init__(self, boss, id):
        """ id = N, S , W, E"""
        Tank.__init__(self, boss.screenrect, boss.id, mass=100.0)
        self.boss = boss
        self.id = id
        self.color = boss.color
        self.peaceful = True # Ghost does not fire
        self.geist = True # i am a ghost
            
    def update(self, tick_seconds):
        # is boss dead ?
        if self.boss.alive == False:
            pygame.sprite.Sprite.kill(self)
            #TankGhost.kill(self)
            
        self.image = self.boss.image
        self.rect = self.image.get_rect()
        if self.id == "N":
            self.rect.center = (self.boss.rect.centerx, self.boss.rect.centery - self.boss.screenrect.height)
        elif self.id == "S":
            self.rect.center = (self.boss.rect.centerx, self.boss.rect.centery + self.boss.screenrect.height)
        elif self.id == "W":
            self.rect.center = (self.boss.rect.centerx - self.boss.screenrect.width , self.boss.rect.centery)
        elif self.id =="E":
            self.rect.center = (self.boss.rect.centerx + self.boss.screenrect.width , self.boss.rect.centery)
        
 
         
    
            
            
            
class Bar(pygame.sprite.Sprite):
        """ a health-bar floating above each player"""
        
        def __init__(self, boss, vectorbar=False, color=(0,255,0)):
            pygame.sprite.Sprite.__init__(self)
            self.vectorbar = vectorbar
            self.boss = boss
            if self.vectorbar:
                self.long = 300 # vectorsprite circle radius * 2
            else:
                self.long = boss.rect.width
            self.longold = self.long
            self.percent = 1.0
            self.percentold = 0.0
            self.color = color
            self.image = pygame.Surface((self.long,5))
            
        def update(self, tick):
            """tick is the time passed since last frame in seconds"""
            if self.boss.hitpoints <= 0:
                self.kill()
            self.percent = (self.boss.hitpoints /
                            self.boss.hitpointsfull)
            if not self.vectorbar:
                self.long = self.boss.rect.width
            if self.percent != self.percentold or self.longold != self.long:
                self.image = pygame.Surface((self.long,5))
                self.rect = self.image.get_rect()
                self.image.fill((255,255,255))        # white
                pygame.draw.rect(self.image,(0,255,0),(0,0,
                                 int(self.long*self.percent), 5))
                #black rectangle
                pygame.draw.rect(self.image,(0,0,0),(0,0,self.long,5), 1) 
                self.image.set_colorkey((255,255,255))
                self.image.convert_alpha()
            if self.vectorbar:
                self.rect.centerx = self.boss.vc[0]
                self.rect.centery = self.boss.vc[1] - 160 # vectorbar above vectorcircle
            else:
                self.rect.centerx = self.boss.rect.centerx
                self.rect.centery = self.boss.rect.y - 10
            self.percentold = self.percent
            self.longold = self.long

class Text(pygame.sprite.Sprite):
        """a changable text"""
        def __init__(self, pos, msg="Hello World",color=(5,5,5), maxlifetime=-1.0, textsize=25, vec=vec2d(0,0),msgchange=False, zoom=False):
            """ negative maxlifetime means stay until Game Over
                msgchange means the message may change over time
                pos in format(x,y), will be changed into vec2d in Text__init__"""
            pygame.sprite.Sprite.__init__(self)
            #self.static = static #static means text does not disappear
            self.zoom = zoom
            self.zoomfactor = 4.0
            self.textsize = int(textsize)
            self.textsize0 = self.textsize
            self.msgchange = msgchange
            self.vec = vec
            self.pos = vec2d(pos)
            self.msg = msg
            self.color = color
            self.recalcimage()
            self.rect.center = pos
            self.lifetime = 0
            self.maxlifetime = maxlifetime #seconds
            self.tick = 0.0 # seconds since last frame
            #self.recalc(self.msg)

        
        def update(self, tick):
            """tick is passed seconds"""
            self.tick = tick
            self.lifetime += tick
            if self.zoom:
                self.textsize = int(self.textsize0 + self.lifetime * self.zoomfactor) # int instead of round becaouse round makes float and float upset pygame.font
                self.msgchange = True
            if self.msgchange:
                self.recalcimage()
            if self.vec != vec2d(0,0):
                self.pos += self.vec * self.tick # self.pos is a vec2d object
                self.rect.center  = (round(self.pos.x,0), round(self.pos.y,0))
            if self.maxlifetime > 0 and self.lifetime > self.maxlifetime:
                self.kill()
        
        #def kill(self):
        #    pygame.sprite.Sprite.kill(self)
            
        def recalcimage(self):
            """renders the text to self.image"""
            self.font = pygame.font.SysFont("None",self.textsize)
            self.textsurface1 = self.font.render(self.msg, True, self.color) #antialias = False
            self.image = pygame.Surface((self.textsurface1.get_size()))
            self.image.fill((255,255,255))         # fill with white
            self.image.set_colorkey((255,255,255)) # make white transparent
            #self.image = self.image1.copy() # to save image1
            self.image.blit(self.textsurface1,(0,0))
            self.rect = self.image.get_rect()
            self.rect.center  = (round(self.pos.x,0), round(self.pos.y,0))
     


class HitpointText(Text):
    """ this class inherit from Text class and updated the hitpoints of a given player"""
    def __init__(self, boss):
        Text.__init__(self, boss.vc , "dummytext", (0,0,0),-1,20, vec2d(0,0), True, False)
        self.boss = boss
        self.pos += vec2d(0,-170) # self.pos is a vec2d object from Text ?
    def update(self, tick):
        self.msg = "Hitpoints:"+str(self.boss.hitpoints)
        Text.update(self, tick)




        
        
class VectorSprite(pygame.sprite.Sprite):
    """draws a drawing of the tank in a fixed position
       including vectors for relative speed, enemy etc."""
    def __init__(self, boss):
        pygame.sprite.Sprite.__init__(self)
        self.boss = boss
        #self.startx = startx
        self.image0 = pygame.Surface((300,300)) # 200,200
        self.image0.fill((255,255,255))         # fill with white
        self.image0.set_colorkey((255,255,255)) # make white transparent
        self.image0.convert_alpha()
        self.vc = (int(self.boss.vc[0]),int(self.boss.vc[1])) # Vectorcenter from boss_sprite
        self.color = self.boss.color # get color from boss
        # draw polygon, same as Tank, but without colors
        self.image1 = pygame.Surface((50,50))
        self.image1.fill((255,255,255))         # fill with white
        self.image1.set_colorkey((255,255,255)) # make white transparent
        pygame.draw.rect(self.image1 , self.color, (0,0,50,8),1)
        pygame.draw.rect(self.image1 , self.color, (0,42,50,8),1) # small bug
        pygame.draw.polygon(self.image1, self.color, [(0,10),(0,40),(50,25)],1)
        pygame.draw.circle(self.image1, self.color,(25,25),16,1) # 0 füllt den Kreis
        pygame.draw.line(self.image1, self.color,(25,25),(50,25),1) # strich schaut nach rechts
        self.image1.convert_alpha()
        self.image = self.image0.copy() # to save image1
        self.rect = self.image.get_rect()
        self.size = 5
        self.dangerlist = []
        self.enemylist = []

    def learn(self, enemy, danger=True):
        """ learn about one enemy at a time, but not about self"""
        if danger:
            self.dangerlist.append(enemy)
        else:
            if enemy.color != self.boss.color:
                self.enemylist.append(enemy)
           
    
    def update(self, tick_seconds):
        """ update the vectors and rotation of the tank.
            tick_seconds is only here to get accepted, but i do nothing with it"""
        #self.dangerlist = []
        #self.enemylist = []
        if self.boss.showvector:
            #--- draw vectorsprite with correct rotation
            self.image = self.image0.copy()
            self.image2 = self.image1.copy()
            self.image2 = pygame.transform.rotate(self.image2 , self.boss.rotating)
            rect = self.image2.get_rect()
            center2 = rect.center
            #self.image.blit(self.image2, (100-rect.width/2, 100-rect.height/2))
            self.image.blit(self.image2, (150-rect.width/2, 150-rect.height/2))
            self.rect.center = self.vc
            
            #--- draw big mousevector
            #cm = vec2d(0,min(150, self.boss.cmd)) # cmd is sprite-mousedistance
            #cm.rotate(self.boss.cm-90) # self.boss.cm contains the angle from tank to mouse
            #cm += vec2d(self.vc)
            #pygame.draw.line(screen, (0,255,255),(self.vc), (cm.x, cm.y),1) # cyan
            
            #--- draw force vector
            f = 1.0 * self.boss.force / self.boss.limit # 1.5 is around the vectorcircle 
            cm = vec2d(0, f*150.0)
            cm.rotate(-self.boss.rotating-90)
            cm += vec2d(self.vc)
            pygame.draw.line(screen, self.boss.color, (self.vc), (round(cm.x,0), round(cm.y,0)),20)
            
            
            #--- draw space vector
            if self.boss.modus =="space":
                speed = 1.0 * self.boss.spacemove.length / self.boss.limit
                s = min(1, speed)
                if speed > 1:
                    self.size +=1
                else:
                    self.size = 5
                cm = vec2d(0, s*150.0)
                cm.rotate(self.boss.spacemove.angle-90)
                cm += vec2d(self.vc)
                pygame.draw.line(screen, (255,0,255), (self.vc), (int(cm.x), int(cm.y)),self.size) # pink
            
            #--- draw sprite vector
            cm =  vec2d(self.boss.rect.center) - vec2d(self.vc)  
            #diagonal = vec2d(self.vc) - vec2d(self.boss.screenrect.width, self.boss.screenrect.height)
            #dia = diagonal.length * 1.0 # to get float
            #cm.length = (cm.length / dia)*100
            cm.length = min(cm.length, 150.0)  #100
            cm+=vec2d(self.vc)
            pygame.draw.line(screen, (0,0,0), (self.vc), (int(cm.x), int(cm.y)),1)
                        
            #--- draw dangervector
            for dang in self.dangerlist:
                size = 1
                cd = dang.pos - self.boss.pos
                cd.length = min(cd.length, 150.0)
                if cd.length < 150:
                    size = int(150 - cd.length)
                cd+=vec2d(self.vc)
                if dang.type == "oil":
                    col = (255,255,0)
                else:
                    col = (0,0,0)
                pygame.draw.line(screen, col, (self.vc), (int(cd.x), int(cd.y)),size)
            self.dangerlist = []
            
            #--- draw enemyvector
            for enemy in self.enemylist:
                size = 1
                cd = enemy.pos - self.boss.pos
                cd.length = min(cd.length, 150.0)
                if cd.length < 150:
                    size = int(150 - cd.length)
                cd+=vec2d(self.vc)
                pygame.draw.line(screen, enemy.color, (self.vc), (int(cd.x), int(cd.y)),size)
            self.enemylist = []
            
        else:
            self.rect.center = (-400,-400) # hide vectorsprite out of screenrect


        
class Ball(pygame.sprite.Sprite):
    def __init__(self, bossSprite, smoke=False, mass=1):
        pygame.sprite.Sprite.__init__(self)
        self.boss = bossSprite
        self.radius = 5  #5
        Ball.paintme(self, self.radius)
        self.pos = vec2d(self.boss.rect.center)
        self.rect.center = (round(self.pos.x,0), round(self.pos.y,0))
        self.moving=vec2d(self.boss.limit * 1.1,0) # speeed of bullet = 110% of max speed of player 
        #self.moving.rotate(self.boss.cm) # shoot to mousepointer
        self.moving.rotate(-self.boss.rotating) # shoot to direction of Tank/Ship
        self.moving += self.boss.spacemove # add tank velocity to ball velocity
        self.massfactor = 0.2 # reduces the impact in relation to a players mass. #.91 a bissl gross
        self.ammowrap = self.boss.ammowrap # how many times bullet can wrap around screen edge
        self.age = 0.0 # lifetime of ball since start in seconds
        self.maxage = self.boss.flytime # ball is killed if he gets older than this time (in seconds)
        
        
    def update(self, tick_seconds):
        '''tick is time passed in seconds'''
        self.age += tick_seconds # ball get older
        if self.age > self.maxage:
            self.kill()
        else:
            factor = self.age / self.maxage # % of oldness of Ball
            Ball.paintme(self, 1 + self.radius - int(self.radius * factor))
        self.pos += self.moving * tick_seconds
        self.rect.center = (round(self.pos.x,0), round(self.pos.y,0))
        if not self.boss.screenrect.collidepoint(self.rect.center):
            if self.ammowrap > 0:
                self.ammowrap -=1 # one less time allowed to wrap world
                if self.pos.x > self.boss.screenrect.width:
                    self.pos.x = 0
                if self.pos.x < 0:
                    self.pos.x = self.boss.screenrect.width
                if self.pos.y > self.boss.screenrect.height:
                    self.pos.y = 0
                if self.pos.y < 0:
                    self.pos.y= self.boss.screenrect.height
            else:
                self.kill()

            
        
    def paintme(self, radius):
        self.tempradius = radius
        self.image = pygame.Surface((radius*2,radius*2))
        self.image.fill((255,255,255))         # fill with white
        self.image.set_colorkey((255,255,255)) # make white transparent
        pygame.draw.circle( self.image,self.boss.color, (radius,radius), radius , 0)
        self.image.convert_alpha()
        self.rect = self.image.get_rect()   

class Danger(pygame.sprite.Sprite):
    """ an enemy to all players. can be a floating piece of burning oil that damage players
    but ignore shooting.
    or can be an enemy ship."""
    oilborder = [0,0,0,0] # n,w,s,o # this is a class variable
    def __init__(self, screenrect, type="oil", size=40, pos=(0,0)):
        pygame.sprite.Sprite.__init__(self)
        self.size = size
        self.lifetime = 0.0
        self.oldsize = size
        self.screenrect = screenrect
        self.limit = 500 # absolute speed limit
        self.type = type # what kind of Danger, an oil or an boat ?
        self.bar = False # check if i have an hitpoint-bar
        self.wo = -1 # indicator of border of oil, -1 means free floating
        #self.pregnant = 0 # spawn a baby danger ?
        #self.birth = 100 # if self.pregnant reach this value, it will spawn a new baby-danger
        
        distance = 30 # distance from screenrect border to oil.rect.center
        
        if self.type == "oil":
            self.vulnerable = False # can  be shot down            
            self.damagePlayer = True # can damage player that bumb into it
            self.color = (255,random.randint(128,255),0) # between yellow and red ?
            
            if self.size==40:
                pass
                ## big oil, patrol border
                #self.hitpoints = 100
                #self.hitpointsfull = 100
                #self.size = 40
                #self.min = 50
                #self.max = 100 # oil float very slow
            
                ## where to spawn oil ?
                #for side in Danger.oilborder:
                    #if side == min(Danger.oilborder):
                        #wo = Danger.oilborder.index(min(Danger.oilborder))
                        #self.wo = wo
                        #Danger.oilborder[wo] += 1
                        #break
                #if wo == 0:
                    #self.pos = vec2d(self.screenrect.centerx, distance) 
                    #self.vec = vec2d(random.randrange(self.min,self.max) * random.choice([-1,1]), 0)
                #elif wo == 1:
                    #self.pos = vec2d(distance, self.screenrect.centery)
                    #self.vec = vec2d(0, random.randrange(self.min,self.max) * random.choice([-1,1]))
                #elif wo == 2:
                    #self.pos = vec2d(self.screenrect.centerx, self.screenrect.height - distance)
                    #self.vec = vec2d(random.randrange(self.min,self.max) * random.choice([-1,1]), 0)
                #elif wo == 3:
                    #self.pos = vec2d(self.screenrect.width - distance, self.screenrect.centery)
                    #self.vec = vec2d(0, random.randrange(self.min,self.max) * random.choice([-1,1]))
                
            else:
                # small oil. self.wo = -1
                self.hitpoints = 10
                self.hitpointsfull = 10
                self.min = 100
                self.max = 200
                self.pos = vec2d(pos)
                self.vec = vec2d(random.randrange(self.min, self.max) * random.choice([-1,1]),
                                 random.randrange(self.min, self.max) * random.choice([-1,1]))
            
            #for all oil:
            self.image = pygame.Surface((self.size,self.size))
            self.damage = 1 # hitpoint loss for player
            pygame.draw.rect( self.image,self.color, (0,0,self.size,self.size), 0)    
          
        elif self.type == "boat":
            self.vulnerable = True
            self.hitpoints = 100
            self.hitpointsfull = 100
            self.size = 50
            self.damagePlayer = True
            self.min = 5 # minimal speed
            self.max = 100 # maximal speed
            self.pos = vec2d(self.screenrect.centerx, self.screenrect.centery) 
            self.vec = vec2d(random.randrange(self.min,self.max) * random.choice([-1,1]), random.randrange(self.min,self.max) * random.choice([-1,1]))
            #self.damageBall = True
            self.heading = self.vec.angle
            self.vulnerable = True
            self.color = (100,100,100) # grey
            #-------
            self.damage = 10 # damage to player ?
            self.image = pygame.Surface((200,200))
            self.image.fill((255,255,255)) # fill white
            self.image.set_colorkey((255,255,255)) # make white transparent
            pygame.draw.polygon(self.image, self.color, [(0,100), (20,70), (190,70), (200,100), (190,130), (20,130) ] , 0)
            pygame.draw.circle(self.image, (40,100,40), (50,100), 20, 0)
            pygame.draw.circle(self.image, (40,100,40), (100,100), 20, 0)
            pygame.draw.rect(self.image, (40,40,100), [120,72,30,58])
            pygame.draw.circle(self.image, (40,100,40), (170,100), 20, 0)
            self.image.convert_alpha()
            #pygame.transform.flip(self.image, True, False)
            self.image0 = self.image.copy()
            
        # start in screenrect
        
        #complete random direction
        
        #self.image.convert_alpha()
        self.rect = self.image.get_rect()
        self.rect.center=  (round(self.pos.x,0), round(self.pos.y,0))
        
        

    
    def update(self, tick_seconds):
        '''tick is time passed in seconds'''
        self.lifetime+= tick_seconds
        if self.hitpoints < 0:
            self.kill()
        if self.type == "oil":
            if self.size != self.oldsize:
                 self.image = pygame.Surface((self.size,self.size))
                 self.rect = self.image.get_rect()
            self.color = (255,random.randint(128,255),0) # between yellow and red ?
            pygame.draw.rect( self.image,self.color, (0,0,self.size,self.size), 0)
        elif self.type == "boat":
            #heading is actual mouse position
            #self.vec = vec2d(pygame.mouse.get_pos) - vec2d(self.rect.center)
            c = vec2d(self.rect.center) # vector to center on screen
            m = vec2d(pygame.mouse.get_pos()) #vector to mousepos on screen
            cm =  m - c # vector from sprite-center to mousepos
            #self.cm = cm.angle
            #self.cmd = cm.length 
            cm.length = min(self.max, cm.length) # not faster than self.max
            self.vec = cm
            #rotate boat toward heading
            #self.oldpos = self.rect.center
            self.image = pygame.transform.rotate( self.image0, self.vec.angle)
            #self.rect.center = self.oldpos
        if self.vec.length > self.limit:
            self.vec.length = self.limit
        self.pos += self.vec * tick_seconds
        
        
        
        grow = False
        #no wrap-around  , stop on end of screen
        if self.pos.x < 0:
            #grow = True
            self.pos.x = 0  # stuck on the left side
            #if self.wo == 0 or self.wo == 2:
                #self.vec = vec2d(random.randrange(self.min,self.max), 0)
            #else:
            self.vec = vec2d(random.randrange(self.min,self.max), random.randrange(self.min,self.max) * random.choice([-1,1]))
        if self.pos.x > self.screenrect.width:
            #grow = True
            self.pos.x = self.screenrect.width # stuck on the right side
            #if self.wo == 0 or self.wo == 2:
                #self.vec = vec2d(random.randrange(self.min,self.max)*-1, 0)
            #else:
            self.vec = vec2d(random.randrange(self.min,self.max)*-1, random.randrange(self.min,self.max) * random.choice([-1,1]))
        if self.pos.y < 0:
            #grow = True
            self.pos.y = 0 # stuck on top border
            #if self.wo == 1 or self.wo == 3:
                #self.vec = vec2d(0,random.randrange(self.min, self.max))
            #else:
            self.vec = vec2d(random.randrange(self.min,self.max) * random.choice([-1,1]), random.randrange(self.min,self.max) )
        if self.pos.y > self.screenrect.height:
            #grow = True
            self.pos.y = self.screenrect.height #stuck on bottom
            #if self.wo == 1 or self.wo == 3:
                #self.vec = vec2d(0,random.randrange(self.min, self.max)*-1)
            #else:
            self.vec = vec2d(random.randrange(self.min,self.max) * random.choice([-1,1]), random.randrange(self.min,self.max) * -1)
        self.rect.center = (round(self.pos.x,0), round(self.pos.y,0))
        #self.oldsize = self.size       
        #if grow and self.wo > -1:
            #self.size +=1
        
class Wound(pygame.sprite.Sprite):
        """ a little explosion marking the 'wound' of a hit"""
        def __init__(self, pos, size, maxlifetime=0.5, boss_sprite=None):
            pygame.sprite.Sprite.__init__(self)
            self.size = int(size) # sometimes, wound comes as float (from tux?)
            #if self.size < medium:
            #    self.size *=2
            
            self.pos = pos
            self.maxlifetime = maxlifetime # in seconds
            self.boss_sprite = boss_sprite
            self.age = 0 # age in decimal seconds
            self.frames = 0 #frames in integer
            self.image = pygame.Surface((size*2, size*2))
            self.surface_center = (self.size, self.size) 
            self.rect = self.image.get_rect()
            self.rect.center = (round(self.pos.x,0),round(self.pos.y,0))
            #if self.boss_sprite:
            #    self.dx = self.pos[0] - self.boss_sprite.rect.centerx 
            #    self.dy = self.pos[1] - self.boss_sprite.rect.centery
            self.update(0) # update need the argument tick
            
        def update(self, tick_seconds):
            """ tick_seconds is decimalseconds passed since last frame"""
            self.age += tick_seconds
            self.frames +=1
            if self.age > self.maxlifetime: #one second at 30 fps
                self.kill()
            else:
                self.rect.center = (round(self.pos.x, 0), round(self.pos.y,0))
                pygame.draw.circle(self.image, (255,random.randint(0,255),0),
                     self.surface_center,min(self.size,self.frames), 0)
                self.image.set_colorkey((0,0,0))
                self.image.convert_alpha()
                #if self.boss_sprite:
                #    self.rect.centerx = self.boss_sprite.rect.centerx + self.dx
                #    self.rect.centery = self.boss_sprite.rect.centery + self.dy
                #else:
                #    self.rect.center = self.pos
                



def paint(x, y,  color):
    pygame.draw.circle(background, color, (x,y), 150, 1)
    pygame.draw.line(background, color, (x-160, y) , (x+160,y), 1)
    pygame.draw.line(background, color, (x,y-160), (x,y+160),1)
    textsurface = myFont.render(u"0° = 360°", True, color)
    background.blit(textsurface, (x+50,y-20))
    textsurface = myFont.render(u"90°", True, color)
    background.blit(textsurface, (x-10,y-150))
    textsurface = myFont.render(u"180°", True, color)
    background.blit(textsurface, (x-140,y-20))
    textsurface = myFont.render(u"270°", True, color)
    background.blit(textsurface, (x-10,y+125))
    
#--- main start  --
pygame.init()
try:
    screen=pygame.display.set_mode((0,0)) # (0,0) uses the full screensize
except:
    x = raw_input("enter x resolution of screen:", 800)
    y = raw_input("enter y resolution of screen:", 600)
    if int(x) <= 0 or int(y) <= 0:
        print "bad integer value. defaulting to 800x600"
        x = 800
        y = 600
    screen=pygame.display.set_mode((x,y))
screenrect = screen.get_rect()
pygame.display.set_caption("press Esc to exit")
#--- background
background = pygame.Surface(screen.get_size())
#background = background.convert()
background.fill((255,255,255))     #fill the background white
#a bit of text
myFont = pygame.font.SysFont("None",25)
#textsurface = myFont.render("Player1: F1=modus, w,as,d,cursor=move, space=shoot", True, (0,0,255))
textsurface0 = myFont.render("pres ESC to Quit", True, (0,0,0))
textsurface1 = myFont.render("Player1: w,a,s,d", True, (0,0,0))
#textsurface2 = myFont.render("Player2: F12=modus, j,i,l,k, Numpad=move, RCTRl,NUM_Enter=shoot", True, (0,0,255))
textsurface2 = myFont.render("Player2: j,i,l,k", True, (0,0,0))
textsurface3 = myFont.render("Player3: cursor-keys", True, (0,0,0))
textsurface4 = myFont.render("Player4: Numpad 4,8,5,6", True, (0,0,0))
#background.blit(textsurface, (20,410)) # blit the textsurface on the backgroundsurface
#background.blit(textsurface2, (20, 430))
#background.blit(textsurface3, (20, 450))
# red circle with crosshair

paint(160, 200, (255,32,32)) # player1, red, left upper corner
paint(screenrect.width - 160, 200, (32,32,255)) # player2, blue right upper corner
paint(160, screenrect.height - 200 , (32,255,32)) # player3, green, lower left corner
paint(screenrect.width - 160, screenrect.height - 200 , (32,32,32)) # player4, black, lower right corner

background.blit(textsurface1, (screenrect.width/2-40,50))
background.blit(textsurface2, (screenrect.width/2-40,70))
background.blit(textsurface3, (screenrect.width/2-40,90))
background.blit(textsurface4, (screenrect.width/2-40,110))
background.blit(textsurface0, (screenrect.width/2-40,130))

backgroundnew = background.copy()
screen.blit(background, (0,0)) # blit the  backgroundsurface on the screen

#--- sprite groups
vectorgroup = pygame.sprite.Group()
ballgroup = pygame.sprite.Group()
tankgroup = pygame.sprite.Group()
bargroup = pygame.sprite.Group()
dangergroup = pygame.sprite.Group()
woundgroup = pygame.sprite.Group()
textgroup = pygame.sprite.Group()
newgroup = pygame.sprite.Group()
# create some sprites
#--- player1 
player1 = Tank(screenrect,"player1") #red
player1vector = VectorSprite(player1)
textgroup.add( HitpointText(player1)  )
#--- player2
player2 = Tank(screenrect,"player2") # blue
player2vector = VectorSprite(player2)
textgroup.add( HitpointText(player2)  )
#-- player3
player3 = Tank(screenrect, "player3") # green
player3vector = VectorSprite(player3)
textgroup.add( HitpointText(player3))
#-- player4
player4 = Tank(screenrect, "player4") # black
player4vector = VectorSprite(player4)
textgroup.add( HitpointText(player4))



# put the sprites in the correct group
bargroup.add(Bar(player1), Bar(player1, True),
             Bar(player2), Bar(player2, True),
             Bar(player3), Bar(player3, True),
             Bar(player4), Bar(player4, True) )

tankgroup.add(player1, TankGhost(player1, "N"), TankGhost(player1, "S"), TankGhost(player1, "W"), TankGhost(player1, "E"),  
              player2, TankGhost(player2, "N"), TankGhost(player2, "S"), TankGhost(player2, "W"), TankGhost(player2, "E"),  
              player3, TankGhost(player3, "N"), TankGhost(player3, "S"), TankGhost(player3, "W"), TankGhost(player3, "E"),  
              player4, TankGhost(player4, "N"), TankGhost(player4, "S"), TankGhost(player4, "W"), TankGhost(player4, "E"))
#stuffgroup.add(player1text, player1speedtext, player1vector,  player2text, player2speedtext, player2vector)
vectorgroup.add(player1vector, player2vector, player3vector, player4vector)
#dangergroup.add( Danger(screenrect, "oil"),
#                 Danger(screenrect, "oil"),
#                 Danger(screenrect, "oil"),
#                 Danger(screenrect, "oil"),
#                 Danger(screenrect, "boat"))
dangergroup.add( Danger(screenrect, "boat") )
# hitpoints for boat
for dang in dangergroup:
    #if dang.vulnerable and not dang.bar:
    if (dang.bar == False)  and (dang.type != "oil"):
        bargroup.add(Bar(dang))
        dang.bar = True

#allgroup = pygame.sprite.LayeredUpdates(playervector, playertext, speedtext, player, ballgroup)
if pygame.ver < "1.8.1":
    allgroup = pygame.sprite.LayeredUpdates(vectorgroup,  dangergroup, woundgroup, textgroup, bargroup, ballgroup, tankgroup)
else:
    allgroup = pygame.sprite.Group(vectorgroup,  dangergroup, woundgroup, textgroup, bargroup, ballgroup, tankgroup)
    



#--- loop prepare ---
mainloop = True
#clock = pygame.time.Clock()
fps = 30 #frames per second
seconds_played = 0.0
recalc = False # recalculate the sprite groups reset to False
finale = False # draw the game-over scene (final explosion etc.)
startGameOverMsg = False
rank = 4 # decrease at each playerkill. 4 because 4 players at maximum
clock = pygame.time.Clock() # create Clock object
#--- mainloop ------
while mainloop:
    tick_time = clock.tick(fps) # milliseconds since last frame
    tick_seconds = tick_time / 1000.0 # decimal-seconds since last frame
    seconds_played += tick_seconds # counter, will not be resetted    
    #clock.tick(30)
    #event handler
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            mainloop = False
        elif event.type == pygame.KEYDOWN:
            # Toggle keys better with keydown than with pressed_keys
            #--- Esc quit -------------
            if event.key == pygame.K_ESCAPE:
                mainloop = False
   
               
            #--- clean F5 -------------
            if event.key == pygame.K_F5:    
                background = backgroundnew.copy()
                screen.blit(background, (0,0)) # redraw the background
            #---tracer F6 -----------------    
            if event.key == pygame.K_F6:
                for tank in tankgroup:
                    tank.drawTracer = not tank.drawTracer          

    #--- fire 
    for tank in tankgroup:
        if tank.fire():
            ballgroup.add(Ball(tank))
            recalc = True        
        
    #--- mainloop core -----------                  
    #--- clear the sprites
    allgroup.clear(screen, background)
     
    if recalc:
        # it is very important that this lines comes AFTER allgroup.clear!
        # else there will be ugly uncleaned sprites all around
        # allgroup = pygame.sprite.LayeredUpdates(stuffgroup,  woundgroup, textgroup, dangergroup, bargroup, tankgroup, ballgroup)
        #allgroup = pygame.sprite.LayeredUpdates(vectorgroup,  dangergroup, woundgroup, textgroup, bargroup, ballgroup, tankgroup)
        if newgroup:
            # any new dangersprites in this group ?
            for dang in newgroup:
                dangergroup.add(dang)
            #newgroup = [] # will be empty at beginning of danger-danger-collision-detection
            print len(dangergroup)
            for dang in dangergroup:
                # add hitbar if missing
                if (dang.bar == False)  and (dang.type != "oil"):
                    bargroup.add(Bar(dang))
                    dang.bar = True
            newgroup.empty() # remove all sprites from this group
                        
        
        if pygame.ver < "1.8.1":
            allgroup = pygame.sprite.LayeredUpdates(vectorgroup,  dangergroup, woundgroup, textgroup, bargroup, ballgroup, tankgroup)
        else:
            allgroup = pygame.sprite.Group(vectorgroup,  dangergroup, woundgroup, textgroup, bargroup, ballgroup, tankgroup)
    
        
        
    recalc = False # reset recalc
    #--- update all sprites
    allgroup.update(tick_seconds)
    #--- feed sprite-position to vectorgroup
    for vsprite in vectorgroup:
        for dang in dangergroup:
            vsprite.learn(dang, True)
        for tank in tankgroup:
            vsprite.learn(tank, False)
       
    #--- collision detection
    
    
    # ball kills ball:--- removed, because too slow
    #for ball in ballgroup:
    #    ballgroup2 = ballgroup.copy()
    #    crashgroup = pygame.sprite.spritecollide(ball, ballgroup2, False)
    #    killme = False
    #    for ball2 in crashgroup:
    #        if ball2.boss == ball.boss:
    #            #friendly fire
    #            pass # survive
    #        else:
    #            ball2.kill()
    #            killme = True
    #    if killme:
    #        ball.kill()
    
    # --- collision with tank and ball ?
    for tank in tankgroup:
        # ball is always inside screenrect.
        crashgroup = pygame.sprite.spritecollide(tank, ballgroup, False)
        for ball in crashgroup:
            if ball.boss != tank: # no friendly fire
               distance1 = vec2d(tank.pos) #calculate distance to tank center
               distance2 = vec2d(ball.pos)
               distance = distance1 - distance2
               if distance.length > tank.hitradius:
                    pass # nothing happened, bullet missed tank
               else:
                   tank.shotby[ball.boss.id] += 1 
                   tank.shotbysomebody += 1
                   ball.boss.hits += 1 # one point for the shooter                   
                   # ----- more damage depending on  impact speed 
                   impact = tank.spacemove - ball.moving
                   damage = int(impact.length/100)
                   # ----- additional damage if radius of Ball > 1 (point blank shot)
                   damage += ball.tempradius - 1 # tempradius is minimum 1
                   #print "imact: %i + ballsize: %i = damage: %i " % (int(impact.length/100), ball.tempradius - 1, damage)
                   textgroup.add(Text(vec2d(tank.pos), str(damage), ball.boss.color, 1.5, 25, 
                                      vec2d(random.randint(-90,90),random.randint(-250,-120)),
                                      False, True )) # vc = vectorcenter
                   if tank.id[0] == "p":
                       # is it the Tank or a TankGhost ? Tank name begins with player
                       tank.hitpoints -= damage 
                       tank.spacemove += ball.moving * ball.massfactor
                   else:
                       tank.boss.hitpoints -= damage
                       tank.boss.spacemove += ball.moving * ball.massfactor
                   woundgroup.add(Wound(ball.pos, 5))
                   recalc = True
                   ball.kill()
    
    # -- collision tank  with danger ?
    for tank in tankgroup:
        crashgroup = pygame.sprite.spritecollide(tank, dangergroup, False)
        for dang in crashgroup:
            if dang.damagePlayer:
                damage = dang.damage
                textgroup.add(Text(vec2d(tank.pos), str(damage), dang.color, 1.5, 25, 
                              vec2d(random.randint(-90,90),random.randint(-250,-120)),
                              False, True )) # vc = vectorcenter
                if tank.id[0] == "p":
                    # is it the Tank or a TankGhost ?                  
                    tank.hitpoints -= damage
                    tank.shotby["danger"] += 1
                else:
                    tank.boss.hitpoints -= damage
                    tank.boss.shotby["danger"] +=1
                tank.shotbysomebody += 1
                if dang.type == "oil":
                    dang.size -=1
                    #dang.size +=1
                    pass
                else:
                    dang.hitpoints -= 1
            
    #-- collision between ball and danger, explosion, :
    for dang in dangergroup:
        if dang.vulnerable:
            # ---------dang is an AI boat
            crashgroup = pygame.sprite.spritecollide(dang, ballgroup, True) #kill shot
            for ball in crashgroup:
                dang.vec += ball.moving * ball.massfactor
                woundgroup.add(Wound(ball.pos, 5))
                dang.hitpoints -= 1
                
                # dang dead ? explosion, drop goodie, respawn at center 
                
    # -- collision danger with danger ? 
    for dang in dangergroup:
         dangergroup2 = dangergroup.copy()
         dangergroup2.remove(dang) # remove myself from the clone group
         crashgroup = pygame.sprite.spritecollide(dang, dangergroup2, False) # dang survives
         for dang2 in crashgroup:
             if dang.type == "oil" and dang2.type == "oil" and dang.wo == -1 and dang2.wo == -1:
                 #  border patrols spawn only at corner
                 #the bigger oil eats the smaller oil
                 if dang.size > dang2.size and dang2.lifetime > 2.0:
                     dang.size += 1
                     dang2.size = max(5, dang2.size-1)
             if dang.type == "boat" and dang2.type == "oil":
                 dang.hitpoints -= 1
                 dang2.size -=1
                 textgroup.add(Text(vec2d(dang.pos), str(1), (255,255,0), 1.5, 25, 
                    vec2d(random.randint(-90,90),random.randint(120,240)),
                    False, True ))
    
    # -- collision tank with other player ?
    for tank in tankgroup:
        if tank.id[0] == "p":
            # is it the Tank or a TankGhost ?
            tankgroup2 = tankgroup.copy() # copy of tankgroup, include self
            tankgroup2.remove(tank) # remove self of group
            crashgroup = pygame.sprite.spritecollide(tank, tankgroup2, False)
            for tank2 in crashgroup:
                #FIXME --- bis mir was bessers einfällt
                tank.hitpoints -= 1
                textgroup.add(Text(vec2d(tank.pos), str(1), tank2.color, 1.5, 25, 
                  vec2d(random.randint(-90,90),random.randint(120,240)),
                  False, True )) # tank-collision: damage text floats DOWN instead up
        else:
            pass # no collision damage for TankGhost (yet)
            
    #-- danger hitpoint check and oilspawn check
    for dang in dangergroup:
        if dang.type == "oil":
            if dang.wo != -1:
                # immortal oil,  patrol the border
                dang.size = max(40, dang.size) # size can not be less than 10
            else:
                if dang.size < 1:
                    woundgroup.add(Wound(dang.pos, 50, .5)) # small explosion
                    dang.kill()
            if dang.size > 40:
                     dang.size = 40 #  reduce size and spawn child
                     newgroup.add(Danger(screenrect, "oil", 10, dang.pos))
        else:
            #dang is a boat
            if dang.hitpoints < 1:
                woundgroup.add(Wound(dang.pos, 75, .5)) # final explosion
                #rebirth of ship in the middel of the screen
                #spawn 5 mini-oils
                newgroup.add(Danger(screenrect, "oil", 10, dang.pos),
                         Danger(screenrect, "oil", 10, dang.pos),
                         Danger(screenrect, "oil", 10, dang.pos),
                         Danger(screenrect, "oil", 10, dang.pos),
                         Danger(screenrect, "oil", 10, dang.pos))
                dang.pos = vec2d(screenrect.centerx, screenrect.centery)
                dang.hitpoints = dang.hitpointsfull                
    
    
    #--- calculate middle position of polygon made by surviving players
    calclist = []
    for tank in tankgroup:
        if not tank.geist:
            calclist.append(tank.rect.center)
    
    #print calclist
    
    #--- tank hitpoint check, game over ?  
    for tank in tankgroup:
        if tank.hitpoints < 0:
            woundgroup.add(Wound(tank.pos, 100, .75)) # final explosion
            tank.peaceful = True # dead Tank cannot shoot
            #spawn oil
            newgroup.add(Danger(screenrect, "oil", 10, tank.pos),
                         Danger(screenrect, "oil", 10, tank.pos),
                         Danger(screenrect, "oil", 10, tank.pos),
                         Danger(screenrect, "oil", 10, tank.pos),
                         Danger(screenrect, "oil", 10, tank.pos))
            if len(tankgroup) > 5:
                textgroup.add(Text(tank.pos, "Loser", tank.color, 5.0, 72, vec2d(0,0), False, True))
                textgroup.add(Text(tank.vc+ vec2d(0,-50), "Loser", (0,0,0), -1, 48, vec2d(0,0), False, False))
                textgroup.add(Text(tank.vc+ vec2d(0,-25), "Rank: %i " % rank, (0,0,0), -1, 48, vec2d(0,0), False, False))
                rank -= 1
                textgroup.add(Text(tank.vc+ vec2d(0,35), "shot by:", (0,0,0), -1, 24, vec2d(0,0), False, False))
                if tank.shotbysomebody == 0:
                    tank.shotbysomebody = 1 # to avoid division by zero
                dy = 75
                if tank.id != "player1" and tank.shotby["player1"] > 0:
                    textgroup.add(Text(tank.vc+ vec2d(0,dy), "player1: %.2f%% " % (100*tank.shotby["player1"] / tank.shotbysomebody), (255,0,0), -1, 24, vec2d(0,0), False, False))
                    dy += 20
                if tank.id != "player2" and tank.shotby["player2"] > 0:
                    textgroup.add(Text(tank.vc+ vec2d(0,dy), "player2: %.2f%% " % (100*tank.shotby["player2"] / tank.shotbysomebody), (0,0,255), -1, 24, vec2d(0,0), False, False))
                    dy += 20
                if tank.id != "player3" and tank.shotby["player3"] > 0:
                    textgroup.add(Text(tank.vc+ vec2d(0,dy), "player3: %.2f%% " % (100*tank.shotby["player3"] / tank.shotbysomebody), (0,255,0), -1, 24, vec2d(0,0), False, False))
                    dy += 20
                if tank.id != "player4" and tank.shotby["player4"] > 0:
                    textgroup.add(Text(tank.vc+ vec2d(0,dy), "player4: %.2f%% " % (100*tank.shotby["player4"] / tank.shotbysomebody), (255,0,255), -1, 24, vec2d(0,0), False, False))
                if tank.hits == 0:
                    quota = 0.0
                else:
                    quota = tank.shots / tank.hits
                textgroup.add(Text(tank.vc+ vec2d(0,5), "hit ratio: %f" % (quota))) # future division, result in float
                tank.showvector = False
            # kill TankGhost's
            tank.kill()
    #if len(tankgroup) == 1 and not startGameOverMsg:
    if len(tankgroup) == 5 and not startGameOverMsg:
        # 1 Tank and 4 TankGhost
        startGameOverMsg = True
        for tank in tankgroup:
            textgroup.add(Text(tank.pos, "Victory", tank.color, 5.0, 72, vec2d(0,-50), False, True))
            textgroup.add(Text(tank.vc + vec2d(0,-50), "Winner", (0,0,0), -1, 48, vec2d(0,0), False, False))
            textgroup.add(Text(tank.vc + vec2d(0,-25), "Rank: %i " % rank, (0,0,0), -1, 48, vec2d(0,0), False, False))
            if tank.hits == 0:
                quota = 0.0
            else:
                quota = tank.shots / tank.hits
            textgroup.add(Text(tank.vc+ vec2d(0,5), "hit ratio: %.2f" % (quota))) # future division, result in float
        textgroup.add(Text((screenrect.width/2,screenrect.height/2), "Game Over", (1,1,1), 5.0,100, vec2d(0,-20), False, True))
        finale = True
            
                         
    if finale:
        if len(woundgroup) == 0:
            mainloop = False # leave game
                                
    #--- draw the sprites
    allgroup.draw(screen)
    #--- decorate screen
    #pygame.display.set_caption("player1 [F1]: %s player2 [F12]: %s mouse: %s balls: %i "
    #% (player.modus, p2.modus , pygame.mouse.get_pos(),len(ballgroup)))
    pygame.display.set_caption( "# text: %i, # balls: %i fps: %.2f" % (len(textgroup), len(ballgroup), 1000.0/tick_time))
    pygame.display.flip()          # flip the screen 30 times a second
#--- end of loop

commentez cette page

~~DISQUS~~

1)
vous pouvez piloter deux vaisseaux si vous utilisez vos deux mains
fr/ressources/jeux/schiff.txt · Last modified: 2020/05/03 21:35 by horst