예제 #1
0
파일: input.py 프로젝트: gdos/parole
def handleKeyDown(event):
    """
    handleKeyDown(event) -> None
    
    Instructs the input module to handle a PyGame keypress event. This is
    automatically invoked by the engine. Most user code shouldn't need to call
    this function unless it wants to artificially inject keypresses (for a
    game replay, perhaps?), in which case it should be careful to ensure that
    the given event is well formed according to PyGame convention.
    """
    if event.type != pygame.KEYDOWN:
        return
    key, mod = event.key, event.mod

    # ignore lone modifier keypresses
    if key in [
        pygame.K_LALT,
        pygame.K_RALT,
        pygame.K_LCTRL,
        pygame.K_RCTRL,
        pygame.K_LSHIFT,
        pygame.K_RSHIFT,
        pygame.K_LMETA,
        pygame.K_RMETA,
    ]:
        return

    # append a nicely stringified representation of this keypress to the list
    result = keyRepr(key, mod)
    __keypresses.append(result)
    parole.debug("Key down: %s. Queue: %s", result, __keypresses)

    # notify any listeners of a keydown event
    for listener in __downUpListeners:
        listener.handleKeyDown(result)
예제 #2
0
파일: resource.py 프로젝트: gdos/parole
def getResource(name, binary=False):
    """
    Attempts to retrieve the bytes of the given resource. If multiple packages
    contain the resource, the copy in the alphabetically latest package is
    returned. Returns C{None} if the given resource is not known by any package.

    @param name: The path + filename of the resource to retrieve.
    @type name: C{str}
    
    @param binary: If C{True}, then an attempt will be made to open the resource
    in binary mode. This probably only has an effect if the file is in a
    directory package. Resources from archive packages are always binary.
    @type binary: C{bool}
    
    @return: C{str} or C{None}
    """
    # see if we've already loaded this resource
    if name in __resTable:
        return __resTable[name]

    # we need to load it
    parole.info('Loading resource: "%s"', name)
    bytes = None

    # go through all packages until we find one with the resource
    # we need. The packages list should already be in the proper order
    for (package, f, g) in packages:
        try:
            bytes = __getResourceFrom(name, package, binary)
            parole.debug('"%s" found in "%s"', name, package)
            break
        except __NotFound, e:
            parole.debug('"%s" not found in "%s"', name, package)
예제 #3
0
def handleKeyDown(event):
    """
    handleKeyDown(event) -> None
    
    Instructs the input module to handle a PyGame keypress event. This is
    automatically invoked by the engine. Most user code shouldn't need to call
    this function unless it wants to artificially inject keypresses (for a
    game replay, perhaps?), in which case it should be careful to ensure that
    the given event is well formed according to PyGame convention.
    """
    if event.type != pygame.KEYDOWN:
        return
    key, mod = event.key, event.mod

    # ignore lone modifier keypresses
    if key in [
            pygame.K_LALT, pygame.K_RALT, pygame.K_LCTRL, pygame.K_RCTRL,
            pygame.K_LSHIFT, pygame.K_RSHIFT, pygame.K_LMETA, pygame.K_RMETA
    ]:
        return

    # append a nicely stringified representation of this keypress to the list
    result = keyRepr(key, mod)
    __keypresses.append(result)
    parole.debug('Key down: %s. Queue: %s', result, __keypresses)

    # notify any listeners of a keydown event
    for listener in __downUpListeners:
        listener.handleKeyDown(result)
예제 #4
0
파일: input.py 프로젝트: gdos/parole
def handleKeyUp(event):
    """
    handleyKeyUp(event) -> None

    Instructs the input module to handle a PyGame key release event. This is
    automatically invoked by the engine. Most user code shouldn't need to call
    this function unless it wants to artificially inject key releases, in
    which case it should be careful to ensure that the given event is well
    formed according to PyGame convention.
    """
    if event.type != pygame.KEYUP:
        return
    key, mod = event.key, event.mod

    # ignore lone modifier keypresses
    if key in [
        pygame.K_LALT,
        pygame.K_RALT,
        pygame.K_LCTRL,
        pygame.K_RCTRL,
        pygame.K_LSHIFT,
        pygame.K_RSHIFT,
        pygame.K_LMETA,
        pygame.K_RMETA,
    ]:
        return

    result = keyRepr(key, mod)

    parole.debug("Key up: %s", result)

    # notify any listeners of a keyup event
    for listener in __downUpListeners:
        listener.handleKeyUp(result)
