oldschool-toontown/toontown/suit/BossCog.py
2020-01-14 14:28:52 -05:00

542 lines
22 KiB
Python

from pandac.PandaModules import *
from libotp import *
from direct.interval.IntervalGlobal import *
from direct.actor import Actor
from otp.avatar import Avatar
from direct.directnotify import DirectNotifyGlobal
from toontown.toonbase import ToontownGlobals
from direct.fsm import FSM
from direct.fsm import State
from toontown.toonbase import TTLocalizer
from toontown.battle import BattleParticles
from . import Suit
from direct.task.Task import Task
from . import SuitDNA
from toontown.battle import BattleProps
from direct.showbase.PythonUtil import Functor
import string
GenericModel = 'phase_9/models/char/bossCog'
ModelDict = {'s': 'phase_9/models/char/sellbotBoss',
'm': 'phase_10/models/char/cashbotBoss',
'l': 'phase_11/models/char/lawbotBoss',
'c': 'phase_12/models/char/bossbotBoss'}
AnimList = ('Ff_speech', 'ltTurn2Wave', 'wave', 'Ff_lookRt', 'turn2Fb', 'Ff_neutral', 'Bb_neutral', 'Ff2Bb_spin', 'Bb2Ff_spin', 'Fb_neutral', 'Bf_neutral', 'Fb_firstHit', 'Fb_downNeutral', 'Fb_downHit', 'Fb_fall', 'Fb_down2Up', 'Fb_downLtSwing', 'Fb_downRtSwing', 'Fb_DownThrow', 'Fb_UpThrow', 'Fb_jump', 'golf_swing')
class BossCog(Avatar.Avatar):
notify = DirectNotifyGlobal.directNotify.newCategory('BossCog')
healthColors = Suit.Suit.healthColors
healthGlowColors = Suit.Suit.healthGlowColors
def __init__(self):
Avatar.Avatar.__init__(self)
self.setFont(ToontownGlobals.getSuitFont())
self.setPlayerType(NametagGroup.CCSuit)
self.setPickable(0)
self.doorA = None
self.doorB = None
self.bubbleL = None
self.bubbleR = None
self.raised = 1
self.forward = 1
self.happy = 1
self.dizzy = 0
self.nowRaised = 1
self.nowForward = 1
self.nowHappy = 1
self.currentAnimIval = None
self.queuedAnimIvals = []
self.treadsLeftPos = 0
self.treadsRightPos = 0
self.healthBar = None
self.healthCondition = 0
self.animDoneEvent = 'BossCogAnimDone'
self.animIvalName = 'BossCogAnimIval'
return
def delete(self):
Avatar.Avatar.delete(self)
self.removeHealthBar()
self.setDizzy(0)
self.stopAnimate()
if self.doorA:
self.doorA.request('Off')
self.doorB.request('Off')
self.doorA = None
self.doorB = None
return
def setDNAString(self, dnaString):
self.dna = SuitDNA.SuitDNA()
self.dna.makeFromNetString(dnaString)
self.setDNA(self.dna)
def setDNA(self, dna):
if self.style:
pass
else:
self.style = dna
self.generateBossCog()
self.initializeDropShadow()
if base.wantNametags:
self.initializeNametag3d()
def generateBossCog(self):
self.throwSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_frisbee_gears.ogg')
self.swingSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_swipe.ogg')
self.spinSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_spin.ogg')
self.rainGearsSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_raining_gears.ogg')
self.swishSfx = loader.loadSfx('phase_5/audio/sfx/General_throw_miss.ogg')
self.boomSfx = loader.loadSfx('phase_3.5/audio/sfx/ENC_cogfall_apart.ogg')
self.deathSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_big_death.ogg')
self.upSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_raise_up.ogg')
self.downSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_collapse.ogg')
self.reelSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_reeling_backwards.ogg')
self.birdsSfx = loader.loadSfx('phase_4/audio/sfx/SZ_TC_bird1.ogg')
self.dizzyAlert = loader.loadSfx('phase_5/audio/sfx/AA_sound_aoogah.ogg')
self.grunt = loader.loadSfx('phase_9/audio/sfx/Boss_COG_VO_grunt.ogg')
self.murmur = loader.loadSfx('phase_9/audio/sfx/Boss_COG_VO_murmur.ogg')
self.statement = loader.loadSfx('phase_9/audio/sfx/Boss_COG_VO_statement.ogg')
self.question = loader.loadSfx('phase_9/audio/sfx/Boss_COG_VO_question.ogg')
self.dialogArray = [self.grunt,
self.murmur,
self.statement,
self.question,
self.statement,
self.statement]
dna = self.style
filePrefix = ModelDict[dna.dept]
self.loadModel(GenericModel + '-legs-zero', 'legs')
self.loadModel(filePrefix + '-torso-zero', 'torso')
self.loadModel(filePrefix + '-head-zero', 'head')
self.twoFaced = dna.dept == 's'
self.attach('head', 'torso', 'joint34')
self.attach('torso', 'legs', 'joint_pelvis')
self.rotateNode = self.attachNewNode('rotate')
geomNode = self.getGeomNode()
geomNode.reparentTo(self.rotateNode)
self.frontAttack = self.rotateNode.attachNewNode('frontAttack')
self.frontAttack.setPos(0, -10, 10)
self.frontAttack.setScale(2)
self.setHeight(26)
self.nametag3d.setScale(2)
for partName in ('legs', 'torso', 'head'):
animDict = {}
for anim in AnimList:
animDict[anim] = '%s-%s-%s' % (GenericModel, partName, anim)
self.loadAnims(animDict, partName)
self.stars = BattleProps.globalPropPool.getProp('stun')
self.stars.setPosHprScale(7, 0, 0, 0, 0, -90, 3, 3, 3)
self.stars.loop('stun')
self.pelvis = self.getPart('torso')
self.pelvisForwardHpr = VBase3(0, 0, 0)
self.pelvisReversedHpr = VBase3(-180, 0, 0)
self.neck = self.getPart('head')
self.neckForwardHpr = VBase3(0, 0, 0)
self.neckReversedHpr = VBase3(0, -540, 0)
self.axle = self.find('**/joint_axle')
self.doorA = self.__setupDoor('**/joint_doorFront', 'doorA', self.doorACallback, VBase3(0, 0, 0), VBase3(0, 0, -80), CollisionPolygon(Point3(5, -4, 0.32), Point3(0, -4, 0), Point3(0, 4, 0), Point3(5, 4, 0.32)))
self.doorB = self.__setupDoor('**/joint_doorRear', 'doorB', self.doorBCallback, VBase3(0, 0, 0), VBase3(0, 0, 80), CollisionPolygon(Point3(-5, 4, 0.84), Point3(0, 4, 0), Point3(0, -4, 0), Point3(-5, -4, 0.84)))
treadsModel = loader.loadModel('%s-treads' % GenericModel)
treadsModel.reparentTo(self.axle)
self.treadsLeft = treadsModel.find('**/right_tread')
self.treadsRight = treadsModel.find('**/left_tread')
self.doorA.request('Closed')
self.doorB.request('Closed')
def initializeBodyCollisions(self, collIdStr):
Avatar.Avatar.initializeBodyCollisions(self, collIdStr)
if not self.ghostMode:
self.collNode.setCollideMask(self.collNode.getIntoCollideMask() | ToontownGlobals.PieBitmask)
def generateHealthBar(self):
self.removeHealthBar()
chestNull = self.find('**/joint_lifeMeter')
if chestNull.isEmpty():
return
model = loader.loadModel('phase_3.5/models/gui/matching_game_gui')
button = model.find('**/minnieCircle')
button.setScale(6.0)
button.setP(-20)
button.setColor(self.healthColors[0])
button.reparentTo(chestNull)
self.healthBar = button
glow = BattleProps.globalPropPool.getProp('glow')
glow.reparentTo(self.healthBar)
glow.setScale(0.28)
glow.setPos(-0.005, 0.01, 0.015)
glow.setColor(self.healthGlowColors[0])
button.flattenLight()
self.healthBarGlow = glow
self.healthCondition = 0
def updateHealthBar(self):
if self.healthBar == None:
return
health = 1.0 - float(self.bossDamage) / float(self.bossMaxDamage)
if health > 0.95:
condition = 0
elif health > 0.7:
condition = 1
elif health > 0.3:
condition = 2
elif health > 0.05:
condition = 3
elif health > 0.0:
condition = 4
else:
condition = 5
if self.healthCondition != condition:
if condition == 4:
blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.75), Task(self.__blinkGray), Task.pause(0.1))
taskMgr.add(blinkTask, self.uniqueName('blink-task'))
elif condition == 5:
if self.healthCondition == 4:
taskMgr.remove(self.uniqueName('blink-task'))
blinkTask = Task.loop(Task(self.__blinkRed), Task.pause(0.25), Task(self.__blinkGray), Task.pause(0.1))
taskMgr.add(blinkTask, self.uniqueName('blink-task'))
else:
self.healthBar.setColor(self.healthColors[condition], 1)
self.healthBarGlow.setColor(self.healthGlowColors[condition], 1)
self.healthCondition = condition
return
def __blinkRed(self, task):
self.healthBar.setColor(self.healthColors[3], 1)
self.healthBarGlow.setColor(self.healthGlowColors[3], 1)
if self.healthCondition == 5:
self.healthBar.setScale(1.17)
return Task.done
def __blinkGray(self, task):
self.healthBar.setColor(self.healthColors[4], 1)
self.healthBarGlow.setColor(self.healthGlowColors[4], 1)
if self.healthCondition == 5:
self.healthBar.setScale(1.0)
return Task.done
def removeHealthBar(self):
if self.healthBar:
self.healthBar.removeNode()
self.healthBar = None
if self.healthCondition == 4 or self.healthCondition == 5:
taskMgr.remove(self.uniqueName('blink-task'))
self.healthCondition = 0
return
def reverseHead(self):
self.neck.setHpr(self.neckReversedHpr)
def forwardHead(self):
self.neck.setHpr(self.neckForwardHpr)
def reverseBody(self):
self.pelvis.setHpr(self.pelvisReversedHpr)
def forwardBody(self):
self.pelvis.setHpr(self.pelvisForwardHpr)
def getShadowJoint(self):
return self.getGeomNode()
def getNametagJoints(self):
return []
def getDialogueArray(self):
return self.dialogArray
def doorACallback(self, isOpen):
pass
def doorBCallback(self, isOpen):
pass
def __rollTreadsInterval(self, object, start = 0, duration = 0, rate = 1):
def rollTexMatrix(t, object = object):
object.setTexOffset(TextureStage.getDefault(), t, 0)
return LerpFunctionInterval(rollTexMatrix, fromData=start, toData=start + rate * duration, duration=duration)
def rollLeftTreads(self, duration, rate):
start = self.treadsLeftPos
self.treadsLeftPos += duration * rate
return self.__rollTreadsInterval(self.treadsLeft, start=start, duration=duration, rate=rate)
def rollRightTreads(self, duration, rate):
start = self.treadsRightPos
self.treadsRightPos += duration * rate
return self.__rollTreadsInterval(self.treadsRight, start=start, duration=duration, rate=rate)
class DoorFSM(FSM.FSM):
def __init__(self, name, animate, callback, openedHpr, closedHpr, uniqueName):
FSM.FSM.__init__(self, name)
self.animate = animate
self.callback = callback
self.openedHpr = openedHpr
self.closedHpr = closedHpr
self.uniqueName = uniqueName
self.ival = 0
self.openSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_door_open.ogg')
self.closeSfx = loader.loadSfx('phase_9/audio/sfx/CHQ_VP_door_close.ogg')
self.request('Closed')
def filterOpening(self, request, args):
if request == 'close':
return 'Closing'
return self.defaultFilter(request, args)
def enterOpening(self):
intervalName = self.uniqueName('open-%s' % self.animate.getName())
self.callback(0)
ival = Parallel(SoundInterval(self.openSfx, node=self.animate, volume=0.2), self.animate.hprInterval(1, self.openedHpr, blendType='easeInOut'), Sequence(Wait(0.2), Func(self.callback, 1)), name=intervalName)
ival.start()
self.ival = ival
def exitOpening(self):
self.ival.pause()
self.ival = None
return
def filterOpened(self, request, args):
if request == 'close':
return 'Closing'
return self.defaultFilter(request, args)
def enterOpened(self):
self.animate.setHpr(self.openedHpr)
self.callback(1)
def filterClosing(self, request, args):
if request == 'open':
return 'Opening'
return self.defaultFilter(request, args)
def enterClosing(self):
intervalName = self.uniqueName('close-%s' % self.animate.getName())
self.callback(1)
ival = Parallel(SoundInterval(self.closeSfx, node=self.animate, volume=0.2), self.animate.hprInterval(1, self.closedHpr, blendType='easeInOut'), Sequence(Wait(0.8), Func(self.callback, 0)), name=intervalName)
ival.start()
self.ival = ival
def exitClosing(self):
self.ival.pause()
self.ival = None
return
def filterClosed(self, request, args):
if request == 'open':
return 'Opening'
return self.defaultFilter(request, args)
def enterClosed(self):
self.animate.setHpr(self.closedHpr)
self.callback(0)
def __setupDoor(self, jointName, name, callback, openedHpr, closedHpr, cPoly):
joint = self.find(jointName)
children = joint.getChildren()
animate = joint.attachNewNode(name)
children.reparentTo(animate)
cnode = CollisionNode('BossZap')
cnode.setCollideMask(ToontownGlobals.PieBitmask | ToontownGlobals.WallBitmask | ToontownGlobals.CameraBitmask)
cnode.addSolid(cPoly)
animate.attachNewNode(cnode)
fsm = self.DoorFSM(name, animate, callback, openedHpr, closedHpr, self.uniqueName)
return fsm
def doAnimate(self, anim = None, now = 0, queueNeutral = 1, raised = None, forward = None, happy = None):
if now:
self.stopAnimate()
if not self.twoFaced:
happy = 1
if raised == None:
raised = self.raised
if forward == None:
forward = self.forward
if happy == None:
happy = self.happy
if now:
self.raised = raised
self.forward = forward
self.happy = happy
if self.currentAnimIval == None:
self.accept(self.animDoneEvent, self.__getNextAnim)
else:
queueNeutral = 0
ival, changed = self.__getAnimIval(anim, raised, forward, happy)
if changed or queueNeutral:
self.queuedAnimIvals.append((ival,
self.raised,
self.forward,
self.happy))
if self.currentAnimIval == None:
self.__getNextAnim()
return
def stopAnimate(self):
self.ignore(self.animDoneEvent)
self.queuedAnimIvals = []
if self.currentAnimIval:
self.currentAnimIval.setDoneEvent('')
self.currentAnimIval.finish()
self.currentAnimIval = None
self.raised = self.nowRaised
self.forward = self.nowForward
self.happy = self.nowHappy
return
def __getNextAnim(self):
if self.queuedAnimIvals:
ival, raised, forward, happy = self.queuedAnimIvals[0]
del self.queuedAnimIvals[0]
else:
ival, changed = self.__getAnimIval(None, self.raised, self.forward, self.happy)
raised = self.raised
forward = self.forward
happy = self.happy
if self.currentAnimIval:
self.currentAnimIval.setDoneEvent('')
self.currentAnimIval.finish()
self.currentAnimIval = ival
self.currentAnimIval.start()
self.nowRaised = raised
self.nowForward = forward
self.nowHappy = happy
return
def __getAnimIval(self, anim, raised, forward, happy):
ival, changed = self.__doGetAnimIval(anim, raised, forward, happy)
seq = Sequence(ival, name=self.animIvalName)
seq.setDoneEvent(self.animDoneEvent)
return (seq, changed)
def __doGetAnimIval(self, anim, raised, forward, happy):
if raised == self.raised and forward == self.forward and happy == self.happy:
return (self.getAnim(anim), anim != None)
startsHappy = self.happy
endsHappy = self.happy
ival = Sequence()
if raised and not self.raised:
upIval = self.getAngryActorInterval('Fb_down2Up')
if self.forward:
ival = upIval
else:
ival = Sequence(Func(self.reverseBody), upIval, Func(self.forwardBody))
ival = Parallel(SoundInterval(self.upSfx, node=self), ival)
if forward != self.forward:
if forward:
animName = 'Bb2Ff_spin'
else:
animName = 'Ff2Bb_spin'
ival = Sequence(ival, ActorInterval(self, animName))
startsHappy = 1
endsHappy = 1
startNeckHpr = self.neckForwardHpr
endNeckHpr = self.neckForwardHpr
if self.happy != startsHappy:
startNeckHpr = self.neckReversedHpr
if happy != endsHappy:
endNeckHpr = self.neckReversedHpr
if startNeckHpr != endNeckHpr:
ival = Sequence(Func(self.neck.setHpr, startNeckHpr), ParallelEndTogether(ival, Sequence(self.neck.hprInterval(0.5, endNeckHpr, startHpr=startNeckHpr, blendType='easeInOut'), Func(self.neck.setHpr, self.neckForwardHpr))))
elif endNeckHpr != self.neckForwardHpr:
ival = Sequence(Func(self.neck.setHpr, startNeckHpr), ival, Func(self.neck.setHpr, self.neckForwardHpr))
if not raised and self.raised:
downIval = self.getAngryActorInterval('Fb_down2Up', playRate=-1)
if forward:
ival = Sequence(ival, downIval)
else:
ival = Sequence(ival, Func(self.reverseBody), downIval, Func(self.forwardBody))
ival = Parallel(SoundInterval(self.downSfx, node=self), ival)
self.raised = raised
self.forward = forward
self.happy = happy
if anim != None:
ival = Sequence(ival, self.getAnim(anim))
return (ival, 1)
def setDizzy(self, dizzy):
if dizzy and not self.dizzy:
base.playSfx(self.dizzyAlert)
self.dizzy = dizzy
if dizzy:
self.stars.reparentTo(self.neck)
base.playSfx(self.birdsSfx, looping=1)
else:
self.stars.detachNode()
self.birdsSfx.stop()
def getAngryActorInterval(self, animName, **kw):
if self.happy:
ival = Sequence(Func(self.reverseHead), ActorInterval(self, animName, **kw), Func(self.forwardHead))
else:
ival = ActorInterval(self, animName, **kw)
return ival
def getAnim(self, anim):
ival = None
if anim == None:
partName = None
if self.happy:
animName = 'Ff_neutral'
else:
animName = 'Fb_neutral'
if self.raised:
ival = ActorInterval(self, animName)
else:
ival = Parallel(ActorInterval(self, animName, partName=['torso', 'head']), ActorInterval(self, 'Fb_downNeutral', partName='legs'))
if not self.forward:
ival = Sequence(Func(self.reverseBody), ival, Func(self.forwardBody))
elif anim == 'down2Up':
ival = Parallel(SoundInterval(self.upSfx, node=self), self.getAngryActorInterval('Fb_down2Up'))
self.raised = 1
elif anim == 'up2Down':
ival = Parallel(SoundInterval(self.downSfx, node=self), self.getAngryActorInterval('Fb_down2Up', playRate=-1))
self.raised = 0
elif anim == 'throw':
self.doAnimate(None, raised=1, happy=0, queueNeutral=0)
ival = Parallel(Sequence(SoundInterval(self.throwSfx, node=self), duration=0), self.getAngryActorInterval('Fb_UpThrow'))
elif anim == 'hit':
if self.raised:
self.raised = 0
ival = self.getAngryActorInterval('Fb_firstHit')
else:
ival = self.getAngryActorInterval('Fb_downHit')
ival = Parallel(SoundInterval(self.reelSfx, node=self), ival)
elif anim == 'ltSwing' or anim == 'rtSwing':
self.doAnimate(None, raised=0, happy=0, queueNeutral=0)
if anim == 'ltSwing':
ival = Sequence(Track((0, self.getAngryActorInterval('Fb_downLtSwing')), (0.9, SoundInterval(self.swingSfx, node=self)), (1, Func(self.bubbleL.unstash))), Func(self.bubbleL.stash))
else:
ival = Sequence(Track((0, self.getAngryActorInterval('Fb_downRtSwing')), (0.9, SoundInterval(self.swingSfx, node=self)), (1, Func(self.bubbleR.unstash))), Func(self.bubbleR.stash))
elif anim == 'frontAttack':
self.doAnimate(None, raised=1, happy=0, queueNeutral=0)
pe = BattleParticles.loadParticleFile('bossCogFrontAttack.ptf')
ival = Sequence(Func(self.reverseHead), ActorInterval(self, 'Bb2Ff_spin'), Func(self.forwardHead))
if self.forward:
ival = Sequence(Func(self.reverseBody), ParallelEndTogether(ival, self.pelvis.hprInterval(0.5, self.pelvisForwardHpr, blendType='easeInOut')))
ival = Sequence(Track((0, ival), (0, SoundInterval(self.spinSfx, node=self)), (0.9, Parallel(SoundInterval(self.rainGearsSfx, node=self), ParticleInterval(pe, self.frontAttack, worldRelative=0, duration=1.5, cleanup=True), duration=0)), (1.9, Func(self.bubbleF.unstash))), Func(self.bubbleF.stash))
self.forward = 1
self.happy = 0
self.raised = 1
elif anim == 'areaAttack':
if self.twoFaced:
self.doAnimate(None, raised=1, happy=0, queueNeutral=0)
else:
self.doAnimate(None, raised=1, happy=1, queueNeutral=1)
ival = Parallel(ActorInterval(self, 'Fb_jump'), Sequence(SoundInterval(self.swishSfx, duration=1.1, node=self), SoundInterval(self.boomSfx, duration=1.9)), Sequence(Wait(1.21), Func(self.announceAreaAttack)))
if self.twoFaced:
self.happy = 0
else:
self.happy = 1
self.raised = 1
elif anim == 'Fb_fall':
ival = Parallel(ActorInterval(self, 'Fb_fall'), Sequence(SoundInterval(self.reelSfx, node=self), SoundInterval(self.deathSfx)))
elif isinstance(anim, str):
ival = ActorInterval(self, anim)
else:
ival = anim
return ival