"""Map display utilities"""
"""Adventures on Planet Zephulor by Aaron Hachez. 

   All code, artwork, and sound copyright 2003, Aaron Hachez.

   ----------------------------------------------------------------------------------------

   This file is part of "Adventures on Planet Zephulor".

   A copy of the GNU General Public License should be included with any
   redistribution of this program in a file named "COPYING.txt".

   "Adventures on Planet Zephulor" 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.

   "Adventures on Planet Zephulor" 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 "Adventures on Planet Zephulor"; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

   ----------------------------------------------------------------------------------------

   "Adventures on Planet Zephulor" uses the following libraries:

   PyGame
   SDL

   Both are at the time of this writing, licensed under the GNU LGPL. Please see
   the included file LGPL for details 
"""

import os
import pickle
import myimg
import pygame
import game_entities
import Tkinter

class App(Tkinter.Frame):
    """Get a string and return it"""
    def __init__(self, master=None, mytitle="Title", myname="default.data"):
        Tkinter.Frame.__init__(self, master)
        self.pack()

        self.label1 = Tkinter.Label(text=mytitle)
        self.label1.pack()
        self.entry = Tkinter.Entry()
        self.entry.pack()

        self.contents = Tkinter.StringVar()
        self.contents.set(myname)
        self.entry["textvariable"] = self.contents

    def get(self):
        return(self.contents.get())