예제 #5
0
def handleKeyUp(event):
    """
    handleyKeyUp(event) -> None

    Instructs the input module to handle a PyGame key release event. This is
    automatically invoked by the engine. Most user code shouldn't need to call
    this function unless it wants to artificially inject key releases, in
    which case it should be careful to ensure that the given event is well
    formed according to PyGame convention.
    """
    if event.type != pygame.KEYUP:
        return
    key, mod = event.key, event.mod

    # ignore lone modifier keypresses
    if key in [
            pygame.K_LALT, pygame.K_RALT, pygame.K_LCTRL, pygame.K_RCTRL,
            pygame.K_LSHIFT, pygame.K_RSHIFT, pygame.K_LMETA, pygame.K_RMETA
    ]:
        return

    result = keyRepr(key, mod)

    parole.debug('Key up: %s', result)

    # notify any listeners of a keyup event
    for listener in __downUpListeners:
        listener.handleKeyUp(result)
예제 #6
0
def getResource(name, binary=False):
    """
    Attempts to retrieve the bytes of the given resource. If multiple packages
    contain the resource, the copy in the alphabetically latest package is
    returned. Returns C{None} if the given resource is not known by any package.

    @param name: The path + filename of the resource to retrieve.
    @type name: C{str}
    
    @param binary: If C{True}, then an attempt will be made to open the resource
    in binary mode. This probably only has an effect if the file is in a
    directory package. Resources from archive packages are always binary.
    @type binary: C{bool}
    
    @return: C{str} or C{None}
    """
    # see if we've already loaded this resource
    if name in __resTable:
        return __resTable[name]

    # we need to load it
    parole.info('Loading resource: "%s"', name)
    bytes = None

    # go through all packages until we find one with the resource
    # we need. The packages list should already be in the proper order
    for (package, f, g) in packages:
        try:
            bytes = __getResourceFrom(name, package, binary)
            parole.debug('"%s" found in "%s"', name, package)
            break
        except __NotFound, e:
            parole.debug('"%s" not found in "%s"', name, package)
예제 #7
0
def clearKeyPresses():
    """
    clearKeyPresses() -> None
    
    Causes the input module to disregard any keypresses which have occurred
    since the last time getKeyPresses() was called.
    """
    global __keypresses
    parole.debug('clearKeyPresses()')
    __keypresses = []
예제 #8
0
파일: input.py 프로젝트: gdos/parole
def clearKeyPresses():
    """
    clearKeyPresses() -> None
    
    Causes the input module to disregard any keypresses which have occurred
    since the last time getKeyPresses() was called.
    """
    global __keypresses
    parole.debug("clearKeyPresses()")
    __keypresses = []
예제 #9
0
파일: base.py 프로젝트: gdos/parole
def pushAnimation():
    """
    Call this function to inform Parole that you are beginning an activity
    that requires continuous frame updates. As a matter of convenience, such
    activities are referred to as "animations", but they don't necessarily
    have to animate anything. Call C{popAnimation} when the animation is done.
    """
    global __continuousUpdates
    __continuousUpdates += 1
    parole.debug('Pushing animation; continuousUpdates = %s',
            __continuousUpdates)
예제 #10
0
파일: base.py 프로젝트: puresandwich/parole
def pushAnimation():
    """
    Call this function to inform Parole that you are beginning an activity
    that requires continuous frame updates. As a matter of convenience, such
    activities are referred to as "animations", but they don't necessarily
    have to animate anything. Call C{popAnimation} when the animation is done.
    """
    global __continuousUpdates
    __continuousUpdates += 1
    parole.debug('Pushing animation; continuousUpdates = %s',
                 __continuousUpdates)
