2022-12-16 18:40:57 -06:00
|
|
|
from panda3d.core import *
|
2019-11-02 17:27:54 -05:00
|
|
|
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
|
2019-12-30 00:07:56 -06:00
|
|
|
from . import CCharPaths
|
2019-11-02 17:27:54 -05:00
|
|
|
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
|
2021-07-09 22:41:59 -05:00
|
|
|
StateData.StateData.load(self)
|
2019-11-02 17:27:54 -05:00
|
|
|
|
|
|
|
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
|
2021-07-09 22:41:59 -05:00
|
|
|
StateData.StateData.load(self)
|
2019-11-02 17:27:54 -05:00
|
|
|
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)
|