450 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			450 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | import base64 | ||
|  | import Utils | ||
|  | import settings | ||
|  | from copy import deepcopy | ||
|  | 
 | ||
|  | from worlds.AutoWorld import World, WebWorld | ||
|  | from BaseClasses import Region, Location, Item, ItemClassification, Tutorial | ||
|  | 
 | ||
|  | from . import client | ||
|  | from .rom import generate_output, SuperMarioLand2ProcedurePatch | ||
|  | from .options import SML2Options | ||
|  | from .locations import (locations, location_name_to_id, level_name_to_id, level_id_to_name, START_IDS, coins_coords, | ||
|  |                         auto_scroll_max) | ||
|  | from .items import items | ||
|  | from .sprites import level_sprites | ||
|  | from .sprite_randomizer import randomize_enemies, randomize_platforms | ||
|  | from .logic import has_pipe_up, has_pipe_down, has_pipe_left, has_pipe_right, has_level_progression, is_auto_scroll | ||
|  | from . import logic | ||
|  | 
 | ||
|  | 
 | ||
|  | class MarioLand2Settings(settings.Group): | ||
|  |     class SML2RomFile(settings.UserFilePath): | ||
|  |         """File name of the Super Mario Land 2 1.0 ROM""" | ||
|  |         description = "Super Mario Land 2 - 6 Golden Coins (USA, Europe) 1.0 ROM File" | ||
|  |         copy_to = "Super Mario Land 2 - 6 Golden Coins (USA, Europe).gb" | ||
|  |         md5s = [SuperMarioLand2ProcedurePatch.hash] | ||
|  | 
 | ||
|  |     rom_file: SML2RomFile = SML2RomFile(SML2RomFile.copy_to) | ||
|  | 
 | ||
|  | 
 | ||
|  | class MarioLand2WebWorld(WebWorld): | ||
|  |     setup_en = Tutorial( | ||
|  |         "Multiworld Setup Guide", | ||
|  |         "A guide to playing Super Mario Land 2 with Archipelago.", | ||
|  |         "English", | ||
|  |         "setup_en.md", | ||
|  |         "setup/en", | ||
|  |         ["Alchav"] | ||
|  |     ) | ||
|  | 
 | ||
|  |     tutorials = [setup_en] | ||
|  | 
 | ||
|  | 
 | ||
|  | class MarioLand2World(World): | ||
|  |     """Super Mario Land 2 is a classic platformer that follows Mario on a quest to reclaim his castle from the
 | ||
|  |     villainous Wario. This iconic game features 32 levels, unique power-ups, and introduces Wario as Mario's | ||
|  |     arch-rival."""  # -ChatGPT
 | ||
|  | 
 | ||
|  |     game = "Super Mario Land 2" | ||
|  | 
 | ||
|  |     settings_key = "sml2_options" | ||
|  |     settings: MarioLand2Settings | ||
|  | 
 | ||
|  |     location_name_to_id = location_name_to_id | ||
|  |     item_name_to_id = {item_name: ID for ID, item_name in enumerate(items, START_IDS)} | ||
|  | 
 | ||
|  |     web = MarioLand2WebWorld() | ||
|  | 
 | ||
|  |     item_name_groups = { | ||
|  |         "Level Progression": { | ||
|  |             item_name for item_name in items if item_name.endswith(("Progression", "Secret", "Secret 1", "Secret 2")) | ||
|  |                                                 and "Auto Scroll" not in item_name | ||
|  |         }, | ||
|  |         "Bells": {item_name for item_name in items if "Bell" in item_name}, | ||
|  |         "Golden Coins": {"Mario Coin", "Macro Coin", "Space Coin", "Tree Coin", "Turtle Coin", "Pumpkin Coin"}, | ||
|  |         "Coins": {"1 Coin", *{f"{i} Coins" for i in range(2, 169)}}, | ||
|  |         "Powerups": {"Mushroom", "Fire Flower", "Carrot"}, | ||
|  |         "Difficulties": {"Easy Mode", "Normal Mode"}, | ||
|  |         "Auto Scroll Traps": {item_name for item_name in items | ||
|  |                               if "Auto Scroll" in item_name and "Cancel" not in item_name}, | ||
|  |         "Cancel Auto Scrolls": {item_name for item_name in items if "Cancel Auto Scroll" in item_name}, | ||
|  |     } | ||
|  | 
 | ||