예제 #11
0
파일: base.py 프로젝트: gdos/parole
def popAnimation():
    """
    Call this function to inform Parole that an animation has finished and no
    longer needs continuous frame updates. Once all animations have been
    popped, parole will wait for input before updating each frame, rather than
    updating continuously. This significantly reduces CPU overhead.  
    """
    global __continuousUpdates 
    __continuousUpdates = max(0, __continuousUpdates - 1)
    pygame.event.post(pygame.event.Event(GeneralUpdateEvent, {}))
    parole.debug('Popping animation; continuousUpdates = %s',
            __continuousUpdates)
예제 #12
0
파일: base.py 프로젝트: puresandwich/parole
def popAnimation():
    """
    Call this function to inform Parole that an animation has finished and no
    longer needs continuous frame updates. Once all animations have been
    popped, parole will wait for input before updating each frame, rather than
    updating continuously. This significantly reduces CPU overhead.  
    """
    global __continuousUpdates
    __continuousUpdates = max(0, __continuousUpdates - 1)
    pygame.event.post(pygame.event.Event(GeneralUpdateEvent, {}))
    parole.debug('Popping animation; continuousUpdates = %s',
                 __continuousUpdates)
예제 #13
0
    def updateSplash(self):
        global doneSplash, splash
        self.touch()
        now = parole.time()
        progress = (now - self.startTime) / \
                    float(self.endTime - self.startTime)

        if progress <= 0.2:
            return

        if progress <= 0.6:
            for (p, dest) in self.pDests.iteritems():
                start = self.pStarts[p]
                y = start[1] + ((progress - 0.2) /
                                (0.6 - 0.2)) * (dest[1] - start[1])
                x = start[0] + ((progress - 0.2) /
                                (0.6 - 0.2)) * (dest[0] - start[0])
                self.positionOf[p] = (int(x), int(y))
            return
        elif not self.movementDone:
            for (p, dest) in self.pDests.iteritems():
                self.positionOf[p] = dest
            self.movementDone = True
            return

        if progress < 1.0 and self.titlePasses.keys()[0] not in self.passes:
            for p, pos in self.titlePasses.items():
                self.addPass(p, pos=pos)
            return

        if progress > 1.0 and not self.gold:
            for p in self.titlePasses.keys() + self.pDests.keys():
                self.remPass(p)
            self.addPass(shader.TextBlockPass(
                self.font, (255, 215, 0),
                text="Python Advanced ROgueLike Engine"),
                         pos=self.titlePos)
            self.goldSound.play()
            self.gold = True
        elif progress > 1.1 and self.fadeAlpha < 255:
            if self.fader not in self.passes:
                parole.debug("adding fader!")
                self.addPass(self.fader, pos=(0, 0))
            self.fadeAlpha = (now - (self.endTime + \
                0.1*(self.endTime-self.startTime)))/500.0 * 255
            self.fader.rgb = (0, 0, 0, min(255, int(self.fadeAlpha)))
        elif self.fadeAlpha >= 255:
            parole.popAnimation()
            self.clean()
            display.scene.remove(self)
            doneSplash = True
            splash = None
