479 lines
17 KiB
Python
479 lines
17 KiB
Python
from panda3d.core import *
|
|
from direct.interval.IntervalGlobal import *
|
|
from toontown.battle.BattleProps import *
|
|
from GoonGlobals import *
|
|
from direct.fsm import FSM
|
|
from direct.distributed import ClockDelta
|
|
from otp.level import BasicEntities
|
|
from otp.level import DistributedEntity
|
|
from direct.directnotify import DirectNotifyGlobal
|
|
from toontown.coghq import DistributedCrushableEntity
|
|
from toontown.toonbase import ToontownGlobals
|
|
from toontown.coghq import MovingPlatform
|
|
import Goon
|
|
from direct.task.Task import Task
|
|
from otp.level import PathEntity
|
|
import GoonDeath
|
|
import random
|
|
|
|
class DistributedGoon(DistributedCrushableEntity.DistributedCrushableEntity, Goon.Goon, FSM.FSM):
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedGoon')
|
|
|
|
def __init__(self, cr):
|
|
try:
|
|
self.DistributedGoon_initialized
|
|
except:
|
|
self.DistributedGoon_initialized = 1
|
|
DistributedCrushableEntity.DistributedCrushableEntity.__init__(self, cr)
|
|
Goon.Goon.__init__(self)
|
|
FSM.FSM.__init__(self, 'DistributedGoon')
|
|
|
|
self.setCacheable(0)
|
|
self.rayNode = None
|
|
self.checkForWalls = 0
|
|
self.triggerEvent = None
|
|
self.animTrack = None
|
|
self.walkTrack = None
|
|
self.pauseTime = 0
|
|
self.paused = 0
|
|
self.path = None
|
|
self.dir = GOON_FORWARD
|
|
self.animMultiplier = 1.0
|
|
self.isDead = 0
|
|
self.isStunned = 0
|
|
self.collapseSound = loader.loadSfx('phase_9/audio/sfx/CHQ_GOON_hunker_down.ogg')
|
|
self.recoverSound = loader.loadSfx('phase_9/audio/sfx/CHQ_GOON_rattle_shake.ogg')
|
|
self.attackSound = loader.loadSfx('phase_9/audio/sfx/CHQ_GOON_tractor_beam_alarmed.ogg')
|
|
return
|
|
|
|
def announceGenerate(self):
|
|
DistributedCrushableEntity.DistributedCrushableEntity.announceGenerate(self)
|
|
if hasattr(self, 'goonType'):
|
|
self.initGoon(self.goonType)
|
|
else:
|
|
self.initGoon('pg')
|
|
self.scaleRadar()
|
|
self.colorHat()
|
|
if self.level:
|
|
self.initClipPlanes()
|
|
self.level.setEntityCreateCallback(self.parentEntId, self.initPath)
|
|
else:
|
|
self.enterOff()
|
|
taskMgr.doMethodLater(0.1, self.makeCollidable, self.taskName('makeCollidable'))
|
|
self.setGoonScale(self.scale)
|
|
self.animMultiplier = self.velocity / (ANIM_WALK_RATE * self.scale)
|
|
self.setPlayRate(self.animMultiplier, 'walk')
|
|
|
|
def initPath(self):
|
|
self.enterOff()
|
|
self.setPath()
|
|
taskMgr.doMethodLater(0.1, self.makeCollidable, self.taskName('makeCollidable'))
|
|
|
|
def makeCollidable(self, task):
|
|
self.initCollisions()
|
|
self.initializeBodyCollisions()
|
|
triggerName = self.uniqueName('GoonTrigger')
|
|
self.trigger.setName(triggerName)
|
|
self.triggerEvent = 'enter%s' % triggerName
|
|
self.startToonDetect()
|
|
|
|
def generate(self):
|
|
DistributedCrushableEntity.DistributedCrushableEntity.generate(self)
|
|
|
|
def scaleRadar(self):
|
|
Goon.Goon.scaleRadar(self)
|
|
self.trigger = self.radar.find('**/trigger')
|
|
triggerName = self.uniqueName('GoonTrigger')
|
|
self.trigger.setName(triggerName)
|
|
|
|
def initCollisions(self):
|
|
self.cSphere = CollisionSphere(0.0, 0.0, 1.0, 1.0)
|
|
self.cSphereNode = CollisionNode('goonCollSphere')
|
|
self.cSphereNode.addSolid(self.cSphere)
|
|
self.cSphereNodePath = self.head.attachNewNode(self.cSphereNode)
|
|
self.cSphereNodePath.hide()
|
|
self.cSphereBitMask = ToontownGlobals.WallBitmask
|
|
self.cSphereNode.setCollideMask(self.cSphereBitMask)
|
|
self.cSphere.setTangible(1)
|
|
self.sSphere = CollisionSphere(0.0, 0.0, self.headHeight + 0.8, 1.2)
|
|
self.sSphereNode = CollisionNode('toonSphere')
|
|
self.sSphereNode.addSolid(self.sSphere)
|
|
self.sSphereNodePath = self.head.attachNewNode(self.sSphereNode)
|
|
self.sSphereNodePath.hide()
|
|
self.sSphereBitMask = ToontownGlobals.WallBitmask
|
|
self.sSphereNode.setCollideMask(self.sSphereBitMask)
|
|
self.sSphere.setTangible(1)
|
|
|
|
def initializeBodyCollisions(self):
|
|
self.cSphereNode.setName(self.uniqueName('goonCollSphere'))
|
|
self.sSphereNode.setName(self.uniqueName('toonSphere'))
|
|
self.accept(self.uniqueName('entertoonSphere'), self.__handleStun)
|
|
|
|
def disableBodyCollisions(self):
|
|
self.ignore(self.uniqueName('entertoonSphere'))
|
|
|
|
def deleteCollisions(self):
|
|
if hasattr(self, 'sSphereNodePath'):
|
|
self.sSphereNodePath.removeNode()
|
|
del self.sSphereNodePath
|
|
del self.sSphereNode
|
|
del self.sSphere
|
|
if hasattr(self, 'cSphereNodePath'):
|
|
self.cSphereNodePath.removeNode()
|
|
del self.cSphereNodePath
|
|
del self.cSphereNode
|
|
del self.cSphere
|
|
|
|
def initClipPlanes(self):
|
|
zoneNum = self.getZoneEntity().getZoneNum()
|
|
clipList = self.level.goonClipPlanes.get(zoneNum)
|
|
if clipList:
|
|
for id in clipList:
|
|
clipPlane = self.level.getEntity(id)
|
|
self.radar.setClipPlane(clipPlane.getPlane())
|
|
|
|
def disableClipPlanes(self):
|
|
if self.radar:
|
|
self.radar.clearClipPlane()
|
|
|
|
def setPath(self):
|
|
self.path = self.level.getEntity(self.parentEntId)
|
|
if self.walkTrack:
|
|
self.walkTrack.pause()
|
|
self.walkTrack = None
|
|
self.walkTrack = self.path.makePathTrack(self, self.velocity, self.uniqueName('goonWalk'), turnTime=T_TURN)
|
|
if self.gridId != None:
|
|
self.sendUpdate('setParameterize', [self.path.pos[0],
|
|
self.path.pos[1],
|
|
self.path.pos[2],
|
|
self.path.pathIndex])
|
|
|
|
def disable(self):
|
|
self.notify.debug('DistributedGoon %d: disabling' % self.getDoId())
|
|
self.ignoreAll()
|
|
self.stopToonDetect()
|
|
taskMgr.remove(self.taskName('resumeWalk'))
|
|
taskMgr.remove(self.taskName('recoveryDone'))
|
|
self.request('Off')
|
|
self.disableBodyCollisions()
|
|
self.disableClipPlanes()
|
|
if self.animTrack:
|
|
self.animTrack.finish()
|
|
self.animTrack = None
|
|
if self.walkTrack:
|
|
self.walkTrack.pause()
|
|
self.walkTrack = None
|
|
DistributedCrushableEntity.DistributedCrushableEntity.disable(self)
|
|
return
|
|
|
|
def delete(self):
|
|
try:
|
|
self.DistributedSuit_deleted
|
|
except:
|
|
self.DistributedSuit_deleted = 1
|
|
self.notify.debug('DistributedGoon %d: deleting' % self.getDoId())
|
|
taskMgr.remove(self.taskName('makeCollidable'))
|
|
self.deleteCollisions()
|
|
self.head.removeNode()
|
|
del self.head
|
|
del self.attackSound
|
|
del self.collapseSound
|
|
del self.recoverSound
|
|
DistributedCrushableEntity.DistributedCrushableEntity.delete(self)
|
|
Goon.Goon.delete(self)
|
|
|
|
def enterOff(self, *args):
|
|
self.nametag3d.stash()
|
|
self.nametag.destroy()
|
|
self.hide()
|
|
self.isStunned = 0
|
|
self.isDead = 0
|
|
if self.animTrack:
|
|
self.animTrack.finish()
|
|
self.animTrack = None
|
|
if self.walkTrack:
|
|
self.walkTrack.pause()
|
|
self.walkTrack = None
|
|
|
|
def exitOff(self):
|
|
self.show()
|
|
|
|
def enterWalk(self, avId = None, ts = 0):
|
|
self.notify.debug('enterWalk, ts = %s' % ts)
|
|
self.startToonDetect()
|
|
self.loop('walk', 0)
|
|
self.isStunned = 0
|
|
if self.path:
|
|
if not self.walkTrack:
|
|
self.walkTrack = self.path.makePathTrack(self, self.velocity, self.uniqueName('goonWalk'), turnTime=T_TURN)
|
|
self.startWalk(ts)
|
|
|
|
def startWalk(self, ts):
|
|
tOffset = ts % self.walkTrack.getDuration()
|
|
self.walkTrack.loop()
|
|
self.walkTrack.pause()
|
|
self.walkTrack.setT(tOffset)
|
|
self.walkTrack.resume()
|
|
self.paused = 0
|
|
|
|
def exitWalk(self):
|
|
self.notify.debug('exitWalk')
|
|
self.stopToonDetect()
|
|
if self.walkTrack and not self.paused:
|
|
self.pauseTime = self.walkTrack.pause()
|
|
self.paused = 1
|
|
self.stop()
|
|
|
|
def enterBattle(self, avId = None, ts = 0):
|
|
self.notify.debug('enterBattle')
|
|
self.stopToonDetect()
|
|
if self.animTrack:
|
|
self.animTrack.finish()
|
|
self.animTrack = None
|
|
self.isStunned = 0
|
|
if avId == base.localAvatar.doId:
|
|
if self.level:
|
|
self.level.b_setOuch(self.strength)
|
|
self.animTrack = self.makeAttackTrack()
|
|
self.animTrack.loop()
|
|
return
|
|
|
|
def exitBattle(self):
|
|
self.notify.debug('exitBattle')
|
|
if self.animTrack:
|
|
self.animTrack.finish()
|
|
self.animTrack = None
|
|
self.head.setHpr(0, 0, 0)
|
|
return
|
|
|
|
def enterStunned(self, ts = 0):
|
|
self.ignore(self.uniqueName('entertoonSphere'))
|
|
self.isStunned = 1
|
|
self.notify.debug('enterStunned')
|
|
if self.radar:
|
|
self.radar.hide()
|
|
self.animTrack = Parallel(Sequence(ActorInterval(self, 'collapse'), Func(self.pose, 'collapse', 48)), SoundInterval(self.collapseSound, node=self))
|
|
self.animTrack.start(ts)
|
|
|
|
def exitStunned(self):
|
|
self.notify.debug('exitStunned')
|
|
if self.radar:
|
|
self.radar.show()
|
|
if self.animTrack:
|
|
self.animTrack.finish()
|
|
self.animTrack = None
|
|
self.accept(self.uniqueName('entertoonSphere'), self.__handleStun)
|
|
return
|
|
|
|
def enterRecovery(self, ts = 0, pauseTime = 0):
|
|
self.notify.debug('enterRecovery')
|
|
self.ignore(self.uniqueName('entertoonSphere'))
|
|
self.isStunned = 1
|
|
if self.animTrack:
|
|
self.animTrack.finish()
|
|
self.animTrack = None
|
|
self.animTrack = self.getRecoveryTrack()
|
|
duration = self.animTrack.getDuration()
|
|
self.animTrack.start(ts)
|
|
delay = max(0, duration - ts)
|
|
taskMgr.remove(self.taskName('recoveryDone'))
|
|
taskMgr.doMethodLater(delay, self.recoveryDone, self.taskName('recoveryDone'), extraArgs=(pauseTime,))
|
|
return
|
|
|
|
def getRecoveryTrack(self):
|
|
return Parallel(Sequence(ActorInterval(self, 'recovery'), Func(self.pose, 'recovery', 96)), Func(base.playSfx, self.recoverSound, node=self))
|
|
|
|
def recoveryDone(self, pauseTime):
|
|
self.request('Walk', None, pauseTime)
|
|
return
|
|
|
|
def exitRecovery(self):
|
|
self.notify.debug('exitRecovery')
|
|
taskMgr.remove(self.taskName('recoveryDone'))
|
|
if self.animTrack:
|
|
self.animTrack.finish()
|
|
self.animTrack = None
|
|
self.accept(self.uniqueName('entertoonSphere'), self.__handleStun)
|
|
return
|
|
|
|
def makeAttackTrack(self):
|
|
h = self.head.getH()
|
|
freakDeg = 60
|
|
hatZ = self.hat.getZ()
|
|
track = Parallel(Sequence(LerpColorScaleInterval(self.eye, 0.2, Vec4(1, 0, 0, 1)), LerpColorScaleInterval(self.eye, 0.2, Vec4(0, 0, 1, 1)), LerpColorScaleInterval(self.eye, 0.2, Vec4(1, 0, 0, 1)), LerpColorScaleInterval(self.eye, 0.2, Vec4(0, 0, 1, 1)), Func(self.eye.clearColorScale)), SoundInterval(self.attackSound, node=self, volume=0.4))
|
|
return track
|
|
|
|
def doDetect(self):
|
|
pass
|
|
|
|
def doAttack(self, avId):
|
|
pass
|
|
|
|
def __startResumeWalkTask(self, ts):
|
|
resumeTime = 1.5
|
|
if ts < resumeTime:
|
|
taskMgr.remove(self.taskName('resumeWalk'))
|
|
taskMgr.doMethodLater(resumeTime - ts, self.request, self.taskName('resumeWalk'), extraArgs=('Walk',))
|
|
else:
|
|
self.request('Walk', ts - resumeTime)
|
|
|
|
def __reverseWalk(self, task):
|
|
self.request('Walk')
|
|
return Task.done
|
|
|
|
def __startRecoverTask(self, ts):
|
|
stunTime = 4.0
|
|
if ts < stunTime:
|
|
taskMgr.remove(self.taskName('resumeWalk'))
|
|
taskMgr.doMethodLater(stunTime - ts, self.request, self.taskName('resumeWalk'), extraArgs=('Recovery',))
|
|
else:
|
|
self.request('Recovery', ts - stunTime)
|
|
|
|
def startToonDetect(self):
|
|
self.radar.show()
|
|
if self.triggerEvent:
|
|
self.accept(self.triggerEvent, self.handleToonDetect)
|
|
|
|
def stopToonDetect(self):
|
|
if self.triggerEvent:
|
|
self.ignore(self.triggerEvent)
|
|
|
|
def handleToonDetect(self, collEntry = None):
|
|
if base.localAvatar.isStunned:
|
|
return
|
|
if self.state == 'Off':
|
|
return
|
|
self.stopToonDetect()
|
|
self.request('Battle', base.localAvatar.doId)
|
|
if self.walkTrack:
|
|
self.pauseTime = self.walkTrack.pause()
|
|
self.paused = 1
|
|
if self.dclass and hasattr(self, 'dclass'):
|
|
self.sendUpdate('requestBattle', [self.pauseTime])
|
|
else:
|
|
self.notify.info('Goon deleted and still trying to call handleToonDetect()')
|
|
|
|
def __handleStun(self, collEntry):
|
|
toon = base.localAvatar
|
|
if toon:
|
|
toonDistance = self.getPos(toon).length()
|
|
if toonDistance > self.attackRadius:
|
|
self.notify.warning('Stunned a good, but outside of attack radius')
|
|
return
|
|
else:
|
|
self.request('Stunned')
|
|
if self.walkTrack:
|
|
self.pauseTime = self.walkTrack.pause()
|
|
self.paused = 1
|
|
self.sendUpdate('requestStunned', [self.pauseTime])
|
|
|
|
def setMovie(self, mode, avId, pauseTime, timestamp):
|
|
if self.isDead:
|
|
return
|
|
ts = ClockDelta.globalClockDelta.localElapsedTime(timestamp)
|
|
self.notify.debug('%s: setMovie(%s,%s,%s,%s)' % (self.doId,
|
|
mode,
|
|
avId,
|
|
pauseTime,
|
|
ts))
|
|
if mode == GOON_MOVIE_BATTLE:
|
|
if self.state != 'Battle':
|
|
self.request('Battle', avId, ts)
|
|
elif mode == GOON_MOVIE_STUNNED:
|
|
if self.state != 'Stunned':
|
|
toon = base.cr.doId2do.get(avId)
|
|
if toon:
|
|
toonDistance = self.getPos(toon).length()
|
|
if toonDistance > self.attackRadius:
|
|
self.notify.warning('Stunned a goon, but outside of attack radius')
|
|
return
|
|
else:
|
|
self.request('Stunned', ts)
|
|
elif mode == GOON_MOVIE_RECOVERY:
|
|
if self.state != 'Recovery':
|
|
self.request('Recovery', ts, pauseTime)
|
|
elif mode == GOON_MOVIE_SYNC:
|
|
if self.walkTrack:
|
|
self.walkTrack.pause()
|
|
self.paused = 1
|
|
if self.state == 'Off' or self.state == 'Walk':
|
|
self.request('Walk', avId, pauseTime + ts)
|
|
else:
|
|
if self.walkTrack:
|
|
self.walkTrack.pause()
|
|
self.walkTrack = None
|
|
self.request('Walk', avId, pauseTime + ts)
|
|
return
|
|
|
|
def stunToon(self, avId):
|
|
self.notify.debug('stunToon(%s)' % avId)
|
|
av = base.cr.doId2do.get(avId)
|
|
if av != None:
|
|
av.stunToon()
|
|
return
|
|
|
|
def isLocalToon(self, avId):
|
|
if avId == base.localAvatar.doId:
|
|
return 1
|
|
return 0
|
|
|
|
def playCrushMovie(self, crusherId, axis):
|
|
goonPos = self.getPos()
|
|
sx = random.uniform(0.3, 0.8) * self.scale
|
|
sz = random.uniform(0.3, 0.8) * self.scale
|
|
crushTrack = Sequence(GoonDeath.createGoonExplosion(self.getParent(), goonPos, VBase3(sx, 1, sz)), name=self.uniqueName('crushTrack'), autoFinish=1)
|
|
self.dead()
|
|
crushTrack.start()
|
|
|
|
def setVelocity(self, velocity):
|
|
self.velocity = velocity
|
|
self.animMultiplier = velocity / (ANIM_WALK_RATE * self.scale)
|
|
self.setPlayRate(self.animMultiplier, 'walk')
|
|
|
|
def dead(self):
|
|
if not self.isDead and not self.isDisabled():
|
|
self.stopToonDetect()
|
|
self.detachNode()
|
|
self.isDead = 1
|
|
|
|
def undead(self):
|
|
if self.isDead:
|
|
self.startToonDetect()
|
|
self.reparentTo(render)
|
|
self.isDead = 0
|
|
|
|
def resync(self):
|
|
if not self.isDead:
|
|
self.sendUpdate('requestResync')
|
|
|
|
def setHFov(self, hFov):
|
|
if hFov != self.hFov:
|
|
self.hFov = hFov
|
|
if self.isGenerated():
|
|
self.scaleRadar()
|
|
|
|
def setAttackRadius(self, attackRadius):
|
|
if attackRadius != self.attackRadius:
|
|
self.attackRadius = attackRadius
|
|
if self.isGenerated():
|
|
self.scaleRadar()
|
|
|
|
def setStrength(self, strength):
|
|
if strength != self.strength:
|
|
self.strength = strength
|
|
if self.isGenerated():
|
|
self.colorHat()
|
|
|
|
def setGoonScale(self, scale):
|
|
if scale != self.scale:
|
|
self.scale = scale
|
|
if self.isGenerated():
|
|
self.getGeomNode().setScale(self.scale)
|
|
self.scaleRadar()
|
|
|
|
def setupGoon(self, velocity, hFov, attackRadius, strength, scale):
|
|
self.setVelocity(velocity)
|
|
self.setHFov(hFov)
|
|
self.setAttackRadius(attackRadius)
|
|
self.setStrength(strength)
|
|
self.setGoonScale(scale)
|