class map_base:
    """Basic map class"""

    # The file name for the map
    mapfile = "default.data"

    # A list of end level maps to load
    nextmaplist = {}
    
    # The file name for the tiledata
    bmpfile = "default.bmp"

    # The tag name of the file to use for the background, and end flag of number.extension will be added
    bgtag = "background"

    # The file to use for music
    musicfile = None

    # The file to get higlight data from
    highlightfile = "highlight.bmp"

    # A text string concerning the load/save status of the map
    status = "No map loaded"

    # Total map width and height in pixels
    width = 0
    height = 0

    # The number of tiles in a row/col of the map
    tileswide = 0
    tileshigh = 0

    # Tile width and height
    tilewidth = 0
    tileheight = 0

    # The offset to add at the end of each displayed row of tiles
    mapoffset = 0

    # The last beging tile used for draw and coldet routines
    lastrangestart = 0

    # The current list of tiles to use for draw and coldet routines
    currentlist = []

    # x/y velocity, i.e. screen scroll speed
    xvel = 0
    yvel = 0

    # Current viewport rect
    viewport = []

    # Viewport width/height in pixels
    viewportwidth = 0
    viewportheight = 0

    # Viewport width/height in tiles
    viewporttilewidth = 0
    viewporttileheight = 0

    # Current pixel offset
    viewportxoff = 0
    viewportyoff = 0

    # Flag for debugging, if not set, collision highlights will not be drawn
    coldebug = 1

    # lists used for highlighting tiles, specifically for troubleshooting
    redblist = []
    blueblist = []
    greenblist = []
    whiteblist = []
    yellowblist = []

    # The tile currently selected for change
    selected = []
    selattrib = []
    
    # A list of tiles to use for this map
    maplist = []

    # A list of map tiles (i.e. images)
    maptiles = []

    # A list for data pertaining to each tile
    # List is passable, passleft, passtop, passright, passbottom, pain, slow - 1 indicates a yes, 0 indicates no. 
    # For passable, if "passable" is set, passleft, passtop, passright and passbottom are ignored.
    # If no data is specified for a tile that has a graphic, the tile is assumed to be non-passable only.
    # If a tile has no graphic, but has a value of -2, then its flags are evaluated, but nothing is drawn.
    mapdata = []

    # A list containing each of the characters on the map
    charlist = []

    # Index to the player's character in the charlist
    playerchar = None

    # A list containing filename data about each char on the map
    chardata = []

    # Last char id assigned. 0 is reserved for main char
    lastid = 1

    # A list of tiles to use for highlight
    highlighttiles = []

    def __init__(self, filename=None, bgtag="background", tilefile=None, mapsize=(0, 0), tilesize=(32, 32), viewrect=[ 0, 0, 0, 0 ]):
        """Initialize the map object. Must specify mapsize=(w, h) (in pixels) and viewrect=(x, y, x2, y2) for it to work properly.
        Optionally one can specify tilesize=(w, h) for tile width and height (32x32 by default). """

        # Copy the background tag info
        self.bgtag = bgtag        
   
        # Define the starting position/total size of the viewport
        self.viewport = viewrect

        # Get the total width and height of the viewport
        self.viewportwidth = viewrect[2] - viewrect[0]
        self.viewportheight = viewrect[3] - viewrect[1]

        # Now get viewport width/height in tiles rather
        # Add one extra for offset
        self.viewporttilewidth = self.viewportwidth / tilesize[0] + 1
        self.viewporttileheight = self.viewportheight / tilesize[1] + 1

        # Setup total map size
        self.width = mapsize[0]
        self.height = mapsize[1]

        # Set tile size
        self.tilewidth = tilesize[0]
        self.tileheight = tilesize[1]
        
        # Record total number of tiles in a map row/col
        self.tileswide = self.width / self.tilewidth
        self.tileshigh = self.height / self.tileheight

        # Set offset that needs to be added to the tile range each row for draw and coldet routines
        self.mapoffset = self.tileswide - self.viewporttilewidth

        self.highlighttiles = myimg.loadtiles(self.highlightfile, tilewidth=self.tilewidth, \
                                               tileheight=self.tileheight, border=1, colorkey=0)
        # Initialize map data
        for n in range(self.tileswide):
            for i in range(self.tileshigh):
                self.maplist += [-1]
                self.mapdata += [[]]

        if tilefile:
            self.bmpfile = tilefile
 
        # If a filename was passed, load the file data, else initiallize the data structures
        if filename:
            # Record map filename
            self.mapfile = filename
            self.loaddata(filename)

        # Load tiles, default if none specified in loaded map file
        self.loadtiles(self.bmpfile)

        # Debug
        #print self.tileswide, self.tileshigh, self.viewporttilewidth, self.viewporttileheight, self.viewport

    def savedata(self, filename=None):
        """Write the map data to given file."""

        if not filename:
            if self.mapfile:
                filename = self.mapfile
            else:
                print "No filename found in function savedata"
                return None

        # Setup os independant path to file
        fullname = os.path.join('../data', filename)

        try:
            # Open file for write
            f = file(fullname, 'w')
        except:
            self.status = "Error opening file " + `filename` + " for save."
            return None

        # Now pickle the data
        # The name of the bitmap to load for tiles
        # The map tile flags list
        # The map tile list
        # The character list
        # The next map to load at levelend
        pickle.dump(self.width, f)
        pickle.dump(self.height, f)
        pickle.dump(self.tilewidth, f)
        pickle.dump(self.tileheight, f)
        pickle.dump(self.bmpfile, f)
        pickle.dump(self.maplist, f)
        pickle.dump(self.mapdata, f)
        pickle.dump(self.chardata, f)
        pickle.dump(self.nextmaplist, f)
        pickle.dump(self.bgtag, f)
        pickle.dump(self.musicfile, f)

        # Debug
        #print self.chardata, self.playerchar

        # Close file
        f.close()

        if not self.maplist:
            self.status = "Error saving " + `filename` + "."
            return None
        else:
            self.status = "Map " + `filename` + " saved."
            return 1

    def loaddata(self, filename=None):
        """Load the map data from given file."""

        if not filename:
            if self.mapfile:
                filename = self.mapfile
            else:
                print "No filename in function loaddata"
                return None

        # Setup os independant path to file
        fullname = os.path.join('../data', filename)

        try:
            # Open file for read
            f = file(fullname, 'r')
        except:
            self.status = "Error opening file " + `filename` + " for load, save to create."
            return None

        # Now unpickle the data
        # The name of the bitmap to load for tiles
        # The map tile flags list
        # The map tile list
        # Character file list
        # Index of main player char
        self.width = pickle.load(f)
        self.height = pickle.load(f)
        self.tilewidth = pickle.load(f)
        self.tileheight = pickle.load(f)
        self.bmpfile = pickle.load(f)
        self.maplist = pickle.load(f)
        self.mapdata = pickle.load(f)
        self.chardata = pickle.load(f)
        self.nextmaplist = pickle.load(f)
        self.bgtag = pickle.load(f)
        self.musicfile = pickle.load(f)

        f.close()

        # Record total number of tiles in a map row/col
        self.tileswide = self.width / self.tilewidth
        self.tileshigh = self.height / self.tileheight

        # Set offset that needs to be added to the tile range each row for draw and coldet routines
        self.mapoffset = self.tileswide - self.viewporttilewidth

        # Now load the tiles from the given bitmap file
        self.loadtiles(self.bmpfile, borderwidth=1)

        # Now load all of the chars
        for (charfile, x, y) in self.chardata:
            self.charlist += [ game_entities.base_entity( charfile, (x, y) ) ]

        if not self.maplist:
            self.status = "Error loading " + `filename` + "."
            return None
        else:
            self.status = "Map " + `filename` + " loaded."
            return 1

    def loadtiles(self, filename=None, borderwidth=1):
        """Load tilemap data"""
 
        if not filename:
            if self.bmpfile:
                filename = self.bmpfile
            else:
                print "No filename found in function loaddata"
                return None
       
        self.maptiles = myimg.loadtiles(filename, tilewidth=self.tilewidth, tileheight=self.tileheight, border=borderwidth, colorkey=0)

    def makelist(self):
        """Make a list of tiles to use in draw, and coldet routines. 
        This function checks the last rangestart (first tile in the list of tiles, 
        and if it is the same as last time, skips the rest of the processing.
        Note: also elemenates tiles with a maplist value of -1"""

        # Rangestart equals the viewportx distance from the left edge of the map
        # divided by the width of the tiles, plus the viewporty distance from top edge
        # of the map divided by the height of the tiles, multiplied by the number of tiles
        # in a row (A full row the length of the map). The rangestop is exactly the same
        # Except that we use viewportx + screenwidth, and viewporty + screenheight.
        # This tells us what tile we are starting at, and can be used in our range loop

        rangestart = self.viewport[0] / self.tilewidth
        rangestart += (self.viewport[1] / self.tileheight) * self.tileswide

        # If rangestart is different than last cycle, or there isn't any list yet
        # Note: This doesn't mean the map might not have moved,
        # only that it didn't move enough to cause new tiles to
        # to be processed.
        if rangestart != self.lastrangestart or not self.currentlist:
            # Copy rangestart for next cycle
            self.lastrangestart = rangestart

            # Clear the list
            self.currentlist = []
 
            # Screen coordinates
            x = 0
            y = 0
            
            # Set first tile
            n = rangestart

            listlen = len(self.maplist)

            # For each row
            for i in range(self.viewporttileheight):
                # Set y display position
                y = i * self.tileheight

                # For each column in row
                for j in range(self.viewporttilewidth):
                    # Set x display position
                    x = j * self.tilewidth

                    if n < listlen:
                        listid = self.maplist[n]
                    else:
                        listid = -1
                    
                    if not listid == -1:
                        # Store tile number and x/y position of said tile
                        self.currentlist += [[n, x, y]]

                    n += 1

                # Reset x position
                x = 0
    
                # Increment to right position in next line
                n += self.mapoffset

    def draw(self, screen):
        """Draw the map to the screen"""

        # Find the current viewport offset (In pixels)
        xoff = self.viewport[0] % self.tilewidth
        yoff = self.viewport[1] % self.tileheight

        # For loop over (tileid, xpos, ypos) for each tile to display
        for (n,x,y) in (self.currentlist):

            listid = self.maplist[n]

            # Function make list should eleminate all tiles that are -1, but if not...
            if (listid != -1):
                if listid == -2:
                    self.blueblist += [(x - xoff, y - yoff)]
                if listid == -3:
                    self.whiteblist += [(x - xoff, y - yoff)]
                if listid == -4:
                    self.yellowblist += [(x - xoff, y - yoff)]

                if listid < len(self.maptiles):
                    screen.blit(self.maptiles[ listid ], (x - xoff, y - yoff))

        for char in self.charlist:
            
            # NEED TO RESOLVE AND UPDATE WITH MAP UPDATE FUNCTION (I think)
            # Resolve all forces on object into single x/y values
            #char.resolve()
            # Finalize character position
            #char.update()
    
            # Figure the draw location of this char dynamically, unlike ingame
            localx = char.screenrect[0] - xoff
            localy = char.screenrect[1] - yoff
            localx -= self.viewport[0]
            localy -= self.viewport[1]

            # Draw the character to the screen
            screen.blit(char.tiles[ 0 ], (localx, localy) )

        if self.coldebug:
            # Draw troubleshooting highlights
            if self.yellowblist:
                for (x,y) in (self.yellowblist):
                    screen.blit(self.highlighttiles[ 4 ], (x, y))
                self.yellowblist = []

            if self.whiteblist:
                for (x,y) in (self.whiteblist):
                    screen.blit(self.highlighttiles[ 3 ], (x, y))
                self.whiteblist = []

            if self.blueblist:
                for (x,y) in (self.blueblist):
                    screen.blit(self.highlighttiles[ 2 ], (x, y))
                self.blueblist = []
    
            if self.greenblist:
                for (x,y) in (self.greenblist):
                    screen.blit(self.highlighttiles[ 1 ], (x, y))
                self.greenblist = []
    
            if self.redblist:
                for (x,y) in (self.redblist):
                    screen.blit(self.highlighttiles[ 0 ], (x, y))
                self.redblist = []