예제 #14
0
파일: splash.py 프로젝트: gdos/parole
    def updateSplash(self):
        global doneSplash, splash
        self.touch()
        now = parole.time()
        progress = (now - self.startTime) / \
                    float(self.endTime - self.startTime)

        if progress <= 0.2:
            return

        if progress <= 0.6:
            for (p, dest) in self.pDests.iteritems():
                start = self.pStarts[p]
                y = start[1] + ((progress-0.2)/(0.6-0.2))*(dest[1]-start[1])
                x = start[0] + ((progress-0.2)/(0.6-0.2))*(dest[0]-start[0])
                self.positionOf[p] = (int(x), int(y))
            return
        elif not self.movementDone:
            for (p, dest) in self.pDests.iteritems():
                self.positionOf[p] = dest
            self.movementDone = True
            return

        if progress < 1.0 and self.titlePasses.keys()[0] not in self.passes:
            for p, pos in self.titlePasses.items():
                self.addPass(p, pos=pos)
            return

        if progress > 1.0 and not self.gold:
            for p in self.titlePasses.keys() + self.pDests.keys():
                self.remPass(p)
            self.addPass(shader.TextBlockPass(self.font, (255,215,0),
                text="Python Advanced ROgueLike Engine"), pos=self.titlePos)
            self.goldSound.play()
            self.gold = True
        elif progress > 1.1 and self.fadeAlpha < 255:
            if self.fader not in self.passes:
                parole.debug("adding fader!")
                self.addPass(self.fader, pos=(0,0))
            self.fadeAlpha = (now - (self.endTime + \
                0.1*(self.endTime-self.startTime)))/500.0 * 255
            self.fader.rgb = (0, 0, 0, min(255, int(self.fadeAlpha)))
        elif self.fadeAlpha >= 255:
            parole.popAnimation()
            self.clean()
            display.scene.remove(self)
            doneSplash = True
            splash = None
예제 #15
0
파일: display.py 프로젝트: gdos/parole
def __onConfigChange(conf):
    global __modeDirty, __clearColor
    for key in __lastModeConfs.keys():
        if conf.display[key] != __lastModeConfs[key]:
            __modeDirty = True
            __lastModeConfs[key] = conf.display[key]
            parole.info("New value for display.%s: %s", key, __lastModeConfs[key])

    pygame.time.set_timer(parole.GeneralUpdateEvent, conf.display.generalUpdateDelay)
    parole.info("General display update delay: %s", conf.display.generalUpdateDelay)

    __clearColor = parseColor(conf.display.clearColor)
    parole.info("Display clear color: %s", __clearColor)

    if __modeDirty:
        parole.debug("Config changed; mode dirty")
예제 #16
0
파일: resource.py 프로젝트: gdos/parole
def getFont(name, size):
    """
    Attempts to retrieve the given font resource as a PyGame Font object.

    @return: C{pygame.font.Font} or C{None}

    @param name: The path + filename of the font resource to retrieve. Must name
    a font file in a format that PyGame can read (e.g., TrueType).
    @type name: C{str}
    """
    # Return any cached copy of the font object
    if (name, size) in __resTable:
        return __resTable[(name, size)]

    parole.info('Loading font "%s" %spt', name, size)

    # name should be a resource whose bytes are loadable by pygame's font
    # module
    bytes = getResource(name, binary=True)
    if not bytes:
        parole.error('"%s" names an empty font resource.', name)
        return None

    parole.debug("Got font bytes. (len=%d)", len(bytes))
    font = None

    # Create a file-like object which pygame can use to read the bytes of
    # the font file
    # pygame 1.9.1release segfaults when reading a font from cStringIO
    # fontf = cStringIO.StringIO(bytes)
    # Workaround:
    import tempfile

    tmpf = tempfile.NamedTemporaryFile(delete=False)
    tmpf.write(bytes)
    tmpf.close()
    fontf = tmpf.name

    # Attempt to load the font
    try:
        parole.debug("Parsing font bytes...")
        font = pygame.font.Font(fontf, size)
    except Exception, e:
        parole.error('Unable to load font "%s" %pt: %s', name, size, e)
        return None
예제 #17
0
파일: input.py 프로젝트: gdos/parole
 def __call__(self, event):
     # we are a ui event handler
     if event.type == pygame.QUIT:
         try:
             command = self.map.system.QUIT
         except AttributeError:
             return
         parole.debug("CommandMap: %s", command)
         self.handler(command)
     else:
         keys = self.peek and peekKeyPresses() or getKeyPresses()
         for keypress in keys:
             try:
                 command = self.map.keypresses[keypress]
             except (AttributeError, KeyError):
                 continue
             parole.debug("CommandMap: %s", command)
             self.handler(command)
