Source

gallox / gallox / map.py

import math

import xml.etree.ElementTree as ET
from xml.dom.minidom import parseString

import pygame

from gallox.sprites import *
from gallox.utils import load_image, load_sound, nearest_point, dist_pr
from gallox.settings import *

class Map(object):
    def __init__(self, path, world):
        self.world = world
        self.allsprites = pygame.sprite.RenderPlain()
        self.plattforms = pygame.sprite.Group()
        self.surf = None
        self.ship = None
        # channel - source rect mapping
        self.sounds = {}
        self._cached_ship_pos = None
        self.load(path)
        
        
        
    def update(self, events, ticks):
        self.allsprites.update(events, ticks)
        
        self.surf.blit(self.bg_image, (0,0))
        self.surf.blit(self.image, (0,0))
        self.allsprites.draw(self.surf)
        # self.surf.blit(self.fg_image, (0,0))
        
        if self.ship.rect.center != self._cached_ship_pos:
            self._cached_ship_pos = sx, sy = self.ship.rect.center
            for channel, rect in self.sounds.items():            
                if channel.get_busy():
                    rx,ry = nearest_point(rect, (sx,sy))
                    
                    dist = math.hypot(rx-sx, ry-sy) or 0.0
#                    angle = math.atan2(sy-ry, sx-rx)
                    # hoerereignisrichtung
#                    sound_dir = 173.2 * math.tan(math.radians(angle))
                    
#                    #dist = math.hypot(x - pos[0], y - pos[1])
#                    ldist = dist_pr((sx-50,sy), rect) or 0.001
#                    rdist = dist_pr((sy+50, sy), rect) or 0.001
#                    #! just a test -> improve
#                    lvolume = min(max(1.0 * 10. / ldist , 0), 1)
#                    rvolume = min(max(1.0 * 10. / rdist, 0), 1)
#                    channel.set_volume(lvolume, rvolume)
                    
                    volume = min(max(1.0 * 10. / dist, 0), 1)
                    channel.set_volume(volume)
                    
                else:
                    del self.sounds[channel]
        
        
        
        return events, [self.rect]      
        
        
    def collides(self, object):
        """
        object must have rect and mask attributes
        """
        return self.mask.overlap(object.mask, object.rect.topleft)
    
    def check_bullet(self, bullet):
        if not self.rect.contains(bullet.rect): # completely outside?
            bullet.kill()
        point = self.collides(bullet)
        if not point:
            if pygame.sprite.spritecollideany(bullet, self.plattforms):
                point = bullet.rect.center
        if point:
            if isinstance(bullet, ShipBullet):
                pygame.draw.circle(self.image, ALPHA_COLOR, point,
                                   BULLET_HOLE_RADIUS)
                # update mask
                self.mask = pygame.mask.from_surface(self.image)
            bullet.kill()
            
        elif isinstance(bullet, GunBullet):
            #! add pixelperfect collision detection later
#                bx, by = bullet.rect.topleft
#                sx, sy = self.ship.rect.topleft
#                off_x = bx - sx
#                off_y = by - sy
#                if self.ship.mask.overlap(bullet.mask, (off_x, off_y)):
                if pygame.sprite.collide_mask(self.ship, bullet):
#                if self.ship.rect.colliderect(bullet.rect):
                    self.ship.kill()
                    bullet.kill()
                    print "#"*30
                    print "ship killed"
                    print "#"*30
                    
    def sound(self, name, source):
        """
        source may be a rect, pos or object that has a rect attribute
        """
        if len(source) == 2:
            rect = pygame.Rect(source, (1,1))
        else:
            rect = pygame.Rect(source)
        s = SOUNDS[name]
        c = load_sound(s["name"]).play(loops=s.get("loops", 0),
                                       fade_ms=s.get("fade_ms", 0))
        if c:
            self.sounds[c] = rect
        return c
    
    def hush(self, channel):
        if channel in self.sounds:
            del self.sounds[channel]
            channel.stop()
        
                    
   
    def load(self, path):
        tree = ET.parse(os.path.join(path, "level.xml"))
        root = tree.getroot()
        general = root.find("about")
        sprites = root.find("sprites")
        about = {}
        if general:
            about["name"] = general.findtext("name", "[no name given]")
            about["message"] = general.findtext("message", "have fun")
            about["authors"] = []
            
            authors = general.findall("author")
            for author in authors:
                if len(author):
                    name = author.findtext("name")
                    if name:
                        about["authors"].append(name)
 
        self.image, self.rect = load_image(os.path.join(path, "main.png"),
                                           ALPHA_COLOR)
        self.mask = pygame.mask.from_surface(self.image)
        
        bg_path = os.path.join(path, "background.png")
        if os.path.exists(bg_path):
            self.bg_image, r = load_image(bg_path)
        else:
            self.bg_image = pygame.Surface(self.rect.size)
        self.surf = self.bg_image.copy()
        


        if sprites:
            self.allsprites.empty()
            self.plattforms.empty()
            has_ship = False
            for se in sprites.findall("sprite"):
                name = se.get("type")
                p = se.find("position")
                pos = int(p.get("x")), int(p.get("y"))
                s = se.find("size")
                size = int(s.get("w")), int(s.get("h"))
                degrees = int(se.get("degrees", 0))
                
                params = {}
                for param in SPRITE_PARAMS[name]:
                    #! improve
                    cf = CONVERT_FUNCTIONS[param["type"]]
                    val = se.findtext(param["name"])
                    if val:
                        params[param["name"]] = cf(val) or cf(param["default"])
                        
                if name == "plattform":
                    Plattform(pos, size, params, self, self.allsprites,
                              self.plattforms)
                if name == "ship" and not has_ship:
                    self.ship = Ship(pos, params, self, self.allsprites)
                    has_ship = True
                if name == "gun":
                    Gun(pos, degrees, params, self, self.allsprites)
                
            if not has_ship:
                raise Exception, "LevelSyntaxError"