508 lines
20 KiB
Python
508 lines
20 KiB
Python
import math
|
|
import random
|
|
import GenericAnimatedProp
|
|
from direct.actor import Actor
|
|
from direct.interval.IntervalGlobal import Sequence, ActorInterval, Wait, Func, SoundInterval, Parallel
|
|
from direct.fsm import FSM
|
|
from direct.showbase.PythonUtil import weightedChoice
|
|
from pandac.PandaModules import TextNode, Vec3
|
|
from toontown.toonbase import ToontownGlobals
|
|
from toontown.hood import ZoneUtil
|
|
|
|
def clearPythonIvals(ival):
|
|
if hasattr(ival, 'function'):
|
|
ival.function = None
|
|
if hasattr(ival, 'pythonIvals'):
|
|
for oneIval in ival.pythonIvals:
|
|
clearPythonIvals(oneIval)
|
|
|
|
ival.pythonIvals = []
|
|
return
|
|
|
|
|
|
class InteractiveAnimatedProp(GenericAnimatedProp.GenericAnimatedProp, FSM.FSM):
|
|
ZoneToIdles = {}
|
|
ZoneToIdleIntoFightAnims = {}
|
|
ZoneToFightAnims = {}
|
|
ZoneToVictoryAnims = {}
|
|
ZoneToSadAnims = {}
|
|
IdlePauseTime = config.GetFloat('prop-idle-pause-time', 0.0)
|
|
HpTextGenerator = TextNode('HpTextGenerator')
|
|
BattleCheerText = '+'
|
|
|
|
def __init__(self, node, holidayId = -1):
|
|
FSM.FSM.__init__(self, 'InteractiveProp-%s' % str(node))
|
|
self.holidayId = holidayId
|
|
self.numIdles = 0
|
|
self.numFightAnims = 0
|
|
self.idleInterval = None
|
|
self.battleCheerInterval = None
|
|
self.sadInterval = None
|
|
self.victoryInterval = None
|
|
self.lastIdleAnimName = ''
|
|
self.lastIdleTime = 0
|
|
self.curIval = None
|
|
self.okToStartNextAnim = False
|
|
cellIndexStr = node.getTag('DNACellIndex')
|
|
self.cellIndex = ord(cellIndexStr)
|
|
self.origAnimNameToSound = {}
|
|
self.lastPlayingAnimPhase = 0
|
|
self.buildingsMakingMeSad = set()
|
|
GenericAnimatedProp.GenericAnimatedProp.__init__(self, node)
|
|
return
|
|
|
|
def delete(self):
|
|
self.exit()
|
|
GenericAnimatedProp.GenericAnimatedProp.delete(self)
|
|
self.idleInterval = None
|
|
self.battleCheerInterval = None
|
|
self.sadInterval = None
|
|
self.victoryInterval = None
|
|
return
|
|
|
|
def getCellIndex(self):
|
|
return self.cellIndex
|
|
|
|
def playBattleCheerAnim(self):
|
|
self.node.loop('battleCheer')
|
|
|
|
def setupActor(self, node):
|
|
if self.hoodId in self.ZoneToIdles:
|
|
self.numIdles = len(self.ZoneToIdles[self.hoodId])
|
|
if self.hoodId in self.ZoneToFightAnims:
|
|
self.numFightAnims = len(self.ZoneToFightAnims[self.hoodId])
|
|
self.idleInterval = None
|
|
anim = node.getTag('DNAAnim')
|
|
self.trashcan = Actor.Actor(node, copy=0)
|
|
self.trashcan.reparentTo(node)
|
|
animDict = {}
|
|
animDict['anim'] = '%s/%s' % (self.path, anim)
|
|
for i in xrange(self.numIdles):
|
|
baseAnim = self.ZoneToIdles[self.hoodId][i]
|
|
if isinstance(baseAnim, tuple):
|
|
baseAnim = baseAnim[0]
|
|
animStr = self.path + '/' + baseAnim
|
|
animKey = 'idle%d' % i
|
|
animDict[animKey] = animStr
|
|
settleName = self.getSettleName(i)
|
|
if settleName:
|
|
settleStr = self.path + '/' + settleName
|
|
settleKey = 'settle%d' % i
|
|
animDict[settleKey] = settleStr
|
|
|
|
for i in xrange(self.numFightAnims):
|
|
animStr = self.path + '/' + self.ZoneToFightAnims[self.hoodId][i]
|
|
animKey = 'fight%d' % i
|
|
animDict[animKey] = animStr
|
|
|
|
if self.hoodId in self.ZoneToIdleIntoFightAnims:
|
|
animStr = self.path + '/' + self.ZoneToIdleIntoFightAnims[self.hoodId]
|
|
animKey = 'idleIntoFight'
|
|
animDict[animKey] = animStr
|
|
if self.hoodId in self.ZoneToIdleIntoFightAnims:
|
|
animStr = self.path + '/' + self.ZoneToVictoryAnims[self.hoodId]
|
|
animKey = 'victory'
|
|
animDict[animKey] = animStr
|
|
if self.hoodId in self.ZoneToSadAnims:
|
|
animStr = self.path + '/' + self.ZoneToSadAnims[self.hoodId]
|
|
animKey = 'sad'
|
|
animDict[animKey] = animStr
|
|
self.trashcan.loadAnims(animDict)
|
|
self.trashcan.pose('anim', 0)
|
|
self.node = self.trashcan
|
|
self.idleInterval = self.createIdleInterval()
|
|
self.battleCheerInterval = self.createBattleCheerInterval()
|
|
self.victoryInterval = self.createVictoryInterval()
|
|
self.sadInterval = self.createSadInterval()
|
|
return
|
|
|
|
def createIdleInterval(self):
|
|
result = Sequence()
|
|
if self.numIdles >= 3:
|
|
numberOfAnimsAbove2 = self.numIdles - 2
|
|
for rareIdle in xrange(2, self.numIdles):
|
|
for i in xrange(2):
|
|
result.append(ActorInterval(self.node, 'idle0'))
|
|
result.append(Wait(self.IdlePauseTime))
|
|
result.append(ActorInterval(self.node, 'idle1'))
|
|
result.append(Wait(self.IdlePauseTime))
|
|
|
|
result.append(ActorInterval(self.node, 'idle%d' % rareIdle))
|
|
result.append(Wait(self.IdlePauseTime))
|
|
|
|
else:
|
|
for i in xrange(self.numIdles):
|
|
result.append(ActorInterval(self.node, 'idle%d' % i))
|
|
|
|
self.notify.debug('idle interval=%s' % result)
|
|
return result
|
|
|
|
def createBattleCheerText(self):
|
|
self.HpTextGenerator.setFont(ToontownGlobals.getSignFont())
|
|
self.HpTextGenerator.setText(self.BattleCheerText)
|
|
self.HpTextGenerator.clearShadow()
|
|
self.HpTextGenerator.setAlign(TextNode.ACenter)
|
|
r = 0
|
|
g = 0
|
|
b = 1
|
|
a = 1
|
|
self.HpTextGenerator.setTextColor(r, g, b, a)
|
|
self.hpTextNode = self.HpTextGenerator.generate()
|
|
self.hpText = self.node.attachNewNode(self.hpTextNode)
|
|
self.hpText.setScale(1)
|
|
self.hpText.setBillboardPointEye()
|
|
self.hpText.setBin('fixed', 100)
|
|
self.hpText.setPos(0, 0, 4)
|
|
self.hpText.hide()
|
|
|
|
def createBattleCheerInterval(self):
|
|
result = Sequence()
|
|
for i in xrange(self.numFightAnims):
|
|
animKey = 'fight%d' % i
|
|
animAndSoundIval = self.createAnimAndSoundIval(animKey)
|
|
origAnimName = self.node.getAnimFilename(animKey).split('/')[-1]
|
|
if self.hasOverrideIval(origAnimName):
|
|
result.append(self.getOverrideIval(origAnimName))
|
|
elif self.hasSpecialIval(origAnimName):
|
|
result.append(Parallel(animAndSoundIval, self.getSpecialIval(origAnimName)))
|
|
else:
|
|
result.append(animAndSoundIval)
|
|
|
|
self.createBattleCheerText()
|
|
battleCheerTextIval = Sequence(Func(self.hpText.show), self.hpText.posInterval(duration=4.0, pos=Vec3(0, 0, 7), startPos=(0, 0, 3)), Func(self.hpText.hide))
|
|
ivalWithText = Parallel(battleCheerTextIval, result)
|
|
return ivalWithText
|
|
|
|
def createSadInterval(self):
|
|
result = Sequence()
|
|
if self.hoodId in self.ZoneToSadAnims:
|
|
result = self.createAnimAndSoundIval('sad')
|
|
return result
|
|
|
|
def hasSpecialIval(self, origAnimName):
|
|
return False
|
|
|
|
def getSpecialIval(self, origAnimName):
|
|
return Sequence()
|
|
|
|
def hasOverrideIval(self, origAnimName):
|
|
return False
|
|
|
|
def getOverrideIval(self, origAnimName):
|
|
return Sequence()
|
|
|
|
def createVictoryInterval(self):
|
|
result = Sequence()
|
|
if self.hoodId in self.ZoneToVictoryAnims:
|
|
animAndSoundIval = self.createAnimAndSoundIval('victory')
|
|
result.append(animAndSoundIval)
|
|
return result
|
|
|
|
def enter(self):
|
|
GenericAnimatedProp.GenericAnimatedProp.enter(self)
|
|
if config.GetBool('props-buff-battles', True):
|
|
self.notify.debug('props buff battles is true')
|
|
if base.cr.newsManager.isHolidayRunning(self.holidayId):
|
|
self.notify.debug('holiday is running, doing idle interval')
|
|
self.node.stop()
|
|
self.node.pose('idle0', 0)
|
|
if config.GetBool('interactive-prop-random-idles', 1):
|
|
self.requestIdleOrSad()
|
|
else:
|
|
self.idleInterval.loop()
|
|
else:
|
|
self.notify.debug('holiday is NOT running, doing nothing')
|
|
self.node.stop()
|
|
self.node.pose('idle0', 0)
|
|
else:
|
|
self.notify.debug('props do not buff battles')
|
|
self.node.stop()
|
|
self.node.pose('idle0', 0)
|
|
|
|
def exit(self):
|
|
self.okToStartNextAnim = False
|
|
self.notify.debug('%s %d okToStartNextAnim=%s' % (self, self.visId, self.okToStartNextAnim))
|
|
GenericAnimatedProp.GenericAnimatedProp.exit(self)
|
|
self.request('Off')
|
|
|
|
def requestIdleOrSad(self):
|
|
if not hasattr(self, 'node') or not self.node:
|
|
self.notify.warning("requestIdleOrSad returning hasattr(self,'node')=%s" % hasattr(self, 'node'))
|
|
return
|
|
if self.buildingsMakingMeSad:
|
|
self.request('Sad')
|
|
else:
|
|
self.request('DoIdleAnim')
|
|
|
|
def enterDoIdleAnim(self):
|
|
self.notify.debug('enterDoIdleAnim numIdels=%d' % self.numIdles)
|
|
self.okToStartNextAnim = True
|
|
self.notify.debug('%s %d okToStartNextAnim=%s' % (self, self.visId, self.okToStartNextAnim))
|
|
self.startNextIdleAnim()
|
|
|
|
def exitDoIdleAnim(self):
|
|
self.notify.debug('exitDoIdlesAnim numIdles=%d' % self.numIdles)
|
|
self.okToStartNextAnim = False
|
|
self.notify.debug('%s %d okToStartNextAnim=%s' % (self, self.visId, self.okToStartNextAnim))
|
|
self.calcLastIdleFrame()
|
|
self.clearCurIval()
|
|
|
|
def calcLastIdleFrame(self):
|
|
if self.curIval and self.curIval.ivals:
|
|
firstIval = self.curIval.ivals[0]
|
|
if isinstance(firstIval, ActorInterval):
|
|
self.lastIdleFrame = firstIval.getCurrentFrame()
|
|
self.lastIdleAnimName = firstIval.animName
|
|
elif isinstance(firstIval, Parallel):
|
|
for testIval in firstIval.ivals:
|
|
if isinstance(firstIval, ActorInterval):
|
|
self.lastIdleTime = testIval.getT()
|
|
self.lastIdleAnimName = testIval.animName
|
|
break
|
|
|
|
def chooseIdleAnimToRun(self):
|
|
result = self.numIdles - 1
|
|
if config.GetBool('randomize-interactive-idles', True):
|
|
pairs = []
|
|
for i in xrange(self.numIdles):
|
|
reversedChance = self.numIdles - i - 1
|
|
pairs.append((math.pow(2, reversedChance), i))
|
|
|
|
sum = math.pow(2, self.numIdles) - 1
|
|
result = weightedChoice(pairs, sum=sum)
|
|
self.notify.debug('chooseAnimToRun numIdles=%s pairs=%s result=%s' % (self.numIdles, pairs, result))
|
|
else:
|
|
result = self.lastPlayingAnimPhase + 1
|
|
if result >= len(self.ZoneToIdles[self.hoodId]):
|
|
result = 0
|
|
return result
|
|
|
|
def startNextIdleAnim(self):
|
|
self.notify.debug('startNextAnim self.okToStartNextAnim=%s' % self.okToStartNextAnim)
|
|
if not hasattr(self, 'node') or not self.node:
|
|
self.notify.warning("startNextIdleAnim returning hasattr(self,'node')=%s" % hasattr(self, 'node'))
|
|
return
|
|
self.curIval = None
|
|
if self.okToStartNextAnim:
|
|
self.notify.debug('got pass okToStartNextAnim')
|
|
whichAnim = self.chooseIdleAnimToRun()
|
|
if self.visId == localAvatar.zoneId:
|
|
self.notify.debug('whichAnim=%s' % whichAnim)
|
|
if __dev__:
|
|
self.notify.info('whichAnim=%s %s' % (whichAnim, self.getOrigIdleAnimName(whichAnim)))
|
|
self.lastPlayingAnimPhase = whichAnim
|
|
self.curIval = self.createIdleAnimSequence(whichAnim)
|
|
self.notify.debug('starting curIval of length %s' % self.curIval.getDuration())
|
|
self.curIval.start()
|
|
else:
|
|
self.curIval = Wait(10)
|
|
self.notify.debug('false self.okToStartNextAnim=%s' % self.okToStartNextAnim)
|
|
return
|
|
|
|
def createIdleAnimAndSoundInterval(self, whichIdleAnim, startingTime = 0):
|
|
animIval = self.node.actorInterval('idle%d' % whichIdleAnim, startTime=startingTime)
|
|
animIvalDuration = animIval.getDuration()
|
|
origAnimName = self.ZoneToIdles[self.hoodId][whichIdleAnim]
|
|
if isinstance(origAnimName, tuple):
|
|
origAnimName = origAnimName[0]
|
|
soundIval = self.createSoundInterval(origAnimName, animIvalDuration)
|
|
soundIvalDuration = soundIval.getDuration()
|
|
if self.hasSpecialIval(origAnimName):
|
|
specialIval = self.getSpecialIval(origAnimName)
|
|
idleAnimAndSound = Parallel(animIval, soundIval, specialIval)
|
|
else:
|
|
idleAnimAndSound = Parallel(animIval, soundIval)
|
|
return idleAnimAndSound
|
|
|
|
def createIdleAnimSequence(self, whichIdleAnim):
|
|
dummyResult = Sequence(Wait(self.IdlePauseTime))
|
|
if not hasattr(self, 'node') or not self.node:
|
|
self.notify.warning("createIdleAnimSequence returning dummyResult hasattr(self,'node')=%s" % hasattr(self, 'node'))
|
|
return dummyResult
|
|
idleAnimAndSound = self.createIdleAnimAndSoundInterval(whichIdleAnim)
|
|
result = Sequence(idleAnimAndSound, Wait(self.IdlePauseTime), Func(self.startNextIdleAnim))
|
|
if isinstance(self.ZoneToIdles[self.hoodId][whichIdleAnim], tuple) and len(self.ZoneToIdles[self.hoodId][whichIdleAnim]) > 2:
|
|
info = self.ZoneToIdles[self.hoodId][whichIdleAnim]
|
|
origAnimName = info[0]
|
|
minLoop = info[1]
|
|
maxLoop = info[2]
|
|
settleAnim = info[3]
|
|
minPauseTime = info[4]
|
|
maxPauseTime = info[5]
|
|
numberOfLoops = random.randrange(minLoop, maxLoop + 1)
|
|
pauseTime = random.randrange(minPauseTime, maxPauseTime + 1)
|
|
result = Sequence()
|
|
for i in xrange(numberOfLoops):
|
|
result.append(idleAnimAndSound)
|
|
|
|
if self.getSettleName(whichIdleAnim):
|
|
result.append(self.node.actorInterval('settle%d' % whichIdleAnim))
|
|
result.append(Wait(pauseTime))
|
|
result.append(Func(self.startNextIdleAnim))
|
|
return result
|
|
|
|
def gotoFaceoff(self):
|
|
self.notify.debugStateCall(self)
|
|
if base.cr.newsManager.isHolidayRunning(self.holidayId):
|
|
self.request('Faceoff')
|
|
else:
|
|
self.notify.debug('not going to faceoff because holiday %d is not running' % self.holidayId)
|
|
|
|
def gotoBattleCheer(self):
|
|
self.notify.debugStateCall(self)
|
|
if base.cr.newsManager.isHolidayRunning(self.holidayId):
|
|
self.request('BattleCheer')
|
|
else:
|
|
self.notify.debug('not going to battleCheer because holiday %d is not running' % self.holidayId)
|
|
|
|
def gotoIdle(self):
|
|
self.notify.debugStateCall(self)
|
|
if base.cr.newsManager.isHolidayRunning(self.holidayId):
|
|
self.request('DoIdleAnim')
|
|
else:
|
|
self.notify.debug('not going to idle because holiday %d is not running' % self.holidayId)
|
|
|
|
def gotoVictory(self):
|
|
self.notify.debugStateCall(self)
|
|
if base.cr.newsManager.isHolidayRunning(self.holidayId):
|
|
self.request('Victory')
|
|
else:
|
|
self.notify.debug('not going to victory because holiday %d is not running' % self.holidayId)
|
|
|
|
def gotoSad(self, buildingDoId):
|
|
self.notify.debugStateCall(self)
|
|
self.buildingsMakingMeSad.add(buildingDoId)
|
|
if base.cr.newsManager.isHolidayRunning(self.holidayId):
|
|
self.request('Sad')
|
|
else:
|
|
self.notify.debug('not going to sad because holiday %d is not running' % self.holidayId)
|
|
|
|
def buildingLiberated(self, buildingDoId):
|
|
self.buildingsMakingMeSad.discard(buildingDoId)
|
|
if not self.buildingsMakingMeSad:
|
|
self.gotoIdle()
|
|
|
|
def enterFaceoff(self):
|
|
self.notify.debugStateCall(self)
|
|
self.curIval = self.createFaceoffInterval()
|
|
self.curIval.start()
|
|
|
|
def exitFaceoff(self):
|
|
self.notify.debugStateCall(self)
|
|
self.curIval.pause()
|
|
self.curIval = None
|
|
return
|
|
|
|
def calcWhichIdleAnim(self, animName):
|
|
result = 0
|
|
info = self.ZoneToIdles[self.hoodId]
|
|
for index, curInfo in enumerate(info):
|
|
if isinstance(curInfo, tuple):
|
|
if curInfo[0] == animName:
|
|
result = index
|
|
break
|
|
elif isinstance(curInfo, str):
|
|
if curInfo == animName:
|
|
result = index
|
|
breal
|
|
|
|
return result
|
|
|
|
def createFaceoffInterval(self):
|
|
result = Sequence()
|
|
if self.lastIdleAnimName:
|
|
whichIdleAnim = self.calcWhichIdleAnim(self.lastIdleAnimName)
|
|
animAndSound = self.createIdleAnimAndSoundInterval(whichIdleAnim, self.lastIdleTime)
|
|
result.append(animAndSound)
|
|
idleIntoFightIval = self.createAnimAndSoundIval('idleIntoFight')
|
|
result.append(idleIntoFightIval)
|
|
result.append(Func(self.gotoBattleCheer))
|
|
return result
|
|
|
|
def enterBattleCheer(self):
|
|
self.notify.debugStateCall(self)
|
|
self.curIval = self.battleCheerInterval
|
|
if self.curIval:
|
|
self.curIval.loop()
|
|
|
|
def exitBattleCheer(self):
|
|
self.notify.debugStateCall(self)
|
|
if self.curIval:
|
|
self.curIval.finish()
|
|
self.curIval = None
|
|
return
|
|
|
|
def enterVictory(self):
|
|
self.notify.debugStateCall(self)
|
|
self.curIval = self.victoryInterval
|
|
if self.curIval:
|
|
self.curIval.loop()
|
|
|
|
def exitVictory(self):
|
|
self.notify.debugStateCall(self)
|
|
if self.curIval:
|
|
self.curIval.finish()
|
|
self.curIval = None
|
|
return
|
|
|
|
def enterSad(self):
|
|
self.notify.debugStateCall(self)
|
|
self.curIval = self.sadInterval
|
|
if self.curIval:
|
|
self.curIval.loop()
|
|
|
|
def exitSad(self):
|
|
self.notify.debugStateCall(self)
|
|
if self.curIval:
|
|
self.curIval.finish()
|
|
self.curIval = None
|
|
return
|
|
|
|
def getSettleName(self, whichIdleAnim):
|
|
result = None
|
|
if isinstance(self.ZoneToIdles[self.hoodId][whichIdleAnim], tuple) and len(self.ZoneToIdles[self.hoodId][whichIdleAnim]) > 3:
|
|
result = self.ZoneToIdles[self.hoodId][whichIdleAnim][3]
|
|
return result
|
|
|
|
def getOrigIdleAnimName(self, whichIdleAnim):
|
|
result = None
|
|
if isinstance(self.ZoneToIdles[self.hoodId][whichIdleAnim], tuple):
|
|
result = self.ZoneToIdles[self.hoodId][whichIdleAnim][0]
|
|
else:
|
|
result = self.ZoneToIdles[self.hoodId][whichIdleAnim]
|
|
return result
|
|
|
|
def createAnimAndSoundIval(self, animKey):
|
|
animIval = self.node.actorInterval(animKey)
|
|
animIvalDuration = animIval.getDuration()
|
|
origAnimName = self.node.getAnimFilename(animKey)
|
|
soundIval = self.createSoundInterval(origAnimName, animIvalDuration)
|
|
soundIvalDuration = soundIval.getDuration()
|
|
printFunc = Func(self.printAnimIfClose, animKey)
|
|
if self.hasSpecialIval(origAnimName):
|
|
specialIval = self.getSpecialIval(origAnimName)
|
|
idleAnimAndSound = Parallel(animIval, soundIval, specialIval)
|
|
if config.GetBool('interactive-prop-info', False):
|
|
idleAnimAndSound.append(printFunc)
|
|
else:
|
|
idleAnimAndSound = Parallel(animIval, soundIval)
|
|
if config.GetBool('interactive-prop-info', False):
|
|
idleAnimAndSound.append(printFunc)
|
|
return idleAnimAndSound
|
|
|
|
def printAnimIfClose(self, animKey):
|
|
if config.GetBool('interactive-prop-info', False):
|
|
try:
|
|
animName = self.node.getAnimFilename(animKey)
|
|
baseAnimName = animName.split('/')[-1]
|
|
if localAvatar.zoneId == self.visId:
|
|
self.notify.info('playing %s' % baseAnimName)
|
|
except Exception, e:
|
|
self.notify.warning('Unknown error in printAnimIfClose, giving up:\n%s' % str(e))
|
|
|
|
def clearCurIval(self):
|
|
if self.curIval:
|
|
self.curIval.finish()
|
|
clearPythonIvals(self.curIval)
|
|
self.curIval = None
|
|
return
|