767 lines
35 KiB
Python
767 lines
35 KiB
Python
from math import ceil
|
|
|
|
from ...logic.smbool import SMBool
|
|
from ...logic.helpers import Helpers, Bosses
|
|
from ...logic.cache import Cache
|
|
from ...rom.rom_patches import RomPatches
|
|
from ...graph.graph_utils import getAccessPoint
|
|
from ...utils.parameters import Settings
|
|
|
|
class HelpersGraph(Helpers):
|
|
def __init__(self, smbm):
|
|
self.smbm = smbm
|
|
|
|
def canEnterAndLeaveGauntletQty(self, nPB, nTanksSpark):
|
|
sm = self.smbm
|
|
# EXPLAINED: to access Gauntlet Entrance from Landing site we can either:
|
|
# -fly to it (infinite bomb jumps or space jump)
|
|
# -shinespark to it
|
|
# -wall jump with high jump boots
|
|
# -wall jump without high jump boots
|
|
# then inside it to break the bomb wals:
|
|
# -use screw attack (easy way)
|
|
# -use power bombs
|
|
# -use bombs
|
|
# -perform a simple short charge on the way in
|
|
# and use power bombs on the way out
|
|
return sm.wand(sm.wor(sm.canFly(),
|
|
sm.haveItem('SpeedBooster'),
|
|
sm.wand(sm.knowsHiJumpGauntletAccess(),
|
|
sm.haveItem('HiJump')),
|
|
sm.knowsHiJumpLessGauntletAccess()),
|
|
sm.wor(sm.haveItem('ScrewAttack'),
|
|
sm.wor(sm.wand(sm.energyReserveCountOkHardRoom('Gauntlet'),
|
|
sm.wand(sm.canUsePowerBombs(),
|
|
sm.wor(sm.itemCountOk('PowerBomb', nPB),
|
|
sm.wand(sm.haveItem('SpeedBooster'),
|
|
sm.energyReserveCountOk(nTanksSpark))))),
|
|
sm.wand(sm.energyReserveCountOkHardRoom('Gauntlet', 0.51),
|
|
sm.canUseBombs()))))
|
|
|
|
@Cache.decorator
|
|
def canEnterAndLeaveGauntlet(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.wand(sm.canShortCharge(),
|
|
sm.canEnterAndLeaveGauntletQty(2, 2)),
|
|
sm.canEnterAndLeaveGauntletQty(2, 3))
|
|
|
|
def canPassTerminatorBombWall(self, fromLandingSite=True):
|
|
sm = self.smbm
|
|
return sm.wor(sm.wand(sm.haveItem('SpeedBooster'),
|
|
sm.wor(SMBool(not fromLandingSite, 0), sm.knowsSimpleShortCharge(), sm.knowsShortCharge())),
|
|
sm.canDestroyBombWalls())
|
|
|
|
@Cache.decorator
|
|
def canPassCrateriaGreenPirates(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.canPassBombPassages(),
|
|
sm.haveMissileOrSuper(),
|
|
sm.energyReserveCountOk(1),
|
|
sm.wor(sm.haveItem('Charge'),
|
|
sm.haveItem('Ice'),
|
|
sm.haveItem('Wave'),
|
|
sm.wor(sm.haveItem('Spazer'),
|
|
sm.haveItem('Plasma'),
|
|
sm.haveItem('ScrewAttack'))))
|
|
|
|
# from blue brin elevator
|
|
@Cache.decorator
|
|
def canAccessBillyMays(self):
|
|
sm = self.smbm
|
|
return sm.wand(sm.wor(RomPatches.has(sm.player, RomPatches.BlueBrinstarBlueDoor),
|
|
sm.traverse('ConstructionZoneRight')),
|
|
sm.canUsePowerBombs(),
|
|
sm.wor(sm.knowsBillyMays(),
|
|
sm.haveItem('Gravity'),
|
|
sm.haveItem('SpaceJump')))
|
|
|
|
@Cache.decorator
|
|
def canAccessKraidsLair(self):
|
|
sm = self.smbm
|
|
# EXPLAINED: access the upper right platform with either:
|
|
# -hijump boots (easy regular way)
|
|
# -fly (space jump or infinite bomb jump)
|
|
# -know how to wall jump on the platform without the hijump boots
|
|
return sm.wand(sm.haveItem('Super'),
|
|
sm.wor(sm.haveItem('HiJump'),
|
|
sm.canFly(),
|
|
sm.knowsEarlyKraid()))
|
|
|
|
@Cache.decorator
|
|
def canPassMoat(self):
|
|
sm = self.smbm
|
|
# EXPLAINED: In the Moat we can either:
|
|
# -use grapple or space jump (easy way)
|
|
# -do a continuous wall jump (https://www.youtube.com/watch?v=4HVhTwwax6g)
|
|
# -do a diagonal bomb jump from the middle platform (https://www.youtube.com/watch?v=5NRqQ7RbK3A&t=10m58s)
|
|
# -do a short charge from the Keyhunter room (https://www.youtube.com/watch?v=kFAYji2gFok)
|
|
# -do a gravity jump from below the right platform
|
|
# -do a mock ball and a bounce ball (https://www.youtube.com/watch?v=WYxtRF--834)
|
|
# -with gravity, either hijump or IBJ
|
|
return sm.wor(sm.haveItem('Grapple'),
|
|
sm.haveItem('SpaceJump'),
|
|
sm.knowsContinuousWallJump(),
|
|
sm.wand(sm.knowsDiagonalBombJump(), sm.canUseBombs()),
|
|
sm.canSimpleShortCharge(),
|
|
sm.wand(sm.haveItem('Gravity'),
|
|
sm.wor(sm.knowsGravityJump(),
|
|
sm.haveItem('HiJump'),
|
|
sm.canInfiniteBombJump())),
|
|
sm.wand(sm.knowsMockballWs(), sm.canUseSpringBall()))
|
|
|
|
@Cache.decorator
|
|
def canPassMoatFromMoat(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('Grapple'),
|
|
sm.haveItem('SpaceJump'),
|
|
sm.wand(sm.knowsDiagonalBombJump(), sm.canUseBombs()),
|
|
sm.wand(sm.haveItem('Gravity'),
|
|
sm.wor(sm.knowsGravityJump(),
|
|
sm.haveItem('HiJump'),
|
|
sm.canInfiniteBombJump())))
|
|
|
|
@Cache.decorator
|
|
def canPassMoatReverse(self):
|
|
sm = self.smbm
|
|
return sm.wor(RomPatches.has(sm.player, RomPatches.MoatShotBlock),
|
|
sm.haveItem('Grapple'),
|
|
sm.haveItem('SpaceJump'),
|
|
sm.haveItem('Gravity'),
|
|
sm.canPassBombPassages())
|
|
|
|
@Cache.decorator
|
|
def canPassSpongeBath(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.wand(sm.canPassBombPassages(),
|
|
sm.knowsSpongeBathBombJump()),
|
|
sm.wand(sm.haveItem('HiJump'),
|
|
sm.knowsSpongeBathHiJump()),
|
|
sm.haveItem('Gravity'),
|
|
sm.haveItem('SpaceJump'),
|
|
sm.wand(sm.haveItem('SpeedBooster'),
|
|
sm.knowsSpongeBathSpeed()),
|
|
sm.canSpringBallJump())
|
|
|
|
@Cache.decorator
|
|
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")))
|
|
|
|
@Cache.decorator
|
|
def canAccessEtecoons(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.canUsePowerBombs(),
|
|
sm.wand(sm.knowsMoondance(), sm.canUseBombs(), sm.traverse('MainShaftBottomRight')))
|
|
|
|
@Cache.decorator
|
|
def canKillBeetoms(self):
|
|
sm = self.smbm
|
|
# can technically be killed with bomb, but it's harder
|
|
return sm.wor(sm.haveMissileOrSuper(), sm.canUsePowerBombs(), sm.haveItem('ScrewAttack'))
|
|
|
|
# the water zone east of WS
|
|
def canPassForgottenHighway(self, fromWs):
|
|
sm = self.smbm
|
|
suitless = sm.wand(sm.haveItem('HiJump'), sm.knowsGravLessLevel1())
|
|
if fromWs is True and RomPatches.has(sm.player, RomPatches.EastOceanPlatforms).bool is False:
|
|
suitless = sm.wand(suitless,
|
|
sm.wor(sm.canSpringBallJump(), # two sbj on the far right
|
|
# to break water line and go through the door on the right
|
|
sm.haveItem('SpaceJump')))
|
|
return sm.wand(sm.wor(sm.haveItem('Gravity'),
|
|
suitless),
|
|
sm.haveItem('Morph')) # for crab maze
|
|
|
|
@Cache.decorator
|
|
def canExitCrabHole(self):
|
|
sm = self.smbm
|
|
return sm.wand(sm.haveItem('Morph'), # morph to exit the hole
|
|
sm.wor(sm.wand(sm.haveItem('Gravity'), # even with gravity you need some way to climb...
|
|
sm.wor(sm.haveItem('Ice'), # ...on crabs...
|
|
sm.wand(sm.haveItem('HiJump'), sm.knowsMaridiaWallJumps()), # ...or by jumping
|
|
sm.knowsGravityJump(),
|
|
sm.canFly())),
|
|
sm.wand(sm.haveItem('Ice'), sm.canDoSuitlessOuterMaridia()), # climbing crabs
|
|
sm.canDoubleSpringBallJump()))
|
|
|
|
# bottom sandpits with the evirs except west sand hall left to right
|
|
@Cache.decorator
|
|
def canTraverseSandPits(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('Gravity'),
|
|
sm.wand(sm.knowsGravLessLevel3(),
|
|
sm.haveItem('HiJump'),
|
|
sm.haveItem('Ice')))
|
|
|
|
@Cache.decorator
|
|
def canTraverseWestSandHallLeftToRight(self):
|
|
sm = self.smbm
|
|
return sm.haveItem('Gravity') # FIXME find suitless condition
|
|
|
|
@Cache.decorator
|
|
def canPassMaridiaToRedTowerNode(self):
|
|
sm = self.smbm
|
|
return sm.wand(sm.haveItem('Morph'),
|
|
sm.wor(RomPatches.has(sm.player, RomPatches.AreaRandoGatesBase),
|
|
sm.haveItem('Super')))
|
|
|
|
@Cache.decorator
|
|
def canPassRedTowerToMaridiaNode(self):
|
|
sm = self.smbm
|
|
return sm.wand(sm.haveItem('Morph'),
|
|
RomPatches.has(sm.player, RomPatches.AreaRandoGatesBase))
|
|
|
|
def canEnterCathedral(self, mult=1.0):
|
|
sm = self.smbm
|
|
return sm.wand(sm.traverse('CathedralEntranceRight'),
|
|
sm.wor(sm.wand(sm.canHellRun('MainUpperNorfair', mult),
|
|
sm.wor(sm.wor(RomPatches.has(sm.player, RomPatches.CathedralEntranceWallJump),
|
|
sm.haveItem('HiJump'),
|
|
sm.canFly()),
|
|
sm.wor(sm.haveItem('SpeedBooster'), # spark
|
|
sm.canSpringBallJump()))),
|
|
sm.wand(sm.canHellRun('MainUpperNorfair', 0.5*mult),
|
|
sm.haveItem('Morph'),
|
|
sm.knowsNovaBoost())))
|
|
|
|
@Cache.decorator
|
|
def canClimbBubbleMountain(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('HiJump'),
|
|
sm.canFly(),
|
|
sm.haveItem('Ice'),
|
|
sm.knowsBubbleMountainWallJump())
|
|
|
|
@Cache.decorator
|
|
def canHellRunToSpeedBooster(self):
|
|
sm = self.smbm
|
|
return sm.canHellRun(**Settings.hellRunsTable['MainUpperNorfair']['Bubble -> Speed Booster w/Speed' if sm.haveItem('SpeedBooster') else 'Bubble -> Speed Booster'])
|
|
|
|
@Cache.decorator
|
|
def canAccessDoubleChamberItems(self):
|
|
sm = self.smbm
|
|
hellRun = Settings.hellRunsTable['MainUpperNorfair']['Bubble -> Wave']
|
|
return sm.wor(sm.wand(sm.traverse('SingleChamberRight'),
|
|
sm.canHellRun(**hellRun)),
|
|
sm.wand(sm.wor(sm.haveItem('HiJump'),
|
|
sm.canSimpleShortCharge(),
|
|
sm.canFly(),
|
|
sm.knowsDoubleChamberWallJump()),
|
|
sm.canHellRun(hellRun['hellRun'], hellRun['mult']*0.8, hellRun['minE'])))
|
|
|
|
def canExitCathedral(self, hellRun):
|
|
# from top: can use bomb/powerbomb jumps
|
|
# from bottom: can do a shinespark or use space jump
|
|
# can do it with highjump + wall jump
|
|
# can do it with only two wall jumps (the first one is delayed like on alcatraz)
|
|
# can do it with a spring ball jump from wall
|
|
sm = self.smbm
|
|
return sm.wand(sm.wor(sm.canHellRun(**hellRun),
|
|
sm.heatProof()),
|
|
sm.wor(sm.wor(sm.canPassBombPassages(),
|
|
sm.haveItem("SpeedBooster")),
|
|
sm.wor(sm.haveItem("SpaceJump"),
|
|
sm.haveItem("HiJump"),
|
|
sm.knowsWallJumpCathedralExit(),
|
|
sm.wand(sm.knowsSpringBallJumpFromWall(), sm.canUseSpringBall()))))
|
|
|
|
@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
|
|
|
|
@Cache.decorator
|
|
def canPassFrogSpeedwayRightToLeft(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('SpeedBooster'),
|
|
sm.wand(sm.knowsFrogSpeedwayWithoutSpeed(),
|
|
sm.haveItem('Wave'),
|
|
sm.wor(sm.haveItem('Spazer'),
|
|
sm.haveItem('Plasma'))))
|
|
|
|
@Cache.decorator
|
|
def canEnterNorfairReserveAreaFromBubbleMoutain(self):
|
|
sm = self.smbm
|
|
return sm.wand(sm.traverse('BubbleMountainTopLeft'),
|
|
sm.wor(sm.canFly(),
|
|
sm.haveItem('Ice'),
|
|
sm.wand(sm.haveItem('HiJump'),
|
|
sm.knowsGetAroundWallJump()),
|
|
sm.wand(sm.canUseSpringBall(),
|
|
sm.knowsSpringBallJumpFromWall())))
|
|
|
|
@Cache.decorator
|
|
def canEnterNorfairReserveAreaFromBubbleMoutainTop(self):
|
|
sm = self.smbm
|
|
return sm.wand(sm.traverse('BubbleMountainTopLeft'),
|
|
sm.wor(sm.haveItem('Grapple'),
|
|
sm.haveItem('SpaceJump'),
|
|
sm.knowsNorfairReserveDBoost()))
|
|
|
|
@Cache.decorator
|
|
def canPassLavaPit(self):
|
|
sm = self.smbm
|
|
nTanks4Dive = 8 / sm.getDmgReduction()[0]
|
|
if sm.haveItem('HiJump').bool == False:
|
|
nTanks4Dive = ceil(nTanks4Dive * 1.25)
|
|
return sm.wand(sm.wor(sm.wand(sm.haveItem('Gravity'), sm.haveItem('SpaceJump')),
|
|
sm.wand(sm.knowsGravityJump(), sm.haveItem('Gravity'), sm.wor(sm.haveItem('HiJump'), sm.knowsLavaDive())),
|
|
sm.wand(sm.wor(sm.wand(sm.knowsLavaDive(), sm.haveItem('HiJump')),
|
|
sm.knowsLavaDiveNoHiJump()),
|
|
sm.energyReserveCountOk(nTanks4Dive))),
|
|
sm.canUsePowerBombs()) # power bomb blocks left and right of LN entrance without any items before
|
|
|
|
@Cache.decorator
|
|
def canPassLavaPitReverse(self):
|
|
sm = self.smbm
|
|
nTanks = 2
|
|
if sm.heatProof().bool == False:
|
|
nTanks = 6
|
|
return sm.energyReserveCountOk(nTanks)
|
|
|
|
@Cache.decorator
|
|
def canPassLowerNorfairChozo(self):
|
|
sm = self.smbm
|
|
# to require one more CF if no heat protection because of distance to cover, wait times, acid...
|
|
return sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Entrance -> GT via Chozo']),
|
|
sm.canUsePowerBombs(),
|
|
sm.wor(RomPatches.has(sm.player, RomPatches.LNChozoSJCheckDisabled), sm.haveItem('SpaceJump')))
|
|
|
|
@Cache.decorator
|
|
def canExitScrewAttackArea(self):
|
|
sm = self.smbm
|
|
|
|
return sm.wand(sm.canDestroyBombWalls(),
|
|
sm.wor(sm.canFly(),
|
|
sm.wand(sm.haveItem('HiJump'),
|
|
sm.haveItem('SpeedBooster'),
|
|
sm.wor(sm.wand(sm.haveItem('ScrewAttack'), sm.knowsScrewAttackExit()),
|
|
sm.knowsScrewAttackExitWithoutScrew())),
|
|
sm.wand(sm.canUseSpringBall(),
|
|
sm.knowsSpringBallJumpFromWall()),
|
|
sm.wand(sm.canSimpleShortCharge(), # fight GT and spark out
|
|
sm.enoughStuffGT())))
|
|
|
|
@Cache.decorator
|
|
def canPassWorstRoom(self):
|
|
sm = self.smbm
|
|
return sm.wand(sm.canDestroyBombWalls(),
|
|
sm.canPassWorstRoomPirates(),
|
|
sm.wor(sm.canFly(),
|
|
sm.wand(sm.knowsWorstRoomIceCharge(), sm.haveItem('Ice'), sm.canFireChargedShots()),
|
|
sm.wor(sm.wand(sm.knowsGetAroundWallJump(), sm.haveItem('HiJump')),
|
|
sm.knowsWorstRoomWallJump()),
|
|
sm.wand(sm.knowsSpringBallJumpFromWall(), sm.canUseSpringBall())))
|
|
|
|
# checks mix of super missiles/health
|
|
def canGoThroughLowerNorfairEnemy(self, nmyHealth, nbNmy, nmyHitDmg, supDmg=300.0):
|
|
sm = self.smbm
|
|
# supers only
|
|
if sm.itemCount('Super')*5*supDmg >= nbNmy*nmyHealth:
|
|
return SMBool(True, 0, items=['Super'])
|
|
|
|
# - or with taking damage as well?
|
|
(dmgRed, redItems) = sm.getDmgReduction(envDmg=False)
|
|
dmg = nmyHitDmg / dmgRed
|
|
if sm.heatProof() and (sm.itemCount('Super')*5*supDmg)/nmyHealth + (sm.energyReserveCount()*100 - 2)/dmg >= nbNmy:
|
|
# require heat proof as long as taking damage is necessary.
|
|
# display all the available energy in the solver.
|
|
return sm.wand(sm.heatProof(), SMBool(True, 0, items=redItems+['Super', '{}-ETank - {}-Reserve'.format(self.smbm.itemCount('ETank'), self.smbm.itemCount('Reserve'))]))
|
|
|
|
return sm.knowsDodgeLowerNorfairEnemies()
|
|
|
|
def canKillRedKiHunters(self, n):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('Plasma'),
|
|
sm.haveItem('ScrewAttack'),
|
|
sm.wand(sm.heatProof(), # this takes a loooong time ...
|
|
sm.wor(sm.haveItem('Spazer'),
|
|
sm.haveItem('Ice'),
|
|
sm.wand(sm.haveItem('Charge'),
|
|
sm.haveItem('Wave')))),
|
|
sm.canGoThroughLowerNorfairEnemy(1800.0, float(n), 200.0))
|
|
|
|
@Cache.decorator
|
|
def canPassThreeMuskateers(self):
|
|
sm = self.smbm
|
|
return sm.canKillRedKiHunters(6)
|
|
|
|
@Cache.decorator
|
|
def canPassRedKiHunters(self):
|
|
sm = self.smbm
|
|
return sm.canKillRedKiHunters(3)
|
|
|
|
@Cache.decorator
|
|
def canPassWastelandDessgeegas(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('Plasma'),
|
|
sm.haveItem('ScrewAttack'),
|
|
sm.wand(sm.heatProof(), # this takes a loooong time ...
|
|
sm.wor(sm.haveItem('Spazer'),
|
|
sm.wand(sm.haveItem('Charge'),
|
|
sm.haveItem('Wave')))),
|
|
sm.itemCountOk('PowerBomb', 4),
|
|
sm.canGoThroughLowerNorfairEnemy(800.0, 3.0, 160.0))
|
|
|
|
@Cache.decorator
|
|
def canPassNinjaPirates(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.itemCountOk('Missile', 10),
|
|
sm.itemCountOk('Super', 2),
|
|
sm.haveItem('Plasma'),
|
|
sm.wor(sm.haveItem('Spazer'),
|
|
sm.wand(sm.haveItem('Charge'),
|
|
sm.wor(sm.haveItem('Wave'),
|
|
sm.haveItem('Ice')))),
|
|
sm.canShortCharge()) # echoes kill
|
|
|
|
@Cache.decorator
|
|
def canPassWorstRoomPirates(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('ScrewAttack'),
|
|
sm.itemCountOk('Missile', 6),
|
|
sm.itemCountOk('Super', 3),
|
|
sm.wand(sm.canFireChargedShots(), sm.haveItem('Plasma')),
|
|
sm.wand(sm.haveItem('Charge'),
|
|
sm.wor(sm.haveItem('Spazer'),
|
|
sm.haveItem('Wave'),
|
|
sm.haveItem('Ice'))),
|
|
sm.knowsDodgeLowerNorfairEnemies())
|
|
|
|
# go though the pirates room filled with acid
|
|
@Cache.decorator
|
|
def canPassAmphitheaterReverse(self):
|
|
sm = self.smbm
|
|
dmgRed = sm.getDmgReduction()[0]
|
|
nTanksGrav = 4 * 4/dmgRed
|
|
nTanksNoGrav = 6 * 4/dmgRed
|
|
return sm.wor(sm.wand(sm.haveItem('Gravity'),
|
|
sm.energyReserveCountOk(nTanksGrav)),
|
|
sm.wand(sm.energyReserveCountOk(nTanksNoGrav),
|
|
sm.knowsLavaDive())) # should be a good enough skill filter for acid wall jumps with no grav...
|
|
|
|
@Cache.decorator
|
|
def canGetBackFromRidleyZone(self):
|
|
sm = self.smbm
|
|
return sm.wand(sm.canUsePowerBombs(),
|
|
sm.wor(sm.canUseSpringBall(),
|
|
sm.canUseBombs(),
|
|
sm.itemCountOk('PowerBomb', 2),
|
|
sm.haveItem('ScrewAttack'),
|
|
sm.canShortCharge()), # speedball
|
|
# in escape you don't have PBs and can't shoot bomb blocks in long tunnels
|
|
# in wasteland and ki hunter room
|
|
sm.wnot(sm.canUseHyperBeam()))
|
|
|
|
@Cache.decorator
|
|
def canClimbRedTower(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.knowsRedTowerClimb(),
|
|
sm.haveItem('Ice'),
|
|
sm.haveItem('SpaceJump'))
|
|
|
|
@Cache.decorator
|
|
def canClimbBottomRedTower(self):
|
|
sm = self.smbm
|
|
return sm.wor(RomPatches.has(sm.player, RomPatches.RedTowerLeftPassage),
|
|
sm.haveItem('HiJump'),
|
|
sm.haveItem('Ice'),
|
|
sm.canFly(),
|
|
sm.canShortCharge())
|
|
|
|
@Cache.decorator
|
|
def canGoUpMtEverest(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.wand(sm.haveItem('Gravity'),
|
|
sm.wor(sm.haveItem('Grapple'),
|
|
sm.haveItem('SpeedBooster'),
|
|
sm.canFly(),
|
|
sm.wand(sm.knowsGravityJump(),
|
|
sm.wor(sm.haveItem('HiJump'),
|
|
sm.knowsMtEverestGravJump())))),
|
|
sm.wand(sm.canDoSuitlessOuterMaridia(),
|
|
sm.haveItem('Grapple')))
|
|
|
|
@Cache.decorator
|
|
def canPassMtEverest(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.wand(sm.haveItem('Gravity'),
|
|
sm.wor(sm.haveItem('Grapple'),
|
|
sm.haveItem('SpeedBooster'),
|
|
sm.canFly(),
|
|
sm.knowsGravityJump())),
|
|
sm.wand(sm.canDoSuitlessOuterMaridia(),
|
|
sm.wor(sm.haveItem('Grapple'),
|
|
sm.wand(sm.haveItem('Ice'), sm.knowsTediousMountEverest(), sm.haveItem('Super')),
|
|
sm.canDoubleSpringBallJump())))
|
|
|
|
@Cache.decorator
|
|
def canJumpUnderwater(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('Gravity'),
|
|
sm.wand(sm.knowsGravLessLevel1(),
|
|
sm.haveItem('HiJump')))
|
|
|
|
@Cache.decorator
|
|
def canDoSuitlessOuterMaridia(self):
|
|
sm = self.smbm
|
|
return sm.wand(sm.knowsGravLessLevel1(),
|
|
sm.haveItem('HiJump'),
|
|
sm.wor(sm.haveItem('Ice'),
|
|
sm.canSpringBallJump()))
|
|
|
|
@Cache.decorator
|
|
def canDoOuterMaridia(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('Gravity'),
|
|
sm.canDoSuitlessOuterMaridia())
|
|
|
|
@Cache.decorator
|
|
def canPassBotwoonHallway(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.wand(sm.haveItem('SpeedBooster'),
|
|
sm.haveItem('Gravity')),
|
|
sm.wand(sm.knowsMochtroidClip(), sm.haveItem('Ice')),
|
|
sm.canCrystalFlashClip())
|
|
|
|
@Cache.decorator
|
|
def canDefeatBotwoon(self):
|
|
sm = self.smbm
|
|
return sm.wand(sm.enoughStuffBotwoon(),
|
|
sm.canPassBotwoonHallway())
|
|
|
|
# the sandpits from aqueduct
|
|
@Cache.decorator
|
|
def canAccessSandPits(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('Gravity'),
|
|
sm.wand(sm.haveItem('HiJump'),
|
|
sm.knowsGravLessLevel3()))
|
|
|
|
@Cache.decorator
|
|
def canReachCacatacAlleyFromBotowoon(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('Gravity'),
|
|
sm.wand(sm.knowsGravLessLevel2(),
|
|
sm.haveItem("HiJump"),
|
|
sm.wor(sm.haveItem('Grapple'),
|
|
sm.haveItem('Ice'),
|
|
sm.canDoubleSpringBallJump())))
|
|
|
|
@Cache.decorator
|
|
def canPassCacatacAlley(self):
|
|
sm = self.smbm
|
|
return sm.wand(Bosses.bossDead(sm, 'Draygon'),
|
|
sm.haveItem('Morph'),
|
|
sm.wor(sm.haveItem('Gravity'),
|
|
sm.wand(sm.knowsGravLessLevel2(),
|
|
sm.haveItem('HiJump'),
|
|
sm.haveItem('SpaceJump'))))
|
|
|
|
@Cache.decorator
|
|
def canGoThroughColosseumSuitless(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('Grapple'),
|
|
sm.haveItem('SpaceJump'),
|
|
sm.wand(sm.haveItem('Ice'),
|
|
sm.energyReserveCountOk(int(7.0/sm.getDmgReduction(False)[0])), # mochtroid dmg
|
|
sm.knowsBotwoonToDraygonWithIce()))
|
|
|
|
@Cache.decorator
|
|
def canBotwoonExitToColosseum(self):
|
|
sm = self.smbm
|
|
# traverse Botwoon Energy Tank Room
|
|
return sm.wand(sm.wor(sm.wand(sm.haveItem('Gravity'), sm.haveItem('SpeedBooster')),
|
|
sm.wand(sm.haveItem('Morph'), sm.canJumpUnderwater())),
|
|
# after Botwoon Energy Tank Room
|
|
sm.wor(sm.haveItem('Gravity'),
|
|
sm.wand(sm.knowsGravLessLevel2(),
|
|
sm.haveItem("HiJump"),
|
|
# get to top right door
|
|
sm.wor(sm.haveItem('Grapple'),
|
|
sm.haveItem('Ice'), # climb mochtroids
|
|
sm.wand(sm.canDoubleSpringBallJump(),
|
|
sm.haveItem('SpaceJump'))),
|
|
sm.canGoThroughColosseumSuitless())))
|
|
|
|
@Cache.decorator
|
|
def canColosseumToBotwoonExit(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('Gravity'),
|
|
sm.wand(sm.knowsGravLessLevel2(),
|
|
sm.haveItem("HiJump"),
|
|
sm.canGoThroughColosseumSuitless()))
|
|
|
|
@Cache.decorator
|
|
def canClimbColosseum(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('Gravity'),
|
|
sm.wand(sm.knowsGravLessLevel2(),
|
|
sm.haveItem("HiJump"),
|
|
sm.wor(sm.haveItem('Grapple'),
|
|
sm.haveItem('Ice'),
|
|
sm.knowsPreciousRoomGravJumpExit())))
|
|
|
|
@Cache.decorator
|
|
def canClimbWestSandHole(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('Gravity'),
|
|
sm.wand(sm.haveItem('HiJump'),
|
|
sm.knowsGravLessLevel3(),
|
|
sm.wor(sm.haveItem('SpaceJump'),
|
|
sm.canSpringBallJump(),
|
|
sm.knowsWestSandHoleSuitlessWallJumps())))
|
|
|
|
@Cache.decorator
|
|
def canAccessItemsInWestSandHole(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.wand(sm.haveItem('HiJump'), # vanilla strat
|
|
sm.canUseSpringBall()),
|
|
sm.wand(sm.haveItem('SpaceJump'), # alternate strat with possible double bomb jump but no difficult wj
|
|
sm.wor(sm.canUseSpringBall(),
|
|
sm.canUseBombs())),
|
|
sm.wand(sm.canPassBombPassages(), # wjs and/or 3 tile mid air morph
|
|
sm.knowsMaridiaWallJumps()))
|
|
|
|
@Cache.decorator
|
|
def getDraygonConnection(self):
|
|
return getAccessPoint('DraygonRoomOut').ConnectedTo
|
|
|
|
@Cache.decorator
|
|
def isVanillaDraygon(self):
|
|
return SMBool(self.getDraygonConnection() == 'DraygonRoomIn')
|
|
|
|
@Cache.decorator
|
|
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))
|
|
|
|
@Cache.decorator
|
|
def canFightDraygon(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('Gravity'),
|
|
sm.wand(sm.haveItem('HiJump'),
|
|
sm.wor(sm.knowsGravLessLevel2(),
|
|
sm.knowsGravLessLevel3())))
|
|
|
|
@Cache.decorator
|
|
def canDraygonCrystalFlashSuit(self):
|
|
sm = self.smbm
|
|
return sm.wand(sm.canCrystalFlash(),
|
|
sm.knowsDraygonRoomCrystalFlash(),
|
|
# ask for 4 PB pack as an ugly workaround for
|
|
# a rando bug which can place a PB at space
|
|
# jump to "get you out" (this check is in
|
|
# PostAvailable condition of the Dray/Space
|
|
# Jump locs)
|
|
sm.itemCountOk('PowerBomb', 4))
|
|
|
|
@Cache.decorator
|
|
def canExitDraygonRoomWithGravity(self):
|
|
sm = self.smbm
|
|
return sm.wand(sm.haveItem('Gravity'),
|
|
sm.wor(sm.canFly(),
|
|
sm.knowsGravityJump(),
|
|
sm.wand(sm.haveItem('HiJump'),
|
|
sm.haveItem('SpeedBooster'))))
|
|
|
|
@Cache.decorator
|
|
def canGrappleExitDraygon(self):
|
|
sm = self.smbm
|
|
return sm.wand(sm.haveItem('Grapple'),
|
|
sm.knowsDraygonRoomGrappleExit())
|
|
|
|
@Cache.decorator
|
|
def canExitDraygonVanilla(self):
|
|
sm = self.smbm
|
|
# to get out of draygon room:
|
|
# with gravity but without highjump/bomb/space jump: gravity jump
|
|
# to exit draygon room: grapple or crystal flash (for free shine spark)
|
|
# to exit precious room: spring ball jump, xray scope glitch or stored spark
|
|
return sm.wor(sm.canExitDraygonRoomWithGravity(),
|
|
sm.wand(sm.canDraygonCrystalFlashSuit(),
|
|
# use the spark either to exit draygon room or precious room
|
|
sm.wor(sm.canGrappleExitDraygon(),
|
|
sm.wand(sm.haveItem('XRayScope'),
|
|
sm.knowsPreciousRoomXRayExit()),
|
|
sm.canSpringBallJump())),
|
|
# spark-less exit (no CF)
|
|
sm.wand(sm.canGrappleExitDraygon(),
|
|
sm.wor(sm.wand(sm.haveItem('XRayScope'),
|
|
sm.knowsPreciousRoomXRayExit()),
|
|
sm.canSpringBallJump())),
|
|
sm.canDoubleSpringBallJump())
|
|
|
|
@Cache.decorator
|
|
def canExitDraygonRandomized(self):
|
|
sm = self.smbm
|
|
# disregard precious room
|
|
return sm.wor(sm.canExitDraygonRoomWithGravity(),
|
|
sm.canDraygonCrystalFlashSuit(),
|
|
sm.canGrappleExitDraygon(),
|
|
sm.canDoubleSpringBallJump())
|
|
|
|
@Cache.decorator
|
|
def canExitDraygon(self):
|
|
sm = self.smbm
|
|
if self.isVanillaDraygon():
|
|
return self.canExitDraygonVanilla()
|
|
else:
|
|
return self.canExitDraygonRandomized()
|
|
|
|
@Cache.decorator
|
|
def canExitPreciousRoomVanilla(self):
|
|
return SMBool(True) # handled by canExitDraygonVanilla
|
|
|
|
@Cache.decorator
|
|
def canExitPreciousRoomRandomized(self):
|
|
sm = self.smbm
|
|
suitlessRoomExit = sm.canSpringBallJump()
|
|
if suitlessRoomExit.bool == False:
|
|
if self.getDraygonConnection() == 'KraidRoomIn':
|
|
suitlessRoomExit = sm.canShortCharge() # charge spark in kraid's room
|
|
elif self.getDraygonConnection() == 'RidleyRoomIn':
|
|
suitlessRoomExit = sm.wand(sm.haveItem('XRayScope'), # get doorstuck in compatible transition
|
|
sm.knowsPreciousRoomXRayExit())
|
|
return sm.wor(sm.wand(sm.haveItem('Gravity'),
|
|
sm.wor(sm.canFly(),
|
|
sm.knowsGravityJump(),
|
|
sm.haveItem('HiJump'))),
|
|
suitlessRoomExit)
|
|
|
|
@Cache.decorator
|
|
def canExitPreciousRoom(self):
|
|
if self.isVanillaDraygon():
|
|
return self.canExitPreciousRoomVanilla()
|
|
else:
|
|
return self.canExitPreciousRoomRandomized()
|
|
|
|
@Cache.decorator
|
|
def canPassDachoraRoom(self):
|
|
sm = self.smbm
|
|
return sm.wor(sm.haveItem('SpeedBooster'), sm.canDestroyBombWalls())
|
|
|
|
@Cache.decorator
|
|
def canTraverseCrabTunnelLeftToRight(self):
|
|
sm = self.smbm
|
|
return sm.wand(sm.traverse('MainStreetBottomRight'),
|
|
sm.wor(sm.haveItem('Super'),
|
|
RomPatches.has(sm.player, RomPatches.AreaRandoGatesOther)))
|