import random from direct.showbase.DirectObject import DirectObject from direct.interval.IntervalGlobal import LerpFunc, ActorInterval, LerpPosInterval from direct.interval.MetaInterval import Sequence from direct.directutil import Mopath from panda3d.core import * from panda3d.physics import * from toontown.toonbase import ToontownGlobals from toontown.suit import Suit from toontown.suit import SuitDNA from toontown.battle import BattleProps from . import CogdoUtil from . import CogdoFlyingGameGlobals as Globals from .CogdoFlyingUtil import swapAvatarShadowPlacer from direct.particles import ParticleEffect from direct.particles import Particles from direct.particles import ForceGroup from enum import IntEnum class CogdoFlyingObtacleFactory: def __init__(self): self._index = -1 self._whirlwindModel = CogdoUtil.loadFlyingModel('whirlwind').find('**/whirlwind') self._fanModel = CogdoUtil.loadFlyingModel('streamer').find('**/streamer') def destroy(self): self._whirlwindModel.removeNode() del self._whirlwindModel self._fanModel.removeNode() del self._fanModel if Globals.Level.AddParticlesToStreamers: self.f.cleanup() del self.f def createFan(self): self._index += 1 return CogdoFlyingFan(self._index, self._fanModel) def createFlyingMinion(self, motionPath = None): self._index += 1 return CogdoFlyingMinionFlying(self._index, motionPath=motionPath) def createWalkingMinion(self, motionPath = None): self._index += 1 return CogdoFlyingMinionWalking(self._index, motionPath=motionPath) def createWhirlwind(self, motionPath = None): self._index += 1 return CogdoFlyingWhirlwind(self._index, self._whirlwindModel, motionPath=motionPath) def createStreamerParticles(self, color1, color2, amp): self.f = ParticleEffect.ParticleEffect('streamer_particles') p0 = Particles.Particles('particles-1') p0.setFactory('PointParticleFactory') p0.setRenderer('SparkleParticleRenderer') p0.setEmitter('RingEmitter') p0.setPoolSize(80) p0.setBirthRate(0.05) p0.setLitterSize(100) p0.setLitterSpread(0) p0.factory.setLifespanBase(3.0) p0.factory.setLifespanSpread(0.5) p0.factory.setMassBase(1.0) p0.factory.setMassSpread(0.0) p0.factory.setTerminalVelocityBase(5.0) p0.factory.setTerminalVelocitySpread(1.0) p0.renderer.setAlphaMode(BaseParticleRenderer.PRALPHAOUT) p0.renderer.setUserAlpha(1.0) p0.renderer.setCenterColor(color1) p0.renderer.setEdgeColor(color2) p0.renderer.setBirthRadius(0.3) p0.renderer.setDeathRadius(0.3) p0.renderer.setLifeScale(SparkleParticleRenderer.SPNOSCALE) p0.emitter.setEmissionType(BaseParticleEmitter.ETRADIATE) p0.emitter.setAmplitude(0) p0.emitter.setAmplitudeSpread(0) f0 = ForceGroup.ForceGroup('Gravity') force0 = LinearVectorForce(Vec3(0.0, 0.0, 10.0), 1.0, 0) force0.setVectorMasks(1, 1, 1) force0.setActive(1) f0.addForce(force0) self.f.addForceGroup(f0) p0.emitter.setRadius(5.0) self.f.addParticles(p0) self.f.setPos(0, 0, 0) self.f.setHpr(0, 0, 0) return self.f class CogdoFlyingObstacle(DirectObject): EnterEventName = 'CogdoFlyingObstacle_Enter' ExitEventName = 'CogdoFlyingObstacle_Exit' MotionTypes = IntEnum('MotionTypes', ('BackForth', 'Loop'), start=0) def __init__(self, type, index, model, collSolid, motionPath = None, motionPattern = None, blendMotion = True, instanceModel = True): self.type = type self.index = index name = 'CogdoFlyingObstacle-%s-%i' % (self.type, self.index) if instanceModel: self.model = NodePath(name) model.instanceTo(self.model) else: self.model = model self.model.setName(name) self.currentT = 0.0 self.direction = 1.0 self.collNode = None self._initCollisions(name, collSolid) self.motionPath = motionPath self.motionPattern = motionPattern self.motionSequence = None if blendMotion: blendType = 'easeInOut' else: blendType = 'noBlend' if motionPath is not None: def moveObstacle(value): self.motionPath.goTo(self.model, value) self.motionPath = Mopath.Mopath(name='obstacle-%i' % self.index) self.motionPath.loadNodePath(motionPath) dur = self.motionPath.getMaxT() self.motionSequence = Sequence(name='%s.obstacle-%i-motionSequence' % (self.__class__.__name__, self.index)) movePart1 = LerpFunc(moveObstacle, fromData=0.0, toData=self.motionPath.getMaxT(), duration=dur, blendType=blendType) self.motionSequence.append(movePart1) if self.motionPattern == CogdoFlyingObstacle.MotionTypes.BackForth: movePart2 = LerpFunc(moveObstacle, fromData=self.motionPath.getMaxT(), toData=0.0, duration=dur, blendType=blendType) self.motionSequence.append(movePart2) return def _initCollisions(self, name, collSolid): self.collName = name self.collSolid = collSolid self.collSolid.setTangible(0) self.collNode = CollisionNode(self.collName) self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) self.collNode.addSolid(self.collSolid) self.collNodePath = self.model.attachNewNode(self.collNode) self.collNodePath.hide() self.accept('enter' + self.collName, self._handleEnterCollision) self.accept('exit' + self.collName, self._handleExitCollision) def disable(self): if self.collNode is not None: self.collNode.setIntoCollideMask(BitMask32(0)) return def enable(self): if self.collNode is not None: self.collNode.setIntoCollideMask(ToontownGlobals.WallBitmask) return def startMoving(self, elapsedTime = 0.0): if self.motionSequence is not None: self.motionSequence.loop() self.motionSequence.setT(elapsedTime % self.motionSequence.getDuration()) return def stopMoving(self): if self.motionSequence is not None: self.motionSequence.pause() return def destroy(self): self.ignoreAll() if self.motionSequence is not None: self.motionSequence.clearToInitial() del self.motionSequence del self.collSolid self.collNodePath.removeNode() del self.collNodePath del self.collNode self.model.removeNode() del self.model del self.motionPath return def update(self, dt): pass def hide(self): self.ignoreAll() self.model.hide() self.collNode.setIntoCollideMask(BitMask32(0)) def _handleEnterCollision(self, collEntry): messenger.send(CogdoFlyingObstacle.EnterEventName, [self, collEntry]) def _handleExitCollision(self, collEntry): messenger.send(CogdoFlyingObstacle.ExitEventName, [self, collEntry]) from panda3d.core import TransformState class CogdoFlyingWhirlwind(CogdoFlyingObstacle): def __init__(self, index, model, motionPath = None): collSolid = CollisionTube(0, 0, 0, 0, 0, Globals.Gameplay.WhirlwindCollisionTubeHeight, Globals.Gameplay.WhirlwindCollisionTubeRadius) CogdoFlyingObstacle.__init__(self, Globals.Level.ObstacleTypes.Whirlwind, index, model, collSolid, motionPath=motionPath, motionPattern=CogdoFlyingObstacle.MotionTypes.BackForth) self.t = 0.0 self._initModel() def _initModel(self): self.model.setDepthWrite(False) self._texStage = self.model.findTextureStage('*') self._soundIval = base.cogdoGameAudioMgr.createSfxIval('whirlwind', source=self.model) self.model.setBin('transparent', self.index) def startMoving(self, elapsedTime): CogdoFlyingObstacle.startMoving(self, elapsedTime) self.t = 0.0 self._soundIval.loop() def update(self, dt): self.t += dt trans = TransformState.makePos((self.t, -self.t, 0)) self.model.setTexTransform(self._texStage, trans) trans = TransformState.makePos((self.t * 2.0, -self.t * 2.0, 0)) def stopMoving(self): CogdoFlyingObstacle.stopMoving(self) self._soundIval.pause() def destroy(self): self._soundIval.clearToInitial() del self._soundIval CogdoFlyingObstacle.destroy(self) class CogdoFlyingMinion(CogdoFlyingObstacle): def __init__(self, index, collSolid, motionPath = None): self.prop = None self.suit = Suit.Suit() d = SuitDNA.SuitDNA() d.newSuit(Globals.Gameplay.MinionDnaName) self.suit.setDNA(d) self.suit.setScale(Globals.Gameplay.MinionScale) swapAvatarShadowPlacer(self.suit, 'minion-%sShadowPlacer' % index) self.mopathNodePath = NodePath('mopathNodePath') self.suit.reparentTo(self.mopathNodePath) CogdoFlyingObstacle.__init__(self, Globals.Level.ObstacleTypes.Minion, index, self.mopathNodePath, collSolid, motionPath=motionPath, motionPattern=CogdoFlyingObstacle.MotionTypes.Loop, blendMotion=False, instanceModel=False) self.lastPos = None self.suit.loop('neutral') return def attachPropeller(self): if self.prop is None: self.prop = BattleProps.globalPropPool.getProp('propeller') head = self.suit.find('**/joint_head') self.prop.reparentTo(head) return def detachPropeller(self): if self.prop: self.prop.cleanup() self.prop.removeNode() self.prop = None return def startMoving(self, elapsedTime): CogdoFlyingObstacle.startMoving(self, elapsedTime) def stopMoving(self): CogdoFlyingObstacle.stopMoving(self) def update(self, dt): CogdoFlyingObstacle.update(self, dt) self.currPos = self.mopathNodePath.getPos() if self.lastPos != None: vec = self.currPos - self.lastPos self.mopathNodePath.lookAt(self.currPos + vec) self.mopathNodePath.setP(0) self.lastPos = self.mopathNodePath.getPos() return def destroy(self): self.mopathNodePath.removeNode() del self.mopathNodePath self.suit.cleanup() self.suit.removeNode() self.suit.delete() CogdoFlyingObstacle.destroy(self) class CogdoFlyingMinionFlying(CogdoFlyingMinion): def __init__(self, index, motionPath = None): radius = Globals.Gameplay.FlyingMinionCollisionRadius offset = Globals.Gameplay.FlyingMinionCollisionHeightOffset collSolid = CollisionSphere(0, 0, offset, radius) CogdoFlyingMinion.__init__(self, index, collSolid, motionPath) self.attachPropeller() self.propTrack = Sequence(ActorInterval(self.prop, 'propeller', startFrame=0, endFrame=14)) dur = Globals.Gameplay.FlyingMinionFloatTime offset = Globals.Gameplay.FlyingMinionFloatOffset suitPos = self.suit.getPos() upperPos = suitPos + Point3(0.0, 0.0, offset / 2.0) lowerPos = suitPos + Point3(0.0, 0.0, -offset / 2.0) self.floatSequence = Sequence(LerpPosInterval(self.suit, dur / 4.0, startPos=suitPos, pos=upperPos, blendType='easeInOut'), LerpPosInterval(self.suit, dur / 2.0, startPos=upperPos, pos=lowerPos, blendType='easeInOut'), LerpPosInterval(self.suit, dur / 4.0, startPos=lowerPos, pos=suitPos, blendType='easeInOut'), name='%s.floatSequence%i' % (self.__class__.__name__, self.index)) def startMoving(self, elapsedTime): CogdoFlyingMinion.startMoving(self, elapsedTime) self.floatSequence.loop(elapsedTime) self.propTrack.loop(elapsedTime) self.suit.pose('landing', 0) def stopMoving(self): CogdoFlyingMinion.stopMoving(self) self.floatSequence.clearToInitial() self.propTrack.pause() def destroy(self): self.floatSequence.clearToInitial() del self.floatSequence self.propTrack.clearToInitial() del self.propTrack CogdoFlyingMinion.destroy(self) class CogdoFlyingMinionWalking(CogdoFlyingMinion): def __init__(self, index, motionPath = None): radius = Globals.Gameplay.WalkingMinionCollisionRadius offset = Globals.Gameplay.WalkingMinionCollisionHeightOffset collSolid = CollisionSphere(0, 0, offset, radius) CogdoFlyingMinion.__init__(self, index, collSolid, motionPath) def startMoving(self, elapsedTime): CogdoFlyingMinion.startMoving(self, elapsedTime) self.suit.loop('walk') def stopMoving(self): CogdoFlyingMinion.stopMoving(self) self.suit.loop('neutral') class CogdoFlyingFan(CogdoFlyingObstacle): def __init__(self, index, model, motionPath = None): collSolid = CollisionTube(0, 0, 0, 0, 0, Globals.Gameplay.FanCollisionTubeHeight, Globals.Gameplay.FanCollisionTubeRadius) CogdoFlyingObstacle.__init__(self, Globals.Level.ObstacleTypes.Fan, index, model, collSolid) self.streamers = self.model.findAllMatches('**/streamer*') self._initIntervals() def _initIntervals(self): self.streamerIvals = [] minDur = Globals.Gameplay.FanStreamerMinDuration maxDur = Globals.Gameplay.FanStreamerMaxDuration for streamer in self.streamers: dur = random.uniform(minDur, maxDur) streamerLerp = LerpFunc(streamer.setH, fromData=0.0, toData=360.0, duration=dur, name='%s.streamerLerp%i-%s' % (self.__class__.__name__, self.index, streamer.getName())) self.streamerIvals.append(streamerLerp) def startMoving(self, elapsedTime = 0.0): CogdoFlyingObstacle.startMoving(self, elapsedTime) timeDelay = 0.0 maxDur = Globals.Gameplay.FanStreamerMaxDuration for ival in self.streamerIvals: taskName = 'delayedStreamerSpinTask-fan-%i-%s' % (self.index, ival.getName()) taskMgr.doMethodLater(timeDelay, ival.loop, taskName, extraArgs=[]) timeDelay += maxDur / (len(self.streamers) - 1) def stopMoving(self): CogdoFlyingObstacle.stopMoving(self) taskMgr.removeTasksMatching('delayedStreamerSpinTask-fan-%i*' % self.index) for streamerLerp in self.streamerIvals: streamerLerp.pause() def setBlowDirection(self): tempNodePath = NodePath('temp') tempNodePath.reparentTo(self.model) tempNodePath.setPos(0, 0, 1) self.blowVec = tempNodePath.getPos(render) - self.model.getPos(render) self.blowVec.normalize() tempNodePath.removeNode() del tempNodePath def getBlowDirection(self): return Vec3(self.blowVec) def destroy(self): taskMgr.removeTasksMatching('delayedStreamerSpinTask-fan-%i*' % self.index) for streamerLerp in self.streamerIvals: streamerLerp.clearToInitial() del self.streamerIvals[:] CogdoFlyingObstacle.destroy(self) del self.blowVec