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

"""
MicroWar 2.0

Sprites class for the game (player, PC, flying saucers, shoots)

Build with :
    Python 2.5.4
    Pygame 1.9.1
    py2app 0.4.2
    
See Source.rtf for licence and usage
"""

# Import Python Modules
import os,random,time,math

# Import local modules
import pygame
from configobj import *
from pygame.locals import *

# Import source
from fgame import *
from mwarbasic import *

class FSpriteWithScore(FSpriteWithSpeed):
    """ FSpriteWithSpeed with Score displayed when hit """
    def __init__(self,game):
        FSpriteWithSpeed.__init__(self,game) #call Sprite initializer
        self.showScore=True
        
    def hit(self, sprite):
        score=FSpriteWithSpeed.hit(self, sprite)
        if score>0 and self.showScore:
            p=Point(self.game,score,self.rect.centerx,self.rect.centery)
        return score
            
class SmallExplosion(FAnimatedSprite):
    def __init__(self,game,rect):
        FAnimatedSprite.__init__(self,game)
        self.set_images(game.explosionSmallImages,kExplosionAnimRate)
        self.set_sound(game.gameSounds[5])
        self.register(game.get_sprites(), kEffectLayer)
        self.do_anim(rect)

class Explosion(FAnimatedSprite):
    def __init__(self,game,rect):
        FAnimatedSprite.__init__(self,game)
        self.set_images(game.explosionImages,kExplosionAnimRate)
        self.set_sound(game.gameSounds[6])
        self.register(game.get_sprites(), kEffectLayer)
        self.do_anim(rect)

class BigExplosion(FAnimatedSprite):
    def __init__(self,game,rect):
        FAnimatedSprite.__init__(self,game)
        self.set_images(game.explosionImages,kExplosionAnimRate)
        self.set_sound(game.gameSounds[6])
        self.register(game.get_sprites(), kEffectLayer)
        self.do_anim(rect)
        
    def set_images(self,images,animRate):
        zoomImages=[]
        for img in images:
            zoom=img.copy()
            (w,h)=zoom.get_size()
            w=2*w
            h=2*h
            zoom=pygame.transform.smoothscale(zoom,(w,h))
            zoomImages.append(zoom)
        
        FAnimatedSprite.set_images(self,zoomImages,animRate)

class BonusExplosion(FAnimatedSprite):
    def __init__(self,game,rect):
        FAnimatedSprite.__init__(self,game)
        self.set_images(game.bonusAnimImages,kExplosionAnimRate)
        self.set_sound(game.gameSounds[5])
        self.register(game.get_sprites(), kEffectLayer)
        self.do_anim(rect)
                
