From a338b886f5994e141f25fe43a90581d06d639820 Mon Sep 17 00:00:00 2001 From: Open Toontown <57279094+opentoontown@users.noreply.github.com> Date: Sat, 17 Dec 2022 13:26:45 -0500 Subject: [PATCH] general: Progress? --- etc/otp.dc | 4 +- otp/distributed/OTPClientRepository.py | 2 +- toontown/building/DistributedBuildingAI.py | 768 +++++++++++++++------ 3 files changed, 565 insertions(+), 209 deletions(-) diff --git a/etc/otp.dc b/etc/otp.dc index 0957765..acdda10 100755 --- a/etc/otp.dc +++ b/etc/otp.dc @@ -41,8 +41,8 @@ struct AvatarPendingDel { dclass Account { string DcObjectType db; - uint32array ACCOUNT_AV_SET required db; - uint32array pirateAvatars required db; + uint32array ACCOUNT_AV_SET = [0,0,0,0,0,0] required db; + uint32array pirateAvatars = [0,0,0,0,0,0] required db; uint32array HOUSE_ID_SET db; uint32 ESTATE_ID db; AvatarPendingDel ACCOUNT_AV_SET_DEL[] db; diff --git a/otp/distributed/OTPClientRepository.py b/otp/distributed/OTPClientRepository.py index efde522..2d00b49 100644 --- a/otp/distributed/OTPClientRepository.py +++ b/otp/distributed/OTPClientRepository.py @@ -1001,7 +1001,7 @@ class OTPClientRepository(ClientRepositoryBase): datagram = PyDatagram() datagram.addUint16(CLIENT_CREATE_AVATAR) datagram.addUint16(0) - datagram.addString(avDNA.makeNetString()) + datagram.addBlob(avDNA.makeNetString()) datagram.addUint8(avPosition) self.newName = avName self.newDNA = avDNA diff --git a/toontown/building/DistributedBuildingAI.py b/toontown/building/DistributedBuildingAI.py index 2f675c6..56e6ebe 100644 --- a/toontown/building/DistributedBuildingAI.py +++ b/toontown/building/DistributedBuildingAI.py @@ -1,5 +1,10 @@ +""" DistributedBuildingAI module: contains the DistributedBuildingAI + class, the server side representation of a 'building'.""" + + from otp.ai.AIBaseGlobal import * from direct.distributed.ClockDelta import * + import types from direct.task.Task import Task from direct.directnotify import DirectNotifyGlobal @@ -7,135 +12,256 @@ from direct.distributed import DistributedObjectAI from direct.fsm import State from direct.fsm import ClassicFSM, State from toontown.toonbase.ToontownGlobals import ToonHall -from . import DistributedToonInteriorAI, DistributedToonHallInteriorAI, DistributedSuitInteriorAI, DistributedDoorAI, DoorTypes, DistributedElevatorExtAI, DistributedKnockKnockDoorAI, SuitPlannerInteriorAI, SuitBuildingGlobals, FADoorCodes +from . import DistributedToonInteriorAI +from . import DistributedToonHallInteriorAI +from . import DistributedSuitInteriorAI +from . import DistributedDoorAI +from . import DoorTypes +from . import DistributedElevatorExtAI +from . import DistributedKnockKnockDoorAI +from . import SuitPlannerInteriorAI +from . import SuitBuildingGlobals +from . import FADoorCodes from toontown.hood import ZoneUtil -import random, time +import random +import time from toontown.cogdominium.DistributedCogdoInteriorAI import DistributedCogdoInteriorAI from toontown.cogdominium.SuitPlannerCogdoInteriorAI import SuitPlannerCogdoInteriorAI from toontown.cogdominium.CogdoLayout import CogdoLayout from toontown.cogdominium.DistributedCogdoElevatorExtAI import DistributedCogdoElevatorExtAI class DistributedBuildingAI(DistributedObjectAI.DistributedObjectAI): + """ + DistributedBuildingAI class: The server side representation of a + single building. This is the object that remember who 'owns' the + associated building (either the bad guys or the toons). The child + of this object, the DistributedBuilding object, is the client side + version and updates the display that client's display based on who + 'owns' the building. + """ + + if __debug__: + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBuildingAI') + FieldOfficeNumFloors = 1 def __init__(self, air, blockNumber, zoneId, trophyMgr): + """blockNumber: the landmark building number (from the name)""" DistributedObjectAI.DistributedObjectAI.__init__(self, air) self.block = blockNumber + assert(self.debugPrint("DistributedBuildingAI(%s, %s)" % ("the air", str(blockNumber)))) self.zoneId = zoneId self.canonicalZoneId = ZoneUtil.getCanonicalZoneId(zoneId) self.trophyMgr = trophyMgr self.victorResponses = None - self.fsm = ClassicFSM.ClassicFSM('DistributedBuildingAI', [ - State.State('off', self.enterOff, self.exitOff, [ - 'waitForVictors', 'becomingToon', 'toon', 'clearOutToonInterior', 'becomingSuit', 'suit', 'clearOutToonInteriorForCogdo', 'becomingCogdo', 'becomingCogdoFromCogdo', 'cogdo']), - State.State('waitForVictors', self.enterWaitForVictors, self.exitWaitForVictors, [ - 'becomingToon']), - State.State('waitForVictorsFromCogdo', self.enterWaitForVictorsFromCogdo, self.exitWaitForVictorsFromCogdo, [ - 'becomingToonFromCogdo', 'becomingCogdoFromCogdo']), - State.State('becomingToon', self.enterBecomingToon, self.exitBecomingToon, [ - 'toon']), - State.State('becomingToonFromCogdo', self.enterBecomingToonFromCogdo, self.exitBecomingToonFromCogdo, [ - 'toon']), - State.State('toon', self.enterToon, self.exitToon, [ - 'clearOutToonInterior', 'clearOutToonInteriorForCogdo']), - State.State('clearOutToonInterior', self.enterClearOutToonInterior, self.exitClearOutToonInterior, [ - 'becomingSuit']), - State.State('becomingSuit', self.enterBecomingSuit, self.exitBecomingSuit, [ - 'suit']), - State.State('suit', self.enterSuit, self.exitSuit, [ - 'waitForVictors', 'becomingToon']), - State.State('clearOutToonInteriorForCogdo', self.enterClearOutToonInteriorForCogdo, self.exitClearOutToonInteriorForCogdo, [ - 'becomingCogdo']), - State.State('becomingCogdo', self.enterBecomingCogdo, self.exitBecomingCogdo, [ - 'cogdo']), - State.State('becomingCogdoFromCogdo', self.enterBecomingCogdoFromCogdo, self.exitBecomingCogdoFromCogdo, [ - 'cogdo']), - State.State('cogdo', self.enterCogdo, self.exitCogdo, [ - 'waitForVictorsFromCogdo', 'becomingToonFromCogdo'])], 'off', 'off') + self.fsm = ClassicFSM.ClassicFSM('DistributedBuildingAI', + [State.State('off', + self.enterOff, + self.exitOff, + ['waitForVictors', + 'becomingToon', + 'toon', + 'clearOutToonInterior', + 'becomingSuit', + 'suit', + 'clearOutToonInteriorForCogdo', + 'becomingCogdo', + 'becomingCogdoFromCogdo', + 'cogdo', + ]), + State.State('waitForVictors', + self.enterWaitForVictors, + self.exitWaitForVictors, + ['becomingToon', + ]), + State.State('waitForVictorsFromCogdo', + self.enterWaitForVictorsFromCogdo, + self.exitWaitForVictorsFromCogdo, + ['becomingToonFromCogdo', + 'becomingCogdoFromCogdo' + ]), + State.State('becomingToon', + self.enterBecomingToon, + self.exitBecomingToon, + ['toon']), + State.State('becomingToonFromCogdo', + self.enterBecomingToonFromCogdo, + self.exitBecomingToonFromCogdo, + ['toon']), + State.State('toon', + self.enterToon, + self.exitToon, + ['clearOutToonInterior', 'clearOutToonInteriorForCogdo']), + State.State('clearOutToonInterior', + self.enterClearOutToonInterior, + self.exitClearOutToonInterior, + ['becomingSuit']), + State.State('becomingSuit', + self.enterBecomingSuit, + self.exitBecomingSuit, + ['suit']), + State.State('suit', + self.enterSuit, + self.exitSuit, + ['waitForVictors', + 'becomingToon', # debug only + ]), + State.State('clearOutToonInteriorForCogdo', + self.enterClearOutToonInteriorForCogdo, + self.exitClearOutToonInteriorForCogdo, + ['becomingCogdo']), + State.State('becomingCogdo', + self.enterBecomingCogdo, + self.exitBecomingCogdo, + ['cogdo']), + State.State('becomingCogdoFromCogdo', + self.enterBecomingCogdoFromCogdo, + self.exitBecomingCogdoFromCogdo, + ['cogdo']), + State.State('cogdo', + self.enterCogdo, + self.exitCogdo, + ['waitForVictorsFromCogdo', + 'becomingToonFromCogdo', # debug only + ])], + # Initial State + 'off', + # Final State + 'off', + ) self.fsm.enterInitialState() - self.track = 'c' - self.difficulty = 1 - self.numFloors = 0 - self.savedBy = [] - self.becameSuitTime = 0 - self.frontDoorPoint = None - self.suitPlannerExt = None + self.track='c' + self.difficulty=1 + self.numFloors=0 + self.savedBy=None + self.becameSuitTime=0 + self.frontDoorPoint=None + self.suitPlannerExt=None self.fSkipElevatorOpening = False - return def cleanup(self): if self.isDeleted(): return + self.fsm.requestFinalState() - if hasattr(self, 'interior'): + + if hasattr(self, "interior"): self.interior.requestDelete() del self.interior - if hasattr(self, 'door'): + + if hasattr(self, "door"): self.door.requestDelete() del self.door self.insideDoor.requestDelete() del self.insideDoor self.knockKnock.requestDelete() del self.knockKnock - if hasattr(self, 'elevator'): + + if hasattr(self, "elevator"): self.elevator.requestDelete() del self.elevator - self.requestDelete() - def delete(self): + self.requestDelete() + + + def delete( self ): + """ + //////////////////////////////////////////////////////////////////// + // Function: clean up tasks that might still be running for this + // building + // Parameters: + // Changes: + //////////////////////////////////////////////////////////////////// + """ + # make sure to remove any tasks we might have created taskMgr.remove(self.taskName('suitbldg-time-out')) + + # remove the doLater associated with the state transition of + # a suit building becoming a toon building taskMgr.remove(self.taskName(str(self.block) + '_becomingToon-timer')) + + # remove the doLater associated with the state transition of + # a toon building becoming a suit building taskMgr.remove(self.taskName(str(self.block) + '_becomingSuit-timer')) + DistributedObjectAI.DistributedObjectAI.delete(self) + del self.fsm - def getBuildingData(self): - buildingData = { + def getPickleData(self): + assert(self.debugPrint("getPickleData()")) + pickleData={ 'state': str(self.fsm.getCurrentState().getName()), 'block': str(self.block), 'track': str(self.track), 'difficulty': str(self.difficulty), 'numFloors': str(self.numFloors), 'savedBy': self.savedBy, - 'becameSuitTime': self.becameSuitTime + 'becameSuitTime' : self.becameSuitTime, } - return buildingData + return pickleData def _getMinMaxFloors(self, difficulty): return SuitBuildingGlobals.SuitBuildingInfo[difficulty][0] - + def suitTakeOver(self, suitTrack, difficulty, buildingHeight): + """Switch from toon to suit building + suitTrack: one of 'c', 'l', 'm', or 's' + difficulty: 0+ + buildingHeight: 0..4, or None to choose based on the difficulty. + """ if not self.isToonBlock(): return - self.updateSavedBy([]) + assert(suitTrack in ['c', 'l', 'm', 's']) + + # Remove the old saved by credit with the old number of floors + self.updateSavedBy(None) + difficulty = min(difficulty, len(SuitBuildingGlobals.SuitBuildingInfo) - 1) minFloors, maxFloors = self._getMinMaxFloors(difficulty) if buildingHeight == None: + # Pick a random floor number from the appropriate range. numFloors = random.randint(minFloors, maxFloors) else: + # The number of floors is specified. numFloors = buildingHeight + 1 - if numFloors < minFloors or numFloors > maxFloors: + + if (numFloors < minFloors or numFloors > maxFloors): + # Hmm, the number of floors is out of range for this + # suit. There must be an invasion in effect. In that + # case, go ahead and make a building of any height + # appropriate to the suit. numFloors = random.randint(minFloors, maxFloors) - self.track = suitTrack - self.difficulty = difficulty - self.numFloors = numFloors + + assert(self.debugPrint("suitTakeOver(%s, %s, %s)" % (suitTrack, difficulty, numFloors - 1))) + + self.track=suitTrack + self.difficulty=difficulty + self.numFloors=numFloors self.becameSuitTime = time.time() self.fsm.request('clearOutToonInterior') - return def cogdoTakeOver(self, suitTrack, difficulty, buildingHeight): if not self.isToonBlock(): return - self.updateSavedBy([]) + + # Remove the old saved by credit with the old number of floors + self.updateSavedBy(None) + numFloors = self.FieldOfficeNumFloors + + assert(self.debugPrint("cogdoTakeOver(%s, %s)" % (difficulty, numFloors - 1))) + self.track = suitTrack - self.difficulty = difficulty - self.numFloors = numFloors + self.difficulty=difficulty + self.numFloors=numFloors self.becameSuitTime = time.time() self.fsm.request('clearOutToonInteriorForCogdo') - return def toonTakeOver(self): + """Switch from suit to toon building + savedBy: a list of 1 to 4 avatar [name, style] lists.""" + assert(self.debugPrint("toonTakeOver(savedBy=%s)"%(self.savedBy))) isCogdo = 'cogdo' in self.fsm.getCurrentState().getName().lower() takenOver = True if isCogdo: @@ -148,88 +274,115 @@ class DistributedBuildingAI(DistributedObjectAI.DistributedObjectAI): self.fsm.request('becomingToon') if takenOver and self.suitPlannerExt: self.suitPlannerExt.recycleBuilding(isCogdo) - if hasattr(self, 'interior'): + if hasattr(self, "interior"): self.interior.requestDelete() del self.interior - + def getFrontDoorPoint(self): + """get any associated path point for this building, useful for + suits to know where to go when exiting from a building""" + assert(self.debugPrint("getFrontDoorPoint()")) return self.frontDoorPoint def setFrontDoorPoint(self, point): + """set the associated front door point with this building""" + assert(self.debugPrint("setFrontDoorPoint(%s)" % (str(point)))) self.frontDoorPoint = point def getBlock(self): + assert(self.debugPrint("getBlock()")) dummy, interiorZoneId = self.getExteriorAndInteriorZoneId() - return [ - self.block, interiorZoneId] - + return [self.block, interiorZoneId] + def getSuitData(self): - return [ - ord(self.track), self.difficulty, self.numFloors] - + assert(self.debugPrint("getSuitData()")) + return [ord(self.track), self.difficulty, self.numFloors] + def getState(self): - return [ - self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()] - + assert(self.debugPrint("getState()")) + return [self.fsm.getCurrentState().getName(), + globalClockDelta.getRealNetworkTime()] + def setState(self, state, timestamp=0): + assert(self.notify.debug(str(self.block)+" setState(state="+str(state)+")")) self.fsm.request(state) - + def isSuitBuilding(self): - state = self.fsm.getCurrentState().getName() - return state == 'suit' or state == 'becomingSuit' or state == 'clearOutToonInterior' - + """return true if that block is a suit building""" + assert(self.debugPrint("isSuitBlock()")) + state=self.fsm.getCurrentState().getName() + return state=='suit' or state=='becomingSuit' or \ + state=='clearOutToonInterior' + def isCogdo(self): - state = self.fsm.getCurrentState().getName() - return state == 'cogdo' or state == 'becomingCogdo' or state == 'becomingCogdoFromCogdo' or state == 'clearOutToonInteriorForCogdo' - + """return true if that block is a cogdo""" + assert(self.debugPrint("isSuitBlock()")) + state=self.fsm.getCurrentState().getName() + return state=='cogdo' or state=='becomingCogdo' or \ + state=='becomingCogdoFromCogdo' or state=='clearOutToonInteriorForCogdo' + def isSuitBlock(self): - state = self.fsm.getCurrentState().getName() + """return true if that block is a suit block/building/cogdo""" + assert(self.debugPrint("isSuitBlock()")) + state=self.fsm.getCurrentState().getName() return self.isSuitBuilding() or self.isCogdo() - + def isEstablishedSuitBlock(self): - state = self.fsm.getCurrentState().getName() - return state == 'suit' + """return true if that block is a fully established suit building""" + assert(self.debugPrint("isEstablishedSuitBlock()")) + state=self.fsm.getCurrentState().getName() + return state=='suit' def isToonBlock(self): - state = self.fsm.getCurrentState().getName() - return state in ('toon', 'becomingToon', 'becomingToonFromCogdo') + """return true if that block is a toon block/building""" + assert(self.debugPrint("isToonBlock()")) + state=self.fsm.getCurrentState().getName() + return state in ('toon', 'becomingToon', 'becomingToonFromCogdo', ) def getExteriorAndInteriorZoneId(self): + assert(self.notify.debug(str(self.block)+" getInteriorZoneId()")) blockNumber = self.block + assert(blockNumber<100) # this may cause trouble for the interiorZoneId, + # it may bump into the next higher zone range. dnaStore = self.air.dnaStoreMap[self.canonicalZoneId] zoneId = dnaStore.getZoneFromBlockNumber(blockNumber) zoneId = ZoneUtil.getTrueZoneId(zoneId, self.zoneId) - interiorZoneId = zoneId - zoneId % 100 + 500 + blockNumber - return ( - zoneId, interiorZoneId) - + interiorZoneId = (zoneId - zoneId % 100) + 500 + blockNumber + assert(self.notify.debug(str(self.block)+" getInteriorZoneId() returning" + +str(interiorZoneId))) + return zoneId, interiorZoneId + def d_setState(self, state): + assert(self.notify.debug(str(self.block)+" d_setState(state="+str(state)+")")) self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()]) - + def b_setVictorList(self, victorList): self.setVictorList(victorList) self.d_setVictorList(victorList) - + return + def d_setVictorList(self, victorList): - self.sendUpdate('setVictorList', [victorList]) - + self.sendUpdate("setVictorList", [victorList]) + return + def setVictorList(self, victorList): self.victorList = victorList + return def findVictorIndex(self, avId): for i in range(len(self.victorList)): if self.victorList[i] == avId: return i - return None def recordVictorResponse(self, avId): index = self.findVictorIndex(avId) if index == None: - self.air.writeServerEvent('suspicious', avId, 'DistributedBuildingAI.setVictorReady from toon not in %s.' % self.victorList) + self.air.writeServerEvent('suspicious', avId, 'DistributedBuildingAI.setVictorReady from toon not in %s.' % (self.victorList)) return + + assert(self.victorResponses[index] == 0 or self.victorResponses[index] == avId) self.victorResponses[index] = avId - return def allVictorsResponded(self): if self.victorResponses == self.victorList: @@ -240,19 +393,24 @@ class DistributedBuildingAI(DistributedObjectAI.DistributedObjectAI): def setVictorReady(self): avId = self.air.getAvatarIdFromSender() if self.victorResponses == None: - self.air.writeServerEvent('suspicious', avId, 'DistributedBuildingAI.setVictorReady in state %s.' % self.fsm.getCurrentState().getName()) + self.air.writeServerEvent('suspicious', avId, 'DistributedBuildingAI.setVictorReady in state %s.' % (self.fsm.getCurrentState().getName())) return + + # Don't tell us about this avatar exiting any more. event = self.air.getAvatarExitEvent(avId) self.ignore(event) + if self.allVictorsResponded(): return + + assert(self.notify.debug("victor %d is ready for bldg %d" % (avId, self.doId))) self.recordVictorResponse(avId) + if self.allVictorsResponded(): self.toonTakeOver() - return def setVictorExited(self, avId): - print('victor %d exited unexpectedly for bldg %d' % (avId, self.doId)) + print("victor %d exited unexpectedly for bldg %d" % (avId, self.doId)) self.recordVictorResponse(avId) if self.allVictorsResponded(): self.toonTakeOver() @@ -277,194 +435,296 @@ class DistributedBuildingAI(DistributedObjectAI.DistributedObjectAI): self.toonTakeOver() return Task.done + ##### off state ##### + def enterOff(self): - pass - + assert(self.debugPrint("enterOff()")) + def exitOff(self): - pass + assert(self.debugPrint("exitOff()")) + + ##### waitForVictors state ##### def getToon(self, toonId): - if toonId in self.air.doId2do: + if (toonId in self.air.doId2do): return self.air.doId2do[toonId] else: - self.notify.warning('getToon() - toon: %d not in repository!' % toonId) + self.notify.warning('getToon() - toon: %d not in repository!' \ + % toonId) return None def updateSavedBy(self, savedBy): + # Clear the old savedBy from the trophy manager if self.savedBy: for avId, name, dna in self.savedBy: + # Don't change building take over score when the toon is in the welcome valley. if not ZoneUtil.isWelcomeValley(self.zoneId): self.trophyMgr.removeTrophy(avId, self.numFloors) - + # Update the new saved by list self.savedBy = savedBy if self.savedBy: for avId, name, dna in self.savedBy: + # Don't change building take over score when the toon is in the welcome valley. if not ZoneUtil.isWelcomeValley(self.zoneId): self.trophyMgr.addTrophy(avId, name, self.numFloors) def enterWaitForVictors(self, victorList, savedBy): + assert(len(victorList) == 4) + + # Grab the list of active toons to pass in for each toon + # (this is used by the quest system) activeToons = [] for t in victorList: toon = None - if t: + if (t): toon = self.getToon(t) - if toon != None: + if (toon != None): activeToons.append(toon) - + # Tell the quest manager that these toons defeated this building for t in victorList: toon = None if t: toon = self.getToon(t) - self.air.writeServerEvent('buildingDefeated', t, '%s|%s|%s|%s' % (self.track, self.numFloors, self.zoneId, victorList)) - if toon != None: - self.air.questManager.toonKilledBuilding(toon, self.track, self.difficulty, self.numFloors, self.zoneId, activeToons) + self.air.writeServerEvent( + 'buildingDefeated', t, "%s|%s|%s|%s" % (self.track, self.numFloors, self.zoneId, victorList)) + if toon != None: + self.air.questManager.toonKilledBuilding( + toon, self.track, self.difficulty, + self.numFloors, self.zoneId, activeToons) + + # Convert the list to all ints. 0 means no one is there. + # Also, if a toon has disconnected, remove him from the list. for i in range(0, 4): victor = victorList[i] if victor == None or victor not in self.air.doId2do: victorList[i] = 0 + else: + # Handle unexpected exit messages for everyone else. event = self.air.getAvatarExitEvent(victor) self.accept(event, self.setVictorExited, extraArgs=[victor]) + # Save the list and also tell it to all the clients. self.b_setVictorList(victorList) self.updateSavedBy(savedBy) - self.victorResponses = [ - 0, 0, 0, 0] - self.d_setState('waitForVictors') + # List of victor responses + self.victorResponses = [0, 0, 0, 0] + # Tell the client to go into waitForVictors state + self.d_setState("waitForVictors") return def exitWaitForVictors(self): + # Stop waiting for unexpected exits. self.victorResponses = None for victor in self.victorList: event = simbase.air.getAvatarExitEvent(victor) - self.ignore(event) - + self.ignore(event) return - + def enterWaitForVictorsFromCogdo(self, victorList, savedBy): + assert(len(victorList) == 4) + + # Grab the list of active toons to pass in for each toon + # (this is used by the quest system) activeToons = [] for t in victorList: toon = None - if t: + if (t): toon = self.getToon(t) - if toon != None: + if (toon != None): activeToons.append(toon) - + # Tell the quest manager that these toons defeated this building self.buildingDefeated = len(savedBy) > 0 if self.buildingDefeated: for t in victorList: toon = None if t: toon = self.getToon(t) - self.air.writeServerEvent('buildingDefeated', t, '%s|%s|%s|%s' % (self.track, self.numFloors, self.zoneId, victorList)) - if toon != None: - self.air.questManager.toonKilledCogdo(toon, self.difficulty, self.numFloors, self.zoneId, activeToons) + self.air.writeServerEvent( + 'buildingDefeated', t, "%s|%s|%s|%s" % (self.track, self.numFloors, self.zoneId, victorList)) + if toon != None: + self.air.questManager.toonKilledCogdo( + toon, self.difficulty, + self.numFloors, self.zoneId, activeToons) + + # Convert the list to all ints. 0 means no one is there. + # Also, if a toon has disconnected, remove him from the list. for i in range(0, 4): victor = victorList[i] if victor == None or victor not in self.air.doId2do: victorList[i] = 0 + else: + # Handle unexpected exit messages for everyone else. event = self.air.getAvatarExitEvent(victor) self.accept(event, self.setVictorExited, extraArgs=[victor]) + # Save the list and also tell it to all the clients. self.b_setVictorList(victorList) self.updateSavedBy(savedBy) - self.victorResponses = [ - 0, 0, 0, 0] + # List of victor responses + self.victorResponses = [0, 0, 0, 0] taskMgr.doMethodLater(30, self.victorsTimedOutTask, self.taskName(str(self.block) + '_waitForVictors-timer')) - self.d_setState('waitForVictorsFromCogdo') + # Tell the client to go into waitForVictors state + self.d_setState("waitForVictorsFromCogdo") return def exitWaitForVictorsFromCogdo(self): taskMgr.remove(self.taskName(str(self.block) + '_waitForVictors-timer')) + # Stop waiting for unexpected exits. self.victorResponses = None for victor in self.victorList: event = simbase.air.getAvatarExitEvent(victor) - self.ignore(event) - + self.ignore(event) return - + + ##### becomingToon state ##### + def enterBecomingToon(self): + assert(self.debugPrint("enterBecomingToon()")) self.d_setState('becomingToon') - name = self.taskName(str(self.block) + '_becomingToon-timer') - taskMgr.doMethodLater(SuitBuildingGlobals.VICTORY_SEQUENCE_TIME, self.becomingToonTask, name) - + name = self.taskName(str(self.block)+'_becomingToon-timer') + taskMgr.doMethodLater( + SuitBuildingGlobals.VICTORY_SEQUENCE_TIME, + self.becomingToonTask, + name) + def exitBecomingToon(self): - name = self.taskName(str(self.block) + '_becomingToon-timer') + assert(self.debugPrint("exitBecomingToon()")) + name = self.taskName(str(self.block)+'_becomingToon-timer') taskMgr.remove(name) - + + ##### becomingToonFromCogdo state ##### + def enterBecomingToonFromCogdo(self): + assert(self.debugPrint("enterBecomingToonFromCogdo()")) self.d_setState('becomingToonFromCogdo') - name = self.taskName(str(self.block) + '_becomingToonFromCogdo-timer') - taskMgr.doMethodLater(SuitBuildingGlobals.VICTORY_SEQUENCE_TIME, self.becomingToonTask, name) - + name = self.taskName(str(self.block)+'_becomingToonFromCogdo-timer') + taskMgr.doMethodLater( + SuitBuildingGlobals.VICTORY_SEQUENCE_TIME, + self.becomingToonTask, + name) + def exitBecomingToonFromCogdo(self): - name = self.taskName(str(self.block) + '_becomingToonFromCogdo-timer') + assert(self.debugPrint("exitBecomingToonFromCogdo()")) + name = self.taskName(str(self.block)+'_becomingToonFromCogdo-timer') taskMgr.remove(name) + + ##### toon state ##### def becomingToonTask(self, task): - self.fsm.request('toon') - self.suitPlannerExt.buildingMgr.save() - return Task.done + assert(self.debugPrint("becomingToonTask()")) + self.fsm.request("toon") + # Save the building state whenever we convert a building to + # toonness. + self.suitPlannerExt.buildingMgr.save() + + return Task.done + def enterToon(self): + assert(self.debugPrint("enterToon()")) self.d_setState('toon') - exteriorZoneId, interiorZoneId = self.getExteriorAndInteriorZoneId() - if simbase.config.GetBool('want-new-toonhall', 1) and ZoneUtil.getCanonicalZoneId(interiorZoneId) == ToonHall: - self.interior = DistributedToonHallInteriorAI.DistributedToonHallInteriorAI(self.block, self.air, interiorZoneId, self) + # Create the DistributedDoor: + exteriorZoneId, interiorZoneId=self.getExteriorAndInteriorZoneId() + # Toon interior: + if simbase.config.GetBool("want-new-toonhall",1) and \ + ZoneUtil.getCanonicalZoneId(interiorZoneId)== ToonHall: + self.interior=DistributedToonHallInteriorAI.DistributedToonHallInteriorAI( + self.block, self.air, interiorZoneId, self) else: - self.interior = DistributedToonInteriorAI.DistributedToonInteriorAI(self.block, self.air, interiorZoneId, self) + self.interior=DistributedToonInteriorAI.DistributedToonInteriorAI( + self.block, self.air, interiorZoneId, self) self.interior.generateWithRequired(interiorZoneId) - door = self.createExteriorDoor() - insideDoor = DistributedDoorAI.DistributedDoorAI(self.air, self.block, DoorTypes.INT_STANDARD) + + # Outside door: + door=self.createExteriorDoor() + # Inside of the same door (different zone, and different distributed object): + insideDoor=DistributedDoorAI.DistributedDoorAI(self.air, self.block, + DoorTypes.INT_STANDARD) + # Tell them about each other: door.setOtherDoor(insideDoor) insideDoor.setOtherDoor(door) - door.zoneId = exteriorZoneId - insideDoor.zoneId = interiorZoneId + door.zoneId=exteriorZoneId + insideDoor.zoneId=interiorZoneId + # Now that they both now about each other, generate them: door.generateWithRequired(exteriorZoneId) insideDoor.generateWithRequired(interiorZoneId) - self.door = door - self.insideDoor = insideDoor + # keep track of them: + self.door=door + self.insideDoor=insideDoor self.becameSuitTime = 0 - self.knockKnock = DistributedKnockKnockDoorAI.DistributedKnockKnockDoorAI(self.air, self.block) + + self.knockKnock=DistributedKnockKnockDoorAI.DistributedKnockKnockDoorAI( + self.air, self.block) self.knockKnock.generateWithRequired(exteriorZoneId) - self.air.writeServerEvent('building-toon', self.doId, '%s|%s' % (self.zoneId, self.block)) + + self.air.writeServerEvent( + 'building-toon', self.doId, + "%s|%s" % (self.zoneId, self.block)) def createExteriorDoor(self): - result = DistributedDoorAI.DistributedDoorAI(self.air, self.block, DoorTypes.EXT_STANDARD) + """Return the DistributedDoor for the exterior, with correct door type set""" + # Created so animated buildings can over ride this function + result = DistributedDoorAI.DistributedDoorAI(self.air, self.block, + DoorTypes.EXT_STANDARD) return result - + def exitToon(self): + assert(self.debugPrint("exitToon()")) self.door.setDoorLock(FADoorCodes.BUILDING_TAKEOVER) - + # The door doesn't get unlocked, because + # it will be distroyed and recreated. + + ##### clearOutToonInterior state ##### + def enterClearOutToonInterior(self): + assert(self.debugPrint("enterClearOutToonInterior()")) self.d_setState('clearOutToonInterior') - if hasattr(self, 'interior'): - self.interior.setState('beingTakenOver') - name = self.taskName(str(self.block) + '_clearOutToonInterior-timer') - taskMgr.doMethodLater(SuitBuildingGlobals.CLEAR_OUT_TOON_BLDG_TIME, self.clearOutToonInteriorTask, name) - + if hasattr(self, "interior"): + self.interior.setState("beingTakenOver") + name = self.taskName(str(self.block)+'_clearOutToonInterior-timer') + taskMgr.doMethodLater( + SuitBuildingGlobals.CLEAR_OUT_TOON_BLDG_TIME, + self.clearOutToonInteriorTask, + name) + def exitClearOutToonInterior(self): - name = self.taskName(str(self.block) + '_clearOutToonInterior-timer') + assert(self.debugPrint("exitClearOutToonInterior()")) + name = self.taskName(str(self.block)+'_clearOutToonInterior-timer') taskMgr.remove(name) + + ##### becomingSuit state ##### def clearOutToonInteriorTask(self, task): - self.fsm.request('becomingSuit') + assert(self.debugPrint("clearOutToonInteriorTask()")) + self.fsm.request("becomingSuit") return Task.done - + def enterBecomingSuit(self): - self.sendUpdate('setSuitData', [ - ord(self.track), self.difficulty, self.numFloors]) - self.d_setState('becomingSuit') - name = self.taskName(str(self.block) + '_becomingSuit-timer') - taskMgr.doMethodLater(SuitBuildingGlobals.TO_SUIT_BLDG_TIME, self.becomingSuitTask, name) + assert(self.debugPrint("enterBecomingSuit()")) + # We have to send this message before we send the distributed + # update to becomingSuit state, because the clients depend on + # knowing what kind of suit building we're becoming. + self.sendUpdate('setSuitData', + [ord(self.track), self.difficulty, self.numFloors]) + + self.d_setState('becomingSuit') + name = self.taskName(str(self.block)+'_becomingSuit-timer') + taskMgr.doMethodLater( + SuitBuildingGlobals.TO_SUIT_BLDG_TIME, + self.becomingSuitTask, + name) + def exitBecomingSuit(self): - name = self.taskName(str(self.block) + '_becomingSuit-timer') + assert(self.debugPrint("exitBecomingSuit()")) + name = self.taskName(str(self.block)+'_becomingSuit-timer') taskMgr.remove(name) - if hasattr(self, 'interior'): + # Clean up the toon distributed objects: + if hasattr(self, "interior"): self.interior.requestDelete() del self.interior self.door.requestDelete() @@ -473,55 +733,101 @@ class DistributedBuildingAI(DistributedObjectAI.DistributedObjectAI): del self.insideDoor self.knockKnock.requestDelete() del self.knockKnock + + ##### suit state ##### def becomingSuitTask(self, task): - self.fsm.request('suit') - self.suitPlannerExt.buildingMgr.save() - return Task.done + assert(self.debugPrint("becomingSuitTask()")) + self.fsm.request("suit") + # Save the building state whenever we convert a building to + # suitness. + self.suitPlannerExt.buildingMgr.save() + + return Task.done + def enterSuit(self): - self.sendUpdate('setSuitData', [ - ord(self.track), self.difficulty, self.numFloors]) + assert(self.debugPrint("enterSuit()")) + + # We have to send this message again, even though we've + # already sent it in becomingSuit, because we might have come + # to this state directly on startup. + self.sendUpdate('setSuitData', + [ord(self.track), self.difficulty, self.numFloors]) + + # Create the suit planner for the interior zoneId, interiorZoneId = self.getExteriorAndInteriorZoneId() - self.planner = SuitPlannerInteriorAI.SuitPlannerInteriorAI(self.numFloors, self.difficulty, self.track, interiorZoneId) + self.planner = SuitPlannerInteriorAI.SuitPlannerInteriorAI( + self.numFloors, self.difficulty, self.track, interiorZoneId) + self.d_setState('suit') - exteriorZoneId, interiorZoneId = self.getExteriorAndInteriorZoneId() - self.elevator = DistributedElevatorExtAI.DistributedElevatorExtAI(self.air, self) + # Create the DistributedDoor: + exteriorZoneId, interiorZoneId=self.getExteriorAndInteriorZoneId() + #todo: ...create the elevator. + self.elevator = DistributedElevatorExtAI.DistributedElevatorExtAI( + self.air, + self) self.elevator.generateWithRequired(exteriorZoneId) - self.air.writeServerEvent('building-cog', self.doId, '%s|%s|%s|%s' % (self.zoneId, self.block, self.track, self.numFloors)) + + self.air.writeServerEvent( + 'building-cog', self.doId, + "%s|%s|%s|%s" % (self.zoneId, self.block, self.track, self.numFloors)) def exitSuit(self): + assert(self.debugPrint("exitSuit()")) del self.planner - if hasattr(self, 'elevator'): + # Clean up the suit distributed objects: + if hasattr(self, "elevator"): self.elevator.requestDelete() del self.elevator + ##### clearOutToonInteriorForCogdo state ##### + def enterClearOutToonInteriorForCogdo(self): + assert(self.debugPrint("enterClearOutToonInteriorForCogdo()")) self.d_setState('clearOutToonInteriorForCogdo') - if hasattr(self, 'interior'): - self.interior.setState('beingTakenOver') - name = self.taskName(str(self.block) + '_clearOutToonInteriorForCogdo-timer') - taskMgr.doMethodLater(SuitBuildingGlobals.CLEAR_OUT_TOON_BLDG_TIME, self.clearOutToonInteriorForCogdoTask, name) - + if hasattr(self, "interior"): + self.interior.setState("beingTakenOver") + name = self.taskName(str(self.block)+'_clearOutToonInteriorForCogdo-timer') + taskMgr.doMethodLater( + SuitBuildingGlobals.CLEAR_OUT_TOON_BLDG_TIME, + self.clearOutToonInteriorForCogdoTask, + name) + def exitClearOutToonInteriorForCogdo(self): - name = self.taskName(str(self.block) + '_clearOutToonInteriorForCogdo-timer') + assert(self.debugPrint("exitClearOutToonInteriorForCogdo()")) + name = self.taskName(str(self.block)+'_clearOutToonInteriorForCogdo-timer') taskMgr.remove(name) + + ##### becomingCogdo state ##### def clearOutToonInteriorForCogdoTask(self, task): - self.fsm.request('becomingCogdo') + assert(self.debugPrint("clearOutToonInteriorForCogdoTask()")) + self.fsm.request("becomingCogdo") return Task.done - + def enterBecomingCogdo(self): - self.sendUpdate('setSuitData', [ - ord(self.track), self.difficulty, self.numFloors]) - self.d_setState('becomingCogdo') - name = self.taskName(str(self.block) + '_becomingCogdo-timer') - taskMgr.doMethodLater(SuitBuildingGlobals.TO_SUIT_BLDG_TIME, self.becomingCogdoTask, name) + assert(self.debugPrint("enterBecomingCogdo()")) + # We have to send this message before we send the distributed + # update to becomingCogdo state, because the clients depend on + # knowing what kind of cogdo building we're becoming. + self.sendUpdate('setSuitData', + [ord(self.track), self.difficulty, self.numFloors]) + + self.d_setState('becomingCogdo') + name = self.taskName(str(self.block)+'_becomingCogdo-timer') + taskMgr.doMethodLater( + SuitBuildingGlobals.TO_SUIT_BLDG_TIME, + self.becomingCogdoTask, + name) + def exitBecomingCogdo(self): - name = self.taskName(str(self.block) + '_becomingCogdo-timer') + assert(self.debugPrint("exitBecomingCogdo()")) + name = self.taskName(str(self.block)+'_becomingCogdo-timer') taskMgr.remove(name) - if hasattr(self, 'interior'): + # Clean up the toon distributed objects: + if hasattr(self, "interior"): self.interior.requestDelete() del self.interior self.door.requestDelete() @@ -531,6 +837,8 @@ class DistributedBuildingAI(DistributedObjectAI.DistributedObjectAI): self.knockKnock.requestDelete() del self.knockKnock + ##### becomingCogdoFromCogdo state ##### + def enterBecomingCogdoFromCogdo(self): self.d_setState('becomingCogdoFromCogdo') name = self.taskName(str(self.block) + '_becomingCogdoFromCogdo-timer') @@ -541,31 +849,62 @@ class DistributedBuildingAI(DistributedObjectAI.DistributedObjectAI): name = self.taskName(str(self.block) + '_becomingCogdoFromCogdo-timer') taskMgr.remove(name) - def becomingCogdoTask(self, task): - self.fsm.request('cogdo') - self.suitPlannerExt.buildingMgr.save() - return Task.done + ##### cogdo state ##### + def becomingCogdoTask(self, task): + assert(self.debugPrint("becomingCogdoTask()")) + self.fsm.request("cogdo") + + # Save the building state whenever we convert a building to + # cogdoness. + self.suitPlannerExt.buildingMgr.save() + + return Task.done + def enterCogdo(self): - self.sendUpdate('setSuitData', [ - ord(self.track), self.difficulty, self.numFloors]) + assert(self.debugPrint("enterCogdo()")) + + # We have to send this message again, even though we've + # already sent it in becomingCogdo, because we might have come + # to this state directly on startup. + self.sendUpdate('setSuitData', + [ord(self.track), self.difficulty, self.numFloors]) + + # Create the suit planner for the interior zoneId, interiorZoneId = self.getExteriorAndInteriorZoneId() self._cogdoLayout = CogdoLayout(self.numFloors) - self.planner = SuitPlannerCogdoInteriorAI(self._cogdoLayout, self.difficulty, self.track, interiorZoneId) + self.planner = SuitPlannerCogdoInteriorAI( + self._cogdoLayout, self.difficulty, self.track, interiorZoneId) + self.d_setState('cogdo') - exteriorZoneId, interiorZoneId = self.getExteriorAndInteriorZoneId() + # Create the DistributedDoor: + exteriorZoneId, interiorZoneId=self.getExteriorAndInteriorZoneId() + #todo: ...create the elevator. self.elevator = DistributedCogdoElevatorExtAI(self.air, self, fSkipOpening=self.fSkipElevatorOpening) self.fSkipElevatorOpening = False self.elevator.generateWithRequired(exteriorZoneId) - self.air.writeServerEvent('building-cogdo', self.doId, '%s|%s|%s' % (self.zoneId, self.block, self.numFloors)) + + self.air.writeServerEvent( + 'building-cogdo', self.doId, + "%s|%s|%s" % (self.zoneId, self.block, self.numFloors)) def exitCogdo(self): + assert(self.debugPrint("exitCogdo()")) del self.planner - if hasattr(self, 'elevator'): + # Clean up the cogdo distributed objects: + if hasattr(self, "elevator"): self.elevator.requestDelete() del self.elevator - def setSuitPlannerExt(self, planner): + def setSuitPlannerExt( self, planner ): + """ + //////////////////////////////////////////////////////////////////// + // Function: let the building know which suit planner contains + // its building manager + // Parameters: planner, the governing suit planner for this bldg + // Changes: + //////////////////////////////////////////////////////////////////// + """ self.suitPlannerExt = planner def _createSuitInterior(self): @@ -573,26 +912,43 @@ class DistributedBuildingAI(DistributedObjectAI.DistributedObjectAI): def _createCogdoInterior(self): return DistributedCogdoInteriorAI(self.air, self.elevator) - + def createSuitInterior(self): + # Create a building interior in the new (interior) zone self.interior = self._createSuitInterior() dummy, interiorZoneId = self.getExteriorAndInteriorZoneId() self.interior.fsm.request('WaitForAllToonsInside') self.interior.generateWithRequired(interiorZoneId) def createCogdoInterior(self): + # Create a building interior in the new (interior) zone self.interior = self._createCogdoInterior() dummy, interiorZoneId = self.getExteriorAndInteriorZoneId() self.interior.fsm.request('WaitForAllToonsInside') self.interior.generateWithRequired(interiorZoneId) def deleteSuitInterior(self): - if hasattr(self, 'interior'): + if hasattr(self, "interior"): self.interior.requestDelete() del self.interior - if hasattr(self, 'elevator'): + if hasattr(self, "elevator"): + # -1 means the lobby. self.elevator.d_setFloor(-1) self.elevator.open() def deleteCogdoInterior(self): self.deleteSuitInterior() + + if __debug__: + def debugPrint(self, message): + """for debugging""" + return self.notify.debug( + str(self.__dict__.get('block', '?'))+' '+message) + +# history +# +# 10May01 jlbutler added frontDoorPoint to the building so a suit or +# the suit planner can get from a building to a suit +# path point which is in front of the building +# +