def __init__(self, filename = None): self.filename = filename self.screenRect = pygame.Rect((0,0,SCREEN_WIDTH,SCREEN_HEIGHT)) self.tileWidth = 16 self.tileHeight = 16 self.tileset = MapTileset("tilesets.txt") self.layers = {} # name : MapLayer self.layerImages = {} # name : (big) surface self.dirtyRects = [] self.dirtySprites = [] self.needFullBlit = True self.mobs = {} # name : Mob self.players = {} # name : Player self.sprites = {} # name : Sprite (for players and mobs) if filename: self.load(filename) #self.addLayer("ground") #self.layers["ground"].fill("gggg") self.updateLayerImage("ground") self.imgW = self.layerImages["ground"].get_width() self.imgH = self.layerImages["ground"].get_height() self.offsetX = 0 self.offsetY = 0 self.offsetXmax = 0 self.offsetYmax = 0 self.selected = None self.collisionVisible = False self.warpVisible = False self.warpImg = None self.warps = [] self.particleManager = MapParticleManager(self) self.arrayblit = [self.dirtyBlit, self.fullBlit]
class Map(GameMap): def __init__(self, filename = None): self.filename = filename self.screenRect = pygame.Rect((0,0,SCREEN_WIDTH,SCREEN_HEIGHT)) self.tileWidth = 16 self.tileHeight = 16 self.tileset = MapTileset("tilesets.txt") self.layers = {} # name : MapLayer self.layerImages = {} # name : (big) surface self.dirtyRects = [] self.dirtySprites = [] self.needFullBlit = True self.mobs = {} # name : Mob self.players = {} # name : Player self.sprites = {} # name : Sprite (for players and mobs) if filename: self.load(filename) #self.addLayer("ground") #self.layers["ground"].fill("gggg") self.updateLayerImage("ground") self.imgW = self.layerImages["ground"].get_width() self.imgH = self.layerImages["ground"].get_height() self.offsetX = 0 self.offsetY = 0 self.offsetXmax = 0 self.offsetYmax = 0 self.selected = None self.collisionVisible = False self.warpVisible = False self.warpImg = None self.warps = [] self.particleManager = MapParticleManager(self) self.arrayblit = [self.dirtyBlit, self.fullBlit] def addWarp(self, name, x, y, w, h): for warp in self.warps: if warp.name == name: return self.warps.append(MapWarp(name, x, y, w, h)) self.makeWarpImage() def selectTarget(self, name): self.selected = name self.selectCursor = ImgDB["graphics/gui/guibase.png"].subsurface((16,32,32,16)).convert_alpha() def unselectTarget(self): if self.selected: if self.selected in self.mobs: sprite = self.mobs[self.selected] self.addDirtyRect(pygame.Rect(sprite.rect.x-4, sprite.rect.y+24, 50,30)) print "cleaning select rect on mob die : %s" % (sprite.name) self.selected = None def addPlayer(self, name, x=50.0, y=50.0): if name not in self.players: self.players[name]=Player(name, self, x, y) self.players[name].setSprite(makePlayerSprite(name, self)) self.players[name].setPos(x,y) #self.players[name].setMovement(0,0) self.players[name].update() else: pass #print("Error, asked to add player %s, but that one is already here." % (name)) def delPlayer(self, playerName): if playerName in self.players: self.addDirtyRect(self.players[playerName]._sprite.getDirtyRect()) del self.players[playerName] def addMob(self, name, mobId, x=50.0, y=50.0): if name not in self.mobs: self.mobs[name]=Mob(name, mobId, self, x, y) self.mobs[name].setSprite(makeMobSprite(name, self)) #self.mobs[name].setPos(x,y) self.mobs[name].setMovement(0,0) self.mobs[name].update() self.needFullBlit = True def delMob(self, name): self.addDirtyRect(self.mobs[name]._sprite.getDirtyRect()) #print "del mob : dirty rect = %s" % (self.mobs[name]._sprite.getDirtyRect()) # ok this doesn't work, so for now, brutal full blit will do #self.needFullBlit = True del self.mobs[name] def update(self, dt): self.particleManager.update() for playerName in self.players: self.players[playerName].update(dt) for mobName in self.mobs: self.mobs[mobName].update(dt) def handleClick(self): #print "--- Map handle click ---" x, y = pygame.mouse.get_pos() for playerName in self.players: player = self.players[playerName] if player._sprite.rect.collidepoint(x, y): print "Player %s was clicked on" % (playerName) for mobName in self.mobs: mob = self.mobs[mobName] if mob._sprite.rect.collidepoint(x, y): self.selectTarget(mobName) print "Monster %s was clicked on" % (mobName) print "click on %s, %s" % (x, y) def setOffset(self, x, y): if x != self.offsetX or y != self.offsetY: self.offsetX = x self.offsetY = y self.needFullBlit = True def isValidPos(self, x, y): if (0<=x<self.w) and (0<=y<self.h): return True return False def isMobOnScreen(self, mobName): if mobName not in self.mobs: return False if self.screenRect.colliderect(self.mobs[mobName]._sprite.rect): return True return False def setSize(self, x, y): for layerName in self.layers: self.w = x self.h = y self.layers[layerName].setSize(x, y) self.makeLayerImage(layerName) def load(self, filename): self.filename = filename content = open(filename).read() for line in content.split("\n"): line = line.strip() if len(line)<2: continue if "=" in line: if len(line.split("="))!=2: continue key , value = line.split("=") if key.strip() == "name": self.name = value.strip() elif key.strip() == "w": self.w = int(value.strip()) elif key.strip() == "h": self.h = int(value.strip()) else: codes = value.split(',') if len(codes) == self.w * self.h: layerName = key.strip() value = value.strip() #print "Loading layer %s" % (layerName) self.addLayer(layerName) self.layers[layerName].setData(value) self.updateLayerImage(layerName) if "collision" not in self.layers: self.makeCollisionGrid() def addLayer(self, name): self.layers[name] = MapLayer(name, self.w, self.h, self.tileWidth, self.tileHeight) self.makeLayerImage(name) def makeLayerImage(self, name): if name not in self.layers:return #print "map makes layer image for %s" % (name) self.layerImages[name] = pygame.surface.Surface((self.w*self.tileWidth, self.h * self.tileHeight)) if name=="collision": self.layerImages[name].fill((255,0,255)) self.layerImages[name].set_colorkey((255,0,255)) self.layerImages[name].set_alpha(120) self.updateLayerImage(name) def updateLayerImage(self, name): self.layerImages[name].fill((255,0,255)) self.layerImages[name].set_colorkey((255,0,255)) for x in range(self.w): for y in range(self.h): code = self.layers[name].getTile(x, y) if code: self.layerImages[name].blit(self.tileset.getTile(code), (x*self.tileWidth, y*self.tileHeight)) if name == "collision": self.layerImages[name].set_alpha(120) def makeCollisionGrid(self): if not self.filename:return False self.addLayer("collision") for x in range(self.w): for y in range(self.h): if self.layers["ground"].getTile(x, y) == "wwww": self.layers["collision"].tiles[x][y] = 1 else: self.layers["collision"].tiles[x][y] = 0 self.updateLayerImage("collision") def makeWarpImage(self): self.warpImg = pygame.surface.Surface((self.w*self.tileWidth, self.h * self.tileHeight)) self.warpImg.fill((255,0,255)) self.warpImg.set_colorkey((255,0,255)) for warp in self.warps: pygame.draw.rect(self.warpImg, (255,120,120), (warp.x, warp.y, warp.w, warp.h)) self.warpImg.set_alpha(120) def addDirtyRect(self, rect): self.dirtyRects.append(rect) def blit(self, screen): self.arrayblit[self.needFullBlit](screen) def dirtyBlit(self, screen): for rect in self.dirtyRects: x = rect.x+self.offsetX y = rect.y+self.offsetY needFill = False baseRect = rect.copy() if x<0: rect.w = rect.w-abs(x) rect.x -= x x=0 needFill = True if y<0: rect.h = rect.h-abs(y) rect.y -= y y = 0 needFill = True if x+rect.w>self.imgW: rect.w = self.imgW - x needFill = True if y+rect.h>self.imgH: rect.h = self.imgH - y needFill = True if needFill: pygame.draw.rect(screen, (0,0,0), (baseRect.x,baseRect.y, baseRect.w, baseRect.h)) img = self.layerImages["ground"].subsurface(x,y, rect.w, rect.h) screen.blit(img, (rect.x,rect.y)) if self.warpVisible and self.warpImg: screen.blit(self.warpImg.subsurface(x,y, rect.w, rect.h), (rect.x,rect.y)) if self.collisionVisible: screen.blit(self.layerImages["collision"].subsurface(x,y, rect.w, rect.h), (rect.x,rect.y)) for sprite in sorted(self.dirtySprites, key = lambda k:k.mapRect.y): if self.selected: if sprite.name == self.selected: screen.blit(self.selectCursor, (sprite.rect.x-4, sprite.rect.y+24)) sprite.blit(screen) if sprite.talk: w = sprite.talkImg.get_width() h = sprite.talkImg.get_height() pygame.draw.rect(screen, (255,0,0), (sprite.rect.x+sprite.rect.w/2-w/2, sprite.rect.y-h-2, w, h), 1) self.particleManager.blit(screen) self.dirtyRects = [] self.dirtySprites = [] def fullBlit(self, screen): screen.fill((0,0,0)) # ground screen.blit(self.layerImages["ground"], (-self.offsetX,-self.offsetY)) #print "Map offset : %s %s " % (self.offsetX, self.offsetY) # collision if self.collisionVisible: screen.blit(self.layerImages["collision"], (-self.offsetX,-self.offsetY)) # warps if self.warpVisible and self.warpImg: screen.blit(self.warpImg, (-self.offsetX, -self.offsetY)) # map objects sprites = [p._sprite for p in self.players.values()] sprites.extend([p._sprite for p in self.mobs.values() if self.isMobOnScreen(p.name)]) for sprite in sorted(sprites, key = lambda k:k.mapRect.y): if self.selected: if sprite.name == self.selected: screen.blit(self.selectCursor, (sprite.rect.x-4, sprite.rect.y+24)) sprite.blit(screen) # particles self.particleManager.blit(screen) # reset dirty lists self.dirtyRects = [] self.dirtySprites = [] self.needFullBlit = False def clearTile(self, layerName, x, y): self.layers[layerName].clearTile(x, y) self.layerImages[layerName].blit(self.tileset.emptyTile, (x*self.tileWidth, y*self.tileHeight)) self.updateLayerImage(layerName) def setTile(self, layerName, x, y, code): if not layerName in self.layers:return if not self.isValidPos(x, y):return if self.name == "collision": code = int(code) if code == self.layers[layerName].getTile(x, y):return self.layers[layerName].setTile(x, y, code) self.layerImages[layerName].blit(self.tileset.getTile(code), (x*self.tileWidth, y*self.tileHeight)) # check tile up left if self.isValidPos(x-1, y-1): oldCode = self.layers[layerName].getTile(x-1, y-1) newCode = oldCode[0:3] + code[0] self.layers[layerName].setTile(x-1, y-1, newCode) self.layerImages[layerName].blit(self.tileset.getTile(newCode), ((x-1)*self.tileWidth, (y-1)*self.tileHeight)) # check tile up if self.isValidPos(x, y-1): oldCode = self.layers[layerName].getTile(x, y-1) newCode = oldCode[0:2] + code[0:2] self.layers[layerName].setTile(x, y-1, newCode) self.layerImages[layerName].blit(self.tileset.getTile(newCode), (x*self.tileWidth, (y-1)*self.tileHeight)) # check tile up right if self.isValidPos(x+1, y-1): oldCode = self.layers[layerName].getTile(x+1, y-1) newCode = oldCode[0:2] + code[1] + oldCode[3] self.layers[layerName].setTile(x+1, y-1, newCode) self.layerImages[layerName].blit(self.tileset.getTile(newCode), ((x+1)*self.tileWidth, (y-1)*self.tileHeight)) # check tile left if self.isValidPos(x-1, y): oldCode = self.layers[layerName].getTile(x-1, y) newCode = oldCode[0] + code[1] + oldCode[2] + code[3] self.layers[layerName].setTile(x-1, y, newCode) self.layerImages[layerName].blit(self.tileset.getTile(newCode), ((x-1)*self.tileWidth, y*self.tileHeight)) # check tile right if self.isValidPos(x+1, y): oldCode = self.layers[layerName].getTile(x+1, y) newCode = code[0] + oldCode[1] + code[2] + oldCode[3] self.layers[layerName].setTile(x+1, y, newCode) self.layerImages[layerName].blit(self.tileset.getTile(newCode), ((x+1)*self.tileWidth, y*self.tileHeight)) # check tile down left if self.isValidPos(x-1, y+1): oldCode = self.layers[layerName].getTile(x-1, y+1) newCode = oldCode[0] + code[2] + oldCode[2:4] self.layers[layerName].setTile(x-1, y+1, newCode) self.layerImages[layerName].blit(self.tileset.getTile(newCode), ((x-1)*self.tileWidth, (y+1)*self.tileHeight)) # check tile down if self.isValidPos(x, y+1): oldCode = self.layers[layerName].getTile(x, y+1) newCode = code[0:2] + oldCode[2:4] self.layers[layerName].setTile(x, y+1, newCode) self.layerImages[layerName].blit(self.tileset.getTile(newCode), (x*self.tileWidth, (y+1)*self.tileHeight)) # check tile down right if self.isValidPos(x+1, y+1): oldCode = self.layers[layerName].getTile(x+1, y+1) newCode = code[0] + oldCode[1:4] self.layers[layerName].setTile(x+1, y+1, newCode) self.layerImages[layerName].blit(self.tileset.getTile(newCode), ((x+1)*self.tileWidth, (y+1)*self.tileHeight))