def __init__(self): """ Based on Level, this kind of level is drawn with a maptile image based on a map specification image and the tilekey image. The map spec has one pixel for every tile to be drawn. Tiles are mapped by this class based on the color in the mapspec. So, if pixel (x,y) = (r,g,b) then the pixel-index in the tilekey image corresponding to (r,g,b) is used to get the correct index from the maptile image to be blitted/collided at (x,y). """ Level.__init__(self) # the name of the level repr. by this object self.lvlName = None self.displayName = "Undefined" # store the ref to the screen self.screen = Screen() # get the resource manager self.texMgr = TextureManager() self.setsMgr = SettingsManager() ## level data ## # some things we'll be filling at load time self.spec, self.specTexID = (None, -1) self.tiles, self.tilesTexID = (None, -1) self.tileKey, self.keyTexID = (None, -1) self.tileSize = (0,0) self.tileScrSize = [4,4] self.tileHalfSize = [2,2] self.scrScroll = [0,0] self.mapSize = (0,0) self.mapSpec = [] self.thiefInitPos = (1, 1) self.copInitPos = (1, 1) self.spriteKey = {} self.animSpeed = 20 self.numTilesInView = [1,1]
class PreBuiltLevel(Level): def __init__(self): """ Based on Level, this kind of level is drawn with a maptile image based on a map specification image and the tilekey image. The map spec has one pixel for every tile to be drawn. Tiles are mapped by this class based on the color in the mapspec. So, if pixel (x,y) = (r,g,b) then the pixel-index in the tilekey image corresponding to (r,g,b) is used to get the correct index from the maptile image to be blitted/collided at (x,y). """ Level.__init__(self) # the name of the level repr. by this object self.lvlName = None self.displayName = "Undefined" # store the ref to the screen self.screen = Screen() # get the resource manager self.texMgr = TextureManager() self.setsMgr = SettingsManager() ## level data ## # some things we'll be filling at load time self.spec, self.specTexID = (None, -1) self.tiles, self.tilesTexID = (None, -1) self.tileKey, self.keyTexID = (None, -1) self.tileSize = (0,0) self.tileScrSize = [4,4] self.tileHalfSize = [2,2] self.scrScroll = [0,0] self.mapSize = (0,0) self.mapSpec = [] self.thiefInitPos = (1, 1) self.copInitPos = (1, 1) self.spriteKey = {} self.animSpeed = 20 self.numTilesInView = [1,1] def load( self, lvlName ): """ Call this to load a level's resources and parse the necessary data """ self.lvlName = lvlName self.config = SafeConfigParser() numSprCols = 0 numSprRows = 0 try: # attempt to open the input file name successful = self.config.read(\ os.path.join( self.setsMgr.home_dir , self.setsMgr.media_home , self.setsMgr.levels_dir , self.lvlName , 'info.ini' )) except ( ParsingError, MissingSectionHeaderError ): print 'Error:Level:Could not read/find info file' raise sys.exc_info()[1], None, sys.exc_info()[2] # The following try/except blocks are separated so that individual messages # can be produced for each missing option try: ########### ## level ## ########### try: self.displayName = eval(self.config.get('level','displayName')) except( NoOptionError ): pass try: # Load the map-spec image self.spec, self.specTexID = self.texMgr.load_image(\ os.path.join( self.setsMgr.levels_dir , self.lvlName , self.config.get('level','specImg'))) except( NoOptionError ): print 'Error : Level : No Map Spec img specified in info.ini' raise sys.exc_info()[1], None, sys.exc_info()[2] try: # Load the map tiles image self.tiles, self.tilesTexID = self.texMgr.load_image(\ os.path.join( self.setsMgr.levels_dir , self.lvlName , self.config.get('level','tileImg'))) except( NoOptionError ): print 'Error : Level : No tile img specified in info.ini' raise sys.exc_info()[1], None, sys.exc_info()[2] try: # load the map-tile key image self.tileKey, self.keyTexID = self.texMgr.load_image(\ os.path.join( self.setsMgr.levels_dir , self.lvlName , self.config.get('level','tileKeyImg'))) except( NoOptionError ): print 'Error : Level : Missing map-tile key from info.ini' raise sys.exc_info()[1], None, sys.exc_info()[2] ########## ## tile ## ########## try: self.tileSize = eval(self.config.get('tile', 'tileSize'), {}, {}) except( NoOptionError ): print 'Error : Level : Missing tile size info : width, height' raise sys.exc_info()[1], None, sys.exc_info()[2] try: numSprCols = eval(self.config.get('tile', 'numCols'), {}, {}) numSprRows = eval(self.config.get('tile', 'numRows'), {}, {}) except( NoOptionError ): print 'Error : Level : Missing numCols and Rows in tile image' raise sys.exc_info()[1], None, sys.exc_info()[2] try: self.tileScrSize = eval(self.config.get('tile', 'scrSize'), {}, {}) except( NoOptionError ): print 'Warning : Level : Missing "scrSize" in "tile"; assuming the actual tile size.' self.tileScrSize = [self.tileSize[0], self.tileSize[1]] except( NoSectionError ): print 'Error : Level : Missing section in info.ini', raise sys.exc_info()[1], None, sys.exc_info()[2] # store the map size for future use in a well-afforded place self.mapSize = self.spec.size # Produce boxes for each tile in the tile-set Sprs = [] for y in range(0, numSprRows*self.tileSize[1], self.tileSize[1]): for x in range(0, numSprCols*self.tileSize[0], self.tileSize[0]): Sprs.append( (x, y+self.tileSize[1], x+self.tileSize[0], y) ) # a double-loop to map sprites to color keys for y in range(0, numSprRows): for x in range(0, numSprCols): # get the tile-key color for this tile color = self.tileKey.getpixel( (x,numSprRows-y-1) ) self.spriteKey[color] = Sprs[ x + (y*numSprCols) ] # convert the map spec from image to list( more efficient ) # Get pixel would be too expensive to do so often each frame self.mapSpec = self._readMapSpec_() def _readMapSpec_( self ): """ Generate the list of colors from the map spec image that represent locations of tiles. """ # the output list mapSpec = [] for y in range( 0, self.mapSize[1] ): for x in range( 0, self.mapSize[0] ): # Unpack Pixel Data pixel = self.spec.getpixel( (x,y) ) # 1-1-0 is the Thief's initial position if pixel == (255,255,0): self.setThiefMapCoord(x, y) pixel = (255, 255, 255) if pixel == (0, 255, 255): self.setCopMapCoord(x, y) pixel = (255, 255, 255) # Look for the thief or spot pos # The last one specified in the file is set. mapSpec.append( pixel ) return mapSpec def setThiefMapCoord(self, x, y): self.thiefInitPos = (x, y) def setCopMapCoord(self, x, y): self.copInitPos = (x, y) def getThiefInitPos(self): """ Calculates and returns the most current pos based on the tileScrSize (player-dependent) """ return (self.thiefInitPos[0]*self.tileScrSize[0] , self.thiefInitPos[1]*self.tileScrSize[1]) def getCopInitPos(self): """ Calculates and returns the most current pos based on the tileScrSize (player-dependent) """ return (self.copInitPos[0]*self.tileScrSize[0] , self.copInitPos[1]*self.tileScrSize[1]) def setTileSize( self, size=(4,4) ): """ This sets how big the tile's will be on screen and is not related to the size in its texture """ self.tileScrSize[0] = size[0] self.tileScrSize[1] = size[1] self.tileHalfSize = [self.tileScrSize[0]/2, self.tileScrSize[1]/2] def setTilesPerView(self, numTiles=-1): """ Set the number of tiles that can be seen within the screen at one time """ # -1 means to show all tiles in the screen if numTiles == -1: self.numTilesInView = [ self.mapSize[0]-1, self.mapSize[1]-1 ] else: self.numTilesInView = [ numTiles[0], numTiles[1] ] # grab the screen if we must if not self.screen: self.screen = Screen() self.setTileSize((1 + (self.screen.size[0] / self.numTilesInView[0])\ ,1 + (self.screen.size[1] / self.numTilesInView[1]))) def getTileSize( self ): """ Returns the screen size of tiles on the screen """ return self.tileScrSize def render( self, scrScroll=(0,0), visibleArea=None ): """ Called by the screen object to draw the (visible portion of the) map """ if not self.screen: self.screen = Screen() if visibleArea: visibleArea[0] = (visibleArea[0]+scrScroll[0]) / self.tileScrSize[0] visibleArea[1] = (visibleArea[1]+scrScroll[1]) / self.tileScrSize[1] visibleArea[2] = (visibleArea[2]+scrScroll[0]) / self.tileScrSize[0] visibleArea[3] = (visibleArea[3]+scrScroll[1]) / self.tileScrSize[1] yOffset = scrScroll[1] % self.tileScrSize[1] xOffset = scrScroll[0] % self.tileScrSize[0] # The bounding box of the screen upon the map scrArea= [scrScroll[0] / self.tileScrSize[0] , scrScroll[1] / self.tileScrSize[1] ,(scrScroll[0] + self.screen.size[0]) / self.tileScrSize[0] ,(scrScroll[1] + self.screen.size[1]) / self.tileScrSize[1]] # Incr the tile count if there is one more tile to be rendered # on the screen than was calculated, due to the offset if yOffset > 0 or self.screen.size[1] % self.tileScrSize[1] > 0: scrArea[3] += 1 if xOffset > 0 or self.screen.size[0] % self.tileScrSize[0] > 0: scrArea[2] += 1 renderList = [] nextPixel = [-xOffset, -yOffset] # Loop through the scrArea (per tile) for y in range(scrArea[1], scrArea[3]): # skip the index if its out of range if y > self.mapSize[1]: continue for x in range(scrArea[0], scrArea[2]): # skip the index if its out of range if x > self.mapSize[0]: continue # Get the color from the mapSpec corresponding to tile[x,y] try: # The color indexes the tileBox array color = self.mapSpec[ x + ( y * self.mapSize[0] ) ] except (IndexError): # Index is out of range #print 'Bad Index: ', x + ( y * self.mapSize[0] ) #raise sys.exc_info()[1], None, sys.exc_info()[2] continue if visibleArea: if x >= visibleArea[0] and x < visibleArea[2]\ and y >= visibleArea[1] and y < visibleArea[3]: # This quad is the position and area on screen where this # tile will be drawn quad = ( nextPixel[0] , nextPixel[1] , nextPixel[0]+self.tileScrSize[0] , nextPixel[1]+self.tileScrSize[1] ) # add the tile to the list to be returned to the screen renderList.append( ( self.tilesTexID # texture ID , self.spriteKey[color] # tile's bbox , quad ) ) # pos/area on screen else: # This quad is the position and area on screen where this # tile will be drawn quad = ( nextPixel[0] , nextPixel[1] , nextPixel[0]+self.tileScrSize[0] , nextPixel[1]+self.tileScrSize[1] ) # add the tile to the list to be returned to the screen renderList.append( ( self.tilesTexID # texture ID , self.spriteKey[color] # tile's bbox , quad ) ) # pos/area on screen # increment the pixel location nextPixel[0] += self.tileScrSize[0] # reset the x pixel location nextPixel[0] = -xOffset # decrement the pixel location nextPixel[1] += self.tileScrSize[1] return renderList def getTileAtPoint(self, pos): """ Returns the type of tile in which the given position(in map-space) resides, and the modulous of the tile division which shows where in the tile they are. """ # ... This is relevant because the specPos division # provides the floor. This all goes back to the map being based on # a 1:1 pixel-to-tile mapping. So, determining which tile is under # a position requires scaling down the position to the mapSpec # space and finding the tile at that pixel location. since the # value is truncated, the collision was workin on one side, but not # for the other. ## Convert from tile to pixel coord specPos = [ int(floor(pos[0] / self.tileScrSize[0])) , int(floor(pos[1] / self.tileScrSize[1])) ] modOfPos = [ (pos[0] % self.tileScrSize[0]) , (pos[1] % self.tileScrSize[1]) ] try: ## Grab the color at specPos in the mapSpec Red,Grn,Blu = self.mapSpec[specPos[0]+(specPos[1]*self.mapSize[0])] except: print 'bad index' return (None, None) box = [ specPos[0], specPos[1] , specPos[0]+self.tileScrSize[0] , specPos[1]+self.tileScrSize[1] ] ## Look for particular Colors # Base : 1-0-0 if Red==255 and Grn==0 and Blu==0: return ('base', modOfPos) # Building : (0-0.2-0), (0-0.4-0), (0-0.6-0), (0-0.8-0), (0-1-0) elif Red==0 and Grn > 0 and Blu==0: return ('building', modOfPos) # Drop Point : 0-0-1 elif Red==0 and Grn==0 and Blu==255: return ('drop-point', modOfPos) # X-X-X else: return (None, None)