195 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			195 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| import copy, random, utils.log
 | |
| 
 | |
| from graph.graph_utils import getAccessPoint
 | |
| from rando.ItemLocContainer import getLocListStr
 | |
| 
 | |
| # Holds settings related to item placement restrictions.
 | |
| # canPlaceAtLocation is the main entry point here
 | |
| class Restrictions(object):
 | |
|     def __init__(self, settings):
 | |
|         self.log = utils.log.get('Restrictions')
 | |
|         self.settings = settings
 | |
|         # Item split : Major, Chozo, Full, Scavenger
 | |
|         self.split = settings.restrictions['MajorMinor']
 | |
|         self.suitsRestrictions = settings.restrictions['Suits']
 | |
|         self.scavLocs = None
 | |
|         self.scavIsVanilla = False
 | |
|         self.scavEscape = False
 | |
|         self.restrictionDictChecker = None
 | |
|         if self.split == 'Scavenger':
 | |
|             self.scavIsVanilla = settings.restrictions['ScavengerParams']['vanillaItems']
 | |
|             self.scavEscape = settings.restrictions['ScavengerParams']['escape']
 | |
|         # checker function chain used by canPlaceAtLocation
 | |
|         self.checkers = self.getCheckers()
 | |
|         self.static = {}
 | |
|         self.dynamic = {}
 | |
|         # only useful in door color rando
 | |
|         self.mandatoryBeams = []
 | |
| 
 | |
|     def disable(self):
 | |
|         self.split = "Full"
 | |
|         self.suitsRestrictions = False
 | |
|         self.checkers = []
 | |
| 
 | |
|     def setScavengerLocs(self, scavLocs):
 | |
|         self.scavLocs = scavLocs
 | |
|         self.log.debug("scavLocs="+getLocListStr(scavLocs))
 | |
|         self.scavItemTypes = [loc.VanillaItemType for loc in scavLocs]
 | |
| 
 | |
|     def isEarlyMorph(self):
 | |
|         return self.settings.restrictions['Morph'] == 'early'
 | |
| 
 | |
|     def isLateMorph(self):
 | |
|         return self.settings.restrictions['Morph'] == 'late'
 | |
| 
 | |
|     def isLateDoors(self):
 | |
|         return self.settings.restrictions['doors'] == 'late'
 | |
| 
 | |
|     def isChozo(self):
 | |
|         return self.split == 'Chozo'
 | |
| 
 | |
|     def isScavenger(self):
 | |
|         return self.split == "Scavenger"
 | |
| 
 | |
|     def lateMorphInit(self, ap, emptyContainer, services):
 | |
|         assert self.isLateMorph()
 | |
|         morph = emptyContainer.getNextItemInPool('Morph')
 | |
|         assert morph is not None
 | |
|         locs = services.possibleLocations(morph, ap, emptyContainer, bossesKilled=False)
 | |
|         self.lateMorphLimit = len(locs)
 | |
|         self.log.debug('lateMorphInit. {} locs: {}'.format(self.lateMorphLimit, getLocListStr(locs)))
 | |
|         areas = {}
 | |
|         for loc in locs:
 | |
|             areas[loc.GraphArea] = areas.get(loc.GraphArea, 0) + 1
 | |
|         self.log.debug('lateMorphLimit. areas: {}'.format(areas))
 | |
|         if len(areas) > 1:
 | |
|             self.lateMorphForbiddenArea = getAccessPoint(ap).GraphArea
 | |
|             self.log.debug('lateMorphLimit. forbid start area: {}'.format(self.lateMorphForbiddenArea))
 | |
|         else:
 | |
|             self.lateMorphForbiddenArea = None
 | |
| 
 | |
|     NoCheckCat = set(['Energy', 'Nothing', 'Boss'])
 | |
| 
 | |
|     def setPlacementRestrictions(self, restrictionDict):
 | |
|         self.log.debug("set placement restrictions")
 | |
|         self.log.debug(restrictionDict)
 | |
|         if self.restrictionDictChecker is not None:
 | |
|             self.checkers.remove(self.restrictionDictChecker)
 | |
|             self.restrictionDictChecker = None
 | |
|         if restrictionDict is None:
 | |
|             return
 | |
|         self.restrictionDictChecker = lambda item, loc, cont: item.Category in Restrictions.NoCheckCat\
 | |
|                                    or (item.Category == 'Ammo' and cont.hasUnrestrictedLocWithItemType(item.Type))\
 | |
|                                    or loc.Name in restrictionDict[loc.GraphArea][item.Type]
 | |
|         self.checkers.append(self.restrictionDictChecker)
 | |
| 
 | |
|     def isLocMajor(self, loc):
 | |
|         return not loc.isBoss() and (self.split == "Full" or loc.isClass(self.split))
 | |
| 
 | |
|     def isLocMinor(self, loc):
 | |
|         return not loc.isBoss() and (self.split == "Full" or not loc.isClass(self.split))
 | |
| 
 | |
|     def isItemMajor(self, item):
 | |
|         if self.split == "Full":
 | |
|             return True
 | |
|         elif self.split == 'Scavenger':
 | |
|             return not self.isItemMinor(item)
 | |
|         else:
 | |
|             return item.Class == self.split
 | |
| 
 | |
|     def isItemMinor(self, item):
 | |