|  |     location_name_groups = { | ||
|  |         "Bosses": { | ||
|  |             "Tree Zone 5 - Boss", "Space Zone 2 - Boss", "Macro Zone 4 - Boss", | ||
|  |             "Pumpkin Zone 4 - Boss", "Mario Zone 4 - Boss", "Turtle Zone 3 - Boss" | ||
|  |                    }, | ||
|  |         "Normal Exits": {location for location in locations if locations[location]["type"] == "level"}, | ||
|  |         "Secret Exits": {location for location in locations if locations[location]["type"] == "secret"}, | ||
|  |         "Bells": {location for location in locations if locations[location]["type"] == "bell"}, | ||
|  |         "Coins": {location for location in location_name_to_id if "Coin" in location} | ||
|  |     } | ||
|  | 
 | ||
|  |     options_dataclass = SML2Options | ||
|  |     options: SML2Options | ||
|  | 
 | ||
|  |     generate_output = generate_output | ||
|  | 
 | ||
|  |     def __init__(self, world, player: int): | ||
|  |         super().__init__(world, player) | ||
|  |         self.auto_scroll_levels = [] | ||
|  |         self.num_coin_locations = [] | ||
|  |         self.max_coin_locations = {} | ||
|  |         self.sprite_data = {} | ||
|  |         self.coin_fragments_required = 0 | ||
|  | 
 | ||
|  |     def generate_early(self): | ||
|  |         self.sprite_data = deepcopy(level_sprites) | ||
|  |         if self.options.randomize_enemies: | ||
|  |             randomize_enemies(self.sprite_data, self.random) | ||
|  |         if self.options.randomize_platforms: | ||
|  |             randomize_platforms(self.sprite_data, self.random) | ||
|  | 
 | ||
|  |         if self.options.marios_castle_midway_bell: | ||
|  |             self.sprite_data["Mario's Castle"][35]["sprite"] = "Midway Bell" | ||
|  | 
 | ||
|  |         if self.options.auto_scroll_chances == "vanilla": | ||
|  |             self.auto_scroll_levels = [int(i in [19, 25, 30]) for i in range(32)] | ||
|  |         else: | ||
|  |             self.auto_scroll_levels = [int(self.random.randint(1, 100) <= self.options.auto_scroll_chances) | ||
|  |                                        for _ in range(32)] | ||
|  | 
 | ||
|  |         self.auto_scroll_levels[level_name_to_id["Mario's Castle"]] = 0 | ||
|  |         unbeatable_scroll_levels = ["Tree Zone 3", "Macro Zone 2", "Space Zone 1", "Turtle Zone 2", "Pumpkin Zone 2"] | ||
|  |         if not self.options.shuffle_midway_bells: | ||
|  |             unbeatable_scroll_levels.append("Pumpkin Zone 1") | ||
|  |         for level, i in enumerate(self.auto_scroll_levels): | ||
|  |             if i == 1: | ||
|  |                 if self.options.auto_scroll_mode in ("global_cancel_item", "level_cancel_items"): | ||
|  |                     self.auto_scroll_levels[level] = 2 | ||
|  |                 elif self.options.auto_scroll_mode == "chaos": | ||
|  |                     if (self.options.accessibility == "full" | ||
|  |                             and level_id_to_name[level] in unbeatable_scroll_levels): | ||
|  |                         self.auto_scroll_levels[level] = 2 | ||
|  |                     else: | ||
|  |                         self.auto_scroll_levels[level] = self.random.randint(1, 3) | ||
|  |                 elif (self.options.accessibility == "full" | ||
|  |                       and level_id_to_name[level] in unbeatable_scroll_levels): | ||
|  |                     self.auto_scroll_levels[level] = 0 | ||
|  |                 if self.auto_scroll_levels[level] == 1 and "trap" in self.options.auto_scroll_mode.current_key: | ||
|  |                     self.auto_scroll_levels[level] = 3 | ||
|  | 
 | ||
