| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  | import logging | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from BaseClasses import ItemClassification, Location, Item | 
					
						
							|  |  |  | from .data import iname, rname | 
					
						
							|  |  |  | from .options import CV64Options, BackgroundMusic, Countdown, IceTrapAppearance, InvisibleItems, CharacterStages | 
					
						
							|  |  |  | from .stages import vanilla_stage_order, get_stage_info | 
					
						
							|  |  |  | from .locations import get_location_info, base_id | 
					
						
							|  |  |  | from .regions import get_region_info | 
					
						
							|  |  |  | from .items import get_item_info, item_info | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from typing import TYPE_CHECKING, Dict, List, Tuple, Union, Iterable | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if TYPE_CHECKING: | 
					
						
							|  |  |  |     from . import CV64World | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | rom_sub_weapon_offsets = { | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |     0x10C6EB: (b"\x10", rname.forest_of_silence),  # Forest | 
					
						
							|  |  |  |     0x10C6F3: (b"\x0F", rname.forest_of_silence), | 
					
						
							|  |  |  |     0x10C6FB: (b"\x0E", rname.forest_of_silence), | 
					
						
							|  |  |  |     0x10C703: (b"\x0D", rname.forest_of_silence), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x10C81F: (b"\x0F", rname.castle_wall),  # Castle Wall | 
					
						
							|  |  |  |     0x10C827: (b"\x10", rname.castle_wall), | 
					
						
							|  |  |  |     0x10C82F: (b"\x0E", rname.castle_wall), | 
					
						
							|  |  |  |     0x7F9A0F: (b"\x0D", rname.castle_wall), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x83A5D9: (b"\x0E", rname.villa),  # Villa | 
					
						
							|  |  |  |     0x83A5E5: (b"\x0D", rname.villa), | 
					
						
							|  |  |  |     0x83A5F1: (b"\x0F", rname.villa), | 
					
						
							|  |  |  |     0xBFC903: (b"\x10", rname.villa), | 
					
						
							|  |  |  |     0x10C987: (b"\x10", rname.villa), | 
					
						
							|  |  |  |     0x10C98F: (b"\x0D", rname.villa), | 
					
						
							|  |  |  |     0x10C997: (b"\x0F", rname.villa), | 
					
						
							|  |  |  |     0x10CF73: (b"\x10", rname.villa), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x10CA57: (b"\x0D", rname.tunnel),  # Tunnel | 
					
						
							|  |  |  |     0x10CA5F: (b"\x0E", rname.tunnel), | 
					
						
							|  |  |  |     0x10CA67: (b"\x10", rname.tunnel), | 
					
						
							|  |  |  |     0x10CA6F: (b"\x0D", rname.tunnel), | 
					
						
							|  |  |  |     0x10CA77: (b"\x0F", rname.tunnel), | 
					
						
							|  |  |  |     0x10CA7F: (b"\x0E", rname.tunnel), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x10CBC7: (b"\x0E", rname.castle_center),  # Castle Center | 
					
						
							|  |  |  |     0x10CC0F: (b"\x0D", rname.castle_center), | 
					
						
							|  |  |  |     0x10CC5B: (b"\x0F", rname.castle_center), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x10CD3F: (b"\x0E", rname.tower_of_execution),  # Character towers | 
					
						
							|  |  |  |     0x10CD65: (b"\x0D", rname.tower_of_execution), | 
					
						
							|  |  |  |     0x10CE2B: (b"\x0E", rname.tower_of_science), | 
					
						
							|  |  |  |     0x10CE83: (b"\x10", rname.duel_tower), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x10CF8B: (b"\x0F", rname.room_of_clocks),  # Room of Clocks | 
					
						
							|  |  |  |     0x10CF93: (b"\x0D", rname.room_of_clocks), | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x99BC5A: (b"\x0D", rname.clock_tower),  # Clock Tower | 
					
						
							|  |  |  |     0x10CECB: (b"\x10", rname.clock_tower), | 
					
						
							|  |  |  |     0x10CED3: (b"\x0F", rname.clock_tower), | 
					
						
							|  |  |  |     0x10CEDB: (b"\x0E", rname.clock_tower), | 
					
						
							|  |  |  |     0x10CEE3: (b"\x0D", rname.clock_tower), | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | rom_sub_weapon_flags = { | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |     0x10C6EC: b"\x02\x00\xFF\x04",  # Forest of Silence | 
					
						
							|  |  |  |     0x10C6FC: b"\x04\x00\xFF\x04", | 
					
						
							|  |  |  |     0x10C6F4: b"\x08\x00\xFF\x04", | 
					
						
							|  |  |  |     0x10C704: b"\x40\x00\xFF\x04", | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |     0x10C831: b"\x08",  # Castle Wall | 
					
						
							|  |  |  |     0x10C829: b"\x10", | 
					
						
							|  |  |  |     0x10C821: b"\x20", | 
					
						
							|  |  |  |     0xBFCA97: b"\x04", | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Villa | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |     0xBFC926: b"\xFF\x04", | 
					
						
							|  |  |  |     0xBFC93A: b"\x80", | 
					
						
							|  |  |  |     0xBFC93F: b"\x01", | 
					
						
							|  |  |  |     0xBFC943: b"\x40", | 
					
						
							|  |  |  |     0xBFC947: b"\x80", | 
					
						
							|  |  |  |     0x10C989: b"\x10", | 
					
						
							|  |  |  |     0x10C991: b"\x20", | 
					
						
							|  |  |  |     0x10C999: b"\x40", | 
					
						
							|  |  |  |     0x10CF77: b"\x80", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x10CA58: b"\x40\x00\xFF\x0E",  # Tunnel | 
					
						
							|  |  |  |     0x10CA6B: b"\x80", | 
					
						
							|  |  |  |     0x10CA60: b"\x10\x00\xFF\x05", | 
					
						
							|  |  |  |     0x10CA70: b"\x20\x00\xFF\x05", | 
					
						
							|  |  |  |     0x10CA78: b"\x40\x00\xFF\x05", | 
					
						
							|  |  |  |     0x10CA80: b"\x80\x00\xFF\x05", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x10CBCA: b"\x02",  # Castle Center | 
					
						
							|  |  |  |     0x10CC10: b"\x80", | 
					
						
							|  |  |  |     0x10CC5C: b"\x40", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x10CE86: b"\x01",  # Duel Tower | 
					
						
							|  |  |  |     0x10CD43: b"\x02",  # Tower of Execution | 
					
						
							|  |  |  |     0x10CE2E: b"\x20",  # Tower of Science | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x10CF8E: b"\x04",  # Room of Clocks | 
					
						
							|  |  |  |     0x10CF96: b"\x08", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x10CECE: b"\x08",  # Clock Tower | 
					
						
							|  |  |  |     0x10CED6: b"\x10", | 
					
						
							|  |  |  |     0x10CEE6: b"\x20", | 
					
						
							|  |  |  |     0x10CEDE: b"\x80", | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | rom_empty_breakables_flags = { | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |     0x10C74D: b"\x40\xFF\x05",  # Forest of Silence | 
					
						
							|  |  |  |     0x10C765: b"\x20\xFF\x0E", | 
					
						
							|  |  |  |     0x10C774: b"\x08\x00\xFF\x0E", | 
					
						
							|  |  |  |     0x10C755: b"\x80\xFF\x05", | 
					
						
							|  |  |  |     0x10C784: b"\x01\x00\xFF\x0E", | 
					
						
							|  |  |  |     0x10C73C: b"\x02\x00\xFF\x0E", | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x10C8D0: b"\x04\x00\xFF\x0E",  # Villa foyer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x10CF9F: b"\x08",  # Room of Clocks flags | 
					
						
							|  |  |  |     0x10CFA7: b"\x01", | 
					
						
							|  |  |  |     0xBFCB6F: b"\x04",  # Room of Clocks candle property IDs | 
					
						
							|  |  |  |     0xBFCB73: b"\x05", | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | rom_axe_cross_lower_values = { | 
					
						
							|  |  |  |     0x6: [0x7C7F97, 0x07],  # Forest | 
					
						
							|  |  |  |     0x8: [0x7C7FA6, 0xF9], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x30: [0x83A60A, 0x71],  # Villa hallway | 
					
						
							|  |  |  |     0x27: [0x83A617, 0x26], | 
					
						
							|  |  |  |     0x2C: [0x83A624, 0x6E], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x16C: [0x850FE6, 0x07],  # Villa maze | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x10A: [0x8C44D3, 0x08],  # CC factory floor | 
					
						
							|  |  |  |     0x109: [0x8C44E1, 0x08], | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     0x74: [0x8DF77C, 0x07],  # CC invention area | 
					
						
							|  |  |  |     0x60: [0x90FD37, 0x43], | 
					
						
							|  |  |  |     0x55: [0xBFCC2B, 0x43], | 
					
						
							|  |  |  |     0x65: [0x90FBA1, 0x51], | 
					
						
							|  |  |  |     0x64: [0x90FBAD, 0x50], | 
					
						
							|  |  |  |     0x61: [0x90FE56, 0x43] | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | rom_looping_music_fade_ins = { | 
					
						
							|  |  |  |     0x10: None, | 
					
						
							|  |  |  |     0x11: None, | 
					
						
							|  |  |  |     0x12: None, | 
					
						
							|  |  |  |     0x13: None, | 
					
						
							|  |  |  |     0x14: None, | 
					
						
							|  |  |  |     0x15: None, | 
					
						
							|  |  |  |     0x16: 0x17, | 
					
						
							|  |  |  |     0x18: 0x19, | 
					
						
							|  |  |  |     0x1A: 0x1B, | 
					
						
							|  |  |  |     0x21: 0x75, | 
					
						
							|  |  |  |     0x27: None, | 
					
						
							|  |  |  |     0x2E: 0x23, | 
					
						
							|  |  |  |     0x39: None, | 
					
						
							|  |  |  |     0x45: 0x63, | 
					
						
							|  |  |  |     0x56: None, | 
					
						
							|  |  |  |     0x57: 0x58, | 
					
						
							|  |  |  |     0x59: None, | 
					
						
							|  |  |  |     0x5A: None, | 
					
						
							|  |  |  |     0x5B: 0x5C, | 
					
						
							|  |  |  |     0x5D: None, | 
					
						
							|  |  |  |     0x5E: None, | 
					
						
							|  |  |  |     0x5F: None, | 
					
						
							|  |  |  |     0x60: 0x61, | 
					
						
							|  |  |  |     0x62: None, | 
					
						
							|  |  |  |     0x64: None, | 
					
						
							|  |  |  |     0x65: None, | 
					
						
							|  |  |  |     0x66: None, | 
					
						
							|  |  |  |     0x68: None, | 
					
						
							|  |  |  |     0x69: None, | 
					
						
							|  |  |  |     0x6D: 0x78, | 
					
						
							|  |  |  |     0x6E: None, | 
					
						
							|  |  |  |     0x6F: None, | 
					
						
							|  |  |  |     0x73: None, | 
					
						
							|  |  |  |     0x74: None, | 
					
						
							|  |  |  |     0x77: None, | 
					
						
							|  |  |  |     0x79: None | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | music_sfx_ids = [0x1C, 0x4B, 0x4C, 0x4D, 0x4E, 0x55, 0x6C, 0x76] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | renon_item_dialogue = { | 
					
						
							|  |  |  |     0x02: "More Sub-weapon uses!\n" | 
					
						
							|  |  |  |           "Just what you need!", | 
					
						
							|  |  |  |     0x03: "Galamoth told me it's\n" | 
					
						
							|  |  |  |           "a heart in other times.", | 
					
						
							|  |  |  |     0x04: "Who needs Warp Rooms\n" | 
					
						
							|  |  |  |           "when you have these?", | 
					
						
							|  |  |  |     0x05: "I was told to safeguard\n" | 
					
						
							|  |  |  |           "this, but I dunno why.", | 
					
						
							|  |  |  |     0x06: "Fresh off a Behemoth!\n" | 
					
						
							|  |  |  |           "Those cows are weird.", | 
					
						
							|  |  |  |     0x07: "Preserved with special\n" | 
					
						
							|  |  |  |           " wall-based methods.", | 
					
						
							|  |  |  |     0x08: "Don't tell Geneva\n" | 
					
						
							|  |  |  |           "about this...", | 
					
						
							|  |  |  |     0x09: "If this existed in 1094,\n" | 
					
						
							|  |  |  |           "that whip wouldn't...", | 
					
						
							|  |  |  |     0x0A: "For when some lizard\n" | 
					
						
							|  |  |  |           "brain spits on your ego.", | 
					
						
							|  |  |  |     0x0C: "It'd be a shame if you\n" | 
					
						
							|  |  |  |           "lost it immediately...", | 
					
						
							|  |  |  |     0x10C: "No consequences should\n" | 
					
						
							|  |  |  |            "you perish with this!", | 
					
						
							|  |  |  |     0x0D: "Arthur was far better\n" | 
					
						
							|  |  |  |           "with it than you!", | 
					
						
							|  |  |  |     0x0E: "Night Creatures handle\n" | 
					
						
							|  |  |  |           "with care!", | 
					
						
							|  |  |  |     0x0F: "Some may call it a\n" | 
					
						
							|  |  |  |           "\"Banshee Boomerang.\"", | 
					
						
							|  |  |  |     0x10: "No weapon triangle\n" | 
					
						
							|  |  |  |           "advantages with this.", | 
					
						
							|  |  |  |     0x12: "It looks sus? Trust me," | 
					
						
							|  |  |  |           "my wares are genuine.", | 
					
						
							|  |  |  |     0x15: "This non-volatile kind\n" | 
					
						
							|  |  |  |           "is safe to handle.", | 
					
						
							|  |  |  |     0x16: "If you can soul-wield,\n" | 
					
						
							|  |  |  |           "they have a good one!", | 
					
						
							|  |  |  |     0x17: "Calls the morning sun\n" | 
					
						
							|  |  |  |           "to vanquish the night.", | 
					
						
							|  |  |  |     0x18: "1 on-demand horrible\n" | 
					
						
							|  |  |  |           "night. Devils love it!", | 
					
						
							|  |  |  |     0x1A: "Want to study here?\n" | 
					
						
							|  |  |  |           "It will cost you.", | 
					
						
							|  |  |  |     0x1B: "\"Let them eat cake!\"\n" | 
					
						
							|  |  |  |           "Said no princess ever.", | 
					
						
							|  |  |  |     0x1C: "Why do I suspect this\n" | 
					
						
							|  |  |  |           "was a toilet room?", | 
					
						
							|  |  |  |     0x1D: "When you see Coller,\n" | 
					
						
							|  |  |  |           "tell him I said hi!", | 
					
						
							|  |  |  |     0x1E: "Atomic number is 29\n" | 
					
						
							|  |  |  |           "and weight is 63.546.", | 
					
						
							|  |  |  |     0x1F: "One torture per pay!\n" | 
					
						
							|  |  |  |           "Who will it be?", | 
					
						
							|  |  |  |     0x20: "Being here feels like\n" | 
					
						
							|  |  |  |           "time is slowing down.", | 
					
						
							|  |  |  |     0x21: "Only one thing beind\n" | 
					
						
							|  |  |  |           "this. Do you dare?", | 
					
						
							|  |  |  |     0x22: "The key 2 Science!\n" | 
					
						
							|  |  |  |           "Both halves of it!", | 
					
						
							|  |  |  |     0x23: "This warehouse can\n" | 
					
						
							|  |  |  |           "be yours for a fee.", | 
					
						
							|  |  |  |     0x24: "Long road ahead if you\n" | 
					
						
							|  |  |  |           "don't have the others.", | 
					
						
							|  |  |  |     0x25: "Will you get the curse\n" | 
					
						
							|  |  |  |           "of eternal burning?", | 
					
						
							|  |  |  |     0x26: "What's beyond time?\n" | 
					
						
							|  |  |  |           "Find out your", | 
					
						
							|  |  |  |     0x27: "Want to take out a\n" | 
					
						
							|  |  |  |           "loan? By all means!", | 
					
						
							|  |  |  |     0x28: "The bag is green,\n" | 
					
						
							|  |  |  |           "so it must be lucky!", | 
					
						
							|  |  |  |     0x29: "(Does this fool realize?)\n" | 
					
						
							|  |  |  |           "Oh, sorry.", | 
					
						
							|  |  |  |     "prog": "They will absolutely\n" | 
					
						
							|  |  |  |             "need it in time!", | 
					
						
							|  |  |  |     "useful": "Now, this would be\n" | 
					
						
							|  |  |  |               "useful to send...", | 
					
						
							|  |  |  |     "common": "Every last little bit\n" | 
					
						
							|  |  |  |               "helps, right?", | 
					
						
							|  |  |  |     "trap": "I'll teach this fool\n" | 
					
						
							|  |  |  |             " a lesson for a price!", | 
					
						
							|  |  |  |     "dlc coin": "1 coin out of... wha!?\n" | 
					
						
							|  |  |  |                 "You imp, why I oughta!" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  | def randomize_lighting(world: "CV64World") -> Dict[int, bytes]: | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |     """Generates randomized data for the map lighting table.""" | 
					
						
							|  |  |  |     randomized_lighting = {} | 
					
						
							|  |  |  |     for entry in range(67): | 
					
						
							|  |  |  |         for sub_entry in range(19): | 
					
						
							|  |  |  |             if sub_entry not in [3, 7, 11, 15] and entry != 4: | 
					
						
							|  |  |  |                 # The fourth entry in the lighting table affects the lighting on some item pickups; skip it | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |                 randomized_lighting[0x1091A0 + (entry * 28) + sub_entry] = bytes([world.random.randint(0, 255)]) | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |     return randomized_lighting | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  | def shuffle_sub_weapons(world: "CV64World") -> Dict[int, bytes]: | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |     """Shuffles the sub-weapons amongst themselves.""" | 
					
						
							|  |  |  |     sub_weapon_dict = {offset: rom_sub_weapon_offsets[offset][0] for offset in rom_sub_weapon_offsets if | 
					
						
							|  |  |  |                        rom_sub_weapon_offsets[offset][1] in world.active_stage_exits} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Remove the one 3HB sub-weapon in Tower of Execution if 3HBs are not shuffled. | 
					
						
							|  |  |  |     if not world.options.multi_hit_breakables and 0x10CD65 in sub_weapon_dict: | 
					
						
							|  |  |  |         del (sub_weapon_dict[0x10CD65]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sub_bytes = list(sub_weapon_dict.values()) | 
					
						
							|  |  |  |     world.random.shuffle(sub_bytes) | 
					
						
							|  |  |  |     return dict(zip(sub_weapon_dict, sub_bytes)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  | def randomize_music(world: "CV64World") -> Dict[int, bytes]: | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |     """Generates randomized or disabled data for all the music in the game.""" | 
					
						
							|  |  |  |     music_array = bytearray(0x7A) | 
					
						
							|  |  |  |     for number in music_sfx_ids: | 
					
						
							|  |  |  |         music_array[number] = number | 
					
						
							|  |  |  |     if world.options.background_music == BackgroundMusic.option_randomized: | 
					
						
							|  |  |  |         looping_songs = [] | 
					
						
							|  |  |  |         non_looping_songs = [] | 
					
						
							|  |  |  |         fade_in_songs = {} | 
					
						
							|  |  |  |         # Create shuffle-able lists of all the looping, non-looping, and fade-in track IDs | 
					
						
							|  |  |  |         for i in range(0x10, len(music_array)): | 
					
						
							|  |  |  |             if i not in rom_looping_music_fade_ins.keys() and i not in rom_looping_music_fade_ins.values() and \ | 
					
						
							|  |  |  |                     i != 0x72:  # Credits song is blacklisted | 
					
						
							|  |  |  |                 non_looping_songs.append(i) | 
					
						
							|  |  |  |             elif i in rom_looping_music_fade_ins.keys(): | 
					
						
							|  |  |  |                 looping_songs.append(i) | 
					
						
							|  |  |  |             elif i in rom_looping_music_fade_ins.values(): | 
					
						
							|  |  |  |                 fade_in_songs[i] = i | 
					
						
							|  |  |  |         # Shuffle the looping songs | 
					
						
							|  |  |  |         rando_looping_songs = looping_songs.copy() | 
					
						
							|  |  |  |         world.random.shuffle(rando_looping_songs) | 
					
						
							|  |  |  |         looping_songs = dict(zip(looping_songs, rando_looping_songs)) | 
					
						
							|  |  |  |         # Shuffle the non-looping songs | 
					
						
							|  |  |  |         rando_non_looping_songs = non_looping_songs.copy() | 
					
						
							|  |  |  |         world.random.shuffle(rando_non_looping_songs) | 
					
						
							|  |  |  |         non_looping_songs = dict(zip(non_looping_songs, rando_non_looping_songs)) | 
					
						
							|  |  |  |         non_looping_songs[0x72] = 0x72 | 
					
						
							|  |  |  |         # Figure out the new fade-in songs if applicable | 
					
						
							|  |  |  |         for vanilla_song in looping_songs: | 
					
						
							|  |  |  |             if rom_looping_music_fade_ins[vanilla_song]: | 
					
						
							|  |  |  |                 if rom_looping_music_fade_ins[looping_songs[vanilla_song]]: | 
					
						
							|  |  |  |                     fade_in_songs[rom_looping_music_fade_ins[vanilla_song]] = rom_looping_music_fade_ins[ | 
					
						
							|  |  |  |                         looping_songs[vanilla_song]] | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     fade_in_songs[rom_looping_music_fade_ins[vanilla_song]] = looping_songs[vanilla_song] | 
					
						
							|  |  |  |         # Build the new music array | 
					
						
							|  |  |  |         for i in range(0x10, len(music_array)): | 
					
						
							|  |  |  |             if i in looping_songs.keys(): | 
					
						
							|  |  |  |                 music_array[i] = looping_songs[i] | 
					
						
							|  |  |  |             elif i in non_looping_songs.keys(): | 
					
						
							|  |  |  |                 music_array[i] = non_looping_songs[i] | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 music_array[i] = fade_in_songs[i] | 
					
						
							|  |  |  |     del (music_array[0x00: 0x10]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |     return {0xBFCD30: bytes(music_array)} | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  | def randomize_shop_prices(world: "CV64World") -> Dict[int, bytes]: | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |     """Randomize the shop prices based on the minimum and maximum values chosen.
 | 
					
						
							|  |  |  |     The minimum price will adjust if it's higher than the max.""" | 
					
						
							|  |  |  |     min_price = world.options.minimum_gold_price.value | 
					
						
							|  |  |  |     max_price = world.options.maximum_gold_price.value | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if min_price > max_price: | 
					
						
							|  |  |  |         min_price = world.random.randint(0, max_price) | 
					
						
							|  |  |  |         logging.warning(f"[{world.multiworld.player_name[world.player]}] The Minimum Gold Price " | 
					
						
							|  |  |  |                         f"({world.options.minimum_gold_price.value * 100}) is higher than the " | 
					
						
							|  |  |  |                         f"Maximum Gold Price ({max_price * 100}). Lowering the minimum to: {min_price * 100}") | 
					
						
							|  |  |  |         world.options.minimum_gold_price.value = min_price | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     shop_price_list = [world.random.randint(min_price * 100, max_price * 100) for _ in range(7)] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |     # Convert the price list into a data dict. | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |     price_dict = {} | 
					
						
							|  |  |  |     for i in range(len(shop_price_list)): | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |         price_dict[0x103D6C + (i * 12)] = int.to_bytes(shop_price_list[i], 4, "big") | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return price_dict | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  | def get_countdown_numbers(options: CV64Options, active_locations: Iterable[Location]) -> Dict[int, bytes]: | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |     """Figures out which Countdown numbers to increase for each Location after verifying the Item on the Location should
 | 
					
						
							|  |  |  |     increase a number. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     First, check the location's info to see if it has a countdown number override. | 
					
						
							|  |  |  |     If not, then figure it out based on the parent region's stage's position in the vanilla stage order. | 
					
						
							|  |  |  |     If the parent region is not part of any stage (as is the case for Renon's shop), skip the location entirely.""" | 
					
						
							|  |  |  |     countdown_list = [0 for _ in range(15)] | 
					
						
							|  |  |  |     for loc in active_locations: | 
					
						
							|  |  |  |         if loc.address is not None and (options.countdown == Countdown.option_all_locations or | 
					
						
							|  |  |  |                                         (options.countdown == Countdown.option_majors | 
					
						
							|  |  |  |                                          and loc.item.advancement)): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             countdown_number = get_location_info(loc.name, "countdown") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if countdown_number is None: | 
					
						
							|  |  |  |                 stage = get_region_info(loc.parent_region.name, "stage") | 
					
						
							|  |  |  |                 if stage is not None: | 
					
						
							|  |  |  |                     countdown_number = vanilla_stage_order.index(stage) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if countdown_number is not None: | 
					
						
							|  |  |  |                 countdown_list[countdown_number] += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |     return {0xBFD818: bytes(countdown_list)} | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_location_data(world: "CV64World", active_locations: Iterable[Location]) \ | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |         -> Tuple[Dict[int, bytes], List[str], List[bytearray], List[List[Union[int, str, None]]]]: | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |     """Gets ALL the item data to go into the ROM. Item data consists of two bytes: the first dictates the appearance of
 | 
					
						
							|  |  |  |     the item, the second determines what the item actually is when picked up. All items from other worlds will be AP | 
					
						
							|  |  |  |     items that do nothing when picked up other than set their flag, and their appearance will depend on whether it's | 
					
						
							|  |  |  |     another CV64 player's item and, if so, what item it is in their game. Ice Traps can assume the form of any item that | 
					
						
							|  |  |  |     is progression, non-progression, or either depending on the player's settings. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Appearance does not matter if it's one of the two NPC-given items (from either Vincent or Heinrich Meyer). For | 
					
						
							|  |  |  |     Renon's shop items, a list containing the shop item names, descriptions, and colors will be returned alongside the | 
					
						
							|  |  |  |     regular data."""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Figure out the list of possible Ice Trap appearances to use based on the settings, first and foremost. | 
					
						
							|  |  |  |     if world.options.ice_trap_appearance == IceTrapAppearance.option_major_only: | 
					
						
							|  |  |  |         allowed_classifications = ["progression", "progression skip balancing"] | 
					
						
							|  |  |  |     elif world.options.ice_trap_appearance == IceTrapAppearance.option_junk_only: | 
					
						
							|  |  |  |         allowed_classifications = ["filler", "useful"] | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         allowed_classifications = ["progression", "progression skip balancing", "filler", "useful"] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     trap_appearances = [] | 
					
						
							|  |  |  |     for item in item_info: | 
					
						
							|  |  |  |         if item_info[item]["default classification"] in allowed_classifications and item != "Ice Trap" and \ | 
					
						
							|  |  |  |                 get_item_info(item, "code") is not None: | 
					
						
							|  |  |  |             trap_appearances.append(item) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     shop_name_list = [] | 
					
						
							|  |  |  |     shop_desc_list = [] | 
					
						
							|  |  |  |     shop_colors_list = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     location_bytes = {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for loc in active_locations: | 
					
						
							|  |  |  |         # If the Location is an event, skip it. | 
					
						
							|  |  |  |         if loc.address is None: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         loc_type = get_location_info(loc.name, "type") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Figure out the item ID bytes to put in each Location here. Write the item itself if either it's the player's | 
					
						
							|  |  |  |         # very own, or it belongs to an Item Link that the player is a part of. | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |         if loc.item.player == world.player: | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |             if loc_type not in ["npc", "shop"] and get_item_info(loc.item.name, "pickup actor id") is not None: | 
					
						
							|  |  |  |                 location_bytes[get_location_info(loc.name, "offset")] = get_item_info(loc.item.name, "pickup actor id") | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |                 location_bytes[get_location_info(loc.name, "offset")] = get_item_info(loc.item.name, "code") & 0xFF | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |         else: | 
					
						
							|  |  |  |             # Make the item the unused Wooden Stake - our multiworld item. | 
					
						
							|  |  |  |             location_bytes[get_location_info(loc.name, "offset")] = 0x11 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Figure out the item's appearance. If it's a CV64 player's item, change the multiworld item's model to | 
					
						
							|  |  |  |         # match what it is. Otherwise, change it to an Archipelago progress or not progress icon. The model "change" | 
					
						
							|  |  |  |         # has to be applied to even local items because this is how the game knows to count it on the Countdown. | 
					
						
							|  |  |  |         if loc.item.game == "Castlevania 64": | 
					
						
							|  |  |  |             location_bytes[get_location_info(loc.name, "offset") - 1] = get_item_info(loc.item.name, "code") | 
					
						
							|  |  |  |         elif loc.item.advancement: | 
					
						
							|  |  |  |             location_bytes[get_location_info(loc.name, "offset") - 1] = 0x11  # Wooden Stakes are majors | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             location_bytes[get_location_info(loc.name, "offset") - 1] = 0x12  # Roses are minors | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # If it's a PermaUp, change the item's model to a big PowerUp no matter what. | 
					
						
							|  |  |  |         if loc.item.game == "Castlevania 64" and loc.item.code == 0x10C + base_id: | 
					
						
							|  |  |  |             location_bytes[get_location_info(loc.name, "offset") - 1] = 0x0B | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # If it's an Ice Trap, change its model to one of the appearances we determined before. | 
					
						
							|  |  |  |         # Unless it's an NPC item, in which case use the Ice Trap's regular ID so that it won't decrement the majors | 
					
						
							|  |  |  |         # Countdown due to how I set up the NPC items to work. | 
					
						
							|  |  |  |         if loc.item.game == "Castlevania 64" and loc.item.code == 0x12 + base_id: | 
					
						
							|  |  |  |             if loc_type == "npc": | 
					
						
							|  |  |  |                 location_bytes[get_location_info(loc.name, "offset") - 1] = 0x12 | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 location_bytes[get_location_info(loc.name, "offset") - 1] = \ | 
					
						
							|  |  |  |                     get_item_info(world.random.choice(trap_appearances), "code") | 
					
						
							|  |  |  |                 # If we chose a PermaUp as our trap appearance, change it to its actual in-game ID of 0x0B. | 
					
						
							|  |  |  |                 if location_bytes[get_location_info(loc.name, "offset") - 1] == 0x10C: | 
					
						
							|  |  |  |                     location_bytes[get_location_info(loc.name, "offset") - 1] = 0x0B | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Apply the invisibility variable depending on the "invisible items" setting. | 
					
						
							|  |  |  |         if (world.options.invisible_items == InvisibleItems.option_vanilla and loc_type == "inv") or \ | 
					
						
							|  |  |  |                 (world.options.invisible_items == InvisibleItems.option_hide_all and loc_type not in ["npc", "shop"]): | 
					
						
							|  |  |  |             location_bytes[get_location_info(loc.name, "offset") - 1] += 0x80 | 
					
						
							|  |  |  |         elif world.options.invisible_items == InvisibleItems.option_chance and loc_type not in ["npc", "shop"]: | 
					
						
							|  |  |  |             invisible = world.random.randint(0, 1) | 
					
						
							|  |  |  |             if invisible: | 
					
						
							|  |  |  |                 location_bytes[get_location_info(loc.name, "offset") - 1] += 0x80 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # If it's an Axe or Cross in a higher freestanding location, lower it into grab range. | 
					
						
							|  |  |  |         # KCEK made these spawn 3.2 units higher for some reason. | 
					
						
							|  |  |  |         if loc.address & 0xFFF in rom_axe_cross_lower_values and loc.item.code & 0xFF in [0x0F, 0x10]: | 
					
						
							|  |  |  |             location_bytes[rom_axe_cross_lower_values[loc.address & 0xFFF][0]] = \ | 
					
						
							|  |  |  |                 rom_axe_cross_lower_values[loc.address & 0xFFF][1] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Figure out the list of shop names, descriptions, and text colors here. | 
					
						
							|  |  |  |         if loc.parent_region.name != rname.renon: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         shop_name = loc.item.name | 
					
						
							|  |  |  |         if len(shop_name) > 18: | 
					
						
							|  |  |  |             shop_name = shop_name[0:18] | 
					
						
							|  |  |  |         shop_name_list.append(shop_name) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if loc.item.player == world.player: | 
					
						
							|  |  |  |             shop_desc_list.append([get_item_info(loc.item.name, "code"), None]) | 
					
						
							|  |  |  |         elif loc.item.game == "Castlevania 64": | 
					
						
							|  |  |  |             shop_desc_list.append([get_item_info(loc.item.name, "code"), | 
					
						
							|  |  |  |                                    world.multiworld.get_player_name(loc.item.player)]) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if loc.item.game == "DLCQuest" and loc.item.name in ["DLC Quest: Coin Bundle", | 
					
						
							|  |  |  |                                                                  "Live Freemium or Die: Coin Bundle"]: | 
					
						
							|  |  |  |                 if getattr(world.multiworld.worlds[loc.item.player].options, "coinbundlequantity") == 1: | 
					
						
							|  |  |  |                     shop_desc_list.append(["dlc coin", world.multiworld.get_player_name(loc.item.player)]) | 
					
						
							|  |  |  |                     shop_colors_list.append(get_item_text_color(loc)) | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if loc.item.advancement: | 
					
						
							|  |  |  |                 shop_desc_list.append(["prog", world.multiworld.get_player_name(loc.item.player)]) | 
					
						
							|  |  |  |             elif loc.item.classification == ItemClassification.useful: | 
					
						
							|  |  |  |                 shop_desc_list.append(["useful", world.multiworld.get_player_name(loc.item.player)]) | 
					
						
							|  |  |  |             elif loc.item.classification == ItemClassification.trap: | 
					
						
							|  |  |  |                 shop_desc_list.append(["trap", world.multiworld.get_player_name(loc.item.player)]) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 shop_desc_list.append(["common", world.multiworld.get_player_name(loc.item.player)]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         shop_colors_list.append(get_item_text_color(loc)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |     return {offset: int.to_bytes(byte, 1, "big") for offset, byte in location_bytes.items()}, shop_name_list,\ | 
					
						
							|  |  |  |         shop_colors_list, shop_desc_list | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_loading_zone_bytes(options: CV64Options, starting_stage: str, | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |                            active_stage_exits: Dict[str, Dict[str, Union[str, int, None]]]) -> Dict[int, bytes]: | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |     """Figure out all the bytes for loading zones and map transitions based on which stages are where in the exit data.
 | 
					
						
							|  |  |  |     The same data was used earlier in figuring out the logic. Map transitions consist of two major components: which map | 
					
						
							|  |  |  |     to send the player to, and which spot within the map to spawn the player at."""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Write the byte for the starting stage to send the player to after the intro narration. | 
					
						
							|  |  |  |     loading_zone_bytes = {0xB73308: get_stage_info(starting_stage, "start map id")} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for stage in active_stage_exits: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Start loading zones | 
					
						
							|  |  |  |         # If the start zone is the start of the line, have it simply refresh the map. | 
					
						
							|  |  |  |         if active_stage_exits[stage]["prev"] == "Menu": | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |             loading_zone_bytes[get_stage_info(stage, "startzone map offset")] = b"\xFF" | 
					
						
							|  |  |  |             loading_zone_bytes[get_stage_info(stage, "startzone spawn offset")] = b"\x00" | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |         elif active_stage_exits[stage]["prev"]: | 
					
						
							|  |  |  |             loading_zone_bytes[get_stage_info(stage, "startzone map offset")] = \ | 
					
						
							|  |  |  |                 get_stage_info(active_stage_exits[stage]["prev"], "end map id") | 
					
						
							|  |  |  |             loading_zone_bytes[get_stage_info(stage, "startzone spawn offset")] = \ | 
					
						
							|  |  |  |                 get_stage_info(active_stage_exits[stage]["prev"], "end spawn id") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # Change CC's end-spawn ID to put you at Carrie's exit if appropriate | 
					
						
							|  |  |  |             if active_stage_exits[stage]["prev"] == rname.castle_center: | 
					
						
							|  |  |  |                 if options.character_stages == CharacterStages.option_carrie_only or \ | 
					
						
							|  |  |  |                         active_stage_exits[rname.castle_center]["alt"] == stage: | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |                     loading_zone_bytes[get_stage_info(stage, "startzone spawn offset")] = b"\x03" | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # End loading zones | 
					
						
							|  |  |  |         if active_stage_exits[stage]["next"]: | 
					
						
							|  |  |  |             loading_zone_bytes[get_stage_info(stage, "endzone map offset")] = \ | 
					
						
							|  |  |  |                 get_stage_info(active_stage_exits[stage]["next"], "start map id") | 
					
						
							|  |  |  |             loading_zone_bytes[get_stage_info(stage, "endzone spawn offset")] = \ | 
					
						
							|  |  |  |                 get_stage_info(active_stage_exits[stage]["next"], "start spawn id") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Alternate end loading zones | 
					
						
							|  |  |  |         if active_stage_exits[stage]["alt"]: | 
					
						
							|  |  |  |             loading_zone_bytes[get_stage_info(stage, "altzone map offset")] = \ | 
					
						
							|  |  |  |                 get_stage_info(active_stage_exits[stage]["alt"], "start map id") | 
					
						
							|  |  |  |             loading_zone_bytes[get_stage_info(stage, "altzone spawn offset")] = \ | 
					
						
							|  |  |  |                 get_stage_info(active_stage_exits[stage]["alt"], "start spawn id") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return loading_zone_bytes | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  | def get_start_inventory_data(player: int, options: CV64Options, precollected_items: List[Item]) -> Dict[int, bytes]: | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |     """Calculate and return the starting inventory values. Not every Item goes into the menu inventory, so everything
 | 
					
						
							|  |  |  |     has to be handled appropriately."""
 | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |     start_inventory_data = {} | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     inventory_items_array = [0 for _ in range(35)] | 
					
						
							|  |  |  |     total_money = 0 | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |     total_jewels = 0 | 
					
						
							|  |  |  |     total_powerups = 0 | 
					
						
							|  |  |  |     total_ice_traps = 0 | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     items_max = 10 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Raise the items max if Increase Item Limit is enabled. | 
					
						
							|  |  |  |     if options.increase_item_limit: | 
					
						
							|  |  |  |         items_max = 99 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for item in precollected_items: | 
					
						
							|  |  |  |         if item.player != player: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         inventory_offset = get_item_info(item.name, "inventory offset") | 
					
						
							|  |  |  |         sub_equip_id = get_item_info(item.name, "sub equip id") | 
					
						
							|  |  |  |         # Starting inventory items | 
					
						
							|  |  |  |         if inventory_offset is not None: | 
					
						
							|  |  |  |             inventory_items_array[inventory_offset] += 1 | 
					
						
							|  |  |  |             if inventory_items_array[inventory_offset] > items_max and "Special" not in item.name: | 
					
						
							|  |  |  |                 inventory_items_array[inventory_offset] = items_max | 
					
						
							|  |  |  |             if item.name == iname.permaup: | 
					
						
							|  |  |  |                 if inventory_items_array[inventory_offset] > 2: | 
					
						
							|  |  |  |                     inventory_items_array[inventory_offset] = 2 | 
					
						
							|  |  |  |         # Starting sub-weapon | 
					
						
							|  |  |  |         elif sub_equip_id is not None: | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |             start_inventory_data[0xBFD883] = bytes(sub_equip_id) | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |         # Starting PowerUps | 
					
						
							|  |  |  |         elif item.name == iname.powerup: | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |             total_powerups += 1 | 
					
						
							|  |  |  |             # Can't have more than 2 PowerUps. | 
					
						
							|  |  |  |             if total_powerups > 2: | 
					
						
							|  |  |  |                 total_powerups = 2 | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |         # Starting Gold | 
					
						
							|  |  |  |         elif "GOLD" in item.name: | 
					
						
							|  |  |  |             total_money += int(item.name[0:4]) | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |             # Money cannot be higher than 99999. | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |             if total_money > 99999: | 
					
						
							|  |  |  |                 total_money = 99999 | 
					
						
							|  |  |  |         # Starting Jewels | 
					
						
							|  |  |  |         elif "jewel" in item.name: | 
					
						
							|  |  |  |             if "L" in item.name: | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |                 total_jewels += 10 | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |                 total_jewels += 5 | 
					
						
							|  |  |  |             # Jewels cannot be higher than 99. | 
					
						
							|  |  |  |             if total_jewels > 99: | 
					
						
							|  |  |  |                 total_jewels = 99 | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  |         # Starting Ice Traps | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |             total_ice_traps += 1 | 
					
						
							|  |  |  |             # Ice Traps cannot be higher than 255. | 
					
						
							|  |  |  |             if total_ice_traps > 0xFF: | 
					
						
							|  |  |  |                 total_ice_traps = 0xFF | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Convert the jewels into data. | 
					
						
							|  |  |  |     start_inventory_data[0xBFD867] = bytes([total_jewels]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Convert the Ice Traps into data. | 
					
						
							|  |  |  |     start_inventory_data[0xBFD88B] = bytes([total_ice_traps]) | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Convert the inventory items into data. | 
					
						
							| 
									
										
										
										
											2024-04-18 10:37:51 -06:00
										 |  |  |     start_inventory_data[0xBFE518] = bytes(inventory_items_array) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # Convert the starting money into data. | 
					
						
							|  |  |  |     start_inventory_data[0xBFE514] = int.to_bytes(total_money, 4, "big") | 
					
						
							| 
									
										
										
										
											2024-03-20 15:03:25 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return start_inventory_data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_item_text_color(loc: Location) -> bytearray: | 
					
						
							|  |  |  |     if loc.item.advancement: | 
					
						
							|  |  |  |         return bytearray([0xA2, 0x0C]) | 
					
						
							|  |  |  |     elif loc.item.classification == ItemClassification.useful: | 
					
						
							|  |  |  |         return bytearray([0xA2, 0x0A]) | 
					
						
							|  |  |  |     elif loc.item.classification == ItemClassification.trap: | 
					
						
							|  |  |  |         return bytearray([0xA2, 0x0B]) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         return bytearray([0xA2, 0x02]) |