# NEW FOR MAP TOOL
    def getclick(self, object):
        """Check to see whether the mouse clicked within the map viewport"""

        # Simple test to see if we are outside any of the bounds of the screen.
        # If so, return None
        if (object.screenrect[0] + self.viewport[0]) > self.viewport[2]:
            return
        elif (object.screenrect[0] + self.viewport[0]) < self.viewport[0]:
            return
        elif (object.screenrect[1] + self.viewport[1]) > self.viewport[3]:
            return
        elif (object.screenrect[1] + self.viewport[1]) < self.viewport[1]:
            return
        else:
            return 1

# NEW FOR MAP EDITOR
    def addselected(self, tile, clear=0, rangelast=0):
        """Add a tile to the selected list (From menu)"""

        # Test to see if this tile is already in the selected list
        for n in self.selected:
            if n == tile:
                return

        # If clear, then reset the list
        if clear:
            self.selected = []
            self.selattrib = []

        # If range last is set, then we select all tiles between the currently selected, and the last selected
        if rangelast == 1:
            tilelast = self.selected[(len(self.selected) - 1)]

            if tilelast > tile:
                start = tile
                end = tilelast
            else:
                start = tilelast
                end = tile

            padstart = start % self.tileswide 
            padend = end % self.tileswide

            for i in range(start, end + 1):

                # If we are clicking outside of the map window
                if (i % self.tileswide) > padend or (i % self.tileswide) < padstart:
                    pass
                else:
                    self.selattrib = self.mapdata[i]
                    self.selected += [i]

            # Now we have to cull tiles that have been selected twice
            selectedcp = self.selected[:]
            for item1 in self.selected:
                # Remove this item from the copy list, so that we don't duplicate
                selectedcp.remove(item1)
                for item2 in selectedcp:
                    if item1 == item2:
                        self.removeselected(item2)
        else:
            self.selattrib = self.mapdata[tile]
            self.selected += [tile]

    def removeselected(self, tile):
        """Remove selected tile from list"""
       
        # Loop through all tiles to make sure the one we want to remove is really in the list 
        for n in self.selected:
            if n == tile:
                self.selected.remove(tile)
                break

    def isselected(self, tile):
        """Return whether or not the given tile is selected"""

        for n in self.selected:
            if n == tile:
                return(1)

