| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-25 14:30:38 -04:00
										 |  |  | import random, copy | 
					
						
							| 
									
										
										
										
											2023-04-08 16:52:34 -04:00
										 |  |  | from ..utils import log | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  | from ..graph.graph_utils import GraphUtils, vanillaTransitions, vanillaBossesTransitions, escapeSource, escapeTargets, graphAreas, getAccessPoint | 
					
						
							| 
									
										
										
										
											2023-04-08 16:52:34 -04:00
										 |  |  | from ..logic.logic import Logic | 
					
						
							|  |  |  | from ..graph.graph import AccessGraphRando as AccessGraph | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  | from ..logic.smbool import SMBool | 
					
						
							|  |  |  | from ..utils.objectives import Objectives | 
					
						
							|  |  |  | from ..rando.ItemLocContainer import getItemLocStr | 
					
						
							|  |  |  | from collections import defaultdict | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | # creates graph and handles randomized escape | 
					
						
							|  |  |  | class GraphBuilder(object): | 
					
						
							|  |  |  |     def __init__(self, graphSettings): | 
					
						
							|  |  |  |         self.graphSettings = graphSettings | 
					
						
							|  |  |  |         self.areaRando = graphSettings.areaRando | 
					
						
							|  |  |  |         self.bossRando = graphSettings.bossRando | 
					
						
							|  |  |  |         self.escapeRando = graphSettings.escapeRando | 
					
						
							|  |  |  |         self.minimizerN = graphSettings.minimizerN | 
					
						
							| 
									
										
										
										
											2023-03-25 14:30:38 -04:00
										 |  |  |         self.log = log.get('GraphBuilder') | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # builds everything but escape transitions | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |     def createGraph(self, maxDiff): | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         transitions = self.graphSettings.plandoRandoTransitions | 
					
						
							|  |  |  |         if transitions is None: | 
					
						
							|  |  |  |             transitions = [] | 
					
						
							|  |  |  |             if self.minimizerN is not None: | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |                 forcedAreas = set() | 
					
						
							|  |  |  |                 # if no Crateria and auto escape trigger, we connect door connected to G4 to climb instead (see below). | 
					
						
							|  |  |  |                 # This wouldn't work here, as Tourian is isolated in the resulting seed (see below again) | 
					
						
							|  |  |  |                 # (well we could do two different transitions on both sides of doors, but that would just be confusing) | 
					
						
							|  |  |  |                 # so we force crateria to be in the graph | 
					
						
							|  |  |  |                 if self.graphSettings.startAP == "Golden Four" and self.graphSettings.tourian == "Disabled": | 
					
						
							|  |  |  |                     forcedAreas.add('Crateria') | 
					
						
							|  |  |  |                 # force areas required by objectives | 
					
						
							|  |  |  |                 # 1st the 'clear area' ones | 
					
						
							|  |  |  |                 forcedAreas = forcedAreas.union({goal.area for goal in Objectives.objDict[self.graphSettings.player].activeGoals if goal.area is not None}) | 
					
						
							|  |  |  |                 # for the rest, base ourselves on escapeAccessPoints : | 
					
						
							|  |  |  |                 # - if only "1 of n" pick an area, preferably one already forced | 
					
						
							|  |  |  |                 # - filter out G4 AP (always there) | 
					
						
							|  |  |  |                 for goal in Objectives.objDict[self.graphSettings.player].activeGoals: | 
					
						
							|  |  |  |                     if goal.area is None: | 
					
						
							|  |  |  |                         n, apNames = goal.escapeAccessPoints | 
					
						
							|  |  |  |                         aps = [getAccessPoint(apName) for apName in apNames] | 
					
						
							|  |  |  |                         if len(aps) >= n: | 
					
						
							|  |  |  |                             n -= len([ap for ap in aps if ap.Boss]) | 
					
						
							|  |  |  |                             escAreas = {ap.GraphArea for ap in aps if not ap.Boss} | 
					
						
							|  |  |  |                             objForced = forcedAreas.intersection(escAreas) | 
					
						
							|  |  |  |                             escAreasList = sorted(list(escAreas)) | 
					
						
							|  |  |  |                             while len(objForced) < n and len(escAreasList) > 0: | 
					
						
							|  |  |  |                                 objForced.add(escAreasList.pop(random.randint(0, len(escAreasList)-1))) | 
					
						
							|  |  |  |                             forcedAreas = forcedAreas.union(objForced) | 
					
						
							|  |  |  |                 transitions = GraphUtils.createMinimizerTransitions(self.graphSettings.startAP, self.minimizerN, sorted(list(forcedAreas))) | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             else: | 
					
						
							|  |  |  |                 if not self.bossRando: | 
					
						
							|  |  |  |                     transitions += vanillaBossesTransitions | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     transitions += GraphUtils.createBossesTransitions() | 
					
						
							|  |  |  |                 if not self.areaRando: | 
					
						
							|  |  |  |                     transitions += vanillaTransitions | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     transitions += GraphUtils.createAreaTransitions(self.graphSettings.lightAreaRando) | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |         ret = AccessGraph(Logic.accessPoints, transitions, self.graphSettings.dotFile) | 
					
						
							|  |  |  |         Objectives.objDict[self.graphSettings.player].setGraph(ret, maxDiff) | 
					
						
							|  |  |  |         return ret | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     def addForeignItems(self, container, itemLocs): | 
					
						
							|  |  |  |         itemPoolCounts = {} | 
					
						
							|  |  |  |         for item in container.itemPool: | 
					
						
							|  |  |  |             if item.Code is not None: | 
					
						
							|  |  |  |                 itemPoolCounts[item.Type] = itemPoolCounts.get(item.Type, 0) + 1 | 
					
						
							|  |  |  |         itemLocsCounts = {} | 
					
						
							|  |  |  |         for il in itemLocs: | 
					
						
							|  |  |  |             if il.Item.Code is not None and il.player == container.sm.player: | 
					
						
							|  |  |  |                 itemLocsCounts[il.Item.Type] = itemLocsCounts.get(il.Item.Type, 0) + 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for item, count in itemPoolCounts.items(): | 
					
						
							|  |  |  |             for n in range(max(0, count - itemLocsCounts.get(item, 0))): | 
					
						
							|  |  |  |                 container.sm.addItem(item) | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # fills in escape transitions if escape rando is enabled | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |     # escapeTrigger = None or (itemLocs, progItemlocs) couple from filler | 
					
						
							|  |  |  |     def escapeGraph(self, container, graph, maxDiff, escapeTrigger): | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         if not self.escapeRando: | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         emptyContainer = copy.copy(container) | 
					
						
							|  |  |  |         emptyContainer.resetCollected(reassignItemLocs=True) | 
					
						
							|  |  |  |         dst = None | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |         if escapeTrigger is None: | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             possibleTargets, dst, path = self.getPossibleEscapeTargets(emptyContainer, graph, maxDiff) | 
					
						
							|  |  |  |             # update graph with escape transition | 
					
						
							|  |  |  |             graph.addTransition(escapeSource, dst) | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             paths = [path] | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             self.addForeignItems(emptyContainer, escapeTrigger[0]) | 
					
						
							|  |  |  |             possibleTargets, paths = self.escapeTrigger(emptyContainer, graph, maxDiff, escapeTrigger) | 
					
						
							|  |  |  |             if paths is None: | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |                 return False | 
					
						
							|  |  |  |         # get timer value | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |         self.escapeTimer(graph, paths, self.areaRando or escapeTrigger is not None) | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         self.log.debug("escapeGraph: ({}, {}) timer: {}".format(escapeSource, dst, graph.EscapeAttributes['Timer'])) | 
					
						
							|  |  |  |         # animals | 
					
						
							|  |  |  |         GraphUtils.escapeAnimalsTransitions(graph, possibleTargets, dst) | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _getTargets(self, sm, graph, maxDiff): | 
					
						
							|  |  |  |         possibleTargets = [target for target in escapeTargets if graph.accessPath(sm, target, 'Landing Site', maxDiff) is not None] | 
					
						
							|  |  |  |         self.log.debug('_getTargets. targets='+str(possibleTargets)) | 
					
						
							|  |  |  |         # failsafe | 
					
						
							|  |  |  |         if len(possibleTargets) == 0: | 
					
						
							|  |  |  |             self.log.debug("Can't randomize escape, fallback to vanilla") | 
					
						
							|  |  |  |             possibleTargets.append('Climb Bottom Left') | 
					
						
							|  |  |  |         random.shuffle(possibleTargets) | 
					
						
							|  |  |  |         return possibleTargets | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def getPossibleEscapeTargets(self, emptyContainer, graph, maxDiff): | 
					
						
							|  |  |  |         sm = emptyContainer.sm | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |         # setup smbm with item pool: | 
					
						
							|  |  |  |         # - Ice not usable because of hyper beam | 
					
						
							|  |  |  |         # - remove energy to avoid hell runs | 
					
						
							|  |  |  |         # - (will add bosses as well) | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         sm.addItems([item.Type for item in emptyContainer.itemPool if item.Type != 'Ice' and item.Category != 'Energy']) | 
					
						
							|  |  |  |         sm.addItem('Hyper') | 
					
						
							|  |  |  |         possibleTargets = self._getTargets(sm, graph, maxDiff) | 
					
						
							|  |  |  |         # pick one | 
					
						
							|  |  |  |         dst = possibleTargets.pop() | 
					
						
							|  |  |  |         path = graph.accessPath(sm, dst, 'Landing Site', maxDiff) | 
					
						
							|  |  |  |         return (possibleTargets, dst, path) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |     def escapeTrigger(self, emptyContainer, graph, maxDiff, escapeTrigger): | 
					
						
							|  |  |  |         container = emptyContainer | 
					
						
							|  |  |  |         sm = container.sm | 
					
						
							|  |  |  |         allItemLocs,progItemLocs,split = escapeTrigger[0],escapeTrigger[1],escapeTrigger[2] | 
					
						
							|  |  |  |         # check if crateria is connected, if not replace Tourian | 
					
						
							|  |  |  |         # connection with Climb and add special escape patch to Climb | 
					
						
							|  |  |  |         if not any(il.Location.GraphArea == "Crateria" for il in allItemLocs): | 
					
						
							|  |  |  |             escapeAttr = graph.EscapeAttributes | 
					
						
							|  |  |  |             if "patches" not in escapeAttr: | 
					
						
							|  |  |  |                 escapeAttr['patches'] = [] | 
					
						
							|  |  |  |             escapeAttr['patches'] += ['climb_disable_bomb_blocks.ips', "Climb_Asleep"] | 
					
						
							|  |  |  |             src, _ = next(t for t in graph.InterAreaTransitions if t[1].Name == "Golden Four") | 
					
						
							|  |  |  |             graph.removeTransitions("Golden Four") | 
					
						
							|  |  |  |             graph.addTransition(src.Name, "Climb Bottom Left") | 
					
						
							|  |  |  |             # disconnect the other side of G4 | 
					
						
							|  |  |  |             graph.addTransition("Golden Four", "Golden Four") | 
					
						
							|  |  |  |         # remove vanilla escape transition | 
					
						
							|  |  |  |         graph.addTransition('Tourian Escape Room 4 Top Right', 'Tourian Escape Room 4 Top Right') | 
					
						
							|  |  |  |         # filter garbage itemLocs | 
					
						
							|  |  |  |         ilCheck = lambda il: not il.Location.isBoss() and not il.Location.restricted and il.Item.Category != "Nothing" | 
					
						
							|  |  |  |         # update item% objectives | 
					
						
							|  |  |  |         accessibleItems = [il.Item for il in allItemLocs if ilCheck(il)] | 
					
						
							|  |  |  |         majorUpgrades = [item.Type for item in accessibleItems if item.BeamBits != 0 or item.ItemBits != 0] | 
					
						
							|  |  |  |         if split == "Scavenger": | 
					
						
							|  |  |  |             # update escape access for scav with last scav loc | 
					
						
							|  |  |  |             lastScavItemLoc = progItemLocs[-1] | 
					
						
							|  |  |  |             sm.objectives.updateScavengerEscapeAccess(lastScavItemLoc.Location.accessPoint) | 
					
						
							|  |  |  |             sm.objectives.setScavengerHuntFunc(lambda sm, ap: sm.haveItem(lastScavItemLoc.Item.Type)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # update "collect all items in areas" funcs | 
					
						
							|  |  |  |             availLocsByArea=defaultdict(list) | 
					
						
							|  |  |  |             for itemLoc in allItemLocs: | 
					
						
							|  |  |  |                 if ilCheck(itemLoc) and (split.startswith("Full") or itemLoc.Location.isClass(split)): | 
					
						
							|  |  |  |                     availLocsByArea[itemLoc.Location.GraphArea].append(itemLoc.Location.Name) | 
					
						
							|  |  |  |             self.log.debug("escapeTrigger. availLocsByArea="+str(availLocsByArea)) | 
					
						
							| 
									
										
										
										
											2023-09-19 19:26:42 -04:00
										 |  |  |             sm.objectives.setItemPercentFuncs(len(accessibleItems), majorUpgrades, container) | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             sm.objectives.setAreaFuncs({area:lambda sm,ap:SMBool(len(container.getLocs(lambda loc: loc.Name in availLocsByArea[area]))==0) for area in availLocsByArea}) | 
					
						
							|  |  |  |         self.log.debug("escapeTrigger. collect locs until G4 access") | 
					
						
							|  |  |  |         # collect all item/locations up until we can pass G4 (the escape triggers) | 
					
						
							|  |  |  |         itemLocs = allItemLocs[:] | 
					
						
							|  |  |  |         ap = "Landing Site" # dummy value it'll be overwritten at first collection | 
					
						
							|  |  |  |         while len(itemLocs) > 0 and not (sm.canPassG4() and graph.canAccess(sm, ap, "Landing Site", maxDiff)): | 
					
						
							|  |  |  |             il = itemLocs.pop(0) | 
					
						
							|  |  |  |             if il.Location.restricted or il.Item.Type == "ArchipelagoItem": | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             self.log.debug("collecting " + getItemLocStr(il)) | 
					
						
							|  |  |  |             container.collect(il) | 
					
						
							|  |  |  |             ap = il.Location.accessPoint | 
					
						
							|  |  |  |         # final update of item% obj | 
					
						
							|  |  |  |         collectedLocsAccessPoints = {il.Location.accessPoint for il in container.itemLocations} | 
					
						
							|  |  |  |         sm.objectives.updateItemPercentEscapeAccess(list(collectedLocsAccessPoints)) | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         possibleTargets = self._getTargets(sm, graph, maxDiff) | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |         # try to escape from all the possible objectives APs | 
					
						
							|  |  |  |         possiblePaths = [] | 
					
						
							|  |  |  |         for goal in Objectives.objDict[self.graphSettings.player].activeGoals: | 
					
						
							|  |  |  |             n, possibleAccessPoints = goal.escapeAccessPoints | 
					
						
							|  |  |  |             count = 0 | 
					
						
							|  |  |  |             for ap in possibleAccessPoints: | 
					
						
							|  |  |  |                 self.log.debug("escapeTrigger. testing AP " + ap) | 
					
						
							|  |  |  |                 path = graph.accessPath(sm, ap, 'Landing Site', maxDiff) | 
					
						
							|  |  |  |                 if path is not None: | 
					
						
							|  |  |  |                     self.log.debug("escapeTrigger. add path from "+ap) | 
					
						
							|  |  |  |                     possiblePaths.append(path) | 
					
						
							|  |  |  |                     count += 1 | 
					
						
							|  |  |  |             if count < n: | 
					
						
							|  |  |  |                 # there is a goal we cannot escape from | 
					
						
							|  |  |  |                 self.log.debug("escapeTrigger. goal %s: found %d/%d possible escapes, abort" % (goal.name, count, n)) | 
					
						
							|  |  |  |                 return (None, None) | 
					
						
							|  |  |  |         # try and get a path from all possible areas | 
					
						
							|  |  |  |         self.log.debug("escapeTrigger. completing paths") | 
					
						
							|  |  |  |         allAreas = {il.Location.GraphArea for il in allItemLocs if not il.Location.restricted and not il.Location.GraphArea in ["Tourian", "Ceres"]} | 
					
						
							|  |  |  |         def getStartArea(path): | 
					
						
							|  |  |  |             return path[0].GraphArea | 
					
						
							|  |  |  |         def apCheck(ap): | 
					
						
							|  |  |  |             nonlocal graph, possiblePaths | 
					
						
							|  |  |  |             apObj = graph.accessPoints[ap] | 
					
						
							|  |  |  |             return apObj.GraphArea not in [getStartArea(path) for path in possiblePaths] | 
					
						
							|  |  |  |         escapeAPs = [ap for ap in collectedLocsAccessPoints if apCheck(ap)] | 
					
						
							|  |  |  |         for ap in escapeAPs: | 
					
						
							|  |  |  |             path = graph.accessPath(sm, ap, 'Landing Site', maxDiff) | 
					
						
							|  |  |  |             if path is not None: | 
					
						
							|  |  |  |                 self.log.debug("escapeTrigger. add path from "+ap) | 
					
						
							|  |  |  |                 possiblePaths.append(path) | 
					
						
							|  |  |  |         def areaPathCheck(): | 
					
						
							|  |  |  |             nonlocal allAreas, possiblePaths | 
					
						
							|  |  |  |             startAreas = {getStartArea(path) for path in possiblePaths} | 
					
						
							|  |  |  |             return len(allAreas - startAreas) == 0 | 
					
						
							|  |  |  |         while not areaPathCheck() and len(itemLocs) > 0: | 
					
						
							|  |  |  |             il = itemLocs.pop(0) | 
					
						
							|  |  |  |             if il.Location.restricted or il.Item.Type == "ArchipelagoItem": | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             self.log.debug("collecting " + getItemLocStr(il)) | 
					
						
							|  |  |  |             container.collect(il) | 
					
						
							|  |  |  |             ap = il.Location.accessPoint | 
					
						
							|  |  |  |             if apCheck(ap): | 
					
						
							|  |  |  |                 path = graph.accessPath(sm, ap, 'Landing Site', maxDiff) | 
					
						
							|  |  |  |                 if path is not None: | 
					
						
							|  |  |  |                     self.log.debug("escapeTrigger. add path from "+ap) | 
					
						
							|  |  |  |                     possiblePaths.append(path) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return (possibleTargets, possiblePaths) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _computeTimer(self, graph, path): | 
					
						
							|  |  |  |         traversedAreas = list(set([ap.GraphArea for ap in path])) | 
					
						
							|  |  |  |         self.log.debug("escapeTimer path: " + str([ap.Name for ap in path])) | 
					
						
							|  |  |  |         self.log.debug("escapeTimer traversedAreas: " + str(traversedAreas)) | 
					
						
							|  |  |  |         # rough estimates of navigation within areas to reach "borders" | 
					
						
							|  |  |  |         # (can obviously be completely off wrt to actual path, but on the generous side) | 
					
						
							|  |  |  |         traversals = { | 
					
						
							|  |  |  |             'Crateria':90, | 
					
						
							|  |  |  |             'GreenPinkBrinstar':90, | 
					
						
							|  |  |  |             'WreckedShip':120, | 
					
						
							|  |  |  |             'LowerNorfair':135, | 
					
						
							|  |  |  |             'WestMaridia':75, | 
					
						
							|  |  |  |             'EastMaridia':100, | 
					
						
							|  |  |  |             'RedBrinstar':75, | 
					
						
							|  |  |  |             'Norfair': 120, | 
					
						
							|  |  |  |             'Kraid': 40, | 
					
						
							|  |  |  |             'Crocomire': 40, | 
					
						
							|  |  |  |             # can't be on the path | 
					
						
							|  |  |  |             'Tourian': 0, | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         t = 90 if self.areaRando else 0 | 
					
						
							|  |  |  |         for area in traversedAreas: | 
					
						
							|  |  |  |             t += traversals[area] | 
					
						
							|  |  |  |         t = max(t, 180) | 
					
						
							|  |  |  |         return t | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # path: as returned by AccessGraph.accessPath | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |     def escapeTimer(self, graph, paths, compute): | 
					
						
							|  |  |  |         if len(paths) == 1: | 
					
						
							|  |  |  |             path = paths.pop() | 
					
						
							|  |  |  |             if compute == True: | 
					
						
							|  |  |  |                 if path[0].Name == 'Climb Bottom Left': | 
					
						
							|  |  |  |                     graph.EscapeAttributes['Timer'] = None | 
					
						
							|  |  |  |                     return | 
					
						
							|  |  |  |                 t = self._computeTimer(graph, path) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 escapeTargetsTimer = { | 
					
						
							|  |  |  |                     'Climb Bottom Left': None, # vanilla | 
					
						
							|  |  |  |                     'Green Brinstar Main Shaft Top Left': 210, # brinstar | 
					
						
							|  |  |  |                     'Basement Left': 210, # wrecked ship | 
					
						
							|  |  |  |                     'Business Center Mid Left': 270, # norfair | 
					
						
							|  |  |  |                     'Crab Hole Bottom Right': 270 # maridia | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 t = escapeTargetsTimer[path[0].Name] | 
					
						
							|  |  |  |             self.log.debug("escapeTimer. t="+str(t)) | 
					
						
							|  |  |  |             graph.EscapeAttributes['Timer'] = t | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             assert compute | 
					
						
							|  |  |  |             graph.EscapeAttributes['Timer'] = 0 | 
					
						
							|  |  |  |             timerValues = {} | 
					
						
							|  |  |  |             graph.EscapeAttributes['TimerTable'] = timerValues | 
					
						
							|  |  |  |             for path in paths: | 
					
						
							|  |  |  |                 area = path[0].GraphArea | 
					
						
							|  |  |  |                 prev = timerValues.get(area, 0) | 
					
						
							|  |  |  |                 t = max(prev, self._computeTimer(graph, path)) | 
					
						
							|  |  |  |                 timerValues[area] = t | 
					
						
							|  |  |  |                 self.log.debug("escapeTimer. area=%s, t=%d" % (area, t)) | 
					
						
							|  |  |  |             for area in graphAreas[1:-1]:  # no Ceres or Tourian | 
					
						
							|  |  |  |                 if area not in timerValues: | 
					
						
							|  |  |  |                     # area not in graph most probably, still write a 10 minute "ultra failsafe" value | 
					
						
							|  |  |  |                     timerValues[area] = 600 |