class Player(FSpriteWithSpeed):
    """ The Player 
        handle user move directly in update()
        indirect image handling, add visual damage on the player and change according to its model """
    
    def __init__(self,game,info):
        FSpriteWithSpeed.__init__(self,game) #call Sprite initializer
        self.name="player"
        self.model=0
        self.processor=0
        self.system=0
        self.area=pygame.display.get_surface().get_rect().inflate(-10,-10)  #set max moving zone (smaller than screen)
        self.info=info
        self.cheatFire=0
        self.arriving=True
        self.disappear=False
        self.scramblingClock=0
        self.BulletClock=0
        self.zooming=False
        self.zoomValue=0.0
        self.upgradingClock=0
        self.set_power_up(0)
        self.life=kRamLife
        self.lifeMax=kRamLife
        self.damage=kPlayerDamage

        self.set_image()      # call after setting life and model
        self.set_pos(((self.area.width-self.rect.width)/2,self.area.height))  #position the initial player position (low centered)

        self.set_explosion_mode(Explosion)
        self.set_hit_mode(SmallExplosion)
        self.register(game.get_sprites(),kSpriteLayer)
        
    def set_image(self):
        """ change player image on the fly, maintaining position 
            made a copy of the original, so we can display on top damage feedbacks """
        (oldx,oldy)=self.get_pos() # store position to reset it, because when we load another image the position is reset
        oldx+=self.rect.width/2
        
        #set the basic image acording to mac model (make a copy of the original)
        FSpriteWithSpeed.set_image(self,get_from_list(self.game.macImages,self.model).copy())
        
        #get the image corresponding to actual mac damage and draw it into the mac's screen
        l=int(len(self.game.macLifeImages)*self.life/self.lifeMax)-1
        img=get_from_list(self.game.macLifeImages,l)
        x=get_from_list(self.game.macScreenX,self.model)
        w=get_from_list(self.game.macScreenW,self.model)
        y=get_from_list(self.game.macScreenY,self.model)
        h=get_from_list(self.game.macScreenH,self.model)
        area=pygame.Rect(x,y,w,h)
        self.image.set_clip(area)
        self.image.blit(img,(x+(w-img.get_width())/2,y+(h-img.get_height())/2))
        self.image.set_clip(None)
        #handlethe zooming effect (when got bonus)
        if self.zooming:
            (w,h)=self.image.get_size()
            w=int(w+self.zoomValue)
            h=int(h+self.zoomValue)
            self.image=pygame.transform.smoothscale(self.image,(w,h))
        #assign the final rect and reset position
        self.rect=self.image.get_rect()
        self.set_pos((oldx-self.rect.width/2,oldy))

    def update(self):
        if self.disappear: #handle disappering mode for a new player's mac model, chained with arriving mode
            self.fmove((0.0,+kPlayerYArrivingSpeed),self.game.get_clock())
            (x,y)=self.get_pos()
            if y>self.area.height+kPlayerYOffset:
                self.set_pos((x,self.area.height+kPlayerYOffset))
                self.disappear=False
                self.arriving=True
                self.model=self.nextModel
                self.set_image()
                self.speed=(kPlayerSpeedBase+kPlayerSpeedLevel*self.model,0.0)
        elif self.arriving:   #handle arriving mode for a new player's mac model
            self.fmove((0.0,-kPlayerYArrivingSpeed),self.game.get_clock())
            (x,y)=self.get_pos()
            if y<self.area.height-self.rect.height-kPlayerYOffset:
                self.set_pos((x,self.area.height-self.rect.height-kPlayerYOffset))
                self.arriving=False
        elif self.game.pref.interface==0:
            #Direct key pressed for arrow (player smooth movement : avoid system delay/repeat key)
            keystate = pygame.key.get_pressed()
            if keystate[K_RIGHT]: 
                self.move(+1.0,self.game.get_clock())
            if keystate[K_LEFT]: 
                self.move(-1.0,self.game.get_clock())
            if keystate[K_SPACE]:
                self.shoot_request(self.game.screen.shotGroup)
        elif self.game.pref.interface==1: 
            (x,y)=pygame.mouse.get_pos()
            if x>self.rect.centerx:
                self.move(+1.0,self.game.get_clock())
            if x<self.rect.centerx:
                self.move(-1.0,self.game.get_clock())
            (b1,b2,b3)=pygame.mouse.get_pressed()
            if b1 or b2 or b3:
                self.shoot_request(self.game.screen.shotGroup)
        if self.scramblingClock!=0:      # handle scrambling player after beeing hit, force rect position
            (x,y)=self.get_pos()
            self.rect.left=int(x+8.0*random.random()-4.0)
            self.rect.top=int(y+8.0*random.random()-4.0)
            self.scramblingClock+=self.game.get_clock()
            if self.scramblingClock>kPlayerScrambling:
                self.scramblingClock=0
        if self.upgradingClock!=0:
            self.zooming=True
            self.upgradingClock+=self.game.get_clock()
            self.zoomValue+=20.0*self.game.get_clock()/kPlayerZooming
            if self.upgradingClock>kPlayerZooming:
                self.upgradingClock=0
                self.zooming=False
                self.zoomValue=0.0
            self.set_image()
        if self.BulletClock!=0:
            self.BulletClock-=self.game.get_clock()
            
        pygame.sprite.Sprite.update(self)
        
    def got_damage(self,value):
        FSprite.got_damage(self,value)
        if value>0:    # active scramble only when damage
            self.scramblingClock=self.game.get_clock()
            sound=self.game.gameSounds[7]    # play the touch sound
            sound.set_volume(self.game.pref.audioVolume)
            sound.play()
            
        self.set_image()
        
    def set_processor(self,value):
        """ set the processor+model and spread to text info object """
        if value<=len(self.game.macProcessors):
            self.processor=value
        else:
            self.processor=0
        self.info.set_processor(self.processor)
        self.set_model(value)

    def set_model(self,value):
        """ define the player mac model : 
                launch the changing animation
                play the boot sound """
        if value<=len(self.game.macModels):
            self.model=value
        else:
            self.model=len(self.game.macModels)-1
            
        # next model would be active when Mac disappear from screen (go down)
        self.disappear=True
        self.arriving=False
        self.nextModel=value
        if self.model==0:    # special case for the first model, don't disappear, just appear
            (x,y)=self.get_pos()
            self.set_pos((x,self.area.height+kPlayerYOffset))

        if self.model>9:    # imac intel and above
            self.shootModel=kPlayerShootIntel
        elif self.model>5:  # imac and above
            self.shootModel=kPlayerShootIMac
        else:               # 68k models
            self.shootModel=kPlayerShootClassic            
        self.info.set_model(self.model) #spread new model to the info box
        
        sound=get_from_list(self.game.macSounds,value)    # play the new model boot sound
        sound.set_volume(self.game.pref.audioVolume)
        sound.play()
            
    def set_system(self,value):
        """ set the system and spread to text info object """
        if value<=len(self.game.macSystems):
            self.system=value
        else:
            self.system=0
        self.info.set_system(self.system)
        
    def set_power_up(self,value):
        self.powerUp=value
        if value<=0: 
            self.powerMode=0
        self.info.set_power_up(self.powerMode,self.powerUp)
        
    def add_power_up(self,mode,value):
        self.powerMode=mode
        self.powerUp+=value
        self.info.set_power_up(self.powerMode,self.powerUp)
        
    def use_power_up(self):
        if self.powerUp>0:
            self.powerUp-=1
        if self.powerUp<=0: 
            self.powerMode=0
        self.info.set_power_up(self.powerMode,self.powerUp)
            
    def move(self,direction,clk):
        """ move the player and clamp it to the screen """
        if self.arriving==False and self.disappear==False:  #only when not upgrading
            self.fmove((direction*self.speed[0],0.0),clk)
            r=self.rect.clamp(self.area)
            if (r!=self.rect):  #need a avoid a rounded bug at high frame rate speed
                self.set_pos((r.left,r.top)) # accord float position if clamped

    def shoot_request(self,group):
        """ player want to shoot, do it if possible
            condition : 1 bullet at a time like Space Invaders (except when player is cheating or some special bonus)
                not arriving and not disappearing"""
        if self.powerMode==kBonusNewton:
            maxBullets=5
        else:
            maxBullets=0
            
        if (len(group)<=maxBullets or self.cheatFire==1) and self.arriving==False and self.disappear==False and self.BulletClock<=0:
            if self.powerMode==0:   # normal shot
                shot=PlayerShot(self.game,self)
            elif self.powerMode==kBonusClarus:  # clarus bonus shot : large explosion
                shot=PlayerShotClarus(self.game,self)
            elif self.powerMode==kBonusNewton:  # newton bonus shot : trail shoot
                shot=PlayerShotNewton(self.game,self)
            elif self.powerMode==kBonusiPod:    # iPod bonus shot : 3 shot at a time
                shot=PlayerShotIPod(self.game,self,2)
                shot.add(group)
                shot=PlayerShotIPod(self.game,self,1)
                shot.add(group)
                shot=PlayerShotIPod(self.game,self,0)
            elif self.powerMode==kBonusiPhone:    # ihone bonus shot : 5 shot at a time
                shot=PlayerShotIPhone(self.game,self,4)
                shot.add(group)
                shot=PlayerShotIPhone(self.game,self,3)
                shot.add(group)
                shot=PlayerShotIPhone(self.game,self,2)
                shot.add(group)
                shot=PlayerShotIPhone(self.game,self,1)
                shot.add(group)
                shot=PlayerShotIPhone(self.game,self,0)
            else:
                print "error : powerMode out of range"
                shot=PlayerShot(self.game,self)
            
            if self.powerMode!=0:    
                self.use_power_up()
            shot.add(group)
            self.BulletClock=kPlayerShootMinDelay
        
class PlayerShot(FSpriteWithSpeed):
    """ Player Shoot class : move only up """
    
    def __init__(self,game,player,imgIndex=None):
        FSpriteWithSpeed.__init__(self,game) #call Sprite initializer
        self.name="player-shot"
        self.powerUp=0
        self.damage=kPlayerDamage
        if imgIndex==None:
            imgIndex=player.shootModel
        img=get_from_list(self.game.shotImages,imgIndex)
        self.set_image(img)
        self.speed=(0.0,-kPlayerShootSpeedBase-kPlayerShootSpeedModel*player.shootModel-kPlayerShootSpeedSystem*player.system)
        self.set_pos((player.rect.left+(player.rect.width-self.rect.width)/2,player.rect.top-self.rect.height))
            
        sound=get_from_list(self.game.shotSounds,player.shootModel)
        sound.set_volume(self.game.pref.audioVolume)
        sound.play()
        
        self.register(game.get_sprites(),kShotLayer)

    def update(self):
        self.fmove(self.speed,self.game.get_clock()) 
        if self.rect.top<-self.rect.height: # kill when off screen
            if self.powerUp==0:
                self.game.success=0 # reset accurate score to ZERO when a (normal) shot goes off-screen
            self.kill()
            
class PlayerShotClarus(PlayerShot):
    def __init__(self,game,player):
        PlayerShot.__init__(self,game,player,kPlayerShootClarus)
        self.powerUp=player.powerMode
        self.damage=kPlayerDamagePower
            
class PlayerShotNewton(PlayerShot):
    def __init__(self,game,player):
        PlayerShot.__init__(self,game,player,kPlayerShootNewton)
        self.powerUp=player.powerMode
        self.damage=kPlayerDamagePower
            