# NEW FOR MAP EDITOR
    def addchar(self, tile, charmenu, char):
        """Add a character to the map at location 'tile'."""

        # Figure out where this tile is.
        tilex = ((tile % self.tileswide)) * self.tilewidth
        tiley = ((tile / self.tileswide)) * self.tileheight

        # Set the player char to the first one onscreen (This will be alterable later)
        if not self.charlist:
            self.playerchar = 0

        # Load char from data
        self.charlist += [ game_entities.base_entity( charmenu.charfiles[char], (tilex, tiley) ) ]

        # Set the id to the next available
        self.charlist[ len(self.charlist) - 1 ].id = self.lastid

        # Increment to next avaliable id
        self.lastid += 1

        # Record filename of char, and x/y position
        self.chardata += [(charmenu.charfiles[char], tilex, tiley)]
       
        # Debug
        #print self.chardata, self.playerchar

    def removechar(self, char):
        """Remove a selected char from charlist"""

        # Loop through te list until we find the char, and remove it.
        # Also copy filename so that we can find the same in the chardata
        for listchar in self.charlist:
            if listchar == char:
                filename = listchar.myfile
                localx = listchar.screenrect[0]
                localy = listchar.screenrect[1]
                self.charlist.remove(listchar)

        # Find entry for this char in chardata based upon filename and x/y position, and remove it
        for chardata in self.chardata:
            if chardata[0] == filename and chardata[1] == localx and chardata[2] == localy:
                self.chardata.remove(chardata)

        print self.chardata, self.charlist

