791 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			791 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
								 | 
							
								from utils.utils import randGaussBounds, getRangeDict, chooseFromRange
							 | 
						||
| 
								 | 
							
								import utils.log, logging, copy, random
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class Item:
							 | 
						||
| 
								 | 
							
								    __slots__ = ( 'Category', 'Class', 'Name', 'Code', 'Type', 'BeamBits', 'ItemBits', 'Id' )
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, Category, Class, Name, Type, Code=None, BeamBits=0, ItemBits=0, Id=None):
							 | 
						||
| 
								 | 
							
								        self.Category = Category
							 | 
						||
| 
								 | 
							
								        self.Class = Class
							 | 
						||
| 
								 | 
							
								        self.Code = Code
							 | 
						||
| 
								 | 
							
								        self.Name = Name
							 | 
						||
| 
								 | 
							
								        self.Type = Type
							 | 
						||
| 
								 | 
							
								        self.BeamBits = BeamBits
							 | 
						||
| 
								 | 
							
								        self.ItemBits = ItemBits
							 | 
						||
| 
								 | 
							
								        self.Id = Id
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def withClass(self, Class):
							 | 
						||
| 
								 | 
							
								        return Item(self.Category, Class, self.Name, self.Type, self.Code, self.BeamBits, self.ItemBits)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __eq__(self, other):
							 | 
						||
| 
								 | 
							
								        # used to remove an item from a list
							 | 
						||
| 
								 | 
							
								        return self.Type == other.Type and self.Class == other.Class
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __hash__(self):
							 | 
						||
| 
								 | 
							
								        # as we define __eq__ we have to also define __hash__ to use items as dictionnary keys
							 | 
						||
| 
								 | 
							
								        # https://docs.python.org/3/reference/datamodel.html#object.__hash__
							 | 
						||
| 
								 | 
							
								        return id(self)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __repr__(self):
							 | 
						||
| 
								 | 
							
								      return "Item({}, {}, {}, {}, {})".format(self.Category,
							 | 
						||
| 
								 | 
							
								          self.Class, self.Code, self.Name, self.Type)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def json(self):
							 | 
						||
| 
								 | 
							
								        # as we have slots instead of dict
							 | 
						||
| 
								 | 
							
								        return {key : getattr(self, key, None) for key in self.__slots__}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ItemManager:
							 | 
						||
