diff --git a/worlds/sm/__init__.py b/worlds/sm/__init__.py index 24d67603..3ce405ac 100644 --- a/worlds/sm/__init__.py +++ b/worlds/sm/__init__.py @@ -22,8 +22,9 @@ import Utils from .variaRandomizer.logic.smboolmanager import SMBoolManager from .variaRandomizer.graph.vanilla.graph_locations import locationsDict from .variaRandomizer.graph.graph_utils import getAccessPoint -from .variaRandomizer.rando.ItemLocContainer import ItemLocation +from .variaRandomizer.rando.ItemLocContainer import ItemLocation, ItemLocContainer from .variaRandomizer.rando.Items import ItemManager +from .variaRandomizer.rando.RandoServices import ComebackCheckType from .variaRandomizer.utils.parameters import * from .variaRandomizer.utils.utils import openFile from .variaRandomizer.logic.logic import Logic @@ -169,9 +170,13 @@ class SMWorld(World): elif item.Category == 'Nothing': isAdvancement = False + classification = ItemClassification.progression if isAdvancement else ItemClassification.filler itemClass = ItemManager.Items[item.Type].Class - smitem = SMItem(item.Name, ItemClassification.progression if isAdvancement else ItemClassification.filler, - item.Type, None if itemClass == 'Boss' else self.item_name_to_id[item.Name], player=self.player) + smitem = SMItem(item.Name, + classification, + item.Type, + None if itemClass == 'Boss' else self.item_name_to_id[item.Name], + player=self.player) if itemClass == 'Boss': self.locked_items[item.Name] = smitem elif item.Category == 'Nothing': @@ -645,10 +650,10 @@ class SMWorld(World): def collect(self, state: CollectionState, item: Item) -> bool: state.smbm[self.player].addItem(item.type) - if (item.location != None and item.location.player == self.player): - for entrance in self.multiworld.get_region(item.location.parent_region.name, self.player).entrances: + if item.location != None: + for entrance in self.multiworld.get_region(item.location.parent_region.name, item.location.player).entrances: if (entrance.parent_region.can_reach(state)): - state.smbm[self.player].lastAP = entrance.parent_region.name + state.smbm[item.location.player].lastAP = entrance.parent_region.name break return super(SMWorld, self).collect(state, item) @@ -797,29 +802,25 @@ class SMLocation(Location): def can_reach(self, state: CollectionState) -> bool: # self.access_rule computes faster on average, so placing it first for faster abort assert self.parent_region, "Can't reach location without region" - return self.access_rule(state) and self.parent_region.can_reach(state) and self.can_comeback(state) + return self.access_rule(state) and \ + self.parent_region.can_reach(state) and \ + self.can_comeback(state, self.item) - def can_comeback(self, state: CollectionState): - # some specific early/late game checks - if self.name == 'Bomb' or self.name == 'Mother Brain': - return True - + def can_comeback(self, state: CollectionState, item): randoExec = state.multiworld.worlds[self.player].variaRando.randoExec + randoService = randoExec.setup.services + + comebackCheck = ComebackCheckType.JustComeback n = 2 if GraphUtils.isStandardStart(randoExec.graphSettings.startAP) else 3 # is early game if (len([loc for loc in state.locations_checked if loc.player == self.player]) <= n): - return True - - for key in locationsDict[self.name].AccessFrom.keys(): - smbm = state.smbm[self.player] - if (randoExec.areaGraph.canAccess( smbm, - smbm.lastAP, - key, - smbm.maxDiff, - None)): - return True - return False - + comebackCheck = ComebackCheckType.NoCheck + container = ItemLocContainer(state.smbm[self.player], [], []) + return randoService.fullComebackCheck( container, + state.smbm[self.player].lastAP, + ItemManager.Items[item.type] if item is not None and item.player == self.player else None, + locationsDict[self.name], + comebackCheck) class SMItem(Item): game = "Super Metroid" diff --git a/worlds/sm/variaRandomizer/graph/graph.py b/worlds/sm/variaRandomizer/graph/graph.py index d6a3a09f..36a061a2 100644 --- a/worlds/sm/variaRandomizer/graph/graph.py +++ b/worlds/sm/variaRandomizer/graph/graph.py @@ -362,7 +362,8 @@ class AccessGraph(object): # test access from an access point to another, given an optional item def canAccess(self, smbm, srcAccessPointName, destAccessPointName, maxDiff, item=None): - if item is not None: + addAndRemoveItem = item is not None and (smbm.isCountItem(item) or not smbm.haveItem(item)) + if addAndRemoveItem: smbm.addItem(item) #print("canAccess: item: {}, src: {}, dest: {}".format(item, srcAccessPointName, destAccessPointName)) destAccessPoint = self.accessPoints[destAccessPointName] @@ -371,7 +372,7 @@ class AccessGraph(object): can = destAccessPoint in availAccessPoints # if not can: # self.log.debug("canAccess KO: avail = {}".format([ap.Name for ap in availAccessPoints.keys()])) - if item is not None: + if addAndRemoveItem: smbm.removeItem(item) #print("canAccess: {}".format(can)) return can diff --git a/worlds/sm/variaRandomizer/graph/location.py b/worlds/sm/variaRandomizer/graph/location.py index 5831a4cc..466a050b 100644 --- a/worlds/sm/variaRandomizer/graph/location.py +++ b/worlds/sm/variaRandomizer/graph/location.py @@ -59,9 +59,12 @@ class Location: def evalPostAvailable(self, smbm): if self.difficulty.bool == True and self.PostAvailable is not None: - smbm.addItem(self.itemName) + addAndRemoveItem = smbm.isCountItem(self.itemName) or not smbm.haveItem(self.itemName) + if addAndRemoveItem: + smbm.addItem(self.itemName) postAvailable = self.PostAvailable(smbm) - smbm.removeItem(self.itemName) + if addAndRemoveItem: + smbm.removeItem(self.itemName) self.difficulty = self.difficulty & postAvailable if self.locDifficulty is not None: diff --git a/worlds/sm/variaRandomizer/logic/smboolmanager.py b/worlds/sm/variaRandomizer/logic/smboolmanager.py index bb315847..351e2835 100644 --- a/worlds/sm/variaRandomizer/logic/smboolmanager.py +++ b/worlds/sm/variaRandomizer/logic/smboolmanager.py @@ -91,9 +91,12 @@ class SMBoolManager(object): return itemsDict def withItem(self, item, func): - self.addItem(item) + addAndRemoveItem = self.isCountItem(item) or not self.haveItem(item) + if addAndRemoveItem: + self.addItem(item) ret = func(self) - self.removeItem(item) + if addAndRemoveItem: + self.removeItem(item) return ret def resetItems(self): diff --git a/worlds/sm/variaRandomizer/rom/rompatcher.py b/worlds/sm/variaRandomizer/rom/rompatcher.py index fedb89e8..5e821da1 100644 --- a/worlds/sm/variaRandomizer/rom/rompatcher.py +++ b/worlds/sm/variaRandomizer/rom/rompatcher.py @@ -279,11 +279,12 @@ class RomPatcher: # apply area patches if self.settings["area"] == True: + areaPatches = list(RomPatcher.IPSPatches['Area']) if not self.settings["areaLayout"]: for p in ['area_rando_layout.ips', 'Sponge_Bath_Blinking_Door', 'east_ocean.ips', 'aqueduct_bomb_blocks.ips']: - RomPatcher.IPSPatches['Area'].remove(p) - RomPatcher.IPSPatches['Area'].append('area_rando_layout_base.ips') - for patchName in RomPatcher.IPSPatches['Area']: + areaPatches.remove(p) + areaPatches.append('area_rando_layout_base.ips') + for patchName in areaPatches: self.applyIPSPatch(patchName) else: self.applyIPSPatch('area_ids_alt.ips') diff --git a/worlds/sm/variaRandomizer/utils/objectives.py b/worlds/sm/variaRandomizer/utils/objectives.py index 5f585b28..8c886674 100644 --- a/worlds/sm/variaRandomizer/utils/objectives.py +++ b/worlds/sm/variaRandomizer/utils/objectives.py @@ -740,7 +740,7 @@ class Objectives(object): if c not in char2tile: continue romFile.writeWord(0x3800 + char2tile[c]) - + Synonyms.alreadyUsed = [] # write goal completed positions y in sprites OAM baseY = 0x40 addr = Addresses.getOne('objectivesSpritesOAM')