mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
Added Super Metroid support (#46)
Varia Randomizer based implementation LttPClient -> SNIClient
This commit is contained in:
134
worlds/sm/variaRandomizer/rando/GraphBuilder.py
Normal file
134
worlds/sm/variaRandomizer/rando/GraphBuilder.py
Normal file
@@ -0,0 +1,134 @@
|
||||
|
||||
import utils.log, random, copy
|
||||
|
||||
from graph.graph_utils import GraphUtils, vanillaTransitions, vanillaBossesTransitions, escapeSource, escapeTargets
|
||||
from logic.logic import Logic
|
||||
from graph.graph import AccessGraphRando as AccessGraph
|
||||
|
||||
# creates graph and handles randomized escape
|
||||
class GraphBuilder(object):
|
||||
def __init__(self, graphSettings):
|
||||
self.graphSettings = graphSettings
|
||||
self.areaRando = graphSettings.areaRando
|
||||
self.bossRando = graphSettings.bossRando
|
||||
self.escapeRando = graphSettings.escapeRando
|
||||
self.minimizerN = graphSettings.minimizerN
|
||||
self.log = utils.log.get('GraphBuilder')
|
||||
|
||||
# builds everything but escape transitions
|
||||
def createGraph(self):
|
||||
transitions = self.graphSettings.plandoRandoTransitions
|
||||
if transitions is None:
|
||||
transitions = []
|
||||
if self.minimizerN is not None:
|
||||
transitions = GraphUtils.createMinimizerTransitions(self.graphSettings.startAP, self.minimizerN)
|
||||
else:
|
||||
if not self.bossRando:
|
||||
transitions += vanillaBossesTransitions
|
||||
else:
|
||||
transitions += GraphUtils.createBossesTransitions()
|
||||
if not self.areaRando:
|
||||
transitions += vanillaTransitions
|
||||
else:
|
||||
transitions += GraphUtils.createAreaTransitions(self.graphSettings.lightAreaRando)
|
||||
return AccessGraph(Logic.accessPoints, transitions, self.graphSettings.dotFile)
|
||||
|
||||
# fills in escape transitions if escape rando is enabled
|
||||
# scavEscape = None or (itemLocs, scavItemLocs) couple from filler
|
||||
def escapeGraph(self, container, graph, maxDiff, scavEscape):
|
||||
if not self.escapeRando:
|
||||
return True
|
||||
emptyContainer = copy.copy(container)
|
||||
emptyContainer.resetCollected(reassignItemLocs=True)
|
||||
dst = None
|
||||
if scavEscape is None:
|
||||
possibleTargets, dst, path = self.getPossibleEscapeTargets(emptyContainer, graph, maxDiff)
|
||||
# update graph with escape transition
|
||||
graph.addTransition(escapeSource, dst)
|
||||
else:
|
||||
possibleTargets, path = self.getScavengerEscape(emptyContainer, graph, maxDiff, scavEscape)
|
||||
if path is None:
|
||||
return False
|
||||
# get timer value
|
||||
self.escapeTimer(graph, path, self.areaRando or scavEscape is not None)
|
||||
self.log.debug("escapeGraph: ({}, {}) timer: {}".format(escapeSource, dst, graph.EscapeAttributes['Timer']))
|
||||
# animals
|
||||
GraphUtils.escapeAnimalsTransitions(graph, possibleTargets, dst)
|
||||
return True
|
||||
|
||||
def _getTargets(self, sm, graph, maxDiff):
|
||||
possibleTargets = [target for target in escapeTargets if graph.accessPath(sm, target, 'Landing Site', maxDiff) is not None]
|
||||
self.log.debug('_getTargets. targets='+str(possibleTargets))
|
||||
# failsafe
|
||||
if len(possibleTargets) == 0:
|
||||
self.log.debug("Can't randomize escape, fallback to vanilla")
|
||||
possibleTargets.append('Climb Bottom Left')
|
||||
random.shuffle(possibleTargets)
|
||||
return possibleTargets
|
||||
|
||||
def getPossibleEscapeTargets(self, emptyContainer, graph, maxDiff):
|
||||
sm = emptyContainer.sm
|
||||
# setup smbm with item pool
|
||||
# Ice not usable because of hyper beam
|
||||
# remove energy to avoid hell runs
|
||||
# (will add bosses as well)
|
||||
sm.addItems([item.Type for item in emptyContainer.itemPool if item.Type != 'Ice' and item.Category != 'Energy'])
|
||||
sm.addItem('Hyper')
|
||||
possibleTargets = self._getTargets(sm, graph, maxDiff)
|
||||
# pick one
|
||||
dst = possibleTargets.pop()
|
||||
path = graph.accessPath(sm, dst, 'Landing Site', maxDiff)
|
||||
return (possibleTargets, dst, path)
|
||||
|
||||
def getScavengerEscape(self, emptyContainer, graph, maxDiff, scavEscape):
|
||||
sm = emptyContainer.sm
|
||||
itemLocs, lastScavItemLoc = scavEscape[0], scavEscape[1][-1]
|
||||
# collect all item/locations up until last scav
|
||||
for il in itemLocs:
|
||||
emptyContainer.collect(il)
|
||||
if il == lastScavItemLoc:
|
||||
break
|
||||
possibleTargets = self._getTargets(sm, graph, maxDiff)
|
||||
path = graph.accessPath(sm, lastScavItemLoc.Location.accessPoint, 'Landing Site', maxDiff)
|
||||
return (possibleTargets, path)
|
||||
|
||||
# path: as returned by AccessGraph.accessPath
|
||||
def escapeTimer(self, graph, path, compute):
|
||||
if compute == True:
|
||||
if path[0].Name == 'Climb Bottom Left':
|
||||
graph.EscapeAttributes['Timer'] = None
|
||||
return
|
||||
traversedAreas = list(set([ap.GraphArea for ap in path]))
|
||||
self.log.debug("escapeTimer path: " + str([ap.Name for ap in path]))
|
||||
self.log.debug("escapeTimer traversedAreas: " + str(traversedAreas))
|
||||
# rough estimates of navigation within areas to reach "borders"
|
||||
# (can obviously be completely off wrt to actual path, but on the generous side)
|
||||
traversals = {
|
||||
'Crateria':90,
|
||||
'GreenPinkBrinstar':90,
|
||||
'WreckedShip':120,
|
||||
'LowerNorfair':135,
|
||||
'WestMaridia':75,
|
||||
'EastMaridia':100,
|
||||
'RedBrinstar':75,
|
||||
'Norfair': 120,
|
||||
'Kraid': 40,
|
||||
'Crocomire': 40,
|
||||
# can't be on the path
|
||||
'Tourian': 0,
|
||||
}
|
||||
t = 90 if self.areaRando else 0
|
||||
for area in traversedAreas:
|
||||
t += traversals[area]
|
||||
t = max(t, 180)
|
||||
else:
|
||||
escapeTargetsTimer = {
|
||||
'Climb Bottom Left': None, # vanilla
|
||||
'Green Brinstar Main Shaft Top Left': 210, # brinstar
|
||||
'Basement Left': 210, # wrecked ship
|
||||
'Business Center Mid Left': 270, # norfair
|
||||
'Crab Hole Bottom Right': 270 # maridia
|
||||
}
|
||||
t = escapeTargetsTimer[path[0].Name]
|
||||
self.log.debug("escapeTimer. t="+str(t))
|
||||
graph.EscapeAttributes['Timer'] = t
|
Reference in New Issue
Block a user