551 lines
19 KiB
Python
551 lines
19 KiB
Python
from panda3d.core import *
|
|
from direct.directnotify import DirectNotifyGlobal
|
|
from toontown.toonbase import ToontownGlobals
|
|
from toontown.coghq import DistributedCashbotBossCraneAI
|
|
from toontown.coghq import DistributedCashbotBossSafeAI
|
|
from toontown.suit import DistributedCashbotBossGoonAI
|
|
from toontown.coghq import DistributedCashbotBossTreasureAI
|
|
from toontown.battle import BattleExperienceAI
|
|
from toontown.chat import ResistanceChat
|
|
from direct.fsm import FSM
|
|
import DistributedBossCogAI
|
|
import SuitDNA
|
|
import random
|
|
from otp.ai.MagicWordGlobal import *
|
|
import math
|
|
|
|
class DistributedCashbotBossAI(DistributedBossCogAI.DistributedBossCogAI, FSM.FSM):
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedCashbotBossAI')
|
|
maxGoons = 8
|
|
BossName = "CFO"
|
|
|
|
def __init__(self, air):
|
|
DistributedBossCogAI.DistributedBossCogAI.__init__(self, air, 'm')
|
|
FSM.FSM.__init__(self, 'DistributedCashbotBossAI')
|
|
self.cranes = None
|
|
self.safes = None
|
|
self.goons = None
|
|
self.treasures = {}
|
|
self.grabbingTreasures = {}
|
|
self.recycledTreasures = []
|
|
self.healAmount = 0
|
|
self.rewardId = ResistanceChat.getRandomId()
|
|
self.rewardedToons = []
|
|
self.scene = NodePath('scene')
|
|
self.reparentTo(self.scene)
|
|
cn = CollisionNode('walls')
|
|
cs = CollisionSphere(0, 0, 0, 13)
|
|
cn.addSolid(cs)
|
|
cs = CollisionInvSphere(0, 0, 0, 42)
|
|
cn.addSolid(cs)
|
|
self.attachNewNode(cn)
|
|
self.heldObject = None
|
|
self.waitingForHelmet = 0
|
|
self.avatarHelmets = {}
|
|
self.bossMaxDamage = ToontownGlobals.CashbotBossMaxDamage
|
|
return
|
|
|
|
def generate(self):
|
|
DistributedBossCogAI.DistributedBossCogAI.generate(self)
|
|
|
|
def getHoodId(self):
|
|
return ToontownGlobals.CashbotHQ
|
|
|
|
def formatReward(self):
|
|
return str(self.rewardId)
|
|
|
|
def makeBattleOneBattles(self):
|
|
self.postBattleState = 'PrepareBattleThree'
|
|
self.initializeBattles(1, ToontownGlobals.CashbotBossBattleOnePosHpr)
|
|
|
|
def generateSuits(self, battleNumber):
|
|
cogs = self.invokeSuitPlanner(11, 0)
|
|
skelecogs = self.invokeSuitPlanner(12, 1)
|
|
activeSuits = cogs['activeSuits'] + skelecogs['activeSuits']
|
|
reserveSuits = cogs['reserveSuits'] + skelecogs['reserveSuits']
|
|
random.shuffle(activeSuits)
|
|
while len(activeSuits) > 4:
|
|
suit = activeSuits.pop()
|
|
reserveSuits.append((suit, 100))
|
|
|
|
def compareJoinChance(a, b):
|
|
return cmp(a[1], b[1])
|
|
|
|
reserveSuits.sort(compareJoinChance)
|
|
return {'activeSuits': activeSuits,
|
|
'reserveSuits': reserveSuits}
|
|
|
|
def removeToon(self, avId):
|
|
if self.cranes != None:
|
|
for crane in self.cranes:
|
|
crane.removeToon(avId)
|
|
|
|
if self.safes != None:
|
|
for safe in self.safes:
|
|
safe.removeToon(avId)
|
|
|
|
if self.goons != None:
|
|
for goon in self.goons:
|
|
goon.removeToon(avId)
|
|
|
|
DistributedBossCogAI.DistributedBossCogAI.removeToon(self, avId)
|
|
return
|
|
|
|
def __makeBattleThreeObjects(self):
|
|
if self.cranes == None:
|
|
self.cranes = []
|
|
for index in xrange(len(ToontownGlobals.CashbotBossCranePosHprs)):
|
|
crane = DistributedCashbotBossCraneAI.DistributedCashbotBossCraneAI(self.air, self, index)
|
|
crane.generateWithRequired(self.zoneId)
|
|
self.cranes.append(crane)
|
|
|
|
if self.safes == None:
|
|
self.safes = []
|
|
for index in xrange(len(ToontownGlobals.CashbotBossSafePosHprs)):
|
|
safe = DistributedCashbotBossSafeAI.DistributedCashbotBossSafeAI(self.air, self, index)
|
|
safe.generateWithRequired(self.zoneId)
|
|
self.safes.append(safe)
|
|
|
|
if self.goons == None:
|
|
self.goons = []
|
|
return
|
|
|
|
def __resetBattleThreeObjects(self):
|
|
if self.cranes != None:
|
|
for crane in self.cranes:
|
|
crane.request('Free')
|
|
|
|
if self.safes != None:
|
|
for safe in self.safes:
|
|
safe.request('Initial')
|
|
|
|
return
|
|
|
|
def __deleteBattleThreeObjects(self):
|
|
if self.cranes != None:
|
|
for crane in self.cranes:
|
|
crane.request('Off')
|
|
crane.requestDelete()
|
|
|
|
self.cranes = None
|
|
if self.safes != None:
|
|
for safe in self.safes:
|
|
safe.request('Off')
|
|
safe.requestDelete()
|
|
|
|
self.safes = None
|
|
if self.goons != None:
|
|
for goon in self.goons:
|
|
goon.request('Off')
|
|
goon.requestDelete()
|
|
|
|
self.goons = None
|
|
return
|
|
|
|
def doNextAttack(self, task):
|
|
if random.random() <= 0.2:
|
|
self.b_setAttackCode(ToontownGlobals.BossCogAreaAttack)
|
|
taskMgr.doMethodLater(7.36, self.__reviveGoons, self.uniqueName('reviveGoons'))
|
|
else:
|
|
self.__doDirectedAttack()
|
|
if self.heldObject == None and not self.waitingForHelmet:
|
|
self.waitForNextHelmet()
|
|
|
|
def __reviveGoons(self, task):
|
|
for goon in self.goons:
|
|
if goon.state == 'Stunned':
|
|
goon.request('Recovery')
|
|
|
|
def __doDirectedAttack(self):
|
|
if self.toonsToAttack:
|
|
toonId = self.toonsToAttack.pop(0)
|
|
while toonId not in self.involvedToons:
|
|
if not self.toonsToAttack:
|
|
self.b_setAttackCode(ToontownGlobals.BossCogNoAttack)
|
|
return
|
|
toonId = self.toonsToAttack.pop(0)
|
|
|
|
self.toonsToAttack.append(toonId)
|
|
self.b_setAttackCode(ToontownGlobals.BossCogSlowDirectedAttack, toonId)
|
|
|
|
def reprieveToon(self, avId):
|
|
if avId in self.toonsToAttack:
|
|
i = self.toonsToAttack.index(avId)
|
|
del self.toonsToAttack[i]
|
|
self.toonsToAttack.append(avId)
|
|
|
|
def makeTreasure(self, goon):
|
|
if self.state != 'BattleThree':
|
|
return
|
|
pos = goon.getPos(self)
|
|
v = Vec3(pos[0], pos[1], 0.0)
|
|
if not v.normalize():
|
|
v = Vec3(1, 0, 0)
|
|
v = v * 27
|
|
angle = random.uniform(0.0, 2.0 * math.pi)
|
|
radius = 10
|
|
dx = radius * math.cos(angle)
|
|
dy = radius * math.sin(angle)
|
|
fpos = self.scene.getRelativePoint(self, Point3(v[0] + dx, v[1] + dy, 0))
|
|
if goon.strength <= 10:
|
|
style = ToontownGlobals.ToontownCentral
|
|
healAmount = 3
|
|
elif goon.strength <= 15:
|
|
style = random.choice([ToontownGlobals.DonaldsDock, ToontownGlobals.DaisyGardens, ToontownGlobals.MinniesMelodyland])
|
|
healAmount = 10
|
|
else:
|
|
style = random.choice([ToontownGlobals.TheBrrrgh, ToontownGlobals.DonaldsDreamland])
|
|
healAmount = 12
|
|
if self.recycledTreasures:
|
|
treasure = self.recycledTreasures.pop(0)
|
|
treasure.d_setGrab(0)
|
|
treasure.b_setGoonId(goon.doId)
|
|
treasure.b_setStyle(style)
|
|
treasure.b_setPosition(pos[0], pos[1], 0)
|
|
treasure.b_setFinalPosition(fpos[0], fpos[1], 0)
|
|
else:
|
|
treasure = DistributedCashbotBossTreasureAI.DistributedCashbotBossTreasureAI(self.air, self, goon, style, fpos[0], fpos[1], 0)
|
|
treasure.generateWithRequired(self.zoneId)
|
|
treasure.healAmount = healAmount
|
|
self.treasures[treasure.doId] = treasure
|
|
|
|
def grabAttempt(self, avId, treasureId):
|
|
av = self.air.doId2do.get(avId)
|
|
if not av:
|
|
return
|
|
treasure = self.treasures.get(treasureId)
|
|
if treasure:
|
|
if treasure.validAvatar(av):
|
|
del self.treasures[treasureId]
|
|
treasure.d_setGrab(avId)
|
|
self.grabbingTreasures[treasureId] = treasure
|
|
taskMgr.doMethodLater(5, self.__recycleTreasure, treasure.uniqueName('recycleTreasure'), extraArgs=[treasure])
|
|
else:
|
|
treasure.d_setReject()
|
|
|
|
def __recycleTreasure(self, treasure):
|
|
if treasure.doId in self.grabbingTreasures:
|
|
del self.grabbingTreasures[treasure.doId]
|
|
self.recycledTreasures.append(treasure)
|
|
|
|
def deleteAllTreasures(self):
|
|
for treasure in self.treasures.values():
|
|
treasure.requestDelete()
|
|
|
|
self.treasures = {}
|
|
for treasure in self.grabbingTreasures.values():
|
|
taskMgr.remove(treasure.uniqueName('recycleTreasure'))
|
|
treasure.requestDelete()
|
|
|
|
self.grabbingTreasures = {}
|
|
for treasure in self.recycledTreasures:
|
|
treasure.requestDelete()
|
|
|
|
self.recycledTreasures = []
|
|
|
|
def getMaxGoons(self):
|
|
t = self.getBattleThreeTime()
|
|
if t <= 1.0:
|
|
return self.maxGoons
|
|
elif t <= 1.1:
|
|
return self.maxGoons + 1
|
|
elif t <= 1.2:
|
|
return self.maxGoons + 2
|
|
elif t <= 1.3:
|
|
return self.maxGoons + 3
|
|
elif t <= 1.4:
|
|
return self.maxGoons + 4
|
|
else:
|
|
return self.maxGoons + 8
|
|
|
|
def makeGoon(self, side = None):
|
|
if side == None:
|
|
side = random.choice(['EmergeA', 'EmergeB'])
|
|
goon = self.__chooseOldGoon()
|
|
if goon == None:
|
|
if len(self.goons) >= self.getMaxGoons():
|
|
return
|
|
goon = DistributedCashbotBossGoonAI.DistributedCashbotBossGoonAI(self.air, self)
|
|
goon.generateWithRequired(self.zoneId)
|
|
self.goons.append(goon)
|
|
if self.getBattleThreeTime() > 1.0:
|
|
goon.STUN_TIME = 4
|
|
goon.b_setupGoon(velocity=8, hFov=90, attackRadius=20, strength=30, scale=1.8)
|
|
else:
|
|
goon.STUN_TIME = self.progressValue(30, 8)
|
|
goon.b_setupGoon(velocity=self.progressRandomValue(3, 7), hFov=self.progressRandomValue(70, 80), attackRadius=self.progressRandomValue(6, 15), strength=int(self.progressRandomValue(5, 25)), scale=self.progressRandomValue(0.5, 1.5))
|
|
goon.request(side)
|
|
return
|
|
|
|
def __chooseOldGoon(self):
|
|
for goon in self.goons:
|
|
if goon.state == 'Off':
|
|
return goon
|
|
|
|
def waitForNextGoon(self, delayTime):
|
|
currState = self.getCurrentOrNextState()
|
|
if currState == 'BattleThree':
|
|
taskName = self.uniqueName('NextGoon')
|
|
taskMgr.remove(taskName)
|
|
taskMgr.doMethodLater(delayTime, self.doNextGoon, taskName)
|
|
|
|
def stopGoons(self):
|
|
taskName = self.uniqueName('NextGoon')
|
|
taskMgr.remove(taskName)
|
|
taskMgr.remove(self.uniqueName('reviveGoons'))
|
|
|
|
def doNextGoon(self, task):
|
|
if self.attackCode != ToontownGlobals.BossCogDizzy:
|
|
self.makeGoon()
|
|
delayTime = self.progressValue(10, 2)
|
|
self.waitForNextGoon(delayTime)
|
|
|
|
def waitForNextHelmet(self):
|
|
currState = self.getCurrentOrNextState()
|
|
if currState == 'BattleThree':
|
|
taskName = self.uniqueName('NextHelmet')
|
|
taskMgr.remove(taskName)
|
|
delayTime = self.progressValue(45, 15)
|
|
taskMgr.doMethodLater(delayTime, self.__donHelmet, taskName)
|
|
self.waitingForHelmet = 1
|
|
|
|
def __donHelmet(self, task):
|
|
self.waitingForHelmet = 0
|
|
if self.heldObject == None:
|
|
safe = self.safes[0]
|
|
safe.request('Grabbed', self.doId, self.doId)
|
|
self.heldObject = safe
|
|
return
|
|
|
|
def stopHelmets(self):
|
|
self.waitingForHelmet = 0
|
|
taskName = self.uniqueName('NextHelmet')
|
|
taskMgr.remove(taskName)
|
|
|
|
def acceptHelmetFrom(self, avId):
|
|
now = globalClock.getFrameTime()
|
|
then = self.avatarHelmets.get(avId, None)
|
|
if then == None or now - then > 300:
|
|
self.avatarHelmets[avId] = now
|
|
return 1
|
|
return 0
|
|
|
|
def magicWordHit(self, damage, avId):
|
|
if self.heldObject:
|
|
self.heldObject.demand('Dropped', avId, self.doId)
|
|
self.heldObject.avoidHelmet = 1
|
|
self.heldObject = None
|
|
self.waitForNextHelmet()
|
|
else:
|
|
self.recordHit(damage)
|
|
return
|
|
|
|
def magicWordReset(self):
|
|
if self.state == 'BattleThree':
|
|
self.__resetBattleThreeObjects()
|
|
|
|
def magicWordResetGoons(self):
|
|
if self.state == 'BattleThree':
|
|
if self.goons != None:
|
|
for goon in self.goons:
|
|
goon.request('Off')
|
|
goon.requestDelete()
|
|
|
|
self.goons = None
|
|
self.__makeBattleThreeObjects()
|
|
return
|
|
|
|
def recordHit(self, damage):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
if not self.validate(avId, avId in self.involvedToons, 'recordHit from unknown avatar'):
|
|
return
|
|
if self.state != 'BattleThree':
|
|
return
|
|
self.b_setBossDamage(self.bossDamage + damage)
|
|
if self.bossDamage >= self.bossMaxDamage:
|
|
self.b_setState('Victory')
|
|
elif self.attackCode != ToontownGlobals.BossCogDizzy:
|
|
if damage >= ToontownGlobals.CashbotBossKnockoutDamage:
|
|
self.b_setAttackCode(ToontownGlobals.BossCogDizzy)
|
|
self.stopHelmets()
|
|
else:
|
|
self.b_setAttackCode(ToontownGlobals.BossCogNoAttack)
|
|
self.stopHelmets()
|
|
self.waitForNextHelmet()
|
|
|
|
def b_setBossDamage(self, bossDamage):
|
|
self.d_setBossDamage(bossDamage)
|
|
self.setBossDamage(bossDamage)
|
|
|
|
def setBossDamage(self, bossDamage):
|
|
self.reportToonHealth()
|
|
self.bossDamage = bossDamage
|
|
|
|
def d_setBossDamage(self, bossDamage):
|
|
self.sendUpdate('setBossDamage', [bossDamage])
|
|
|
|
def d_setRewardId(self, rewardId):
|
|
self.sendUpdate('setRewardId', [rewardId])
|
|
|
|
def applyReward(self):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
if avId in self.involvedToons and avId not in self.rewardedToons:
|
|
self.rewardedToons.append(avId)
|
|
toon = self.air.doId2do.get(avId)
|
|
if toon:
|
|
toon.doResistanceEffect(self.rewardId)
|
|
|
|
def enterOff(self):
|
|
DistributedBossCogAI.DistributedBossCogAI.enterOff(self)
|
|
self.rewardedToons = []
|
|
|
|
def exitOff(self):
|
|
DistributedBossCogAI.DistributedBossCogAI.exitOff(self)
|
|
|
|
def enterIntroduction(self):
|
|
DistributedBossCogAI.DistributedBossCogAI.enterIntroduction(self)
|
|
self.__makeBattleThreeObjects()
|
|
self.__resetBattleThreeObjects()
|
|
|
|
def exitIntroduction(self):
|
|
DistributedBossCogAI.DistributedBossCogAI.exitIntroduction(self)
|
|
self.__deleteBattleThreeObjects()
|
|
|
|
def enterPrepareBattleThree(self):
|
|
self.resetBattles()
|
|
self.__makeBattleThreeObjects()
|
|
self.__resetBattleThreeObjects()
|
|
self.barrier = self.beginBarrier('PrepareBattleThree', self.involvedToons, 55, self.__donePrepareBattleThree)
|
|
|
|
def __donePrepareBattleThree(self, avIds):
|
|
self.b_setState('BattleThree')
|
|
|
|
def exitPrepareBattleThree(self):
|
|
if self.newState != 'BattleThree':
|
|
self.__deleteBattleThreeObjects()
|
|
self.ignoreBarrier(self.barrier)
|
|
|
|
def enterBattleThree(self):
|
|
self.setPosHpr(*ToontownGlobals.CashbotBossBattleThreePosHpr)
|
|
self.__makeBattleThreeObjects()
|
|
self.__resetBattleThreeObjects()
|
|
self.reportToonHealth()
|
|
self.toonsToAttack = self.involvedToons[:]
|
|
random.shuffle(self.toonsToAttack)
|
|
self.b_setBossDamage(0)
|
|
self.battleThreeStart = globalClock.getFrameTime()
|
|
self.resetBattles()
|
|
self.waitForNextAttack(15)
|
|
self.waitForNextHelmet()
|
|
self.makeGoon(side='EmergeA')
|
|
self.makeGoon(side='EmergeB')
|
|
taskName = self.uniqueName('NextGoon')
|
|
taskMgr.remove(taskName)
|
|
taskMgr.doMethodLater(2, self.__doInitialGoons, taskName)
|
|
|
|
def __doInitialGoons(self, task):
|
|
self.makeGoon(side='EmergeA')
|
|
self.makeGoon(side='EmergeB')
|
|
self.waitForNextGoon(10)
|
|
|
|
def exitBattleThree(self):
|
|
helmetName = self.uniqueName('helmet')
|
|
taskMgr.remove(helmetName)
|
|
if self.newState != 'Victory':
|
|
self.__deleteBattleThreeObjects()
|
|
self.deleteAllTreasures()
|
|
self.stopAttacks()
|
|
self.stopGoons()
|
|
self.stopHelmets()
|
|
self.heldObject = None
|
|
return
|
|
|
|
def enterVictory(self):
|
|
self.resetBattles()
|
|
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:
|
|
toon.addResistanceMessage(self.rewardId)
|
|
toon.b_promote(self.deptIndex)
|
|
|
|
def exitVictory(self):
|
|
self.__deleteBattleThreeObjects()
|
|
|
|
def enterEpilogue(self):
|
|
DistributedBossCogAI.DistributedBossCogAI.enterEpilogue(self)
|
|
self.d_setRewardId(self.rewardId)
|
|
|
|
|
|
@magicWord(category=CATEGORY_ADMINISTRATOR)
|
|
def restartCraneRound():
|
|
"""
|
|
Restarts the crane round in the CFO.
|
|
"""
|
|
invoker = spellbook.getInvoker()
|
|
boss = None
|
|
for do in simbase.air.doId2do.values():
|
|
if isinstance(do, DistributedCashbotBossAI):
|
|
if invoker.doId in do.involvedToons:
|
|
boss = do
|
|
break
|
|
if not boss:
|
|
return "You aren't in a CFO!"
|
|
boss.exitIntroduction()
|
|
boss.b_setState('PrepareBattleThree')
|
|
boss.b_setState('BattleThree')
|
|
return 'Restarting the crane round...'
|
|
|
|
|
|
@magicWord(category=CATEGORY_ADMINISTRATOR)
|
|
def skipCFO():
|
|
"""
|
|
Skips to the final round of the CFO.
|
|
"""
|
|
invoker = spellbook.getInvoker()
|
|
boss = None
|
|
for do in simbase.air.doId2do.values():
|
|
if isinstance(do, DistributedCashbotBossAI):
|
|
if invoker.doId in do.involvedToons:
|
|
boss = do
|
|
break
|
|
if not boss:
|
|
return "You aren't in a CFO!"
|
|
if boss.state in ('PrepareBattleThree', 'BattleThree'):
|
|
return "You can't skip this round."
|
|
boss.exitIntroduction()
|
|
boss.b_setState('PrepareBattleThree')
|
|
return 'Skipping the first round...'
|
|
|
|
@magicWord(category=CATEGORY_ADMINISTRATOR)
|
|
def killCFO():
|
|
"""
|
|
Kills the CFO.
|
|
"""
|
|
invoker = spellbook.getInvoker()
|
|
boss = None
|
|
for do in simbase.air.doId2do.values():
|
|
if isinstance(do, DistributedCashbotBossAI):
|
|
if invoker.doId in do.involvedToons:
|
|
boss = do
|
|
break
|
|
if not boss:
|
|
return "You aren't in a CFO"
|
|
boss.b_setState('Victory')
|
|
return 'Killed CFO.'
|