class PlayerShotIPod(PlayerShot):
    def __init__(self,game,player,count):
        PlayerShot.__init__(self,game,player,kPlayerShootIPod)
        if count==1:
            self.set_pos((player.rect.left+(player.rect.width-self.rect.width)/2+player.rect.width/2,player.rect.top-self.rect.height))
            self.speed=(-0.1*self.speed[1],self.speed[1]*0.98)        
        elif count==2:
            self.set_pos((player.rect.left+(player.rect.width-self.rect.width)/2-player.rect.width/2,player.rect.top-self.rect.height))
            self.speed=(+0.1*self.speed[1],self.speed[1]*0.98)        
        self.powerUp=player.powerMode
        self.damage=kPlayerDamagePower
            
class PlayerShotIPhone(PlayerShot):
    def __init__(self,game,player,count):
        PlayerShot.__init__(self,game,player,kPlayerShootIPhone)
        if count==1:
            self.set_pos((player.rect.left+(player.rect.width-self.rect.width)/2+player.rect.width/2,player.rect.top-self.rect.height))
            self.speed=(-0.1*self.speed[1],self.speed[1]*0.96)        
        elif count==2:
            self.set_pos((player.rect.left+(player.rect.width-self.rect.width)/2+player.rect.width/3,player.rect.top-self.rect.height))
            self.speed=(-0.05*self.speed[1],self.speed[1]*0.98)        
        elif count==3:
            self.set_pos((player.rect.left+(player.rect.width-self.rect.width)/2-player.rect.width/2,player.rect.top-self.rect.height))
            self.speed=(+0.1*self.speed[1],self.speed[1]*0.96)        
        elif count==4:
            self.set_pos((player.rect.left+(player.rect.width-self.rect.width)/2-player.rect.width/3,player.rect.top-self.rect.height))
            self.speed=(+0.05*self.speed[1],self.speed[1]*0.98)        
        self.powerUp=player.powerMode
        self.damage=kPlayerDamagePower
 
class SaucerGroup(pygame.sprite.RenderUpdates):
    """ The saucer group : saucer drop bonus
        saucer group has only one saucer at a time """
    
    def __init__(self,game):
        pygame.sprite.RenderUpdates.__init__(self)
        self.game=game
        self.saucerRate=FRandom(kSaucerRate,kSaucerMinDelay)
        
    def update(self):
        #no Saucer ? Create one ?
        if len(self)==0 and not self.game.screen.pause:
            if self.saucerRate.get_chance(self.game.get_clock()):
                self.empty()
                saucer=Saucer(self.game)
                saucer.add(self)

class Saucer(FSpriteWithScore):
    """ Saucer class : move on top of the screen dropping bonus """
    
    def __init__(self,game):
        FSpriteWithScore.__init__(self,game) #call Sprite intializer
        
        self.name="saucer"
        if random.random()<0.5: # random direction : select image accordingly
            img=self.game.saucerImages[0] 
            speed=kSaucerSpeedBase
        else:
            img=self.game.saucerImages[1]
            speed=-kSaucerSpeedBase
        self.set_image(img)
        self.life=kSaucerLife
        self.lifeMax=kSaucerLife
        self.set_score(kSaucerScoreHit,kSaucerScoreKill,self.game.screen.score)
        self.area=pygame.display.get_surface().get_rect()
        self.bombRate=FRandom(kSaucerShootRate,kSaucerShootMinDelay)
        self.bombModel=kBonusApple1
        if speed>0:
            self.set_pos((self.area.left-self.rect.width,20))
        else:
            self.set_pos((self.area.right+self.rect.width,20))
        self.speed=(speed,0.0)
        self.set_explosion_mode(BigExplosion)
        self.set_hit_mode(SmallExplosion)

        sound=self.game.gameSounds[2] 
        sound.set_volume(self.game.pref.audioVolume)
        sound.play()
        
        self.register(game.get_sprites(),kSpriteLayer)

    def update(self):
        self.fmove(self.speed,self.game.get_clock()) 
        if self.area.contains(self.rect):
            if self.bombRate.get_chance(self.game.get_clock()): # launch a bonus ?              
                level=self.game.get_level() # determine bonus chance table according to level 
                rApple1=get_from_list(self.game.apple1,level)            
                rApple2=rApple1+get_from_list(self.game.apple2,level)            
                rMotorola=rApple2+get_from_list(self.game.motorola,level)           
                rIbm=rMotorola+get_from_list(self.game.ibm,level)           
                rIntel=rIbm+get_from_list(self.game.intel,level)
                rClarus=rIntel+get_from_list(self.game.clarus,level)
                rNewton=rClarus+get_from_list(self.game.newton,level)
                riPod=rIntel+get_from_list(self.game.ipod,level)
                riPhone=riPod+get_from_list(self.game.iphone,level)
                                 
                r=random.random()  # determine bonus model, according to saucer type
                if r<rApple1:    
                    self.bombModel=kBonusApple1    # apple = system updates (from system 1 to system 7.5)
                elif r<rApple2:
                    self.bombModel=kBonusApple2    # apple = macos updates (from macos 7.6 to ...)
                elif r<rMotorola:
                    self.bombModel=kBonus68K    # motorola = 68k processor updates
                elif r<rIbm:
                    self.bombModel=kBonusPPC    # ibm = PowerPC processor updates
                elif r<rIntel:
                    self.bombModel=kBonusIntel    # intel processor updates
                elif r<rClarus:
                    self.bombModel=kBonusClarus    # clarus : special power bonus, small clarus big explosion
                elif r<rNewton:
                    self.bombModel=kBonusNewton    # newton : special power bonus, 
                elif r<riPod:
                    self.bombModel=kBonusiPod    # ipod : special power bonus,
                elif r<riPhone:
                    self.bombModel=kBonusiPhone    # iphone : special power bonus,
                else:
                    self.bombModel=kBonusRam    # RAM bonus
                                    
                bonus=Bonus(self.game,self)
                bonus.add(self.game.screen.bonusGroup) 

        # kill when the saucer is out of screen                                       
        if self.speed[0]>0 and self.rect.left>self.area.right:
            self.kill()
        if self.speed[0]<0 and self.rect.right<self.area.left:
            self.kill()
                    
    def hit(self,sprite):           
        if sprite.name=="player-shot":    # PC hit by a player shot
            bonus=kShipScoreBonus*self.game.success   # one bonus for successive hit
            self.set_score(kSaucerScoreHit+bonus,kSaucerScoreKill+bonus,self.game.screen.score)
            self.game.success=self.game.success+1

        FSpriteWithScore.hit(self,sprite)
        
        if self.life<=0.0:  # if dead, launch virus
            for i in range(kSaucerVirus):
                virus=Virus(self.game,self)
                virus.add(self.game.screen.virusGroup)
        
