def delete(self): try: self.Avatar_deleted except: self.Avatar_deleted = 1 self.removeLoopTask() Actor.delete(self)
class HealthGoblet(): healthGivenFactor = 8 minHealthGiven = 5 def __init__(self, mainRef, enemyRef): print 'HealthGoblet instantiated' self._enemyRef = enemyRef self._playerRef = mainRef.player self.healthGobletNode = mainRef.mainNode.attachNewNode( 'healthGobletNode') self.initAttributes() self.initModel() taskMgr.doMethodLater(1.5, self.updateHealthGoblet, 'updateHealthGobletTask') def initAttributes(self): self.healthGiven = utils.getDXY( self.minHealthGiven, self._enemyRef.level * self.healthGivenFactor) self.perceptionRange = 0.5 self.healthGobletNode.setPos(self._enemyRef.enemyNode.getPos()) def initModel(self): self.healthGobletModel = Actor('models/HealthGoblet', {'float': 'models/HealthGoblet-anim'}) self.healthGobletModel.reparentTo(self.healthGobletNode) self.healthGobletModel.setCollideMask(BitMask32.allOff()) self.healthGobletModel.setScale(0.5) self.healthGobletModel.loop('float') def updateHealthGoblet(self, task): playerNode = self._playerRef.playerNode if utils.getIsInRange(playerNode.getPos(), self.healthGobletNode.getPos(), self.perceptionRange): print 'healed player for:', self.healthGiven self._playerRef.heal(self.healthGiven) self.healthGobletModel.stop() self.suicide() return task.done return task.again def suicide(self): # Cleanup the healthGobletModel self.healthGobletModel.cleanup() self.healthGobletModel.delete() self.healthGobletNode.removeNode() self._enemyRef = None self._playerRef = None
def delete(self): try: self.DistributedActor_deleted except: self.DistributedActor_deleted = 1 DistributedSmoothNode.delete(self) Actor.delete(self)
def delete(self): try: self.Avatar_deleted except: self.Avatar_deleted = 1 self.removeLoopTask() Actor.delete(self)
class FieldActor(FSM): def __init__(self, name, indexNumber, modelName, stage, actorsList, hero): self.indexNumber = indexNumber self.modelName = modelName self.stage = stage self.name = name self.actorsList = actorsList self.hero = hero FSM.__init__( self, "FSM-FieldActor-{}_{}".format(self.name, self.indexNumber)) self.initActor() def initActor(self): faEmpty = self.stage.find("**/{}_{}".format(self.name, self.indexNumber)) faPos = faEmpty.getPos() self.actor = Actor( "{}".format(self.modelName), { "idle": "{}-idle".format(self.modelName), "walk": "{}-walk".format(self.modelName) }) self.actor.reparentTo(self.stage) self.actor.setPos(faPos) cNode = CollisionNode("{}_{}".format(self.name, self.indexNumber)) cNode.addSolid(CollisionBox(0, 1.5, 1.5, 1)) faCollision = self.actor.attachNewNode(cNode) ##################################################### # faCollision.show() ##################################################### base.pusher.addCollider(faCollision, self.actor, base.drive.node()) base.cTrav.addCollider(faCollision, base.pusher) self.actorsList.append(self) AIchar = AICharacter("{}_{}".format(self.name, self.indexNumber), self.actor, 100, 0.05, 5) base.AIworld.addAiChar(AIchar) self.AIbehaviors = AIchar.getAiBehaviors() # FSM ################################################################## def enterIdle(self): self.actor.loop('idle') def enterWander(self): self.actor.loop('walk') self.AIbehaviors.wander(5, 0, 10, 1.0) def enterPursue(self): self.actor.loop('walk') self.AIbehaviors.pursue(self.hero) def enterHide(self): self.actor.hide() def enterShow(self): self.actor.show() def enterDeleteActor(self): self.actor.delete()
def delete(self): try: self.Avatar_deleted except: Actor.cleanup(self) self.Avatar_deleted = 1 del self.__font del self.style Actor.delete(self)
class MachineGun: def __init__(self, cycle, mount): self.cycle = cycle self.actor = Actor("../Models/MGActor.egg") self.model = loader.loadModel("../Models/MachineGun.bam") self.actor.reparentTo(mount) self.model.reparentTo(self.actor) self.flashModel = loader.loadModel("../Models/LaserFlash.bam") self.projModel = loader.loadModel("../Models/LaserProj.bam") self.projModel.setScale(.25, 1, .25) self.refNP = self.cycle.trgtrMount.attachNewNode("MGRefNP") self.muzzle = self.actor.exposeJoint(None, "modelRoot", "Muzzle") reloadTime = .25 self.flashLerp = LerpScaleInterval(self.flashModel, reloadTime * .75, Point3(1, 1, 1), Point3(.1, .1, .1)) self.firePar = Parallel(Func(self.setEffects), self.flashLerp) self.fireSeq = Sequence(self.firePar, Func(self.clearEffects), Wait(reloadTime * .25)) def fire(self): if (self.fireSeq.isPlaying() == False): self.refNP.setPos(0, 15, 0) self.fireSeq.start() return def setEffects(self): self.flashModel.reparentTo(self.muzzle) self.projModel.reparentTo(self.muzzle) self.projModel.lookAt(self.refNP.getPos(self.muzzle)) self.projModel.setSy( trueDist(Point3(0, 0, 0), self.refNP.getPos(self.muzzle)) * 2) return def clearEffects(self): self.flashModel.detachNode() self.projModel.detachNode() return def destroy(self): self.actor.delete() self.model.removeNode() self.flashModel.removeNode() self.projModel.removeNode() self.refNP.removeNode() self.cycle = None self.flashLerp = None self.firePar = None self.fireSeq = None return
class HealthGoblet(): healthGivenFactor = 8 minHealthGiven = 5 def __init__(self, mainRef, enemyRef): print 'HealthGoblet instantiated' self._enemyRef = enemyRef self._playerRef = mainRef.player self.healthGobletNode = mainRef.mainNode.attachNewNode('healthGobletNode') self.initAttributes() self.initModel() taskMgr.doMethodLater(1.5, self.updateHealthGoblet, 'updateHealthGobletTask') def initAttributes(self): self.healthGiven = utils.getDXY(self.minHealthGiven, self._enemyRef.level * self.healthGivenFactor) self.perceptionRange = 0.5 self.healthGobletNode.setPos(self._enemyRef.enemyNode.getPos()) def initModel(self): self.healthGobletModel = Actor('models/HealthGoblet', {'float':'models/HealthGoblet-anim'}) self.healthGobletModel.reparentTo(self.healthGobletNode) self.healthGobletModel.setCollideMask(BitMask32.allOff()) self.healthGobletModel.setScale(0.5) self.healthGobletModel.loop('float') def updateHealthGoblet(self, task): playerNode = self._playerRef.playerNode if utils.getIsInRange(playerNode.getPos(), self.healthGobletNode.getPos(), self.perceptionRange): print 'healed player for:', self.healthGiven self._playerRef.heal(self.healthGiven) self.healthGobletModel.stop() self.suicide() return task.done return task.again def suicide(self): # Cleanup the healthGobletModel self.healthGobletModel.cleanup() self.healthGobletModel.delete() self.healthGobletNode.removeNode() self._enemyRef = None self._playerRef = None
def delete(self): try: self.Avatar_deleted except: Actor.cleanup(self) self.Avatar_deleted = 1 self.style = None self.collTube = None self.hpText = None self.hpTextGenerator = None ShadowCaster.delete(self) Actor.delete(self)
def delete(self): try: self.Avatar_deleted except: self.deleteNametag3d() Actor.cleanup(self) self.Avatar_deleted = 1 del self.__font del self.style del self.soundChatBubble self.nametag.destroy() del self.nametag self.nametag3d.removeNode() ShadowCaster.delete(self) Actor.delete(self)
def delete(self): try: self.Avatar_deleted except: self.deleteNametag3d() Actor.cleanup(self) if self.ManagesNametagAmbientLightChanged: self.ignoreNametagAmbientLightChange() self.Avatar_deleted = 1 del self.__font del self.style del self.soundChatBubble del self.nametag self.nametag3d.removeNode() ShadowCaster.delete(self) Actor.delete(self)
def delete(self): try: self.Avatar_deleted except: self.deleteNametag3d() Actor.cleanup(self) if self.ManagesNametagAmbientLightChanged: self.ignoreNametagAmbientLightChange() self.Avatar_deleted = 1 del self.__font del self.style del self.soundChatBubble del self.nametag self.nametag3d.removeNode() ShadowCaster.delete(self) Actor.delete(self)
class SpellInst(): def __init__(self,spell,caster,pos,rot,worldNP,bullet): self.spell=spell self.caster=caster # Load spell model self.model=Actor(spell.model) shape=BulletSphereShape(0.666) self.collNP=worldNP.attachNewNode(BulletGhostNode('SpellSphere')) self.collNP.setCollideMask(BitMask32.allOn()) self.collNP.node().addShape(shape) self.collNP.setPos(pos+move_forwards(rot,1.0)) self.collNP.setHpr(Vec3(rot,0,0)) bullet.attachGhost(self.collNP.node()) self.collNP.reparentTo(worldNP) self.bullet=bullet # Reparent the model to render. self.model.reparentTo(self.collNP) self.distance=0 self.remove_me=False def __del__(self): self.bullet.removeGhost(self.collNP.node()) self.collNP.removeNode() self.model.delete() def update(self,dt,bulletworld): retval='' if self.collNP.node().getNumOverlappingNodes()>0: for node in self.collNP.node().getOverlappingNodes(): if node.getName()==self.caster: retval='hit owner' else: retval=node.getName() vel=move_forwards(self.collNP.getH(),self.spell.speed) self.collNP.setPos(self.collNP.getPos()+vel*dt) self.distance+=vel.length()*dt if self.distance>self.spell.range: retval='over range' return retval
def delete(self): try: self.Avatar_deleted return except: self.Avatar_deleted = 1 self.setBlend(frameBlend=False) self.deleteNametag3d() Actor.cleanup(self) if self.ManagesNametagAmbientLightChanged: self.ignoreNametagAmbientLightChange() del self.__font del self.style del self.soundChatBubble self.nametag.destroy() del self.nametag self.nametag3d.removeNode() ShadowCaster.delete(self) Actor.delete(self)
class Persona(DirectObject): def __init__(self, contexto, _input): # referencias self.contexto = contexto self.input = _input self.topografia = None # componentes self.actor = None def iniciar(self): log.info("iniciar") b = self.contexto.base # referencias self.topografia = self.contexto.obtener_proveedor(Topografia.Nombre) # actor self.actor = Actor("modelos/hombre/male.egg") self.actor.reparentTo(b.render) # tasks self.contexto.base.taskMgr.add(self._update, "Persona_update") # return True def terminar(self): log.info("terminar") # tasks self.contexto.base.taskMgr.remove("Persona_update") # referencias self.input = None self.topografia = None # actor if self.actor: self.actor.delete() self.actor = None def _update(self, task): pos2d = Vec2(self.actor.getX(), self.actor.getY()) self.actor.setZ(self.topografia.obtener_altitud(pos2d)) return task.cont
class DistributedPartyFireworksActivity(DistributedPartyActivity, FireworkShowMixin): notify = directNotify.newCategory('DistributedPartyFireworksActivity') def __init__(self, cr): DistributedPartyFireworksActivity.notify.debug('__init__') DistributedPartyActivity.__init__(self, cr, ActivityIds.PartyFireworks, ActivityTypes.HostInitiated, wantLever=True) FireworkShowMixin.__init__(self, restorePlaygroundMusic=True, startDelay=FireworksPostLaunchDelay) def setEventId(self, eventId): DistributedPartyFireworksActivity.notify.debug('setEventId( %s )' % FireworkShows.getString(eventId)) self.eventId = eventId def setShowStyle(self, showStyle): DistributedPartyFireworksActivity.notify.debug('setShowStyle( %d )' % showStyle) self.showStyle = showStyle def setSongId(self, songId): self.songId = songId def load(self): DistributedPartyFireworksActivity.notify.debug('load') DistributedPartyActivity.load(self) self.eventId = PartyGlobals.FireworkShows.Summer self.launchPadModel = loader.loadModel('phase_13/models/parties/launchPad') self.launchPadModel.setH(90.0) self.launchPadModel.setPos(0.0, -18.0, 0.0) self.launchPadModel.reparentTo(self.root) railingsCollection = self.launchPadModel.findAllMatches('**/launchPad_mesh/*railing*') for i in xrange(railingsCollection.getNumPaths()): railingsCollection[i].setAttrib(AlphaTestAttrib.make(RenderAttrib.MGreater, 0.75)) leverLocator = self.launchPadModel.find('**/RocketLever_locator') self.lever.setPosHpr(Vec3.zero(), Vec3.zero()) self.lever.reparentTo(leverLocator) self.toonPullingLeverInterval = None self.sign.reparentTo(self.launchPadModel.find('**/launchPad_sign_locator')) self.rocketActor = Actor('phase_13/models/parties/rocket_model', {'launch': 'phase_13/models/parties/rocket_launch'}) rocketLocator = self.launchPadModel.find('**/rocket_locator') self.rocketActor.reparentTo(rocketLocator) self.rocketActor.node().setBound(OmniBoundingVolume()) self.rocketActor.node().setFinal(True) effectsLocator = self.rocketActor.find('**/joint1') self.rocketExplosionEffect = RocketExplosion(effectsLocator, rocketLocator) self.rocketParticleSeq = None self.launchSound = base.loadSfx('phase_13/audio/sfx/rocket_launch.ogg') self.activityFSM = FireworksActivityFSM(self) self.activityFSM.request('Idle') return def unload(self): DistributedPartyFireworksActivity.notify.debug('unload') taskMgr.remove(self.taskName('delayedStartShow')) if self.rocketParticleSeq: self.rocketParticleSeq.pause() self.rocketParticleSeq = None self.launchPadModel.removeNode() del self.launchPadModel del self.toonPullingLeverInterval self.rocketActor.delete() self.rocketExplosionEffect.destroy() self.activityFSM.request('Disabled') del self.rocketActor del self.launchSound del self.activityFSM del self.eventId del self.showStyle DistributedPartyActivity.unload(self) return def _leverPulled(self, collEntry): DistributedPartyFireworksActivity.notify.debug('_leverPulled') hostPulledLever = DistributedPartyActivity._leverPulled(self, collEntry) if self.activityFSM.getCurrentOrNextState() == 'Active': self.showMessage(TTLocalizer.PartyFireworksAlreadyActive) elif self.activityFSM.getCurrentOrNextState() == 'Disabled': self.showMessage(TTLocalizer.PartyFireworksAlreadyDone) elif self.activityFSM.getCurrentOrNextState() == 'Idle': if hostPulledLever: base.cr.playGame.getPlace().fsm.request('activity') self.toonPullingLeverInterval = self.getToonPullingLeverInterval(base.localAvatar) self.toonPullingLeverInterval.append(Func(self.d_toonJoinRequest)) self.toonPullingLeverInterval.append(Func(base.cr.playGame.getPlace().fsm.request, 'walk')) self.toonPullingLeverInterval.start() else: self.showMessage(TTLocalizer.PartyOnlyHostLeverPull) def setState(self, newState, timestamp): DistributedPartyFireworksActivity.notify.debug('setState( newState=%s, ... )' % newState) DistributedPartyActivity.setState(self, newState, timestamp) if newState == 'Active': self.activityFSM.request(newState, timestamp) else: self.activityFSM.request(newState) def startIdle(self): DistributedPartyFireworksActivity.notify.debug('startIdle') def finishIdle(self): DistributedPartyFireworksActivity.notify.debug('finishIdle') def startActive(self, showStartTimestamp): DistributedPartyFireworksActivity.notify.debug('startActive') messenger.send(FireworksStartedEvent) timeSinceStart = globalClockDelta.localElapsedTime(showStartTimestamp) if timeSinceStart > self.rocketActor.getDuration('launch'): self.rocketActor.hide() if timeSinceStart < 60: self.startShow(self.eventId, self.showStyle, self.songId, showStartTimestamp) else: self.rocketActor.play('launch') self.rocketParticleSeq = Sequence(Wait(RocketSoundDelay), Func(base.playSfx, self.launchSound), Func(self.rocketExplosionEffect.start), Wait(RocketDirectionDelay), LerpHprInterval(self.rocketActor, 4.0, Vec3(0, 0, -60)), Func(self.rocketExplosionEffect.end), Func(self.rocketActor.hide)) self.rocketParticleSeq.start() taskMgr.doMethodLater(FireworksPostLaunchDelay, self.startShow, self.taskName('delayedStartShow'), extraArgs=[self.eventId, self.showStyle, self.songId, showStartTimestamp, self.root]) def finishActive(self): self.rocketParticleSeq = None DistributedPartyFireworksActivity.notify.debug('finishActive') messenger.send(FireworksFinishedEvent) taskMgr.remove(self.taskName('delayedStartShow')) FireworkShowMixin.disable(self) return def startDisabled(self): DistributedPartyFireworksActivity.notify.debug('startDisabled') if not self.rocketActor.isEmpty(): self.rocketActor.hide() def finishDisabled(self): DistributedPartyFireworksActivity.notify.debug('finishDisabled') def handleToonDisabled(self, toonId): self.notify.warning('handleToonDisabled no implementation yet')
class DistributedPartyJukeboxActivityBase(DistributedPartyActivity): notify = directNotify.newCategory("DistributedPartyJukeboxActivityBase") def __init__(self, cr, actId, phaseToMusicData): DistributedPartyActivity.__init__(self, cr, actId, ActivityTypes.Continuous) self.phaseToMusicData = phaseToMusicData self.jukebox = None self.gui = None self.tunes = [] self.music = None # Data is tuple (phase, filename) self.currentSongData = None self.localQueuedSongInfo = None self.localQueuedSongListItem = None def generateInit(self): self.gui = JukeboxGui(self.phaseToMusicData) def load(self): DistributedPartyActivity.load(self) # Load Jukebox actor self.jukebox = Actor( "phase_13/models/parties/jukebox_model", {"dance": "phase_13/models/parties/jukebox_dance"}) self.jukebox.reparentTo(self.root) # Create collision area for jukebox self.collNode = CollisionNode(self.getCollisionName()) self.collNode.setCollideMask(ToontownGlobals.CameraBitmask | ToontownGlobals.WallBitmask) collTube = CollisionTube(0, 0, 0, 0.0, 0.0, 4.25, 2.25) collTube.setTangible(1) self.collNode.addSolid(collTube) self.collNodePath = self.jukebox.attachNewNode(self.collNode) self.sign.setPos(-5.0, 0, 0) self.activate() def unload(self): DistributedPartyActivity.unload(self) self.gui.unload() if self.music is not None: self.music.stop() self.jukebox.stop() self.jukebox.delete() self.jukebox = None self.ignoreAll() def getCollisionName(self): return self.uniqueName("jukeboxCollision") def activate(self): self.accept("enter" + self.getCollisionName(), self.__handleEnterCollision) #=============================================================================== # Enter/Exit Jukebox #=============================================================================== def __handleEnterCollision(self, collisionEntry): if base.cr.playGame.getPlace().fsm.getCurrentState().getName( ) == "walk": base.cr.playGame.getPlace().fsm.request("activity") self.d_toonJoinRequest() def joinRequestDenied(self, reason): DistributedPartyActivity.joinRequestDenied(self, reason) self.showMessage(TTLocalizer.PartyJukeboxOccupied) # Distributed (broadcast ram) def handleToonJoined(self, toonId): """ Toon requested to use the jukebox and the request has been granted. """ toon = base.cr.doId2do.get(toonId) if toon: self.jukebox.lookAt(base.cr.doId2do[toonId]) self.jukebox.setHpr(self.jukebox.getH() + 180.0, 0, 0) if toonId == base.localAvatar.doId: self.__localUseJukebox() def handleToonExited(self, toonId): """ Typically called when toon times out. """ if toonId == base.localAvatar.doId and self.gui.isLoaded(): self.__deactivateGui() def handleToonDisabled(self, toonId): """ A toon dropped unexpectedly from the game. Handle it! """ self.notify.warning("handleToonDisabled no implementation yet") def __localUseJukebox(self): """ Sets the local toon to use the jukebox, including activating the GUI and changing the client into the appropriate state. """ base.localAvatar.disableAvatarControls() base.localAvatar.stopPosHprBroadcast() self.__activateGui() self.accept(JukeboxGui.CLOSE_EVENT, self.__handleGuiClose) # We are locking exiting now in case the AI times the toon out of the Jukebox. taskMgr.doMethodLater(0.5, self.__localToonWillExitTask, self.uniqueName("toonWillExitJukeboxOnTimeout"), extraArgs=None) self.accept(JukeboxGui.ADD_SONG_CLICK_EVENT, self.__handleQueueSong) if self.isUserHost(): self.accept(JukeboxGui.MOVE_TO_TOP_CLICK_EVENT, self.__handleMoveSongToTop) def __localToonWillExitTask(self, task): self.localToonExiting() return Task.done def __activateGui(self): self.gui.enable(timer=JUKEBOX_TIMEOUT) self.gui.disableAddSongButton() if self.currentSongData is not None: self.gui.setSongCurrentlyPlaying(self.currentSongData[0], self.currentSongData[1]) self.d_queuedSongsRequest() def __deactivateGui(self): self.ignore(JukeboxGui.CLOSE_EVENT) self.ignore(JukeboxGui.SONG_SELECT_EVENT) self.ignore(JukeboxGui.MOVE_TO_TOP_CLICK_EVENT) base.cr.playGame.getPlace().setState("walk") base.localAvatar.startPosHprBroadcast() base.localAvatar.enableAvatarControls() self.gui.unload() self.__localClearQueuedSong() def isUserHost(self): """ Checks if the localAvatar is the host of the party Returns true if localAvatar is the host of the party """ return (self.party.partyInfo.hostId == base.localAvatar.doId) # Distributed (clsend airecv) def d_queuedSongsRequest(self): self.sendUpdate("queuedSongsRequest") # Distributed (only sender receives this response) def queuedSongsResponse(self, songInfoList, index): """ Gets a list of songs and adds them to the queue. Called when the toon first interacts with the jukebox. Parameters: songInfoList is the list of songs index is the item # on the list for a song previously queued by the player. """ if self.gui.isLoaded(): for i in range(len(songInfoList)): songInfo = songInfoList[i] self.__addSongToQueue(songInfo, isLocalQueue=(index >= 0 and i == index)) self.gui.enableAddSongButton() def __handleGuiClose(self): """ Closes Jukebox GUI, cleans up event handlers, and sets client state to normal. Typically triggered when the player clicks the close button in the jukebox, which fires CLOSE_EVENT. """ self.__deactivateGui() self.d_toonExitDemand() #=============================================================================== # Queueing songs #=============================================================================== def __handleQueueSong(self, name, values): """ Requests to queue a song. Triggered when the localAvatar clicks the "Add/Replace Song button". """ self.d_setNextSong(values[0], values[1]) # Distributed (clsend airecv) def d_setNextSong(self, phase, filename): self.sendUpdate("setNextSong", [(phase, filename)]) # Distributed (sender only gets it back) def setSongInQueue(self, songInfo): if self.gui.isLoaded(): phase = sanitizePhase(songInfo[0]) filename = songInfo[1] data = self.getMusicData(phase, filename) if data: if self.localQueuedSongListItem is not None: self.localQueuedSongListItem["text"] = data[0] else: self.__addSongToQueue(songInfo, isLocalQueue=True) def __addSongToQueue(self, songInfo, isLocalQueue=False): """ Adds a song to the queue. If it's the localAvatar's queued song, then it marks it. Parameters: songInfo is a list of phase and data isLocalQueue is flag marking whether this is the localAvatar's queued song """ isHost = (isLocalQueue and self.isUserHost()) data = self.getMusicData(sanitizePhase(songInfo[0]), songInfo[1]) if data: listItem = self.gui.addSongToQueue(data[0], highlight=isLocalQueue, moveToTopButton=isHost) if isLocalQueue: self.localQueuedSongInfo = songInfo self.localQueuedSongListItem = listItem def __localClearQueuedSong(self): """ Clears the queued song records. """ self.localQueuedSongInfo = None self.localQueuedSongListItem = None #=============================================================================== # Music Playback #=============================================================================== def __play(self, phase, filename, length): """ Plays some music! """ assert self.notify.debugStateCall(self) self.music = base.loadMusic((MUSIC_PATH + "%s") % (phase, filename)) if self.music: if self.__checkPartyValidity() and hasattr( base.cr.playGame.getPlace().loader, "music") and base.cr.playGame.getPlace().loader.music: base.cr.playGame.getPlace().loader.music.stop() base.resetMusic.play() self.music.setTime(0.0) self.music.setLoopCount(getMusicRepeatTimes(length)) self.music.play() jukeboxAnimControl = self.jukebox.getAnimControl("dance") if not jukeboxAnimControl.isPlaying(): self.jukebox.loop("dance") self.currentSongData = (phase, filename) def __stop(self): """ Stops animations and and clears GUI. """ self.jukebox.stop() self.currentSongData = None if self.music: self.music.stop() if self.gui.isLoaded(): self.gui.clearSongCurrentlyPlaying() # Distributed (broadcast ram) def setSongPlaying(self, songInfo, toonId): """ Sets the song from the AI to play in the client. Parameters: songInfo is a list with two items: phase and filename """ phase = sanitizePhase(songInfo[0]) filename = songInfo[1] assert (self.notify.debug("setSongPlaying phase_%d/%s" % (phase, filename))) # setSongPlaying sends empty filename if songs have stop playing # in order for the client to clean up. if not filename: self.__stop() return data = self.getMusicData(phase, filename) if data: self.__play(phase, filename, data[1]) self.setSignNote(data[0]) # Update the gui if it's active: if self.gui.isLoaded(): item = self.gui.popSongFromQueue() self.gui.setSongCurrentlyPlaying(phase, filename) if item == self.localQueuedSongListItem: self.__localClearQueuedSong() if toonId == localAvatar.doId: localAvatar.setSystemMessage(0, TTLocalizer.PartyJukeboxNowPlaying) #=============================================================================== # Host only: Push his/her queued song to the top of the playlist. #=============================================================================== def __handleMoveSongToTop(self): """ Requests to move the localAvatar's queued song to top. It is triggered when the user clicks on the "moveToTopButton" on the GUI. This is only enabled for the host of the party. """ if self.isUserHost() and self.localQueuedSongListItem is not None: self.d_moveHostSongToTopRequest() # Distributed (clsend airecv) def d_moveHostSongToTopRequest(self): self.notify.debug("d_moveHostSongToTopRequest") self.sendUpdate("moveHostSongToTopRequest") # Distributed (only host gets it) def moveHostSongToTop(self): self.notify.debug("moveHostSongToTop") if self.gui.isLoaded(): self.gui.pushQueuedItemToTop(self.localQueuedSongListItem) def getMusicData(self, phase, filename): data = [] phase = sanitizePhase(phase) phase = self.phaseToMusicData.get(phase) if phase: data = phase.get(filename, []) return data def __checkPartyValidity(self): """ Function that checks the validity of a street, it's loader and the geometry""" if hasattr(base.cr.playGame, "getPlace") and base.cr.playGame.getPlace() and \ hasattr(base.cr.playGame.getPlace(), "loader") and base.cr.playGame.getPlace().loader: return True else: return False
class Npc(): def __init__(self,controlPointId,id, anchorx, anchory, anchorz,render,team): self.id = id self.anchorx = anchorx self.anchory = anchory self.anchorz = anchorz self.controlPointId = controlPointId self.target = None self.isMoving = False self.health = 200 self.isCurrentUser = False self.damage = 8 self.attackTimer = 0 self._is_dead = False self._team = team self.render = render '''Initializing NPC actors''' self.npc = Actor("models/priest", {"walk": "models/priest-walk", "attack":"models/priest-attack", "hurt":"models/priest-hit", "die":"models/priest-die"}) if self._team==0: self.npcTex = loader.loadTexture("models/tex/guard_red.png") else: self.npcTex = loader.loadTexture("models/tex/guard_blue.png") self.npc.setTexture(self.npcTex) self.npc.setScale(0.5, 0.5, 0.5) self.npc.clearColor() self.npc.clearColorScale() self.npc.setColor(255, 0, 0, 0) self.npc.setColorScale(255, 0, 0, 0) self.npc.reparentTo(self.render) self.npc.setPos(anchorx,anchory,anchorz) self.AIchar = AICharacter("npc"+str(self.id),self.npc, 100, 0.05, 5) self.AIbehaviors = self.AIchar.getAiBehaviors() self.hb = HealthBar(1.5, value=self.health) #self._floater = NodePath(PandaNode("char_info")) #self._floater.reparentTo(self.npc) self.hb.setPos(0, 0, 11.9) self.hb.reparentTo(self.npc) #self.hb.reparentTo(self.npc) def renderBlue(self,AIworld): if not self._is_dead: self.AIbehaviors.removeAi("pursue") self.npc.detachNode() print "Started delete procedure for npc ", print self.id self.npc.delete() self.npc = Actor("models/priest", {"walk": "models/priest-walk", "attack":"models/priest-attack", "hurt":"models/priest-hit", "die":"models/priest-die"}) self.npcTex = loader.loadTexture("models/tex/guard_blue.png") self.npc.setTexture(self.npcTex) self.npc.setScale(0.5, 0.5, 0.5) self.npc.clearColor() self.npc.clearColorScale() self.npc.setColor(255, 0, 0, 0) self.npc.setColorScale(255, 0, 0, 0) self.npc.reparentTo(self.render) self.npc.setPos(self.anchorx,self.anchory,self.anchorz) AIworld.removeAiChar("npc"+str(self.id)) self.AIchar = AICharacter("npc"+str(self.id),self.npc, 100, 0.05, 5) self.AIbehaviors = self.AIchar.getAiBehaviors() AIworld.addAiChar(self.AIchar) self.hb = HealthBar(1.5, value=self.health) self.hb.setPos(0, 0, 18.1) self.hb.reparentTo(self.npc) #self.hb.reparentTo(self.npc) def renderRed(self,AIworld): if not self._is_dead: self.AIbehaviors.removeAi("pursue") self.npc.detachNode() self.npc.delete() self.npc = Actor("models/priest", {"walk": "models/priest-walk", "attack":"models/priest-attack", "hurt":"models/priest-hit", "die":"models/priest-die"}) self.npcTex = loader.loadTexture("models/tex/guard_red.png") self.npc.setTexture(self.npcTex) self.npc.setScale(0.5, 0.5, 0.5) self.npc.clearColor() self.npc.clearColorScale() self.npc.setColor(255, 0, 0, 0) self.npc.setColorScale(255, 0, 0, 0) self.npc.reparentTo(self.render) self.npc.setPos(self.anchorx,self.anchory,self.anchorz) AIworld.removeAiChar("npc"+str(self.id)) self.AIchar = AICharacter("npc"+str(self.id),self.npc, 100, 0.05, 5) self.AIbehaviors = self.AIchar.getAiBehaviors() AIworld.addAiChar(self.AIchar) AIworld.update() self.hb = HealthBar(1.5, value=self.health) self.hb.setPos(0, 0, 8.1) self.hb.reparentTo(self.npc) def switchTeam(self,AIworld): print self.id, print " from team ", print self._team, print " getting deleted." if self._team==0: self._team=1 self.renderBlue(AIworld) else: self._team=0 self.renderRed(AIworld) if self.isCurrentUser: main.freeDeadNpc(self.id) self.target = None self.isMoving = False self.health = 200 self.isCurrentUser = False self.damage = 8 self.attackTimer = 0 self._is_dead = False def set_health(self, health): self.health = health def take_damage(self, health_change): health = self.health if health <= health_change and not self._is_dead: self.killNpc() else: health = health-health_change self.set_health(health) self.hb.setValue(self.health) self.npc.play("hurt") def killNpc(self): self.set_health(0) self.hb.setValue(0) self.AIbehaviors.removeAi("pursue") hurt_interval = self.npc.actorInterval("hurt") death_interval = self.npc.actorInterval("die") seq = Sequence(hurt_interval, death_interval) seq.start() self.npc.pose("die",45) self._is_dead = True self.npc.detachNode() if self.isCurrentUser: main.freeDeadNpc(self.id) #main.cManager.sendRequest(Constants.CMSG_NPCDEATH, [self.id]) print Constants.CMSG_NPCDEATH, print " + ", print self.id def chaseTarget(self, target, status = False): if(not self.isMoving): self.target = target self.AIbehaviors.pursue(self.target) self.npc.loop("walk") self.isMoving = True self.isCurrentUser = status def stopChase(self): #self.AIbehaviors.pauseAi("pursue") if not self._is_dead: self.AIbehaviors.removeAi("pursue") p1 = LerpHprInterval(self.npc, 4, Point3(180,0,0)) p2 = LerpPosInterval(self.npc, 4, Point3(self.anchorx, self.anchory, self.anchorz)) animInterval = self.npc.actorInterval("walk", loop = 1, duration=4) p2.start() p1.start() animInterval.start() self.isMoving = False self.target = None self.isCurrentUser = False def givNPCDistance(self,charachter): x = self.npc.getX() y = self.npc.getY() z = self.npc.getZ() minDist = math.sqrt( (charachter.getX()-x)*(charachter.getX()-x) + (charachter.getY()-y)*(charachter.getY()-y) + (charachter.getZ()-z)*(charachter.getZ()-z) ) return minDist def checkNpcIsAlive(self): if(self.health>0): return True else: return False def shouldAttack(self,currentTime,cManager): if not self._is_dead: if self.isMoving: if self.attackTimer>0: self.attackTimer = self.attackTimer-currentTime #print self.attackTimer if self.AIbehaviors.behaviorStatus("pursue")=="done": #self.npc.stop("walk") #print self.npc.getAnimControl("walk") if self.attackTimer<=0: if self.npc.getAnimControl("walk").isPlaying(): self.npc.stop("walk") if not self.npc.getAnimControl("attack").isPlaying(): #self.npc.loop("attack") self.npc.play("attack") self.attackTimer = 2 #myInterval = self.npc.actorInterval("attack") #seq = Sequence(myInterval) #seq.append(Wait(3)) #seq.start() if self.isCurrentUser: cManager.sendRequest(Constants.CMSG_NPCATTACK, [self.id, self.damage]) if self.AIbehaviors.behaviorStatus("pursue")=="active": if self.npc.getAnimControl("attack").isPlaying(): self.npc.stop("attack") if not self.npc.getAnimControl("walk").isPlaying(): self.npc.loop("walk")
class Monster(): def __init__(self, id, parent, type, pos): self.id = id self.parent = parent self.hp = 100 self.speed = 1 self.can_move = True if type == 'baby': self.node = Actor( 'models/baby', { 'walk': 'models/baby-walk', 'stand': 'models/baby-stand', 'idle': 'models/baby-idle', 'jump': 'models/baby-jump', 'bite1': 'models/baby-bite1', 'bite2': 'models/baby-bite2', 'head_attack': 'models/baby-head_attack', 'hit1': 'models/baby-hit1', 'hit2': 'models/baby-hit2', 'die': 'models/baby-die' }) self.head_node = self.node.exposeJoint(None, "modelRoot", "Bip01_Head") self.body_node = self.node.exposeJoint(None, "modelRoot", "Bip01_Pelvis") self.node.setH(180) self.node.setScale(0.03) self.node.flattenLight() self.zpos = 0 self.node.setPos(pos[0] * TILE_SIZE, pos[1] * TILE_SIZE, self.zpos) self.node.setTexture(loader.loadTexture('models/Zomby_D.png')) self.ts_normal = TextureStage('ts_normal') self.tex_normal = loader.loadTexture('models/Zomby_N.png') self.ts_normal.setMode(TextureStage.MNormal) self.node.setTexture(self.ts_normal, self.tex_normal) self.ts_gloss = TextureStage('ts_gloss') self.tex_gloss = loader.loadTexture('models/Zomby_S1.png') self.ts_gloss.setMode(TextureStage.MGloss) self.node.setTexture(self.ts_gloss, self.tex_gloss) self.ts_glow = TextureStage('ts_glow') self.tex_glow = loader.loadTexture('models/Zomby_I.png') self.ts_glow.setMode(TextureStage.MGlow) self.node.setTexture(self.ts_glow, self.tex_glow) self.node.reparentTo(render) self.node.loop('walk') elif type == 'nos': self.node = loader.loadModel('models/nos') self.zpos = 5 self.node.setPos(pos[0] * TILE_SIZE, pos[1] * TILE_SIZE, self.zpos) self.node.setScale(2) if self.id == 1: self.node.setColor(1, 0, 0) elif self.id == 2: self.node.setColor(0, 1, 0) elif self.id == 3: self.node.setColor(0, 0, 1) else: self.node.setColor(1, 1, 1) self.node.reparentTo(render) #self.patrol_points = [(1,1), (4,11), (12,20), (18,4), (19,17)] #initialize 3d sound self.audio3d = Audio3DManager.Audio3DManager(base.sfxManagerList[0], base.camera) self.shot_head = self.audio3d.loadSfx( 'audio/Zombie In Pain-SoundBible.com-134322253.wav') self.shot_body = self.audio3d.loadSfx( 'audio/Zombie Moan-SoundBible.com-565291980.wav') self.moan1 = self.audio3d.loadSfx( 'audio/Mindless Zombie Awakening-SoundBible.com-255444348.wav') self.moan2 = self.audio3d.loadSfx( 'audio/Zombie Brain Eater-SoundBible.com-1076387080.wav') self.aggro_sound = self.audio3d.loadSfx( 'audio/Mummy Zombie-SoundBible.com-1966938763.wav') self.attack_sound = self.audio3d.loadSfx( 'audio/Chopping Off Limb-SoundBible.com-884800545.wav') self.audio3d.attachSoundToObject(self.moan1, self.node) self.audio3d.attachSoundToObject(self.moan2, self.node) self.audio3d.attachSoundToObject(self.shot_head, self.node) self.audio3d.attachSoundToObject(self.shot_body, self.node) self.audio3d.attachSoundToObject(self.aggro_sound, self.node) self.audio3d.attachSoundToObject(self.attack_sound, self.node) delay0 = Wait(d(35)) delay1 = Wait(25 + d(35)) delay2 = Wait(25 + d(35)) self.moan_sequence = Sequence(delay0, SoundInterval(self.moan1), delay1, SoundInterval(self.moan2), delay2) self.moan_sequence.loop() self.parent.collision_manager.createMonsterCollision(self) self.aggro_sound_last_played = 0 #--------------------------brain------------------------- self.node.setH(160) self.pause = False self.action = ACTION_IDLE if percent(20): self.orders = ORDERS_PATROL else: self.orders = ORDERS_IDLE self.last_melee = 0 self.player_last_seen_abs = None self.idle_timer = time.time() self.idle_value = 1 self.current_waypoint = None #self.wait_until = None self.herding_timer = None self.path = None taskMgr.doMethodLater(1, self.behaviourTask, 'MonsterBehaviourTask' + str(self.id)) taskMgr.doMethodLater(1, self.debugMoveTask, 'MonsterMoveTask' + str(self.id)) def getLOS(self): return self.parent.collision_manager.checkMonsterPlayerLos(self) def sensePlayer(self): """Return True if player sensed, and his last known coordinates are stored in self.player_last_seen_abs""" # if the player is dead, do not sense him if self.parent.player.health <= 0: return False #get player's position p_pos_abs = self.parent.player.node.getPos() my_pos_abs = self.node.getPos() #--------------------------------SENSE--------------------------------- #if player is within SENSING_RANGE we know he is there if self.distanceToPlayer() < SENSING_RANGE: #print "TOO CLOSE LOOSER!" self.player_last_seen_abs = p_pos_abs return True #---------------------------------HEAR---------------------------------- #if player is within HEARING_RANGE we know he is there effective_hearing_range = HEARING_RANGE if self.parent.player.gunshot_at: effective_hearing_range *= 3 else: if self.parent.player.sprint: effective_hearing_range *= 2 if not self.parent.player.moving: effective_hearing_range = 0 if self.distanceToPlayer() < effective_hearing_range: print "I HEAR U!" self.parent.player.adrenaline() #if we can see go chase him if self.getLOS(): self.player_last_seen_abs = p_pos_abs return True #we cannot see him, build new path to that tile else: dest = getTile(p_pos_abs) path = pathFind(self.parent.level, getTile(self.node.getPos()), dest) if path: self.path = path self.orders = ORDERS_PATROL self.action = ACTION_FOLLOW_PATH return False #-------------------------------SEE--------------------------------- #if player is in front of us if self.angleToPlayerAbs() <= 45: #if he is close enough to see and we can see him if self.distanceToPlayer() <= VIEW_RANGE and self.getLOS(): self.player_last_seen_abs = p_pos_abs #print "vidim!" return True #if player has a flashlight lit, and we can see him go after him if self.parent.player.flashlight and self.getLOS(): self.player_last_seen_abs = p_pos_abs #print "vidim flashlight" return True #---------------------SEE MY OWN SHADOW--------------------------- #if player is behind us and has a lit up flashlight and we have LOS to him if self.angleToPlayerAbs() > 135 and self.angleToPlayerAbs() < 225: if self.parent.player.flashlight and self.getLOS(): #if he is looking at us my_pos_rel = self.node.getPos(self.parent.player.node) forward = Vec2(0, 1) if math.fabs( forward.signedAngleDeg( Vec2(my_pos_rel[0], my_pos_rel[1]))) <= 30: #go after my own shadow print "herding" self.orders = ORDERS_HERDING self.node.setH(self.parent.player.node.getH()) self.herding_timer = time.time() return False def distanceToPlayer(self): p_pos_abs = self.parent.player.node.getPos() my_pos_abs = self.node.getPos() return math.sqrt( math.pow(p_pos_abs[0] - my_pos_abs[0], 2) + math.pow(p_pos_abs[1] - my_pos_abs[1], 2)) def angleToPlayer(self): p_pos_rel = self.parent.player.node.getPos(self.node) forward = Vec2(0, 1) return forward.signedAngleDeg(Vec2(p_pos_rel[0], p_pos_rel[1])) def angleToPlayerAbs(self): return math.fabs(self.angleToPlayer()) def behaviourTask(self, task): if self.pause: return task.again #top priority, if we sense a player, go after him! if self.sensePlayer(): if time.time() - self.aggro_sound_last_played > 5: self.aggro_sound.play() self.aggro_sound_last_played = time.time() self.action = ACTION_CHASE return task.again elif self.orders == ORDERS_IDLE: #percent chance to go on patrol if percent(10): self.orders = ORDERS_PATROL return task.again self.action = ACTION_IDLE elif self.orders == ORDERS_PATROL: #percent chance to get idle if percent(5): self.orders = ORDERS_IDLE return task.again #if we are already patroling, dont change anything if self.action == ACTION_FOLLOW_PATH: return task.again #build a new path for patrol dest = self.getNewPatrolPoint() self.path = pathFind(self.parent.level, getTile(self.node.getPos()), dest) self.action = ACTION_FOLLOW_PATH elif self.orders == ORDERS_HERDING: self.action = ACTION_MOVE if time.time() - self.herding_timer > HERDING_TIMEOUT: self.orders = ORDERS_IDLE return task.again def debugMoveTask(self, task): if self.pause: return task.cont #print "orders:", self.orders if self.action == ACTION_CHASE: self.parent.player.adrenaline() look_pos = Point3(self.player_last_seen_abs.getX(), self.player_last_seen_abs.getY(), self.zpos) self.node.lookAt(look_pos) self.node.setFluidPos(self.node, 0, CHASE_SPEED * globalClock.getDt(), 0) if self.distanceToPlayer( ) <= MELEE_RANGE and self.angleToPlayerAbs() <= 45 and self.getLOS( ): if time.time() - self.last_melee >= MELEE_TIME: self.attack_sound.play() att = random.randint(0, 2) if att == 0: animname = 'bite1' elif att == 1: animname = 'bite2' else: animname = 'head_attack' self.node.play(animname) duration = self.node.getNumFrames( animname) / 24 # animation play rate taskMgr.doMethodLater(duration, self.finishedAnim, 'FinishedAnim', extraArgs=[]) self.parent.player.getDamage() self.last_melee = time.time() elif self.action == ACTION_IDLE: if time.time() - self.idle_timer > IDLE_TIME: #we are standing still and rotating, see on whic side we will rotate now self.idle_timer = time.time() if percent(20): self.idle_value *= -1 self.rotateBy(self.idle_value * IDLE_ROTATE_SPEED) elif self.action == ACTION_FOLLOW_PATH: #if we dont have a waypoint, calculate one if not self.current_waypoint: try: #get next tile from path tile = self.path[0] self.path = self.path[1:] #calculate waypoint varx = 5 - (d(4) + d(4)) vary = 5 - (d(4) + d(4)) self.current_waypoint = (Point3(tile[0] * TILE_SIZE + varx, tile[1] * TILE_SIZE + vary, self.zpos), time.time()) #print "waypoint:", self.current_waypoint self.node.lookAt(self.current_waypoint[0]) except (IndexError, TypeError): #we have reached the end of path self.orders = ORDERS_IDLE self.current_waypoint = None #if we have a waypoint move forward towards it, and check if we arrived at it else: self.node.setFluidPos(self.node, 0, NORMAL_SPEED * globalClock.getDt(), 0) my_pos = self.node.getPos() #if we are close enough to the waypoint or if we didnt get to waypoint in time, delete it so we know we need a new one if math.fabs( my_pos[0] - self.current_waypoint[0][0] ) < 1 and math.fabs( my_pos[1] - self.current_waypoint[0][1] ) < 2 \ or time.time() - self.current_waypoint[1] > WAYPOINT_TIMER: self.current_waypoint = None elif self.action == ACTION_MOVE: self.node.setFluidPos(self.node, 0, NORMAL_SPEED * globalClock.getDt(), 0) return task.cont def finishedAnim(self): if not self.pause: self.node.loop('walk') def rotateBy(self, value): self.node.setH((self.node.getH() + value) % 360) def getNewPatrolPoint(self): lvl = self.parent.level allTiles = lvl.getFloorTiles() while True: t = (d(lvl.getMaxX()), d(lvl.getMaxY())) if t in allTiles: return t def hitWall(self): if self.action == ACTION_CHASE: return #print "lupio!" """self.moan1.play() self.rotateBy( 180 ) self.node.setFluidPos(self.node, 0, CHASE_SPEED*globalClock.getDt(), 0) #self.action = IDLE """ """ old = self.node.getH() rnd = 80 + random.randint( 0, 20 ) forward = Vec2( 0, 1 ) impact = Vec2( pos[0], pos[1] ) angle = forward.signedAngleDeg( impact ) #print "angle:", angle if angle < 0: #+ cause angle is negative rnd = 91 + angle self.node.setH( (self.node.getH() + rnd)%360 ) elif angle > 0: rnd = -91 + angle self.node.setH( (self.node.getH() + rnd)%360 ) #print "stari:", old, " novi:", self.node.getH() """ pass def pauze(self): if self.moan_sequence: self.moan_sequence.pause() self.pause = True def resume(self): if self.moan_sequence: self.moan_sequence.resume() self.pause = False def destroy(self): self.audio3d.detachSound(self.moan1) self.audio3d.detachSound(self.moan2) self.audio3d.detachSound(self.shot_head) self.audio3d.detachSound(self.shot_body) self.audio3d.detachSound(self.aggro_sound) self.audio3d.detachSound(self.attack_sound) if self.moan_sequence != None: self.moan_sequence.pause() self.moan_sequence = None taskMgr.remove('MonsterBehaviourTask' + str(self.id)) taskMgr.remove('MonsterMoveTask' + str(self.id)) self.cn_head.node().clearPythonTag('node') self.cn_body.node().clearPythonTag('node') self.cn_pusher.node().clearPythonTag('node') self.cn_ray.node().clearPythonTag('node') self.node.delete() self.node.cleanup() self.node.removeNode() """
class Npc(): def __init__(self, controlPointId, id, anchorx, anchory, anchorz, render, team): self.id = id self.anchorx = anchorx self.anchory = anchory self.anchorz = anchorz self.controlPointId = controlPointId self.target = None self.isMoving = False self.health = 200 self.isCurrentUser = False self.damage = 8 self.attackTimer = 0 self._is_dead = False self._team = team self.render = render '''Initializing NPC actors''' self.npc = Actor( "models/priest", { "walk": "models/priest-walk", "attack": "models/priest-attack", "hurt": "models/priest-hit", "die": "models/priest-die" }) if self._team == 0: self.npcTex = loader.loadTexture("models/tex/guard_red.png") else: self.npcTex = loader.loadTexture("models/tex/guard_blue.png") self.npc.setTexture(self.npcTex) self.npc.setScale(0.5, 0.5, 0.5) self.npc.clearColor() self.npc.clearColorScale() self.npc.setColor(255, 0, 0, 0) self.npc.setColorScale(255, 0, 0, 0) self.npc.reparentTo(self.render) self.npc.setPos(anchorx, anchory, anchorz) self.AIchar = AICharacter("npc" + str(self.id), self.npc, 100, 0.05, 5) self.AIbehaviors = self.AIchar.getAiBehaviors() self.hb = HealthBar(1.5, value=self.health) #self._floater = NodePath(PandaNode("char_info")) #self._floater.reparentTo(self.npc) self.hb.setPos(0, 0, 11.9) self.hb.reparentTo(self.npc) #self.hb.reparentTo(self.npc) def renderBlue(self, AIworld): if not self._is_dead: self.AIbehaviors.removeAi("pursue") self.npc.detachNode() print "Started delete procedure for npc ", print self.id self.npc.delete() self.npc = Actor( "models/priest", { "walk": "models/priest-walk", "attack": "models/priest-attack", "hurt": "models/priest-hit", "die": "models/priest-die" }) self.npcTex = loader.loadTexture("models/tex/guard_blue.png") self.npc.setTexture(self.npcTex) self.npc.setScale(0.5, 0.5, 0.5) self.npc.clearColor() self.npc.clearColorScale() self.npc.setColor(255, 0, 0, 0) self.npc.setColorScale(255, 0, 0, 0) self.npc.reparentTo(self.render) self.npc.setPos(self.anchorx, self.anchory, self.anchorz) AIworld.removeAiChar("npc" + str(self.id)) self.AIchar = AICharacter("npc" + str(self.id), self.npc, 100, 0.05, 5) self.AIbehaviors = self.AIchar.getAiBehaviors() AIworld.addAiChar(self.AIchar) self.hb = HealthBar(1.5, value=self.health) self.hb.setPos(0, 0, 18.1) self.hb.reparentTo(self.npc) #self.hb.reparentTo(self.npc) def renderRed(self, AIworld): if not self._is_dead: self.AIbehaviors.removeAi("pursue") self.npc.detachNode() self.npc.delete() self.npc = Actor( "models/priest", { "walk": "models/priest-walk", "attack": "models/priest-attack", "hurt": "models/priest-hit", "die": "models/priest-die" }) self.npcTex = loader.loadTexture("models/tex/guard_red.png") self.npc.setTexture(self.npcTex) self.npc.setScale(0.5, 0.5, 0.5) self.npc.clearColor() self.npc.clearColorScale() self.npc.setColor(255, 0, 0, 0) self.npc.setColorScale(255, 0, 0, 0) self.npc.reparentTo(self.render) self.npc.setPos(self.anchorx, self.anchory, self.anchorz) AIworld.removeAiChar("npc" + str(self.id)) self.AIchar = AICharacter("npc" + str(self.id), self.npc, 100, 0.05, 5) self.AIbehaviors = self.AIchar.getAiBehaviors() AIworld.addAiChar(self.AIchar) AIworld.update() self.hb = HealthBar(1.5, value=self.health) self.hb.setPos(0, 0, 8.1) self.hb.reparentTo(self.npc) def switchTeam(self, AIworld): print self.id, print " from team ", print self._team, print " getting deleted." if self._team == 0: self._team = 1 self.renderBlue(AIworld) else: self._team = 0 self.renderRed(AIworld) if self.isCurrentUser: main.freeDeadNpc(self.id) self.target = None self.isMoving = False self.health = 200 self.isCurrentUser = False self.damage = 8 self.attackTimer = 0 self._is_dead = False def set_health(self, health): self.health = health def take_damage(self, health_change): health = self.health if health <= health_change and not self._is_dead: self.killNpc() else: health = health - health_change self.set_health(health) self.hb.setValue(self.health) self.npc.play("hurt") def killNpc(self): self.set_health(0) self.hb.setValue(0) self.AIbehaviors.removeAi("pursue") hurt_interval = self.npc.actorInterval("hurt") death_interval = self.npc.actorInterval("die") seq = Sequence(hurt_interval, death_interval) seq.start() self.npc.pose("die", 45) self._is_dead = True self.npc.detachNode() if self.isCurrentUser: main.freeDeadNpc(self.id) #main.cManager.sendRequest(Constants.CMSG_NPCDEATH, [self.id]) print Constants.CMSG_NPCDEATH, print " + ", print self.id def chaseTarget(self, target, status=False): if (not self.isMoving): self.target = target self.AIbehaviors.pursue(self.target) self.npc.loop("walk") self.isMoving = True self.isCurrentUser = status def stopChase(self): #self.AIbehaviors.pauseAi("pursue") if not self._is_dead: self.AIbehaviors.removeAi("pursue") p1 = LerpHprInterval(self.npc, 4, Point3(180, 0, 0)) p2 = LerpPosInterval( self.npc, 4, Point3(self.anchorx, self.anchory, self.anchorz)) animInterval = self.npc.actorInterval("walk", loop=1, duration=4) p2.start() p1.start() animInterval.start() self.isMoving = False self.target = None self.isCurrentUser = False def givNPCDistance(self, charachter): x = self.npc.getX() y = self.npc.getY() z = self.npc.getZ() minDist = math.sqrt((charachter.getX() - x) * (charachter.getX() - x) + (charachter.getY() - y) * (charachter.getY() - y) + (charachter.getZ() - z) * (charachter.getZ() - z)) return minDist def checkNpcIsAlive(self): if (self.health > 0): return True else: return False def shouldAttack(self, currentTime, cManager): if not self._is_dead: if self.isMoving: if self.attackTimer > 0: self.attackTimer = self.attackTimer - currentTime #print self.attackTimer if self.AIbehaviors.behaviorStatus("pursue") == "done": #self.npc.stop("walk") #print self.npc.getAnimControl("walk") if self.attackTimer <= 0: if self.npc.getAnimControl("walk").isPlaying(): self.npc.stop("walk") if not self.npc.getAnimControl("attack").isPlaying(): #self.npc.loop("attack") self.npc.play("attack") self.attackTimer = 2 #myInterval = self.npc.actorInterval("attack") #seq = Sequence(myInterval) #seq.append(Wait(3)) #seq.start() if self.isCurrentUser: cManager.sendRequest(Constants.CMSG_NPCATTACK, [self.id, self.damage]) if self.AIbehaviors.behaviorStatus("pursue") == "active": if self.npc.getAnimControl("attack").isPlaying(): self.npc.stop("attack") if not self.npc.getAnimControl("walk").isPlaying(): self.npc.loop("walk")
class Character(FSM.FSM,Vehicle): """ An animated character with various steering behaviors. By enabling and disabling different steering behaviors Character can be used as a keyboard + mouse controlled player avatar, or can be controlled (for example) by a Finite State Machine and used to implement a non-player character. """ # FIXME: Think it might be clearer and safer if Character has a FSM instead # of inheriting from FSM (classes that inherit from Character will likely # want to inherit from FSM too). def __init__(self,name='Ralph',model='models/ralph',run='models/ralph-run', walk='models/ralph-walk',pos=None,avoidObstacles=True, avoidVehicles=True, hprs=(180,0,0,1,1,1), # Ralph's Y is backward ): """Initialise the character. By default tries to load Panda3D's Ralph model: models/ralph, models/ralph-run and models/ralph-walk.""" FSM.FSM.__init__(self,'Character') Vehicle.__init__(self,pos=pos,avoidObstacles=avoidObstacles, avoidVehicles=avoidVehicles,radius=2.5) self.name = name self.lastPose = 0 # Used when transitioning between animations self.actor = Actor(model,{"run":run,"walk":walk}) self.actor.setHprScale(*hprs) self.actor.reparentTo(self.prime) # Add a task for this Character to the global task manager. self.characterStepTask=taskMgr.add(self.characterStep,"Character step task") def characterStep(self,task): """Update the character. Called every frame by Panda's global task manager.""" # Update the orientation of the Character. Want to face in the # direction of the Vehicle's velocity in the X and Y dimensions, but # always face straight forward (not up or down) in the Z dimension. pr = self.prime.getP(),self.prime.getR() self.prime.lookAt((self._pos+self._velocity).getX(), (self._pos+self._velocity).getY(),0) self.prime.setHpr(self.prime.getH(),*pr) # Animate the Character's Actor. The Character automatically changes # between stand, run and walk animations and varies the playrate of the # animations depending on the speed of the Character's Vehicle. speed = self.getspeed() if speed < .05: if self.getCurrentOrNextState() != 'Stand': self.request('Stand') elif speed < .25: self.actor.setPlayRate(speed*4,"walk") if self.getCurrentOrNextState() != 'Walk': self.request('Walk') else: self.actor.setPlayRate(speed*2,"run") if self.getCurrentOrNextState() != 'Run': self.request('Run') return Task.cont def destroy(self): """Prepare this Character to be garbage-collected by Python: Remove all of the Character's nodes from the scene graph, remove its CollisionSolids from the global CollisionTraverser, clear its CollisionHandler, remove tasks, destroy Actor. After executing this method, any remaining references to the Character object can be destroyed by the user module, and the Character will be garbage-collected by Python. """ taskMgr.remove(self.characterStepTask) self.cleanup() self.actor.delete() Vehicle.destroy(self) # Methods for handling animations. def storeLastPose(self): currAnim=self.actor.getCurrentAnim() numFrames=self.actor.getNumFrames(currAnim) animFrame=self.actor.getCurrentFrame(currAnim) self.lastPose=float(animFrame)/float(numFrames) self.actor.stop(currAnim) def loopFromPose(self,animName): self.actor.pose(animName,frame=self.lastPose*self.actor.getNumFrames(animName)) self.actor.loop(animName,restart=0) # FSM State handlers. Called when transitioning to a new state. def enterRun(self): self.loopFromPose("run") def exitRun(self): self.storeLastPose() def enterWalk(self): self.loopFromPose("walk") def exitWalk(self): self.storeLastPose() def enterStand(self): standPoseFrame=6 # frame 6 (the most acceptable stand pose) numFrames=self.actor.getNumFrames("walk") lastFrame=self.lastPose*numFrames # "mirror" the frame to bring it closer to the most acceptable stand pose if lastFrame>.5*(numFrames-1): lastFrame=numFrames-1-lastFrame frameDiff=standPoseFrame-lastFrame # if already at stand pose, don't do anything if frameDiff==0: return # forward animation playback if frameDiff>=0: fromFrame=lastFrame toFrame=standPoseFrame else: # backward animation playback fromFrame=standPoseFrame toFrame=lastFrame playDir=2*frameDiff/numFrames self.actor.setPlayRate(playDir,"walk") self.actor.play("walk", fromFrame=fromFrame, toFrame=toFrame)
class Enemy(FSM, Unit): # Declare private variables _enemyActive = False _removeCorpseDelay = 2 # seconds before corpse is cleaned def __init__(self, mainRef, attributes): print("Enemy instantiated") Unit.__init__(self) FSM.__init__(self, 'playerFSM') self._mainRef = mainRef self._playerRef = mainRef.player self._AIworldRef = mainRef.AIworld self._enemyListRef = mainRef.enemyList self._ddaHandlerRef = mainRef.DDAHandler self._stateHandlerRef = mainRef.stateHandler self._scenarioHandlerRef = mainRef.scenarioHandler #self.topEnemyNode = mainRef.mainNode.attachNewNode('topEnemyNode') self.initEnemyNode(mainRef.mainNode) utils.enemyDictionary[self.enemyNode.getName()] = self self.loadEnemyModel(attributes.modelName) self.initAttributes(attributes) self.initEnemyAi() self.initEnemyDDA() self.initEnemyCollisionHandlers() self.initEnemyCollisionSolids() #self.request('Idle') self.request('Disabled') # Start enemy updater task self.enemyUpdaterTask = taskMgr.add(self.enemyUpdater, 'enemyUpdaterTask') def initEnemyNode(self, parentNode): enemyName = 'enemy' + str(len(self._enemyListRef)) self.enemyNode = parentNode.attachNewNode(enemyName) self._enemyListRef.append(self) def loadEnemyModel(self, modelName): modelPrefix = 'models/' + modelName self.enemyModel = Actor( modelPrefix + '-model', { 'walk': modelPrefix + '-walk', 'attack': modelPrefix + '-attack', 'idle': modelPrefix + '-idle', 'awake': modelPrefix + '-awake', 'stop': modelPrefix + '-stop', 'hit': modelPrefix + '-hit', 'death1': modelPrefix + '-death1', 'death2': modelPrefix + '-death2' }) self.enemyModel.reparentTo(self.enemyNode) self.enemyNode.setPos(Point3.zero()) self.enemyNode.setDepthOffset(-1) def initAttributes(self, attributes): perceptionRangeMultiplier = 1.2 combatRangeMultiplier = .3 speedMultiplier = .1 self.strength = attributes.strength self.constitution = attributes.constitution self.dexterity = attributes.dexterity self.mass = attributes.mass self.movementSpeed = speedMultiplier * attributes.movementSpeed self.perceptionRange = perceptionRangeMultiplier * attributes.perceptionRange self.combatRange = combatRangeMultiplier * attributes.combatRange self.attackBonus = attributes.attackBonus self.damageBonus = attributes.damageBonus self.damageRange = attributes.damageRange self.initiativeBonus = attributes.initiativeBonus self.fixedHealthPoints = attributes.fixedHealthPoints self.armorClass = attributes.armorClass if attributes.startLevel > 1: for i in range(attributes.startLevel - 1): self.increaseLevel() self.expAward = attributes.expAward self.initHealth() def initEnemyDDA(self): if self._scenarioHandlerRef.getHasDDA(): maxLevelDifference = self._ddaHandlerRef.maxLevelDifference # Level enemy up to player's level minus maxLevelDifference levelDifference = self._playerRef.level - self.level if levelDifference >= maxLevelDifference: for i in range(levelDifference - maxLevelDifference): self.increaseLevel() def getAttackBonus(self): modifier = self.getStrengthModifier() if self.getStrengthModifier( ) > self.getDexterityModifier() else self.getDexterityModifier() ab = self.attackBonus + (self.level / 2) + modifier # + utils.getD20() if self._scenarioHandlerRef.getHasDDA(): attackBonusModifier = self._ddaHandlerRef.attackBonusModifier if attackBonusModifier < 0: ab -= attackBonusModifier if ab < 1: ab = 1 return ab + utils.getD20() def initEnemyAi(self): self.enemyAI = AICharacter( 'enemy', self.enemyNode, self.mass, # Mass 0.1, # Movt force self.movementSpeed) # Max force self._AIworldRef.addAiChar(self.enemyAI) self.enemyAIBehaviors = self.enemyAI.getAiBehaviors() #self.enemyAIBehaviors.obstacleAvoidance(1.0) def initEnemyCollisionHandlers(self): self.groundHandler = CollisionHandlerQueue() self.collPusher = CollisionHandlerPusher() def initEnemyCollisionSolids(self): # Enemy ground ray groundRay = CollisionRay(0, 0, 2, 0, 0, -1) groundColl = CollisionNode('enemyGroundRay') groundColl.addSolid(groundRay) groundColl.setIntoCollideMask(BitMask32.allOff()) groundColl.setFromCollideMask(BitMask32.bit(1)) self.groundRayNode = self.enemyNode.attachNewNode(groundColl) #self.groundRayNode.show() base.cTrav.addCollider(self.groundRayNode, self.groundHandler) # Enemy collision sphere collSphereNode = CollisionNode('enemyCollSphere') collSphere = CollisionSphere(0, 0, 0.1, 0.2) collSphereNode.addSolid(collSphere) collSphereNode.setIntoCollideMask(BitMask32.allOff()) collSphereNode.setFromCollideMask(BitMask32.bit(2)) self.sphereNode = self.enemyNode.attachNewNode(collSphereNode) #sphereNode.show() base.cTrav.addCollider(self.sphereNode, self.collPusher) self.collPusher.addCollider(self.sphereNode, self.enemyNode) # Enemy picker collision sphere pickerSphereCollNode = CollisionNode(self.enemyNode.getName()) pickerCollSphere = CollisionSphere(0, 0, 0, 0.5) pickerSphereCollNode.addSolid(pickerCollSphere) pickerSphereCollNode.setFromCollideMask(BitMask32.allOff()) pickerSphereCollNode.setIntoCollideMask(BitMask32.bit(1)) self.pickerNode = self.enemyNode.attachNewNode(pickerSphereCollNode) #sphereNodePath.show() # Enemy attack collision sphere attackCollSphereNode = CollisionNode(self.enemyNode.getName() + 'atkSph') attackCollSphere = CollisionSphere(0, 0, 0.1, 0.15) attackCollSphereNode.addSolid(attackCollSphere) attackCollSphereNode.setIntoCollideMask(BitMask32.bit(3)) attackCollSphereNode.setFromCollideMask(BitMask32.allOff()) attackSphereNode = self.enemyNode.attachNewNode(attackCollSphereNode) #attackSphereNode.show() def slowMovementByPercentage(self, percentage=30, slowDuration=20): #print self.enemyNode.getName(), ' slowed by ', percentage, ' %' oldSpeed = self.movementSpeed newSpeed = ((100.0 - percentage) / 100.0) * oldSpeed if newSpeed < 1.0: newSpeed = 1.0 self.movementSpeed = newSpeed taskMgr.doMethodLater(slowDuration, self.removeSlowMovement, 'removeSlowMovementTask', extraArgs=[oldSpeed], appendTask=True) def removeSlowMovement(self, oldSpeed, task): self.movementSpeed = oldSpeed return task.done def checkGroundCollisions(self): if self.groundHandler.getNumEntries() > 0: self.groundHandler.sortEntries() entries = [] for i in range(self.groundHandler.getNumEntries()): entry = self.groundHandler.getEntry(i) #print('entry:', entry) entries.append(entry) entries.sort(lambda x, y: cmp( y.getSurfacePoint(render).getZ(), x.getSurfacePoint(render).getZ())) for i in range(len(entries)): if entries[i].getIntoNode().getName()[:6] == 'ground': #print('entryFound:', entries[0]) newZ = entries[i].getSurfacePoint(base.render).getZ() self.enemyNode.setZ(newZ) #print('enemyZ:', newZ) break def enemyUpdater(self, task): if self._stateHandlerRef.state != self._stateHandlerRef.PLAY or not self._enemyActive: self.enemyModel.stop() # Do not do anything when paused return task.cont self.checkGroundCollisions() if self.getIsDead(): self.onDeath() return task.done if self._playerRef.getIsDead(): return task.cont playerPos = self._playerRef.playerNode.getPos() enemyPos = self.enemyNode.getPos() # If player is within enemy perception range if utils.getIsInRange(playerPos, enemyPos, self.perceptionRange): # If enemy is not doing anything if self.state == 'Idle': # Start pursuing if self.state != 'Pursue': self.request('Pursue') # If enemy is already pursueing elif self.state == 'Pursue': # If player is within combat range if utils.getIsInRange(playerPos, enemyPos, self.combatRange): #print 'enemy go to combat' # Go to combat if self.state != 'Combat': self.request('Combat') # If enemy is already in combat elif self.state == 'Combat': # If player has moved out of combat range if not utils.getIsInRange(playerPos, enemyPos, self.combatRange): # Start pursuing if self.state != 'Pursue': self.request('Pursue') # If enemy is disabled elif self.state == 'Disabled': if self.enemyAI.getMaxForce() != self.movementSpeed: self.enemyAI.setMaxForce(self.movementSpeed) # If player is not within perception range else: if self.state != 'Idle': self.request('Idle') return task.cont def pursuePlayer(self): taskMgr.add(self.pursue, 'pursueTask') def pursue(self, task): if self.state == 'Pursue' and not self.getIsDead(): pitchRoll = self.enemyNode.getP(), self.enemyNode.getR() self.enemyNode.headsUp(self._playerRef.playerNode) self.enemyNode.setHpr(self.enemyNode.getH() - 180, *pitchRoll) speed = -self.movementSpeed * globalClock.getDt() self.enemyNode.setFluidPos(self.enemyNode, 0, speed, 0) return task.cont else: task.done def enterIdle(self): #print 'enemy enterIdle' stopEnemy = self.enemyModel.actorInterval('stop', loop=0) idleEnemy = self.enemyModel.actorInterval('idle', startFrame=0, endFrame=1, loop=0) self.stopSequence = Sequence(stopEnemy, idleEnemy) self.stopSequence.start() self.isSleeping = True def exitIdle(self): #print('enemy exitIdle') self.stopSequence.finish() def enterPursue(self): #print('enemy enterPursue') loopWalkEnemy = Func(self.enemyModel.loop, 'walk', fromFrame=0, toFrame=12) # Only awake enemy if it comes from idle if self.isSleeping: self.isSleeping = False awakeEnemy = self.enemyModel.actorInterval('awake', loop=0) self.awakeSequence = Sequence(awakeEnemy, loopWalkEnemy, Func(self.pursuePlayer)) else: self.awakeSequence = Sequence(loopWalkEnemy, Func(self.pursuePlayer)) self.awakeSequence.start() def exitPursue(self): #print('enemy exitPursue') self.awakeSequence.finish() def enterCombat(self): #print('enemy enterCombat') self.enemyModel.stop() attackDelay = self.getInitiativeRoll() self.attackTask = taskMgr.doMethodLater(attackDelay, self.attackPlayer, 'attackPlayerTask') def enterDisabled(self): #print 'enterDisable' pass def exitDisabled(self): #print 'exitDisable' pass def exitCombat(self): #print('enemy exitCombat') taskMgr.remove(self.attackTask) def enterDeath(self): #print('enemy enterDeath') self.enemyAIBehaviors.removeAi('all') randomDeathAnim = 'death' + str(utils.getD2()) self.enemyModel.play(randomDeathAnim) def attack(self, other): if not self.getIsDead() and not other.getIsDead(): if self.getAttackBonus() >= other.getArmorClass(): dmg = self.getDamageBonus() #print(self.getName(), ' damaged ', other.getName(), ' for ', dmg, ' damage') other.receiveDamage(dmg) return 2 # Returns 2 when self damages other return 1 # Returns 1 when self attacks other, but misses return 0 # Returns 0 when either self or other is dead def attackPlayer(self, task): if self._stateHandlerRef.state != self._stateHandlerRef.PLAY or not self._enemyActive: # Do not do anything when paused return task.again if self._playerRef.getIsDead(): print('player is already dead') self.request('Idle') return task.done elif self.getIsDead(): return task.done else: #print('Attack player!') # Make sure enemy is facing player when attacking pitchRoll = self.enemyNode.getP(), self.enemyNode.getR() self.enemyNode.headsUp(self._playerRef.playerNode) self.enemyNode.setHpr(self.enemyNode.getH() - 180, *pitchRoll) attacked = self.attack(self._playerRef) if attacked != 0: self.playAttackAnimation() if attacked == 2: self._playerRef.playerModel.play('hit') return task.again def playAttackAnimation(self): self.enemyModel.play('attack', fromFrame=0, toFrame=12) def playHitAnimation(self): self.enemyModel.play('hit') def moveEnemy(self, x, y): self.enemyNode.setPos(x, y, .01) def handleHealthGlobe(self): global dropChanceFactor global dropChance global maxDropChance # if we drop, create health goblet chance = dropChanceFactor + dropChance if self._scenarioHandlerRef.getHasDDA(): chance *= self._ddaHandlerRef.healthGobletModifier if utils.getD100() <= chance: HealthGoblet(self._mainRef, self) print 'dropping health goblet' # Otherwise, increase dropChance else: if dropChance + dropChanceFactor <= maxDropChance: dropChance += dropChanceFactor def suicide(self): print('suicide: ', self) # Remove AI behavior self.enemyAIBehaviors.removeAi('all') # Remove enemy picker sphere (handlerQueue) self.pickerNode.removeNode() taskMgr.add(self.removeCorpse, 'removeCorpseTask') def onDeath(self): if self.getIsDead(): # Remove AI behavior self.enemyAIBehaviors.removeAi('all') # Award the player exp self._playerRef.receiveEXP(self.expAward) # Remove enemy picker sphere (handlerQueue) self.pickerNode.removeNode() # Change state self.request('Death') # Increase DDA death count if self._scenarioHandlerRef.getHasDDA(): self._ddaHandlerRef.enemyDeathCount += 1 # Handle health globe self.handleHealthGlobe() # Remove enemy corpse and clean up taskMgr.doMethodLater(self._removeCorpseDelay, self.removeCorpse, 'removeCorpseTask') def removeCorpse(self, task): # Remove enemy collision sphere (pusher) self.sphereNode.removeNode() # Stop the collision pusher self.collPusher = None # Remove enemy from enemyList self._enemyListRef.remove(self) # Cleanup the enemy model self.enemyModel.cleanup() self.enemyModel.delete() # Cleanup FSM self.cleanup() # Remove the enemy node self.enemyNode.removeNode() #self.topEnemyNode.removeNode() # Remove enemy updater tasks taskMgr.remove(self.enemyUpdaterTask) # Remove the passive regeneration task (from Unit class) self.removePassiveRegeneration() # Remove references self._mainRef = None self._playerRef = None self._AIworldRef = None self._enemyListRef = None self._stateHandlerRef = None return task.done
class Player(DirectObject): #---------------- # Initialization #---------------- def __init__(self): self.pNode = render.attachNewNode('playerRoot') # The root node of the player self.eyeHeight = 1.5 # The z height of the camera self.selectedBlock = 0 self.adjacentBlock = 0 self.selectedPlayer = None self.playerState = PlayerState(self) self.inputBuffer = InputBuffer() self.currentItem = None self.itemModels = {} self.playerModel = None # The bounding box of the player for collision with environment geometry self.boundingBoxCenterNode = self.pNode.attachNewNode('bboxCenter') self.boundingBoxCenterNode.setPos(0, 0, 0.9) self.boundingBox = BoundingBox(self.boundingBoxCenterNode, [0.2, 0.2, 0.9]) self.animFSM = PlayerAnimationFSM(self) self.animFSM.request('Idle') self.camNode = self.pNode.attachNewNode('cam') # The position of the player's eyes self.camNode.setPos(0, 0, self.eyeHeight) self.playerParent = self.pNode.attachNewNode('playerParent') # Change this back so items follow player turning #self.itemNode = self.playerParent.attachNewNode('3rdPersonItem') self.itemNode = self.pNode.attachNewNode('3rdPersonItem') self.itemNode.setPos(0, 0, 1.2) self.camItemNode = self.camNode.attachNewNode('1stPersonItem') # Node for name text self.nameText = None self.nameNode = self.pNode.attachNewNode('NameNode') self.nameNode.setPos(0, 0, 1.97) self.lookingRayNode = self.pNode.attachNewNode('lookingRayNode') self.lookingRayNode.setPos(0, 0, self.eyeHeight) lookingSeg = CollisionSegment(0, 0, 0, 0, 100, 0) self.lookingRay = self.lookingRayNode.attachNewNode(CollisionNode('lookingRay')) self.lookingRay.node().addSolid(lookingSeg) self.lookingRay.node().setFromCollideMask(Globals.BLOCK_PICKER_BITMASK | Globals.PLAYER_BITMASK) self.lookingRay.node().setIntoCollideMask(BitMask32.allOff()) self.lookingRayCollisionEntry = None self.pickerCollisionHandler = CollisionHandlerQueue() self.pickerTraverser = CollisionTraverser('pickerTraverser') self.pickerTraverser.addCollider(self.lookingRay, self.pickerCollisionHandler) if(not Settings.IS_SERVER): self.selectionGeom = SelectionGeom() else: self.selectionGeom = None self.CreateCollisionGeoms() self.LoadModels() def CreateCollisionGeoms(self): self.collisionNode = self.pNode.attachNewNode('CollisionNode') collisionGeom = self.collisionNode.attachNewNode(CollisionNode("legsPlayer")) collisionGeom.node().addSolid(CollisionSphere(0, 0, 0.35, 0.3)) collisionGeom.node().setFromCollideMask(Globals.ITEM_BITMASK) collisionGeom.node().setIntoCollideMask(Globals.PLAYER_BITMASK) #collisionGeom.show() collisionGeom.setPythonTag(Globals.TAG_COLLISION, Globals.COLLISION_LEG) collisionGeom.setPythonTag(Globals.TAG_PLAYER, self) collisionGeom = self.collisionNode.attachNewNode(CollisionNode("bodyPlayer")) collisionGeom.node().addSolid(CollisionSphere(0, 0, 0.9, 0.3)) collisionGeom.node().setFromCollideMask(BitMask32.allOff()) collisionGeom.node().setIntoCollideMask(Globals.PLAYER_BITMASK) #collisionGeom.show() collisionGeom.setPythonTag(Globals.TAG_COLLISION, Globals.COLLISION_BODY) collisionGeom.setPythonTag(Globals.TAG_PLAYER, self) collisionGeom = self.collisionNode.attachNewNode(CollisionNode("headPlayer")) collisionGeom.node().addSolid(CollisionSphere(0, 0, 1.45, 0.35)) collisionGeom.node().setFromCollideMask(BitMask32.allOff()) collisionGeom.node().setIntoCollideMask(Globals.PLAYER_BITMASK) collisionGeom.setPythonTag(Globals.TAG_COLLISION, Globals.COLLISION_HEAD) collisionGeom.setPythonTag(Globals.TAG_PLAYER, self) def CreateNameTextNode(self, name): self.nameText = PlayerName(name, self.nameNode) def RemoveSelectionGeom(self): if(self.selectionGeom is not None): self.selectionGeom.Destroy() self.selectionGeom = None def LoadModels(self, teamId = Game.SPECTATE): if(self.playerModel is not None): self.playerModel.cleanup() if(teamId == Game.TEAM_1): self.playerModel = Actor('Assets/Models/Players/ralph', {"run":"Assets/Models/Players/ralph-run", "walk":"Assets/Models/Players/ralph-walk"}) #elif(teamId == Game.TEAM_2): else: self.playerModel = Actor('Assets/Models/Players/ralph2', {"run":"Assets/Models/Players/ralph-run", "walk":"Assets/Models/Players/ralph-walk"}) self.playerModel.setScale(0.36) self.playerModel.reparentTo(self.playerParent) self.playerModel.setH(180) #self.playerModel.hide() self.playerModel.enableBlend() self.playerModel.loop('run') self.playerModel.pose('walk', 5) # self.outlineFilter = CommonFilters(base.win, base.cam) # if(not self.outlineFilter.setCartoonInk(0.5)): # print 'ERROR CARTOON FILTER' #-------------------- # Controlling models #-------------------- def HidePlayerModel(self): self.playerModel.hide() def ShowPlayerModel(self): self.playerModel.show() def LoopAnimation(self, animation): self.playerModel.setPlayRate(1.0, animation) self.playerModel.loop(animation) def RequestFSMTransition(self, animFSMState): self.animFSM.request(animFSMState) #-------------------- # Updating #-------------------- def Update(self, deltaTime): if(self.currentItem): self.currentItem.IncreaseTimeSinceLastUse(deltaTime) def UpdateLookingDirection(self, lookingDirection): if(not self.playerModel.isHidden() and self.GetPlayerState().GetValue(PlayerState.PLAYING_STATE) == PlayerState.PS_PLAYING): facingDirection = Vec3(lookingDirection.getX(), lookingDirection.getY(), 0) facingDirection.normalize() self.playerParent.lookAt(Point3(facingDirection)) self.itemNode.lookAt(Point3(self.itemNode.getPos() + lookingDirection)) def UpdateLookingRayDirection(self, lookingDirection): self.lookingRayNode.lookAt(Point3(lookingDirection.getX(), lookingDirection.getY(), lookingDirection.getZ() + self.eyeHeight)) def MovePosToPriorSnapshot(self, numFramesBack): self.pNode.setPos(self.GetPlayerState().GetValue(PlayerState.POSITION_HISTORY).GetPosition(numFramesBack)) def ReturnToCurrentPosition(self): self.pNode.setPos(self.currentPosition) #----------------------- # Weapons / items #----------------------- def OnSelectedItemChangeEvent(self, event): if(event.GetItemStack()): self.ChangeItem(event.GetItemStack().GetItem()) else: self.ChangeItem(None) def ChangeItem(self, item): print 'CHANGE ITEM curr', self.currentItem, 'new', item, self.GetPlayerState().GetValue(PlayerState.PID) # if(self.currentItem): # self.playerState.UpdateValue(PlayerState.CURRENT_ITEM, self.currentItem.GetItemId()) # else: # self.playerState.UpdateValue(PlayerState.CURRENT_ITEM, ItemId.NoItem) if(item != self.currentItem): oldItem = self.currentItem self.currentItem = item if(self.currentItem): self.playerState.UpdateValue(PlayerState.CURRENT_ITEM, self.currentItem.GetItemId()) else: self.playerState.UpdateValue(PlayerState.CURRENT_ITEM, ItemId.NoItem) #If both are item objects, equip dequip if(self.currentItem and oldItem): if(not Settings.IS_SERVER): self.currentItem.LoadContent() self.currentItem.ReparentTo(self.camItemNode) self.currentItem.EquipDequip(oldItem) # If just the old item is an obj, dequip it elif(oldItem): oldItem.Dequip() # If just the new item is an obj, equip it elif(self.currentItem): if(not Settings.IS_SERVER): self.currentItem.LoadContent() self.currentItem.ReparentTo(self.camItemNode) self.currentItem.Equip() # Change the item as though this is ourself, but then # reparent it so that it shows up properly in 3rd person def ThirdPersonChangeItem(self, item): self.ChangeItem(item) if(item and not Settings.IS_SERVER): self.currentItem.ReparentTo(self.itemNode) def ChangeBlockToPlace(self, direction): if(isinstance(self.currentItem, Builder)): if(direction > 0): self.currentItem.IncreaseBlock() else: self.currentItem.DecreaseBlock() def Reload(self): if(isinstance(self.currentItem, Firearm)): self.currentItem.Reload() # If we reloaded, tell fire an event if(self.GetPlayerState().GetValue(PlayerState.PID) == Globals.MY_PID): PlayerReloadEvent(self).Fire() def OnTeamChange(self, teamId): self.GetPlayerState().UpdateValue(PlayerState.TEAM, teamId) self.LoadModels(teamId) if(self.GetPlayerState().GetValue(PlayerState.PID) == Globals.MY_PID): self.HidePlayerModel() else: if(teamId != Globals.MY_TEAM): self.nameText.reshowOnInView = False self.nameText.Hide() else: self.nameText.reshowOnInView = True self.ShowNameAboveHead() self.nameText.SetColor(Globals.TEAM_COLORS[teamId]) #self.nameTextNode.setTextColor(Globals.TEAM_COLORS[teamId]) #------------------ # Death / Respawn #------------------- def OnDeath(self): self.collisionNode.stash() LerpHprInterval(self.playerParent, 0.7, (self.playerParent.getH(), 90, self.playerParent.getR())).start() self.itemNode.hide() self.camItemNode.hide() self.GetPlayerState().UpdateValue(PlayerState.PLAYING_STATE, PlayerState.PS_PLAYING_DEAD) # If this is the actual player if(self.GetPlayerState().GetValue(PlayerState.PID) == Globals.MY_PID): self.ShowPlayerModel() if(isinstance(self.currentItem, Firearm) and self.currentItem.IsADS()): self.currentItem.ToggleADS() def OnRespawn(self): self.collisionNode.unstash() self.GetInputBuffer().Clear() self.itemNode.show() self.camItemNode.show() self.GetPlayerState().UpdateValue(PlayerState.PLAYING_STATE, PlayerState.PS_PLAYING) if(self.GetPlayerState().GetValue(PlayerState.PID) == Globals.MY_PID): print 'Respawn event for me' self.HidePlayerModel() SelectedItemChangeEvent(None, ItemStack(self.currentItem)).Fire() else: self.ShowPlayerModel() #---------------------- # Accessor / Mutators #---------------------- def GetPos(self): return self.pNode.getPos() def SetPos(self, value): self.pNode.setPos(value) def GetZ(self): return self.pNode.getZ() def LerpTo(self, pos): LerpPosInterval(self.pNode, 0.1, pos).start() #--------------------------------------- # Playernames on screen #--------------------------------------- def ShowNameAboveHead(self): self.nameText.Show() def FadeOutNameAboveHead(self, task = None): self.nameText.FadeOut() #--------------------------------------- # Picking things with looking direction #---------------------------------------- #@pstat def PerfromLookingRayCollision(self, isSelf = True): self.pickerTraverser.traverse(render) entries = [] for i in range(self.pickerCollisionHandler.getNumEntries()): entry = self.pickerCollisionHandler.getEntry(i) entries.append(entry) numEntries = len(entries) if(numEntries > 0): entries.sort(lambda x,y: cmp(self.DistanceFromCam(x.getSurfacePoint(render)), self.DistanceFromCam(y.getSurfacePoint(render)))) self.lookingRayCollisionEntry = None playerEntry = None blockCollisionEntry = None for i in xrange(numEntries): if(entries[i].getIntoNodePath().getParent() != self.collisionNode): target = entries[i].getIntoNodePath().__str__() if(target.endswith('blockCollision')): blockCollisionEntry = entries[i] self.lookingRayCollisionEntry = blockCollisionEntry break elif(target.endswith('Player')): playerEntry = entries[i] self.lookingRayCollisionEntry = playerEntry break if(blockCollisionEntry and isinstance(self.currentItem, Builder)): point = blockCollisionEntry.getSurfacePoint(render) if(self.DistanceFromCam(point) <= Globals.MAX_SELECT_DISTANCE): norm = blockCollisionEntry.getSurfaceNormal(render) self.adjacentBlock = point + norm * 0.2 point -= norm * 0.2 x = point.getX() y = point.getY() z = point.getZ() if(self.selectionGeom): self.selectionGeom.SetPos(int(x), int(y), int(z)) self.selectionGeom.Show() self.selectedBlock = self.selectionGeom.GetPos() else: self.selectedBlock = VBase3(int(x), int(y), int(z)) else: self.selectedBlock = None self.adjacentBlock = None if(self.selectionGeom): self.selectionGeom.Hide() else: self.selectedBlock = None self.adjacentBlock = None if(self.selectionGeom): self.selectionGeom.Hide() if(playerEntry): player = playerEntry.getIntoNodePath().getPythonTag(Globals.TAG_PLAYER) if(player): # Each frame, if the crosshairs are hovering a player, fire the event # so the HUD can respond. PlayerSelectedEvent(player).Fire() self.selectedPlayer = player # # Before, we were only firing on changes # if(self.selectedPlayer != player): # self.selectedPlayer = player # if(not Settings.IS_SERVER and isSelf): # PlayerSelectedEvent(player).Fire() else: if(self.selectedPlayer != None): self.selectedPlayer = None if(not Settings.IS_SERVER and isSelf): PlayerSelectedEvent(None).Fire() def DistanceFromCam(self, otherPoint): eye = self.camNode.getPos(render) return (otherPoint - eye).length() def GetSelectedBlock(self): return self.selectedBlock def GetAdjacentBlock(self): return self.adjacentBlock def GetPlayerState(self): return self.playerState def GetInputBuffer(self): return self.inputBuffer def GetPlayerOverheadName(self): return self.nameText def GetCurrentItem(self): return self.currentItem #--------------- # Cleanup #--------------- def Destroy(self): for child in self.pNode.getChildren(): child.removeNode() self.pNode.removeNode() if(self.playerModel): self.playerModel.delete()
class Usuario(object): ''' Usuario ''' def __init__(self, dadosUsuario, principal=False): self.key = dadosUsuario['key'] self.nick = dadosUsuario['nick'] self.vida_real = float(dadosUsuario['vida_real']) self.vida_total = float(dadosUsuario['vida_total']) self.mana_real = float(dadosUsuario['mana_real']) self.mana_total = float(dadosUsuario['mana_total']) self.forca = float(dadosUsuario['forca']) self.velocidade = float(dadosUsuario['velocidade']) self.velocidade_atack = float(dadosUsuario['velocidade_atack']) self.estaMovendo = False self.estaRodando = False self.__task_name = "Task Usuario - " + self.key self.keyMap = { TECLA_esquerda: EVENT_up, TECLA_direita: EVENT_up, TECLA_frente: EVENT_up, TECLA_traz: EVENT_up } pandaFileModelo = get_path_modelo("ralph") pandaFileAnimacaoRun = get_path_animacao("ralph-run") pandaFileAnimacaoWalk = get_path_animacao("ralph-walk") self.modelo = Actor(pandaFileModelo, { "run": pandaFileAnimacaoRun, "walk": pandaFileAnimacaoWalk }) self.modelo.reparentTo(render) self.modelo.setScale(SCALE_MODELOS) self.set_pos((float(dadosUsuario['x']), float(dadosUsuario['y']), float(dadosUsuario['z']))) self.set_h(float(dadosUsuario['h'])) if not principal: self.text = Text(parent=self.modelo, pos=(0, 0, 5.5), scale=.5, align='center', cor=COR_VERDE, text=self.nick) self.text.billboardEffect() self.__setup_collision() taskMgr.add(self.__task_movimentacao, self.__task_name, sort=1) def get_x(self): return self.modelo.getX() def get_y(self): return self.modelo.getY() def get_z(self): return self.modelo.getZ() def get_h(self): return self.modelo.getH() def get_pos(self): return self.modelo.getPos() def set_x(self, x): self.modelo.setX(x) def set_y(self, y): self.modelo.setY(y) def set_z(self, z): self.modelo.setZ(z) def set_h(self, h): self.modelo.setH(h) def set_pos(self, pos): self.modelo.setPos(pos) def delete(self): taskMgr.remove(self.__task_name) self.modelo.delete() def __setup_collision(self): #Colisao self.cTrav = CollisionTraverser('usuarioTraverser') self.ralphGroundRay = CollisionRay() self.ralphGroundRay.setOrigin(0, 0, 5) self.ralphGroundRay.setDirection(0, 0, -1) self.ralphGroundCol = CollisionNode('ralphRay') self.ralphGroundCol.addSolid(self.ralphGroundRay) self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0)) self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff()) self.ralphGroundColNp = self.modelo.attachNewNode(self.ralphGroundCol) self.ralphGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler) #self.ralphGroundColNp.show() #self.cTrav.showCollisions(render) #Colisao def __task_movimentacao(self, task): dt = globalClock.getDt() startpos = self.modelo.getPos() #movimentos usuario modelo if self.keyMap[TECLA_esquerda] == EVENT_down and self.keyMap[ TECLA_direita] == EVENT_up: self.modelo.setH(self.modelo, ((self.velocidade * 5) * dt)) if not self.estaRodando: self.estaRodando = True elif self.keyMap[TECLA_direita] == EVENT_down and self.keyMap[ TECLA_esquerda] == EVENT_up: self.modelo.setH(self.modelo, ((self.velocidade * 5) * dt) * -1) if not self.estaRodando: self.estaRodando = True elif self.estaRodando: self.estaRodando = False if self.keyMap[TECLA_frente] == EVENT_down and self.keyMap[ TECLA_traz] == EVENT_up: self.modelo.setY(self.modelo, ((self.velocidade * 2) * dt) * -1) if self.estaMovendo is False: self.modelo.loop("run") self.estaMovendo = True elif self.keyMap[TECLA_traz] == EVENT_down and self.keyMap[ TECLA_frente] == EVENT_up: self.modelo.setY(self.modelo, (self.velocidade * dt)) if self.estaMovendo is False: self.modelo.loop("walk") self.estaMovendo = True elif self.estaMovendo: self.modelo.stop() self.modelo.pose("walk", 5) self.estaMovendo = False #movimentos usuario modelo if self.estaMovendo: self.cTrav.traverse(render) if self.ralphGroundHandler.getNumEntries() == 1: entry = self.ralphGroundHandler.getEntry(0) if entry.getIntoNode().getName() == "terrain": self.modelo.setZ(entry.getSurfacePoint(render).getZ()) else: self.modelo.setPos(startpos) return task.cont
class Creep(object): def __init__(self, showbase, midPoints, positionInList): self.showbase = showbase self.startHp = 50 self.totalHp = int(self.startHp * ( (self.showbase.level/4.0) + (self.showbase.level*0.1) )) self.showbase.totalHp = self.totalHp #CurrentHp that will be updated and used as value in hpBar self.hp = self.totalHp self.midPoints = midPoints self.end = midPoints[len(self.midPoints)-1][len(self.midPoints[0])-1] #Get just X and Y self.end = (self.end[0], self.end[1]) self.position = positionInList self.start = midPoints[0][0] self.creep = Actor("models/panda-model", {"walk": "models/panda-walk4"}) self.creep.setPos(self.start) self.creep.setScale(Point3(0.005,0.005,0.005)) self.hpBar = DirectWaitBar(text = "", range = 100, value = 100) self.hpBar.setScale(Point3(2.5, 4, 4)) self.hpBar.lookAt(0,0,-1) #Starts off running normal speed, not slowed self.isSlowed = False #For if another slow is overlapping self.overlapSlow = False #Make sure the value of hpBar shows self.hpBar.setDepthTest(False) self.hpBar.setDepthWrite(False) #Spanws the creep and its hpBar def spawn(self): self.creep.reparentTo(self.showbase.render) self.hpBar.reparentTo(self.showbase.render) #Makes the hpBar follow the creep # #Tried to, instead, make it reparent to self.creep #rather than self.showbase.render #So I didn't have to constantly update its position #But it didn't display when I tried this def showHpBar(self, task): x = self.creep.getPos()[0] y = self.creep.getPos()[1] z = self.creep.getPos()[2] self.hpBar.setPos(Point3( x, y+3, z+1)) #Creep has reached the end if ( int(x), int(y) ) == self.end: self.remove() return Task.cont def hit(self, damage): self.hp -= damage if self.isDead(): self.kill() #Update as a percentage of 100 (initial hpBar range is 100) self.hpBar.update(int((self.hp*100.0)/(self.totalHp))) def isDead(self): if self.hp <= 0: #Dead return True return False #Killed by towers, counts as kill def kill(self): #Remove the creep & hpBar from the screen self.creep.delete() self.hpBar.removeNode() #Set the value of this creep in the list of all creeps to 0 self.showbase.creeps[self.position] = 0 #Every five levels, double the gold self.showbase.gold += (1+(self.showbase.level/5)) #Either 1 or 2 if self.showbase.displayGameInfoNode1 != None: self.showbase.displayGameInfoNode1.destroy() self.showbase.displayGameInfoNode.destroy() self.showbase.displayGameInfo() #Reached end of board, does not count as kill def remove(self): #Remove the creep & hpBar from the screen self.creep.delete() self.hpBar.removeNode() #Set the value of this creep in the list of all creeps to 0 self.showbase.creeps[self.position] = 0 self.showbase.lives -= 1 if self.showbase.displayGameInfoNode1 != None: self.showbase.displayGameInfoNode1.destroy() self.showbase.displayGameInfoNode.destroy() self.showbase.displayGameInfo() #No more lives left, game over if self.showbase.lives == 0: self.showbase.displayGameOver() sys.exit() def move(self, path): self.pathSequence = Sequence() for i in xrange(len(path)-1): step = self.creep.posInterval( .4, path[i+1], startPos = path[i] ) self.pathSequence.append(step) self.pathSequence.start() def slow(self): self.pathSequence.setPlayRate(0.4) def resume(self): self.pathSequence.setPlayRate(1) def getPosition(self): return self.creep.getPos() def getTotalHp(self): return self.totalHp
class Usuario(object): """ Usuario """ def __init__(self, dadosUsuario, principal=False): self.key = dadosUsuario["key"] self.nick = dadosUsuario["nick"] self.vida_real = float(dadosUsuario["vida_real"]) self.vida_total = float(dadosUsuario["vida_total"]) self.mana_real = float(dadosUsuario["mana_real"]) self.mana_total = float(dadosUsuario["mana_total"]) self.forca = float(dadosUsuario["forca"]) self.velocidade = float(dadosUsuario["velocidade"]) self.velocidade_atack = float(dadosUsuario["velocidade_atack"]) self.estaMovendo = False self.estaRodando = False self.__task_name = "Task Usuario - " + self.key self.keyMap = {TECLA_esquerda: EVENT_up, TECLA_direita: EVENT_up, TECLA_frente: EVENT_up, TECLA_traz: EVENT_up} pandaFileModelo = get_path_modelo("ralph") pandaFileAnimacaoRun = get_path_animacao("ralph-run") pandaFileAnimacaoWalk = get_path_animacao("ralph-walk") self.modelo = Actor(pandaFileModelo, {"run": pandaFileAnimacaoRun, "walk": pandaFileAnimacaoWalk}) self.modelo.reparentTo(render) self.modelo.setScale(SCALE_MODELOS) self.set_pos((float(dadosUsuario["x"]), float(dadosUsuario["y"]), float(dadosUsuario["z"]))) self.set_h(float(dadosUsuario["h"])) if not principal: self.text = Text( parent=self.modelo, pos=(0, 0, 5.5), scale=0.5, align="center", cor=COR_VERDE, text=self.nick ) self.text.billboardEffect() self.__setup_collision() taskMgr.add(self.__task_movimentacao, self.__task_name, sort=1) def get_x(self): return self.modelo.getX() def get_y(self): return self.modelo.getY() def get_z(self): return self.modelo.getZ() def get_h(self): return self.modelo.getH() def get_pos(self): return self.modelo.getPos() def set_x(self, x): self.modelo.setX(x) def set_y(self, y): self.modelo.setY(y) def set_z(self, z): self.modelo.setZ(z) def set_h(self, h): self.modelo.setH(h) def set_pos(self, pos): self.modelo.setPos(pos) def delete(self): taskMgr.remove(self.__task_name) self.modelo.delete() def __setup_collision(self): # Colisao self.cTrav = CollisionTraverser("usuarioTraverser") self.ralphGroundRay = CollisionRay() self.ralphGroundRay.setOrigin(0, 0, 5) self.ralphGroundRay.setDirection(0, 0, -1) self.ralphGroundCol = CollisionNode("ralphRay") self.ralphGroundCol.addSolid(self.ralphGroundRay) self.ralphGroundCol.setFromCollideMask(BitMask32.bit(0)) self.ralphGroundCol.setIntoCollideMask(BitMask32.allOff()) self.ralphGroundColNp = self.modelo.attachNewNode(self.ralphGroundCol) self.ralphGroundHandler = CollisionHandlerQueue() self.cTrav.addCollider(self.ralphGroundColNp, self.ralphGroundHandler) # self.ralphGroundColNp.show() # self.cTrav.showCollisions(render) # Colisao def __task_movimentacao(self, task): dt = globalClock.getDt() startpos = self.modelo.getPos() # movimentos usuario modelo if self.keyMap[TECLA_esquerda] == EVENT_down and self.keyMap[TECLA_direita] == EVENT_up: self.modelo.setH(self.modelo, ((self.velocidade * 5) * dt)) if not self.estaRodando: self.estaRodando = True elif self.keyMap[TECLA_direita] == EVENT_down and self.keyMap[TECLA_esquerda] == EVENT_up: self.modelo.setH(self.modelo, ((self.velocidade * 5) * dt) * -1) if not self.estaRodando: self.estaRodando = True elif self.estaRodando: self.estaRodando = False if self.keyMap[TECLA_frente] == EVENT_down and self.keyMap[TECLA_traz] == EVENT_up: self.modelo.setY(self.modelo, ((self.velocidade * 2) * dt) * -1) if self.estaMovendo is False: self.modelo.loop("run") self.estaMovendo = True elif self.keyMap[TECLA_traz] == EVENT_down and self.keyMap[TECLA_frente] == EVENT_up: self.modelo.setY(self.modelo, (self.velocidade * dt)) if self.estaMovendo is False: self.modelo.loop("walk") self.estaMovendo = True elif self.estaMovendo: self.modelo.stop() self.modelo.pose("walk", 5) self.estaMovendo = False # movimentos usuario modelo if self.estaMovendo: self.cTrav.traverse(render) if self.ralphGroundHandler.getNumEntries() == 1: entry = self.ralphGroundHandler.getEntry(0) if entry.getIntoNode().getName() == "terrain": self.modelo.setZ(entry.getSurfacePoint(render).getZ()) else: self.modelo.setPos(startpos) return task.cont
class Personaje: # suelo SueloNulo = 0 SueloGenerico = 1 SueloEscalera = 2 SueloPendienteSubida = 3 SueloPendienteBajada = 4 # # estados EstadoNulo = 0 EstadoQuieto = 1 EstadoClaseDerivadaBase = 100 # parametros de estados ParamEstadoNulo = 0 def __init__(self, clase): # variables internas self._suelo = self.SueloNulo self._partes_actor = list() # [] | [nombre_parte, ...] self._estado_capa = [self.EstadoNulo, self.EstadoNulo] # [estado,estado] self._params_estado = Personaje.ParamEstadoNulo self._altura = 0.0 self._velocidad_lineal = LVector3() self._velocidad_angular = LVector3() # grados self._ajuste_altura = 0.0 # variables externas self.altitud_suelo = 0.0 self.altitud_agua = 0.0 self.contactos = None # |list [BulletContact,...] self.objetos_estados = dict() # {Estado:NodePath,...} # parametros self.directorio_recursos = "personajes" self.clase = clase self.prefijo_cuerpo_suelo = "cuerpo_suelo_" self.prefijo_cuerpo_personaje = "cuerpo_personaje_" self.rapidez_caminar = 1.0 # multiplicador self.rapidez_correr = 2.0 # multiplicador # referencias self.sistema = None self.input_mapper = None self.bullet_world = None # componentes self.cuerpo = None self.actor = None def iniciar(self, parent_node_path, bullet_world, partes=list()): log.info("iniciar") # sistema self.sistema = Sistema.obtener_instancia() # recursos ruta_dir = os.path.join(os.getcwd(), self.directorio_recursos, self.clase) if not os.path.exists(ruta_dir): raise Exception("no existe el directorio '%s'" % (ruta_dir)) archivos = [ archivo for archivo in os.listdir(ruta_dir) if archivo[-4:].lower() == ".egg" or archivo[-4:].lower() == ".bam" ] archivo_actor = "" dict_animaciones = dict() for archivo in archivos: if archivo[:-4] == "actor": archivo_actor = archivo else: dict_animaciones[archivo[:-4]] = Filename.fromOsSpecific( os.path.join(ruta_dir, archivo)) if archivo_actor == "": raise Exception( "no se encontró ningún archivo de actor (actor.[egg|bam]) en '%s'" % ruta_dir) # cuerpo self.bullet_world = bullet_world rb = self._generar_cuerpo_fisica() self.bullet_world.attach(rb) self.cuerpo = parent_node_path.attachNewNode(rb) self.cuerpo.setCollideMask(BitMask32.bit(2)) # actor self.actor = Actor( Filename.fromOsSpecific(os.path.join(ruta_dir, archivo_actor)), dict_animaciones) self.actor.reparentTo(self.cuerpo) self.actor.setZ(-self._ajuste_altura) # partes for parte in partes: self.actor.makeSubpart(parte[0], parte[1], parte[2]) self._partes_actor.append(parte[0]) # shader GestorShader.aplicar(self.actor, GestorShader.ClasePersonaje, 2) def terminar(self): log.info("terminar") self.sistema = None self.actor.delete() self.bullet_world.remove(self.cuerpo.node()) self.actor = None self.cuerpo = None self.input_mapper = None self.bullet_world = None def setPos(self, posicion): _posicion = Vec3(posicion[0], posicion[1], posicion[2] + self._ajuste_altura) self.cuerpo.setPos(_posicion) def chequear_estado(self, estado, idx_capa): if idx_capa == 1: # capa 1 return (self._estado_capa[1] & estado) == estado elif idx_capa == 0: # capa 0 return estado == self._estado_capa[0] def update(self, dt): if self._estado_capa[0] != Personaje.EstadoQuieto: # altitud suelo self.altitud_suelo = self.sistema.obtener_altitud_suelo( self.cuerpo.getPos()) # definir ambiente y suelo self._definir_suelo() # altura desde el suelo (se encuentra al final para ser evaluada en forma porterior) self._altura = self.cuerpo.getZ( ) - self.altitud_suelo - self._ajuste_altura #|0.5 # definir estados for idx_capa in range(len(self._estado_capa)): estado_nuevo = self._definir_estado(idx_capa) if self._estado_capa[idx_capa] != estado_nuevo: # procesar cambio de estado self._cambio_estado(idx_capa, self._estado_capa[idx_capa], estado_nuevo) self._estado_capa[idx_capa] = estado_nuevo # verificar cambio de parametros param_estado_nuevo = self.input_mapper.parametros if self._params_estado != param_estado_nuevo: # procesar cambio en parametros self._cambio_params(self._params_estado, param_estado_nuevo) self._params_estado = param_estado_nuevo # procesar estados self._procesar_estados(dt) # contactos self._procesar_contactos() def obtener_info(self): info = "Personaje:BulletRigidBodyNode vl=%s va=%s\n" % ( str(self.cuerpo.node().getLinearVelocity()), str(self.cuerpo.node().getAngularVelocity())) info += "velocidad_lineal=%s velocidad_angular=%s\n" % (str( self._velocidad_lineal), str(self._velocidad_angular)) info += "posicion=%s altitud_suelo=%s altura=%s\n" % (str( self.cuerpo.getPos()), str(self.altitud_suelo), str(self._altura)) info += "ambiente=? suelo=%s\n" % (str(self._suelo)) info += "estado=%s params=%s\n" % (str( self._estado_capa), str(self._params_estado)) if self.contactos: info += "contactos=%s\n" % (str( [c.getNode1().getName() for c in self.contactos])) if len(self.objetos_estados) > 0: info += "objetos_estados=%s\n" % ("; ".join([ "%i %s" % (e, np.getName()) for e, np in self.objetos_estados.items() ])) return info def _definir_suelo(self): if self._suelo != Personaje.SueloNulo and self._altura > 0.5: self._suelo = Personaje.SueloNulo elif self._suelo == Personaje.SueloNulo and self._altura < 0.05: self._suelo = Personaje.SueloGenerico def _generar_cuerpo_fisica(self): log.warning("_generar_cuerpo_fisica no implementado (%s)" % self.clase) return None def _definir_estado(self, idx_capa): log.warning("_definir_estado no implementado (%s)" % self.clase) pass def _cambio_estado(self, idx_capa, estado_previo, estado_nuevo): log.warning("_cambio_estado no implementado (%s)" % self.clase) def _cambio_params(self, params_previos, params_nuevos): log.warning("_cambio_params no implementado (%s)" % self.clase) pass def _procesar_estados(self, dt): log.warning("_procesar_estados no implementado (%s)" % self.clase) pass def _procesar_contactos(self): log.warning("_procesar_contactos no implementado (%s)" % self.clase) pass def _contacto_iniciado(self, contacto): log.debug("_contacto_iniciado %s %s" % (self.clase, contacto.getNode1().getName())) log.warning("_contacto_iniciado no implementado (%s)" % self.clase) pass def _contacto_finalizado(self, contacto): log.debug("_contacto_finalizado %s %s" % (self.clase, contacto.getNode1().getName())) log.warning("_contacto_finalizado no implementado (%s)" % self.clase) pass def _agarrar(self): log.warning("_agarrar no implementado (%s)" % self.clase) return False def _soltar(self): log.warning("_soltar no implementado (%s)" % self.clase) return False def _usar(self, nodo_objeto): log.warning("_usar no implementado (%s)" % self.clase) return False
class DistributedPartyFireworksActivity(DistributedPartyActivity, FireworkShowMixin): notify = directNotify.newCategory('DistributedPartyFireworksActivity') def __init__(self, cr): DistributedPartyFireworksActivity.notify.debug('__init__') DistributedPartyActivity.__init__(self, cr, ActivityIds.PartyFireworks, ActivityTypes.HostInitiated, wantLever=True) FireworkShowMixin.__init__(self, restorePlaygroundMusic=True, startDelay=FireworksPostLaunchDelay) def setEventId(self, eventId): DistributedPartyFireworksActivity.notify.debug( 'setEventId( %s )' % FireworkShows.getString(eventId)) self.eventId = eventId def setShowStyle(self, showStyle): DistributedPartyFireworksActivity.notify.debug('setShowStyle( %d )' % showStyle) self.showStyle = showStyle def setSongId(self, songId): self.songId = songId def load(self): DistributedPartyFireworksActivity.notify.debug('load') DistributedPartyActivity.load(self) self.eventId = PartyGlobals.FireworkShows.Summer self.launchPadModel = loader.loadModel( 'phase_13/models/parties/launchPad') self.launchPadModel.setH(90.0) self.launchPadModel.setPos(0.0, -18.0, 0.0) self.launchPadModel.reparentTo(self.root) railingsCollection = self.launchPadModel.findAllMatches( '**/launchPad_mesh/*railing*') for i in xrange(railingsCollection.getNumPaths()): railingsCollection[i].setAttrib( AlphaTestAttrib.make(RenderAttrib.MGreater, 0.75)) leverLocator = self.launchPadModel.find('**/RocketLever_locator') self.lever.setPosHpr(Vec3.zero(), Vec3.zero()) self.lever.reparentTo(leverLocator) self.toonPullingLeverInterval = None self.sign.reparentTo( self.launchPadModel.find('**/launchPad_sign_locator')) self.rocketActor = Actor( 'phase_13/models/parties/rocket_model', {'launch': 'phase_13/models/parties/rocket_launch'}) rocketLocator = self.launchPadModel.find('**/rocket_locator') self.rocketActor.reparentTo(rocketLocator) self.rocketActor.node().setBound(OmniBoundingVolume()) self.rocketActor.node().setFinal(True) effectsLocator = self.rocketActor.find('**/joint1') self.rocketExplosionEffect = RocketExplosion(effectsLocator, rocketLocator) self.rocketParticleSeq = None self.launchSound = base.loader.loadSfx( 'phase_13/audio/sfx/rocket_launch.ogg') self.activityFSM = FireworksActivityFSM(self) self.activityFSM.request('Idle') return def unload(self): DistributedPartyFireworksActivity.notify.debug('unload') taskMgr.remove(self.taskName('delayedStartShow')) if self.rocketParticleSeq: self.rocketParticleSeq.pause() self.rocketParticleSeq = None self.launchPadModel.removeNode() del self.launchPadModel del self.toonPullingLeverInterval self.rocketActor.delete() self.rocketExplosionEffect.destroy() self.activityFSM.request('Disabled') del self.rocketActor del self.launchSound del self.activityFSM del self.eventId del self.showStyle DistributedPartyActivity.unload(self) return def _leverPulled(self, collEntry): DistributedPartyFireworksActivity.notify.debug('_leverPulled') hostPulledLever = DistributedPartyActivity._leverPulled( self, collEntry) if self.activityFSM.getCurrentOrNextState() == 'Active': self.showMessage(TTLocalizer.PartyFireworksAlreadyActive) elif self.activityFSM.getCurrentOrNextState() == 'Disabled': self.showMessage(TTLocalizer.PartyFireworksAlreadyDone) elif self.activityFSM.getCurrentOrNextState() == 'Idle': if hostPulledLever: base.cr.playGame.getPlace().fsm.request('activity') self.toonPullingLeverInterval = self.getToonPullingLeverInterval( base.localAvatar) self.toonPullingLeverInterval.append( Func(self.d_toonJoinRequest)) self.toonPullingLeverInterval.append( Func(base.cr.playGame.getPlace().fsm.request, 'walk')) self.toonPullingLeverInterval.start() else: self.showMessage(TTLocalizer.PartyOnlyHostLeverPull) def setState(self, newState, timestamp): DistributedPartyFireworksActivity.notify.debug( 'setState( newState=%s, ... )' % newState) DistributedPartyActivity.setState(self, newState, timestamp) if newState == 'Active': self.activityFSM.request(newState, timestamp) else: self.activityFSM.request(newState) def startIdle(self): DistributedPartyFireworksActivity.notify.debug('startIdle') def finishIdle(self): DistributedPartyFireworksActivity.notify.debug('finishIdle') def startActive(self, showStartTimestamp): DistributedPartyFireworksActivity.notify.debug('startActive') messenger.send(FireworksStartedEvent) timeSinceStart = globalClockDelta.localElapsedTime(showStartTimestamp) if timeSinceStart > self.rocketActor.getDuration('launch'): self.rocketActor.hide() if timeSinceStart < 60: self.startShow(self.eventId, self.showStyle, self.songId, showStartTimestamp) else: self.rocketActor.play('launch') self.rocketParticleSeq = Sequence( Wait(RocketSoundDelay), Func(base.playSfx, self.launchSound), Func(self.rocketExplosionEffect.start), Wait(RocketDirectionDelay), LerpHprInterval(self.rocketActor, 4.0, Vec3(0, 0, -60)), Func(self.rocketExplosionEffect.end), Func(self.rocketActor.hide)) self.rocketParticleSeq.start() taskMgr.doMethodLater(FireworksPostLaunchDelay, self.startShow, self.taskName('delayedStartShow'), extraArgs=[ self.eventId, self.showStyle, self.songId, showStartTimestamp, self.root ]) def finishActive(self): self.rocketParticleSeq = None DistributedPartyFireworksActivity.notify.debug('finishActive') messenger.send(FireworksFinishedEvent) taskMgr.remove(self.taskName('delayedStartShow')) FireworkShowMixin.disable(self) return def startDisabled(self): DistributedPartyFireworksActivity.notify.debug('startDisabled') if not self.rocketActor.isEmpty(): self.rocketActor.hide() def finishDisabled(self): DistributedPartyFireworksActivity.notify.debug('finishDisabled') def handleToonDisabled(self, toonId): self.notify.warning('handleToonDisabled no implementation yet')
class Digger(enemy.Enemy): def __init__(self, mainRef): enemy.Enemy.__init__(self, mainRef, enemy.koboldSkirmisher) # Change to correct kobold self.holeModel = None def initAttributes(self, attributes): super(Digger, self).initAttributes(attributes) rangeMultiplier = 1.5 self.combatRange *= rangeMultiplier self.perceptionRange *= rangeMultiplier def loadEnemyModel(self, modelName): modelPrefix = 'models/digger-' self.enemyModel = Actor(modelPrefix + 'model', { 'attack1':modelPrefix+'attack1', 'attack2':modelPrefix+'attack2', 'attack3':modelPrefix+'attack3', 'attack4':modelPrefix+'attack4', 'special-attack':modelPrefix+'special-attack', 'pursue':modelPrefix+'pursue', 'pursue-to-idle':modelPrefix+'pursue-stop', 'idle-walk':modelPrefix+'idle-walk', 'idle-walk-to-pursue':modelPrefix+'idle-walk-to-pursue', 'idle-walk-to-dig':modelPrefix+'idle-walk-to-dig', 'idle-walk-to-dig-to-sleep':modelPrefix+'idle-walk-to-dig-to-sleep', 'hit1':modelPrefix+'hit1', 'hit2':modelPrefix+'hit2', 'death1':modelPrefix+'death1', 'death2':modelPrefix+'death2', 'death3':modelPrefix+'death3' }) self.enemyModel.reparentTo(self.enemyNode) self.enemyNode.setPos(Point3.zero()) self.enemyNode.setDepthOffset(-1) def enterIdle(self): #print 'enemy enterIdle' #idleTime = 60.0 * 3.0 stopEnemy = self.enemyModel.actorInterval('pursue-to-idle', startFrame=0, endFrame=12) idleEnemy = self.enemyModel.actorInterval('idle-walk-to-dig', startFrame=0, endFrame=60) digHole = Parallel(Func(self.createHole), self.enemyModel.actorInterval('idle-walk-to-dig-to-sleep', startFrame=0, endFrame=120)) #hideEnemy = Func(self.enemyNode.hide) #suicide = Func(self.suicide) self.stopSequence = Sequence(stopEnemy, idleEnemy, digHole) self.stopSequence.start() self.isSleeping = True #taskMgr.doMethodLater(3, self.showEnemy, 'showEnemyTask') def showEnemy(self, task): if self.state == 'Idle': self.enemyNode.show() return task.done def enterPursue(self): #print('enemy enterPursue') loopWalkEnemy = Func(self.enemyModel.loop, 'pursue', fromFrame=0, toFrame=24) # Only awake enemy if it comes from idle if self.isSleeping: self.isSleeping = False awakeEnemy = self.enemyModel.actorInterval('idle-walk-to-pursue', startFrame=0, endFrame=24) self.awakeSequence = Sequence(awakeEnemy, loopWalkEnemy, Func(self.pursuePlayer)) else: self.awakeSequence = Sequence(loopWalkEnemy, Func(self.pursuePlayer)) self.awakeSequence.start() def enterDeath(self): #print('enemy enterDeath') self.enemyAIBehaviors.removeAi('all') randomDeathAnim = 'death' + str(utils.getDX(3)) self.enemyModel.play(randomDeathAnim, fromFrame=0, toFrame=12) def playAttackAnimation(self): randomAttackAnim = 'attack' + str(utils.getD4()) self.enemyModel.play(randomAttackAnim, fromFrame=0, toFrame=12) def playHitAnimation(self): randomHitAnim = 'hit' + str(utils.getDX(2)) self.enemyModel.play(randomHitAnim, fromFrame=0, toFrame=12) def createHole(self): if self.holeModel == None: self.holeModel = Actor('models/hole-model', {'anim':'models/hole-anim'}) self.holeModel.reparentTo(self._mainRef.mainNode) self.holeModel.setPos(self.enemyNode, 0, -.1, 0) self.holeModel.play('anim', fromFrame=0, toFrame=120) removeHoleDelay = 12 taskMgr.doMethodLater(removeHoleDelay, self.removeHole, 'removeHoleTask') def removeHole(self, task): if self.holeModel != None: self.holeModel.cleanup() self.holeModel.delete() self.holeModel = None return task.done
class Enemy(FSM, Unit): # Declare private variables _enemyActive = False _removeCorpseDelay = 2 # seconds before corpse is cleaned def __init__(self, mainRef, attributes): print("Enemy instantiated") Unit.__init__(self) FSM.__init__(self, 'playerFSM') self._mainRef = mainRef self._playerRef = mainRef.player self._AIworldRef = mainRef.AIworld self._enemyListRef = mainRef.enemyList self._ddaHandlerRef = mainRef.DDAHandler self._stateHandlerRef = mainRef.stateHandler self._scenarioHandlerRef = mainRef.scenarioHandler #self.topEnemyNode = mainRef.mainNode.attachNewNode('topEnemyNode') self.initEnemyNode(mainRef.mainNode) utils.enemyDictionary[self.enemyNode.getName()] = self self.loadEnemyModel(attributes.modelName) self.initAttributes(attributes) self.initEnemyAi() self.initEnemyDDA() self.initEnemyCollisionHandlers() self.initEnemyCollisionSolids() #self.request('Idle') self.request('Disabled') # Start enemy updater task self.enemyUpdaterTask = taskMgr.add(self.enemyUpdater, 'enemyUpdaterTask') def initEnemyNode(self, parentNode): enemyName = 'enemy' + str(len(self._enemyListRef)) self.enemyNode = parentNode.attachNewNode(enemyName) self._enemyListRef.append(self) def loadEnemyModel(self, modelName): modelPrefix = 'models/' + modelName self.enemyModel = Actor(modelPrefix + '-model', { 'walk':modelPrefix+'-walk', 'attack':modelPrefix+'-attack', 'idle':modelPrefix+'-idle', 'awake':modelPrefix+'-awake', 'stop':modelPrefix+'-stop', 'hit':modelPrefix+'-hit', 'death1':modelPrefix+'-death1', 'death2':modelPrefix+'-death2' }) self.enemyModel.reparentTo(self.enemyNode) self.enemyNode.setPos(Point3.zero()) self.enemyNode.setDepthOffset(-1) def initAttributes(self, attributes): perceptionRangeMultiplier = 1.2 combatRangeMultiplier = .3 speedMultiplier = .1 self.strength = attributes.strength self.constitution = attributes.constitution self.dexterity = attributes.dexterity self.mass = attributes.mass self.movementSpeed = speedMultiplier * attributes.movementSpeed self.perceptionRange = perceptionRangeMultiplier * attributes.perceptionRange self.combatRange = combatRangeMultiplier * attributes.combatRange self.attackBonus = attributes.attackBonus self.damageBonus = attributes.damageBonus self.damageRange = attributes.damageRange self.initiativeBonus = attributes.initiativeBonus self.fixedHealthPoints = attributes.fixedHealthPoints self.armorClass = attributes.armorClass if attributes.startLevel > 1: for i in range(attributes.startLevel-1): self.increaseLevel() self.expAward = attributes.expAward self.initHealth() def initEnemyDDA(self): if self._scenarioHandlerRef.getHasDDA(): maxLevelDifference = self._ddaHandlerRef.maxLevelDifference # Level enemy up to player's level minus maxLevelDifference levelDifference = self._playerRef.level - self.level if levelDifference >= maxLevelDifference: for i in range (levelDifference-maxLevelDifference): self.increaseLevel() def getAttackBonus(self): modifier = self.getStrengthModifier() if self.getStrengthModifier() > self.getDexterityModifier() else self.getDexterityModifier() ab = self.attackBonus + (self.level / 2) + modifier# + utils.getD20() if self._scenarioHandlerRef.getHasDDA(): attackBonusModifier = self._ddaHandlerRef.attackBonusModifier if attackBonusModifier < 0: ab -= attackBonusModifier if ab < 1: ab = 1 return ab + utils.getD20() def initEnemyAi(self): self.enemyAI = AICharacter('enemy', self.enemyNode, self.mass, # Mass 0.1, # Movt force self.movementSpeed) # Max force self._AIworldRef.addAiChar(self.enemyAI) self.enemyAIBehaviors = self.enemyAI.getAiBehaviors() #self.enemyAIBehaviors.obstacleAvoidance(1.0) def initEnemyCollisionHandlers(self): self.groundHandler = CollisionHandlerQueue() self.collPusher = CollisionHandlerPusher() def initEnemyCollisionSolids(self): # Enemy ground ray groundRay = CollisionRay(0, 0, 2, 0, 0, -1) groundColl = CollisionNode('enemyGroundRay') groundColl.addSolid(groundRay) groundColl.setIntoCollideMask(BitMask32.allOff()) groundColl.setFromCollideMask(BitMask32.bit(1)) self.groundRayNode = self.enemyNode.attachNewNode(groundColl) #self.groundRayNode.show() base.cTrav.addCollider(self.groundRayNode, self.groundHandler) # Enemy collision sphere collSphereNode = CollisionNode('enemyCollSphere') collSphere = CollisionSphere(0, 0, 0.1, 0.2) collSphereNode.addSolid(collSphere) collSphereNode.setIntoCollideMask(BitMask32.allOff()) collSphereNode.setFromCollideMask(BitMask32.bit(2)) self.sphereNode = self.enemyNode.attachNewNode(collSphereNode) #sphereNode.show() base.cTrav.addCollider(self.sphereNode, self.collPusher) self.collPusher.addCollider(self.sphereNode, self.enemyNode) # Enemy picker collision sphere pickerSphereCollNode = CollisionNode(self.enemyNode.getName()) pickerCollSphere = CollisionSphere(0, 0, 0, 0.5) pickerSphereCollNode.addSolid(pickerCollSphere) pickerSphereCollNode.setFromCollideMask(BitMask32.allOff()) pickerSphereCollNode.setIntoCollideMask(BitMask32.bit(1)) self.pickerNode = self.enemyNode.attachNewNode(pickerSphereCollNode) #sphereNodePath.show() # Enemy attack collision sphere attackCollSphereNode = CollisionNode(self.enemyNode.getName()+'atkSph') attackCollSphere = CollisionSphere(0, 0, 0.1, 0.15) attackCollSphereNode.addSolid(attackCollSphere) attackCollSphereNode.setIntoCollideMask(BitMask32.bit(3)) attackCollSphereNode.setFromCollideMask(BitMask32.allOff()) attackSphereNode = self.enemyNode.attachNewNode(attackCollSphereNode) #attackSphereNode.show() def slowMovementByPercentage(self, percentage=30, slowDuration=20): #print self.enemyNode.getName(), ' slowed by ', percentage, ' %' oldSpeed = self.movementSpeed newSpeed = ((100.0 - percentage) / 100.0) * oldSpeed if newSpeed < 1.0: newSpeed = 1.0 self.movementSpeed = newSpeed taskMgr.doMethodLater(slowDuration, self.removeSlowMovement, 'removeSlowMovementTask', extraArgs=[oldSpeed], appendTask=True) def removeSlowMovement(self, oldSpeed, task): self.movementSpeed = oldSpeed return task.done def checkGroundCollisions(self): if self.groundHandler.getNumEntries() > 0: self.groundHandler.sortEntries() entries = [] for i in range(self.groundHandler.getNumEntries()): entry = self.groundHandler.getEntry(i) #print('entry:', entry) entries.append(entry) entries.sort(lambda x,y: cmp(y.getSurfacePoint(render).getZ(), x.getSurfacePoint(render).getZ())) for i in range(len(entries)): if entries[i].getIntoNode().getName()[:6] == 'ground': #print('entryFound:', entries[0]) newZ = entries[i].getSurfacePoint(base.render).getZ() self.enemyNode.setZ(newZ) #print('enemyZ:', newZ) break; def enemyUpdater(self, task): if self._stateHandlerRef.state != self._stateHandlerRef.PLAY or not self._enemyActive: self.enemyModel.stop() # Do not do anything when paused return task.cont self.checkGroundCollisions() if self.getIsDead(): self.onDeath() return task.done if self._playerRef.getIsDead(): return task.cont playerPos = self._playerRef.playerNode.getPos() enemyPos = self.enemyNode.getPos() # If player is within enemy perception range if utils.getIsInRange(playerPos, enemyPos, self.perceptionRange): # If enemy is not doing anything if self.state == 'Idle': # Start pursuing if self.state != 'Pursue': self.request('Pursue') # If enemy is already pursueing elif self.state == 'Pursue': # If player is within combat range if utils.getIsInRange(playerPos, enemyPos, self.combatRange): #print 'enemy go to combat' # Go to combat if self.state != 'Combat': self.request('Combat') # If enemy is already in combat elif self.state == 'Combat': # If player has moved out of combat range if not utils.getIsInRange(playerPos, enemyPos, self.combatRange): # Start pursuing if self.state != 'Pursue': self.request('Pursue') # If enemy is disabled elif self.state == 'Disabled': if self.enemyAI.getMaxForce() != self.movementSpeed: self.enemyAI.setMaxForce(self.movementSpeed) # If player is not within perception range else: if self.state != 'Idle': self.request('Idle') return task.cont def pursuePlayer(self): taskMgr.add(self.pursue, 'pursueTask') def pursue(self, task): if self.state == 'Pursue' and not self.getIsDead(): pitchRoll = self.enemyNode.getP(), self.enemyNode.getR() self.enemyNode.headsUp(self._playerRef.playerNode) self.enemyNode.setHpr(self.enemyNode.getH()-180, *pitchRoll) speed = -self.movementSpeed * globalClock.getDt() self.enemyNode.setFluidPos(self.enemyNode, 0, speed, 0) return task.cont else: task.done def enterIdle(self): #print 'enemy enterIdle' stopEnemy = self.enemyModel.actorInterval('stop', loop=0) idleEnemy = self.enemyModel.actorInterval('idle', startFrame=0, endFrame=1, loop=0) self.stopSequence = Sequence(stopEnemy, idleEnemy) self.stopSequence.start() self.isSleeping = True def exitIdle(self): #print('enemy exitIdle') self.stopSequence.finish() def enterPursue(self): #print('enemy enterPursue') loopWalkEnemy = Func(self.enemyModel.loop, 'walk', fromFrame=0, toFrame=12) # Only awake enemy if it comes from idle if self.isSleeping: self.isSleeping = False awakeEnemy = self.enemyModel.actorInterval('awake', loop=0) self.awakeSequence = Sequence(awakeEnemy, loopWalkEnemy, Func(self.pursuePlayer)) else: self.awakeSequence = Sequence(loopWalkEnemy, Func(self.pursuePlayer)) self.awakeSequence.start() def exitPursue(self): #print('enemy exitPursue') self.awakeSequence.finish() def enterCombat(self): #print('enemy enterCombat') self.enemyModel.stop() attackDelay = self.getInitiativeRoll() self.attackTask = taskMgr.doMethodLater(attackDelay, self.attackPlayer, 'attackPlayerTask') def enterDisabled(self): #print 'enterDisable' pass def exitDisabled(self): #print 'exitDisable' pass def exitCombat(self): #print('enemy exitCombat') taskMgr.remove(self.attackTask) def enterDeath(self): #print('enemy enterDeath') self.enemyAIBehaviors.removeAi('all') randomDeathAnim = 'death' + str(utils.getD2()) self.enemyModel.play(randomDeathAnim) def attack(self, other): if not self.getIsDead() and not other.getIsDead(): if self.getAttackBonus() >= other.getArmorClass(): dmg = self.getDamageBonus() #print(self.getName(), ' damaged ', other.getName(), ' for ', dmg, ' damage') other.receiveDamage(dmg) return 2 # Returns 2 when self damages other return 1 # Returns 1 when self attacks other, but misses return 0 # Returns 0 when either self or other is dead def attackPlayer(self, task): if self._stateHandlerRef.state != self._stateHandlerRef.PLAY or not self._enemyActive: # Do not do anything when paused return task.again if self._playerRef.getIsDead(): print('player is already dead') self.request('Idle') return task.done elif self.getIsDead(): return task.done else: #print('Attack player!') # Make sure enemy is facing player when attacking pitchRoll = self.enemyNode.getP(), self.enemyNode.getR() self.enemyNode.headsUp(self._playerRef.playerNode) self.enemyNode.setHpr(self.enemyNode.getH()-180, *pitchRoll) attacked = self.attack(self._playerRef) if attacked != 0: self.playAttackAnimation() if attacked == 2: self._playerRef.playerModel.play('hit') return task.again def playAttackAnimation(self): self.enemyModel.play('attack', fromFrame=0, toFrame=12) def playHitAnimation(self): self.enemyModel.play('hit') def moveEnemy(self, x, y): self.enemyNode.setPos(x, y, .01) def handleHealthGlobe(self): global dropChanceFactor global dropChance global maxDropChance # if we drop, create health goblet chance = dropChanceFactor + dropChance if self._scenarioHandlerRef.getHasDDA(): chance *= self._ddaHandlerRef.healthGobletModifier if utils.getD100() <= chance: HealthGoblet(self._mainRef, self) print 'dropping health goblet' # Otherwise, increase dropChance else: if dropChance+dropChanceFactor <= maxDropChance: dropChance += dropChanceFactor def suicide(self): print('suicide: ', self) # Remove AI behavior self.enemyAIBehaviors.removeAi('all') # Remove enemy picker sphere (handlerQueue) self.pickerNode.removeNode() taskMgr.add(self.removeCorpse, 'removeCorpseTask') def onDeath(self): if self.getIsDead(): # Remove AI behavior self.enemyAIBehaviors.removeAi('all') # Award the player exp self._playerRef.receiveEXP(self.expAward) # Remove enemy picker sphere (handlerQueue) self.pickerNode.removeNode() # Change state self.request('Death') # Increase DDA death count if self._scenarioHandlerRef.getHasDDA(): self._ddaHandlerRef.enemyDeathCount += 1 # Handle health globe self.handleHealthGlobe() # Remove enemy corpse and clean up taskMgr.doMethodLater(self._removeCorpseDelay, self.removeCorpse, 'removeCorpseTask') def removeCorpse(self, task): # Remove enemy collision sphere (pusher) self.sphereNode.removeNode() # Stop the collision pusher self.collPusher = None # Remove enemy from enemyList self._enemyListRef.remove(self) # Cleanup the enemy model self.enemyModel.cleanup() self.enemyModel.delete() # Cleanup FSM self.cleanup() # Remove the enemy node self.enemyNode.removeNode() #self.topEnemyNode.removeNode() # Remove enemy updater tasks taskMgr.remove(self.enemyUpdaterTask) # Remove the passive regeneration task (from Unit class) self.removePassiveRegeneration() # Remove references self._mainRef = None self._playerRef = None self._AIworldRef = None self._enemyListRef = None self._stateHandlerRef = None return task.done
class Cannon: def __init__(self, cycle, mount, audio3D): self.cycle = cycle self.name = "Virtue X-A9 Equalizer" self.audio3D = audio3D self.actor = Actor("../Models/CannonActor.egg") self.model = loader.loadModel("../Models/Cannon.bam") self.actor.reparentTo(mount) self.model.reparentTo(self.actor) self.flashModel = loader.loadModel("../Models/LaserFlash.bam") self.projModel = loader.loadModel("../Models/LaserProj.bam") self.refNP = self.cycle.trgtrMount.attachNewNode("CannonRefNP") self.muzzle = self.actor.exposeJoint(None, "modelRoot", "Muzzle") self.audio3D = audio3D self.fireSfx = self.audio3D.loadSfx("../Sound/LaserShot.wav") self.audio3D.attachSoundToObject(self.fireSfx, self.muzzle) reloadTime = 1.5 self.damage = 75 self.energyCost = 5 self.blastR = 10 self.flashLerp = LerpScaleInterval(self.flashModel, reloadTime * .1, Point3(2, 2, 2), Point3(.2, .2, .2)) self.firePar = Parallel(Func(self.checkForHit), Func(self.setEffects), self.flashLerp) self.fireSeq = Sequence(self.firePar, Func(self.clearEffects), Wait(reloadTime * .9)) def fire(self): if (self.fireSeq.isPlaying() == False): self.fireSeq.start() self.fireSfx.play() self.cycle.energy -= self.energyCost return def setEffects(self): self.flashModel.reparentTo(self.muzzle) self.projModel.reparentTo(self.muzzle) self.projModel.lookAt(self.refNP.getPos(self.muzzle)) self.projModel.setSy( trueDist(Point3(0, 0, 0), self.refNP.getPos(self.muzzle)) * 2) return def clearEffects(self): self.flashModel.detachNode() self.projModel.detachNode() return def checkForHit(self): self.cycle.trgtrCTrav.traverse(render) if (self.cycle.trgtrCHan.getNumEntries() > 0): self.cycle.trgtrCHan.sortEntries() entry = self.cycle.trgtrCHan.getEntry(0) # If collisions were detected sort them nearest to far and pull out the first one. colPoint = entry.getSurfacePoint(render) self.refNP.setPos(render, colPoint) # Get the collision point and the range to the collision. boom = Boom(colPoint, self.blastR, self.damage, self.audio3D) # Create an explosion at the collision point. else: self.refNP.setPos(self.cycle.trgtrCNP, 0, 300, 0) boom = Boom(self.refNP.getPos(render), self.blastR, self.damage, self.audio3D) # If no collision was detected, create a boom at a distant point. def destroy(self): self.actor.delete() self.model.removeNode() self.flashModel.removeNode() self.projModel.removeNode() self.refNP.removeNode() self.cycle = None self.flashLerp = None self.firePar = None self.fireSeq = None self.audio3D.detachSound(self.fireSfx) return
class DistributedPartyJukeboxActivityBase(DistributedPartyActivity): __module__ = __name__ notify = directNotify.newCategory('DistributedPartyJukeboxActivityBase') def __init__(self, cr, actId, phaseToMusicData): DistributedPartyActivity.__init__(self, cr, actId, ActivityTypes.Continuous) self.phaseToMusicData = phaseToMusicData self.jukebox = None self.gui = None self.tunes = [] self.music = None self.currentSongData = None self.localQueuedSongInfo = None self.localQueuedSongListItem = None return def generateInit(self): self.gui = JukeboxGui(self.phaseToMusicData) def load(self): DistributedPartyActivity.load(self) self.jukebox = Actor( 'phase_13/models/parties/jukebox_model', {'dance': 'phase_13/models/parties/jukebox_dance'}) self.jukebox.reparentTo(self.root) self.collNode = CollisionNode(self.getCollisionName()) self.collNode.setCollideMask(ToontownGlobals.CameraBitmask | ToontownGlobals.WallBitmask) collTube = CollisionTube(0, 0, 0, 0.0, 0.0, 4.25, 2.25) collTube.setTangible(1) self.collNode.addSolid(collTube) self.collNodePath = self.jukebox.attachNewNode(self.collNode) self.sign.setPos(-5.0, 0, 0) self.activate() def unload(self): DistributedPartyActivity.unload(self) self.gui.unload() if self.music is not None: self.music.stop() self.jukebox.stop() self.jukebox.delete() self.jukebox = None self.ignoreAll() return def getCollisionName(self): return self.uniqueName('jukeboxCollision') def activate(self): self.accept('enter' + self.getCollisionName(), self.__handleEnterCollision) def __handleEnterCollision(self, collisionEntry): if base.cr.playGame.getPlace().fsm.getCurrentState().getName( ) == 'walk': base.cr.playGame.getPlace().fsm.request('activity') self.d_toonJoinRequest() def joinRequestDenied(self, reason): DistributedPartyActivity.joinRequestDenied(self, reason) self.showMessage(TTLocalizer.PartyJukeboxOccupied) def handleToonJoined(self, toonId): toon = base.cr.doId2do.get(toonId) if toon: self.jukebox.lookAt(base.cr.doId2do[toonId]) self.jukebox.setHpr(self.jukebox.getH() + 180.0, 0, 0) if toonId == base.localAvatar.doId: self.__localUseJukebox() def handleToonExited(self, toonId): if toonId == base.localAvatar.doId and self.gui.isLoaded(): self.__deactivateGui() def handleToonDisabled(self, toonId): self.notify.warning('handleToonDisabled no implementation yet') def __localUseJukebox(self): base.localAvatar.disableAvatarControls() base.localAvatar.stopPosHprBroadcast() self.__activateGui() self.accept(JukeboxGui.CLOSE_EVENT, self.__handleGuiClose) taskMgr.doMethodLater(0.5, self.__localToonWillExitTask, self.uniqueName('toonWillExitJukeboxOnTimeout'), extraArgs=None) self.accept(JukeboxGui.ADD_SONG_CLICK_EVENT, self.__handleQueueSong) if self.isUserHost(): self.accept(JukeboxGui.MOVE_TO_TOP_CLICK_EVENT, self.__handleMoveSongToTop) return def __localToonWillExitTask(self, task): self.localToonExiting() return Task.done def __activateGui(self): self.gui.enable(timer=JUKEBOX_TIMEOUT) self.gui.disableAddSongButton() if self.currentSongData is not None: self.gui.setSongCurrentlyPlaying(self.currentSongData[0], self.currentSongData[1]) self.d_queuedSongsRequest() return def __deactivateGui(self): self.ignore(JukeboxGui.CLOSE_EVENT) self.ignore(JukeboxGui.SONG_SELECT_EVENT) self.ignore(JukeboxGui.MOVE_TO_TOP_CLICK_EVENT) base.cr.playGame.getPlace().setState('walk') base.localAvatar.startPosHprBroadcast() base.localAvatar.enableAvatarControls() self.gui.unload() self.__localClearQueuedSong() def isUserHost(self): return self.party.partyInfo.hostId == base.localAvatar.doId def d_queuedSongsRequest(self): self.sendUpdate('queuedSongsRequest') def queuedSongsResponse(self, songInfoList, index): if self.gui.isLoaded(): for i in range(len(songInfoList)): songInfo = songInfoList[i] self.__addSongToQueue(songInfo, isLocalQueue=index >= 0 and i == index) self.gui.enableAddSongButton() def __handleGuiClose(self): self.__deactivateGui() self.d_toonExitDemand() def __handleQueueSong(self, name, values): self.d_setNextSong(values[0], values[1]) def d_setNextSong(self, phase, filename): self.sendUpdate('setNextSong', [(phase, filename)]) def setSongInQueue(self, songInfo): if self.gui.isLoaded(): phase = sanitizePhase(songInfo[0]) filename = songInfo[1] data = self.getMusicData(phase, filename) if data: if self.localQueuedSongListItem is not None: self.localQueuedSongListItem['text'] = data[0] else: self.__addSongToQueue(songInfo, isLocalQueue=True) return def __addSongToQueue(self, songInfo, isLocalQueue=False): if isLocalQueue: isHost = self.isUserHost() data = self.getMusicData(sanitizePhase(songInfo[0]), songInfo[1]) if data: listItem = self.gui.addSongToQueue(data[0], highlight=isLocalQueue, moveToTopButton=isHost) self.localQueuedSongInfo = isLocalQueue and songInfo self.localQueuedSongListItem = listItem def __localClearQueuedSong(self): self.localQueuedSongInfo = None self.localQueuedSongListItem = None return def __play(self, phase, filename, length): self.music = base.loadMusic((MUSIC_PATH + '%s') % (phase, filename)) if self.music: if self.__checkPartyValidity() and hasattr( base.cr.playGame.getPlace().loader, 'music') and base.cr.playGame.getPlace().loader.music: base.cr.playGame.getPlace().loader.music.stop() base.resetMusic.play() self.music.setTime(0.0) self.music.setLoopCount(getMusicRepeatTimes(length)) self.music.play() jukeboxAnimControl = self.jukebox.getAnimControl('dance') if not jukeboxAnimControl.isPlaying(): self.jukebox.loop('dance') self.currentSongData = (phase, filename) def __stop(self): self.jukebox.stop() self.currentSongData = None if self.music: self.music.stop() if self.gui.isLoaded(): self.gui.clearSongCurrentlyPlaying() return def setSongPlaying(self, songInfo, toonId): phase = sanitizePhase(songInfo[0]) filename = songInfo[1] if not filename: self.__stop() return data = self.getMusicData(phase, filename) if data: self.__play(phase, filename, data[1]) self.setSignNote(data[0]) if self.gui.isLoaded(): item = self.gui.popSongFromQueue() self.gui.setSongCurrentlyPlaying(phase, filename) if item == self.localQueuedSongListItem: self.__localClearQueuedSong() if toonId == localAvatar.doId: localAvatar.setSystemMessage(0, TTLocalizer.PartyJukeboxNowPlaying) def __handleMoveSongToTop(self): if self.isUserHost() and self.localQueuedSongListItem is not None: self.d_moveHostSongToTopRequest() return def d_moveHostSongToTopRequest(self): self.notify.debug('d_moveHostSongToTopRequest') self.sendUpdate('moveHostSongToTopRequest') def moveHostSongToTop(self): self.notify.debug('moveHostSongToTop') if self.gui.isLoaded(): self.gui.pushQueuedItemToTop(self.localQueuedSongListItem) def getMusicData(self, phase, filename): data = [] phase = sanitizePhase(phase) phase = self.phaseToMusicData.get(phase) if phase: data = phase.get(filename, []) return data def __checkPartyValidity(self): if hasattr(base.cr.playGame, 'getPlace') and base.cr.playGame.getPlace() and hasattr( base.cr.playGame.getPlace(), 'loader') and base.cr.playGame.getPlace().loader: return True else: return False
class Cog(): def __init__(self): #Define variables to keep track of the current head, body, and department self.currentBody = 0 self.currentHead = 0 self.currentDept = 0 #Define a boolean to determine if the cog is in view self.isInView = False #Establish current file path location self.currentDirectory = os.path.abspath(sys.path[0]) self.pandaDirectory = Filename.fromOsSpecific( self.currentDirectory).getFullpath() #Create the cog head and body (mainly to allow it to be #destroyed later in replacement of the proper body) self.cog = Actor( self.pandaDirectory + '/resources/cogs/models/tt_a_ene_cga_zero.bam', { 'neutral': (self.pandaDirectory + '/resources/cogs/animations/tt_a_ene_cga_neutral.bam'), 'walk': (self.pandaDirectory + '/resources/cogs/animations/tt_a_ene_cga_walk.bam') }) self.head = loader.loadModel(self.pandaDirectory + '/resources/cogs/models/suitA-heads.bam') #Determine the body type and set the head self.determineBody() def determineBody(self): #Destroy the current body to unrender it self.cog.delete() self.cog.removeNode() #Determine the new cog body type if self.currentBody == 0: self.cog = Actor( self.pandaDirectory + '/resources/cogs/models/tt_a_ene_cga_zero.bam', { 'neutral': (self.pandaDirectory + '/resources/cogs/animations/tt_a_ene_cga_neutral.bam'), 'walk': (self.pandaDirectory + '/resources/cogs/animations/tt_a_ene_cga_walk.bam'), 'victory': (self.pandaDirectory + '/resources/cogs/animations/tt_a_ene_cga_victory.bam') }) elif self.currentBody == 1: self.cog = Actor( self.pandaDirectory + '/resources/cogs/models/tt_a_ene_cgb_zero.bam', { 'neutral': (self.pandaDirectory + '/resources/cogs/animations/tt_a_ene_cgb_neutral.bam'), 'walk': (self.pandaDirectory + '/resources/cogs/animations/tt_a_ene_cgb_walk.bam'), 'victory': (self.pandaDirectory + '/resources/cogs/animations/tt_a_ene_cgb_victory.bam') }) elif self.currentBody == 2: self.cog = Actor( self.pandaDirectory + '/resources/cogs/models/tt_a_ene_cgc_zero.bam', { 'neutral': (self.pandaDirectory + '/resources/cogs/animations/tt_a_ene_cgc_neutral.bam'), 'walk': (self.pandaDirectory + '/resources/cogs/animations/tt_a_ene_cgc_walk.bam'), 'victory': (self.pandaDirectory + '/resources/cogs/animations/tt_a_ene_cgc_victory.bam') }) else: raise Exception( 'UH OH! currentBody is not between 0 and 2! The value of currentBody is {}' .format(self.currentBody)) self.cog.reparentTo(render) if self.isInView: self.cog.loop('neutral') self.cog.setPos(6, 2, 0) self.cog.setHpr(180, 0, 0) else: self.resetCogPos() #Define the lerp for the cog's movement and animations self.walkIn = self.cog.posInterval(2, pos=(6, 2, 0)) self.walkOut = self.cog.posInterval(2, pos=(15, 2, 0)) self.turnToCam = self.cog.hprInterval(1, hpr=(180, 0, 0)) self.turnAway = self.cog.hprInterval(1, hpr=(270, 0, 0)) #Define the sequences #Note: If you want to loop an animation after a sequence plays, #you have to append the Func(self.actor.loop, 'animName') to the end of the function. self.enterView = Sequence(self.walkIn, self.turnToCam, Func(self.cog.loop, 'neutral')) self.exitView = Sequence(Func(self.cog.loop, 'walk'), self.turnAway, self.walkOut) #Call the other methods to reattach and reapply the head and department textures self.determineHead() self.determineDept() def determineHead(self): #Destroy the current head to unrender it self.head.removeNode() #Set up a boolean to make sure the cog is or isn't a flunky hasGlasses = False #Determine the cog head type if self.currentHead <= 12: self.headList = loader.loadModel( self.pandaDirectory + '/resources/cogs/models/suitA-heads.bam') if self.currentHead == 0: self.head = self.headList.find('**/backstabber') elif self.currentHead == 1: self.head = self.headList.find('**/bigcheese') elif self.currentHead == 2: self.head = self.headList.find('**/bigwig') elif self.currentHead == 3: self.head = self.headList.find('**/headhunter') elif self.currentHead == 4: self.head = self.headList.find('**/legaleagle') elif self.currentHead == 5: self.head = self.headList.find('**/numbercruncher') elif self.currentHead == 6: #name dropper self.head = self.headList.find('**/numbercruncher') self.head.setTexture(loader.loadTexture(self.pandaDirectory + \ '/resources/cogs/textures/name-dropper.jpg'), 1) elif self.currentHead == 7: self.head = self.headList.find('**/pennypincher') elif self.currentHead == 8: self.head = self.headList.find('**/yesman') elif self.currentHead == 9: #robber baron self.head = self.headList.find('**/yesman') self.head.setTexture(loader.loadTexture(self.pandaDirectory + \ '/resources/cogs/textures/robber-baron.jpg'), 1) elif self.currentHead == 10: self.head = self.headList.find('**/twoface') elif self.currentHead == 11: #mingler self.head = self.headList.find('**/twoface') self.head.setTexture(loader.loadTexture(self.pandaDirectory + \ '/resources/cogs/textures/mingler.jpg'), 1) else: #double talker self.head = self.headList.find('**/twoface') self.head.setTexture(loader.loadTexture(self.pandaDirectory + \ '/resources/cogs/textures/double-talker.jpg'), 1) elif self.currentHead <= 20: self.headList = loader.loadModel( self.pandaDirectory + '/resources/cogs/models/suitB-heads.bam') if self.currentHead == 13: self.head = self.headList.find('**/ambulancechaser') elif self.currentHead == 14: self.head = self.headList.find('**/beancounter') elif self.currentHead == 15: self.head = self.headList.find('**/loanshark') elif self.currentHead == 16: self.head = self.headList.find('**/movershaker') elif self.currentHead == 17: #bloodsucker self.head = self.headList.find('**/movershaker') self.head.setTexture(loader.loadTexture(self.pandaDirectory + \ '/resources/cogs/textures/blood-sucker.jpg'), 1) elif self.currentHead == 18: self.head = self.headList.find('**/pencilpusher') elif self.currentHead == 19: self.head = self.headList.find('**/telemarketer') else: #spin doctor self.head = self.headList.find('**/telemarketer') self.head.setTexture(loader.loadTexture(self.pandaDirectory + \ '/resources/cogs/textures/spin-doctor.jpg'), 1) elif self.currentHead <= 29: self.headList = loader.loadModel( self.pandaDirectory + '/resources/cogs/models/suitC-heads.bam') if self.currentHead == 21: #actually a short change self.head = self.headList.find('**/coldcaller') elif self.currentHead == 22: #cold caller needs to be recolored self.head = self.headList.find('**/coldcaller') self.head.setColor(0, 0, 255, 1) elif self.currentHead == 23: self.head = self.headList.find('**/flunky') hasGlasses = True elif self.currentHead == 24: #corporate raider self.head = self.headList.find('**/flunky') self.head.setTexture(loader.loadTexture(self.pandaDirectory + \ '/resources/cogs/textures/corporate-raider.jpg'), 1) elif self.currentHead == 25: self.head = self.headList.find('**/gladhander') elif self.currentHead == 26: self.head = self.headList.find('**/micromanager') elif self.currentHead == 27: self.head = self.headList.find('**/moneybags') elif self.currentHead == 28: self.head = self.headList.find('**/tightwad') else: #bottom feeder self.head = self.headList.find('**/tightwad') self.head.setTexture(loader.loadTexture(self.pandaDirectory + \ '/resources/cogs/textures/bottom-feeder.jpg'), 1) else: raise Exception( 'UH OH! currentHead is not between 0 and 31! The value of currentBody is {}' .format(self.currentHead)) #Attach the head to the model self.head.reparentTo(self.cog.find('**/def_head')) if hasGlasses: self.glasses = self.headList.find('**/glasses') self.glasses.reparentTo(self.head) def determineDept(self): #Determine the cog department and set the textures of the suit if self.currentDept == 0: self.cog.findAllMatches('**/torso').setTexture( loader.loadTexture(self.pandaDirectory + '/resources/cogs/textures/s_blazer.jpg'), 1) self.cog.findAllMatches('**/arms').setTexture( loader.loadTexture(self.pandaDirectory + '/resources/cogs/textures/s_sleeve.jpg'), 1) self.cog.findAllMatches('**/legs').setTexture( loader.loadTexture(self.pandaDirectory + '/resources/cogs/textures/s_leg.jpg'), 1) elif self.currentDept == 1: self.cog.findAllMatches('**/torso').setTexture( loader.loadTexture(self.pandaDirectory + '/resources/cogs/textures/m_blazer.jpg'), 1) self.cog.findAllMatches('**/arms').setTexture( loader.loadTexture(self.pandaDirectory + '/resources/cogs/textures/m_sleeve.jpg'), 1) self.cog.findAllMatches('**/legs').setTexture( loader.loadTexture(self.pandaDirectory + '/resources/cogs/textures/m_leg.jpg'), 1) elif self.currentDept == 2: self.cog.findAllMatches('**/torso').setTexture( loader.loadTexture(self.pandaDirectory + '/resources/cogs/textures/l_blazer.jpg'), 1) self.cog.findAllMatches('**/arms').setTexture( loader.loadTexture(self.pandaDirectory + '/resources/cogs/textures/l_sleeve.jpg'), 1) self.cog.findAllMatches('**/legs').setTexture( loader.loadTexture(self.pandaDirectory + '/resources/cogs/textures/l_leg.jpg'), 1) elif self.currentDept == 3: self.cog.findAllMatches('**/torso').setTexture( loader.loadTexture(self.pandaDirectory + '/resources/cogs/textures/c_blazer.jpg'), 1) self.cog.findAllMatches('**/arms').setTexture( loader.loadTexture(self.pandaDirectory + '/resources/cogs/textures/c_sleeve.jpg'), 1) self.cog.findAllMatches('**/legs').setTexture( loader.loadTexture(self.pandaDirectory + '/resources/cogs/textures/c_leg.jpg'), 1) elif self.currentDept == 4: self.cog.findAllMatches('**/torso').setTexture( loader.loadTexture( self.pandaDirectory + '/resources/cogs/textures/waiter_m_blazer.jpg'), 1) self.cog.findAllMatches('**/arms').setTexture( loader.loadTexture( self.pandaDirectory + '/resources/cogs/textures/waiter_m_sleeve.jpg'), 1) self.cog.findAllMatches('**/legs').setTexture( loader.loadTexture( self.pandaDirectory + '/resources/cogs/textures/waiter_m_leg.jpg'), 1) else: raise Exception( 'UH OH! currentDept is not between 0 and 4! The value of currentDept is {}' .format(self.currentDept)) def nextHead(self): #Keep the head variable within the range and switch to the next head if self.currentHead >= 29: self.currentHead = 0 else: self.currentHead += 1 self.determineHead() def previousHead(self): #Keep the head variable within the range and switch to the previous head if self.currentHead <= 0: self.currentHead = 29 else: self.currentHead -= 1 self.determineHead() def nextBody(self): #Keep the head variable within the range and switch to the next body if self.currentBody >= 2: self.currentBody = 0 else: self.currentBody += 1 self.determineBody() def previousBody(self): #Keep the head variable within the range and switch to the previous body if self.currentBody <= 0: self.currentBody = 2 else: self.currentBody -= 1 self.determineBody() def nextDept(self): #Keep the head variable within the range and switch to the next department if self.currentDept >= 4: self.currentDept = 0 else: self.currentDept += 1 self.determineDept() def previousDept(self): #Keep the head variable within the range and switch to the previous department if self.currentDept <= 0: self.currentDept = 4 else: self.currentDept -= 1 self.determineDept() def resetCogPos(self): #Sets the cog's position to its starting location outside the scene self.cog.loop('walk') self.cog.setHpr(90, 0, 0) self.cog.setPos(15, 2, 0) def enterScene(self): #Establish the sequence for the cog to walk in the scene self.resetCogPos() self.enterView.start() self.isInView = True def exitScene(self): #Establish the sequence for the cog to walk out of the scene self.exitView.start() self.isInView = False def playNeutral(self): #Makes the neutral animation available to the GUI class self.cog.loop('neutral') def playVictory(self): #Makes the victory animation available to the GUI class self.cog.loop('victory') def getAnimPlaying(self): #returns the animation playing for the GUI class return self.cog.getCurrentAnim()
class DistributedSafezoneJukebox(DistributedObject): notify = directNotify.newCategory('DistributedSafezoneJukebox') def __init__(self, cr): DistributedObject.__init__(self, cr) self.activityName = TTLocalizer.SafezoneJukeboxTitle self.phaseToMusicData = PhaseToMusicData60 self.jukebox = None self.gui = None self.tunes = [] self.music = None self.messageGui = None self.currentSongData = None self.localQueuedSongInfo = None self.localQueuedSongListItem = None self._localToonRequestStatus = None self.toonIds = [] self._toonId2ror = {} self.root = NodePath('root') return def announceGenerate(self): DistributedObject.announceGenerate(self) self.load() def disable(self): self.notify.debug('BASE: disable') DistributedObject.disable(self) rorToonIds = self._toonId2ror.keys() for toonId in rorToonIds: self.cr.relatedObjectMgr.abortRequest(self._toonId2ror[toonId]) del self._toonId2ror[toonId] self.ignore(self.messageDoneEvent) if self.messageGui is not None and not self.messageGui.isEmpty(): self.messageGui.cleanup() self.messageGui = None return def delete(self): self.notify.debug('BASE: delete') self.unload() self.ignoreAll() DistributedObject.delete(self) def generateInit(self): self.gui = JukeboxGui(self.phaseToMusicData) def load(self): self.jukebox = Actor( 'phase_13/models/parties/jukebox_model', {'dance': 'phase_13/models/parties/jukebox_dance'}) self.jukebox.reparentTo(self.root) self.collNode = CollisionNode(self.getCollisionName()) self.collNode.setCollideMask(ToontownGlobals.CameraBitmask | ToontownGlobals.WallBitmask) collTube = CollisionTube(0, 0, 0, 0.0, 0.0, 4.25, 2.25) collTube.setTangible(1) self.collNode.addSolid(collTube) self.collNodePath = self.jukebox.attachNewNode(self.collNode) self.loadSign() self.sign.setPos(-5.0, 0, 0) self.messageDoneEvent = self.uniqueName('messageDoneEvent') self.root.reparentTo(render) self.activate() def loadSign(self): self.sign = self.root.attachNewNode('%sSign' % self.activityName) self.defaultSignModel = loader.loadModel( 'phase_13/models/parties/eventSign') self.signModel = self.defaultSignModel.copyTo(self.sign) self.signFlat = self.signModel.find('**/sign_flat') self.signFlatWithNote = self.signModel.find('**/sign_withNote') self.signTextLocator = self.signModel.find('**/signText_locator') self.activityIconsModel = loader.loadModel( 'phase_4/models/parties/eventSignIcons') textureNodePath = self.activityIconsModel.find('**/PartyJukebox40Icon') textureNodePath.setPos(0.0, -0.02, 2.2) textureNodePath.setScale(2.35) textureNodePath.copyTo(self.signFlat) textureNodePath.copyTo(self.signFlatWithNote) text = TextNode('noteText') text.setTextColor(0.2, 0.1, 0.7, 1.0) text.setAlign(TextNode.ACenter) text.setFont(OTPGlobals.getInterfaceFont()) text.setWordwrap(10.0) text.setText('') self.noteText = self.signFlatWithNote.attachNewNode(text) self.noteText.setPosHpr(self.signTextLocator, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0) self.noteText.setScale(0.2) self.signFlatWithNote.stash() self.signTextLocator.stash() def unload(self): self.gui.unload() if self.music is not None: self.music.stop() self.jukebox.stop() self.jukebox.delete() self.jukebox = None self.signModel.removeNode() del self.signModel self.sign.removeNode() del self.sign self.ignoreAll() self.root.removeNode() del self.root del self.activityName del self.messageGui if hasattr(self, 'toonIds'): del self.toonIds self.ignoreAll() return def getCollisionName(self): return self.uniqueName('jukeboxCollision') def activate(self): self.accept('enter' + self.getCollisionName(), self.__handleEnterCollision) def __handleEnterCollision(self, collisionEntry): if base.cr.playGame.getPlace().fsm.getCurrentState().getName( ) == 'walk': base.cr.playGame.getPlace().fsm.request('activity') self.d_toonJoinRequest() def localToonExiting(self): self._localToonRequestStatus = ActivityRequestStatus.Exiting def localToonJoining(self): self._localToonRequestStatus = ActivityRequestStatus.Joining def d_toonJoinRequest(self): if self._localToonRequestStatus is None: self.localToonJoining() self.sendUpdate('toonJoinRequest') return def d_toonExitRequest(self): if self._localToonRequestStatus is None: self.localToonExiting() self.sendUpdate('toonExitRequest') return def d_toonExitDemand(self): self.localToonExiting() self.sendUpdate('toonExitDemand') def joinRequestDenied(self, reason): self._localToonRequestStatus = None self.showMessage(TTLocalizer.PartyJukeboxOccupied) return def handleToonJoined(self, toonId): toon = base.cr.doId2do.get(toonId) if toon: self.jukebox.lookAt(base.cr.doId2do[toonId]) self.jukebox.setHpr(self.jukebox.getH() + 180.0, 0, 0) if toonId == base.localAvatar.doId: self.__localUseJukebox() def handleToonExited(self, toonId): if toonId == base.localAvatar.doId and self.gui.isLoaded(): self.__deactivateGui() def handleToonDisabled(self, toonId): self.notify.warning('handleToonDisabled no implementation yet') def setToonsPlaying(self, toonIds): exitedToons, joinedToons = self.getToonsPlayingChanges( self.toonIds, toonIds) self.setToonIds(toonIds) self._processExitedToons(exitedToons) self._processJoinedToons(joinedToons) def _processExitedToons(self, exitedToons): for toonId in exitedToons: if toonId != base.localAvatar.doId or toonId == base.localAvatar.doId and self.isLocalToonRequestStatus( ActivityRequestStatus.Exiting): toon = self.getAvatar(toonId) if toon is not None: self.ignore(toon.uniqueName('disable')) self.handleToonExited(toonId) if toonId == base.localAvatar.doId: self._localToonRequestStatus = None if toonId in self._toonId2ror: self.cr.relatedObjectMgr.abortRequest( self._toonId2ror[toonId]) del self._toonId2ror[toonId] return def _processJoinedToons(self, joinedToons): for toonId in joinedToons: if toonId != base.localAvatar.doId or toonId == base.localAvatar.doId and self.isLocalToonRequestStatus( ActivityRequestStatus.Joining): if toonId not in self._toonId2ror: request = self.cr.relatedObjectMgr.requestObjects( [toonId], allCallback=self._handlePlayerPresent) if toonId in self._toonId2ror: del self._toonId2ror[toonId] else: self._toonId2ror[toonId] = request def _handlePlayerPresent(self, toons): toon = toons[0] toonId = toon.doId if toonId in self._toonId2ror: del self._toonId2ror[toonId] else: self._toonId2ror[toonId] = None self._enableHandleToonDisabled(toonId) self.handleToonJoined(toonId) if toonId == base.localAvatar.doId: self._localToonRequestStatus = None return def _enableHandleToonDisabled(self, toonId): toon = self.getAvatar(toonId) if toon is not None: self.acceptOnce(toon.uniqueName('disable'), self.handleToonDisabled, [toonId]) else: self.notify.warning( 'BASE: unable to get handle to toon with toonId:%d. Hook for handleToonDisabled not set.' % toonId) return def isLocalToonRequestStatus(self, requestStatus): return self._localToonRequestStatus == requestStatus def setToonIds(self, toonIds): self.toonIds = toonIds def getToonsPlayingChanges(self, oldToonIds, newToonIds): oldToons = set(oldToonIds) newToons = set(newToonIds) exitedToons = oldToons.difference(newToons) joinedToons = newToons.difference(oldToons) return (list(exitedToons), list(joinedToons)) def getAvatar(self, toonId): if toonId in self.cr.doId2do: return self.cr.doId2do[toonId] else: self.notify.warning( 'BASE: getAvatar: No avatar in doId2do with id: ' + str(toonId)) return return def __localUseJukebox(self): base.localAvatar.disableAvatarControls() base.localAvatar.stopPosHprBroadcast() self.__activateGui() self.accept(JukeboxGui.CLOSE_EVENT, self.__handleGuiClose) taskMgr.doMethodLater(0.5, self.__localToonWillExitTask, self.uniqueName('toonWillExitJukeboxOnTimeout'), extraArgs=None) self.accept(JukeboxGui.ADD_SONG_CLICK_EVENT, self.__handleQueueSong) if self.isUserHost(): self.accept(JukeboxGui.MOVE_TO_TOP_CLICK_EVENT, self.__handleMoveSongToTop) return def __localToonWillExitTask(self, task): self.localToonExiting() return Task.done def __activateGui(self): self.gui.enable(timer=JUKEBOX_TIMEOUT) self.gui.disableAddSongButton() if self.currentSongData is not None: self.gui.setSongCurrentlyPlaying(self.currentSongData[0], self.currentSongData[1]) self.d_queuedSongsRequest() return def __deactivateGui(self): self.ignore(JukeboxGui.CLOSE_EVENT) self.ignore(JukeboxGui.SONG_SELECT_EVENT) self.ignore(JukeboxGui.MOVE_TO_TOP_CLICK_EVENT) base.cr.playGame.getPlace().setState('walk') base.localAvatar.startPosHprBroadcast() base.localAvatar.enableAvatarControls() self.gui.unload() self.__localClearQueuedSong() def isUserHost(self): return base.localAvatar.getAccessLevel() >= 200 def d_queuedSongsRequest(self): self.sendUpdate('queuedSongsRequest') def queuedSongsResponse(self, songInfoList, index): if self.gui.isLoaded(): for i in xrange(len(songInfoList)): songInfo = songInfoList[i] self.__addSongToQueue(songInfo, isLocalQueue=index >= 0 and i == index) self.gui.enableAddSongButton() def __handleGuiClose(self): self.__deactivateGui() self.d_toonExitDemand() def __handleQueueSong(self, name, values): self.d_setNextSong(values[0], values[1]) def d_setNextSong(self, phase, filename): self.sendUpdate('setNextSong', [(phase, filename)]) def setSongInQueue(self, songInfo): if self.gui.isLoaded(): phase = sanitizePhase(songInfo[0]) filename = songInfo[1] data = self.getMusicData(phase, filename) if data: if self.localQueuedSongListItem is not None: self.localQueuedSongListItem['text'] = data[0] else: self.__addSongToQueue(songInfo, isLocalQueue=True) return def __addSongToQueue(self, songInfo, isLocalQueue=False): isHost = isLocalQueue and self.isUserHost() data = self.getMusicData(sanitizePhase(songInfo[0]), songInfo[1]) if data: listItem = self.gui.addSongToQueue(data[0], highlight=isLocalQueue, moveToTopButton=isHost) if isLocalQueue: self.localQueuedSongInfo = songInfo self.localQueuedSongListItem = listItem def __localClearQueuedSong(self): self.localQueuedSongInfo = None self.localQueuedSongListItem = None return def __play(self, phase, filename, length): self.music = base.loader.loadMusic( (MUSIC_PATH + '%s') % (phase, filename)) if self.music: if self.__checkPartyValidity() and hasattr( base.cr.playGame.getPlace().loader, 'music') and base.cr.playGame.getPlace().loader.music: base.cr.playGame.getPlace().loader.music.stop() self.music.setTime(0.0) self.music.setLoopCount(int(getMusicRepeatTimes(length))) self.music.play() jukeboxAnimControl = self.jukebox.getAnimControl('dance') if not jukeboxAnimControl.isPlaying(): self.jukebox.loop('dance', fromFrame=0, toFrame=48) self.currentSongData = (phase, filename) def __stop(self): self.jukebox.stop() self.currentSongData = None if self.music: self.music.stop() if self.gui.isLoaded(): self.gui.clearSongCurrentlyPlaying() return def setSongPlaying(self, songInfo, toonId): phase = sanitizePhase(songInfo[0]) filename = songInfo[1] if not filename: self.__stop() return data = self.getMusicData(phase, filename) if data: self.__play(phase, filename, data[1]) self.setSignNote(data[0]) if self.gui.isLoaded(): item = self.gui.popSongFromQueue() self.gui.setSongCurrentlyPlaying(phase, filename) if item == self.localQueuedSongListItem: self.__localClearQueuedSong() if toonId == localAvatar.doId: localAvatar.setSystemMessage(0, TTLocalizer.PartyJukeboxNowPlaying) def setSignNote(self, note): self.noteText.node().setText(note) if len(note.strip()) > 0: self.signFlat.stash() self.signFlatWithNote.unstash() self.signTextLocator.unstash() else: self.signFlat.unstash() self.signFlatWithNote.stash() self.signTextLocator.stash() def __handleMoveSongToTop(self): if self.isUserHost() and self.localQueuedSongListItem is not None: self.d_moveHostSongToTopRequest() return def d_moveHostSongToTopRequest(self): self.notify.debug('d_moveHostSongToTopRequest') self.sendUpdate('moveHostSongToTopRequest') def moveHostSongToTop(self): self.notify.debug('moveHostSongToTop') if self.gui.isLoaded(): self.gui.pushQueuedItemToTop(self.localQueuedSongListItem) def getMusicData(self, phase, filename): data = [] phase = sanitizePhase(phase) phase = self.phaseToMusicData.get(phase) if phase: data = phase.get(filename, []) return data def __checkPartyValidity(self): if hasattr(base.cr.playGame, 'getPlace') and base.cr.playGame.getPlace() and hasattr( base.cr.playGame.getPlace(), 'loader') and base.cr.playGame.getPlace().loader: return True else: return False def showMessage(self, message, endState='walk'): base.cr.playGame.getPlace().fsm.request('activity') self.acceptOnce(self.messageDoneEvent, self.__handleMessageDone) self.messageGui = TTDialog.TTGlobalDialog( doneEvent=self.messageDoneEvent, message=message, style=TTDialog.Acknowledge) self.messageGui.endState = endState def __handleMessageDone(self): self.ignore(self.messageDoneEvent) if hasattr(base.cr.playGame.getPlace(), 'fsm'): if self.messageGui and hasattr(self.messageGui, 'endState'): self.notify.info('__handleMessageDone (endState=%s)' % self.messageGui.endState) base.cr.playGame.getPlace().fsm.request( self.messageGui.endState) else: self.notify.warning( "messageGui has no endState, defaulting to 'walk'") base.cr.playGame.getPlace().fsm.request('walk') if self.messageGui is not None and not self.messageGui.isEmpty(): self.messageGui.cleanup() self.messageGui = None return
class Cycle(DirectObject): def __init__(self, inputManager, track, audio3D, startPos, name, ai = None): self.inputManager = inputManager # Stores a reference to the InputManager to access user input. self.setupVarsNPs(audio3D, startPos, name) self.setupCollisions() # Sets up initial variables, NodePaths, and collision objects. self.track = track # Stores a reference to the track. self.lanes = self.track.trackLanes.lanes # gets a reference to the lanes on the track, to shorten the variable name. startingLane = self.track.trackLanes.getNearestMarker(self).lane # Gets the lane for the closest marker to the cycle. self.uc1 = self.lanes[startingLane][0] self.uc2 = self.lanes[startingLane][1] self.uc3 = self.lanes[startingLane][2] # Sets up 3 variables to store references to the 3 up-coming markers on the # track, ahead of the cycle. These are used by the AI to steer, and by player # cycles to measure progress along the track. if(ai == True): self.ai = CycleAI(self) # if the ai input is passed anything, it won't default to None, and the cycle will create # an AI to control itself. elif(ai == None): self.ai = None taskMgr.add(self.cycleControl, "Cycle Control", sort = int(self.root.getY() + 100)) # If the cycle isn't using an AI, activate player control. The cycle is also given a unique # sort value based on it's distance from the finish line. The unique sort value fixes a bug # in the AI aim code that can cause jittering in the aim when the tasks that control the # cycles don't execute in the same order every frame. # With this set up, if AI == False, the cycle will be completely uncontrolled. self.setupLight() # Calls the method that creates a light to represent the glow of the cycle's discs and engine. def setupVarsNPs(self, audio3D, startPos, name): self.name = name # Stores a unique name for this cycle. self.audio3D = audio3D # Stores a reference to the audio3DManager the cycle will use. self.root = render.attachNewNode("Root") # Creates and stores the NodePath that will be used as the root of the cycle # for the purpose of movement. self.cycle = self.root.attachNewNode("Cycle") # Creates a dummy node to go between self.root and the actualy cycle model. if(startPos == 1): self.root.setPos(5,0,0) self.model = loader.loadModel("../Models/RedCycle.bam") self.turret = loader.loadModel("../Models/RedTurr.bam") elif(startPos == 2): self.root.setPos(-5,-5,0) self.model = loader.loadModel("../Models/BlueCycle.bam") self.turret = loader.loadModel("../Models/BlueTurr.bam") elif(startPos == 3): self.root.setPos(5,-10,0) self.model = loader.loadModel("../Models/GreenCycle.bam") self.turret = loader.loadModel("../Models/GreenTurr.bam") elif(startPos == 4): self.root.setPos(-5,-15,0) self.model = loader.loadModel("../Models/YellowCycle.bam") self.turret = loader.loadModel("../Models/YellowTurr.bam") # Sets the root to a new position according to what place it is given. # Also loads the model and turret of the appropriate color. self.mounts = Actor("../Models/Mounts.egg") # Loads an actor with 3 mounting joints for the moving parts of the cycle. self.model.reparentTo(self.cycle) self.mounts.reparentTo(self.model) # Attaches the model to the go-between node and attaches the mounts to # the model. turretMount = self.mounts.exposeJoint(None, "modelRoot", "Turret") fdMount = self.mounts.exposeJoint(None, "modelRoot", "FrontDisc") rdMount = self.mounts.exposeJoint(None, "modelRoot", "RearDisc") # exposes the mount joints so pieces can be attached to them. self.fd = loader.loadModel("../Models/Disc.bam") self.rd = loader.loadModel("../Models/Disc.bam") # loads the models for the discs. self.turretActor = Actor("../Models/TurretActor.egg") self.turretActor.reparentTo(turretMount) self.turret.reparentTo(self.turretActor) self.trgtrMount = self.turretActor.exposeJoint(None, "modelRoot", "TargeterMount") self.fd.reparentTo(fdMount) self.rd.reparentTo(rdMount) # Attaches the turret and discs to their mounts. self.LMGMount = self.turretActor.exposeJoint(None, "modelRoot", "LMGMount") self.RMGMount = self.turretActor.exposeJoint(None, "modelRoot", "RMGMount") self.LMG = MachineGun(self, self.LMGMount, self.audio3D) self.RMG = MachineGun(self, self.RMGMount, self.audio3D) self.CannonMount = self.turretActor.exposeJoint(None, "modelRoot", "CannonMount") self.cannon = Cannon(self, self.CannonMount, self.audio3D) self.engineSfx = self.audio3D.loadSfx("../Sound/Engine.wav") self.audio3D.attachSoundToObject(self.engineSfx, self.root) self.engineSfx.setPlayRate(.5) self.engineSfx.setLoop(True) self.engineSfx.setVolume(2) self.engineSfx.play() # loads a sound effect for the engine and sets the starting play rate. self.dirNP = self.root.attachNewNode("DirNP") self.refNP = self.root.attachNewNode("RefNP") # Creates and stores two more NodePaths, one to track the direction the cycle # is moving in and another to use as a reference during various calculations # the cycle performs. self.dirVec = Vec3(0,0,0) self.cycleVec = Vec3(0,0,0) self.refVec = Vec3(0,0,0) # Creates and stores 3 vector objects to be used in simulating drift when the # cycle turns. self.speed = 0 self.throttle = 0 self.maxSpeed = 200 self.maxShield = 500 self.shield = self.maxShield self.accel = 25 self.handling = 25 self.maxEnergy = 100 self.energy = self.maxEnergy self.stability = 25 self.shieldRchrg = 10 self.energyRchrg = 5 self.shutDown = False # Sets basic variables for the cycle's racing attributes. self.turning = None self.lean = 0 # Creates two variables to control the cycle's lean self.markerCount = 0 self.currentLap = 0 # Creates two variables that will be used to track the progress # of the cycle on the track. self.freeFall = False self.fallSpeed = 0 # Creates two variables to assist with managing the cycle's falls from # hills and ramps. self.trackNP = render.attachNewNode(self.name + "_TrackNode") # Creates a NodePath to use when setting the cycle's height above the track. # refNP won't serve for this purpose, because it exists in root's coordinate space. # We need a NodePath in render's coordinate space for this purpose. self.active = False # creates a variable that determines if the cycle can be controlled or not. return # setupVarsNPs: Initializes most of the non-collision variables and NodePaths needed by the cycle. def setupCollisions(self): self.shieldCN = CollisionNode(self.name + "_ShieldCN") # Creates a CollisionNode to store the 3 CollisionSpheres that represent the # invisible energy shield surrounding the cycle. self.shieldCN.setPythonTag("owner", self) # Connects a reference to the instance of this class to the shield CollisionNode. # This will make it much easier to identify this cycle when its shield is struck. CS1 = CollisionSphere(0, -.025, .75, .785) CS2 = CollisionSphere(0, -1.075, .85, .835) CS3 = CollisionSphere(0, 1.125, .6, .61) self.shieldCN.addSolid(CS1) self.shieldCN.addSolid(CS2) self.shieldCN.addSolid(CS3) # Creates the 3 CollisionSpheres that make up the shield and adds them into # the CollisionNode. self.shieldCN.setIntoCollideMask(BitMask32.range(2,3)) self.shieldCN.setFromCollideMask(BitMask32.bit(2)) # Sets the BitMasks on the CollisionNode. The From mask has bit 2 turned on, so # the shield can bump into things on bit 2. The Into mask has bits 2, 3, and 4 turned # on so the shield can be struck by things on bit 2 (other cycles), 3 (guns), and 4 (explosions) self.shieldCNP = self.model.attachNewNode(self.shieldCN) # Connects the shield and its 3 spheres to the cycle model and gets a NodePath to # the shield. self.bumpCTrav = CollisionTraverser() self.bumpHan = CollisionHandlerPusher() # Creates a CollisionTraverser and a Pusher-type handler to detect and manage # collisions caused by the cycle's shield. self.bumpHan.addCollider(self.shieldCNP, self.root) # Tells the pusher handler to push back on self.root when self.shieldCNP is # involved in a collision. self.bumpHan.addAgainPattern("%fn-again") # Tells the pusher handler to create an event using the From CollisionNode's # name when it handles recurring collisions. self.bumpCTrav.addCollider(self.shieldCNP, self.bumpHan) # Registers the shield and the handler with the traverser. self.accept(self.name + "_ShieldCN-again", self.bump) # Registers the event that the pusher handler will generate and connects it to # the bump method. self.gRayCN = CollisionNode(self.name + "_GRayCN") # Creates a second CollisionNode to store the rays that will be used for collision # with the ground. self.fRay = CollisionRay(0, .5, 10, 0, 0, -1) self.bRay = CollisionRay(0, -.5, 10, 0, 0, -1) # Creates two rays 10 meters up and points them downward. This time we keep a # reference to the rays because we will need them when organizing their collision data. self.gRayCN.addSolid(self.fRay) self.gRayCN.addSolid(self.bRay) # Adds the rays to the CollisionNode. self.gRayCN.setFromCollideMask(BitMask32.bit(1)) self.gRayCN.setIntoCollideMask(BitMask32.allOff()) # Sets the BitMasks for gRayCN. The From mask has bit 1 on so it can cause collisions # with the ground. The Into mask is turned off completely, because rays should never # be collided into. self.gRayCNP = self.cycle.attachNewNode(self.gRayCN) # Attaches the gRayCN to the cycle and gets a NodePath to it. self.gCTrav = CollisionTraverser() self.gHan = CollisionHandlerQueue() self.gCTrav.addCollider(self.gRayCNP, self.gHan) # Creates a traverser and a Queue-type handler to detect and manage the rays' collisions. # Then registers gRayCNP and the handler with the traverser. self.trgtrCN = CollisionNode(self.name + "_TargeterCN") self.trgtrRay = CollisionRay(0,0,0,0,1,0) self.trgtrCN.addSolid(self.trgtrRay) self.trgtrCN.setFromCollideMask(BitMask32.bit(3)) self.trgtrCN.setIntoCollideMask(BitMask32.allOff()) self.trgtrCNP = self.trgtrMount.attachNewNode(self.trgtrCN) # creates 1 collision ray to represent the line of fire of the cycle for the purpose shooting. # The ray is added to a collision node which is parented to the targeter mount of the turret. self.trgtrCTrav = CollisionTraverser() self.trgtrCHan = CollisionHandlerQueue() self.trgtrCTrav.addCollider(self.trgtrCNP, self.trgtrCHan) # creates a traverser and Queue-type handler to detect and manage the collisions of the # targeter ray, and registers the ray and handler with the traverser. return # setupCollisions: Preps CollisionNodes, collision solids, traversers, and handlers to get them # ready for use. def setupLight(self): self.glow = self.cycle.attachNewNode( PointLight(self.name + "Glow")) self.glow.node().setColor(Vec4(.2,.6,1,1)) # Creates a point light on the cycle and sets it's color to a blue-white. self.glow.node().setAttenuation(Vec3(0,0,.75)) # Sets the attenuation on the point light. This controls the fall-off. self.cycle.setLight(self.glow) self.track.track.setLight(self.glow) # Sets the light to illuminate the cycle and the track. return # setupLight: Creates a point light to represent the glow of the cycle and sets its attributes. Then # sets the light to illuminate the scene. def cycleControl(self, task): if(self.cycle == None): return task.done # Ends the task if the cycle model is removed. dt = globalClock.getDt() if( dt > .20): return task.cont # Gets the amount of time that has passed since the last frame from the global clock. # If the value is too large, there has been a hiccup and the frame will be skipped. if(self.active == True and self.shutDown == False): # limits player control to only occur when self.active is true and # self.shutDown is false. if(self.inputManager.keyMap["up"] == True): self.adjustThrottle("up", dt) elif(self.inputManager.keyMap["down"] == True): self.adjustThrottle("down", dt) # Checks the InputManager for throttle control initiated by the user and performs # the adjustments. if(self.inputManager.keyMap["right"] == True): self.turn("r", dt) self.turning = "r" elif(self.inputManager.keyMap["left"] == True): self.turn("l", dt) self.turning = "l" else: self.turning = None # Checks the InputManager for turning initiated by the user and performs it. # Also updates the self.turning variable to reflect it. if(self.inputManager.keyMap["mouse1"] == True): self.LMG.fire() self.RMG.fire() # If the left mouse button is pressed, fire the machine guns. if(self.inputManager.keyMap["mouse3"] == True): self.cannon.fire() # If the right mouse button is pressed, fire the cannon. aimPoint = self.inputManager.getMouseAim() if(aimPoint != None): self.turretActor.lookAt(render, aimPoint) self.speedCheck(dt) self.simDrift(dt) self.groundCheck(dt) self.move(dt) self.checkMarkers() self.recharge(dt) # Calls the methods that control cycle behavior frame-by-frame. self.bumpCTrav.traverse(render) # Checks for collisions caused by the cycle's shield. return task.cont # cycleControl: Manages the cycle's behavior when under player control. def cameraZoom(self, dir, dt): if(dir == "in"): base.camera.setY(base.camera, 10 * dt) else: base.camera.setY(base.camera, -10 * dt) return # cameraZoom: Moves the camera toward or away from the cycle at a rate of 10 # meters per second. def turn(self, dir, dt): turnRate = self.handling * (2 - (self.speed / self.maxSpeed)) # Determines the current turn rate of the cycle according to its speed. if(dir == "r"): turnRate = -turnRate self.turning = "r" # If this is a right turn, then turnRate should be negative. self.cycle.setH(self.cycle, turnRate * dt) # Rotates the cycle according to the turnRate and time. return # turn: Rotates the cycle based on its speed to execute turns. def adjustThrottle(self, dir, dt): if(dir == "up"): self.throttle += .25 * dt # Increases the throttle setting at a rate of 25% per second. if(self.throttle > 1 ): self.throttle = 1 # Limits the throttle to a maximum of 100% else: self.throttle -= .25 * dt # Decreases the throttle setting at a rate of 25% per second. if(self.throttle < -1 ): self.throttle = -1 # Limits the throttle to a minimum of 100% return # adjustThrottle: Increases or decreases the throttle. def speedCheck(self, dt): if(self.freeFall == False): # The cycle can't accelerate or deccelerate under it's own power if # it's in freefall, so we check to make sure it isn't. tSetting = (self.maxSpeed * self.throttle) # Gets the KpH value that corresponds to the current throttle setting. if(self.speed < tSetting): # Checks if the speed is too low. if((self.speed + (self.accel * dt)) > tSetting): # If so, check if accelerating at the normal rate would raise speed too high. self.speed = tSetting # If so, just set the speed to the throttle setting. else: self.speed += (self.accel * dt) # If accelerating won't raise the speed too high, go ahead and accelerate. elif(self.speed > tSetting): # Checks if the speed is too high. if((self.speed - (self.accel * dt)) < tSetting): # If so, check if decelerating at the normal rate would lower speed too much. self.speed = tSetting # If so, just set the speed to the throttle setting. else: self.speed -= (self.accel * dt) # If decelerating won't loser the speed too much, go ahead and decelerate. else: self.speed -= (self.speed * .125) * dt # If the cycle is in freefall, lower it's speed by 12.5% per second to simulate loss # of momentum to friction. speedRatio = self.speed / self.maxSpeed self.engineSfx.setPlayRate(.5 + speedRatio) # Adjusts the playrate of the engine sound based on speed. return # speedCheck: Controls the speed at which the cycle is moving by adjusting it according to the # throttle, or degrading it over time when in freefall. def simDrift(self, dt): self.refNP.setPos(self.dirNP, 0, 1, 0) self.dirVec.set(self.refNP.getX(), self.refNP.getY(), 0) # Uses refNP to get a vector that describes the facing of dirNP. The height value is # discarded as it is unnecessary. self.refNP.setPos(self.cycle, 0, 1, 0) self.cycleVec.set(self.refNP.getX(), self.refNP.getY(), 0) # Uses refNP to get a vector that describes the facing of the cycle. The height value is # discarded as it is unnecessary. self.refVec.set(0,0,1) # Sets refVec to point straight up. This vector will be the axis used to determine the # difference in the angle between dirNP and the cycle. vecDiff = self.dirVec.signedAngleDeg(self.cycleVec, self.refVec) # Gets a signed angle that describes the difference between the facing of dirNP and # the cycle. if(vecDiff < .1 and vecDiff > -.1): self.dirNP.setHpr(self.cycle.getH(), 0, 0) # if the difference between the two facings is insignificant, set dirNP to face the # same direction as the cycle. else: self.dirNP.setHpr(self.dirNP, vecDiff * dt * 2.5, 0, 0) # If the difference is significant, tell dirNP to slowly rotate to try and catch up # to the cycle's facing. self.dirNP.setP(self.cycle.getP()) self.dirNP.setR(0) # Constrains dirNP pitch and roll to the cycle and 0, respectively. return # simDrift: This function simulates the cycle drifting when it turns by causing the dirNP, which # faces the direction the cycle is moving in, to slowly catch up to the actual facing of the cycle # over time. def groundCheck(self, dt): self.gCTrav.traverse(render) # Checks for collisions between the ground and the CollisionRays attached to the cycle. points = [None, None] # Preps a list to hold the data from the collisions. if(self.gHan.getNumEntries() > 1): # Verifies that at least 2 collisions occured. If not, there's no point in checking # the collisions because we can't get data from both rays. self.gHan.sortEntries() # Arranges the collision entries in the cues from nearest to furthest. for E in range(self.gHan.getNumEntries()): # Iterates through all the entries in the CollisionHandlerQueue entry = self.gHan.getEntry(E) # Stores the current entry in a temporary variable. if(entry.getFrom() == self.fRay and points[0] == None): # Checks if this entry is a collision caused by the front ray, and # verifies that we don't have front ray data for this frame yet. points[0] = entry.getSurfacePoint(render) # Stores the actual point of collision, in the coordinate system of render, # in the first slot of the points list. elif(entry.getFrom() == self.bRay and points[1] == None): # Checks if this entry is a collision caused by the back ray, and # verifies that we don't have back ray data for this frame yet. points[1] = entry.getSurfacePoint(render) # Stores the actual point of collision, in the coordinate system of render, # in the second slot of the points list. if(points[0] == None or points[1] == None): self.teleport() return # If either ray didn't collide with the track, the cycle is going out of bounds # and needs to be teleported back onto the track. else: # If both rays gave us collision data, we can proceed. ''' The following segment of code controls the pitch of the cycle ''' if(self.freeFall == False): # Checks if the cycle is in freefall. If it's not, we'll need to make the cycle's # pitch match the angle of the track. self.refNP.setPos(points[1]) # Sets refNP to the spot on the track where the back ray collided. self.refNP.lookAt(points[0]) # Tells refNP to point at the spot on the track where the front ray collided. pDiff = self.refNP.getP()- self.cycle.getP() # Finds the difference in pitch between refNP and the cycle, which is equivalent # to the difference between the cycle's pitch and the angle of the track. if(pDiff < .1 and pDiff > -.1): self.cycle.setP(self.refNP.getP()) # If the pitch difference is insignificant, set the cycle to match the pitch of # refNP. else: self.cycle.setP(self.cycle, pDiff * dt * 5) # If the difference is significant, smoothly adjust the cycle's pitch toward the # pitch of refNP. elif((self.cycle.getP() - (dt * 10)) > -15): self.cycle.setP(self.cycle, -(dt * 10)) # If the cycle is in freefall and slowly dropping the pitch won't lower it past -15, # go ahead and slowly lower it. else: self.cycle.setP(-15) # If we're in freefall and the cycle's pitch is to low to drop it any further, lock it # to exactly -15. ''' End pitch control ''' ''' The following section of code will control the height of the cycle above the track. ''' if(self.speed >= 0): self.trackNP.setPos(points[0].getX(), points[0].getY(), points[0].getZ()) else: self.trackNP.setPos(points[1].getX(), points[1].getY(), points[1].getZ()) # Set trackNP at the collision point on the leading end of the cycle. height = self.root.getZ(self.trackNP) # Get the height of root as seen from the trackNP. if(height > 2 and self.freeFall == False): self.freeFall = True self.fallSpeed = 0 # If the height is greater than 2 and we aren't in freefall, # enter a freefall state and prep freefall variables. if(self.freeFall == True): self.fallSpeed += (self.track.gravity * 9.8) * dt newHeight = height - (self.fallSpeed * dt) # In a freefall state, begin accelerating the fall speed and # calculate a new height based on that fall speed. else: hDiff = 1 - height # If not in a freefall state, calculate the difference in the actual height and the # desired height. if(hDiff > .01 or hDiff < -.01): newHeight = height + (hDiff * dt * 5) # If the difference is significant, calculate a new height that drifts toward # the desired height. else: newHeight = 1 # If you're close to the desired height, just set it as the calculated new height. if(newHeight >= 0): self.root.setZ(self.trackNP, newHeight) # If the new height is greater than or equal to zero, set it as root's height. else: self.root.setZ(self.trackNP, 0) self.freeFall = False # Otherwise, set the root node to a height of 0, and turn off the freefall state. ''' end of height control code ''' self.cycle.setR(0) # Constrain the cycle's roll to 0. return # groundCheck: Controls the cycle's pitch and it's height above the track. def move(self, dt): mps = self.speed * 1000 / 3600 # Convert kph to meters per second self.refNP.setPos(self.dirNP, 0, 1, 0) self.dirVec.set(self.refNP.getX(), self.refNP.getY(), self.refNP.getZ()) # Uses refNP to get a vector describing the direction dirNP is facing. self.root.setPos(self.root, self.dirVec.getX() * dt * mps, self.dirVec.getY() * dt * mps, self.dirVec.getZ() * dt * mps) # Moves root forward according to the direction vector, speed, and time. currentLean = self.model.getR() # Get the current amount of lean. if(self.turning == "r"): self.lean += 2.5 if(self.lean > 25): self.lean = 25 self.model.setR(self.model, (self.lean - currentLean) * dt * 5) # If the cycle is turning right, increase lean up to 25 and model the frame to lean the cycle. elif(self.turning == "l"): self.lean -= 2.5 if(self.lean < -25): self.lean = -25 self.model.setR(self.model, (self.lean - currentLean) * dt * 5) # If the cycle is turning left, decrease lean down to -25 and roll the model to lean the cycle. else: self.lean = 0 self.model.setR(self.model, (self.lean - currentLean) * dt * 5) # If the cycle isn't turning, set lean to 0 and roll the model back toward upright. self.fd.setH(self.fd, 5 + (20 * self.throttle)) self.rd.setH(self.rd, -5 + (-20 * self.throttle)) return # move: Controls the forward or backward movement of the cycle. def checkMarkers(self): if(self.uc1.checkInFront(self) == True): # Checks if the cycle has passed in front of uc1. self.uc1 = self.uc2 self.uc2 = self.uc3 self.uc3 = self.uc2.nextMarker # If so, get the next set of markers on the track. self.markerCount += 1 # Update the cycle's marker count by one. if(self.uc1 == self.lanes[0][1] or self.uc1 == self.lanes[1][1]): self.currentLap += 1 # If the cycle just passed the first marker, which is at the finish line, increment the lap count. return # checkMarkers: Checks if the nearest marker has been passed, and if so, # updates all the markers, marker count, and lap count if needed. def recharge(self, dt): if(self.energy < self.maxEnergy and self.shutDown == False): # Checks if the cycle should recharge energy. newEnergy = self.energy + (self.energyRchrg * dt) # determines what the new energy level will be. if(newEnergy > self.maxEnergy): self.energy = self.maxEnergy else: self.energy = newEnergy # if the new energy level exceeds to maximum, set # energy to max. Otherwise set energy to new level. if(self.shield <= 0 and self.shutDown == False): self.shutDown = True self.throttle = 0 self.shield = 0 # Checks if the cycle should enter emergency shut down and sets all # the appropriate values if so. if(self.shutDown == True): newShield = self.shield + (self.shieldRchrg * dt) * 10 # finds the new shield level if the cycle is in emergency shut down. if(newShield >= self.maxShield): self.shutDown = False # Ends the emergency shut down when the shield is recharged. elif(self.shield < self.maxShield): newShield = self.shield + (self.shieldRchrg * dt) # finds the new shield level if the cycle is not in emergency shut # down, and needs to recharge its shield. else: return # If the cycle doesn't need to recharge its shield, we're done and # we can exit the method. if(newShield <= self.maxShield): self.shield = newShield else: self.shield = self.maxShield # if the new energy level exceeds to maximum, set # energy to max. Otherwise set energy to new level. return # recharge: Evaluates the shield and energy levels of the cycle and recharges # them if necessary. Also determines if the cycle needs to enter or exit # emergency shut down. def teleport(self): marker = self.track.trackLanes.getNearestMarker(self) markerPos = marker.getPos() self.root.setPos(markerPos.getX(), markerPos.getY(), self.root.getZ()) # Put the cycle back on the track. self.gCTrav.traverse(render) # Checks for collisions between the ground and the CollisionRays attached to the cycle. points = [None, None] # Preps a list to hold the data from the collisions. if(self.gHan.getNumEntries() > 1): # Verifies that at least 2 collisions occured. If not, there's no point in checking # the collisions because we can't get data from both rays. self.gHan.sortEntries() # Arranges the collision entries in the cues from nearest to furthest. for E in range(self.gHan.getNumEntries()): # Iterates through all the entries in the CollisionHandlerQueue entry = self.gHan.getEntry(E) # Stores the current entry in a temporary variable. if(entry.getFrom() == self.fRay and points[0] == None): # Checks if this entry is a collision caused by the front ray, and # verifies that we don't have front ray data for this frame yet. points[0] = entry.getSurfacePoint(render) # Stores the actual point of collision, in the coordinate system of render, # in the first slot of the points list. elif(entry.getFrom() == self.bRay and points[1] == None): # Checks if this entry is a collision caused by the back ray, and # verifies that we don't have back ray data for this frame yet. points[1] = entry.getSurfacePoint(render) # Stores the actual point of collision, in the coordinate system of render, # in the second slot of the points list. if(self.speed >= 0): self.trackNP.setPos(points[0].getX(), points[0].getY(), points[0].getZ()) else: self.trackNP.setPos(points[1].getX(), points[1].getY(), points[1].getZ()) # Set the track node at the collision point on the leading end of the cycle. self.root.setZ(self.trackNP, 1) # Set the root to a height of 1 above trackNP. self.dirNP.setHpr(marker.getHpr()) self.cycle.setHpr(marker.getHpr()) # Reorients dirNP and the cycle to the same facing as the marker we teleported to. self.speed /= 2 # Cuts the speed by half as a penalty for going off the track. return # teleport: Moves the cycle back onto the track, fixes its height, and fixes its orientation. def bump(self, entry): #print(entry.getFromNodePath().getPythonTag("owner").name) #print("has bumped into:") #print(entry.getIntoNodePath().getPythonTag("owner").name) #print("") return # bump: Prints a message to the command prompt when the cycle bumps into another cycle. def hit(self, damage): self.shield -= damage # reduces the shield strength according to the damage. instability = (damage / 2) - self.stability # calculates the instability caused by the damage. if(instability > 0): self.speed -= instability # if the instability is positive, reduce the cycle speed by # the instability. return # hit: Handles tracking damage for the cycle. def getPos(self, ref = None): if(ref == None): return(self.root.getPos()) else: return(self.root.getPos(ref)) # getPos: returns the position of root in the coordinate system of the given NodePath, if one is given. def destroy(self): self.root.removeNode() self.cycle.removeNode() self.mounts.delete() self.turretActor.delete() self.model.removeNode() self.turret.removeNode() self.fd.removeNode() self.rd.removeNode() self.dirNP.removeNode() self.refNP.removeNode() self.trackNP.removeNode() self.shieldCNP.removeNode() self.gRayCNP.removeNode() self.trgtrCNP.removeNode() self.glow.removeNode() # removes all of the cycle's NodePaths from the scene. self.LMG.destroy() self.LMG = None self.RMG.destroy() self.RMG = None self.cannon.destroy() self.cannon = None # Removes the cycle's weapons. self.audio3D.detachSound(self.engineSfx) self.cycle = None # sets self.cycle to None to end player cycle control and notify # any AI that the cycle is being removed. if(self.ai != None): self.ai = None # removes the cycles reference to the AI, if it has one. return # destroy: Cleans up all of the cycles components so they can be removed # from memory.
class Monster(): def __init__(self, id, parent, type, pos): self.id = id self.parent = parent self.hp = 100 self.speed = 1 self.can_move = True if type == 'baby': self.node = Actor('models/baby', {'walk':'models/baby-walk', 'stand':'models/baby-stand', 'idle':'models/baby-idle', 'jump':'models/baby-jump', 'bite1':'models/baby-bite1', 'bite2':'models/baby-bite2', 'head_attack':'models/baby-head_attack', 'hit1':'models/baby-hit1', 'hit2':'models/baby-hit2', 'die':'models/baby-die'}) self.head_node = self.node.exposeJoint(None,"modelRoot","Bip01_Head") self.body_node = self.node.exposeJoint(None,"modelRoot","Bip01_Pelvis") self.node.setH(180) self.node.setScale(0.03) self.node.flattenLight() self.zpos = 0 self.node.setPos(pos[0]*TILE_SIZE,pos[1]*TILE_SIZE,self.zpos) self.node.setTexture(loader.loadTexture('models/Zomby_D.png')) self.ts_normal = TextureStage('ts_normal') self.tex_normal = loader.loadTexture('models/Zomby_N.png') self.ts_normal.setMode(TextureStage.MNormal) self.node.setTexture(self.ts_normal, self.tex_normal) self.ts_gloss = TextureStage('ts_gloss') self.tex_gloss = loader.loadTexture('models/Zomby_S1.png') self.ts_gloss.setMode(TextureStage.MGloss) self.node.setTexture(self.ts_gloss, self.tex_gloss) self.ts_glow = TextureStage('ts_glow') self.tex_glow = loader.loadTexture('models/Zomby_I.png') self.ts_glow.setMode(TextureStage.MGlow) self.node.setTexture(self.ts_glow, self.tex_glow) self.node.reparentTo(render) self.node.loop('walk') elif type == 'nos': self.node = loader.loadModel('models/nos') self.zpos = 5 self.node.setPos(pos[0]*TILE_SIZE,pos[1]*TILE_SIZE,self.zpos) self.node.setScale(2) if self.id == 1: self.node.setColor(1,0,0) elif self.id == 2: self.node.setColor(0,1,0) elif self.id == 3: self.node.setColor(0,0,1) else: self.node.setColor(1,1,1) self.node.reparentTo(render) #self.patrol_points = [(1,1), (4,11), (12,20), (18,4), (19,17)] #initialize 3d sound self.audio3d = Audio3DManager.Audio3DManager(base.sfxManagerList[0], base.camera) self.shot_head = self.audio3d.loadSfx('audio/Zombie In Pain-SoundBible.com-134322253.wav') self.shot_body = self.audio3d.loadSfx('audio/Zombie Moan-SoundBible.com-565291980.wav') self.moan1 = self.audio3d.loadSfx('audio/Mindless Zombie Awakening-SoundBible.com-255444348.wav') self.moan2 = self.audio3d.loadSfx('audio/Zombie Brain Eater-SoundBible.com-1076387080.wav') self.aggro_sound = self.audio3d.loadSfx('audio/Mummy Zombie-SoundBible.com-1966938763.wav') self.attack_sound = self.audio3d.loadSfx('audio/Chopping Off Limb-SoundBible.com-884800545.wav') self.audio3d.attachSoundToObject(self.moan1, self.node) self.audio3d.attachSoundToObject(self.moan2, self.node) self.audio3d.attachSoundToObject(self.shot_head, self.node) self.audio3d.attachSoundToObject(self.shot_body, self.node) self.audio3d.attachSoundToObject(self.aggro_sound, self.node) self.audio3d.attachSoundToObject(self.attack_sound, self.node) delay0 = Wait(d(35)) delay1 = Wait(25+d(35)) delay2 = Wait(25+d(35)) self.moan_sequence = Sequence(delay0, SoundInterval(self.moan1), delay1, SoundInterval(self.moan2), delay2) self.moan_sequence.loop() self.parent.collision_manager.createMonsterCollision(self) self.aggro_sound_last_played = 0 #--------------------------brain------------------------- self.node.setH( 160 ) self.pause = False self.action = ACTION_IDLE if percent(20): self.orders = ORDERS_PATROL else: self.orders = ORDERS_IDLE self.last_melee = 0 self.player_last_seen_abs = None self.idle_timer = time.time() self.idle_value = 1 self.current_waypoint = None #self.wait_until = None self.herding_timer = None self.path = None taskMgr.doMethodLater(1, self.behaviourTask, 'MonsterBehaviourTask'+str(self.id) ) taskMgr.doMethodLater(1, self.debugMoveTask, 'MonsterMoveTask'+str(self.id)) def getLOS(self): return self.parent.collision_manager.checkMonsterPlayerLos(self) def sensePlayer(self): """Return True if player sensed, and his last known coordinates are stored in self.player_last_seen_abs""" # if the player is dead, do not sense him if self.parent.player.health <= 0: return False #get player's position p_pos_abs = self.parent.player.node.getPos() my_pos_abs = self.node.getPos() #--------------------------------SENSE--------------------------------- #if player is within SENSING_RANGE we know he is there if self.distanceToPlayer() < SENSING_RANGE: #print "TOO CLOSE LOOSER!" self.player_last_seen_abs = p_pos_abs return True #---------------------------------HEAR---------------------------------- #if player is within HEARING_RANGE we know he is there effective_hearing_range = HEARING_RANGE if self.parent.player.gunshot_at: effective_hearing_range *= 3 else: if self.parent.player.sprint: effective_hearing_range *= 2 if not self.parent.player.moving: effective_hearing_range = 0 if self.distanceToPlayer() < effective_hearing_range: print "I HEAR U!" self.parent.player.adrenaline() #if we can see go chase him if self.getLOS(): self.player_last_seen_abs = p_pos_abs return True #we cannot see him, build new path to that tile else: dest = getTile( p_pos_abs ) path = pathFind(self.parent.level, getTile( self.node.getPos()), dest) if path: self.path = path self.orders = ORDERS_PATROL self.action = ACTION_FOLLOW_PATH return False #-------------------------------SEE--------------------------------- #if player is in front of us if self.angleToPlayerAbs() <= 45: #if he is close enough to see and we can see him if self.distanceToPlayer() <= VIEW_RANGE and self.getLOS(): self.player_last_seen_abs = p_pos_abs #print "vidim!" return True #if player has a flashlight lit, and we can see him go after him if self.parent.player.flashlight and self.getLOS(): self.player_last_seen_abs = p_pos_abs #print "vidim flashlight" return True #---------------------SEE MY OWN SHADOW--------------------------- #if player is behind us and has a lit up flashlight and we have LOS to him if self.angleToPlayerAbs() > 135 and self.angleToPlayerAbs() < 225: if self.parent.player.flashlight and self.getLOS(): #if he is looking at us my_pos_rel = self.node.getPos( self.parent.player.node ) forward = Vec2( 0, 1 ) if math.fabs( forward.signedAngleDeg( Vec2( my_pos_rel[0], my_pos_rel[1] ) ) ) <= 30: #go after my own shadow print "herding" self.orders = ORDERS_HERDING self.node.setH( self.parent.player.node.getH() ) self.herding_timer = time.time() return False def distanceToPlayer(self): p_pos_abs = self.parent.player.node.getPos() my_pos_abs = self.node.getPos() return math.sqrt( math.pow( p_pos_abs[0] - my_pos_abs[0], 2) + math.pow( p_pos_abs[1] - my_pos_abs[1], 2) ) def angleToPlayer(self): p_pos_rel = self.parent.player.node.getPos( self.node ) forward = Vec2( 0, 1 ) return forward.signedAngleDeg( Vec2( p_pos_rel[0], p_pos_rel[1] ) ) def angleToPlayerAbs(self): return math.fabs( self.angleToPlayer() ) def behaviourTask(self, task): if self.pause: return task.again #top priority, if we sense a player, go after him! if self.sensePlayer(): if time.time() - self.aggro_sound_last_played > 5: self.aggro_sound.play() self.aggro_sound_last_played = time.time() self.action = ACTION_CHASE return task.again elif self.orders == ORDERS_IDLE: #percent chance to go on patrol if percent( 10 ): self.orders = ORDERS_PATROL return task.again self.action = ACTION_IDLE elif self.orders == ORDERS_PATROL: #percent chance to get idle if percent( 5 ): self.orders = ORDERS_IDLE return task.again #if we are already patroling, dont change anything if self.action == ACTION_FOLLOW_PATH: return task.again #build a new path for patrol dest = self.getNewPatrolPoint() self.path = pathFind(self.parent.level, getTile(self.node.getPos()), dest) self.action = ACTION_FOLLOW_PATH elif self.orders == ORDERS_HERDING: self.action = ACTION_MOVE if time.time() - self.herding_timer > HERDING_TIMEOUT: self.orders = ORDERS_IDLE return task.again def debugMoveTask(self, task): if self.pause: return task.cont #print "orders:", self.orders if self.action == ACTION_CHASE: self.parent.player.adrenaline() look_pos = Point3(self.player_last_seen_abs.getX(), self.player_last_seen_abs.getY(), self.zpos) self.node.lookAt( look_pos ) self.node.setFluidPos(self.node, 0, CHASE_SPEED*globalClock.getDt(), 0) if self.distanceToPlayer() <= MELEE_RANGE and self.angleToPlayerAbs() <= 45 and self.getLOS(): if time.time() - self.last_melee >= MELEE_TIME: self.attack_sound.play() att = random.randint(0,2) if att == 0: animname = 'bite1' elif att == 1: animname = 'bite2' else: animname = 'head_attack' self.node.play(animname) duration = self.node.getNumFrames(animname) / 24 # animation play rate taskMgr.doMethodLater(duration, self.finishedAnim, 'FinishedAnim', extraArgs = []) self.parent.player.getDamage() self.last_melee = time.time() elif self.action == ACTION_IDLE: if time.time() - self.idle_timer > IDLE_TIME: #we are standing still and rotating, see on whic side we will rotate now self.idle_timer = time.time() if percent(20): self.idle_value *= -1 self.rotateBy( self.idle_value * IDLE_ROTATE_SPEED ) elif self.action == ACTION_FOLLOW_PATH: #if we dont have a waypoint, calculate one if not self.current_waypoint: try: #get next tile from path tile = self.path[0] self.path = self.path[1:] #calculate waypoint varx= 5 - (d(4) + d(4)) vary= 5 - (d(4) + d(4)) self.current_waypoint = (Point3( tile[0] * TILE_SIZE + varx, tile[1] * TILE_SIZE + vary, self.zpos ), time.time() ) #print "waypoint:", self.current_waypoint self.node.lookAt( self.current_waypoint[0] ) except (IndexError, TypeError): #we have reached the end of path self.orders = ORDERS_IDLE self.current_waypoint = None #if we have a waypoint move forward towards it, and check if we arrived at it else: self.node.setFluidPos(self.node, 0, NORMAL_SPEED*globalClock.getDt(), 0) my_pos = self.node.getPos() #if we are close enough to the waypoint or if we didnt get to waypoint in time, delete it so we know we need a new one if math.fabs( my_pos[0] - self.current_waypoint[0][0] ) < 1 and math.fabs( my_pos[1] - self.current_waypoint[0][1] ) < 2 \ or time.time() - self.current_waypoint[1] > WAYPOINT_TIMER: self.current_waypoint = None elif self.action == ACTION_MOVE: self.node.setFluidPos(self.node, 0, NORMAL_SPEED*globalClock.getDt(), 0) return task.cont def finishedAnim(self): if not self.pause: self.node.loop('walk') def rotateBy(self, value): self.node.setH( (self.node.getH() + value) % 360 ) def getNewPatrolPoint(self): lvl = self.parent.level allTiles = lvl.getFloorTiles() while True: t = ( d(lvl.getMaxX()), d(lvl.getMaxY()) ) if t in allTiles: return t def hitWall(self): if self.action == ACTION_CHASE: return #print "lupio!" """self.moan1.play() self.rotateBy( 180 ) self.node.setFluidPos(self.node, 0, CHASE_SPEED*globalClock.getDt(), 0) #self.action = IDLE """ """ old = self.node.getH() rnd = 80 + random.randint( 0, 20 ) forward = Vec2( 0, 1 ) impact = Vec2( pos[0], pos[1] ) angle = forward.signedAngleDeg( impact ) #print "angle:", angle if angle < 0: #+ cause angle is negative rnd = 91 + angle self.node.setH( (self.node.getH() + rnd)%360 ) elif angle > 0: rnd = -91 + angle self.node.setH( (self.node.getH() + rnd)%360 ) #print "stari:", old, " novi:", self.node.getH() """ pass def pauze(self): if self.moan_sequence: self.moan_sequence.pause() self.pause = True def resume(self): if self.moan_sequence: self.moan_sequence.resume() self.pause = False def destroy(self): self.audio3d.detachSound(self.moan1) self.audio3d.detachSound(self.moan2) self.audio3d.detachSound(self.shot_head) self.audio3d.detachSound(self.shot_body) self.audio3d.detachSound(self.aggro_sound) self.audio3d.detachSound(self.attack_sound) if self.moan_sequence != None: self.moan_sequence.pause() self.moan_sequence = None taskMgr.remove('MonsterBehaviourTask'+str(self.id)) taskMgr.remove('MonsterMoveTask'+str(self.id)) self.cn_head.node().clearPythonTag('node') self.cn_body.node().clearPythonTag('node') self.cn_pusher.node().clearPythonTag('node') self.cn_ray.node().clearPythonTag('node') self.node.delete() self.node.cleanup() self.node.removeNode() """
class Character(FSM.FSM, Vehicle): """ An animated character with various steering behaviors. By enabling and disabling different steering behaviors Character can be used as a keyboard + mouse controlled player avatar, or can be controlled (for example) by a Finite State Machine and used to implement a non-player character. """ # FIXME: Think it might be clearer and safer if Character has a FSM instead # of inheriting from FSM (classes that inherit from Character will likely # want to inherit from FSM too). def __init__( self, name='Ralph', model='models/ralph', run='models/ralph-run', walk='models/ralph-walk', pos=None, avoidObstacles=True, avoidVehicles=True, hprs=(180, 0, 0, 1, 1, 1), # Ralph's Y is backward ): """Initialise the character. By default tries to load Panda3D's Ralph model: models/ralph, models/ralph-run and models/ralph-walk.""" FSM.FSM.__init__(self, 'Character') Vehicle.__init__(self, pos=pos, avoidObstacles=avoidObstacles, avoidVehicles=avoidVehicles, radius=2.5) self.name = name self.lastPose = 0 # Used when transitioning between animations self.actor = Actor(model, {"run": run, "walk": walk}) self.actor.setHprScale(*hprs) self.actor.reparentTo(self.prime) # Add a task for this Character to the global task manager. self.characterStepTask = taskMgr.add(self.characterStep, "Character step task") def characterStep(self, task): """Update the character. Called every frame by Panda's global task manager.""" # Update the orientation of the Character. Want to face in the # direction of the Vehicle's velocity in the X and Y dimensions, but # always face straight forward (not up or down) in the Z dimension. pr = self.prime.getP(), self.prime.getR() self.prime.lookAt((self._pos + self._velocity).getX(), (self._pos + self._velocity).getY(), 0) self.prime.setHpr(self.prime.getH(), *pr) # Animate the Character's Actor. The Character automatically changes # between stand, run and walk animations and varies the playrate of the # animations depending on the speed of the Character's Vehicle. speed = self.getspeed() if speed < .05: if self.getCurrentOrNextState() != 'Stand': self.request('Stand') elif speed < .25: self.actor.setPlayRate(speed * 4, "walk") if self.getCurrentOrNextState() != 'Walk': self.request('Walk') else: self.actor.setPlayRate(speed * 2, "run") if self.getCurrentOrNextState() != 'Run': self.request('Run') return Task.cont def destroy(self): """Prepare this Character to be garbage-collected by Python: Remove all of the Character's nodes from the scene graph, remove its CollisionSolids from the global CollisionTraverser, clear its CollisionHandler, remove tasks, destroy Actor. After executing this method, any remaining references to the Character object can be destroyed by the user module, and the Character will be garbage-collected by Python. """ taskMgr.remove(self.characterStepTask) self.cleanup() self.actor.delete() Vehicle.destroy(self) # Methods for handling animations. def storeLastPose(self): currAnim = self.actor.getCurrentAnim() numFrames = self.actor.getNumFrames(currAnim) animFrame = self.actor.getCurrentFrame(currAnim) self.lastPose = float(animFrame) / float(numFrames) self.actor.stop(currAnim) def loopFromPose(self, animName): self.actor.pose(animName, frame=self.lastPose * self.actor.getNumFrames(animName)) self.actor.loop(animName, restart=0) # FSM State handlers. Called when transitioning to a new state. def enterRun(self): self.loopFromPose("run") def exitRun(self): self.storeLastPose() def enterWalk(self): self.loopFromPose("walk") def exitWalk(self): self.storeLastPose() def enterStand(self): standPoseFrame = 6 # frame 6 (the most acceptable stand pose) numFrames = self.actor.getNumFrames("walk") lastFrame = self.lastPose * numFrames # "mirror" the frame to bring it closer to the most acceptable stand pose if lastFrame > .5 * (numFrames - 1): lastFrame = numFrames - 1 - lastFrame frameDiff = standPoseFrame - lastFrame # if already at stand pose, don't do anything if frameDiff == 0: return # forward animation playback if frameDiff >= 0: fromFrame = lastFrame toFrame = standPoseFrame else: # backward animation playback fromFrame = standPoseFrame toFrame = lastFrame playDir = 2 * frameDiff / numFrames self.actor.setPlayRate(playDir, "walk") self.actor.play("walk", fromFrame=fromFrame, toFrame=toFrame)
class PlayerBase(DirectObject): def __init__(self): # Player Model setup self.player = Actor("Player", {"Run":"Player-Run", "Sidestep":"Player-Sidestep", "Idle":"Player-Idle"}) self.player.setBlend(frameBlend = True) self.player.setPos(0, 0, 0) self.player.pose("Idle", 0) self.player.reparentTo(render) self.player.hide() self.footstep = base.audio3d.loadSfx('footstep.ogg') self.footstep.setLoop(True) base.audio3d.attachSoundToObject(self.footstep, self.player) # Create a brush to paint on the texture splat = PNMImage("../data/Splat.png") self.colorBrush = PNMBrush.makeImage(splat, 6, 6, 1) CamMask = BitMask32.bit(0) AvBufMask = BitMask32.bit(1) self.avbuf = None if base.win: self.avbufTex = Texture('avbuf') self.avbuf = base.win.makeTextureBuffer('avbuf', 256, 256, self.avbufTex, True) cam = Camera('avbuf') cam.setLens(base.camNode.getLens()) self.avbufCam = base.cam.attachNewNode(cam) dr = self.avbuf.makeDisplayRegion() dr.setCamera(self.avbufCam) self.avbuf.setActive(False) self.avbuf.setClearColor((1, 0, 0, 1)) cam.setCameraMask(AvBufMask) base.camNode.setCameraMask(CamMask) # avbuf renders everything it sees with the gradient texture. tex = loader.loadTexture('gradient.png') np = NodePath('np') np.setTexture(tex, 100) np.setColor((1, 1, 1, 1), 100) np.setColorScaleOff(100) np.setTransparency(TransparencyAttrib.MNone, 100) np.setLightOff(100) cam.setInitialState(np.getState()) #render.hide(AvBufMask) # Setup a texture stage to paint on the player self.paintTs = TextureStage('paintTs') self.paintTs.setMode(TextureStage.MDecal) self.paintTs.setSort(10) self.paintTs.setPriority(10) self.tex = Texture('paint_av_%s'%id(self)) # Setup a PNMImage that will hold the paintable texture of the player self.imageSizeX = 64 self.imageSizeY = 64 self.p = PNMImage(self.imageSizeX, self.imageSizeY, 4) self.p.fill(1) self.p.alphaFill(0) self.tex.load(self.p) self.tex.setWrapU(self.tex.WMClamp) self.tex.setWrapV(self.tex.WMClamp) # Apply the paintable texture to the avatar self.player.setTexture(self.paintTs, self.tex) # team self.playerTeam = "" # A lable that will display the players team self.lblTeam = DirectLabel( scale = 1, pos = (0, 0, 3), frameColor = (0, 0, 0, 0), text = "TEAM", text_align = TextNode.ACenter, text_fg = (0,0,0,1)) self.lblTeam.reparentTo(self.player) self.lblTeam.setBillboardPointEye() # basic player values self.maxHits = 3 self.currentHits = 0 self.isOut = False self.TorsorControl = self.player.controlJoint(None,"modelRoot","Torsor") # setup the collision detection # wall and object collision self.playerSphere = CollisionSphere(0, 0, 1, 1) self.playerCollision = self.player.attachNewNode(CollisionNode("playerCollision%d"%id(self))) self.playerCollision.node().addSolid(self.playerSphere) base.pusher.addCollider(self.playerCollision, self.player) base.cTrav.addCollider(self.playerCollision, base.pusher) # foot (walk) collision self.playerFootRay = self.player.attachNewNode(CollisionNode("playerFootCollision%d"%id(self))) self.playerFootRay.node().addSolid(CollisionRay(0, 0, 2, 0, 0, -1)) self.playerFootRay.node().setIntoCollideMask(0) self.lifter = CollisionHandlerFloor() self.lifter.addCollider(self.playerFootRay, self.player) base.cTrav.addCollider(self.playerFootRay, self.lifter) # Player weapon setup self.gunAttach = self.player.exposeJoint(None, "modelRoot", "WeaponSlot_R") self.color = LPoint3f(1, 1, 1) self.gun = Gun(id(self)) self.gun.reparentTo(self.gunAttach) self.gun.hide() self.gun.setColor(self.color) self.hud = None # Player controls setup self.keyMap = {"left":0, "right":0, "forward":0, "backward":0} # screen sizes self.winXhalf = base.win.getXSize() / 2 self.winYhalf = base.win.getYSize() / 2 self.mouseSpeedX = 0.1 self.mouseSpeedY = 0.1 # AI controllable variables self.AIP = 0.0 self.AIH = 0.0 self.movespeed = 5.0 self.userControlled = False self.accept("Bulet-hit-playerCollision%d" % id(self), self.hit) self.accept("window-event", self.recalcAspectRatio) def runBase(self): self.player.show() self.gun.show() taskMgr.add(self.move, "moveTask%d"%id(self), priority=-4) def stopBase(self): taskMgr.remove("moveTask%d"%id(self)) self.ignoreAll() self.gun.remove() self.footstep.stop() base.audio3d.detachSound(self.footstep) self.player.delete() def setKey(self, key, value): self.keyMap[key] = value def setPos(self, pos): self.player.setPos(pos) def setColor(self, color=LPoint3f(0,0,0)): self.color = color self.gun.setColor(color) c = (color[0], color[1], color[2], 1.0) self.lblTeam["text_fg"] = c def setTeam(self, team): self.playerTeam = team self.lblTeam["text"] = team def shoot(self, shotVec=None): self.gun.shoot(shotVec) if self.hud != None: self.hud.updateAmmo(self.gun.maxAmmunition, self.gun.ammunition) def reload(self): self.gun.reload() if self.hud != None: self.hud.updateAmmo(self.gun.maxAmmunition, self.gun.ammunition) def recalcAspectRatio(self, window): self.winXhalf = window.getXSize() / 2 self.winYhalf = window.getYSize() / 2 def hit(self, entry, color): self.currentHits += 1 # Create a brush to paint on the texture splat = PNMImage("../data/Splat.png") splat = splat * LColorf(color[0], color[1], color[2], 1.0) self.colorBrush = PNMBrush.makeImage(splat, 6, 6, 1) self.paintAvatar(entry) if self.currentHits >= self.maxHits: base.messenger.send("GameOver-player%d" % id(self)) self.isOut = True def __paint(self, s, t): """ Paints a point on the avatar at texture coordinates (s, t). """ x = (s * self.p.getXSize()) y = ((1.0 - t) * self.p.getYSize()) # Draw in color directly on the avatar p1 = PNMPainter(self.p) p1.setPen(self.colorBrush) p1.drawPoint(x, y) self.tex.load(self.p) self.tex.setWrapU(self.tex.WMClamp) self.tex.setWrapV(self.tex.WMClamp) self.paintDirty = True def paintAvatar(self, entry): """ Paints onto an avatar. Returns true on success, false on failure (because there are no avatar pixels under the mouse, for instance). """ # First, we have to render the avatar in its false-color # image, to determine which part of its texture is under the # mouse. if not self.avbuf: return False #mpos = base.mouseWatcherNode.getMouse() mpos = entry.getSurfacePoint(self.player) ppos = entry.getSurfacePoint(render) self.player.showThrough(BitMask32.bit(1)) self.avbuf.setActive(True) base.graphicsEngine.renderFrame() self.player.show(BitMask32.bit(1)) self.avbuf.setActive(False) # Now we have the rendered image in self.avbufTex. if not self.avbufTex.hasRamImage(): print "Weird, no image in avbufTex." return False p = PNMImage() self.avbufTex.store(p) ix = int((1 + mpos.getX()) * p.getXSize() * 0.5) iy = int((1 - mpos.getY()) * p.getYSize() * 0.5) x = 1 if ix >= 0 and ix < p.getXSize() and iy >= 0 and iy < p.getYSize(): s = p.getBlue(ix, iy) t = p.getGreen(ix, iy) x = p.getRed(ix, iy) if x > 0.5: # Off the avatar. return False # At point (s, t) on the avatar's map. self.__paint(s, t) return True def move(self, task): if self is None: return task.done if self.userControlled: if not base.mouseWatcherNode.hasMouse(): return task.cont self.pointer = base.win.getPointer(0) mouseX = self.pointer.getX() mouseY = self.pointer.getY() if base.win.movePointer(0, self.winXhalf, self.winYhalf): p = self.TorsorControl.getP() + (mouseY - self.winYhalf) * self.mouseSpeedY if p <-80: p = -80 elif p > 90: p = 90 self.TorsorControl.setP(p) h = self.player.getH() - (mouseX - self.winXhalf) * self.mouseSpeedX if h <-360: h = 360 elif h > 360: h = -360 self.player.setH(h) else: self.TorsorControl.setP(self.AIP) self.player.setH(self.AIH) forward = self.keyMap["forward"] != 0 backward = self.keyMap["backward"] != 0 if self.keyMap["left"] != 0: if self.player.getCurrentAnim() != "Sidestep" and not (forward or backward): self.player.loop("Sidestep") self.player.setPlayRate(5, "Sidestep") self.player.setX(self.player, self.movespeed * globalClock.getDt()) elif self.keyMap["right"] != 0: if self.player.getCurrentAnim() != "Sidestep" and not (forward or backward): self.player.loop("Sidestep") self.player.setPlayRate(5, "Sidestep") self.player.setX(self.player, -self.movespeed * globalClock.getDt()) else: self.player.stop("Sidestep") if forward: if self.player.getCurrentAnim() != "Run": self.player.loop("Run") self.player.setPlayRate(5, "Run") self.player.setY(self.player, -self.movespeed * globalClock.getDt()) elif backward: if self.player.getCurrentAnim() != "Run": self.player.loop("Run") self.player.setPlayRate(-5, "Run") self.player.setY(self.player, self.movespeed * globalClock.getDt()) else: self.player.stop("Run") if not (self.keyMap["left"] or self.keyMap["right"] or self.keyMap["forward"] or self.keyMap["backward"] or self.player.getCurrentAnim() == "Idle"): self.player.loop("Idle") self.footstep.stop() else: self.footstep.play() return task.cont
class MachineGun: def __init__(self, cycle, mount, audio3D): self.cycle = cycle self.name = "JR Martin J59 Jabber" self.actor = Actor("../Models/MGActor.egg") self.model = loader.loadModel("../Models/MachineGun.bam") self.actor.reparentTo(mount) self.model.reparentTo(self.actor) self.flashModel = loader.loadModel("../Models/LaserFlash.bam") self.projModel = loader.loadModel("../Models/LaserProj.bam") self.projModel.setScale(.25, 1, .25) self.refNP = self.cycle.trgtrMount.attachNewNode("MGRefNP") self.muzzle = self.actor.exposeJoint(None, "modelRoot", "Muzzle") self.audio3D = audio3D self.fireSfx = self.audio3D.loadSfx("../Sound/LaserShot.wav") self.audio3D.attachSoundToObject(self.fireSfx, self.muzzle) reloadTime = .25 self.damage = 10 self.energyCost = 1.25 self.flashLerp = LerpScaleInterval(self.flashModel, reloadTime * .75, Point3(1, 1, 1), Point3(.1, .1, .1)) self.firePar = Parallel(Func(self.checkForHit), Func(self.setEffects), self.flashLerp) self.fireSeq = Sequence(self.firePar, Func(self.clearEffects), Wait(reloadTime * .25)) def fire(self): if (self.fireSeq.isPlaying() == False): self.fireSeq.start() self.fireSfx.play() self.cycle.energy -= self.energyCost return def setEffects(self): self.flashModel.reparentTo(self.muzzle) self.projModel.reparentTo(self.muzzle) self.projModel.lookAt(self.refNP.getPos(self.muzzle)) self.projModel.setSy( trueDist(Point3(0, 0, 0), self.refNP.getPos(self.muzzle)) * 2) return def clearEffects(self): self.flashModel.detachNode() self.projModel.detachNode() return def checkForHit(self): self.cycle.trgtrCTrav.traverse(render) if (self.cycle.trgtrCHan.getNumEntries() > 0): self.cycle.trgtrCHan.sortEntries() entry = self.cycle.trgtrCHan.getEntry(0) # If collisions were detected sort them nearest to far and pull out the first one. colPoint = entry.getSurfacePoint(render) self.refNP.setPos(render, colPoint) # Get the collision point and the range to the collision. pop = Pop(colPoint) # Create an explosion at the collision point. thingHit = entry.getIntoNodePath() if (thingHit.hasPythonTag("owner")): thingHit.getPythonTag("owner").hit(self.damage) else: self.refNP.setPos(self.cycle.trgtrCNP, 0, 300, 0) pop = Pop(self.cycle.refNP.getPos(render)) # If no collision was detected, create a pop at a distant point. def destroy(self): self.actor.delete() self.model.removeNode() self.flashModel.removeNode() self.projModel.removeNode() self.refNP.removeNode() self.cycle = None self.flashLerp = None self.firePar = None self.fireSeq = None self.audio3D.detachSound(self.fireSfx) return
class DistributedPartyFireworksActivity(DistributedPartyActivity, FireworkShowMixin): notify = directNotify.newCategory("DistributedPartyFireworksActivity") def __init__(self, cr): """ cr: instance of ClientRepository """ DistributedPartyFireworksActivity.notify.debug("__init__") DistributedPartyActivity.__init__(self, cr, ActivityIds.PartyFireworks, ActivityTypes.HostInitiated, wantLever=True) FireworkShowMixin.__init__(self, restorePlaygroundMusic=True, startDelay=FireworksPostLaunchDelay) def setEventId(self, eventId): DistributedPartyFireworksActivity.notify.debug( "setEventId( %s )" % FireworkShows.getString(eventId)) self.eventId = eventId def setShowStyle(self, showStyle): DistributedPartyFireworksActivity.notify.debug("setShowStyle( %d )" % showStyle) self.showStyle = showStyle def load(self): """ Load the necessary assets """ DistributedPartyFireworksActivity.notify.debug("load") DistributedPartyActivity.load(self) self.eventId = PartyGlobals.FireworkShows.Summer # load the rocket platform and place it in party space self.launchPadModel = loader.loadModel( 'phase_13/models/parties/launchPad') # Compensate for pivot of fireworks model and center it in 2x4 space self.launchPadModel.setH(90.0) self.launchPadModel.setPos(0.0, -18.0, 0.0) # reparent to root self.launchPadModel.reparentTo(self.root) # special case the depth testing on the railings to prevent # transparency oddness railingsCollection = self.launchPadModel.findAllMatches( "**/launchPad_mesh/*railing*") for i in range(railingsCollection.getNumPaths()): railingsCollection[i].setAttrib( AlphaTestAttrib.make(RenderAttrib.MGreater, 0.75)) # place the lever on the platform leverLocator = self.launchPadModel.find("**/RocketLever_locator") self.lever.setPosHpr(Vec3.zero(), Vec3.zero()) self.lever.reparentTo(leverLocator) self.toonPullingLeverInterval = None # place the activity sign self.sign.reparentTo( self.launchPadModel.find("**/launchPad_sign_locator")) # load the rocket with animation and place it on the platform self.rocketActor = Actor( 'phase_13/models/parties/rocket_model', {'launch': 'phase_13/models/parties/rocket_launch'}, ) rocketLocator = self.launchPadModel.find("**/rocket_locator") self.rocketActor.reparentTo(rocketLocator) # ensure the rocket is never culled self.rocketActor.node().setBound(OmniBoundingVolume()) self.rocketActor.node().setFinal(True) effectsLocator = self.rocketActor.find("**/joint1") self.rocketExplosionEffect = RocketExplosion(effectsLocator, rocketLocator) self.rocketParticleSeq = None self.launchSound = base.loader.loadSfx( "phase_13/audio/sfx/rocket_launch.mp3") # create state machine and set initial state self.activityFSM = FireworksActivityFSM(self) self.activityFSM.request("Idle") def unload(self): DistributedPartyFireworksActivity.notify.debug("unload") taskMgr.remove(self.taskName("delayedStartShow")) if self.rocketParticleSeq: self.rocketParticleSeq.pause() self.rocketParticleSeq = None self.launchPadModel.removeNode() del self.launchPadModel del self.toonPullingLeverInterval self.rocketActor.delete() self.rocketExplosionEffect.destroy() self.activityFSM.request("Disabled") del self.rocketActor del self.launchSound del self.activityFSM del self.eventId del self.showStyle DistributedPartyActivity.unload(self) def _leverPulled(self, collEntry): DistributedPartyFireworksActivity.notify.debug("_leverPulled") hostPulledLever = DistributedPartyActivity._leverPulled( self, collEntry) if self.activityFSM.getCurrentOrNextState() == "Active": self.showMessage(TTLocalizer.PartyFireworksAlreadyActive) elif self.activityFSM.getCurrentOrNextState() == "Disabled": self.showMessage(TTLocalizer.PartyFireworksAlreadyDone) elif self.activityFSM.getCurrentOrNextState() == "Idle": if hostPulledLever: base.cr.playGame.getPlace().fsm.request( "activity") # prevent toon from moving self.toonPullingLeverInterval = self.getToonPullingLeverInterval( base.localAvatar) self.toonPullingLeverInterval.append( Func(self.d_toonJoinRequest)) self.toonPullingLeverInterval.append( Func(base.cr.playGame.getPlace().fsm.request, 'walk')) self.toonPullingLeverInterval.start() else: self.showMessage(TTLocalizer.PartyOnlyHostLeverPull) # FSM utility methods def setState(self, newState, timestamp): DistributedPartyFireworksActivity.notify.debug( "setState( newState=%s, ... )" % newState) DistributedPartyActivity.setState(self, newState, timestamp) # pass additional parameters only to those states that need it if newState == "Active": self.activityFSM.request(newState, timestamp) else: self.activityFSM.request(newState) # FSM transition methods def startIdle(self): DistributedPartyFireworksActivity.notify.debug("startIdle") def finishIdle(self): DistributedPartyFireworksActivity.notify.debug("finishIdle") def startActive(self, showStartTimestamp): DistributedPartyFireworksActivity.notify.debug("startActive") messenger.send(FireworksStartedEvent) # if too much time has passed since the show started, don't bother # playing the rocket animation, just hide it timeSinceStart = globalClockDelta.localElapsedTime(showStartTimestamp) if timeSinceStart > self.rocketActor.getDuration("launch"): self.rocketActor.hide() self.startShow(self.eventId, self.showStyle, showStartTimestamp) else: self.rocketActor.play("launch") self.rocketParticleSeq = Sequence(Wait(RocketSoundDelay), \ Func(base.playSfx,self.launchSound), \ Func(self.rocketExplosionEffect.start), \ Wait(RocketDirectionDelay), \ LerpHprInterval(self.rocketActor, 4.0, Vec3(0, 0, -60)), \ Func(self.rocketExplosionEffect.end), \ Func(self.rocketActor.hide)) self.rocketParticleSeq.start() # give rocket animation some time to play out before starting the show taskMgr.doMethodLater( FireworksPostLaunchDelay, self.startShow, self.taskName("delayedStartShow"), extraArgs=[ self.eventId, self.showStyle, showStartTimestamp, self.root ], ) def finishActive(self): self.rocketParticleSeq = None DistributedPartyFireworksActivity.notify.debug("finishActive") messenger.send(FireworksFinishedEvent) taskMgr.remove(self.taskName("delayedStartShow")) FireworkShowMixin.disable(self) # stop the fireworks show def startDisabled(self): DistributedPartyFireworksActivity.notify.debug("startDisabled") if not self.rocketActor.isEmpty(): self.rocketActor.hide() # so late comers won't see the rocket def finishDisabled(self): DistributedPartyFireworksActivity.notify.debug("finishDisabled") def handleToonDisabled(self, toonId): """ A toon dropped unexpectedly from the game. Handle it! """ self.notify.warning("handleToonDisabled no implementation yet")
class DistributedPartyJukeboxActivityBase(DistributedPartyActivity): notify = directNotify.newCategory("DistributedPartyJukeboxActivityBase") def __init__(self, cr, actId, phaseToMusicData): DistributedPartyActivity.__init__(self, cr, actId, ActivityTypes.Continuous) self.phaseToMusicData = phaseToMusicData self.jukebox = None self.gui = None self.tunes = [] self.music = None self.currentSongData = None self.localQueuedSongInfo = None self.localQueuedSongListItem = None return def generateInit(self): self.gui = JukeboxGui(self.phaseToMusicData) def load(self): DistributedPartyActivity.load(self) self.jukebox = Actor( "phase_13/models/parties/jukebox_model", {"dance": "phase_13/models/parties/jukebox_dance"} ) self.jukebox.reparentTo(self.root) self.jukebox.loop("dance", fromFrame=0, toFrame=48) self.collNode = CollisionNode(self.getCollisionName()) self.collNode.setCollideMask(ToontownGlobals.CameraBitmask | ToontownGlobals.WallBitmask) collTube = CollisionTube(0, 0, 0, 0.0, 0.0, 4.25, 2.25) collTube.setTangible(1) self.collNode.addSolid(collTube) self.collNodePath = self.jukebox.attachNewNode(self.collNode) self.sign.setPos(-5.0, 0, 0) self.activate() def unload(self): DistributedPartyActivity.unload(self) self.gui.unload() if self.music is not None: self.music.stop() self.jukebox.stop() self.jukebox.delete() self.jukebox = None self.ignoreAll() return def getCollisionName(self): return self.uniqueName("jukeboxCollision") def activate(self): self.accept("enter" + self.getCollisionName(), self.__handleEnterCollision) def __handleEnterCollision(self, collisionEntry): if base.cr.playGame.getPlace().fsm.getCurrentState().getName() == "walk": base.cr.playGame.getPlace().fsm.request("activity") self.d_toonJoinRequest() def joinRequestDenied(self, reason): DistributedPartyActivity.joinRequestDenied(self, reason) self.showMessage(TTLocalizer.PartyJukeboxOccupied) def handleToonJoined(self, toonId): toon = base.cr.doId2do.get(toonId) if toon: self.jukebox.lookAt(base.cr.doId2do[toonId]) self.jukebox.setHpr(self.jukebox.getH() + 180.0, 0, 0) if toonId == base.localAvatar.doId: self.__localUseJukebox() def handleToonExited(self, toonId): if toonId == base.localAvatar.doId and self.gui.isLoaded(): self.__deactivateGui() def handleToonDisabled(self, toonId): self.notify.warning("handleToonDisabled no implementation yet") def __localUseJukebox(self): base.localAvatar.disableAvatarControls() base.localAvatar.stopPosHprBroadcast() self.__activateGui() self.accept(JukeboxGui.CLOSE_EVENT, self.__handleGuiClose) taskMgr.doMethodLater( 0.5, self.__localToonWillExitTask, self.uniqueName("toonWillExitJukeboxOnTimeout"), extraArgs=None ) self.accept(JukeboxGui.ADD_SONG_CLICK_EVENT, self.__handleQueueSong) if self.isUserHost(): self.accept(JukeboxGui.MOVE_TO_TOP_CLICK_EVENT, self.__handleMoveSongToTop) return def __localToonWillExitTask(self, task): self.localToonExiting() return Task.done def __activateGui(self): self.gui.enable(timer=JUKEBOX_TIMEOUT) self.gui.disableAddSongButton() if self.currentSongData is not None: self.gui.setSongCurrentlyPlaying(self.currentSongData[0], self.currentSongData[1]) self.d_queuedSongsRequest() return def __deactivateGui(self): self.ignore(JukeboxGui.CLOSE_EVENT) self.ignore(JukeboxGui.SONG_SELECT_EVENT) self.ignore(JukeboxGui.MOVE_TO_TOP_CLICK_EVENT) base.cr.playGame.getPlace().setState("walk") base.localAvatar.startPosHprBroadcast() base.localAvatar.enableAvatarControls() self.gui.unload() self.__localClearQueuedSong() def isUserHost(self): return self.party.partyInfo.hostId == base.localAvatar.doId def d_queuedSongsRequest(self): self.sendUpdate("queuedSongsRequest") def queuedSongsResponse(self, songInfoList, index): if self.gui.isLoaded(): for i in range(len(songInfoList)): songInfo = songInfoList[i] self.__addSongToQueue(songInfo, isLocalQueue=index >= 0 and i == index) self.gui.enableAddSongButton() def __handleGuiClose(self): self.__deactivateGui() self.d_toonExitDemand() def __handleQueueSong(self, name, values): self.d_setNextSong(values[0], values[1]) def d_setNextSong(self, phase, filename): self.sendUpdate("setNextSong", [(phase, filename)]) def setSongInQueue(self, songInfo): if self.gui.isLoaded(): phase = sanitizePhase(songInfo[0]) filename = songInfo[1] data = self.getMusicData(phase, filename) if data: if self.localQueuedSongListItem is not None: self.localQueuedSongListItem["text"] = data[0] else: self.__addSongToQueue(songInfo, isLocalQueue=True) return def __addSongToQueue(self, songInfo, isLocalQueue=False): isHost = isLocalQueue and self.isUserHost() data = self.getMusicData(sanitizePhase(songInfo[0]), songInfo[1]) if data: listItem = self.gui.addSongToQueue(data[0], highlight=isLocalQueue, moveToTopButton=isHost) if isLocalQueue: self.localQueuedSongInfo = songInfo self.localQueuedSongListItem = listItem def __localClearQueuedSong(self): self.localQueuedSongInfo = None self.localQueuedSongListItem = None return def __play(self, phase, filename, length): self.music = base.loadMusic((MUSIC_PATH + "%s") % (phase, filename)) if self.music: if ( self.__checkPartyValidity() and hasattr(base.cr.playGame.getPlace().loader, "music") and base.cr.playGame.getPlace().loader.music ): base.cr.playGame.getPlace().loader.music.stop() self.music.setTime(0.0) self.music.setLoopCount(getMusicRepeatTimes(length)) self.music.play() self.currentSongData = (phase, filename) def __stop(self): self.currentSongData = None if self.music: self.music.stop() if self.gui.isLoaded(): self.gui.clearSongCurrentlyPlaying() return def setSongPlaying(self, songInfo, toonId): phase = sanitizePhase(songInfo[0]) filename = songInfo[1] if not filename: self.__stop() return data = self.getMusicData(phase, filename) if data: self.__play(phase, filename, data[1]) self.setSignNote(data[0]) if self.gui.isLoaded(): item = self.gui.popSongFromQueue() self.gui.setSongCurrentlyPlaying(phase, filename) if item == self.localQueuedSongListItem: self.__localClearQueuedSong() if toonId == localAvatar.doId: localAvatar.setSystemMessage(0, TTLocalizer.PartyJukeboxNowPlaying) def __handleMoveSongToTop(self): if self.isUserHost() and self.localQueuedSongListItem is not None: self.d_moveHostSongToTopRequest() return def d_moveHostSongToTopRequest(self): self.notify.debug("d_moveHostSongToTopRequest") self.sendUpdate("moveHostSongToTopRequest") def moveHostSongToTop(self): self.notify.debug("moveHostSongToTop") if self.gui.isLoaded(): self.gui.pushQueuedItemToTop(self.localQueuedSongListItem) def getMusicData(self, phase, filename): data = [] phase = sanitizePhase(phase) phase = self.phaseToMusicData.get(phase) if phase: data = phase.get(filename, []) return data def __checkPartyValidity(self): if ( hasattr(base.cr.playGame, "getPlace") and base.cr.playGame.getPlace() and hasattr(base.cr.playGame.getPlace(), "loader") and base.cr.playGame.getPlace().loader ): return True else: return False
class BossBattleHealthBar(DirectFrame): def __init__(self, dept, maxHp, **kw): DirectFrame.__init__(self, parent=render2d, relief=None, **kw) self.dept = dept self.filePrefix = ModelDict[dept] self.maxHp = float(maxHp) self.hp = self.maxHp self.head = None self.headActor = None self.animDict = {} self.healthBar = None self.healthCondition = None self.hitInterval = None self.blinkTask = None self.dizzy = False self.helmet = False self.healthColors = Suit.Suit.healthColors def load(self): self.head = loader.loadModel(self.filePrefix + '-head-zero') for anim in AnimList: self.animDict[anim] = '%s-%s-%s' % (GenericModel, 'head', anim) self.headActor = Actor(self.head, self.animDict) self.headActor.hide() self.headActor.setBin("fixed", 40) self.headActor.setDepthTest(True) self.headActor.setDepthWrite(True) self.headActor.reparentTo(self) self.headActor.setHpr(-90, 0, 270) self.headActor.setScale(0.021) self.headActor.setPos(-0.25, 0.0, 0.75) self.headActor.setPlayRate(2.0, 'turn2Fb') self.headActor.loop('Ff_neutral') self.eyes = loader.loadModel('phase_10/models/cogHQ/CashBotBossEyes.bam') self.eyes.setPosHprScale(4.5, 0, -2.5, 90, 90, 0, 0.4, 0.4, 0.4) self.eyes.reparentTo(self.headActor) self.eyes.hide() self.stars = globalPropPool.getProp('stun') self.stars.setPosHprScale(7, 0, 0, 0, 0, -90, 3, 3, 3) self.stars.loop('stun') self.safe = loader.loadModel('phase_10/models/cogHQ/CBSafe.bam') self.safe.reparentTo(self.headActor) self.safe.setPosHpr(-1, 0, 0.2, 0, -90, 90) self.safe.setBin("fixed", 40) self.safe.setDepthTest(True) self.safe.setDepthWrite(True) self.safe.hide() self.headActor.show() self.healthBar = DirectWaitBar(parent=self, pos=(0, 0, 0.85), relief=DGG.SUNKEN, frameSize=(-1.75, 1.75, -0.3, 0.3), borderWidth=(0.02, 0.02), scale=0.1, range=1, sortOrder=50, frameColor=(0.5, 0.5, 0.5, 0.5), barColor=(0.75, 0.75, 1.0, 0.8), text='', text_scale=0.35, text_fg=(1, 1, 1, 1), text_align=TextNode.ACenter, text_pos=(0, -0.05), text_font=getSuitFont()) self.updateHealthBar(self.maxHp) def getHealthCondition(self, hp): hp /= self.maxHp print hp if hp > 0.95: condition = 0 elif hp > 0.9: condition = 1 elif hp > 0.8: condition = 2 elif hp > 0.7: condition = 3 elif hp > 0.6: condition = 4 elif hp > 0.5: condition = 5 elif hp > 0.3: condition = 6 elif hp > 0.15: condition = 7 elif hp > 0.05: condition = 8 elif hp > 0.0: condition = 9 else: condition = 9 return condition def updateHealthBar(self, hp): self.hp = float(hp) self.healthCondition = self.getHealthCondition(hp) if self.healthCondition == 9 and hp > 0: if self.blinkTask is None: self.startBlinkTask() elif self.blinkTask: self.stopBlinkTask() if self.healthBar: self.healthBar.setProp('text', str(int(hp))) self.healthBar.setProp('barColor', self.healthColors[self.healthCondition]) self.healthBar.setProp('value', hp / self.maxHp) self.doHit() def cleanupHit(self): if self.hitInterval: self.hitInterval.finish() self.hitInterval = None return def doHit(self): self.cleanupHit() if not self.headActor: return self.hitInterval = Sequence( Parallel( Sequence( Func(self.headActor.setColorScale, 1, 1, 1, 1), self.headActor.colorScaleInterval(0.1, colorScale=VBase4(1, 0, 0, 1)), self.headActor.colorScaleInterval(0.3, colorScale=VBase4(1, 1, 1, 1)) ), ActorInterval(self.headActor, 'turn2Fb') ), Func(self.headActor.loop, 'Ff_neutral') ) self.hitInterval.start() def startBlinkTask(self): self.blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.75), Task(self.__blinkGray), Task.pause(0.1)) taskMgr.add(self.blinkTask, 'bosshealthbar-blink-task') def __blinkRed(self, task): if not self.healthBar: return self.healthBar.setProp('barColor', self.healthColors[8]) return Task.done def __blinkGray(self, task): if not self.healthBar: return self.healthBar.setProp('barColor', self.healthColors[9]) return Task.done def stopBlinkTask(self): taskMgr.remove('bosshealthbar-blink-task') self.blinkTask = None def setDizzy(self, dizzy): self.dizzy = dizzy if dizzy: self.stars.reparentTo(self.headActor) else: self.stars.detachNode() def setHelmet(self, helmet): self.helmet = helmet if helmet: self.safe.show() self.eyes.show() else: self.safe.hide() self.eyes.hide() def destroy(self): self.cleanupHit() self.stars.cleanup() self.stopBlinkTask() self.healthBar.destroy() self.headActor.delete() self.head.removeNode() self.safe.removeNode() self.eyes.removeNode()