2023-03-25 18:30:38 +00:00
|
|
|
import copy, random
|
2021-11-12 13:00:11 +00:00
|
|
|
|
2023-04-08 20:52:34 +00:00
|
|
|
from ..utils import log
|
|
|
|
from ..utils.utils import randGaussBounds
|
|
|
|
from ..logic.smbool import SMBool, smboolFalse
|
|
|
|
from ..logic.smboolmanager import SMBoolManager
|
|
|
|
from ..logic.helpers import Bosses
|
|
|
|
from ..graph.graph_utils import getAccessPoint, GraphUtils
|
|
|
|
from ..rando.Filler import FrontFiller
|
|
|
|
from ..rando.ItemLocContainer import ItemLocContainer, getLocListStr, ItemLocation, getItemListStr
|
|
|
|
from ..rando.Restrictions import Restrictions
|
|
|
|
from ..utils.parameters import infinity
|
2021-11-12 13:00:11 +00:00
|
|
|
|
|
|
|
# checks init conditions for the randomizer: processes super fun settings, graph, start location, special restrictions
|
|
|
|
# the entry point is createItemLocContainer
|
|
|
|
class RandoSetup(object):
|
|
|
|
def __init__(self, graphSettings, locations, services, player):
|
|
|
|
self.sm = SMBoolManager(player, services.settings.maxDiff)
|
|
|
|
self.settings = services.settings
|
|
|
|
self.graphSettings = graphSettings
|
|
|
|
self.startAP = graphSettings.startAP
|
|
|
|
self.superFun = self.settings.getSuperFun()
|
|
|
|
self.container = None
|
|
|
|
self.services = services
|
|
|
|
self.restrictions = services.restrictions
|
|
|
|
self.areaGraph = services.areaGraph
|
|
|
|
self.allLocations = locations
|
|
|
|
self.locations = self.areaGraph.getAccessibleLocations(locations, self.startAP)
|
|
|
|
# print("nLocs Setup: "+str(len(self.locations)))
|
|
|
|
self.itemManager = self.settings.getItemManager(self.sm, len(self.locations))
|
|
|
|
self.forbiddenItems = []
|
|
|
|
self.restrictedLocs = []
|
|
|
|
self.lastRestricted = []
|
|
|
|
self.bossesLocs = sorted(['Draygon', 'Kraid', 'Ridley', 'Phantoon', 'Mother Brain'])
|
|
|
|
self.suits = ['Varia', 'Gravity']
|
|
|
|
# organized by priority
|
|
|
|
self.movementItems = ['SpaceJump', 'HiJump', 'SpeedBooster', 'Bomb', 'Grapple', 'SpringBall']
|
|
|
|
# organized by priority
|
|
|
|
self.combatItems = ['ScrewAttack', 'Plasma', 'Wave', 'Spazer']
|
|
|
|
# OMG
|
|
|
|
self.bossChecks = {
|
|
|
|
'Kraid' : self.sm.enoughStuffsKraid,
|
|
|
|
'Phantoon' : self.sm.enoughStuffsPhantoon,
|
|
|
|
'Draygon' : self.sm.enoughStuffsDraygon,
|
|
|
|
'Ridley' : self.sm.enoughStuffsRidley,
|
|
|
|
'Mother Brain': self.sm.enoughStuffsMotherbrain
|
|
|
|
}
|
|
|
|
self.okay = lambda: SMBool(True, 0)
|
|
|
|
exclude = self.settings.getExcludeItems(self.locations)
|
|
|
|
# we have to use item manager only once, otherwise pool will change
|
|
|
|
self.itemManager.createItemPool(exclude)
|
|
|
|
self.basePool = self.itemManager.getItemPool()[:]
|
2023-03-25 18:30:38 +00:00
|
|
|
self.log = log.get('RandoSetup')
|
2021-11-12 13:00:11 +00:00
|
|
|
if len(locations) != len(self.locations):
|
|
|
|
self.log.debug("inaccessible locations :"+getLocListStr([loc for loc in locations if loc not in self.locations]))
|
|
|
|
|
|
|
|
# processes everything and returns an ItemLocContainer, or None if failed (invalid init conditions/settings)
|
|
|
|
def createItemLocContainer(self, endDate, vcr=None):
|
|
|
|
self.getForbidden()
|
|
|
|
self.log.debug("LAST CHECKPOOL")
|
|
|
|
if not self.checkPool():
|
|
|
|
self.log.debug("createItemLocContainer: last checkPool fail")
|
|
|
|
return None
|
|
|
|
# reset restricted in locs from previous attempt
|
|
|
|
for loc in self.locations:
|
|
|
|
loc.restricted = False
|
|
|
|
for loc in self.restrictedLocs:
|
|
|
|
self.log.debug("createItemLocContainer: loc is restricted: {}".format(loc.Name))
|
|
|
|
loc.restricted = True
|
|
|
|
self.checkDoorBeams()
|
|
|
|
self.container = ItemLocContainer(self.sm, self.getItemPool(), self.locations)
|
|
|
|
if self.restrictions.isLateMorph():
|
|
|
|
self.restrictions.lateMorphInit(self.startAP, self.container, self.services)
|
|
|
|
isStdStart = GraphUtils.isStandardStart(self.startAP)
|
|
|
|
# ensure we have an area layout that can put morph outside start area
|
|
|
|
# TODO::allow for custom start which doesn't require morph early
|
|
|
|
if self.graphSettings.areaRando and isStdStart and not self.restrictions.suitsRestrictions and self.restrictions.lateMorphForbiddenArea is None:
|
|
|
|
self.container = None
|
|
|
|
self.log.debug("createItemLocContainer: checkLateMorph fail")
|
|
|
|
return None
|
|
|
|
# checkStart needs the container
|
|
|
|
if not self.checkStart():
|
|
|
|
self.container = None
|
|
|
|
self.log.debug("createItemLocContainer: checkStart fail")
|
|
|
|
return None
|
|
|
|
self.settings.updateSuperFun(self.superFun)
|
|
|
|
return self.container
|
|
|
|
|
|
|
|
def getRestrictionsDict(self):
|
|
|
|
itemTypes = {item.Type for item in self.container.itemPool if item.Category not in Restrictions.NoCheckCat}
|
|
|
|
allAreas = {loc.GraphArea for loc in self.locations}
|
|
|
|
items = [self.container.getNextItemInPool(itemType) for itemType in itemTypes]
|
|
|
|
restrictionDict = {}
|
|
|
|
for area in allAreas:
|
|
|
|
restrictionDict[area] = {}
|
|
|
|
for itemType in itemTypes:
|
|
|
|
restrictionDict[area][itemType] = set()
|
|
|
|
for item in items:
|
|
|
|
itemType = item.Type
|
|
|
|
poss = self.services.possibleLocations(item, self.startAP, self.container)
|
|
|
|
for loc in poss:
|
|
|
|
restrictionDict[loc.GraphArea][itemType].add(loc.Name)
|
|
|
|
if self.restrictions.isEarlyMorph() and GraphUtils.isStandardStart(self.startAP):
|
|
|
|
morphLocs = ['Morphing Ball']
|
|
|
|
if self.restrictions.split in ['Full', 'Major']:
|
|
|
|
dboost = self.sm.knowsCeilingDBoost()
|
|
|
|
if dboost.bool == True and dboost.difficulty <= self.settings.maxDiff:
|
|
|
|
morphLocs.append('Energy Tank, Brinstar Ceiling')
|
|
|
|
for area, locDict in restrictionDict.items():
|
|
|
|
if area == 'Crateria':
|
|
|
|
locDict['Morph'] = set(morphLocs)
|
|
|
|
else:
|
|
|
|
locDict['Morph'] = set()
|
|
|
|
return restrictionDict
|
|
|
|
|
|
|
|
# fill up unreachable locations with "junk" to maximize the chance of the ROM
|
|
|
|
# to be finishable
|
|
|
|
def fillRestrictedLocations(self):
|
|
|
|
def getPred(itemType, loc):
|
|
|
|
return lambda item: (itemType is None or item.Type == itemType) and self.restrictions.canPlaceAtLocation(item, loc, self.container)
|
|
|
|
locs = self.restrictedLocs
|
|
|
|
self.log.debug("fillRestrictedLocations. locs="+getLocListStr(locs))
|
|
|
|
for loc in locs:
|
|
|
|
itemLocation = ItemLocation(None, loc)
|
|
|
|
if self.container.hasItemInPool(getPred('Nothing', loc)):
|
|
|
|
itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('Nothing', loc))
|
|
|
|
elif self.container.hasItemInPool(getPred('NoEnergy', loc)):
|
|
|
|
itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('NoEnergy', loc))
|
|
|
|
elif self.container.countItems(getPred('Missile', loc)) > 3:
|
|
|
|
itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('Missile', loc))
|
|
|
|
elif self.container.countItems(getPred('Super', loc)) > 2:
|
|
|
|
itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('Super', loc))
|
|
|
|
elif self.container.countItems(getPred('PowerBomb', loc)) > 1:
|
|
|
|
itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('PowerBomb', loc))
|
|
|
|
elif self.container.countItems(getPred('Reserve', loc)) > 1:
|
|
|
|
itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('Reserve', loc))
|
|
|
|
elif self.container.countItems(getPred('ETank', loc)) > 3:
|
|
|
|
itemLocation.Item = self.container.getNextItemInPoolMatching(getPred('ETank', loc))
|
|
|
|
else:
|
|
|
|
raise RuntimeError("Cannot fill restricted locations")
|
|
|
|
self.log.debug("Fill: {}/{} at {}".format(itemLocation.Item.Type, itemLocation.Item.Class, itemLocation.Location.Name))
|
|
|
|
self.container.collect(itemLocation, False)
|
|
|
|
|
|
|
|
def getItemPool(self, forbidden=[]):
|
|
|
|
self.itemManager.setItemPool(self.basePool[:]) # reuse base pool to have constant base item set
|
|
|
|
return self.itemManager.removeForbiddenItems(self.forbiddenItems + forbidden)
|
|
|
|
|
|
|
|
# if needed, do a simplified "pre-randomization" of a few items to check start AP/area layout validity
|
|
|
|
def checkStart(self):
|
|
|
|
ap = getAccessPoint(self.startAP)
|
|
|
|
if not self.graphSettings.areaRando or ap.Start is None or \
|
|
|
|
(('needsPreRando' not in ap.Start or not ap.Start['needsPreRando']) and\
|
|
|
|
('areaMode' not in ap.Start or not ap.Start['areaMode'])):
|
|
|
|
return True
|
|
|
|
self.log.debug("********* PRE RANDO START")
|
|
|
|
container = copy.copy(self.container)
|
|
|
|
filler = FrontFiller(self.startAP, self.areaGraph, self.restrictions, container)
|
|
|
|
condition = filler.createStepCountCondition(4)
|
|
|
|
(isStuck, itemLocations, progItems) = filler.generateItems(condition)
|
|
|
|
self.log.debug("********* PRE RANDO END")
|
|
|
|
return not isStuck and len(self.services.currentLocations(filler.ap, filler.container)) > 0
|
|
|
|
|
|
|
|
# in door color rando, determine mandatory beams
|
|
|
|
def checkDoorBeams(self):
|
|
|
|
if self.restrictions.isLateDoors():
|
|
|
|
doorBeams = ['Wave','Ice','Spazer','Plasma']
|
|
|
|
self.restrictions.mandatoryBeams = [beam for beam in doorBeams if not self.checkPool(forbidden=[beam])]
|
|
|
|
self.log.debug("checkDoorBeams. mandatoryBeams="+str(self.restrictions.mandatoryBeams))
|
|
|
|
|
|
|
|
def checkPool(self, forbidden=None):
|
|
|
|
self.log.debug("checkPool. forbidden=" + str(forbidden) + ", self.forbiddenItems=" + str(self.forbiddenItems))
|
|
|
|
if not self.graphSettings.isMinimizer() and not self.settings.isPlandoRando() and len(self.allLocations) > len(self.locations):
|
|
|
|
# invalid graph with looped areas
|
|
|
|
self.log.debug("checkPool: not all areas are connected, but minimizer param is off / not a plando rando")
|
|
|
|
return False
|
|
|
|
ret = True
|
|
|
|
if forbidden is not None:
|
|
|
|
pool = self.getItemPool(forbidden)
|
|
|
|
else:
|
|
|
|
pool = self.getItemPool()
|
|
|
|
# get restricted locs
|
|
|
|
totalAvailLocs = []
|
|
|
|
comeBack = {}
|
|
|
|
try:
|
|
|
|
container = ItemLocContainer(self.sm, pool, self.locations)
|
|
|
|
except AssertionError as e:
|
|
|
|
# invalid graph altogether
|
|
|
|
self.log.debug("checkPool: AssertionError when creating ItemLocContainer: {}".format(e))
|
|
|
|
return False
|
|
|
|
# restrict item pool in chozo: game should be finishable with chozo items only
|
|
|
|
contPool = []
|
|
|
|
contPool += [item for item in pool if item in container.itemPool]
|
|
|
|
# give us everything and beat every boss to see what we can access
|
|
|
|
self.disableBossChecks()
|
|
|
|
self.sm.resetItems()
|
|
|
|
self.sm.addItems([item.Type for item in contPool]) # will add bosses as well
|
|
|
|
self.log.debug('pool={}'.format(getItemListStr(container.itemPool)))
|
|
|
|
locs = self.services.currentLocations(self.startAP, container, post=True)
|
|
|
|
self.areaGraph.useCache(True)
|
|
|
|
for loc in locs:
|
|
|
|
ap = loc.accessPoint
|
|
|
|
if ap not in comeBack:
|
|
|
|
# we chose Golden Four because it is always there.
|
|
|
|
# Start APs might not have comeback transitions
|
|
|
|
# possible start AP issues are handled in checkStart
|
|
|
|
comeBack[ap] = self.areaGraph.canAccess(self.sm, ap, 'Golden Four', self.settings.maxDiff)
|
|
|
|
if comeBack[ap]:
|
|
|
|
totalAvailLocs.append(loc)
|
|
|
|
self.areaGraph.useCache(False)
|
|
|
|
self.lastRestricted = [loc for loc in self.locations if loc not in totalAvailLocs]
|
|
|
|
self.log.debug("restricted=" + str([loc.Name for loc in self.lastRestricted]))
|
|
|
|
|
|
|
|
# check if all inter-area APs can reach each other
|
|
|
|
interAPs = [ap for ap in self.areaGraph.getAccessibleAccessPoints(self.startAP) if not ap.isInternal() and not ap.isLoop()]
|
|
|
|
for startAp in interAPs:
|
|
|
|
availAccessPoints = self.areaGraph.getAvailableAccessPoints(startAp, self.sm, self.settings.maxDiff)
|
|
|
|
for ap in interAPs:
|
|
|
|
if not ap in availAccessPoints:
|
|
|
|
self.log.debug("checkPool: ap {} non accessible from {}".format(ap.Name, startAp.Name))
|
|
|
|
ret = False
|
|
|
|
if not ret:
|
|
|
|
self.log.debug("checkPool. inter-area APs check failed")
|
|
|
|
# cleanup
|
|
|
|
self.sm.resetItems()
|
|
|
|
self.restoreBossChecks()
|
|
|
|
# check if we can reach/beat all bosses
|
|
|
|
if ret:
|
|
|
|
for loc in self.lastRestricted:
|
|
|
|
if loc.Name in self.bossesLocs:
|
|
|
|
ret = False
|
|
|
|
self.log.debug("unavail Boss: " + loc.Name)
|
|
|
|
if ret:
|
|
|
|
# revive bosses
|
|
|
|
self.sm.addItems([item.Type for item in contPool if item.Category != 'Boss'])
|
|
|
|
maxDiff = self.settings.maxDiff
|
|
|
|
# see if phantoon doesn't block himself, and if we can reach draygon if she's alive
|
|
|
|
ret = self.areaGraph.canAccess(self.sm, self.startAP, 'PhantoonRoomIn', maxDiff)\
|
|
|
|
and self.areaGraph.canAccess(self.sm, self.startAP, 'DraygonRoomIn', maxDiff)
|
|
|
|
if ret:
|
|
|
|
# see if we can beat bosses with this equipment (infinity as max diff for a "onlyBossesLeft" type check
|
|
|
|
beatableBosses = sorted([loc.Name for loc in self.services.currentLocations(self.startAP, container, diff=infinity) if loc.isBoss()])
|
|
|
|
self.log.debug("checkPool. beatableBosses="+str(beatableBosses))
|
|
|
|
ret = beatableBosses == Bosses.Golden4()
|
|
|
|
if ret:
|
|
|
|
# check that we can then kill mother brain
|
|
|
|
self.sm.addItems(Bosses.Golden4())
|
|
|
|
beatableMotherBrain = [loc.Name for loc in self.services.currentLocations(self.startAP, container, diff=infinity) if loc.Name == 'Mother Brain']
|
|
|
|
ret = len(beatableMotherBrain) > 0
|
|
|
|
self.log.debug("checkPool. beatable Mother Brain={}".format(ret))
|
|
|
|
else:
|
|
|
|
self.log.debug('checkPool. locked by Phantoon or Draygon')
|
|
|
|
self.log.debug('checkPool. boss access sanity check: '+str(ret))
|
|
|
|
|
|
|
|
if self.restrictions.isChozo() or self.restrictions.isScavenger():
|
|
|
|
# in chozo or scavenger, we cannot put other items than NoEnergy in the restricted locations,
|
|
|
|
# we would be forced to put majors in there, which can make seed generation fail:
|
|
|
|
# don't put more restricted major locations than removed major items
|
|
|
|
# FIXME something to do there for chozo/ultra sparse, it gives us up to 3 more spots for nothing items
|
|
|
|
restrictedLocs = self.restrictedLocs + [loc for loc in self.lastRestricted if loc not in self.restrictedLocs]
|
|
|
|
nRestrictedMajor = sum(1 for loc in restrictedLocs if self.restrictions.isLocMajor(loc))
|
|
|
|
nNothingMajor = sum(1 for item in pool if self.restrictions.isItemMajor(item) and item.Category == 'Nothing')
|
|
|
|
ret &= nRestrictedMajor <= nNothingMajor
|
|
|
|
self.log.debug('checkPool. nRestrictedMajor='+str(nRestrictedMajor)+', nNothingMajor='+str(nNothingMajor))
|
|
|
|
self.log.debug('checkPool. result: '+str(ret))
|
|
|
|
return ret
|
|
|
|
|
|
|
|
def disableBossChecks(self):
|
|
|
|
self.sm.enoughStuffsKraid = self.okay
|
|
|
|
self.sm.enoughStuffsPhantoon = self.okay
|
|
|
|
self.sm.enoughStuffsDraygon = self.okay
|
|
|
|
self.sm.enoughStuffsRidley = self.okay
|
|
|
|
def mbCheck():
|
|
|
|
(possible, energyDiff) = self.sm.mbEtankCheck()
|
|
|
|
if possible == True:
|
|
|
|
return self.okay()
|
|
|
|
return smboolFalse
|
|
|
|
self.sm.enoughStuffsMotherbrain = mbCheck
|
|
|
|
|
|
|
|
def restoreBossChecks(self):
|
|
|
|
self.sm.enoughStuffsKraid = self.bossChecks['Kraid']
|
|
|
|
self.sm.enoughStuffsPhantoon = self.bossChecks['Phantoon']
|
|
|
|
self.sm.enoughStuffsDraygon = self.bossChecks['Draygon']
|
|
|
|
self.sm.enoughStuffsRidley = self.bossChecks['Ridley']
|
|
|
|
self.sm.enoughStuffsMotherbrain = self.bossChecks['Mother Brain']
|
|
|
|
|
|
|
|
def addRestricted(self):
|
|
|
|
self.checkPool()
|
|
|
|
for r in self.lastRestricted:
|
|
|
|
if r not in self.restrictedLocs:
|
|
|
|
self.restrictedLocs.append(r)
|
|
|
|
|
|
|
|
def getForbiddenItemsFromList(self, itemList):
|
|
|
|
self.log.debug('getForbiddenItemsFromList: ' + str(itemList))
|
|
|
|
remove = []
|
|
|
|
n = randGaussBounds(len(itemList))
|
|
|
|
for i in range(n):
|
|
|
|
idx = random.randint(0, len(itemList) - 1)
|
|
|
|
item = itemList.pop(idx)
|
|
|
|
if item is not None:
|
|
|
|
remove.append(item)
|
|
|
|
return remove
|
|
|
|
|
|
|
|
def addForbidden(self, removable):
|
|
|
|
forb = None
|
|
|
|
# it can take several tries if some item combination removal
|
|
|
|
# forbids access to more stuff than each individually
|
|
|
|
tries = 0
|
|
|
|
while forb is None and tries < 100:
|
|
|
|
forb = self.getForbiddenItemsFromList(removable[:])
|
|
|
|
self.log.debug("addForbidden. forb="+str(forb))
|
|
|
|
if self.checkPool(forb) == False:
|
|
|
|
forb = None
|
|
|
|
tries += 1
|
|
|
|
if forb is None:
|
|
|
|
# we couldn't find a combination, just pick an item
|
|
|
|
firstItem = next((itemType for itemType in removable if itemType is not None), None)
|
|
|
|
if firstItem is not None:
|
|
|
|
forb = [firstItem]
|
|
|
|
else:
|
|
|
|
forb = []
|
|
|
|
self.forbiddenItems += forb
|
|
|
|
self.checkPool()
|
|
|
|
self.addRestricted()
|
|
|
|
return len(forb)
|
|
|
|
|
|
|
|
def getForbiddenSuits(self):
|
|
|
|
self.log.debug("getForbiddenSuits BEGIN. forbidden="+str(self.forbiddenItems)+",ap="+self.startAP)
|
|
|
|
removableSuits = [suit for suit in self.suits if self.checkPool([suit])]
|
|
|
|
if 'Varia' in removableSuits and self.startAP in ['Bubble Mountain', 'Firefleas Top']:
|
|
|
|
# Varia has to be first item there, and checkPool can't detect it
|
|
|
|
removableSuits.remove('Varia')
|
|
|
|
self.log.debug("getForbiddenSuits removable="+str(removableSuits))
|
|
|
|
if len(removableSuits) > 0:
|
|
|
|
# remove at least one
|
|
|
|
if self.addForbidden(removableSuits) == 0:
|
|
|
|
self.forbiddenItems.append(removableSuits.pop())
|
|
|
|
self.checkPool()
|
|
|
|
self.addRestricted()
|
|
|
|
else:
|
|
|
|
self.superFun.remove('Suits')
|
|
|
|
self.log.debug("Super Fun : Could not remove any suit")
|
|
|
|
self.log.debug("getForbiddenSuits END. forbidden="+str(self.forbiddenItems))
|
|
|
|
|
|
|
|
def getForbiddenMovement(self):
|
|
|
|
self.log.debug("getForbiddenMovement BEGIN. forbidden="+str(self.forbiddenItems))
|
|
|
|
removableMovement = [mvt for mvt in self.movementItems if self.checkPool([mvt])]
|
|
|
|
self.log.debug("getForbiddenMovement removable="+str(removableMovement))
|
|
|
|
if len(removableMovement) > 0:
|
|
|
|
# remove at least the most important
|
|
|
|
self.forbiddenItems.append(removableMovement.pop(0))
|
|
|
|
self.addForbidden(removableMovement + [None])
|
|
|
|
else:
|
|
|
|
self.superFun.remove('Movement')
|
|
|
|
self.log.debug('Super Fun : Could not remove any movement item')
|
|
|
|
self.log.debug("getForbiddenMovement END. forbidden="+str(self.forbiddenItems))
|
|
|
|
|
|
|
|
def getForbiddenCombat(self):
|
|
|
|
self.log.debug("getForbiddenCombat BEGIN. forbidden="+str(self.forbiddenItems))
|
|
|
|
removableCombat = [cbt for cbt in self.combatItems if self.checkPool([cbt])]
|
|
|
|
self.log.debug("getForbiddenCombat removable="+str(removableCombat))
|
|
|
|
if len(removableCombat) > 0:
|
|
|
|
fake = [] # placeholders to avoid tricking the gaussian into removing too much stuff
|
|
|
|
if len(removableCombat) > 0:
|
|
|
|
# remove at least one if possible (will be screw or plasma)
|
|
|
|
self.forbiddenItems.append(removableCombat.pop(0))
|
|
|
|
fake.append(None)
|
|
|
|
# if plasma is still available, remove it as well if we can
|
|
|
|
if len(removableCombat) > 0 and removableCombat[0] == 'Plasma' and self.checkPool([removableCombat[0]]):
|
|
|
|
self.forbiddenItems.append(removableCombat.pop(0))
|
|
|
|
fake.append(None)
|
|
|
|
self.addForbidden(removableCombat + fake)
|
|
|
|
else:
|
|
|
|
self.superFun.remove('Combat')
|
|
|
|
self.log.debug('Super Fun : Could not remove any combat item')
|
|
|
|
self.log.debug("getForbiddenCombat END. forbidden="+str(self.forbiddenItems))
|
|
|
|
|
|
|
|
def getForbidden(self):
|
|
|
|
self.forbiddenItems = []
|
|
|
|
self.restrictedLocs = []
|
|
|
|
self.errorMsgs = []
|
|
|
|
if 'Suits' in self.superFun: # impact on movement item
|
|
|
|
self.getForbiddenSuits()
|
|
|
|
if 'Movement' in self.superFun:
|
|
|
|
self.getForbiddenMovement()
|
|
|
|
if 'Combat' in self.superFun:
|
|
|
|
self.getForbiddenCombat()
|
|
|
|
# if no super fun, check that there's no restricted locations (for ultra sparse)
|
|
|
|
if len(self.superFun) == 0:
|
|
|
|
self.addRestricted()
|
|
|
|
self.log.debug("forbiddenItems: {}".format(self.forbiddenItems))
|
|
|
|
self.log.debug("restrictedLocs: {}".format([loc.Name for loc in self.restrictedLocs]))
|