1182 lines
55 KiB
Python
1182 lines
55 KiB
Python
from panda3d.toontown import *
|
|
from otp.ai.AIBaseGlobal import *
|
|
from direct.distributed import DistributedObjectAI
|
|
from . import SuitPlannerBase, DistributedSuitAI
|
|
from toontown.battle import BattleManagerAI
|
|
from direct.task import Task
|
|
from direct.directnotify import DirectNotifyGlobal
|
|
from . import SuitDNA
|
|
from toontown.battle import SuitBattleGlobals
|
|
from . import SuitTimings
|
|
from toontown.toon import NPCToons
|
|
from toontown.building import HQBuildingAI
|
|
from toontown.hood import ZoneUtil
|
|
from toontown.building import SuitBuildingGlobals
|
|
from toontown.building.DistributedBuildingAI import DistributedBuildingAI
|
|
from toontown.toonbase import ToontownBattleGlobals
|
|
from toontown.toonbase import ToontownGlobals
|
|
import math, time, random
|
|
|
|
class DistributedSuitPlannerAI(DistributedObjectAI.DistributedObjectAI, SuitPlannerBase.SuitPlannerBase):
|
|
CogdoPopFactor = config.GetFloat('cogdo-pop-factor', 1.5)
|
|
CogdoRatio = min(1.0, max(0.0, config.GetFloat('cogdo-ratio', 0.5)))
|
|
MinimumOfOne = config.GetBool('minimum-of-one-building', 0)
|
|
SuitHoodInfo = [
|
|
[
|
|
2100, 5, 15, 0, 5, 20, 3, (1, 5, 10, 40, 60, 80), (25, 25, 25, 25), (1, 2, 3), []], [2200, 3, 10, 0, 5, 15, 3, (1, 5, 10, 40, 60, 80), (10, 70, 10, 10), (1, 2, 3), []], [2300, 3, 10, 0, 5, 15, 3, (1, 5, 10, 40, 60, 80), (10, 10, 40, 40), (1, 2, 3), []], [1100, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (90, 10, 0, 0), (2, 3, 4), []], [1200, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (0, 0, 90, 10), (3, 4, 5, 6), []], [1300, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (40, 40, 10, 10), (3, 4, 5, 6), []], [3100, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (90, 10, 0, 0), (5, 6, 7), []], [3200, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (10, 20, 30, 40), (5, 6, 7), []], [3300, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (5, 85, 5, 5), (7, 8, 9), []], [4100, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (0, 0, 50, 50), (2, 3, 4), []], [4200, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (0, 0, 90, 10), (3, 4, 5, 6), []], [4300, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (50, 50, 0, 0), (3, 4, 5, 6), []], [5100, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (0, 20, 10, 70), (2, 3, 4), []], [5200, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (10, 70, 0, 20), (3, 4, 5, 6), []], [5300, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (5, 5, 5, 85), (3, 4, 5, 6), []], [9100, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (25, 25, 25, 25), (6, 7, 8, 9), []], [9200, 1, 5, 0, 99, 100, 4, (1, 5, 10, 40, 60, 80), (5, 5, 85, 5), (6, 7, 8, 9), []], [11000, 3, 15, 0, 0, 0, 4, (1, 5, 10, 40, 60, 80), (0, 0, 0, 100), (4, 5, 6), []], [11200, 10, 20, 0, 0, 0, 4, (1, 5, 10, 40, 60, 80), (0, 0, 0, 100), (4, 5, 6), []], [12000, 10, 20, 0, 0, 0, 4, (1, 5, 10, 40, 60, 80), (0, 0, 100, 0), (7, 8, 9), []], [13000, 10, 20, 0, 0, 0, 4, (1, 5, 10, 40, 60, 80), (0, 100, 0, 0), (8, 9, 10), []]]
|
|
SUIT_HOOD_INFO_ZONE = 0
|
|
SUIT_HOOD_INFO_MIN = 1
|
|
SUIT_HOOD_INFO_MAX = 2
|
|
SUIT_HOOD_INFO_BMIN = 3
|
|
SUIT_HOOD_INFO_BMAX = 4
|
|
SUIT_HOOD_INFO_BWEIGHT = 5
|
|
SUIT_HOOD_INFO_SMAX = 6
|
|
SUIT_HOOD_INFO_JCHANCE = 7
|
|
SUIT_HOOD_INFO_TRACK = 8
|
|
SUIT_HOOD_INFO_LVL = 9
|
|
SUIT_HOOD_INFO_HEIGHTS = 10
|
|
MAX_SUIT_TYPES = 6
|
|
POP_UPKEEP_DELAY = 10
|
|
POP_ADJUST_DELAY = 300
|
|
PATH_COLLISION_BUFFER = 5
|
|
TOTAL_MAX_SUITS = 50
|
|
MIN_PATH_LEN = 40
|
|
MAX_PATH_LEN = 300
|
|
MIN_TAKEOVER_PATH_LEN = 2
|
|
SUITS_ENTER_BUILDINGS = 1
|
|
SUIT_BUILDING_NUM_SUITS = 1.5
|
|
SUIT_BUILDING_TIMEOUT = [
|
|
None, None, None, None, None, None, 72, 60, 48, 36, 24, 12, 6, 3, 1, 0.5]
|
|
TOTAL_SUIT_BUILDING_PCT = 18 * CogdoPopFactor
|
|
BUILDING_HEIGHT_DISTRIBUTION = [
|
|
14, 18, 25, 23, 20]
|
|
TOTAL_BWEIGHT = 0
|
|
TOTAL_BWEIGHT_PER_TRACK = [
|
|
0, 0, 0, 0]
|
|
TOTAL_BWEIGHT_PER_HEIGHT = [
|
|
0, 0, 0, 0, 0]
|
|
for currHoodInfo in SuitHoodInfo:
|
|
weight = currHoodInfo[SUIT_HOOD_INFO_BWEIGHT]
|
|
tracks = currHoodInfo[SUIT_HOOD_INFO_TRACK]
|
|
levels = currHoodInfo[SUIT_HOOD_INFO_LVL]
|
|
heights = [
|
|
0, 0, 0, 0, 0]
|
|
for level in levels:
|
|
minFloors, maxFloors = SuitBuildingGlobals.SuitBuildingInfo[level - 1][0]
|
|
for i in range(minFloors - 1, maxFloors):
|
|
heights[i] += 1
|
|
|
|
currHoodInfo[SUIT_HOOD_INFO_HEIGHTS] = heights
|
|
TOTAL_BWEIGHT += weight
|
|
TOTAL_BWEIGHT_PER_TRACK[0] += weight * tracks[0]
|
|
TOTAL_BWEIGHT_PER_TRACK[1] += weight * tracks[1]
|
|
TOTAL_BWEIGHT_PER_TRACK[2] += weight * tracks[2]
|
|
TOTAL_BWEIGHT_PER_TRACK[3] += weight * tracks[3]
|
|
TOTAL_BWEIGHT_PER_HEIGHT[0] += weight * heights[0]
|
|
TOTAL_BWEIGHT_PER_HEIGHT[1] += weight * heights[1]
|
|
TOTAL_BWEIGHT_PER_HEIGHT[2] += weight * heights[2]
|
|
TOTAL_BWEIGHT_PER_HEIGHT[3] += weight * heights[3]
|
|
TOTAL_BWEIGHT_PER_HEIGHT[4] += weight * heights[4]
|
|
|
|
defaultSuitName = simbase.config.GetString('suit-type', 'random')
|
|
if defaultSuitName == 'random':
|
|
defaultSuitName = None
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedSuitPlannerAI')
|
|
|
|
def __init__(self, air, zoneId):
|
|
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
|
SuitPlannerBase.SuitPlannerBase.__init__(self)
|
|
self.air = air
|
|
self.zoneId = zoneId
|
|
self.canonicalZoneId = ZoneUtil.getCanonicalZoneId(zoneId)
|
|
if simbase.air.wantCogdominiums:
|
|
if not hasattr(self.__class__, 'CogdoPopAdjusted'):
|
|
self.__class__.CogdoPopAdjusted = True
|
|
for index in range(len(self.SuitHoodInfo)):
|
|
hoodInfo = self.SuitHoodInfo[index]
|
|
hoodInfo[self.SUIT_HOOD_INFO_BMIN] = int(0.5 + self.CogdoPopFactor * hoodInfo[self.SUIT_HOOD_INFO_BMIN])
|
|
hoodInfo[self.SUIT_HOOD_INFO_BMAX] = int(0.5 + self.CogdoPopFactor * hoodInfo[self.SUIT_HOOD_INFO_BMAX])
|
|
|
|
self.hoodInfoIdx = -1
|
|
for index in range(len(self.SuitHoodInfo)):
|
|
currHoodInfo = self.SuitHoodInfo[index]
|
|
if currHoodInfo[self.SUIT_HOOD_INFO_ZONE] == self.canonicalZoneId:
|
|
self.hoodInfoIdx = index
|
|
|
|
self.currDesired = None
|
|
self.baseNumSuits = (self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_MIN] + self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_MAX]) // 2
|
|
self.targetNumCogdos = 0
|
|
if simbase.air.wantCogdominiums:
|
|
self.targetNumCogdos = int(0.5 + self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_BMIN] * self.CogdoRatio)
|
|
if self.MinimumOfOne:
|
|
self.targetNumCogdos = max(self.targetNumCogdos, 1)
|
|
self.targetNumSuitBuildings = self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_BMIN]
|
|
self.targetNumSuitBuildings -= self.targetNumCogdos
|
|
if self.MinimumOfOne:
|
|
self.targetNumSuitBuildings = max(self.targetNumSuitBuildings, 1)
|
|
if ZoneUtil.isWelcomeValley(self.zoneId):
|
|
self.targetNumCogdos = 0
|
|
self.targetNumSuitBuildings = 0
|
|
self.pendingBuildingTracks = []
|
|
self.pendingBuildingHeights = []
|
|
self.pendingCogdoHeights = []
|
|
self.suitList = []
|
|
self.numFlyInSuits = 0
|
|
self.numBuildingSuits = 0
|
|
self.numAttemptingTakeover = 0
|
|
self.numAttemptingCogdoTakeover = 0
|
|
self.zoneInfo = {}
|
|
self.zoneIdToPointMap = None
|
|
self.cogHQDoors = []
|
|
self.battleList = []
|
|
self.battleMgr = BattleManagerAI.BattleManagerAI(self.air)
|
|
self.setupDNA()
|
|
if self.notify.getDebug():
|
|
self.notify.debug('Creating a building manager AI in zone' + str(self.zoneId))
|
|
self.buildingMgr = self.air.buildingManagers.get(self.zoneId)
|
|
if self.buildingMgr:
|
|
blocks, hqBlocks, gagshopBlocks, petshopBlocks, kartshopBlocks, animBldgBlocks = self.buildingMgr.getDNABlockLists()
|
|
for currBlock in blocks:
|
|
bldg = self.buildingMgr.getBuilding(currBlock)
|
|
bldg.setSuitPlannerExt(self)
|
|
|
|
for currBlock in animBldgBlocks:
|
|
bldg = self.buildingMgr.getBuilding(currBlock)
|
|
bldg.setSuitPlannerExt(self)
|
|
|
|
self.dnaStore.resetBlockNumbers()
|
|
self.initBuildingsAndPoints()
|
|
numSuits = simbase.config.GetInt('suit-count', -1)
|
|
if numSuits >= 0:
|
|
self.currDesired = numSuits
|
|
suitHood = simbase.config.GetInt('suits-only-in-hood', -1)
|
|
if suitHood >= 0:
|
|
if self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_ZONE] != suitHood:
|
|
self.currDesired = 0
|
|
self.suitCountAdjust = 0
|
|
return
|
|
|
|
def cleanup(self):
|
|
taskMgr.remove(self.taskName('sptUpkeepPopulation'))
|
|
taskMgr.remove(self.taskName('sptAdjustPopulation'))
|
|
for suit in self.suitList:
|
|
suit.stopTasks()
|
|
if suit.isGenerated():
|
|
self.zoneChange(suit, suit.zoneId)
|
|
suit.requestDelete()
|
|
|
|
self.suitList = []
|
|
self.numFlyInSuits = 0
|
|
self.numBuildingSuits = 0
|
|
self.numAttemptingTakeover = 0
|
|
self.numAttemptingCogdoTakeover = 0
|
|
|
|
def delete(self):
|
|
self.cleanup()
|
|
DistributedObjectAI.DistributedObjectAI.delete(self)
|
|
|
|
def initBuildingsAndPoints(self):
|
|
if not self.buildingMgr:
|
|
return
|
|
if self.notify.getDebug():
|
|
self.notify.debug('Initializing building points')
|
|
self.buildingFrontDoors = {}
|
|
self.buildingSideDoors = {}
|
|
for p in self.frontdoorPointList:
|
|
blockNumber = p.getLandmarkBuildingIndex()
|
|
if p.getPointType() < 0:
|
|
self.notify.warning('No landmark building for (%s) in zone %d' % (repr(p), self.zoneId))
|
|
elif blockNumber in self.buildingFrontDoors:
|
|
self.notify.warning('Multiple front doors for building %d in zone %d' % (blockNumber, self.zoneId))
|
|
else:
|
|
self.buildingFrontDoors[blockNumber] = p
|
|
|
|
for p in self.sidedoorPointList:
|
|
blockNumber = p.getLandmarkBuildingIndex()
|
|
if p.getPointType() < 0:
|
|
self.notify.warning('No landmark building for (%s) in zone %d' % (repr(p), self.zoneId))
|
|
elif blockNumber in self.buildingSideDoors:
|
|
self.buildingSideDoors[blockNumber].append(p)
|
|
else:
|
|
self.buildingSideDoors[blockNumber] = [
|
|
p]
|
|
|
|
for bldg in self.buildingMgr.getBuildings():
|
|
if isinstance(bldg, HQBuildingAI.HQBuildingAI):
|
|
continue
|
|
blockNumber = bldg.getBlock()[0]
|
|
if blockNumber not in self.buildingFrontDoors:
|
|
self.notify.warning('No front door for building %d in zone %d' % (blockNumber, self.zoneId))
|
|
if blockNumber not in self.buildingSideDoors:
|
|
self.notify.warning('No side door for building %d in zone %d' % (blockNumber, self.zoneId))
|
|
|
|
def countNumSuitsPerTrack(self, count):
|
|
for suit in self.suitList:
|
|
if suit.track in count:
|
|
count[suit.track] += 1
|
|
else:
|
|
count[suit.track] = 1
|
|
|
|
def countNumBuildingsPerTrack(self, count):
|
|
if self.buildingMgr:
|
|
for building in self.buildingMgr.getBuildings():
|
|
if building.isSuitBuilding():
|
|
if building.track in count:
|
|
count[building.track] += 1
|
|
else:
|
|
count[building.track] = 1
|
|
|
|
def countNumBuildingsPerHeight(self, count):
|
|
if self.buildingMgr:
|
|
for building in self.buildingMgr.getBuildings():
|
|
if building.isSuitBuilding():
|
|
height = building.numFloors - 1
|
|
if height in count:
|
|
count[height] += 1
|
|
else:
|
|
count[height] = 1
|
|
|
|
def formatNumSuitsPerTrack(self, count):
|
|
result = ' '
|
|
for track, num in list(count.items()):
|
|
result += ' %s:%d' % (track, num)
|
|
|
|
return result[2:]
|
|
|
|
def calcDesiredNumFlyInSuits(self):
|
|
if self.currDesired != None:
|
|
return 0
|
|
return self.baseNumSuits + self.suitCountAdjust
|
|
|
|
def calcDesiredNumBuildingSuits(self):
|
|
if self.currDesired != None:
|
|
return self.currDesired
|
|
if not self.buildingMgr:
|
|
return 0
|
|
suitBuildings = self.buildingMgr.getEstablishedSuitBlocks()
|
|
return int(len(suitBuildings) * self.SUIT_BUILDING_NUM_SUITS)
|
|
|
|
def getZoneIdToPointMap(self):
|
|
if self.zoneIdToPointMap != None:
|
|
return self.zoneIdToPointMap
|
|
self.zoneIdToPointMap = {}
|
|
for point in self.streetPointList:
|
|
points = self.dnaStore.getAdjacentPoints(point)
|
|
i = points.getNumPoints() - 1
|
|
while i >= 0:
|
|
pi = points.getPointIndex(i)
|
|
p = self.pointIndexes[pi]
|
|
i -= 1
|
|
zoneName = self.dnaStore.getSuitEdgeZone(point.getIndex(), p.getIndex())
|
|
zoneId = int(self.extractGroupName(zoneName))
|
|
if zoneId in self.zoneIdToPointMap:
|
|
self.zoneIdToPointMap[zoneId].append(point)
|
|
else:
|
|
self.zoneIdToPointMap[zoneId] = [
|
|
point]
|
|
|
|
return self.zoneIdToPointMap
|
|
|
|
def getStreetPointsForBuilding(self, blockNumber):
|
|
pointList = []
|
|
if blockNumber in self.buildingSideDoors:
|
|
for doorPoint in self.buildingSideDoors[blockNumber]:
|
|
points = self.dnaStore.getAdjacentPoints(doorPoint)
|
|
i = points.getNumPoints() - 1
|
|
while i >= 0:
|
|
pi = points.getPointIndex(i)
|
|
point = self.pointIndexes[pi]
|
|
if point.getPointType() == DNASuitPoint.STREETPOINT:
|
|
pointList.append(point)
|
|
i -= 1
|
|
|
|
if blockNumber in self.buildingFrontDoors:
|
|
doorPoint = self.buildingFrontDoors[blockNumber]
|
|
points = self.dnaStore.getAdjacentPoints(doorPoint)
|
|
i = points.getNumPoints() - 1
|
|
while i >= 0:
|
|
pi = points.getPointIndex(i)
|
|
pointList.append(self.pointIndexes[pi])
|
|
i -= 1
|
|
|
|
return pointList
|
|
|
|
def createNewSuit(self, blockNumbers, streetPoints, toonBlockTakeover=None, cogdoTakeover=None, minPathLen=None, maxPathLen=None, buildingHeight=None, suitLevel=None, suitType=None, suitTrack=None, suitName=None, skelecog=None, revives=None):
|
|
startPoint = None
|
|
blockNumber = None
|
|
if self.notify.getDebug():
|
|
self.notify.debug('Choosing origin from %d+%d possibles.' % (len(streetPoints), len(blockNumbers)))
|
|
while startPoint == None and len(blockNumbers) > 0:
|
|
bn = random.choice(blockNumbers)
|
|
blockNumbers.remove(bn)
|
|
if bn in self.buildingSideDoors:
|
|
for doorPoint in self.buildingSideDoors[bn]:
|
|
points = self.dnaStore.getAdjacentPoints(doorPoint)
|
|
i = points.getNumPoints() - 1
|
|
while blockNumber == None and i >= 0:
|
|
pi = points.getPointIndex(i)
|
|
p = self.pointIndexes[pi]
|
|
i -= 1
|
|
startTime = SuitTimings.fromSuitBuilding
|
|
startTime += self.dnaStore.getSuitEdgeTravelTime(doorPoint.getIndex(), pi, self.suitWalkSpeed)
|
|
if not self.pointCollision(p, doorPoint, startTime):
|
|
startTime = SuitTimings.fromSuitBuilding
|
|
startPoint = doorPoint
|
|
blockNumber = bn
|
|
|
|
while startPoint == None and len(streetPoints) > 0:
|
|
p = random.choice(streetPoints)
|
|
streetPoints.remove(p)
|
|
if not self.pointCollision(p, None, SuitTimings.fromSky):
|
|
startPoint = p
|
|
startTime = SuitTimings.fromSky
|
|
|
|
if startPoint == None:
|
|
return
|
|
newSuit = DistributedSuitAI.DistributedSuitAI(simbase.air, self)
|
|
newSuit.startPoint = startPoint
|
|
if blockNumber != None:
|
|
newSuit.buildingSuit = 1
|
|
if suitTrack == None:
|
|
suitTrack = self.buildingMgr.getBuildingTrack(blockNumber)
|
|
else:
|
|
newSuit.flyInSuit = 1
|
|
newSuit.attemptingTakeover = self.newSuitShouldAttemptTakeover()
|
|
if newSuit.attemptingTakeover:
|
|
cogdosNeeded = self.countNumNeededCogdos()
|
|
bldgsNeeded = self.countNumNeededBuildings()
|
|
cogdosAvailable = cogdosNeeded - self.numAttemptingCogdoTakeover
|
|
bldgsAvailable = bldgsNeeded - (self.numAttemptingTakeover - self.numAttemptingCogdoTakeover)
|
|
totalAvailable = cogdosAvailable + bldgsAvailable
|
|
if cogdoTakeover is None:
|
|
cogdoTakeover = False
|
|
if simbase.air.wantCogdominiums:
|
|
if totalAvailable > 0:
|
|
r = random.randrange(totalAvailable)
|
|
if r < cogdosAvailable:
|
|
cogdoTakeover = True
|
|
newSuit.takeoverIsCogdo = cogdoTakeover
|
|
if newSuit.takeoverIsCogdo:
|
|
pendingTracks = [
|
|
's']
|
|
pendingHeights = self.pendingCogdoHeights
|
|
else:
|
|
pendingTracks = self.pendingBuildingTracks
|
|
pendingHeights = self.pendingBuildingHeights
|
|
if suitTrack == None and len(pendingTracks) > 0:
|
|
suitTrack = pendingTracks[0]
|
|
del pendingTracks[0]
|
|
pendingTracks.append(suitTrack)
|
|
if buildingHeight == None and len(pendingHeights) > 0:
|
|
buildingHeight = pendingHeights[0]
|
|
del pendingHeights[0]
|
|
pendingHeights.append(buildingHeight)
|
|
else:
|
|
if cogdoTakeover and suitTrack == None:
|
|
suitTrack = random.choice(['s'])
|
|
if suitName == None:
|
|
if not cogdoTakeover:
|
|
suitName, skelecog = self.air.suitInvasionManager.getInvadingCog()
|
|
if suitName == None:
|
|
suitName = self.defaultSuitName
|
|
if suitType == None and suitName != None:
|
|
suitType = SuitDNA.getSuitType(suitName)
|
|
suitTrack = SuitDNA.getSuitDept(suitName)
|
|
if suitLevel == None and buildingHeight != None:
|
|
if not cogdoTakeover:
|
|
suitLevel = self.chooseSuitLevel(self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_LVL], buildingHeight)
|
|
else:
|
|
suitLevel = self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_LVL][-1] + 1
|
|
suitLevel, suitType, suitTrack = self.pickLevelTypeAndTrack(suitLevel, suitType, suitTrack)
|
|
newSuit.setupSuitDNA(suitLevel, suitType, suitTrack)
|
|
newSuit.buildingHeight = buildingHeight
|
|
gotDestination = self.chooseDestination(newSuit, startTime, toonBlockTakeover=toonBlockTakeover, cogdoTakeover=cogdoTakeover, minPathLen=minPathLen, maxPathLen=maxPathLen)
|
|
if not gotDestination:
|
|
self.notify.debug("Couldn't get a destination in %d!" % self.zoneId)
|
|
newSuit.doNotDeallocateChannel = None
|
|
newSuit.delete()
|
|
return
|
|
newSuit.initializePath()
|
|
self.zoneChange(newSuit, None, newSuit.zoneId)
|
|
if skelecog:
|
|
newSuit.setSkelecog(skelecog)
|
|
if revives:
|
|
newSuit.setSkeleRevives(revives)
|
|
newSuit.generateWithRequired(newSuit.zoneId)
|
|
newSuit.moveToNextLeg(None)
|
|
self.suitList.append(newSuit)
|
|
if newSuit.flyInSuit:
|
|
self.numFlyInSuits += 1
|
|
if newSuit.buildingSuit:
|
|
self.numBuildingSuits += 1
|
|
if newSuit.attemptingTakeover:
|
|
self.numAttemptingTakeover += 1
|
|
if newSuit.takeoverIsCogdo:
|
|
self.numAttemptingCogdoTakeover += 1
|
|
return newSuit
|
|
|
|
def countNumNeededBuildings(self):
|
|
if not self.buildingMgr:
|
|
return 0
|
|
numSuitBuildings = len(self.buildingMgr.getSuitBlocks()) - len(self.buildingMgr.getCogdoBlocks())
|
|
numNeeded = self.targetNumSuitBuildings - numSuitBuildings
|
|
return numNeeded
|
|
|
|
def countNumNeededCogdos(self):
|
|
if not self.buildingMgr:
|
|
return 0
|
|
numCogdos = len(self.buildingMgr.getCogdoBlocks())
|
|
numNeeded = self.targetNumCogdos - numCogdos
|
|
return numNeeded
|
|
|
|
def newSuitShouldAttemptTakeover(self):
|
|
if not self.SUITS_ENTER_BUILDINGS:
|
|
return 0
|
|
numNeeded = self.countNumNeededBuildings()
|
|
if simbase.air.wantCogdominiums:
|
|
numNeeded += self.countNumNeededCogdos()
|
|
if self.numAttemptingTakeover >= numNeeded:
|
|
self.pendingBuildingTracks = []
|
|
return 0
|
|
self.notify.debug('DSP %d is planning a takeover attempt in zone %d' % (self.getDoId(), self.zoneId))
|
|
return 1
|
|
|
|
def chooseDestination(self, suit, startTime, toonBlockTakeover=None, cogdoTakeover=None, minPathLen=None, maxPathLen=None):
|
|
possibles = []
|
|
backup = []
|
|
if cogdoTakeover is None:
|
|
cogdoTakeover = False
|
|
if toonBlockTakeover != None:
|
|
suit.attemptingTakeover = 1
|
|
blockNumber = toonBlockTakeover
|
|
if blockNumber in self.buildingFrontDoors:
|
|
possibles.append((blockNumber, self.buildingFrontDoors[blockNumber]))
|
|
elif suit.attemptingTakeover:
|
|
for blockNumber in self.buildingMgr.getToonBlocks():
|
|
building = self.buildingMgr.getBuilding(blockNumber)
|
|
extZoneId, intZoneId = building.getExteriorAndInteriorZoneId()
|
|
if not NPCToons.isZoneProtected(intZoneId):
|
|
if blockNumber in self.buildingFrontDoors:
|
|
possibles.append((blockNumber, self.buildingFrontDoors[blockNumber]))
|
|
|
|
else:
|
|
if self.buildingMgr:
|
|
for blockNumber in self.buildingMgr.getSuitBlocks():
|
|
track = self.buildingMgr.getBuildingTrack(blockNumber)
|
|
if track == suit.track and blockNumber in self.buildingSideDoors:
|
|
for doorPoint in self.buildingSideDoors[blockNumber]:
|
|
possibles.append((blockNumber, doorPoint))
|
|
|
|
backup = []
|
|
for p in self.streetPointList:
|
|
backup.append((None, p))
|
|
|
|
if self.notify.getDebug():
|
|
self.notify.debug('Choosing destination point from %d+%d possibles.' % (len(possibles), len(backup)))
|
|
if len(possibles) == 0:
|
|
possibles = backup
|
|
backup = []
|
|
if minPathLen == None:
|
|
if suit.attemptingTakeover:
|
|
minPathLen = self.MIN_TAKEOVER_PATH_LEN
|
|
else:
|
|
minPathLen = self.MIN_PATH_LEN
|
|
if maxPathLen == None:
|
|
maxPathLen = self.MAX_PATH_LEN
|
|
retryCount = 0
|
|
while len(possibles) > 0 and retryCount < 50:
|
|
p = random.choice(possibles)
|
|
possibles.remove(p)
|
|
if len(possibles) == 0:
|
|
possibles = backup
|
|
backup = []
|
|
path = self.genPath(suit.startPoint, p[1], minPathLen, maxPathLen)
|
|
if path and not self.pathCollision(path, startTime):
|
|
suit.endPoint = p[1]
|
|
suit.minPathLen = minPathLen
|
|
suit.maxPathLen = maxPathLen
|
|
suit.buildingDestination = p[0]
|
|
suit.buildingDestinationIsCogdo = cogdoTakeover
|
|
suit.setPath(path)
|
|
return 1
|
|
retryCount += 1
|
|
|
|
return 0
|
|
|
|
def pathCollision(self, path, elapsedTime):
|
|
pathLength = path.getNumPoints()
|
|
i = 0
|
|
pi = path.getPointIndex(i)
|
|
point = self.pointIndexes[pi]
|
|
adjacentPoint = self.pointIndexes[path.getPointIndex(i + 1)]
|
|
while point.getPointType() == DNASuitPoint.FRONTDOORPOINT or point.getPointType() == DNASuitPoint.SIDEDOORPOINT:
|
|
i += 1
|
|
lastPi = pi
|
|
pi = path.getPointIndex(i)
|
|
adjacentPoint = point
|
|
point = self.pointIndexes[pi]
|
|
elapsedTime += self.dnaStore.getSuitEdgeTravelTime(lastPi, pi, self.suitWalkSpeed)
|
|
|
|
result = self.pointCollision(point, adjacentPoint, elapsedTime)
|
|
return result
|
|
|
|
def pointCollision(self, point, adjacentPoint, elapsedTime):
|
|
for suit in self.suitList:
|
|
if suit.pointInMyPath(point, elapsedTime):
|
|
return 1
|
|
|
|
if adjacentPoint != None:
|
|
return self.battleCollision(point, adjacentPoint)
|
|
else:
|
|
points = self.dnaStore.getAdjacentPoints(point)
|
|
i = points.getNumPoints() - 1
|
|
while i >= 0:
|
|
pi = points.getPointIndex(i)
|
|
p = self.pointIndexes[pi]
|
|
i -= 1
|
|
if self.battleCollision(point, p):
|
|
return 1
|
|
|
|
return 0
|
|
|
|
def battleCollision(self, point, adjacentPoint):
|
|
zoneName = self.dnaStore.getSuitEdgeZone(point.getIndex(), adjacentPoint.getIndex())
|
|
zoneId = int(self.extractGroupName(zoneName))
|
|
return self.battleMgr.cellHasBattle(zoneId)
|
|
|
|
def removeSuit(self, suit):
|
|
self.zoneChange(suit, suit.zoneId)
|
|
if self.suitList.count(suit) > 0:
|
|
self.suitList.remove(suit)
|
|
if suit.flyInSuit:
|
|
self.numFlyInSuits -= 1
|
|
if suit.buildingSuit:
|
|
self.numBuildingSuits -= 1
|
|
if suit.attemptingTakeover:
|
|
self.numAttemptingTakeover -= 1
|
|
if suit.takeoverIsCogdo:
|
|
self.numAttemptingCogdoTakeover -= 1
|
|
suit.requestDelete()
|
|
|
|
def countTakeovers(self):
|
|
count = 0
|
|
for suit in self.suitList:
|
|
if suit.attemptingTakeover:
|
|
count += 1
|
|
|
|
return count
|
|
|
|
def countCogdoTakeovers(self):
|
|
count = 0
|
|
for suit in self.suitList:
|
|
if suit.attemptingTakeover and suit.takeoverIsCogdo:
|
|
count += 1
|
|
|
|
return count
|
|
|
|
def __waitForNextUpkeep(self):
|
|
t = random.random() * 2.0 + self.POP_UPKEEP_DELAY
|
|
taskMgr.doMethodLater(t, self.upkeepSuitPopulation, self.taskName('sptUpkeepPopulation'))
|
|
|
|
def __waitForNextAdjust(self):
|
|
t = random.random() * 10.0 + self.POP_ADJUST_DELAY
|
|
taskMgr.doMethodLater(t, self.adjustSuitPopulation, self.taskName('sptAdjustPopulation'))
|
|
|
|
def upkeepSuitPopulation(self, task):
|
|
targetFlyInNum = self.calcDesiredNumFlyInSuits()
|
|
targetFlyInNum = min(targetFlyInNum, self.TOTAL_MAX_SUITS - self.numBuildingSuits)
|
|
streetPoints = self.streetPointList[:]
|
|
flyInDeficit = (targetFlyInNum - self.numFlyInSuits + 3) // 4
|
|
while flyInDeficit > 0:
|
|
if not self.createNewSuit([], streetPoints):
|
|
break
|
|
flyInDeficit -= 1
|
|
|
|
if self.buildingMgr:
|
|
suitBuildings = self.buildingMgr.getEstablishedSuitBlocks()
|
|
else:
|
|
suitBuildings = []
|
|
if self.currDesired != None:
|
|
targetBuildingNum = max(0, self.currDesired - self.numFlyInSuits)
|
|
else:
|
|
targetBuildingNum = int(len(suitBuildings) * self.SUIT_BUILDING_NUM_SUITS)
|
|
targetBuildingNum += flyInDeficit
|
|
targetBuildingNum = min(targetBuildingNum, self.TOTAL_MAX_SUITS - self.numFlyInSuits)
|
|
buildingDeficit = (targetBuildingNum - self.numBuildingSuits + 3) // 4
|
|
while buildingDeficit > 0:
|
|
if not self.createNewSuit(suitBuildings, streetPoints):
|
|
break
|
|
buildingDeficit -= 1
|
|
|
|
if self.notify.getDebug() and self.currDesired == None:
|
|
self.notify.debug('zone %d has %d of %d fly-in and %d of %d building suits.' % (self.zoneId, self.numFlyInSuits, targetFlyInNum, self.numBuildingSuits, targetBuildingNum))
|
|
if buildingDeficit != 0:
|
|
self.notify.debug('remaining deficit is %d.' % buildingDeficit)
|
|
if self.buildingMgr:
|
|
suitBuildings = self.buildingMgr.getEstablishedSuitBlocks()
|
|
timeoutIndex = min(len(suitBuildings), len(self.SUIT_BUILDING_TIMEOUT) - 1)
|
|
timeout = self.SUIT_BUILDING_TIMEOUT[timeoutIndex]
|
|
if timeout != None:
|
|
timeout *= 3600.0
|
|
oldest = None
|
|
oldestAge = 0
|
|
now = time.time()
|
|
for b in suitBuildings:
|
|
building = self.buildingMgr.getBuilding(b)
|
|
if hasattr(building, 'elevator'):
|
|
if building.elevator.fsm.getCurrentState().getName() == 'waitEmpty':
|
|
age = now - building.becameSuitTime
|
|
if age > oldestAge:
|
|
oldest = building
|
|
oldestAge = age
|
|
|
|
if oldestAge > timeout:
|
|
self.notify.info('Street %d has %d buildings; reclaiming %0.2f-hour-old building.' % (self.zoneId, len(suitBuildings), oldestAge / 3600.0))
|
|
oldest.b_setVictorList([0, 0, 0, 0])
|
|
oldest.updateSavedBy([])
|
|
oldest.toonTakeOver()
|
|
self.__waitForNextUpkeep()
|
|
return Task.done
|
|
|
|
def adjustSuitPopulation(self, task):
|
|
hoodInfo = self.SuitHoodInfo[self.hoodInfoIdx]
|
|
if hoodInfo[self.SUIT_HOOD_INFO_MAX] == 0:
|
|
self.__waitForNextAdjust()
|
|
return Task.done
|
|
min = hoodInfo[self.SUIT_HOOD_INFO_MIN]
|
|
max = hoodInfo[self.SUIT_HOOD_INFO_MAX]
|
|
adjustment = random.choice((-2, -1, -1, 0, 0, 0, 1, 1, 2))
|
|
self.suitCountAdjust += adjustment
|
|
desiredNum = self.calcDesiredNumFlyInSuits()
|
|
if desiredNum < min:
|
|
self.suitCountAdjust = min - self.baseNumSuits
|
|
else:
|
|
if desiredNum > max:
|
|
self.suitCountAdjust = max - self.baseNumSuits
|
|
self.__waitForNextAdjust()
|
|
return Task.done
|
|
|
|
def suitTakeOver(self, blockNumber, suitTrack, difficulty, buildingHeight):
|
|
if self.pendingBuildingTracks.count(suitTrack) > 0:
|
|
self.pendingBuildingTracks.remove(suitTrack)
|
|
if self.pendingBuildingHeights.count(buildingHeight) > 0:
|
|
self.pendingBuildingHeights.remove(buildingHeight)
|
|
building = self.buildingMgr.getBuilding(blockNumber)
|
|
building.suitTakeOver(suitTrack, difficulty, buildingHeight)
|
|
|
|
def cogdoTakeOver(self, blockNumber, suitTrack, difficulty, buildingHeight):
|
|
if self.pendingCogdoHeights.count(buildingHeight) > 0:
|
|
self.pendingCogdoHeights.remove(buildingHeight)
|
|
building = self.buildingMgr.getBuilding(blockNumber)
|
|
building.cogdoTakeOver(suitTrack, difficulty, buildingHeight)
|
|
|
|
def recycleBuilding(self, isCogdo):
|
|
bmin = self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_BMIN]
|
|
current = len(self.buildingMgr.getSuitBlocks())
|
|
target = self.targetNumSuitBuildings + self.targetNumCogdos
|
|
if target > bmin and current <= target:
|
|
if isCogdo:
|
|
if self.targetNumCogdos > choice(self.MinimumOfOne, 1, 0):
|
|
self.targetNumCogdos -= 1
|
|
self.assignCogdos(1)
|
|
elif self.targetNumSuitBuildings > choice(self.MinimumOfOne, 1, 0):
|
|
self.targetNumSuitBuildings -= 1
|
|
self.assignSuitBuildings(1)
|
|
|
|
def assignInitialSuitBuildings(self):
|
|
totalBuildings = 0
|
|
targetSuitBuildings = 0
|
|
actualSuitBuildings = 0
|
|
targetCogdos = 0
|
|
actualCogdos = 0
|
|
for sp in list(self.air.suitPlanners.values()):
|
|
totalBuildings += len(sp.frontdoorPointList)
|
|
targetSuitBuildings += sp.targetNumSuitBuildings
|
|
targetCogdos += sp.targetNumCogdos
|
|
if sp.buildingMgr:
|
|
numCogdoBlocks = len(sp.buildingMgr.getCogdoBlocks())
|
|
actualSuitBuildings += len(sp.buildingMgr.getSuitBlocks()) - numCogdoBlocks
|
|
actualCogdos += numCogdoBlocks
|
|
|
|
wantedSuitBuildings = int(totalBuildings * self.TOTAL_SUIT_BUILDING_PCT / 100)
|
|
if simbase.air.wantCogdominiums:
|
|
wantedCogdos = int(wantedSuitBuildings * self.CogdoRatio)
|
|
wantedSuitBuildings -= wantedCogdos
|
|
else:
|
|
wantedCogdos = 0
|
|
self.notify.debug('Want %d out of %d total suit buildings; we currently have %d assigned, %d actual.' % (wantedSuitBuildings, totalBuildings, targetSuitBuildings, actualSuitBuildings))
|
|
if actualSuitBuildings > 0:
|
|
numReassigned = 0
|
|
for sp in list(self.air.suitPlanners.values()):
|
|
if sp.buildingMgr:
|
|
numBuildings = len(sp.buildingMgr.getSuitBlocks()) - len(sp.buildingMgr.getCogdoBlocks())
|
|
else:
|
|
numBuildings = 0
|
|
if numBuildings > sp.targetNumSuitBuildings:
|
|
more = numBuildings - sp.targetNumSuitBuildings
|
|
sp.targetNumSuitBuildings += more
|
|
targetSuitBuildings += more
|
|
numReassigned += more
|
|
|
|
if numReassigned > 0:
|
|
self.notify.debug('Assigned %d buildings where suit buildings already existed.' % numReassigned)
|
|
if simbase.air.wantCogdominiums:
|
|
if actualCogdos > 0:
|
|
numReassigned = 0
|
|
for sp in list(self.air.suitPlanners.values()):
|
|
if sp.buildingMgr:
|
|
numCogdos = len(sp.buildingMgr.getCogdoBlocks())
|
|
else:
|
|
numCogdos = 0
|
|
if numCogdos > sp.targetNumCogdos:
|
|
more = numCogdos - sp.targetNumCogdos
|
|
sp.targetNumCogdos += more
|
|
targetCogdos += more
|
|
numReassigned += more
|
|
|
|
if numReassigned > 0:
|
|
self.notify.debug('Assigned %d cogdos where cogdos already existed.' % numReassigned)
|
|
if wantedSuitBuildings > targetSuitBuildings:
|
|
additionalBuildings = wantedSuitBuildings - targetSuitBuildings
|
|
self.assignSuitBuildings(additionalBuildings)
|
|
else:
|
|
if wantedSuitBuildings < targetSuitBuildings:
|
|
extraBuildings = targetSuitBuildings - wantedSuitBuildings
|
|
self.unassignSuitBuildings(extraBuildings)
|
|
if simbase.air.wantCogdominiums:
|
|
if wantedCogdos > targetCogdos:
|
|
additionalCogdos = wantedCogdos - targetCogdos
|
|
self.assignCogdos(additionalCogdos)
|
|
elif wantedCogdos < targetCogdos:
|
|
extraCogdos = targetCogdos - wantedCogdos
|
|
self.unassignCogdos(extraCogdos)
|
|
|
|
def assignSuitBuildings(self, numToAssign):
|
|
hoodInfo = self.SuitHoodInfo[:]
|
|
totalWeight = self.TOTAL_BWEIGHT
|
|
totalWeightPerTrack = self.TOTAL_BWEIGHT_PER_TRACK[:]
|
|
totalWeightPerHeight = self.TOTAL_BWEIGHT_PER_HEIGHT[:]
|
|
numPerTrack = {'c': 0, 'l': 0, 'm': 0, 's': 0}
|
|
for sp in list(self.air.suitPlanners.values()):
|
|
sp.countNumBuildingsPerTrack(numPerTrack)
|
|
numPerTrack['c'] += sp.pendingBuildingTracks.count('c')
|
|
numPerTrack['l'] += sp.pendingBuildingTracks.count('l')
|
|
numPerTrack['m'] += sp.pendingBuildingTracks.count('m')
|
|
numPerTrack['s'] += sp.pendingBuildingTracks.count('s')
|
|
|
|
numPerHeight = {0: 0, 1: 0, 2: 0, 3: 0, 4: 0}
|
|
for sp in list(self.air.suitPlanners.values()):
|
|
sp.countNumBuildingsPerHeight(numPerHeight)
|
|
numPerHeight[0] += sp.pendingBuildingHeights.count(0)
|
|
numPerHeight[1] += sp.pendingBuildingHeights.count(1)
|
|
numPerHeight[2] += sp.pendingBuildingHeights.count(2)
|
|
numPerHeight[3] += sp.pendingBuildingHeights.count(3)
|
|
numPerHeight[4] += sp.pendingBuildingHeights.count(4)
|
|
|
|
while numToAssign > 0:
|
|
smallestCount = None
|
|
smallestTracks = []
|
|
for trackIndex in range(4):
|
|
if totalWeightPerTrack[trackIndex]:
|
|
track = SuitDNA.suitDepts[trackIndex]
|
|
count = numPerTrack[track]
|
|
if smallestCount == None or count < smallestCount:
|
|
smallestTracks = [
|
|
track]
|
|
smallestCount = count
|
|
elif count == smallestCount:
|
|
smallestTracks.append(track)
|
|
|
|
if not smallestTracks:
|
|
self.notify.info('No more room for buildings, with %s still to assign.' % numToAssign)
|
|
return
|
|
buildingTrack = random.choice(smallestTracks)
|
|
buildingTrackIndex = SuitDNA.suitDepts.index(buildingTrack)
|
|
smallestCount = None
|
|
smallestHeights = []
|
|
for height in range(5):
|
|
if totalWeightPerHeight[height]:
|
|
count = float(numPerHeight[height]) / float(self.BUILDING_HEIGHT_DISTRIBUTION[height])
|
|
if smallestCount == None or count < smallestCount:
|
|
smallestHeights = [
|
|
height]
|
|
smallestCount = count
|
|
elif count == smallestCount:
|
|
smallestHeights.append(height)
|
|
|
|
if not smallestHeights:
|
|
self.notify.info('No more room for buildings, with %s still to assign.' % numToAssign)
|
|
return
|
|
buildingHeight = random.choice(smallestHeights)
|
|
self.notify.info('Existing buildings are (%s, %s), choosing from (%s, %s), chose %s, %s.' % (self.formatNumSuitsPerTrack(numPerTrack), self.formatNumSuitsPerTrack(numPerHeight), smallestTracks, smallestHeights, buildingTrack, buildingHeight))
|
|
repeat = 1
|
|
while repeat and buildingTrack != None and buildingHeight != None:
|
|
if len(hoodInfo) == 0:
|
|
self.notify.warning('No more streets can have suit buildings, with %d buildings unassigned!' % numToAssign)
|
|
return
|
|
repeat = 0
|
|
currHoodInfo = self.chooseStreetWithPreference(hoodInfo, buildingTrackIndex, buildingHeight)
|
|
zoneId = currHoodInfo[self.SUIT_HOOD_INFO_ZONE]
|
|
if zoneId in self.air.suitPlanners:
|
|
sp = self.air.suitPlanners[zoneId]
|
|
numCogdos = sp.targetNumCogdos
|
|
numBldgs = sp.targetNumSuitBuildings
|
|
numTotalBuildings = len(sp.frontdoorPointList)
|
|
else:
|
|
numCogdos = 0
|
|
numBldgs = 0
|
|
numTotalBuildings = 0
|
|
if numCogdos + numBldgs >= currHoodInfo[self.SUIT_HOOD_INFO_BMAX] or numCogdos + numBldgs >= numTotalBuildings:
|
|
self.notify.info('Zone %d has enough buildings.' % zoneId)
|
|
hoodInfo.remove(currHoodInfo)
|
|
weight = currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT]
|
|
tracks = currHoodInfo[self.SUIT_HOOD_INFO_TRACK]
|
|
heights = currHoodInfo[self.SUIT_HOOD_INFO_HEIGHTS]
|
|
totalWeight -= weight
|
|
totalWeightPerTrack[0] -= weight * tracks[0]
|
|
totalWeightPerTrack[1] -= weight * tracks[1]
|
|
totalWeightPerTrack[2] -= weight * tracks[2]
|
|
totalWeightPerTrack[3] -= weight * tracks[3]
|
|
totalWeightPerHeight[0] -= weight * heights[0]
|
|
totalWeightPerHeight[1] -= weight * heights[1]
|
|
totalWeightPerHeight[2] -= weight * heights[2]
|
|
totalWeightPerHeight[3] -= weight * heights[3]
|
|
totalWeightPerHeight[4] -= weight * heights[4]
|
|
if totalWeightPerTrack[buildingTrackIndex] <= 0:
|
|
buildingTrack = None
|
|
if totalWeightPerHeight[buildingHeight] <= 0:
|
|
buildingHeight = None
|
|
repeat = 1
|
|
|
|
if buildingTrack != None and buildingHeight != None:
|
|
sp.targetNumSuitBuildings += 1
|
|
sp.pendingBuildingTracks.append(buildingTrack)
|
|
sp.pendingBuildingHeights.append(buildingHeight)
|
|
self.notify.info('Assigning building to zone %d, pending tracks = %s, pending heights = %s' % (zoneId, sp.pendingBuildingTracks, sp.pendingBuildingHeights))
|
|
numPerTrack[buildingTrack] += 1
|
|
numPerHeight[buildingHeight] += 1
|
|
numToAssign -= 1
|
|
|
|
return
|
|
|
|
def unassignSuitBuildings(self, numToAssign):
|
|
hoodInfo = self.SuitHoodInfo[:]
|
|
totalWeight = self.TOTAL_BWEIGHT
|
|
while numToAssign > 0:
|
|
repeat = 1
|
|
while repeat:
|
|
if len(hoodInfo) == 0:
|
|
self.notify.warning('No more streets can remove suit buildings, with %d buildings too many!' % numToAssign)
|
|
return
|
|
repeat = 0
|
|
currHoodInfo = self.chooseStreetNoPreference(hoodInfo, totalWeight)
|
|
zoneId = currHoodInfo[self.SUIT_HOOD_INFO_ZONE]
|
|
if zoneId in self.air.suitPlanners:
|
|
sp = self.air.suitPlanners[zoneId]
|
|
numCogdos = sp.targetNumCogdos
|
|
numBldgs = sp.targetNumSuitBuildings
|
|
numTotalBuildings = len(sp.frontdoorPointList)
|
|
else:
|
|
numCogdos = 0
|
|
numBldgs = 0
|
|
numTotalBuildings = 0
|
|
overallStrapped = numCogdos + numBldgs <= currHoodInfo[self.SUIT_HOOD_INFO_BMIN]
|
|
bldgStrapped = numBldgs <= choice(self.MinimumOfOne, 1, 0)
|
|
if overallStrapped or bldgStrapped:
|
|
self.notify.info("Zone %d can't remove any more buildings." % zoneId)
|
|
hoodInfo.remove(currHoodInfo)
|
|
totalWeight -= currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT]
|
|
repeat = 1
|
|
|
|
self.notify.info('Unassigning building from zone %d.' % zoneId)
|
|
sp.targetNumSuitBuildings -= 1
|
|
numToAssign -= 1
|
|
|
|
def assignCogdos(self, numToAssign):
|
|
hoodInfo = self.SuitHoodInfo[:]
|
|
totalWeight = self.TOTAL_BWEIGHT
|
|
while numToAssign > 0:
|
|
while 1:
|
|
if len(hoodInfo) == 0:
|
|
self.notify.warning('No more streets can have cogdos, with %d cogdos unassigned!' % numToAssign)
|
|
return
|
|
currHoodInfo = self.chooseStreetNoPreference(hoodInfo, totalWeight)
|
|
zoneId = currHoodInfo[self.SUIT_HOOD_INFO_ZONE]
|
|
if zoneId in self.air.suitPlanners:
|
|
sp = self.air.suitPlanners[zoneId]
|
|
numCogdos = sp.targetNumCogdos
|
|
numBldgs = sp.targetNumSuitBuildings
|
|
numTotalBuildings = len(sp.frontdoorPointList)
|
|
else:
|
|
numCogdos = 0
|
|
numBldgs = 0
|
|
numTotalBuildings = 0
|
|
if numCogdos + numBldgs >= currHoodInfo[self.SUIT_HOOD_INFO_BMAX] or numCogdos + numBldgs >= numTotalBuildings:
|
|
self.notify.info('Zone %d has enough cogdos.' % zoneId)
|
|
hoodInfo.remove(currHoodInfo)
|
|
weight = currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT]
|
|
totalWeight -= weight
|
|
continue
|
|
break
|
|
|
|
sp.targetNumCogdos += 1
|
|
sp.pendingCogdoHeights.append(DistributedBuildingAI.FieldOfficeNumFloors)
|
|
self.notify.info('Assigning cogdo to zone %d' % zoneId)
|
|
numToAssign -= 1
|
|
|
|
def unassignCogdos(self, numToAssign):
|
|
hoodInfo = self.SuitHoodInfo[:]
|
|
totalWeight = self.TOTAL_BWEIGHT
|
|
while numToAssign > 0:
|
|
while 1:
|
|
currHoodInfo = self.chooseStreetNoPreference(hoodInfo, totalWeight)
|
|
zoneId = currHoodInfo[self.SUIT_HOOD_INFO_ZONE]
|
|
if zoneId in self.air.suitPlanners:
|
|
sp = self.air.suitPlanners[zoneId]
|
|
numCogdos = sp.targetNumCogdos
|
|
numBldgs = sp.targetNumSuitBuildings
|
|
numTotalBuildings = len(sp.frontdoorPointList)
|
|
else:
|
|
numCogdos = 0
|
|
numBldgs = 0
|
|
numTotalBuildings = 0
|
|
overallStrapped = numCogdos + numBldgs <= currHoodInfo[self.SUIT_HOOD_INFO_BMIN]
|
|
cogdoStrapped = numCogdos <= choice(self.MinimumOfOne, 1, 0)
|
|
if overallStrapped or cogdoStrapped:
|
|
self.notify.info("Zone %s can't remove any more cogdos." % zoneId)
|
|
hoodInfo.remove(currHoodInfo)
|
|
totalWeight -= currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT]
|
|
continue
|
|
break
|
|
|
|
self.notify.info('Unassigning cogdo from zone %s.' % zoneId)
|
|
sp.targetNumCogdos -= 1
|
|
numToAssign -= 1
|
|
|
|
def chooseStreetNoPreference(self, hoodInfo, totalWeight):
|
|
c = random.random() * totalWeight
|
|
t = 0
|
|
for currHoodInfo in hoodInfo:
|
|
weight = currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT]
|
|
t += weight
|
|
if c < t:
|
|
return currHoodInfo
|
|
|
|
self.notify.warning('Weighted random choice failed! Total is %s, chose %s' % (t, c))
|
|
return random.choice(hoodInfo)
|
|
|
|
def chooseStreetWithPreference(self, hoodInfo, buildingTrackIndex, buildingHeight):
|
|
dist = []
|
|
for currHoodInfo in hoodInfo:
|
|
weight = currHoodInfo[self.SUIT_HOOD_INFO_BWEIGHT]
|
|
thisValue = weight * currHoodInfo[self.SUIT_HOOD_INFO_TRACK][buildingTrackIndex] * currHoodInfo[self.SUIT_HOOD_INFO_HEIGHTS][buildingHeight]
|
|
dist.append(thisValue)
|
|
|
|
totalWeight = sum(dist)
|
|
c = random.random() * totalWeight
|
|
t = 0
|
|
for i in range(len(hoodInfo)):
|
|
t += dist[i]
|
|
if c < t:
|
|
return hoodInfo[i]
|
|
|
|
self.notify.warning('Weighted random choice failed! Total is %s, chose %s' % (t, c))
|
|
return random.choice(hoodInfo)
|
|
|
|
def chooseSuitLevel(self, possibleLevels, buildingHeight):
|
|
choices = []
|
|
for level in possibleLevels:
|
|
minFloors, maxFloors = SuitBuildingGlobals.SuitBuildingInfo[level - 1][0]
|
|
if buildingHeight >= minFloors - 1 and buildingHeight <= maxFloors - 1:
|
|
choices.append(level)
|
|
|
|
return random.choice(choices)
|
|
|
|
def initTasks(self):
|
|
self.__waitForNextUpkeep()
|
|
self.__waitForNextAdjust()
|
|
|
|
def resyncSuits(self):
|
|
for suit in self.suitList:
|
|
suit.resync()
|
|
|
|
def flySuits(self):
|
|
for suit in self.suitList:
|
|
if suit.pathState == 1:
|
|
suit.flyAwayNow()
|
|
|
|
def requestBattle(self, zoneId, suit, toonId):
|
|
self.notify.debug('requestBattle() - zone: %d suit: %d toon: %d' % (zoneId, suit.doId, toonId))
|
|
canonicalZoneId = ZoneUtil.getCanonicalZoneId(zoneId)
|
|
if canonicalZoneId not in self.battlePosDict:
|
|
return 0
|
|
toon = self.air.doId2do.get(toonId)
|
|
if toon.getBattleId() > 0:
|
|
self.notify.warning('We tried to request a battle when the toon was already in battle')
|
|
return 0
|
|
if toon:
|
|
if hasattr(toon, 'doId'):
|
|
print((
|
|
'Setting toonID ', toonId))
|
|
toon.b_setBattleId(toonId)
|
|
pos = self.battlePosDict[canonicalZoneId]
|
|
interactivePropTrackBonus = -1
|
|
if simbase.config.GetBool('props-buff-battles', True) and canonicalZoneId in self.cellToGagBonusDict:
|
|
tentativeBonusTrack = self.cellToGagBonusDict[canonicalZoneId]
|
|
trackToHolidayDict = {ToontownBattleGlobals.SQUIRT_TRACK: ToontownGlobals.HYDRANTS_BUFF_BATTLES, ToontownBattleGlobals.THROW_TRACK: ToontownGlobals.MAILBOXES_BUFF_BATTLES, ToontownBattleGlobals.HEAL_TRACK: ToontownGlobals.TRASHCANS_BUFF_BATTLES}
|
|
if tentativeBonusTrack in trackToHolidayDict:
|
|
holidayId = trackToHolidayDict[tentativeBonusTrack]
|
|
if simbase.air.holidayManager.isHolidayRunning(holidayId) and simbase.air.holidayManager.getCurPhase(holidayId) >= 1:
|
|
interactivePropTrackBonus = tentativeBonusTrack
|
|
self.battleMgr.newBattle(zoneId, zoneId, pos, suit, toonId, self.__battleFinished, self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_SMAX], interactivePropTrackBonus)
|
|
for currOther in self.zoneInfo[zoneId]:
|
|
self.notify.debug('Found suit %d in this new battle zone %d' % (currOther.getDoId(), zoneId))
|
|
if currOther != suit:
|
|
if currOther.pathState == 1 and currOther.legType == SuitLeg.TWalk:
|
|
self.checkForBattle(zoneId, currOther)
|
|
|
|
return 1
|
|
|
|
def __battleFinished(self, zoneId):
|
|
self.notify.debug('DistSuitPlannerAI: battle in zone ' + str(zoneId) + ' finished')
|
|
currBattleIdx = 0
|
|
while currBattleIdx < len(self.battleList):
|
|
currBattle = self.battleList[currBattleIdx]
|
|
if currBattle[0] == zoneId:
|
|
self.notify.debug('DistSuitPlannerAI: battle removed')
|
|
self.battleList.remove(currBattle)
|
|
else:
|
|
currBattleIdx = currBattleIdx + 1
|
|
|
|
return None
|
|
|
|
def __suitCanJoinBattle(self, zoneId):
|
|
battle = self.battleMgr.getBattle(zoneId)
|
|
if len(battle.suits) >= 4:
|
|
return 0
|
|
if battle:
|
|
if simbase.config.GetBool('suits-always-join', 0):
|
|
return 1
|
|
jChanceList = self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_JCHANCE]
|
|
ratioIdx = len(battle.toons) - battle.numSuitsEver + 2
|
|
if ratioIdx >= 0:
|
|
if ratioIdx < len(jChanceList):
|
|
if random.randint(0, 99) < jChanceList[ratioIdx]:
|
|
return 1
|
|
else:
|
|
self.notify.warning('__suitCanJoinBattle idx out of range!')
|
|
return 1
|
|
return 0
|
|
|
|
def checkForBattle(self, zoneId, suit):
|
|
if self.battleMgr.cellHasBattle(zoneId):
|
|
if self.__suitCanJoinBattle(zoneId) and self.battleMgr.requestBattleAddSuit(zoneId, suit):
|
|
pass
|
|
else:
|
|
suit.flyAwayNow()
|
|
return 1
|
|
else:
|
|
return 0
|
|
|
|
def postBattleResumeCheck(self, suit):
|
|
self.notify.debug('DistSuitPlannerAI:postBattleResumeCheck: suit ' + str(suit.getDoId()) + ' is leaving battle')
|
|
battleIndex = 0
|
|
for currBattle in self.battleList:
|
|
if suit.zoneId == currBattle[0]:
|
|
self.notify.debug(' battle found' + str(suit.zoneId))
|
|
for currPath in currBattle[1]:
|
|
for currPathPtSuit in range(suit.currWpt, suit.myPath.getNumPoints()):
|
|
ptIdx = suit.myPath.getPointIndex(currPathPtSuit)
|
|
if self.notify.getDebug():
|
|
self.notify.debug(' comparing' + str(ptIdx) + 'with' + str(currPath))
|
|
if currPath == ptIdx:
|
|
if self.notify.getDebug():
|
|
self.notify.debug(' match found, telling' + 'suit to fly')
|
|
return 0
|
|
|
|
else:
|
|
battleIndex = battleIndex + 1
|
|
|
|
pointList = []
|
|
for currPathPtSuit in range(suit.currWpt, suit.myPath.getNumPoints()):
|
|
ptIdx = suit.myPath.getPointIndex(currPathPtSuit)
|
|
if self.notify.getDebug():
|
|
self.notify.debug(' appending point with index of' + str(ptIdx))
|
|
pointList.append(ptIdx)
|
|
|
|
self.battleList.append([suit.zoneId, pointList])
|
|
return 1
|
|
|
|
def zoneChange(self, suit, oldZone, newZone=None):
|
|
if oldZone in self.zoneInfo and suit in self.zoneInfo[oldZone]:
|
|
self.zoneInfo[oldZone].remove(suit)
|
|
if newZone != None:
|
|
if newZone not in self.zoneInfo:
|
|
self.zoneInfo[newZone] = []
|
|
self.zoneInfo[newZone].append(suit)
|
|
return
|
|
|
|
def d_setZoneId(self, zoneId):
|
|
self.sendUpdate('setZoneId', [self.getZoneId()])
|
|
|
|
def getZoneId(self):
|
|
return self.zoneId
|
|
|
|
def suitListQuery(self):
|
|
suitIndexList = []
|
|
for suit in self.suitList:
|
|
suitIndexList.append(SuitDNA.suitHeadTypes.index(suit.dna.name))
|
|
|
|
self.sendUpdateToAvatarId(self.air.getAvatarIdFromSender(), 'suitListResponse', [suitIndexList])
|
|
|
|
def buildingListQuery(self):
|
|
buildingDict = {}
|
|
self.countNumBuildingsPerTrack(buildingDict)
|
|
buildingList = [0, 0, 0, 0]
|
|
for dept in SuitDNA.suitDepts:
|
|
if dept in buildingDict:
|
|
buildingList[SuitDNA.suitDepts.index(dept)] = buildingDict[dept]
|
|
|
|
self.sendUpdateToAvatarId(self.air.getAvatarIdFromSender(), 'buildingListResponse', [buildingList])
|
|
|
|
def pickLevelTypeAndTrack(self, level=None, type=None, track=None):
|
|
if level == None:
|
|
level = random.choice(self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_LVL])
|
|
if type == None:
|
|
typeChoices = list(range(max(level - 4, 1), min(level, self.MAX_SUIT_TYPES) + 1))
|
|
type = random.choice(typeChoices)
|
|
else:
|
|
level = min(max(level, type), type + 4)
|
|
if track == None:
|
|
track = SuitDNA.suitDepts[SuitBattleGlobals.pickFromFreqList(self.SuitHoodInfo[self.hoodInfoIdx][self.SUIT_HOOD_INFO_TRACK])]
|
|
self.notify.debug('pickLevelTypeAndTrack: %d %d %s' % (level, type, track))
|
|
return (
|
|
level, type, track)
|
|
|
|
@classmethod
|
|
def dump(cls):
|
|
s = ''
|
|
totalBldgs = 0
|
|
totalCogdos = 0
|
|
targetTotalBldgs = 0
|
|
targetTotalCogdos = 0
|
|
for index in range(len(cls.SuitHoodInfo)):
|
|
currHoodInfo = cls.SuitHoodInfo[index]
|
|
zoneId, min, max, bmin, bmax, bweight, smax, jchance, track, lvl, heights = currHoodInfo
|
|
sp = simbase.air.suitPlanners[zoneId]
|
|
targetCogdos = sp.targetNumCogdos
|
|
targetBldgs = sp.targetNumSuitBuildings
|
|
bm = simbase.air.buildingManagers.get(zoneId)
|
|
if bm:
|
|
numCogdos = len(bm.getCogdoBlocks())
|
|
numBldgs = len(bm.getSuitBlocks()) - numCogdos
|
|
s += ' %s: %2s/%2s buildings, %2s/%2s cogdos\n' % (zoneId, numBldgs, targetBldgs, numCogdos, targetCogdos)
|
|
totalBldgs += numBldgs
|
|
totalCogdos += numCogdos
|
|
targetTotalBldgs += targetBldgs
|
|
targetTotalCogdos += targetCogdos
|
|
|
|
header = '%s\n' % (cls.__name__,)
|
|
header += ' %s/%s buildings, %s/%s cogdos\n' % (totalBldgs, targetTotalBldgs, totalCogdos, targetTotalCogdos)
|
|
s = header + s
|
|
print(s)
|