예제 #18
0
 def __call__(self, event):
     # we are a ui event handler
     if event.type == pygame.QUIT:
         try:
             command = self.map.system.QUIT
         except AttributeError:
             return
         parole.debug('CommandMap: %s', command)
         self.handler(command)
     else:
         keys = self.peek and peekKeyPresses() or getKeyPresses()
         for keypress in keys:
             try:
                 command = self.map.keypresses[keypress]
             except (AttributeError, KeyError):
                 continue
             parole.debug('CommandMap: %s', command)
             self.handler(command)
예제 #19
0
def getFont(name, size):
    """
    Attempts to retrieve the given font resource as a PyGame Font object.

    @return: C{pygame.font.Font} or C{None}

    @param name: The path + filename of the font resource to retrieve. Must name
    a font file in a format that PyGame can read (e.g., TrueType).
    @type name: C{str}
    """
    # Return any cached copy of the font object
    if (name, size) in __resTable:
        return __resTable[(name, size)]

    parole.info('Loading font "%s" %spt', name, size)

    # name should be a resource whose bytes are loadable by pygame's font
    # module
    bytes = getResource(name, binary=True)
    if not bytes:
        parole.error('"%s" names an empty font resource.', name)
        return None

    parole.debug('Got font bytes. (len=%d)', len(bytes))
    font = None

    # Create a file-like object which pygame can use to read the bytes of
    # the font file
    # pygame 1.9.1release segfaults when reading a font from cStringIO
    #fontf = cStringIO.StringIO(bytes)
    # Workaround:
    import tempfile
    tmpf = tempfile.NamedTemporaryFile(delete=False)
    tmpf.write(bytes)
    tmpf.close()
    fontf = tmpf.name

    # Attempt to load the font
    try:
        parole.debug('Parsing font bytes...')
        font = pygame.font.Font(fontf, size)
    except Exception, e:
        parole.error('Unable to load font "%s" %pt: %s', name, size, e)
        return None
예제 #20
0
def __onConfigChange(conf):
    global __modeDirty, __clearColor
    for key in __lastModeConfs.keys():
        if conf.display[key] != __lastModeConfs[key]:
            __modeDirty = True
            __lastModeConfs[key] = conf.display[key]
            parole.info('New value for display.%s: %s', key,
                        __lastModeConfs[key])

    pygame.time.set_timer(parole.GeneralUpdateEvent,
                          conf.display.generalUpdateDelay)
    parole.info('General display update delay: %s',
                conf.display.generalUpdateDelay)

    __clearColor = parseColor(conf.display.clearColor)
    parole.info('Display clear color: %s', __clearColor)

    if __modeDirty:
        parole.debug('Config changed; mode dirty')
예제 #21
0
파일: display.py 프로젝트: gdos/parole
def __setMode():
    global __modeDirty, __displaySurf, __workSurf, __lastDepth, __clearedSurf
    __modeDirty = False
    resolution = (int(parole.conf.display.width), int(parole.conf.display.height))
    fs = bool(parole.conf.display.fullscreen)
    hw = bool(parole.conf.display.hwaccel)
    depth = parole.conf.display.depth
    if depth not in __validDepths:
        error("Bad value for display.depth: %s. Should be one of %s.", depth, ", ".join(__validDepths))
        depth = __lastDepth
    else:
        __lastDepth = depth

    flags = 0
    if fs:
        flags |= pygame.FULLSCREEN

    if hw:
        flags |= pygame.HWSURFACE
        flags |= pygame.DOUBLEBUF

    parole.debug("Creating display surface...")
    __displaySurf = pygame.display.set_mode(resolution, flags, depth)
    parole.debug("... bingo!")
    # __clearedSurf = pygame.Surface(resolution).convert_alpha()
    # clearSurface(__clearedSurf, __clearedSurf.get_rect())

    if hw:
        __workSurf = pygame.Surface(resolution, pygame.SWSURFACE, __displaySurf)
    else:
        __workSurf = None

    parole.info(
        "New mode: %s %s %sx%sx%s",
        hw and "HW" or "SW",
        fs and "Fullscreen" or "Window",
        resolution[0],
        resolution[1],
        depth,
    )
