| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | from collections import Counter | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  | from dataclasses import dataclass | 
					
						
							| 
									
										
										
										
											2024-07-02 19:32:01 -07:00
										 |  |  | from typing import ClassVar, Dict, Literal, Tuple | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  | from typing_extensions import TypeGuard  # remove when Python >= 3.10 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-02 19:32:01 -07:00
										 |  |  | from Options import Choice, DefaultOnToggle, NamedRange, OptionGroup, PerGameCommonOptions, Range, Removed, Toggle | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-14 06:48:30 -08:00
										 |  |  | from zilliandomizer.options import ( | 
					
						
							|  |  |  |     Options as ZzOptions, char_to_gun, char_to_jump, ID, | 
					
						
							|  |  |  |     VBLR as ZzVBLR, Chars, ItemCounts as ZzItemCounts | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | from zilliandomizer.options.parsing import validate as zz_validate | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-25 00:10:52 +01:00
										 |  |  | class ZillionContinues(NamedRange): | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     number of continues before game over | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     game over teleports you to your ship, keeping items and open doors | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     default = 3 | 
					
						
							|  |  |  |     range_start = 0 | 
					
						
							|  |  |  |     range_end = 21 | 
					
						
							|  |  |  |     display_name = "continues" | 
					
						
							|  |  |  |     special_range_names = { | 
					
						
							|  |  |  |         "vanilla": 3, | 
					
						
							|  |  |  |         "infinity": 21 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ZillionFloppyReq(Range): | 
					
						
							|  |  |  |     """ how many floppy disks are required """ | 
					
						
							|  |  |  |     range_start = 0 | 
					
						
							|  |  |  |     range_end = 8 | 
					
						
							|  |  |  |     default = 5 | 
					
						
							|  |  |  |     display_name = "floppies required" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class VBLR(Choice): | 
					
						
							|  |  |  |     option_vanilla = 0 | 
					
						
							|  |  |  |     option_balanced = 1 | 
					
						
							|  |  |  |     option_low = 2 | 
					
						
							|  |  |  |     option_restrictive = 3 | 
					
						
							|  |  |  |     default = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |     def to_zz_vblr(self) -> ZzVBLR: | 
					
						
							|  |  |  |         def is_vblr(o: str) -> TypeGuard[ZzVBLR]: | 
					
						
							|  |  |  |             """
 | 
					
						
							|  |  |  |             This function is because mypy doesn't support narrowing with `in`, | 
					
						
							|  |  |  |             https://github.com/python/mypy/issues/12535 | 
					
						
							|  |  |  |             so this is the only way I see to get type narrowing to `Literal`. | 
					
						
							|  |  |  |             """
 | 
					
						
							|  |  |  |             return o in ("vanilla", "balanced", "low", "restrictive") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         key = self.current_key | 
					
						
							|  |  |  |         assert is_vblr(key), f"{key=}" | 
					
						
							|  |  |  |         return key | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | class ZillionGunLevels(VBLR): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     Zillion gun power for the number of Zillion power ups you pick up | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     For "restrictive", Champ is the only one that can get Zillion gun power level 3. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     display_name = "gun levels" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ZillionJumpLevels(VBLR): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     jump levels for each character level | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     For "restrictive", Apple is the only one that can get jump level 3. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     display_name = "jump levels" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ZillionRandomizeAlarms(DefaultOnToggle): | 
					
						
							|  |  |  |     """ whether to randomize the locations of alarm sensors """ | 
					
						
							|  |  |  |     display_name = "randomize alarms" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ZillionMaxLevel(Range): | 
					
						
							|  |  |  |     """ the highest level you can get """ | 
					
						
							|  |  |  |     range_start = 3 | 
					
						
							|  |  |  |     range_end = 8 | 
					
						
							|  |  |  |     default = 8 | 
					
						
							|  |  |  |     display_name = "max level" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ZillionOpasPerLevel(Range): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     how many Opa-Opas are required to level up | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Lower makes you level up faster. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     range_start = 1 | 
					
						
							|  |  |  |     range_end = 5 | 
					
						
							|  |  |  |     default = 2 | 
					
						
							|  |  |  |     display_name = "Opa-Opas per level" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ZillionStartChar(Choice): | 
					
						
							|  |  |  |     """ which character you start with """ | 
					
						
							|  |  |  |     option_jj = 0 | 
					
						
							|  |  |  |     option_apple = 1 | 
					
						
							|  |  |  |     option_champ = 2 | 
					
						
							|  |  |  |     display_name = "start character" | 
					
						
							|  |  |  |     default = "random" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-14 06:48:30 -08:00
										 |  |  |     _name_capitalization: ClassVar[Dict[int, Chars]] = { | 
					
						
							|  |  |  |         option_jj: "JJ", | 
					
						
							|  |  |  |         option_apple: "Apple", | 
					
						
							|  |  |  |         option_champ: "Champ", | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_char(self) -> Chars: | 
					
						
							|  |  |  |         return ZillionStartChar._name_capitalization[self.value] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | class ZillionIDCardCount(Range): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     how many ID Cards are in the game | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vanilla is 63 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     maximum total for all items is 144 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     range_start = 0 | 
					
						
							|  |  |  |     range_end = 126 | 
					
						
							|  |  |  |     default = 42 | 
					
						
							|  |  |  |     display_name = "ID Card count" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ZillionBreadCount(Range): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     how many Breads are in the game | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vanilla is 33 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     maximum total for all items is 144 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     range_start = 0 | 
					
						
							|  |  |  |     range_end = 126 | 
					
						
							|  |  |  |     default = 50 | 
					
						
							|  |  |  |     display_name = "Bread count" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ZillionOpaOpaCount(Range): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     how many Opa-Opas are in the game | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vanilla is 26 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     maximum total for all items is 144 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     range_start = 0 | 
					
						
							|  |  |  |     range_end = 126 | 
					
						
							|  |  |  |     default = 26 | 
					
						
							|  |  |  |     display_name = "Opa-Opa count" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ZillionZillionCount(Range): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     how many Zillion gun power ups are in the game | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vanilla is 6 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     maximum total for all items is 144 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     range_start = 0 | 
					
						
							|  |  |  |     range_end = 126 | 
					
						
							|  |  |  |     default = 8 | 
					
						
							|  |  |  |     display_name = "Zillion power up count" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ZillionFloppyDiskCount(Range): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     how many Floppy Disks are in the game | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vanilla is 5 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     maximum total for all items is 144 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     range_start = 0 | 
					
						
							|  |  |  |     range_end = 126 | 
					
						
							|  |  |  |     default = 7 | 
					
						
							|  |  |  |     display_name = "Floppy Disk count" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ZillionScopeCount(Range): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     how many Scopes are in the game | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vanilla is 4 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     maximum total for all items is 144 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     range_start = 0 | 
					
						
							|  |  |  |     range_end = 126 | 
					
						
							|  |  |  |     default = 4 | 
					
						
							|  |  |  |     display_name = "Scope count" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ZillionRedIDCardCount(Range): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     how many Red ID Cards are in the game | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vanilla is 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     maximum total for all items is 144 | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     range_start = 0 | 
					
						
							|  |  |  |     range_end = 126 | 
					
						
							|  |  |  |     default = 2 | 
					
						
							|  |  |  |     display_name = "Red ID Card count" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-16 10:32:33 -06:00
										 |  |  | class ZillionEarlyScope(Toggle): | 
					
						
							|  |  |  |     """ make sure Scope is available early """ | 
					
						
							|  |  |  |     display_name = "early scope" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | class ZillionSkill(Range): | 
					
						
							| 
									
										
										
										
											2024-04-20 17:43:13 -07:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     the difficulty level of the game | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     higher skill: | 
					
						
							|  |  |  |     - can require more precise platforming movement | 
					
						
							|  |  |  |     - lowers your defense | 
					
						
							|  |  |  |     - gives you less time to escape at the end | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |     range_start = 0 | 
					
						
							|  |  |  |     range_end = 5 | 
					
						
							|  |  |  |     default = 2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-25 00:10:52 +01:00
										 |  |  | class ZillionStartingCards(NamedRange): | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     how many ID Cards to start the game with | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Refilling at the ship also ensures you have at least this many cards. | 
					
						
							|  |  |  |     0 gives vanilla behavior. | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     default = 2 | 
					
						
							|  |  |  |     range_start = 0 | 
					
						
							|  |  |  |     range_end = 10 | 
					
						
							|  |  |  |     display_name = "starting cards" | 
					
						
							|  |  |  |     special_range_names = { | 
					
						
							|  |  |  |         "vanilla": 0 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-02 19:32:01 -07:00
										 |  |  | class ZillionMapGen(Choice): | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     - none: vanilla map | 
					
						
							|  |  |  |     - rooms: random terrain inside rooms, but path through base is vanilla | 
					
						
							|  |  |  |     - full: random path through base | 
					
						
							|  |  |  |     """
 | 
					
						
							|  |  |  |     display_name = "map generation" | 
					
						
							|  |  |  |     option_none = 0 | 
					
						
							|  |  |  |     option_rooms = 1 | 
					
						
							|  |  |  |     option_full = 2 | 
					
						
							|  |  |  |     default = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def zz_value(self) -> Literal['none', 'rooms', 'full']: | 
					
						
							|  |  |  |         if self.value == ZillionMapGen.option_none: | 
					
						
							|  |  |  |             return "none" | 
					
						
							|  |  |  |         if self.value == ZillionMapGen.option_rooms: | 
					
						
							|  |  |  |             return "rooms" | 
					
						
							|  |  |  |         assert self.value == ZillionMapGen.option_full | 
					
						
							|  |  |  |         return "full" | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  | @dataclass | 
					
						
							|  |  |  | class ZillionOptions(PerGameCommonOptions): | 
					
						
							|  |  |  |     continues: ZillionContinues | 
					
						
							|  |  |  |     floppy_req: ZillionFloppyReq | 
					
						
							|  |  |  |     gun_levels: ZillionGunLevels | 
					
						
							|  |  |  |     jump_levels: ZillionJumpLevels | 
					
						
							|  |  |  |     randomize_alarms: ZillionRandomizeAlarms | 
					
						
							|  |  |  |     max_level: ZillionMaxLevel | 
					
						
							|  |  |  |     start_char: ZillionStartChar | 
					
						
							|  |  |  |     opas_per_level: ZillionOpasPerLevel | 
					
						
							|  |  |  |     id_card_count: ZillionIDCardCount | 
					
						
							|  |  |  |     bread_count: ZillionBreadCount | 
					
						
							|  |  |  |     opa_opa_count: ZillionOpaOpaCount | 
					
						
							|  |  |  |     zillion_count: ZillionZillionCount | 
					
						
							|  |  |  |     floppy_disk_count: ZillionFloppyDiskCount | 
					
						
							|  |  |  |     scope_count: ZillionScopeCount | 
					
						
							|  |  |  |     red_id_card_count: ZillionRedIDCardCount | 
					
						
							|  |  |  |     early_scope: ZillionEarlyScope | 
					
						
							|  |  |  |     skill: ZillionSkill | 
					
						
							|  |  |  |     starting_cards: ZillionStartingCards | 
					
						
							| 
									
										
										
										
											2024-07-02 19:32:01 -07:00
										 |  |  |     map_gen: ZillionMapGen | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     room_gen: Removed | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-19 11:36:47 -07:00
										 |  |  | z_option_groups = [ | 
					
						
							|  |  |  |     OptionGroup("item counts", [ | 
					
						
							|  |  |  |         ZillionIDCardCount, ZillionBreadCount, ZillionOpaOpaCount, ZillionZillionCount, | 
					
						
							|  |  |  |         ZillionFloppyDiskCount, ZillionScopeCount, ZillionRedIDCardCount | 
					
						
							|  |  |  |     ]) | 
					
						
							|  |  |  | ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | def convert_item_counts(ic: "Counter[str]") -> ZzItemCounts: | 
					
						
							|  |  |  |     tr: ZzItemCounts = { | 
					
						
							|  |  |  |         ID.card: ic["ID Card"], | 
					
						
							|  |  |  |         ID.red: ic["Red ID Card"], | 
					
						
							|  |  |  |         ID.floppy: ic["Floppy Disk"], | 
					
						
							|  |  |  |         ID.bread: ic["Bread"], | 
					
						
							|  |  |  |         ID.gun: ic["Zillion"], | 
					
						
							|  |  |  |         ID.opa: ic["Opa-Opa"], | 
					
						
							|  |  |  |         ID.scope: ic["Scope"], | 
					
						
							|  |  |  |         ID.empty: ic["Empty"], | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return tr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  | def validate(options: ZillionOptions) -> "Tuple[ZzOptions, Counter[str]]": | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |     """
 | 
					
						
							|  |  |  |     adjusts options to make game completion possible | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |     `options` parameter is ZillionOptions object that was put on my world by the core | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |     skill = options.skill.value | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |     jump_option = options.jump_levels.to_zz_vblr() | 
					
						
							|  |  |  |     required_level = char_to_jump["Apple"][jump_option].index(3) + 1 | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |     if skill == 0: | 
					
						
							|  |  |  |         # because of hp logic on final boss | 
					
						
							|  |  |  |         required_level = 8 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |     gun_option = options.gun_levels.to_zz_vblr() | 
					
						
							|  |  |  |     guns_required = char_to_gun["Champ"][gun_option].index(3) | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |     floppy_req = options.floppy_req | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     item_counts = Counter({ | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |         "ID Card": options.id_card_count, | 
					
						
							|  |  |  |         "Bread": options.bread_count, | 
					
						
							|  |  |  |         "Opa-Opa": options.opa_opa_count, | 
					
						
							|  |  |  |         "Zillion": options.zillion_count, | 
					
						
							|  |  |  |         "Floppy Disk": options.floppy_disk_count, | 
					
						
							|  |  |  |         "Scope": options.scope_count, | 
					
						
							|  |  |  |         "Red ID Card": options.red_id_card_count | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |     }) | 
					
						
							|  |  |  |     minimums = Counter({ | 
					
						
							|  |  |  |         "ID Card": 0, | 
					
						
							|  |  |  |         "Bread": 0, | 
					
						
							|  |  |  |         "Opa-Opa": required_level - 1, | 
					
						
							|  |  |  |         "Zillion": guns_required, | 
					
						
							|  |  |  |         "Floppy Disk": floppy_req.value, | 
					
						
							|  |  |  |         "Scope": 0, | 
					
						
							|  |  |  |         "Red ID Card": 1 | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |     for key in minimums: | 
					
						
							|  |  |  |         item_counts[key] = max(minimums[key], item_counts[key]) | 
					
						
							|  |  |  |     max_movables = 144 - sum(minimums.values()) | 
					
						
							|  |  |  |     movables = item_counts - minimums | 
					
						
							|  |  |  |     while sum(movables.values()) > max_movables: | 
					
						
							|  |  |  |         # logging.warning("zillion options validate: player options item counts too high") | 
					
						
							|  |  |  |         total = sum(movables.values()) | 
					
						
							|  |  |  |         scaler = max_movables / total | 
					
						
							|  |  |  |         for key in movables: | 
					
						
							|  |  |  |             movables[key] = int(movables[key] * scaler) | 
					
						
							|  |  |  |     item_counts = movables + minimums | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # now have required items, and <= 144 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # now fill remaining with empty | 
					
						
							|  |  |  |     total = sum(item_counts.values()) | 
					
						
							|  |  |  |     diff = 144 - total | 
					
						
							|  |  |  |     if "Empty" not in item_counts: | 
					
						
							|  |  |  |         item_counts["Empty"] = 0 | 
					
						
							|  |  |  |     item_counts["Empty"] += diff | 
					
						
							|  |  |  |     assert sum(item_counts.values()) == 144 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |     max_level = options.max_level | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |     max_level.value = max(required_level, max_level.value) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |     opas_per_level = options.opas_per_level | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |     while (opas_per_level.value > 1) and (1 + item_counts["Opa-Opa"] // opas_per_level.value < max_level.value): | 
					
						
							|  |  |  |         # logging.warning( | 
					
						
							|  |  |  |         #     "zillion options validate: option opas_per_level incompatible with options max_level and opa_opa_count" | 
					
						
							|  |  |  |         # ) | 
					
						
							|  |  |  |         opas_per_level.value -= 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # that should be all of the level requirements met | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |     starting_cards = options.starting_cards | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-02 19:32:01 -07:00
										 |  |  |     map_gen = options.map_gen.zz_value() | 
					
						
							| 
									
										
										
										
											2022-11-16 10:32:33 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |     zz_item_counts = convert_item_counts(item_counts) | 
					
						
							|  |  |  |     zz_op = ZzOptions( | 
					
						
							|  |  |  |         zz_item_counts, | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |         jump_option, | 
					
						
							|  |  |  |         gun_option, | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |         opas_per_level.value, | 
					
						
							|  |  |  |         max_level.value, | 
					
						
							|  |  |  |         False,  # tutorial | 
					
						
							|  |  |  |         skill, | 
					
						
							| 
									
										
										
										
											2024-01-14 06:48:30 -08:00
										 |  |  |         options.start_char.get_char(), | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |         floppy_req.value, | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |         options.continues.value, | 
					
						
							|  |  |  |         bool(options.randomize_alarms.value), | 
					
						
							|  |  |  |         bool(options.early_scope.value), | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |         True,  # balance defense | 
					
						
							|  |  |  |         starting_cards.value, | 
					
						
							| 
									
										
										
										
											2024-07-02 19:32:01 -07:00
										 |  |  |         map_gen | 
					
						
							| 
									
										
										
										
											2022-10-20 10:41:11 -07:00
										 |  |  |     ) | 
					
						
							|  |  |  |     zz_validate(zz_op) | 
					
						
							|  |  |  |     return zz_op, item_counts |