import copy import DistributedElevatorIntAI from ElevatorConstants import * from direct.directnotify import DirectNotifyGlobal from direct.distributed import DistributedObjectAI from direct.distributed.ClockDelta import * from direct.fsm import ClassicFSM, State from direct.fsm import State from direct.task import Timer from otp.ai.AIBaseGlobal import * from toontown.battle import BattleBase from toontown.battle import DistributedBattleBldgAI from toontown.toonbase.ToontownBattleGlobals import * class DistributedSuitInteriorAI(DistributedObjectAI.DistributedObjectAI): def __init__(self, air, elevator): self.air = air DistributedObjectAI.DistributedObjectAI.__init__(self, air) (self.extZoneId, self.zoneId) = elevator.bldg.getExteriorAndInteriorZoneId() self.numFloors = elevator.bldg.planner.numFloors self.avatarExitEvents = [] self.toons = [] self.toonSkillPtsGained = {} self.toonExp = {} self.toonOrigQuests = {} self.toonItems = {} self.toonOrigMerits = {} self.toonMerits = {} self.toonParts = {} self.helpfulToons = [] self.currentFloor = 0 self.topFloor = self.numFloors - 1 self.bldg = elevator.bldg self.elevator = elevator self.suits = [] self.activeSuits = [] self.reserveSuits = [] self.joinedReserves = [] self.suitsKilled = [] self.suitsKilledPerFloor = [] self.battle = None self.timer = Timer.Timer() self.responses = {} self.ignoreResponses = 0 self.ignoreElevatorDone = 0 self.ignoreReserveJoinDone = 0 self.toonIds = copy.copy(elevator.seats) for toonId in self.toonIds: if toonId is not None: self.__addToon(toonId) self.savedByMap = {} self.fsm = ClassicFSM.ClassicFSM( 'DistributedSuitInteriorAI', [ State.State('WaitForAllToonsInside', self.enterWaitForAllToonsInside, self.exitWaitForAllToonsInside, ['Elevator']), State.State('Elevator', self.enterElevator, self.exitElevator, ['Battle']), State.State('Battle', self.enterBattle, self.exitBattle, ['ReservesJoining', 'BattleDone']), State.State('ReservesJoining', self.enterReservesJoining, self.exitReservesJoining, ['Battle']), State.State('BattleDone', self.enterBattleDone, self.exitBattleDone, ['Resting', 'Reward']), State.State('Resting', self.enterResting, self.exitResting, ['Elevator']), State.State('Reward', self.enterReward, self.exitReward, ['Off']), State.State('Off', self.enterOff, self.exitOff, ['WaitForAllToonsInside']) ], 'Off', 'Off', onUndefTransition=ClassicFSM.ClassicFSM.ALLOW) self.fsm.enterInitialState() def delete(self): self.ignoreAll() self.toons = [] self.toonIds = [] self.fsm.requestFinalState() del self.fsm del self.bldg del self.elevator self.timer.stop() del self.timer self.__cleanupFloorBattle() taskName = self.taskName('deleteInterior') taskMgr.remove(taskName) DistributedObjectAI.DistributedObjectAI.delete(self) def __handleUnexpectedExit(self, toonId): self.notify.warning('toon: %d exited unexpectedly' % toonId) self.__removeToon(toonId) if len(self.toons) == 0: self.timer.stop() if self.fsm.getCurrentState().getName() == 'Resting': return elif self.battle is None: self.bldg.deleteSuitInterior() def __addToon(self, toonId): if toonId not in self.air.doId2do: self.notify.warning('addToon() - no toon for doId: %d' % toonId) return event = self.air.getAvatarExitEvent(toonId) self.avatarExitEvents.append(event) self.accept(event, self.__handleUnexpectedExit, extraArgs=[toonId]) self.toons.append(toonId) self.responses[toonId] = 0 def __removeToon(self, toonId): if toonId in self.toons: self.toons.remove(toonId) if toonId in self.toonIds: self.toonIds[self.toonIds.index(toonId)] = None if toonId in self.responses: del self.responses[toonId] event = self.air.getAvatarExitEvent(toonId) if self.avatarExitEvents.count(event): self.avatarExitEvents.remove(event) self.ignore(event) def __resetResponses(self): self.responses = {} for toon in self.toons: self.responses[toon] = 0 self.ignoreResponses = 0 def __allToonsResponded(self): for toon in self.toons: if self.responses[toon] == 0: return 0 self.ignoreResponses = 1 return 1 def getZoneId(self): return self.zoneId def getExtZoneId(self): return self.extZoneId def getDistBldgDoId(self): return self.bldg.getDoId() def getNumFloors(self): return self.numFloors def d_setToons(self): self.sendUpdate('setToons', self.getToons()) def getToons(self): sendIds = [] for toonId in self.toonIds: if toonId is None: sendIds.append(0) else: sendIds.append(toonId) return [sendIds, 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 b_setState(self, state): self.d_setState(state) self.setState(state) def d_setState(self, state): stime = globalClock.getRealTime() + BattleBase.SERVER_BUFFER_TIME self.sendUpdate('setState', [ state, globalClockDelta.localToNetworkTime(stime)]) def setState(self, state): self.fsm.request(state) def getState(self): return [self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()] def setAvatarJoined(self): avId = self.air.getAvatarIdFromSender() if self.toons.count(avId) == 0: self.air.writeServerEvent('suspicious', avId, 'DistributedSuitInteriorAI.setAvatarJoined from toon not in %s.' % self.toons) self.notify.warning('setAvatarJoined() - av: %d not in list' % avId) return avatar = self.air.doId2do.get(avId) if avatar is not None: self.savedByMap[avId] = (avatar.getName(), avatar.dna.asTuple()) self.responses[avId] += 1 if self.__allToonsResponded(): self.fsm.request('Elevator') def elevatorDone(self): toonId = self.air.getAvatarIdFromSender() if self.ignoreResponses == 1: return elif self.fsm.getCurrentState().getName() != 'Elevator': self.notify.warning('elevatorDone() - in state: %s' % self.fsm.getCurrentState().getName()) return elif self.toons.count(toonId) == 0: self.notify.warning('elevatorDone() - toon not in toon list: %d' % toonId) return self.responses[toonId] += 1 if self.__allToonsResponded() and self.ignoreElevatorDone == 0: self.b_setState('Battle') def reserveJoinDone(self): toonId = self.air.getAvatarIdFromSender() if self.ignoreResponses == 1: return elif self.fsm.getCurrentState().getName() != 'ReservesJoining': self.notify.warning('reserveJoinDone() - in state: %s' % self.fsm.getCurrentState().getName()) return elif self.toons.count(toonId) == 0: self.notify.warning('reserveJoinDone() - toon not in list: %d' % toonId) return self.responses[toonId] += 1 if self.__allToonsResponded() and self.ignoreReserveJoinDone == 0: self.b_setState('Battle') def enterOff(self): pass def exitOff(self): pass def enterWaitForAllToonsInside(self): self.__resetResponses() def exitWaitForAllToonsInside(self): self.__resetResponses() def enterElevator(self): suitHandles = self.bldg.planner.genFloorSuits(self.currentFloor) self.suits = suitHandles['activeSuits'] self.activeSuits = [] for suit in self.suits: self.activeSuits.append(suit) self.reserveSuits = suitHandles['reserveSuits'] self.d_setToons() self.d_setSuits() self.__resetResponses() self.d_setState('Elevator') self.timer.startCallback(BattleBase.ELEVATOR_T + ElevatorData[ELEVATOR_NORMAL]['openTime'] + BattleBase.SERVER_BUFFER_TIME, self.__serverElevatorDone) def __serverElevatorDone(self): self.ignoreElevatorDone = 1 self.b_setState('Battle') def exitElevator(self): self.timer.stop() self.__resetResponses() def __createFloorBattle(self): if self.currentFloor == self.topFloor: bossBattle = 1 else: bossBattle = 0 self.battle = DistributedBattleBldgAI.DistributedBattleBldgAI(self.air, self.zoneId, self.__handleRoundDone, self.__handleBattleDone, bossBattle = bossBattle) 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.currentFloor) if self.air.suitInvasionManager.getInvading(): mult *= getInvasionMultiplier() self.battle.battleCalc.setSkillCreditMultiplier(mult) def __cleanupFloorBattle(self): for suit in self.suits: self.notify.debug('cleaning up floor suit: %d' % suit.doId) if suit.isDeleted(): self.notify.debug('whoops, suit %d is deleted.' % suit.doId) 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.fsm.request('ReservesJoining') self.d_setSuits() return if len(self.activeSuits) == 0: self.fsm.request('BattleDone', [toonIds]) else: self.battle.resume() def __handleBattleDone(self, zoneId, toonIds): if len(toonIds) == 0: taskName = self.taskName('deleteInterior') taskMgr.doMethodLater(10, self.__doDeleteInterior, taskName) elif self.currentFloor == self.topFloor: self.setState('Reward') else: self.b_setState('Resting') def __doDeleteInterior(self, task): self.bldg.deleteSuitInterior() def enterBattle(self): if self.battle is None: self.__createFloorBattle() self.elevator.d_setFloor(self.currentFloor) def exitBattle(self): pass def enterReservesJoining(self): self.__resetResponses() self.timer.startCallback(ElevatorData[ELEVATOR_NORMAL]['openTime'] + SUIT_HOLD_ELEVATOR_TIME + BattleBase.SERVER_BUFFER_TIME, self.__serverReserveJoinDone) def __serverReserveJoinDone(self): self.ignoreReserveJoinDone = 1 self.b_setState('Battle') def exitReservesJoining(self): self.timer.stop() self.__resetResponses() for info in self.joinedReserves: self.battle.suitRequestJoin(info[0]) self.battle.resume() self.joinedReserves = [] def enterBattleDone(self, toonIds): 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.bldg.deleteSuitInterior() elif self.currentFloor == self.topFloor: self.battle.resume(self.currentFloor, topFloor = 1) else: self.battle.resume(self.currentFloor, topFloor = 0) def exitBattleDone(self): self.__cleanupFloorBattle() def __handleEnterElevator(self): self.fsm.request('Elevator') def enterResting(self): self.intElevator = DistributedElevatorIntAI.DistributedElevatorIntAI(self.air, self, self.toons) self.intElevator.generateWithRequired(self.zoneId) def handleAllAboard(self, seats): if not hasattr(self, 'fsm'): return numOfEmptySeats = seats.count(None) if numOfEmptySeats == 4: self.bldg.deleteSuitInterior() return elif numOfEmptySeats >= 0 and numOfEmptySeats <= 3: pass else: self.error('Bad number of empty seats: %s' % numOfEmptySeats) for toon in self.toons: if seats.count(toon) == 0: self.__removeToon(toon) continue self.toonIds = copy.copy(seats) self.toons = [] for toonId in self.toonIds: if toonId != None: self.toons.append(toonId) continue self.d_setToons() self.currentFloor += 1 self.fsm.request('Elevator') def exitResting(self): self.intElevator.requestDelete() del self.intElevator def enterReward(self): victors = self.toonIds[:] savedBy = [] for v in victors: tuple = self.savedByMap.get(v) if tuple: savedBy.append([v, tuple[0], tuple[1]]) continue self.bldg.fsm.request('waitForVictors', [victors, savedBy]) self.d_setState('Reward') def exitReward(self): pass