oldschool-toontown/toontown/building/DistributedBuildingAI.py
2019-12-30 01:07:56 -05:00

590 lines
24 KiB
Python

from otp.ai.AIBaseGlobal import *
from direct.distributed.ClockDelta import *
import types
from direct.task.Task import Task
from direct.directnotify import DirectNotifyGlobal
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 toontown.hood import ZoneUtil
import random, 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):
FieldOfficeNumFloors = 1
def __init__(self, air, blockNumber, zoneId, trophyMgr):
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
self.block = 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.enterInitialState()
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'):
self.interior.requestDelete()
del self.interior
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'):
self.elevator.requestDelete()
del self.elevator
self.requestDelete()
def delete(self):
taskMgr.remove(self.taskName('suitbldg-time-out'))
taskMgr.remove(self.taskName(str(self.block) + '_becomingToon-timer'))
taskMgr.remove(self.taskName(str(self.block) + '_becomingSuit-timer'))
DistributedObjectAI.DistributedObjectAI.delete(self)
del self.fsm
def getPickleData(self):
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}
return pickleData
def _getMinMaxFloors(self, difficulty):
return SuitBuildingGlobals.SuitBuildingInfo[difficulty][0]
def suitTakeOver(self, suitTrack, difficulty, buildingHeight):
if not self.isToonBlock():
return
self.updateSavedBy(None)
difficulty = min(difficulty, len(SuitBuildingGlobals.SuitBuildingInfo) - 1)
minFloors, maxFloors = self._getMinMaxFloors(difficulty)
if buildingHeight == None:
numFloors = random.randint(minFloors, maxFloors)
else:
numFloors = buildingHeight + 1
if numFloors < minFloors or numFloors > maxFloors:
numFloors = random.randint(minFloors, maxFloors)
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(None)
numFloors = self.FieldOfficeNumFloors
self.track = suitTrack
self.difficulty = difficulty
self.numFloors = numFloors
self.becameSuitTime = time.time()
self.fsm.request('clearOutToonInteriorForCogdo')
return
def toonTakeOver(self):
isCogdo = 'cogdo' in self.fsm.getCurrentState().getName().lower()
takenOver = True
if isCogdo:
if self.buildingDefeated:
self.fsm.request('becomingToonFromCogdo')
else:
self.fsm.request('becomingCogdoFromCogdo')
takenOver = False
else:
self.fsm.request('becomingToon')
if takenOver and self.suitPlannerExt:
self.suitPlannerExt.recycleBuilding(isCogdo)
if hasattr(self, 'interior'):
self.interior.requestDelete()
del self.interior
def getFrontDoorPoint(self):
return self.frontDoorPoint
def setFrontDoorPoint(self, point):
self.frontDoorPoint = point
def getBlock(self):
dummy, interiorZoneId = self.getExteriorAndInteriorZoneId()
return [
self.block, interiorZoneId]
def getSuitData(self):
return [
ord(self.track), self.difficulty, self.numFloors]
def getState(self):
return [
self.fsm.getCurrentState().getName(), globalClockDelta.getRealNetworkTime()]
def setState(self, state, timestamp=0):
self.fsm.request(state)
def isSuitBuilding(self):
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'
def isSuitBlock(self):
state = self.fsm.getCurrentState().getName()
return self.isSuitBuilding() or self.isCogdo()
def isEstablishedSuitBlock(self):
state = self.fsm.getCurrentState().getName()
return state == 'suit'
def isToonBlock(self):
state = self.fsm.getCurrentState().getName()
return state in ('toon', 'becomingToon', 'becomingToonFromCogdo')
def getExteriorAndInteriorZoneId(self):
blockNumber = self.block
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)
def d_setState(self, state):
self.sendUpdate('setState', [state, globalClockDelta.getRealNetworkTime()])
def b_setVictorList(self, victorList):
self.setVictorList(victorList)
self.d_setVictorList(victorList)
def d_setVictorList(self, victorList):
self.sendUpdate('setVictorList', [victorList])
def setVictorList(self, victorList):
self.victorList = victorList
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)
return
self.victorResponses[index] = avId
return
def allVictorsResponded(self):
if self.victorResponses == self.victorList:
return 1
else:
return 0
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())
return
event = self.air.getAvatarExitEvent(avId)
self.ignore(event)
if self.allVictorsResponded():
return
self.recordVictorResponse(avId)
if self.allVictorsResponded():
self.toonTakeOver()
return
def setVictorExited(self, avId):
print('victor %d exited unexpectedly for bldg %d' % (avId, self.doId))
self.recordVictorResponse(avId)
if self.allVictorsResponded():
self.toonTakeOver()
def victorsTimedOutTask(self, task):
if self.allVictorsResponded():
return
if hasattr(self, 'interior'):
self.notify.info('victorsTimedOutTask: ejecting players by deleting interior.')
self.interior.requestDelete()
del self.interior
task.delayTime = 15.0
return task.again
self.notify.info('victorsTimedOutTask: suspicious players remaining, advancing state.')
for i in range(len(self.victorList)):
if self.victorList[i] and self.victorResponses[i] == 0:
self.air.writeServerEvent('suspicious', self.victorList[i], 'DistributedBuildingAI toon client refused to leave building.')
self.recordVictorResponse(self.victorList[i])
event = self.air.getAvatarExitEvent(self.victorList[i])
self.ignore(event)
self.toonTakeOver()
return Task.done
def enterOff(self):
pass
def exitOff(self):
pass
def getToon(self, toonId):
if toonId in self.air.doId2do:
return self.air.doId2do[toonId]
else:
self.notify.warning('getToon() - toon: %d not in repository!' % toonId)
return None
def updateSavedBy(self, savedBy):
if self.savedBy:
for avId, name, dna in self.savedBy:
if not ZoneUtil.isWelcomeValley(self.zoneId):
self.trophyMgr.removeTrophy(avId, self.numFloors)
self.savedBy = savedBy
if self.savedBy:
for avId, name, dna in self.savedBy:
if not ZoneUtil.isWelcomeValley(self.zoneId):
self.trophyMgr.addTrophy(avId, name, self.numFloors)
def enterWaitForVictors(self, victorList, savedBy):
activeToons = []
for t in victorList:
toon = None
if t:
toon = self.getToon(t)
if toon != None:
activeToons.append(toon)
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)
for i in range(0, 4):
victor = victorList[i]
if victor == None or victor not in self.air.doId2do:
victorList[i] = 0
else:
event = self.air.getAvatarExitEvent(victor)
self.accept(event, self.setVictorExited, extraArgs=[victor])
self.b_setVictorList(victorList)
self.updateSavedBy(savedBy)
self.victorResponses = [
0, 0, 0, 0]
self.d_setState('waitForVictors')
return
def exitWaitForVictors(self):
self.victorResponses = None
for victor in self.victorList:
event = simbase.air.getAvatarExitEvent(victor)
self.ignore(event)
return
def enterWaitForVictorsFromCogdo(self, victorList, savedBy):
activeToons = []
for t in victorList:
toon = None
if t:
toon = self.getToon(t)
if toon != None:
activeToons.append(toon)
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)
for i in range(0, 4):
victor = victorList[i]
if victor == None or victor not in self.air.doId2do:
victorList[i] = 0
else:
event = self.air.getAvatarExitEvent(victor)
self.accept(event, self.setVictorExited, extraArgs=[victor])
self.b_setVictorList(victorList)
self.updateSavedBy(savedBy)
self.victorResponses = [
0, 0, 0, 0]
taskMgr.doMethodLater(30, self.victorsTimedOutTask, self.taskName(str(self.block) + '_waitForVictors-timer'))
self.d_setState('waitForVictorsFromCogdo')
return
def exitWaitForVictorsFromCogdo(self):
taskMgr.remove(self.taskName(str(self.block) + '_waitForVictors-timer'))
self.victorResponses = None
for victor in self.victorList:
event = simbase.air.getAvatarExitEvent(victor)
self.ignore(event)
return
def enterBecomingToon(self):
self.d_setState('becomingToon')
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')
taskMgr.remove(name)
def enterBecomingToonFromCogdo(self):
self.d_setState('becomingToonFromCogdo')
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')
taskMgr.remove(name)
def becomingToonTask(self, task):
self.fsm.request('toon')
self.suitPlannerExt.buildingMgr.save()
return Task.done
def enterToon(self):
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)
else:
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)
door.setOtherDoor(insideDoor)
insideDoor.setOtherDoor(door)
door.zoneId = exteriorZoneId
insideDoor.zoneId = interiorZoneId
door.generateWithRequired(exteriorZoneId)
insideDoor.generateWithRequired(interiorZoneId)
self.door = door
self.insideDoor = insideDoor
self.becameSuitTime = 0
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))
def createExteriorDoor(self):
result = DistributedDoorAI.DistributedDoorAI(self.air, self.block, DoorTypes.EXT_STANDARD)
return result
def exitToon(self):
self.door.setDoorLock(FADoorCodes.BUILDING_TAKEOVER)
def enterClearOutToonInterior(self):
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)
def exitClearOutToonInterior(self):
name = self.taskName(str(self.block) + '_clearOutToonInterior-timer')
taskMgr.remove(name)
def clearOutToonInteriorTask(self, task):
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)
def exitBecomingSuit(self):
name = self.taskName(str(self.block) + '_becomingSuit-timer')
taskMgr.remove(name)
if hasattr(self, 'interior'):
self.interior.requestDelete()
del self.interior
self.door.requestDelete()
del self.door
self.insideDoor.requestDelete()
del self.insideDoor
self.knockKnock.requestDelete()
del self.knockKnock
def becomingSuitTask(self, task):
self.fsm.request('suit')
self.suitPlannerExt.buildingMgr.save()
return Task.done
def enterSuit(self):
self.sendUpdate('setSuitData', [
ord(self.track), self.difficulty, self.numFloors])
zoneId, interiorZoneId = self.getExteriorAndInteriorZoneId()
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)
self.elevator.generateWithRequired(exteriorZoneId)
self.air.writeServerEvent('building-cog', self.doId, '%s|%s|%s|%s' % (self.zoneId, self.block, self.track, self.numFloors))
def exitSuit(self):
del self.planner
if hasattr(self, 'elevator'):
self.elevator.requestDelete()
del self.elevator
def enterClearOutToonInteriorForCogdo(self):
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)
def exitClearOutToonInteriorForCogdo(self):
name = self.taskName(str(self.block) + '_clearOutToonInteriorForCogdo-timer')
taskMgr.remove(name)
def clearOutToonInteriorForCogdoTask(self, task):
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)
def exitBecomingCogdo(self):
name = self.taskName(str(self.block) + '_becomingCogdo-timer')
taskMgr.remove(name)
if hasattr(self, 'interior'):
self.interior.requestDelete()
del self.interior
self.door.requestDelete()
del self.door
self.insideDoor.requestDelete()
del self.insideDoor
self.knockKnock.requestDelete()
del self.knockKnock
def enterBecomingCogdoFromCogdo(self):
self.d_setState('becomingCogdoFromCogdo')
name = self.taskName(str(self.block) + '_becomingCogdoFromCogdo-timer')
taskMgr.doMethodLater(SuitBuildingGlobals.VICTORY_RUN_TIME, self.becomingCogdoTask, name)
def exitBecomingCogdoFromCogdo(self):
self.fSkipElevatorOpening = True
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
def enterCogdo(self):
self.sendUpdate('setSuitData', [
ord(self.track), self.difficulty, self.numFloors])
zoneId, interiorZoneId = self.getExteriorAndInteriorZoneId()
self._cogdoLayout = CogdoLayout(self.numFloors)
self.planner = SuitPlannerCogdoInteriorAI(self._cogdoLayout, self.difficulty, self.track, interiorZoneId)
self.d_setState('cogdo')
exteriorZoneId, interiorZoneId = self.getExteriorAndInteriorZoneId()
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))
def exitCogdo(self):
del self.planner
if hasattr(self, 'elevator'):
self.elevator.requestDelete()
del self.elevator
def setSuitPlannerExt(self, planner):
self.suitPlannerExt = planner
def _createSuitInterior(self):
return DistributedSuitInteriorAI.DistributedSuitInteriorAI(self.air, self.elevator)
def _createCogdoInterior(self):
return DistributedCogdoInteriorAI(self.air, self.elevator)
def createSuitInterior(self):
self.interior = self._createSuitInterior()
dummy, interiorZoneId = self.getExteriorAndInteriorZoneId()
self.interior.fsm.request('WaitForAllToonsInside')
self.interior.generateWithRequired(interiorZoneId)
def createCogdoInterior(self):
self.interior = self._createCogdoInterior()
dummy, interiorZoneId = self.getExteriorAndInteriorZoneId()
self.interior.fsm.request('WaitForAllToonsInside')
self.interior.generateWithRequired(interiorZoneId)
def deleteSuitInterior(self):
if hasattr(self, 'interior'):
self.interior.requestDelete()
del self.interior
if hasattr(self, 'elevator'):
self.elevator.d_setFloor(-1)
self.elevator.open()
def deleteCogdoInterior(self):
self.deleteSuitInterior()