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 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): 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]