class Bonus(FSpriteWithScore):
    """ Bonus : saucer bombs """
    
    def __init__(self,game,saucer):
        FSpriteWithScore.__init__(self,game) #call Sprite initializer
        self.name="bonus"
        if saucer.bombModel>=len(self.game.bonusImages): 
            print "error saucer bonus model out of range %d" % saucer.bombmodel
        self.index=saucer.bombModel
        self.canLevelUp=False
        self.set_image(self.game.bonusImages[self.index])
        self.speed=(0.0,kSaucerShootSpeed)
        self.life=kBonusLife
        self.lifeMax=kBonusLife
        self.set_pos((saucer.rect.centerx,saucer.rect.centery+self.rect.height))
        self.set_score(kBonusScore,kBonusScore,self.game.screen.score)
        level=self.game.get_level() 
        
        #get value depending on bonus index
        if self.index==kBonusApple1 or self.index==kBonusApple2:    # Apple Bonus : System
            self.value=get_from_list(self.game.macSystemLevel,level) 
        elif self.index>=kBonus68K and self.index<=kBonusIntel:    # Processor Bonus 
            self.value=get_from_list(self.game.macProcessorLevel,level)
        elif self.index==kBonusClarus: # Clarus
            self.value=0.0
        elif self.index==kBonusNewton: # Newton
            self.value=0.0
        elif self.index==kBonusiPod: # iPod
            self.value=0.0
        elif self.index==kBonusiPhone: # iPhone
            self.value=0.0
        else:    # Ram Bonus
            self.value=0.5
        self.bottomLimit=pygame.display.get_surface().get_rect().bottom+self.rect.height
        self.set_explosion_mode(BonusExplosion)

        self.register(game.get_sprites(),kBonusLayer)
        
    def set_image(self, image):
        (x, y)=self.get_pos()
        r=image.get_rect()
        if self.canLevelUp: # if this bonus can level-up player, display a halo behind
            self.image=self.game.bonusImages[kBonusHalo].copy()
            self.rect=self.image.get_rect()
            ir=image.get_rect()
            self.image.blit(image, ((self.rect.width-ir.width)/2, (self.rect.height-ir.height)/2))
        else:
            self.image=image
            self.rect=self.image.get_rect()
        x=x+0.5*(self.rect.width-r.width)
        y=y+0.5*(self.rect.height-r.height)
        self.set_pos((x, y))
        
    def catch(self,player):
        """ Catch Bonus, handle effets on player according to type """
        levelup=False
        if self.index==kBonusApple1 or self.index==kBonusApple2:      # Apple bonus
            if self.value>player.system:  # catch a new system : update
                player.set_system(self.value)
                levelup=True
        elif self.index>=kBonus68K and self.index<=kBonusIntel:     # Motorola, Ibm or Intel bonus
            if self.value>player.processor:  # catch a new processor : update
                player.set_processor(self.value)
                levelup=True
        elif self.index==kBonusClarus: # Clarus
            player.add_power_up(self.index,kPowerUp)
            levelup=True
        elif self.index==kBonusNewton: # Newton
            player.add_power_up(self.index,5*kPowerUp)
            levelup=True
        elif self.index==kBonusiPod: # iPod
            player.add_power_up(self.index,2*kPowerUp)
            levelup=True
        elif self.index==kBonusiPhone: # iPhone
            player.add_power_up(self.index,kPowerUp)
            levelup=True
        else:      # RAM bonus
            if not player.life_max():  # catch RAM : upgrade if not max
                levelup=True
            player.got_damage(-self.value)
                
        if levelup:
            self.set_score(kBonusScoreNew,kBonusScoreNew,self.game.screen.score)
            sound=self.game.gameSounds[3] 
            player.upgradingClock+=self.game.get_clock()
        else:
            self.set_score(kBonusScore,kBonusScore,self.game.screen.score)
            sound=self.game.gameSounds[4] 
        sound.set_volume(self.game.pref.audioVolume)
        sound.play()
        self.hit(player)

    def update(self):
        canLevelUp=False
        if self.index==kBonusApple1 or self.index==kBonusApple2:      # Apple bonus
            if self.value>self.game.screen.player.system:  # catch a new system : update
                canLevelUp=True
        elif self.index>=kBonus68K and self.index<=kBonusIntel:     # Motorola, Ibm or Intel bonus
            if self.value>self.game.screen.player.processor:  # catch a new processor : update
                canLevelUp=True
        elif self.index==kBonusClarus: # Clarus
            canLevelUp=True
        elif self.index==kBonusNewton: # Newton
            canLevelUp=True
        elif self.index==kBonusiPod: # iPod
            canLevelUp=True
        elif self.index==kBonusiPhone: # iPhone
            canLevelUp=True
        else:      # RAM bonus
            if not self.game.screen.player.life_max():  # catch RAM : upgrade if not max
                canLevelUp=True
        if self.canLevelUp!=canLevelUp:
            self.canLevelUp=canLevelUp
            self.set_image(self.game.bonusImages[self.index])
            
        self.fmove(self.speed,self.game.get_clock()) 
        if self.rect.bottom>self.bottomLimit:  # kill when off screen
            self.kill()
       
class Virus(FSpriteWithSpeed):
    """ Virus : appear when Saucer explode, can infect PC """
    
    def __init__(self,game,saucer):
        FSpriteWithSpeed.__init__(self,game) #call Sprite initializer
        self.name="virus"
        self.set_image(self.game.virusImages[0])
        xPos=random.random()*saucer.rect.width
        self.speed=((xPos-0.4*saucer.rect.width)/saucer.rect.width,-0.2*(random.random()+0.05))
        self.bottomLimit=pygame.display.get_surface().get_rect().bottom+self.rect.height
        self.set_pos((saucer.rect.left+xPos,saucer.rect.top-self.rect.height))
        
        self.register(game.get_sprites(),kShotLayer)

    def update(self):
        (xs,ys)=self.speed
        xs*=(1.0-0.0015*self.game.get_clock())
        ys+=0.001*self.game.get_clock()
        if ys>1.0:
            ys=1.0
        self.speed=(xs,ys)
        self.fmove(self.speed,self.game.get_clock()) 
        if self.rect.bottom>self.bottomLimit:  # kill when off screen
            self.kill()