# NEW FOR MAP EDITOR
    def returntile(self, object):
        """Take x/y coords, and return what tile they are on"""

        x = object.screenrect[0]
        y = object.screenrect[1]

        if x < self.viewportwidth and y < self.viewportheight:
    
            wherex = self.viewport[0] + x
            wherey = self.viewport[1] + y
    
            tile = wherex / self.tilewidth
            tile += (wherey / self.tileheight) * self.tileswide

            return tile
        else:
            return

# NEW FOR MAP EDITOR
    def highlighttile(self, tile, red=0, green=0, blue=0, white=0, yellow=0, add=0):
        """Highlight a single tile, pass color=1 for that color selection, and add=1 for additive rather than replace"""

        if tile != None:
   
            tilex = ((tile % self.tileswide)) * self.tilewidth
            tiley = ((tile / self.tileswide)) * self.tileheight

            tilex -= self.viewport[0]
            tiley -= self.viewport[1]

            if green:
                if not add:
                    self.greenblist = []
                self.greenblist += [(tilex, tiley)]
            elif red:
                if not add:
                    self.redblist = []
                self.redblist += [(tilex, tiley)]
            elif blue:
                if not add:
                    self.blueblist = []
                self.blueblist += [(tilex, tiley)]
            elif white:
                if not add:
                    self.whiteblist = []
                self.whiteblist += [(tilex, tiley)]
            elif yellow:
                if not add:
                    self.yellowblist = []
                self.yellowblist += [(tilex, tiley)]


# NEW FOR MAP EDITOR
    def changeattrib(self, attributes=[]):
        """Change the attributes on currently selected tiles"""

        for n in self.selected:
            self.mapdata[n] = attributes[:]


    def changeallattrib(self, listid, attributes=[]):
        """Change all tiles of type, to have the same attributes"""

        print "Changing attributes for all tiles with id:", listid

        for i in range(len(self.maplist)):

            if self.maplist[i] == listid:
                self.mapdata[i] = attributes[:]