|  |     def create_regions(self): | ||
|  |         menu_region = Region("Menu", self.player, self.multiworld) | ||
|  |         self.multiworld.regions.append(menu_region) | ||
|  |         created_regions = [] | ||
|  |         for location_name, data in locations.items(): | ||
|  |             region_name = location_name.split(" -")[0] | ||
|  |             if region_name in created_regions: | ||
|  |                 region = self.multiworld.get_region(region_name, self.player) | ||
|  |             else: | ||
|  |                 region = Region(region_name, self.player, self.multiworld) | ||
|  |                 if region_name == "Tree Zone Secret Course": | ||
|  |                     region_to_connect = self.multiworld.get_region("Tree Zone 2", self.player) | ||
|  |                 elif region_name == "Space Zone Secret Course": | ||
|  |                     region_to_connect = self.multiworld.get_region("Space Zone 1", self.player) | ||
|  |                 elif region_name == "Macro Zone Secret Course": | ||
|  |                     region_to_connect = self.multiworld.get_region("Macro Zone 1", self.player) | ||
|  |                 elif region_name == "Pumpkin Zone Secret Course 1": | ||
|  |                     region_to_connect = self.multiworld.get_region("Pumpkin Zone 2", self.player) | ||
|  |                 elif region_name == "Pumpkin Zone Secret Course 2": | ||
|  |                     region_to_connect = self.multiworld.get_region("Pumpkin Zone 3", self.player) | ||
|  |                 elif region_name == "Turtle Zone Secret Course": | ||
|  |                     region_to_connect = self.multiworld.get_region("Turtle Zone 2", self.player) | ||
|  |                 elif region_name.split(" ")[-1].isdigit() and int(region_name.split(" ")[-1]) > 1: | ||
|  |                     region_to_connect = self.multiworld.get_region(" ".join(region_name.split(" ")[:2]) | ||
|  |                                                                    + f" {int(region_name.split(' ')[2]) - 1}", | ||
|  |                                                                    self.player) | ||
|  |                 else: | ||
|  |                     region_to_connect = menu_region | ||
|  |                 region_to_connect.connect(region) | ||
|  |                 self.multiworld.regions.append(region) | ||
|  |                 created_regions.append(region_name) | ||
|  | 
 | ||
|  |             if location_name == "Mario's Castle - Midway Bell" and not self.options.marios_castle_midway_bell: | ||
|  |                 continue | ||
|  |             region.locations.append(MarioLand2Location(self.player, location_name, | ||
|  |                                                        self.location_name_to_id[location_name], region)) | ||
|  |         self.multiworld.get_region("Macro Zone Secret Course", self.player).connect( | ||
|  |             self.multiworld.get_region("Macro Zone 4", self.player)) | ||
|  |         self.multiworld.get_region("Macro Zone 4", self.player).connect( | ||
|  |             self.multiworld.get_region("Macro Zone Secret Course", self.player)) | ||
|  | 
 | ||
|  |         castle = self.multiworld.get_region("Mario's Castle", self.player) | ||
|  |         wario = MarioLand2Location(self.player, "Mario's Castle - Wario", parent=castle) | ||
|  |         castle.locations.append(wario) | ||
|  |         wario.place_locked_item(MarioLand2Item("Wario Defeated", ItemClassification.progression, None, self.player)) | ||
|  | 
 | ||
