def loadAssets(self): """Load our assets.""" self.root = render.attachNewNode('golfSpot-%d' % self.index) self.root.setPos(*self.positions[self.index]) self.ballModel = loader.loadModel('phase_6/models/golf/golf_ball') self.ballColor = VBase4(1, 1, 1, 1) if self.index < len(GolfGlobals.PlayerColors): self.ballColor = VBase4(*GolfGlobals.PlayerColors[self.index]) self.ballModel.setColorScale(self.ballColor) self.ballModel.reparentTo(self.root) self.club = loader.loadModel('phase_6/models/golf/putter') self.clubLookatSpot = self.root.attachNewNode('clubLookat') self.clubLookatSpot.setY(-(GolfGlobals.GOLF_BALL_RADIUS + 0.1)) # create a collision sphere to trigger when we touch the ball # Make a trigger sphere so we can detect when the local avatar # runs up to the controls. We bury the sphere mostly under # the floor to minimize accidental collisions. cs = CollisionSphere(0, 0, 0, 1) cs.setTangible(0) cn = CollisionNode(self.triggerName) cn.addSolid(cs) cn.setIntoCollideMask(ToontownGlobals.WallBitmask) self.trigger = self.root.attachNewNode(cn) self.trigger.stash() self.hitBallSfx = loader.loadSfx('phase_6/audio/sfx/Golf_Hit_Ball.mp3')
def _initCollisions(self): name = "CogdoMazeLock-%d" % self.id collSphere = CollisionSphere(0, 0, 0.0, 0.25) collSphere.setTangible(0) collNode = CollisionNode(name) collNode.setFromCollideMask(ToontownGlobals.CatchGameBitmask) collNode.addSolid(collSphere) self.model.attachNewNode(collNode) self.enterCollisionEventName = "enter" + name
def createThrowGag(self, gag): throwGag = CogdoMazePlayer.createThrowGag(self, gag) collSphere = CollisionSphere(0, 0, 0, 0.5) collSphere.setTangible(0) name = Globals.GagCollisionName collNode = CollisionNode(name) collNode.setFromCollideMask(ToontownGlobals.PieBitmask) collNode.addSolid(collSphere) colNp = throwGag.attachNewNode(collNode) base.cTrav.addCollider(colNp, self.gagHandler) return throwGag
def pieThrow(self, avId, timestamp, heading, pos, power): """Show local or remote toon throwing a pie.""" toon = self.activity.getAvatar(avId) if toon is None: return tossTrack, pieTrack, flyPie = self.getTossPieInterval( toon, pos[0], pos[1], pos[2], heading, 0, 0, power) if avId == base.localAvatar.doId: flyPie.setTag('throwerId', str(avId)) collSphere = CollisionSphere(0, 0, 0, 0.5) # Make the sphere intangible collSphere.setTangible(0) name = "PieSphere-%d" % avId collSphereName = self.activity.uniqueName(name) collNode = CollisionNode(collSphereName) collNode.setFromCollideMask(ToontownGlobals.PieBitmask) collNode.addSolid(collSphere) collNP = flyPie.attachNewNode(collNode) base.cTrav.addCollider(collNP, self.pieHandler) self.toonPieEventNames[collNP] = 'pieHit-' + collSphereName self.accept(self.toonPieEventNames[collNP], self.handlePieCollision) else: player = self.players.get(avId) if player is not None: player.faceForward() def matchRunningAnim(toon=toon): toon.playingAnim = None toon.setSpeed(toon.forwardSpeed, toon.rotateSpeed) newTossTrack = Sequence(tossTrack, Func(matchRunningAnim)) pieTrack = Parallel(newTossTrack, pieTrack, name="PartyCogActivity.pieTrack-%d-%s" % (avId, timestamp)) elapsedTime = globalClockDelta.localElapsedTime(timestamp) if elapsedTime < 16. / 24.: elapsedTime = 16. / 24. # make the pie fly immediately pieTrack.start(elapsedTime) self.pieIvals.append(pieTrack) self.toonPieTracks[avId] = pieTrack
def __initCollisions(self): collSphere = CollisionSphere(0, 0, 0, Globals.PlayerCollisionRadius) collSphere.setTangible(0) self.mazeCollisionName = Globals.LocalPlayerCollisionName collNode = CollisionNode(self.mazeCollisionName) collNode.addSolid(collSphere) collNodePath = self.toon.attachNewNode(collNode) collNodePath.hide() handler = CollisionHandlerEvent() handler.addInPattern('%fn-into-%in') base.cTrav.addCollider(collNodePath, handler) self.handler = handler self._collNodePath = collNodePath
def setupHeadSphere(self, avatarNodePath): collSphere = CollisionSphere(0, 0, 0, 1) collSphere.setTangible(1) collNode = CollisionNode('Flyer.cHeadCollSphere') collNode.setFromCollideMask(ToontownGlobals.CeilingBitmask) collNode.setIntoCollideMask(BitMask32.allOff()) collNode.addSolid(collSphere) self.cHeadSphereNodePath = avatarNodePath.attachNewNode(collNode) self.cHeadSphereNodePath.setZ(base.localAvatar.getHeight() + 1.0) self.headCollisionEvent = CollisionHandlerEvent() self.headCollisionEvent.addInPattern('%fn-enter-%in') self.headCollisionEvent.addOutPattern('%fn-exit-%in') base.cTrav.addCollider(self.cHeadSphereNodePath, self.headCollisionEvent)
def setupEventSphere(self, bitmask, avatarRadius): self.avatarRadius = avatarRadius cSphere = CollisionSphere(0.0, 0.0, self.avatarRadius + 0.75, self.avatarRadius * 1.04) cSphere.setTangible(0) cSphereNode = CollisionNode('Flyer.cEventSphereNode') cSphereNode.addSolid(cSphere) cSphereNodePath = self.avatarNodePath.attachNewNode(cSphereNode) cSphereNode.setFromCollideMask(bitmask) cSphereNode.setIntoCollideMask(BitMask32.allOff()) self.event = CollisionHandlerEvent() self.event.addInPattern('enter%in') self.event.addOutPattern('exit%in') self.cEventSphereNodePath = cSphereNodePath
def loadCollision( self ): collTube = CollisionTube(0.0, 0.0, 0.0, 0.0, 0.0, 6.0, 5.4) collTube.setTangible(True) self.trampolineCollision = CollisionNode(self.uniqueName("TrampolineCollision")) self.trampolineCollision.addSolid(collTube) self.trampolineCollision.setCollideMask(OTPGlobals.CameraBitmask | OTPGlobals.WallBitmask) self.trampolineCollisionNP = self.tramp.attachNewNode(self.trampolineCollision) collSphere = CollisionSphere(0.0, 0.0, 0.0, 7.0) collSphere.setTangible(False) self.trampolineTrigger = CollisionNode(self.uniqueName("TrampolineTrigger")) self.trampolineTrigger.addSolid(collSphere) self.trampolineTrigger.setIntoCollideMask(OTPGlobals.WallBitmask) self.trampolineTriggerNP = self.tramp.attachNewNode(self.trampolineTrigger) self.accept( "enter%s" % self.uniqueName("TrampolineTrigger"), self.onTrampolineTrigger )
def generate(self): """generate(self) This method is called when the DistributedObject is reintroduced to the world, either for the first time or from the cache. """ DistributedObject.DistributedObject.generate(self) loader = self.cr.playGame.hood.loader partyGate = loader.geom.find('**/partyGate_grp') if partyGate.isEmpty(): self.notify.warning('Could not find partyGate_grp in loader.geom') return self.clockFlat = partyGate.find("**/clock_flat") collSphere = CollisionSphere(0, 0, 0, 6.9) collSphere.setTangible(1) self.partyGateSphere = CollisionNode("PartyGateSphere") self.partyGateSphere.addSolid(collSphere) self.partyGateCollNodePath = partyGate.find( "**/partyGate_stepsLocator").attachNewNode(self.partyGateSphere) self.__enableCollisions() # self.tunnelOrigin = NodePath("PartyGateTunnelOrigin") # self.tunnelOrigin.reparentTo(partyGate) # self.tunnelOrigin.setPos(partyGate.find("**/clockText_locator").getPos() + Point3(0.0, 0.0, -12.0)) self.toontownTimeGui = ServerTimeGui(partyGate, hourCallback=self.hourChange) self.toontownTimeGui.setPos( partyGate.find("**/clockText_locator").getPos() + Point3(0.0, 0.0, -0.2)) self.toontownTimeGui.setHpr( partyGate.find("**/clockText_locator").getHpr()) self.toontownTimeGui.setScale(12.0, 1.0, 26.0) self.toontownTimeGui.amLabel.setPos(-0.035, 0, -0.032) self.toontownTimeGui.amLabel.setScale(0.5) self.toontownTimeGui.updateTime() self.setupSignText()
def loadLever(self): """ SubClasses can override this if they want to move their lever somewhere special... call this, then change the position. """ self.lever = self.root.attachNewNode('%sLever' % self.activityName) self.leverModel = self.party.defaultLeverModel.copyTo(self.lever) # Do some crazy reparenting so you can scale the whole thing nicely self.controlColumn = NodePath('cc') column = self.leverModel.find('**/column') column.getChildren().reparentTo(self.controlColumn) self.controlColumn.reparentTo(column) self.stickHinge = self.controlColumn.attachNewNode('stickHinge') self.stick = self.party.defaultStickModel.copyTo(self.stickHinge) self.stickHinge.setHpr(0.0, 90.0, 0.0) self.stick.setHpr(0, -90.0, 0) self.stick.flattenLight() self.bottom = self.leverModel.find('**/bottom') self.bottom.wrtReparentTo(self.controlColumn) self.bottomPos = self.bottom.getPos() # Make a trigger sphere so we can detect when the local avatar # runs up to the lever. cs = CollisionSphere(0.0, 1.35, 2.0, 1.0) cs.setTangible(False) cn = CollisionNode(self.leverTriggerEvent) cn.addSolid(cs) cn.setIntoCollideMask(OTPGlobals.WallBitmask) self.leverTrigger = self.root.attachNewNode(cn) self.leverTrigger.reparentTo(self.lever) self.leverTrigger.stash() # Also, a solid tube to keep us from running through the # lever itself. This one scales with the control # model. cs = CollisionTube(0.0, 2.7, 0.0, 0.0, 2.7, 3.0, 1.2) cn = CollisionNode('levertube') cn.addSolid(cs) cn.setIntoCollideMask(OTPGlobals.WallBitmask) self.leverTube = self.leverModel.attachNewNode(cn) # Let's set the height of the lever to the height of the host host = base.cr.doId2do.get(self.party.partyInfo.hostId) if host is None: self.notify.debug( "%s loadLever : Host has left the game before lever could be created." % self.activityName) return # We start by figuring out where we are going by setting the # scale and position appropriately # origScale = self.leverModel.getSz() # origCcPos = self.controlColumn.getPos() # origBottomPos = self.bottom.getPos() # origStickHingeHpr = self.stickHinge.getHpr() # First, scale the thing overall to match the host's scale, # including cheesy effect scales. scale = host.getGeomNode().getChild(0).getSz(render) self.leverModel.setScale(scale) # Then get the position of the host's right hand when he's # standing at the controls in a leverNeutral pose. self.controlColumn.setPos(0, 0, 0) host.setPosHpr(self.lever, 0, 0, 0, 0, 0, 0) host.pose('leverNeutral', 0) host.update() pos = host.rightHand.getPos(self.controlColumn) # Now set the control column to the right height and position # to put the top of the stick approximately in his hand. self.controlColumn.setPos(pos[0], pos[1], pos[2] - 1) # And put the bottom piece back on the floor, wherever that # is from here. self.bottom.setZ(host, 0.0) self.bottom.setPos(self.bottomPos[0], self.bottomPos[1], self.bottom.getZ()) # Also put the joystick in his hand. lookAtPoint = Point3(0.3, 0, 0.1) lookAtUp = Vec3(0, -1, 0) self.stickHinge.lookAt(host.rightHand, lookAtPoint, lookAtUp) host.play('walk') host.update()
def _initCollisions(self): collSphere = CollisionSphere(0, 0, 0, 3.0) collSphere.setTangible(0) self.collNode = CollisionNode(self.getName()) self.collNode.addSolid(collSphere) self.collNP = self.attachNewNode(self.collNode)
class CogdoMazeWaterCooler(NodePath, DirectObject): UpdateTaskName = 'CogdoMazeWaterCooler_Update' def __init__(self, serialNum, model): NodePath.__init__(self, 'CogdoMazeWaterCooler-%i' % serialNum) self.serialNum = serialNum self._model = model self._model.reparentTo(self) self._model.setPosHpr(0, 0, 0, 0, 0, 0) self._initCollisions() self._initArrow() self._update = None self.__startUpdateTask() return def destroy(self): self.ignoreAll() self.__stopUpdateTask() self.collNodePath.removeNode() self.removeNode() def _initCollisions(self): offset = Globals.WaterCoolerTriggerOffset self.collSphere = CollisionSphere(offset[0], offset[1], offset[2], Globals.WaterCoolerTriggerRadius) self.collSphere.setTangible(0) name = Globals.WaterCoolerCollisionName self.collNode = CollisionNode(name) self.collNode.addSolid(self.collSphere) self.collNodePath = self.attachNewNode(self.collNode) def _initArrow(self): matchingGameGui = loader.loadModel('phase_3.5/models/gui/matching_game_gui') arrow = matchingGameGui.find('**/minnieArrow') arrow.setScale(Globals.CoolerArrowScale) arrow.setColor(*Globals.CoolerArrowColor) arrow.setPos(0, 0, Globals.CoolerArrowZ) arrow.setHpr(0, 0, 90) arrow.setBillboardAxis() self._arrow = NodePath('Arrow') arrow.reparentTo(self._arrow) self._arrow.reparentTo(self) self._arrowTime = 0 self.accept(Globals.WaterCoolerShowEventName, self.showArrow) self.accept(Globals.WaterCoolerHideEventName, self.hideArrow) matchingGameGui.removeNode() def showArrow(self): self._arrow.unstash() def hideArrow(self): self._arrow.stash() def update(self, dt): newZ = math.sin(globalClock.getFrameTime() * Globals.CoolerArrowSpeed) * Globals.CoolerArrowBounce self._arrow.setZ(newZ) def __startUpdateTask(self): self.__stopUpdateTask() self._update = taskMgr.add(self._updateTask, self.UpdateTaskName, 45) def __stopUpdateTask(self): if self._update is not None: taskMgr.remove(self._update) return def _updateTask(self, task): dt = globalClock.getDt() self.update(dt) return Task.cont
class IceTreasure(DirectObject): """ Treasures toons can pickup swinging from ice to ice. Based on MazeTreasure """ notify = DirectNotifyGlobal.directNotify.newCategory("IceTreasure") RADIUS = 1.0 def __init__(self, model, pos, serialNum, gameId, penalty=False): # there are going to be MANY (~650) of these created and destroyed # all at once for 4-player games; make it lean self.serialNum = serialNum self.penalty = penalty # the fruit has a bit of height, lets recenter center = model.getBounds().getCenter() center = Point3(0, 0, 0) self.nodePath = model.copyTo(render) self.nodePath.setPos(pos[0] - center[0], pos[1] - center[1], pos[2] - center[2]) self.nodePath.setZ(0) # real assets have bottom at zero self.notify.debug('newPos = %s' % self.nodePath.getPos()) #self.nodePath.setScale(1.0) #if self.penalty: # self.nodePath.setColorScale(0.5,0.5,0.5,1.0) # Make a sphere, name it uniquely, and child it # to the nodepath. if self.penalty: self.sphereName = "penaltySphere-%s-%s" % (gameId, self.serialNum) else: self.sphereName = "treasureSphere-%s-%s" % (gameId, self.serialNum) self.collSphere = CollisionSphere(center[0], center[1], center[2], self.RADIUS) # Make the sphere intangible self.collSphere.setTangible(0) self.collNode = CollisionNode(self.sphereName) self.collNode.setIntoCollideMask(ToontownGlobals.PieBitmask) self.collNode.addSolid(self.collSphere) self.collNodePath = render.attachNewNode(self.collNode) self.collNodePath.setPos(pos[0] - center[0], pos[1] - center[1], pos[2] - center[2]) self.collNodePath.hide() self.track = None # Add a hook looking for collisions with localToon #self.accept('enter' + self.sphereName, self.__handleEnterSphere) # now that the treasure and sphere have been placed, flatten the # whole silly thing # self.nodePath.flattenLight() if self.penalty: #self.nodePath.setScale(1,1,0.5) self.tip = self.nodePath.find('**/fusetip') #self.tip.setX(2) #self.tip.setY(0.5) #self.tip.setZ(1.5) sparks = BattleParticles.createParticleEffect(file='icetnt') self.sparksEffect = sparks sparks.start(self.tip) self.penaltyGrabSound = loader.loadSfx( "phase_4/audio/sfx/MG_cannon_fire_alt.mp3") self.penaltyGrabSound.setVolume(0.75) kaboomAttachPoint = self.nodePath.attachNewNode('kaboomAttach') kaboomAttachPoint.setZ(3) self.kaboom = loader.loadModel( 'phase_4/models/minigames/ice_game_kaboom') self.kaboom.reparentTo(kaboomAttachPoint) #self.kaboom.hide() self.kaboom.setScale(2.0) self.kaboom.setBillboardPointEye() #self.kaboom.setBin('fixed', serialNum) #self.kaboom.setDepthTest(False) #self.kaboom.setDepthWrite(False) def destroy(self): self.ignoreAll() if self.penalty: self.sparksEffect.cleanup() if self.track: self.track.finish() self.nodePath.removeNode() del self.nodePath del self.collSphere self.collNodePath.removeNode() del self.collNodePath del self.collNode ## def __handleEnterSphere(self, collEntry): ## self.ignoreAll() ## # announce that this treasure was grabbed ## self.notify.debug('treasuerGrabbed') ## messenger.send("IceTreasureGrabbed", [self.serialNum]) def showGrab(self): self.nodePath.hide() self.collNodePath.hide() # disable collisions self.collNode.setIntoCollideMask(BitMask32(0)) if self.penalty: self.track = Parallel( SoundInterval(self.penaltyGrabSound), Sequence( Func(self.kaboom.showThrough), LerpScaleInterval(self.kaboom, duration=0.5, scale=Point3(10, 10, 10), blendType='easeOut'), Func(self.kaboom.hide), )) self.track.start()
class DistributedCogKart(DistributedElevatorExt.DistributedElevatorExt): notify = DirectNotifyGlobal.directNotify.newCategory("DistributedCogKart") JumpOutOffsets = ((6.5, -2, -0.025), (-6.5, -2, -0.025), (3.75, 5, -0.025), (-3.75, 5, -0.025)) def __init__(self, cr): """__init__(cr) """ DistributedElevatorExt.DistributedElevatorExt.__init__(self, cr) self.type = ElevatorConstants.ELEVATOR_COUNTRY_CLUB # note since we did elevator init last, self.fsm is DistributedElevator.fsm self.kartModelPath = 'phase_12/models/bossbotHQ/Coggolf_cart3.bam' self.leftDoor = None self.rightDoor = None self.fillSlotTrack = None def generate(self): """generate(self) This method is called when the DistributedObject is reintroduced to the world, either for the first time or from the cache. """ DistributedElevatorExt.DistributedElevatorExt.generate(self) # Get the state machine stuff for playGame self.loader = self.cr.playGame.hood.loader if(self.loader): self.notify.debug("Loader has been loaded") self.notify.debug(str(self.loader)) else: self.notify.debug("Loader has not been loaded") self.golfKart = render.attachNewNode('golfKartNode') self.kart = loader.loadModel(self.kartModelPath) self.kart.setPos(0, 0, 0) self.kart.setScale(1) self.kart.reparentTo(self.golfKart) self.golfKart.reparentTo(self.loader.geom) # Wheels self.wheels = self.kart.findAllMatches('**/wheelNode*') self.numWheels = self.wheels.getNumPaths() def announceGenerate(self): """Setup other fields dependent on the required fields.""" DistributedElevatorExt.DistributedElevatorExt.announceGenerate(self) angle = self.startingHpr[0] angle -= 90 radAngle = deg2Rad(angle) unitVec = Vec3( math.cos(radAngle), math.sin(radAngle), 0) unitVec *= 45.0 self.endPos = self.startingPos + unitVec self.endPos.setZ(0.5) dist = Vec3(self.endPos - self.enteringPos).length() wheelAngle = (dist / (4.8 * 1.4 * math.pi)) * 360 self.kartEnterAnimateInterval = Parallel( # start a lerp HPR for each wheel LerpHprInterval(self.wheels[0], 5.0, Vec3(self.wheels[0].getH(), wheelAngle, self.wheels[0].getR())), LerpHprInterval(self.wheels[1], 5.0, Vec3(self.wheels[1].getH(), wheelAngle, self.wheels[1].getR())), LerpHprInterval(self.wheels[2], 5.0, Vec3(self.wheels[2].getH(), wheelAngle, self.wheels[2].getR())), LerpHprInterval(self.wheels[3], 5.0, Vec3(self.wheels[3].getH(), wheelAngle, self.wheels[3].getR())), name = "CogKartAnimate") trolleyExitTrack1 = Parallel( LerpPosInterval(self.golfKart, 5.0, self.endPos), self.kartEnterAnimateInterval, name = "CogKartExitTrack") self.trolleyExitTrack = Sequence( trolleyExitTrack1, # Func(self.hideSittingToons), # we may not need this ) self.trolleyEnterTrack = Sequence( LerpPosInterval(self.golfKart, 5.0, self.startingPos, startPos = self.enteringPos)) self.closeDoors = Sequence( self.trolleyExitTrack, Func(self.onDoorCloseFinish)) self.openDoors = Sequence( self.trolleyEnterTrack ) def delete(self): """ This method is called when the DistributedObject is permanently removed from the world and deleted from the cache. """ DistributedElevatorExt.DistributedElevatorExt.delete(self) if hasattr(self, 'elevatorFSM') : del self.elevatorFSM def setBldgDoId(self, bldgDoId): """Handle the AI telling us the associated bldg doid.""" # The doId is junk, there is no building object for the factory # exterior elevators. Do the appropriate things that # DistributedElevator.gotBldg does. self.bldg = None self.setupElevatorKart() def setupElevatorKart(self): """Setup elevator related fields.""" # Establish a collision sphere. There must be an easier way! collisionRadius = ElevatorConstants.ElevatorData[self.type]['collRadius'] self.elevatorSphere = CollisionSphere(0, 0, 0, collisionRadius) self.elevatorSphere.setTangible(1) self.elevatorSphereNode = CollisionNode(self.uniqueName("elevatorSphere")) self.elevatorSphereNode.setIntoCollideMask(ToontownGlobals.WallBitmask) self.elevatorSphereNode.addSolid(self.elevatorSphere) self.elevatorSphereNodePath = self.getElevatorModel().attachNewNode( self.elevatorSphereNode) self.elevatorSphereNodePath.hide() self.elevatorSphereNodePath.reparentTo(self.getElevatorModel()) self.elevatorSphereNodePath.stash() self.boardedAvIds = {} self.finishSetup() def setColor(self, r, g, b): """Ignore this AI message to make it look cog grayish.""" pass def getElevatorModel(self): return self.golfKart def enterWaitEmpty(self, ts): """Handle entering the wait empty state.""" DistributedElevatorExt.DistributedElevatorExt.enterWaitEmpty(self, ts) def exitWaitEmpty(self): """Handle exiting the wait empty state.""" DistributedElevatorExt.DistributedElevatorExt.exitWaitEmpty(self) def forceDoorsOpen(self): """Deliberately do nothing.""" pass def forceDoorsClosed(self): """Deliberately do nothing.""" pass def setPosHpr(self, x, y, z, h, p ,r): """Set the pos hpr as dictated by the AI.""" self.startingPos = Vec3(x, y, z) self.enteringPos = Vec3(x, y, z - 10) self.startingHpr = Vec3(h, 0, 0) self.golfKart.setPosHpr( x, y, z, h, 0, 0 ) def enterClosing(self, ts): # Close the elevator doors if self.localToonOnBoard: elevator = self.getPlaceElevator() if elevator: elevator.fsm.request("elevatorClosing") self.closeDoors.start(ts) def enterClosed(self, ts): self.forceDoorsClosed() self.kartDoorsClosed(self.getZoneId()) return def kartDoorsClosed(self, zoneId): assert(self.notify.debug('doorsClosed()')) if (self.localToonOnBoard): hoodId = ZoneUtil.getHoodId(zoneId) doneStatus = { 'loader' : 'suitInterior', 'where' : 'suitInterior', 'hoodId' : hoodId, 'zoneId' : zoneId, 'shardId' : None, } elevator = self.elevatorFSM #self.getPlaceElevator() del self.elevatorFSM elevator.signalDone(doneStatus) def setCountryClubInteriorZone(self, zoneId): if (self.localToonOnBoard): hoodId = self.cr.playGame.hood.hoodId countryClubId = self.countryClubId if bboard.has('countryClubIdOverride'): countryClubId = bboard.get('countryClubIdOverride') doneStatus = { 'loader' : "cogHQLoader", 'where' : "countryClubInterior", 'how' : "teleportIn", 'zoneId' : zoneId, 'countryClubId' : self.countryClubId, 'hoodId' : hoodId, } self.cr.playGame.getPlace().elevator.signalDone(doneStatus) def setCountryClubInteriorZoneForce(self, zoneId): place = self.cr.playGame.getPlace() if place: place.fsm.request("elevator", [self, 1]) hoodId = self.cr.playGame.hood.hoodId countryClubId = self.countryClubId if bboard.has('countryClubIdOverride'): countryClubId = bboard.get('countryClubIdOverride') doneStatus = { 'loader' : "cogHQLoader", 'where' : "countryClubInterior", 'how' : "teleportIn", 'zoneId' : zoneId, 'countryClubId' : self.countryClubId, 'hoodId' : hoodId, } if hasattr(place, 'elevator') and place.elevator: place.elevator.signalDone(doneStatus) else: self.notify.warning("setMintInteriorZoneForce: Couldn't find playGame.getPlace().elevator, zoneId: %s" %zoneId) else: self.notify.warning("setCountryClubInteriorZoneForce: Couldn't find playGame.getPlace(), zoneId: %s" %zoneId) def setCountryClubId(self, countryClubId): self.countryClubId = countryClubId def getZoneId(self): return 0 def fillSlot(self, index, avId, wantBoardingShow = 0): """Put someone in the kart, as dictated by the AI.""" self.notify.debug("%s.fillSlot(%s, %s, ... %s)" % (self.doId, index, avId, globalClock.getRealTime())) request = self.toonRequests.get(index) if request: self.cr.relatedObjectMgr.abortRequest(request) del self.toonRequests[index] if avId == 0: # This means that the slot is now empty, and no action should # be taken. pass elif avId not in self.cr.doId2do: # It's someone who hasn't been generated yet. func = PythonUtil.Functor( self.gotToon, index, avId) assert index not in self.toonRequests self.toonRequests[index] = self.cr.relatedObjectMgr.requestObjects( [avId], allCallback = func) elif not self.isSetup: # We haven't set up the elevator yet. self.deferredSlots.append((index, avId, wantBoardingShow)) else: # If localToon is boarding, he needs to change state if avId == base.localAvatar.getDoId(): place = base.cr.playGame.getPlace() if not place: return elevator = self.getPlaceElevator() if elevator == None: place.fsm.request('elevator') elevator = self.getPlaceElevator() if not elevator: return self.localToonOnBoard = 1 if hasattr(localAvatar, "boardingParty") and localAvatar.boardingParty: localAvatar.boardingParty.forceCleanupInviteePanel() localAvatar.boardingParty.forceCleanupInviterPanels() # Cleanup any leftover elevator messages before boarding the elevator. if hasattr(base.localAvatar, "elevatorNotifier"): base.localAvatar.elevatorNotifier.cleanup() cameraTrack = Sequence() # Move the camera towards and face the elevator. cameraTrack.append(Func(elevator.fsm.request, "boarding", [self.getElevatorModel()])) # Enable the Hop off button. cameraTrack.append(Func(elevator.fsm.request, "boarded")) toon = self.cr.doId2do[avId] # Parent it to the elevator toon.stopSmooth() toon.wrtReparentTo(self.golfKart) sitStartDuration = toon.getDuration("sit-start") jumpTrack = self.generateToonJumpTrack(toon, index) track = Sequence( jumpTrack, Func(toon.setAnimState, "Sit", 1.0), Func(self.clearToonTrack, avId), name = toon.uniqueName("fillElevator"), autoPause = 1) if wantBoardingShow: boardingTrack, boardingTrackType = self.getBoardingTrack(toon, index, True) track = Sequence(boardingTrack, track) if avId == base.localAvatar.getDoId(): cameraWaitTime = 2.5 if (boardingTrackType == BoardingGroupShow.TRACK_TYPE_RUN): cameraWaitTime = 0.5 cameraTrack = Sequence(Wait(cameraWaitTime), cameraTrack) if self.canHideBoardingQuitBtn(avId): track = Sequence(Func(localAvatar.boardingParty.groupPanel.disableQuitButton), track) # Start the camera track in parallel here if avId == base.localAvatar.getDoId(): track = Parallel(cameraTrack, track) track.delayDelete = DelayDelete.DelayDelete(toon, 'CogKart.fillSlot') self.storeToonTrack(avId, track) track.start() self.fillSlotTrack = track assert avId not in self.boardedAvIds self.boardedAvIds[avId] = None def generateToonJumpTrack(self, av, seatIndex): """Return an interval of the toon jumping into the golf kart.""" av.pose('sit', 47) hipOffset = av.getHipsParts()[2].getPos(av) def getToonJumpTrack( av, seatIndex ): # using a local func allows the ProjectileInterval to # calculate this pos at run-time def getJumpDest(av = av, node = self.golfKart): dest = Point3(0,0,0) if hasattr(self, 'golfKart') and self.golfKart: dest = Vec3(self.golfKart.getPos(av.getParent())) seatNode = self.golfKart.find("**/seat" + str(seatIndex + 1)) dest += seatNode.getPos(self.golfKart) dna = av.getStyle() dest -= hipOffset if(seatIndex < 2): dest.setY( dest.getY() + 2 * hipOffset.getY()) dest.setZ(dest.getZ() + 0.1) else: self.notify.warning('getJumpDestinvalid golfKart, returning (0,0,0)') return dest def getJumpHpr(av = av, node = self.golfKart): hpr = Point3(0,0,0) if hasattr(self, 'golfKart') and self.golfKart: hpr = self.golfKart.getHpr(av.getParent()) if(seatIndex < 2): hpr.setX( hpr.getX() + 180) else: hpr.setX( hpr.getX() ) angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) hpr.setX(angle) else: self.notify.warning('getJumpHpr invalid golfKart, returning (0,0,0)') return hpr toonJumpTrack = Parallel(ActorInterval( av, 'jump' ), Sequence(Wait( 0.43 ), Parallel(LerpHprInterval(av, hpr = getJumpHpr, duration = .9 ), ProjectileInterval(av, endPos = getJumpDest, duration = .9 ), ), ), ) return toonJumpTrack def getToonSitTrack( av ): toonSitTrack = Sequence( ActorInterval( av, 'sit-start' ), Func( av.loop, 'sit' ) ) return toonSitTrack toonJumpTrack = getToonJumpTrack( av, seatIndex ) toonSitTrack = getToonSitTrack( av ) jumpTrack = Sequence( Parallel( toonJumpTrack, Sequence( Wait(1), toonSitTrack, ), ), ## Func( av.wrtReparentTo, self.golfKart ), ) return jumpTrack def emptySlot(self, index, avId, bailFlag, timestamp, timeSent=0): """Remove someone as dictated by the AI.""" if self.fillSlotTrack: self.fillSlotTrack.finish() self.fillSlotTrack = None # If localToon is exiting, he needs to change state if avId == 0: # This means that no one is currently exiting, and no action # should be taken pass elif not self.isSetup: # We haven't set up the elevator yet. Remove the toon # from the deferredSlots list, if it is there. newSlots = [] for slot in self.deferredSlots: if slot[0] != index: newSlots.append(slot) self.deferredSlots = newSlots else: if avId in self.cr.doId2do: # See if we need to reset the clock # (countdown assumes we've created a clockNode already) if (bailFlag == 1 and hasattr(self, 'clockNode')): if (timestamp < self.countdownTime and timestamp >= 0): self.countdown(self.countdownTime - timestamp) else: self.countdown(self.countdownTime) # If the toon exists, look it up toon = self.cr.doId2do[avId] # avoid wrtReparent so that we don't muck with the toon's scale # Parent it to render #toon.wrtReparentTo(render) toon.stopSmooth() sitStartDuration = toon.getDuration("sit-start") jumpOutTrack = self.generateToonReverseJumpTrack(toon, index) # Place it on the appropriate spot relative to the # elevator track = Sequence( # TODO: Find the right coords for the elevator jumpOutTrack, # Tell the toon he is free to roam now Func(self.notifyToonOffElevator, toon), Func(self.clearToonTrack, avId), name = toon.uniqueName("emptyElevator"), autoPause = 1) if self.canHideBoardingQuitBtn(avId): # Enable the Boarding Group Panel Quit Button here if it is relevant. track.append(Func(localAvatar.boardingParty.groupPanel.enableQuitButton)) # Enable the Boarding Group GO Button here if it is relevant. track.append(Func(localAvatar.boardingParty.enableGoButton)) track.delayDelete = DelayDelete.DelayDelete(toon, 'CogKart.emptySlot') self.storeToonTrack(toon.doId, track) track.start() # Tell localToon he is exiting (if localToon is on board) if avId == base.localAvatar.getDoId(): messenger.send("exitElevator") # if the elevator is generated as a toon is leaving it, # we will not have gotten a corresponding 'fillSlot' message # for that toon, hence the toon will not be found in # boardedAvIds if avId in self.boardedAvIds: del self.boardedAvIds[avId] else: self.notify.warning("toon: " + str(avId) + " doesn't exist, and" + " cannot exit the elevator!") def generateToonReverseJumpTrack( self, av, seatIndex ): """Return an interval of the toon jumping out of the golf kart.""" self.notify.debug("av.getH() = %s" % av.getH()) def getToonJumpTrack( av, destNode ): # using a local func allows the ProjectileInterval to # calculate this pos at run-time def getJumpDest(av = av, node = destNode): dest = node.getPos(av.getParent()) dest += Vec3(*self.JumpOutOffsets[seatIndex]) return dest def getJumpHpr(av = av, node = destNode): hpr = node.getHpr(av.getParent()) hpr.setX( hpr.getX() + 180) angle = PythonUtil.fitDestAngle2Src(av.getH(), hpr.getX()) hpr.setX(angle) return hpr toonJumpTrack = Parallel( ActorInterval( av, 'jump' ), Sequence( Wait( 0.1), #43 ), Parallel( #LerpHprInterval( av, # hpr = getJumpHpr, # duration = .9 ), ProjectileInterval( av, endPos = getJumpDest, duration = .9 ) ) ) ) return toonJumpTrack toonJumpTrack = getToonJumpTrack( av, self.golfKart) jumpTrack = Sequence( toonJumpTrack, Func( av.loop, 'neutral' ), Func( av.wrtReparentTo, render ), #Func( self.av.setPosHpr, self.exitMovieNode, 0,0,0,0,0,0 ), ) return jumpTrack def startCountdownClock(self, countdownTime, ts): """Start the countdown clock.""" # just reverse the text counter DistributedElevatorExt.DistributedElevatorExt.startCountdownClock(self, countdownTime, ts) self.clock.setH(self.clock.getH() + 180) def rejectBoard(self, avId, reason = 0): """Show the reason why he was rejected.""" # Only difference from base clase is the use of KartMinLaff # This should only be sent to us if our localToon requested # permission to board the elevator. # reason 0: unknown, 1: shuffle, 2: too low laff, 3: no seat, 4: need promotion print(("rejectBoard %s" % (reason))) if hasattr(base.localAvatar, "elevatorNotifier"): if reason == ElevatorConstants.REJECT_SHUFFLE: base.localAvatar.elevatorNotifier.showMe(TTLocalizer.ElevatorHoppedOff) elif reason == ElevatorConstants.REJECT_MINLAFF: base.localAvatar.elevatorNotifier.showMe((TTLocalizer.KartMinLaff % (self.minLaff))) elif reason == ElevatorConstants.REJECT_PROMOTION: base.localAvatar.elevatorNotifier.showMe(TTLocalizer.BossElevatorRejectMessage) elif reason == ElevatorConstants.REJECT_NOT_YET_AVAILABLE: base.localAvatar.elevatorNotifier.showMe(TTLocalizer.NotYetAvailable) assert(base.localAvatar.getDoId() == avId) doneStatus = { 'where' : 'reject', } elevator = self.getPlaceElevator() if elevator: elevator.signalDone(doneStatus) def getDestName(self): if self.countryClubId == ToontownGlobals.BossbotCountryClubIntA: return TTLocalizer.ElevatorBossBotCourse0 elif self.countryClubId == ToontownGlobals.BossbotCountryClubIntB: return TTLocalizer.ElevatorBossBotCourse1 elif self.countryClubId == ToontownGlobals.BossbotCountryClubIntC: return TTLocalizer.ElevatorBossBotCourse2
class CogThief(DirectObject): """This represents a single cog thief in the cog thief game""" notify = directNotify.newCategory("CogThief") DefaultSpeedWalkAnim = 4. CollisionRadius = 1.25 MaxFriendsVisible = 4 Infinity = 100000.0 # just a really big number SeparationDistance = 6.0 MinUrgency = 0.5 MaxUrgency = 0.75 def __init__(self, cogIndex, suitType, game, cogSpeed): self.cogIndex = cogIndex self.suitType = suitType self.game = game self.cogSpeed = cogSpeed suit = Suit.Suit() d = SuitDNA.SuitDNA() d.newSuit(suitType) suit.setDNA(d) # cache the walk anim suit.pose('walk', 0) self.suit = suit self.goal = CTGG.NoGoal self.goalId = CTGG.InvalidGoalId self.lastLocalTimeStampFromAI = 0 self.lastPosFromAI = Point3(0, 0, 0) self.lastThinkTime = 0 self.doneAdjust = False self.barrel = CTGG.NoBarrelCarried self.signalledAtReturnPos = False self.defaultPlayRate = 1.0 self.netTimeSentToStartByHit = 0 # steering loosely based on boid code game programming gems #1 # "Portions Copyright (C) Steven Woodcock, 2000" self.velocity = Vec3(0, 0, 0) self.oldVelocity = Vec3(0, 0, 0) self.acceleration = Vec3(0, 0, 0) self.bodyLength = self.CollisionRadius * 2 # Desired distance from closest neighbor when flying. self.cruiseDistance = 2 * self.bodyLength self.maxVelocity = self.cogSpeed # Maximum magnitude of acceleration as a fraction of maxSpeed. self.maxAcceleration = 5.0 self.perceptionRange = 6 self.notify.debug('cogSpeed=%s' % self.cogSpeed) self.kaboomSound = loader.loadSfx( "phase_4/audio/sfx/MG_cannon_fire_alt.mp3") self.kaboom = loader.loadModel( 'phase_4/models/minigames/ice_game_kaboom') self.kaboom.setScale(2.0) self.kaboom.setBillboardPointEye() self.kaboom.hide() self.kaboomTrack = None splatName = 'splat-creampie' self.splat = globalPropPool.getProp(splatName) self.splat.setBillboardPointEye() self.splatType = globalPropPool.getPropType(splatName) self.pieHitSound = globalBattleSoundCache.getSound( 'AA_wholepie_only.mp3') def destroy(self): self.ignoreAll() self.suit.delete() self.game = None def uniqueName(self, baseStr): return baseStr + '-' + str(self.game.doId) def handleEnterSphere(self, collEntry): """Handle the suit colliding with localToon.""" #assert self.notify.debugStateCall(self) intoNp = collEntry.getIntoNodePath() self.notify.debug('handleEnterSphere suit %d hit %s' % (self.cogIndex, intoNp)) if self.game: self.game.handleEnterSphere(collEntry) def gameStart(self, gameStartTime): self.gameStartTime = gameStartTime self.initCollisions() self.startWalkAnim() def gameEnd(self): self.moveIval.pause() del self.moveIval self.shutdownCollisions() # keep the suits from walking in place self.suit.loop('neutral') def initCollisions(self): # Make a sphere, give it a unique name, and parent it # to the suit. self.collSphere = CollisionSphere(0, 0, 0, 1.25) # Make he sphere intangible self.collSphere.setTangible(1) name = "CogThiefSphere-%d" % self.cogIndex self.collSphereName = self.uniqueName(name) self.collNode = CollisionNode(self.collSphereName) self.collNode.setIntoCollideMask(CTGG.BarrelBitmask | ToontownGlobals.WallBitmask) self.collNode.addSolid(self.collSphere) self.collNodePath = self.suit.attachNewNode(self.collNode) #self.collNodePath.hide() # Add a hook looking for collisions with localToon self.accept('enter' + self.collSphereName, self.handleEnterSphere) # we need a taller collision tube to collide against for pie self.pieCollSphere = CollisionTube(0, 0, 0, 0, 0, 4, self.CollisionRadius) # Make he sphere intangible self.pieCollSphere.setTangible(1) name = "CogThiefPieSphere-%d" % self.cogIndex self.pieCollSphereName = self.uniqueName(name) self.pieCollNode = CollisionNode(self.pieCollSphereName) self.pieCollNode.setIntoCollideMask(ToontownGlobals.PieBitmask) self.pieCollNode.addSolid(self.pieCollSphere) self.pieCollNodePath = self.suit.attachNewNode(self.pieCollNode) #self.pieCollNodePath.show() # Add a hook looking for collisions with localToon #self.accept('enter' + self.pieCollSphereName, # self.handleEnter) def shutdownCollisions(self): self.ignore(self.uniqueName('enter' + self.collSphereName)) del self.collSphere self.collNodePath.removeNode() del self.collNodePath del self.collNode def updateGoal(self, timestamp, inResponseClientStamp, goalType, goalId, pos): """Update our goal and position.""" assert self.notify.debugStateCall(self) self.notify.debug('self.netTimeSentToStartByHit =%s' % self.netTimeSentToStartByHit) if not self.game: self.notify.debug('updateGoal self.game is None, just returning') return if not self.suit: self.notify.debug('updateGoal self.suit is None, just returning') return if self.goal == CTGG.NoGoal: self.startWalkAnim() if goalType == CTGG.NoGoal: self.notify.debug('updateGoal setting position to %s' % pos) self.suit.setPos(pos) self.lastThinkTime = 0 self.velocity = Vec3(0, 0, 0) self.oldVelocity = Vec3(0, 0, 0) self.acceleration = Vec3(0, 0, 0) if goalType == CTGG.RunAwayGoal: #import pdb; pdb.set_trace() pass if inResponseClientStamp < self.netTimeSentToStartByHit and \ self.goal == CTGG.NoGoal and \ goalType == CTGG.RunAwayGoal: #import pdb; pdb.set_trace() self.notify.warning( 'ignoring newGoal %s as cog %d was recently hit responsetime=%s hitTime=%s' % (CTGG.GoalStr[goalType], self.cogIndex, inResponseClientStamp, self.netTimeSentToStartByHit)) else: self.lastLocalTimeStampFromAI = globalClockDelta.networkToLocalTime( timestamp, bits=32) self.goal = goalType self.goalId = goalId self.lastPosFromAI = pos self.doneAdjust = False self.signalledAtReturnPos = False # TODO move the suit to where we expect him to be given the time difference def startWalkAnim(self): if self.suit: self.suit.loop('walk') speed = self.cogSpeed # float(MazeData.CELL_WIDTH) / self.cellWalkDuration self.defaultPlayRate = float(self.cogSpeed / self.DefaultSpeedWalkAnim) self.suit.setPlayRate(self.defaultPlayRate, 'walk') def think(self): """Calculate where we should go.""" if self.goal == CTGG.ToonGoal: self.thinkAboutCatchingToon() elif self.goal == CTGG.BarrelGoal: self.thinkAboutGettingBarrel() elif self.goal == CTGG.RunAwayGoal: self.thinkAboutRunAway() def thinkAboutCatchingToon(self): if not self.game: return av = self.game.getAvatar(self.goalId) if av: if not self.lastThinkTime: self.lastThinkTime = globalClock.getFrameTime() diffTime = globalClock.getFrameTime() - self.lastThinkTime avPos = av.getPos() myPos = self.suit.getPos() if not self.doneAdjust: myPos = self.lastPosFromAI self.notify.debug( 'thinkAboutCatchingToon not doneAdjust setting pos %s' % myPos) self.doneAdjust = True self.suit.setPos(myPos) if self.game.isToonPlayingHitTrack(self.goalId): # do nothing, just look at toon self.suit.headsUp(av) self.velocity = Vec3(0, 0, 0) self.oldVelocity = Vec3(0, 0, 0) self.acceleration = Vec3(0, 0, 0) else: self.commonMove() newPos = self.suit.getPos() self.adjustPlayRate(newPos, myPos, diffTime) self.lastThinkTime = globalClock.getFrameTime() def convertNetworkStampToGameTime(self, timestamp): """Convert a network timestamp to game time.""" localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) gameTime = self.game.local2GameTime(localStamp) return gameTime def respondToToonHit(self, timestamp): """The toon hit us, react appropriately.""" assert self.notify.debugStateCall(self) localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) # using 1.0 sec as fudge #if localStamp > self.lastLocalTimeStampFromAI: if self.netTimeSentToStartByHit < timestamp: self.clearGoal() self.showKaboom() # move him to his starting postion startPos = CTGG.CogStartingPositions[self.cogIndex] oldPos = self.suit.getPos() self.suit.setPos(startPos) if self.netTimeSentToStartByHit < timestamp: self.netTimeSentToStartByHit = timestamp else: self.notify.debug( 'localStamp = %s, lastLocalTimeStampFromAI=%s, ignoring respondToToonHit' % (localStamp, self.lastLocalTimeStampFromAI)) self.notify.debug( 'respondToToonHit self.netTimeSentToStartByHit = %s' % self.netTimeSentToStartByHit) def clearGoal(self): """Clear goal and goal id.""" self.goal = CTGG.NoGoal self.goalId = CTGG.InvalidGoalId def thinkAboutGettingBarrel(self): """Go for a barrel.""" if not self.game: return if not hasattr(self.game, 'barrels'): return if not self.goalId in range(len(self.game.barrels)): return if not self.lastThinkTime: self.lastThinkTime = globalClock.getFrameTime() diffTime = globalClock.getFrameTime() - self.lastThinkTime barrel = self.game.barrels[self.goalId] barrelPos = barrel.getPos() myPos = self.suit.getPos() if not self.doneAdjust: myPos = self.lastPosFromAI self.notify.debug( 'thinkAboutGettingBarrel not doneAdjust setting position to %s' % myPos) self.suit.setPos(myPos) """ diffTime = globalClock.getFrameTime()- self.lastLocalTimeStampFromAI self.notify.debug('doing adjust, diffTime = %s' % diffTime) if diffTime < 0: # it just looks really weird when it moves backwards diffTime = 0 self.notify.debug('forcing diffTime to %s' % diffTime) """ self.doneAdjust = True displacement = barrelPos - myPos distanceToToon = displacement.length() #self.notify.debug('diffTime = %s' % diffTime) self.suit.headsUp(barrel) lengthTravelled = diffTime * self.cogSpeed #self.notify.debug('lengthTravelled = %s' % lengthTravelled) # don't overshoot our target if lengthTravelled > distanceToToon: lengthTravelled = distanceToToon #self.notify.debug('overshooting lengthTravelled = %s' % lengthTravelled) displacement.normalize() dirVector = displacement dirVector *= lengthTravelled newPos = myPos + dirVector # always keep them grounded newPos.setZ(0) self.suit.setPos(newPos) self.adjustPlayRate(newPos, myPos, diffTime) self.lastThinkTime = globalClock.getFrameTime() def stopWalking(self, timestamp): """Stop the cog from walking.""" localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) if localStamp > self.lastLocalTimeStampFromAI: self.suit.loop('neutral') self.clearGoal() def thinkAboutRunAway(self): """Go for a barrel.""" if not self.game: return if not self.lastThinkTime: self.lastThinkTime = globalClock.getFrameTime() diffTime = globalClock.getFrameTime() - self.lastThinkTime returnPos = CTGG.CogReturnPositions[self.goalId] myPos = self.suit.getPos() if not self.doneAdjust: myPos = self.lastPosFromAI self.suit.setPos(myPos) """ diffTime = globalClock.getFrameTime()- self.lastLocalTimeStampFromAI self.notify.debug('run away doing adjust, diffTime = %s' % diffTime) if diffTime < 0: # it just looks really weird when it moves backwards diffTime = 0 self.notify.debug('forcing diffTime to %s' % diffTime) """ self.doneAdjust = True displacement = returnPos - myPos distanceToToon = displacement.length() #self.notify.debug('diffTime = %s' % diffTime) tempNp = render.attachNewNode('tempRet') tempNp.setPos(returnPos) self.suit.headsUp(tempNp) tempNp.removeNode() lengthTravelled = diffTime * self.cogSpeed #self.notify.debug('lengthTravelled = %s' % lengthTravelled) # don't overshoot our target if lengthTravelled > distanceToToon: lengthTravelled = distanceToToon #self.notify.debug('overshooting lengthTravelled = %s' % lengthTravelled) displacement.normalize() dirVector = displacement dirVector *= lengthTravelled newPos = myPos + dirVector # always keep them grounded newPos.setZ(0) self.suit.setPos(newPos) self.adjustPlayRate(newPos, myPos, diffTime) if (self.suit.getPos() - returnPos).length() < 0.0001: if not self.signalledAtReturnPos and self.barrel >= 0: # tell the AI we're at return Pos self.game.sendCogAtReturnPos(self.cogIndex, self.barrel) self.signalledAtReturnPos = True self.lastThinkTime = globalClock.getFrameTime() def makeCogCarryBarrel(self, timestamp, inResponseClientStamp, barrelModel, barrelIndex, cogPos): """Handle the AI telling us the barrel is attached to a cog.""" #assert self.notify.debugStateCall(self) if not self.game: return localTimeStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) # TODO validate time? self.lastLocalTimeStampFromAI = localTimeStamp inResponseGameTime = self.convertNetworkStampToGameTime( inResponseClientStamp) self.notify.debug('inResponseGameTime =%s timeSentToStart=%s' % (inResponseGameTime, self.netTimeSentToStartByHit)) if inResponseClientStamp < self.netTimeSentToStartByHit and \ self.goal == CTGG.NoGoal: self.notify.warning('ignoring makeCogCarrybarrel') else: barrelModel.setPos(0, -1.0, 1.5) barrelModel.reparentTo(self.suit) self.suit.setPos(cogPos) self.barrel = barrelIndex def makeCogDropBarrel(self, timestamp, inResponseClientStamp, barrelModel, barrelIndex, barrelPos): """Handle the AI telling us the barrel is attached to a cog.""" #assert self.notify.debugStateCall(self) localTimeStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) # TODO validate time? self.lastLocalTimeStampFromAI = localTimeStamp barrelModel.reparentTo(render) barrelModel.setPos(barrelPos) self.barrel = CTGG.NoBarrelCarried # #self.suit.setPos(cogPos) def respondToPieHit(self, timestamp): """The toon hit us, react appropriately.""" assert self.notify.debugStateCall(self) localStamp = globalClockDelta.networkToLocalTime(timestamp, bits=32) # argh using 1.0 sec as fudge #if localStamp > self.lastLocalTimeStampFromAI: if self.netTimeSentToStartByHit < timestamp: self.clearGoal() self.showSplat() # move him to his starting postion startPos = CTGG.CogStartingPositions[self.cogIndex] oldPos = self.suit.getPos() self.suit.setPos(startPos) if self.netTimeSentToStartByHit < timestamp: self.netTimeSentToStartByHit = timestamp else: self.notify.debug( 'localStamp = %s, lastLocalTimeStampFromAI=%s, ignoring respondToPieHit' % (localStamp, self.lastLocalTimeStampFromAI)) self.notify.debug( 'respondToPieHit self.netTimeSentToStartByHit = %s' % self.netTimeSentToStartByHit) def cleanup(self): """Do whatever is necessary to cleanup properly.""" self.clearGoal() self.ignoreAll() self.suit.delete() if self.kaboomTrack and self.kaboomTrack.isPlaying(): self.kaboomTrack.finish() self.suit = None self.game = None def adjustPlayRate(self, newPos, oldPos, diffTime): """Adjust animation rate based on how far he's moved.""" # lets slowdown playrate if they're not moving much lengthTravelled = (newPos - oldPos).length() if diffTime: speed = lengthTravelled / diffTime else: speed = self.cogSpeed rateMult = speed / self.cogSpeed newRate = rateMult * self.defaultPlayRate self.suit.setPlayRate(newRate, 'walk') def commonMove(self): """Move the cog thief. Common for all 3 behaviors """ if not self.lastThinkTime: self.lastThinkTime = globalClock.getFrameTime() dt = globalClock.getFrameTime() - self.lastThinkTime # Step 1: Update our position. # Update our position based on the velocity # vector we computed last time around. self.oldpos = self.suit.getPos() # save off our previous position pos = self.suit.getPos() pos += self.velocity * dt # apply velocities. self.suit.setPos(pos) # Step 2: SeeFriends. # Determine if we can see any of our flockmates. self.seeFriends() acc = Vec3(0, 0, 0) # well first off we want to move to our target self.accumulate(acc, self.getTargetVector()) # Step 3: Flocking behavior. # Do we see any of our flockmates? If yes, it's time to implement # the first Three Rules (they don't matter if we can't see anybody) if self.numFlockmatesSeen > 0: #if hasattr(base,'doDebug') and base.doDebug: # import pdb; pdb.set_trace() keepDistanceVector = self.keepDistance() oldAcc = Vec3(acc) self.accumulate(acc, keepDistanceVector) if self.cogIndex == 0: #self.notify.debug('oldAcc=%s, keepDist=%s newAcc=%s' % # (oldAcc,keepDistanceVector, acc)) pass # Step 8: Constrain acceleration # If our acceleration change is more than we allow, constrain it if (acc.length() > self.maxAcceleration): # definitely too much...constrain to maximum change acc.normalize() acc *= self.maxAcceleration # Step 9: Implementation. # Here's where we apply our newly computed acceleration vector # to create a new velocity vector to use next update cycle. self.oldVelocity = self.velocity # save off our previous velocity # now add in the acceleration self.velocity += acc # Step 10: constraint Y velocity changes. # Attempt to restrict flight straight up/down by damping out Y axis velocity. # This isn't strictly necessary, but does lead to more realistic looking flight. # Step 11: Constrain our speed. # If we're moving faster than we're allowed to move, constrain our velocity. if self.velocity.length() > self.maxVelocity: self.velocity.normalize() self.velocity *= self.maxVelocity # Step 12: Compute roll/pitch/yaw. # Compute our orientation after all this speed adjustment nonsense. # bah no need, we turn on a dime towards our velocity forwardVec = Vec3(1, 0, 0) heading = rad2Deg(math.atan2(self.velocity[1], self.velocity[0])) heading -= 90 self.suit.setH(heading) def getTargetVector(self): """Return a vector to my goal.""" targetPos = Point3(0, 0, 0) if self.goal == CTGG.ToonGoal: av = self.game.getAvatar(self.goalId) if av: targetPos = av.getPos() elif self.goal == CTGG.BarrelGoal: barrel = self.game.barrels[self.goalId] targetPos = barrel.getPos() elif self.goal == CTGG.RunAwayGoal: targetPos = CTGG.CogReturnPositions[self.goalId] targetPos.setZ(0) myPos = self.suit.getPos() diff = targetPos - myPos if diff.length() > 1.0: diff.normalize() diff *= 1.0 return diff def accumulate(self, accumulator, valueToAdd): """Return the magnitude of the accumulated vector.""" accumulator += valueToAdd return accumulator.length() def seeFriends(self): """Determines which flockmates a given flock boid can see.""" # clear the existing visibility list of any holdover from last round self.clearVisibleList() for cogIndex in list(self.game.cogInfo.keys()): if cogIndex == self.cogIndex: continue if self.sameGoal(cogIndex): dist = self.canISee(cogIndex) if dist != self.Infinity: self.addToVisibleList(cogIndex) if dist < self.distToNearestFlockmate: self.nearestFlockmate = cogIndex self.distToNearestFlockmate = dist return self.numFlockmatesSeen def clearVisibleList(self): """Clears the visibility list and associated fields.""" self.visibleFriendsList = [] self.numFlockmatesSeen = 0 self.nearestFlockmate = None self.distToNearestFlockmate = self.Infinity def addToVisibleList(self, cogIndex): """Add the cog to the visible list.""" # test: do we see enough buddies already? if self.numFlockmatesSeen < self.MaxFriendsVisible: #nope--we can add to this one to the list self.visibleFriendsList.append(cogIndex) self.numFlockmatesSeen += 1 if self.cogIndex == 0: #self.notify.debug('self.numFlockmatesSeen = %s' % self.numFlockmatesSeen) pass def canISee(self, cogIndex): """Return distance if I can see the other cog, infinity otherwise""" if self.cogIndex == cogIndex: # well we should never see ourself return self.Infinity cogThief = self.game.getCogThief(cogIndex) distance = self.suit.getDistance(cogThief.suit) if distance < self.perceptionRange: #self.notify.debug('%s can see %s' % (self.cogIndex, cogIndex)) return distance # fell through; can not see it return self.Infinity def sameGoal(self, cogIndex): """Return true if we have the same goal.""" cogThief = self.game.getCogThief(cogIndex) result = (cogThief.goalId == self.goalId) and (cogThief.goal == self.goal) return result def keepDistance(self): """Generates a vector for a flock boid to maintain his desired separation distance from the nearest flockmate he sees. """ ratio = self.distToNearestFlockmate / self.SeparationDistance nearestThief = self.game.getCogThief(self.nearestFlockmate) change = nearestThief.suit.getPos() - self.suit.getPos() if ratio < self.MinUrgency: ratio = self.MinUrgency if ratio > self.MaxUrgency: ratio = self.MaxUrgency # test: are we too close to our nearest flockmate? if self.distToNearestFlockmate < self.SeparationDistance: #self.notify.debug('%d is too close to %d' % (self.cogIndex, self.nearestFlockmate)) # too close...move away from our neighbor change.normalize() change *= -(1 - ratio ) # the close we are the more we are pushed away elif self.distToNearestFlockmate > self.SeparationDistance: # too far away move towards our neighbor change.normalize() change *= ratio else: # in the UNLIKELY event we're exactly the right distance away, do nothing change = Vec3(0, 0, 0) return change def showKaboom(self): """Show the kaboom graphic and sound.""" if self.kaboomTrack and self.kaboomTrack.isPlaying(): self.kaboomTrack.finish() self.kaboom.reparentTo(render) self.kaboom.setPos(self.suit.getPos()) self.kaboom.setZ(3) self.kaboomTrack = Parallel( SoundInterval(self.kaboomSound, volume=0.5), Sequence( Func(self.kaboom.showThrough), LerpScaleInterval(self.kaboom, duration=0.5, scale=Point3(10, 10, 10), startScale=Point3(1, 1, 1), blendType='easeOut'), Func(self.kaboom.hide), )) self.kaboomTrack.start() def showSplat(self): """Show the splat graphic and sound.""" if self.kaboomTrack and self.kaboomTrack.isPlaying(): self.kaboomTrack.finish() self.splat.reparentTo(render) self.splat.setPos(self.suit.getPos()) self.splat.setZ(3) self.kaboomTrack = Parallel( SoundInterval(self.pieHitSound, volume=1.0), Sequence( Func(self.splat.showThrough), LerpScaleInterval(self.splat, duration=0.5, scale=1.75, startScale=Point3(0.1, 0.1, 0.1), blendType='easeOut'), Func(self.splat.hide), )) self.kaboomTrack.start()
class PartyCog(FSM): notify = directNotify.newCategory("PartyCog") HpTextGenerator = TextNode("HpTextGenerator") hpText = None height = 7 def __init__(self, parentNode, id, bounceSpeed=3, bounceHeight=1, rotateSpeed=1, heightShift=1, xMoveSpeed=0, xMoveDistance=0, bounceOffset=0): self.id = id FSM.__init__(self, "PartyCogFSM-%d" % self.id) self.showFacingStatus = False self.xMoveSpeed = xMoveSpeed self.xMoveDistance = xMoveDistance self.heightShift = heightShift self.bounceSpeed = bounceSpeed self.bounceHeight = bounceHeight self.rotateSpeed = rotateSpeed self.parentNode = parentNode self.bounceOffset = bounceOffset self.hitInterval = None self.kaboomTrack = None self.resetRollIval = None self.netTimeSentToStartByHit = 0 self.load() self.request("Down") def load(self): self.root = NodePath("PartyCog-%d" % self.id) self.root.reparentTo(self.parentNode) path = "phase_13/models/parties/cogPinata_" self.actor = Actor( path + "actor", { "idle": path + "idle_anim", "down": path + "down_anim", "up": path + "up_anim", "bodyHitBack": path + "bodyHitBack_anim", "bodyHitFront": path + "bodyHitFront_anim", "headHitBack": path + "headHitBack_anim", "headHitFront": path + "headHitFront_anim", }) self.actor.reparentTo(self.root) self.temp_transform = Mat4() self.head_locator = self.actor.attachNewNode("temphead") self.bodyColl = CollisionTube(0, 0, 1, 0, 0, 5.75, 0.75) self.bodyColl.setTangible(1) self.bodyCollNode = CollisionNode("PartyCog-%d-Body-Collision" % self.id) self.bodyCollNode.setCollideMask(ToontownGlobals.PieBitmask) self.bodyCollNode.addSolid(self.bodyColl) self.bodyCollNodePath = self.root.attachNewNode(self.bodyCollNode) self.headColl = CollisionTube(0, 0, 3, 0, 0, 3.0, 1.5) self.headColl.setTangible(1) self.headCollNode = CollisionNode("PartyCog-%d-Head-Collision" % self.id) self.headCollNode.setCollideMask(ToontownGlobals.PieBitmask) self.headCollNode.addSolid(self.headColl) self.headCollNodePath = self.root.attachNewNode(self.headCollNode) # Cog's Left Arm self.arm1Coll = CollisionSphere(1.65, 0, 3.95, 1.0) self.arm1Coll.setTangible(1) self.arm1CollNode = CollisionNode("PartyCog-%d-Arm1-Collision" % self.id) self.arm1CollNode.setCollideMask(ToontownGlobals.PieBitmask) self.arm1CollNode.addSolid(self.arm1Coll) self.arm1CollNodePath = self.root.attachNewNode(self.arm1CollNode) # Cog's Right Arm self.arm2Coll = CollisionSphere(-1.65, 0, 3.45, 1.0) self.arm2Coll.setTangible(1) self.arm2CollNode = CollisionNode("PartyCog-%d-Arm2-Collision" % self.id) self.arm2CollNode.setCollideMask(ToontownGlobals.PieBitmask) self.arm2CollNode.addSolid(self.arm2Coll) self.arm2CollNodePath = self.root.attachNewNode(self.arm2CollNode) splatName = 'splat-creampie' self.splat = globalPropPool.getProp(splatName) self.splat.setBillboardPointEye() self.splatType = globalPropPool.getPropType(splatName) self.pieHitSound = globalBattleSoundCache.getSound( 'AA_wholepie_only.mp3') self.upSound = globalBattleSoundCache.getSound('AV_jump_to_side.mp3') self.hole = loader.loadModel("phase_13/models/parties/cogPinataHole") self.hole.setTransparency(True) self.hole.setP(-90.0) self.hole.setScale(3) self.hole.setBin("ground", 3) self.hole.reparentTo(self.parentNode) def unload(self): self.request("Off") self.clearHitInterval() if self.hole is not None: self.hole.removeNode() self.hole = None if self.actor is not None: self.actor.cleanup() self.actor.removeNode() self.actor = None if self.root is not None: self.root.removeNode() self.root = None if self.kaboomTrack is not None and self.kaboomTrack.isPlaying(): self.kaboomTrack.finish() self.kaboomTrack = None if self.resetRollIval is not None and self.resetRollIval.isPlaying(): self.resetRollIval.finish() self.resetRollIval = None if self.hitInterval is not None and self.hitInterval.isPlaying(): self.hitInterval.finish() self.hitInterval = None del self.upSound del self.pieHitSound #=============================================================================== # FSM States #=============================================================================== def enterStatic(self): pass def exitStatic(self): pass def enterActive(self, startTime): self.root.setR(0.0) updateTask = Task.Task(self.updateTask) updateTask.startTime = startTime taskMgr.add(updateTask, "PartyCog.update-%d" % self.id) def exitActive(self): taskMgr.remove("PartyCog.update-%d" % self.id) taskMgr.remove("PartyCog.bounceTask-%d" % self.id) self.clearHitInterval() self.resetRollIval = self.root.hprInterval(0.5, Point3( self.root.getH(), 0.0, 0.0), blendType="easeInOut") self.resetRollIval.start() self.actor.stop() def enterDown(self): if self.oldState == "Off": self.actor.pose("down", self.actor.getNumFrames("down") - 1) return self.clearHitInterval() startScale = self.hole.getScale() endScale = Point3(5, 5, 5) self.hitInterval = Sequence( LerpFunc(self.setAlongSpline, duration=1.0, fromData=self.currentT, toData=0.0), LerpScaleInterval(self.hole, duration=0.175, scale=endScale, startScale=startScale, blendType="easeIn"), Parallel( SoundInterval(self.upSound, volume=0.6, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), ActorInterval(self.actor, "down", loop=0), ), LerpScaleInterval(self.hole, duration=0.175, scale=Point3(3, 3, 3), startScale=endScale, blendType="easeOut"), ) self.hitInterval.start() def exitDown(self): self.root.setR(0.0) self.root.setH(0.0) self.targetDistance = 0.0 self.targetFacing = 0.0 self.currentT = 0.0 self.setAlongSpline(0.0) self.clearHitInterval() startScale = self.hole.getScale() endScale = Point3(5, 5, 5) self.hitInterval = Sequence( LerpScaleInterval(self.hole, duration=0.175, scale=endScale, startScale=startScale, blendType="easeIn"), Parallel( SoundInterval(self.upSound, volume=0.6, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), ActorInterval(self.actor, "up", loop=0), ), Func(self.actor.loop, "idle"), LerpScaleInterval(self.hole, duration=0.175, scale=Point3(3, 3, 3), startScale=endScale, blendType="easeOut"), ) self.hitInterval.start() def filterDown(self, request, args): if request == "Down": return None else: return self.defaultFilter(request, args) #------------------------------------------------------------------------------ def setEndPoints(self, start, end, amplitude=1.7): self.sinAmplitude = amplitude self.sinPeriod = (end.getX() - start.getX()) / 2 self.sinDisplacement = start.getY() self.startPoint = start self.endPoint = end self.currentT = 0.0 self.targetDistance = 0.0 self.currentFacing = 0.0 self.targetFacing = 0.0 self.setAlongSpline(self.currentT) self.hole.setPos(self.root.getPos()) self.hole.setZ(0.02) def rockBackAndForth(self, task): t = task.startTime + task.time angle = math.sin(t) * 20.0 self.root.setR(angle) # if self.id == 0: # print angle return task.cont def updateDistance(self, distance): self.targetDistance = clamp(distance, -1.0, 1.0) def updateTask(self, task): self.rockBackAndForth(task) if self.targetDistance > self.currentT: self.currentT += min(0.01, self.targetDistance - self.currentT) self.setAlongSpline(self.currentT) elif self.targetDistance < self.currentT: self.currentT += max(-0.01, self.targetDistance - self.currentT) self.setAlongSpline(self.currentT) if self.currentT < 0.0: self.targetFacing = -90.0 elif self.currentT > 0.0: self.targetFacing = 90.0 else: self.targetFacing = 0.0 if self.targetFacing > self.currentFacing: self.currentFacing += min(10, self.targetFacing - self.currentFacing) elif self.targetFacing < self.currentFacing: self.currentFacing += max(-10, self.targetFacing - self.currentFacing) self.root.setH(self.currentFacing) return task.cont def setAlongSpline(self, t): t = t + 1.0 dist = (self.endPoint.getX() - self.startPoint.getX()) / 2.0 x = self.startPoint.getX() + t * dist y = self.startPoint.getY() - math.sin( t * 2 * math.pi) * self.sinAmplitude self.root.setPos(x, y, 0) def startBounce(self): taskMgr.add(self.bounce, "PartyCog.bounceTask-%d" % self.id) def bounce(self, task): #self.root.setH(self.root.getH() - self.rotateSpeed) self.root.setZ((math.sin((self.bounceOffset + task.time) * self.bounceSpeed) * self.bounceHeight) + self.heightShift) return task.cont def setPos(self, position): self.root.setPos(position) def respondToPieHit(self, timestamp, position, hot=False, direction=1.0): """The toon hit us, react appropriately.""" assert (self.notify.debugStateCall(self)) if self.netTimeSentToStartByHit < timestamp: self.__showSplat(position, direction, hot) if self.netTimeSentToStartByHit < timestamp: self.netTimeSentToStartByHit = timestamp else: #self.notify.debug('localStamp = %s, lastLocalTimeStampFromAI=%s, ignoring respondToPieHit' % (localStamp, self.lastLocalTimeStampFromAI)) self.notify.debug( 'respondToPieHit self.netTimeSentToStartByHit = %s' % self.netTimeSentToStartByHit) def clearHitInterval(self): if self.hitInterval is not None and self.hitInterval.isPlaying(): self.hitInterval.clearToInitial() def __showSplat(self, position, direction, hot=False): """Show the splat graphic and sound.""" if self.kaboomTrack is not None and self.kaboomTrack.isPlaying(): self.kaboomTrack.finish() self.clearHitInterval() splatName = 'splat-creampie' self.splat = globalPropPool.getProp(splatName) self.splat.setBillboardPointEye() self.splat.reparentTo(render) self.splat.setPos(self.root, position) self.splat.setAlphaScale(1.0) if not direction == 1.0: #self.splat.setColorScale(Vec4(0.0, 0.0, 50.0, 1.0)) self.splat.setColorScale(PartyGlobals.CogActivitySplatColors[0]) if self.currentFacing > 0.0: facing = "HitFront" else: facing = "HitBack" else: self.splat.setColorScale(PartyGlobals.CogActivitySplatColors[1]) #self.splat.setColorScale(Vec4(1.0, 0.6, 0.08, 1.0)) if self.currentFacing > 0.0: facing = "HitBack" else: facing = "HitFront" if hot: targetscale = 0.75 part = "head" else: targetscale = 0.5 part = "body" def setSplatAlpha(amount): self.splat.setAlphaScale(amount) self.hitInterval = Sequence( ActorInterval(self.actor, part + facing, loop=0), Func(self.actor.loop, "idle"), ) self.hitInterval.start() self.kaboomTrack = Parallel( SoundInterval(self.pieHitSound, volume=1.0, node=self.actor, cutOff=PartyGlobals.PARTY_COG_CUTOFF), Sequence( Func(self.splat.showThrough), Parallel( Sequence( LerpScaleInterval(self.splat, duration=0.175, scale=targetscale, startScale=Point3(0.1, 0.1, 0.1), blendType="easeOut"), Wait(0.175), ), Sequence( Wait(0.1), LerpFunc( setSplatAlpha, duration=1.0, #0.4, fromData=1.0, toData=0.0, blendType="easeOut"))), Func(self.splat.cleanup), Func(self.splat.removeNode), )) self.kaboomTrack.start() def showHitScore(self, number, scale=1): """ Shows the hit score. Borrowed from otp.avatar.DistributedAvatar.showHpText """ if number <= 0: return # Get rid of the number if it is already there. if self.hpText: self.hideHitScore() # Set the font self.HpTextGenerator.setFont(ToontownGlobals.getSignFont()) # Show both negative and positive signs if number < 0: self.HpTextGenerator.setText(str(number)) else: self.HpTextGenerator.setText("+" + str(number)) # No shadow self.HpTextGenerator.clearShadow() # Center the number self.HpTextGenerator.setAlign(TextNode.ACenter) # Red, always #if number < 0: r = 1 #0.9 g = 1 #0 b = 0 a = 1 self.HpTextGenerator.setTextColor(r, g, b, a) self.hpTextNode = self.HpTextGenerator.generate() # Put the hpText over the head of the avatar self.hpText = render.attachNewNode(self.hpTextNode) self.hpText.setScale(scale) # Make sure it is a billboard self.hpText.setBillboardPointEye() # Render it after other things in the scene. self.hpText.setBin('fixed', 100) # Initial position ... Center of the body... the "tan tien" self.hpText.setPos(self.root, 0, 0, self.height / 2) # Black magic from the early days of Panda3D, later replaced by a Sequence seq = Task.sequence( # Fly the number out of the character self.hpText.lerpPos(Point3( self.root.getX(render), self.root.getY(render), self.root.getZ(render) + self.height + 1.0), 0.25, blendType='easeOut'), Task.pause(0.25), # Fade the number self.hpText.lerpColor(Vec4(r, g, b, a), Vec4(r, g, b, 0), 0.1), # Get rid of the number Task.Task(self.__hideHitScoreTask)) taskMgr.add(seq, "PartyCogHpText" + str(self.id)) def __hideHitScoreTask(self, task): self.hideHitScore() return Task.done def hideHitScore(self): if self.hpText: taskMgr.remove("PartyCogHpText" + str(self.id)) self.hpText.removeNode() self.hpText = None def getHeadLocation(self): (self.actor.getJoints(jointName="head")[0]).getNetTransform( self.temp_transform) self.head_locator.setMat(self.temp_transform) #print self.head_locator.getZ() return self.head_locator.getZ(self.root)
class DistributedFireworksCannon(DistributedFireworkShow.DistributedFireworkShow): notify = directNotify.newCategory("DistributedFireworksCannon") def __init__(self, cr): DistributedFireworkShow.DistributedFireworkShow.__init__(self,cr) self.fireworksGui = None self.load() def generateInit(self): DistributedFireworkShow.DistributedFireworkShow.generateInit(self) """generateInit(self) This method is called when the DistributedObject is first introduced to the world... Not when it is pulled from the cache. """ self.fireworksSphereEvent = self.uniqueName("fireworksSphere") self.fireworksSphereEnterEvent = "enter" + self.fireworksSphereEvent self.fireworksGuiDoneEvent = "fireworksGuiDone" self.shootEvent = "fireworkShootEvent" self.collSphere = CollisionSphere(0, 0, 0, 2.5) # Make the sphere intangible self.collSphere.setTangible(1) self.collNode = CollisionNode(self.fireworksSphereEvent) self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) self.collNode.addSolid(self.collSphere) self.collNodePath = self.geom.attachNewNode(self.collNode) def generate(self): """generate(self) This method is called when the DistributedObject is reintroduced to the world, either for the first time or from the cache. """ DistributedFireworkShow.DistributedFireworkShow.generate(self) def announceGenerate(self): self.notify.debug("announceGenerate") self.accept(self.fireworksSphereEnterEvent, self.__handleEnterSphere) def disable(self): self.notify.debug("disable") self.ignore(self.fireworksSphereEnterEvent) self.ignore(self.shootEvent) self.ignore(self.fireworksGuiDoneEvent) if self.fireworksGui: self.fireworksGui.destroy() self.fireworksGui = None DistributedFireworkShow.DistributedFireworkShow.disable(self) def delete(self): self.notify.debug("delete") self.geom.removeNode() DistributedFireworkShow.DistributedFireworkShow.delete(self) def load(self): self.geom = loader.loadModel("phase_5/models/props/trashcan_TT.bam") self.geom.reparentTo(base.cr.playGame.hood.loader.geom) self.geom.setScale(.5) def __handleEnterSphere(self, collEntry): self.notify.debug("handleEnterSphere()") # don't let other toons access the fireworks now self.ignore(self.fireworksSphereEnterEvent) self.sendUpdate("avatarEnter", []) def __handleFireworksDone(self): self.ignore(self.fireworksGuiDoneEvent) self.ignore(self.shootEvent) self.sendUpdate("avatarExit") self.fireworksGui.destroy() self.fireworksGui = None def freeAvatar(self): """ This is a message from the AI used to free the avatar from movie mode """ # If we are the local toon and we have simply taken too long, # just free us base.localAvatar.posCamera(0,0) base.cr.playGame.getPlace().setState("walk") # Start accepting the fireworks collision sphere event again self.accept(self.fireworksSphereEnterEvent, self.__handleEnterSphere) def setMovie(self, mode, avId, timestamp): """ This is a message from the AI describing a movie between this fireworks cannon and a Toon that has approached us. """ timeStamp = globalClockDelta.localElapsedTime(timestamp) # See if this is the local toon isLocalToon = (avId == base.localAvatar.doId) if (mode == FIREWORKS_MOVIE_CLEAR): self.notify.debug("setMovie: clear") return elif (mode == FIREWORKS_MOVIE_GUI): self.notify.debug("setMovie: gui") if isLocalToon: self.fireworksGui = FireworksGui.FireworksGui(self.fireworksGuiDoneEvent, self.shootEvent) self.accept(self.fireworksGuiDoneEvent, self.__handleFireworksDone) self.accept(self.shootEvent, self.localShootFirework) return else: self.notify.warning("unknown mode in setMovie: %s" % (mode)) def setPosition(self, x, y, z): self.pos = [x,y,z] self.geom.setPos(x,y,z) def localShootFirework(self, index): style = index col1,col2 = self.fireworksGui.getCurColor() amp = 30 # attach a dummy node to the toon that always in front of him. # This is where the fireworks will shoot from. dummy = base.localAvatar.attachNewNode("dummy") dummy.setPos(0,100,60) pos = dummy.getPos(render) dummy.removeNode() print(("lauFirework: %s, col=%s" % (index,col1))) self.d_requestFirework(pos[0],pos[1],pos[2],style,col1,col2)
class CogdoGameGatherable(NodePath, DirectObject): EnterEventName = 'CogdoGameGatherable_Enter' def __init__(self, serialNum, model, triggerRadius, triggerOffset=(0, 0, 0), animate=True, animDuration=0.2, instanceModel=True, name='CogdoGameGatherable'): NodePath.__init__(self, '%s-%d' % (name, serialNum)) self.serialNum = serialNum self._animate = animate if instanceModel: model.instanceTo(self) self._model = self else: self._model = model self._model.reparentTo(self) self._model.setPosHpr(0, 0, 0, 0, 0, 0) self._animDuration = animDuration self._animSeq = None self._initCollisions(triggerRadius, triggerOffset) self._update = None self._wasPickedUp = False return def _initCollisions(self, triggerRadius, triggerOffset): self.collSphere = CollisionSphere(triggerOffset[0], triggerOffset[1], triggerOffset[2], triggerRadius) self.collSphere.setTangible(0) self.collNode = CollisionNode(self.getName()) self.collNode.addSolid(self.collSphere) self.collNodePath = self.attachNewNode(self.collNode) def destroy(self): self.disable() del self._model if self._animSeq is not None: self._animSeq.finish() self._animSeq = None self.collNodePath.removeNode() self.removeNode() return def enable(self): self.accept('enter' + self.getName(), self._handleEnterCollision) self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) def disable(self): self.ignoreAll() self.collNode.setIntoCollideMask(BitMask32(0)) def show(self): if not self.wasPickedUp(): NodePath.show(self) self.enable() def hide(self): self.disable() NodePath.hide(self) def _handleEnterCollision(self, collEntry): messenger.send(CogdoGameGatherable.EnterEventName, [self]) def wasPickedUp(self): return self._wasPickedUp def wasPickedUpByToon(self): pass def update(self, dt): pass def getModel(self): return self._model def pickUp(self, toon, elapsedSeconds=0.0): self._wasPickedUp = True if self._animSeq is not None: self._animSeq.finish() self._animSeq = None if self._animate: def lerpFlyToToon(t): vec = toon.getPos(render) - self.getPos(render) vec[2] += toon.getHeight() self.setPos(self.getPos() + vec * t) self.setScale(1.0 - t * 0.8) self._animSeq = Sequence( LerpFunc(lerpFlyToToon, fromData=0.0, toData=1.0, duration=self._animDuration), Wait(0.1), Func(self.hide)) self._animSeq.start(elapsedSeconds) else: self.hide() return
class CogdoFlyingLegalEagle(FSM, DirectObject): CollSphereName = 'CogdoFlyingLegalEagleSphere' CollisionEventName = 'CogdoFlyingLegalEagleCollision' InterestCollName = 'CogdoFlyingLegalEagleInterestCollision' RequestAddTargetEventName = 'CogdoFlyingLegalEagleRequestTargetEvent' RequestAddTargetAgainEventName = 'CogdoFlyingLegalEagleRequestTargetAgainEvent' RequestRemoveTargetEventName = 'CogdoFlyingLegalEagleRemoveTargetEvent' ForceRemoveTargetEventName = 'CogdoFlyingLegalEagleForceRemoveTargetEvent' EnterLegalEagle = 'CogdoFlyingLegalEagleDamageToon' ChargingToAttackEventName = 'LegalEagleChargingToAttack' LockOnToonEventName = 'LegalEagleLockOnToon' CooldownEventName = 'LegalEagleCooldown' notify = DirectNotifyGlobal.directNotify.newCategory('CogdoFlyingLegalEagle') def __init__(self, nest, index, suitDnaName = 'le'): FSM.__init__(self, 'CogdoFlyingLegalEagle') self.defaultTransitions = {'Off' : ['Roost'], 'Roost' : ['TakeOff', 'Off'], 'TakeOff' : ['LockOnToon', 'LandOnNest', 'Off'], 'LockOnToon' : ['RetreatToNest', 'ChargeUpAttack', 'Off'], 'ChargeUpAttack' : ['RetreatToNest', 'Attack', 'Off'], 'Attack' : ['RetreatToSky', 'Off'], 'RetreatToSky' : ['Cooldown', 'Off'], 'Cooldown' : ['LockOnToon', 'LandOnNest', 'Off'], 'RetreatToNest' : ['LandOnNest', 'Off'], 'LandOnNest' : ['Roost', 'Off'], } self.index = index self.nest = nest self.target = None self.isEagleInterested = False self.collSphere = None self.suit = Suit.Suit() d = SuitDNA.SuitDNA() d.newSuit(suitDnaName) self.suit.setDNA(d) self.suit.reparentTo(render) swapAvatarShadowPlacer(self.suit, 'legalEagle-%sShadowPlacer' % index) self.suit.setPos(self.nest.getPos(render)) self.suit.setHpr(-180, 0, 0) self.suit.stash() self.prop = None self.attachPropeller() head = self.suit.find('**/joint_head') self.interestConeOrigin = self.nest.attachNewNode('fakeHeadNodePath') self.interestConeOrigin.setPos(render, head.getPos(render) + Vec3(0, Globals.LegalEagle.InterestConeOffset, 0)) self.attackTargetPos = None self.startOfRetreatToSkyPos = None pathModel = CogdoUtil.loadFlyingModel('legalEaglePaths') self.chargeUpMotionPath = Mopath.Mopath(name='chargeUpMotionPath-%i' % self.index) self.chargeUpMotionPath.loadNodePath(pathModel.find('**/charge_path')) self.retreatToSkyMotionPath = Mopath.Mopath(name='retreatToSkyMotionPath-%i' % self.index) self.retreatToSkyMotionPath.loadNodePath(pathModel.find('**/retreat_path')) audioMgr = base.cogdoGameAudioMgr self._screamSfx = audioMgr.createSfx('legalEagleScream', self.suit) self.initIntervals() return def attachPropeller(self): if self.prop == None: self.prop = BattleProps.globalPropPool.getProp('propeller') head = self.suit.find('**/joint_head') self.prop.reparentTo(head) return def detachPropeller(self): if self.prop: self.prop.cleanup() self.prop.removeNode() self.prop = None return def _getAnimationIval(self, animName, startFrame = 0, endFrame = None, duration = 1): if endFrame == None: self.suit.getNumFrames(animName) - 1 frames = endFrame - startFrame frameRate = self.suit.getFrameRate(animName) newRate = frames / duration playRate = newRate / frameRate ival = Sequence(ActorInterval(self.suit, animName, playRate=playRate)) return ival def initIntervals(self): dur = Globals.LegalEagle.LiftOffTime nestPos = self.nest.getPos(render) airPos = nestPos + Vec3(0.0, 0.0, Globals.LegalEagle.LiftOffHeight) self.takeOffSeq = Sequence(Parallel(Sequence(Wait(dur * 0.6), LerpPosInterval(self.suit, dur * 0.4, startPos=nestPos, pos=airPos, blendType='easeInOut'))), Wait(1.5), Func(self.request, 'next'), name='%s.takeOffSeq-%i' % (self.__class__.__name__, self.index)) self.landOnNestPosLerp = LerpPosInterval(self.suit, 1.0, startPos=airPos, pos=nestPos, blendType='easeInOut') self.landingSeq = Sequence(Func(self.updateLandOnNestPosLerp), Parallel(self.landOnNestPosLerp), Func(self.request, 'next'), name='%s.landingSeq-%i' % (self.__class__.__name__, self.index)) dur = Globals.LegalEagle.ChargeUpTime self.chargeUpPosLerp = LerpFunc(self.moveAlongChargeUpMopathFunc, fromData=0.0, toData=self.chargeUpMotionPath.getMaxT(), duration=dur, blendType='easeInOut') self.chargeUpAttackSeq = Sequence(Func(self.updateChargeUpPosLerp), self.chargeUpPosLerp, Func(self.request, 'next'), name='%s.chargeUpAttackSeq-%i' % (self.__class__.__name__, self.index)) dur = Globals.LegalEagle.RetreatToNestTime self.retreatToNestPosLerp = LerpPosInterval(self.suit, dur, startPos=Vec3(0, 0, 0), pos=airPos, blendType='easeInOut') self.retreatToNestSeq = Sequence(Func(self.updateRetreatToNestPosLerp), self.retreatToNestPosLerp, Func(self.request, 'next'), name='%s.retreatToNestSeq-%i' % (self.__class__.__name__, self.index)) dur = Globals.LegalEagle.RetreatToSkyTime self.retreatToSkyPosLerp = LerpFunc(self.moveAlongRetreatMopathFunc, fromData=0.0, toData=self.retreatToSkyMotionPath.getMaxT(), duration=dur, blendType='easeOut') self.retreatToSkySeq = Sequence(Func(self.updateRetreatToSkyPosLerp), self.retreatToSkyPosLerp, Func(self.request, 'next'), name='%s.retreatToSkySeq-%i' % (self.__class__.__name__, self.index)) dur = Globals.LegalEagle.PreAttackTime self.preAttackLerpXY = LerpFunc(self.updateAttackXY, fromData=0.0, toData=1.0, duration=dur) self.preAttackLerpZ = LerpFunc(self.updateAttackZ, fromData=0.0, toData=1.0, duration=dur, blendType='easeOut') dur = Globals.LegalEagle.PostAttackTime self.postAttackPosLerp = LerpPosInterval(self.suit, dur, startPos=Vec3(0, 0, 0), pos=Vec3(0, 0, 0)) self.attackSeq = Sequence(Parallel(self.preAttackLerpXY, self.preAttackLerpZ), Func(self.updatePostAttackPosLerp), self.postAttackPosLerp, Func(self.request, 'next'), name='%s.attackSeq-%i' % (self.__class__.__name__, self.index)) dur = Globals.LegalEagle.CooldownTime self.cooldownSeq = Sequence(Wait(dur), Func(self.request, 'next'), name='%s.cooldownSeq-%i' % (self.__class__.__name__, self.index)) self.propTrack = Sequence(ActorInterval(self.prop, 'propeller', startFrame=0, endFrame=14)) self.hoverOverNestSeq = Sequence(ActorInterval(self.suit, 'landing', startFrame=10, endFrame=20, playRate=0.5), ActorInterval(self.suit, 'landing', startFrame=20, endFrame=10, playRate=0.5)) def initCollision(self): self.collSphere = CollisionSphere(0, 0, 0, 0) self.collSphere.setTangible(0) self.collNode = CollisionNode('%s-%s' % (self.CollSphereName, self.index)) self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) self.collNode.addSolid(self.collSphere) self.collNodePath = self.suit.attachNewNode(self.collNode) self.collNodePath.hide() self.accept('enter%s-%s' % (self.CollSphereName, self.index), self.handleEnterSphere) self.setCollSphereToNest() def getInterestConeLength(self): return Globals.LegalEagle.InterestConeLength + Globals.LegalEagle.InterestConeOffset def isToonInView(self, toon): distanceThreshold = self.getInterestConeLength() angleThreshold = Globals.LegalEagle.InterestConeAngle toonPos = toon.getPos(render) nestPos = self.nest.getPos(render) distance = toon.getDistance(self.interestConeOrigin) if distance > distanceThreshold: return False if toonPos[1] > nestPos[1]: return False a = toon.getPos(render) - self.interestConeOrigin.getPos(render) a.normalize() b = Vec3(0, -1, 0) dotProduct = a.dot(b) angle = math.degrees(math.acos(dotProduct)) if angle <= angleThreshold / 2.0: return True else: return False def update(self, dt, localPlayer): if Globals.Dev.NoLegalEagleAttacks: return inView = self.isToonInView(localPlayer.toon) if inView and not self.isEagleInterested: self.handleEnterInterest() elif inView and self.isEagleInterested: self.handleAgainInterest() elif not inView and self.isEagleInterested: self.handleExitInterest() def updateLockOnTask(self): dt = globalClock.getDt() targetPos = self.target.getPos(render) suitPos = self.suit.getPos(render) nestPos = self.nest.getPos(render) attackPos = Vec3(targetPos) attackPos[1] = nestPos[1] + Globals.LegalEagle.LockOnDistanceFromNest attackPos[2] += Globals.LegalEagle.VerticalOffset if attackPos[2] < nestPos[2]: attackPos[2] = nestPos[2] attackChangeVec = (attackPos - suitPos) * Globals.LegalEagle.LockOnSpeed self.suit.setPos(suitPos + attackChangeVec * dt) return Task.cont def updateAttackXY(self, value): if Globals.LegalEagle.EagleAttackShouldXCorrect: x = self.readyToAttackPos.getX() + (self.attackTargetPos.getX() - self.readyToAttackPos.getX()) * value self.suit.setX(x) y = self.readyToAttackPos.getY() + (self.attackTargetPos.getY() - self.readyToAttackPos.getY()) * value self.suit.setY(y) def updateAttackZ(self, value): z = self.readyToAttackPos.getZ() + (self.attackTargetPos.getZ() - self.readyToAttackPos.getZ()) * value self.suit.setZ(z) def moveAlongChargeUpMopathFunc(self, value): self.chargeUpMotionPath.goTo(self.suit, value) self.suit.setPos(self.suit.getPos() + self.startOfChargeUpPos) def moveAlongRetreatMopathFunc(self, value): self.retreatToSkyMotionPath.goTo(self.suit, value) self.suit.setPos(self.suit.getPos() + self.startOfRetreatToSkyPos) def updateChargeUpPosLerp(self): self.startOfChargeUpPos = self.suit.getPos(render) def updateLandOnNestPosLerp(self): self.landOnNestPosLerp.setStartPos(self.suit.getPos()) def updateRetreatToNestPosLerp(self): self.retreatToNestPosLerp.setStartPos(self.suit.getPos()) def updateRetreatToSkyPosLerp(self): self.startOfRetreatToSkyPos = self.suit.getPos(render) def updatePostAttackPosLerp(self): suitPos = self.suit.getPos(render) finalPos = suitPos + Vec3(0, -Globals.LegalEagle.PostAttackLength, 0) self.postAttackPosLerp.setStartPos(suitPos) self.postAttackPosLerp.setEndPos(finalPos) def handleEnterSphere(self, collEntry): self.notify.debug('handleEnterSphere:%i' % self.index) messenger.send(CogdoFlyingLegalEagle.EnterLegalEagle, [self, collEntry]) def handleEnterInterest(self): self.notify.debug('handleEnterInterestColl:%i' % self.index) self.isEagleInterested = True messenger.send(CogdoFlyingLegalEagle.RequestAddTargetEventName, [self.index]) def handleAgainInterest(self): self.isEagleInterested = True messenger.send(CogdoFlyingLegalEagle.RequestAddTargetAgainEventName, [self.index]) def handleExitInterest(self): self.notify.debug('handleExitInterestSphere:%i' % self.index) self.isEagleInterested = False messenger.send(CogdoFlyingLegalEagle.RequestRemoveTargetEventName, [self.index]) def hasTarget(self): if self.target != None: return True else: return False return def setTarget(self, toon, elapsedTime = 0.0): self.notify.debug('Setting eagle %i to target: %s, elapsed time: %s' % (self.index, toon.getName(), elapsedTime)) self.target = toon if self.state == 'Roost': self.request('next', elapsedTime) if self.state == 'ChargeUpAttack': messenger.send(CogdoFlyingLegalEagle.ChargingToAttackEventName, [self.target.doId]) def clearTarget(self, elapsedTime = 0.0): self.notify.debug('Clearing target from eagle %i, elapsed time: %s' % (self.index, elapsedTime)) messenger.send(CogdoFlyingLegalEagle.CooldownEventName, [self.target.doId]) self.target = None if self.state in ['LockOnToon']: self.request('next', elapsedTime) return def leaveCooldown(self, elapsedTime = 0.0): if self.state in ['Cooldown']: self.request('next', elapsedTime) def shouldBeInFrame(self): if self.state in ['TakeOff', 'LockOnToon', 'ChargeUpAttack']: return True elif self.state == 'Attack': distance = self.suit.getDistance(self.target) threshold = Globals.LegalEagle.EagleAndTargetDistCameraTrackThreshold suitPos = self.suit.getPos(render) targetPos = self.target.getPos(render) if distance > threshold and suitPos[1] > targetPos[1]: return True return False def getTarget(self): return self.target def onstage(self): self.suit.unstash() self.request('Roost') def offstage(self): self.suit.stash() self.request('Off') def gameStart(self, gameStartTime): self.gameStartTime = gameStartTime self.initCollision() def gameEnd(self): self.shutdownCollisions() def shutdownCollisions(self): self.ignoreAll() if self.collSphere != None: del self.collSphere self.collSphere = None if self.collNodePath != None: self.collNodePath.removeNode() del self.collNodePath self.collNodePath = None if self.collNode != None: del self.collNode self.collNode = None return def destroy(self): self.request('Off') self.detachPropeller() del self._screamSfx self.suit.cleanup() self.suit.removeNode() self.suit.delete() self.interestConeOrigin.removeNode() del self.interestConeOrigin self.nest = None self.target = None taskMgr.remove('updateLockOnTask-%i' % self.index) taskMgr.remove('exitLockOnToon-%i' % self.index) self.propTrack.clearToInitial() del self.propTrack del self.chargeUpMotionPath del self.retreatToSkyMotionPath self.takeOffSeq.clearToInitial() del self.takeOffSeq del self.landOnNestPosLerp self.landingSeq.clearToInitial() del self.landingSeq del self.chargeUpPosLerp self.chargeUpAttackSeq.clearToInitial() del self.chargeUpAttackSeq del self.retreatToNestPosLerp self.retreatToNestSeq.clearToInitial() del self.retreatToNestSeq del self.retreatToSkyPosLerp self.retreatToSkySeq.clearToInitial() del self.retreatToSkySeq del self.postAttackPosLerp self.attackSeq.clearToInitial() del self.attackSeq self.cooldownSeq.clearToInitial() del self.cooldownSeq self.hoverOverNestSeq.clearToInitial() del self.hoverOverNestSeq del self.preAttackLerpXY del self.preAttackLerpZ return def requestNext(self): self.request('next') def setCollSphereToNest(self): if hasattr(self, 'collSphere') and self.collSphere is not None: radius = Globals.LegalEagle.OnNestDamageSphereRadius self.collSphere.setCenter(Point3(0.0, -Globals.Level.LaffPowerupNestOffset[1], self.suit.getHeight() / 2.0)) self.collSphere.setRadius(radius) return def setCollSphereToTargeting(self): if hasattr(self, 'collSphere') and self.collSphere is not None: radius = Globals.LegalEagle.DamageSphereRadius self.collSphere.setCenter(Point3(0, 0, radius * 2)) self.collSphere.setRadius(radius) return def enterRoost(self): self.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) self.hoverOverNestSeq.loop() self.propTrack.loop() self.setCollSphereToNest() def filterRoost(self, request, args): self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) if request == self.state: return None elif request == 'next': return 'TakeOff' else: return self.defaultFilter(request, args) return None def exitRoost(self): self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) self.hoverOverNestSeq.pause() self.setCollSphereToTargeting() def enterTakeOff(self, elapsedTime = 0.0): self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState, self.oldState, self.newState, elapsedTime)) self.takeOffSeq.start(elapsedTime) self.hoverOverNestSeq.loop() def filterTakeOff(self, request, args): self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) if request == self.state: return None elif request == 'next': if self.hasTarget(): return 'LockOnToon' else: return 'LandOnNest' else: return self.defaultFilter(request, args) return None def exitTakeOff(self): self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) self.takeOffSeq.clearToInitial() self.hoverOverNestSeq.pause() def enterLockOnToon(self, elapsedTime = 0.0): self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState, self.oldState, self.newState, elapsedTime)) taskName = 'updateLockOnTask-%i' % self.index taskMgr.add(self.updateLockOnTask, taskName, 45, extraArgs=[]) messenger.send(CogdoFlyingLegalEagle.LockOnToonEventName, [self.target.doId]) range = self.target.getDistance(self.interestConeOrigin) / self.getInterestConeLength() range = clamp(range, 0.0, 1.0) dur = Globals.LegalEagle.LockOnTime if self.oldState == 'TakeOff': dur *= range else: dur += Globals.LegalEagle.ExtraPostCooldownTime taskName = 'exitLockOnToon-%i' % self.index taskMgr.doMethodLater(dur, self.requestNext, taskName, extraArgs=[]) def filterLockOnToon(self, request, args): self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) if request == self.state: return None elif request == 'next': if self.hasTarget(): return 'ChargeUpAttack' else: return 'RetreatToNest' else: return self.defaultFilter(request, args) return None def exitLockOnToon(self): self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) taskMgr.remove('updateLockOnTask-%i' % self.index) taskMgr.remove('exitLockOnToon-%i' % self.index) def enterChargeUpAttack(self, elapsedTime = 0.0): self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState, self.oldState, self.newState, elapsedTime)) self.chargeUpAttackSeq.start(elapsedTime) messenger.send(CogdoFlyingLegalEagle.ChargingToAttackEventName, [self.target.doId]) def filterChargeUpAttack(self, request, args): self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) if request == self.state: return None elif request == 'next': if self.hasTarget(): return 'Attack' else: return 'RetreatToNest' else: return self.defaultFilter(request, args) return None def exitChargeUpAttack(self): self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) self.chargeUpAttackSeq.clearToInitial() def enterAttack(self, elapsedTime = 0.0): self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState, self.oldState, self.newState, elapsedTime)) self.attackTargetPos = self.target.getPos(render) targetState = self.target.animFSM.getCurrentState().getName() self._screamSfx.play() if targetState == 'jumpAirborne': self.attackTargetPos[2] += Globals.LegalEagle.VerticalOffset else: self.attackTargetPos[2] += Globals.LegalEagle.PlatformVerticalOffset self.readyToAttackPos = self.suit.getPos(render) self.attackSeq.start(elapsedTime) def filterAttack(self, request, args): self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) if request == self.state: return None elif request == 'next': return 'RetreatToSky' else: return self.defaultFilter(request, args) return None def exitAttack(self): self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) self.attackSeq.clearToInitial() taskMgr.remove('updateAttackPosTask-%i' % self.index) def enterRetreatToSky(self, elapsedTime = 0.0): self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState, self.oldState, self.newState, elapsedTime)) self.retreatToSkySeq.start(elapsedTime) def filterRetreatToSky(self, request, args): self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) if request == self.state: return None elif request == 'next': return 'Cooldown' else: return self.defaultFilter(request, args) return None def exitRetreatToSky(self): self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) self.retreatToSkySeq.clearToInitial() def enterCooldown(self): if self.target != None: messenger.send(CogdoFlyingLegalEagle.CooldownEventName, [self.target.doId]) self.suit.stash() self.notify.info("enter%s: '%s' -> '%s'" % (self.newState, self.oldState, self.newState)) return def filterCooldown(self, request, args): self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) if request == self.state: return None elif request == 'next': if self.hasTarget(): return 'LockOnToon' else: return 'LandOnNest' else: return self.defaultFilter(request, args) return None def exitCooldown(self): self.notify.debug("exit%s: '%s' -> '%s'" % (self.oldState, self.oldState, self.newState)) self.suit.unstash() self.cooldownSeq.clearToInitial() if self.newState != 'Off': heightOffNest = Globals.LegalEagle.PostCooldownHeightOffNest nestPos = self.nest.getPos(render) if self.newState in ['LandOnNest']: self.suit.setPos(nestPos + Vec3(0, 0, heightOffNest)) else: targetPos = self.target.getPos(render) attackPos = Vec3(targetPos) attackPos[1] = nestPos[1] attackPos[2] = nestPos[2] + heightOffNest self.suit.setPos(attackPos) def enterRetreatToNest(self, elapsedTime = 0.0): self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState, self.oldState, self.newState, elapsedTime)) self.retreatToNestSeq.start(elapsedTime) def filterRetreatToNest(self, request, args): self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) if request == self.state: return None elif request == 'next': return 'LandOnNest' else: return self.defaultFilter(request, args) return None def exitRetreatToNest(self): self.retreatToNestSeq.clearToInitial() def enterLandOnNest(self, elapsedTime = 0.0): self.notify.info("enter%s: '%s' -> '%s', elapsedTime:%s" % (self.newState, self.oldState, self.newState, elapsedTime)) self.landingSeq.start(elapsedTime) def filterLandOnNest(self, request, args): self.notify.debug("filter%s( '%s', '%s' )" % (self.state, request, args)) if request == self.state: return None elif request == 'next': if self.hasTarget(): return 'TakeOff' else: return 'Roost' else: return self.defaultFilter(request, args) return None def exitLandOnNest(self): self.landingSeq.clearToInitial()