418 lines
18 KiB
Python
418 lines
18 KiB
Python
from DistributedMinigameAI import *
|
|
from toontown.ai.ToonBarrier import *
|
|
from direct.fsm import ClassicFSM, State
|
|
from direct.directnotify import DirectNotifyGlobal
|
|
from toontown.minigame import ToonBlitzGlobals
|
|
from math import sqrt
|
|
|
|
class DistributedTwoDGameAI(DistributedMinigameAI):
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTwoDGameAI')
|
|
|
|
def __init__(self, air, minigameId):
|
|
try:
|
|
self.DistributedTwoDGameAI_initialized
|
|
except:
|
|
self.DistributedTwoDGame_initialized = 1
|
|
DistributedMinigameAI.__init__(self, air, minigameId)
|
|
self.gameFSM = ClassicFSM.ClassicFSM('DistributedTwoDGameAI', [State.State('inactive', self.enterInactive, self.exitInactive, ['play']), State.State('play', self.enterPlay, self.exitPlay, ['cleanup']), State.State('cleanup', self.enterCleanup, self.exitCleanup, ['inactive'])], 'inactive', 'inactive')
|
|
self.addChildGameFSM(self.gameFSM)
|
|
self.finishedBonusDict = {}
|
|
self.finishedTimeLeftDict = {}
|
|
self.numFallDownDict = {}
|
|
self.numHitByEnemyDict = {}
|
|
self.numSquishDict = {}
|
|
self.treasuresCollectedDict = {}
|
|
self.sectionsSelected = []
|
|
self.enemyHealthTable = []
|
|
self.treasureTakenTable = []
|
|
self.sectionIndexList = []
|
|
|
|
def generate(self):
|
|
self.notify.debug('generate')
|
|
DistributedMinigameAI.generate(self)
|
|
|
|
def delete(self):
|
|
self.notify.debug('delete')
|
|
del self.gameFSM
|
|
DistributedMinigameAI.delete(self)
|
|
|
|
def setTrolleyZone(self, trolleyZone):
|
|
DistributedMinigameAI.setTrolleyZone(self, trolleyZone)
|
|
self.setupSections()
|
|
|
|
def setGameReady(self):
|
|
self.notify.debug('setGameReady')
|
|
DistributedMinigameAI.setGameReady(self)
|
|
self.numTreasures = ToonBlitzGlobals.NumTreasures
|
|
self.numEnemies = ToonBlitzGlobals.NumEnemies
|
|
self.numTreasuresTaken = 0
|
|
self.numEnemiesKilled = 0
|
|
for avId in self.scoreDict.keys():
|
|
self.scoreDict[avId] = 0
|
|
self.finishedBonusDict[avId] = 0
|
|
self.finishedTimeLeftDict[avId] = -1
|
|
self.numFallDownDict[avId] = 0
|
|
self.numHitByEnemyDict[avId] = 0
|
|
self.numSquishDict[avId] = 0
|
|
self.treasuresCollectedDict[avId] = [0,
|
|
0,
|
|
0,
|
|
0]
|
|
|
|
for i in xrange(len(self.sectionsSelected)):
|
|
sectionIndex = self.sectionsSelected[i][0]
|
|
attribs = ToonBlitzGlobals.SectionTypes[sectionIndex]
|
|
enemiesPool = attribs[3]
|
|
self.enemyHealthTable += [[]]
|
|
enemyIndicesSelected = self.sectionsSelected[i][1]
|
|
for j in xrange(len(enemyIndicesSelected)):
|
|
enemyIndex = enemyIndicesSelected[j]
|
|
enemyType = enemiesPool[enemyIndex][0]
|
|
self.enemyHealthTable[i] += [ToonBlitzGlobals.EnemyBaseHealth]
|
|
self.enemyHealthTable[i][j] *= self.numPlayers
|
|
if enemyType in ToonBlitzGlobals.EnemyHealthMultiplier:
|
|
self.enemyHealthTable[i][j] *= ToonBlitzGlobals.EnemyHealthMultiplier[enemyType]
|
|
|
|
self.treasureTakenTable += [[]]
|
|
treasureIndicesSelected = self.sectionsSelected[i][2]
|
|
for j in xrange(len(treasureIndicesSelected)):
|
|
self.treasureTakenTable[i] += [0]
|
|
|
|
enemyIndicesSelected = self.sectionsSelected[i][1]
|
|
for j in xrange(len(enemyIndicesSelected)):
|
|
self.treasureTakenTable[i] += [0]
|
|
|
|
def setGameStart(self, timestamp):
|
|
self.notify.debug('setGameStart')
|
|
DistributedMinigameAI.setGameStart(self, timestamp)
|
|
self.gameFSM.request('play')
|
|
|
|
def setGameAbort(self):
|
|
self.notify.debug('setGameAbort')
|
|
if self.gameFSM.getCurrentState():
|
|
self.gameFSM.request('cleanup')
|
|
DistributedMinigameAI.setGameAbort(self)
|
|
|
|
def gameOver(self):
|
|
self.notify.debug('gameOver')
|
|
scoreList = []
|
|
finishedBonusList = []
|
|
timeLeftList = []
|
|
treasureCollectedList = []
|
|
playerErrorList = []
|
|
for avId in self.avIdList:
|
|
scoreList.append(self.scoreDict[avId])
|
|
finishedBonusList.append(self.finishedBonusDict[avId])
|
|
timeLeftList.append(self.finishedTimeLeftDict[avId])
|
|
treasureCollectedList.append(self.treasuresCollectedDict[avId])
|
|
playerError = [self.numFallDownDict[avId], self.numHitByEnemyDict[avId], self.numSquishDict[avId]]
|
|
playerErrorList.append(playerError)
|
|
self.scoreDict[avId] = max(0, self.scoreDict[avId])
|
|
jellybeans = sqrt(self.scoreDict[avId] * ToonBlitzGlobals.ScoreToJellyBeansMultiplier)
|
|
self.scoreDict[avId] = max(1, int(jellybeans))
|
|
|
|
self.air.writeServerEvent('minigame_twoD', self.doId, '%s|%s|%s|%s|%s|%s|%s|%s|%s' % (ToontownGlobals.TwoDGameId,
|
|
self.getSafezoneId(),
|
|
self.avIdList,
|
|
scoreList,
|
|
finishedBonusList,
|
|
timeLeftList,
|
|
treasureCollectedList,
|
|
playerErrorList,
|
|
self.sectionIndexList))
|
|
self.notify.debug('minigame_twoD%s: %s|%s|%s|%s|%s|%s|%s|%s|%s' % (self.doId,
|
|
ToontownGlobals.TwoDGameId,
|
|
self.getSafezoneId(),
|
|
self.avIdList,
|
|
scoreList,
|
|
finishedBonusList,
|
|
timeLeftList,
|
|
treasureCollectedList,
|
|
playerErrorList,
|
|
self.sectionIndexList))
|
|
self.gameFSM.request('cleanup')
|
|
DistributedMinigameAI.gameOver(self)
|
|
|
|
def enterInactive(self):
|
|
self.notify.debug('enterInactive')
|
|
|
|
def exitInactive(self):
|
|
pass
|
|
|
|
def enterPlay(self):
|
|
self.notify.debug('enterPlay')
|
|
|
|
def allToonsDone(self = self):
|
|
self.notify.debug('allToonsDone')
|
|
self.sendUpdate('setEveryoneDone')
|
|
if not ToonBlitzGlobals.EndlessGame:
|
|
self.gameOver()
|
|
|
|
def handleTimeout(avIds, self = self):
|
|
self.notify.debug('handleTimeout: avatars %s did not report "done"' % avIds)
|
|
self.setGameAbort()
|
|
|
|
self.doneBarrier = ToonBarrier('waitClientsDone', self.uniqueName('waitClientsDone'), self.avIdList, ToonBlitzGlobals.GameDuration[self.getSafezoneId()] + ToonBlitzGlobals.ShowScoresDuration + MinigameGlobals.latencyTolerance, allToonsDone, handleTimeout)
|
|
|
|
def exitPlay(self):
|
|
pass
|
|
|
|
def enterCleanup(self):
|
|
self.notify.debug('enterCleanup')
|
|
self.doneBarrier.cleanup()
|
|
del self.doneBarrier
|
|
self.gameFSM.request('inactive')
|
|
|
|
def exitCleanup(self):
|
|
pass
|
|
|
|
def claimTreasure(self, sectionIndex, treasureIndex):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
self.notify.debug('treasure %s-%s claimed by %s' % (sectionIndex, treasureIndex, avId))
|
|
if sectionIndex < 0 or sectionIndex >= len(self.sectionsSelected):
|
|
self.air.writeServerEvent('warning', sectionIndex, 'TwoDGameAI.claimTreasure sectionIndex out of range.')
|
|
return
|
|
if treasureIndex < 0 or treasureIndex >= len(self.treasureTakenTable[sectionIndex]):
|
|
self.notify.warning('Treasure %s: TwoDGameAI.claimTreasure treasureIndex out of range.' % treasureIndex)
|
|
self.air.writeServerEvent('warning', treasureIndex, 'TwoDGameAI.claimTreasure treasureIndex out of range.')
|
|
return
|
|
if self.treasureTakenTable[sectionIndex][treasureIndex]:
|
|
return
|
|
initialTreasureList = self.sectionsSelected[sectionIndex][2]
|
|
if treasureIndex < len(initialTreasureList):
|
|
treasureValue = initialTreasureList[treasureIndex][1]
|
|
else:
|
|
treasureValue = self.numPlayers
|
|
self.treasureTakenTable[sectionIndex][treasureIndex] = treasureValue
|
|
self.treasuresCollectedDict[avId][treasureValue - 1] += 1
|
|
self.scoreDict[avId] += ToonBlitzGlobals.ScoreGainPerTreasure * treasureValue
|
|
self.numTreasuresTaken += 1
|
|
self.sendUpdate('setTreasureGrabbed', [avId, sectionIndex, treasureIndex])
|
|
|
|
def claimEnemyShot(self, sectionIndex, enemyIndex):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
self.notify.debug('enemy %s-%s shot claimed by %s' % (sectionIndex, enemyIndex, avId))
|
|
if sectionIndex < 0 or sectionIndex >= len(self.sectionsSelected):
|
|
self.air.writeServerEvent('warning', sectionIndex, 'TwoDGameAI.claimEnemyShot sectionIndex out of range.')
|
|
return
|
|
if enemyIndex < 0 or enemyIndex >= len(self.sectionsSelected[sectionIndex][1]):
|
|
self.air.writeServerEvent('warning', enemyIndex, 'TwoDGameAI.claimEnemyShot enemyIndex out of range.')
|
|
return
|
|
if self.enemyHealthTable[sectionIndex][enemyIndex] > 0:
|
|
self.enemyHealthTable[sectionIndex][enemyIndex] -= ToonBlitzGlobals.DamagePerBullet
|
|
if self.enemyHealthTable[sectionIndex][enemyIndex] <= 0:
|
|
self.numEnemiesKilled += 1
|
|
self.sendUpdate('setEnemyShot', [avId,
|
|
sectionIndex,
|
|
enemyIndex,
|
|
self.enemyHealthTable[sectionIndex][enemyIndex]])
|
|
|
|
def reportDone(self):
|
|
if self.gameFSM.getCurrentState() == None or self.gameFSM.getCurrentState().getName() != 'play':
|
|
return
|
|
avId = self.air.getAvatarIdFromSender()
|
|
self.notify.debug('reportDone: avatar %s is done' % avId)
|
|
self.doneBarrier.clear(avId)
|
|
return
|
|
|
|
def toonVictory(self, avId, timestamp):
|
|
if self.gameFSM.getCurrentState() == None or self.gameFSM.getCurrentState().getName() != 'play':
|
|
msg = 'TwoDGameAI.toonVictory not in play state!'
|
|
self.notify.warning('suspicious: ' + str(avId) + ' ' + msg)
|
|
self.air.writeServerEvent('suspicious: ', avId, msg)
|
|
return
|
|
if avId not in self.scoreDict.keys():
|
|
self.notify.warning('Avatar %s not in list.' % avId)
|
|
self.air.writeServerEvent('suspicious: ', avId, 'TwoDGameAI.toonVictory toon not in list.')
|
|
return
|
|
curTime = self.getCurrentGameTime()
|
|
timeLeft = ToonBlitzGlobals.GameDuration[self.getSafezoneId()] - curTime
|
|
self.notify.debug('curTime =%s timeLeft = %s' % (curTime, timeLeft))
|
|
addBonus = int(ToonBlitzGlobals.BaseBonusOnCompletion[self.getSafezoneId()] + ToonBlitzGlobals.BonusPerSecondLeft * timeLeft)
|
|
self.notify.debug('addBOnus = %d' % addBonus)
|
|
if addBonus < 0:
|
|
addBonus = 0
|
|
self.finishedBonusDict[avId] = addBonus
|
|
timeLeftStr = '%.1f' % timeLeft
|
|
self.finishedTimeLeftDict[avId] = timeLeftStr
|
|
self.scoreDict[avId] += addBonus
|
|
self.sendUpdate('addVictoryScore', [avId, addBonus])
|
|
self.doneBarrier.clear(avId)
|
|
return
|
|
|
|
def toonFellDown(self, avId, timestamp):
|
|
if avId not in self.scoreDict.keys():
|
|
self.notify.warning('Avatar %s not in list.' % avId)
|
|
self.air.writeServerEvent('warning', avId, 'TwoDGameAI.toonFellDown toon not in list.')
|
|
return
|
|
self.numFallDownDict[avId] += 1
|
|
self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerFallDown[self.getSafezoneId()]
|
|
|
|
def toonHitByEnemy(self, avId, timestamp):
|
|
if avId not in self.scoreDict.keys():
|
|
self.notify.warning('Avatar %s not in list.' % avId)
|
|
self.air.writeServerEvent('warning', avId, 'TwoDGameAI.toonHitByEnemy toon not in list.')
|
|
return
|
|
self.numHitByEnemyDict[avId] += 1
|
|
self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerEnemyCollision[self.getSafezoneId()]
|
|
|
|
def toonSquished(self, avId, timestamp):
|
|
if avId not in self.scoreDict.keys():
|
|
self.notify.warning('Avatar %s not in list.' % avId)
|
|
self.air.writeServerEvent('warning', avId, 'TwoDGameAI.toonSquished toon not in list.')
|
|
return
|
|
self.numSquishDict[avId] += 1
|
|
self.scoreDict[avId] += ToonBlitzGlobals.ScoreLossPerStomperSquish[self.getSafezoneId()]
|
|
|
|
def setupSections(self):
|
|
szId = self.getSafezoneId()
|
|
sectionWeights = ToonBlitzGlobals.SectionWeights[szId]
|
|
numSections = ToonBlitzGlobals.NumSections[szId]
|
|
difficultyPool = []
|
|
difficultyList = []
|
|
sectionsPool = ToonBlitzGlobals.SectionsPool
|
|
sectionTypes = ToonBlitzGlobals.SectionTypes
|
|
sectionsPoolByDifficulty = [[],
|
|
[],
|
|
[],
|
|
[],
|
|
[],
|
|
[]]
|
|
sectionsSelectedByDifficulty = [[],
|
|
[],
|
|
[],
|
|
[],
|
|
[],
|
|
[]]
|
|
sectionIndicesSelected = []
|
|
for weight in sectionWeights:
|
|
difficulty, probability = weight
|
|
difficultyPool += [difficulty] * probability
|
|
|
|
for i in xrange(numSections):
|
|
difficulty = random.choice(difficultyPool)
|
|
difficultyList.append(difficulty)
|
|
|
|
difficultyList.sort()
|
|
for sectionIndex in sectionsPool:
|
|
difficulty = sectionTypes[sectionIndex][0]
|
|
sectionsPoolByDifficulty[difficulty] += [sectionIndex]
|
|
|
|
for targetDifficulty in difficultyList:
|
|
whileCount = 0
|
|
difficulty = targetDifficulty
|
|
while not len(sectionsPoolByDifficulty[difficulty]) > 0:
|
|
difficulty += 1
|
|
if difficulty >= 5:
|
|
difficulty = 0
|
|
whileCount += 1
|
|
if whileCount > 1:
|
|
break
|
|
else:
|
|
sectionIndexChoice = random.choice(sectionsPoolByDifficulty[difficulty])
|
|
sectionsSelectedByDifficulty[difficulty] += [sectionIndexChoice]
|
|
sectionsPoolByDifficulty[difficulty].remove(sectionIndexChoice)
|
|
|
|
if whileCount > 1:
|
|
self.notify.debug('We need more sections than we have choices. We have to now repeat.')
|
|
|
|
for i in xrange(len(sectionsSelectedByDifficulty)):
|
|
for j in xrange(len(sectionsSelectedByDifficulty[i])):
|
|
sectionIndicesSelected.append(sectionsSelectedByDifficulty[i][j])
|
|
|
|
for i in xrange(len(sectionIndicesSelected)):
|
|
sectionIndex = sectionIndicesSelected[i]
|
|
self.sectionIndexList.append(sectionIndex)
|
|
attribs = sectionTypes[sectionIndex]
|
|
difficulty = attribs[0]
|
|
length = attribs[1]
|
|
blocksPool = attribs[2]
|
|
enemiesPool = attribs[3]
|
|
treasuresPool = attribs[4]
|
|
spawnPointsPool = attribs[5]
|
|
stompersPool = attribs[6]
|
|
enemyIndicesPool = []
|
|
enemyIndicesSelected = []
|
|
if enemiesPool != None:
|
|
minEnemies, maxEnemies = attribs[7]
|
|
for i in xrange(len(enemiesPool)):
|
|
enemyIndicesPool += [i]
|
|
|
|
numEnemies = maxEnemies * ToonBlitzGlobals.PercentMaxEnemies[szId] / 100
|
|
numEnemies = max(numEnemies, minEnemies)
|
|
for j in xrange(int(numEnemies)):
|
|
if len(enemyIndicesPool) == 0:
|
|
break
|
|
enemyIndex = random.choice(enemyIndicesPool)
|
|
enemyIndicesSelected.append(enemyIndex)
|
|
enemyIndicesPool.remove(enemyIndex)
|
|
|
|
enemyIndicesSelected.sort()
|
|
treasureIndicesPool = []
|
|
treasureValuePool = []
|
|
for value in xrange(1, 5):
|
|
treasureValuePool += [value] * ToonBlitzGlobals.TreasureValueProbability[value]
|
|
|
|
treasureIndicesSelected = []
|
|
if treasuresPool != None:
|
|
minTreasures, maxTreasures = attribs[8]
|
|
for i in xrange(len(treasuresPool)):
|
|
treasureIndicesPool += [i]
|
|
|
|
numTreasures = maxTreasures * ToonBlitzGlobals.PercentMaxTreasures[szId] / 100
|
|
numTreasures = max(numTreasures, minTreasures)
|
|
for i in xrange(int(numTreasures)):
|
|
if len(treasureIndicesPool) == 0:
|
|
break
|
|
treasureIndex = random.choice(treasureIndicesPool)
|
|
treasureValue = random.choice(treasureValuePool)
|
|
treasure = (treasureIndex, treasureValue)
|
|
treasureIndicesPool.remove(treasureIndex)
|
|
treasureIndicesSelected.append(treasure)
|
|
|
|
treasureIndicesSelected.sort()
|
|
spawnPointIndicesPool = []
|
|
spawnPointIndicesSelected = []
|
|
if spawnPointsPool != None:
|
|
minSpawnPoints, maxSpawnPoints = attribs[9]
|
|
for i in xrange(len(spawnPointsPool)):
|
|
spawnPointIndicesPool += [i]
|
|
|
|
numSpawnPoints = maxSpawnPoints * ToonBlitzGlobals.PercentMaxSpawnPoints[szId] / 100
|
|
numSpawnPoints = max(numSpawnPoints, minSpawnPoints)
|
|
for i in xrange(int(numSpawnPoints)):
|
|
if len(spawnPointIndicesPool) == 0:
|
|
break
|
|
spawnPoint = random.choice(spawnPointIndicesPool)
|
|
spawnPointIndicesSelected.append(spawnPoint)
|
|
spawnPointIndicesPool.remove(spawnPoint)
|
|
|
|
spawnPointIndicesSelected.sort()
|
|
stomperIndicesPool = []
|
|
stomperIndicesSelected = []
|
|
if stompersPool != None:
|
|
minStompers, maxStompers = attribs[10]
|
|
for i in xrange(len(stompersPool)):
|
|
stomperIndicesPool += [i]
|
|
|
|
numStompers = maxStompers * ToonBlitzGlobals.PercentMaxStompers[szId] / 100
|
|
numStompers = max(numStompers, minStompers)
|
|
for i in xrange(int(numStompers)):
|
|
if len(stomperIndicesPool) == 0:
|
|
break
|
|
stomper = random.choice(stomperIndicesPool)
|
|
stomperIndicesSelected.append(stomper)
|
|
stomperIndicesPool.remove(stomper)
|
|
|
|
stomperIndicesSelected.sort()
|
|
sctionTuple = (sectionIndex,
|
|
enemyIndicesSelected,
|
|
treasureIndicesSelected,
|
|
spawnPointIndicesSelected,
|
|
stomperIndicesSelected)
|
|
self.sectionsSelected.append(sctionTuple)
|
|
|
|
return
|
|
|
|
def getSectionsSelected(self):
|
|
return self.sectionsSelected
|