ToonTownRewritten/toontown/classicchars/CharStateDatas.py

225 lines
9.8 KiB
Python

from pandac.PandaModules import *
from direct.interval.IntervalGlobal import *
from direct.distributed.ClockDelta import *
from direct.fsm import StateData
from direct.directnotify import DirectNotifyGlobal
from direct.showbase.PythonUtil import *
from direct.task import Task
import CCharPaths
from toontown.toonbase import ToontownGlobals
class CharNeutralState(StateData.StateData):
notify = DirectNotifyGlobal.directNotify.newCategory('CharNeutralState')
def __init__(self, doneEvent, character):
StateData.StateData.__init__(self, doneEvent)
self.__doneEvent = doneEvent
self.character = character
def enter(self, startTrack = None, playRate = None):
StateData.StateData.enter(self)
self.notify.debug('Neutral ' + self.character.getName() + '...')
self.__neutralTrack = Sequence(name=self.character.getName() + '-neutral')
if startTrack:
self.__neutralTrack.append(startTrack)
if playRate:
self.__neutralTrack.append(Func(self.character.setPlayRate, playRate, 'neutral'))
self.__neutralTrack.append(Func(self.character.loop, 'neutral'))
self.__neutralTrack.start()
def exit(self):
StateData.StateData.exit(self)
self.__neutralTrack.finish()
def __doneHandler(self):
doneStatus = {}
doneStatus['state'] = 'walk'
doneStatus['status'] = 'done'
messenger.send(self.__doneEvent, [doneStatus])
return Task.done
class CharWalkState(StateData.StateData):
notify = DirectNotifyGlobal.directNotify.newCategory('CharWalkState')
def __init__(self, doneEvent, character, diffPath = None):
StateData.StateData.__init__(self, doneEvent)
self.doneEvent = doneEvent
self.character = character
if diffPath == None:
self.paths = CCharPaths.getPaths(character.getName(), character.getCCLocation())
else:
self.paths = CCharPaths.getPaths(diffPath, character.getCCLocation())
self.speed = character.walkSpeed()
self.offsetX = 0
self.offsetY = 0
self.oldOffsetX = 0
self.olfOffsetY = 0
self.walkTrack = None
return
def enter(self, startTrack = None, playRate = None):
StateData.StateData.enter(self)
self.notify.debug('Walking ' + self.character.getName() + '... from ' + str(self.walkInfo[0]) + ' to ' + str(self.walkInfo[1]))
posPoints = CCharPaths.getPointsFromTo(self.walkInfo[0], self.walkInfo[1], self.paths)
lastPos = posPoints[-1]
newLastPos = Point3(lastPos[0] + self.offsetX, lastPos[1] + self.offsetY, lastPos[2])
posPoints[-1] = newLastPos
firstPos = posPoints[0]
newFirstPos = Point3(firstPos[0] + self.oldOffsetX, firstPos[1] + self.oldOffsetY, firstPos[2])
posPoints[0] = newFirstPos
self.walkTrack = Sequence(name=self.character.getName() + '-walk')
if startTrack:
self.walkTrack.append(startTrack)
self.character.setPos(posPoints[0])
raycast = CCharPaths.getRaycastFlag(self.walkInfo[0], self.walkInfo[1], self.paths)
moveTrack = self.makePathTrack(self.character, posPoints, self.speed, raycast)
if playRate:
self.walkTrack.append(Func(self.character.setPlayRate, playRate, 'walk'))
self.walkTrack.append(Func(self.character.loop, 'walk'))
self.walkTrack.append(moveTrack)
doneEventName = self.character.getName() + 'WalkDone'
self.walkTrack.append(Func(messenger.send, doneEventName))
ts = globalClockDelta.localElapsedTime(self.walkInfo[2])
self.accept(doneEventName, self.doneHandler)
self.notify.debug('walkTrack.start(%s)' % ts)
self.walkTrack.start(ts)
def makePathTrack(self, nodePath, posPoints, velocity, raycast = 0):
track = Sequence()
if raycast:
track.append(Func(nodePath.enableRaycast, 1))
startHpr = nodePath.getHpr()
for pointIndex in range(len(posPoints) - 1):
startPoint = posPoints[pointIndex]
endPoint = posPoints[pointIndex + 1]
track.append(Func(nodePath.setPos, startPoint))
distance = Vec3(endPoint - startPoint).length()
duration = distance / velocity
curHpr = nodePath.getHpr()
nodePath.headsUp(endPoint[0], endPoint[1], endPoint[2])
destHpr = nodePath.getHpr()
reducedCurH = reduceAngle(curHpr[0])
reducedCurHpr = Vec3(reducedCurH, curHpr[1], curHpr[2])
reducedDestH = reduceAngle(destHpr[0])
shortestAngle = closestDestAngle(reducedCurH, reducedDestH)
shortestHpr = Vec3(shortestAngle, destHpr[1], destHpr[2])
turnTime = abs(shortestAngle) / 270.0
nodePath.setHpr(shortestHpr)
if duration - turnTime > 0.01:
track.append(Parallel(Func(nodePath.loop, 'walk'), LerpHprInterval(nodePath, turnTime, shortestHpr, startHpr=reducedCurHpr, name='lerp' + nodePath.getName() + 'Hpr'), LerpPosInterval(nodePath, duration=duration - turnTime, pos=Point3(endPoint), startPos=Point3(startPoint), fluid=1)))
nodePath.setHpr(startHpr)
if raycast:
track.append(Func(nodePath.enableRaycast, 0))
return track
def doneHandler(self):
doneStatus = {}
doneStatus['state'] = 'walk'
doneStatus['status'] = 'done'
messenger.send(self.doneEvent, [doneStatus])
return Task.done
def exit(self):
StateData.StateData.exit(self)
self.ignore(self.character.getName() + 'WalkDone')
if self.walkTrack:
self.walkTrack.finish()
self.walkTrack = None
return
def setWalk(self, srcNode, destNode, timestamp, offsetX = 0, offsetY = 0):
self.oldOffsetX = self.offsetX
self.oldOffsetY = self.offsetY
self.walkInfo = (srcNode, destNode, timestamp)
self.offsetX = offsetX
self.offsetY = offsetY
class CharFollowChipState(CharWalkState):
notify = DirectNotifyGlobal.directNotify.newCategory('CharFollowChipState')
completeRevolutionDistance = 13
def __init__(self, doneEvent, character, chipId):
CharWalkState.__init__(self, doneEvent, character)
self.offsetDict = {'a': (ToontownGlobals.DaleOrbitDistance, 0)}
self.chipId = chipId
def setWalk(self, srcNode, destNode, timestamp, offsetX = 0, offsetY = 0):
self.offsetDict[destNode] = (offsetX, offsetY)
self.srcNode = srcNode
self.destNode = destNode
self.orbitDistance = ToontownGlobals.DaleOrbitDistance
if (srcNode, destNode) in CCharPaths.DaleOrbitDistanceOverride:
self.orbitDistance = CCharPaths.DaleOrbitDistanceOverride[srcNode, destNode]
elif (destNode, srcNode) in CCharPaths.DaleOrbitDistanceOverride:
self.orbitDistance = CCharPaths.DaleOrbitDistanceOverride[destNode, srcNode]
CharWalkState.setWalk(self, srcNode, destNode, timestamp, offsetX, offsetY)
def makePathTrack(self, nodePath, posPoints, velocity, raycast = 0):
retval = Sequence()
if raycast:
retval.append(Func(nodePath.enableRaycast, 1))
chip = base.cr.doId2do.get(self.chipId)
self.chipPaths = CCharPaths.getPaths(chip.getName(), chip.getCCLocation())
self.posPoints = posPoints
chipDuration = chip.walk.walkTrack.getDuration()
self.notify.debug('chipDuration = %f' % chipDuration)
chipDistance = CCharPaths.getWalkDistance(self.srcNode, self.destNode, ToontownGlobals.ChipSpeed, self.chipPaths)
self.revolutions = chipDistance / self.completeRevolutionDistance
srcOffset = (0, 0)
if self.srcNode in self.offsetDict:
srcOffset = self.offsetDict[self.srcNode]
srcTheta = math.atan2(srcOffset[1], srcOffset[0])
if srcTheta < 0:
srcTheta += 2 * math.pi
if srcTheta > 0:
srcRev = (2 * math.pi - srcTheta) / (2 * math.pi)
else:
srcRev = 0
self.srcTheta = srcTheta
destOffset = (0, 0)
if self.destNode in self.offsetDict:
destOffset = self.offsetDict[self.destNode]
destTheta = math.atan2(destOffset[1], destOffset[0])
if destTheta < 0:
destTheta += 2 * math.pi
self.destTheta = destTheta
self.revolutions += srcRev
endingTheta = srcTheta + self.revolutions % 1.0 * 2 * math.pi
diffTheta = destTheta - endingTheta
destRev = diffTheta / (2 * math.pi)
self.revolutions += destRev
while self.revolutions < 1:
self.revolutions += 1
def positionDale(t):
self.orbitChip(t)
retval.append(LerpFunctionInterval(positionDale, chipDuration))
if raycast:
retval.append(Func(nodePath.enableRaycast, 0))
return retval
def orbitChip(self, t):
srcOffset = (0, 0)
if self.srcNode in self.offsetDict:
srcOffset = self.offsetDict[self.srcNode]
chipSrcPos = Point3(self.posPoints[0][0] - srcOffset[0], self.posPoints[0][1] - srcOffset[1], self.posPoints[0][2])
destOffset = (0, 0)
if self.destNode in self.offsetDict:
destOffset = self.offsetDict[self.destNode]
chipDestPos = Point3(self.posPoints[-1][0] - destOffset[0], self.posPoints[-1][1] - destOffset[1], self.posPoints[-1][2])
displacement = chipDestPos - chipSrcPos
displacement *= t
chipPos = chipSrcPos + displacement
diffTheta = t * self.revolutions * 2 * math.pi
curTheta = self.srcTheta + diffTheta
newOffsetX = math.cos(curTheta) * self.orbitDistance
newOffsetY = math.sin(curTheta) * self.orbitDistance
dalePos = Point3(chipPos[0] + newOffsetX, chipPos[1] + newOffsetY, chipPos[2])
self.character.setPos(dalePos)
newHeading = rad2Deg(curTheta)
newHeading %= 360
self.character.setH(newHeading)