toontown-just-works/toontown/minigame/DistributedTwoDGameAI.py
2024-07-07 18:08:39 -05:00

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