class DistributedPartyCannonActivity(DistributedPartyActivity): # I never go anywhere without my party cannon! notify = DirectNotifyGlobal.directNotify.newCategory( 'DistributedPartyCannonActivity') HIT_GROUND = 0 HIT_TOWER = 1 HIT_WATER = 2 REACTIVATE_CLOUD_TASK = 'PartyActivity_ReactivateLastCloud' RULES_DONE_EVENT = 'DistributedPartyCannonActivity_RULES_DONE_EVENT' LOCAL_TOON_LANDED_EVENT = 'DistributedPartyCannonActivity_LOCAL_TOON_LANDED_EVENT' NetDivisor = 100 TimeFactor = 0.75 BroadcastPeriod = 0.2 def __init__(self, cr): DistributedPartyActivity.__init__(self, cr, ActivityIds.PartyCannon, ActivityTypes.Continuous, wantRewardGui=True) self.gui = None self.firingCannon = None self.shadowNode = None self.partyDoId = None self.splash = None self.dustCloud = None self.lastWakeTime = 0 self.localFlyingDropShadow = None self.localFlyingToon = None self.localFlyingToonId = 0 self._lastBroadcastTime = -self.BroadcastPeriod self._dirtyNewVel = None self.hitBumper = 0 self.hitCloud = 0 self.lastPos = Vec3(0, 0, 0) self.lastVel = Vec3(0, 0, 0) self.vel = Vec3(0, 0, 0) self.landingPos = Vec3(0, 0, 0) self.t = 0 self.lastT = 0 self.deltaT = 0 self._lastCloudHit = None self.cameraPos = Vec3(0, -15.0, -25.0) self.cameraSpeed = 5.0 self.camNode = None self.flyingToonOffsetRotation = 0 self.flyingToonOffsetAngle = 0 self.flyingToonOffsetX = 0 self.flyingToonOffsetY = 0 self.flyingToonCloudsHit = 0 self.initialFlyVel = 0 self._localPlayedBefore = False self.hitTrack = None self.cTrav = None self.flyColNode = None self.flyColNodePath = None self._flyingCollisionTaskName = None return def generateInit(self): DistributedPartyActivity.generateInit(self) self.taskNameFireCannon = self.taskName('fireCannon') self.taskNameShoot = self.taskName('shootTask') self.taskNameFly = self.taskName('flyTask') self.gui = CannonGui() def load(self): self.notify.debug('load') DistributedPartyActivity.load(self) base.cr.playGame.hood.loader.loadClouds() base.cr.playGame.hood.loader.setCloudSwitch(1) self.shadow = loader.loadModel('phase_3/models/props/drop_shadow') self.shadowNode = hidden.attachNewNode('dropShadow') self.shadow.copyTo(self.shadowNode) self.shadowNode.setColor(0, 0, 0, 0.5) self.shadowNode.setBin('fixed', 0, 1) self.splash = Splash.Splash(render) self.dustCloud = DustCloud.DustCloud(render) self.dustCloud.setBillboardPointEye() self.sndHitGround = base.loader.loadSfx( 'phase_4/audio/sfx/MG_cannon_hit_dirt.ogg') self.sndHitWater = base.loader.loadSfx( 'phase_4/audio/sfx/MG_cannon_splash.ogg') self.sndHitHouse = base.loader.loadSfx( 'phase_5/audio/sfx/AA_drop_sandbag.ogg') self.sndBounce1 = base.loader.loadSfx('phase_13/audio/sfx/bounce1.ogg') self.sndBounce2 = base.loader.loadSfx('phase_13/audio/sfx/bounce2.ogg') self.sndBounce3 = base.loader.loadSfx('phase_13/audio/sfx/bounce3.ogg') self.onstage() self.sign.reparentTo(hidden) self.sign.setPos(-6.0, 10.0, 0.0) self.accept(FireworksStartedEvent, self.__handleFireworksStarted) self.accept(FireworksFinishedEvent, self.__handleFireworksFinished) def generate(self): DistributedPartyActivity.generate(self) self._doneCannons = False self._avId2trajectoryInfo = {} self._remoteToonFlyTaskName = 'remoteToonFlyTask-%s' % self.doId taskMgr.add(self._remoteToonFlyTask, self._remoteToonFlyTaskName, priority=45) self.d_cloudsColorRequest() def unload(self): self.notify.debug('unload') DistributedPartyActivity.unload(self) if self.shadowNode is not None: self.shadowNode.removeNode() del self.shadowNode if self.splash is not None: self.splash.destroy() del self.splash if self.dustCloud is not None: self.dustCloud.destroy() del self.dustCloud del self.sndHitHouse del self.sndHitGround del self.sndHitWater del self.sndBounce1 del self.sndBounce2 del self.sndBounce3 if self.localFlyingToon: self.__resetToon(self.localFlyingToon) self.localFlyingToon.loop('neutral') self.localFlyingToon.setPlayRate(1.0, 'run') self.localFlyingToon = None self.ignoreAll() return def onstage(self): self.notify.debug('onstage') self.splash.reparentTo(render) self.dustCloud.reparentTo(render) def offstage(self): self.notify.debug('offstage') if self.splash is not None: self.splash.reparentTo(hidden) self.splash.stop() if self.dustCloud is not None: self.dustCloud.reparentTo(hidden) self.dustCloud.stop() return def disable(self): taskMgr.remove(self._remoteToonFlyTaskName) if self._flyingCollisionTaskName: taskMgr.remove(self._flyingCollisionTaskName) taskMgr.remove(self.taskNameFireCannon) taskMgr.remove(self.taskNameShoot) taskMgr.remove(self.taskNameFly) taskMgr.remove(DistributedPartyCannonActivity.REACTIVATE_CLOUD_TASK) self.ignoreAll() if self.localFlyingToonId: self.__stopCollisionHandler(self.localFlyingToon) self.__stopLocalFlyTask(self.localFlyingToonId) self.setMovie(PartyGlobals.CANNON_MOVIE_CLEAR, self.localFlyingToonId) if self.hitTrack is not None: self.hitTrack.finish() del self.hitTrack self.hitTrack = None DistributedPartyActivity.disable(self) return def delete(self): self.offstage() DistributedPartyActivity.delete(self) def setMovie(self, mode, toonId): self.notify.debug('%s setMovie(%s, %s)' % (self.doId, toonId, mode)) if toonId != base.localAvatar.doId: return if mode == PartyGlobals.CANNON_MOVIE_CLEAR: self.landToon(toonId) elif mode == PartyGlobals.CANNON_MOVIE_LANDED: self.landToon(toonId) elif mode == PartyGlobals.CANNON_MOVIE_FORCE_EXIT: self.landToon(toonId) def __handleAvatarGone(self): self.setMovie(PartyGlobals.CANNON_MOVIE_CLEAR, 0) def handleToonDisabled(self, toonId): self.notify.warning('handleToonDisabled no implementation yet') def handleToonJoined(self, toonId): self.notify.warning('handleToonJoined no implementation yet') def isLocalToon(self, av): return base.localAvatar == av def isLocalToonId(self, toonId): return base.localAvatar.doId == toonId def getTitle(self): return TTLocalizer.PartyCannonActivityTitle def getInstructions(self): return TTLocalizer.PartyCannonActivityInstructions def hasPlayedBefore(self): return self._localPlayedBefore def displayRules(self): self.startRules() def handleRulesDone(self): self.finishRules() self._localPlayedBefore = True messenger.send(DistributedPartyCannonActivity.RULES_DONE_EVENT) def setCannonWillFire(self, cannonId, zRot, angle): self.notify.debug('setCannonWillFire: %d %d %d' % (cannonId, zRot, angle)) cannon = base.cr.doId2do.get(cannonId) if cannon is None: self.notify.warning( "Cannon has not been created, but we got this message. Don't show firing." ) return if not cannon.getToonInside(): self.notify.warning( "setCannonWillFire, but no toon insde. Don't show firing") return if self.isLocalToon(cannon.getToonInside()): self.localFlyingToon = base.localAvatar self.localFlyingToonId = base.localAvatar.doId self.localFiringCannon = cannon self.flyingToonCloudsHit = 0 cannon.updateModel(zRot, angle) toonId = cannon.getToonInside().doId task = PythonTask(self.__fireCannonTask) task.toonId = toonId task.cannon = cannon taskMgr.add(task, self.taskNameFireCannon) self.toonIds.append(toonId) return def __fireCannonTask(self, task): launchTime = 0.0 toonId = task.toonId cannon = task.cannon toon = cannon.getToonInside() self.notify.debug( str(self.doId) + ' FIRING CANNON FOR TOON ' + str(toonId)) if not cannon.isToonInside(): return Task.done if self.isLocalToonId(toonId): self.inWater = 0 startPos, startHpr, startVel, trajectory = self.__calcFlightResults( cannon, toonId, launchTime) self.notify.debug('start position: ' + str(startPos)) self.notify.debug('start velocity: ' + str(startVel)) self.notify.debug('time of launch: ' + str(launchTime)) cannon.removeToonReadyToFire() shootTask = PythonTask(self.__shootTask, self.taskNameShoot) shootTask.info = {'toonId': toonId, 'cannon': cannon} if self.isLocalToonId(toonId): self.flyingToonOffsetRotation = 0 self.flyingToonOffsetAngle = 0 self.flyingToonOffsetX = 0 self.flyingToonOffsetY = 0 self.hitCloud = 0 self.initialFlyVel = INITIAL_VELOCITY self.camNode = NodePath(self.uniqueName('flyingCamera')) self.camNode.setScale(0.5) self.camNode.setPos(self.localFlyingToon.getPos()) self.camNode.setHpr(self.localFlyingToon.getHpr()) self.camNode.reparentTo(render) self.lastStartVel = startVel place = base.cr.playGame.getPlace() place.fsm.request('activity') toon.dropShadow.hide() self.localFlyingDropShadow = self.shadowNode.copyTo(hidden) vel = startVel toon.lookAt(toon.getPos() + Vec3(vel[0], vel[1], vel[2])) toon.setP(localAvatar, -90) hpr = toon.getHpr() toon.d_setPosHpr(startPos[0], startPos[1], startPos[2], hpr[0], hpr[1], hpr[2]) self.localFlyingToon.wrtReparentTo(render) info = {} info['toonId'] = toonId info['trajectory'] = trajectory info['launchTime'] = launchTime info['toon'] = self.localFlyingToon info['hRot'] = cannon.getRotation() camera.wrtReparentTo(self.localFlyingToon) flyTask = PythonTask(self.__localFlyTask, self.taskNameFly) flyTask.info = info seqTask = Task.sequence(shootTask, flyTask) self.__startCollisionHandler() self.notify.debug('Disable standard local toon controls.') base.localAvatar.disableAvatarControls() frameTime = globalClock.getFrameTime() netLaunchTime = globalClockDelta.localToNetworkTime(launchTime + frameTime, bits=31) self.sendUpdate('setToonTrajectoryAi', [ netLaunchTime, startPos[0], startPos[1], startPos[2], startHpr[0], startHpr[1], startHpr[2], startVel[0], startVel[1], startVel[2] ]) else: seqTask = shootTask taskMgr.add(seqTask, self.taskName('flyingToon') + '-' + str(toonId)) toon.startSmooth() return Task.done def setToonTrajectory(self, avId, launchTime, x, y, z, h, p, r, vx, vy, vz): if avId == localAvatar.doId: return startPos = Vec3(x, y, z) startHpr = Vec3(h, p, r) startVel = Vec3(vx, vy, vz) startT = globalClockDelta.networkToLocalTime(launchTime, bits=31) + 0.2 trajectory = Trajectory.Trajectory(0.0, startPos, startVel) self._avId2trajectoryInfo[avId] = ScratchPad(startPos=startPos, startHpr=startHpr, startVel=startVel, startT=startT, trajectory=trajectory) def _remoteToonFlyTask(self, task=None): ids2del = [] frameTime = globalClock.getFrameTime() for avId, trajInfo in self._avId2trajectoryInfo.iteritems(): trajectory = trajInfo.trajectory startTime = trajInfo.startT groundTime = trajectory.calcTimeOfImpactOnPlane( 0.0) / self.TimeFactor + startTime now = frameTime if now < startTime: now = startTime if now > groundTime: now = groundTime t = max(0.0, now - startTime) t *= self.TimeFactor toon = self.cr.getDo(avId) if toon is None: ids2del.append(avId) else: toon.setFluidPos(trajectory.getPos(t)) vel = trajectory.getVel(t) toon.lookAt(toon.getPos() + Vec3(vel[0], vel[1], vel[2])) toon.setP(toon, -90) for avId in ids2del: del self._avId2trajectoryInfo[avId] return Task.cont def __calcFlightResults(self, cannon, toonId, launchTime): def quantizeVec(vec, divisor): quantize = lambda value, divisor: float(int(value * int(divisor)) ) / int(divisor) vec[0] = quantize(vec[0], divisor) vec[1] = quantize(vec[1], divisor) vec[2] = quantize(vec[2], divisor) startPos = cannon.getToonFirePos() startHpr = cannon.getToonFireHpr() startVel = cannon.getToonFireVel() quantizeVec(startPos, self.NetDivisor) quantizeVec(startHpr, self.NetDivisor) quantizeVec(startVel, self.NetDivisor) trajectory = Trajectory.Trajectory(launchTime, startPos, startVel) self.trajectory = trajectory return startPos, startHpr, startVel, trajectory def __shootTask(self, task): task.info['cannon'].fire() toonId = task.info['toonId'] toon = base.cr.doId2do.get(toonId) if toon: toon.loop('swim') else: self.notify.debug( '__shootTask avoided a crash, toon %d not found' % toonId) if self.isLocalToonId(task.info['toonId']): self.localFlyingDropShadow.reparentTo(render) self.gui.enableAimKeys() return Task.done def d_setLanded(self, toonId): printStack() self.notify.debug('d_setLanded %s' % toonId) if self.isLocalToonId(toonId): if self.cr: self.sendUpdate('setLanded', [toonId]) else: self.notify.debug('we avoided crash 2') def setLanded(self, toonId): if toonId in self._avId2trajectoryInfo: del self._avId2trajectoryInfo[toonId] def landToon(self, toonId): self.notify.debug('%s landToon' % self.doId) toon = base.cr.doId2do.get(toonId) if toon is not None: toon.resetLOD() if toon == base.localAvatar: self.__stopCollisionHandler(base.localAvatar) toon.wrtReparentTo(render) self.__setToonUpright(toon) toon.setPlayRate(1.0, 'run') toon.startSmooth() toon.setScale(1.0) self.ignore(toon.uniqueName('disable')) self.__cleanupFlyingToonData(toon) toon.dropShadow.show() place = base.cr.playGame.getPlace() if place is not None: if not hasattr(place, 'fsm'): return if toon is not None and toon == base.localAvatar: self.__localDisplayLandedResults() return def __localDisplayLandedResults(self): if self.flyingToonCloudsHit > 0: self._doneCannons = True else: self.__localToonDoneLanding() def handleRewardDone(self): DistributedPartyActivity.handleRewardDone(self) if self._doneCannons: self.__localToonDoneLanding() def __localToonDoneLanding(self): base.cr.playGame.getPlace().fsm.request('walk') self.notify.debug('__localToonDoneLanding') base.localAvatar.collisionsOn() base.localAvatar.startPosHprBroadcast() base.localAvatar.enableAvatarControls() messenger.send(DistributedPartyCannonActivity.LOCAL_TOON_LANDED_EVENT) def __setToonUpright(self, toon, pos=None): if toon: if self.inWater: toon.setP(0) toon.setR(0) return if not pos: pos = toon.getPos(render) toon.setPos(render, pos) toon.loop('neutral') if self.localFiringCannon and hasattr(self.localFiringCannon, 'cannonNode'): if self.localFiringCannon.cannonNode: toon.lookAt(self.localFiringCannon.cannonNode) else: self.notify.debug('we avoided crash 1.') toon.setP(0) toon.setR(0) toon.setScale(1, 1, 1) def __resetToonToCannon(self, avatar): self.notify.debug('__resetToonToCannon') if not avatar and self.localFlyingToonId: avatar = base.cr.doId2do.get(self.localFlyingToonId, None) if avatar: self.__resetToon(avatar) return def __resetToon(self, avatar, pos=None): self.notify.debug('__resetToon') if avatar: self.__stopCollisionHandler(avatar) self.__setToonUpright(avatar, pos) if self.isLocalToonId(avatar.doId): self.notify.debug('toon setting position to %s' % pos) if pos: base.localAvatar.setPos(pos) camera.reparentTo(avatar) self.d_setLanded(avatar.doId) def __updateFlightVelocity(self, trajectory): hpr = LRotationf(self.flyingToonOffsetRotation, 0, 0) newVel = hpr.xform(self.lastStartVel) hpr = LRotationf(0, self.flyingToonOffsetAngle, 0) zVel = hpr.xform(self.lastStartVel).getZ() if zVel < newVel.getZ(): newVel.setZ(zVel) trajectory.setStartVel(newVel) now = globalClock.getFrameTime() if now - self._lastBroadcastTime >= self.BroadcastPeriod: self._dirtyNewVel = newVel if self._dirtyNewVel: self.sendUpdate('updateToonTrajectoryStartVelAi', [ self._dirtyNewVel[0], self._dirtyNewVel[1], self._dirtyNewVel[2] ]) self._lastBroadcastTime = now self._dirtyNewVel = None return def updateToonTrajectoryStartVel(self, avId, vx, vy, vz): if avId == localAvatar.doId: return if avId in self._avId2trajectoryInfo: self._avId2trajectoryInfo[avId].trajectory.setStartVel( Vec3(vx, vy, vz)) def __isFlightKeyPressed(self): return self.gui.leftPressed or self.gui.rightPressed or self.gui.upPressed or self.gui.downPressed def __moveFlyingToon(self, toon): toonP = toon.getP(render) isToonFlyingHorizontal = toonP > -150 and toonP < -30 OFFSET = 0.25 rotVel = 0 if self.gui.leftPressed: if isToonFlyingHorizontal: rotVel += CANNON_ROTATION_VEL else: self.flyingToonOffsetX -= OFFSET if self.gui.rightPressed: if isToonFlyingHorizontal: rotVel -= CANNON_ROTATION_VEL else: self.flyingToonOffsetX += OFFSET self.flyingToonOffsetRotation += rotVel * globalClock.getDt() angVel = 0 if self.gui.upPressed: if not isToonFlyingHorizontal: self.flyingToonOffsetY -= OFFSET if self.gui.downPressed: if isToonFlyingHorizontal: angVel += CANNON_ANGLE_VEL else: self.flyingToonOffsetY += OFFSET self.flyingToonOffsetAngle += angVel * globalClock.getDt() def __stopLocalFlyTask(self, toonId): taskMgr.remove(self.taskName('flyingToon') + '-' + str(toonId)) self.gui.disableAimKeys() def __localFlyTask(self, task): toon = task.info['toon'] if toon.isEmpty(): self.__resetToonToCannon(self.localFlyingToon) return Task.done curTime = task.time + task.info['launchTime'] t = curTime t *= self.TimeFactor self.lastT = self.t self.t = t deltaT = self.t - self.lastT self.deltaT = deltaT if self.hitBumper: pos = self.lastPos + self.lastVel * deltaT vel = self.lastVel self.lastVel += Vec3(0, 0, -32.0) * deltaT self.lastPos = pos toon.setFluidPos(pos) lastR = toon.getR() toon.setR(lastR - deltaT * self.angularVel * 2.0) cameraView = 0 else: if not self.hitCloud and self.__isFlightKeyPressed(): self.__moveFlyingToon(toon) self.__updateFlightVelocity(task.info['trajectory']) if self.hitCloud == 1: vel = task.info['trajectory'].getVel(t) startPos = toon.getPos(render) task.info['trajectory'].setStartTime(t) task.info['trajectory'].setStartPos(startPos) task.info['trajectory'].setStartVel(self.lastVel) toon.lookAt(toon.getPos() + vel) toon.setH(-toon.getH()) now = globalClock.getFrameTime() netLaunchTime = globalClockDelta.localToNetworkTime(now, bits=31) hpr = toon.getHpr() self.sendUpdate('setToonTrajectoryAi', [ netLaunchTime, startPos[0], startPos[1], startPos[2], hpr[0], hpr[1], hpr[2], self.lastVel[0], self.lastVel[1], self.lastVel[2] ]) self._lastBroadcastTime = now self._dirtyNewVel = None self.flyingToonOffsetRotation = 0 self.flyingToonOffsetAngle = 0 self.flyingToonOffsetX = 0 self.flyingToonOffsetY = 0 self.hitCloud = 2 pos = task.info['trajectory'].getPos(t) toon.setFluidPos(pos) toon.setFluidPos(toon, self.flyingToonOffsetX, self.flyingToonOffsetY, 0) vel = task.info['trajectory'].getVel(t) toon.lookAt(toon.getPos() + Vec3(vel[0], vel[1], vel[2])) toon.setP(toon.getP() - 90) cameraView = 2 if self.hitCloud == 2: self.lastStartVel = vel self.hitCloud = 0 shadowPos = toon.getPos() shadowPos.setZ(SHADOW_Z_OFFSET) self.localFlyingDropShadow.setPos(shadowPos) if pos.getZ() < -20 or pos.getZ() > 1000: self.notify.debug('stopping fly task toon.getZ()=%.2f' % pos.getZ()) self.__resetToonToCannon(self.localFlyingToon) return Task.done self.__setFlyingCameraView(task.info['toon'], cameraView, deltaT) return Task.cont def __setFlyingCameraView(self, toon, view, deltaT): if toon != base.localAvatar: return lookAt = toon.getPos(render) hpr = toon.getHpr(render) if view == 0: camera.wrtReparentTo(render) camera.lookAt(lookAt) elif view == 1: camera.reparentTo(render) camera.setPos(render, 100, 100, 35.25) camera.lookAt(render, lookAt) elif view == 2: if camera.getParent() != self.camNode: camera.wrtReparentTo(self.camNode) camera.setPos(self.cameraPos) camera.lookAt(toon) self.camNode.setPos(toon.getPos(render)) camHpr = self.camNode.getHpr(toon) vec = -Point3(0, 0, 0) - camHpr relativeSpeed = math.pow(vec.length() / 60.0, 2) + 0.1 newHpr = camHpr + vec * deltaT * self.cameraSpeed * relativeSpeed self.camNode.setHpr(toon, newHpr) camera.lookAt(self.camNode) camera.setR(render, 0) def __cleanupFlyingToonData(self, toon): self.notify.debug('__cleanupFlyingToonData') if toon: toon.dropShadow.show() self.toonIds.remove(toon.doId) if self.isLocalToon(toon): if self.localFlyingDropShadow != None: self.localFlyingDropShadow.removeNode() self.localFlyingDropShadow = None self.hitBumper = 0 self.angularVel = 0 self.vel = Vec3(0, 0, 0) self.lastVel = Vec3(0, 0, 0) self.lastPos = Vec3(0, 0, 0) self.landingPos = Vec3(0, 0, 0) self.t = 0 self.lastT = 0 self.deltaT = 0 self.lastWakeTime = 0 self.localFlyingToon = None self.localFlyingToonId = 0 self.localFiringCannon = None if hasattr(self, 'camNode') and self.camNode: self.camNode.removeNode() self.camNode = None return def __startCollisionHandler(self): self.flyColSphere = CollisionSphere( 0, 0, self.localFlyingToon.getHeight() / 2.0, 1.0) self.flyColNode = CollisionNode(self.uniqueName('flySphere')) self.flyColNode.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.FloorBitmask) self.flyColNode.addSolid(self.flyColSphere) self.flyColNodePath = self.localFlyingToon.attachNewNode( self.flyColNode) self.flyColNodePath.setColor(1, 0, 0, 1) self._activeCollisions = set() self.handler = CollisionHandlerQueue() self._flyingCollisionTaskName = 'checkFlyingToonCollision-%s' % self.doId taskMgr.add(self._checkFlyingToonCollision, self._flyingCollisionTaskName) base.cTrav.addCollider(self.flyColNodePath, self.handler) def __stopCollisionHandler(self, avatar): self.notify.debug('%s __stopCollisionHandler' % self.doId) if self._flyingCollisionTaskName: taskMgr.remove(self._flyingCollisionTaskName) self._flyingCollisionTaskName = None self._activeCollisions = set() if avatar: avatar.loop('neutral') if self.flyColNode: self.flyColNode = None self.flyColSphere = None if self.flyColNodePath: base.cTrav.removeCollider(self.flyColNodePath) self.flyColNodePath.removeNode() self.flyColNodePath = None self.handler = None return def _checkFlyingToonCollision(self, task=None): curCollisions = set() if self.handler.getNumEntries(): self.handler.sortEntries() i = self.handler.getNumEntries() activeEntry = None while i > 0: entry = self.handler.getEntry(i - 1) k = (str(entry.getFromNodePath()), str(entry.getIntoNodePath())) curCollisions.add(k) if activeEntry is None and k not in self._activeCollisions: activeEntry = entry self._activeCollisions.add(k) i -= 1 if activeEntry is not None: self.__handleFlyingToonCollision(activeEntry) if self.handler: self.handler.clearEntries() for k in list(self._activeCollisions): if k not in curCollisions: self._activeCollisions.remove(k) return Task.cont def __handleFlyingToonCollision(self, collisionEntry): self.notify.debug('%s __handleToonCollision' % self.doId) if self.localFlyingToon == None or self.flyColNode == None: return hitNode = collisionEntry.getIntoNode().getName() self.notify.debug('hitNode = %s' % hitNode) self.notify.debug('hitNodePath.getParent = %s' % collisionEntry.getIntoNodePath().getParent()) self.vel = self.trajectory.getVel(self.t) vel = self.trajectory.getVel(self.t) vel.normalize() if self.hitBumper: vel = self.lastVel * 1 vel.normalize() self.notify.debug('normalized vel=%s' % vel) solid = collisionEntry.getInto() intoNormal = collisionEntry.getSurfaceNormal( collisionEntry.getIntoNodePath()) self.notify.debug('old intoNormal = %s' % intoNormal) intoNormal = collisionEntry.getSurfaceNormal(render) self.notify.debug('new intoNormal = %s' % intoNormal) hitPylonAboveWater = False hitPylonBelowWater = False hitNormal = intoNormal if hitNode.find('cSphere') == 0 or hitNode.find( 'treasureSphere' ) == 0 or hitNode.find('prop') == 0 or hitNode.find( 'distAvatarCollNode' ) == 0 or hitNode.find('CannonSphere') == 0 or hitNode.find( 'plotSphere' ) == 0 or hitNode.find('flySphere') == 0 or hitNode.find( 'FishingSpotSphere' ) == 0 or hitNode.find( 'TrampolineTrigger' ) == 0 or hitNode == 'gagtree_collision' or hitNode == 'sign_collision' or hitNode == 'FlowerSellBox' or hitPylonBelowWater: self.notify.debug('--------------hit and ignoring %s' % hitNode) return if vel.dot( hitNormal ) > 0 and not hitNode == 'collision_roof' and not hitNode == 'collision_fence': self.notify.debug( '--------------hit and ignoring backfacing %s, dot=%s' % (hitNode, vel.dot(hitNormal))) return intoNode = collisionEntry.getIntoNodePath() bumperNodes = [ 'sky_collision' ] + PartyCannonCollisions['bounce'] + PartyCannonCollisions['fence'] cloudBumpers = PartyCannonCollisions['clouds'] bumperNodes += cloudBumpers if hitNode in bumperNodes or hitNode.find( 'cogPie' ) == 0 or PartyCannonCollisions['trampoline_bounce'] in hitNode: if hitNode == 'sky_collision' or hitNode in PartyCannonCollisions[ 'fence'] or hitNode.find('cogPie') == 0: self.__hitFence(self.localFlyingToon, collisionEntry) elif PartyCannonCollisions[ 'trampoline_bounce'] in hitNode or hitNode in PartyCannonCollisions[ 'bounce']: if hitNode == 'wall_collision': hitSound = self.sndBounce2 else: hitSound = self.sndBounce3 self.hitCloud = 1 self.__hitBumper(self.localFlyingToon, collisionEntry, hitSound, kr=0.09, angVel=5) self.hitBumper = 0 elif hitNode in cloudBumpers: self.__hitCloudPlatform(self.localFlyingToon, collisionEntry) elif hitNode == 'statuaryCol': self.__hitStatuary(self.localFlyingToon, collisionEntry) else: self.notify.debug( '*************** hit something else ************') return else: self.__stopCollisionHandler(self.localFlyingToon) self.__stopLocalFlyTask(self.localFlyingToonId) self.notify.debug('stopping flying since we hit %s' % hitNode) if self.isLocalToonId(self.localFlyingToon.doId): camera.wrtReparentTo(render) if self.localFlyingDropShadow: self.localFlyingDropShadow.reparentTo(hidden) pos = collisionEntry.getSurfacePoint(render) hpr = self.localFlyingToon.getHpr() hitPos = collisionEntry.getSurfacePoint(render) pos = hitPos self.landingPos = pos self.notify.debug('hitNode, Normal = %s,%s' % (hitNode, intoNormal)) track = Sequence() track.append(Func(self.localFlyingToon.wrtReparentTo, render)) if self.isLocalToonId(self.localFlyingToon.doId): track.append(Func(self.localFlyingToon.collisionsOff)) if hitNode in PartyCannonCollisions['ground']: track.append(Func(self.__hitGround, self.localFlyingToon, pos)) track.append(Wait(1.0)) track.append( Func(self.__setToonUpright, self.localFlyingToon, self.landingPos)) elif hitNode in PartyCannonCollisions['fence']: track.append( Func(self.__hitFence, self.localFlyingToon, collisionEntry)) elif hitNode == 'collision3': track.append( Func(self.__hitWater, self.localFlyingToon, pos, collisionEntry)) track.append(Wait(2.0)) track.append( Func(self.__setToonUpright, self.localFlyingToon, self.landingPos)) elif hitNode.find('cloudSphere') == 0: track.append( Func(self.__hitCloudPlatform, self.localFlyingToon, collisionEntry)) else: self.notify.warning( '************* unhandled hitNode=%s parent =%s' % (hitNode, collisionEntry.getIntoNodePath().getParent())) track.append(Func(self.d_setLanded, self.localFlyingToonId)) if self.isLocalToonId(self.localFlyingToonId): track.append(Func(self.localFlyingToon.collisionsOn)) if self.hitTrack: self.hitTrack.finish() self.hitTrack = track self.hitTrack.start() return def __hitBumper(self, avatar, collisionEntry, sound, kr=0.6, angVel=1): self.hitBumper = 1 base.playSfx(sound) hitP = avatar.getPos(render) self.lastPos = hitP normal = collisionEntry.getSurfaceNormal(render) self.notify.debug('normal = %s' % normal) vel = self.vel * 1 speed = vel.length() vel.normalize() self.notify.debug('old vel = %s' % vel) if self.hitCloud: centerVec = Vec3(-avatar.getPos(self.getParentNodePath())) centerVec.setZ(0) d = centerVec.length() / 15.0 centerVec.setZ(abs(centerVec.length() * math.sin(70.0))) centerVec.normalize() newVel = centerVec * d + normal * 0.2 newVel = newVel * (kr * speed) self.initialFlyVel = kr * speed else: newVel = (normal * 2.0 + vel) * (kr * speed) self.lastVel = newVel self.notify.debug('new vel = %s' % newVel) self.angularVel = angVel * 360 if self.hitCloud: return t = Sequence(Func(avatar.pose, 'lose', 110)) t.start() def __hitGround(self, avatar, pos, extraArgs=[]): self.notify.debug('__hitGround') hitP = avatar.getPos(render) self.notify.debug('hitGround pos = %s, hitP = %s' % (pos, hitP)) self.notify.debug('avatar hpr = %s' % avatar.getHpr()) avatar.setPos(pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) avatar.setHpr(avatar.getH(), -135, 0) self.notify.debug('parent = %s' % avatar.getParent()) self.notify.debug('pos = %s, hpr = %s' % (avatar.getPos(render), avatar.getHpr(render))) self.__playDustCloud(avatar, pos) base.playSfx(self.sndHitGround) avatar.setPlayRate(2.0, 'run') avatar.loop('run') def __playDustCloud(self, toon, pos): self.dustCloud.setPos(render, pos[0], pos[1], pos[2] + toon.getHeight() / 3.0) self.dustCloud.setScale(0.35) self.dustCloud.play() def __hitFence(self, avatar, collisionEntry, extraArgs=[]): self.notify.debug('__hitFence') self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.2, angVel=3) def __hitWater(self, avatar, pos, collisionEntry, extraArgs=[]): hitP = avatar.getPos(render) if hitP[2] > ToontownGlobals.EstateWakeWaterHeight: self.notify.debug('we hit the ground before we hit water') self.__hitGround(avatar, pos, extraArgs) return self.notify.debug('hit water') hitP = avatar.getPos(render) avatar.loop('neutral') self.splash.setPos(hitP) self.splash.setZ(ToontownGlobals.EstateWakeWaterHeight) self.splash.setScale(2) self.splash.play() base.playSfx(self.sndHitWater) place = base.cr.playGame.getPlace() def __hitStatuary(self, avatar, collisionEntry, extraArgs=[]): self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.4, angVel=5) def d_cloudsColorRequest(self): self.notify.debug('cloudsColorRequest') self.sendUpdate('cloudsColorRequest') def cloudsColorResponse(self, cloudColorList): self.notify.debug('cloudsColorResponse: %s' % cloudColorList) for cloudColor in cloudColorList: self.setCloudHit(*cloudColor) def d_requestCloudHit(self, cloudNumber, color): self.sendUpdate( 'requestCloudHit', [cloudNumber, color.getX(), color.getY(), color.getZ()]) def setCloudHit(self, cloudNumber, r, g, b): cloud = render.find('**/cloud-%d' % cloudNumber) if not cloud.isEmpty(): cloud.setColor(r, g, b, 1.0) else: self.notify.debug('Could not find cloud-%d' % cloudNumber) def __hitCloudPlatform(self, avatar, collisionEntry, extraArgs=[]): if not self.hitBumper and not self.hitCloud: self.hitCloud = 1 self.__hitBumper(avatar, collisionEntry, self.sndBounce1, kr=0.35, angVel=5) self.hitBumper = 0 if self._lastCloudHit is None: cloud = collisionEntry.getIntoNodePath().getParent() self._lastCloudHit = cloud cloud.setColor(base.localAvatar.style.getHeadColor()) cloudNumber = int(cloud.getNetTag('number')) self.d_requestCloudHit(cloudNumber, base.localAvatar.style.getHeadColor()) self.__playDustCloud(avatar, collisionEntry.getSurfacePoint(render)) self.flyingToonCloudsHit += 1 taskMgr.doMethodLater( 0.25, self.__reactivateLastCloudHit, DistributedPartyCannonActivity.REACTIVATE_CLOUD_TASK) return def __reactivateLastCloudHit(self, task): self._lastCloudHit = None return Task.done def __handleFireworksStarted(self): self.notify.debug('__handleFireworksStarted') base.cr.playGame.hood.loader.fadeClouds() def __handleFireworksFinished(self): self.notify.debug('__handleFireworksFinished') if self.__checkHoodValidity(): base.cr.playGame.hood.loader.fadeClouds() else: self.notify.debug('Toon has left the party') def __checkHoodValidity(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 True else: return False def handleToonExited(self, toonId): self.notify.debug( 'DistributedPartyCannonActivity handleToonExited( toonId=%s ) ' % toonId) if toonId in self.cr.doId2do: self.notify.warning('handleToonExited is not defined')
class DistributedPartyCannonActivity(DistributedPartyActivity): # I never go anywhere without my party cannon! notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPartyCannonActivity') HIT_GROUND = 0 HIT_TOWER = 1 HIT_WATER = 2 REACTIVATE_CLOUD_TASK = 'PartyActivity_ReactivateLastCloud' RULES_DONE_EVENT = 'DistributedPartyCannonActivity_RULES_DONE_EVENT' LOCAL_TOON_LANDED_EVENT = 'DistributedPartyCannonActivity_LOCAL_TOON_LANDED_EVENT' NetDivisor = 100 TimeFactor = 0.75 BroadcastPeriod = 0.2 def __init__(self, cr): DistributedPartyActivity.__init__(self, cr, ActivityIds.PartyCannon, ActivityTypes.Continuous, wantRewardGui=True) self.gui = None self.firingCannon = None self.shadowNode = None self.partyDoId = None self.splash = None self.dustCloud = None self.lastWakeTime = 0 self.localFlyingDropShadow = None self.localFlyingToon = None self.localFlyingToonId = 0 self._lastBroadcastTime = -self.BroadcastPeriod self._dirtyNewVel = None self.hitBumper = 0 self.hitCloud = 0 self.lastPos = Vec3(0, 0, 0) self.lastVel = Vec3(0, 0, 0) self.vel = Vec3(0, 0, 0) self.landingPos = Vec3(0, 0, 0) self.t = 0 self.lastT = 0 self.deltaT = 0 self._lastCloudHit = None self.cameraPos = Vec3(0, -15.0, -25.0) self.cameraSpeed = 5.0 self.camNode = None self.flyingToonOffsetRotation = 0 self.flyingToonOffsetAngle = 0 self.flyingToonOffsetX = 0 self.flyingToonOffsetY = 0 self.flyingToonCloudsHit = 0 self.initialFlyVel = 0 self._localPlayedBefore = False self.hitTrack = None self.cTrav = None self.flyColNode = None self.flyColNodePath = None self._flyingCollisionTaskName = None return def generateInit(self): DistributedPartyActivity.generateInit(self) self.taskNameFireCannon = self.taskName('fireCannon') self.taskNameShoot = self.taskName('shootTask') self.taskNameFly = self.taskName('flyTask') self.gui = CannonGui() def load(self): self.notify.debug('load') DistributedPartyActivity.load(self) base.cr.playGame.hood.loader.loadClouds() base.cr.playGame.hood.loader.setCloudSwitch(1) self.shadow = loader.loadModel('phase_3/models/props/drop_shadow') self.shadowNode = hidden.attachNewNode('dropShadow') self.shadow.copyTo(self.shadowNode) self.shadowNode.setColor(0, 0, 0, 0.5) self.shadowNode.setBin('fixed', 0, 1) self.splash = Splash.Splash(render) self.dustCloud = DustCloud.DustCloud(render) self.dustCloud.setBillboardPointEye() self.sndHitGround = base.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.mp3') self.sndHitWater = base.loadSfx('phase_4/audio/sfx/MG_cannon_splash.mp3') self.sndHitHouse = base.loadSfx('phase_5/audio/sfx/AA_drop_sandbag.mp3') self.sndBounce1 = base.loadSfx('phase_13/audio/sfx/bounce1.mp3') self.sndBounce2 = base.loadSfx('phase_13/audio/sfx/bounce2.mp3') self.sndBounce3 = base.loadSfx('phase_13/audio/sfx/bounce3.mp3') self.onstage() self.sign.reparentTo(hidden) self.sign.setPos(-6.0, 10.0, 0.0) self.accept(FireworksStartedEvent, self.__handleFireworksStarted) self.accept(FireworksFinishedEvent, self.__handleFireworksFinished) def generate(self): DistributedPartyActivity.generate(self) self._doneCannons = False self._avId2trajectoryInfo = {} self._remoteToonFlyTaskName = 'remoteToonFlyTask-%s' % self.doId taskMgr.add(self._remoteToonFlyTask, self._remoteToonFlyTaskName, priority=45) self.d_cloudsColorRequest() def unload(self): self.notify.debug('unload') DistributedPartyActivity.unload(self) if self.shadowNode is not None: self.shadowNode.removeNode() del self.shadowNode if self.splash is not None: self.splash.destroy() del self.splash if self.dustCloud is not None: self.dustCloud.destroy() del self.dustCloud del self.sndHitHouse del self.sndHitGround del self.sndHitWater del self.sndBounce1 del self.sndBounce2 del self.sndBounce3 if self.localFlyingToon: self.__resetToon(self.localFlyingToon) self.localFlyingToon.loop('neutral') self.localFlyingToon.setPlayRate(1.0, 'run') self.localFlyingToon = None self.ignoreAll() return def onstage(self): self.notify.debug('onstage') self.splash.reparentTo(render) self.dustCloud.reparentTo(render) def offstage(self): self.notify.debug('offstage') if self.splash is not None: self.splash.reparentTo(hidden) self.splash.stop() if self.dustCloud is not None: self.dustCloud.reparentTo(hidden) self.dustCloud.stop() return def disable(self): taskMgr.remove(self._remoteToonFlyTaskName) if self._flyingCollisionTaskName: taskMgr.remove(self._flyingCollisionTaskName) taskMgr.remove(self.taskNameFireCannon) taskMgr.remove(self.taskNameShoot) taskMgr.remove(self.taskNameFly) taskMgr.remove(DistributedPartyCannonActivity.REACTIVATE_CLOUD_TASK) self.ignoreAll() if self.localFlyingToonId: self.__stopCollisionHandler(self.localFlyingToon) self.__stopLocalFlyTask(self.localFlyingToonId) self.setMovie(PartyGlobals.CANNON_MOVIE_CLEAR, self.localFlyingToonId) if self.hitTrack is not None: self.hitTrack.finish() del self.hitTrack self.hitTrack = None DistributedPartyActivity.disable(self) return def delete(self): self.offstage() DistributedPartyActivity.delete(self) def setMovie(self, mode, toonId): self.notify.debug('%s setMovie(%s, %s)' % (self.doId, toonId, mode)) if toonId != base.localAvatar.doId: return if mode == PartyGlobals.CANNON_MOVIE_CLEAR: self.landToon(toonId) elif mode == PartyGlobals.CANNON_MOVIE_LANDED: self.landToon(toonId) elif mode == PartyGlobals.CANNON_MOVIE_FORCE_EXIT: self.landToon(toonId) def __handleAvatarGone(self): self.setMovie(PartyGlobals.CANNON_MOVIE_CLEAR, 0) def handleToonDisabled(self, toonId): self.notify.warning('handleToonDisabled no implementation yet') def handleToonJoined(self, toonId): self.notify.warning('handleToonJoined no implementation yet') def isLocalToon(self, av): return base.localAvatar == av def isLocalToonId(self, toonId): return base.localAvatar.doId == toonId def getTitle(self): return TTLocalizer.PartyCannonActivityTitle def getInstructions(self): return TTLocalizer.PartyCannonActivityInstructions def hasPlayedBefore(self): return self._localPlayedBefore def displayRules(self): self.startRules() def handleRulesDone(self): self.finishRules() self._localPlayedBefore = True messenger.send(DistributedPartyCannonActivity.RULES_DONE_EVENT) def setCannonWillFire(self, cannonId, zRot, angle): self.notify.debug('setCannonWillFire: %d %d %d' % (cannonId, zRot, angle)) cannon = base.cr.doId2do.get(cannonId) if cannon is None: self.notify.warning("Cannon has not been created, but we got this message. Don't show firing.") return if not cannon.getToonInside(): self.notify.warning("setCannonWillFire, but no toon insde. Don't show firing") return if self.isLocalToon(cannon.getToonInside()): self.localFlyingToon = base.localAvatar self.localFlyingToonId = base.localAvatar.doId self.localFiringCannon = cannon self.flyingToonCloudsHit = 0 cannon.updateModel(zRot, angle) toonId = cannon.getToonInside().doId task = Task(self.__fireCannonTask) task.toonId = toonId task.cannon = cannon taskMgr.add(task, self.taskNameFireCannon) self.toonIds.append(toonId) return def __fireCannonTask(self, task): launchTime = 0.0 toonId = task.toonId cannon = task.cannon toon = cannon.getToonInside() self.notify.debug(str(self.doId) + ' FIRING CANNON FOR TOON ' + str(toonId)) if not cannon.isToonInside(): return Task.done if self.isLocalToonId(toonId): self.inWater = 0 flightResults = self.__calcFlightResults(cannon, toonId, launchTime) if not isClient(): print 'EXECWARNING DistributedPartyCannonActivity: %s' % flightResults printStack() for key in flightResults: exec "%s = flightResults['%s']" % (key, key) self.notify.debug('start position: ' + str(startPos)) self.notify.debug('start velocity: ' + str(startVel)) self.notify.debug('time of launch: ' + str(launchTime)) cannon.removeToonReadyToFire() shootTask = Task(self.__shootTask, self.taskNameShoot) shootTask.info = {'toonId': toonId, 'cannon': cannon} if self.isLocalToonId(toonId): self.flyingToonOffsetRotation = 0 self.flyingToonOffsetAngle = 0 self.flyingToonOffsetX = 0 self.flyingToonOffsetY = 0 self.hitCloud = 0 self.initialFlyVel = INITIAL_VELOCITY self.camNode = NodePath(self.uniqueName('flyingCamera')) self.camNode.setScale(0.5) self.camNode.setPos(self.localFlyingToon.getPos()) self.camNode.setHpr(self.localFlyingToon.getHpr()) self.camNode.reparentTo(render) self.lastStartVel = startVel place = base.cr.playGame.getPlace() place.fsm.request('activity') toon.dropShadow.hide() self.localFlyingDropShadow = self.shadowNode.copyTo(hidden) vel = startVel toon.lookAt(toon.getPos() + Vec3(vel[0], vel[1], vel[2])) toon.setP(localAvatar, -90) hpr = toon.getHpr() toon.d_setPosHpr(startPos[0], startPos[1], startPos[2], hpr[0], hpr[1], hpr[2]) self.localFlyingToon.wrtReparentTo(render) info = {} info['toonId'] = toonId info['trajectory'] = trajectory info['launchTime'] = launchTime info['toon'] = self.localFlyingToon info['hRot'] = cannon.getRotation() camera.wrtReparentTo(self.localFlyingToon) flyTask = Task(self.__localFlyTask, self.taskNameFly) flyTask.info = info seqTask = Task.sequence(shootTask, flyTask) self.__startCollisionHandler() self.notify.debug('Disable standard local toon controls.') base.localAvatar.disableAvatarControls() frameTime = globalClock.getFrameTime() netLaunchTime = globalClockDelta.localToNetworkTime(launchTime + frameTime, bits=31) self.sendUpdate('setToonTrajectoryAi', [netLaunchTime, startPos[0], startPos[1], startPos[2], startHpr[0], startHpr[1], startHpr[2], startVel[0], startVel[1], startVel[2]]) else: seqTask = shootTask taskMgr.add(seqTask, self.taskName('flyingToon') + '-' + str(toonId)) toon.startSmooth() return Task.done def setToonTrajectory(self, avId, launchTime, x, y, z, h, p, r, vx, vy, vz): if avId == localAvatar.doId: return startPos = Vec3(x, y, z) startHpr = Vec3(h, p, r) startVel = Vec3(vx, vy, vz) startT = globalClockDelta.networkToLocalTime(launchTime, bits=31) + 0.2 trajectory = Trajectory.Trajectory(0.0, startPos, startVel) self._avId2trajectoryInfo[avId] = ScratchPad(startPos=startPos, startHpr=startHpr, startVel=startVel, startT=startT, trajectory=trajectory) def _remoteToonFlyTask(self, task = None): ids2del = [] frameTime = globalClock.getFrameTime() for avId, trajInfo in self._avId2trajectoryInfo.iteritems(): trajectory = trajInfo.trajectory startTime = trajInfo.startT groundTime = trajectory.calcTimeOfImpactOnPlane(0.0) / self.TimeFactor + startTime now = frameTime if now < startTime: now = startTime if now > groundTime: now = groundTime t = max(0.0, now - startTime) t *= self.TimeFactor toon = self.cr.getDo(avId) if toon is None: ids2del.append(avId) else: toon.setFluidPos(trajectory.getPos(t)) vel = trajectory.getVel(t) toon.lookAt(toon.getPos() + Vec3(vel[0], vel[1], vel[2])) toon.setP(toon, -90) for avId in ids2del: del self._avId2trajectoryInfo[avId] return Task.cont def __calcFlightResults(self, cannon, toonId, launchTime): startPos = cannon.getToonFirePos() startHpr = cannon.getToonFireHpr() startVel = cannon.getToonFireVel() quantizeVec(startPos, self.NetDivisor) quantizeVec(startHpr, self.NetDivisor) quantizeVec(startVel, self.NetDivisor) trajectory = Trajectory.Trajectory(launchTime, startPos, startVel) self.trajectory = trajectory return {'startPos': startPos, 'startHpr': startHpr, 'startVel': startVel, 'trajectory': trajectory} def __shootTask(self, task): task.info['cannon'].fire() toonId = task.info['toonId'] toon = base.cr.doId2do.get(toonId) if toon: toon.loop('swim') else: self.notify.debug('__shootTask avoided a crash, toon %d not found' % toonId) if self.isLocalToonId(task.info['toonId']): self.localFlyingDropShadow.reparentTo(render) self.gui.enableAimKeys() return Task.done def d_setLanded(self, toonId): printStack() self.notify.debug('d_setLanded %s' % toonId) if self.isLocalToonId(toonId): if self.cr: self.sendUpdate('setLanded', [toonId]) else: self.notify.debug('we avoided crash 2') def setLanded(self, toonId): if toonId in self._avId2trajectoryInfo: del self._avId2trajectoryInfo[toonId] def landToon(self, toonId): self.notify.debug('%s landToon' % self.doId) toon = base.cr.doId2do.get(toonId) if toon is not None: toon.resetLOD() if toon == base.localAvatar: self.__stopCollisionHandler(base.localAvatar) toon.wrtReparentTo(render) self.__setToonUpright(toon) toon.setPlayRate(1.0, 'run') toon.startSmooth() toon.setScale(1.0) self.ignore(toon.uniqueName('disable')) self.__cleanupFlyingToonData(toon) toon.dropShadow.show() place = base.cr.playGame.getPlace() if place is not None: if not hasattr(place, 'fsm'): return if toon is not None and toon == base.localAvatar: self.__localDisplayLandedResults() return def __localDisplayLandedResults(self): if self.flyingToonCloudsHit > 0: self._doneCannons = True else: self.__localToonDoneLanding() def handleRewardDone(self): DistributedPartyActivity.handleRewardDone(self) if self._doneCannons: self.__localToonDoneLanding() def __localToonDoneLanding(self): base.cr.playGame.getPlace().fsm.request('walk') self.notify.debug('__localToonDoneLanding') base.localAvatar.collisionsOn() base.localAvatar.startPosHprBroadcast() base.localAvatar.enableAvatarControls() messenger.send(DistributedPartyCannonActivity.LOCAL_TOON_LANDED_EVENT) def __setToonUpright(self, toon, pos = None): if toon: if self.inWater: toon.setP(0) toon.setR(0) return if not pos: pos = toon.getPos(render) toon.setPos(render, pos) toon.loop('neutral') if self.localFiringCannon and hasattr(self.localFiringCannon, 'cannonNode'): if self.localFiringCannon.cannonNode: toon.lookAt(self.localFiringCannon.cannonNode) else: self.notify.debug('we avoided crash 1.') toon.setP(0) toon.setR(0) toon.setScale(1, 1, 1) def __resetToonToCannon(self, avatar): self.notify.debug('__resetToonToCannon') if not avatar and self.localFlyingToonId: avatar = base.cr.doId2do.get(self.localFlyingToonId, None) if avatar: self.__resetToon(avatar) return def __resetToon(self, avatar, pos = None): self.notify.debug('__resetToon') if avatar: self.__stopCollisionHandler(avatar) self.__setToonUpright(avatar, pos) if self.isLocalToonId(avatar.doId): self.notify.debug('toon setting position to %s' % pos) if pos: base.localAvatar.setPos(pos) camera.reparentTo(avatar) self.d_setLanded(avatar.doId) def __updateFlightVelocity(self, trajectory): hpr = LRotationf(self.flyingToonOffsetRotation, 0, 0) newVel = hpr.xform(self.lastStartVel) hpr = LRotationf(0, self.flyingToonOffsetAngle, 0) zVel = hpr.xform(self.lastStartVel).getZ() if zVel < newVel.getZ(): newVel.setZ(zVel) trajectory.setStartVel(newVel) now = globalClock.getFrameTime() if now - self._lastBroadcastTime >= self.BroadcastPeriod: self._dirtyNewVel = newVel if self._dirtyNewVel: self.sendUpdate('updateToonTrajectoryStartVelAi', [self._dirtyNewVel[0], self._dirtyNewVel[1], self._dirtyNewVel[2]]) self._lastBroadcastTime = now self._dirtyNewVel = None return def updateToonTrajectoryStartVel(self, avId, vx, vy, vz): if avId == localAvatar.doId: return if avId in self._avId2trajectoryInfo: self._avId2trajectoryInfo[avId].trajectory.setStartVel(Vec3(vx, vy, vz)) def __isFlightKeyPressed(self): return self.gui.leftPressed or self.gui.rightPressed or self.gui.upPressed or self.gui.downPressed def __moveFlyingToon(self, toon): toonP = toon.getP(render) isToonFlyingHorizontal = toonP > -150 and toonP < -30 OFFSET = 0.25 rotVel = 0 if self.gui.leftPressed: if isToonFlyingHorizontal: rotVel += CANNON_ROTATION_VEL else: self.flyingToonOffsetX -= OFFSET if self.gui.rightPressed: if isToonFlyingHorizontal: rotVel -= CANNON_ROTATION_VEL else: self.flyingToonOffsetX += OFFSET self.flyingToonOffsetRotation += rotVel * globalClock.getDt() angVel = 0 if self.gui.upPressed: if not isToonFlyingHorizontal: self.flyingToonOffsetY -= OFFSET if self.gui.downPressed: if isToonFlyingHorizontal: angVel += CANNON_ANGLE_VEL else: self.flyingToonOffsetY += OFFSET self.flyingToonOffsetAngle += angVel * globalClock.getDt() def __stopLocalFlyTask(self, toonId): taskMgr.remove(self.taskName('flyingToon') + '-' + str(toonId)) self.gui.disableAimKeys() def __localFlyTask(self, task): toon = task.info['toon'] if toon.isEmpty(): self.__resetToonToCannon(self.localFlyingToon) return Task.done curTime = task.time + task.info['launchTime'] t = curTime t *= self.TimeFactor self.lastT = self.t self.t = t deltaT = self.t - self.lastT self.deltaT = deltaT if self.hitBumper: pos = self.lastPos + self.lastVel * deltaT vel = self.lastVel self.lastVel += Vec3(0, 0, -32.0) * deltaT self.lastPos = pos toon.setFluidPos(pos) lastR = toon.getR() toon.setR(lastR - deltaT * self.angularVel * 2.0) cameraView = 0 else: if not self.hitCloud and self.__isFlightKeyPressed(): self.__moveFlyingToon(toon) self.__updateFlightVelocity(task.info['trajectory']) if self.hitCloud == 1: vel = task.info['trajectory'].getVel(t) startPos = toon.getPos(render) task.info['trajectory'].setStartTime(t) task.info['trajectory'].setStartPos(startPos) task.info['trajectory'].setStartVel(self.lastVel) toon.lookAt(toon.getPos() + vel) toon.setH(-toon.getH()) now = globalClock.getFrameTime() netLaunchTime = globalClockDelta.localToNetworkTime(now, bits=31) hpr = toon.getHpr() self.sendUpdate('setToonTrajectoryAi', [netLaunchTime, startPos[0], startPos[1], startPos[2], hpr[0], hpr[1], hpr[2], self.lastVel[0], self.lastVel[1], self.lastVel[2]]) self._lastBroadcastTime = now self._dirtyNewVel = None self.flyingToonOffsetRotation = 0 self.flyingToonOffsetAngle = 0 self.flyingToonOffsetX = 0 self.flyingToonOffsetY = 0 self.hitCloud = 2 pos = task.info['trajectory'].getPos(t) toon.setFluidPos(pos) toon.setFluidPos(toon, self.flyingToonOffsetX, self.flyingToonOffsetY, 0) vel = task.info['trajectory'].getVel(t) toon.lookAt(toon.getPos() + Vec3(vel[0], vel[1], vel[2])) toon.setP(toon.getP() - 90) cameraView = 2 if self.hitCloud == 2: self.lastStartVel = vel self.hitCloud = 0 shadowPos = toon.getPos() shadowPos.setZ(SHADOW_Z_OFFSET) self.localFlyingDropShadow.setPos(shadowPos) if pos.getZ() < -20 or pos.getZ() > 1000: self.notify.debug('stopping fly task toon.getZ()=%.2f' % pos.getZ()) self.__resetToonToCannon(self.localFlyingToon) return Task.done self.__setFlyingCameraView(task.info['toon'], cameraView, deltaT) return Task.cont def __setFlyingCameraView(self, toon, view, deltaT): if toon != base.localAvatar: return lookAt = toon.getPos(render) hpr = toon.getHpr(render) if view == 0: camera.wrtReparentTo(render) camera.lookAt(lookAt) elif view == 1: camera.reparentTo(render) camera.setPos(render, 100, 100, 35.25) camera.lookAt(render, lookAt) elif view == 2: if camera.getParent() != self.camNode: camera.wrtReparentTo(self.camNode) camera.setPos(self.cameraPos) camera.lookAt(toon) self.camNode.setPos(toon.getPos(render)) camHpr = self.camNode.getHpr(toon) vec = -Point3(0, 0, 0) - camHpr relativeSpeed = math.pow(vec.length() / 60.0, 2) + 0.1 newHpr = camHpr + vec * deltaT * self.cameraSpeed * relativeSpeed self.camNode.setHpr(toon, newHpr) camera.lookAt(self.camNode) camera.setR(render, 0) def __cleanupFlyingToonData(self, toon): self.notify.debug('__cleanupFlyingToonData') if toon: toon.dropShadow.show() self.toonIds.remove(toon.doId) if self.isLocalToon(toon): if self.localFlyingDropShadow != None: self.localFlyingDropShadow.removeNode() self.localFlyingDropShadow = None self.hitBumper = 0 self.angularVel = 0 self.vel = Vec3(0, 0, 0) self.lastVel = Vec3(0, 0, 0) self.lastPos = Vec3(0, 0, 0) self.landingPos = Vec3(0, 0, 0) self.t = 0 self.lastT = 0 self.deltaT = 0 self.lastWakeTime = 0 self.localFlyingToon = None self.localFlyingToonId = 0 self.localFiringCannon = None if hasattr(self, 'camNode') and self.camNode: self.camNode.removeNode() self.camNode = None return def __startCollisionHandler(self): self.flyColSphere = CollisionSphere(0, 0, self.localFlyingToon.getHeight() / 2.0, 1.0) self.flyColNode = CollisionNode(self.uniqueName('flySphere')) self.flyColNode.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.FloorBitmask) self.flyColNode.addSolid(self.flyColSphere) self.flyColNodePath = self.localFlyingToon.attachNewNode(self.flyColNode) self.flyColNodePath.setColor(1, 0, 0, 1) self._activeCollisions = set() self.handler = CollisionHandlerQueue() self._flyingCollisionTaskName = 'checkFlyingToonCollision-%s' % self.doId taskMgr.add(self._checkFlyingToonCollision, self._flyingCollisionTaskName) base.cTrav.addCollider(self.flyColNodePath, self.handler) def __stopCollisionHandler(self, avatar): self.notify.debug('%s __stopCollisionHandler' % self.doId) if self._flyingCollisionTaskName: taskMgr.remove(self._flyingCollisionTaskName) self._flyingCollisionTaskName = None self._activeCollisions = set() if avatar: avatar.loop('neutral') if self.flyColNode: self.flyColNode = None self.flyColSphere = None if self.flyColNodePath: base.cTrav.removeCollider(self.flyColNodePath) self.flyColNodePath.removeNode() self.flyColNodePath = None self.handler = None return def _checkFlyingToonCollision(self, task = None): curCollisions = set() if self.handler.getNumEntries(): self.handler.sortEntries() i = self.handler.getNumEntries() activeEntry = None while i > 0: entry = self.handler.getEntry(i - 1) k = (str(entry.getFromNodePath()), str(entry.getIntoNodePath())) curCollisions.add(k) if activeEntry is None and k not in self._activeCollisions: activeEntry = entry self._activeCollisions.add(k) i -= 1 if activeEntry is not None: self.__handleFlyingToonCollision(activeEntry) if self.handler: self.handler.clearEntries() for k in list(self._activeCollisions): if k not in curCollisions: self._activeCollisions.remove(k) return Task.cont def __handleFlyingToonCollision(self, collisionEntry): self.notify.debug('%s __handleToonCollision' % self.doId) if self.localFlyingToon == None or self.flyColNode == None: return hitNode = collisionEntry.getIntoNode().getName() self.notify.debug('hitNode = %s' % hitNode) self.notify.debug('hitNodePath.getParent = %s' % collisionEntry.getIntoNodePath().getParent()) self.vel = self.trajectory.getVel(self.t) vel = self.trajectory.getVel(self.t) vel.normalize() if self.hitBumper: vel = self.lastVel * 1 vel.normalize() self.notify.debug('normalized vel=%s' % vel) solid = collisionEntry.getInto() intoNormal = collisionEntry.getSurfaceNormal(collisionEntry.getIntoNodePath()) self.notify.debug('old intoNormal = %s' % intoNormal) intoNormal = collisionEntry.getSurfaceNormal(render) self.notify.debug('new intoNormal = %s' % intoNormal) hitPylonAboveWater = False hitPylonBelowWater = False hitNormal = intoNormal if hitNode.find('cSphere') == 0 or hitNode.find('treasureSphere') == 0 or hitNode.find('prop') == 0 or hitNode.find('distAvatarCollNode') == 0 or hitNode.find('CannonSphere') == 0 or hitNode.find('plotSphere') == 0 or hitNode.find('flySphere') == 0 or hitNode.find('FishingSpotSphere') == 0 or hitNode.find('TrampolineTrigger') == 0 or hitNode == 'gagtree_collision' or hitNode == 'sign_collision' or hitNode == 'FlowerSellBox' or hitPylonBelowWater: self.notify.debug('--------------hit and ignoring %s' % hitNode) return if vel.dot(hitNormal) > 0 and not hitNode == 'collision_roof' and not hitNode == 'collision_fence': self.notify.debug('--------------hit and ignoring backfacing %s, dot=%s' % (hitNode, vel.dot(hitNormal))) return intoNode = collisionEntry.getIntoNodePath() bumperNodes = ['sky_collision'] + PartyCannonCollisions['bounce'] + PartyCannonCollisions['fence'] cloudBumpers = PartyCannonCollisions['clouds'] bumperNodes += cloudBumpers if hitNode in bumperNodes or hitNode.find('cogPie') == 0 or PartyCannonCollisions['trampoline_bounce'] in hitNode: if hitNode == 'sky_collision' or hitNode in PartyCannonCollisions['fence'] or hitNode.find('cogPie') == 0: self.__hitFence(self.localFlyingToon, collisionEntry) elif PartyCannonCollisions['trampoline_bounce'] in hitNode or hitNode in PartyCannonCollisions['bounce']: if hitNode == 'wall_collision': hitSound = self.sndBounce2 else: hitSound = self.sndBounce3 self.hitCloud = 1 self.__hitBumper(self.localFlyingToon, collisionEntry, hitSound, kr=0.09, angVel=5) self.hitBumper = 0 elif hitNode in cloudBumpers: self.__hitCloudPlatform(self.localFlyingToon, collisionEntry) elif hitNode == 'statuaryCol': self.__hitStatuary(self.localFlyingToon, collisionEntry) else: self.notify.debug('*************** hit something else ************') return else: self.__stopCollisionHandler(self.localFlyingToon) self.__stopLocalFlyTask(self.localFlyingToonId) self.notify.debug('stopping flying since we hit %s' % hitNode) if self.isLocalToonId(self.localFlyingToon.doId): camera.wrtReparentTo(render) if self.localFlyingDropShadow: self.localFlyingDropShadow.reparentTo(hidden) pos = collisionEntry.getSurfacePoint(render) hpr = self.localFlyingToon.getHpr() hitPos = collisionEntry.getSurfacePoint(render) pos = hitPos self.landingPos = pos self.notify.debug('hitNode, Normal = %s,%s' % (hitNode, intoNormal)) track = Sequence() track.append(Func(self.localFlyingToon.wrtReparentTo, render)) if self.isLocalToonId(self.localFlyingToon.doId): track.append(Func(self.localFlyingToon.collisionsOff)) if hitNode in PartyCannonCollisions['ground']: track.append(Func(self.__hitGround, self.localFlyingToon, pos)) track.append(Wait(1.0)) track.append(Func(self.__setToonUpright, self.localFlyingToon, self.landingPos)) elif hitNode in PartyCannonCollisions['fence']: track.append(Func(self.__hitFence, self.localFlyingToon, collisionEntry)) elif hitNode == 'collision3': track.append(Func(self.__hitWater, self.localFlyingToon, pos, collisionEntry)) track.append(Wait(2.0)) track.append(Func(self.__setToonUpright, self.localFlyingToon, self.landingPos)) elif hitNode.find('cloudSphere') == 0: track.append(Func(self.__hitCloudPlatform, self.localFlyingToon, collisionEntry)) else: self.notify.warning('************* unhandled hitNode=%s parent =%s' % (hitNode, collisionEntry.getIntoNodePath().getParent())) track.append(Func(self.d_setLanded, self.localFlyingToonId)) if self.isLocalToonId(self.localFlyingToonId): track.append(Func(self.localFlyingToon.collisionsOn)) if self.hitTrack: self.hitTrack.finish() self.hitTrack = track self.hitTrack.start() return def __hitBumper(self, avatar, collisionEntry, sound, kr = 0.6, angVel = 1): self.hitBumper = 1 base.playSfx(sound) hitP = avatar.getPos(render) self.lastPos = hitP normal = collisionEntry.getSurfaceNormal(render) self.notify.debug('normal = %s' % normal) vel = self.vel * 1 speed = vel.length() vel.normalize() self.notify.debug('old vel = %s' % vel) if self.hitCloud: centerVec = Vec3(-avatar.getPos(self.getParentNodePath())) centerVec.setZ(0) d = centerVec.length() / 15.0 centerVec.setZ(abs(centerVec.length() * math.sin(70.0))) centerVec.normalize() newVel = centerVec * d + normal * 0.2 newVel = newVel * (kr * speed) self.initialFlyVel = kr * speed else: newVel = (normal * 2.0 + vel) * (kr * speed) self.lastVel = newVel self.notify.debug('new vel = %s' % newVel) self.angularVel = angVel * 360 if self.hitCloud: return t = Sequence(Func(avatar.pose, 'lose', 110)) t.start() def __hitGround(self, avatar, pos, extraArgs = []): self.notify.debug('__hitGround') hitP = avatar.getPos(render) self.notify.debug('hitGround pos = %s, hitP = %s' % (pos, hitP)) self.notify.debug('avatar hpr = %s' % avatar.getHpr()) avatar.setPos(pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) avatar.setHpr(avatar.getH(), -135, 0) self.notify.debug('parent = %s' % avatar.getParent()) self.notify.debug('pos = %s, hpr = %s' % (avatar.getPos(render), avatar.getHpr(render))) self.__playDustCloud(avatar, pos) base.playSfx(self.sndHitGround) avatar.setPlayRate(2.0, 'run') avatar.loop('run') def __playDustCloud(self, toon, pos): self.dustCloud.setPos(render, pos[0], pos[1], pos[2] + toon.getHeight() / 3.0) self.dustCloud.setScale(0.35) self.dustCloud.play() def __hitFence(self, avatar, collisionEntry, extraArgs = []): self.notify.debug('__hitFence') self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.2, angVel=3) def __hitWater(self, avatar, pos, collisionEntry, extraArgs = []): hitP = avatar.getPos(render) if hitP[2] > ToontownGlobals.EstateWakeWaterHeight: self.notify.debug('we hit the ground before we hit water') self.__hitGround(avatar, pos, extraArgs) return self.notify.debug('hit water') hitP = avatar.getPos(render) avatar.loop('neutral') self.splash.setPos(hitP) self.splash.setZ(ToontownGlobals.EstateWakeWaterHeight) self.splash.setScale(2) self.splash.play() base.playSfx(self.sndHitWater) place = base.cr.playGame.getPlace() def __hitStatuary(self, avatar, collisionEntry, extraArgs = []): self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.4, angVel=5) def d_cloudsColorRequest(self): self.notify.debug('cloudsColorRequest') self.sendUpdate('cloudsColorRequest') def cloudsColorResponse(self, cloudColorList): self.notify.debug('cloudsColorResponse: %s' % cloudColorList) for cloudColor in cloudColorList: self.setCloudHit(*cloudColor) def d_requestCloudHit(self, cloudNumber, color): self.sendUpdate('requestCloudHit', [cloudNumber, color.getX(), color.getY(), color.getZ()]) def setCloudHit(self, cloudNumber, r, g, b): cloud = render.find('**/cloud-%d' % cloudNumber) if not cloud.isEmpty(): cloud.setColor(r, g, b, 1.0) else: self.notify.debug('Could not find cloud-%d' % cloudNumber) def __hitCloudPlatform(self, avatar, collisionEntry, extraArgs = []): if not self.hitBumper and not self.hitCloud: self.hitCloud = 1 self.__hitBumper(avatar, collisionEntry, self.sndBounce1, kr=0.35, angVel=5) self.hitBumper = 0 if self._lastCloudHit is None: cloud = collisionEntry.getIntoNodePath().getParent() self._lastCloudHit = cloud cloud.setColor(base.localAvatar.style.getHeadColor()) cloudNumber = int(cloud.getNetTag('number')) self.d_requestCloudHit(cloudNumber, base.localAvatar.style.getHeadColor()) self.__playDustCloud(avatar, collisionEntry.getSurfacePoint(render)) self.flyingToonCloudsHit += 1 taskMgr.doMethodLater(0.25, self.__reactivateLastCloudHit, DistributedPartyCannonActivity.REACTIVATE_CLOUD_TASK) return def __reactivateLastCloudHit(self, task): self._lastCloudHit = None return Task.done def __handleFireworksStarted(self): self.notify.debug('__handleFireworksStarted') base.cr.playGame.hood.loader.fadeClouds() def __handleFireworksFinished(self): self.notify.debug('__handleFireworksFinished') if self.__checkHoodValidity(): base.cr.playGame.hood.loader.fadeClouds() else: self.notify.debug('Toon has left the party') def __checkHoodValidity(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 True else: return False def handleToonExited(self, toonId): self.notify.debug('DistributedPartyCannonActivity handleToonExited( toonId=%s ) ' % toonId) if self.cr.doId2do.has_key(toonId): self.notify.warning('handleToonExited is not defined')
class DistributedPartyCannonActivity(DistributedPartyActivity): notify = DirectNotifyGlobal.directNotify.newCategory( "DistributedPartyCannonActivity") # flags for objects that the toons might hit HIT_GROUND = 0 HIT_TOWER = 1 HIT_WATER = 2 REACTIVATE_CLOUD_TASK = "PartyActivity_ReactivateLastCloud" RULES_DONE_EVENT = "DistributedPartyCannonActivity_RULES_DONE_EVENT" LOCAL_TOON_LANDED_EVENT = "DistributedPartyCannonActivity_LOCAL_TOON_LANDED_EVENT" def __init__(self, cr): DistributedPartyActivity.__init__( self, cr, ActivityIds.PartyCannon, ActivityTypes.Continuous, wantRewardGui=True, ) self.gui = None self.firingCannon = None self.shadowNode = None self.partyDoId = None self.splash = None self.dustCloud = None self.lastWakeTime = 0 # Flying info # This is local. Every client controls flight. self.localFlyingDropShadow = None self.localFlyingToon = None self.localFlyingToonId = 0 self.hitBumper = 0 self.hitCloud = 0 self.lastPos = Vec3(0, 0, 0) self.lastVel = Vec3(0, 0, 0) self.vel = Vec3(0, 0, 0) self.landingPos = Vec3(0, 0, 0) self.t = 0 self.lastT = 0 self.deltaT = 0 self._lastCloudHit = None self.cameraPos = Vec3(0, -15.0, -25.0) self.cameraSpeed = 5.0 self.camNode = None self.flyingToonOffsetRotation = 0 self.flyingToonOffsetAngle = 0 self.flyingToonOffsetX = 0 self.flyingToonOffsetY = 0 self.flyingToonCloudsHit = 0 self.initialFlyVel = 0 self._localPlayedBefore = False self.hitTrack = None self.cTrav = None # for collisions self.flyColNode = None self.flyColNodePath = None self._flyingCollisionTaskName = None def generateInit(self): DistributedPartyActivity.generateInit(self) self.taskNameFireCannon = self.taskName("fireCannon") self.taskNameShoot = self.taskName("shootTask") self.taskNameFly = self.taskName("flyTask") self.gui = CannonGui() # Load is called by the DistributedPartyActivity.announceGenerate def load(self): self.notify.debug("load") DistributedPartyActivity.load(self) # Show clouds. base.cr.playGame.hood.loader.loadClouds() base.cr.playGame.hood.loader.setCloudSwitch(1) # The shadow is used while the local toon is flying around self.shadow = loader.loadModel("phase_3/models/props/drop_shadow") self.shadowNode = hidden.attachNewNode("dropShadow") self.shadow.copyTo(self.shadowNode) self.shadowNode.setColor(0, 0, 0, 0.5) # put the shadow in the 'fixed' bin, so that it will be drawn correctly # in front of the translucent water. # NOTE: if we put trees or other opaque/transparent objects in the scene, # put the shadow in the fixed bin only when it's over the water. self.shadowNode.setBin('fixed', 0, 1) # undo with shadow.clearBin() # Splash object for when toon hits the water self.splash = Splash.Splash(render) # Dust cloud object for when toon hits ground self.dustCloud = DustCloud.DustCloud(render) self.dustCloud.setBillboardPointEye() # Collision Sounds self.sndHitGround = base.loader.loadSfx( "phase_4/audio/sfx/MG_cannon_hit_dirt.mp3") self.sndHitWater = base.loader.loadSfx( "phase_4/audio/sfx/MG_cannon_splash.mp3") self.sndHitHouse = base.loader.loadSfx( "phase_5/audio/sfx/AA_drop_sandbag.mp3") self.sndBounce1 = base.loader.loadSfx("phase_13/audio/sfx/bounce1.mp3") self.sndBounce2 = base.loader.loadSfx("phase_13/audio/sfx/bounce2.mp3") self.sndBounce3 = base.loader.loadSfx("phase_13/audio/sfx/bounce3.mp3") self.onstage() self.sign.reparentTo(hidden) self.sign.setPos(-6.0, 10.0, 0.0) self.accept(FireworksStartedEvent, self.__handleFireworksStarted) self.accept(FireworksFinishedEvent, self.__handleFireworksFinished) def generate(self): DistributedPartyActivity.generate(self) self._doneCannons = False # Request cloud colors. This is done in case the toon walks in # and clouds have been colored. self.d_cloudsColorRequest() def unload(self): self.notify.debug("unload") DistributedPartyActivity.unload(self) # get rid of original dropshadow model if self.shadowNode is not None: self.shadowNode.removeNode() del self.shadowNode # get rid of the splash if self.splash is not None: self.splash.destroy() del self.splash # get rid of the dust cloud if self.dustCloud is not None: self.dustCloud.destroy() del self.dustCloud # Get rid of audio del self.sndHitHouse del self.sndHitGround del self.sndHitWater del self.sndBounce1 del self.sndBounce2 del self.sndBounce3 if self.localFlyingToon: self.__resetToon(self.localFlyingToon) # Reset anim an run play rate self.localFlyingToon.loop('neutral') self.localFlyingToon.setPlayRate(1.0, 'run') self.localFlyingToon = None self.ignoreAll() def onstage(self): """ Called when cannons are activated """ self.notify.debug("onstage") # show everything self.splash.reparentTo(render) self.dustCloud.reparentTo(render) def offstage(self): """ Called when cannons are deactivated. """ self.notify.debug("offstage") if self.splash is not None: self.splash.reparentTo(hidden) self.splash.stop() if self.dustCloud is not None: self.dustCloud.reparentTo(hidden) self.dustCloud.stop() def disable(self): if self._flyingCollisionTaskName: taskMgr.remove(self._flyingCollisionTaskName) taskMgr.remove(self.taskNameFireCannon) taskMgr.remove(self.taskNameShoot) taskMgr.remove(self.taskNameFly) taskMgr.remove(DistributedPartyCannonActivity.REACTIVATE_CLOUD_TASK) self.ignoreAll() if self.localFlyingToonId: self.__stopCollisionHandler(self.localFlyingToon) self.__stopLocalFlyTask(self.localFlyingToonId) self.setMovie(PartyGlobals.CANNON_MOVIE_CLEAR, self.localFlyingToonId) if self.hitTrack is not None: self.hitTrack.finish() del self.hitTrack self.hitTrack = None DistributedPartyActivity.disable(self) def delete(self): self.offstage() DistributedPartyActivity.delete(self) # Distributed (broadcast ram) def setMovie(self, mode, toonId): self.notify.debug("%s setMovie(%s, %s)" % (self.doId, toonId, mode)) if toonId != base.localAvatar.doId: return if mode == PartyGlobals.CANNON_MOVIE_CLEAR: # No one is in the cannon; it's available. self.landToon(toonId) elif mode == PartyGlobals.CANNON_MOVIE_LANDED: self.landToon(toonId) elif mode == PartyGlobals.CANNON_MOVIE_FORCE_EXIT: self.landToon(toonId) def __handleAvatarGone(self): # Called when the avatar in the fishing spot vanishes. # The AI will call setMovie(FORCE_EXIT), too, but we call it first # just to be on the safe side, so we don't try to access a # non-existent avatar. self.setMovie(PartyGlobals.CANNON_MOVIE_CLEAR, 0) def handleToonDisabled(self, toonId): """ A toon dropped unexpectedly from the game. Handle it! """ self.notify.warning("handleToonDisabled no implementation yet") def handleToonJoined(self, toonId): """ Whenever a new toon joins the activity, this function is called. Subclasses should override this function. Parameters: toonId: doId of the toon that joined """ self.notify.warning("handleToonJoined no implementation yet") def isLocalToon(self, av): return (base.localAvatar == av) def isLocalToonId(self, toonId): return (base.localAvatar.doId == toonId) def getTitle(self): return TTLocalizer.PartyCannonActivityTitle def getInstructions(self): return TTLocalizer.PartyCannonActivityInstructions def hasPlayedBefore(self): return self._localPlayedBefore def displayRules(self): self.startRules() def handleRulesDone(self): self.finishRules() self._localPlayedBefore = True messenger.send(DistributedPartyCannonActivity.RULES_DONE_EVENT) #=============================================================================== # Fire Cannon #=============================================================================== # Distributed (broadcast) def setCannonWillFire(self, cannonId, zRot, angle): """ AI is telling us that a cannon needs to fire, so make sure that cannon is updated and spawn the the fire cannon task. """ self.notify.debug("setCannonWillFire: %d %d %d" % (cannonId, zRot, angle)) cannon = base.cr.doId2do.get(cannonId) # we might get this message from the server before we even get a # chance to place the toon inside the cannon... for instance if we just # walked in the zone. If this is the case, don't create a fire task if cannon is None: self.notify.warning( "Cannon has not been created, but we got this message. Don't show firing." ) return if not cannon.getToonInside(): self.notify.warning( "setCannonWillFire, but no toon insde. Don't show firing") return if self.isLocalToon(cannon.getToonInside()): self.localFlyingToon = base.localAvatar self.localFlyingToonId = base.localAvatar.doId self.localFiringCannon = cannon self.flyingToonCloudsHit = 0 # Set final cannon position. # NOTE: do this for the local toon; cannon angles may have been # modified by conversion to and from fixed-point, and we want # to have the same values that all the other clients have cannon.updateModel(zRot, angle) toonId = cannon.getToonInside().doId # create a task to fire off the cannon task = Task(self.__fireCannonTask) task.toonId = toonId task.cannon = cannon taskMgr.add(task, self.taskNameFireCannon) self.toonIds.append(toonId) def __fireCannonTask(self, task): """ Decides how to fire a cannon. If the local toon is firing then set up flight control Other clients just fire the cannon and get the toon position from broadcast. Always returns Task.done """ launchTime = 0.0 toonId = task.toonId cannon = task.cannon toon = cannon.getToonInside() self.notify.debug( str(self.doId) + " FIRING CANNON FOR TOON " + str(toonId)) # Make sure if a toon is still inside # The a client may have crashed at this point if not cannon.isToonInside(): return Task.done # Compute the flight results if the local toon is firing out of the cannon. if self.isLocalToonId(toonId): self.inWater = 0 # calculate the trajectory flightResults = self.__calcFlightResults(cannon, toonId, launchTime) # pull all the results (startPos, startHpr, startVel, trajectory) into the local namespace for key in flightResults: exec("%s = flightResults['%s']" % (key, key)) #self.notify.debug("start position: " + str(startPos)) #self.notify.debug("start velocity: " + str(startVel)) #self.notify.debug("time of launch: " + str(launchTime)) cannon.removeToonReadyToFire() # Create the shoot task shootTask = Task(self.__shootTask, self.taskNameShoot) shootTask.info = {'toonId': toonId, 'cannon': cannon} if self.isLocalToonId(toonId): self.flyingToonOffsetRotation = 0 self.flyingToonOffsetAngle = 0 self.flyingToonOffsetX = 0 self.flyingToonOffsetY = 0 self.hitCloud = 0 self.initialFlyVel = INITIAL_VELOCITY self.camNode = NodePath(self.uniqueName("flyingCamera")) self.camNode.setScale(.5) self.camNode.setPos(self.localFlyingToon.getPos()) self.camNode.setHpr(self.localFlyingToon.getHpr()) self.camNode.reparentTo(render) startVel = cannon.getToonFireVel() self.lastStartVel = startVel place = base.cr.playGame.getPlace() place.fsm.request("activity") toon.dropShadow.hide() self.localFlyingDropShadow = self.shadowNode.copyTo(hidden) # stock the tasks up with the info they need # store the info in a shared dictionary self.localFlyingToon.wrtReparentTo(render) info = {} info['toonId'] = toonId info['trajectory'] = self.trajectory info['launchTime'] = launchTime info['toon'] = self.localFlyingToon info['hRot'] = cannon.getRotation() camera.wrtReparentTo(self.localFlyingToon) flyTask = Task(self.__localFlyTask, self.taskNameFly) flyTask.info = info seqTask = Task.sequence(shootTask, flyTask) self.__startCollisionHandler() self.notify.debug("Disable standard local toon controls.") base.localAvatar.disableAvatarControls() base.localAvatar.startPosHprBroadcast() else: seqTask = shootTask taskMgr.add(seqTask, self.taskName('flyingToon') + "-" + str(toonId)) toon.startSmooth() return Task.done def __calcFlightResults(self, cannon, toonId, launchTime): """ returns dict with keys: startPos, startHpr, startVel, trajectory, timeOfImpact, hitWhat """ startPos = cannon.getToonFirePos() startHpr = cannon.getToonFireHpr() startVel = cannon.getToonFireVel() trajectory = Trajectory.Trajectory(launchTime, startPos, startVel) self.trajectory = trajectory return { 'startPos': startPos, 'startHpr': startHpr, 'startVel': startVel, 'trajectory': trajectory, } def __shootTask(self, task): """ Plays a poof of smoke and enables controls for the local avatar if they're firing out of the cannon. Return Value: Task.done """ task.info["cannon"].fire() toonId = task.info["toonId"] toon = base.cr.doId2do.get(toonId) if toon: toon.loop('swim') else: self.notify.debug( "__shootTask avoided a crash, toon %d not found" % toonId) pass if self.isLocalToonId(task.info["toonId"]): self.localFlyingDropShadow.reparentTo(render) self.gui.enableAimKeys() return Task.done #=============================================================================== # Landing #=============================================================================== # Distributed (clsend airecv) def d_setLanded(self, toonId): self.notify.debug("d_setLanded %s" % toonId) # The shooter can tell the server he's landed, and then the server # will pass the message along to all the other clients in this zone. if self.isLocalToonId(toonId): if self.cr: # we can get here if he's kicked out while flying in the air self.sendUpdate("setLanded", [toonId]) else: self.notify.debug('we avoided crash 2') def landToon(self, toonId): """ Toon landed, set upright and in the right position. """ self.notify.debug("%s landToon" % self.doId) toon = base.cr.doId2do.get(toonId) if toon is not None: toon.resetLOD() if toon == base.localAvatar: self.__stopCollisionHandler(base.localAvatar) toon.wrtReparentTo(render) self.__setToonUpright(toon) toon.setPlayRate(1.0, 'run') toon.startSmooth() toon.setScale(1.0) self.ignore(toon.uniqueName("disable")) self.__cleanupFlyingToonData(toon) toon.showShadow() place = base.cr.playGame.getPlace() if place is not None: # this is crashing LIVE if not hasattr(place, 'fsm'): return if toon is not None and toon == base.localAvatar: self.__localDisplayLandedResults() def __localDisplayLandedResults(self): """ Displays to local avatar the flight results. If they hit no clouds it immediately sets them up for running again. """ if self.flyingToonCloudsHit > 0: self._doneCannons = True else: self.__localToonDoneLanding() def handleRewardDone(self): DistributedPartyActivity.handleRewardDone(self) if self._doneCannons: self.__localToonDoneLanding() def __localToonDoneLanding(self): """ Local toon has finally landed (and has read results dialog). Set the toon back to the standard state. """ base.cr.playGame.getPlace().fsm.request("walk") self.notify.debug("__localToonDoneLanding") base.localAvatar.collisionsOn() base.localAvatar.startPosHprBroadcast() base.localAvatar.enableAvatarControls() messenger.send(DistributedPartyCannonActivity.LOCAL_TOON_LANDED_EVENT) def __setToonUpright(self, toon, pos=None): """ Sets the Toon to a standing posture after landing. """ if toon: if self.inWater: toon.setP(0) toon.setR(0) return if not pos: pos = toon.getPos(render) toon.setPos(render, pos) toon.loop('neutral') if self.localFiringCannon and hasattr(self.localFiringCannon, "cannonNode"): if self.localFiringCannon.cannonNode: toon.lookAt(self.localFiringCannon.cannonNode) else: self.notify.debug("we avoided crash 1.") toon.setP(0) toon.setR(0) toon.setScale(1, 1, 1) def __resetToonToCannon(self, avatar): self.notify.debug("__resetToonToCannon") if not avatar and self.localFlyingToonId: avatar = base.cr.doId2do.get(self.localFlyingToonId, None) if avatar: #if self.cannon: # self.cannon.removeToonDidNotFire() self.__resetToon(avatar) def __resetToon(self, avatar, pos=None): self.notify.debug("__resetToon") if avatar: self.__stopCollisionHandler(avatar) self.__setToonUpright(avatar, pos) if self.isLocalToonId(avatar.doId): self.notify.debug("toon setting position to %s" % pos) if pos: base.localAvatar.setPos(pos) camera.reparentTo(avatar) self.d_setLanded(avatar.doId) #=============================================================================== # Flying #=============================================================================== def __updateFlightVelocity(self, trajectory): """ Recalculate the trajectory if the toon has moved """ # Rotate horizontal velocity based on the rotationOffset hpr = LRotationf(self.flyingToonOffsetRotation, 0, 0) newVel = hpr.xform(self.lastStartVel) # Rotate z velocity based on the angleOffset hpr = LRotationf(0, self.flyingToonOffsetAngle, 0) zVel = hpr.xform(self.lastStartVel).getZ() # This is a workaround when sometimes the z vel is less and it # looks like the toon is flying up. if zVel < newVel.getZ(): newVel.setZ(zVel) trajectory.setStartVel(newVel) def __isFlightKeyPressed(self): return (self.gui.leftPressed or self.gui.rightPressed or self.gui.upPressed or self.gui.downPressed) def __moveFlyingToon(self, toon): """ Update the local toon position if up, down, left, right keys are pressed. If the toon is horizontal, they can decrease the angle and change the rotation. If the toon is vertical, they can offset their position in X and Y """ toonP = toon.getP(render) isToonFlyingHorizontal = (toonP > -150 and toonP < -30) OFFSET = .25 #.15 rotVel = 0 if self.gui.leftPressed: if isToonFlyingHorizontal: rotVel += CANNON_ROTATION_VEL else: self.flyingToonOffsetX -= OFFSET if self.gui.rightPressed: if isToonFlyingHorizontal: rotVel -= CANNON_ROTATION_VEL else: self.flyingToonOffsetX += OFFSET self.flyingToonOffsetRotation += rotVel * globalClock.getDt() angVel = 0 if self.gui.upPressed: if not isToonFlyingHorizontal: self.flyingToonOffsetY -= OFFSET if self.gui.downPressed: if isToonFlyingHorizontal: angVel += CANNON_ANGLE_VEL else: self.flyingToonOffsetY += OFFSET self.flyingToonOffsetAngle += angVel * globalClock.getDt() def __stopLocalFlyTask(self, toonId): taskMgr.remove(self.taskName('flyingToon') + "-" + str(toonId)) self.gui.disableAimKeys() def __localFlyTask(self, task): """ Updates the local flying toon position, and sets the right camera view Returns Task.cont unless the toon is too far up or down, then it returns Task.done """ toon = task.info['toon'] if toon.isEmpty(): # The flying toon has been deleted before the task ended. # So, end the task. self.__resetToonToCannon(self.localFlyingToon) return Task.done curTime = task.time + task.info['launchTime'] # don't overshoot past the time of landing #t = min(curTime, task.info['timeOfImpact']) #let him keep going now t = curTime #if not self.hitBumper: t *= 0.75 self.lastT = self.t self.t = t deltaT = self.t - self.lastT self.deltaT = deltaT # Update Toon position if self.hitBumper: # Toon hit a bumper pos = self.lastPos + self.lastVel * deltaT vel = self.lastVel self.lastVel += Vec3(0, 0, -32.0) * deltaT self.lastPos = pos toon.setFluidPos(pos) lastR = toon.getR() toon.setR(lastR - deltaT * self.angularVel * 2.0) cameraView = 0 else: # update position if character is moving if not self.hitCloud and self.__isFlightKeyPressed(): self.__moveFlyingToon(toon) self.__updateFlightVelocity(task.info['trajectory']) # Toon hit cloud, set new trajectory: if self.hitCloud == 1: vel = task.info['trajectory'].getVel(t) startPos = toon.getPos(render) task.info['trajectory'].setStartTime(t) task.info['trajectory'].setStartPos(startPos) task.info['trajectory'].setStartVel(self.lastVel) toon.lookAt(toon.getPos() + vel) toon.setH(-toon.getH()) self.flyingToonOffsetRotation = 0 self.flyingToonOffsetAngle = 0 self.flyingToonOffsetX = 0 self.flyingToonOffsetY = 0 self.hitCloud = 2 # get position pos = task.info['trajectory'].getPos(t) # update toon position toon.setFluidPos(pos) toon.setFluidPos(toon, self.flyingToonOffsetX, self.flyingToonOffsetY, 0) # set the toon's tilt based on its velocity # h rotation is constant, and corresponds to the cannon's # z rotation (left to right) vel = task.info['trajectory'].getVel(t) # Because we want a typically standing toon to lie down, # we subtract 90 from the velocity angle toon.lookAt(toon.getPos() + Vec3(vel[0], vel[1], vel[2])) toon.setP(toon.getP() - 90) cameraView = 2 if self.hitCloud == 2: self.lastStartVel = vel self.hitCloud = 0 # update drop shadow position shadowPos = toon.getPos() shadowPos.setZ(SHADOW_Z_OFFSET) self.localFlyingDropShadow.setPos(shadowPos) # Toon is just flying into space at this point, so stop flying if pos.getZ() < -20 or pos.getZ() > 1000: self.notify.debug("stopping fly task toon.getZ()=%.2f" % pos.getZ()) self.__resetToonToCannon(self.localFlyingToon) return Task.done self.__setFlyingCameraView(task.info["toon"], cameraView, deltaT) return Task.cont def __setFlyingCameraView(self, toon, view, deltaT): """ Adjust the camera view while the toon is flying/falling. """ # Only the local toon is allowed if toon != base.localAvatar: return lookAt = toon.getPos(render) hpr = toon.getHpr(render) if view == 0: # Fixed camera angle, look at toon. camera.wrtReparentTo(render) camera.lookAt(lookAt) elif view == 1: # third person pt of view camera.reparentTo(render) camera.setPos(render, 100, 100, 35.25) camera.lookAt(render, lookAt) elif view == 2: # Follows a flying toon. # Camera is always at the same distance fromt the toon, but it # needs to catch up to be right at the cameraPoint # speed depends on how far the camera is from the desired position; # It moves faster when the camera is further, and slower when it's not. if camera.getParent() != self.camNode: camera.wrtReparentTo(self.camNode) camera.setPos(self.cameraPos) camera.lookAt(toon) self.camNode.setPos(toon.getPos(render)) camHpr = self.camNode.getHpr(toon) vec = -Point3(0, 0, 0) - camHpr # increase the hpr exponentially; the further the faster it will move. relativeSpeed = math.pow(vec.length() / 60.0, 2) + 0.1 newHpr = camHpr + vec * deltaT * self.cameraSpeed * relativeSpeed self.camNode.setHpr(toon, newHpr) camera.lookAt(self.camNode) camera.setR(render, 0) def __cleanupFlyingToonData(self, toon): self.notify.debug("__cleanupFlyingToonData") if toon: # show the toons original drop shadows.. toon.showShadow() self.toonIds.remove(toon.doId) if self.isLocalToon(toon): # ... and destroy the one used for flight if (self.localFlyingDropShadow != None): self.localFlyingDropShadow.removeNode() self.localFlyingDropShadow = None self.hitBumper = 0 self.angularVel = 0 self.vel = Vec3(0, 0, 0) self.lastVel = Vec3(0, 0, 0) self.lastPos = Vec3(0, 0, 0) self.landingPos = Vec3(0, 0, 0) self.t = 0 self.lastT = 0 self.deltaT = 0 self.lastWakeTime = 0 self.localFlyingToon = None self.localFlyingToonId = 0 self.localFiringCannon = None if hasattr(self, "camNode") and self.camNode: self.camNode.removeNode() self.camNode = None #=============================================================================== # Collisions #=============================================================================== def __startCollisionHandler(self): """ Sets up collision handler for local flying toon. """ self.flyColSphere = CollisionSphere( 0, 0, self.localFlyingToon.getHeight() / 2.0, 1.0) self.flyColNode = CollisionNode(self.uniqueName('flySphere')) self.flyColNode.setCollideMask(ToontownGlobals.WallBitmask | ToontownGlobals.FloorBitmask) self.flyColNode.addSolid(self.flyColSphere) self.flyColNodePath = self.localFlyingToon.attachNewNode( self.flyColNode) self.flyColNodePath.setColor(1, 0, 0, 1) # used to make sure we don't re-collide with something we just collided against self._activeCollisions = set() self.handler = CollisionHandlerQueue() self._flyingCollisionTaskName = 'checkFlyingToonCollision-%s' % self.doId taskMgr.add(self._checkFlyingToonCollision, self._flyingCollisionTaskName) #self.handler.setInPattern(self.uniqueName('flyingToonCollision')) base.cTrav.addCollider(self.flyColNodePath, self.handler) #self.accept(self.uniqueName('flyingToonCollision'), self.__handleFlyingToonCollision) def __stopCollisionHandler(self, avatar): """ Cleans up collision handler for local flying toon. """ self.notify.debug("%s __stopCollisionHandler" % self.doId) if self._flyingCollisionTaskName: taskMgr.remove(self._flyingCollisionTaskName) self._flyingCollisionTaskName = None self._activeCollisions = set() #self.ignore(self.uniqueName('flyingToonCollision')) if avatar: avatar.loop('neutral') # Reset collisions if self.flyColNode: self.flyColNode = None self.flyColSphere = None if self.flyColNodePath: base.cTrav.removeCollider(self.flyColNodePath) self.flyColNodePath.removeNode() self.flyColNodePath = None self.handler = None def _checkFlyingToonCollision(self, task=None): curCollisions = set() if self.handler.getNumEntries(): self.handler.sortEntries() i = self.handler.getNumEntries() activeEntry = None while i > 0: entry = self.handler.getEntry(i - 1) k = (str(entry.getFromNodePath()), str(entry.getIntoNodePath())) curCollisions.add(k) if (activeEntry is None) and (k not in self._activeCollisions): activeEntry = entry self._activeCollisions.add(k) i -= 1 if activeEntry is not None: self.__handleFlyingToonCollision(activeEntry) if self.handler: self.handler.clearEntries() # keep track of the collisions that we have handled and that are still happening # so that we don't accidentally re-collide for k in list(self._activeCollisions): if k not in curCollisions: self._activeCollisions.remove(k) return Task.cont def __handleFlyingToonCollision(self, collisionEntry): """ Handles when flying toon hits something """ self.notify.debug("%s __handleToonCollision" % self.doId) if self.localFlyingToon == None or self.flyColNode == None: return # ignore cSphere hits (butterflies) and treasureSphere hits hitNode = collisionEntry.getIntoNode().getName() self.notify.debug('hitNode = %s' % hitNode) self.notify.debug('hitNodePath.getParent = %s' % collisionEntry.getIntoNodePath().getParent()) self.vel = self.trajectory.getVel(self.t) vel = self.trajectory.getVel(self.t) vel.normalize() if self.hitBumper: vel = self.lastVel * 1 vel.normalize() self.notify.debug('normalized vel=%s' % vel) #space = collisionEntry.getIntoMat() solid = collisionEntry.getInto() #space = solid.getMat() intoNormal = collisionEntry.getSurfaceNormal( collisionEntry.getIntoNodePath()) self.notify.debug('old intoNormal = %s' % intoNormal) intoNormal = collisionEntry.getSurfaceNormal(render) self.notify.debug('new intoNormal = %s' % intoNormal) #hitNormal = space.xformVec(intoNormal) hitPylonAboveWater = False hitPylonBelowWater = False hitNormal = intoNormal if (hitNode.find("cSphere") == 0 or hitNode.find("treasureSphere") == 0 or hitNode.find("prop") == 0 or hitNode.find("distAvatarCollNode") == 0 or hitNode.find("CannonSphere") == 0 or hitNode.find("plotSphere") == 0 or hitNode.find("flySphere") == 0 or hitNode.find("FishingSpotSphere") == 0 or hitNode.find("TrampolineTrigger") == 0 or hitNode == "gagtree_collision" or hitNode == "sign_collision" or hitNode == "FlowerSellBox" or hitPylonBelowWater): self.notify.debug("--------------hit and ignoring %s" % hitNode) return if ( vel.dot(hitNormal) > 0 ) and not hitNode == 'collision_roof' and not hitNode == 'collision_fence': pass self.notify.debug( "--------------hit and ignoring backfacing %s, dot=%s" % (hitNode, vel.dot(hitNormal))) return intoNode = collisionEntry.getIntoNodePath() bumperNodes = [ "sky_collision" ] + PartyCannonCollisions["bounce"] + PartyCannonCollisions["fence"] # there is only one for now cloudBumpers = PartyCannonCollisions["clouds"] bumperNodes += cloudBumpers if (hitNode in bumperNodes) or (hitNode.find("cogPie") == 0) or ( PartyCannonCollisions["trampoline_bounce"] in hitNode): # If we hit the house or the bridge, we will ricochet and keep # flying, so don't kill the flyTask #if self.hitBumper != 1: if hitNode == "sky_collision" or hitNode in PartyCannonCollisions["fence"] or \ (hitNode.find("cogPie") == 0): self.__hitFence(self.localFlyingToon, collisionEntry) elif (PartyCannonCollisions["trampoline_bounce"] in hitNode) or \ hitNode in PartyCannonCollisions["bounce"]: if hitNode == "wall_collision": hitSound = self.sndBounce2 else: hitSound = self.sndBounce3 self.hitCloud = 1 self.__hitBumper(self.localFlyingToon, collisionEntry, hitSound, kr=0.09, angVel=5) self.hitBumper = 0 elif hitNode in cloudBumpers: self.__hitCloudPlatform(self.localFlyingToon, collisionEntry) elif hitNode == 'statuaryCol': self.__hitStatuary(self.localFlyingToon, collisionEntry) else: self.notify.debug( "*************** hit something else ************") return else: self.__stopCollisionHandler(self.localFlyingToon) self.__stopLocalFlyTask(self.localFlyingToonId) self.notify.debug('stopping flying since we hit %s' % hitNode) # reparent camera to render now so dustcloud works if self.isLocalToonId(self.localFlyingToon.doId): camera.wrtReparentTo(render) # hide the drop shadow if self.localFlyingDropShadow: self.localFlyingDropShadow.reparentTo(hidden) pos = collisionEntry.getSurfacePoint(render) hpr = self.localFlyingToon.getHpr() hitPos = collisionEntry.getSurfacePoint(render) pos = hitPos self.landingPos = pos self.notify.debug("hitNode, Normal = %s,%s" % (hitNode, intoNormal)) track = Sequence() track.append(Func(self.localFlyingToon.wrtReparentTo, render)) #track.append(Func(self.localFlyingToon.b_setParent, ToontownGlobals.SPRender)) if self.isLocalToonId(self.localFlyingToon.doId): track.append(Func(self.localFlyingToon.collisionsOff)) if hitNode in PartyCannonCollisions["ground"]: track.append(Func(self.__hitGround, self.localFlyingToon, pos)) track.append(Wait(1.0)) track.append( Func(self.__setToonUpright, self.localFlyingToon, self.landingPos)) elif hitNode in PartyCannonCollisions["fence"]: track.append( Func(self.__hitFence, self.localFlyingToon, collisionEntry)) elif hitNode == "collision3": track.append( Func(self.__hitWater, self.localFlyingToon, pos, collisionEntry)) track.append(Wait(2.0)) track.append( Func(self.__setToonUpright, self.localFlyingToon, self.landingPos)) elif hitNode.find("cloudSphere") == 0: track.append( Func(self.__hitCloudPlatform, self.localFlyingToon, collisionEntry)) else: self.notify.warning( "************* unhandled hitNode=%s parent =%s" % (hitNode, collisionEntry.getIntoNodePath().getParent())) track.append(Func(self.d_setLanded, self.localFlyingToonId)) if self.isLocalToonId(self.localFlyingToonId): track.append(Func(self.localFlyingToon.collisionsOn)) if self.hitTrack: self.hitTrack.finish() self.hitTrack = track self.hitTrack.start() # Hitting a bumper means we will bounce off at an angle # equal to the reflection of our incoming velocity about # the bumper's normal at the point of collision def __hitBumper(self, avatar, collisionEntry, sound, kr=.6, angVel=1): self.hitBumper = 1 # play the sound immediately base.playSfx(sound) # get the point of collision hitP = avatar.getPos(render) self.lastPos = hitP #get the normal from collision object normal = collisionEntry.getSurfaceNormal(render) self.notify.debug('normal = %s' % normal) # find velocity of toon vel = self.vel * 1 # we need a copy speed = vel.length() vel.normalize() self.notify.debug('old vel = %s' % vel) # calculate the reflected velocity if self.hitCloud: # Get the normal from the toon to the center of the party grounds centerVec = Vec3(-avatar.getPos(self.getParentNodePath())) centerVec.setZ(0) d = centerVec.length() / 15.0 centerVec.setZ(abs(centerVec.length() * math.sin(70.0))) centerVec.normalize() newVel = centerVec * d + normal * 0.2 newVel = newVel * (kr * speed) self.initialFlyVel = (kr * speed) else: newVel = (normal * 2.0 + vel) * (kr * speed) self.lastVel = newVel self.notify.debug('new vel = %s' % newVel) # impose an angular velocity - 'cause it looks cool! self.angularVel = angVel * 360 if self.hitCloud: return # put the toon in fetal position while he spins t = Sequence(Func(avatar.pose, 'lose', 110)) t.start() def __hitGround(self, avatar, pos, extraArgs=[]): self.notify.debug("__hitGround") hitP = avatar.getPos(render) self.notify.debug("hitGround pos = %s, hitP = %s" % (pos, hitP)) self.notify.debug("avatar hpr = %s" % avatar.getHpr()) avatar.setPos(pos[0], pos[1], pos[2] + avatar.getHeight() / 3.0) # make avatar look in direction of velocity avatar.setHpr(avatar.getH(), -135, 0) self.notify.debug("parent = %s" % avatar.getParent()) self.notify.debug("pos = %s, hpr = %s" % (avatar.getPos(render), avatar.getHpr(render))) self.__playDustCloud(avatar, pos) base.playSfx(self.sndHitGround) # Make toon feel stuck to the ground by wiggling the legs a bit faster avatar.setPlayRate(2.0, "run") avatar.loop("run") def __playDustCloud(self, toon, pos): self.dustCloud.setPos(render, pos[0], pos[1], pos[2] + toon.getHeight() / 3.0) self.dustCloud.setScale(0.35) self.dustCloud.play() def __hitFence(self, avatar, collisionEntry, extraArgs=[]): self.notify.debug("__hitFence") self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=.2, angVel=3) # TODO: If score is introduced into this cannon game, the code should be placed here. def __hitWater(self, avatar, pos, collisionEntry, extraArgs=[]): hitP = avatar.getPos(render) if hitP[2] > ToontownGlobals.EstateWakeWaterHeight: # we hit the ground before we hit water self.notify.debug("we hit the ground before we hit water") self.__hitGround(avatar, pos, extraArgs) #print("but not really") return self.notify.debug("hit water") hitP = avatar.getPos(render) # we landed in the water avatar.loop('neutral') # Show a splash self.splash.setPos(hitP) self.splash.setZ(ToontownGlobals.EstateWakeWaterHeight) self.splash.setScale(2) self.splash.play() # Play the splash sound base.playSfx(self.sndHitWater) place = base.cr.playGame.getPlace() # stand the toon upright #task.info['toon'].setHpr(task.info['hRot'],0,0) #self.__somebodyWon(task.info['toonId']) def __hitStatuary(self, avatar, collisionEntry, extraArgs=[]): self.__hitBumper(avatar, collisionEntry, self.sndHitHouse, kr=0.4, angVel=5) # TODO: If score is introduced into this cannon game, the code should be placed here. def d_cloudsColorRequest(self): self.notify.debug("cloudsColorRequest") self.sendUpdate("cloudsColorRequest") def cloudsColorResponse(self, cloudColorList): self.notify.debug("cloudsColorResponse: %s" % cloudColorList) for cloudColor in cloudColorList: self.setCloudHit(*cloudColor) # Distributed (clsend airecv) def d_requestCloudHit(self, cloudNumber, color): self.sendUpdate( "requestCloudHit", [cloudNumber, color.getX(), color.getY(), color.getZ()]) # Distributed (broadcast) def setCloudHit(self, cloudNumber, r, g, b): cloud = render.find("**/cloud-%d" % cloudNumber) if not cloud.isEmpty(): cloud.setColor(r, g, b, 1.0) else: self.notify.debug("Could not find cloud-%d" % cloudNumber) def __hitCloudPlatform(self, avatar, collisionEntry, extraArgs=[]): # Only hit cloud if flying, otherwise fall through. if not self.hitBumper and not self.hitCloud: self.hitCloud = 1 self.__hitBumper(avatar, collisionEntry, self.sndBounce1, kr=0.35, angVel=5) self.hitBumper = 0 if self._lastCloudHit is None: cloud = collisionEntry.getIntoNodePath().getParent() self._lastCloudHit = cloud cloud.setColor(base.localAvatar.style.getHeadColor()) cloudNumber = int(cloud.getNetTag('number')) self.d_requestCloudHit(cloudNumber, base.localAvatar.style.getHeadColor()) self.__playDustCloud(avatar, collisionEntry.getSurfacePoint(render)) self.flyingToonCloudsHit += 1 taskMgr.doMethodLater( 0.25, self.__reactivateLastCloudHit, DistributedPartyCannonActivity.REACTIVATE_CLOUD_TASK) def __reactivateLastCloudHit(self, task): """Reactivates collisions for the last cloud hit""" self._lastCloudHit = None return Task.done def __handleFireworksStarted(self): """ Hide the clouds during the fireworks show """ self.notify.debug("__handleFireworksStarted") base.cr.playGame.hood.loader.fadeClouds() def __handleFireworksFinished(self): """ Show the clouds after the fireworks show is over """ self.notify.debug("__handleFireworksFinished") if self.__checkHoodValidity(): base.cr.playGame.hood.loader.fadeClouds() else: self.notify.debug("Toon has left the party") def __checkHoodValidity(self): """Function that checks the validity of a hood, it's loader and the geometry """ 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 True else: return False def handleToonExited(self, toonId): self.notify.debug( "DistributedPartyCannonActivity handleToonExited( toonId=%s ) " % toonId) if toonId in self.cr.doId2do: self.notify.warning("handleToonExited is not defined")