toontown-just-works/toontown/golf/DistributedGolfHoleAI.py
2024-07-07 18:08:39 -05:00

493 lines
18 KiB
Python

from direct.distributed import DistributedObjectAI
from direct.directnotify import DirectNotifyGlobal
from toontown.toonbase import ToontownGlobals
from panda3d.core import *
import DistributedPhysicsWorldAI
from direct.fsm.FSM import FSM
from toontown.ai.ToonBarrier import *
from toontown.golf import GolfGlobals
import random
from toontown.golf import GolfHoleBase
class DistributedGolfHoleAI(DistributedPhysicsWorldAI.DistributedPhysicsWorldAI, FSM, GolfHoleBase.GolfHoleBase):
defaultTransitions = {'Off': ['Cleanup', 'WaitTee'],
'WaitTee': ['WaitSwing',
'Cleanup',
'WaitTee',
'WaitPlayback'],
'WaitSwing': ['WaitPlayback',
'Cleanup',
'WaitSwing',
'WaitTee'],
'WaitPlayback': ['WaitSwing',
'Cleanup',
'WaitTee',
'WaitPlayback'],
'Cleanup': ['Off']}
id = 0
notify = directNotify.newCategory('DistributedGolfHoleAI')
def __init__(self, zoneId, golfCourse, holeId):
FSM.__init__(self, 'Golf_%s_FSM' % self.id)
DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.__init__(self, simbase.air)
GolfHoleBase.GolfHoleBase.__init__(self)
self.zoneId = zoneId
self.golfCourse = golfCourse
self.holeId = holeId
self.avIdList = golfCourse.avIdList[:]
self.watched = [0,
0,
0,
0]
self.barrierPlayback = None
self.trustedAvId = None
self.activeGolferIndex = None
self.activeGolferId = None
self.holeInfo = GolfGlobals.HoleInfo[self.holeId]
self.teeChosen = {}
for avId in self.avIdList:
self.teeChosen[avId] = -1
self.ballPos = {}
for avId in self.avIdList:
self.ballPos[avId] = Vec3(0, 0, 0)
self.playStarted = False
return
def curGolfBall(self):
return self.ball
def generate(self):
DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.generate(self)
self.ball = self.createBall()
self.createRays()
if len(self.teePositions) > 1:
startPos = self.teePositions[1]
else:
startPos = self.teePositions[0]
startPos += Vec3(0, 0, GolfGlobals.GOLF_BALL_RADIUS)
self.ball.setPosition(startPos)
def delete(self):
self.notify.debug('__delete__')
DistributedPhysicsWorldAI.DistributedPhysicsWorldAI.delete(self)
self.notify.debug('calling self.terrainModel.removeNode')
self.terrainModel.removeNode()
self.notify.debug('self.barrierPlayback is %s' % self.barrierPlayback)
if self.barrierPlayback:
self.notify.debug('calling self.barrierPlayback.cleanup')
self.barrierPlayback.cleanup()
self.notify.debug('calling self.barrierPlayback = None')
self.barrierPlayback = None
self.activeGolferId = None
return
def setZoneId(self, zoneId):
self.zoneId = zoneId
def setAvatarReadyHole(self):
self.notify.debugStateCall(self)
avId = self.air.getAvatarIdFromSender()
self.golfCourse.avatarReadyHole(avId)
def startPlay(self):
self.notify.debug('startPlay')
self.playStarted = True
self.numGolfers = len(self.golfCourse.getGolferIds())
self.selectNextGolfer()
def selectNextGolfer(self):
self.notify.debug('selectNextGolfer, old golferIndex=%s old golferId=%s' % (self.activeGolferIndex, self.activeGolferId))
if self.golfCourse.isCurHoleDone():
return
if self.activeGolferIndex == None:
self.activeGolferIndex = 0
self.activeGolferId = self.golfCourse.getGolferIds()[self.activeGolferIndex]
else:
self.activeGolferIndex += 1
if self.activeGolferIndex >= len(self.golfCourse.getGolferIds()):
self.activeGolferIndex = 0
self.activeGolferId = self.golfCourse.getGolferIds()[self.activeGolferIndex]
safety = 0
while safety < 50 and not self.golfCourse.checkGolferPlaying(self.golfCourse.getGolferIds()[self.activeGolferIndex]):
self.activeGolferIndex += 1
self.notify.debug('Index %s' % self.activeGolferIndex)
if self.activeGolferIndex >= len(self.golfCourse.getGolferIds()):
self.activeGolferIndex = 0
self.activeGolferId = self.golfCourse.getGolferIds()[self.activeGolferIndex]
safety += 1
if safety != 50:
golferId = self.golfCourse.getGolferIds()[self.activeGolferIndex]
if self.teeChosen[golferId] == -1:
self.sendUpdate('golferChooseTee', [golferId])
self.request('WaitTee')
else:
self.sendUpdate('golfersTurn', [golferId])
self.request('WaitSwing')
else:
self.notify.debug('safety')
self.notify.debug('selectNextGolfer, new golferIndex=%s new golferId=%s' % (self.activeGolferIndex, self.activeGolferId))
return
def clearWatched(self):
self.watched = [1,
1,
1,
1]
for index in xrange(len(self.golfCourse.getGolferIds())):
self.watched[index] = 0
def setWatched(self, avId):
for index in xrange(len(self.golfCourse.getGolferIds())):
if self.golfCourse.getGolferIds()[index] == avId:
self.watched[index] = 1
def checkWatched(self):
if 0 not in self.watched:
return True
else:
return False
def turnDone(self):
self.notify.debug('Turn Done')
avId = self.air.getAvatarIdFromSender()
if self.barrierPlayback:
self.barrierPlayback.clear(avId)
def ballInHole(self, golferId = None):
self.notify.debug('ballInHole')
if golferId:
avId = golferId
else:
avId = self.air.getAvatarIdFromSender()
self.golfCourse.setBallIn(avId)
if self.golfCourse.isCurHoleDone():
self.notify.debug('ballInHole doing nothing')
else:
self.notify.debug('ballInHole calling self.selectNextGolfer')
self.selectNextGolfer()
def getHoleId(self):
return self.holeId
def finishHole(self):
self.notify.debug('finishHole')
self.golfCourse.holeOver()
def getGolferIds(self):
return self.avIdList
def loadLevel(self):
GolfHoleBase.GolfHoleBase.loadLevel(self)
optionalObjects = self.terrainModel.findAllMatches('**/optional*')
requiredObjects = self.terrainModel.findAllMatches('**/required*')
self.parseLocators(optionalObjects, 1)
self.parseLocators(requiredObjects, 0)
self.teeNodePath = self.terrainModel.find('**/tee0')
if self.teeNodePath.isEmpty():
teePos = Vec3(0, 0, 10)
else:
teePos = self.teeNodePath.getPos()
teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS)
self.notify.debug('teeNodePath heading = %s' % self.teeNodePath.getH())
self.teePositions = [teePos]
teeIndex = 1
teeNode = self.terrainModel.find('**/tee%d' % teeIndex)
while not teeNode.isEmpty():
teePos = teeNode.getPos()
teePos.setZ(teePos.getZ() + GolfGlobals.GOLF_BALL_RADIUS)
self.teePositions.append(teePos)
self.notify.debug('teeNodeP heading = %s' % teeNode.getH())
teeIndex += 1
teeNode = self.terrainModel.find('**/tee%d' % teeIndex)
def createLocatorDict(self):
self.locDict = {}
locatorNum = 1
curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum)
while not curNodePath.isEmpty():
self.locDict[locatorNum] = curNodePath
locatorNum += 1
curNodePath = self.hardSurfaceNodePath.find('**/locator%d' % locatorNum)
def loadBlockers(self):
loadAll = simbase.config.GetBool('golf-all-blockers', 0)
self.createLocatorDict()
self.blockerNums = self.holeInfo['blockers']
for locatorNum in self.locDict:
if locatorNum in self.blockerNums or loadAll:
locator = self.locDict[locatorNum]
locatorParent = locator.getParent()
locator.getChildren().wrtReparentTo(locatorParent)
else:
self.locDict[locatorNum].removeNode()
self.hardSurfaceNodePath.flattenStrong()
def createBall(self):
golfBallGeom = self.createSphere(self.world, self.space, GolfGlobals.GOLF_BALL_DENSITY, GolfGlobals.GOLF_BALL_RADIUS, 1)[1]
return golfBallGeom
def preStep(self):
GolfHoleBase.GolfHoleBase.preStep(self)
def postStep(self):
GolfHoleBase.GolfHoleBase.postStep(self)
def postSwing(self, cycleTime, power, x, y, z, dirX, dirY):
avId = self.air.getAvatarIdFromSender()
self.storeAction = [avId,
cycleTime,
power,
x,
y,
z,
dirX,
dirY]
if self.commonHoldData:
self.doAction()
def postSwingState(self, cycleTime, power, x, y, z, dirX, dirY, curAimTime, commonObjectData):
self.notify.debug('postSwingState')
if not self.golfCourse.getStillPlayingAvIds():
return
avId = self.air.getAvatarIdFromSender()
self.storeAction = [avId,
cycleTime,
power,
x,
y,
z,
dirX,
dirY]
self.commonHoldData = commonObjectData
self.trustedAvId = self.chooseAvatarToSimulate()
self.sendUpdateToAvatarId(self.trustedAvId, 'assignRecordSwing', [avId,
cycleTime,
power,
x,
y,
z,
dirX,
dirY,
commonObjectData])
self.golfCourse.addAimTime(avId, curAimTime)
def chooseAvatarToSimulate(self):
stillPlaying = self.golfCourse.getStillPlayingAvIds()
return stillPlaying[0] if stillPlaying else 0
def ballMovie2AI(self, cycleTime, avId, movie, spinMovie, ballInFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame, commonObjectData):
sentFromId = self.air.getAvatarIdFromSender()
if sentFromId == self.trustedAvId:
lastFrameNum = len(movie) - 2
if lastFrameNum < 0:
lastFrameNum = 0
lastFrame = movie[lastFrameNum]
lastPos = Vec3(lastFrame[1], lastFrame[2], lastFrame[3])
self.ballPos[avId] = lastPos
self.golfCourse.incrementScore(avId)
for id in self.golfCourse.getStillPlayingAvIds():
if not id == sentFromId:
self.sendUpdateToAvatarId(id, 'ballMovie2Client', [cycleTime,
avId,
movie,
spinMovie,
ballInFrame,
ballTouchedHoleFrame,
ballFirstTouchedHoleFrame,
commonObjectData])
if self.state == 'WaitPlayback' or self.state == 'WaitTee':
self.notify.warning('ballMovie2AI requesting from %s to WaitPlayback' % self.state)
self.request('WaitPlayback')
elif self.trustedAvId == None:
return
else:
self.doAction()
self.trustedAvId = None
return
def performReadyAction(self):
avId = self.storeAction[0]
if self.state == 'WaitPlayback':
self.notify.debugStateCall(self)
self.notify.debug('ignoring the postSwing for avId=%d since we are in WaitPlayback' % avId)
return
if avId == self.activeGolferId:
self.golfCourse.incrementScore(self.activeGolferId)
else:
self.notify.warning('activGolferId %d not equal to sender avId %d' % (self.activeGolferId, avId))
if avId not in self.golfCourse.drivingToons:
position = self.ballPos[avId]
else:
position = Vec3(self.storeAction[3], self.storeAction[4], self.storeAction[5])
self.useCommonObjectData(self.commonHoldData)
newPos = self.trackRecordBodyFlight(self.ball, self.storeAction[1], self.storeAction[2], position, self.storeAction[6], self.storeAction[7])
if self.state == 'WaitPlayback' or self.state == 'WaitTee':
self.notify.warning('performReadyAction requesting from %s to WaitPlayback' % self.state)
self.request('WaitPlayback')
self.sendUpdate('ballMovie2Client', [self.storeAction[1],
avId,
self.recording,
self.aVRecording,
self.ballInHoleFrame,
self.ballTouchedHoleFrame,
self.ballFirstTouchedHoleFrame,
self.commonHoldData])
self.ballPos[avId] = newPos
self.trustedAvId = None
return
def postResult(self, cycleTime, avId, recording, aVRecording, ballInHoleFrame, ballTouchedHoleFrame, ballFirstTouchedHoleFrame):
pass
def enterWaitSwing(self):
pass
def exitWaitSwing(self):
pass
def enterWaitTee(self):
pass
def exitWaitTee(self):
pass
def enterWaitPlayback(self):
self.notify.debug('enterWaitPlayback')
stillPlayingList = self.golfCourse.getStillPlayingAvIds()
self.barrierPlayback = ToonBarrier('waitClientsPlayback', self.uniqueName('waitClientsPlayback'), stillPlayingList, 120, self.handleWaitPlaybackDone, self.handlePlaybackTimeout)
def hasCurGolferReachedMaxSwing(self):
strokes = self.golfCourse.getCurHoleScore(self.activeGolferId)
maxSwing = self.holeInfo['maxSwing']
retval = strokes >= maxSwing
if retval:
av = simbase.air.doId2do.get(self.activeGolferId)
if av:
if av.getUnlimitedSwing():
retval = False
return retval
def handleWaitPlaybackDone(self):
if self.isCurBallInHole(self.activeGolferId) or self.hasCurGolferReachedMaxSwing():
if self.activeGolferId:
self.ballInHole(self.activeGolferId)
else:
self.selectNextGolfer()
def isCurBallInHole(self, golferId):
retval = False
for holePos in self.holePositions:
displacement = self.ballPos[golferId] - holePos
length = displacement.length()
self.notify.debug('hole %s length=%s' % (holePos, length))
if length <= GolfGlobals.DistanceToBeInHole:
retval = True
break
return retval
def exitWaitPlayback(self):
self.notify.debug('exitWaitPlayback')
if hasattr(self, 'barrierPlayback') and self.barrierPlayback:
self.barrierPlayback.cleanup()
self.barrierPlayback = None
return
def enterCleanup(self):
pass
def exitCleanup(self):
pass
def handlePlaybackTimeout(self, task = None):
self.notify.debug('handlePlaybackTimeout')
self.handleWaitPlaybackDone()
def getGolfCourseDoId(self):
return self.golfCourse.doId
def avatarDropped(self, avId):
self.notify.warning('avId %d dropped, self.state=%s' % (avId, self.state))
if self.barrierPlayback:
self.barrierPlayback.clear(avId)
else:
if avId == self.trustedAvId:
self.doAction()
if avId == self.activeGolferId and not self.golfCourse.haveAllGolfersExited():
self.selectNextGolfer()
def setAvatarTee(self, chosenTee):
golferId = self.air.getAvatarIdFromSender()
self.teeChosen[golferId] = chosenTee
self.ballPos[golferId] = self.teePositions[chosenTee]
self.sendUpdate('setAvatarFinalTee', [golferId, chosenTee])
self.sendUpdate('golfersTurn', [golferId])
self.request('WaitSwing')
def setBox(self, pos0, pos1, pos2, quat0, quat1, quat2, quat3, anV0, anV1, anV2, lnV0, lnV1, lnV2):
self.sendUpdate('sendBox', [pos0,
pos1,
pos2,
quat0,
quat1,
quat2,
quat3,
anV0,
anV1,
anV2,
lnV0,
lnV1,
lnV2])
def parseLocators(self, objectCollection, optional = 0):
if optional and objectCollection.getNumPaths():
if 'optionalMovers' in self.holeInfo:
for optionalMoverId in self.holeInfo['optionalMovers']:
searchStr = 'optional_mover_' + str(optionalMoverId)
for objIndex in xrange(objectCollection.getNumPaths()):
object = objectCollection.getPath(objIndex)
if searchStr in object.getName():
self.fillLocator(objectCollection, objIndex)
break
else:
for index in xrange(objectCollection.getNumPaths()):
self.fillLocator(objectCollection, index)
def fillLocator(self, objectCollection, index):
path = objectCollection[index]
pathName = path.getName()
pathArray = pathName.split('_')
sizeX = None
sizeY = None
move = None
type = None
for subString in pathArray:
if subString[:1] == 'X':
dataString = subString[1:]
dataString = dataString.replace('p', '.')
sizeX = float(dataString)
elif subString[:1] == 'Y':
dataString = subString[1:]
dataString = dataString.replace('p', '.')
sizeY = float(dataString)
elif subString[:1] == 'd':
dataString = subString[1:]
dataString = dataString.replace('p', '.')
move = float(dataString)
elif subString == 'mover':
type = 4
elif subString == 'windmillLocator':
type = 3
if type == 4 and move and sizeX and sizeY:
self.createCommonObject(4, path.getPos(), path.getHpr(), sizeX, sizeY, move)
elif type == 3:
self.createCommonObject(3, path.getPos(), path.getHpr())
return