SM: 20221101 update (#1479)
This adds support to most of Varia's 20221101 update. Notably, added Options for: - Objectives - Tourian - RelaxedRoundRobinCF As well as previously unsupported Options: - EscapeRando - RemoveEscapeEnemies - HideItems
This commit is contained in:
@@ -140,7 +140,7 @@ class AccessGraph(object):
|
||||
|
||||
def addAccessPoint(self, ap):
|
||||
ap.distance = 0
|
||||
self.accessPoints[ap.Name] = ap
|
||||
self.accessPoints[ap.Name] = copy.deepcopy(ap)
|
||||
|
||||
def toDot(self, dotFile):
|
||||
colors = ['red', 'blue', 'green', 'yellow', 'skyblue', 'violet', 'orange',
|
||||
@@ -175,6 +175,15 @@ class AccessGraph(object):
|
||||
if both is True:
|
||||
self.addTransition(dstName, srcName, False)
|
||||
|
||||
# remove transitions whose source or dest matches apName
|
||||
def removeTransitions(self, apName):
|
||||
toRemove = [t for t in self.InterAreaTransitions if t[0].Name == apName or t[1].Name == apName]
|
||||
for t in toRemove:
|
||||
src, dst = t
|
||||
self.InterAreaTransitions.remove(t)
|
||||
src.disconnect()
|
||||
dst.disconnect()
|
||||
|
||||
# availNodes: all already available nodes
|
||||
# nodesToCheck: nodes we have to check transitions for
|
||||
# smbm: smbm to test logic on. if None, discard logic check, assume we can reach everything
|
||||
|
||||
@@ -238,7 +238,10 @@ class GraphUtils:
|
||||
for ap in unusedAPs:
|
||||
transitions.append((ap.Name, ap.Name))
|
||||
|
||||
def createMinimizerTransitions(startApName, locLimit):
|
||||
# crateria can be forced in corner cases
|
||||
def createMinimizerTransitions(startApName, locLimit, forcedAreas=None):
|
||||
if forcedAreas is None:
|
||||
forcedAreas = []
|
||||
if startApName == 'Ceres':
|
||||
startApName = 'Landing Site'
|
||||
startAp = getAccessPoint(startApName)
|
||||
@@ -249,7 +252,10 @@ class GraphUtils:
|
||||
return len([loc for loc in locList if locsPredicate(loc) == True and not loc.SolveArea.endswith(" Boss") and not loc.isBoss()])
|
||||
availAreas = list(sorted({ap.GraphArea for ap in Logic.accessPoints if ap.GraphArea != startAp.GraphArea and getNLocs(lambda loc: loc.GraphArea == ap.GraphArea) > 0}))
|
||||
areas = [startAp.GraphArea]
|
||||
if startAp.GraphArea in forcedAreas:
|
||||
forcedAreas.remove(startAp.GraphArea)
|
||||
GraphUtils.log.debug("availAreas: {}".format(availAreas))
|
||||
GraphUtils.log.debug("forcedAreas: {}".format(forcedAreas))
|
||||
GraphUtils.log.debug("areas: {}".format(areas))
|
||||
inBossCheck = lambda ap: ap.Boss and ap.Name.endswith("In")
|
||||
nLocs = 0
|
||||
@@ -260,22 +266,27 @@ class GraphUtils:
|
||||
def openTransitions():
|
||||
nonlocal areas, inBossCheck, usedAPs
|
||||
return GraphUtils.getAPs(lambda ap: ap.GraphArea in areas and not ap.isInternal() and not inBossCheck(ap) and not ap in usedAPs)
|
||||
while nLocs < locLimit or len(openTransitions()) < trLimit:
|
||||
while nLocs < locLimit or len(openTransitions()) < trLimit or len(forcedAreas) > 0:
|
||||
GraphUtils.log.debug("openTransitions="+str([ap.Name for ap in openTransitions()]))
|
||||
fromAreas = availAreas
|
||||
if nLocs >= locLimit:
|
||||
if len(openTransitions()) <= 1: # dont' get stuck by adding dead ends
|
||||
GraphUtils.log.debug("avoid being stuck")
|
||||
fromAreas = [area for area in fromAreas if len(GraphUtils.getAPs(lambda ap: ap.GraphArea == area and not ap.isInternal())) > 1]
|
||||
elif len(forcedAreas) > 0: # no risk to get stuck, we can pick a forced area if necessary
|
||||
GraphUtils.log.debug("add forced area")
|
||||
fromAreas = forcedAreas
|
||||
elif nLocs >= locLimit: # we just need transitions, avoid adding a huge area
|
||||
GraphUtils.log.debug("not enough open transitions")
|
||||
# we just need transitions, avoid adding a huge area
|
||||
fromAreas = []
|
||||
n = trLimit - len(openTransitions())
|
||||
while len(fromAreas) == 0:
|
||||
fromAreas = [area for area in availAreas if len(GraphUtils.getAPs(lambda ap: not ap.isInternal())) > n]
|
||||
n -= 1
|
||||
minLocs = min([getNLocs(lambda loc: loc.GraphArea == area) for area in fromAreas])
|
||||
minLocs = min([getNLocs(lambda loc: loc.GraphArea == area) for area in fromAreas])
|
||||
fromAreas = [area for area in fromAreas if getNLocs(lambda loc: loc.GraphArea == area) == minLocs]
|
||||
elif len(openTransitions()) <= 1: # dont' get stuck by adding dead ends
|
||||
fromAreas = [area for area in fromAreas if len(GraphUtils.getAPs(lambda ap: ap.GraphArea == area and not ap.isInternal())) > 1]
|
||||
nextArea = random.choice(fromAreas)
|
||||
if nextArea in forcedAreas:
|
||||
forcedAreas.remove(nextArea)
|
||||
GraphUtils.log.debug("nextArea="+str(nextArea))
|
||||
apCheck = lambda ap: not ap.isInternal() and not inBossCheck(ap) and ap not in usedAPs
|
||||
possibleSources = GraphUtils.getAPs(lambda ap: ap.GraphArea in areas and apCheck(ap))
|
||||
@@ -399,18 +410,37 @@ class GraphUtils:
|
||||
def escapeAnimalsTransitions(graph, possibleTargets, firstEscape):
|
||||
n = len(possibleTargets)
|
||||
assert (n < 4 and firstEscape is not None) or (n <= 4 and firstEscape is None), "Invalid possibleTargets list: " + str(possibleTargets)
|
||||
# first get our list of 4 entries for escape patch
|
||||
GraphUtils.log.debug("escapeAnimalsTransitions. possibleTargets="+str(possibleTargets)+", firstEscape="+str(firstEscape))
|
||||
if n >= 1:
|
||||
# get actual animals: pick one of the remaining targets
|
||||
animalsAccess = possibleTargets.pop()
|
||||
graph.EscapeAttributes['Animals'] = animalsAccess
|
||||
# we now have at most 3 targets left, fill up to fill cycling 4 targets for animals suprise
|
||||
possibleTargets.append('Climb Bottom Left')
|
||||
# complete possibleTargets. we need at least 2: one to
|
||||
# hide the animals in, and one to connect the vanilla
|
||||
# animals door to
|
||||
if not any(t[1].Name == 'Climb Bottom Left' for t in graph.InterAreaTransitions):
|
||||
# add standard Climb if not already in graph: it can be in Crateria-less minimizer + Disabled Tourian case
|
||||
possibleTargets.append('Climb Bottom Left')
|
||||
# make the escape possibilities loop by adding back the first escape
|
||||
if firstEscape is not None:
|
||||
possibleTargets.append(firstEscape)
|
||||
poss = possibleTargets[:]
|
||||
while len(possibleTargets) < 4:
|
||||
possibleTargets.append(poss.pop(random.randint(0, len(poss)-1)))
|
||||
n = len(possibleTargets)
|
||||
# check if we can both hide the animals and connect the vanilla animals door to a cycling escape
|
||||
if n >= 2:
|
||||
# get actual animals: pick the first of the remaining targets (will contain a map room AP)
|
||||
animalsAccess = possibleTargets.pop(0)
|
||||
graph.EscapeAttributes['Animals'] = animalsAccess
|
||||
# poss will contain the remaining map room AP(s) + optional AP(s) added above, to
|
||||
# get the cycling 4 escapes from vanilla animals room
|
||||
poss = possibleTargets[:]
|
||||
GraphUtils.log.debug("escapeAnimalsTransitions. poss="+str(poss))
|
||||
while len(possibleTargets) < 4:
|
||||
if len(poss) > 1:
|
||||
possibleTargets.append(poss.pop(random.randint(0, len(poss)-1)))
|
||||
else:
|
||||
# no more possible variety, spam the last possible escape
|
||||
possibleTargets.append(poss[0])
|
||||
|
||||
else:
|
||||
# failsafe: if not enough targets left, abort and do vanilla animals
|
||||
animalsAccess = 'Flyway Right'
|
||||
|
||||
@@ -64,6 +64,8 @@ class Location:
|
||||
smbm.removeItem(self.itemName)
|
||||
|
||||
self.difficulty = self.difficulty & postAvailable
|
||||
if self.locDifficulty is not None:
|
||||
self.locDifficulty = self.locDifficulty & postAvailable
|
||||
|
||||
def evalComeBack(self, smbm, areaGraph, ap):
|
||||
if self.difficulty.bool == True:
|
||||
@@ -102,7 +104,7 @@ class Location:
|
||||
|
||||
def define_location(
|
||||
Area, GraphArea, SolveArea, Name, Class, CanHidden, Address, Id,
|
||||
Visibility, Room, VanillaItemType=None, AccessFrom=None, Available=None, PostAvailable=None, HUD=None):
|
||||
Visibility, Room, VanillaItemType=None, BossItemType=None, AccessFrom=None, Available=None, PostAvailable=None, HUD=None):
|
||||
name = Name.replace(' ', '').replace(',', '') + 'Location'
|
||||
subclass = type(name, (Location,), {
|
||||
'Area': Area,
|
||||
@@ -116,6 +118,7 @@ def define_location(
|
||||
'Visibility': Visibility,
|
||||
'Room': Room,
|
||||
'VanillaItemType': VanillaItemType,
|
||||
'BossItemType': BossItemType,
|
||||
'HUD': HUD,
|
||||
'AccessFrom': AccessFrom,
|
||||
'Available': Available,
|
||||
@@ -322,6 +325,7 @@ define_location(
|
||||
Id=None,
|
||||
Visibility="Hidden",
|
||||
Room='Kraid Room',
|
||||
BossItemType="Kraid"
|
||||
),
|
||||
"Varia Suit":
|
||||
define_location(
|
||||
@@ -445,12 +449,15 @@ define_location(
|
||||
GraphArea="LowerNorfair",
|
||||
SolveArea="Ridley Boss",
|
||||
Name="Ridley",
|
||||
Class=["Boss"],
|
||||
Class=["Boss", "Scavenger"],
|
||||
CanHidden=False,
|
||||
Address=0xB055B056,
|
||||
Id=None,
|
||||
Id=0xaa,
|
||||
Visibility="Hidden",
|
||||
Room="Ridley's Room",
|
||||
VanillaItemType="Ridley",
|
||||
BossItemType="Ridley",
|
||||
HUD=16
|
||||
),
|
||||
"Energy Tank, Ridley":
|
||||
define_location(
|
||||
@@ -531,6 +538,7 @@ define_location(
|
||||
Id=None,
|
||||
Visibility="Hidden",
|
||||
Room="Phantoon's Room",
|
||||
BossItemType="Phantoon"
|
||||
),
|
||||
"Right Super, Wrecked Ship":
|
||||
define_location(
|
||||
@@ -641,6 +649,7 @@ define_location(
|
||||
Id=None,
|
||||
Visibility="Hidden",
|
||||
Room="Draygon's Room",
|
||||
BossItemType="Draygon"
|
||||
),
|
||||
"Space Jump":
|
||||
define_location(
|
||||
@@ -669,6 +678,63 @@ define_location(
|
||||
Visibility="Hidden",
|
||||
CanHidden=False,
|
||||
Room='Mother Brain Room',
|
||||
BossItemType="MotherBrain"
|
||||
),
|
||||
"Spore Spawn":
|
||||
define_location(
|
||||
Area="Brinstar",
|
||||
GraphArea="GreenPinkBrinstar",
|
||||
SolveArea="Pink Brinstar",
|
||||
Name="Spore Spawn",
|
||||
Class=["Boss"],
|
||||
CanHidden=False,
|
||||
Address=0xB055B055,
|
||||
Id=None,
|
||||
Visibility="Hidden",
|
||||
Room='Spore Spawn Room',
|
||||
BossItemType="SporeSpawn"
|
||||
),
|
||||
"Botwoon":
|
||||
define_location(
|
||||
Area="Maridia",
|
||||
GraphArea="EastMaridia",
|
||||
SolveArea="Maridia Pink Top",
|
||||
Name="Botwoon",
|
||||
Class=["Boss"],
|
||||
CanHidden=False,
|
||||
Address=0xB055B055,
|
||||
Id=None,
|
||||
Visibility="Hidden",
|
||||
Room="Botwoon's Room",
|
||||
BossItemType="Botwoon"
|
||||
),
|
||||
"Crocomire":
|
||||
define_location(
|
||||
Area="Norfair",
|
||||
GraphArea="Crocomire",
|
||||
SolveArea="Crocomire",
|
||||
Name="Crocomire",
|
||||
Class=["Boss"],
|
||||
CanHidden=False,
|
||||
Address=0xB055B055,
|
||||
Id=None,
|
||||
Visibility="Hidden",
|
||||
Room="Crocomire's Room",
|
||||
BossItemType="Crocomire"
|
||||
),
|
||||
"Golden Torizo":
|
||||
define_location(
|
||||
Area="LowerNorfair",
|
||||
GraphArea="LowerNorfair",
|
||||
SolveArea="Lower Norfair Screw Attack",
|
||||
Name="Golden Torizo",
|
||||
Class=["Boss"],
|
||||
CanHidden=False,
|
||||
Address=0xB055B055,
|
||||
Id=None,
|
||||
Visibility="Hidden",
|
||||
Room="Golden Torizo's Room",
|
||||
BossItemType="GoldenTorizo"
|
||||
),
|
||||
###### MINORS
|
||||
"Power Bomb (Crateria surface)":
|
||||
|
||||
@@ -42,9 +42,8 @@ accessPoints = [
|
||||
}, traverse = Cache.ldeco(lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.AreaRandoMoreBlueDoors),
|
||||
sm.traverse('GreenPiratesShaftBottomRight'))),
|
||||
roomInfo = {'RoomPtr':0x99bd, "area": 0x0, 'songs':[0x99ce]},
|
||||
# the doorAsmPtr 7FE00 is set by the g4_skip.ips patch, we have to call it
|
||||
exitInfo = {'DoorPtr':0x8c52, 'direction': 0x4, "cap": (0x1, 0x6), "bitFlag": 0x0,
|
||||
"screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0xfe00},
|
||||
"screen": (0x0, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000},
|
||||
entryInfo = {'SamusX':0xcc, 'SamusY':0x688, 'song': 0x9},
|
||||
dotOrientation = 'e'),
|
||||
AccessPoint('Moat Right', 'Crateria', {
|
||||
@@ -173,7 +172,9 @@ accessPoints = [
|
||||
sm.canPassSpongeBath()),
|
||||
sm.wand(sm.wnot(Bosses.bossDead(sm, 'Phantoon')),
|
||||
RomPatches.has(sm.player, RomPatches.SpongeBathBlueDoor)))),
|
||||
'PhantoonRoomOut': Cache.ldeco(lambda sm: sm.wand(sm.traverse('WreckedShipMainShaftBottom'), sm.canPassBombPassages()))
|
||||
'PhantoonRoomOut': Cache.ldeco(lambda sm: sm.wand(sm.traverse('WreckedShipMainShaftBottom'), sm.canPassBombPassages())),
|
||||
'Bowling': Cache.ldeco(lambda sm: sm.wand(sm.canMorphJump(),
|
||||
sm.canPassBowling()))
|
||||
}, internal=True,
|
||||
start={'spawn':0x0300,
|
||||
'doors':[0x83,0x8b], 'patches':[RomPatches.SpongeBathBlueDoor, RomPatches.WsEtankBlueDoor],
|
||||
@@ -183,6 +184,9 @@ accessPoints = [
|
||||
'Wrecked Ship Main': lambda sm: SMBool(True),
|
||||
'Crab Maze Left': Cache.ldeco(lambda sm: sm.canPassForgottenHighway(True))
|
||||
}, internal=True),
|
||||
AccessPoint('Bowling', 'WreckedShip', {
|
||||
'West Ocean Left': lambda sm: SMBool(True)
|
||||
}, internal=True),
|
||||
AccessPoint('Crab Maze Left', 'WreckedShip', {
|
||||
'Wrecked Ship Back': Cache.ldeco(lambda sm: sm.canPassForgottenHighway(False))
|
||||
}, traverse=Cache.ldeco(lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.AreaRandoBlueDoors),
|
||||
@@ -245,6 +249,8 @@ accessPoints = [
|
||||
sm.canUsePowerBombs()))
|
||||
}, internal=True),
|
||||
AccessPoint('LN Above GT', 'LowerNorfair', {
|
||||
'LN Entrance': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']),
|
||||
sm.canPassBombPassages())),
|
||||
'Screw Attack Bottom': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']),
|
||||
sm.enoughStuffGT()))
|
||||
}, internal=True),
|
||||
@@ -423,11 +429,9 @@ accessPoints = [
|
||||
entryInfo = {'SamusX':0x134, 'SamusY':0x288, 'song': 0x15},
|
||||
dotOrientation = 'se'),
|
||||
AccessPoint('Crocomire Speedway Bottom', 'Norfair', {
|
||||
'Business Center': Cache.ldeco(lambda sm: sm.wor(sm.wand(sm.canPassFrogSpeedwayRightToLeft(),
|
||||
sm.canHellRun(**Settings.hellRunsTable['Ice']['Croc -> Norfair Entrance'])),
|
||||
sm.wand(sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Croc -> Norfair Entrance']),
|
||||
sm.canGrappleEscape(),
|
||||
sm.haveItem('Super')))),
|
||||
'Grapple Escape': lambda sm: sm.canGrappleEscape(),
|
||||
'Business Center': Cache.ldeco(lambda sm: sm.wand(sm.canPassFrogSpeedwayRightToLeft(),
|
||||
sm.canHellRun(**Settings.hellRunsTable['Ice']['Croc -> Norfair Entrance']))),
|
||||
'Bubble Mountain Bottom': Cache.ldeco(lambda sm: sm.canHellRun(**Settings.hellRunsTable['Ice']['Croc -> Bubble Mountain'])),
|
||||
'Kronic Boost Room Bottom Left': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Kronic Boost Room <-> Croc']),
|
||||
sm.haveItem('Morph')))
|
||||
@@ -437,6 +441,10 @@ accessPoints = [
|
||||
"screen": (0x3, 0x0), "distanceToSpawn": 0x8000, "doorAsmPtr": 0x0000},
|
||||
entryInfo = {'SamusX':0xc57, 'SamusY':0x2b8},
|
||||
dotOrientation = 'se'),
|
||||
AccessPoint('Grapple Escape', 'Norfair', {
|
||||
'Business Center': lambda sm: sm.haveItem('Super'),
|
||||
'Crocomire Speedway Bottom': lambda sm: sm.canHellRunBackFromGrappleEscape()
|
||||
}, internal=True),
|
||||
AccessPoint('Bubble Mountain', 'Norfair', {
|
||||
'Business Center': lambda sm: sm.canExitCathedral(Settings.hellRunsTable['MainUpperNorfair']['Bubble -> Norfair Entrance']),
|
||||
'Bubble Mountain Top': lambda sm: sm.canClimbBubbleMountain(),
|
||||
@@ -494,8 +502,7 @@ accessPoints = [
|
||||
dotOrientation = 'se'),
|
||||
### West Maridia
|
||||
AccessPoint('Main Street Bottom', 'WestMaridia', {
|
||||
'Red Fish Room Left': Cache.ldeco(lambda sm: sm.wand(sm.canGoUpMtEverest(),
|
||||
sm.haveItem('Morph'))),
|
||||
'Red Fish Room Bottom': lambda sm: sm.canGoUpMtEverest(),
|
||||
'Crab Hole Bottom Left': Cache.ldeco(lambda sm: sm.wand(sm.haveItem('Morph'),
|
||||
sm.canTraverseCrabTunnelLeftToRight())),
|
||||
# this transition leads to EastMaridia directly
|
||||
@@ -532,12 +539,17 @@ accessPoints = [
|
||||
entryInfo = {'SamusX':0x28, 'SamusY':0x188},
|
||||
dotOrientation = 'se'),
|
||||
AccessPoint('Red Fish Room Left', 'WestMaridia', {
|
||||
'Main Street Bottom': Cache.ldeco(lambda sm: sm.haveItem('Morph')) # just go down
|
||||
'Red Fish Room Bottom': Cache.ldeco(lambda sm: sm.haveItem('Morph')) # just go down
|
||||
}, roomInfo = {'RoomPtr':0xd104, "area": 0x4},
|
||||
exitInfo = {'DoorPtr':0xa480, 'direction': 0x5, "cap": (0x2e, 0x36), "bitFlag": 0x40,
|
||||
"screen": (0x2, 0x3), "distanceToSpawn": 0x8000, "doorAsmPtr": 0xe367},
|
||||
entryInfo = {'SamusX':0x34, 'SamusY':0x88},
|
||||
dotOrientation = 'w'),
|
||||
AccessPoint('Red Fish Room Bottom', 'WestMaridia', {
|
||||
'Main Street Bottom': lambda sm: SMBool(True), # just go down
|
||||
'Red Fish Room Left': Cache.ldeco(lambda sm: sm.wand(sm.haveItem('Morph'),
|
||||
sm.canJumpUnderwater()))
|
||||
}, internal=True),
|
||||
AccessPoint('Crab Shaft Left', 'WestMaridia', {
|
||||
'Main Street Bottom': lambda sm: SMBool(True), # fall down
|
||||
'Beach': lambda sm: sm.canDoOuterMaridia(),
|
||||
@@ -586,7 +598,9 @@ accessPoints = [
|
||||
dotOrientation = 'ne'),
|
||||
### East Maridia
|
||||
AccessPoint('Aqueduct Top Left', 'EastMaridia', {
|
||||
'Aqueduct Bottom': lambda sm: sm.canUsePowerBombs()
|
||||
'Aqueduct Bottom': lambda sm: sm.wor(sm.wand(RomPatches.has(sm.player, RomPatches.AqueductBombBlocks),
|
||||
sm.canDestroyBombWallsUnderwater()),
|
||||
sm.canUsePowerBombs())
|
||||
}, roomInfo = {'RoomPtr':0xd5a7, "area": 0x4},
|
||||
exitInfo = {'DoorPtr':0xa708, 'direction': 0x5, "cap": (0x1e, 0x36), "bitFlag": 0x0,
|
||||
"screen": (0x1, 0x3), "distanceToSpawn": 0x8000, "doorAsmPtr": 0xe398},
|
||||
@@ -596,7 +610,8 @@ accessPoints = [
|
||||
'Aqueduct Top Left': Cache.ldeco(lambda sm: sm.wand(sm.canDestroyBombWallsUnderwater(), # top left bomb blocks
|
||||
sm.canJumpUnderwater())),
|
||||
'Post Botwoon': Cache.ldeco(lambda sm: sm.wand(sm.canJumpUnderwater(),
|
||||
sm.canDefeatBotwoon())), # includes botwoon hallway conditions
|
||||
sm.canPassBotwoonHallway(),
|
||||
sm.haveItem('Botwoon'))),
|
||||
'Left Sandpit': lambda sm: sm.canAccessSandPits(),
|
||||
'Right Sandpit': lambda sm: sm.canAccessSandPits(),
|
||||
'Aqueduct': Cache.ldeco(lambda sm: sm.wand(sm.wor(sm.haveItem('SpeedBooster'),
|
||||
|
||||
@@ -241,6 +241,21 @@ class HelpersGraph(Helpers):
|
||||
sm = self.smbm
|
||||
return sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Bubble -> Speed Booster w/Speed' if sm.haveItem('SpeedBooster') else 'Bubble -> Speed Booster'])
|
||||
|
||||
# with door color rando, there can be situations where you have to come back from the missile
|
||||
# loc without being able to open the speed booster door
|
||||
@Cache.decorator
|
||||
def canHellRunBackFromSpeedBoosterMissile(self):
|
||||
sm = self.smbm
|
||||
# require more health to count 1st hell run + way back is slower
|
||||
hellrun = 'MainUpperNorfair'
|
||||
tbl = Settings.hellRunsTable[hellrun]['Bubble -> Speed Booster']
|
||||
mult = tbl['mult']
|
||||
minE = tbl['minE']
|
||||
mult *= 0.66 if sm.haveItem('SpeedBooster') else 0.33 # speed booster usable for 1st hell run
|
||||
return sm.wor(RomPatches.has(sm.player, RomPatches.SpeedAreaBlueDoors),
|
||||
sm.traverse('SpeedBoosterHallRight'),
|
||||
sm.canHellRun(hellrun, mult, minE))
|
||||
|
||||
@Cache.decorator
|
||||
def canAccessDoubleChamberItems(self):
|
||||
sm = self.smbm
|
||||
@@ -253,6 +268,19 @@ class HelpersGraph(Helpers):
|
||||
sm.knowsDoubleChamberWallJump()),
|
||||
sm.canHellRun(hellRun['hellRun'], hellRun['mult']*0.8, hellRun['minE'])))
|
||||
|
||||
@Cache.decorator
|
||||
def canExitWaveBeam(self):
|
||||
sm = self.smbm
|
||||
return sm.wor(sm.haveItem('Morph'), # exit through lower passage under the spikes
|
||||
sm.wand(sm.wor(sm.haveItem('SpaceJump'), # exit through blue gate
|
||||
sm.haveItem('Grapple')),
|
||||
sm.wor(sm.haveItem('Wave'),
|
||||
sm.wand(sm.heatProof(), # hell run + green gate glitch is too much
|
||||
sm.canBlueGateGlitch(),
|
||||
# if missiles were required to open the door, require two packs as no farming around
|
||||
sm.wor(sm.wnot(SMBool('Missile' in sm.traverse('DoubleChamberRight').items)),
|
||||
sm.itemCountOk("Missile", 2))))))
|
||||
|
||||
def canExitCathedral(self, hellRun):
|
||||
# from top: can use bomb/powerbomb jumps
|
||||
# from bottom: can do a shinespark or use space jump
|
||||
@@ -272,16 +300,40 @@ class HelpersGraph(Helpers):
|
||||
@Cache.decorator
|
||||
def canGrappleEscape(self):
|
||||
sm = self.smbm
|
||||
return sm.wor(sm.wor(sm.haveItem('SpaceJump'),
|
||||
sm.wand(sm.canInfiniteBombJump(), # IBJ from lava...either have grav or freeze the enemy there if hellrunning (otherwise single DBJ at the end)
|
||||
sm.wor(sm.heatProof(),
|
||||
sm.haveItem('Gravity'),
|
||||
sm.haveItem('Ice')))),
|
||||
sm.haveItem('Grapple'),
|
||||
sm.wand(sm.haveItem('SpeedBooster'),
|
||||
sm.wor(sm.haveItem('HiJump'), # jump from the blocks below
|
||||
sm.knowsShortCharge())), # spark from across the grapple blocks
|
||||
sm.wand(sm.haveItem('HiJump'), sm.canSpringBallJump())) # jump from the blocks below
|
||||
access = sm.wor(sm.wor(sm.haveItem('SpaceJump'),
|
||||
sm.wand(sm.canInfiniteBombJump(), # IBJ from lava...either have grav or freeze the enemy there if hellrunning (otherwise single DBJ at the end)
|
||||
sm.wor(sm.heatProof(),
|
||||
sm.haveItem('Gravity'),
|
||||
sm.haveItem('Ice')))),
|
||||
sm.haveItem('Grapple'),
|
||||
sm.wand(sm.haveItem('SpeedBooster'),
|
||||
sm.wor(sm.haveItem('HiJump'), # jump from the blocks below
|
||||
sm.knowsShortCharge())), # spark from across the grapple blocks
|
||||
sm.wand(sm.haveItem('HiJump'), sm.canSpringBallJump())) # jump from the blocks below
|
||||
hellrun = 'MainUpperNorfair'
|
||||
tbl = Settings.hellRunsTable[hellrun]['Croc -> Norfair Entrance']
|
||||
mult = tbl['mult']
|
||||
minE = tbl['minE']
|
||||
if 'InfiniteBombJump' in access.knows or 'ShortCharge' in access.knows:
|
||||
mult *= 0.7
|
||||
elif 'SpaceJump' in access.items:
|
||||
mult *= 1.5
|
||||
elif 'Grapple' in access.items:
|
||||
mult *= 1.25
|
||||
return sm.wand(access,
|
||||
sm.canHellRun(hellrun, mult, minE))
|
||||
|
||||
@Cache.decorator
|
||||
def canHellRunBackFromGrappleEscape(self):
|
||||
sm = self.smbm
|
||||
# require more health to count 1st hell run from croc speedway bottom to here+hellrun back (which is faster)
|
||||
hellrun = 'MainUpperNorfair'
|
||||
tbl = Settings.hellRunsTable[hellrun]['Croc -> Norfair Entrance']
|
||||
mult = tbl['mult']
|
||||
minE = tbl['minE']
|
||||
mult *= 0.6
|
||||
return sm.canHellRun(hellrun, mult, minE)
|
||||
|
||||
|
||||
@Cache.decorator
|
||||
def canPassFrogSpeedwayRightToLeft(self):
|
||||
@@ -733,7 +785,9 @@ class HelpersGraph(Helpers):
|
||||
@Cache.decorator
|
||||
def canExitPreciousRoomRandomized(self):
|
||||
sm = self.smbm
|
||||
suitlessRoomExit = sm.canSpringBallJump()
|
||||
suitlessRoomExit = sm.wand(sm.wnot(sm.haveItem('Gravity')),
|
||||
sm.canJumpUnderwater(),
|
||||
sm.canSpringBallJump())
|
||||
if suitlessRoomExit.bool == False:
|
||||
if self.getDraygonConnection() == 'KraidRoomIn':
|
||||
suitlessRoomExit = sm.canShortCharge() # charge spark in kraid's room
|
||||
@@ -764,3 +818,27 @@ class HelpersGraph(Helpers):
|
||||
return sm.wand(sm.traverse('MainStreetBottomRight'),
|
||||
sm.wor(sm.haveItem('Super'),
|
||||
RomPatches.has(sm.player, RomPatches.AreaRandoGatesOther)))
|
||||
|
||||
@Cache.decorator
|
||||
def canAccessShaktoolFromPantsRoom(self):
|
||||
sm = self.smbm
|
||||
return sm.wor(sm.wand(sm.haveItem('Ice'), # puyo clip
|
||||
sm.wor(sm.wand(sm.haveItem('Gravity'),
|
||||
sm.knowsPuyoClip()),
|
||||
sm.wand(sm.haveItem('Gravity'),
|
||||
sm.haveItem('XRayScope'),
|
||||
sm.knowsPuyoClipXRay()),
|
||||
sm.knowsSuitlessPuyoClip())),
|
||||
sm.wand(sm.haveItem('Grapple'), # go through grapple block
|
||||
sm.wor(sm.wand(sm.haveItem('Gravity'),
|
||||
sm.wor(sm.wor(sm.wand(sm.haveItem('HiJump'), sm.knowsAccessSpringBallWithHiJump()),
|
||||
sm.haveItem('SpaceJump')),
|
||||
sm.knowsAccessSpringBallWithGravJump(),
|
||||
sm.wand(sm.haveItem('Bomb'),
|
||||
sm.wor(sm.knowsAccessSpringBallWithBombJumps(),
|
||||
sm.wand(sm.haveItem('SpringBall'),
|
||||
sm.knowsAccessSpringBallWithSpringBallBombJumps()))),
|
||||
sm.wand(sm.haveItem('SpringBall'), sm.knowsAccessSpringBallWithSpringBallJump()))),
|
||||
sm.wand(sm.haveItem('SpaceJump'), sm.knowsAccessSpringBallWithFlatley()))),
|
||||
sm.wand(sm.haveItem('XRayScope'), sm.knowsAccessSpringBallWithXRayClimb()), # XRay climb
|
||||
sm.canCrystalFlashClip())
|
||||
|
||||
@@ -157,7 +157,7 @@ locationsDict["Energy Tank, Crocomire"].AccessFrom = {
|
||||
'Crocomire Room Top': lambda sm: SMBool(True)
|
||||
}
|
||||
locationsDict["Energy Tank, Crocomire"].Available = (
|
||||
lambda sm: sm.wand(sm.enoughStuffCroc(),
|
||||
lambda sm: sm.wand(sm.haveItem('Crocomire'),
|
||||
sm.wor(sm.haveItem('Grapple'),
|
||||
sm.haveItem('SpaceJump'),
|
||||
sm.energyReserveCountOk(3/sm.getDmgReduction()[0])))
|
||||
@@ -176,7 +176,7 @@ locationsDict["Grapple Beam"].AccessFrom = {
|
||||
'Crocomire Room Top': lambda sm: SMBool(True)
|
||||
}
|
||||
locationsDict["Grapple Beam"].Available = (
|
||||
lambda sm: sm.wand(sm.enoughStuffCroc(),
|
||||
lambda sm: sm.wand(sm.haveItem('Crocomire'),
|
||||
sm.wor(sm.wand(sm.haveItem('Morph'),
|
||||
sm.canFly()),
|
||||
sm.wand(sm.haveItem('SpeedBooster'),
|
||||
@@ -220,11 +220,7 @@ locationsDict["Wave Beam"].Available = (
|
||||
lambda sm: sm.traverse('DoubleChamberRight')
|
||||
)
|
||||
locationsDict["Wave Beam"].PostAvailable = (
|
||||
lambda sm: sm.wor(sm.haveItem('Morph'), # exit through lower passage under the spikes
|
||||
sm.wand(sm.wor(sm.haveItem('SpaceJump'), # exit through blue gate
|
||||
sm.haveItem('Grapple')),
|
||||
sm.wor(sm.wand(sm.canBlueGateGlitch(), sm.heatProof()), # hell run + green gate glitch is too much
|
||||
sm.haveItem('Wave'))))
|
||||
lambda sm: sm.canExitWaveBeam()
|
||||
)
|
||||
locationsDict["Ridley"].AccessFrom = {
|
||||
'RidleyRoomIn': lambda sm: SMBool(True)
|
||||
@@ -233,7 +229,7 @@ locationsDict["Ridley"].Available = (
|
||||
lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']), sm.enoughStuffsRidley())
|
||||
)
|
||||
locationsDict["Energy Tank, Ridley"].AccessFrom = {
|
||||
'RidleyRoomIn': lambda sm: sm.haveItem('Ridley')
|
||||
'RidleyRoomIn': lambda sm: sm.wand(sm.haveItem('Ridley'), sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']))
|
||||
}
|
||||
locationsDict["Energy Tank, Ridley"].Available = (
|
||||
lambda sm: sm.haveItem('Morph')
|
||||
@@ -354,27 +350,8 @@ locationsDict["Spring Ball"].AccessFrom = {
|
||||
'Oasis Bottom': lambda sm: sm.canTraverseSandPits()
|
||||
}
|
||||
locationsDict["Spring Ball"].Available = (
|
||||
lambda sm: sm.wand(sm.canUsePowerBombs(), # in Shaktool room to let Shaktool access the sand blocks
|
||||
sm.wor(sm.wand(sm.haveItem('Ice'), # puyo clip
|
||||
sm.wor(sm.wand(sm.haveItem('Gravity'),
|
||||
sm.knowsPuyoClip()),
|
||||
sm.wand(sm.haveItem('Gravity'),
|
||||
sm.haveItem('XRayScope'),
|
||||
sm.knowsPuyoClipXRay()),
|
||||
sm.knowsSuitlessPuyoClip())),
|
||||
sm.wand(sm.haveItem('Grapple'), # go through grapple block
|
||||
sm.wor(sm.wand(sm.haveItem('Gravity'),
|
||||
sm.wor(sm.wor(sm.wand(sm.haveItem('HiJump'), sm.knowsAccessSpringBallWithHiJump()),
|
||||
sm.haveItem('SpaceJump')),
|
||||
sm.knowsAccessSpringBallWithGravJump(),
|
||||
sm.wand(sm.haveItem('Bomb'),
|
||||
sm.wor(sm.knowsAccessSpringBallWithBombJumps(),
|
||||
sm.wand(sm.haveItem('SpringBall'),
|
||||
sm.knowsAccessSpringBallWithSpringBallBombJumps()))),
|
||||
sm.wand(sm.haveItem('SpringBall'), sm.knowsAccessSpringBallWithSpringBallJump()))),
|
||||
sm.wand(sm.haveItem('SpaceJump'), sm.knowsAccessSpringBallWithFlatley()))),
|
||||
sm.wand(sm.haveItem('XRayScope'), sm.knowsAccessSpringBallWithXRayClimb()), # XRay climb
|
||||
sm.canCrystalFlashClip()),
|
||||
lambda sm: sm.wand(sm.canAccessShaktoolFromPantsRoom(),
|
||||
sm.canUsePowerBombs(), # in Shaktool room to let Shaktool access the sand blocks
|
||||
sm.wor(sm.haveItem('Gravity'), sm.canUseSpringBall())) # acess the item in spring ball room
|
||||
)
|
||||
locationsDict["Spring Ball"].PostAvailable = (
|
||||
@@ -406,10 +383,37 @@ locationsDict["Space Jump"].PostAvailable = (
|
||||
lambda sm: Bosses.bossDead(sm, 'Draygon')
|
||||
)
|
||||
locationsDict["Mother Brain"].AccessFrom = {
|
||||
'Golden Four': lambda sm: Bosses.allBossesDead(sm)
|
||||
'Golden Four': lambda sm: sm.canPassG4()
|
||||
}
|
||||
locationsDict["Mother Brain"].Available = (
|
||||
lambda sm: sm.enoughStuffTourian()
|
||||
lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.NoTourian),
|
||||
sm.enoughStuffTourian())
|
||||
)
|
||||
locationsDict["Spore Spawn"].AccessFrom = {
|
||||
'Big Pink': lambda sm: SMBool(True)
|
||||
}
|
||||
locationsDict["Spore Spawn"].Available = (
|
||||
lambda sm: sm.wand(sm.traverse('BigPinkTopRight'),
|
||||
sm.enoughStuffSporeSpawn())
|
||||
)
|
||||
locationsDict["Botwoon"].AccessFrom = {
|
||||
'Aqueduct Bottom': lambda sm: sm.canJumpUnderwater()
|
||||
}
|
||||
locationsDict["Botwoon"].Available = (
|
||||
# includes botwoon hallway conditions
|
||||
lambda sm: sm.canDefeatBotwoon()
|
||||
)
|
||||
locationsDict["Crocomire"].AccessFrom = {
|
||||
'Crocomire Room Top': lambda sm: SMBool(True)
|
||||
}
|
||||
locationsDict["Crocomire"].Available = (
|
||||
lambda sm: sm.enoughStuffCroc()
|
||||
)
|
||||
locationsDict["Golden Torizo"].AccessFrom = {
|
||||
'Screw Attack Bottom': lambda sm: SMBool(True)
|
||||
}
|
||||
locationsDict["Golden Torizo"].Available = (
|
||||
lambda sm: sm.enoughStuffGT()
|
||||
)
|
||||
locationsDict["Power Bomb (Crateria surface)"].AccessFrom = {
|
||||
'Landing Site': lambda sm: SMBool(True)
|
||||
@@ -506,10 +510,10 @@ locationsDict["Super Missile (pink Brinstar)"].AccessFrom = {
|
||||
}
|
||||
locationsDict["Super Missile (pink Brinstar)"].Available = (
|
||||
lambda sm: sm.wor(sm.wand(sm.traverse('BigPinkTopRight'),
|
||||
sm.enoughStuffSporeSpawn()),
|
||||
sm.haveItem('SporeSpawn')),
|
||||
# back way into spore spawn
|
||||
sm.wand(sm.canOpenGreenDoors(),
|
||||
sm.canPassBombPassages()))
|
||||
sm.wand(sm.canOpenGreenDoors(),
|
||||
sm.canPassBombPassages()))
|
||||
)
|
||||
locationsDict["Super Missile (pink Brinstar)"].PostAvailable = (
|
||||
lambda sm: sm.wand(sm.canOpenGreenDoors(),
|
||||
@@ -665,10 +669,10 @@ locationsDict["Missile (below Ice Beam)"].Available = (
|
||||
lambda sm: SMBool(True)
|
||||
)
|
||||
locationsDict["Missile (above Crocomire)"].AccessFrom = {
|
||||
'Crocomire Speedway Bottom': lambda sm: sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Croc -> Grapple Escape Missiles'])
|
||||
'Grapple Escape': lambda sm: SMBool(True)
|
||||
}
|
||||
locationsDict["Missile (above Crocomire)"].Available = (
|
||||
lambda sm: sm.canGrappleEscape()
|
||||
lambda sm: SMBool(True)
|
||||
)
|
||||
locationsDict["Missile (Hi-Jump Boots)"].AccessFrom = {
|
||||
'Business Center': lambda sm: sm.wor(RomPatches.has(sm.player, RomPatches.HiJumpAreaBlueDoor), sm.traverse('BusinessCenterBottomLeft'))
|
||||
@@ -691,7 +695,7 @@ locationsDict["Power Bomb (Crocomire)"].AccessFrom = {
|
||||
}
|
||||
locationsDict["Power Bomb (Crocomire)"].Available = (
|
||||
lambda sm: sm.wand(sm.traverse('PostCrocomireUpperLeft'),
|
||||
sm.enoughStuffCroc(),
|
||||
sm.haveItem('Crocomire'),
|
||||
sm.wor(sm.wor(sm.canFly(),
|
||||
sm.haveItem('Grapple'),
|
||||
sm.wand(sm.haveItem('SpeedBooster'),
|
||||
@@ -706,13 +710,13 @@ locationsDict["Missile (below Crocomire)"].AccessFrom = {
|
||||
'Crocomire Room Top': lambda sm: SMBool(True)
|
||||
}
|
||||
locationsDict["Missile (below Crocomire)"].Available = (
|
||||
lambda sm: sm.wand(sm.traverse('PostCrocomireShaftRight'), sm.enoughStuffCroc(), sm.haveItem('Morph'))
|
||||
lambda sm: sm.wand(sm.traverse('PostCrocomireShaftRight'), sm.haveItem('Crocomire'), sm.haveItem('Morph'))
|
||||
)
|
||||
locationsDict["Missile (Grapple Beam)"].AccessFrom = {
|
||||
'Crocomire Room Top': lambda sm: SMBool(True)
|
||||
}
|
||||
locationsDict["Missile (Grapple Beam)"].Available = (
|
||||
lambda sm: sm.wand(sm.enoughStuffCroc(),
|
||||
lambda sm: sm.wand(sm.haveItem('Crocomire'),
|
||||
sm.wor(sm.wor(sm.wand(sm.haveItem('Morph'), # from below
|
||||
sm.canFly()),
|
||||
sm.wand(sm.haveItem('SpeedBooster'),
|
||||
@@ -754,6 +758,9 @@ locationsDict["Missile (Speed Booster)"].AccessFrom = {
|
||||
locationsDict["Missile (Speed Booster)"].Available = (
|
||||
lambda sm: sm.canHellRunToSpeedBooster()
|
||||
)
|
||||
locationsDict["Missile (Speed Booster)"].PostAvailable = (
|
||||
lambda sm: sm.canHellRunBackFromSpeedBoosterMissile()
|
||||
)
|
||||
locationsDict["Missile (Wave Beam)"].AccessFrom = {
|
||||
'Bubble Mountain Top': lambda sm: sm.canAccessDoubleChamberItems()
|
||||
}
|
||||
@@ -773,7 +780,7 @@ locationsDict["Super Missile (Gold Torizo)"].AccessFrom = {
|
||||
'Screw Attack Bottom': lambda sm: SMBool(True)
|
||||
}
|
||||
locationsDict["Super Missile (Gold Torizo)"].Available = (
|
||||
lambda sm: SMBool(True)
|
||||
lambda sm: sm.canDestroyBombWalls()
|
||||
)
|
||||
locationsDict["Super Missile (Gold Torizo)"].PostAvailable = (
|
||||
lambda sm: sm.enoughStuffGT()
|
||||
|
||||
Reference in New Issue
Block a user