|  |         if self.options.coinsanity: | ||
|  |             coinsanity_checks = self.options.coinsanity_checks.value | ||
|  |             self.num_coin_locations = [[region, 1] for region in created_regions if region != "Mario's Castle"] | ||
|  |             self.max_coin_locations = {region: len(coins_coords[region]) for region in created_regions | ||
|  |                                        if region != "Mario's Castle"} | ||
|  |             if self.options.accessibility == "full" or self.options.auto_scroll_mode == "always": | ||
|  |                 for level in self.max_coin_locations: | ||
|  |                     if level in auto_scroll_max and self.auto_scroll_levels[level_name_to_id[level]] in (1, 3): | ||
|  |                         if isinstance(auto_scroll_max[level], tuple): | ||
|  |                             self.max_coin_locations[level] = min( | ||
|  |                                 auto_scroll_max[level][int(self.options.shuffle_midway_bells.value)], | ||
|  |                                 self.max_coin_locations[level]) | ||
|  |                         else: | ||
|  |                             self.max_coin_locations[level] = min(auto_scroll_max[level], self.max_coin_locations[level]) | ||
|  |             coinsanity_checks = min(sum(self.max_coin_locations.values()), coinsanity_checks) | ||
|  |             for i in range(coinsanity_checks - 31): | ||
|  |                 self.num_coin_locations.sort(key=lambda region: self.max_coin_locations[region[0]] / region[1]) | ||
|  |                 self.num_coin_locations[-1][1] += 1 | ||
|  |             coin_locations = [] | ||
|  |             for level, coins in self.num_coin_locations: | ||
|  |                 if self.max_coin_locations[level]: | ||
|  |                     coin_thresholds = self.random.sample(range(1, self.max_coin_locations[level] + 1), coins) | ||
|  |                     coin_locations += [f"{level} - {i} Coin{'s' if i > 1 else ''}" for i in coin_thresholds] | ||
|  |             for location_name in coin_locations: | ||
|  |                 region = self.multiworld.get_region(location_name.split(" -")[0], self.player) | ||
|  |                 region.locations.append(MarioLand2Location(self.player, location_name, | ||
|  |                                                            self.location_name_to_id[location_name], parent=region)) | ||
|  | 
 | ||
|  |     def set_rules(self): | ||
|  |         entrance_rules = { | ||
|  |             "Menu -> Space Zone 1": lambda state: state.has("Hippo Bubble", self.player) | ||
|  |                                                   or (state.has("Carrot", self.player) | ||
|  |                                                       and not is_auto_scroll(state, self.player, "Hippo Zone")), | ||
|  |             "Space Zone 1 -> Space Zone Secret Course": lambda state: state.has("Space Zone Secret", self.player), | ||
|  |             "Space Zone 1 -> Space Zone 2": lambda state: has_level_progression(state, "Space Zone Progression", self.player), | ||
|  |             "Tree Zone 1 -> Tree Zone 2": lambda state: has_level_progression(state, "Tree Zone Progression", self.player), | ||
|  |             "Tree Zone 2 -> Tree Zone Secret Course": lambda state: state.has("Tree Zone Secret", self.player), | ||
|  |             "Tree Zone 2 -> Tree Zone 3": lambda state: has_level_progression(state, "Tree Zone Progression", self.player, 2), | ||
|  |             "Tree Zone 4 -> Tree Zone 5": lambda state: has_level_progression(state, "Tree Zone Progression", self.player, 3), | ||
|  |             "Macro Zone 1 -> Macro Zone Secret Course": lambda state: state.has("Macro Zone Secret 1", self.player), | ||
|  |             "Macro Zone Secret Course -> Macro Zone 4": lambda state: state.has("Macro Zone Secret 2", self.player), | ||
|  |             "Macro Zone 1 -> Macro Zone 2": lambda state: has_level_progression(state, "Macro Zone Progression", self.player), | ||
|  |             "Macro Zone 2 -> Macro Zone 3": lambda state: has_level_progression(state, "Macro Zone Progression", self.player, 2), | ||
|  |             "Macro Zone 3 -> Macro Zone 4": lambda state: has_level_progression(state, "Macro Zone Progression", self.player, 3), | ||
|  |             "Macro Zone 4 -> Macro Zone Secret Course": lambda state: state.has("Macro Zone Secret 2", self.player), | ||
|  |             "Pumpkin Zone 1 -> Pumpkin Zone 2": lambda state: has_level_progression(state, "Pumpkin Zone Progression", self.player), | ||
|  |             "Pumpkin Zone 2 -> Pumpkin Zone Secret Course 1": lambda state: state.has("Pumpkin Zone Secret 1", self.player), | ||
|  |             "Pumpkin Zone 2 -> Pumpkin Zone 3": lambda state: has_level_progression(state, "Pumpkin Zone Progression", self.player, 2), | ||
|  |             "Pumpkin Zone 3 -> Pumpkin Zone Secret Course 2": lambda state: state.has("Pumpkin Zone Secret 2", self.player), | ||
|  |             "Pumpkin Zone 3 -> Pumpkin Zone 4": lambda state: has_level_progression(state, "Pumpkin Zone Progression", self.player, 3), | ||
|  |             "Mario Zone 1 -> Mario Zone 2": lambda state: has_level_progression(state, "Mario Zone Progression", self.player), | ||
|  |             "Mario Zone 2 -> Mario Zone 3": lambda state: has_level_progression(state, "Mario Zone Progression", self.player, 2), | ||
|  |             "Mario Zone 3 -> Mario Zone 4": lambda state: has_level_progression(state, "Mario Zone Progression", self.player, 3), | ||
|  |             "Turtle Zone 1 -> Turtle Zone 2": lambda state: has_level_progression(state, "Turtle Zone Progression", self.player), | ||
|  |             "Turtle Zone 2 -> Turtle Zone Secret Course": lambda state: state.has("Turtle Zone Secret", self.player), | ||
|  |             "Turtle Zone 2 -> Turtle Zone 3": lambda state: has_level_progression(state, "Turtle Zone Progression", self.player, 2), | ||
|  |         } | ||
|  | 
 | ||
