| 
									
										
										
										
											2023-04-08 16:52:34 -04:00
										 |  |  | from ..utils.utils import randGaussBounds, getRangeDict, chooseFromRange | 
					
						
							|  |  |  | from ..utils import log | 
					
						
							| 
									
										
										
										
											2023-03-25 14:30:38 -04:00
										 |  |  | import logging, copy, random | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Energy Tank", | 
					
						
							|  |  |  |             Type='ETank', | 
					
						
							|  |  |  |             Id=0 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'Missile': Item( | 
					
						
							|  |  |  |             Category='Ammo', | 
					
						
							|  |  |  |             Class='Minor', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Missile", | 
					
						
							|  |  |  |             Type='Missile', | 
					
						
							|  |  |  |             Id=1 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'Super': Item( | 
					
						
							|  |  |  |             Category='Ammo', | 
					
						
							|  |  |  |             Class='Minor', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Super Missile", | 
					
						
							|  |  |  |             Type='Super', | 
					
						
							|  |  |  |             Id=2 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'PowerBomb': Item( | 
					
						
							|  |  |  |             Category='Ammo', | 
					
						
							|  |  |  |             Class='Minor', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Power Bomb", | 
					
						
							|  |  |  |             Type='PowerBomb', | 
					
						
							|  |  |  |             Id=3 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'Bomb': Item( | 
					
						
							|  |  |  |             Category='Progression', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Bomb", | 
					
						
							|  |  |  |             Type='Bomb', | 
					
						
							|  |  |  |             ItemBits=0x1000, | 
					
						
							|  |  |  |             Id=4 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'Charge': Item( | 
					
						
							|  |  |  |             Category='Beam', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Charge Beam", | 
					
						
							|  |  |  |             Type='Charge', | 
					
						
							|  |  |  |             BeamBits=0x1000, | 
					
						
							|  |  |  |             Id=5 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'Ice': Item( | 
					
						
							|  |  |  |             Category='Progression', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Ice Beam", | 
					
						
							|  |  |  |             Type='Ice', | 
					
						
							|  |  |  |             BeamBits=0x2, | 
					
						
							|  |  |  |             Id=6 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'HiJump': Item( | 
					
						
							|  |  |  |             Category='Progression', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Hi-Jump Boots", | 
					
						
							|  |  |  |             Type='HiJump', | 
					
						
							|  |  |  |             ItemBits=0x100, | 
					
						
							|  |  |  |             Id=7 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'SpeedBooster': Item( | 
					
						
							|  |  |  |             Category='Progression', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Speed Booster", | 
					
						
							|  |  |  |             Type='SpeedBooster', | 
					
						
							|  |  |  |             ItemBits=0x2000, | 
					
						
							|  |  |  |             Id=8 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'Wave': Item( | 
					
						
							|  |  |  |             Category='Beam', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Wave Beam", | 
					
						
							|  |  |  |             Type='Wave', | 
					
						
							|  |  |  |             BeamBits=0x1, | 
					
						
							|  |  |  |             Id=9 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'Spazer': Item( | 
					
						
							|  |  |  |             Category='Beam', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Spazer", | 
					
						
							|  |  |  |             Type='Spazer', | 
					
						
							|  |  |  |             BeamBits=0x4, | 
					
						
							|  |  |  |             Id=10 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'SpringBall': Item( | 
					
						
							|  |  |  |             Category='Misc', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Spring Ball", | 
					
						
							|  |  |  |             Type='SpringBall', | 
					
						
							|  |  |  |             ItemBits=0x2, | 
					
						
							|  |  |  |             Id=11 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'Varia': Item( | 
					
						
							|  |  |  |             Category='Progression', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Varia Suit", | 
					
						
							|  |  |  |             Type='Varia', | 
					
						
							|  |  |  |             ItemBits=0x1, | 
					
						
							|  |  |  |             Id=12 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'Plasma': Item( | 
					
						
							|  |  |  |             Category='Beam', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Plasma Beam", | 
					
						
							|  |  |  |             Type='Plasma', | 
					
						
							|  |  |  |             BeamBits=0x8, | 
					
						
							|  |  |  |             Id=15 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'Grapple': Item( | 
					
						
							|  |  |  |             Category='Progression', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Grappling Beam", | 
					
						
							|  |  |  |             Type='Grapple', | 
					
						
							|  |  |  |             ItemBits=0x4000, | 
					
						
							|  |  |  |             Id=16 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'Morph': Item( | 
					
						
							|  |  |  |             Category='Progression', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Morph Ball", | 
					
						
							|  |  |  |             Type='Morph', | 
					
						
							|  |  |  |             ItemBits=0x4, | 
					
						
							|  |  |  |             Id=19 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'Reserve': Item( | 
					
						
							|  |  |  |             Category='Energy', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Reserve Tank", | 
					
						
							|  |  |  |             Type='Reserve', | 
					
						
							|  |  |  |             Id=20 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'Gravity': Item( | 
					
						
							|  |  |  |             Category='Progression', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Gravity Suit", | 
					
						
							|  |  |  |             Type='Gravity', | 
					
						
							|  |  |  |             ItemBits=0x20, | 
					
						
							|  |  |  |             Id=13 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'XRayScope': Item( | 
					
						
							|  |  |  |             Category='Misc', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="X-Ray Scope", | 
					
						
							|  |  |  |             Type='XRayScope', | 
					
						
							|  |  |  |             ItemBits=0x8000, | 
					
						
							|  |  |  |             Id=14 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'SpaceJump': Item( | 
					
						
							|  |  |  |             Category='Progression', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             Name="Space Jump", | 
					
						
							|  |  |  |             Type='SpaceJump', | 
					
						
							|  |  |  |             ItemBits=0x200, | 
					
						
							|  |  |  |             Id=17 | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'ScrewAttack': Item( | 
					
						
							|  |  |  |             Category='Misc', | 
					
						
							|  |  |  |             Class='Major', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             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", | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Type='Phantoon', | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         ), | 
					
						
							|  |  |  |         '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', | 
					
						
							|  |  |  |         ), | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |         'SporeSpawn': Item( | 
					
						
							|  |  |  |             Category='MiniBoss', | 
					
						
							|  |  |  |             Class='Boss', | 
					
						
							|  |  |  |             Name="Spore Spawn", | 
					
						
							|  |  |  |             Type='SporeSpawn', | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'Crocomire': Item( | 
					
						
							|  |  |  |             Category='MiniBoss', | 
					
						
							|  |  |  |             Class='Boss', | 
					
						
							|  |  |  |             Name="Crocomire", | 
					
						
							|  |  |  |             Type='Crocomire', | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'Botwoon': Item( | 
					
						
							|  |  |  |             Category='MiniBoss', | 
					
						
							|  |  |  |             Class='Boss', | 
					
						
							|  |  |  |             Name="Botwoon", | 
					
						
							|  |  |  |             Type='Botwoon', | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         'GoldenTorizo': Item( | 
					
						
							|  |  |  |             Category='MiniBoss', | 
					
						
							|  |  |  |             Class='Boss', | 
					
						
							|  |  |  |             Name="Golden Torizo", | 
					
						
							|  |  |  |             Type='GoldenTorizo', | 
					
						
							|  |  |  |         ), | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         # 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', | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             Code=0xfc20, | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |     def __init__(self, majorsSplit, qty, sm, nLocs, bossesItems, maxDiff): | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         self.qty = qty | 
					
						
							|  |  |  |         self.sm = sm | 
					
						
							|  |  |  |         self.majorsSplit = majorsSplit | 
					
						
							|  |  |  |         self.nLocs = nLocs | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |         self.bossesItems = bossesItems | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             for boss in self.bossesItems: | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |                 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): | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |     # 100 item locs, 5 bosses, 4 mini bosses | 
					
						
							|  |  |  |     maxLocs = 109 | 
					
						
							|  |  |  |     nbBosses = 9 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |     @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) | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |         elif nLocs == ItemPoolGenerator.maxLocs: | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             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 | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |         self.maxItems = ItemPoolGenerator.maxLocs | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         self.maxEnergy = 18 # 14E, 4R | 
					
						
							|  |  |  |         self.maxDiff = maxDiff | 
					
						
							| 
									
										
										
										
											2023-03-25 14:30:38 -04:00
										 |  |  |         self.log = log.get('ItemPool') | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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: | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             self.maxMinors = 0.66*(self.maxItems - ItemPoolGenerator.nbBosses) | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         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)) | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             # remove bosses and minimal minors | 
					
						
							|  |  |  |             maxItems = self.maxItems - (self.nbMinorsAlready + len(self.itemManager.bossesItems)) | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             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' | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             if item in ['Missile', 'Super', 'PowerBomb', 'Kraid', 'Phantoon', 'Draygon', 'Ridley', 'MotherBrain', 'SporeSpawn', 'Crocomire', 'Botwoon', 'GoldenTorizo']: | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |                 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 | 
					
						
							| 
									
										
										
										
											2023-04-09 18:35:46 -04:00
										 |  |  |             for boss in self.itemManager.bossesItems: | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |                 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() |