SM: failing generation fixes (#1726)

- fixed wrong condition in Collect to assign lastAP
- fixed possible infinite loop in generating output when many SM worlds are present
- fixed new VARIA code that changed a list used for every SM worlds and would throw if many SM worlds uses Aea rando and not AreaLayout
This commit is contained in:
lordlou 2023-04-16 23:46:19 -04:00 committed by GitHub
parent f6758524d5
commit cb634fa8d4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 43 additions and 34 deletions

View File

@ -22,8 +22,9 @@ import Utils
from .variaRandomizer.logic.smboolmanager import SMBoolManager from .variaRandomizer.logic.smboolmanager import SMBoolManager
from .variaRandomizer.graph.vanilla.graph_locations import locationsDict from .variaRandomizer.graph.vanilla.graph_locations import locationsDict
from .variaRandomizer.graph.graph_utils import getAccessPoint 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.Items import ItemManager
from .variaRandomizer.rando.RandoServices import ComebackCheckType
from .variaRandomizer.utils.parameters import * from .variaRandomizer.utils.parameters import *
from .variaRandomizer.utils.utils import openFile from .variaRandomizer.utils.utils import openFile
from .variaRandomizer.logic.logic import Logic from .variaRandomizer.logic.logic import Logic
@ -169,9 +170,13 @@ class SMWorld(World):
elif item.Category == 'Nothing': elif item.Category == 'Nothing':
isAdvancement = False isAdvancement = False
classification = ItemClassification.progression if isAdvancement else ItemClassification.filler
itemClass = ItemManager.Items[item.Type].Class itemClass = ItemManager.Items[item.Type].Class
smitem = SMItem(item.Name, ItemClassification.progression if isAdvancement else ItemClassification.filler, smitem = SMItem(item.Name,
item.Type, None if itemClass == 'Boss' else self.item_name_to_id[item.Name], player=self.player) classification,
item.Type,
None if itemClass == 'Boss' else self.item_name_to_id[item.Name],
player=self.player)
if itemClass == 'Boss': if itemClass == 'Boss':
self.locked_items[item.Name] = smitem self.locked_items[item.Name] = smitem
elif item.Category == 'Nothing': elif item.Category == 'Nothing':
@ -645,10 +650,10 @@ class SMWorld(World):
def collect(self, state: CollectionState, item: Item) -> bool: def collect(self, state: CollectionState, item: Item) -> bool:
state.smbm[self.player].addItem(item.type) state.smbm[self.player].addItem(item.type)
if (item.location != None and item.location.player == self.player): if item.location != None:
for entrance in self.multiworld.get_region(item.location.parent_region.name, self.player).entrances: for entrance in self.multiworld.get_region(item.location.parent_region.name, item.location.player).entrances:
if (entrance.parent_region.can_reach(state)): 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 break
return super(SMWorld, self).collect(state, item) return super(SMWorld, self).collect(state, item)
@ -797,29 +802,25 @@ class SMLocation(Location):
def can_reach(self, state: CollectionState) -> bool: def can_reach(self, state: CollectionState) -> bool:
# self.access_rule computes faster on average, so placing it first for faster abort # self.access_rule computes faster on average, so placing it first for faster abort
assert self.parent_region, "Can't reach location without region" 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): def can_comeback(self, state: CollectionState, item):
# some specific early/late game checks
if self.name == 'Bomb' or self.name == 'Mother Brain':
return True
randoExec = state.multiworld.worlds[self.player].variaRando.randoExec 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 n = 2 if GraphUtils.isStandardStart(randoExec.graphSettings.startAP) else 3
# is early game # is early game
if (len([loc for loc in state.locations_checked if loc.player == self.player]) <= n): if (len([loc for loc in state.locations_checked if loc.player == self.player]) <= n):
return True comebackCheck = ComebackCheckType.NoCheck
container = ItemLocContainer(state.smbm[self.player], [], [])
for key in locationsDict[self.name].AccessFrom.keys(): return randoService.fullComebackCheck( container,
smbm = state.smbm[self.player] state.smbm[self.player].lastAP,
if (randoExec.areaGraph.canAccess( smbm, ItemManager.Items[item.type] if item is not None and item.player == self.player else None,
smbm.lastAP, locationsDict[self.name],
key, comebackCheck)
smbm.maxDiff,
None)):
return True
return False
class SMItem(Item): class SMItem(Item):
game = "Super Metroid" game = "Super Metroid"

