toontown-just-works/toontown/suit/DistributedGoon.py
2024-07-07 18:08:39 -05:00

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)