oldschool-toontown/toontown/toon/TTEmote.py
Little Cat 1801d2b9fb
all: replace pandac.PandaModules imports.
UD/AI + Client boots up.
2022-12-16 20:40:57 -04:00

620 lines
20 KiB
Python

from . import Toon, ToonDNA
from direct.interval.IntervalGlobal import *
from otp.otpbase import OTPLocalizer
from toontown.toonbase import TTLocalizer
from otp.otpbase import OTPLocalizer
from direct.showbase import PythonUtil
from panda3d.core import *
from panda3d.otp import *
from otp.avatar import Emote
from direct.directnotify import DirectNotifyGlobal
EmoteSleepIndex = 4
EmoteClear = -1
def doVictory(toon, volume = 1):
duration = toon.getDuration('victory', 'legs')
sfx = base.loader.loadSfx('phase_3.5/audio/sfx/ENC_Win.ogg')
sfxDuration = duration - 1.0
sfxTrack = SoundInterval(sfx, loop=1, duration=sfxDuration, node=toon, volume=volume)
track = Sequence(Func(toon.play, 'victory'), sfxTrack, duration=0)
return (track, duration, None)
def doJump(toon, volume = 1):
track = Sequence(Func(toon.play, 'jump'))
return (track, 0, None)
def doDead(toon, volume = 1):
toon.animFSM.request('Sad')
return (None, 0, None)
def doAnnoyed(toon, volume = 1):
duration = toon.getDuration('angry', 'torso')
sfx = None
if toon.style.getAnimal() == 'bear':
sfx = base.loader.loadSfx('phase_3.5/audio/dial/AV_bear_exclaim.ogg')
else:
sfx = base.loader.loadSfx('phase_3.5/audio/sfx/avatar_emotion_angry.ogg')
def playSfx():
base.playSfx(sfx, volume=volume, node=toon)
track = Sequence(Func(toon.angryEyes), Func(toon.blinkEyes), Func(toon.play, 'angry'), Func(playSfx))
exitTrack = Sequence(Func(toon.normalEyes), Func(toon.blinkEyes))
return (track, duration, exitTrack)
def doAngryEyes(toon, volume = 1):
track = Sequence(Func(toon.angryEyes), Func(toon.blinkEyes), Wait(10.0), Func(toon.normalEyes))
return (track, 0.1, None)
def doHappy(toon, volume = 1):
track = Sequence(Func(toon.play, 'jump'), Func(toon.normalEyes), Func(toon.blinkEyes))
duration = toon.getDuration('jump', 'legs')
return (track, duration, None)
def doSad(toon, volume = 1):
track = Sequence(Func(toon.sadEyes), Func(toon.blinkEyes))
exitTrack = Sequence(Func(toon.normalEyes), Func(toon.blinkEyes))
return (track, 3, exitTrack)
def doSleep(toon, volume = 1):
duration = 4
track = Sequence(Func(toon.stopLookAround), Func(toon.stopBlink), Func(toon.closeEyes), Func(toon.lerpLookAt, Point3(0, 1, -4)), Func(toon.loop, 'neutral'), Func(toon.setPlayRate, 0.4, 'neutral'), Func(toon.setChatAbsolute, TTLocalizer.ToonSleepString, CFThought))
def wakeUpFromSleepEmote():
toon.startLookAround()
toon.openEyes()
toon.startBlink()
toon.setPlayRate(1, 'neutral')
if toon.nametag.getChat() == TTLocalizer.ToonSleepString:
toon.clearChat()
toon.lerpLookAt(Point3(0, 1, 0), time=0.25)
exitTrack = Sequence(Func(wakeUpFromSleepEmote))
return (track, duration, exitTrack)
def doYes(toon, volume = 1):
tracks = Parallel(autoFinish=1)
for lod in toon.getLODNames():
h = toon.getPart('head', lod)
tracks.append(Sequence(LerpHprInterval(h, 0.1, Vec3(0, -30, 0)), LerpHprInterval(h, 0.15, Vec3(0, 20, 0)), LerpHprInterval(h, 0.15, Vec3(0, -20, 0)), LerpHprInterval(h, 0.15, Vec3(0, 20, 0)), LerpHprInterval(h, 0.15, Vec3(0, -20, 0)), LerpHprInterval(h, 0.15, Vec3(0, 20, 0)), LerpHprInterval(h, 0.1, Vec3(0, 0, 0))))
tracks.start()
return (None, 0, None)
def doNo(toon, volume = 1):
tracks = Parallel(autoFinish=1)
for lod in toon.getLODNames():
h = toon.getPart('head', lod)
tracks.append(Sequence(LerpHprInterval(h, 0.1, Vec3(40, 0, 0)), LerpHprInterval(h, 0.15, Vec3(-40, 0, 0)), LerpHprInterval(h, 0.15, Vec3(40, 0, 0)), LerpHprInterval(h, 0.15, Vec3(-40, 0, 0)), LerpHprInterval(h, 0.15, Vec3(20, 0, 0)), LerpHprInterval(h, 0.15, Vec3(-20, 0, 0)), LerpHprInterval(h, 0.1, Vec3(0, 0, 0))))
tracks.start()
return (None, 0, None)
def doOk(toon, volume = 1):
return (None, 0, None)
def doShrug(toon, volume = 1):
sfx = base.loader.loadSfx('phase_3.5/audio/sfx/avatar_emotion_shrug.ogg')
def playSfx():
base.playSfx(sfx, volume=volume, node=toon)
track = Sequence(Func(toon.play, 'shrug'), Func(playSfx))
duration = toon.getDuration('shrug', 'torso')
return (track, duration, None)
def doWave(toon, volume = 1):
track = Sequence(Func(toon.play, 'wave'))
duration = toon.getDuration('wave', 'torso')
return (track, duration, None)
def doApplause(toon, volume = 1):
sfx = base.loader.loadSfx('phase_4/audio/sfx/avatar_emotion_applause.ogg')
def playSfx():
base.playSfx(sfx, volume=1, node=toon)
track = Sequence(Func(toon.play, 'applause'), Func(playSfx))
duration = toon.getDuration('applause', 'torso')
return (track, duration, None)
def doConfused(toon, volume = 1):
sfx = base.loader.loadSfx('phase_4/audio/sfx/avatar_emotion_confused.ogg')
def playSfx():
base.playSfx(sfx, node=toon, volume=volume)
track = Sequence(Func(toon.play, 'confused'), Func(playSfx))
duration = toon.getDuration('confused', 'torso')
return (track, duration, None)
def doSlipForward(toon, volume = 1):
sfx = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg')
def playSfx():
base.playSfx(sfx, volume=volume, node=toon)
sfxDelay = 0.7
track = Sequence(Func(toon.play, 'slip-forward'), Wait(sfxDelay), Func(playSfx))
duration = toon.getDuration('slip-forward', 'torso') - sfxDelay
return (track, duration, None)
def doBored(toon, volume = 1):
sfx = base.loader.loadSfx('phase_4/audio/sfx/avatar_emotion_bored.ogg')
def playSfx():
base.playSfx(sfx, volume=volume, node=toon)
sfxDelay = 2.2
track = Sequence(Func(toon.play, 'bored'), Wait(sfxDelay), Func(playSfx))
duration = toon.getDuration('bored', 'torso') - sfxDelay
return (track, duration, None)
def doBow(toon, volume = 1):
if toon.style.torso[1] == 'd':
track = Sequence(Func(toon.play, 'curtsy'))
duration = toon.getDuration('curtsy', 'torso')
else:
track = Sequence(Func(toon.play, 'bow'))
duration = toon.getDuration('bow', 'torso')
return (track, duration, None)
def doSlipBackward(toon, volume = 1):
sfx = base.loader.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.ogg')
def playSfx():
base.playSfx(sfx, volume=volume, node=toon)
sfxDelay = 0.7
track = Sequence(Func(toon.play, 'slip-backward'), Wait(sfxDelay), Func(playSfx))
duration = toon.getDuration('slip-backward', 'torso') - sfxDelay
return (track, duration, None)
def doThink(toon, volume = 1):
duration = 47.0 / 24.0 * 2
animTrack = Sequence(ActorInterval(toon, 'think', startFrame=0, endFrame=46), ActorInterval(toon, 'think', startFrame=46, endFrame=0))
track = Sequence(animTrack, duration=0)
return (track, duration, None)
def doCringe(toon, volume = 1):
track = Sequence(Func(toon.play, 'cringe'))
duration = toon.getDuration('cringe', 'torso')
return (track, duration, None)
def doResistanceSalute(toon, volume = 1):
playRate = 0.75
duration = 10.0 / 24.0 * (1 / playRate) * 2
animTrack = Sequence(Func(toon.setChatAbsolute, OTPLocalizer.CustomSCStrings[4020], CFSpeech | CFTimeout), Func(toon.setPlayRate, playRate, 'victory'), ActorInterval(toon, 'victory', playRate=playRate, startFrame=0, endFrame=9), ActorInterval(toon, 'victory', playRate=playRate, startFrame=9, endFrame=0))
track = Sequence(animTrack, duration=0)
return (track, duration, None)
def doNothing(toon, volume = 1):
return (None, 0, None)
def doSurprise(toon, volume = 1):
sfx = None
sfx = base.loader.loadSfx('phase_4/audio/sfx/avatar_emotion_surprise.ogg')
def playSfx(volume = 1):
base.playSfx(sfx, volume=volume, node=toon)
def playAnim(anim):
anim.start()
def stopAnim(anim):
anim.finish()
toon.stop()
sfx.stop()
anim = Sequence(ActorInterval(toon, 'conked', startFrame=9, endFrame=50), ActorInterval(toon, 'conked', startFrame=70, endFrame=101))
track = Sequence(Func(toon.stopBlink), Func(toon.surpriseEyes), Func(toon.showSurpriseMuzzle), Parallel(Func(playAnim, anim), Func(playSfx, volume)))
exitTrack = Sequence(Func(toon.hideSurpriseMuzzle), Func(toon.openEyes), Func(toon.startBlink), Func(stopAnim, anim))
return (track, 3.0, exitTrack)
def doUpset(toon, volume = 1):
sfx = None
sfx = base.loader.loadSfx('phase_4/audio/sfx/avatar_emotion_very_sad_1.ogg')
def playSfx(volume = 1):
base.playSfx(sfx, volume=volume, node=toon)
def playAnim(anim):
anim.start()
def stopAnim(anim):
anim.finish()
toon.stop()
sfx.stop()
anim = Sequence(ActorInterval(toon, 'bad-putt', startFrame=29, endFrame=59, playRate=-0.75), ActorInterval(toon, 'bad-putt', startFrame=29, endFrame=59, playRate=0.75))
track = Sequence(Func(toon.sadEyes), Func(toon.blinkEyes), Func(toon.showSadMuzzle), Parallel(Func(playAnim, anim), Func(playSfx, volume)))
exitTrack = Sequence(Func(toon.hideSadMuzzle), Func(toon.normalEyes), Func(stopAnim, anim))
return (track, 4.0, exitTrack)
def doDelighted(toon, volume = 1):
sfx = None
sfx = base.loader.loadSfx('phase_4/audio/sfx/delighted_06.ogg')
def playSfx(volume = 1):
base.playSfx(sfx, volume=volume, node=toon)
def playAnim(anim):
anim.start()
def stopAnim(anim):
anim.finish()
toon.stop()
sfx.stop()
anim = Sequence(ActorInterval(toon, 'left'), Wait(1), ActorInterval(toon, 'left', playRate=-1))
track = Sequence(Func(toon.blinkEyes), Func(toon.showSmileMuzzle), Parallel(Func(playAnim, anim), Func(playSfx, volume)))
exitTrack = Sequence(Func(toon.hideSmileMuzzle), Func(toon.blinkEyes), Func(stopAnim, anim))
return (track, 2.5, exitTrack)
def doFurious(toon, volume = 1):
duration = toon.getDuration('angry', 'torso')
sfx = None
sfx = base.loader.loadSfx('phase_4/audio/sfx/furious_03.ogg')
def playSfx(volume = 1):
base.playSfx(sfx, volume=volume, node=toon)
track = Sequence(Func(toon.angryEyes), Func(toon.blinkEyes), Func(toon.showAngryMuzzle), Func(toon.play, 'angry'), Func(playSfx, volume))
exitTrack = Sequence(Func(toon.normalEyes), Func(toon.blinkEyes), Func(toon.hideAngryMuzzle))
return (track, duration, exitTrack)
def doLaugh(toon, volume = 1):
sfx = None
sfx = base.loader.loadSfx('phase_4/audio/sfx/avatar_emotion_laugh.ogg')
def playSfx(volume = 1):
base.playSfx(sfx, volume=volume, node=toon)
def playAnim():
toon.setPlayRate(10, 'neutral')
toon.loop('neutral')
def stopAnim():
toon.setPlayRate(1, 'neutral')
track = Sequence(Func(toon.blinkEyes), Func(toon.showLaughMuzzle), Func(playAnim), Func(playSfx, volume))
exitTrack = Sequence(Func(toon.hideLaughMuzzle), Func(toon.blinkEyes), Func(stopAnim))
return (track, 2, exitTrack)
def getSingingNote(toon, note, volume = 1):
sfx = None
filePath = 'phase_3.5/audio/dial/'
filePrefix = 'tt_s_dlg_sng_'
fileSuffix = '.ogg'
speciesName = ToonDNA.getSpeciesName(toon.style.head)
sfx = base.loader.loadSfx(filePath + filePrefix + speciesName + '_' + note + fileSuffix)
def playSfx(volume = 1):
base.playSfx(sfx, volume=volume, node=toon)
def playAnim():
toon.loop('neutral')
def stopAnim():
toon.setPlayRate(1, 'neutral')
track = Sequence(Func(toon.showSurpriseMuzzle), Parallel(Func(playAnim), Func(playSfx, volume)))
exitTrack = Sequence(Func(toon.hideSurpriseMuzzle), Func(stopAnim))
return (track, 0.1, exitTrack)
def playSingingAnim(toon):
pass
def stopSinginAnim(toon):
pass
def singNote1(toon, volume = 1):
if base.config.GetBool('want-octaves', True):
if toon.style.getTorsoSize() == 'short':
return getSingingNote(toon, 'g1')
elif toon.style.getTorsoSize() == 'medium':
return getSingingNote(toon, 'g2')
elif toon.style.getTorsoSize() == 'long':
return getSingingNote(toon, 'g3')
def singNote2(toon, volume = 1):
if base.config.GetBool('want-octaves', True):
if toon.style.getTorsoSize() == 'short':
return getSingingNote(toon, 'a1')
elif toon.style.getTorsoSize() == 'medium':
return getSingingNote(toon, 'a2')
elif toon.style.getTorsoSize() == 'long':
return getSingingNote(toon, 'a3')
def singNote3(toon, volume = 1):
if base.config.GetBool('want-octaves', True):
if toon.style.getTorsoSize() == 'short':
return getSingingNote(toon, 'b1')
elif toon.style.getTorsoSize() == 'medium':
return getSingingNote(toon, 'b2')
elif toon.style.getTorsoSize() == 'long':
return getSingingNote(toon, 'b3')
def singNote4(toon, volume = 1):
if base.config.GetBool('want-octaves', True):
if toon.style.getTorsoSize() == 'short':
return getSingingNote(toon, 'c1')
elif toon.style.getTorsoSize() == 'medium':
return getSingingNote(toon, 'c2')
elif toon.style.getTorsoSize() == 'long':
return getSingingNote(toon, 'c3')
def singNote5(toon, volume = 1):
if base.config.GetBool('want-octaves', True):
if toon.style.getTorsoSize() == 'short':
return getSingingNote(toon, 'd1')
elif toon.style.getTorsoSize() == 'medium':
return getSingingNote(toon, 'd2')
elif toon.style.getTorsoSize() == 'long':
return getSingingNote(toon, 'd3')
def singNote6(toon, volume = 1):
if base.config.GetBool('want-octaves', True):
if toon.style.getTorsoSize() == 'short':
return getSingingNote(toon, 'e1')
elif toon.style.getTorsoSize() == 'medium':
return getSingingNote(toon, 'e2')
elif toon.style.getTorsoSize() == 'long':
return getSingingNote(toon, 'e3')
def singNote7(toon, volume = 1):
if base.config.GetBool('want-octaves', True):
if toon.style.getTorsoSize() == 'short':
return getSingingNote(toon, 'f1')
elif toon.style.getTorsoSize() == 'medium':
return getSingingNote(toon, 'f2')
elif toon.style.getTorsoSize() == 'long':
return getSingingNote(toon, 'f3')
def singNote8(toon, volume = 1):
if base.config.GetBool('want-octaves', True):
if toon.style.getTorsoSize() == 'short':
return getSingingNote(toon, 'g2')
elif toon.style.getTorsoSize() == 'medium':
return getSingingNote(toon, 'g3')
elif toon.style.getTorsoSize() == 'long':
return getSingingNote(toon, 'g4')
def singNoteEmpty(toon, volume = 0):
track = Sequence()
return (track, 0.1, None)
def returnToLastAnim(toon):
if hasattr(toon, 'playingAnim') and toon.playingAnim:
toon.loop(toon.playingAnim)
elif not hasattr(toon, 'hp') or toon.hp > 0:
toon.loop('neutral')
else:
toon.loop('sad-neutral')
EmoteFunc = [[doWave, 0],
[doHappy, 0],
[doSad, 0],
[doAnnoyed, 0],
[doSleep, 0],
[doShrug, 0],
[doVictory, 0],
[doThink, 0],
[doBored, 0],
[doApplause, 0],
[doCringe, 0],
[doConfused, 0],
[doSlipForward, 0],
[doBow, 0],
[doSlipBackward, 0],
[doResistanceSalute, 0],
[doNothing, 0],
[doYes, 0],
[doNo, 0],
[doOk, 0],
[doSurprise, 0],
[doUpset, 0],
[doDelighted, 0],
[doFurious, 0],
[doLaugh, 0]]
class TTEmote(Emote.Emote):
notify = DirectNotifyGlobal.directNotify.newCategory('TTEmote')
SLEEP_INDEX = 4
def __init__(self):
self.emoteFunc = EmoteFunc
self.bodyEmotes = [0,
1,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
20,
21,
22,
23,
24]
self.headEmotes = [2,
17,
18,
19]
if len(self.emoteFunc) != len(OTPLocalizer.EmoteList):
self.notify.error('Emote.EmoteFunc and OTPLocalizer.EmoteList are different lengths.')
self.track = None
self.stateChangeMsgLocks = 0
self.stateHasChanged = 0
return
def lockStateChangeMsg(self):
self.stateChangeMsgLocks += 1
def unlockStateChangeMsg(self):
if self.stateChangeMsgLocks <= 0:
print(PythonUtil.lineTag() + ': someone unlocked too many times')
return
self.stateChangeMsgLocks -= 1
if self.stateChangeMsgLocks == 0 and self.stateHasChanged:
messenger.send(self.EmoteEnableStateChanged)
self.stateHasChanged = 0
def emoteEnableStateChanged(self):
if self.stateChangeMsgLocks > 0:
self.stateHasChanged = 1
else:
messenger.send(self.EmoteEnableStateChanged)
def disableAll(self, toon, msg = None):
if toon != base.localAvatar:
return
self.disableGroup(list(range(len(self.emoteFunc))), toon)
def releaseAll(self, toon, msg = None):
if toon != base.localAvatar:
return
self.enableGroup(list(range(len(self.emoteFunc))), toon)
def disableBody(self, toon, msg = None):
if toon != base.localAvatar:
return
self.disableGroup(self.bodyEmotes, toon)
def releaseBody(self, toon, msg = None):
if toon != base.localAvatar:
return
self.enableGroup(self.bodyEmotes, toon)
def disableHead(self, toon, msg = None):
if toon != base.localAvatar:
return
self.disableGroup(self.headEmotes, toon)
def releaseHead(self, toon, msg = None):
if toon != base.localAvatar:
return
self.enableGroup(self.headEmotes, toon)
def getHeadEmotes(self):
return self.headEmotes
def disableGroup(self, indices, toon):
self.lockStateChangeMsg()
for i in indices:
self.disable(i, toon)
self.unlockStateChangeMsg()
def enableGroup(self, indices, toon):
self.lockStateChangeMsg()
for i in indices:
self.enable(i, toon)
self.unlockStateChangeMsg()
def disable(self, index, toon):
if isinstance(index, str):
index = OTPLocalizer.EmoteFuncDict[index]
self.emoteFunc[index][1] = self.emoteFunc[index][1] + 1
if toon is base.localAvatar:
if self.emoteFunc[index][1] == 1:
self.emoteEnableStateChanged()
def enable(self, index, toon):
if isinstance(index, str):
index = OTPLocalizer.EmoteFuncDict[index]
self.emoteFunc[index][1] = self.emoteFunc[index][1] - 1
if toon is base.localAvatar:
if self.emoteFunc[index][1] == 0:
self.emoteEnableStateChanged()
def doEmote(self, toon, emoteIndex, ts = 0, volume = 1):
try:
func = self.emoteFunc[emoteIndex][0]
except:
print('Error in finding emote func %s' % emoteIndex)
return (None, None)
def clearEmoteTrack():
base.localAvatar.emoteTrack = None
base.localAvatar.d_setEmoteState(self.EmoteClear, 1.0)
return
if volume == 1:
track, duration, exitTrack = func(toon)
else:
track, duration, exitTrack = func(toon, volume)
if track != None:
track = Sequence(Func(self.disableAll, toon, 'doEmote'), track)
if duration > 0:
track = Sequence(track, Wait(duration))
if exitTrack != None:
track = Sequence(track, exitTrack)
if duration > 0:
track = Sequence(track, Func(returnToLastAnim, toon))
track = Sequence(track, Func(self.releaseAll, toon, 'doEmote'), autoFinish=1)
if toon.isLocal():
track = Sequence(track, Func(clearEmoteTrack))
if track != None:
if toon.emote != None:
toon.emote.finish()
toon.emote = None
toon.emote = track
track.start(ts)
del clearEmoteTrack
return (track, duration)
def printEmoteState(self, action, msg):
pass
Emote.globalEmote = TTEmote()