from direct.directnotify import DirectNotifyGlobal from direct.distributed.DistributedObjectAI import DistributedObjectAI from direct.distributed.ClockDelta import * from direct.fsm import FSM from direct.task import Timer from toontown.battle import BattleBase from toontown.building.ElevatorConstants import * from toontown.toonbase.ToontownGlobals import * from toontown.toonbase.ToontownBattleGlobals import * import DistCogdoMazeGameAI, CogdoMazeGameGlobals, DistributedCogdoElevatorIntAI import DistCogdoFlyingGameAI, DistributedCogdoBarrelAI from DistributedCogdoBattleBldgAI import DistributedCogdoBattleBldgAI from SuitPlannerCogdoInteriorAI import SuitPlannerCogdoInteriorAI from toontown.cogdominium import CogdoBarrelRoomConsts from toontown.toon import NPCToons from toontown.quest import Quests import random, math NUM_FLOORS_DICT = { 's': 1, 'l': 2, 'm':1, 'c': 1 } BATTLE_INTRO_DURATION = 10 BARREL_INTRO_DURATION = 12 BARREL_ROOM_DURATION = 30 BARREL_ROOM_REWARD_DURATION = 7 class DistributedCogdoInteriorAI(DistributedObjectAI, FSM.FSM): notify = DirectNotifyGlobal.directNotify.newCategory("DistributedCogdoInteriorAI") def __init__(self, air, exterior): DistributedObjectAI.__init__(self, air) FSM.FSM.__init__(self, 'CogdoInteriorAIFSM') self.toons = filter(None, exterior.elevator.seats[:]) self.responses = {} self.bldgDoId = exterior.doId self.numFloors = NUM_FLOORS_DICT[exterior.track] self.sosNPC = self.__generateSOS(exterior.difficulty) self.shopOwnerNpcId = 0 self.extZoneId, self.zoneId = exterior.getExteriorAndInteriorZoneId() npcIdList = NPCToons.zone2NpcDict.get(self.zoneId, []) if len(npcIdList) == 0: self.notify.info('No NPC in taken cogdo at %s' % self.zoneId) else: if len(npcIdList) > 1: self.notify.warning('Multiple NPCs in taken cogdo at %s' % self.zoneId) self.shopOwnerNpcId = npcIdList[0] self.gameDone = 0 self.bossBattleDone = 0 self.curFloor = 0 self.topFloor = 2 self.timer = Timer.Timer() self.exterior = exterior self.planner = self.exterior.planner self.savedByMap = { } self.battle = None self.FOType = exterior.track self.gameFloor = 1 self.battleFloor = 2 self.barrelFloor = -1 if self.FOType == 'l': self.battleFloor = 3 self.barrelFloor = 2 self.topFloor += 1 self.toonSkillPtsGained = { } self.toonExp = { } self.toonOrigQuests = { } self.toonItems = { } self.toonOrigMerits = { } self.toonMerits = { } self.toonParts = { } self.helpfulToons = [] self.barrels = [] self.suits = [] self.activeSuits = [] self.reserveSuits = [] self.joinedReserves = [] self.suitsKilled = [] self.suitsKilledPerFloor = [] self.ignoreResponses = 0 self.ignoreElevatorDone = 0 self.ignoreReserveJoinDone = 0 def __generateSOS(self, difficulty): g = lambda: random.choice(NPCToons.FOnpcFriends.keys()) v = g() getStars = lambda x: NPCToons.getNPCTrackLevelHpRarity(x)[-1] maxStars = min(2, int(math.ceil(difficulty / 5.))) minStars = max(0, maxStars - 1) while not (minStars <= getStars(v) <= maxStars): v = g() self.notify.info('selected SOS %s (stars = %s)' % (v, getStars(v))) return v def setZoneId(self, zoneId): self.zoneId = zoneId def getZoneId(self): return self.zoneId def setExtZoneId(self, extZoneId): self.extZoneId = extZoneId def getExtZoneId(self): return self.extZoneId def setDistBldgDoId(self, bldgDoId): self.bldgDoId = bldgDoId def getDistBldgDoId(self): return self.bldgDoId def setNumFloors(self, numFloors): self.numFloors = numFloors def getNumFloors(self): return self.numFloors def setShopOwnerNpcId(self, id): self.shopOwnerNpcId = id def getShopOwnerNpcId(self): return self.shopOwnerNpcId def setState(self, state, timestamp): self.request(state) def getState(self): timestamp = globalClockDelta.getRealNetworkTime() return [self.state, timestamp] def b_setState(self, state): self.setState(state, 0) self.d_setState(state) def d_setState(self, state): timestamp = globalClockDelta.getRealNetworkTime() self.sendUpdate('setState', [state, timestamp]) def reserveJoinDone(self): toonId = self.air.getAvatarIdFromSender() if self.ignoreResponses == 1: return None elif self.toons.count(toonId) == 0: self.notify.warning('reserveJoinDone() - toon not in list: %d' % toonId) return None self.b_setState('Battle') def elevatorDone(self): toonId = self.air.getAvatarIdFromSender() if self.ignoreResponses == 1: return None elif self.toons.count(toonId) == 0: self.notify.warning('elevatorDone() - toon not in toon list: %d' % toonId) def enterWaitForAllToonsInside(self): self.resetResponses() if self.FOType == "s": self.game = DistCogdoMazeGameAI.DistCogdoMazeGameAI(self.air) self.game.setNumSuits(CogdoMazeGameGlobals.NumSuits) elif self.FOType == "l": self.game = DistCogdoFlyingGameAI.DistCogdoFlyingGameAI(self.air) elif self.FOType == "m": self.game = DistCogdoCraneGameAI.DistCogdoCraneGameAI(self.air) self.sendUpdate("setSOSNpcId", [self.sosNPC]) self.sendUpdate("setFOType", [ord(self.FOType)]) def resetResponses(self): for toon in self.toons: self.responses[toon] = 0 def setAvatarJoined(self): avId = self.air.getAvatarIdFromSender() self.responses[avId] = 1 avatar = self.air.doId2do.get(avId) if avatar != None: self.savedByMap[avId] = (avatar.getName(), avatar.dna.asTuple()) self.addToon(avId) if self.allToonsJoined(): self.request('Elevator') def addToon(self, avId): if not avId in self.toons: self.toons.append(avId) if self.air.doId2do.has_key(avId): event = self.air.getAvatarExitEvent(avId) self.accept(event, self.__handleUnexpectedExit, [avId]) def __handleUnexpectedExit(self, avId): self.removeToon(avId) if len(self.toons) == 0: self.exterior.deleteSuitInterior() if self.battle: self.battle.requestDelete() self.battle = None def removeToon(self, avId): if avId in self.toons: self.toons.pop(avId) def enterElevator(self): self.curFloor += 1 self.d_setToons() self.resetResponses() if self.curFloor == self.gameFloor: self.enterGame() self.d_setState('Elevator') self.timer.stop() self.timer.startCallback(BattleBase.ELEVATOR_T + ElevatorData[ELEVATOR_FIELD]['openTime'], self.serverElevatorDone) if self.curFloor == self.battleFloor: self.planner.myPrint() suitHandles = self.planner.genFloorSuits(0) self.suits = suitHandles['activeSuits'] self.activeSuits = self.suits[:] self.reserveSuits = suitHandles['reserveSuits'] self.d_setSuits() def exitElevator(self): self.timer.stop() def serverElevatorDone(self): if self.curFloor == self.gameFloor: self.d_setState('Game') elif self.curFloor == self.battleFloor: self.b_setState('BattleIntro') self.timer.startCallback(BATTLE_INTRO_DURATION, self.battleIntroDone) else: self.notify.warning('Unknown floor %s (track=%s)' % (self.curFloor, self.FOType)) def battleIntroDone(self): if self.air: self.createBattle() self.b_setState('Battle') def barrelIntroDone(self): if not self.air: return self.b_setState('CollectBarrels') for i in xrange(len(CogdoBarrelRoomConsts.BarrelProps)): barrel = DistributedCogdoBarrelAI.DistributedCogdoBarrelAI(self.air, i) barrel.generateWithRequired(self.zoneId) self.barrels.append(barrel) self.timer.startCallback(BARREL_ROOM_DURATION, self.barrelReward) def barrelReward(self): if not self.air: return self.b_setState('BarrelRoomReward') for i in self.barrels: i.requestDelete() self.timer.startCallback(BARREL_ROOM_REWARD_DURATION, self.barrelRewardDone) def barrelRewardDone(self): if not self.air: return barrelPlanner = SuitPlannerCogdoInteriorAI(self.exterior._cogdoLayout, max(0, self.exterior.difficulty - 5), self.FOType, self.exterior.getExteriorAndInteriorZoneId()[1]) barrelPlanner.myPrint() suitHandles = barrelPlanner.genFloorSuits(0) self.suits = suitHandles['activeSuits'] self.activeSuits = self.suits[:] self.reserveSuits = suitHandles['reserveSuits'] self.d_setSuits() self.battleIntroDone() def handleAllAboard(self, seats): if not hasattr(self, 'air') or not self.air: return None numOfEmptySeats = seats.count(None) if numOfEmptySeats == 4: self.exterior.deleteSuitInterior() return elif not 0 <= numOfEmptySeats <= 3: self.notify.error('Bad number of empty seats: %s' % numOfEmptySeats) for toon in self.toons: if toon not in seats: self.removeToon(toon) self.toons = filter(None, seats) self.d_setToons() self.request('Elevator') def enterGame(self): self.game.setToons(self.toons) self.game.setInteriorId(self.doId) self.game.setExteriorZone(self.exterior.zoneId) self.game.setDifficultyOverrides(2147483647, -1) self.game.generateWithRequired(self.zoneId) self.game.d_startIntro() self.accept(self.game.finishEvent, self.__handleGameDone) self.accept(self.game.gameOverEvent, self.__handleGameOver) def __handleGameDone(self, toons): self.game.requestDelete() self.gameDone = 1 self.toons = toons if self.curFloor == self.barrelFloor - 1: self.curFloor += 1 self.d_setToons() self.resetResponses() self.b_setState('BarrelRoomIntro') self.timer.startCallback(BARREL_INTRO_DURATION, self.barrelIntroDone) else: self.request('Elevator') def __handleGameOver(self): self.game.requestDelete() self.exterior.deleteSuitInterior() def createBattle(self): isBoss = self.curFloor == self.topFloor self.battle = DistributedCogdoBattleBldgAI(self.air, self.zoneId, self.__handleRoundDone, self.__handleBattleDone, bossBattle = isBoss) self.battle.suitsKilled = self.suitsKilled self.battle.suitsKilledPerFloor = self.suitsKilledPerFloor self.battle.battleCalc.toonSkillPtsGained = self.toonSkillPtsGained self.battle.toonExp = self.toonExp self.battle.toonOrigQuests = self.toonOrigQuests self.battle.toonItems = self.toonItems self.battle.toonOrigMerits = self.toonOrigMerits self.battle.toonMerits = self.toonMerits self.battle.toonParts = self.toonParts self.battle.helpfulToons = self.helpfulToons self.battle.setInitialMembers(self.toons, self.suits) self.battle.generateWithRequired(self.zoneId) mult = getCreditMultiplier(self.curFloor) self.battle.battleCalc.setSkillCreditMultiplier(self.battle.battleCalc.getSkillCreditMultiplier() * mult) def enterBattleDone(self, toonIds): toonIds = toonIds[0] if len(toonIds) != len(self.toons): deadToons = [] for toon in self.toons: if toonIds.count(toon) == 0: deadToons.append(toon) continue for toon in deadToons: self.removeToon(toon) self.d_setToons() if len(self.toons) == 0: self.exterior.deleteSuitInterior() elif self.curFloor == self.topFloor: self.battle.resume(self.curFloor, topFloor = 1) else: self.battle.resume(self.curFloor, topFloor = 0) def __doDeleteInterior(self, task): self.exterior.deleteSuitInterior() return task.done def exitBattleDone(self): self.cleanupFloorBattle() def cleanupFloorBattle(self): for suit in self.suits: if suit.isDeleted(): continue suit.requestDelete() self.suits = [] self.reserveSuits = [] self.activeSuits = [] if self.battle != None: self.battle.requestDelete() self.battle = None def __handleRoundDone(self, toonIds, totalHp, deadSuits): totalMaxHp = 0 for suit in self.suits: totalMaxHp += suit.maxHP for suit in deadSuits: self.activeSuits.remove(suit) if len(self.reserveSuits) > 0 and len(self.activeSuits) < 4: self.joinedReserves = [] hpPercent = 100 - (totalHp / totalMaxHp) * 100.0 for info in self.reserveSuits: if info[1] <= hpPercent and len(self.activeSuits) < 4: self.suits.append(info[0]) self.activeSuits.append(info[0]) self.joinedReserves.append(info) continue for info in self.joinedReserves: self.reserveSuits.remove(info) if len(self.joinedReserves) > 0: self.d_setSuits() self.request('ReservesJoining') return if len(self.activeSuits) == 0: self.request('BattleDone', [ toonIds]) else: self.battle.resume() def enterReservesJoining(self): self.resetResponses() self.timer.startCallback(ElevatorData[ELEVATOR_FIELD]['openTime'] + SUIT_HOLD_ELEVATOR_TIME + BattleBase.SERVER_BUFFER_TIME, self.serverReserveJoinDone) def exitReservesJoining(self): self.timer.stop() self.resetResponses() for info in self.joinedReserves: self.battle.suitRequestJoin(info[0]) self.battle.resume() self.joinedReserves = [] def serverReserveJoinDone(self): self.ignoreReserveJoinDone = 1 self.b_setState('Battle') def __handleBattleDone(self, zoneId, toonIds): if len(toonIds) == 0: taskMgr.doMethodLater(10, self.__doDeleteInterior, self.taskName('deleteInterior')) elif self.curFloor == self.topFloor: self.request('Reward') else: self.b_setState('Resting') def enterResting(self): self.intElevator = DistributedCogdoElevatorIntAI.DistributedCogdoElevatorIntAI(self.air, self, self.toons) self.intElevator.generateWithRequired(self.zoneId) def exitResting(self): self.intElevator.requestDelete() def enterReward(self): victors = self.toons[:] savedBy = [] for v in victors: tuple = self.savedByMap.get(v) if tuple: savedBy.append([ v, tuple[0], tuple[1]]) toon = self.air.doId2do.get(v) if toon: if self.FOType == 's': if not toon.attemptAddNPCFriend(self.sosNPC, Quests.InFO): self.notify.info('%s unable to add NPCFriend %s to %s.' % (self.doId, self.sosNPC, v)) elif self.FOType == 'l': reward = self.getEmblemsReward() toon.addEmblems(reward) else: self.notify.warning('%s unable to reward %s: unknown reward for track %s' % (self.doId, v, self.FOType)) self.exterior.fsm.request('waitForVictorsFromCogdo', [ victors, savedBy]) self.d_setState('Reward') def removeToon(self, toonId): if self.toons.count(toonId): self.toons.remove(toonId) def d_setToons(self): self.sendUpdate('setToons', self.getToons()) def getToons(self): return [self.toons, 0] def d_setSuits(self): self.sendUpdate('setSuits', self.getSuits()) def getSuits(self): suitIds = [] for suit in self.activeSuits: suitIds.append(suit.doId) reserveIds = [] values = [] for info in self.reserveSuits: reserveIds.append(info[0].doId) values.append(info[1]) return [ suitIds, reserveIds, values] def allToonsJoined(self): for toon in self.toons: if self.responses[toon] == 0: return 0 return 1 def delete(self): DistributedObjectAI.delete(self) self.timer.stop() def getEmblemsReward(self): hoodIdMap = {2: .5, # Toontown Central 1: 1., # Donald's Dock 5: 1.5, # Daisy Gardens 4: 2., # Minnie's Melodyland 3: 2.7, # The Brrrgh 9: 3.5 # Donald's Dreamland } hoodValue = hoodIdMap[int(self.exterior.zoneId // 1000)] diff = max(self.exterior.difficulty, 1) memos = self.game.getTotalMemos() E = (hoodValue * max(memos, 1) * diff) / 2.5 return divmod(E, 100)[::-1]