예제 #22
0
def __setMode():
    global __modeDirty, __displaySurf, __workSurf, __lastDepth, __clearedSurf
    __modeDirty = False
    resolution = (int(parole.conf.display.width),
                  int(parole.conf.display.height))
    fs = bool(parole.conf.display.fullscreen)
    hw = bool(parole.conf.display.hwaccel)
    depth = parole.conf.display.depth
    if depth not in __validDepths:
        error('Bad value for display.depth: %s. Should be one of %s.', depth,
              ', '.join(__validDepths))
        depth = __lastDepth
    else:
        __lastDepth = depth

    flags = 0
    if fs:
        flags |= pygame.FULLSCREEN

    if hw:
        flags |= pygame.HWSURFACE
        flags |= pygame.DOUBLEBUF

    parole.debug('Creating display surface...')
    __displaySurf = pygame.display.set_mode(resolution, flags, depth)
    parole.debug('... bingo!')
    #__clearedSurf = pygame.Surface(resolution).convert_alpha()
    #clearSurface(__clearedSurf, __clearedSurf.get_rect())

    if hw:
        __workSurf = pygame.Surface(resolution, pygame.SWSURFACE,
                                    __displaySurf)
    else:
        __workSurf = None

    parole.info('New mode: %s %s %sx%sx%s', hw and 'HW' or 'SW',
                fs and 'Fullscreen' or 'Window', resolution[0], resolution[1],
                depth)
예제 #23
0
def setMap(map, playerPos, applyLight=True):
    mbox = util.messageBox('Patience...')
    parole.display.update()
    oldMap = data['mapframe'].getMap()
    if oldMap:
        oldMap[player.pos].remove(player)
        if applyLight:
            data['light'].remove(oldMap)
        data['mapframe'].setMap(None)

    map[playerPos].add(player)
    if applyLight:
        data['light'].apply(map, playerPos)
    parole.debug('setting...')
    data['mapframe'].setMap(map)
    parole.debug('set!')
    if data['fov']:
        parole.debug('binding...')
        data['mapframe'].bindVisibilityToFOV(player, 16, remember=data['memory'])
        parole.debug('bound!')
    data['mapframe'].centerAtTile(playerPos)
    parole.debug('updating...')
    map.update()
    parole.debug('updated!')
    parole.display.scene.remove(mbox)
    data['currentMap'] = map
    parole.debug('leaving setMap')
예제 #24
0
파일: resource.py 프로젝트: gdos/parole
    tmpf.write(bytes)
    tmpf.close()
    fontf = tmpf.name

    # Attempt to load the font
    try:
        parole.debug("Parsing font bytes...")
        font = pygame.font.Font(fontf, size)
    except Exception, e:
        parole.error('Unable to load font "%s" %pt: %s', name, size, e)
        return None

    # Store the font object in the resource table, rather
    # than the actual bytes of the font file
    __resTable[(name, size)] = font
    parole.debug('Font "%s" loaded.', name)
    return font


# ==============================================================================


def getShader(scriptName):
    """
    Attempts to retrieve a L{Shader} object resource. 
    
    @param scriptName: 
    Should name a resource that is a python script. The script should, upon
    execution, create a global (to itself) object C{theShader}, which is
    expected to be an instance of L{Shader}. This object is what is retrieved
    and cached by this function.
예제 #25
0
    tmpf.write(bytes)
    tmpf.close()
    fontf = tmpf.name

    # Attempt to load the font
    try:
        parole.debug('Parsing font bytes...')
        font = pygame.font.Font(fontf, size)
    except Exception, e:
        parole.error('Unable to load font "%s" %pt: %s', name, size, e)
        return None

    # Store the font object in the resource table, rather
    # than the actual bytes of the font file
    __resTable[(name, size)] = font
    parole.debug('Font "%s" loaded.', name)
    return font


#==============================================================================


def getShader(scriptName):
    """
    Attempts to retrieve a L{Shader} object resource. 
    
    @param scriptName: 
    Should name a resource that is a python script. The script should, upon
    execution, create a global (to itself) object C{theShader}, which is
    expected to be an instance of L{Shader}. This object is what is retrieved
    and cached by this function.