class ShipGroup(pygame.sprite.RenderUpdates):
    """ ShipGroup : Handle a wave of ennemies
        A wave has 2 mode :
            intro (caming on screen)
            not intro (goes right from left going down to the bottom) 
        + from Win95PC, each PC can leave the squad and fly by himself,like galaxian ships
    """
    
    def __init__(self,game):
        pygame.sprite.RenderUpdates.__init__(self)
        self.game=game
        self.area=pygame.display.get_surface().get_rect()
        self.area.top+=64
        self.level=0
        self.xSpeed=0.0
        self.ySpeed=0.0
        self.down=0
        self.pdown=0
        self.direction=0
        self.size=0
        self.lineSize=0
        self.intro=0

    def update(self):
        """ update the PC wave :
                adjust wave speed
                determine which PC can shoot
                move it and handle screen bounce """      
        nbAlien=len(self)
        if nbAlien==0:
            #no Ships, no explosion, no introlevel beginning ? ask for a new level
            if not self.game.screen.introLevel:
                self.game.screen.nextLevel=True
            coef=0.0
            self.globalrect=pygame.Rect(0,0,0,0)
            nbAlienDetach=0
        else:
            # compute speed coefficient, according to wave size
            coef=kShipGroupSpeedBase+kShipGroupSpeedSize*(self.size-nbAlien)/self.size
            if nbAlien<=3:
                coef=coef*(1.0+(kShipGroupSpeedAccelerator-nbAlien)/kShipGroupSpeedAccelerator)

            # build the shoot and detach list : those who can fire bombs (bottom of there column)
            # build the global squad rect
            shootList=[]
            detachList=[]
            colSize=self.size/self.lineSize
            firstLine=[nbAlien+1]*colSize
            lastLine=[-1]*colSize
            lastCol=[-1]*self.lineSize
            first=True
            nbAlienDetach=0
            nbInfected=0
            for item in self:
                if item.inSquad:
                    if first:
                        self.globalrect=item.rect
                        first=False
                    else:
                        self.globalrect=self.globalrect.union(item.rect)
                    if item.index>lastCol[item.column]:
                        lastCol[item.column]=item.index
                    if item.index<firstLine[item.line]:
                        firstLine[item.line]=item.index
                    if item.index>lastLine[item.line]:
                        lastLine[item.line]=item.index
                else:
                    nbAlienDetach+=1
                    shootList.append(item.index)
                if item.infected:
                    nbInfected+=1
                item.canShoot=False

            if nbInfected>0:    # adjust the speed coef with infected PC
                coef=coef*(1.0-0.5*nbInfected/nbAlien)

            # determine if bouncing on screen
            if self.globalrect.left<self.area.left:
                changed=self.direction<0
                self.direction=1
            elif self.globalrect.right>self.area.right:
                changed=self.direction>0
                self.direction=-1
            else:
                changed=False

            if changed:
                down=self.down
            else:
                down=0
                
            # handle the shootlist and detachList
            for i in range(self.lineSize):
                if lastCol[i]>=0:
                    shootList.append(lastCol[i])
            for i in range(colSize):
                if firstLine[i]>=0:
                    detachList.append(firstLine[i])
                elif lastLine[i]>=0:
                    detachList.append(lastLine[i])
                        
            # apply to PC
            if self.intro:
                speed=(0.0,self.ySpeed*coef)
                down=0
                if self.globalrect.top>self.area.top:    # stop intro test
                    self.intro=False
                    if random.random()<0.5:     # and choose a random direction
                        self.direction=1
                    else:
                        self.direction=-1
            else:
                speed=(self.xSpeed*self.direction*coef,0.0)
            for item in self:
                if item.index in shootList: # PC can shoot if in shootlist and not infected
                    item.canShoot=not item.infected
                if item.inSquad:
                    item.speed=speed
                    item.down=down
                if not self.intro:
                    item.canDetach=False
                    if item.inSquad and item.index in detachList:
                        if item.imageLogoIndex>=kShipWin95 and not item.infected and nbAlien-nbAlienDetach>2:
                            nbMax=int(0.67*(item.imageLogoIndex-kShipWin95)+1.0)
                            if nbAlienDetach<nbMax:
                                if self.direction>0:
                                    if self.globalrect.left>kShipDetachSpace and item.rect.bottom<self.game.height-kMinShipDetachHeight:
                                        item.canDetach=True
                                        nbAlienDetach+=1
                                else:
                                    if self.area.right-self.globalrect.right>kShipDetachSpace and item.rect.bottom<self.game.height-kMinShipDetachHeight:
                                        item.canDetach=True
                                        nbAlienDetach+=1
                            
            if self.globalrect.top>self.area.bottom: # kill if out of screen
                self.empty()

    def new_wave(self,levelObj,sound):
        """ create a new wave of PC according to level (year)
                adjust speed according to level
        """
        self.empty()
        self.level=levelObj
        level=levelObj.get_level()
        self.xSpeed=kShipSpeedBaseX+kShipSpeedLevel*level
        self.ySpeed=kShipSpeedBaseY+kShipSpeedLevel*level
        self.down=kShipDown
        self.direction=0
        self.intro=True
        sound.set_volume(self.game.pref.audioVolume)
        sound.play()

        # prepare randomizer for building wave according to level (year)
        
        # each PC will have a familly (Commodore 64, Atari, Amiga, Other or Microsoft PC)
        rPC=get_from_list(self.game.pcFamilly,level)
        rC64=rPC+get_from_list(self.game.c64Familly,level)
        rAtari=rC64+get_from_list(self.game.atariFamilly,level)
        rAmiga=rAtari+get_from_list(self.game.amigaFamilly,level)
        
        # for PC from PC familly will have an OS depending on level
        rMsdos=get_from_list(self.game.msdos,level)
        rWin10=rMsdos+get_from_list(self.game.win10,level)
        rWin20=rWin10+get_from_list(self.game.win20,level)
        rWin30=rWin20+get_from_list(self.game.win30,level)
        rWin95=rWin30+get_from_list(self.game.win95,level)
        rWin98=rWin95+get_from_list(self.game.win98,level)
        rWinXP=rWin98+get_from_list(self.game.winxp,level)
        rWinVS=rWinXP+get_from_list(self.game.winvs,level)
        rWin70=rWinVS+get_from_list(self.game.win70,level)

        # determine the wave shape (size and linesize, must be multiplier) and compute grid
        self.size=get_from_list(self.game.pcForces,level)
        self.lineSize=get_from_list(self.game.pcForcesL,level)
        xGrid=66+(7-self.lineSize)*2
        yGrid=67

        # create the wave, one pc at time randomizing types
        # bomb model depends on PC model/submodel
        for i in range(self.size):
            x=i%self.lineSize
            y=i//self.lineSize
            bModel=0
            r=random.random()
            if r<rPC:   # Microsoft type
                r=random.random()
                if r<rMsdos:
                    images=self.game.msdosImages
                    logo=kShipMSDOS
                    bModel=kShipShootDisk5
                elif r<rWin10:
                    images=self.game.win10Images
                    logo=kShipWin10
                    bModel=kShipShootDisk5
                elif r<rWin20:
                    images=self.game.win20Images
                    logo=kShipWin20
                    bModel=kShipShootDisk3
                elif r<rWin30:
                    images=self.game.win30Images
                    logo=kShipWin30
                    bModel=kShipShootDisk3
                elif r<rWin95:
                    images=self.game.win95Images
                    logo=kShipWin95
                    bModel=kShipShootDisk3
                elif r<rWin98:
                    images=self.game.win98Images
                    logo=kShipWin98
                    bModel=kShipShootCD
                elif r<rWinXP:
                    images=self.game.winxpImages
                    logo=kShipWinXP
                    bModel=kShipShootCD
                elif r<rWinVS:
                    images=self.game.winvsImages
                    logo=kShipWinVista
                    bModel=kShipShootCD
                elif r<rWin70:
                    images=self.game.win70Images
                    logo=kShipWin70
                    bModel=kShipShootCD
                else:
                    images=self.game.os2Images
                    logo=kShipOS2
                    bModel=kShipShootDisk3
            elif r<rC64:    # Commodore 64 type
                images=self.game.c64Images
                logo=kShipC64
                bModel=kShipShootK7
            elif r<rAtari:  # Atari type
                images=self.game.cAtariImages
                logo=kShipAtari
                bModel=kShipShootDisk3
            elif r<rAmiga:  # Amiga type
                images=self.game.cAmigaImages
                logo=kShipAmiga
                bModel=kShipShootDisk5
            else:           # other PC type (Amstrad)
                images=self.game.cAmstradImages
                logo=kShipAmstrad
                bModel=kShipShootK7

            # Create the ship and set it at the right position in the wave
            xOffset=(self.area.width-self.lineSize*xGrid)/2
            yOffset=-yGrid*(self.size//self.lineSize+1)
            ind=int(len(images)*random.random())
            ship=Ship(self.game,images[ind],logo,bModel)
            ship.set_pos_in_group(i,x,y,self)
            ship.set_pos((x*xGrid+xOffset+(xGrid-ship.rect.width)/2,y*yGrid+yOffset+xGrid-ship.rect.height))
            ship.add(self)
        
class Ship(FSpriteWithScore):
    """ Ship : ennemy computer coming down in waves
        The ship move in its wave : see ShipGroup """
    
    def __init__(self,game,imgIndex,logo,bModel):
        FSpriteWithScore.__init__(self,game) #call Sprite intializer
        self.name="ship"
        self.imageIndex=imgIndex
        self.imageLogoIndex=logo
        self.life=kShipLife
        self.lifeMax=kShipLife
        self.damage=kShipDamage
        self.set_score(kShipScoreHit,kShipScoreKill,self.game.screen.score)
        self.down=0
        self.bombModel=bModel
        self.bombRate=FRandom(kShipShootRateBase+self.bombModel*kShipShootRateModel,kShipShootMinDelay)
        self.detachRate=FRandom(kShipDetachRateBase+kShipDetachRateModel*self.imageLogoIndex,kShipDetachMinDelay)
        self.canShoot=False
        self.infected=False
        self.canDetach=False
        self.inSquad=True
        self.detachLevel=self.game.get_level()
        self.set_explosion_mode(Explosion)
        self.set_hit_mode(SmallExplosion)
        self.set_image()
        
        self.register(game.get_sprites(),kSpriteLayer)
        
    def set_pos_in_group(self,index,column,line,group):
        self.group=group
        self.index=index
        self.column=column
        self.line=line
        
    def set_image(self,angle=0.0):
        """ change player image on the fly, maintaining position 
            made a copy of the original, so we can display a logo into the defined area """
        (oldx,oldy)=self.get_pos() # store position to reset it, because when we load another image the position is reset
        oldx+=self.rect.width/2 
        FSpriteWithScore.set_image(self,get_from_list(self.game.pcImages,self.imageIndex).copy())
        x=get_from_list(self.game.pcScreenX,self.imageIndex)
        y=get_from_list(self.game.pcScreenY,self.imageIndex)
        w=get_from_list(self.game.pcScreenW,self.imageIndex)
        h=get_from_list(self.game.pcScreenH,self.imageIndex)
        area=pygame.Rect(x,y,w,h)
        self.image.set_clip(area)
        if self.infected:
            img=self.game.pcOSImages[kShipVirus]
        else:
            img=self.game.pcOSImages[self.imageLogoIndex]
        self.image.blit(img,(x+(w-img.get_width())/2,y+(h-img.get_height())/2))
        self.image.set_clip(None)
        self.rect=self.image.get_rect()
        self.bottomLimit=self.game.height+self.rect.height
        self.set_pos((oldx-self.rect.width/2,oldy))
        if angle!=0.0:  # rotate PC image, if needed (detached PC)
            self.image=pygame.transform.rotate(self.image,angle)

    def update(self):
        clk=self.game.get_clock()
        if self.down==0:
            self.fmove(self.speed,clk)
        else:
            self.fpmove(self.speed,clk,(0,self.down))
        if not self.group.intro:
            (x,y)=self.get_pos()
            if self.canDetach:  # detach if possible
                if self.detachRate.get_chance(clk):
                    self.inSquad=False
                    self.canDetach=False
                    self.detachLevel=self.game.get_level()
                    self.widthTarget=kShipTargetWidth
                    self.choose_target()
            if self.canShoot:   # if PC can shoot, determine if he do it and why
                if self.bombModel>kShipShootDisk5: # from disk5 shoot-rate can detect "player"
                    rate=kShipShootRateBase+self.bombModel*kShipShootRateModel
                    (xp,yp)=self.game.screen.player.get_pos()
                    d=kShipShootWidthBonus*abs(xp-x)
                    if d<self.bombModel or not self.inSquad:
                        rate-=(self.bombModel-d)*kShipShootRateBonus
                    self.bombRate.set_rate(rate,kShipShootMinDelay)
                if self.bombRate.get_chance(clk): 
                    bomb=Bomb(self.game,self)
                    bomb.add(self.game.screen.bombGroup)
            if not self.inSquad:    # handle detached PC
                # compute actual speed vector and target needed vector
                if self.detachLevel>kShipDetachLevel:
                    (xp,yp)=self.game.screen.player.get_pos()
                    dx=sign(xp-self.targetx+kShipDetachWidth*random.random()-0.5*kShipDetachWidth)
                    self.targetx+=dx*kShipDetachAngle*(self.detachLevel-kShipDetachLevelOffset)*clk
                else:
                    self.targetx+=(kShipDetachMiniWidth*random.random()-0.5*kShipDetachMiniWidth)*clk
                v=math.hypot(self.speed[0],self.speed[1])
                at=math.atan2(self.targety-y,self.targetx-x)
                a=math.atan2(self.speed[1],self.speed[0])
                da=at-a
                if da>math.pi:
                    da=2.0*math.pi-da
                if da<-math.pi:
                    da=2.0*math.pi+da
                if self.infected:   # slow infected-detached PC
                    acoef=kShipDetachInfectedCoef
                else:
                    acoef=kShipDetachNormalCoef
                a+=sign(da)*acoef*math.pi*clk
                
                # apply speed computed
                self.speed=(v*math.cos(a),v*math.sin(a))
                
                if x<-kShipDetachXKill or x>self.game.width+kShipDetachXKill:
                    self.kill()
                
        if self.rect.bottom>self.game.height+kOutScreenLimit:  # kill when off screen
            if self.inSquad or self.infected:
                self.kill()
            else:
                (x,y)=self.get_pos()
                self.set_pos((x,-kOutScreenLimit))
                self.choose_target()
                
    def choose_target(self):
        self.detachLevel+=3
        (x,y)=self.get_pos()
        (xp,yp)=self.game.screen.player.get_pos()
        self.targetx=xp+(2.0*self.widthTarget*random.random())-self.widthTarget
        self.targety=self.game.height+kOutScreenLimit
        dx=self.targetx-x
        dy=self.targety-y
        if abs(dx)>abs(dy):  # make sure target is not too far away
            self.target=sign(dx)*(dy+x)
        speed=sign(self.speed[0])*(kShipSpeedBaseX+kShipSpeedLevel*self.detachLevel)*0.6
        self.speed=(speed,-abs(speed))
        at=-math.atan2(self.game.height-y,self.targetx-x)+0.5*math.pi
        self.set_image(180.0*at/math.pi)
        if self.widthTarget>0:
            self.widthTarget-=kShipTargetWidthStep
        self.bombRate=FRandom(kShipShootRateBase*(self.bombModel+0.25*self.detachLevel)+kShipShootRateModel,kShipShootMinDelay)
    
    def hit(self,sprite):
        if sprite.name=="player-shot":    # PC hit by a player shot
            bonus=kShipScoreBonus*self.game.success   # one bonus for successive hit
            if not self.inSquad:            # another bonus if the PC was flying
                bonus=bonus+kShipDetachScoreKill
            self.set_score(kShipScoreHit+bonus,kShipScoreKill+bonus,self.game.screen.score)
            self.game.success=self.game.success+1
            if sprite.powerUp==kBonusClarus:  # if hit by a Clarus amno : exploding distance magnify
                sprite.powerUp=0    # deactive powerup to avoid a complete chained explosion
                for alien in self.group:
                    d=math.hypot((sprite.rect.centerx-alien.rect.centerx),(sprite.rect.centery-alien.rect.centery))
                    if d<kPowerUpExplodeDistance:
                        alien.hit(sprite)
                self.set_explosion_mode(BigExplosion)            
        if sprite.name=="virus":    # PC hit by a virus
            self.infected=True
            self.set_image()
                            
        FSpriteWithScore.hit(self,sprite)
        
class Bomb(FSpriteWithSpeed):
    """ Ship bombs : may hurt player """

    def __init__(self,game,ship):
        FSpriteWithSpeed.__init__(self,game) #call Sprite initializer
        self.name="ship-bomb"
        if ship.bombModel>=len(self.game.bombImages):
            print "error pc bomb model out of range %d" % ship.bombmodel
        self.set_image(self.game.bombImages[ship.bombModel])
        self.speed=(0.0,kShipShootSpeedBase+ship.bombModel*kShipShootSpeedModel+self.game.get_level()*kShipShootSpeedLevel)
        self.damage=kShipShootDamage
        self.bottomLimit=self.game.height+self.rect.height
        self.set_pos((ship.rect.centerx,ship.rect.centery+self.rect.height))
        
        self.register(game.get_sprites(),kShotLayer)

    def update(self):
        self.fmove(self.speed,self.game.get_clock())
        if self.rect.bottom>self.bottomLimit:  # kill when off screen
            self.kill()


class Score(Label):
    """ Handle player's score and display it on screen
        derivate from GameEngine Label"""
    
    def __init__(self,game,highScore,font,size):
        Label.__init__(self,game,font,size)
        self.set_color(kFontNormalColor)
        self.set_position((5,10))
        self.value=0
        self.highScore=highScore
        self.modified=True
        
        self.register(game.get_sprites(),kFrontLayer)

    def update(self):
        if self.changed:
            str=self.game.get_widget("strscore")
            self.set_text("%s %06d" % (str.get_text(self.game.language),self.value))
        Label.update(self)

    def add_score(self,v):
        self.value+=v
        self.changed=True
        if not self.game.screen.cheat:
            self.highScore.compare_score(self.value)

class HighScore(Label):
    """ Handle high score and display it on screen
        derivate from GameEngine Label"""
    
    def __init__(self,game,font,size):
        Label.__init__(self,game,font,size)
        self.set_color(kFontNormalColor)
        self.set_position((kSCREEN_WIDTH/2,10),"center")
        self.value=int(game.scores.highScore[0])
        self.modified=True
        
        self.register(game.get_sprites(),kFrontLayer)

    def update(self):
        if self.changed:
            str=self.game.get_widget("strhscore")
            self.set_text("%s %06d" % (str.get_text(self.game.language),self.value))
            Label.update(self)

    def compare_score(self,v):
        if v>self.value:
            self.value=v
            self.changed=True

class Level(Label):
    """ Handle game's level and display it on screen
        derivate from GameEngine Label
        Level is offset from year (1984) """
        
    def __init__(self,game,font,size):
        Label.__init__(self,game,font,size)
        self.set_color(kFontLevelColor)
        self.set_position((kSCREEN_WIDTH-5,10),"right")
        self._level=-1
        self._clock=0.0
        
        self.register(game.get_sprites(),kFrontLayer)

    def update(self):
        self._clock+=0.001*self.game.get_clock() 
        if self.changed:
            if self._level>=0:
                str=self.game.get_widget('strlevel')
                self.set_text('%s %04d' % (str.get_text(self.game.language),self._level+1984))
            else:
                self.set_text('-----')
            Label.update(self)

    def bonus(self):
        """ return the score bonus for the current level """
        if self._clock<kLevelScoreClock:
            bonus=int(kLevelScoreClock-self._clock)*kLevelScoreTime+kLevelScoreBase
        else:
            bonus=kLevelScoreBase
        self.game.screen.score.add_score(bonus) 
        return bonus

    def next_level(self):
        """ go to the next level """
        self._level+=1
        self._clock=0.0
        self.changed=True

    def set_level(self,level):
        """ go to the desire level """
        self._level=level
        self._clock=0.0
        self.changed=True

    def get_level(self):
        return self._level

    def get_text_list(self):
        """ return the intro text for the current level """
        txt=self.game.get_widget("%d" % (self._level+1984))
        if txt!=None:
            return txt.get_text(self.game.language)
        else:
            return []

class PlayerInfo():
    """ Handle player's Mac information and display them on screen
        3 icons + 3 texts (label from FGame) + Life display
        see InfoIcon and InfoText for details """
    
    def __init__(self,game):
        self.game=game
        area=pygame.display.get_surface().get_rect()
        
        x=kInfoXPosition
        y=area.height+kInfoYPosition
        self.modelicon=InfoIcon(game,(x,y),self.game.infoIcons[0]) 
        self.modeltext=InfoText(game,kGameFontName,kFontInfoSize)
        self.modeltext.set_position((x+kInfoXIcon,y))
        self.modeltext.set_color(kInfoTextColor)
        
        y+=kInfoDY
        self.procicon=InfoIcon(game,(x,y),self.game.infoIcons[1]) 
        self.proctext=InfoText(game,kGameFontName,kFontInfoSize)
        self.proctext.set_position((x+kInfoXIcon,y))
        self.proctext.set_color(kInfoTextColor)
        
        y+=kInfoDY
        self.osicon=InfoIcon(game,(x,y),self.game.infoIcons[2]) 
        self.ostext=InfoText(game,kGameFontName,kFontInfoSize)
        self.ostext.set_position((x+kInfoXIcon,y))
        self.ostext.set_color(kInfoTextColor)
        
        y+=kInfoDY
        self.powericon=InfoIcon(game,(x,y),self.game.infoIcons[3]) 
        self.powertext=InfoText(game,kGameFontName,kFontInfoSize)
        self.powertext.set_position((x+kInfoXIcon,y))
        self.powertext.set_color(kInfoTextColor)
        
        self.lifetext=InfoText(game,kGameFontName,kFontInfoSize)
        self.lifetext.set_position((kSCREEN_WIDTH-kLifeWidth-kLifeMargin+kLifeTextXOffset,kSCREEN_HEIGHT-kLifeHeight-kLifeMargin+kLifeTextYOffset))
        self.lifetext.set_color(kFontTextColor)
        txt=self.game.get_widget("strlife")
        self.lifetext.set_text(txt.get_text(self.game.pref.language))
        self.lifetext.posMode="right"

    def add(self,*groups):  # add me to groups
        self.osicon.add(groups)
        self.ostext.add(groups)
        self.procicon.add(groups)
        self.proctext.add(groups)
        self.modelicon.add(groups)
        self.modeltext.add(groups)
        self.powericon.add(groups)
        self.powertext.add(groups)
        self.lifetext.add(groups)
        
    def set_model(self,value):
        str=self.game.macModels[value] 
        if self.modeltext.text!=str:
            self.modeltext.set_text(str)
            self.modeltext.blinkClock=1    # start blinking 

    def set_processor(self,value):
        str=self.game.macProcessors[value] 
        if self.proctext.text!=str:
            self.proctext.set_text(str)
            self.proctext.blinkClock=1    # start blinking 
            
    def set_system(self,value):
        str=self.game.macSystems[value]
        if self.ostext.text!=str:
            self.ostext.set_text(str)
            self.ostext.blinkClock=1    # start blinking
            
    def set_power_up(self,mode,value):
        txt=self.game.get_widget("strpowerup")
        self.powertext.set_text(txt.get_text(self.game.pref.language) % value)
        self.powertext.blinkClock=1    # start blinking       

class InfoIcon(FBasicSprite):
    """ InfoIcon class
        Display player characteristics icon """

    def __init__(self,game,pos,img):
        FBasicSprite.__init__(self,game)
        self.set_image(img)
        self.rect.left=pos[0]
        self.rect.top=pos[1]-self.rect.height/2
        
        self.register(game.get_sprites(),kDefaultLayer)

class InfoText(Label):
    """ InfoText class
        Display player characteristics
        Label + blinking mode when changed """
    
    def __init__(self,game,font,size):
        Label.__init__(self,game,font,size)
        self.blinkClock=0
        self.blinkRate=0
        self.blinkState=False
        self.changed=True
        
        self.register(game.get_sprites(),kDefaultLayer)
 
    def update(self):
        if self.blinkClock!=0:
            clk=self.game.get_clock()
            self.blinkClock+=clk 
            if self.blinkClock>kInfoBlink:  # stop blinking
                self.blinkClock=0
                self.blinkRate=0
                self.changed=True
                self.set_color(kInfoTextColor)
            else:
                self.blinkRate+=clk 
                if self.blinkRate>kInfoBlinkRate:   # blink:inverse state
                    self.blinkState=not self.blinkState
                    self.blinkRate=0
                    self.changed=True
                    if self.blinkState:
                        self.set_color(kFontLevelColor)
                    else:
                        self.set_color(kFontNormalColor)
        elif self.changed:
            self.set_color(kInfoTextColor)
        if self.changed:
            Label.update(self)

class LevelSprite(FBasicSprite):
    def __init__(self,game,desc):
        FBasicSprite.__init__(self,game)
        self.set_image(pygame.Surface((0,0)))
        self.desc=desc
        
        self.register(game.get_sprites(),kDefaultLayer)
    
    def display(self,value):
        if value<0 or value>=len(self.game.levelImages):
            self.set_image(pygame.Surface((0,0)))
        else:
            self.set_image(self.game.levelImages[value])
            self.rect.move_ip((kSCREEN_WIDTH-self.rect.width)/2,self.desc.rect.bottom+50)
            
class LifeZone(FBasicSprite):
    """ LifeZone class
        Display the player life as a colored bar """
    
    def __init__(self,game,player):
        FBasicSprite.__init__(self,game)
        self.set_image(pygame.Surface((kLifeWidth,kLifeHeight)))
        self.rect=(kSCREEN_WIDTH-kLifeWidth-kLifeMargin,kSCREEN_HEIGHT-kLifeHeight-kLifeMargin,kSCREEN_WIDTH-kLifeMargin,kSCREEN_HEIGHT-kLifeMargin)
        self.player=player
        self.oldLife=-1
        
        self.register(game.get_sprites(),kFrontLayer)
        
    def update(self):
        if self.player.life!=self.oldLife:
            ram=self.player.life*self.image.get_width()/self.player.lifeMax
            self.image.fill(kLifeBackground)
            if self.player.life<=1.0:   # determine bar color according to the player life
                color=kLifeLow
            elif self.player.life<=2.0:
                color=kLifeMedium
            else:
                color=kLifeHigh
            self.image.fill(color,(0,0,ram,self.image.get_height()))
            self.oldLife=self.player.life
        
class LoadingZone(FBasicSprite):
    """ LoadingZone class
        Display a colored bar while loading data """
    
    def __init__(self,max,game):
        FBasicSprite.__init__(self,game)
        self.set_image(pygame.Surface((kLoadWidth,kLoadHeight)))
        self.rect=Rect(((kSCREEN_WIDTH-kLoadWidth)/2,kLoadYPos),(kLoadWidth,kLoadHeight))
        self.value=0
        self.max=max
        self.changed=True
        
        self.register(game.get_sprites(),kFrontLayer)
        
    def set_value(self,value):
        if value>self.max:
            self.value=max
        else:
            self.value=value
        self.changed=True
        
    def update(self):
        if self.changed:
            v=self.value*self.rect.width/self.max
            self.image.fill(kLifeBackground)
            self.image.fill(kLoadBar,(0,0,v,self.image.get_height()))

class Point(FSpriteWithSpeed):
    """ Point class
        display a small text with the points that go to player score """
    
    def __init__(self, game, point, posx, posy):
        FSpriteWithSpeed.__init__(self,game)
        self.font = game.load_font(kGameFontName, kFontInfoSize)
        self.color = kFontLevelColor
        self.clock=0.0
        self.animClk=point*kPointClk+kPointClkBase
        self.speed=(0.0,kPointSpeed)
        self.msg="%d" % point
        self.image = self.font.render(self.msg, 0, self.color)
        self.rect=self.image.get_rect()
        self.set_pos((posx-self.rect.width/2, posy-self.rect.height/2))
        self.set_explosion_mode(False)
        self.set_hit_mode(False)
        
        self.register(self.game.get_sprites(),kEffectLayer)

    def update(self):
        self.fmove(self.speed,self.game.get_clock()) 
        self.clock+=self.game.get_clock()
        if self.clock>self.animClk:
            self.kill()
