Archipelago/worlds/sm/variaRandomizer/graph/vanilla/graph_helpers.py

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