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