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)
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)
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)
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)
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)
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 = []
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 = []
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)
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)
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
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
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")
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
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)
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)
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
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')
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, )
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)
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')
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.
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.
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()