예제 #26
0
def handleWalk(command):
    global lookAnnote, player, zapping
    if not player:
        return
    map = data['mapframe'].getMap()
    frame = data['mapframe']

    displacement = (0,0)
    moveTree = 'tree' in command
    if moveTree and not data['tree']:
        return

    if command in ['north','treenorth']:
        displacement = (0,-1)
    elif command in ['south', 'treesouth']:
        displacement = (0, 1)
    elif command in ['east', 'treeeast']:
        displacement = (1, 0)
    elif command in ['west', 'treewest']:
        displacement = (-1, 0)
    elif command in ['northeast', 'treenortheast']:
        displacement = (1,-1)
    elif command in ['northwest', 'treenorthwest']:
        displacement = (-1, -1)
    elif command in ['southeast', 'treesoutheast']:
        displacement = (1, 1)
    elif command in ['southwest', 'treesouthwest']:
        displacement = (-1,1)
    elif command == 'more ambient':
        map.setAmbientLight(map.ambientRGB, min(1., 
                                                map.ambientIntensity + 0.05))
        map.update()
        return
    elif command == 'less ambient':
        map.setAmbientLight(map.ambientRGB, max(0., 
                                                map.ambientIntensity - 0.05))
        map.update()
        return
    elif command in ('examine', 'zap'):
        if lookAnnote:
            if zapping:
                zapping = False
                zapPos = (lookAnnote.tile.col, lookAnnote.tile.row)
                if map.traceLOS(player.pos, zapPos, remAimOverlay) is \
                        map[zapPos]:
                    message('You zap that space into oblivion!')
                    map[zapPos].clear()
                else:
                    message('You need line-of-sight to zap!')

            frame.removeAnnotation(lookAnnote)
            lookAnnote = None
        else:
            playerTile = map[player.pos]
            if command == 'zap':
                zapping = True
                lookAnnote = frame.annotate(playerTile,
                    'Zap: %s.' % ', '.join([str(x) for x in playerTile]),
                    lineRGB=(64,32,255), reticleRGB=(64,32,255))
            else:
                lookAnnote = frame.annotate(playerTile,
                    'You see: %s.' % ', '.join([str(x) for x in \
                        playerTile]))
        return
    elif command == 'save':
        mbox = util.messageBox('Saving...')
        parole.display.update()
        time = parole.time()
        data['mapframe'].setMap(None)
        f = bz2.BZ2File('mapsave.sav', 'w')
        saveData = (data['outdoors'], data['dungeon'], data['dungeonStairs'],
                data['outdoorsStairs'], data['currentMap'], player,
                data['light'], data['tree'])
        cPickle.dump(saveData, f, protocol=-1)
        f.close()
        data['mapframe'].setMap(map)
        if data['fov']:
            data['mapframe'].bindVisibilityToFOV(player, 16,
            remember=data['memory'])
        time = (parole.time() - time) or 1
        parole.info('Map save time: %dms', time)
        parole.display.scene.remove(mbox)
        return

    elif command == 'restore':
        mbox = util.messageBox('Restoring...')
        parole.display.update()
        
        if lookAnnote:
            data['mapframe'].removeAnnotation(lookAnnote)
            lookAnnote = None
        data['mapframe'].setMap(None)
        time = parole.time()
        f = bz2.BZ2File('mapsave.sav', 'r')
        (data['outdoors'], data['dungeon'], data['dungeonStairs'],
            data['outdoorsStars'], data['currentMap'], player, data['light'],
            data['tree']) = cPickle.load(f)
        f.close()

        time = (parole.time() - time) or 1
        parole.info('Map restore time: %dms', time)
        parole.display.scene.remove(mbox)
        parole.display.update()

        setMap(data['currentMap'], player.pos, False)
        parole.debug('leaving restore')
        return

    elif command == 'toggle fov':
        mbox = util.messageBox('Patience...')
        parole.display.update()
        if data['fov']:
            data['mapframe'].bindVisibilityToFOV(None, None)
        elif player:
            data['mapframe'].bindVisibilityToFOV(player, 16,
                    remember=data['memory'])
        data['fov'] = not data['fov']
        parole.display.scene.remove(mbox)
        return
    elif command == 'down':
        for obj in map[player.pos]:
            if obj.name == 'a stairway leading down':
                message('You climb down.')
                if not data['dungeon']:
                    dungeon, playerPos = mapgen.makeDungeonMap(data)
                    data['dungeon'] = dungeon
                    data['dungeonStairs'] = playerPos
                setMap(data['dungeon'], data['dungeonStairs'])
                return
        message('There are no downward stairs here.')
        return
    elif command == 'up':
        for obj in map[player.pos]:
            if obj.name == 'a stairway leading up':
                message('You climb up.')
                setMap(data['outdoors'], data['outdoorsStairs'])
                return
        message('There are no upward stairs here.')
        return
    elif command == 'toggle memory':
        if data['fov'] and player:
            mbox = util.messageBox('Patience...')
            parole.display.update()
            data['memory'] = not data['memory']
            data['mapframe'].bindVisibilityToFOV(player, 16,
                    remember=data['memory'])
            parole.display.scene.remove(mbox)
            message('Memory %s.' % (data['memory'] and 'on' or 'off',))
        return
        
    if data['msg3Shader'].text:
        message('')
    curPos = moveTree and data['tree'].pos or player.pos
    if not moveTree and lookAnnote:
        curPos = (lookAnnote.tile.col, lookAnnote.tile.row)
    newPos = (curPos[0] + displacement[0], curPos[1] + displacement[1])

    if newPos[0] < 0 or newPos[1] < 0 or newPos[0] >= map.cols or \
            newPos[1] >= map.rows:
        return

    if lookAnnote and not moveTree:
        frame.removeAnnotation(lookAnnote)
        lookTile = map[newPos]
        if zapping:
            map.traceLOS(player.pos, curPos, remAimOverlay)
            if map.traceLOS(player.pos, newPos, addAimOverlay) is \
                    lookTile:
                lookAnnote = frame.annotate(lookTile,
                    'Zap: %s.' % ', '.join([str(x) for x in lookTile]),
                    lineRGB=(64,32,255), reticleRGB=(64,32,255))
            else:
                lookAnnote = frame.annotate(lookTile,
                    'Not in LOS.',
                    lineRGB=(64,32,255), reticleRGB=(64,32,255))
        else:
            if data['mapframe'].inFOV(lookTile):
                lookAnnote = frame.annotate(lookTile,
                    'You see: %s.' % ', '.join([str(x) for x in lookTile]))
            else:
                lookAnnote = frame.annotate(lookTile,
                    'You cannot see here.')

        return

    for obj in map[newPos]:
        if obj.blocker:
            message('Your way is blocked by %s.' % obj)
            return
        if obj.passer:
            message('You pass by %s.' % obj)


    map[curPos].remove(moveTree and data['tree'] or player)
    #data['light'].remove(map)
    #data['light'].rgb = random.choice([colors['Orange'],
    #    colors['Chocolate'], colors['Coral'], colors['Yellow'],
    #    colors['Pink']])
    map[newPos].add(moveTree and data['tree'] or player)
    #data['light'].apply(map, player.pos)

    data['mapframe'].centerAtTile(newPos)

    # This works, but is too slow without some sort of pre-caching and/or
    # optimization: animated water (each application uses random new
    # perlin Z).
    #map.applyGenerator(data['waterGenerator'], 
    #        pygame.Rect((25, 35), (20, 20)))

    map.update()