|  |         if self.options.shuffle_golden_coins == "mario_coin_fragment_hunt": | ||
|  |             # Require the other coins just to ensure they are being added to start inventory properly, | ||
|  |             # and so they show up in Playthrough as required | ||
|  |             entrance_rules["Menu -> Mario's Castle"] = lambda state: (state.has_all( | ||
|  |                 ["Tree Coin", "Space Coin", "Macro Coin", "Pumpkin Coin", "Turtle Coin"], self.player) | ||
|  |                 and state.has("Mario Coin Fragment", self.player, self.coin_fragments_required)) | ||
|  |         else: | ||
|  |             entrance_rules["Menu -> Mario's Castle"] = lambda state: state.has_from_list_unique([ | ||
|  |                 "Tree Coin", "Space Coin", "Macro Coin", "Pumpkin Coin", "Mario Coin", "Turtle Coin" | ||
|  |                 ], self.player, self.options.required_golden_coins) | ||
|  | 
 | ||
|  | 
 | ||
|  |         for entrance, rule in entrance_rules.items(): | ||
|  |             self.multiworld.get_entrance(entrance, self.player).access_rule = rule | ||
|  | 
 | ||
|  |         for location in self.multiworld.get_locations(self.player): | ||
|  |             if location.name.endswith(("Coins", "Coin")): | ||
|  |                 rule = getattr(logic, location.parent_region.name.lower().replace(" ", "_") + "_coins", None) | ||
|  |                 if rule: | ||
|  |                     coins = int(location.name.split(" ")[-2]) | ||
|  |                     location.access_rule = lambda state, coin_rule=rule, num_coins=coins: \ | ||
|  |                         coin_rule(state, self.player, num_coins) | ||
|  |             else: | ||
|  |                 rule = getattr(logic, location.name.lower().replace( | ||
|  |                     " - ", "_").replace(" ", "_").replace("'", ""), None) | ||
|  |                 if rule: | ||
|  |                     location.access_rule = lambda state, loc_rule=rule: loc_rule(state, self.player) | ||
|  |         self.multiworld.completion_condition[self.player] = lambda state: state.has("Wario Defeated", self.player) | ||
|  | 
 | ||
