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)))