998 lines
37 KiB
Python
998 lines
37 KiB
Python
from direct.distributed import DistributedObjectAI
|
|
from direct.directnotify import DirectNotifyGlobal
|
|
from toontown.toonbase import ToontownGlobals
|
|
from toontown.golf import DistributedGolfHoleAI
|
|
from panda3d.core import *
|
|
from direct.fsm.FSM import FSM
|
|
from toontown.ai.ToonBarrier import *
|
|
from toontown.golf import GolfGlobals
|
|
from toontown.uberdog import TopToonsGlobals
|
|
INITIAL = 0
|
|
EXITED = 1
|
|
EXPECTED = 2
|
|
JOINED = 3
|
|
READY = 4
|
|
ONHOLE = 5
|
|
BALLIN = 6
|
|
JOIN_TIMEOUT = 30
|
|
READY_TIMEOUT = 30
|
|
EXIT_TIMEOUT = 30
|
|
REWARD_TIMEOUT = 30
|
|
|
|
class DistributedGolfCourseAI(DistributedObjectAI.DistributedObjectAI, FSM):
|
|
notify = directNotify.newCategory('DistributedGolfCourseAI')
|
|
defaultTransitions = {'Off': ['WaitJoin'],
|
|
'WaitJoin': ['WaitReadyCourse', 'Cleanup'],
|
|
'WaitReadyCourse': ['WaitReadyHole', 'Cleanup'],
|
|
'WaitReadyHole': ['PlayHole',
|
|
'Cleanup',
|
|
'WaitLeaveHole',
|
|
'WaitReward'],
|
|
'PlayHole': ['PlayHole',
|
|
'WaitLeaveHole',
|
|
'Cleanup',
|
|
'WaitReward'],
|
|
'WaitLeaveHole': ['WaitReadyHole',
|
|
'WaitLeaveCourse',
|
|
'Cleanup',
|
|
'WaitReward'],
|
|
'WaitReward': ['WaitLeaveCourse', 'Cleanup', 'WaitLeaveHole'],
|
|
'WaitLeaveCourse': ['Cleanup'],
|
|
'Cleanup': ['Off']}
|
|
|
|
def __init__(self, zoneId, avIds, courseId, preferredHoleId = None):
|
|
FSM.__init__(self, 'GolfCourse_%s_FSM' % zoneId)
|
|
DistributedObjectAI.DistributedObjectAI.__init__(self, simbase.air)
|
|
self.notify.debug('GOLF COURSE: init')
|
|
self.zoneId = zoneId
|
|
self.currentHole = None
|
|
self.avIdList = []
|
|
self.avStateDict = {}
|
|
self.addExpectedGolfers(avIds)
|
|
self.courseId = courseId
|
|
self.preferredHoleId = preferredHoleId
|
|
self.courseInfo = GolfGlobals.CourseInfo[self.courseId]
|
|
self.numHoles = self.courseInfo['numHoles']
|
|
self.holeIds = self.calcHolesToUse()
|
|
self.notify.debug('self.holeIds = %s' % self.holeIds)
|
|
self.numHolesPlayed = 0
|
|
self.curHoleIndex = 0
|
|
self.trophyListLen = 0
|
|
self.courseBestListLen = 0
|
|
self.holeBestListLen = 0
|
|
self.cupListLen = 0
|
|
self.scores = {}
|
|
self.aimTimes = {}
|
|
self.startingHistory = {}
|
|
self.endingHistory = {}
|
|
self.startingHoleBest = {}
|
|
self.endingHoleBest = {}
|
|
self.startingCourseBest = {}
|
|
self.endingCourseBest = {}
|
|
self.startingCups = {}
|
|
self.endingCups = {}
|
|
self.initHistory()
|
|
self.newTrophies = {}
|
|
self.newHoleBest = {}
|
|
self.newCourseBest = {}
|
|
self.newCups = {}
|
|
self.drivingToons = []
|
|
self.__barrier = None
|
|
self.winnerByTieBreak = 0
|
|
return
|
|
|
|
def initHistory(self):
|
|
for avId in self.avIdList:
|
|
av = simbase.air.doId2do.get(avId)
|
|
if av:
|
|
history = av.getGolfHistory()
|
|
self.startingHistory[avId] = history[:]
|
|
self.endingHistory[avId] = history[:]
|
|
holeBest = av.getGolfHoleBest()
|
|
self.startingHoleBest[avId] = holeBest[:]
|
|
self.endingHoleBest[avId] = holeBest[:]
|
|
courseBest = av.getGolfCourseBest()
|
|
self.startingCourseBest[avId] = courseBest[:]
|
|
self.endingCourseBest[avId] = courseBest[:]
|
|
|
|
def generate(self):
|
|
DistributedObjectAI.DistributedObjectAI.generate(self)
|
|
self.grabGolfers()
|
|
|
|
def delete(self):
|
|
self.notify.debug('GOLF COURSE: delete: deleting AI GolfCourse object')
|
|
if hasattr(self, 'rewardBarrier'):
|
|
self.rewardBarrier.cleanup()
|
|
del self.rewardBarrier
|
|
if self.currentHole:
|
|
self.notify.debug('calling requestDelete on hole %d' % self.currentHole.doId)
|
|
self.currentHole.requestDelete()
|
|
self.currentHole = None
|
|
self.ignoreAll()
|
|
from toontown.golf import GolfManagerAI
|
|
GolfManagerAI.GolfManagerAI().removeCourse(self)
|
|
if self.__barrier:
|
|
self.__barrier.cleanup()
|
|
self.__barrier = None
|
|
DistributedObjectAI.DistributedObjectAI.delete(self)
|
|
return
|
|
|
|
def load(self):
|
|
self.b_setCourseReady()
|
|
self.request('WaitReadyCourse')
|
|
|
|
def getZoneId(self):
|
|
return self.zoneId
|
|
|
|
def addExpectedGolfers(self, avIdList):
|
|
self.notify.debug('Sending %s to course %s' % (avIdList, self.zoneId))
|
|
for avId in avIdList:
|
|
golfer = simbase.air.doId2do.get(avId)
|
|
if golfer:
|
|
if avId not in self.avIdList:
|
|
self.avIdList.append(avId)
|
|
self.avStateDict[avId] = INITIAL
|
|
elif self.avStateDict[avId] == EXITED:
|
|
if self.isGenerated():
|
|
pass
|
|
else:
|
|
self.notify.warning('GOLF COURSE: trying to grab golfer %s that is already on the course' % avId)
|
|
|
|
def grabGolfers(self):
|
|
for avId in self.avIdList:
|
|
golfer = simbase.air.doId2do.get(avId)
|
|
if golfer:
|
|
if self.avStateDict[avId] == INITIAL:
|
|
self.avStateDict[avId] = EXPECTED
|
|
|
|
self.request('WaitJoin')
|
|
|
|
def getGolferIds(self):
|
|
return self.avIdList
|
|
|
|
def checkGolferPlaying(self, avId):
|
|
if self.avStateDict[avId] == ONHOLE:
|
|
return 1
|
|
else:
|
|
return 0
|
|
|
|
def b_setCourseReady(self):
|
|
self.setCourseReady()
|
|
self.d_setCourseReady()
|
|
|
|
def d_setCourseReady(self):
|
|
self.notify.debug('GOLF COURSE: Sending setCourseReady')
|
|
self.sendUpdate('setCourseReady', [self.numHoles, self.holeIds, self.calcCoursePar()])
|
|
|
|
def setCourseReady(self):
|
|
self.notify.debug('GOLF COURSE: setCourseReady: golf course ready with avatars: %s' % self.avIdList)
|
|
self.trophyListLen = 0
|
|
self.courseBestListLen = 0
|
|
self.holeBestListLen = 0
|
|
self.cupListLen = 0
|
|
self.normalExit = 1
|
|
|
|
def d_setPlayHole(self):
|
|
self.notify.debug('GOLF COURSE: setPlayHole: play on golf hole about to start')
|
|
self.sendUpdate('setPlayHole', [])
|
|
|
|
def b_setCourseExit(self):
|
|
self.d_setCourseExit()
|
|
self.setCourseExit()
|
|
|
|
def d_setCourseExit(self):
|
|
self.notify.debug('GOLF COURSE: Sending setGameExit')
|
|
self.sendUpdate('setCourseExit', [])
|
|
|
|
def setCourseExit(self):
|
|
self.notify.debug('GOLF COURSE: setGameExit')
|
|
|
|
def handleExitedAvatar(self, avId):
|
|
self.notify.warning('GOLF COURSE: handleExitedAvatar: avatar id exited: ' + str(avId))
|
|
self.avStateDict[avId] = EXITED
|
|
self.sendUpdate('avExited', [avId])
|
|
if self.currentHole and not self.haveAllGolfersExited():
|
|
self.currentHole.avatarDropped(avId)
|
|
if self.haveAllGolfersExited():
|
|
self.setCourseAbort()
|
|
elif self.isCurHoleDone():
|
|
if self.isPlayingLastHole():
|
|
if self.state not in ['WaitReward', 'WaitReadyHole']:
|
|
self.safeDemand('WaitReward')
|
|
else:
|
|
self.notify.debug('allBalls are in holes, calling holeOver')
|
|
self.holeOver()
|
|
if hasattr(self, 'rewardBarrier'):
|
|
if self.rewardBarrier:
|
|
self.rewardBarrier.clear(avId)
|
|
if hasattr(self, '__barrier'):
|
|
if self.__barrier:
|
|
self.__.clear(avId)
|
|
|
|
def startNextHole(self):
|
|
self.notify.debugStateCall(self)
|
|
holeId = self.holeIds[self.numHolesPlayed]
|
|
self.currentHole = DistributedGolfHoleAI.DistributedGolfHoleAI(self.zoneId, golfCourse=self, holeId=holeId)
|
|
self.currentHole.generateWithRequired(self.zoneId)
|
|
self.d_setCurHoleDoId(self.currentHole.doId)
|
|
self.safeDemand('WaitReadyHole')
|
|
|
|
def holeOver(self):
|
|
self.notify.debug('GOLF COURSE: holeOver')
|
|
self.numHolesPlayed += 1
|
|
if self.numHolesPlayed < self.numHoles:
|
|
self.b_setCurHoleIndex(self.numHolesPlayed)
|
|
self.safeDemand('WaitLeaveHole')
|
|
|
|
def setCourseAbort(self):
|
|
self.notify.debug('GOLF COURSE: setGameAbort')
|
|
self.normalExit = 0
|
|
self.sendUpdate('setCourseAbort', [0])
|
|
self.safeDemand('Cleanup')
|
|
|
|
def enterOff(self):
|
|
self.notify.debug('GOLF COURSE: enterOff')
|
|
|
|
def exitOff(self):
|
|
self.notify.debug('GOLF COURSE: exitOff')
|
|
|
|
def enterWaitJoin(self):
|
|
self.notify.debug('GOLF COURSE: enterWaitJoin')
|
|
for avId in self.avIdList:
|
|
self.avStateDict[avId] = EXPECTED
|
|
self.acceptOnce(self.air.getAvatarExitEvent(avId), self.handleExitedAvatar, extraArgs=[avId])
|
|
|
|
def allAvatarsJoined(self = self):
|
|
self.notify.debug('GOLF COURSE: all avatars joined')
|
|
self.load()
|
|
|
|
def handleTimeout(avIds, self = self):
|
|
self.notify.debug('GOLF COURSE: timed out waiting for clients %s to join' % avIds)
|
|
for avId in self.avStateDict:
|
|
if not self.avStateDict[avId] == JOINED:
|
|
self.handleExitedAvatar(avId)
|
|
|
|
if self.haveAllGolfersExited():
|
|
self.setCourseAbort()
|
|
else:
|
|
self.load()
|
|
|
|
self.__barrier = ToonBarrier('waitClientsJoin', self.uniqueName('waitClientsJoin'), self.avIdList, JOIN_TIMEOUT, allAvatarsJoined, handleTimeout)
|
|
|
|
def exitWaitJoin(self):
|
|
self.notify.debugStateCall(self)
|
|
self.__barrier.cleanup()
|
|
self.__barrier = None
|
|
return
|
|
|
|
def setAvatarJoined(self):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
self.notify.debug('GOLF COURSE: setAvatarJoined: avatar id joined: ' + str(avId))
|
|
self.avStateDict[avId] = JOINED
|
|
self.notify.debug('GOLF COURSE: setAvatarJoined: new states: ' + str(self.avStateDict))
|
|
if hasattr(self, '_DistributedGolfCourseAI__barrier') and self.__barrier:
|
|
self.__barrier.clear(avId)
|
|
else:
|
|
self.notify.warning('setAvatarJoined avId=%d but barrier is invalid' % avId)
|
|
|
|
def exitFrameworkWaitClientsJoin(self):
|
|
self.__barrier.cleanup()
|
|
del self.__barrier
|
|
|
|
def enterWaitReadyCourse(self):
|
|
self.notify.debug('GOLF COURSE: enterWaitReadyCourse')
|
|
|
|
def allAvatarsInCourse(self = self):
|
|
self.notify.debug('GOLF COURSE: all avatars ready course')
|
|
for avId in self.avIdList:
|
|
blankScoreList = [0] * self.numHoles
|
|
self.scores[avId] = blankScoreList
|
|
self.aimTimes[avId] = 0
|
|
|
|
self.notify.debug('self.scores = %s' % self.scores)
|
|
self.startNextHole()
|
|
|
|
def handleTimeout(avIds, self = self):
|
|
self.notify.debug("GOLF COURSE: Course timed out waiting for clients %s to report 'ready'" % avIds)
|
|
if self.haveAllGolfersExited():
|
|
self.setCourseAbort()
|
|
else:
|
|
allAvatarsInCourse()
|
|
|
|
self.__barrier = ToonBarrier('WaitReadyCourse', self.uniqueName('WaitReadyCourse'), self.avIdList, READY_TIMEOUT, allAvatarsInCourse, handleTimeout)
|
|
for avId in self.avStateDict.keys():
|
|
if self.avStateDict[avId] == READY:
|
|
self.__barrier.clear(avId)
|
|
|
|
def setAvatarReadyCourse(self):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
self.notify.debug('GOLF COURSE: setAvatarReadyCourse: avatar id ready: ' + str(avId))
|
|
self.avStateDict[avId] = READY
|
|
self.notify.debug('GOLF COURSE: setAvatarReadyCourse: new avId states: ' + str(self.avStateDict))
|
|
if self.state == 'WaitReadyCourse':
|
|
self.__barrier.clear(avId)
|
|
|
|
def exitWaitReadyCourse(self):
|
|
self.notify.debugStateCall(self)
|
|
self.__barrier.cleanup()
|
|
self.__barrier = None
|
|
return
|
|
|
|
def enterWaitReadyHole(self):
|
|
self.notify.debug('GOLF COURSE: enterWaitReadyHole')
|
|
|
|
def allAvatarsInHole(self = self):
|
|
self.notify.debug('GOLF COURSE: all avatars ready hole')
|
|
if self.safeDemand('PlayHole'):
|
|
self.d_setPlayHole()
|
|
|
|
def handleTimeout(avIds, self = self):
|
|
self.notify.debug("GOLF COURSE: Hole timed out waiting for clients %s to report 'ready'" % avIds)
|
|
if self.haveAllGolfersExited():
|
|
self.setCourseAbort()
|
|
elif self.safeDemand('PlayHole'):
|
|
self.d_setPlayHole()
|
|
|
|
stillPlaying = self.getStillPlayingAvIds()
|
|
self.__barrier = ToonBarrier('WaitReadyHole', self.uniqueName('WaitReadyHole'), stillPlaying, READY_TIMEOUT, allAvatarsInHole, handleTimeout)
|
|
for avId in self.avStateDict.keys():
|
|
if self.avStateDict[avId] == ONHOLE:
|
|
self.__barrier.clear(avId)
|
|
|
|
def exitWaitReadyHole(self):
|
|
self.notify.debugStateCall(self)
|
|
if hasattr(self, '__barrier'):
|
|
self.__barrier.cleanup()
|
|
self.__barrier = None
|
|
return
|
|
|
|
def getStillPlayingAvIds(self):
|
|
retval = []
|
|
for avId in self.avIdList:
|
|
av = simbase.air.doId2do.get(avId)
|
|
if av:
|
|
if avId in self.avStateDict and not self.avStateDict[avId] == EXITED:
|
|
retval.append(avId)
|
|
|
|
return retval
|
|
|
|
def avatarReadyHole(self, avId):
|
|
if self.state not in ['WaitJoin', 'WaitReadyCourse', 'WaitReadyHole']:
|
|
self.notify.debug('GOLF COURSE: Ignoring setAvatarReadyHole message')
|
|
return
|
|
self.notify.debug('GOLF COURSE: setAvatarReadyHole: avatar id ready: ' + str(avId))
|
|
self.avStateDict[avId] = ONHOLE
|
|
self.notify.debug('GOLF COURSE: setAvatarReadyHole: new avId states: ' + str(self.avStateDict))
|
|
if self.state == 'WaitReadyHole':
|
|
self.__barrier.clear(avId)
|
|
|
|
def enterPlayHole(self):
|
|
self.notify.debug('GOLF COURSE: enterPlayHole')
|
|
if self.currentHole and not self.currentHole.playStarted:
|
|
self.currentHole.startPlay()
|
|
|
|
def exitPlayHole(self):
|
|
self.notify.debug('GOLF COURSE: exitPlayHole')
|
|
|
|
def enterWaitLeaveHole(self):
|
|
self.notify.debugStateCall(self)
|
|
self.notify.debug('calling requestDelete on hole %d' % self.currentHole.doId)
|
|
self.currentHole.requestDelete()
|
|
self.currentHole = None
|
|
if self.numHolesPlayed >= self.numHoles:
|
|
pass
|
|
else:
|
|
self.startNextHole()
|
|
return
|
|
|
|
def exitWaitLeaveHole(self):
|
|
pass
|
|
|
|
def enterWaitReward(self):
|
|
self.updateHistoryForCourseComplete()
|
|
self.awardTrophies()
|
|
self.awardCups()
|
|
self.awardHoleBest()
|
|
self.awardCourseBest()
|
|
self.recordHoleInOne()
|
|
self.recordCourseUnderPar()
|
|
trophiesList = []
|
|
for index in xrange(len(self.avIdList)):
|
|
avId = self.avIdList[index]
|
|
if avId in self.newTrophies:
|
|
oneTrophyList = self.newTrophies[avId]
|
|
trophiesList.append(oneTrophyList)
|
|
else:
|
|
trophiesList.append([])
|
|
|
|
while len(trophiesList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
|
|
trophiesList.append([])
|
|
|
|
holeBestList = []
|
|
for index in xrange(len(self.avIdList)):
|
|
avId = self.avIdList[index]
|
|
if avId in self.newHoleBest:
|
|
oneTrophyList = self.newHoleBest[avId]
|
|
holeBestList.append(oneTrophyList)
|
|
else:
|
|
holeBestList.append([])
|
|
|
|
while len(holeBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
|
|
holeBestList.append([])
|
|
|
|
courseBestList = []
|
|
for index in xrange(len(self.avIdList)):
|
|
avId = self.avIdList[index]
|
|
if avId in self.newCourseBest:
|
|
oneTrophyList = self.newCourseBest[avId]
|
|
courseBestList.append(oneTrophyList)
|
|
else:
|
|
courseBestList.append([])
|
|
|
|
while len(courseBestList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
|
|
courseBestList.append([])
|
|
|
|
cupList = []
|
|
for index in xrange(len(self.avIdList)):
|
|
avId = self.avIdList[index]
|
|
if avId in self.newCups:
|
|
oneCupList = self.newCups[avId]
|
|
cupList.append(oneCupList)
|
|
self.cupListLen = self.cupListLen + 1
|
|
else:
|
|
cupList.append([])
|
|
|
|
while len(cupList) < GolfGlobals.MAX_PLAYERS_PER_HOLE:
|
|
cupList.append([])
|
|
|
|
REWARD_TIMEOUT = (self.trophyListLen + self.holeBestListLen + self.courseBestListLen + self.cupListLen) * 5 + 19
|
|
aimTimesList = [0] * 4
|
|
aimIndex = 0
|
|
stillPlaying = self.getStillPlayingAvIds()
|
|
for avId in self.avIdList:
|
|
if avId in stillPlaying:
|
|
aimTime = 0
|
|
if avId in self.aimTimes:
|
|
aimTime = self.aimTimes[avId]
|
|
aimTimesList[aimIndex] = aimTime
|
|
aimIndex += 1
|
|
|
|
self.sendUpdate('setReward', [trophiesList,
|
|
self.rankings,
|
|
holeBestList,
|
|
courseBestList,
|
|
cupList,
|
|
self.winnerByTieBreak,
|
|
aimTimesList[0],
|
|
aimTimesList[1],
|
|
aimTimesList[2],
|
|
aimTimesList[3]])
|
|
|
|
def allAvatarsRewarded(self = self):
|
|
self.notify.debug('GOLF COURSE: all avatars rewarded')
|
|
self.rewardDone()
|
|
|
|
def handleRewardTimeout(avIds, self = self):
|
|
self.notify.debug('GOLF COURSE: timed out waiting for clients %s to finish reward' % avIds)
|
|
self.rewardDone()
|
|
|
|
stillPlaying = self.getStillPlayingAvIds()
|
|
self.rewardBarrier = ToonBarrier('waitReward', self.uniqueName('waitReward'), stillPlaying, REWARD_TIMEOUT, allAvatarsRewarded, handleRewardTimeout)
|
|
|
|
def exitWaitReward(self):
|
|
pass
|
|
|
|
def enterWaitLeaveCourse(self):
|
|
self.notify.debugStateCall(self)
|
|
self.setCourseAbort()
|
|
|
|
def exitWaitLeaveCourse(self):
|
|
pass
|
|
|
|
def enterCleanup(self):
|
|
self.notify.debug('GOLF COURSE: enterCleanup')
|
|
self.requestDelete()
|
|
|
|
def exitCleanup(self):
|
|
self.notify.debug('GOLF COURSE: exitCleanup')
|
|
|
|
def isCurHoleDone(self):
|
|
retval = False
|
|
if self.areAllBallsInHole():
|
|
retval = True
|
|
else:
|
|
retval = True
|
|
for state in self.avStateDict.values():
|
|
if not (state == BALLIN or state == EXITED):
|
|
retval = False
|
|
break
|
|
|
|
return retval
|
|
|
|
def areAllBallsInHole(self):
|
|
self.notify.debug('areAllBallsInHole, self.avStateDict=%s' % self.avStateDict)
|
|
allBallsInHole = True
|
|
for state in self.avStateDict.values():
|
|
if state != BALLIN:
|
|
allBallsInHole = False
|
|
|
|
return allBallsInHole
|
|
|
|
def isPlayingLastHole(self):
|
|
retval = self.numHoles - self.numHolesPlayed == 1
|
|
return retval
|
|
|
|
def setBallIn(self, avId):
|
|
self.notify.debug('setBallIn %d' % avId)
|
|
if self.avStateDict[avId] == BALLIN:
|
|
self.notify.debug('setBallIn already in BALLIN state, just returning')
|
|
return
|
|
self.avStateDict[avId] = BALLIN
|
|
self.updateHistoryForBallIn(avId)
|
|
if self.isCurHoleDone():
|
|
if self.isPlayingLastHole():
|
|
if self.state != 'WaitReward':
|
|
self.safeDemand('WaitReward')
|
|
else:
|
|
self.notify.debug('allBalls are in holes, calling holeOver')
|
|
self.holeOver()
|
|
|
|
def updateHistoryForBallIn(self, avId):
|
|
if self.currentHole == None:
|
|
return
|
|
holeId = self.currentHole.holeId
|
|
holeInfo = GolfGlobals.HoleInfo[holeId]
|
|
par = holeInfo['par']
|
|
holeIndex = self.numHolesPlayed
|
|
if holeIndex >= self.numHoles:
|
|
self.notify.warning('updateHistoryForBallIn invalid holeIndex %d' % holeIndex)
|
|
holeIndex = self.numHoles - 1
|
|
elif holeIndex < 0:
|
|
self.notify.warning('updateHistoryForBallIn invalid holeIndex %d' % holeIndex)
|
|
holeIndex = 0
|
|
strokes = self.scores[avId][holeIndex]
|
|
self.notify.debug('self.scores = %s' % self.scores)
|
|
diff = strokes - par
|
|
if strokes == 1:
|
|
self.incrementEndingHistory(avId, GolfGlobals.HoleInOneShots)
|
|
if diff <= -2:
|
|
self.incrementEndingHistory(avId, GolfGlobals.EagleOrBetterShots)
|
|
if diff <= -1:
|
|
self.incrementEndingHistory(avId, GolfGlobals.BirdieOrBetterShots)
|
|
if diff <= 0:
|
|
self.endingHistory[avId][GolfGlobals.ParOrBetterShots] += 1
|
|
if strokes < self.endingHoleBest[avId][holeId] or self.endingHoleBest[avId][holeId] == 0:
|
|
self.endingHoleBest[avId][holeId] = strokes
|
|
return
|
|
|
|
def incrementEndingHistory(self, avId, historyIndex):
|
|
if avId in self.endingHistory and historyIndex in GolfGlobals.TrophyRequirements:
|
|
maximumAmount = GolfGlobals.TrophyRequirements[historyIndex][-1]
|
|
if self.endingHistory[avId][historyIndex] < maximumAmount:
|
|
self.endingHistory[avId][historyIndex] += 1
|
|
|
|
def getCourseId(self):
|
|
return self.courseId
|
|
|
|
def abortCurrentHole(self):
|
|
holeId = self.currentHole.holeId
|
|
holeDoId = self.currentHole.doId
|
|
self.currentHole.finishHole()
|
|
return (holeId, holeDoId)
|
|
|
|
def calcHolesToUse(self):
|
|
retval = []
|
|
if simbase.air.config.GetBool('golf-course-randomized', 1):
|
|
retval = self.calcHolesToUseRandomized(self.courseId)
|
|
self.notify.debug('randomized courses!')
|
|
for x in xrange(len(retval)):
|
|
self.notify.debug('Hole is: %s' % retval[x])
|
|
|
|
else:
|
|
validHoles = self.calcUniqueHoles(self.courseId)
|
|
if self.preferredHoleId in validHoles:
|
|
retval.append(self.preferredHoleId)
|
|
while len(retval) < self.numHoles:
|
|
for holeId in GolfGlobals.CourseInfo[self.courseId]['holeIds']:
|
|
if type(holeId) == type(0):
|
|
retval.append(holeId)
|
|
elif type(holeId) == type(()):
|
|
retval.append(holeId[0])
|
|
else:
|
|
self.notify.warning('cant handle %s' % self.holeId)
|
|
if len(retval) >= self.numHoles:
|
|
break
|
|
|
|
return retval
|
|
|
|
def incrementScore(self, avId):
|
|
self.notify.debug('incrementScore self.scores=%s avId=%s' % (self.scores, avId))
|
|
self.scores[avId][self.numHolesPlayed] += 1
|
|
self.notify.debug('after increment self.score=%s' % self.scores)
|
|
self.sendScores()
|
|
|
|
def sendScores(self):
|
|
self.notify.debug('sendScores self.scores = %s' % self.scores)
|
|
scorelist = []
|
|
for avId in self.avIdList:
|
|
for score in self.scores[avId]:
|
|
scorelist.append(score)
|
|
|
|
self.sendUpdate('setScores', [scorelist])
|
|
self.notify.debug('sendScores end self.scores = %s' % self.scores)
|
|
|
|
def getCurHoleIndex(self):
|
|
return self.curHoleIndex
|
|
|
|
def b_setCurHoleIndex(self, holeIndex):
|
|
self.setCurHoleIndex(holeIndex)
|
|
self.d_setCurHoleIndex(holeIndex)
|
|
|
|
def d_setCurHoleIndex(self, holeIndex):
|
|
self.sendUpdate('setCurHoleIndex', [holeIndex])
|
|
|
|
def setCurHoleIndex(self, holeIndex):
|
|
self.curHoleIndex = holeIndex
|
|
|
|
def setDoneReward(self):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
self.notify.debug('got rewardDone from %d' % avId)
|
|
if hasattr(self, 'rewardBarrier'):
|
|
self.rewardBarrier.clear(avId)
|
|
self.sendUpdate('setCourseAbort', [avId])
|
|
|
|
def rewardDone(self):
|
|
self.notify.debug('rewardDone')
|
|
self.holeOver()
|
|
self.safeDemand('WaitLeaveCourse')
|
|
|
|
def updateHistoryForCourseComplete(self):
|
|
self.calcRankings()
|
|
stillPlaying = self.getStillPlayingAvIds()
|
|
for avId in stillPlaying:
|
|
self.incrementEndingHistory(avId, GolfGlobals.CoursesCompleted)
|
|
coursePar = self.calcCoursePar()
|
|
totalScore = self.getTotalScore(avId)
|
|
if totalScore < coursePar:
|
|
self.incrementEndingHistory(avId, GolfGlobals.CoursesUnderPar)
|
|
if len(stillPlaying) > 1:
|
|
self.incrementEndingHistory(avId, GolfGlobals.MultiPlayerCoursesCompleted)
|
|
if self.rankingsById[avId] == 1:
|
|
if self.courseId == 0:
|
|
self.incrementEndingHistory(avId, GolfGlobals.CourseZeroWins)
|
|
elif self.courseId == 1:
|
|
self.incrementEndingHistory(avId, GolfGlobals.CourseOneWins)
|
|
elif self.courseId == 2:
|
|
self.incrementEndingHistory(avId, GolfGlobals.CourseTwoWins)
|
|
else:
|
|
self.notify.warning('unhandled case, self.courseId=%s' % self.courseId)
|
|
if totalScore < self.endingCourseBest[avId][self.courseId] or self.endingCourseBest[avId][self.courseId] == 0:
|
|
self.endingCourseBest[avId][self.courseId] = totalScore
|
|
|
|
def calcRankings(self):
|
|
stillPlaying = self.getStillPlayingAvIds()
|
|
self.rankings = []
|
|
totalScores = []
|
|
for avId in self.avIdList:
|
|
aimTime = 0
|
|
if avId in self.aimTimes:
|
|
aimTime = self.aimTimes[avId]
|
|
if avId in stillPlaying:
|
|
totalScores.append((avId, self.getTotalScore(avId), aimTime))
|
|
else:
|
|
totalScores.append((avId, 255, aimTime))
|
|
|
|
def scoreCompareNoTime(tupleA, tupleB):
|
|
if tupleA[1] > tupleB[1]:
|
|
return 1
|
|
elif tupleA[1] == tupleB[1]:
|
|
return 0
|
|
else:
|
|
return -1
|
|
|
|
def scoreCompareWithTime(tupleA, tupleB):
|
|
if tupleA[1] > tupleB[1]:
|
|
return 1
|
|
elif tupleA[1] == tupleB[1]:
|
|
if tupleA[2] > tupleB[2]:
|
|
return 1
|
|
elif tupleA[2] == tupleB[2]:
|
|
return 0
|
|
else:
|
|
return -1
|
|
else:
|
|
return -1
|
|
|
|
if GolfGlobals.TIME_TIE_BREAKER:
|
|
totalScores.sort(scoreCompareWithTime)
|
|
else:
|
|
totalScores.sort(scoreCompareNoTime)
|
|
curRank = 0
|
|
oldScore = 0
|
|
oldTime = 0
|
|
self.rankingsById = {}
|
|
for scoreTuple in totalScores:
|
|
time = scoreTuple[2]
|
|
score = scoreTuple[1]
|
|
avId = scoreTuple[0]
|
|
if score > oldScore or GolfGlobals.TIME_TIE_BREAKER and score == oldScore and time > oldTime:
|
|
curRank += 1
|
|
oldScore = score
|
|
oldTime = time
|
|
self.rankingsById[avId] = curRank
|
|
|
|
tiedForFirst = []
|
|
tempRank = 0
|
|
oldScore = 0
|
|
oldTime = 0
|
|
for scoreTuple in totalScores:
|
|
time = scoreTuple[2]
|
|
score = scoreTuple[1]
|
|
avId = scoreTuple[0]
|
|
if score > oldScore:
|
|
tempRank += 1
|
|
oldScore = score
|
|
oldTime = time
|
|
if tempRank == 1:
|
|
tiedForFirst.append(avId)
|
|
|
|
for avId in self.avIdList:
|
|
if avId in stillPlaying:
|
|
self.rankings.append(self.rankingsById[avId])
|
|
else:
|
|
self.rankings.append(-1)
|
|
|
|
if len(tiedForFirst) >= 2 and not GolfGlobals.TIME_TIE_BREAKER:
|
|
winnerAvId = random.choice(tiedForFirst)
|
|
winnerIndex = self.avIdList.index(winnerAvId)
|
|
self.winnerByTieBreak = winnerAvId
|
|
for index in xrange(len(self.rankings)):
|
|
if self.rankings[index] > 0 and index != winnerIndex:
|
|
self.rankings[index] += 1
|
|
|
|
for avId in self.rankingsById:
|
|
if self.rankingsById[avId] > 0 and avId != winnerAvId:
|
|
self.rankingsById[avId] += 1
|
|
|
|
elif len(tiedForFirst) >= 2:
|
|
winnerAvId = totalScores[0][0]
|
|
self.winnerByTieBreak = winnerAvId
|
|
|
|
def awardTrophies(self):
|
|
stillPlaying = self.getStillPlayingAvIds()
|
|
for avId in stillPlaying:
|
|
av = simbase.air.doId2do.get(avId)
|
|
if av:
|
|
oldHistory = self.startingHistory[avId]
|
|
endingHistory = self.endingHistory[avId]
|
|
oldTrophies = GolfGlobals.calcTrophyListFromHistory(oldHistory)
|
|
endingTrophies = GolfGlobals.calcTrophyListFromHistory(endingHistory)
|
|
av.b_setGolfHistory(endingHistory)
|
|
av.addStat(ToontownGlobals.STAT_GOLF)
|
|
newTrophies = []
|
|
for index in xrange(len(oldTrophies)):
|
|
if not oldTrophies[index] and endingTrophies[index]:
|
|
self.notify.debug('New Trophy %d' % index)
|
|
self.air.writeServerEvent('golf_trophy', avId, '%s' % index)
|
|
newTrophies.append(True)
|
|
self.trophyListLen = self.trophyListLen + 1
|
|
else:
|
|
newTrophies.append(False)
|
|
|
|
self.newTrophies[avId] = newTrophies
|
|
|
|
def awardCups(self):
|
|
stillPlaying = self.getStillPlayingAvIds()
|
|
for avId in stillPlaying:
|
|
av = simbase.air.doId2do.get(avId)
|
|
if av:
|
|
oldHistory = self.startingHistory[avId]
|
|
endingHistory = self.endingHistory[avId]
|
|
oldCups = GolfGlobals.calcCupListFromHistory(oldHistory)
|
|
endingCups = GolfGlobals.calcCupListFromHistory(endingHistory)
|
|
newCups = []
|
|
for index in xrange(len(oldCups)):
|
|
if not oldCups[index] and endingCups[index]:
|
|
self.notify.debug('New Trophy %d' % index)
|
|
newCups.append(True)
|
|
self.air.writeServerEvent('golf_cup', avId, '%s' % index)
|
|
newMaxHp = av.getMaxHp() + 1
|
|
av.b_setMaxHp(newMaxHp)
|
|
av.toonUp(newMaxHp)
|
|
else:
|
|
newCups.append(False)
|
|
|
|
self.newCups[avId] = newCups
|
|
|
|
def awardHoleBest(self):
|
|
stillPlaying = self.getStillPlayingAvIds()
|
|
for avId in stillPlaying:
|
|
av = simbase.air.doId2do.get(avId)
|
|
if av:
|
|
oldHoleBest = self.startingHoleBest[avId]
|
|
endingHoleBest = self.endingHoleBest[avId]
|
|
av.b_setGolfHoleBest(endingHoleBest)
|
|
newHoleBest = []
|
|
longestHoleBestList = 0
|
|
for index in xrange(len(oldHoleBest)):
|
|
if endingHoleBest[index] < oldHoleBest[index]:
|
|
self.notify.debug('New HoleBest %d' % index)
|
|
newHoleBest.append(True)
|
|
longestHoleBestList = longestHoleBestList + 1
|
|
else:
|
|
newHoleBest.append(False)
|
|
|
|
if longestHoleBestList > self.holeBestListLen:
|
|
self.holeBestListLen = longestHoleBestList
|
|
self.newHoleBest[avId] = newHoleBest
|
|
|
|
def awardCourseBest(self):
|
|
stillPlaying = self.getStillPlayingAvIds()
|
|
for avId in stillPlaying:
|
|
av = simbase.air.doId2do.get(avId)
|
|
if av:
|
|
oldCourseBest = self.startingCourseBest[avId]
|
|
endingCourseBest = self.endingCourseBest[avId]
|
|
av.b_setGolfCourseBest(endingCourseBest)
|
|
newCourseBest = []
|
|
longestCourseBestList = 0
|
|
for index in xrange(len(oldCourseBest)):
|
|
if endingCourseBest[index] < oldCourseBest[index]:
|
|
self.notify.debug('New CourseBest %d' % index)
|
|
newCourseBest.append(True)
|
|
longestCourseBestList = longestCourseBestList + 1
|
|
else:
|
|
newCourseBest.append(False)
|
|
|
|
if longestCourseBestList > self.courseBestListLen:
|
|
self.courseBestListLen = longestCourseBestList
|
|
self.newCourseBest[avId] = newCourseBest
|
|
|
|
def haveAllGolfersExited(self):
|
|
retval = True
|
|
for avId in self.avStateDict:
|
|
if not self.avStateDict[avId] == EXITED:
|
|
retval = False
|
|
break
|
|
|
|
return retval
|
|
|
|
def getCurHoleDoId(self):
|
|
retval = 0
|
|
if self.currentHole:
|
|
retval = self.currentHole.doId
|
|
return retval
|
|
|
|
def d_setCurHoleDoId(self, curHoleDoId):
|
|
self.sendUpdate('setCurHoleDoId', [curHoleDoId])
|
|
|
|
def calcCoursePar(self):
|
|
retval = 0
|
|
for holeId in self.holeIds:
|
|
holeInfo = GolfGlobals.HoleInfo[holeId]
|
|
retval += holeInfo['par']
|
|
|
|
return retval
|
|
|
|
def getTotalScore(self, avId):
|
|
retval = 0
|
|
if avId in self.scores:
|
|
for holeScore in self.scores[avId]:
|
|
retval += holeScore
|
|
|
|
return retval
|
|
|
|
def getCurHoleScore(self, avId):
|
|
retval = 0
|
|
if avId in self.scores and self.numHolesPlayed < len(self.scores[avId]):
|
|
retval = self.scores[avId][self.numHolesPlayed]
|
|
return retval
|
|
|
|
def toggleDrivePermission(self, avId):
|
|
if avId in self.drivingToons:
|
|
self.drivingToons.remove(avId)
|
|
self.sendUpdate('changeDrivePermission', [avId, 0])
|
|
retval = False
|
|
else:
|
|
self.drivingToons.append(avId)
|
|
self.sendUpdate('changeDrivePermission', [avId, 1])
|
|
retval = True
|
|
return retval
|
|
|
|
def safeDemand(self, newState):
|
|
doingDemand = False
|
|
if self.state == 'Cleanup':
|
|
pass
|
|
else:
|
|
if self.state in self.defaultTransitions:
|
|
if newState in self.defaultTransitions[self.state]:
|
|
self.demand(newState)
|
|
doingDemand = True
|
|
elif self.state == None:
|
|
self.demand(newState)
|
|
doingDemand = True
|
|
if not doingDemand:
|
|
self.notify.warning('doId=%d ignoring demand from %s to %s' % (self.doId, self.state, newState))
|
|
return doingDemand
|
|
|
|
def setAvatarExited(self):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
self.handleExitedAvatar(avId)
|
|
|
|
def createChoicesList(self, courseId, possibleHoles):
|
|
retval = []
|
|
holeIds = GolfGlobals.CourseInfo[courseId]['holeIds']
|
|
for holeOrTuple in holeIds:
|
|
if type(holeOrTuple) == type(()):
|
|
holeId = holeOrTuple[0]
|
|
weight = holeOrTuple[1]
|
|
elif type(holeOrTuple) == type(0):
|
|
holeId = holeOrTuple
|
|
weight = 1
|
|
else:
|
|
self.notify.warning('cant handle %s' % holeOrTuple)
|
|
continue
|
|
if holeId in possibleHoles:
|
|
retval += [holeId] * weight
|
|
|
|
return retval
|
|
|
|
def calcUniqueHoles(self, courseId):
|
|
uniqueHoles = set()
|
|
for holeOrTuple in GolfGlobals.CourseInfo[courseId]['holeIds']:
|
|
if type(holeOrTuple) == type(()):
|
|
uniqueHoles.add(holeOrTuple[0])
|
|
elif type(holeOrTuple) == type(0):
|
|
uniqueHoles.add(holeOrTuple)
|
|
else:
|
|
self.notify.warning('cant handle %s' % holeOrTuple)
|
|
|
|
return uniqueHoles
|
|
|
|
def calcHolesToUseRandomized(self, courseId):
|
|
retval = []
|
|
numHoles = GolfGlobals.CourseInfo[courseId]['numHoles']
|
|
uniqueHoles = self.calcUniqueHoles(courseId)
|
|
curHolesChosen = set()
|
|
while len(retval) < numHoles:
|
|
if uniqueHoles == curHolesChosen:
|
|
curHolesChosen = set()
|
|
possibleHoles = uniqueHoles - curHolesChosen
|
|
choicesList = self.createChoicesList(courseId, possibleHoles)
|
|
if not self.preferredHoleId == None and self.preferredHoleId in choicesList and self.preferredHoleId not in curHolesChosen:
|
|
holeChosen = self.preferredHoleId
|
|
else:
|
|
holeChosen = random.choice(choicesList)
|
|
retval.append(holeChosen)
|
|
curHolesChosen.add(holeChosen)
|
|
|
|
return retval
|
|
|
|
def recordHoleInOne(self):
|
|
stillPlaying = self.getStillPlayingAvIds()
|
|
for avId in stillPlaying:
|
|
scoreList = self.scores[avId]
|
|
ns = 0
|
|
for holeIndex in xrange(len(scoreList)):
|
|
strokes = scoreList[holeIndex]
|
|
if strokes == 1:
|
|
ns +=1
|
|
holeId = self.holeIds[holeIndex]
|
|
self.air.writeServerEvent('golf_ace', avId, '%d|%d|%s' % (self.courseId, holeId, stillPlaying))
|
|
|
|
if ns:
|
|
messenger.send('topToonsManager-event', [avId, TopToonsGlobals.CAT_HOLE_IN_ONE, ns])
|
|
|
|
def recordCourseUnderPar(self):
|
|
coursePar = self.calcCoursePar()
|
|
stillPlaying = self.getStillPlayingAvIds()
|
|
for avId in stillPlaying:
|
|
totalScore = self.getTotalScore(avId)
|
|
netScore = totalScore - coursePar
|
|
if netScore < 0:
|
|
self.air.writeServerEvent('golf_underPar', avId, '%d|%d|%s' % (self.courseId, netScore, stillPlaying))
|
|
messenger.send('topToonsManager-event', [avId, TopToonsGlobals.CAT_COURSE_UNDER_PAR, 1])
|
|
|
|
def addAimTime(self, avId, aimTime):
|
|
if avId in self.aimTimes:
|
|
self.aimTimes[avId] += aimTime
|