|         if self.split == "Full":
 | |
|             return True
 | |
|         elif self.split == 'Scavenger':
 | |
|             return item.Class != "Major" or item.Category == "Energy"
 | |
|         else:
 | |
|             return item.Class == "Minor"
 | |
| 
 | |
|     def isItemLocMatching(self, item, loc):
 | |
|         if self.split == "Full":
 | |
|             return True
 | |
|         if loc.isClass(self.split):
 | |
|             return item.Class == self.split
 | |
|         else:
 | |
|             return item.Class == "Minor"
 | |
| 
 | |
|     # return True if we can keep morph as a possibility
 | |
|     def lateMorphCheck(self, container, possibleLocs):
 | |
|         # the closer we get to the limit the higher the chances of allowing morph
 | |
|         proba = random.randint(0, self.lateMorphLimit)
 | |
|         if self.split == 'Full':
 | |
|             nbItems = len(container.currentItems)
 | |
|         else:
 | |
|             nbItems = len([item for item in container.currentItems if self.split == item.Class])
 | |
|         if proba > nbItems:
 | |
|             return None
 | |
|         if self.lateMorphForbiddenArea is not None:
 | |
|             morphLocs = [loc for loc in possibleLocs if loc.GraphArea != self.lateMorphForbiddenArea]
 | |
|             forbidden = len(morphLocs) == 0
 | |
|             possibleLocs = morphLocs if not forbidden else None
 | |
|         return possibleLocs
 | |
| 
 | |
|     def isSuit(self, item):
 | |
|         return item.Type == 'Varia' or item.Type == 'Gravity'
 | |
|     
 | |
|     def getCheckers(self):
 | |
|         checkers = []
 | |
|         self.log.debug("add bosses restriction")
 | |
|         checkers.append(lambda item, loc, cont: (item.Category != 'Boss' and not loc.isBoss()) or (item.Category == 'Boss' and item.Name == loc.Name))
 | |
|         if self.split != 'Full':
 | |
|             if self.split != 'Scavenger':
 | |
|                 self.log.debug("add majorsSplit restriction")
 | |
|                 checkers.append(lambda item, loc, cont: self.isItemLocMatching(item, loc))
 | |
|             else:
 | |
|                 self.log.debug("add scavenger restriction")
 | |
|                 baseScavCheck = lambda item, loc: ((loc.VanillaItemType is None and self.isItemMinor(item))
 | |
|                                                 or (loc.VanillaItemType is not None and self.isItemMajor(item)))
 | |
|                 vanillaScavCheck = lambda item, loc: (self.scavLocs is None
 | |
|                                                   or (loc not in self.scavLocs and item.Type not in self.scavItemTypes)
 | |
|                                                   or (item.Type == loc.VanillaItemType and loc in self.scavLocs))
 | |
|                 nonVanillaScavCheck = lambda item, loc: (self.scavLocs is None
 | |
|                                                       or loc not in self.scavLocs
 | |
|                                                       or (loc in self.scavLocs and item.Category != 'Nothing'))
 | |
|                 if self.scavIsVanilla:
 | |
|                     checkers.append(lambda item, loc, cont: baseScavCheck(item, loc) and vanillaScavCheck(item, loc))
 | |
|                 else:
 | |
|                     checkers.append(lambda item, loc, cont: baseScavCheck(item, loc) and nonVanillaScavCheck(item, loc))
 | |
|         if self.suitsRestrictions:
 | |
|             self.log.debug("add suits restriction")
 | |
|             checkers.append(lambda item, loc, cont: not self.isSuit(item) or loc.GraphArea != 'Crateria')
 | |
|         return checkers
 | |
| 
 | |
|     # return bool telling whether we can place a given item at a given location
 | |
|     def canPlaceAtLocation(self, item, location, container):
 | |
|         ret = True
 | |
|         for chk in self.checkers:
 | |
|             ret = ret and chk(item, location, container)
 | |
|             if not ret:
 | |
|                 break
 | |
| 
 | |
|         return ret
 | |
| 
 | |
|     ### Below : faster implementation tailored for random fill
 | |
| 
 | |
|     def precomputeRestrictions(self, container):
 | |
|         # precompute the values for canPlaceAtLocation. only for random filler.
 | |
|         # dict (loc name, item type) -> bool
 | |
|         items = container.getDistinctItems()
 | |
|         for item in items:
 | |
|             for location in container.unusedLocations:
 | |
|                 self.static[(location.Name, item.Type)] = self.canPlaceAtLocation(item, location, container)
 | |
| 
 | |
|         container.unrestrictedItems = set(['Super', 'PowerBomb'])
 | |
|         for item in items:
 | |
|             if item.Type not in ['Super', 'PowerBomb']:
 | |
|                 continue
 | |
|             for location in container.unusedLocations:
 | |
|                 self.dynamic[(location.Name, item.Type)] = self.canPlaceAtLocation(item, location, container)
 | |
|         container.unrestrictedItems = set()
 | |
| 
 | |
|     def canPlaceAtLocationFast(self, itemType, locName, container):
 | |
|         if itemType in ['Super', 'PowerBomb'] and container.hasUnrestrictedLocWithItemType(itemType):
 | |
|             return self.dynamic.get((locName, itemType))
 | |
|         else:
 | |
|             return self.static.get((locName, itemType))
 | 