View File

@ -362,7 +362,8 @@ class AccessGraph(object):
# test access from an access point to another, given an optional item # test access from an access point to another, given an optional item
def canAccess(self, smbm, srcAccessPointName, destAccessPointName, maxDiff, item=None): 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) smbm.addItem(item)
#print("canAccess: item: {}, src: {}, dest: {}".format(item, srcAccessPointName, destAccessPointName)) #print("canAccess: item: {}, src: {}, dest: {}".format(item, srcAccessPointName, destAccessPointName))
destAccessPoint = self.accessPoints[destAccessPointName] destAccessPoint = self.accessPoints[destAccessPointName]
@ -371,7 +372,7 @@ class AccessGraph(object):
can = destAccessPoint in availAccessPoints can = destAccessPoint in availAccessPoints
# if not can: # if not can:
# self.log.debug("canAccess KO: avail = {}".format([ap.Name for ap in availAccessPoints.keys()])) # self.log.debug("canAccess KO: avail = {}".format([ap.Name for ap in availAccessPoints.keys()]))
if item is not None: if addAndRemoveItem:
smbm.removeItem(item) smbm.removeItem(item)
#print("canAccess: {}".format(can)) #print("canAccess: {}".format(can))
return can return can

View File

@ -59,9 +59,12 @@ class Location:
def evalPostAvailable(self, smbm): def evalPostAvailable(self, smbm):
if self.difficulty.bool == True and self.PostAvailable is not None: 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) postAvailable = self.PostAvailable(smbm)
smbm.removeItem(self.itemName) if addAndRemoveItem:
smbm.removeItem(self.itemName)
self.difficulty = self.difficulty & postAvailable self.difficulty = self.difficulty & postAvailable
if self.locDifficulty is not None: if self.locDifficulty is not None:

View File

@ -91,9 +91,12 @@ class SMBoolManager(object):
return itemsDict return itemsDict
def withItem(self, item, func): 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) ret = func(self)
self.removeItem(item) if addAndRemoveItem:
self.removeItem(item)
return ret return ret
def resetItems(self): def resetItems(self):

View File

@ -279,11 +279,12 @@ class RomPatcher:
# apply area patches # apply area patches
if self.settings["area"] == True: if self.settings["area"] == True:
areaPatches = list(RomPatcher.IPSPatches['Area'])
if not self.settings["areaLayout"]: if not self.settings["areaLayout"]:
for p in ['area_rando_layout.ips', 'Sponge_Bath_Blinking_Door', 'east_ocean.ips', 'aqueduct_bomb_blocks.ips']: for p in ['area_rando_layout.ips', 'Sponge_Bath_Blinking_Door', 'east_ocean.ips', 'aqueduct_bomb_blocks.ips']:
RomPatcher.IPSPatches['Area'].remove(p) areaPatches.remove(p)
RomPatcher.IPSPatches['Area'].append('area_rando_layout_base.ips') areaPatches.append('area_rando_layout_base.ips')
for patchName in RomPatcher.IPSPatches['Area']: for patchName in areaPatches:
self.applyIPSPatch(patchName) self.applyIPSPatch(patchName)
else: else:
self.applyIPSPatch('area_ids_alt.ips') self.applyIPSPatch('area_ids_alt.ips')

View File

@ -740,7 +740,7 @@ class Objectives(object):
if c not in char2tile: if c not in char2tile:
continue continue
romFile.writeWord(0x3800 + char2tile[c]) romFile.writeWord(0x3800 + char2tile[c])
Synonyms.alreadyUsed = []
# write goal completed positions y in sprites OAM # write goal completed positions y in sprites OAM
baseY = 0x40 baseY = 0x40
addr = Addresses.getOne('objectivesSpritesOAM') addr = Addresses.getOne('objectivesSpritesOAM')