def createSprite(self, sprite): ''' Creates a new sprite and returns a SpriteRenderInterface for controlling this sprite instance. The newly created sprite is located at (0,0) in screen coordinates its dimensions are specified by the given Sprite argument. @param sprite: A Sprite resource that describes the properties of the sprite to created. @return: A SpriteRenderInterface instance or None if it failed to create the sprite. ''' if sprite.eggFile is not None: spriteNP = self.resources.loadModel(sprite.eggFile) tex = None else: if sprite.video is not None: card = self.cardMaker.generate() tex = VideoPlayer.renderToTexture(self.resources, video=sprite.video, audio=sprite.audio) elif sprite.image is not None: card = self.cardMaker.generate() tex = self.resources.loadTexture(sprite.image) sprite.width = tex.getXSize() sprite.height = tex.getYSize() else: self.log.error('Could not determine type for sprite: %s' % sprite.name) return None spriteNP = NodePath(card) spriteNP.setTexture(tex) nodeName = self.getSpriteNodeName(sprite.name) spriteNP.setName(nodeName) spriteNP.setPos(0, 1, 0) spriteNP.setScale(sprite.width, 1.0, sprite.height) spriteNP.setTransparency(1) spriteNP.reparentTo(self.sprite2d) spriteNP.setDepthTest(False) spriteNP.setDepthWrite(False) spriteNP.setBin("fixed", PanoConstants.RENDER_ORDER_SPRITES) spriteNP.setPythonTag('sprite', sprite) if sprite.video is not None: spriteNP.setPythonTag('video', tex) tex.setLoop(True) tex.play() return SpriteRenderInterface(spriteNP)
class MinigameHood: def __init__(self, cr): self.cr = cr self.isLoaded = 0 self.dnaStore = DNAStorage() def createHood(self, loadStorage=1): if loadStorage: loadDNAFile(self.dnaStore, "phase_13/dna/storage_party_sz.dna") self.node = loadDNAFile(self.dnaStore, "phase_13/dna/party_sz.dna") if self.node.getNumParents() == 1: self.geom = NodePath(self.node.getParent(0)) self.geom.reparentTo(hidden) else: self.geom = hidden.attachNewNode(self.node) gsg = base.win.getGsg() if gsg: self.geom.prepareScene(gsg) self.geom.setName('minigames') base.hoodBGM = base.loadMusic( "phase_13/audio/bgm/party_original_theme.ogg") base.hoodBGM.setVolume(0.7) base.hoodBGM.setLoop(True) base.hoodBGM.play() self.sky = loader.loadModel("phase_3.5/models/props/TT_sky.bam") self.sky.reparentTo(self.geom) self.sky.setPos(9.15527e-005, -1.90735e-006, 2.6226e-006) self.sky.setH(-90) self.skyUtil = SkyUtil() self.skyUtil.startSky(self.sky) self.geom.reparentTo(render) self.isLoaded = 1 messenger.send("loadedHood") def unloadHood(self): self.isLoaded = 0 self.skyUtil.stopSky() self.geom.removeNode() base.hoodBGM.stop()
def createHotspotCollisionGeom(self, hp): ''' Creates a collision box that covers the hotspot's interactive area in order to be able to detect a hotspot by raycasting from the mouse position. ''' dim = self.getFaceTextureDimensions(hp.face) # get world space coords of top left corner t1 = hp.xo / dim[0] t2 = hp.yo / dim[1] topLeft = self.getWorldPointFromFacePoint(hp.face, (t1, t2)) # get world space coords of top right corner t1 = hp.xe / dim[0] t2 = hp.yo / dim[1] topRight = self.getWorldPointFromFacePoint(hp.face, (t1, t2)) # get world space coords of bottom right corner t1 = hp.xe / dim[0] t2 = hp.ye / dim[1] bottomRight = self.getWorldPointFromFacePoint(hp.face, (t1, t2)) # get world space coords of bottom left corner t1 = hp.xo / dim[0] t2 = hp.ye / dim[1] bottomLeft = self.getWorldPointFromFacePoint(hp.face, (t1, t2)) # polygon = CollisionPolygon(topLeft, bottomLeft, bottomRight, topRight) polygon = CollisionPolygon(topLeft, topRight, bottomRight, bottomLeft) # name it as the hotspot, it will be convenient for raycasting cn = CollisionNode(hp.name) cn.addSolid(polygon) collisionNP = NodePath(cn) collisionNP.setName(hp.name) collisionNP.reparentTo(self.collisionsGeomsParent) if not hp.active: collisionNP.hide() else: collisionNP.show()
def _applyShaderInputs(self): ''' Applies the specified texture, vector and matrix shader inputs. Note: Matrix inputs are applied to a shader through a NodePath whose local transformation matches the matrix. ''' for param, texName in self.texParams.items(): tex = self.game.getResources().loadTexture(texName) if tex is not None: tex.setWrapU(Texture.WMRepeat) tex.setWrapV(Texture.WMRepeat) self.screenQuad.setShaderInput(param, tex) else: self.log.error( 'Failed to set shader input %s because the texture was not found' % texName) for param, val in self.floatParams.items(): self.screenQuad.setShaderInput( param, Vec4(val[0], val[1], val[2], val[3])) if self.nodesRoot is None: self.nodesRoot = render.attachNewNode('k_matrices') for param, val in self.matParams.items(): np = NodePath() np.setName(param + '_transform') mat = Mat4() for i in range(4): mat.setRow( i, Vec4(val[i * 4 + 1], val[i * 4 + 2], val[i * 4 + 3], val[i * 4 + 4])) np.setMat(mat) np.reparentTo(self.nodesRoot) self.screenQuad.setShaderInput(param, np) self._applyPredefinedInputs()
class DistributedPartyActivity(DistributedObject.DistributedObject): deferFor = 1 def __init__(self, cr, activityId, activityType, wantLever=False, wantRewardGui=False): DistributedObject.DistributedObject.__init__(self, cr) self.activityId = activityId self.activityName = PartyGlobals.ActivityIds.getString(self.activityId) self.activityType = activityType self.wantLever = wantLever self.wantRewardGui = wantRewardGui self.messageGui = None self.rewardGui = None self.toonIds = [] self._toonId2ror = {} childName = '%s' % self childName = childName[childName.rfind('.DistributedParty') + len('.DistributedParty'):childName. rfind('Activity instance')] if not hasattr(base, 'partyActivityDict'): base.partyActivityDict = {} base.partyActivityDict[childName] = self self.root = NodePath('root') self.rulesDoneEvent = 'rulesDone' self.modelCount = 500 self.cleanupActions = [] self.usesSmoothing = 0 self.usesLookAround = 0 self.difficultyOverride = None self.trolleyZoneOverride = None self._localToonRequestStatus = None #self.root.setPos(self.x, self.y, self.z) return def localToonExiting(self): self._localToonRequestStatus = PartyGlobals.ActivityRequestStatus.Exiting def localToonJoining(self): self._localToonRequestStatus = PartyGlobals.ActivityRequestStatus.Joining def d_toonJoinRequest(self): if self._localToonRequestStatus is None: self.localToonJoining() self.sendUpdate('toonJoinRequest') return def d_toonExitRequest(self): if self._localToonRequestStatus is None: self.localToonExiting() self.sendUpdate('toonExitRequest') return def d_toonExitDemand(self): self.localToonExiting() self.sendUpdate('toonExitDemand') def joinRequestDenied(self, reason): self._localToonRequestStatus = None return def exitRequestDenied(self, reason): self._localToonRequestStatus = None return def handleToonJoined(self, toonId): self.notify.error('BASE: handleToonJoined should be overridden %s' % self.activityName) def handleToonExited(self, toonId): self.notify.error('BASE: handleToonExited should be overridden %s' % self.activityName) def handleToonDisabled(self, toonId): self.notify.error('BASE: handleToonDisabled should be overridden %s' % self.activityName) def setToonsPlaying(self, toonIds): exitedToons, joinedToons = self.getToonsPlayingChanges( self.toonIds, toonIds) self.setToonIds(toonIds) self._processExitedToons(exitedToons) self._processJoinedToons(joinedToons) def _processExitedToons(self, exitedToons): for toonId in exitedToons: if toonId != base.localAvatar.doId or toonId == base.localAvatar.doId and self.isLocalToonRequestStatus( PartyGlobals.ActivityRequestStatus.Exiting): toon = self.getAvatar(toonId) if toon is not None: self.ignore(toon.uniqueName('disable')) self.handleToonExited(toonId) if toonId == base.localAvatar.doId: self._localToonRequestStatus = None if toonId in self._toonId2ror: self.cr.relatedObjectMgr.abortRequest( self._toonId2ror[toonId]) del self._toonId2ror[toonId] return def _processJoinedToons(self, joinedToons): for toonId in joinedToons: if toonId != base.localAvatar.doId or toonId == base.localAvatar.doId and self.isLocalToonRequestStatus( PartyGlobals.ActivityRequestStatus.Joining): if toonId not in self._toonId2ror: request = self.cr.relatedObjectMgr.requestObjects( [toonId], allCallback=self._handlePlayerPresent) if toonId in self._toonId2ror: del self._toonId2ror[toonId] else: self._toonId2ror[toonId] = request def _handlePlayerPresent(self, toons): toon = toons[0] toonId = toon.doId if toonId in self._toonId2ror: del self._toonId2ror[toonId] else: self._toonId2ror[toonId] = None self._enableHandleToonDisabled(toonId) self.handleToonJoined(toonId) if toonId == base.localAvatar.doId: self._localToonRequestStatus = None return def _enableHandleToonDisabled(self, toonId): toon = self.getAvatar(toonId) if toon is not None: self.acceptOnce(toon.uniqueName('disable'), self.handleToonDisabled, [toonId]) else: self.notify.warning( 'BASE: unable to get handle to toon with toonId:%d. Hook for handleToonDisabled not set.' % toonId) return def isLocalToonRequestStatus(self, requestStatus): return self._localToonRequestStatus == requestStatus def setToonIds(self, toonIds): self.toonIds = toonIds def getToonsPlayingChanges(self, oldToonIds, newToonIds): oldToons = set(oldToonIds) newToons = set(newToonIds) exitedToons = oldToons.difference(newToons) joinedToons = newToons.difference(oldToons) return (list(exitedToons), list(joinedToons)) def setUsesSmoothing(self): self.usesSmoothing = True def setUsesLookAround(self): self.usesLookAround = True def getInstructions(self): return TTLocalizer.DefaultPartyActivityInstructions def getParentNodePath(self): if hasattr(base.cr.playGame, 'hood') and base.cr.playGame.hood and hasattr( base.cr.playGame.hood, 'loader') and base.cr.playGame.hood.loader and hasattr( base.cr.playGame.hood.loader, 'geom') and base.cr.playGame.hood.loader.geom: return base.cr.playGame.hood.loader.geom else: self.notify.warning( 'Hood or loader not created, defaulting to render') return render def __createRandomNumGen(self): self.notify.debug('BASE: self.doId=0x%08X' % self.doId) self.randomNumGen = RandomNumGen.RandomNumGen(self.doId) def destroy(self=self): self.notify.debug('BASE: destroying random num gen') del self.randomNumGen self.cleanupActions.append(destroy) def generate(self): DistributedObject.DistributedObject.generate(self) self.notify.debug('BASE: generate, %s' % self.getTitle()) self.__createRandomNumGen() def announceGenerate(self): DistributedObject.DistributedObject.announceGenerate(self) self.notify.debug('BASE: announceGenerate %s' % self.activityName) self.root.setName(self.activityName + 'Root') centeredX, centeredY = getCenterPosFromGridSize( self.x, self.y, PartyGlobals.ActivityInformationDict[self.activityId]['gridsize']) self.root.setPos(centeredX, centeredY, 0.0) self.root.setH(self.h) self.root.setZ(self.z) self.normalExit = True if self.wantLever: self.leverTriggerEvent = self.uniqueName('leverTriggerEvent') self.load() def cleanup(self=self): self.notify.debug('BASE: cleanup: normalExit=%s' % self.normalExit) base.cr.renderFrame() if self.normalExit: self.sendUpdate('toonExitRequest') self.cleanupActions.append(cleanup) def disable(self): self.notify.debug('BASE: disable') DistributedObject.DistributedObject.disable(self) rorToonIds = self._toonId2ror.keys() for toonId in rorToonIds: self.cr.relatedObjectMgr.abortRequest(self._toonId2ror[toonId]) del self._toonId2ror[toonId] self.ignore(self.messageDoneEvent) if self.messageGui is not None and not self.messageGui.isEmpty(): self.messageGui.cleanup() self.messageGui = None return def delete(self): self.notify.debug('BASE: delete') self.unload() self.ignoreAll() DistributedObject.DistributedObject.delete(self) def load(self): self.notify.debug('BASE: load') self.loadSign() if self.wantLever: self.loadLever() if self.wantRewardGui: self.showRewardDoneEvent = self.uniqueName('showRewardDoneEvent') self.rewardGui = JellybeanRewardGui(self.showRewardDoneEvent) self.messageDoneEvent = self.uniqueName('messageDoneEvent') self.root.reparentTo(self.getParentNodePath()) self._enableCollisions() def loadSign(self): actNameForSign = self.activityName if self.activityId == PartyGlobals.ActivityIds.PartyJukebox40: actNameForSign = PartyGlobals.ActivityIds.getString( PartyGlobals.ActivityIds.PartyJukebox) elif self.activityId == PartyGlobals.ActivityIds.PartyDance20: actNameForSign = PartyGlobals.ActivityIds.getString( PartyGlobals.ActivityIds.PartyDance) self.sign = self.root.attachNewNode('%sSign' % self.activityName) self.signModel = self.party.defaultSignModel.copyTo(self.sign) self.signFlat = self.signModel.find('**/sign_flat') self.signFlatWithNote = self.signModel.find('**/sign_withNote') self.signTextLocator = self.signModel.find('**/signText_locator') textureNodePath = getPartyActivityIcon(self.party.activityIconsModel, actNameForSign) textureNodePath.setPos(0.0, -0.02, 2.2) textureNodePath.setScale(2.35) textureNodePath.copyTo(self.signFlat) textureNodePath.copyTo(self.signFlatWithNote) text = TextNode('noteText') text.setTextColor(0.2, 0.1, 0.7, 1.0) text.setAlign(TextNode.ACenter) text.setFont(OTPGlobals.getInterfaceFont()) text.setWordwrap(10.0) text.setText('') self.noteText = self.signFlatWithNote.attachNewNode(text) self.noteText.setPosHpr(self.signTextLocator, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0) self.noteText.setScale(0.2) self.signFlatWithNote.stash() self.signTextLocator.stash() def loadLever(self): self.lever = self.root.attachNewNode('%sLever' % self.activityName) self.leverModel = self.party.defaultLeverModel.copyTo(self.lever) self.controlColumn = NodePath('cc') column = self.leverModel.find('**/column') column.getChildren().reparentTo(self.controlColumn) self.controlColumn.reparentTo(column) self.stickHinge = self.controlColumn.attachNewNode('stickHinge') self.stick = self.party.defaultStickModel.copyTo(self.stickHinge) self.stickHinge.setHpr(0.0, 90.0, 0.0) self.stick.setHpr(0, -90.0, 0) self.stick.flattenLight() self.bottom = self.leverModel.find('**/bottom') self.bottom.wrtReparentTo(self.controlColumn) self.bottomPos = self.bottom.getPos() cs = CollisionSphere(0.0, 1.35, 2.0, 1.0) cs.setTangible(False) cn = CollisionNode(self.leverTriggerEvent) cn.addSolid(cs) cn.setIntoCollideMask(OTPGlobals.WallBitmask) self.leverTrigger = self.root.attachNewNode(cn) self.leverTrigger.reparentTo(self.lever) self.leverTrigger.stash() cs = CollisionTube(0.0, 2.7, 0.0, 0.0, 2.7, 3.0, 1.2) cn = CollisionNode('levertube') cn.addSolid(cs) cn.setIntoCollideMask(OTPGlobals.WallBitmask) self.leverTube = self.leverModel.attachNewNode(cn) host = base.cr.doId2do.get(self.party.partyInfo.hostId) if host is None: self.notify.debug( '%s loadLever : Host has left the game before lever could be created.' % self.activityName) return scale = host.getGeomNode().getChild(0).getSz(render) self.leverModel.setScale(scale) self.controlColumn.setPos(0, 0, 0) host.setPosHpr(self.lever, 0, 0, 0, 0, 0, 0) host.pose('leverNeutral', 0) host.update() pos = host.rightHand.getPos(self.controlColumn) self.controlColumn.setPos(pos[0], pos[1], pos[2] - 1) self.bottom.setZ(host, 0.0) self.bottom.setPos(self.bottomPos[0], self.bottomPos[1], self.bottom.getZ()) lookAtPoint = Point3(0.3, 0, 0.1) lookAtUp = Vec3(0, -1, 0) self.stickHinge.lookAt(host.rightHand, lookAtPoint, lookAtUp) host.play('walk') host.update() return def unloadLever(self): self.lever.removeNode() self.leverModel.removeNode() self.controlColumn.removeNode() self.stickHinge.removeNode() self.stick.removeNode() self.bottom.removeNode() self.leverTrigger.removeNode() self.leverTube.removeNode() del self.bottomPos del self.lever del self.leverModel del self.controlColumn del self.stickHinge del self.stick del self.bottom del self.leverTrigger del self.leverTube def _enableCollisions(self): if self.wantLever: self.leverTrigger.unstash() self.accept('enter%s' % self.leverTriggerEvent, self._leverPulled) def _disableCollisions(self): if self.wantLever: self.leverTrigger.stash() self.ignore('enter%s' % self.leverTriggerEvent) def _leverPulled(self, collEntry): self.notify.debug('_leverPulled : Someone pulled the lever!!! ') if self.activityType == PartyGlobals.ActivityTypes.HostInitiated and base.localAvatar.doId != self.party.partyInfo.hostId: return False return True def getToonPullingLeverInterval(self, toon): walkTime = 0.2 reach = ActorInterval(toon, 'leverReach', playRate=2.0) pull = ActorInterval(toon, 'leverPull', startFrame=6) origPos = toon.getPos(render) origHpr = toon.getHpr(render) newPos = self.lever.getPos(render) newHpr = self.lever.getHpr(render) origHpr.setX(PythonUtil.fitSrcAngle2Dest(origHpr[0], newHpr[0])) toon.setPosHpr(origPos, origHpr) reachAndPull = Sequence( ActorInterval(toon, 'walk', loop=True, duration=walkTime - reach.getDuration()), reach, pull) leverSeq = Sequence( Wait(walkTime + reach.getDuration() - 0.1), self.stick.hprInterval(0.55, Point3(0.0, 25.0, 0.0), Point3(0.0, 0.0, 0.0)), Wait(0.3), self.stick.hprInterval(0.4, Point3(0.0, 0.0, 0.0), Point3(0.0, 25.0, 0.0))) returnSeq = Sequence( Parallel(toon.posInterval(walkTime, newPos, origPos), toon.hprInterval(walkTime, newHpr, origHpr), leverSeq, reachAndPull)) return returnSeq def showMessage(self, message, endState='walk'): base.cr.playGame.getPlace().fsm.request('activity') self.acceptOnce(self.messageDoneEvent, self.__handleMessageDone) self.messageGui = TTDialog.TTGlobalDialog( doneEvent=self.messageDoneEvent, message=message, style=TTDialog.Acknowledge) self.messageGui.endState = endState def __handleMessageDone(self): self.ignore(self.messageDoneEvent) if hasattr(base.cr.playGame.getPlace(), 'fsm'): if self.messageGui and hasattr(self.messageGui, 'endState'): self.notify.info('__handleMessageDone (endState=%s)' % self.messageGui.endState) base.cr.playGame.getPlace().fsm.request( self.messageGui.endState) else: self.notify.warning( "messageGui has no endState, defaulting to 'walk'") base.cr.playGame.getPlace().fsm.request('walk') if self.messageGui is not None and not self.messageGui.isEmpty(): self.messageGui.cleanup() self.messageGui = None return def showJellybeanReward(self, earnedAmount, jarAmount, message): if not self.isLocalToonInActivity( ) or base.localAvatar.doId in self.getToonIdsAsList(): messenger.send('DistributedPartyActivity-showJellybeanReward') base.cr.playGame.getPlace().fsm.request('activity') self.acceptOnce(self.showRewardDoneEvent, self.__handleJellybeanRewardDone) self.rewardGui.showReward(earnedAmount, jarAmount, message) def __handleJellybeanRewardDone(self): self.ignore(self.showRewardDoneEvent) self.handleRewardDone() def handleRewardDone(self): if base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(), 'fsm'): base.cr.playGame.getPlace().fsm.request('walk') def setSignNote(self, note): self.noteText.node().setText(note) if len(note.strip()) > 0: self.signFlat.stash() self.signFlatWithNote.unstash() self.signTextLocator.unstash() else: self.signFlat.unstash() self.signFlatWithNote.stash() self.signTextLocator.stash() def unload(self): self.notify.debug('BASE: unload') self.finishRules() self._disableCollisions() self.signModel.removeNode() del self.signModel self.sign.removeNode() del self.sign self.ignoreAll() if self.wantLever: self.unloadLever() self.root.removeNode() del self.root del self.activityId del self.activityName del self.activityType del self.wantLever del self.messageGui if self.rewardGui is not None: self.rewardGui.destroy() del self.rewardGui if hasattr(self, 'toonIds'): del self.toonIds del self.rulesDoneEvent del self.modelCount del self.cleanupActions del self.usesSmoothing del self.usesLookAround del self.difficultyOverride del self.trolleyZoneOverride if hasattr(base, 'partyActivityDict'): del base.partyActivityDict return def setPartyDoId(self, partyDoId): #print base.cr.doId2do self.party = base.cr.doId2do[partyDoId] def setX(self, x): self.x = x def setY(self, y): self.y = y def setZ(self, z): self.z = z def setH(self, h): self.h = h def setState(self, newState, timestamp): if newState == 'Active': self.activityStartTime = globalClockDelta.networkToLocalTime( timestamp) def turnOffSmoothingOnGuests(self): for toonId in self.toonIds: avatar = self.getAvatar(toonId) if avatar: if not self.usesSmoothing: avatar.stopSmooth() if not self.usesLookAround: avatar.stopLookAround() def getAvatar(self, toonId): if self.cr.doId2do.has_key(toonId): return self.cr.doId2do[toonId] else: self.notify.warning( 'BASE: getAvatar: No avatar in doId2do with id: ' + str(toonId)) return None return None def getAvatarName(self, toonId): avatar = self.getAvatar(toonId) if avatar: return avatar.getName() else: return 'Unknown' def isLocalToonInActivity(self): result = False place = base.cr.playGame.getPlace() if place and place.__class__.__name__ == 'Party' and hasattr( place, 'fsm') and place.fsm: result = place.fsm.getCurrentState().getName() == 'activity' return result def getToonIdsAsList(self): return self.toonIds def startRules(self, timeout=PartyGlobals.DefaultRulesTimeout): self.notify.debug('BASE: startRules') self.accept(self.rulesDoneEvent, self.handleRulesDone) self.rulesPanel = MinigameRulesPanel('PartyRulesPanel', self.getTitle(), self.getInstructions(), self.rulesDoneEvent, timeout) base.setCellsAvailable( base.bottomCells + [base.leftCells[0], base.rightCells[1]], False) self.rulesPanel.load() self.rulesPanel.enter() def finishRules(self): self.notify.debug('BASE: finishRules') self.ignore(self.rulesDoneEvent) if hasattr(self, 'rulesPanel'): self.rulesPanel.exit() self.rulesPanel.unload() del self.rulesPanel base.setCellsAvailable( base.bottomCells + [base.leftCells[0], base.rightCells[1]], True) def handleRulesDone(self): self.notify.error('BASE: handleRulesDone should be overridden') def getTitle(self): return TTLocalizer.PartyActivityNameDict[self.activityId]['generic'] def local2ActivityTime(self, timestamp): return timestamp - self.activityStartTime def activity2LocalTime(self, timestamp): return timestamp + self.activityStartTime def getCurrentActivityTime(self): return self.local2ActivityTime(globalClock.getFrameTime()) def disableEmotes(self): Emote.globalEmote.disableAll(base.localAvatar) def enableEmotes(self): Emote.globalEmote.releaseAll(base.localAvatar)
class DistributedPartyActivity(DistributedObject.DistributedObject): deferFor = 1 def __init__(self, cr, activityId, activityType, wantLever = False, wantRewardGui = False): DistributedObject.DistributedObject.__init__(self, cr) self.activityId = activityId self.activityName = PartyGlobals.ActivityIds.getString(self.activityId) self.activityType = activityType self.wantLever = wantLever self.wantRewardGui = wantRewardGui self.messageGui = None self.rewardGui = None self.toonIds = [] self._toonId2ror = {} childName = '%s' % self childName = childName[childName.rfind('.DistributedParty') + len('.DistributedParty'):childName.rfind('Activity instance')] if not hasattr(base, 'partyActivityDict'): base.partyActivityDict = {} base.partyActivityDict[childName] = self self.root = NodePath('root') self.rulesDoneEvent = 'rulesDone' self.modelCount = 500 self.cleanupActions = [] self.usesSmoothing = 0 self.usesLookAround = 0 self.difficultyOverride = None self.trolleyZoneOverride = None self._localToonRequestStatus = None return def localToonExiting(self): self._localToonRequestStatus = PartyGlobals.ActivityRequestStatus.Exiting def localToonJoining(self): self._localToonRequestStatus = PartyGlobals.ActivityRequestStatus.Joining def d_toonJoinRequest(self): if self._localToonRequestStatus is None: self.localToonJoining() self.sendUpdate('toonJoinRequest') return def d_toonExitRequest(self): if self._localToonRequestStatus is None: self.localToonExiting() self.sendUpdate('toonExitRequest') return def d_toonExitDemand(self): self.localToonExiting() self.sendUpdate('toonExitDemand') def joinRequestDenied(self, reason): self._localToonRequestStatus = None return def exitRequestDenied(self, reason): self._localToonRequestStatus = None return def handleToonJoined(self, toonId): self.notify.error('BASE: handleToonJoined should be overridden %s' % self.activityName) def handleToonExited(self, toonId): self.notify.error('BASE: handleToonExited should be overridden %s' % self.activityName) def handleToonDisabled(self, toonId): self.notify.error('BASE: handleToonDisabled should be overridden %s' % self.activityName) def setToonsPlaying(self, toonIds): exitedToons, joinedToons = self.getToonsPlayingChanges(self.toonIds, toonIds) self.setToonIds(toonIds) self._processExitedToons(exitedToons) self._processJoinedToons(joinedToons) def _processExitedToons(self, exitedToons): for toonId in exitedToons: if toonId != base.localAvatar.doId or toonId == base.localAvatar.doId and self.isLocalToonRequestStatus(PartyGlobals.ActivityRequestStatus.Exiting): toon = self.getAvatar(toonId) if toon is not None: self.ignore(toon.uniqueName('disable')) self.handleToonExited(toonId) if toonId == base.localAvatar.doId: self._localToonRequestStatus = None if toonId in self._toonId2ror: self.cr.relatedObjectMgr.abortRequest(self._toonId2ror[toonId]) del self._toonId2ror[toonId] return def _processJoinedToons(self, joinedToons): for toonId in joinedToons: if toonId != base.localAvatar.doId or toonId == base.localAvatar.doId and self.isLocalToonRequestStatus(PartyGlobals.ActivityRequestStatus.Joining): if toonId not in self._toonId2ror: request = self.cr.relatedObjectMgr.requestObjects([toonId], allCallback=self._handlePlayerPresent) if toonId in self._toonId2ror: del self._toonId2ror[toonId] else: self._toonId2ror[toonId] = request def _handlePlayerPresent(self, toons): toon = toons[0] toonId = toon.doId if toonId in self._toonId2ror: del self._toonId2ror[toonId] else: self._toonId2ror[toonId] = None self._enableHandleToonDisabled(toonId) self.handleToonJoined(toonId) if toonId == base.localAvatar.doId: self._localToonRequestStatus = None return def _enableHandleToonDisabled(self, toonId): toon = self.getAvatar(toonId) if toon is not None: self.acceptOnce(toon.uniqueName('disable'), self.handleToonDisabled, [toonId]) else: self.notify.warning('BASE: unable to get handle to toon with toonId:%d. Hook for handleToonDisabled not set.' % toonId) return def isLocalToonRequestStatus(self, requestStatus): return self._localToonRequestStatus == requestStatus def setToonIds(self, toonIds): self.toonIds = toonIds def getToonsPlayingChanges(self, oldToonIds, newToonIds): oldToons = set(oldToonIds) newToons = set(newToonIds) exitedToons = oldToons.difference(newToons) joinedToons = newToons.difference(oldToons) return (list(exitedToons), list(joinedToons)) def setUsesSmoothing(self): self.usesSmoothing = True def setUsesLookAround(self): self.usesLookAround = True def getInstructions(self): return TTLocalizer.DefaultPartyActivityInstructions def getParentNodePath(self): if hasattr(base.cr.playGame, 'hood') and base.cr.playGame.hood and hasattr(base.cr.playGame.hood, 'loader') and base.cr.playGame.hood.loader and hasattr(base.cr.playGame.hood.loader, 'geom') and base.cr.playGame.hood.loader.geom: return base.cr.playGame.hood.loader.geom else: self.notify.warning('Hood or loader not created, defaulting to render') return render def __createRandomNumGen(self): self.notify.debug('BASE: self.doId=0x%08X' % self.doId) self.randomNumGen = RandomNumGen.RandomNumGen(self.doId) def destroy(self = self): self.notify.debug('BASE: destroying random num gen') del self.randomNumGen self.cleanupActions.append(destroy) def generate(self): DistributedObject.DistributedObject.generate(self) self.notify.debug('BASE: generate, %s' % self.getTitle()) self.__createRandomNumGen() def announceGenerate(self): DistributedObject.DistributedObject.announceGenerate(self) self.notify.debug('BASE: announceGenerate %s' % self.activityName) self.root.setName(self.activityName + 'Root') centeredX, centeredY = getCenterPosFromGridSize(self.x, self.y, PartyGlobals.ActivityInformationDict[self.activityId]['gridsize']) self.root.setPos(centeredX, centeredY, 0.0) self.root.setH(self.h) self.normalExit = True if self.wantLever: self.leverTriggerEvent = self.uniqueName('leverTriggerEvent') self.load() def cleanup(self = self): self.notify.debug('BASE: cleanup: normalExit=%s' % self.normalExit) base.cr.renderFrame() if self.normalExit: self.sendUpdate('toonExitRequest') self.cleanupActions.append(cleanup) def disable(self): self.notify.debug('BASE: disable') DistributedObject.DistributedObject.disable(self) rorToonIds = self._toonId2ror.keys() for toonId in rorToonIds: self.cr.relatedObjectMgr.abortRequest(self._toonId2ror[toonId]) del self._toonId2ror[toonId] self.ignore(self.messageDoneEvent) if self.messageGui is not None and not self.messageGui.isEmpty(): self.messageGui.cleanup() self.messageGui = None return def delete(self): self.notify.debug('BASE: delete') self.unload() self.ignoreAll() DistributedObject.DistributedObject.delete(self) def load(self): self.notify.debug('BASE: load') self.loadSign() if self.wantLever: self.loadLever() if self.wantRewardGui: self.showRewardDoneEvent = self.uniqueName('showRewardDoneEvent') self.rewardGui = JellybeanRewardGui(self.showRewardDoneEvent) self.messageDoneEvent = self.uniqueName('messageDoneEvent') self.root.reparentTo(self.getParentNodePath()) self._enableCollisions() def loadSign(self): actNameForSign = self.activityName if self.activityId == PartyGlobals.ActivityIds.PartyJukebox40: actNameForSign = PartyGlobals.ActivityIds.getString(PartyGlobals.ActivityIds.PartyJukebox) elif self.activityId == PartyGlobals.ActivityIds.PartyDance20: actNameForSign = PartyGlobals.ActivityIds.getString(PartyGlobals.ActivityIds.PartyDance) self.sign = self.root.attachNewNode('%sSign' % self.activityName) self.signModel = self.party.defaultSignModel.copyTo(self.sign) self.signFlat = self.signModel.find('**/sign_flat') self.signFlatWithNote = self.signModel.find('**/sign_withNote') self.signTextLocator = self.signModel.find('**/signText_locator') textureNodePath = getPartyActivityIcon(self.party.activityIconsModel, actNameForSign) textureNodePath.setPos(0.0, -0.02, 2.2) textureNodePath.setScale(2.35) textureNodePath.copyTo(self.signFlat) textureNodePath.copyTo(self.signFlatWithNote) text = TextNode('noteText') text.setTextColor(0.2, 0.1, 0.7, 1.0) text.setAlign(TextNode.ACenter) text.setFont(OTPGlobals.getInterfaceFont()) text.setWordwrap(10.0) text.setText('') self.noteText = self.signFlatWithNote.attachNewNode(text) self.noteText.setPosHpr(self.signTextLocator, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0) self.noteText.setScale(0.2) self.signFlatWithNote.stash() self.signTextLocator.stash() def loadLever(self): self.lever = self.root.attachNewNode('%sLever' % self.activityName) self.leverModel = self.party.defaultLeverModel.copyTo(self.lever) self.controlColumn = NodePath('cc') column = self.leverModel.find('**/column') column.getChildren().reparentTo(self.controlColumn) self.controlColumn.reparentTo(column) self.stickHinge = self.controlColumn.attachNewNode('stickHinge') self.stick = self.party.defaultStickModel.copyTo(self.stickHinge) self.stickHinge.setHpr(0.0, 90.0, 0.0) self.stick.setHpr(0, -90.0, 0) self.stick.flattenLight() self.bottom = self.leverModel.find('**/bottom') self.bottom.wrtReparentTo(self.controlColumn) self.bottomPos = self.bottom.getPos() cs = CollisionSphere(0.0, 1.35, 2.0, 1.0) cs.setTangible(False) cn = CollisionNode(self.leverTriggerEvent) cn.addSolid(cs) cn.setIntoCollideMask(OTPGlobals.WallBitmask) self.leverTrigger = self.root.attachNewNode(cn) self.leverTrigger.reparentTo(self.lever) self.leverTrigger.stash() cs = CollisionTube(0.0, 2.7, 0.0, 0.0, 2.7, 3.0, 1.2) cn = CollisionNode('levertube') cn.addSolid(cs) cn.setIntoCollideMask(OTPGlobals.WallBitmask) self.leverTube = self.leverModel.attachNewNode(cn) host = base.cr.doId2do.get(self.party.partyInfo.hostId) if host is None: self.notify.debug('%s loadLever : Host has left the game before lever could be created.' % self.activityName) return scale = host.getGeomNode().getChild(0).getSz(render) self.leverModel.setScale(scale) self.controlColumn.setPos(0, 0, 0) host.setPosHpr(self.lever, 0, 0, 0, 0, 0, 0) host.pose('leverNeutral', 0) host.update() pos = host.rightHand.getPos(self.controlColumn) self.controlColumn.setPos(pos[0], pos[1], pos[2] - 1) self.bottom.setZ(host, 0.0) self.bottom.setPos(self.bottomPos[0], self.bottomPos[1], self.bottom.getZ()) lookAtPoint = Point3(0.3, 0, 0.1) lookAtUp = Vec3(0, -1, 0) self.stickHinge.lookAt(host.rightHand, lookAtPoint, lookAtUp) host.play('walk') host.update() return def unloadLever(self): self.lever.removeNode() self.leverModel.removeNode() self.controlColumn.removeNode() self.stickHinge.removeNode() self.stick.removeNode() self.bottom.removeNode() self.leverTrigger.removeNode() self.leverTube.removeNode() del self.bottomPos del self.lever del self.leverModel del self.controlColumn del self.stickHinge del self.stick del self.bottom del self.leverTrigger del self.leverTube def _enableCollisions(self): if self.wantLever: self.leverTrigger.unstash() self.accept('enter%s' % self.leverTriggerEvent, self._leverPulled) def _disableCollisions(self): if self.wantLever: self.leverTrigger.stash() self.ignore('enter%s' % self.leverTriggerEvent) def _leverPulled(self, collEntry): self.notify.debug('_leverPulled : Someone pulled the lever!!! ') if self.activityType == PartyGlobals.ActivityTypes.HostInitiated and base.localAvatar.doId != self.party.partyInfo.hostId: return False return True def getToonPullingLeverInterval(self, toon): walkTime = 0.2 reach = ActorInterval(toon, 'leverReach', playRate=2.0) pull = ActorInterval(toon, 'leverPull', startFrame=6) origPos = toon.getPos(render) origHpr = toon.getHpr(render) newPos = self.lever.getPos(render) newHpr = self.lever.getHpr(render) origHpr.setX(PythonUtil.fitSrcAngle2Dest(origHpr[0], newHpr[0])) toon.setPosHpr(origPos, origHpr) reachAndPull = Sequence(ActorInterval(toon, 'walk', loop=True, duration=walkTime - reach.getDuration()), reach, pull) leverSeq = Sequence(Wait(walkTime + reach.getDuration() - 0.1), self.stick.hprInterval(0.55, Point3(0.0, 25.0, 0.0), Point3(0.0, 0.0, 0.0)), Wait(0.3), self.stick.hprInterval(0.4, Point3(0.0, 0.0, 0.0), Point3(0.0, 25.0, 0.0))) returnSeq = Sequence(Parallel(toon.posInterval(walkTime, newPos, origPos), toon.hprInterval(walkTime, newHpr, origHpr), leverSeq, reachAndPull)) return returnSeq def showMessage(self, message, endState = 'walk'): base.cr.playGame.getPlace().fsm.request('activity') self.acceptOnce(self.messageDoneEvent, self.__handleMessageDone) self.messageGui = TTDialog.TTGlobalDialog(doneEvent=self.messageDoneEvent, message=message, style=TTDialog.Acknowledge) self.messageGui.endState = endState def __handleMessageDone(self): self.ignore(self.messageDoneEvent) if hasattr(base.cr.playGame.getPlace(), 'fsm'): if self.messageGui and hasattr(self.messageGui, 'endState'): self.notify.info('__handleMessageDone (endState=%s)' % self.messageGui.endState) base.cr.playGame.getPlace().fsm.request(self.messageGui.endState) else: self.notify.warning("messageGui has no endState, defaulting to 'walk'") base.cr.playGame.getPlace().fsm.request('walk') if self.messageGui is not None and not self.messageGui.isEmpty(): self.messageGui.cleanup() self.messageGui = None return def showJellybeanReward(self, earnedAmount, jarAmount, message): if not self.isLocalToonInActivity() or base.localAvatar.doId in self.getToonIdsAsList(): messenger.send('DistributedPartyActivity-showJellybeanReward') base.cr.playGame.getPlace().fsm.request('activity') self.acceptOnce(self.showRewardDoneEvent, self.__handleJellybeanRewardDone) self.rewardGui.showReward(earnedAmount, jarAmount, message) def __handleJellybeanRewardDone(self): self.ignore(self.showRewardDoneEvent) self.handleRewardDone() def handleRewardDone(self): if base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(), 'fsm'): base.cr.playGame.getPlace().fsm.request('walk') def setSignNote(self, note): self.noteText.node().setText(note) if len(note.strip()) > 0: self.signFlat.stash() self.signFlatWithNote.unstash() self.signTextLocator.unstash() else: self.signFlat.unstash() self.signFlatWithNote.stash() self.signTextLocator.stash() def unload(self): self.notify.debug('BASE: unload') self.finishRules() self._disableCollisions() self.signModel.removeNode() del self.signModel self.sign.removeNode() del self.sign self.ignoreAll() if self.wantLever: self.unloadLever() self.root.removeNode() del self.root del self.activityId del self.activityName del self.activityType del self.wantLever del self.messageGui if self.rewardGui is not None: self.rewardGui.destroy() del self.rewardGui if hasattr(self, 'toonIds'): del self.toonIds del self.rulesDoneEvent del self.modelCount del self.cleanupActions del self.usesSmoothing del self.usesLookAround del self.difficultyOverride del self.trolleyZoneOverride if hasattr(base, 'partyActivityDict'): del base.partyActivityDict return def setPartyDoId(self, partyDoId): self.party = base.cr.doId2do[partyDoId] def setX(self, x): self.x = x def setY(self, y): self.y = y def setH(self, h): self.h = h def setState(self, newState, timestamp): if newState == 'Active': self.activityStartTime = globalClockDelta.networkToLocalTime(timestamp) def turnOffSmoothingOnGuests(self): for toonId in self.toonIds: avatar = self.getAvatar(toonId) if avatar: if not self.usesSmoothing: avatar.stopSmooth() if not self.usesLookAround: avatar.stopLookAround() def getAvatar(self, toonId): if self.cr.doId2do.has_key(toonId): return self.cr.doId2do[toonId] else: self.notify.warning('BASE: getAvatar: No avatar in doId2do with id: ' + str(toonId)) return None return None def getAvatarName(self, toonId): avatar = self.getAvatar(toonId) if avatar: return avatar.getName() else: return 'Unknown' def isLocalToonInActivity(self): result = False place = base.cr.playGame.getPlace() if place and place.__class__.__name__ == 'Party' and hasattr(place, 'fsm') and place.fsm: result = place.fsm.getCurrentState().getName() == 'activity' return result def getToonIdsAsList(self): return self.toonIds def startRules(self, timeout = PartyGlobals.DefaultRulesTimeout): self.notify.debug('BASE: startRules') self.accept(self.rulesDoneEvent, self.handleRulesDone) self.rulesPanel = MinigameRulesPanel('PartyRulesPanel', self.getTitle(), self.getInstructions(), self.rulesDoneEvent, timeout) base.setCellsAvailable(base.bottomCells + [base.leftCells[0], base.rightCells[1]], False) self.rulesPanel.load() self.rulesPanel.enter() def finishRules(self): self.notify.debug('BASE: finishRules') self.ignore(self.rulesDoneEvent) if hasattr(self, 'rulesPanel'): self.rulesPanel.exit() self.rulesPanel.unload() del self.rulesPanel base.setCellsAvailable(base.bottomCells + [base.leftCells[0], base.rightCells[1]], True) def handleRulesDone(self): self.notify.error('BASE: handleRulesDone should be overridden') def getTitle(self): return TTLocalizer.PartyActivityNameDict[self.activityId]['generic'] def local2ActivityTime(self, timestamp): return timestamp - self.activityStartTime def activity2LocalTime(self, timestamp): return timestamp + self.activityStartTime def getCurrentActivityTime(self): return self.local2ActivityTime(globalClock.getFrameTime()) def disableEmotes(self): Emote.globalEmote.disableAll(base.localAvatar) def enableEmotes(self): Emote.globalEmote.releaseAll(base.localAvatar)
class Node2DRenderer: def __init__(self, resources, spriteEngine): self.log = logging.getLogger('pano.render2D') self.resources = resources # the Node that is being rendered self.node = None # the scene's root node self.sceneRoot = None # scene node for the full screen quad that renders the background texture of 2D nodes self.bgCard = None # bounds in XZ plane, by default we assume the node is parented to render2d # the order is left, right, top, bottom self.bounds = (-1.0, 1.0, -1.0, 1.0) self.spritesByHotspot = {} self.debugSprites = {} # used for detecting the hotspot under a window coordinate self.raycaster = None # this caches the node's hotspot map, if any self.hotspotsMap = None self.spritesEngine = spriteEngine def initialize(self): asp = base.getAspectRatio() lens = OrthographicLens() lens.setFilmSize(2.0, 2.0) lens.setNearFar(-1000, 1000) self.getCamera().node().setLens(lens) # creates the root node if self.sceneRoot is None: self.sceneRoot = render2d.attachNewNode( PanoConstants.NODE2D_ROOT_NODE) self.raycaster = NodeRaycaster(self) self.raycaster.initialize() def dispose(self): ''' Disposes any rendering resources, it assumes that this instance won't be used again. ''' if self.raycaster is not None: self.raycaster.dispose() self.clearScene() if self.sceneRoot is not None: self.sceneRoot.removeNode() self.sceneRoot = None def clearScene(self): ''' Clears the scenegraph effectively removing all nodes from rendering. ''' if self.bgCard is not None and not self.bgCard.empty(): self.bgCard.removeNode() for spr in self.spritesByHotspot.values(): spr.remove() for dbgSpr in self.debugSprites.values(): dbgSpr.remove() self.spritesByHotspot = {} self.debugSprites = {} self.hotspotsMap = None def displayNode(self, node): """ Displays the given node. """ if self.node is not None and self.node.getName() == node.getName(): return else: self.clearScene() self.node = node if self.node.parent2d is None or self.node.parent2d == Node.PT_Render2D: # when the parent is not explicitly defined, then assume render2d self.bounds = (-1.0, 1.0, -1.0, 1.0) self.sceneRoot.setScale(1.0, 1.0, 1.0) self.sceneRoot.reparentTo(render2d) else: # asp = self.getCamera().node().getLens().getAspectRatio() asp = base.getAspectRatio() self.bounds = (-asp, asp, -1.0, 1.0) # aspectRatio = base.getAspectRatio() # self.sceneRoot.setScale(1.0 / asp, 1.0, 1.0) self.sceneRoot.reparentTo(aspect2d) if self.node.image is not None: self._setBackgroundTexture() if self.node.hotspotsMapFilename: if self.hotspotsMap is None: self.hotspotsMap = self.resources.loadHotspotsMap( self.node.hotspotsMapFilename) self._createHotspotsGeoms() def render(self, millis): pass def getNode(self): """ Returns the Node object that we are currently rendering. """ return self.node def getSceneRoot(self): ''' Returns the Nodepath that acts as the scene root. ''' return self.sceneRoot def getCamera(self): return base.cam2d def _setBackgroundTexture(self): ''' Sets the background texture for 2D nodes. It creates a full screen quad that has the corresponding texture applied to it. Note: The field node.parent2d specifies if the quad will be parented to render2d or aspect2d. ''' bgTex = self.resources.loadTexture(self.node.image) if bgTex is not None: cm = CardMaker('node_bg') cm.setFrame(self.bounds[0], self.bounds[1], self.bounds[2], self.bounds[3]) cm.setHasUvs(True) self.bgCard = NodePath(cm.generate()) self.bgCard.setName('2dnode_image') self.bgCard.setTexture(bgTex) bgTex.setMinfilter(Texture.FTNearest) bgTex.setMagfilter(Texture.FTNearest) self.bgCard.reparentTo(self.sceneRoot) self.bgCard.setBin("fixed", PanoConstants.RENDER_ORDER_BACKGROUND_IMAGE) else: self.log.error('Failed to set background texture for node %s.' % self.node.name) def _createHotspotsGeoms(self): """ Creates nodes for the pickable geometry (a box) of hotspots and their sprite visuals. """ for hp in self.node.getHotspots(): self.renderHotspot(hp) self.renderHotspotDebugGeom(hp) def renderHotspot(self, hp, spriteOverride=None): ''' Renders the given hotspot using its associated sprite. @param hp: The hotspot to render. @param sprite: If not None it is used for overriding the hotspot's sprite. ''' spriteName = hp.sprite if spriteOverride is None else spriteOverride if spriteName is not None: sprite = self.resources.loadSprite(spriteName) if sprite is None: self.log.error('Failed to render sprite %s' % sprite.name) return sri = self.spritesEngine.createSprite(sprite) sri.setPos(hp.xo + hp.width / 2, 1.0, hp.yo + hp.height / 2) if not hp.active: sri.hide() self.spritesByHotspot[hp.name] = sri def renderHotspotDebugGeom(self, hp): ''' Renders a debug geometry for the given hotspot. @param hp: The hotspot for which to render debug geometry. ''' # dbgSprite = Sprite('debug_' + hp.name) # dbgSprite.eggFile = 'debug_box.egg' # dbgSprite.width = hp.width # dbgSprite.height = hp.height # # sri = self.spritesEngine.createSprite(dbgSprite) # sri.setPos(hp.xo, 1.0, hp.yo) # self.debugSprites[hp.name] = sri def getHotspotSprite(self, hotspot): """ Returns a SpriteRenderInterface which can be used to control the hotspot's sprite. @param hotspot: The hotspot instance @return: A SpriteRenderInterface instance or None. """ return self.spritesByHotspot.get(hotspot.name) def removeHotspot(self, hotspot): ''' Removes a hotspot from the render list. The debug geometry and sprite associated with the hotspot won't be visible anymore. @param hotspot: A pano.model.Hotspot instance for the hotspot to be removed. ''' if self.log.isEnabledFor(logging.DEBUG): self.log.debug('Removing hotspot %s' % hotspot.name) spr = self.getHotspotSprite(hotspot) if spr is not None: spr.remove() del self.spritesByHotspot[hotspot.name] # remove the hotspot's debug geometry spr = self.debugSprites.get(hotspot.name) if spr is not None: spr.remove() del self.debugSprites[hotspot.name] def hideHotspot(self, hp): ''' Hides the scene node that parents the hotspots sprite in the scene. @param hp: The hotspot that will be hidden. ''' if self.log.isEnabledFor(logging.DEBUG): self.log.debug('Hiding hotspot %s' % hp.name) sri = self.getHotspotSprite(hp) if sri is not None: sri.hide() def showHotspot(self, hp): ''' Shows the scene node that parents the hotspots sprite in the scene. @param hp: The hotspot that will be shown. ''' if self.log.isEnabledFor(logging.DEBUG): self.log.debug('Showing hotspot %s' % hp.name) sri = self.getHotspotSprite(hp) if sri is not None: sri.show() def replaceHotspotSprite(self, hotspot, newSprite): ''' Changes the visual appearance of a hotspot by replacing its sprite with a new one. @param hp: The hotspot that will have its sprite replaced. @param newSprite: The name of the new sprite to use for rendering. ''' if self.log.isEnabledFor(logging.DEBUG): self.log.debug("Replacing hotspot's %s sprite with %s" % (hotspot.name, newSprite)) self.removeHotspot(hotspot) self.renderHotspot(hotspot, newSprite) self.renderHotspotDebugGeom(hotspot) def pauseAnimations(self): """ Stops all node animations. """ for sri in self.spritesByHotspot.values(): sri.pause() def resumeAnimations(self): """ Resumes node animations. """ for sri in self.spritesByHotspot.values(): sri.play() def drawDebugHotspots(self, flag): self.drawHotspots = flag for spr in self.debugSprites.values(): spr.show() else: spr.hide() def raycastHotspots(self, x, y): ''' Returns the hotspot(s) under the given window coordinates. @param x: The x window coordinate of the mouse in render space. @param y: The y window coordinate of the mouse in render space. @return: A list of Hotspot instances or None. ''' screenPoint = sprite2d.getRelativePoint(render, Vec3(x, 1.0, y)) hotspots = [] sx = int(screenPoint.getX()) sy = int(screenPoint.getZ()) # use the node's hotspots map if exists, otherwise check if the click point # lies inside a hotspot's bounds if self.node.hotspotsMapFilename: # convert the coordinates from -1..1 to 0..1 range x = (x + 1) / 2.0 y = (y + 1) / 2.0 hpName = self.hotspotsMap.getHotspot(x, 1.0 - y) hp = self.node.getHotspot(hpName) if hp: hotspots.append(hp) else: # check through bounds for hp in self.node.getHotspots(): if sx >= hp.xo and sx <= hp.xe and sy >= hp.yo and sy <= hp.ye: maskImg = self.hotspotsImageMasks.get(hp.name) if maskImg is not None: imgX = int(maskImg.getXSize() * sx / (hp.xe - hp.xo)) imgY = int(maskImg.getYSize() * sy / (hp.ye - hp.yo)) col = maskImg.getXel(imgX, imgY) if col[0] == 0.0 and col[1] == 0.0 and col[2] == 0.0: continue hotspots.append(hp) return hotspots def _getImageDimensions(self): tex = self.bgCard.getTexture() return (tex.getXSize(), tex.getYSize())