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.'