971 lines
36 KiB
Python
971 lines
36 KiB
Python
from direct.directnotify import DirectNotifyGlobal
|
|
from direct.distributed.ClockDelta import globalClockDelta
|
|
from direct.fsm import FSM
|
|
from direct.interval.IntervalGlobal import LerpPosInterval
|
|
import math
|
|
from pandac.PandaModules import Point3
|
|
import random
|
|
|
|
from otp.ai.MagicWordGlobal import *
|
|
from toontown.battle import BattleExperienceAI
|
|
from toontown.battle import DistributedBattleDinersAI
|
|
from toontown.battle import DistributedBattleWaitersAI
|
|
from toontown.building import SuitBuildingGlobals
|
|
from toontown.coghq import DistributedBanquetTableAI
|
|
from toontown.coghq import DistributedFoodBeltAI
|
|
from toontown.coghq import DistributedGolfSpotAI
|
|
from toontown.suit import DistributedBossCogAI
|
|
from toontown.suit import DistributedSuitAI
|
|
from toontown.suit import SuitDNA
|
|
from toontown.toonbase import ToontownBattleGlobals
|
|
from toontown.toonbase import ToontownGlobals
|
|
|
|
|
|
class DistributedBossbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FSM):
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBossbotBossAI')
|
|
maxToonLevels = 77
|
|
toonUpLevels = [1,
|
|
2,
|
|
3,
|
|
4]
|
|
BossName = "CEO"
|
|
|
|
def __init__(self, air):
|
|
DistributedBossCogAI.DistributedBossCogAI.__init__(self, air, 'c')
|
|
FSM.FSM.__init__(self, 'DistributedBossbotBossAI')
|
|
self.battleOneBattlesMade = False
|
|
self.battleThreeBattlesMade = False
|
|
self.battleFourSetup = False
|
|
self.foodBelts = []
|
|
self.numTables = 1
|
|
self.numDinersPerTable = 3
|
|
self.tables = []
|
|
self.numGolfSpots = 4
|
|
self.golfSpots = []
|
|
self.toonFoodStatus = {}
|
|
self.bossMaxDamage = ToontownGlobals.BossbotBossMaxDamage
|
|
self.threatDict = {}
|
|
self.keyStates.append('BattleFour')
|
|
self.battleFourStart = 0
|
|
self.battleDifficulty = 0
|
|
self.movingToTable = False
|
|
self.tableDest = -1
|
|
self.curTable = -1
|
|
self.speedDamage = 0
|
|
self.maxSpeedDamage = ToontownGlobals.BossbotMaxSpeedDamage
|
|
self.speedRecoverRate = ToontownGlobals.BossbotSpeedRecoverRate
|
|
self.speedRecoverStartTime = 0
|
|
self.battleFourTimeStarted = 0
|
|
self.numDinersExploded = 0
|
|
self.numMoveAttacks = 0
|
|
self.numGolfAttacks = 0
|
|
self.numGearAttacks = 0
|
|
self.numGolfAreaAttacks = 0
|
|
self.numToonupGranted = 0
|
|
self.totalLaffHealed = 0
|
|
self.toonupsGranted = []
|
|
self.doneOvertimeOneAttack = False
|
|
self.doneOvertimeTwoAttack = False
|
|
self.overtimeOneTime = simbase.air.config.GetInt('overtime-one-time', 1200)
|
|
self.battleFourDuration = simbase.air.config.GetInt('battle-four-duration', 1800)
|
|
self.overtimeOneStart = float(self.overtimeOneTime) / self.battleFourDuration
|
|
self.moveAttackAllowed = True
|
|
|
|
def delete(self):
|
|
self.notify.debug('DistributedBossbotBossAI.delete')
|
|
self.deleteBanquetTables()
|
|
self.deleteFoodBelts()
|
|
self.deleteGolfSpots()
|
|
return DistributedBossCogAI.DistributedBossCogAI.delete(self)
|
|
|
|
def enterElevator(self):
|
|
DistributedBossCogAI.DistributedBossCogAI.enterElevator(self)
|
|
self.makeBattleOneBattles()
|
|
|
|
def enterIntroduction(self):
|
|
self.arenaSide = None
|
|
self.makeBattleOneBattles()
|
|
self.barrier = self.beginBarrier('Introduction', self.involvedToons, 45, self.doneIntroduction)
|
|
return
|
|
|
|
def makeBattleOneBattles(self):
|
|
if not self.battleOneBattlesMade:
|
|
self.postBattleState = 'PrepareBattleTwo'
|
|
self.initializeBattles(1, ToontownGlobals.BossbotBossBattleOnePosHpr)
|
|
self.battleOneBattlesMade = True
|
|
|
|
def getHoodId(self):
|
|
return ToontownGlobals.LawbotHQ
|
|
|
|
def generateSuits(self, battleNumber):
|
|
if battleNumber == 1:
|
|
weakenedValue = ((1, 1),
|
|
(2, 2),
|
|
(2, 2),
|
|
(1, 1),
|
|
(1, 1, 1, 1, 1))
|
|
listVersion = list(SuitBuildingGlobals.SuitBuildingInfo)
|
|
if simbase.config.GetBool('bossbot-boss-cheat', 0):
|
|
listVersion[14] = weakenedValue
|
|
SuitBuildingGlobals.SuitBuildingInfo = tuple(listVersion)
|
|
retval = self.invokeSuitPlanner(14, 0)
|
|
return retval
|
|
else:
|
|
suits = self.generateDinerSuits()
|
|
return suits
|
|
|
|
def invokeSuitPlanner(self, buildingCode, skelecog):
|
|
suits = DistributedBossCogAI.DistributedBossCogAI.invokeSuitPlanner(self, buildingCode, skelecog)
|
|
activeSuits = suits['activeSuits'][:]
|
|
reserveSuits = suits['reserveSuits'][:]
|
|
if len(activeSuits) + len(reserveSuits) >= 4:
|
|
while len(activeSuits) < 4:
|
|
activeSuits.append(reserveSuits.pop()[0])
|
|
|
|
retval = {'activeSuits': activeSuits,
|
|
'reserveSuits': reserveSuits}
|
|
return retval
|
|
|
|
def makeBattle(self, bossCogPosHpr, battlePosHpr, roundCallback, finishCallback, battleNumber, battleSide):
|
|
if battleNumber == 1:
|
|
battle = DistributedBattleWaitersAI.DistributedBattleWaitersAI(self.air, self, roundCallback, finishCallback, battleSide)
|
|
else:
|
|
battle = DistributedBattleDinersAI.DistributedBattleDinersAI(self.air, self, roundCallback, finishCallback, battleSide)
|
|
self.setBattlePos(battle, bossCogPosHpr, battlePosHpr)
|
|
battle.suitsKilled = self.suitsKilled
|
|
battle.battleCalc.toonSkillPtsGained = self.toonSkillPtsGained
|
|
battle.toonExp = self.toonExp
|
|
battle.toonOrigQuests = self.toonOrigQuests
|
|
battle.toonItems = self.toonItems
|
|
battle.toonOrigMerits = self.toonOrigMerits
|
|
battle.toonMerits = self.toonMerits
|
|
battle.toonParts = self.toonParts
|
|
battle.helpfulToons = self.helpfulToons
|
|
mult = ToontownBattleGlobals.getBossBattleCreditMultiplier(battleNumber)
|
|
battle.battleCalc.setSkillCreditMultiplier(mult)
|
|
activeSuits = self.activeSuitsA
|
|
if battleSide:
|
|
activeSuits = self.activeSuitsB
|
|
for suit in activeSuits:
|
|
battle.addSuit(suit)
|
|
|
|
battle.generateWithRequired(self.zoneId)
|
|
return battle
|
|
|
|
def initializeBattles(self, battleNumber, bossCogPosHpr):
|
|
self.resetBattles()
|
|
if not self.involvedToons:
|
|
self.notify.warning('initializeBattles: no toons!')
|
|
return
|
|
self.battleNumber = battleNumber
|
|
suitHandles = self.generateSuits(battleNumber)
|
|
self.suitsA = suitHandles['activeSuits']
|
|
self.activeSuitsA = self.suitsA[:]
|
|
self.reserveSuits = suitHandles['reserveSuits']
|
|
if battleNumber == 3:
|
|
if self.toonsB:
|
|
movedSuit = self.suitsA.pop()
|
|
self.suitsB = [movedSuit]
|
|
self.activeSuitsB = [movedSuit]
|
|
self.activeSuitsA.remove(movedSuit)
|
|
else:
|
|
self.suitsB = []
|
|
self.activeSuitsB = []
|
|
else:
|
|
suitHandles = self.generateSuits(battleNumber)
|
|
self.suitsB = suitHandles['activeSuits']
|
|
self.activeSuitsB = self.suitsB[:]
|
|
self.reserveSuits += suitHandles['reserveSuits']
|
|
if self.toonsA:
|
|
if battleNumber == 1:
|
|
self.battleA = self.makeBattle(bossCogPosHpr, ToontownGlobals.WaiterBattleAPosHpr, self.handleRoundADone, self.handleBattleADone, battleNumber, 0)
|
|
self.battleAId = self.battleA.doId
|
|
else:
|
|
self.battleA = self.makeBattle(bossCogPosHpr, ToontownGlobals.DinerBattleAPosHpr, self.handleRoundADone, self.handleBattleADone, battleNumber, 0)
|
|
self.battleAId = self.battleA.doId
|
|
else:
|
|
self.moveSuits(self.activeSuitsA)
|
|
self.suitsA = []
|
|
self.activeSuitsA = []
|
|
if self.arenaSide == None:
|
|
self.b_setArenaSide(0)
|
|
if self.toonsB:
|
|
if battleNumber == 1:
|
|
self.battleB = self.makeBattle(bossCogPosHpr, ToontownGlobals.WaiterBattleBPosHpr, self.handleRoundBDone, self.handleBattleBDone, battleNumber, 1)
|
|
self.battleBId = self.battleB.doId
|
|
else:
|
|
self.battleB = self.makeBattle(bossCogPosHpr, ToontownGlobals.DinerBattleBPosHpr, self.handleRoundBDone, self.handleBattleBDone, battleNumber, 1)
|
|
self.battleBId = self.battleB.doId
|
|
else:
|
|
self.moveSuits(self.activeSuitsB)
|
|
self.suitsB = []
|
|
self.activeSuitsB = []
|
|
if self.arenaSide == None:
|
|
self.b_setArenaSide(1)
|
|
self.sendBattleIds()
|
|
return
|
|
|
|
def enterPrepareBattleTwo(self):
|
|
self.barrier = self.beginBarrier('PrepareBattleTwo', self.involvedToons, 45, self.__donePrepareBattleTwo)
|
|
self.createFoodBelts()
|
|
self.createBanquetTables()
|
|
|
|
def __donePrepareBattleTwo(self, avIds):
|
|
self.b_setState('BattleTwo')
|
|
|
|
def exitPrepareBattleTwo(self):
|
|
self.ignoreBarrier(self.barrier)
|
|
|
|
def createFoodBelts(self):
|
|
if self.foodBelts:
|
|
return
|
|
for i in xrange(2):
|
|
newBelt = DistributedFoodBeltAI.DistributedFoodBeltAI(self.air, self, i)
|
|
self.foodBelts.append(newBelt)
|
|
newBelt.generateWithRequired(self.zoneId)
|
|
|
|
def deleteFoodBelts(self):
|
|
for belt in self.foodBelts:
|
|
belt.requestDelete()
|
|
|
|
self.foodBelts = []
|
|
|
|
def createBanquetTables(self):
|
|
if self.tables:
|
|
return
|
|
self.calcAndSetBattleDifficulty()
|
|
diffInfo = ToontownGlobals.BossbotBossDifficultySettings[self.battleDifficulty]
|
|
self.diffInfo = diffInfo
|
|
self.numTables = diffInfo[0]
|
|
self.numDinersPerTable = diffInfo[1]
|
|
dinerLevel = diffInfo[2]
|
|
for i in xrange(self.numTables):
|
|
newTable = DistributedBanquetTableAI.DistributedBanquetTableAI(self.air, self, i, self.numDinersPerTable, dinerLevel)
|
|
self.tables.append(newTable)
|
|
newTable.generateWithRequired(self.zoneId)
|
|
|
|
def deleteBanquetTables(self):
|
|
for table in self.tables:
|
|
table.requestDelete()
|
|
|
|
self.tables = []
|
|
|
|
def enterBattleTwo(self):
|
|
self.resetBattles()
|
|
self.createFoodBelts()
|
|
self.createBanquetTables()
|
|
for belt in self.foodBelts:
|
|
belt.turnOn()
|
|
|
|
for table in self.tables:
|
|
table.turnOn()
|
|
|
|
self.barrier = self.beginBarrier('BattleTwo', self.involvedToons, ToontownGlobals.BossbotBossServingDuration + 1, self.__doneBattleTwo)
|
|
|
|
def exitBattleTwo(self):
|
|
self.ignoreBarrier(self.barrier)
|
|
for table in self.tables:
|
|
table.goInactive()
|
|
|
|
for belt in self.foodBelts:
|
|
belt.goInactive()
|
|
|
|
def __doneBattleTwo(self, avIds):
|
|
self.b_setState('PrepareBattleThree')
|
|
|
|
def requestGetFood(self, beltIndex, foodIndex, foodNum):
|
|
grantRequest = False
|
|
avId = self.air.getAvatarIdFromSender()
|
|
if self.state != 'BattleTwo':
|
|
grantRequest = False
|
|
elif (beltIndex, foodNum) not in self.toonFoodStatus.values():
|
|
if avId not in self.toonFoodStatus:
|
|
grantRequest = True
|
|
elif self.toonFoodStatus[avId] == None:
|
|
grantRequest = True
|
|
if grantRequest:
|
|
self.toonFoodStatus[avId] = (beltIndex, foodNum)
|
|
self.sendUpdate('toonGotFood', [avId,
|
|
beltIndex,
|
|
foodIndex,
|
|
foodNum])
|
|
return
|
|
|
|
def requestServeFood(self, tableIndex, chairIndex):
|
|
grantRequest = False
|
|
avId = self.air.getAvatarIdFromSender()
|
|
if self.state != 'BattleTwo':
|
|
grantRequest = False
|
|
elif tableIndex < len(self.tables):
|
|
table = self.tables[tableIndex]
|
|
dinerStatus = table.getDinerStatus(chairIndex)
|
|
if dinerStatus in (table.HUNGRY, table.ANGRY):
|
|
if self.toonFoodStatus[avId]:
|
|
grantRequest = True
|
|
if grantRequest:
|
|
self.toonFoodStatus[avId] = None
|
|
table.foodServed(chairIndex)
|
|
self.sendUpdate('toonServeFood', [avId, tableIndex, chairIndex])
|
|
return
|
|
|
|
def enterPrepareBattleThree(self):
|
|
self.barrier = self.beginBarrier('PrepareBattleThree', self.involvedToons, ToontownGlobals.BossbotBossServingDuration + 1, self.__donePrepareBattleThree)
|
|
self.divideToons()
|
|
self.makeBattleThreeBattles()
|
|
|
|
def exitPrepareBattleThree(self):
|
|
self.ignoreBarrier(self.barrier)
|
|
|
|
def __donePrepareBattleThree(self, avIds):
|
|
self.b_setState('BattleThree')
|
|
|
|
def makeBattleThreeBattles(self):
|
|
if not self.battleThreeBattlesMade:
|
|
if not self.tables:
|
|
self.createBanquetTables()
|
|
for table in self.tables:
|
|
table.turnOn()
|
|
table.goInactive()
|
|
|
|
notDeadList = []
|
|
for table in self.tables:
|
|
tableInfo = table.getNotDeadInfo()
|
|
notDeadList += tableInfo
|
|
|
|
self.notDeadList = notDeadList
|
|
self.postBattleState = 'PrepareBattleFour'
|
|
self.initializeBattles(3, ToontownGlobals.BossbotBossBattleThreePosHpr)
|
|
self.battleThreeBattlesMade = True
|
|
|
|
def generateDinerSuits(self):
|
|
diners = []
|
|
for i in xrange(len(self.notDeadList)):
|
|
if simbase.config.GetBool('bossbot-boss-cheat', 0):
|
|
suit = self.__genSuitObject(self.zoneId, 2, 'c', 2, 0)
|
|
else:
|
|
info = self.notDeadList[i]
|
|
suitType = info[2] - 4
|
|
suitLevel = info[2]
|
|
suit = self.__genSuitObject(self.zoneId, suitType, 'c', suitLevel, 1)
|
|
diners.append((suit, 100))
|
|
|
|
active = []
|
|
for i in xrange(2):
|
|
if simbase.config.GetBool('bossbot-boss-cheat', 0):
|
|
suit = self.__genSuitObject(self.zoneId, 2, 'c', 2, 0)
|
|
else:
|
|
suitType = 8
|
|
suitLevel = 12
|
|
suit = self.__genSuitObject(self.zoneId, suitType, 'c', suitLevel, 1)
|
|
active.append(suit)
|
|
|
|
return {'activeSuits': active,
|
|
'reserveSuits': diners}
|
|
|
|
def __genSuitObject(self, suitZone, suitType, bldgTrack, suitLevel, revives = 0):
|
|
newSuit = DistributedSuitAI.DistributedSuitAI(simbase.air, None)
|
|
skel = self.__setupSuitInfo(newSuit, bldgTrack, suitLevel, suitType)
|
|
if skel:
|
|
newSuit.setSkelecog(1)
|
|
newSuit.setSkeleRevives(revives)
|
|
newSuit.generateWithRequired(suitZone)
|
|
newSuit.node().setName('suit-%s' % newSuit.doId)
|
|
return newSuit
|
|
|
|
def __setupSuitInfo(self, suit, bldgTrack, suitLevel, suitType):
|
|
dna = SuitDNA.SuitDNA()
|
|
dna.newSuitRandom(suitType, bldgTrack)
|
|
suit.dna = dna
|
|
self.notify.debug('Creating suit type ' + suit.dna.name + ' of level ' + str(suitLevel) + ' from type ' + str(suitType) + ' and track ' + str(bldgTrack))
|
|
suit.setLevel(suitLevel)
|
|
return False
|
|
|
|
def enterBattleThree(self):
|
|
self.makeBattleThreeBattles()
|
|
self.notify.debug('self.battleA = %s' % self.battleA)
|
|
if self.battleA:
|
|
self.battleA.startBattle(self.toonsA, self.suitsA)
|
|
if self.battleB:
|
|
self.battleB.startBattle(self.toonsB, self.suitsB)
|
|
|
|
def exitBattleThree(self):
|
|
self.resetBattles()
|
|
|
|
def enterPrepareBattleFour(self):
|
|
self.resetBattles()
|
|
self.setupBattleFourObjects()
|
|
self.barrier = self.beginBarrier('PrepareBattleFour', self.involvedToons, 45, self.__donePrepareBattleFour)
|
|
|
|
def __donePrepareBattleFour(self, avIds):
|
|
self.b_setState('BattleFour')
|
|
|
|
def exitPrepareBattleFour(self):
|
|
self.ignoreBarrier(self.barrier)
|
|
|
|
def enterBattleFour(self):
|
|
self.battleFourTimeStarted = globalClock.getFrameTime()
|
|
self.numToonsAtStart = len(self.involvedToons)
|
|
self.resetBattles()
|
|
self.setupBattleFourObjects()
|
|
self.battleFourStart = globalClock.getFrameTime()
|
|
self.waitForNextAttack(5)
|
|
|
|
def exitBattleFour(self):
|
|
self.recordCeoInfo()
|
|
for belt in self.foodBelts:
|
|
belt.goInactive()
|
|
|
|
def recordCeoInfo(self):
|
|
didTheyWin = 0
|
|
if self.bossDamage == self.bossMaxDamage:
|
|
didTheyWin = 1
|
|
self.battleFourTimeInMin = globalClock.getFrameTime() - self.battleFourTimeStarted
|
|
self.battleFourTimeInMin /= 60.0
|
|
self.numToonsAtEnd = 0
|
|
toonHps = []
|
|
for toonId in self.involvedToons:
|
|
toon = simbase.air.doId2do.get(toonId)
|
|
if toon:
|
|
self.numToonsAtEnd += 1
|
|
toonHps.append(toon.hp)
|
|
|
|
self.air.writeServerEvent('ceoInfo', self.doId, '%d|%.2f|%d|%d|%d|%d|%d|%d|%s|%s|%.1f|%d|%d|%d|%d|%d}%d|%s|' % (didTheyWin,
|
|
self.battleFourTimeInMin,
|
|
self.battleDifficulty,
|
|
self.numToonsAtStart,
|
|
self.numToonsAtEnd,
|
|
self.numTables,
|
|
self.numTables * self.numDinersPerTable,
|
|
self.numDinersExploded,
|
|
toonHps,
|
|
self.involvedToons,
|
|
self.speedDamage,
|
|
self.numMoveAttacks,
|
|
self.numGolfAttacks,
|
|
self.numGearAttacks,
|
|
self.numGolfAreaAttacks,
|
|
self.numToonupGranted,
|
|
self.totalLaffHealed,
|
|
'ceoBugfixes'))
|
|
|
|
def setupBattleFourObjects(self):
|
|
if self.battleFourSetup:
|
|
return
|
|
if not self.tables:
|
|
self.createBanquetTables()
|
|
for table in self.tables:
|
|
table.goFree()
|
|
|
|
if not self.golfSpots:
|
|
self.createGolfSpots()
|
|
self.createFoodBelts()
|
|
for belt in self.foodBelts:
|
|
belt.goToonup()
|
|
|
|
self.battleFourSetup = True
|
|
|
|
def hitBoss(self, bossDamage):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
if not self.validate(avId, avId in self.involvedToons, 'hitBoss from unknown avatar'):
|
|
return
|
|
self.validate(avId, bossDamage <= 3, 'invalid bossDamage %s' % bossDamage)
|
|
if bossDamage < 1:
|
|
return
|
|
currState = self.getCurrentOrNextState()
|
|
if currState != 'BattleFour':
|
|
return
|
|
bossDamage *= 2
|
|
bossDamage = min(self.getBossDamage() + bossDamage, self.bossMaxDamage)
|
|
self.b_setBossDamage(bossDamage, 0, 0)
|
|
if self.bossDamage >= self.bossMaxDamage:
|
|
self.b_setState('Victory')
|
|
else:
|
|
self.__recordHit(bossDamage)
|
|
|
|
def __recordHit(self, bossDamage):
|
|
now = globalClock.getFrameTime()
|
|
self.hitCount += 1
|
|
avId = self.air.getAvatarIdFromSender()
|
|
self.addThreat(avId, bossDamage)
|
|
|
|
def getBossDamage(self):
|
|
return self.bossDamage
|
|
|
|
def b_setBossDamage(self, bossDamage, recoverRate, recoverStartTime):
|
|
self.d_setBossDamage(bossDamage, recoverRate, recoverStartTime)
|
|
self.setBossDamage(bossDamage, recoverRate, recoverStartTime)
|
|
|
|
def setBossDamage(self, bossDamage, recoverRate, recoverStartTime):
|
|
self.bossDamage = bossDamage
|
|
self.recoverRate = recoverRate
|
|
self.recoverStartTime = recoverStartTime
|
|
|
|
def d_setBossDamage(self, bossDamage, recoverRate, recoverStartTime):
|
|
timestamp = globalClockDelta.localToNetworkTime(recoverStartTime)
|
|
self.sendUpdate('setBossDamage', [bossDamage, recoverRate, timestamp])
|
|
|
|
def getSpeedDamage(self):
|
|
now = globalClock.getFrameTime()
|
|
elapsed = now - self.speedRecoverStartTime
|
|
self.notify.debug('elapsed=%s' % elapsed)
|
|
floatSpeedDamage = max(self.speedDamage - self.speedRecoverRate * elapsed / 60.0, 0)
|
|
self.notify.debug('floatSpeedDamage = %s' % floatSpeedDamage)
|
|
return int(max(self.speedDamage - self.speedRecoverRate * elapsed / 60.0, 0))
|
|
|
|
def getFloatSpeedDamage(self):
|
|
now = globalClock.getFrameTime()
|
|
elapsed = now - self.speedRecoverStartTime
|
|
floatSpeedDamage = max(self.speedDamage - self.speedRecoverRate * elapsed / 60.0, 0)
|
|
self.notify.debug('floatSpeedDamage = %s' % floatSpeedDamage)
|
|
return max(self.speedDamage - self.speedRecoverRate * elapsed / 60.0, 0)
|
|
|
|
def b_setSpeedDamage(self, speedDamage, recoverRate, recoverStartTime):
|
|
self.d_setSpeedDamage(speedDamage, recoverRate, recoverStartTime)
|
|
self.setSpeedDamage(speedDamage, recoverRate, recoverStartTime)
|
|
|
|
def setSpeedDamage(self, speedDamage, recoverRate, recoverStartTime):
|
|
self.speedDamage = speedDamage
|
|
self.speedRecoverRate = recoverRate
|
|
self.speedRecoverStartTime = recoverStartTime
|
|
|
|
def d_setSpeedDamage(self, speedDamage, recoverRate, recoverStartTime):
|
|
timestamp = globalClockDelta.localToNetworkTime(recoverStartTime)
|
|
self.sendUpdate('setSpeedDamage', [speedDamage, recoverRate, timestamp])
|
|
|
|
def createGolfSpots(self):
|
|
if self.golfSpots:
|
|
return
|
|
for i in xrange(self.numGolfSpots):
|
|
newGolfSpot = DistributedGolfSpotAI.DistributedGolfSpotAI(self.air, self, i)
|
|
self.golfSpots.append(newGolfSpot)
|
|
newGolfSpot.generateWithRequired(self.zoneId)
|
|
newGolfSpot.forceFree()
|
|
|
|
def deleteGolfSpots(self):
|
|
for spot in self.golfSpots:
|
|
spot.requestDelete()
|
|
|
|
self.golfSpots = []
|
|
|
|
def ballHitBoss(self, speedDamage):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
if not self.validate(avId, avId in self.involvedToons, 'hitBoss from unknown avatar'):
|
|
return
|
|
if speedDamage < 1:
|
|
return
|
|
currState = self.getCurrentOrNextState()
|
|
if currState != 'BattleFour':
|
|
return
|
|
now = globalClock.getFrameTime()
|
|
newDamage = self.getSpeedDamage() + speedDamage
|
|
self.notify.debug('newDamage = %s' % newDamage)
|
|
speedDamage = min(self.getFloatSpeedDamage() + speedDamage, self.maxSpeedDamage)
|
|
self.b_setSpeedDamage(speedDamage, self.speedRecoverRate, now)
|
|
self.addThreat(avId, 0.1)
|
|
|
|
def enterVictory(self):
|
|
self.resetBattles()
|
|
for table in self.tables:
|
|
table.turnOff()
|
|
|
|
for golfSpot in self.golfSpots:
|
|
golfSpot.turnOff()
|
|
|
|
self.suitsKilled.append({'type': None,
|
|
'level': None,
|
|
'track': self.dna.dept,
|
|
'isSkelecog': 0,
|
|
'isForeman': 0,
|
|
'isBoss': 1,
|
|
'isSupervisor': 0,
|
|
'isVirtual': 0,
|
|
'activeToons': self.involvedToons[:]})
|
|
self.addStats()
|
|
self.barrier = self.beginBarrier('Victory', self.involvedToons, 30, self.__doneVictory)
|
|
return
|
|
|
|
def __doneVictory(self, avIds):
|
|
self.d_setBattleExperience()
|
|
self.b_setState('Reward')
|
|
BattleExperienceAI.assignRewards(self.involvedToons, self.toonSkillPtsGained, self.suitsKilled, ToontownGlobals.dept2cogHQ(self.dept), self.helpfulToons)
|
|
for toonId in self.involvedToons:
|
|
toon = self.air.doId2do.get(toonId)
|
|
if toon:
|
|
self.givePinkSlipReward(toon)
|
|
toon.b_promote(self.deptIndex)
|
|
|
|
def givePinkSlipReward(self, toon):
|
|
toon.addPinkSlips(self.battleDifficulty + 1)
|
|
|
|
def getThreat(self, toonId):
|
|
if toonId in self.threatDict:
|
|
return self.threatDict[toonId]
|
|
else:
|
|
return 0
|
|
|
|
def addThreat(self, toonId, threat):
|
|
if toonId in self.threatDict:
|
|
self.threatDict[toonId] += threat
|
|
else:
|
|
self.threatDict[toonId] = threat
|
|
|
|
def subtractThreat(self, toonId, threat):
|
|
if toonId in self.threatDict:
|
|
self.threatDict[toonId] -= threat
|
|
else:
|
|
self.threatDict[toonId] = 0
|
|
if self.threatDict[toonId] < 0:
|
|
self.threatDict[toonId] = 0
|
|
|
|
def waitForNextAttack(self, delayTime):
|
|
currState = self.getCurrentOrNextState()
|
|
if currState == 'BattleFour':
|
|
taskName = self.uniqueName('NextAttack')
|
|
taskMgr.remove(taskName)
|
|
taskMgr.doMethodLater(delayTime, self.doNextAttack, taskName)
|
|
|
|
def doNextAttack(self, task):
|
|
attackCode = -1
|
|
optionalParam = None
|
|
if self.movingToTable:
|
|
self.waitForNextAttack(5)
|
|
elif self.attackCode == ToontownGlobals.BossCogDizzyNow:
|
|
attackCode = ToontownGlobals.BossCogRecoverDizzyAttack
|
|
elif self.getBattleFourTime() > self.overtimeOneStart and not self.doneOvertimeOneAttack:
|
|
attackCode = ToontownGlobals.BossCogOvertimeAttack
|
|
self.doneOvertimeOneAttack = True
|
|
optionalParam = 0
|
|
elif self.getBattleFourTime() > 1.0 and not self.doneOvertimeTwoAttack:
|
|
attackCode = ToontownGlobals.BossCogOvertimeAttack
|
|
self.doneOvertimeTwoAttack = True
|
|
optionalParam = 1
|
|
else:
|
|
attackCode = random.choice([ToontownGlobals.BossCogGolfAreaAttack,
|
|
ToontownGlobals.BossCogDirectedAttack,
|
|
ToontownGlobals.BossCogDirectedAttack,
|
|
ToontownGlobals.BossCogDirectedAttack,
|
|
ToontownGlobals.BossCogDirectedAttack])
|
|
if attackCode == ToontownGlobals.BossCogAreaAttack:
|
|
self.__doAreaAttack()
|
|
if attackCode == ToontownGlobals.BossCogGolfAreaAttack:
|
|
self.__doGolfAreaAttack()
|
|
elif attackCode == ToontownGlobals.BossCogDirectedAttack:
|
|
self.__doDirectedAttack()
|
|
elif attackCode >= 0:
|
|
self.b_setAttackCode(attackCode, optionalParam)
|
|
return
|
|
|
|
def progressValue(self, fromValue, toValue):
|
|
t0 = float(self.bossDamage) / float(self.bossMaxDamage)
|
|
elapsed = globalClock.getFrameTime() - self.battleFourStart
|
|
t1 = elapsed / float(self.battleThreeDuration)
|
|
t = max(t0, t1)
|
|
progVal = fromValue + (toValue - fromValue) * min(t, 1)
|
|
self.notify.debug('progVal=%s' % progVal)
|
|
return progVal
|
|
|
|
def __doDirectedAttack(self):
|
|
toonId = self.getMaxThreatToon()
|
|
self.notify.debug('toonToAttack=%s' % toonId)
|
|
unflattenedToons = self.getUnflattenedToons()
|
|
attackTotallyRandomToon = random.random() < 0.1
|
|
if unflattenedToons and (attackTotallyRandomToon or toonId == 0):
|
|
toonId = random.choice(unflattenedToons)
|
|
if toonId:
|
|
toonThreat = self.getThreat(toonId)
|
|
toonThreat *= 0.25
|
|
threatToSubtract = max(toonThreat, 10)
|
|
self.subtractThreat(toonId, threatToSubtract)
|
|
if self.isToonRoaming(toonId):
|
|
self.b_setAttackCode(ToontownGlobals.BossCogGolfAttack, toonId)
|
|
self.numGolfAttacks += 1
|
|
elif self.isToonOnTable(toonId):
|
|
doesMoveAttack = simbase.air.config.GetBool('ceo-does-move-attack', 1)
|
|
if doesMoveAttack:
|
|
chanceToShoot = 0.25
|
|
else:
|
|
chanceToShoot = 1.0
|
|
if not self.moveAttackAllowed:
|
|
self.notify.debug('moveAttack is not allowed, doing gearDirectedAttack')
|
|
chanceToShoot = 1.0
|
|
if random.random() < chanceToShoot:
|
|
self.b_setAttackCode(ToontownGlobals.BossCogGearDirectedAttack, toonId)
|
|
self.numGearAttacks += 1
|
|
else:
|
|
tableIndex = self.getToonTableIndex(toonId)
|
|
self.doMoveAttack(tableIndex)
|
|
else:
|
|
self.b_setAttackCode(ToontownGlobals.BossCogGolfAttack, toonId)
|
|
else:
|
|
uprightTables = self.getUprightTables()
|
|
if uprightTables:
|
|
tableToMoveTo = random.choice(uprightTables)
|
|
self.doMoveAttack(tableToMoveTo)
|
|
else:
|
|
self.waitForNextAttack(4)
|
|
|
|
def doMoveAttack(self, tableIndex):
|
|
self.numMoveAttacks += 1
|
|
self.movingToTable = True
|
|
self.tableDest = tableIndex
|
|
self.b_setAttackCode(ToontownGlobals.BossCogMoveAttack, tableIndex)
|
|
|
|
def getUnflattenedToons(self):
|
|
result = []
|
|
uprightTables = self.getUprightTables()
|
|
for toonId in self.involvedToons:
|
|
toonTable = self.getToonTableIndex(toonId)
|
|
if toonTable >= 0 and toonTable not in uprightTables:
|
|
pass
|
|
else:
|
|
result.append(toonId)
|
|
|
|
return result
|
|
|
|
def getMaxThreatToon(self):
|
|
returnedToonId = 0
|
|
maxThreat = 0
|
|
maxToons = []
|
|
for toonId in self.threatDict:
|
|
curThreat = self.threatDict[toonId]
|
|
tableIndex = self.getToonTableIndex(toonId)
|
|
if tableIndex > -1 and self.tables[tableIndex].state == 'Flat':
|
|
pass
|
|
elif curThreat > maxThreat:
|
|
maxToons = [toonId]
|
|
maxThreat = curThreat
|
|
elif curThreat == maxThreat:
|
|
maxToons.append(toonId)
|
|
|
|
if maxToons:
|
|
returnedToonId = random.choice(maxToons)
|
|
return returnedToonId
|
|
|
|
def getToonDifficulty(self):
|
|
totalCogSuitTier = 0
|
|
totalToons = 0
|
|
|
|
for toonId in self.involvedToons:
|
|
toon = simbase.air.doId2do.get(toonId)
|
|
if toon:
|
|
totalToons += 1
|
|
totalCogSuitTier += toon.cogTypes[1]
|
|
|
|
averageTier = math.floor(totalCogSuitTier / totalToons) + 1
|
|
return int(averageTier)
|
|
|
|
def calcAndSetBattleDifficulty(self):
|
|
self.toonLevels = self.getToonDifficulty()
|
|
battleDifficulty = int(math.floor(self.toonLevels / 2))
|
|
self.b_setBattleDifficulty(battleDifficulty)
|
|
|
|
def b_setBattleDifficulty(self, batDiff):
|
|
self.setBattleDifficulty(batDiff)
|
|
self.d_setBattleDifficulty(batDiff)
|
|
|
|
def setBattleDifficulty(self, batDiff):
|
|
self.battleDifficulty = batDiff
|
|
|
|
def d_setBattleDifficulty(self, batDiff):
|
|
self.sendUpdate('setBattleDifficulty', [batDiff])
|
|
|
|
def getUprightTables(self):
|
|
tableList = []
|
|
for table in self.tables:
|
|
if table.state != 'Flat':
|
|
tableList.append(table.index)
|
|
|
|
return tableList
|
|
|
|
def getToonTableIndex(self, toonId):
|
|
tableIndex = -1
|
|
for table in self.tables:
|
|
if table.avId == toonId:
|
|
tableIndex = table.index
|
|
break
|
|
|
|
return tableIndex
|
|
|
|
def getToonGolfSpotIndex(self, toonId):
|
|
golfSpotIndex = -1
|
|
for golfSpot in self.golfSpots:
|
|
if golfSpot.avId == toonId:
|
|
golfSpotIndex = golfSpot.index
|
|
break
|
|
|
|
return golfSpotIndex
|
|
|
|
def isToonOnTable(self, toonId):
|
|
result = self.getToonTableIndex(toonId) != -1
|
|
return result
|
|
|
|
def isToonOnGolfSpot(self, toonId):
|
|
result = self.getToonGolfSpotIndex(toonId) != -1
|
|
return result
|
|
|
|
def isToonRoaming(self, toonId):
|
|
result = not self.isToonOnTable(toonId) and not self.isToonOnGolfSpot(toonId)
|
|
return result
|
|
|
|
def reachedTable(self, tableIndex):
|
|
if self.movingToTable and self.tableDest == tableIndex:
|
|
self.movingToTable = False
|
|
self.curTable = self.tableDest
|
|
self.tableDest = -1
|
|
|
|
def hitTable(self, tableIndex):
|
|
self.notify.debug('hitTable tableIndex=%d' % tableIndex)
|
|
if tableIndex < len(self.tables):
|
|
table = self.tables[tableIndex]
|
|
if table.state != 'Flat':
|
|
table.goFlat()
|
|
|
|
def awayFromTable(self, tableIndex):
|
|
self.notify.debug('awayFromTable tableIndex=%d' % tableIndex)
|
|
if tableIndex < len(self.tables):
|
|
taskName = 'Unflatten-%d' % tableIndex
|
|
unflattenTime = self.diffInfo[3]
|
|
taskMgr.doMethodLater(unflattenTime, self.unflattenTable, taskName, extraArgs=[tableIndex])
|
|
|
|
def unflattenTable(self, tableIndex):
|
|
if tableIndex < len(self.tables):
|
|
table = self.tables[tableIndex]
|
|
if table.state == 'Flat':
|
|
if table.avId and table.avId in self.involvedToons:
|
|
table.forceControl(table.avId)
|
|
else:
|
|
table.goFree()
|
|
|
|
def incrementDinersExploded(self):
|
|
self.numDinersExploded += 1
|
|
|
|
def magicWordHit(self, damage, avId):
|
|
self.hitBoss(damage)
|
|
|
|
def __doAreaAttack(self):
|
|
self.b_setAttackCode(ToontownGlobals.BossCogAreaAttack)
|
|
|
|
def __doGolfAreaAttack(self):
|
|
self.numGolfAreaAttacks += 1
|
|
self.b_setAttackCode(ToontownGlobals.BossCogGolfAreaAttack)
|
|
|
|
def hitToon(self, toonId):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
if not self.validate(avId, avId != toonId, 'hitToon on self'):
|
|
return
|
|
if avId not in self.involvedToons or toonId not in self.involvedToons:
|
|
return
|
|
toon = self.air.doId2do.get(toonId)
|
|
if toon:
|
|
self.healToon(toon, 1)
|
|
self.sendUpdate('toonGotHealed', [toonId])
|
|
|
|
def requestGetToonup(self, beltIndex, toonupIndex, toonupNum):
|
|
grantRequest = False
|
|
avId = self.air.getAvatarIdFromSender()
|
|
if self.state != 'BattleFour':
|
|
grantRequest = False
|
|
elif (beltIndex, toonupNum) not in self.toonupsGranted:
|
|
toon = simbase.air.doId2do.get(avId)
|
|
if toon:
|
|
grantRequest = True
|
|
if grantRequest:
|
|
self.toonupsGranted.insert(0, (beltIndex, toonupNum))
|
|
if len(self.toonupsGranted) > 8:
|
|
self.toonupsGranted = self.toonupsGranted[0:8]
|
|
self.sendUpdate('toonGotToonup', [avId,
|
|
beltIndex,
|
|
toonupIndex,
|
|
toonupNum])
|
|
if toonupIndex < len(self.toonUpLevels):
|
|
self.healToon(toon, self.toonUpLevels[toonupIndex])
|
|
self.numToonupGranted += 1
|
|
self.totalLaffHealed += self.toonUpLevels[toonupIndex]
|
|
else:
|
|
self.notify.warning('requestGetToonup this should not happen')
|
|
self.healToon(toon, 1)
|
|
|
|
def toonLeftTable(self, tableIndex):
|
|
if self.movingToTable and self.tableDest == tableIndex:
|
|
if random.random() < 0.5:
|
|
self.movingToTable = False
|
|
self.waitForNextAttack(0)
|
|
|
|
def getBattleFourTime(self):
|
|
if self.state != 'BattleFour':
|
|
t1 = 0
|
|
else:
|
|
elapsed = globalClock.getFrameTime() - self.battleFourStart
|
|
t1 = elapsed / float(self.battleFourDuration)
|
|
return t1
|
|
|
|
def getDamageMultiplier(self):
|
|
mult = 1.0
|
|
if self.doneOvertimeOneAttack and not self.doneOvertimeTwoAttack:
|
|
mult = 1.25
|
|
if self.getBattleFourTime() > 1.0:
|
|
mult = self.getBattleFourTime() + 1
|
|
return mult
|
|
|
|
def toggleMove(self):
|
|
self.moveAttackAllowed = not self.moveAttackAllowed
|
|
return self.moveAttackAllowed
|
|
|
|
|
|
def getCEO(toon):
|
|
for object in simbase.air.doId2do.values():
|
|
if isinstance(object, DistributedBossbotBossAI):
|
|
if toon.doId in object.involvedToons:
|
|
return object
|
|
|
|
return None
|
|
|
|
@magicWord(category=CATEGORY_ADMINISTRATOR)
|
|
def skipCEOBanquet():
|
|
"""
|
|
Skips to the banquet stage of the CEO.
|
|
"""
|
|
boss = getCEO(spellbook.getInvoker())
|
|
if not boss:
|
|
return "You aren't in a CEO!"
|
|
if boss.state in ('PrepareBattleTwo', 'BattleTwo'):
|
|
return "You can't skip this round."
|
|
boss.exitIntroduction()
|
|
boss.b_setState('PrepareBattleTwo')
|
|
|
|
@magicWord(category=CATEGORY_ADMINISTRATOR)
|
|
def skipCEO():
|
|
"""
|
|
Skips to the third round of the CEO.
|
|
"""
|
|
boss = getCEO(spellbook.getInvoker())
|
|
if not boss:
|
|
return "You aren't in a CEO!"
|
|
if boss.state in ('PrepareBattleThree', 'BattleThree'):
|
|
return "You can't skip this round."
|
|
boss.exitIntroduction()
|
|
boss.b_setState('PrepareBattleThree')
|
|
|
|
@magicWord(category=CATEGORY_ADMINISTRATOR)
|
|
def skipCEOFinal():
|
|
"""
|
|
Skips to the final round of the CEO.
|
|
"""
|
|
boss = getCEO(spellbook.getInvoker())
|
|
if not boss:
|
|
return "You aren't in a CEO!"
|
|
if boss.state in ('PrepareBattleFour', 'BattleFour'):
|
|
return "You can't skip this round."
|
|
boss.exitIntroduction()
|
|
boss.b_setState('PrepareBattleFour')
|
|
|
|
@magicWord(category=CATEGORY_ADMINISTRATOR)
|
|
def killCEO():
|
|
"""
|
|
Kills the CEO.
|
|
"""
|
|
boss = getCEO(spellbook.getInvoker())
|
|
if not boss:
|
|
return "You aren't in a CEO!"
|
|
boss.b_setState('Victory')
|
|
return 'Killed CEO.'
|