|  |     def create_items(self): | ||
|  |         item_counts = { | ||
|  |             "Space Zone Progression": 1, | ||
|  |             "Space Zone Secret": 1, | ||
|  |             "Tree Zone Progression": 3, | ||
|  |             "Tree Zone Secret": 1, | ||
|  |             "Macro Zone Progression": 3, | ||
|  |             "Macro Zone Secret 1": 1, | ||
|  |             "Macro Zone Secret 2": 1, | ||
|  |             "Pumpkin Zone Progression": 3, | ||
|  |             "Pumpkin Zone Secret 1": 1, | ||
|  |             "Pumpkin Zone Secret 2": 1, | ||
|  |             "Mario Zone Progression": 3, | ||
|  |             "Turtle Zone Progression": 2, | ||
|  |             "Turtle Zone Secret": 1, | ||
|  |             "Mushroom": 1, | ||
|  |             "Fire Flower": 1, | ||
|  |             "Carrot": 1, | ||
|  |             "Space Physics": 1, | ||
|  |             "Hippo Bubble": 1, | ||
|  |             "Water Physics": 1, | ||
|  |             "Super Star Duration Increase": 2, | ||
|  |             "Mario Coin Fragment": 0, | ||
|  |         } | ||
|  | 
 | ||
|  |         if self.options.shuffle_golden_coins == "mario_coin_fragment_hunt": | ||
|  |             # There are 5 Zone Progression items that can be condensed. | ||
|  |             item_counts["Mario Coin Fragment"] = 1 + ((5 * self.options.mario_coin_fragment_percentage) // 100) | ||
|  | 
 | ||
|  |         if self.options.coinsanity: | ||
|  |             coin_count = sum([level[1] for level in self.num_coin_locations]) | ||
|  |             max_coins = sum(self.max_coin_locations.values()) | ||
|  |             if self.options.shuffle_golden_coins == "mario_coin_fragment_hunt": | ||
|  |                 removed_coins = (coin_count * self.options.mario_coin_fragment_percentage) // 100 | ||
|  |                 coin_count -= removed_coins | ||
|  |                 item_counts["Mario Coin Fragment"] += removed_coins | ||
|  |                 # Randomly remove some coin items for variety | ||
|  |                 coin_count -= (coin_count // self.random.randint(100, max(100, coin_count))) | ||
|  | 
 | ||
|  |             if coin_count: | ||
|  |                 coin_bundle_sizes = [max_coins // coin_count] * coin_count | ||
|  |                 remainder = max_coins - sum(coin_bundle_sizes) | ||
|  |                 for i in range(remainder): | ||
|  |                     coin_bundle_sizes[i] += 1 | ||
|  |                 for a, b in zip(range(1, len(coin_bundle_sizes), 2), range(2, len(coin_bundle_sizes), 2)): | ||
|  |                     split = self.random.randint(1, coin_bundle_sizes[a] + coin_bundle_sizes[b] - 1) | ||
|  |                     coin_bundle_sizes[a], coin_bundle_sizes[b] = split, coin_bundle_sizes[a] + coin_bundle_sizes[b] - split | ||
|  |                 for coin_bundle_size in coin_bundle_sizes: | ||
|  |                     item_name = f"{coin_bundle_size} Coin{'s' if coin_bundle_size > 1 else ''}" | ||
|  |                     if item_name in item_counts: | ||
|  |                         item_counts[item_name] += 1 | ||
|  |                     else: | ||
|  |                         item_counts[item_name] = 1 | ||
|  | 
 | ||
|  |         if self.options.shuffle_golden_coins == "shuffle": | ||
|  |             for item in self.item_name_groups["Golden Coins"]: | ||
|  |                 item_counts[item] = 1 | ||
|  |         elif self.options.shuffle_golden_coins == "mario_coin_fragment_hunt": | ||
|  |             for item in ("Tree Coin", "Space Coin", "Macro Coin", "Pumpkin Coin", "Turtle Coin"): | ||
|  |                 self.multiworld.push_precollected(self.create_item(item)) | ||
|  |         else: | ||
|  |             for item, location_name in ( | ||
|  |                     ("Mario Coin", "Mario Zone 4 - Boss"), | ||
|  |                     ("Tree Coin", "Tree Zone 5 - Boss"), | ||
|  |                     ("Space Coin", "Space Zone 2 - Boss"), | ||
|  |                     ("Macro Coin", "Macro Zone 4 - Boss"), | ||
|  |                     ("Pumpkin Coin", "Pumpkin Zone 4 - Boss"), | ||
|  |                     ("Turtle Coin", "Turtle Zone 3 - Boss") | ||
|  |             ): | ||
|  |                 location = self.multiworld.get_location(location_name, self.player) | ||
|  |                 location.place_locked_item(self.create_item(item)) | ||
|  |                 location.address = None | ||
|  |                 location.item.code = None | ||
|  | 
 | ||
|  |         if self.options.shuffle_midway_bells: | ||
|  |             for item in [item for item in items if "Midway Bell" in item]: | ||
|  |                 if item != "Mario's Castle Midway Bell" or self.options.marios_castle_midway_bell: | ||
|  |                     item_counts[item] = 1 | ||
|  | 
 | ||
|  |         if self.options.difficulty_mode == "easy_to_normal": | ||
|  |             item_counts["Normal Mode"] = 1 | ||
|  |         elif self.options.difficulty_mode == "normal_to_easy": | ||
|  |             item_counts["Easy Mode"] = 1 | ||
|  | 
 | ||
|  |         if self.options.shuffle_pipe_traversal == "single": | ||
|  |             item_counts["Pipe Traversal"] = 1 | ||
|  |         elif self.options.shuffle_pipe_traversal == "split": | ||
|  |             item_counts["Pipe Traversal - Right"] = 1 | ||
|  |             item_counts["Pipe Traversal - Left"] = 1 | ||
|  |             item_counts["Pipe Traversal - Up"] = 1 | ||
|  |             item_counts["Pipe Traversal - Down"] = 1 | ||
|  |         else: | ||
|  |             self.multiworld.push_precollected(self.create_item("Pipe Traversal")) | ||
|  | 
 | ||
|  |         if any(self.auto_scroll_levels): | ||
|  |             if self.options.auto_scroll_mode == "global_trap_item": | ||
|  |                 item_counts["Auto Scroll"] = 1 | ||
|  |             elif self.options.auto_scroll_mode == "global_cancel_item": | ||
|  |                 item_counts["Cancel Auto Scroll"] = 1 | ||
|  |             else: | ||
|  |                 for level, i in enumerate(self.auto_scroll_levels): | ||
|  |                     if i == 3: | ||
|  |                         item_counts[f"Auto Scroll - {level_id_to_name[level]}"] = 1 | ||
|  |                     elif i == 2: | ||
|  |                         item_counts[f"Cancel Auto Scroll - {level_id_to_name[level]}"] = 1 | ||
|  | 
 | ||
|  |         for item in self.multiworld.precollected_items[self.player]: | ||
|  |             if item.name in item_counts and item_counts[item.name] > 0: | ||
|  |                 item_counts[item.name] -= 1 | ||
|  | 
 | ||
|  |         location_count = len(self.multiworld.get_unfilled_locations(self.player)) | ||
|  |         items_to_add = location_count - sum(item_counts.values()) | ||
|  |         if items_to_add > 0: | ||
|  |             mario_coin_frags = 0 | ||
|  |             if self.options.shuffle_golden_coins == "mario_coin_fragment_hunt": | ||
|  |                 mario_coin_frags = (items_to_add * self.options.mario_coin_fragment_percentage) // 100 | ||
|  |                 item_counts["Mario Coin Fragment"] += mario_coin_frags | ||
|  |             item_counts["Super Star Duration Increase"] += items_to_add - mario_coin_frags | ||
|  |         elif items_to_add < 0: | ||
|  |             if self.options.coinsanity: | ||
|  |                 for i in range(1, 168): | ||
|  |                     coin_name = f"{i} Coin{'s' if i > 1 else ''}" | ||
|  |                     if coin_name in item_counts: | ||
|  |                         amount_to_remove = min(-items_to_add, item_counts[coin_name]) | ||
|  |                         item_counts[coin_name] -= amount_to_remove | ||
|  |                         items_to_add += amount_to_remove | ||
|  |                         if items_to_add >= 0: | ||
|  |                             break | ||
|  | 
 | ||
|  |             double_progression_items = ["Tree Zone Progression", "Macro Zone Progression", "Pumpkin Zone Progression", | ||
|  |                                         "Mario Zone Progression", "Turtle Zone Progression"] | ||
|  |             self.random.shuffle(double_progression_items) | ||
|  |             while sum(item_counts.values()) > location_count: | ||
|  |                 if double_progression_items: | ||
|  |                     double_progression_item = double_progression_items.pop() | ||
|  |                     item_counts[double_progression_item] -= 2 | ||
|  |                     item_counts[double_progression_item + " x2"] = 1 | ||
|  |                     continue | ||
|  |                 if self.options.auto_scroll_mode in ("level_trap_items", "level_cancel_items", | ||
|  |                                                      "chaos"): | ||
|  |                     auto_scroll_item = self.random.choice([item for item in item_counts if "Auto Scroll" in item]) | ||
|  |                     level = auto_scroll_item.split("- ")[1] | ||
|  |                     self.auto_scroll_levels[level_name_to_id[level]] = 0 | ||
|  |                     del item_counts[auto_scroll_item] | ||
|  |                     continue | ||
|  |                 raise Exception(f"Too many items in the item pool for Super Mario Land 2 player {self.player_name}") | ||
|  |                 # item = self.random.choice(list(item_counts)) | ||
|  |                 # item_counts[item] -= 1 | ||
|  |                 # if item_counts[item] == 0: | ||
|  |                 #     del item_counts[item] | ||
|  |                 # self.multiworld.push_precollected(self.create_item(item)) | ||
|  | 
 | ||
|  |         self.coin_fragments_required = max((item_counts["Mario Coin Fragment"] | ||
|  |                                            * self.options.mario_coin_fragments_required_percentage) // 100, 1) | ||
|  | 
 | ||
|  |         for item_name, count in item_counts.items(): | ||
|  |             self.multiworld.itempool += [self.create_item(item_name) for _ in range(count)] | ||
|  | 
 | ||
|  |     def fill_slot_data(self): | ||
|  |         return { | ||
|  |             "energy_link": self.options.energy_link.value | ||
|  |         } | ||
|  | 
 | ||
|  |     def create_item(self, name: str) -> Item: | ||
|  |         return MarioLand2Item(name, items[name], self.item_name_to_id[name], self.player) | ||
|  | 
 | ||
|  |     def get_filler_item_name(self): | ||
|  |         return "1 Coin" | ||
|  | 
 | ||
|  |     def modify_multidata(self, multidata: dict): | ||
|  |         rom_name = bytearray(f'AP{Utils.__version__.replace(".", "")[0:3]}_{self.player}_{self.multiworld.seed:11}\0', | ||
|  |                              'utf8')[:21] | ||
|  |         rom_name.extend([0] * (21 - len(rom_name))) | ||
|  |         new_name = base64.b64encode(bytes(rom_name)).decode() | ||
|  |         multidata["connect_names"][new_name] = multidata["connect_names"][self.player_name] | ||
|  | 
 | ||
|  | 
 | ||
|  | class MarioLand2Location(Location): | ||
|  |     game = "Super Mario Land 2" | ||
|  | 
 | ||
|  | 
 | ||
|  | class MarioLand2Item(Item): | ||
|  |     game = "Super Mario Land 2" |