# NEW FOR MAP EDITOR
    def changetile(self, domenu, seed):
        """Change the selected tile to the type requested"""

        # domenu contains the tile(s) id's to replace the selected, and seed is for random
        # picking of the tiles

        # For each selected tile
        for n in self.selected:

            # Get a quasi random tile
            select = domenu.returnselected(n * seed)

            # Replace map tile with said tile
            self.maplist[n] = domenu.selected[select]

            # If the tile we are placing is "Erase" (-1)
            if domenu.selected[select] == -1:
                # Erase attribute set
                self.selattrib = []
                self.mapdata[n] = []

                # List comprehension to take apart the dictionary of next maps
                dictlist = [(k,v) for k,v in self.nextmaplist.items()]
                
                # Loop through the end level flags and remove any that were matched to this tile
                for index,mapname in dictlist:
                    if index == n:
                        dictlist.remove( (index, mapname) )
 
                # Now turn list back into dictionary
                self.nextmaplist = dict(dictlist)

                print self.nextmaplist

            # If the tile we are placing is endlevel
            elif domenu.selected[select] == -3:
                # Create a tk dialog to get filename
                fnamedialog = App(mytitle="End level filename")
                fnamedialog.mainloop()

                # List comprehension to take apart the dictionary
                dictlist = [(k,v) for k,v in self.nextmaplist.items()]
                # Now append the next entry as a tuple, n as the tile index, and fnamedialog.get() as the file name
                dictlist.append( (n, fnamedialog.get()) )
                # Now turn list back into dictionary
                self.nextmaplist = dict(dictlist)

                #self.nextmaplist += 1 : fnamedialog.get() 
                print self.nextmaplist
            # Else copy attributes, or create blank
            else:
                # If no attributes for this tile yet
                if not self.selattrib:
                    self.selattrib = [ 0,0,0,0,0,0,0 ]

                # Copy attributes
                self.mapdata[n] = self.selattrib[:]

    def hardcenter(self, object):
        """Center the map on a target"""

        # Find where the map should be to be properly centered on this item
        xgoal = (self.viewportwidth - object.width) / 2
        ygoal = (self.viewportheight - object.height) / 2

        # The ammount to move by
        move = 0

        # Test for object out of the x/ygoal bounds
        # Test for screen bounds, and if all workout, move screen and object by appropriate opposing ammounts.
        if object.screenrect[0] > xgoal:
            move = object.screenrect[0] - xgoal
            if not ((self.viewport[2] + move) >= (self.width - 1)):
                self.adjustpos(x=move)
                object.adjposscr(x=-move)
        elif object.screenrect[0] < xgoal:
            move = xgoal - object.screenrect[0]
            if not ((self.viewport[0] - move) <= 0):
                self.adjustpos(x=-move)
                object.adjposscr(x=move)

        if object.screenrect[1] > ygoal:
            move = object.screenrect[1] - ygoal
            if not ((self.viewport[3] + move) >= self.height):
                self.adjustpos(y=move)
                object.adjposscr(y=-move)
            else:
                leftovermove = self.height - self.viewport[3]
                self.adjustpos(y=leftovermove)
                object.adjposscr(y=-leftovermove)
        elif object.screenrect[1] < ygoal:
            move = ygoal - object.screenrect[1]
            if not ((self.viewport[1] - move) <= 0):
                self.adjustpos(y=-move)
                object.adjposscr(y=move)
                

    def softcenter(self, object, speed=5):
        """Center map on target via soft updates, i.e. add velocity until the map reaches a certain point"""

        # Padding for centering
        pad = 2 * speed

        # Find where the map should be to be properly centered on this item
        xgoal = (self.viewportwidth - object.width) / 2
        ygoal = (self.viewportheight - object.height) / 2

        #print xgoal, ygoal, object.screenrect

        # If the object is position is greater than goal + plus a little padding
        # elif the object is position is less than goal
        if object.screenrect[0] > xgoal + pad:
            # Is viewport already at max?
            if not (self.viewport[2] >= (self.width - 1)):
                # Debug
                #print self.viewport[2], self.width, "right"

                # Will viewport be greater than max at next cycle?
                if (self.viewport[2] + speed) > (self.width - 1):
                    speed = self.width - self.viewport[2] - 1

                self.xvel += speed
                object.adjposscr(x=-speed)
        elif object.screenrect[0] < xgoal - pad:
            if not (self.viewport[0] <= 0):
                #Debug
                #print self.viewport[0], 0, "left"

                if (self.viewport[0] - speed) < 0:
                    speed = self.viewport[0]

                self.xvel -= speed
                object.adjposscr(x=speed)

        if object.screenrect[1] > ygoal + pad:
            if not (self.viewport[3] >= self.height - 1):
                #Debug
                #print self.viewport[3], self.height, "down"

                if (self.viewport[3] + speed) > (self.height - 1):
                    speed = self.height - self.viewport[3] - 1
                
                self.yvel += speed
                object.adjposscr(y=-speed)
        elif object.screenrect[1] < ygoal - pad:
            if not (self.viewport[1] <= 0):
                #Debug
                #print self.viewport[1], 0, "up"

                if (self.viewport[1] - speed) < 0:
                    speed = self.viewport[1]

                self.yvel -= speed 
                object.adjposscr(y=speed)

    def update(self):
        """Update viewport position and record last upper left postion"""
        self.xlast = self.viewport[0]
        self.ylast = self.viewport[1]

        self.viewport[0] += self.xvel
        self.viewport[1] += self.yvel
        self.viewport[2] += self.xvel
        self.viewport[3] += self.yvel

        self.xvel = 0
        self.yvel = 0

        if self.viewport[0] < 0:
            self.move(x=-1)
        if self.viewport[1] < 0:
            self.move(y=-1)
        if self.viewport[2] > self.width:
            self.move(x=self.width - 1)
        if self.viewport[3] > self.height:
            self.move(y=self.height - 1)

        for n in self.selected:
            self.highlighttile(n, red=1, add=1)

    def adjustpos(self, x=None, y=None):
        """Adjust position by x or y"""

        if x != None:
            self.viewport[0] += x
            self.viewport[2] += x

        if y != None:
            self.viewport[1] += y
            self.viewport[3] += y

    def move(self, x=None, y=None):
        """Move map to x/y position, if x or y == -1 revert to last position"""
        
        if x == -1:
            self.viewport[0] = self.xlast
            self.viewport[2] = self.viewport[0] + self.viewportwidth
        elif x:
            self.viewport[0] = x
            self.viewport[2] = self.viewport[0] + self.viewportwidth

        if y == -1:
            self.viewport[1] = self.ylast
            self.viewport[3] = self.viewport[1] + self.viewportheight
        elif y:
            self.viewport[1] = y
            self.viewport[3] = self.viewport[1] + self.viewportheight