| 
								 | 
							
								    Items = {
							 | 
						||
| 
								 | 
							
								        'ETank': Item(
							 | 
						||
| 
								 | 
							
								            Category='Energy',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Energy Tank",
							 | 
						||
| 
								 | 
							
								            Type='ETank',
							 | 
						||
| 
								 | 
							
								            Id=0
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Missile': Item(
							 | 
						||
| 
								 | 
							
								            Category='Ammo',
							 | 
						||
| 
								 | 
							
								            Class='Minor',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Missile",
							 | 
						||
| 
								 | 
							
								            Type='Missile',
							 | 
						||
| 
								 | 
							
								            Id=1
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Super': Item(
							 | 
						||
| 
								 | 
							
								            Category='Ammo',
							 | 
						||
| 
								 | 
							
								            Class='Minor',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Super Missile",
							 | 
						||
| 
								 | 
							
								            Type='Super',
							 | 
						||
| 
								 | 
							
								            Id=2
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'PowerBomb': Item(
							 | 
						||
| 
								 | 
							
								            Category='Ammo',
							 | 
						||
| 
								 | 
							
								            Class='Minor',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Power Bomb",
							 | 
						||
| 
								 | 
							
								            Type='PowerBomb',
							 | 
						||
| 
								 | 
							
								            Id=3
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Bomb': Item(
							 | 
						||
| 
								 | 
							
								            Category='Progression',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Bomb",
							 | 
						||
| 
								 | 
							
								            Type='Bomb',
							 | 
						||
| 
								 | 
							
								            ItemBits=0x1000,
							 | 
						||
| 
								 | 
							
								            Id=4
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Charge': Item(
							 | 
						||
| 
								 | 
							
								            Category='Beam',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Charge Beam",
							 | 
						||
| 
								 | 
							
								            Type='Charge',
							 | 
						||
| 
								 | 
							
								            BeamBits=0x1000,
							 | 
						||
| 
								 | 
							
								            Id=5
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Ice': Item(
							 | 
						||
| 
								 | 
							
								            Category='Progression',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Ice Beam",
							 | 
						||
| 
								 | 
							
								            Type='Ice',
							 | 
						||
| 
								 | 
							
								            BeamBits=0x2,
							 | 
						||
| 
								 | 
							
								            Id=6
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'HiJump': Item(
							 | 
						||
| 
								 | 
							
								            Category='Progression',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Hi-Jump Boots",
							 | 
						||
| 
								 | 
							
								            Type='HiJump',
							 | 
						||
| 
								 | 
							
								            ItemBits=0x100,
							 | 
						||
| 
								 | 
							
								            Id=7
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'SpeedBooster': Item(
							 | 
						||
| 
								 | 
							
								            Category='Progression',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Speed Booster",
							 | 
						||
| 
								 | 
							
								            Type='SpeedBooster',
							 | 
						||
| 
								 | 
							
								            ItemBits=0x2000,
							 | 
						||
| 
								 | 
							
								            Id=8
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Wave': Item(
							 | 
						||
| 
								 | 
							
								            Category='Beam',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Wave Beam",
							 | 
						||
| 
								 | 
							
								            Type='Wave',
							 | 
						||
| 
								 | 
							
								            BeamBits=0x1,
							 | 
						||
| 
								 | 
							
								            Id=9
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Spazer': Item(
							 | 
						||
| 
								 | 
							
								            Category='Beam',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Spazer",
							 | 
						||
| 
								 | 
							
								            Type='Spazer',
							 | 
						||
| 
								 | 
							
								            BeamBits=0x4,
							 | 
						||
| 
								 | 
							
								            Id=10
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'SpringBall': Item(
							 | 
						||
| 
								 | 
							
								            Category='Misc',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Spring Ball",
							 | 
						||
| 
								 | 
							
								            Type='SpringBall',
							 | 
						||
| 
								 | 
							
								            ItemBits=0x2,
							 | 
						||
| 
								 | 
							
								            Id=11
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Varia': Item(
							 | 
						||
| 
								 | 
							
								            Category='Progression',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Varia Suit",
							 | 
						||
| 
								 | 
							
								            Type='Varia',
							 | 
						||
| 
								 | 
							
								            ItemBits=0x1,
							 | 
						||
| 
								 | 
							
								            Id=12
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Plasma': Item(
							 | 
						||
| 
								 | 
							
								            Category='Beam',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Plasma Beam",
							 | 
						||
| 
								 | 
							
								            Type='Plasma',
							 | 
						||
| 
								 | 
							
								            BeamBits=0x8,
							 | 
						||
| 
								 | 
							
								            Id=15
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Grapple': Item(
							 | 
						||
| 
								 | 
							
								            Category='Progression',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Grappling Beam",
							 | 
						||
| 
								 | 
							
								            Type='Grapple',
							 | 
						||
| 
								 | 
							
								            ItemBits=0x4000,
							 | 
						||
| 
								 | 
							
								            Id=16
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Morph': Item(
							 | 
						||
| 
								 | 
							
								            Category='Progression',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Morph Ball",
							 | 
						||
| 
								 | 
							
								            Type='Morph',
							 | 
						||
| 
								 | 
							
								            ItemBits=0x4,
							 | 
						||
| 
								 | 
							
								            Id=19
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Reserve': Item(
							 | 
						||
| 
								 | 
							
								            Category='Energy',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Reserve Tank",
							 | 
						||
| 
								 | 
							
								            Type='Reserve',
							 | 
						||
| 
								 | 
							
								            Id=20
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Gravity': Item(
							 | 
						||
| 
								 | 
							
								            Category='Progression',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Gravity Suit",
							 | 
						||
| 
								 | 
							
								            Type='Gravity',
							 | 
						||
| 
								 | 
							
								            ItemBits=0x20,
							 | 
						||
| 
								 | 
							
								            Id=13
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'XRayScope': Item(
							 | 
						||
| 
								 | 
							
								            Category='Misc',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="X-Ray Scope",
							 | 
						||
| 
								 | 
							
								            Type='XRayScope',
							 | 
						||
| 
								 | 
							
								            ItemBits=0x8000,
							 | 
						||
| 
								 | 
							
								            Id=14
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'SpaceJump': Item(
							 | 
						||
| 
								 | 
							
								            Category='Progression',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Space Jump",
							 | 
						||
| 
								 | 
							
								            Type='SpaceJump',
							 | 
						||
| 
								 | 
							
								            ItemBits=0x200,
							 | 
						||
| 
								 | 
							
								            Id=17
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'ScrewAttack': Item(
							 | 
						||
| 
								 | 
							
								            Category='Misc',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Screw Attack",
							 | 
						||
| 
								 | 
							
								            Type='ScrewAttack',
							 | 
						||
| 
								 | 
							
								            ItemBits= 0x8,
							 | 
						||
| 
								 | 
							
								            Id=18
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Nothing': Item(
							 | 
						||
| 
								 | 
							
								            Category='Nothing',
							 | 
						||
| 
								 | 
							
								            Class='Minor',
							 | 
						||
| 
								 | 
							
								            Code=0xbae9, # new nothing plm
							 | 
						||
| 
								 | 
							
								            Name="Nothing",
							 | 
						||
| 
								 | 
							
								            Type='Nothing',
							 | 
						||
| 
								 | 
							
								            Id=22
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'NoEnergy': Item(
							 | 
						||
| 
								 | 
							
								            Category='Nothing',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xbae9, # see above
							 | 
						||
| 
								 | 
							
								            Name="No Energy",
							 | 
						||
| 
								 | 
							
								            Type='NoEnergy',
							 | 
						||
| 
								 | 
							
								            Id=23
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Kraid': Item(
							 | 
						||
| 
								 | 
							
								            Category='Boss',
							 | 
						||
| 
								 | 
							
								            Class='Boss',
							 | 
						||
| 
								 | 
							
								            Name="Kraid",
							 | 
						||
| 
								 | 
							
								            Type='Kraid',
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Phantoon': Item(
							 | 
						||
| 
								 | 
							
								            Category='Boss',
							 | 
						||
| 
								 | 
							
								            Class='Boss',
							 | 
						||
| 
								 | 
							
								            Name="Phantoon",
							 | 
						||
| 
								 | 
							
								            Type='Phantoon'
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Draygon': Item(
							 | 
						||
| 
								 | 
							
								            Category='Boss',
							 | 
						||
| 
								 | 
							
								            Class='Boss',
							 | 
						||
| 
								 | 
							
								            Name="Draygon",
							 | 
						||
| 
								 | 
							
								            Type='Draygon',
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'Ridley': Item(
							 | 
						||
| 
								 | 
							
								            Category='Boss',
							 | 
						||
| 
								 | 
							
								            Class='Boss',
							 | 
						||
| 
								 | 
							
								            Name="Ridley",
							 | 
						||
| 
								 | 
							
								            Type='Ridley',
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'MotherBrain': Item(
							 | 
						||
| 
								 | 
							
								            Category='Boss',
							 | 
						||
| 
								 | 
							
								            Class='Boss',
							 | 
						||
| 
								 | 
							
								            Name="Mother Brain",
							 | 
						||
| 
								 | 
							
								            Type='MotherBrain',
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        # used only during escape path check
							 | 
						||
| 
								 | 
							
								        'Hyper': Item(
							 | 
						||
| 
								 | 
							
								            Category='Beam',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xffff,
							 | 
						||
| 
								 | 
							
								            Name="Hyper Beam",
							 | 
						||
| 
								 | 
							
								            Type='Hyper',
							 | 
						||
| 
								 | 
							
								        ),
							 | 
						||
| 
								 | 
							
								        'ArchipelagoItem': Item(
							 | 
						||
| 
								 | 
							
								            Category='ArchipelagoItem',
							 | 
						||
| 
								 | 
							
								            Class='Major',
							 | 
						||
| 
								 | 
							
								            Code=0xf870,
							 | 
						||
| 
								 | 
							
								            Name="Generic",
							 | 
						||
| 
								 | 
							
								            Type='ArchipelagoItem',
							 | 
						||
| 
								 | 
							
								            Id=21
							 | 
						||
| 
								 | 
							
								        )
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    for itemType, item in Items.items():
							 | 
						||
| 
								 | 
							
								      if item.Type != itemType:
							 | 
						||
| 
								 | 
							
								        raise RuntimeError("Wrong item type for {} (expected {})".format(item, itemType))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def isBeam(item):
							 | 
						||
| 
								 | 
							
								        return item.BeamBits != 0
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def getItemTypeCode(item, itemVisibility):
							 | 
						||
| 
								 | 
							
								        if item.Category == 'Nothing':
							 | 
						||
| 
								 | 
							
								            if itemVisibility in ['Visible', 'Chozo']:
							 | 
						||
| 
								 | 
							
								                modifier = 0
							 | 
						||
| 
								 | 
							
								            elif itemVisibility == 'Hidden':
							 | 
						||
| 
								 | 
							
								                modifier = 4
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            if itemVisibility == 'Visible':
							 | 
						||
| 
								 | 
							
								                modifier = 0
							 | 
						||
| 
								 | 
							
								            elif itemVisibility == 'Chozo':
							 | 
						||
| 
								 | 
							
								                modifier = 4
							 | 
						||
| 
								 | 
							
								            elif itemVisibility == 'Hidden':
							 | 
						||
| 
								 | 
							
								                modifier = 8
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        itemCode = item.Code + modifier
							 | 
						||
| 
								 | 
							
								        return itemCode
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, majorsSplit, qty, sm, nLocs, maxDiff):
							 | 
						||
| 
								 | 
							
								        self.qty = qty
							 | 
						||
| 
								 | 
							
								        self.sm = sm
							 | 
						||
| 
								 | 
							
								        self.majorsSplit = majorsSplit
							 | 
						||
| 
								 | 
							
								        self.nLocs = nLocs
							 | 
						||
| 
								 | 
							
								        self.maxDiff = maxDiff
							 | 
						||
| 
								 | 
							
								        self.majorClass = 'Chozo' if majorsSplit == 'Chozo' else 'Major'
							 | 
						||
| 
								 | 
							
								        self.itemPool = []
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def newItemPool(self, addBosses=True):
							 | 
						||
| 
								 | 
							
								        self.itemPool = []
							 | 
						||
| 
								 | 
							
								        if addBosses == True:
							 | 
						||
| 
								 | 
							
								            # for the bosses
							 | 
						||
| 
								 | 
							
								            for boss in ['Kraid', 'Phantoon', 'Draygon', 'Ridley', 'MotherBrain']:
							 | 
						||
| 
								 | 
							
								                self.addMinor(boss)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def getItemPool(self):
							 | 
						||
| 
								 | 
							
								        return self.itemPool
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def setItemPool(self, pool):
							 | 
						||
| 
								 | 
							
								        self.itemPool = pool
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def addItem(self, itemType, itemClass=None):
							 | 
						||
| 
								 | 
							
								        self.itemPool.append(ItemManager.getItem(itemType, itemClass))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def addMinor(self, minorType):
							 | 
						||
| 
								 | 
							
								        self.addItem(minorType, 'Minor')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # remove from pool an item of given type. item type has to be in original Items list.
							 | 
						||
| 
								 | 
							
								    def removeItem(self, itemType):
							 | 
						||
| 
								 | 
							
								        for idx, item in enumerate(self.itemPool):
							 | 
						||
| 
								 | 
							
								            if item.Type == itemType:
							 | 
						||
| 
								 | 
							
								                self.itemPool = self.itemPool[0:idx] + self.itemPool[idx+1:]
							 | 
						||
| 
								 | 
							
								                return item
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def removeForbiddenItems(self, forbiddenItems):
							 | 
						||
| 
								 | 
							
								        # the pool is the one managed by the Randomizer
							 | 
						||
| 
								 | 
							
								        for itemType in forbiddenItems:
							 | 
						||
| 
								 | 
							
								            self.removeItem(itemType)
							 | 
						||
| 
								 | 
							
								            self.addItem('NoEnergy', self.majorClass)
							 | 
						||
| 
								 | 
							
								        return self.itemPool
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def getItem(itemType, itemClass=None):
							 | 
						||
| 
								 | 
							
								        if itemClass is None:
							 | 
						||
| 
								 | 
							
								            return copy.copy(ItemManager.Items[itemType])
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return ItemManager.Items[itemType].withClass(itemClass)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def createItemPool(self, exclude=None):
							 | 
						||
| 
								 | 
							
								        itemPoolGenerator = ItemPoolGenerator.factory(self.majorsSplit, self, self.qty, self.sm, exclude, self.nLocs, self.maxDiff)
							 | 
						||
| 
								 | 
							
								        self.itemPool = itemPoolGenerator.getItemPool()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def getProgTypes():
							 | 
						||
| 
								 | 
							
								        return [item for item in ItemManager.Items if ItemManager.Items[item].Category == 'Progression']
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def hasItemInPoolCount(self, itemName, count):
							 | 
						||
| 
								 | 
							
								        return len([item for item in self.itemPool if item.Type == itemName]) >= count
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ItemPoolGenerator(object):
							 | 
						||
| 
								 | 
							
								    @staticmethod
							 | 
						||
| 
								 | 
							
								    def factory(majorsSplit, itemManager, qty, sm, exclude, nLocs, maxDiff):
							 | 
						||
| 
								 | 
							
								        if majorsSplit == 'Chozo':
							 | 
						||
| 
								 | 
							
								            return ItemPoolGeneratorChozo(itemManager, qty, sm, maxDiff)
							 | 
						||
| 
								 | 
							
								        elif majorsSplit == 'Plando':
							 | 
						||
| 
								 | 
							
								            return ItemPoolGeneratorPlando(itemManager, qty, sm, exclude, nLocs, maxDiff)
							 | 
						||
| 
								 | 
							
								        elif nLocs == 105:
							 | 
						||
| 
								 | 
							
								            if majorsSplit == "Scavenger":
							 | 
						||
| 
								 | 
							
								                return ItemPoolGeneratorScavenger(itemManager, qty, sm, maxDiff)
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                return ItemPoolGeneratorMajors(itemManager, qty, sm, maxDiff)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            return ItemPoolGeneratorMinimizer(itemManager, qty, sm, nLocs, maxDiff)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, itemManager, qty, sm, maxDiff):
							 | 
						||
| 
								 | 
							
								        self.itemManager = itemManager
							 | 
						||
| 
								 | 
							
								        self.qty = qty
							 | 
						||
| 
								 | 
							
								        self.sm = sm
							 | 
						||
| 
								 | 
							
								        self.maxItems = 105 # 100 item locs and 5 bosses
							 | 
						||
| 
								 | 
							
								        self.maxEnergy = 18 # 14E, 4R
							 | 
						||
| 
								 | 
							
								        self.maxDiff = maxDiff
							 | 
						||
| 
								 | 
							
								        self.log = utils.log.get('ItemPool')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def isUltraSparseNoTanks(self):
							 | 
						||
| 
								 | 
							
								        # if low stuff botwoon is not known there is a hard energy req of one tank, even
							 | 
						||
| 
								 | 
							
								        # with both suits
							 | 
						||
| 
								 | 
							
								        lowStuffBotwoon = self.sm.knowsLowStuffBotwoon()
							 | 
						||
| 
								 | 
							
								        return random.random() < 0.5 and (lowStuffBotwoon.bool == True and lowStuffBotwoon.difficulty <= self.maxDiff)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def calcMaxMinors(self):
							 | 
						||
| 
								 | 
							
								        pool = self.itemManager.getItemPool()
							 | 
						||
| 
								 | 
							
								        energy = [item for item in pool if item.Category == 'Energy']
							 | 
						||
| 
								 | 
							
								        if len(energy) == 0:
							 | 
						||
| 
								 | 
							
								            self.maxMinors = 0.66*(self.maxItems - 5) # 5 for bosses
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            # if energy has been placed, we can be as accurate as possible
							 | 
						||
| 
								 | 
							
								            self.maxMinors = self.maxItems - len(pool) + self.nbMinorsAlready
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def calcMaxAmmo(self):
							 | 
						||
| 
								 | 
							
								        self.nbMinorsAlready = 5
							 | 
						||
| 
								 | 
							
								        # always add enough minors to pass zebetites (1100 damages) and mother brain 1 (3000 damages)
							 | 
						||
| 
								 | 
							
								        # accounting for missile refill. so 15-10, or 10-10 if ice zeb skip is known (Ice is always in item pool)
							 | 
						||
| 
								 | 
							
								        zebSkip = self.sm.knowsIceZebSkip()
							 | 
						||
| 
								 | 
							
								        if zebSkip.bool == False or zebSkip.difficulty > self.maxDiff:
							 | 
						||
| 
								 | 
							
								            self.log.debug("Add missile because ice zeb skip is not known")
							 | 
						||
| 
								 | 
							
								            self.itemManager.addMinor('Missile')
							 | 
						||
| 
								 | 
							
								            self.nbMinorsAlready += 1
							 | 
						||
| 
								 | 
							
								        self.calcMaxMinors()
							 | 
						||
| 
								 | 
							
								        self.log.debug("maxMinors: "+str(self.maxMinors))
							 | 
						||
| 
								 | 
							
								        self.minorLocations = max(0, self.maxMinors*self.qty['minors']/100.0 - self.nbMinorsAlready)
							 | 
						||
| 
								 | 
							
								        self.log.debug("minorLocations: {}".format(self.minorLocations))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    # add ammo given quantity settings
							 | 
						||
| 
								 | 
							
								    def addAmmo(self):
							 | 
						||
| 
								 | 
							
								        self.calcMaxAmmo()
							 | 
						||
| 
								 | 
							
								        # we have to remove the minors already added
							 | 
						||
| 
								 | 
							
								        maxItems = min(len(self.itemManager.getItemPool()) + int(self.minorLocations), self.maxItems)
							 | 
						||
| 
								 | 
							
								        self.log.debug("maxItems: {}, (self.maxItems={})".format(maxItems, self.maxItems))
							 | 
						||
| 
								 | 
							
								        ammoQty = self.qty['ammo']
							 | 
						||
| 
								 | 
							
								        if not self.qty['strictMinors']:
							 | 
						||
| 
								 | 
							
								            rangeDict = getRangeDict(ammoQty)
							 | 
						||
| 
								 | 
							
								            self.log.debug("rangeDict: {}".format(rangeDict))
							 | 
						||
| 
								 | 
							
								            while len(self.itemManager.getItemPool()) < maxItems:
							 | 
						||
| 
								 | 
							
								                item = chooseFromRange(rangeDict)
							 | 
						||
| 
								 | 
							
								                self.itemManager.addMinor(item)
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            minorsTypes = ['Missile', 'Super', 'PowerBomb']
							 | 
						||
| 
								 | 
							
								            totalProps = sum(ammoQty[m] for m in minorsTypes)
							 | 
						||
| 
								 | 
							
								            minorsByProp = sorted(minorsTypes, key=lambda m: ammoQty[m])
							 | 
						||
| 
								 | 
							
								            totalMinorLocations = self.minorLocations + self.nbMinorsAlready
							 | 
						||
| 
								 | 
							
								            self.log.debug("totalMinorLocations: {}".format(totalMinorLocations))
							 | 
						||
| 
								 | 
							
								            def ammoCount(ammo):
							 | 
						||
| 
								 | 
							
								                return float(len([item for item in self.itemManager.getItemPool() if item.Type == ammo]))
							 | 
						||
| 
								 | 
							
								            def targetRatio(ammo):
							 | 
						||
| 
								 | 
							
								                return round(float(ammoQty[ammo])/totalProps, 3)
							 | 
						||
| 
								 | 
							
								            def cmpRatio(ammo, ratio):
							 | 
						||
| 
								 | 
							
								                thisAmmo = ammoCount(ammo)
							 | 
						||
| 
								 | 
							
								                thisRatio = round(thisAmmo/totalMinorLocations, 3)
							 | 
						||
| 
								 | 
							
								                nextRatio = round((thisAmmo + 1)/totalMinorLocations, 3)
							 | 
						||
| 
								 | 
							
								                self.log.debug("{} current, next/target ratio: {}, {}/{}".format(ammo, thisRatio, nextRatio, ratio))
							 | 
						||
| 
								 | 
							
								                return abs(nextRatio - ratio) < abs(thisRatio - ratio)
							 | 
						||
| 
								 | 
							
								            def fillAmmoType(ammo, checkRatio=True):
							 | 
						||
| 
								 | 
							
								                ratio = targetRatio(ammo)
							 | 
						||
| 
								 | 
							
								                self.log.debug("{}: target ratio: {}".format(ammo, ratio))
							 | 
						||
| 
								 | 
							
								                while len(self.itemManager.getItemPool()) < maxItems and (not checkRatio or cmpRatio(ammo, ratio)):
							 | 
						||
| 
								 | 
							
								                    self.log.debug("Add {}".format(ammo))
							 | 
						||
| 
								 | 
							
								                    self.itemManager.addMinor(ammo)
							 | 
						||
| 
								 | 
							
								            for m in minorsByProp:
							 | 
						||
| 
								 | 
							
								                fillAmmoType(m)
							 | 
						||
| 
								 | 
							
								            # now that the ratios have been matched as exactly as possible, we distribute the error
							 | 
						||
| 
								 | 
							
								            def getError(m, countOffset=0):
							 | 
						||
| 
								 | 
							
								                return abs((ammoCount(m)+countOffset)/totalMinorLocations - targetRatio(m))
							 | 
						||
| 
								 | 
							
								            while len(self.itemManager.getItemPool()) < maxItems:
							 | 
						||
| 
								 | 
							
								                minNextError = 1000
							 | 
						||
| 
								 | 
							
								                chosenAmmo = None
							 | 
						||
| 
								 | 
							
								                for m in minorsByProp:
							 | 
						||
| 
								 | 
							
								                    nextError = getError(m, 1)
							 | 
						||
| 
								 | 
							
								                    if nextError < minNextError:
							 | 
						||
| 
								 | 
							
								                        minNextError = nextError
							 | 
						||
| 
								 | 
							
								                        chosenAmmo = m
							 | 
						||
| 
								 | 
							
								                self.itemManager.addMinor(chosenAmmo)
							 | 
						||
| 
								 | 
							
								        # fill up the rest with blank items
							 | 
						||
| 
								 | 
							
								        for i in range(self.maxItems - maxItems):
							 | 
						||
| 
								 | 
							
								            self.itemManager.addMinor('Nothing')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ItemPoolGeneratorChozo(ItemPoolGenerator):
							 | 
						||
| 
								 | 
							
								    def addEnergy(self):
							 | 
						||
| 
								 | 
							
								        total = 18
							 | 
						||
| 
								 | 
							
								        energyQty = self.qty['energy']
							 | 
						||
| 
								 | 
							
								        if energyQty == 'ultra sparse':
							 | 
						||
| 
								 | 
							
								            # 0-1, remove reserve tank and two etanks, check if it also remove the last etank
							 | 
						||
| 
								 | 
							
								            self.itemManager.removeItem('Reserve')
							 | 
						||
| 
								 | 
							
								            self.itemManager.addItem('NoEnergy', 'Chozo')
							 | 
						||
| 
								 | 
							
								            self.itemManager.removeItem('ETank')
							 | 
						||
| 
								 | 
							
								            self.itemManager.addItem('NoEnergy', 'Chozo')
							 | 
						||
| 
								 | 
							
								            self.itemManager.removeItem('ETank')
							 | 
						||
| 
								 | 
							
								            self.itemManager.addItem('NoEnergy', 'Chozo')
							 | 
						||
| 
								 | 
							
								            if self.isUltraSparseNoTanks():
							 | 
						||
| 
								 | 
							
								                # no etank nor reserve
							 | 
						||
| 
								 | 
							
								                self.itemManager.removeItem('ETank')
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem('NoEnergy', 'Chozo')
							 | 
						||
| 
								 | 
							
								            elif random.random() < 0.5:
							 | 
						||
| 
								 | 
							
								                # replace only etank with reserve
							 | 
						||
| 
								 | 
							
								                self.itemManager.removeItem('ETank')
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem('Reserve', 'Chozo')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # complete up to 18 energies with nothing item
							 | 
						||
| 
								 | 
							
								            alreadyInPool = 4
							 | 
						||
| 
								 | 
							
								            for i in range(total - alreadyInPool):
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem('Nothing', 'Minor')
							 | 
						||
| 
								 | 
							
								        elif energyQty == 'sparse':
							 | 
						||
| 
								 | 
							
								            # 4-6
							 | 
						||
| 
								 | 
							
								            # already 3E and 1R
							 | 
						||
| 
								 | 
							
								            alreadyInPool = 4
							 | 
						||
| 
								 | 
							
								            rest = randGaussBounds(2, 5)
							 | 
						||
| 
								 | 
							
								            if rest >= 1:
							 | 
						||
| 
								 | 
							
								                if random.random() < 0.5:
							 | 
						||
| 
								 | 
							
								                    self.itemManager.addItem('Reserve', 'Minor')
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    self.itemManager.addItem('ETank', 'Minor')
							 | 
						||
| 
								 | 
							
								            for i in range(rest-1):
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem('ETank', 'Minor')
							 | 
						||
| 
								 | 
							
								            # complete up to 18 energies with nothing item
							 | 
						||
| 
								 | 
							
								            for i in range(total - alreadyInPool - rest):
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem('Nothing', 'Minor')
							 | 
						||
| 
								 | 
							
								        elif energyQty == 'medium':
							 | 
						||
| 
								 | 
							
								            # 8-12
							 | 
						||
| 
								 | 
							
								            # add up to 3 Reserves or ETanks (cannot add more than 3 reserves)
							 | 
						||
| 
								 | 
							
								            for i in range(3):
							 | 
						||
| 
								 | 
							
								                if random.random() < 0.5:
							 | 
						||
| 
								 | 
							
								                    self.itemManager.addItem('Reserve', 'Minor')
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    self.itemManager.addItem('ETank', 'Minor')
							 | 
						||
| 
								 | 
							
								            # 7 already in the pool (3 E, 1 R, + the previous 3)
							 | 
						||
| 
								 | 
							
								            alreadyInPool = 7
							 | 
						||
| 
								 | 
							
								            rest = 1 + randGaussBounds(4, 3.7)
							 | 
						||
| 
								 | 
							
								            for i in range(rest):
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem('ETank', 'Minor')
							 | 
						||
| 
								 | 
							
								            # fill the rest with NoEnergy
							 | 
						||
| 
								 | 
							
								            for i in range(total - alreadyInPool - rest):
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem('Nothing', 'Minor')
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            # add the vanilla 3 reserves and 13 Etanks
							 | 
						||
| 
								 | 
							
								            for i in range(3):
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem('Reserve', 'Minor')
							 | 
						||
| 
								 | 
							
								            for i in range(11):
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem('ETank', 'Minor')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def getItemPool(self):
							 | 
						||
| 
								 | 
							
								        self.itemManager.newItemPool()
							 | 
						||
| 
								 | 
							
								        # 25 locs: 16 majors, 3 etanks, 1 reserve, 2 missile, 2 supers, 1 pb
							 | 
						||
| 
								 | 
							
								        for itemType in ['ETank', 'ETank', 'ETank', 'Reserve', 'Missile', 'Missile', 'Super', 'Super', 'PowerBomb', 'Bomb', 'Charge', 'Ice', 'HiJump', 'SpeedBooster', 'Wave', 'Spazer', 'SpringBall', 'Varia', 'Plasma', 'Grapple', 'Morph', 'Gravity', 'XRayScope', 'SpaceJump', 'ScrewAttack']:
							 | 
						||
| 
								 | 
							
								            self.itemManager.addItem(itemType, 'Chozo')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.addEnergy()
							 | 
						||
| 
								 | 
							
								        self.addAmmo()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return self.itemManager.getItemPool()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ItemPoolGeneratorMajors(ItemPoolGenerator):
							 | 
						||
| 
								 | 
							
								    def __init__(self, itemManager, qty, sm, maxDiff):
							 | 
						||
| 
								 | 
							
								        super(ItemPoolGeneratorMajors, self).__init__(itemManager, qty, sm, maxDiff)
							 | 
						||
| 
								 | 
							
								        self.sparseRest = 1 + randGaussBounds(2, 5)
							 | 
						||
| 
								 | 
							
								        self.mediumRest = 3 + randGaussBounds(4, 3.7)
							 | 
						||
| 
								 | 
							
								        self.ultraSparseNoTanks = self.isUltraSparseNoTanks()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def addNoEnergy(self):
							 | 
						||
| 
								 | 
							
								        self.itemManager.addItem('NoEnergy')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def addEnergy(self):
							 | 
						||
| 
								 | 
							
								        total = self.maxEnergy
							 | 
						||
| 
								 | 
							
								        alreadyInPool = 2
							 | 
						||
| 
								 | 
							
								        def getE(toAdd):
							 | 
						||
| 
								 | 
							
								            nonlocal total, alreadyInPool
							 | 
						||
| 
								 | 
							
								            d = total - alreadyInPool - toAdd
							 | 
						||
| 
								 | 
							
								            if d < 0:
							 | 
						||
| 
								 | 
							
								                toAdd += d
							 | 
						||
| 
								 | 
							
								            return toAdd
							 | 
						||
| 
								 | 
							
								        energyQty = self.qty['energy']
							 | 
						||
| 
								 | 
							
								        if energyQty == 'ultra sparse':
							 | 
						||
| 
								 | 
							
								            # 0-1, add up to one energy (etank or reserve)
							 | 
						||
| 
								 | 
							
								            self.itemManager.removeItem('Reserve')
							 | 
						||
| 
								 | 
							
								            self.itemManager.removeItem('ETank')
							 | 
						||
| 
								 | 
							
								            self.addNoEnergy()
							 | 
						||
| 
								 | 
							
								            if self.ultraSparseNoTanks:
							 | 
						||
| 
								 | 
							
								                # no energy at all
							 | 
						||
| 
								 | 
							
								                self.addNoEnergy()
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                if random.random() < 0.5:
							 | 
						||
| 
								 | 
							
								                    self.itemManager.addItem('ETank')
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    self.itemManager.addItem('Reserve')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # complete with nothing item
							 | 
						||
| 
								 | 
							
								            for i in range(total - alreadyInPool):
							 | 
						||
| 
								 | 
							
								                self.addNoEnergy()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        elif energyQty == 'sparse':
							 | 
						||
| 
								 | 
							
								            # 4-6
							 | 
						||
| 
								 | 
							
								            if random.random() < 0.5:
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem('Reserve')
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem('ETank')
							 | 
						||
| 
								 | 
							
								            # 3 in the pool (1 E, 1 R + the previous one)
							 | 
						||
| 
								 | 
							
								            alreadyInPool = 3
							 | 
						||
| 
								 | 
							
								            rest = self.sparseRest
							 | 
						||
| 
								 | 
							
								            for i in range(rest):
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem('ETank')
							 | 
						||
| 
								 | 
							
								            # complete with nothing item
							 | 
						||
| 
								 | 
							
								            for i in range(total - alreadyInPool - rest):
							 | 
						||
| 
								 | 
							
								                self.addNoEnergy()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        elif energyQty == 'medium':
							 | 
						||
| 
								 | 
							
								            # 8-12
							 | 
						||
| 
								 | 
							
								            # add up to 3 Reserves or ETanks (cannot add more than 3 reserves)
							 | 
						||
| 
								 | 
							
								            alreadyInPool = 2
							 | 
						||
| 
								 | 
							
								            n = getE(3)
							 | 
						||
| 
								 | 
							
								            for i in range(n):
							 | 
						||
| 
								 | 
							
								                if random.random() < 0.5:
							 | 
						||
| 
								 | 
							
								                    self.itemManager.addItem('Reserve')
							 | 
						||
| 
								 | 
							
								                else:
							 | 
						||
| 
								 | 
							
								                    self.itemManager.addItem('ETank')
							 | 
						||
| 
								 | 
							
								            alreadyInPool += n
							 | 
						||
| 
								 | 
							
								            rest = getE(self.mediumRest)
							 | 
						||
| 
								 | 
							
								            for i in range(rest):
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem('ETank')
							 | 
						||
| 
								 | 
							
								            # fill the rest with NoEnergy
							 | 
						||
| 
								 | 
							
								            for i in range(total - alreadyInPool - rest):
							 | 
						||
| 
								 | 
							
								                self.addNoEnergy()
							 | 
						||
| 
								 | 
							
								        else:
							 | 
						||
| 
								 | 
							
								            nE = getE(13)
							 | 
						||
| 
								 | 
							
								            alreadyInPool += nE
							 | 
						||
| 
								 | 
							
								            nR = getE(3)
							 | 
						||
| 
								 | 
							
								            alreadyInPool += nR
							 | 
						||
| 
								 | 
							
								            for i in range(nR):
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem('Reserve')
							 | 
						||
| 
								 | 
							
								            for i in range(nE):
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem('ETank')
							 | 
						||
| 
								 | 
							
								            for i in range(total - alreadyInPool):
							 | 
						||
| 
								 | 
							
								                self.addNoEnergy()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def getItemPool(self):
							 | 
						||
| 
								 | 
							
								        self.itemManager.newItemPool()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        for itemType in ['ETank', 'Reserve', 'Bomb', 'Charge', 'Ice', 'HiJump', 'SpeedBooster', 'Wave', 'Spazer', 'SpringBall', 'Varia', 'Plasma', 'Grapple', 'Morph', 'Gravity', 'XRayScope', 'SpaceJump', 'ScrewAttack']:
							 | 
						||
| 
								 | 
							
								            self.itemManager.addItem(itemType, 'Major')
							 | 
						||
| 
								 | 
							
								        for itemType in ['Missile', 'Missile', 'Super', 'Super', 'PowerBomb']:
							 | 
						||
| 
								 | 
							
								            self.itemManager.addItem(itemType, 'Minor')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        self.addEnergy()
							 | 
						||
| 
								 | 
							
								        self.addAmmo()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return self.itemManager.getItemPool()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ItemPoolGeneratorScavenger(ItemPoolGeneratorMajors):
							 | 
						||
| 
								 | 
							
								    def __init__(self, itemManager, qty, sm, maxDiff):
							 | 
						||
| 
								 | 
							
								        super(ItemPoolGeneratorScavenger, self).__init__(itemManager, qty, sm, maxDiff)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def addNoEnergy(self):
							 | 
						||
| 
								 | 
							
								        self.itemManager.addItem('Nothing')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ItemPoolGeneratorMinimizer(ItemPoolGeneratorMajors):
							 | 
						||
| 
								 | 
							
								    def __init__(self, itemManager, qty, sm, nLocs, maxDiff):
							 | 
						||
| 
								 | 
							
								        super(ItemPoolGeneratorMinimizer, self).__init__(itemManager, qty, sm, maxDiff)
							 | 
						||
| 
								 | 
							
								        self.maxItems = nLocs
							 | 
						||
| 
								 | 
							
								        self.calcMaxAmmo()
							 | 
						||
| 
								 | 
							
								        nMajors = len([itemName for itemName,item in ItemManager.Items.items() if item.Class == 'Major' and item.Category != 'Energy'])
							 | 
						||
| 
								 | 
							
								        energyQty = self.qty['energy']
							 | 
						||
| 
								 | 
							
								        if energyQty == 'medium':
							 | 
						||
| 
								 | 
							
								            if nLocs < 40:
							 | 
						||
| 
								 | 
							
								                self.maxEnergy = 5
							 | 
						||
| 
								 | 
							
								            elif nLocs < 55:
							 | 
						||
| 
								 | 
							
								                self.maxEnergy = 6
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                self.maxEnergy = 5 + self.mediumRest
							 | 
						||
| 
								 | 
							
								        elif energyQty == 'vanilla':
							 | 
						||
| 
								 | 
							
								            if nLocs < 40:
							 | 
						||
| 
								 | 
							
								                self.maxEnergy = 6
							 | 
						||
| 
								 | 
							
								            elif nLocs < 55:
							 | 
						||
| 
								 | 
							
								                self.maxEnergy = 8
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                self.maxEnergy = 8 + int(float(nLocs - 55)/50.0 * 8)
							 | 
						||
| 
								 | 
							
								            self.log.debug("maxEnergy: "+str(self.maxEnergy))
							 | 
						||
| 
								 | 
							
								            maxItems = self.maxItems - 10 # remove bosses and minimal minore
							 | 
						||
| 
								 | 
							
								            self.maxEnergy = int(max(self.maxEnergy, maxItems - nMajors - self.minorLocations))
							 | 
						||
| 
								 | 
							
								            if self.maxEnergy > 18:
							 | 
						||
| 
								 | 
							
								                self.maxEnergy = 18
							 | 
						||
| 
								 | 
							
								        elif energyQty == 'ultra sparse':
							 | 
						||
| 
								 | 
							
								            self.maxEnergy = 0 if self.ultraSparseNoTanks else 1
							 | 
						||
| 
								 | 
							
								        elif energyQty == 'sparse':
							 | 
						||
| 
								 | 
							
								            self.maxEnergy = 3 + self.sparseRest
							 | 
						||
| 
								 | 
							
								        self.log.debug("maxEnergy: "+str(self.maxEnergy))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ItemPoolGeneratorPlando(ItemPoolGenerator):
							 | 
						||
| 
								 | 
							
								    def __init__(self, itemManager, qty, sm, exclude, nLocs, maxDiff):
							 | 
						||
| 
								 | 
							
								        super(ItemPoolGeneratorPlando, self).__init__(itemManager, qty, sm, maxDiff)
							 | 
						||
| 
								 | 
							
								        # in exclude dict:
							 | 
						||
| 
								 | 
							
								        #   in alreadyPlacedItems:
							 | 
						||
| 
								 | 
							
								        #     dict of 'itemType: count' of items already added in the plando.
							 | 
						||
| 
								 | 
							
								        #     also a 'total: count' with the total number of items already added in the plando.
							 | 
						||
| 
								 | 
							
								        #   in forbiddenItems: list of item forbidden in the pool
							 | 
						||
| 
								 | 
							
								        self.exclude = exclude
							 | 
						||
| 
								 | 
							
								        self.maxItems = nLocs
							 | 
						||
| 
								 | 
							
								        self.log.debug("maxItems: {}".format(self.maxItems))
							 | 
						||
| 
								 | 
							
								        self.log.debug("exclude: {}".format(self.exclude))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def getItemPool(self):
							 | 
						||
| 
								 | 
							
								        exceptionMessage = "Too many items already placed by the plando or not enough available locations:"
							 | 
						||
| 
								 | 
							
								        self.itemManager.newItemPool(addBosses=False)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        # add the already placed items by the plando
							 | 
						||
| 
								 | 
							
								        for item, count in self.exclude['alreadyPlacedItems'].items():
							 | 
						||
| 
								 | 
							
								            if item == 'total':
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            itemClass = 'Major'
							 | 
						||
| 
								 | 
							
								            if item in ['Missile', 'Super', 'PowerBomb', 'Kraid', 'Phantoon', 'Draygon', 'Ridley', 'MotherBrain']:
							 | 
						||
| 
								 | 
							
								                itemClass = 'Minor'
							 | 
						||
| 
								 | 
							
								            for i in range(count):
							 | 
						||
| 
								 | 
							
								                self.itemManager.addItem(item, itemClass)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        remain = self.maxItems - self.exclude['alreadyPlacedItems']['total']
							 | 
						||
| 
								 | 
							
								        self.log.debug("Plando: remain start: {}".format(remain))
							 | 
						||
| 
								 | 
							
								        if remain > 0:
							 | 
						||
| 
								 | 
							
								            # add missing bosses
							 | 
						||
| 
								 | 
							
								            for boss in ['Kraid', 'Phantoon', 'Draygon', 'Ridley', 'MotherBrain']:
							 | 
						||
| 
								 | 
							
								                if self.exclude['alreadyPlacedItems'][boss] == 0:
							 | 
						||
| 
								 | 
							
								                    self.itemManager.addItem(boss, 'Minor')
							 | 
						||
| 
								 | 
							
								                    self.exclude['alreadyPlacedItems'][boss] = 1
							 | 
						||
| 
								 | 
							
								                    remain -= 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            self.log.debug("Plando: remain after bosses: {}".format(remain))
							 | 
						||
| 
								 | 
							
								            if remain < 0:
							 | 
						||
| 
								 | 
							
								                raise Exception("{} can't add the remaining bosses".format(exceptionMessage))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # add missing majors
							 | 
						||
| 
								 | 
							
								            majors = []
							 | 
						||
| 
								 | 
							
								            for itemType in ['Bomb', 'Charge', 'Ice', 'HiJump', 'SpeedBooster', 'Wave', 'Spazer', 'SpringBall', 'Varia', 'Plasma', 'Grapple', 'Morph', 'Gravity', 'XRayScope', 'SpaceJump', 'ScrewAttack']:
							 | 
						||
| 
								 | 
							
								                if self.exclude['alreadyPlacedItems'][itemType] == 0 and itemType not in self.exclude['forbiddenItems']:
							 | 
						||
| 
								 | 
							
								                    self.itemManager.addItem(itemType, 'Major')
							 | 
						||
| 
								 | 
							
								                    self.exclude['alreadyPlacedItems'][itemType] = 1
							 | 
						||
| 
								 | 
							
								                    majors.append(itemType)
							 | 
						||
| 
								 | 
							
								                    remain -= 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            self.log.debug("Plando: remain after majors: {}".format(remain))
							 | 
						||
| 
								 | 
							
								            if remain < 0:
							 | 
						||
| 
								 | 
							
								                raise Exception("{} can't add the remaining majors: {}".format(exceptionMessage, ', '.join(majors)))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # add minimum minors to finish the game
							 | 
						||
| 
								 | 
							
								            for (itemType, minimum) in [('Missile', 3), ('Super', 2), ('PowerBomb', 1)]:
							 | 
						||
| 
								 | 
							
								                while self.exclude['alreadyPlacedItems'][itemType] < minimum and itemType not in self.exclude['forbiddenItems']:
							 | 
						||
| 
								 | 
							
								                    self.itemManager.addItem(itemType, 'Minor')
							 | 
						||
| 
								 | 
							
								                    self.exclude['alreadyPlacedItems'][itemType] += 1
							 | 
						||
| 
								 | 
							
								                    remain -= 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            self.log.debug("Plando: remain after minimum minors: {}".format(remain))
							 | 
						||
| 
								 | 
							
								            if remain < 0:
							 | 
						||
| 
								 | 
							
								                raise Exception("{} can't add the minimum minors to finish the game".format(exceptionMessage))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # add energy
							 | 
						||
| 
								 | 
							
								            energyQty = self.qty['energy']
							 | 
						||
| 
								 | 
							
								            limits = {
							 | 
						||
| 
								 | 
							
								                "sparse": [('ETank', 4), ('Reserve', 1)],
							 | 
						||
| 
								 | 
							
								                "medium": [('ETank', 8), ('Reserve', 2)],
							 | 
						||
| 
								 | 
							
								                "vanilla": [('ETank', 14), ('Reserve', 4)]
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								            for (itemType, minimum) in limits[energyQty]:
							 | 
						||
| 
								 | 
							
								                while self.exclude['alreadyPlacedItems'][itemType] < minimum and itemType not in self.exclude['forbiddenItems']:
							 | 
						||
| 
								 | 
							
								                    self.itemManager.addItem(itemType, 'Major')
							 | 
						||
| 
								 | 
							
								                    self.exclude['alreadyPlacedItems'][itemType] += 1
							 | 
						||
| 
								 | 
							
								                    remain -= 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            self.log.debug("Plando: remain after energy: {}".format(remain))
							 | 
						||
| 
								 | 
							
								            if remain < 0:
							 | 
						||
| 
								 | 
							
								                raise Exception("{} can't add energy".format(exceptionMessage))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # add ammo
							 | 
						||
| 
								 | 
							
								            nbMinorsAlready = self.exclude['alreadyPlacedItems']['Missile'] + self.exclude['alreadyPlacedItems']['Super'] + self.exclude['alreadyPlacedItems']['PowerBomb']
							 | 
						||
| 
								 | 
							
								            minorLocations = max(0, 0.66*self.qty['minors'] - nbMinorsAlready)
							 | 
						||
| 
								 | 
							
								            maxItems = len(self.itemManager.getItemPool()) + int(minorLocations)
							 | 
						||
| 
								 | 
							
								            ammoQty = {itemType: qty for itemType, qty in self.qty['ammo'].items() if itemType not in self.exclude['forbiddenItems']}
							 | 
						||
| 
								 | 
							
								            if ammoQty:
							 | 
						||
| 
								 | 
							
								                rangeDict = getRangeDict(ammoQty)
							 | 
						||
| 
								 | 
							
								                while len(self.itemManager.getItemPool()) < maxItems and remain > 0:
							 | 
						||
| 
								 | 
							
								                    item = chooseFromRange(rangeDict)
							 | 
						||
| 
								 | 
							
								                    self.itemManager.addMinor(item)
							 | 
						||
| 
								 | 
							
								                    remain -= 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            self.log.debug("Plando: remain after ammo: {}".format(remain))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            # add nothing
							 | 
						||
| 
								 | 
							
								            while remain > 0:
							 | 
						||
| 
								 | 
							
								                self.itemManager.addMinor('Nothing')
							 | 
						||
| 
								 | 
							
								                remain -= 1
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            self.log.debug("Plando: remain after nothing: {}".format(remain))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        return self.itemManager.getItemPool()
							 |