def canUseCrocRoomToChargeSpeed(self): sm = self.smbm crocRoom = getAccessPoint('Crocomire Room Top') speedway = getAccessPoint('Crocomire Speedway Bottom') return sm.wand( SMBool(crocRoom.ConnectedTo == 'Crocomire Speedway Bottom'), crocRoom.traverse(sm), speedway.traverse(sm))
def loadTransitions(self, tourian): # return the transitions rooms = GraphUtils.getRooms() bossTransitions = {} areaTransitions = {} for accessPoint in Logic.accessPoints: if accessPoint.isInternal() == True: continue key = self.getTransition(accessPoint.ExitInfo['DoorPtr']) if key not in rooms: # can happen with race mode seeds continue destAP = rooms[key] if accessPoint.Boss == True or destAP.Boss == True: bossTransitions[accessPoint.Name] = destAP.Name else: areaTransitions[accessPoint.Name] = destAP.Name def removeBiTrans(transitions): # remove bidirectionnal transitions # can't del keys in a dict while iterating it transitionsCopy = copy.copy(transitions) for src in transitionsCopy: if src in transitions: dest = transitions[src] if dest in transitions: if transitions[dest] == src: del transitions[dest] return [(t, transitions[t]) for t in transitions] # get escape transition if tourian == 'Disabled': escapeSrcAP = getAccessPoint('Climb Bottom Left') else: escapeSrcAP = getAccessPoint('Tourian Escape Room 4 Top Right') key = self.getTransition(escapeSrcAP.ExitInfo['DoorPtr']) # may not be set in plandomizer if key in rooms: escapeDstAP = rooms[key] escapeTransition = [(escapeSrcAP.Name, escapeDstAP.Name)] else: escapeTransition = [] areaTransitions = removeBiTrans(areaTransitions) bossTransitions = removeBiTrans(bossTransitions) return (areaTransitions, bossTransitions, escapeTransition, GraphUtils.hasMixedTransitions(areaTransitions, bossTransitions))
def applyStartAP(self, apName, plms, doors): ap = getAccessPoint(apName) # if start loc is not Ceres or Landing Site, or the ceiling loc picked up before morph loc, # Zebes will be awake and morph loc item will disappear. # this PLM ensures the item will be here whenever zebes awakes plms.append('Morph_Zebes_Awake') (w0, w1) = getWord(ap.Start['spawn']) if 'doors' in ap.Start: doors += ap.Start['doors'] doors.append(0x0) addr = 0x10F200 patch = [w0, w1] + doors assert (addr + len(patch)) < 0x10F210, "Stopped before new_game overwrite" patchDict = { 'StartAP': { addr: patch }, } self.applyIPSPatch('StartAP', patchDict) # handle custom saves if 'save' in ap.Start: self.applyIPSPatch(ap.Start['save']) plms.append(ap.Start['save']) # handle optional rom patches if 'rom_patches' in ap.Start: for patch in ap.Start['rom_patches']: self.applyIPSPatch(patch)
def applyStartAP(self, apName, plms, doors): ap = getAccessPoint(apName) if not GraphUtils.isStandardStart(apName): # not Ceres or Landing Site, so Zebes will be awake plms.append('Morph_Zebes_Awake') (w0, w1) = getWord(ap.Start['spawn']) if 'doors' in ap.Start: doors += ap.Start['doors'] doors.append(0x0) addr = 0x10F200 patch = [w0, w1] + doors assert (addr + len(patch)) < 0x10F210, "Stopped before new_game overwrite" patchDict = { 'StartAP': { addr: patch }, } self.applyIPSPatch('StartAP', patchDict) # handle custom saves if 'save' in ap.Start: self.applyIPSPatch(ap.Start['save']) plms.append(ap.Start['save']) # handle optional rom patches if 'rom_patches' in ap.Start: for patch in ap.Start['rom_patches']: self.applyIPSPatch(patch)
def isBoss(self): romFile = self.getROM() phOut = getAccessPoint('PhantoonRoomOut') doorPtr = phOut.ExitInfo['DoorPtr'] romFile.seek((0x10000 | doorPtr) + 10) asmPtr = romFile.readWord() return asmPtr != 0 # this is at 0 in vanilla
def updateLocationsClass(self, split): if split != 'Full' and split != 'Scavenger': startAP = getAccessPoint(self.graphSettings.startAP) possibleMajLocs, preserveMajLocs, nMaj, nChozo = Logic.LocationsHelper.getStartMajors( startAP.Name) if split == 'Major': n = nMaj elif split == 'Chozo': n = nChozo GraphUtils.updateLocClassesStart(startAP.GraphArea, split, possibleMajLocs, preserveMajLocs, n)
def checkStart(self): ap = getAccessPoint(self.startAP) if not self.graphSettings.areaRando or ap.Start is None or \ (('needsPreRando' not in ap.Start or not ap.Start['needsPreRando']) and\ ('areaMode' not in ap.Start or not ap.Start['areaMode'])): return True self.log.debug("********* PRE RANDO START") container = copy.copy(self.container) filler = FrontFiller(self.startAP, self.areaGraph, self.restrictions, container) condition = filler.createStepCountCondition(4) (isStuck, itemLocations, progItems) = filler.generateItems(condition) self.log.debug("********* PRE RANDO END") return not isStuck and len(self.services.currentLocations(filler.ap, filler.container)) > 0
def __init__(self, majorsSplit, startLocation, areaGraph, locations, vcr=None): self.interactive = False self.checkDuplicateMajor = False self.vcr = vcr # for compatibility with some common methods of the interactive solver self.mode = 'standard' self.log = utils.log.get('Solver') # default conf self.setConf(easy, 'all', [], False) self.firstLogFile = None self.extStatsFilename = None self.extStatsStep = None self.type = 'rando' self.output = Out.factory(self.type, self) self.outputFileName = None self.locations = locations self.smbm = SMBoolManager() # preset already loaded by rando self.presetFileName = None self.pickup = Pickup(Conf.itemsPickup) self.comeBack = ComeBack(self) # load ROM info, patches are already loaded by the rando. get the graph from the rando too self.majorsSplit = majorsSplit self.startLocation = startLocation self.startArea = getAccessPoint(startLocation).Start['solveArea'] self.areaGraph = areaGraph self.objectives = Objectives() # store at each step how many locations are available self.nbAvailLocs = [] # limit to a few seconds to avoid cases with a lot of rewinds which could last for minutes self.runtimeLimit_s = 5 self.startTime = time.process_time()
def getRawPatches(self): # for interactive solver result = {} for patchName in self.patches: value = self.romFile.readByte(self.patches[patchName]['address']) result[self.patches[patchName]['address']] = value # add boss detection bytes doorPtr = getAccessPoint('PhantoonRoomOut').ExitInfo['DoorPtr'] doorPtr = (0x10000 | doorPtr) + 10 result[doorPtr] = self.romFile.readByte(doorPtr) result[doorPtr+1] = self.romFile.readByte() return result
def getDefaultPatches(): # called by the isolver in seedless mode # activate only layout patch (the most common one) and blue bt/red tower blue doors ret = {} for patch in RomReader.patches: if patch in ['layout', 'startLS']: ret[RomReader.patches[patch]['address']] = RomReader.patches[patch]['value'] else: ret[RomReader.patches[patch]['address']] = 0xFF # add phantoon door ptr used by boss rando detection doorPtr = getAccessPoint('PhantoonRoomOut').ExitInfo['DoorPtr'] doorPtr = (0x10000 | doorPtr) + 10 ret[doorPtr] = 0 ret[doorPtr+1] = 0 return ret
def generateItem(self, comeback=ComebackCheckType.Undefined): comebackCheck = comeback if comeback != ComebackCheckType.Undefined else self.getComebackCheck() itemLocDict, possibleProg = self.services.getPossiblePlacements(self.ap, self.container, comebackCheck) if self.isEarlyGame() and possibleProg == True: # cheat a little bit if non-standard start: place early # progression away from crateria/blue brin if possible startAp = getAccessPoint(self.startAP) if startAp.GraphArea != "Crateria": newItemLocDict = {} for w, locs in itemLocDict.items(): filtered = [loc for loc in locs if loc.GraphArea != 'Crateria'] if len(filtered) > 0: newItemLocDict[w] = filtered if len(newItemLocDict) > 0: itemLocDict = newItemLocDict itemLoc = self.chooseItemLoc(itemLocDict, possibleProg) self.log.debug("generateItem. itemLoc="+"None" if itemLoc is None else getItemLocStr(itemLoc)) return itemLoc
def loadTransitions(self): # return the transitions rooms = GraphUtils.getRooms() bossTransitions = {} areaTransitions = {} for accessPoint in Logic.accessPoints: if accessPoint.isInternal() == True: continue key = self.getTransition(accessPoint.ExitInfo['DoorPtr']) destAP = rooms[key] if accessPoint.Boss == True or destAP.Boss == True: bossTransitions[accessPoint.Name] = destAP.Name else: areaTransitions[accessPoint.Name] = destAP.Name def removeBiTrans(transitions): # remove bidirectionnal transitions # can't del keys in a dict while iterating it transitionsCopy = copy.copy(transitions) for src in transitionsCopy: if src in transitions: dest = transitions[src] if dest in transitions: if transitions[dest] == src: del transitions[dest] return [(t, transitions[t]) for t in transitions] # get escape transition escapeSrcAP = getAccessPoint('Tourian Escape Room 4 Top Right') key = self.getTransition(escapeSrcAP.ExitInfo['DoorPtr']) escapeDstAP = rooms[key] escapeTransition = [(escapeSrcAP.Name, escapeDstAP.Name)] areaTransitions = removeBiTrans(areaTransitions) bossTransitions = removeBiTrans(bossTransitions) return (areaTransitions, bossTransitions, escapeTransition, GraphUtils.hasMixedTransitions(areaTransitions, bossTransitions))
def lateMorphInit(self, ap, emptyContainer, services): assert self.isLateMorph() morph = emptyContainer.getNextItemInPool('Morph') assert morph is not None locs = services.possibleLocations(morph, ap, emptyContainer, bossesKilled=False) self.lateMorphLimit = len(locs) self.log.debug('lateMorphInit. {} locs: {}'.format( self.lateMorphLimit, getLocListStr(locs))) areas = {} for loc in locs: areas[loc.GraphArea] = areas.get(loc.GraphArea, 0) + 1 self.log.debug('lateMorphLimit. areas: {}'.format(areas)) if len(areas) > 1: self.lateMorphForbiddenArea = getAccessPoint(ap).GraphArea self.log.debug('lateMorphLimit. forbid start area: {}'.format( self.lateMorphForbiddenArea)) else: self.lateMorphForbiddenArea = None
def isVanillaCroc(self): crocRoom = getAccessPoint('Crocomire Room Top') return SMBool(crocRoom.ConnectedTo == 'Crocomire Speedway Bottom')
def getDraygonConnection(self): return getAccessPoint('DraygonRoomOut').ConnectedTo
if apName in startLocationList ] optErrMsgs.append( 'Invalid start locations list with your settings.') dumpErrorMsgs(args.output, optErrMsgs) sys.exit(-1) args.startAP = random.choice(possibleStartAPs) elif args.startAP not in possibleStartAPs: optErrMsgs.append('Invalid start location: {}. {}'.format( args.startAP, reasons[args.startAP])) optErrMsgs.append( 'Possible start locations with these settings: {}'.format( possibleStartAPs)) dumpErrorMsgs(args.output, optErrMsgs) sys.exit(-1) ap = getAccessPoint(args.startAP) if 'forcedEarlyMorph' in ap.Start and ap.Start['forcedEarlyMorph'] == True: forceArg( 'morphPlacement', 'early', "'Morph Placement' forced to early for custom start location") else: if progSpeed == 'speedrun': if args.morphPlacement == 'late': forceArg('morphPlacement', 'normal', "'Morph Placement' forced to normal instead of late") elif (not GraphUtils.isStandardStart( args.startAP)) and args.morphPlacement != 'normal': forceArg( 'morphPlacement', 'normal', "'Morph Placement' forced to normal for custom start location" )
def loadRom(self, rom, interactive=False, magic=None, startLocation=None): self.scavengerOrder = [] self.plandoScavengerOrder = [] # startLocation param is only use for seedless if rom == None: # TODO::add a --logic parameter for seedless Logic.factory('vanilla') self.romFileName = 'seedless' self.majorsSplit = 'Full' self.masterMajorsSplit = 'Full' self.areaRando = True self.bossRando = True self.escapeRando = False self.escapeTimer = "03:00" self.startLocation = startLocation RomPatches.setDefaultPatches(startLocation) self.startArea = getAccessPoint(startLocation).Start['solveArea'] # in seedless load all the vanilla transitions self.areaTransitions = vanillaTransitions[:] self.bossTransitions = vanillaBossesTransitions[:] self.escapeTransition = [vanillaEscapeTransitions[0]] # in seedless we allow mixing of area and boss transitions self.hasMixedTransitions = True self.curGraphTransitions = self.bossTransitions + self.areaTransitions + self.escapeTransition self.locations = Logic.locations for loc in self.locations: loc.itemName = 'Nothing' # set doors related to default patches DoorsManager.setDoorsColor() self.doorsRando = False self.hasNothing = False self.objectives.setVanilla() self.tourian = 'Vanilla' else: self.romFileName = rom self.romLoader = RomLoader.factory(rom, magic) Logic.factory(self.romLoader.readLogic()) self.locations = Logic.locations (self.majorsSplit, self.masterMajorsSplit) = self.romLoader.assignItems( self.locations) (self.startLocation, self.startArea, startPatches) = self.romLoader.getStartAP() if not GraphUtils.isStandardStart( self.startLocation) and self.majorsSplit != 'Full': # update major/chozo locs in non standard start self.romLoader.updateSplitLocs(self.majorsSplit, self.locations) (self.areaRando, self.bossRando, self.escapeRando, hasObjectives, self.tourian) = self.romLoader.loadPatches() RomPatches.ActivePatches += startPatches self.escapeTimer = self.romLoader.getEscapeTimer() self.doorsRando = self.romLoader.loadDoorsColor() self.hasNothing = self.checkLocsForNothing() if self.majorsSplit == 'Scavenger': self.scavengerOrder = self.romLoader.loadScavengerOrder( self.locations) if hasObjectives: self.romLoader.loadObjectives(self.objectives) else: if self.majorsSplit == "Scavenger": self.objectives.setScavengerHunt() self.objectives.tourianRequired = not self.romLoader.hasPatch( 'Escape_Trigger') else: self.objectives.setVanilla() self.majorUpgrades = self.romLoader.loadMajorUpgrades() self.splitLocsByArea = self.romLoader.getSplitLocsByArea( self.locations) self.objectives.setSolverMode(self) if interactive == False: print( "ROM {} majors: {} area: {} boss: {} escape: {} patches: {} activePatches: {}" .format(rom, self.majorsSplit, self.areaRando, self.bossRando, self.escapeRando, sorted(self.romLoader.getPatches()), sorted(RomPatches.ActivePatches))) else: print( "majors: {} area: {} boss: {} escape: {} activepatches: {}" .format(self.majorsSplit, self.areaRando, self.bossRando, self.escapeRando, sorted(RomPatches.ActivePatches))) (self.areaTransitions, self.bossTransitions, self.escapeTransition, self.hasMixedTransitions) = self.romLoader.getTransitions( self.tourian) if interactive == True and self.debug == False: # in interactive area mode we build the graph as we play along if self.areaRando == True and self.bossRando == True: self.curGraphTransitions = [] elif self.areaRando == True: self.curGraphTransitions = self.bossTransitions[:] elif self.bossRando == True: self.curGraphTransitions = self.areaTransitions[:] else: self.curGraphTransitions = self.bossTransitions + self.areaTransitions if self.escapeRando == False: self.curGraphTransitions += self.escapeTransition else: self.curGraphTransitions = self.bossTransitions + self.areaTransitions + self.escapeTransition self.smbm = SMBoolManager() self.buildGraph() # store at each step how many locations are available self.nbAvailLocs = [] if self.log.getEffectiveLevel() == logging.DEBUG: self.log.debug("Display items at locations:") for loc in self.locations: self.log.debug('{:>50}: {:>16}'.format(loc.Name, loc.itemName))
def computeDifficulty(self): # loop on the available locations depending on the collected items. # before getting a new item, loop on all of them and get their difficulty, # the next collected item is the one with the smallest difficulty, # if equality between major and minor, take major first. # remove mother brain location (there items pickup conditions on top of going to mother brain location) mbLoc = self.getLoc('Mother Brain') self.locations.remove(mbLoc) if self.majorsSplit == 'Major': self.majorLocations = [ loc for loc in self.locations if loc.isMajor() or loc.isBoss() ] self.minorLocations = [ loc for loc in self.locations if loc.isMinor() ] elif self.majorsSplit == 'Chozo': self.majorLocations = [ loc for loc in self.locations if loc.isChozo() or loc.isBoss() ] self.minorLocations = [ loc for loc in self.locations if not loc.isChozo() and not loc.isBoss() ] elif self.majorsSplit == 'Scavenger': self.majorLocations = [ loc for loc in self.locations if loc.isScavenger() or loc.isBoss() ] self.minorLocations = [ loc for loc in self.locations if not loc.isScavenger() and not loc.isBoss() ] else: # Full self.majorLocations = self.locations[:] # copy self.minorLocations = self.majorLocations self.visitedLocations = [] self.collectedItems = [] self.log.debug( "{}: available major: {}, available minor: {}, visited: {}".format( Conf.itemsPickup, len(self.majorLocations), len(self.minorLocations), len(self.visitedLocations))) isEndPossible = False endDifficulty = mania diffThreshold = self.getDiffThreshold() self.motherBrainKilled = False self.motherBrainCouldBeKilled = False while True: # actual while condition hasEnoughMinors = self.pickup.enoughMinors(self.smbm, self.minorLocations) hasEnoughMajors = self.pickup.enoughMajors(self.smbm, self.majorLocations) hasEnoughItems = hasEnoughMajors and hasEnoughMinors canEndGame = self.canEndGame() (isEndPossible, endDifficulty) = (canEndGame.bool, canEndGame.difficulty) if isEndPossible and hasEnoughItems: if not self.objectives.tourianRequired: if self.checkEscape(): self.log.debug( "checkMB: disabled, can escape to gunship, END") break else: self.log.debug( "checkMB: disabled, can't escape to gunship") else: if endDifficulty <= diffThreshold: if self.checkMB(mbLoc): self.log.debug( "checkMB: all end game checks are ok, END") break else: self.log.debug( "checkMB: canEnd but MB loc not accessible") else: if not self.motherBrainCouldBeKilled: self.motherBrainCouldBeKilled = self.checkMB( mbLoc, justCheck=True) self.log.debug( "checkMB: end checks ok except MB difficulty, MB could be killed: {}" .format(self.motherBrainCouldBeKilled)) # check time limit if self.runtimeLimit_s > 0: if time.process_time() - self.startTime > self.runtimeLimit_s: self.log.debug("time limit exceeded ({})".format( self.runtimeLimit_s)) return (-1, False) self.log.debug("Current AP/Area: {}/{}".format( self.lastAP, self.lastArea)) # compute the difficulty of all the locations self.computeLocationsDifficulty(self.majorLocations) if self.majorsSplit != 'Full': self.computeLocationsDifficulty(self.minorLocations, phase="minor") # keep only the available locations majorsAvailable = [ loc for loc in self.majorLocations if loc.difficulty is not None and loc.difficulty.bool == True ] minorsAvailable = [ loc for loc in self.minorLocations if loc.difficulty is not None and loc.difficulty.bool == True ] self.nbAvailLocs.append( len(self.getAllLocs(majorsAvailable, minorsAvailable))) # remove next scavenger locs before checking if we're stuck if self.majorsSplit == 'Scavenger': majorsAvailable = self.filterScavengerLocs(majorsAvailable) # check if we're stuck if len(majorsAvailable) == 0 and len(minorsAvailable) == 0: if not isEndPossible: self.log.debug("STUCK MAJORS and MINORS") if self.comeBack.rewind(len(self.collectedItems)) == True: continue else: # we're really stucked self.log.debug("STUCK CAN'T REWIND") break else: self.log.debug("HARD END 2") if self.checkMB(mbLoc): self.log.debug("all end game checks are ok, END") break else: self.log.debug( "We're stucked somewhere and can't reach mother brain" ) # check if we were able to access MB and kill it. # we do it before rollbacks to avoid endless rollbacks. if self.motherBrainCouldBeKilled: self.log.debug( "we're stucked but we could have killed MB before" ) # add MB loc for the spoiler log, remove its path as it's not the correct one # from when the loc was accessible mbLoc.path = [getAccessPoint('Golden Four')] self.visitedLocations.append(mbLoc) self.motherBrainKilled = True break else: # we're really stucked, try to rollback if self.comeBack.rewind(len( self.collectedItems)) == True: continue else: self.log.debug( "We could end but we're STUCK CAN'T REWIND" ) return (-1, False) # handle no comeback locations rewindRequired = self.comeBack.handleNoComeBack( self.getAllLocs(majorsAvailable, minorsAvailable), len(self.collectedItems)) if rewindRequired == True: if self.comeBack.rewind(len(self.collectedItems)) == True: continue else: # we're really stucked self.log.debug("STUCK CAN'T REWIND") break # sort them on difficulty and proximity self.log.debug("getAvailableItemsList majors") majorsAvailable = self.getAvailableItemsList( majorsAvailable, diffThreshold) if self.majorsSplit == 'Full': minorsAvailable = majorsAvailable else: self.log.debug("getAvailableItemsList minors") minorsAvailable = self.getAvailableItemsList( minorsAvailable, diffThreshold) # choose one to pick up self.nextDecision(majorsAvailable, minorsAvailable, hasEnoughMinors, diffThreshold) self.comeBack.cleanNoComeBack( self.getAllLocs(self.majorLocations, self.minorLocations)) # compute difficulty value (difficulty, itemsOk) = self.computeDifficultyValue() if self.log.getEffectiveLevel() == logging.DEBUG: self.log.debug("difficulty={}".format(difficulty)) self.log.debug("itemsOk={}".format(itemsOk)) self.log.debug( "{}: remaining major: {}, remaining minor: {}, visited: {}". format(Conf.itemsPickup, len(self.majorLocations), len(self.minorLocations), len(self.visitedLocations))) self.log.debug("remaining majors:") for loc in self.majorLocations: self.log.debug("{} ({})".format(loc.Name, loc.itemName)) self.log.debug("bosses: {}".format([ (boss, Bosses.bossDead(self.smbm, boss)) for boss in Bosses.Golden4() ])) return (difficulty, itemsOk)
def loadRom(self, rom, interactive=False, magic=None, startAP=None): # startAP param is only use for seedless if rom == None: # TODO::add a --logic parameter for seedless Logic.factory('varia') self.romFileName = 'seedless' self.majorsSplit = 'Full' self.areaRando = True self.bossRando = True self.escapeRando = False self.escapeTimer = "03:00" self.startAP = startAP RomPatches.setDefaultPatches(startAP) self.startArea = getAccessPoint(startAP).Start['solveArea'] # in seedless load all the vanilla transitions self.areaTransitions = vanillaTransitions[:] self.bossTransitions = vanillaBossesTransitions[:] self.escapeTransition = [vanillaEscapeTransitions[0]] # in seedless we allow mixing of area and boss transitions self.hasMixedTransitions = True self.curGraphTransitions = self.bossTransitions + self.areaTransitions + self.escapeTransition self.locations = Logic.locations for loc in self.locations: loc.itemName = 'Nothing' # set doors related to default patches DoorsManager.setDoorsColor() self.doorsRando = False else: self.romFileName = rom self.romLoader = RomLoader.factory(rom, magic) Logic.factory(self.romLoader.readLogic()) self.romLoader.readNothingId() self.locations = Logic.locations self.majorsSplit = self.romLoader.assignItems(self.locations) (self.startAP, self.startArea, startPatches) = self.romLoader.getStartAP() (self.areaRando, self.bossRando, self.escapeRando) = self.romLoader.loadPatches() RomPatches.ActivePatches += startPatches self.escapeTimer = self.romLoader.getEscapeTimer() self.doorsRando = self.romLoader.loadDoorsColor() if interactive == False: print( "ROM {} majors: {} area: {} boss: {} escape: {} patches: {} activePatches: {}" .format(rom, self.majorsSplit, self.areaRando, self.bossRando, self.escapeRando, sorted(self.romLoader.getPatches()), sorted(RomPatches.ActivePatches))) else: print( "majors: {} area: {} boss: {} escape: {} activepatches: {}" .format(self.majorsSplit, self.areaRando, self.bossRando, self.escapeRando, sorted(RomPatches.ActivePatches))) (self.areaTransitions, self.bossTransitions, self.escapeTransition, self.hasMixedTransitions) = self.romLoader.getTransitions() if interactive == True and self.debug == False: # in interactive area mode we build the graph as we play along if self.areaRando == True and self.bossRando == True: self.curGraphTransitions = [] elif self.areaRando == True: self.curGraphTransitions = self.bossTransitions[:] elif self.bossRando == True: self.curGraphTransitions = self.areaTransitions[:] else: self.curGraphTransitions = self.bossTransitions + self.areaTransitions if self.escapeRando == False: self.curGraphTransitions += self.escapeTransition else: self.curGraphTransitions = self.bossTransitions + self.areaTransitions + self.escapeTransition self.smbm = SMBoolManager() self.areaGraph = AccessGraph(Logic.accessPoints, self.curGraphTransitions) # store at each step how many locations are available self.nbAvailLocs = [] if self.log.getEffectiveLevel() == logging.DEBUG: self.log.debug("Display items at locations:") for loc in self.locations: self.log.debug('{:>50}: {:>16}'.format(loc.Name, loc.itemName))
startLocationList = args.startLocationList.replace('_', ' ') startLocationList = startLocationList.split(',') # intersection between user whishes and reality possibleStartAPs = sorted(list(set(possibleStartAPs).intersection(set(startLocationList)))) if len(possibleStartAPs) == 0: optErrMsgs += ["%s : %s" % (apName, cause) for apName, cause in reasons.items() if apName in startLocationList] optErrMsgs.append('Invalid start locations list with your settings.') dumpErrorMsgs(args.output, optErrMsgs) sys.exit(-1) args.startLocation = random.choice(possibleStartAPs) elif args.startLocation not in possibleStartAPs: optErrMsgs.append('Invalid start location: {}. {}'.format(args.startLocation, reasons[args.startLocation])) optErrMsgs.append('Possible start locations with these settings: {}'.format(possibleStartAPs)) dumpErrorMsgs(args.output, optErrMsgs) sys.exit(-1) ap = getAccessPoint(args.startLocation) if 'forcedEarlyMorph' in ap.Start and ap.Start['forcedEarlyMorph'] == True: forceArg('morphPlacement', 'early', "'Morph Placement' forced to early for custom start location") else: if progSpeed == 'speedrun': if args.morphPlacement == 'late': forceArg('morphPlacement', 'normal', "'Morph Placement' forced to normal instead of late") elif (not GraphUtils.isStandardStart(args.startLocation)) and args.morphPlacement != 'normal': forceArg('morphPlacement', 'normal', "'Morph Placement' forced to normal for custom start location") if args.majorsSplit == 'Chozo' and args.morphPlacement == "late": forceArg('morphPlacement', 'normal', "'Morph Placement' forced to normal for Chozo") if args.patchOnly == False: print("SEED: " + str(seed)) objectivesManager = Objectives(args.tourian != 'Disabled') addedObjectives = 0