class CreditsState(FSMState): ''' Controls the state of the game while displaying the credits page. ''' def __init__(self, gameRef=None): FSMState.__init__(self, gameRef, PanoConstants.STATE_CREDITS) self.log = logging.getLogger('pano.creditsState') self.alphaMask = None self.tex = None # scene root self.scene = None # parent node of self.creditsOST self.textParent = None # filename of the background image self.background = None # OnscreenImage used for background rendering self.backgroundOSI = None # name of the font definition to use self.fontName = None self.fontColor = None # scroll speed in pixels / s self.scrollSpeed = 1.0 # the duration of credits self.scrollTime = 0.0 self.credits = None # OnscreenText object for rendering the credits self.creditsOST = None # positional interval used to scroll the texts self.scrollInterval = None self.camera = None self.camNP = None self.displayRegion = None self.playlist = None def _configure(self): cfg = self.game.getConfig() self.background = cfg.get(PanoConstants.CVAR_CREDITS_BACKGROUND) self.fontName = cfg.get(PanoConstants.CVAR_CREDITS_FONT) self.fontColor = cfg.getVec4(PanoConstants.CVAR_CREDITS_FONT_COLOR) self.fontScale = cfg.getFloat(PanoConstants.CVAR_CREDITS_FONT_SCALE) self.scrollSpeed = cfg.getFloat( PanoConstants.CVAR_CREDITS_SCROLL_SPEED) self.playlist = cfg.get(PanoConstants.CVAR_CREDITS_MUSIC) creditsFile = cfg.get(PanoConstants.CVAR_CREDITS_TEXT_FILE) self.credits = self.game.getResources().loadText(creditsFile) def enter(self): FSMState.enter(self) self.game.getView().fadeOut(1.0) self._configure() globalClock.setMode(ClockObject.MSlave) # create alpha mask that fades the top and bottom img = PNMImage(8, 8) img.addAlpha() img.fill(1) img.alphaFill(1) for x in range(img.getXSize()): for y in range(img.getYSize()): img.setAlpha( x, y, -.2 + 2.5 * math.sin(math.pi * y / img.getYSize())) self.alphaMask = img # create texture for applying the alpha mask self.tex = Texture() self.tex.load(self.alphaMask) self.tex.setWrapU(Texture.WMClamp) self.tex.setWrapV(Texture.WMClamp) # create scenegraph self.scene = NodePath('credits_scene_root') self.scene.setDepthTest(False) self.scene.setDepthWrite(False) self.textParent = self.scene.attachNewNode('text_parent') ts = TextureStage('') self.textParent.setTexGen(ts, TexGenAttrib.MWorldPosition) self.textParent.setTexScale(ts, .5, .5) self.textParent.setTexOffset(ts, .5, .5) self.textParent.setTexture(ts, self.tex) # background self.backgroundOSI = OnscreenImage( self.game.getResources().getResourceFullPath( PanoConstants.RES_TYPE_TEXTURES, self.background), parent=render2d) # create credits text i18n = self.game.getI18n() localizedFont = i18n.getLocalizedFont(self.fontName) fontPath = self.game.getResources().getResourceFullPath( PanoConstants.RES_TYPE_FONTS, localizedFont) fontRes = loader.loadFont(fontPath) self.creditsOST = TextNode('creditsTextNode') crNP = self.textParent.attachNewNode(self.creditsOST) crNP.setScale(self.fontScale * PandaUtil.getFontPixelPerfectScale(fontRes)) self.creditsOST.setFont(fontRes) self.creditsOST.setEncoding(TextEncoder.EUtf8) self.creditsOST.setText(self.credits) self.creditsOST.setTextColor( Vec4(self.fontColor[0], self.fontColor[1], self.fontColor[2], self.fontColor[3])) self.creditsOST.setAlign(TextNode.ACenter) crNP.setP(-90) crNP.flattenLight() b3 = self.textParent.getTightBounds() dims = b3[1] - b3[0] pixels = PandaUtil.unitsToPixelsY(dims[1] + 2.0 - 1.05) self.scrollTime = pixels / self.scrollSpeed self.log.debug('time to scroll: %f' % self.scrollTime) self.scrollInterval = LerpPosInterval(self.textParent, self.scrollTime, Point3(0, dims[1] + 2, 0), Point3(0, -1.05, 0)) # render scene through an orthographic camera self.camera = Camera('credits_camera') lens = OrthographicLens() lens.setFilmSize(2, 2) lens.setNearFar(-1000, 1000) self.camera.setLens(lens) self.camNP = NodePath(self.camera) self.camNP.reparentTo(self.scene) self.camNP.setP(-90) self.displayRegion = self.game.getView().getWindow().makeDisplayRegion( ) self.displayRegion.setClearColor(Vec4(0, 0, 0, 1)) self.displayRegion.setCamera(self.camNP) self.displayRegion.setSort(20) self.game.getInput().addMappings('credits') self.game.getView().fadeIn(.5) self.scrollInterval.start() globalClock.setMode(ClockObject.MNormal) if self.playlist is not None: self.game.getMusic().setPlaylist( self.game.getResources().loadPlaylist(self.playlist)) self.game.getMusic().play() def update(self, millis): self.scrollTime -= (millis / 1000.0) if self.scrollInterval is not None and not self.scrollInterval.isPlaying( ): self.log.debug('starting scroll interval') self.scrollInterval.start() if self.scrollInterval is None or self.scrollTime < 0: #self.scrollInterval.isStopped(): self.log.debug(self.scrollInterval) self.log.debug('stopping scrollInterval') self.game.quit() # self.game.getState().scheduleStateChange(PanoConstants.STATE_EXIT) def exit(self): FSMState.exit(self) if self.playlist is not None: self.game.getMusic().stop() self.game.getInput().removeMappings('credits') if self.displayRegion is not None: base.win.removeDisplayRegion(self.displayRegion) self.displayRegion = None self.camera = None if self.scrollInterval is not None: self.scrollInterval.finish() self.scrollInterval = None self.backgroundOSI.destroy() self.backgroundOSI = None self.creditsOST = None self.scene.removeNode() self.scene = None self.tex = None self.alphaMask = None def onInputAction(self, action): if action == "interrupt": self.log.debug('credits display interrupted') if self.scrollInterval is not None: self.scrollInterval.finish() self.scrollTime = -1.0 return True else: return False def allowPausing(self): return False