def canPassCacatacAlley(self): sm = self.smbm return sm.wand(Bosses.bossDead(sm, 'Draygon'), sm.wor(sm.haveItem('Gravity'), sm.wand(sm.knowsGravLessLevel2(), sm.haveItem('HiJump'), sm.haveItem('SpaceJump'))))
def canPassBowling(self): sm = self.smbm return sm.wand( Bosses.bossDead(sm, 'Phantoon'), sm.wor(SMBool(sm.getDmgReduction()[0] >= 2), sm.energyReserveCountOk(1), sm.haveItem("SpaceJump"), sm.haveItem("Grapple")))
def canPassBowling(self): sm = self.smbm return sm.wand(Bosses.bossDead(sm, 'Phantoon'), sm.wor(sm.heatProof(), sm.energyReserveCountOk(1), sm.haveItem("SpaceJump"), sm.haveItem("Grapple")))
def canPassCacatacAlley(self): sm = self.smbm return sm.wand(Bosses.bossDead(sm, 'Draygon'), # cacatac alley suitless: hijump + gravless level 1 # butterfly room suitless: hijump + ice + gravless level 2 sm.wor(sm.haveItem('Gravity'), sm.wand(sm.haveItem('HiJump'), sm.haveItem('Ice'), sm.knowsGravLessLevel2())))
def fullComebackCheck(self, container, ap, item, loc, comebackCheck): sm = container.sm tmpItems = [] # draygon special case: there are two locations, and we can # place one item, but we might need both the item and the boss # dead to get out if loc.SolveArea == "Draygon Boss" and Bosses.bossDead(sm, 'Draygon').bool == False: # temporary kill draygon tmpItems.append('Draygon') sm.addItems(tmpItems) ret = self.locPostAvailable(sm, loc, item.Type if item is not None else None) and not self.isSoftlockPossible(container, ap, item, loc, comebackCheck) for tmp in tmpItems: sm.removeItem(tmp) return ret
sm.canSpringBallJump())))))) ) locationsDict["Spazer"].AccessFrom = { 'East Tunnel Right': lambda sm: SMBool(True) } locationsDict["Spazer"].Available = ( lambda sm: sm.wand(sm.traverse('BelowSpazerTopRight'), sm.wor(sm.canPassBombPassages(), sm.wand(sm.haveItem('Morph'), RomPatches.has(RomPatches.SpazerShotBlock)))) ) locationsDict["Energy Tank, Kraid"].AccessFrom = { 'Warehouse Zeela Room Left': lambda sm: SMBool(True) } locationsDict["Energy Tank, Kraid"].Available = ( lambda sm: Bosses.bossDead(sm, 'Kraid') ) locationsDict["Kraid"].AccessFrom = { 'KraidRoomIn': lambda sm: SMBool(True) } locationsDict["Kraid"].Available = ( lambda sm: sm.enoughStuffsKraid() ) locationsDict["Varia Suit"].AccessFrom = { 'KraidRoomIn': lambda sm: SMBool(True) } locationsDict["Varia Suit"].Available = ( lambda sm: Bosses.bossDead(sm, 'Kraid') ) locationsDict["Ice Beam"].AccessFrom = { 'Business Center': lambda sm: sm.traverse('BusinessCenterTopLeft')
locationsDict["Spazer"].Available = ( lambda sm: sm.wand(sm.traverse('BelowSpazerTopRight'), sm.wor(sm.canPassBombPassages(), sm.wand(sm.haveItem('Morph'), RomPatches.has(RomPatches.SpazerShotBlock)))) ) locationsDict["Spazer"].PostAvailable = ( # either with a bomb jump/springball to exit on top, or with bomb/pb to exit in the middle lambda sm: sm.wor(sm.canPassBombPassages(), sm.wand(RomPatches.has(RomPatches.SpazerShotBlock), sm.canUseSpringBall()) ) locationsDict["Energy Tank, Kraid"].AccessFrom = { 'Warehouse Zeela Room Left': lambda sm: sm.haveItem('Morph') } locationsDict["Energy Tank, Kraid"].Available = ( lambda sm: Bosses.bossDead(sm, 'Kraid') ) locationsDict["Kraid"].AccessFrom = { 'KraidRoomIn': lambda sm: SMBool(True) } locationsDict["Kraid"].Available = ( lambda sm: sm.enoughStuffsKraid() ) locationsDict["Varia Suit"].AccessFrom = { 'KraidRoomIn': lambda sm: SMBool(True) } locationsDict["Varia Suit"].Available = ( lambda sm: Bosses.bossDead(sm, 'Kraid') ) locationsDict["Ice Beam"].AccessFrom = { 'Business Center': lambda sm: sm.traverse('BusinessCenterTopLeft')
def getMiniBossesEscapeAccessPoints(n): return (n, [Bosses.accessPoints[boss] for boss in Bosses.miniBosses()]) def getAreaEscapeAccessPoints(area): return (1, list({ list(loc.AccessFrom.keys())[0] for loc in Logic.locations if loc.GraphArea == area })) _goalsList = [ Goal("kill kraid", "boss", lambda sm, ap: Bosses.bossDead(sm, 'Kraid'), "kraid_is_dead", escapeAccessPoints=getBossEscapeAccessPoint("Kraid"), exclusion={"list": ["kill all G4", "kill one G4"]}, items=["Kraid"], text="{} kraid", category="Bosses"), Goal("kill phantoon", "boss", lambda sm, ap: Bosses.bossDead(sm, 'Phantoon'), "phantoon_is_dead", escapeAccessPoints=getBossEscapeAccessPoint("Phantoon"), exclusion={"list": ["kill all G4", "kill one G4"]}, items=["Phantoon"], text="{} phantoon", category="Bosses"),
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() ] 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() 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 and endDifficulty <= diffThreshold: if self.checkMB(mbLoc): self.log.debug("END") break else: self.log.debug("canEnd but MB loc not accessible") # 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 ] if self.majorsSplit == 'Full': locs = majorsAvailable else: locs = majorsAvailable + minorsAvailable self.nbAvailLocs.append(len(locs)) # 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") self.checkMB(mbLoc) break # handle no comeback locations rewindRequired = self.comeBack.handleNoComeBack( locs, 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(locs) # 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 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)